@iloom/cli 0.6.1 → 0.7.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 (177) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +35 -18
  3. package/dist/{BranchNamingService-B5PVRR7F.js → BranchNamingService-FLPUUFOB.js} +2 -2
  4. package/dist/ClaudeContextManager-KE5TBZVZ.js +14 -0
  5. package/dist/ClaudeService-CRSETT3A.js +13 -0
  6. package/dist/{GitHubService-S2OGUTDR.js → GitHubService-O7U4UQ7N.js} +3 -3
  7. package/dist/{LoomLauncher-5LFM4LXB.js → LoomLauncher-NL65LSKP.js} +6 -6
  8. package/dist/{MetadataManager-DFI73J3G.js → MetadataManager-XJ2YB762.js} +2 -2
  9. package/dist/PRManager-2ABCWXHW.js +16 -0
  10. package/dist/{ProjectCapabilityDetector-S5FLNCFI.js → ProjectCapabilityDetector-UZYW32SY.js} +3 -3
  11. package/dist/{PromptTemplateManager-C3DK6XZL.js → PromptTemplateManager-7L3HJQQU.js} +2 -2
  12. package/dist/README.md +35 -18
  13. package/dist/{SettingsManager-35F5RUJH.js → SettingsManager-YU4VYPTW.js} +2 -2
  14. package/dist/agents/iloom-issue-analyze-and-plan.md +42 -17
  15. package/dist/agents/iloom-issue-analyzer.md +14 -14
  16. package/dist/agents/iloom-issue-complexity-evaluator.md +38 -15
  17. package/dist/agents/iloom-issue-enhancer.md +15 -15
  18. package/dist/agents/iloom-issue-implementer.md +44 -15
  19. package/dist/agents/iloom-issue-planner.md +121 -17
  20. package/dist/agents/iloom-issue-reviewer.md +15 -15
  21. package/dist/{build-FJVYP7EV.js → build-O2EJHDEW.js} +9 -9
  22. package/dist/{chunk-ZPSTA5PR.js → chunk-3CDWFEGL.js} +2 -2
  23. package/dist/{chunk-VU3QMIP2.js → chunk-453NC377.js} +91 -15
  24. package/dist/chunk-453NC377.js.map +1 -0
  25. package/dist/{chunk-UQIXZ3BA.js → chunk-5V74K5ZA.js} +2 -2
  26. package/dist/{chunk-7WANFUIK.js → chunk-6TL3BYH6.js} +2 -2
  27. package/dist/{chunk-5TXLVEXT.js → chunk-C3AKFAIR.js} +2 -2
  28. package/dist/{chunk-K7SEEHKO.js → chunk-CNSTXBJ3.js} +7 -419
  29. package/dist/chunk-CNSTXBJ3.js.map +1 -0
  30. package/dist/{chunk-VDA5JMB4.js → chunk-EPPPDVHD.js} +21 -8
  31. package/dist/chunk-EPPPDVHD.js.map +1 -0
  32. package/dist/{chunk-LVBRMTE6.js → chunk-FEAJR6PN.js} +6 -6
  33. package/dist/{chunk-6YSFTPKW.js → chunk-FM4KBPVA.js} +18 -13
  34. package/dist/chunk-FM4KBPVA.js.map +1 -0
  35. package/dist/{chunk-AEIMYF4P.js → chunk-FP7G7DG3.js} +6 -2
  36. package/dist/chunk-FP7G7DG3.js.map +1 -0
  37. package/dist/{chunk-LT3SGBR7.js → chunk-GCPAZSGV.js} +36 -2
  38. package/dist/{chunk-LT3SGBR7.js.map → chunk-GCPAZSGV.js.map} +1 -1
  39. package/dist/chunk-GJMEKEI5.js +517 -0
  40. package/dist/chunk-GJMEKEI5.js.map +1 -0
  41. package/dist/{chunk-64O2UIWO.js → chunk-GV5X6XUE.js} +4 -4
  42. package/dist/{chunk-7Q66W4OH.js → chunk-HBJITKSZ.js} +37 -1
  43. package/dist/chunk-HBJITKSZ.js.map +1 -0
  44. package/dist/{chunk-7HIRPCKU.js → chunk-HVQNVRAF.js} +2 -2
  45. package/dist/{chunk-BXCPJJYM.js → chunk-ITN64ENQ.js} +1 -1
  46. package/dist/chunk-ITN64ENQ.js.map +1 -0
  47. package/dist/{chunk-6U6VI4SZ.js → chunk-KVS4XGBQ.js} +4 -4
  48. package/dist/{chunk-AXX3QIKK.js → chunk-LLWX3PCW.js} +2 -2
  49. package/dist/{chunk-2A7WQKBE.js → chunk-LQBLDI47.js} +96 -6
  50. package/dist/chunk-LQBLDI47.js.map +1 -0
  51. package/dist/{chunk-SN3Z6EZO.js → chunk-N7FVXZNI.js} +2 -2
  52. package/dist/chunk-NTIZLX42.js +822 -0
  53. package/dist/chunk-NTIZLX42.js.map +1 -0
  54. package/dist/{chunk-I75JMBNB.js → chunk-S7YMZQUD.js} +31 -43
  55. package/dist/chunk-S7YMZQUD.js.map +1 -0
  56. package/dist/chunk-TIYJEEVO.js +79 -0
  57. package/dist/chunk-TIYJEEVO.js.map +1 -0
  58. package/dist/{chunk-EK3XCAAS.js → chunk-UDRZY65Y.js} +2 -2
  59. package/dist/{chunk-3PT7RKL5.js → chunk-USJSNHGG.js} +2 -2
  60. package/dist/{chunk-CFUWQHCJ.js → chunk-VWGKGNJP.js} +114 -35
  61. package/dist/chunk-VWGKGNJP.js.map +1 -0
  62. package/dist/{chunk-F6WVM437.js → chunk-WFQ5CLTR.js} +6 -3
  63. package/dist/chunk-WFQ5CLTR.js.map +1 -0
  64. package/dist/{chunk-TRQ76ISK.js → chunk-Z6BO53V7.js} +9 -9
  65. package/dist/{chunk-GEXP5IOF.js → chunk-ZA575VLF.js} +21 -8
  66. package/dist/chunk-ZA575VLF.js.map +1 -0
  67. package/dist/{claude-H33OQMXO.js → claude-6H36IBHO.js} +4 -2
  68. package/dist/{cleanup-BRUAINKE.js → cleanup-ZPOMRSNN.js} +20 -16
  69. package/dist/cleanup-ZPOMRSNN.js.map +1 -0
  70. package/dist/cli.js +341 -954
  71. package/dist/cli.js.map +1 -1
  72. package/dist/commit-6S2RIA2K.js +237 -0
  73. package/dist/commit-6S2RIA2K.js.map +1 -0
  74. package/dist/{compile-ULNO5F7Q.js → compile-LRMAADUT.js} +9 -9
  75. package/dist/{contribute-Q6GX6AXK.js → contribute-GXKOIA42.js} +5 -5
  76. package/dist/{dev-server-4RCDJ5MU.js → dev-server-GREJUEKW.js} +22 -74
  77. package/dist/dev-server-GREJUEKW.js.map +1 -0
  78. package/dist/{feedback-O4Q55SVS.js → feedback-G7G5QCY4.js} +10 -10
  79. package/dist/{git-FVMGBHC2.js → git-ENLT2VNI.js} +6 -4
  80. package/dist/hooks/iloom-hook.js +30 -2
  81. package/dist/{ignite-VHV65WEZ.js → ignite-YUAOJ5PP.js} +20 -20
  82. package/dist/ignite-YUAOJ5PP.js.map +1 -0
  83. package/dist/index.d.ts +71 -27
  84. package/dist/index.js +196 -266
  85. package/dist/index.js.map +1 -1
  86. package/dist/init-XQQMFDM6.js +21 -0
  87. package/dist/{lint-5JMCWE4Y.js → lint-OFVN7FT6.js} +9 -9
  88. package/dist/mcp/issue-management-server.js +359 -13
  89. package/dist/mcp/issue-management-server.js.map +1 -1
  90. package/dist/mcp/recap-server.js +13 -4
  91. package/dist/mcp/recap-server.js.map +1 -1
  92. package/dist/{open-WHVUYGPY.js → open-MCWQAPSZ.js} +25 -76
  93. package/dist/open-MCWQAPSZ.js.map +1 -0
  94. package/dist/{projects-SA76I4TZ.js → projects-PQOTWUII.js} +11 -4
  95. package/dist/projects-PQOTWUII.js.map +1 -0
  96. package/dist/prompts/init-prompt.txt +62 -51
  97. package/dist/prompts/issue-prompt.txt +132 -63
  98. package/dist/prompts/pr-prompt.txt +3 -3
  99. package/dist/prompts/regular-prompt.txt +16 -18
  100. package/dist/prompts/session-summary-prompt.txt +13 -13
  101. package/dist/{rebase-Y4AS6LQW.js → rebase-RKQED567.js} +53 -8
  102. package/dist/rebase-RKQED567.js.map +1 -0
  103. package/dist/{recap-VOOUXOGP.js → recap-ZKGHZCX6.js} +6 -6
  104. package/dist/{run-NCRK5NPR.js → run-CCG24PBC.js} +25 -76
  105. package/dist/run-CCG24PBC.js.map +1 -0
  106. package/dist/schema/settings.schema.json +14 -3
  107. package/dist/{shell-SBLXVOVJ.js → shell-2NNSIU34.js} +6 -6
  108. package/dist/{summary-CVFAMDOJ.js → summary-G6L3VAKK.js} +11 -10
  109. package/dist/{summary-CVFAMDOJ.js.map → summary-G6L3VAKK.js.map} +1 -1
  110. package/dist/{test-3KIVXI6J.js → test-QZDOEUIO.js} +9 -9
  111. package/dist/{test-git-ZB6AGGRW.js → test-git-E2BLXR6M.js} +4 -4
  112. package/dist/{test-prefix-FBGXKMPA.js → test-prefix-A7JGGYAA.js} +4 -4
  113. package/dist/{test-webserver-YVQD42W6.js → test-webserver-NRMGT2HB.js} +29 -8
  114. package/dist/test-webserver-NRMGT2HB.js.map +1 -0
  115. package/package.json +3 -1
  116. package/dist/ClaudeContextManager-6J2EB4QU.js +0 -14
  117. package/dist/ClaudeService-O2PB22GX.js +0 -13
  118. package/dist/PRManager-GB3FOJ2W.js +0 -14
  119. package/dist/chunk-2A7WQKBE.js.map +0 -1
  120. package/dist/chunk-6YSFTPKW.js.map +0 -1
  121. package/dist/chunk-7Q66W4OH.js.map +0 -1
  122. package/dist/chunk-AEIMYF4P.js.map +0 -1
  123. package/dist/chunk-BXCPJJYM.js.map +0 -1
  124. package/dist/chunk-CFUWQHCJ.js.map +0 -1
  125. package/dist/chunk-F6WVM437.js.map +0 -1
  126. package/dist/chunk-GEXP5IOF.js.map +0 -1
  127. package/dist/chunk-I75JMBNB.js.map +0 -1
  128. package/dist/chunk-K7SEEHKO.js.map +0 -1
  129. package/dist/chunk-VDA5JMB4.js.map +0 -1
  130. package/dist/chunk-VU3QMIP2.js.map +0 -1
  131. package/dist/chunk-W6WVRHJ6.js +0 -251
  132. package/dist/chunk-W6WVRHJ6.js.map +0 -1
  133. package/dist/cleanup-BRUAINKE.js.map +0 -1
  134. package/dist/dev-server-4RCDJ5MU.js.map +0 -1
  135. package/dist/ignite-VHV65WEZ.js.map +0 -1
  136. package/dist/init-UTYRHNJJ.js +0 -21
  137. package/dist/open-WHVUYGPY.js.map +0 -1
  138. package/dist/projects-SA76I4TZ.js.map +0 -1
  139. package/dist/rebase-Y4AS6LQW.js.map +0 -1
  140. package/dist/run-NCRK5NPR.js.map +0 -1
  141. package/dist/test-webserver-YVQD42W6.js.map +0 -1
  142. /package/dist/{BranchNamingService-B5PVRR7F.js.map → BranchNamingService-FLPUUFOB.js.map} +0 -0
  143. /package/dist/{ClaudeContextManager-6J2EB4QU.js.map → ClaudeContextManager-KE5TBZVZ.js.map} +0 -0
  144. /package/dist/{ClaudeService-O2PB22GX.js.map → ClaudeService-CRSETT3A.js.map} +0 -0
  145. /package/dist/{GitHubService-S2OGUTDR.js.map → GitHubService-O7U4UQ7N.js.map} +0 -0
  146. /package/dist/{LoomLauncher-5LFM4LXB.js.map → LoomLauncher-NL65LSKP.js.map} +0 -0
  147. /package/dist/{MetadataManager-DFI73J3G.js.map → MetadataManager-XJ2YB762.js.map} +0 -0
  148. /package/dist/{PRManager-GB3FOJ2W.js.map → PRManager-2ABCWXHW.js.map} +0 -0
  149. /package/dist/{ProjectCapabilityDetector-S5FLNCFI.js.map → ProjectCapabilityDetector-UZYW32SY.js.map} +0 -0
  150. /package/dist/{PromptTemplateManager-C3DK6XZL.js.map → PromptTemplateManager-7L3HJQQU.js.map} +0 -0
  151. /package/dist/{SettingsManager-35F5RUJH.js.map → SettingsManager-YU4VYPTW.js.map} +0 -0
  152. /package/dist/{build-FJVYP7EV.js.map → build-O2EJHDEW.js.map} +0 -0
  153. /package/dist/{chunk-ZPSTA5PR.js.map → chunk-3CDWFEGL.js.map} +0 -0
  154. /package/dist/{chunk-UQIXZ3BA.js.map → chunk-5V74K5ZA.js.map} +0 -0
  155. /package/dist/{chunk-7WANFUIK.js.map → chunk-6TL3BYH6.js.map} +0 -0
  156. /package/dist/{chunk-5TXLVEXT.js.map → chunk-C3AKFAIR.js.map} +0 -0
  157. /package/dist/{chunk-LVBRMTE6.js.map → chunk-FEAJR6PN.js.map} +0 -0
  158. /package/dist/{chunk-64O2UIWO.js.map → chunk-GV5X6XUE.js.map} +0 -0
  159. /package/dist/{chunk-7HIRPCKU.js.map → chunk-HVQNVRAF.js.map} +0 -0
  160. /package/dist/{chunk-6U6VI4SZ.js.map → chunk-KVS4XGBQ.js.map} +0 -0
  161. /package/dist/{chunk-AXX3QIKK.js.map → chunk-LLWX3PCW.js.map} +0 -0
  162. /package/dist/{chunk-SN3Z6EZO.js.map → chunk-N7FVXZNI.js.map} +0 -0
  163. /package/dist/{chunk-EK3XCAAS.js.map → chunk-UDRZY65Y.js.map} +0 -0
  164. /package/dist/{chunk-3PT7RKL5.js.map → chunk-USJSNHGG.js.map} +0 -0
  165. /package/dist/{chunk-TRQ76ISK.js.map → chunk-Z6BO53V7.js.map} +0 -0
  166. /package/dist/{claude-H33OQMXO.js.map → claude-6H36IBHO.js.map} +0 -0
  167. /package/dist/{compile-ULNO5F7Q.js.map → compile-LRMAADUT.js.map} +0 -0
  168. /package/dist/{contribute-Q6GX6AXK.js.map → contribute-GXKOIA42.js.map} +0 -0
  169. /package/dist/{feedback-O4Q55SVS.js.map → feedback-G7G5QCY4.js.map} +0 -0
  170. /package/dist/{git-FVMGBHC2.js.map → git-ENLT2VNI.js.map} +0 -0
  171. /package/dist/{init-UTYRHNJJ.js.map → init-XQQMFDM6.js.map} +0 -0
  172. /package/dist/{lint-5JMCWE4Y.js.map → lint-OFVN7FT6.js.map} +0 -0
  173. /package/dist/{recap-VOOUXOGP.js.map → recap-ZKGHZCX6.js.map} +0 -0
  174. /package/dist/{shell-SBLXVOVJ.js.map → shell-2NNSIU34.js.map} +0 -0
  175. /package/dist/{test-3KIVXI6J.js.map → test-QZDOEUIO.js.map} +0 -0
  176. /package/dist/{test-git-ZB6AGGRW.js.map → test-git-E2BLXR6M.js.map} +0 -0
  177. /package/dist/{test-prefix-FBGXKMPA.js.map → test-prefix-A7JGGYAA.js.map} +0 -0
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  SessionSummaryService
4
- } from "./chunk-K7SEEHKO.js";
4
+ } from "./chunk-CNSTXBJ3.js";
5
5
  import "./chunk-NXMDEL3F.js";
