@iloom/cli 0.11.1 → 0.12.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 (286) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +153 -13
  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-IENAE2CP.js +14 -0
  7. package/dist/ClaudeService-YIJCZUUB.js +13 -0
  8. package/dist/GitHubService-UTAYZXL3.js +12 -0
  9. package/dist/IssueTrackerFactory-2OI7YIN6.js +15 -0
  10. package/dist/{LoomLauncher-5AZU2F5I.js → LoomLauncher-3TSFW7QP.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 +153 -13
  15. package/dist/{SettingsManager-WQ5NSGAH.js → SettingsManager-BMQCAXPP.js} +13 -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-DMWSIME6.js +27 -0
  28. package/dist/{chunk-UHIBKD73.js → chunk-35CBWAJL.js} +13 -32
  29. package/dist/{chunk-UHIBKD73.js.map → chunk-35CBWAJL.js.map} +1 -1
  30. package/dist/{chunk-YRCEOQPX.js → chunk-4JZEQBWV.js} +4 -3
  31. package/dist/chunk-4JZEQBWV.js.map +1 -0
  32. package/dist/{chunk-ET6A2JR4.js → chunk-653XBU3L.js} +111 -18
  33. package/dist/chunk-653XBU3L.js.map +1 -0
  34. package/dist/{chunk-G2MNSPA4.js → chunk-772N5WCA.js} +2 -2
  35. package/dist/{chunk-WG4MLJ6J.js → chunk-7RCUWU3I.js} +2 -2
  36. package/dist/chunk-7RCUWU3I.js.map +1 -0
  37. package/dist/{chunk-NOMQ5RFG.js → chunk-7UBEHQTP.js} +2 -2
  38. package/dist/{chunk-7NFCGKZT.js → chunk-AQUSMNBF.js} +3 -3
  39. package/dist/{chunk-IDCE26KD.js → chunk-AUYSAMXV.js} +3 -3
  40. package/dist/chunk-AYLC633W.js +406 -0
  41. package/dist/chunk-AYLC633W.js.map +1 -0
  42. package/dist/{chunk-HLDY5S4C.js → chunk-BFF27W3S.js} +3 -3
  43. package/dist/{chunk-QVAA5KHK.js → chunk-BZ7KTXPB.js} +16 -8
  44. package/dist/chunk-BZ7KTXPB.js.map +1 -0
  45. package/dist/{chunk-K7R5QY6C.js → chunk-CE676WCN.js} +2 -2
  46. package/dist/{chunk-5UFGO4ZT.js → chunk-CQHHEW2M.js} +6 -3
  47. package/dist/chunk-CQHHEW2M.js.map +1 -0
  48. package/dist/{chunk-LHDD4JHC.js → chunk-D4Q7T5KD.js} +4 -4
  49. package/dist/{chunk-RBYTXYGD.js → chunk-D75KSI3V.js} +2 -2
  50. package/dist/{chunk-Y3RX7LZT.js → chunk-DDHWZNGL.js} +18 -12
  51. package/dist/chunk-DDHWZNGL.js.map +1 -0
  52. package/dist/{chunk-5LTID2AF.js → chunk-DMSL5BAP.js} +35 -6
  53. package/dist/{chunk-5LTID2AF.js.map → chunk-DMSL5BAP.js.map} +1 -1
  54. package/dist/{chunk-SQYHPBFP.js → chunk-EGNUOALL.js} +2 -2
  55. package/dist/{chunk-LL6TOX3G.js → chunk-EQIII6GI.js} +10 -10
  56. package/dist/chunk-EQIII6GI.js.map +1 -0
  57. package/dist/{chunk-ZAXRQLK3.js → chunk-FTYWGQFM.js} +2 -2
  58. package/dist/{chunk-LE2NOUTN.js → chunk-FV4KXBGO.js} +3 -3
  59. package/dist/{chunk-XFQGI2E3.js → chunk-GWJWECZB.js} +51 -45
  60. package/dist/chunk-GWJWECZB.js.map +1 -0
  61. package/dist/{chunk-QNHZM5ZV.js → chunk-HIGWKLQR.js} +3 -3
  62. package/dist/{chunk-VMZG66UV.js → chunk-HKEXRZMU.js} +3 -3
  63. package/dist/{chunk-V4STTBQD.js → chunk-IHSA7VGI.js} +9 -9
  64. package/dist/{chunk-TEJAGQX2.js → chunk-IS46GQRA.js} +33 -33
  65. package/dist/{chunk-VNYWBHKR.js → chunk-JD3K2344.js} +3 -3
  66. package/dist/{chunk-NCPZYQ4B.js → chunk-K3QGG4O2.js} +2 -2
  67. package/dist/{chunk-QR4FU53I.js → chunk-KCZSUJUR.js} +22 -12
  68. package/dist/chunk-KCZSUJUR.js.map +1 -0
  69. package/dist/{chunk-NH3QZYE5.js → chunk-KV4NU3RP.js} +2 -2
  70. package/dist/{chunk-NDSGJZI2.js → chunk-LOAYWTJJ.js} +2 -2
  71. package/dist/{chunk-ABVMUNCD.js → chunk-M3FBM4T3.js} +64 -10
  72. package/dist/chunk-M3FBM4T3.js.map +1 -0
  73. package/dist/{chunk-GMDSYLI6.js → chunk-MY2Q3FJ3.js} +2 -2
  74. package/dist/{chunk-CV47VCMQ.js → chunk-NPVA65KS.js} +2 -2
  75. package/dist/{chunk-7OCGBJLR.js → chunk-OIVFHJOA.js} +2 -2
  76. package/dist/{chunk-7FIXNAUO.js → chunk-OKB2NEDQ.js} +66 -43
  77. package/dist/chunk-OKB2NEDQ.js.map +1 -0
  78. package/dist/{chunk-3RXYOBME.js → chunk-OPQC4OWM.js} +5 -5
  79. package/dist/{chunk-3RXYOBME.js.map → chunk-OPQC4OWM.js.map} +1 -1
  80. package/dist/chunk-P5MXXHXQ.js +284 -0
  81. package/dist/chunk-P5MXXHXQ.js.map +1 -0
  82. package/dist/{chunk-IR74O2F6.js → chunk-PDG74IJT.js} +239 -162
  83. package/dist/chunk-PDG74IJT.js.map +1 -0
  84. package/dist/{chunk-NN5RYWXA.js → chunk-PH65MFQM.js} +6 -6
  85. package/dist/{chunk-TZNNJLGT.js → chunk-PMB6TYV4.js} +6 -6
  86. package/dist/chunk-QC65IOV3.js +304 -0
  87. package/dist/chunk-QC65IOV3.js.map +1 -0
  88. package/dist/{chunk-6YVJVUR4.js → chunk-QF2DROQR.js} +3 -3
  89. package/dist/{chunk-XXFSOVL3.js → chunk-QFDM23CO.js} +4 -4
  90. package/dist/{chunk-RMLADZRY.js → chunk-R7DGN73N.js} +5 -5
  91. package/dist/{chunk-5PNZBH6V.js → chunk-V5IYLWRA.js} +2 -2
  92. package/dist/{chunk-KQFIGI37.js → chunk-VA6CWUAE.js} +7 -7
  93. package/dist/{chunk-3GTUXW26.js → chunk-VIQOQ463.js} +19 -3
  94. package/dist/chunk-VIQOQ463.js.map +1 -0
  95. package/dist/{chunk-H2SSF24U.js → chunk-VRPPI6GU.js} +17 -6
  96. package/dist/{chunk-H2SSF24U.js.map → chunk-VRPPI6GU.js.map} +1 -1
  97. package/dist/{chunk-YETJNRQM.js → chunk-WEBMMJKL.js} +2 -1
  98. package/dist/{chunk-VUUN3KE4.js → chunk-XVCGPTEQ.js} +8 -8
  99. package/dist/chunk-XVCGPTEQ.js.map +1 -0
  100. package/dist/chunk-YWNF5755.js +696 -0
  101. package/dist/chunk-YWNF5755.js.map +1 -0
  102. package/dist/{chunk-UDCI3QTS.js → chunk-ZM2AYHMO.js} +2 -2
  103. package/dist/{claude-ONQTDWV3.js → claude-ACL7G4CF.js} +4 -4
  104. package/dist/{cleanup-YOM6PQCN.js → cleanup-RLBLNQZN.js} +37 -34
  105. package/dist/{cleanup-YOM6PQCN.js.map → cleanup-RLBLNQZN.js.map} +1 -1
  106. package/dist/cli.js +290 -141
  107. package/dist/cli.js.map +1 -1
  108. package/dist/{color-VQD52LOI.js → color-AC6F2QE7.js} +3 -3
  109. package/dist/{commit-DC2Q5CDY.js → commit-RILBXFWO.js} +15 -15
  110. package/dist/{compile-4NCQECKE.js → compile-QEL5724K.js} +11 -11
  111. package/dist/{contribute-M5UWXCAV.js → contribute-EHWLYOMZ.js} +11 -11
  112. package/dist/{contribute-M5UWXCAV.js.map → contribute-EHWLYOMZ.js.map} +1 -1
  113. package/dist/{mcp/darwin-3JFFE3W2.js → darwin-5K3I4FTH.js} +2 -2
  114. package/dist/{dev-server-CYRP6M73.js → dev-server-2WSWZXJG.js} +35 -21
  115. package/dist/dev-server-2WSWZXJG.js.map +1 -0
  116. package/dist/{feedback-BMAZGKRW.js → feedback-I6ZEHEUB.js} +17 -17
  117. package/dist/{git-BXUD6CL5.js → git-I3PO6FY7.js} +6 -6
  118. package/dist/ignite-XZFYRVRJ.js +35 -0
  119. package/dist/index.d.ts +200 -16
  120. package/dist/index.js +164 -55
  121. package/dist/index.js.map +1 -1
  122. package/dist/{init-CI43GJHV.js → init-A6WRP77L.js} +18 -18
  123. package/dist/{install-deps-SRTM5U7D.js → install-deps-HXP2TM7G.js} +11 -11
  124. package/dist/{installation-detector-HF6QN7KP.js → installation-detector-PYAZ2O6U.js} +3 -3
  125. package/dist/{issues-DMRQJH7E.js → issues-SUFQJY6O.js} +69 -56
  126. package/dist/issues-SUFQJY6O.js.map +1 -0
  127. package/dist/lint-FDZC77GL.js +27 -0
  128. package/dist/{linux-RYLOP2LY.js → linux-WUGRYCJY.js} +2 -2
  129. package/dist/mcp/{chunk-PIIRD4LO.js → chunk-4HZMW2V3.js} +1 -1
  130. package/dist/mcp/{chunk-PIIRD4LO.js.map → chunk-4HZMW2V3.js.map} +1 -1
  131. package/dist/{darwin-5BHWRJ7D.js → mcp/darwin-U25WIGH6.js} +2 -2
  132. package/dist/mcp/issue-management-server.js +900 -20
  133. package/dist/mcp/issue-management-server.js.map +1 -1
  134. package/dist/mcp/{linux-JBVS4R3A.js → linux-5BXVBGSY.js} +2 -2
  135. package/dist/mcp/recap-server.js +24 -22
  136. package/dist/mcp/recap-server.js.map +1 -1
  137. package/dist/mcp/{tmux-RYBLEHUZ.js → tmux-CU26ZTNM.js} +2 -2
  138. package/dist/mcp/{wsl-4QZIQLLE.js → wsl-KI25UDOF.js} +2 -2
  139. package/dist/neon-helpers-LCZAN4U4.js +11 -0
  140. package/dist/{open-2Y7GSUTJ.js → open-US4XACLW.js} +36 -21
  141. package/dist/open-US4XACLW.js.map +1 -0
  142. package/dist/{plan-SWFPLNJE.js → plan-PL3ZB32J.js} +47 -43
  143. package/dist/{plan-SWFPLNJE.js.map → plan-PL3ZB32J.js.map} +1 -1
  144. package/dist/{projects-IUSUXD5D.js → projects-L5AHUBGA.js} +6 -6
  145. package/dist/{prompt-7LZB4PAT.js → prompt-FUU5NMJQ.js} +3 -3
  146. package/dist/prompt-FUU5NMJQ.js.map +1 -0
  147. package/dist/prompts/init-prompt.txt +160 -23
  148. package/dist/prompts/issue-prompt.txt +93 -157
  149. package/dist/prompts/plan-prompt.txt +55 -0
  150. package/dist/prompts/swarm-orchestrator-prompt.txt +78 -21
  151. package/dist/{rebase-S6OHAOOF.js → rebase-JA3RW2XO.js} +12 -12
  152. package/dist/{recap-GGVCG5VH.js → recap-5TO42HN2.js} +9 -9
  153. package/dist/{remote-MZTFHHTU.js → remote-RO4LZKT2.js} +3 -3
  154. package/dist/remote-RO4LZKT2.js.map +1 -0
  155. package/dist/{run-ST3FR75O.js → run-KKCRBRLW.js} +36 -21
  156. package/dist/run-KKCRBRLW.js.map +1 -0
  157. package/dist/schema/settings.schema.json +147 -11
  158. package/dist/{shell-W4SBQPTE.js → shell-GAB2FCXH.js} +8 -8
  159. package/dist/{summary-P2JCIIJO.js → summary-P7QE3TNW.js} +21 -19
  160. package/dist/summary-P7QE3TNW.js.map +1 -0
  161. package/dist/test-6LFB5WOO.js +27 -0
  162. package/dist/{test-git-2KFFAQ6B.js → test-git-PYJOYSED.js} +6 -6
  163. package/dist/{test-jira-FKDKG6CD.js → test-jira-SM7IU5HW.js} +8 -8
  164. package/dist/{test-prefix-GP2DAX37.js → test-prefix-HIRZBXTM.js} +6 -6
  165. package/dist/{test-tabs-YDWMWTVA.js → test-tabs-NGPTFD5T.js} +2 -2
  166. package/dist/{test-webserver-QI3QQFZ3.js → test-webserver-43PVP2JL.js} +8 -8
  167. package/dist/{tmux-7ZTA3BDI.js → tmux-6LRFH3DM.js} +2 -2
  168. package/dist/{update-XLW7R7FL.js → update-AD3GE5C4.js} +4 -4
  169. package/dist/{update-notifier-EYLAXZAA.js → update-notifier-VYDTDMSJ.js} +3 -3
  170. package/dist/update-notifier-VYDTDMSJ.js.map +1 -0
  171. package/dist/{vscode-TOGE5N67.js → vscode-HXIXRZ3A.js} +12 -12
  172. package/dist/{vscode-announcement-NIX7O2MG.js → vscode-announcement-AL3EHORH.js} +3 -3
  173. package/dist/{wsl-Y4GUTOQ7.js → wsl-4VMVT2PO.js} +2 -2
  174. package/package.json +1 -1
  175. package/dist/ClaudeContextManager-SXDCWDJA.js +0 -14
  176. package/dist/ClaudeService-6E6MCGJE.js +0 -13
  177. package/dist/GitHubService-2R5GQG4K.js +0 -12
  178. package/dist/IssueTrackerFactory-XN6MQ4UN.js +0 -14
  179. package/dist/MetadataManager-CMQQTFLQ.js +0 -10
  180. package/dist/ProjectCapabilityDetector-IC6NAFGY.js +0 -11
  181. package/dist/SettingsMigrationManager-S6J7OHUH.js +0 -10
  182. package/dist/build-OLS6J5KZ.js +0 -27
  183. package/dist/chunk-3GTUXW26.js.map +0 -1
  184. package/dist/chunk-5UFGO4ZT.js.map +0 -1
  185. package/dist/chunk-7FIXNAUO.js.map +0 -1
  186. package/dist/chunk-ABVMUNCD.js.map +0 -1
  187. package/dist/chunk-ET6A2JR4.js.map +0 -1
  188. package/dist/chunk-IR74O2F6.js.map +0 -1
  189. package/dist/chunk-LL6TOX3G.js.map +0 -1
  190. package/dist/chunk-QR4FU53I.js.map +0 -1
  191. package/dist/chunk-QVAA5KHK.js.map +0 -1
  192. package/dist/chunk-RVI6C2H5.js +0 -220
  193. package/dist/chunk-RVI6C2H5.js.map +0 -1
  194. package/dist/chunk-VUUN3KE4.js.map +0 -1
  195. package/dist/chunk-WG4MLJ6J.js.map +0 -1
  196. package/dist/chunk-XFQGI2E3.js.map +0 -1
  197. package/dist/chunk-Y3RX7LZT.js.map +0 -1
  198. package/dist/chunk-YRCEOQPX.js.map +0 -1
  199. package/dist/dev-server-CYRP6M73.js.map +0 -1
  200. package/dist/ignite-IO4LXVXJ.js +0 -35
  201. package/dist/issues-DMRQJH7E.js.map +0 -1
  202. package/dist/lint-BSWRMGPZ.js +0 -27
  203. package/dist/neon-helpers-HWIYRKOW.js +0 -11
  204. package/dist/open-2Y7GSUTJ.js.map +0 -1
  205. package/dist/run-ST3FR75O.js.map +0 -1
  206. package/dist/summary-P2JCIIJO.js.map +0 -1
  207. package/dist/test-6JH4FE2X.js +0 -27
  208. /package/dist/{BranchNamingService-XBCO747L.js.map → BitBucketApiClient-J2ZSCS5N.js.map} +0 -0
  209. /package/dist/{ClaudeContextManager-SXDCWDJA.js.map → BitBucketVCSProvider-5X64IXXW.js.map} +0 -0
  210. /package/dist/{ClaudeService-6E6MCGJE.js.map → BranchNamingService-MEK2WZUD.js.map} +0 -0
  211. /package/dist/{GitHubService-2R5GQG4K.js.map → ClaudeContextManager-IENAE2CP.js.map} +0 -0
  212. /package/dist/{IssueTrackerFactory-XN6MQ4UN.js.map → ClaudeService-YIJCZUUB.js.map} +0 -0
  213. /package/dist/{MetadataManager-CMQQTFLQ.js.map → GitHubService-UTAYZXL3.js.map} +0 -0
  214. /package/dist/{ProjectCapabilityDetector-IC6NAFGY.js.map → IssueTrackerFactory-2OI7YIN6.js.map} +0 -0
  215. /package/dist/{LoomLauncher-5AZU2F5I.js.map → LoomLauncher-3TSFW7QP.js.map} +0 -0
  216. /package/dist/{PromptTemplateManager-T5VTLJP3.js.map → MetadataManager-V4LSJ2PB.js.map} +0 -0
  217. /package/dist/{SettingsManager-WQ5NSGAH.js.map → ProjectCapabilityDetector-I4J66WKF.js.map} +0 -0
  218. /package/dist/{SettingsMigrationManager-S6J7OHUH.js.map → PromptTemplateManager-I75WKXM4.js.map} +0 -0
  219. /package/dist/{claude-ONQTDWV3.js.map → SettingsManager-BMQCAXPP.js.map} +0 -0
  220. /package/dist/{color-VQD52LOI.js.map → SettingsMigrationManager-ZPARZ5KH.js.map} +0 -0
  221. /package/dist/{darwin-5BHWRJ7D.js.map → browser-VZY7F2DF.js.map} +0 -0
  222. /package/dist/{build-OLS6J5KZ.js.map → build-DMWSIME6.js.map} +0 -0
  223. /package/dist/{chunk-G2MNSPA4.js.map → chunk-772N5WCA.js.map} +0 -0
  224. /package/dist/{chunk-NOMQ5RFG.js.map → chunk-7UBEHQTP.js.map} +0 -0
  225. /package/dist/{chunk-7NFCGKZT.js.map → chunk-AQUSMNBF.js.map} +0 -0
  226. /package/dist/{chunk-IDCE26KD.js.map → chunk-AUYSAMXV.js.map} +0 -0
  227. /package/dist/{chunk-HLDY5S4C.js.map → chunk-BFF27W3S.js.map} +0 -0
  228. /package/dist/{chunk-K7R5QY6C.js.map → chunk-CE676WCN.js.map} +0 -0
  229. /package/dist/{chunk-LHDD4JHC.js.map → chunk-D4Q7T5KD.js.map} +0 -0
  230. /package/dist/{chunk-RBYTXYGD.js.map → chunk-D75KSI3V.js.map} +0 -0
  231. /package/dist/{chunk-SQYHPBFP.js.map → chunk-EGNUOALL.js.map} +0 -0
  232. /package/dist/{chunk-ZAXRQLK3.js.map → chunk-FTYWGQFM.js.map} +0 -0
  233. /package/dist/{chunk-LE2NOUTN.js.map → chunk-FV4KXBGO.js.map} +0 -0
  234. /package/dist/{chunk-QNHZM5ZV.js.map → chunk-HIGWKLQR.js.map} +0 -0
  235. /package/dist/{chunk-VMZG66UV.js.map → chunk-HKEXRZMU.js.map} +0 -0
  236. /package/dist/{chunk-V4STTBQD.js.map → chunk-IHSA7VGI.js.map} +0 -0
  237. /package/dist/{chunk-TEJAGQX2.js.map → chunk-IS46GQRA.js.map} +0 -0
  238. /package/dist/{chunk-VNYWBHKR.js.map → chunk-JD3K2344.js.map} +0 -0
  239. /package/dist/{chunk-NCPZYQ4B.js.map → chunk-K3QGG4O2.js.map} +0 -0
  240. /package/dist/{chunk-NH3QZYE5.js.map → chunk-KV4NU3RP.js.map} +0 -0
  241. /package/dist/{chunk-NDSGJZI2.js.map → chunk-LOAYWTJJ.js.map} +0 -0
  242. /package/dist/{chunk-GMDSYLI6.js.map → chunk-MY2Q3FJ3.js.map} +0 -0
  243. /package/dist/{chunk-CV47VCMQ.js.map → chunk-NPVA65KS.js.map} +0 -0
  244. /package/dist/{chunk-7OCGBJLR.js.map → chunk-OIVFHJOA.js.map} +0 -0
  245. /package/dist/{chunk-NN5RYWXA.js.map → chunk-PH65MFQM.js.map} +0 -0
  246. /package/dist/{chunk-TZNNJLGT.js.map → chunk-PMB6TYV4.js.map} +0 -0
  247. /package/dist/{chunk-6YVJVUR4.js.map → chunk-QF2DROQR.js.map} +0 -0
  248. /package/dist/{chunk-XXFSOVL3.js.map → chunk-QFDM23CO.js.map} +0 -0
  249. /package/dist/{chunk-RMLADZRY.js.map → chunk-R7DGN73N.js.map} +0 -0
  250. /package/dist/{chunk-5PNZBH6V.js.map → chunk-V5IYLWRA.js.map} +0 -0
  251. /package/dist/{chunk-KQFIGI37.js.map → chunk-VA6CWUAE.js.map} +0 -0
  252. /package/dist/{chunk-YETJNRQM.js.map → chunk-WEBMMJKL.js.map} +0 -0
  253. /package/dist/{chunk-UDCI3QTS.js.map → chunk-ZM2AYHMO.js.map} +0 -0
  254. /package/dist/{git-BXUD6CL5.js.map → claude-ACL7G4CF.js.map} +0 -0
  255. /package/dist/{ignite-IO4LXVXJ.js.map → color-AC6F2QE7.js.map} +0 -0
  256. /package/dist/{commit-DC2Q5CDY.js.map → commit-RILBXFWO.js.map} +0 -0
  257. /package/dist/{compile-4NCQECKE.js.map → compile-QEL5724K.js.map} +0 -0
  258. /package/dist/{installation-detector-HF6QN7KP.js.map → darwin-5K3I4FTH.js.map} +0 -0
  259. /package/dist/{feedback-BMAZGKRW.js.map → feedback-I6ZEHEUB.js.map} +0 -0
  260. /package/dist/{mcp/darwin-3JFFE3W2.js.map → git-I3PO6FY7.js.map} +0 -0
  261. /package/dist/{neon-helpers-HWIYRKOW.js.map → ignite-XZFYRVRJ.js.map} +0 -0
  262. /package/dist/{init-CI43GJHV.js.map → init-A6WRP77L.js.map} +0 -0
  263. /package/dist/{install-deps-SRTM5U7D.js.map → install-deps-HXP2TM7G.js.map} +0 -0
  264. /package/dist/{prompt-7LZB4PAT.js.map → installation-detector-PYAZ2O6U.js.map} +0 -0
  265. /package/dist/{lint-BSWRMGPZ.js.map → lint-FDZC77GL.js.map} +0 -0
  266. /package/dist/{linux-RYLOP2LY.js.map → linux-WUGRYCJY.js.map} +0 -0
  267. /package/dist/{remote-MZTFHHTU.js.map → mcp/darwin-U25WIGH6.js.map} +0 -0
  268. /package/dist/mcp/{linux-JBVS4R3A.js.map → linux-5BXVBGSY.js.map} +0 -0
  269. /package/dist/mcp/{tmux-RYBLEHUZ.js.map → tmux-CU26ZTNM.js.map} +0 -0
  270. /package/dist/mcp/{wsl-4QZIQLLE.js.map → wsl-KI25UDOF.js.map} +0 -0
  271. /package/dist/{update-notifier-EYLAXZAA.js.map → neon-helpers-LCZAN4U4.js.map} +0 -0
  272. /package/dist/{projects-IUSUXD5D.js.map → projects-L5AHUBGA.js.map} +0 -0
  273. /package/dist/{rebase-S6OHAOOF.js.map → rebase-JA3RW2XO.js.map} +0 -0
  274. /package/dist/{recap-GGVCG5VH.js.map → recap-5TO42HN2.js.map} +0 -0
  275. /package/dist/{shell-W4SBQPTE.js.map → shell-GAB2FCXH.js.map} +0 -0
  276. /package/dist/{test-6JH4FE2X.js.map → test-6LFB5WOO.js.map} +0 -0
  277. /package/dist/{test-git-2KFFAQ6B.js.map → test-git-PYJOYSED.js.map} +0 -0
  278. /package/dist/{test-jira-FKDKG6CD.js.map → test-jira-SM7IU5HW.js.map} +0 -0
  279. /package/dist/{test-prefix-GP2DAX37.js.map → test-prefix-HIRZBXTM.js.map} +0 -0
  280. /package/dist/{test-tabs-YDWMWTVA.js.map → test-tabs-NGPTFD5T.js.map} +0 -0
  281. /package/dist/{test-webserver-QI3QQFZ3.js.map → test-webserver-43PVP2JL.js.map} +0 -0
  282. /package/dist/{tmux-7ZTA3BDI.js.map → tmux-6LRFH3DM.js.map} +0 -0
  283. /package/dist/{update-XLW7R7FL.js.map → update-AD3GE5C4.js.map} +0 -0
  284. /package/dist/{vscode-TOGE5N67.js.map → vscode-HXIXRZ3A.js.map} +0 -0
  285. /package/dist/{vscode-announcement-NIX7O2MG.js.map → vscode-announcement-AL3EHORH.js.map} +0 -0
  286. /package/dist/{wsl-Y4GUTOQ7.js.map → wsl-4VMVT2PO.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/ignite.ts","../src/lib/ClaudeHookManager.ts","../src/lib/SwarmSetupService.ts","../src/utils/language-detector.ts","../src/utils/system-prompt-writer.ts"],"sourcesContent":["import path from 'path'\nimport fs from 'fs-extra'\nimport { logger, createStderrLogger } from '../utils/logger.js'\nimport { withLogger } from '../utils/logger-context.js'\nimport { ClaudeWorkflowOptions } from '../lib/ClaudeService.js'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { launchClaude, ClaudeCliOptions } from '../utils/claude.js'\nimport { PromptTemplateManager, TemplateVariables, buildReviewTemplateVariables } from '../lib/PromptTemplateManager.js'\nimport { generateIssueManagementMcpConfig, generateRecapMcpConfig, generateAndWriteMcpConfigFile, resolveRecapFilePath, readRecapFile, writeRecapFile } from '../utils/mcp.js'\nimport { AgentManager } from '../lib/AgentManager.js'\nimport { IssueTrackerFactory } from '../lib/IssueTrackerFactory.js'\nimport { SettingsManager, type IloomSettings } from '../lib/SettingsManager.js'\nimport { MetadataManager } from '../lib/MetadataManager.js'\nimport { extractSettingsOverrides } from '../utils/cli-overrides.js'\nimport { FirstRunManager } from '../utils/FirstRunManager.js'\nimport { extractIssueNumber, isValidGitRepo, getWorktreeRoot, findMainWorktreePathWithSettings, generateWorktreePath } from '../utils/git.js'\nimport { getWorkspacePort } from '../utils/port.js'\nimport { readFile } from 'fs/promises'\nimport { ClaudeHookManager } from '../lib/ClaudeHookManager.js'\nimport type { OneShotMode, ComplexityOverride } from '../types/index.js'\nimport { fetchChildIssueDetails } from '../utils/list-children.js'\nimport { buildDependencyMap } from '../utils/dependency-map.js'\nimport { SwarmSetupService } from '../lib/SwarmSetupService.js'\nimport type { LoomMetadata } from '../lib/MetadataManager.js'\nimport { TelemetryService } from '../lib/TelemetryService.js'\nimport { detectProjectLanguage } from '../utils/language-detector.js'\nimport { prepareSystemPromptForPlatform } from '../utils/system-prompt-writer.js'\nimport { preAcceptClaudeTrust } from '../utils/claude-trust.js'\n\n/**\n * Error thrown when the spin command is run from an invalid location\n */\nexport class WorktreeValidationError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic readonly suggestion: string\n\t) {\n\t\tsuper(message)\n\t\tthis.name = 'WorktreeValidationError'\n\t}\n}\n\n/**\n * IgniteCommand: Auto-detect workspace context and launch Claude\n *\n * This command:\n * 1. Auto-detects context from current directory and git branch\n * 2. Loads appropriate prompt template with variable substitution\n * 3. Launches Claude with existing agent system (NO changes to agent loading)\n * 4. Executes in current terminal (not opening a new window)\n *\n * CRITICAL: This command works with agents exactly as they currently function.\n * NO modifications to agent loading mechanisms.\n */\nexport class IgniteCommand {\n\tprivate templateManager: PromptTemplateManager\n\tprivate gitWorktreeManager: GitWorktreeManager\n\tprivate agentManager: AgentManager\n\tprivate settingsManager: SettingsManager\n\tprivate firstRunManager: FirstRunManager\n\tprivate hookManager: ClaudeHookManager\n\tprivate settings?: IloomSettings\n\n\tconstructor(\n\t\ttemplateManager?: PromptTemplateManager,\n\t\tgitWorktreeManager?: GitWorktreeManager,\n\t\tagentManager?: AgentManager,\n\t\tsettingsManager?: SettingsManager,\n\t\tfirstRunManager?: FirstRunManager,\n\t\thookManager?: ClaudeHookManager\n\t) {\n\t\tthis.templateManager = templateManager ?? new PromptTemplateManager()\n\t\tthis.gitWorktreeManager = gitWorktreeManager ?? new GitWorktreeManager()\n\t\tthis.agentManager = agentManager ?? new AgentManager()\n\t\tthis.settingsManager = settingsManager ?? new SettingsManager()\n\t\tthis.firstRunManager = firstRunManager ?? new FirstRunManager('spin')\n\t\tthis.hookManager = hookManager ?? new ClaudeHookManager()\n\t}\n\n\t/**\n\t * Validate that we're not running from the main worktree\n\t * @param workspacePath - Optional explicit workspace path; defaults to process.cwd()\n\t * @throws WorktreeValidationError if running from main worktree\n\t */\n\tprivate async validateNotMainWorktree(workspacePath?: string): Promise<void> {\n\t\tconst currentDir = workspacePath ?? process.cwd()\n\n\t\t// Step 1: Check if we're in a git repository at all\n\t\tconst isGitRepo = await isValidGitRepo(currentDir)\n\t\tif (!isGitRepo) {\n\t\t\t// Not a git repo - let detectWorkspaceContext handle this gracefully\n\t\t\treturn\n\t\t}\n\n\t\t// Step 2: Get the worktree root (handles subdirectories)\n\t\tconst worktreeRoot = await getWorktreeRoot(currentDir)\n\t\tif (!worktreeRoot) {\n\t\t\t// Could not determine root - let detectWorkspaceContext handle this\n\t\t\treturn\n\t\t}\n\n\t\t// Step 3: Check if this path is a registered git worktree\n\t\tconst worktrees = await this.gitWorktreeManager.listWorktrees()\n\t\tconst currentWorktree = worktrees.find(wt => wt.path === worktreeRoot)\n\n\t\tif (!currentWorktree) {\n\t\t\t// Not a registered worktree - let detectWorkspaceContext handle this\n\t\t\treturn\n\t\t}\n\n\t\t// Step 4: Check if this is the main worktree\n\t\tconst isMain = await this.gitWorktreeManager.isMainWorktree(currentWorktree, this.settingsManager)\n\t\tif (isMain) {\n\t\t\tthrow new WorktreeValidationError(\n\t\t\t\t'You cannot run the command from the main worktree.',\n\t\t\t\t\"Navigate to a feature worktree created by 'il start <issue>' and run 'il spin' from there.\"\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Print mode options for headless/CI execution\n\t */\n\tpublic printOptions: {\n\t\tprint?: boolean\n\t\toutputFormat?: 'json' | 'stream-json' | 'text'\n\t\tverbose?: boolean\n\t\tjson?: boolean // --json flag: output final JSON object\n\t\tjsonStream?: boolean // --json-stream flag: stream JSONL to stdout\n\t} | undefined\n\n\t/**\n\t * Main entry point for spin command\n\t * @param oneShot - One-shot automation mode\n\t * @param printOptions - Print mode options for headless/CI execution\n\t * @param skipCleanup - Skip cleanup after execution\n\t * @param workspacePath - Optional explicit workspace path for programmatic invocation (avoids process.chdir())\n\t * @param complexity - Override complexity evaluation (session-only, takes priority over metadata)\n\t */\n\tasync execute(oneShot?: OneShotMode, printOptions?: {\n\t\tprint?: boolean\n\t\toutputFormat?: 'json' | 'stream-json' | 'text'\n\t\tverbose?: boolean\n\t\tjson?: boolean\n\t\tjsonStream?: boolean\n\t}, skipCleanup?: boolean, workspacePath?: string, complexity?: ComplexityOverride): Promise<void> {\n\t\tthis.printOptions = printOptions\n\n\t\t// Wrap execution in stderr logger for JSON modes to keep stdout clean\n\t\tconst isJsonMode = (this.printOptions?.json ?? false) || (this.printOptions?.jsonStream ?? false)\n\t\tif (isJsonMode) {\n\t\t\tconst jsonLogger = createStderrLogger()\n\t\t\treturn withLogger(jsonLogger, () => this.executeInternal(oneShot, skipCleanup, workspacePath, complexity))\n\t\t}\n\n\t\treturn this.executeInternal(oneShot, skipCleanup, workspacePath, complexity)\n\t}\n\n\t/**\n\t * Internal execution method (separated for withLogger wrapping)\n\t */\n\tprivate async executeInternal(oneShot?: OneShotMode, skipCleanup?: boolean, workspacePath?: string, complexity?: ComplexityOverride): Promise<void> {\n\t\t// Set ILOOM=1 so hooks know this is an iloom session\n\t\t// This is inherited by the Claude child process\n\t\tprocess.env.ILOOM = '1'\n\n\t\t// Validate we're not in the main worktree first\n\t\ttry {\n\t\t\tawait this.validateNotMainWorktree(workspacePath)\n\t\t} catch (error) {\n\t\t\tif (error instanceof WorktreeValidationError) {\n\t\t\t\tlogger.error(error.message)\n\t\t\t\tlogger.info(error.suggestion)\n\t\t\t\tthrow error\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\n\t\ttry {\n\t\t\tlogger.info('🚀 Your loom is spinning up, please wait...')\n\n\t\t\t// Step 0.5: Check if this is first-time user\n\t\t\tconst isFirstRun = await this.firstRunManager.isFirstRun()\n\t\t\tif (isFirstRun) {\n\t\t\t\tlogger.success('Welcome to iloom! Preparing first-time experience...')\n\t\t\t}\n\n\t\t\t// Step 0.6: Install Claude hooks for VSCode integration (idempotent, quick)\n\t\t\tawait this.hookManager.installHooks()\n\n\t\t\t// Step 1: Auto-detect workspace context\n\t\t\tconst context = await this.detectWorkspaceContext(workspacePath)\n\n\t\t\tlogger.debug('Auto-detected workspace context', { context })\n\n\t\t\t// Inform user what context was detected\n\t\t\tthis.logDetectedContext(context)\n\n\t\t\tlogger.info('📝 Loading prompt template and preparing Claude...')\n\n\t\t\t// Step 2: Read metadata early to get draftPrNumber and draftPrUrl for templates and MCP config\n\t\t\tconst metadataManager = new MetadataManager()\n\t\t\tconst metadata = await metadataManager.readMetadata(context.workspacePath)\n\t\t\tconst draftPrNumber = metadata?.draftPrNumber ?? undefined\n\t\t\t// Extract draft PR URL from prUrls map if available\n\t\t\tconst draftPrUrl = draftPrNumber && metadata?.prUrls?.[String(draftPrNumber)]\n\t\t\t\t? metadata.prUrls[String(draftPrNumber)]\n\t\t\t\t: undefined\n\n\t\t\t// Step 2.0.3: Prevent il spin in child worktrees of epic looms\n\t\t\t// Child issues managed by a swarm orchestrator must not launch independent agents.\n\t\t\t// Exception: child epics (issueType === 'epic') need il spin for their own swarm.\n\t\t\tif (metadata?.parentLoom?.type === 'epic' && metadata.issueType !== 'epic') {\n\t\t\t\tthrow new WorktreeValidationError(\n\t\t\t\t\t'Cannot run il spin in a child worktree of an epic loom. The swarm orchestrator manages agent execution for these issues.',\n\t\t\t\t\t'Run il spin from the parent epic worktree instead to launch the swarm orchestrator.'\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// Step 2.0.4: Determine effective oneShot mode\n\t\t\t// If print mode is enabled, force noReview to skip interactive reviews\n\t\t\t// If oneShot is provided (any value including 'default'), use it\n\t\t\t// If oneShot is undefined (not passed), use metadata or fallback to 'default'\n\t\t\t// Note: metadata?.oneShot can be null (for legacy looms), so we need double nullish coalescing\n\t\t\tconst storedOneShot = metadata?.oneShot ?? 'default'\n\t\t\tconst isHeadlessForOneShot = this.printOptions?.print ?? false\n\t\t\tconst effectiveOneShot: OneShotMode = isHeadlessForOneShot ? 'noReview' : (oneShot ?? storedOneShot)\n\n\t\t\t// Determine effective complexity override\n\t\t\t// CLI flag takes priority over loom metadata\n\t\t\tconst effectiveComplexity = complexity ?? metadata?.complexity ?? undefined\n\n\t\t\t// Set recap complexity if overridden and not already set\n\t\t\tif (effectiveComplexity) {\n\t\t\t\ttry {\n\t\t\t\t\tconst recapFilePath = resolveRecapFilePath(context.workspacePath)\n\t\t\t\t\tconst recap = await readRecapFile(recapFilePath)\n\t\t\t\t\tif (!recap.complexity) {\n\t\t\t\t\t\trecap.complexity = { level: effectiveComplexity, reason: 'Overridden via CLI flag', timestamp: new Date().toISOString() }\n\t\t\t\t\t\tawait writeRecapFile(recapFilePath, recap)\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tlogger.debug(`Failed to set recap complexity: ${error instanceof Error ? error.message : error}`)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Step 2.0.5: Load settings early if not cached (needed for port calculation)\n\t\t\tif (!this.settings) {\n\t\t\t\tconst cliOverrides = extractSettingsOverrides()\n\t\t\t\tthis.settings = await this.settingsManager.loadSettings(undefined, cliOverrides)\n\t\t\t}\n\n\t\t\t// Step 2.0.5.1: Track session.started telemetry\n\t\t\ttry {\n\t\t\t\tconst hasNeon = !!this.settings?.databaseProviders?.neon\n\t\t\t\tconst language = await detectProjectLanguage(context.workspacePath)\n\t\t\t\tTelemetryService.getInstance().track('session.started', {\n\t\t\t\t\thas_neon: hasNeon,\n\t\t\t\t\tlanguage,\n\t\t\t\t})\n\t\t\t} catch (error) {\n\t\t\t\tlogger.debug(`Telemetry session.started tracking failed: ${error instanceof Error ? error.message : error}`)\n\t\t\t}\n\n\t\t\t// Step 2.0.6: Calculate port for web-capable looms\n\t\t\tif (metadata?.capabilities?.includes('web') && context.branchName) {\n\t\t\t\tconst basePort = this.settings?.capabilities?.web?.basePort ?? 3000\n\t\t\t\tcontext.port = await getWorkspacePort({\n\t\t\t\t\tbasePort,\n\t\t\t\t\tworktreePath: context.workspacePath,\n\t\t\t\t\tworktreeBranch: context.branchName,\n\t\t\t\t})\n\t\t\t\tlogger.info(`🌐 Development server port: ${context.port}`)\n\t\t\t}\n\n\t\t\t// Step 2.1: Fetch and persist epic child data if this is an epic loom\n\t\t\t// Detection: check for childIssues already stored (re-spin of an epic)\n\t\t\t// or check for 'epic' issueType once issue #624 adds it\n\t\t\tconst isEpicLoom = metadata && metadata.issue_numbers.length > 0\n\t\t\t\t&& ((metadata.childIssues?.length ?? 0) > 0 || metadata.issueType === 'epic')\n\t\t\tif (isEpicLoom && this.settings) {\n\t\t\t\tawait this.fetchAndStoreEpicChildData(metadataManager, metadata, context.workspacePath, this.settings)\n\t\t\t}\n\n\t\t\t// Step 2.1.1: If this is an epic loom, enter swarm mode\n\t\t\tif (isEpicLoom && this.settings) {\n\t\t\t\t// Re-read metadata to get freshly persisted child data\n\t\t\t\tconst freshMetadata = await metadataManager.readMetadata(context.workspacePath)\n\t\t\t\tif (freshMetadata && freshMetadata.childIssues.length > 0) {\n\t\t\t\t\tawait this.executeSwarmMode(\n\t\t\t\t\t\tfreshMetadata,\n\t\t\t\t\t\tcontext.workspacePath,\n\t\t\t\t\t\tcontext.branchName ?? '',\n\t\t\t\t\t\tmetadataManager,\n\t\t\t\t\t\tskipCleanup,\n\t\t\t\t\t)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Step 2.2: Get prompt template with variable substitution\n\t\t\tconst variables = this.buildTemplateVariables(context, effectiveOneShot, draftPrNumber, draftPrUrl, effectiveComplexity)\n\n\t\t\t// Step 2.5: Add first-time user context if needed\n\t\t\tif (isFirstRun) {\n\t\t\t\tvariables.FIRST_TIME_USER = true\n\t\t\t\tvariables.README_CONTENT = await this.loadReadmeContent()\n\t\t\t\tvariables.SETTINGS_SCHEMA_CONTENT = await this.loadSettingsSchemaContent()\n\t\t\t}\n\n\t\t\tconst systemInstructions = await this.templateManager.getPrompt(context.type, variables)\n\n\t\t\t// User prompt to trigger the workflow (includes one-shot bypass instructions if needed)\n\t\t\tconst userPrompt = this.buildUserPrompt(effectiveOneShot)\n\n\t\t\t// Step 3: Determine model and permission mode based on workflow type\n\t\t\tconst model = this.settingsManager.getSpinModel(this.settings)\n\t\t\tlet permissionMode = this.getPermissionModeForWorkflow(context.type)\n\n\t\t\t// Override permission mode if bypassPermissions oneShot mode\n\t\t\tif (effectiveOneShot === 'bypassPermissions') {\n\t\t\t\tpermissionMode = 'bypassPermissions'\n\t\t\t}\n\n\t\t\t// Display warning if bypassPermissions is used\n\t\t\tif (permissionMode === 'bypassPermissions') {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t'⚠️ WARNING: Using bypassPermissions mode - Claude will execute all tool calls without confirmation. ' +\n\t\t\t\t\t\t'This can be dangerous. Use with caution.'\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// Step 4: Build Claude CLI options\n\t\t\t// Session ID must come from loom metadata - no fallback generation\n\t\t\tconst sessionId = metadata?.sessionId\n\t\t\tif (!sessionId) {\n\t\t\t\tthrow new Error('No session ID found in loom metadata. This loom may need to be recreated with `il start`.')\n\t\t\t}\n\t\t\tlogger.debug('Using session ID from metadata', { sessionId })\n\n\t\t\t// Determine if we're in print/headless mode\n\t\t\tconst isHeadless = this.printOptions?.print ?? false\n\n\t\t\tconst claudeOptions: ClaudeCliOptions = {\n\t\t\t\theadless: isHeadless,\n\t\t\t\taddDir: context.workspacePath,\n\t\t\t\tsessionId, // Enable Claude Code session resume\n\t\t\t}\n\n\t\t\t// Add optional model if present\n\t\t\tif (model !== undefined) {\n\t\t\t\tclaudeOptions.model = model\n\t\t\t}\n\n\t\t\t// Add permission mode if not default\n\t\t\t// When print mode is enabled, force bypassPermissions for autonomous execution\n\t\t\tif (isHeadless) {\n\t\t\t\tpermissionMode = 'bypassPermissions'\n\t\t\t}\n\t\t\tif (permissionMode !== undefined && permissionMode !== 'default') {\n\t\t\t\tclaudeOptions.permissionMode = permissionMode\n\t\t\t}\n\n\t\t\t// Add output format and verbose options if provided (print mode only)\n\t\t\tif (this.printOptions?.outputFormat !== undefined) {\n\t\t\t\tclaudeOptions.outputFormat = this.printOptions.outputFormat\n\t\t\t}\n\t\t\tif (this.printOptions?.verbose !== undefined) {\n\t\t\t\tclaudeOptions.verbose = this.printOptions.verbose\n\t\t\t}\n\n\t\t\t// Add JSON mode if specified (requires print mode)\n\t\t\tif (this.printOptions?.json) {\n\t\t\t\tclaudeOptions.jsonMode = 'json'\n\t\t\t\tclaudeOptions.outputFormat = 'stream-json' // Force stream-json for parsing\n\t\t\t} else if (this.printOptions?.jsonStream) {\n\t\t\t\tclaudeOptions.jsonMode = 'stream'\n\t\t\t\tclaudeOptions.outputFormat = 'stream-json' // Force stream-json for streaming\n\t\t\t}\n\n\t\t\t// Add optional branch name for context\n\t\t\tif (context.branchName !== undefined) {\n\t\t\t\tclaudeOptions.branchName = context.branchName\n\t\t\t}\n\n\t\t\t// Step 4.5: Generate MCP config and tool filtering for issue/PR workflows\n\t\t\tlet mcpConfig: Record<string, unknown>[] | undefined\n\t\t\tlet allowedTools: string[] | undefined\n\t\t\tlet disallowedTools: string[] | undefined\n\n\t\t\tif (context.type === 'issue' || context.type === 'pr') {\n\t\t\t\ttry {\n\t\t\t\t\tconst provider = this.settings ? IssueTrackerFactory.getProviderName(this.settings) : 'github'\n\t\t\t\t\t// Pass draftPrNumber to route comments to PR when in github-draft-pr mode\n\t\t\t\t\tmcpConfig = await generateIssueManagementMcpConfig(context.type, undefined, provider, this.settings, draftPrNumber)\n\t\t\t\t\tlogger.debug('Generated MCP configuration for issue management', { provider, draftPrNumber })\n\n\t\t\t\t\t// Configure tool filtering for issue/PR workflows\n\t\t\t\t\t// Note: set_goal is only allowed for PR workflow (user's purpose unclear)\n\t\t\t\t\t// For issue workflow, the issue title provides context so set_goal is not needed\n\t\t\t\t\tconst baseTools = [\n\t\t\t\t\t\t'mcp__issue_management__get_issue',\n\t\t\t\t\t\t'mcp__issue_management__get_comment',\n\t\t\t\t\t\t'mcp__issue_management__create_comment',\n\t\t\t\t\t\t'mcp__issue_management__update_comment',\n\t\t\t\t\t\t'mcp__issue_management__create_issue',\n\t\t\t\t\t\t'mcp__issue_management__close_issue',\n\t\t\t\t\t\t'mcp__issue_management__reopen_issue',\n\t\t\t\t\t\t'mcp__issue_management__edit_issue',\n\t\t\t\t\t\t'mcp__recap__add_entry',\n\t\t\t\t\t\t'mcp__recap__get_recap',\n\t\t\t\t\t\t'mcp__recap__add_artifact',\n\t\t\t\t\t\t'mcp__recap__set_complexity',\n\t\t\t\t\t\t'mcp__recap__set_loom_state',\n\t\t\t\t\t\t'mcp__recap__get_loom_state'\n\t\t\t\t\t]\n\t\t\t\t\tallowedTools = context.type === 'pr'\n\t\t\t\t\t\t? [...baseTools, 'mcp__issue_management__get_pr', 'mcp__issue_management__get_review_comments', 'mcp__recap__set_goal']\n\t\t\t\t\t\t: baseTools\n\t\t\t\t\tdisallowedTools = context.type === 'pr'\n\t\t\t\t\t\t? ['Bash(gh issue comment:*)']\n\t\t\t\t\t\t: ['Bash(gh api:*)', 'Bash(gh issue comment:*)']\n\n\t\t\t\t\tlogger.debug('Configured tool filtering for issue/PR workflow', { allowedTools, disallowedTools })\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// Log warning but continue without MCP\n\t\t\t\t\tlogger.warn(`Failed to generate MCP config: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Regular/branch workflow - allow recap tools (including set_goal since no issue/PR context)\n\t\t\t\tallowedTools = [\n\t\t\t\t\t'mcp__recap__set_goal',\n\t\t\t\t\t'mcp__recap__add_entry',\n\t\t\t\t\t'mcp__recap__get_recap',\n\t\t\t\t\t'mcp__recap__set_complexity',\n\t\t\t\t\t'mcp__recap__set_loom_state',\n\t\t\t\t\t'mcp__recap__get_loom_state',\n\t\t\t\t]\n\t\t\t\tlogger.debug('Configured tool filtering for regular workflow', { allowedTools })\n\t\t\t}\n\n\t\t\t// Step 4.5.1: Generate recap MCP config (always added for all workflow types)\n\t\t\t// Reuses metadata already read in Step 2\n\t\t\ttry {\n\t\t\t\tif (!metadata) {\n\t\t\t\t\tthrow new Error('No loom metadata found for this workspace')\n\t\t\t\t}\n\t\t\t\tconst recapMcpConfig = generateRecapMcpConfig(context.workspacePath, metadata)\n\t\t\t\tif (mcpConfig) {\n\t\t\t\t\tmcpConfig.push(...recapMcpConfig)\n\t\t\t\t} else {\n\t\t\t\t\tmcpConfig = recapMcpConfig\n\t\t\t\t}\n\t\t\t\tlogger.debug('Generated MCP configuration for recap server')\n\t\t\t} catch (error) {\n\t\t\t\t// Log warning but continue without recap MCP\n\t\t\t\tlogger.warn(`Failed to generate recap MCP config: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t\t\t}\n\n\t\t\t// Step 4.6: Load agent configurations using cached settings\n\t\t\tlet agents: Record<string, unknown> | undefined\n\t\t\ttry {\n\t\t\t\t// Use cached settings from Step 2.5\n\t\t\t\tif (this.settings?.agents && Object.keys(this.settings.agents).length > 0) {\n\t\t\t\t\tlogger.debug('Loaded project settings', {\n\t\t\t\t\t\tagentOverrides: Object.keys(this.settings.agents),\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\t// Load agents with settings overrides and template variables for substitution\n\t\t\t\t// Exclude init-only agents (e.g., framework-detector which is only for il init)\n\t\t\t\tconst loadedAgents = await this.agentManager.loadAgents(\n\t\t\t\t\tthis.settings,\n\t\t\t\t\tvariables,\n\t\t\t\t\t['*.md', '!iloom-framework-detector.md']\n\t\t\t\t)\n\n\t\t\t\tif (process.platform === 'darwin') {\n\t\t\t\t\t// macOS: pass agents inline via --agents flag (unchanged behavior)\n\t\t\t\t\tagents = this.agentManager.formatForCli(loadedAgents)\n\t\t\t\t\tlogger.debug('Loaded agent configurations for CLI', {\n\t\t\t\t\t\tagentCount: Object.keys(agents).length,\n\t\t\t\t\t\tagentNames: Object.keys(agents),\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\t// Linux/Windows: render agents to .claude/agents/ for auto-discovery\n\t\t\t\t\tconst agentsDir = path.join(context.workspacePath, '.claude', 'agents')\n\t\t\t\t\tconst rendered = await this.agentManager.renderAgentsToDisk(loadedAgents, agentsDir)\n\t\t\t\t\tlogger.debug('Rendered agent files to disk for auto-discovery', {\n\t\t\t\t\t\tagentCount: rendered.length,\n\t\t\t\t\t\tagentNames: rendered,\n\t\t\t\t\t\ttargetDir: agentsDir,\n\t\t\t\t\t})\n\t\t\t\t\t// agents remains undefined - not passed to launchClaude\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\t// Log warning but continue without agents\n\t\t\t\tlogger.warn(`Failed to load agents: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t\t\t}\n\n\t\t\tlogger.debug('Launching Claude in current terminal', {\n\t\t\t\ttype: context.type,\n\t\t\t\tmodel,\n\t\t\t\tpermissionMode,\n\t\t\t\tworkspacePath: context.workspacePath,\n\t\t\t\thasMcpConfig: !!mcpConfig,\n\t\t\t})\n\n\t\t\t// Pre-accept Claude Code trust for this worktree path\n\t\t\ttry {\n\t\t\t\tawait preAcceptClaudeTrust(context.workspacePath)\n\t\t\t} catch (error) {\n\t\t\t\tlogger.warn(`Failed to pre-accept Claude trust: ${error instanceof Error ? error.message : String(error)}`)\n\t\t\t}\n\n\t\t\tlogger.info(isHeadless ? '✨ Launching Claude in headless mode...' : '✨ Launching Claude in current terminal...')\n\n\t\t\t// Prepare system prompt based on platform\n\t\t\tconst systemPromptConfig = await prepareSystemPromptForPlatform(\n\t\t\t\tsystemInstructions,\n\t\t\t\tcontext.workspacePath,\n\t\t\t)\n\n\t\t\t// Determine the initial user prompt (Windows overrides with /clear)\n\t\t\tconst effectiveUserPrompt = systemPromptConfig.initialPromptOverride ?? userPrompt\n\n\t\t\t// Step 5: Launch Claude with system instructions appended and user prompt\n\t\t\tconst claudeResult = await launchClaude(effectiveUserPrompt, {\n\t\t\t\t...claudeOptions,\n\t\t\t\t...(systemPromptConfig.appendSystemPrompt && { appendSystemPrompt: systemPromptConfig.appendSystemPrompt }),\n\t\t\t\t...(systemPromptConfig.pluginDir && { pluginDir: systemPromptConfig.pluginDir }),\n\t\t\t\t...(mcpConfig && { mcpConfig }),\n\t\t\t\t...(allowedTools && { allowedTools }),\n\t\t\t\t...(disallowedTools && { disallowedTools }),\n\t\t\t\t...(agents && { agents }),\n\t\t\t})\n\n\t\t\t// Output final JSON for --json mode (--json-stream already streamed to stdout)\n\t\t\tif (this.printOptions?.json) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.log(JSON.stringify({\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\toutput: claudeResult ?? ''\n\t\t\t\t}))\n\t\t\t}\n\n\t\t\t// Step 6: Mark as run after successful launch\n\t\t\tif (isFirstRun) {\n\t\t\t\tawait this.firstRunManager.markAsRun()\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : 'Unknown error'\n\t\t\t// Output error as JSON for --json mode\n\t\t\tif (this.printOptions?.json) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.log(JSON.stringify({\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: errorMessage\n\t\t\t\t}))\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Log user-friendly information about detected context\n\t */\n\tprivate logDetectedContext(context: ClaudeWorkflowOptions): void {\n\t\tif (context.type === 'issue') {\n\t\t\tlogger.info(`🎯 Detected issue workflow: Issue #${context.issueNumber}`)\n\t\t} else if (context.type === 'pr') {\n\t\t\tlogger.info(`🔄 Detected PR workflow: PR #${context.prNumber}`)\n\t\t} else {\n\t\t\tlogger.info('🌟 Detected regular workflow')\n\t\t}\n\n\t\tif (context.branchName) {\n\t\t\tlogger.info(`🌿 Working on branch: ${context.branchName}`)\n\t\t}\n\n\t\tif (context.port) {\n\t\t\tlogger.info(`🌐 Development server port: ${context.port}`)\n\t\t}\n\t}\n\n\t/**\n\t * Build template variables from context\n\t */\n\tprivate buildTemplateVariables(\n\t\tcontext: ClaudeWorkflowOptions,\n\t\toneShot: OneShotMode,\n\t\tdraftPrNumber?: number,\n\t\tdraftPrUrl?: string,\n\t\tcomplexity?: ComplexityOverride\n\t): TemplateVariables {\n\t\tconst variables: TemplateVariables = {\n\t\t\tWORKSPACE_PATH: context.workspacePath,\n\t\t}\n\n\t\tif (context.issueNumber !== undefined) {\n\t\t\tvariables.ISSUE_NUMBER = context.issueNumber\n\t\t}\n\n\t\tif (context.prNumber !== undefined) {\n\t\t\tvariables.PR_NUMBER = context.prNumber\n\t\t}\n\n\t\tif (context.title !== undefined) {\n\t\t\tif (context.type === 'issue') {\n\t\t\t\tvariables.ISSUE_TITLE = context.title\n\t\t\t} else if (context.type === 'pr') {\n\t\t\t\tvariables.PR_TITLE = context.title\n\t\t\t}\n\t\t}\n\n\t\tif (context.port !== undefined) {\n\t\t\tvariables.PORT = context.port\n\t\t}\n\n\t\t// Set ONE_SHOT_MODE or INTERACTIVE_MODE flag for template conditional sections\n\t\tif (oneShot === 'noReview' || oneShot === 'bypassPermissions') {\n\t\t\tvariables.ONE_SHOT_MODE = true\n\t\t} else {\n\t\t\tvariables.INTERACTIVE_MODE = true\n\t\t}\n\n\t\t// Set review configuration variables (code reviewer + artifact reviewer + per-agent flags)\n\t\tObject.assign(variables, buildReviewTemplateVariables(false, this.settings?.agents))\n\n\t\t// Set complexity override if provided (CLI flag or loom metadata)\n\t\tif (complexity) {\n\t\t\tvariables.COMPLEXITY_OVERRIDE = complexity\n\t\t}\n\n\t\t// Set draft PR mode flags (mutually exclusive)\n\t\t// When draftPrNumber is set, we're in github-draft-pr mode\n\t\tif (draftPrNumber !== undefined) {\n\t\t\tvariables.DRAFT_PR_MODE = true\n\t\t\tvariables.DRAFT_PR_NUMBER = draftPrNumber\n\t\t\tif (draftPrUrl) {\n\t\t\t\tvariables.DRAFT_PR_URL = draftPrUrl\n\t\t\t}\n\t\t\t// Set AUTO_COMMIT_PUSH when in draft PR mode and not explicitly disabled\n\t\t\t// Default is true (enabled) for draft PR mode\n\t\t\tconst autoCommitPushEnabled = this.settings?.mergeBehavior?.autoCommitPush !== false\n\t\t\tvariables.AUTO_COMMIT_PUSH = autoCommitPushEnabled\n\t\t\t// Set GIT_REMOTE from settings or default to 'origin'\n\t\t\tconst remote = this.settings?.mergeBehavior?.remote ?? 'origin'\n\t\t\tif (!/^[a-zA-Z0-9_-]+$/.test(remote)) {\n\t\t\t\tthrow new Error(`Invalid git remote name: \"${remote}\". Remote names can only contain alphanumeric characters, underscores, and hyphens.`)\n\t\t\t}\n\t\t\tvariables.GIT_REMOTE = remote\n\t\t} else if (context.type === 'regular') {\n\t\t\t// Branch mode without draft PR\n\t\t\tvariables.STANDARD_BRANCH_MODE = true\n\t\t} else {\n\t\t\t// Issue/PR mode without draft PR\n\t\t\tvariables.STANDARD_ISSUE_MODE = true\n\t\t}\n\n\t\t// Detect VS Code mode\n\t\tconst isVscodeMode = process.env.ILOOM_VSCODE === '1'\n\t\tvariables.IS_VSCODE_MODE = isVscodeMode\n\n\t\treturn variables\n\t}\n\n\t/**\n\t * Get the appropriate permission mode for a workflow type\n\t * Same logic as ClaudeService.getPermissionModeForWorkflow()\n\t */\n\tprivate getPermissionModeForWorkflow(\n\t\ttype: 'issue' | 'pr' | 'regular'\n\t): ClaudeCliOptions['permissionMode'] {\n\t\t// Check settings for configured permission mode\n\t\tif (this.settings?.workflows) {\n\t\t\tconst workflowConfig =\n\t\t\t\ttype === 'issue'\n\t\t\t\t\t? this.settings.workflows.issue\n\t\t\t\t\t: type === 'pr'\n\t\t\t\t\t\t? this.settings.workflows.pr\n\t\t\t\t\t\t: this.settings.workflows.regular\n\n\t\t\tif (workflowConfig?.permissionMode) {\n\t\t\t\treturn workflowConfig.permissionMode\n\t\t\t}\n\t\t}\n\n\t\t// Fall back to current defaults\n\t\tif (type === 'issue') {\n\t\t\treturn 'acceptEdits'\n\t\t}\n\t\t// For PR and regular workflows, use default permissions\n\t\treturn 'default'\n\t}\n\n\t/**\n\t * Auto-detect workspace context from current directory and git branch\n\t *\n\t * Detection priority:\n\t * 1. Directory name patterns (_pr_N, issue-N)\n\t * 2. Git branch name patterns\n\t * 3. Fallback to 'regular' workflow\n\t *\n\t * This leverages the same logic as FinishCommand.autoDetectFromCurrentDirectory()\n\t */\n\tprivate async detectWorkspaceContext(workspacePath?: string): Promise<ClaudeWorkflowOptions> {\n\t\tconst workspacePath_ = workspacePath ?? process.cwd()\n\t\tconst currentDir = path.basename(workspacePath_)\n\n\t\t// Check for PR worktree pattern: _pr_N suffix\n\t\t// Pattern: /.*_pr_(\\d+)$/\n\t\tconst prPattern = /_pr_(\\d+)$/\n\t\tconst prMatch = currentDir.match(prPattern)\n\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tlogger.debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`)\n\n\t\t\treturn this.buildContextForPR(prNumber, workspacePath_)\n\t\t}\n\n\t\t// Check for issue pattern in directory name\n\t\tconst issueNumber = extractIssueNumber(currentDir)\n\n\t\tif (issueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${issueNumber} from directory: ${currentDir}`)\n\n\t\t\treturn this.buildContextForIssue(issueNumber, workspacePath_)\n\t\t}\n\n\t\t// Fallback: Try to extract from git branch name\n\t\ttry {\n\t\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\t\tconst currentBranch = repoInfo.currentBranch\n\n\t\t\tif (currentBranch) {\n\t\t\t\t// Try to extract issue from branch name\n\t\t\t\tconst branchIssueNumber = extractIssueNumber(currentBranch)\n\t\t\t\tif (branchIssueNumber !== null) {\n\t\t\t\t\tlogger.debug(`Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`)\n\n\t\t\t\t\treturn this.buildContextForIssue(branchIssueNumber, workspacePath_, currentBranch)\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Git command failed - not a git repo or other git error\n\t\t\tlogger.debug('Could not detect from git branch', { error })\n\t\t}\n\n\t\t// Last resort: use regular workflow\n\t\tlogger.debug('No specific context detected, using regular workflow')\n\t\treturn this.buildContextForRegular(workspacePath_)\n\t}\n\n\t/**\n\t * Build context for issue workflow\n\t */\n\tprivate async buildContextForIssue(\n\t\tissueNumber: string | number,\n\t\tworkspacePath: string,\n\t\tbranchName?: string\n\t): Promise<ClaudeWorkflowOptions> {\n\t\t// Get branch name if not provided\n\t\tif (!branchName) {\n\t\t\ttry {\n\t\t\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\t\t\tbranchName = repoInfo.currentBranch ?? undefined\n\t\t\t} catch {\n\t\t\t\t// Ignore git errors\n\t\t\t}\n\t\t}\n\n\t\tconst context: ClaudeWorkflowOptions = {\n\t\t\ttype: 'issue',\n\t\t\tissueNumber,\n\t\t\tworkspacePath,\n\t\t\theadless: false, // Interactive mode\n\t\t}\n\n\t\tif (branchName !== undefined) {\n\t\t\tcontext.branchName = branchName\n\t\t}\n\n\t\treturn context\n\t}\n\n\t/**\n\t * Build context for PR workflow\n\t */\n\tprivate async buildContextForPR(\n\t\tprNumber: number,\n\t\tworkspacePath: string\n\t): Promise<ClaudeWorkflowOptions> {\n\t\t// Get branch name\n\t\tlet branchName: string | undefined\n\t\ttry {\n\t\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\t\tbranchName = repoInfo.currentBranch ?? undefined\n\t\t} catch {\n\t\t\t// Ignore git errors\n\t\t}\n\n\t\tconst context: ClaudeWorkflowOptions = {\n\t\t\ttype: 'pr',\n\t\t\tprNumber,\n\t\t\tworkspacePath,\n\t\t\theadless: false, // Interactive mode\n\t\t}\n\n\t\tif (branchName !== undefined) {\n\t\t\tcontext.branchName = branchName\n\t\t}\n\n\t\treturn context\n\t}\n\n\t/**\n\t * Build context for regular workflow\n\t */\n\tprivate async buildContextForRegular(workspacePath: string): Promise<ClaudeWorkflowOptions> {\n\t\t// Get branch name\n\t\tlet branchName: string | undefined\n\t\ttry {\n\t\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\t\tbranchName = repoInfo.currentBranch ?? undefined\n\t\t} catch {\n\t\t\t// Ignore git errors\n\t\t}\n\n\t\tconst context: ClaudeWorkflowOptions = {\n\t\t\ttype: 'regular',\n\t\t\tworkspacePath,\n\t\t\theadless: false, // Interactive mode\n\t\t}\n\n\t\tif (branchName !== undefined) {\n\t\t\tcontext.branchName = branchName\n\t\t}\n\n\t\treturn context\n\t}\n\n\n\t/**\n\t * Fetch and store epic child issue data and dependency map in metadata\n\t *\n\t * Called during spin setup for epic looms. Fetches child issue details\n\t * and dependency relationships from the issue tracker, then persists\n\t * them in the loom metadata for use by the orchestrator.\n\t */\n\tprivate async fetchAndStoreEpicChildData(\n\t\tmetadataManager: MetadataManager,\n\t\tmetadata: import('../lib/MetadataManager.js').LoomMetadata,\n\t\tworktreePath: string,\n\t\tsettings: import('../lib/SettingsManager.js').IloomSettings,\n\t): Promise<void> {\n\t\tconst parentIssueNumber = metadata.issue_numbers[0]\n\t\tif (!parentIssueNumber) return\n\n\t\tlogger.info('Fetching child issue data for epic...')\n\n\t\ttry {\n\t\t\tconst issueTracker = IssueTrackerFactory.create(settings)\n\n\t\t\t// Fetch child issue details and build dependency map in parallel\n\t\t\tconst childIssueDetails = await fetchChildIssueDetails(\n\t\t\t\tparentIssueNumber, issueTracker\n\t\t\t)\n\n\t\t\tif (childIssueDetails.length === 0) {\n\t\t\t\tlogger.debug('No child issues found for epic')\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Extract raw IDs for dependency map building (strip prefixes)\n\t\t\tconst childIds = childIssueDetails.map((child) => child.number.replace(/^#/, ''))\n\n\t\t\tconst dependencyMap = await buildDependencyMap(childIds, settings)\n\n\t\t\t// Persist to metadata\n\t\t\tawait metadataManager.updateMetadata(worktreePath, {\n\t\t\t\tchildIssues: childIssueDetails,\n\t\t\t\tdependencyMap,\n\t\t\t})\n\n\t\t\tlogger.info(`Stored ${childIssueDetails.length} child issues and dependency map in metadata`)\n\t\t} catch (error) {\n\t\t\t// Non-fatal: epic can still spin without child data\n\t\t\tlogger.warn(`Failed to fetch epic child data: ${error instanceof Error ? error.message : String(error)}`)\n\t\t}\n\t}\n\n\t/**\n\t * Execute swarm mode for an epic loom.\n\t *\n\t * Creates child worktrees, renders swarm agents/skill, builds the\n\t * orchestrator prompt, and launches Claude with agent teams enabled.\n\t */\n\tprivate async executeSwarmMode(\n\t\tmetadata: LoomMetadata,\n\t\tepicWorktreePath: string,\n\t\tepicBranch: string,\n\t\tmetadataManager: MetadataManager,\n\t\tskipCleanup?: boolean,\n\t): Promise<void> {\n\t\tif (!this.settings) {\n\t\t\tthrow new Error('Settings not loaded. Cannot enter swarm mode.')\n\t\t}\n\t\tconst settings = this.settings\n\t\tconst epicIssueNumber = metadata.issue_numbers[0]\n\t\tif (!epicIssueNumber) {\n\t\t\tthrow new Error('Epic loom has no issue number in metadata')\n\t\t}\n\n\t\tlogger.info('Epic loom detected - entering swarm mode...')\n\n\t\t// Determine main worktree path and issue tracker provider\n\t\tconst mainWorktreePath = await findMainWorktreePathWithSettings()\n\t\tconst providerName = IssueTrackerFactory.getProviderName(settings)\n\n\t\t// Create SwarmSetupService\n\t\tconst swarmSetup = new SwarmSetupService(\n\t\t\tthis.gitWorktreeManager,\n\t\t\tmetadataManager,\n\t\t\tthis.agentManager,\n\t\t\tthis.settingsManager,\n\t\t\tthis.templateManager,\n\t\t)\n\n\t\t// Generate and write per-loom MCP config file for the epic worktree\n\t\ttry {\n\t\t\tconst epicMcpConfigPath = await generateAndWriteMcpConfigFile(\n\t\t\t\tepicWorktreePath,\n\t\t\t\tmetadata,\n\t\t\t\tproviderName as 'github' | 'linear' | 'jira',\n\t\t\t\tsettings,\n\t\t\t)\n\t\t\tawait metadataManager.updateMetadata(epicWorktreePath, { mcpConfigPath: epicMcpConfigPath })\n\n\t\t\t// Write MCP config path to .claude/iloom-swarm-mcp-config-path for worker discovery\n\t\t\tconst epicClaudeDir = path.join(epicWorktreePath, '.claude')\n\t\t\tawait fs.ensureDir(epicClaudeDir)\n\t\t\tawait fs.writeFile(\n\t\t\t\tpath.join(epicClaudeDir, 'iloom-swarm-mcp-config-path'),\n\t\t\t\tepicMcpConfigPath,\n\t\t\t\t'utf-8',\n\t\t\t)\n\n\t\t\tlogger.debug('Wrote MCP config for epic loom', { epicMcpConfigPath })\n\t\t} catch (error) {\n\t\t\tlogger.warn(`Failed to write MCP config for epic loom: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t\t}\n\n\t\t// Build MCP configs for the orchestrator's own launchClaude call\n\t\tconst mcpConfigs: Record<string, unknown>[] = []\n\n\t\t// Issue management MCP\n\t\ttry {\n\t\t\tconst issueMcpConfigs = await generateIssueManagementMcpConfig(\n\t\t\t\t'issue',\n\t\t\t\tundefined,\n\t\t\t\tproviderName as 'github' | 'linear' | 'jira',\n\t\t\t\tsettings,\n\t\t\t)\n\t\t\tmcpConfigs.push(...issueMcpConfigs)\n\t\t} catch (error) {\n\t\t\tlogger.warn(`Failed to generate issue management MCP config: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t\t}\n\n\t\t// Recap MCP for the epic loom\n\t\ttry {\n\t\t\tconst recapMcpConfigs = generateRecapMcpConfig(epicWorktreePath, metadata)\n\t\t\tmcpConfigs.push(...recapMcpConfigs)\n\t\t} catch (error) {\n\t\t\tlogger.warn(`Failed to generate recap MCP config: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t\t}\n\n\t\t// Filter out children that are already done (finished looms may have metadata\n\t\t// in the \"looms/finished\" directory, not just in active worktree metadata)\n\t\tconst finishedMetadata = await metadataManager.listFinishedMetadata()\n\t\tconst finishedByIssueNumber = new Map<string, LoomMetadata>()\n\t\tfor (const meta of finishedMetadata) {\n\t\t\tfor (const issueNum of meta.issue_numbers) {\n\t\t\t\t// listFinishedMetadata returns newest first; preserve the newest entry\n\t\t\t\tif (!finishedByIssueNumber.has(issueNum)) {\n\t\t\t\t\tfinishedByIssueNumber.set(issueNum, meta)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst pendingChildIssues: typeof metadata.childIssues = []\n\t\tconst skippedChildren: Array<{ number: string; state: string }> = []\n\n\t\tfor (const child of metadata.childIssues) {\n\t\t\tconst rawId = child.number.replace(/^#/, '')\n\t\t\tconst safeId = rawId.replace(/[^a-zA-Z0-9-_]/g, '-')\n\t\t\tconst childBranch = `issue/${safeId}`\n\t\t\tconst childWorktreePath = generateWorktreePath(childBranch, mainWorktreePath)\n\n\t\t\t// Check active worktree metadata first, then fall back to finished metadata\n\t\t\tconst childMeta = await metadataManager.readMetadata(childWorktreePath)\n\t\t\t\t?? finishedByIssueNumber.get(rawId) ?? null\n\n\t\t\tif (childMeta?.state === 'done') {\n\t\t\t\tskippedChildren.push({ number: child.number, state: childMeta.state })\n\t\t\t} else {\n\t\t\t\tpendingChildIssues.push(child)\n\t\t\t}\n\t\t}\n\n\t\tif (skippedChildren.length > 0) {\n\t\t\tfor (const skipped of skippedChildren) {\n\t\t\t\tlogger.info(`Skipping child ${skipped.number} (state: ${skipped.state})`)\n\t\t\t}\n\t\t}\n\n\t\t// Run swarm setup for any pending child issues (may be empty if all are done)\n\t\tconst swarmResult = await swarmSetup.setupSwarm(\n\t\t\tepicIssueNumber,\n\t\t\tepicBranch,\n\t\t\tepicWorktreePath,\n\t\t\tpendingChildIssues,\n\t\t\tmainWorktreePath,\n\t\t\tproviderName,\n\t\t\tsettings,\n\t\t)\n\n\t\t// Build template variables for orchestrator prompt\n\t\tconst successfulWorktrees = swarmResult.childWorktrees.filter((c) => c.success)\n\t\tconst worktreeMap = new Map(successfulWorktrees.map((cw) => [cw.issueId, cw]))\n\n\t\tconst childIssuesData = pendingChildIssues\n\t\t\t.filter((ci) => worktreeMap.has(ci.number.replace(/^#/, '')))\n\t\t\t.map((ci) => {\n\t\t\t\tconst rawId = ci.number.replace(/^#/, '')\n\t\t\t\tconst wt = worktreeMap.get(rawId)\n\t\t\t\treturn {\n\t\t\t\t\tnumber: rawId,\n\t\t\t\t\ttitle: ci.title,\n\t\t\t\t\tbody: ci.body,\n\t\t\t\t\tworktreePath: wt?.worktreePath ?? '',\n\t\t\t\t\tbranchName: wt?.branch ?? '',\n\t\t\t\t}\n\t\t\t})\n\n\t\t// Get metadata file path for the orchestrator prompt template\n\t\tconst epicMetadataPath = metadataManager.getMetadataFilePath(epicWorktreePath)\n\n\t\t// Determine issue prefix for commit message trailers\n\t\tconst issuePrefix = providerName === 'github' ? '#' : ''\n\n\t\t// Post-swarm review defaults to true (matches SpinAgentSettingsSchema default)\n\t\tconst postSwarmReview = settings.spin?.postSwarmReview !== false\n\n\t\tconst variables: TemplateVariables = {\n\t\t\tEPIC_ISSUE_NUMBER: epicIssueNumber,\n\t\t\tEPIC_WORKTREE_PATH: epicWorktreePath,\n\t\t\tEPIC_METADATA_PATH: epicMetadataPath,\n\t\t\tCHILD_ISSUES: JSON.stringify(childIssuesData, null, 2),\n\t\t\tDEPENDENCY_MAP: JSON.stringify(metadata.dependencyMap, null, 2),\n\t\t\tISSUE_PREFIX: issuePrefix,\n\t\t\t...(skipCleanup && { NO_CLEANUP: true }),\n\t\t\t...(postSwarmReview && { POST_SWARM_REVIEW: true }),\n\t\t}\n\n\t\t// Set draft PR mode flags for swarm orchestrator (same logic as buildTemplateVariables)\n\t\tconst draftPrNumber = metadata.draftPrNumber ?? undefined\n\t\tif (draftPrNumber !== undefined) {\n\t\t\tvariables.DRAFT_PR_MODE = true\n\t\t\tvariables.DRAFT_PR_NUMBER = draftPrNumber\n\t\t\tconst draftPrUrl = metadata.prUrls?.[String(draftPrNumber)]\n\t\t\tif (draftPrUrl) {\n\t\t\t\tvariables.DRAFT_PR_URL = draftPrUrl\n\t\t\t}\n\t\t\tconst autoCommitPushEnabled = settings.mergeBehavior?.autoCommitPush !== false\n\t\t\tvariables.AUTO_COMMIT_PUSH = autoCommitPushEnabled\n\t\t\tconst remote = settings.mergeBehavior?.remote ?? 'origin'\n\t\t\tif (!/^[a-zA-Z0-9_-]+$/.test(remote)) {\n\t\t\t\tthrow new Error(`Invalid git remote name: \"${remote}\". Remote names can only contain alphanumeric characters, underscores, and hyphens.`)\n\t\t\t}\n\t\t\tvariables.GIT_REMOTE = remote\n\t\t}\n\n\t\tconst orchestratorPrompt = await this.templateManager.getPrompt('swarm-orchestrator', variables)\n\n\t\t// Build allowed tools\n\t\tconst allowedTools = [\n\t\t\t'mcp__issue_management__get_issue',\n\t\t\t'mcp__issue_management__get_comment',\n\t\t\t'mcp__issue_management__create_comment',\n\t\t\t'mcp__issue_management__update_comment',\n\t\t\t'mcp__issue_management__create_issue',\n\t\t\t'mcp__issue_management__close_issue',\n\t\t\t'mcp__issue_management__reopen_issue',\n\t\t\t'mcp__issue_management__edit_issue',\n\t\t\t'mcp__recap__add_entry',\n\t\t\t'mcp__recap__get_recap',\n\t\t\t'mcp__recap__add_artifact',\n\t\t\t'mcp__recap__set_complexity',\n\t\t\t'mcp__recap__set_loom_state',\n\t\t\t'mcp__recap__get_loom_state',\n\t\t]\n\n\t\t// Launch Claude with agent teams enabled\n\t\tconst model = this.settingsManager.getSpinModel(settings, 'swarm')\n\n\t\tlogger.info('Launching swarm orchestrator...')\n\t\tlogger.info(` Model: ${model ?? 'default'}`)\n\t\tlogger.info(` Permission mode: bypassPermissions`)\n\t\tlogger.info(` Agent teams: enabled`)\n\t\tlogger.info(` Child worktrees: ${successfulWorktrees.length}`)\n\n\t\t// Load agents for the orchestrator\n\t\tlet agents: Record<string, unknown> | undefined\n\t\ttry {\n\t\t\tconst loadedAgents = await this.agentManager.loadAgents(\n\t\t\t\tsettings,\n\t\t\t\tvariables,\n\t\t\t\t['*.md', '!iloom-framework-detector.md']\n\t\t\t)\n\n\t\t\tif (process.platform === 'darwin') {\n\t\t\t\tagents = this.agentManager.formatForCli(loadedAgents)\n\t\t\t} else {\n\t\t\t\tconst agentsDir = path.join(epicWorktreePath, '.claude', 'agents')\n\t\t\t\tawait this.agentManager.renderAgentsToDisk(loadedAgents, agentsDir)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tlogger.warn(`Failed to load agents: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t\t}\n\n\t\t// Track swarm.started before launching orchestrator\n\t\tconst swarmStartTime = Date.now()\n\t\ttry {\n\t\t\tTelemetryService.getInstance().track('swarm.started', {\n\t\t\t\tchild_count: successfulWorktrees.length,\n\t\t\t\ttracker: providerName,\n\t\t\t})\n\t\t} catch (error) {\n\t\t\tlogger.debug(`Telemetry swarm.started tracking failed: ${error instanceof Error ? error.message : error}`)\n\t\t}\n\n\t\t// Prepare orchestrator prompt based on platform\n\t\tconst orchestratorPromptConfig = await prepareSystemPromptForPlatform(\n\t\t\torchestratorPrompt,\n\t\t\tepicWorktreePath,\n\t\t)\n\n\t\tconst effectiveSwarmPrompt = orchestratorPromptConfig.initialPromptOverride\n\t\t\t?? `You are the swarm orchestrator for epic #${epicIssueNumber}. Begin by reading your system prompt instructions and executing the workflow.`\n\n\t\t// Set env vars directly on process.env so they propagate to Claude Code\n\t\t// and its child processes (execa's env option doesn't reliably pass them)\n\t\tprocess.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = '1'\n\t\tprocess.env.ILOOM_SWARM = '1'\n\t\tprocess.env.ENABLE_TOOL_SEARCH = 'auto:30'\n\t\tprocess.env.CLAUDE_CODE_DISABLE_FILE_CHECKPOINTING = '1'\n\t\tprocess.env.CLAUDE_CODE_DISABLE_AUTO_MEMORY = '1'\n\t\tprocess.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = '1'\n\t\tprocess.env.CLAUDE_CODE_EFFORT_LEVEL = 'medium'\n\n\t\tawait launchClaude(effectiveSwarmPrompt, {\n\t\t\tmodel,\n\t\t\tpermissionMode: 'bypassPermissions',\n\t\t\taddDir: epicWorktreePath,\n\t\t\theadless: false,\n\t\t\t...(metadata.sessionId && { sessionId: metadata.sessionId }),\n\t\t\t...(orchestratorPromptConfig.appendSystemPrompt && { appendSystemPrompt: orchestratorPromptConfig.appendSystemPrompt }),\n\t\t\t...(orchestratorPromptConfig.pluginDir && { pluginDir: orchestratorPromptConfig.pluginDir }),\n\t\t\tmcpConfig: mcpConfigs,\n\t\t\tallowedTools,\n\t\t\t...(agents && { agents }),\n\t\t})\n\n\t\t// Track swarm child completions and overall completion\n\t\ttry {\n\t\t\tconst swarmEndTime = Date.now()\n\t\t\tlet succeeded = 0\n\t\t\tlet failed = 0\n\n\t\t\tfor (const child of successfulWorktrees) {\n\t\t\t\tconst childMeta = await metadataManager.readMetadata(child.worktreePath)\n\t\t\t\tconst isSuccess = childMeta?.state === 'done'\n\t\t\t\tif (isSuccess) {\n\t\t\t\t\tsucceeded++\n\t\t\t\t} else {\n\t\t\t\t\tfailed++\n\t\t\t\t}\n\n\t\t\t\tconst parsed = childMeta?.created_at ? Date.parse(childMeta.created_at) : NaN\n\t\t\t\tconst childCreatedAt = Number.isNaN(parsed) ? swarmStartTime : parsed\n\t\t\t\tconst childDuration = Math.max(0, Math.round((swarmEndTime - childCreatedAt) / 60000))\n\n\t\t\t\tTelemetryService.getInstance().track('swarm.child_completed', {\n\t\t\t\t\tsuccess: isSuccess,\n\t\t\t\t\tduration_minutes: childDuration,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tTelemetryService.getInstance().track('swarm.completed', {\n\t\t\t\ttotal_children: successfulWorktrees.length,\n\t\t\t\tsucceeded,\n\t\t\t\tfailed,\n\t\t\t\tduration_minutes: Math.round((swarmEndTime - swarmStartTime) / 60000),\n\t\t\t})\n\t\t} catch (error) {\n\t\t\tlogger.debug(`Telemetry swarm completion tracking failed: ${error instanceof Error ? error.message : error}`)\n\t\t}\n\t}\n\n\t/**\n\t * Build user prompt based on one-shot mode\n\t */\n\tprivate buildUserPrompt(oneShot: OneShotMode = 'default'): string {\n\t\t// For one-shot modes, add bypass instructions to override template approval requirements\n\t\tif (oneShot === 'noReview' || oneShot === 'bypassPermissions') {\n\t\t\treturn 'Guide the user through the iloom workflow! The user has requested you move through the workflow without awaiting confirmation. This supersedes any other guidance.'\n\t\t}\n\n\t\t// Default mode: simple \"Go!\" prompt\n\t\treturn 'Guide the user through the iloom workflow!'\n\t}\n\n\t/**\n\t * Load README.md content for first-time users\n\t * Walks up from dist directory to find README.md in project root\n\t */\n\tprivate async loadReadmeContent(): Promise<string> {\n\t\ttry {\n\t\t\t// Walk up from current file location to find README.md\n\t\t\t// Use same pattern as PromptTemplateManager for finding files\n\t\t\tlet currentDir = path.dirname(new URL(import.meta.url).pathname)\n\n\t\t\t// Walk up to find README.md\n\t\t\twhile (currentDir !== path.dirname(currentDir)) {\n\t\t\t\tconst readmePath = path.join(currentDir, 'README.md')\n\t\t\t\ttry {\n\t\t\t\t\tconst content = await readFile(readmePath, 'utf-8')\n\t\t\t\t\tlogger.debug('Loaded README.md for first-time user', { readmePath })\n\t\t\t\t\treturn content\n\t\t\t\t} catch {\n\t\t\t\t\tcurrentDir = path.dirname(currentDir)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlogger.debug('README.md not found, returning empty string')\n\t\t\treturn ''\n\t\t} catch (error) {\n\t\t\t// Graceful degradation - return empty string on error\n\t\t\tlogger.debug(`Failed to load README.md: ${error}`)\n\t\t\treturn ''\n\t\t}\n\t}\n\n\t/**\n\t * Load settings schema content for first-time users\n\t * Walks up from dist directory to find .iloom/README.md\n\t */\n\tprivate async loadSettingsSchemaContent(): Promise<string> {\n\t\ttry {\n\t\t\t// Walk up from current file location to find .iloom/README.md\n\t\t\tlet currentDir = path.dirname(new URL(import.meta.url).pathname)\n\n\t\t\t// Walk up to find .iloom/README.md\n\t\t\twhile (currentDir !== path.dirname(currentDir)) {\n\t\t\t\tconst schemaPath = path.join(currentDir, '.iloom', 'README.md')\n\t\t\t\ttry {\n\t\t\t\t\tconst content = await readFile(schemaPath, 'utf-8')\n\t\t\t\t\tlogger.debug('Loaded .iloom/README.md for first-time user', { schemaPath })\n\t\t\t\t\treturn content\n\t\t\t\t} catch {\n\t\t\t\t\tcurrentDir = path.dirname(currentDir)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlogger.debug('.iloom/README.md not found, returning empty string')\n\t\t\treturn ''\n\t\t} catch (error) {\n\t\t\t// Graceful degradation - return empty string on error\n\t\t\tlogger.debug(`Failed to load .iloom/README.md: ${error}`)\n\t\t\treturn ''\n\t\t}\n\t}\n}\n","import os from 'os'\nimport path from 'path'\nimport fs from 'fs-extra'\nimport { parse, modify, applyEdits, ParseError } from 'jsonc-parser'\nimport { fileURLToPath } from 'url'\nimport { accessSync } from 'fs'\nimport { logger } from '../utils/logger.js'\n\n/**\n * Hook configuration for a single event\n */\ninterface HookEntry {\n\ttype: 'command'\n\tcommand: string\n\ttimeout?: number\n}\n\n/**\n * Hook event configuration\n */\ninterface HookEventConfig {\n\tmatcher?: string\n\thooks: HookEntry[]\n}\n\n/**\n * Claude settings.json structure (partial)\n */\ninterface ClaudeSettings {\n\thooks?: Record<string, HookEventConfig[]>\n\t[key: string]: unknown\n}\n\n/**\n * Manages installation of Claude Code hooks to ~/.claude/\n *\n * Hooks enable real-time monitoring of Claude session state\n * via Unix socket communication with the iloom-vscode extension.\n */\nexport class ClaudeHookManager {\n\tprivate claudeDir: string\n\tprivate hooksDir: string\n\tprivate settingsPath: string\n\tprivate templateDir: string\n\n\tconstructor() {\n\t\t// Initialize paths using os.homedir()\n\t\tthis.claudeDir = path.join(os.homedir(), '.claude')\n\t\tthis.hooksDir = path.join(this.claudeDir, 'hooks')\n\t\tthis.settingsPath = path.join(this.claudeDir, 'settings.json')\n\n\t\t// Find templates relative to the package installation\n\t\t// Same pattern as PromptTemplateManager\n\t\tconst currentFileUrl = import.meta.url\n\t\tconst currentFilePath = fileURLToPath(currentFileUrl)\n\t\tconst distDir = path.dirname(currentFilePath)\n\n\t\t// Walk up to find the hooks template directory\n\t\tlet templateDir = path.join(distDir, 'hooks')\n\t\tlet currentDir = distDir\n\n\t\twhile (currentDir !== path.dirname(currentDir)) {\n\t\t\tconst candidatePath = path.join(currentDir, 'hooks')\n\t\t\ttry {\n\t\t\t\taccessSync(candidatePath)\n\t\t\t\ttemplateDir = candidatePath\n\t\t\t\tbreak\n\t\t\t} catch {\n\t\t\t\tcurrentDir = path.dirname(currentDir)\n\t\t\t}\n\t\t}\n\n\t\tthis.templateDir = templateDir\n\t\tlogger.debug('ClaudeHookManager initialized', {\n\t\t\tclaudeDir: this.claudeDir,\n\t\t\thooksDir: this.hooksDir,\n\t\t\tsettingsPath: this.settingsPath,\n\t\t\ttemplateDir: this.templateDir\n\t\t})\n\t}\n\n\t/**\n\t * Install Claude hooks for VSCode integration\n\t *\n\t * This is idempotent - safe to call on every spin.\n\t * Installs hook script to ~/.claude/hooks/ and merges\n\t * hook configuration into ~/.claude/settings.json\n\t */\n\tasync installHooks(): Promise<void> {\n\t\ttry {\n\t\t\t// 1. Create ~/.claude/hooks if missing\n\t\t\tawait fs.ensureDir(this.hooksDir)\n\n\t\t\t// 2. Install hook script from bundled templates\n\t\t\tawait this.installHookScript()\n\n\t\t\t// 3. Merge hook config into settings.json\n\t\t\tawait this.mergeHookConfig()\n\n\t\t\tlogger.debug('Claude hooks installed successfully')\n\t\t} catch (error) {\n\t\t\t// Log warning but don't fail - hooks are optional enhancement\n\t\t\tlogger.warn(\n\t\t\t\t`Failed to install Claude hooks: ${error instanceof Error ? error.message : 'Unknown error'}`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Check if hooks are already installed\n\t */\n\tasync isHooksInstalled(): Promise<boolean> {\n\t\ttry {\n\t\t\t// Check if hook script exists\n\t\t\tconst hookScriptPath = path.join(this.hooksDir, 'iloom-hook.js')\n\t\t\tif (!(await fs.pathExists(hookScriptPath))) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// Check if settings.json has our hooks\n\t\t\tif (!(await fs.pathExists(this.settingsPath))) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tconst content = await fs.readFile(this.settingsPath, 'utf8')\n\t\t\tconst errors: ParseError[] = []\n\t\t\tconst settings = parse(content, errors, { allowTrailingComma: true }) as ClaudeSettings\n\n\t\t\tif (errors.length > 0 || !settings?.hooks) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// Check if our hooks are registered (check for SessionStart as indicator)\n\t\t\treturn Array.isArray(settings.hooks.SessionStart)\n\t\t} catch {\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Install the hook script from bundled templates\n\t * Skips write if destination already has identical content\n\t */\n\tprivate async installHookScript(): Promise<void> {\n\t\tconst sourcePath = path.join(this.templateDir, 'iloom-hook.js')\n\t\tconst destPath = path.join(this.hooksDir, 'iloom-hook.js')\n\n\t\t// Check if source template exists\n\t\tif (!(await fs.pathExists(sourcePath))) {\n\t\t\tthrow new Error(`Hook template not found at ${sourcePath}`)\n\t\t}\n\n\t\t// Skip if destination exists and content matches\n\t\tif (await fs.pathExists(destPath)) {\n\t\t\tconst [sourceContent, destContent] = await Promise.all([\n\t\t\t\tfs.readFile(sourcePath, 'utf8'),\n\t\t\t\tfs.readFile(destPath, 'utf8')\n\t\t\t])\n\t\t\tif (sourceContent === destContent) {\n\t\t\t\tlogger.debug('Hook script already up to date, skipping')\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\t// Copy hook script (only when content differs or doesn't exist)\n\t\tawait fs.copyFile(sourcePath, destPath)\n\t\tlogger.debug('Hook script installed', { sourcePath, destPath })\n\t}\n\n\t/**\n\t * Merge hook configuration into settings.json\n\t * Preserves existing user hooks and comments\n\t */\n\tprivate async mergeHookConfig(): Promise<void> {\n\t\t// Ensure ~/.claude directory exists\n\t\tawait fs.ensureDir(this.claudeDir)\n\n\t\t// Read existing settings (or create empty)\n\t\tlet existingContent = '{}'\n\t\tlet existingSettings: ClaudeSettings = {}\n\n\t\tif (await fs.pathExists(this.settingsPath)) {\n\t\t\texistingContent = await fs.readFile(this.settingsPath, 'utf8')\n\t\t\tconst errors: ParseError[] = []\n\t\t\texistingSettings = parse(existingContent, errors, { allowTrailingComma: true }) as ClaudeSettings\n\n\t\t\tif (errors.length > 0) {\n\t\t\t\tlogger.warn('Existing settings.json has parse errors, will attempt to merge anyway')\n\t\t\t}\n\t\t}\n\n\t\t// Get our hook configuration\n\t\tconst ourHooks = this.getHookConfig()\n\n\t\t// Merge hooks - preserve user's existing hooks on same events\n\t\tconst mergedHooks: Record<string, HookEventConfig[]> = { ...(existingSettings.hooks ?? {}) }\n\t\tlet hooksAdded = false\n\n\t\tfor (const [eventName, eventConfigs] of Object.entries(ourHooks)) {\n\t\t\tconst existing = mergedHooks[eventName] ?? []\n\n\t\t\t// Check if our hook is already registered\n\t\t\tconst ourConfig = eventConfigs[0]\n\t\t\tconst ourCommand = ourConfig?.hooks?.[0]?.command\n\t\t\tconst existingConfigIndex = existing.findIndex(\n\t\t\t\t(config) => config.hooks?.some((h) => h.command === ourCommand)\n\t\t\t)\n\n\t\t\tif (existingConfigIndex === -1) {\n\t\t\t\t// Add our hook config to the event\n\t\t\t\tmergedHooks[eventName] = [...existing, ...eventConfigs]\n\t\t\t\thooksAdded = true\n\t\t\t} else {\n\t\t\t\t// Hook is already registered - check if we need to update the matcher\n\t\t\t\tconst existingConfig = existing[existingConfigIndex]\n\t\t\t\tconst ourMatcher = ourConfig?.matcher\n\n\t\t\t\t// Update matcher if our config has one and existing doesn't match\n\t\t\t\tif (existingConfig && ourMatcher !== undefined && existingConfig.matcher !== ourMatcher) {\n\t\t\t\t\texisting[existingConfigIndex] = {\n\t\t\t\t\t\t...existingConfig,\n\t\t\t\t\t\tmatcher: ourMatcher\n\t\t\t\t\t}\n\t\t\t\t\thooksAdded = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Skip write if no new hooks were added\n\t\tif (!hooksAdded) {\n\t\t\tlogger.debug('All hooks already registered, skipping settings.json update')\n\t\t\treturn\n\t\t}\n\n\t\t// Write updated settings\n\t\tlet content: string\n\n\t\t// Check if existing content has comments\n\t\tif (existingContent.includes('//') || existingContent.includes('/*')) {\n\t\t\t// Use jsonc-parser to preserve comments\n\t\t\tlet modifiedContent = existingContent\n\t\t\tconst edits = modify(modifiedContent, ['hooks'], mergedHooks, {})\n\t\t\tcontent = applyEdits(modifiedContent, edits)\n\t\t} else {\n\t\t\t// No comments - use JSON.stringify\n\t\t\tconst updatedSettings: ClaudeSettings = {\n\t\t\t\t...existingSettings,\n\t\t\t\thooks: mergedHooks\n\t\t\t}\n\t\t\tcontent = JSON.stringify(updatedSettings, null, 2) + '\\n'\n\t\t}\n\n\t\t// Write atomically using temp file + rename\n\t\tconst tempPath = `${this.settingsPath}.tmp`\n\t\tawait fs.writeFile(tempPath, content, 'utf8')\n\t\tawait fs.rename(tempPath, this.settingsPath)\n\n\t\tlogger.debug('Hook configuration merged into settings.json')\n\t}\n\n\t/**\n\t * Get the hook configuration to register\n\t *\n\t * Each event maps to a hook that runs iloom-hook.js\n\t */\n\tprivate getHookConfig(): Record<string, HookEventConfig[]> {\n\t\tconst hookCommand = `node ${path.join(this.hooksDir, 'iloom-hook.js')}`\n\n\t\treturn {\n\t\t\tNotification: [\n\t\t\t\t{ hooks: [{ type: 'command', command: hookCommand }] }\n\t\t\t],\n\t\t\tStop: [\n\t\t\t\t{ hooks: [{ type: 'command', command: hookCommand }] }\n\t\t\t],\n\t\t\tSubagentStop: [\n\t\t\t\t{ hooks: [{ type: 'command', command: hookCommand }] }\n\t\t\t],\n\t\t\tPermissionRequest: [\n\t\t\t\t{ matcher: '*', hooks: [{ type: 'command', command: hookCommand, timeout: 86400 }] }\n\t\t\t],\n\t\t\tPreToolUse: [\n\t\t\t\t{ matcher: '*', hooks: [{ type: 'command', command: hookCommand }] }\n\t\t\t],\n\t\t\tPostToolUse: [\n\t\t\t\t{ matcher: '*', hooks: [{ type: 'command', command: hookCommand }] }\n\t\t\t],\n\t\t\tSessionStart: [\n\t\t\t\t{ matcher: '*', hooks: [{ type: 'command', command: hookCommand }] }\n\t\t\t],\n\t\t\tSessionEnd: [\n\t\t\t\t{ hooks: [{ type: 'command', command: hookCommand }] }\n\t\t\t],\n\t\t\tUserPromptSubmit: [\n\t\t\t\t{ hooks: [{ type: 'command', command: hookCommand }] }\n\t\t\t]\n\t\t}\n\t}\n}\n","import path from 'path'\nimport fs from 'fs-extra'\nimport { GitWorktreeManager } from './GitWorktreeManager.js'\nimport { MetadataManager, type WriteMetadataInput, type SwarmState } from './MetadataManager.js'\nimport { AgentManager } from './AgentManager.js'\nimport { SettingsManager, type IloomSettings } from './SettingsManager.js'\nimport { PromptTemplateManager, buildReviewTemplateVariables, type TemplateVariables } from './PromptTemplateManager.js'\nimport { IssueTrackerFactory } from './IssueTrackerFactory.js'\nimport { IssueManagementProviderFactory } from '../mcp/IssueManagementProviderFactory.js'\nimport { getLogger } from '../utils/logger-context.js'\nimport { preAcceptClaudeTrust } from '../utils/claude-trust.js'\nimport { installDependencies } from '../utils/package-manager.js'\nimport { generateWorktreePath } from '../utils/git.js'\nimport { generateAndWriteMcpConfigFile } from '../utils/mcp.js'\n\n/**\n * Result of the swarm setup process\n */\nexport interface SwarmSetupResult {\n\tepicWorktreePath: string\n\tepicBranch: string\n\tchildWorktrees: Array<{\n\t\tissueId: string\n\t\tworktreePath: string\n\t\tbranch: string\n\t\tsuccess: boolean\n\t\terror?: string\n\t}>\n\tagentsRendered: string[]\n\tworkerAgentRendered: boolean\n}\n\n/**\n * Metadata extracted from agent YAML frontmatter for use in claude -p commands.\n * Maps agent file name (without .md) to model and tools info.\n */\nexport interface SwarmAgentMetadata {\n\t[agentFileName: string]: {\n\t\tmodel: string\n\t\ttools?: string[]\n\t}\n}\n\n/**\n * Child issue data as stored in epic metadata\n */\nexport interface SwarmChildIssue {\n\tnumber: string // Prefixed: \"#123\" for GitHub, \"ENG-123\" for Linear\n\ttitle: string\n\tbody: string\n\turl: string\n}\n\n/**\n * SwarmSetupService handles the creation of child worktrees\n * for swarm mode, plus rendering swarm-mode agents and skill files.\n *\n * Called from the spin command (ignite.ts) when an epic loom is detected.\n * The epic worktree already exists (created by `il start`).\n */\nexport class SwarmSetupService {\n\tconstructor(\n\t\tprivate gitWorktree: GitWorktreeManager,\n\t\tprivate metadataManager: MetadataManager,\n\t\tprivate agentManager: AgentManager,\n\t\tprivate settingsManager: SettingsManager,\n\t\tprivate templateManager: PromptTemplateManager,\n\t) {}\n\n\t/**\n\t * Create child worktrees for each child issue, branched off the epic branch.\n\t * Writes iloom-metadata.json for each child with state: 'pending' and parentLoom.\n\t * Generates and writes per-loom MCP config file for each child.\n\t *\n\t * Uses standard iloom naming conventions via generateWorktreePath().\n\t *\n\t * @param childIssues - Array of child issues from epic metadata\n\t * @param epicBranch - The epic branch name (base branch for children)\n\t * @param epicWorktreePath - Path to the epic worktree\n\t * @param mainWorktreePath - Path to the main worktree (project root)\n\t * @param epicIssueNumber - The parent epic issue number\n\t * @param issueTrackerName - The issue tracker provider name (e.g., 'github')\n\t * @param settings - Optional settings for MCP config generation\n\t * @returns Array of results for each child worktree creation\n\t */\n\tasync createChildWorktrees(\n\t\tchildIssues: SwarmChildIssue[],\n\t\tepicBranch: string,\n\t\tepicWorktreePath: string,\n\t\tmainWorktreePath: string,\n\t\tepicIssueNumber: string | number,\n\t\tissueTrackerName: string,\n\t\tsettings?: IloomSettings,\n\t): Promise<SwarmSetupResult['childWorktrees']> {\n\t\treturn Promise.all(childIssues.map(async (child) => {\n\t\t\ttry {\n\t\t\t\t// Strip prefix from child number (e.g., \"#123\" -> \"123\", \"ENG-123\" stays as-is for branch naming)\n\t\t\t\tconst rawId = child.number.replace(/^#/, '')\n\n\t\t\t\t// Sanitize ID for safe git branch naming (replace non-alphanumeric except - and _ with -)\n\t\t\t\tconst safeId = rawId.replace(/[^a-zA-Z0-9-_]/g, '-')\n\n\t\t\t\t// Use standard iloom branch naming: issue/<id> pattern\n\t\t\t\tconst childBranch = `issue/${safeId}`\n\n\t\t\t\t// Use standard iloom worktree path generation\n\t\t\t\tconst childWorktreePath = generateWorktreePath(\n\t\t\t\t\tchildBranch,\n\t\t\t\t\tmainWorktreePath,\n\t\t\t\t)\n\n\t\t\t\tgetLogger().info(`Creating child worktree for ${child.number}: ${childWorktreePath}...`)\n\n\t\t\t\tawait this.gitWorktree.createWorktree({\n\t\t\t\t\tpath: childWorktreePath,\n\t\t\t\t\tbranch: childBranch,\n\t\t\t\t\tcreateBranch: true,\n\t\t\t\t\tbaseBranch: epicBranch,\n\t\t\t\t})\n\n\t\t\t\t// Pre-accept Claude Code trust for child worktree\n\t\t\t\ttry {\n\t\t\t\t\tawait preAcceptClaudeTrust(childWorktreePath)\n\t\t\t\t} catch (error) {\n\t\t\t\t\tgetLogger().warn(`Failed to pre-accept Claude trust for child worktree: ${error instanceof Error ? error.message : String(error)}`)\n\t\t\t\t}\n\n\t\t\t\t// Write metadata with state: 'pending' and parentLoom\n\t\t\t\tconst metadataInput: WriteMetadataInput = {\n\t\t\t\t\tdescription: child.title,\n\t\t\t\t\tbranchName: childBranch,\n\t\t\t\t\tworktreePath: childWorktreePath,\n\t\t\t\t\tissueType: 'issue',\n\t\t\t\t\tissue_numbers: [rawId],\n\t\t\t\t\tpr_numbers: [],\n\t\t\t\t\tissueTracker: issueTrackerName,\n\t\t\t\t\tcolorHex: '#808080',\n\t\t\t\t\tsessionId: '', // No session - not launching Claude directly\n\t\t\t\t\tprojectPath: mainWorktreePath,\n\t\t\t\t\tissueUrls: { [rawId]: child.url },\n\t\t\t\t\tprUrls: {},\n\t\t\t\t\tcapabilities: [],\n\t\t\t\t\tstate: 'pending' as SwarmState,\n\t\t\t\t\tparentLoom: {\n\t\t\t\t\t\ttype: 'epic',\n\t\t\t\t\t\tidentifier: epicIssueNumber,\n\t\t\t\t\t\tbranchName: epicBranch,\n\t\t\t\t\t\tworktreePath: epicWorktreePath,\n\t\t\t\t\t},\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tawait this.metadataManager.writeMetadata(childWorktreePath, metadataInput)\n\t\t\t\t} catch (metaError) {\n\t\t\t\t\t// Clean up the worktree to avoid zombie worktrees without metadata\n\t\t\t\t\tgetLogger().warn(`Metadata write failed for ${child.number}, cleaning up worktree...`)\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait this.gitWorktree.removeWorktree(childWorktreePath, { force: true })\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tgetLogger().debug(`Could not clean up worktree at ${childWorktreePath}`)\n\t\t\t\t\t}\n\t\t\t\t\tthrow metaError\n\t\t\t\t}\n\n\t\t\t\t// Generate and write per-loom MCP config file\n\t\t\t\ttry {\n\t\t\t\t\tconst childMetadata = await this.metadataManager.readMetadata(childWorktreePath)\n\t\t\t\t\tif (childMetadata) {\n\t\t\t\t\t\tconst providerName = IssueTrackerFactory.getProviderName(\n\t\t\t\t\t\t\tsettings ?? await this.settingsManager.loadSettings(),\n\t\t\t\t\t\t) as 'github' | 'linear' | 'jira'\n\t\t\t\t\t\tconst mcpConfigPath = await generateAndWriteMcpConfigFile(\n\t\t\t\t\t\t\tchildWorktreePath,\n\t\t\t\t\t\t\tchildMetadata,\n\t\t\t\t\t\t\tproviderName,\n\t\t\t\t\t\t\tsettings,\n\t\t\t\t\t\t)\n\t\t\t\t\t\tawait this.metadataManager.updateMetadata(childWorktreePath, { mcpConfigPath })\n\n\t\t\t\t\t\t// Write MCP config path to .claude/iloom-swarm-mcp-config-path for worker discovery\n\t\t\t\t\t\tconst claudeDir = path.join(childWorktreePath, '.claude')\n\t\t\t\t\t\tawait fs.ensureDir(claudeDir)\n\t\t\t\t\t\tawait fs.writeFile(\n\t\t\t\t\t\t\tpath.join(claudeDir, 'iloom-swarm-mcp-config-path'),\n\t\t\t\t\t\t\tmcpConfigPath,\n\t\t\t\t\t\t\t'utf-8',\n\t\t\t\t\t\t)\n\n\t\t\t\t\t\tgetLogger().debug(`Wrote MCP config for ${child.number}: ${mcpConfigPath}`)\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// Non-fatal: child can still work without MCP config\n\t\t\t\t\tgetLogger().warn(\n\t\t\t\t\t\t`Failed to write MCP config for child ${child.number}: ${error instanceof Error ? error.message : 'Unknown error'}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\n\t\t\t\t// Install dependencies in the child worktree\n\t\t\t\ttry {\n\t\t\t\t\tawait installDependencies(childWorktreePath, true, true)\n\t\t\t\t} catch (error) {\n\t\t\t\t\tgetLogger().warn(\n\t\t\t\t\t\t`Failed to install dependencies in child worktree ${child.number}: ${error instanceof Error ? error.message : 'Unknown error'}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\n\t\t\t\tgetLogger().success(`Created child worktree for ${child.number}`)\n\t\t\t\treturn {\n\t\t\t\t\tissueId: rawId,\n\t\t\t\t\tworktreePath: childWorktreePath,\n\t\t\t\t\tbranch: childBranch,\n\t\t\t\t\tsuccess: true,\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst rawId = child.number.replace(/^#/, '')\n\t\t\t\tconst errorMessage = error instanceof Error ? error.message : 'Unknown error'\n\t\t\t\tgetLogger().warn(`Failed to create child worktree for ${child.number}: ${errorMessage}`)\n\t\t\t\treturn {\n\t\t\t\t\tissueId: rawId,\n\t\t\t\t\tworktreePath: '',\n\t\t\t\t\tbranch: '',\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: errorMessage,\n\t\t\t\t}\n\t\t\t}\n\t\t}))\n\t}\n\n\t/**\n\t * Render swarm-mode agent templates to the epic worktree's .claude/agents/ directory.\n\t *\n\t * Phase agent files are written WITHOUT frontmatter (prompt body only) because they are\n\t * loaded via `--append-system-prompt-file` which does not parse YAML frontmatter.\n\t * Model and tools metadata is extracted from the agent config and returned separately\n\t * for use as CLI flags in `claude -p` commands.\n\t */\n\tasync renderSwarmAgents(epicWorktreePath: string): Promise<{\n\t\trenderedFiles: string[]\n\t\tmetadata: SwarmAgentMetadata\n\t}> {\n\t\tconst claudeAgentsDir = path.join(epicWorktreePath, '.claude', 'agents')\n\t\tawait fs.ensureDir(claudeAgentsDir)\n\n\t\tconst settings = await this.settingsManager.loadSettings()\n\n\t\tconst templateVariables: TemplateVariables = {\n\t\t\tSWARM_MODE: true,\n\t\t}\n\n\t\tconst agents = await this.agentManager.loadAgents(settings, templateVariables)\n\n\t\t// Default swarmModel map for \"Balanced\" mode. All swarm phase agents are\n\t\t// listed explicitly so that swarm mode never accidentally inherits a\n\t\t// non-swarm model override. User-configured swarmModel values always\n\t\t// take precedence.\n\t\tconst defaultSwarmModels: Record<string, 'sonnet' | 'opus' | 'haiku'> = {\n\t\t\t'iloom-issue-analyzer': 'opus',\n\t\t\t'iloom-issue-analyze-and-plan': 'opus',\n\t\t\t'iloom-issue-planner': 'sonnet',\n\t\t\t'iloom-issue-implementer': 'sonnet',\n\t\t\t'iloom-issue-enhancer': 'sonnet',\n\t\t\t'iloom-code-reviewer': 'sonnet',\n\t\t\t'iloom-issue-complexity-evaluator': 'haiku',\n\t\t}\n\n\t\t// Apply per-agent swarmModel overrides (user-configured takes precedence over defaults)\n\t\tfor (const [agentName, agentConfig] of Object.entries(agents)) {\n\t\t\tconst userSwarmModel = settings?.agents?.[agentName]?.swarmModel\n\t\t\tif (userSwarmModel) {\n\t\t\t\tagents[agentName] = { ...agentConfig, model: userSwarmModel }\n\t\t\t} else if (defaultSwarmModels[agentName]) {\n\t\t\t\tagents[agentName] = { ...agentConfig, model: defaultSwarmModels[agentName] }\n\t\t\t}\n\t\t}\n\n\t\tconst renderedFiles: string[] = []\n\t\tconst metadata: SwarmAgentMetadata = {}\n\n\t\tfor (const [agentName, agentConfig] of Object.entries(agents)) {\n\t\t\tconst swarmFileName = agentName.startsWith('iloom-')\n\t\t\t\t? `iloom-swarm-${agentName.slice('iloom-'.length)}.md`\n\t\t\t\t: `iloom-swarm-${agentName}.md`\n\n\t\t\tconst agentKey = swarmFileName.replace('.md', '')\n\n\t\t\t// Extract metadata from agent config for use in claude -p CLI flags\n\t\t\tmetadata[agentKey] = {\n\t\t\t\tmodel: agentConfig.model,\n\t\t\t\t...(agentConfig.tools && { tools: agentConfig.tools }),\n\t\t\t}\n\n\t\t\t// Write file WITHOUT frontmatter - prompt body only\n\t\t\t// Phase agents are loaded via --append-system-prompt-file which does not parse YAML frontmatter\n\t\t\tconst outputPath = path.join(claudeAgentsDir, swarmFileName)\n\t\t\tawait fs.writeFile(outputPath, agentConfig.prompt + '\\n', 'utf-8')\n\t\t\trenderedFiles.push(swarmFileName)\n\t\t\tgetLogger().debug(`Rendered swarm agent: ${swarmFileName}`)\n\t\t}\n\n\t\tgetLogger().success(`Rendered ${renderedFiles.length} swarm agents to ${claudeAgentsDir}`)\n\t\treturn { renderedFiles, metadata }\n\t}\n\n\t/**\n\t * Render the swarm worker agent file to the epic worktree's .claude/agents/ directory.\n\t *\n\t * This creates an agent file at `.claude/agents/iloom-swarm-worker.md` containing\n\t * the full iloom workflow instructions (rendered from issue-prompt.txt with SWARM_MODE=true).\n\t * The orchestrator spawns children with `subagent_type: \"iloom-swarm-worker\"` so these\n\t * instructions become the agent's system prompt (high authority), rather than arriving\n\t * as a skill invocation (low authority user message).\n\t *\n\t * The agent file is shared across all children. Issue-specific context (number, title,\n\t * worktree path, body) is provided per-child via the Task prompt from the orchestrator.\n\t */\n\tasync renderSwarmWorkerAgent(\n\t\tepicWorktreePath: string,\n\t\tagentMetadata?: SwarmAgentMetadata,\n\t): Promise<boolean> {\n\t\tconst agentsDir = path.join(epicWorktreePath, '.claude', 'agents')\n\t\tconst agentOutputPath = path.join(agentsDir, 'iloom-swarm-worker.md')\n\n\t\tawait fs.ensureDir(agentsDir)\n\n\t\ttry {\n\t\t\t// Load settings for review configuration and issue prefix\n\t\t\tconst settings = await this.settingsManager.loadSettings()\n\t\t\tconst providerType = settings?.issueManagement?.provider ?? 'github'\n\t\t\tconst issuePrefix = IssueManagementProviderFactory.create(providerType, settings ?? undefined).issuePrefix\n\n\t\t\t// Compute sub-agent timeout in milliseconds (default: 10 minutes)\n\t\t\tconst subAgentTimeoutMinutes = settings?.agents?.['iloom-swarm-worker']?.subAgentTimeout ?? 10\n\t\t\tconst subAgentTimeoutMs = subAgentTimeoutMinutes * 60 * 1000\n\n\t\t\t// Build template variables for swarm worker agent rendering\n\t\t\tconst variables: TemplateVariables = {\n\t\t\t\tSWARM_MODE: true,\n\t\t\t\tONE_SHOT_MODE: true,\n\t\t\t\tCOMPLEXITY_OVERRIDE: 'simple',\n\t\t\t\tEPIC_WORKTREE_PATH: epicWorktreePath,\n\t\t\t\tISSUE_PREFIX: issuePrefix,\n\t\t\t\tSWARM_SUB_AGENT_TIMEOUT_MS: subAgentTimeoutMs,\n\t\t\t\t...(agentMetadata && { SWARM_AGENT_METADATA: JSON.stringify(agentMetadata) }),\n\t\t\t\t...buildReviewTemplateVariables(true, settings?.agents),\n\t\t\t}\n\n\t\t\t// Render issue prompt template with swarm variables\n\t\t\tconst agentBody = await this.templateManager.getPrompt('issue', variables)\n\n\t\t\t// Build the agent file with frontmatter\n\t\t\tconst workerModel = settings?.agents?.['iloom-swarm-worker']?.model ?? 'opus'\n\n\t\t\tconst frontmatter = [\n\t\t\t\t'---',\n\t\t\t\t'name: iloom-swarm-worker',\n\t\t\t\t'description: Swarm worker agent that implements a child issue following the full iloom workflow.',\n\t\t\t\t`model: ${workerModel}`,\n\t\t\t\t'---',\n\t\t\t].join('\\n')\n\n\t\t\tconst content = `${frontmatter}\\n\\n${agentBody}\\n`\n\n\t\t\tawait fs.writeFile(agentOutputPath, content, 'utf-8')\n\t\t\tgetLogger().success(`Rendered swarm worker agent to ${agentOutputPath}`)\n\t\t\treturn true\n\t\t} catch (error) {\n\t\t\t// Intentional graceful degradation: setupSwarm reports workerAgentRendered=false\n\t\t\t// in its result rather than aborting the entire swarm setup.\n\t\t\tgetLogger().warn(\n\t\t\t\t`Failed to render swarm worker agent: ${error instanceof Error ? error.message : 'Unknown error'}`,\n\t\t\t)\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Copy .claude/agents/ from the epic worktree to each child worktree.\n\t *\n\t * Child workers need local access to agent files (used via --append-system-prompt-file).\n\t * Without this copy, child worktrees lack the rendered agent files since they only\n\t * exist in the epic worktree after renderSwarmAgents/renderSwarmWorkerAgent.\n\t */\n\tasync copyAgentsToChildWorktrees(\n\t\tepicWorktreePath: string,\n\t\tchildWorktrees: SwarmSetupResult['childWorktrees'],\n\t): Promise<void> {\n\t\tconst sourceDir = path.join(epicWorktreePath, '.claude', 'agents')\n\n\t\tif (!await fs.pathExists(sourceDir)) {\n\t\t\tgetLogger().warn('No .claude/agents/ directory in epic worktree to copy')\n\t\t\treturn\n\t\t}\n\n\t\tconst successfulChildren = childWorktrees.filter((c) => c.success)\n\n\t\tawait Promise.all(successfulChildren.map(async (child) => {\n\t\t\ttry {\n\t\t\t\tconst targetDir = path.join(child.worktreePath, '.claude', 'agents')\n\t\t\t\tawait fs.copy(sourceDir, targetDir, { overwrite: true })\n\t\t\t\tgetLogger().debug(`Copied .claude/agents/ to ${child.worktreePath}`)\n\t\t\t} catch (error) {\n\t\t\t\t// Non-fatal: worker can fall back to epic worktree path\n\t\t\t\tgetLogger().warn(\n\t\t\t\t\t`Failed to copy agents to child worktree ${child.issueId}: ${error instanceof Error ? error.message : 'Unknown error'}`,\n\t\t\t\t)\n\t\t\t}\n\t\t}))\n\n\t\tgetLogger().success(`Copied agents to ${successfulChildren.length} child worktrees`)\n\t}\n\n\t/**\n\t * Run the full swarm setup: child worktrees, agents, and worker agent.\n\t *\n\t * The epic worktree already exists (created by `il start`).\n\t */\n\tasync setupSwarm(\n\t\tepicIssueNumber: string | number,\n\t\tepicBranch: string,\n\t\tepicWorktreePath: string,\n\t\tchildIssues: SwarmChildIssue[],\n\t\tmainWorktreePath: string,\n\t\tissueTrackerName: string,\n\t\tsettings?: IloomSettings,\n\t): Promise<SwarmSetupResult> {\n\t\t// 1. Create child worktrees (with per-loom MCP config generation)\n\t\tconst childWorktrees = await this.createChildWorktrees(\n\t\t\tchildIssues,\n\t\t\tepicBranch,\n\t\t\tepicWorktreePath,\n\t\t\tmainWorktreePath,\n\t\t\tepicIssueNumber,\n\t\t\tissueTrackerName,\n\t\t\tsettings,\n\t\t)\n\n\t\t// 2. Render swarm agents to epic worktree's .claude/ directory (returns metadata)\n\t\tconst { renderedFiles: agentsRendered, metadata: agentMetadata } =\n\t\t\tawait this.renderSwarmAgents(epicWorktreePath)\n\n\t\t// 3. Render the swarm worker agent file with agent metadata\n\t\tconst workerAgentRendered = await this.renderSwarmWorkerAgent(\n\t\t\tepicWorktreePath,\n\t\t\tagentMetadata,\n\t\t)\n\n\t\t// 4. Copy .claude/agents/ from epic worktree to each child worktree\n\t\tawait this.copyAgentsToChildWorktrees(epicWorktreePath, childWorktrees)\n\n\t\tconst successCount = childWorktrees.filter((c) => c.success).length\n\t\tconst failCount = childWorktrees.filter((c) => !c.success).length\n\n\t\tgetLogger().success(\n\t\t\t`Swarm setup complete: ${successCount} child worktrees` +\n\t\t\t\t(failCount > 0 ? ` (${failCount} failed)` : ''),\n\t\t)\n\n\t\treturn {\n\t\t\tepicWorktreePath,\n\t\t\tepicBranch,\n\t\t\tchildWorktrees,\n\t\t\tagentsRendered,\n\t\t\tworkerAgentRendered,\n\t\t}\n\t}\n}\n","import fs from 'fs-extra'\nimport path from 'path'\nimport fg from 'fast-glob'\n\n/**\n * Detect the primary programming language of a project by checking for common project files.\n * Checks are performed in priority order; the first match wins.\n * All errors are caught and return 'unknown' — this function is non-blocking.\n */\nexport async function detectProjectLanguage(projectPath: string): Promise<string> {\n try {\n // 1. package.json → check for 'typescript' in deps/devDeps → 'typescript' or 'javascript'\n const packageJsonPath = path.join(projectPath, 'package.json')\n if (await fs.pathExists(packageJsonPath)) {\n try {\n const pkg = await fs.readJson(packageJsonPath) as {\n dependencies?: Record<string, string>\n devDependencies?: Record<string, string>\n }\n const allDeps = { ...pkg.dependencies, ...pkg.devDependencies }\n return 'typescript' in allDeps ? 'typescript' : 'javascript'\n } catch {\n return 'javascript'\n }\n }\n\n // 2. Cargo.toml → 'rust'\n if (await fs.pathExists(path.join(projectPath, 'Cargo.toml'))) {\n return 'rust'\n }\n\n // 3. go.mod → 'go'\n if (await fs.pathExists(path.join(projectPath, 'go.mod'))) {\n return 'go'\n }\n\n // 4. pyproject.toml | setup.py | requirements.txt → 'python'\n for (const file of ['pyproject.toml', 'setup.py', 'requirements.txt']) {\n if (await fs.pathExists(path.join(projectPath, file))) {\n return 'python'\n }\n }\n\n // 5. Gemfile → 'ruby'\n if (await fs.pathExists(path.join(projectPath, 'Gemfile'))) {\n return 'ruby'\n }\n\n // 6. pom.xml | build.gradle | build.gradle.kts → 'java'\n for (const file of ['pom.xml', 'build.gradle', 'build.gradle.kts']) {\n if (await fs.pathExists(path.join(projectPath, file))) {\n return 'java'\n }\n }\n\n // 7. *.csproj via glob → 'csharp'\n const csprojFiles = await fg('*.csproj', { cwd: projectPath, dot: false })\n if (csprojFiles.length > 0) {\n return 'csharp'\n }\n\n // 8. Default\n return 'unknown'\n } catch {\n return 'unknown'\n }\n}\n","import path from 'path'\nimport fs from 'fs-extra'\n\n/**\n * Result of preparing the system prompt for a specific platform.\n * Exactly one of these strategies will be populated.\n */\nexport interface SystemPromptConfig {\n\t/** Inline system prompt (macOS + Linux) */\n\tappendSystemPrompt?: string\n\t/** Plugin directory for --plugin-dir (Windows) */\n\tpluginDir?: string\n\t/** Override the initial user prompt (Windows: '/clear' to trigger SessionStart) */\n\tinitialPromptOverride?: string\n}\n\n/**\n * Prepare the system prompt for the current platform.\n *\n * - darwin: inline via --append-system-prompt (unchanged)\n * - linux: inline via --append-system-prompt (80KB < 128KB limit)\n * - win32: write to file, create SessionStart plugin, pass /clear\n */\nexport async function prepareSystemPromptForPlatform(\n\tsystemPrompt: string,\n\tworkspacePath: string,\n\tplatform: string = process.platform,\n): Promise<SystemPromptConfig> {\n\tif (platform === 'darwin' || platform === 'linux') {\n\t\t// macOS and Linux: inline system prompt\n\t\treturn { appendSystemPrompt: systemPrompt }\n\t}\n\n\t// Windows: write system prompt to file, create SessionStart hook plugin\n\tconst claudeDir = path.join(workspacePath, '.claude')\n\tconst promptFilePath = path.join(claudeDir, 'iloom-system-prompt.md')\n\tconst pluginDir = path.join(claudeDir, 'iloom-plugin')\n\n\tawait fs.ensureDir(claudeDir)\n\tawait fs.writeFile(promptFilePath, systemPrompt, 'utf-8')\n\n\t// Create plugin directory with SessionStart hook\n\tawait createSessionStartPlugin(pluginDir, promptFilePath)\n\n\treturn {\n\t\tpluginDir,\n\t\tinitialPromptOverride: '/clear',\n\t}\n}\n\n/**\n * Create a SessionStart hook plugin that injects the system prompt file content.\n *\n * Writes a small runner.js script that reads and outputs the prompt file,\n * since `cat` is not available natively on Windows and Node.js is guaranteed\n * to be present (Claude Code requires it).\n *\n * Uses a runner file instead of `node -e` to avoid command injection when\n * the workspace path contains quotes or special characters.\n */\nexport async function createSessionStartPlugin(\n\tpluginDir: string,\n\tpromptFilePath: string,\n): Promise<void> {\n\tawait fs.ensureDir(pluginDir)\n\n\t// Write a small runner script that safely embeds the file path via JSON.stringify\n\tconst runnerScript = `process.stdout.write(require('fs').readFileSync(${JSON.stringify(promptFilePath)}, 'utf-8'));`\n\tawait fs.writeFile(path.join(pluginDir, 'runner.js'), runnerScript, 'utf-8')\n\n\t// Use forward slashes in the hooks command for cross-platform portability\n\tconst portableRunnerPath = path.join(pluginDir, 'runner.js').replace(/\\\\/g, '/')\n\n\tconst hooksConfig = {\n\t\thooks: {\n\t\t\tSessionStart: [\n\t\t\t\t{\n\t\t\t\t\tmatcher: '*',\n\t\t\t\t\thooks: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: 'command' as const,\n\t\t\t\t\t\t\tcommand: `node \"${portableRunnerPath}\"`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t],\n\t\t},\n\t}\n\n\tawait fs.writeFile(\n\t\tpath.join(pluginDir, 'hooks.json'),\n\t\tJSON.stringify(hooksConfig, null, 2),\n\t\t'utf-8',\n\t)\n}\n"],"mappings":"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","names":["path","fs","_a","path","fs","path","fs","fs","path","path","fs","path","fs"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/MergeManager.ts","../src/lib/BuildRunner.ts"],"sourcesContent":["import { executeGitCommand, fetchOrigin, findMainWorktreePathWithSettings, findWorktreeForBranch, getMergeTargetBranch, GitCommandError } from '../utils/git.js'\nimport { getLogger } from '../utils/logger-context.js'\nimport { detectClaudeCli, launchClaude } from '../utils/claude.js'\nimport { SettingsManager } from './SettingsManager.js'\nimport { MetadataManager } from './MetadataManager.js'\nimport type { MergeOptions, RebaseOutcome } from '../types/index.js'\n\n/**\n * MergeManager handles Git rebase and fast-forward merge operations\n * Implements fail-fast behavior for conflicts (Phase 1 - no Claude assistance)\n *\n * Ports bash/merge-and-clean.sh lines 781-1090\n */\nexport class MergeManager {\n\tprivate settingsManager: SettingsManager\n\tprivate metadataManager: MetadataManager\n\n\tconstructor(settingsManager?: SettingsManager, metadataManager?: MetadataManager) {\n\t\tthis.settingsManager = settingsManager ?? new SettingsManager()\n\t\tthis.metadataManager = metadataManager ?? new MetadataManager()\n\t}\n\n\t/**\n\t * Get the merge target branch for a loom\n\t * Priority: parent loom metadata > configured main branch > 'main'\n\t * @param worktreePath - Optional path to load settings/metadata from (defaults to process.cwd())\n\t * @private\n\t */\n\tprivate async getMainBranch(worktreePath?: string): Promise<string> {\n\t\t// Delegate to shared utility function\n\t\treturn getMergeTargetBranch(worktreePath ?? process.cwd(), {\n\t\t\tsettingsManager: this.settingsManager,\n\t\t\tmetadataManager: this.metadataManager,\n\t\t})\n\t}\n\n\t/**\n\t * Rebase current branch on main with fail-fast on conflicts\n\t * Ports bash/merge-and-clean.sh lines 781-913\n\t *\n\t * @param worktreePath - Path to the worktree\n\t * @param options - Merge options (dryRun, force)\n\t * @throws Error if main branch doesn't exist, uncommitted changes exist, or conflicts occur\n\t */\n\tasync rebaseOnMain(worktreePath: string, options: MergeOptions = {}): Promise<RebaseOutcome> {\n\t\tconst { dryRun = false, force = false, jsonStream = false } = options\n\n\t\t// Pre-check: abort any in-progress rebase before starting a new one\n\t\tawait this.abortInProgressRebase(worktreePath)\n\n\t\tconst mainBranch = await this.getMainBranch(worktreePath)\n\n\t\t// Determine whether to use remote (origin/) or local branch reference\n\t\t// - Child looms: always use local parent branch (parent may not be pushed)\n\t\t// - PR modes (github-pr, github-draft-pr) for non-child: fetch and use origin/{branch}\n\t\t// - Local mode: use local branch (no fetch)\n\t\tconst metadata = await this.metadataManager.readMetadata(worktreePath)\n\t\tconst isChildLoom = !!metadata?.parentLoom\n\t\tconst settings = await this.settingsManager.loadSettings(worktreePath)\n\t\tconst mergeBehaviorMode = settings.mergeBehavior?.mode ?? 'local'\n\t\tconst isPRMode = mergeBehaviorMode === 'github-pr' || mergeBehaviorMode === 'github-draft-pr'\n\t\tconst useRemote = isPRMode && !isChildLoom\n\n\t\tlet targetBranch: string\n\t\tif (useRemote) {\n\t\t\t// PR modes (non-child): fetch and use origin/{branch}\n\t\t\tgetLogger().info('Fetching from origin...')\n\t\t\tawait fetchOrigin(worktreePath)\n\t\t\ttargetBranch = `origin/${mainBranch}`\n\t\t} else {\n\t\t\t// Local mode or child loom: use local branch\n\t\t\tgetLogger().info(`Using local branch ${mainBranch} for rebase...`)\n\t\t\ttargetBranch = mainBranch\n\t\t}\n\n\t\tgetLogger().info(`Starting rebase on ${targetBranch}...`)\n\n\t\t// Step 1: Check if branch exists (remote ref for origin/, local ref otherwise)\n\t\tconst refPath = useRemote ? `refs/remotes/${targetBranch}` : `refs/heads/${targetBranch}`\n\t\ttry {\n\t\t\tawait executeGitCommand(['show-ref', '--verify', '--quiet', refPath], {\n\t\t\t\tcwd: worktreePath,\n\t\t\t})\n\t\t} catch {\n\t\t\tif (useRemote) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Remote branch \"${targetBranch}\" does not exist. Cannot rebase.\\n` +\n\t\t\t\t\t\t`Ensure the repository has a \"${mainBranch}\" branch on origin.`\n\t\t\t\t)\n\t\t\t} else {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Local branch \"${targetBranch}\" does not exist. Cannot rebase.\\n` +\n\t\t\t\t\t\t`Ensure the branch exists locally.`\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\t// Step 2: Check for uncommitted changes and create WIP commit if needed\n\t\tconst statusOutput = await executeGitCommand(['status', '--porcelain'], {\n\t\t\tcwd: worktreePath,\n\t\t})\n\n\t\tlet wipCommitHash: string | null = null\n\t\tif (statusOutput.trim()) {\n\t\t\tgetLogger().info('Uncommitted changes detected, creating temporary WIP commit...')\n\t\t\twipCommitHash = await this.createWipCommit(worktreePath)\n\t\t\tgetLogger().debug(`Created WIP commit: ${wipCommitHash}`)\n\t\t}\n\n\t\t// Step 3: Check if rebase is needed by comparing merge-base with target HEAD\n\t\tconst mergeBase = await executeGitCommand(['merge-base', targetBranch, 'HEAD'], {\n\t\t\tcwd: worktreePath,\n\t\t})\n\n\t\tconst targetHead = await executeGitCommand(['rev-parse', targetBranch], {\n\t\t\tcwd: worktreePath,\n\t\t})\n\n\t\tconst mergeBaseTrimmed = mergeBase.trim()\n\t\tconst targetHeadTrimmed = targetHead.trim()\n\n\t\t// If merge-base matches target HEAD, branch is already up to date\n\t\tif (mergeBaseTrimmed === targetHeadTrimmed) {\n\t\t\tgetLogger().success(`Branch is already up to date with ${targetBranch}. No rebase needed.`)\n\t\t\t// Restore WIP commit if created (soft reset to remove temporary commit)\n\t\t\tif (wipCommitHash) {\n\t\t\t\tawait this.restoreWipCommit(worktreePath, wipCommitHash)\n\t\t\t}\n\t\t\treturn { conflictsDetected: false, claudeLaunched: false, conflictsResolved: false }\n\t\t}\n\n\t\t// Step 4: Show commits to be rebased (for informational purposes)\n\t\tconst commitsOutput = await executeGitCommand(['log', '--oneline', `${targetBranch}..HEAD`], {\n\t\t\tcwd: worktreePath,\n\t\t})\n\n\t\tconst commits = commitsOutput.trim()\n\t\tconst commitLines = commits ? commits.split('\\n') : []\n\n\t\tif (commits) {\n\t\t\t// Show commits that will be rebased\n\t\t\tgetLogger().info(`Found ${commitLines.length} commit(s) to rebase:`)\n\t\t\tcommitLines.forEach((commit) => getLogger().info(` ${commit}`))\n\t\t} else {\n\t\t\t// Target has moved forward but branch has no new commits\n\t\t\tgetLogger().info(`${targetBranch} has moved forward. Rebasing to update branch...`)\n\t\t}\n\n\t\t// Step 5: User confirmation (unless force mode or dry-run)\n\t\tif (!force && !dryRun) {\n\t\t\t// TODO: Implement interactive prompt for confirmation\n\t\t\t// For now, proceeding automatically (use --force to skip this message)\n\t\t\tgetLogger().info('Proceeding with rebase... (use --force to skip confirmations)')\n\t\t}\n\n\t\t// Step 6: Execute rebase (unless dry-run)\n\t\tif (dryRun) {\n\t\t\tgetLogger().info(`[DRY RUN] Would execute: git rebase ${targetBranch}`)\n\t\t\tif (commitLines.length > 0) {\n\t\t\t\tgetLogger().info(`[DRY RUN] This would rebase ${commitLines.length} commit(s)`)\n\t\t\t}\n\t\t\treturn { conflictsDetected: false, claudeLaunched: false, conflictsResolved: false }\n\t\t}\n\n\t\t// Execute rebase\n\t\t// Use -c core.hooksPath=/dev/null to disable hooks during rebase\n\t\t// This prevents pre-commit hooks from running when commits are re-applied\n\t\ttry {\n\t\t\tawait executeGitCommand(['-c', 'core.hooksPath=/dev/null', 'rebase', targetBranch], { cwd: worktreePath })\n\t\t\tgetLogger().success('Rebase completed successfully!')\n\n\t\t\t// Restore WIP commit if created\n\t\t\tif (wipCommitHash) {\n\t\t\t\tawait this.restoreWipCommit(worktreePath, wipCommitHash)\n\t\t\t}\n\t\t\treturn { conflictsDetected: false, claudeLaunched: false, conflictsResolved: false }\n\t\t} catch (error) {\n\t\t\t// Detect conflicts\n\t\t\tconst conflictedFiles = await this.detectConflictedFiles(worktreePath)\n\n\t\t\tif (conflictedFiles.length > 0) {\n\t\t\t\t// Try Claude-assisted resolution first\n\t\t\t\tgetLogger().info('Merge conflicts detected, attempting Claude-assisted resolution...')\n\n\t\t\t\tconst resolved = await this.attemptClaudeConflictResolution(\n\t\t\t\t\tworktreePath,\n\t\t\t\t\tconflictedFiles,\n\t\t\t\t\t{ jsonStream }\n\t\t\t\t)\n\n\t\t\t\tif (resolved) {\n\t\t\t\t\tgetLogger().success('Conflicts resolved with Claude assistance, rebase completed')\n\n\t\t\t\t\t// Restore WIP commit if created\n\t\t\t\t\tif (wipCommitHash) {\n\t\t\t\t\t\tawait this.restoreWipCommit(worktreePath, wipCommitHash)\n\t\t\t\t\t}\n\t\t\t\t\treturn { conflictsDetected: true, claudeLaunched: true, conflictsResolved: true }\n\t\t\t\t}\n\n\t\t\t\t// Claude couldn't resolve or not available - fail fast\n\t\t\t\tconst conflictError = this.formatConflictError(conflictedFiles)\n\t\t\t\tthrow new Error(conflictError)\n\t\t\t}\n\n\t\t\t// If not a conflict, re-throw the original error\n\t\t\tthrow new Error(\n\t\t\t\t`Rebase failed: ${error instanceof Error ? error.message : String(error)}\\n` +\n\t\t\t\t\t'Run: git status for more details\\n' +\n\t\t\t\t\t'Or: git rebase --abort to cancel the rebase'\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Validate that fast-forward merge is possible\n\t * Ports bash/merge-and-clean.sh lines 957-968\n\t *\n\t * @param branchName - Name of the branch to merge\n\t * @param mainWorktreePath - Path where main branch is checked out\n\t * @throws Error if fast-forward is not possible\n\t */\n\tasync validateFastForwardPossible(mainBranch: string, branchName: string, mainWorktreePath: string): Promise<void> {\n\n\t\t// Step 1: Get merge-base between main and branch\n\t\tconst mergeBase = await executeGitCommand(['merge-base', mainBranch, branchName], {\n\t\t\tcwd: mainWorktreePath,\n\t\t})\n\n\t\t// Step 2: Get current HEAD of main\n\t\tconst mainHead = await executeGitCommand(['rev-parse', mainBranch], {\n\t\t\tcwd: mainWorktreePath,\n\t\t})\n\n\t\t// Step 3: Compare - they must match for fast-forward\n\t\tconst mergeBaseTrimmed = mergeBase.trim()\n\t\tconst mainHeadTrimmed = mainHead.trim()\n\n\t\tif (mergeBaseTrimmed !== mainHeadTrimmed) {\n\t\t\tthrow new Error(\n\t\t\t\t'Cannot perform fast-forward merge.\\n' +\n\t\t\t\t\t`The ${mainBranch} branch has moved forward since this branch was created.\\n` +\n\t\t\t\t\t`Merge base: ${mergeBaseTrimmed}\\n` +\n\t\t\t\t\t`Main HEAD: ${mainHeadTrimmed}\\n\\n` +\n\t\t\t\t\t'To fix this:\\n' +\n\t\t\t\t\t` 1. Rebase the branch on ${mainBranch}: git rebase ${mainBranch}\\n` +\n\t\t\t\t\t` 2. Or use: il finish to automatically rebase and merge\\n`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Perform fast-forward only merge\n\t * Ports bash/merge-and-clean.sh lines 938-994\n\t *\n\t * @param branchName - Name of the branch to merge\n\t * @param worktreePath - Path to the worktree\n\t * @param options - Merge options (dryRun, force)\n\t * @throws Error if checkout, validation, or merge fails\n\t */\n\tasync performFastForwardMerge(\n\t\tbranchName: string,\n\t\tworktreePath: string,\n\t\toptions: MergeOptions = {}\n\t): Promise<void> {\n\t\tconst { dryRun = false, force = false } = options\n\n\t\tgetLogger().info('Starting fast-forward merge...')\n\n\t\t// Step 1: Get the merge target branch FIRST\n\t\t// For child looms, this will be the parent branch from metadata\n\t\t// For regular looms, this falls back to settings.mainBranch or 'main'\n\t\tconst mainBranch = await this.getMainBranch(worktreePath)\n\n\t\t// Step 2: Find where the merge target branch is checked out\n\t\t// CRITICAL: We must find the worktree for the MERGE TARGET, not settings.mainBranch\n\t\t// This fixes the child loom bug where we'd find the 'main' worktree instead of the parent branch worktree\n\t\tlet mainWorktreePath: string\n\t\tif (options.repoRoot) {\n\t\t\tmainWorktreePath = options.repoRoot\n\t\t} else {\n\t\t\ttry {\n\t\t\t\t// First try to find worktree with the exact merge target branch checked out\n\t\t\t\tmainWorktreePath = await findWorktreeForBranch(mainBranch, worktreePath)\n\t\t\t} catch {\n\t\t\t\t// Fallback: if no worktree has the branch checked out, use settings-based lookup\n\t\t\t\t// This handles edge cases like bare repos or detached HEAD states\n\t\t\t\tgetLogger().debug(`No worktree found for branch '${mainBranch}', falling back to settings-based lookup`)\n\t\t\t\tmainWorktreePath = await findMainWorktreePathWithSettings(worktreePath, this.settingsManager)\n\t\t\t}\n\t\t}\n\n\t\t// Step 3: No need to checkout - the merge target branch is already checked out in mainWorktreePath\n\t\tgetLogger().debug(`Using ${mainBranch} branch location: ${mainWorktreePath}`)\n\n\t\t// Step 4: Verify we're on the correct branch\n\t\tconst currentBranch = await executeGitCommand(['branch', '--show-current'], {\n\t\t\tcwd: mainWorktreePath,\n\t\t})\n\n\t\tif (currentBranch.trim() !== mainBranch) {\n\t\t\tthrow new Error(\n\t\t\t\t`Expected ${mainBranch} branch but found: ${currentBranch.trim()}\\n` +\n\t\t\t\t\t`At location: ${mainWorktreePath}\\n` +\n\t\t\t\t\t'This indicates the main worktree detection failed.'\n\t\t\t)\n\t\t}\n\n\t\t// Step 5: Validate fast-forward is possible\n\t\tawait this.validateFastForwardPossible(mainBranch, branchName, mainWorktreePath)\n\n\t\t// Step 6: Show commits to be merged\n\t\tconst commitsOutput = await executeGitCommand(['log', '--oneline', `${mainBranch}..${branchName}`], {\n\t\t\tcwd: mainWorktreePath,\n\t\t})\n\n\t\tconst commits = commitsOutput.trim()\n\n\t\t// If no commits, branch has no changes ahead of main\n\t\tif (!commits) {\n\t\t\tgetLogger().success(`Branch has no commits ahead of ${mainBranch}. No merge needed.`)\n\t\t\treturn\n\t\t}\n\n\t\t// Show commits that will be merged\n\t\tconst commitLines = commits.split('\\n')\n\t\tgetLogger().info(`Found ${commitLines.length} commit(s) to merge:`)\n\t\tcommitLines.forEach((commit) => getLogger().info(` ${commit}`))\n\n\t\t// Step 7: User confirmation (unless force mode or dry-run)\n\t\tif (!force && !dryRun) {\n\t\t\t// TODO: Implement interactive prompt for confirmation\n\t\t\t// For now, proceeding automatically (use --force to skip this message)\n\t\t\tgetLogger().info('Proceeding with fast-forward merge... (use --force to skip confirmations)')\n\t\t}\n\n\t\t// Step 8: Execute merge (unless dry-run)\n\t\tif (dryRun) {\n\t\t\tgetLogger().info(`[DRY RUN] Would execute: git merge --ff-only ${branchName}`)\n\t\t\tgetLogger().info(`[DRY RUN] This would merge ${commitLines.length} commit(s)`)\n\t\t\treturn\n\t\t}\n\n\t\t// Execute fast-forward merge\n\t\ttry {\n\t\t\tgetLogger().debug(`Executing fast-forward merge of ${branchName} into ${mainBranch} using cwd: ${mainWorktreePath}...`)\n\t\t\tawait executeGitCommand(['merge', '--ff-only', branchName], { cwd: mainWorktreePath })\n\t\t\tgetLogger().success(`Fast-forward merge completed! Merged ${commitLines.length} commit(s).`)\n\t\t} catch (error) {\n\t\t\tthrow new Error(\n\t\t\t\t`Fast-forward merge failed: ${error instanceof Error ? error.message : String(error)}\\n\\n` +\n\t\t\t\t\t'To recover:\\n' +\n\t\t\t\t\t' 1. Check merge status: git status\\n' +\n\t\t\t\t\t' 2. Abort merge if needed: git merge --abort\\n' +\n\t\t\t\t\t' 3. Verify branch is rebased: git rebase main\\n' +\n\t\t\t\t\t' 4. Try merge again: il finish'\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Helper: Detect conflicted files after failed rebase\n\t * @private\n\t */\n\tprivate async detectConflictedFiles(worktreePath: string): Promise<string[]> {\n\t\ttry {\n\t\t\tconst output = await executeGitCommand(['diff', '--name-only', '--diff-filter=U'], {\n\t\t\t\tcwd: worktreePath,\n\t\t\t})\n\n\t\t\treturn output\n\t\t\t\t.trim()\n\t\t\t\t.split('\\n')\n\t\t\t\t.filter((file) => file.length > 0)\n\t\t} catch {\n\t\t\t// If command fails, return empty array (might not be a conflict)\n\t\t\treturn []\n\t\t}\n\t}\n\n\t/**\n\t * Create a temporary WIP commit to preserve uncommitted changes during rebase\n\t * Stages all changes (tracked, untracked) using git add -A\n\t * Uses --no-verify to skip pre-commit hooks since this is a temporary internal commit\n\t * @param worktreePath - Path to the worktree\n\t * @returns The commit hash of the WIP commit\n\t * @private\n\t */\n\tprivate async createWipCommit(worktreePath: string): Promise<string> {\n\t\t// Stage all changes including untracked files\n\t\tawait executeGitCommand(['add', '-A'], { cwd: worktreePath })\n\n\t\t// Create WIP commit with distinctive message\n\t\t// Use --no-verify to skip pre-commit hooks - this is a temporary internal commit\n\t\tawait executeGitCommand(['commit', '--no-verify', '-m', 'WIP: Auto-stash for rebase'], { cwd: worktreePath })\n\n\t\t// Get and return the commit hash\n\t\tconst hash = await executeGitCommand(['rev-parse', 'HEAD'], { cwd: worktreePath })\n\t\treturn hash.trim()\n\t}\n\n\t/**\n\t * Restore uncommitted changes from WIP commit via soft reset\n\t * Logs warning but does not fail if soft reset fails (changes are safe in commit history)\n\t * @param worktreePath - Path to the worktree\n\t * @param wipCommitHash - Original WIP commit hash for verification logging\n\t * @private\n\t */\n\tprivate async restoreWipCommit(worktreePath: string, wipCommitHash: string): Promise<void> {\n\t\tgetLogger().info('Restoring uncommitted changes from WIP commit...')\n\n\t\ttry {\n\t\t\t// Soft reset to parent - changes become staged\n\t\t\tawait executeGitCommand(['reset', '--soft', 'HEAD~1'], { cwd: worktreePath })\n\n\t\t\t// Unstage files to restore to original working directory state\n\t\t\tawait executeGitCommand(['reset', 'HEAD'], { cwd: worktreePath })\n\n\t\t\tgetLogger().success('Restored uncommitted changes from WIP commit')\n\t\t} catch (error) {\n\t\t\t// Log warning but consider rebase successful - work is not lost\n\t\t\tgetLogger().warn(\n\t\t\t\t`Failed to restore WIP commit (${wipCommitHash}). ` +\n\t\t\t\t\t`Your changes are safe in the commit history. ` +\n\t\t\t\t\t`Manual recovery: git reset --soft HEAD~1`,\n\t\t\t\t{ error: error instanceof Error ? error.message : String(error) }\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Helper: Format conflict error message with manual resolution steps\n\t * @private\n\t */\n\tprivate formatConflictError(conflictedFiles: string[]): string {\n\t\tconst fileList = conflictedFiles.map((file) => ` • ${file}`).join('\\n')\n\n\t\treturn (\n\t\t\t'Rebase failed - merge conflicts detected in:\\n' +\n\t\t\tfileList +\n\t\t\t'\\n\\n' +\n\t\t\t'To resolve manually:\\n' +\n\t\t\t' 1. Fix conflicts in the files above\\n' +\n\t\t\t' 2. Stage resolved files: git add <files>\\n' +\n\t\t\t' 3. Continue rebase: git rebase --continue\\n' +\n\t\t\t' 4. Or abort rebase: git rebase --abort\\n' +\n\t\t\t' 5. Then re-run: il finish <issue-number>'\n\t\t)\n\t}\n\n\t/**\n\t * Attempt to resolve conflicts using Claude\n\t * Ports bash/merge-and-clean.sh lines 839-894\n\t *\n\t * @param worktreePath - Path to the worktree\n\t * @param conflictedFiles - List of files with conflicts\n\t * @returns true if conflicts resolved, false otherwise\n\t * @private\n\t */\n\tprivate async attemptClaudeConflictResolution(\n\t\tworktreePath: string,\n\t\tconflictedFiles: string[],\n\t\toptions: { jsonStream?: boolean } = {}\n\t): Promise<boolean> {\n\t\t// Check if Claude CLI is available\n\t\tconst isClaudeAvailable = await detectClaudeCli()\n\t\tif (!isClaudeAvailable) {\n\t\t\tgetLogger().debug('Claude CLI not available, skipping conflict resolution')\n\t\t\treturn false\n\t\t}\n\n\t\tgetLogger().info(`Launching Claude to resolve conflicts in ${conflictedFiles.length} file(s)...`)\n\n\t\t// Hard-coded prompt matching bash script line 844\n\t\t// No templates, no complexity - just the essential instruction\n\t\tconst systemPrompt =\n\t\t\t`Please help resolve the git rebase conflicts in this repository. ` +\n\t\t\t`Analyze the conflicted files, understand the changes from both branches, ` +\n\t\t\t`fix the conflicts, then run 'git add .' to stage the resolved files, ` +\n\t\t\t`and finally run 'git rebase --continue' to continue the rebase process. ` +\n\t\t\t`Once the issue is resolved, tell the user they can use /exit to continue with the process.`\n\n\t\tconst prompt =\n\t\t\t`Help me with this rebase please.`\n\n\t\t// Git commands to auto-approve during rebase conflict resolution\n\t\t// These are the essential commands Claude needs to analyze and resolve conflicts\n\t\t// Note: git reset and git checkout are intentionally excluded as they can be destructive\n\t\tconst rebaseAllowedTools = [\n\t\t\t'Bash(git status:*)',\n\t\t\t'Bash(git diff:*)',\n\t\t\t'Bash(git log:*)',\n\t\t\t'Bash(git add:*)',\n\t\t\t'Bash(git rebase:*)',\n\t\t\t'Bash(GIT_EDITOR=true git rebase:*)',\n\t\t]\n\n\t\ttry {\n\t\t\t// Launch Claude interactively in current terminal\n\t\t\t// User will interact directly with Claude to resolve conflicts\n\t\t\t// When jsonStream is true, run headless with stdout passthrough for JSONL streaming\n\t\t\tawait launchClaude(prompt, {\n\t\t\t\tappendSystemPrompt: systemPrompt,\n\t\t\t\taddDir: worktreePath,\n\t\t\t\theadless: options.jsonStream ? true : false,\n\t\t\t\t...(options.jsonStream && {\n\t\t\t\t\tpermissionMode: 'bypassPermissions' as const,\n\t\t\t\t\tpassthroughStdout: true,\n\t\t\t\t}),\n\t\t\t\tallowedTools: rebaseAllowedTools,\n\t\t\t\tnoSessionPersistence: true, // Utility operation - no session persistence needed\n\t\t\t})\n\n\t\t\t// After Claude interaction completes, check if conflicts resolved\n\t\t\tconst remainingConflicts = await this.detectConflictedFiles(worktreePath)\n\n\t\t\tif (remainingConflicts.length > 0) {\n\t\t\t\tgetLogger().warn(\n\t\t\t\t\t`Conflicts still exist in ${remainingConflicts.length} file(s) after Claude assistance`\n\t\t\t\t)\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// Check if rebase completed or still in progress\n\t\t\tconst rebaseInProgress = await this.isRebaseInProgress(worktreePath)\n\n\t\t\tif (rebaseInProgress) {\n\t\t\t\tgetLogger().warn('Rebase still in progress after Claude assistance')\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\treturn true\n\t\t} catch (error) {\n\t\t\tgetLogger().warn('Claude conflict resolution failed', {\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t})\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Check if a git rebase is currently in progress\n\t * Checks for .git/rebase-merge or .git/rebase-apply directories\n\t * Ports bash script logic from lines 853-856\n\t *\n\t * @param worktreePath - Path to the worktree\n\t * @returns true if rebase in progress, false otherwise\n\t * @private\n\t */\n\tprivate async isRebaseInProgress(worktreePath: string): Promise<boolean> {\n\t\tconst fs = await import('node:fs/promises')\n\t\tconst path = await import('node:path')\n\n\t\t// In git worktrees, .git is a file pointing to the actual git dir.\n\t\t// Use git rev-parse to resolve the real git directory.\n\t\tconst gitDir = (await executeGitCommand(\n\t\t\t['rev-parse', '--absolute-git-dir'],\n\t\t\t{ cwd: worktreePath }\n\t\t)).trim()\n\n\t\tconst rebaseMergePath = path.join(gitDir, 'rebase-merge')\n\t\tconst rebaseApplyPath = path.join(gitDir, 'rebase-apply')\n\n\t\t// Check for rebase-merge directory\n\t\ttry {\n\t\t\tawait fs.access(rebaseMergePath)\n\t\t\treturn true\n\t\t} catch {\n\t\t\t// Directory doesn't exist, continue checking\n\t\t}\n\n\t\t// Check for rebase-apply directory\n\t\ttry {\n\t\t\tawait fs.access(rebaseApplyPath)\n\t\t\treturn true\n\t\t} catch {\n\t\t\t// Directory doesn't exist\n\t\t}\n\n\t\treturn false\n\t}\n\n\t/**\n\t * Abort an in-progress rebase if one is detected\n\t * This handles cases where a previous rebase was interrupted (e.g., terminal closed,\n\t * Claude session ended, user manually stopped) and the worktree is left in a dirty state.\n\t * Since we're about to start a new rebase, the stale rebase state is irrelevant and safe to abort.\n\t *\n\t * @param worktreePath - Path to the worktree\n\t * @private\n\t */\n\tprivate async abortInProgressRebase(worktreePath: string): Promise<void> {\n\t\tconst rebaseInProgress = await this.isRebaseInProgress(worktreePath)\n\n\t\tif (!rebaseInProgress) {\n\t\t\treturn\n\t\t}\n\n\t\tgetLogger().warn('A rebase is already in progress. Aborting the stale rebase before proceeding...')\n\n\t\ttry {\n\t\t\tawait executeGitCommand(['rebase', '--abort'], { cwd: worktreePath })\n\t\t\tgetLogger().info('Stale rebase aborted successfully.')\n\t\t} catch (error) {\n\t\t\t// Handle race condition: rebase may have been resolved between check and abort\n\t\t\tconst errorMsg = error instanceof Error ? error.message : String(error)\n\t\t\tif (errorMsg.includes('No rebase in progress')) {\n\t\t\t\tgetLogger().info('Rebase was already resolved by another process.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (error instanceof GitCommandError) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Failed to abort in-progress rebase: ${error.message}\\n` +\n\t\t\t\t\t\t'Manual recovery: run \"git rebase --abort\" in the worktree directory.'\n\t\t\t\t)\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t}\n}\n","import { getLogger } from '../utils/logger-context.js'\nimport { detectPackageManager, runScript } from '../utils/package-manager.js'\nimport { getPackageConfig, hasScript } from '../utils/package-json.js'\nimport { ProjectCapabilityDetector } from './ProjectCapabilityDetector.js'\n\nexport interface BuildOptions {\n\tdryRun?: boolean\n}\n\nexport interface BuildResult {\n\tsuccess: boolean\n\tskipped: boolean\n\treason?: string\n\tduration: number\n}\n\n/**\n * BuildRunner handles post-merge build verification for CLI projects\n * Only runs build when project has CLI capabilities (bin field in package.json)\n */\nexport class BuildRunner {\n\tprivate capabilityDetector: ProjectCapabilityDetector\n\n\tconstructor(capabilityDetector?: ProjectCapabilityDetector) {\n\t\tthis.capabilityDetector = capabilityDetector ?? new ProjectCapabilityDetector()\n\t}\n\n\t/**\n\t * Run build verification in the specified directory\n\t * @param buildPath - Path where build should run (typically main worktree path)\n\t * @param options - Build options\n\t */\n\tasync runBuild(buildPath: string, options: BuildOptions = {}): Promise<BuildResult> {\n\t\tconst startTime = Date.now()\n\n\t\ttry {\n\t\t\t// Step 1: Check if build script exists (checks .iloom/package.iloom.json first, then package.json)\n\t\t\tconst pkgJson = await getPackageConfig(buildPath)\n\t\t\tconst hasBuildScript = hasScript(pkgJson, 'build')\n\n\t\t\tif (!hasBuildScript) {\n\t\t\tgetLogger().debug('Skipping build - no build script found')\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tskipped: true,\n\t\t\t\t\treason: 'No build script found in package configuration',\n\t\t\t\t\tduration: Date.now() - startTime,\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Handle missing package.json - skip build for non-Node.js projects without package.iloom.json\n\t\t\tif (error instanceof Error && error.message.includes('package.json not found')) {\n\t\t\tgetLogger().debug('Skipping build - no package configuration found')\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tskipped: true,\n\t\t\t\t\treason: 'No package configuration found in project',\n\t\t\t\t\tduration: Date.now() - startTime,\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Re-throw other errors\n\t\t\tthrow error\n\t\t}\n\n\t\t// Step 2: Check if project has CLI capability (bin field)\n\t\tconst capabilities = await this.capabilityDetector.detectCapabilities(buildPath)\n\t\tconst isCLIProject = capabilities.capabilities.includes('cli')\n\n\t\tif (!isCLIProject) {\n\t\tgetLogger().debug('Skipping build - not a CLI project (no bin field)')\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\tskipped: true,\n\t\t\t\treason: 'Project is not a CLI project (no bin field in package.json)',\n\t\t\t\tduration: Date.now() - startTime,\n\t\t\t}\n\t\t}\n\n\t\t// Step 3: Detect package manager\n\t\tconst packageManager = await detectPackageManager(buildPath)\n\n\t\t// Step 4: Handle dry-run mode\n\t\tif (options.dryRun) {\n\t\t\tconst command =\n\t\t\t\tpackageManager === 'npm' ? 'npm run build' : `${packageManager} build`\n\t\tgetLogger().info(`[DRY RUN] Would run: ${command}`)\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\tskipped: false,\n\t\t\t\tduration: Date.now() - startTime,\n\t\t\t}\n\t\t}\n\n\t\t// Step 5: Execute build\n\tgetLogger().info('Running build...')\n\n\t\ttry {\n\t\t\tawait runScript('build', buildPath, [], { quiet: true })\n\t\tgetLogger().success('Build completed successfully')\n\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\tskipped: false,\n\t\t\t\tduration: Date.now() - startTime,\n\t\t\t}\n\t\t} catch {\n\t\t\t// Step 6: Throw detailed error on failure\n\t\t\tconst runCommand =\n\t\t\t\tpackageManager === 'npm' ? 'npm run build' : `${packageManager} build`\n\n\t\t\tthrow new Error(\n\t\t\t\t`Error: Build failed.\\n` +\n\t\t\t\t\t`Fix build errors before proceeding.\\n\\n` +\n\t\t\t\t\t`Run '${runCommand}' to see detailed errors.`\n\t\t\t)\n\t\t}\n\t}\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/SessionSummaryService.ts","../src/utils/claude-transcript.ts"],"sourcesContent":["/**\n * SessionSummaryService: Generates and posts Claude session summaries\n *\n * This service orchestrates:\n * 1. Reading session metadata to get session ID\n * 2. Loading and processing the session-summary prompt template\n * 3. Invoking Claude headless to generate the summary\n * 4. Posting the summary as a comment to the issue/PR\n */\n\nimport path from 'path'\nimport os from 'os'\nimport fs from 'fs-extra'\nimport { logger } from '../utils/logger.js'\nimport { launchClaude, generateDeterministicSessionId } from '../utils/claude.js'\nimport { readSessionContext } from '../utils/claude-transcript.js'\nimport { PromptTemplateManager } from './PromptTemplateManager.js'\nimport { MetadataManager } from './MetadataManager.js'\nimport { SettingsManager, type IloomSettings } from './SettingsManager.js'\nimport { IssueManagementProviderFactory } from '../mcp/IssueManagementProviderFactory.js'\nimport type { IssueProvider } from '../mcp/types.js'\nimport { hasMultipleRemotes } from '../utils/remote.js'\nimport type { RecapFile, RecapOutput } from '../mcp/recap-types.js'\nimport { formatRecapMarkdown } from '../utils/recap-formatter.js'\n\nconst RECAPS_DIR = path.join(os.homedir(), '.config', 'iloom-ai', 'recaps')\n\n/**\n * Slugify path to recap filename (matches MetadataManager/RecapCommand algorithm)\n *\n * Algorithm:\n * 1. Trim trailing slashes\n * 2. Replace all path separators (/ or \\) with ___ (triple underscore)\n * 3. Replace any other non-alphanumeric characters (except _ and -) with -\n * 4. Append .json\n */\nfunction slugifyPath(loomPath: string): string {\n\tlet slug = loomPath.replace(/[/\\\\]+$/, '')\n\tslug = slug.replace(/[/\\\\]/g, '___')\n\tslug = slug.replace(/[^a-zA-Z0-9_-]/g, '-')\n\treturn `${slug}.json`\n}\n\n/**\n * Read recap file for a worktree path with graceful degradation\n * Returns formatted recap string or null if not found/error\n */\nasync function readRecapFile(worktreePath: string): Promise<string | null> {\n\ttry {\n\t\tconst filePath = path.join(RECAPS_DIR, slugifyPath(worktreePath))\n\t\tif (await fs.pathExists(filePath)) {\n\t\t\tconst content = await fs.readFile(filePath, 'utf8')\n\t\t\tconst recap = JSON.parse(content) as RecapFile\n\n\t\t\t// Check if recap has any meaningful content\n\t\t\tconst hasGoal = recap.goal !== null && recap.goal !== undefined\n\t\t\tconst hasComplexity = recap.complexity !== null && recap.complexity !== undefined\n\t\t\tconst hasEntries = Array.isArray(recap.entries) && recap.entries.length > 0\n\t\t\tconst hasArtifacts = Array.isArray(recap.artifacts) && recap.artifacts.length > 0\n\t\t\tconst hasContent = hasGoal || hasComplexity || hasEntries || hasArtifacts\n\n\t\t\tif (hasContent) {\n\t\t\t\t// Convert RecapFile (optional fields) to RecapOutput (required fields)\n\t\t\t\t// Same pattern as RecapCommand.ts:61-66\n\t\t\t\tconst recapOutput: RecapOutput = {\n\t\t\t\t\tfilePath,\n\t\t\t\t\tgoal: recap.goal ?? null,\n\t\t\t\t\tcomplexity: recap.complexity ?? null,\n\t\t\t\t\tentries: recap.entries ?? [],\n\t\t\t\t\tartifacts: recap.artifacts ?? [],\n\t\t\t\t}\n\t\t\t\treturn formatRecapMarkdown(recapOutput)\n\t\t\t}\n\t\t}\n\t\treturn null\n\t} catch {\n\t\t// Graceful degradation - return null on any error\n\t\treturn null\n\t}\n}\n\n/**\n * Input for generating and posting a session summary\n */\nexport interface SessionSummaryInput {\n\tworktreePath: string\n\tissueNumber: string | number\n\tbranchName: string\n\tloomType: 'issue' | 'pr' | 'branch' | 'epic'\n\t/** Optional PR number - when provided, summary is posted to the PR instead of the issue */\n\tprNumber?: number\n}\n\n/**\n * Result from generating a session summary\n */\nexport interface SessionSummaryResult {\n\tsummary: string\n\tsessionId: string\n}\n\n/**\n * Service that generates and posts Claude session summaries to issues\n */\nexport class SessionSummaryService {\n\tprivate templateManager: PromptTemplateManager\n\tprivate metadataManager: MetadataManager\n\tprivate settingsManager: SettingsManager\n\n\tconstructor(\n\t\ttemplateManager?: PromptTemplateManager,\n\t\tmetadataManager?: MetadataManager,\n\t\tsettingsManager?: SettingsManager\n\t) {\n\t\tthis.templateManager = templateManager ?? new PromptTemplateManager()\n\t\tthis.metadataManager = metadataManager ?? new MetadataManager()\n\t\tthis.settingsManager = settingsManager ?? new SettingsManager()\n\t}\n\n\t/**\n\t * Generate and post a session summary to the issue\n\t *\n\t * Non-blocking: Catches all errors and logs warnings instead of throwing\n\t * This ensures the finish workflow continues even if summary generation fails\n\t */\n\tasync generateAndPostSummary(input: SessionSummaryInput): Promise<void> {\n\t\ttry {\n\t\t\t// 1. Skip for branch type (no issue to comment on)\n\t\t\tif (input.loomType === 'branch') {\n\t\t\t\tlogger.debug('Skipping session summary: branch type has no associated issue')\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// 2. Read metadata to get sessionId, or generate deterministically\n\t\t\tconst metadata = await this.metadataManager.readMetadata(input.worktreePath)\n\t\t\tconst sessionId = metadata?.sessionId ?? generateDeterministicSessionId(input.worktreePath)\n\n\t\t\t// 3. Load settings to check generateSummary config\n\t\t\tconst settings = await this.settingsManager.loadSettings(input.worktreePath)\n\t\t\tif (!this.shouldGenerateSummary(input.loomType, settings)) {\n\t\t\t\tlogger.debug(`Skipping session summary: generateSummary is disabled for ${input.loomType} workflow`)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tlogger.info('Generating session summary...')\n\n\t\t\t// 4. Try to read compact summaries from session transcript for additional context\n\t\t\tlogger.debug(`Looking for session transcript with sessionId: ${sessionId}`)\n\t\t\tconst compactSummaries = await readSessionContext(input.worktreePath, sessionId)\n\t\t\tif (compactSummaries) {\n\t\t\t\tlogger.debug(`Found compact summaries (${compactSummaries.length} chars)`)\n\t\t\t} else {\n\t\t\t\tlogger.debug('No compact summaries found in session transcript')\n\t\t\t}\n\n\t\t\t// 5. Try to read recap data for high-signal context\n\t\t\tconst recapData = await readRecapFile(input.worktreePath)\n\t\t\tif (recapData) {\n\t\t\t\tlogger.debug(`Found recap data (${recapData.length} chars)`)\n\t\t\t} else {\n\t\t\t\tlogger.debug('No recap data found')\n\t\t\t}\n\n\t\t\t// 6. Load and process the session-summary template\n\t\t\tconst prompt = await this.templateManager.getPrompt('session-summary', {\n\t\t\t\tISSUE_NUMBER: String(input.issueNumber),\n\t\t\t\tBRANCH_NAME: input.branchName,\n\t\t\t\tLOOM_TYPE: input.loomType,\n\t\t\t\tCOMPACT_SUMMARIES: compactSummaries ?? '',\n\t\t\t\tRECAP_DATA: recapData ?? '',\n\t\t\t})\n\n\t\t\tlogger.debug('Session summary prompt:\\n' + prompt)\n\n\t\t\t// 7. Invoke Claude headless to generate summary\n\t\t\t// Use --resume with session ID so Claude knows which conversation to summarize\n\t\t\tconst summaryModel = this.settingsManager.getSummaryModel(settings)\n\t\t\tconst summaryResult = await launchClaude(prompt, {\n\t\t\t\theadless: true,\n\t\t\t\tmodel: summaryModel,\n\t\t\t\tsessionId: sessionId, // Resume this session so Claude has conversation context\n\t\t\t\tnoSessionPersistence: true, // Don't persist new data after generating summary\n\t\t\t})\n\n\t\t\tif (!summaryResult || typeof summaryResult !== 'string' || summaryResult.trim() === '') {\n\t\t\t\tlogger.warn('Session summary generation returned empty result')\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst summary = summaryResult.trim()\n\n\t\t\t// 8. Skip posting if summary is too short (likely failed generation)\n\t\t\tif (summary.length < 100) {\n\t\t\t\tlogger.warn('Session summary too short, skipping post')\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// 9. Post summary to issue or PR (PR takes priority when prNumber is provided)\n\t\t\tawait this.postSummaryToIssue(input.issueNumber, summary, settings, input.worktreePath, input.prNumber)\n\n\t\t\tconst targetDescription = input.prNumber ? `PR #${input.prNumber}` : 'issue'\n\t\t\tlogger.success(`Session summary posted to ${targetDescription}`)\n\t\t} catch (error) {\n\t\t\t// Non-blocking: Log warning but don't throw\n\t\t\tconst errorMessage = error instanceof Error ? error.message : String(error)\n\t\t\tlogger.warn(`Failed to generate session summary: ${errorMessage}`)\n\t\t\tlogger.debug('Session summary generation error details:', { error })\n\t\t}\n\t}\n\n\t/**\n\t * Generate a session summary without posting it\n\t *\n\t * This method is useful for previewing the summary or for use by CLI commands\n\t * that want to display the summary before optionally posting it.\n\t *\n\t * @param worktreePath - Path to the worktree\n\t * @param branchName - Name of the branch\n\t * @param loomType - Type of loom ('issue' | 'pr' | 'branch')\n\t * @param issueNumber - Issue or PR number (optional, for template variables)\n\t * @returns The generated summary and session ID\n\t * @throws Error if Claude invocation fails\n\t */\n\tasync generateSummary(\n\t\tworktreePath: string,\n\t\tbranchName: string,\n\t\tloomType: 'issue' | 'pr' | 'branch' | 'epic',\n\t\tissueNumber?: string | number\n\t): Promise<SessionSummaryResult> {\n\t\t// 1. Read metadata or generate deterministic session ID\n\t\tconst metadata = await this.metadataManager.readMetadata(worktreePath)\n\t\tconst sessionId = metadata?.sessionId ?? generateDeterministicSessionId(worktreePath)\n\n\t\t// 2. Load settings for model configuration\n\t\tconst settings = await this.settingsManager.loadSettings(worktreePath)\n\n\t\tlogger.info('Generating session summary...')\n\n\t\t// 3. Try to read compact summaries from session transcript for additional context\n\t\tlogger.debug(`Looking for session transcript with sessionId: ${sessionId}`)\n\t\tconst compactSummaries = await readSessionContext(worktreePath, sessionId)\n\t\tif (compactSummaries) {\n\t\t\tlogger.debug(`Found compact summaries (${compactSummaries.length} chars)`)\n\t\t} else {\n\t\t\tlogger.debug('No compact summaries found in session transcript')\n\t\t}\n\n\t\t// 4. Try to read recap data for high-signal context\n\t\tconst recapData = await readRecapFile(worktreePath)\n\t\tif (recapData) {\n\t\t\tlogger.debug(`Found recap data (${recapData.length} chars)`)\n\t\t} else {\n\t\t\tlogger.debug('No recap data found')\n\t\t}\n\n\t\t// 5. Load and process the session-summary template\n\t\tconst prompt = await this.templateManager.getPrompt('session-summary', {\n\t\t\tISSUE_NUMBER: issueNumber !== undefined ? String(issueNumber) : '',\n\t\t\tBRANCH_NAME: branchName,\n\t\t\tLOOM_TYPE: loomType,\n\t\t\tCOMPACT_SUMMARIES: compactSummaries ?? '',\n\t\t\tRECAP_DATA: recapData ?? '',\n\t\t})\n\n\t\tlogger.debug('Session summary prompt:\\n' + prompt)\n\n\t\t// 6. Invoke Claude headless to generate summary\n\t\tconst summaryModel = this.settingsManager.getSummaryModel(settings)\n\t\tconst summaryResult = await launchClaude(prompt, {\n\t\t\theadless: true,\n\t\t\tmodel: summaryModel,\n\t\t\tsessionId: sessionId,\n\t\t\tnoSessionPersistence: true, // Don't persist new data after generating summary\n\t\t})\n\n\t\tif (!summaryResult || typeof summaryResult !== 'string' || summaryResult.trim() === '') {\n\t\t\tthrow new Error('Session summary generation returned empty result')\n\t\t}\n\n\t\tconst summary = summaryResult.trim()\n\n\t\t// 7. Check if summary is too short (likely failed generation)\n\t\tif (summary.length < 100) {\n\t\t\tthrow new Error('Session summary too short - generation may have failed')\n\t\t}\n\n\t\treturn {\n\t\t\tsummary,\n\t\t\tsessionId: sessionId,\n\t\t}\n\t}\n\n\t/**\n\t * Post a summary to an issue (used by both generateAndPostSummary and CLI commands)\n\t *\n\t * @param issueNumber - Issue or PR number to post to\n\t * @param summary - The summary text to post\n\t * @param worktreePath - Path to worktree for loading settings (optional)\n\t */\n\tasync postSummary(\n\t\tissueNumber: string | number,\n\t\tsummary: string,\n\t\tworktreePath?: string,\n\t\tprNumber?: number\n\t): Promise<void> {\n\t\tconst settings = await this.settingsManager.loadSettings(worktreePath)\n\t\tawait this.postSummaryToIssue(issueNumber, summary, settings, worktreePath ?? process.cwd(), prNumber)\n\t\tconst target = prNumber ? `PR #${prNumber}` : 'issue'\n\t\tlogger.success(`Session summary posted to ${target}`)\n\t}\n\n\t/**\n\t * Determine if summary should be generated based on loom type and settings\n\t *\n\t * @param loomType - The type of loom being finished\n\t * @param settings - The loaded iloom settings\n\t * @returns true if summary should be generated\n\t */\n\tshouldGenerateSummary(\n\t\tloomType: 'issue' | 'pr' | 'branch' | 'epic',\n\t\tsettings: IloomSettings\n\t): boolean {\n\t\t// Branch type never generates summaries (no issue to comment on)\n\t\tif (loomType === 'branch') {\n\t\t\treturn false\n\t\t}\n\n\t\t// Get workflow-specific config\n\t\tconst workflowConfig =\n\t\t\tloomType === 'issue'\n\t\t\t\t? settings.workflows?.issue\n\t\t\t\t: settings.workflows?.pr\n\n\t\t// Default to true if not explicitly set (for issue and pr types)\n\t\treturn workflowConfig?.generateSummary ?? true\n\t}\n\n\t/**\n\t * Apply attribution footer to summary based on settings\n\t *\n\t * @param summary - The summary text\n\t * @param worktreePath - Path to worktree for loading settings and detecting remotes\n\t * @returns Summary with attribution footer if applicable\n\t */\n\tasync applyAttribution(summary: string, worktreePath: string): Promise<string> {\n\t\tconst settings = await this.settingsManager.loadSettings(worktreePath)\n\t\treturn this.applyAttributionWithSettings(summary, settings, worktreePath)\n\t}\n\n\t/**\n\t * Apply attribution footer to summary based on provided settings\n\t *\n\t * @param summary - The summary text\n\t * @param settings - The loaded iloom settings\n\t * @param worktreePath - Path to worktree for detecting remotes\n\t * @returns Summary with attribution footer if applicable\n\t */\n\tasync applyAttributionWithSettings(\n\t\tsummary: string,\n\t\tsettings: IloomSettings,\n\t\tworktreePath: string\n\t): Promise<string> {\n\t\tconst attributionSetting = settings.attribution ?? 'upstreamOnly'\n\t\tlogger.debug(`Attribution setting from config: ${settings.attribution}`)\n\t\tlogger.debug(`Attribution setting (with default): ${attributionSetting}`)\n\n\t\tlet shouldShowAttribution = false\n\t\tif (attributionSetting === 'on') {\n\t\t\tshouldShowAttribution = true\n\t\t\tlogger.debug('Attribution: always on')\n\t\t} else if (attributionSetting === 'upstreamOnly') {\n\t\t\t// Only show attribution when contributing to external repos (multiple remotes)\n\t\t\tshouldShowAttribution = await hasMultipleRemotes(worktreePath)\n\t\t\tlogger.debug(`Attribution: upstreamOnly, hasMultipleRemotes=${shouldShowAttribution}`)\n\t\t} else {\n\t\t\tlogger.debug('Attribution: off')\n\t\t}\n\t\t// 'off' keeps shouldShowAttribution = false\n\n\t\tlogger.debug(`Should show attribution: ${shouldShowAttribution}`)\n\t\tif (shouldShowAttribution) {\n\t\t\tlogger.debug('Attribution footer appended to summary')\n\t\t\treturn `${summary}\\n\\n---\\n*Generated with 🤖❤️ by [iloom.ai](https://iloom.ai)*`\n\t\t}\n\n\t\treturn summary\n\t}\n\n\t/**\n\t * Post the summary as a comment to the issue or PR\n\t *\n\t * @param issueNumber - The issue number (used when prNumber is not provided)\n\t * @param summary - The summary text to post\n\t * @param settings - The loaded iloom settings\n\t * @param worktreePath - Path to worktree for attribution detection\n\t * @param prNumber - Optional PR number - when provided, posts to the PR instead\n\t */\n\tprivate async postSummaryToIssue(\n\t\tissueNumber: string | number,\n\t\tsummary: string,\n\t\tsettings: IloomSettings,\n\t\tworktreePath: string,\n\t\tprNumber?: number\n\t): Promise<void> {\n\t\t// Get the issue management provider from settings\n\t\t// PRs only exist on GitHub, so always use 'github' provider when prNumber is provided\n\t\t// (see types.ts:32-33 and LinearIssueManagementProvider.getPR())\n\t\tconst providerType = prNumber !== undefined\n\t\t\t? 'github'\n\t\t\t: (settings.issueManagement?.provider ?? 'github') as IssueProvider\n\t\tconst provider = IssueManagementProviderFactory.create(providerType, settings)\n\n\t\t// Apply attribution if configured\n\t\tconst finalSummary = await this.applyAttributionWithSettings(summary, settings, worktreePath)\n\n\t\t// When prNumber is provided, post to the PR instead of the issue\n\t\tconst targetNumber = prNumber ?? issueNumber\n\t\tconst targetType = prNumber !== undefined ? 'pr' : 'issue'\n\n\t\t// Create the comment\n\t\tawait provider.createComment({\n\t\t\tnumber: String(targetNumber),\n\t\t\tbody: finalSummary,\n\t\t\ttype: targetType,\n\t\t})\n\t}\n}\n","/**\n * Claude Transcript Utilities\n *\n * Provides functions to read and parse Claude Code session transcript files\n * stored in ~/.claude/projects/. These transcripts contain the full conversation\n * history including compact summaries from when conversations were compacted.\n */\n\nimport { readFile } from 'fs/promises'\nimport { homedir } from 'os'\nimport { join } from 'path'\nimport { logger } from './logger.js'\n\n/**\n * Entry in a Claude Code JSONL transcript file\n */\nexport interface TranscriptEntry {\n\ttype: 'user' | 'assistant' | 'system' | 'file-history-snapshot' | 'queue-operation'\n\tsessionId?: string\n\tmessage?: { role: string; content: string | Array<{ type: string; text?: string }> }\n\tisCompactSummary?: boolean\n\tisVisibleInTranscriptOnly?: boolean\n\tsubtype?: string // 'compact_boundary' for compaction markers\n\tcontent?: string\n\ttimestamp?: string\n\tuuid?: string\n\tparentUuid?: string\n}\n\n/**\n * Get the Claude projects directory path encoding for a worktree path\n * Encoding: /Users/adam/Projects/foo_bar -> -Users-adam-Projects-foo-bar\n *\n * Claude Code encodes paths by replacing both '/' and '_' with '-'\n *\n * @param worktreePath - Absolute path to the worktree\n * @returns Encoded directory name for Claude projects\n */\nexport function getClaudeProjectPath(worktreePath: string): string {\n\t// Replace all '/' and '_' with '-' (matching Claude Code's encoding)\n\treturn worktreePath.replace(/[/_]/g, '-')\n}\n\n/**\n * Get the full path to the Claude projects directory\n * @returns Path to ~/.claude/projects/\n */\nexport function getClaudeProjectsDir(): string {\n\treturn join(homedir(), '.claude', 'projects')\n}\n\n/**\n * Find the session transcript file for a given worktree and session ID\n *\n * @param worktreePath - Absolute path to the worktree\n * @param sessionId - Session ID to find transcript for\n * @returns Full path to the transcript file, or null if not found\n */\nexport function findSessionTranscript(worktreePath: string, sessionId: string): string | null {\n\tconst projectsDir = getClaudeProjectsDir()\n\tconst projectDirName = getClaudeProjectPath(worktreePath)\n\tconst transcriptPath = join(projectsDir, projectDirName, `${sessionId}.jsonl`)\n\treturn transcriptPath\n}\n\n/**\n * Extract the content from a compact summary message\n * Handles both string content and array content formats\n */\nfunction extractMessageContent(message: TranscriptEntry['message']): string | null {\n\tif (!message) return null\n\n\tif (typeof message.content === 'string') {\n\t\treturn message.content\n\t}\n\n\tif (Array.isArray(message.content)) {\n\t\t// Concatenate all text elements\n\t\treturn message.content\n\t\t\t.filter((item) => item.type === 'text' && item.text)\n\t\t\t.map((item) => item.text)\n\t\t\t.join('\\n')\n\t}\n\n\treturn null\n}\n\n/**\n * Extract compact summaries from a session transcript file\n *\n * Returns empty array if file doesn't exist or no summaries found.\n * Each compact summary contains structured history of pre-compaction conversation.\n *\n * @param transcriptPath - Full path to the transcript JSONL file\n * @param maxSummaries - Maximum number of summaries to return (default 3)\n * @returns Array of compact summary content strings, newest first\n */\nexport async function extractCompactSummaries(\n\ttranscriptPath: string,\n\tmaxSummaries = 3\n): Promise<string[]> {\n\ttry {\n\t\tconst content = await readFile(transcriptPath, 'utf-8')\n\t\tconst lines = content.split('\\n').filter((line) => line.trim())\n\n\t\tconst summaries: string[] = []\n\n\t\tfor (const line of lines) {\n\t\t\ttry {\n\t\t\t\tconst entry = JSON.parse(line) as TranscriptEntry\n\n\t\t\t\t// Look for compact summary entries\n\t\t\t\tif (entry.isCompactSummary === true && entry.message) {\n\t\t\t\t\tconst summaryContent = extractMessageContent(entry.message)\n\t\t\t\t\tif (summaryContent) {\n\t\t\t\t\t\tsummaries.push(summaryContent)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Skip malformed JSON lines\n\t\t\t\tlogger.debug('Skipping malformed JSONL line in transcript')\n\t\t\t}\n\t\t}\n\n\t\t// Return most recent summaries (they appear in order in the file)\n\t\t// Limit to maxSummaries\n\t\treturn summaries.slice(-maxSummaries)\n\t} catch (error) {\n\t\t// File not found or permission error - return empty array (graceful degradation)\n\t\tif (error instanceof Error && 'code' in error && error.code === 'ENOENT') {\n\t\t\tlogger.debug('Transcript file not found:', transcriptPath)\n\t\t} else {\n\t\t\tlogger.debug('Error reading transcript file:', error)\n\t\t}\n\t\treturn []\n\t}\n}\n\n/**\n * Read session transcript and extract compact summaries for summary generation\n *\n * This is the main entry point for SessionSummaryService to get pre-compaction\n * conversation context. It gracefully handles all error cases.\n *\n * @param worktreePath - Absolute path to the worktree\n * @param sessionId - Session ID to find transcript for\n * @param maxSummaries - Maximum number of summaries to return (default 3)\n * @returns Formatted string of compact summaries, or null if none found\n */\nexport async function readSessionContext(\n\tworktreePath: string,\n\tsessionId: string,\n\tmaxSummaries = 3\n): Promise<string | null> {\n\tconst transcriptPath = findSessionTranscript(worktreePath, sessionId)\n\tif (!transcriptPath) {\n\t\treturn null\n\t}\n\n\tlogger.debug(`Checking transcript at: ${transcriptPath}`)\n\n\tconst summaries = await extractCompactSummaries(transcriptPath, maxSummaries)\n\n\tif (summaries.length === 0) {\n\t\treturn null\n\t}\n\n\t// Format summaries with separators\n\t// Newest summaries are at the end, so we reverse to show newest first\n\tconst formattedSummaries = summaries\n\t\t.reverse()\n\t\t.map((summary, index) => {\n\t\t\tconst header =\n\t\t\t\tsummaries.length > 1\n\t\t\t\t\t? `### Compact Summary ${index + 1} of ${summaries.length}\\n\\n`\n\t\t\t\t\t: ''\n\t\t\treturn `${header}${summary}`\n\t\t})\n\t\t.join('\\n\\n---\\n\\n')\n\n\treturn formattedSummaries\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/remote.ts"],"sourcesContent":["import { execa } from 'execa'\nimport type { IloomSettings } from '../lib/SettingsManager.js'\nimport logger from './logger.js'\n\n/**\n * Represents a parsed git remote\n */\nexport interface GitRemote {\n\tname: string\n\turl: string\n\towner: string\n\trepo: string\n}\n\n/**\n * Parse git remotes from `git remote -v` output\n * Deduplicates fetch/push entries and extracts owner/repo from URLs\n */\nexport async function parseGitRemotes(cwd?: string): Promise<GitRemote[]> {\n\tconst result = await execa('git', ['remote', '-v'], {\n\t\tcwd: cwd ?? process.cwd(),\n\t\tencoding: 'utf8',\n\t})\n\n\tconst lines = result.stdout.trim().split('\\n')\n\tconst remoteMap = new Map<string, GitRemote>()\n\n\tfor (const line of lines) {\n\t\t// Format: \"origin git@github.com:owner/repo.git (fetch)\"\n\t\t// Format: \"origin https://github.com/owner/repo.git (fetch)\"\n\t\tconst match = line.match(/^(\\S+)\\s+(\\S+)\\s+\\((fetch|push)\\)/)\n\t\tif (!match) continue\n\n\t\tconst name = match[1]\n\t\tconst url = match[2]\n\t\tif (!name || !url) continue\n\n\t\t// Skip if we already processed this remote\n\t\tif (remoteMap.has(name)) continue\n\n\t\t// Extract owner/repo from URL\n\t\tconst ownerRepo = extractOwnerRepoFromUrl(url)\n\t\tif (!ownerRepo) continue\n\n\t\tremoteMap.set(name, {\n\t\t\tname,\n\t\t\turl,\n\t\t\towner: ownerRepo.owner,\n\t\t\trepo: ownerRepo.repo,\n\t\t})\n\t}\n\n\treturn Array.from(remoteMap.values())\n}\n\n/**\n * Extract owner and repo from GitHub URL\n * Supports both HTTPS and SSH formats\n */\nfunction extractOwnerRepoFromUrl(url: string): { owner: string; repo: string } | null {\n\t// Remove .git suffix if present\n\tconst cleanUrl = url.replace(/\\.git$/, '')\n\n\t// HTTPS format: https://github.com/owner/repo\n\tconst httpsMatch = cleanUrl.match(/https?:\\/\\/github\\.com\\/([^/]+)\\/([^/]+)/)\n\tif (httpsMatch?.[1] && httpsMatch?.[2]) {\n\t\treturn { owner: httpsMatch[1], repo: httpsMatch[2] }\n\t}\n\n\t// SSH format: git@github.com:owner/repo\n\tconst sshMatch = cleanUrl.match(/git@github\\.com:([^/]+)\\/(.+)/)\n\tif (sshMatch?.[1] && sshMatch?.[2]) {\n\t\treturn { owner: sshMatch[1], repo: sshMatch[2] }\n\t}\n\n\treturn null\n}\n\n/**\n * Check if repository has multiple remotes\n */\nexport async function hasMultipleRemotes(cwd?: string): Promise<boolean> {\n\ttry {\n\t\tconst remotes = await parseGitRemotes(cwd)\n\t\treturn remotes.length > 1\n\t} catch (error) {\n\t\t// if error is \"not a git repository\" then just log a debug message, otherwise log a warning message\n\t\tconst errMsg = error instanceof Error ? error.message : String(error)\n\t\tif (/not a git repository/i.test(errMsg)) {\n\t\t\tlogger.debug('Skipping git remote check: not a git repository')\n\t\t} else {\n\t\t\tlogger.warn(`Unable to check git remotes: ${errMsg}`)\n\t\t}\n\t\treturn false\n\t}\n}\n\n/**\n * Get configured repository string from settings\n * Returns \"owner/repo\" format for use with gh CLI --repo flag\n * Throws if configured remote not found\n */\nexport async function getConfiguredRepoFromSettings(\n\tsettings: IloomSettings,\n\tcwd?: string,\n): Promise<string> {\n\tconst remoteName = settings.issueManagement?.github?.remote\n\n\tif (!remoteName) {\n\t\tthrow new Error(\n\t\t\t'GitHub remote not configured. Run \"il init\" to configure which repository to use for GitHub operations.',\n\t\t)\n\t}\n\n\t// Validate configured remote exists\n\tawait validateConfiguredRemote(remoteName, cwd)\n\n\t// Parse remotes and find the configured one\n\tconst remotes = await parseGitRemotes(cwd)\n\tconst remote = remotes.find((r) => r.name === remoteName)\n\n\tif (!remote) {\n\t\tthrow new Error(\n\t\t\t`Configured remote \"${remoteName}\" not found in git remotes. Run \"il init\" to reconfigure.`,\n\t\t)\n\t}\n\n\treturn `${remote.owner}/${remote.repo}`\n}\n\n/**\n * Validate that a remote exists in git config\n * Throws if remote doesn't exist\n */\nexport async function validateConfiguredRemote(remoteName: string, cwd?: string): Promise<void> {\n\ttry {\n\t\tawait execa('git', ['remote', 'get-url', remoteName], {\n\t\t\tcwd: cwd ?? process.cwd(),\n\t\t\tencoding: 'utf8',\n\t\t})\n\t} catch {\n\t\tthrow new Error(\n\t\t\t`Remote \"${remoteName}\" does not exist in git configuration. Run \"il init\" to reconfigure.`,\n\t\t)\n\t}\n}\n\n/**\n * Get the effective PR target remote based on settings\n * Priority: mergeBehavior.remote > issueManagement.github.remote > 'origin'\n */\nexport async function getEffectivePRTargetRemote(\n\tsettings: IloomSettings,\n\tcwd?: string,\n): Promise<string> {\n\tconst prRemote =\n\t\tsettings.mergeBehavior?.remote ?? settings.issueManagement?.github?.remote ?? 'origin'\n\tawait validateConfiguredRemote(prRemote, cwd)\n\treturn prRemote\n}\n"],"mappings":"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","names":[]}