chore: Push command files (part 1)
Some checks failed
/ test (push) Failing after 8s

This commit is contained in:
2026-02-03 08:39:26 +00:00
parent 8569130584
commit 4a19ad3879

View File

@@ -1,32 +1,26 @@
import * as path from 'path';
import { Command } from 'commander'; import { Command } from 'commander';
import chalk from 'chalk'; import chalk from 'chalk';
import inquirer from 'inquirer';
import ora from 'ora';
import { import {
getWorkspacePath, getWorkspacePath,
loadWorkspaceConfig, loadWorkspaceConfig,
sanitizeAgentName, sanitizeAgentName,
hasUncommittedChanges, hasUncommittedChanges
readPackageJson
} from '../utils/file-utils'; } from '../utils/file-utils';
import { import {
mergeToMain,
createGit, createGit,
mergeToMain,
hasUncommittedChanges as gitHasUncommittedChanges hasUncommittedChanges as gitHasUncommittedChanges
} from '../utils/git-utils'; } from '../utils/git-utils';
import { GitAgentSyncError, WorkspaceNotFoundError, MergeConflictError } from '../utils/errors'; import { GitAgentSyncError, WorkspaceNotFoundError, MergeFailedError } from '../utils/errors';
import { ConflictInfo } from '../types/index';
import { MergeOptions } from '../types';
export function createMergeCommand(): Command { export function createMergeCommand(): Command {
const cmd = new Command('merge') const cmd = new Command('merge')
.description('Safely merge agent changes back to main branch') .description('Safely merge agent changes back to main branch')
.argument('<agent-name>', 'Name of the agent workspace to merge') .argument('<agent-name>', 'Name of the agent workspace to merge')
.option('--force', 'Force merge even with conflicts (use with caution)') .option('--force', 'Force merge even with uncommitted changes')
.option('--dry-run', 'Preview merge without making changes') .option('--dry-run', 'Show what would happen without making changes')
.option('--message <msg>', 'Custom merge commit message') .option('-m, --message <message>', 'Custom commit message')
.action(async (agentName, options: MergeOptions) => { .action(async (agentName, options) => {
try { try {
const currentPath = process.cwd(); const currentPath = process.cwd();
const sanitizedName = sanitizeAgentName(agentName); const sanitizedName = sanitizeAgentName(agentName);
@@ -37,119 +31,46 @@ export function createMergeCommand(): Command {
throw new WorkspaceNotFoundError(sanitizedName); throw new WorkspaceNotFoundError(sanitizedName);
} }
const spinner = ora('Preparing for merge...').start(); if (!options.force && await hasUncommittedChanges(workspacePath)) {
throw new GitAgentSyncError('Workspace has uncommitted changes. Commit or stash them first, or use --force.');
const uncommitted = await gitHasUncommittedChanges(workspacePath);
if (uncommitted) {
spinner.stop();
console.log(chalk.yellow('\n⚠ Workspace has uncommitted changes.'));
const { shouldCommit } = await inquirer.prompt([
{
type: 'confirm',
name: 'shouldCommit',
message: 'Commit changes before merging?',
default: true
}
]);
if (shouldCommit) {
const workspaceGit = createGit(workspacePath);
await workspaceGit.add('.');
await workspaceGit.commit('Uncommitted changes before merge');
}
} }
spinner.text = 'Running pre-merge validation...';
const mainBranch = config.mainBranch;
const workspaceGit = createGit(workspacePath); const workspaceGit = createGit(workspacePath);
const pkgJson = await readPackageJson(workspacePath); console.log(chalk.cyan(`\n🔀 Merging ${sanitizedName} into ${config.mainBranch}...`));
let testsPassed = true;
if (pkgJson?.scripts?.test && !options.force) { const result = await mergeToMain(
spinner.text = 'Running tests...';
const { execa } = await import('execa');
try {
await execa('npm', ['test'], { cwd: workspacePath, reject: false });
} catch {
testsPassed = false;
}
}
if (!testsPassed && !options.force) {
spinner.fail(chalk.red('Tests failed. Use --force to bypass.'));
process.exit(1);
}
spinner.text = 'Merging changes...';
const mergeResult = await mergeToMain(
currentPath, currentPath,
workspacePath, workspacePath,
sanitizedName, sanitizedName,
mainBranch, config.mainBranch,
options.message, options.message,
options.dryRun options.dryRun
); );
if (result.success) {
if (options.dryRun) { if (options.dryRun) {
spinner.stop(); console.log(chalk.yellow(`\n⚠ [DRY RUN] ${result.message}`));
console.log(chalk.cyan('\n🔍 Dry Run Result:')); } else {
console.log(chalk.gray('─'.repeat(50))); console.log(chalk.green('\n✓ Merge completed successfully!'));
console.log(` ${mergeResult.message}`); console.log(` Message: ${result.message}`);
console.log(chalk.gray('─'.repeat(50)));
return;
} }
} else {
if (!mergeResult.success && mergeResult.conflicts) { if (result.conflicts && result.conflicts.length > 0) {
spinner.fail(chalk.red('Merge conflicts detected!')); console.log(chalk.red('\n❌ Merge conflicts detected!'));
console.log(chalk.cyan('\nConflicting files:'));
console.log(chalk.yellow('\n📝 Conflicting files:')); for (const conflict of result.conflicts) {
for (const conflict of mergeResult.conflicts) {
console.log(` ${chalk.red(conflict.file)}`); console.log(` ${chalk.red(conflict.file)}`);
} }
console.log(chalk.cyan('\n💡 Resolve conflicts manually and commit before trying again.'));
const { shouldRollback } = await inquirer.prompt([ } else {
{ throw new MergeFailedError(sanitizedName, result.error || 'Unknown error');
type: 'confirm',
name: 'shouldRollback',
message: 'Rollback merge?',
default: true
} }
]);
if (shouldRollback) {
const { execa } = await import('execa');
await execa('git', ['merge', '--abort'], { cwd: currentPath });
console.log(chalk.green('\n✅ Rollback complete. Resolve conflicts and try again.'));
} }
throw new MergeConflictError(mergeResult.conflicts.map((c: ConflictInfo) => c.file));
}
spinner.succeed(chalk.green('Merge completed successfully!'));
console.log(chalk.cyan('\n📦 Merge Summary'));
console.log(chalk.gray('─'.repeat(50)));
console.log(` Agent: ${chalk.white(sanitizedName)}`);
console.log(` From Branch: ${chalk.white(config.branch)}`);
console.log(` To Branch: ${chalk.white(mainBranch)}`);
console.log(` Status: ${chalk.green('✓ Merged')}`);
if (mergeResult.commitSha) {
console.log(` Commit: ${chalk.white(mergeResult.commitSha.substring(0, 7))}`);
}
console.log(chalk.gray('─'.repeat(50)));
console.log(chalk.cyan('\n✨ You can now delete the workspace with:'));
console.log(` ${chalk.white(\`git-agent-sync destroy ${sanitizedName}\`)}`);
} catch (error) { } catch (error) {
if (error instanceof MergeConflictError) {
console.error(chalk.red(`\n ${error.message}`));
} else {
const gitError = error instanceof GitAgentSyncError ? error : new GitAgentSyncError(String(error)); const gitError = error instanceof GitAgentSyncError ? error : new GitAgentSyncError(String(error));
console.error(chalk.red(`\n❌ Error: ${gitError.message}`)); console.error(chalk.red(`\n❌ Error: ${gitError.message}`));
}
process.exit(1); process.exit(1);
} }
}); });