@iloom/cli 0.3.4 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/README.md +13 -3
  2. package/dist/{BranchNamingService-A77VI6AI.js → BranchNamingService-GCCWB3LK.js} +4 -3
  3. package/dist/ClaudeContextManager-DK77227F.js +16 -0
  4. package/dist/ClaudeService-W3SA7HVG.js +15 -0
  5. package/dist/GitHubService-RPM27GWD.js +12 -0
  6. package/dist/{LoomLauncher-ZV3ZZIBA.js → LoomLauncher-S3YGJRJQ.js} +43 -27
  7. package/dist/LoomLauncher-S3YGJRJQ.js.map +1 -0
  8. package/dist/PromptTemplateManager-2TDZAUC6.js +9 -0
  9. package/dist/README.md +13 -3
  10. package/dist/{SettingsManager-I2LRCW2A.js → SettingsManager-FJFU6JJD.js} +7 -3
  11. package/dist/SettingsMigrationManager-EH3J2TCN.js +10 -0
  12. package/dist/{chunk-5Q3NDNNV.js → chunk-2W2FBL5G.js} +153 -6
  13. package/dist/chunk-2W2FBL5G.js.map +1 -0
  14. package/dist/{chunk-OXAM2WVC.js → chunk-55TB3FSG.js} +21 -1
  15. package/dist/chunk-55TB3FSG.js.map +1 -0
  16. package/dist/chunk-6UIGZD2N.js +20 -0
  17. package/dist/chunk-6UIGZD2N.js.map +1 -0
  18. package/dist/{chunk-RIEO2WML.js → chunk-74VMN2KC.js} +26 -2
  19. package/dist/chunk-74VMN2KC.js.map +1 -0
  20. package/dist/{chunk-2MAIX45J.js → chunk-BIIQHEXJ.js} +104 -43
  21. package/dist/chunk-BIIQHEXJ.js.map +1 -0
  22. package/dist/{chunk-UAN4A3YU.js → chunk-G6CIIJLT.js} +11 -11
  23. package/dist/{chunk-DLHA5VQ3.js → chunk-HD5SUKI2.js} +36 -179
  24. package/dist/chunk-HD5SUKI2.js.map +1 -0
  25. package/dist/{chunk-2IJEMXOB.js → chunk-IARWMDAX.js} +427 -428
  26. package/dist/chunk-IARWMDAX.js.map +1 -0
  27. package/dist/chunk-IJ7IGJT3.js +192 -0
  28. package/dist/chunk-IJ7IGJT3.js.map +1 -0
  29. package/dist/{chunk-2CXREBLZ.js → chunk-JC5HXN75.js} +8 -6
  30. package/dist/chunk-JC5HXN75.js.map +1 -0
  31. package/dist/{chunk-3RUPPQRG.js → chunk-KO2FOMHL.js} +43 -2
  32. package/dist/{chunk-3RUPPQRG.js.map → chunk-KO2FOMHL.js.map} +1 -1
  33. package/dist/{chunk-4XIDC3NF.js → chunk-MD6HA5IK.js} +2 -2
  34. package/dist/{chunk-OC4H6HJD.js → chunk-O7WHXLCB.js} +2 -2
  35. package/dist/{chunk-M7JJCX53.js → chunk-OEGECBFS.js} +20 -20
  36. package/dist/chunk-OEGECBFS.js.map +1 -0
  37. package/dist/{chunk-MKWYLDFK.js → chunk-OF7BNW4D.js} +43 -3
  38. package/dist/chunk-OF7BNW4D.js.map +1 -0
  39. package/dist/{chunk-PGPI5LR4.js → chunk-POI7KLBH.js} +7 -21
  40. package/dist/chunk-POI7KLBH.js.map +1 -0
  41. package/dist/{chunk-PA6Q6AWM.js → chunk-PSFVTBM7.js} +2 -2
  42. package/dist/chunk-QHA67Q7A.js +281 -0
  43. package/dist/chunk-QHA67Q7A.js.map +1 -0
  44. package/dist/{chunk-SUOXY5WJ.js → chunk-QIUJPPJQ.js} +5 -5
  45. package/dist/chunk-QIUJPPJQ.js.map +1 -0
  46. package/dist/{chunk-ZM3CFL5L.js → chunk-QRBOPFAA.js} +3 -3
  47. package/dist/{chunk-OYF4VIFI.js → chunk-RUC7OULH.js} +147 -22
  48. package/dist/chunk-RUC7OULH.js.map +1 -0
  49. package/dist/{chunk-CE26YH2U.js → chunk-SJ2GZ6RF.js} +48 -50
  50. package/dist/chunk-SJ2GZ6RF.js.map +1 -0
  51. package/dist/{chunk-SSCQCCJ7.js → chunk-THF25ICZ.js} +2 -2
  52. package/dist/chunk-TMZAVPGF.js +667 -0
  53. package/dist/chunk-TMZAVPGF.js.map +1 -0
  54. package/dist/{chunk-5VK4NRSF.js → chunk-UNXRACJ7.js} +35 -36
  55. package/dist/chunk-UNXRACJ7.js.map +1 -0
  56. package/dist/{chunk-AKUJXDNW.js → chunk-UPUAQYAW.js} +3 -3
  57. package/dist/{chunk-GEHQXLEI.js → chunk-UYVWLISQ.js} +18 -35
  58. package/dist/chunk-UYVWLISQ.js.map +1 -0
  59. package/dist/{chunk-OSCLCMDG.js → chunk-UYWAESOT.js} +3 -3
  60. package/dist/{chunk-RW54ZMBM.js → chunk-VAYGNQTE.js} +2 -2
  61. package/dist/{chunk-ZT3YZB4K.js → chunk-VBFDVGAE.js} +12 -12
  62. package/dist/chunk-VBFDVGAE.js.map +1 -0
  63. package/dist/{chunk-IFB4Z76W.js → chunk-VTXCGKV5.js} +13 -12
  64. package/dist/chunk-VTXCGKV5.js.map +1 -0
  65. package/dist/{chunk-CDZERT7Z.js → chunk-VWNS6DH5.js} +48 -4
  66. package/dist/chunk-VWNS6DH5.js.map +1 -0
  67. package/dist/{chunk-CFFQ2Z7A.js → chunk-WUQQNE63.js} +2 -2
  68. package/dist/{chunk-UJL4HI2R.js → chunk-Z5NXYJIG.js} +20 -2
  69. package/dist/chunk-Z5NXYJIG.js.map +1 -0
  70. package/dist/{claude-W52VKI6L.js → claude-ACVXNB6N.js} +8 -5
  71. package/dist/{cleanup-H4VXU3C3.js → cleanup-KDLVTT7M.js} +133 -122
  72. package/dist/cleanup-KDLVTT7M.js.map +1 -0
  73. package/dist/cli.js +953 -430
  74. package/dist/cli.js.map +1 -1
  75. package/dist/{color-F7RU6B6Z.js → color-ZPIIUADB.js} +3 -3
  76. package/dist/{contribute-Y7IQV5QY.js → contribute-HY372S6F.js} +8 -6
  77. package/dist/{contribute-Y7IQV5QY.js.map → contribute-HY372S6F.js.map} +1 -1
  78. package/dist/dev-server-JCJGQ3PV.js +298 -0
  79. package/dist/dev-server-JCJGQ3PV.js.map +1 -0
  80. package/dist/{feedback-XTUCKJNT.js → feedback-7PVBQNLJ.js} +13 -12
  81. package/dist/{feedback-XTUCKJNT.js.map → feedback-7PVBQNLJ.js.map} +1 -1
  82. package/dist/{git-IYA53VIC.js → git-4BVOOOOV.js} +16 -4
  83. package/dist/hooks/iloom-hook.js +258 -0
  84. package/dist/{ignite-T74RYXCA.js → ignite-3B264M7K.js} +245 -39
  85. package/dist/ignite-3B264M7K.js.map +1 -0
  86. package/dist/index.d.ts +461 -124
  87. package/dist/index.js +743 -210
  88. package/dist/index.js.map +1 -1
  89. package/dist/init-LBA6NUK2.js +21 -0
  90. package/dist/{installation-detector-VARGFFRZ.js → installation-detector-6R6YOFVZ.js} +3 -3
  91. package/dist/mcp/issue-management-server.js +2 -1
  92. package/dist/mcp/issue-management-server.js.map +1 -1
  93. package/dist/neon-helpers-L5CXQ5CT.js +11 -0
  94. package/dist/{open-UMXANW5S.js → open-OGCV32Z4.js} +15 -13
  95. package/dist/{open-UMXANW5S.js.map → open-OGCV32Z4.js.map} +1 -1
  96. package/dist/projects-P55273AB.js +73 -0
  97. package/dist/projects-P55273AB.js.map +1 -0
  98. package/dist/{prompt-QALMYTVC.js → prompt-A7GGRHSY.js} +3 -3
  99. package/dist/prompts/init-prompt.txt +49 -0
  100. package/dist/prompts/issue-prompt.txt +110 -8
  101. package/dist/prompts/regular-prompt.txt +90 -0
  102. package/dist/prompts/session-summary-prompt.txt +82 -0
  103. package/dist/{rebase-VJ2VKR6R.js → rebase-4T5FQHNH.js} +11 -9
  104. package/dist/{rebase-VJ2VKR6R.js.map → rebase-4T5FQHNH.js.map} +1 -1
  105. package/dist/{remote-VUNCQZ6J.js → remote-73TZ2ADI.js} +3 -3
  106. package/dist/{run-MJYY4PUT.js → run-HNOP6WE2.js} +15 -13
  107. package/dist/{run-MJYY4PUT.js.map → run-HNOP6WE2.js.map} +1 -1
  108. package/dist/schema/settings.schema.json +49 -0
  109. package/dist/shell-DE3HKJSM.js +240 -0
  110. package/dist/shell-DE3HKJSM.js.map +1 -0
  111. package/dist/summary-GDT7DTRI.js +244 -0
  112. package/dist/summary-GDT7DTRI.js.map +1 -0
  113. package/dist/{test-git-IT5EWQ5C.js → test-git-YMAE57UP.js} +6 -4
  114. package/dist/{test-git-IT5EWQ5C.js.map → test-git-YMAE57UP.js.map} +1 -1
  115. package/dist/{test-prefix-NPWDPUUH.js → test-prefix-YCKL6CMT.js} +6 -4
  116. package/dist/{test-prefix-NPWDPUUH.js.map → test-prefix-YCKL6CMT.js.map} +1 -1
  117. package/dist/{test-tabs-PRMRSHKI.js → test-tabs-3SCJWRKT.js} +4 -4
  118. package/dist/{test-webserver-DAHONWCS.js → test-webserver-VPNLAFZ3.js} +2 -2
  119. package/dist/{update-4TDDUR5K.js → update-LETF5ASC.js} +4 -4
  120. package/dist/{update-notifier-QEX3CJHA.js → update-notifier-H55ZK7NU.js} +3 -3
  121. package/package.json +6 -6
  122. package/dist/ClaudeContextManager-BN7RE5ZQ.js +0 -15
  123. package/dist/ClaudeService-DLYLJUPA.js +0 -14
  124. package/dist/GitHubService-FZHHBOFG.js +0 -11
  125. package/dist/LoomLauncher-ZV3ZZIBA.js.map +0 -1
  126. package/dist/PromptTemplateManager-6HH3PVXV.js +0 -9
  127. package/dist/SettingsMigrationManager-TJ7UWZG5.js +0 -10
  128. package/dist/chunk-2CXREBLZ.js.map +0 -1
  129. package/dist/chunk-2IJEMXOB.js.map +0 -1
  130. package/dist/chunk-2MAIX45J.js.map +0 -1
  131. package/dist/chunk-5Q3NDNNV.js.map +0 -1
  132. package/dist/chunk-5VK4NRSF.js.map +0 -1
  133. package/dist/chunk-CDZERT7Z.js.map +0 -1
  134. package/dist/chunk-CE26YH2U.js.map +0 -1
  135. package/dist/chunk-DLHA5VQ3.js.map +0 -1
  136. package/dist/chunk-GEHQXLEI.js.map +0 -1
  137. package/dist/chunk-IFB4Z76W.js.map +0 -1
  138. package/dist/chunk-M7JJCX53.js.map +0 -1
  139. package/dist/chunk-MKWYLDFK.js.map +0 -1
  140. package/dist/chunk-OXAM2WVC.js.map +0 -1
  141. package/dist/chunk-OYF4VIFI.js.map +0 -1
  142. package/dist/chunk-PGPI5LR4.js.map +0 -1
  143. package/dist/chunk-RIEO2WML.js.map +0 -1
  144. package/dist/chunk-SUOXY5WJ.js.map +0 -1
  145. package/dist/chunk-UJL4HI2R.js.map +0 -1
  146. package/dist/chunk-ZT3YZB4K.js.map +0 -1
  147. package/dist/cleanup-H4VXU3C3.js.map +0 -1
  148. package/dist/ignite-T74RYXCA.js.map +0 -1
  149. package/dist/init-4FHTAM3F.js +0 -19
  150. package/dist/logger-MKYH4UDV.js +0 -12
  151. package/dist/neon-helpers-77PBPGJ5.js +0 -10
  152. package/dist/update-notifier-QEX3CJHA.js.map +0 -1
  153. /package/dist/{BranchNamingService-A77VI6AI.js.map → BranchNamingService-GCCWB3LK.js.map} +0 -0
  154. /package/dist/{ClaudeContextManager-BN7RE5ZQ.js.map → ClaudeContextManager-DK77227F.js.map} +0 -0
  155. /package/dist/{ClaudeService-DLYLJUPA.js.map → ClaudeService-W3SA7HVG.js.map} +0 -0
  156. /package/dist/{GitHubService-FZHHBOFG.js.map → GitHubService-RPM27GWD.js.map} +0 -0
  157. /package/dist/{PromptTemplateManager-6HH3PVXV.js.map → PromptTemplateManager-2TDZAUC6.js.map} +0 -0
  158. /package/dist/{SettingsManager-I2LRCW2A.js.map → SettingsManager-FJFU6JJD.js.map} +0 -0
  159. /package/dist/{SettingsMigrationManager-TJ7UWZG5.js.map → SettingsMigrationManager-EH3J2TCN.js.map} +0 -0
  160. /package/dist/{chunk-UAN4A3YU.js.map → chunk-G6CIIJLT.js.map} +0 -0
  161. /package/dist/{chunk-4XIDC3NF.js.map → chunk-MD6HA5IK.js.map} +0 -0
  162. /package/dist/{chunk-OC4H6HJD.js.map → chunk-O7WHXLCB.js.map} +0 -0
  163. /package/dist/{chunk-PA6Q6AWM.js.map → chunk-PSFVTBM7.js.map} +0 -0
  164. /package/dist/{chunk-ZM3CFL5L.js.map → chunk-QRBOPFAA.js.map} +0 -0
  165. /package/dist/{chunk-SSCQCCJ7.js.map → chunk-THF25ICZ.js.map} +0 -0
  166. /package/dist/{chunk-AKUJXDNW.js.map → chunk-UPUAQYAW.js.map} +0 -0
  167. /package/dist/{chunk-OSCLCMDG.js.map → chunk-UYWAESOT.js.map} +0 -0
  168. /package/dist/{chunk-RW54ZMBM.js.map → chunk-VAYGNQTE.js.map} +0 -0
  169. /package/dist/{chunk-CFFQ2Z7A.js.map → chunk-WUQQNE63.js.map} +0 -0
  170. /package/dist/{claude-W52VKI6L.js.map → claude-ACVXNB6N.js.map} +0 -0
  171. /package/dist/{color-F7RU6B6Z.js.map → color-ZPIIUADB.js.map} +0 -0
  172. /package/dist/{git-IYA53VIC.js.map → git-4BVOOOOV.js.map} +0 -0
  173. /package/dist/{init-4FHTAM3F.js.map → init-LBA6NUK2.js.map} +0 -0
  174. /package/dist/{installation-detector-VARGFFRZ.js.map → installation-detector-6R6YOFVZ.js.map} +0 -0
  175. /package/dist/{logger-MKYH4UDV.js.map → neon-helpers-L5CXQ5CT.js.map} +0 -0
  176. /package/dist/{neon-helpers-77PBPGJ5.js.map → prompt-A7GGRHSY.js.map} +0 -0
  177. /package/dist/{prompt-QALMYTVC.js.map → remote-73TZ2ADI.js.map} +0 -0
  178. /package/dist/{test-tabs-PRMRSHKI.js.map → test-tabs-3SCJWRKT.js.map} +0 -0
  179. /package/dist/{test-webserver-DAHONWCS.js.map → test-webserver-VPNLAFZ3.js.map} +0 -0
  180. /package/dist/{update-4TDDUR5K.js.map → update-LETF5ASC.js.map} +0 -0
  181. /package/dist/{remote-VUNCQZ6J.js.map → update-notifier-H55ZK7NU.js.map} +0 -0
package/dist/cli.js CHANGED
@@ -1,63 +1,62 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ SessionSummaryService
4
+ } from "./chunk-TMZAVPGF.js";
2
5
  import {
3
6
  CLIIsolationManager,
4
7
  DatabaseManager,
5
8
  EnvironmentManager,
6
9
  LoomManager,
7
- MetadataManager,
8
10
  ResourceCleanup
9
- } from "./chunk-2IJEMXOB.js";
10
- import {
11
- IdentifierParser
12
- } from "./chunk-OXAM2WVC.js";
13
- import {
14
- ProcessManager
15
- } from "./chunk-VU3QMIP2.js";
11
+ } from "./chunk-IARWMDAX.js";
16
12
  import {
17
13
  InitCommand,
18
14
  ShellCompletion
19
- } from "./chunk-UAN4A3YU.js";
20
- import {
21
- getConfiguredRepoFromSettings,
22
- getEffectivePRTargetRemote,
23
- hasMultipleRemotes,
24
- parseGitRemotes
25
- } from "./chunk-PA6Q6AWM.js";
26
- import "./chunk-OSCLCMDG.js";
15
+ } from "./chunk-G6CIIJLT.js";
16
+ import "./chunk-UYWAESOT.js";
27
17
  import {
28
18
  IssueEnhancementService,
29
19
  capitalizeFirstLetter
30
- } from "./chunk-IFB4Z76W.js";
31
- import {
32
- openBrowser
33
- } from "./chunk-YETJNRQM.js";
20
+ } from "./chunk-VTXCGKV5.js";
34
21
  import {
35
22
  MergeManager
36
- } from "./chunk-2MAIX45J.js";
23
+ } from "./chunk-BIIQHEXJ.js";
37
24
  import {
38
25
  FirstRunManager,
39
26
  IssueTrackerFactory,
40
27
  generateIssueManagementMcpConfig
41
- } from "./chunk-DLHA5VQ3.js";
28
+ } from "./chunk-HD5SUKI2.js";
29
+ import "./chunk-QHA67Q7A.js";
42
30
  import {
43
31
  AgentManager
44
- } from "./chunk-OC4H6HJD.js";
32
+ } from "./chunk-O7WHXLCB.js";
33
+ import {
34
+ IdentifierParser
35
+ } from "./chunk-55TB3FSG.js";
36
+ import {
37
+ ProcessManager
38
+ } from "./chunk-VU3QMIP2.js";
39
+ import {
40
+ openBrowser
41
+ } from "./chunk-YETJNRQM.js";
45
42
  import {
46
43
  GitWorktreeManager
47
- } from "./chunk-2CXREBLZ.js";
44
+ } from "./chunk-JC5HXN75.js";
48
45
  import {
49
46
  detectPackageManager,
50
47
  installDependencies,
51
48
  runScript
52
- } from "./chunk-ZT3YZB4K.js";
49
+ } from "./chunk-VBFDVGAE.js";
53
50
  import {
54
51
  ClaudeContextManager
55
- } from "./chunk-AKUJXDNW.js";
56
- import "./chunk-PGPI5LR4.js";
57
- import "./chunk-RIEO2WML.js";
52
+ } from "./chunk-UPUAQYAW.js";
53
+ import "./chunk-POI7KLBH.js";
54
+ import {
55
+ extractSettingsOverrides
56
+ } from "./chunk-GYCR2LOU.js";
58
57
  import {
59
58
  DefaultBranchNamingService
60
- } from "./chunk-SUOXY5WJ.js";
59
+ } from "./chunk-QIUJPPJQ.js";
61
60
  import {
62
61
  ProjectCapabilityDetector
63
62
  } from "./chunk-EBISESAP.js";
