@iloom/cli 0.10.0 → 0.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +2 -2
- package/dist/{BranchNamingService-ECJHBB67.js → BranchNamingService-4OP6LOH6.js} +2 -2
- package/dist/ClaudeContextManager-ZKTUVQB2.js +14 -0
- package/dist/ClaudeService-TRWOYQ6O.js +13 -0
- package/dist/{LoomLauncher-L64HHS3T.js → LoomLauncher-FRECYMXS.js} +6 -6
- package/dist/{PromptTemplateManager-DULSVRRE.js → PromptTemplateManager-YOE2SIPG.js} +2 -2
- package/dist/README.md +2 -2
- package/dist/{SettingsManager-BQDQA3FK.js → SettingsManager-FNKCOZMQ.js} +2 -2
- package/dist/{build-5GO3XW26.js → build-VHGEMXBA.js} +6 -6
- package/dist/{chunk-ZW2LKWWE.js → chunk-2VEWSM34.js} +3 -3
- package/dist/{chunk-LXLMMXXY.js → chunk-2YZCWAVZ.js} +17 -12
- package/dist/chunk-2YZCWAVZ.js.map +1 -0
- package/dist/{chunk-UD3WJDIV.js → chunk-3F27M7ZD.js} +11 -774
- package/dist/chunk-3F27M7ZD.js.map +1 -0
- package/dist/chunk-4E7LCFUG.js +24 -0
- package/dist/chunk-4E7LCFUG.js.map +1 -0
- package/dist/{chunk-MNHZB4Z2.js → chunk-4FGEGQW4.js} +3 -3
- package/dist/{chunk-WY4QBK43.js → chunk-63QWFWH3.js} +2 -2
- package/dist/{chunk-YYAKPQBT.js → chunk-7VHJNVLF.js} +19 -9
- package/dist/chunk-7VHJNVLF.js.map +1 -0
- package/dist/chunk-BFHDVFSK.js +651 -0
- package/dist/chunk-BFHDVFSK.js.map +1 -0
- package/dist/{chunk-FB47TIJG.js → chunk-BFLMCE2U.js} +4 -23
- package/dist/chunk-BFLMCE2U.js.map +1 -0
- package/dist/{chunk-SN3SQCFK.js → chunk-BU53XIGY.js} +4 -4
- package/dist/{chunk-SF2P22EE.js → chunk-C6HNNJIV.js} +2 -2
- package/dist/{chunk-5MWV33NN.js → chunk-CVCTIDDK.js} +2 -2
- package/dist/{chunk-ZEWU5PZK.js → chunk-G5V75JD5.js} +2 -2
- package/dist/{chunk-ZHPNZC75.js → chunk-HYGUPUV5.js} +26 -21
- package/dist/chunk-HYGUPUV5.js.map +1 -0
- package/dist/{chunk-VGGST52X.js → chunk-I5T677EA.js} +2 -2
- package/dist/{chunk-VECNX6VX.js → chunk-KIK2ZFAL.js} +2 -2
- package/dist/{chunk-3D7WQM7I.js → chunk-LLHXQS3C.js} +2 -2
- package/dist/{chunk-Y4YZTHZE.js → chunk-LUKXJSRI.js} +2 -2
- package/dist/{chunk-ONQYPICO.js → chunk-PZ5WSR5Z.js} +63 -9
- package/dist/chunk-PZ5WSR5Z.js.map +1 -0
- package/dist/{chunk-J5S7DFYC.js → chunk-QFTDZ5E3.js} +2 -2
- package/dist/chunk-RJ3VBUFK.js +781 -0
- package/dist/chunk-RJ3VBUFK.js.map +1 -0
- package/dist/{chunk-UWGVCXRF.js → chunk-SKSYYBCU.js} +23 -1
- package/dist/chunk-SKSYYBCU.js.map +1 -0
- package/dist/{chunk-JO2LZ6EQ.js → chunk-SWSJWA2S.js} +2 -2
- package/dist/{chunk-4WJNIR5O.js → chunk-UUEW5KWB.js} +1 -1
- package/dist/chunk-UUEW5KWB.js.map +1 -0
- package/dist/{chunk-6EU6TCF6.js → chunk-V3SVMFDQ.js} +5 -5
- package/dist/{chunk-NRSWLOAZ.js → chunk-WXIM2WS7.js} +4 -4
- package/dist/{chunk-RYWFS37M.js → chunk-XE4BDRZD.js} +2 -2
- package/dist/{ignite-CGOV3TD4.js → chunk-ZGM2FE2R.js} +105 -73
- package/dist/chunk-ZGM2FE2R.js.map +1 -0
- package/dist/{claude-P3NQR6IJ.js → claude-LN7OWVNI.js} +2 -2
- package/dist/{cleanup-6UCPVMFG.js → cleanup-4ZM2AJDC.js} +19 -17
- package/dist/{cleanup-6UCPVMFG.js.map → cleanup-4ZM2AJDC.js.map} +1 -1
- package/dist/cli.js +167 -614
- package/dist/cli.js.map +1 -1
- package/dist/{commit-L3EPY5QG.js → commit-4CFLXRZ3.js} +12 -10
- package/dist/commit-4CFLXRZ3.js.map +1 -0
- package/dist/{compile-ZS4HYRX5.js → compile-7ALJHZ4N.js} +6 -6
- package/dist/{contribute-ORDDQGSL.js → contribute-5GKLK3BQ.js} +3 -3
- package/dist/{dev-server-FYZ2AQIH.js → dev-server-7SMIB7OF.js} +8 -8
- package/dist/{feedback-TMBXSCM5.js → feedback-EZWF5CAL.js} +10 -8
- package/dist/{feedback-TMBXSCM5.js.map → feedback-EZWF5CAL.js.map} +1 -1
- package/dist/{git-ET64COO3.js → git-GTLKAZRJ.js} +3 -3
- package/dist/ignite-MQETGFNA.js +34 -0
- package/dist/ignite-MQETGFNA.js.map +1 -0
- package/dist/index.d.ts +113 -18
- package/dist/index.js +180 -15
- package/dist/index.js.map +1 -1
- package/dist/{init-GFQ5W7GK.js → init-ZB2RITW6.js} +8 -8
- package/dist/install-deps-RLSGSHH7.js +43 -0
- package/dist/install-deps-RLSGSHH7.js.map +1 -0
- package/dist/{issues-T4ZZSPEG.js → issues-4UUAQ5K6.js} +3 -3
- package/dist/{lint-6TQXDZ3T.js → lint-AAN2NZWG.js} +6 -6
- package/dist/mcp/harness-server.js +140 -0
- package/dist/mcp/harness-server.js.map +1 -0
- package/dist/mcp/issue-management-server.js +140 -18
- package/dist/mcp/issue-management-server.js.map +1 -1
- package/dist/{open-5QZGXQRF.js → open-FXWW3VI4.js} +8 -8
- package/dist/{plan-U7ZQWLFY.js → plan-D3KSN5MU.js} +338 -36
- package/dist/plan-D3KSN5MU.js.map +1 -0
- package/dist/prompts/CLAUDE.md +2 -2
- package/dist/prompts/init-prompt.txt +102 -27
- package/dist/prompts/issue-prompt.txt +46 -0
- package/dist/prompts/plan-prompt.txt +59 -19
- package/dist/prompts/swarm-orchestrator-prompt.txt +121 -80
- package/dist/{rebase-DWIB77KV.js → rebase-62FDLIH4.js} +17 -8
- package/dist/rebase-62FDLIH4.js.map +1 -0
- package/dist/{recap-MX63HAKV.js → recap-OMBOKJST.js} +6 -6
- package/dist/{run-O3TFNQFC.js → run-BBXLRIZB.js} +8 -8
- package/dist/schema/settings.schema.json +36 -2
- package/dist/{shell-G6VC2CYR.js → shell-RF7LTND5.js} +5 -5
- package/dist/{summary-FWHAX55O.js → summary-YZI25KW4.js} +9 -9
- package/dist/{test-F7JNJZYP.js → test-SGO6I5Z7.js} +6 -6
- package/dist/{test-git-BTAOIUE2.js → test-git-XM4TM65W.js} +3 -3
- package/dist/{test-jira-CHYNV33F.js → test-jira-LDTOYFSD.js} +3 -3
- package/dist/{test-prefix-Q6TFSU6F.js → test-prefix-GBO37XCN.js} +3 -3
- package/dist/{test-webserver-EONCG7E7.js → test-webserver-NZ3JTVLL.js} +5 -5
- package/dist/{vscode-VA5X4P25.js → vscode-6XUGHJKL.js} +5 -5
- package/package.json +1 -1
- package/dist/ClaudeContextManager-QXX6ZFST.js +0 -14
- package/dist/ClaudeService-NJNK2SUH.js +0 -13
- package/dist/chunk-4WJNIR5O.js.map +0 -1
- package/dist/chunk-FB47TIJG.js.map +0 -1
- package/dist/chunk-LXLMMXXY.js.map +0 -1
- package/dist/chunk-ONQYPICO.js.map +0 -1
- package/dist/chunk-UD3WJDIV.js.map +0 -1
- package/dist/chunk-UVD4CZKS.js +0 -101
- package/dist/chunk-UVD4CZKS.js.map +0 -1
- package/dist/chunk-UWGVCXRF.js.map +0 -1
- package/dist/chunk-YYAKPQBT.js.map +0 -1
- package/dist/chunk-ZHPNZC75.js.map +0 -1
- package/dist/commit-L3EPY5QG.js.map +0 -1
- package/dist/ignite-CGOV3TD4.js.map +0 -1
- package/dist/plan-U7ZQWLFY.js.map +0 -1
- package/dist/rebase-DWIB77KV.js.map +0 -1
- /package/dist/{BranchNamingService-ECJHBB67.js.map → BranchNamingService-4OP6LOH6.js.map} +0 -0
- /package/dist/{ClaudeContextManager-QXX6ZFST.js.map → ClaudeContextManager-ZKTUVQB2.js.map} +0 -0
- /package/dist/{ClaudeService-NJNK2SUH.js.map → ClaudeService-TRWOYQ6O.js.map} +0 -0
- /package/dist/{LoomLauncher-L64HHS3T.js.map → LoomLauncher-FRECYMXS.js.map} +0 -0
- /package/dist/{PromptTemplateManager-DULSVRRE.js.map → PromptTemplateManager-YOE2SIPG.js.map} +0 -0
- /package/dist/{SettingsManager-BQDQA3FK.js.map → SettingsManager-FNKCOZMQ.js.map} +0 -0
- /package/dist/{build-5GO3XW26.js.map → build-VHGEMXBA.js.map} +0 -0
- /package/dist/{chunk-ZW2LKWWE.js.map → chunk-2VEWSM34.js.map} +0 -0
- /package/dist/{chunk-MNHZB4Z2.js.map → chunk-4FGEGQW4.js.map} +0 -0
- /package/dist/{chunk-WY4QBK43.js.map → chunk-63QWFWH3.js.map} +0 -0
- /package/dist/{chunk-SN3SQCFK.js.map → chunk-BU53XIGY.js.map} +0 -0
- /package/dist/{chunk-SF2P22EE.js.map → chunk-C6HNNJIV.js.map} +0 -0
- /package/dist/{chunk-5MWV33NN.js.map → chunk-CVCTIDDK.js.map} +0 -0
- /package/dist/{chunk-ZEWU5PZK.js.map → chunk-G5V75JD5.js.map} +0 -0
- /package/dist/{chunk-VGGST52X.js.map → chunk-I5T677EA.js.map} +0 -0
- /package/dist/{chunk-VECNX6VX.js.map → chunk-KIK2ZFAL.js.map} +0 -0
- /package/dist/{chunk-3D7WQM7I.js.map → chunk-LLHXQS3C.js.map} +0 -0
- /package/dist/{chunk-Y4YZTHZE.js.map → chunk-LUKXJSRI.js.map} +0 -0
- /package/dist/{chunk-J5S7DFYC.js.map → chunk-QFTDZ5E3.js.map} +0 -0
- /package/dist/{chunk-JO2LZ6EQ.js.map → chunk-SWSJWA2S.js.map} +0 -0
- /package/dist/{chunk-6EU6TCF6.js.map → chunk-V3SVMFDQ.js.map} +0 -0
- /package/dist/{chunk-NRSWLOAZ.js.map → chunk-WXIM2WS7.js.map} +0 -0
- /package/dist/{chunk-RYWFS37M.js.map → chunk-XE4BDRZD.js.map} +0 -0
- /package/dist/{claude-P3NQR6IJ.js.map → claude-LN7OWVNI.js.map} +0 -0
- /package/dist/{compile-ZS4HYRX5.js.map → compile-7ALJHZ4N.js.map} +0 -0
- /package/dist/{contribute-ORDDQGSL.js.map → contribute-5GKLK3BQ.js.map} +0 -0
- /package/dist/{dev-server-FYZ2AQIH.js.map → dev-server-7SMIB7OF.js.map} +0 -0
- /package/dist/{git-ET64COO3.js.map → git-GTLKAZRJ.js.map} +0 -0
- /package/dist/{init-GFQ5W7GK.js.map → init-ZB2RITW6.js.map} +0 -0
- /package/dist/{issues-T4ZZSPEG.js.map → issues-4UUAQ5K6.js.map} +0 -0
- /package/dist/{lint-6TQXDZ3T.js.map → lint-AAN2NZWG.js.map} +0 -0
- /package/dist/{open-5QZGXQRF.js.map → open-FXWW3VI4.js.map} +0 -0
- /package/dist/{recap-MX63HAKV.js.map → recap-OMBOKJST.js.map} +0 -0
- /package/dist/{run-O3TFNQFC.js.map → run-BBXLRIZB.js.map} +0 -0
- /package/dist/{shell-G6VC2CYR.js.map → shell-RF7LTND5.js.map} +0 -0
- /package/dist/{summary-FWHAX55O.js.map → summary-YZI25KW4.js.map} +0 -0
- /package/dist/{test-F7JNJZYP.js.map → test-SGO6I5Z7.js.map} +0 -0
- /package/dist/{test-git-BTAOIUE2.js.map → test-git-XM4TM65W.js.map} +0 -0
- /package/dist/{test-jira-CHYNV33F.js.map → test-jira-LDTOYFSD.js.map} +0 -0
- /package/dist/{test-prefix-Q6TFSU6F.js.map → test-prefix-GBO37XCN.js.map} +0 -0
- /package/dist/{test-webserver-EONCG7E7.js.map → test-webserver-NZ3JTVLL.js.map} +0 -0
- /package/dist/{vscode-VA5X4P25.js.map → vscode-6XUGHJKL.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/first-run-setup.ts","../src/commands/start.ts"],"sourcesContent":["import { existsSync } from 'fs'\nimport { readFile } from 'fs/promises'\nimport path from 'path'\nimport chalk from 'chalk'\nimport { logger } from './logger.js'\nimport { FirstRunManager } from './FirstRunManager.js'\nimport { getRepoRoot } from './git.js'\nimport { promptConfirmation, waitForKeypress } from './prompt.js'\n\n/**\n * Get the project root path for first-run tracking\n * Uses git repo root if available, otherwise falls back to cwd\n * This ensures consistent path resolution regardless of where the CLI is run from\n */\nasync function getProjectRoot(): Promise<string> {\n\tconst repoRoot = await getRepoRoot()\n\tif (repoRoot) {\n\t\tlogger.debug(`getProjectRoot: Using git repo root: ${repoRoot}`)\n\t\treturn repoRoot\n\t}\n\tconst cwd = process.cwd()\n\tlogger.debug(`getProjectRoot: Not in git repo, using cwd: ${cwd}`)\n\treturn cwd\n}\n\n/**\n * Check if project needs first-run setup\n * Returns true if:\n * 1. Project is not tracked as configured globally AND\n * 2. .iloom directory is missing or settings files are empty\n *\n * Uses git repo root for path resolution to ensure consistent behavior\n * regardless of whether the CLI is run from a subdirectory or worktree\n */\nexport async function needsFirstRunSetup(): Promise<boolean> {\n\tconst projectRoot = await getProjectRoot()\n\tconst firstRunManager = new FirstRunManager()\n\n\t// Check if project is tracked as configured globally\n\tconst isConfigured = await firstRunManager.isProjectConfigured(projectRoot)\n\tif (isConfigured) {\n\t\tlogger.debug('needsFirstRunSetup: Project is tracked as configured globally')\n\t\treturn false\n\t}\n\n\tconst iloomDir = path.join(projectRoot, '.iloom')\n\n\t// Check if .iloom directory exists\n\tif (!existsSync(iloomDir)) {\n\t\treturn true\n\t}\n\n\t// Check if either settings file has meaningful content\n\tconst settingsPath = path.join(iloomDir, 'settings.json')\n\tconst settingsLocalPath = path.join(iloomDir, 'settings.local.json')\n\n\tconst hasSettings = await hasNonEmptySettings(settingsPath)\n\tconst hasLocalSettings = await hasNonEmptySettings(settingsLocalPath)\n\n\treturn !hasSettings && !hasLocalSettings\n}\n\nasync function hasNonEmptySettings(filePath: string): Promise<boolean> {\n\tif (!existsSync(filePath)) return false\n\ttry {\n\t\tconst content = await readFile(filePath, 'utf-8')\n\t\tconst parsed = JSON.parse(content)\n\t\treturn Object.keys(parsed).length > 0\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/**\n * Display default configuration values in a formatted box\n */\nfunction displayDefaultsBox(): void {\n\tlogger.info(chalk.bold('Default Configuration:'))\n\tlogger.info('')\n\tlogger.info(` ${chalk.cyan('Main Branch:')} main`)\n\tlogger.info(` ${chalk.cyan('IDE:')} vscode`)\n\tlogger.info(` ${chalk.cyan('Issue Tracker:')} GitHub Issues`)\n\tlogger.info(` ${chalk.cyan('Merge Mode:')} local ${chalk.dim('(merge locally)')}`)\n\tlogger.info(` ${chalk.cyan('Base Port:')} 3000`)\n}\n\n/**\n * Launch interactive first-run setup via InitCommand\n * Shows defaults first, allows quick acceptance or full wizard\n */\nexport async function launchFirstRunSetup(): Promise<void> {\n\tlogger.info('First-time project setup detected.')\n\tlogger.info('')\n\n\t// Display the defaults\n\tdisplayDefaultsBox()\n\n\tlogger.info('')\n\n\t// Import prompt utility\n\n\t// Ask if defaults are OK (default to Yes)\n\tconst acceptDefaults = await promptConfirmation(\n\t\t'Are these defaults OK?',\n\t\ttrue // default to true, so Enter accepts\n\t)\n\n\tif (acceptDefaults) {\n\t\t// User accepted defaults - just mark as configured\n\t\tconst projectRoot = await getProjectRoot()\n\t\tconst firstRunManager = new FirstRunManager()\n\t\tawait firstRunManager.markProjectAsConfigured(projectRoot)\n\t\tlogger.info(chalk.green('Configuration complete! Using defaults.'))\n\t\tlogger.info('You can run `il init` anytime to customize settings.')\n\t\treturn\n\t}\n\n\t// User declined - launch full wizard\n\tlogger.info('')\n\tlogger.info('iloom will now launch an interactive configuration session with Claude.')\n\n\tawait waitForKeypress('Press any key to start configuration...')\n\n\tconst { InitCommand } = await import('../commands/init.js')\n\tconst initCommand = new InitCommand()\n\tawait initCommand.execute(\n\t\t'Help me configure iloom settings for this project. This is my first time using iloom here. Note: Your iloom command will execute once we are done with configuration changes.'\n\t)\n\t// Note: InitCommand.execute() now handles markProjectAsConfigured() internally\n\t// when the guided init completes successfully\n\n\tlogger.info('Configuration complete! Continuing with your original command...')\n}\n","import path from 'path'\nimport { getLogger } from '../utils/logger-context.js'\nimport type { IssueTracker } from '../lib/IssueTracker.js'\nimport { GitHubService } from '../lib/GitHubService.js'\nimport { LoomManager } from '../lib/LoomManager.js'\nimport { DefaultBranchNamingService } from '../lib/BranchNamingService.js'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { EnvironmentManager } from '../lib/EnvironmentManager.js'\nimport { ClaudeContextManager } from '../lib/ClaudeContextManager.js'\nimport { ProjectCapabilityDetector } from '../lib/ProjectCapabilityDetector.js'\nimport { CLIIsolationManager } from '../lib/CLIIsolationManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { AgentManager } from '../lib/AgentManager.js'\nimport { DatabaseManager } from '../lib/DatabaseManager.js'\nimport { findMainWorktreePathWithSettings } from '../utils/git.js'\nimport { matchIssueIdentifier } from '../utils/IdentifierParser.js'\nimport { loadEnvIntoProcess } from '../utils/env.js'\nimport { extractSettingsOverrides } from '../utils/cli-overrides.js'\nimport { createNeonProviderFromSettings } from '../utils/neon-helpers.js'\nimport { getConfiguredRepoFromSettings, hasMultipleRemotes } from '../utils/remote.js'\nimport { capitalizeFirstLetter } from '../utils/text.js'\nimport type { StartOptions, StartResult } from '../types/index.js'\nimport { fetchChildIssues, fetchChildIssueDetails } from '../utils/list-children.js'\nimport { buildDependencyMap } from '../utils/dependency-map.js'\nimport { IssueTrackerFactory } from '../lib/IssueTrackerFactory.js'\nimport { launchFirstRunSetup, needsFirstRunSetup } from '../utils/first-run-setup.js'\nimport { isInteractiveEnvironment, promptConfirmation } from '../utils/prompt.js'\nimport { TelemetryService } from '../lib/TelemetryService.js'\nimport type { LoomCreatedProperties } from '../types/telemetry.js'\n\nexport interface StartCommandInput {\n\tidentifier: string\n\toptions: StartOptions\n}\n\nexport interface ParsedInput {\n\ttype: 'issue' | 'pr' | 'branch' | 'description' | 'epic'\n\tnumber?: string | number\n\tbranchName?: string\n\toriginalInput: string\n}\n\nexport class StartCommand {\n\tprivate issueTracker: IssueTracker\n\tprivate loomManager: LoomManager | null = null\n\tprivate settingsManager: SettingsManager\n\tprivate providedLoomManager: LoomManager | undefined\n\tprivate githubService: GitHubService | null = null\n\n\tconstructor(\n\t\tissueTracker: IssueTracker,\n\t\tloomManager?: LoomManager,\n\t\t_agentManager?: AgentManager, // Kept for API compatibility\n\t\tsettingsManager?: SettingsManager\n\t) {\n\t\tthis.issueTracker = issueTracker\n\t\tthis.settingsManager = settingsManager ?? new SettingsManager()\n\t\t// Store provided LoomManager for testing, but don't initialize yet\n\t\tthis.providedLoomManager = loomManager\n\n\t\t// Load environment variables first\n\t\tconst envResult = loadEnvIntoProcess()\n\t\tif (envResult.error) {\n\t\t\tgetLogger().debug(`Environment loading warning: ${envResult.error.message}`)\n\t\t}\n\t\tif (envResult.parsed) {\n\t\t\tgetLogger().debug(`Loaded ${Object.keys(envResult.parsed).length} environment variables`)\n\t\t}\n\t}\n\n\t/**\n\t * Get or create a GitHubService instance for PR operations\n\t * Used when the configured issue tracker doesn't support PRs (e.g., Linear)\n\t */\n\tprivate getGitHubService(): GitHubService {\n\t\tthis.githubService ??= new GitHubService()\n\t\treturn this.githubService\n\t}\n\n\t/**\n\t * Initialize LoomManager with the main worktree path\n\t * Uses lazy initialization to ensure we have the correct path\n\t */\n\tprivate async initializeLoomManager(): Promise<LoomManager> {\n\t\tif (this.loomManager) {\n\t\t\treturn this.loomManager\n\t\t}\n\n\t\tif (this.providedLoomManager) {\n\t\t\tthis.loomManager = this.providedLoomManager\n\t\t\treturn this.loomManager\n\t\t}\n\n\t\t// Find main worktree path\n\t\tconst mainWorktreePath = await findMainWorktreePathWithSettings()\n\n\t\t// Load settings to get database configuration\n\t\tconst settings = await this.settingsManager.loadSettings()\n\n\t\t// Create DatabaseManager with NeonProvider and EnvironmentManager\n\t\tconst environmentManager = new EnvironmentManager()\n\t\tconst neonProvider = createNeonProviderFromSettings(settings)\n\t\tconst databaseUrlEnvVarName = settings.capabilities?.database?.databaseUrlEnvVarName ?? 'DATABASE_URL'\n\n\t\tconst databaseManager = new DatabaseManager(neonProvider, environmentManager, databaseUrlEnvVarName)\n\n\t\t// Create BranchNamingService (defaults to Claude-based strategy)\n\t\tconst branchNaming = new DefaultBranchNamingService({ useClaude: true })\n\n\t\tthis.loomManager = new LoomManager(\n\t\t\tnew GitWorktreeManager(mainWorktreePath),\n\t\t\tthis.issueTracker,\n\t\t\tbranchNaming, // Add branch naming service\n\t\t\tenvironmentManager, // Reuse same instance\n\t\t\tnew ClaudeContextManager(),\n\t\t\tnew ProjectCapabilityDetector(),\n\t\t\tnew CLIIsolationManager(),\n\t\t\tthis.settingsManager, // Use same instance with CLI overrides\n\t\t\tdatabaseManager // Add database manager\n\t\t)\n\n\t\treturn this.loomManager\n\t}\n\n\t/**\n\t * Main entry point for the start command\n\t */\n\tpublic async execute(input: StartCommandInput): Promise<StartResult | void> {\n\t\tconst isJsonMode = input.options.json === true\n\n\t\ttry {\n\t\t\t// Step 0: Load settings and get configured repo for GitHub operations\n\t\t\tconst initialSettings = await this.settingsManager.loadSettings()\n\n\t\t\t// Skip first-run setup in JSON mode\n\t\t\tif (!isJsonMode && (process.env.FORCE_FIRST_TIME_SETUP === \"true\" || await needsFirstRunSetup())) {\n\t\t\t\tawait launchFirstRunSetup()\n\t\t\t\t// Reload settings and recreate issueTracker if provider changed during setup\n\t\t\t\tconst newSettings = await this.settingsManager.loadSettings()\n\t\t\t\tconst newProvider = newSettings.issueManagement?.provider ?? 'github'\n\t\t\t\tif (newProvider !== this.issueTracker.providerName) {\n\t\t\t\t\tgetLogger().debug(`Reinitializing issue tracker: provider changed to \"${newProvider}\"`)\n\t\t\t\t\tthis.issueTracker = IssueTrackerFactory.create(newSettings)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlet repo: string | undefined\n\n\t\t\t// Only get repo if we have multiple remotes (prehook already validated config)\n\t\t\tif (this.issueTracker.providerName === 'github' && (await hasMultipleRemotes())) {\n\t\t\t\t// Only relevant for GitHub - Linear doesn't use repo info\n\t\t\t\trepo = await getConfiguredRepoFromSettings(initialSettings)\n\t\t\t\tgetLogger().info(`Using GitHub repository: ${repo}`)\n\t\t\t}\n\n\t\t\t// Step 0.5: Initialize LoomManager with main worktree path\n\t\t\tconst loomManager = await this.initializeLoomManager()\n\n\t\t\t// Step 0.6: Detect if running from inside an existing loom (for nested loom support)\n\t\t\tlet parentLoom = await this.detectParentLoom(loomManager)\n\n\t\t\t// Step 1: Parse and validate input (pass repo to methods)\n\t\t\tconst parsed = await this.parseInput(input.identifier, repo)\n\n\t\t\t// Step 2: Validate based on type\n\t\t\tawait this.validateInput(parsed, repo)\n\n\t\t\t// Step 2.4: Handle child loom decision\n\t\t\tif (parentLoom) {\n\n\t\t\t\t// Format display message based on parent type\n\t\t\t\tconst parentDisplay = parentLoom.type === 'issue'\n\t\t\t\t\t? `issue #${parentLoom.identifier}`\n\t\t\t\t\t: parentLoom.type === 'pr'\n\t\t\t\t\t? `PR #${parentLoom.identifier}`\n\t\t\t\t\t: `branch ${parentLoom.identifier}`\n\n\t\t\t\t// Check for explicit flag first\n\t\t\t\tif (input.options.childLoom === true) {\n\t\t\t\t\t// --child-loom flag: force child loom (no prompt)\n\t\t\t\t\tgetLogger().info(`Creating as child loom of ${parentDisplay} (--child-loom flag)`)\n\t\t\t\t} else if (input.options.childLoom === false) {\n\t\t\t\t\t// --no-child-loom flag: force independent (no prompt)\n\t\t\t\t\tparentLoom = null\n\t\t\t\t\tgetLogger().info('Creating as independent loom (--no-child-loom flag)')\n\t\t\t\t} else {\n\t\t\t\t\t// No flag: use existing behavior (prompt or error if non-interactive)\n\t\t\t\t\t// JSON mode requires explicit flag\n\t\t\t\t\tif (isJsonMode) {\n\t\t\t\t\t\tthrow new Error('JSON mode requires explicit --child-loom or --no-child-loom flag when running from inside a loom')\n\t\t\t\t\t}\n\t\t\t\t\tlet createAsChild = true // Default for non-interactive\n\t\t\t\t\tif (isInteractiveEnvironment()) {\n\t\t\t\t\t\tcreateAsChild = await promptConfirmation(\n\t\t\t\t\t\t\t`You are not in your main worktree. Create as a child loom of ${parentDisplay}?`,\n\t\t\t\t\t\t\ttrue // Default yes\n\t\t\t\t\t\t)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new Error('Non-interactive environment detected, use either --child-loom or --no-child-loom to specify behavior')\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!createAsChild) {\n\t\t\t\t\t\tparentLoom = null // User declined, proceed as normal loom\n\t\t\t\t\t\tgetLogger().info('Creating as independent loom')\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (input.options.childLoom === true) {\n\t\t\t\t// --child-loom flag but not in a parent loom - ignore silently (per requirements)\n\t\t\t\tgetLogger().debug('--child-loom flag provided but not running from inside an existing loom (ignored)')\n\t\t\t}\n\t\t\t// Note: --no-child-loom when no parent is a no-op (already independent)\n\n\t\t\t// Step 2.5: Handle description input - create issue\n\t\t\tif (parsed.type === 'description') {\n\t\t\t\tgetLogger().info('Creating issue from description...')\n\t\t\t\t// Apply first-letter capitalization to title and body\n\t\t\t\tconst title = capitalizeFirstLetter(parsed.originalInput)\n\t\t\t\tconst body = input.options.body ? capitalizeFirstLetter(input.options.body) : \"\"\n\t\t\t\tconst result = await this.issueTracker.createIssue(\n\t\t\t\t\ttitle, // Use capitalized description as title\n\t\t\t\t\tbody // Use capitalized body or empty\n\t\t\t\t)\n\t\t\t\tgetLogger().success(`Created issue #${result.number}: ${result.url}`)\n\t\t\t\t// Update parsed to be an issue type with the new number\n\t\t\t\tparsed.type = 'issue'\n\t\t\t\tparsed.number = result.number\n\t\t\t}\n\n\t\t\t// Step 2.6: Detect epic (issue with child issues) and handle --epic/--no-epic flags\n\t\t\tlet childIssueNumbers: string[] = []\n\t\t\tlet childIssues: Array<{ number: string; title: string; body: string; url: string }> = []\n\t\t\tlet dependencyMap: Record<string, string[]> = {}\n\n\t\t\tif (parsed.type === 'issue' && parsed.number) {\n\t\t\t\tconst settings = await this.settingsManager.loadSettings()\n\t\t\t\tconst epicIssueTracker = IssueTrackerFactory.create(settings)\n\t\t\t\tlet children: Awaited<ReturnType<typeof fetchChildIssues>> = []\n\t\t\t\ttry {\n\t\t\t\t\tchildren = await fetchChildIssues(String(parsed.number), epicIssueTracker, repo)\n\t\t\t\t} catch (error) {\n\t\t\t\t\tgetLogger().warn(`Failed to check for child issues: ${error instanceof Error ? error.message : 'Unknown error'}. Proceeding as normal loom.`)\n\t\t\t\t}\n\n\t\t\t\tif (children.length > 0) {\n\t\t\t\t\tchildIssueNumbers = children.map(c => c.id)\n\t\t\t\t\tlet createAsEpic = false\n\n\t\t\t\t\tif (input.options.epic === true) {\n\t\t\t\t\t\t// --epic flag: force epic mode (no prompt)\n\t\t\t\t\t\tcreateAsEpic = true\n\t\t\t\t\t\tgetLogger().info(`Creating as epic loom with ${children.length} child issue(s) (--epic flag)`)\n\t\t\t\t\t} else if (input.options.epic === false) {\n\t\t\t\t\t\t// --no-epic flag: proceed as normal loom (no prompt)\n\t\t\t\t\t\tcreateAsEpic = false\n\t\t\t\t\t\tgetLogger().info('Creating as normal loom (--no-epic flag)')\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// No flag: prompt or error\n\t\t\t\t\t\tif (isJsonMode) {\n\t\t\t\t\t\t\tthrow new Error('JSON mode requires explicit --epic or --no-epic flag when issue has child issues')\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (isInteractiveEnvironment()) {\n\t\t\t\t\t\t\tcreateAsEpic = await promptConfirmation(\n\t\t\t\t\t\t\t\t`This issue has ${children.length} child issue(s). Create as epic loom?`,\n\t\t\t\t\t\t\t\ttrue // Default yes\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthrow new Error('Non-interactive environment detected, use either --epic or --no-epic to specify behavior')\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (createAsEpic) {\n\t\t\t\t\t\tparsed.type = 'epic'\n\n\t\t\t\t\t\t// Fetch rich child issue details and dependency map for epic metadata\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst [details, depMap] = await Promise.all([\n\t\t\t\t\t\t\t\tfetchChildIssueDetails(String(parsed.number), epicIssueTracker, repo),\n\t\t\t\t\t\t\t\tbuildDependencyMap(childIssueNumbers, settings, repo),\n\t\t\t\t\t\t\t])\n\t\t\t\t\t\t\tchildIssues = details ?? []\n\t\t\t\t\t\t\tdependencyMap = depMap ?? {}\n\t\t\t\t\t\t\tgetLogger().info(`Fetched ${childIssues.length} child issue details and dependency map`)\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t// Revert to issue type since child data fetch failed\n\t\t\t\t\t\t\t// il spin needs child data to enter swarm mode, so an epic without it would be broken\n\t\t\t\t\t\t\tparsed.type = 'issue'\n\t\t\t\t\t\t\tchildIssueNumbers = []\n\t\t\t\t\t\t\tgetLogger().warn(`Failed to fetch epic child data, reverting to normal loom: ${error instanceof Error ? error.message : String(error)}`)\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Not creating as epic, clear child issue numbers\n\t\t\t\t\t\tchildIssueNumbers = []\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// --epic or --no-epic flags are silently ignored when there are no child issues\n\t\t\t}\n\n\t\t\t// Step 2.7: Confirm bypassPermissions mode if applicable\n\t\t\t// Only prompt in interactive mode when Claude is enabled.\n\t\t\t// Skip when: --no-claude (Claude won't launch now), JSON mode (non-interactive).\n\t\t\t// The explicit --one-shot=bypassPermissions flag is sufficient intent.\n\t\t\t// The warning is shown again when Claude launches via 'il spin'.\n\t\t\tif (input.options.oneShot === 'bypassPermissions' && input.options.claude !== false && !isJsonMode) {\n\t\t\t\tconst confirmed = await promptConfirmation(\n\t\t\t\t\t'WARNING: bypassPermissions mode will allow Claude to execute all tool calls without confirmation. ' +\n\t\t\t\t\t'This can be dangerous. Do you want to proceed?'\n\t\t\t\t)\n\t\t\t\tif (!confirmed) {\n\t\t\t\t\tgetLogger().info('Operation cancelled by user')\n\t\t\t\t\tprocess.exit(0)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Step 2.8: Load workflow-specific settings with CLI overrides\n\t\t\tconst cliOverrides = extractSettingsOverrides()\n\t\t\tconst settings = await this.settingsManager.loadSettings(undefined, cliOverrides)\n\t\t\tconst workflowType = parsed.type === 'branch' ? 'regular' : parsed.type === 'epic' ? 'issue' : parsed.type\n\t\t\tconst workflowConfig = settings.workflows?.[workflowType]\n\n\t\t\t// Step 2.9: Extract raw --set arguments and executable path for forwarding to spin\n\t\t\tconst { extractRawSetArguments, getExecutablePath } = await import('../utils/cli-overrides.js')\n\t\t\tconst setArguments = extractRawSetArguments()\n\t\t\tconst executablePath = getExecutablePath()\n\n\t\t\t// Step 3: Log success and create loom\n\t\t\tgetLogger().info(`Validated input: ${this.formatParsedInput(parsed)}`)\n\n\t\t\t// Step 4: Create loom using LoomManager\n\t\t\tconst identifier =\n\t\t\t\tparsed.type === 'branch'\n\t\t\t\t\t? parsed.branchName ?? ''\n\t\t\t\t\t: parsed.number ?? 0\n\n\t\t\t// Apply configuration precedence: CLI flags > workflow config > defaults (true)\n\t\t\tconst enableClaude = input.options.claude ?? workflowConfig?.startAiAgent ?? true\n\t\t\tconst enableCode = input.options.code ?? workflowConfig?.startIde ?? true\n\t\t\tconst enableDevServer = input.options.devServer ?? workflowConfig?.startDevServer ?? true\n\t\t\tconst enableTerminal = input.options.terminal ?? workflowConfig?.startTerminal ?? false\n\n\t\t\tgetLogger().debug('Final workflow config values:', {\n\t\t\t\tenableClaude,\n\t\t\t\tenableCode,\n\t\t\t\tenableDevServer,\n\t\t\t\tenableTerminal,\n\t\t\t})\n\n\t\t\tconst loom = await loomManager.createIloom({\n\t\t\t\ttype: parsed.type,\n\t\t\t\tidentifier,\n\t\t\t\toriginalInput: parsed.originalInput,\n\t\t\t\t...(parentLoom && { parentLoom }),\n\t\t\t\toptions: {\n\t\t\t\t\tenableClaude,\n\t\t\t\t\tenableCode,\n\t\t\t\t\tenableDevServer,\n\t\t\t\t\tenableTerminal,\n\t\t\t\t\t...(input.options.oneShot && { oneShot: input.options.oneShot }),\n\t\t\t\t\t...(setArguments.length > 0 && { setArguments }),\n\t\t\t\t\t...(executablePath && { executablePath }),\n\t\t\t\t\t...(childIssueNumbers.length > 0 && { childIssueNumbers }),\n\t\t\t\t\t...(childIssues.length > 0 && { childIssues }),\n\t\t\t\t\t...(Object.keys(dependencyMap).length > 0 && { dependencyMap }),\n\t\t\t\t},\n\t\t\t})\n\n\t\t\tgetLogger().success(`Created loom: ${loom.id} at ${loom.path}`)\n\n\t\t\t// Track loom.created telemetry event\n\t\t\ttry {\n\t\t\t\tconst oneShotMap: Record<string, LoomCreatedProperties['one_shot_mode']> = {\n\t\t\t\t\tnoReview: 'skip-reviews',\n\t\t\t\t\tbypassPermissions: 'yolo',\n\t\t\t\t}\n\t\t\t\tTelemetryService.getInstance().track('loom.created', {\n\t\t\t\t\tsource_type: parsed.type === 'epic' ? 'issue' : parsed.type as LoomCreatedProperties['source_type'],\n\t\t\t\t\ttracker: this.issueTracker.providerName,\n\t\t\t\t\tis_child_loom: !!parentLoom,\n\t\t\t\t\tone_shot_mode: oneShotMap[input.options.oneShot ?? ''] ?? 'default',\n\t\t\t\t})\n\t\t\t} catch (error: unknown) {\n\t\t\t\tgetLogger().debug(`Failed to track loom.created telemetry: ${error instanceof Error ? error.message : String(error)}`)\n\t\t\t}\n\n\t\t\tgetLogger().info(` Branch: ${loom.branch}`)\n\t\t\t// Only show port for web projects\n\t\t\tif (loom.capabilities?.includes('web')) {\n\t\t\t\tgetLogger().info(` Port: ${loom.port}`)\n\t\t\t}\n\t\t\tif (loom.issueData?.title) {\n\t\t\t\tgetLogger().info(` Title: ${loom.issueData.title}`)\n\t\t\t}\n\t\t\tif (parsed.type === 'epic') {\n\t\t\t\tgetLogger().info(` Epic: yes (${childIssueNumbers.length} child issue(s))`)\n\t\t\t}\n\n\t\t\t// Return StartResult in JSON mode\n\t\t\tif (isJsonMode) {\n\t\t\t\treturn {\n\t\t\t\t\tid: loom.id,\n\t\t\t\t\tpath: loom.path,\n\t\t\t\t\tbranch: loom.branch,\n\t\t\t\t\ttype: parsed.type,\n\t\t\t\t\tidentifier: loom.identifier,\n\t\t\t\t\t...(loom.port !== undefined && { port: loom.port }),\n\t\t\t\t\t...(loom.issueData?.title && { title: loom.issueData.title }),\n\t\t\t\t\t...(loom.capabilities && { capabilities: loom.capabilities }),\n\t\t\t\t\t...(childIssueNumbers.length > 0 && { childIssueNumbers }),\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error) {\n\t\t\t\tgetLogger().error(`${error.message}`)\n\t\t\t} else {\n\t\t\t\tgetLogger().error('An unknown error occurred')\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Parse input to determine type and extract relevant data\n\t */\n\tprivate async parseInput(identifier: string, repo?: string): Promise<ParsedInput> {\n\t\t// Check if user wants to skip capitalization by prefixing with space\n\t\t// We preserve this for description types so capitalizeFirstLetter() can handle it\n\t\tconst hasLeadingSpace = identifier.startsWith(' ')\n\n\t\t// Handle empty input\n\t\tconst trimmedIdentifier = identifier.trim()\n\t\tif (!trimmedIdentifier) {\n\t\t\tthrow new Error('Missing required argument: identifier')\n\t\t}\n\n\t\t// Check for description: >15 chars AND has spaces (likely a natural language description)\n\t\t// Short inputs with spaces are rejected later as invalid branch names\n\t\tconst spaceCount = (trimmedIdentifier.match(/ /g) ?? []).length\n\t\tif (trimmedIdentifier.length > 15 && spaceCount >= 1) {\n\t\t\t// Preserve leading space if present so capitalizeFirstLetter() can detect the override\n\t\t\treturn {\n\t\t\t\ttype: 'description',\n\t\t\t\toriginalInput: hasLeadingSpace ? ' ' + trimmedIdentifier : trimmedIdentifier,\n\t\t\t}\n\t\t}\n\n\t\t// Check for PR-specific formats: pr/123, PR-123, PR/123, Pr-123 (case-insensitive)\n\t\tconst prPattern = /^pr[/-](\\d+)$/i\n\t\tconst prMatch = trimmedIdentifier.match(prPattern)\n\t\tif (prMatch?.[1]) {\n\t\t\treturn {\n\t\t\t\ttype: 'pr',\n\t\t\t\tnumber: parseInt(prMatch[1], 10),\n\t\t\t\toriginalInput: trimmedIdentifier,\n\t\t\t}\n\t\t}\n\n\t\t// Check for issue identifier patterns using shared utility\n\t\t// - Project key pattern: ENG-123 (requires at least 2 letters before dash)\n\t\t// - Numeric pattern: #123 or 123 (GitHub format)\n\t\tconst identifierMatch = matchIssueIdentifier(trimmedIdentifier)\n\n\t\tif (identifierMatch.type === 'project-key' && identifierMatch.identifier) {\n\t\t\t// Use IssueTracker to validate it exists\n\t\t\tconst detection = await this.issueTracker.detectInputType(\n\t\t\t\ttrimmedIdentifier,\n\t\t\t\trepo\n\t\t\t)\n\n\t\t\tif (detection.type === 'issue' && detection.identifier) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'issue',\n\t\t\t\t\tnumber: detection.identifier, // Keep as string for project key identifiers\n\t\t\t\t\toriginalInput: trimmedIdentifier,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Project key identifier format matched but not found\n\t\t\tthrow new Error(\n\t\t\t\t`Could not find issue matching identifier ${identifierMatch.identifier}`\n\t\t\t)\n\t\t}\n\n\t\t// Check for numeric pattern (could be issue or PR)\n\t\tif (identifierMatch.type === 'numeric' && identifierMatch.identifier) {\n\t\t\tconst number = parseInt(identifierMatch.identifier, 10)\n\n\t\t\t// If issue tracker supports PRs, use it for detection\n\t\t\tif (this.issueTracker.supportsPullRequests) {\n\t\t\t\tconst detection = await this.issueTracker.detectInputType(\n\t\t\t\t\ttrimmedIdentifier,\n\t\t\t\t\trepo\n\t\t\t\t)\n\n\t\t\t\tif (detection.type === 'pr') {\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttype: 'pr',\n\t\t\t\t\t\tnumber: detection.identifier ? parseInt(detection.identifier, 10) : number,\n\t\t\t\t\t\toriginalInput: trimmedIdentifier,\n\t\t\t\t\t}\n\t\t\t\t} else if (detection.type === 'issue') {\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttype: 'issue',\n\t\t\t\t\t\tnumber: detection.identifier ? parseInt(detection.identifier, 10) : number,\n\t\t\t\t\t\toriginalInput: trimmedIdentifier,\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Error(`Could not find issue or PR #${number}`)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Issue tracker doesn't support PRs (e.g., Linear, Jira)\n\t\t\t\t// Check GitHub first for PR, then fall back to issue tracker for issues\n\t\t\t\tconst githubService = this.getGitHubService()\n\t\t\t\tconst detection = await githubService.detectInputType(trimmedIdentifier, repo)\n\n\t\t\t\tif (detection.type === 'pr') {\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttype: 'pr',\n\t\t\t\t\t\tnumber: detection.identifier ? parseInt(detection.identifier, 10) : number,\n\t\t\t\t\t\toriginalInput: trimmedIdentifier,\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Not a GitHub PR - try the configured issue tracker\n\t\t\t\t\t// This allows future trackers with numeric IDs to work naturally\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttype: 'issue',\n\t\t\t\t\t\tnumber,\n\t\t\t\t\t\toriginalInput: trimmedIdentifier,\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Treat as branch name\n\t\treturn {\n\t\t\ttype: 'branch',\n\t\t\tbranchName: trimmedIdentifier,\n\t\t\toriginalInput: trimmedIdentifier,\n\t\t}\n\t}\n\n\t/**\n\t * Validate the parsed input based on its type\n\t */\n\tprivate async validateInput(parsed: ParsedInput, repo?: string): Promise<void> {\n\t\tswitch (parsed.type) {\n\t\t\tcase 'pr': {\n\t\t\t\tif (!parsed.number) {\n\t\t\t\t\tthrow new Error('Invalid PR number')\n\t\t\t\t}\n\n\t\t\t\t// Determine which service to use for PR operations\n\t\t\t\tif (this.issueTracker.supportsPullRequests && this.issueTracker.fetchPR && this.issueTracker.validatePRState) {\n\t\t\t\t\t// Use issue tracker for PR operations (e.g., GitHub)\n\t\t\t\t\tconst pr = await this.issueTracker.fetchPR(parsed.number, repo)\n\t\t\t\t\tawait this.issueTracker.validatePRState(pr)\n\t\t\t\t} else {\n\t\t\t\t\t// Use GitHubService for PR operations when issue tracker doesn't support PRs (e.g., Linear)\n\t\t\t\t\tconst githubService = this.getGitHubService()\n\t\t\t\t\tconst pr = await githubService.fetchPR(parsed.number as number, repo)\n\t\t\t\t\tawait githubService.validatePRState(pr)\n\t\t\t\t}\n\t\t\t\tgetLogger().debug(`Validated PR #${parsed.number}`)\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tcase 'issue': {\n\t\t\t\tif (!parsed.number) {\n\t\t\t\t\tthrow new Error('Invalid issue number')\n\t\t\t\t}\n\t\t\t\t// Fetch and validate issue state\n\t\t\t\tconst issue = await this.issueTracker.fetchIssue(parsed.number, repo)\n\t\t\t\tawait this.issueTracker.validateIssueState(issue)\n\t\t\t\tgetLogger().debug(`Validated issue #${parsed.number}`)\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tcase 'branch': {\n\t\t\t\tif (!parsed.branchName) {\n\t\t\t\t\tthrow new Error('Invalid branch name')\n\t\t\t\t}\n\t\t\t\t// Validate branch name characters (from bash script line 586)\n\t\t\t\tif (!this.isValidBranchName(parsed.branchName)) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t'Invalid branch name. Use only letters, numbers, hyphens, underscores, and slashes'\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tgetLogger().debug(`Validated branch name: ${parsed.branchName}`)\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tcase 'description': {\n\t\t\t\t// Description inputs are valid - they will be converted to issues\n\t\t\t\tgetLogger().debug('Detected description input', {\n\t\t\t\t\tlength: parsed.originalInput.length\n\t\t\t\t})\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tdefault: {\n\t\t\t\tconst unknownType = parsed as { type: string }\n\t\t\t\tthrow new Error(`Unknown input type: ${unknownType.type}`)\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Validate branch name format\n\t */\n\tprivate isValidBranchName(branch: string): boolean {\n\t\t// Pattern from bash script line 586\n\t\treturn /^[a-zA-Z0-9/_-]+$/.test(branch)\n\t}\n\n\t/**\n\t * Format parsed input for display\n\t */\n\tprivate formatParsedInput(parsed: ParsedInput): string {\n\t\tswitch (parsed.type) {\n\t\t\tcase 'pr':\n\t\t\t\treturn `PR #${parsed.number}`\n\t\t\tcase 'issue':\n\t\t\t\treturn `Issue #${parsed.number}`\n\t\t\tcase 'epic':\n\t\t\t\treturn `Epic #${parsed.number}`\n\t\t\tcase 'branch':\n\t\t\t\treturn `Branch '${parsed.branchName}'`\n\t\t\tcase 'description':\n\t\t\t\treturn `Description: ${parsed.originalInput.slice(0, 50)}...`\n\t\t\tdefault:\n\t\t\t\treturn 'Unknown input'\n\t\t}\n\t}\n\n\t/**\n\t * Detect if running from inside an existing loom worktree\n\t * Returns parent loom info if detected, null otherwise\n\t */\n\tprivate async detectParentLoom(loomManager: LoomManager): Promise<{\n\t\ttype: 'issue' | 'pr' | 'branch' | 'epic'\n\t\tidentifier: string | number\n\t\tbranchName: string\n\t\tworktreePath: string\n\t\tdatabaseBranch?: string\n\t} | null> {\n\t\ttry {\n\t\t\tconst cwd = process.cwd()\n\t\t\tconst looms = await loomManager.listLooms()\n\n\t\t\tif (!looms) {\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\t// Get main worktree path to exclude it from valid parents\n\t\t\tconst mainWorktreePath = await findMainWorktreePathWithSettings()\n\n\t\t\t// Find loom containing current directory\n\t\t\t// Fix #2: Add path.sep check to prevent false positives (e.g., issue-123 vs issue-1234)\n\t\t\t// Exclude main worktree from being a valid parent\n\t\t\tconst parentLoom = looms.find(loom => {\n\t\t\t\t// Skip main worktree - it shouldn't be a parent for child looms\n\t\t\t\tif (loom.path === mainWorktreePath) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\t// Either exact match OR cwd starts with loom.path followed by path separator\n\t\t\t\treturn cwd === loom.path || cwd.startsWith(loom.path + path.sep)\n\t\t\t})\n\t\t\tif (!parentLoom) {\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\tgetLogger().debug(`Detected parent loom: ${parentLoom.type} ${parentLoom.identifier} at ${parentLoom.path}`)\n\n\t\t\tconst result: {\n\t\t\t\ttype: 'issue' | 'pr' | 'branch' | 'epic'\n\t\t\t\tidentifier: string | number\n\t\t\t\tbranchName: string\n\t\t\t\tworktreePath: string\n\t\t\t\tdatabaseBranch?: string\n\t\t\t} = {\n\t\t\t\ttype: parentLoom.type,\n\t\t\t\tidentifier: parentLoom.identifier,\n\t\t\t\tbranchName: parentLoom.branch,\n\t\t\t\tworktreePath: parentLoom.path,\n\t\t\t}\n\n\t\t\t// Only include databaseBranch if it exists (exactOptionalPropertyTypes compatibility)\n\t\t\tif (parentLoom.databaseBranch) {\n\t\t\t\tresult.databaseBranch = parentLoom.databaseBranch\n\t\t\t}\n\n\t\t\t// Try to get database branch from parent's .env file via reverse lookup\n\t\t\tif (!result.databaseBranch) {\n\t\t\t\tconst databaseBranch = await loomManager.getDatabaseBranchForLoom(parentLoom.path)\n\t\t\t\tif (databaseBranch) {\n\t\t\t\t\tresult.databaseBranch = databaseBranch\n\t\t\t\t\tgetLogger().debug(`Detected parent database branch: ${databaseBranch}`)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn result\n\t\t} catch (error) {\n\t\t\t// If detection fails for any reason, just return null (don't break the start workflow)\n\t\t\tgetLogger().debug(`Failed to detect parent loom: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t\t\treturn null\n\t\t}\n\t}\n\n}\n"],"mappings":"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","names":["path","settings","path"]}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
generateIssueManagementMcpConfig
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-SKSYYBCU.js";
|
|
5
5
|
import {
|
|
6
6
|
openBrowser
|
|
7
7
|
} from "./chunk-YETJNRQM.js";
|
|
8
8
|
import {
|
|
9
9
|
launchClaude
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-PZ5WSR5Z.js";
|
|
11
11
|
import {
|
|
12
12
|
getLogger
|
|
13
13
|
} from "./chunk-6MLEBAYZ.js";
|
|
@@ -240,26 +240,7 @@ IMPORTANT: When you create your analysis comment, tag @${author} in the "Questio
|
|
|
240
240
|
}
|
|
241
241
|
};
|
|
242
242
|
|
|
243
|
-
// src/utils/text.ts
|
|
244
|
-
function capitalizeFirstLetter(str) {
|
|
245
|
-
if (!str || str.length === 0) {
|
|
246
|
-
return str;
|
|
247
|
-
}
|
|
248
|
-
if (str.startsWith(" ")) {
|
|
249
|
-
return str.slice(1);
|
|
250
|
-
}
|
|
251
|
-
const firstChar = str.charAt(0);
|
|
252
|
-
const upperChar = firstChar.toUpperCase();
|
|
253
|
-
if (upperChar !== firstChar.toLowerCase() || new RegExp("\\p{L}", "u").test(firstChar)) {
|
|
254
|
-
if (upperChar !== firstChar) {
|
|
255
|
-
return upperChar + str.slice(1);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
return str;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
243
|
export {
|
|
262
|
-
IssueEnhancementService
|
|
263
|
-
capitalizeFirstLetter
|
|
244
|
+
IssueEnhancementService
|
|
264
245
|
};
|
|
265
|
-
//# sourceMappingURL=chunk-
|
|
246
|
+
//# sourceMappingURL=chunk-BFLMCE2U.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/IssueEnhancementService.ts"],"sourcesContent":["import type { IssueTracker } from './IssueTracker.js'\nimport type { AgentManager } from './AgentManager.js'\nimport type { SettingsManager } from './SettingsManager.js'\nimport type { TemplateVariables } from './PromptTemplateManager.js'\nimport { launchClaude } from '../utils/claude.js'\nimport { openBrowser } from '../utils/browser.js'\nimport { waitForKeypress } from '../utils/prompt.js'\nimport { getLogger } from '../utils/logger-context.js'\nimport { generateIssueManagementMcpConfig } from '../utils/mcp.js'\n\n/**\n * Options for enhancing an existing issue\n */\nexport interface EnhanceExistingIssueOptions {\n\t/** GitHub username of issue author for tagging in questions */\n\tauthor?: string\n\t/** Repository in \"owner/repo\" format */\n\trepo?: string\n}\n\n/**\n * Result of enhancing an existing issue\n */\nexport interface EnhanceExistingIssueResult {\n\t/** Whether the issue was enhanced */\n\tenhanced: boolean\n\t/** URL of the comment if enhancement occurred */\n\turl?: string\n}\n\n/**\n * Service for enhancing and creating issues with AI assistance.\n * Extracts reusable issue enhancement logic from StartCommand.\n */\nexport class IssueEnhancementService {\n\tconstructor(\n\t\tprivate issueTrackerService: IssueTracker,\n\t\tprivate agentManager: AgentManager,\n\t\tprivate settingsManager: SettingsManager\n\t) {\n\t\t// No-op - logger now uses AsyncLocalStorage context\n\t}\n\n\t/**\n\t * Expose issue tracker for provider checks\n\t */\n\tpublic get issueTracker(): IssueTracker {\n\t\treturn this.issueTrackerService\n\t}\n\n\t/**\n\t * Validates that a description meets minimum requirements.\n\t *\n\t * When hasBody is false (default): Strict validation - >30 characters AND >2 spaces\n\t * When hasBody is true: Relaxed validation - only requires non-empty description\n\t *\n\t * @param description - The description text to validate\n\t * @param hasBody - If true, skip strict validation (only require non-empty)\n\t */\n\tpublic validateDescription(description: string, hasBody = false): boolean {\n\t\tconst trimmedDescription = description.trim()\n\n\t\t// When --body is provided, only require non-empty description\n\t\tif (hasBody) {\n\t\t\treturn trimmedDescription.length > 0\n\t\t}\n\n\t\t// Standard validation: >30 chars AND >2 spaces\n\t\tconst spaceCount = (trimmedDescription.match(/ /g) ?? []).length\n\t\treturn trimmedDescription.length > 30 && spaceCount > 2\n\t}\n\n\t/**\n\t * Enhances a description using Claude Code in headless mode.\n\t * Falls back to original description if enhancement fails.\n\t */\n\tpublic async enhanceDescription(description: string): Promise<string> {\n\t\ttry {\n\t\t\tgetLogger().info('Enhancing description with Claude Code. This may take a moment...')\n\n\t\t\t// Load only the enhancer agent with template variables so Handlebars expressions resolve\n\t\t\tconst settings = await this.settingsManager.loadSettings()\n\t\t\tconst templateVariables: TemplateVariables = {\n\t\t\t\tSTANDARD_ISSUE_MODE: true,\n\t\t\t\tDIRECT_PROMPT_MODE: true,\n\t\t\t}\n\t\t\tconst loadedAgents = await this.agentManager.loadAgents(\n\t\t\t\tsettings,\n\t\t\t\ttemplateVariables,\n\t\t\t\t['iloom-issue-enhancer.md']\n\t\t\t)\n\t\t\tconst agents = this.agentManager.formatForCli(loadedAgents)\n\n\t\t\t// Call Claude in headless mode with issue enhancer agent\n\t\t\tconst prompt = `@agent-iloom-issue-enhancer\n\nTASK: Enhance the following issue description for the issue tracker.\n\nINPUT:\n${description}\n\nOUTPUT REQUIREMENTS:\n- Return ONLY the enhanced description markdown text\n- Use GitHub-Flavored Markdown syntax ONLY\n- NEVER use Jira Wiki format (e.g., {code}, h1., *bold*, {quote}, [link|url])\n- NO meta-commentary (no \"Here is...\", \"The enhanced...\", \"I have...\", etc)\n- NO code block markers (\\`\\`\\`)\n- NO conversational framing or acknowledgments\n- NO explanations of your work\n- Start your response immediately with the enhanced content\n\nYour response should be the raw markdown that will become the issue body.`\n\n\t\t\tconst enhanced = await launchClaude(prompt, {\n\t\t\t\theadless: true,\n\t\t\t\tmodel: 'sonnet',\n\t\t\t\tagents,\n\t\t\t\tnoSessionPersistence: true, // Utility operation - don't persist session\n\t\t\t})\n\n\t\t\tif (enhanced && typeof enhanced === 'string') {\n\t\t\t\tgetLogger().success('Description enhanced successfully')\n\t\t\t\treturn enhanced\n\t\t\t}\n\n\t\t\t// Fallback to original description\n\t\t\tgetLogger().warn('Claude enhancement returned empty result, using original description')\n\t\t\treturn description\n\t\t} catch (error) {\n\t\t\tgetLogger().warn(`Failed to enhance description: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t\t\treturn description\n\t\t}\n\t}\n\n\t/**\n\t * Creates a GitHub issue with title and enhanced body.\n\t * @param originalDescription - Used as the issue title\n\t * @param enhancedDescription - Used as the issue body\n\t * @param repository - Optional repository override (format: \"owner/repo\")\n\t * @param labels - Optional array of label names to add to the issue\n\t * @returns Issue number and URL\n\t */\n\tpublic async createEnhancedIssue(\n\t\toriginalDescription: string,\n\t\tenhancedDescription: string,\n\t\trepository?: string,\n\t\tlabels?: string[]\n\t): Promise<{ number: string | number; url: string }> {\n\t\tgetLogger().info('Creating issue from description...')\n\n\t\tconst result = await this.issueTrackerService.createIssue(\n\t\t\toriginalDescription, // Use original description as title\n\t\t\tenhancedDescription, // Use enhanced description as body\n\t\t\trepository,\n\t\t\tlabels\n\t\t)\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Waits for user keypress and opens issue in browser for review.\n\t * @param issueNumber - Issue number to open for review\n\t * @param confirm - If true, wait for additional keypress after opening browser before returning\n\t * @param repository - Optional repository to fetch issue from (format: \"owner/repo\")\n\t */\n\tpublic async waitForReviewAndOpen(issueNumber: string | number, confirm = false, repository?: string): Promise<void> {\n\t\t// Check if running in non-interactive environment (CI or no TTY)\n\t\tconst isCI = process.env.CI === 'true'\n\t\tconst isNonInteractive = isCI || !process.stdin.isTTY\n\n\t\tif (isNonInteractive) {\n\t\t\t// In non-interactive environment: Skip all interactive operations\n\t\t\tgetLogger().info(`Running in non-interactive environment - skipping interactive prompts for issue #${issueNumber}`)\n\t\t\treturn\n\t\t}\n\n\t\t// Get issue URL\n\t\tconst issueUrl = await this.issueTrackerService.getIssueUrl(issueNumber, repository)\n\n\t\t// Display message and wait for first keypress\n\t\tconst message = `Created issue #${issueNumber}.\nReview and edit the issue in your browser if needed.\nPress any key to open issue for editing...`\n\t\tawait waitForKeypress(message)\n\n\t\t// Open issue in browser\n\t\tawait openBrowser(issueUrl)\n\n\t\t// If confirmation required, wait for second keypress\n\t\tif (confirm) {\n\t\t\tawait waitForKeypress('Press any key to continue with loom creation...')\n\t\t}\n\t}\n\n\t/**\n\t * Enhances an existing issue using the issue enhancer agent.\n\t * This method encapsulates the Claude invocation, MCP config generation,\n\t * and response parsing for enhancing existing issues.\n\t *\n\t * @param issueNumber - The issue number to enhance\n\t * @param options - Optional enhancement options (author, repo)\n\t * @returns Result indicating whether enhancement occurred and the comment URL if so\n\t */\n\tpublic async enhanceExistingIssue(\n\t\tissueNumber: string | number,\n\t\toptions?: EnhanceExistingIssueOptions\n\t): Promise<EnhanceExistingIssueResult> {\n\t\tconst { author, repo } = options ?? {}\n\n\t\t// Load only the enhancer agent with template variables so Handlebars expressions resolve\n\t\tconst settings = await this.settingsManager.loadSettings()\n\t\tconst templateVariables: TemplateVariables = {\n\t\t\tISSUE_NUMBER: issueNumber,\n\t\t\tSTANDARD_ISSUE_MODE: true,\n\t\t}\n\t\tconst loadedAgents = await this.agentManager.loadAgents(\n\t\t\tsettings,\n\t\t\ttemplateVariables,\n\t\t\t['iloom-issue-enhancer.md']\n\t\t)\n\t\tconst agents = this.agentManager.formatForCli(loadedAgents)\n\n\t\t// Generate MCP config and tool filtering for issue management\n\t\tlet mcpConfig: Record<string, unknown>[] | undefined\n\t\tlet allowedTools: string[] | undefined\n\t\tlet disallowedTools: string[] | undefined\n\n\t\ttry {\n\t\t\tconst provider = this.issueTrackerService.providerName as 'github' | 'linear'\n\t\t\tmcpConfig = await generateIssueManagementMcpConfig('issue', repo, provider, settings)\n\t\t\tgetLogger().debug('Generated MCP configuration for issue management:', { mcpConfig })\n\n\t\t\t// Configure tool filtering for issue workflows\n\t\t\tallowedTools = [\n\t\t\t\t'mcp__issue_management__get_issue',\n\t\t\t\t'mcp__issue_management__get_comment',\n\t\t\t\t'mcp__issue_management__create_comment',\n\t\t\t\t'mcp__issue_management__update_comment',\n\t\t\t\t'mcp__issue_management__create_issue',\n\t\t\t]\n\t\t\tdisallowedTools = ['Bash(gh api:*)']\n\n\t\t\tgetLogger().debug('Configured tool filtering for issue workflow', { allowedTools, disallowedTools })\n\t\t} catch (error) {\n\t\t\t// Log warning but continue without MCP\n\t\t\tgetLogger().warn(`Failed to generate MCP config: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t\t}\n\n\t\t// Construct prompt for the orchestrating Claude instance\n\t\tconst prompt = this.constructEnhancerPrompt(issueNumber, author)\n\n\t\t// Invoke Claude CLI with enhancer agent\n\t\tconst response = await launchClaude(prompt, {\n\t\t\theadless: true,\n\t\t\tmodel: 'sonnet',\n\t\t\tagents,\n\t\t\tnoSessionPersistence: true, // Headless operation - no session persistence needed\n\t\t\t...(mcpConfig && { mcpConfig }),\n\t\t\t...(allowedTools && { allowedTools }),\n\t\t\t...(disallowedTools && { disallowedTools }),\n\t\t})\n\n\t\t// Parse response to determine outcome\n\t\treturn this.parseEnhancerResponse(response)\n\t}\n\n\t/**\n\t * Construct the prompt for the orchestrating Claude instance.\n\t * This prompt is very clear about expected output format to ensure reliable parsing.\n\t */\n\tprivate constructEnhancerPrompt(issueNumber: string | number, author?: string): string {\n\t\tconst authorInstruction = author\n\t\t\t? `\\nIMPORTANT: When you create your analysis comment, tag @${author} in the \"Questions for Reporter\" section if you have questions.\\n`\n\t\t\t: ''\n\n\t\treturn `Execute @agent-iloom-issue-enhancer ${issueNumber}${authorInstruction}\n\n## OUTPUT REQUIREMENTS\n* If the issue was not enhanced, return ONLY: \"No enhancement needed\"\n* If the issue WAS enhanced, return ONLY: <FULL URL OF THE COMMENT INCLUDING COMMENT ID>\n* If you encounter permission/authentication/access errors, return ONLY: \"Permission denied: <specific error description>\"\n* IMPORTANT: Return ONLY one of the above - DO NOT include commentary such as \"I created a comment at <URL>\" or \"I examined the issue and found no enhancement was necessary\"\n* CONTEXT: Your output is going to be parsed programmatically, so adherence to the output requirements is CRITICAL.`\n\t}\n\n\t/**\n\t * Parse the response from the enhancer agent.\n\t * Returns either { enhanced: false } or { enhanced: true, url: \"...\" }\n\t * Throws specific errors for permission issues.\n\t */\n\tprivate parseEnhancerResponse(response: string | void): EnhanceExistingIssueResult {\n\t\t// Handle empty or void response\n\t\tif (!response || typeof response !== 'string') {\n\t\t\tthrow new Error('No response from enhancer agent')\n\t\t}\n\n\t\tconst trimmed = response.trim()\n\n\t\tgetLogger().debug(`RESPONSE FROM ENHANCER AGENT: '${trimmed}'`)\n\n\t\t// Check for permission denied errors (case-insensitive)\n\t\tif (trimmed.toLowerCase().startsWith('permission denied:')) {\n\t\t\tconst errorMessage = trimmed.substring('permission denied:'.length).trim()\n\t\t\tthrow new Error(`Permission denied: ${errorMessage}`)\n\t\t}\n\n\t\t// Check for \"No enhancement needed\" (case-insensitive)\n\t\tif (trimmed.toLowerCase().includes('no enhancement needed')) {\n\t\t\treturn { enhanced: false }\n\t\t}\n\n\t\t// Check if response looks like a GitHub comment URL\n\t\tconst urlPattern = /https:\\/\\/github\\.com\\/[^/]+\\/[^/]+\\/issues\\/\\d+#issuecomment-\\d+/\n\t\tconst match = trimmed.match(urlPattern)\n\n\t\tif (match) {\n\t\t\treturn { enhanced: true, url: match[0] }\n\t\t}\n\n\t\t// Unexpected response format\n\t\tthrow new Error(`Unexpected response from enhancer agent: ${trimmed}`)\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -3,13 +3,13 @@ import {
|
|
|
3
3
|
detectClaudeCli,
|
|
4
4
|
launchClaude,
|
|
5
5
|
launchClaudeInNewTerminalWindow
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-PZ5WSR5Z.js";
|
|
7
7
|
import {
|
|
8
8
|
PromptTemplateManager
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-UUEW5KWB.js";
|
|
10
10
|
import {
|
|
11
11
|
SettingsManager
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-7VHJNVLF.js";
|
|
13
13
|
import {
|
|
14
14
|
logger
|
|
15
15
|
} from "./chunk-VT4PDUYT.js";
|
|
@@ -121,4 +121,4 @@ var ClaudeService = class {
|
|
|
121
121
|
export {
|
|
122
122
|
ClaudeService
|
|
123
123
|
};
|
|
124
|
-
//# sourceMappingURL=chunk-
|
|
124
|
+
//# sourceMappingURL=chunk-BU53XIGY.js.map
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
PromptTemplateManager,
|
|
4
4
|
buildReviewTemplateVariables
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-UUEW5KWB.js";
|
|
6
6
|
import {
|
|
7
7
|
logger
|
|
8
8
|
} from "./chunk-VT4PDUYT.js";
|
|
@@ -274,4 +274,4 @@ var AgentManager = class {
|
|
|
274
274
|
export {
|
|
275
275
|
AgentManager
|
|
276
276
|
};
|
|
277
|
-
//# sourceMappingURL=chunk-
|
|
277
|
+
//# sourceMappingURL=chunk-C6HNNJIV.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ProcessManager
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-G5V75JD5.js";
|
|
5
5
|
import {
|
|
6
6
|
detectPackageManager,
|
|
7
7
|
runScript
|
|
@@ -217,4 +217,4 @@ var DevServerManager = class {
|
|
|
217
217
|
export {
|
|
218
218
|
DevServerManager
|
|
219
219
|
};
|
|
220
|
-
//# sourceMappingURL=chunk-
|
|
220
|
+
//# sourceMappingURL=chunk-CVCTIDDK.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
calculatePortFromIdentifier
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-LLHXQS3C.js";
|
|
5
5
|
|
|
6
6
|
// src/lib/process/ProcessManager.ts
|
|
7
7
|
import { execa } from "execa";
|
|
@@ -179,4 +179,4 @@ var ProcessManager = class {
|
|
|
179
179
|
export {
|
|
180
180
|
ProcessManager
|
|
181
181
|
};
|
|
182
|
-
//# sourceMappingURL=chunk-
|
|
182
|
+
//# sourceMappingURL=chunk-G5V75JD5.js.map
|
|
@@ -10,10 +10,10 @@ import {
|
|
|
10
10
|
import {
|
|
11
11
|
detectClaudeCli,
|
|
12
12
|
launchClaude
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-PZ5WSR5Z.js";
|
|
14
14
|
import {
|
|
15
15
|
executeGitCommand
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-4FGEGQW4.js";
|
|
17
17
|
import {
|
|
18
18
|
getLogger
|
|
19
19
|
} from "./chunk-6MLEBAYZ.js";
|
|
@@ -35,10 +35,12 @@ var ValidationRunner = class {
|
|
|
35
35
|
async runValidations(worktreePath, options = {}) {
|
|
36
36
|
const startTime = Date.now();
|
|
37
37
|
const steps = [];
|
|
38
|
+
const { jsonStream } = options;
|
|
38
39
|
if (!options.skipTypecheck) {
|
|
39
40
|
const typecheckResult = await this.runTypecheck(
|
|
40
41
|
worktreePath,
|
|
41
|
-
options.dryRun ?? false
|
|
42
|
+
options.dryRun ?? false,
|
|
43
|
+
{ jsonStream }
|
|
42
44
|
);
|
|
43
45
|
steps.push(typecheckResult);
|
|
44
46
|
if (!typecheckResult.passed && !typecheckResult.skipped) {
|
|
@@ -50,7 +52,7 @@ var ValidationRunner = class {
|
|
|
50
52
|
}
|
|
51
53
|
}
|
|
52
54
|
if (!options.skipLint) {
|
|
53
|
-
const lintResult = await this.runLint(worktreePath, options.dryRun ?? false);
|
|
55
|
+
const lintResult = await this.runLint(worktreePath, options.dryRun ?? false, { jsonStream });
|
|
54
56
|
steps.push(lintResult);
|
|
55
57
|
if (!lintResult.passed && !lintResult.skipped) {
|
|
56
58
|
return { success: false, steps, totalDuration: Date.now() - startTime };
|
|
@@ -59,7 +61,8 @@ var ValidationRunner = class {
|
|
|
59
61
|
if (!options.skipTests) {
|
|
60
62
|
const testResult = await this.runTests(
|
|
61
63
|
worktreePath,
|
|
62
|
-
options.dryRun ?? false
|
|
64
|
+
options.dryRun ?? false,
|
|
65
|
+
{ jsonStream }
|
|
63
66
|
);
|
|
64
67
|
steps.push(testResult);
|
|
65
68
|
if (!testResult.passed && !testResult.skipped) {
|
|
@@ -72,7 +75,7 @@ var ValidationRunner = class {
|
|
|
72
75
|
* Run typecheck validation
|
|
73
76
|
* Prefers 'compile' script over 'typecheck' if both exist
|
|
74
77
|
*/
|
|
75
|
-
async runTypecheck(worktreePath, dryRun) {
|
|
78
|
+
async runTypecheck(worktreePath, dryRun, options = {}) {
|
|
76
79
|
const stepStartTime = Date.now();
|
|
77
80
|
let scriptToRun = null;
|
|
78
81
|
try {
|
|
@@ -130,7 +133,8 @@ var ValidationRunner = class {
|
|
|
130
133
|
const fixed = await this.attemptClaudeFix(
|
|
131
134
|
scriptToRun,
|
|
132
135
|
worktreePath,
|
|
133
|
-
packageManager
|
|
136
|
+
packageManager,
|
|
137
|
+
{ jsonStream: options.jsonStream }
|
|
134
138
|
);
|
|
135
139
|
if (fixed) {
|
|
136
140
|
return {
|
|
@@ -153,7 +157,7 @@ Run '${runCommand}' to see detailed errors.`
|
|
|
153
157
|
/**
|
|
154
158
|
* Run lint validation
|
|
155
159
|
*/
|
|
156
|
-
async runLint(worktreePath, dryRun) {
|
|
160
|
+
async runLint(worktreePath, dryRun, options = {}) {
|
|
157
161
|
const stepStartTime = Date.now();
|
|
158
162
|
try {
|
|
159
163
|
const pkgJson = await getPackageConfig(worktreePath);
|
|
@@ -204,7 +208,8 @@ Run '${runCommand}' to see detailed errors.`
|
|
|
204
208
|
const fixed = await this.attemptClaudeFix(
|
|
205
209
|
"lint",
|
|
206
210
|
worktreePath,
|
|
207
|
-
packageManager
|
|
211
|
+
packageManager,
|
|
212
|
+
{ jsonStream: options.jsonStream }
|
|
208
213
|
);
|
|
209
214
|
if (fixed) {
|
|
210
215
|
return {
|
|
@@ -226,7 +231,7 @@ Run '${runCommand}' to see detailed errors.`
|
|
|
226
231
|
/**
|
|
227
232
|
* Run test validation
|
|
228
233
|
*/
|
|
229
|
-
async runTests(worktreePath, dryRun) {
|
|
234
|
+
async runTests(worktreePath, dryRun, options = {}) {
|
|
230
235
|
const stepStartTime = Date.now();
|
|
231
236
|
try {
|
|
232
237
|
const pkgJson = await getPackageConfig(worktreePath);
|
|
@@ -277,7 +282,8 @@ Run '${runCommand}' to see detailed errors.`
|
|
|
277
282
|
const fixed = await this.attemptClaudeFix(
|
|
278
283
|
"test",
|
|
279
284
|
worktreePath,
|
|
280
|
-
packageManager
|
|
285
|
+
packageManager,
|
|
286
|
+
{ jsonStream: options.jsonStream }
|
|
281
287
|
);
|
|
282
288
|
if (fixed) {
|
|
283
289
|
return {
|
|
@@ -305,7 +311,7 @@ Run '${runCommand}' to see detailed errors.`
|
|
|
305
311
|
* @param packageManager - Detected package manager
|
|
306
312
|
* @returns true if Claude fixed the issue, false otherwise
|
|
307
313
|
*/
|
|
308
|
-
async attemptClaudeFix(validationType, worktreePath, packageManager) {
|
|
314
|
+
async attemptClaudeFix(validationType, worktreePath, packageManager, options = {}) {
|
|
309
315
|
const isClaudeAvailable = await detectClaudeCli();
|
|
310
316
|
if (!isClaudeAvailable) {
|
|
311
317
|
getLogger().debug("Claude CLI not available, skipping auto-fix");
|
|
@@ -318,14 +324,11 @@ Run '${runCommand}' to see detailed errors.`
|
|
|
318
324
|
try {
|
|
319
325
|
await launchClaude(prompt, {
|
|
320
326
|
addDir: worktreePath,
|
|
321
|
-
headless:
|
|
322
|
-
|
|
323
|
-
permissionMode: "acceptEdits",
|
|
324
|
-
// Auto-accept edits
|
|
327
|
+
headless: !!options.jsonStream,
|
|
328
|
+
permissionMode: options.jsonStream ? "bypassPermissions" : "acceptEdits",
|
|
325
329
|
model: "sonnet",
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
// Utility operation - no session persistence needed
|
|
330
|
+
noSessionPersistence: true,
|
|
331
|
+
...options.jsonStream && { passthroughStdout: true }
|
|
329
332
|
});
|
|
330
333
|
getLogger().info(`Re-running ${validationTypeCapitalized} after Claude's fixes...`);
|
|
331
334
|
try {
|
|
@@ -657,8 +660,10 @@ ${trailer} ${options.issuePrefix}${options.issueNumber}`;
|
|
|
657
660
|
timeout: 12e4,
|
|
658
661
|
// 120 second timeout
|
|
659
662
|
appendSystemPrompt: "Output only the requested content. Never include preamble, analysis, or meta-commentary. Your response is used verbatim.",
|
|
660
|
-
noSessionPersistence: true
|
|
663
|
+
noSessionPersistence: true,
|
|
661
664
|
// Utility operation - don't persist session
|
|
665
|
+
env: { CLAUDE_CODE_SIMPLE: "1" }
|
|
666
|
+
// Minimal mode - no MCP, hooks, or CLAUDE.md loading
|
|
662
667
|
};
|
|
663
668
|
getLogger().debug("Claude CLI call parameters:", {
|
|
664
669
|
options: claudeOptions,
|
|
@@ -823,4 +828,4 @@ export {
|
|
|
823
828
|
UserAbortedCommitError,
|
|
824
829
|
CommitManager
|
|
825
830
|
};
|
|
826
|
-
//# sourceMappingURL=chunk-
|
|
831
|
+
//# sourceMappingURL=chunk-HYGUPUV5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/ValidationRunner.ts","../src/types/index.ts","../src/utils/vscode.ts","../src/lib/CommitManager.ts"],"sourcesContent":["import { getLogger } from '../utils/logger-context.js'\nimport { detectPackageManager, runScript } from '../utils/package-manager.js'\nimport { getPackageConfig, hasScript } from '../utils/package-json.js'\nimport { detectClaudeCli, launchClaude } from '../utils/claude.js'\nimport type {\n\tValidationOptions,\n\tValidationResult,\n\tValidationStepResult,\n} from '../types/index.js'\n\n/**\n * ValidationRunner orchestrates pre-merge validation pipeline\n * Runs typecheck, lint, and tests in sequence with fail-fast behavior\n */\nexport class ValidationRunner {\n\tconstructor() {\n\t\t// Uses getLogger() for all logging operations\n\t}\n\n\t/**\n\t * Run all validations in sequence: typecheck → lint → test\n\t * Fails fast on first error\n\t */\n\tasync runValidations(\n\t\tworktreePath: string,\n\t\toptions: ValidationOptions = {}\n\t): Promise<ValidationResult> {\n\t\tconst startTime = Date.now()\n\t\tconst steps: ValidationStepResult[] = []\n\n\t\tconst { jsonStream } = options\n\n\t\t// Run typecheck\n\t\tif (!options.skipTypecheck) {\n\t\t\tconst typecheckResult = await this.runTypecheck(\n\t\t\t\tworktreePath,\n\t\t\t\toptions.dryRun ?? false,\n\t\t\t\t{ jsonStream }\n\t\t\t)\n\t\t\tsteps.push(typecheckResult)\n\n\t\t\tif (!typecheckResult.passed && !typecheckResult.skipped) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tsteps,\n\t\t\t\t\ttotalDuration: Date.now() - startTime,\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Run lint\n\t\tif (!options.skipLint) {\n\t\t\tconst lintResult = await this.runLint(worktreePath, options.dryRun ?? false, { jsonStream })\n\t\t\tsteps.push(lintResult)\n\n\t\t\tif (!lintResult.passed && !lintResult.skipped) {\n\t\t\t\treturn { success: false, steps, totalDuration: Date.now() - startTime }\n\t\t\t}\n\t\t}\n\n\t\t// Run tests\n\t\tif (!options.skipTests) {\n\t\t\tconst testResult = await this.runTests(\n\t\t\t\tworktreePath,\n\t\t\t\toptions.dryRun ?? false,\n\t\t\t\t{ jsonStream }\n\t\t\t)\n\t\t\tsteps.push(testResult)\n\n\t\t\tif (!testResult.passed && !testResult.skipped) {\n\t\t\t\treturn { success: false, steps, totalDuration: Date.now() - startTime }\n\t\t\t}\n\t\t}\n\n\t\treturn { success: true, steps, totalDuration: Date.now() - startTime }\n\t}\n\n\t/**\n\t * Run typecheck validation\n\t * Prefers 'compile' script over 'typecheck' if both exist\n\t */\n\tprivate async runTypecheck(\n\t\tworktreePath: string,\n\t\tdryRun: boolean,\n\t\toptions: { jsonStream?: boolean | undefined } = {}\n\t): Promise<ValidationStepResult> {\n\t\tconst stepStartTime = Date.now()\n\n\t\tlet scriptToRun: 'compile' | 'typecheck' | null = null\n\n\t\ttry {\n\t\t\t// Check for compile and typecheck scripts - prefer compile if both exist\n\t\t\tconst pkgJson = await getPackageConfig(worktreePath)\n\t\t\tconst hasCompileScript = hasScript(pkgJson, 'compile')\n\t\t\tconst hasTypecheckScript = hasScript(pkgJson, 'typecheck')\n\n\t\t\tif (hasCompileScript) {\n\t\t\t\tscriptToRun = 'compile'\n\t\t\t} else if (hasTypecheckScript) {\n\t\t\t\tscriptToRun = 'typecheck'\n\t\t\t}\n\n\t\t\tif (!scriptToRun) {\n\t\t\t\tgetLogger().debug('Skipping typecheck - no compile or typecheck script found')\n\t\t\t\treturn {\n\t\t\t\t\tstep: 'typecheck',\n\t\t\t\t\tpassed: true,\n\t\t\t\t\tskipped: true,\n\t\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Handle missing package.json - skip validation for non-Node.js projects\n\t\t\tif (error instanceof Error && error.message.includes('package.json not found')) {\n\t\t\t\tgetLogger().debug('Skipping typecheck - no package.json found (non-Node.js project)')\n\t\t\t\treturn {\n\t\t\t\t\tstep: 'typecheck',\n\t\t\t\t\tpassed: true,\n\t\t\t\t\tskipped: true,\n\t\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Re-throw other errors\n\t\t\tthrow error\n\t\t}\n\n\t\tconst packageManager = await detectPackageManager(worktreePath)\n\n\t\tif (dryRun) {\n\t\t\tconst command =\n\t\t\t\tpackageManager === 'npm'\n\t\t\t\t\t? `npm run ${scriptToRun}`\n\t\t\t\t\t: `${packageManager} ${scriptToRun}`\n\t\t\tgetLogger().info(`[DRY RUN] Would run: ${command}`)\n\t\t\treturn {\n\t\t\t\tstep: scriptToRun,\n\t\t\t\tpassed: true,\n\t\t\t\tskipped: false,\n\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t}\n\t\t}\n\n\t\tgetLogger().info(`Running ${scriptToRun}...`)\n\n\t\ttry {\n\t\t\tawait runScript(scriptToRun, worktreePath, [], { quiet: true })\n\t\t\tgetLogger().success(`${scriptToRun.charAt(0).toUpperCase() + scriptToRun.slice(1)} passed`)\n\n\t\t\treturn {\n\t\t\t\tstep: scriptToRun,\n\t\t\t\tpassed: true,\n\t\t\t\tskipped: false,\n\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t}\n\t\t} catch {\n\t\t\t// Attempt Claude-assisted fix before failing\n\t\t\tconst fixed = await this.attemptClaudeFix(\n\t\t\t\tscriptToRun,\n\t\t\t\tworktreePath,\n\t\t\t\tpackageManager,\n\t\t\t\t{ jsonStream: options.jsonStream }\n\t\t\t)\n\n\t\t\tif (fixed) {\n\t\t\t\treturn {\n\t\t\t\t\tstep: scriptToRun,\n\t\t\t\t\tpassed: true,\n\t\t\t\t\tskipped: false,\n\t\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Claude couldn't fix - throw original error\n\t\t\tconst runCommand =\n\t\t\t\tpackageManager === 'npm'\n\t\t\t\t\t? `npm run ${scriptToRun}`\n\t\t\t\t\t: `${packageManager} ${scriptToRun}`\n\n\t\t\tconst stepLabel = scriptToRun.charAt(0).toUpperCase() + scriptToRun.slice(1)\n\t\t\tthrow new Error(\n\t\t\t\t`Error: ${stepLabel} failed.\\n` +\n\t\t\t\t\t`Fix type errors before merging.\\n\\n` +\n\t\t\t\t\t`Run '${runCommand}' to see detailed errors.`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Run lint validation\n\t */\n\tprivate async runLint(\n\t\tworktreePath: string,\n\t\tdryRun: boolean,\n\t\toptions: { jsonStream?: boolean | undefined } = {}\n\t): Promise<ValidationStepResult> {\n\t\tconst stepStartTime = Date.now()\n\n\t\ttry {\n\t\t\t// Check if lint script exists\n\t\t\tconst pkgJson = await getPackageConfig(worktreePath)\n\t\t\tconst hasLintScript = hasScript(pkgJson, 'lint')\n\n\t\t\tif (!hasLintScript) {\n\t\t\t\tgetLogger().debug('Skipping lint - no lint script found')\n\t\t\t\treturn {\n\t\t\t\t\tstep: 'lint',\n\t\t\t\t\tpassed: true,\n\t\t\t\t\tskipped: true,\n\t\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Handle missing package.json - skip validation for non-Node.js projects\n\t\t\tif (error instanceof Error && error.message.includes('package.json not found')) {\n\t\t\t\tgetLogger().debug('Skipping lint - no package.json found (non-Node.js project)')\n\t\t\t\treturn {\n\t\t\t\t\tstep: 'lint',\n\t\t\t\t\tpassed: true,\n\t\t\t\t\tskipped: true,\n\t\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Re-throw other errors\n\t\t\tthrow error\n\t\t}\n\n\t\tconst packageManager = await detectPackageManager(worktreePath)\n\n\t\tif (dryRun) {\n\t\t\tconst command =\n\t\t\t\tpackageManager === 'npm' ? 'npm run lint' : `${packageManager} lint`\n\t\t\tgetLogger().info(`[DRY RUN] Would run: ${command}`)\n\t\t\treturn {\n\t\t\t\tstep: 'lint',\n\t\t\t\tpassed: true,\n\t\t\t\tskipped: false,\n\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t}\n\t\t}\n\n\t\tgetLogger().info('Running lint...')\n\n\t\ttry {\n\t\t\tawait runScript('lint', worktreePath, [], { quiet: true })\n\t\t\tgetLogger().success('Linting passed')\n\n\t\t\treturn {\n\t\t\t\tstep: 'lint',\n\t\t\t\tpassed: true,\n\t\t\t\tskipped: false,\n\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t}\n\t\t} catch {\n\t\t\t// Attempt Claude-assisted fix before failing\n\t\t\tconst fixed = await this.attemptClaudeFix(\n\t\t\t\t'lint',\n\t\t\t\tworktreePath,\n\t\t\t\tpackageManager,\n\t\t\t\t{ jsonStream: options.jsonStream }\n\t\t\t)\n\n\t\t\tif (fixed) {\n\t\t\t\t// logger.success('Linting passed after Claude auto-fix')\n\t\t\t\treturn {\n\t\t\t\t\tstep: 'lint',\n\t\t\t\t\tpassed: true,\n\t\t\t\t\tskipped: false,\n\t\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Claude couldn't fix - throw original error\n\t\t\tconst runCommand =\n\t\t\t\tpackageManager === 'npm' ? 'npm run lint' : `${packageManager} lint`\n\n\t\t\tthrow new Error(\n\t\t\t\t`Error: Linting failed.\\n` +\n\t\t\t\t\t`Fix linting errors before merging.\\n\\n` +\n\t\t\t\t\t`Run '${runCommand}' to see detailed errors.`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Run test validation\n\t */\n\tprivate async runTests(\n\t\tworktreePath: string,\n\t\tdryRun: boolean,\n\t\toptions: { jsonStream?: boolean | undefined } = {}\n\t): Promise<ValidationStepResult> {\n\t\tconst stepStartTime = Date.now()\n\n\t\ttry {\n\t\t\t// Check if test script exists\n\t\t\tconst pkgJson = await getPackageConfig(worktreePath)\n\t\t\tconst hasTestScript = hasScript(pkgJson, 'test')\n\n\t\t\tif (!hasTestScript) {\n\t\t\t\tgetLogger().debug('Skipping tests - no test script found')\n\t\t\t\treturn {\n\t\t\t\t\tstep: 'test',\n\t\t\t\t\tpassed: true,\n\t\t\t\t\tskipped: true,\n\t\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Handle missing package.json - skip validation for non-Node.js projects\n\t\t\tif (error instanceof Error && error.message.includes('package.json not found')) {\n\t\t\t\tgetLogger().debug('Skipping tests - no package.json found (non-Node.js project)')\n\t\t\t\treturn {\n\t\t\t\t\tstep: 'test',\n\t\t\t\t\tpassed: true,\n\t\t\t\t\tskipped: true,\n\t\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Re-throw other errors\n\t\t\tthrow error\n\t\t}\n\n\t\tconst packageManager = await detectPackageManager(worktreePath)\n\n\t\tif (dryRun) {\n\t\t\tconst command =\n\t\t\t\tpackageManager === 'npm' ? 'npm run test' : `${packageManager} test`\n\t\t\tgetLogger().info(`[DRY RUN] Would run: ${command}`)\n\t\t\treturn {\n\t\t\t\tstep: 'test',\n\t\t\t\tpassed: true,\n\t\t\t\tskipped: false,\n\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t}\n\t\t}\n\n\t\tgetLogger().info('Running tests...')\n\n\t\ttry {\n\t\t\tawait runScript('test', worktreePath, [], { quiet: true })\n\t\t\tgetLogger().success('Tests passed')\n\n\t\t\treturn {\n\t\t\t\tstep: 'test',\n\t\t\t\tpassed: true,\n\t\t\t\tskipped: false,\n\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t}\n\t\t} catch {\n\t\t\t// Attempt Claude-assisted fix before failing\n\t\t\tconst fixed = await this.attemptClaudeFix(\n\t\t\t\t'test',\n\t\t\t\tworktreePath,\n\t\t\t\tpackageManager,\n\t\t\t\t{ jsonStream: options.jsonStream }\n\t\t\t)\n\n\t\t\tif (fixed) {\n\t\t\t\t// logger.success('Tests passed after Claude auto-fix')\n\t\t\t\treturn {\n\t\t\t\t\tstep: 'test',\n\t\t\t\t\tpassed: true,\n\t\t\t\t\tskipped: false,\n\t\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Claude couldn't fix - throw original error\n\t\t\tconst runCommand =\n\t\t\t\tpackageManager === 'npm' ? 'npm run test' : `${packageManager} test`\n\n\t\t\tthrow new Error(\n\t\t\t\t`Error: Tests failed.\\n` +\n\t\t\t\t\t`Fix test failures before merging.\\n\\n` +\n\t\t\t\t\t`Run '${runCommand}' to see detailed errors.`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Attempt to fix validation errors using Claude\n\t * Pattern based on MergeManager.attemptClaudeConflictResolution\n\t *\n\t * @param validationType - Type of validation that failed ('compile' | 'typecheck' | 'lint' | 'test')\n\t * @param worktreePath - Path to the worktree\n\t * @param packageManager - Detected package manager\n\t * @returns true if Claude fixed the issue, false otherwise\n\t */\n\tprivate async attemptClaudeFix(\n\t\tvalidationType: 'compile' | 'typecheck' | 'lint' | 'test',\n\t\tworktreePath: string,\n\t\tpackageManager: string,\n\t\toptions: { jsonStream?: boolean | undefined } = {}\n\t): Promise<boolean> {\n\t\t// Check if Claude CLI is available\n\t\tconst isClaudeAvailable = await detectClaudeCli()\n\t\tif (!isClaudeAvailable) {\n\t\t\tgetLogger().debug('Claude CLI not available, skipping auto-fix')\n\t\t\treturn false\n\t\t}\n\n\t\t// Build validation command for the prompt\n\t\tconst validationCommand = this.getValidationCommand(validationType, packageManager)\n\n\t\t// Build prompt based on validation type (matching bash script prompts)\n\t\tconst prompt = this.getClaudePrompt(validationType, validationCommand)\n\n\t\tconst validationTypeCapitalized = validationType.charAt(0).toUpperCase() + validationType.slice(1)\n\t\tgetLogger().info(`Launching Claude to help fix ${validationTypeCapitalized} errors...`)\n\n\t\ttry {\n\t\t\t// When jsonStream is true, run Claude headless with stdout passthrough for JSONL streaming\n\t\t\t// Otherwise, launch interactively in the current terminal\n\t\t\tawait launchClaude(prompt, {\n\t\t\t\taddDir: worktreePath,\n\t\t\t\theadless: !!options.jsonStream,\n\t\t\t\tpermissionMode: options.jsonStream ? 'bypassPermissions' : 'acceptEdits',\n\t\t\t\tmodel: 'sonnet',\n\t\t\t\tnoSessionPersistence: true,\n\t\t\t\t...(options.jsonStream && { passthroughStdout: true }),\n\t\t\t})\n\n\t\t\t// After Claude completes, re-run validation to verify fix\n\t\t\tgetLogger().info(`Re-running ${validationTypeCapitalized} after Claude's fixes...`)\n\n\t\t\ttry {\n\t\t\t\tawait runScript(validationType, worktreePath, [], { quiet: true })\n\t\t\t\t// Validation passed after Claude fix\n\t\t\t\tgetLogger().success(`${validationTypeCapitalized} passed after Claude auto-fix`)\n\t\t\t\treturn true\n\t\t\t} catch {\n\t\t\t\t// Validation still failing after Claude's attempt\n\t\t\t\tgetLogger().warn(`${validationTypeCapitalized} still failing after Claude's help`)\n\t\t\t\treturn false\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Claude launch failed or crashed\n\t\t\tgetLogger().warn('Claude auto-fix failed', {\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t})\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Get validation command string for prompts\n\t * Uses il commands for multi-language project support\n\t */\n\tprivate getValidationCommand(\n\t\tvalidationType: 'compile' | 'typecheck' | 'lint' | 'test',\n\t\t_packageManager: string\n\t): string {\n\t\t// Use il commands for consistent multi-language project support\n\t\treturn `il ${validationType}`\n\t}\n\n\t/**\n\t * Get Claude prompt for specific validation type\n\t * Matches bash script prompts exactly\n\t */\n\tprivate getClaudePrompt(\n\t\tvalidationType: 'compile' | 'typecheck' | 'lint' | 'test',\n\t\tvalidationCommand: string\n\t): string {\n\t\tswitch (validationType) {\n\t\t\tcase 'compile':\n\t\t\tcase 'typecheck':\n\t\t\t\treturn (\n\t\t\t\t\t`There are compilation errors in this codebase. ` +\n\t\t\t\t\t`Please analyze the ${validationType} output, identify all type errors, and fix them. ` +\n\t\t\t\t\t`Run '${validationCommand}' to see the errors, then make the necessary code changes to resolve all type issues. ` +\n\t\t\t\t\t`When you are done, tell the user to quit using /exit to continue the validation process.`\n\t\t\t\t)\n\t\t\tcase 'lint':\n\t\t\t\treturn (\n\t\t\t\t\t`There are Lint errors in this codebase. ` +\n\t\t\t\t\t`Please analyze the linting output, identify all linting issues, and fix them. ` +\n\t\t\t\t\t`Run '${validationCommand}' to see the errors, then make the necessary code changes to resolve all linting issues. ` +\n\t\t\t\t\t`Focus on code quality, consistency, and following the project's linting rules. ` +\n\t\t\t\t\t`When you are done, tell the user to quit using /exit to continue the validation process.`\n\t\t\t\t)\n\t\t\tcase 'test':\n\t\t\t\treturn (\n\t\t\t\t\t`There are unit test failures in this codebase. ` +\n\t\t\t\t\t`Please analyze the test output to understand what's failing, then fix the issues. ` +\n\t\t\t\t\t`This might involve updating test code, fixing bugs in the source code, or updating tests to match new behavior. ` +\n\t\t\t\t\t`Run '${validationCommand}' to see the detailed test failures, then make the necessary changes to get all tests passing. ` +\n\t\t\t\t\t`When you are done, tell the user to quit using /exit to continue the validation process.`\n\t\t\t\t)\n\t\t}\n\t}\n}\n","// Core types\nexport interface Workspace {\n id: string\n path: string\n branch: string\n issueNumber?: string | number\n prNumber?: number\n port: number\n databaseBranch?: string\n createdAt: Date\n lastAccessed: Date\n}\n\nexport interface WorkspaceInput {\n identifier: string\n type: 'issue' | 'pr' | 'branch'\n skipClaude?: boolean\n}\n\nexport interface WorkspaceSummary {\n id: string\n issueNumber?: string | number\n prNumber?: number\n title: string\n branch: string\n port: number\n status: 'active' | 'stale' | 'error'\n lastAccessed: string\n}\n\n// Git types\nexport interface Worktree {\n path: string\n branch: string\n commit: string\n isPR: boolean\n prNumber?: number\n issueNumber?: string | number\n port?: number\n}\n\nexport interface GitStatus {\n hasUncommittedChanges: boolean\n unstagedFiles: string[]\n stagedFiles: string[]\n currentBranch: string\n isAheadOfRemote: boolean\n isBehindRemote: boolean\n}\n\n// GitHub types\nexport interface Issue {\n number: string | number\n title: string\n body: string\n state: 'open' | 'closed'\n labels: string[]\n assignees: string[]\n url: string\n}\n\nexport interface PullRequest {\n number: number\n title: string\n body: string\n state: 'open' | 'closed' | 'merged'\n branch: string\n baseBranch: string\n url: string\n isDraft: boolean\n isFork?: boolean\n}\n\n// Issue Tracker types\n/**\n * Generic input detection result for issue trackers\n * String-based identifier to support non-numeric IDs (e.g., Linear \"ENG-123\")\n */\nexport interface IssueTrackerInputDetection {\n\ttype: 'issue' | 'pr' | 'unknown'\n\tidentifier: string | null\n\trawInput: string\n}\n\n/**\n * Re-export branch naming types from branch-naming module\n * These types are provider-agnostic and support all issue trackers\n */\nexport type { BranchNameStrategy, BranchGenerationOptions } from './branch-naming.js'\n\n// Database types\n\n/**\n * Result of database branch deletion operation\n * Distinguishes between successful deletion, branch not found, and errors\n */\nexport interface DatabaseDeletionResult {\n /** Overall operation succeeded (true even if branch didn't exist) */\n success: boolean\n /** True only if a branch was actually deleted */\n deleted: boolean\n /** True if branch didn't exist (not an error, just nothing to do) */\n notFound: boolean\n /** Error message if operation failed */\n error?: string\n /** User declined deletion (for preview databases) */\n userDeclined?: boolean\n /** Name of the branch that was processed */\n branchName?: string\n}\n\nexport interface DatabaseProvider {\n // Core operations\n createBranch(name: string, fromBranch?: string, cwd?: string): Promise<string>\n deleteBranch(name: string, isPreview?: boolean, cwd?: string): Promise<DatabaseDeletionResult>\n getConnectionString(branch: string, cwd?: string): Promise<string>\n listBranches(cwd?: string): Promise<string[]>\n branchExists(name: string, cwd?: string): Promise<boolean>\n\n // Additional operations for Vercel integration and validation\n findPreviewBranch(branchName: string, cwd?: string): Promise<string | null>\n getBranchNameFromEndpoint(endpointId: string, cwd?: string): Promise<string | null>\n sanitizeBranchName(branchName: string): string\n isAuthenticated(cwd?: string): Promise<boolean>\n isCliAvailable(): Promise<boolean>\n\n // Configuration validation\n isConfigured(): boolean\n}\n\n// Configuration types\nexport interface Config {\n defaultPort: number\n databaseProvider?: 'neon' | 'supabase' | 'planetscale'\n claudeModel?: 'opus' | 'sonnet' | 'haiku'\n skipClaude?: boolean\n customWorkspaceRoot?: string\n}\n\n// One-shot automation mode type\nexport type OneShotMode = 'default' | 'noReview' | 'bypassPermissions'\n\n// Command option types\nexport interface StartOptions {\n // Individual component flags (can be combined)\n claude?: boolean\n code?: boolean\n devServer?: boolean\n terminal?: boolean\n // Child loom control flag\n childLoom?: boolean\n // Epic loom control flag (for issues with child issues)\n epic?: boolean\n // One-shot automation mode\n oneShot?: OneShotMode\n // Optional body text for issue creation\n body?: string\n // Output result as JSON\n json?: boolean\n}\n\nexport interface AddIssueOptions {\n // Optional body text for issue (skips AI enhancement)\n body?: string\n // Output result as JSON\n json?: boolean\n}\n\nexport interface FeedbackOptions {\n // Optional body text for feedback (added after diagnostics)\n body?: string\n}\n\nexport interface EnhanceOptions {\n noBrowser?: boolean // --no-browser flag - skip browser opening prompt\n json?: boolean // --json flag - output result as JSON\n}\n\nexport interface FinishOptions {\n force?: boolean // -f, --force - Skip confirmation prompts\n dryRun?: boolean // -n, --dry-run - Preview actions without executing\n pr?: number // --pr <number> - Treat input as PR number\n skipBuild?: boolean // --skip-build - Skip post-merge build verification\n noBrowser?: boolean // --no-browser - Skip opening PR in browser (github-pr mode only)\n cleanup?: boolean // --cleanup / --no-cleanup - Control worktree cleanup after finishing\n json?: boolean // --json - Output result as JSON\n skipToPr?: boolean // --skip-to-pr - Skip rebase/validation/commit, go directly to PR creation (debug)\n jsonStream?: boolean // --json-stream - Stream JSONL output for Claude conflict resolution\n review?: boolean // --review - Review commit message before committing (default: auto-commit without review)\n}\n\n/**\n * Options for the cleanup command\n * All flags are optional and can be combined (subject to validation)\n */\nexport interface CleanupOptions {\n /** List all worktrees without removing anything */\n list?: boolean\n /** Remove all worktrees (interactive confirmation required unless --force) */\n all?: boolean\n /** Cleanup by specific issue number */\n issue?: number\n /** Skip confirmations and force removal */\n force?: boolean\n /** Show what would be done without actually doing it */\n dryRun?: boolean\n /** Output result as JSON */\n json?: boolean\n /** Wait specified milliseconds before cleanup execution */\n defer?: number\n /** Archive metadata instead of deleting (preserves loom in il list --finished) */\n archive?: boolean\n}\n\nexport interface ListOptions {\n json?: boolean\n}\n\n// JSON output result types for add-issue and enhance commands\nexport interface AddIssueResult {\n url: string\n id: number\n title: string\n created_at: string\n}\n\nexport interface EnhanceResult {\n url: string\n id: number\n title: string\n created_at: string\n enhanced: boolean\n}\n\nexport interface StartResult {\n id: string\n path: string\n branch: string\n port?: number\n type: 'issue' | 'pr' | 'branch' | 'epic'\n identifier: string | number\n title?: string\n capabilities?: string[]\n childIssueNumbers?: string[]\n}\n\nexport interface FinishResult {\n success: boolean\n type: 'issue' | 'pr' | 'branch' | 'epic'\n identifier: string | number\n /** Whether this was a dry-run operation */\n dryRun?: boolean\n operations: Array<{\n type: 'validation' | 'commit' | 'rebase' | 'merge' | 'cleanup' | 'pr-creation' | 'pr-ready' | 'build'\n message: string\n success: boolean\n error?: string\n }>\n prUrl?: string\n cleanupResult?: import('./cleanup.js').CleanupResult\n}\n\nexport interface SummaryResult {\n summary: string\n sessionId: string\n issueNumber?: string | number\n branchName: string\n loomType: 'issue' | 'pr' | 'branch' | 'epic'\n}\n\nexport interface RebaseResult {\n success: boolean\n conflictsDetected: boolean\n claudeLaunched: boolean\n conflictsResolved?: boolean\n error?: string\n}\n\nexport interface RebaseOutcome {\n conflictsDetected: boolean\n claudeLaunched: boolean\n conflictsResolved: boolean\n}\n\n// Deprecated: Result types - use exception-based error handling instead\n// export type Result<T, E = Error> = { success: true; data: T } | { success: false; error: E }\n\n// Mock factory types for testing\nexport interface MockOptions {\n scenario: 'empty' | 'existing' | 'conflicts' | 'error'\n data?: unknown\n}\n\n// Worktree management types\nexport * from './worktree.js'\n\n// Environment management types\nexport * from './environment.js'\n\n// Loom types\nexport * from './loom.js'\n\n// Cleanup types\nexport * from './cleanup.js'\n\n// Process types (excluding Platform which is already defined above)\nexport type { ProcessInfo } from './process.js'\n\n// Color synchronization types\nexport interface RgbColor {\n\tr: number\n\tg: number\n\tb: number\n}\n\nexport interface ColorData {\n\trgb: RgbColor\n\thex: string\n\tindex: number\n}\n\nexport type Platform = 'darwin' | 'linux' | 'win32' | 'unsupported'\n\n// Validation types\nexport interface ValidationOptions {\n\tdryRun?: boolean\n\tskipTypecheck?: boolean\n\tskipLint?: boolean\n\tskipTests?: boolean\n\tjsonStream?: boolean\n}\n\nexport interface ValidationStepResult {\n\tstep: 'typecheck' | 'compile' | 'lint' | 'test'\n\tpassed: boolean\n\tskipped: boolean\n\toutput?: string\n\terror?: string\n\tduration?: number\n}\n\nexport interface ValidationResult {\n\tsuccess: boolean\n\tsteps: ValidationStepResult[]\n\ttotalDuration: number\n}\n\n// Commit management types\nexport interface CommitOptions {\n\tdryRun?: boolean\n\tissueNumber?: string | number // For \"Fixes #N\" or \"Refs #N\" trailer\n\tissuePrefix: string // \"#\" for GitHub, \"\" for Linear\n\tmessage?: string // Custom message override\n\tnoReview?: boolean // Skip user review of commit message\n\tskipVerify?: boolean // Skip pre-commit hooks (--no-verify flag)\n\tskipVerifySilent?: boolean // Skip without warning (for --wip-commit)\n\ttrailerType?: 'Refs' | 'Fixes' // Trailer type: \"Refs\" references issue, \"Fixes\" closes it (default: 'Fixes' for backward compat)\n\ttimeout?: number // Timeout in milliseconds for commit operation\n}\n\n/**\n * Error thrown when user aborts a commit operation\n * Used by CommitManager when user selects 'abort' at the commit prompt\n */\nexport class UserAbortedCommitError extends Error {\n\tconstructor(message = 'User aborted the commit') {\n\t\tsuper(message)\n\t\tthis.name = 'UserAbortedCommitError'\n\t}\n}\n\n// Merge management types\nexport interface MergeOptions {\n\tdryRun?: boolean // Preview actions without executing\n\tforce?: boolean // Skip confirmation prompts\n\trepoRoot?: string // Repository root path (optional, auto-detected if not provided)\n\tjsonStream?: boolean // When true, run Claude headless and stream JSONL for conflict resolution\n}\n\nexport interface MergeResult {\n\tsuccess: boolean\n\tbranchName: string\n\tcommitsMerged: number\n\trebaseCompleted: boolean\n\tmergeCompleted: boolean\n}\n\n// Update notification types\nexport interface UpdateCheckCache {\n\tlastCheck: number // Unix timestamp\n\tlatestVersion: string\n}\n\nexport interface UpdateCheckResult {\n\tcurrentVersion: string\n\tlatestVersion: string\n\tupdateAvailable: boolean\n}\n\nexport type InstallationMethod = 'global' | 'local' | 'linked' | 'unknown'\n\n// Telemetry types\nexport * from './telemetry.js'\n","import { execa } from 'execa'\nimport { logger } from './logger.js'\n\n/**\n * Check if running inside VSCode's integrated terminal\n * VSCode sets TERM_PROGRAM=vscode in its integrated terminal\n */\nexport function isRunningInVSCode(): boolean {\n\treturn process.env.TERM_PROGRAM === 'vscode'\n}\n\n/**\n * Check if running inside Cursor's integrated terminal\n * Cursor sets CURSOR_TRACE_ID environment variable in its terminal\n * Note: Cursor may also set TERM_PROGRAM=vscode, so this check should be done first\n */\nexport function isRunningInCursor(): boolean {\n\treturn !!process.env.CURSOR_TRACE_ID\n}\n\n/**\n * Check if running inside Antigravity's integrated terminal\n * Antigravity sets ANTIGRAVITY_CLI_ALIAS environment variable\n * Note: This check should be done FIRST before Cursor and VSCode\n */\nexport function isRunningInAntigravity(): boolean {\n\treturn !!process.env.ANTIGRAVITY_CLI_ALIAS\n}\n\n/**\n * Check if VSCode command-line tool is available\n */\nexport async function isVSCodeAvailable(): Promise<boolean> {\n\ttry {\n\t\tawait execa('command', ['-v', 'code'], {\n\t\t\tshell: true,\n\t\t\ttimeout: 5000,\n\t\t})\n\t\treturn true\n\t} catch (error) {\n\t\tlogger.debug('VSCode CLI not available', { error })\n\t\treturn false\n\t}\n}\n\n/**\n * Check if Cursor command-line tool is available\n */\nexport async function isCursorAvailable(): Promise<boolean> {\n\ttry {\n\t\tawait execa('command', ['-v', 'cursor'], {\n\t\t\tshell: true,\n\t\t\ttimeout: 5000,\n\t\t})\n\t\treturn true\n\t} catch (error) {\n\t\tlogger.debug('Cursor CLI not available', { error })\n\t\treturn false\n\t}\n}\n\n/**\n * Check if Antigravity command-line tool is available\n */\nexport async function isAntigravityAvailable(): Promise<boolean> {\n\ttry {\n\t\tawait execa('command', ['-v', 'agy'], {\n\t\t\tshell: true,\n\t\t\ttimeout: 5000,\n\t\t})\n\t\treturn true\n\t} catch (error) {\n\t\tlogger.debug('Antigravity CLI not available', { error })\n\t\treturn false\n\t}\n}\n\n/**\n * Open VSCode window for workspace\n * Throws error if VSCode not available\n */\nexport async function openVSCodeWindow(workspacePath: string): Promise<void> {\n\t// Check availability first\n\tconst available = await isVSCodeAvailable()\n\tif (!available) {\n\t\tthrow new Error(\n\t\t\t'VSCode is not available. Please install VSCode and ensure the \"code\" command is in your PATH.\\n' +\n\t\t\t\t'Install command-line tools: Open VSCode > Command Palette > \"Shell Command: Install \\'code\\' command in PATH\"'\n\t\t)\n\t}\n\n\ttry {\n\t\t// Launch VSCode with workspace path\n\t\tawait execa('code', [workspacePath])\n\t\tlogger.debug(`Opened VSCode for workspace: ${workspacePath}`)\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Failed to open VSCode: ${error instanceof Error ? error.message : 'Unknown error'}`\n\t\t)\n\t}\n}\n","import { executeGitCommand } from '../utils/git.js'\nimport { getLogger } from '../utils/logger-context.js'\nimport { launchClaude, detectClaudeCli } from '../utils/claude.js'\nimport { promptCommitAction } from '../utils/prompt.js'\nimport { isRunningInVSCode, isVSCodeAvailable, isRunningInCursor, isCursorAvailable, isRunningInAntigravity, isAntigravityAvailable } from '../utils/vscode.js'\nimport { UserAbortedCommitError } from '../types/index.js'\nimport type { GitStatus, CommitOptions } from '../types/index.js'\nimport { writeFile, readFile, unlink } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { execa } from 'execa'\n\n/**\n * CommitManager handles uncommitted changes detection and auto-commit\n * Ports logic from bash/merge-and-clean.sh lines 610-643\n */\nexport class CommitManager {\n constructor() {\n // Uses getLogger() for all logging operations\n }\n\n /**\n * Detect uncommitted changes in a worktree\n * Parses git status --porcelain output into structured GitStatus\n */\n async detectUncommittedChanges(worktreePath: string): Promise<GitStatus> {\n // Execute: git status --porcelain\n const porcelainOutput = await executeGitCommand(['status', '--porcelain'], {\n cwd: worktreePath,\n })\n\n // Parse output to get staged and unstaged files\n const { stagedFiles, unstagedFiles } = this.parseGitStatus(porcelainOutput)\n\n // Get current branch name\n const currentBranch = await executeGitCommand(['branch', '--show-current'], {\n cwd: worktreePath,\n })\n\n return {\n hasUncommittedChanges: stagedFiles.length > 0 || unstagedFiles.length > 0,\n unstagedFiles,\n stagedFiles,\n currentBranch: currentBranch.trim(),\n // Defer these to future enhancement\n isAheadOfRemote: false,\n isBehindRemote: false,\n }\n }\n\n\n /**\n * Stage all changes and commit with Claude-generated or simple message\n * Tries Claude first, falls back to simple message if Claude unavailable or fails\n * Returns the commit message that was used\n */\n async commitChanges(worktreePath: string, options: CommitOptions): Promise<{ message: string }> {\n // Step 1: Check dry-run mode\n if (options.dryRun) {\n getLogger().info('[DRY RUN] Would run: git add -A')\n getLogger().info('[DRY RUN] Would generate commit message with Claude (if available)')\n const fallbackMessage = this.generateFallbackMessage(options)\n const verifyFlag = options.skipVerify ? ' --no-verify' : ''\n getLogger().info(`[DRY RUN] Would commit with message${verifyFlag}: ${fallbackMessage}`)\n return { message: fallbackMessage }\n }\n\n // Step 2: Stage all changes\n await executeGitCommand(['add', '-A'], { cwd: worktreePath })\n\n // Step 3: Generate commit message (try Claude first, fallback to simple)\n let message: string | null = null\n\n // Skip Claude if custom message provided\n if (!options.message) {\n try {\n message = await this.generateClaudeCommitMessage(worktreePath, options.issueNumber, options.issuePrefix, options.trailerType)\n } catch (error) {\n getLogger().debug('Claude commit message generation failed, using fallback', { error })\n }\n }\n\n // Fallback to simple message if Claude failed or unavailable\n message ??= this.generateFallbackMessage(options)\n\n // Step 4: Log warning if --no-verify is configured (but not for silent skip like --wip-commit)\n if (options.skipVerify && !options.skipVerifySilent) {\n getLogger().warn('Skipping pre-commit hooks (--no-verify configured in settings)')\n }\n\n // Step 5: Commit with user review via prompt (unless noReview specified)\n try {\n if (options.noReview || options.message) {\n // Direct commit without review (custom message or noReview flag)\n const commitArgs = ['commit', '-m', message]\n if (options.skipVerify) {\n commitArgs.push('--no-verify')\n }\n await executeGitCommand(commitArgs, { cwd: worktreePath, ...(options.timeout !== undefined && { timeout: options.timeout }) })\n } else {\n // Prompt user for action instead of going straight to editor\n const action = await promptCommitAction(message)\n\n if (action === 'abort') {\n throw new UserAbortedCommitError()\n }\n\n if (action === 'accept') {\n // Direct commit with -m flag (no editor)\n const commitArgs = ['commit', '-m', message]\n if (options.skipVerify) {\n commitArgs.push('--no-verify')\n }\n await executeGitCommand(commitArgs, { cwd: worktreePath, ...(options.timeout !== undefined && { timeout: options.timeout }) })\n } else {\n // action === 'edit': Use git editor for user review\n getLogger().info('Opening editor for commit message review...')\n\n // Check for Antigravity FIRST as it takes priority\n if (isRunningInAntigravity() && await isAntigravityAvailable()) {\n await this.commitWithExternalEditor(worktreePath, message, options, 'agy', 'Antigravity')\n // Check for Cursor SECOND since it may also set TERM_PROGRAM=vscode\n // Use Cursor-specific flow when running in Cursor terminal\n } else if (isRunningInCursor() && await isCursorAvailable()) {\n await this.commitWithExternalEditor(worktreePath, message, options, 'cursor', 'Cursor')\n // Use VSCode-specific flow when running in VSCode terminal\n // This opens the file in the current VSCode window instead of a random one\n } else if (isRunningInVSCode() && await isVSCodeAvailable()) {\n await this.commitWithExternalEditor(worktreePath, message, options, 'code', 'VSCode')\n } else {\n // Standard git editor flow for non-VSCode/Cursor/Antigravity environments\n const commitArgs = ['commit', '-e', '-m', message]\n if (options.skipVerify) {\n commitArgs.push('--no-verify')\n }\n await executeGitCommand(commitArgs, {\n cwd: worktreePath,\n stdio: 'inherit',\n timeout: Math.max(options.timeout ?? 0, 300000) // Use the larger of configured timeout or 5 minutes for interactive editing\n })\n }\n }\n }\n return { message }\n } catch (error) {\n // Re-throw UserAbortedCommitError as-is\n if (error instanceof UserAbortedCommitError) {\n throw error\n }\n // Handle \"nothing to commit\" scenario gracefully\n if (error instanceof Error && error.message.includes('nothing to commit')) {\n getLogger().info('No changes to commit')\n return { message: '' }\n }\n // Re-throw all other errors (including pre-commit hook failures)\n throw error\n }\n }\n\n\n /**\n * Commit with external editor CLI (VSCode, Cursor, Antigravity, etc.)\n * Handles file creation, editing, and commit to ensure the file opens\n * in the current editor window (preserves IPC context)\n */\n private async commitWithExternalEditor(\n worktreePath: string,\n message: string,\n options: CommitOptions,\n cliCommand: string,\n editorName: string\n ): Promise<void> {\n // Put the commit message file in the worktree root so the editor opens it\n // in the correct window (files within a workspace folder open in that workspace's window)\n const commitMsgPath = join(worktreePath, '.COMMIT_EDITMSG')\n\n // Write the initial commit message (with git-style comments)\n const initialContent = `${message}\n\n# Please enter the commit message for your changes. Lines starting\n# with '#' will be ignored, and an empty message aborts the commit.\n#\n# Save and close the file to complete the commit.\n`\n await writeFile(commitMsgPath, initialContent, 'utf-8')\n\n try {\n getLogger().debug(`Opening commit message in ${editorName}: ${commitMsgPath}`)\n\n // Open the file with --wait flag so we block until the user closes it\n await execa(cliCommand, ['--wait', commitMsgPath], {\n cwd: worktreePath,\n stdio: 'inherit'\n })\n\n // Read the edited message\n const editedContent = await readFile(commitMsgPath, 'utf-8')\n\n // Strip comment lines and trim\n const finalMessage = editedContent\n .split('\\n')\n .filter(line => !line.startsWith('#'))\n .join('\\n')\n .trim()\n\n // Check for empty message (user aborted)\n if (!finalMessage) {\n throw new UserAbortedCommitError()\n }\n\n // Commit with the edited message\n const commitArgs = ['commit', '-F', commitMsgPath]\n if (options.skipVerify) {\n commitArgs.push('--no-verify')\n }\n\n // Rewrite the file without comments for git commit -F\n await writeFile(commitMsgPath, finalMessage, 'utf-8')\n await executeGitCommand(commitArgs, { cwd: worktreePath, ...(options.timeout !== undefined && { timeout: options.timeout }) })\n\n } finally {\n // Clean up - git normally handles this but we should be safe\n try {\n await unlink(commitMsgPath)\n } catch {\n // Ignore cleanup errors\n }\n }\n }\n\n /**\n * Generate simple fallback commit message when Claude unavailable\n * Used as fallback for Claude-powered commit messages\n */\n private generateFallbackMessage(options: CommitOptions): string {\n // If custom message provided, use it\n if (options.message) {\n return options.message\n }\n\n // Generate WIP message\n if (options.issueNumber) {\n const trailer = options.trailerType ?? 'Fixes'\n return `WIP: Auto-commit for issue ${options.issuePrefix}${options.issueNumber}\\n\\n${trailer} ${options.issuePrefix}${options.issueNumber}`\n } else {\n return 'WIP: Auto-commit uncommitted changes'\n }\n }\n\n /**\n * Parse git status --porcelain output\n * Format: \"XY filename\" where X=index, Y=worktree\n * Examples:\n * \"M file.ts\" - staged modification\n * \" M file.ts\" - unstaged modification\n * \"MM file.ts\" - both staged and unstaged\n * \"?? file.ts\" - untracked\n */\n private parseGitStatus(porcelainOutput: string): {\n stagedFiles: string[]\n unstagedFiles: string[]\n } {\n const stagedFiles: string[] = []\n const unstagedFiles: string[] = []\n\n if (!porcelainOutput.trim()) {\n return { stagedFiles, unstagedFiles }\n }\n\n const lines = porcelainOutput.split('\\n').filter((line) => line.trim())\n\n for (const line of lines) {\n if (line.length < 3) continue\n\n const indexStatus = line[0] // First character - staging area status\n const worktreeStatus = line[1] // Second character - working tree status\n const filename = line.substring(3) // Everything after \"XY \"\n\n // Check if file is staged\n // First char != ' ' and != '?' → staged\n if (indexStatus !== ' ' && indexStatus !== '?') {\n stagedFiles.push(filename)\n }\n\n // Check if file is unstaged\n // Second char != ' ' or line starts with '??' → unstaged\n if (worktreeStatus !== ' ' || line.startsWith('??')) {\n unstagedFiles.push(filename)\n }\n }\n\n return { stagedFiles, unstagedFiles }\n }\n\n /**\n * Generate commit message using Claude Code\n * Claude examines the git repository directly via --add-dir option\n * Returns null if Claude unavailable or fails validation\n */\n private async generateClaudeCommitMessage(\n worktreePath: string,\n issueNumber: string | number | undefined,\n issuePrefix: string,\n trailerType?: 'Refs' | 'Fixes'\n ): Promise<string | null> {\n const startTime = Date.now()\n\n if (getLogger().isDebugEnabled()) {\n getLogger().debug('Claude commit message generation started', {\n worktreePath: worktreePath.split('/').pop(), // Just show the folder name for privacy\n issueNumber\n })\n } else {\n getLogger().info('Generating commit message with Claude...')\n }\n\n // Check if Claude CLI is available\n getLogger().debug('Checking Claude CLI availability...')\n const isClaudeAvailable = await detectClaudeCli()\n if (!isClaudeAvailable) {\n getLogger().info('Claude CLI not available, skipping Claude commit message generation')\n return null\n }\n getLogger().debug('Claude CLI is available')\n\n // Build XML-based structured prompt\n getLogger().debug('Building commit message prompt...')\n const prompt = this.buildCommitMessagePrompt(issueNumber, issuePrefix, trailerType)\n getLogger().debug('Prompt built', { promptLength: prompt.length })\n\n // Debug log the actual prompt content for troubleshooting\n getLogger().debug('Claude prompt content:', {\n prompt: prompt,\n truncatedPreview: prompt.substring(0, 500) + (prompt.length > 500 ? '...[truncated]' : '')\n })\n\n try {\n const claudeStartTime = Date.now()\n\n // Debug log the Claude call parameters\n const claudeOptions = {\n headless: true,\n addDir: worktreePath,\n model: 'claude-haiku-4-5-20251001', // Fast, cost-effective model\n timeout: 120000, // 120 second timeout\n appendSystemPrompt: 'Output only the requested content. Never include preamble, analysis, or meta-commentary. Your response is used verbatim.',\n noSessionPersistence: true, // Utility operation - don't persist session\n env: { CLAUDE_CODE_SIMPLE: '1' }, // Minimal mode - no MCP, hooks, or CLAUDE.md loading\n }\n getLogger().debug('Claude CLI call parameters:', {\n options: claudeOptions,\n worktreePathForAnalysis: worktreePath,\n addDirContents: 'Will include entire worktree directory for analysis'\n })\n\n // Launch Claude in headless mode with repository access and shorter timeout for commit messages\n const result = await launchClaude(prompt, claudeOptions)\n\n const claudeDuration = Date.now() - claudeStartTime\n getLogger().debug('Claude API call completed', { duration: `${claudeDuration}ms` })\n\n if (typeof result !== 'string') {\n getLogger().warn('Claude returned non-string result', { resultType: typeof result })\n return null\n }\n\n getLogger().debug('Raw Claude output received', {\n outputLength: result.length,\n preview: result.substring(0, 200) + (result.length > 200 ? '...' : '')\n })\n\n\n // Sanitize output - remove meta-commentary and clean formatting\n getLogger().debug('Sanitizing Claude output...')\n const sanitized = this.sanitizeClaudeOutput(result)\n getLogger().debug('Output sanitized', {\n originalLength: result.length,\n sanitizedLength: sanitized.length,\n sanitized: sanitized.substring(0, 200) + (sanitized.length > 200 ? '...' : '')\n })\n\n // Ensure empty strings are rejected\n if (!sanitized) {\n getLogger().warn('Claude returned empty message after sanitization')\n return null\n }\n\n // Append trailer (e.g., \"Fixes #N\" or \"Refs #N\") if issue number provided\n let finalMessage = sanitized\n if (issueNumber) {\n const trailer = trailerType ?? 'Fixes'\n const trailerRef = `${trailer} ${issuePrefix}${issueNumber}`\n // Add trailer if not already present\n if (!finalMessage.includes(trailerRef)) {\n finalMessage = `${finalMessage}\\n\\n${trailerRef}`\n getLogger().debug(`Added \"${trailerRef}\" trailer to commit message`)\n } else {\n getLogger().debug(`\"${trailerRef}\" already present in commit message`)\n }\n }\n\n const totalDuration = Date.now() - startTime\n if (getLogger().isDebugEnabled()) {\n getLogger().debug('Claude commit message generated', {\n message: finalMessage,\n totalDuration: `${totalDuration}ms`,\n claudeApiDuration: `${claudeDuration}ms`\n })\n } else {\n getLogger().info('Commit message generated')\n }\n\n return finalMessage\n } catch (error) {\n const totalDuration = Date.now() - startTime\n const errorMessage = error instanceof Error ? error.message : 'Unknown error'\n\n if (errorMessage.includes('timed out') || errorMessage.includes('timeout')) {\n getLogger().warn('Claude commit message generation timed out after 45 seconds', {\n totalDuration: `${totalDuration}ms`,\n worktreePath: worktreePath.split('/').pop()\n })\n } else {\n getLogger().warn('Failed to generate commit message with Claude', {\n error: errorMessage,\n totalDuration: `${totalDuration}ms`,\n worktreePath: worktreePath.split('/').pop()\n })\n }\n return null\n }\n }\n\n /**\n * Build structured XML prompt for commit message generation\n * Uses XML format for clear task definition and output expectations\n */\n private buildCommitMessagePrompt(\n issueNumber: string | number | undefined,\n issuePrefix: string,\n trailerType?: 'Refs' | 'Fixes'\n ): string {\n const trailer = trailerType ?? 'Fixes'\n const issueContext = issueNumber\n ? `\\n<IssueContext>\nThis commit is associated with issue ${issuePrefix}${issueNumber}.\n${trailer === 'Fixes' ? 'If the changes appear to resolve the issue, include' : 'Include'} \"${trailer} ${issuePrefix}${issueNumber}\" at the end of the first line of commit message.\n</IssueContext>`\n : ''\n\n const examplePrefix = issuePrefix || '' // Use empty string for Linear examples\n return `<Task>\nYou are a software engineer writing a commit message for this repository.\nExamine the staged changes in the git repository and generate a concise, meaningful commit message.\n</Task>\n\n<Requirements>\n<Format>The first line must be a brief summary of the changes made as a full sentence. If it references an issue, include \"${trailer} ${examplePrefix}N\" at the end of this line.\n\nAdd 2 newlines, then add a bullet-point form description of the changes made, each change on a new line.</Format>\n<Mood>Use imperative mood (e.g., \"Add feature\" not \"Added feature\")</Mood>\n<Focus>Be specific about what was changed and why</Focus>\n<Conciseness>Keep message under 72 characters for subject line when possible</Conciseness>\n<NoMeta>CRITICAL: Do NOT include ANY explanatory text, analysis, or meta-commentary. Output ONLY the raw commit message.</NoMeta>\n<Examples>\nGood: \"Add user authentication with JWT tokens. ${trailer} ${examplePrefix}42\n\n- Implement login and registration endpoints\n- Secure routes with JWT middleware\n- Update user model to store hashed passwords\"\nGood: \"Fix navigation bug in sidebar menu.\"\nBad: \"Based on the changes, I'll create: Add user authentication\"\nBad: \"Looking at the files, this commit should be: Fix navigation bug\"\n</Examples>\n${issueContext}\n</Requirements>\n\n<Output>\nIMPORTANT: Your entire response will be used directly as the git commit message.\nDo not include any explanatory text before or after the commit message.\nStart your response immediately with the commit message text.\n</Output>`\n }\n\n /**\n * Sanitize Claude output to remove meta-commentary and clean formatting\n * Handles cases where Claude includes explanatory text despite instructions\n */\n private sanitizeClaudeOutput(rawOutput: string): string {\n let cleaned = rawOutput.trim()\n\n // Remove common meta-commentary patterns (case-insensitive)\n const metaPatterns = [\n /^.*?based on.*?changes.*?:/i,\n /^.*?looking at.*?files.*?:/i,\n /^.*?examining.*?:/i,\n /^.*?analyzing.*?:/i,\n /^.*?i'll.*?generate.*?:/i,\n /^.*?let me.*?:/i,\n /^.*?the commit message.*?should be.*?:/i,\n /^.*?here.*?is.*?commit.*?message.*?:/i,\n ]\n\n for (const pattern of metaPatterns) {\n cleaned = cleaned.replace(pattern, '').trim()\n }\n\n // Extract content after separators only if it looks like meta-commentary\n // Only split on colons if there's clear meta-commentary before it\n if (cleaned.includes(':')) {\n const colonIndex = cleaned.indexOf(':')\n const beforeColon = cleaned.substring(0, colonIndex).trim().toLowerCase()\n\n // Only split if the text before colon looks like meta-commentary\n const metaIndicators = [\n 'here is the commit message',\n 'commit message',\n 'here is',\n 'the message should be',\n 'i suggest',\n 'my suggestion'\n ]\n\n const isMetaCommentary = metaIndicators.some(indicator => beforeColon.includes(indicator))\n\n if (isMetaCommentary) {\n const afterColon = cleaned.substring(colonIndex + 1).trim()\n if (afterColon && afterColon.length > 10) {\n cleaned = afterColon\n }\n }\n }\n\n // Remove quotes if the entire message is wrapped in them\n if ((cleaned.startsWith('\"') && cleaned.endsWith('\"')) ||\n (cleaned.startsWith(\"'\") && cleaned.endsWith(\"'\"))) {\n cleaned = cleaned.slice(1, -1).trim()\n }\n\n return cleaned\n }\n}\n"],"mappings":"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","names":["execa"]}
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
isPRBranch,
|
|
12
12
|
isValidGitRepo,
|
|
13
13
|
parseWorktreeList
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-4FGEGQW4.js";
|
|
15
15
|
import {
|
|
16
16
|
getLogger
|
|
17
17
|
} from "./chunk-6MLEBAYZ.js";
|
|
@@ -388,4 +388,4 @@ var GitWorktreeManager = class {
|
|
|
388
388
|
export {
|
|
389
389
|
GitWorktreeManager
|
|
390
390
|
};
|
|
391
|
-
//# sourceMappingURL=chunk-
|
|
391
|
+
//# sourceMappingURL=chunk-I5T677EA.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
GitWorktreeManager
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-I5T677EA.js";
|
|
5
5
|
import {
|
|
6
6
|
logger
|
|
7
7
|
} from "./chunk-VT4PDUYT.js";
|
|
@@ -299,4 +299,4 @@ Please consult your shell's documentation for setting up custom completions.
|
|
|
299
299
|
export {
|
|
300
300
|
ShellCompletion
|
|
301
301
|
};
|
|
302
|
-
//# sourceMappingURL=chunk-
|
|
302
|
+
//# sourceMappingURL=chunk-KIK2ZFAL.js.map
|