@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
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/ResourceCleanup.ts"],"sourcesContent":["import path from 'path'\nimport { GitWorktreeManager } from './GitWorktreeManager.js'\nimport { DatabaseManager } from './DatabaseManager.js'\nimport { ProcessManager } from './process/ProcessManager.js'\nimport { CLIIsolationManager } from './CLIIsolationManager.js'\nimport { SettingsManager } from './SettingsManager.js'\nimport { MetadataManager } from './MetadataManager.js'\nimport { DockerManager } from './DockerManager.js'\nimport { getLogger } from '../utils/logger-context.js'\nimport { hasUncommittedChanges, executeGitCommand, findMainWorktreePathWithSettings, extractIssueNumber, isBranchMergedIntoMain, checkRemoteBranchStatus, getMergeTargetBranch, findWorktreeForBranch, type RemoteBranchStatus } from '../utils/git.js'\nimport { removeClaudeTrust } from '../utils/claude-trust.js'\nimport { getWorkspacePort } from '../utils/port.js'\nimport { archiveRecap } from '../utils/recap-archiver.js'\n\nimport type {\n\tResourceCleanupOptions,\n\tCleanupResult,\n\tOperationResult,\n\tSafetyCheck,\n\tBranchDeleteOptions,\n} from '../types/cleanup.js'\nimport type { GitWorktree } from '../types/worktree.js'\nimport type { ParsedInput } from '../commands/start.js'\n\n/**\n * Manages resource cleanup for worktrees\n * Provides shared cleanup functionality for finish and cleanup commands\n */\nexport class ResourceCleanup {\n\tprivate settingsManager: SettingsManager\n\tprivate metadataManager: MetadataManager\n\n\tconstructor(\n\t\tprivate gitWorktree: GitWorktreeManager,\n\t\tprivate processManager: ProcessManager,\n\t\tprivate database?: DatabaseManager,\n\t\tprivate cliIsolation?: CLIIsolationManager,\n\t\tsettingsManager?: SettingsManager\n\t) {\n\t\tthis.settingsManager = settingsManager ?? new SettingsManager()\n\t\tthis.metadataManager = new MetadataManager()\n\t}\n\n\t/**\n\t * Cleanup a worktree and associated resources\n\t * Main orchestration method\n\t *\n\t * @param parsed - ParsedInput from IdentifierParser with type information\n\t * @param options - Cleanup options\n\t */\n\tasync cleanupWorktree(\n\t\tparsed: ParsedInput,\n\t\toptions: ResourceCleanupOptions = {}\n\t): Promise<CleanupResult> {\n\t\tconst operations: OperationResult[] = []\n\t\tconst errors: Error[] = []\n\n\t\tconst displayIdentifier = parsed.branchName ?? parsed.number?.toString() ?? parsed.originalInput\n\t\tgetLogger().info(`Starting cleanup for: ${displayIdentifier}`)\n\n\t\t// Step 1: Find worktree using specific methods based on type\n\t\tlet worktree: GitWorktree | null = null\n\t\ttry {\n\t\t\t// Use pre-resolved worktree if provided (skips the search step)\n\t\t\tif (options.worktree) {\n\t\t\t\tworktree = {\n\t\t\t\t\tpath: options.worktree.path,\n\t\t\t\t\tbranch: options.worktree.branch,\n\t\t\t\t\tcommit: '',\n\t\t\t\t\tbare: false,\n\t\t\t\t\tdetached: false,\n\t\t\t\t\tlocked: false,\n\t\t\t\t}\n\t\t\t\tgetLogger().debug(`Using pre-resolved worktree: path=\"${worktree.path}\", branch=\"${worktree.branch}\"`)\n\t\t\t} else {\n\t\t\t\t// Use specific finding methods based on parsed type for precision\n\t\t\t\tif (parsed.type === 'pr' && parsed.number !== undefined) {\n\t\t\t\t\t// For PRs, ensure the number is numeric (PRs are always numeric per GitHub)\n\t\t\t\t\tconst prNumber = typeof parsed.number === 'number' ? parsed.number : Number(parsed.number)\n\t\t\t\t\tif (isNaN(prNumber) || !isFinite(prNumber)) {\n\t\t\t\t\t\tthrow new Error(`Invalid PR number: ${parsed.number}. PR numbers must be numeric.`)\n\t\t\t\t\t}\n\t\t\t\t\t// For PRs, pass empty string for branchName since we're detecting from path pattern\n\t\t\t\t\tworktree = await this.gitWorktree.findWorktreeForPR(prNumber, '')\n\t\t\t\t} else if (parsed.type === 'issue' && parsed.number !== undefined) {\n\t\t\t\t\tworktree = await this.gitWorktree.findWorktreeForIssue(parsed.number)\n\t\t\t\t} else if (parsed.type === 'branch' && parsed.branchName) {\n\t\t\t\t\tworktree = await this.gitWorktree.findWorktreeForBranch(parsed.branchName)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!worktree) {\n\t\t\t\tthrow new Error(`No worktree found for identifier: ${displayIdentifier}`)\n\t\t\t}\n\n\t\t\tgetLogger().debug(`Found worktree: path=\"${worktree.path}\", branch=\"${worktree.branch}\"`)\n\t\t} catch (error) {\n\t\t\tconst err = error instanceof Error ? error : new Error('Unknown error')\n\t\t\terrors.push(err)\n\n\t\t\treturn {\n\t\t\t\tidentifier: displayIdentifier,\n\t\t\t\tsuccess: false,\n\t\t\t\toperations,\n\t\t\t\terrors,\n\t\t\t\trollbackRequired: false,\n\t\t\t}\n\t\t}\n\n\t\t// Step 1.5: Terminate dev server if applicable\n\t\t// Done after worktree is found so we can use getWorkspacePort which checks .env PORT overrides\n\t\t{\n\t\t\tconst settings = await this.settingsManager.loadSettings()\n\t\t\tconst port = await getWorkspacePort({\n\t\t\t\tworktreePath: worktree.path,\n\t\t\t\tworktreeBranch: worktree.branch,\n\t\t\t\tbasePort: settings?.capabilities?.web?.basePort,\n\t\t\t\tcheckEnvFile: true,\n\t\t\t})\n\t\t\tconst devServerMode = settings?.capabilities?.web?.devServer ?? 'process'\n\t\t\tconst dockerIdentifier = devServerMode === 'docker'\n\t\t\t\t? (parsed.number?.toString() ?? parsed.branchName ?? parsed.originalInput)\n\t\t\t\t: undefined\n\n\t\t\tif (options.dryRun) {\n\t\t\t\toperations.push({\n\t\t\t\t\ttype: 'dev-server',\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tmessage: `[DRY RUN] Would check for dev server on port ${port}`,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tconst terminated = await this.terminateDevServer(port, dockerIdentifier)\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: 'dev-server',\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\tmessage: terminated\n\t\t\t\t\t\t\t? `Dev server on port ${port} terminated`\n\t\t\t\t\t\t\t: `No dev server running on port ${port}`,\n\t\t\t\t\t})\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst err = error instanceof Error ? error : new Error('Unknown error')\n\t\t\t\t\terrors.push(err)\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: 'dev-server',\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\tmessage: `Failed to terminate dev server`,\n\t\t\t\t\t\terror: err.message,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Step 2.5: Validate safety before proceeding with cleanup (unless force flag is set)\n\t\t// Check merge safety if: deleteBranch is true AND checkMergeSafety is not explicitly false\n\t\t// This prevents the scenario where worktree is deleted but branch deletion fails\n\t\tlet safetyCheckPassed = false\n\t\tif (!options.force) {\n\t\t\tconst shouldCheckMergeSafety = options.checkMergeSafety ?? (options.deleteBranch === true)\n\t\t\tconst shouldCheckRemoteBranch = options.checkRemoteBranch ?? false\n\t\t\tconst safety = await this.validateWorktreeSafety(worktree, parsed.originalInput, shouldCheckMergeSafety, shouldCheckRemoteBranch)\n\n\t\t\tif (!safety.isSafe) {\n\t\t\t\t// Format blocker messages for error output\n\t\t\t\tconst blockerMessage = safety.blockers.join('\\n\\n')\n\t\t\t\tthrow new Error(`Cannot cleanup:\\n\\n${blockerMessage}`)\n\t\t\t}\n\n\t\t\tsafetyCheckPassed = true\n\n\t\t\t// Log warnings if any\n\t\t\tif (safety.warnings.length > 0) {\n\t\t\t\tsafety.warnings.forEach(warning => {\n\t\t\t\t\tgetLogger().warn(warning)\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\t// Step 3: Pre-fetch database configuration before worktree removal\n\t\t// This config is used AFTER worktree deletion when env file no longer exists\n\t\tlet databaseConfig: { shouldCleanup: boolean; envFilePath: string } | null = null\n\t\tif (!options.keepDatabase && worktree) {\n\t\t\tconst envFilePath = path.join(worktree.path, '.env')\n\t\t\ttry {\n\t\t\t\t// Pre-check if database cleanup should happen by reading .env file now\n\t\t\t\tconst shouldCleanup = this.database\n\t\t\t\t\t? await this.database.shouldUseDatabaseBranching(envFilePath)\n\t\t\t\t\t: false\n\t\t\t\tdatabaseConfig = { shouldCleanup, envFilePath }\n\t\t\t} catch (error) {\n\t\t\t\t// If we can't read the config, we'll skip database cleanup\n\t\t\t\tgetLogger().warn(\n\t\t\t\t\t`Failed to read database config from ${envFilePath}, skipping database cleanup: ${\n\t\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t\t}`\n\t\t\t\t)\n\t\t\t\tdatabaseConfig = { shouldCleanup: false, envFilePath }\n\t\t\t}\n\t\t}\n\n\t\t// Step 3.5: Find main worktree path before deletion (needed for branch and database operations)\n\t\tlet mainWorktreePath: string | null = null\n\t\tif (!options.dryRun) {\n\t\t\ttry {\n\t\t\t\tmainWorktreePath = await findMainWorktreePathWithSettings(worktree.path, this.settingsManager)\n\t\t\t} catch (error) {\n\t\t\t\tgetLogger().warn(\n\t\t\t\t\t`Failed to find main worktree path: ${error instanceof Error ? error.message : String(error)}`\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\t// Step 3.6: Pre-fetch merge target branch before worktree deletion\n\t\t// This is needed because deleteBranch() needs to know the merge target (parent branch for child looms)\n\t\t// but the worktree metadata won't be readable after deletion in Step 4\n\t\tlet mergeTargetBranch: string | null = null\n\t\tif (options.deleteBranch && worktree && !options.dryRun) {\n\t\t\ttry {\n\t\t\t\tmergeTargetBranch = await getMergeTargetBranch(worktree.path, {\n\t\t\t\t\tsettingsManager: this.settingsManager,\n\t\t\t\t\tmetadataManager: this.metadataManager,\n\t\t\t\t})\n\t\t\t\tgetLogger().debug(`Pre-fetched merge target branch: ${mergeTargetBranch}`)\n\t\t\t} catch (error) {\n\t\t\t\tgetLogger().warn(\n\t\t\t\t\t`Failed to pre-fetch merge target branch: ${error instanceof Error ? error.message : String(error)}`\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\t// Step 4: Remove worktree\n\t\tif (options.dryRun) {\n\t\t\toperations.push({\n\t\t\t\ttype: 'worktree',\n\t\t\t\tsuccess: true,\n\t\t\t\tmessage: `[DRY RUN] Would remove worktree: ${worktree.path}`,\n\t\t\t})\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tconst worktreeOptions: { force?: boolean; removeDirectory: true; removeBranch: false } =\n\t\t\t\t\t{\n\t\t\t\t\t\tremoveDirectory: true,\n\t\t\t\t\t\tremoveBranch: false, // Handle branch separately\n\t\t\t\t\t}\n\t\t\t\tif (options.force !== undefined) {\n\t\t\t\t\tworktreeOptions.force = options.force\n\t\t\t\t}\n\t\t\t\tawait this.gitWorktree.removeWorktree(worktree.path, worktreeOptions)\n\n\t\t\t\toperations.push({\n\t\t\t\t\ttype: 'worktree',\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tmessage: `Worktree removed: ${worktree.path}`,\n\t\t\t\t})\n\t\t\t} catch (error) {\n\t\t\t\tconst err = error instanceof Error ? error : new Error('Unknown error')\n\t\t\t\terrors.push(err)\n\t\t\t\toperations.push({\n\t\t\t\t\ttype: 'worktree',\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tmessage: `Failed to remove worktree`,\n\t\t\t\t\terror: err.message,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\t// Step 4.5: Archive recap file\n\t\tif (worktree) {\n\t\t\tif (options.dryRun) {\n\t\t\t\toperations.push({\n\t\t\t\t\ttype: 'recap',\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tmessage: `[DRY RUN] Would archive recap file for: ${worktree.path}`,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tawait archiveRecap(worktree.path)\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: 'recap',\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\tmessage: `Recap file archived`,\n\t\t\t\t\t})\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// Non-fatal: log warning but don't add to errors\n\t\t\t\t\tconst err = error instanceof Error ? error : new Error('Unknown error')\n\t\t\t\t\tgetLogger().warn(`Recap archival failed: ${err.message}`)\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: 'recap',\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\tmessage: 'Recap archival failed (non-fatal)',\n\t\t\t\t\t\terror: err.message,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Step 5: Delete branch if requested\n\t\tif (options.deleteBranch && worktree) {\n\t\t\tif (options.dryRun) {\n\t\t\t\toperations.push({\n\t\t\t\t\ttype: 'branch',\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tmessage: `[DRY RUN] Would delete branch: ${worktree.branch}`,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tconst branchOptions: BranchDeleteOptions = {\n\t\t\t\t\t\tdryRun: false,\n\t\t\t\t\t\tsafetyVerified: safetyCheckPassed,\n\t\t\t\t\t}\n\t\t\t\t\t// Pass pre-fetched merge target (fetched in Step 3.6 before worktree deletion)\n\t\t\t\t\tif (mergeTargetBranch !== null) {\n\t\t\t\t\t\tbranchOptions.mergeTargetBranch = mergeTargetBranch\n\t\t\t\t\t}\n\t\t\t\t\tif (options.force !== undefined) {\n\t\t\t\t\t\tbranchOptions.force = options.force\n\t\t\t\t\t}\n\t\t\t\t\t// Pass main worktree path to ensure we can execute git commands\n\t\t\t\t\tawait this.deleteBranch(worktree.branch, branchOptions, mainWorktreePath ?? undefined)\n\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: 'branch',\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\tmessage: `Branch deleted: ${worktree.branch}`,\n\t\t\t\t\t})\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst err = error instanceof Error ? error : new Error('Unknown error')\n\t\t\t\t\terrors.push(err)\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: 'branch',\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\tmessage: `Failed to delete branch`,\n\t\t\t\t\t\terror: err.message,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Step 5.5: Cleanup CLI symlinks if CLI isolation is available\n\t\t// Derive identifier from parsed input (number for issue/PR, branchName for branch)\n\t\tconst cliIdentifier = parsed.number ?? parsed.branchName\n\t\tif (this.cliIsolation && cliIdentifier !== undefined) {\n\t\t\tif (options.dryRun) {\n\t\t\t\toperations.push({\n\t\t\t\t\ttype: 'cli-symlinks',\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tmessage: `[DRY RUN] Would cleanup CLI symlinks for: ${cliIdentifier}`,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tconst removed = await this.cliIsolation.cleanupVersionedExecutables(cliIdentifier)\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: 'cli-symlinks',\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\tmessage: removed.length > 0\n\t\t\t\t\t\t\t? `CLI symlinks removed: ${removed.length}`\n\t\t\t\t\t\t\t: 'No CLI symlinks to cleanup',\n\t\t\t\t\t})\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// Log warning but don't fail\n\t\t\t\t\tconst err = error instanceof Error ? error : new Error('Unknown error')\n\t\t\t\t\terrors.push(err)\n\t\t\t\t\tgetLogger().warn(\n\t\t\t\t\t\t`CLI symlink cleanup failed: ${err.message}`\n\t\t\t\t\t)\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: 'cli-symlinks',\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\tmessage: 'CLI symlink cleanup failed (non-fatal)',\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Step 6: Cleanup database after worktree and branch removal (using pre-read config)\n\t\tif (databaseConfig && worktree) {\n\t\t\tif (options.dryRun) {\n\t\t\t\toperations.push({\n\t\t\t\t\ttype: 'database',\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tmessage: `[DRY RUN] Would cleanup database branch for: ${worktree.branch}`,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tif (databaseConfig.shouldCleanup && this.database) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t// Call database deletion with pre-fetched shouldCleanup value and main worktree path\n\t\t\t\t\t\t\t// This avoids reading the already-deleted env file and running commands from deleted directories\n\t\t\t\t\t\t\tconst deletionResult = await this.database.deleteBranchIfConfigured(\n\t\t\t\t\t\t\t\tworktree.branch,\n\t\t\t\t\t\t\t\tdatabaseConfig.shouldCleanup,\n\t\t\t\t\t\t\t\tfalse, // isPreview\n\t\t\t\t\t\t\t\tmainWorktreePath ?? undefined\n\t\t\t\t\t\t\t)\n\n\t\t\t\t\t\t\t// Create operation result based on what actually happened\n\t\t\t\t\t\t\tif (deletionResult.deleted) {\n\t\t\t\t\t\t\t\t// Branch was actually deleted\n\t\t\t\t\t\t\t\tgetLogger().info(`Database branch deleted: ${worktree.branch}`)\n\t\t\t\t\t\t\t\toperations.push({\n\t\t\t\t\t\t\t\t\ttype: 'database',\n\t\t\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\t\t\tmessage: `Database branch deleted`,\n\t\t\t\t\t\t\t\t\tdeleted: true,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t} else if (deletionResult.notFound) {\n\t\t\t\t\t\t\t\t// Branch didn't exist - not an error, just nothing to delete\n\t\t\t\t\t\t\t\tgetLogger().debug(`No database branch found for: ${worktree.branch}`)\n\t\t\t\t\t\t\t\toperations.push({\n\t\t\t\t\t\t\t\t\ttype: 'database',\n\t\t\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\t\t\tmessage: `No database branch found (skipped)`,\n\t\t\t\t\t\t\t\t\tdeleted: false,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t} else if (deletionResult.userDeclined) {\n\t\t\t\t\t\t\t\t// User declined preview database deletion\n\t\t\t\t\t\t\t\tgetLogger().info('Preview database deletion declined by user')\n\t\t\t\t\t\t\t\toperations.push({\n\t\t\t\t\t\t\t\t\ttype: 'database',\n\t\t\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\t\t\tmessage: `Database cleanup skipped (user declined)`,\n\t\t\t\t\t\t\t\t\tdeleted: false,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t} else if (!deletionResult.success) {\n\t\t\t\t\t\t\t\t// Deletion failed with error\n\t\t\t\t\t\t\t\tconst errorMsg = deletionResult.error ?? 'Unknown error'\n\t\t\t\t\t\t\t\terrors.push(new Error(errorMsg))\n\t\t\t\t\t\t\t\tgetLogger().warn(`Database cleanup failed: ${errorMsg}`)\n\t\t\t\t\t\t\t\toperations.push({\n\t\t\t\t\t\t\t\t\ttype: 'database',\n\t\t\t\t\t\t\t\t\tsuccess: false, // Non-fatal, but report error\n\t\t\t\t\t\t\t\t\tmessage: `Database cleanup failed`,\n\t\t\t\t\t\t\t\t\terror: errorMsg,\n\t\t\t\t\t\t\t\t\tdeleted: false,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// Unexpected state - log for debugging\n\t\t\t\t\t\t\t\terrors.push(new Error('Database cleanup in an unknown state'))\n\t\t\t\t\t\t\t\tgetLogger().warn('Database deletion returned unexpected result state')\n\t\t\t\t\t\t\t\toperations.push({\n\t\t\t\t\t\t\t\t\ttype: 'database',\n\t\t\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\t\t\tmessage: `Database cleanup in an unknown state`,\n\t\t\t\t\t\t\t\t\tdeleted: false,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t// Unexpected exception (shouldn't happen with result object pattern)\n\t\t\t\t\t\t\terrors.push(error instanceof Error ? error : new Error(String(error)))\n\t\t\t\t\t\t\tgetLogger().warn(\n\t\t\t\t\t\t\t\t`Unexpected database cleanup exception: ${error instanceof Error ? error.message : String(error)}`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\toperations.push({\n\t\t\t\t\t\t\t\ttype: 'database',\n\t\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\t\tmessage: `Database cleanup failed`,\n\t\t\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t\t\t\tdeleted: false,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Database manager not available or not configured\n\t\t\t\t\t\toperations.push({\n\t\t\t\t\t\t\ttype: 'database',\n\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\tmessage: `Database cleanup skipped (not available)`,\n\t\t\t\t\t\t\tdeleted: false,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// This catch block is for any unexpected errors in the outer logic\n\t\t\t\t\tconst err = error instanceof Error ? error : new Error('Unknown error')\n\t\t\t\t\terrors.push(err)\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: 'database',\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\tmessage: `Database cleanup failed`,\n\t\t\t\t\t\terror: err.message,\n\t\t\t\t\t\tdeleted: false,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Step 7: Delete or archive metadata file\n\t\tif (worktree) {\n\t\t\tif (options.dryRun) {\n\t\t\t\tconst action = options.archive ? 'archive' : 'delete'\n\t\t\t\toperations.push({\n\t\t\t\t\ttype: 'metadata',\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tmessage: `[DRY RUN] Would ${action} metadata for worktree: ${worktree.path}`,\n\t\t\t\t})\n\t\t\t} else if (options.archive) {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.metadataManager.archiveMetadata(worktree.path)\n\t\t\t\t\tgetLogger().info(`Metadata archived for worktree: ${worktree.path}`)\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: 'metadata',\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\tmessage: 'Metadata archived',\n\t\t\t\t\t})\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst err = error instanceof Error ? error : new Error(String(error))\n\t\t\t\t\terrors.push(err)\n\t\t\t\t\tgetLogger().warn(`Metadata archival failed: ${err.message}`)\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: 'metadata',\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\tmessage: 'Metadata archival failed (non-fatal)',\n\t\t\t\t\t\terror: err.message,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.metadataManager.deleteMetadata(worktree.path)\n\t\t\t\t\tgetLogger().info(`Metadata deleted for worktree: ${worktree.path}`)\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: 'metadata',\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\tmessage: 'Metadata deleted',\n\t\t\t\t\t})\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst err = error instanceof Error ? error : new Error(String(error))\n\t\t\t\t\terrors.push(err)\n\t\t\t\t\tgetLogger().warn(`Metadata deletion failed: ${err.message}`)\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: 'metadata',\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\tmessage: 'Metadata deletion failed (non-fatal)',\n\t\t\t\t\t\terror: err.message,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Step 7.5: Remove Claude trust entry for the worktree\n\t\tif (worktree && !options.dryRun) {\n\t\t\ttry {\n\t\t\t\tawait removeClaudeTrust(worktree.path)\n\t\t\t\tgetLogger().debug(`Claude trust entry removed for worktree: ${worktree.path}`)\n\t\t\t\toperations.push({\n\t\t\t\t\ttype: 'trust',\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tmessage: 'Claude trust entry removed',\n\t\t\t\t})\n\t\t\t} catch (error) {\n\t\t\t\t// Non-fatal: trust removal failure should not block cleanup\n\t\t\t\tconst err = error instanceof Error ? error : new Error(String(error))\n\t\t\t\tgetLogger().warn(`Claude trust removal failed (non-fatal): ${err.message}`)\n\t\t\t\toperations.push({\n\t\t\t\t\ttype: 'trust',\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tmessage: 'Claude trust removal failed (non-fatal)',\n\t\t\t\t\terror: err.message,\n\t\t\t\t})\n\t\t\t}\n\t\t} else if (worktree && options.dryRun) {\n\t\t\toperations.push({\n\t\t\t\ttype: 'trust',\n\t\t\t\tsuccess: true,\n\t\t\t\tmessage: `[DRY RUN] Would remove Claude trust entry for: ${worktree.path}`,\n\t\t\t})\n\t\t}\n\n\t\t// Calculate overall success\n\t\tconst success = errors.length === 0\n\n\t\treturn {\n\t\t\tidentifier: displayIdentifier,\n\t\t\tbranchName: worktree?.branch,\n\t\t\tsuccess,\n\t\t\toperations,\n\t\t\terrors,\n\t\t\trollbackRequired: false, // Cleanup operations are generally not reversible\n\t\t}\n\t}\n\n\t/**\n\t * Terminate dev server on specified port.\n\t * When a dockerIdentifier is provided, attempts Docker container cleanup first\n\t * before falling back to process-based detection.\n\t *\n\t * @param port - Port the dev server is running on\n\t * @param dockerIdentifier - Optional identifier for Docker container lookup (issue number, branch name, etc.)\n\t */\n\tasync terminateDevServer(port: number, dockerIdentifier?: string | number): Promise<boolean> {\n\t\tgetLogger().debug(`Checking for dev server on port ${port}`)\n\n\t\t// Try Docker container cleanup first if identifier provided\n\t\tif (dockerIdentifier !== undefined) {\n\t\t\tconst containerName = DockerManager.buildContainerName(dockerIdentifier)\n\t\t\tconst isRunning = await DockerManager.isContainerRunning(containerName)\n\t\t\tif (isRunning) {\n\t\t\t\tgetLogger().info(`Terminating Docker container: ${containerName}`)\n\t\t\t\tawait DockerManager.stopAndRemoveContainer(containerName)\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tgetLogger().debug(`No Docker container found with name \"${containerName}\", falling back to process detection`)\n\t\t}\n\n\t\t// Existing process-based detection\n\t\tconst processInfo = await this.processManager.detectDevServer(port)\n\n\t\tif (!processInfo) {\n\t\t\tgetLogger().debug(`No process found on port ${port}`)\n\t\t\treturn false\n\t\t}\n\n\t\tif (!processInfo.isDevServer) {\n\t\t\tgetLogger().warn(\n\t\t\t\t`Process on port ${port} (${processInfo.name}) doesn't appear to be a dev server, skipping`\n\t\t\t)\n\t\t\treturn false\n\t\t}\n\n\t\tgetLogger().info(`Terminating dev server: ${processInfo.name} (PID: ${processInfo.pid})`)\n\n\t\tawait this.processManager.terminateProcess(processInfo.pid)\n\n\t\t// Verify termination\n\t\tconst isFree = await this.processManager.verifyPortFree(port)\n\t\tif (!isFree) {\n\t\t\tthrow new Error(`Dev server may still be running on port ${port}`)\n\t\t}\n\n\t\treturn true\n\t}\n\n\t/**\n\t * Delete a Git branch with safety checks\n\t *\n\t * @param branchName - Name of the branch to delete\n\t * @param options - Delete options (force, dryRun)\n\t * @param cwd - Working directory to execute git command from (defaults to finding main worktree)\n\t */\n\tasync deleteBranch(\n\t\tbranchName: string,\n\t\toptions: BranchDeleteOptions = {},\n\t\tcwd?: string\n\t): Promise<boolean> {\n\t\t// Get protected branches list from centralized method\n\t\tconst protectedBranches = await this.settingsManager.getProtectedBranches(cwd)\n\n\t\t// Check for protected branches\n\t\tif (protectedBranches.includes(branchName)) {\n\t\t\tthrow new Error(`Cannot delete protected branch: ${branchName}`)\n\t\t}\n\n\t\t// Use provided cwd, or find main worktree path as fallback\n\t\t// This ensures we're not running git commands from a deleted directory\n\t\tconst workingDir = cwd ?? await findMainWorktreePathWithSettings(undefined, this.settingsManager)\n\n\t\t// Check if branch exists before attempting deletion (idempotent behavior)\n\t\ttry {\n\t\t\tawait executeGitCommand(['rev-parse', '--verify', `refs/heads/${branchName}`], {\n\t\t\t\tcwd: workingDir\n\t\t\t})\n\t\t} catch {\n\t\t\t// Branch doesn't exist - already deleted, return success\n\t\t\tgetLogger().debug(`Branch ${branchName} does not exist, skipping deletion`)\n\t\t\treturn true\n\t\t}\n\n\t\tif (options.dryRun) {\n\t\t\tgetLogger().info(`[DRY RUN] Would delete branch: ${branchName}`)\n\t\t\treturn true\n\t\t}\n\n\t\t// Execute git branch deletion\n\t\t// deleteCwd is declared outside try so it's accessible in the catch block for safetyVerified retry\n\t\tlet deleteCwd = workingDir // Default: main worktree\n\t\ttry {\n\t\t\t// Determine the correct delete flag and working directory\n\t\t\tlet deleteFlag = '-d' // Default: safe delete\n\n\t\t\tif (options.force) {\n\t\t\t\t// User explicitly requested force delete\n\t\t\t\tdeleteFlag = '-D'\n\t\t\t} else if (options.mergeTargetBranch) {\n\t\t\t\t// Use pre-fetched merge target (from Step 3.6, fetched before worktree deletion)\n\t\t\t\t// For child looms, git branch -d checks against HEAD, which may not be the correct target\n\t\t\t\t// Instead of using -D (force), we run git branch -d from the worktree where the\n\t\t\t\t// parent branch is checked out. This lets git do its own safety verification.\n\t\t\t\tconst mergeTarget = options.mergeTargetBranch\n\n\t\t\t\t// Find the worktree where the merge target (parent branch) is checked out\n\t\t\t\ttry {\n\t\t\t\t\tconst targetWorktreePath = await findWorktreeForBranch(mergeTarget, workingDir)\n\t\t\t\t\t// Run git branch -d from that worktree - HEAD will be the correct branch\n\t\t\t\t\t// and git will correctly verify the merge itself\n\t\t\t\t\tgetLogger().debug(`Running branch delete from worktree where '${mergeTarget}' is checked out: ${targetWorktreePath}`)\n\t\t\t\t\tdeleteCwd = targetWorktreePath\n\t\t\t\t} catch {\n\t\t\t\t\t// If we can't find the worktree for the target branch, fall back to checking merge status\n\t\t\t\t\t// and using -D if merged (the previous behavior)\n\t\t\t\t\tgetLogger().debug(`Could not find worktree for branch '${mergeTarget}', falling back to merge check`)\n\t\t\t\t\tconst isMerged = await isBranchMergedIntoMain(branchName, mergeTarget, workingDir)\n\n\t\t\t\t\tif (isMerged) {\n\t\t\t\t\t\tgetLogger().debug(`Branch '${branchName}' verified merged into '${mergeTarget}', using force delete`)\n\t\t\t\t\t\tdeleteFlag = '-D'\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (options.worktreePath) {\n\t\t\t\t// DEPRECATED: Fall back to reading from worktree path if mergeTargetBranch not provided\n\t\t\t\t// This path should not be used when called from cleanupWorktree() since the worktree\n\t\t\t\t// has already been deleted by the time deleteBranch() is called\n\t\t\t\tgetLogger().warn('deleteBranch called with worktreePath but no mergeTargetBranch - this may fail if worktree was deleted')\n\t\t\t\ttry {\n\t\t\t\t\tconst mergeTarget = await getMergeTargetBranch(options.worktreePath, {\n\t\t\t\t\t\tsettingsManager: this.settingsManager,\n\t\t\t\t\t\tmetadataManager: this.metadataManager,\n\t\t\t\t\t})\n\n\t\t\t\t\t// Find the worktree where the merge target (parent branch) is checked out\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst targetWorktreePath = await findWorktreeForBranch(mergeTarget, workingDir)\n\t\t\t\t\t\tgetLogger().debug(`Running branch delete from worktree where '${mergeTarget}' is checked out: ${targetWorktreePath}`)\n\t\t\t\t\t\tdeleteCwd = targetWorktreePath\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tgetLogger().debug(`Could not find worktree for branch '${mergeTarget}', falling back to merge check`)\n\t\t\t\t\t\tconst isMerged = await isBranchMergedIntoMain(branchName, mergeTarget, workingDir)\n\n\t\t\t\t\t\tif (isMerged) {\n\t\t\t\t\t\t\tgetLogger().debug(`Branch '${branchName}' verified merged into '${mergeTarget}', using force delete`)\n\t\t\t\t\t\t\tdeleteFlag = '-D'\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// If we can't read the merge target (e.g., worktree deleted), just use safe delete\n\t\t\t\t\tgetLogger().debug(`Could not read merge target from worktreePath: ${error instanceof Error ? error.message : String(error)}`)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait executeGitCommand(['branch', deleteFlag, branchName], {\n\t\t\t\tcwd: deleteCwd\n\t\t\t})\n\n\t\t\tgetLogger().info(`Branch deleted: ${branchName}`)\n\t\t\treturn true\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : String(error)\n\n\t\t\t// Handle \"branch not found\" - may occur in race conditions\n\t\t\tif (errorMessage.includes('not found') || errorMessage.includes('does not exist')) {\n\t\t\t\tgetLogger().debug(`Branch ${branchName} already deleted`)\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\tif (options.force) {\n\t\t\t\tthrow error\n\t\t\t}\n\n\t\t\t// Git error for unmerged branch typically contains \"not fully merged\"\n\t\t\tif (errorMessage.includes('not fully merged')) {\n\t\t\t\tif (options.safetyVerified) {\n\t\t\t\t\t// Safety check already confirmed no data loss - retry with force delete\n\t\t\t\t\tgetLogger().info(`Branch '${branchName}' not merged into HEAD but safety verified - using force delete`)\n\t\t\t\t\tawait executeGitCommand(['branch', '-D', branchName], { cwd: deleteCwd })\n\t\t\t\t\tgetLogger().info(`Branch deleted: ${branchName}`)\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Cannot delete unmerged branch '${branchName}'. Use --force to delete anyway.`\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// For other errors, show the actual git error\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Cleanup database branch\n\t * Gracefully handles missing DatabaseManager\n\t *\n\t * @deprecated This method is deprecated and should not be used for post-deletion cleanup.\n\t * Use the pre-fetch mechanism in cleanupWorktree() instead.\n\t * This method will fail if called after worktree deletion because\n\t * it attempts to read the .env file which has been deleted.\n\t *\n\t * @param branchName - Name of the branch to delete\n\t * @param worktreePath - Path to worktree (must still exist with .env file)\n\t */\n\tasync cleanupDatabase(branchName: string, worktreePath: string): Promise<boolean> {\n\t\tif (!this.database) {\n\t\t\tgetLogger().debug('Database manager not available, skipping database cleanup')\n\t\t\treturn false\n\t\t}\n\n\t\ttry {\n\t\t\t// Pre-fetch configuration before deletion\n\t\t\tconst envFilePath = path.join(worktreePath, '.env')\n\t\t\tconst shouldCleanup = await this.database.shouldUseDatabaseBranching(envFilePath)\n\n\t\t\t// Find main worktree path to avoid running commands from potentially deleted directories\n\t\t\tlet cwd: string | undefined\n\t\t\ttry {\n\t\t\t\tcwd = await findMainWorktreePathWithSettings(worktreePath, this.settingsManager)\n\t\t\t} catch (error) {\n\t\t\t\t// If we can't find main worktree, commands will run from current directory\n\t\t\t\tgetLogger().debug(\n\t\t\t\t\t`Could not find main worktree path, using current directory: ${error instanceof Error ? error.message : String(error)}`\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tconst result = await this.database.deleteBranchIfConfigured(\n\t\t\t\tbranchName,\n\t\t\t\tshouldCleanup,\n\t\t\t\tfalse, // isPreview\n\t\t\t\tcwd\n\t\t\t)\n\n\t\t\t// Only return true if deletion actually occurred\n\t\t\tif (result.deleted) {\n\t\t\t\tgetLogger().info(`Database branch deleted: ${branchName}`)\n\t\t\t\treturn true\n\t\t\t} else if (result.notFound) {\n\t\t\t\tgetLogger().debug(`No database branch found for: ${branchName}`)\n\t\t\t\treturn false\n\t\t\t} else if (result.userDeclined) {\n\t\t\t\tgetLogger().info('Preview database deletion declined by user')\n\t\t\t\treturn false\n\t\t\t} else if (!result.success) {\n\t\t\t\tgetLogger().warn(`Database cleanup failed: ${result.error ?? 'Unknown error'}`)\n\t\t\t\treturn false\n\t\t\t} else {\n\t\t\t\t// Unexpected state\n\t\t\t\tgetLogger().debug('Database deletion returned unexpected result')\n\t\t\t\treturn false\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Unexpected exception\n\t\t\tgetLogger().warn(\n\t\t\t\t`Unexpected database cleanup error: ${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 * Cleanup multiple worktrees\n\t */\n\tasync cleanupMultipleWorktrees(\n\t\tidentifiers: string[],\n\t\toptions: ResourceCleanupOptions = {}\n\t): Promise<CleanupResult[]> {\n\t\tconst results: CleanupResult[] = []\n\n\t\tfor (const identifier of identifiers) {\n\t\t\t// Parse the identifier to get ParsedInput format\n\t\t\tconst parsed = this.parseIdentifier(identifier)\n\t\t\tconst result = await this.cleanupWorktree(parsed, options)\n\t\t\tresults.push(result)\n\t\t}\n\n\t\treturn results\n\t}\n\n\t/**\n\t * Validate worktree safety given a worktree object\n\t * Private method used internally when worktree is already known\n\t *\n\t * @param worktree - The worktree to validate\n\t * @param identifier - The original identifier used (for error messages)\n\t * @param checkBranchMerge - Whether to check if branch is merged into main (for branch deletion)\n\t * @param checkRemoteBranch - Whether to check if branch exists on remote (for GitHub-PR mode)\n\t */\n\tprivate async validateWorktreeSafety(\n\t\tworktree: GitWorktree,\n\t\tidentifier: string,\n\t\tcheckBranchMerge: boolean = false,\n\t\tcheckRemoteBranch: boolean = false\n\t): Promise<SafetyCheck> {\n\t\tconst warnings: string[] = []\n\t\tconst blockers: string[] = []\n\n\t\t// Check if main worktree\n\t\tconst isMain = await this.gitWorktree.isMainWorktree(worktree, this.settingsManager)\n\t\tif (isMain) {\n\t\t\tblockers.push(`Cannot cleanup main worktree: \"${worktree.branch}\" @ \"${worktree.path}\"`)\n\t\t}\n\n\t\t// Check for uncommitted changes\n\t\tconst hasChanges = await hasUncommittedChanges(worktree.path)\n\t\tif (hasChanges) {\n\t\t\t// Create simple blocker message with actionable guidance\n\t\t\tconst blockerMessage =\n\t\t\t\t`Worktree has uncommitted changes.\\n\\n` +\n\t\t\t\t`Please resolve before cleanup - you have some options:\\n` +\n\t\t\t\t` • Commit changes: cd ${worktree.path} && git commit -am \"message\"\\n` +\n\t\t\t\t` • Stash changes: cd ${worktree.path} && git stash\\n` +\n\t\t\t\t` • Force cleanup: il cleanup ${identifier} --force (WARNING: will discard changes)`\n\n\t\t\tblockers.push(blockerMessage)\n\t\t}\n\n\t\t// 5-point safety check for branch deletion\n\t\t// The key insight: we care about DATA LOSS, not about remote state\n\t\t// - Remote ahead of local is SAFE (commits exist on remote, no data loss)\n\t\t// - Local ahead of remote is DANGEROUS (unpushed commits would be lost)\n\t\t//\n\t\t// 1. Network error -> BLOCK (can't verify safety)\n\t\t// 2. Remote ahead of local -> OK (no data loss - commits exist on remote)\n\t\t// 3. Local ahead of remote (unpushed commits) -> BLOCK (data loss risk)\n\t\t// 4. No remote, merged to main -> OK (work is in main)\n\t\t// 5. No remote, NOT merged to main -> BLOCK (unmerged work would be lost)\n\t\tif ((checkBranchMerge || checkRemoteBranch) && worktree.branch) {\n\t\t\t// Use shared utility to get merge target (parent branch for child looms, main for others)\n\t\t\tconst mainBranch = await getMergeTargetBranch(worktree.path, {\n\t\t\t\tsettingsManager: this.settingsManager,\n\t\t\t\tmetadataManager: this.metadataManager,\n\t\t\t})\n\n\t\t\t// Check remote branch status\n\t\t\tconst remoteStatus: RemoteBranchStatus = await checkRemoteBranchStatus(worktree.branch, worktree.path)\n\n\t\t\t// Scenario 1: Network error checking remote -> Block\n\t\t\tif (remoteStatus.networkError) {\n\t\t\t\tconst blockerMessage =\n\t\t\t\t\t`Cannot verify remote branch status due to network error.\\n\\n` +\n\t\t\t\t\t`Error: ${remoteStatus.errorMessage ?? 'Unknown network error'}\\n\\n` +\n\t\t\t\t\t`Unable to determine if branch '${worktree.branch}' is safely backed up.\\n` +\n\t\t\t\t\t`Use --force to proceed without verification.`\n\n\t\t\t\tblockers.push(blockerMessage)\n\t\t\t}\n\t\t\t// Scenario 3: Local ahead of remote (unpushed commits) -> Block (data loss risk)\n\t\t\telse if (remoteStatus.exists && remoteStatus.localAhead) {\n\t\t\t\tconst blockerMessage =\n\t\t\t\t\t`Branch '${worktree.branch}' has unpushed commits that would be lost.\\n` +\n\t\t\t\t\t`The remote branch exists but your local branch is ahead.\\n\\n` +\n\t\t\t\t\t`Please resolve before cleanup:\\n` +\n\t\t\t\t\t` • Push your commits: git push origin ${worktree.branch}\\n` +\n\t\t\t\t\t` • Force cleanup: il cleanup ${identifier} --force (WARNING: will lose commits)`\n\n\t\t\t\tblockers.push(blockerMessage)\n\t\t\t}\n\t\t\t// Scenario 2: Remote ahead of local OR same commits -> Safe (work is on remote)\n\t\t\telse if (remoteStatus.exists && !remoteStatus.localAhead) {\n\t\t\t\t// Work is safely on remote (either remote is ahead or same commits)\n\t\t\t\t// No blocker needed\n\t\t\t}\n\t\t\t// Remote doesn't exist - need to check merge status\n\t\t\telse if (!remoteStatus.exists) {\n\t\t\t\tconst isMerged = await isBranchMergedIntoMain(worktree.branch, mainBranch, worktree.path)\n\n\t\t\t\tif (isMerged) {\n\t\t\t\t\t// Scenario 4: Remote doesn't exist, but merged to main -> Safe\n\t\t\t\t\t// No blocker needed\n\t\t\t\t} else {\n\t\t\t\t\t// Scenario 5: Remote doesn't exist AND not merged to main -> Block\n\t\t\t\t\tconst blockerMessage =\n\t\t\t\t\t\t`Branch '${worktree.branch}' has not been pushed to remote and is not merged into '${mainBranch}'.\\n` +\n\t\t\t\t\t\t`Deleting this branch would result in data loss.\\n\\n` +\n\t\t\t\t\t\t`Please resolve before cleanup - you have some options:\\n` +\n\t\t\t\t\t\t` • Push to remote: git push -u origin ${worktree.branch}\\n` +\n\t\t\t\t\t\t` • Merge to ${mainBranch}: git checkout ${mainBranch} && git merge ${worktree.branch}\\n` +\n\t\t\t\t\t\t` • Force cleanup: il cleanup ${identifier} --force (WARNING: will lose commits)`\n\n\t\t\t\t\tblockers.push(blockerMessage)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tisSafe: blockers.length === 0,\n\t\t\twarnings,\n\t\t\tblockers,\n\t\t}\n\t}\n\n\t/**\n\t * Validate cleanup safety\n\t */\n\tasync validateCleanupSafety(identifier: string): Promise<SafetyCheck> {\n\t\tconst warnings: string[] = []\n\t\tconst blockers: string[] = []\n\n\t\t// Find worktree\n\t\tconst worktrees = await this.gitWorktree.findWorktreesByIdentifier(identifier)\n\n\t\tif (worktrees.length === 0) {\n\t\t\tblockers.push(`No worktree found for: ${identifier}`)\n\t\t\treturn { isSafe: false, warnings, blockers }\n\t\t}\n\n\t\tconst worktree = worktrees[0]\n\t\tif (!worktree) {\n\t\t\tblockers.push(`No worktree found for: ${identifier}`)\n\t\t\treturn { isSafe: false, warnings, blockers }\n\t\t}\n\n\t\t// Delegate to private method that validates the worktree\n\t\treturn await this.validateWorktreeSafety(worktree, identifier)\n\t}\n\n\t/**\n\t * Parse identifier to determine type and extract number\n\t * Helper method for port calculation\n\t */\n\tprivate parseIdentifier(identifier: string): ParsedInput {\n\t\t// Check for issue pattern\n\t\tconst issueId = extractIssueNumber(identifier)\n\t\tif (issueId !== null) {\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: issueId,\n\t\t\t\toriginalInput: identifier\n\t\t\t}\n\t\t}\n\n\t\t// Check for PR pattern\n\t\tconst prMatch = identifier.match(/(?:pr|PR)[/-](\\d+)/)\n\t\tif (prMatch?.[1]) {\n\t\t\treturn {\n\t\t\t\ttype: 'pr',\n\t\t\t\tnumber: parseInt(prMatch[1], 10),\n\t\t\t\toriginalInput: identifier\n\t\t\t}\n\t\t}\n\n\t\t// Check for numeric identifier\n\t\tconst numericMatch = identifier.match(/^#?(\\d+)$/)\n\t\tif (numericMatch?.[1]) {\n\t\t\t// Assume issue for numeric identifiers\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: parseInt(numericMatch[1], 10),\n\t\t\t\toriginalInput: identifier\n\t\t\t}\n\t\t}\n\n\t\t// Treat as branch name\n\t\treturn {\n\t\t\ttype: 'branch',\n\t\t\tbranchName: identifier,\n\t\t\toriginalInput: identifier\n\t\t}\n\t}\n}\n"],"mappings":"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","names":[]}
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  SettingsManager
4
- } from "./chunk-ET6A2JR4.js";
4
+ } from "./chunk-653XBU3L.js";
5
5
  import {
6
6
  MetadataManager
7
- } from "./chunk-YRCEOQPX.js";
7
+ } from "./chunk-4JZEQBWV.js";
8
8
  import {
9
9
  logger
10
- } from "./chunk-H2SSF24U.js";
10
+ } from "./chunk-VRPPI6GU.js";
11
11
 
