Add CLI commands (create, list, status, merge, destroy, diff)

This commit is contained in:
2026-02-03 07:57:00 +00:00
parent 00837dec59
commit e758d08e15

145
.src/commands/create.ts Normal file
View File

@@ -0,0 +1,145 @@
import * as path from 'path';
import * as fs from 'fs-extra';
import { Command } from 'commander';
import ora from 'ora';
import chalk from 'chalk';
import {
createWorktree,
createGit,
getMainBranch
} from '../utils/git-utils';
import {
loadGlobalConfig,
saveWorkspaceConfig,
getWorkspacePath,
isValidAgentName,
detectPackageJson,
installDependencies,
getGitUserInfo
} from '../utils/file-utils';
import { setupEnvironment, runCustomSetupScript } from '../utils/env-utils';
import { sanitizeAgentName } from '../utils/file-utils';
import { GitAgentSyncError, InvalidAgentNameError } from '../utils/errors';
import { CreateOptions } from '../types';
export function createCreateCommand(): Command {
const cmd = new Command('create')
.description('Create an isolated git worktree for an AI agent')
.argument('<agent-name>', 'Name identifier for the agent (will become agent-{name})')
.option('-p, --path <path>', 'Custom path for the workspace')
.option('-t, --template <template>', 'Path to environment template directory')
.option('--no-install', 'Skip automatic dependency installation')
.option('--env <key=value>', 'Set environment variable (can be used multiple times)', collectEnvVars)
.action(async (agentName, options) => {
try {
const sanitizedName = sanitizeAgentName(agentName);
if (!isValidAgentName(sanitizedName)) {
throw new InvalidAgentNameError(agentName);
}
const currentPath = process.cwd();
const spinner = ora('Initializing...').start();
const globalConfig = await loadGlobalConfig();
const branchName = `agent-${sanitizedName}`;
const workspacePath = options.path
? path.resolve(currentPath, options.path)
: getWorkspacePath(currentPath, sanitizedName);
spinner.text = 'Checking git repository...';
const git = createGit(currentPath);
try {
await git.raw(['rev-parse', '--is-inside-work-tree']);
} catch {
throw new GitAgentSyncError('Not a git repository. Please run this command from within a git repository.');
}
spinner.text = 'Checking workspace existence...';
if (await fs.pathExists(workspacePath)) {
throw new GitAgentSyncError(`Workspace already exists at ${workspacePath}. Use a different agent name or destroy the existing workspace first.`);
}
spinner.text = 'Determining main branch...';
const mainBranch = await getMainBranch(git, globalConfig.defaultBranch);
spinner.text = 'Creating worktree...';
await createWorktree(currentPath, workspacePath, branchName, mainBranch);
const gitUserInfo = await getGitUserInfo();
const createOptions: CreateOptions = {
path: options.path,
template: options.template,
installDeps: options.install !== false,
environment: parseEnvVars(options.env || [])
};
spinner.text = 'Setting up environment...';
await setupEnvironment(workspacePath, sanitizedName, createOptions);
if (createOptions.installDeps) {
spinner.text = 'Checking for dependencies...';
if (await detectPackageJson(workspacePath)) {
spinner.text = 'Installing dependencies...';
await installDependencies(workspacePath);
}
}
const workspaceConfig = {
agentName: sanitizedName,
branch: branchName,
createdAt: new Date().toISOString(),
mainBranch,
environment: {
AGENT_ID: sanitizedName,
WORKSPACE_PATH: workspacePath,
GIT_USER_EMAIL: gitUserInfo.email,
GIT_USER_NAME: gitUserInfo.name
}
};
await saveWorkspaceConfig(workspacePath, workspaceConfig);
spinner.succeed(chalk.green('Workspace created successfully!'));
console.log(chalk.cyan('\n📁 Workspace Details:'));
console.log(` Agent Name: ${chalk.white(sanitizedName)}`);
console.log(` Branch: ${chalk.white(branchName)}`);
console.log(` Path: ${chalk.white(workspacePath)}`);
console.log(` Main Branch: ${chalk.white(mainBranch)}`);
console.log(` Created: ${chalk.white(new Date().toISOString())}`);
console.log(chalk.cyan('\n📝 Next Steps:'));
console.log(` 1. ${chalk.white(`cd ${workspacePath}`)}`);
console.log(` 2. ${chalk.white('Start your coding tasks')}`);
console.log(` 3. ${chalk.white('git-agent-sync status')} - Track changes`);
console.log(` 4. ${chalk.white('git-agent-sync diff')} - Generate diffs`);
console.log(` 5. ${chalk.white('git-agent-sync merge')} - Merge back to main`);
} catch (error) {
const gitError = error instanceof GitAgentSyncError ? error : new GitAgentSyncError(String(error));
console.error(chalk.red(`\n❌ Error: ${gitError.message}`));
process.exit(1);
}
});
return cmd;
}
function collectEnvVars(value: string, previous: string[]): string[] {
return [...(previous || []), value];
}
function parseEnvVars(envVars: string[]): Record<string, string> {
const result: Record<string, string> = {};
for (const envVar of envVars) {
const [key, ...valueParts] = envVar.split('=');
if (key && valueParts.length > 0) {
result[key] = valueParts.join('=');
}
}
return result;
}