@iloom/cli 0.13.0-beta.0 → 0.13.1

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.
Files changed (173) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +3 -18
  3. package/dist/{ClaudeContextManager-RRGREEZQ.js → ClaudeContextManager-ZH6LEA5I.js} +5 -5
  4. package/dist/{ClaudeService-LEPW6QAC.js → ClaudeService-YR66WXZN.js} +4 -4
  5. package/dist/{IssueTrackerFactory-KE2BDCLC.js → IssueTrackerFactory-O2ZBA666.js} +3 -3
  6. package/dist/{LoomLauncher-GKQMR5E6.js → LoomLauncher-V54ENBEF.js} +5 -5
  7. package/dist/{MetadataManager-V4LSJ2PB.js → MetadataManager-HHE6LQF2.js} +2 -2
  8. package/dist/{PromptTemplateManager-I75WKXM4.js → PromptTemplateManager-4RFELNYY.js} +2 -2
  9. package/dist/README.md +3 -18
  10. package/dist/{SettingsManager-KQU7OX7G.js → SettingsManager-SLSYEYDZ.js} +4 -4
  11. package/dist/agents/iloom-artifact-reviewer.md +1 -0
  12. package/dist/agents/iloom-code-reviewer.md +21 -0
  13. package/dist/agents/iloom-issue-analyze-and-plan.md +30 -12
  14. package/dist/agents/iloom-issue-analyzer.md +32 -7
  15. package/dist/agents/iloom-issue-complexity-evaluator.md +32 -12
  16. package/dist/agents/iloom-issue-implementer.md +31 -12
  17. package/dist/agents/iloom-issue-planner.md +30 -12
  18. package/dist/agents/iloom-wave-verifier.md +177 -4
  19. package/dist/{build-V3KADFMO.js → build-ZTGWDHWU.js} +8 -8
  20. package/dist/{chunk-VVQQIG64.js → chunk-55NTREIU.js} +33 -30
  21. package/dist/chunk-55NTREIU.js.map +1 -0
  22. package/dist/{chunk-AYLC633W.js → chunk-7TN5VW4I.js} +65 -7
  23. package/dist/chunk-7TN5VW4I.js.map +1 -0
  24. package/dist/{chunk-RFCAPHL5.js → chunk-C2BVNJW5.js} +2 -2
  25. package/dist/{chunk-3XEXT35Z.js → chunk-E5OM25WK.js} +3 -3
  26. package/dist/{chunk-Q7VXHJP6.js → chunk-EHAITKLS.js} +10 -6
  27. package/dist/{chunk-Q7VXHJP6.js.map → chunk-EHAITKLS.js.map} +1 -1
  28. package/dist/{chunk-ZUIFO7B4.js → chunk-ERMEYFT6.js} +7 -2
  29. package/dist/chunk-ERMEYFT6.js.map +1 -0
  30. package/dist/{chunk-WGUGB54H.js → chunk-F5NKWLMQ.js} +21 -24
  31. package/dist/chunk-F5NKWLMQ.js.map +1 -0
  32. package/dist/{chunk-TN2D2RX7.js → chunk-G2DGDCDP.js} +33 -224
  33. package/dist/chunk-G2DGDCDP.js.map +1 -0
  34. package/dist/{chunk-NUUFP53X.js → chunk-GPBX2BY2.js} +2 -2
  35. package/dist/{chunk-SN4S5CWL.js → chunk-GQDVH6FA.js} +2 -2
  36. package/dist/{chunk-YUOVWWJX.js → chunk-HKEXRZMU.js} +5 -310
  37. package/dist/chunk-HKEXRZMU.js.map +1 -0
  38. package/dist/{chunk-TAEVA4QR.js → chunk-HWDQRW3O.js} +3 -3
  39. package/dist/chunk-HWDQRW3O.js.map +1 -0
  40. package/dist/{chunk-KQSV7FOG.js → chunk-J5JOJPK3.js} +2 -2
  41. package/dist/{chunk-PD75ZCFT.js → chunk-KCAWSZUO.js} +18 -17
  42. package/dist/chunk-KCAWSZUO.js.map +1 -0
  43. package/dist/{chunk-QNPJXO53.js → chunk-KGOBNC5A.js} +4 -4
  44. package/dist/{chunk-H3T3EPF3.js → chunk-LNY2Y32V.js} +2 -2
  45. package/dist/{chunk-7RCUWU3I.js → chunk-MRPIDNZU.js} +1 -1
  46. package/dist/chunk-MRPIDNZU.js.map +1 -0
  47. package/dist/{chunk-VIQOQ463.js → chunk-OLJ54WGW.js} +15 -10
  48. package/dist/chunk-OLJ54WGW.js.map +1 -0
  49. package/dist/{chunk-QQULYI2S.js → chunk-P5MNWBLH.js} +108 -47
  50. package/dist/chunk-P5MNWBLH.js.map +1 -0
  51. package/dist/{chunk-4VQXMEEP.js → chunk-PPQ5LV7U.js} +3 -3
  52. package/dist/{chunk-4VQXMEEP.js.map → chunk-PPQ5LV7U.js.map} +1 -1
  53. package/dist/{chunk-QED2WB2D.js → chunk-PS6K2AOV.js} +5 -5
  54. package/dist/{chunk-JD3K2344.js → chunk-QNRXRSKC.js} +36 -3
  55. package/dist/chunk-QNRXRSKC.js.map +1 -0
  56. package/dist/{chunk-SA446KA2.js → chunk-T4KFKKEB.js} +7 -7
  57. package/dist/{chunk-XCP2WDYA.js → chunk-T4NESGYB.js} +3 -3
  58. package/dist/{chunk-QXGM32TO.js → chunk-TJDKGKQV.js} +2 -2
  59. package/dist/{chunk-X5DRLONY.js → chunk-UXBVDD7U.js} +6 -6
  60. package/dist/{chunk-JDN4SPV3.js → chunk-WYDLOQYO.js} +2 -2
  61. package/dist/{chunk-4JZEQBWV.js → chunk-XIVLGWUX.js} +3 -1
  62. package/dist/chunk-XIVLGWUX.js.map +1 -0
  63. package/dist/{chunk-NTDY5AMO.js → chunk-ZEFTWM5Z.js} +2 -2
  64. package/dist/{cleanup-RJKLI47I.js → cleanup-BCVY7PEF.js} +22 -22
  65. package/dist/cleanup-BCVY7PEF.js.map +1 -0
  66. package/dist/cli.js +136 -105
  67. package/dist/cli.js.map +1 -1
  68. package/dist/{commit-SUHRUMDE.js → commit-L5JNBU4U.js} +8 -8
  69. package/dist/{compile-2MD346PO.js → compile-GPJOHXH4.js} +8 -8
  70. package/dist/{contribute-P4BMRY7C.js → contribute-QEGCI4PS.js} +4 -4
  71. package/dist/{dev-server-ZNTLWOL5.js → dev-server-UQKNKU2S.js} +249 -31
  72. package/dist/dev-server-UQKNKU2S.js.map +1 -0
  73. package/dist/{feedback-Q6WG2WX4.js → feedback-2LWXKLQZ.js} +4 -4
  74. package/dist/{git-TX2IEMB3.js → git-IS7AV3ED.js} +4 -4
  75. package/dist/hooks/iloom-hook.js +40 -2
  76. package/dist/{ignite-P644W2PK.js → ignite-VQDJQ37S.js} +12 -14
  77. package/dist/index.d.ts +73 -75
  78. package/dist/index.js +32 -32
  79. package/dist/index.js.map +1 -1
  80. package/dist/{init-5HFY7JG6.js → init-7SDJUAEZ.js} +8 -8
  81. package/dist/{install-deps-J4ALTM27.js → install-deps-NGSFDNUW.js} +8 -8
  82. package/dist/{issues-LZMIF22U.js → issues-4HQKEUP7.js} +5 -5
  83. package/dist/{lint-XIXKU22H.js → lint-C5FOVRXY.js} +8 -8
  84. package/dist/mcp/issue-management-server.js +19 -22
  85. package/dist/mcp/issue-management-server.js.map +1 -1
  86. package/dist/neon-helpers-LCZAN4U4.js +11 -0
  87. package/dist/{open-KUO35JIJ.js → open-2HL6GV5F.js} +19 -15
  88. package/dist/open-2HL6GV5F.js.map +1 -0
  89. package/dist/{plan-7CF56OIR.js → plan-GC3HF73T.js} +86 -66
  90. package/dist/plan-GC3HF73T.js.map +1 -0
  91. package/dist/{projects-L5AHUBGA.js → projects-3F6T3KZL.js} +2 -2
  92. package/dist/prompts/init-prompt.txt +40 -36
  93. package/dist/prompts/issue-prompt.txt +4 -1
  94. package/dist/prompts/plan-prompt.txt +97 -16
  95. package/dist/prompts/regular-prompt.txt +1 -1
  96. package/dist/prompts/swarm-orchestrator-prompt.txt +25 -12
  97. package/dist/{rebase-MAMWPA2L.js → rebase-MLIN572O.js} +7 -7
  98. package/dist/{recap-IDBO3KM5.js → recap-CKGKFDJL.js} +7 -7
  99. package/dist/{run-RGZHCQ6M.js → run-CUNRQNZS.js} +19 -15
  100. package/dist/run-CUNRQNZS.js.map +1 -0
  101. package/dist/schema/settings.schema.json +35 -31
  102. package/dist/{shell-7ADCDFIV.js → shell-M2YYPNGV.js} +6 -6
  103. package/dist/{summary-7J2HORFD.js → summary-XR4CBJEG.js} +9 -9
  104. package/dist/{test-SRB7EWU6.js → test-ESDAHEVE.js} +8 -8
  105. package/dist/{test-git-G7ATVIXG.js → test-git-KWPLHYSI.js} +4 -4
  106. package/dist/{test-jira-Q2HPA522.js → test-jira-6NK7UHSV.js} +3 -3
  107. package/dist/{test-prefix-JMDGXR5A.js → test-prefix-VVODGHXP.js} +4 -4
  108. package/dist/{test-webserver-GZFVXBGD.js → test-webserver-AHXKC6H4.js} +6 -6
  109. package/dist/{vscode-3I7ISHUU.js → vscode-OY7HOVRO.js} +6 -6
  110. package/package.json +1 -1
  111. package/dist/chunk-4JZEQBWV.js.map +0 -1
  112. package/dist/chunk-7RCUWU3I.js.map +0 -1
  113. package/dist/chunk-AYLC633W.js.map +0 -1
  114. package/dist/chunk-JD3K2344.js.map +0 -1
  115. package/dist/chunk-PD75ZCFT.js.map +0 -1
  116. package/dist/chunk-QQULYI2S.js.map +0 -1
  117. package/dist/chunk-TAEVA4QR.js.map +0 -1
  118. package/dist/chunk-TN2D2RX7.js.map +0 -1
  119. package/dist/chunk-VIQOQ463.js.map +0 -1
  120. package/dist/chunk-VVQQIG64.js.map +0 -1
  121. package/dist/chunk-WGUGB54H.js.map +0 -1
  122. package/dist/chunk-YUOVWWJX.js.map +0 -1
  123. package/dist/chunk-ZUIFO7B4.js.map +0 -1
  124. package/dist/cleanup-RJKLI47I.js.map +0 -1
  125. package/dist/database-helpers-PRDFNDRO.js +0 -11
  126. package/dist/dev-server-ZNTLWOL5.js.map +0 -1
  127. package/dist/open-KUO35JIJ.js.map +0 -1
  128. package/dist/plan-7CF56OIR.js.map +0 -1
  129. package/dist/run-RGZHCQ6M.js.map +0 -1
  130. /package/dist/{ClaudeContextManager-RRGREEZQ.js.map → ClaudeContextManager-ZH6LEA5I.js.map} +0 -0
  131. /package/dist/{ClaudeService-LEPW6QAC.js.map → ClaudeService-YR66WXZN.js.map} +0 -0
  132. /package/dist/{IssueTrackerFactory-KE2BDCLC.js.map → IssueTrackerFactory-O2ZBA666.js.map} +0 -0
  133. /package/dist/{LoomLauncher-GKQMR5E6.js.map → LoomLauncher-V54ENBEF.js.map} +0 -0
  134. /package/dist/{MetadataManager-V4LSJ2PB.js.map → MetadataManager-HHE6LQF2.js.map} +0 -0
  135. /package/dist/{PromptTemplateManager-I75WKXM4.js.map → PromptTemplateManager-4RFELNYY.js.map} +0 -0
  136. /package/dist/{SettingsManager-KQU7OX7G.js.map → SettingsManager-SLSYEYDZ.js.map} +0 -0
  137. /package/dist/{build-V3KADFMO.js.map → build-ZTGWDHWU.js.map} +0 -0
  138. /package/dist/{chunk-RFCAPHL5.js.map → chunk-C2BVNJW5.js.map} +0 -0
  139. /package/dist/{chunk-3XEXT35Z.js.map → chunk-E5OM25WK.js.map} +0 -0
  140. /package/dist/{chunk-NUUFP53X.js.map → chunk-GPBX2BY2.js.map} +0 -0
  141. /package/dist/{chunk-SN4S5CWL.js.map → chunk-GQDVH6FA.js.map} +0 -0
  142. /package/dist/{chunk-KQSV7FOG.js.map → chunk-J5JOJPK3.js.map} +0 -0
  143. /package/dist/{chunk-QNPJXO53.js.map → chunk-KGOBNC5A.js.map} +0 -0
  144. /package/dist/{chunk-H3T3EPF3.js.map → chunk-LNY2Y32V.js.map} +0 -0
  145. /package/dist/{chunk-QED2WB2D.js.map → chunk-PS6K2AOV.js.map} +0 -0
  146. /package/dist/{chunk-SA446KA2.js.map → chunk-T4KFKKEB.js.map} +0 -0
  147. /package/dist/{chunk-XCP2WDYA.js.map → chunk-T4NESGYB.js.map} +0 -0
  148. /package/dist/{chunk-QXGM32TO.js.map → chunk-TJDKGKQV.js.map} +0 -0
  149. /package/dist/{chunk-X5DRLONY.js.map → chunk-UXBVDD7U.js.map} +0 -0
  150. /package/dist/{chunk-JDN4SPV3.js.map → chunk-WYDLOQYO.js.map} +0 -0
  151. /package/dist/{chunk-NTDY5AMO.js.map → chunk-ZEFTWM5Z.js.map} +0 -0
  152. /package/dist/{commit-SUHRUMDE.js.map → commit-L5JNBU4U.js.map} +0 -0
  153. /package/dist/{compile-2MD346PO.js.map → compile-GPJOHXH4.js.map} +0 -0
  154. /package/dist/{contribute-P4BMRY7C.js.map → contribute-QEGCI4PS.js.map} +0 -0
  155. /package/dist/{feedback-Q6WG2WX4.js.map → feedback-2LWXKLQZ.js.map} +0 -0
  156. /package/dist/{database-helpers-PRDFNDRO.js.map → git-IS7AV3ED.js.map} +0 -0
  157. /package/dist/{git-TX2IEMB3.js.map → ignite-VQDJQ37S.js.map} +0 -0
  158. /package/dist/{init-5HFY7JG6.js.map → init-7SDJUAEZ.js.map} +0 -0
  159. /package/dist/{install-deps-J4ALTM27.js.map → install-deps-NGSFDNUW.js.map} +0 -0
  160. /package/dist/{issues-LZMIF22U.js.map → issues-4HQKEUP7.js.map} +0 -0
  161. /package/dist/{lint-XIXKU22H.js.map → lint-C5FOVRXY.js.map} +0 -0
  162. /package/dist/{ignite-P644W2PK.js.map → neon-helpers-LCZAN4U4.js.map} +0 -0
  163. /package/dist/{projects-L5AHUBGA.js.map → projects-3F6T3KZL.js.map} +0 -0
  164. /package/dist/{rebase-MAMWPA2L.js.map → rebase-MLIN572O.js.map} +0 -0
  165. /package/dist/{recap-IDBO3KM5.js.map → recap-CKGKFDJL.js.map} +0 -0
  166. /package/dist/{shell-7ADCDFIV.js.map → shell-M2YYPNGV.js.map} +0 -0
  167. /package/dist/{summary-7J2HORFD.js.map → summary-XR4CBJEG.js.map} +0 -0
  168. /package/dist/{test-SRB7EWU6.js.map → test-ESDAHEVE.js.map} +0 -0
  169. /package/dist/{test-git-G7ATVIXG.js.map → test-git-KWPLHYSI.js.map} +0 -0
  170. /package/dist/{test-jira-Q2HPA522.js.map → test-jira-6NK7UHSV.js.map} +0 -0
  171. /package/dist/{test-prefix-JMDGXR5A.js.map → test-prefix-VVODGHXP.js.map} +0 -0
  172. /package/dist/{test-webserver-GZFVXBGD.js.map → test-webserver-AHXKC6H4.js.map} +0 -0
  173. /package/dist/{vscode-3I7ISHUU.js.map → vscode-OY7HOVRO.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/providers/NeonProvider.ts","../src/lib/providers/SupabaseProvider.ts","../src/utils/database-helpers.ts"],"sourcesContent":["import { execa, type ExecaError } from 'execa'\nimport type { DatabaseProvider } from '../../types/index.js'\nimport { getLogger } from '../../utils/logger-context.js'\nimport { promptConfirmation } from '../../utils/prompt.js'\n\ninterface NeonBranch {\n name: string\n id: string\n [key: string]: unknown\n}\n\nexport interface NeonConfig {\n projectId: string\n parentBranch: string\n}\n\n/**\n * Validate Neon configuration\n * Checks that required configuration values are present\n */\nexport function validateNeonConfig(config: {\n projectId?: string\n parentBranch?: string\n}): { valid: boolean; error?: string } {\n if (!config.projectId) {\n return {\n valid: false,\n error: 'Neon projectId is required. Configure in .iloom/settings.json under databaseProviders.neon',\n }\n }\n\n if (!config.parentBranch) {\n return {\n valid: false,\n error: 'Neon parentBranch is required. Configure in .iloom/settings.json under databaseProviders.neon',\n }\n }\n\n // Basic validation for project ID format (should start with appropriate prefix)\n if (!/^[a-zA-Z0-9-]+$/.test(config.projectId)) {\n return {\n valid: false,\n error: 'Neon projectId contains invalid characters',\n }\n }\n\n return { valid: true }\n}\n\n/**\n * Neon database provider implementation\n * Ports functionality from bash/utils/neon-utils.sh\n */\nexport class NeonProvider implements DatabaseProvider {\n readonly displayName = 'Neon'\n readonly installHint = 'npm install -g neonctl'\n private _isConfigured: boolean = false\n\n constructor(private config: NeonConfig) {\n getLogger().debug('NeonProvider initialized with config:', {\n projectId: config.projectId,\n parentBranch: config.parentBranch,\n hasProjectId: !!config.projectId,\n hasParentBranch: !!config.parentBranch,\n })\n\n // Validate config but don't throw - just mark as not configured\n // This allows the provider to be instantiated even when Neon is not being used\n const validation = validateNeonConfig(config)\n if (!validation.valid) {\n getLogger().debug(`NeonProvider not configured: ${validation.error}`)\n getLogger().debug('Neon database branching will not be used')\n this._isConfigured = false\n } else {\n this._isConfigured = true\n }\n }\n\n /**\n * Check if provider is properly configured\n * Returns true if projectId and parentBranch are valid in settings\n */\n isConfigured(): boolean {\n return this._isConfigured\n }\n\n /**\n * Execute a Neon CLI command and return stdout\n * Throws an error if the command fails\n *\n * @param args - Command arguments to pass to neon CLI\n * @param cwd - Optional working directory to run the command from (defaults to current directory)\n */\n private async executeNeonCommand(args: string[], cwd?: string): Promise<string> {\n // Check if provider is properly configured\n if (!this._isConfigured) {\n throw new Error('NeonProvider is not configured. Check databaseProviders.neon configuration in .iloom/settings.json')\n }\n\n // Log the exact command being executed for debugging\n const command = `neon ${args.join(' ')}`\n getLogger().debug(`Executing Neon CLI command: ${command}`)\n getLogger().debug(`Project ID being used: ${this.config.projectId}`)\n if (cwd) {\n getLogger().debug(`Working directory: ${cwd}`)\n }\n\n const result = await execa('neon', args, {\n timeout: 30000,\n encoding: 'utf8',\n stdio: 'pipe',\n ...(cwd && { cwd }),\n })\n return result.stdout\n }\n\n /**\n * Check if neon CLI is available\n * Ports: check_neon_cli() from bash/utils/neon-utils.sh:18-23\n */\n async isCliAvailable(): Promise<boolean> {\n try {\n await execa('command', ['-v', 'neon'], {\n timeout: 5000,\n shell: true,\n })\n return true\n } catch {\n return false\n }\n }\n\n /**\n * Check if user is authenticated with Neon CLI\n * Ports: check_neon_auth() from bash/utils/neon-utils.sh:25-36\n *\n * @param cwd - Optional working directory to run the command from (prevents issues with deleted directories)\n * @throws Error if authentication check fails for reasons other than not being authenticated\n */\n async isAuthenticated(cwd?: string): Promise<boolean> {\n const cliAvailable = await this.isCliAvailable()\n if (!cliAvailable) {\n return false\n }\n\n try {\n await execa('neon', ['me'], {\n timeout: 10000,\n stdio: 'pipe',\n ...(cwd && { cwd }),\n })\n return true\n } catch (error) {\n const execaError = error as ExecaError\n const stderr = execaError.stderr?.trim() ?? ''\n\n // Check for authentication failure patterns (should return false, not throw)\n const isAuthError =\n stderr.toLowerCase().includes('not authenticated') ||\n stderr.toLowerCase().includes('not logged in') ||\n stderr.toLowerCase().includes('authentication required') ||\n stderr.toLowerCase().includes('login required')\n\n if (isAuthError) {\n return false\n }\n\n // For any other error, let it bubble up\n throw error\n }\n }\n\n /**\n * Sanitize branch name for Neon (replace slashes with underscores)\n * Ports: sanitize_neon_branch_name() from bash/utils/neon-utils.sh:11-15\n */\n sanitizeBranchName(branchName: string): string {\n return branchName.replace(/\\//g, '_')\n }\n\n /**\n * Extract endpoint ID from Neon connection string\n * Pattern matches: ep-abc-123 or ep-abc-123-pooler\n * Returns: ep-abc-123 (without -pooler suffix)\n * Used by: get_neon_branch_name() from bash/utils/neon-utils.sh:294\n */\n private extractEndpointId(connectionString: string): string | null {\n // First, extract the full host part between @ and first dot\n // Examples:\n // @ep-abc123.us-east-1.neon.tech -> ep-abc123\n // @ep-abc123-pooler.us-east-1.neon.tech -> ep-abc123-pooler\n const hostMatch = connectionString.match(/@(ep-[a-z0-9-]+)\\./)\n if (!hostMatch?.[1]) {\n return null\n }\n\n const fullEndpoint = hostMatch[1]\n // Remove -pooler suffix if present\n return fullEndpoint.replace(/-pooler$/, '')\n }\n\n /**\n * List all branches in the Neon project\n * Ports: list_neon_branches() from bash/utils/neon-utils.sh:63-74\n *\n * @param cwd - Optional working directory to run commands from\n */\n async listBranches(cwd?: string): Promise<string[]> {\n const output = await this.executeNeonCommand([\n 'branches',\n 'list',\n '--project-id',\n this.config.projectId,\n '--output',\n 'json',\n ], cwd)\n\n const branches: NeonBranch[] = JSON.parse(output)\n return branches.map(branch => branch.name)\n }\n\n /**\n * Check if a branch exists\n * Ports: check_neon_branch_exists() from bash/utils/neon-utils.sh:38-61\n *\n * @param name - Branch name to check\n * @param cwd - Optional working directory to run commands from\n */\n async branchExists(name: string, cwd?: string): Promise<boolean> {\n const branches = await this.listBranches(cwd)\n return branches.includes(name)\n }\n\n /**\n * Get connection string for a specific branch\n * Ports: get_neon_connection_string() from bash/utils/neon-utils.sh:76-90\n *\n * @param branch - Branch name to get connection string for\n * @param cwd - Optional working directory to run commands from\n */\n async getConnectionString(branch: string, cwd?: string): Promise<string> {\n const connectionString = await this.executeNeonCommand([\n 'connection-string',\n '--branch',\n branch,\n '--project-id',\n this.config.projectId,\n ], cwd)\n return connectionString.trim()\n }\n\n /**\n * Find Vercel preview database branch\n * Checks for both patterns: preview/<branch> and preview_<sanitized-branch>\n * Ports: find_preview_database_branch() from bash/utils/neon-utils.sh:92-124\n *\n * @param branchName - Branch name to find preview for\n * @param cwd - Optional working directory to run commands from\n */\n async findPreviewBranch(branchName: string, cwd?: string): Promise<string | null> {\n // Check for exact preview branch match with slash pattern\n const slashPattern = `preview/${branchName}`\n if (await this.branchExists(slashPattern, cwd)) {\n getLogger().info(`Found Vercel preview database: ${slashPattern}`)\n return slashPattern\n }\n\n // Check for underscore pattern variation\n const sanitized = this.sanitizeBranchName(branchName)\n const underscorePattern = `preview_${sanitized}`\n if (await this.branchExists(underscorePattern, cwd)) {\n getLogger().info(`Found Vercel preview database: ${underscorePattern}`)\n return underscorePattern\n }\n\n return null\n }\n\n /**\n * Remove expiration date from a Neon branch\n * Used when parent branches need to have child branches created\n * Neon limitation: \"Branches with an expiration date cannot have child branches\"\n *\n * @param branchName - Name of the branch to remove expiration from\n * @param cwd - Optional working directory to run commands from\n */\n private async removeExpiration(branchName: string, cwd?: string): Promise<void> {\n getLogger().info(`Removing expiration date from branch: ${branchName}`)\n await this.executeNeonCommand([\n 'branches',\n 'set-expiration',\n branchName,\n '--project-id',\n this.config.projectId,\n ], cwd)\n getLogger().success(`Expiration date removed from ${branchName}`)\n }\n\n /**\n * Create a new database branch\n * ALWAYS checks for Vercel preview database first\n * Handles Neon limitation where parent branches with expiration dates cannot have children\n * Returns connection string for the branch\n * Ports: create_neon_database_branch() from bash/utils/neon-utils.sh:126-187\n *\n * @param name - Name for the new branch\n * @param fromBranch - Parent branch to create from (defaults to config.parentBranch)\n * @param cwd - Optional working directory to run commands from\n */\n async createBranch(name: string, fromBranch?: string, cwd?: string): Promise<string> {\n // Always check for existing Vercel preview database first (lines 149-158)\n const previewBranch = await this.findPreviewBranch(name, cwd)\n if (previewBranch) {\n const connectionString = await this.getConnectionString(previewBranch, cwd)\n getLogger().success(`Using existing Vercel preview database: ${previewBranch}`)\n return connectionString\n }\n\n // Sanitize branch name for Neon (replace slashes with underscores)\n const sanitizedName = this.sanitizeBranchName(name)\n const parentBranch = fromBranch ?? this.config.parentBranch\n\n getLogger().info('Creating Neon database branch...')\n getLogger().info(` Parent branch: ${parentBranch}`)\n getLogger().info(` New branch: ${sanitizedName}`)\n\n try {\n // Create the database branch\n await this.executeNeonCommand([\n 'branches',\n 'create',\n '--name',\n sanitizedName,\n '--parent',\n parentBranch,\n '--project-id',\n this.config.projectId,\n ], cwd)\n } catch (error) {\n // Check if error is about parent branch having an expiration date\n const errorMessage = error instanceof Error ? error.message : String(error)\n if (errorMessage.toLowerCase().includes('expiration') &&\n errorMessage.toLowerCase().includes('child')) {\n getLogger().warn('Parent branch has an expiration date - removing it to allow child branch creation')\n\n // Remove expiration from parent branch\n await this.removeExpiration(parentBranch, cwd)\n\n // Retry branch creation\n getLogger().info('Retrying database branch creation...')\n await this.executeNeonCommand([\n 'branches',\n 'create',\n '--name',\n sanitizedName,\n '--parent',\n parentBranch,\n '--project-id',\n this.config.projectId,\n ], cwd)\n } else {\n // Re-throw if it's a different error\n throw error\n }\n }\n\n getLogger().success('Database branch created successfully')\n\n // Get the connection string for the new branch\n getLogger().info('Getting connection string for new database branch...')\n const connectionString = await this.getConnectionString(sanitizedName, cwd)\n\n return connectionString\n }\n\n /**\n * Delete a database branch\n * Includes preview database protection with user confirmation\n * Ports: delete_neon_database_branch() from bash/utils/neon-utils.sh:204-259\n *\n * @param name - Name of the branch to delete\n * @param isPreview - Whether this is a preview database branch\n * @param cwd - Optional working directory to run commands from (prevents issues with deleted directories)\n */\n async deleteBranch(name: string, isPreview: boolean = false, cwd?: string): Promise<import('../../types/index.js').DatabaseDeletionResult> {\n // Sanitize branch name for Neon\n const sanitizedName = this.sanitizeBranchName(name)\n\n // For preview contexts, check for preview databases first\n if (isPreview) {\n const previewBranch = await this.findPreviewBranch(name, cwd)\n if (previewBranch) {\n getLogger().warn(`Found Vercel preview database: ${previewBranch}`)\n getLogger().warn('Preview databases are managed by Vercel and will be cleaned up automatically')\n getLogger().warn('Manual deletion may interfere with Vercel\\'s preview deployments')\n\n const confirmed = await promptConfirmation(\n 'Delete preview database anyway?',\n false\n )\n\n if (confirmed) {\n // User confirmed - delete preview branch\n try {\n getLogger().info(`Deleting Vercel preview database: ${previewBranch}`)\n await this.executeNeonCommand([\n 'branches',\n 'delete',\n previewBranch,\n '--project-id',\n this.config.projectId,\n ], cwd)\n getLogger().success('Preview database deleted successfully')\n return {\n success: true,\n deleted: true,\n notFound: false,\n branchName: previewBranch\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n getLogger().error(`Failed to delete preview database: ${errorMessage}`)\n return {\n success: false,\n deleted: false,\n notFound: false,\n error: errorMessage,\n branchName: previewBranch\n }\n }\n } else {\n // User declined deletion\n getLogger().info('Skipping preview database deletion')\n return {\n success: true,\n deleted: false,\n notFound: false,\n userDeclined: true,\n branchName: previewBranch\n }\n }\n }\n // If no preview database found, fall through to check regular branch\n }\n\n // Check for regular branch\n getLogger().info(`Checking for Neon database branch: ${sanitizedName}`)\n\n try {\n const exists = await this.branchExists(sanitizedName, cwd)\n\n if (!exists) {\n getLogger().info(`No database branch found for '${name}'`)\n return {\n success: true,\n deleted: false,\n notFound: true,\n branchName: sanitizedName\n }\n }\n\n // Branch exists - delete it\n getLogger().info(`Deleting Neon database branch: ${sanitizedName}`)\n await this.executeNeonCommand([\n 'branches',\n 'delete',\n sanitizedName,\n '--project-id',\n this.config.projectId,\n ], cwd)\n getLogger().success('Database branch deleted successfully')\n\n return {\n success: true,\n deleted: true,\n notFound: false,\n branchName: sanitizedName\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n getLogger().error(`Failed to delete database branch: ${errorMessage}`)\n return {\n success: false,\n deleted: false,\n notFound: false,\n error: errorMessage,\n branchName: sanitizedName\n }\n }\n }\n\n /**\n * Get branch name from endpoint ID (reverse lookup)\n * Searches all branches to find one with matching endpoint\n * Ports: get_neon_branch_name() from bash/utils/neon-utils.sh:262-308\n *\n * @param endpointId - Endpoint ID to search for\n * @param cwd - Optional working directory to run commands from\n */\n async getBranchNameFromEndpoint(endpointId: string, cwd?: string): Promise<string | null> {\n const branches = await this.listBranches(cwd)\n\n for (const branch of branches) {\n try {\n const connectionString = await this.getConnectionString(branch, cwd)\n const branchEndpointId = this.extractEndpointId(connectionString)\n\n if (branchEndpointId === endpointId) {\n return branch\n }\n } catch {\n // Skip branches that fail to get connection string\n continue\n }\n }\n\n return null\n }\n\n /**\n * Get branch name from a connection string (reverse lookup)\n * Extracts endpoint ID from connection string and finds matching branch\n *\n * @param connectionString - Neon connection string (e.g., postgres://...@ep-abc-123.region.neon.tech/...)\n * @param cwd - Optional working directory to run commands from\n */\n async getBranchNameFromConnectionString(connectionString: string, cwd?: string): Promise<string | null> {\n const endpointId = this.extractEndpointId(connectionString)\n if (!endpointId) {\n getLogger().debug('Could not extract endpoint ID from connection string')\n return null\n }\n return this.getBranchNameFromEndpoint(endpointId, cwd)\n }\n}\n","import { execa, type ExecaError } from 'execa'\nimport type { DatabaseProvider, DatabaseDeletionResult } from '../../types/index.js'\nimport { getLogger } from '../../utils/logger-context.js'\n\nexport interface SupabaseConfig {\n projectRef: string\n parentBranch?: string\n withData?: boolean // default: true\n}\n\n/**\n * Validate Supabase configuration\n * Checks that required configuration values are present\n */\nexport function validateSupabaseConfig(config: {\n projectRef?: string\n parentBranch?: string\n}): { valid: boolean; error?: string } {\n if (!config.projectRef) {\n return {\n valid: false,\n error:\n 'Supabase projectRef is required. Configure in .iloom/settings.json under databaseProviders.supabase',\n }\n }\n\n // parentBranch is optional — Supabase currently always branches from the default branch\n\n // Basic validation for project ref format (alphanumeric and hyphens)\n if (!/^[a-zA-Z0-9-]+$/.test(config.projectRef)) {\n return {\n valid: false,\n error: 'Supabase projectRef contains invalid characters',\n }\n }\n\n return { valid: true }\n}\n\n/**\n * Supabase database provider implementation\n * Provides database branching via the Supabase CLI\n */\nexport class SupabaseProvider implements DatabaseProvider {\n private _isConfigured: boolean = false\n\n readonly displayName = 'Supabase CLI'\n readonly installHint = 'Install with: npm install -g supabase'\n\n constructor(private config: SupabaseConfig) {\n getLogger().debug('SupabaseProvider initialized with config:', {\n projectRef: config.projectRef,\n parentBranch: config.parentBranch,\n withData: config.withData,\n hasProjectRef: !!config.projectRef,\n hasParentBranch: !!config.parentBranch,\n })\n\n // Validate config but don't throw - just mark as not configured\n // This allows the provider to be instantiated even when Supabase is not being used\n const validation = validateSupabaseConfig(config)\n if (!validation.valid) {\n getLogger().debug(`SupabaseProvider not configured: ${validation.error}`)\n getLogger().debug('Supabase database branching will not be used')\n this._isConfigured = false\n } else {\n this._isConfigured = true\n }\n\n if (config.parentBranch) {\n getLogger().debug(\n `parentBranch '${config.parentBranch}' is stored but Supabase currently always branches from the default branch`\n )\n }\n }\n\n /**\n * Check if provider is properly configured\n * Returns true if projectRef and parentBranch are valid in settings\n */\n isConfigured(): boolean {\n return this._isConfigured\n }\n\n /**\n * Execute a Supabase CLI command and return stdout\n * Throws an error if the command fails\n *\n * @param args - Command arguments to pass to supabase CLI\n * @param cwd - Optional working directory to run the command from (defaults to current directory)\n */\n private async executeSupabaseCommand(args: string[], cwd?: string, timeout: number = 30000): Promise<string> {\n // Check if provider is properly configured\n if (!this._isConfigured) {\n throw new Error(\n 'SupabaseProvider is not configured. Check databaseProviders.supabase configuration in .iloom/settings.json'\n )\n }\n\n // Log the exact command being executed for debugging\n const command = `supabase ${args.join(' ')}`\n getLogger().debug(`Executing Supabase CLI command: ${command}`)\n getLogger().debug(`Project ref being used: ${this.config.projectRef}`)\n if (cwd) {\n getLogger().debug(`Working directory: ${cwd}`)\n }\n\n const result = await execa('supabase', args, {\n timeout,\n encoding: 'utf8',\n stdio: 'pipe',\n ...(cwd && { cwd }),\n })\n return result.stdout\n }\n\n /**\n * Check if supabase CLI is available\n */\n async isCliAvailable(): Promise<boolean> {\n try {\n await execa('supabase', ['--version'], {\n timeout: 5000,\n stdio: 'pipe',\n })\n return true\n } catch (error) {\n const errorCode = (error as NodeJS.ErrnoException).code\n // ENOENT means the binary was not found on the system\n // EACCES means the binary exists but has no execute permission\n if (errorCode === 'ENOENT' || errorCode === 'EACCES') {\n return false\n }\n // Any other error (e.g., non-zero exit) still means CLI is present\n return true\n }\n }\n\n /**\n * Check if user is authenticated with Supabase CLI\n *\n * @param cwd - Optional working directory to run the command from (prevents issues with deleted directories)\n * @throws Error if authentication check fails for reasons other than not being authenticated\n */\n async isAuthenticated(cwd?: string): Promise<boolean> {\n const cliAvailable = await this.isCliAvailable()\n if (!cliAvailable) {\n return false\n }\n\n try {\n await execa('supabase', ['projects', 'list'], {\n timeout: 10000,\n stdio: 'pipe',\n ...(cwd && { cwd }),\n })\n return true\n } catch (error) {\n const execaError = error as ExecaError\n const stderr = execaError.stderr?.trim() ?? ''\n\n // Check for authentication failure patterns (should return false, not throw)\n const isAuthError =\n stderr.toLowerCase().includes('not authenticated') ||\n stderr.toLowerCase().includes('not logged in') ||\n stderr.toLowerCase().includes('authentication required') ||\n stderr.toLowerCase().includes('login required') ||\n stderr.toLowerCase().includes('access token not provided') ||\n stderr.toLowerCase().includes('you need to be logged in')\n\n if (isAuthError) {\n return false\n }\n\n // For any other error, let it bubble up\n throw error\n }\n }\n\n /**\n * Sanitize branch name for Supabase (replace slashes with hyphens)\n * Supabase uses hyphens as separator (not underscores like Neon)\n */\n sanitizeBranchName(branchName: string): string {\n let sanitized = branchName\n .replace(/\\//g, '-') // replace slashes with hyphens\n .replace(/[^a-zA-Z0-9_-]/g, '') // remove chars that aren't alphanumeric, hyphens, or underscores\n .replace(/^-+/, '') // strip leading hyphens (prevents CLI flag injection)\n return sanitized || 'unnamed-branch'\n }\n\n /**\n * List all branches in the Supabase project\n *\n * @param cwd - Optional working directory to run commands from\n */\n async listBranches(cwd?: string): Promise<string[]> {\n const output = await this.executeSupabaseCommand(\n ['branches', 'list', '--project-ref', this.config.projectRef, '-o', 'json'],\n cwd\n )\n\n interface SupabaseBranch {\n name: string\n [key: string]: unknown\n }\n\n let jsonString = output\n // CLI tools can prepend warnings to stdout; strip non-JSON prefixes\n const firstBracket = output.indexOf('[')\n if (firstBracket > 0) {\n jsonString = output.slice(firstBracket)\n }\n\n let branches: SupabaseBranch[]\n try {\n branches = JSON.parse(jsonString)\n } catch (parseError) {\n throw new Error(\n `Failed to parse Supabase branch list as JSON: ${parseError instanceof Error ? parseError.message : String(parseError)}`\n )\n }\n return branches.map((branch) => branch.name)\n }\n\n /**\n * Check if a branch exists\n * Uses `supabase branches get` for a direct lookup (more efficient than listing all)\n *\n * @param name - Branch name to check\n * @param cwd - Optional working directory to run commands from\n */\n async branchExists(name: string, cwd?: string): Promise<boolean> {\n const sanitizedName = this.sanitizeBranchName(name)\n try {\n await this.executeSupabaseCommand(\n ['branches', 'get', sanitizedName, '--project-ref', this.config.projectRef],\n cwd\n )\n return true\n } catch (error) {\n const execaError = error as ExecaError\n const stderr = execaError.stderr?.toLowerCase() ?? ''\n const stdout = execaError.stdout?.toLowerCase() ?? ''\n const message = (error instanceof Error ? error.message : String(error)).toLowerCase()\n\n // Only return false for explicit \"not found\" error signatures\n // Note: Supabase CLI uses exitCode=1 for \"not found\" and exitCode=2 for auth errors\n const isNotFound =\n stderr.includes('not found') ||\n stderr.includes('does not exist') ||\n stderr.includes('no branch') ||\n stdout.includes('not found') ||\n message.includes('not found') ||\n message.includes('does not exist')\n\n if (isNotFound) {\n return false\n }\n\n // For any other error (auth, network, CLI unavailable), rethrow\n throw error\n }\n }\n\n /**\n * Get connection string for a specific branch\n * Parses POSTGRES_URL_NON_POOLING from `supabase branches get <name> -o env` output\n * Connection strings are never logged at info level or above (security)\n *\n * @param branch - Branch name to get connection string for\n * @param cwd - Optional working directory to run commands from\n */\n async getConnectionString(branch: string, cwd?: string): Promise<string> {\n const sanitizedBranch = this.sanitizeBranchName(branch)\n const output = await this.executeSupabaseCommand(\n ['branches', 'get', sanitizedBranch, '--project-ref', this.config.projectRef, '-o', 'env'],\n cwd\n )\n\n // Parse POSTGRES_URL_NON_POOLING from env output\n const match = output.match(/^POSTGRES_URL_NON_POOLING=(.+)$/m)\n if (!match?.[1]) {\n throw new Error(\n `Could not find POSTGRES_URL_NON_POOLING in branch '${branch}' environment output`\n )\n }\n\n const connectionString = match[1].trim()\n // Log only at debug level - never at info level or above (security)\n getLogger().debug(`Connection string retrieved for branch '${branch}'`)\n return connectionString\n }\n\n /**\n * Create a new database branch\n * Returns connection string for the branch\n *\n * Note: Supabase preview branches always branch from the production database.\n * The fromBranch parameter is accepted for interface compatibility but ignored.\n *\n * @param name - Name for the new branch\n * @param fromBranch - Accepted for interface compatibility but ignored (Supabase always branches from production)\n * @param cwd - Optional working directory to run commands from\n */\n async createBranch(name: string, fromBranch?: string, cwd?: string): Promise<string> {\n void fromBranch // accepted for interface compatibility but ignored - Supabase always branches from production\n\n const sanitizedName = this.sanitizeBranchName(name)\n\n getLogger().info('Creating Supabase database branch...')\n getLogger().info(` New branch: ${sanitizedName}`)\n\n const args = [\n 'branches',\n 'create',\n sanitizedName,\n '--project-ref',\n this.config.projectRef,\n ]\n\n // Add --with-data flag when withData is true (default: true per acceptance criteria)\n if (this.config.withData !== false) {\n args.push('--with-data')\n }\n\n await this.executeSupabaseCommand(args, cwd, 300000)\n\n getLogger().success('Database branch created successfully')\n\n // Get the connection string for the new branch\n getLogger().info('Getting connection string for new database branch...')\n const connectionString = await this.getConnectionString(sanitizedName, cwd)\n\n return connectionString\n }\n\n /**\n * Delete a database branch\n *\n * @param name - Name of the branch to delete\n * @param isPreview - Accepted but ignored (Neon-specific concept for Vercel preview databases)\n * @param cwd - Optional working directory to run commands from (prevents issues with deleted directories)\n */\n async deleteBranch(\n name: string,\n isPreview: boolean = false,\n cwd?: string\n ): Promise<DatabaseDeletionResult> {\n void isPreview // accepted but ignored - Neon-specific concept\n\n const sanitizedName = this.sanitizeBranchName(name)\n\n getLogger().info(`Checking for Supabase database branch: ${sanitizedName}`)\n\n try {\n const exists = await this.branchExists(sanitizedName, cwd)\n\n if (!exists) {\n getLogger().info(`No database branch found for '${name}'`)\n return {\n success: true,\n deleted: false,\n notFound: true,\n branchName: sanitizedName,\n }\n }\n\n // Branch exists - delete it\n getLogger().info(`Deleting Supabase database branch: ${sanitizedName}`)\n await this.executeSupabaseCommand(\n ['branches', 'delete', sanitizedName, '--project-ref', this.config.projectRef],\n cwd\n )\n getLogger().success('Database branch deleted successfully')\n\n return {\n success: true,\n deleted: true,\n notFound: false,\n branchName: sanitizedName,\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n getLogger().error(`Failed to delete database branch: ${errorMessage}`)\n return {\n success: false,\n deleted: false,\n notFound: false,\n error: errorMessage,\n branchName: sanitizedName,\n }\n }\n }\n\n}\n","import { NeonProvider } from '../lib/providers/NeonProvider.js'\nimport { SupabaseProvider } from '../lib/providers/SupabaseProvider.js'\nimport type { IloomSettings } from '../lib/SettingsManager.js'\nimport type { DatabaseProvider } from '../types/index.js'\n\n/**\n * Create the appropriate database provider from iloom settings.\n *\n * - Returns a NeonProvider when databaseProviders.neon is configured\n * - Returns a SupabaseProvider when databaseProviders.supabase is configured\n * - Throws if both neon and supabase are configured simultaneously\n * - Returns an unconfigured NeonProvider (isConfigured() = false) when neither is configured\n */\nexport function createDatabaseProviderFromSettings(settings: IloomSettings): DatabaseProvider {\n\tconst neonConfig = settings.databaseProviders?.neon\n\tconst supabaseConfig = settings.databaseProviders?.supabase\n\n\tif (neonConfig && supabaseConfig) {\n\t\tthrow new Error(\n\t\t\t'Cannot configure both Neon and Supabase database providers simultaneously. ' +\n\t\t\t\t'Remove one from databaseProviders in .iloom/settings.json.',\n\t\t)\n\t}\n\n\tif (supabaseConfig) {\n\t\treturn new SupabaseProvider({\n\t\t\tprojectRef: supabaseConfig.projectRef,\n\t\t\t...(supabaseConfig.parentBranch && { parentBranch: supabaseConfig.parentBranch }),\n\t\t\t...(supabaseConfig.withData !== undefined && { withData: supabaseConfig.withData }),\n\t\t})\n\t}\n\n\treturn new NeonProvider({\n\t\tprojectId: neonConfig?.projectId ?? '',\n\t\tparentBranch: neonConfig?.parentBranch ?? '',\n\t})\n}\n"],"mappings":"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","names":["connectionString","execa","execa"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/port.ts"],"sourcesContent":["import { createHash } from 'crypto'\nimport path from 'path'\nimport fs from 'fs-extra'\nimport { parseEnvFile, extractPort, findEnvFileContainingVariable } from './env.js'\nimport { extractIssueNumber } from './git.js'\nimport { logger } from './logger.js'\n\n/**\n * Wrap a raw port that exceeds 65535 into the valid port range.\n * Uses modulo arithmetic to wrap back into [basePort+1, 65535].\n *\n * @param rawPort - The calculated port (basePort + issueNumber)\n * @param basePort - The base port (default: 3000)\n * @returns Port in valid range [basePort+1, 65535]\n */\nexport function wrapPort(rawPort: number, basePort: number): number {\n\tif (rawPort <= 65535) return rawPort\n\tconst range = 65535 - basePort\n\treturn ((rawPort - basePort - 1) % range) + basePort + 1\n}\n\n/**\n * Extract numeric suffix from alphanumeric issue ID (e.g., MARK-324 -> 324)\n * @returns The numeric part or null if no trailing number found\n */\nexport function extractNumericSuffix(issueId: string): number | null {\n\t// Match trailing digits after optional separator (-, _)\n\tconst match = issueId.match(/[-_]?(\\d+)$/)\n\tconst digits = match?.[1]\n\tif (digits === undefined) return null\n\treturn parseInt(digits, 10)\n}\n\n/**\n * Generate deterministic port offset from branch name using SHA256 hash\n * Range: 1-999 (matches existing random range for branches)\n *\n * @param branchName - Branch name to generate port offset from\n * @returns Port offset in range [1, 999]\n * @throws Error if branchName is empty\n */\nexport function generatePortOffsetFromBranchName(branchName: string): number {\n\t// Validate input\n\tif (!branchName || branchName.trim().length === 0) {\n\t\tthrow new Error('Branch name cannot be empty')\n\t}\n\n\t// Generate SHA256 hash of branch name (same pattern as color.ts)\n\tconst hash = createHash('sha256').update(branchName).digest('hex')\n\n\t// Take first 8 hex characters and convert to port offset (1-999)\n\tconst hashPrefix = hash.slice(0, 8)\n\tconst hashAsInt = parseInt(hashPrefix, 16)\n\tconst portOffset = (hashAsInt % 999) + 1 // +1 ensures range is 1-999, not 0-998\n\n\treturn portOffset\n}\n\n/**\n * Calculate deterministic port for branch-based workspace\n *\n * @param branchName - Branch name\n * @param basePort - Base port (default: 3000)\n * @returns Port number\n * @throws Error if branchName is empty\n */\n// SYNC: If this default changes, update displayDefaultsBox() in src/utils/first-run-setup.ts\nexport function calculatePortForBranch(branchName: string, basePort: number = 3000): number {\n\tconst offset = generatePortOffsetFromBranchName(branchName)\n\tconst port = basePort + offset\n\n\t// Use wrap-around for port overflow\n\treturn wrapPort(port, basePort)\n}\n\n/**\n * Calculate port from an identifier (issue number, PR number, or string).\n * This is the single source of truth for port calculation logic.\n *\n * Algorithm:\n * 1. Numeric identifiers: basePort + number (with wrapPort for overflow)\n * 2. String numeric (e.g., \"42\"): parse and same as above\n * 3. Alphanumeric with suffix (e.g., \"MARK-324\"): extract suffix and same as above\n * 4. Pure strings without numeric suffix: hash-based calculation via calculatePortForBranch\n *\n * @param identifier - The identifier (issue number, PR number, or string)\n * @param basePort - Base port (default: 3000)\n * @returns Port number in valid range\n */\n// SYNC: If this default changes, update displayDefaultsBox() in src/utils/first-run-setup.ts\nexport function calculatePortFromIdentifier(\n\tidentifier: string | number,\n\tbasePort: number = 3000\n): number {\n\t// Handle numeric identifiers directly\n\tif (typeof identifier === 'number') {\n\t\treturn wrapPort(basePort + identifier, basePort)\n\t}\n\n\t// Handle string identifiers\n\t// First, try to parse as pure numeric string\n\tconst numericValue = parseInt(identifier, 10)\n\tif (!isNaN(numericValue) && String(numericValue) === identifier) {\n\t\treturn wrapPort(basePort + numericValue, basePort)\n\t}\n\n\t// Try extracting numeric suffix from alphanumeric identifiers (e.g., MARK-324 -> 324)\n\tconst numericSuffix = extractNumericSuffix(identifier)\n\tif (numericSuffix !== null) {\n\t\treturn wrapPort(basePort + numericSuffix, basePort)\n\t}\n\n\t// For non-numeric strings without numeric suffix, use hash-based calculation\n\treturn calculatePortForBranch(`issue-${identifier}`, basePort)\n}\n\nexport interface GetWorkspacePortOptions {\n\tbasePort?: number | undefined\n\tworktreePath: string\n\tworktreeBranch: string\n\t/** If true, check .env files for PORT override before calculating. Defaults to false. */\n\tcheckEnvFile?: boolean\n}\n\nexport interface GetWorkspacePortDependencies {\n\tfileExists?: (path: string) => Promise<boolean>\n\treadFile?: (path: string) => Promise<string>\n\tlistWorktrees?: () => Promise<Array<{ path: string; branch: string }>>\n}\n\n/**\n * Get port for workspace - calculates based on workspace type, optionally checking .env files first.\n * Consolidates logic previously duplicated across dev-server, run, open commands.\n *\n * Priority (when checkEnvFile is true):\n * 1. Read PORT from dotenv-flow files (if present)\n * 2. Calculate from PR pattern (_pr_N suffix in directory name)\n * 3. Calculate from issue pattern (issue-N or alphanumeric like MARK-324)\n * 4. Calculate from branch name using deterministic hash\n *\n * When checkEnvFile is false (default), skips step 1 and only calculates.\n */\nexport async function getWorkspacePort(\n\toptions: GetWorkspacePortOptions,\n\tdependencies?: GetWorkspacePortDependencies\n): Promise<number> {\n\tconst basePort = options.basePort ?? 3000\n\tconst checkEnvFile = options.checkEnvFile ?? false\n\n\t// Only check .env files if explicitly requested\n\tif (checkEnvFile) {\n\t\tconst deps = {\n\t\t\tfileExists:\n\t\t\t\tdependencies?.fileExists ?? ((p: string): Promise<boolean> => fs.pathExists(p)),\n\t\t\treadFile:\n\t\t\t\tdependencies?.readFile ?? ((p: string): Promise<string> => fs.readFile(p, 'utf8')),\n\t\t}\n\n\t\t// Try to read PORT from any dotenv-flow file (as override)\n\t\tconst envFile = await findEnvFileContainingVariable(\n\t\t\toptions.worktreePath,\n\t\t\t'PORT',\n\t\t\tasync (p) => deps.fileExists(p),\n\t\t\tasync (p, varName) => {\n\t\t\t\tconst content = await deps.readFile(p)\n\t\t\t\tconst envMap = parseEnvFile(content)\n\t\t\t\treturn envMap.get(varName) ?? null\n\t\t\t}\n\t\t)\n\n\t\tif (envFile) {\n\t\t\tconst envPath = path.join(options.worktreePath, envFile)\n\t\t\tconst envContent = await deps.readFile(envPath)\n\t\t\tconst envMap = parseEnvFile(envContent)\n\t\t\tconst port = extractPort(envMap)\n\n\t\t\tif (port) {\n\t\t\t\tlogger.debug(`Using PORT from ${envFile}: ${port}`)\n\t\t\t\treturn port\n\t\t\t}\n\t\t}\n\n\t\tlogger.debug('PORT not found in any dotenv-flow file, calculating from workspace identifier')\n\t}\n\n\t// Calculate based on workspace identifier\n\n\t// Extract identifier from worktree path/branch\n\tconst dirName = path.basename(options.worktreePath)\n\n\t// Check for PR pattern: _pr_N\n\tconst prPattern = /_pr_(\\d+)$/\n\tconst prMatch = dirName.match(prPattern)\n\tif (prMatch?.[1]) {\n\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\tconst port = calculatePortFromIdentifier(prNumber, basePort)\n\t\tlogger.debug(`Calculated PORT for PR #${prNumber}: ${port}`)\n\t\treturn port\n\t}\n\n\t// Check for issue pattern: issue-N or alphanumeric like MARK-324\n\tconst issueId = extractIssueNumber(dirName) ?? extractIssueNumber(options.worktreeBranch)\n\tif (issueId !== null) {\n\t\tconst port = calculatePortFromIdentifier(issueId, basePort)\n\t\tlogger.debug(`Calculated PORT for issue ${issueId}: ${port}`)\n\t\treturn port\n\t}\n\n\t// Branch-based workspace - use deterministic hash\n\tconst port = calculatePortForBranch(options.worktreeBranch, basePort)\n\tlogger.debug(`Calculated PORT for branch \"${options.worktreeBranch}\": ${port}`)\n\treturn port\n}\n"],"mappings":"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","names":["port"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/cleanup.ts"],"sourcesContent":["import { getLogger } from '../utils/logger-context.js'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { ResourceCleanup } from '../lib/ResourceCleanup.js'\nimport { ProcessManager } from '../lib/process/ProcessManager.js'\nimport { DatabaseManager } from '../lib/DatabaseManager.js'\nimport { EnvironmentManager } from '../lib/EnvironmentManager.js'\nimport { CLIIsolationManager } from '../lib/CLIIsolationManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { promptConfirmation } from '../utils/prompt.js'\nimport { IdentifierParser } from '../utils/IdentifierParser.js'\nimport { loadEnvIntoProcess } from '../utils/env.js'\nimport { createDatabaseProviderFromSettings } from '../utils/database-helpers.js'\nimport { LoomManager } from '../lib/LoomManager.js'\nimport { TelemetryService } from '../lib/TelemetryService.js'\nimport { MetadataManager } from '../lib/MetadataManager.js'\nimport type { LoomMetadata } from '../lib/MetadataManager.js'\n\nfunction trackLoomAbandoned(metadata: LoomMetadata): void {\n\ttry {\n\t\tconst durationMinutes = metadata.created_at\n\t\t\t? Math.round((Date.now() - new Date(metadata.created_at).getTime()) / 60000)\n\t\t\t: 0\n\t\tTelemetryService.getInstance().track('loom.abandoned', {\n\t\t\tduration_minutes: isNaN(durationMinutes) ? 0 : durationMinutes,\n\t\t\tphase_reached: metadata.state ?? 'unknown',\n\t\t})\n\t} catch (error: unknown) {\n\t\tgetLogger().debug(`Failed to track loom.abandoned telemetry: ${error instanceof Error ? error.message : String(error)}`)\n\t}\n}\nimport type { CleanupOptions } from '../types/index.js'\nimport type { CleanupResult } from '../types/cleanup.js'\nimport type { ParsedInput } from './start.js'\n\n/**\n * Input structure for CleanupCommand.execute()\n */\nexport interface CleanupCommandInput {\n identifier?: string\n options: CleanupOptions\n}\n\n/**\n * Parsed and validated cleanup command input\n * Mode determines which cleanup operation to perform\n */\nexport interface ParsedCleanupInput {\n mode: 'list' | 'single' | 'issue' | 'all'\n identifier?: string\n issueNumber?: string | number\n branchName?: string\n originalInput?: string\n options: CleanupOptions\n}\n\n/**\n * Manages cleanup command execution with option parsing and validation\n * Follows the command pattern established by StartCommand\n *\n * This implementation handles ONLY parsing, validation, and mode determination.\n * Actual cleanup operations are deferred to subsequent sub-issues.\n */\nexport class CleanupCommand {\n private readonly gitWorktreeManager: GitWorktreeManager\n private resourceCleanup?: ResourceCleanup\n private loomManager?: import('../lib/LoomManager.js').LoomManager\n private readonly identifierParser: IdentifierParser\n\n constructor(\n gitWorktreeManager?: GitWorktreeManager,\n resourceCleanup?: ResourceCleanup\n ) {\n // Load environment variables first\n const envResult = loadEnvIntoProcess()\n if (envResult.error) {\n getLogger().debug(`Environment loading warning: ${envResult.error.message}`)\n }\n if (envResult.parsed) {\n getLogger().debug(`Loaded ${Object.keys(envResult.parsed).length} environment variables`)\n }\n\n this.gitWorktreeManager = gitWorktreeManager ?? new GitWorktreeManager()\n\n // Initialize ResourceCleanup with DatabaseManager and CLIIsolationManager\n // ResourceCleanup will be initialized lazily with proper configuration\n if (resourceCleanup) {\n this.resourceCleanup = resourceCleanup\n }\n\n // Initialize IdentifierParser for pattern-based detection\n this.identifierParser = new IdentifierParser(this.gitWorktreeManager)\n }\n\n /**\n * Lazy initialization of ResourceCleanup and LoomManager with properly configured DatabaseManager\n */\n private async ensureResourceCleanup(): Promise<void> {\n if (this.resourceCleanup && this.loomManager) {\n return\n }\n\n const settingsManager = new SettingsManager()\n const settings = await settingsManager.loadSettings()\n const databaseUrlEnvVarName = settings.capabilities?.database?.databaseUrlEnvVarName ?? 'DATABASE_URL'\n\n const environmentManager = new EnvironmentManager()\n const databaseProvider = createDatabaseProviderFromSettings(settings)\n const databaseManager = new DatabaseManager(databaseProvider, environmentManager, databaseUrlEnvVarName)\n const cliIsolationManager = new CLIIsolationManager()\n\n this.resourceCleanup ??= new ResourceCleanup(\n this.gitWorktreeManager,\n new ProcessManager(),\n databaseManager,\n cliIsolationManager\n )\n\n // Initialize LoomManager if not provided (for child loom detection)\n if (!this.loomManager) {\n const { IssueTrackerFactory } = await import('../lib/IssueTrackerFactory.js')\n const { ClaudeContextManager } = await import('../lib/ClaudeContextManager.js')\n const { ProjectCapabilityDetector } = await import('../lib/ProjectCapabilityDetector.js')\n const { DefaultBranchNamingService } = await import('../lib/BranchNamingService.js')\n\n this.loomManager = new LoomManager(\n this.gitWorktreeManager,\n IssueTrackerFactory.create(settings),\n new DefaultBranchNamingService({ useClaude: true }),\n environmentManager,\n new ClaudeContextManager(),\n new ProjectCapabilityDetector(),\n cliIsolationManager,\n settingsManager,\n databaseManager\n )\n }\n }\n\n /**\n * Check for child looms and exit gracefully if any exist\n * Always checks the TARGET loom (the one being cleaned up), not the current directory's loom\n *\n * @param parsed - The parsed input identifying the loom being cleaned up\n */\n private async checkForChildLooms(parsed: ParsedCleanupInput): Promise<void> {\n await this.ensureResourceCleanup()\n if (!this.loomManager) {\n throw new Error('Failed to initialize LoomManager')\n }\n\n // Determine which branch is being cleaned up based on parsed input\n let targetBranch: string | undefined\n\n if (parsed.branchName) {\n targetBranch = parsed.branchName\n } else if (parsed.mode === 'issue' && parsed.issueNumber !== undefined) {\n // For issues, try to find the worktree by issue number to get the branch name\n const worktree = await this.gitWorktreeManager.findWorktreeForIssue(parsed.issueNumber)\n targetBranch = worktree?.branch\n }\n\n // If we can't determine the target branch, skip the check\n if (!targetBranch) {\n getLogger().debug(`Cannot determine target branch for child loom check`)\n return\n }\n\n // Check if the TARGET loom has any child looms\n const hasChildLooms = await this.loomManager.checkAndWarnChildLooms(targetBranch)\n if (hasChildLooms) {\n throw new Error('Cannot cleanup loom while child looms exist. Please \\'finish\\' or \\'cleanup\\' child looms first.')\n }\n }\n\n /**\n * Main entry point for the cleanup command\n * Parses input, validates options, and determines operation mode\n */\n public async execute(input: CleanupCommandInput): Promise<CleanupResult | void> {\n // Step 1: Parse input and determine mode\n const parsed = this.parseInput(input)\n\n // Step 2: Validate option combinations (fail fast before any delay)\n this.validateInput(parsed)\n\n // Note: JSON mode auto-skips routine confirmations (programmatic use can't interact)\n // Safety checks still require --force to bypass (ResourceCleanup.validateWorktreeSafety throws errors)\n\n // Step 3: Check for child looms AFTER parsing input\n // This ensures we only block when cleaning the CURRENT loom (parent), not a child\n await this.checkForChildLooms(parsed)\n\n // Step 4: Handle deferred execution (after all validation passes)\n if (input.options.defer) {\n getLogger().info(`Waiting ${input.options.defer}ms before cleanup...`)\n await new Promise(resolve => globalThis.setTimeout(resolve, input.options.defer))\n }\n\n // Step 5: Execute based on mode\n getLogger().info(`Cleanup mode: ${parsed.mode}`)\n\n if (parsed.mode === 'single') {\n return await this.executeSingleCleanup(parsed)\n } else if (parsed.mode === 'list') {\n getLogger().info('Would list all worktrees') // TODO: Implement in Sub-issue #2\n getLogger().success('Command parsing and validation successful')\n return {\n identifier: 'list',\n success: true,\n dryRun: parsed.options.dryRun ?? false,\n operations: [],\n errors: [],\n rollbackRequired: false,\n }\n } else if (parsed.mode === 'all') {\n getLogger().info('Would remove all worktrees') // TODO: Implement in Sub-issue #5\n getLogger().success('Command parsing and validation successful')\n return {\n identifier: 'all',\n success: true,\n dryRun: parsed.options.dryRun ?? false,\n operations: [],\n errors: [],\n rollbackRequired: false,\n }\n } else if (parsed.mode === 'issue') {\n return await this.executeIssueCleanup(parsed)\n }\n }\n\n /**\n * Parse input to determine cleanup mode and extract relevant data\n * Implements auto-detection: numeric input = issue number, non-numeric = branch name\n *\n * @private\n */\n private parseInput(input: CleanupCommandInput): ParsedCleanupInput {\n const { identifier, options } = input\n\n // Trim identifier if present\n const trimmedIdentifier = identifier?.trim() ?? undefined\n\n // Mode: List (takes priority - it's informational only)\n if (options.list) {\n const result: ParsedCleanupInput = {\n mode: 'list',\n options\n }\n if (trimmedIdentifier) {\n result.identifier = trimmedIdentifier\n }\n return result\n }\n\n // Mode: All (remove everything)\n if (options.all) {\n const result: ParsedCleanupInput = {\n mode: 'all',\n options\n }\n if (trimmedIdentifier) {\n result.identifier = trimmedIdentifier\n }\n if (options.issue !== undefined) {\n result.issueNumber = options.issue\n }\n return result\n }\n\n // Mode: Explicit issue number via --issue flag\n if (options.issue !== undefined) {\n // Need to determine if identifier is branch or numeric to set branchName\n if (trimmedIdentifier) {\n const numericPattern = /^[0-9]+$/\n if (!numericPattern.test(trimmedIdentifier)) {\n // Identifier is a branch name with explicit --issue flag\n return {\n mode: 'issue',\n issueNumber: options.issue,\n branchName: trimmedIdentifier,\n identifier: trimmedIdentifier,\n originalInput: trimmedIdentifier,\n options\n }\n }\n }\n const result: ParsedCleanupInput = {\n mode: 'issue',\n issueNumber: options.issue,\n options\n }\n if (trimmedIdentifier) {\n result.identifier = trimmedIdentifier\n }\n return result\n }\n\n // Mode: Auto-detect from identifier\n if (!trimmedIdentifier) {\n throw new Error('Missing required argument: identifier. Use --all to remove all worktrees or --list to list them.')\n }\n\n // Auto-detection: Check if identifier is purely numeric\n // Pattern from bash script line 364: ^[0-9]+$\n const numericPattern = /^[0-9]+$/\n if (numericPattern.test(trimmedIdentifier)) {\n // Numeric input = issue number\n return {\n mode: 'issue',\n issueNumber: parseInt(trimmedIdentifier, 10),\n identifier: trimmedIdentifier,\n originalInput: trimmedIdentifier,\n options\n }\n } else {\n // Non-numeric = branch name\n return {\n mode: 'single',\n branchName: trimmedIdentifier,\n identifier: trimmedIdentifier,\n originalInput: trimmedIdentifier,\n options\n }\n }\n }\n\n /**\n * Validate parsed input for option conflicts\n * Throws descriptive errors for invalid option combinations\n *\n * @private\n */\n private validateInput(parsed: ParsedCleanupInput): void {\n const { mode, options, branchName } = parsed\n\n // Conflict: --list is informational only, incompatible with destructive operations\n if (mode === 'list') {\n if (options.all) {\n throw new Error('Cannot use --list with --all (list is informational only)')\n }\n if (options.issue !== undefined) {\n throw new Error('Cannot use --list with --issue (list is informational only)')\n }\n if (parsed.identifier) {\n throw new Error('Cannot use --list with a specific identifier (list shows all worktrees)')\n }\n }\n\n // Conflict: --all removes everything, can't combine with specific identifier or --issue\n if (mode === 'all') {\n if (parsed.identifier) {\n throw new Error('Cannot use --all with a specific identifier. Use one or the other.')\n }\n if (parsed.issueNumber !== undefined) {\n throw new Error('Cannot use --all with a specific identifier. Use one or the other.')\n }\n }\n\n // Conflict: explicit --issue flag with branch name identifier\n // (This prevents confusion when user provides both)\n if (options.issue !== undefined && branchName) {\n throw new Error('Cannot use --issue flag with branch name identifier. Use numeric identifier or --issue flag alone.')\n }\n\n // Note: --force and --dry-run are compatible with all modes (no conflicts)\n }\n\n /**\n * Execute cleanup for single worktree\n * Implements two-stage confirmation: worktree removal, then branch deletion\n * Uses IdentifierParser for pattern-based detection without GitHub API calls\n */\n private async executeSingleCleanup(parsed: ParsedCleanupInput): Promise<CleanupResult> {\n const identifier = parsed.branchName ?? parsed.identifier ?? ''\n if (!identifier) {\n throw new Error('No identifier found for cleanup')\n }\n const { force, dryRun } = parsed.options\n\n // Step 1: Parse identifier using pattern-based detection\n let parsedInput: ParsedInput = await this.identifierParser.parseForPatternDetection(identifier)\n\n // If type is 'branch', try to extract issue number for CLI symlink cleanup\n if (parsedInput.type === 'branch' && parsedInput.branchName) {\n const { extractIssueNumber } = await import('../utils/git.js')\n const extractedNumber = extractIssueNumber(parsedInput.branchName)\n if (extractedNumber !== null) {\n parsedInput = {\n ...parsedInput,\n number: extractedNumber // Add number for CLI symlink cleanup\n }\n }\n }\n\n // Step 2: Display worktree details\n getLogger().info(`Preparing to cleanup worktree: ${identifier}`)\n\n // Step 3: Routine confirmation - worktree removal\n // Skip if --force (user explicitly bypasses) or --json (programmatic use can't interact)\n // Note: Safety checks (uncommitted changes, unmerged work) still require --force to bypass\n if (!force && !parsed.options.json) {\n const confirmWorktree = await promptConfirmation('Remove this worktree?', true)\n if (!confirmWorktree) {\n getLogger().info('Cleanup cancelled')\n return {\n identifier,\n success: false,\n dryRun: dryRun ?? false,\n operations: [],\n errors: [],\n rollbackRequired: false,\n }\n }\n }\n\n // Step 3.5: Read metadata BEFORE cleanup (cleanup deletes the worktree)\n let preCleanupMetadata: LoomMetadata | null = null\n try {\n // Find worktree path for metadata lookup\n const worktree = parsedInput.type === 'branch' && parsedInput.branchName\n ? await this.gitWorktreeManager.findWorktreeForBranch(parsedInput.branchName)\n : parsedInput.number !== undefined\n ? await this.gitWorktreeManager.findWorktreeForIssue(parsedInput.number)\n : null\n if (worktree) {\n const metadataManager = new MetadataManager()\n preCleanupMetadata = await metadataManager.readMetadata(worktree.path)\n }\n } catch (error: unknown) {\n getLogger().debug(`Failed to read metadata for telemetry: ${error instanceof Error ? error.message : String(error)}`)\n }\n\n // Step 4: Execute worktree cleanup (includes safety validation)\n // Issue #275 fix: Run 5-point safety check BEFORE any deletion\n // This prevents the scenario where worktree is deleted but branch deletion fails\n await this.ensureResourceCleanup()\n if (!this.resourceCleanup) {\n throw new Error('Failed to initialize ResourceCleanup')\n }\n const cleanupResult = await this.resourceCleanup.cleanupWorktree(parsedInput, {\n dryRun: dryRun ?? false,\n force: force ?? false,\n deleteBranch: true, // Always include branch deletion (safety checks run first)\n keepDatabase: false,\n checkMergeSafety: true, // Run 5-point safety check BEFORE any deletion\n archive: parsed.options.archive ?? false,\n })\n\n // Add dryRun flag to result\n cleanupResult.dryRun = dryRun ?? false\n\n // Step 5: Report cleanup results\n this.reportCleanupResults(cleanupResult)\n\n // Track loom.abandoned telemetry event (only for unfinished looms)\n if (cleanupResult.success && preCleanupMetadata && preCleanupMetadata.status !== 'finished') {\n trackLoomAbandoned(preCleanupMetadata)\n }\n\n // Final success message\n if (cleanupResult.success) {\n getLogger().success('Cleanup completed successfully')\n } else {\n getLogger().warn('Cleanup completed with errors - see details above')\n }\n\n return cleanupResult\n }\n\n /**\n * Report cleanup operation results to user\n */\n private reportCleanupResults(result: CleanupResult): void {\n getLogger().info('Cleanup operations:')\n\n result.operations.forEach(op => {\n const status = op.success ? '✓' : '✗'\n const message = op.error ? `${op.message}: ${op.error}` : op.message\n\n if (op.success) {\n getLogger().info(` ${status} ${message}`)\n } else {\n getLogger().error(` ${status} ${message}`)\n }\n })\n\n if (result.errors.length > 0) {\n getLogger().warn(`${result.errors.length} error(s) occurred during cleanup`)\n }\n }\n\n /**\n * Execute cleanup for all worktrees associated with an issue or PR number\n * Searches for worktrees by their path patterns (e.g., issue-25, pr-25, 25-feature, _pr_25)\n * Implements bash cleanup-worktree.sh remove_worktrees_by_issue() (lines 157-242)\n */\n private async executeIssueCleanup(parsed: ParsedCleanupInput): Promise<CleanupResult> {\n const issueNumber = parsed.issueNumber\n if (issueNumber === undefined) {\n throw new Error('No issue/PR number provided for cleanup')\n }\n\n const { force, dryRun } = parsed.options\n\n getLogger().info(`Finding worktrees related to issue/PR #${issueNumber}...`)\n\n // Step 1: Get all worktrees and filter by path pattern\n const worktrees = await this.gitWorktreeManager.listWorktrees()\n const matchingWorktrees = worktrees.filter(wt => {\n const path = wt.path.toLowerCase()\n // Lowercase for case-insensitive matching (Linear IDs are uppercase like MARK-1)\n const idStr = String(issueNumber).toLowerCase()\n\n // Check if path contains the identifier with proper word boundaries\n // Matches: issue-25, pr-25, 25-feature, _pr_25, issue-mark-1, etc.\n // Uses word boundary or common separators (-, _, /) for alphanumeric IDs\n const pattern = new RegExp(`(?:^|[/_-])${idStr}(?:[/_-]|$)`)\n return pattern.test(path)\n })\n\n if (matchingWorktrees.length === 0) {\n getLogger().warn(`No worktrees found for issue/PR #${issueNumber}`)\n getLogger().info(`Searched for worktree paths containing: ${issueNumber}, _pr_${issueNumber}, issue-${issueNumber}, etc.`)\n return {\n identifier: String(issueNumber),\n success: true,\n dryRun: dryRun ?? false,\n operations: [],\n errors: [],\n rollbackRequired: false,\n }\n }\n\n // Step 2: Build targets list from matching worktrees\n const targets: Array<{ branchName: string; hasWorktree: boolean; worktreePath?: string }> =\n matchingWorktrees.map(wt => ({\n branchName: wt.branch,\n hasWorktree: true,\n worktreePath: wt.path\n }))\n\n // Step 3: Display preview\n getLogger().info(`Found ${targets.length} worktree(s) related to issue/PR #${issueNumber}:`)\n for (const target of targets) {\n getLogger().info(` Branch: ${target.branchName} (${target.worktreePath})`)\n }\n\n // Step 4: Routine batch confirmation\n // Skip if --force (user explicitly bypasses) or --json (programmatic use can't interact)\n // Note: Safety checks per-worktree (uncommitted changes, unmerged work) still require --force to bypass\n if (!force && !parsed.options.json) {\n const confirmCleanup = await promptConfirmation(\n `Remove ${targets.length} worktree(s)?`,\n true\n )\n if (!confirmCleanup) {\n getLogger().info('Cleanup cancelled')\n return {\n identifier: String(issueNumber),\n success: false,\n dryRun: dryRun ?? false,\n operations: [],\n errors: [],\n rollbackRequired: false,\n }\n }\n }\n\n // Step 5: Process each target sequentially\n let worktreesRemoved = 0\n let branchesDeleted = 0\n const databaseBranchesDeletedList: string[] = []\n let failed = 0\n\n for (const target of targets) {\n getLogger().info(`Processing worktree: ${target.branchName}`)\n\n // Cleanup worktree using ResourceCleanup with ParsedInput\n // Now includes branch deletion with 5-point safety check BEFORE any deletion\n try {\n // Read metadata BEFORE cleanup for telemetry\n let targetMetadata: LoomMetadata | null = null\n if (target.worktreePath) {\n try {\n const metadataManager = new MetadataManager()\n targetMetadata = await metadataManager.readMetadata(target.worktreePath)\n } catch (error: unknown) {\n getLogger().debug(`Failed to read metadata for telemetry: ${error instanceof Error ? error.message : String(error)}`)\n }\n }\n\n // Use the known issue number directly instead of parsing from branch name\n // This ensures CLI symlinks (created with issue number) are properly cleaned up\n const parsedInput: ParsedInput = {\n type: 'issue',\n number: issueNumber, // Use the known issue number, not parsed from branch\n branchName: target.branchName,\n originalInput: String(issueNumber)\n }\n\n await this.ensureResourceCleanup()\n if (!this.resourceCleanup) {\n throw new Error('Failed to initialize ResourceCleanup')\n }\n // Issue #275 fix: Run safety checks BEFORE deleting worktree\n // This prevents the scenario where worktree is deleted but branch deletion fails\n const result = await this.resourceCleanup.cleanupWorktree(parsedInput, {\n dryRun: dryRun ?? false,\n force: force ?? false,\n deleteBranch: true, // Include branch deletion (with safety checks)\n keepDatabase: false,\n checkMergeSafety: true, // Run 5-point safety check BEFORE any deletion\n archive: parsed.options.archive ?? false,\n ...(target.worktreePath && { worktree: { path: target.worktreePath, branch: target.branchName } }),\n })\n\n if (result.success) {\n worktreesRemoved++\n getLogger().success(` Worktree removed: ${target.branchName}`)\n\n // Check if branch was deleted\n const branchOperation = result.operations.find(op => op.type === 'branch')\n if (branchOperation?.success) {\n branchesDeleted++\n getLogger().success(` Branch deleted: ${target.branchName}`)\n }\n\n // Check if database branch was actually deleted (use explicit deleted field)\n const dbOperation = result.operations.find(op => op.type === 'database')\n if (dbOperation?.deleted) {\n // Get branch name from result or use the target branch name\n const deletedBranchName = target.branchName\n databaseBranchesDeletedList.push(deletedBranchName)\n }\n\n // Track loom.abandoned telemetry (only for unfinished looms)\n if (targetMetadata && targetMetadata.status !== 'finished') {\n trackLoomAbandoned(targetMetadata)\n }\n } else {\n failed++\n getLogger().error(` Failed to remove worktree: ${target.branchName}`)\n for (const err of result.errors) {\n getLogger().error(` ${err.message}`)\n }\n }\n } catch (error) {\n failed++\n const errMsg = error instanceof Error ? error.message : 'Unknown error'\n getLogger().error(` Failed to cleanup: ${errMsg}`)\n continue // Continue with next worktree even if this one failed\n }\n }\n\n // Step 7: Report statistics\n getLogger().success(`Completed cleanup for issue/PR #${issueNumber}:`)\n getLogger().info(` Worktrees removed: ${worktreesRemoved}`)\n getLogger().info(` Branches deleted: ${branchesDeleted}`)\n if (databaseBranchesDeletedList.length > 0) {\n // Display branch names in the format requested\n getLogger().info(` Database branches deleted: ${databaseBranchesDeletedList.join(', ')}`)\n }\n if (failed > 0) {\n getLogger().warn(` Failed operations: ${failed}`)\n }\n\n // Return aggregated result\n return {\n identifier: String(issueNumber),\n success: failed === 0,\n dryRun: dryRun ?? false,\n operations: [\n { type: 'worktree' as const, success: true, message: `Removed ${worktreesRemoved} worktree(s)` },\n { type: 'branch' as const, success: true, message: `Deleted ${branchesDeleted} branch(es)` },\n ...(databaseBranchesDeletedList.length > 0 ? [{ type: 'database' as const, success: true, message: `Deleted ${databaseBranchesDeletedList.length} database branch(es)`, deleted: true }] : []),\n ],\n errors: [],\n rollbackRequired: false,\n }\n }\n}\n"],"mappings":"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","names":["numericPattern"]}
@@ -1,11 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- createDatabaseProviderFromSettings
4
- } from "./chunk-YUOVWWJX.js";
5
- import "./chunk-NPVA65KS.js";
6
- import "./chunk-FTYWGQFM.js";
7
- import "./chunk-VRPPI6GU.js";
8
- export {
9
- createDatabaseProviderFromSettings
10
- };
11
- //# sourceMappingURL=database-helpers-PRDFNDRO.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/dev-server.ts"],"sourcesContent":["import path from 'path'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { MetadataManager } from '../lib/MetadataManager.js'\nimport { ProjectCapabilityDetector } from '../lib/ProjectCapabilityDetector.js'\nimport { DevServerManager } from '../lib/DevServerManager.js'\nimport { DockerManager } from '../lib/DockerManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { IdentifierParser } from '../utils/IdentifierParser.js'\nimport { loadWorkspaceEnv, isNoEnvFilesFoundError } from '../utils/env.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 DevServerCommandInput {\n\tidentifier?: string | undefined\n\tjson?: boolean | undefined\n}\n\nexport interface DevServerResult {\n\tstatus: 'started' | 'already_running' | 'no_web_capability'\n\turl?: string\n\tport?: number\n\tpid?: number\n\tmessage: string\n}\n\ninterface ParsedDevServerInput {\n\ttype: 'issue' | 'pr' | 'branch' | 'epic'\n\tnumber?: string | number\n\tbranchName?: string\n\toriginalInput: string\n\tautoDetected: boolean\n}\n\n/**\n * DevServerCommand - Start dev server for workspace in foreground mode\n * Runs in foreground (blocking terminal until user stops it)\n */\nexport class DevServerCommand {\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\tprivate metadataManager = new MetadataManager()\n\t) {}\n\n\t/**\n\t * Output JSON to stdout (used for --json flag)\n\t */\n\tprivate outputJson(data: DevServerResult | Record<string, unknown>): void {\n\t\tprocess.stdout.write(JSON.stringify(data, null, 2) + '\\n')\n\t}\n\n\tasync execute(input: DevServerCommandInput): Promise<DevServerResult> {\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 and Docker config\n\t\tconst settings = await this.settingsManager.loadSettings()\n\t\tconst shouldLoadEnv = settings.sourceEnvOnStart ?? false\n\n\t\t// 3a. Extract Docker configuration if Docker mode is enabled\n\t\tconst identifier = parsed.number?.toString() ?? parsed.branchName ?? parsed.originalInput\n\t\tconst dockerConfig = DockerManager.buildDockerConfigFromSettings(\n\t\t\tsettings.capabilities?.web,\n\t\t\tidentifier\n\t\t)\n\n\t\tif (dockerConfig) {\n\t\t\tawait DockerManager.assertAvailable()\n\t\t\tconst { dockerFile, containerPort, identifier } = dockerConfig\n\t\t\tlogger.debug(`Docker mode enabled with config: ${JSON.stringify({ dockerFile, containerPort, identifier })}`)\n\t\t}\n\n\t\t// Build environment variables\n\t\tlet envOverrides: Record<string, string> = {}\n\n\t\tif (shouldLoadEnv) {\n\t\t\tconst envResult = loadWorkspaceEnv(worktree.path)\n\t\t\tif (envResult.parsed) {\n\t\t\t\tenvOverrides = envResult.parsed\n\t\t\t}\n\t\t\tif (envResult.error && !isNoEnvFilesFoundError(envResult.error)) {\n\t\t\t\tlogger.warn(`Failed to load env files: ${envResult.error.message}`)\n\t\t\t}\n\t\t}\n\n\t\t// 3b. Set ILOOM_LOOM for loom identification\n\t\tenvOverrides.ILOOM_LOOM = this.formatLoomIdentifier(parsed)\n\n\t\t// 3c. Set ILOOM_COLOR_HEX from loom metadata if available\n\t\tconst metadata = await this.metadataManager.readMetadata(worktree.path)\n\t\tif (metadata?.colorHex) {\n\t\t\tenvOverrides.ILOOM_COLOR_HEX = metadata.colorHex\n\t\t}\n\n\t\t// 4. Detect project capabilities\n\t\tconst { capabilities } =\n\t\t\tawait this.capabilityDetector.detectCapabilities(worktree.path)\n\n\t\tlogger.debug(`Detected capabilities: ${capabilities.join(', ')}`)\n\n\t\t// 4. If no web capability, return gracefully with info message\n\t\tif (!capabilities.includes('web')) {\n\t\t\tconst message = 'No web capability detected in this workspace. Dev server not started.'\n\t\t\tif (input.json) {\n\t\t\t\tthis.outputJson({\n\t\t\t\t\tstatus: 'no_web_capability',\n\t\t\t\t\tmessage,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tlogger.info(message)\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tstatus: 'no_web_capability',\n\t\t\t\tmessage,\n\t\t\t}\n\t\t}\n\n\t\t// 5. Get port for workspace\n\t\tconst cliOverrides = extractSettingsOverrides()\n\t\tconst settingsForPort = 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: settingsForPort.capabilities?.web?.basePort,\n\t\t\tcheckEnvFile: true,\n\t\t})\n\t\tconst url = `http://localhost:${port}`\n\n\t\t// 6. Check if server already running\n\t\tconst isRunning = await this.devServerManager.isServerRunning(port, dockerConfig)\n\n\t\tif (isRunning) {\n\t\t\tconst message = `Dev server already running at ${url}`\n\t\t\tif (input.json) {\n\t\t\t\tthis.outputJson({\n\t\t\t\t\tstatus: 'already_running',\n\t\t\t\t\turl,\n\t\t\t\t\tport,\n\t\t\t\t\tmessage,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tlogger.info(message)\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tstatus: 'already_running',\n\t\t\t\turl,\n\t\t\t\tport,\n\t\t\t\tmessage,\n\t\t\t}\n\t\t}\n\n\t\t// 7. Start server in foreground\n\t\tconst message = `Starting dev server at ${url}`\n\t\tif (!input.json) {\n\t\t\tlogger.info(message)\n\t\t}\n\n\t\tlet finalResult: DevServerResult = {\n\t\t\tstatus: 'started',\n\t\t\turl,\n\t\t\tport,\n\t\t\tmessage,\n\t\t}\n\n\t\t// This will block until user stops the server (Ctrl+C)\n\t\t// In JSON mode, redirect npm output to stderr so JSON can go to stdout\n\t\tconst processInfo = await this.devServerManager.runServerForeground(\n\t\t\tworktree.path,\n\t\t\tport,\n\t\t\t!!input.json,\n\t\t\t// Callback called immediately when process starts (for JSON output)\n\t\t\t(pid) => {\n\t\t\t\tif (input.json && pid) {\n\t\t\t\t\tfinalResult.pid = pid\n\t\t\t\t\tthis.outputJson(finalResult)\n\t\t\t\t}\n\t\t\t},\n\t\t\tenvOverrides,\n\t\t\tdockerConfig\n\t\t)\n\n\t\tif (processInfo.pid) {\n\t\t\tfinalResult.pid = processInfo.pid\n\t\t}\n\n\t\treturn finalResult\n\t}\n\n\t/**\n\t * Parse explicit identifier input\n\t */\n\tprivate async parseExplicitInput(identifier: string): Promise<ParsedDevServerInput> {\n\t\tconst parsed = await this.identifierParser.parseForPatternDetection(identifier)\n\n\t\t// Description type should never reach dev-server command\n\t\tif (parsed.type === 'description') {\n\t\t\tthrow new Error('Description input type is not supported in dev-server command')\n\t\t}\n\n\t\tconst result: ParsedDevServerInput = {\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<ParsedDevServerInput> {\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: ParsedDevServerInput): 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: ParsedDevServerInput): 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: ParsedDevServerInput): string {\n\t\treturn parsed.originalInput\n\t}\n}\n"],"mappings":"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","names":["identifier","message"]}
@@ -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 { DockerManager } from '../lib/DockerManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { IdentifierParser } from '../utils/IdentifierParser.js'\nimport { openBrowser } from '../utils/browser.js'\nimport { getWorkspacePort } from '../utils/port.js'\nimport { extractIssueNumber } from '../utils/git.js'\nimport { 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' | 'epic'\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// Extract Docker configuration if Docker mode is enabled\n\t\tconst issueNumber = extractIssueNumber(worktree.branch)\n\t\tconst dockerIdentifier = issueNumber?.toString() ?? worktree.branch\n\t\tconst dockerConfig = DockerManager.buildDockerConfigFromSettings(\n\t\t\tsettings.capabilities?.web,\n\t\t\tdockerIdentifier\n\t\t)\n\n\t\tif (dockerConfig) {\n\t\t\tawait DockerManager.assertAvailable()\n\t\t\tconst { dockerFile, containerPort, identifier } = dockerConfig\n\t\t\tlogger.debug(`Docker mode enabled with config: ${JSON.stringify({ dockerFile, containerPort, identifier })}`)\n\t\t}\n\n\t\t// Ensure dev server is running on the port\n\t\tconst serverReady = await this.devServerManager.ensureServerRunning(\n\t\t\tworktree.path,\n\t\t\tport,\n\t\t\tdockerConfig\n\t\t)\n\n\t\tif (!serverReady) {\n\t\t\tlogger.warn(\n\t\t\t\t`Dev server failed to start on port ${port}. Opening browser anyway...`\n\t\t\t)\n\t\t}\n\n\t\t// Construct URL and open browser\n\t\tconst 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","../src/lib/HarnessServer.ts"],"sourcesContent":["/* global AbortController, setImmediate */\nimport { 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, generateHarnessMcpConfig } from '../utils/mcp.js'\nimport { HarnessServer } from '../lib/HarnessServer.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'\nimport { TelemetryService } from '../lib/TelemetryService.js'\nimport { StartCommand } from './start.js'\nimport { IgniteCommand } from './ignite.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\tautoSwarm?: boolean\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, autoSwarm))\n\t\t}\n\n\t\treturn this.executeInternal(prompt, model, yolo, planner, reviewer, printOptions, autoSwarm)\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\tautoSwarm?: boolean\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// - Project key pattern: ENG-123, PROJ-456 (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, settings ?? undefined)\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// --- Auto-swarm harness lifecycle ---\n\t\tlet harness: HarnessServer | null = null\n\t\tlet externalHarness = false\n\t\tlet epicData: { epicIssueNumber: string; childIssues: number[] } | null = null\n\t\tconst controller = autoSwarm ? new AbortController() : null\n\t\tconst autoSwarmStartTime = autoSwarm ? Date.now() : null\n\t\tlet autoSwarmSuccess = false\n\t\tlet autoSwarmPhaseReached: 'plan' | 'start' | 'spin' = 'plan'\n\t\tlet autoSwarmFallbackToNormal = false\n\n\t\tif (autoSwarm) {\n\t\t\tconst autoSwarmSource = decompositionContext ? 'decomposition' : 'fresh'\n\t\t\ttry {\n\t\t\t\tTelemetryService.getInstance().track('auto_swarm.started', {\n\t\t\t\t\tsource: autoSwarmSource,\n\t\t\t\t\tplanner: effectivePlanner,\n\t\t\t\t})\n\t\t\t} catch (error) {\n\t\t\t\tlogger.debug(`Telemetry auto_swarm.started tracking failed: ${error instanceof Error ? error.message : error}`)\n\t\t\t}\n\n\t\t\t// 1. Force yolo mode\n\t\t\tyolo = true\n\n\t\t\t// 2. Check for external harness (e.g., VS Code extension provides its own socket)\n\t\t\tconst externalSocket = process.env.ILOOM_HARNESS_SOCKET\n\t\t\texternalHarness = !!externalSocket\n\n\t\t\tif (!externalSocket) {\n\t\t\t\t// 3. Create and start harness server\n\t\t\t\tharness = new HarnessServer()\n\t\t\t\tawait harness.start()\n\t\t\t}\n\n\t\t\tconst socketPath = externalSocket ?? harness?.path\n\t\t\tif (!socketPath) {\n\t\t\t\tthrow new Error('Unexpected: no harness socket path available')\n\t\t\t}\n\n\t\t\t// 4. Register \"done\" handler (only when we own the harness server)\n\t\t\tif (harness) {\n\t\t\t\tharness.registerHandler('done', (data) => {\n\t\t\t\t\tepicData = data as typeof epicData\n\t\t\t\t\tsetImmediate(() => { controller?.abort() })\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttype: 'instruction' as const,\n\t\t\t\t\t\tcontent: 'Planning complete. The auto-swarm pipeline will now create the epic workspace and launch swarm mode automatically.',\n\t\t\t\t\t}\n\t\t\t\t}, { idempotent: true })\n\t\t\t}\n\n\t\t\t// 5. Merge harness MCP config\n\t\t\tconst harnessMcpConfig = generateHarnessMcpConfig(socketPath)\n\t\t\tmcpConfig = [...mcpConfig, ...harnessMcpConfig]\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// Get wave verification setting (default true)\n\t\tconst waveVerification = settingsManager.getPlanWaveVerification(settings ?? undefined)\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\tWAVE_VERIFICATION: waveVerification,\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\tAUTO_SWARM_MODE: autoSwarm ?? false,\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\tif (autoSwarm) {\n\t\t\tallowedTools.push('mcp__harness__signal')\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\ttry {\n\t\t\tconst claudeResult = await launchClaude(initialMessage, {\n\t\t\t\t...claudeOptions,\n\t\t\t\t...(effectiveYolo && { permissionMode: 'bypassPermissions' as const }),\n\t\t\t\t...(controller && { signal: controller.signal }),\n\t\t\t})\n\n\t\t\t// Check auto-swarm outcome\n\t\t\tif (autoSwarm) {\n\t\t\t\t// When an external harness (e.g., VS Code) owns the socket, it handles\n\t\t\t\t// the \"done\" signal and manages the start/spin pipeline itself.\n\t\t\t\t// The CLI just exits cleanly after the plan phase.\n\t\t\t\tif (externalHarness) {\n\t\t\t\t\tlogger.info(chalk.green('Planning session ended. External harness will manage the pipeline.'))\n\t\t\t\t\tautoSwarmSuccess = true\n\t\t\t\t\tautoSwarmPhaseReached = 'plan'\n\t\t\t\t} else if (!epicData) {\n\t\t\t\t\tthrow new Error('Plan phase exited without completing. The Architect did not signal done.')\n\t\t\t\t} else {\n\t\t\t\t\t// Cast required because TypeScript cannot narrow let variables mutated in closures.\n\t\t\t\t\t// Defensively default childIssues — the data comes from AI-generated signal payloads.\n\t\t\t\t\tconst resolvedEpicData = epicData as { epicIssueNumber: string; childIssues?: number[] }\n\t\t\t\t\tconst epicIssueNumber = resolvedEpicData.epicIssueNumber\n\t\t\t\t\tconst childIssues = resolvedEpicData.childIssues ?? []\n\t\t\t\t\tlogger.info(chalk.green(`Planning complete. Epic issue: #${epicIssueNumber}`))\n\t\t\t\t\tautoSwarmFallbackToNormal = childIssues.length === 0\n\n\t\t\t\t\tconst startCommand = new StartCommand(IssueTrackerFactory.create(settings ?? {}))\n\n\t\t\t\t\tif (childIssues.length === 0) {\n\t\t\t\t\t\t// Zero-children fallback: normal (non-epic) autonomous loom\n\t\t\t\t\t\tlogger.info('No child issues created. Starting as a normal autonomous loom.')\n\t\t\t\t\t\tlet startResult\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tstartResult = await startCommand.execute({\n\t\t\t\t\t\t\t\tidentifier: String(epicIssueNumber),\n\t\t\t\t\t\t\t\toptions: { oneShot: 'bypassPermissions', json: true, claude: false, code: false, devServer: false, terminal: false },\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t} catch (startError) {\n\t\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t\t`Auto-swarm: failed to create epic workspace. ${startError instanceof Error ? startError.message : String(startError)}`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst epicWorktreePath = startResult?.path\n\t\t\t\t\t\tif (!epicWorktreePath) {\n\t\t\t\t\t\t\tthrow new Error('Auto-swarm: StartCommand did not return a workspace path.')\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst igniteCommand = new IgniteCommand()\n\t\t\t\t\t\tawait igniteCommand.execute('bypassPermissions', undefined, undefined, epicWorktreePath)\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Epic mode: start + spin with swarm\n\t\t\t\t\t\tlet startResult\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tstartResult = await startCommand.execute({\n\t\t\t\t\t\t\t\tidentifier: String(epicIssueNumber),\n\t\t\t\t\t\t\t\toptions: { epic: true, json: true, oneShot: 'bypassPermissions', claude: false, code: false, devServer: false, terminal: false },\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t} catch (startError) {\n\t\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t\t`Auto-swarm: failed to create epic workspace. ${startError instanceof Error ? startError.message : String(startError)}`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst epicWorktreePath = startResult?.path\n\t\t\t\t\t\tif (!epicWorktreePath) {\n\t\t\t\t\t\t\tthrow new Error('Auto-swarm: StartCommand did not return a workspace path.')\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst igniteCommand = new IgniteCommand()\n\t\t\t\t\t\tawait igniteCommand.execute('bypassPermissions', undefined, undefined, epicWorktreePath)\n\t\t\t\t\t}\n\n\t\t\t\t\tautoSwarmSuccess = true\n\t\t\t\t\tautoSwarmPhaseReached = 'spin'\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Track epic.planned telemetry for decomposition sessions\n\t\t\tif (decompositionContext) {\n\t\t\t\ttry {\n\t\t\t\t\tconst mcpProv = IssueManagementProviderFactory.create(provider as IssueProvider, settings ?? undefined)\n\t\t\t\t\tconst children = await mcpProv.getChildIssues({ number: decompositionContext.identifier })\n\t\t\t\t\tTelemetryService.getInstance().track('epic.planned', {\n\t\t\t\t\t\tchild_count: children.length,\n\t\t\t\t\t\ttracker: provider,\n\t\t\t\t\t})\n\t\t\t\t} catch (error) {\n\t\t\t\t\tlogger.debug(`Telemetry epic.planned tracking failed: ${error instanceof Error ? error.message : error}`)\n\t\t\t\t}\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 (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\tlogger.debug('Claude session completed')\n\t\t\tlogger.info(chalk.green('Planning session ended.'))\n\t\t} finally {\n\t\t\tif (harness) {\n\t\t\t\tawait harness.stop()\n\t\t\t}\n\n\t\t\tif (autoSwarm && autoSwarmStartTime !== null) {\n\t\t\t\tconst durationMinutes = (Date.now() - autoSwarmStartTime) / 60000\n\t\t\t\tconst autoSwarmSource = decompositionContext ? 'decomposition' : 'fresh'\n\t\t\t\tconst resolvedEpicData = epicData as { epicIssueNumber: string; childIssues: number[] } | null\n\t\t\t\ttry {\n\t\t\t\t\tTelemetryService.getInstance().track('auto_swarm.completed', {\n\t\t\t\t\t\tsource: autoSwarmSource,\n\t\t\t\t\t\tsuccess: autoSwarmSuccess,\n\t\t\t\t\t\tchild_count: resolvedEpicData?.childIssues.length ?? 0,\n\t\t\t\t\t\tduration_minutes: Math.round(durationMinutes * 10) / 10,\n\t\t\t\t\t\tphase_reached: autoSwarmPhaseReached,\n\t\t\t\t\t\tfallback_to_normal: autoSwarmFallbackToNormal,\n\t\t\t\t\t})\n\t\t\t\t} catch (error) {\n\t\t\t\t\tlogger.debug(`Telemetry auto_swarm.completed tracking failed: ${error instanceof Error ? error.message : error}`)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n","import net from 'net'\nimport fs from 'fs'\nimport os from 'os'\nimport path from 'path'\nimport { randomUUID } from 'crypto'\nimport { logger } from '../utils/logger.js'\n\nexport interface HarnessMessage {\n\ttype: string\n\tdata?: unknown\n}\n\nexport interface HarnessResponse {\n\ttype: 'instruction' | 'acknowledged' | 'error'\n\tcontent?: string\n}\n\nexport type HarnessHandler = (data: unknown) => HarnessResponse | Promise<HarnessResponse>\n\nexport interface HarnessServerOptions {\n\tsocketPath?: string\n}\n\nexport class HarnessServer {\n\tprivate server: net.Server | null = null\n\tprivate readonly socketPath: string\n\tprivate readonly handlers: Map<string, HarnessHandler> = new Map()\n\tprivate readonly idempotentTypes: Set<string> = new Set()\n\tprivate readonly handledTypes: Set<string> = new Set()\n\tprivate readonly connections: Set<net.Socket> = new Set()\n\tprivate readonly waiters: Map<string, Array<(data: unknown) => void>> = new Map()\n\tprivate readonly boundSignalHandler: (signal: NodeJS.Signals) => void\n\n\tconstructor(options: HarnessServerOptions = {}) {\n\t\tthis.socketPath =\n\t\t\toptions.socketPath ??\n\t\t\tpath.join(os.tmpdir(), `iloom-harness-${randomUUID()}.sock`)\n\t\tthis.boundSignalHandler = (signal: NodeJS.Signals): void => {\n\t\t\tvoid this.stop().finally(() => {\n\t\t\t\t// Re-raise so the default handler terminates the process\n\t\t\t\tprocess.kill(process.pid, signal)\n\t\t\t})\n\t\t}\n\t}\n\n\tget path(): string {\n\t\treturn this.socketPath\n\t}\n\n\tregisterHandler(type: string, handler: HarnessHandler, options?: { idempotent?: boolean }): void {\n\t\tthis.handlers.set(type, handler)\n\t\tif (options?.idempotent) {\n\t\t\tthis.idempotentTypes.add(type)\n\t\t}\n\t}\n\n\tasync start(): Promise<void> {\n\t\tif (this.server !== null) {\n\t\t\tthrow new Error('HarnessServer is already started')\n\t\t}\n\n\t\t// Remove stale socket file from crashed previous runs\n\t\tfs.rmSync(this.socketPath, { force: true })\n\n\t\tconst server = net.createServer((socket) => {\n\t\t\tthis.handleConnection(socket)\n\t\t})\n\t\tthis.server = server\n\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tserver.listen(this.socketPath, () => resolve())\n\t\t\tserver.once('error', reject)\n\t\t})\n\n\t\t// Set socket to owner read/write only\n\t\tfs.chmodSync(this.socketPath, 0o600)\n\n\t\t// Register signal handlers for cleanup on process exit\n\t\tprocess.on('SIGINT', this.boundSignalHandler)\n\t\tprocess.on('SIGTERM', this.boundSignalHandler)\n\n\t\tlogger.debug(`HarnessServer listening on ${this.socketPath}`)\n\t}\n\n\tasync stop(): Promise<void> {\n\t\tif (this.server === null) {\n\t\t\treturn\n\t\t}\n\n\t\t// Destroy all active connections so the server can close\n\t\tfor (const socket of this.connections) {\n\t\t\tsocket.destroy()\n\t\t}\n\t\tthis.connections.clear()\n\n\t\t// Close the server (set to null first for idempotency guard)\n\t\tconst serverToClose = this.server\n\t\tthis.server = null\n\n\t\ttry {\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\tserverToClose.close((err) => {\n\t\t\t\t\tif (err) reject(err)\n\t\t\t\t\telse resolve()\n\t\t\t\t})\n\t\t\t})\n\t\t} finally {\n\t\t\t// Cleanup must run even if server.close() rejects\n\t\t\tfs.rmSync(this.socketPath, { force: true })\n\t\t\tthis.waiters.clear()\n\t\t\tprocess.off('SIGINT', this.boundSignalHandler)\n\t\t\tprocess.off('SIGTERM', this.boundSignalHandler)\n\t\t\tlogger.debug('HarnessServer stopped')\n\t\t}\n\t}\n\n\twaitFor(type: string): Promise<unknown> {\n\t\treturn new Promise<unknown>((resolve) => {\n\t\t\tconst resolvers = this.waiters.get(type) ?? []\n\t\t\tresolvers.push(resolve)\n\t\t\tthis.waiters.set(type, resolvers)\n\t\t})\n\t}\n\n\tprivate handleConnection(socket: net.Socket): void {\n\t\tthis.connections.add(socket)\n\t\tlet buffer = ''\n\t\tconst MAX_BUFFER_SIZE = 1024 * 1024 // 1MB\n\n\t\tsocket.on('data', (chunk: Buffer) => {\n\t\t\tbuffer += chunk.toString()\n\t\t\tif (buffer.length > MAX_BUFFER_SIZE) {\n\t\t\t\tsocket.destroy(new Error('Payload too large'))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst lines = buffer.split('\\n')\n\t\t\t// Keep the last potentially incomplete segment in the buffer\n\t\t\tbuffer = lines.pop() ?? ''\n\t\t\tfor (const line of lines) {\n\t\t\t\tconst trimmed = line.trim()\n\t\t\t\tif (trimmed) {\n\t\t\t\t\tvoid this.processMessage(trimmed, socket)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\n\t\tsocket.on('close', () => {\n\t\t\tthis.connections.delete(socket)\n\t\t})\n\n\t\tsocket.on('error', (err: Error) => {\n\t\t\tlogger.debug(`HarnessServer socket error: ${err.message}`)\n\t\t})\n\t}\n\n\tprivate async processMessage(raw: string, socket: net.Socket): Promise<void> {\n\t\tlet message: HarnessMessage\n\t\ttry {\n\t\t\tconst parsed: unknown = JSON.parse(raw)\n\t\t\tif (!parsed || typeof parsed !== 'object' || typeof (parsed as Record<string, unknown>).type !== 'string') {\n\t\t\t\tthis.sendResponse(socket, { type: 'error', content: 'Invalid message format' })\n\t\t\t\treturn\n\t\t\t}\n\t\t\tmessage = parsed as HarnessMessage\n\t\t} catch {\n\t\t\tthis.sendResponse(socket, { type: 'error', content: 'Malformed JSON' })\n\t\t\treturn\n\t\t}\n\n\t\t// Resolve any waiters registered for this message type\n\t\tconst resolvers = this.waiters.get(message.type)\n\t\tif (resolvers && resolvers.length > 0) {\n\t\t\tthis.waiters.delete(message.type)\n\t\t\tfor (const resolve of resolvers) {\n\t\t\t\tresolve(message.data)\n\t\t\t}\n\t\t}\n\n\t\t// Idempotent handling: only applies to handlers registered with { idempotent: true }\n\t\tif (this.idempotentTypes.has(message.type) && this.handledTypes.has(message.type)) {\n\t\t\tthis.sendResponse(socket, { type: 'acknowledged' })\n\t\t\treturn\n\t\t}\n\n\t\t// No handler registered for this type\n\t\tconst handler = this.handlers.get(message.type)\n\t\tif (!handler) {\n\t\t\tthis.sendResponse(socket, {\n\t\t\t\ttype: 'error',\n\t\t\t\tcontent: `No handler registered for type: ${message.type}`,\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\t// Mark as handled before calling handler (only for idempotent types)\n\t\tif (this.idempotentTypes.has(message.type)) {\n\t\t\tthis.handledTypes.add(message.type)\n\t\t}\n\t\tconst response = await handler(message.data)\n\t\tthis.sendResponse(socket, response)\n\t}\n\n\tprivate sendResponse(socket: net.Socket, response: HarnessResponse): void {\n\t\tsocket.write(JSON.stringify(response) + '\\n')\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGA,OAAO,WAAW;;;ACHlB,OAAO,SAAS;AAChB,OAAO,QAAQ;AACf,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,kBAAkB;AAmBpB,IAAM,gBAAN,MAAoB;AAAA,EAU1B,YAAY,UAAgC,CAAC,GAAG;AAThD,SAAQ,SAA4B;AAEpC,SAAiB,WAAwC,oBAAI,IAAI;AACjE,SAAiB,kBAA+B,oBAAI,IAAI;AACxD,SAAiB,eAA4B,oBAAI,IAAI;AACrD,SAAiB,cAA+B,oBAAI,IAAI;AACxD,SAAiB,UAAuD,oBAAI,IAAI;AAI/E,SAAK,aACJ,QAAQ,cACR,KAAK,KAAK,GAAG,OAAO,GAAG,iBAAiB,WAAW,CAAC,OAAO;AAC5D,SAAK,qBAAqB,CAAC,WAAiC;AAC3D,WAAK,KAAK,KAAK,EAAE,QAAQ,MAAM;AAE9B,gBAAQ,KAAK,QAAQ,KAAK,MAAM;AAAA,MACjC,CAAC;AAAA,IACF;AAAA,EACD;AAAA,EAEA,IAAI,OAAe;AAClB,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,gBAAgB,MAAc,SAAyB,SAA0C;AAChG,SAAK,SAAS,IAAI,MAAM,OAAO;AAC/B,QAAI,mCAAS,YAAY;AACxB,WAAK,gBAAgB,IAAI,IAAI;AAAA,IAC9B;AAAA,EACD;AAAA,EAEA,MAAM,QAAuB;AAC5B,QAAI,KAAK,WAAW,MAAM;AACzB,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACnD;AAGA,OAAG,OAAO,KAAK,YAAY,EAAE,OAAO,KAAK,CAAC;AAE1C,UAAM,SAAS,IAAI,aAAa,CAAC,WAAW;AAC3C,WAAK,iBAAiB,MAAM;AAAA,IAC7B,CAAC;AACD,SAAK,SAAS;AAEd,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,aAAO,OAAO,KAAK,YAAY,MAAM,QAAQ,CAAC;AAC9C,aAAO,KAAK,SAAS,MAAM;AAAA,IAC5B,CAAC;AAGD,OAAG,UAAU,KAAK,YAAY,GAAK;AAGnC,YAAQ,GAAG,UAAU,KAAK,kBAAkB;AAC5C,YAAQ,GAAG,WAAW,KAAK,kBAAkB;AAE7C,WAAO,MAAM,8BAA8B,KAAK,UAAU,EAAE;AAAA,EAC7D;AAAA,EAEA,MAAM,OAAsB;AAC3B,QAAI,KAAK,WAAW,MAAM;AACzB;AAAA,IACD;AAGA,eAAW,UAAU,KAAK,aAAa;AACtC,aAAO,QAAQ;AAAA,IAChB;AACA,SAAK,YAAY,MAAM;AAGvB,UAAM,gBAAgB,KAAK;AAC3B,SAAK,SAAS;AAEd,QAAI;AACH,YAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,sBAAc,MAAM,CAAC,QAAQ;AAC5B,cAAI,IAAK,QAAO,GAAG;AAAA,cACd,SAAQ;AAAA,QACd,CAAC;AAAA,MACF,CAAC;AAAA,IACF,UAAE;AAED,SAAG,OAAO,KAAK,YAAY,EAAE,OAAO,KAAK,CAAC;AAC1C,WAAK,QAAQ,MAAM;AACnB,cAAQ,IAAI,UAAU,KAAK,kBAAkB;AAC7C,cAAQ,IAAI,WAAW,KAAK,kBAAkB;AAC9C,aAAO,MAAM,uBAAuB;AAAA,IACrC;AAAA,EACD;AAAA,EAEA,QAAQ,MAAgC;AACvC,WAAO,IAAI,QAAiB,CAAC,YAAY;AACxC,YAAM,YAAY,KAAK,QAAQ,IAAI,IAAI,KAAK,CAAC;AAC7C,gBAAU,KAAK,OAAO;AACtB,WAAK,QAAQ,IAAI,MAAM,SAAS;AAAA,IACjC,CAAC;AAAA,EACF;AAAA,EAEQ,iBAAiB,QAA0B;AAClD,SAAK,YAAY,IAAI,MAAM;AAC3B,QAAI,SAAS;AACb,UAAM,kBAAkB,OAAO;AAE/B,WAAO,GAAG,QAAQ,CAAC,UAAkB;AACpC,gBAAU,MAAM,SAAS;AACzB,UAAI,OAAO,SAAS,iBAAiB;AACpC,eAAO,QAAQ,IAAI,MAAM,mBAAmB,CAAC;AAC7C;AAAA,MACD;AACA,YAAM,QAAQ,OAAO,MAAM,IAAI;AAE/B,eAAS,MAAM,IAAI,KAAK;AACxB,iBAAW,QAAQ,OAAO;AACzB,cAAM,UAAU,KAAK,KAAK;AAC1B,YAAI,SAAS;AACZ,eAAK,KAAK,eAAe,SAAS,MAAM;AAAA,QACzC;AAAA,MACD;AAAA,IACD,CAAC;AAED,WAAO,GAAG,SAAS,MAAM;AACxB,WAAK,YAAY,OAAO,MAAM;AAAA,IAC/B,CAAC;AAED,WAAO,GAAG,SAAS,CAAC,QAAe;AAClC,aAAO,MAAM,+BAA+B,IAAI,OAAO,EAAE;AAAA,IAC1D,CAAC;AAAA,EACF;AAAA,EAEA,MAAc,eAAe,KAAa,QAAmC;AAC5E,QAAI;AACJ,QAAI;AACH,YAAM,SAAkB,KAAK,MAAM,GAAG;AACtC,UAAI,CAAC,UAAU,OAAO,WAAW,YAAY,OAAQ,OAAmC,SAAS,UAAU;AAC1G,aAAK,aAAa,QAAQ,EAAE,MAAM,SAAS,SAAS,yBAAyB,CAAC;AAC9E;AAAA,MACD;AACA,gBAAU;AAAA,IACX,QAAQ;AACP,WAAK,aAAa,QAAQ,EAAE,MAAM,SAAS,SAAS,iBAAiB,CAAC;AACtE;AAAA,IACD;AAGA,UAAM,YAAY,KAAK,QAAQ,IAAI,QAAQ,IAAI;AAC/C,QAAI,aAAa,UAAU,SAAS,GAAG;AACtC,WAAK,QAAQ,OAAO,QAAQ,IAAI;AAChC,iBAAW,WAAW,WAAW;AAChC,gBAAQ,QAAQ,IAAI;AAAA,MACrB;AAAA,IACD;AAGA,QAAI,KAAK,gBAAgB,IAAI,QAAQ,IAAI,KAAK,KAAK,aAAa,IAAI,QAAQ,IAAI,GAAG;AAClF,WAAK,aAAa,QAAQ,EAAE,MAAM,eAAe,CAAC;AAClD;AAAA,IACD;AAGA,UAAM,UAAU,KAAK,SAAS,IAAI,QAAQ,IAAI;AAC9C,QAAI,CAAC,SAAS;AACb,WAAK,aAAa,QAAQ;AAAA,QACzB,MAAM;AAAA,QACN,SAAS,mCAAmC,QAAQ,IAAI;AAAA,MACzD,CAAC;AACD;AAAA,IACD;AAGA,QAAI,KAAK,gBAAgB,IAAI,QAAQ,IAAI,GAAG;AAC3C,WAAK,aAAa,IAAI,QAAQ,IAAI;AAAA,IACnC;AACA,UAAM,WAAW,MAAM,QAAQ,QAAQ,IAAI;AAC3C,SAAK,aAAa,QAAQ,QAAQ;AAAA,EACnC;AAAA,EAEQ,aAAa,QAAoB,UAAiC;AACzE,WAAO,MAAM,KAAK,UAAU,QAAQ,IAAI,IAAI;AAAA,EAC7C;AACD;;;ADzLA,IAAM,oBAAoB,CAAC,UAAU,UAAU,OAAO;AACtD,IAAM,qBAAqB,CAAC,UAAU,UAAU,SAAS,MAAM;AAQ/D,SAAS,kBAAkB,UAA8B,aAA6B;AACrF,MAAI,SAAS,WAAW,EAAG,QAAO;AAClC,SAAO,SACL,IAAI,WAAS,KAAK,WAAW,GAAG,MAAM,EAAE,KAAK,MAAM,KAAK,KAAK,MAAM,KAAK,GAAG,EAC3E,KAAK,IAAI;AACZ;AAKA,SAAS,mBAAmB,cAAkC,aAA6B;AAC1F,QAAM,QAAkB,CAAC;AAEzB,MAAI,aAAa,UAAU,SAAS,GAAG;AACtC,UAAM,KAAK,iBAAiB;AAC5B,eAAW,OAAO,aAAa,WAAW;AACzC,YAAM,KAAK,KAAK,WAAW,GAAG,IAAI,EAAE,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,GAAG;AAAA,IACpE;AAAA,EACD;AAEA,MAAI,aAAa,SAAS,SAAS,GAAG;AACrC,QAAI,MAAM,SAAS,EAAG,OAAM,KAAK,EAAE;AACnC,UAAM,KAAK,eAAe;AAC1B,eAAW,OAAO,aAAa,UAAU;AACxC,YAAM,KAAK,KAAK,WAAW,GAAG,IAAI,EAAE,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,GAAG;AAAA,IACpE;AAAA,EACD;AAEA,SAAO,MAAM,SAAS,IAAI,MAAM,KAAK,IAAI,IAAI;AAC9C;AAWO,IAAM,cAAN,MAAkB;AAAA,EAGxB,YAAY,iBAAyC;AACpD,SAAK,kBAAkB,mBAAmB,IAAI,sBAAsB;AAAA,EACrE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAa,QACZ,QACA,OACA,MACA,SACA,UACA,cAOA,WACgB;AAEhB,UAAM,eAAc,6CAAc,SAAQ,YAAW,6CAAc,eAAc;AACjF,QAAI,YAAY;AACf,YAAM,aAAa,mBAAmB;AACtC,aAAO,WAAW,YAAY,MAAM,KAAK,gBAAgB,QAAQ,OAAO,MAAM,SAAS,UAAU,cAAc,SAAS,CAAC;AAAA,IAC1H;AAEA,WAAO,KAAK,gBAAgB,QAAQ,OAAO,MAAM,SAAS,UAAU,cAAc,SAAS;AAAA,EAC5F;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,gBACb,QACA,OACA,MACA,SACA,UACA,cAOA,WACgB;AAEhB,QAAI;AACJ,QAAI,SAAS;AACZ,YAAM,aAAa,QAAQ,YAAY;AACvC,YAAM,SAAS,0BAA0B,MAAM,QAAQ,UAAU,UAAU;AAC3E,UAAI,CAAC,OAAO,SAAS;AACpB,cAAM,IAAI,MAAM,qBAAqB,OAAO,sBAAsB,kBAAkB,KAAK,IAAI,CAAC,EAAE;AAAA,MACjG;AACA,0BAAoB;AAAA,IACrB;AAGA,QAAI;AACJ,QAAI,UAAU;AACb,YAAM,aAAa,SAAS,YAAY;AACxC,YAAM,SAAS,0BAA0B,MAAM,SAAS,UAAU,UAAU;AAC5E,UAAI,CAAC,OAAO,SAAS;AACpB,cAAM,IAAI,MAAM,sBAAsB,QAAQ,sBAAsB,mBAAmB,KAAK,IAAI,CAAC,EAAE;AAAA,MACpG;AACA,2BAAqB;AAAA,IACtB;AAEA,WAAO,MAAM,kCAAkC;AAAA,MAC9C,KAAK,QAAQ,IAAI;AAAA,MACjB,WAAW,CAAC,CAAC;AAAA,MACb;AAAA,MACA,SAAS,qBAAqB;AAAA,MAC9B,UAAU,sBAAsB;AAAA,IACjC,CAAC;AAGD,QAAI,QAAQ,IAAI,2BAA2B,UAAU,MAAM,mBAAmB,GAAG;AAChF,YAAM,oBAAoB;AAAA,IAC3B;AAEA,WAAO,KAAK,MAAM,KAAK,0CAA0C,CAAC;AAGlE,WAAO,MAAM,kCAAkC;AAC/C,UAAM,kBAAkB,MAAM,gBAAgB;AAC9C,WAAO,MAAM,wCAAwC,EAAE,gBAAgB,CAAC;AAExE,QAAI,CAAC,iBAAiB;AACrB,aAAO;AAAA,QACN;AAAA,MACD;AACA,YAAM,IAAI,MAAM,mDAAmD;AAAA,IACpE;AAGA,UAAM,kBAAkB,IAAI,gBAAgB;AAC5C,UAAM,WAAW,MAAM,gBAAgB,aAAa;AAMpD,UAAM,kBAAkB,SAAS,qBAAqB,MAAM,IAAI,EAAE,mBAAmB,MAAM;AAC3F,UAAM,2BAA2B,gBAAgB;AACjD,QAAI,uBAMO;AAEX,UAAM,WAAW,WAAW,oBAAoB,gBAAgB,QAAQ,IAAI;AAC5E,UAAM,cAAc,aAAa,WAAW,MAAM;AAElD,QAAI,UAAU,0BAA0B;AAEvC,YAAM,eAAe,oBAAoB,OAAO,QAAQ;AAExD,aAAO,MAAM,oEAAoE,EAAE,YAAY,OAAO,CAAC;AAGvG,YAAM,YAAY,MAAM,aAAa,gBAAgB,MAAM;AAE3D,UAAI,UAAU,SAAS,WAAW,UAAU,YAAY;AAEvD,cAAM,QAAQ,MAAM,aAAa,WAAW,UAAU,UAAU;AAChE,+BAAuB;AAAA,UACtB,YAAY,OAAO,MAAM,MAAM;AAAA,UAC/B,OAAO,MAAM;AAAA,UACb,MAAM,MAAM;AAAA,QACb;AACA,eAAO,KAAK,MAAM,IAAI,kDAAkD,qBAAqB,UAAU,KAAK,qBAAqB,KAAK,EAAE,CAAC;AAIzI,YAAI;AACH,gBAAM,cAAc,+BAA+B,OAAO,UAA2B,YAAY,MAAS;AAG1G,iBAAO,MAAM,mDAAmD,EAAE,YAAY,qBAAqB,WAAW,CAAC;AAC/G,gBAAM,WAAW,MAAM,YAAY,eAAe,EAAE,QAAQ,qBAAqB,WAAW,CAAC;AAC7F,cAAI,SAAS,SAAS,GAAG;AACxB,iCAAqB,WAAW;AAChC,mBAAO,MAAM,+BAA+B,EAAE,OAAO,SAAS,OAAO,CAAC;AAAA,UACvE;AAGA,iBAAO,MAAM,mDAAmD,EAAE,YAAY,qBAAqB,WAAW,CAAC;AAC/G,gBAAM,eAAe,MAAM,YAAY,gBAAgB;AAAA,YACtD,QAAQ,qBAAqB;AAAA,YAC7B,WAAW;AAAA,UACZ,CAAC;AACD,cAAI,aAAa,SAAS,SAAS,KAAK,aAAa,UAAU,SAAS,GAAG;AAC1E,iCAAqB,eAAe;AACpC,mBAAO,MAAM,+BAA+B;AAAA,cAC3C,UAAU,aAAa,SAAS;AAAA,cAChC,WAAW,aAAa,UAAU;AAAA,YACnC,CAAC;AAAA,UACF;AAAA,QACD,SAAS,OAAO;AAEf,iBAAO,MAAM,kEAAkE;AAAA,YAC9E,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,UACjD,CAAC;AAAA,QACF;AAAA,MACD,OAAO;AAEN,eAAO,MAAM,+EAA+E;AAAA,UAC3F,YAAY;AAAA,UACZ,eAAe,UAAU;AAAA,QAC1B,CAAC;AAAA,MACF;AAAA,IACD;AAGA,UAAM,iBAAiB,SAAS,gBAAgB,aAAa,YAAY,MAAS;AAGlF,UAAM,mBAAmB,qBAAqB,gBAAgB,eAAe,YAAY,MAAS;AAClG,UAAM,oBAAoB,sBAAsB,gBAAgB,gBAAgB,YAAY,MAAS;AAErG,WAAO,MAAM,yDAAyD;AAAA,MACrE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD,CAAC;AAID,WAAO,MAAM,4CAA4C;AACzD,QAAI;AACJ,QAAI;AACH,kBAAY,MAAM,iCAAiC,QAAW,QAAW,UAAU,YAAY,MAAS;AAAA,IACzG,SAAS,OAAO;AACf,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AAGzD,UAAI,yBAAyB,GAAG;AAC/B,cAAM,gBAAgB,MAAM;AAAA,UAC3B;AAAA,UACA;AAAA,QACD;AACA,YAAI,eAAe;AAElB,iBAAO,KAAK,MAAM,KAAK,yBAAyB,CAAC;AACjD,gBAAM,EAAE,YAAY,IAAI,MAAM,OAAO,oBAAW;AAChD,gBAAM,cAAc,IAAI,YAAY;AACpC,gBAAM,YAAY;AAAA,YACjB;AAAA,UACD;AAGA,iBAAO,KAAK,MAAM,KAAK,oCAAoC,CAAC;AAC5D,cAAI;AACH,wBAAY,MAAM,iCAAiC,QAAW,QAAW,UAAU,YAAY,MAAS;AAAA,UACzG,SAAS,YAAY;AACpB,kBAAM,eAAe,sBAAsB,QAAQ,WAAW,UAAU;AACxE,mBAAO,MAAM,kCAAkC,YAAY,EAAE;AAC7D,gBAAI,aAAa,UAAU;AAC1B,qBAAO;AAAA,gBACN;AAAA,cACD;AACA,oBAAM,IAAI;AAAA,gBACT,6CAA6C,YAAY;AAAA,cAC1D;AAAA,YACD,OAAO;AACN,qBAAO;AAAA,gBACN;AAAA,cACD;AACA,oBAAM,IAAI;AAAA,gBACT,6CAA6C,YAAY;AAAA,cAC1D;AAAA,YACD;AAAA,UACD;AAAA,QACD,OAAO;AAEN,iBAAO,MAAM,kCAAkC,OAAO,EAAE;AACxD,cAAI,aAAa,UAAU;AAC1B,mBAAO;AAAA,cACN;AAAA,YACD;AACA,kBAAM,IAAI;AAAA,cACT,kCAAkC,OAAO;AAAA,YAC1C;AAAA,UACD,OAAO;AACN,mBAAO;AAAA,cACN;AAAA,YACD;AACA,kBAAM,IAAI;AAAA,cACT,kCAAkC,OAAO;AAAA,YAC1C;AAAA,UACD;AAAA,QACD;AAAA,MACD,OAAO;AAEN,eAAO,MAAM,kCAAkC,OAAO,EAAE;AACxD,YAAI,aAAa,UAAU;AAC1B,iBAAO;AAAA,YACN;AAAA,UACD;AACA,gBAAM,IAAI;AAAA,YACT,kCAAkC,OAAO;AAAA,UAC1C;AAAA,QACD,OAAO;AACN,iBAAO;AAAA,YACN;AAAA,UACD;AACA,gBAAM,IAAI;AAAA,YACT,kCAAkC,OAAO;AAAA,UAC1C;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAEA,WAAO,MAAM,wBAAwB;AAAA,MACpC,aAAa,UAAU;AAAA,IACxB,CAAC;AAGD,QAAI,UAAgC;AACpC,QAAI,kBAAkB;AACtB,QAAI,WAAsE;AAC1E,UAAM,aAAa,YAAY,IAAI,gBAAgB,IAAI;AACvD,UAAM,qBAAqB,YAAY,KAAK,IAAI,IAAI;AACpD,QAAI,mBAAmB;AACvB,QAAI,wBAAmD;AACvD,QAAI,4BAA4B;AAEhC,QAAI,WAAW;AACd,YAAM,kBAAkB,uBAAuB,kBAAkB;AACjE,UAAI;AACH,yBAAiB,YAAY,EAAE,MAAM,sBAAsB;AAAA,UAC1D,QAAQ;AAAA,UACR,SAAS;AAAA,QACV,CAAC;AAAA,MACF,SAAS,OAAO;AACf,eAAO,MAAM,iDAAiD,iBAAiB,QAAQ,MAAM,UAAU,KAAK,EAAE;AAAA,MAC/G;AAGA,aAAO;AAGP,YAAM,iBAAiB,QAAQ,IAAI;AACnC,wBAAkB,CAAC,CAAC;AAEpB,UAAI,CAAC,gBAAgB;AAEpB,kBAAU,IAAI,cAAc;AAC5B,cAAM,QAAQ,MAAM;AAAA,MACrB;AAEA,YAAM,aAAa,mBAAkB,mCAAS;AAC9C,UAAI,CAAC,YAAY;AAChB,cAAM,IAAI,MAAM,8CAA8C;AAAA,MAC/D;AAGA,UAAI,SAAS;AACZ,gBAAQ,gBAAgB,QAAQ,CAAC,SAAS;AACzC,qBAAW;AACX,uBAAa,MAAM;AAAE,qDAAY;AAAA,UAAQ,CAAC;AAC1C,iBAAO;AAAA,YACN,MAAM;AAAA,YACN,SAAS;AAAA,UACV;AAAA,QACD,GAAG,EAAE,YAAY,KAAK,CAAC;AAAA,MACxB;AAGA,YAAM,mBAAmB,yBAAyB,UAAU;AAC5D,kBAAY,CAAC,GAAG,WAAW,GAAG,gBAAgB;AAAA,IAC/C;AAGA,UAAM,eAAe,QAAQ,IAAI,iBAAiB;AAClD,WAAO,MAAM,0BAA0B,EAAE,aAAa,CAAC;AAIvD,UAAM,gBAAgB,kBAAkB,OAAO,CAAC,KAAK,OAAO;AAAA,MAC3D,GAAG;AAAA,MACH,CAAC,OAAO,EAAE,YAAY,CAAC,UAAU,GAAG,qBAAqB;AAAA,IAC1D,IAAI,CAAC,CAA4B;AAGhC,IAAC,CAAC,UAAU,UAAU,OAAO,EAAY,QAAQ,OAAK;AACtD,oBAAc,OAAO,EAAE,YAAY,CAAC,WAAW,IAAI,sBAAsB;AAAA,IAC1E,CAAC;AAGD,UAAM,mBAAmB,gBAAgB,wBAAwB,YAAY,MAAS;AAGtF,WAAO,MAAM,8BAA8B;AAC3C,UAAM,oBAAuC;AAAA,MAC5C,gBAAgB;AAAA,MAChB,mBAAmB;AAAA,MACnB,qBAAqB,CAAC,CAAC;AAAA,MACvB,qBAAqB,CAAC;AAAA,MACtB,qBAAqB,6DAAsB;AAAA,MAC3C,oBAAoB,6DAAsB;AAAA,MAC1C,mBAAmB,6DAAsB;AAAA,MACzC,wBAAuB,6DAAsB,YAC1C,kBAAkB,qBAAqB,UAAU,WAAW,IAC5D;AAAA,MACH,4BAA2B,6DAAsB,gBAC9C,mBAAmB,qBAAqB,cAAc,WAAW,IACjE;AAAA,MACH,SAAS;AAAA,MACT,UAAU;AAAA,MACV,cAAc,sBAAsB;AAAA,MACpC,iBAAiB,aAAa;AAAA,MAC9B,GAAG;AAAA,IACJ;AACA,UAAM,kBAAkB,MAAM,KAAK,gBAAgB,UAAU,QAAQ,iBAAiB;AACtF,WAAO,MAAM,sBAAsB;AAAA,MAClC,cAAc,gBAAgB;AAAA,MAC9B,MAAM,uBAAuB,kBAAkB;AAAA,IAChD,CAAC;AAGD,UAAM,eAAe;AAAA;AAAA,MAEpB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAEA,QAAI,WAAW;AACd,mBAAa,KAAK,sBAAsB;AAAA,IACzC;AAGA,UAAM,cAAa,6CAAc,UAAS;AAG1C,UAAM,gBAAoD;AAAA,MACzD,OAAO;AAAA,MACP,UAAU;AAAA,MACV,oBAAoB;AAAA,MACpB;AAAA,MACA,QAAQ,QAAQ,IAAI;AAAA,MACpB;AAAA,IACD;AAGA,SAAI,6CAAc,kBAAiB,QAAW;AAC7C,oBAAc,eAAe,aAAa;AAAA,IAC3C;AACA,SAAI,6CAAc,aAAY,QAAW;AACxC,oBAAc,UAAU,aAAa;AAAA,IACtC;AAGA,QAAI,6CAAc,MAAM;AACvB,oBAAc,WAAW;AACzB,oBAAc,eAAe;AAAA,IAC9B,WAAW,6CAAc,YAAY;AACpC,oBAAc,WAAW;AACzB,oBAAc,eAAe;AAAA,IAC9B;AAGA,UAAM,iBAAiB,QAAQ,UAAU;AAGzC,QAAI,eAAe;AAElB,UAAI,QAAQ,CAAC,QAAQ;AACpB,cAAM,IAAI,MAAM,+GAA+G;AAAA,MAChI;AACA,aAAO;AAAA,QACN;AAAA,MACD;AAAA,IACD;AAEA,WAAO,MAAM,iCAAiC;AAAA,MAC7C,YAAY,OAAO,KAAK,aAAa;AAAA,MACrC,UAAU,cAAc;AAAA,MACxB,iBAAiB,CAAC,CAAC,cAAc;AAAA,MACjC,QAAQ,cAAc;AAAA,MACtB;AAAA,MACA,OAAO;AAAA,IACR,CAAC;AAID,QAAI;AACJ,QAAI,sBAAsB;AAEzB,uBAAiB,qBAAqB,qBAAqB,UAAU;AAAA,IACtE,WAAW,QAAQ;AAElB,uBAAiB;AAAA,IAClB,OAAO;AAEN,uBAAiB;AAAA,IAClB;AAGA,QAAI,eAAe;AAClB,uBAAiB;AAAA;AAAA;AAAA;AAAA,EAIlB,cAAc;AAAA,IACd;AAEA,QAAI;AACH,YAAM,eAAe,MAAM,aAAa,gBAAgB;AAAA,QACvD,GAAG;AAAA,QACH,GAAI,iBAAiB,EAAE,gBAAgB,oBAA6B;AAAA,QACpE,GAAI,cAAc,EAAE,QAAQ,WAAW,OAAO;AAAA,MAC/C,CAAC;AAGD,UAAI,WAAW;AAId,YAAI,iBAAiB;AACpB,iBAAO,KAAK,MAAM,MAAM,oEAAoE,CAAC;AAC7F,6BAAmB;AACnB,kCAAwB;AAAA,QACzB,WAAW,CAAC,UAAU;AACrB,gBAAM,IAAI,MAAM,0EAA0E;AAAA,QAC3F,OAAO;AAGN,gBAAM,mBAAmB;AACzB,gBAAM,kBAAkB,iBAAiB;AACzC,gBAAM,cAAc,iBAAiB,eAAe,CAAC;AACrD,iBAAO,KAAK,MAAM,MAAM,mCAAmC,eAAe,EAAE,CAAC;AAC7E,sCAA4B,YAAY,WAAW;AAEnD,gBAAM,eAAe,IAAI,aAAa,oBAAoB,OAAO,YAAY,CAAC,CAAC,CAAC;AAEhF,cAAI,YAAY,WAAW,GAAG;AAE7B,mBAAO,KAAK,gEAAgE;AAC5E,gBAAI;AACJ,gBAAI;AACH,4BAAc,MAAM,aAAa,QAAQ;AAAA,gBACxC,YAAY,OAAO,eAAe;AAAA,gBAClC,SAAS,EAAE,SAAS,qBAAqB,MAAM,MAAM,QAAQ,OAAO,MAAM,OAAO,WAAW,OAAO,UAAU,MAAM;AAAA,cACpH,CAAC;AAAA,YACF,SAAS,YAAY;AACpB,oBAAM,IAAI;AAAA,gBACT,gDAAgD,sBAAsB,QAAQ,WAAW,UAAU,OAAO,UAAU,CAAC;AAAA,cACtH;AAAA,YACD;AAEA,kBAAM,mBAAmB,2CAAa;AACtC,gBAAI,CAAC,kBAAkB;AACtB,oBAAM,IAAI,MAAM,2DAA2D;AAAA,YAC5E;AAEA,kBAAM,gBAAgB,IAAI,cAAc;AACxC,kBAAM,cAAc,QAAQ,qBAAqB,QAAW,QAAW,gBAAgB;AAAA,UACxF,OAAO;AAEN,gBAAI;AACJ,gBAAI;AACH,4BAAc,MAAM,aAAa,QAAQ;AAAA,gBACxC,YAAY,OAAO,eAAe;AAAA,gBAClC,SAAS,EAAE,MAAM,MAAM,MAAM,MAAM,SAAS,qBAAqB,QAAQ,OAAO,MAAM,OAAO,WAAW,OAAO,UAAU,MAAM;AAAA,cAChI,CAAC;AAAA,YACF,SAAS,YAAY;AACpB,oBAAM,IAAI;AAAA,gBACT,gDAAgD,sBAAsB,QAAQ,WAAW,UAAU,OAAO,UAAU,CAAC;AAAA,cACtH;AAAA,YACD;AAEA,kBAAM,mBAAmB,2CAAa;AACtC,gBAAI,CAAC,kBAAkB;AACtB,oBAAM,IAAI,MAAM,2DAA2D;AAAA,YAC5E;AAEA,kBAAM,gBAAgB,IAAI,cAAc;AACxC,kBAAM,cAAc,QAAQ,qBAAqB,QAAW,QAAW,gBAAgB;AAAA,UACxF;AAEA,6BAAmB;AACnB,kCAAwB;AAAA,QACzB;AAAA,MACD;AAGA,UAAI,sBAAsB;AACzB,YAAI;AACH,gBAAM,UAAU,+BAA+B,OAAO,UAA2B,YAAY,MAAS;AACtG,gBAAM,WAAW,MAAM,QAAQ,eAAe,EAAE,QAAQ,qBAAqB,WAAW,CAAC;AACzF,2BAAiB,YAAY,EAAE,MAAM,gBAAgB;AAAA,YACpD,aAAa,SAAS;AAAA,YACtB,SAAS;AAAA,UACV,CAAC;AAAA,QACF,SAAS,OAAO;AACf,iBAAO,MAAM,2CAA2C,iBAAiB,QAAQ,MAAM,UAAU,KAAK,EAAE;AAAA,QACzG;AAAA,MACD;AAGA,UAAI,6CAAc,MAAM;AAEvB,gBAAQ,IAAI,KAAK,UAAU;AAAA,UAC1B,SAAS;AAAA,UACT,QAAQ,gBAAgB;AAAA,QACzB,CAAC,CAAC;AAAA,MACH;AAEA,aAAO,MAAM,0BAA0B;AACvC,aAAO,KAAK,MAAM,MAAM,yBAAyB,CAAC;AAAA,IACnD,UAAE;AACD,UAAI,SAAS;AACZ,cAAM,QAAQ,KAAK;AAAA,MACpB;AAEA,UAAI,aAAa,uBAAuB,MAAM;AAC7C,cAAM,mBAAmB,KAAK,IAAI,IAAI,sBAAsB;AAC5D,cAAM,kBAAkB,uBAAuB,kBAAkB;AACjE,cAAM,mBAAmB;AACzB,YAAI;AACH,2BAAiB,YAAY,EAAE,MAAM,wBAAwB;AAAA,YAC5D,QAAQ;AAAA,YACR,SAAS;AAAA,YACT,cAAa,qDAAkB,YAAY,WAAU;AAAA,YACrD,kBAAkB,KAAK,MAAM,kBAAkB,EAAE,IAAI;AAAA,YACrD,eAAe;AAAA,YACf,oBAAoB;AAAA,UACrB,CAAC;AAAA,QACF,SAAS,OAAO;AACf,iBAAO,MAAM,mDAAmD,iBAAiB,QAAQ,MAAM,UAAU,KAAK,EAAE;AAAA,QACjH;AAAA,MACD;AAAA,IACD;AAAA,EACD;AACD;","names":[]}
@@ -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 { DockerManager } from '../lib/DockerManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { IdentifierParser } from '../utils/IdentifierParser.js'\nimport { openBrowser } from '../utils/browser.js'\nimport { getWorkspacePort } from '../utils/port.js'\nimport { extractIssueNumber } from '../utils/git.js'\nimport { logger } from '../utils/logger.js'\nimport { extractSettingsOverrides } from '../utils/cli-overrides.js'\nimport type { GitWorktree } from '../types/worktree.js'\n\nexport interface RunCommandInput {\n\tidentifier?: string\n\targs?: string[]\n}\n\ninterface ParsedRunInput {\n\ttype: 'issue' | 'pr' | 'branch' | 'epic'\n\tnumber?: string | number // For issues and PRs\n\tbranchName?: string // For branches\n\toriginalInput: string\n\tautoDetected: boolean\n}\n\n/**\n * RunCommand - Runs CLI tool or opens workspace in browser\n * Priority: CLI first, Web fallback\n */\nexport class RunCommand {\n\tconstructor(\n\t\tprivate gitWorktreeManager = new GitWorktreeManager(),\n\t\tprivate capabilityDetector = new ProjectCapabilityDetector(),\n\t\tprivate identifierParser = new IdentifierParser(new GitWorktreeManager()),\n\t\tprivate devServerManager = new DevServerManager(),\n\t\tprivate settingsManager = new SettingsManager()\n\t) {}\n\n\tasync execute(input: RunCommandInput): Promise<void> {\n\t\t// 1. Parse or auto-detect identifier\n\t\tconst parsed = input.identifier\n\t\t\t? await this.parseExplicitInput(input.identifier)\n\t\t\t: await this.autoDetectFromCurrentDirectory()\n\n\t\tlogger.debug(`Parsed input: ${JSON.stringify(parsed)}`)\n\n\t\t// 2. Find worktree path based on identifier\n\t\tconst worktree = await this.findWorktreeForIdentifier(parsed)\n\n\t\tlogger.info(`Found worktree at: ${worktree.path}`)\n\n\t\t// 3. Detect project capabilities\n\t\tconst { capabilities, binEntries } =\n\t\t\tawait this.capabilityDetector.detectCapabilities(worktree.path)\n\n\t\tlogger.debug(`Detected capabilities: ${capabilities.join(', ')}`)\n\n\t\t// 4. Execute based on capabilities (CLI first, web fallback)\n\t\tif (capabilities.includes('cli')) {\n\t\t\tawait this.runCLITool(worktree.path, binEntries, input.args ?? [])\n\t\t} else if (capabilities.includes('web')) {\n\t\t\tawait this.openWebBrowser(worktree)\n\t\t} else {\n\t\t\tthrow new Error(\n\t\t\t\t`No CLI or web capabilities detected for workspace at ${worktree.path}`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Parse explicit identifier input\n\t */\n\tprivate async parseExplicitInput(identifier: string): Promise<ParsedRunInput> {\n\t\tconst parsed = await this.identifierParser.parseForPatternDetection(identifier)\n\n\t\t// Description type should never reach run command (converted in start)\n\t\tif (parsed.type === 'description') {\n\t\t\tthrow new Error('Description input type is not supported in run command')\n\t\t}\n\n\t\tconst result: ParsedRunInput = {\n\t\t\ttype: parsed.type,\n\t\t\toriginalInput: parsed.originalInput,\n\t\t\tautoDetected: false,\n\t\t}\n\n\t\tif (parsed.number !== undefined) {\n\t\t\tresult.number = parsed.number\n\t\t}\n\t\tif (parsed.branchName !== undefined) {\n\t\t\tresult.branchName = parsed.branchName\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Auto-detect identifier from current directory\n\t * Same logic as FinishCommand.autoDetectFromCurrentDirectory()\n\t */\n\tprivate async autoDetectFromCurrentDirectory(): Promise<ParsedRunInput> {\n\t\tconst currentDir = path.basename(process.cwd())\n\n\t\t// Check for PR worktree pattern: _pr_N suffix\n\t\tconst prPattern = /_pr_(\\d+)$/\n\t\tconst prMatch = currentDir.match(prPattern)\n\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tlogger.debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'pr',\n\t\t\t\tnumber: prNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Check for issue pattern in directory\n\t\tconst issueNumber = extractIssueNumber(currentDir)\n\n\t\tif (issueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${issueNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: issueNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Fallback: get current branch name\n\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\tconst currentBranch = repoInfo.currentBranch\n\n\t\tif (!currentBranch) {\n\t\t\tthrow new Error(\n\t\t\t\t'Could not auto-detect identifier. Please provide an issue number, PR number, or branch name.\\n' +\n\t\t\t\t\t'Expected directory pattern: feat/issue-XX-description OR worktree with _pr_N suffix'\n\t\t\t)\n\t\t}\n\n\t\t// Try to extract issue from branch name\n\t\tconst branchIssueNumber = extractIssueNumber(currentBranch)\n\t\tif (branchIssueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: branchIssueNumber,\n\t\t\t\toriginalInput: currentBranch,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Last resort: use branch name\n\t\treturn {\n\t\t\ttype: 'branch',\n\t\t\tbranchName: currentBranch,\n\t\t\toriginalInput: currentBranch,\n\t\t\tautoDetected: true,\n\t\t}\n\t}\n\n\t/**\n\t * Find worktree for the given identifier\n\t */\n\tprivate async findWorktreeForIdentifier(parsed: ParsedRunInput): Promise<GitWorktree> {\n\t\tlet worktree: GitWorktree | null = null\n\n\t\tif (parsed.type === 'issue' && parsed.number !== undefined) {\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForIssue(parsed.number)\n\t\t} else if (parsed.type === 'pr' && parsed.number !== undefined) {\n\t\t\t// For PRs, ensure the number is numeric (PRs are always numeric per GitHub)\n\t\t\tconst prNumber = typeof parsed.number === 'number' ? parsed.number : Number(parsed.number)\n\t\t\tif (isNaN(prNumber) || !isFinite(prNumber)) {\n\t\t\t\tthrow new Error(`Invalid PR number: ${parsed.number}. PR numbers must be numeric.`)\n\t\t\t}\n\t\t\t// Pass empty string for branch name since we don't know it yet\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForPR(prNumber, '')\n\t\t} else if (parsed.type === 'branch' && parsed.branchName) {\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForBranch(\n\t\t\t\tparsed.branchName\n\t\t\t)\n\t\t}\n\n\t\tif (!worktree) {\n\t\t\tthrow new Error(\n\t\t\t\t`No worktree found for ${this.formatParsedInput(parsed)}. ` +\n\t\t\t\t\t`Run 'il start ${parsed.originalInput}' to create one.`\n\t\t\t)\n\t\t}\n\n\t\treturn worktree\n\t}\n\n\t/**\n\t * Format parsed input for display\n\t */\n\tprivate formatParsedInput(parsed: ParsedRunInput): string {\n\t\tconst autoLabel = parsed.autoDetected ? ' (auto-detected)' : ''\n\n\t\tif (parsed.type === 'issue') {\n\t\t\treturn `issue #${parsed.number}${autoLabel}`\n\t\t}\n\t\tif (parsed.type === 'pr') {\n\t\t\treturn `PR #${parsed.number}${autoLabel}`\n\t\t}\n\t\treturn `branch \"${parsed.branchName}\"${autoLabel}`\n\t}\n\n\t/**\n\t * Run CLI tool directly from worktree bin path (NO SYMLINKS!)\n\t */\n\tprivate async runCLITool(\n\t\tworktreePath: string,\n\t\tbinEntries: Record<string, string>,\n\t\targs: string[]\n\t): Promise<void> {\n\t\t// Validate binEntries exist\n\t\tif (Object.keys(binEntries).length === 0) {\n\t\t\tthrow new Error('No bin entries found in package.json')\n\t\t}\n\n\t\t// Get first bin entry (deterministic)\n\t\tconst firstEntry = Object.entries(binEntries)[0]\n\t\tif (!firstEntry) {\n\t\t\tthrow new Error('No bin entries found in package.json')\n\t\t}\n\t\tconst [binName, binPath] = firstEntry\n\t\tlogger.debug(`Using bin entry: ${binName} -> ${binPath}`)\n\n\t\t// CRITICAL: Construct absolute path (NO SYMLINKS!)\n\t\tconst binFilePath = path.resolve(worktreePath, binPath)\n\t\tlogger.debug(`Resolved bin file path: ${binFilePath}`)\n\n\t\t// Verify file exists\n\t\tif (!(await fs.pathExists(binFilePath))) {\n\t\t\tthrow new Error(\n\t\t\t\t`CLI executable not found: ${binFilePath}\\n` +\n\t\t\t\t\t`Make sure the project is built (run 'il start' first)`\n\t\t\t)\n\t\t}\n\n\t\t// Execute with Node.js\n\t\tlogger.info(`Running CLI: node ${binFilePath} ${args.join(' ')}`)\n\t\tawait execa('node', [binFilePath, ...args], {\n\t\t\tstdio: 'inherit', // Allow interactive CLIs (prompts, colors, etc.)\n\t\t\tcwd: worktreePath, // Execute in worktree context\n\t\t\tenv: process.env, // Inherit environment\n\t\t})\n\t}\n\n\t/**\n\t * Open web browser with workspace URL\n\t * Auto-starts dev server if not already running\n\t */\n\tprivate async openWebBrowser(worktree: GitWorktree): Promise<void> {\n\t\tconst cliOverrides = extractSettingsOverrides()\n\t\tconst settings = await this.settingsManager.loadSettings(undefined, cliOverrides)\n\t\tconst 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// Extract Docker configuration if Docker mode is enabled\n\t\tconst issueNumber = extractIssueNumber(worktree.branch)\n\t\tconst dockerIdentifier = issueNumber?.toString() ?? worktree.branch\n\t\tconst dockerConfig = DockerManager.buildDockerConfigFromSettings(\n\t\t\tsettings.capabilities?.web,\n\t\t\tdockerIdentifier\n\t\t)\n\n\t\tif (dockerConfig) {\n\t\t\tawait DockerManager.assertAvailable()\n\t\t\tconst { dockerFile, containerPort, identifier } = dockerConfig\n\t\t\tlogger.debug(`Docker mode enabled with config: ${JSON.stringify({ dockerFile, containerPort, identifier })}`)\n\t\t}\n\n\t\t// Ensure dev server is running on the port\n\t\tconst serverReady = await this.devServerManager.ensureServerRunning(\n\t\t\tworktree.path,\n\t\t\tport,\n\t\t\tdockerConfig\n\t\t)\n\n\t\tif (!serverReady) {\n\t\t\tlogger.warn(\n\t\t\t\t`Dev server failed to start on port ${port}. Opening browser anyway...`\n\t\t\t)\n\t\t}\n\n\t\t// Construct URL and open browser\n\t\tconst 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":[]}