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