@iloom/cli 0.9.2 → 0.10.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 +159 -40
- package/dist/{BranchNamingService-K6XNWQ6C.js → BranchNamingService-ECJHBB67.js} +2 -2
- package/dist/ClaudeContextManager-QXX6ZFST.js +14 -0
- package/dist/ClaudeService-NJNK2SUH.js +13 -0
- package/dist/{GitHubService-TGWJN4V4.js → GitHubService-MEHKHUQP.js} +4 -4
- package/dist/IssueTrackerFactory-NG53YX5S.js +14 -0
- package/dist/{LoomLauncher-73NXL2CL.js → LoomLauncher-L64HHS3T.js} +9 -9
- package/dist/{MetadataManager-W3C54UYT.js → MetadataManager-5QZSTKNN.js} +2 -2
- package/dist/{ProjectCapabilityDetector-N5L7T4IY.js → ProjectCapabilityDetector-5KSYUTBJ.js} +3 -3
- package/dist/{PromptTemplateManager-36YLQRHP.js → PromptTemplateManager-DULSVRRE.js} +2 -2
- package/dist/README.md +159 -40
- package/dist/{SettingsManager-AW3JTJHD.js → SettingsManager-BQDQA3FK.js} +4 -2
- package/dist/agents/iloom-artifact-reviewer.md +11 -0
- package/dist/agents/iloom-code-reviewer.md +14 -0
- package/dist/agents/iloom-issue-analyze-and-plan.md +55 -12
- package/dist/agents/iloom-issue-analyzer.md +49 -6
- package/dist/agents/iloom-issue-complexity-evaluator.md +47 -6
- package/dist/agents/iloom-issue-enhancer.md +86 -7
- package/dist/agents/iloom-issue-implementer.md +48 -7
- package/dist/agents/iloom-issue-planner.md +115 -62
- package/dist/{build-THZI572G.js → build-5GO3XW26.js} +9 -9
- package/dist/{chunk-NUACL52E.js → chunk-3D7WQM7I.js} +2 -2
- package/dist/chunk-4232AHNQ.js +35 -0
- package/dist/chunk-4232AHNQ.js.map +1 -0
- package/dist/{chunk-QN47QVBX.js → chunk-4WJNIR5O.js} +1 -1
- package/dist/chunk-4WJNIR5O.js.map +1 -0
- package/dist/{chunk-A7NJF73J.js → chunk-5MWV33NN.js} +4 -4
- package/dist/{chunk-3I4ONZRT.js → chunk-6EU6TCF6.js} +10 -10
- package/dist/chunk-6EU6TCF6.js.map +1 -0
- package/dist/{chunk-CWRI4JC3.js → chunk-FB47TIJG.js} +29 -11
- package/dist/chunk-FB47TIJG.js.map +1 -0
- package/dist/chunk-HEXKPKCK.js +1396 -0
- package/dist/chunk-HEXKPKCK.js.map +1 -0
- package/dist/{chunk-KAYXR544.js → chunk-J5S7DFYC.js} +2 -2
- package/dist/{chunk-ULSWCPQG.js → chunk-JO2LZ6EQ.js} +476 -5
- package/dist/chunk-JO2LZ6EQ.js.map +1 -0
- package/dist/{chunk-KBEIQP4G.js → chunk-KB64WNBZ.js} +43 -3
- package/dist/chunk-KB64WNBZ.js.map +1 -0
- package/dist/{chunk-OFDN5NKS.js → chunk-KXDRI47U.js} +69 -12
- package/dist/chunk-KXDRI47U.js.map +1 -0
- package/dist/{chunk-R4YWBGY6.js → chunk-LXLMMXXY.js} +54 -14
- package/dist/chunk-LXLMMXXY.js.map +1 -0
- package/dist/{chunk-AR5QKYNE.js → chunk-MNHZB4Z2.js} +4 -4
- package/dist/{chunk-TL72BGP6.js → chunk-MORRVYPT.js} +2 -2
- package/dist/{chunk-KJTVU3HZ.js → chunk-NRSWLOAZ.js} +8 -8
- package/dist/chunk-NRSWLOAZ.js.map +1 -0
- package/dist/{chunk-FO5GGFOV.js → chunk-ONQYPICO.js} +13 -5
- package/dist/chunk-ONQYPICO.js.map +1 -0
- package/dist/{chunk-7ZEHSSUP.js → chunk-P4O6EH46.js} +4 -4
- package/dist/chunk-QZWEJVWV.js +207 -0
- package/dist/chunk-QZWEJVWV.js.map +1 -0
- package/dist/chunk-RSYT7MVI.js +202 -0
- package/dist/chunk-RSYT7MVI.js.map +1 -0
- package/dist/{chunk-Z2TWEXR7.js → chunk-RYWFS37M.js} +6 -6
- package/dist/chunk-RYWFS37M.js.map +1 -0
- package/dist/{chunk-B7U6OKUR.js → chunk-SF2P22EE.js} +11 -3
- package/dist/chunk-SF2P22EE.js.map +1 -0
- package/dist/{chunk-6IIL5M2L.js → chunk-SN3SQCFK.js} +10 -8
- package/dist/{chunk-6IIL5M2L.js.map → chunk-SN3SQCFK.js.map} +1 -1
- package/dist/{chunk-SOSQILHO.js → chunk-UD3WJDIV.js} +92 -82
- package/dist/chunk-UD3WJDIV.js.map +1 -0
- package/dist/{chunk-KXGQYLFZ.js → chunk-UKBAJ2QQ.js} +61 -7
- package/dist/chunk-UKBAJ2QQ.js.map +1 -0
- package/dist/{chunk-W6DP5RVR.js → chunk-UVD4CZKS.js} +3 -3
- package/dist/chunk-UWGVCXRF.js +207 -0
- package/dist/chunk-UWGVCXRF.js.map +1 -0
- package/dist/{chunk-NWMORW3U.js → chunk-VECNX6VX.js} +2 -2
- package/dist/{chunk-4CO6KG5S.js → chunk-VG45TUYK.js} +53 -7
- package/dist/{chunk-4CO6KG5S.js.map → chunk-VG45TUYK.js.map} +1 -1
- package/dist/{chunk-TC7APDKU.js → chunk-VGGST52X.js} +2 -2
- package/dist/{chunk-4LKGCFGG.js → chunk-WWKOVDWC.js} +2 -2
- package/dist/{chunk-YKFCCV6S.js → chunk-WY4QBK43.js} +7 -7
- package/dist/chunk-WY4QBK43.js.map +1 -0
- package/dist/chunk-Y4YZTHZE.js +73 -0
- package/dist/chunk-Y4YZTHZE.js.map +1 -0
- package/dist/{chunk-VOGGLPG5.js → chunk-YQ57ORTV.js} +14 -1
- package/dist/chunk-YQ57ORTV.js.map +1 -0
- package/dist/{chunk-RI2YL6TK.js → chunk-YYAKPQBT.js} +65 -18
- package/dist/chunk-YYAKPQBT.js.map +1 -0
- package/dist/{chunk-IZIYLYPK.js → chunk-ZEWU5PZK.js} +2 -2
- package/dist/{chunk-VPTAX5TR.js → chunk-ZHPNZC75.js} +12 -12
- package/dist/chunk-ZHPNZC75.js.map +1 -0
- package/dist/{chunk-DGG2VY7B.js → chunk-ZW2LKWWE.js} +9 -9
- package/dist/chunk-ZW2LKWWE.js.map +1 -0
- package/dist/{claude-TP2QO3BU.js → claude-P3NQR6IJ.js} +2 -2
- package/dist/{cleanup-PJRIFFU4.js → cleanup-6UCPVMFG.js} +81 -32
- package/dist/cleanup-6UCPVMFG.js.map +1 -0
- package/dist/cli.js +638 -349
- package/dist/cli.js.map +1 -1
- package/dist/{commit-IVP3M4HG.js → commit-L3EPY5QG.js} +21 -20
- package/dist/commit-L3EPY5QG.js.map +1 -0
- package/dist/{compile-R2J65HBQ.js → compile-ZS4HYRX5.js} +9 -9
- package/dist/{contribute-VDZXHK5Y.js → contribute-ORDDQGSL.js} +14 -6
- package/dist/contribute-ORDDQGSL.js.map +1 -0
- package/dist/{dev-server-7F622OEO.js → dev-server-FYZ2AQIH.js} +29 -15
- package/dist/dev-server-FYZ2AQIH.js.map +1 -0
- package/dist/{feedback-E7VET7CL.js → feedback-TMBXSCM5.js} +15 -15
- package/dist/{git-2QDQ2X2S.js → git-ET64COO3.js} +4 -4
- package/dist/hooks/iloom-hook.js +15 -0
- package/dist/ignite-CGOV3TD4.js +1393 -0
- package/dist/ignite-CGOV3TD4.js.map +1 -0
- package/dist/index.d.ts +382 -53
- package/dist/index.js +1167 -36
- package/dist/index.js.map +1 -1
- package/dist/{init-676DHF6R.js → init-GFQ5W7GK.js} +57 -21
- package/dist/init-GFQ5W7GK.js.map +1 -0
- package/dist/{issues-PJSOLOBJ.js → issues-T4ZZSPEG.js} +61 -20
- package/dist/issues-T4ZZSPEG.js.map +1 -0
- package/dist/{lint-CJM7BAIM.js → lint-6TQXDZ3T.js} +9 -9
- package/dist/mcp/issue-management-server.js +2471 -256
- package/dist/mcp/issue-management-server.js.map +1 -1
- package/dist/mcp/recap-server.js +144 -21
- package/dist/mcp/recap-server.js.map +1 -1
- package/dist/{neon-helpers-VVFFTLXE.js → neon-helpers-CQN2PB4S.js} +3 -3
- package/dist/neon-helpers-CQN2PB4S.js.map +1 -0
- package/dist/{open-544H7JF5.js → open-5QZGXQRF.js} +15 -15
- package/dist/open-5QZGXQRF.js.map +1 -0
- package/dist/{plan-Q7ELXDLC.js → plan-U7ZQWLFY.js} +41 -25
- package/dist/plan-U7ZQWLFY.js.map +1 -0
- package/dist/{projects-LH362JZQ.js → projects-2UOXFLNZ.js} +4 -4
- package/dist/prompts/CLAUDE.md +62 -0
- package/dist/prompts/init-prompt.txt +347 -26
- package/dist/prompts/issue-prompt.txt +427 -54
- package/dist/prompts/plan-prompt.txt +97 -16
- package/dist/prompts/pr-prompt.txt +44 -1
- package/dist/prompts/regular-prompt.txt +42 -1
- package/dist/prompts/session-summary-prompt.txt +14 -0
- package/dist/prompts/swarm-orchestrator-prompt.txt +437 -0
- package/dist/{rebase-YND35CIE.js → rebase-DWIB77KV.js} +10 -10
- package/dist/{recap-3W7COH7D.js → recap-MX63HAKV.js} +47 -19
- package/dist/recap-MX63HAKV.js.map +1 -0
- package/dist/{run-QUXJKDQQ.js → run-O3TFNQFC.js} +15 -15
- package/dist/run-O3TFNQFC.js.map +1 -0
- package/dist/schema/package-iloom.schema.json +58 -0
- package/dist/schema/settings.schema.json +115 -15
- package/dist/{shell-QGECBLST.js → shell-G6VC2CYR.js} +14 -7
- package/dist/shell-G6VC2CYR.js.map +1 -0
- package/dist/{summary-G2T4452H.js → summary-FWHAX55O.js} +27 -25
- package/dist/summary-FWHAX55O.js.map +1 -0
- package/dist/{test-EA5NQFDC.js → test-F7JNJZYP.js} +9 -9
- package/dist/{test-git-M7LSLEFL.js → test-git-BTAOIUE2.js} +4 -4
- package/dist/test-jira-CHYNV33F.js +96 -0
- package/dist/test-jira-CHYNV33F.js.map +1 -0
- package/dist/{test-prefix-64NAAUON.js → test-prefix-Q6TFSU6F.js} +4 -4
- package/dist/{test-webserver-OK6Z5FJM.js → test-webserver-EONCG7E7.js} +6 -6
- package/dist/{vscode-AR5NNXXI.js → vscode-VA5X4P25.js} +7 -7
- package/package.json +5 -1
- package/dist/ClaudeContextManager-HR5JQKAI.js +0 -14
- package/dist/ClaudeService-TK7FMC2X.js +0 -13
- package/dist/chunk-3I4ONZRT.js.map +0 -1
- package/dist/chunk-B7U6OKUR.js.map +0 -1
- package/dist/chunk-CWRI4JC3.js.map +0 -1
- package/dist/chunk-DGG2VY7B.js.map +0 -1
- package/dist/chunk-FJDRTVJX.js +0 -520
- package/dist/chunk-FJDRTVJX.js.map +0 -1
- package/dist/chunk-FO5GGFOV.js.map +0 -1
- package/dist/chunk-KBEIQP4G.js.map +0 -1
- package/dist/chunk-KJTVU3HZ.js.map +0 -1
- package/dist/chunk-KXGQYLFZ.js.map +0 -1
- package/dist/chunk-OFDN5NKS.js.map +0 -1
- package/dist/chunk-QN47QVBX.js.map +0 -1
- package/dist/chunk-R4YWBGY6.js.map +0 -1
- package/dist/chunk-RI2YL6TK.js.map +0 -1
- package/dist/chunk-SOSQILHO.js.map +0 -1
- package/dist/chunk-ULSWCPQG.js.map +0 -1
- package/dist/chunk-VOGGLPG5.js.map +0 -1
- package/dist/chunk-VPTAX5TR.js.map +0 -1
- package/dist/chunk-WHI5KEOX.js +0 -121
- package/dist/chunk-WHI5KEOX.js.map +0 -1
- package/dist/chunk-YKFCCV6S.js.map +0 -1
- package/dist/chunk-Z2TWEXR7.js.map +0 -1
- package/dist/cleanup-PJRIFFU4.js.map +0 -1
- package/dist/commit-IVP3M4HG.js.map +0 -1
- package/dist/contribute-VDZXHK5Y.js.map +0 -1
- package/dist/dev-server-7F622OEO.js.map +0 -1
- package/dist/ignite-IW35CDBD.js +0 -784
- package/dist/ignite-IW35CDBD.js.map +0 -1
- package/dist/init-676DHF6R.js.map +0 -1
- package/dist/issues-PJSOLOBJ.js.map +0 -1
- package/dist/open-544H7JF5.js.map +0 -1
- package/dist/plan-Q7ELXDLC.js.map +0 -1
- package/dist/recap-3W7COH7D.js.map +0 -1
- package/dist/run-QUXJKDQQ.js.map +0 -1
- package/dist/shell-QGECBLST.js.map +0 -1
- package/dist/summary-G2T4452H.js.map +0 -1
- /package/dist/{BranchNamingService-K6XNWQ6C.js.map → BranchNamingService-ECJHBB67.js.map} +0 -0
- /package/dist/{ClaudeContextManager-HR5JQKAI.js.map → ClaudeContextManager-QXX6ZFST.js.map} +0 -0
- /package/dist/{ClaudeService-TK7FMC2X.js.map → ClaudeService-NJNK2SUH.js.map} +0 -0
- /package/dist/{GitHubService-TGWJN4V4.js.map → GitHubService-MEHKHUQP.js.map} +0 -0
- /package/dist/{MetadataManager-W3C54UYT.js.map → IssueTrackerFactory-NG53YX5S.js.map} +0 -0
- /package/dist/{LoomLauncher-73NXL2CL.js.map → LoomLauncher-L64HHS3T.js.map} +0 -0
- /package/dist/{ProjectCapabilityDetector-N5L7T4IY.js.map → MetadataManager-5QZSTKNN.js.map} +0 -0
- /package/dist/{PromptTemplateManager-36YLQRHP.js.map → ProjectCapabilityDetector-5KSYUTBJ.js.map} +0 -0
- /package/dist/{SettingsManager-AW3JTJHD.js.map → PromptTemplateManager-DULSVRRE.js.map} +0 -0
- /package/dist/{claude-TP2QO3BU.js.map → SettingsManager-BQDQA3FK.js.map} +0 -0
- /package/dist/{build-THZI572G.js.map → build-5GO3XW26.js.map} +0 -0
- /package/dist/{chunk-NUACL52E.js.map → chunk-3D7WQM7I.js.map} +0 -0
- /package/dist/{chunk-A7NJF73J.js.map → chunk-5MWV33NN.js.map} +0 -0
- /package/dist/{chunk-KAYXR544.js.map → chunk-J5S7DFYC.js.map} +0 -0
- /package/dist/{chunk-AR5QKYNE.js.map → chunk-MNHZB4Z2.js.map} +0 -0
- /package/dist/{chunk-TL72BGP6.js.map → chunk-MORRVYPT.js.map} +0 -0
- /package/dist/{chunk-7ZEHSSUP.js.map → chunk-P4O6EH46.js.map} +0 -0
- /package/dist/{chunk-W6DP5RVR.js.map → chunk-UVD4CZKS.js.map} +0 -0
- /package/dist/{chunk-NWMORW3U.js.map → chunk-VECNX6VX.js.map} +0 -0
- /package/dist/{chunk-TC7APDKU.js.map → chunk-VGGST52X.js.map} +0 -0
- /package/dist/{chunk-4LKGCFGG.js.map → chunk-WWKOVDWC.js.map} +0 -0
- /package/dist/{chunk-IZIYLYPK.js.map → chunk-ZEWU5PZK.js.map} +0 -0
- /package/dist/{git-2QDQ2X2S.js.map → claude-P3NQR6IJ.js.map} +0 -0
- /package/dist/{compile-R2J65HBQ.js.map → compile-ZS4HYRX5.js.map} +0 -0
- /package/dist/{feedback-E7VET7CL.js.map → feedback-TMBXSCM5.js.map} +0 -0
- /package/dist/{neon-helpers-VVFFTLXE.js.map → git-ET64COO3.js.map} +0 -0
- /package/dist/{lint-CJM7BAIM.js.map → lint-6TQXDZ3T.js.map} +0 -0
- /package/dist/{projects-LH362JZQ.js.map → projects-2UOXFLNZ.js.map} +0 -0
- /package/dist/{rebase-YND35CIE.js.map → rebase-DWIB77KV.js.map} +0 -0
- /package/dist/{test-EA5NQFDC.js.map → test-F7JNJZYP.js.map} +0 -0
- /package/dist/{test-git-M7LSLEFL.js.map → test-git-BTAOIUE2.js.map} +0 -0
- /package/dist/{test-prefix-64NAAUON.js.map → test-prefix-Q6TFSU6F.js.map} +0 -0
- /package/dist/{test-webserver-OK6Z5FJM.js.map → test-webserver-EONCG7E7.js.map} +0 -0
- /package/dist/{vscode-AR5NNXXI.js.map → vscode-VA5X4P25.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/ignite.ts","../src/lib/ClaudeHookManager.ts"],"sourcesContent":["import path from 'path'\nimport { logger, createStderrLogger } from '../utils/logger.js'\nimport { withLogger } from '../utils/logger-context.js'\nimport { ClaudeWorkflowOptions } from '../lib/ClaudeService.js'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { launchClaude, ClaudeCliOptions } from '../utils/claude.js'\nimport { PromptTemplateManager, TemplateVariables, buildReviewTemplateVariables } from '../lib/PromptTemplateManager.js'\nimport { generateIssueManagementMcpConfig, generateRecapMcpConfig } from '../utils/mcp.js'\nimport { AgentManager } from '../lib/AgentManager.js'\nimport { IssueTrackerFactory } from '../lib/IssueTrackerFactory.js'\nimport { SettingsManager, type IloomSettings } from '../lib/SettingsManager.js'\nimport { MetadataManager } from '../lib/MetadataManager.js'\nimport { extractSettingsOverrides } from '../utils/cli-overrides.js'\nimport { FirstRunManager } from '../utils/FirstRunManager.js'\nimport { extractIssueNumber, isValidGitRepo, getWorktreeRoot } from '../utils/git.js'\nimport { getWorkspacePort } from '../utils/port.js'\nimport { readFile } from 'fs/promises'\nimport { ClaudeHookManager } from '../lib/ClaudeHookManager.js'\nimport type { OneShotMode } from '../types/index.js'\n\n/**\n * Error thrown when the spin command is run from an invalid location\n */\nexport class WorktreeValidationError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic readonly suggestion: string\n\t) {\n\t\tsuper(message)\n\t\tthis.name = 'WorktreeValidationError'\n\t}\n}\n\n/**\n * IgniteCommand: Auto-detect workspace context and launch Claude\n *\n * This command:\n * 1. Auto-detects context from current directory and git branch\n * 2. Loads appropriate prompt template with variable substitution\n * 3. Launches Claude with existing agent system (NO changes to agent loading)\n * 4. Executes in current terminal (not opening a new window)\n *\n * CRITICAL: This command works with agents exactly as they currently function.\n * NO modifications to agent loading mechanisms.\n */\nexport class IgniteCommand {\n\tprivate templateManager: PromptTemplateManager\n\tprivate gitWorktreeManager: GitWorktreeManager\n\tprivate agentManager: AgentManager\n\tprivate settingsManager: SettingsManager\n\tprivate firstRunManager: FirstRunManager\n\tprivate hookManager: ClaudeHookManager\n\tprivate settings?: IloomSettings\n\n\tconstructor(\n\t\ttemplateManager?: PromptTemplateManager,\n\t\tgitWorktreeManager?: GitWorktreeManager,\n\t\tagentManager?: AgentManager,\n\t\tsettingsManager?: SettingsManager,\n\t\tfirstRunManager?: FirstRunManager,\n\t\thookManager?: ClaudeHookManager\n\t) {\n\t\tthis.templateManager = templateManager ?? new PromptTemplateManager()\n\t\tthis.gitWorktreeManager = gitWorktreeManager ?? new GitWorktreeManager()\n\t\tthis.agentManager = agentManager ?? new AgentManager()\n\t\tthis.settingsManager = settingsManager ?? new SettingsManager()\n\t\tthis.firstRunManager = firstRunManager ?? new FirstRunManager('spin')\n\t\tthis.hookManager = hookManager ?? new ClaudeHookManager()\n\t}\n\n\t/**\n\t * Validate that we're not running from the main worktree\n\t * @throws WorktreeValidationError if running from main worktree\n\t */\n\tprivate async validateNotMainWorktree(): Promise<void> {\n\t\tconst currentDir = process.cwd()\n\n\t\t// Step 1: Check if we're in a git repository at all\n\t\tconst isGitRepo = await isValidGitRepo(currentDir)\n\t\tif (!isGitRepo) {\n\t\t\t// Not a git repo - let detectWorkspaceContext handle this gracefully\n\t\t\treturn\n\t\t}\n\n\t\t// Step 2: Get the worktree root (handles subdirectories)\n\t\tconst worktreeRoot = await getWorktreeRoot(currentDir)\n\t\tif (!worktreeRoot) {\n\t\t\t// Could not determine root - let detectWorkspaceContext handle this\n\t\t\treturn\n\t\t}\n\n\t\t// Step 3: Check if this path is a registered git worktree\n\t\tconst worktrees = await this.gitWorktreeManager.listWorktrees()\n\t\tconst currentWorktree = worktrees.find(wt => wt.path === worktreeRoot)\n\n\t\tif (!currentWorktree) {\n\t\t\t// Not a registered worktree - let detectWorkspaceContext handle this\n\t\t\treturn\n\t\t}\n\n\t\t// Step 4: Check if this is the main worktree\n\t\tconst isMain = await this.gitWorktreeManager.isMainWorktree(currentWorktree, this.settingsManager)\n\t\tif (isMain) {\n\t\t\tthrow new WorktreeValidationError(\n\t\t\t\t'You cannot run the command from the main worktree.',\n\t\t\t\t\"Navigate to a feature worktree created by 'il start <issue>' and run 'il spin' from there.\"\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Print mode options for headless/CI execution\n\t */\n\tpublic printOptions: {\n\t\tprint?: boolean\n\t\toutputFormat?: 'json' | 'stream-json' | 'text'\n\t\tverbose?: boolean\n\t\tjson?: boolean // --json flag: output final JSON object\n\t\tjsonStream?: boolean // --json-stream flag: stream JSONL to stdout\n\t} | undefined\n\n\t/**\n\t * Main entry point for spin command\n\t * @param oneShot - One-shot automation mode\n\t * @param printOptions - Print mode options for headless/CI execution\n\t */\n\tasync execute(oneShot?: OneShotMode, printOptions?: {\n\t\tprint?: boolean\n\t\toutputFormat?: 'json' | 'stream-json' | 'text'\n\t\tverbose?: boolean\n\t\tjson?: boolean\n\t\tjsonStream?: boolean\n\t}): Promise<void> {\n\t\tthis.printOptions = printOptions\n\n\t\t// Wrap execution in stderr logger for JSON modes to keep stdout clean\n\t\tconst isJsonMode = (this.printOptions?.json ?? false) || (this.printOptions?.jsonStream ?? false)\n\t\tif (isJsonMode) {\n\t\t\tconst jsonLogger = createStderrLogger()\n\t\t\treturn withLogger(jsonLogger, () => this.executeInternal(oneShot))\n\t\t}\n\n\t\treturn this.executeInternal(oneShot)\n\t}\n\n\t/**\n\t * Internal execution method (separated for withLogger wrapping)\n\t */\n\tprivate async executeInternal(oneShot?: OneShotMode): Promise<void> {\n\t\t// Set ILOOM=1 so hooks know this is an iloom session\n\t\t// This is inherited by the Claude child process\n\t\tprocess.env.ILOOM = '1'\n\n\t\t// Validate we're not in the main worktree first\n\t\ttry {\n\t\t\tawait this.validateNotMainWorktree()\n\t\t} catch (error) {\n\t\t\tif (error instanceof WorktreeValidationError) {\n\t\t\t\tlogger.error(error.message)\n\t\t\t\tlogger.info(error.suggestion)\n\t\t\t\tthrow error\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\n\t\ttry {\n\t\t\tlogger.info('🚀 Your loom is spinning up, please wait...')\n\n\t\t\t// Step 0.5: Check if this is first-time user\n\t\t\tconst isFirstRun = await this.firstRunManager.isFirstRun()\n\t\t\tif (isFirstRun) {\n\t\t\t\tlogger.success('Welcome to iloom! Preparing first-time experience...')\n\t\t\t}\n\n\t\t\t// Step 0.6: Install Claude hooks for VSCode integration (idempotent, quick)\n\t\t\tawait this.hookManager.installHooks()\n\n\t\t\t// Step 1: Auto-detect workspace context\n\t\t\tconst context = await this.detectWorkspaceContext()\n\n\t\t\tlogger.debug('Auto-detected workspace context', { context })\n\n\t\t\t// Inform user what context was detected\n\t\t\tthis.logDetectedContext(context)\n\n\t\t\tlogger.info('📝 Loading prompt template and preparing Claude...')\n\n\t\t\t// Step 2: Read metadata early to get draftPrNumber and draftPrUrl for templates and MCP config\n\t\t\tconst metadataManager = new MetadataManager()\n\t\t\tconst metadata = await metadataManager.readMetadata(context.workspacePath)\n\t\t\tconst draftPrNumber = metadata?.draftPrNumber ?? undefined\n\t\t\t// Extract draft PR URL from prUrls map if available\n\t\t\tconst draftPrUrl = draftPrNumber && metadata?.prUrls?.[String(draftPrNumber)]\n\t\t\t\t? metadata.prUrls[String(draftPrNumber)]\n\t\t\t\t: undefined\n\n\t\t\t// Step 2.0.4: Determine effective oneShot mode\n\t\t\t// If print mode is enabled, force noReview to skip interactive reviews\n\t\t\t// If oneShot is provided (any value including 'default'), use it\n\t\t\t// If oneShot is undefined (not passed), use metadata or fallback to 'default'\n\t\t\t// Note: metadata?.oneShot can be null (for legacy looms), so we need double nullish coalescing\n\t\t\tconst storedOneShot = metadata?.oneShot ?? 'default'\n\t\t\tconst isHeadlessForOneShot = this.printOptions?.print ?? false\n\t\t\tconst effectiveOneShot: OneShotMode = isHeadlessForOneShot ? 'noReview' : (oneShot ?? storedOneShot)\n\n\t\t\t// Step 2.0.5: Load settings early if not cached (needed for port calculation)\n\t\t\tif (!this.settings) {\n\t\t\t\tconst cliOverrides = extractSettingsOverrides()\n\t\t\t\tthis.settings = await this.settingsManager.loadSettings(undefined, cliOverrides)\n\t\t\t}\n\n\t\t\t// Step 2.0.6: Calculate port for web-capable looms\n\t\t\tif (metadata?.capabilities?.includes('web') && context.branchName) {\n\t\t\t\tconst basePort = this.settings?.capabilities?.web?.basePort ?? 3000\n\t\t\t\tcontext.port = await getWorkspacePort({\n\t\t\t\t\tbasePort,\n\t\t\t\t\tworktreePath: context.workspacePath,\n\t\t\t\t\tworktreeBranch: context.branchName,\n\t\t\t\t})\n\t\t\t\tlogger.info(`🌐 Development server port: ${context.port}`)\n\t\t\t}\n\n\t\t\t// Step 2.1: Get prompt template with variable substitution\n\t\t\tconst variables = this.buildTemplateVariables(context, effectiveOneShot, draftPrNumber, draftPrUrl)\n\n\t\t\t// Step 2.5: Add first-time user context if needed\n\t\t\tif (isFirstRun) {\n\t\t\t\tvariables.FIRST_TIME_USER = true\n\t\t\t\tvariables.README_CONTENT = await this.loadReadmeContent()\n\t\t\t\tvariables.SETTINGS_SCHEMA_CONTENT = await this.loadSettingsSchemaContent()\n\t\t\t}\n\n\t\t\tconst systemInstructions = await this.templateManager.getPrompt(context.type, variables)\n\n\t\t\t// User prompt to trigger the workflow (includes one-shot bypass instructions if needed)\n\t\t\tconst userPrompt = this.buildUserPrompt(effectiveOneShot)\n\n\t\t\t// Step 3: Determine model and permission mode based on workflow type\n\t\t\tconst model = this.settingsManager.getSpinModel(this.settings)\n\t\t\tlet permissionMode = this.getPermissionModeForWorkflow(context.type)\n\n\t\t\t// Override permission mode if bypassPermissions oneShot mode\n\t\t\tif (effectiveOneShot === 'bypassPermissions') {\n\t\t\t\tpermissionMode = 'bypassPermissions'\n\t\t\t}\n\n\t\t\t// Display warning if bypassPermissions is used\n\t\t\tif (permissionMode === 'bypassPermissions') {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t'⚠️ WARNING: Using bypassPermissions mode - Claude will execute all tool calls without confirmation. ' +\n\t\t\t\t\t\t'This can be dangerous. Use with caution.'\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// Step 4: Build Claude CLI options\n\t\t\t// Session ID must come from loom metadata - no fallback generation\n\t\t\tconst sessionId = metadata?.sessionId\n\t\t\tif (!sessionId) {\n\t\t\t\tthrow new Error('No session ID found in loom metadata. This loom may need to be recreated with `il start`.')\n\t\t\t}\n\t\t\tlogger.debug('Using session ID from metadata', { sessionId })\n\n\t\t\t// Determine if we're in print/headless mode\n\t\t\tconst isHeadless = this.printOptions?.print ?? false\n\n\t\t\tconst claudeOptions: ClaudeCliOptions = {\n\t\t\t\theadless: isHeadless,\n\t\t\t\taddDir: context.workspacePath,\n\t\t\t\tsessionId, // Enable Claude Code session resume\n\t\t\t}\n\n\t\t\t// Add optional model if present\n\t\t\tif (model !== undefined) {\n\t\t\t\tclaudeOptions.model = model\n\t\t\t}\n\n\t\t\t// Add permission mode if not default\n\t\t\t// When print mode is enabled, force bypassPermissions for autonomous execution\n\t\t\tif (isHeadless) {\n\t\t\t\tpermissionMode = 'bypassPermissions'\n\t\t\t}\n\t\t\tif (permissionMode !== undefined && permissionMode !== 'default') {\n\t\t\t\tclaudeOptions.permissionMode = permissionMode\n\t\t\t}\n\n\t\t\t// Add output format and verbose options if provided (print mode only)\n\t\t\tif (this.printOptions?.outputFormat !== undefined) {\n\t\t\t\tclaudeOptions.outputFormat = this.printOptions.outputFormat\n\t\t\t}\n\t\t\tif (this.printOptions?.verbose !== undefined) {\n\t\t\t\tclaudeOptions.verbose = this.printOptions.verbose\n\t\t\t}\n\n\t\t\t// Add JSON mode if specified (requires print mode)\n\t\t\tif (this.printOptions?.json) {\n\t\t\t\tclaudeOptions.jsonMode = 'json'\n\t\t\t\tclaudeOptions.outputFormat = 'stream-json' // Force stream-json for parsing\n\t\t\t} else if (this.printOptions?.jsonStream) {\n\t\t\t\tclaudeOptions.jsonMode = 'stream'\n\t\t\t\tclaudeOptions.outputFormat = 'stream-json' // Force stream-json for streaming\n\t\t\t}\n\n\t\t\t// Add optional branch name for context\n\t\t\tif (context.branchName !== undefined) {\n\t\t\t\tclaudeOptions.branchName = context.branchName\n\t\t\t}\n\n\t\t\t// Step 4.5: Generate MCP config and tool filtering for issue/PR workflows\n\t\t\tlet mcpConfig: Record<string, unknown>[] | undefined\n\t\t\tlet allowedTools: string[] | undefined\n\t\t\tlet disallowedTools: string[] | undefined\n\n\t\t\tif (context.type === 'issue' || context.type === 'pr') {\n\t\t\t\ttry {\n\t\t\t\t\tconst provider = this.settings ? IssueTrackerFactory.getProviderName(this.settings) : 'github'\n\t\t\t\t\t// Pass draftPrNumber to route comments to PR when in github-draft-pr mode\n\t\t\t\t\tmcpConfig = await generateIssueManagementMcpConfig(context.type, undefined, provider, this.settings, draftPrNumber)\n\t\t\t\t\tlogger.debug('Generated MCP configuration for issue management', { provider, draftPrNumber })\n\n\t\t\t\t\t// Configure tool filtering for issue/PR workflows\n\t\t\t\t\t// Note: set_goal is only allowed for PR workflow (user's purpose unclear)\n\t\t\t\t\t// For issue workflow, the issue title provides context so set_goal is not needed\n\t\t\t\t\tconst baseTools = [\n\t\t\t\t\t\t'mcp__issue_management__get_issue',\n\t\t\t\t\t\t'mcp__issue_management__get_comment',\n\t\t\t\t\t\t'mcp__issue_management__create_comment',\n\t\t\t\t\t\t'mcp__issue_management__update_comment',\n\t\t\t\t\t\t'mcp__issue_management__create_issue',\n\t\t\t\t\t\t'mcp__recap__add_entry',\n\t\t\t\t\t\t'mcp__recap__get_recap',\n\t\t\t\t\t\t'mcp__recap__add_artifact',\n\t\t\t\t\t\t'mcp__recap__set_complexity'\n\t\t\t\t\t]\n\t\t\t\t\tallowedTools = context.type === 'pr'\n\t\t\t\t\t\t? [...baseTools, 'mcp__issue_management__get_pr', 'mcp__recap__set_goal']\n\t\t\t\t\t\t: baseTools\n\t\t\t\t\tdisallowedTools = ['Bash(gh api:*), Bash(gh issue view:*), Bash(gh pr view:*), Bash(gh issue comment:*)']\n\n\t\t\t\t\tlogger.debug('Configured tool filtering for issue/PR workflow', { allowedTools, disallowedTools })\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// Log warning but continue without MCP\n\t\t\t\t\tlogger.warn(`Failed to generate MCP config: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Regular/branch workflow - allow recap tools (including set_goal since no issue/PR context)\n\t\t\t\tallowedTools = [\n\t\t\t\t\t'mcp__recap__set_goal',\n\t\t\t\t\t'mcp__recap__add_entry',\n\t\t\t\t\t'mcp__recap__get_recap',\n\t\t\t\t\t'mcp__recap__set_complexity',\n\t\t\t\t]\n\t\t\t\tlogger.debug('Configured tool filtering for regular workflow', { allowedTools })\n\t\t\t}\n\n\t\t\t// Step 4.5.1: Generate recap MCP config (always added for all workflow types)\n\t\t\t// Reuses metadata already read in Step 2\n\t\t\ttry {\n\t\t\t\tif (!metadata) {\n\t\t\t\t\tthrow new Error('No loom metadata found for this workspace')\n\t\t\t\t}\n\t\t\t\tconst recapMcpConfig = generateRecapMcpConfig(context.workspacePath, metadata)\n\t\t\t\tif (mcpConfig) {\n\t\t\t\t\tmcpConfig.push(...recapMcpConfig)\n\t\t\t\t} else {\n\t\t\t\t\tmcpConfig = recapMcpConfig\n\t\t\t\t}\n\t\t\t\tlogger.debug('Generated MCP configuration for recap server')\n\t\t\t} catch (error) {\n\t\t\t\t// Log warning but continue without recap MCP\n\t\t\t\tlogger.warn(`Failed to generate recap MCP config: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t\t\t}\n\n\t\t\t// Step 4.6: Load agent configurations using cached settings\n\t\t\tlet agents: Record<string, unknown> | undefined\n\t\t\ttry {\n\t\t\t\t// Use cached settings from Step 2.5\n\t\t\t\tif (this.settings?.agents && Object.keys(this.settings.agents).length > 0) {\n\t\t\t\t\tlogger.debug('Loaded project settings', {\n\t\t\t\t\t\tagentOverrides: Object.keys(this.settings.agents),\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\t// Load agents with settings overrides and template variables for substitution\n\t\t\t\t// Exclude init-only agents (e.g., framework-detector which is only for il init)\n\t\t\t\tconst loadedAgents = await this.agentManager.loadAgents(\n\t\t\t\t\tthis.settings,\n\t\t\t\t\tvariables,\n\t\t\t\t\t['*.md', '!iloom-framework-detector.md']\n\t\t\t\t)\n\t\t\t\tagents = this.agentManager.formatForCli(loadedAgents)\n\t\t\t\tlogger.debug('Loaded agent configurations', {\n\t\t\t\t\tagentCount: Object.keys(agents).length,\n\t\t\t\t\tagentNames: Object.keys(agents),\n\t\t\t\t})\n\t\t\t} catch (error) {\n\t\t\t\t// Log warning but continue without agents\n\t\t\t\tlogger.warn(`Failed to load agents: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t\t\t}\n\n\t\t\tlogger.debug('Launching Claude in current terminal', {\n\t\t\t\ttype: context.type,\n\t\t\t\tmodel,\n\t\t\t\tpermissionMode,\n\t\t\t\tworkspacePath: context.workspacePath,\n\t\t\t\thasMcpConfig: !!mcpConfig,\n\t\t\t})\n\n\t\t\tlogger.info(isHeadless ? '✨ Launching Claude in headless mode...' : '✨ Launching Claude in current terminal...')\n\n\t\t\t// Step 5: Launch Claude with system instructions appended and user prompt\n\t\t\tconst claudeResult = await launchClaude(userPrompt, {\n\t\t\t\t...claudeOptions,\n\t\t\t\tappendSystemPrompt: systemInstructions,\n\t\t\t\t...(mcpConfig && { mcpConfig }),\n\t\t\t\t...(allowedTools && { allowedTools }),\n\t\t\t\t...(disallowedTools && { disallowedTools }),\n\t\t\t\t...(agents && { agents }),\n\t\t\t})\n\n\t\t\t// Output final JSON for --json mode (--json-stream already streamed to stdout)\n\t\t\tif (this.printOptions?.json) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.log(JSON.stringify({\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\toutput: claudeResult ?? ''\n\t\t\t\t}))\n\t\t\t}\n\n\t\t\t// Step 6: Mark as run after successful launch\n\t\t\tif (isFirstRun) {\n\t\t\t\tawait this.firstRunManager.markAsRun()\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : 'Unknown error'\n\t\t\t// Output error as JSON for --json mode\n\t\t\tif (this.printOptions?.json) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.log(JSON.stringify({\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: errorMessage\n\t\t\t\t}))\n\t\t\t} else {\n\t\t\t\tlogger.error(`Failed to launch Claude: ${errorMessage}`)\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Log user-friendly information about detected context\n\t */\n\tprivate logDetectedContext(context: ClaudeWorkflowOptions): void {\n\t\tif (context.type === 'issue') {\n\t\t\tlogger.info(`🎯 Detected issue workflow: Issue #${context.issueNumber}`)\n\t\t} else if (context.type === 'pr') {\n\t\t\tlogger.info(`🔄 Detected PR workflow: PR #${context.prNumber}`)\n\t\t} else {\n\t\t\tlogger.info('🌟 Detected regular workflow')\n\t\t}\n\n\t\tif (context.branchName) {\n\t\t\tlogger.info(`🌿 Working on branch: ${context.branchName}`)\n\t\t}\n\n\t\tif (context.port) {\n\t\t\tlogger.info(`🌐 Development server port: ${context.port}`)\n\t\t}\n\t}\n\n\t/**\n\t * Build template variables from context\n\t */\n\tprivate buildTemplateVariables(\n\t\tcontext: ClaudeWorkflowOptions,\n\t\toneShot: OneShotMode,\n\t\tdraftPrNumber?: number,\n\t\tdraftPrUrl?: string\n\t): TemplateVariables {\n\t\tconst variables: TemplateVariables = {\n\t\t\tWORKSPACE_PATH: context.workspacePath,\n\t\t}\n\n\t\tif (context.issueNumber !== undefined) {\n\t\t\tvariables.ISSUE_NUMBER = context.issueNumber\n\t\t}\n\n\t\tif (context.prNumber !== undefined) {\n\t\t\tvariables.PR_NUMBER = context.prNumber\n\t\t}\n\n\t\tif (context.title !== undefined) {\n\t\t\tif (context.type === 'issue') {\n\t\t\t\tvariables.ISSUE_TITLE = context.title\n\t\t\t} else if (context.type === 'pr') {\n\t\t\t\tvariables.PR_TITLE = context.title\n\t\t\t}\n\t\t}\n\n\t\tif (context.port !== undefined) {\n\t\t\tvariables.PORT = context.port\n\t\t}\n\n\t\t// Set ONE_SHOT_MODE or INTERACTIVE_MODE flag for template conditional sections\n\t\tif (oneShot === 'noReview' || oneShot === 'bypassPermissions') {\n\t\t\tvariables.ONE_SHOT_MODE = true\n\t\t} else {\n\t\t\tvariables.INTERACTIVE_MODE = true\n\t\t}\n\n\t\t// Set review configuration variables (code reviewer + artifact reviewer + per-agent flags)\n\t\tObject.assign(variables, buildReviewTemplateVariables(this.settings?.agents))\n\n\t\t// Set draft PR mode flags (mutually exclusive)\n\t\t// When draftPrNumber is set, we're in github-draft-pr mode\n\t\tif (draftPrNumber !== undefined) {\n\t\t\tvariables.DRAFT_PR_MODE = true\n\t\t\tvariables.DRAFT_PR_NUMBER = draftPrNumber\n\t\t\tif (draftPrUrl) {\n\t\t\t\tvariables.DRAFT_PR_URL = draftPrUrl\n\t\t\t}\n\t\t\t// Set AUTO_COMMIT_PUSH when in draft PR mode and not explicitly disabled\n\t\t\t// Default is true (enabled) for draft PR mode\n\t\t\tconst autoCommitPushEnabled = this.settings?.mergeBehavior?.autoCommitPush !== false\n\t\t\tvariables.AUTO_COMMIT_PUSH = autoCommitPushEnabled\n\t\t\t// Set GIT_REMOTE from settings or default to 'origin'\n\t\t\tconst remote = this.settings?.mergeBehavior?.remote ?? 'origin'\n\t\t\tif (!/^[a-zA-Z0-9_-]+$/.test(remote)) {\n\t\t\t\tthrow new Error(`Invalid git remote name: \"${remote}\". Remote names can only contain alphanumeric characters, underscores, and hyphens.`)\n\t\t\t}\n\t\t\tvariables.GIT_REMOTE = remote\n\t\t} else if (context.type === 'regular') {\n\t\t\t// Branch mode without draft PR\n\t\t\tvariables.STANDARD_BRANCH_MODE = true\n\t\t} else {\n\t\t\t// Issue/PR mode without draft PR\n\t\t\tvariables.STANDARD_ISSUE_MODE = true\n\t\t}\n\n\t\treturn variables\n\t}\n\n\t/**\n\t * Get the appropriate permission mode for a workflow type\n\t * Same logic as ClaudeService.getPermissionModeForWorkflow()\n\t */\n\tprivate getPermissionModeForWorkflow(\n\t\ttype: 'issue' | 'pr' | 'regular'\n\t): ClaudeCliOptions['permissionMode'] {\n\t\t// Check settings for configured permission mode\n\t\tif (this.settings?.workflows) {\n\t\t\tconst workflowConfig =\n\t\t\t\ttype === 'issue'\n\t\t\t\t\t? this.settings.workflows.issue\n\t\t\t\t\t: type === 'pr'\n\t\t\t\t\t\t? this.settings.workflows.pr\n\t\t\t\t\t\t: this.settings.workflows.regular\n\n\t\t\tif (workflowConfig?.permissionMode) {\n\t\t\t\treturn workflowConfig.permissionMode\n\t\t\t}\n\t\t}\n\n\t\t// Fall back to current defaults\n\t\tif (type === 'issue') {\n\t\t\treturn 'acceptEdits'\n\t\t}\n\t\t// For PR and regular workflows, use default permissions\n\t\treturn 'default'\n\t}\n\n\t/**\n\t * Auto-detect workspace context from current directory and git branch\n\t *\n\t * Detection priority:\n\t * 1. Directory name patterns (_pr_N, issue-N)\n\t * 2. Git branch name patterns\n\t * 3. Fallback to 'regular' workflow\n\t *\n\t * This leverages the same logic as FinishCommand.autoDetectFromCurrentDirectory()\n\t */\n\tprivate async detectWorkspaceContext(): Promise<ClaudeWorkflowOptions> {\n\t\tconst workspacePath = process.cwd()\n\t\tconst currentDir = path.basename(workspacePath)\n\n\t\t// Check for PR worktree pattern: _pr_N suffix\n\t\t// Pattern: /.*_pr_(\\d+)$/\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\n\t\t\treturn this.buildContextForPR(prNumber, workspacePath)\n\t\t}\n\n\t\t// Check for issue pattern in directory name\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\n\t\t\treturn this.buildContextForIssue(issueNumber, workspacePath)\n\t\t}\n\n\t\t// Fallback: Try to extract from git branch name\n\t\ttry {\n\t\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\t\tconst currentBranch = repoInfo.currentBranch\n\n\t\t\tif (currentBranch) {\n\t\t\t\t// Try to extract issue from branch name\n\t\t\t\tconst branchIssueNumber = extractIssueNumber(currentBranch)\n\t\t\t\tif (branchIssueNumber !== null) {\n\t\t\t\t\tlogger.debug(`Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`)\n\n\t\t\t\t\treturn this.buildContextForIssue(branchIssueNumber, workspacePath, currentBranch)\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Git command failed - not a git repo or other git error\n\t\t\tlogger.debug('Could not detect from git branch', { error })\n\t\t}\n\n\t\t// Last resort: use regular workflow\n\t\tlogger.debug('No specific context detected, using regular workflow')\n\t\treturn this.buildContextForRegular(workspacePath)\n\t}\n\n\t/**\n\t * Build context for issue workflow\n\t */\n\tprivate async buildContextForIssue(\n\t\tissueNumber: string | number,\n\t\tworkspacePath: string,\n\t\tbranchName?: string\n\t): Promise<ClaudeWorkflowOptions> {\n\t\t// Get branch name if not provided\n\t\tif (!branchName) {\n\t\t\ttry {\n\t\t\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\t\t\tbranchName = repoInfo.currentBranch ?? undefined\n\t\t\t} catch {\n\t\t\t\t// Ignore git errors\n\t\t\t}\n\t\t}\n\n\t\tconst context: ClaudeWorkflowOptions = {\n\t\t\ttype: 'issue',\n\t\t\tissueNumber,\n\t\t\tworkspacePath,\n\t\t\theadless: false, // Interactive mode\n\t\t}\n\n\t\tif (branchName !== undefined) {\n\t\t\tcontext.branchName = branchName\n\t\t}\n\n\t\treturn context\n\t}\n\n\t/**\n\t * Build context for PR workflow\n\t */\n\tprivate async buildContextForPR(\n\t\tprNumber: number,\n\t\tworkspacePath: string\n\t): Promise<ClaudeWorkflowOptions> {\n\t\t// Get branch name\n\t\tlet branchName: string | undefined\n\t\ttry {\n\t\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\t\tbranchName = repoInfo.currentBranch ?? undefined\n\t\t} catch {\n\t\t\t// Ignore git errors\n\t\t}\n\n\t\tconst context: ClaudeWorkflowOptions = {\n\t\t\ttype: 'pr',\n\t\t\tprNumber,\n\t\t\tworkspacePath,\n\t\t\theadless: false, // Interactive mode\n\t\t}\n\n\t\tif (branchName !== undefined) {\n\t\t\tcontext.branchName = branchName\n\t\t}\n\n\t\treturn context\n\t}\n\n\t/**\n\t * Build context for regular workflow\n\t */\n\tprivate async buildContextForRegular(workspacePath: string): Promise<ClaudeWorkflowOptions> {\n\t\t// Get branch name\n\t\tlet branchName: string | undefined\n\t\ttry {\n\t\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\t\tbranchName = repoInfo.currentBranch ?? undefined\n\t\t} catch {\n\t\t\t// Ignore git errors\n\t\t}\n\n\t\tconst context: ClaudeWorkflowOptions = {\n\t\t\ttype: 'regular',\n\t\t\tworkspacePath,\n\t\t\theadless: false, // Interactive mode\n\t\t}\n\n\t\tif (branchName !== undefined) {\n\t\t\tcontext.branchName = branchName\n\t\t}\n\n\t\treturn context\n\t}\n\n\n\t/**\n\t * Build user prompt based on one-shot mode\n\t */\n\tprivate buildUserPrompt(oneShot: OneShotMode = 'default'): string {\n\t\t// For one-shot modes, add bypass instructions to override template approval requirements\n\t\tif (oneShot === 'noReview' || oneShot === 'bypassPermissions') {\n\t\t\treturn 'Guide the user through the iloom workflow! The user has requested you move through the workflow without awaiting confirmation. This supersedes any other guidance.'\n\t\t}\n\n\t\t// Default mode: simple \"Go!\" prompt\n\t\treturn 'Guide the user through the iloom workflow!'\n\t}\n\n\t/**\n\t * Load README.md content for first-time users\n\t * Walks up from dist directory to find README.md in project root\n\t */\n\tprivate async loadReadmeContent(): Promise<string> {\n\t\ttry {\n\t\t\t// Walk up from current file location to find README.md\n\t\t\t// Use same pattern as PromptTemplateManager for finding files\n\t\t\tlet currentDir = path.dirname(new URL(import.meta.url).pathname)\n\n\t\t\t// Walk up to find README.md\n\t\t\twhile (currentDir !== path.dirname(currentDir)) {\n\t\t\t\tconst readmePath = path.join(currentDir, 'README.md')\n\t\t\t\ttry {\n\t\t\t\t\tconst content = await readFile(readmePath, 'utf-8')\n\t\t\t\t\tlogger.debug('Loaded README.md for first-time user', { readmePath })\n\t\t\t\t\treturn content\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\tlogger.debug('README.md not found, returning empty string')\n\t\t\treturn ''\n\t\t} catch (error) {\n\t\t\t// Graceful degradation - return empty string on error\n\t\t\tlogger.debug(`Failed to load README.md: ${error}`)\n\t\t\treturn ''\n\t\t}\n\t}\n\n\t/**\n\t * Load settings schema content for first-time users\n\t * Walks up from dist directory to find .iloom/README.md\n\t */\n\tprivate async loadSettingsSchemaContent(): Promise<string> {\n\t\ttry {\n\t\t\t// Walk up from current file location to find .iloom/README.md\n\t\t\tlet currentDir = path.dirname(new URL(import.meta.url).pathname)\n\n\t\t\t// Walk up to find .iloom/README.md\n\t\t\twhile (currentDir !== path.dirname(currentDir)) {\n\t\t\t\tconst schemaPath = path.join(currentDir, '.iloom', 'README.md')\n\t\t\t\ttry {\n\t\t\t\t\tconst content = await readFile(schemaPath, 'utf-8')\n\t\t\t\t\tlogger.debug('Loaded .iloom/README.md for first-time user', { schemaPath })\n\t\t\t\t\treturn content\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\tlogger.debug('.iloom/README.md not found, returning empty string')\n\t\t\treturn ''\n\t\t} catch (error) {\n\t\t\t// Graceful degradation - return empty string on error\n\t\t\tlogger.debug(`Failed to load .iloom/README.md: ${error}`)\n\t\t\treturn ''\n\t\t}\n\t}\n}\n","import os from 'os'\nimport path from 'path'\nimport fs from 'fs-extra'\nimport { parse, modify, applyEdits, ParseError } from 'jsonc-parser'\nimport { fileURLToPath } from 'url'\nimport { accessSync } from 'fs'\nimport { logger } from '../utils/logger.js'\n\n/**\n * Hook configuration for a single event\n */\ninterface HookEntry {\n\ttype: 'command'\n\tcommand: string\n\ttimeout?: number\n}\n\n/**\n * Hook event configuration\n */\ninterface HookEventConfig {\n\tmatcher?: string\n\thooks: HookEntry[]\n}\n\n/**\n * Claude settings.json structure (partial)\n */\ninterface ClaudeSettings {\n\thooks?: Record<string, HookEventConfig[]>\n\t[key: string]: unknown\n}\n\n/**\n * Manages installation of Claude Code hooks to ~/.claude/\n *\n * Hooks enable real-time monitoring of Claude session state\n * via Unix socket communication with the iloom-vscode extension.\n */\nexport class ClaudeHookManager {\n\tprivate claudeDir: string\n\tprivate hooksDir: string\n\tprivate settingsPath: string\n\tprivate templateDir: string\n\n\tconstructor() {\n\t\t// Initialize paths using os.homedir()\n\t\tthis.claudeDir = path.join(os.homedir(), '.claude')\n\t\tthis.hooksDir = path.join(this.claudeDir, 'hooks')\n\t\tthis.settingsPath = path.join(this.claudeDir, 'settings.json')\n\n\t\t// Find templates relative to the package installation\n\t\t// Same pattern as PromptTemplateManager\n\t\tconst currentFileUrl = import.meta.url\n\t\tconst currentFilePath = fileURLToPath(currentFileUrl)\n\t\tconst distDir = path.dirname(currentFilePath)\n\n\t\t// Walk up to find the hooks template directory\n\t\tlet templateDir = path.join(distDir, 'hooks')\n\t\tlet currentDir = distDir\n\n\t\twhile (currentDir !== path.dirname(currentDir)) {\n\t\t\tconst candidatePath = path.join(currentDir, 'hooks')\n\t\t\ttry {\n\t\t\t\taccessSync(candidatePath)\n\t\t\t\ttemplateDir = candidatePath\n\t\t\t\tbreak\n\t\t\t} catch {\n\t\t\t\tcurrentDir = path.dirname(currentDir)\n\t\t\t}\n\t\t}\n\n\t\tthis.templateDir = templateDir\n\t\tlogger.debug('ClaudeHookManager initialized', {\n\t\t\tclaudeDir: this.claudeDir,\n\t\t\thooksDir: this.hooksDir,\n\t\t\tsettingsPath: this.settingsPath,\n\t\t\ttemplateDir: this.templateDir\n\t\t})\n\t}\n\n\t/**\n\t * Install Claude hooks for VSCode integration\n\t *\n\t * This is idempotent - safe to call on every spin.\n\t * Installs hook script to ~/.claude/hooks/ and merges\n\t * hook configuration into ~/.claude/settings.json\n\t */\n\tasync installHooks(): Promise<void> {\n\t\ttry {\n\t\t\t// 1. Create ~/.claude/hooks if missing\n\t\t\tawait fs.ensureDir(this.hooksDir)\n\n\t\t\t// 2. Install hook script from bundled templates\n\t\t\tawait this.installHookScript()\n\n\t\t\t// 3. Merge hook config into settings.json\n\t\t\tawait this.mergeHookConfig()\n\n\t\t\tlogger.debug('Claude hooks installed successfully')\n\t\t} catch (error) {\n\t\t\t// Log warning but don't fail - hooks are optional enhancement\n\t\t\tlogger.warn(\n\t\t\t\t`Failed to install Claude hooks: ${error instanceof Error ? error.message : 'Unknown error'}`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Check if hooks are already installed\n\t */\n\tasync isHooksInstalled(): Promise<boolean> {\n\t\ttry {\n\t\t\t// Check if hook script exists\n\t\t\tconst hookScriptPath = path.join(this.hooksDir, 'iloom-hook.js')\n\t\t\tif (!(await fs.pathExists(hookScriptPath))) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// Check if settings.json has our hooks\n\t\t\tif (!(await fs.pathExists(this.settingsPath))) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tconst content = await fs.readFile(this.settingsPath, 'utf8')\n\t\t\tconst errors: ParseError[] = []\n\t\t\tconst settings = parse(content, errors, { allowTrailingComma: true }) as ClaudeSettings\n\n\t\t\tif (errors.length > 0 || !settings?.hooks) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// Check if our hooks are registered (check for SessionStart as indicator)\n\t\t\treturn Array.isArray(settings.hooks.SessionStart)\n\t\t} catch {\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Install the hook script from bundled templates\n\t * Skips write if destination already has identical content\n\t */\n\tprivate async installHookScript(): Promise<void> {\n\t\tconst sourcePath = path.join(this.templateDir, 'iloom-hook.js')\n\t\tconst destPath = path.join(this.hooksDir, 'iloom-hook.js')\n\n\t\t// Check if source template exists\n\t\tif (!(await fs.pathExists(sourcePath))) {\n\t\t\tthrow new Error(`Hook template not found at ${sourcePath}`)\n\t\t}\n\n\t\t// Skip if destination exists and content matches\n\t\tif (await fs.pathExists(destPath)) {\n\t\t\tconst [sourceContent, destContent] = await Promise.all([\n\t\t\t\tfs.readFile(sourcePath, 'utf8'),\n\t\t\t\tfs.readFile(destPath, 'utf8')\n\t\t\t])\n\t\t\tif (sourceContent === destContent) {\n\t\t\t\tlogger.debug('Hook script already up to date, skipping')\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\t// Copy hook script (only when content differs or doesn't exist)\n\t\tawait fs.copyFile(sourcePath, destPath)\n\t\tlogger.debug('Hook script installed', { sourcePath, destPath })\n\t}\n\n\t/**\n\t * Merge hook configuration into settings.json\n\t * Preserves existing user hooks and comments\n\t */\n\tprivate async mergeHookConfig(): Promise<void> {\n\t\t// Ensure ~/.claude directory exists\n\t\tawait fs.ensureDir(this.claudeDir)\n\n\t\t// Read existing settings (or create empty)\n\t\tlet existingContent = '{}'\n\t\tlet existingSettings: ClaudeSettings = {}\n\n\t\tif (await fs.pathExists(this.settingsPath)) {\n\t\t\texistingContent = await fs.readFile(this.settingsPath, 'utf8')\n\t\t\tconst errors: ParseError[] = []\n\t\t\texistingSettings = parse(existingContent, errors, { allowTrailingComma: true }) as ClaudeSettings\n\n\t\t\tif (errors.length > 0) {\n\t\t\t\tlogger.warn('Existing settings.json has parse errors, will attempt to merge anyway')\n\t\t\t}\n\t\t}\n\n\t\t// Get our hook configuration\n\t\tconst ourHooks = this.getHookConfig()\n\n\t\t// Merge hooks - preserve user's existing hooks on same events\n\t\tconst mergedHooks: Record<string, HookEventConfig[]> = { ...(existingSettings.hooks ?? {}) }\n\t\tlet hooksAdded = false\n\n\t\tfor (const [eventName, eventConfigs] of Object.entries(ourHooks)) {\n\t\t\tconst existing = mergedHooks[eventName] ?? []\n\n\t\t\t// Check if our hook is already registered\n\t\t\tconst ourConfig = eventConfigs[0]\n\t\t\tconst ourCommand = ourConfig?.hooks?.[0]?.command\n\t\t\tconst existingConfigIndex = existing.findIndex(\n\t\t\t\t(config) => config.hooks?.some((h) => h.command === ourCommand)\n\t\t\t)\n\n\t\t\tif (existingConfigIndex === -1) {\n\t\t\t\t// Add our hook config to the event\n\t\t\t\tmergedHooks[eventName] = [...existing, ...eventConfigs]\n\t\t\t\thooksAdded = true\n\t\t\t} else {\n\t\t\t\t// Hook is already registered - check if we need to update the matcher\n\t\t\t\tconst existingConfig = existing[existingConfigIndex]\n\t\t\t\tconst ourMatcher = ourConfig?.matcher\n\n\t\t\t\t// Update matcher if our config has one and existing doesn't match\n\t\t\t\tif (existingConfig && ourMatcher !== undefined && existingConfig.matcher !== ourMatcher) {\n\t\t\t\t\texisting[existingConfigIndex] = {\n\t\t\t\t\t\t...existingConfig,\n\t\t\t\t\t\tmatcher: ourMatcher\n\t\t\t\t\t}\n\t\t\t\t\thooksAdded = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Skip write if no new hooks were added\n\t\tif (!hooksAdded) {\n\t\t\tlogger.debug('All hooks already registered, skipping settings.json update')\n\t\t\treturn\n\t\t}\n\n\t\t// Write updated settings\n\t\tlet content: string\n\n\t\t// Check if existing content has comments\n\t\tif (existingContent.includes('//') || existingContent.includes('/*')) {\n\t\t\t// Use jsonc-parser to preserve comments\n\t\t\tlet modifiedContent = existingContent\n\t\t\tconst edits = modify(modifiedContent, ['hooks'], mergedHooks, {})\n\t\t\tcontent = applyEdits(modifiedContent, edits)\n\t\t} else {\n\t\t\t// No comments - use JSON.stringify\n\t\t\tconst updatedSettings: ClaudeSettings = {\n\t\t\t\t...existingSettings,\n\t\t\t\thooks: mergedHooks\n\t\t\t}\n\t\t\tcontent = JSON.stringify(updatedSettings, null, 2) + '\\n'\n\t\t}\n\n\t\t// Write atomically using temp file + rename\n\t\tconst tempPath = `${this.settingsPath}.tmp`\n\t\tawait fs.writeFile(tempPath, content, 'utf8')\n\t\tawait fs.rename(tempPath, this.settingsPath)\n\n\t\tlogger.debug('Hook configuration merged into settings.json')\n\t}\n\n\t/**\n\t * Get the hook configuration to register\n\t *\n\t * Each event maps to a hook that runs iloom-hook.js\n\t */\n\tprivate getHookConfig(): Record<string, HookEventConfig[]> {\n\t\tconst hookCommand = `node ${path.join(this.hooksDir, 'iloom-hook.js')}`\n\n\t\treturn {\n\t\t\tNotification: [\n\t\t\t\t{ hooks: [{ type: 'command', command: hookCommand }] }\n\t\t\t],\n\t\t\tStop: [\n\t\t\t\t{ hooks: [{ type: 'command', command: hookCommand }] }\n\t\t\t],\n\t\t\tSubagentStop: [\n\t\t\t\t{ hooks: [{ type: 'command', command: hookCommand }] }\n\t\t\t],\n\t\t\tPermissionRequest: [\n\t\t\t\t{ matcher: '*', hooks: [{ type: 'command', command: hookCommand, timeout: 86400 }] }\n\t\t\t],\n\t\t\tPreToolUse: [\n\t\t\t\t{ matcher: '*', hooks: [{ type: 'command', command: hookCommand }] }\n\t\t\t],\n\t\t\tPostToolUse: [\n\t\t\t\t{ matcher: '*', hooks: [{ type: 'command', command: hookCommand }] }\n\t\t\t],\n\t\t\tSessionStart: [\n\t\t\t\t{ matcher: '*', hooks: [{ type: 'command', command: hookCommand }] }\n\t\t\t],\n\t\t\tSessionEnd: [\n\t\t\t\t{ hooks: [{ type: 'command', command: hookCommand }] }\n\t\t\t],\n\t\t\tUserPromptSubmit: [\n\t\t\t\t{ hooks: [{ type: 'command', command: hookCommand }] }\n\t\t\t]\n\t\t}\n\t}\n}\n"],"mappings":"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","names":["path","_a","path"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/init.ts"],"sourcesContent":["import { logger } from '../utils/logger.js'\nimport { ShellCompletion } from '../lib/ShellCompletion.js'\nimport chalk from 'chalk'\nimport { mkdir, readFile } from 'fs/promises'\nimport { existsSync } from 'fs'\nimport path from 'path'\nimport os from 'os'\nimport { detectClaudeCli, launchClaude } from '../utils/claude.js'\nimport { PromptTemplateManager } from '../lib/PromptTemplateManager.js'\nimport { AgentManager } from '../lib/AgentManager.js'\nimport { fileURLToPath } from 'url'\nimport { GitRemote, parseGitRemotes } from '../utils/remote.js'\nimport { SettingsMigrationManager } from '../lib/SettingsMigrationManager.js'\nimport { getRepoRoot, isFileGitignored } from '../utils/git.js'\nimport { FirstRunManager } from '../utils/FirstRunManager.js'\n\n/**\n * Initialize iloom configuration\n * Implements the `il init` command requested in issue #94\n */\nexport class InitCommand {\n private readonly shellCompletion: ShellCompletion\n private readonly templateManager: PromptTemplateManager\n private readonly agentManager: AgentManager\n\n constructor(\n shellCompletion?: ShellCompletion,\n templateManager?: PromptTemplateManager,\n agentManager?: AgentManager\n ) {\n this.shellCompletion = shellCompletion ?? new ShellCompletion()\n this.templateManager = templateManager ?? new PromptTemplateManager()\n this.agentManager = agentManager ?? new AgentManager()\n }\n\n /**\n * Main entry point for the init command\n * @param customInitialMessage Optional custom initial message to send to Claude (defaults to \"Help me configure iloom settings.\")\n */\n public async execute(customInitialMessage?: string): Promise<void> {\n try {\n logger.debug('InitCommand.execute() starting', {\n cwd: process.cwd(),\n nodeVersion: process.version,\n hasCustomInitialMessage: !!customInitialMessage\n })\n\n logger.info(chalk.bold('Welcome to iloom setup'))\n\n // Setup project configuration\n logger.info(chalk.bold('Verifying current setup...'))\n\n await this.setupProjectConfiguration()\n\n // Launch guided Claude configuration if available\n const guidedInitSucceeded = await this.launchGuidedInit(customInitialMessage)\n\n // Only mark project as configured if guided init succeeded and not already marked\n // This enables VSCode extension detection and ensures project appears in `il projects` list\n if (guidedInitSucceeded) {\n const projectRoot = await getRepoRoot() ?? process.cwd()\n const firstRunManager = new FirstRunManager()\n const alreadyConfigured = await firstRunManager.isProjectConfigured(projectRoot)\n if (!alreadyConfigured) {\n await firstRunManager.markProjectAsConfigured(projectRoot)\n logger.debug('Project marked as configured', { projectRoot })\n } else {\n logger.debug('Project already marked as configured, skipping', { projectRoot })\n }\n } else {\n logger.debug('Skipping project marker - guided init did not complete successfully')\n }\n\n logger.info(chalk.green('Setup complete! Enjoy using iloom CLI.'))\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error'\n logger.error(`Initialization failed: ${message}`)\n throw error\n }\n }\n\n /**\n * Setup project configuration files\n * Ensures .iloom directory exists and runs legacy migrations\n */\n private async setupProjectConfiguration(): Promise<void> {\n logger.debug('setupProjectConfiguration() starting')\n\n // Migrate legacy .hatchbox settings to .iloom (BEFORE creating new files)\n try {\n logger.debug('Loading SettingsMigrationManager for legacy migration')\n const migrationManager = new SettingsMigrationManager()\n logger.debug('Running settings migration check')\n await migrationManager.migrateSettingsIfNeeded()\n logger.debug('Settings migration check completed')\n } catch (error) {\n // Log warning but don't fail\n logger.warn(`Settings migration failed: ${error instanceof Error ? error.message : 'Unknown'}`)\n logger.debug('Settings migration error details', { error })\n }\n\n // Ensure .iloom directory exists\n const iloomDir = path.join(process.cwd(), '.iloom')\n logger.debug('Creating .iloom directory', { iloomDir })\n await mkdir(iloomDir, { recursive: true })\n logger.debug('.iloom directory created/verified')\n logger.debug('setupProjectConfiguration() completed')\n }\n\n /**\n * Launch interactive Claude-guided configuration\n * @param customInitialMessage Optional custom initial message to send to Claude\n * @returns true if Claude session completed successfully, false otherwise\n */\n private async launchGuidedInit(customInitialMessage?: string): Promise<boolean> {\n logger.debug('launchGuidedInit() starting', { hasCustomInitialMessage: !!customInitialMessage })\n logger.info(chalk.bold('Starting interactive Claude-guided configuration...'))\n\n // Check if Claude CLI is available\n logger.debug('Checking Claude CLI availability')\n const claudeAvailable = await detectClaudeCli()\n logger.debug('Claude CLI availability check result', { claudeAvailable })\n\n if (!claudeAvailable) {\n logger.warn('Claude Code not detected. Skipping guided configuration.')\n logger.info('iloom won\\'t be able to help you much without Claude Code, so please install it: npm install -g @anthropic-ai/claude-code')\n logger.debug('Exiting launchGuidedInit() due to missing Claude CLI')\n return false\n }\n\n try {\n // Load schema from dist/schema/settings.schema.json\n // Use similar approach to PromptTemplateManager for path resolution\n const __filename = fileURLToPath(import.meta.url)\n const __dirname = path.dirname(__filename)\n\n // Walk up to find the schema directory (in case of chunked files)\n let schemaPath = path.join(__dirname, 'schema', 'settings.schema.json')\n\n logger.debug('Loading settings schema', {\n __filename,\n __dirname,\n schemaPath,\n schemaExists: existsSync(schemaPath)\n })\n\n let schemaContent = ''\n if (existsSync(schemaPath)) {\n logger.debug('Reading schema file')\n schemaContent = await readFile(schemaPath, 'utf-8')\n logger.debug('Schema file loaded', {\n contentLength: schemaContent.length,\n isValidJson: ((): boolean => {\n try {\n JSON.parse(schemaContent)\n return true\n } catch {\n return false\n }\n })()\n })\n } else {\n logger.warn('Schema file not found - Claude will work without schema validation')\n logger.debug('Schema file not found at expected path', { schemaPath })\n }\n\n // Check for existing settings - read ALL three files if they exist (global, project, local)\n const settingsGlobalPath = path.join(os.homedir(), '.config', 'iloom-ai', 'settings.json')\n const settingsLocalPath = path.join(process.cwd(), '.iloom', 'settings.local.json')\n const settingsCommittedPath = path.join(process.cwd(), '.iloom', 'settings.json')\n\n let settingsGlobalJson = ''\n let settingsJson = ''\n let settingsLocalJson = ''\n\n logger.debug('Checking for settings files', {\n settingsGlobalPath,\n settingsLocalPath,\n settingsCommittedPath,\n globalExists: existsSync(settingsGlobalPath),\n localExists: existsSync(settingsLocalPath),\n committedExists: existsSync(settingsCommittedPath)\n })\n\n // Read global settings.json if it exists\n if (existsSync(settingsGlobalPath)) {\n logger.debug('Reading global settings.json')\n const content = await readFile(settingsGlobalPath, 'utf-8')\n const trimmed = content.trim()\n if (trimmed !== '{}' && trimmed !== '') {\n settingsGlobalJson = content\n logger.debug('global settings.json loaded', {\n contentLength: content.length,\n isValidJson: ((): boolean => {\n try {\n JSON.parse(content)\n return true\n } catch {\n return false\n }\n })()\n })\n } else {\n logger.debug('global settings.json is empty, skipping')\n }\n } else {\n logger.debug('global settings.json does not exist')\n }\n\n // Read settings.json if it exists\n if (existsSync(settingsCommittedPath)) {\n logger.debug('Reading settings.json')\n const content = await readFile(settingsCommittedPath, 'utf-8')\n const trimmed = content.trim()\n if (trimmed !== '{}' && trimmed !== '') {\n settingsJson = content\n logger.debug('settings.json loaded', {\n contentLength: content.length,\n isValidJson: ((): boolean => {\n try {\n JSON.parse(content)\n return true\n } catch {\n return false\n }\n })()\n })\n } else {\n logger.debug('settings.json is empty, skipping')\n }\n } else {\n logger.debug('settings.json does not exist')\n }\n\n // Read settings.local.json if it exists\n if (existsSync(settingsLocalPath)) {\n logger.debug('Reading settings.local.json')\n const content = await readFile(settingsLocalPath, 'utf-8')\n const trimmed = content.trim()\n if (trimmed !== '{}' && trimmed !== '') {\n settingsLocalJson = content\n logger.debug('settings.local.json loaded', {\n contentLength: content.length,\n isValidJson: ((): boolean => {\n try {\n JSON.parse(content)\n return true\n } catch {\n return false\n }\n })()\n })\n } else {\n logger.debug('settings.local.json is empty, skipping')\n }\n } else {\n logger.debug('settings.local.json does not exist')\n }\n\n // Log summary\n logger.debug('Settings files summary', {\n hasSettingsGlobalJson: !!settingsGlobalJson,\n hasSettingsJson: !!settingsJson,\n hasSettingsLocalJson: !!settingsLocalJson,\n settingsGlobalJsonLength: settingsGlobalJson.length,\n settingsJsonLength: settingsJson.length,\n settingsLocalJsonLength: settingsLocalJson.length\n })\n\n // Detect shell and read config\n logger.debug('Detecting user shell')\n const shell = this.shellCompletion.detectShell()\n logger.debug('Shell detection result', { shell })\n\n let shellConfigPath = ''\n let shellConfigContent = ''\n\n if (shell !== 'unknown') {\n logger.debug('Grepping shell config for completion setup')\n const shellConfig = await this.shellCompletion.grepCompletionConfig(shell)\n if (shellConfig) {\n shellConfigPath = shellConfig.path\n shellConfigContent = shellConfig.content\n logger.debug('Shell config completion grep completed', {\n path: shellConfigPath,\n contentLength: shellConfigContent.length,\n configExists: existsSync(shellConfigPath),\n hasMatches: shellConfigContent.trim().length > 0\n })\n } else {\n logger.debug('Could not read shell config')\n }\n } else {\n logger.debug('Unknown shell detected, skipping config read')\n }\n\n let remotes: GitRemote[] = []\n try {\n // Detect git remotes for GitHub configuration\n logger.debug('Detecting git remotes for GitHub configuration')\n remotes = await parseGitRemotes()\n logger.debug('Git remotes detected', { count: remotes.length, remotes })\n } catch (error) {\n const message = error instanceof Error ? error.stack : 'Unknown error'\n logger.debug(\"Error occured while getting remote info: \", message)\n }\n\n // Detect if .vscode/settings.json is gitignored\n let vscodeSettingsGitignored = false\n try {\n vscodeSettingsGitignored = await isFileGitignored('.vscode/settings.json')\n logger.debug('VSCode settings gitignore status', { vscodeSettingsGitignored })\n } catch (error) {\n logger.debug('Could not detect gitignore status for .vscode/settings.json', { error })\n }\n\n let remotesInfo = ''\n let multipleRemotes = false\n let singleRemote = false\n let singleRemoteName = ''\n let singleRemoteUrl = ''\n let noRemotes = false\n\n if (remotes.length === 0) {\n noRemotes = true\n remotesInfo = 'No git remotes detected in this repository.'\n } else if (remotes.length === 1 && remotes[0]) {\n singleRemote = true\n singleRemoteName = remotes[0].name\n singleRemoteUrl = remotes[0].url\n remotesInfo = `Detected Remote:\\n- **${remotes[0].name}**: ${remotes[0].url} (${remotes[0].owner}/${remotes[0].repo})`\n } else {\n multipleRemotes = true\n remotesInfo = `Detected Remotes (${remotes.length}):\\n` +\n remotes.map(r => `- **${r.name}**: ${r.url} (${r.owner}/${r.repo})`).join('\\n')\n }\n\n // Load README content for comprehensive documentation\n logger.debug('README content loading...')\n const readmeContent = await this.loadReadmeContent()\n logger.debug('README content loaded', {\n readmeContentLength: readmeContent.length,\n })\n\n // Detect if project has package.json for multi-language support\n const packageJsonPath = path.join(process.cwd(), 'package.json')\n const hasPackageJson = existsSync(packageJsonPath)\n logger.debug('Package.json detection', { packageJsonPath, hasPackageJson })\n\n // Build template variables\n const variables = {\n SETTINGS_SCHEMA: schemaContent,\n SETTINGS_GLOBAL_JSON: settingsGlobalJson,\n SETTINGS_JSON: settingsJson,\n SETTINGS_LOCAL_JSON: settingsLocalJson,\n SHELL_TYPE: shell,\n SHELL_CONFIG_PATH: shellConfigPath,\n SHELL_CONFIG_CONTENT: shellConfigContent,\n REMOTES_INFO: remotesInfo,\n MULTIPLE_REMOTES: multipleRemotes.toString(),\n SINGLE_REMOTE: singleRemote.toString(),\n SINGLE_REMOTE_NAME: singleRemoteName,\n SINGLE_REMOTE_URL: singleRemoteUrl,\n NO_REMOTES: noRemotes.toString(),\n README_CONTENT: readmeContent,\n VSCODE_SETTINGS_GITIGNORED: vscodeSettingsGitignored.toString(),\n // Multi-language support - mutually exclusive booleans\n HAS_PACKAGE_JSON: hasPackageJson,\n NO_PACKAGE_JSON: !hasPackageJson,\n }\n\n logger.debug('Building template variables', {\n variableKeys: Object.keys(variables),\n schemaContentLength: schemaContent.length,\n settingsGlobalJsonLength: settingsGlobalJson.length,\n settingsJsonLength: settingsJson.length,\n settingsLocalJsonLength: settingsLocalJson.length\n })\n\n // Get init prompt\n logger.debug('Loading init prompt template')\n const prompt = await this.templateManager.getPrompt('init', variables)\n\n logger.debug('Init prompt loaded', {\n promptLength: prompt.length,\n containsSchema: prompt.includes('SETTINGS_SCHEMA'),\n containsExistingSettings: prompt.includes('EXISTING_SETTINGS')\n })\n\n // Load framework-detector agent for non-Node.js project setup\n let agents: Record<string, unknown> | undefined\n try {\n const loadedAgents = await this.agentManager.loadAgents(\n undefined, // No settings overrides for init\n variables,\n ['iloom-framework-detector.md']\n )\n agents = this.agentManager.formatForCli(loadedAgents)\n logger.debug('Loaded framework-detector agent for init', {\n agentCount: Object.keys(agents).length,\n agentNames: Object.keys(agents),\n })\n } catch (error) {\n // Log warning but continue without agents\n logger.warn(`Failed to load agents: ${error instanceof Error ? error.message : 'Unknown error'}`)\n }\n\n // Pre-approved tools for init workflow to reduce permission prompts\n const initAllowedTools = [\n 'Bash(git rev-parse:*)',\n 'Bash(git init:*)',\n 'Bash(git status:*)',\n 'Bash(git add:*)',\n 'Bash(git commit:*)',\n 'Read',\n 'Write',\n 'Edit',\n ]\n\n const claudeOptions = {\n model: 'opus',\n headless: false,\n appendSystemPrompt: prompt,\n addDir: process.cwd(),\n allowedTools: initAllowedTools,\n ...(agents && { agents }),\n }\n\n logger.debug('Launching Claude with options', {\n optionKeys: Object.keys(claudeOptions),\n headless: claudeOptions.headless,\n hasSystemPrompt: !!claudeOptions.appendSystemPrompt,\n addDir: claudeOptions.addDir,\n promptLength: prompt.length,\n hasCustomInitialMessage: !!customInitialMessage,\n hasAgents: !!agents,\n })\n\n // Launch Claude in interactive mode with custom initial message if provided\n const initialMessage = customInitialMessage ?? 'Help me configure iloom settings.'\n await launchClaude(initialMessage, claudeOptions)\n logger.debug('Claude session completed successfully')\n return true\n\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error'\n logger.warn(`Guided configuration failed: ${message}`)\n logger.debug('launchGuidedInit() error details', error instanceof Error ? error.stack : {error})\n logger.info('You can manually edit .iloom/settings.json to configure iloom.')\n return false\n }\n }\n\n /**\n * Load README.md content for init prompt\n * Walks up from dist directory to find README.md in project root\n */\n private async loadReadmeContent(): Promise<string> {\n try {\n // Walk up from current file location to find README.md\n // Use same pattern as PromptTemplateManager for finding files\n let currentDir = path.dirname(fileURLToPath(import.meta.url))\n\n // Walk up to find README.md\n while (currentDir !== path.dirname(currentDir)) {\n const readmePath = path.join(currentDir, 'README.md')\n try {\n const content = await readFile(readmePath, 'utf-8')\n logger.debug('Loaded README.md for init prompt', { readmePath })\n return content\n } catch {\n currentDir = path.dirname(currentDir)\n }\n }\n\n logger.debug('README.md not found, returning empty string')\n return ''\n } catch (error) {\n // Graceful degradation - return empty string on error\n logger.debug(`Failed to load README.md: ${error}`)\n return ''\n }\n }\n\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/issues.ts"],"sourcesContent":["import os from 'os'\nimport path from 'path'\nimport fs from 'fs-extra'\nimport crypto from 'crypto'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { IssueTrackerFactory } from '../lib/IssueTrackerFactory.js'\nimport { findMainWorktreePathWithSettings } from '../utils/git.js'\nimport { fetchGitHubIssueList, fetchGitHubPRList } from '../utils/github.js'\nimport { fetchLinearIssueList } from '../utils/linear.js'\nimport { getLogger } from '../utils/logger-context.js'\n\n/**\n * Unified output interface for issues from any provider\n */\nexport interface IssueListItem {\n id: string\n title: string\n updatedAt: string\n url: string\n state: string\n type?: 'issue' | 'pr'\n}\n\n/**\n * File-based cache structure (follows UpdateNotifier pattern)\n */\ninterface IssuesCacheFile {\n timestamp: number // Date.now() when cached\n projectPath: string // for verification\n provider: string // 'github' | 'linear'\n data: IssueListItem[]\n}\n\n// Cache configuration\nconst CACHE_TTL_MS = 2 * 60 * 1000 // 2 minutes\nconst CACHE_DIR = path.join(os.homedir(), '.config', 'iloom-ai', 'cache')\n\n/**\n * Generate a deterministic cache file path from project path + provider\n */\nfunction getCacheFilePath(projectPath: string, provider: string, limit: number): string {\n const hash = crypto.createHash('md5').update(`${projectPath}:${provider}:${limit}`).digest('hex').slice(0, 12)\n return path.join(CACHE_DIR, `issues-${hash}.json`)\n}\n\n/**\n * Read cache file, return null if missing/expired/corrupted\n * Follows UpdateNotifier.getCachedCheck pattern\n */\nasync function readCacheFile(cacheFilePath: string): Promise<IssueListItem[] | null> {\n try {\n if (!fs.existsSync(cacheFilePath)) return null\n const content = await fs.readFile(cacheFilePath, 'utf8')\n const cache = JSON.parse(content) as IssuesCacheFile\n if (Date.now() - cache.timestamp < CACHE_TTL_MS) return cache.data\n return null // expired\n } catch {\n return null // corrupted or unreadable, treat as cache miss\n }\n}\n\n/**\n * Write cache file following UpdateNotifier.saveCacheFile pattern\n */\nasync function writeCacheFile(\n cacheFilePath: string,\n data: IssueListItem[],\n projectPath: string,\n provider: string,\n): Promise<void> {\n try {\n await fs.ensureDir(CACHE_DIR)\n const cache: IssuesCacheFile = { timestamp: Date.now(), projectPath, provider, data }\n await fs.writeFile(cacheFilePath, JSON.stringify(cache, null, 2), 'utf8')\n } catch {\n // Cache write failure is non-fatal, just log debug\n getLogger().debug(`Failed to write issues cache to ${cacheFilePath}`)\n }\n}\n\nexport interface IssuesCommandOptions {\n projectPath?: string | undefined\n limit?: number | undefined\n}\n\n/**\n * IssuesCommand: List open issues from the configured issue tracker\n *\n * Returns JSON array of issues. Uses file-based caching with ~2 minute TTL.\n * Follows the ProjectsCommand pattern for structure.\n */\nexport class IssuesCommand {\n private readonly settingsManager: SettingsManager\n\n constructor(settingsManager?: SettingsManager) {\n this.settingsManager = settingsManager ?? new SettingsManager()\n }\n\n /**\n * Execute the issues command\n * @param options - Command options\n * @returns Array of issue list items\n */\n async execute(options?: IssuesCommandOptions): Promise<IssueListItem[]> {\n const logger = getLogger()\n const limit = options?.limit ?? 100\n\n // 1. Resolve project root\n let resolvedProjectPath: string\n if (options?.projectPath) {\n resolvedProjectPath = options.projectPath\n } else {\n try {\n resolvedProjectPath = await findMainWorktreePathWithSettings()\n } catch {\n logger.debug('Failed to resolve worktree path, falling back to cwd')\n resolvedProjectPath = process.cwd()\n }\n }\n\n // 2. Load settings from resolved root\n const settings = await this.settingsManager.loadSettings(resolvedProjectPath)\n\n // 3. Determine provider\n const provider = IssueTrackerFactory.getProviderName(settings)\n\n // 4. Check file-based cache\n const cacheFilePath = getCacheFilePath(resolvedProjectPath, provider, limit)\n const cached = await readCacheFile(cacheFilePath)\n if (cached !== null) {\n logger.debug(`Returning cached issues (${cached.length} items)`)\n // Backfill type field for cache entries from before PR support was added\n return cached.map(item => ({ type: 'issue' as const, ...item }))\n }\n\n // 5. Fetch issues based on provider\n let results: IssueListItem[]\n\n if (provider === 'github') {\n results = await fetchGitHubIssueList({\n limit,\n cwd: resolvedProjectPath,\n })\n } else if (provider === 'linear') {\n const teamId = settings.issueManagement?.linear?.teamId\n if (!teamId) {\n throw new Error(\n 'Linear team ID not configured. Set issueManagement.linear.teamId in your settings.json.',\n )\n }\n const apiToken = settings.issueManagement?.linear?.apiToken ?? process.env.LINEAR_API_TOKEN\n results = await fetchLinearIssueList(teamId, {\n limit,\n ...(apiToken ? { apiToken } : {}),\n })\n } else {\n throw new Error(`Unsupported issue tracker provider: ${provider}`)\n }\n\n // Tag issues with type\n results.forEach(item => { item.type = 'issue' })\n\n // 6. Fetch PRs from GitHub (PRs are a GitHub concept regardless of issue tracker)\n try {\n const prs = await fetchGitHubPRList({\n limit,\n cwd: resolvedProjectPath,\n })\n const prItems: IssueListItem[] = prs.map(pr => ({ ...pr, type: 'pr' as const }))\n results = [...results, ...prItems]\n } catch (error) {\n // Only catch expected, non-fatal errors from gh CLI\n // Per CLAUDE.md: \"DO NOT SWALLOW ERRORS\" -- must check specifically\n const stderr = (error as NodeJS.ErrnoException & { stderr?: string }).stderr ?? ''\n const isExpectedError = error instanceof Error && (\n error.message.includes('not logged in') ||\n error.message.includes('auth login') ||\n error.message.includes('rate limit') ||\n error.message.includes('ETIMEDOUT') ||\n error.message.includes('ECONNREFUSED') ||\n stderr.includes('not logged in') ||\n stderr.includes('rate limit')\n )\n if (isExpectedError) {\n logger.warn(`PR fetch failed (non-fatal), continuing with issues only: ${error.message}`)\n } else {\n throw error // Re-throw unexpected errors -- do not swallow\n }\n }\n\n // 7. Sort by updatedAt descending and apply limit\n results.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime())\n results = results.slice(0, limit)\n\n // 8. Write results to cache file\n await writeCacheFile(cacheFilePath, results, resolvedProjectPath, provider)\n\n return results\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/open.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 { 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 { logger } from '../utils/logger.js'\nimport { extractSettingsOverrides } from '../utils/cli-overrides.js'\nimport type { GitWorktree } from '../types/worktree.js'\n\nexport interface OpenCommandInput {\n\tidentifier?: string\n\targs?: string[]\n}\n\ninterface ParsedOpenInput {\n\ttype: 'issue' | 'pr' | 'branch'\n\tnumber?: string | number // For issues and PRs\n\tbranchName?: string // For branches\n\toriginalInput: string\n\tautoDetected: boolean\n}\n\n/**\n * OpenCommand - Opens workspace in browser or runs CLI tool\n * Priority: Web first, CLI fallback\n */\nexport class OpenCommand {\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: OpenCommandInput): 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 (web first, CLI fallback)\n\t\tif (capabilities.includes('web')) {\n\t\t\tawait this.openWebBrowser(worktree)\n\t\t} else if (capabilities.includes('cli')) {\n\t\t\tawait this.runCLITool(worktree.path, binEntries, input.args ?? [])\n\t\t} else {\n\t\t\tthrow new Error(\n\t\t\t\t`No web or CLI 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<ParsedOpenInput> {\n\t\tconst parsed = await this.identifierParser.parseForPatternDetection(identifier)\n\n\t\t// Description type should never reach open command (converted in start)\n\t\tif (parsed.type === 'description') {\n\t\t\tthrow new Error('Description input type is not supported in open command')\n\t\t}\n\n\t\tconst result: ParsedOpenInput = {\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<ParsedOpenInput> {\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: ParsedOpenInput): 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: ParsedOpenInput): 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 * 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 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})\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)\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 url = `http://localhost:${port}`\n\t\tlogger.info(`Opening browser: ${url}`)\n\t\tawait openBrowser(url)\n\t\tlogger.success('Browser opened')\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"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/plan.ts"],"sourcesContent":["import { logger, createStderrLogger } from '../utils/logger.js'\nimport { withLogger } from '../utils/logger-context.js'\nimport chalk from 'chalk'\nimport { detectClaudeCli, launchClaude } from '../utils/claude.js'\nimport { PromptTemplateManager, type TemplateVariables } from '../lib/PromptTemplateManager.js'\nimport { generateIssueManagementMcpConfig } from '../utils/mcp.js'\nimport { SettingsManager, PlanCommandSettingsSchema } from '../lib/SettingsManager.js'\nimport { IssueTrackerFactory } from '../lib/IssueTrackerFactory.js'\nimport { matchIssueIdentifier } from '../utils/IdentifierParser.js'\nimport { IssueManagementProviderFactory } from '../mcp/IssueManagementProviderFactory.js'\nimport { needsFirstRunSetup, launchFirstRunSetup } from '../utils/first-run-setup.js'\nimport type { IssueProvider, ChildIssueResult, DependenciesResult } from '../mcp/types.js'\nimport { promptConfirmation, isInteractiveEnvironment } from '../utils/prompt.js'\n\n// Define provider arrays for validation and dynamic flag generation\nconst PLANNER_PROVIDERS = ['claude', 'gemini', 'codex'] as const\nconst REVIEWER_PROVIDERS = ['claude', 'gemini', 'codex', 'none'] as const\n\ntype PlannerProvider = (typeof PLANNER_PROVIDERS)[number]\ntype ReviewerProvider = (typeof REVIEWER_PROVIDERS)[number]\n\n/**\n * Format child issues as a markdown list for inclusion in the prompt\n */\nfunction formatChildIssues(children: ChildIssueResult[], issuePrefix: string): string {\n\tif (children.length === 0) return 'None'\n\treturn children\n\t\t.map(child => `- ${issuePrefix}${child.id}: ${child.title} (${child.state})`)\n\t\t.join('\\n')\n}\n\n/**\n * Format dependencies as a markdown list for inclusion in the prompt\n */\nfunction formatDependencies(dependencies: DependenciesResult, issuePrefix: string): string {\n\tconst lines: string[] = []\n\n\tif (dependencies.blockedBy.length > 0) {\n\t\tlines.push('**Blocked by:**')\n\t\tfor (const dep of dependencies.blockedBy) {\n\t\t\tlines.push(`- ${issuePrefix}${dep.id}: ${dep.title} (${dep.state})`)\n\t\t}\n\t}\n\n\tif (dependencies.blocking.length > 0) {\n\t\tif (lines.length > 0) lines.push('')\n\t\tlines.push('**Blocking:**')\n\t\tfor (const dep of dependencies.blocking) {\n\t\t\tlines.push(`- ${issuePrefix}${dep.id}: ${dep.title} (${dep.state})`)\n\t\t}\n\t}\n\n\treturn lines.length > 0 ? lines.join('\\n') : 'None'\n}\n\n/**\n * Launch interactive planning session with Architect persona\n * Implements the `il plan` command requested in issue #471\n *\n * The Architect persona helps users:\n * - Break epics down into child issues following \"1 issue = 1 loom = 1 PR\" pattern\n * - Think through implementation approaches\n * - Create issues at the end of the planning session using MCP tools\n */\nexport class PlanCommand {\n\tprivate readonly templateManager: PromptTemplateManager\n\n\tconstructor(templateManager?: PromptTemplateManager) {\n\t\tthis.templateManager = templateManager ?? new PromptTemplateManager()\n\t}\n\n\t/**\n\t * Main entry point for the plan command\n\t * @param prompt - Optional initial planning prompt or topic\n\t * @param model - Optional model to use (defaults to 'opus')\n\t * @param yolo - Optional flag to enable autonomous mode (skip permission prompts)\n\t * @param planner - Optional planner provider (defaults to 'claude')\n\t * @param reviewer - Optional reviewer provider (defaults to 'none')\n\t * @param printOptions - Print mode options for headless/CI execution\n\t */\n\tpublic async execute(\n\t\tprompt?: string,\n\t\tmodel?: string,\n\t\tyolo?: boolean,\n\t\tplanner?: string,\n\t\treviewer?: string,\n\t\tprintOptions?: {\n\t\t\tprint?: boolean\n\t\t\toutputFormat?: 'json' | 'stream-json' | 'text'\n\t\t\tverbose?: boolean\n\t\t\tjson?: boolean\n\t\t\tjsonStream?: boolean\n\t\t}\n\t): Promise<void> {\n\t\t// Wrap execution in stderr logger for JSON modes to keep stdout clean\n\t\tconst isJsonMode = (printOptions?.json ?? false) || (printOptions?.jsonStream ?? false)\n\t\tif (isJsonMode) {\n\t\t\tconst jsonLogger = createStderrLogger()\n\t\t\treturn withLogger(jsonLogger, () => this.executeInternal(prompt, model, yolo, planner, reviewer, printOptions))\n\t\t}\n\n\t\treturn this.executeInternal(prompt, model, yolo, planner, reviewer, printOptions)\n\t}\n\n\t/**\n\t * Internal execution method (separated for withLogger wrapping)\n\t */\n\tprivate async executeInternal(\n\t\tprompt?: string,\n\t\tmodel?: string,\n\t\tyolo?: boolean,\n\t\tplanner?: string,\n\t\treviewer?: string,\n\t\tprintOptions?: {\n\t\t\tprint?: boolean\n\t\t\toutputFormat?: 'json' | 'stream-json' | 'text'\n\t\t\tverbose?: boolean\n\t\t\tjson?: boolean\n\t\t\tjsonStream?: boolean\n\t\t}\n\t): Promise<void> {\n\t\t// Validate and normalize planner CLI argument\n\t\tlet normalizedPlanner: PlannerProvider | undefined\n\t\tif (planner) {\n\t\t\tconst normalized = planner.toLowerCase()\n\t\t\tconst result = PlanCommandSettingsSchema.shape.planner.safeParse(normalized)\n\t\t\tif (!result.success) {\n\t\t\t\tthrow new Error(`Invalid planner: \"${planner}\". Allowed values: ${PLANNER_PROVIDERS.join(', ')}`)\n\t\t\t}\n\t\t\tnormalizedPlanner = normalized as PlannerProvider\n\t\t}\n\n\t\t// Validate and normalize reviewer CLI argument\n\t\tlet normalizedReviewer: ReviewerProvider | undefined\n\t\tif (reviewer) {\n\t\t\tconst normalized = reviewer.toLowerCase()\n\t\t\tconst result = PlanCommandSettingsSchema.shape.reviewer.safeParse(normalized)\n\t\t\tif (!result.success) {\n\t\t\t\tthrow new Error(`Invalid reviewer: \"${reviewer}\". Allowed values: ${REVIEWER_PROVIDERS.join(', ')}`)\n\t\t\t}\n\t\t\tnormalizedReviewer = normalized as ReviewerProvider\n\t\t}\n\n\t\tlogger.debug('PlanCommand.execute() starting', {\n\t\t\tcwd: process.cwd(),\n\t\t\thasPrompt: !!prompt,\n\t\t\tyolo,\n\t\t\tplanner: normalizedPlanner ?? planner,\n\t\t\treviewer: normalizedReviewer ?? reviewer,\n\t\t})\n\n\t\t// Check for first-run setup (same check as StartCommand)\n\t\tif (process.env.FORCE_FIRST_TIME_SETUP === \"true\" || await needsFirstRunSetup()) {\n\t\t\tawait launchFirstRunSetup()\n\t\t}\n\n\t\tlogger.info(chalk.bold('Starting interactive planning session...'))\n\n\t\t// Check if Claude CLI is available\n\t\tlogger.debug('Checking Claude CLI availability')\n\t\tconst claudeAvailable = await detectClaudeCli()\n\t\tlogger.debug('Claude CLI availability check result', { claudeAvailable })\n\n\t\tif (!claudeAvailable) {\n\t\t\tlogger.error(\n\t\t\t\t\"Claude Code not detected. Please install it: npm install -g @anthropic-ai/claude-code\"\n\t\t\t)\n\t\t\tthrow new Error('Claude Code CLI is required for planning sessions')\n\t\t}\n\n\t\t// Load settings to detect configured issue provider and model\n\t\tconst settingsManager = new SettingsManager()\n\t\tconst settings = await settingsManager.loadSettings()\n\n\t\t// Detect if prompt is an issue number for decomposition mode\n\t\t// Uses shared matchIssueIdentifier() utility to identify issue identifiers:\n\t\t// - Numeric pattern: #123 or 123 (GitHub format)\n\t\t// - Linear pattern: ENG-123 (requires at least 2 letters before dash)\n\t\tconst identifierMatch = prompt ? matchIssueIdentifier(prompt) : { isIssueIdentifier: false }\n\t\tconst looksLikeIssueIdentifier = identifierMatch.isIssueIdentifier\n\t\tlet decompositionContext: {\n\t\t\tidentifier: string\n\t\t\ttitle: string\n\t\t\tbody: string\n\t\t\tchildren?: ChildIssueResult[]\n\t\t\tdependencies?: DependenciesResult\n\t\t} | null = null\n\n\t\tconst provider = settings ? IssueTrackerFactory.getProviderName(settings) : 'github'\n\t\tconst issuePrefix = provider === 'github' ? '#' : ''\n\n\t\tif (prompt && looksLikeIssueIdentifier) {\n\t\t\t// Validate and fetch issue using issueTracker.detectInputType() pattern from StartCommand\n\t\t\tconst issueTracker = IssueTrackerFactory.create(settings)\n\n\t\t\tlogger.debug('Detected potential issue identifier, validating via issueTracker', { identifier: prompt })\n\n\t\t\t// Use detectInputType to validate the identifier exists (same pattern as StartCommand)\n\t\t\tconst detection = await issueTracker.detectInputType(prompt)\n\n\t\t\tif (detection.type === 'issue' && detection.identifier) {\n\t\t\t\t// Valid issue found - fetch full details for decomposition context\n\t\t\t\tconst issue = await issueTracker.fetchIssue(detection.identifier)\n\t\t\t\tdecompositionContext = {\n\t\t\t\t\tidentifier: String(issue.number),\n\t\t\t\t\ttitle: issue.title,\n\t\t\t\t\tbody: issue.body\n\t\t\t\t}\n\t\t\t\tlogger.info(chalk.dim(`Preparing to create a detailed plan for issue #${decompositionContext.identifier}: ${decompositionContext.title}`))\n\n\t\t\t\t// Fetch existing children and dependencies using MCP provider\n\t\t\t\t// This allows users to resume planning where they left off\n\t\t\t\ttry {\n\t\t\t\t\tconst mcpProvider = IssueManagementProviderFactory.create(provider as IssueProvider)\n\n\t\t\t\t\t// Fetch child issues\n\t\t\t\t\tlogger.debug('Fetching child issues for decomposition context', { identifier: decompositionContext.identifier })\n\t\t\t\t\tconst children = await mcpProvider.getChildIssues({ number: decompositionContext.identifier })\n\t\t\t\t\tif (children.length > 0) {\n\t\t\t\t\t\tdecompositionContext.children = children\n\t\t\t\t\t\tlogger.debug('Found existing child issues', { count: children.length })\n\t\t\t\t\t}\n\n\t\t\t\t\t// Fetch dependencies (both directions)\n\t\t\t\t\tlogger.debug('Fetching dependencies for decomposition context', { identifier: decompositionContext.identifier })\n\t\t\t\t\tconst dependencies = await mcpProvider.getDependencies({\n\t\t\t\t\t\tnumber: decompositionContext.identifier,\n\t\t\t\t\t\tdirection: 'both'\n\t\t\t\t\t})\n\t\t\t\t\tif (dependencies.blocking.length > 0 || dependencies.blockedBy.length > 0) {\n\t\t\t\t\t\tdecompositionContext.dependencies = dependencies\n\t\t\t\t\t\tlogger.debug('Found existing dependencies', {\n\t\t\t\t\t\t\tblocking: dependencies.blocking.length,\n\t\t\t\t\t\t\tblockedBy: dependencies.blockedBy.length\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// Log but don't fail - children/dependencies are optional context\n\t\t\t\t\tlogger.debug('Failed to fetch children/dependencies, continuing without them', {\n\t\t\t\t\t\terror: error instanceof Error ? error.message : 'Unknown error'\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Input matched issue pattern but issue not found - treat as regular prompt\n\t\t\t\tlogger.debug('Input matched issue pattern but issue not found, treating as planning topic', {\n\t\t\t\t\tidentifier: prompt,\n\t\t\t\t\tdetectionType: detection.type\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\t// Use CLI model if provided, otherwise use settings (plan.model), defaults to opus\n\t\tconst effectiveModel = model ?? settingsManager.getPlanModel(settings ?? undefined)\n\n\t\t// Get effective planner/reviewer (CLI > settings > default)\n\t\tconst effectivePlanner = normalizedPlanner ?? settingsManager.getPlanPlanner(settings ?? undefined)\n\t\tconst effectiveReviewer = normalizedReviewer ?? settingsManager.getPlanReviewer(settings ?? undefined)\n\n\t\tlogger.debug('Detected issue provider, model, planner, and reviewer', {\n\t\t\tprovider,\n\t\t\teffectiveModel,\n\t\t\teffectivePlanner,\n\t\t\teffectiveReviewer,\n\t\t})\n\n\t\t// Generate MCP config for issue management tools\n\t\t// This will throw if no git remote is configured - offer to run 'il init' as fallback\n\t\tlogger.debug('Generating MCP config for issue management')\n\t\tlet mcpConfig: Record<string, unknown>[]\n\t\ttry {\n\t\t\tmcpConfig = await generateIssueManagementMcpConfig(undefined, undefined, provider, settings ?? undefined)\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : 'Unknown error'\n\n\t\t\t// Check if running in interactive mode - offer to run init\n\t\t\tif (isInteractiveEnvironment()) {\n\t\t\t\tconst shouldRunInit = await promptConfirmation(\n\t\t\t\t\t\"No git repository or remote found. Would you like to run 'il init' to set up?\",\n\t\t\t\t\ttrue\n\t\t\t\t)\n\t\t\t\tif (shouldRunInit) {\n\t\t\t\t\t// Dynamically import and run InitCommand\n\t\t\t\t\tlogger.info(chalk.bold('Launching iloom init...'))\n\t\t\t\t\tconst { InitCommand } = await import('./init.js')\n\t\t\t\t\tconst initCommand = new InitCommand()\n\t\t\t\t\tawait initCommand.execute(\n\t\t\t\t\t\t'Help the user set up a GitHub repository or Linear project for this project so they can use issue management features. When complete tell the user they can exit to continue the planning session.'\n\t\t\t\t\t)\n\n\t\t\t\t\t// Retry MCP config generation after init\n\t\t\t\t\tlogger.info(chalk.bold('Retrying planning session setup...'))\n\t\t\t\t\ttry {\n\t\t\t\t\t\tmcpConfig = await generateIssueManagementMcpConfig(undefined, undefined, provider, settings ?? undefined)\n\t\t\t\t\t} catch (retryError) {\n\t\t\t\t\t\tconst retryMessage = retryError instanceof Error ? retryError.message : 'Unknown error'\n\t\t\t\t\t\tlogger.error(`Failed to generate MCP config: ${retryMessage}`)\n\t\t\t\t\t\tif (provider === 'github') {\n\t\t\t\t\t\t\tlogger.error(\n\t\t\t\t\t\t\t\t'GitHub issue management requires a git repository with a GitHub remote configured.'\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t\t`Cannot start planning session after init: ${retryMessage}. Ensure you are in a git repository with a GitHub remote configured.`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tlogger.error(\n\t\t\t\t\t\t\t\t'Linear issue management requires LINEAR_API_TOKEN to be configured.'\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t\t`Cannot start planning session after init: ${retryMessage}. Ensure LINEAR_API_TOKEN is configured in settings or environment.`\n\t\t\t\t\t\t\t)\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// User declined init prompt - show provider-specific error messages\n\t\t\t\t\tlogger.error(`Failed to generate MCP config: ${message}`)\n\t\t\t\t\tif (provider === 'github') {\n\t\t\t\t\t\tlogger.error(\n\t\t\t\t\t\t\t'GitHub issue management requires a git repository with a GitHub remote configured.'\n\t\t\t\t\t\t)\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`Cannot start planning session: ${message}. Ensure you are in a git repository with a GitHub remote configured.`\n\t\t\t\t\t\t)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlogger.error(\n\t\t\t\t\t\t\t'Linear issue management requires LINEAR_API_TOKEN to be configured.'\n\t\t\t\t\t\t)\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`Cannot start planning session: ${message}. Ensure LINEAR_API_TOKEN is configured in settings or environment.`\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Non-interactive mode - show provider-specific error messages\n\t\t\t\tlogger.error(`Failed to generate MCP config: ${message}`)\n\t\t\t\tif (provider === 'github') {\n\t\t\t\t\tlogger.error(\n\t\t\t\t\t\t'GitHub issue management requires a git repository with a GitHub remote configured.'\n\t\t\t\t\t)\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Cannot start planning session: ${message}. Ensure you are in a git repository with a GitHub remote configured.`\n\t\t\t\t\t)\n\t\t\t\t} else {\n\t\t\t\t\tlogger.error(\n\t\t\t\t\t\t'Linear issue management requires LINEAR_API_TOKEN to be configured.'\n\t\t\t\t\t)\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Cannot start planning session: ${message}. Ensure LINEAR_API_TOKEN is configured in settings or environment.`\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlogger.debug('MCP config generated', {\n\t\t\tserverCount: mcpConfig.length,\n\t\t})\n\n\t\t// Detect VS Code mode\n\t\tconst isVscodeMode = process.env.ILOOM_VSCODE === '1'\n\t\tlogger.debug('VS Code mode detection', { isVscodeMode })\n\n\t\t// Compute template variables for multi-AI provider support\n\t\t// Generate USE_*_PLANNER and USE_*_REVIEWER flags dynamically\n\t\tconst providerFlags = PLANNER_PROVIDERS.reduce((acc, p) => ({\n\t\t\t...acc,\n\t\t\t[`USE_${p.toUpperCase()}_PLANNER`]: effectivePlanner === p,\n\t\t}), {} as Record<string, boolean>)\n\n\t\t// Add reviewer flags (excluding 'none')\n\t\t;(['claude', 'gemini', 'codex'] as const).forEach(p => {\n\t\t\tproviderFlags[`USE_${p.toUpperCase()}_REVIEWER`] = effectiveReviewer === p\n\t\t})\n\n\t\t// Load plan prompt template with mode-specific variables\n\t\tlogger.debug('Loading plan prompt template')\n\t\tconst templateVariables: TemplateVariables = {\n\t\t\tIS_VSCODE_MODE: isVscodeMode,\n\t\t\tEXISTING_ISSUE_MODE: !!decompositionContext,\n\t\t\tFRESH_PLANNING_MODE: !decompositionContext,\n\t\t\tPARENT_ISSUE_NUMBER: decompositionContext?.identifier,\n\t\t\tPARENT_ISSUE_TITLE: decompositionContext?.title,\n\t\t\tPARENT_ISSUE_BODY: decompositionContext?.body,\n\t\t\tPARENT_ISSUE_CHILDREN: decompositionContext?.children\n\t\t\t\t? formatChildIssues(decompositionContext.children, issuePrefix)\n\t\t\t\t: undefined,\n\t\t\tPARENT_ISSUE_DEPENDENCIES: decompositionContext?.dependencies\n\t\t\t\t? formatDependencies(decompositionContext.dependencies, issuePrefix)\n\t\t\t\t: undefined,\n\t\t\tPLANNER: effectivePlanner,\n\t\t\tREVIEWER: effectiveReviewer,\n\t\t\tHAS_REVIEWER: effectiveReviewer !== 'none',\n\t\t\t...providerFlags,\n\t\t}\n\t\tconst architectPrompt = await this.templateManager.getPrompt('plan', templateVariables)\n\t\tlogger.debug('Plan prompt loaded', {\n\t\t\tpromptLength: architectPrompt.length,\n\t\t\tmode: decompositionContext ? 'decomposition' : 'fresh',\n\t\t})\n\n\t\t// Define allowed tools for the Architect persona\n\t\tconst allowedTools = [\n\t\t\t// Issue management tools\n\t\t\t'mcp__issue_management__create_issue',\n\t\t\t'mcp__issue_management__create_child_issue',\n\t\t\t'mcp__issue_management__get_issue',\n\t\t\t'mcp__issue_management__get_child_issues',\n\t\t\t'mcp__issue_management__get_comment',\n\t\t\t'mcp__issue_management__create_comment',\n\t\t\t// Dependency management tools\n\t\t\t'mcp__issue_management__create_dependency',\n\t\t\t'mcp__issue_management__get_dependencies',\n\t\t\t'mcp__issue_management__remove_dependency',\n\t\t\t// Codebase exploration tools (read-only)\n\t\t\t'Read',\n\t\t\t'Glob',\n\t\t\t'Grep',\n\t\t\t'Task',\n\t\t\t// Web research tools\n\t\t\t'WebFetch',\n\t\t\t'WebSearch',\n\t\t\t// Git commands for understanding repo state\n\t\t\t'Bash(git status:*)',\n\t\t\t'Bash(git log:*)',\n\t\t\t'Bash(git branch:*)',\n\t\t\t'Bash(git remote:*)',\n\t\t\t'Bash(git diff:*)',\n\t\t\t'Bash(git show:*)',\n\t\t]\n\n\t\t// Determine if we're in print/headless mode\n\t\tconst isHeadless = printOptions?.print ?? false\n\n\t\t// Build Claude options\n\t\tconst claudeOptions: Parameters<typeof launchClaude>[1] = {\n\t\t\tmodel: effectiveModel,\n\t\t\theadless: isHeadless,\n\t\t\tappendSystemPrompt: architectPrompt,\n\t\t\tmcpConfig,\n\t\t\taddDir: process.cwd(),\n\t\t\tallowedTools,\n\t\t}\n\n\t\t// Add output format and verbose options if provided (print mode only)\n\t\tif (printOptions?.outputFormat !== undefined) {\n\t\t\tclaudeOptions.outputFormat = printOptions.outputFormat\n\t\t}\n\t\tif (printOptions?.verbose !== undefined) {\n\t\t\tclaudeOptions.verbose = printOptions.verbose\n\t\t}\n\n\t\t// Add JSON mode if specified (requires print mode)\n\t\tif (printOptions?.json) {\n\t\t\tclaudeOptions.jsonMode = 'json'\n\t\t\tclaudeOptions.outputFormat = 'stream-json' // Force stream-json for parsing\n\t\t} else if (printOptions?.jsonStream) {\n\t\t\tclaudeOptions.jsonMode = 'stream'\n\t\t\tclaudeOptions.outputFormat = 'stream-json' // Force stream-json for streaming\n\t\t}\n\n\t\t// Force yolo mode when print mode is enabled (headless execution requires autonomous mode)\n\t\tconst effectiveYolo = (yolo ?? false) || isHeadless\n\n\t\t// Handle --yolo mode\n\t\tif (effectiveYolo) {\n\t\t\t// Only require prompt for explicit --yolo flag, not for print mode auto-yolo\n\t\t\tif (yolo && !prompt) {\n\t\t\t\tthrow new Error('--yolo requires a prompt or issue identifier (e.g., il plan --yolo \"add gitlab support\" or il plan --yolo 42)')\n\t\t\t}\n\t\t\tlogger.warn(\n\t\t\t\t'⚠️ YOLO mode enabled - Claude will skip permission prompts and proceed autonomously. This could destroy important data or make irreversible changes. Proceeding means you accept this risk.'\n\t\t\t)\n\t\t}\n\n\t\tlogger.debug('Launching Claude with options', {\n\t\t\toptionKeys: Object.keys(claudeOptions),\n\t\t\theadless: claudeOptions.headless,\n\t\t\thasSystemPrompt: !!claudeOptions.appendSystemPrompt,\n\t\t\taddDir: claudeOptions.addDir,\n\t\t\tyolo,\n\t\t\tprint: isHeadless,\n\t\t})\n\n\t\t// Launch Claude in interactive mode\n\t\t// Construct initial message based on mode\n\t\tlet initialMessage: string\n\t\tif (decompositionContext) {\n\t\t\t// Issue decomposition mode - provide context about what to decompose\n\t\t\tinitialMessage = `Break down issue #${decompositionContext.identifier} into child issues.`\n\t\t} else if (prompt) {\n\t\t\t// Fresh planning with user-provided topic\n\t\t\tinitialMessage = prompt\n\t\t} else {\n\t\t\t// Interactive mode - no topic provided\n\t\t\tinitialMessage = 'Help me plan a feature or decompose work into issues.'\n\t\t}\n\n\t\t// Apply yolo mode wrapper if enabled (includes print mode)\n\t\tif (effectiveYolo) {\n\t\t\tinitialMessage = `[AUTONOMOUS MODE]\nProceed through the flow without requiring user interaction. Make and document your assumptions and proceed to create the epic and child issues and dependencies if necessary. This guidance supersedes all previous guidance.\n\n[TOPIC]\n${initialMessage}`\n\t\t}\n\n\t\tconst claudeResult = await launchClaude(initialMessage, {\n\t\t\t...claudeOptions,\n\t\t\t...(effectiveYolo && { permissionMode: 'bypassPermissions' as const }),\n\t\t})\n\n\t\t// Output final JSON for --json mode (--json-stream already streamed to stdout)\n\t\tif (printOptions?.json) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.log(JSON.stringify({\n\t\t\t\tsuccess: true,\n\t\t\t\toutput: claudeResult ?? ''\n\t\t\t}))\n\t\t}\n\n\t\tlogger.debug('Claude session completed')\n\t\tlogger.info(chalk.green('Planning session ended.'))\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/recap.ts"],"sourcesContent":["/**\n * RecapCommand - Fast read-only command for VS Code extension\n *\n * Reads ~/.config/iloom-ai/recaps/{current-loom}.json and outputs it.\n * Skips config validation for fast startup.\n * Includes filePath in output so extension can set up file watcher.\n */\nimport path from 'path'\nimport os from 'os'\nimport fs from 'fs-extra'\nimport type { RecapFile, RecapOutput } from '../mcp/recap-types.js'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { IdentifierParser } from '../utils/IdentifierParser.js'\nimport { formatRecapMarkdown } from '../utils/recap-formatter.js'\n\nconst RECAPS_DIR = path.join(os.homedir(), '.config', 'iloom-ai', 'recaps')\n\n/**\n * Reuse MetadataManager.slugifyPath() algorithm\n *\n * Algorithm:\n * 1. Trim trailing slashes\n * 2. Replace all path separators (/ or \\) with ___ (triple underscore)\n * 3. Replace any other non-alphanumeric characters (except _ and -) with -\n * 4. Append .json\n */\nfunction slugifyPath(loomPath: string): string {\n\tlet slug = loomPath.replace(/[/\\\\]+$/, '')\n\tslug = slug.replace(/[/\\\\]/g, '___')\n\tslug = slug.replace(/[^a-zA-Z0-9_-]/g, '-')\n\treturn `${slug}.json`\n}\n\nexport interface RecapCommandInput {\n\tidentifier?: string | undefined // Optional identifier (issue number, PR number, branch name)\n\tjson?: boolean | undefined\n}\n\nexport class RecapCommand {\n\t/**\n\t * Execute the recap command\n\t * Returns RecapOutput in JSON mode, void otherwise\n\t */\n\tasync execute(input: RecapCommandInput): Promise<RecapOutput | void> {\n\t\t// Resolve loom path from identifier or fall back to cwd\n\t\tconst loomPath = await this.resolveLoomPath(input.identifier)\n\t\tconst filePath = path.join(RECAPS_DIR, slugifyPath(loomPath))\n\n\t\t// Read recap file (return empty object if not found)\n\t\tlet recap: RecapFile = {}\n\t\ttry {\n\t\t\tif (await fs.pathExists(filePath)) {\n\t\t\t\tconst content = await fs.readFile(filePath, 'utf8')\n\t\t\t\trecap = JSON.parse(content) as RecapFile\n\t\t\t}\n\t\t} catch {\n\t\t\t// Graceful degradation - return empty recap on read error\n\t\t\t// This is intentional for fast startup\n\t\t}\n\n\t\t// Build output with filePath for file watching (provide defaults for optional fields)\n\t\tconst goal = recap.goal ?? null\n\t\tconst complexity = recap.complexity ?? null\n\t\tconst entries = recap.entries ?? []\n\t\tconst artifacts = recap.artifacts ?? []\n\t\tconst result: RecapOutput = { filePath, goal, complexity, entries, artifacts }\n\n\t\tif (input.json) {\n\t\t\treturn result\n\t\t}\n\n\t\t// Non-JSON mode: print markdown format (intentionally using console.log for piping/redirection)\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.log(formatRecapMarkdown(result))\n\t}\n\n\t/**\n\t * Resolve identifier to loom path\n\t * Falls back to cwd when no identifier is provided (backward compatible)\n\t */\n\tprivate async resolveLoomPath(identifier: string | undefined): Promise<string> {\n\t\t// Default: use current working directory\n\t\tif (!identifier?.trim()) {\n\t\t\treturn process.cwd()\n\t\t}\n\n\t\tconst trimmedId = identifier.trim()\n\t\tconst gitWorktreeManager = new GitWorktreeManager()\n\t\tconst identifierParser = new IdentifierParser(gitWorktreeManager)\n\n\t\t// Check for PR-specific formats: pr/123, PR-123, PR/123\n\t\tconst prPattern = /^(?:pr|PR)[/-](\\d+)$/\n\t\tconst prMatch = trimmedId.match(prPattern)\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tconst worktree = await gitWorktreeManager.findWorktreeForPR(prNumber, '')\n\t\t\tif (worktree) {\n\t\t\t\treturn worktree.path\n\t\t\t}\n\t\t\tthrow new Error(`No worktree found for PR #${prNumber}`)\n\t\t}\n\n\t\t// Use IdentifierParser for pattern-based detection\n\t\ttry {\n\t\t\tconst parsed = await identifierParser.parseForPatternDetection(trimmedId)\n\n\t\t\t// Find worktree based on parsed type\n\t\t\tif (parsed.type === 'pr' && typeof parsed.number === 'number') {\n\t\t\t\tconst worktree = await gitWorktreeManager.findWorktreeForPR(parsed.number, '')\n\t\t\t\tif (worktree) {\n\t\t\t\t\treturn worktree.path\n\t\t\t\t}\n\t\t\t\tthrow new Error(`No worktree found for PR #${parsed.number}`)\n\t\t\t}\n\n\t\t\tif (parsed.type === 'issue' && parsed.number !== undefined) {\n\t\t\t\tconst worktree = await gitWorktreeManager.findWorktreeForIssue(parsed.number)\n\t\t\t\tif (worktree) {\n\t\t\t\t\treturn worktree.path\n\t\t\t\t}\n\t\t\t\tthrow new Error(`No worktree found for issue #${parsed.number}`)\n\t\t\t}\n\n\t\t\tif (parsed.type === 'branch' && parsed.branchName) {\n\t\t\t\tconst worktree = await gitWorktreeManager.findWorktreeForBranch(parsed.branchName)\n\t\t\t\tif (worktree) {\n\t\t\t\t\treturn worktree.path\n\t\t\t\t}\n\t\t\t\tthrow new Error(`No worktree found for branch: ${parsed.branchName}`)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Re-throw IdentifierParser errors with context\n\t\t\tif (error instanceof Error) {\n\t\t\t\tthrow new Error(`Could not resolve identifier '${identifier}': ${error.message}`)\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\n\t\t// Should not reach here, but provide a fallback error\n\t\tthrow new Error(`Could not resolve identifier: ${identifier}`)\n\t}\n}\n"],"mappings":"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","names":[]}
|
package/dist/run-QUXJKDQQ.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
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 { 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 { 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'\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 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})\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)\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 url = `http://localhost:${port}`\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":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/shell.ts"],"sourcesContent":["import path from 'path'\nimport { execa } from 'execa'\nimport fs from 'fs-extra'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { IdentifierParser } from '../utils/IdentifierParser.js'\nimport { loadWorkspaceEnv, getDotenvFlowFiles } from '../utils/env.js'\nimport { extractIssueNumber } from '../utils/git.js'\nimport { logger } from '../utils/logger.js'\nimport type { GitWorktree } from '../types/worktree.js'\n\nexport interface ShellCommandInput {\n\tidentifier?: string | undefined\n}\n\ninterface ParsedShellInput {\n\ttype: 'issue' | 'pr' | 'branch'\n\tnumber?: string | number\n\tbranchName?: string\n\toriginalInput: string\n\tautoDetected: boolean\n}\n\n/**\n * ShellCommand - Open interactive shell with workspace environment\n * Loads dotenv-flow files when sourceEnvOnStart is configured\n */\nexport class ShellCommand {\n\tconstructor(\n\t\tprivate gitWorktreeManager = new GitWorktreeManager(),\n\t\tprivate identifierParser = new IdentifierParser(new GitWorktreeManager()),\n\t\tprivate settingsManager = new SettingsManager()\n\t) {}\n\n\tasync execute(input: ShellCommandInput): 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.debug(`Found worktree at: ${worktree.path}`)\n\n\t\t// 3. Load settings to check sourceEnvOnStart\n\t\tconst settings = await this.settingsManager.loadSettings()\n\t\tconst shouldLoadEnv = settings.sourceEnvOnStart ?? false\n\n\t\t// 4. Build environment variables\n\t\tlet envVars: Record<string, string> = { ...process.env as Record<string, string> }\n\t\tlet loadedEnvFiles: string[] = []\n\n\t\tif (shouldLoadEnv) {\n\t\t\tconst envResult = loadWorkspaceEnv(worktree.path)\n\t\t\tif (envResult.parsed) {\n\t\t\t\tenvVars = { ...envVars, ...envResult.parsed }\n\t\t\t}\n\t\t\t// Determine which files were actually loaded\n\t\t\tloadedEnvFiles = await this.getExistingEnvFiles(worktree.path)\n\t\t}\n\n\t\t// 5. Set ILOOM_LOOM for PS1 customization\n\t\tconst loomIdentifier = this.formatLoomIdentifier(parsed)\n\t\tenvVars.ILOOM_LOOM = loomIdentifier\n\n\t\t// 6. Detect shell\n\t\tconst shell = this.detectShell()\n\n\t\t// 7. Print summary\n\t\tthis.printSummary(worktree.path, shell, loadedEnvFiles, envVars, shouldLoadEnv)\n\n\t\t// 8. Launch interactive shell\n\t\tawait execa(shell, [], {\n\t\t\tcwd: worktree.path,\n\t\t\tenv: envVars,\n\t\t\tstdio: 'inherit',\n\t\t})\n\t}\n\n\t/**\n\t * Parse explicit identifier input\n\t */\n\tprivate async parseExplicitInput(identifier: string): Promise<ParsedShellInput> {\n\t\tconst parsed = await this.identifierParser.parseForPatternDetection(identifier)\n\n\t\t// Description type should never reach terminal command\n\t\tif (parsed.type === 'description') {\n\t\t\tthrow new Error('Description input type is not supported in terminal command')\n\t\t}\n\n\t\tconst result: ParsedShellInput = {\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 */\n\tprivate async autoDetectFromCurrentDirectory(): Promise<ParsedShellInput> {\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: ParsedShellInput): 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\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\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: ParsedShellInput): 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 * Format loom identifier for ILOOM_LOOM env var\n\t */\n\tprivate formatLoomIdentifier(parsed: ParsedShellInput): string {\n\t\tif (parsed.type === 'issue') {\n\t\t\treturn `issue-${parsed.number}`\n\t\t}\n\t\tif (parsed.type === 'pr') {\n\t\t\treturn `pr-${parsed.number}`\n\t\t}\n\t\treturn parsed.branchName ?? parsed.originalInput\n\t}\n\n\t/**\n\t * Detect shell based on platform and environment variables\n\t */\n\tprivate detectShell(): string {\n\t\t// ILOOM_SHELL takes highest priority\n\t\tif (process.env.ILOOM_SHELL) {\n\t\t\treturn process.env.ILOOM_SHELL\n\t\t}\n\n\t\t// Platform-specific detection\n\t\tif (process.platform === 'win32') {\n\t\t\t// Windows: prefer PowerShell, fall back to COMSPEC (cmd.exe)\n\t\t\treturn process.env.COMSPEC ?? 'cmd.exe'\n\t\t}\n\n\t\t// Unix/macOS: use SHELL or fall back to /bin/bash\n\t\treturn process.env.SHELL ?? '/bin/bash'\n\t}\n\n\t/**\n\t * Get list of existing dotenv-flow files in workspace\n\t */\n\tprivate async getExistingEnvFiles(workspacePath: string): Promise<string[]> {\n\t\tconst files = getDotenvFlowFiles()\n\t\tconst existing: string[] = []\n\n\t\tfor (const file of files) {\n\t\t\tconst fullPath = path.join(workspacePath, file)\n\t\t\tif (await fs.pathExists(fullPath)) {\n\t\t\t\texisting.push(file)\n\t\t\t}\n\t\t}\n\n\t\treturn existing\n\t}\n\n\t/**\n\t * Print summary of shell session\n\t */\n\tprivate printSummary(\n\t\tworkspacePath: string,\n\t\tshell: string,\n\t\tloadedEnvFiles: string[],\n\t\tenvVars: Record<string, string>,\n\t\tenvEnabled: boolean\n\t): void {\n\t\tlogger.info('Opening interactive shell')\n\t\tlogger.info(` Workspace: ${workspacePath}`)\n\t\tlogger.info(` Shell: ${shell}`)\n\n\t\tif (envEnabled) {\n\t\t\tif (loadedEnvFiles.length > 0) {\n\t\t\t\tlogger.info(` Env files: ${loadedEnvFiles.join(', ')}`)\n\t\t\t} else {\n\t\t\t\tlogger.info(' Env files: (none found)')\n\t\t\t}\n\n\t\t\t// Print key environment variables if present\n\t\t\tconst keyVars = ['PORT', 'DATABASE_URL', 'NODE_ENV']\n\t\t\tconst presentVars = keyVars.filter(v => envVars[v])\n\t\t\tif (presentVars.length > 0) {\n\t\t\t\tconst varSummary = presentVars.map(v => {\n\t\t\t\t\tconst value = envVars[v]\n\t\t\t\t\t// Truncate long values like DATABASE_URL\n\t\t\t\t\tconst displayValue = value && value.length > 40\n\t\t\t\t\t\t? value.substring(0, 37) + '...'\n\t\t\t\t\t\t: value\n\t\t\t\t\treturn `${v}=${displayValue}`\n\t\t\t\t}).join(', ')\n\t\t\t\tlogger.info(` Key vars: ${varSummary}`)\n\t\t\t}\n\t\t} else {\n\t\t\tlogger.info(' Env loading: disabled (set sourceEnvOnStart in settings to enable)')\n\t\t}\n\n\t\tlogger.info('')\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/summary.ts"],"sourcesContent":["import path from 'path'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { MetadataManager } from '../lib/MetadataManager.js'\nimport { SessionSummaryService } from '../lib/SessionSummaryService.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { PRManager } from '../lib/PRManager.js'\nimport { getLogger } from '../utils/logger-context.js'\nimport { extractIssueNumber } from '../utils/git.js'\nimport type { GitWorktree } from '../types/worktree.js'\nimport type { SummaryResult } from '../types/index.js'\n\n/**\n * Options for the summary command\n */\nexport interface SummaryOptions {\n\twithComment?: boolean\n\tjson?: boolean\n}\n\n/**\n * Input for the summary command\n */\nexport interface SummaryCommandInput {\n\tidentifier?: string | undefined\n\toptions: SummaryOptions\n}\n\n/**\n * Parsed input with loom information\n */\ninterface ParsedSummaryInput {\n\tworktree: GitWorktree\n\tloomType: 'issue' | 'pr' | 'branch'\n\tissueNumber?: string | number | undefined\n}\n\n/**\n * SummaryCommand - Generate and optionally post Claude session summaries\n *\n * This command allows generating the session summary without going through\n * the full `il finish` workflow. It can:\n * 1. Generate a summary and print it to stdout\n * 2. Optionally post the summary as a comment (--with-comment flag)\n */\nexport class SummaryCommand {\n\tconstructor(\n\t\tprivate gitWorktreeManager = new GitWorktreeManager(),\n\t\tprivate metadataManager = new MetadataManager(),\n\t\tprivate sessionSummaryService = new SessionSummaryService(),\n\t\tprivate settingsManager = new SettingsManager()\n\t) {}\n\n\t/**\n\t * Determine PR number based on merge mode and metadata\n\t * Returns undefined if should post to issue instead\n\t */\n\tprivate async getPRNumberForPosting(\n\t\tworktreePath: string,\n\t\tbranchName: string\n\t): Promise<number | undefined> {\n\t\tconst settings = await this.settingsManager.loadSettings(worktreePath)\n\t\tconst mergeMode = settings.mergeBehavior?.mode ?? 'local'\n\n\t\tif (mergeMode === 'github-draft-pr') {\n\t\t\tconst metadata = await this.metadataManager.readMetadata(worktreePath)\n\t\t\treturn metadata?.draftPrNumber ?? undefined\n\t\t}\n\n\t\tif (mergeMode === 'github-pr') {\n\t\t\tconst prManager = new PRManager(settings)\n\t\t\tconst existingPR = await prManager.checkForExistingPR(branchName, worktreePath)\n\t\t\treturn existingPR?.number\n\t\t}\n\n\t\treturn undefined // local mode - post to issue\n\t}\n\n\t/**\n\t * Execute the summary command\n\t *\n\t * @param input - Command input containing identifier and options\n\t * @returns SummaryResult when in JSON mode, void otherwise\n\t */\n\tasync execute(input: SummaryCommandInput): Promise<SummaryResult | void> {\n\t\tconst logger = getLogger()\n\n\t\t// 1. Find the loom by identifier (or auto-detect from current directory)\n\t\tconst parsed = input.identifier?.trim()\n\t\t\t? await this.findLoom(input.identifier.trim())\n\t\t\t: await this.autoDetectFromCurrentDirectory()\n\n\t\t// 2. Generate the summary (service handles session ID internally, including deterministic fallback)\n\t\tconst result = await this.sessionSummaryService.generateSummary(\n\t\t\tparsed.worktree.path,\n\t\t\tparsed.worktree.branch,\n\t\t\tparsed.loomType,\n\t\t\tparsed.issueNumber\n\t\t)\n\n\t\t// 4. Apply attribution if --with-comment is used (so output matches what will be posted)\n\t\tlet displaySummary = result.summary\n\t\tif (input.options.withComment && parsed.loomType !== 'branch') {\n\t\t\tdisplaySummary = await this.sessionSummaryService.applyAttribution(\n\t\t\t\tresult.summary,\n\t\t\t\tparsed.worktree.path\n\t\t\t)\n\t\t}\n\n\t\t// 5. In JSON mode, return the structured result\n\t\tif (input.options.json) {\n\t\t\tconst jsonResult: SummaryResult = {\n\t\t\t\tsummary: displaySummary,\n\t\t\t\tsessionId: result.sessionId,\n\t\t\t\tbranchName: parsed.worktree.branch,\n\t\t\t\tloomType: parsed.loomType,\n\t\t\t}\n\t\t\t// Only include issueNumber if defined\n\t\t\tif (parsed.issueNumber !== undefined) {\n\t\t\t\tjsonResult.issueNumber = parsed.issueNumber\n\t\t\t}\n\t\t\treturn jsonResult\n\t\t}\n\n\t\t// 6. Print the summary to stdout (intentionally using console.log for piping/redirection)\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.log(displaySummary)\n\n\t\t// 7. Optionally post the summary as a comment\n\t\tif (input.options.withComment) {\n\t\t\t// Skip posting for branch type looms (no issue to comment on)\n\t\t\tif (parsed.loomType === 'branch') {\n\t\t\t\tlogger.debug('Skipping comment posting: branch type looms have no associated issue')\n\t\t\t} else if (parsed.issueNumber !== undefined) {\n\t\t\t\t// Determine if we should post to PR instead of issue\n\t\t\t\tconst prNumber = await this.getPRNumberForPosting(\n\t\t\t\t\tparsed.worktree.path,\n\t\t\t\t\tparsed.worktree.branch\n\t\t\t\t)\n\t\t\t\tawait this.sessionSummaryService.postSummary(\n\t\t\t\t\tparsed.issueNumber,\n\t\t\t\t\tresult.summary,\n\t\t\t\t\tparsed.worktree.path,\n\t\t\t\t\tprNumber\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Find a loom by identifier\n\t *\n\t * Supports:\n\t * - Numeric identifiers (issue numbers): \"123\", \"#123\"\n\t * - PR identifiers: \"pr/123\"\n\t * - Branch names: \"my-feature-branch\"\n\t */\n\tprivate async findLoom(identifier: string): Promise<ParsedSummaryInput> {\n\t\t// Remove # prefix if present and trim whitespace\n\t\tconst cleanId = identifier.replace(/^#/, '').trim()\n\n\t\t// Check for PR pattern: pr/123 or PR/123\n\t\tconst prMatch = cleanId.match(/^pr\\/(\\d+)$/i)\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tconst worktree = await this.gitWorktreeManager.findWorktreeForPR(prNumber, '')\n\t\t\tif (worktree) {\n\t\t\t\tconst metadata = await this.metadataManager.readMetadata(worktree.path)\n\t\t\t\treturn {\n\t\t\t\t\tworktree,\n\t\t\t\t\tloomType: 'pr',\n\t\t\t\t\tissueNumber: metadata?.pr_numbers?.[0] ?? String(prNumber),\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new Error(`No loom found for identifier: ${identifier}`)\n\t\t}\n\n\t\t// Check if input is numeric (issue number)\n\t\tconst numericMatch = cleanId.match(/^(\\d+)$/)\n\t\tif (numericMatch?.[1]) {\n\t\t\tconst issueNumber = parseInt(numericMatch[1], 10)\n\n\t\t\t// Try issue first\n\t\t\tconst issueWorktree = await this.gitWorktreeManager.findWorktreeForIssue(issueNumber)\n\t\t\tif (issueWorktree) {\n\t\t\t\tconst metadata = await this.metadataManager.readMetadata(issueWorktree.path)\n\t\t\t\treturn {\n\t\t\t\t\tworktree: issueWorktree,\n\t\t\t\t\tloomType: metadata?.issueType ?? 'issue',\n\t\t\t\t\tissueNumber: metadata?.issue_numbers?.[0] ?? String(issueNumber),\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Then try PR\n\t\t\tconst prWorktree = await this.gitWorktreeManager.findWorktreeForPR(issueNumber, '')\n\t\t\tif (prWorktree) {\n\t\t\t\tconst metadata = await this.metadataManager.readMetadata(prWorktree.path)\n\t\t\t\treturn {\n\t\t\t\t\tworktree: prWorktree,\n\t\t\t\t\tloomType: 'pr',\n\t\t\t\t\tissueNumber: metadata?.pr_numbers?.[0] ?? String(issueNumber),\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthrow new Error(`No loom found for identifier: ${identifier}`)\n\t\t}\n\n\t\t// Check for alphanumeric issue identifier (Linear/Jira style: ABC-123)\n\t\tconst alphanumericMatch = cleanId.match(/^([A-Za-z]+-\\d+)$/)\n\t\tif (alphanumericMatch?.[1]) {\n\t\t\tconst alphanumericId = alphanumericMatch[1]\n\t\t\tconst issueWorktree = await this.gitWorktreeManager.findWorktreeForIssue(alphanumericId)\n\t\t\tif (issueWorktree) {\n\t\t\t\tconst metadata = await this.metadataManager.readMetadata(issueWorktree.path)\n\t\t\t\treturn {\n\t\t\t\t\tworktree: issueWorktree,\n\t\t\t\t\tloomType: metadata?.issueType ?? 'issue',\n\t\t\t\t\tissueNumber: metadata?.issue_numbers?.[0] ?? alphanumericId,\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new Error(`No loom found for identifier: ${identifier}`)\n\t\t}\n\n\t\t// Treat as branch name\n\t\tconst branchWorktree = await this.gitWorktreeManager.findWorktreeForBranch(cleanId)\n\t\tif (branchWorktree) {\n\t\t\tconst metadata = await this.metadataManager.readMetadata(branchWorktree.path)\n\t\t\tconst loomType = metadata?.issueType ?? 'branch'\n\n\t\t\t// For branch looms, try to get issue number from metadata\n\t\t\tlet issueNumber: string | number | undefined\n\t\t\tif (loomType === 'issue' && metadata?.issue_numbers?.[0]) {\n\t\t\t\tissueNumber = metadata.issue_numbers[0]\n\t\t\t} else if (loomType === 'pr' && metadata?.pr_numbers?.[0]) {\n\t\t\t\tissueNumber = metadata.pr_numbers[0]\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tworktree: branchWorktree,\n\t\t\t\tloomType,\n\t\t\t\tissueNumber,\n\t\t\t}\n\t\t}\n\n\t\tthrow new Error(`No loom found for identifier: ${identifier}`)\n\t}\n\n\t/**\n\t * Auto-detect loom from current working directory\n\t * Ports logic from FinishCommand.autoDetectFromCurrentDirectory()\n\t *\n\t * Detection strategy:\n\t * 1. Check current directory name for PR pattern (_pr_N suffix)\n\t * 2. Check current directory name for issue pattern (issue-N or -N-)\n\t * 3. Get current branch and check for issue pattern\n\t * 4. Fall back to using current branch as branch loom\n\t */\n\tprivate async autoDetectFromCurrentDirectory(): Promise<ParsedSummaryInput> {\n\t\tconst logger = getLogger()\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\n\t\t\tconst worktree = await this.gitWorktreeManager.findWorktreeForPR(prNumber, '')\n\t\t\tif (worktree) {\n\t\t\t\tconst metadata = await this.metadataManager.readMetadata(worktree.path)\n\t\t\t\treturn {\n\t\t\t\t\tworktree,\n\t\t\t\t\tloomType: 'pr',\n\t\t\t\t\tissueNumber: metadata?.pr_numbers?.[0] ?? String(prNumber),\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new Error(`No loom found for auto-detected PR #${prNumber}`)\n\t\t}\n\n\t\t// Check for issue pattern in directory name\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\n\t\t\tconst worktree = await this.gitWorktreeManager.findWorktreeForIssue(issueNumber)\n\t\t\tif (worktree) {\n\t\t\t\tconst metadata = await this.metadataManager.readMetadata(worktree.path)\n\t\t\t\treturn {\n\t\t\t\t\tworktree,\n\t\t\t\t\tloomType: metadata?.issueType ?? 'issue',\n\t\t\t\t\tissueNumber: metadata?.issue_numbers?.[0] ?? String(issueNumber),\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new Error(`No loom found for auto-detected issue #${issueNumber}`)\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 loom. Please provide an issue number, PR number, or branch name.\\n' +\n\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\n\t\t\tconst worktree = await this.gitWorktreeManager.findWorktreeForIssue(branchIssueNumber)\n\t\t\tif (worktree) {\n\t\t\t\tconst metadata = await this.metadataManager.readMetadata(worktree.path)\n\t\t\t\treturn {\n\t\t\t\t\tworktree,\n\t\t\t\t\tloomType: metadata?.issueType ?? 'issue',\n\t\t\t\t\tissueNumber: metadata?.issue_numbers?.[0] ?? String(branchIssueNumber),\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Last resort: use current branch as branch loom\n\t\tconst branchWorktree = await this.gitWorktreeManager.findWorktreeForBranch(currentBranch)\n\t\tif (branchWorktree) {\n\t\t\tconst metadata = await this.metadataManager.readMetadata(branchWorktree.path)\n\t\t\tconst loomType = metadata?.issueType ?? 'branch'\n\n\t\t\t// For branch looms, try to get issue number from metadata\n\t\t\tlet resolvedIssueNumber: string | number | undefined\n\t\t\tif (loomType === 'issue' && metadata?.issue_numbers?.[0]) {\n\t\t\t\tresolvedIssueNumber = metadata.issue_numbers[0]\n\t\t\t} else if (loomType === 'pr' && metadata?.pr_numbers?.[0]) {\n\t\t\t\tresolvedIssueNumber = metadata.pr_numbers[0]\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tworktree: branchWorktree,\n\t\t\t\tloomType,\n\t\t\t\tissueNumber: resolvedIssueNumber,\n\t\t\t}\n\t\t}\n\n\t\tthrow new Error(\n\t\t\t`Could not auto-detect loom from current directory or branch: ${currentBranch}\\n` +\n\t\t\t'Please provide an issue number, PR number, or branch name.'\n\t\t)\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,OAAO,UAAU;AA4CV,IAAM,iBAAN,MAAqB;AAAA,EAC3B,YACS,qBAAqB,IAAI,mBAAmB,GAC5C,kBAAkB,IAAI,gBAAgB,GACtC,wBAAwB,IAAI,sBAAsB,GAClD,kBAAkB,IAAI,gBAAgB,GAC7C;AAJO;AACA;AACA;AACA;AAAA,EACN;AAAA;AAAA;AAAA;AAAA;AAAA,EAMH,MAAc,sBACb,cACA,YAC8B;AA3DhC;AA4DE,UAAM,WAAW,MAAM,KAAK,gBAAgB,aAAa,YAAY;AACrE,UAAM,cAAY,cAAS,kBAAT,mBAAwB,SAAQ;AAElD,QAAI,cAAc,mBAAmB;AACpC,YAAM,WAAW,MAAM,KAAK,gBAAgB,aAAa,YAAY;AACrE,cAAO,qCAAU,kBAAiB;AAAA,IACnC;AAEA,QAAI,cAAc,aAAa;AAC9B,YAAM,YAAY,IAAI,UAAU,QAAQ;AACxC,YAAM,aAAa,MAAM,UAAU,mBAAmB,YAAY,YAAY;AAC9E,aAAO,yCAAY;AAAA,IACpB;AAEA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,QAAQ,OAA2D;AAnF1E;AAoFE,UAAM,SAAS,UAAU;AAGzB,UAAM,WAAS,WAAM,eAAN,mBAAkB,UAC9B,MAAM,KAAK,SAAS,MAAM,WAAW,KAAK,CAAC,IAC3C,MAAM,KAAK,+BAA+B;AAG7C,UAAM,SAAS,MAAM,KAAK,sBAAsB;AAAA,MAC/C,OAAO,SAAS;AAAA,MAChB,OAAO,SAAS;AAAA,MAChB,OAAO;AAAA,MACP,OAAO;AAAA,IACR;AAGA,QAAI,iBAAiB,OAAO;AAC5B,QAAI,MAAM,QAAQ,eAAe,OAAO,aAAa,UAAU;AAC9D,uBAAiB,MAAM,KAAK,sBAAsB;AAAA,QACjD,OAAO;AAAA,QACP,OAAO,SAAS;AAAA,MACjB;AAAA,IACD;AAGA,QAAI,MAAM,QAAQ,MAAM;AACvB,YAAM,aAA4B;AAAA,QACjC,SAAS;AAAA,QACT,WAAW,OAAO;AAAA,QAClB,YAAY,OAAO,SAAS;AAAA,QAC5B,UAAU,OAAO;AAAA,MAClB;AAEA,UAAI,OAAO,gBAAgB,QAAW;AACrC,mBAAW,cAAc,OAAO;AAAA,MACjC;AACA,aAAO;AAAA,IACR;AAIA,YAAQ,IAAI,cAAc;AAG1B,QAAI,MAAM,QAAQ,aAAa;AAE9B,UAAI,OAAO,aAAa,UAAU;AACjC,eAAO,MAAM,sEAAsE;AAAA,MACpF,WAAW,OAAO,gBAAgB,QAAW;AAE5C,cAAM,WAAW,MAAM,KAAK;AAAA,UAC3B,OAAO,SAAS;AAAA,UAChB,OAAO,SAAS;AAAA,QACjB;AACA,cAAM,KAAK,sBAAsB;AAAA,UAChC,OAAO;AAAA,UACP,OAAO;AAAA,UACP,OAAO,SAAS;AAAA,UAChB;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAc,SAAS,YAAiD;AA5JzE;AA8JE,UAAM,UAAU,WAAW,QAAQ,MAAM,EAAE,EAAE,KAAK;AAGlD,UAAM,UAAU,QAAQ,MAAM,cAAc;AAC5C,QAAI,mCAAU,IAAI;AACjB,YAAM,WAAW,SAAS,QAAQ,CAAC,GAAG,EAAE;AACxC,YAAM,WAAW,MAAM,KAAK,mBAAmB,kBAAkB,UAAU,EAAE;AAC7E,UAAI,UAAU;AACb,cAAM,WAAW,MAAM,KAAK,gBAAgB,aAAa,SAAS,IAAI;AACtE,eAAO;AAAA,UACN;AAAA,UACA,UAAU;AAAA,UACV,eAAa,0CAAU,eAAV,mBAAuB,OAAM,OAAO,QAAQ;AAAA,QAC1D;AAAA,MACD;AACA,YAAM,IAAI,MAAM,iCAAiC,UAAU,EAAE;AAAA,IAC9D;AAGA,UAAM,eAAe,QAAQ,MAAM,SAAS;AAC5C,QAAI,6CAAe,IAAI;AACtB,YAAM,cAAc,SAAS,aAAa,CAAC,GAAG,EAAE;AAGhD,YAAM,gBAAgB,MAAM,KAAK,mBAAmB,qBAAqB,WAAW;AACpF,UAAI,eAAe;AAClB,cAAM,WAAW,MAAM,KAAK,gBAAgB,aAAa,cAAc,IAAI;AAC3E,eAAO;AAAA,UACN,UAAU;AAAA,UACV,WAAU,qCAAU,cAAa;AAAA,UACjC,eAAa,0CAAU,kBAAV,mBAA0B,OAAM,OAAO,WAAW;AAAA,QAChE;AAAA,MACD;AAGA,YAAM,aAAa,MAAM,KAAK,mBAAmB,kBAAkB,aAAa,EAAE;AAClF,UAAI,YAAY;AACf,cAAM,WAAW,MAAM,KAAK,gBAAgB,aAAa,WAAW,IAAI;AACxE,eAAO;AAAA,UACN,UAAU;AAAA,UACV,UAAU;AAAA,UACV,eAAa,0CAAU,eAAV,mBAAuB,OAAM,OAAO,WAAW;AAAA,QAC7D;AAAA,MACD;AAEA,YAAM,IAAI,MAAM,iCAAiC,UAAU,EAAE;AAAA,IAC9D;AAGA,UAAM,oBAAoB,QAAQ,MAAM,mBAAmB;AAC3D,QAAI,uDAAoB,IAAI;AAC3B,YAAM,iBAAiB,kBAAkB,CAAC;AAC1C,YAAM,gBAAgB,MAAM,KAAK,mBAAmB,qBAAqB,cAAc;AACvF,UAAI,eAAe;AAClB,cAAM,WAAW,MAAM,KAAK,gBAAgB,aAAa,cAAc,IAAI;AAC3E,eAAO;AAAA,UACN,UAAU;AAAA,UACV,WAAU,qCAAU,cAAa;AAAA,UACjC,eAAa,0CAAU,kBAAV,mBAA0B,OAAM;AAAA,QAC9C;AAAA,MACD;AACA,YAAM,IAAI,MAAM,iCAAiC,UAAU,EAAE;AAAA,IAC9D;AAGA,UAAM,iBAAiB,MAAM,KAAK,mBAAmB,sBAAsB,OAAO;AAClF,QAAI,gBAAgB;AACnB,YAAM,WAAW,MAAM,KAAK,gBAAgB,aAAa,eAAe,IAAI;AAC5E,YAAM,YAAW,qCAAU,cAAa;AAGxC,UAAI;AACJ,UAAI,aAAa,aAAW,0CAAU,kBAAV,mBAA0B,KAAI;AACzD,sBAAc,SAAS,cAAc,CAAC;AAAA,MACvC,WAAW,aAAa,UAAQ,0CAAU,eAAV,mBAAuB,KAAI;AAC1D,sBAAc,SAAS,WAAW,CAAC;AAAA,MACpC;AAEA,aAAO;AAAA,QACN,UAAU;AAAA,QACV;AAAA,QACA;AAAA,MACD;AAAA,IACD;AAEA,UAAM,IAAI,MAAM,iCAAiC,UAAU,EAAE;AAAA,EAC9D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAc,iCAA8D;AAhQ7E;AAiQE,UAAM,SAAS,UAAU;AACzB,UAAM,aAAa,KAAK,SAAS,QAAQ,IAAI,CAAC;AAG9C,UAAM,YAAY;AAClB,UAAM,UAAU,WAAW,MAAM,SAAS;AAE1C,QAAI,mCAAU,IAAI;AACjB,YAAM,WAAW,SAAS,QAAQ,CAAC,GAAG,EAAE;AACxC,aAAO,MAAM,qBAAqB,QAAQ,oBAAoB,UAAU,EAAE;AAE1E,YAAM,WAAW,MAAM,KAAK,mBAAmB,kBAAkB,UAAU,EAAE;AAC7E,UAAI,UAAU;AACb,cAAM,WAAW,MAAM,KAAK,gBAAgB,aAAa,SAAS,IAAI;AACtE,eAAO;AAAA,UACN;AAAA,UACA,UAAU;AAAA,UACV,eAAa,0CAAU,eAAV,mBAAuB,OAAM,OAAO,QAAQ;AAAA,QAC1D;AAAA,MACD;AACA,YAAM,IAAI,MAAM,uCAAuC,QAAQ,EAAE;AAAA,IAClE;AAGA,UAAM,cAAc,mBAAmB,UAAU;AAEjD,QAAI,gBAAgB,MAAM;AACzB,aAAO,MAAM,wBAAwB,WAAW,oBAAoB,UAAU,EAAE;AAEhF,YAAM,WAAW,MAAM,KAAK,mBAAmB,qBAAqB,WAAW;AAC/E,UAAI,UAAU;AACb,cAAM,WAAW,MAAM,KAAK,gBAAgB,aAAa,SAAS,IAAI;AACtE,eAAO;AAAA,UACN;AAAA,UACA,WAAU,qCAAU,cAAa;AAAA,UACjC,eAAa,0CAAU,kBAAV,mBAA0B,OAAM,OAAO,WAAW;AAAA,QAChE;AAAA,MACD;AACA,YAAM,IAAI,MAAM,0CAA0C,WAAW,EAAE;AAAA,IACxE;AAGA,UAAM,WAAW,MAAM,KAAK,mBAAmB,YAAY;AAC3D,UAAM,gBAAgB,SAAS;AAE/B,QAAI,CAAC,eAAe;AACnB,YAAM,IAAI;AAAA,QACT;AAAA,MAED;AAAA,IACD;AAGA,UAAM,oBAAoB,mBAAmB,aAAa;AAC1D,QAAI,sBAAsB,MAAM;AAC/B,aAAO,MAAM,wBAAwB,iBAAiB,iBAAiB,aAAa,EAAE;AAEtF,YAAM,WAAW,MAAM,KAAK,mBAAmB,qBAAqB,iBAAiB;AACrF,UAAI,UAAU;AACb,cAAM,WAAW,MAAM,KAAK,gBAAgB,aAAa,SAAS,IAAI;AACtE,eAAO;AAAA,UACN;AAAA,UACA,WAAU,qCAAU,cAAa;AAAA,UACjC,eAAa,0CAAU,kBAAV,mBAA0B,OAAM,OAAO,iBAAiB;AAAA,QACtE;AAAA,MACD;AAAA,IACD;AAGA,UAAM,iBAAiB,MAAM,KAAK,mBAAmB,sBAAsB,aAAa;AACxF,QAAI,gBAAgB;AACnB,YAAM,WAAW,MAAM,KAAK,gBAAgB,aAAa,eAAe,IAAI;AAC5E,YAAM,YAAW,qCAAU,cAAa;AAGxC,UAAI;AACJ,UAAI,aAAa,aAAW,0CAAU,kBAAV,mBAA0B,KAAI;AACzD,8BAAsB,SAAS,cAAc,CAAC;AAAA,MAC/C,WAAW,aAAa,UAAQ,0CAAU,eAAV,mBAAuB,KAAI;AAC1D,8BAAsB,SAAS,WAAW,CAAC;AAAA,MAC5C;AAEA,aAAO;AAAA,QACN,UAAU;AAAA,QACV;AAAA,QACA,aAAa;AAAA,MACd;AAAA,IACD;AAEA,UAAM,IAAI;AAAA,MACT,gEAAgE,aAAa;AAAA;AAAA,IAE9E;AAAA,EACD;AACD;","names":[]}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/dist/{PromptTemplateManager-36YLQRHP.js.map → ProjectCapabilityDetector-5KSYUTBJ.js.map}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|