From 7cc13d6b32c32ed6e44248bed047de1bb5afe76d Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Tue, 3 Feb 2026 09:05:07 +0000 Subject: [PATCH] feat: add destroy command --- src/commands/destroy.ts | 170 ++++++++++++---------------------------- 1 file changed, 48 insertions(+), 122 deletions(-) diff --git a/src/commands/destroy.ts b/src/commands/destroy.ts index c481a2a..f1362a0 100644 --- a/src/commands/destroy.ts +++ b/src/commands/destroy.ts @@ -3,132 +3,58 @@ import { Command } from 'commander'; import chalk from 'chalk'; import inquirer from 'inquirer'; import ora from 'ora'; -import { - getWorkspacePath, - loadWorkspaceConfig, - sanitizeAgentName, - hasUncommittedChanges, - exportChangesAsPatch, - getAllWorkspaces -} from '../utils/file-utils'; -import { - createGit, - createWorktree, - removeWorktree, - deleteBranch, - hasUncommittedChanges as gitHasUncommittedChanges, - stashChanges -} from '../utils/git-utils'; +import { getWorkspacePath, loadWorkspaceConfig, sanitizeAgentName, hasUncommittedChanges, exportChangesAsPatch } from '../utils/file-utils'; +import { createGit, removeWorktree, deleteBranch, hasUncommittedChanges as gitHasUncommittedChanges, stashChanges } from '../utils/git-utils'; import { GitAgentSyncError, WorkspaceNotFoundError } from '../utils/errors'; export function createDestroyCommand(): Command { - const cmd = new Command('destroy') - .alias('rm') - .description('Safely remove an agent workspace') - .argument('', 'Name of the agent workspace to destroy') - .option('--force', 'Skip confirmation and remove without preservation') - .option('--preserve', 'Preserve uncommitted changes as a patch file') - .option('--output ', 'Path for patch file (if --preserve is used)') - .action(async (agentName, options) => { - try { - const currentPath = process.cwd(); - const sanitizedName = sanitizeAgentName(agentName); - const workspacePath = getWorkspacePath(currentPath, sanitizedName); - - const config = await loadWorkspaceConfig(workspacePath); - if (!config) { - throw new WorkspaceNotFoundError(sanitizedName); - } - - const uncommitted = await gitHasUncommittedChanges(workspacePath); - const spinner = ora('Checking workspace status...').start(); - - if (uncommitted && !options.force) { - if (!options.preserve) { - spinner.stop(); - const { shouldPreserve } = await inquirer.prompt([ - { - type: 'confirm', - name: 'shouldPreserve', - message: `Workspace has uncommitted changes. Preserve them as a patch file?`, - default: true - } - ]); - - if (shouldPreserve) { - options.preserve = true; - } - } - } - - spinner.text = 'Preparing to destroy workspace...'; + const cmd = new Command('destroy').alias('rm').description('Safely remove an agent workspace').argument('', 'Name of the agent workspace to destroy').option('--force', 'Skip confirmation and remove without preservation').option('--preserve', 'Preserve uncommitted changes as a patch file').option('--output ', 'Path for patch file').action(async (agentName, options) => { + try { + const currentPath = process.cwd(); + const sanitizedName = sanitizeAgentName(agentName); + const workspacePath = getWorkspacePath(currentPath, sanitizedName); + const config = await loadWorkspaceConfig(workspacePath); + if (!config) throw new WorkspaceNotFoundError(sanitizedName); + const uncommitted = await gitHasUncommittedChanges(workspacePath); + const spinner = ora('Checking workspace status...').start(); + if (uncommitted && !options.force) { spinner.stop(); - - if (!options.force) { - console.log(chalk.cyan('\n🗑️ Workspace Destruction Summary')); - console.log(chalk.gray('─'.repeat(50))); - console.log(` Agent: ${chalk.white(sanitizedName)}`); - console.log(` Branch: ${chalk.white(config.branch)}`); - console.log(` Path: ${chalk.white(workspacePath)}`); - console.log(` Created: ${chalk.white(new Date(config.createdAt).toLocaleString())}`); - console.log(` Uncommitted changes: ${uncommitted ? chalk.yellow('Yes') : chalk.green('No')}`); - console.log(chalk.gray('─'.repeat(50))); - - const { confirm } = await inquirer.prompt([ - { - type: 'confirm', - name: 'confirm', - message: `Are you sure you want to destroy workspace '${sanitizedName}'?`, - default: false - } - ]); - - if (!confirm) { - console.log(chalk.yellow('\n⏸️ Destruction cancelled.')); - return; - } + if (!options.preserve) { + const { shouldPreserve } = await inquirer.prompt([{ type: 'confirm', name: 'shouldPreserve', message: `Workspace has uncommitted changes. Preserve them as a patch file?`, default: true }]); + if (shouldPreserve) options.preserve = true; } - - const destroySpinner = ora('Destroying workspace...').start(); - - if (uncommitted && options.preserve) { - const patchPath = options.output || path.join(currentPath, `${sanitizedName}-changes.patch`); - await exportChangesAsPatch(workspacePath, patchPath); - destroySpinner.text = `Changes exported to ${patchPath}`; - } - - await removeWorktree(currentPath, workspacePath, config.branch); - await deleteBranch(createGit(currentPath), config.branch, true); - - const fs = await import('fs-extra'); - const configPath = path.join(workspacePath, '.agent-workspace.json'); - const envPath = path.join(workspacePath, '.env'); - - await fs.remove(configPath); - await fs.remove(envPath); - - const workspaceRoot = path.dirname(workspacePath); - if (await fs.pathExists(workspaceRoot)) { - const remaining = await fs.readdir(workspaceRoot); - if (remaining.length === 0) { - await fs.remove(workspaceRoot); - } - } - - destroySpinner.succeed(chalk.green('Workspace destroyed successfully!')); - - if (uncommitted && options.preserve) { - console.log(chalk.yellow(`\n📦 Changes preserved at: ${options.output || `${sanitizedName}-changes.patch`}`)); - } - - console.log(chalk.cyan('\n✨ Done')); - - } catch (error) { - const gitError = error instanceof GitAgentSyncError ? error : new GitAgentSyncError(String(error)); - console.error(chalk.red(`\n❌ Error: ${gitError.message}`)); - process.exit(1); } - }); - + if (!options.force) { + console.log(chalk.cyan('\n🗑️ Workspace Destruction Summary')); + console.log(` Agent: ${chalk.white(sanitizedName)}`); + console.log(` Branch: ${chalk.white(config.branch)}`); + console.log(` Uncommitted changes: ${uncommitted ? chalk.yellow('Yes') : chalk.green('No')}`); + const { confirm } = await inquirer.prompt([{ type: 'confirm', name: 'confirm', message: `Are you sure you want to destroy workspace '${sanitizedName}'?`, default: false }]); + if (!confirm) { console.log(chalk.yellow('\n⏸️ Destruction cancelled.')); return; } + } + const destroySpinner = ora('Destroying workspace...').start(); + if (uncommitted && options.preserve) { + const patchPath = options.output || path.join(currentPath, `${sanitizedName}-changes.patch`); + await exportChangesAsPatch(workspacePath, patchPath); + destroySpinner.text = `Changes exported to ${patchPath}`; + } + await removeWorktree(currentPath, workspacePath, config.branch); + await deleteBranch(createGit(currentPath), config.branch, true); + const fs = await import('fs-extra'); + const configPath = path.join(workspacePath, '.agent-workspace.json'); + await fs.remove(configPath); + const workspaceRoot = path.dirname(workspacePath); + if (await fs.pathExists(workspaceRoot)) { + const remaining = await fs.readdir(workspaceRoot); + if (remaining.length === 0) await fs.remove(workspaceRoot); + } + destroySpinner.succeed(chalk.green('Workspace destroyed successfully!')); + if (uncommitted && options.preserve) console.log(chalk.yellow(`\n📦 Changes preserved at: ${options.output || `${sanitizedName}-changes.patch`}`)); + } 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; -} +} \ No newline at end of file