6
6
  import {
7
7
  CLIIsolationManager,
@@ -9,30 +9,28 @@ import {
9
9
  EnvironmentManager,
10
10
  LoomManager,
11
11
  ResourceCleanup
12
- } from "./chunk-I75JMBNB.js";
12
+ } from "./chunk-S7YMZQUD.js";
13
+ import {
14
+ BuildRunner,
15
+ MergeManager
16
+ } from "./chunk-LQBLDI47.js";
13
17
  import {
14
18
  IssueTrackerFactory,
15
19
  generateIssueManagementMcpConfig
16
- } from "./chunk-6YSFTPKW.js";
17
- import "./chunk-7Q66W4OH.js";
20
+ } from "./chunk-FM4KBPVA.js";
18
21
  import {
19
22
  ProcessManager
20
- } from "./chunk-VU3QMIP2.js";
21
- import {
22
- detectPackageManager,
23
- installDependencies,
24
- runScript
25
- } from "./chunk-AXX3QIKK.js";
23
+ } from "./chunk-453NC377.js";
26
24
  import {
27
25
  IdentifierParser
28
- } from "./chunk-UQIXZ3BA.js";
26
+ } from "./chunk-5V74K5ZA.js";
29
27
  import {
30
28
  createNeonProviderFromSettings
31
29
  } from "./chunk-7LSSNB7Y.js";
32
30
  import {
33
31
  InitCommand,
34
32
  ShellCompletion
35
- } from "./chunk-LVBRMTE6.js";
33
+ } from "./chunk-FEAJR6PN.js";
36
34
  import {
37
35
  FirstRunManager
38
36
  } from "./chunk-Q7POFB5Q.js";
@@ -40,29 +38,35 @@ import "./chunk-F2PWIRV4.js";
40
38
  import {
41
39
  IssueEnhancementService,
42
40
  capitalizeFirstLetter
43
- } from "./chunk-7HIRPCKU.js";
41
+ } from "./chunk-HVQNVRAF.js";
44
42
  import {
45
43
  ProjectCapabilityDetector
46
- } from "./chunk-ZPSTA5PR.js";
47
- import {
48
- getPackageConfig,
49
- hasScript
50
- } from "./chunk-BXCPJJYM.js";
44
+ } from "./chunk-3CDWFEGL.js";
51
45
  import {
52
46
  AgentManager
53
- } from "./chunk-SN3Z6EZO.js";
47
+ } from "./chunk-N7FVXZNI.js";
54
48
  import {
55
- MergeManager
56
- } from "./chunk-2A7WQKBE.js";
49
+ CommitManager,
50
+ UserAbortedCommitError,
51
+ ValidationRunner
52
+ } from "./chunk-NTIZLX42.js";
53
+ import {
54
+ installDependencies
55
+ } from "./chunk-LLWX3PCW.js";
57
56
  import {
58
57
  GitWorktreeManager
59
- } from "./chunk-EK3XCAAS.js";
58
+ } from "./chunk-UDRZY65Y.js";
59
+ import "./chunk-ITN64ENQ.js";
60
60
  import {
61
61
  PRManager
62
- } from "./chunk-VDA5JMB4.js";
62
+ } from "./chunk-EPPPDVHD.js";
63
63
  import {
64
64
  openBrowser
65
65
  } from "./chunk-YETJNRQM.js";
66
+ import {
67
+ IssueManagementProviderFactory
68
+ } from "./chunk-GJMEKEI5.js";
69
+ import "./chunk-HBJITKSZ.js";
66
70
  import {
67
71
  getConfiguredRepoFromSettings,
68
72
  hasMultipleRemotes
@@ -74,16 +78,17 @@ import {
74
78
  } from "./chunk-O7VL5N6S.js";
75
79
  import {
76
80
  ClaudeContextManager
77
- } from "./chunk-7WANFUIK.js";
78
- import "./chunk-6U6VI4SZ.js";
79
- import "./chunk-W6WVRHJ6.js";
81
+ } from "./chunk-6TL3BYH6.js";
82
+ import "./chunk-KVS4XGBQ.js";
83
+ import "./chunk-TIYJEEVO.js";
80
84
  import {
81
85
  extractSettingsOverrides
82
86
  } from "./chunk-GYCR2LOU.js";
83
87
  import {
84
88
  DefaultBranchNamingService
85
- } from "./chunk-5TXLVEXT.js";
89
+ } from "./chunk-C3AKFAIR.js";
86
90
  import {
91
+ GitCommandError,
87
92
  executeGitCommand,
88
93
  extractIssueNumber,
89
94
  findMainWorktreePathWithSettings,
@@ -91,30 +96,29 @@ import {
91
96
  getMergeTargetBranch,
92
97
  getRepoRoot,
93
98
  isPlaceholderCommit,
99
+ isValidGitRepo,
94
100
  pushBranchToRemote,
95
101
  removePlaceholderCommitFromHead,
96
102
  removePlaceholderCommitFromHistory
97
- } from "./chunk-GEXP5IOF.js";
103
+ } from "./chunk-ZA575VLF.js";
98
104
  import {
99
105
  SettingsManager
100
- } from "./chunk-F6WVM437.js";
106
+ } from "./chunk-WFQ5CLTR.js";
101
107
  import {
102
108
  MetadataManager
103
- } from "./chunk-CFUWQHCJ.js";
109
+ } from "./chunk-VWGKGNJP.js";
104
110
  import {
105
111
  GitHubService
106
- } from "./chunk-3PT7RKL5.js";
107
- import "./chunk-LT3SGBR7.js";
112
+ } from "./chunk-USJSNHGG.js";
113
+ import "./chunk-GCPAZSGV.js";
108
114
  import {
109
- promptCommitAction,
110
115
  promptConfirmation,
111
116
  waitForKeypress
112
117
  } from "./chunk-ZX3GTM7O.js";
113
118
  import "./chunk-433MOLAU.js";
114
119
  import {
115
- detectClaudeCli,
116
120
  launchClaude
117
- } from "./chunk-AEIMYF4P.js";
121
+ } from "./chunk-FP7G7DG3.js";
118
122
  import {
119
123
  getLogger,
120
124
  withLogger
@@ -687,7 +691,8 @@ var EnhanceCommand = class {
687
691
  "mcp__issue_management__get_issue",
688
692
  "mcp__issue_management__get_comment",
689
693
  "mcp__issue_management__create_comment",
690
- "mcp__issue_management__update_comment"
694
+ "mcp__issue_management__update_comment",
695
+ "mcp__issue_management__create_issue"
691
696
  ];
692
697
  disallowedTools = ["Bash(gh api:*)"];
693
698
  getLogger().debug("Configured tool filtering for issue workflow", { allowedTools, disallowedTools });
@@ -815,860 +820,6 @@ IMPORTANT: When you create your analysis comment, tag @${author} in the "Questio
815
820
  }
816
821
  };
817
822
 
