import * as path from 'path'; import { Command } from 'commander'; import chalk from 'chalk'; import { loadGlobalConfig, getAllWorkspaces, loadWorkspaceConfig, loadChangeTracking } from '../utils/file-utils'; import { createGit, getWorktreeStatus, hasUncommittedChanges, getLastCommitInfo } from '../utils/git-utils'; import { GitAgentSyncError } from '../utils/errors'; import { WorkspaceInfo, WorkspaceChange } from '../types'; export function createListCommand(): Command { const cmd = new Command('list') .alias('ls') .description('List all active agent workspaces') .option('--json', 'Output as JSON') .option('--verbose', 'Show detailed information') .action(async (options) => { try { const currentPath = process.cwd(); const globalConfig = await loadGlobalConfig(); const workspaces = await getAllWorkspaces(currentPath); if (workspaces.length === 0) { console.log(chalk.yellow('\nšŸ“­ No agent workspaces found.')); console.log(` Create one with: ${chalk.cyan('git-agent-sync create ')}`); return; } const workspaceDetails: (WorkspaceInfo & { status?: WorkspaceChange; lastCommit?: string })[] = []; for (const workspace of workspaces) { const config = await loadWorkspaceConfig(workspace.path); if (config) { const status = await getWorktreeStatus(workspace.path); const lastCommit = await getLastCommitInfo(workspace.path); workspaceDetails.push({ ...workspace, hasChanges: status.uncommittedCount > 0, changeCount: status.uncommittedCount, lastCommit: lastCommit.message || 'No commits' }); } } if (options.json) { console.log(JSON.stringify(workspaceDetails, null, 2)); return; } console.log(chalk.cyan('\nšŸ¤– Agent Workspaces')); console.log(chalk.gray('─'.repeat(80))); const tableData = workspaceDetails.map(ws => ({ name: ws.name, branch: ws.branch, path: path.relative(currentPath, ws.path), status: ws.hasChanges ? chalk.yellow('šŸ”„ modified') : chalk.green('āœ“ clean'), changes: ws.hasChanges ? chalk.yellow(`${ws.changeCount}`) : chalk.gray('0'), lastCommit: truncateString(ws.lastCommit || '', 30) })); const maxName = Math.max(...tableData.map(d => d.name.length)); const maxBranch = Math.max(...tableData.map(d => d.branch.length)); const maxPath = Math.max(...tableData.map(d => d.path.length)); for (const row of tableData) { const nameCol = row.name.padEnd(maxName); const branchCol = row.branch.padEnd(maxBranch); const pathCol = row.path.padEnd(maxPath); const changesCol = row.changes.padStart(3); console.log(` ${chalk.white(nameCol)} ${chalk.gray('│')} ${chalk.white(branchCol)} ${chalk.gray('│')} ${chalk.white(pathCol)} ${chalk.gray('│')} ${row.status} ${chalk.gray('│')} ${changesCol} changes`); } console.log(chalk.gray('─'.repeat(80))); console.log(` ${chalk.white(`${workspaces.length} workspace(s)`)}`); const totalChanges = workspaceDetails.reduce((sum, ws) => sum + ws.changeCount, 0); console.log(` ${chalk.white(`${totalChanges} total uncommitted change(s)`)}`); if (options.verbose) { console.log(chalk.cyan('\nšŸ“ Workspace Paths:')); for (const ws of workspaceDetails) { console.log(` ${chalk.white(ws.name)}: ${ws.path}`); } } } 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 truncateString(str: string, maxLength: number): string { if (str.length <= maxLength) { return str; } return str.substring(0, maxLength - 3) + '...'; }