@iloom/cli 0.11.1 → 0.13.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (290) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +170 -15
  3. package/dist/BitBucketApiClient-J2ZSCS5N.js +10 -0
  4. package/dist/BitBucketVCSProvider-5X64IXXW.js +12 -0
  5. package/dist/{BranchNamingService-XBCO747L.js → BranchNamingService-MEK2WZUD.js} +4 -4
  6. package/dist/ClaudeContextManager-RRGREEZQ.js +14 -0
  7. package/dist/ClaudeService-LEPW6QAC.js +13 -0
  8. package/dist/GitHubService-UTAYZXL3.js +12 -0
  9. package/dist/IssueTrackerFactory-KE2BDCLC.js +15 -0
  10. package/dist/{LoomLauncher-5AZU2F5I.js → LoomLauncher-GKQMR5E6.js} +10 -10
  11. package/dist/MetadataManager-V4LSJ2PB.js +10 -0
  12. package/dist/ProjectCapabilityDetector-I4J66WKF.js +11 -0
  13. package/dist/{PromptTemplateManager-T5VTLJP3.js → PromptTemplateManager-I75WKXM4.js} +3 -3
  14. package/dist/README.md +170 -15
  15. package/dist/{SettingsManager-WQ5NSGAH.js → SettingsManager-KQU7OX7G.js} +15 -5
  16. package/dist/SettingsMigrationManager-ZPARZ5KH.js +10 -0
  17. package/dist/agents/iloom-code-reviewer.md +2 -1
  18. package/dist/agents/iloom-framework-detector.md +0 -1
  19. package/dist/agents/iloom-issue-analyze-and-plan.md +4 -1
  20. package/dist/agents/iloom-issue-analyzer.md +4 -1
  21. package/dist/agents/iloom-issue-complexity-evaluator.md +4 -1
  22. package/dist/agents/iloom-issue-enhancer.md +4 -1
  23. package/dist/agents/iloom-issue-implementer.md +5 -2
  24. package/dist/agents/iloom-issue-planner.md +4 -1
  25. package/dist/agents/iloom-wave-verifier.md +186 -0
  26. package/dist/browser-VZY7F2DF.js +10 -0
  27. package/dist/build-V3KADFMO.js +27 -0
  28. package/dist/{chunk-XXFSOVL3.js → chunk-3XEXT35Z.js} +4 -4
  29. package/dist/{chunk-YRCEOQPX.js → chunk-4JZEQBWV.js} +4 -3
  30. package/dist/chunk-4JZEQBWV.js.map +1 -0
  31. package/dist/{chunk-LE2NOUTN.js → chunk-4VQXMEEP.js} +3 -3
  32. package/dist/{chunk-G2MNSPA4.js → chunk-772N5WCA.js} +2 -2
  33. package/dist/{chunk-WG4MLJ6J.js → chunk-7RCUWU3I.js} +2 -2
  34. package/dist/chunk-7RCUWU3I.js.map +1 -0
  35. package/dist/{chunk-NOMQ5RFG.js → chunk-7UBEHQTP.js} +2 -2
  36. package/dist/{chunk-7NFCGKZT.js → chunk-AQUSMNBF.js} +3 -3
  37. package/dist/{chunk-IDCE26KD.js → chunk-AUYSAMXV.js} +3 -3
  38. package/dist/chunk-AYLC633W.js +406 -0
  39. package/dist/chunk-AYLC633W.js.map +1 -0
  40. package/dist/{chunk-QVAA5KHK.js → chunk-BZ7KTXPB.js} +16 -8
  41. package/dist/chunk-BZ7KTXPB.js.map +1 -0
  42. package/dist/{chunk-K7R5QY6C.js → chunk-CE676WCN.js} +2 -2
  43. package/dist/{chunk-5UFGO4ZT.js → chunk-CQHHEW2M.js} +6 -3
  44. package/dist/chunk-CQHHEW2M.js.map +1 -0
  45. package/dist/{chunk-LHDD4JHC.js → chunk-D4Q7T5KD.js} +4 -4
  46. package/dist/{chunk-RBYTXYGD.js → chunk-D75KSI3V.js} +2 -2
  47. package/dist/{chunk-Y3RX7LZT.js → chunk-DDHWZNGL.js} +18 -12
  48. package/dist/chunk-DDHWZNGL.js.map +1 -0
  49. package/dist/{chunk-5LTID2AF.js → chunk-DMSL5BAP.js} +35 -6
  50. package/dist/{chunk-5LTID2AF.js.map → chunk-DMSL5BAP.js.map} +1 -1
  51. package/dist/{chunk-SQYHPBFP.js → chunk-EGNUOALL.js} +2 -2
  52. package/dist/{chunk-ZAXRQLK3.js → chunk-FTYWGQFM.js} +2 -2
  53. package/dist/{chunk-5PNZBH6V.js → chunk-H3T3EPF3.js} +2 -2
  54. package/dist/{chunk-VNYWBHKR.js → chunk-JD3K2344.js} +3 -3
  55. package/dist/{chunk-6YVJVUR4.js → chunk-JDN4SPV3.js} +3 -3
  56. package/dist/{chunk-NCPZYQ4B.js → chunk-K3QGG4O2.js} +2 -2
  57. package/dist/{chunk-ABVMUNCD.js → chunk-KQSV7FOG.js} +64 -10
  58. package/dist/chunk-KQSV7FOG.js.map +1 -0
  59. package/dist/{chunk-NH3QZYE5.js → chunk-KV4NU3RP.js} +2 -2
  60. package/dist/{chunk-NDSGJZI2.js → chunk-LOAYWTJJ.js} +2 -2
  61. package/dist/{chunk-GMDSYLI6.js → chunk-MY2Q3FJ3.js} +2 -2
  62. package/dist/{chunk-CV47VCMQ.js → chunk-NPVA65KS.js} +2 -2
  63. package/dist/{chunk-RMLADZRY.js → chunk-NTDY5AMO.js} +5 -5
  64. package/dist/{chunk-UHIBKD73.js → chunk-NUUFP53X.js} +13 -32
  65. package/dist/{chunk-UHIBKD73.js.map → chunk-NUUFP53X.js.map} +1 -1
  66. package/dist/{chunk-7OCGBJLR.js → chunk-OIVFHJOA.js} +2 -2
  67. package/dist/chunk-P5MXXHXQ.js +284 -0
  68. package/dist/chunk-P5MXXHXQ.js.map +1 -0
  69. package/dist/{chunk-TEJAGQX2.js → chunk-PD75ZCFT.js} +35 -35
  70. package/dist/chunk-PD75ZCFT.js.map +1 -0
  71. package/dist/{chunk-NN5RYWXA.js → chunk-PH65MFQM.js} +6 -6
  72. package/dist/{chunk-LL6TOX3G.js → chunk-Q7VXHJP6.js} +10 -10
  73. package/dist/chunk-Q7VXHJP6.js.map +1 -0
  74. package/dist/chunk-QC65IOV3.js +304 -0
  75. package/dist/chunk-QC65IOV3.js.map +1 -0
  76. package/dist/{chunk-V4STTBQD.js → chunk-QED2WB2D.js} +9 -9
  77. package/dist/{chunk-3RXYOBME.js → chunk-QNPJXO53.js} +5 -5
  78. package/dist/{chunk-3RXYOBME.js.map → chunk-QNPJXO53.js.map} +1 -1
  79. package/dist/chunk-QQULYI2S.js +696 -0
  80. package/dist/chunk-QQULYI2S.js.map +1 -0
  81. package/dist/{chunk-QNHZM5ZV.js → chunk-QXGM32TO.js} +3 -3
  82. package/dist/{chunk-TZNNJLGT.js → chunk-RFCAPHL5.js} +6 -6
  83. package/dist/{chunk-7FIXNAUO.js → chunk-SA446KA2.js} +66 -43
  84. package/dist/chunk-SA446KA2.js.map +1 -0
  85. package/dist/{chunk-UDCI3QTS.js → chunk-SN4S5CWL.js} +2 -2
  86. package/dist/{chunk-VUUN3KE4.js → chunk-TAEVA4QR.js} +8 -8
  87. package/dist/chunk-TAEVA4QR.js.map +1 -0
  88. package/dist/{chunk-IR74O2F6.js → chunk-TN2D2RX7.js} +253 -174
  89. package/dist/chunk-TN2D2RX7.js.map +1 -0
  90. package/dist/{chunk-3GTUXW26.js → chunk-VIQOQ463.js} +19 -3
  91. package/dist/chunk-VIQOQ463.js.map +1 -0
  92. package/dist/{chunk-H2SSF24U.js → chunk-VRPPI6GU.js} +17 -6
  93. package/dist/{chunk-H2SSF24U.js.map → chunk-VRPPI6GU.js.map} +1 -1
  94. package/dist/{chunk-XFQGI2E3.js → chunk-VVQQIG64.js} +58 -53
  95. package/dist/chunk-VVQQIG64.js.map +1 -0
  96. package/dist/{chunk-YETJNRQM.js → chunk-WEBMMJKL.js} +2 -1
  97. package/dist/{chunk-ET6A2JR4.js → chunk-WGUGB54H.js} +120 -18
  98. package/dist/chunk-WGUGB54H.js.map +1 -0
  99. package/dist/{chunk-QR4FU53I.js → chunk-X5DRLONY.js} +22 -12
  100. package/dist/chunk-X5DRLONY.js.map +1 -0
  101. package/dist/{chunk-KQFIGI37.js → chunk-XCP2WDYA.js} +7 -7
  102. package/dist/{chunk-VMZG66UV.js → chunk-YUOVWWJX.js} +312 -7
  103. package/dist/chunk-YUOVWWJX.js.map +1 -0
  104. package/dist/{chunk-HLDY5S4C.js → chunk-ZUIFO7B4.js} +3 -3
  105. package/dist/{claude-ONQTDWV3.js → claude-ACL7G4CF.js} +4 -4
  106. package/dist/{cleanup-YOM6PQCN.js → cleanup-RJKLI47I.js} +40 -37
  107. package/dist/cleanup-RJKLI47I.js.map +1 -0
  108. package/dist/cli.js +322 -169
  109. package/dist/cli.js.map +1 -1
  110. package/dist/{color-VQD52LOI.js → color-AC6F2QE7.js} +3 -3
  111. package/dist/{commit-DC2Q5CDY.js → commit-SUHRUMDE.js} +15 -15
  112. package/dist/{compile-4NCQECKE.js → compile-2MD346PO.js} +11 -11
  113. package/dist/{contribute-M5UWXCAV.js → contribute-P4BMRY7C.js} +11 -11
  114. package/dist/{contribute-M5UWXCAV.js.map → contribute-P4BMRY7C.js.map} +1 -1
  115. package/dist/{mcp/darwin-3JFFE3W2.js → darwin-5K3I4FTH.js} +2 -2
  116. package/dist/database-helpers-PRDFNDRO.js +11 -0
  117. package/dist/{dev-server-CYRP6M73.js → dev-server-ZNTLWOL5.js} +35 -21
  118. package/dist/dev-server-ZNTLWOL5.js.map +1 -0
  119. package/dist/{feedback-BMAZGKRW.js → feedback-Q6WG2WX4.js} +17 -17
  120. package/dist/{git-BXUD6CL5.js → git-TX2IEMB3.js} +6 -6
  121. package/dist/ignite-P644W2PK.js +35 -0
  122. package/dist/index.d.ts +236 -18
  123. package/dist/index.js +180 -63
  124. package/dist/index.js.map +1 -1
  125. package/dist/{init-CI43GJHV.js → init-5HFY7JG6.js} +18 -18
  126. package/dist/{install-deps-SRTM5U7D.js → install-deps-J4ALTM27.js} +11 -11
  127. package/dist/{installation-detector-HF6QN7KP.js → installation-detector-PYAZ2O6U.js} +3 -3
  128. package/dist/{issues-DMRQJH7E.js → issues-LZMIF22U.js} +69 -56
  129. package/dist/issues-LZMIF22U.js.map +1 -0
  130. package/dist/lint-XIXKU22H.js +27 -0
  131. package/dist/{linux-RYLOP2LY.js → linux-WUGRYCJY.js} +2 -2
  132. package/dist/mcp/{chunk-PIIRD4LO.js → chunk-4HZMW2V3.js} +1 -1
  133. package/dist/mcp/{chunk-PIIRD4LO.js.map → chunk-4HZMW2V3.js.map} +1 -1
  134. package/dist/{darwin-5BHWRJ7D.js → mcp/darwin-U25WIGH6.js} +2 -2
  135. package/dist/mcp/issue-management-server.js +908 -20
  136. package/dist/mcp/issue-management-server.js.map +1 -1
  137. package/dist/mcp/{linux-JBVS4R3A.js → linux-5BXVBGSY.js} +2 -2
  138. package/dist/mcp/recap-server.js +24 -22
  139. package/dist/mcp/recap-server.js.map +1 -1
  140. package/dist/mcp/{tmux-RYBLEHUZ.js → tmux-CU26ZTNM.js} +2 -2
  141. package/dist/mcp/{wsl-4QZIQLLE.js → wsl-KI25UDOF.js} +2 -2
  142. package/dist/{open-2Y7GSUTJ.js → open-KUO35JIJ.js} +36 -21
  143. package/dist/open-KUO35JIJ.js.map +1 -0
  144. package/dist/{plan-SWFPLNJE.js → plan-7CF56OIR.js} +47 -43
  145. package/dist/{plan-SWFPLNJE.js.map → plan-7CF56OIR.js.map} +1 -1
  146. package/dist/{projects-IUSUXD5D.js → projects-L5AHUBGA.js} +6 -6
  147. package/dist/{prompt-7LZB4PAT.js → prompt-FUU5NMJQ.js} +3 -3
  148. package/dist/prompt-FUU5NMJQ.js.map +1 -0
  149. package/dist/prompts/init-prompt.txt +184 -23
  150. package/dist/prompts/issue-prompt.txt +94 -158
  151. package/dist/prompts/plan-prompt.txt +55 -0
  152. package/dist/prompts/regular-prompt.txt +1 -1
  153. package/dist/prompts/swarm-orchestrator-prompt.txt +78 -21
  154. package/dist/{rebase-S6OHAOOF.js → rebase-MAMWPA2L.js} +12 -12
  155. package/dist/{recap-GGVCG5VH.js → recap-IDBO3KM5.js} +9 -9
  156. package/dist/{remote-MZTFHHTU.js → remote-RO4LZKT2.js} +3 -3
  157. package/dist/remote-RO4LZKT2.js.map +1 -0
  158. package/dist/{run-ST3FR75O.js → run-RGZHCQ6M.js} +36 -21
  159. package/dist/run-RGZHCQ6M.js.map +1 -0
  160. package/dist/schema/settings.schema.json +171 -11
  161. package/dist/{shell-W4SBQPTE.js → shell-7ADCDFIV.js} +8 -8
  162. package/dist/{summary-P2JCIIJO.js → summary-7J2HORFD.js} +21 -19
  163. package/dist/summary-7J2HORFD.js.map +1 -0
  164. package/dist/test-SRB7EWU6.js +27 -0
  165. package/dist/{test-git-2KFFAQ6B.js → test-git-G7ATVIXG.js} +6 -6
  166. package/dist/{test-jira-FKDKG6CD.js → test-jira-Q2HPA522.js} +8 -8
  167. package/dist/{test-prefix-GP2DAX37.js → test-prefix-JMDGXR5A.js} +6 -6
  168. package/dist/{test-tabs-YDWMWTVA.js → test-tabs-NGPTFD5T.js} +2 -2
  169. package/dist/{test-webserver-QI3QQFZ3.js → test-webserver-GZFVXBGD.js} +8 -8
  170. package/dist/{tmux-7ZTA3BDI.js → tmux-6LRFH3DM.js} +2 -2
  171. package/dist/{update-XLW7R7FL.js → update-AD3GE5C4.js} +4 -4
  172. package/dist/{update-notifier-EYLAXZAA.js → update-notifier-VYDTDMSJ.js} +3 -3
  173. package/dist/update-notifier-VYDTDMSJ.js.map +1 -0
  174. package/dist/{vscode-TOGE5N67.js → vscode-3I7ISHUU.js} +12 -12
  175. package/dist/{vscode-announcement-NIX7O2MG.js → vscode-announcement-AL3EHORH.js} +3 -3
  176. package/dist/{wsl-Y4GUTOQ7.js → wsl-4VMVT2PO.js} +2 -2
  177. package/package.json +1 -1
  178. package/dist/ClaudeContextManager-SXDCWDJA.js +0 -14
  179. package/dist/ClaudeService-6E6MCGJE.js +0 -13
  180. package/dist/GitHubService-2R5GQG4K.js +0 -12
  181. package/dist/IssueTrackerFactory-XN6MQ4UN.js +0 -14
  182. package/dist/MetadataManager-CMQQTFLQ.js +0 -10
  183. package/dist/ProjectCapabilityDetector-IC6NAFGY.js +0 -11
  184. package/dist/SettingsMigrationManager-S6J7OHUH.js +0 -10
  185. package/dist/build-OLS6J5KZ.js +0 -27
  186. package/dist/chunk-3GTUXW26.js.map +0 -1
  187. package/dist/chunk-5UFGO4ZT.js.map +0 -1
  188. package/dist/chunk-7FIXNAUO.js.map +0 -1
  189. package/dist/chunk-ABVMUNCD.js.map +0 -1
  190. package/dist/chunk-ET6A2JR4.js.map +0 -1
  191. package/dist/chunk-IR74O2F6.js.map +0 -1
  192. package/dist/chunk-LL6TOX3G.js.map +0 -1
  193. package/dist/chunk-QR4FU53I.js.map +0 -1
  194. package/dist/chunk-QVAA5KHK.js.map +0 -1
  195. package/dist/chunk-RVI6C2H5.js +0 -220
  196. package/dist/chunk-RVI6C2H5.js.map +0 -1
  197. package/dist/chunk-TEJAGQX2.js.map +0 -1
  198. package/dist/chunk-VMZG66UV.js.map +0 -1
  199. package/dist/chunk-VUUN3KE4.js.map +0 -1
  200. package/dist/chunk-WG4MLJ6J.js.map +0 -1
  201. package/dist/chunk-XFQGI2E3.js.map +0 -1
  202. package/dist/chunk-Y3RX7LZT.js.map +0 -1
  203. package/dist/chunk-YRCEOQPX.js.map +0 -1
  204. package/dist/cleanup-YOM6PQCN.js.map +0 -1
  205. package/dist/dev-server-CYRP6M73.js.map +0 -1
  206. package/dist/ignite-IO4LXVXJ.js +0 -35
  207. package/dist/issues-DMRQJH7E.js.map +0 -1
  208. package/dist/lint-BSWRMGPZ.js +0 -27
  209. package/dist/neon-helpers-HWIYRKOW.js +0 -11
  210. package/dist/open-2Y7GSUTJ.js.map +0 -1
  211. package/dist/run-ST3FR75O.js.map +0 -1
  212. package/dist/summary-P2JCIIJO.js.map +0 -1
  213. package/dist/test-6JH4FE2X.js +0 -27
  214. /package/dist/{BranchNamingService-XBCO747L.js.map → BitBucketApiClient-J2ZSCS5N.js.map} +0 -0
  215. /package/dist/{ClaudeContextManager-SXDCWDJA.js.map → BitBucketVCSProvider-5X64IXXW.js.map} +0 -0
  216. /package/dist/{ClaudeService-6E6MCGJE.js.map → BranchNamingService-MEK2WZUD.js.map} +0 -0
  217. /package/dist/{GitHubService-2R5GQG4K.js.map → ClaudeContextManager-RRGREEZQ.js.map} +0 -0
  218. /package/dist/{IssueTrackerFactory-XN6MQ4UN.js.map → ClaudeService-LEPW6QAC.js.map} +0 -0
  219. /package/dist/{MetadataManager-CMQQTFLQ.js.map → GitHubService-UTAYZXL3.js.map} +0 -0
  220. /package/dist/{ProjectCapabilityDetector-IC6NAFGY.js.map → IssueTrackerFactory-KE2BDCLC.js.map} +0 -0
  221. /package/dist/{LoomLauncher-5AZU2F5I.js.map → LoomLauncher-GKQMR5E6.js.map} +0 -0
  222. /package/dist/{PromptTemplateManager-T5VTLJP3.js.map → MetadataManager-V4LSJ2PB.js.map} +0 -0
  223. /package/dist/{SettingsManager-WQ5NSGAH.js.map → ProjectCapabilityDetector-I4J66WKF.js.map} +0 -0
  224. /package/dist/{SettingsMigrationManager-S6J7OHUH.js.map → PromptTemplateManager-I75WKXM4.js.map} +0 -0
  225. /package/dist/{claude-ONQTDWV3.js.map → SettingsManager-KQU7OX7G.js.map} +0 -0
  226. /package/dist/{color-VQD52LOI.js.map → SettingsMigrationManager-ZPARZ5KH.js.map} +0 -0
  227. /package/dist/{darwin-5BHWRJ7D.js.map → browser-VZY7F2DF.js.map} +0 -0
  228. /package/dist/{build-OLS6J5KZ.js.map → build-V3KADFMO.js.map} +0 -0
  229. /package/dist/{chunk-XXFSOVL3.js.map → chunk-3XEXT35Z.js.map} +0 -0
  230. /package/dist/{chunk-LE2NOUTN.js.map → chunk-4VQXMEEP.js.map} +0 -0
  231. /package/dist/{chunk-G2MNSPA4.js.map → chunk-772N5WCA.js.map} +0 -0
  232. /package/dist/{chunk-NOMQ5RFG.js.map → chunk-7UBEHQTP.js.map} +0 -0
  233. /package/dist/{chunk-7NFCGKZT.js.map → chunk-AQUSMNBF.js.map} +0 -0
  234. /package/dist/{chunk-IDCE26KD.js.map → chunk-AUYSAMXV.js.map} +0 -0
  235. /package/dist/{chunk-K7R5QY6C.js.map → chunk-CE676WCN.js.map} +0 -0
  236. /package/dist/{chunk-LHDD4JHC.js.map → chunk-D4Q7T5KD.js.map} +0 -0
  237. /package/dist/{chunk-RBYTXYGD.js.map → chunk-D75KSI3V.js.map} +0 -0
  238. /package/dist/{chunk-SQYHPBFP.js.map → chunk-EGNUOALL.js.map} +0 -0
  239. /package/dist/{chunk-ZAXRQLK3.js.map → chunk-FTYWGQFM.js.map} +0 -0
  240. /package/dist/{chunk-5PNZBH6V.js.map → chunk-H3T3EPF3.js.map} +0 -0
  241. /package/dist/{chunk-VNYWBHKR.js.map → chunk-JD3K2344.js.map} +0 -0
  242. /package/dist/{chunk-6YVJVUR4.js.map → chunk-JDN4SPV3.js.map} +0 -0
  243. /package/dist/{chunk-NCPZYQ4B.js.map → chunk-K3QGG4O2.js.map} +0 -0
  244. /package/dist/{chunk-NH3QZYE5.js.map → chunk-KV4NU3RP.js.map} +0 -0
  245. /package/dist/{chunk-NDSGJZI2.js.map → chunk-LOAYWTJJ.js.map} +0 -0
  246. /package/dist/{chunk-GMDSYLI6.js.map → chunk-MY2Q3FJ3.js.map} +0 -0
  247. /package/dist/{chunk-CV47VCMQ.js.map → chunk-NPVA65KS.js.map} +0 -0
  248. /package/dist/{chunk-RMLADZRY.js.map → chunk-NTDY5AMO.js.map} +0 -0
  249. /package/dist/{chunk-7OCGBJLR.js.map → chunk-OIVFHJOA.js.map} +0 -0
  250. /package/dist/{chunk-NN5RYWXA.js.map → chunk-PH65MFQM.js.map} +0 -0
  251. /package/dist/{chunk-V4STTBQD.js.map → chunk-QED2WB2D.js.map} +0 -0
  252. /package/dist/{chunk-QNHZM5ZV.js.map → chunk-QXGM32TO.js.map} +0 -0
  253. /package/dist/{chunk-TZNNJLGT.js.map → chunk-RFCAPHL5.js.map} +0 -0
  254. /package/dist/{chunk-UDCI3QTS.js.map → chunk-SN4S5CWL.js.map} +0 -0
  255. /package/dist/{chunk-YETJNRQM.js.map → chunk-WEBMMJKL.js.map} +0 -0
  256. /package/dist/{chunk-KQFIGI37.js.map → chunk-XCP2WDYA.js.map} +0 -0
  257. /package/dist/{chunk-HLDY5S4C.js.map → chunk-ZUIFO7B4.js.map} +0 -0
  258. /package/dist/{git-BXUD6CL5.js.map → claude-ACL7G4CF.js.map} +0 -0
  259. /package/dist/{ignite-IO4LXVXJ.js.map → color-AC6F2QE7.js.map} +0 -0
  260. /package/dist/{commit-DC2Q5CDY.js.map → commit-SUHRUMDE.js.map} +0 -0
  261. /package/dist/{compile-4NCQECKE.js.map → compile-2MD346PO.js.map} +0 -0
  262. /package/dist/{installation-detector-HF6QN7KP.js.map → darwin-5K3I4FTH.js.map} +0 -0
  263. /package/dist/{mcp/darwin-3JFFE3W2.js.map → database-helpers-PRDFNDRO.js.map} +0 -0
  264. /package/dist/{feedback-BMAZGKRW.js.map → feedback-Q6WG2WX4.js.map} +0 -0
  265. /package/dist/{neon-helpers-HWIYRKOW.js.map → git-TX2IEMB3.js.map} +0 -0
  266. /package/dist/{prompt-7LZB4PAT.js.map → ignite-P644W2PK.js.map} +0 -0
  267. /package/dist/{init-CI43GJHV.js.map → init-5HFY7JG6.js.map} +0 -0
  268. /package/dist/{install-deps-SRTM5U7D.js.map → install-deps-J4ALTM27.js.map} +0 -0
  269. /package/dist/{remote-MZTFHHTU.js.map → installation-detector-PYAZ2O6U.js.map} +0 -0
  270. /package/dist/{lint-BSWRMGPZ.js.map → lint-XIXKU22H.js.map} +0 -0
  271. /package/dist/{linux-RYLOP2LY.js.map → linux-WUGRYCJY.js.map} +0 -0
  272. /package/dist/{update-notifier-EYLAXZAA.js.map → mcp/darwin-U25WIGH6.js.map} +0 -0
  273. /package/dist/mcp/{linux-JBVS4R3A.js.map → linux-5BXVBGSY.js.map} +0 -0
  274. /package/dist/mcp/{tmux-RYBLEHUZ.js.map → tmux-CU26ZTNM.js.map} +0 -0
  275. /package/dist/mcp/{wsl-4QZIQLLE.js.map → wsl-KI25UDOF.js.map} +0 -0
  276. /package/dist/{projects-IUSUXD5D.js.map → projects-L5AHUBGA.js.map} +0 -0
  277. /package/dist/{rebase-S6OHAOOF.js.map → rebase-MAMWPA2L.js.map} +0 -0
  278. /package/dist/{recap-GGVCG5VH.js.map → recap-IDBO3KM5.js.map} +0 -0
  279. /package/dist/{shell-W4SBQPTE.js.map → shell-7ADCDFIV.js.map} +0 -0
  280. /package/dist/{test-6JH4FE2X.js.map → test-SRB7EWU6.js.map} +0 -0
  281. /package/dist/{test-git-2KFFAQ6B.js.map → test-git-G7ATVIXG.js.map} +0 -0
  282. /package/dist/{test-jira-FKDKG6CD.js.map → test-jira-Q2HPA522.js.map} +0 -0
  283. /package/dist/{test-prefix-GP2DAX37.js.map → test-prefix-JMDGXR5A.js.map} +0 -0
  284. /package/dist/{test-tabs-YDWMWTVA.js.map → test-tabs-NGPTFD5T.js.map} +0 -0
  285. /package/dist/{test-webserver-QI3QQFZ3.js.map → test-webserver-GZFVXBGD.js.map} +0 -0
  286. /package/dist/{tmux-7ZTA3BDI.js.map → tmux-6LRFH3DM.js.map} +0 -0
  287. /package/dist/{update-XLW7R7FL.js.map → update-AD3GE5C4.js.map} +0 -0
  288. /package/dist/{vscode-TOGE5N67.js.map → vscode-3I7ISHUU.js.map} +0 -0
  289. /package/dist/{vscode-announcement-NIX7O2MG.js.map → vscode-announcement-AL3EHORH.js.map} +0 -0
  290. /package/dist/{wsl-Y4GUTOQ7.js.map → wsl-4VMVT2PO.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/claude.ts"],"sourcesContent":["/* global AbortSignal */\nimport { execa, type ExecaChildProcess } from 'execa'\nimport { existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { createHash, randomUUID } from 'node:crypto'\nimport { logger } from './logger.js'\nimport { getLogger } from './logger-context.js'\nimport { openTerminalWindow } from './terminal.js'\n\n/**\n * Generate a deterministic UUID v5 from a worktree path\n * Uses SHA1 hash with URL namespace to create a consistent session ID\n * that can be used to resume Claude Code sessions\n */\nexport function generateDeterministicSessionId(worktreePath: string): string {\n\t// UUID v5 namespace for URLs (RFC 4122)\n\tconst URL_NAMESPACE = '6ba7b811-9dad-11d1-80b4-00c04fd430c8'\n\n\t// Create SHA1 hash of namespace + path\n\tconst hash = createHash('sha1')\n\n\t// Convert namespace UUID to bytes\n\tconst namespaceBytes = Buffer.from(URL_NAMESPACE.replace(/-/g, ''), 'hex')\n\thash.update(namespaceBytes)\n\thash.update(worktreePath)\n\n\tconst digest = hash.digest()\n\n\t// Format as UUID v5:\n\t// - Set version (bits 12-15 of time_hi_and_version) to 5\n\t// - Set variant (bits 6-7 of clock_seq_hi_and_reserved) to binary 10\n\tconst bytes = Array.from(digest.subarray(0, 16))\n\n\t// Set version to 5 (byte 6, high nibble)\n\tconst byte6 = bytes[6] ?? 0\n\tbytes[6] = (byte6 & 0x0f) | 0x50\n\n\t// Set variant to RFC 4122 (byte 8, high 2 bits = 10)\n\tconst byte8 = bytes[8] ?? 0\n\tbytes[8] = (byte8 & 0x3f) | 0x80\n\n\t// Format as UUID string\n\tconst hex = Buffer.from(bytes).toString('hex')\n\treturn `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`\n}\n\n/**\n * Generate a random UUID v4 for session ID\n * Uses crypto.randomUUID() for cryptographically secure random UUID generation\n * Used to create unique session IDs for each loom, enabling fresh Claude sessions\n */\nexport function generateRandomSessionId(): string {\n\treturn randomUUID()\n}\n\nexport interface ClaudeCliOptions {\n\tmodel?: string\n\tpermissionMode?: 'plan' | 'acceptEdits' | 'bypassPermissions' | 'default'\n\taddDir?: string\n\theadless?: boolean\n\tbranchName?: string // Optional branch name for terminal coloring\n\tport?: number // Optional port for terminal window export\n\ttimeout?: number // Timeout in milliseconds\n\tappendSystemPrompt?: string // System instructions to append to system prompt\n\tmcpConfig?: Record<string, unknown>[] // Array of MCP server configurations\n\tallowedTools?: string[] // Tools to allow via --allowed-tools flag\n\tdisallowedTools?: string[] // Tools to disallow via --disallowed-tools flag\n\tagents?: Record<string, unknown> // Agent configurations for --agents flag\n\tpluginDir?: string // Path to plugin directory for --plugin-dir flag\n\toneShot?: import('../types/index.js').OneShotMode // One-shot automation mode\n\tsetArguments?: string[] // Raw --set arguments to forward (e.g., ['workflows.issue.startIde=false'])\n\texecutablePath?: string // Executable path to use for spin command (e.g., 'il', 'il-125', or '/path/to/dist/cli.js')\n\tsessionId?: string // Session ID for Claude Code resume support (must be valid UUID)\n\tnoSessionPersistence?: boolean // Prevent session data from being saved to disk (for utility operations)\n\toutputFormat?: 'json' | 'stream-json' | 'text' // Output format for Claude CLI (headless mode)\n\tverbose?: boolean // Enable verbose output (headless mode) - defaults to true when headless\n\tjsonMode?: 'json' | 'stream' // JSON output mode: 'json' for final object, 'stream' for real-time JSONL\n\tpassthroughStdout?: boolean // In headless mode, pipe stdout to process.stdout instead of capturing\n\tenv?: Record<string, string> // Additional environment variables to pass to the Claude process\n\tsignal?: AbortSignal // Optional AbortSignal for graceful termination of the Claude process\n}\n\n/**\n * Detect if Claude CLI is available on the system\n */\nexport async function detectClaudeCli(): Promise<boolean> {\n\ttry {\n\t\t// Use 'command -v' for cross-platform compatibility (works on macOS/Linux)\n\t\tawait execa('command', ['-v', 'claude'], {\n\t\t\tshell: true,\n\t\t\ttimeout: 5000,\n\t\t})\n\t\treturn true\n\t} catch (error) {\n\t\t// Claude CLI not found\n\t\tlogger.debug('Claude CLI not available', { error })\n\t\treturn false\n\t}\n}\n\n/**\n * Get Claude CLI version\n */\nexport async function getClaudeVersion(): Promise<string | null> {\n\ttry {\n\t\tconst result = await execa('claude', ['--version'], {\n\t\t\ttimeout: 5000,\n\t\t})\n\t\treturn result.stdout.trim()\n\t} catch (error) {\n\t\tlogger.warn('Failed to get Claude version', { error })\n\t\treturn null\n\t}\n}\n\n/**\n * Parse JSON stream output and extract result from last JSON object with type:\"result\"\n */\nfunction parseJsonStreamOutput(output: string): string {\n\ttry {\n\t\t// Split by newlines and filter out empty lines\n\t\tconst lines = output.split('\\n').filter(line => line.trim())\n\n\t\t// Find the last valid JSON object with type:\"result\"\n\t\tlet lastResult = ''\n\t\tfor (const line of lines) {\n\t\t\ttry {\n\t\t\t\tconst jsonObj = JSON.parse(line)\n\t\t\t\tif (jsonObj && typeof jsonObj === 'object' && jsonObj.type === 'result' && 'result' in jsonObj) {\n\t\t\t\t\tlastResult = jsonObj.result\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Skip invalid JSON lines\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\treturn lastResult || output // Fallback to original output if no valid result found\n\t} catch {\n\t\t// If parsing fails completely, return original output\n\t\treturn output\n\t}\n}\n\n/**\n * Launch Claude CLI with specified options\n * In headless mode, returns stdout. In interactive mode, returns void.\n */\nexport async function launchClaude(\n\tprompt: string,\n\toptions: ClaudeCliOptions = {}\n): Promise<string | void> {\n\tconst { model, permissionMode, addDir, headless = false, appendSystemPrompt, mcpConfig, allowedTools, disallowedTools, agents, pluginDir, sessionId, noSessionPersistence, outputFormat, verbose, jsonMode, passthroughStdout, env: extraEnv, signal } = options\n\tconst log = getLogger()\n\n\t// Build command arguments\n\tconst args: string[] = []\n\n\tif (headless) {\n\t\targs.push('-p')\n\n\t\t// Use user-provided outputFormat or default to stream-json for progress tracking\n\t\tconst effectiveOutputFormat = outputFormat ?? 'stream-json'\n\t\targs.push('--output-format', effectiveOutputFormat)\n\n\t\t// Use user-provided verbose setting or default to true\n\t\tif (verbose !== false) {\n\t\t\targs.push('--verbose')\n\t\t}\n\t}\n\n\tif (model) {\n\t\targs.push('--model', model)\n\t}\n\n\tif (permissionMode && permissionMode !== 'default') {\n\t\targs.push('--permission-mode', permissionMode)\n\t}\n\n\tif (addDir) {\n\t\targs.push('--add-dir', addDir)\n\t}\n\n\targs.push('--add-dir', '/tmp') //TODO: Won't work on Windows\n\n\t// Add system prompt flag\n\tif (appendSystemPrompt) {\n\t\targs.push('--append-system-prompt', appendSystemPrompt)\n\t}\n\n\t// Add --mcp-config flags for each MCP server configuration\n\tif (mcpConfig && mcpConfig.length > 0) {\n\t\tfor (const config of mcpConfig) {\n\t\t\targs.push('--mcp-config', JSON.stringify(config))\n\t\t}\n\t}\n\n\t// Add --allowed-tools flags if provided\n\tif (allowedTools && allowedTools.length > 0) {\n\t\targs.push('--allowed-tools', ...allowedTools)\n\t}\n\n\t// Add --disallowed-tools flags if provided\n\tif (disallowedTools && disallowedTools.length > 0) {\n\t\targs.push('--disallowed-tools', ...disallowedTools)\n\t}\n\n\t// Add --agents flag if provided\n\tif (agents) {\n\t\targs.push('--agents', JSON.stringify(agents))\n\t}\n\n\t// Add --plugin-dir flag if provided\n\tif (pluginDir) {\n\t\targs.push('--plugin-dir', pluginDir)\n\t}\n\n\t// Add --session-id flag if provided (enables Claude Code session resume)\n\tif (sessionId) {\n\t\targs.push('--session-id', sessionId)\n\t}\n\n\t// Add --no-session-persistence flag if requested (for utility operations that don't need session persistence)\n\t// Note: --no-session-persistence can only be used with --print mode (-p), which is only added in headless mode\n\tif (noSessionPersistence && headless) {\n\t\targs.push('--no-session-persistence')\n\t}\n\n\t// Set CLAUDECODE=0 to prevent Claude from detecting it's running inside Claude Code\n\tconst claudeEnv = { ...process.env, CLAUDECODE: '0' }\n\n\t// Helper to attach AbortSignal to a subprocess for graceful termination\n\tfunction attachAbortSignal(subprocess: ExecaChildProcess): void {\n\t\tif (!signal) return\n\t\tconst onAbort = (): void => {\n\t\t\tsubprocess.kill('SIGTERM')\n\t\t}\n\t\tsignal.addEventListener('abort', onAbort, { once: true })\n\t\tsubprocess.on('exit', (): void => {\n\t\t\tsignal.removeEventListener('abort', onAbort)\n\t\t})\n\t}\n\n\ttry {\n\t\tif (headless && passthroughStdout) {\n\t\t\t// Headless + passthrough: Claude's stdout goes directly to process.stdout\n\t\t\t// Used for --json-stream where JSONL must reach the caller's stdout\n\t\t\tconst subprocess = execa('claude', args, {\n\t\t\t\tinput: prompt,\n\t\t\t\ttimeout: 0,\n\t\t\t\t...(addDir && { cwd: addDir }),\n\t\t\t\tenv: { ...claudeEnv, ...extraEnv }, // CLAUDECODE=0 + any extra env vars\n\t\t\t\tstdio: ['pipe', 'inherit', 'pipe'], // stdin: pipe (for prompt), stdout: inherit (passthrough), stderr: pipe (capture errors)\n\t\t\t})\n\n\t\t\tattachAbortSignal(subprocess)\n\t\t\ttry {\n\t\t\t\tawait subprocess\n\t\t\t} catch (err) {\n\t\t\t\tif (signal?.aborted) return\n\t\t\t\tthrow err\n\t\t\t}\n\t\t\treturn // No output to return - it went directly to stdout\n\t\t}\n\n\t\tif (headless) {\n\t\t\t// Headless mode: capture and return output\n\t\t\tconst isDebugMode = logger.isDebugEnabled()\n\n\t\t\t// Set up execa options based on debug mode\n\t\t\tconst execaOptions = {\n\t\t\t\tinput: prompt,\n\t\t\t\ttimeout: 0, // Disable timeout for long responses\n\t\t\t\t...(addDir && { cwd: addDir }), // Run Claude in the worktree directory\n\t\t\t\tverbose: isDebugMode,\n\t\t\t\tenv: { ...claudeEnv, ...extraEnv }, // CLAUDECODE=0 + any extra env vars\n\t\t\t\t...(isDebugMode && { stdio: ['pipe', 'pipe', 'pipe'] as const }), // Enable streaming in debug mode\n\t\t\t}\n\n\t\t\tconst subprocess = execa('claude', args, execaOptions)\n\t\t\tattachAbortSignal(subprocess)\n\n\t\t\t// Check if JSON streaming format is enabled (always true in headless mode)\n\t\t\tconst isJsonStreamFormat = args.includes('--output-format') && args.includes('stream-json')\n\n\t\t\t// Handle real-time streaming (enabled for progress tracking)\n\t\t\tlet outputBuffer = ''\n\t\t\tlet isStreaming = false\n\t\t\tlet isFirstProgress = true\n\t\t\tif (subprocess.stdout && typeof subprocess.stdout.on === 'function') {\n\t\t\t\tisStreaming = true\n\t\t\t\tsubprocess.stdout.on('data', (chunk: Buffer) => {\n\t\t\t\t\tconst text = chunk.toString()\n\t\t\t\t\toutputBuffer += text\n\n\t\t\t\t\tif (jsonMode === 'stream') {\n\t\t\t\t\t\t// --json-stream: Output raw JSONL to stdout immediately\n\t\t\t\t\t\tprocess.stdout.write(text)\n\t\t\t\t\t} else if (jsonMode === 'json') {\n\t\t\t\t\t\t// --json: Suppress all progress output (will return final JSON)\n\t\t\t\t\t\t// Do nothing - just accumulate in buffer\n\t\t\t\t\t} else if (isDebugMode) {\n\t\t\t\t\t\tlog.stdout.write(text) // Full JSON streaming in debug mode\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Progress dots in non-debug mode with robot emoji prefix\n\t\t\t\t\t\tif (isFirstProgress) {\n\t\t\t\t\t\t\tlog.stdout.write('🤖 .')\n\t\t\t\t\t\t\tisFirstProgress = false\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tlog.stdout.write('.')\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\t\tlet result: any\n\t\t\ttry {\n\t\t\t\tresult = await subprocess\n\t\t\t} catch (subprocessError) {\n\t\t\t\t// If aborted intentionally, do not treat as an error\n\t\t\t\tif (signal?.aborted) return\n\t\t\t\tthrow subprocessError\n\t\t\t}\n\n\t\t\t// Return streamed output if we were streaming, otherwise use result.stdout\n\t\t\tif (isStreaming) {\n\t\t\t\tconst rawOutput = outputBuffer.trim()\n\n\t\t\t\t// Clean up progress dots with newline in non-debug mode (skip for json modes)\n\t\t\t\tif (!isDebugMode && !jsonMode) {\n\t\t\t\t\tlog.stdout.write('\\n')\n\t\t\t\t}\n\n\t\t\t\treturn isJsonStreamFormat ? parseJsonStreamOutput(rawOutput) : rawOutput\n\t\t\t} else {\n\t\t\t\t// Fallback for mocked tests or when streaming not available\n\t\t\t\tif (isDebugMode) {\n\t\t\t\t\t// In debug mode, write to stdout even if not streaming (old behavior for tests)\n\t\t\t\t\tlog.stdout.write(result.stdout)\n\t\t\t\t\tif (result.stdout && !result.stdout.endsWith('\\n')) {\n\t\t\t\t\t\tlog.stdout.write('\\n')\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// In non-debug mode, show a single progress dot even without streaming (for tests)\n\t\t\t\t\tlog.stdout.write('🤖 .')\n\t\t\t\t\tlog.stdout.write('\\n')\n\t\t\t\t}\n\t\t\t\tconst rawOutput = result.stdout.trim()\n\t\t\t\treturn isJsonStreamFormat ? parseJsonStreamOutput(rawOutput) : rawOutput\n\t\t\t}\n\t\t} else {\n\t\t\t// Simple interactive mode: run Claude in current terminal with stdio inherit\n\t\t\t// Used for conflict resolution, error fixing, etc.\n\t\t\t// This is the simple approach: claude -- \"prompt\"\n\n\t\t\t// First attempt: capture stderr to detect session ID conflicts\n\t\t\t// stdin/stdout inherit for interactivity, stderr captured for error detection\n\t\t\ttry {\n\t\t\t\tconst interactiveSubprocess = execa('claude', [...args, '--', prompt], {\n\t\t\t\t\t...(addDir && { cwd: addDir }),\n\t\t\t\t\tstdio: ['inherit', 'inherit', 'pipe'], // Capture stderr to detect session conflicts\n\t\t\t\t\ttimeout: 0, // Disable timeout\n\t\t\t\t\tverbose: logger.isDebugEnabled(),\n\t\t\t\t\tenv: { ...claudeEnv, ...extraEnv }, // CLAUDECODE=0 + any extra env vars\n\t\t\t\t})\n\t\t\t\tattachAbortSignal(interactiveSubprocess)\n\t\t\t\ttry {\n\t\t\t\t\tawait interactiveSubprocess\n\t\t\t\t} catch (err) {\n\t\t\t\t\tif (signal?.aborted) return\n\t\t\t\t\tthrow err\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t} catch (interactiveError) {\n\t\t\t\tif (signal?.aborted) return\n\t\t\t\tconst interactiveExecaError = interactiveError as { stderr?: string; message?: string }\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- intentional: empty string stderr should fall through to message\n\t\t\t\tconst interactiveErrorMessage = interactiveExecaError.stderr || interactiveExecaError.message || ''\n\n\t\t\t\t// Check for session ID conflict\n\t\t\t\tconst sessionMatch = interactiveErrorMessage.match(/Session ID ([0-9a-f-]+) is already in use/i)\n\t\t\t\tconst conflictSessionId = sessionMatch?.[1]\n\t\t\t\tif (sessionMatch && sessionId && conflictSessionId) {\n\t\t\t\t\tlog.debug(`Session ID ${conflictSessionId} already in use, retrying with --resume`)\n\n\t\t\t\t\t// Rebuild args with --resume instead of --session-id\n\t\t\t\t\tconst resumeArgs = args.filter((arg, idx) => {\n\t\t\t\t\t\tif (arg === '--session-id') return false\n\t\t\t\t\t\tif (idx > 0 && args[idx - 1] === '--session-id') return false\n\t\t\t\t\t\treturn true\n\t\t\t\t\t})\n\t\t\t\t\tresumeArgs.push('--resume', conflictSessionId)\n\n\t\t\t\t\t// Retry with full stdio inherit for proper interactive experience\n\t\t\t\t\t// Note: When using --resume, we omit the prompt since the session already has context\n\t\t\t\t\tconst resumeSubprocess = execa('claude', resumeArgs, {\n\t\t\t\t\t\t...(addDir && { cwd: addDir }),\n\t\t\t\t\t\tstdio: 'inherit',\n\t\t\t\t\t\ttimeout: 0,\n\t\t\t\t\t\tverbose: logger.isDebugEnabled(),\n\t\t\t\t\t\tenv: claudeEnv,\n\t\t\t\t\t})\n\t\t\t\t\tattachAbortSignal(resumeSubprocess)\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait resumeSubprocess\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tif (signal?.aborted) return\n\t\t\t\t\t\tthrow err\n\t\t\t\t\t}\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\t// Not a session conflict, re-throw\n\t\t\t\tthrow interactiveError\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\t// If aborted intentionally, do not treat as an error\n\t\tif (signal?.aborted) return\n\n\t\t// Check for specific Claude CLI errors\n\t\tconst execaError = error as {\n\t\t\tstderr?: string\n\t\t\tmessage?: string\n\t\t\texitCode?: number\n\t\t}\n\n\t\t// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- intentional: empty string stderr should fall through to message\n\t\tconst errorMessage = execaError.stderr || execaError.message || 'Unknown Claude CLI error'\n\n\t\t// Check for \"Session ID ... is already in use\" error and retry with --resume\n\t\tconst sessionInUseMatch = errorMessage.match(/Session ID ([0-9a-f-]+) is already in use/i)\n\t\tconst extractedSessionId = sessionInUseMatch?.[1]\n\t\tif (sessionInUseMatch && sessionId && extractedSessionId) {\n\t\t\tlog.debug(`Session ID ${extractedSessionId} already in use, retrying with --resume`)\n\n\t\t\t// Rebuild args with --resume instead of --session-id\n\t\t\tconst resumeArgs = args.filter((arg, idx) => {\n\t\t\t\t// Filter out --session-id and its value\n\t\t\t\tif (arg === '--session-id') return false\n\t\t\t\tif (idx > 0 && args[idx - 1] === '--session-id') return false\n\t\t\t\treturn true\n\t\t\t})\n\t\t\tresumeArgs.push('--resume', extractedSessionId)\n\n\t\t\ttry {\n\t\t\t\tif (headless) {\n\t\t\t\t\tconst isDebugMode = logger.isDebugEnabled()\n\t\t\t\t\t// Note: In headless mode, we still need to pass the prompt even with --resume\n\t\t\t\t\t// because there's no interactive input mechanism\n\t\t\t\t\tconst execaOptions = {\n\t\t\t\t\t\tinput: prompt,\n\t\t\t\t\t\ttimeout: 0,\n\t\t\t\t\t\t...(addDir && { cwd: addDir }),\n\t\t\t\t\t\tverbose: isDebugMode,\n\t\t\t\t\t\tenv: claudeEnv,\n\t\t\t\t\t\t...(isDebugMode && { stdio: ['pipe', 'pipe', 'pipe'] as const }),\n\t\t\t\t\t}\n\n\t\t\t\t\tconst subprocess = execa('claude', resumeArgs, execaOptions)\n\t\t\t\t\tconst isJsonStreamFormat = resumeArgs.includes('--output-format') && resumeArgs.includes('stream-json')\n\n\t\t\t\t\tlet outputBuffer = ''\n\t\t\t\t\tlet isStreaming = false\n\t\t\t\t\tlet isFirstProgress = true\n\t\t\t\t\tif (subprocess.stdout && typeof subprocess.stdout.on === 'function') {\n\t\t\t\t\t\tisStreaming = true\n\t\t\t\t\t\tsubprocess.stdout.on('data', (chunk: Buffer) => {\n\t\t\t\t\t\t\tconst text = chunk.toString()\n\t\t\t\t\t\t\toutputBuffer += text\n\t\t\t\t\t\t\tif (jsonMode === 'stream') {\n\t\t\t\t\t\t\t\tprocess.stdout.write(text)\n\t\t\t\t\t\t\t} else if (jsonMode === 'json') {\n\t\t\t\t\t\t\t\t// Suppress progress output for json mode\n\t\t\t\t\t\t\t} else if (isDebugMode) {\n\t\t\t\t\t\t\t\tlog.stdout.write(text)\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tif (isFirstProgress) {\n\t\t\t\t\t\t\t\t\tlog.stdout.write('🤖 .')\n\t\t\t\t\t\t\t\t\tisFirstProgress = false\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tlog.stdout.write('.')\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\n\t\t\t\t\tconst result = await subprocess\n\n\t\t\t\t\tif (isStreaming) {\n\t\t\t\t\t\tconst rawOutput = outputBuffer.trim()\n\t\t\t\t\t\tif (!isDebugMode && !jsonMode) {\n\t\t\t\t\t\t\tlog.stdout.write('\\n')\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn isJsonStreamFormat ? parseJsonStreamOutput(rawOutput) : rawOutput\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (isDebugMode) {\n\t\t\t\t\t\t\tlog.stdout.write(result.stdout)\n\t\t\t\t\t\t\tif (result.stdout && !result.stdout.endsWith('\\n')) {\n\t\t\t\t\t\t\t\tlog.stdout.write('\\n')\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tlog.stdout.write('🤖 .')\n\t\t\t\t\t\t\tlog.stdout.write('\\n')\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst rawOutput = result.stdout.trim()\n\t\t\t\t\t\treturn isJsonStreamFormat ? parseJsonStreamOutput(rawOutput) : rawOutput\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Note: When using --resume, we omit the prompt since the session already has context\n\t\t\t\t\tawait execa('claude', resumeArgs, {\n\t\t\t\t\t\t...(addDir && { cwd: addDir }),\n\t\t\t\t\t\tstdio: 'inherit',\n\t\t\t\t\t\ttimeout: 0,\n\t\t\t\t\t\tverbose: logger.isDebugEnabled(),\n\t\t\t\t\t\tenv: claudeEnv,\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t} catch (retryError) {\n\t\t\t\tconst retryExecaError = retryError as { stderr?: string; message?: string }\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- intentional: empty string stderr should fall through to message\n\t\t\t\tconst retryErrorMessage = retryExecaError.stderr || retryExecaError.message || 'Unknown Claude CLI error'\n\t\t\t\tthrow new Error(`Claude CLI error: ${retryErrorMessage}`)\n\t\t\t}\n\t\t}\n\n\t\t// Re-throw with more context\n\t\tthrow new Error(`Claude CLI error: ${errorMessage}`)\n\t}\n}\n\n/**\n * Launch Claude in a new terminal window with rich context\n * This is specifically for \"end of il start\" workflow\n * Ports the terminal window opening, coloring, and .env sourcing behavior\n */\nexport async function launchClaudeInNewTerminalWindow(\n\t_prompt: string,\n\toptions: ClaudeCliOptions & {\n\t\tworkspacePath: string // Required for terminal window launch\n\t}\n): Promise<void> {\n\tconst { workspacePath, branchName, oneShot = 'default', port, setArguments, executablePath } = options\n\n\t// Verify required parameter\n\tif (!workspacePath) {\n\t\tthrow new Error('workspacePath is required for terminal window launch')\n\t}\n\n\t// Build launch command with optional --one-shot flag\n\t// Use provided executable path or fallback to 'il'\n\tconst executable = executablePath ?? 'iloom'\n\tlet launchCommand = `${executable} spin`\n\tif (oneShot !== 'default') {\n\t\tlaunchCommand += ` --one-shot=${oneShot}`\n\t}\n\n\t// Append --set arguments if provided\n\tif (setArguments && setArguments.length > 0) {\n\t\tfor (const setArg of setArguments) {\n\t\t\tlaunchCommand += ` --set ${setArg}`\n\t\t}\n\t}\n\n\t// Apply terminal background color if branch name available\n\tlet backgroundColor: { r: number; g: number; b: number } | undefined\n\tif (branchName) {\n\t\ttry {\n\t\t\tconst { generateColorFromBranchName } = await import('./color.js')\n\t\t\tconst colorData = generateColorFromBranchName(branchName)\n\t\t\tbackgroundColor = colorData.rgb\n\t\t} catch (error) {\n\t\t\tlogger.warn(\n\t\t\t\t`Failed to generate terminal color: ${error instanceof Error ? error.message : 'Unknown error'}`\n\t\t\t)\n\t\t}\n\t}\n\n\t// Check if .env file exists in workspace\n\tconst hasEnvFile = existsSync(join(workspacePath, '.env'))\n\n\t// Open new terminal window with Claude\n\tawait openTerminalWindow({\n\t\tworkspacePath,\n\t\tcommand: launchCommand,\n\t\t...(backgroundColor && { backgroundColor }),\n\t\tincludeEnvSetup: hasEnvFile, // source .env only if it exists\n\t\t...(port !== undefined && { port, includePortExport: true }),\n\t})\n}\n\n/**\n * Generate a branch name using Claude with fallback\n * This matches the implementation that was working in ClaudeBranchNameStrategy\n */\nexport async function generateBranchName(\n\tissueTitle: string,\n\tissueNumber: string | number,\n\tmodel: string = 'haiku'\n): Promise<string> {\n\ttry {\n\t\t// Check if Claude CLI is available\n\t\tconst isAvailable = await detectClaudeCli()\n\t\tif (!isAvailable) {\n\t\t\tlogger.warn('Claude CLI not available, using fallback branch name')\n\t\t\treturn `feat/issue-${issueNumber}`\n\t\t}\n\n\t\tlogger.debug('Generating branch name with Claude', { issueNumber, issueTitle })\n\n\t\t// Use the proven prompt format from ClaudeBranchNameStrategy\n\t\tconst prompt = `<Task>\nGenerate a git branch name for the following issue:\n<Issue>\n<IssueNumber>${issueNumber}</IssueNumber>\n<IssueTitle>${issueTitle}</IssueTitle>\n</Issue>\n\n<Requirements>\n<IssueNumber>Must use this exact issue number: ${issueNumber}</IssueNumber>\n<Format>Format must be: {prefix}/issue-${issueNumber}__{description}</Format>\n<Prefix>Prefix must be one of: feat, fix, docs, refactor, test, chore</Prefix>\n<MaxLength>Maximum 50 characters total</MaxLength>\n<Characters>Only lowercase letters, numbers, and hyphens allowed</Characters>\n<Output>Reply with ONLY the branch name, nothing else</Output>\n</Requirements>\n</Task>`\n\n\t\tlogger.debug('Sending prompt to Claude', { prompt })\n\n\t\tconst result = (await launchClaude(prompt, {\n\t\t\tmodel,\n\t\t\theadless: true,\n\t\t\tnoSessionPersistence: true, // Utility operation - don't persist session\n\t\t\tenv: { CLAUDE_CODE_SIMPLE: '1' }, // Minimal mode - no MCP, hooks, or CLAUDE.md loading\n\t\t})) as string\n\n\t\t// Normalize to lowercase for consistency (Linear IDs are uppercase but branches should be lowercase)\n\t\tconst branchName = result.trim().toLowerCase()\n\t\tlogger.debug('Claude returned branch name', { branchName, issueNumber })\n\n\t\t// Validate generated name using same validation as ClaudeBranchNameStrategy\n\t\tif (!branchName || !isValidBranchName(branchName, issueNumber)) {\n\t\t\tlogger.warn('Invalid branch name from Claude, using fallback', { branchName })\n\t\t\treturn `feat/issue-${issueNumber}`.toLowerCase()\n\t\t}\n\n\t\treturn branchName\n\t} catch (error) {\n\t\tlogger.warn('Failed to generate branch name with Claude', { error })\n\t\treturn `feat/issue-${issueNumber}`.toLowerCase()\n\t}\n}\n\n/**\n * Validate branch name format\n * Check format: {prefix}/issue-{number}__{description}\n * Uses case-insensitive matching for issue number (Linear uses uppercase like MARK-1)\n */\nfunction isValidBranchName(name: string, issueNumber: string | number): boolean {\n\tconst pattern = new RegExp(`^(feat|fix|docs|refactor|test|chore)/issue-${issueNumber}__[a-z0-9-]+$`, 'i')\n\treturn pattern.test(name) && name.length <= 50\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/MetadataManager.ts"],"sourcesContent":["import path from 'path'\nimport os from 'os'\nimport fs from 'fs-extra'\nimport { getLogger } from '../utils/logger-context.js'\nimport type { ProjectCapability } from '../types/loom.js'\nimport type { ComplexityOverride, OneShotMode } from '../types/index.js'\n\nexport type SwarmState = 'pending' | 'in_progress' | 'code_review' | 'done' | 'failed'\n\n/**\n * Schema for metadata JSON file\n * Stored in ~/.config/iloom-ai/looms/\n */\nexport interface MetadataFile {\n description: string\n created_at?: string\n version: number\n // Additional metadata fields (v2)\n branchName?: string\n worktreePath?: string\n issueType?: 'branch' | 'issue' | 'pr' | 'epic'\n issueKey?: string // Canonical, properly-cased issue key (e.g., \"PROJ-123\")\n issue_numbers?: string[]\n pr_numbers?: string[]\n issueTracker?: string\n colorHex?: string // Stored hex color (e.g., \"#dcebff\") - robust against palette changes\n sessionId?: string // Claude Code session ID for resume support\n projectPath?: string // Main worktree path (project root) - enables project identification\n issueUrls?: Record<string, string> // Map of issue ID to URL in the issue tracker\n prUrls?: Record<string, string> // Map of PR number to URL in the issue tracker\n draftPrNumber?: number // Draft PR number if github-draft-pr mode was used\n oneShot?: OneShotMode // One-shot automation mode stored during loom creation\n complexity?: ComplexityOverride // Complexity override stored during loom creation\n capabilities?: ProjectCapability[] // Detected project capabilities\n state?: SwarmState // Swarm mode lifecycle state\n childIssueNumbers?: string[] // Child issue numbers for epic looms\n parentLoom?: {\n type: 'issue' | 'pr' | 'branch' | 'epic'\n identifier: string | number\n branchName: string\n worktreePath: string\n databaseBranch?: string\n }\n // Epic/swarm child issue data (populated during spin setup)\n childIssues?: Array<{\n number: string // Prefixed: \"#123\" for GitHub, \"ENG-123\" for Linear\n title: string\n body: string\n url: string\n }>\n dependencyMap?: Record<string, string[]> // issueNumber -> array of blocking issueNumbers\n mcpConfigPath?: string // Path to per-loom MCP config file (for swarm claude -p commands)\n}\n\n/**\n * Input for writing metadata (all fields except version and created_at)\n * Note: issueTracker is required because every loom should have an associated\n * issue tracker provider (defaults to 'github' via IssueTrackerFactory)\n */\nexport interface WriteMetadataInput {\n description: string\n branchName: string\n worktreePath: string\n issueType: 'branch' | 'issue' | 'pr' | 'epic'\n issueKey?: string // Canonical, properly-cased issue key (e.g., \"PROJ-123\")\n issue_numbers: string[]\n pr_numbers: string[]\n issueTracker: string\n colorHex: string // Hex color (e.g., \"#dcebff\") - robust against palette changes\n sessionId: string // Claude Code session ID for resume support (required for new looms)\n projectPath: string // Main worktree path (project root) - required for new looms\n issueUrls: Record<string, string> // Map of issue ID to URL in the issue tracker\n prUrls: Record<string, string> // Map of PR number to URL in the issue tracker\n draftPrNumber?: number // Draft PR number for github-draft-pr mode\n oneShot?: OneShotMode // One-shot automation mode to persist\n complexity?: ComplexityOverride // Complexity override to persist\n capabilities: ProjectCapability[] // Detected project capabilities (required for new looms)\n state?: SwarmState // Swarm mode lifecycle state\n childIssueNumbers?: string[] // Child issue numbers for epic looms\n parentLoom?: {\n type: 'issue' | 'pr' | 'branch' | 'epic'\n identifier: string | number\n branchName: string\n worktreePath: string\n databaseBranch?: string\n }\n // Epic/swarm child issue data (populated during spin setup)\n childIssues?: Array<{\n number: string // Prefixed: \"#123\" for GitHub, \"ENG-123\" for Linear\n title: string\n body: string\n url: string\n }>\n dependencyMap?: Record<string, string[]> // issueNumber -> array of blocking issueNumbers\n mcpConfigPath?: string // Path to per-loom MCP config file (for swarm claude -p commands)\n}\n\n/**\n * Result of reading metadata for a worktree\n */\nexport interface LoomMetadata {\n status?: 'active' | 'finished'\n finishedAt?: string | null\n description: string\n created_at: string | null\n branchName: string | null\n worktreePath: string | null\n issueType: 'branch' | 'issue' | 'pr' | 'epic' | null\n issueKey: string | null // Canonical, properly-cased issue key (e.g., \"PROJ-123\")\n issue_numbers: string[]\n pr_numbers: string[]\n issueTracker: string | null\n colorHex: string | null // Hex color (e.g., \"#dcebff\") - robust against palette changes\n sessionId: string | null // Claude Code session ID (null for legacy looms)\n projectPath: string | null // Main worktree path (null for legacy looms)\n issueUrls: Record<string, string> // Map of issue ID to URL ({} for legacy looms)\n prUrls: Record<string, string> // Map of PR number to URL ({} for legacy looms)\n draftPrNumber: number | null // Draft PR number (null if not draft mode)\n oneShot: OneShotMode | null // One-shot mode (null for legacy looms)\n complexity: ComplexityOverride | null // Complexity override (null when not overridden)\n capabilities: ProjectCapability[] // Detected project capabilities (empty for legacy looms)\n state: SwarmState | null // Swarm mode lifecycle state (null for non-swarm looms)\n childIssueNumbers: string[] // Child issue numbers for epic looms (empty for non-epic looms)\n parentLoom: {\n type: 'issue' | 'pr' | 'branch' | 'epic'\n identifier: string | number\n branchName: string\n worktreePath: string\n databaseBranch?: string\n } | null\n // Epic/swarm child issue data (empty arrays/objects for non-epic looms)\n childIssues: Array<{\n number: string\n title: string\n body: string\n url: string\n }>\n dependencyMap: Record<string, string[]>\n mcpConfigPath: string | null // Path to per-loom MCP config file (null for non-swarm looms)\n}\n\n/**\n * MetadataManager: Manage loom metadata persistence\n *\n * Stores loom metadata in ~/.config/iloom-ai/looms/ directory.\n * Each worktree gets a JSON file named by slugifying its absolute path.\n *\n * Per spec section 2.2:\n * - Filename derived from worktree absolute path\n * - Path separators replaced with double underscores\n * - Non-alphanumeric chars (except _ and -) replaced with hyphens\n */\nexport class MetadataManager {\n private readonly loomsDir: string\n private readonly finishedDir: string\n\n constructor() {\n this.loomsDir = path.join(os.homedir(), '.config', 'iloom-ai', 'looms')\n this.finishedDir = path.join(this.loomsDir, 'finished')\n }\n\n /**\n * Convert MetadataFile to LoomMetadata with default values for optional fields\n */\n private toMetadata(data: MetadataFile): LoomMetadata {\n return {\n description: data.description,\n created_at: data.created_at ?? null,\n branchName: data.branchName ?? null,\n worktreePath: data.worktreePath ?? null,\n issueType: data.issueType ?? null,\n issueKey: data.issueKey ?? null,\n issue_numbers: data.issue_numbers ?? [],\n pr_numbers: data.pr_numbers ?? [],\n issueTracker: data.issueTracker ?? null,\n colorHex: data.colorHex ?? null,\n sessionId: data.sessionId ?? null,\n projectPath: data.projectPath ?? null,\n issueUrls: data.issueUrls ?? {},\n prUrls: data.prUrls ?? {},\n draftPrNumber: data.draftPrNumber ?? null,\n oneShot: data.oneShot ?? null,\n complexity: data.complexity ?? null,\n capabilities: data.capabilities ?? [],\n state: data.state ?? null,\n childIssueNumbers: data.childIssueNumbers ?? [],\n parentLoom: data.parentLoom ?? null,\n childIssues: data.childIssues ?? [],\n dependencyMap: data.dependencyMap ?? {},\n mcpConfigPath: data.mcpConfigPath ?? null,\n }\n }\n\n /**\n * Convert worktree path to filename slug per spec section 2.2\n *\n * Algorithm:\n * 1. Trim trailing slashes\n * 2. Replace all path separators (/ or \\) with __ (double underscore)\n * 3. Replace any other non-alphanumeric characters (except _ and -) with -\n * 4. Append .json\n *\n * Example:\n * - Worktree: /Users/jane/dev/repo\n * - Filename: _Users__jane__dev__repo.json\n */\n slugifyPath(worktreePath: string): string {\n // 1. Trim trailing slashes\n let slug = worktreePath.replace(/[/\\\\]+$/, '')\n\n // 2. Replace path separators with triple underscores\n slug = slug.replace(/[/\\\\]/g, '___')\n\n // 3. Replace non-alphanumeric chars (except _ and -) with hyphens\n slug = slug.replace(/[^a-zA-Z0-9_-]/g, '-')\n\n // 4. Append .json\n return `${slug}.json`\n }\n\n /**\n * Get the full path to the metadata file for a worktree\n */\n private getFilePath(worktreePath: string): string {\n const filename = this.slugifyPath(worktreePath)\n return path.join(this.loomsDir, filename)\n }\n\n /**\n * Get the full path to the metadata file for a worktree (public API)\n * Used by other services that need to reference the metadata file location\n * (e.g., MCP servers that need to read loom context)\n */\n getMetadataFilePath(worktreePath: string): string {\n return this.getFilePath(worktreePath)\n }\n\n /**\n * Write metadata for a worktree (spec section 3.1)\n *\n * @param worktreePath - Absolute path to the worktree (used for file naming)\n * @param input - Metadata to write (description plus additional fields)\n */\n async writeMetadata(worktreePath: string, input: WriteMetadataInput): Promise<void> {\n try {\n // 1. Ensure looms directory exists\n await fs.ensureDir(this.loomsDir, { mode: 0o755 })\n\n // 2. Create JSON content\n const content: MetadataFile = {\n description: input.description,\n created_at: new Date().toISOString(),\n version: 1,\n branchName: input.branchName,\n worktreePath: input.worktreePath,\n issueType: input.issueType,\n ...(input.issueKey && { issueKey: input.issueKey }),\n issue_numbers: input.issue_numbers,\n pr_numbers: input.pr_numbers,\n issueTracker: input.issueTracker,\n colorHex: input.colorHex,\n sessionId: input.sessionId,\n projectPath: input.projectPath,\n issueUrls: input.issueUrls,\n prUrls: input.prUrls,\n capabilities: input.capabilities,\n ...(input.draftPrNumber && { draftPrNumber: input.draftPrNumber }),\n ...(input.oneShot && { oneShot: input.oneShot }),\n ...(input.complexity && { complexity: input.complexity }),\n ...(input.state && { state: input.state }),\n ...(input.childIssueNumbers && input.childIssueNumbers.length > 0 && { childIssueNumbers: input.childIssueNumbers }),\n ...(input.parentLoom && { parentLoom: input.parentLoom }),\n ...(input.childIssues && input.childIssues.length > 0 && { childIssues: input.childIssues }),\n ...(input.dependencyMap && Object.keys(input.dependencyMap).length > 0 && { dependencyMap: input.dependencyMap }),\n ...(input.mcpConfigPath && { mcpConfigPath: input.mcpConfigPath }),\n }\n\n // 3. Write to slugified filename\n const filePath = this.getFilePath(worktreePath)\n await fs.writeFile(filePath, JSON.stringify(content, null, 2), { mode: 0o644 })\n\n getLogger().debug(`Metadata written for worktree: ${worktreePath}`)\n } catch (error) {\n // Log warning but don't throw - metadata is supplementary\n getLogger().warn(\n `Failed to write metadata for worktree: ${error instanceof Error ? error.message : String(error)}`\n )\n }\n }\n\n /**\n * Read metadata for a worktree (spec section 3.2)\n *\n * @param worktreePath - Absolute path to the worktree\n * @returns The metadata object with all fields, or null if not found/invalid\n */\n async readMetadata(worktreePath: string): Promise<LoomMetadata | null> {\n try {\n const filePath = this.getFilePath(worktreePath)\n\n // Check if file exists\n if (!(await fs.pathExists(filePath))) {\n return null\n }\n\n // Read and parse JSON\n const content = await fs.readFile(filePath, 'utf8')\n const data: MetadataFile = JSON.parse(content)\n\n if (!data.description) {\n return null\n }\n\n return this.toMetadata(data)\n } catch (error) {\n // Return null on any error (graceful degradation per spec)\n getLogger().debug(\n `Could not read metadata for worktree ${worktreePath}: ${error instanceof Error ? error.message : String(error)}`\n )\n return null\n }\n }\n\n /**\n * List all stored loom metadata files\n *\n * Returns an array of LoomMetadata objects for all valid metadata files\n * in the looms directory. Invalid or unreadable files are skipped.\n *\n * @returns Array of LoomMetadata objects from all stored files\n */\n async listAllMetadata(): Promise<LoomMetadata[]> {\n const results: LoomMetadata[] = []\n\n try {\n // Check if looms directory exists\n if (!(await fs.pathExists(this.loomsDir))) {\n return results\n }\n\n // Read all files in looms directory\n const files = await fs.readdir(this.loomsDir)\n\n // Filter to only .json files and read each\n for (const file of files) {\n if (!file.endsWith('.json')) {\n continue\n }\n\n try {\n const filePath = path.join(this.loomsDir, file)\n const content = await fs.readFile(filePath, 'utf8')\n const data: MetadataFile = JSON.parse(content)\n\n // Skip files without required description field\n if (!data.description) {\n continue\n }\n\n results.push(this.toMetadata(data))\n } catch (error) {\n // Skip individual files that fail to parse (graceful degradation)\n getLogger().debug(\n `Skipping metadata file ${file}: ${error instanceof Error ? error.message : String(error)}`\n )\n }\n }\n } catch (error) {\n // Log error but return empty array (graceful degradation)\n getLogger().debug(\n `Could not list metadata files: ${error instanceof Error ? error.message : String(error)}`\n )\n }\n\n return results\n }\n\n /**\n * Update existing metadata for a worktree by merging new fields\n *\n * Reads the existing metadata file, merges the provided updates,\n * and writes back. Only provided fields are overwritten.\n *\n * @param worktreePath - Absolute path to the worktree\n * @param updates - Partial metadata fields to merge\n */\n async updateMetadata(worktreePath: string, updates: Partial<MetadataFile>): Promise<void> {\n try {\n const filePath = this.getFilePath(worktreePath)\n\n // Check if file exists\n if (!(await fs.pathExists(filePath))) {\n getLogger().warn(`No metadata file to update for worktree: ${worktreePath}`)\n return\n }\n\n // Read existing data\n const content = await fs.readFile(filePath, 'utf8')\n const data: MetadataFile = JSON.parse(content)\n\n // Merge updates\n const merged = { ...data, ...updates }\n\n // Write back\n await fs.writeFile(filePath, JSON.stringify(merged, null, 2), { mode: 0o644 })\n\n getLogger().debug(`Metadata updated for worktree: ${worktreePath}`)\n } catch (error) {\n getLogger().warn(\n `Failed to update metadata for worktree: ${error instanceof Error ? error.message : String(error)}`\n )\n throw error\n }\n }\n\n /**\n * Delete metadata for a worktree (spec section 3.3)\n *\n * Idempotent: silently succeeds if file doesn't exist\n * Non-fatal: logs warning on permission errors but doesn't throw\n *\n * @param worktreePath - Absolute path to the worktree\n */\n async deleteMetadata(worktreePath: string): Promise<void> {\n try {\n const filePath = this.getFilePath(worktreePath)\n\n // Check if file exists - silently return if not\n if (!(await fs.pathExists(filePath))) {\n getLogger().debug(`No metadata file to delete for worktree: ${worktreePath}`)\n return\n }\n\n // Delete the file\n await fs.unlink(filePath)\n getLogger().debug(`Metadata deleted for worktree: ${worktreePath}`)\n } catch (error) {\n // Log warning on permission error but don't throw (per spec section 3.3)\n getLogger().warn(\n `Failed to delete metadata for worktree: ${error instanceof Error ? error.message : String(error)}`\n )\n }\n }\n\n /**\n * Archive metadata for a finished worktree\n *\n * Moves the metadata file to the finished/ subdirectory and adds\n * status: 'finished' and finishedAt timestamp fields.\n *\n * Idempotent: silently succeeds if source file doesn't exist\n * Non-fatal: logs warning on errors but doesn't throw\n *\n * @param worktreePath - Absolute path to the worktree\n */\n async archiveMetadata(worktreePath: string): Promise<void> {\n try {\n const filename = this.slugifyPath(worktreePath)\n const sourcePath = path.join(this.loomsDir, filename)\n\n // Check if source file exists - silently return if not (idempotent)\n if (!(await fs.pathExists(sourcePath))) {\n getLogger().debug(`No metadata file to archive for worktree: ${worktreePath}`)\n return\n }\n\n // Read existing metadata\n const content = await fs.readFile(sourcePath, 'utf8')\n const data: MetadataFile = JSON.parse(content)\n\n // Add finished status and timestamp\n const finishedData = {\n ...data,\n status: 'finished' as const,\n finishedAt: new Date().toISOString(),\n }\n\n // Ensure finished directory exists\n await fs.ensureDir(this.finishedDir, { mode: 0o755 })\n\n // Write to finished subdirectory\n const destPath = path.join(this.finishedDir, filename)\n await fs.writeFile(destPath, JSON.stringify(finishedData, null, 2), { mode: 0o644 })\n\n // Delete original file\n await fs.unlink(sourcePath)\n\n getLogger().debug(`Metadata archived for worktree: ${worktreePath}`)\n } catch (error) {\n // Log warning but don't throw - archiving is supplementary\n getLogger().warn(\n `Failed to archive metadata for worktree: ${error instanceof Error ? error.message : String(error)}`\n )\n }\n }\n\n /**\n * List all finished loom metadata files\n *\n * Returns an array of LoomMetadata objects for all finished looms\n * in the finished/ subdirectory, sorted by finishedAt in descending order\n * (most recently finished first).\n *\n * @returns Array of LoomMetadata objects from finished files, sorted by finishedAt desc\n */\n async listFinishedMetadata(): Promise<LoomMetadata[]> {\n const results: LoomMetadata[] = []\n\n try {\n // Check if finished directory exists\n if (!(await fs.pathExists(this.finishedDir))) {\n return results\n }\n\n // Read all files in finished directory\n const files = await fs.readdir(this.finishedDir)\n\n // Filter to only .json files and read each\n for (const file of files) {\n if (!file.endsWith('.json')) {\n continue\n }\n\n try {\n const filePath = path.join(this.finishedDir, file)\n const content = await fs.readFile(filePath, 'utf8')\n const data = JSON.parse(content) as MetadataFile & { status?: string; finishedAt?: string }\n\n // Skip files without required description field\n if (!data.description) {\n continue\n }\n\n const metadata = this.toMetadata(data)\n // Add finished-specific fields\n metadata.status = (data.status as 'active' | 'finished') ?? 'finished'\n metadata.finishedAt = data.finishedAt ?? null\n\n results.push(metadata)\n } catch (error) {\n // Skip individual files that fail to parse (graceful degradation)\n getLogger().warn(\n `Skipping finished metadata file ${file}: ${error instanceof Error ? error.message : String(error)}`\n )\n }\n }\n\n // Sort by finishedAt descending (most recently finished first)\n results.sort((a, b) => {\n const aTime = a.finishedAt ? new Date(a.finishedAt).getTime() : 0\n const bTime = b.finishedAt ? new Date(b.finishedAt).getTime() : 0\n return bTime - aTime\n })\n } catch (error) {\n // Log error but return empty array (graceful degradation)\n getLogger().warn(\n `Could not list finished metadata files: ${error instanceof Error ? error.message : String(error)}`\n )\n }\n\n return results\n }\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/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 { createNeonProviderFromSettings } from '../utils/neon-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 neonProvider = createNeonProviderFromSettings(settings)\n const databaseManager = new DatabaseManager(neonProvider, 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 +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 { 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\n\t\tconst settings = await this.settingsManager.loadSettings()\n\t\tconst shouldLoadEnv = settings.sourceEnvOnStart ?? false\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)\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)\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":["message"]}
@@ -1,35 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- IgniteCommand,
4
- WorktreeValidationError
5
- } from "./chunk-IR74O2F6.js";
6
- import "./chunk-TZNNJLGT.js";
7
- import "./chunk-NOMQ5RFG.js";
8
- import "./chunk-5UFGO4ZT.js";
9
- import "./chunk-RMLADZRY.js";
10
- import "./chunk-4232AHNQ.js";
11
- import "./chunk-HLDY5S4C.js";
12
- import "./chunk-3GTUXW26.js";
13
- import "./chunk-GYCR2LOU.js";
14
- import "./chunk-NCPZYQ4B.js";
15
- import "./chunk-GMDSYLI6.js";
16
- import "./chunk-SQYHPBFP.js";
17
- import "./chunk-VNYWBHKR.js";
18
- import "./chunk-LE2NOUTN.js";
19
- import "./chunk-UHIBKD73.js";
20
- import "./chunk-5LTID2AF.js";
21
- import "./chunk-LHDD4JHC.js";
22
- import "./chunk-NH3QZYE5.js";
23
- import "./chunk-CV47VCMQ.js";
24
- import "./chunk-Y3RX7LZT.js";
25
- import "./chunk-WG4MLJ6J.js";
26
- import "./chunk-3RXYOBME.js";
27
- import "./chunk-ET6A2JR4.js";
28
- import "./chunk-YRCEOQPX.js";
29
- import "./chunk-ZAXRQLK3.js";
30
- import "./chunk-H2SSF24U.js";
31
- export {
32
- IgniteCommand,
33
- WorktreeValidationError
34
- };
35
- //# sourceMappingURL=ignite-IO4LXVXJ.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/issues.ts"],"sourcesContent":["import os from 'os'\nimport path from 'path'\nimport fs from 'fs-extra'\nimport crypto from 'crypto'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { IssueTrackerFactory } from '../lib/IssueTrackerFactory.js'\nimport { findMainWorktreePathWithSettings } from '../utils/git.js'\nimport { fetchGitHubIssueList, fetchGitHubPRList } from '../utils/github.js'\nimport { fetchLinearIssueList } from '../utils/linear.js'\nimport { fetchJiraIssueList } from '../utils/jira.js'\nimport { JiraApiClient } from '../lib/providers/jira/index.js'\nimport { getLogger } from '../utils/logger-context.js'\n\n/**\n * Unified output interface for issues from any provider\n */\nexport interface IssueListItem {\n id: string\n title: string\n updatedAt: string\n url: string\n state: string\n type?: 'issue' | 'pr'\n}\n\n/**\n * File-based cache structure (follows UpdateNotifier pattern)\n */\ninterface IssuesCacheFile {\n timestamp: number // Date.now() when cached\n projectPath: string // for verification\n provider: string // 'github' | 'linear' | 'jira'\n data: IssueListItem[]\n}\n\n// Cache configuration\nconst CACHE_TTL_MS = 2 * 60 * 1000 // 2 minutes\nconst CACHE_DIR = path.join(os.homedir(), '.config', 'iloom-ai', 'cache')\n\n/**\n * Generate a deterministic cache file path from project path + provider\n */\nfunction getCacheFilePath(projectPath: string, provider: string, limit: number, sprint?: string, mine?: boolean): string {\n const hash = crypto.createHash('md5').update(`${projectPath}:${provider}:${limit}:${sprint ?? ''}:${mine ? 'mine' : ''}`).digest('hex').slice(0, 12)\n return path.join(CACHE_DIR, `issues-${hash}.json`)\n}\n\n/**\n * Read cache file, return null if missing/expired/corrupted\n * Follows UpdateNotifier.getCachedCheck pattern\n */\nasync function readCacheFile(cacheFilePath: string): Promise<IssueListItem[] | null> {\n try {\n if (!fs.existsSync(cacheFilePath)) return null\n const content = await fs.readFile(cacheFilePath, 'utf8')\n const cache = JSON.parse(content) as IssuesCacheFile\n if (Date.now() - cache.timestamp < CACHE_TTL_MS) return cache.data\n return null // expired\n } catch {\n return null // corrupted or unreadable, treat as cache miss\n }\n}\n\n/**\n * Write cache file following UpdateNotifier.saveCacheFile pattern\n */\nasync function writeCacheFile(\n cacheFilePath: string,\n data: IssueListItem[],\n projectPath: string,\n provider: string,\n): Promise<void> {\n try {\n await fs.ensureDir(CACHE_DIR)\n const cache: IssuesCacheFile = { timestamp: Date.now(), projectPath, provider, data }\n await fs.writeFile(cacheFilePath, JSON.stringify(cache, null, 2), 'utf8')\n } catch {\n // Cache write failure is non-fatal, just log debug\n getLogger().debug(`Failed to write issues cache to ${cacheFilePath}`)\n }\n}\n\nexport interface IssuesCommandOptions {\n projectPath?: string | undefined\n limit?: number | undefined\n sprint?: string | undefined\n mine?: boolean | undefined\n}\n\n/**\n * IssuesCommand: List open issues from the configured issue tracker\n *\n * Returns JSON array of issues. Uses file-based caching with ~2 minute TTL.\n * Follows the ProjectsCommand pattern for structure.\n */\nexport class IssuesCommand {\n private readonly settingsManager: SettingsManager\n\n constructor(settingsManager?: SettingsManager) {\n this.settingsManager = settingsManager ?? new SettingsManager()\n }\n\n /**\n * Execute the issues command\n * @param options - Command options\n * @returns Array of issue list items\n */\n async execute(options?: IssuesCommandOptions): Promise<IssueListItem[]> {\n const logger = getLogger()\n const limit = options?.limit ?? 100\n const sprint = options?.sprint\n const mine = options?.mine\n\n // 1. Resolve project root\n let resolvedProjectPath: string\n if (options?.projectPath) {\n resolvedProjectPath = options.projectPath\n } else {\n try {\n resolvedProjectPath = await findMainWorktreePathWithSettings()\n } catch {\n logger.debug('Failed to resolve worktree path, falling back to cwd')\n resolvedProjectPath = process.cwd()\n }\n }\n\n // 2. Load settings from resolved root\n const settings = await this.settingsManager.loadSettings(resolvedProjectPath)\n\n // 3. Determine provider\n const provider = IssueTrackerFactory.getProviderName(settings)\n\n // Warn if Jira-only flags used with non-Jira provider (--sprint is Jira-only; --mine works across all providers)\n if (provider !== 'jira' && sprint) {\n logger.warn('--sprint flag is only supported with the Jira issue tracker. Ignoring.')\n }\n\n // 4. Check file-based cache\n const cacheFilePath = getCacheFilePath(resolvedProjectPath, provider, limit, sprint, mine)\n const cached = await readCacheFile(cacheFilePath)\n if (cached !== null) {\n logger.debug(`Returning cached issues (${cached.length} items)`)\n // Backfill type field for cache entries from before PR support was added\n return cached.map(item => ({ type: 'issue' as const, ...item }))\n }\n\n // 5. Fetch issues based on provider\n let results: IssueListItem[]\n\n if (provider === 'github') {\n results = await fetchGitHubIssueList({\n limit,\n cwd: resolvedProjectPath,\n ...(mine ? { mine } : {}),\n })\n } else if (provider === 'linear') {\n const teamId = settings.issueManagement?.linear?.teamId\n if (!teamId) {\n throw new Error(\n 'Linear team ID not configured. Set issueManagement.linear.teamId in your settings.json.',\n )\n }\n const apiToken = settings.issueManagement?.linear?.apiToken ?? process.env.LINEAR_API_TOKEN\n results = await fetchLinearIssueList(teamId, {\n limit,\n ...(apiToken ? { apiToken } : {}),\n ...(mine ? { mine } : {}),\n })\n } else if (provider === 'jira') {\n const jiraSettings = settings.issueManagement?.jira\n const host = jiraSettings?.host\n if (!host) {\n throw new Error(\n 'Jira host not configured. Set issueManagement.jira.host in your settings.json.',\n )\n }\n const username = jiraSettings?.username\n if (!username) {\n throw new Error(\n 'Jira username not configured. Set issueManagement.jira.username in your settings.json.',\n )\n }\n const apiToken = jiraSettings?.apiToken\n if (!apiToken) {\n throw new Error(\n 'Jira API token not configured. Set issueManagement.jira.apiToken in your settings.json or settings.local.json.',\n )\n }\n const projectKey = jiraSettings?.projectKey\n if (!projectKey) {\n throw new Error(\n 'Jira project key not configured. Set issueManagement.jira.projectKey in your settings.json.',\n )\n }\n const doneStatuses = jiraSettings?.doneStatuses\n const client = new JiraApiClient({ host, username, apiToken })\n results = await fetchJiraIssueList(client, { host, projectKey, doneStatuses, limit, sprint, mine })\n } else {\n throw new Error(`Unsupported issue tracker provider: ${provider}`)\n }\n\n // Tag issues with type\n results.forEach(item => { item.type = 'issue' })\n\n // 6. Fetch PRs from GitHub (PRs are a GitHub concept regardless of issue tracker)\n // TODO(bitbucket): detect bitbucket configuration and fetch PRs from Bitbucket instead of GitHub when relevant\n try {\n const prs = await fetchGitHubPRList({\n limit,\n cwd: resolvedProjectPath,\n ...(mine ? { mine } : {}),\n })\n const prItems: IssueListItem[] = prs.map(pr => ({ ...pr, type: 'pr' as const }))\n results = [...results, ...prItems]\n } catch (error) {\n // Only catch expected, non-fatal errors from gh CLI\n // Per CLAUDE.md: \"DO NOT SWALLOW ERRORS\" -- must check specifically\n const stderr = (error as NodeJS.ErrnoException & { stderr?: string }).stderr ?? ''\n const isExpectedError = error instanceof Error && (\n error.message.includes('not logged in') ||\n error.message.includes('auth login') ||\n error.message.includes('rate limit') ||\n error.message.includes('ETIMEDOUT') ||\n error.message.includes('ECONNREFUSED') ||\n error.message.includes('no git remotes found') ||\n stderr.includes('not logged in') ||\n stderr.includes('rate limit')\n )\n if (isExpectedError) {\n logger.warn(`PR fetch failed (non-fatal), continuing with issues only: ${error.message}`)\n } else {\n throw error // Re-throw unexpected errors -- do not swallow\n }\n }\n\n // 7. Sort by updatedAt descending and apply limit\n results.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime())\n results = results.slice(0, limit)\n\n // 8. Write results to cache file\n await writeCacheFile(cacheFilePath, results, resolvedProjectPath, provider)\n\n return results\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,27 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- ScriptCommandBase
4
- } from "./chunk-V4STTBQD.js";
5
- import "./chunk-UDCI3QTS.js";
6
- import "./chunk-3GTUXW26.js";
7
- import "./chunk-NCPZYQ4B.js";
8
- import "./chunk-LE2NOUTN.js";
9
- import "./chunk-3RXYOBME.js";
10
- import "./chunk-ET6A2JR4.js";
11
- import "./chunk-YRCEOQPX.js";
12
- import "./chunk-ZAXRQLK3.js";
13
- import "./chunk-H2SSF24U.js";
14
-
15
- // src/commands/lint.ts
16
- var LintCommand = class extends ScriptCommandBase {
17
- getScriptName() {
18
- return "lint";
19
- }
20
- getScriptDisplayName() {
21
- return "Lint";
22
- }
23
- };
24
- export {
25
- LintCommand
26
- };
27
- //# sourceMappingURL=lint-BSWRMGPZ.js.map
@@ -1,11 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- createNeonProviderFromSettings
4
- } from "./chunk-VMZG66UV.js";
5
- import "./chunk-CV47VCMQ.js";
6
- import "./chunk-ZAXRQLK3.js";
7
- import "./chunk-H2SSF24U.js";
8
- export {
9
- createNeonProviderFromSettings
10
- };
11
- //# sourceMappingURL=neon-helpers-HWIYRKOW.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/open.ts"],"sourcesContent":["import path from 'path'\nimport fs from 'fs-extra'\nimport { execa } from 'execa'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { ProjectCapabilityDetector } from '../lib/ProjectCapabilityDetector.js'\nimport { DevServerManager } from '../lib/DevServerManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { IdentifierParser } from '../utils/IdentifierParser.js'\nimport { openBrowser } from '../utils/browser.js'\nimport { getWorkspacePort } from '../utils/port.js'\nimport { extractIssueNumber } from '../utils/git.js'\nimport { logger } from '../utils/logger.js'\nimport { extractSettingsOverrides } from '../utils/cli-overrides.js'\nimport type { GitWorktree } from '../types/worktree.js'\n\nexport interface OpenCommandInput {\n\tidentifier?: string\n\targs?: string[]\n}\n\ninterface ParsedOpenInput {\n\ttype: 'issue' | 'pr' | 'branch' | '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// Ensure dev server is running on the port\n\t\tconst serverReady = await this.devServerManager.ensureServerRunning(\n\t\t\tworktree.path,\n\t\t\tport\n\t\t)\n\n\t\tif (!serverReady) {\n\t\t\tlogger.warn(\n\t\t\t\t`Dev server failed to start on port ${port}. Opening browser anyway...`\n\t\t\t)\n\t\t}\n\n\t\t// Construct URL and open browser\n\t\tconst url = `http://localhost:${port}`\n\t\tlogger.info(`Opening browser: ${url}`)\n\t\tawait openBrowser(url)\n\t\tlogger.success('Browser opened')\n\t}\n\n\t/**\n\t * Run CLI tool directly from worktree bin path (NO SYMLINKS!)\n\t */\n\tprivate async runCLITool(\n\t\tworktreePath: string,\n\t\tbinEntries: Record<string, string>,\n\t\targs: string[]\n\t): Promise<void> {\n\t\t// Validate binEntries exist\n\t\tif (Object.keys(binEntries).length === 0) {\n\t\t\tthrow new Error('No bin entries found in package.json')\n\t\t}\n\n\t\t// Get first bin entry (deterministic)\n\t\tconst firstEntry = Object.entries(binEntries)[0]\n\t\tif (!firstEntry) {\n\t\t\tthrow new Error('No bin entries found in package.json')\n\t\t}\n\t\tconst [binName, binPath] = firstEntry\n\t\tlogger.debug(`Using bin entry: ${binName} -> ${binPath}`)\n\n\t\t// CRITICAL: Construct absolute path (NO SYMLINKS!)\n\t\tconst binFilePath = path.resolve(worktreePath, binPath)\n\t\tlogger.debug(`Resolved bin file path: ${binFilePath}`)\n\n\t\t// Verify file exists\n\t\tif (!(await fs.pathExists(binFilePath))) {\n\t\t\tthrow new Error(\n\t\t\t\t`CLI executable not found: ${binFilePath}\\n` +\n\t\t\t\t\t`Make sure the project is built (run 'il start' first)`\n\t\t\t)\n\t\t}\n\n\t\t// Execute with Node.js\n\t\tlogger.info(`Running CLI: node ${binFilePath} ${args.join(' ')}`)\n\t\tawait execa('node', [binFilePath, ...args], {\n\t\t\tstdio: 'inherit', // Allow interactive CLIs (prompts, colors, etc.)\n\t\t\tcwd: worktreePath, // Execute in worktree context\n\t\t\tenv: process.env, // Inherit environment\n\t\t})\n\t}\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/run.ts"],"sourcesContent":["import path from 'path'\nimport fs from 'fs-extra'\nimport { execa } from 'execa'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { ProjectCapabilityDetector } from '../lib/ProjectCapabilityDetector.js'\nimport { DevServerManager } from '../lib/DevServerManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { IdentifierParser } from '../utils/IdentifierParser.js'\nimport { openBrowser } from '../utils/browser.js'\nimport { getWorkspacePort } from '../utils/port.js'\nimport { extractIssueNumber } from '../utils/git.js'\nimport { logger } from '../utils/logger.js'\nimport { extractSettingsOverrides } from '../utils/cli-overrides.js'\nimport type { GitWorktree } from '../types/worktree.js'\n\nexport interface RunCommandInput {\n\tidentifier?: string\n\targs?: string[]\n}\n\ninterface ParsedRunInput {\n\ttype: 'issue' | 'pr' | 'branch' | '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// Ensure dev server is running on the port\n\t\tconst serverReady = await this.devServerManager.ensureServerRunning(\n\t\t\tworktree.path,\n\t\t\tport\n\t\t)\n\n\t\tif (!serverReady) {\n\t\t\tlogger.warn(\n\t\t\t\t`Dev server failed to start on port ${port}. Opening browser anyway...`\n\t\t\t)\n\t\t}\n\n\t\t// Construct URL and open browser\n\t\tconst url = `http://localhost:${port}`\n\t\tlogger.info(`Opening browser: ${url}`)\n\t\tawait openBrowser(url)\n\t\tlogger.success('Browser opened')\n\t}\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/summary.ts"],"sourcesContent":["import path from 'path'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { MetadataManager } from '../lib/MetadataManager.js'\nimport { SessionSummaryService } from '../lib/SessionSummaryService.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { PRManager } from '../lib/PRManager.js'\nimport { getLogger } from '../utils/logger-context.js'\nimport { extractIssueNumber } from '../utils/git.js'\nimport type { GitWorktree } from '../types/worktree.js'\nimport type { SummaryResult } from '../types/index.js'\n\n/**\n * Options for the summary command\n */\nexport interface SummaryOptions {\n\twithComment?: boolean\n\tjson?: boolean\n}\n\n/**\n * Input for the summary command\n */\nexport interface SummaryCommandInput {\n\tidentifier?: string | undefined\n\toptions: SummaryOptions\n}\n\n/**\n * Parsed input with loom information\n */\ninterface ParsedSummaryInput {\n\tworktree: GitWorktree\n\tloomType: 'issue' | 'pr' | 'branch' | 'epic'\n\tissueNumber?: string | number | undefined\n}\n\n/**\n * SummaryCommand - Generate and optionally post Claude session summaries\n *\n * This command allows generating the session summary without going through\n * the full `il finish` workflow. It can:\n * 1. Generate a summary and print it to stdout\n * 2. Optionally post the summary as a comment (--with-comment flag)\n */\nexport class SummaryCommand {\n\tconstructor(\n\t\tprivate gitWorktreeManager = new GitWorktreeManager(),\n\t\tprivate metadataManager = new MetadataManager(),\n\t\tprivate sessionSummaryService = new SessionSummaryService(),\n\t\tprivate settingsManager = new SettingsManager()\n\t) {}\n\n\t/**\n\t * Determine PR number based on merge mode and metadata\n\t * Returns undefined if should post to issue instead\n\t */\n\tprivate async getPRNumberForPosting(\n\t\tworktreePath: string,\n\t\tbranchName: string\n\t): Promise<number | undefined> {\n\t\tconst settings = await this.settingsManager.loadSettings(worktreePath)\n\t\tconst mergeMode = settings.mergeBehavior?.mode ?? 'local'\n\n\t\tif (mergeMode === 'github-draft-pr') {\n\t\t\tconst metadata = await this.metadataManager.readMetadata(worktreePath)\n\t\t\treturn metadata?.draftPrNumber ?? undefined\n\t\t}\n\n\t\tif (mergeMode === 'github-pr') {\n\t\t\tconst prManager = new PRManager(settings)\n\t\t\tconst existingPR = await prManager.checkForExistingPR(branchName, worktreePath)\n\t\t\treturn existingPR?.number\n\t\t}\n\n\t\treturn undefined // local mode - post to issue\n\t}\n\n\t/**\n\t * Execute the summary command\n\t *\n\t * @param input - Command input containing identifier and options\n\t * @returns SummaryResult when in JSON mode, void otherwise\n\t */\n\tasync execute(input: SummaryCommandInput): Promise<SummaryResult | void> {\n\t\tconst logger = getLogger()\n\n\t\t// 1. Find the loom by identifier (or auto-detect from current directory)\n\t\tconst parsed = input.identifier?.trim()\n\t\t\t? await this.findLoom(input.identifier.trim())\n\t\t\t: await this.autoDetectFromCurrentDirectory()\n\n\t\t// 2. Generate the summary (service handles session ID internally, including deterministic fallback)\n\t\tconst result = await this.sessionSummaryService.generateSummary(\n\t\t\tparsed.worktree.path,\n\t\t\tparsed.worktree.branch,\n\t\t\tparsed.loomType,\n\t\t\tparsed.issueNumber\n\t\t)\n\n\t\t// 4. Apply attribution if --with-comment is used (so output matches what will be posted)\n\t\tlet displaySummary = result.summary\n\t\tif (input.options.withComment && parsed.loomType !== 'branch') {\n\t\t\tdisplaySummary = await this.sessionSummaryService.applyAttribution(\n\t\t\t\tresult.summary,\n\t\t\t\tparsed.worktree.path\n\t\t\t)\n\t\t}\n\n\t\t// 5. In JSON mode, return the structured result\n\t\tif (input.options.json) {\n\t\t\tconst jsonResult: SummaryResult = {\n\t\t\t\tsummary: displaySummary,\n\t\t\t\tsessionId: result.sessionId,\n\t\t\t\tbranchName: parsed.worktree.branch,\n\t\t\t\tloomType: parsed.loomType,\n\t\t\t}\n\t\t\t// Only include issueNumber if defined\n\t\t\tif (parsed.issueNumber !== undefined) {\n\t\t\t\tjsonResult.issueNumber = parsed.issueNumber\n\t\t\t}\n\t\t\treturn jsonResult\n\t\t}\n\n\t\t// 6. Print the summary to stdout (intentionally using console.log for piping/redirection)\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.log(displaySummary)\n\n\t\t// 7. Optionally post the summary as a comment\n\t\tif (input.options.withComment) {\n\t\t\t// Skip posting for branch type looms (no issue to comment on)\n\t\t\tif (parsed.loomType === 'branch') {\n\t\t\t\tlogger.debug('Skipping comment posting: branch type looms have no associated issue')\n\t\t\t} else if (parsed.issueNumber !== undefined) {\n\t\t\t\t// Determine if we should post to PR instead of issue\n\t\t\t\tconst prNumber = await this.getPRNumberForPosting(\n\t\t\t\t\tparsed.worktree.path,\n\t\t\t\t\tparsed.worktree.branch\n\t\t\t\t)\n\t\t\t\tawait this.sessionSummaryService.postSummary(\n\t\t\t\t\tparsed.issueNumber,\n\t\t\t\t\tresult.summary,\n\t\t\t\t\tparsed.worktree.path,\n\t\t\t\t\tprNumber\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Find a loom by identifier\n\t *\n\t * Supports:\n\t * - Numeric identifiers (issue numbers): \"123\", \"#123\"\n\t * - PR identifiers: \"pr/123\"\n\t * - Branch names: \"my-feature-branch\"\n\t */\n\tprivate async findLoom(identifier: string): Promise<ParsedSummaryInput> {\n\t\t// Remove # prefix if present and trim whitespace\n\t\tconst cleanId = identifier.replace(/^#/, '').trim()\n\n\t\t// Check for PR pattern: pr/123 or PR/123\n\t\tconst prMatch = cleanId.match(/^pr\\/(\\d+)$/i)\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tconst worktree = await this.gitWorktreeManager.findWorktreeForPR(prNumber, '')\n\t\t\tif (worktree) {\n\t\t\t\tconst metadata = await this.metadataManager.readMetadata(worktree.path)\n\t\t\t\treturn {\n\t\t\t\t\tworktree,\n\t\t\t\t\tloomType: 'pr',\n\t\t\t\t\tissueNumber: metadata?.pr_numbers?.[0] ?? String(prNumber),\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new Error(`No loom found for identifier: ${identifier}`)\n\t\t}\n\n\t\t// Check if input is numeric (issue number)\n\t\tconst numericMatch = cleanId.match(/^(\\d+)$/)\n\t\tif (numericMatch?.[1]) {\n\t\t\tconst issueNumber = parseInt(numericMatch[1], 10)\n\n\t\t\t// Try issue first\n\t\t\tconst issueWorktree = await this.gitWorktreeManager.findWorktreeForIssue(issueNumber)\n\t\t\tif (issueWorktree) {\n\t\t\t\tconst metadata = await this.metadataManager.readMetadata(issueWorktree.path)\n\t\t\t\treturn {\n\t\t\t\t\tworktree: issueWorktree,\n\t\t\t\t\tloomType: metadata?.issueType ?? 'issue',\n\t\t\t\t\tissueNumber: metadata?.issueKey ?? metadata?.issue_numbers?.[0] ?? String(issueNumber),\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Then try PR\n\t\t\tconst prWorktree = await this.gitWorktreeManager.findWorktreeForPR(issueNumber, '')\n\t\t\tif (prWorktree) {\n\t\t\t\tconst metadata = await this.metadataManager.readMetadata(prWorktree.path)\n\t\t\t\treturn {\n\t\t\t\t\tworktree: prWorktree,\n\t\t\t\t\tloomType: 'pr',\n\t\t\t\t\tissueNumber: metadata?.pr_numbers?.[0] ?? String(issueNumber),\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthrow new Error(`No loom found for identifier: ${identifier}`)\n\t\t}\n\n\t\t// Check for alphanumeric issue identifier (Linear/Jira style: ABC-123)\n\t\tconst alphanumericMatch = cleanId.match(/^([A-Za-z]+-\\d+)$/)\n\t\tif (alphanumericMatch?.[1]) {\n\t\t\tconst alphanumericId = alphanumericMatch[1]\n\t\t\tconst issueWorktree = await this.gitWorktreeManager.findWorktreeForIssue(alphanumericId)\n\t\t\tif (issueWorktree) {\n\t\t\t\tconst metadata = await this.metadataManager.readMetadata(issueWorktree.path)\n\t\t\t\treturn {\n\t\t\t\t\tworktree: issueWorktree,\n\t\t\t\t\tloomType: metadata?.issueType ?? 'issue',\n\t\t\t\t\tissueNumber: metadata?.issueKey ?? metadata?.issue_numbers?.[0] ?? alphanumericId,\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new Error(`No loom found for identifier: ${identifier}`)\n\t\t}\n\n\t\t// Treat as branch name\n\t\tconst branchWorktree = await this.gitWorktreeManager.findWorktreeForBranch(cleanId)\n\t\tif (branchWorktree) {\n\t\t\tconst metadata = await this.metadataManager.readMetadata(branchWorktree.path)\n\t\t\tconst loomType = metadata?.issueType ?? 'branch'\n\n\t\t\t// For branch looms, try to get issue number from metadata\n\t\t\tlet issueNumber: string | number | undefined\n\t\t\tif (loomType === 'issue' && (metadata?.issueKey || metadata?.issue_numbers?.[0])) {\n\t\t\t\tissueNumber = metadata?.issueKey ?? metadata?.issue_numbers?.[0]\n\t\t\t} else if (loomType === 'pr' && metadata?.pr_numbers?.[0]) {\n\t\t\t\tissueNumber = metadata.pr_numbers[0]\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tworktree: branchWorktree,\n\t\t\t\tloomType,\n\t\t\t\tissueNumber,\n\t\t\t}\n\t\t}\n\n\t\tthrow new Error(`No loom found for identifier: ${identifier}`)\n\t}\n\n\t/**\n\t * Auto-detect loom from current working directory\n\t * Ports logic from FinishCommand.autoDetectFromCurrentDirectory()\n\t *\n\t * Detection strategy:\n\t * 1. Check current directory name for PR pattern (_pr_N suffix)\n\t * 2. Check current directory name for issue pattern (issue-N or -N-)\n\t * 3. Get current branch and check for issue pattern\n\t * 4. Fall back to using current branch as branch loom\n\t */\n\tprivate async autoDetectFromCurrentDirectory(): Promise<ParsedSummaryInput> {\n\t\tconst logger = getLogger()\n\t\tconst currentDir = path.basename(process.cwd())\n\n\t\t// Check for PR worktree pattern: _pr_N suffix\n\t\tconst prPattern = /_pr_(\\d+)$/\n\t\tconst prMatch = currentDir.match(prPattern)\n\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tlogger.debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`)\n\n\t\t\tconst worktree = await this.gitWorktreeManager.findWorktreeForPR(prNumber, '')\n\t\t\tif (worktree) {\n\t\t\t\tconst metadata = await this.metadataManager.readMetadata(worktree.path)\n\t\t\t\treturn {\n\t\t\t\t\tworktree,\n\t\t\t\t\tloomType: 'pr',\n\t\t\t\t\tissueNumber: metadata?.pr_numbers?.[0] ?? String(prNumber),\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new Error(`No loom found for auto-detected PR #${prNumber}`)\n\t\t}\n\n\t\t// Check for issue pattern in directory name\n\t\tconst issueNumber = extractIssueNumber(currentDir)\n\n\t\tif (issueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${issueNumber} from directory: ${currentDir}`)\n\n\t\t\tconst worktree = await this.gitWorktreeManager.findWorktreeForIssue(issueNumber)\n\t\t\tif (worktree) {\n\t\t\t\tconst metadata = await this.metadataManager.readMetadata(worktree.path)\n\t\t\t\treturn {\n\t\t\t\t\tworktree,\n\t\t\t\t\tloomType: metadata?.issueType ?? 'issue',\n\t\t\t\t\tissueNumber: metadata?.issueKey ?? metadata?.issue_numbers?.[0] ?? String(issueNumber),\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new Error(`No loom found for auto-detected issue #${issueNumber}`)\n\t\t}\n\n\t\t// Fallback: get current branch name\n\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\tconst currentBranch = repoInfo.currentBranch\n\n\t\tif (!currentBranch) {\n\t\t\tthrow new Error(\n\t\t\t\t'Could not auto-detect loom. Please provide an issue number, PR number, or branch name.\\n' +\n\t\t\t\t'Expected directory pattern: feat/issue-XX-description OR worktree with _pr_N suffix'\n\t\t\t)\n\t\t}\n\n\t\t// Try to extract issue from branch name\n\t\tconst branchIssueNumber = extractIssueNumber(currentBranch)\n\t\tif (branchIssueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`)\n\n\t\t\tconst worktree = await this.gitWorktreeManager.findWorktreeForIssue(branchIssueNumber)\n\t\t\tif (worktree) {\n\t\t\t\tconst metadata = await this.metadataManager.readMetadata(worktree.path)\n\t\t\t\treturn {\n\t\t\t\t\tworktree,\n\t\t\t\t\tloomType: metadata?.issueType ?? 'issue',\n\t\t\t\t\tissueNumber: metadata?.issueKey ?? metadata?.issue_numbers?.[0] ?? String(branchIssueNumber),\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Last resort: use current branch as branch loom\n\t\tconst branchWorktree = await this.gitWorktreeManager.findWorktreeForBranch(currentBranch)\n\t\tif (branchWorktree) {\n\t\t\tconst metadata = await this.metadataManager.readMetadata(branchWorktree.path)\n\t\t\tconst loomType = metadata?.issueType ?? 'branch'\n\n\t\t\t// For branch looms, try to get issue number from metadata\n\t\t\tlet resolvedIssueNumber: string | number | undefined\n\t\t\tif (loomType === 'issue' && (metadata?.issueKey || metadata?.issue_numbers?.[0])) {\n\t\t\t\tresolvedIssueNumber = metadata?.issueKey ?? metadata?.issue_numbers?.[0]\n\t\t\t} else if (loomType === 'pr' && metadata?.pr_numbers?.[0]) {\n\t\t\t\tresolvedIssueNumber = metadata.pr_numbers[0]\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tworktree: branchWorktree,\n\t\t\t\tloomType,\n\t\t\t\tissueNumber: resolvedIssueNumber,\n\t\t\t}\n\t\t}\n\n\t\tthrow new Error(\n\t\t\t`Could not auto-detect loom from current directory or branch: ${currentBranch}\\n` +\n\t\t\t'Please provide an issue number, PR number, or branch name.'\n\t\t)\n\t}\n}\n"],"mappings":"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","names":[]}
@@ -1,27 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- ScriptCommandBase
4
- } from "./chunk-V4STTBQD.js";
5
- import "./chunk-UDCI3QTS.js";
6
- import "./chunk-3GTUXW26.js";
7
- import "./chunk-NCPZYQ4B.js";
8
- import "./chunk-LE2NOUTN.js";
9
- import "./chunk-3RXYOBME.js";
10
- import "./chunk-ET6A2JR4.js";
11
- import "./chunk-YRCEOQPX.js";
12
- import "./chunk-ZAXRQLK3.js";
13
- import "./chunk-H2SSF24U.js";
14
-
15
- // src/commands/test.ts
16
- var TestCommand = class extends ScriptCommandBase {
17
- getScriptName() {
18
- return "test";
19
- }
20
- getScriptDisplayName() {
21
- return "Test";
22
- }
23
- };
24
- export {
25
- TestCommand
26
- };
27
- //# sourceMappingURL=test-6JH4FE2X.js.map