818
- // src/lib/ValidationRunner.ts
819
- var ValidationRunner = class {
820
- constructor() {
821
- }
822
- /**
823
- * Run all validations in sequence: typecheck → lint → test
824
- * Fails fast on first error
825
- */
826
- async runValidations(worktreePath, options = {}) {
827
- const startTime = Date.now();
828
- const steps = [];
829
- if (!options.skipTypecheck) {
830
- const typecheckResult = await this.runTypecheck(
831
- worktreePath,
832
- options.dryRun ?? false
833
- );
834
- steps.push(typecheckResult);
835
- if (!typecheckResult.passed && !typecheckResult.skipped) {
836
- return {
837
- success: false,
838
- steps,
839
- totalDuration: Date.now() - startTime
840
- };
841
- }
842
- }
843
- if (!options.skipLint) {
844
- const lintResult = await this.runLint(worktreePath, options.dryRun ?? false);
845
- steps.push(lintResult);
846
- if (!lintResult.passed && !lintResult.skipped) {
847
- return { success: false, steps, totalDuration: Date.now() - startTime };
848
- }
849
- }
850
- if (!options.skipTests) {
851
- const testResult = await this.runTests(
852
- worktreePath,
853
- options.dryRun ?? false
854
- );
855
- steps.push(testResult);
856
- if (!testResult.passed && !testResult.skipped) {
857
- return { success: false, steps, totalDuration: Date.now() - startTime };
858
- }
859
- }
860
- return { success: true, steps, totalDuration: Date.now() - startTime };
861
- }
862
- /**
863
- * Run typecheck validation
864
- * Prefers 'compile' script over 'typecheck' if both exist
865
- */
866
- async runTypecheck(worktreePath, dryRun) {
867
- const stepStartTime = Date.now();
868
- let scriptToRun = null;
869
- try {
870
- const pkgJson = await getPackageConfig(worktreePath);
871
- const hasCompileScript = hasScript(pkgJson, "compile");
872
- const hasTypecheckScript = hasScript(pkgJson, "typecheck");
873
- if (hasCompileScript) {
874
- scriptToRun = "compile";
875
- } else if (hasTypecheckScript) {
876
- scriptToRun = "typecheck";
877
- }
878
- if (!scriptToRun) {
879
- getLogger().debug("Skipping typecheck - no compile or typecheck script found");
880
- return {
881
- step: "typecheck",
882
- passed: true,
883
- skipped: true,
884
- duration: Date.now() - stepStartTime
885
- };
886
- }
887
- } catch (error) {
888
- if (error instanceof Error && error.message.includes("package.json not found")) {
889
- getLogger().debug("Skipping typecheck - no package.json found (non-Node.js project)");
890
- return {
891
- step: "typecheck",
892
- passed: true,
893
- skipped: true,
894
- duration: Date.now() - stepStartTime
895
- };
896
- }
897
- throw error;
898
- }
899
- const packageManager = await detectPackageManager(worktreePath);
900
- if (dryRun) {
901
- const command = packageManager === "npm" ? `npm run ${scriptToRun}` : `${packageManager} ${scriptToRun}`;
902
- getLogger().info(`[DRY RUN] Would run: ${command}`);
903
- return {
904
- step: scriptToRun,
905
- passed: true,
906
- skipped: false,
907
- duration: Date.now() - stepStartTime
908
- };
909
- }
910
- getLogger().info(`Running ${scriptToRun}...`);
911
- try {
912
- await runScript(scriptToRun, worktreePath, [], { quiet: true });
913
- getLogger().success(`${scriptToRun.charAt(0).toUpperCase() + scriptToRun.slice(1)} passed`);
914
- return {
915
- step: scriptToRun,
916
- passed: true,
917
- skipped: false,
918
- duration: Date.now() - stepStartTime
919
- };
920
- } catch {
921
- const fixed = await this.attemptClaudeFix(
922
- scriptToRun,
923
- worktreePath,
924
- packageManager
925
- );
926
- if (fixed) {
927
- return {
928
- step: scriptToRun,
929
- passed: true,
930
- skipped: false,
931
- duration: Date.now() - stepStartTime
932
- };
933
- }
934
- const runCommand = packageManager === "npm" ? `npm run ${scriptToRun}` : `${packageManager} ${scriptToRun}`;
935
- const stepLabel = scriptToRun.charAt(0).toUpperCase() + scriptToRun.slice(1);
936
- throw new Error(
937
- `Error: ${stepLabel} failed.
938
- Fix type errors before merging.
939
-
940
- Run '${runCommand}' to see detailed errors.`
941
- );
942
- }
943
- }
944
- /**
945
- * Run lint validation
946
- */
947
- async runLint(worktreePath, dryRun) {
948
- const stepStartTime = Date.now();
949
- try {
950
- const pkgJson = await getPackageConfig(worktreePath);
951
- const hasLintScript = hasScript(pkgJson, "lint");
952
- if (!hasLintScript) {
953
- getLogger().debug("Skipping lint - no lint script found");
954
- return {
955
- step: "lint",
956
- passed: true,
957
- skipped: true,
958
- duration: Date.now() - stepStartTime
959
- };
960
- }
961
- } catch (error) {
962
- if (error instanceof Error && error.message.includes("package.json not found")) {
963
- getLogger().debug("Skipping lint - no package.json found (non-Node.js project)");
964
- return {
965
- step: "lint",
966
- passed: true,
967
- skipped: true,
968
- duration: Date.now() - stepStartTime
969
- };
970
- }
971
- throw error;
972
- }
973
- const packageManager = await detectPackageManager(worktreePath);
974
- if (dryRun) {
975
- const command = packageManager === "npm" ? "npm run lint" : `${packageManager} lint`;
976
- getLogger().info(`[DRY RUN] Would run: ${command}`);
977
- return {
978
- step: "lint",
979
- passed: true,
980
- skipped: false,
981
- duration: Date.now() - stepStartTime
982
- };
983
- }
984
- getLogger().info("Running lint...");
985
- try {
986
- await runScript("lint", worktreePath, [], { quiet: true });
987
- getLogger().success("Linting passed");
988
- return {
989
- step: "lint",
990
- passed: true,
991
- skipped: false,
992
- duration: Date.now() - stepStartTime
993
- };
994
- } catch {
995
- const fixed = await this.attemptClaudeFix(
996
- "lint",
997
- worktreePath,
998
- packageManager
999
- );
1000
- if (fixed) {
1001
- return {
1002
- step: "lint",
1003
- passed: true,
1004
- skipped: false,
1005
- duration: Date.now() - stepStartTime
1006
- };
1007
- }
1008
- const runCommand = packageManager === "npm" ? "npm run lint" : `${packageManager} lint`;
1009
- throw new Error(
1010
- `Error: Linting failed.
1011
- Fix linting errors before merging.
1012
-
1013
- Run '${runCommand}' to see detailed errors.`
1014
- );
1015
- }
1016
- }
1017
- /**
1018
- * Run test validation
1019
- */
1020
- async runTests(worktreePath, dryRun) {
1021
- const stepStartTime = Date.now();
1022
- try {
1023
- const pkgJson = await getPackageConfig(worktreePath);
1024
- const hasTestScript = hasScript(pkgJson, "test");
1025
- if (!hasTestScript) {
1026
- getLogger().debug("Skipping tests - no test script found");
1027
- return {
1028
- step: "test",
1029
- passed: true,
1030
- skipped: true,
1031
- duration: Date.now() - stepStartTime
1032
- };
1033
- }
1034
- } catch (error) {
1035
- if (error instanceof Error && error.message.includes("package.json not found")) {
1036
- getLogger().debug("Skipping tests - no package.json found (non-Node.js project)");
1037
- return {
1038
- step: "test",
1039
- passed: true,
1040
- skipped: true,
1041
- duration: Date.now() - stepStartTime
1042
- };
1043
- }
1044
- throw error;
1045
- }
1046
- const packageManager = await detectPackageManager(worktreePath);
1047
- if (dryRun) {
1048
- const command = packageManager === "npm" ? "npm run test" : `${packageManager} test`;
1049
- getLogger().info(`[DRY RUN] Would run: ${command}`);
1050
- return {
1051
- step: "test",
1052
- passed: true,
1053
- skipped: false,
1054
- duration: Date.now() - stepStartTime
1055
- };
1056
- }
1057
- getLogger().info("Running tests...");
1058
- try {
1059
- await runScript("test", worktreePath, [], { quiet: true });
1060
- getLogger().success("Tests passed");
1061
- return {
1062
- step: "test",
1063
- passed: true,
1064
- skipped: false,
1065
- duration: Date.now() - stepStartTime
1066
- };
1067
- } catch {
1068
- const fixed = await this.attemptClaudeFix(
1069
- "test",
1070
- worktreePath,
1071
- packageManager
1072
- );
1073
- if (fixed) {
1074
- return {
1075
- step: "test",
1076
- passed: true,
1077
- skipped: false,
1078
- duration: Date.now() - stepStartTime
1079
- };
1080
- }
1081
- const runCommand = packageManager === "npm" ? "npm run test" : `${packageManager} test`;
1082
- throw new Error(
1083
- `Error: Tests failed.
1084
- Fix test failures before merging.
1085
-
1086
- Run '${runCommand}' to see detailed errors.`
1087
- );
1088
- }
1089
- }
1090
- /**
1091
- * Attempt to fix validation errors using Claude
1092
- * Pattern based on MergeManager.attemptClaudeConflictResolution
1093
- *
1094
- * @param validationType - Type of validation that failed ('compile' | 'typecheck' | 'lint' | 'test')
1095
- * @param worktreePath - Path to the worktree
1096
- * @param packageManager - Detected package manager
1097
- * @returns true if Claude fixed the issue, false otherwise
1098
- */
1099
- async attemptClaudeFix(validationType, worktreePath, packageManager) {
1100
- const isClaudeAvailable = await detectClaudeCli();
1101
- if (!isClaudeAvailable) {
1102
- getLogger().debug("Claude CLI not available, skipping auto-fix");
1103
- return false;
1104
- }
1105
- const validationCommand = this.getValidationCommand(validationType, packageManager);
1106
- const prompt = this.getClaudePrompt(validationType, validationCommand);
1107
- const validationTypeCapitalized = validationType.charAt(0).toUpperCase() + validationType.slice(1);
1108
- getLogger().info(`Launching Claude to help fix ${validationTypeCapitalized} errors...`);
1109
- try {
1110
- await launchClaude(prompt, {
1111
- addDir: worktreePath,
1112
- headless: false,
1113
- // Interactive mode
1114
- permissionMode: "acceptEdits",
1115
- // Auto-accept edits
1116
- model: "sonnet"
1117
- // Use Sonnet model
1118
- });
1119
- getLogger().info(`Re-running ${validationTypeCapitalized} after Claude's fixes...`);
1120
- try {
1121
- await runScript(validationType, worktreePath, [], { quiet: true });
1122
- getLogger().success(`${validationTypeCapitalized} passed after Claude auto-fix`);
1123
- return true;
1124
- } catch {
1125
- getLogger().warn(`${validationTypeCapitalized} still failing after Claude's help`);
1126
- return false;
1127
- }
1128
- } catch (error) {
1129
- getLogger().warn("Claude auto-fix failed", {
1130
- error: error instanceof Error ? error.message : String(error)
1131
- });
1132
- return false;
1133
- }
1134
- }
1135
- /**
1136
- * Get validation command string for prompts
1137
- * Uses il commands for multi-language project support
1138
- */
1139
- getValidationCommand(validationType, _packageManager) {
1140
- return `il ${validationType}`;
1141
- }
1142
- /**
1143
- * Get Claude prompt for specific validation type
1144
- * Matches bash script prompts exactly
1145
- */
1146
- getClaudePrompt(validationType, validationCommand) {
1147
- switch (validationType) {
1148
- case "compile":
1149
- case "typecheck":
1150
- return `There are TypeScript errors in this codebase. Please analyze the ${validationType} output, identify all type errors, and fix them. Run '${validationCommand}' to see the errors, then make the necessary code changes to resolve all type issues. When you are done, tell the user to quit using /exit to continue the validation process.`;
1151
- case "lint":
1152
- return `There are ESLint errors in this codebase. Please analyze the linting output, identify all linting issues, and fix them. Run '${validationCommand}' to see the errors, then make the necessary code changes to resolve all linting issues. Focus on code quality, consistency, and following the project's linting rules. When you are done, tell the user to quit using /exit to continue the validation process.`;
1153
- case "test":
1154
- return `There are unit test failures in this codebase. Please analyze the test output to understand what's failing, then fix the issues. This might involve updating test code, fixing bugs in the source code, or updating tests to match new behavior. Run '${validationCommand}' to see the detailed test failures, then make the necessary changes to get all tests passing. When you are done, tell the user to quit using /exit to continue the validation process.`;
1155
- }
1156
- }
1157
- };
1158
-
1159
- // src/utils/vscode.ts
1160
- import { execa } from "execa";
1161
- function isRunningInVSCode() {
1162
- return process.env.TERM_PROGRAM === "vscode";
1163
- }
1164
- function isRunningInCursor() {
1165
- return !!process.env.CURSOR_TRACE_ID;
1166
- }
1167
- function isRunningInAntigravity() {
1168
- return !!process.env.ANTIGRAVITY_CLI_ALIAS;
1169
- }
1170
- async function isVSCodeAvailable() {
1171
- try {
1172
- await execa("command", ["-v", "code"], {
1173
- shell: true,
1174
- timeout: 5e3
1175
- });
1176
- return true;
1177
- } catch (error) {
1178
- logger.debug("VSCode CLI not available", { error });
1179
- return false;
1180
- }
1181
- }
1182
- async function isCursorAvailable() {
1183
- try {
1184
- await execa("command", ["-v", "cursor"], {
1185
- shell: true,
1186
- timeout: 5e3
1187
- });
1188
- return true;
1189
- } catch (error) {
1190
- logger.debug("Cursor CLI not available", { error });
1191
- return false;
1192
- }
1193
- }
1194
- async function isAntigravityAvailable() {
1195
- try {
1196
- await execa("command", ["-v", "agy"], {
1197
- shell: true,
1198
- timeout: 5e3
1199
- });
1200
- return true;
1201
- } catch (error) {
1202
- logger.debug("Antigravity CLI not available", { error });
1203
- return false;
1204
- }
1205
- }
1206
-
1207
- // src/types/index.ts
1208
- var UserAbortedCommitError = class extends Error {
1209
- constructor(message = "User aborted the commit") {
1210
- super(message);
1211
- this.name = "UserAbortedCommitError";
1212
- }
1213
- };
1214
-
1215
- // src/lib/CommitManager.ts
1216
- import { writeFile, readFile as readFile2, unlink } from "fs/promises";
1217
- import { join } from "path";
1218
- import { execa as execa2 } from "execa";
1219
- var CommitManager = class {
1220
- constructor() {
1221
- }
1222
- /**
1223
- * Detect uncommitted changes in a worktree
1224
- * Parses git status --porcelain output into structured GitStatus
1225
- */
1226
- async detectUncommittedChanges(worktreePath) {
1227
- const porcelainOutput = await executeGitCommand(["status", "--porcelain"], {
1228
- cwd: worktreePath
1229
- });
1230
- const { stagedFiles, unstagedFiles } = this.parseGitStatus(porcelainOutput);
1231
- const currentBranch = await executeGitCommand(["branch", "--show-current"], {
1232
- cwd: worktreePath
1233
- });
1234
- return {
1235
- hasUncommittedChanges: stagedFiles.length > 0 || unstagedFiles.length > 0,
1236
- unstagedFiles,
1237
- stagedFiles,
1238
- currentBranch: currentBranch.trim(),
1239
- // Defer these to future enhancement
1240
- isAheadOfRemote: false,
1241
- isBehindRemote: false
1242
- };
1243
- }
1244
- /**
1245
- * Stage all changes and commit with Claude-generated or simple message
1246
- * Tries Claude first, falls back to simple message if Claude unavailable or fails
1247
- */
1248
- async commitChanges(worktreePath, options) {
1249
- if (options.dryRun) {
1250
- getLogger().info("[DRY RUN] Would run: git add -A");
1251
- getLogger().info("[DRY RUN] Would generate commit message with Claude (if available)");
1252
- const fallbackMessage = this.generateFallbackMessage(options);
1253
- const verifyFlag = options.skipVerify ? " --no-verify" : "";
1254
- getLogger().info(`[DRY RUN] Would commit with message${verifyFlag}: ${fallbackMessage}`);
1255
- return;
1256
- }
1257
- await executeGitCommand(["add", "-A"], { cwd: worktreePath });
1258
- let message = null;
1259
- if (!options.message) {
1260
- try {
1261
- message = await this.generateClaudeCommitMessage(worktreePath, options.issueNumber);
1262
- } catch (error) {
1263
- getLogger().debug("Claude commit message generation failed, using fallback", { error });
1264
- }
1265
- }
1266
- message ??= this.generateFallbackMessage(options);
1267
- if (options.skipVerify) {
1268
- getLogger().warn("Skipping pre-commit hooks (--no-verify configured in settings)");
1269
- }
1270
- try {
1271
- if (options.noReview || options.message) {
1272
- const commitArgs = ["commit", "-m", message];
1273
- if (options.skipVerify) {
1274
- commitArgs.push("--no-verify");
1275
- }
1276
- await executeGitCommand(commitArgs, { cwd: worktreePath });
1277
- } else {
1278
- const action = await promptCommitAction(message);
1279
- if (action === "abort") {
1280
- throw new UserAbortedCommitError();
1281
- }
1282
- if (action === "accept") {
1283
- const commitArgs = ["commit", "-m", message];
1284
- if (options.skipVerify) {
1285
- commitArgs.push("--no-verify");
1286
- }
1287
- await executeGitCommand(commitArgs, { cwd: worktreePath });
1288
- } else {
1289
- getLogger().info("Opening editor for commit message review...");
1290
- if (isRunningInAntigravity() && await isAntigravityAvailable()) {
1291
- await this.commitWithExternalEditor(worktreePath, message, options, "agy", "Antigravity");
1292
- } else if (isRunningInCursor() && await isCursorAvailable()) {
1293
- await this.commitWithExternalEditor(worktreePath, message, options, "cursor", "Cursor");
1294
- } else if (isRunningInVSCode() && await isVSCodeAvailable()) {
1295
- await this.commitWithExternalEditor(worktreePath, message, options, "code", "VSCode");
1296
- } else {
1297
- const commitArgs = ["commit", "-e", "-m", message];
1298
- if (options.skipVerify) {
1299
- commitArgs.push("--no-verify");
1300
- }
1301
- await executeGitCommand(commitArgs, {
1302
- cwd: worktreePath,
1303
- stdio: "inherit",
1304
- timeout: 3e5
1305
- // 5 minutes for interactive editing
1306
- });
1307
- }
1308
- }
1309
- }
1310
- } catch (error) {
1311
- if (error instanceof UserAbortedCommitError) {
1312
- throw error;
1313
- }
1314
- if (error instanceof Error && error.message.includes("nothing to commit")) {
1315
- getLogger().info("No changes to commit");
1316
- return;
1317
- }
1318
- throw error;
1319
- }
1320
- }
1321
- /**
1322
- * Commit with external editor CLI (VSCode, Cursor, Antigravity, etc.)
1323
- * Handles file creation, editing, and commit to ensure the file opens
1324
- * in the current editor window (preserves IPC context)
1325
- */
1326
- async commitWithExternalEditor(worktreePath, message, options, cliCommand, editorName) {
1327
- const commitMsgPath = join(worktreePath, ".COMMIT_EDITMSG");
1328
- const initialContent = `${message}
1329
-
1330
- # Please enter the commit message for your changes. Lines starting
1331
- # with '#' will be ignored, and an empty message aborts the commit.
1332
- #
1333
- # Save and close the file to complete the commit.
1334
- `;
1335
- await writeFile(commitMsgPath, initialContent, "utf-8");
1336
- try {
1337
- getLogger().debug(`Opening commit message in ${editorName}: ${commitMsgPath}`);
1338
- await execa2(cliCommand, ["--wait", commitMsgPath], {
1339
- cwd: worktreePath,
1340
- stdio: "inherit"
1341
- });
1342
- const editedContent = await readFile2(commitMsgPath, "utf-8");
1343
- const finalMessage = editedContent.split("\n").filter((line) => !line.startsWith("#")).join("\n").trim();
1344
- if (!finalMessage) {
1345
- throw new UserAbortedCommitError();
1346
- }
1347
- const commitArgs = ["commit", "-F", commitMsgPath];
1348
- if (options.skipVerify) {
1349
- commitArgs.push("--no-verify");
1350
- }
1351
- await writeFile(commitMsgPath, finalMessage, "utf-8");
1352
- await executeGitCommand(commitArgs, { cwd: worktreePath });
1353
- } finally {
1354
- try {
1355
- await unlink(commitMsgPath);
1356
- } catch {
1357
- }
1358
- }
1359
- }
1360
- /**
1361
- * Generate simple fallback commit message when Claude unavailable
1362
- * Used as fallback for Claude-powered commit messages
1363
- */
1364
- generateFallbackMessage(options) {
1365
- if (options.message) {
1366
- return options.message;
1367
- }
1368
- if (options.issueNumber) {
1369
- return `WIP: Auto-commit for issue #${options.issueNumber}
1370
-
1371
- Fixes #${options.issueNumber}`;
1372
- } else {
1373
- return "WIP: Auto-commit uncommitted changes";
1374
- }
1375
- }
1376
- /**
1377
- * Parse git status --porcelain output
1378
- * Format: "XY filename" where X=index, Y=worktree
1379
- * Examples:
1380
- * "M file.ts" - staged modification
1381
- * " M file.ts" - unstaged modification
1382
- * "MM file.ts" - both staged and unstaged
1383
- * "?? file.ts" - untracked
1384
- */
1385
- parseGitStatus(porcelainOutput) {
1386
- const stagedFiles = [];
1387
- const unstagedFiles = [];
1388
- if (!porcelainOutput.trim()) {
1389
- return { stagedFiles, unstagedFiles };
1390
- }
1391
- const lines = porcelainOutput.split("\n").filter((line) => line.trim());
1392
- for (const line of lines) {
1393
- if (line.length < 3) continue;
1394
- const indexStatus = line[0];
1395
- const worktreeStatus = line[1];
1396
- const filename = line.substring(3);
1397
- if (indexStatus !== " " && indexStatus !== "?") {
1398
- stagedFiles.push(filename);
1399
- }
1400
- if (worktreeStatus !== " " || line.startsWith("??")) {
1401
- unstagedFiles.push(filename);
1402
- }
1403
- }
1404
- return { stagedFiles, unstagedFiles };
1405
- }
1406
- /**
1407
- * Generate commit message using Claude Code
1408
- * Claude examines the git repository directly via --add-dir option
1409
- * Returns null if Claude unavailable or fails validation
1410
- */
1411
- async generateClaudeCommitMessage(worktreePath, issueNumber) {
1412
- const startTime = Date.now();
1413
- getLogger().info("Starting Claude commit message generation...", {
1414
- worktreePath: worktreePath.split("/").pop(),
1415
- // Just show the folder name for privacy
1416
- issueNumber
1417
- });
1418
- getLogger().debug("Checking Claude CLI availability...");
1419
- const isClaudeAvailable = await detectClaudeCli();
1420
- if (!isClaudeAvailable) {
1421
- getLogger().info("Claude CLI not available, skipping Claude commit message generation");
1422
- return null;
1423
- }
1424
- getLogger().debug("Claude CLI is available");
1425
- getLogger().debug("Building commit message prompt...");
1426
- const prompt = this.buildCommitMessagePrompt(issueNumber);
1427
- getLogger().debug("Prompt built", { promptLength: prompt.length });
1428
- getLogger().debug("Claude prompt content:", {
1429
- prompt,
1430
- truncatedPreview: prompt.substring(0, 500) + (prompt.length > 500 ? "...[truncated]" : "")
1431
- });
1432
- try {
1433
- getLogger().info("Calling Claude CLI for commit message generation...");
1434
- const claudeStartTime = Date.now();
1435
- const claudeOptions = {
1436
- headless: true,
1437
- addDir: worktreePath,
1438
- model: "claude-haiku-4-5-20251001",
1439
- // Fast, cost-effective model
1440
- timeout: 12e4,
1441
- // 120 second timeout
1442
- appendSystemPrompt: "Output only the requested content. Never include preamble, analysis, or meta-commentary. Your response is used verbatim."
1443
- };
1444
- getLogger().debug("Claude CLI call parameters:", {
1445
- options: claudeOptions,
1446
- worktreePathForAnalysis: worktreePath,
1447
- addDirContents: "Will include entire worktree directory for analysis"
1448
- });
1449
- const result = await launchClaude(prompt, claudeOptions);
1450
- const claudeDuration = Date.now() - claudeStartTime;
1451
- getLogger().debug("Claude API call completed", { duration: `${claudeDuration}ms` });
1452
- if (typeof result !== "string") {
1453
- getLogger().warn("Claude returned non-string result", { resultType: typeof result });
1454
- return null;
1455
- }
1456
- getLogger().debug("Raw Claude output received", {
1457
- outputLength: result.length,
1458
- preview: result.substring(0, 200) + (result.length > 200 ? "..." : "")
1459
- });
1460
- getLogger().debug("Sanitizing Claude output...");
1461
- const sanitized = this.sanitizeClaudeOutput(result);
1462
- getLogger().debug("Output sanitized", {
1463
- originalLength: result.length,
1464
- sanitizedLength: sanitized.length,
1465
- sanitized: sanitized.substring(0, 200) + (sanitized.length > 200 ? "..." : "")
1466
- });
1467
- if (!sanitized) {
1468
- getLogger().warn("Claude returned empty message after sanitization");
1469
- return null;
1470
- }
1471
- let finalMessage = sanitized;
1472
- if (issueNumber) {
1473
- if (!finalMessage.includes(`Fixes #${issueNumber}`)) {
1474
- finalMessage = `${finalMessage}
1475
-
1476
- Fixes #${issueNumber}`;
1477
- getLogger().debug(`Added "Fixes #${issueNumber}" trailer to commit message`);
1478
- } else {
1479
- getLogger().debug(`"Fixes #${issueNumber}" already present in commit message`);
1480
- }
1481
- }
1482
- const totalDuration = Date.now() - startTime;
1483
- getLogger().info("Claude commit message generated successfully", {
1484
- message: finalMessage,
1485
- totalDuration: `${totalDuration}ms`,
1486
- claudeApiDuration: `${claudeDuration}ms`
1487
- });
1488
- return finalMessage;
1489
- } catch (error) {
1490
- const totalDuration = Date.now() - startTime;
1491
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
1492
- if (errorMessage.includes("timed out") || errorMessage.includes("timeout")) {
1493
- getLogger().warn("Claude commit message generation timed out after 45 seconds", {
1494
- totalDuration: `${totalDuration}ms`,
1495
- worktreePath: worktreePath.split("/").pop()
1496
- });
1497
- } else {
1498
- getLogger().warn("Failed to generate commit message with Claude", {
1499
- error: errorMessage,
1500
- totalDuration: `${totalDuration}ms`,
1501
- worktreePath: worktreePath.split("/").pop()
1502
- });
1503
- }
1504
- return null;
1505
- }
1506
- }
1507
- /**
1508
- * Build structured XML prompt for commit message generation
1509
- * Uses XML format for clear task definition and output expectations
1510
- */
1511
- buildCommitMessagePrompt(issueNumber) {
1512
- const issueContext = issueNumber ? `
1513
- <IssueContext>
1514
- This commit is associated with GitHub issue #${issueNumber}.
1515
- If the changes appear to resolve the issue, include "Fixes #${issueNumber}" at the end of the first line of commit message.
1516
- </IssueContext>` : "";
1517
- return `<Task>
1518
- You are a software engineer writing a commit message for this repository.
1519
- Examine the staged changes in the git repository and generate a concise, meaningful commit message.
1520
- </Task>
1521
-
1522
- <Requirements>
1523
- <Format>The first line must be a brief summary of the changes made as a full sentence. If it references an issue, include "Fixes #N" at the end of this line.
1524
-
1525
- Add 2 newlines, then add a bullet-point form description of the changes made, each change on a new line.</Format>
1526
- <Mood>Use imperative mood (e.g., "Add feature" not "Added feature")</Mood>
1527
- <Focus>Be specific about what was changed and why</Focus>
1528
- <Conciseness>Keep message under 72 characters for subject line when possible</Conciseness>
1529
- <NoMeta>CRITICAL: Do NOT include ANY explanatory text, analysis, or meta-commentary. Output ONLY the raw commit message.</NoMeta>
1530
- <Examples>
1531
- Good: "Add user authentication with JWT tokens. Fixes #42
1532
-
1533
- - Implement login and registration endpoints
1534
- - Secure routes with JWT middleware
1535
- - Update user model to store hashed passwords"
1536
- Good: "Fix navigation bug in sidebar menu."
1537
- Bad: "Based on the changes, I'll create: Add user authentication"
1538
- Bad: "Looking at the files, this commit should be: Fix navigation bug"
1539
- </Examples>
1540
- ${issueContext}
1541
- </Requirements>
1542
-
1543
- <Output>
1544
- IMPORTANT: Your entire response will be used directly as the git commit message.
1545
- Do not include any explanatory text before or after the commit message.
1546
- Start your response immediately with the commit message text.
1547
- </Output>`;
1548
- }
1549
- /**
1550
- * Sanitize Claude output to remove meta-commentary and clean formatting
1551
- * Handles cases where Claude includes explanatory text despite instructions
1552
- */
1553
- sanitizeClaudeOutput(rawOutput) {
1554
- let cleaned = rawOutput.trim();
1555
- const metaPatterns = [
1556
- /^.*?based on.*?changes.*?:/i,
1557
- /^.*?looking at.*?files.*?:/i,
1558
- /^.*?examining.*?:/i,
1559
- /^.*?analyzing.*?:/i,
1560
- /^.*?i'll.*?generate.*?:/i,
1561
- /^.*?let me.*?:/i,
1562
- /^.*?the commit message.*?should be.*?:/i,
1563
- /^.*?here.*?is.*?commit.*?message.*?:/i
1564
- ];
1565
- for (const pattern of metaPatterns) {
1566
- cleaned = cleaned.replace(pattern, "").trim();
1567
- }
1568
- if (cleaned.includes(":")) {
1569
- const colonIndex = cleaned.indexOf(":");
1570
- const beforeColon = cleaned.substring(0, colonIndex).trim().toLowerCase();
1571
- const metaIndicators = [
1572
- "here is the commit message",
1573
- "commit message",
1574
- "here is",
1575
- "the message should be",
1576
- "i suggest",
1577
- "my suggestion"
1578
- ];
1579
- const isMetaCommentary = metaIndicators.some((indicator) => beforeColon.includes(indicator));
1580
- if (isMetaCommentary) {
1581
- const afterColon = cleaned.substring(colonIndex + 1).trim();
1582
- if (afterColon && afterColon.length > 10) {
1583
- cleaned = afterColon;
1584
- }
1585
- }
1586
- }
1587
- if (cleaned.startsWith('"') && cleaned.endsWith('"') || cleaned.startsWith("'") && cleaned.endsWith("'")) {
1588
- cleaned = cleaned.slice(1, -1).trim();
1589
- }
1590
- return cleaned;
1591
- }
1592
- };
1593
-
1594
- // src/lib/BuildRunner.ts
1595
- var BuildRunner = class {
1596
- constructor(capabilityDetector) {
1597
- this.capabilityDetector = capabilityDetector ?? new ProjectCapabilityDetector();
1598
- }
1599
- /**
1600
- * Run build verification in the specified directory
1601
- * @param buildPath - Path where build should run (typically main worktree path)
1602
- * @param options - Build options
1603
- */
1604
- async runBuild(buildPath, options = {}) {
1605
- const startTime = Date.now();
1606
- try {
1607
- const pkgJson = await getPackageConfig(buildPath);
1608
- const hasBuildScript = hasScript(pkgJson, "build");
1609
- if (!hasBuildScript) {
1610
- getLogger().debug("Skipping build - no build script found");
1611
- return {
1612
- success: true,
1613
- skipped: true,
1614
- reason: "No build script found in package configuration",
1615
- duration: Date.now() - startTime
1616
- };
1617
- }
1618
- } catch (error) {
1619
- if (error instanceof Error && error.message.includes("package.json not found")) {
1620
- getLogger().debug("Skipping build - no package configuration found");
1621
- return {
1622
- success: true,
1623
- skipped: true,
1624
- reason: "No package configuration found in project",
1625
- duration: Date.now() - startTime
1626
- };
1627
- }
1628
- throw error;
1629
- }
1630
- const capabilities = await this.capabilityDetector.detectCapabilities(buildPath);
1631
- const isCLIProject = capabilities.capabilities.includes("cli");
1632
- if (!isCLIProject) {
1633
- getLogger().debug("Skipping build - not a CLI project (no bin field)");
1634
- return {
1635
- success: true,
1636
- skipped: true,
1637
- reason: "Project is not a CLI project (no bin field in package.json)",
1638
- duration: Date.now() - startTime
1639
- };
1640
- }
1641
- const packageManager = await detectPackageManager(buildPath);
1642
- if (options.dryRun) {
1643
- const command = packageManager === "npm" ? "npm run build" : `${packageManager} build`;
1644
- getLogger().info(`[DRY RUN] Would run: ${command}`);
1645
- return {
1646
- success: true,
1647
- skipped: false,
1648
- duration: Date.now() - startTime
1649
- };
1650
- }
1651
- getLogger().info("Running build...");
1652
- try {
1653
- await runScript("build", buildPath, [], { quiet: true });
1654
- getLogger().success("Build completed successfully");
1655
- return {
1656
- success: true,
1657
- skipped: false,
1658
- duration: Date.now() - startTime
1659
- };
1660
- } catch {
1661
- const runCommand = packageManager === "npm" ? "npm run build" : `${packageManager} build`;
1662
- throw new Error(
1663
- `Error: Build failed.
1664
- Fix build errors before proceeding.
1665
-
1666
- Run '${runCommand}' to see detailed errors.`
1667
- );
1668
- }
1669
- }
1670
- };
1671
-
1672
823
  // src/commands/finish.ts
