@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/run.ts"],"sourcesContent":["import path from 'path'\nimport fs from 'fs-extra'\nimport { execa } from 'execa'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { ProjectCapabilityDetector } from '../lib/ProjectCapabilityDetector.js'\nimport { DevServerManager } from '../lib/DevServerManager.js'\nimport { DockerManager } from '../lib/DockerManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { IdentifierParser } from '../utils/IdentifierParser.js'\nimport { openBrowser } from '../utils/browser.js'\nimport { getWorkspacePort } from '../utils/port.js'\nimport { extractIssueNumber } from '../utils/git.js'\nimport { buildDevServerUrl } from '../utils/dev-server.js'\nimport { logger } from '../utils/logger.js'\nimport { extractSettingsOverrides } from '../utils/cli-overrides.js'\nimport type { GitWorktree } from '../types/worktree.js'\n\nexport interface RunCommandInput {\n\tidentifier?: string\n\targs?: string[]\n}\n\ninterface ParsedRunInput {\n\ttype: 'issue' | 'pr' | 'branch' | 'epic'\n\tnumber?: string | number // For issues and PRs\n\tbranchName?: string // For branches\n\toriginalInput: string\n\tautoDetected: boolean\n}\n\n/**\n * RunCommand - Runs CLI tool or opens workspace in browser\n * Priority: CLI first, Web fallback\n */\nexport class RunCommand {\n\tconstructor(\n\t\tprivate gitWorktreeManager = new GitWorktreeManager(),\n\t\tprivate capabilityDetector = new ProjectCapabilityDetector(),\n\t\tprivate identifierParser = new IdentifierParser(new GitWorktreeManager()),\n\t\tprivate devServerManager = new DevServerManager(),\n\t\tprivate settingsManager = new SettingsManager()\n\t) {}\n\n\tasync execute(input: RunCommandInput): Promise<void> {\n\t\t// 1. Parse or auto-detect identifier\n\t\tconst parsed = input.identifier\n\t\t\t? await this.parseExplicitInput(input.identifier)\n\t\t\t: await this.autoDetectFromCurrentDirectory()\n\n\t\tlogger.debug(`Parsed input: ${JSON.stringify(parsed)}`)\n\n\t\t// 2. Find worktree path based on identifier\n\t\tconst worktree = await this.findWorktreeForIdentifier(parsed)\n\n\t\tlogger.info(`Found worktree at: ${worktree.path}`)\n\n\t\t// 3. Detect project capabilities\n\t\tconst { capabilities, binEntries } =\n\t\t\tawait this.capabilityDetector.detectCapabilities(worktree.path)\n\n\t\tlogger.debug(`Detected capabilities: ${capabilities.join(', ')}`)\n\n\t\t// 4. Execute based on capabilities (CLI first, web fallback)\n\t\tif (capabilities.includes('cli')) {\n\t\t\tawait this.runCLITool(worktree.path, binEntries, input.args ?? [])\n\t\t} else if (capabilities.includes('web')) {\n\t\t\tawait this.openWebBrowser(worktree)\n\t\t} else {\n\t\t\tthrow new Error(\n\t\t\t\t`No CLI or web capabilities detected for workspace at ${worktree.path}`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Parse explicit identifier input\n\t */\n\tprivate async parseExplicitInput(identifier: string): Promise<ParsedRunInput> {\n\t\tconst parsed = await this.identifierParser.parseForPatternDetection(identifier)\n\n\t\t// Description type should never reach run command (converted in start)\n\t\tif (parsed.type === 'description') {\n\t\t\tthrow new Error('Description input type is not supported in run command')\n\t\t}\n\n\t\tconst result: ParsedRunInput = {\n\t\t\ttype: parsed.type,\n\t\t\toriginalInput: parsed.originalInput,\n\t\t\tautoDetected: false,\n\t\t}\n\n\t\tif (parsed.number !== undefined) {\n\t\t\tresult.number = parsed.number\n\t\t}\n\t\tif (parsed.branchName !== undefined) {\n\t\t\tresult.branchName = parsed.branchName\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Auto-detect identifier from current directory\n\t * Same logic as FinishCommand.autoDetectFromCurrentDirectory()\n\t */\n\tprivate async autoDetectFromCurrentDirectory(): Promise<ParsedRunInput> {\n\t\tconst currentDir = path.basename(process.cwd())\n\n\t\t// Check for PR worktree pattern: _pr_N suffix\n\t\tconst prPattern = /_pr_(\\d+)$/\n\t\tconst prMatch = currentDir.match(prPattern)\n\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tlogger.debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'pr',\n\t\t\t\tnumber: prNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Check for issue pattern in directory\n\t\tconst issueNumber = extractIssueNumber(currentDir)\n\n\t\tif (issueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${issueNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: issueNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Fallback: get current branch name\n\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\tconst currentBranch = repoInfo.currentBranch\n\n\t\tif (!currentBranch) {\n\t\t\tthrow new Error(\n\t\t\t\t'Could not auto-detect identifier. Please provide an issue number, PR number, or branch name.\\n' +\n\t\t\t\t\t'Expected directory pattern: feat/issue-XX-description OR worktree with _pr_N suffix'\n\t\t\t)\n\t\t}\n\n\t\t// Try to extract issue from branch name\n\t\tconst branchIssueNumber = extractIssueNumber(currentBranch)\n\t\tif (branchIssueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: branchIssueNumber,\n\t\t\t\toriginalInput: currentBranch,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Last resort: use branch name\n\t\treturn {\n\t\t\ttype: 'branch',\n\t\t\tbranchName: currentBranch,\n\t\t\toriginalInput: currentBranch,\n\t\t\tautoDetected: true,\n\t\t}\n\t}\n\n\t/**\n\t * Find worktree for the given identifier\n\t */\n\tprivate async findWorktreeForIdentifier(parsed: ParsedRunInput): Promise<GitWorktree> {\n\t\tlet worktree: GitWorktree | null = null\n\n\t\tif (parsed.type === 'issue' && parsed.number !== undefined) {\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForIssue(parsed.number)\n\t\t} else if (parsed.type === 'pr' && parsed.number !== undefined) {\n\t\t\t// For PRs, ensure the number is numeric (PRs are always numeric per GitHub)\n\t\t\tconst prNumber = typeof parsed.number === 'number' ? parsed.number : Number(parsed.number)\n\t\t\tif (isNaN(prNumber) || !isFinite(prNumber)) {\n\t\t\t\tthrow new Error(`Invalid PR number: ${parsed.number}. PR numbers must be numeric.`)\n\t\t\t}\n\t\t\t// Pass empty string for branch name since we don't know it yet\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForPR(prNumber, '')\n\t\t} else if (parsed.type === 'branch' && parsed.branchName) {\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForBranch(\n\t\t\t\tparsed.branchName\n\t\t\t)\n\t\t}\n\n\t\tif (!worktree) {\n\t\t\tthrow new Error(\n\t\t\t\t`No worktree found for ${this.formatParsedInput(parsed)}. ` +\n\t\t\t\t\t`Run 'il start ${parsed.originalInput}' to create one.`\n\t\t\t)\n\t\t}\n\n\t\treturn worktree\n\t}\n\n\t/**\n\t * Format parsed input for display\n\t */\n\tprivate formatParsedInput(parsed: ParsedRunInput): string {\n\t\tconst autoLabel = parsed.autoDetected ? ' (auto-detected)' : ''\n\n\t\tif (parsed.type === 'issue') {\n\t\t\treturn `issue #${parsed.number}${autoLabel}`\n\t\t}\n\t\tif (parsed.type === 'pr') {\n\t\t\treturn `PR #${parsed.number}${autoLabel}`\n\t\t}\n\t\treturn `branch \"${parsed.branchName}\"${autoLabel}`\n\t}\n\n\t/**\n\t * Run CLI tool directly from worktree bin path (NO SYMLINKS!)\n\t */\n\tprivate async runCLITool(\n\t\tworktreePath: string,\n\t\tbinEntries: Record<string, string>,\n\t\targs: string[]\n\t): Promise<void> {\n\t\t// Validate binEntries exist\n\t\tif (Object.keys(binEntries).length === 0) {\n\t\t\tthrow new Error('No bin entries found in package.json')\n\t\t}\n\n\t\t// Get first bin entry (deterministic)\n\t\tconst firstEntry = Object.entries(binEntries)[0]\n\t\tif (!firstEntry) {\n\t\t\tthrow new Error('No bin entries found in package.json')\n\t\t}\n\t\tconst [binName, binPath] = firstEntry\n\t\tlogger.debug(`Using bin entry: ${binName} -> ${binPath}`)\n\n\t\t// CRITICAL: Construct absolute path (NO SYMLINKS!)\n\t\tconst binFilePath = path.resolve(worktreePath, binPath)\n\t\tlogger.debug(`Resolved bin file path: ${binFilePath}`)\n\n\t\t// Verify file exists\n\t\tif (!(await fs.pathExists(binFilePath))) {\n\t\t\tthrow new Error(\n\t\t\t\t`CLI executable not found: ${binFilePath}\\n` +\n\t\t\t\t\t`Make sure the project is built (run 'il start' first)`\n\t\t\t)\n\t\t}\n\n\t\t// Execute with Node.js\n\t\tlogger.info(`Running CLI: node ${binFilePath} ${args.join(' ')}`)\n\t\tawait execa('node', [binFilePath, ...args], {\n\t\t\tstdio: 'inherit', // Allow interactive CLIs (prompts, colors, etc.)\n\t\t\tcwd: worktreePath, // Execute in worktree context\n\t\t\tenv: process.env, // Inherit environment\n\t\t})\n\t}\n\n\t/**\n\t * Open web browser with workspace URL\n\t * Auto-starts dev server if not already running\n\t */\n\tprivate async openWebBrowser(worktree: GitWorktree): Promise<void> {\n\t\tconst cliOverrides = extractSettingsOverrides()\n\t\tconst settings = await this.settingsManager.loadSettings(undefined, cliOverrides)\n\t\tconst isMainWorktree = await this.gitWorktreeManager.isMainWorktree(worktree, this.settingsManager)\n\t\tconst port = await getWorkspacePort({\n\t\t\tworktreePath: worktree.path,\n\t\t\tworktreeBranch: worktree.branch,\n\t\t\tbasePort: settings.capabilities?.web?.basePort,\n\t\t\tcheckEnvFile: true,\n\t\t\tisMainWorktree,\n\t\t})\n\n\t\t// Extract Docker configuration if Docker mode is enabled\n\t\tconst issueNumber = extractIssueNumber(worktree.branch)\n\t\tconst dockerIdentifier = issueNumber?.toString() ?? worktree.branch\n\t\tconst dockerConfig = DockerManager.buildDockerConfigFromSettings(\n\t\t\tsettings.capabilities?.web,\n\t\t\tdockerIdentifier\n\t\t)\n\n\t\tif (dockerConfig) {\n\t\t\tawait DockerManager.assertAvailable()\n\t\t\tconst { dockerFile, containerPort, identifier } = dockerConfig\n\t\t\tlogger.debug(`Docker mode enabled with config: ${JSON.stringify({ dockerFile, containerPort, identifier })}`)\n\t\t}\n\n\t\t// Ensure dev server is running on the port\n\t\tconst serverReady = await this.devServerManager.ensureServerRunning(\n\t\t\tworktree.path,\n\t\t\tport,\n\t\t\tdockerConfig\n\t\t)\n\n\t\tif (!serverReady) {\n\t\t\tlogger.warn(\n\t\t\t\t`Dev server failed to start on port ${port}. Opening browser anyway...`\n\t\t\t)\n\t\t}\n\n\t\t// Construct URL and open browser\n\t\tconst protocol = settings.capabilities?.web?.protocol ?? 'http'\n\t\tconst url = buildDevServerUrl(port, protocol)\n\t\tlogger.info(`Opening browser: ${url}`)\n\t\tawait openBrowser(url)\n\t\tlogger.success('Browser opened')\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -193,16 +193,20 @@
|
|
|
193
193
|
"enum": [
|
|
194
194
|
"sonnet",
|
|
195
195
|
"opus",
|
|
196
|
-
"haiku"
|
|
196
|
+
"haiku",
|
|
197
|
+
"sonnet[1m]",
|
|
198
|
+
"opus[1m]"
|
|
197
199
|
],
|
|
198
|
-
"description": "Claude model shorthand: sonnet, opus, or
|
|
200
|
+
"description": "Claude model shorthand: sonnet, opus, haiku, sonnet[1m], or opus[1m]"
|
|
199
201
|
},
|
|
200
202
|
"swarmModel": {
|
|
201
203
|
"type": "string",
|
|
202
204
|
"enum": [
|
|
203
205
|
"sonnet",
|
|
204
206
|
"opus",
|
|
205
|
-
"haiku"
|
|
207
|
+
"haiku",
|
|
208
|
+
"sonnet[1m]",
|
|
209
|
+
"opus[1m]"
|
|
206
210
|
],
|
|
207
211
|
"description": "Model to use for this agent in swarm mode. Overrides the base model when running inside swarm workers."
|
|
208
212
|
},
|
|
@@ -252,7 +256,9 @@
|
|
|
252
256
|
"enum": [
|
|
253
257
|
"sonnet",
|
|
254
258
|
"opus",
|
|
255
|
-
"haiku"
|
|
259
|
+
"haiku",
|
|
260
|
+
"sonnet[1m]",
|
|
261
|
+
"opus[1m]"
|
|
256
262
|
],
|
|
257
263
|
"default": "opus",
|
|
258
264
|
"description": "Claude model shorthand for spin orchestrator"
|
|
@@ -262,7 +268,9 @@
|
|
|
262
268
|
"enum": [
|
|
263
269
|
"sonnet",
|
|
264
270
|
"opus",
|
|
265
|
-
"haiku"
|
|
271
|
+
"haiku",
|
|
272
|
+
"sonnet[1m]",
|
|
273
|
+
"opus[1m]"
|
|
266
274
|
],
|
|
267
275
|
"description": "Model for the spin orchestrator when running in swarm mode. Overrides spin.model for swarm workflows."
|
|
268
276
|
},
|
|
@@ -283,7 +291,9 @@
|
|
|
283
291
|
"enum": [
|
|
284
292
|
"sonnet",
|
|
285
293
|
"opus",
|
|
286
|
-
"haiku"
|
|
294
|
+
"haiku",
|
|
295
|
+
"sonnet[1m]",
|
|
296
|
+
"opus[1m]"
|
|
287
297
|
],
|
|
288
298
|
"default": "opus",
|
|
289
299
|
"description": "Claude model shorthand for plan command"
|
|
@@ -326,7 +336,9 @@
|
|
|
326
336
|
"enum": [
|
|
327
337
|
"sonnet",
|
|
328
338
|
"opus",
|
|
329
|
-
"haiku"
|
|
339
|
+
"haiku",
|
|
340
|
+
"sonnet[1m]",
|
|
341
|
+
"opus[1m]"
|
|
330
342
|
],
|
|
331
343
|
"default": "sonnet",
|
|
332
344
|
"description": "Claude model shorthand for session summary generation"
|
|
@@ -347,6 +359,15 @@
|
|
|
347
359
|
"maximum": 65535,
|
|
348
360
|
"description": "Base port for web workspace port calculations (default: 3000)"
|
|
349
361
|
},
|
|
362
|
+
"protocol": {
|
|
363
|
+
"type": "string",
|
|
364
|
+
"enum": [
|
|
365
|
+
"http",
|
|
366
|
+
"https"
|
|
367
|
+
],
|
|
368
|
+
"default": "http",
|
|
369
|
+
"description": "Protocol for dev server URLs (http or https)"
|
|
370
|
+
},
|
|
350
371
|
"devServer": {
|
|
351
372
|
"type": "string",
|
|
352
373
|
"enum": [
|
|
@@ -374,6 +395,13 @@
|
|
|
374
395
|
},
|
|
375
396
|
"description": "Build arguments to pass to docker build (e.g., {\"NODE_ENV\": \"development\"})"
|
|
376
397
|
},
|
|
398
|
+
"dockerBuildSecrets": {
|
|
399
|
+
"type": "object",
|
|
400
|
+
"additionalProperties": {
|
|
401
|
+
"type": "string"
|
|
402
|
+
},
|
|
403
|
+
"description": "Secret files to mount during docker build via --secret (e.g., {\"npmrc\": \"~/.npmrc\"}). Keys are secret IDs, values are source file paths."
|
|
404
|
+
},
|
|
377
405
|
"dockerRunArgs": {
|
|
378
406
|
"type": "array",
|
|
379
407
|
"items": {
|
|
@@ -472,30 +500,6 @@
|
|
|
472
500
|
],
|
|
473
501
|
"additionalProperties": false,
|
|
474
502
|
"description": "Neon database configuration. Requires Neon CLI installed and authenticated for database branching."
|
|
475
|
-
},
|
|
476
|
-
"supabase": {
|
|
477
|
-
"type": "object",
|
|
478
|
-
"properties": {
|
|
479
|
-
"projectRef": {
|
|
480
|
-
"type": "string",
|
|
481
|
-
"minLength": 1,
|
|
482
|
-
"description": "Supabase project reference ID (e.g., \"abcdefghijklmnop\")"
|
|
483
|
-
},
|
|
484
|
-
"parentBranch": {
|
|
485
|
-
"type": "string",
|
|
486
|
-
"minLength": 1,
|
|
487
|
-
"description": "Reserved for future use. Supabase currently always branches from the default branch."
|
|
488
|
-
},
|
|
489
|
-
"withData": {
|
|
490
|
-
"type": "boolean",
|
|
491
|
-
"description": "Whether to include data when creating a new branch (defaults to true)"
|
|
492
|
-
}
|
|
493
|
-
},
|
|
494
|
-
"required": [
|
|
495
|
-
"projectRef"
|
|
496
|
-
],
|
|
497
|
-
"additionalProperties": false,
|
|
498
|
-
"description": "Supabase database configuration. Requires Supabase CLI installed and authenticated for database branching."
|
|
499
503
|
}
|
|
500
504
|
},
|
|
501
505
|
"additionalProperties": false,
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
IdentifierParser
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-GQDVH6FA.js";
|
|
5
5
|
import {
|
|
6
6
|
GitWorktreeManager
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-PPQ5LV7U.js";
|
|
8
8
|
import {
|
|
9
9
|
extractIssueNumber
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-KGOBNC5A.js";
|
|
11
11
|
import {
|
|
12
12
|
SettingsManager
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-F5NKWLMQ.js";
|
|
14
14
|
import {
|
|
15
15
|
MetadataManager
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-XIVLGWUX.js";
|
|
17
17
|
import "./chunk-FTYWGQFM.js";
|
|
18
18
|
import {
|
|
19
19
|
getDotenvFlowFiles,
|
|
@@ -242,4 +242,4 @@ var ShellCommand = class {
|
|
|
242
242
|
export {
|
|
243
243
|
ShellCommand
|
|
244
244
|
};
|
|
245
|
-
//# sourceMappingURL=shell-
|
|
245
|
+
//# sourceMappingURL=shell-M2YYPNGV.js.map
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
SessionSummaryService
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-UXBVDD7U.js";
|
|
5
5
|
import "./chunk-NXMDEL3F.js";
|
|
6
6
|
import {
|
|
7
7
|
PRManager
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
8
|
+
} from "./chunk-J5JOJPK3.js";
|
|
9
|
+
import "./chunk-ZEFTWM5Z.js";
|
|
10
10
|
import "./chunk-4232AHNQ.js";
|
|
11
11
|
import {
|
|
12
12
|
GitWorktreeManager
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-PPQ5LV7U.js";
|
|
14
14
|
import "./chunk-WEBMMJKL.js";
|
|
15
|
-
import "./chunk-
|
|
15
|
+
import "./chunk-MRPIDNZU.js";
|
|
16
16
|
import "./chunk-DMSL5BAP.js";
|
|
17
17
|
import "./chunk-KV4NU3RP.js";
|
|
18
18
|
import "./chunk-NPVA65KS.js";
|
|
@@ -22,13 +22,13 @@ import "./chunk-BZ7KTXPB.js";
|
|
|
22
22
|
import "./chunk-DDHWZNGL.js";
|
|
23
23
|
import {
|
|
24
24
|
extractIssueNumber
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-KGOBNC5A.js";
|
|
26
26
|
import {
|
|
27
27
|
SettingsManager
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-F5NKWLMQ.js";
|
|
29
29
|
import {
|
|
30
30
|
MetadataManager
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-XIVLGWUX.js";
|
|
32
32
|
import {
|
|
33
33
|
getLogger
|
|
34
34
|
} from "./chunk-FTYWGQFM.js";
|
|
@@ -284,4 +284,4 @@ Please provide an issue number, PR number, or branch name.`
|
|
|
284
284
|
export {
|
|
285
285
|
SummaryCommand
|
|
286
286
|
};
|
|
287
|
-
//# sourceMappingURL=summary-
|
|
287
|
+
//# sourceMappingURL=summary-XR4CBJEG.js.map
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ScriptCommandBase
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-PS6K2AOV.js";
|
|
5
|
+
import "./chunk-GQDVH6FA.js";
|
|
6
|
+
import "./chunk-OLJ54WGW.js";
|
|
7
|
+
import "./chunk-PPQ5LV7U.js";
|
|
8
8
|
import "./chunk-K3QGG4O2.js";
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-KGOBNC5A.js";
|
|
10
|
+
import "./chunk-F5NKWLMQ.js";
|
|
11
|
+
import "./chunk-XIVLGWUX.js";
|
|
12
12
|
import "./chunk-FTYWGQFM.js";
|
|
13
13
|
import "./chunk-VRPPI6GU.js";
|
|
14
14
|
|
|
@@ -24,4 +24,4 @@ var TestCommand = class extends ScriptCommandBase {
|
|
|
24
24
|
export {
|
|
25
25
|
TestCommand
|
|
26
26
|
};
|
|
27
|
-
//# sourceMappingURL=test-
|
|
27
|
+
//# sourceMappingURL=test-ESDAHEVE.js.map
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
findMainWorktreePath
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-KGOBNC5A.js";
|
|
5
5
|
import {
|
|
6
6
|
SettingsManager
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-F5NKWLMQ.js";
|
|
8
|
+
import "./chunk-XIVLGWUX.js";
|
|
9
9
|
import "./chunk-FTYWGQFM.js";
|
|
10
10
|
import {
|
|
11
11
|
logger
|
|
@@ -51,4 +51,4 @@ var TestGitCommand = class {
|
|
|
51
51
|
export {
|
|
52
52
|
TestGitCommand
|
|
53
53
|
};
|
|
54
|
-
//# sourceMappingURL=test-git-
|
|
54
|
+
//# sourceMappingURL=test-git-KWPLHYSI.js.map
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
IssueManagementProviderFactory
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ZEFTWM5Z.js";
|
|
5
5
|
import "./chunk-4232AHNQ.js";
|
|
6
6
|
import "./chunk-DMSL5BAP.js";
|
|
7
7
|
import "./chunk-KV4NU3RP.js";
|
|
8
8
|
import "./chunk-NPVA65KS.js";
|
|
9
9
|
import {
|
|
10
10
|
SettingsManager
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-F5NKWLMQ.js";
|
|
12
12
|
import "./chunk-FTYWGQFM.js";
|
|
13
13
|
import {
|
|
14
14
|
logger
|
|
@@ -93,4 +93,4 @@ var TestJiraCommand = class {
|
|
|
93
93
|
export {
|
|
94
94
|
TestJiraCommand
|
|
95
95
|
};
|
|
96
|
-
//# sourceMappingURL=test-jira-
|
|
96
|
+
//# sourceMappingURL=test-jira-6NK7UHSV.js.map
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
generateWorktreePath
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-KGOBNC5A.js";
|
|
5
5
|
import {
|
|
6
6
|
SettingsManager
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-F5NKWLMQ.js";
|
|
8
|
+
import "./chunk-XIVLGWUX.js";
|
|
9
9
|
import "./chunk-FTYWGQFM.js";
|
|
10
10
|
import {
|
|
11
11
|
logger
|
|
@@ -67,4 +67,4 @@ var TestPrefixCommand = class {
|
|
|
67
67
|
export {
|
|
68
68
|
TestPrefixCommand
|
|
69
69
|
};
|
|
70
|
-
//# sourceMappingURL=test-prefix-
|
|
70
|
+
//# sourceMappingURL=test-prefix-VVODGHXP.js.map
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ProcessManager
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-LNY2Y32V.js";
|
|
5
|
+
import "./chunk-ERMEYFT6.js";
|
|
6
|
+
import "./chunk-KGOBNC5A.js";
|
|
7
7
|
import {
|
|
8
8
|
SettingsManager
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-F5NKWLMQ.js";
|
|
10
|
+
import "./chunk-XIVLGWUX.js";
|
|
11
11
|
import "./chunk-FTYWGQFM.js";
|
|
12
12
|
import {
|
|
13
13
|
logger
|
|
@@ -81,4 +81,4 @@ var TestWebserverCommand = class {
|
|
|
81
81
|
export {
|
|
82
82
|
TestWebserverCommand
|
|
83
83
|
};
|
|
84
|
-
//# sourceMappingURL=test-webserver-
|
|
84
|
+
//# sourceMappingURL=test-webserver-AHXKC6H4.js.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
IdentifierParser
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-GQDVH6FA.js";
|
|
5
5
|
import {
|
|
6
6
|
GitWorktreeManager
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-PPQ5LV7U.js";
|
|
8
8
|
import {
|
|
9
9
|
getInstallHint,
|
|
10
10
|
isIdeAvailable
|
|
@@ -14,9 +14,9 @@ import {
|
|
|
14
14
|
} from "./chunk-NPVA65KS.js";
|
|
15
15
|
import {
|
|
16
16
|
extractIssueNumber
|
|
17
|
-
} from "./chunk-
|
|
18
|
-
import "./chunk-
|
|
19
|
-
import "./chunk-
|
|
17
|
+
} from "./chunk-KGOBNC5A.js";
|
|
18
|
+
import "./chunk-F5NKWLMQ.js";
|
|
19
|
+
import "./chunk-XIVLGWUX.js";
|
|
20
20
|
import "./chunk-FTYWGQFM.js";
|
|
21
21
|
import {
|
|
22
22
|
logger
|
|
@@ -161,4 +161,4 @@ var VSCodeCommand = class {
|
|
|
161
161
|
export {
|
|
162
162
|
VSCodeCommand
|
|
163
163
|
};
|
|
164
|
-
//# sourceMappingURL=vscode-
|
|
164
|
+
//# sourceMappingURL=vscode-OY7HOVRO.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iloom/cli",
|
|
3
|
-
"version": "0.13.0
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Control plane for maintaining alignment between you and Claude Code as you work across multiple issues using isolated environments, visible context, and multi-agent workflows to scale understanding, not just output",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/MetadataManager.ts"],"sourcesContent":["import path from 'path'\nimport os from 'os'\nimport fs from 'fs-extra'\nimport { getLogger } from '../utils/logger-context.js'\nimport type { ProjectCapability } from '../types/loom.js'\nimport type { ComplexityOverride, OneShotMode } from '../types/index.js'\n\nexport type SwarmState = 'pending' | 'in_progress' | 'code_review' | 'done' | 'failed'\n\n/**\n * Schema for metadata JSON file\n * Stored in ~/.config/iloom-ai/looms/\n */\nexport interface MetadataFile {\n description: string\n created_at?: string\n version: number\n // Additional metadata fields (v2)\n branchName?: string\n worktreePath?: string\n issueType?: 'branch' | 'issue' | 'pr' | 'epic'\n issueKey?: string // Canonical, properly-cased issue key (e.g., \"PROJ-123\")\n issue_numbers?: string[]\n pr_numbers?: string[]\n issueTracker?: string\n colorHex?: string // Stored hex color (e.g., \"#dcebff\") - robust against palette changes\n sessionId?: string // Claude Code session ID for resume support\n projectPath?: string // Main worktree path (project root) - enables project identification\n issueUrls?: Record<string, string> // Map of issue ID to URL in the issue tracker\n prUrls?: Record<string, string> // Map of PR number to URL in the issue tracker\n draftPrNumber?: number // Draft PR number if draft-pr mode was used\n oneShot?: OneShotMode // One-shot automation mode stored during loom creation\n complexity?: ComplexityOverride // Complexity override stored during loom creation\n capabilities?: ProjectCapability[] // Detected project capabilities\n state?: SwarmState // Swarm mode lifecycle state\n childIssueNumbers?: string[] // Child issue numbers for epic looms\n parentLoom?: {\n type: 'issue' | 'pr' | 'branch' | 'epic'\n identifier: string | number\n branchName: string\n worktreePath: string\n databaseBranch?: string\n }\n // Epic/swarm child issue data (populated during spin setup)\n childIssues?: Array<{\n number: string // Prefixed: \"#123\" for GitHub, \"ENG-123\" for Linear\n title: string\n body: string\n url: string\n }>\n dependencyMap?: Record<string, string[]> // issueNumber -> array of blocking issueNumbers\n mcpConfigPath?: string // Path to per-loom MCP config file (for swarm claude -p commands)\n swarmTeamName?: string // Unique team name for swarm orchestrator (persisted for re-runs)\n}\n\n/**\n * Input for writing metadata (all fields except version and created_at)\n * Note: issueTracker is required because every loom should have an associated\n * issue tracker provider (defaults to 'github' via IssueTrackerFactory)\n */\nexport interface WriteMetadataInput {\n description: string\n branchName: string\n worktreePath: string\n issueType: 'branch' | 'issue' | 'pr' | 'epic'\n issueKey?: string // Canonical, properly-cased issue key (e.g., \"PROJ-123\")\n issue_numbers: string[]\n pr_numbers: string[]\n issueTracker: string\n colorHex: string // Hex color (e.g., \"#dcebff\") - robust against palette changes\n sessionId: string // Claude Code session ID for resume support (required for new looms)\n projectPath: string // Main worktree path (project root) - required for new looms\n issueUrls: Record<string, string> // Map of issue ID to URL in the issue tracker\n prUrls: Record<string, string> // Map of PR number to URL in the issue tracker\n draftPrNumber?: number // Draft PR number for draft-pr mode\n oneShot?: OneShotMode // One-shot automation mode to persist\n complexity?: ComplexityOverride // Complexity override to persist\n capabilities: ProjectCapability[] // Detected project capabilities (required for new looms)\n state?: SwarmState // Swarm mode lifecycle state\n childIssueNumbers?: string[] // Child issue numbers for epic looms\n parentLoom?: {\n type: 'issue' | 'pr' | 'branch' | 'epic'\n identifier: string | number\n branchName: string\n worktreePath: string\n databaseBranch?: string\n }\n // Epic/swarm child issue data (populated during spin setup)\n childIssues?: Array<{\n number: string // Prefixed: \"#123\" for GitHub, \"ENG-123\" for Linear\n title: string\n body: string\n url: string\n }>\n dependencyMap?: Record<string, string[]> // issueNumber -> array of blocking issueNumbers\n mcpConfigPath?: string // Path to per-loom MCP config file (for swarm claude -p commands)\n swarmTeamName?: string // Unique team name for swarm orchestrator (persisted for re-runs)\n}\n\n/**\n * Result of reading metadata for a worktree\n */\nexport interface LoomMetadata {\n status?: 'active' | 'finished'\n finishedAt?: string | null\n description: string\n created_at: string | null\n branchName: string | null\n worktreePath: string | null\n issueType: 'branch' | 'issue' | 'pr' | 'epic' | null\n issueKey: string | null // Canonical, properly-cased issue key (e.g., \"PROJ-123\")\n issue_numbers: string[]\n pr_numbers: string[]\n issueTracker: string | null\n colorHex: string | null // Hex color (e.g., \"#dcebff\") - robust against palette changes\n sessionId: string | null // Claude Code session ID (null for legacy looms)\n projectPath: string | null // Main worktree path (null for legacy looms)\n issueUrls: Record<string, string> // Map of issue ID to URL ({} for legacy looms)\n prUrls: Record<string, string> // Map of PR number to URL ({} for legacy looms)\n draftPrNumber: number | null // Draft PR number (null if not draft mode)\n oneShot: OneShotMode | null // One-shot mode (null for legacy looms)\n complexity: ComplexityOverride | null // Complexity override (null when not overridden)\n capabilities: ProjectCapability[] // Detected project capabilities (empty for legacy looms)\n state: SwarmState | null // Swarm mode lifecycle state (null for non-swarm looms)\n childIssueNumbers: string[] // Child issue numbers for epic looms (empty for non-epic looms)\n parentLoom: {\n type: 'issue' | 'pr' | 'branch' | 'epic'\n identifier: string | number\n branchName: string\n worktreePath: string\n databaseBranch?: string\n } | null\n // Epic/swarm child issue data (empty arrays/objects for non-epic looms)\n childIssues: Array<{\n number: string\n title: string\n body: string\n url: string\n }>\n dependencyMap: Record<string, string[]>\n mcpConfigPath: string | null // Path to per-loom MCP config file (null for non-swarm looms)\n swarmTeamName: string | null // Unique team name for swarm orchestrator (null for non-swarm looms)\n}\n\n/**\n * MetadataManager: Manage loom metadata persistence\n *\n * Stores loom metadata in ~/.config/iloom-ai/looms/ directory.\n * Each worktree gets a JSON file named by slugifying its absolute path.\n *\n * Per spec section 2.2:\n * - Filename derived from worktree absolute path\n * - Path separators replaced with double underscores\n * - Non-alphanumeric chars (except _ and -) replaced with hyphens\n */\nexport class MetadataManager {\n private readonly loomsDir: string\n private readonly finishedDir: string\n\n constructor() {\n this.loomsDir = path.join(os.homedir(), '.config', 'iloom-ai', 'looms')\n this.finishedDir = path.join(this.loomsDir, 'finished')\n }\n\n /**\n * Convert MetadataFile to LoomMetadata with default values for optional fields\n */\n private toMetadata(data: MetadataFile): LoomMetadata {\n return {\n description: data.description,\n created_at: data.created_at ?? null,\n branchName: data.branchName ?? null,\n worktreePath: data.worktreePath ?? null,\n issueType: data.issueType ?? null,\n issueKey: data.issueKey ?? null,\n issue_numbers: data.issue_numbers ?? [],\n pr_numbers: data.pr_numbers ?? [],\n issueTracker: data.issueTracker ?? null,\n colorHex: data.colorHex ?? null,\n sessionId: data.sessionId ?? null,\n projectPath: data.projectPath ?? null,\n issueUrls: data.issueUrls ?? {},\n prUrls: data.prUrls ?? {},\n draftPrNumber: data.draftPrNumber ?? null,\n oneShot: data.oneShot ?? null,\n complexity: data.complexity ?? null,\n capabilities: data.capabilities ?? [],\n state: data.state ?? null,\n childIssueNumbers: data.childIssueNumbers ?? [],\n parentLoom: data.parentLoom ?? null,\n childIssues: data.childIssues ?? [],\n dependencyMap: data.dependencyMap ?? {},\n mcpConfigPath: data.mcpConfigPath ?? null,\n swarmTeamName: data.swarmTeamName ?? null,\n }\n }\n\n /**\n * Convert worktree path to filename slug per spec section 2.2\n *\n * Algorithm:\n * 1. Trim trailing slashes\n * 2. Replace all path separators (/ or \\) with __ (double underscore)\n * 3. Replace any other non-alphanumeric characters (except _ and -) with -\n * 4. Append .json\n *\n * Example:\n * - Worktree: /Users/jane/dev/repo\n * - Filename: _Users__jane__dev__repo.json\n */\n slugifyPath(worktreePath: string): string {\n // 1. Trim trailing slashes\n let slug = worktreePath.replace(/[/\\\\]+$/, '')\n\n // 2. Replace path separators with triple underscores\n slug = slug.replace(/[/\\\\]/g, '___')\n\n // 3. Replace non-alphanumeric chars (except _ and -) with hyphens\n slug = slug.replace(/[^a-zA-Z0-9_-]/g, '-')\n\n // 4. Append .json\n return `${slug}.json`\n }\n\n /**\n * Get the full path to the metadata file for a worktree\n */\n private getFilePath(worktreePath: string): string {\n const filename = this.slugifyPath(worktreePath)\n return path.join(this.loomsDir, filename)\n }\n\n /**\n * Get the full path to the metadata file for a worktree (public API)\n * Used by other services that need to reference the metadata file location\n * (e.g., MCP servers that need to read loom context)\n */\n getMetadataFilePath(worktreePath: string): string {\n return this.getFilePath(worktreePath)\n }\n\n /**\n * Write metadata for a worktree (spec section 3.1)\n *\n * @param worktreePath - Absolute path to the worktree (used for file naming)\n * @param input - Metadata to write (description plus additional fields)\n */\n async writeMetadata(worktreePath: string, input: WriteMetadataInput): Promise<void> {\n try {\n // 1. Ensure looms directory exists\n await fs.ensureDir(this.loomsDir, { mode: 0o755 })\n\n // 2. Create JSON content\n const content: MetadataFile = {\n description: input.description,\n created_at: new Date().toISOString(),\n version: 1,\n branchName: input.branchName,\n worktreePath: input.worktreePath,\n issueType: input.issueType,\n ...(input.issueKey && { issueKey: input.issueKey }),\n issue_numbers: input.issue_numbers,\n pr_numbers: input.pr_numbers,\n issueTracker: input.issueTracker,\n colorHex: input.colorHex,\n sessionId: input.sessionId,\n projectPath: input.projectPath,\n issueUrls: input.issueUrls,\n prUrls: input.prUrls,\n capabilities: input.capabilities,\n ...(input.draftPrNumber && { draftPrNumber: input.draftPrNumber }),\n ...(input.oneShot && { oneShot: input.oneShot }),\n ...(input.complexity && { complexity: input.complexity }),\n ...(input.state && { state: input.state }),\n ...(input.childIssueNumbers && input.childIssueNumbers.length > 0 && { childIssueNumbers: input.childIssueNumbers }),\n ...(input.parentLoom && { parentLoom: input.parentLoom }),\n ...(input.childIssues && input.childIssues.length > 0 && { childIssues: input.childIssues }),\n ...(input.dependencyMap && Object.keys(input.dependencyMap).length > 0 && { dependencyMap: input.dependencyMap }),\n ...(input.mcpConfigPath && { mcpConfigPath: input.mcpConfigPath }),\n }\n\n // 3. Write to slugified filename\n const filePath = this.getFilePath(worktreePath)\n await fs.writeFile(filePath, JSON.stringify(content, null, 2), { mode: 0o644 })\n\n getLogger().debug(`Metadata written for worktree: ${worktreePath}`)\n } catch (error) {\n // Log warning but don't throw - metadata is supplementary\n getLogger().warn(\n `Failed to write metadata for worktree: ${error instanceof Error ? error.message : String(error)}`\n )\n }\n }\n\n /**\n * Read metadata for a worktree (spec section 3.2)\n *\n * @param worktreePath - Absolute path to the worktree\n * @returns The metadata object with all fields, or null if not found/invalid\n */\n async readMetadata(worktreePath: string): Promise<LoomMetadata | null> {\n try {\n const filePath = this.getFilePath(worktreePath)\n\n // Check if file exists\n if (!(await fs.pathExists(filePath))) {\n return null\n }\n\n // Read and parse JSON\n const content = await fs.readFile(filePath, 'utf8')\n const data: MetadataFile = JSON.parse(content)\n\n if (!data.description) {\n return null\n }\n\n return this.toMetadata(data)\n } catch (error) {\n // Return null on any error (graceful degradation per spec)\n getLogger().debug(\n `Could not read metadata for worktree ${worktreePath}: ${error instanceof Error ? error.message : String(error)}`\n )\n return null\n }\n }\n\n /**\n * List all stored loom metadata files\n *\n * Returns an array of LoomMetadata objects for all valid metadata files\n * in the looms directory. Invalid or unreadable files are skipped.\n *\n * @returns Array of LoomMetadata objects from all stored files\n */\n async listAllMetadata(): Promise<LoomMetadata[]> {\n const results: LoomMetadata[] = []\n\n try {\n // Check if looms directory exists\n if (!(await fs.pathExists(this.loomsDir))) {\n return results\n }\n\n // Read all files in looms directory\n const files = await fs.readdir(this.loomsDir)\n\n // Filter to only .json files and read each\n for (const file of files) {\n if (!file.endsWith('.json')) {\n continue\n }\n\n try {\n const filePath = path.join(this.loomsDir, file)\n const content = await fs.readFile(filePath, 'utf8')\n const data: MetadataFile = JSON.parse(content)\n\n // Skip files without required description field\n if (!data.description) {\n continue\n }\n\n results.push(this.toMetadata(data))\n } catch (error) {\n // Skip individual files that fail to parse (graceful degradation)\n getLogger().debug(\n `Skipping metadata file ${file}: ${error instanceof Error ? error.message : String(error)}`\n )\n }\n }\n } catch (error) {\n // Log error but return empty array (graceful degradation)\n getLogger().debug(\n `Could not list metadata files: ${error instanceof Error ? error.message : String(error)}`\n )\n }\n\n return results\n }\n\n /**\n * Update existing metadata for a worktree by merging new fields\n *\n * Reads the existing metadata file, merges the provided updates,\n * and writes back. Only provided fields are overwritten.\n *\n * @param worktreePath - Absolute path to the worktree\n * @param updates - Partial metadata fields to merge\n */\n async updateMetadata(worktreePath: string, updates: Partial<MetadataFile>): Promise<void> {\n try {\n const filePath = this.getFilePath(worktreePath)\n\n // Check if file exists\n if (!(await fs.pathExists(filePath))) {\n getLogger().warn(`No metadata file to update for worktree: ${worktreePath}`)\n return\n }\n\n // Read existing data\n const content = await fs.readFile(filePath, 'utf8')\n const data: MetadataFile = JSON.parse(content)\n\n // Merge updates\n const merged = { ...data, ...updates }\n\n // Write back\n await fs.writeFile(filePath, JSON.stringify(merged, null, 2), { mode: 0o644 })\n\n getLogger().debug(`Metadata updated for worktree: ${worktreePath}`)\n } catch (error) {\n getLogger().warn(\n `Failed to update metadata for worktree: ${error instanceof Error ? error.message : String(error)}`\n )\n throw error\n }\n }\n\n /**\n * Delete metadata for a worktree (spec section 3.3)\n *\n * Idempotent: silently succeeds if file doesn't exist\n * Non-fatal: logs warning on permission errors but doesn't throw\n *\n * @param worktreePath - Absolute path to the worktree\n */\n async deleteMetadata(worktreePath: string): Promise<void> {\n try {\n const filePath = this.getFilePath(worktreePath)\n\n // Check if file exists - silently return if not\n if (!(await fs.pathExists(filePath))) {\n getLogger().debug(`No metadata file to delete for worktree: ${worktreePath}`)\n return\n }\n\n // Delete the file\n await fs.unlink(filePath)\n getLogger().debug(`Metadata deleted for worktree: ${worktreePath}`)\n } catch (error) {\n // Log warning on permission error but don't throw (per spec section 3.3)\n getLogger().warn(\n `Failed to delete metadata for worktree: ${error instanceof Error ? error.message : String(error)}`\n )\n }\n }\n\n /**\n * Archive metadata for a finished worktree\n *\n * Moves the metadata file to the finished/ subdirectory and adds\n * status: 'finished' and finishedAt timestamp fields.\n *\n * Idempotent: silently succeeds if source file doesn't exist\n * Non-fatal: logs warning on errors but doesn't throw\n *\n * @param worktreePath - Absolute path to the worktree\n */\n async archiveMetadata(worktreePath: string): Promise<void> {\n try {\n const filename = this.slugifyPath(worktreePath)\n const sourcePath = path.join(this.loomsDir, filename)\n\n // Check if source file exists - silently return if not (idempotent)\n if (!(await fs.pathExists(sourcePath))) {\n getLogger().debug(`No metadata file to archive for worktree: ${worktreePath}`)\n return\n }\n\n // Read existing metadata\n const content = await fs.readFile(sourcePath, 'utf8')\n const data: MetadataFile = JSON.parse(content)\n\n // Add finished status and timestamp\n const finishedData = {\n ...data,\n status: 'finished' as const,\n finishedAt: new Date().toISOString(),\n }\n\n // Ensure finished directory exists\n await fs.ensureDir(this.finishedDir, { mode: 0o755 })\n\n // Write to finished subdirectory\n const destPath = path.join(this.finishedDir, filename)\n await fs.writeFile(destPath, JSON.stringify(finishedData, null, 2), { mode: 0o644 })\n\n // Delete original file\n await fs.unlink(sourcePath)\n\n getLogger().debug(`Metadata archived for worktree: ${worktreePath}`)\n } catch (error) {\n // Log warning but don't throw - archiving is supplementary\n getLogger().warn(\n `Failed to archive metadata for worktree: ${error instanceof Error ? error.message : String(error)}`\n )\n }\n }\n\n /**\n * List all finished loom metadata files\n *\n * Returns an array of LoomMetadata objects for all finished looms\n * in the finished/ subdirectory, sorted by finishedAt in descending order\n * (most recently finished first).\n *\n * @returns Array of LoomMetadata objects from finished files, sorted by finishedAt desc\n */\n async listFinishedMetadata(): Promise<LoomMetadata[]> {\n const results: LoomMetadata[] = []\n\n try {\n // Check if finished directory exists\n if (!(await fs.pathExists(this.finishedDir))) {\n return results\n }\n\n // Read all files in finished directory\n const files = await fs.readdir(this.finishedDir)\n\n // Filter to only .json files and read each\n for (const file of files) {\n if (!file.endsWith('.json')) {\n continue\n }\n\n try {\n const filePath = path.join(this.finishedDir, file)\n const content = await fs.readFile(filePath, 'utf8')\n const data = JSON.parse(content) as MetadataFile & { status?: string; finishedAt?: string }\n\n // Skip files without required description field\n if (!data.description) {\n continue\n }\n\n const metadata = this.toMetadata(data)\n // Add finished-specific fields\n metadata.status = (data.status as 'active' | 'finished') ?? 'finished'\n metadata.finishedAt = data.finishedAt ?? null\n\n results.push(metadata)\n } catch (error) {\n // Skip individual files that fail to parse (graceful degradation)\n getLogger().warn(\n `Skipping finished metadata file ${file}: ${error instanceof Error ? error.message : String(error)}`\n )\n }\n }\n\n // Sort by finishedAt descending (most recently finished first)\n results.sort((a, b) => {\n const aTime = a.finishedAt ? new Date(a.finishedAt).getTime() : 0\n const bTime = b.finishedAt ? new Date(b.finishedAt).getTime() : 0\n return bTime - aTime\n })\n } catch (error) {\n // Log error but return empty array (graceful degradation)\n getLogger().warn(\n `Could not list finished metadata files: ${error instanceof Error ? error.message : String(error)}`\n )\n }\n\n return results\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/PromptTemplateManager.ts"],"sourcesContent":["import { readFile } from 'fs/promises'\nimport { accessSync } from 'fs'\nimport path from 'path'\nimport { fileURLToPath } from 'url'\nimport Handlebars from 'handlebars'\nimport { logger } from '../utils/logger.js'\nimport type { AgentSettings } from './SettingsManager.js'\n\n// Register raw helper to handle content with curly braces (e.g., JSON)\n// Usage: {{{{raw}}}}{{VARIABLE}}{{{{/raw}}}}\n// This outputs the variable content as-is without Handlebars parsing its curly braces\nHandlebars.registerHelper('raw', function (this: unknown, options: Handlebars.HelperOptions) {\n\treturn options.fn(this)\n})\n\nexport interface TemplateVariables {\n\tISSUE_NUMBER?: string | number\n\tPR_NUMBER?: number\n\tISSUE_TITLE?: string\n\tPR_TITLE?: string\n\tWORKSPACE_PATH?: string\n\tPORT?: number\n\tONE_SHOT_MODE?: boolean\n\tINTERACTIVE_MODE?: boolean\n\tSETTINGS_SCHEMA?: string\n\tSETTINGS_GLOBAL_JSON?: string\n\tSETTINGS_JSON?: string\n\tSETTINGS_LOCAL_JSON?: string\n\tSHELL_TYPE?: string\n\tSHELL_CONFIG_PATH?: string\n\tSHELL_CONFIG_CONTENT?: string\n\tREMOTES_INFO?: string\n\tMULTIPLE_REMOTES?: string\n\tSINGLE_REMOTE?: string\n\tSINGLE_REMOTE_NAME?: string\n\tSINGLE_REMOTE_URL?: string\n\tNO_REMOTES?: string\n\tREADME_CONTENT?: string\n\tSETTINGS_SCHEMA_CONTENT?: string\n\tFIRST_TIME_USER?: boolean\n\tVSCODE_SETTINGS_GITIGNORED?: string\n\t// Session summary template variables\n\tSESSION_CONTEXT?: string // Session ID for Claude to reference its conversation\n\tBRANCH_NAME?: string // Branch being finished\n\tLOOM_TYPE?: string // 'issue' or 'pr'\n\tCOMPACT_SUMMARIES?: string // Extracted compact summaries from session transcript\n\tRECAP_DATA?: string // Formatted recap data (goal, complexity, entries, artifacts)\n\t// Draft PR mode variables - mutually exclusive with standard issue mode\n\tDRAFT_PR_NUMBER?: number // PR number for draft PR workflow\n\tDRAFT_PR_URL?: string // Full URL of the draft PR (e.g., https://github.com/owner/repo/pull/123)\n\tDRAFT_PR_MODE?: boolean // True when using draft-pr merge mode\n\tAUTO_COMMIT_PUSH?: boolean // True when auto-commit/push is enabled for draft PR mode\n\tSTANDARD_ISSUE_MODE?: boolean // True when using standard issue commenting (not draft PR)\n\tSTANDARD_BRANCH_MODE?: boolean // True when using standard branch mode (not draft PR)\n\t// Direct prompt mode - agent enhances raw text without issue context or MCP tools\n\tDIRECT_PROMPT_MODE?: boolean\n\t// VS Code environment detection\n\tIS_VSCODE_MODE?: boolean // True when ILOOM_VSCODE=1 environment variable is set\n\t// Multi-language support variables - mutually exclusive\n\tHAS_PACKAGE_JSON?: boolean // True when project has package.json\n\tNO_PACKAGE_JSON?: boolean // True when project does not have package.json (non-Node.js projects)\n\t// Review agent configuration variables (code reviewer)\n\tREVIEW_ENABLED?: boolean // True if review is enabled (defaults to true)\n\tREVIEW_CLAUDE_MODEL?: string // Claude model if configured (defaults to 'sonnet')\n\tREVIEW_GEMINI_MODEL?: string // Gemini model if configured\n\tREVIEW_CODEX_MODEL?: string // Codex model if configured\n\tHAS_REVIEW_CLAUDE?: boolean // True if claude provider configured (defaults to true)\n\tHAS_REVIEW_GEMINI?: boolean // True if gemini provider configured\n\tHAS_REVIEW_CODEX?: boolean // True if codex provider configured\n\t// Artifact reviewer configuration variables\n\tARTIFACT_REVIEW_ENABLED?: boolean // True if artifact review is enabled (defaults to true)\n\tARTIFACT_REVIEW_CLAUDE_MODEL?: string // Claude model if configured (defaults to 'sonnet')\n\tARTIFACT_REVIEW_GEMINI_MODEL?: string // Gemini model if configured\n\tARTIFACT_REVIEW_CODEX_MODEL?: string // Codex model if configured\n\tHAS_ARTIFACT_REVIEW_CLAUDE?: boolean // True if claude provider configured (defaults to true)\n\tHAS_ARTIFACT_REVIEW_GEMINI?: boolean // True if gemini provider configured\n\tHAS_ARTIFACT_REVIEW_CODEX?: boolean // True if codex provider configured\n\t// Per-agent review flags (whether artifacts should be reviewed before posting)\n\tENHANCER_REVIEW_ENABLED?: boolean // True if enhancer artifacts should be reviewed\n\tANALYZER_REVIEW_ENABLED?: boolean // True if analyzer artifacts should be reviewed\n\tPLANNER_REVIEW_ENABLED?: boolean // True if planner artifacts should be reviewed\n\tANALYZE_AND_PLAN_REVIEW_ENABLED?: boolean // True if analyze-and-plan artifacts should be reviewed\n\tIMPLEMENTER_REVIEW_ENABLED?: boolean // True if implementer artifacts should be reviewed\n\tCOMPLEXITY_REVIEW_ENABLED?: boolean // True if complexity evaluator artifacts should be reviewed\n\tCOMPLEXITY_OVERRIDE?: 'trivial' | 'simple' | 'complex' // Complexity override from CLI flag or loom metadata\n\t// Planning mode variables - mutually exclusive\n\tEXISTING_ISSUE_MODE?: boolean // True when decomposing an existing issue (il plan 42)\n\tFRESH_PLANNING_MODE?: boolean // True when starting fresh planning session (il plan \"feature idea\")\n\t// Issue context for decomposition mode\n\tPARENT_ISSUE_NUMBER?: string | undefined // Issue number being decomposed\n\tPARENT_ISSUE_TITLE?: string | undefined // Title of issue being decomposed\n\tPARENT_ISSUE_BODY?: string | undefined // Body of issue being decomposed\n\t// Existing children and dependencies context for decomposition mode\n\tPARENT_ISSUE_CHILDREN?: string | undefined // Formatted list of existing child issues (if any)\n\tPARENT_ISSUE_DEPENDENCIES?: string | undefined // Formatted list of existing dependencies (if any)\n\t// Multi-AI provider support for plan command\n\tPLANNER?: 'claude' | 'gemini' | 'codex'\n\tREVIEWER?: 'claude' | 'gemini' | 'codex' | 'none'\n\tUSE_CLAUDE_PLANNER?: boolean\n\tUSE_GEMINI_PLANNER?: boolean\n\tUSE_CODEX_PLANNER?: boolean\n\tUSE_CLAUDE_REVIEWER?: boolean\n\tUSE_GEMINI_REVIEWER?: boolean\n\tUSE_CODEX_REVIEWER?: boolean\n\tHAS_REVIEWER?: boolean\n\t// Git remote configuration\n\tGIT_REMOTE?: string // Remote name for push (defaults to 'origin')\n\t// Swarm orchestrator variables\n\tEPIC_ISSUE_NUMBER?: string | number\n\tEPIC_WORKTREE_PATH?: string\n\tEPIC_METADATA_PATH?: string // Path to the epic's metadata JSON file\n\tCHILD_ISSUES?: string // JSON stringified array of child issues with worktree paths\n\tDEPENDENCY_MAP?: string // JSON stringified dependency map\n\tSWARM_MODE?: boolean // True when rendering agents in swarm mode\n\tWAVE_VERIFICATION?: boolean // True when wave verification is enabled (planner generates verification child issues)\n\tAUTO_SWARM_MODE?: boolean // True when plan command launched with --auto-swarm flag\n\tNO_CLEANUP?: boolean // True when child loom cleanup should be skipped (e.g., manual cleanup later)\n\tPOST_SWARM_REVIEW?: boolean // True when post-swarm code review is enabled (defaults to true)\n\tISSUE_PREFIX?: string // \"#\" for GitHub, \"\" for Linear/Jira — used in commit message templates\n\tSWARM_TEAM_NAME?: string // Unique team name for swarm orchestrator (project-epic-timestamp)\n}\n\n/**\n * Build review-related template variables from settings.\n * Used by both the ignite command (for prompt templates) and AgentManager (for agent prompts).\n */\nexport function buildReviewTemplateVariables(isSwarmMode: boolean, agents?: Record<string, AgentSettings> | null): Partial<TemplateVariables> {\n\tconst variables: Partial<TemplateVariables> = {}\n\n\t// Code reviewer configuration\n\tconst reviewerSettings = agents?.['iloom-code-reviewer']\n\tconst reviewEnabled = reviewerSettings?.enabled !== false // Default to true\n\tvariables.REVIEW_ENABLED = reviewEnabled\n\n\tif (reviewEnabled) {\n\t\tconst providers = reviewerSettings?.providers ?? {}\n\t\tconst hasAnyProvider = Object.keys(providers).length > 0\n\n\t\tconst claudeModel = providers.claude ?? (hasAnyProvider ? undefined : 'sonnet')\n\t\tif (claudeModel) {\n\t\t\tvariables.REVIEW_CLAUDE_MODEL = claudeModel\n\t\t}\n\t\tif (providers.gemini) {\n\t\t\tvariables.REVIEW_GEMINI_MODEL = providers.gemini\n\t\t}\n\t\tif (providers.codex) {\n\t\t\tvariables.REVIEW_CODEX_MODEL = providers.codex\n\t\t}\n\t\tvariables.HAS_REVIEW_CLAUDE = !!claudeModel\n\t\tvariables.HAS_REVIEW_GEMINI = !!providers.gemini\n\t\tvariables.HAS_REVIEW_CODEX = !!providers.codex\n\t}\n\n\t// Artifact reviewer configuration\n\tconst artifactReviewerSettings = agents?.['iloom-artifact-reviewer']\n\tconst artifactReviewEnabled = artifactReviewerSettings?.enabled !== false // Default to true\n\tvariables.ARTIFACT_REVIEW_ENABLED = artifactReviewEnabled\n\n\tif (artifactReviewEnabled) {\n\t\tconst artifactProviders = artifactReviewerSettings?.providers ?? {}\n\t\tconst hasAnyArtifactProvider = Object.keys(artifactProviders).length > 0\n\n\t\tconst artifactClaudeModel = artifactProviders.claude ?? (hasAnyArtifactProvider ? undefined : 'sonnet')\n\t\tif (artifactClaudeModel) {\n\t\t\tvariables.ARTIFACT_REVIEW_CLAUDE_MODEL = artifactClaudeModel\n\t\t}\n\t\tif (artifactProviders.gemini) {\n\t\t\tvariables.ARTIFACT_REVIEW_GEMINI_MODEL = artifactProviders.gemini\n\t\t}\n\t\tif (artifactProviders.codex) {\n\t\t\tvariables.ARTIFACT_REVIEW_CODEX_MODEL = artifactProviders.codex\n\t\t}\n\t\tvariables.HAS_ARTIFACT_REVIEW_CLAUDE = !!artifactClaudeModel\n\t\tvariables.HAS_ARTIFACT_REVIEW_GEMINI = !!artifactProviders.gemini\n\t\tvariables.HAS_ARTIFACT_REVIEW_CODEX = !!artifactProviders.codex\n\t}\n\n\t// Per-agent review flags (defaults to false for each)\n\t// In swarm mode, swarmReview is used directly (defaults to false if not set)\n\tconst resolveReview = (agent: AgentSettings | undefined): boolean => {\n\t\tif (isSwarmMode) {\n\t\t\treturn agent?.swarmReview === true\n\t\t}\n\t\treturn agent?.review === true\n\t}\n\tvariables.ENHANCER_REVIEW_ENABLED = resolveReview(agents?.['iloom-issue-enhancer'])\n\tvariables.ANALYZER_REVIEW_ENABLED = resolveReview(agents?.['iloom-issue-analyzer'])\n\tvariables.PLANNER_REVIEW_ENABLED = resolveReview(agents?.['iloom-issue-planner'])\n\tvariables.ANALYZE_AND_PLAN_REVIEW_ENABLED = resolveReview(agents?.['iloom-issue-analyze-and-plan'])\n\tvariables.IMPLEMENTER_REVIEW_ENABLED = resolveReview(agents?.['iloom-issue-implementer'])\n\tvariables.COMPLEXITY_REVIEW_ENABLED = resolveReview(agents?.['iloom-issue-complexity-evaluator'])\n\n\treturn variables\n}\n\nexport class PromptTemplateManager {\n\tprivate templateDir: string\n\n\tconstructor(templateDir?: string) {\n\t\tif (templateDir) {\n\t\t\tthis.templateDir = templateDir\n\t\t} else {\n\t\t\t// Find templates relative to the package installation\n\t\t\t// When running from dist/, templates are copied to dist/prompts/\n\t\t\tconst currentFileUrl = import.meta.url\n\t\t\tconst currentFilePath = fileURLToPath(currentFileUrl)\n\t\t\tconst distDir = path.dirname(currentFilePath) // dist directory (may be chunked file location)\n\n\t\t\t// Walk up to find the dist directory (in case of chunked files)\n\t\t\tlet templateDir = path.join(distDir, 'prompts')\n\t\t\tlet currentDir = distDir\n\n\t\t\t// Try to find the prompts directory by walking up\n\t\t\twhile (currentDir !== path.dirname(currentDir)) {\n\t\t\t\tconst candidatePath = path.join(currentDir, 'prompts')\n\t\t\t\ttry {\n\t\t\t\t\t// Check if this directory exists (sync check for constructor)\n\t\t\t\t\taccessSync(candidatePath)\n\t\t\t\t\ttemplateDir = 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.templateDir = templateDir\n\t\t\tlogger.debug('PromptTemplateManager initialized', {\n\t\t\t\tcurrentFilePath,\n\t\t\t\tdistDir,\n\t\t\t\ttemplateDir: this.templateDir\n\t\t\t})\n\t\t}\n\t}\n\n\t/**\n\t * Load a template file by name\n\t */\n\tasync loadTemplate(templateName: 'issue' | 'pr' | 'regular' | 'init' | 'session-summary' | 'plan' | 'swarm-orchestrator'): Promise<string> {\n\t\tconst templatePath = path.join(this.templateDir, `${templateName}-prompt.txt`)\n\n\t\tlogger.debug('Loading template', {\n\t\t\ttemplateName,\n\t\t\ttemplateDir: this.templateDir,\n\t\t\ttemplatePath\n\t\t})\n\n\t\ttry {\n\t\t\treturn await readFile(templatePath, 'utf-8')\n\t\t} catch (error) {\n\t\t\tlogger.error('Failed to load template', { templateName, templatePath, error })\n\t\t\tthrow new Error(`Template not found: ${templatePath}`)\n\t\t}\n\t}\n\n\t/**\n\t * Substitute variables in a template string using Handlebars\n\t */\n\tsubstituteVariables(template: string, variables: TemplateVariables): string {\n\t\tconst compiled = Handlebars.compile(template, { noEscape: true })\n\t\treturn compiled(variables)\n\t}\n\n\t/**\n\t * Get a fully processed prompt for a workflow type\n\t */\n\tasync getPrompt(\n\t\ttype: 'issue' | 'pr' | 'regular' | 'init' | 'session-summary' | 'plan' | 'swarm-orchestrator',\n\t\tvariables: TemplateVariables\n\t): Promise<string> {\n\t\tconst template = await this.loadTemplate(type)\n\t\treturn this.substituteVariables(template, variables)\n\t}\n}\n"],"mappings":"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","names":["templateDir"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/DockerManager.ts","../src/utils/docker.ts"],"sourcesContent":["import { execa } from 'execa'\nimport { logger } from '../utils/logger.js'\nimport { restoreTerminalState } from '../utils/terminal.js'\nimport {\n\tisDockerInstalled,\n\tisDockerRunning,\n\tassertDockerAvailable,\n\tparseDockerfileExpose,\n\tinspectImagePorts,\n\tsanitizeContainerName,\n\tbuildContainerName,\n\tbuildImageName,\n} from '../utils/docker.js'\n\n/**\n * Configuration for Docker-based dev server mode.\n * When provided to DevServerManager methods, the server runs inside a Docker container\n * with port mapping instead of as a local process.\n */\nexport interface DockerConfig {\n\t/** Path to Dockerfile (relative to worktree) */\n\tdockerFile: string\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\tdockerBuildArgs?: Record<string, string> | undefined\n\t/** Additional docker run flags (e.g., volume mounts) */\n\tdockerRunArgs?: string[] | undefined\n\t/** Identifier for container naming (issue number, branch name) */\n\tidentifier: string\n}\n\n/**\n * Web settings shape accepted by buildDockerConfigFromSettings.\n * Matches the capabilities.web section of IloomSettings.\n * Uses `| undefined` for optional properties to satisfy exactOptionalPropertyTypes.\n */\ninterface WebSettings {\n\tdevServer?: 'process' | 'docker' | undefined\n\tdockerFile?: string | undefined\n\tcontainerPort?: number | undefined\n\tdockerBuildArgs?: Record<string, string> | undefined\n\tdockerRunArgs?: string[] | undefined\n}\n\n/**\n * DockerManager encapsulates all Docker CLI interactions.\n * Used by DevServerManager and ResourceCleanup for Docker-based dev server operations.\n *\n * All methods are static since no instance state is needed -- Docker CLI is stateless.\n */\nexport class DockerManager {\n\t/**\n\t * Check if Docker CLI is available and daemon is running.\n\t * @returns true if Docker is ready, false otherwise\n\t */\n\tstatic async isAvailable(): Promise<boolean> {\n\t\tconst installed = await isDockerInstalled()\n\t\tif (!installed) return false\n\t\treturn isDockerRunning()\n\t}\n\n\t/**\n\t * Assert that Docker is available, throwing a clear error if not.\n\t * Call this early in any workflow that requires Docker.\n\t */\n\tstatic async assertAvailable(): Promise<void> {\n\t\treturn assertDockerAvailable()\n\t}\n\n\t/**\n\t * Build a Docker image.\n\t *\n\t * @param cwd - Working directory (worktree path)\n\t * @param imageName - Image tag name\n\t * @param dockerFile - Path to Dockerfile (relative to cwd)\n\t * @param buildArgs - Optional build arguments passed as --build-arg\n\t */\n\tstatic async buildImage(\n\t\tcwd: string,\n\t\timageName: string,\n\t\tdockerFile: string,\n\t\tbuildArgs?: Record<string, string>\n\t): Promise<void> {\n\t\tconst args = ['build', '-t', imageName, '-f', dockerFile]\n\n\t\tif (buildArgs) {\n\t\t\tfor (const [key, value] of Object.entries(buildArgs)) {\n\t\t\t\targs.push('--build-arg', `${key}=${value}`)\n\t\t\t}\n\t\t}\n\n\t\t// Context directory is always cwd\n\t\targs.push('.')\n\n\t\tlogger.info(`Building Docker image \"${imageName}\" from ${dockerFile}...`)\n\n\t\ttry {\n\t\t\tawait execa('docker', args, {\n\t\t\t\tcwd,\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 mode (background).\n\t * If a container with the same name already exists, it is force-removed first.\n\t *\n\t * @param imageName - Image to run\n\t * @param containerName - Container name (must be sanitized)\n\t * @param hostPort - Port on the host to map\n\t * @param containerPort - Port inside the container\n\t * @param additionalArgs - Additional docker run flags (e.g., volume mounts)\n\t * @returns Container ID\n\t */\n\tstatic async runDetached(\n\t\timageName: string,\n\t\tcontainerName: string,\n\t\thostPort: number,\n\t\tcontainerPort: number,\n\t\tadditionalArgs?: string[]\n\t): Promise<string> {\n\t\t// Force-remove any existing container with same name to avoid name collision\n\t\tawait DockerManager.forceRemoveContainer(containerName)\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]\n\n\t\tif (additionalArgs) {\n\t\t\targs.push(...additionalArgs)\n\t\t}\n\n\t\targs.push(imageName)\n\n\t\tlogger.info(`Starting Docker container \"${containerName}\" (${hostPort}:${containerPort})...`)\n\n\t\ttry {\n\t\t\tconst result = await execa('docker', args)\n\t\t\tconst containerId = result.stdout.trim()\n\t\t\tlogger.success(`Docker container \"${containerName}\" started (ID: ${containerId.substring(0, 12)})`)\n\t\t\treturn containerId\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\t}\n\n\t/**\n\t * Run a container in foreground mode (attached, blocking).\n\t * The container is automatically removed on exit (--rm flag).\n\t * Stdout/stderr are streamed to the terminal.\n\t *\n\t * Signal forwarding: Captures SIGINT/SIGTERM on the host process and\n\t * forwards them to the container via `docker kill --signal` for graceful\n\t * shutdown of the framework running inside the container.\n\t *\n\t * @param imageName - Image to run\n\t * @param containerName - Container name (must be sanitized)\n\t * @param hostPort - Port on the host to map\n\t * @param containerPort - Port inside the container\n\t * @param additionalArgs - Additional docker run flags\n\t * @param redirectToStderr - If true, redirect stdout/stderr to stderr\n\t */\n\tstatic async runForeground(\n\t\timageName: string,\n\t\tcontainerName: string,\n\t\thostPort: number,\n\t\tcontainerPort: number,\n\t\tadditionalArgs?: string[],\n\t\tredirectToStderr?: boolean\n\t): Promise<void> {\n\t\t// Force-remove any existing container with same name to avoid name collision\n\t\tawait DockerManager.forceRemoveContainer(containerName)\n\n\t\tconst args = [\n\t\t\t'run',\n\t\t\t'--name', containerName,\n\t\t\t'--rm',\n\t\t\t'-p', `${hostPort}:${containerPort}`,\n\t\t]\n\n\t\tif (additionalArgs) {\n\t\t\targs.push(...additionalArgs)\n\t\t}\n\n\t\targs.push(imageName)\n\n\t\tlogger.info(`Running Docker container \"${containerName}\" in foreground (${hostPort}:${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// Set up signal forwarding to gracefully shut down the container\n\t\tconst forwardSignal = (signal: string): void => {\n\t\t\tlogger.debug(`Forwarding ${signal} to container \"${containerName}\"`)\n\t\t\tvoid execa('docker', ['kill', `--signal=${signal}`, containerName], { reject: false })\n\t\t}\n\n\t\tconst onSigint = (): void => forwardSignal('SIGINT')\n\t\tconst onSigterm = (): void => forwardSignal('SIGTERM')\n\n\t\tprocess.on('SIGINT', onSigint)\n\t\tprocess.on('SIGTERM', onSigterm)\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\t}\n\n\t/**\n\t * Stop and remove a container by name using atomic force-remove.\n\t * No-op if the container doesn't exist or is already removed.\n\t * Uses `docker rm -f` which handles both running and stopped containers\n\t * atomically, eliminating race conditions between stop and remove.\n\t *\n\t * @param containerName - Name of the container to remove\n\t * @returns true if a container was removed, false if no container was found\n\t */\n\tstatic async stopAndRemoveContainer(containerName: string): Promise<boolean> {\n\t\tconst isRunning = await DockerManager.isContainerRunning(containerName)\n\n\t\tif (!isRunning) {\n\t\t\tlogger.debug(`No running container found with name \"${containerName}\"`)\n\t\t\t// Still try to remove in case container exists but is stopped\n\t\t\tawait execa('docker', ['rm', '-f', containerName], { reject: false })\n\t\t\treturn false\n\t\t}\n\n\t\tlogger.info(`Removing Docker container \"${containerName}\"...`)\n\n\t\t// Atomic force-remove: handles running and stopped containers\n\t\tawait execa('docker', ['rm', '-f', containerName], { reject: false })\n\n\t\tlogger.success(`Docker container \"${containerName}\" removed`)\n\t\treturn true\n\t}\n\n\t/**\n\t * Check if a named container is currently running.\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\tstatic async 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 * Parse the EXPOSE directive from a Dockerfile.\n\t * Returns the last exposed port number (handles multi-stage builds correctly),\n\t * or null if none found.\n\t *\n\t * @param dockerfilePath - Absolute path to the Dockerfile\n\t * @returns Last exposed port number, or null if no EXPOSE directive found\n\t */\n\tstatic async parseExposeFromDockerfile(\n\t\tdockerfilePath: string\n\t): Promise<number | null> {\n\t\treturn parseDockerfileExpose(dockerfilePath)\n\t}\n\n\t/**\n\t * Sanitize a string for use as a Docker container name.\n\t *\n\t * @param name - Raw name to sanitize\n\t * @returns Sanitized container name\n\t */\n\tstatic sanitizeContainerName(name: string): string {\n\t\treturn sanitizeContainerName(name)\n\t}\n\n\t/**\n\t * Build the standard container name for an iloom dev server.\n\t *\n\t * @param identifier - Issue number, branch name, or other identifier\n\t * @returns Sanitized container name in the format \"iloom-dev-{identifier}\"\n\t */\n\tstatic buildContainerName(identifier: string | number): string {\n\t\treturn buildContainerName(identifier)\n\t}\n\n\t/**\n\t * Build the standard image name for an iloom dev server.\n\t *\n\t * @param identifier - Issue number, branch name, or other identifier\n\t * @returns Sanitized image name\n\t */\n\tstatic buildImageName(identifier: string | number): string {\n\t\treturn buildImageName(identifier)\n\t}\n\n\t/**\n\t * Detect exposed ports from a built Docker image using `docker image inspect`.\n\t *\n\t * @param imageName - Name of the built Docker image to inspect\n\t * @returns First exposed port number, or null if none found\n\t */\n\tstatic async inspectImagePorts(imageName: string): Promise<number | null> {\n\t\treturn inspectImagePorts(imageName)\n\t}\n\n\t/**\n\t * Resolve the container port from config, image inspection, or Dockerfile EXPOSE directive.\n\t * Priority: config > docker image inspect > Dockerfile regex\n\t * Throws a clear error if no source provides a port.\n\t *\n\t * @param configPort - Port from settings (containerPort field)\n\t * @param dockerfilePath - Absolute path to the Dockerfile\n\t * @param imageName - Optional built image name for inspection (preferred over Dockerfile regex)\n\t * @returns Resolved container port number\n\t */\n\tstatic async resolveContainerPort(\n\t\tconfigPort: number | undefined,\n\t\tdockerfilePath: string,\n\t\timageName?: string\n\t): Promise<number> {\n\t\tif (configPort !== undefined) {\n\t\t\treturn configPort\n\t\t}\n\n\t\t// Try image inspection first (most accurate - handles multi-stage builds, inherited images)\n\t\tif (imageName) {\n\t\t\tconst inspectedPort = await DockerManager.inspectImagePorts(imageName)\n\t\t\tif (inspectedPort !== null) {\n\t\t\t\tlogger.debug(`Auto-detected container port ${inspectedPort} from Docker image inspect`)\n\t\t\t\treturn inspectedPort\n\t\t\t}\n\t\t}\n\n\t\t// Fall back to Dockerfile regex\n\t\tconst exposedPort = await DockerManager.parseExposeFromDockerfile(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. No \"containerPort\" configured in settings and no EXPOSE directive found in ${dockerfilePath}.\\n` +\n\t\t\t\t'Either add an EXPOSE directive to your Dockerfile or set \"containerPort\" in capabilities.web settings.'\n\t\t)\n\t}\n\n\t/**\n\t * Force-remove a container by name (no-op if it doesn't exist).\n\t * Used internally before running a new container to avoid name collisions.\n\t */\n\tprivate static async forceRemoveContainer(containerName: string): Promise<void> {\n\t\tawait execa('docker', ['rm', '-f', containerName], { reject: false })\n\t}\n\n\t/**\n\t * Build a DockerConfig from iloom web settings and an identifier.\n\t * Centralizes the Docker config extraction logic used by dev-server, open, and run commands.\n\t *\n\t * @param webSettings - The capabilities.web section from IloomSettings\n\t * @param identifier - Identifier for container naming (issue number, branch name, etc.)\n\t * @returns DockerConfig if Docker mode is enabled, undefined otherwise\n\t */\n\tstatic buildDockerConfigFromSettings(\n\t\twebSettings: WebSettings | undefined,\n\t\tidentifier: string\n\t): DockerConfig | undefined {\n\t\tif (!webSettings || webSettings.devServer !== 'docker') {\n\t\t\treturn undefined\n\t\t}\n\n\t\treturn {\n\t\t\tdockerFile: webSettings.dockerFile ?? './Dockerfile',\n\t\t\tcontainerPort: webSettings.containerPort,\n\t\t\tdockerBuildArgs: webSettings.dockerBuildArgs,\n\t\t\tdockerRunArgs: webSettings.dockerRunArgs,\n\t\t\tidentifier,\n\t\t}\n\t}\n}\n","import { execa } from 'execa'\nimport { readFile } from 'fs/promises'\n\n/**\n * Maximum length for Docker container names.\n * Docker allows up to 63 characters for container names.\n */\nconst MAX_CONTAINER_NAME_LENGTH = 63\n\n/**\n * Check if Docker CLI is installed.\n * Runs `docker --version` — only checks CLI presence, not daemon status.\n * @returns true if Docker CLI is installed, false otherwise\n */\nexport async function isDockerInstalled(): Promise<boolean> {\n\ttry {\n\t\tconst result = await execa('docker', ['--version'], { reject: false })\n\t\treturn result.exitCode === 0\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/**\n * Check if the Docker daemon is running.\n * Runs `docker info` — requires the daemon to be running.\n * @returns true if Docker daemon is running, false otherwise\n */\nexport async function isDockerRunning(): Promise<boolean> {\n\ttry {\n\t\tconst result = await execa('docker', ['info'], { reject: false })\n\t\treturn result.exitCode === 0\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/**\n * Assert that Docker is installed and the daemon is running.\n * Throws a clear, actionable error if either check fails.\n */\nexport async function assertDockerAvailable(): Promise<void> {\n\tconst installed = await isDockerInstalled()\n\tif (!installed) {\n\t\tthrow new Error(\n\t\t\t'Docker is not installed. Please install Docker to use this feature.\\n' +\n\t\t\t\t'Install Docker: https://docs.docker.com/get-docker/'\n\t\t)\n\t}\n\n\tconst running = await isDockerRunning()\n\tif (!running) {\n\t\tthrow new Error(\n\t\t\t'Docker daemon is not running. Please start Docker and try again.\\n' +\n\t\t\t\t'On macOS/Windows: Open the Docker Desktop application.\\n' +\n\t\t\t\t'On Linux: Run `sudo systemctl start docker`'\n\t\t)\n\t}\n}\n\n/**\n * Parse the EXPOSE directive from a Dockerfile.\n * Returns the LAST exposed port number (handles multi-stage builds correctly),\n * or null if none found.\n *\n * Handles formats like:\n * - EXPOSE 4200\n * - EXPOSE 4200/tcp\n * - EXPOSE 8080/udp\n * - Ignores comment lines (# EXPOSE ...)\n *\n * @param dockerfilePath - Absolute path to the Dockerfile\n * @returns Last exposed port number, or null if no valid EXPOSE directive found\n */\nexport async function parseDockerfileExpose(\n\tdockerfilePath: string\n): Promise<number | null> {\n\ttry {\n\t\tconst content = await readFile(dockerfilePath, 'utf-8')\n\t\tconst lines = content.split('\\n')\n\t\tlet lastPort: number | null = null\n\n\t\tfor (const line of lines) {\n\t\t\tconst trimmed = line.trim()\n\t\t\t// Skip comment lines\n\t\t\tif (trimmed.startsWith('#')) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst match = /^EXPOSE\\s+(\\d+)/i.exec(trimmed)\n\t\t\tif (match?.[1]) {\n\t\t\t\tconst port = parseInt(match[1], 10)\n\t\t\t\tif (!isNaN(port) && port >= 1 && port <= 65535) {\n\t\t\t\t\tlastPort = port\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn lastPort\n\t} catch {\n\t\treturn null\n\t}\n}\n\n/**\n * Detect exposed ports from a built Docker image using `docker image inspect`.\n * Returns the first exposed port number, or null if none found.\n *\n * @param imageName - Name of the built Docker image to inspect\n * @returns First exposed port number, or null if none found\n */\nexport async function inspectImagePorts(imageName: string): Promise<number | null> {\n\ttry {\n\t\tconst result = await execa('docker', [\n\t\t\t'image', 'inspect', imageName,\n\t\t\t'--format', '{{json .Config.ExposedPorts}}',\n\t\t], { reject: false })\n\n\t\tif (result.exitCode !== 0 || !result.stdout.trim()) {\n\t\t\treturn null\n\t\t}\n\n\t\tconst output = result.stdout.trim()\n\t\t// Output format: {\"4200/tcp\":{},\"8080/tcp\":{}} or null\n\t\tif (output === 'null' || output === '<nil>' || output === '{}') {\n\t\t\treturn null\n\t\t}\n\n\t\tconst parsed: unknown = JSON.parse(output)\n\t\tif (typeof parsed !== 'object' || parsed === null) {\n\t\t\treturn null\n\t\t}\n\n\t\t// Get first key (e.g., \"4200/tcp\") and extract port number\n\t\tconst firstKey = Object.keys(parsed)[0]\n\t\tif (!firstKey) {\n\t\t\treturn null\n\t\t}\n\n\t\tconst portMatch = /^(\\d+)/.exec(firstKey)\n\t\tif (portMatch?.[1]) {\n\t\t\tconst port = parseInt(portMatch[1], 10)\n\t\t\tif (!isNaN(port) && port >= 1 && port <= 65535) {\n\t\t\t\treturn port\n\t\t\t}\n\t\t}\n\n\t\treturn null\n\t} catch {\n\t\treturn null\n\t}\n}\n\n/**\n * Sanitize a string for use as a Docker container name.\n *\n * Docker container names must match: [a-zA-Z0-9][a-zA-Z0-9_.-]\n * - Replace slashes, spaces, and other invalid characters with hyphens\n * - Ensure the name starts with an alphanumeric character\n * - Truncate to a maximum of 63 characters\n *\n * @param name - Raw name to sanitize\n * @returns Sanitized container name\n */\nexport function sanitizeContainerName(name: string | number): string {\n\tlet sanitized = String(name)\n\n\t// Replace any character that isn't alphanumeric, underscore, dot, or hyphen with a hyphen\n\tsanitized = sanitized.replace(/[^a-zA-Z0-9_.-]/g, '-')\n\n\t// Collapse consecutive hyphens into one\n\tsanitized = sanitized.replace(/-{2,}/g, '-')\n\n\t// Remove leading non-alphanumeric characters (Docker requires starting with [a-zA-Z0-9])\n\tsanitized = sanitized.replace(/^[^a-zA-Z0-9]+/, '')\n\n\t// Remove trailing hyphens\n\tsanitized = sanitized.replace(/-+$/, '')\n\n\t// Truncate to max length\n\tif (sanitized.length > MAX_CONTAINER_NAME_LENGTH) {\n\t\tsanitized = sanitized.substring(0, MAX_CONTAINER_NAME_LENGTH)\n\t\t// Remove trailing hyphen if truncation created one\n\t\tsanitized = sanitized.replace(/-+$/, '')\n\t}\n\n\t// Fallback if everything was stripped\n\tif (sanitized.length === 0) {\n\t\tsanitized = 'iloom-container'\n\t}\n\n\treturn sanitized\n}\n\n/**\n * Build the standard container name for an iloom dev server.\n *\n * @param identifier - Issue number, branch name, or other identifier\n * @returns Sanitized container name in the format \"iloom-dev-{identifier}\"\n */\nexport function buildContainerName(identifier: string | number): string {\n\treturn sanitizeContainerName(`iloom-dev-${identifier}`)\n}\n\n/**\n * Build the standard image name for an iloom dev server.\n *\n * @param identifier - Issue number, branch name, or other identifier\n * @returns Sanitized image name in the format \"iloom-dev-{identifier}\"\n */\nexport function buildImageName(identifier: string | number): string {\n\t// Docker image tags must be lowercase\n\treturn sanitizeContainerName(`iloom-dev-${identifier}`).toLowerCase()\n}\n"],"mappings":"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","names":["execa","execa"]}
|