@@ -65,45 +64,58 @@ import {
65
64
  hasScript,
66
65
  readPackageJson
67
66
  } from "./chunk-2ZPFJQ3B.js";
68
- import {
69
- extractSettingsOverrides
70
- } from "./chunk-GYCR2LOU.js";
71
67
  import {
72
68
  createNeonProviderFromSettings
73
- } from "./chunk-5VK4NRSF.js";
69
+ } from "./chunk-UNXRACJ7.js";
70
+ import {
71
+ getConfiguredRepoFromSettings,
72
+ getEffectivePRTargetRemote,
73
+ hasMultipleRemotes,
74
+ parseGitRemotes
75
+ } from "./chunk-PSFVTBM7.js";
74
76
  import {
75
77
  executeGitCommand,
76
78
  extractIssueNumber,
77
79
  findMainWorktreePathWithSettings,
80
+ getMergeTargetBranch,
78
81
  getRepoRoot,
79
82
  pushBranchToRemote
80
- } from "./chunk-5Q3NDNNV.js";
83
+ } from "./chunk-2W2FBL5G.js";
84
+ import {
85
+ MetadataManager
86
+ } from "./chunk-IJ7IGJT3.js";
81
87
  import {
82
88
  SettingsManager
83
- } from "./chunk-CDZERT7Z.js";
89
+ } from "./chunk-VWNS6DH5.js";
84
90
  import {
85
91
  GitHubService
86
- } from "./chunk-M7JJCX53.js";
92
+ } from "./chunk-OEGECBFS.js";
87
93
  import {
88
94
  executeGhCommand
89
- } from "./chunk-3RUPPQRG.js";
95
+ } from "./chunk-KO2FOMHL.js";
90
96
  import {
91
97
  promptCommitAction,
92
98
  promptConfirmation,
93
99
  waitForKeypress
94
- } from "./chunk-CE26YH2U.js";
95
- import "./chunk-CFFQ2Z7A.js";
100
+ } from "./chunk-SJ2GZ6RF.js";
101
+ import "./chunk-WUQQNE63.js";
96
102
  import {
97
103
  detectClaudeCli,
98
104
  launchClaude
99
- } from "./chunk-OYF4VIFI.js";
100
- import "./chunk-RW54ZMBM.js";
105
+ } from "./chunk-RUC7OULH.js";
106
+ import "./chunk-VAYGNQTE.js";
101
107
  import {
102
108
  loadEnvIntoProcess
103
- } from "./chunk-UJL4HI2R.js";
109
+ } from "./chunk-Z5NXYJIG.js";
110
+ import {
111
+ getLogger,
112
+ withLogger
113
+ } from "./chunk-6UIGZD2N.js";
114
+ import "./chunk-74VMN2KC.js";
104
115
  import {
116
+ createStderrLogger,
105
117
  logger
106
- } from "./chunk-GEHQXLEI.js";
118
+ } from "./chunk-UYVWLISQ.js";
107
119
 
108
120
  // src/cli.ts
109
121
  import { program, Option } from "commander";
