@iloom/cli 0.13.0-beta.0 → 0.13.0
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 +3 -18
- package/dist/{ClaudeContextManager-RRGREEZQ.js → ClaudeContextManager-ZH6LEA5I.js} +5 -5
- package/dist/{ClaudeService-LEPW6QAC.js → ClaudeService-YR66WXZN.js} +4 -4
- package/dist/{IssueTrackerFactory-KE2BDCLC.js → IssueTrackerFactory-O2ZBA666.js} +3 -3
- package/dist/{LoomLauncher-GKQMR5E6.js → LoomLauncher-V54ENBEF.js} +5 -5
- package/dist/{MetadataManager-V4LSJ2PB.js → MetadataManager-HHE6LQF2.js} +2 -2
- package/dist/{PromptTemplateManager-I75WKXM4.js → PromptTemplateManager-4RFELNYY.js} +2 -2
- package/dist/README.md +3 -18
- package/dist/{SettingsManager-KQU7OX7G.js → SettingsManager-SLSYEYDZ.js} +4 -4
- package/dist/agents/iloom-artifact-reviewer.md +1 -0
- package/dist/agents/iloom-code-reviewer.md +21 -0
- package/dist/agents/iloom-issue-analyze-and-plan.md +30 -12
- package/dist/agents/iloom-issue-analyzer.md +32 -7
- package/dist/agents/iloom-issue-complexity-evaluator.md +32 -12
- package/dist/agents/iloom-issue-implementer.md +31 -12
- package/dist/agents/iloom-issue-planner.md +30 -12
- package/dist/agents/iloom-wave-verifier.md +177 -4
- package/dist/{build-V3KADFMO.js → build-ZTGWDHWU.js} +8 -8
- package/dist/{chunk-VVQQIG64.js → chunk-55NTREIU.js} +33 -30
- package/dist/chunk-55NTREIU.js.map +1 -0
- package/dist/{chunk-AYLC633W.js → chunk-7TN5VW4I.js} +65 -7
- package/dist/chunk-7TN5VW4I.js.map +1 -0
- package/dist/{chunk-RFCAPHL5.js → chunk-C2BVNJW5.js} +2 -2
- package/dist/{chunk-3XEXT35Z.js → chunk-E5OM25WK.js} +3 -3
- package/dist/{chunk-ZUIFO7B4.js → chunk-ERMEYFT6.js} +7 -2
- package/dist/chunk-ERMEYFT6.js.map +1 -0
- package/dist/{chunk-WGUGB54H.js → chunk-F5NKWLMQ.js} +21 -24
- package/dist/chunk-F5NKWLMQ.js.map +1 -0
- package/dist/{chunk-TN2D2RX7.js → chunk-G2DGDCDP.js} +33 -224
- package/dist/chunk-G2DGDCDP.js.map +1 -0
- package/dist/{chunk-NUUFP53X.js → chunk-GPBX2BY2.js} +2 -2
- package/dist/{chunk-SN4S5CWL.js → chunk-GQDVH6FA.js} +2 -2
- package/dist/{chunk-YUOVWWJX.js → chunk-HKEXRZMU.js} +5 -310
- package/dist/chunk-HKEXRZMU.js.map +1 -0
- package/dist/{chunk-TAEVA4QR.js → chunk-HWDQRW3O.js} +3 -3
- package/dist/chunk-HWDQRW3O.js.map +1 -0
- package/dist/{chunk-KQSV7FOG.js → chunk-J5JOJPK3.js} +2 -2
- package/dist/{chunk-PD75ZCFT.js → chunk-KCAWSZUO.js} +18 -17
- package/dist/chunk-KCAWSZUO.js.map +1 -0
- package/dist/{chunk-QNPJXO53.js → chunk-KGOBNC5A.js} +4 -4
- package/dist/{chunk-H3T3EPF3.js → chunk-LNY2Y32V.js} +2 -2
- package/dist/{chunk-7RCUWU3I.js → chunk-MRPIDNZU.js} +1 -1
- package/dist/chunk-MRPIDNZU.js.map +1 -0
- package/dist/{chunk-QQULYI2S.js → chunk-N6DY47YN.js} +80 -45
- package/dist/chunk-N6DY47YN.js.map +1 -0
- package/dist/{chunk-VIQOQ463.js → chunk-OLJ54WGW.js} +15 -10
- package/dist/chunk-OLJ54WGW.js.map +1 -0
- package/dist/{chunk-4VQXMEEP.js → chunk-PPQ5LV7U.js} +3 -3
- package/dist/{chunk-4VQXMEEP.js.map → chunk-PPQ5LV7U.js.map} +1 -1
- package/dist/{chunk-QED2WB2D.js → chunk-PS6K2AOV.js} +5 -5
- package/dist/{chunk-JD3K2344.js → chunk-QNRXRSKC.js} +36 -3
- package/dist/chunk-QNRXRSKC.js.map +1 -0
- package/dist/{chunk-Q7VXHJP6.js → chunk-SM3BCHYB.js} +5 -5
- package/dist/{chunk-SA446KA2.js → chunk-T4KFKKEB.js} +7 -7
- package/dist/{chunk-XCP2WDYA.js → chunk-T4NESGYB.js} +3 -3
- package/dist/{chunk-QXGM32TO.js → chunk-TJDKGKQV.js} +2 -2
- package/dist/{chunk-X5DRLONY.js → chunk-UXBVDD7U.js} +6 -6
- package/dist/{chunk-JDN4SPV3.js → chunk-WYDLOQYO.js} +2 -2
- package/dist/{chunk-4JZEQBWV.js → chunk-XIVLGWUX.js} +3 -1
- package/dist/chunk-XIVLGWUX.js.map +1 -0
- package/dist/{chunk-NTDY5AMO.js → chunk-ZEFTWM5Z.js} +2 -2
- package/dist/{cleanup-RJKLI47I.js → cleanup-BCVY7PEF.js} +22 -22
- package/dist/cleanup-BCVY7PEF.js.map +1 -0
- package/dist/cli.js +126 -105
- package/dist/cli.js.map +1 -1
- package/dist/{commit-SUHRUMDE.js → commit-L5JNBU4U.js} +8 -8
- package/dist/{compile-2MD346PO.js → compile-GPJOHXH4.js} +8 -8
- package/dist/{contribute-P4BMRY7C.js → contribute-QEGCI4PS.js} +4 -4
- package/dist/{dev-server-ZNTLWOL5.js → dev-server-67NPVWUN.js} +247 -31
- package/dist/dev-server-67NPVWUN.js.map +1 -0
- package/dist/{feedback-Q6WG2WX4.js → feedback-2LWXKLQZ.js} +4 -4
- package/dist/{git-TX2IEMB3.js → git-IS7AV3ED.js} +4 -4
- package/dist/hooks/iloom-hook.js +40 -2
- package/dist/{ignite-P644W2PK.js → ignite-VQDJQ37S.js} +12 -14
- package/dist/index.d.ts +73 -75
- package/dist/index.js +32 -32
- package/dist/index.js.map +1 -1
- package/dist/{init-5HFY7JG6.js → init-7SDJUAEZ.js} +8 -8
- package/dist/{install-deps-J4ALTM27.js → install-deps-NGSFDNUW.js} +8 -8
- package/dist/{issues-LZMIF22U.js → issues-4HQKEUP7.js} +5 -5
- package/dist/{lint-XIXKU22H.js → lint-C5FOVRXY.js} +8 -8
- package/dist/mcp/issue-management-server.js +19 -22
- package/dist/mcp/issue-management-server.js.map +1 -1
- package/dist/neon-helpers-LCZAN4U4.js +11 -0
- package/dist/{open-KUO35JIJ.js → open-WUTLRI6S.js} +19 -15
- package/dist/open-WUTLRI6S.js.map +1 -0
- package/dist/{plan-7CF56OIR.js → plan-GC3HF73T.js} +86 -66
- package/dist/plan-GC3HF73T.js.map +1 -0
- package/dist/{projects-L5AHUBGA.js → projects-3F6T3KZL.js} +2 -2
- package/dist/prompts/init-prompt.txt +40 -36
- package/dist/prompts/issue-prompt.txt +4 -1
- package/dist/prompts/plan-prompt.txt +97 -16
- package/dist/prompts/regular-prompt.txt +1 -1
- package/dist/prompts/swarm-orchestrator-prompt.txt +25 -12
- package/dist/{rebase-MAMWPA2L.js → rebase-CSGQICAP.js} +7 -7
- package/dist/{recap-IDBO3KM5.js → recap-CKGKFDJL.js} +7 -7
- package/dist/{run-RGZHCQ6M.js → run-3YL2IXXI.js} +19 -15
- package/dist/run-3YL2IXXI.js.map +1 -0
- package/dist/schema/settings.schema.json +35 -31
- package/dist/{shell-7ADCDFIV.js → shell-M2YYPNGV.js} +6 -6
- package/dist/{summary-7J2HORFD.js → summary-XR4CBJEG.js} +9 -9
- package/dist/{test-SRB7EWU6.js → test-ESDAHEVE.js} +8 -8
- package/dist/{test-git-G7ATVIXG.js → test-git-KWPLHYSI.js} +4 -4
- package/dist/{test-jira-Q2HPA522.js → test-jira-6NK7UHSV.js} +3 -3
- package/dist/{test-prefix-JMDGXR5A.js → test-prefix-VVODGHXP.js} +4 -4
- package/dist/{test-webserver-GZFVXBGD.js → test-webserver-AHXKC6H4.js} +6 -6
- package/dist/{vscode-3I7ISHUU.js → vscode-OY7HOVRO.js} +6 -6
- package/package.json +1 -1
- package/dist/chunk-4JZEQBWV.js.map +0 -1
- package/dist/chunk-7RCUWU3I.js.map +0 -1
- package/dist/chunk-AYLC633W.js.map +0 -1
- package/dist/chunk-JD3K2344.js.map +0 -1
- package/dist/chunk-PD75ZCFT.js.map +0 -1
- package/dist/chunk-QQULYI2S.js.map +0 -1
- package/dist/chunk-TAEVA4QR.js.map +0 -1
- package/dist/chunk-TN2D2RX7.js.map +0 -1
- package/dist/chunk-VIQOQ463.js.map +0 -1
- package/dist/chunk-VVQQIG64.js.map +0 -1
- package/dist/chunk-WGUGB54H.js.map +0 -1
- package/dist/chunk-YUOVWWJX.js.map +0 -1
- package/dist/chunk-ZUIFO7B4.js.map +0 -1
- package/dist/cleanup-RJKLI47I.js.map +0 -1
- package/dist/database-helpers-PRDFNDRO.js +0 -11
- package/dist/dev-server-ZNTLWOL5.js.map +0 -1
- package/dist/open-KUO35JIJ.js.map +0 -1
- package/dist/plan-7CF56OIR.js.map +0 -1
- package/dist/run-RGZHCQ6M.js.map +0 -1
- /package/dist/{ClaudeContextManager-RRGREEZQ.js.map → ClaudeContextManager-ZH6LEA5I.js.map} +0 -0
- /package/dist/{ClaudeService-LEPW6QAC.js.map → ClaudeService-YR66WXZN.js.map} +0 -0
- /package/dist/{IssueTrackerFactory-KE2BDCLC.js.map → IssueTrackerFactory-O2ZBA666.js.map} +0 -0
- /package/dist/{LoomLauncher-GKQMR5E6.js.map → LoomLauncher-V54ENBEF.js.map} +0 -0
- /package/dist/{MetadataManager-V4LSJ2PB.js.map → MetadataManager-HHE6LQF2.js.map} +0 -0
- /package/dist/{PromptTemplateManager-I75WKXM4.js.map → PromptTemplateManager-4RFELNYY.js.map} +0 -0
- /package/dist/{SettingsManager-KQU7OX7G.js.map → SettingsManager-SLSYEYDZ.js.map} +0 -0
- /package/dist/{build-V3KADFMO.js.map → build-ZTGWDHWU.js.map} +0 -0
- /package/dist/{chunk-RFCAPHL5.js.map → chunk-C2BVNJW5.js.map} +0 -0
- /package/dist/{chunk-3XEXT35Z.js.map → chunk-E5OM25WK.js.map} +0 -0
- /package/dist/{chunk-NUUFP53X.js.map → chunk-GPBX2BY2.js.map} +0 -0
- /package/dist/{chunk-SN4S5CWL.js.map → chunk-GQDVH6FA.js.map} +0 -0
- /package/dist/{chunk-KQSV7FOG.js.map → chunk-J5JOJPK3.js.map} +0 -0
- /package/dist/{chunk-QNPJXO53.js.map → chunk-KGOBNC5A.js.map} +0 -0
- /package/dist/{chunk-H3T3EPF3.js.map → chunk-LNY2Y32V.js.map} +0 -0
- /package/dist/{chunk-QED2WB2D.js.map → chunk-PS6K2AOV.js.map} +0 -0
- /package/dist/{chunk-Q7VXHJP6.js.map → chunk-SM3BCHYB.js.map} +0 -0
- /package/dist/{chunk-SA446KA2.js.map → chunk-T4KFKKEB.js.map} +0 -0
- /package/dist/{chunk-XCP2WDYA.js.map → chunk-T4NESGYB.js.map} +0 -0
- /package/dist/{chunk-QXGM32TO.js.map → chunk-TJDKGKQV.js.map} +0 -0
- /package/dist/{chunk-X5DRLONY.js.map → chunk-UXBVDD7U.js.map} +0 -0
- /package/dist/{chunk-JDN4SPV3.js.map → chunk-WYDLOQYO.js.map} +0 -0
- /package/dist/{chunk-NTDY5AMO.js.map → chunk-ZEFTWM5Z.js.map} +0 -0
- /package/dist/{commit-SUHRUMDE.js.map → commit-L5JNBU4U.js.map} +0 -0
- /package/dist/{compile-2MD346PO.js.map → compile-GPJOHXH4.js.map} +0 -0
- /package/dist/{contribute-P4BMRY7C.js.map → contribute-QEGCI4PS.js.map} +0 -0
- /package/dist/{feedback-Q6WG2WX4.js.map → feedback-2LWXKLQZ.js.map} +0 -0
- /package/dist/{database-helpers-PRDFNDRO.js.map → git-IS7AV3ED.js.map} +0 -0
- /package/dist/{git-TX2IEMB3.js.map → ignite-VQDJQ37S.js.map} +0 -0
- /package/dist/{init-5HFY7JG6.js.map → init-7SDJUAEZ.js.map} +0 -0
- /package/dist/{install-deps-J4ALTM27.js.map → install-deps-NGSFDNUW.js.map} +0 -0
- /package/dist/{issues-LZMIF22U.js.map → issues-4HQKEUP7.js.map} +0 -0
- /package/dist/{lint-XIXKU22H.js.map → lint-C5FOVRXY.js.map} +0 -0
- /package/dist/{ignite-P644W2PK.js.map → neon-helpers-LCZAN4U4.js.map} +0 -0
- /package/dist/{projects-L5AHUBGA.js.map → projects-3F6T3KZL.js.map} +0 -0
- /package/dist/{rebase-MAMWPA2L.js.map → rebase-CSGQICAP.js.map} +0 -0
- /package/dist/{recap-IDBO3KM5.js.map → recap-CKGKFDJL.js.map} +0 -0
- /package/dist/{shell-7ADCDFIV.js.map → shell-M2YYPNGV.js.map} +0 -0
- /package/dist/{summary-7J2HORFD.js.map → summary-XR4CBJEG.js.map} +0 -0
- /package/dist/{test-SRB7EWU6.js.map → test-ESDAHEVE.js.map} +0 -0
- /package/dist/{test-git-G7ATVIXG.js.map → test-git-KWPLHYSI.js.map} +0 -0
- /package/dist/{test-jira-Q2HPA522.js.map → test-jira-6NK7UHSV.js.map} +0 -0
- /package/dist/{test-prefix-JMDGXR5A.js.map → test-prefix-VVODGHXP.js.map} +0 -0
- /package/dist/{test-webserver-GZFVXBGD.js.map → test-webserver-AHXKC6H4.js.map} +0 -0
- /package/dist/{vscode-3I7ISHUU.js.map → vscode-OY7HOVRO.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/AgentManager.ts","../src/utils/MarkdownAgentParser.ts"],"sourcesContent":["import { readFile } from 'fs/promises'\nimport { accessSync } from 'fs'\nimport path from 'path'\nimport { fileURLToPath } from 'url'\nimport fg from 'fast-glob'\nimport fs from 'fs-extra'\nimport { MarkdownAgentParser } from '../utils/MarkdownAgentParser.js'\nimport { logger } from '../utils/logger.js'\nimport type { IloomSettings } from './SettingsManager.js'\nimport { PromptTemplateManager, TemplateVariables, buildReviewTemplateVariables } from './PromptTemplateManager.js'\n\n// Agent schema interface\nexport interface AgentConfig {\n\tdescription: string\n\tprompt: string\n\ttools?: string[] // Optional - when omitted, agent inherits all tools from parent\n\tmodel: string\n\tcolor?: string\n}\n\n// Container for all loaded agents (keyed by agent name without extension)\nexport interface AgentConfigs {\n\t[agentName: string]: AgentConfig\n}\n\nexport class AgentManager {\n\tprivate agentDir: string\n\tprivate templateManager: PromptTemplateManager\n\n\tconstructor(agentDir?: string, templateManager?: PromptTemplateManager) {\n\t\tthis.templateManager = templateManager ?? new PromptTemplateManager()\n\t\tif (agentDir) {\n\t\t\tthis.agentDir = agentDir\n\t\t} else {\n\t\t\t// Find agents relative to package installation\n\t\t\t// Same pattern as PromptTemplateManager\n\t\t\t// When running from dist/, agents are copied to dist/agents/\n\t\t\tconst currentFileUrl = import.meta.url\n\t\t\tconst currentFilePath = fileURLToPath(currentFileUrl)\n\t\t\tconst distDir = path.dirname(currentFilePath)\n\n\t\t\t// Walk up to find the agents directory\n\t\t\tlet agentDirPath = path.join(distDir, 'agents')\n\t\t\tlet currentDir = distDir\n\n\t\t\twhile (currentDir !== path.dirname(currentDir)) {\n\t\t\t\tconst candidatePath = path.join(currentDir, 'agents')\n\t\t\t\ttry {\n\t\t\t\t\taccessSync(candidatePath)\n\t\t\t\t\tagentDirPath = candidatePath\n\t\t\t\t\tbreak\n\t\t\t\t} catch {\n\t\t\t\t\tcurrentDir = path.dirname(currentDir)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.agentDir = agentDirPath\n\t\t\tlogger.debug('AgentManager initialized', { agentDir: this.agentDir })\n\t\t}\n\t}\n\n\t/**\n\t * Load agent configuration files from markdown (.md) format\n\t * Optionally apply model overrides from settings and template variable substitution\n\t * Throws error if agents directory doesn't exist or files are malformed\n\t * @param settings - Optional project settings with per-agent model overrides\n\t * @param templateVariables - Optional variables for template substitution in agent prompts\n\t * @param patterns - Optional glob patterns to filter which agents to load (default: ['*.md'])\n\t * Supports negation patterns like ['*.md', '!iloom-framework-detector.md']\n\t */\n\tasync loadAgents(\n\t\tsettings?: IloomSettings,\n\t\ttemplateVariables?: TemplateVariables,\n\t\tpatterns: string[] = ['*.md']\n\t): Promise<AgentConfigs> {\n\t\t// Use fast-glob to filter agent files based on patterns\n\t\tconst agentFiles = await fg(patterns, {\n\t\t\tcwd: this.agentDir,\n\t\t\tonlyFiles: true,\n\t\t})\n\n\t\tconst agents: AgentConfigs = {}\n\n\t\tfor (const filename of agentFiles) {\n\t\t\tconst agentPath = path.join(this.agentDir, filename)\n\n\t\t\ttry {\n\t\t\t\tconst content = await readFile(agentPath, 'utf-8')\n\n\t\t\t\t// Parse markdown with frontmatter\n\t\t\t\tconst parsed = this.parseMarkdownAgent(content, filename)\n\t\t\t\tconst agentConfig = parsed.config\n\t\t\t\tconst agentName = parsed.name\n\n\t\t\t\t// Validate required fields\n\t\t\t\tthis.validateAgentConfig(agentConfig, agentName)\n\n\t\t\t\tagents[agentName] = agentConfig\n\t\t\t\tlogger.debug(`Loaded agent: ${agentName}`)\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(`Failed to load agent from ${filename}`, { error })\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Failed to load agent from ${filename}: ${error instanceof Error ? error.message : 'Unknown error'}`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\t// Apply template variable substitution to agent prompts if variables provided\n\t\tif (templateVariables) {\n\t\t\t// Extract review config from settings and add to template variables\n\t\t\tObject.assign(templateVariables, buildReviewTemplateVariables(!!templateVariables.SWARM_MODE, settings?.agents))\n\n\t\t\tfor (const [agentName, agentConfig] of Object.entries(agents)) {\n\t\t\t\tagents[agentName] = {\n\t\t\t\t\t...agentConfig,\n\t\t\t\t\tprompt: this.templateManager.substituteVariables(agentConfig.prompt, templateVariables),\n\t\t\t\t}\n\t\t\t\tlogger.debug(`Applied template substitution to agent: ${agentName}`)\n\t\t\t}\n\t\t}\n\n\t\t// Apply settings overrides if provided\n\t\tif (settings?.agents) {\n\t\t\tfor (const [agentName, agentSettings] of Object.entries(settings.agents)) {\n\t\t\t\tif (agents[agentName] && agentSettings.model) {\n\t\t\t\t\tlogger.debug(`Overriding model for ${agentName}: ${agents[agentName].model} -> ${agentSettings.model}`)\n\t\t\t\t\tagents[agentName] = {\n\t\t\t\t\t\t...agents[agentName],\n\t\t\t\t\t\tmodel: agentSettings.model,\n\t\t\t\t\t}\n\t\t\t\t} else if (!agents[agentName]) {\n\t\t\t\t\t// Skip warning for runtime-generated agents (e.g., swarm worker)\n\t\t\t\t\tconst RUNTIME_GENERATED_AGENTS = ['iloom-swarm-worker']\n\t\t\t\t\tif (!RUNTIME_GENERATED_AGENTS.includes(agentName)) {\n\t\t\t\t\t\t// Only warn if the agent file doesn't exist at all (typo in settings)\n\t\t\t\t\t\t// Skip warning if the agent exists but wasn't loaded due to pattern filtering\n\t\t\t\t\t\tconst agentFile = path.join(this.agentDir, `${agentName}.md`)\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\taccessSync(agentFile)\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\tlogger.warn(`Settings reference unknown agent: ${agentName}`)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn agents\n\t}\n\n\t/**\n\t * Validate agent configuration has required fields\n\t * Note: tools is optional - when omitted, agent inherits all tools from parent\n\t */\n\tprivate validateAgentConfig(config: AgentConfig, agentName: string): void {\n\t\tconst requiredFields: (keyof AgentConfig)[] = ['description', 'prompt', 'model']\n\n\t\tfor (const field of requiredFields) {\n\t\t\tif (!config[field]) {\n\t\t\t\tthrow new Error(`Agent ${agentName} missing required field: ${field}`)\n\t\t\t}\n\t\t}\n\n\t\t// Tools is optional, but if present must be an array\n\t\tif (config.tools !== undefined && !Array.isArray(config.tools)) {\n\t\t\tthrow new Error(`Agent ${agentName} tools must be an array`)\n\t\t}\n\t}\n\n\t/**\n\t * Parse markdown agent file with YAML frontmatter\n\t * @param content - Raw markdown file content\n\t * @param filename - Original filename for error messages\n\t * @returns Parsed agent config and name\n\t */\n\tprivate parseMarkdownAgent(content: string, filename: string): { config: AgentConfig; name: string } {\n\t\ttry {\n\t\t\t// Parse frontmatter using custom parser\n\t\t\tconst { data, content: markdownBody } = MarkdownAgentParser.parse(content)\n\n\t\t\t// Validate frontmatter has required fields\n\t\t\tif (!data.name) {\n\t\t\t\tthrow new Error('Missing required field: name')\n\t\t\t}\n\t\t\tif (!data.description) {\n\t\t\t\tthrow new Error('Missing required field: description')\n\t\t\t}\n\t\t\t// Note: tools is now optional - when omitted, agent inherits all tools from parent\n\t\t\tif (!data.model) {\n\t\t\t\tthrow new Error('Missing required field: model')\n\t\t\t}\n\n\t\t\t// Parse tools from comma-separated string to array (only if tools field is present)\n\t\t\tlet tools: string[] | undefined\n\t\t\tif (data.tools) {\n\t\t\t\ttools = data.tools\n\t\t\t\t\t.split(',')\n\t\t\t\t\t.map((tool: string) => tool.trim())\n\t\t\t\t\t.filter((tool: string) => tool.length > 0)\n\t\t\t}\n\n\t\t\t// Validate model and warn if non-standard\n\t\t\tconst validModels = ['sonnet', 'opus', 'haiku']\n\t\t\tif (!validModels.includes(data.model)) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Agent ${data.name} uses model \"${data.model}\" which may not be recognized by Claude CLI, and your workflow may fail or produce unexpected results. ` +\n\t\t\t\t\t\t`Valid values are: ${validModels.join(', ')}`\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// Construct AgentConfig\n\t\t\tconst config: AgentConfig = {\n\t\t\t\tdescription: data.description,\n\t\t\t\tprompt: markdownBody.trim(),\n\t\t\t\tmodel: data.model,\n\t\t\t\t...(tools && { tools }),\n\t\t\t\t...(data.color && { color: data.color }),\n\t\t\t}\n\n\t\t\treturn { config, name: data.name }\n\t\t} catch (error) {\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to parse markdown agent ${filename}: ${error instanceof Error ? error.message : 'Unknown error'}`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Format loaded agents for Claude CLI --agents flag\n\t * Returns object suitable for JSON.stringify\n\t */\n\tformatForCli(agents: AgentConfigs): Record<string, unknown> {\n\t\t// The agents object is already in the correct format\n\t\t// Just return it - launchClaude will JSON.stringify it\n\t\treturn agents as Record<string, unknown>\n\t}\n\n\t/**\n\t * Render loaded agents to disk as markdown files with YAML frontmatter.\n\t * Claude Code auto-discovers agents from .claude/agents/ directory.\n\t *\n\t * @param agents - Loaded agent configs (from loadAgents())\n\t * @param targetDir - Absolute path to target directory (e.g., <worktree>/.claude/agents/)\n\t * @returns Array of rendered filenames\n\t */\n\tasync renderAgentsToDisk(agents: AgentConfigs, targetDir: string): Promise<string[]> {\n\t\tawait fs.ensureDir(targetDir)\n\n\t\t// Clean existing iloom agent files to avoid stale agents from previous runs\n\t\tconst existingFiles = await fg('iloom-*.md', { cwd: targetDir, onlyFiles: true })\n\t\tfor (const file of existingFiles) {\n\t\t\tawait fs.remove(path.join(targetDir, file))\n\t\t}\n\n\t\tconst renderedFiles: string[] = []\n\t\tfor (const [agentName, config] of Object.entries(agents)) {\n\t\t\tconst safeName = path.basename(agentName)\n\t\t\tconst filename = `${safeName}.md`\n\t\t\t// Build YAML frontmatter\n\t\t\tconst frontmatterLines = ['---', `name: ${agentName}`, `description: ${config.description}`]\n\t\t\tif (config.tools) frontmatterLines.push(`tools: ${config.tools.join(', ')}`)\n\t\t\tfrontmatterLines.push(`model: ${config.model}`)\n\t\t\tif (config.color) frontmatterLines.push(`color: ${config.color}`)\n\t\t\tfrontmatterLines.push('---')\n\n\t\t\tconst content = frontmatterLines.join('\\n') + '\\n\\n' + config.prompt + '\\n'\n\t\t\tawait fs.writeFile(path.join(targetDir, filename), content, 'utf-8')\n\t\t\trenderedFiles.push(filename)\n\t\t}\n\t\treturn renderedFiles\n\t}\n}\n","/**\n * Custom YAML frontmatter parser for agent markdown files\n * Replaces gray-matter dependency with lightweight custom implementation\n */\n\ninterface ParseResult {\n\tdata: Record<string, string>\n\tcontent: string\n}\n\nexport class MarkdownAgentParser {\n\t/**\n\t * Parse markdown content with YAML frontmatter\n\t * @param content - Raw markdown file content\n\t * @returns Object with parsed frontmatter data and markdown body content\n\t * @throws Error if frontmatter is malformed or missing\n\t */\n\tstatic parse(content: string): ParseResult {\n\t\tconst lines = content.split('\\n')\n\n\t\t// Check for opening frontmatter delimiter\n\t\tif (lines[0]?.trim() !== '---') {\n\t\t\tthrow new Error('Missing opening frontmatter delimiter (---)')\n\t\t}\n\n\t\t// Find closing frontmatter delimiter\n\t\tlet closingDelimiterIndex = -1\n\t\tfor (let i = 1; i < lines.length; i++) {\n\t\t\tif (lines[i]?.trim() === '---') {\n\t\t\t\tclosingDelimiterIndex = i\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif (closingDelimiterIndex === -1) {\n\t\t\tthrow new Error('Missing closing frontmatter delimiter (---)')\n\t\t}\n\n\t\t// Extract frontmatter lines (between the delimiters)\n\t\tconst frontmatterLines = lines.slice(1, closingDelimiterIndex)\n\n\t\t// Extract markdown body (after closing delimiter)\n\t\tconst bodyLines = lines.slice(closingDelimiterIndex + 1)\n\t\tconst markdownBody = bodyLines.join('\\n')\n\n\t\t// Parse YAML frontmatter into key-value pairs\n\t\tconst data = this.parseYaml(frontmatterLines.join('\\n'))\n\n\t\treturn {\n\t\t\tdata,\n\t\t\tcontent: markdownBody,\n\t\t}\n\t}\n\n\t/**\n\t * Parse simplified YAML into key-value object\n\t * Supports:\n\t * - Simple key: value pairs\n\t * - Multiline values with | indicator\n\t * - Values with special characters and newlines\n\t *\n\t * @param yaml - YAML string to parse\n\t * @returns Object with parsed key-value pairs\n\t */\n\tprivate static parseYaml(yaml: string): Record<string, string> {\n\t\tconst result: Record<string, string> = {}\n\t\tconst lines = yaml.split('\\n')\n\t\tlet currentKey: string | null = null\n\t\tlet currentValue: string[] = []\n\t\tlet isMultiline = false\n\n\t\tconst finalizeCurrent = (): void => {\n\t\t\tif (currentKey && currentValue.length > 0) {\n\t\t\t\tresult[currentKey] = currentValue.join('\\n').trim()\n\t\t\t\tcurrentKey = null\n\t\t\t\tcurrentValue = []\n\t\t\t}\n\t\t}\n\n\t\tfor (const line of lines) {\n\t\t\t// Skip empty lines when not in multiline mode\n\t\t\tif (!isMultiline && line.trim() === '') {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Check if this is a new key-value pair\n\t\t\tconst keyValueMatch = line.match(/^([a-zA-Z_][a-zA-Z0-9_-]*)\\s*:\\s*(.*)$/)\n\n\t\t\tif (keyValueMatch && !isMultiline) {\n\t\t\t\t// Finalize previous key if exists\n\t\t\t\tfinalizeCurrent()\n\n\t\t\t\tconst [, key, value] = keyValueMatch\n\t\t\t\tif (!key || value === undefined) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tcurrentKey = key\n\n\t\t\t\t// Check for multiline indicator\n\t\t\t\tif (value.trim() === '|') {\n\t\t\t\t\tisMultiline = true\n\t\t\t\t\tcurrentValue = []\n\t\t\t\t} else {\n\t\t\t\t\t// Single line value\n\t\t\t\t\tcurrentValue = [value]\n\t\t\t\t\tfinalizeCurrent()\n\t\t\t\t\tisMultiline = false\n\t\t\t\t}\n\t\t\t} else if (isMultiline && currentKey) {\n\t\t\t\t// Continuation of multiline value\n\t\t\t\t// Check if we've returned to normal indentation (new key)\n\t\t\t\tif (line.match(/^[a-zA-Z_][a-zA-Z0-9_-]*\\s*:/) && !line.startsWith(' ')) {\n\t\t\t\t\t// End of multiline, this is a new key\n\t\t\t\t\tfinalizeCurrent()\n\t\t\t\t\tisMultiline = false\n\n\t\t\t\t\t// Process this line as a new key\n\t\t\t\t\tconst match = line.match(/^([a-zA-Z_][a-zA-Z0-9_-]*)\\s*:\\s*(.*)$/)\n\t\t\t\t\tif (match) {\n\t\t\t\t\t\tconst [, key, value] = match\n\t\t\t\t\t\tif (key && value !== undefined) {\n\t\t\t\t\t\t\tcurrentKey = key\n\t\t\t\t\t\t\tcurrentValue = [value]\n\t\t\t\t\t\t\tfinalizeCurrent()\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Remove leading spaces (common indentation) from multiline values\n\t\t\t\t\tconst trimmedLine = line.replace(/^ {2}/, '')\n\t\t\t\t\tcurrentValue.push(trimmedLine)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Finalize last key\n\t\tfinalizeCurrent()\n\n\t\treturn result\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
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 { createDatabaseProviderFromSettings } from '../utils/database-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'\nimport { resolveRecapFilePath, readRecapFile, writeRecapFile } from '../utils/mcp.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 databaseProvider = createDatabaseProviderFromSettings(settings)\n\t\tconst databaseUrlEnvVarName = settings.capabilities?.database?.databaseUrlEnvVarName ?? 'DATABASE_URL'\n\n\t\tconst databaseManager = new DatabaseManager(databaseProvider, 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\t// Handle --create-only flag: disable all launch components\n\t\tif (input.options.createOnly) {\n\t\t\tinput.options.claude = false\n\t\t\tinput.options.code = false\n\t\t\tinput.options.devServer = false\n\t\t\tinput.options.terminal = false\n\t\t}\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...(input.options.complexity && { complexity: input.options.complexity }),\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// Set recap complexity if overridden via CLI flag\n\t\t\tif (input.options.complexity) {\n\t\t\t\ttry {\n\t\t\t\t\tconst recapFilePath = resolveRecapFilePath(loom.path)\n\t\t\t\t\tconst recap = await readRecapFile(recapFilePath)\n\t\t\t\t\trecap.complexity = { level: input.options.complexity, reason: 'Overridden via CLI flag', timestamp: new Date().toISOString() }\n\t\t\t\t\tawait writeRecapFile(recapFilePath, recap)\n\t\t\t\t} catch (error) {\n\t\t\t\t\tgetLogger().debug(`Failed to set recap complexity: ${error instanceof Error ? error.message : error}`)\n\t\t\t\t}\n\t\t\t}\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\tcomplexity_override: !!input.options.complexity,\n\t\t\t\t\tcreate_only: !!input.options.createOnly,\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 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/DevServerManager.ts","../src/lib/DockerDevServerStrategy.ts","../src/lib/NativeDevServerStrategy.ts","../src/utils/dev-server.ts"],"sourcesContent":["import path from 'path'\nimport { ProcessManager } from './process/ProcessManager.js'\nimport { DockerManager, type DockerConfig } from './DockerManager.js'\nimport { DockerDevServerStrategy, type DockerConfig as StrategyDockerConfig, type DockerUtils } from './DockerDevServerStrategy.js'\nimport { NativeDevServerStrategy } from './NativeDevServerStrategy.js'\nimport { logger } from '../utils/logger.js'\n\n/**\n * Default startup timeout in milliseconds (180 seconds)\n * Can be overridden via ILOOM_DEV_SERVER_TIMEOUT environment variable\n */\nconst DEFAULT_STARTUP_TIMEOUT = 180000\n\n/**\n * Bridge DockerManager static methods to the DockerUtils interface\n * expected by DockerDevServerStrategy.\n */\nconst dockerUtils: DockerUtils = {\n\tparseDockerfileExpose: (filePath: string) => DockerManager.parseExposeFromDockerfile(filePath),\n\tinspectImagePorts: (imageName: string) => DockerManager.inspectImagePorts(imageName),\n\tbuildContainerName: (id: string | number) => DockerManager.buildContainerName(id),\n\tbuildImageName: (id: string | number) => DockerManager.buildImageName(id),\n\tassertDockerAvailable: () => DockerManager.assertAvailable(),\n}\n\nfunction getStartupTimeout(): number {\n\tconst envTimeout = process.env.ILOOM_DEV_SERVER_TIMEOUT\n\tif (envTimeout) {\n\t\tconst parsed = parseInt(envTimeout, 10)\n\t\tif (!isNaN(parsed) && parsed > 0) {\n\t\t\treturn parsed\n\t\t}\n\t}\n\treturn DEFAULT_STARTUP_TIMEOUT\n}\n\nexport interface DevServerManagerOptions {\n\t/**\n\t * Maximum time to wait for server to start (in milliseconds)\n\t * Default: 180000 (180 seconds)\n\t * Can be overridden via ILOOM_DEV_SERVER_TIMEOUT environment variable\n\t */\n\tstartupTimeout?: number\n\n\t/**\n\t * Interval between port checks (in milliseconds)\n\t * Default: 1000 (1 second)\n\t */\n\tcheckInterval?: number\n}\n\n// Re-export DockerConfig from DockerManager for backward compatibility\nexport type { DockerConfig } from './DockerManager.js'\n\n/**\n * Convert a DockerConfig (from DockerManager) to a StrategyDockerConfig\n * (for DockerDevServerStrategy).\n */\nfunction toStrategyConfig(config: DockerConfig): StrategyDockerConfig {\n\treturn {\n\t\tdockerFile: config.dockerFile,\n\t\tcontainerPort: config.containerPort,\n\t\tbuildArgs: config.dockerBuildArgs,\n\t\trunArgs: config.dockerRunArgs,\n\t\tidentifier: config.identifier,\n\t}\n}\n\n/**\n * DevServerManager handles auto-starting and monitoring dev servers.\n * Used by open/run commands to ensure dev server is running before opening browser.\n *\n * When devServer config is absent OR mode is not 'docker', behavior is identical\n * to the native process-based implementation via NativeDevServerStrategy.\n * When Docker mode is configured, all operations delegate to DockerDevServerStrategy.\n */\nexport class DevServerManager {\n\tprivate readonly processManager: ProcessManager\n\tprivate readonly options: Required<DevServerManagerOptions>\n\tprivate readonly nativeStrategy: NativeDevServerStrategy\n\tprivate runningDockerContainers: Map<number, string> = new Map()\n\n\tconstructor(\n\t\tprocessManager?: ProcessManager,\n\t\toptions: DevServerManagerOptions = {}\n\t) {\n\t\tthis.processManager = processManager ?? new ProcessManager()\n\t\tthis.options = {\n\t\t\tstartupTimeout: options.startupTimeout ?? getStartupTimeout(),\n\t\t\tcheckInterval: options.checkInterval ?? 1000,\n\t\t}\n\t\tthis.nativeStrategy = new NativeDevServerStrategy(\n\t\t\tthis.processManager,\n\t\t\tthis.options.startupTimeout,\n\t\t\tthis.options.checkInterval\n\t\t)\n\t}\n\n\t/**\n\t * Create a DockerDevServerStrategy for the given Docker config.\n\t * The strategy encapsulates all Docker container lifecycle operations.\n\t */\n\tprivate createDockerStrategy(dockerConfig: DockerConfig): DockerDevServerStrategy {\n\t\treturn new DockerDevServerStrategy(toStrategyConfig(dockerConfig), dockerUtils)\n\t}\n\n\t/**\n\t * Ensure dev server is running on the specified port.\n\t * If not running, start it and wait for it to be ready.\n\t *\n\t * @param worktreePath - Path to the worktree\n\t * @param port - Port the server should run on\n\t * @param dockerConfig - Optional Docker configuration for container-based server\n\t * @returns true if server is ready, false if startup failed/timed out\n\t */\n\tasync ensureServerRunning(worktreePath: string, port: number, dockerConfig?: DockerConfig): Promise<boolean> {\n\t\tlogger.debug(`Checking if dev server is running on port ${port}...`)\n\n\t\t// Docker mode: check if container is already running\n\t\tif (dockerConfig) {\n\t\t\tconst strategy = this.createDockerStrategy(dockerConfig)\n\t\t\tconst containerName = dockerUtils.buildContainerName(dockerConfig.identifier)\n\t\t\tconst isRunning = await strategy.isContainerRunning(containerName)\n\t\t\tif (isRunning) {\n\t\t\t\tlogger.debug(`Docker container \"${containerName}\" already running on port ${port}`)\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\tlogger.info(`Docker dev server not running on port ${port}, starting...`)\n\t\t\ttry {\n\t\t\t\tawait this.startDockerServer(worktreePath, port, dockerConfig, strategy)\n\t\t\t\treturn true\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(\n\t\t\t\t\t`Failed to start Docker dev server: ${error instanceof Error ? error.message : 'Unknown error'}`\n\t\t\t\t)\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\n\t\t// Native mode: check if a process is listening on the port\n\t\tconst existingProcess = await this.processManager.detectDevServer(port)\n\t\tif (existingProcess) {\n\t\t\tlogger.debug(\n\t\t\t\t`Dev server already running on port ${port} (PID: ${existingProcess.pid})`\n\t\t\t)\n\t\t\treturn true\n\t\t}\n\n\t\t// Not running - start it\n\t\tlogger.info(`Dev server not running on port ${port}, starting...`)\n\n\t\ttry {\n\t\t\tawait this.nativeStrategy.startBackground(worktreePath, port)\n\t\t\treturn true\n\t\t} catch (error) {\n\t\t\tlogger.error(\n\t\t\t\t`Failed to start dev server: ${error instanceof Error ? error.message : 'Unknown error'}`\n\t\t\t)\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Start dev server in Docker container (background) and wait for it to be ready.\n\t * Builds the image, resolves the container port, starts the container detached,\n\t * and polls the host port for readiness.\n\t */\n\tprivate async startDockerServer(\n\t\tworktreePath: string,\n\t\tport: number,\n\t\tdockerConfig: DockerConfig,\n\t\tstrategy: DockerDevServerStrategy\n\t): Promise<void> {\n\t\tconst strategyConfig = toStrategyConfig(dockerConfig)\n\t\tconst imageName = dockerUtils.buildImageName(dockerConfig.identifier)\n\t\tconst dockerfilePath = path.resolve(worktreePath, dockerConfig.dockerFile)\n\n\t\t// Build image\n\t\tawait strategy.buildImage(worktreePath, strategyConfig)\n\n\t\t// Resolve container port (config > image inspect > Dockerfile EXPOSE)\n\t\tconst containerPort = await strategy.resolveContainerPort(\n\t\t\tstrategyConfig,\n\t\t\timageName,\n\t\t\tdockerfilePath\n\t\t)\n\n\t\t// Run container detached\n\t\tconst containerName = await strategy.runContainerDetached(\n\t\t\tworktreePath,\n\t\t\tport,\n\t\t\tcontainerPort,\n\t\t\tstrategyConfig\n\t\t)\n\n\t\t// Track for cleanup\n\t\tthis.runningDockerContainers.set(port, containerName)\n\n\t\t// Wait for server to be ready via TCP probe (Docker proxy listens on host port)\n\t\t// Pass container name for early crash detection\n\t\tlogger.info(`Waiting for Docker dev server to start on port ${port}...`)\n\t\tconst ready = await strategy.waitForReady(\n\t\t\tport,\n\t\t\tthis.options.startupTimeout,\n\t\t\tthis.options.checkInterval,\n\t\t\tcontainerName\n\t\t)\n\n\t\tif (!ready) {\n\t\t\t// Clean up the container if startup failed\n\t\t\tawait strategy.stopContainer(containerName)\n\t\t\tthis.runningDockerContainers.delete(port)\n\t\t\tthrow new Error(\n\t\t\t\t`Docker dev server failed to start within ${this.options.startupTimeout}ms timeout`\n\t\t\t)\n\t\t}\n\n\t\tlogger.success(`Docker dev server started successfully on port ${port}`)\n\t}\n\n\t/**\n\t * Check if a dev server is running on the specified port\n\t *\n\t * @param port - Port to check\n\t * @param dockerConfig - Optional Docker configuration; when provided, checks container status\n\t * @returns true if server is running, false otherwise\n\t */\n\tasync isServerRunning(port: number, dockerConfig?: DockerConfig): Promise<boolean> {\n\t\tif (dockerConfig) {\n\t\t\tconst strategy = this.createDockerStrategy(dockerConfig)\n\t\t\tconst containerName = dockerUtils.buildContainerName(dockerConfig.identifier)\n\t\t\treturn strategy.isContainerRunning(containerName)\n\t\t}\n\t\tconst existingProcess = await this.processManager.detectDevServer(port)\n\t\treturn existingProcess !== null\n\t}\n\n\t/**\n\t * Run dev server in foreground mode (blocking).\n\t * This method blocks until the server is stopped (e.g., via Ctrl+C).\n\t *\n\t * @param worktreePath - Path to the worktree\n\t * @param port - Port the server should run on\n\t * @param redirectToStderr - If true, redirect stdout/stderr to stderr (useful for JSON output)\n\t * @param onProcessStarted - Callback called immediately after process starts with PID\n\t * @returns Process information including PID\n\t */\n\tasync runServerForeground(\n\t\tworktreePath: string,\n\t\tport: number,\n\t\tredirectToStderr = false,\n\t\tonProcessStarted?: (pid?: number) => void,\n\t\tenvOverrides?: Record<string, string>,\n\t\tdockerConfig?: DockerConfig\n\t): Promise<{ pid?: number }> {\n\t\t// Docker mode: build image and run container in foreground\n\t\tif (dockerConfig) {\n\t\t\tlogger.debug(`Starting Docker dev server in foreground on port ${port}`)\n\n\t\t\tconst strategy = this.createDockerStrategy(dockerConfig)\n\t\t\tconst strategyConfig = toStrategyConfig(dockerConfig)\n\t\t\tconst imageName = dockerUtils.buildImageName(dockerConfig.identifier)\n\t\t\tconst containerName = dockerUtils.buildContainerName(dockerConfig.identifier)\n\t\t\tconst dockerfilePath = path.resolve(worktreePath, dockerConfig.dockerFile)\n\n\t\t\t// Build image\n\t\t\tawait strategy.buildImage(worktreePath, strategyConfig)\n\n\t\t\t// Resolve container port\n\t\t\tconst containerPort = await strategy.resolveContainerPort(\n\t\t\t\tstrategyConfig,\n\t\t\t\timageName,\n\t\t\t\tdockerfilePath\n\t\t\t)\n\n\t\t\tif (onProcessStarted) {\n\t\t\t\tonProcessStarted(undefined)\n\t\t\t}\n\n\t\t\t// Track container for cleanup\n\t\t\tthis.runningDockerContainers.set(port, containerName)\n\t\t\ttry {\n\t\t\t\t// Run container in foreground (blocks until stopped)\n\t\t\t\t// DockerDevServerStrategy.runContainerForeground handles signal forwarding internally\n\t\t\t\tawait strategy.runContainerForeground(\n\t\t\t\t\tworktreePath,\n\t\t\t\t\tport,\n\t\t\t\t\tcontainerPort,\n\t\t\t\t\tstrategyConfig,\n\t\t\t\t\t{ redirectToStderr, envOverrides }\n\t\t\t\t)\n\t\t\t} finally {\n\t\t\t\tthis.runningDockerContainers.delete(port)\n\t\t\t}\n\n\t\t\treturn {}\n\t\t}\n\n\t\t// Native mode: delegate to NativeDevServerStrategy\n\t\treturn this.nativeStrategy.startForeground(worktreePath, port, {\n\t\t\tredirectToStderr,\n\t\t\t...(onProcessStarted !== undefined && { onProcessStarted }),\n\t\t\t...(envOverrides !== undefined && { envOverrides }),\n\t\t})\n\t}\n\n\t/**\n\t * Clean up all running server processes and Docker containers.\n\t * This should be called when the manager is being disposed.\n\t */\n\tasync cleanup(): Promise<void> {\n\t\t// Clean up native process-based servers\n\t\tawait this.nativeStrategy.stopAll()\n\n\t\t// Clean up Docker containers using DockerDevServerStrategy\n\t\tfor (const [port, containerName] of this.runningDockerContainers.entries()) {\n\t\t\ttry {\n\t\t\t\tlogger.debug(`Cleaning up Docker container \"${containerName}\" on port ${port}`)\n\t\t\t\t// Create a minimal strategy just for stopContainer\n\t\t\t\tconst strategy = new DockerDevServerStrategy({}, dockerUtils)\n\t\t\t\tawait strategy.stopContainer(containerName)\n\t\t\t} catch (error) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Failed to stop Docker container \"${containerName}\" on port ${port}: ${error instanceof Error ? error.message : 'Unknown error'}`\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\tthis.runningDockerContainers.clear()\n\t}\n}\n","import { execa } from 'execa'\nimport net from 'net'\nimport { logger } from '../utils/logger.js'\nimport { restoreTerminalState } from '../utils/terminal.js'\n\n/**\n * Docker configuration shape consumed by DockerDevServerStrategy.\n * Matches the DevServerSettings['docker'] shape from the settings schema.\n */\nexport interface DockerConfig {\n\t/** Path to Dockerfile (relative to worktree) */\n\tdockerFile?: string | undefined\n\t/** Port inside the container (auto-detected from image inspect or Dockerfile EXPOSE if not set) */\n\tcontainerPort?: number | undefined\n\t/** Build arguments passed as --build-arg to docker build */\n\tbuildArgs?: Record<string, string> | undefined\n\t/** Additional docker run flags */\n\trunArgs?: string[] | undefined\n\t/** Identifier for naming containers/images (issue number, branch name). Falls back to worktreePath if not set. */\n\tidentifier?: string | undefined\n}\n\n/**\n * Options for runContainerForeground.\n */\nexport interface RunForegroundOptions {\n\t/** If true, redirect stdout/stderr to process.stderr */\n\tredirectToStderr?: boolean | undefined\n\t/** Called immediately after the container starts */\n\tonProcessStarted?: ((pid?: number) => void) | undefined\n\t/** Additional environment variables to forward into the container */\n\tenvOverrides?: Record<string, string> | undefined\n}\n\n/**\n * Utility function contracts from the docker-utils sibling issue.\n * Coded against these shapes so this class compiles without waiting for the sibling merge.\n */\ntype ParseDockerfileExposeFn = (path: string) => Promise<number | null>\ntype InspectImagePortsFn = (name: string) => Promise<number | null>\ntype BuildContainerNameFn = (id: string | number) => string\ntype BuildImageNameFn = (id: string | number) => string\ntype AssertDockerAvailableFn = () => Promise<void>\n\n/**\n * Injected docker utility functions.\n * Default implementations are imported from DockerManager for backward compatibility\n * until the dedicated docker-utils module is merged.\n */\nexport interface DockerUtils {\n\tparseDockerfileExpose: ParseDockerfileExposeFn\n\tinspectImagePorts: InspectImagePortsFn\n\tbuildContainerName: BuildContainerNameFn\n\tbuildImageName: BuildImageNameFn\n\tassertDockerAvailable: AssertDockerAvailableFn\n}\n\n/**\n * Attempt a single TCP connection to localhost:port.\n * Resolves true if the connection succeeds, false otherwise.\n */\nfunction tcpProbe(port: number): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tconst socket = net.createConnection({ port, host: '127.0.0.1' })\n\t\tsocket.once('connect', () => {\n\t\t\tsocket.destroy()\n\t\t\tresolve(true)\n\t\t})\n\t\tsocket.once('error', () => {\n\t\t\tsocket.destroy()\n\t\t\tresolve(false)\n\t\t})\n\t})\n}\n\n/**\n * DockerDevServerStrategy handles the full Docker container lifecycle for a dev server:\n * - Image building\n * - Container running (detached and foreground)\n * - Container stopping\n * - Readiness detection via TCP probe\n * - Port resolution (3-tier: config > image inspect > Dockerfile EXPOSE)\n *\n * This class is the core Docker logic delegated to by DevServerManager.\n * It does NOT modify DevServerManager, ResourceCleanup, or CLI commands.\n */\nexport class DockerDevServerStrategy {\n\tprivate readonly utils: DockerUtils\n\n\tconstructor(_config: DockerConfig, utils: DockerUtils) {\n\t\tthis.utils = utils\n\t}\n\n\t/**\n\t * Resolve the container port using 3-tier fallback:\n\t * 1. config.containerPort (explicit)\n\t * 2. inspectImagePorts(imageName) (from built image)\n\t * 3. parseDockerfileExpose(dockerfilePath) (from Dockerfile)\n\t *\n\t * Throws a clear error if all three return null.\n\t *\n\t * @param config - Docker config (may override the constructor config)\n\t * @param imageName - Name of the built Docker image\n\t * @param dockerfilePath - Absolute path to the Dockerfile\n\t */\n\tasync resolveContainerPort(\n\t\tconfig: DockerConfig,\n\t\timageName: string,\n\t\tdockerfilePath: string\n\t): Promise<number> {\n\t\tif (config.containerPort !== undefined) {\n\t\t\treturn config.containerPort\n\t\t}\n\n\t\tconst inspectedPort = await this.utils.inspectImagePorts(imageName)\n\t\tif (inspectedPort !== null) {\n\t\t\tlogger.debug(`Auto-detected container port ${inspectedPort} from Docker image inspect`)\n\t\t\treturn inspectedPort\n\t\t}\n\n\t\tconst exposedPort = await this.utils.parseDockerfileExpose(dockerfilePath)\n\t\tif (exposedPort !== null) {\n\t\t\tlogger.debug(`Auto-detected container port ${exposedPort} from Dockerfile EXPOSE directive`)\n\t\t\treturn exposedPort\n\t\t}\n\n\t\tthrow new Error(\n\t\t\t'Cannot determine container port. Set `devServer.docker.containerPort` in settings or add an `EXPOSE` directive to your Dockerfile.'\n\t\t)\n\t}\n\n\t/**\n\t * Build a Docker image for the worktree.\n\t * Build context is always the worktree root directory.\n\t *\n\t * @param worktreePath - Absolute path to the worktree (build context)\n\t * @param config - Docker config with Dockerfile path and build args\n\t */\n\tasync buildImage(worktreePath: string, config: DockerConfig): Promise<void> {\n\t\tconst imageName = this.utils.buildImageName(config.identifier ?? worktreePath)\n\t\tconst dockerfilePath = config.dockerFile ?? './Dockerfile'\n\n\t\tconst args = ['build', '-t', imageName, '-f', dockerfilePath]\n\n\t\tif (config.buildArgs) {\n\t\t\tfor (const [key, value] of Object.entries(config.buildArgs)) {\n\t\t\t\targs.push('--build-arg', `${key}=${value}`)\n\t\t\t}\n\t\t}\n\n\t\t// Context is always the worktree root\n\t\targs.push('.')\n\n\t\tlogger.info(`Building Docker image \"${imageName}\" from ${dockerfilePath}...`)\n\n\t\ttry {\n\t\t\tawait execa('docker', args, {\n\t\t\t\tcwd: worktreePath,\n\t\t\t\tstdio: 'inherit',\n\t\t\t})\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : 'Unknown error'\n\t\t\tthrow new Error(`Docker build failed for image \"${imageName}\": ${message}`)\n\t\t}\n\n\t\tlogger.success(`Docker image \"${imageName}\" built successfully`)\n\t}\n\n\t/**\n\t * Run a container in detached (background) mode.\n\t * Force-removes any existing container with the same name first.\n\t * Mounts the worktree at /app and adds an anonymous volume for node_modules.\n\t * Forwards PORT and any envOverrides into the container.\n\t *\n\t * @param worktreePath - Absolute path to the worktree (mounted at /app)\n\t * @param hostPort - Port on the host to map\n\t * @param containerPort - Port inside the container\n\t * @param config - Docker config with run args\n\t * @param envOverrides - Additional environment variables to set in the container\n\t * @returns The container name\n\t */\n\tasync runContainerDetached(\n\t\tworktreePath: string,\n\t\thostPort: number,\n\t\tcontainerPort: number,\n\t\tconfig: DockerConfig,\n\t\tenvOverrides?: Record<string, string>\n\t): Promise<string> {\n\t\tconst nameId = config.identifier ?? worktreePath\n\t\tconst imageName = this.utils.buildImageName(nameId)\n\t\tconst containerName = this.utils.buildContainerName(nameId)\n\n\t\t// Force-remove any existing container with same name\n\t\tawait execa('docker', ['rm', '-f', containerName], { reject: false })\n\n\t\tconst args = [\n\t\t\t'run', '-d',\n\t\t\t'--name', containerName,\n\t\t\t'-p', `${hostPort}:${containerPort}`,\n\t\t\t// Mount worktree at /app\n\t\t\t'-v', `${worktreePath}:/app`,\n\t\t\t// Anonymous volume for node_modules to prevent host/container conflicts\n\t\t\t'-v', '/app/node_modules',\n\t\t\t// Forward PORT as the container port so the app listens where Docker expects.\n\t\t\t// The -p mapping handles host-to-container translation.\n\t\t\t'-e', `PORT=${containerPort}`,\n\t\t]\n\n\t\t// Forward additional environment variables\n\t\tif (envOverrides) {\n\t\t\tfor (const [key, value] of Object.entries(envOverrides)) {\n\t\t\t\targs.push('-e', `${key}=${value}`)\n\t\t\t}\n\t\t}\n\n\t\t// Additional run flags from config\n\t\tif (config.runArgs) {\n\t\t\targs.push(...config.runArgs)\n\t\t}\n\n\t\targs.push(imageName)\n\n\t\tlogger.info(`Starting Docker container \"${containerName}\" in background (http://localhost:${hostPort} → container:${containerPort})...`)\n\n\t\ttry {\n\t\t\tawait execa('docker', args)\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : 'Unknown error'\n\t\t\tthrow new Error(`Failed to start Docker container \"${containerName}\": ${message}`)\n\t\t}\n\n\t\tlogger.success(`Docker container \"${containerName}\" started on port ${hostPort}`)\n\t\treturn containerName\n\t}\n\n\t/**\n\t * Run a container in foreground (blocking) mode.\n\t * The container is automatically removed on exit (--rm flag).\n\t * Traps SIGINT and SIGTERM and forwards them to the container via docker stop.\n\t *\n\t * @param worktreePath - Absolute path to the worktree (mounted at /app)\n\t * @param hostPort - Port on the host to map\n\t * @param containerPort - Port inside the container\n\t * @param config - Docker config with run args\n\t * @param opts - Additional options (redirectToStderr, onProcessStarted, envOverrides)\n\t * @returns Object with optional pid (Docker containers don't expose host PID)\n\t */\n\tasync runContainerForeground(\n\t\tworktreePath: string,\n\t\thostPort: number,\n\t\tcontainerPort: number,\n\t\tconfig: DockerConfig,\n\t\topts: RunForegroundOptions = {}\n\t): Promise<{ pid?: number }> {\n\t\tconst nameId = config.identifier ?? worktreePath\n\t\tconst imageName = this.utils.buildImageName(nameId)\n\t\tconst containerName = this.utils.buildContainerName(nameId)\n\t\tconst { redirectToStderr, onProcessStarted, envOverrides } = opts\n\n\t\t// Force-remove any existing container with same name (stale from previous ungraceful exit)\n\t\tawait execa('docker', ['rm', '-f', containerName], { reject: false })\n\n\t\tconst args = [\n\t\t\t'run', '--rm',\n\t\t\t'--name', containerName,\n\t\t\t'-p', `${hostPort}:${containerPort}`,\n\t\t\t// Mount worktree at /app\n\t\t\t'-v', `${worktreePath}:/app`,\n\t\t\t// Anonymous volume for node_modules to prevent host/container conflicts\n\t\t\t'-v', '/app/node_modules',\n\t\t\t// Forward PORT as the container port so the app listens where Docker expects.\n\t\t\t// The -p mapping handles host-to-container translation.\n\t\t\t'-e', `PORT=${containerPort}`,\n\t\t]\n\n\t\t// Forward additional environment variables\n\t\tif (envOverrides) {\n\t\t\tfor (const [key, value] of Object.entries(envOverrides)) {\n\t\t\t\targs.push('-e', `${key}=${value}`)\n\t\t\t}\n\t\t}\n\n\t\t// Additional run flags from config\n\t\tif (config.runArgs) {\n\t\t\targs.push(...config.runArgs)\n\t\t}\n\n\t\targs.push(imageName)\n\n\t\tlogger.info(`Running Docker container \"${containerName}\" in foreground (http://localhost:${hostPort} → container:${containerPort})...`)\n\n\t\tconst stdio = redirectToStderr\n\t\t\t? [process.stdin, process.stderr, process.stderr] as const\n\t\t\t: 'inherit' as const\n\n\t\t// Signal forwarding: trap SIGINT/SIGTERM and forward to container\n\t\tconst forwardSignal = (): void => {\n\t\t\tlogger.debug(`Stopping container \"${containerName}\"`)\n\t\t\tvoid execa('docker', ['stop', containerName], { reject: false })\n\t\t}\n\n\t\tconst onSigint = (): void => forwardSignal()\n\t\tconst onSigterm = (): void => forwardSignal()\n\n\t\tprocess.on('SIGINT', onSigint)\n\t\tprocess.on('SIGTERM', onSigterm)\n\n\t\tif (onProcessStarted) {\n\t\t\tonProcessStarted(undefined)\n\t\t}\n\n\t\ttry {\n\t\t\tawait execa('docker', args, { stdio })\n\t\t} finally {\n\t\t\t// Clean up signal handlers to avoid leaks\n\t\t\tprocess.removeListener('SIGINT', onSigint)\n\t\t\tprocess.removeListener('SIGTERM', onSigterm)\n\t\t\trestoreTerminalState()\n\t\t}\n\n\t\treturn {}\n\t}\n\n\t/**\n\t * Stop and remove a container by name.\n\t * Uses docker rm -f which handles both running and stopped containers atomically.\n\t * Handles already-stopped containers gracefully (no error thrown).\n\t *\n\t * @param containerName - Name of the container to stop and remove\n\t */\n\tasync stopContainer(containerName: string): Promise<void> {\n\t\tlogger.debug(`Stopping and removing container \"${containerName}\"...`)\n\t\tawait execa('docker', ['rm', '-f', containerName], { reject: false })\n\t\tlogger.debug(`Container \"${containerName}\" stopped and removed`)\n\t}\n\n\t/**\n\t * Check if a named container is currently running.\n\t * Uses exact name matching with anchored regex to avoid partial name matches.\n\t *\n\t * @param containerName - Name of the container to check\n\t * @returns true if the container is running, false otherwise\n\t */\n\tasync isContainerRunning(containerName: string): Promise<boolean> {\n\t\ttry {\n\t\t\tconst result = await execa('docker', [\n\t\t\t\t'ps',\n\t\t\t\t'--filter', `name=^${containerName}$`,\n\t\t\t\t'--format', '{{.Names}}',\n\t\t\t], { reject: false })\n\n\t\t\treturn result.exitCode === 0 && result.stdout.trim() === containerName\n\t\t} catch {\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Wait for the dev server to be ready by probing the TCP port.\n\t * Uses net.createConnection instead of lsof-based detection since Docker port\n\t * forwarding shows com.docker.backend as the listening process (not the dev server).\n\t * Exits early if the container has stopped (crash detection).\n\t *\n\t * @param port - Host port to probe\n\t * @param timeout - Maximum time to wait in milliseconds\n\t * @param interval - Interval between probes in milliseconds\n\t * @param containerName - Optional container name to monitor for early exit\n\t * @returns true if the port accepts connections within the timeout, false otherwise\n\t */\n\tasync waitForReady(port: number, timeout: number, interval: number, containerName?: string): Promise<boolean> {\n\t\tconst startTime = Date.now()\n\t\tlet attempts = 0\n\n\t\twhile (Date.now() - startTime < timeout) {\n\t\t\tattempts++\n\n\t\t\t// Early exit: if the container has stopped, stop polling\n\t\t\tif (containerName && attempts % 3 === 0) {\n\t\t\t\tconst stillRunning = await this.isContainerRunning(containerName)\n\t\t\t\tif (!stillRunning) {\n\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t`Docker container \"${containerName}\" exited before becoming ready (after ${attempts} attempts, ${Date.now() - startTime}ms)`\n\t\t\t\t\t)\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst isReady = await tcpProbe(port)\n\t\t\tif (isReady) {\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\tawait new Promise<void>((resolve) => globalThis.setTimeout(resolve, interval))\n\t\t}\n\n\t\treturn false\n\t}\n}\n","import { execa, type ExecaChildProcess, type ExecaError } from 'execa'\nimport { setTimeout } from 'timers/promises'\nimport { ProcessManager } from './process/ProcessManager.js'\nimport { buildDevServerCommand } from '../utils/dev-server.js'\nimport { runScript } from '../utils/package-manager.js'\nimport { getPackageScripts } from '../utils/package-json.js'\nimport { logger } from '../utils/logger.js'\nimport { restoreTerminalState } from '../utils/terminal.js'\nimport type { DevServerStrategy, ForegroundOpts } from './DevServerStrategy.js'\n\n/**\n * NativeDevServerStrategy implements DevServerStrategy for process-based dev servers.\n * This is the default mode — the dev server runs directly on the host as a child process.\n */\nexport class NativeDevServerStrategy implements DevServerStrategy {\n\tprivate readonly processManager: ProcessManager\n\tprivate readonly startupTimeout: number\n\tprivate readonly checkInterval: number\n\tprivate runningServers: Map<number, ExecaChildProcess> = new Map()\n\n\tconstructor(\n\t\tprocessManager: ProcessManager,\n\t\tstartupTimeout: number,\n\t\tcheckInterval: number\n\t) {\n\t\tthis.processManager = processManager\n\t\tthis.startupTimeout = startupTimeout\n\t\tthis.checkInterval = checkInterval\n\t}\n\n\tasync isRunning(port: number): Promise<boolean> {\n\t\tconst process = await this.processManager.detectDevServer(port)\n\t\treturn process !== null\n\t}\n\n\tasync startBackground(\n\t\tworktreePath: string,\n\t\tport: number,\n\t\tenvOverrides?: Record<string, string>\n\t): Promise<void> {\n\t\t// Guard: Check if a dev script exists in package.json or package.iloom.json\n\t\tconst scripts = await getPackageScripts(worktreePath)\n\t\tif (!scripts['dev']) {\n\t\t\tlogger.warn('Skipping auto-start: no \"dev\" script found in package.json or package.iloom.json')\n\t\t\treturn\n\t\t}\n\n\t\t// Build dev server command\n\t\tconst devCommand = await buildDevServerCommand(worktreePath)\n\t\tlogger.debug(`Starting dev server with command: ${devCommand}`)\n\n\t\t// Start server in background\n\t\tconst serverProcess = execa('sh', ['-c', devCommand], {\n\t\t\tcwd: worktreePath,\n\t\t\tenv: {\n\t\t\t\t...process.env,\n\t\t\t\t...envOverrides,\n\t\t\t\tPORT: port.toString(),\n\t\t\t},\n\t\t\t// Important: Don't inherit stdio - server runs in background\n\t\t\tstdio: 'ignore',\n\t\t\t// Detach from parent process so it continues running\n\t\t\tdetached: true,\n\t\t})\n\n\t\t// Store reference to prevent cleanup\n\t\tthis.runningServers.set(port, serverProcess)\n\n\t\t// Remove from map when process exits naturally or crashes\n\t\tserverProcess.on('exit', () => {\n\t\t\tthis.runningServers.delete(port)\n\t\t})\n\n\t\t// Unref so parent can exit\n\t\tserverProcess.unref()\n\n\t\t// Wait for server to be ready (pass process ref for early crash detection)\n\t\tlogger.info(`Waiting for dev server to start on port ${port}...`)\n\t\tconst ready = await this.waitForReady(port, serverProcess)\n\n\t\tif (!ready) {\n\t\t\tthrow new Error(\n\t\t\t\t`Dev server failed to start within ${this.startupTimeout}ms timeout`\n\t\t\t)\n\t\t}\n\n\t\tlogger.success(`Dev server started successfully on port ${port}`)\n\t}\n\n\tasync startForeground(\n\t\tworktreePath: string,\n\t\tport: number,\n\t\topts: ForegroundOpts\n\t): Promise<{ pid?: number }> {\n\t\tconst { redirectToStderr = false, onProcessStarted, envOverrides } = opts\n\n\t\tlogger.debug(`Starting dev server in foreground on port ${port}`)\n\n\t\tif (redirectToStderr) {\n\t\t\t// For redirectToStderr, we need direct execa control for custom stdio\n\t\t\tconst devCommand = await buildDevServerCommand(worktreePath)\n\t\t\tlogger.debug(`Starting dev server with command: ${devCommand}`)\n\n\t\t\tconst serverProcess = execa('sh', ['-c', devCommand], {\n\t\t\t\tcwd: worktreePath,\n\t\t\t\tenv: {\n\t\t\t\t\t...process.env,\n\t\t\t\t\t...envOverrides,\n\t\t\t\t\tPORT: port.toString(),\n\t\t\t\t},\n\t\t\t\tstdio: [process.stdin, process.stderr, process.stderr],\n\t\t\t})\n\n\t\t\tconst processInfo: { pid?: number } =\n\t\t\t\tserverProcess.pid !== undefined ? { pid: serverProcess.pid } : {}\n\n\t\t\tif (onProcessStarted) {\n\t\t\t\tonProcessStarted(processInfo.pid)\n\t\t\t}\n\n\t\t\t// Register no-op SIGINT handler to prevent signal-exit from re-raising SIGINT\n\t\t\t// before finally blocks can run, ensuring terminal state is restored on Ctrl+C.\n\t\t\tconst onSigint = (): void => {}\n\t\t\tprocess.on('SIGINT', onSigint)\n\n\t\t\ttry {\n\t\t\t\tawait serverProcess\n\t\t\t} catch (error) {\n\t\t\t\tconst execaError = error as ExecaError\n\t\t\t\t// If killed by SIGINT, the user intentionally cancelled — return silently\n\t\t\t\tif (execaError.signal !== 'SIGINT') {\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tprocess.removeListener('SIGINT', onSigint)\n\t\t\t\trestoreTerminalState()\n\t\t\t}\n\n\t\t\treturn processInfo\n\t\t}\n\n\t\t// Use runScript for standard foreground mode\n\t\treturn await runScript('dev', worktreePath, [], {\n\t\t\tenv: {\n\t\t\t\t...envOverrides,\n\t\t\t\tPORT: port.toString(),\n\t\t\t},\n\t\t\tforeground: true,\n\t\t\t...(onProcessStarted && { onStart: onProcessStarted }),\n\t\t\tnoCi: true, // Dev servers should not have CI=true\n\t\t})\n\t}\n\n\tasync stop(port: number): Promise<boolean> {\n\t\tconst serverProcess = this.runningServers.get(port)\n\t\tif (!serverProcess) {\n\t\t\treturn false\n\t\t}\n\n\t\ttry {\n\t\t\t// Kill the entire process group (negative PID) since the server is\n\t\t\t// spawned with detached:true via `sh -c`. Without this, only the\n\t\t\t// shell process receives the signal and the actual dev server\n\t\t\t// (node/vite/next) remains running as an orphan.\n\t\t\tif (serverProcess.pid) {\n\t\t\t\tprocess.kill(-serverProcess.pid, 'SIGTERM')\n\t\t\t} else {\n\t\t\t\tserverProcess.kill()\n\t\t\t}\n\t\t\tthis.runningServers.delete(port)\n\t\t\treturn true\n\t\t} catch (error) {\n\t\t\tlogger.warn(\n\t\t\t\t`Failed to kill server process on port ${port}: ${error instanceof Error ? error.message : 'Unknown error'}`\n\t\t\t)\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Stop all tracked server processes. Called during cleanup.\n\t */\n\tasync stopAll(): Promise<void> {\n\t\tfor (const [port] of this.runningServers.entries()) {\n\t\t\tawait this.stop(port)\n\t\t}\n\t}\n\n\t/**\n\t * Wait for server to be ready by polling the port.\n\t * Exits early if the spawned process has already exited (crash detection).\n\t * Public so DevServerManager can reuse it for Docker mode readiness checks.\n\t *\n\t * @param port - Port to poll\n\t * @param processRef - Optional spawned process to monitor for early exit\n\t */\n\tasync waitForReady(port: number, processRef?: ExecaChildProcess): Promise<boolean> {\n\t\tconst startTime = Date.now()\n\t\tlet attempts = 0\n\n\t\twhile (Date.now() - startTime < this.startupTimeout) {\n\t\t\tattempts++\n\n\t\t\t// Early exit: if the spawned process has already exited, stop polling\n\t\t\t// Check both null and undefined since exitCode is undefined before the process exits\n\t\t\tif (processRef && processRef.exitCode != null) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Dev server process exited with code ${processRef.exitCode} before becoming ready (after ${attempts} attempts, ${Date.now() - startTime}ms)`\n\t\t\t\t)\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tconst processInfo = await this.processManager.detectDevServer(port)\n\n\t\t\tif (processInfo) {\n\t\t\t\tlogger.debug(\n\t\t\t\t\t`Server detected on port ${port} after ${attempts} attempts (${Date.now() - startTime}ms)`\n\t\t\t\t)\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\tawait setTimeout(this.checkInterval)\n\t\t}\n\n\t\tlogger.warn(\n\t\t\t`Server did not start on port ${port} after ${this.startupTimeout}ms (${attempts} attempts)`\n\t\t)\n\t\treturn false\n\t}\n}\n","import { detectPackageManager } from './package-manager.js'\nimport { logger } from './logger.js'\nimport type { Capability } from '../types/loom.js'\n\n/**\n * Build dev server command for workspace\n * Detects package manager and constructs appropriate command\n */\nexport async function buildDevServerCommand(\n\tworkspacePath: string\n): Promise<string> {\n\tconst packageManager = await detectPackageManager(workspacePath)\n\n\tlet devCommand: string\n\n\tswitch (packageManager) {\n\t\tcase 'pnpm':\n\t\t\tdevCommand = 'pnpm dev'\n\t\t\tbreak\n\t\tcase 'npm':\n\t\t\tdevCommand = 'npm run dev'\n\t\t\tbreak\n\t\tcase 'yarn':\n\t\t\tdevCommand = 'yarn dev'\n\t\t\tbreak\n\t\tdefault:\n\t\t\t// Fallback to npm (handles bun and other package managers)\n\t\t\tlogger.warn(`Unknown or unsupported package manager: ${packageManager}, defaulting to npm`)\n\t\t\tdevCommand = 'npm run dev'\n\t}\n\n\tlogger.debug(`Dev server command: ${devCommand}`)\n\treturn devCommand\n}\n\n/**\n * Build complete dev server launch command for terminal\n * Includes VSCode launch, echo message (only for web projects), and dev server start\n */\nexport async function getDevServerLaunchCommand(\n\tworkspacePath: string,\n\tport?: number,\n\tcapabilities: Capability[] = []\n): Promise<string> {\n\tconst devCommand = await buildDevServerCommand(workspacePath)\n\n\tconst commands: string[] = []\n\n\t// // Open VSCode\n\t// commands.push('code .')\n\n\t// Echo message (only for web projects)\n\tif (capabilities.includes('web')) {\n\t\tif (port !== undefined) {\n\t\t\tcommands.push(`echo 'Starting dev server on PORT=${port}...'`)\n\t\t} else {\n\t\t\tcommands.push(`echo 'Starting dev server...'`)\n\t\t}\n\t}\n\n\t// Start dev server\n\tcommands.push(devCommand)\n\n\treturn commands.join(' && ')\n}\n"],"mappings":"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","names":["execa","process","execa"]}
|
|
@@ -1 +0,0 @@
|
|
|
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 // Human-readable provider metadata\n displayName: string\n installHint: string\n\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 validation\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// Complexity override type\nexport type ComplexityOverride = 'trivial' | 'simple' | 'complex'\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 // Create-only mode: skip Claude, IDE, terminal, dev server, and epic\n createOnly?: boolean\n // One-shot automation mode\n oneShot?: OneShotMode\n // Complexity override (skips complexity evaluation)\n complexity?: ComplexityOverride\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 (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"]}
|