12
12
  // src/utils/git.ts
13
13
  import path from "path";
@@ -375,7 +375,7 @@ async function getDefaultBranch(path2 = process.cwd()) {
375
375
  }
376
376
  async function findAllBranchesForIssue(issueNumber, path2 = process.cwd(), settingsManager) {
377
377
  if (!settingsManager) {
378
- const { SettingsManager: SM } = await import("./SettingsManager-WQ5NSGAH.js");
378
+ const { SettingsManager: SM } = await import("./SettingsManager-BMQCAXPP.js");
379
379
  settingsManager = new SM();
380
380
  }
381
381
  const protectedBranches = await settingsManager.getProtectedBranches(path2);
@@ -738,4 +738,4 @@ export {
738
738
  removePlaceholderCommitFromHead,
739
739
  removePlaceholderCommitFromHistory
740
740
  };
741
- //# sourceMappingURL=chunk-3RXYOBME.js.map
741
+ //# sourceMappingURL=chunk-OPQC4OWM.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/git.ts"],"sourcesContent":["import path from 'path'\nimport { execa, type ExecaError } from 'execa'\nimport { type GitWorktree } from '../types/worktree.js'\nimport { SettingsManager, type SettingsManager as SettingsManagerType } from '../lib/SettingsManager.js'\nimport { MetadataManager } from '../lib/MetadataManager.js'\nimport { logger } from './logger.js'\n\n/**\n * Custom error class for Git command failures\n * Preserves exit code and stderr for precise error handling\n */\nexport class GitCommandError extends Error {\n constructor(\n message: string,\n public readonly exitCode: number | undefined,\n public readonly stderr: string\n ) {\n super(message)\n this.name = 'GitCommandError'\n }\n}\n\n/**\n * Execute a Git command and return the stdout result\n * Throws a GitCommandError if the command fails\n */\nexport async function executeGitCommand(\n args: string[],\n options?: { cwd?: string; timeout?: number; stdio?: 'inherit' | 'pipe'; env?: NodeJS.ProcessEnv }\n): Promise<string> {\n try {\n const result = await execa('git', args, {\n cwd: options?.cwd ?? process.cwd(),\n timeout: options?.timeout ?? 30000,\n encoding: 'utf8',\n stdio: options?.stdio ?? 'pipe',\n verbose: logger.isDebugEnabled(),\n // Spread env conditionally - only include if defined\n ...(options?.env && { env: options.env }),\n })\n\n return result.stdout\n } catch (error) {\n const execaError = error as ExecaError\n const stderr = execaError.stderr ?? execaError.message ?? 'Unknown Git error'\n throw new GitCommandError(\n `Git command failed: ${stderr}`,\n execaError.exitCode,\n stderr\n )\n }\n}\n\n/**\n * Parse git worktree list output into structured data\n * @param output - The output from git worktree list --porcelain\n * @param defaultBranch - Default branch name to use for bare repositories (defaults to 'main')\n */\nexport function parseWorktreeList(output: string, defaultBranch?: string): GitWorktree[] {\n const worktrees: GitWorktree[] = []\n const lines = output.trim().split('\\n')\n\n let i = 0\n while (i < lines.length) {\n const pathLine = lines[i]\n if (!pathLine?.startsWith('worktree ')) {\n i++\n continue\n }\n\n // Parse path line: \"worktree /path/to/worktree\"\n const pathMatch = pathLine.match(/^worktree (.+)$/)\n if (!pathMatch) {\n i++\n continue\n }\n\n let branch = ''\n let commit = ''\n let detached = false\n let bare = false\n let locked = false\n let lockReason: string | undefined\n\n // Process subsequent lines for this worktree\n i++\n while (i < lines.length && !lines[i]?.startsWith('worktree ')) {\n const line = lines[i]?.trim()\n if (!line) {\n i++\n continue\n }\n\n if (line === 'bare') {\n bare = true\n branch = defaultBranch ?? 'main' // Default assumption for bare repo\n } else if (line === 'detached') {\n detached = true\n branch = 'HEAD'\n } else if (line.startsWith('locked')) {\n locked = true\n const lockMatch = line.match(/^locked (.+)$/)\n lockReason = lockMatch?.[1]\n branch = branch || 'unknown'\n } else if (line.startsWith('HEAD ')) {\n // Parse commit line: \"HEAD abc123def456...\"\n const commitMatch = line.match(/^HEAD ([a-f0-9]+)/)\n if (commitMatch) {\n commit = commitMatch[1] ?? ''\n }\n } else if (line.startsWith('branch ')) {\n // Parse branch line: \"branch refs/heads/feature-branch\"\n const branchMatch = line.match(/^branch refs\\/heads\\/(.+)$/)\n branch = branchMatch?.[1] ?? line.replace('branch ', '')\n }\n\n i++\n }\n\n const worktree: GitWorktree = {\n path: pathMatch[1] ?? '',\n branch,\n commit,\n bare,\n detached,\n locked,\n }\n\n if (lockReason !== undefined) {\n worktree.lockReason = lockReason\n }\n\n worktrees.push(worktree)\n }\n\n return worktrees\n}\n\n/**\n * Check if a branch name follows PR naming patterns\n */\nexport function isPRBranch(branchName: string): boolean {\n const prPatterns = [\n /^pr\\/\\d+/i, // pr/123, pr/123-feature-name\n /^pull\\/\\d+/i, // pull/123\n /^\\d+[-_]/, // 123-feature-name, 123_feature_name\n /^feature\\/pr[-_]?\\d+/i, // feature/pr123, feature/pr-123\n /^hotfix\\/pr[-_]?\\d+/i, // hotfix/pr123\n ]\n\n return prPatterns.some(pattern => pattern.test(branchName))\n}\n\n/**\n * Extract PR number from branch name\n */\nexport function extractPRNumber(branchName: string): number | null {\n const patterns = [\n /^pr\\/(\\d+)/i, // pr/123\n /^pull\\/(\\d+)/i, // pull/123\n /^(\\d+)[-_]/, // 123-feature-name\n /^feature\\/pr[-_]?(\\d+)/i, // feature/pr123\n /^hotfix\\/pr[-_]?(\\d+)/i, // hotfix/pr123\n /pr[-_]?(\\d+)/i, // anywhere with pr123 or pr-123\n ]\n\n for (const pattern of patterns) {\n const match = branchName.match(pattern)\n if (match?.[1]) {\n const num = parseInt(match[1], 10)\n if (!isNaN(num)) return num\n }\n }\n\n return null\n}\n\n/**\n * Extract issue number from branch name\n * Supports both new format (issue-{issueId}__{slug}) and old format (issue-{number}-{slug})\n * @returns string issue ID (alphanumeric) or null if not found\n */\nexport function extractIssueNumber(branchName: string): string | null {\n // Priority 1: New format - issue-{issueId}__ (alphanumeric ID with double underscore)\n const newFormatPattern = /issue-([^_]+)__/i\n const newMatch = branchName.match(newFormatPattern)\n if (newMatch?.[1]) return newMatch[1]\n\n // Priority 2: Old format - issue-{number}- or issue-{number}$ (numeric only, dash or end)\n const oldFormatPattern = /issue-(\\d+)(?:-|$)/i\n const oldMatch = branchName.match(oldFormatPattern)\n if (oldMatch?.[1]) return oldMatch[1]\n\n // Priority 3: Alphanumeric ID at end (either format without description)\n const alphanumericEndPattern = /issue-([^_\\s/]+)$/i\n const alphanumericMatch = branchName.match(alphanumericEndPattern)\n if (alphanumericMatch?.[1]) return alphanumericMatch[1]\n\n // Priority 4: Legacy patterns (issue_N, leading number)\n const legacyPatterns = [\n /issue_(\\d+)/i, // issue_42\n /^(\\d+)-/, // 42-feature-name\n ]\n for (const pattern of legacyPatterns) {\n const match = branchName.match(pattern)\n if (match?.[1]) return match[1]\n }\n\n return null\n}\n\n/**\n * Check if a path follows worktree naming patterns\n */\nexport function isWorktreePath(path: string): boolean {\n const worktreePatterns = [\n /\\/worktrees?\\//i, // Contains /worktree/ or /worktrees/\n /\\/workspace[-_]?\\d+/i, // workspace123, workspace-123\n /\\/issue[-_]?\\d+/i, // issue123, issue-123\n /\\/pr[-_]?\\d+/i, // pr123, pr-123\n /-worktree$/i, // ends with -worktree\n /\\.worktree$/i, // ends with .worktree\n ]\n\n return worktreePatterns.some(pattern => pattern.test(path))\n}\n\n/**\n * Generate a worktree path based on branch name and root directory\n * For PRs, adds _pr_<PR_NUM> suffix to distinguish from issue branches\n */\nexport function generateWorktreePath(\n branchName: string,\n rootDir: string = process.cwd(),\n options?: { isPR?: boolean; prNumber?: number; prefix?: string }\n): string {\n // Replace slashes with dashes (matches bash line 593)\n let sanitized = branchName.replace(/\\//g, '-')\n\n // Add PR suffix if this is a PR (matches bash lines 595-597)\n if (options?.isPR && options?.prNumber) {\n sanitized = `${sanitized}_pr_${options.prNumber}`\n }\n\n const parentDir = path.dirname(rootDir)\n\n // Handle prefix logic\n let prefix: string\n\n if (options?.prefix === undefined) {\n // No prefix in options - calculate default: <basename>-looms\n const mainFolderName = path.basename(rootDir)\n prefix = mainFolderName ? `${mainFolderName}-looms/` : 'looms/'\n } else if (options.prefix === '') {\n // Empty string = no prefix mode\n prefix = ''\n } else {\n // Custom prefix provided\n prefix = options.prefix\n\n // Check if prefix contains forward slashes (nested directory structure)\n const hasNestedPath = prefix.includes('/')\n\n if (hasNestedPath) {\n // Check if it ends with a separator character (dash, underscore, or slash)\n const endsWithSeparator = /[-_/]$/.test(prefix)\n\n if (!endsWithSeparator) {\n // Has nested path but no trailing separator: auto-append hyphen\n // Example: \"temp/looms\" becomes \"temp/looms-\"\n prefix = `${prefix}-`\n }\n // If it already ends with -, _, or /, keep as-is\n } else {\n // Single-level prefix: auto-append separator if it doesn't end with one\n const endsWithSeparator = /[-_]$/.test(prefix)\n if (!endsWithSeparator) {\n prefix = `${prefix}-`\n }\n }\n }\n\n // Apply prefix (or not, if empty)\n if (prefix === '') {\n return path.join(parentDir, sanitized)\n } else if (prefix.endsWith('/')) {\n // Forward slash = nested directory, use path.join for proper handling\n return path.join(parentDir, prefix, sanitized)\n } else if (prefix.includes('/')) {\n // Contains slash but doesn't end with slash = nested with separator (e.g., \"looms/myprefix-\")\n // Split and handle: last part is prefix with separator, rest is directory path\n const lastSlashIndex = prefix.lastIndexOf('/')\n const dirPath = prefix.substring(0, lastSlashIndex)\n const prefixWithSeparator = prefix.substring(lastSlashIndex + 1)\n return path.join(parentDir, dirPath, `${prefixWithSeparator}${sanitized}`)\n } else {\n // Dash/underscore separator = single directory name\n return path.join(parentDir, `${prefix}${sanitized}`)\n }\n}\n\n/**\n * Validate that a directory is a valid Git repository\n */\nexport async function isValidGitRepo(path: string): Promise<boolean> {\n try {\n await executeGitCommand(['rev-parse', '--git-dir'], { cwd: path })\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Get the current branch name for a repository\n */\nexport async function getCurrentBranch(path: string = process.cwd()): Promise<string | null> {\n try {\n const result = await executeGitCommand(['branch', '--show-current'], { cwd: path })\n return result.trim()\n } catch {\n return null\n }\n}\n\n/**\n * Check if a branch exists (local or remote)\n */\nexport async function branchExists(\n branchName: string,\n path: string = process.cwd(),\n includeRemote = true\n): Promise<boolean> {\n try {\n // Check local branches\n const localResult = await executeGitCommand(['branch', '--list', branchName], { cwd: path })\n if (localResult.trim()) {\n return true\n }\n\n // Check remote branches if requested\n if (includeRemote) {\n const remoteResult = await executeGitCommand(['branch', '-r', '--list', `*/${branchName}`], {\n cwd: path,\n })\n if (remoteResult.trim()) {\n return true\n }\n }\n\n return false\n } catch {\n return false\n }\n}\n\n/**\n * Get the root directory of the current worktree\n * Returns the worktree root when in a linked worktree, or main repo root when in main worktree\n */\nexport async function getWorktreeRoot(path: string = process.cwd()): Promise<string | null> {\n try {\n const result = await executeGitCommand(['rev-parse', '--show-toplevel'], { cwd: path })\n return result.trim()\n } catch {\n return null\n }\n}\n\n/**\n * Get the main repository root directory\n * Returns the main repo root even when called from a linked worktree\n */\nexport async function getRepoRoot(path: string = process.cwd()): Promise<string | null> {\n try {\n // Get the common git directory (shared by all worktrees)\n const gitCommonDir = await executeGitCommand(\n ['rev-parse', '--path-format=absolute', '--git-common-dir'],\n { cwd: path }\n )\n const trimmedPath = gitCommonDir.trim()\n\n // Handle linked worktree: /path/to/repo/.git/worktrees/worktree-name -> /path/to/repo\n // Handle main worktree: /path/to/repo/.git -> /path/to/repo\n const repoRoot = trimmedPath\n .replace(/\\/\\.git\\/worktrees\\/[^/]+$/, '') // Remove /.git/worktrees/name suffix\n .replace(/\\/\\.git$/, '') // Remove /.git suffix\n\n return repoRoot\n } catch(error) {\n // \"not a git repository\" is expected when running outside a git repo - use debug level\n // Check for GitCommandError with exit code 128 or the specific stderr message\n if (error instanceof GitCommandError &&\n (error.exitCode === 128 || /fatal: not a git repository/i.test(error.stderr))) {\n logger.info(`Note: No git repository detected: ${path}`)\n } else {\n const errorMessage = error instanceof Error ? error.message : String(error)\n logger.warn(`Failed to determine repo root from git-common-dir: ${path}`, errorMessage)\n }\n return null\n }\n}\n\n/**\n * Find the worktree path where main branch is checked out\n * Copies bash script approach: parse git worktree list to find main\n */\nexport async function findMainWorktreePath(\n path: string = process.cwd(),\n options?: { mainBranch?: string }\n): Promise<string> {\n try {\n const output = await executeGitCommand(['worktree', 'list', '--porcelain'], { cwd: path })\n const worktrees = parseWorktreeList(output, options?.mainBranch)\n\n // Guard: empty worktree list\n if (worktrees.length === 0) {\n throw new Error('No worktrees found in repository')\n }\n\n // Tier 1: Check for specified mainBranch in options\n if (options?.mainBranch) {\n const specified = worktrees.find(wt => wt.branch === options.mainBranch)\n if (!specified?.path) {\n throw new Error(\n `No worktree found with branch '${options.mainBranch}' (specified in settings). Available worktrees: ${worktrees.map(wt => `${wt.path} (${wt.branch})`).join(', ')}`\n )\n }\n return specified.path\n }\n\n // Tier 2: Look for \"main\" branch\n const mainBranch = worktrees.find(wt => wt.branch === 'main')\n if (mainBranch?.path) {\n return mainBranch.path\n }\n\n // Tier 3: Use first worktree (primary worktree)\n const firstWorktree = worktrees[0]\n if (!firstWorktree?.path) {\n throw new Error('Failed to determine primary worktree path')\n }\n return firstWorktree.path\n } catch (error) {\n if (\n error instanceof Error &&\n (error.message.includes('No worktree found with branch') ||\n error.message.includes('No worktrees found') ||\n error.message.includes('Failed to determine primary worktree'))\n ) {\n // Re-throw our specific errors\n throw error\n }\n throw new Error(`Failed to find main worktree: ${error instanceof Error ? error.message : String(error)}`)\n }\n}\n\n/**\n * Find main worktree path with automatic settings loading\n *\n * This is a convenience wrapper that:\n * 1. Loads project settings from .iloom/settings.json\n * 2. Extracts mainBranch configuration if present\n * 3. Calls findMainWorktreePath with appropriate options\n *\n * @param path - Path to search from (defaults to process.cwd())\n * @param settingsManager - Optional SettingsManager instance (for DI/testing)\n * @returns Path to main worktree\n * @throws Error if main worktree cannot be found\n */\nexport async function findMainWorktreePathWithSettings(\n path?: string,\n settingsManager?: SettingsManagerType\n): Promise<string> {\n // Lazy load SettingsManager to avoid circular dependencies\n settingsManager ??= new SettingsManager()\n\n const settings = await settingsManager.loadSettings(path)\n const findOptions = settings.mainBranch ? { mainBranch: settings.mainBranch } : undefined\n return findMainWorktreePath(path, findOptions)\n}\n\n/**\n * Find the worktree path where a specific branch is checked out\n *\n * Used by MergeManager to find the correct worktree for child loom merges.\n * When finishing a child loom, we need to find where the PARENT branch is\n * checked out (the merge target), not where settings.mainBranch is checked out.\n *\n * @param branchName - The branch name to find\n * @param path - Path to search from (defaults to process.cwd())\n * @returns Path to worktree where the branch is checked out\n * @throws Error if no worktree has the specified branch checked out\n */\nexport async function findWorktreeForBranch(\n branchName: string,\n path: string = process.cwd()\n): Promise<string> {\n try {\n const output = await executeGitCommand(['worktree', 'list', '--porcelain'], { cwd: path })\n const worktrees = parseWorktreeList(output, branchName)\n\n // Guard: empty worktree list\n if (worktrees.length === 0) {\n throw new Error('No worktrees found in repository')\n }\n\n // Find the worktree with the specified branch\n const targetWorktree = worktrees.find(wt => wt.branch === branchName)\n if (!targetWorktree?.path) {\n throw new Error(\n `No worktree found with branch '${branchName}' checked out. ` +\n `Available worktrees: ${worktrees.map(wt => `${wt.path} (${wt.branch})`).join(', ')}`\n )\n }\n return targetWorktree.path\n } catch (error) {\n if (\n error instanceof Error &&\n (error.message.includes('No worktree found with branch') ||\n error.message.includes('No worktrees found'))\n ) {\n // Re-throw our specific errors\n throw error\n }\n throw new Error(`Failed to find worktree for branch '${branchName}': ${error instanceof Error ? error.message : String(error)}`)\n }\n}\n\n/**\n * Check if there are uncommitted changes in a repository\n */\nexport async function hasUncommittedChanges(path: string = process.cwd()): Promise<boolean> {\n try {\n const result = await executeGitCommand(['status', '--porcelain'], { cwd: path })\n return result.trim().length > 0\n } catch {\n return false\n }\n}\n\n/**\n * Get the default branch name for a repository\n */\nexport async function getDefaultBranch(path: string = process.cwd()): Promise<string> {\n try {\n // Try to get from remote\n const remoteResult = await executeGitCommand(['symbolic-ref', 'refs/remotes/origin/HEAD'], {\n cwd: path,\n })\n const match = remoteResult.match(/refs\\/remotes\\/origin\\/(.+)/)\n if (match) return match[1] ?? 'main'\n\n // Fallback to common default branch names\n const commonDefaults = ['main', 'master', 'develop']\n for (const branch of commonDefaults) {\n if (await branchExists(branch, path)) {\n return branch\n }\n }\n\n return 'main' // Final fallback\n } catch {\n return 'main'\n }\n}\n\n/**\n * Find all branches related to a GitHub issue or PR number\n * Matches patterns like:\n * - Issue patterns: issue-25, issue/25, 25-feature, feat-25, feat/issue-25\n * - PR patterns: pr/25, pull/25, pr-25, feature/pr-25\n *\n * Based on bash cleanup-worktree.sh find_issue_branches() (lines 133-154)\n *\n * @param issueNumber - The issue or PR number to search for\n * @param path - Working directory to search from (defaults to process.cwd())\n * @param settingsManager - Optional SettingsManager instance (for DI/testing)\n */\nexport async function findAllBranchesForIssue(\n issueNumber: string | number,\n path: string = process.cwd(),\n settingsManager?: SettingsManagerType\n): Promise<string[]> {\n // Lazy load SettingsManager to avoid circular dependencies\n if (!settingsManager) {\n const { SettingsManager: SM } = await import('../lib/SettingsManager.js')\n settingsManager = new SM()\n }\n\n // Get protected branches list from centralized method\n const protectedBranches = await settingsManager.getProtectedBranches(path)\n\n // Get all branches (local and remote)\n const output = await executeGitCommand(['branch', '-a'], { cwd: path })\n\n const branches: string[] = []\n const lines = output.split('\\n').filter(Boolean)\n\n for (const line of lines) {\n // Skip remotes/origin/HEAD pointer\n if (line.includes('remotes/origin/HEAD')) {\n continue\n }\n\n // Clean the branch name:\n // 1. Remove git status markers (* + spaces at start)\n let cleanBranch = line.replace(/^[*+ ]+/, '')\n\n // 2. Remove 'origin/' prefix if present\n cleanBranch = cleanBranch.replace(/^origin\\//, '')\n\n // 3. Remove 'remotes/origin/' prefix if present\n cleanBranch = cleanBranch.replace(/^remotes\\/origin\\//, '')\n\n // 4. Trim any remaining whitespace\n cleanBranch = cleanBranch.trim()\n\n // Skip protected branches\n if (protectedBranches.includes(cleanBranch)) {\n continue\n }\n\n // Check if branch contains issue number with strict word boundary pattern\n // The issue number must NOT be:\n // - Part of a larger number (preceded or followed by a digit)\n // - After an unknown word (like \"tissue-25\")\n // The issue number CAN be:\n // - At start: \"25-feature\"\n // - After known prefix + separator: \"issue-25\", \"feat-25\", \"fix-25\", \"pr-25\"\n // - After just a separator with no prefix: test_25 (separator at start)\n\n // First check: not part of a larger number\n const notPartOfNumber = new RegExp(`(?<!\\\\d)${issueNumber}(?!\\\\d)`)\n if (!notPartOfNumber.test(cleanBranch)) {\n continue\n }\n\n // Second check: if preceded by letters, validate they're known issue-related prefixes\n // This prevents \"tissue-25\" but allows \"issue-25\", \"feat-25\", etc.\n const beforeNumber = cleanBranch.substring(0, cleanBranch.indexOf(String(issueNumber)))\n\n if (beforeNumber) {\n // Extract the last word (letters) before the number\n const lastWord = beforeNumber.match(/([a-zA-Z]+)[-_/\\s]*$/)\n if (lastWord?.[1]) {\n const word = lastWord[1].toLowerCase()\n // Known prefixes for issue-related branches\n const knownPrefixes = [\n 'issue', 'issues',\n 'feat', 'feature', 'features',\n 'fix', 'fixes', 'bugfix', 'hotfix',\n 'pr', 'pull',\n 'test', 'tests',\n 'chore',\n 'docs',\n 'refactor',\n 'perf',\n 'style',\n 'ci',\n 'build',\n 'revert'\n ]\n\n // If we found a word and it's NOT in the known list, skip this branch\n if (!knownPrefixes.includes(word)) {\n continue\n }\n }\n }\n\n // Passed all checks - add to results\n if (!branches.includes(cleanBranch)) {\n branches.push(cleanBranch)\n }\n }\n\n return branches\n}\n\n/**\n * Check if a repository is empty (has no commits yet)\n * @param path - Repository path to check (defaults to process.cwd())\n * @returns true if repository has no commits, false otherwise\n */\nexport async function isEmptyRepository(path: string = process.cwd()): Promise<boolean> {\n try {\n await executeGitCommand(['rev-parse', '--verify', 'HEAD'], { cwd: path })\n return false // HEAD exists, repo has commits\n } catch {\n return true // HEAD doesn't exist, repo is empty\n }\n}\n\n/**\n * Ensure repository has at least one commit\n * Creates an initial empty commit if repository is empty\n * @param path - Repository path (defaults to process.cwd())\n */\nexport async function ensureRepositoryHasCommits(path: string = process.cwd()): Promise<void> {\n const isEmpty = await isEmptyRepository(path)\n if (isEmpty) {\n await executeGitCommand(['commit', '--no-verify', '--allow-empty', '-m', 'Initial commit'], { cwd: path })\n }\n}\n\n/**\n * Push a branch to remote repository\n * Used for PR workflow to push changes to remote without merging locally\n *\n * @param branchName - The branch name to push\n * @param worktreePath - The worktree path where the branch is checked out\n * @param options - Push options\n * @throws Error if push fails\n */\nexport async function pushBranchToRemote(\n branchName: string,\n worktreePath: string,\n options?: { dryRun?: boolean }\n): Promise<void> {\n if (options?.dryRun) {\n // In dry-run mode, just log what would be done\n return\n }\n\n try {\n // Execute: git push origin <branch-name>\n // This matches the bash script behavior (merge-and-clean.sh line 359)\n await executeGitCommand(['push', 'origin', branchName], {\n cwd: worktreePath,\n timeout: 120000, // 120 second timeout for push operations\n })\n } catch (error) {\n // Provide helpful error message based on common push failures\n const errorMessage = error instanceof Error ? error.message : String(error)\n\n // Check for common error patterns and provide context, but ALWAYS include original error\n if (errorMessage.includes('failed to push') || errorMessage.includes('rejected')) {\n throw new Error(\n `Failed to push changes to origin/${branchName}\\n\\n` +\n ` Git error: ${errorMessage}\\n\\n` +\n ` Possible causes:\\n` +\n ` • Remote branch was deleted\\n` +\n ` • Push was rejected (non-fast-forward)\\n` +\n ` • Network connectivity issues\\n\\n` +\n ` To retry: il finish --pr <number>\\n` +\n ` To force push: git push origin ${branchName} --force`\n )\n }\n\n if (errorMessage.includes('Could not resolve host') || errorMessage.includes('network')) {\n throw new Error(\n `Failed to push changes to origin/${branchName}: Network connectivity issues\\n\\n` +\n ` Git error: ${errorMessage}\\n\\n` +\n ` Check your internet connection and try again.`\n )\n }\n\n if (errorMessage.includes('No such remote')) {\n throw new Error(\n `Failed to push changes: Remote 'origin' not found\\n\\n` +\n ` Git error: ${errorMessage}\\n\\n` +\n ` Configure remote: git remote add origin <url>`\n )\n }\n\n // For other errors, re-throw with original message\n throw new Error(`Failed to push to remote: ${errorMessage}`)\n }\n}\n\n/**\n * Fetch from origin to ensure we have latest refs\n * Used before branching and rebasing operations to stay current with remote\n *\n * @param cwd - Working directory to run git command in\n * @throws Error if fetch fails (network issues, access denied, etc.)\n */\nexport async function fetchOrigin(cwd: string): Promise<void> {\n try {\n await executeGitCommand(['fetch', 'origin'], {\n cwd,\n timeout: 60000, // 60 second timeout for fetch operations\n })\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n throw new Error(\n `Failed to fetch from origin: ${errorMessage}\\n\\n` +\n `Check your network connection and repository access.`\n )\n }\n}\n\n/**\n * Check if a file is tracked by git\n * Uses git ls-files to check if file is in the index\n * @param filePath - Absolute or relative path to the file\n * @param cwd - Working directory (defaults to process.cwd())\n * @returns true if file is tracked, false otherwise\n */\nexport async function isFileTrackedByGit(\n filePath: string,\n cwd: string = process.cwd()\n): Promise<boolean> {\n try {\n const result = await executeGitCommand(\n ['ls-files', '--error-unmatch', filePath],\n { cwd }\n )\n return result.trim().length > 0\n } catch (error) {\n // Only return false if it's the specific \"pathspec did not match\" error\n const errorMessage = error instanceof Error ? error.message : String(error)\n if (errorMessage.includes('pathspec') && errorMessage.includes('did not match')) {\n return false\n }\n // Re-throw other errors\n throw error\n }\n}\n\n/**\n * Check if a file is gitignored\n * Uses `git check-ignore` which handles nested gitignore files and global patterns\n *\n * @param filePath - Path to file to check (relative to repo root)\n * @param cwd - Working directory (defaults to process.cwd())\n * @returns true if file IS ignored, false if NOT ignored or on error\n */\nexport async function isFileGitignored(\n filePath: string,\n cwd: string = process.cwd()\n): Promise<boolean> {\n try {\n await executeGitCommand(['check-ignore', '-q', filePath], { cwd })\n return true // Exit 0 = file IS ignored\n } catch {\n return false // Exit 1 = NOT ignored (or error)\n }\n}\n\n/**\n * Check if a branch is merged into the main branch\n *\n * Uses `git merge-base --is-ancestor` which is more reliable than `git branch -d`'s check.\n * The `-d` flag checks against current HEAD, which can give false positives when:\n * - Running from a worktree where main isn't checked out\n * - Squash or rebase merges were used\n *\n * This function explicitly checks if the branch tip is an ancestor of the main branch,\n * providing consistent results regardless of which worktree the command runs from.\n *\n * @param branchName - The branch to check\n * @param mainBranch - The main branch to check against (defaults to 'main')\n * @param cwd - Working directory (defaults to process.cwd())\n * @returns true if branch is merged into main, false otherwise\n */\nexport async function isBranchMergedIntoMain(\n branchName: string,\n mainBranch: string = 'main',\n cwd: string = process.cwd()\n): Promise<boolean> {\n try {\n // git merge-base --is-ancestor exits 0 if branchName is ancestor of mainBranch, 1 if not\n await executeGitCommand(['merge-base', '--is-ancestor', branchName, mainBranch], { cwd })\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Check if a branch exists on the remote (origin) and is up-to-date with local\n * Useful for GitHub-PR workflows to ensure branch has been pushed and is current\n *\n * @param branchName - Name of the branch to check\n * @param cwd - Working directory to run git command in\n * @returns Promise<boolean> - true if remote branch exists and matches local HEAD, false otherwise\n */\nexport async function isRemoteBranchUpToDate(\n branchName: string,\n cwd: string\n): Promise<boolean> {\n try {\n // First, check if remote branch exists and get its commit hash\n const remoteResult = await executeGitCommand(['ls-remote', '--heads', 'origin', branchName], { cwd })\n\n if (remoteResult.trim().length === 0) {\n // Remote branch doesn't exist\n return false\n }\n\n // Extract the commit hash from ls-remote output (format: \"hash\\trefs/heads/branchname\")\n const remoteCommit = remoteResult.trim().split('\\t')[0]\n\n // Get the local branch's HEAD commit\n const localCommit = await executeGitCommand(['rev-parse', branchName], { cwd })\n\n // Both must exist and match\n return localCommit.trim() === remoteCommit\n } catch {\n return false\n }\n}\n\n/**\n * Result of checking remote branch status for safety validation\n */\nexport interface RemoteBranchStatus {\n /** Whether the remote branch exists */\n exists: boolean\n /** Whether the remote is ahead of local (has commits not present locally) */\n remoteAhead: boolean\n /** Whether local is ahead of remote (has unpushed commits) */\n localAhead: boolean\n /** Whether a network error occurred during the check */\n networkError: boolean\n /** Error message if network error occurred */\n errorMessage?: string\n}\n\n/**\n * Check the status of a remote branch for safety validation during cleanup\n * This function provides detailed status needed for the 5-point safety check:\n *\n * The key insight: we care about DATA LOSS, not about remote state\n * - Remote ahead of local is SAFE (commits exist on remote, no data loss)\n * - Local ahead of remote is DANGEROUS (unpushed commits would be lost)\n *\n * 5-point safety logic:\n * 1. Network error -> BLOCK (can't verify safety)\n * 2. Remote ahead of local -> OK (no data loss - commits exist on remote)\n * 3. Local ahead of remote (unpushed commits) -> BLOCK (data loss risk)\n * 4. No remote, merged to main -> OK (work is in main)\n * 5. No remote, NOT merged to main -> BLOCK (unmerged work would be lost)\n *\n * @param branchName - Name of the branch to check\n * @param cwd - Working directory to run git command in\n * @returns Promise<RemoteBranchStatus> - Detailed status of the remote branch\n */\nexport async function checkRemoteBranchStatus(\n branchName: string,\n cwd: string\n): Promise<RemoteBranchStatus> {\n try {\n // First, fetch to ensure we have the latest remote refs\n // This is important to accurately detect if remote is ahead\n try {\n await executeGitCommand(['fetch', 'origin', branchName], { cwd, timeout: 30000 })\n } catch (fetchError) {\n // Fetch failing for a specific branch is OK - branch might not exist on remote\n // We'll detect this in the ls-remote call\n const fetchErrorMessage = fetchError instanceof Error ? fetchError.message : String(fetchError)\n\n // Check if this is a network error vs branch not found\n if (fetchErrorMessage.includes('Could not resolve host') ||\n fetchErrorMessage.includes('unable to access') ||\n fetchErrorMessage.includes('network') ||\n fetchErrorMessage.includes('Connection refused') ||\n fetchErrorMessage.includes('Connection timed out')) {\n return {\n exists: false,\n remoteAhead: false,\n localAhead: false,\n networkError: true,\n errorMessage: fetchErrorMessage\n }\n }\n // Otherwise continue - branch might just not exist on remote\n }\n\n // Check if remote branch exists using ls-remote\n const remoteResult = await executeGitCommand(['ls-remote', '--heads', 'origin', branchName], { cwd })\n\n if (remoteResult.trim().length === 0) {\n // Remote branch doesn't exist\n return {\n exists: false,\n remoteAhead: false,\n localAhead: false,\n networkError: false\n }\n }\n\n // Remote branch exists - check if it's ahead of local\n // Extract the remote commit hash\n const remoteCommit = remoteResult.trim().split('\\t')[0]\n\n // Guard against undefined (shouldn't happen but TypeScript wants it)\n if (!remoteCommit) {\n return {\n exists: false,\n remoteAhead: false,\n localAhead: false,\n networkError: false\n }\n }\n\n // Get the local branch's HEAD commit\n const localCommit = await executeGitCommand(['rev-parse', branchName], { cwd })\n const localCommitTrimmed = localCommit.trim()\n\n if (remoteCommit === localCommitTrimmed) {\n // Remote and local are at the same commit - safe (no unpushed commits)\n return {\n exists: true,\n remoteAhead: false,\n localAhead: false,\n networkError: false\n }\n }\n\n // Commits differ - check if remote is ahead of local\n // Use merge-base to find common ancestor, then compare\n try {\n // Check if localCommit is an ancestor of remoteCommit (meaning remote is ahead)\n await executeGitCommand(['merge-base', '--is-ancestor', localCommitTrimmed, remoteCommit], { cwd })\n // If we get here, local IS an ancestor of remote, meaning remote is ahead\n // This is SAFE - no data loss because commits exist on remote\n return {\n exists: true,\n remoteAhead: true,\n localAhead: false,\n networkError: false\n }\n } catch {\n // Local is NOT an ancestor of remote\n // This means local is ahead or branches have diverged\n // Either way, local has unpushed commits - this is DANGEROUS (data loss risk)\n return {\n exists: true,\n remoteAhead: false,\n localAhead: true,\n networkError: false\n }\n }\n } catch (error) {\n // Check if this is a network error\n const errorMessage = error instanceof Error ? error.message : String(error)\n\n if (errorMessage.includes('Could not resolve host') ||\n errorMessage.includes('unable to access') ||\n errorMessage.includes('network') ||\n errorMessage.includes('Connection refused') ||\n errorMessage.includes('Connection timed out')) {\n return {\n exists: false,\n remoteAhead: false,\n localAhead: false,\n networkError: true,\n errorMessage\n }\n }\n\n // For other errors, assume remote doesn't exist\n return {\n exists: false,\n remoteAhead: false,\n localAhead: false,\n networkError: false\n }\n }\n}\n\n/**\n * Get the merge target branch for a loom\n * Priority: parent loom metadata (parentLoom.branchName) > configured main branch > 'main'\n *\n * This is the shared utility for determining where a branch should merge to.\n * Child looms merge to their parent branch, standalone looms merge to main.\n *\n * @param worktreePath - Path to load metadata/settings from (defaults to process.cwd())\n * @param options - Optional dependency injection for testing\n * @returns The branch name to merge into\n */\nexport async function getMergeTargetBranch(\n worktreePath: string = process.cwd(),\n options?: {\n settingsManager?: SettingsManagerType\n metadataManager?: MetadataManager\n }\n): Promise<string> {\n const settingsManager = options?.settingsManager ?? new SettingsManager()\n const metadataManager = options?.metadataManager ?? new MetadataManager()\n\n // Check for parent loom metadata first (child looms merge to parent)\n logger.debug(`Checking for parent loom metadata at: ${worktreePath}`)\n const metadata = await metadataManager.readMetadata(worktreePath)\n if (metadata?.parentLoom?.branchName) {\n logger.debug(`Using parent branch as merge target: ${metadata.parentLoom.branchName}`)\n return metadata.parentLoom.branchName\n }\n logger.debug('No parent loom metadata found, falling back to settings')\n\n // Fall back to configured main branch\n const settings = await settingsManager.loadSettings(worktreePath)\n const mainBranch = settings.mainBranch ?? 'main'\n logger.debug(`Using configured main branch as merge target: ${mainBranch}`)\n return mainBranch\n}\n\n/**\n * Placeholder commit prefix used by github-draft-pr mode.\n * Created during il start to enable draft PR creation (GitHub requires at least one commit ahead of base).\n * Removed during il finish before the final push to maintain clean commit history.\n */\nexport const PLACEHOLDER_COMMIT_PREFIX = '[iloom]'\n\n/**\n * Check if HEAD commit is a placeholder commit\n * @param cwd - Working directory (defaults to process.cwd())\n * @returns true if HEAD is a placeholder commit\n */\nexport async function isPlaceholderCommit(cwd: string = process.cwd()): Promise<boolean> {\n try {\n const subject = await executeGitCommand(['log', '-1', '--format=%s', 'HEAD'], { cwd })\n return subject.trim().startsWith(PLACEHOLDER_COMMIT_PREFIX)\n } catch {\n // No HEAD (empty repo) or other error - not a placeholder\n return false\n }\n}\n\n/**\n * Find placeholder commit SHA in history using git log --grep\n * @param worktreePath - Working directory\n * @returns SHA of placeholder commit if found, null otherwise\n */\nexport async function findPlaceholderCommitSha(worktreePath: string): Promise<string | null> {\n try {\n // Search commit history for placeholder prefix\n // Use --fixed-strings to treat the pattern literally (brackets are regex special chars)\n const log = await executeGitCommand(\n ['log', '--format=%H', '--fixed-strings', '--grep', PLACEHOLDER_COMMIT_PREFIX, '-n', '1'],\n { cwd: worktreePath }\n )\n const sha = log.trim()\n if (sha.length === 0) {\n return null\n }\n\n // Verify the found commit actually has the placeholder prefix in its subject\n // This guards against git grep matching in commit body instead of subject\n const subject = await executeGitCommand(\n ['log', '-1', '--format=%s', sha],\n { cwd: worktreePath }\n )\n if (!subject.trim().startsWith(PLACEHOLDER_COMMIT_PREFIX)) {\n return null\n }\n\n return sha\n } catch {\n return null\n }\n}\n\n/**\n * Remove placeholder commit when it's HEAD\n * Uses soft reset to preserve any staged changes\n * @param worktreePath - Working directory\n * @returns true if placeholder was removed\n */\nexport async function removePlaceholderCommitFromHead(worktreePath: string): Promise<boolean> {\n if (!await isPlaceholderCommit(worktreePath)) {\n return false\n }\n await executeGitCommand(['reset', '--soft', 'HEAD~1'], { cwd: worktreePath })\n return true\n}\n\n/**\n * Remove placeholder commit from history using rebase\n * Used when user has made commits on top of placeholder\n * @param worktreePath - Working directory\n * @param placeholderSha - SHA of the placeholder commit to remove\n * @throws Error if rebase fails\n */\nexport async function removePlaceholderCommitFromHistory(\n worktreePath: string,\n placeholderSha: string\n): Promise<void> {\n // Get parent of placeholder commit\n const parentSha = await executeGitCommand(\n ['rev-parse', `${placeholderSha}^`],\n { cwd: worktreePath }\n )\n\n // Rebase to drop the placeholder: rebase --onto parent^ placeholder\n await executeGitCommand(\n ['rebase', '--onto', parentSha.trim(), placeholderSha],\n { cwd: worktreePath }\n )\n}\n"],"mappings":"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","names":["path"]}
1
+ {"version":3,"sources":["../src/utils/git.ts"],"sourcesContent":["import path from 'path'\nimport { execa, type ExecaError } from 'execa'\nimport { type GitWorktree } from '../types/worktree.js'\nimport { SettingsManager, type SettingsManager as SettingsManagerType } from '../lib/SettingsManager.js'\nimport { MetadataManager } from '../lib/MetadataManager.js'\nimport { logger } from './logger.js'\n\n/**\n * Custom error class for Git command failures\n * Preserves exit code and stderr for precise error handling\n */\nexport class GitCommandError extends Error {\n constructor(\n message: string,\n public readonly exitCode: number | undefined,\n public readonly stderr: string\n ) {\n super(message)\n this.name = 'GitCommandError'\n }\n}\n\n/**\n * Execute a Git command and return the stdout result\n * Throws a GitCommandError if the command fails\n */\nexport async function executeGitCommand(\n args: string[],\n options?: { cwd?: string; timeout?: number; stdio?: 'inherit' | 'pipe'; env?: NodeJS.ProcessEnv }\n): Promise<string> {\n try {\n const result = await execa('git', args, {\n cwd: options?.cwd ?? process.cwd(),\n timeout: options?.timeout ?? 30000,\n encoding: 'utf8',\n stdio: options?.stdio ?? 'pipe',\n verbose: logger.isDebugEnabled(),\n // Spread env conditionally - only include if defined\n ...(options?.env && { env: options.env }),\n })\n\n return result.stdout\n } catch (error) {\n const execaError = error as ExecaError\n const stderr = execaError.stderr ?? execaError.message ?? 'Unknown Git error'\n throw new GitCommandError(\n `Git command failed: ${stderr}`,\n execaError.exitCode,\n stderr\n )\n }\n}\n\n/**\n * Parse git worktree list output into structured data\n * @param output - The output from git worktree list --porcelain\n * @param defaultBranch - Default branch name to use for bare repositories (defaults to 'main')\n */\nexport function parseWorktreeList(output: string, defaultBranch?: string): GitWorktree[] {\n const worktrees: GitWorktree[] = []\n const lines = output.trim().split('\\n')\n\n let i = 0\n while (i < lines.length) {\n const pathLine = lines[i]\n if (!pathLine?.startsWith('worktree ')) {\n i++\n continue\n }\n\n // Parse path line: \"worktree /path/to/worktree\"\n const pathMatch = pathLine.match(/^worktree (.+)$/)\n if (!pathMatch) {\n i++\n continue\n }\n\n let branch = ''\n let commit = ''\n let detached = false\n let bare = false\n let locked = false\n let lockReason: string | undefined\n\n // Process subsequent lines for this worktree\n i++\n while (i < lines.length && !lines[i]?.startsWith('worktree ')) {\n const line = lines[i]?.trim()\n if (!line) {\n i++\n continue\n }\n\n if (line === 'bare') {\n bare = true\n branch = defaultBranch ?? 'main' // Default assumption for bare repo\n } else if (line === 'detached') {\n detached = true\n branch = 'HEAD'\n } else if (line.startsWith('locked')) {\n locked = true\n const lockMatch = line.match(/^locked (.+)$/)\n lockReason = lockMatch?.[1]\n branch = branch || 'unknown'\n } else if (line.startsWith('HEAD ')) {\n // Parse commit line: \"HEAD abc123def456...\"\n const commitMatch = line.match(/^HEAD ([a-f0-9]+)/)\n if (commitMatch) {\n commit = commitMatch[1] ?? ''\n }\n } else if (line.startsWith('branch ')) {\n // Parse branch line: \"branch refs/heads/feature-branch\"\n const branchMatch = line.match(/^branch refs\\/heads\\/(.+)$/)\n branch = branchMatch?.[1] ?? line.replace('branch ', '')\n }\n\n i++\n }\n\n const worktree: GitWorktree = {\n path: pathMatch[1] ?? '',\n branch,\n commit,\n bare,\n detached,\n locked,\n }\n\n if (lockReason !== undefined) {\n worktree.lockReason = lockReason\n }\n\n worktrees.push(worktree)\n }\n\n return worktrees\n}\n\n/**\n * Check if a branch name follows PR naming patterns\n */\nexport function isPRBranch(branchName: string): boolean {\n const prPatterns = [\n /^pr\\/\\d+/i, // pr/123, pr/123-feature-name\n /^pull\\/\\d+/i, // pull/123\n /^\\d+[-_]/, // 123-feature-name, 123_feature_name\n /^feature\\/pr[-_]?\\d+/i, // feature/pr123, feature/pr-123\n /^hotfix\\/pr[-_]?\\d+/i, // hotfix/pr123\n ]\n\n return prPatterns.some(pattern => pattern.test(branchName))\n}\n\n/**\n * Extract PR number from branch name\n */\nexport function extractPRNumber(branchName: string): number | null {\n const patterns = [\n /^pr\\/(\\d+)/i, // pr/123\n /^pull\\/(\\d+)/i, // pull/123\n /^(\\d+)[-_]/, // 123-feature-name\n /^feature\\/pr[-_]?(\\d+)/i, // feature/pr123\n /^hotfix\\/pr[-_]?(\\d+)/i, // hotfix/pr123\n /pr[-_]?(\\d+)/i, // anywhere with pr123 or pr-123\n ]\n\n for (const pattern of patterns) {\n const match = branchName.match(pattern)\n if (match?.[1]) {\n const num = parseInt(match[1], 10)\n if (!isNaN(num)) return num\n }\n }\n\n return null\n}\n\n/**\n * Extract issue number from branch name\n * Supports both new format (issue-{issueId}__{slug}) and old format (issue-{number}-{slug})\n * @returns string issue ID (alphanumeric) or null if not found\n */\nexport function extractIssueNumber(branchName: string): string | null {\n // Priority 1: New format - issue-{issueId}__ (alphanumeric ID with double underscore)\n const newFormatPattern = /issue-([^_]+)__/i\n const newMatch = branchName.match(newFormatPattern)\n if (newMatch?.[1]) return newMatch[1]\n\n // Priority 2: Old format - issue-{number}- or issue-{number}$ (numeric only, dash or end)\n const oldFormatPattern = /issue-(\\d+)(?:-|$)/i\n const oldMatch = branchName.match(oldFormatPattern)\n if (oldMatch?.[1]) return oldMatch[1]\n\n // Priority 3: Alphanumeric ID at end (either format without description)\n const alphanumericEndPattern = /issue-([^_\\s/]+)$/i\n const alphanumericMatch = branchName.match(alphanumericEndPattern)\n if (alphanumericMatch?.[1]) return alphanumericMatch[1]\n\n // Priority 4: Legacy patterns (issue_N, leading number)\n const legacyPatterns = [\n /issue_(\\d+)/i, // issue_42\n /^(\\d+)-/, // 42-feature-name\n ]\n for (const pattern of legacyPatterns) {\n const match = branchName.match(pattern)\n if (match?.[1]) return match[1]\n }\n\n return null\n}\n\n/**\n * Check if a path follows worktree naming patterns\n */\nexport function isWorktreePath(path: string): boolean {\n const worktreePatterns = [\n /\\/worktrees?\\//i, // Contains /worktree/ or /worktrees/\n /\\/workspace[-_]?\\d+/i, // workspace123, workspace-123\n /\\/issue[-_]?\\d+/i, // issue123, issue-123\n /\\/pr[-_]?\\d+/i, // pr123, pr-123\n /-worktree$/i, // ends with -worktree\n /\\.worktree$/i, // ends with .worktree\n ]\n\n return worktreePatterns.some(pattern => pattern.test(path))\n}\n\n/**\n * Generate a worktree path based on branch name and root directory\n * For PRs, adds _pr_<PR_NUM> suffix to distinguish from issue branches\n */\nexport function generateWorktreePath(\n branchName: string,\n rootDir: string = process.cwd(),\n options?: { isPR?: boolean; prNumber?: number; prefix?: string }\n): string {\n // Replace slashes with dashes (matches bash line 593)\n let sanitized = branchName.replace(/\\//g, '-')\n\n // Add PR suffix if this is a PR (matches bash lines 595-597)\n if (options?.isPR && options?.prNumber) {\n sanitized = `${sanitized}_pr_${options.prNumber}`\n }\n\n const parentDir = path.dirname(rootDir)\n\n // Handle prefix logic\n let prefix: string\n\n if (options?.prefix === undefined) {\n // No prefix in options - calculate default: <basename>-looms\n const mainFolderName = path.basename(rootDir)\n prefix = mainFolderName ? `${mainFolderName}-looms/` : 'looms/'\n } else if (options.prefix === '') {\n // Empty string = no prefix mode\n prefix = ''\n } else {\n // Custom prefix provided\n prefix = options.prefix\n\n // Check if prefix contains forward slashes (nested directory structure)\n const hasNestedPath = prefix.includes('/')\n\n if (hasNestedPath) {\n // Check if it ends with a separator character (dash, underscore, or slash)\n const endsWithSeparator = /[-_/]$/.test(prefix)\n\n if (!endsWithSeparator) {\n // Has nested path but no trailing separator: auto-append hyphen\n // Example: \"temp/looms\" becomes \"temp/looms-\"\n prefix = `${prefix}-`\n }\n // If it already ends with -, _, or /, keep as-is\n } else {\n // Single-level prefix: auto-append separator if it doesn't end with one\n const endsWithSeparator = /[-_]$/.test(prefix)\n if (!endsWithSeparator) {\n prefix = `${prefix}-`\n }\n }\n }\n\n // Apply prefix (or not, if empty)\n if (prefix === '') {\n return path.join(parentDir, sanitized)\n } else if (prefix.endsWith('/')) {\n // Forward slash = nested directory, use path.join for proper handling\n return path.join(parentDir, prefix, sanitized)\n } else if (prefix.includes('/')) {\n // Contains slash but doesn't end with slash = nested with separator (e.g., \"looms/myprefix-\")\n // Split and handle: last part is prefix with separator, rest is directory path\n const lastSlashIndex = prefix.lastIndexOf('/')\n const dirPath = prefix.substring(0, lastSlashIndex)\n const prefixWithSeparator = prefix.substring(lastSlashIndex + 1)\n return path.join(parentDir, dirPath, `${prefixWithSeparator}${sanitized}`)\n } else {\n // Dash/underscore separator = single directory name\n return path.join(parentDir, `${prefix}${sanitized}`)\n }\n}\n\n/**\n * Validate that a directory is a valid Git repository\n */\nexport async function isValidGitRepo(path: string): Promise<boolean> {\n try {\n await executeGitCommand(['rev-parse', '--git-dir'], { cwd: path })\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Get the current branch name for a repository\n */\nexport async function getCurrentBranch(path: string = process.cwd()): Promise<string | null> {\n try {\n const result = await executeGitCommand(['branch', '--show-current'], { cwd: path })\n return result.trim()\n } catch {\n return null\n }\n}\n\n/**\n * Check if a branch exists (local or remote)\n */\nexport async function branchExists(\n branchName: string,\n path: string = process.cwd(),\n includeRemote = true\n): Promise<boolean> {\n try {\n // Check local branches\n const localResult = await executeGitCommand(['branch', '--list', branchName], { cwd: path })\n if (localResult.trim()) {\n return true\n }\n\n // Check remote branches if requested\n if (includeRemote) {\n const remoteResult = await executeGitCommand(['branch', '-r', '--list', `*/${branchName}`], {\n cwd: path,\n })\n if (remoteResult.trim()) {\n return true\n }\n }\n\n return false\n } catch {\n return false\n }\n}\n\n/**\n * Get the root directory of the current worktree\n * Returns the worktree root when in a linked worktree, or main repo root when in main worktree\n */\nexport async function getWorktreeRoot(path: string = process.cwd()): Promise<string | null> {\n try {\n const result = await executeGitCommand(['rev-parse', '--show-toplevel'], { cwd: path })\n return result.trim()\n } catch {\n return null\n }\n}\n\n/**\n * Get the main repository root directory\n * Returns the main repo root even when called from a linked worktree\n */\nexport async function getRepoRoot(path: string = process.cwd()): Promise<string | null> {\n try {\n // Get the common git directory (shared by all worktrees)\n const gitCommonDir = await executeGitCommand(\n ['rev-parse', '--path-format=absolute', '--git-common-dir'],\n { cwd: path }\n )\n const trimmedPath = gitCommonDir.trim()\n\n // Handle linked worktree: /path/to/repo/.git/worktrees/worktree-name -> /path/to/repo\n // Handle main worktree: /path/to/repo/.git -> /path/to/repo\n const repoRoot = trimmedPath\n .replace(/\\/\\.git\\/worktrees\\/[^/]+$/, '') // Remove /.git/worktrees/name suffix\n .replace(/\\/\\.git$/, '') // Remove /.git suffix\n\n return repoRoot\n } catch(error) {\n // \"not a git repository\" is expected when running outside a git repo - use debug level\n // Check for GitCommandError with exit code 128 or the specific stderr message\n if (error instanceof GitCommandError &&\n (error.exitCode === 128 || /fatal: not a git repository/i.test(error.stderr))) {\n logger.info(`Note: No git repository detected: ${path}`)\n } else {\n const errorMessage = error instanceof Error ? error.message : String(error)\n logger.warn(`Failed to determine repo root from git-common-dir: ${path}`, errorMessage)\n }\n return null\n }\n}\n\n/**\n * Find the worktree path where main branch is checked out\n * Copies bash script approach: parse git worktree list to find main\n */\nexport async function findMainWorktreePath(\n path: string = process.cwd(),\n options?: { mainBranch?: string }\n): Promise<string> {\n try {\n const output = await executeGitCommand(['worktree', 'list', '--porcelain'], { cwd: path })\n const worktrees = parseWorktreeList(output, options?.mainBranch)\n\n // Guard: empty worktree list\n if (worktrees.length === 0) {\n throw new Error('No worktrees found in repository')\n }\n\n // Tier 1: Check for specified mainBranch in options\n if (options?.mainBranch) {\n const specified = worktrees.find(wt => wt.branch === options.mainBranch)\n if (!specified?.path) {\n throw new Error(\n `No worktree found with branch '${options.mainBranch}' (specified in settings). Available worktrees: ${worktrees.map(wt => `${wt.path} (${wt.branch})`).join(', ')}`\n )\n }\n return specified.path\n }\n\n // Tier 2: Look for \"main\" branch\n const mainBranch = worktrees.find(wt => wt.branch === 'main')\n if (mainBranch?.path) {\n return mainBranch.path\n }\n\n // Tier 3: Use first worktree (primary worktree)\n const firstWorktree = worktrees[0]\n if (!firstWorktree?.path) {\n throw new Error('Failed to determine primary worktree path')\n }\n return firstWorktree.path\n } catch (error) {\n if (\n error instanceof Error &&\n (error.message.includes('No worktree found with branch') ||\n error.message.includes('No worktrees found') ||\n error.message.includes('Failed to determine primary worktree'))\n ) {\n // Re-throw our specific errors\n throw error\n }\n throw new Error(`Failed to find main worktree: ${error instanceof Error ? error.message : String(error)}`)\n }\n}\n\n/**\n * Find main worktree path with automatic settings loading\n *\n * This is a convenience wrapper that:\n * 1. Loads project settings from .iloom/settings.json\n * 2. Extracts mainBranch configuration if present\n * 3. Calls findMainWorktreePath with appropriate options\n *\n * @param path - Path to search from (defaults to process.cwd())\n * @param settingsManager - Optional SettingsManager instance (for DI/testing)\n * @returns Path to main worktree\n * @throws Error if main worktree cannot be found\n */\nexport async function findMainWorktreePathWithSettings(\n path?: string,\n settingsManager?: SettingsManagerType\n): Promise<string> {\n // Lazy load SettingsManager to avoid circular dependencies\n settingsManager ??= new SettingsManager()\n\n const settings = await settingsManager.loadSettings(path)\n const findOptions = settings.mainBranch ? { mainBranch: settings.mainBranch } : undefined\n return findMainWorktreePath(path, findOptions)\n}\n\n/**\n * Find the worktree path where a specific branch is checked out\n *\n * Used by MergeManager to find the correct worktree for child loom merges.\n * When finishing a child loom, we need to find where the PARENT branch is\n * checked out (the merge target), not where settings.mainBranch is checked out.\n *\n * @param branchName - The branch name to find\n * @param path - Path to search from (defaults to process.cwd())\n * @returns Path to worktree where the branch is checked out\n * @throws Error if no worktree has the specified branch checked out\n */\nexport async function findWorktreeForBranch(\n branchName: string,\n path: string = process.cwd()\n): Promise<string> {\n try {\n const output = await executeGitCommand(['worktree', 'list', '--porcelain'], { cwd: path })\n const worktrees = parseWorktreeList(output, branchName)\n\n // Guard: empty worktree list\n if (worktrees.length === 0) {\n throw new Error('No worktrees found in repository')\n }\n\n // Find the worktree with the specified branch\n const targetWorktree = worktrees.find(wt => wt.branch === branchName)\n if (!targetWorktree?.path) {\n throw new Error(\n `No worktree found with branch '${branchName}' checked out. ` +\n `Available worktrees: ${worktrees.map(wt => `${wt.path} (${wt.branch})`).join(', ')}`\n )\n }\n return targetWorktree.path\n } catch (error) {\n if (\n error instanceof Error &&\n (error.message.includes('No worktree found with branch') ||\n error.message.includes('No worktrees found'))\n ) {\n // Re-throw our specific errors\n throw error\n }\n throw new Error(`Failed to find worktree for branch '${branchName}': ${error instanceof Error ? error.message : String(error)}`)\n }\n}\n\n/**\n * Check if there are uncommitted changes in a repository\n */\nexport async function hasUncommittedChanges(path: string = process.cwd()): Promise<boolean> {\n try {\n const result = await executeGitCommand(['status', '--porcelain'], { cwd: path })\n return result.trim().length > 0\n } catch {\n return false\n }\n}\n\n/**\n * Get the default branch name for a repository\n */\nexport async function getDefaultBranch(path: string = process.cwd()): Promise<string> {\n try {\n // Try to get from remote\n const remoteResult = await executeGitCommand(['symbolic-ref', 'refs/remotes/origin/HEAD'], {\n cwd: path,\n })\n const match = remoteResult.match(/refs\\/remotes\\/origin\\/(.+)/)\n if (match) return match[1] ?? 'main'\n\n // Fallback to common default branch names\n const commonDefaults = ['main', 'master', 'develop']\n for (const branch of commonDefaults) {\n if (await branchExists(branch, path)) {\n return branch\n }\n }\n\n return 'main' // Final fallback\n } catch {\n return 'main'\n }\n}\n\n/**\n * Find all branches related to a GitHub issue or PR number\n * Matches patterns like:\n * - Issue patterns: issue-25, issue/25, 25-feature, feat-25, feat/issue-25\n * - PR patterns: pr/25, pull/25, pr-25, feature/pr-25\n *\n * Based on bash cleanup-worktree.sh find_issue_branches() (lines 133-154)\n *\n * @param issueNumber - The issue or PR number to search for\n * @param path - Working directory to search from (defaults to process.cwd())\n * @param settingsManager - Optional SettingsManager instance (for DI/testing)\n */\nexport async function findAllBranchesForIssue(\n issueNumber: string | number,\n path: string = process.cwd(),\n settingsManager?: SettingsManagerType\n): Promise<string[]> {\n // Lazy load SettingsManager to avoid circular dependencies\n if (!settingsManager) {\n const { SettingsManager: SM } = await import('../lib/SettingsManager.js')\n settingsManager = new SM()\n }\n\n // Get protected branches list from centralized method\n const protectedBranches = await settingsManager.getProtectedBranches(path)\n\n // Get all branches (local and remote)\n const output = await executeGitCommand(['branch', '-a'], { cwd: path })\n\n const branches: string[] = []\n const lines = output.split('\\n').filter(Boolean)\n\n for (const line of lines) {\n // Skip remotes/origin/HEAD pointer\n if (line.includes('remotes/origin/HEAD')) {\n continue\n }\n\n // Clean the branch name:\n // 1. Remove git status markers (* + spaces at start)\n let cleanBranch = line.replace(/^[*+ ]+/, '')\n\n // 2. Remove 'origin/' prefix if present\n cleanBranch = cleanBranch.replace(/^origin\\//, '')\n\n // 3. Remove 'remotes/origin/' prefix if present\n cleanBranch = cleanBranch.replace(/^remotes\\/origin\\//, '')\n\n // 4. Trim any remaining whitespace\n cleanBranch = cleanBranch.trim()\n\n // Skip protected branches\n if (protectedBranches.includes(cleanBranch)) {\n continue\n }\n\n // Check if branch contains issue number with strict word boundary pattern\n // The issue number must NOT be:\n // - Part of a larger number (preceded or followed by a digit)\n // - After an unknown word (like \"tissue-25\")\n // The issue number CAN be:\n // - At start: \"25-feature\"\n // - After known prefix + separator: \"issue-25\", \"feat-25\", \"fix-25\", \"pr-25\"\n // - After just a separator with no prefix: test_25 (separator at start)\n\n // First check: not part of a larger number\n const notPartOfNumber = new RegExp(`(?<!\\\\d)${issueNumber}(?!\\\\d)`)\n if (!notPartOfNumber.test(cleanBranch)) {\n continue\n }\n\n // Second check: if preceded by letters, validate they're known issue-related prefixes\n // This prevents \"tissue-25\" but allows \"issue-25\", \"feat-25\", etc.\n const beforeNumber = cleanBranch.substring(0, cleanBranch.indexOf(String(issueNumber)))\n\n if (beforeNumber) {\n // Extract the last word (letters) before the number\n const lastWord = beforeNumber.match(/([a-zA-Z]+)[-_/\\s]*$/)\n if (lastWord?.[1]) {\n const word = lastWord[1].toLowerCase()\n // Known prefixes for issue-related branches\n const knownPrefixes = [\n 'issue', 'issues',\n 'feat', 'feature', 'features',\n 'fix', 'fixes', 'bugfix', 'hotfix',\n 'pr', 'pull',\n 'test', 'tests',\n 'chore',\n 'docs',\n 'refactor',\n 'perf',\n 'style',\n 'ci',\n 'build',\n 'revert'\n ]\n\n // If we found a word and it's NOT in the known list, skip this branch\n if (!knownPrefixes.includes(word)) {\n continue\n }\n }\n }\n\n // Passed all checks - add to results\n if (!branches.includes(cleanBranch)) {\n branches.push(cleanBranch)\n }\n }\n\n return branches\n}\n\n/**\n * Check if a repository is empty (has no commits yet)\n * @param path - Repository path to check (defaults to process.cwd())\n * @returns true if repository has no commits, false otherwise\n */\nexport async function isEmptyRepository(path: string = process.cwd()): Promise<boolean> {\n try {\n await executeGitCommand(['rev-parse', '--verify', 'HEAD'], { cwd: path })\n return false // HEAD exists, repo has commits\n } catch {\n return true // HEAD doesn't exist, repo is empty\n }\n}\n\n/**\n * Ensure repository has at least one commit\n * Creates an initial empty commit if repository is empty\n * @param path - Repository path (defaults to process.cwd())\n */\nexport async function ensureRepositoryHasCommits(path: string = process.cwd()): Promise<void> {\n const isEmpty = await isEmptyRepository(path)\n if (isEmpty) {\n await executeGitCommand(['commit', '--no-verify', '--allow-empty', '-m', 'Initial commit'], { cwd: path })\n }\n}\n\n/**\n * Push a branch to remote repository\n * Used for PR workflow to push changes to remote without merging locally\n *\n * @param branchName - The branch name to push\n * @param worktreePath - The worktree path where the branch is checked out\n * @param options - Push options\n * @throws Error if push fails\n */\nexport async function pushBranchToRemote(\n branchName: string,\n worktreePath: string,\n options?: { dryRun?: boolean }\n): Promise<void> {\n if (options?.dryRun) {\n // In dry-run mode, just log what would be done\n return\n }\n\n try {\n // Execute: git push origin <branch-name>\n // This matches the bash script behavior (merge-and-clean.sh line 359)\n await executeGitCommand(['push', 'origin', branchName], {\n cwd: worktreePath,\n timeout: 120000, // 120 second timeout for push operations\n })\n } catch (error) {\n // Provide helpful error message based on common push failures\n const errorMessage = error instanceof Error ? error.message : String(error)\n\n // Check for common error patterns and provide context, but ALWAYS include original error\n if (errorMessage.includes('failed to push') || errorMessage.includes('rejected')) {\n throw new Error(\n `Failed to push changes to origin/${branchName}\\n\\n` +\n ` Git error: ${errorMessage}\\n\\n` +\n ` Possible causes:\\n` +\n ` • Remote branch was deleted\\n` +\n ` • Push was rejected (non-fast-forward)\\n` +\n ` • Network connectivity issues\\n\\n` +\n ` To retry: il finish --pr <number>\\n` +\n ` To force push: git push origin ${branchName} --force`\n )\n }\n\n if (errorMessage.includes('Could not resolve host') || errorMessage.includes('network')) {\n throw new Error(\n `Failed to push changes to origin/${branchName}: Network connectivity issues\\n\\n` +\n ` Git error: ${errorMessage}\\n\\n` +\n ` Check your internet connection and try again.`\n )\n }\n\n if (errorMessage.includes('No such remote')) {\n throw new Error(\n `Failed to push changes: Remote 'origin' not found\\n\\n` +\n ` Git error: ${errorMessage}\\n\\n` +\n ` Configure remote: git remote add origin <url>`\n )\n }\n\n // For other errors, re-throw with original message\n throw new Error(`Failed to push to remote: ${errorMessage}`)\n }\n}\n\n/**\n * Fetch from origin to ensure we have latest refs\n * Used before branching and rebasing operations to stay current with remote\n *\n * @param cwd - Working directory to run git command in\n * @throws Error if fetch fails (network issues, access denied, etc.)\n */\nexport async function fetchOrigin(cwd: string): Promise<void> {\n try {\n await executeGitCommand(['fetch', 'origin'], {\n cwd,\n timeout: 60000, // 60 second timeout for fetch operations\n })\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n throw new Error(\n `Failed to fetch from origin: ${errorMessage}\\n\\n` +\n `Check your network connection and repository access.`\n )\n }\n}\n\n/**\n * Check if a file is tracked by git\n * Uses git ls-files to check if file is in the index\n * @param filePath - Absolute or relative path to the file\n * @param cwd - Working directory (defaults to process.cwd())\n * @returns true if file is tracked, false otherwise\n */\nexport async function isFileTrackedByGit(\n filePath: string,\n cwd: string = process.cwd()\n): Promise<boolean> {\n try {\n const result = await executeGitCommand(\n ['ls-files', '--error-unmatch', filePath],\n { cwd }\n )\n return result.trim().length > 0\n } catch (error) {\n // Only return false if it's the specific \"pathspec did not match\" error\n const errorMessage = error instanceof Error ? error.message : String(error)\n if (errorMessage.includes('pathspec') && errorMessage.includes('did not match')) {\n return false\n }\n // Re-throw other errors\n throw error\n }\n}\n\n/**\n * Check if a file is gitignored\n * Uses `git check-ignore` which handles nested gitignore files and global patterns\n *\n * @param filePath - Path to file to check (relative to repo root)\n * @param cwd - Working directory (defaults to process.cwd())\n * @returns true if file IS ignored, false if NOT ignored or on error\n */\nexport async function isFileGitignored(\n filePath: string,\n cwd: string = process.cwd()\n): Promise<boolean> {\n try {\n await executeGitCommand(['check-ignore', '-q', filePath], { cwd })\n return true // Exit 0 = file IS ignored\n } catch {\n return false // Exit 1 = NOT ignored (or error)\n }\n}\n\n/**\n * Check if a branch is merged into the main branch\n *\n * Uses `git merge-base --is-ancestor` which is more reliable than `git branch -d`'s check.\n * The `-d` flag checks against current HEAD, which can give false positives when:\n * - Running from a worktree where main isn't checked out\n * - Squash or rebase merges were used\n *\n * This function explicitly checks if the branch tip is an ancestor of the main branch,\n * providing consistent results regardless of which worktree the command runs from.\n *\n * @param branchName - The branch to check\n * @param mainBranch - The main branch to check against (defaults to 'main')\n * @param cwd - Working directory (defaults to process.cwd())\n * @returns true if branch is merged into main, false otherwise\n */\nexport async function isBranchMergedIntoMain(\n branchName: string,\n mainBranch: string = 'main',\n cwd: string = process.cwd()\n): Promise<boolean> {\n try {\n // git merge-base --is-ancestor exits 0 if branchName is ancestor of mainBranch, 1 if not\n await executeGitCommand(['merge-base', '--is-ancestor', branchName, mainBranch], { cwd })\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Check if a branch exists on the remote (origin) and is up-to-date with local\n * Useful for GitHub-PR workflows to ensure branch has been pushed and is current\n *\n * @param branchName - Name of the branch to check\n * @param cwd - Working directory to run git command in\n * @returns Promise<boolean> - true if remote branch exists and matches local HEAD, false otherwise\n */\nexport async function isRemoteBranchUpToDate(\n branchName: string,\n cwd: string\n): Promise<boolean> {\n try {\n // First, check if remote branch exists and get its commit hash\n const remoteResult = await executeGitCommand(['ls-remote', '--heads', 'origin', branchName], { cwd })\n\n if (remoteResult.trim().length === 0) {\n // Remote branch doesn't exist\n return false\n }\n\n // Extract the commit hash from ls-remote output (format: \"hash\\trefs/heads/branchname\")\n const remoteCommit = remoteResult.trim().split('\\t')[0]\n\n // Get the local branch's HEAD commit\n const localCommit = await executeGitCommand(['rev-parse', branchName], { cwd })\n\n // Both must exist and match\n return localCommit.trim() === remoteCommit\n } catch {\n return false\n }\n}\n\n/**\n * Result of checking remote branch status for safety validation\n */\nexport interface RemoteBranchStatus {\n /** Whether the remote branch exists */\n exists: boolean\n /** Whether the remote is ahead of local (has commits not present locally) */\n remoteAhead: boolean\n /** Whether local is ahead of remote (has unpushed commits) */\n localAhead: boolean\n /** Whether a network error occurred during the check */\n networkError: boolean\n /** Error message if network error occurred */\n errorMessage?: string\n}\n\n/**\n * Check the status of a remote branch for safety validation during cleanup\n * This function provides detailed status needed for the 5-point safety check:\n *\n * The key insight: we care about DATA LOSS, not about remote state\n * - Remote ahead of local is SAFE (commits exist on remote, no data loss)\n * - Local ahead of remote is DANGEROUS (unpushed commits would be lost)\n *\n * 5-point safety logic:\n * 1. Network error -> BLOCK (can't verify safety)\n * 2. Remote ahead of local -> OK (no data loss - commits exist on remote)\n * 3. Local ahead of remote (unpushed commits) -> BLOCK (data loss risk)\n * 4. No remote, merged to main -> OK (work is in main)\n * 5. No remote, NOT merged to main -> BLOCK (unmerged work would be lost)\n *\n * @param branchName - Name of the branch to check\n * @param cwd - Working directory to run git command in\n * @returns Promise<RemoteBranchStatus> - Detailed status of the remote branch\n */\nexport async function checkRemoteBranchStatus(\n branchName: string,\n cwd: string\n): Promise<RemoteBranchStatus> {\n try {\n // First, fetch to ensure we have the latest remote refs\n // This is important to accurately detect if remote is ahead\n try {\n await executeGitCommand(['fetch', 'origin', branchName], { cwd, timeout: 30000 })\n } catch (fetchError) {\n // Fetch failing for a specific branch is OK - branch might not exist on remote\n // We'll detect this in the ls-remote call\n const fetchErrorMessage = fetchError instanceof Error ? fetchError.message : String(fetchError)\n\n // Check if this is a network error vs branch not found\n if (fetchErrorMessage.includes('Could not resolve host') ||\n fetchErrorMessage.includes('unable to access') ||\n fetchErrorMessage.includes('network') ||\n fetchErrorMessage.includes('Connection refused') ||\n fetchErrorMessage.includes('Connection timed out')) {\n return {\n exists: false,\n remoteAhead: false,\n localAhead: false,\n networkError: true,\n errorMessage: fetchErrorMessage\n }\n }\n // Otherwise continue - branch might just not exist on remote\n }\n\n // Check if remote branch exists using ls-remote\n const remoteResult = await executeGitCommand(['ls-remote', '--heads', 'origin', branchName], { cwd })\n\n if (remoteResult.trim().length === 0) {\n // Remote branch doesn't exist\n return {\n exists: false,\n remoteAhead: false,\n localAhead: false,\n networkError: false\n }\n }\n\n // Remote branch exists - check if it's ahead of local\n // Extract the remote commit hash\n const remoteCommit = remoteResult.trim().split('\\t')[0]\n\n // Guard against undefined (shouldn't happen but TypeScript wants it)\n if (!remoteCommit) {\n return {\n exists: false,\n remoteAhead: false,\n localAhead: false,\n networkError: false\n }\n }\n\n // Get the local branch's HEAD commit\n const localCommit = await executeGitCommand(['rev-parse', branchName], { cwd })\n const localCommitTrimmed = localCommit.trim()\n\n if (remoteCommit === localCommitTrimmed) {\n // Remote and local are at the same commit - safe (no unpushed commits)\n return {\n exists: true,\n remoteAhead: false,\n localAhead: false,\n networkError: false\n }\n }\n\n // Commits differ - check if remote is ahead of local\n // Use merge-base to find common ancestor, then compare\n try {\n // Check if localCommit is an ancestor of remoteCommit (meaning remote is ahead)\n await executeGitCommand(['merge-base', '--is-ancestor', localCommitTrimmed, remoteCommit], { cwd })\n // If we get here, local IS an ancestor of remote, meaning remote is ahead\n // This is SAFE - no data loss because commits exist on remote\n return {\n exists: true,\n remoteAhead: true,\n localAhead: false,\n networkError: false\n }\n } catch {\n // Local is NOT an ancestor of remote\n // This means local is ahead or branches have diverged\n // Either way, local has unpushed commits - this is DANGEROUS (data loss risk)\n return {\n exists: true,\n remoteAhead: false,\n localAhead: true,\n networkError: false\n }\n }\n } catch (error) {\n // Check if this is a network error\n const errorMessage = error instanceof Error ? error.message : String(error)\n\n if (errorMessage.includes('Could not resolve host') ||\n errorMessage.includes('unable to access') ||\n errorMessage.includes('network') ||\n errorMessage.includes('Connection refused') ||\n errorMessage.includes('Connection timed out')) {\n return {\n exists: false,\n remoteAhead: false,\n localAhead: false,\n networkError: true,\n errorMessage\n }\n }\n\n // For other errors, assume remote doesn't exist\n return {\n exists: false,\n remoteAhead: false,\n localAhead: false,\n networkError: false\n }\n }\n}\n\n/**\n * Get the merge target branch for a loom\n * Priority: parent loom metadata (parentLoom.branchName) > configured main branch > 'main'\n *\n * This is the shared utility for determining where a branch should merge to.\n * Child looms merge to their parent branch, standalone looms merge to main.\n *\n * @param worktreePath - Path to load metadata/settings from (defaults to process.cwd())\n * @param options - Optional dependency injection for testing\n * @returns The branch name to merge into\n */\nexport async function getMergeTargetBranch(\n worktreePath: string = process.cwd(),\n options?: {\n settingsManager?: SettingsManagerType\n metadataManager?: MetadataManager\n }\n): Promise<string> {\n const settingsManager = options?.settingsManager ?? new SettingsManager()\n const metadataManager = options?.metadataManager ?? new MetadataManager()\n\n // Check for parent loom metadata first (child looms merge to parent)\n logger.debug(`Checking for parent loom metadata at: ${worktreePath}`)\n const metadata = await metadataManager.readMetadata(worktreePath)\n if (metadata?.parentLoom?.branchName) {\n logger.debug(`Using parent branch as merge target: ${metadata.parentLoom.branchName}`)\n return metadata.parentLoom.branchName\n }\n logger.debug('No parent loom metadata found, falling back to settings')\n\n // Fall back to configured main branch\n const settings = await settingsManager.loadSettings(worktreePath)\n const mainBranch = settings.mainBranch ?? 'main'\n logger.debug(`Using configured main branch as merge target: ${mainBranch}`)\n return mainBranch\n}\n\n/**\n * Placeholder commit prefix used by draft-pr mode.\n * Created during il start to enable draft PR creation (GitHub requires at least one commit ahead of base).\n * Removed during il finish before the final push to maintain clean commit history.\n */\nexport const PLACEHOLDER_COMMIT_PREFIX = '[iloom]'\n\n/**\n * Check if HEAD commit is a placeholder commit\n * @param cwd - Working directory (defaults to process.cwd())\n * @returns true if HEAD is a placeholder commit\n */\nexport async function isPlaceholderCommit(cwd: string = process.cwd()): Promise<boolean> {\n try {\n const subject = await executeGitCommand(['log', '-1', '--format=%s', 'HEAD'], { cwd })\n return subject.trim().startsWith(PLACEHOLDER_COMMIT_PREFIX)\n } catch {\n // No HEAD (empty repo) or other error - not a placeholder\n return false\n }\n}\n\n/**\n * Find placeholder commit SHA in history using git log --grep\n * @param worktreePath - Working directory\n * @returns SHA of placeholder commit if found, null otherwise\n */\nexport async function findPlaceholderCommitSha(worktreePath: string): Promise<string | null> {\n try {\n // Search commit history for placeholder prefix\n // Use --fixed-strings to treat the pattern literally (brackets are regex special chars)\n const log = await executeGitCommand(\n ['log', '--format=%H', '--fixed-strings', '--grep', PLACEHOLDER_COMMIT_PREFIX, '-n', '1'],\n { cwd: worktreePath }\n )\n const sha = log.trim()\n if (sha.length === 0) {\n return null\n }\n\n // Verify the found commit actually has the placeholder prefix in its subject\n // This guards against git grep matching in commit body instead of subject\n const subject = await executeGitCommand(\n ['log', '-1', '--format=%s', sha],\n { cwd: worktreePath }\n )\n if (!subject.trim().startsWith(PLACEHOLDER_COMMIT_PREFIX)) {\n return null\n }\n\n return sha\n } catch {\n return null\n }\n}\n\n/**\n * Remove placeholder commit when it's HEAD\n * Uses soft reset to preserve any staged changes\n * @param worktreePath - Working directory\n * @returns true if placeholder was removed\n */\nexport async function removePlaceholderCommitFromHead(worktreePath: string): Promise<boolean> {\n if (!await isPlaceholderCommit(worktreePath)) {\n return false\n }\n await executeGitCommand(['reset', '--soft', 'HEAD~1'], { cwd: worktreePath })\n return true\n}\n\n/**\n * Remove placeholder commit from history using rebase\n * Used when user has made commits on top of placeholder\n * @param worktreePath - Working directory\n * @param placeholderSha - SHA of the placeholder commit to remove\n * @throws Error if rebase fails\n */\nexport async function removePlaceholderCommitFromHistory(\n worktreePath: string,\n placeholderSha: string\n): Promise<void> {\n // Get parent of placeholder commit\n const parentSha = await executeGitCommand(\n ['rev-parse', `${placeholderSha}^`],\n { cwd: worktreePath }\n )\n\n // Rebase to drop the placeholder: rebase --onto parent^ placeholder\n await executeGitCommand(\n ['rebase', '--onto', parentSha.trim(), placeholderSha],\n { cwd: worktreePath }\n )\n}\n"],"mappings":"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","names":["path"]}