Add CLI commands (create, list, status, merge, destroy, diff)
This commit is contained in:
145
.src/commands/create.ts
Normal file
145
.src/commands/create.ts
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user