@@ -158,7 +170,7 @@ async function launchFirstRunSetup() {
158
170
  logger.info(
159
171
  "iloom will now launch an interactive configuration session with Claude."
160
172
  );
161
- const { waitForKeypress: waitForKeypress2 } = await import("./prompt-QALMYTVC.js");
173
+ const { waitForKeypress: waitForKeypress2 } = await import("./prompt-A7GGRHSY.js");
162
174
  await waitForKeypress2("Press any key to start configuration...");
163
175
  const initCommand = new InitCommand();
164
176
  await initCommand.execute(
@@ -181,10 +193,10 @@ var StartCommand = class {
181
193
  this.providedLoomManager = loomManager;
182
194
  const envResult = loadEnvIntoProcess();
183
195
  if (envResult.error) {
184
- logger.debug(`Environment loading warning: ${envResult.error.message}`);
196
+ getLogger().debug(`Environment loading warning: ${envResult.error.message}`);
185
197
  }
186
198
  if (envResult.parsed) {
187
- logger.debug(`Loaded ${Object.keys(envResult.parsed).length} environment variables`);
199
+ getLogger().debug(`Loaded ${Object.keys(envResult.parsed).length} environment variables`);
188
200
  }
189
201
  }
190
202
  /**
@@ -228,36 +240,40 @@ var StartCommand = class {
228
240
  * Main entry point for the start command
229
241
  */
230
242
  async execute(input) {
231
- var _a, _b, _c, _d;
243
+ var _a, _b, _c, _d, _e;
244
+ const isJsonMode = input.options.json === true;
232
245
  try {
233
246
  const initialSettings = await this.settingsManager.loadSettings();
234
- if (process.env.FORCE_FIRST_TIME_SETUP === "true" || await needsFirstRunSetup()) {
247
+ if (!isJsonMode && (process.env.FORCE_FIRST_TIME_SETUP === "true" || await needsFirstRunSetup())) {
235
248
  await launchFirstRunSetup();
236
249
  const newSettings = await this.settingsManager.loadSettings();
237
250
  const newProvider = ((_a = newSettings.issueManagement) == null ? void 0 : _a.provider) ?? "github";
238
251
  if (newProvider !== this.issueTracker.providerName) {
239
- logger.debug(`Reinitializing issue tracker: provider changed to "${newProvider}"`);
252
+ getLogger().debug(`Reinitializing issue tracker: provider changed to "${newProvider}"`);
240
253
  this.issueTracker = IssueTrackerFactory.create(newSettings);
241
254
  }
242
255
  }
243
256
  let repo;
244
257
  if (this.issueTracker.providerName === "github" && await hasMultipleRemotes()) {
245
258
  repo = await getConfiguredRepoFromSettings(initialSettings);
246
- logger.info(`Using GitHub repository: ${repo}`);
259
+ getLogger().info(`Using GitHub repository: ${repo}`);
247
260
  }
248
261
  const loomManager = await this.initializeLoomManager();
249
262
  let parentLoom = await this.detectParentLoom(loomManager);
250
263
  const parsed = await this.parseInput(input.identifier, repo);
251
264
  await this.validateInput(parsed, repo);
252
265
  if (parentLoom) {
253
- const { isInteractiveEnvironment, promptConfirmation: promptConfirmation2 } = await import("./prompt-QALMYTVC.js");
266
+ const { isInteractiveEnvironment, promptConfirmation: promptConfirmation2 } = await import("./prompt-A7GGRHSY.js");
254
267
  const parentDisplay = parentLoom.type === "issue" ? `issue #${parentLoom.identifier}` : parentLoom.type === "pr" ? `PR #${parentLoom.identifier}` : `branch ${parentLoom.identifier}`;
255
268
  if (input.options.childLoom === true) {
256
- logger.info(`Creating as child loom of ${parentDisplay} (--child-loom flag)`);
269
+ getLogger().info(`Creating as child loom of ${parentDisplay} (--child-loom flag)`);
257
270
  } else if (input.options.childLoom === false) {
258
271
  parentLoom = null;
259
- logger.info("Creating as independent loom (--no-child-loom flag)");
272
+ getLogger().info("Creating as independent loom (--no-child-loom flag)");
260
273
  } else {
274
+ if (isJsonMode) {
275
+ throw new Error("JSON mode requires explicit --child-loom or --no-child-loom flag when running from inside a loom");
276
+ }
261
277
  let createAsChild = true;
262
278
  if (isInteractiveEnvironment()) {
263
279
  createAsChild = await promptConfirmation2(
@@ -266,19 +282,19 @@ var StartCommand = class {
266
282
  // Default yes
267
283
  );
268
284
  } else {
269
- logger.error(`Non-interactive environment detected, use either --child-loom or --no-child-loom to specify behavior`);
285
+ getLogger().error(`Non-interactive environment detected, use either --child-loom or --no-child-loom to specify behavior`);
270
286
  process.exit(1);
271
287
  }
272
288
  if (!createAsChild) {
273
289
  parentLoom = null;
274
- logger.info("Creating as independent loom");
290
+ getLogger().info("Creating as independent loom");
275
291
  }
276
292
  }
277
293
  } else if (input.options.childLoom === true) {
278
- logger.debug("--child-loom flag provided but not running from inside an existing loom (ignored)");
294
+ getLogger().debug("--child-loom flag provided but not running from inside an existing loom (ignored)");
279
295
  }
280
296
  if (parsed.type === "description") {
281
- logger.info("Creating GitHub issue from description...");
297
+ getLogger().info("Creating GitHub issue from description...");
282
298
  const title = capitalizeFirstLetter(parsed.originalInput);
283
299
  const body = input.options.body ? capitalizeFirstLetter(input.options.body) : "";
284
300
  const result = await this.issueTracker.createIssue(
@@ -287,17 +303,20 @@ var StartCommand = class {
287
303
  body
288
304
  // Use capitalized body or empty
289
305
  );
290
- logger.success(`Created issue #${result.number}: ${result.url}`);
306
+ getLogger().success(`Created issue #${result.number}: ${result.url}`);
291
307
  parsed.type = "issue";
292
308
  parsed.number = result.number;
293
309
  }
294
310
  if (input.options.oneShot === "bypassPermissions") {
295
- const { promptConfirmation: promptConfirmation2 } = await import("./prompt-QALMYTVC.js");
311
+ if (isJsonMode) {
312
+ throw new Error("JSON mode does not support bypassPermissions confirmation prompt");
313
+ }
314
+ const { promptConfirmation: promptConfirmation2 } = await import("./prompt-A7GGRHSY.js");
296
315
  const confirmed = await promptConfirmation2(
297
- "\u26A0\uFE0F WARNING: bypassPermissions mode will allow Claude to execute all tool calls without confirmation. This can be dangerous. Do you want to proceed?"
316
+ "WARNING: bypassPermissions mode will allow Claude to execute all tool calls without confirmation. This can be dangerous. Do you want to proceed?"
298
317
  );
299
318
  if (!confirmed) {
300
- logger.info("Operation cancelled by user");
319
+ getLogger().info("Operation cancelled by user");
301
320
  process.exit(0);
302
321
  }
303
322
  }
@@ -308,13 +327,13 @@ var StartCommand = class {
308
327
  const { extractRawSetArguments, getExecutablePath } = await import("./cli-overrides-XFZWY7CM.js");
309
328
  const setArguments = extractRawSetArguments();
310
329
  const executablePath = getExecutablePath();
311
- logger.info(`\u2705 Validated input: ${this.formatParsedInput(parsed)}`);
330
+ getLogger().info(`Validated input: ${this.formatParsedInput(parsed)}`);
312
331
  const identifier = parsed.type === "branch" ? parsed.branchName ?? "" : parsed.number ?? 0;
313
332
  const enableClaude = input.options.claude ?? (workflowConfig == null ? void 0 : workflowConfig.startAiAgent) ?? true;
314
333
  const enableCode = input.options.code ?? (workflowConfig == null ? void 0 : workflowConfig.startIde) ?? true;
315
334
  const enableDevServer = input.options.devServer ?? (workflowConfig == null ? void 0 : workflowConfig.startDevServer) ?? true;
316
335
  const enableTerminal = input.options.terminal ?? (workflowConfig == null ? void 0 : workflowConfig.startTerminal) ?? false;
317
- logger.debug("Final workflow config values:", {
336
+ getLogger().debug("Final workflow config values:", {
318
337
  enableClaude,
319
338
  enableCode,
320
339
  enableDevServer,
@@ -335,19 +354,31 @@ var StartCommand = class {
335
354
  ...executablePath && { executablePath }
336
355
  }
337
356
  });
338
- logger.success(`\u2705 Created loom: ${loom.id} at ${loom.path}`);
339
- logger.info(` Branch: ${loom.branch}`);
357
+ getLogger().success(`Created loom: ${loom.id} at ${loom.path}`);
358
+ getLogger().info(` Branch: ${loom.branch}`);
340
359
  if ((_c = loom.capabilities) == null ? void 0 : _c.includes("web")) {
341
- logger.info(` Port: ${loom.port}`);
360
+ getLogger().info(` Port: ${loom.port}`);
342
361
  }
343
362
  if ((_d = loom.issueData) == null ? void 0 : _d.title) {
344
- logger.info(` Title: ${loom.issueData.title}`);
363
+ getLogger().info(` Title: ${loom.issueData.title}`);
364
+ }
365
+ if (isJsonMode) {
366
+ return {
367
+ id: loom.id,
368
+ path: loom.path,
369
+ branch: loom.branch,
370
+ type: parsed.type,
371
+ identifier: loom.identifier,
372
+ ...loom.port !== void 0 && { port: loom.port },
373
+ ...((_e = loom.issueData) == null ? void 0 : _e.title) && { title: loom.issueData.title },
374
+ ...loom.capabilities && { capabilities: loom.capabilities }
375
+ };
345
376
  }
346
377
  } catch (error) {
347
378
  if (error instanceof Error) {
348
- logger.error(`\u274C ${error.message}`);
379
+ getLogger().error(`${error.message}`);
349
380
  } else {
350
- logger.error("\u274C An unknown error occurred");
381
+ getLogger().error("An unknown error occurred");
351
382
  }
352
383
  throw error;
353
384
  }
@@ -440,7 +471,7 @@ var StartCommand = class {
440
471
  }
441
472
  const pr = await this.issueTracker.fetchPR(parsed.number, repo);
442
473
  await this.issueTracker.validatePRState(pr);
443
- logger.debug(`Validated PR #${parsed.number}`);
474
+ getLogger().debug(`Validated PR #${parsed.number}`);
444
475
  break;
445
476
  }
446
477
  case "issue": {
@@ -449,7 +480,7 @@ var StartCommand = class {
449
480
  }
450
481
  const issue = await this.issueTracker.fetchIssue(parsed.number, repo);
451
482
  await this.issueTracker.validateIssueState(issue);
452
- logger.debug(`Validated issue #${parsed.number}`);
483
+ getLogger().debug(`Validated issue #${parsed.number}`);
453
484
  break;
454
485
  }
455
486
  case "branch": {
@@ -461,11 +492,11 @@ var StartCommand = class {
461
492
  "Invalid branch name. Use only letters, numbers, hyphens, underscores, and slashes"
462
493
  );
463
494
  }
464
- logger.debug(`Validated branch name: ${parsed.branchName}`);
495
+ getLogger().debug(`Validated branch name: ${parsed.branchName}`);
465
496
  break;
466
497
  }
467
498
  case "description": {
468
- logger.debug("Detected description input", {
499
+ getLogger().debug("Detected description input", {
469
500
  length: parsed.originalInput.length
470
501
  });
471
502
  break;
@@ -520,7 +551,7 @@ var StartCommand = class {
520
551
  if (!parentLoom) {
521
552
  return null;
522
553
  }
523
- logger.debug(`Detected parent loom: ${parentLoom.type} ${parentLoom.identifier} at ${parentLoom.path}`);
554
+ getLogger().debug(`Detected parent loom: ${parentLoom.type} ${parentLoom.identifier} at ${parentLoom.path}`);
524
555
  const result = {
525
556
  type: parentLoom.type,
526
557
  identifier: parentLoom.identifier,
@@ -534,12 +565,12 @@ var StartCommand = class {
534
565
  const databaseBranch = await loomManager.getDatabaseBranchForLoom(parentLoom.path);
535
566
  if (databaseBranch) {
536
567
  result.databaseBranch = databaseBranch;
537
- logger.debug(`Detected parent database branch: ${databaseBranch}`);
568
+ getLogger().debug(`Detected parent database branch: ${databaseBranch}`);
538
569
  }
539
570
  }
540
571
  return result;
541
572
  } catch (error) {
542
- logger.debug(`Failed to detect parent loom: ${error instanceof Error ? error.message : "Unknown error"}`);
573
+ getLogger().debug(`Failed to detect parent loom: ${error instanceof Error ? error.message : "Unknown error"}`);
543
574
  return null;
544
575
  }
545
576
  }
@@ -556,20 +587,23 @@ var AddIssueCommand = class {
556
587
  * 1. Validate description format
557
588
  * 2. Skip enhancement if body provided, otherwise enhance description with Claude Code
558
589
  * 3. Create GitHub issue
559
- * 4. Wait for keypress and open browser for review
560
- * 5. Return issue number
590
+ * 4. Wait for keypress and open browser for review (unless --json mode)
591
+ * 5. Return issue number or full result object (when --json)
561
592
  */
562
593
  async execute(input) {
563
594
  const description = capitalizeFirstLetter(input.description);
564
595
  const body = input.options.body ? capitalizeFirstLetter(input.options.body) : void 0;
565
- if (process.env.FORCE_FIRST_TIME_SETUP === "true" || await needsFirstRunSetup()) {
596
+ const isJsonMode = input.options.json === true;
597
+ if (!isJsonMode && (process.env.FORCE_FIRST_TIME_SETUP === "true" || await needsFirstRunSetup())) {
566
598
  await launchFirstRunSetup();
567
599
  }
568
600
  const settings = await this.settingsManager.loadSettings();
569
601
  let repo;
570
602
  if (this.enhancementService.issueTracker.providerName === "github" && await hasMultipleRemotes()) {
571
603
  repo = await getConfiguredRepoFromSettings(settings);
572
- logger.info(`Using GitHub repository: ${repo}`);
604
+ if (!isJsonMode) {
605
+ getLogger().info(`Using GitHub repository: ${repo}`);
606
+ }
573
607
  }
574
608
  if (!description || !this.enhancementService.validateDescription(description)) {
575
609
  throw new Error("Description is required and must be more than 30 characters with at least 3 words");
@@ -580,6 +614,15 @@ var AddIssueCommand = class {
580
614
  issueBody,
581
615
  repo
582
616
  );
617
+ if (isJsonMode) {
618
+ const resultData = {
619
+ url: result.url,
620
+ id: typeof result.number === "string" ? parseInt(result.number, 10) : result.number,
621
+ title: description,
622
+ created_at: (/* @__PURE__ */ new Date()).toISOString()
623
+ };
624
+ return resultData;
625
+ }
583
626
  await this.enhancementService.waitForReviewAndOpen(result.number);
584
627
  return result.number;
585
628
  }
@@ -599,25 +642,31 @@ var EnhanceCommand = class {
599
642
  * 3. Load agent configurations
600
643
  * 4. Invoke Claude CLI with enhancer agent
601
644
  * 5. Parse response to determine outcome
602
- * 6. Handle browser interaction based on outcome
645
+ * 6. Handle browser interaction based on outcome (unless --json mode)
646
+ * 7. Return result object when --json mode
603
647
  */
604
648
  async execute(input) {
605
649
  const { issueNumber, options } = input;
606
650
  const { author } = options;
607
- if (process.env.FORCE_FIRST_TIME_SETUP === "true" || await needsFirstRunSetup()) {
651
+ const isJsonMode = options.json === true;
652
+ if (!isJsonMode && (process.env.FORCE_FIRST_TIME_SETUP === "true" || await needsFirstRunSetup())) {
608
653
  await launchFirstRunSetup();
609
654
  }
610
655
  const settings = await this.settingsManager.loadSettings();
611
656
  let repo;
612
657
  if (this.issueTracker.providerName === "github" && await hasMultipleRemotes()) {
613
658
  repo = await getConfiguredRepoFromSettings(settings);
614
- logger.info(`Using GitHub repository: ${repo}`);
659
+ if (!isJsonMode) {
660
+ getLogger().info(`Using GitHub repository: ${repo}`);
661
+ }
615
662
  }
616
663
  this.validateIssueNumber(issueNumber);
617
- logger.info(`Fetching issue #${issueNumber}...`);
664
+ if (!isJsonMode) {
665
+ getLogger().info(`Fetching issue #${issueNumber}...`);
666
+ }
618
667
  const issue = await this.issueTracker.fetchIssue(issueNumber, repo);
619
- logger.debug("Issue fetched successfully", { number: issue.number, title: issue.title });
620
- logger.debug("Loading agent configurations...");
668
+ getLogger().debug("Issue fetched successfully", { number: issue.number, title: issue.title });
669
+ getLogger().debug("Loading agent configurations...");
621
670
  const loadedAgents = await this.agentManager.loadAgents(settings);
622
671
  const agents = this.agentManager.formatForCli(loadedAgents);
623
672
  let mcpConfig;
@@ -626,7 +675,7 @@ var EnhanceCommand = class {
626
675
  try {
627
676
  const provider = this.issueTracker.providerName;
628
677
  mcpConfig = await generateIssueManagementMcpConfig("issue", repo, provider, settings);
629
- logger.debug("Generated MCP configuration for issue management:", { mcpConfig });
678
+ getLogger().debug("Generated MCP configuration for issue management:", { mcpConfig });
630
679
  allowedTools = [
631
680
  "mcp__issue_management__get_issue",
632
681
  "mcp__issue_management__get_comment",
@@ -634,11 +683,13 @@ var EnhanceCommand = class {
634
683
  "mcp__issue_management__update_comment"
635
684
  ];
636
685
  disallowedTools = ["Bash(gh api:*)"];
637
- logger.debug("Configured tool filtering for issue workflow", { allowedTools, disallowedTools });
686
+ getLogger().debug("Configured tool filtering for issue workflow", { allowedTools, disallowedTools });
638
687
  } catch (error) {
639
- logger.warn(`Failed to generate MCP config: ${error instanceof Error ? error.message : "Unknown error"}`);
688
+ getLogger().warn(`Failed to generate MCP config: ${error instanceof Error ? error.message : "Unknown error"}`);
689
+ }
690
+ if (!isJsonMode) {
691
+ getLogger().info("Invoking enhancer agent. This may take a moment...");
640
692
  }
641
- logger.info("Invoking enhancer agent. This may take a moment...");
642
693
  const prompt = this.constructPrompt(issueNumber, author);
643
694
  const response = await launchClaude(prompt, {
644
695
  headless: true,
@@ -649,16 +700,36 @@ var EnhanceCommand = class {
649
700
  ...disallowedTools && { disallowedTools }
650
701
  });
651
702
  const result = this.parseEnhancerResponse(response);
703
+ if (isJsonMode) {
704
+ const commentId = result.url ? this.extractCommentId(result.url) : 0;
705
+ const resultData = {
706
+ url: result.url ?? issue.url,
707
+ id: commentId,
708
+ title: issue.title,
709
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
710
+ enhanced: result.enhanced
711
+ };
712
+ return resultData;
713
+ }
652
714
  if (!result.enhanced) {
653
- logger.success("Issue already has thorough description. No enhancement needed.");
715
+ getLogger().success("Issue already has thorough description. No enhancement needed.");
654
716
  return;
655
717
  }
656
- logger.success(`Issue #${issueNumber} enhanced successfully!`);
657
- logger.info(`Enhanced specification available at: ${result.url}`);
718
+ getLogger().success(`Issue #${issueNumber} enhanced successfully!`);
719
+ getLogger().info(`Enhanced specification available at: ${result.url}`);
658
720
  if (!options.noBrowser && result.url) {
659
721
  await this.promptAndOpenBrowser(result.url);
660
722
  }
661
723
  }
724
+ /**
725
+ * Extract comment ID from GitHub comment URL
726
+ * @param url - GitHub comment URL (e.g., https://github.com/owner/repo/issues/123#issuecomment-456789)
727
+ * @returns Comment ID as number, or 0 if not found
728
+ */
729
+ extractCommentId(url) {
730
+ const match = url.match(/issuecomment-(\d+)/);
731
+ return (match == null ? void 0 : match[1]) ? parseInt(match[1], 10) : 0;
732
+ }
662
733
  /**
663
734
  * Validate that issue number is a valid positive integer
664
735
  */
@@ -702,7 +773,7 @@ IMPORTANT: When you create your analysis comment, tag @${author} in the "Questio
702
773
  throw new Error("No response from enhancer agent");
703
774
  }
704
775
  const trimmed = response.trim();
705
- logger.debug(`RESPONSE FROM ENHANCER AGENT: '${trimmed}'`);
776
+ getLogger().debug(`RESPONSE FROM ENHANCER AGENT: '${trimmed}'`);
706
777
  if (trimmed.toLowerCase().startsWith("permission denied:")) {
707
778
  const errorMessage = trimmed.substring("permission denied:".length).trim();
708
779
  throw new Error(`Permission denied: ${errorMessage}`);
@@ -727,18 +798,20 @@ IMPORTANT: When you create your analysis comment, tag @${author} in the "Questio
727
798
  "Press q to quit or any other key to view the enhanced issue in a web browser..."
728
799
  );
729
800
  if (key.toLowerCase() === "q") {
730
- logger.info("Skipping browser opening");
801
+ getLogger().info("Skipping browser opening");
731
802
  return;
732
803
  }
733
804
  await openBrowser(commentUrl);
734
805
  } catch (error) {
735
- logger.warn(`Failed to open browser: ${error instanceof Error ? error.message : "Unknown error"}`);
806
+ getLogger().warn(`Failed to open browser: ${error instanceof Error ? error.message : "Unknown error"}`);
736
807
  }
737
808
  }
738
809
  };
739
810
 
740
811
  // src/lib/ValidationRunner.ts
741
812
  var ValidationRunner = class {
813
+ constructor() {
814
+ }
742
815
  /**
743
816
  * Run all validations in sequence: typecheck → lint → test
744
817
  * Fails fast on first error
@@ -781,14 +854,22 @@ var ValidationRunner = class {
781
854
  }
782
855
  /**
783
856
  * Run typecheck validation
857
+ * Prefers 'compile' script over 'typecheck' if both exist
784
858
  */
785
859
  async runTypecheck(worktreePath, dryRun) {
786
860
  const stepStartTime = Date.now();
861
+ let scriptToRun = null;
787
862
  try {
788
863
  const pkgJson = await readPackageJson(worktreePath);
864
+ const hasCompileScript = hasScript(pkgJson, "compile");
789
865
  const hasTypecheckScript = hasScript(pkgJson, "typecheck");
790
- if (!hasTypecheckScript) {
791
- logger.debug("Skipping typecheck - no typecheck script found");
866
+ if (hasCompileScript) {
867
+ scriptToRun = "compile";
868
+ } else if (hasTypecheckScript) {
869
+ scriptToRun = "typecheck";
870
+ }
871
+ if (!scriptToRun) {
872
+ getLogger().debug("Skipping typecheck - no compile or typecheck script found");
792
873
  return {
793
874
  step: "typecheck",
794
875
  passed: true,
@@ -798,7 +879,7 @@ var ValidationRunner = class {
798
879
  }
799
880
  } catch (error) {
800
881
  if (error instanceof Error && error.message.includes("package.json not found")) {
801
- logger.debug("Skipping typecheck - no package.json found (non-Node.js project)");
882
+ getLogger().debug("Skipping typecheck - no package.json found (non-Node.js project)");
802
883
  return {
803
884
  step: "typecheck",
804
885
  passed: true,
@@ -810,42 +891,43 @@ var ValidationRunner = class {
810
891
  }
811
892
  const packageManager = await detectPackageManager(worktreePath);
812
893
  if (dryRun) {
813
- const command = packageManager === "npm" ? "npm run typecheck" : `${packageManager} typecheck`;
814
- logger.info(`[DRY RUN] Would run: ${command}`);
894
+ const command = packageManager === "npm" ? `npm run ${scriptToRun}` : `${packageManager} ${scriptToRun}`;
895
+ getLogger().info(`[DRY RUN] Would run: ${command}`);
815
896
  return {
816
- step: "typecheck",
897
+ step: scriptToRun,
817
898
  passed: true,
818
899
  skipped: false,
819
900
  duration: Date.now() - stepStartTime
820
901
  };
821
902
  }
822
- logger.info("Running typecheck...");
903
+ getLogger().info(`Running ${scriptToRun}...`);
823
904
  try {
824
- await runScript("typecheck", worktreePath, [], { quiet: true });
825
- logger.success("Typecheck passed");
905
+ await runScript(scriptToRun, worktreePath, [], { quiet: true });
906
+ getLogger().success(`${scriptToRun.charAt(0).toUpperCase() + scriptToRun.slice(1)} passed`);
826
907
  return {
827
- step: "typecheck",
908
+ step: scriptToRun,
828
909
  passed: true,
829
910
  skipped: false,
830
911
  duration: Date.now() - stepStartTime
831
912
  };
832
913
  } catch {
833
914
  const fixed = await this.attemptClaudeFix(
834
- "typecheck",
915
+ scriptToRun,
835
916
  worktreePath,
836
917
  packageManager
837
918
  );
838
919
  if (fixed) {
839
920
  return {
840
- step: "typecheck",
921
+ step: scriptToRun,
841
922
  passed: true,
842
923
  skipped: false,
843
924
  duration: Date.now() - stepStartTime
844
925
  };
845
926
  }
846
- const runCommand = packageManager === "npm" ? "npm run typecheck" : `${packageManager} typecheck`;
927
+ const runCommand = packageManager === "npm" ? `npm run ${scriptToRun}` : `${packageManager} ${scriptToRun}`;
928
+ const stepLabel = scriptToRun.charAt(0).toUpperCase() + scriptToRun.slice(1);
847
929
  throw new Error(
848
- `Error: Typecheck failed.
930
+ `Error: ${stepLabel} failed.
849
931
  Fix type errors before merging.
850
932
 
851
933
  Run '${runCommand}' to see detailed errors.`
@@ -861,7 +943,7 @@ Run '${runCommand}' to see detailed errors.`
861
943
  const pkgJson = await readPackageJson(worktreePath);
862
944
  const hasLintScript = hasScript(pkgJson, "lint");
863
945
  if (!hasLintScript) {
864
- logger.debug("Skipping lint - no lint script found");
946
+ getLogger().debug("Skipping lint - no lint script found");
865
947
  return {
866
948
  step: "lint",
867
949
  passed: true,
@@ -871,7 +953,7 @@ Run '${runCommand}' to see detailed errors.`
871
953
  }
872
954
  } catch (error) {
873
955
  if (error instanceof Error && error.message.includes("package.json not found")) {
874
- logger.debug("Skipping lint - no package.json found (non-Node.js project)");
956
+ getLogger().debug("Skipping lint - no package.json found (non-Node.js project)");
875
957
  return {
876
958
  step: "lint",
877
959
  passed: true,
@@ -884,7 +966,7 @@ Run '${runCommand}' to see detailed errors.`
884
966
  const packageManager = await detectPackageManager(worktreePath);
885
967
  if (dryRun) {
886
968
  const command = packageManager === "npm" ? "npm run lint" : `${packageManager} lint`;
887
- logger.info(`[DRY RUN] Would run: ${command}`);
969
+ getLogger().info(`[DRY RUN] Would run: ${command}`);
888
970
  return {
889
971
  step: "lint",
890
972
  passed: true,
@@ -892,10 +974,10 @@ Run '${runCommand}' to see detailed errors.`
892
974
  duration: Date.now() - stepStartTime
893
975
  };
894
976
  }
895
- logger.info("Running lint...");
977
+ getLogger().info("Running lint...");
896
978
  try {
897
979
  await runScript("lint", worktreePath, [], { quiet: true });
898
- logger.success("Linting passed");
980
+ getLogger().success("Linting passed");
899
981
  return {
900
982
  step: "lint",
901
983
  passed: true,
@@ -934,7 +1016,7 @@ Run '${runCommand}' to see detailed errors.`
934
1016
  const pkgJson = await readPackageJson(worktreePath);
935
1017
  const hasTestScript = hasScript(pkgJson, "test");
936
1018
  if (!hasTestScript) {
937
- logger.debug("Skipping tests - no test script found");
1019
+ getLogger().debug("Skipping tests - no test script found");
938
1020
  return {
939
1021
  step: "test",
940
1022
  passed: true,
@@ -944,7 +1026,7 @@ Run '${runCommand}' to see detailed errors.`
944
1026
  }
945
1027
  } catch (error) {
946
1028
  if (error instanceof Error && error.message.includes("package.json not found")) {
947
- logger.debug("Skipping tests - no package.json found (non-Node.js project)");
1029
+ getLogger().debug("Skipping tests - no package.json found (non-Node.js project)");
948
1030
  return {
949
1031
  step: "test",
950
1032
  passed: true,
@@ -957,7 +1039,7 @@ Run '${runCommand}' to see detailed errors.`
957
1039
  const packageManager = await detectPackageManager(worktreePath);
958
1040
  if (dryRun) {
959
1041
  const command = packageManager === "npm" ? "npm run test" : `${packageManager} test`;
960
- logger.info(`[DRY RUN] Would run: ${command}`);
1042
+ getLogger().info(`[DRY RUN] Would run: ${command}`);
961
1043
  return {
962
1044
  step: "test",
963
1045
  passed: true,
@@ -965,10 +1047,10 @@ Run '${runCommand}' to see detailed errors.`
965
1047
  duration: Date.now() - stepStartTime
966
1048
  };
967
1049
  }
968
- logger.info("Running tests...");
1050
+ getLogger().info("Running tests...");
969
1051
  try {
970
1052
  await runScript("test", worktreePath, [], { quiet: true });
971
- logger.success("Tests passed");
1053
+ getLogger().success("Tests passed");
972
1054
  return {
973
1055
  step: "test",
974
1056
  passed: true,
@@ -1002,7 +1084,7 @@ Run '${runCommand}' to see detailed errors.`
1002
1084
  * Attempt to fix validation errors using Claude
1003
1085
  * Pattern based on MergeManager.attemptClaudeConflictResolution
1004
1086
  *
1005
- * @param validationType - Type of validation that failed ('typecheck' | 'lint' | 'test')
1087
+ * @param validationType - Type of validation that failed ('compile' | 'typecheck' | 'lint' | 'test')
1006
1088
  * @param worktreePath - Path to the worktree
1007
1089
  * @param packageManager - Detected package manager
1008
1090
  * @returns true if Claude fixed the issue, false otherwise
@@ -1010,13 +1092,13 @@ Run '${runCommand}' to see detailed errors.`
1010
1092
  async attemptClaudeFix(validationType, worktreePath, packageManager) {
1011
1093
  const isClaudeAvailable = await detectClaudeCli();
1012
1094
  if (!isClaudeAvailable) {
1013
- logger.debug("Claude CLI not available, skipping auto-fix");
1095
+ getLogger().debug("Claude CLI not available, skipping auto-fix");
1014
1096
  return false;
1015
1097
  }
1016
1098
  const validationCommand = this.getValidationCommand(validationType, packageManager);
1017
1099
  const prompt = this.getClaudePrompt(validationType, validationCommand);
1018
1100
  const validationTypeCapitalized = validationType.charAt(0).toUpperCase() + validationType.slice(1);
1019
- logger.info(`Launching Claude to help fix ${validationTypeCapitalized} errors...`);
1101
+ getLogger().info(`Launching Claude to help fix ${validationTypeCapitalized} errors...`);
1020
1102
  try {
1021
1103
  await launchClaude(prompt, {
1022
1104
  addDir: worktreePath,
@@ -1027,17 +1109,17 @@ Run '${runCommand}' to see detailed errors.`
1027
1109
  model: "sonnet"
1028
1110
  // Use Sonnet model
1029
1111
  });
1030
- logger.info(`Re-running ${validationTypeCapitalized} after Claude's fixes...`);
1112
+ getLogger().info(`Re-running ${validationTypeCapitalized} after Claude's fixes...`);
1031
1113
  try {
1032
1114
  await runScript(validationType, worktreePath, [], { quiet: true });
1033
- logger.success(`${validationTypeCapitalized} passed after Claude auto-fix`);
1115
+ getLogger().success(`${validationTypeCapitalized} passed after Claude auto-fix`);
1034
1116
  return true;
1035
1117
  } catch {
1036
- logger.warn(`${validationTypeCapitalized} still failing after Claude's help`);
1118
+ getLogger().warn(`${validationTypeCapitalized} still failing after Claude's help`);
1037
1119
  return false;
1038
1120
  }
1039
1121
  } catch (error) {
1040
- logger.warn("Claude auto-fix failed", {
1122
+ getLogger().warn("Claude auto-fix failed", {
1041
1123
  error: error instanceof Error ? error.message : String(error)
1042
1124
  });
1043
1125
  return false;
@@ -1058,8 +1140,9 @@ Run '${runCommand}' to see detailed errors.`
1058
1140
  */
1059
1141
  getClaudePrompt(validationType, validationCommand) {
1060
1142
  switch (validationType) {
1143
+ case "compile":
1061
1144
  case "typecheck":
1062
- return `There are TypeScript errors in this codebase. Please analyze the typecheck 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.`;
1145
+ 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.`;
1063
1146
  case "lint":
1064
1147
  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.`;
1065
1148
  case "test":
@@ -1068,6 +1151,54 @@ Run '${runCommand}' to see detailed errors.`
1068
1151
  }
1069
1152
  };
1070
1153
 
1154
+ // src/utils/vscode.ts
1155
+ import { execa } from "execa";
1156
+ function isRunningInVSCode() {
1157
+ return process.env.TERM_PROGRAM === "vscode";
1158
+ }
1159
+ function isRunningInCursor() {
1160
+ return !!process.env.CURSOR_TRACE_ID;
1161
+ }
1162
+ function isRunningInAntigravity() {
1163
+ return !!process.env.ANTIGRAVITY_CLI_ALIAS;
1164
+ }
1165
+ async function isVSCodeAvailable() {
1166
+ try {
1167
+ await execa("command", ["-v", "code"], {
1168
+ shell: true,
1169
+ timeout: 5e3
1170
+ });
1171
+ return true;
1172
+ } catch (error) {
1173
+ logger.debug("VSCode CLI not available", { error });
1174
+ return false;
1175
+ }
1176
+ }
1177
+ async function isCursorAvailable() {
1178
+ try {
1179
+ await execa("command", ["-v", "cursor"], {
1180
+ shell: true,
1181
+ timeout: 5e3
1182
+ });
1183
+ return true;
1184
+ } catch (error) {
1185
+ logger.debug("Cursor CLI not available", { error });
1186
+ return false;
1187
+ }
1188
+ }
1189
+ async function isAntigravityAvailable() {
1190
+ try {
1191
+ await execa("command", ["-v", "agy"], {
1192
+ shell: true,
1193
+ timeout: 5e3
1194
+ });
1195
+ return true;
1196
+ } catch (error) {
1197
+ logger.debug("Antigravity CLI not available", { error });
1198
+ return false;
1199
+ }
1200
+ }
1201
+
1071
1202
  // src/types/index.ts
1072
1203
  var UserAbortedCommitError = class extends Error {
1073
1204
  constructor(message = "User aborted the commit") {
@@ -1077,7 +1208,12 @@ var UserAbortedCommitError = class extends Error {
1077
1208
  };
1078
1209
 
1079
1210
  // src/lib/CommitManager.ts
1211
+ import { writeFile, readFile as readFile2, unlink } from "fs/promises";
1212
+ import { join } from "path";
1213
+ import { execa as execa2 } from "execa";
1080
1214
  var CommitManager = class {
1215
+ constructor() {
1216
+ }
1081
1217
  /**
1082
1218
  * Detect uncommitted changes in a worktree
1083
1219
  * Parses git status --porcelain output into structured GitStatus
@@ -1106,11 +1242,11 @@ var CommitManager = class {
1106
1242
  */
1107
1243
  async commitChanges(worktreePath, options) {
1108
1244
  if (options.dryRun) {
1109
- logger.info("[DRY RUN] Would run: git add -A");
1110
- logger.info("[DRY RUN] Would generate commit message with Claude (if available)");
1245
+ getLogger().info("[DRY RUN] Would run: git add -A");
1246
+ getLogger().info("[DRY RUN] Would generate commit message with Claude (if available)");
1111
1247
  const fallbackMessage = this.generateFallbackMessage(options);
1112
1248
  const verifyFlag = options.skipVerify ? " --no-verify" : "";
1113
- logger.info(`[DRY RUN] Would commit with message${verifyFlag}: ${fallbackMessage}`);
1249
+ getLogger().info(`[DRY RUN] Would commit with message${verifyFlag}: ${fallbackMessage}`);
1114
1250
  return;
1115
1251
  }
1116
1252
  await executeGitCommand(["add", "-A"], { cwd: worktreePath });
@@ -1119,12 +1255,12 @@ var CommitManager = class {
1119
1255
  try {
1120
1256
  message = await this.generateClaudeCommitMessage(worktreePath, options.issueNumber);
1121
1257
  } catch (error) {
1122
- logger.debug("Claude commit message generation failed, using fallback", { error });
1258
+ getLogger().debug("Claude commit message generation failed, using fallback", { error });
1123
1259
  }
1124
1260
  }
1125
1261
  message ??= this.generateFallbackMessage(options);
1126
1262
  if (options.skipVerify) {
1127
- logger.warn("\u26A0\uFE0F Skipping pre-commit hooks (--no-verify configured in settings)");
1263
+ getLogger().warn("Skipping pre-commit hooks (--no-verify configured in settings)");
1128
1264
  }
1129
1265
  try {
1130
1266
  if (options.noReview || options.message) {
@@ -1145,17 +1281,25 @@ var CommitManager = class {
1145
1281
  }
1146
1282
  await executeGitCommand(commitArgs, { cwd: worktreePath });
1147
1283
  } else {
1148
- logger.info("Opening git editor for commit message review...");
1149
- const commitArgs = ["commit", "-e", "-m", message];
1150
- if (options.skipVerify) {
1151
- commitArgs.push("--no-verify");
1284
+ getLogger().info("Opening editor for commit message review...");
1285
+ if (isRunningInAntigravity() && await isAntigravityAvailable()) {
1286
+ await this.commitWithExternalEditor(worktreePath, message, options, "agy", "Antigravity");
1287
+ } else if (isRunningInCursor() && await isCursorAvailable()) {
1288
+ await this.commitWithExternalEditor(worktreePath, message, options, "cursor", "Cursor");
1289
+ } else if (isRunningInVSCode() && await isVSCodeAvailable()) {
1290
+ await this.commitWithExternalEditor(worktreePath, message, options, "code", "VSCode");
1291
+ } else {
1292
+ const commitArgs = ["commit", "-e", "-m", message];
1293
+ if (options.skipVerify) {
1294
+ commitArgs.push("--no-verify");
1295
+ }
1296
+ await executeGitCommand(commitArgs, {
1297
+ cwd: worktreePath,
1298
+ stdio: "inherit",
1299
+ timeout: 3e5
1300
+ // 5 minutes for interactive editing
1301
+ });
1152
1302
  }
1153
- await executeGitCommand(commitArgs, {
1154
- cwd: worktreePath,
1155
- stdio: "inherit",
1156
- timeout: 3e5
1157
- // 5 minutes for interactive editing
1158
- });
1159
1303
  }
1160
1304
  }
1161
1305
  } catch (error) {
@@ -1163,12 +1307,51 @@ var CommitManager = class {
1163
1307
  throw error;
1164
1308
  }
1165
1309
  if (error instanceof Error && error.message.includes("nothing to commit")) {
1166
- logger.info("No changes to commit");
1310
+ getLogger().info("No changes to commit");
1167
1311
  return;
1168
1312
  }
1169
1313
  throw error;
1170
1314
  }
1171
1315
  }
1316
+ /**
1317
+ * Commit with external editor CLI (VSCode, Cursor, Antigravity, etc.)
1318
+ * Handles file creation, editing, and commit to ensure the file opens
1319
+ * in the current editor window (preserves IPC context)
1320
+ */
1321
+ async commitWithExternalEditor(worktreePath, message, options, cliCommand, editorName) {
1322
+ const commitMsgPath = join(worktreePath, ".COMMIT_EDITMSG");
1323
+ const initialContent = `${message}
1324
+
1325
+ # Please enter the commit message for your changes. Lines starting
1326
+ # with '#' will be ignored, and an empty message aborts the commit.
1327
+ #
1328
+ # Save and close the file to complete the commit.
1329
+ `;
1330
+ await writeFile(commitMsgPath, initialContent, "utf-8");
1331
+ try {
1332
+ getLogger().debug(`Opening commit message in ${editorName}: ${commitMsgPath}`);
1333
+ await execa2(cliCommand, ["--wait", commitMsgPath], {
1334
+ cwd: worktreePath,
1335
+ stdio: "inherit"
1336
+ });
1337
+ const editedContent = await readFile2(commitMsgPath, "utf-8");
1338
+ const finalMessage = editedContent.split("\n").filter((line) => !line.startsWith("#")).join("\n").trim();
1339
+ if (!finalMessage) {
1340
+ throw new UserAbortedCommitError();
1341
+ }
1342
+ const commitArgs = ["commit", "-F", commitMsgPath];
1343
+ if (options.skipVerify) {
1344
+ commitArgs.push("--no-verify");
1345
+ }
1346
+ await writeFile(commitMsgPath, finalMessage, "utf-8");
1347
+ await executeGitCommand(commitArgs, { cwd: worktreePath });
1348
+ } finally {
1349
+ try {
1350
+ await unlink(commitMsgPath);
1351
+ } catch {
1352
+ }
1353
+ }
1354
+ }
1172
1355
  /**
1173
1356
  * Generate simple fallback commit message when Claude unavailable
1174
1357
  * Used as fallback for Claude-powered commit messages
@@ -1222,61 +1405,62 @@ Fixes #${options.issueNumber}`;
1222
1405
  */
1223
1406
  async generateClaudeCommitMessage(worktreePath, issueNumber) {
1224
1407
  const startTime = Date.now();
1225
- logger.info("Starting Claude commit message generation...", {
1408
+ getLogger().info("Starting Claude commit message generation...", {
1226
1409
  worktreePath: worktreePath.split("/").pop(),
1227
1410
  // Just show the folder name for privacy
1228
1411
  issueNumber
1229
1412
  });
1230
- logger.debug("Checking Claude CLI availability...");
1413
+ getLogger().debug("Checking Claude CLI availability...");
1231
1414
  const isClaudeAvailable = await detectClaudeCli();
1232
1415
  if (!isClaudeAvailable) {
1233
- logger.info("Claude CLI not available, skipping Claude commit message generation");
1416
+ getLogger().info("Claude CLI not available, skipping Claude commit message generation");
1234
1417
  return null;
1235
1418
  }
1236
- logger.debug("Claude CLI is available");
1237
- logger.debug("Building commit message prompt...");
1419
+ getLogger().debug("Claude CLI is available");
1420
+ getLogger().debug("Building commit message prompt...");
1238
1421
  const prompt = this.buildCommitMessagePrompt(issueNumber);
1239
- logger.debug("Prompt built", { promptLength: prompt.length });
1240
- logger.debug("Claude prompt content:", {
1422
+ getLogger().debug("Prompt built", { promptLength: prompt.length });
1423
+ getLogger().debug("Claude prompt content:", {
1241
1424
  prompt,
1242
1425
  truncatedPreview: prompt.substring(0, 500) + (prompt.length > 500 ? "...[truncated]" : "")
1243
1426
  });
1244
1427
  try {
1245
- logger.info("Calling Claude CLI for commit message generation...");
1428
+ getLogger().info("Calling Claude CLI for commit message generation...");
1246
1429
  const claudeStartTime = Date.now();
1247
1430
  const claudeOptions = {
1248
1431
  headless: true,
1249
1432
  addDir: worktreePath,
1250
1433
  model: "claude-haiku-4-5-20251001",
1251
1434
  // Fast, cost-effective model
1252
- timeout: 12e4
1435
+ timeout: 12e4,
1253
1436
  // 120 second timeout
1437
+ appendSystemPrompt: "Output only the requested content. Never include preamble, analysis, or meta-commentary. Your response is used verbatim."
1254
1438
  };
1255
- logger.debug("Claude CLI call parameters:", {
1439
+ getLogger().debug("Claude CLI call parameters:", {
1256
1440
  options: claudeOptions,
1257
1441
  worktreePathForAnalysis: worktreePath,
1258
1442
  addDirContents: "Will include entire worktree directory for analysis"
1259
1443
  });
1260
1444
  const result = await launchClaude(prompt, claudeOptions);
1261
1445
  const claudeDuration = Date.now() - claudeStartTime;
1262
- logger.debug("Claude API call completed", { duration: `${claudeDuration}ms` });
1446
+ getLogger().debug("Claude API call completed", { duration: `${claudeDuration}ms` });
1263
1447
  if (typeof result !== "string") {
1264
- logger.warn("Claude returned non-string result", { resultType: typeof result });
1448
+ getLogger().warn("Claude returned non-string result", { resultType: typeof result });
1265
1449
  return null;
1266
1450
  }
1267
- logger.debug("Raw Claude output received", {
1451
+ getLogger().debug("Raw Claude output received", {
1268
1452
  outputLength: result.length,
1269
1453
  preview: result.substring(0, 200) + (result.length > 200 ? "..." : "")
1270
1454
  });
1271
- logger.debug("Sanitizing Claude output...");
1455
+ getLogger().debug("Sanitizing Claude output...");
1272
1456
  const sanitized = this.sanitizeClaudeOutput(result);
1273
- logger.debug("Output sanitized", {
1457
+ getLogger().debug("Output sanitized", {
1274
1458
  originalLength: result.length,
1275
1459
  sanitizedLength: sanitized.length,
1276
1460
  sanitized: sanitized.substring(0, 200) + (sanitized.length > 200 ? "..." : "")
1277
1461
  });
1278
1462
  if (!sanitized) {
1279
- logger.warn("Claude returned empty message after sanitization");
1463
+ getLogger().warn("Claude returned empty message after sanitization");
1280
1464
  return null;
1281
1465
  }
1282
1466
  let finalMessage = sanitized;
@@ -1285,13 +1469,13 @@ Fixes #${options.issueNumber}`;
1285
1469
  finalMessage = `${finalMessage}
1286
1470
 
1287
1471
  Fixes #${issueNumber}`;
1288
- logger.debug(`Added "Fixes #${issueNumber}" trailer to commit message`);
1472
+ getLogger().debug(`Added "Fixes #${issueNumber}" trailer to commit message`);
1289
1473
  } else {
1290
- logger.debug(`"Fixes #${issueNumber}" already present in commit message`);
1474
+ getLogger().debug(`"Fixes #${issueNumber}" already present in commit message`);
1291
1475
  }
1292
1476
  }
1293
1477
  const totalDuration = Date.now() - startTime;
1294
- logger.info("Claude commit message generated successfully", {
1478
+ getLogger().info("Claude commit message generated successfully", {
1295
1479
  message: finalMessage,
1296
1480
  totalDuration: `${totalDuration}ms`,
1297
1481
  claudeApiDuration: `${claudeDuration}ms`
@@ -1301,12 +1485,12 @@ Fixes #${issueNumber}`;
1301
1485
  const totalDuration = Date.now() - startTime;
1302
1486
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
1303
1487
  if (errorMessage.includes("timed out") || errorMessage.includes("timeout")) {
1304
- logger.warn("Claude commit message generation timed out after 45 seconds", {
1488
+ getLogger().warn("Claude commit message generation timed out after 45 seconds", {
1305
1489
  totalDuration: `${totalDuration}ms`,
1306
1490
  worktreePath: worktreePath.split("/").pop()
1307
1491
  });
1308
1492
  } else {
1309
- logger.warn("Failed to generate commit message with Claude", {
1493
+ getLogger().warn("Failed to generate commit message with Claude", {
1310
1494
  error: errorMessage,
1311
1495
  totalDuration: `${totalDuration}ms`,
1312
1496
  worktreePath: worktreePath.split("/").pop()
@@ -1418,7 +1602,7 @@ var BuildRunner = class {
1418
1602
  const pkgJson = await readPackageJson(buildPath);
1419
1603
  const hasBuildScript = hasScript(pkgJson, "build");
1420
1604
  if (!hasBuildScript) {
1421
- logger.debug("Skipping build - no build script found");
1605
+ getLogger().debug("Skipping build - no build script found");
1422
1606
  return {
1423
1607
  success: true,
1424
1608
  skipped: true,
@@ -1428,7 +1612,7 @@ var BuildRunner = class {
1428
1612
  }
1429
1613
  } catch (error) {
1430
1614
  if (error instanceof Error && error.message.includes("package.json not found")) {
1431
- logger.debug("Skipping build - no package.json found (non-Node.js project)");
1615
+ getLogger().debug("Skipping build - no package.json found (non-Node.js project)");
1432
1616
  return {
1433
1617
  success: true,
1434
1618
  skipped: true,
@@ -1441,7 +1625,7 @@ var BuildRunner = class {
1441
1625
  const capabilities = await this.capabilityDetector.detectCapabilities(buildPath);
1442
1626
  const isCLIProject = capabilities.capabilities.includes("cli");
1443
1627
  if (!isCLIProject) {
1444
- logger.debug("Skipping build - not a CLI project (no bin field)");
1628
+ getLogger().debug("Skipping build - not a CLI project (no bin field)");
1445
1629
  return {
1446
1630
  success: true,
1447
1631
  skipped: true,
@@ -1452,17 +1636,17 @@ var BuildRunner = class {
1452
1636
  const packageManager = await detectPackageManager(buildPath);
1453
1637
  if (options.dryRun) {
1454
1638
  const command = packageManager === "npm" ? "npm run build" : `${packageManager} build`;
1455
- logger.info(`[DRY RUN] Would run: ${command}`);
1639
+ getLogger().info(`[DRY RUN] Would run: ${command}`);
1456
1640
  return {
1457
1641
  success: true,
1458
1642
  skipped: false,
1459
1643
  duration: Date.now() - startTime
1460
1644
  };
1461
1645
  }
1462
- logger.info("Running build...");
1646
+ getLogger().info("Running build...");
1463
1647
  try {
1464
1648
  await runScript("build", buildPath, [], { quiet: true });
1465
- logger.success("Build completed successfully");
1649
+ getLogger().success("Build completed successfully");
1466
1650
  return {
1467
1651
  success: true,
1468
1652
  skipped: false,
@@ -1502,7 +1686,7 @@ var PRManager = class {
1502
1686
  }
1503
1687
  return null;
1504
1688
  } catch (error) {
1505
- logger.debug("Error checking for existing PR", { error });
1689
+ getLogger().debug("Error checking for existing PR", { error });
1506
1690
  return null;
1507
1691
  }
1508
1692
  }
@@ -1529,7 +1713,7 @@ var PRManager = class {
1529
1713
  }
1530
1714
  }
1531
1715
  } catch (error) {
1532
- logger.debug("Claude PR body generation failed, using template", { error });
1716
+ getLogger().debug("Claude PR body generation failed, using template", { error });
1533
1717
  }
1534
1718
  }
1535
1719
  let body = "This PR contains changes from the iloom workflow.\n\n";
@@ -1647,7 +1831,7 @@ Start your response immediately with the PR body text.
1647
1831
  const originRemote = remotes.find((r) => r.name === "origin");
1648
1832
  if (originRemote) {
1649
1833
  headValue = `${originRemote.owner}:${branchName}`;
1650
- logger.debug(`Fork workflow detected, using head: ${headValue}`);
1834
+ getLogger().debug(`Fork workflow detected, using head: ${headValue}`);
1651
1835
  }
1652
1836
  }
1653
1837
  const args = ["pr", "create", "--head", headValue, "--title", title, "--body", body, "--base", baseBranch];
@@ -1686,9 +1870,9 @@ Then retry: il finish`
1686
1870
  async openPRInBrowser(url) {
1687
1871
  try {
1688
1872
  await openBrowser(url);
1689
- logger.debug("Opened PR in browser", { url });
1873
+ getLogger().debug("Opened PR in browser", { url });
1690
1874
  } catch (error) {
1691
- logger.warn("Failed to open PR in browser", { error });
1875
+ getLogger().warn("Failed to open PR in browser", { error });
1692
1876
  }
1693
1877
  }
1694
1878
  /**
@@ -1704,7 +1888,7 @@ Then retry: il finish`
1704
1888
  async createOrOpenPR(branchName, title, issueNumber, baseBranch, worktreePath, openInBrowser) {
1705
1889
  const existingPR = await this.checkForExistingPR(branchName, worktreePath);
1706
1890
  if (existingPR) {
1707
- logger.info(`Pull request already exists: ${existingPR.url}`);
1891
+ getLogger().info(`Pull request already exists: ${existingPR.url}`);
1708
1892
  if (openInBrowser) {
1709
1893
  await this.openPRInBrowser(existingPR.url);
1710
1894
  }
@@ -1715,7 +1899,7 @@ Then retry: il finish`
1715
1899
  };
1716
1900
  }
1717
1901
  const body = await this.generatePRBody(issueNumber, worktreePath);
1718
- logger.info("Creating pull request...");
1902
+ getLogger().info("Creating pull request...");
1719
1903
  const url = await this.createPR(branchName, title, body, baseBranch, worktreePath);
1720
1904
  const prNumber = this.extractPRNumberFromUrl(url);
1721
1905
  if (openInBrowser) {
@@ -1747,10 +1931,10 @@ var FinishCommand = class {
1747
1931
  constructor(issueTracker, gitWorktreeManager, validationRunner, commitManager, mergeManager, identifierParser, resourceCleanup, buildRunner, settingsManager, loomManager) {
1748
1932
  const envResult = loadEnvIntoProcess();
1749
1933
  if (envResult.error) {
1750
- logger.debug(`Environment loading warning: ${envResult.error.message}`);
1934
+ getLogger().debug(`Environment loading warning: ${envResult.error.message}`);
1751
1935
  }
1752
1936
  if (envResult.parsed) {
1753
- logger.debug(`Loaded ${Object.keys(envResult.parsed).length} environment variables`);
1937
+ getLogger().debug(`Loaded ${Object.keys(envResult.parsed).length} environment variables`);
1754
1938
  }
1755
1939
  this.issueTracker = issueTracker;
1756
1940
  this.gitWorktreeManager = gitWorktreeManager ?? new GitWorktreeManager();
@@ -1781,7 +1965,7 @@ var FinishCommand = class {
1781
1965
  const neonProvider = createNeonProviderFromSettings(settings);
1782
1966
  const databaseManager = new DatabaseManager(neonProvider, environmentManager, databaseUrlEnvVarName);
1783
1967
  const cliIsolationManager = new CLIIsolationManager();
1784
- const { DefaultBranchNamingService: DefaultBranchNamingService2 } = await import("./BranchNamingService-A77VI6AI.js");
1968
+ const { DefaultBranchNamingService: DefaultBranchNamingService2 } = await import("./BranchNamingService-GCCWB3LK.js");
1785
1969
  this.loomManager ??= new LoomManager(
1786
1970
  this.gitWorktreeManager,
1787
1971
  this.issueTracker,
@@ -1826,12 +2010,12 @@ var FinishCommand = class {
1826
2010
  targetBranch = worktree == null ? void 0 : worktree.branch;
1827
2011
  }
1828
2012
  if (!targetBranch) {
1829
- logger.debug(`Cannot determine target branch for child loom check`);
2013
+ getLogger().debug(`Cannot determine target branch for child loom check`);
1830
2014
  return;
1831
2015
  }
1832
2016
  const hasChildLooms = await this.loomManager.checkAndWarnChildLooms(targetBranch);
1833
2017
  if (hasChildLooms) {
1834
- logger.error("Cannot finish loom while child looms exist. Please finish child looms first.");
2018
+ getLogger().error("Cannot finish loom while child looms exist. Please 'finish' or 'cleanup' child looms first.");
1835
2019
  process.exit(1);
1836
2020
  }
1837
2021
  }
@@ -1839,42 +2023,53 @@ var FinishCommand = class {
1839
2023
  * Main entry point for finish command
1840
2024
  */
1841
2025
  async execute(input) {
1842
- var _a;
1843
- try {
1844
- const settings = await this.settingsManager.loadSettings();
1845
- let repo;
1846
- const needsRepo = ((_a = settings.mergeBehavior) == null ? void 0 : _a.mode) === "github-pr" || this.issueTracker.providerName === "github";
1847
- if (needsRepo && await hasMultipleRemotes()) {
1848
- repo = await getConfiguredRepoFromSettings(settings);
1849
- logger.info(`Using GitHub repository: ${repo}`);
1850
- }
1851
- const parsed = await this.parseInput(input.identifier, input.options);
1852
- await this.checkForChildLooms(parsed);
1853
- const worktrees = await this.validateInput(parsed, input.options, repo);
1854
- logger.info(`Validated input: ${this.formatParsedInput(parsed)}`);
1855
- const worktree = worktrees[0];
1856
- if (!worktree) {
1857
- throw new Error("No worktree found");
1858
- }
1859
- if (parsed.type === "pr") {
1860
- if (!parsed.number) {
1861
- throw new Error("Invalid PR number");
1862
- }
1863
- if (!this.issueTracker.supportsPullRequests || !this.issueTracker.fetchPR) {
1864
- throw new Error("Issue tracker does not support pull requests");
1865
- }
1866
- const pr = await this.issueTracker.fetchPR(parsed.number, repo);
1867
- await this.executePRWorkflow(parsed, input.options, worktree, pr);
1868
- } else {
1869
- await this.executeIssueWorkflow(parsed, input.options, worktree);
2026
+ var _a, _b;
2027
+ const isJsonMode = input.options.json === true;
2028
+ const result = {
2029
+ success: false,
2030
+ type: "issue",
2031
+ identifier: "",
2032
+ dryRun: input.options.dryRun ?? false,
2033
+ operations: []
2034
+ };
2035
+ if (isJsonMode) {
2036
+ const settings2 = await this.settingsManager.loadSettings();
2037
+ if (((_a = settings2.mergeBehavior) == null ? void 0 : _a.mode) === "github-pr" && input.options.cleanup === void 0) {
2038
+ throw new Error("JSON mode with github-pr workflow requires --cleanup or --no-cleanup flag. Use: il finish --json --cleanup <identifier>");
1870
2039
  }
1871
- } catch (error) {
1872
- if (error instanceof Error) {
1873
- logger.error(`${error.message}`);
1874
- } else {
1875
- logger.error("An unknown error occurred");
2040
+ }
2041
+ const settings = await this.settingsManager.loadSettings();
2042
+ let repo;
2043
+ const needsRepo = ((_b = settings.mergeBehavior) == null ? void 0 : _b.mode) === "github-pr" || this.issueTracker.providerName === "github";
2044
+ if (needsRepo && await hasMultipleRemotes()) {
2045
+ repo = await getConfiguredRepoFromSettings(settings);
2046
+ getLogger().info(`Using GitHub repository: ${repo}`);
2047
+ }
2048
+ const parsed = await this.parseInput(input.identifier, input.options);
2049
+ result.type = parsed.type;
2050
+ result.identifier = parsed.number ?? parsed.branchName ?? "";
2051
+ await this.checkForChildLooms(parsed);
2052
+ const worktrees = await this.validateInput(parsed, input.options, repo);
2053
+ getLogger().info(`Validated input: ${this.formatParsedInput(parsed)}`);
2054
+ const worktree = worktrees[0];
2055
+ if (!worktree) {
2056
+ throw new Error("No worktree found");
2057
+ }
2058
+ if (parsed.type === "pr") {
2059
+ if (!parsed.number) {
2060
+ throw new Error("Invalid PR number");
1876
2061
  }
1877
- throw error;
2062
+ if (!this.issueTracker.supportsPullRequests || !this.issueTracker.fetchPR) {
2063
+ throw new Error("Issue tracker does not support pull requests");
2064
+ }
2065
+ const pr = await this.issueTracker.fetchPR(parsed.number, repo);
2066
+ await this.executePRWorkflow(parsed, input.options, worktree, pr, result);
2067
+ } else {
2068
+ await this.executeIssueWorkflow(parsed, input.options, worktree, result);
2069
+ }
2070
+ result.success = true;
2071
+ if (isJsonMode) {
2072
+ return result;
1878
2073
  }
1879
2074
  }
1880
2075
  /**
@@ -1937,7 +2132,7 @@ var FinishCommand = class {
1937
2132
  const prMatch = currentDir.match(prPattern);
1938
2133
  if (prMatch == null ? void 0 : prMatch[1]) {
1939
2134
  const prNumber = parseInt(prMatch[1], 10);
1940
- logger.debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`);
2135
+ getLogger().debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`);
1941
2136
  return {
1942
2137
  type: "pr",
1943
2138
  number: prNumber,
@@ -1947,7 +2142,7 @@ var FinishCommand = class {
1947
2142
  }
1948
2143
  const issueNumber = extractIssueNumber(currentDir);
1949
2144
  if (issueNumber !== null) {
1950
- logger.debug(
2145
+ getLogger().debug(
1951
2146
  `Auto-detected issue #${issueNumber} from directory: ${currentDir}`
1952
2147
  );
1953
2148
  return {
@@ -1966,7 +2161,7 @@ var FinishCommand = class {
1966
2161
  }
1967
2162
  const branchIssueNumber = extractIssueNumber(currentBranch);
1968
2163
  if (branchIssueNumber !== null) {
1969
- logger.debug(
2164
+ getLogger().debug(
1970
2165
  `Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`
1971
2166
  );
1972
2167
  return {
@@ -1996,7 +2191,7 @@ var FinishCommand = class {
1996
2191
  throw new Error("Issue tracker does not support pull requests");
1997
2192
  }
1998
2193
  const pr = await this.issueTracker.fetchPR(parsed.number);
1999
- logger.debug(`Validated PR #${parsed.number} (state: ${pr.state})`);
2194
+ getLogger().debug(`Validated PR #${parsed.number} (state: ${pr.state})`);
2000
2195
  return await this.findWorktreeForIdentifier(parsed);
2001
2196
  }
2002
2197
  case "issue": {
@@ -2009,7 +2204,7 @@ var FinishCommand = class {
2009
2204
  `Issue #${parsed.number} is closed. Use --force to finish anyway.`
2010
2205
  );
2011
2206
  }
2012
- logger.debug(`Validated issue #${parsed.number} (state: ${issue.state})`);
2207
+ getLogger().debug(`Validated issue #${parsed.number} (state: ${issue.state})`);
2013
2208
  return await this.findWorktreeForIdentifier(parsed);
2014
2209
  }
2015
2210
  case "branch": {
@@ -2021,7 +2216,7 @@ var FinishCommand = class {
2021
2216
  "Invalid branch name. Use only letters, numbers, hyphens, underscores, and slashes"
2022
2217
  );
2023
2218
  }
2024
- logger.debug(`Validated branch name: ${parsed.branchName}`);
2219
+ getLogger().debug(`Validated branch name: ${parsed.branchName}`);
2025
2220
  return await this.findWorktreeForIdentifier(parsed);
2026
2221
  }
2027
2222
  default: {
@@ -2080,7 +2275,7 @@ var FinishCommand = class {
2080
2275
  `No worktree found for ${this.formatParsedInput(parsed)}. Use 'il list' to see available worktrees.`
2081
2276
  );
2082
2277
  }
2083
- logger.debug(`Found worktree: ${worktree.path}`);
2278
+ getLogger().debug(`Found worktree: ${worktree.path}`);
2084
2279
  return [worktree];
2085
2280
  }
2086
2281
  /**
@@ -2109,23 +2304,38 @@ var FinishCommand = class {
2109
2304
  * Execute workflow for issues and branches (merge into main)
2110
2305
  * This is the traditional workflow: validate → commit → rebase → merge → cleanup
2111
2306
  */
2112
- async executeIssueWorkflow(parsed, options, worktree) {
2307
+ async executeIssueWorkflow(parsed, options, worktree, result) {
2113
2308
  var _a, _b;
2114
2309
  if (!options.dryRun) {
2115
- logger.info("Running pre-merge validations...");
2310
+ getLogger().info("Running pre-merge validations...");
2116
2311
  await this.validationRunner.runValidations(worktree.path, {
2117
2312
  dryRun: options.dryRun ?? false
2118
2313
  });
2119
- logger.success("All validations passed");
2314
+ getLogger().success("All validations passed");
2315
+ result.operations.push({
2316
+ type: "validation",
2317
+ message: "Pre-merge validations passed",
2318
+ success: true
2319
+ });
2120
2320
  } else {
2121
- logger.info("[DRY RUN] Would run pre-merge validations");
2321
+ getLogger().info("[DRY RUN] Would run pre-merge validations");
2322
+ result.operations.push({
2323
+ type: "validation",
2324
+ message: "Would run pre-merge validations (dry-run)",
2325
+ success: true
2326
+ });
2122
2327
  }
2123
2328
  const gitStatus = await this.commitManager.detectUncommittedChanges(worktree.path);
2124
2329
  if (gitStatus.hasUncommittedChanges) {
2125
2330
  if (options.dryRun) {
2126
- logger.info("[DRY RUN] Would auto-commit uncommitted changes (validation passed)");
2331
+ getLogger().info("[DRY RUN] Would auto-commit uncommitted changes (validation passed)");
2332
+ result.operations.push({
2333
+ type: "commit",
2334
+ message: "Would auto-commit uncommitted changes (dry-run)",
2335
+ success: true
2336
+ });
2127
2337
  } else {
2128
- logger.info("Validation passed, auto-committing uncommitted changes...");
2338
+ getLogger().info("Validation passed, auto-committing uncommitted changes...");
2129
2339
  const settings2 = await this.settingsManager.loadSettings(worktree.path);
2130
2340
  const skipVerify = ((_b = (_a = settings2.workflows) == null ? void 0 : _a.issue) == null ? void 0 : _b.noVerify) ?? false;
2131
2341
  const commitOptions = {
@@ -2137,17 +2347,27 @@ var FinishCommand = class {
2137
2347
  }
2138
2348
  try {
2139
2349
  await this.commitManager.commitChanges(worktree.path, commitOptions);
2140
- logger.success("Changes committed successfully");
2350
+ getLogger().success("Changes committed successfully");
2351
+ result.operations.push({
2352
+ type: "commit",
2353
+ message: "Changes committed successfully",
2354
+ success: true
2355
+ });
2141
2356
  } catch (error) {
2142
2357
  if (error instanceof UserAbortedCommitError) {
2143
- logger.info("Commit aborted by user");
2358
+ getLogger().info("Commit aborted by user");
2359
+ result.operations.push({
2360
+ type: "commit",
2361
+ message: "Commit aborted by user",
2362
+ success: false
2363
+ });
2144
2364
  return;
2145
2365
  }
2146
2366
  throw error;
2147
2367
  }
2148
2368
  }
2149
2369
  } else {
2150
- logger.debug("No uncommitted changes found");
2370
+ getLogger().debug("No uncommitted changes found");
2151
2371
  }
2152
2372
  const settings = await this.settingsManager.loadSettings(worktree.path);
2153
2373
  const mergeBehavior = settings.mergeBehavior ?? { mode: "local" };
@@ -2157,32 +2377,43 @@ var FinishCommand = class {
2157
2377
  `The 'github-pr' merge mode requires a GitHub-compatible issue tracker. Your current provider (${this.issueTracker.providerName}) does not support pull requests. Either change mergeBehavior.mode to 'local' in your settings, or use GitHub as your issue tracker.`
2158
2378
  );
2159
2379
  }
2160
- await this.executeGitHubPRWorkflow(parsed, options, worktree, settings);
2380
+ await this.executeGitHubPRWorkflow(parsed, options, worktree, settings, result);
2161
2381
  return;
2162
2382
  }
2163
- logger.info("Rebasing branch on main...");
2383
+ getLogger().info("Rebasing branch on main...");
2164
2384
  const mergeOptions = {
2165
2385
  dryRun: options.dryRun ?? false,
2166
2386
  force: options.force ?? false
2167
2387
  };
2168
2388
  await this.mergeManager.rebaseOnMain(worktree.path, mergeOptions);
2169
- logger.success("Branch rebased successfully");
2170
- logger.info("Performing fast-forward merge...");
2389
+ getLogger().success("Branch rebased successfully");
2390
+ result.operations.push({
2391
+ type: "rebase",
2392
+ message: "Branch rebased on main",
2393
+ success: true
2394
+ });
2395
+ getLogger().info("Performing fast-forward merge...");
2171
2396
  await this.mergeManager.performFastForwardMerge(worktree.branch, worktree.path, mergeOptions);
2172
- logger.success("Fast-forward merge completed successfully");
2397
+ getLogger().success("Fast-forward merge completed successfully");
2398
+ result.operations.push({
2399
+ type: "merge",
2400
+ message: "Fast-forward merge completed",
2401
+ success: true
2402
+ });
2173
2403
  if (options.dryRun) {
2174
- logger.info("[DRY RUN] Would install dependencies in main worktree");
2404
+ getLogger().info("[DRY RUN] Would install dependencies in main worktree");
2175
2405
  } else {
2176
- logger.info("Installing dependencies in main worktree...");
2406
+ getLogger().info("Installing dependencies in main worktree...");
2177
2407
  const mainWorktreePath = await findMainWorktreePathWithSettings(worktree.path, this.settingsManager);
2178
2408
  await installDependencies(mainWorktreePath, true, true);
2179
2409
  }
2180
2410
  if (!options.skipBuild) {
2181
- await this.runPostMergeBuild(worktree.path, options);
2411
+ await this.runPostMergeBuild(worktree.path, options, result);
2182
2412
  } else {
2183
- logger.debug("Skipping build verification (--skip-build flag provided)");
2413
+ getLogger().debug("Skipping build verification (--skip-build flag provided)");
2184
2414
  }
2185
- await this.performPostMergeCleanup(parsed, options, worktree);
2415
+ await this.generateSessionSummaryIfConfigured(parsed, worktree, options);
2416
+ await this.performPostMergeCleanup(parsed, options, worktree, result);
2186
2417
  }
2187
2418
  /**
2188
2419
  * Execute workflow for Pull Requests
@@ -2190,27 +2421,37 @@ var FinishCommand = class {
2190
2421
  * - OPEN: Commit changes, push to remote, keep worktree active
2191
2422
  * - CLOSED/MERGED: Skip to cleanup
2192
2423
  */
2193
- async executePRWorkflow(parsed, options, worktree, pr) {
2424
+ async executePRWorkflow(parsed, options, worktree, pr, result) {
2194
2425
  var _a, _b;
2195
2426
  if (pr.state === "closed" || pr.state === "merged") {
2196
- logger.info(`PR #${parsed.number} is ${pr.state.toUpperCase()} - skipping to cleanup`);
2427
+ getLogger().info(`PR #${parsed.number} is ${pr.state.toUpperCase()} - skipping to cleanup`);
2197
2428
  const gitStatus = await this.commitManager.detectUncommittedChanges(worktree.path);
2198
2429
  if (gitStatus.hasUncommittedChanges && !options.force) {
2199
- logger.warn("PR has uncommitted changes");
2430
+ getLogger().warn("PR has uncommitted changes");
2200
2431
  throw new Error(
2201
2432
  "Cannot cleanup PR with uncommitted changes. Commit or stash changes, then run again with --force to cleanup anyway."
2202
2433
  );
2203
2434
  }
2204
- await this.performPRCleanup(parsed, options, worktree);
2205
- logger.success(`PR #${parsed.number} cleanup completed`);
2435
+ await this.performPRCleanup(parsed, options, worktree, pr.state, result);
2436
+ getLogger().success(`PR #${parsed.number} cleanup completed`);
2437
+ result.operations.push({
2438
+ type: "cleanup",
2439
+ message: `PR #${parsed.number} cleanup completed`,
2440
+ success: true
2441
+ });
2206
2442
  } else {
2207
- logger.info(`PR #${parsed.number} is OPEN - will push changes and keep worktree active`);
2443
+ getLogger().info(`PR #${parsed.number} is OPEN - will push changes and keep worktree active`);
2208
2444
  const gitStatus = await this.commitManager.detectUncommittedChanges(worktree.path);
2209
2445
  if (gitStatus.hasUncommittedChanges) {
2210
2446
  if (options.dryRun) {
2211
- logger.info("[DRY RUN] Would commit uncommitted changes");
2447
+ getLogger().info("[DRY RUN] Would commit uncommitted changes");
2448
+ result.operations.push({
2449
+ type: "commit",
2450
+ message: "Would commit uncommitted changes (dry-run)",
2451
+ success: true
2452
+ });
2212
2453
  } else {
2213
- logger.info("Committing uncommitted changes...");
2454
+ getLogger().info("Committing uncommitted changes...");
2214
2455
  const settings = await this.settingsManager.loadSettings(worktree.path);
2215
2456
  const skipVerify = ((_b = (_a = settings.workflows) == null ? void 0 : _a.pr) == null ? void 0 : _b.noVerify) ?? false;
2216
2457
  try {
@@ -2219,43 +2460,54 @@ var FinishCommand = class {
2219
2460
  skipVerify
2220
2461
  // Do NOT pass issueNumber for PRs - no "Fixes #" trailer needed
2221
2462
  });
2222
- logger.success("Changes committed");
2463
+ getLogger().success("Changes committed");
2464
+ result.operations.push({
2465
+ type: "commit",
2466
+ message: "Changes committed successfully",
2467
+ success: true
2468
+ });
2223
2469
  } catch (error) {
2224
2470
  if (error instanceof UserAbortedCommitError) {
2225
- logger.info("Commit aborted by user");
2471
+ getLogger().info("Commit aborted by user");
2472
+ result.operations.push({
2473
+ type: "commit",
2474
+ message: "Commit aborted by user",
2475
+ success: false
2476
+ });
2226
2477
  return;
2227
2478
  }
2228
2479
  throw error;
2229
2480
  }
2230
2481
  }
2231
2482
  } else {
2232
- logger.debug("No uncommitted changes found");
2483
+ getLogger().debug("No uncommitted changes found");
2233
2484
  }
2234
2485
  if (options.dryRun) {
2235
- logger.info(`[DRY RUN] Would push changes to origin/${pr.branch}`);
2486
+ getLogger().info(`[DRY RUN] Would push changes to origin/${pr.branch}`);
2236
2487
  } else {
2237
- logger.info("Pushing changes to remote...");
2488
+ getLogger().info("Pushing changes to remote...");
2238
2489
  await pushBranchToRemote(pr.branch, worktree.path, {
2239
2490
  dryRun: false
2240
2491
  });
2241
- logger.success(`Changes pushed to PR #${parsed.number}`);
2492
+ getLogger().success(`Changes pushed to PR #${parsed.number}`);
2242
2493
  }
2243
- logger.success(`PR #${parsed.number} updated successfully`);
2244
- logger.info("Worktree remains active for continued work");
2245
- logger.info(`To cleanup when done: il cleanup ${parsed.number}`);
2494
+ getLogger().success(`PR #${parsed.number} updated successfully`);
2495
+ getLogger().info("Worktree remains active for continued work");
2496
+ getLogger().info(`To cleanup when done: il cleanup ${parsed.number}`);
2497
+ result.prUrl = pr.url;
2246
2498
  }
2247
2499
  }
2248
2500
  /**
2249
2501
  * Execute workflow for GitHub PR creation (github-pr merge mode)
2250
2502
  * Validates → Commits → Pushes → Creates PR → Prompts for cleanup
2251
2503
  */
2252
- async executeGitHubPRWorkflow(parsed, options, worktree, settings) {
2504
+ async executeGitHubPRWorkflow(parsed, options, worktree, settings, finishResult) {
2253
2505
  if (options.dryRun) {
2254
- logger.info("[DRY RUN] Would push branch to origin");
2506
+ getLogger().info("[DRY RUN] Would push branch to origin");
2255
2507
  } else {
2256
- logger.info("Pushing branch to origin...");
2508
+ getLogger().info("Pushing branch to origin...");
2257
2509
  await pushBranchToRemote(worktree.branch, worktree.path, { dryRun: false });
2258
- logger.success("Branch pushed successfully");
2510
+ getLogger().success("Branch pushed successfully");
2259
2511
  }
2260
2512
  const prManager = new PRManager(settings);
2261
2513
  let prTitle = `Work from ${worktree.branch}`;
@@ -2264,17 +2516,22 @@ var FinishCommand = class {
2264
2516
  const issue = await this.issueTracker.fetchIssue(parsed.number);
2265
2517
  prTitle = issue.title;
2266
2518
  } catch (error) {
2267
- logger.debug("Could not fetch issue title, using branch name", { error });
2519
+ getLogger().debug("Could not fetch issue title, using branch name", { error });
2268
2520
  }
2269
2521
  }
2522
+ const baseBranch = await getMergeTargetBranch(worktree.path);
2270
2523
  if (options.dryRun) {
2271
- logger.info("[DRY RUN] Would create GitHub PR");
2272
- logger.info(` Title: ${prTitle}`);
2273
- logger.info(` Base: ${settings.mainBranch ?? "main"}`);
2524
+ getLogger().info("[DRY RUN] Would create GitHub PR");
2525
+ getLogger().info(` Title: ${prTitle}`);
2526
+ getLogger().info(` Base: ${baseBranch}`);
2527
+ finishResult.operations.push({
2528
+ type: "pr-creation",
2529
+ message: "Would create GitHub PR (dry-run)",
2530
+ success: true
2531
+ });
2274
2532
  } else {
2275
- const baseBranch = settings.mainBranch ?? "main";
2276
2533
  const openInBrowser = options.noBrowser !== true;
2277
- const result = await prManager.createOrOpenPR(
2534
+ const prResult = await prManager.createOrOpenPR(
2278
2535
  worktree.branch,
2279
2536
  prTitle,
2280
2537
  parsed.type === "issue" ? parsed.number : void 0,
@@ -2282,40 +2539,52 @@ var FinishCommand = class {
2282
2539
  worktree.path,
2283
2540
  openInBrowser
2284
2541
  );
2285
- if (result.wasExisting) {
2286
- logger.success(`Existing pull request: ${result.url}`);
2542
+ if (prResult.wasExisting) {
2543
+ getLogger().success(`Existing pull request: ${prResult.url}`);
2544
+ finishResult.operations.push({
2545
+ type: "pr-creation",
2546
+ message: `Found existing pull request`,
2547
+ success: true
2548
+ });
2287
2549
  } else {
2288
- logger.success(`Pull request created: ${result.url}`);
2550
+ getLogger().success(`Pull request created: ${prResult.url}`);
2551
+ finishResult.operations.push({
2552
+ type: "pr-creation",
2553
+ message: `Pull request created`,
2554
+ success: true
2555
+ });
2289
2556
  }
2290
- await this.handlePRCleanupPrompt(parsed, options, worktree);
2557
+ finishResult.prUrl = prResult.url;
2558
+ await this.generateSessionSummaryIfConfigured(parsed, worktree, options);
2559
+ await this.handlePRCleanupPrompt(parsed, options, worktree, finishResult);
2291
2560
  }
2292
2561
  }
2293
2562
  /**
2294
2563
  * Handle cleanup prompt after PR creation
2295
2564
  * Respects --cleanup and --no-cleanup flags, otherwise prompts user
2296
2565
  */
2297
- async handlePRCleanupPrompt(parsed, options, worktree) {
2566
+ async handlePRCleanupPrompt(parsed, options, worktree, finishResult) {
2298
2567
  if (options.cleanup === true) {
2299
- logger.info("Cleaning up worktree (--cleanup flag)...");
2300
- await this.performWorktreeCleanup(parsed, options, worktree);
2568
+ getLogger().info("Cleaning up worktree (--cleanup flag)...");
2569
+ await this.performWorktreeCleanup(parsed, options, worktree, finishResult);
2301
2570
  } else if (options.cleanup === false) {
2302
- logger.info("Worktree kept active for continued work (--no-cleanup flag)");
2303
- logger.info(`To cleanup later: il cleanup ${parsed.originalInput}`);
2571
+ getLogger().info("Worktree kept active for continued work (--no-cleanup flag)");
2572
+ getLogger().info(`To cleanup later: il cleanup ${parsed.originalInput}`);
2304
2573
  } else {
2305
- logger.info("");
2306
- logger.info("PR created successfully. Would you like to clean up the worktree?");
2307
- logger.info(` Worktree: ${worktree.path}`);
2308
- logger.info(` Branch: ${worktree.branch}`);
2309
- logger.info("");
2574
+ getLogger().info("");
2575
+ getLogger().info("PR created successfully. Would you like to clean up the worktree?");
2576
+ getLogger().info(` Worktree: ${worktree.path}`);
2577
+ getLogger().info(` Branch: ${worktree.branch}`);
2578
+ getLogger().info("");
2310
2579
  const shouldCleanup = await promptConfirmation(
2311
2580
  "Clean up worktree now?",
2312
2581
  false
2313
2582
  // Default to keeping worktree (safer option)
2314
2583
  );
2315
2584
  if (shouldCleanup) {
2316
- await this.performWorktreeCleanup(parsed, options, worktree);
2585
+ await this.performWorktreeCleanup(parsed, options, worktree, finishResult);
2317
2586
  } else {
2318
- logger.info("Worktree kept active. Run `il cleanup` when ready.");
2587
+ getLogger().info("Worktree kept active. Run `il cleanup` when ready.");
2319
2588
  }
2320
2589
  }
2321
2590
  }
@@ -2323,7 +2592,7 @@ var FinishCommand = class {
2323
2592
  * Perform worktree cleanup (used by GitHub PR workflow)
2324
2593
  * Similar to performPostMergeCleanup but for PR workflow
2325
2594
  */
2326
- async performWorktreeCleanup(parsed, options, worktree) {
2595
+ async performWorktreeCleanup(parsed, options, worktree, finishResult) {
2327
2596
  const cleanupInput = {
2328
2597
  type: parsed.type,
2329
2598
  originalInput: parsed.originalInput,
@@ -2339,56 +2608,98 @@ var FinishCommand = class {
2339
2608
  force: options.force ?? false
2340
2609
  };
2341
2610
  try {
2342
- logger.info("Starting worktree cleanup...");
2611
+ getLogger().info("Starting worktree cleanup...");
2343
2612
  await this.ensureResourceCleanup();
2344
2613
  if (!this.resourceCleanup) {
2345
2614
  throw new Error("Failed to initialize ResourceCleanup");
2346
2615
  }
2347
- const result = await this.resourceCleanup.cleanupWorktree(cleanupInput, cleanupOptions);
2348
- this.reportCleanupResults(result);
2349
- if (!result.success) {
2350
- logger.warn("Some cleanup operations failed - manual cleanup may be required");
2616
+ const cleanupResult = await this.resourceCleanup.cleanupWorktree(cleanupInput, cleanupOptions);
2617
+ this.reportCleanupResults(cleanupResult);
2618
+ finishResult.cleanupResult = cleanupResult;
2619
+ if (!cleanupResult.success) {
2620
+ getLogger().warn("Some cleanup operations failed - manual cleanup may be required");
2351
2621
  this.showManualCleanupInstructions(worktree);
2622
+ finishResult.operations.push({
2623
+ type: "cleanup",
2624
+ message: "Worktree cleanup partially failed",
2625
+ success: false
2626
+ });
2352
2627
  } else {
2353
- logger.success("Worktree cleanup completed successfully");
2628
+ getLogger().success("Worktree cleanup completed successfully");
2629
+ finishResult.operations.push({
2630
+ type: "cleanup",
2631
+ message: "Worktree cleanup completed",
2632
+ success: true
2633
+ });
2354
2634
  }
2355
2635
  if (this.isRunningFromWithinWorktree(worktree.path)) {
2356
2636
  this.showTerminalCloseWarning(worktree);
2357
2637
  }
2358
2638
  } catch (error) {
2359
2639
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
2360
- logger.warn(`Cleanup failed: ${errorMessage}`);
2361
- logger.warn("Manual cleanup may be required");
2640
+ getLogger().warn(`Cleanup failed: ${errorMessage}`);
2641
+ getLogger().warn("Manual cleanup may be required");
2362
2642
  this.showManualCleanupInstructions(worktree);
2643
+ finishResult.operations.push({
2644
+ type: "cleanup",
2645
+ message: "Worktree cleanup failed",
2646
+ success: false,
2647
+ error: errorMessage
2648
+ });
2363
2649
  }
2364
2650
  }
2365
2651
  /**
2366
2652
  * Perform cleanup for closed/merged PRs
2367
2653
  * Similar to performPostMergeCleanup but with different messaging
2654
+ *
2655
+ * Safety check behavior differs based on PR state:
2656
+ * - MERGED: Skip safety checks - work is safely in main branch
2657
+ * - CLOSED (not merged): Enable safety checks - PR was rejected/abandoned,
2658
+ * local commits may not exist anywhere else
2659
+ *
2660
+ * @param parsed - Parsed input identifying the PR
2661
+ * @param options - Finish options
2662
+ * @param worktree - The worktree to clean up
2663
+ * @param prState - The PR state ('closed' or 'merged')
2664
+ * @param finishResult - Result object to populate
2368
2665
  */
2369
- async performPRCleanup(parsed, options, worktree) {
2666
+ async performPRCleanup(parsed, options, worktree, prState, finishResult) {
2370
2667
  const cleanupInput = {
2371
2668
  type: parsed.type,
2372
2669
  originalInput: parsed.originalInput,
2373
2670
  ...parsed.number !== void 0 && { number: parsed.number },
2374
2671
  ...parsed.branchName !== void 0 && { branchName: parsed.branchName }
2375
2672
  };
2673
+ const isMerged = prState === "merged";
2376
2674
  const cleanupOptions = {
2377
2675
  dryRun: options.dryRun ?? false,
2378
2676
  deleteBranch: true,
2379
2677
  // Delete branch for closed/merged PRs
2380
2678
  keepDatabase: false,
2381
- force: options.force ?? false
2679
+ force: options.force ?? false,
2680
+ // For merged PRs: skip merge check (work is in main)
2681
+ // For closed PRs: enable merge check (may have unpushed local commits)
2682
+ checkMergeSafety: !isMerged,
2683
+ // Skip remote branch check for MERGED PRs because:
2684
+ // 1. The PR is merged - the work is safely in main
2685
+ // 2. GitHub may have auto-deleted the branch after merge
2686
+ // 3. The user may have manually deleted the remote branch post-merge
2687
+ //
2688
+ // For CLOSED PRs, we rely on checkMergeSafety to verify no unpushed commits
2689
+ // rather than checkRemoteBranch, since the remote branch may still exist
2690
+ // but local may have additional commits
2691
+ checkRemoteBranch: false
2382
2692
  };
2383
2693
  try {
2384
2694
  await this.ensureResourceCleanup();
2385
2695
  if (!this.resourceCleanup) {
2386
2696
  throw new Error("Failed to initialize ResourceCleanup");
2387
2697
  }
2388
- const result = await this.resourceCleanup.cleanupWorktree(cleanupInput, cleanupOptions);
2389
- this.reportCleanupResults(result);
2390
- if (!result.success) {
2391
- logger.warn("Some cleanup operations failed - manual cleanup may be required");
2698
+ const cleanupResult = await this.resourceCleanup.cleanupWorktree(cleanupInput, cleanupOptions);
2699
+ this.reportCleanupResults(cleanupResult);
2700
+ finishResult.cleanupResult = cleanupResult;
2701
+ if (!cleanupResult.success) {
2702
+ getLogger().warn("Some cleanup operations failed - manual cleanup may be required");
2392
2703
  this.showManualCleanupInstructions(worktree);
2393
2704
  } else {
2394
2705
  if (this.isRunningFromWithinWorktree(worktree.path)) {
@@ -2397,29 +2708,86 @@ var FinishCommand = class {
2397
2708
  }
2398
2709
  } catch (error) {
2399
2710
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
2400
- logger.warn(`Cleanup failed: ${errorMessage}`);
2711
+ getLogger().warn(`Cleanup failed: ${errorMessage}`);
2401
2712
  this.showManualCleanupInstructions(worktree);
2402
2713
  throw error;
2403
2714
  }
2404
2715
  }
2716
+ /**
2717
+ * Generate and post session summary if configured
2718
+ *
2719
+ * Non-blocking: Catches all errors and logs warnings instead of throwing
2720
+ * This ensures the finish workflow continues even if summary generation fails
2721
+ *
2722
+ * In dry-run mode: generates summary and shows preview, but doesn't post
2723
+ */
2724
+ async generateSessionSummaryIfConfigured(parsed, worktree, options) {
2725
+ if (parsed.type === "branch") {
2726
+ return;
2727
+ }
2728
+ this.sessionSummaryService ??= new SessionSummaryService(
2729
+ void 0,
2730
+ // Use default PromptTemplateManager
2731
+ void 0,
2732
+ // Use default MetadataManager
2733
+ this.settingsManager
2734
+ );
2735
+ if (options.dryRun) {
2736
+ try {
2737
+ const result = await this.sessionSummaryService.generateSummary(
2738
+ worktree.path,
2739
+ worktree.branch,
2740
+ parsed.type,
2741
+ parsed.number
2742
+ );
2743
+ const preview = result.summary.slice(0, 100).replace(/\n/g, " ");
2744
+ getLogger().info(`[DRY RUN] Would post session summary: "${preview}..."`);
2745
+ } catch (error) {
2746
+ const errorMessage = error instanceof Error ? error.message : String(error);
2747
+ getLogger().warn(`[DRY RUN] Session summary generation failed: ${errorMessage}`);
2748
+ }
2749
+ return;
2750
+ }
2751
+ await this.sessionSummaryService.generateAndPostSummary({
2752
+ worktreePath: worktree.path,
2753
+ issueNumber: parsed.number ?? 0,
2754
+ branchName: worktree.branch,
2755
+ loomType: parsed.type
2756
+ });
2757
+ }
2405
2758
  /**
2406
2759
  * Run post-merge build verification for CLI projects
2407
2760
  * Runs in main worktree to verify merged code builds successfully
2408
2761
  */
2409
- async runPostMergeBuild(worktreePath, options) {
2762
+ async runPostMergeBuild(worktreePath, options, finishResult) {
2410
2763
  const mainWorktreePath = await findMainWorktreePathWithSettings(worktreePath, this.settingsManager);
2411
2764
  if (options.dryRun) {
2412
- logger.info("[DRY RUN] Would run post-merge build");
2765
+ getLogger().info("[DRY RUN] Would run post-merge build");
2766
+ finishResult.operations.push({
2767
+ type: "build",
2768
+ message: "Would run post-merge build (dry-run)",
2769
+ success: true
2770
+ });
2413
2771
  return;
2414
2772
  }
2415
- logger.info("Running post-merge build...");
2416
- const result = await this.buildRunner.runBuild(mainWorktreePath, {
2773
+ getLogger().info("Running post-merge build...");
2774
+ const buildResult = await this.buildRunner.runBuild(mainWorktreePath, {
2417
2775
  dryRun: options.dryRun ?? false
2418
2776
  });
2419
- if (result.skipped) {
2420
- logger.debug(`Build skipped: ${result.reason}`);
2777
+ if (buildResult.skipped) {
2778
+ getLogger().debug(`Build skipped: ${buildResult.reason}`);
2779
+ finishResult.operations.push({
2780
+ type: "build",
2781
+ message: `Build skipped: ${buildResult.reason}`,
2782
+ success: true
2783
+ });
2421
2784
  } else {
2422
- logger.success("Post-merge build completed successfully");
2785
+ getLogger().success("Post-merge build completed successfully");
2786
+ finishResult.operations.push({
2787
+ type: "build",
2788
+ message: "Post-merge build completed",
2789
+ success: true
2790
+ });
2423
2791
  }
2424
2792
  }
2425
2793
  /**
@@ -2427,7 +2795,7 @@ var FinishCommand = class {
2427
2795
  * Converts ParsedFinishInput to ParsedInput and calls ResourceCleanup
2428
2796
  * Handles failures gracefully without throwing
2429
2797
  */
2430
- async performPostMergeCleanup(parsed, options, worktree) {
2798
+ async performPostMergeCleanup(parsed, options, worktree, finishResult) {
2431
2799
  await this.ensureResourceCleanup();
2432
2800
  if (!this.loomManager) {
2433
2801
  throw new Error("Failed to initialize LoomManager");
@@ -2448,26 +2816,43 @@ var FinishCommand = class {
2448
2816
  force: options.force ?? false
2449
2817
  };
2450
2818
  try {
2451
- logger.info("Starting post-merge cleanup...");
2819
+ getLogger().info("Starting post-merge cleanup...");
2452
2820
  if (!this.resourceCleanup) {
2453
2821
  throw new Error("Failed to initialize ResourceCleanup");
2454
2822
  }
2455
- const result = await this.resourceCleanup.cleanupWorktree(cleanupInput, cleanupOptions);
2456
- this.reportCleanupResults(result);
2457
- if (!result.success) {
2458
- logger.warn("Some cleanup operations failed - manual cleanup may be required");
2823
+ const cleanupResult = await this.resourceCleanup.cleanupWorktree(cleanupInput, cleanupOptions);
2824
+ this.reportCleanupResults(cleanupResult);
2825
+ finishResult.cleanupResult = cleanupResult;
2826
+ if (!cleanupResult.success) {
2827
+ getLogger().warn("Some cleanup operations failed - manual cleanup may be required");
2459
2828
  this.showManualCleanupInstructions(worktree);
2829
+ finishResult.operations.push({
2830
+ type: "cleanup",
2831
+ message: "Post-merge cleanup partially failed",
2832
+ success: false
2833
+ });
2460
2834
  } else {
2461
- logger.success("Post-merge cleanup completed successfully");
2835
+ getLogger().success("Post-merge cleanup completed successfully");
2836
+ finishResult.operations.push({
2837
+ type: "cleanup",
2838
+ message: "Post-merge cleanup completed",
2839
+ success: true
2840
+ });
2462
2841
  }
2463
2842
  if (this.isRunningFromWithinWorktree(worktree.path)) {
2464
2843
  this.showTerminalCloseWarning(worktree);
2465
2844
  }
2466
2845
  } catch (error) {
2467
2846
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
2468
- logger.warn(`Cleanup failed: ${errorMessage}`);
2469
- logger.warn("Merge completed successfully, but manual cleanup is required");
2847
+ getLogger().warn(`Cleanup failed: ${errorMessage}`);
2848
+ getLogger().warn("Merge completed successfully, but manual cleanup is required");
2470
2849
  this.showManualCleanupInstructions(worktree);
2850
+ finishResult.operations.push({
2851
+ type: "cleanup",
2852
+ message: "Post-merge cleanup failed",
2853
+ success: false,
2854
+ error: errorMessage
2855
+ });
2471
2856
  }
2472
2857
  }
2473
2858
  /**
@@ -2477,14 +2862,14 @@ var FinishCommand = class {
2477
2862
  if (result.operations.length === 0) {
2478
2863
  return;
2479
2864
  }
2480
- logger.info("Cleanup operations:");
2865
+ getLogger().info("Cleanup operations:");
2481
2866
  for (const op of result.operations) {
2482
2867
  const status = op.success ? "\u2713" : "\u2717";
2483
2868
  const message = op.error ? `${op.message}: ${op.error}` : op.message;
2484
2869
  if (op.success) {
2485
- logger.info(` ${status} ${message}`);
2870
+ getLogger().info(` ${status} ${message}`);
2486
2871
  } else {
2487
- logger.warn(` ${status} ${message}`);
2872
+ getLogger().warn(` ${status} ${message}`);
2488
2873
  }
2489
2874
  }
2490
2875
  }
@@ -2492,10 +2877,10 @@ var FinishCommand = class {
2492
2877
  * Show manual cleanup instructions when cleanup fails
2493
2878
  */
2494
2879
  showManualCleanupInstructions(worktree) {
2495
- logger.info("\nManual cleanup commands:");
2496
- logger.info(` 1. Remove worktree: git worktree remove ${worktree.path}`);
2497
- logger.info(` 2. Delete branch: git branch -d ${worktree.branch}`);
2498
- logger.info(` 3. Check dev servers: lsof -i :PORT (and kill if needed)`);
2880
+ getLogger().info("\nManual cleanup commands:");
2881
+ getLogger().info(` 1. Remove worktree: git worktree remove ${worktree.path}`);
2882
+ getLogger().info(` 2. Delete branch: git branch -d ${worktree.branch}`);
2883
+ getLogger().info(` 3. Check dev servers: lsof -i :PORT (and kill if needed)`);
2499
2884
  }
2500
2885
  /**
2501
2886
  * Check if current working directory is within the target worktree
@@ -2509,17 +2894,17 @@ var FinishCommand = class {
2509
2894
  * Display warning to close terminal/IDE when running from within finished loom
2510
2895
  */
2511
2896
  showTerminalCloseWarning(worktree) {
2512
- logger.info("");
2513
- logger.info("You are currently in the directory of the loom that was just finished.");
2514
- logger.info("Please close this terminal and any IDE/terminal windows using this directory.");
2515
- logger.info(`Directory: ${worktree.path}`);
2897
+ getLogger().info("");
2898
+ getLogger().info("You are currently in the directory of the loom that was just finished.");
2899
+ getLogger().info("Please close this terminal and any IDE/terminal windows using this directory.");
2900
+ getLogger().info(`Directory: ${worktree.path}`);
2516
2901
  }
2517
2902
  };
2518
2903
 
2519
2904
  // src/utils/package-info.ts
2520
2905
  import { readFileSync } from "fs";
2521
2906
  import { fileURLToPath } from "url";
2522
- import { dirname, join } from "path";
2907
+ import { dirname, join as join2 } from "path";
2523
2908
  function getPackageInfo(scriptPath) {
2524
2909
  try {
2525
2910
  let basePath;
@@ -2530,7 +2915,7 @@ function getPackageInfo(scriptPath) {
2530
2915
  basePath = __filename2;
2531
2916
  }
2532
2917
  const __dirname = dirname(basePath);
2533
- const packageJsonPath = join(__dirname, "..", "package.json");
2918
+ const packageJsonPath = join2(__dirname, "..", "package.json");
2534
2919
  const packageJsonContent = readFileSync(packageJsonPath, "utf8");
2535
2920
  const packageJson2 = JSON.parse(packageJsonContent);
2536
2921
  return packageJson2;
@@ -2606,7 +2991,8 @@ function formatLoomForJson(worktree, mainWorktreePath, metadata) {
2606
2991
  description: (metadata == null ? void 0 : metadata.description) ?? null,
2607
2992
  created_at: (metadata == null ? void 0 : metadata.created_at) ?? null,
2608
2993
  issueTracker: (metadata == null ? void 0 : metadata.issueTracker) ?? null,
2609
- colorHex: (metadata == null ? void 0 : metadata.colorHex) ?? null
2994
+ colorHex: (metadata == null ? void 0 : metadata.colorHex) ?? null,
2995
+ projectPath: (metadata == null ? void 0 : metadata.projectPath) ?? null
2610
2996
  };
2611
2997
  }
2612
2998
  function formatLoomsForJson(worktrees, mainWorktreePath, metadata) {
@@ -2631,14 +3017,14 @@ program.name("iloom").description(packageJson.description).version(packageJson.v
2631
3017
  process.exit(0);
2632
3018
  }
2633
3019
  try {
2634
- const { checkAndNotifyUpdate } = await import("./update-notifier-QEX3CJHA.js");
2635
- const { detectInstallationMethod } = await import("./installation-detector-VARGFFRZ.js");
3020
+ const { checkAndNotifyUpdate } = await import("./update-notifier-H55ZK7NU.js");
3021
+ const { detectInstallationMethod } = await import("./installation-detector-6R6YOFVZ.js");
2636
3022
  const installMethod = detectInstallationMethod(__filename);
2637
3023
  await checkAndNotifyUpdate(packageJson.version, packageJson.name, installMethod);
2638
3024
  } catch {
2639
3025
  }
2640
3026
  try {
2641
- const { SettingsMigrationManager } = await import("./SettingsMigrationManager-TJ7UWZG5.js");
3027
+ const { SettingsMigrationManager } = await import("./SettingsMigrationManager-EH3J2TCN.js");
2642
3028
  const migrationManager = new SettingsMigrationManager();
2643
3029
  await migrationManager.migrateSettingsIfNeeded();
2644
3030
  } catch (error) {
@@ -2728,21 +3114,21 @@ async function autoLaunchInitForMultipleRemotes() {
2728
3114
  logger.info("iloom will now launch an interactive configuration session with Claude");
2729
3115
  logger.info("to help you select which remote to use for GitHub operations.");
2730
3116
  logger.info("");
2731
- const { waitForKeypress: waitForKeypress2 } = await import("./prompt-QALMYTVC.js");
3117
+ const { waitForKeypress: waitForKeypress2 } = await import("./prompt-A7GGRHSY.js");
2732
3118
  await waitForKeypress2("Press any key to start configuration...");
2733
3119
  logger.info("");
2734
3120
  try {
2735
- const { InitCommand: InitCommand2 } = await import("./init-4FHTAM3F.js");
3121
+ const { InitCommand: InitCommand2 } = await import("./init-LBA6NUK2.js");
2736
3122
  const initCommand = new InitCommand2();
2737
3123
  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.";
2738
3124
  await initCommand.execute(customInitialMessage);
2739
3125
  logger.info("");
2740
3126
  logger.info("Configuration complete! Continuing with your original command...");
2741
3127
  logger.info("");
2742
- const { SettingsManager: SettingsManager2 } = await import("./SettingsManager-I2LRCW2A.js");
3128
+ const { SettingsManager: SettingsManager2 } = await import("./SettingsManager-FJFU6JJD.js");
2743
3129
  const settingsManager = new SettingsManager2();
2744
3130
  const settings = await settingsManager.loadSettings();
2745
- const { hasMultipleRemotes: hasMultipleRemotes2 } = await import("./remote-VUNCQZ6J.js");
3131
+ const { hasMultipleRemotes: hasMultipleRemotes2 } = await import("./remote-73TZ2ADI.js");
2746
3132
  const multipleRemotes = await hasMultipleRemotes2();
2747
3133
  if (multipleRemotes && !((_b = (_a = settings.issueManagement) == null ? void 0 : _a.github) == null ? void 0 : _b.remote)) {
2748
3134
  logger.error("Configuration incomplete: GitHub remote is still not configured.");
@@ -2758,50 +3144,82 @@ async function autoLaunchInitForMultipleRemotes() {
2758
3144
  }
2759
3145
  var shellCompletion = new ShellCompletion();
2760
3146
  shellCompletion.init();
2761
- program.command("start").alias("new").alias("create").alias("up").description("Create isolated workspace for an issue/PR").argument("[identifier]", "Issue number, PR number, or branch name (optional - will prompt if not provided)").option("--claude", "Enable Claude integration (default: true)").option("--no-claude", "Disable Claude integration").option("--code", "Enable VSCode (default: true)").option("--no-code", "Disable VSCode").option("--dev-server", "Enable dev server in terminal (default: true)").option("--no-dev-server", "Disable dev server").option("--terminal", "Enable terminal without dev server (default: false)").option("--no-terminal", "Disable terminal").option("--child-loom", "Force create as child loom (skip prompt)").option("--no-child-loom", "Force create as independent loom (skip prompt)").option("--body <text>", "Body text for issue (skips AI enhancement)").addOption(
3147
+ program.command("start").alias("new").alias("create").alias("up").description("Create isolated workspace for an issue/PR").argument("[identifier]", "Issue number, PR number, or branch name (optional - will prompt if not provided)").option("--claude", "Enable Claude integration (default: true)").option("--no-claude", "Disable Claude integration").option("--code", "Enable VSCode (default: true)").option("--no-code", "Disable VSCode").option("--dev-server", "Enable dev server in terminal (default: true)").option("--no-dev-server", "Disable dev server").option("--terminal", "Enable terminal without dev server (default: false)").option("--no-terminal", "Disable terminal").option("--child-loom", "Force create as child loom (skip prompt)").option("--no-child-loom", "Force create as independent loom (skip prompt)").option("--body <text>", "Body text for issue (skips AI enhancement)").option("--json", "Output result as JSON").addOption(
2762
3148
  new Option("--one-shot <mode>", "One-shot automation mode").choices(["default", "noReview", "bypassPermissions"]).default("default")
2763
3149
  ).action(async (identifier, options) => {
2764
- try {
2765
- let finalIdentifier = identifier;
2766
- if (!finalIdentifier) {
2767
- const { promptInput } = await import("./prompt-QALMYTVC.js");
2768
- finalIdentifier = await promptInput("Enter issue number, PR number (pr/123), or branch name");
2769
- if (!(finalIdentifier == null ? void 0 : finalIdentifier.trim())) {
2770
- logger.error("Identifier is required");
2771
- process.exit(1);
3150
+ const executeAction = async () => {
3151
+ try {
3152
+ let finalIdentifier = identifier;
3153
+ if (!finalIdentifier) {
3154
+ if (options.json) {
3155
+ logger.error("JSON mode requires identifier argument");
3156
+ process.exit(1);
3157
+ }
3158
+ const { promptInput } = await import("./prompt-A7GGRHSY.js");
3159
+ finalIdentifier = await promptInput("Enter issue number, PR number (pr/123), or branch name");
3160
+ if (!(finalIdentifier == null ? void 0 : finalIdentifier.trim())) {
3161
+ logger.error("Identifier is required");
3162
+ process.exit(1);
3163
+ }
3164
+ }
3165
+ const settingsManager = new SettingsManager();
3166
+ const settings = await settingsManager.loadSettings();
3167
+ const issueTracker = IssueTrackerFactory.create(settings);
3168
+ const command = new StartCommand(issueTracker, void 0, void 0, settingsManager);
3169
+ const result = await command.execute({ identifier: finalIdentifier, options });
3170
+ if (options.json && result) {
3171
+ console.log(JSON.stringify(result, null, 2));
2772
3172
  }
3173
+ process.exit(0);
3174
+ } catch (error) {
3175
+ logger.error(`Failed to start workspace: ${error instanceof Error ? error.message : "Unknown error"}`);
3176
+ process.exit(1);
2773
3177
  }
2774
- const settingsManager = new SettingsManager();
2775
- const settings = await settingsManager.loadSettings();
2776
- const issueTracker = IssueTrackerFactory.create(settings);
2777
- const command = new StartCommand(issueTracker, void 0, void 0, settingsManager);
2778
- await command.execute({ identifier: finalIdentifier, options });
2779
- } catch (error) {
2780
- logger.error(`Failed to start workspace: ${error instanceof Error ? error.message : "Unknown error"}`);
2781
- process.exit(1);
3178
+ };
3179
+ if (options.json) {
3180
+ const jsonLogger = createStderrLogger();
3181
+ await withLogger(jsonLogger, executeAction);
3182
+ } else {
3183
+ await executeAction();
2782
3184
  }
2783
3185
  });
2784
- program.command("add-issue").alias("a").description("Create and enhance GitHub issue without starting workspace").argument("<description>", "Natural language description of the issue (>50 chars, >2 spaces)").option("--body <text>", "Body text for issue (skips AI enhancement)").action(async (description, options) => {
2785
- try {
2786
- const settingsManager = new SettingsManager();
2787
- const settings = await settingsManager.loadSettings();
2788
- const issueTracker = IssueTrackerFactory.create(settings);
2789
- const enhancementService = new IssueEnhancementService(issueTracker, new AgentManager(), settingsManager);
2790
- const command = new AddIssueCommand(enhancementService, settingsManager);
2791
- const issueNumber = await command.execute({
2792
- description,
2793
- options: options.body ? { body: options.body } : {}
2794
- });
2795
- logger.success(`Issue #${issueNumber} created successfully`);
2796
- process.exit(0);
2797
- } catch (error) {
2798
- logger.error(`Failed to create issue: ${error instanceof Error ? error.message : "Unknown error"}`);
2799
- process.exit(1);
3186
+ program.command("add-issue").alias("a").description("Create and enhance GitHub issue without starting workspace").argument("<description>", "Natural language description of the issue (>50 chars, >2 spaces)").option("--body <text>", "Body text for issue (skips AI enhancement)").option("--json", "Output result as JSON").action(async (description, options) => {
3187
+ const executeAction = async () => {
3188
+ try {
3189
+ const settingsManager = new SettingsManager();
3190
+ const settings = await settingsManager.loadSettings();
3191
+ const issueTracker = IssueTrackerFactory.create(settings);
3192
+ const enhancementService = new IssueEnhancementService(issueTracker, new AgentManager(), settingsManager);
3193
+ const command = new AddIssueCommand(enhancementService, settingsManager);
3194
+ const result = await command.execute({
3195
+ description,
3196
+ options: {
3197
+ ...options.body && { body: options.body },
3198
+ ...options.json && { json: options.json }
3199
+ }
3200
+ });
3201
+ if (options.json && result) {
3202
+ console.log(JSON.stringify(result, null, 2));
3203
+ } else if (result) {
3204
+ const issueNumber = typeof result === "object" ? result.id : result;
3205
+ logger.success(`Issue #${issueNumber} created successfully`);
3206
+ }
3207
+ process.exit(0);
3208
+ } catch (error) {
3209
+ logger.error(`Failed to create issue: ${error instanceof Error ? error.message : "Unknown error"}`);
3210
+ process.exit(1);
3211
+ }
3212
+ };
3213
+ if (options.json) {
3214
+ const jsonLogger = createStderrLogger();
3215
+ await withLogger(jsonLogger, executeAction);
3216
+ } else {
3217
+ await executeAction();
2800
3218
  }
2801
3219
  });
2802
3220
  program.command("feedback").alias("f").description("Submit feedback/bug report to iloom-cli repository").argument("<description>", "Natural language description of feedback (>50 chars, >2 spaces)").option("--body <text>", "Body text for feedback (added after diagnostics)").action(async (description, options) => {
2803
3221
  try {
2804
- const { FeedbackCommand } = await import("./feedback-XTUCKJNT.js");
3222
+ const { FeedbackCommand } = await import("./feedback-7PVBQNLJ.js");
2805
3223
  const command = new FeedbackCommand();
2806
3224
  const feedbackOptions = {};
2807
3225
  if (options.body !== void 0) {
@@ -2818,41 +3236,70 @@ program.command("feedback").alias("f").description("Submit feedback/bug report t
2818
3236
  process.exit(1);
2819
3237
  }
2820
3238
  });
2821
- program.command("enhance").description("Apply enhancement agent to existing GitHub issue").argument("<issue-number>", "GitHub issue identifier to enhance", parseIssueIdentifier).option("--no-browser", "Skip browser opening prompt").option("--author <username>", "GitHub username to tag in questions (for CI usage)").action(async (issueNumber, options) => {
2822
- try {
2823
- const settingsManager = new SettingsManager();
2824
- const settings = await settingsManager.loadSettings();
2825
- const issueTracker = IssueTrackerFactory.create(settings);
2826
- const command = new EnhanceCommand(issueTracker);
2827
- await command.execute({
2828
- issueNumber,
2829
- options: {
2830
- noBrowser: options.browser === false,
2831
- ...options.author && { author: options.author }
3239
+ program.command("enhance").description("Apply enhancement agent to existing GitHub issue").argument("<issue-number>", "GitHub issue identifier to enhance", parseIssueIdentifier).option("--no-browser", "Skip browser opening prompt").option("--author <username>", "GitHub username to tag in questions (for CI usage)").option("--json", "Output result as JSON").action(async (issueNumber, options) => {
3240
+ const executeAction = async () => {
3241
+ try {
3242
+ const settingsManager = new SettingsManager();
3243
+ const settings = await settingsManager.loadSettings();
3244
+ const issueTracker = IssueTrackerFactory.create(settings);
3245
+ const command = new EnhanceCommand(issueTracker);
3246
+ const result = await command.execute({
3247
+ issueNumber,
3248
+ options: {
3249
+ noBrowser: options.browser === false,
3250
+ ...options.author && { author: options.author },
3251
+ ...options.json && { json: options.json }
3252
+ }
3253
+ });
3254
+ if (options.json && result) {
3255
+ console.log(JSON.stringify(result, null, 2));
3256
+ } else {
3257
+ logger.success(`Enhancement process completed for issue #${issueNumber}`);
2832
3258
  }
2833
- });
2834
- logger.success(`Enhancement process completed for issue #${issueNumber}`);
2835
- process.exit(0);
2836
- } catch (error) {
2837
- logger.error(`Failed to enhance issue: ${error instanceof Error ? error.message : "Unknown error"}`);
2838
- process.exit(1);
3259
+ process.exit(0);
3260
+ } catch (error) {
3261
+ logger.error(`Failed to enhance issue: ${error instanceof Error ? error.message : "Unknown error"}`);
3262
+ process.exit(1);
3263
+ }
3264
+ };
3265
+ if (options.json) {
3266
+ const jsonLogger = createStderrLogger();
3267
+ await withLogger(jsonLogger, executeAction);
3268
+ } else {
3269
+ await executeAction();
2839
3270
  }
2840
3271
  });
2841
- 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)").action(async (identifier, options) => {
2842
- try {
2843
- const settingsManager = new SettingsManager();
2844
- const settings = await settingsManager.loadSettings();
2845
- const issueTracker = IssueTrackerFactory.create(settings);
2846
- const command = new FinishCommand(issueTracker);
2847
- await command.execute({ identifier, options });
2848
- } catch (error) {
2849
- logger.error(`Failed to finish workspace: ${error instanceof Error ? error.message : "Unknown error"}`);
2850
- process.exit(1);
3272
+ 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) => {
3273
+ const executeAction = async () => {
3274
+ try {
3275
+ const settingsManager = new SettingsManager();
3276
+ const settings = await settingsManager.loadSettings();
3277
+ const issueTracker = IssueTrackerFactory.create(settings);
3278
+ const command = new FinishCommand(issueTracker);
3279
+ const result = await command.execute({ identifier, options });
3280
+ if (options.json && result) {
3281
+ console.log(JSON.stringify(result, null, 2));
3282
+ }
3283
+ process.exit(0);
3284
+ } catch (error) {
3285
+ if (options.json) {
3286
+ console.log(JSON.stringify({ success: false, error: error instanceof Error ? error.message : "Unknown error" }, null, 2));
3287
+ } else {
3288
+ logger.error(`Failed to finish workspace: ${error instanceof Error ? error.message : "Unknown error"}`);
3289
+ }
3290
+ process.exit(1);
3291
+ }
3292
+ };
3293
+ if (options.json) {
3294
+ const jsonLogger = createStderrLogger();
3295
+ await withLogger(jsonLogger, executeAction);
3296
+ } else {
3297
+ await executeAction();
2851
3298
  }
2852
3299
  });
2853
3300
  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) => {
2854
3301
  try {
2855
- const { RebaseCommand } = await import("./rebase-VJ2VKR6R.js");
3302
+ const { RebaseCommand } = await import("./rebase-4T5FQHNH.js");
2856
3303
  const command = new RebaseCommand();
2857
3304
  await command.execute(options);
2858
3305
  } catch (error) {
@@ -2864,7 +3311,7 @@ program.command("spin").alias("ignite").description("Launch Claude with auto-det
2864
3311
  new Option("--one-shot <mode>", "One-shot automation mode").choices(["default", "noReview", "bypassPermissions"]).default("default")
2865
3312
  ).action(async (options) => {
2866
3313
  try {
2867
- const { IgniteCommand } = await import("./ignite-T74RYXCA.js");
3314
+ const { IgniteCommand } = await import("./ignite-3B264M7K.js");
2868
3315
  const command = new IgniteCommand();
2869
3316
  await command.execute(options.oneShot ?? "default");
2870
3317
  } catch (error) {
@@ -2875,7 +3322,7 @@ program.command("spin").alias("ignite").description("Launch Claude with auto-det
2875
3322
  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) => {
2876
3323
  try {
2877
3324
  const args = (command == null ? void 0 : command.args) ? command.args.slice(identifier ? 1 : 0) : [];
2878
- const { OpenCommand } = await import("./open-UMXANW5S.js");
3325
+ const { OpenCommand } = await import("./open-OGCV32Z4.js");
2879
3326
  const cmd = new OpenCommand();
2880
3327
  const input = identifier ? { identifier, args } : { args };
2881
3328
  await cmd.execute(input);
@@ -2887,7 +3334,7 @@ program.command("open").description("Open workspace in browser or run CLI tool")
2887
3334
  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) => {
2888
3335
  try {
2889
3336
  const args = (command == null ? void 0 : command.args) ? command.args.slice(identifier ? 1 : 0) : [];
2890
- const { RunCommand } = await import("./run-MJYY4PUT.js");
3337
+ const { RunCommand } = await import("./run-HNOP6WE2.js");
2891
3338
  const cmd = new RunCommand();
2892
3339
  const input = identifier ? { identifier, args } : { args };
2893
3340
  await cmd.execute(input);
@@ -2896,22 +3343,58 @@ program.command("run").description("Run CLI tool or open workspace in browser").
2896
3343
  process.exit(1);
2897
3344
  }
2898
3345
  });
2899
- 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").action(async (identifier, options) => {
3346
+ 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) => {
2900
3347
  try {
2901
- const { CleanupCommand } = await import("./cleanup-H4VXU3C3.js");
2902
- const command = new CleanupCommand();
2903
- const input = {
2904
- options: options ?? {}
2905
- };
2906
- if (identifier) {
2907
- input.identifier = identifier;
2908
- }
2909
- await command.execute(input);
3348
+ const { DevServerCommand } = await import("./dev-server-JCJGQ3PV.js");
3349
+ const cmd = new DevServerCommand();
3350
+ await cmd.execute({ identifier, json: options == null ? void 0 : options.json });
3351
+ } catch (error) {
3352
+ logger.error(`Failed to start dev server: ${error instanceof Error ? error.message : "Unknown error"}`);
3353
+ process.exit(1);
3354
+ }
3355
+ });
3356
+ 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) => {
3357
+ try {
3358
+ const { ShellCommand } = await import("./shell-DE3HKJSM.js");
3359
+ const cmd = new ShellCommand();
3360
+ await cmd.execute({ identifier });
2910
3361
  } catch (error) {
2911
- logger.error(`Failed to cleanup worktrees: ${error instanceof Error ? error.message : "Unknown error"}`);
3362
+ logger.error(`Failed to open shell: ${error instanceof Error ? error.message : "Unknown error"}`);
2912
3363
  process.exit(1);
2913
3364
  }
2914
3365
  });
3366
+ 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) => {
3367
+ const executeAction = async () => {
3368
+ try {
3369
+ const { CleanupCommand } = await import("./cleanup-KDLVTT7M.js");
3370
+ const command = new CleanupCommand();
3371
+ const input = {
3372
+ options: options ?? {}
3373
+ };
3374
+ if (identifier) {
3375
+ input.identifier = identifier;
3376
+ }
3377
+ const result = await command.execute(input);
3378
+ if ((options == null ? void 0 : options.json) && result) {
3379
+ console.log(JSON.stringify(result, null, 2));
3380
+ }
3381
+ process.exit(0);
3382
+ } catch (error) {
3383
+ if (options == null ? void 0 : options.json) {
3384
+ console.log(JSON.stringify({ success: false, error: error instanceof Error ? error.message : "Unknown error" }, null, 2));
3385
+ } else {
3386
+ logger.error(error instanceof Error ? error.message : "Unknown error");
3387
+ }
3388
+ process.exit(1);
3389
+ }
3390
+ };
3391
+ if (options == null ? void 0 : options.json) {
3392
+ const jsonLogger = createStderrLogger();
3393
+ await withLogger(jsonLogger, executeAction);
3394
+ } else {
3395
+ await executeAction();
3396
+ }
3397
+ });
2915
3398
  program.command("list").description("Show active workspaces").option("--json", "Output as JSON").action(async (options) => {
2916
3399
  try {
2917
3400
  const manager = new GitWorktreeManager();
@@ -2955,9 +3438,20 @@ program.command("list").description("Show active workspaces").option("--json", "
2955
3438
  process.exit(1);
2956
3439
  }
2957
3440
  });
3441
+ program.command("projects").description("List configured iloom projects").option("--json", "Output as JSON (default behavior)").action(async (options) => {
3442
+ try {
3443
+ const { ProjectsCommand } = await import("./projects-P55273AB.js");
3444
+ const command = new ProjectsCommand();
3445
+ const result = await command.execute(options);
3446
+ console.log(JSON.stringify(result, null, 2));
3447
+ } catch (error) {
3448
+ logger.error(`Failed to list projects: ${error instanceof Error ? error.message : "Unknown error"}`);
3449
+ process.exit(1);
3450
+ }
3451
+ });
2958
3452
  program.command("init").alias("config").description("Initialize iloom configuration and setup shell autocomplete").argument("[prompt]", 'Custom initial message to send to Claude (defaults to "Help me configure iloom settings.")').action(async (prompt) => {
2959
3453
  try {
2960
- const { InitCommand: InitCommand2 } = await import("./init-4FHTAM3F.js");
3454
+ const { InitCommand: InitCommand2 } = await import("./init-LBA6NUK2.js");
2961
3455
  const command = new InitCommand2();
2962
3456
  const trimmedPrompt = prompt == null ? void 0 : prompt.trim();
2963
3457
  const customPrompt = trimmedPrompt && trimmedPrompt.length > 0 ? trimmedPrompt : void 0;
@@ -2969,7 +3463,7 @@ program.command("init").alias("config").description("Initialize iloom configurat
2969
3463
  });
2970
3464
  program.command("contribute").description("Set up local development environment for contributing to iloom").action(async () => {
2971
3465
  try {
2972
- const { ContributeCommand } = await import("./contribute-Y7IQV5QY.js");
3466
+ const { ContributeCommand } = await import("./contribute-HY372S6F.js");
2973
3467
  const command = new ContributeCommand();
2974
3468
  await command.execute();
2975
3469
  } catch (error) {
@@ -2979,7 +3473,7 @@ program.command("contribute").description("Set up local development environment
2979
3473
  });
2980
3474
  program.command("update").description("Update iloom-cli to the latest version").option("--dry-run", "Show what would be done without actually updating").action(async (options) => {
2981
3475
  try {
2982
- const { UpdateCommand } = await import("./update-4TDDUR5K.js");
3476
+ const { UpdateCommand } = await import("./update-LETF5ASC.js");
2983
3477
  const command = new UpdateCommand();
2984
3478
  await command.execute(options);
2985
3479
  } catch (error) {
@@ -2989,8 +3483,8 @@ program.command("update").description("Update iloom-cli to the latest version").
2989
3483
  });
2990
3484
  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) => {
2991
3485
  try {
2992
- const { GitHubService: GitHubService2 } = await import("./GitHubService-FZHHBOFG.js");
2993
- const { DefaultBranchNamingService: DefaultBranchNamingService2 } = await import("./BranchNamingService-A77VI6AI.js");
3486
+ const { GitHubService: GitHubService2 } = await import("./GitHubService-RPM27GWD.js");
3487
+ const { DefaultBranchNamingService: DefaultBranchNamingService2 } = await import("./BranchNamingService-GCCWB3LK.js");
2994
3488
  logger.info("Testing GitHub Integration\n");
2995
3489
  const service = new GitHubService2();
2996
3490
  const branchNaming = new DefaultBranchNamingService2({ useClaude: options.claude !== false });
@@ -3048,10 +3542,10 @@ program.command("test-github").description("Test GitHub integration (Issue #3)")
3048
3542
  });
3049
3543
  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) => {
3050
3544
  try {
3051
- const { detectClaudeCli: detectClaudeCli2, getClaudeVersion, generateBranchName, launchClaude: launchClaude2 } = await import("./claude-W52VKI6L.js");
3052
- const { PromptTemplateManager } = await import("./PromptTemplateManager-6HH3PVXV.js");
3053
- const { ClaudeService } = await import("./ClaudeService-DLYLJUPA.js");
3054
- const { ClaudeContextManager: ClaudeContextManager2 } = await import("./ClaudeContextManager-BN7RE5ZQ.js");
3545
+ const { detectClaudeCli: detectClaudeCli2, getClaudeVersion, generateBranchName, launchClaude: launchClaude2 } = await import("./claude-ACVXNB6N.js");
3546
+ const { PromptTemplateManager } = await import("./PromptTemplateManager-2TDZAUC6.js");
3547
+ const { ClaudeService } = await import("./ClaudeService-W3SA7HVG.js");
3548
+ const { ClaudeContextManager: ClaudeContextManager2 } = await import("./ClaudeContextManager-DK77227F.js");
3055
3549
  logger.info("Testing Claude Integration\n");
3056
3550
  if (options.detect) {
3057
3551
  logger.info("Detecting Claude CLI...");
@@ -3186,7 +3680,7 @@ program.command("test-claude").description("Test Claude integration (Issue #10)"
3186
3680
  });
3187
3681
  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) => {
3188
3682
  try {
3189
- const { TestWebserverCommand } = await import("./test-webserver-DAHONWCS.js");
3683
+ const { TestWebserverCommand } = await import("./test-webserver-VPNLAFZ3.js");
3190
3684
  const command = new TestWebserverCommand();
3191
3685
  await command.execute({ issueNumber, options });
3192
3686
  } catch (error) {
@@ -3199,7 +3693,7 @@ program.command("test-webserver").description("Test if a web server is running o
3199
3693
  });
3200
3694
  program.command("test-git").description("Test Git integration - findMainWorktreePath() function (reads .iloom/settings.json)").action(async () => {
3201
3695
  try {
3202
- const { TestGitCommand } = await import("./test-git-IT5EWQ5C.js");
3696
+ const { TestGitCommand } = await import("./test-git-YMAE57UP.js");
3203
3697
  const command = new TestGitCommand();
3204
3698
  await command.execute();
3205
3699
  } catch (error) {
@@ -3212,7 +3706,7 @@ program.command("test-git").description("Test Git integration - findMainWorktree
3212
3706
  });
3213
3707
  program.command("test-tabs").description("Test iTerm2 dual tab functionality - opens two tabs with test commands").action(async () => {
3214
3708
  try {
3215
- const { TestTabsCommand } = await import("./test-tabs-PRMRSHKI.js");
3709
+ const { TestTabsCommand } = await import("./test-tabs-3SCJWRKT.js");
3216
3710
  const command = new TestTabsCommand();
3217
3711
  await command.execute();
3218
3712
  } catch (error) {
@@ -3225,7 +3719,7 @@ program.command("test-tabs").description("Test iTerm2 dual tab functionality - o
3225
3719
  });
3226
3720
  program.command("test-prefix").description("Test worktree prefix configuration - preview worktree paths (reads .iloom/settings.json)").action(async () => {
3227
3721
  try {
3228
- const { TestPrefixCommand } = await import("./test-prefix-NPWDPUUH.js");
3722
+ const { TestPrefixCommand } = await import("./test-prefix-YCKL6CMT.js");
3229
3723
  const command = new TestPrefixCommand();
3230
3724
  await command.execute();
3231
3725
  } catch (error) {
@@ -3236,11 +3730,40 @@ program.command("test-prefix").description("Test worktree prefix configuration -
3236
3730
  process.exit(1);
3237
3731
  }
3238
3732
  });
3733
+ 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) => {
3734
+ const executeAction = async () => {
3735
+ try {
3736
+ const { SummaryCommand } = await import("./summary-GDT7DTRI.js");
3737
+ const command = new SummaryCommand();
3738
+ const result = await command.execute({ identifier, options });
3739
+ if (options.json && result) {
3740
+ console.log(JSON.stringify(result, null, 2));
3741
+ }
3742
+ process.exit(0);
3743
+ } catch (error) {
3744
+ if (options.json) {
3745
+ console.log(JSON.stringify({ error: error instanceof Error ? error.message : "Unknown error" }, null, 2));
3746
+ } else {
3747
+ logger.error(`Summary failed: ${error instanceof Error ? error.message : "Unknown error"}`);
3748
+ if (error instanceof Error && error.stack) {
3749
+ logger.debug(error.stack);
3750
+ }
3751
+ }
3752
+ process.exit(1);
3753
+ }
3754
+ };
3755
+ if (options.json) {
3756
+ const jsonLogger = createStderrLogger();
3757
+ await withLogger(jsonLogger, executeAction);
3758
+ } else {
3759
+ await executeAction();
3760
+ }
3761
+ });
3239
3762
  program.command("test-neon").description("Test Neon integration and debug configuration").action(async () => {
3240
3763
  var _a;
3241
3764
  try {
3242
- const { SettingsManager: SettingsManager2 } = await import("./SettingsManager-I2LRCW2A.js");
3243
- const { createNeonProviderFromSettings: createNeonProviderFromSettings2 } = await import("./neon-helpers-77PBPGJ5.js");
3765
+ const { SettingsManager: SettingsManager2 } = await import("./SettingsManager-FJFU6JJD.js");
3766
+ const { createNeonProviderFromSettings: createNeonProviderFromSettings2 } = await import("./neon-helpers-L5CXQ5CT.js");
3244
3767
  logger.info("Testing Neon Integration\n");
3245
3768
  logger.info("1. Settings Configuration:");
3246
3769
  const settingsManager = new SettingsManager2();