1673
824
  import path3 from "path";
1674
825
  var FinishCommand = class {
@@ -1709,7 +860,7 @@ var FinishCommand = class {
1709
860
  const neonProvider = createNeonProviderFromSettings(settings);
1710
861
  const databaseManager = new DatabaseManager(neonProvider, environmentManager, databaseUrlEnvVarName);
1711
862
  const cliIsolationManager = new CLIIsolationManager();
1712
- const { DefaultBranchNamingService: DefaultBranchNamingService2 } = await import("./BranchNamingService-B5PVRR7F.js");
863
+ const { DefaultBranchNamingService: DefaultBranchNamingService2 } = await import("./BranchNamingService-FLPUUFOB.js");
1713
864
  this.loomManager ??= new LoomManager(
1714
865
  this.gitWorktreeManager,
1715
866
  this.issueTracker,
@@ -1768,6 +919,7 @@ var FinishCommand = class {
1768
919
  */
1769
920
  async execute(input) {
1770
921
  var _a, _b, _c, _d;
922
+ process.env.ILOOM = "1";
1771
923
  const isJsonMode = input.options.json === true;
1772
924
  const result = {
1773
925
  success: false,
@@ -2049,7 +1201,7 @@ var FinishCommand = class {
2049
1201
  * This is the workflow: rebase → validate → commit → merge → cleanup
2050
1202
  */
2051
1203
  async executeIssueWorkflow(parsed, options, worktree, result) {
2052
- var _a, _b, _c;
1204
+ var _a, _b, _c, _d;
2053
1205
  getLogger().info("Rebasing branch on main...");
2054
1206
  const mergeOptions = {
2055
1207
  dryRun: options.dryRun ?? false,
@@ -2094,9 +1246,12 @@ var FinishCommand = class {
2094
1246
  getLogger().info("Validation passed, auto-committing uncommitted changes...");
2095
1247
  const settings2 = await this.settingsManager.loadSettings(worktree.path);
2096
1248
  const skipVerify = ((_b = (_a = settings2.workflows) == null ? void 0 : _a.issue) == null ? void 0 : _b.noVerify) ?? false;
1249
+ const providerType = ((_c = settings2.issueManagement) == null ? void 0 : _c.provider) ?? "github";
1250
+ const issuePrefix = IssueManagementProviderFactory.create(providerType).issuePrefix;
2097
1251
  const commitOptions = {
2098
1252
  dryRun: options.dryRun ?? false,
2099
- skipVerify
1253
+ skipVerify,
1254
+ issuePrefix
2100
1255
  };
2101
1256
  if (parsed.type === "issue" && parsed.number) {
2102
1257
  commitOptions.issueNumber = parsed.number;
@@ -2142,9 +1297,9 @@ var FinishCommand = class {
2142
1297
  `The 'github-draft-pr' merge mode requires a GitHub-compatible issue tracker. Your provider (${this.issueTracker.providerName}) does not support pull requests.`
2143
1298
  );
2144
1299
  }
2145
- const { MetadataManager: MetadataManager2 } = await import("./MetadataManager-DFI73J3G.js");
2146
- const metadataManager = new MetadataManager2();
2147
- const metadata = await metadataManager.readMetadata(worktree.path);
1300
+ const { MetadataManager: MetadataManager3 } = await import("./MetadataManager-XJ2YB762.js");
1301
+ const metadataManager2 = new MetadataManager3();
1302
+ const metadata = await metadataManager2.readMetadata(worktree.path);
2148
1303
  getLogger().debug(`Draft PR mode: worktree=${worktree.path}, draftPrNumber=${(metadata == null ? void 0 : metadata.draftPrNumber) ?? "none"}`);
2149
1304
  if (!(metadata == null ? void 0 : metadata.draftPrNumber)) {
2150
1305
  getLogger().warn("No draft PR found in metadata, creating new PR...");
@@ -2209,7 +1364,7 @@ var FinishCommand = class {
2209
1364
  } else {
2210
1365
  getLogger().info(`[DRY RUN] Would mark PR #${metadata.draftPrNumber} as ready for review`);
2211
1366
  }
2212
- const prUrl = (_c = metadata.prUrls) == null ? void 0 : _c[String(metadata.draftPrNumber)];
1367
+ const prUrl = (_d = metadata.prUrls) == null ? void 0 : _d[String(metadata.draftPrNumber)];
2213
1368
  if (prUrl) {
2214
1369
  result.prUrl = prUrl;
2215
1370
  }
@@ -2243,7 +1398,17 @@ var FinishCommand = class {
2243
1398
  getLogger().debug("Skipping build verification (--skip-build flag provided)");
2244
1399
  }
2245
1400
  await this.generateSessionSummaryIfConfigured(parsed, worktree, options);
2246
- await this.performPostMergeCleanup(parsed, options, worktree, result);
1401
+ const { MetadataManager: MetadataManager2 } = await import("./MetadataManager-XJ2YB762.js");
1402
+ const metadataManager = new MetadataManager2();
1403
+ if (!options.dryRun) {
1404
+ await metadataManager.archiveMetadata(worktree.path);
1405
+ }
1406
+ if (options.cleanup === false) {
1407
+ getLogger().info("Worktree kept active (--no-cleanup flag)");
1408
+ getLogger().info(`To cleanup later: il cleanup ${parsed.originalInput}`);
1409
+ } else {
1410
+ await this.performPostMergeCleanup(parsed, options, worktree, result);
1411
+ }
2247
1412
  }
2248
1413
  /**
2249
1414
  * Execute workflow for Pull Requests
@@ -2252,7 +1417,7 @@ var FinishCommand = class {
2252
1417
  * - CLOSED/MERGED: Skip to cleanup
2253
1418
  */
2254
1419
  async executePRWorkflow(parsed, options, worktree, pr, result) {
2255
- var _a, _b;
1420
+ var _a, _b, _c;
2256
1421
  if (pr.state === "closed" || pr.state === "merged") {
2257
1422
  getLogger().info(`PR #${parsed.number} is ${pr.state.toUpperCase()} - skipping to cleanup`);
2258
1423
  const gitStatus = await this.commitManager.detectUncommittedChanges(worktree.path);
@@ -2262,6 +1427,11 @@ var FinishCommand = class {
2262
1427
  "Cannot cleanup PR with uncommitted changes. Commit or stash changes, then run again with --force to cleanup anyway."
2263
1428
  );
2264
1429
  }
1430
+ const { MetadataManager: MetadataManager2 } = await import("./MetadataManager-XJ2YB762.js");
1431
+ const metadataManager = new MetadataManager2();
1432
+ if (!options.dryRun) {
1433
+ await metadataManager.archiveMetadata(worktree.path);
1434
+ }
2265
1435
  await this.performPRCleanup(parsed, options, worktree, pr.state, result);
2266
1436
  getLogger().success(`PR #${parsed.number} cleanup completed`);
2267
1437
  result.operations.push({
@@ -2284,10 +1454,13 @@ var FinishCommand = class {
2284
1454
  getLogger().info("Committing uncommitted changes...");
2285
1455
  const settings = await this.settingsManager.loadSettings(worktree.path);
2286
1456
  const skipVerify = ((_b = (_a = settings.workflows) == null ? void 0 : _a.pr) == null ? void 0 : _b.noVerify) ?? false;
1457
+ const providerType = ((_c = settings.issueManagement) == null ? void 0 : _c.provider) ?? "github";
1458
+ const issuePrefix = IssueManagementProviderFactory.create(providerType).issuePrefix;
2287
1459
  try {
2288
1460
  await this.commitManager.commitChanges(worktree.path, {
2289
1461
  dryRun: false,
2290
- skipVerify
1462
+ skipVerify,
1463
+ issuePrefix
2291
1464
  // Do NOT pass issueNumber for PRs - no "Fixes #" trailer needed
2292
1465
  });
2293
1466
  getLogger().success("Changes committed");
@@ -2386,6 +1559,11 @@ var FinishCommand = class {
2386
1559
  }
2387
1560
  finishResult.prUrl = prResult.url;
2388
1561
  await this.generateSessionSummaryIfConfigured(parsed, worktree, options, prResult.number);
1562
+ const { MetadataManager: MetadataManager2 } = await import("./MetadataManager-XJ2YB762.js");
1563
+ const metadataManager = new MetadataManager2();
1564
+ if (!options.dryRun) {
1565
+ await metadataManager.archiveMetadata(worktree.path);
1566
+ }
2389
1567
  await this.handlePRCleanupPrompt(parsed, options, worktree, finishResult);
2390
1568
  }
2391
1569
  }
@@ -2740,7 +1918,7 @@ var FinishCommand = class {
2740
1918
  // src/utils/package-info.ts
2741
1919
  import { readFileSync } from "fs";
2742
1920
  import { fileURLToPath } from "url";
2743
- import { dirname, join as join2 } from "path";
1921
+ import { dirname, join } from "path";
2744
1922
  function getPackageInfo(scriptPath) {
2745
1923
  try {
2746
1924
  let basePath;
@@ -2751,7 +1929,7 @@ function getPackageInfo(scriptPath) {
2751
1929
  basePath = __filename2;
2752
1930
  }
2753
1931
  const __dirname = dirname(basePath);
2754
- const packageJsonPath = join2(__dirname, "..", "package.json");
1932
+ const packageJsonPath = join(__dirname, "..", "package.json");
2755
1933
  const packageJsonContent = readFileSync(packageJsonPath, "utf8");
2756
1934
  const packageJson2 = JSON.parse(packageJsonContent);
2757
1935
  return packageJson2;
@@ -2830,12 +2008,40 @@ function formatLoomForJson(worktree, mainWorktreePath, metadata) {
2830
2008
  colorHex: (metadata == null ? void 0 : metadata.colorHex) ?? null,
2831
2009
  projectPath: (metadata == null ? void 0 : metadata.projectPath) ?? null,
2832
2010
  issueUrls: (metadata == null ? void 0 : metadata.issueUrls) ?? {},
2833
- prUrls: (metadata == null ? void 0 : metadata.prUrls) ?? {}
2011
+ prUrls: (metadata == null ? void 0 : metadata.prUrls) ?? {},
2012
+ capabilities: (metadata == null ? void 0 : metadata.capabilities) ?? []
2834
2013
  };
2835
2014
  }
2836
2015
  function formatLoomsForJson(worktrees, mainWorktreePath, metadata) {
2837
2016
  return worktrees.map((wt) => formatLoomForJson(wt, mainWorktreePath, metadata == null ? void 0 : metadata.get(wt.path)));
2838
2017
  }
2018
+ function formatFinishedLoomForJson(metadata) {
2019
+ const loomType = metadata.issueType ?? "branch";
2020
+ return {
2021
+ name: metadata.branchName ?? metadata.worktreePath ?? "unknown",
2022
+ worktreePath: null,
2023
+ // Finished looms no longer have a worktree
2024
+ branch: metadata.branchName,
2025
+ type: loomType,
2026
+ issue_numbers: metadata.issue_numbers,
2027
+ pr_numbers: metadata.pr_numbers,
2028
+ isMainWorktree: false,
2029
+ // Finished looms are never the main worktree
2030
+ description: metadata.description ?? null,
2031
+ created_at: metadata.created_at ?? null,
2032
+ issueTracker: metadata.issueTracker ?? null,
2033
+ colorHex: metadata.colorHex ?? null,
2034
+ projectPath: metadata.projectPath ?? null,
2035
+ issueUrls: metadata.issueUrls ?? {},
2036
+ prUrls: metadata.prUrls ?? {},
2037
+ capabilities: metadata.capabilities ?? [],
2038
+ status: metadata.status ?? "finished",
2039
+ finishedAt: metadata.finishedAt ?? null
2040
+ };
2041
+ }
2042
+
2043
+ // src/cli.ts
2044
+ import fs3 from "fs-extra";
2839
2045
 
2840
2046
  // src/lib/VersionMigrationManager.ts
2841
2047
  import fs2 from "fs-extra";
@@ -3162,14 +2368,14 @@ async function autoLaunchInitForMultipleRemotes() {
3162
2368
  await waitForKeypress2("Press any key to start configuration...");
3163
2369
  logger.info("");
3164
2370
  try {
3165
- const { InitCommand: InitCommand2 } = await import("./init-UTYRHNJJ.js");
2371
+ const { InitCommand: InitCommand2 } = await import("./init-XQQMFDM6.js");
3166
2372
  const initCommand = new InitCommand2();
3167
2373
  const customInitialMessage = "Help me configure which git remote iloom should use for GitHub operations. I have multiple remotes and need to select the correct one.";
3168
2374
  await initCommand.execute(customInitialMessage);
3169
2375
  logger.info("");
3170
2376
  logger.info("Configuration complete! Continuing with your original command...");
3171
2377
  logger.info("");
3172
- const { SettingsManager: SettingsManager2 } = await import("./SettingsManager-35F5RUJH.js");
2378
+ const { SettingsManager: SettingsManager2 } = await import("./SettingsManager-YU4VYPTW.js");
3173
2379
  const settingsManager = new SettingsManager2();
3174
2380
  const settings = await settingsManager.loadSettings();
3175
2381
  const { hasMultipleRemotes: hasMultipleRemotes2 } = await import("./remote-IJAMOEAP.js");
@@ -3263,7 +2469,7 @@ program.command("add-issue").alias("a").description("Create and enhance GitHub i
3263
2469
  });
3264
2470
  program.command("feedback").alias("f").description("Submit feedback/bug report to iloom-cli repository").argument("<description>", "Feedback title (>30 chars, >2 spaces; or any non-empty text when --body provided)").option("--body <text>", "Body text for feedback (added after diagnostics)").action(async (description, options) => {
3265
2471
  try {
3266
- const { FeedbackCommand } = await import("./feedback-O4Q55SVS.js");
2472
+ const { FeedbackCommand } = await import("./feedback-G7G5QCY4.js");
3267
2473
  const command = new FeedbackCommand();
3268
2474
  const feedbackOptions = {};
3269
2475
  if (options.body !== void 0) {
@@ -3313,7 +2519,7 @@ program.command("enhance").description("Apply enhancement agent to existing GitH
3313
2519
  await executeAction();
3314
2520
  }
3315
2521
  });
3316
- program.command("finish").alias("dn").description("Merge work and cleanup workspace").argument("[identifier]", "Issue number, PR number, or branch name (auto-detected if omitted)").option("-f, --force", "Skip confirmation prompts").option("-n, --dry-run", "Preview actions without executing").option("--pr <number>", "Treat input as PR number", parseFloat).option("--skip-build", "Skip post-merge build verification").option("--no-browser", "Skip opening PR in browser (github-pr mode only)").option("--cleanup", "Clean up worktree after PR creation (github-pr mode only)").option("--no-cleanup", "Keep worktree after PR creation (github-pr mode only)").option("--json", "Output result as JSON").action(async (identifier, options) => {
2522
+ program.command("finish").alias("dn").description("Merge work and cleanup workspace").argument("[identifier]", "Issue number, PR number, or branch name (auto-detected if omitted)").option("-f, --force", "Skip confirmation prompts").option("-n, --dry-run", "Preview actions without executing").option("--pr <number>", "Treat input as PR number", parseFloat).option("--skip-build", "Skip post-merge build verification").option("--no-browser", "Skip opening PR in browser (github-pr mode only)").option("--cleanup", "Clean up worktree after finishing (default in local mode)").option("--no-cleanup", "Keep worktree after finishing").option("--json", "Output result as JSON").action(async (identifier, options) => {
3317
2523
  const executeAction = async () => {
3318
2524
  try {
3319
2525
  const settingsManager = new SettingsManager();
@@ -3344,9 +2550,45 @@ program.command("finish").alias("dn").description("Merge work and cleanup worksp
3344
2550
  await executeAction();
3345
2551
  }
3346
2552
  });
2553
+ program.command("commit").alias("c").description("Commit all uncommitted files with issue reference").option("-m, --message <text>", "Custom commit message (skip Claude generation)").option("--fixes", 'Use "Fixes #N" trailer instead of "Refs #N" (closes issue)').option("--no-review", "Skip commit message review prompt").option("--json", "Output result as JSON (implies --no-review)").option("--wip-commit", "Quick WIP commit: skip validations and pre-commit hooks").action(async (options) => {
2554
+ const executeAction = async () => {
2555
+ try {
2556
+ const { CommitCommand } = await import("./commit-6S2RIA2K.js");
2557
+ const command = new CommitCommand();
2558
+ const noReview = options.review === false || options.json === true;
2559
+ const result = await command.execute({
2560
+ message: options.message,
2561
+ fixes: options.fixes ?? false,
2562
+ noReview,
2563
+ json: options.json ?? false,
2564
+ wipCommit: options.wipCommit ?? false
2565
+ });
2566
+ if (options.json && result) {
2567
+ console.log(JSON.stringify(result, null, 2));
2568
+ }
2569
+ process.exit(0);
2570
+ } catch (error) {
2571
+ if (error instanceof UserAbortedCommitError) {
2572
+ process.exit(130);
2573
+ }
2574
+ if (options.json) {
2575
+ console.log(JSON.stringify({ success: false, error: error instanceof Error ? error.message : "Unknown error" }, null, 2));
2576
+ } else {
2577
+ logger.error(`Commit failed: ${error instanceof Error ? error.message : "Unknown error"}`);
2578
+ }
2579
+ process.exit(1);
2580
+ }
2581
+ };
2582
+ if (options.json) {
2583
+ const jsonLogger = createStderrLogger();
2584
+ await withLogger(jsonLogger, executeAction);
2585
+ } else {
2586
+ await executeAction();
2587
+ }
2588
+ });
3347
2589
  program.command("rebase").description("Rebase current branch on main with Claude-assisted conflict resolution").option("-f, --force", "Skip confirmation prompts").option("-n, --dry-run", "Preview actions without executing").action(async (options) => {
3348
2590
  try {
3349
- const { RebaseCommand } = await import("./rebase-Y4AS6LQW.js");
2591
+ const { RebaseCommand } = await import("./rebase-RKQED567.js");
3350
2592
  const command = new RebaseCommand();
3351
2593
  await command.execute(options);
3352
2594
  } catch (error) {
@@ -3358,7 +2600,7 @@ program.command("spin").alias("ignite").description("Launch Claude with auto-det
3358
2600
  new Option("--one-shot <mode>", "One-shot automation mode").choices(["default", "noReview", "bypassPermissions"]).default("default")
3359
2601
  ).action(async (options) => {
3360
2602
  try {
3361
- const { IgniteCommand } = await import("./ignite-VHV65WEZ.js");
2603
+ const { IgniteCommand } = await import("./ignite-YUAOJ5PP.js");
3362
2604
  const command = new IgniteCommand();
3363
2605
  await command.execute(options.oneShot ?? "default");
3364
2606
  } catch (error) {
@@ -3369,7 +2611,7 @@ program.command("spin").alias("ignite").description("Launch Claude with auto-det
3369
2611
  program.command("open").description("Open workspace in browser or run CLI tool").argument("[identifier]", "Issue number, PR number, or branch name (auto-detected if omitted)").allowUnknownOption().action(async (identifier, _options, command) => {
3370
2612
  try {
3371
2613
  const args = (command == null ? void 0 : command.args) ? command.args.slice(identifier ? 1 : 0) : [];
3372
- const { OpenCommand } = await import("./open-WHVUYGPY.js");
2614
+ const { OpenCommand } = await import("./open-MCWQAPSZ.js");
3373
2615
  const cmd = new OpenCommand();
3374
2616
  const input = identifier ? { identifier, args } : { args };
3375
2617
  await cmd.execute(input);
@@ -3381,7 +2623,7 @@ program.command("open").description("Open workspace in browser or run CLI tool")
3381
2623
  program.command("run").description("Run CLI tool or open workspace in browser").argument("[identifier]", "Issue number, PR number, or branch name (auto-detected if omitted)").allowUnknownOption().action(async (identifier, _options, command) => {
3382
2624
  try {
3383
2625
  const args = (command == null ? void 0 : command.args) ? command.args.slice(identifier ? 1 : 0) : [];
3384
- const { RunCommand } = await import("./run-NCRK5NPR.js");
2626
+ const { RunCommand } = await import("./run-CCG24PBC.js");
3385
2627
  const cmd = new RunCommand();
3386
2628
  const input = identifier ? { identifier, args } : { args };
3387
2629
  await cmd.execute(input);
@@ -3392,7 +2634,7 @@ program.command("run").description("Run CLI tool or open workspace in browser").
3392
2634
  });
3393
2635
  program.command("dev-server").alias("dev").description("Start dev server for workspace (foreground)").argument("[identifier]", "Issue number, PR number, or branch name (auto-detected if omitted)").option("--json", "Output as JSON").action(async (identifier, options) => {
3394
2636
  try {
3395
- const { DevServerCommand } = await import("./dev-server-4RCDJ5MU.js");
2637
+ const { DevServerCommand } = await import("./dev-server-GREJUEKW.js");
3396
2638
  const cmd = new DevServerCommand();
3397
2639
  await cmd.execute({ identifier, json: options == null ? void 0 : options.json });
3398
2640
  } catch (error) {
@@ -3402,7 +2644,7 @@ program.command("dev-server").alias("dev").description("Start dev server for wor
3402
2644
  });
3403
2645
  program.command("shell").alias("terminal").description("Open interactive shell with workspace environment").argument("[identifier]", "Issue number, PR number, or branch name (auto-detected if omitted)").action(async (identifier) => {
3404
2646
  try {
3405
- const { ShellCommand } = await import("./shell-SBLXVOVJ.js");
2647
+ const { ShellCommand } = await import("./shell-2NNSIU34.js");
3406
2648
  const cmd = new ShellCommand();
3407
2649
  await cmd.execute({ identifier });
3408
2650
  } catch (error) {
@@ -3412,7 +2654,7 @@ program.command("shell").alias("terminal").description("Open interactive shell w
3412
2654
  });
3413
2655
  program.command("build").description("Run the build script").argument("[identifier]", "Issue number, PR number, or branch name (auto-detected if omitted)").action(async (identifier) => {
3414
2656
  try {
3415
- const { BuildCommand } = await import("./build-FJVYP7EV.js");
2657
+ const { BuildCommand } = await import("./build-O2EJHDEW.js");
3416
2658
  const cmd = new BuildCommand();
3417
2659
  await cmd.execute(identifier ? { identifier } : {});
3418
2660
  } catch (error) {
@@ -3422,7 +2664,7 @@ program.command("build").description("Run the build script").argument("[identifi
3422
2664
  });
3423
2665
  program.command("lint").description("Run the lint script").argument("[identifier]", "Issue number, PR number, or branch name (auto-detected if omitted)").action(async (identifier) => {
3424
2666
  try {
3425
- const { LintCommand } = await import("./lint-5JMCWE4Y.js");
2667
+ const { LintCommand } = await import("./lint-OFVN7FT6.js");
3426
2668
  const cmd = new LintCommand();
3427
2669
  await cmd.execute(identifier ? { identifier } : {});
3428
2670
  } catch (error) {
@@ -3432,7 +2674,7 @@ program.command("lint").description("Run the lint script").argument("[identifier
3432
2674
  });
3433
2675
  program.command("test").description("Run the test script").argument("[identifier]", "Issue number, PR number, or branch name (auto-detected if omitted)").action(async (identifier) => {
3434
2676
  try {
3435
- const { TestCommand } = await import("./test-3KIVXI6J.js");
2677
+ const { TestCommand } = await import("./test-QZDOEUIO.js");
3436
2678
  const cmd = new TestCommand();
3437
2679
  await cmd.execute(identifier ? { identifier } : {});
3438
2680
  } catch (error) {
@@ -3442,7 +2684,7 @@ program.command("test").description("Run the test script").argument("[identifier
3442
2684
  });
3443
2685
  program.command("compile").alias("typecheck").description("Run the compile or typecheck script (prefers compile if both exist)").argument("[identifier]", "Issue number, PR number, or branch name (auto-detected if omitted)").action(async (identifier) => {
3444
2686
  try {
3445
- const { CompileCommand } = await import("./compile-ULNO5F7Q.js");
2687
+ const { CompileCommand } = await import("./compile-LRMAADUT.js");
3446
2688
  const cmd = new CompileCommand();
3447
2689
  await cmd.execute(identifier ? { identifier } : {});
3448
2690
  } catch (error) {
@@ -3450,10 +2692,10 @@ program.command("compile").alias("typecheck").description("Run the compile or ty
3450
2692
  process.exit(1);
3451
2693
  }
3452
2694
  });
3453
- program.command("cleanup").alias("remove").alias("clean").description("Remove workspaces").argument("[identifier]", "Branch name or issue number to cleanup (auto-detected)").option("-l, --list", "List all worktrees").option("-a, --all", "Remove all worktrees (interactive confirmation)").option("-i, --issue <number>", "Cleanup by issue number", parseInt).option("-f, --force", "Skip confirmations and force removal").option("--dry-run", "Show what would be done without doing it").option("--json", "Output result as JSON").action(async (identifier, options) => {
2695
+ program.command("cleanup").alias("remove").alias("clean").description("Remove workspaces").argument("[identifier]", "Branch name or issue number to cleanup (auto-detected)").option("-l, --list", "List all worktrees").option("-a, --all", "Remove all worktrees (interactive confirmation)").option("-i, --issue <number>", "Cleanup by issue number", parseInt).option("-f, --force", "Skip confirmations and force removal").option("--dry-run", "Show what would be done without doing it").option("--json", "Output result as JSON").option("--defer <ms>", "Wait specified milliseconds before cleanup", parseInt).action(async (identifier, options) => {
3454
2696
  const executeAction = async () => {
3455
2697
  try {
3456
- const { CleanupCommand } = await import("./cleanup-BRUAINKE.js");
2698
+ const { CleanupCommand } = await import("./cleanup-ZPOMRSNN.js");
3457
2699
  const command = new CleanupCommand();
3458
2700
  const input = {
3459
2701
  options: options ?? {}
@@ -3482,15 +2724,78 @@ program.command("cleanup").alias("remove").alias("clean").description("Remove wo
3482
2724
  await executeAction();
3483
2725
  }
3484
2726
  });
3485
- program.command("list").description("Show active workspaces").option("--json", "Output as JSON").action(async (options) => {
2727
+ program.command("list").description("Show active workspaces").option("--json", "Output as JSON").option("--finished", "Show only finished looms (sorted by finish time, latest first)").option("--all", "Show both active and finished looms").option("--global", "Show looms from all projects (default: current project only)").action(async (options) => {
3486
2728
  try {
3487
2729
  const manager = new GitWorktreeManager();
3488
2730
  const metadataManager = new MetadataManager();
3489
- const worktrees = await manager.listWorktrees({ porcelain: true });
2731
+ let currentProjectPath = null;
2732
+ if (!options.global) {
2733
+ try {
2734
+ currentProjectPath = await findMainWorktreePathWithSettings();
2735
+ } catch (error) {
2736
+ if (error instanceof GitCommandError && (error.exitCode === 128 || /fatal: not a git repository/i.test(error.stderr))) {
2737
+ currentProjectPath = null;
2738
+ } else if (error instanceof Error && error.message.includes("Invalid settings")) {
2739
+ currentProjectPath = null;
2740
+ } else {
2741
+ throw error;
2742
+ }
2743
+ }
2744
+ }
2745
+ const showActive = !options.finished;
2746
+ const showFinished = Boolean(options.finished) || Boolean(options.all);
2747
+ let worktrees = [];
3490
2748
  const metadata = /* @__PURE__ */ new Map();
3491
- for (const worktree of worktrees) {
3492
- const loomMetadata = await metadataManager.readMetadata(worktree.path);
3493
- metadata.set(worktree.path, loomMetadata);
2749
+ let globalActiveLooms = [];
2750
+ if (showActive) {
2751
+ if (options.global) {
2752
+ const allMetadata = await metadataManager.listAllMetadata();
2753
+ for (const loom of allMetadata) {
2754
+ if (!loom.worktreePath) {
2755
+ continue;
2756
+ }
2757
+ const pathExists = await fs3.pathExists(loom.worktreePath);
2758
+ if (!pathExists) {
2759
+ continue;
2760
+ }
2761
+ const isValid = await isValidGitRepo(loom.worktreePath);
2762
+ if (!isValid) {
2763
+ continue;
2764
+ }
2765
+ globalActiveLooms.push(loom);
2766
+ metadata.set(loom.worktreePath, loom);
2767
+ }
2768
+ } else {
2769
+ try {
2770
+ worktrees = await manager.listWorktrees({ porcelain: true });
2771
+ for (const worktree of worktrees) {
2772
+ const loomMetadata = await metadataManager.readMetadata(worktree.path);
2773
+ metadata.set(worktree.path, loomMetadata);
2774
+ }
2775
+ } catch (error) {
2776
+ if (error instanceof GitCommandError && (error.exitCode === 128 || /fatal: not a git repository/i.test(error.stderr))) {
2777
+ worktrees = [];
2778
+ } else {
2779
+ throw error;
2780
+ }
2781
+ }
2782
+ }
2783
+ }
2784
+ let finishedLooms = [];
2785
+ if (showFinished) {
2786
+ finishedLooms = await metadataManager.listFinishedMetadata();
2787
+ }
2788
+ let filteredWorktrees = worktrees;
2789
+ let filteredGlobalActiveLooms = globalActiveLooms;
2790
+ let filteredFinishedLooms = finishedLooms;
2791
+ if (currentProjectPath) {
2792
+ filteredWorktrees = worktrees.filter((wt) => {
2793
+ const loomMetadata = metadata.get(wt.path);
2794
+ return (loomMetadata == null ? void 0 : loomMetadata.projectPath) == null || (loomMetadata == null ? void 0 : loomMetadata.projectPath) === currentProjectPath;
2795
+ });
2796
+ filteredFinishedLooms = finishedLooms.filter(
2797
+ (loom) => loom.projectPath == null || loom.projectPath === currentProjectPath
2798
+ );
3494
2799
  }
3495
2800
  if (options.json) {
3496
2801
  let mainWorktreePath;
@@ -3498,26 +2803,108 @@ program.command("list").description("Show active workspaces").option("--json", "
3498
2803
  mainWorktreePath = await findMainWorktreePathWithSettings();
3499
2804
  } catch {
3500
2805
  }
3501
- console.log(JSON.stringify(formatLoomsForJson(worktrees, mainWorktreePath, metadata), null, 2));
2806
+ let activeJson = [];
2807
+ if (showActive) {
2808
+ if (options.global) {
2809
+ activeJson = globalActiveLooms.map((loom) => ({
2810
+ name: loom.branchName ?? loom.worktreePath ?? "unknown",
2811
+ worktreePath: loom.worktreePath,
2812
+ branch: loom.branchName,
2813
+ type: loom.issueType ?? "branch",
2814
+ issue_numbers: loom.issue_numbers,
2815
+ pr_numbers: loom.pr_numbers,
2816
+ isMainWorktree: false,
2817
+ // Global looms from other projects are never the main worktree
2818
+ description: loom.description ?? null,
2819
+ created_at: loom.created_at ?? null,
2820
+ issueTracker: loom.issueTracker ?? null,
2821
+ colorHex: loom.colorHex ?? null,
2822
+ projectPath: loom.projectPath ?? null,
2823
+ issueUrls: loom.issueUrls ?? {},
2824
+ prUrls: loom.prUrls ?? {},
2825
+ status: "active",
2826
+ finishedAt: null
2827
+ }));
2828
+ } else {
2829
+ activeJson = formatLoomsForJson(worktrees, mainWorktreePath, metadata).map((loom) => ({
2830
+ ...loom,
2831
+ status: "active",
2832
+ finishedAt: null
2833
+ }));
2834
+ }
2835
+ }
2836
+ if (currentProjectPath && !options.global) {
2837
+ activeJson = activeJson.filter(
2838
+ (loom) => loom.projectPath == null || loom.projectPath === currentProjectPath
2839
+ );
2840
+ }
2841
+ let finishedJson = finishedLooms.map(formatFinishedLoomForJson);
2842
+ if (currentProjectPath) {
2843
+ finishedJson = finishedJson.filter(
2844
+ (loom) => loom.projectPath == null || loom.projectPath === currentProjectPath
2845
+ );
2846
+ }
2847
+ const allLooms = [...activeJson, ...finishedJson];
2848
+ console.log(JSON.stringify(allLooms, null, 2));
3502
2849
  return;
3503
2850
  }
3504
- if (worktrees.length === 0) {
3505
- logger.info("No worktrees found");
2851
+ const hasActive = options.global ? filteredGlobalActiveLooms.length > 0 : filteredWorktrees.length > 0;
2852
+ const hasFinished = filteredFinishedLooms.length > 0;
2853
+ if (!hasActive && !hasFinished) {
2854
+ if (options.finished) {
2855
+ logger.info("No finished looms found");
2856
+ } else if (options.all) {
2857
+ logger.info("No looms found");
2858
+ } else {
2859
+ logger.info("No worktrees found");
2860
+ }
3506
2861
  return;
3507
2862
  }
3508
- logger.info("Active workspaces:");
3509
- for (const worktree of worktrees) {
3510
- const formatted = manager.formatWorktree(worktree);
3511
- const loomMetadata = metadata.get(worktree.path);
3512
- logger.info(` ${formatted.title}`);
3513
- if (loomMetadata == null ? void 0 : loomMetadata.description) {
3514
- logger.info(` Description: ${loomMetadata.description}`);
2863
+ if (showActive && hasActive) {
2864
+ logger.info("Active workspaces:");
2865
+ if (options.global) {
2866
+ for (const loom of filteredGlobalActiveLooms) {
2867
+ logger.info(` ${loom.branchName ?? "unknown"}`);
2868
+ if (loom.description) {
2869
+ logger.info(` Description: ${loom.description}`);
2870
+ }
2871
+ if (loom.worktreePath) {
2872
+ logger.info(` Path: ${loom.worktreePath}`);
2873
+ }
2874
+ if (loom.projectPath) {
2875
+ logger.info(` Project: ${loom.projectPath}`);
2876
+ }
2877
+ }
2878
+ } else {
2879
+ for (const worktree of filteredWorktrees) {
2880
+ const formatted = manager.formatWorktree(worktree);
2881
+ const loomMetadata = metadata.get(worktree.path);
2882
+ logger.info(` ${formatted.title}`);
2883
+ if (loomMetadata == null ? void 0 : loomMetadata.description) {
2884
+ logger.info(` Description: ${loomMetadata.description}`);
2885
+ }
2886
+ logger.info(` Path: ${formatted.path}`);
2887
+ logger.info(` Commit: ${formatted.commit}`);
2888
+ }
2889
+ }
2890
+ }
2891
+ if (showFinished && hasFinished) {
2892
+ if (showActive && hasActive) {
2893
+ logger.info("");
2894
+ }
2895
+ logger.info("Finished looms:");
2896
+ for (const loom of filteredFinishedLooms) {
2897
+ logger.info(` ${loom.branchName ?? "unknown"}`);
2898
+ if (loom.description) {
2899
+ logger.info(` Description: ${loom.description}`);
2900
+ }
2901
+ if (loom.finishedAt) {
2902
+ logger.info(` Finished: ${new Date(loom.finishedAt).toLocaleString()}`);
2903
+ }
3515
2904
  }
3516
- logger.info(` Path: ${formatted.path}`);
3517
- logger.info(` Commit: ${formatted.commit}`);
3518
2905
  }
3519
2906
  } catch (error) {
3520
- if (error instanceof Error && error.message.includes("not a git repository")) {
2907
+ if (error instanceof GitCommandError && (error.exitCode === 128 || /fatal: not a git repository/i.test(error.stderr))) {
3521
2908
  if (options.json) {
3522
2909
  console.log("[]");
3523
2910
  } else {
@@ -3531,7 +2918,7 @@ program.command("list").description("Show active workspaces").option("--json", "
3531
2918
  });
3532
2919
  program.command("projects").description("List configured iloom projects").option("--json", "Output as JSON (default behavior)").action(async (options) => {
3533
2920
  try {
3534
- const { ProjectsCommand } = await import("./projects-SA76I4TZ.js");
2921
+ const { ProjectsCommand } = await import("./projects-PQOTWUII.js");
3535
2922
  const command = new ProjectsCommand();
3536
2923
  const result = await command.execute(options);
3537
2924
  console.log(JSON.stringify(result, null, 2));
@@ -3542,7 +2929,7 @@ program.command("projects").description("List configured iloom projects").option
3542
2929
  });
3543
2930
  program.command("init").alias("config").description("Initialize iloom configuration").argument("[prompt]", 'Custom initial message to send to Claude (defaults to "Help me configure iloom settings.")').action(async (prompt) => {
3544
2931
  try {
3545
- const { InitCommand: InitCommand2 } = await import("./init-UTYRHNJJ.js");
2932
+ const { InitCommand: InitCommand2 } = await import("./init-XQQMFDM6.js");
3546
2933
  const command = new InitCommand2();
3547
2934
  const trimmedPrompt = prompt == null ? void 0 : prompt.trim();
3548
2935
  const customPrompt = trimmedPrompt && trimmedPrompt.length > 0 ? trimmedPrompt : void 0;
@@ -3554,7 +2941,7 @@ program.command("init").alias("config").description("Initialize iloom configurat
3554
2941
  });
3555
2942
  program.command("contribute").description("Set up local development environment for contributing to a GitHub project").argument("[repository]", "GitHub repository (owner/repo, github.com/owner/repo, or full URL). Defaults to iloom-ai/iloom-cli").action(async (repository) => {
3556
2943
  try {
3557
- const { ContributeCommand } = await import("./contribute-Q6GX6AXK.js");
2944
+ const { ContributeCommand } = await import("./contribute-GXKOIA42.js");
3558
2945
  const command = new ContributeCommand();
3559
2946
  await command.execute(repository);
3560
2947
  } catch (error) {
@@ -3574,8 +2961,8 @@ program.command("update").description("Update iloom-cli to the latest version").
3574
2961
  });
3575
2962
  program.command("test-github").description("Test GitHub integration (Issue #3)").argument("<identifier>", "Issue number or PR number").option("--no-claude", "Skip Claude for branch name generation").action(async (identifier, options) => {
3576
2963
  try {
3577
- const { GitHubService: GitHubService2 } = await import("./GitHubService-S2OGUTDR.js");
3578
- const { DefaultBranchNamingService: DefaultBranchNamingService2 } = await import("./BranchNamingService-B5PVRR7F.js");
2964
+ const { GitHubService: GitHubService2 } = await import("./GitHubService-O7U4UQ7N.js");
2965
+ const { DefaultBranchNamingService: DefaultBranchNamingService2 } = await import("./BranchNamingService-FLPUUFOB.js");
3579
2966
  logger.info("Testing GitHub Integration\n");
3580
2967
  const service = new GitHubService2();
3581
2968
  const branchNaming = new DefaultBranchNamingService2({ useClaude: options.claude !== false });
@@ -3633,14 +3020,14 @@ program.command("test-github").description("Test GitHub integration (Issue #3)")
3633
3020
  });
3634
3021
  program.command("test-claude").description("Test Claude integration (Issue #10)").option("--detect", "Test Claude CLI detection").option("--version", "Get Claude CLI version").option("--branch <title>", "Test branch name generation with given title").option("--issue <number>", "Issue number for branch generation", "123").option("--launch <prompt>", "Launch Claude with a prompt (headless)").option("--interactive", "Launch Claude interactively (requires --launch)").option("--template <name>", "Test template loading").action(async (options) => {
3635
3022
  try {
3636
- const { detectClaudeCli: detectClaudeCli2, getClaudeVersion, generateBranchName, launchClaude: launchClaude2 } = await import("./claude-H33OQMXO.js");
3637
- const { PromptTemplateManager } = await import("./PromptTemplateManager-C3DK6XZL.js");
3638
- const { ClaudeService } = await import("./ClaudeService-O2PB22GX.js");
3639
- const { ClaudeContextManager: ClaudeContextManager2 } = await import("./ClaudeContextManager-6J2EB4QU.js");
3023
+ const { detectClaudeCli, getClaudeVersion, generateBranchName, launchClaude: launchClaude2 } = await import("./claude-6H36IBHO.js");
3024
+ const { PromptTemplateManager } = await import("./PromptTemplateManager-7L3HJQQU.js");
3025
+ const { ClaudeService } = await import("./ClaudeService-CRSETT3A.js");
3026
+ const { ClaudeContextManager: ClaudeContextManager2 } = await import("./ClaudeContextManager-KE5TBZVZ.js");
3640
3027
  logger.info("Testing Claude Integration\n");
3641
3028
  if (options.detect) {
3642
3029
  logger.info("Detecting Claude CLI...");
3643
- const isAvailable = await detectClaudeCli2();
3030
+ const isAvailable = await detectClaudeCli();
3644
3031
  if (isAvailable) {
3645
3032
  logger.success(" Claude CLI is available");
3646
3033
  } else {
@@ -3696,7 +3083,7 @@ program.command("test-claude").description("Test Claude integration (Issue #10)"
3696
3083
  if (!options.detect && !options.version && !options.branch && !options.launch && !options.template) {
3697
3084
  logger.info("Running full Claude integration test suite...\n");
3698
3085
  logger.info("1. Testing Claude CLI detection...");
3699
- const isAvailable = await detectClaudeCli2();
3086
+ const isAvailable = await detectClaudeCli();
3700
3087
  if (isAvailable) {
3701
3088
  logger.success(" Claude CLI is available");
3702
3089
  } else {
@@ -3771,7 +3158,7 @@ program.command("test-claude").description("Test Claude integration (Issue #10)"
3771
3158
  });
3772
3159
  program.command("test-webserver").description("Test if a web server is running on a workspace port").argument("<issue-number>", "Issue number (port will be calculated as 3000 + issue number)", parseInt).option("--kill", "Kill the web server if detected").action(async (issueNumber, options) => {
3773
3160
  try {
3774
- const { TestWebserverCommand } = await import("./test-webserver-YVQD42W6.js");
3161
+ const { TestWebserverCommand } = await import("./test-webserver-NRMGT2HB.js");
3775
3162
  const command = new TestWebserverCommand();
3776
3163
  await command.execute({ issueNumber, options });
3777
3164
  } catch (error) {
@@ -3784,7 +3171,7 @@ program.command("test-webserver").description("Test if a web server is running o
3784
3171
  });
3785
3172
  program.command("test-git").description("Test Git integration - findMainWorktreePath() function (reads .iloom/settings.json)").action(async () => {
3786
3173
  try {
3787
- const { TestGitCommand } = await import("./test-git-ZB6AGGRW.js");
3174
+ const { TestGitCommand } = await import("./test-git-E2BLXR6M.js");
3788
3175
  const command = new TestGitCommand();
3789
3176
  await command.execute();
3790
3177
  } catch (error) {
@@ -3810,7 +3197,7 @@ program.command("test-tabs").description("Test iTerm2 dual tab functionality - o
3810
3197
  });
3811
3198
  program.command("test-prefix").description("Test worktree prefix configuration - preview worktree paths (reads .iloom/settings.json)").action(async () => {
3812
3199
  try {
3813
- const { TestPrefixCommand } = await import("./test-prefix-FBGXKMPA.js");
3200
+ const { TestPrefixCommand } = await import("./test-prefix-A7JGGYAA.js");
3814
3201
  const command = new TestPrefixCommand();
3815
3202
  await command.execute();
3816
3203
  } catch (error) {
@@ -3824,7 +3211,7 @@ program.command("test-prefix").description("Test worktree prefix configuration -
3824
3211
  program.command("summary").description("Generate Claude session summary for a loom").argument("[identifier]", "Issue number, PR number (pr/123), or branch name (auto-detected if omitted)").option("--with-comment", "Post summary as a comment to the issue/PR").option("--json", "Output result as JSON").action(async (identifier, options) => {
3825
3212
  const executeAction = async () => {
3826
3213
  try {
3827
- const { SummaryCommand } = await import("./summary-CVFAMDOJ.js");
3214
+ const { SummaryCommand } = await import("./summary-G6L3VAKK.js");
3828
3215
  const command = new SummaryCommand();
3829
3216
  const result = await command.execute({ identifier, options });
3830
3217
  if (options.json && result) {
@@ -3853,7 +3240,7 @@ program.command("summary").description("Generate Claude session summary for a lo
3853
3240
  program.command("recap").description("Get recap for a loom (defaults to current directory)").argument("[identifier]", "Issue number, PR number (pr/123), or branch name (auto-detected if omitted)").option("--json", "Output as JSON with filePath for file watching").action(async (identifier, options) => {
3854
3241
  const executeAction = async () => {
3855
3242
  try {
3856
- const { RecapCommand } = await import("./recap-VOOUXOGP.js");
3243
+ const { RecapCommand } = await import("./recap-ZKGHZCX6.js");
3857
3244
  const command = new RecapCommand();
3858
3245
  const result = await command.execute({ identifier, json: options.json });
3859
3246
  if (options.json && result) {
@@ -3882,7 +3269,7 @@ program.command("recap").description("Get recap for a loom (defaults to current
3882
3269
  program.command("test-neon").description("Test Neon integration and debug configuration").action(async () => {
3883
3270
  var _a;
3884
3271
  try {
3885
- const { SettingsManager: SettingsManager2 } = await import("./SettingsManager-35F5RUJH.js");
3272
+ const { SettingsManager: SettingsManager2 } = await import("./SettingsManager-YU4VYPTW.js");
3886
3273
  const { createNeonProviderFromSettings: createNeonProviderFromSettings2 } = await import("./neon-helpers-3KBC4A3Y.js");
3887
3274
  logger.info("Testing Neon Integration\n");
3888
3275
  logger.info("1. Settings Configuration:");