@iloom/cli 0.3.4 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -3
- package/dist/{BranchNamingService-A77VI6AI.js → BranchNamingService-GCCWB3LK.js} +4 -3
- package/dist/ClaudeContextManager-DK77227F.js +16 -0
- package/dist/ClaudeService-W3SA7HVG.js +15 -0
- package/dist/GitHubService-RPM27GWD.js +12 -0
- package/dist/{LoomLauncher-ZV3ZZIBA.js → LoomLauncher-S3YGJRJQ.js} +43 -27
- package/dist/LoomLauncher-S3YGJRJQ.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-5Q3NDNNV.js → chunk-2W2FBL5G.js} +153 -6
- package/dist/chunk-2W2FBL5G.js.map +1 -0
- package/dist/{chunk-OXAM2WVC.js → chunk-55TB3FSG.js} +21 -1
- package/dist/chunk-55TB3FSG.js.map +1 -0
- 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-2MAIX45J.js → chunk-BIIQHEXJ.js} +104 -43
- package/dist/chunk-BIIQHEXJ.js.map +1 -0
- package/dist/{chunk-UAN4A3YU.js → chunk-G6CIIJLT.js} +11 -11
- package/dist/{chunk-DLHA5VQ3.js → chunk-HD5SUKI2.js} +36 -179
- package/dist/chunk-HD5SUKI2.js.map +1 -0
- package/dist/{chunk-2IJEMXOB.js → chunk-IARWMDAX.js} +427 -428
- package/dist/chunk-IARWMDAX.js.map +1 -0
- package/dist/chunk-IJ7IGJT3.js +192 -0
- package/dist/chunk-IJ7IGJT3.js.map +1 -0
- package/dist/{chunk-2CXREBLZ.js → chunk-JC5HXN75.js} +8 -6
- package/dist/chunk-JC5HXN75.js.map +1 -0
- 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-4XIDC3NF.js → chunk-MD6HA5IK.js} +2 -2
- 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-PGPI5LR4.js → chunk-POI7KLBH.js} +7 -21
- package/dist/chunk-POI7KLBH.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-SUOXY5WJ.js → chunk-QIUJPPJQ.js} +5 -5
- package/dist/chunk-QIUJPPJQ.js.map +1 -0
- package/dist/{chunk-ZM3CFL5L.js → chunk-QRBOPFAA.js} +3 -3
- package/dist/{chunk-OYF4VIFI.js → chunk-RUC7OULH.js} +147 -22
- package/dist/chunk-RUC7OULH.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-TMZAVPGF.js +667 -0
- package/dist/chunk-TMZAVPGF.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-AKUJXDNW.js → chunk-UPUAQYAW.js} +3 -3
- 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-RW54ZMBM.js → chunk-VAYGNQTE.js} +2 -2
- package/dist/{chunk-ZT3YZB4K.js → chunk-VBFDVGAE.js} +12 -12
- package/dist/chunk-VBFDVGAE.js.map +1 -0
- package/dist/{chunk-IFB4Z76W.js → chunk-VTXCGKV5.js} +13 -12
- package/dist/chunk-VTXCGKV5.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/{chunk-UJL4HI2R.js → chunk-Z5NXYJIG.js} +20 -2
- package/dist/chunk-Z5NXYJIG.js.map +1 -0
- package/dist/{claude-W52VKI6L.js → claude-ACVXNB6N.js} +8 -5
- package/dist/{cleanup-H4VXU3C3.js → cleanup-KDLVTT7M.js} +133 -122
- package/dist/cleanup-KDLVTT7M.js.map +1 -0
- package/dist/cli.js +953 -430
- package/dist/cli.js.map +1 -1
- package/dist/{color-F7RU6B6Z.js → color-ZPIIUADB.js} +3 -3
- package/dist/{contribute-Y7IQV5QY.js → contribute-HY372S6F.js} +8 -6
- package/dist/{contribute-Y7IQV5QY.js.map → contribute-HY372S6F.js.map} +1 -1
- package/dist/dev-server-JCJGQ3PV.js +298 -0
- package/dist/dev-server-JCJGQ3PV.js.map +1 -0
- package/dist/{feedback-XTUCKJNT.js → feedback-7PVBQNLJ.js} +13 -12
- package/dist/{feedback-XTUCKJNT.js.map → feedback-7PVBQNLJ.js.map} +1 -1
- package/dist/{git-IYA53VIC.js → git-4BVOOOOV.js} +16 -4
- package/dist/hooks/iloom-hook.js +258 -0
- package/dist/{ignite-T74RYXCA.js → ignite-3B264M7K.js} +245 -39
- package/dist/ignite-3B264M7K.js.map +1 -0
- package/dist/index.d.ts +461 -124
- package/dist/index.js +743 -210
- package/dist/index.js.map +1 -1
- package/dist/init-LBA6NUK2.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-OGCV32Z4.js} +15 -13
- package/dist/{open-UMXANW5S.js.map → open-OGCV32Z4.js.map} +1 -1
- package/dist/projects-P55273AB.js +73 -0
- package/dist/projects-P55273AB.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-4T5FQHNH.js} +11 -9
- package/dist/{rebase-VJ2VKR6R.js.map → rebase-4T5FQHNH.js.map} +1 -1
- package/dist/{remote-VUNCQZ6J.js → remote-73TZ2ADI.js} +3 -3
- package/dist/{run-MJYY4PUT.js → run-HNOP6WE2.js} +15 -13
- package/dist/{run-MJYY4PUT.js.map → run-HNOP6WE2.js.map} +1 -1
- package/dist/schema/settings.schema.json +49 -0
- package/dist/shell-DE3HKJSM.js +240 -0
- package/dist/shell-DE3HKJSM.js.map +1 -0
- package/dist/summary-GDT7DTRI.js +244 -0
- package/dist/summary-GDT7DTRI.js.map +1 -0
- package/dist/{test-git-IT5EWQ5C.js → test-git-YMAE57UP.js} +6 -4
- package/dist/{test-git-IT5EWQ5C.js.map → test-git-YMAE57UP.js.map} +1 -1
- package/dist/{test-prefix-NPWDPUUH.js → test-prefix-YCKL6CMT.js} +6 -4
- package/dist/{test-prefix-NPWDPUUH.js.map → test-prefix-YCKL6CMT.js.map} +1 -1
- package/dist/{test-tabs-PRMRSHKI.js → test-tabs-3SCJWRKT.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-UJL4HI2R.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-GCCWB3LK.js.map} +0 -0
- /package/dist/{ClaudeContextManager-BN7RE5ZQ.js.map → ClaudeContextManager-DK77227F.js.map} +0 -0
- /package/dist/{ClaudeService-DLYLJUPA.js.map → ClaudeService-W3SA7HVG.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-UAN4A3YU.js.map → chunk-G6CIIJLT.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-AKUJXDNW.js.map → chunk-UPUAQYAW.js.map} +0 -0
- /package/dist/{chunk-OSCLCMDG.js.map → chunk-UYWAESOT.js.map} +0 -0
- /package/dist/{chunk-RW54ZMBM.js.map → chunk-VAYGNQTE.js.map} +0 -0
- /package/dist/{chunk-CFFQ2Z7A.js.map → chunk-WUQQNE63.js.map} +0 -0
- /package/dist/{claude-W52VKI6L.js.map → claude-ACVXNB6N.js.map} +0 -0
- /package/dist/{color-F7RU6B6Z.js.map → color-ZPIIUADB.js.map} +0 -0
- /package/dist/{git-IYA53VIC.js.map → git-4BVOOOOV.js.map} +0 -0
- /package/dist/{init-4FHTAM3F.js.map → init-LBA6NUK2.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-3SCJWRKT.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/index.js
CHANGED
|
@@ -9,12 +9,6 @@ var __export = (target, all) => {
|
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
// src/utils/logger.ts
|
|
12
|
-
var logger_exports = {};
|
|
13
|
-
__export(logger_exports, {
|
|
14
|
-
createLogger: () => createLogger,
|
|
15
|
-
default: () => logger_default,
|
|
16
|
-
logger: () => logger
|
|
17
|
-
});
|
|
18
12
|
import chalk, { Chalk } from "chalk";
|
|
19
13
|
function formatMessage(message, ...args) {
|
|
20
14
|
const formattedArgs = args.map(
|
|
@@ -52,7 +46,8 @@ function createLogger(options = {}) {
|
|
|
52
46
|
},
|
|
53
47
|
isDebugEnabled: () => {
|
|
54
48
|
return false;
|
|
55
|
-
}
|
|
49
|
+
},
|
|
50
|
+
stdout: process.stdout
|
|
56
51
|
};
|
|
57
52
|
}
|
|
58
53
|
return {
|
|
@@ -93,7 +88,57 @@ function createLogger(options = {}) {
|
|
|
93
88
|
},
|
|
94
89
|
isDebugEnabled: () => {
|
|
95
90
|
return globalDebugEnabled;
|
|
96
|
-
}
|
|
91
|
+
},
|
|
92
|
+
stdout: process.stdout
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function createStderrLogger(options = {}) {
|
|
96
|
+
const { prefix = "", timestamp = false, forceColor, debug = globalDebugEnabled } = options;
|
|
97
|
+
let localDebugEnabled = debug;
|
|
98
|
+
const customChalk = forceColor !== void 0 ? new Chalk({ level: forceColor ? 3 : 0 }) : stderrChalk;
|
|
99
|
+
const prefixStr = prefix ? `[${prefix}] ` : "";
|
|
100
|
+
const getTimestamp = () => timestamp ? `[${(/* @__PURE__ */ new Date()).toISOString()}] ` : "";
|
|
101
|
+
return {
|
|
102
|
+
info: (message, ...args) => {
|
|
103
|
+
const formatted = formatMessage(message, ...args);
|
|
104
|
+
const fullMessage = `${getTimestamp()}${prefixStr}${formatted}`;
|
|
105
|
+
const output = formatWithEmoji(fullMessage, "\u{1F5C2}\uFE0F ", customChalk.blue);
|
|
106
|
+
console.error(output);
|
|
107
|
+
},
|
|
108
|
+
success: (message, ...args) => {
|
|
109
|
+
const formatted = formatMessage(message, ...args);
|
|
110
|
+
const fullMessage = `${getTimestamp()}${prefixStr}${formatted}`;
|
|
111
|
+
const output = formatWithEmoji(fullMessage, "\u2705", customChalk.green);
|
|
112
|
+
console.error(output);
|
|
113
|
+
},
|
|
114
|
+
warn: (message, ...args) => {
|
|
115
|
+
const formatted = formatMessage(message, ...args);
|
|
116
|
+
const fullMessage = `${getTimestamp()}${prefixStr}${formatted}`;
|
|
117
|
+
const output = formatWithEmoji(fullMessage, "\u26A0\uFE0F ", customChalk.yellow);
|
|
118
|
+
console.error(output);
|
|
119
|
+
},
|
|
120
|
+
error: (message, ...args) => {
|
|
121
|
+
const formatted = formatMessage(message, ...args);
|
|
122
|
+
const fullMessage = `${getTimestamp()}${prefixStr}${formatted}`;
|
|
123
|
+
const output = formatWithEmoji(fullMessage, "\u274C", customChalk.red);
|
|
124
|
+
console.error(output);
|
|
125
|
+
},
|
|
126
|
+
debug: (message, ...args) => {
|
|
127
|
+
if (localDebugEnabled) {
|
|
128
|
+
const formatted = formatMessage(message, ...args);
|
|
129
|
+
const fullMessage = `${getTimestamp()}${prefixStr}${formatted}`;
|
|
130
|
+
const output = formatWithEmoji(fullMessage, "\u{1F50D}", customChalk.gray);
|
|
131
|
+
console.error(output);
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
setDebug: (enabled) => {
|
|
135
|
+
localDebugEnabled = enabled;
|
|
136
|
+
},
|
|
137
|
+
isDebugEnabled: () => {
|
|
138
|
+
return globalDebugEnabled;
|
|
139
|
+
},
|
|
140
|
+
stdout: process.stderr
|
|
141
|
+
// Use stderr for progress output in JSON mode
|
|
97
142
|
};
|
|
98
143
|
}
|
|
99
144
|
var stdoutChalk, stderrChalk, globalDebugEnabled, logger, logger_default;
|
|
@@ -136,7 +181,8 @@ var init_logger = __esm({
|
|
|
136
181
|
},
|
|
137
182
|
isDebugEnabled: () => {
|
|
138
183
|
return globalDebugEnabled;
|
|
139
|
-
}
|
|
184
|
+
},
|
|
185
|
+
stdout: process.stdout
|
|
140
186
|
};
|
|
141
187
|
logger_default = logger;
|
|
142
188
|
}
|
|
@@ -153,6 +199,8 @@ __export(SettingsManager_exports, {
|
|
|
153
199
|
IloomSettingsSchemaNoDefaults: () => IloomSettingsSchemaNoDefaults,
|
|
154
200
|
NeonSettingsSchema: () => NeonSettingsSchema,
|
|
155
201
|
SettingsManager: () => SettingsManager,
|
|
202
|
+
SpinAgentSettingsSchema: () => SpinAgentSettingsSchema,
|
|
203
|
+
SummarySettingsSchema: () => SummarySettingsSchema,
|
|
156
204
|
WorkflowPermissionSchema: () => WorkflowPermissionSchema,
|
|
157
205
|
WorkflowPermissionSchemaNoDefaults: () => WorkflowPermissionSchemaNoDefaults,
|
|
158
206
|
WorkflowsSettingsSchema: () => WorkflowsSettingsSchema,
|
|
@@ -163,7 +211,7 @@ import path from "path";
|
|
|
163
211
|
import os from "os";
|
|
164
212
|
import { z } from "zod";
|
|
165
213
|
import deepmerge from "deepmerge";
|
|
166
|
-
var AgentSettingsSchema, WorkflowPermissionSchema, WorkflowPermissionSchemaNoDefaults, WorkflowsSettingsSchema, WorkflowsSettingsSchemaNoDefaults, CapabilitiesSettingsSchema, CapabilitiesSettingsSchemaNoDefaults, NeonSettingsSchema, DatabaseProvidersSettingsSchema, IloomSettingsSchema, IloomSettingsSchemaNoDefaults, SettingsManager;
|
|
214
|
+
var AgentSettingsSchema, SpinAgentSettingsSchema, SummarySettingsSchema, WorkflowPermissionSchema, WorkflowPermissionSchemaNoDefaults, WorkflowsSettingsSchema, WorkflowsSettingsSchemaNoDefaults, CapabilitiesSettingsSchema, CapabilitiesSettingsSchemaNoDefaults, NeonSettingsSchema, DatabaseProvidersSettingsSchema, IloomSettingsSchema, IloomSettingsSchemaNoDefaults, SettingsManager;
|
|
167
215
|
var init_SettingsManager = __esm({
|
|
168
216
|
"src/lib/SettingsManager.ts"() {
|
|
169
217
|
"use strict";
|
|
@@ -172,13 +220,20 @@ var init_SettingsManager = __esm({
|
|
|
172
220
|
model: z.enum(["sonnet", "opus", "haiku"]).optional().describe("Claude model shorthand: sonnet, opus, or haiku")
|
|
173
221
|
// Future: could add other per-agent overrides
|
|
174
222
|
});
|
|
223
|
+
SpinAgentSettingsSchema = z.object({
|
|
224
|
+
model: z.enum(["sonnet", "opus", "haiku"]).default("opus").describe("Claude model shorthand for spin orchestrator")
|
|
225
|
+
});
|
|
226
|
+
SummarySettingsSchema = z.object({
|
|
227
|
+
model: z.enum(["sonnet", "opus", "haiku"]).default("sonnet").describe("Claude model shorthand for session summary generation")
|
|
228
|
+
});
|
|
175
229
|
WorkflowPermissionSchema = z.object({
|
|
176
230
|
permissionMode: z.enum(["plan", "acceptEdits", "bypassPermissions", "default"]).optional().describe("Permission mode for Claude CLI in this workflow type"),
|
|
177
231
|
noVerify: z.boolean().optional().describe("Skip pre-commit hooks (--no-verify) when committing during finish workflow"),
|
|
178
232
|
startIde: z.boolean().default(true).describe("Launch IDE (code) when starting this workflow type"),
|
|
179
233
|
startDevServer: z.boolean().default(true).describe("Launch development server when starting this workflow type"),
|
|
180
234
|
startAiAgent: z.boolean().default(true).describe("Launch Claude Code agent when starting this workflow type"),
|
|
181
|
-
startTerminal: z.boolean().default(false).describe("Launch terminal window without dev server when starting this workflow type")
|
|
235
|
+
startTerminal: z.boolean().default(false).describe("Launch terminal window without dev server when starting this workflow type"),
|
|
236
|
+
generateSummary: z.boolean().default(true).describe("Generate and post Claude session summary when finishing this workflow type")
|
|
182
237
|
});
|
|
183
238
|
WorkflowPermissionSchemaNoDefaults = z.object({
|
|
184
239
|
permissionMode: z.enum(["plan", "acceptEdits", "bypassPermissions", "default"]).optional().describe("Permission mode for Claude CLI in this workflow type"),
|
|
@@ -186,7 +241,8 @@ var init_SettingsManager = __esm({
|
|
|
186
241
|
startIde: z.boolean().optional().describe("Launch IDE (code) when starting this workflow type"),
|
|
187
242
|
startDevServer: z.boolean().optional().describe("Launch development server when starting this workflow type"),
|
|
188
243
|
startAiAgent: z.boolean().optional().describe("Launch Claude Code agent when starting this workflow type"),
|
|
189
|
-
startTerminal: z.boolean().optional().describe("Launch terminal window without dev server when starting this workflow type")
|
|
244
|
+
startTerminal: z.boolean().optional().describe("Launch terminal window without dev server when starting this workflow type"),
|
|
245
|
+
generateSummary: z.boolean().optional().describe("Generate and post Claude session summary when finishing this workflow type")
|
|
190
246
|
});
|
|
191
247
|
WorkflowsSettingsSchema = z.object({
|
|
192
248
|
issue: WorkflowPermissionSchema.optional(),
|
|
@@ -254,6 +310,12 @@ var init_SettingsManager = __esm({
|
|
|
254
310
|
agents: z.record(z.string(), AgentSettingsSchema).optional().nullable().describe(
|
|
255
311
|
"Per-agent configuration overrides. Available agents: iloom-issue-analyzer (analyzes issues), iloom-issue-planner (creates implementation plans), iloom-issue-analyze-and-plan (combined analysis and planning), iloom-issue-complexity-evaluator (evaluates complexity), iloom-issue-enhancer (enhances issue descriptions), iloom-issue-implementer (implements code changes), iloom-issue-reviewer (reviews code changes against requirements)"
|
|
256
312
|
),
|
|
313
|
+
spin: SpinAgentSettingsSchema.optional().describe(
|
|
314
|
+
"Spin orchestrator configuration. Model defaults to opus when not configured."
|
|
315
|
+
),
|
|
316
|
+
summary: SummarySettingsSchema.optional().describe(
|
|
317
|
+
"Session summary generation configuration. Model defaults to sonnet when not configured."
|
|
318
|
+
),
|
|
257
319
|
capabilities: CapabilitiesSettingsSchema.describe("Project capability configurations"),
|
|
258
320
|
databaseProviders: DatabaseProvidersSettingsSchema.describe("Database provider configurations"),
|
|
259
321
|
issueManagement: z.object({
|
|
@@ -316,6 +378,12 @@ var init_SettingsManager = __esm({
|
|
|
316
378
|
agents: z.record(z.string(), AgentSettingsSchema).optional().nullable().describe(
|
|
317
379
|
"Per-agent configuration overrides. Available agents: iloom-issue-analyzer (analyzes issues), iloom-issue-planner (creates implementation plans), iloom-issue-analyze-and-plan (combined analysis and planning), iloom-issue-complexity-evaluator (evaluates complexity), iloom-issue-enhancer (enhances issue descriptions), iloom-issue-implementer (implements code changes), iloom-issue-reviewer (reviews code changes against requirements)"
|
|
318
380
|
),
|
|
381
|
+
spin: z.object({
|
|
382
|
+
model: z.enum(["sonnet", "opus", "haiku"]).optional()
|
|
383
|
+
}).optional().describe("Spin orchestrator configuration"),
|
|
384
|
+
summary: z.object({
|
|
385
|
+
model: z.enum(["sonnet", "opus", "haiku"]).optional()
|
|
386
|
+
}).optional().describe("Session summary generation configuration"),
|
|
319
387
|
capabilities: CapabilitiesSettingsSchemaNoDefaults.describe("Project capability configurations"),
|
|
320
388
|
databaseProviders: DatabaseProvidersSettingsSchema.describe("Database provider configurations"),
|
|
321
389
|
issueManagement: z.object({
|
|
@@ -445,8 +513,8 @@ Note: CLI overrides were applied. Check your --set arguments.`);
|
|
|
445
513
|
*/
|
|
446
514
|
formatAllZodErrors(error, settingsPath) {
|
|
447
515
|
const errorMessages = error.issues.map((issue) => {
|
|
448
|
-
const
|
|
449
|
-
return ` - ${
|
|
516
|
+
const path7 = issue.path.length > 0 ? issue.path.join(".") : "root";
|
|
517
|
+
return ` - ${path7}: ${issue.message}`;
|
|
450
518
|
});
|
|
451
519
|
return new Error(
|
|
452
520
|
`Settings validation failed at ${settingsPath}:
|
|
@@ -548,6 +616,28 @@ ${errorMessages.join("\n")}`
|
|
|
548
616
|
}
|
|
549
617
|
return protectedBranches;
|
|
550
618
|
}
|
|
619
|
+
/**
|
|
620
|
+
* Get the spin orchestrator model with default applied
|
|
621
|
+
* Default is defined in SpinAgentSettingsSchema
|
|
622
|
+
*
|
|
623
|
+
* @param settings - Pre-loaded settings object
|
|
624
|
+
* @returns Model shorthand ('opus', 'sonnet', or 'haiku')
|
|
625
|
+
*/
|
|
626
|
+
getSpinModel(settings) {
|
|
627
|
+
var _a;
|
|
628
|
+
return ((_a = settings == null ? void 0 : settings.spin) == null ? void 0 : _a.model) ?? SpinAgentSettingsSchema.parse({}).model;
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Get the session summary model with default applied
|
|
632
|
+
* Default is defined in SummarySettingsSchema
|
|
633
|
+
*
|
|
634
|
+
* @param settings - Pre-loaded settings object
|
|
635
|
+
* @returns Model shorthand ('opus', 'sonnet', or 'haiku')
|
|
636
|
+
*/
|
|
637
|
+
getSummaryModel(settings) {
|
|
638
|
+
var _a;
|
|
639
|
+
return ((_a = settings == null ? void 0 : settings.summary) == null ? void 0 : _a.model) ?? SummarySettingsSchema.parse({}).model;
|
|
640
|
+
}
|
|
551
641
|
};
|
|
552
642
|
}
|
|
553
643
|
});
|
|
@@ -728,14 +818,209 @@ var WorkspaceManager = class {
|
|
|
728
818
|
};
|
|
729
819
|
|
|
730
820
|
// src/lib/GitWorktreeManager.ts
|
|
821
|
+
import path4 from "path";
|
|
822
|
+
import fs2 from "fs-extra";
|
|
823
|
+
|
|
824
|
+
// src/utils/git.ts
|
|
825
|
+
init_SettingsManager();
|
|
731
826
|
import path3 from "path";
|
|
827
|
+
import { execa } from "execa";
|
|
828
|
+
|
|
829
|
+
// src/lib/MetadataManager.ts
|
|
830
|
+
import path2 from "path";
|
|
831
|
+
import os2 from "os";
|
|
732
832
|
import fs from "fs-extra";
|
|
733
833
|
|
|
834
|
+
// src/utils/logger-context.ts
|
|
835
|
+
init_logger();
|
|
836
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
837
|
+
var loggerStorage = new AsyncLocalStorage();
|
|
838
|
+
function getLogger() {
|
|
839
|
+
return loggerStorage.getStore() ?? logger;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
// src/lib/MetadataManager.ts
|
|
843
|
+
var MetadataManager = class {
|
|
844
|
+
constructor() {
|
|
845
|
+
this.loomsDir = path2.join(os2.homedir(), ".config", "iloom-ai", "looms");
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Convert worktree path to filename slug per spec section 2.2
|
|
849
|
+
*
|
|
850
|
+
* Algorithm:
|
|
851
|
+
* 1. Trim trailing slashes
|
|
852
|
+
* 2. Replace all path separators (/ or \) with __ (double underscore)
|
|
853
|
+
* 3. Replace any other non-alphanumeric characters (except _ and -) with -
|
|
854
|
+
* 4. Append .json
|
|
855
|
+
*
|
|
856
|
+
* Example:
|
|
857
|
+
* - Worktree: /Users/jane/dev/repo
|
|
858
|
+
* - Filename: _Users__jane__dev__repo.json
|
|
859
|
+
*/
|
|
860
|
+
slugifyPath(worktreePath) {
|
|
861
|
+
let slug = worktreePath.replace(/[/\\]+$/, "");
|
|
862
|
+
slug = slug.replace(/[/\\]/g, "___");
|
|
863
|
+
slug = slug.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
864
|
+
return `${slug}.json`;
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* Get the full path to the metadata file for a worktree
|
|
868
|
+
*/
|
|
869
|
+
getFilePath(worktreePath) {
|
|
870
|
+
const filename = this.slugifyPath(worktreePath);
|
|
871
|
+
return path2.join(this.loomsDir, filename);
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* Write metadata for a worktree (spec section 3.1)
|
|
875
|
+
*
|
|
876
|
+
* @param worktreePath - Absolute path to the worktree (used for file naming)
|
|
877
|
+
* @param input - Metadata to write (description plus additional fields)
|
|
878
|
+
*/
|
|
879
|
+
async writeMetadata(worktreePath, input) {
|
|
880
|
+
try {
|
|
881
|
+
await fs.ensureDir(this.loomsDir, { mode: 493 });
|
|
882
|
+
const content = {
|
|
883
|
+
description: input.description,
|
|
884
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
885
|
+
version: 1,
|
|
886
|
+
branchName: input.branchName,
|
|
887
|
+
worktreePath: input.worktreePath,
|
|
888
|
+
issueType: input.issueType,
|
|
889
|
+
issue_numbers: input.issue_numbers,
|
|
890
|
+
pr_numbers: input.pr_numbers,
|
|
891
|
+
issueTracker: input.issueTracker,
|
|
892
|
+
colorHex: input.colorHex,
|
|
893
|
+
sessionId: input.sessionId,
|
|
894
|
+
projectPath: input.projectPath,
|
|
895
|
+
...input.parentLoom && { parentLoom: input.parentLoom }
|
|
896
|
+
};
|
|
897
|
+
const filePath = this.getFilePath(worktreePath);
|
|
898
|
+
await fs.writeFile(filePath, JSON.stringify(content, null, 2), { mode: 420 });
|
|
899
|
+
getLogger().debug(`Metadata written for worktree: ${worktreePath}`);
|
|
900
|
+
} catch (error) {
|
|
901
|
+
getLogger().warn(
|
|
902
|
+
`Failed to write metadata for worktree: ${error instanceof Error ? error.message : String(error)}`
|
|
903
|
+
);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
/**
|
|
907
|
+
* Read metadata for a worktree (spec section 3.2)
|
|
908
|
+
*
|
|
909
|
+
* @param worktreePath - Absolute path to the worktree
|
|
910
|
+
* @returns The metadata object with all fields, or null if not found/invalid
|
|
911
|
+
*/
|
|
912
|
+
async readMetadata(worktreePath) {
|
|
913
|
+
try {
|
|
914
|
+
const filePath = this.getFilePath(worktreePath);
|
|
915
|
+
if (!await fs.pathExists(filePath)) {
|
|
916
|
+
return null;
|
|
917
|
+
}
|
|
918
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
919
|
+
const data = JSON.parse(content);
|
|
920
|
+
if (!data.description) {
|
|
921
|
+
return null;
|
|
922
|
+
}
|
|
923
|
+
return {
|
|
924
|
+
description: data.description,
|
|
925
|
+
created_at: data.created_at ?? null,
|
|
926
|
+
branchName: data.branchName ?? null,
|
|
927
|
+
worktreePath: data.worktreePath ?? null,
|
|
928
|
+
issueType: data.issueType ?? null,
|
|
929
|
+
issue_numbers: data.issue_numbers ?? [],
|
|
930
|
+
pr_numbers: data.pr_numbers ?? [],
|
|
931
|
+
issueTracker: data.issueTracker ?? null,
|
|
932
|
+
colorHex: data.colorHex ?? null,
|
|
933
|
+
sessionId: data.sessionId ?? null,
|
|
934
|
+
projectPath: data.projectPath ?? null,
|
|
935
|
+
parentLoom: data.parentLoom ?? null
|
|
936
|
+
};
|
|
937
|
+
} catch (error) {
|
|
938
|
+
getLogger().debug(
|
|
939
|
+
`Could not read metadata for worktree ${worktreePath}: ${error instanceof Error ? error.message : String(error)}`
|
|
940
|
+
);
|
|
941
|
+
return null;
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* List all stored loom metadata files
|
|
946
|
+
*
|
|
947
|
+
* Returns an array of LoomMetadata objects for all valid metadata files
|
|
948
|
+
* in the looms directory. Invalid or unreadable files are skipped.
|
|
949
|
+
*
|
|
950
|
+
* @returns Array of LoomMetadata objects from all stored files
|
|
951
|
+
*/
|
|
952
|
+
async listAllMetadata() {
|
|
953
|
+
const results = [];
|
|
954
|
+
try {
|
|
955
|
+
if (!await fs.pathExists(this.loomsDir)) {
|
|
956
|
+
return results;
|
|
957
|
+
}
|
|
958
|
+
const files = await fs.readdir(this.loomsDir);
|
|
959
|
+
for (const file of files) {
|
|
960
|
+
if (!file.endsWith(".json")) {
|
|
961
|
+
continue;
|
|
962
|
+
}
|
|
963
|
+
try {
|
|
964
|
+
const filePath = path2.join(this.loomsDir, file);
|
|
965
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
966
|
+
const data = JSON.parse(content);
|
|
967
|
+
if (!data.description) {
|
|
968
|
+
continue;
|
|
969
|
+
}
|
|
970
|
+
results.push({
|
|
971
|
+
description: data.description,
|
|
972
|
+
created_at: data.created_at ?? null,
|
|
973
|
+
branchName: data.branchName ?? null,
|
|
974
|
+
worktreePath: data.worktreePath ?? null,
|
|
975
|
+
issueType: data.issueType ?? null,
|
|
976
|
+
issue_numbers: data.issue_numbers ?? [],
|
|
977
|
+
pr_numbers: data.pr_numbers ?? [],
|
|
978
|
+
issueTracker: data.issueTracker ?? null,
|
|
979
|
+
colorHex: data.colorHex ?? null,
|
|
980
|
+
sessionId: data.sessionId ?? null,
|
|
981
|
+
projectPath: data.projectPath ?? null,
|
|
982
|
+
parentLoom: data.parentLoom ?? null
|
|
983
|
+
});
|
|
984
|
+
} catch (error) {
|
|
985
|
+
getLogger().debug(
|
|
986
|
+
`Skipping metadata file ${file}: ${error instanceof Error ? error.message : String(error)}`
|
|
987
|
+
);
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
} catch (error) {
|
|
991
|
+
getLogger().debug(
|
|
992
|
+
`Could not list metadata files: ${error instanceof Error ? error.message : String(error)}`
|
|
993
|
+
);
|
|
994
|
+
}
|
|
995
|
+
return results;
|
|
996
|
+
}
|
|
997
|
+
/**
|
|
998
|
+
* Delete metadata for a worktree (spec section 3.3)
|
|
999
|
+
*
|
|
1000
|
+
* Idempotent: silently succeeds if file doesn't exist
|
|
1001
|
+
* Non-fatal: logs warning on permission errors but doesn't throw
|
|
1002
|
+
*
|
|
1003
|
+
* @param worktreePath - Absolute path to the worktree
|
|
1004
|
+
*/
|
|
1005
|
+
async deleteMetadata(worktreePath) {
|
|
1006
|
+
try {
|
|
1007
|
+
const filePath = this.getFilePath(worktreePath);
|
|
1008
|
+
if (!await fs.pathExists(filePath)) {
|
|
1009
|
+
getLogger().debug(`No metadata file to delete for worktree: ${worktreePath}`);
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
await fs.unlink(filePath);
|
|
1013
|
+
getLogger().debug(`Metadata deleted for worktree: ${worktreePath}`);
|
|
1014
|
+
} catch (error) {
|
|
1015
|
+
getLogger().warn(
|
|
1016
|
+
`Failed to delete metadata for worktree: ${error instanceof Error ? error.message : String(error)}`
|
|
1017
|
+
);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
};
|
|
1021
|
+
|
|
734
1022
|
// src/utils/git.ts
|
|
735
|
-
init_SettingsManager();
|
|
736
1023
|
init_logger();
|
|
737
|
-
import path2 from "path";
|
|
738
|
-
import { execa } from "execa";
|
|
739
1024
|
async function executeGitCommand(args, options) {
|
|
740
1025
|
try {
|
|
741
1026
|
const result = await execa("git", args, {
|
|
@@ -743,7 +1028,9 @@ async function executeGitCommand(args, options) {
|
|
|
743
1028
|
timeout: (options == null ? void 0 : options.timeout) ?? 3e4,
|
|
744
1029
|
encoding: "utf8",
|
|
745
1030
|
stdio: (options == null ? void 0 : options.stdio) ?? "pipe",
|
|
746
|
-
verbose: logger.isDebugEnabled()
|
|
1031
|
+
verbose: logger.isDebugEnabled(),
|
|
1032
|
+
// Spread env conditionally - only include if defined
|
|
1033
|
+
...(options == null ? void 0 : options.env) && { env: options.env }
|
|
747
1034
|
});
|
|
748
1035
|
return result.stdout;
|
|
749
1036
|
} catch (error) {
|
|
@@ -879,7 +1166,7 @@ function extractIssueNumber(branchName) {
|
|
|
879
1166
|
}
|
|
880
1167
|
return null;
|
|
881
1168
|
}
|
|
882
|
-
function isWorktreePath(
|
|
1169
|
+
function isWorktreePath(path7) {
|
|
883
1170
|
const worktreePatterns = [
|
|
884
1171
|
/\/worktrees?\//i,
|
|
885
1172
|
// Contains /worktree/ or /worktrees/
|
|
@@ -894,17 +1181,17 @@ function isWorktreePath(path6) {
|
|
|
894
1181
|
/\.worktree$/i
|
|
895
1182
|
// ends with .worktree
|
|
896
1183
|
];
|
|
897
|
-
return worktreePatterns.some((pattern) => pattern.test(
|
|
1184
|
+
return worktreePatterns.some((pattern) => pattern.test(path7));
|
|
898
1185
|
}
|
|
899
1186
|
function generateWorktreePath(branchName, rootDir = process.cwd(), options) {
|
|
900
1187
|
let sanitized = branchName.replace(/\//g, "-");
|
|
901
1188
|
if ((options == null ? void 0 : options.isPR) && (options == null ? void 0 : options.prNumber)) {
|
|
902
1189
|
sanitized = `${sanitized}_pr_${options.prNumber}`;
|
|
903
1190
|
}
|
|
904
|
-
const parentDir =
|
|
1191
|
+
const parentDir = path3.dirname(rootDir);
|
|
905
1192
|
let prefix;
|
|
906
1193
|
if ((options == null ? void 0 : options.prefix) === void 0) {
|
|
907
|
-
const mainFolderName =
|
|
1194
|
+
const mainFolderName = path3.basename(rootDir);
|
|
908
1195
|
prefix = mainFolderName ? `${mainFolderName}-looms/` : "looms/";
|
|
909
1196
|
} else if (options.prefix === "") {
|
|
910
1197
|
prefix = "";
|
|
@@ -924,43 +1211,43 @@ function generateWorktreePath(branchName, rootDir = process.cwd(), options) {
|
|
|
924
1211
|
}
|
|
925
1212
|
}
|
|
926
1213
|
if (prefix === "") {
|
|
927
|
-
return
|
|
1214
|
+
return path3.join(parentDir, sanitized);
|
|
928
1215
|
} else if (prefix.endsWith("/")) {
|
|
929
|
-
return
|
|
1216
|
+
return path3.join(parentDir, prefix, sanitized);
|
|
930
1217
|
} else if (prefix.includes("/")) {
|
|
931
1218
|
const lastSlashIndex = prefix.lastIndexOf("/");
|
|
932
1219
|
const dirPath = prefix.substring(0, lastSlashIndex);
|
|
933
1220
|
const prefixWithSeparator = prefix.substring(lastSlashIndex + 1);
|
|
934
|
-
return
|
|
1221
|
+
return path3.join(parentDir, dirPath, `${prefixWithSeparator}${sanitized}`);
|
|
935
1222
|
} else {
|
|
936
|
-
return
|
|
1223
|
+
return path3.join(parentDir, `${prefix}${sanitized}`);
|
|
937
1224
|
}
|
|
938
1225
|
}
|
|
939
|
-
async function isValidGitRepo(
|
|
1226
|
+
async function isValidGitRepo(path7) {
|
|
940
1227
|
try {
|
|
941
|
-
await executeGitCommand(["rev-parse", "--git-dir"], { cwd:
|
|
1228
|
+
await executeGitCommand(["rev-parse", "--git-dir"], { cwd: path7 });
|
|
942
1229
|
return true;
|
|
943
1230
|
} catch {
|
|
944
1231
|
return false;
|
|
945
1232
|
}
|
|
946
1233
|
}
|
|
947
|
-
async function getCurrentBranch(
|
|
1234
|
+
async function getCurrentBranch(path7 = process.cwd()) {
|
|
948
1235
|
try {
|
|
949
|
-
const result = await executeGitCommand(["branch", "--show-current"], { cwd:
|
|
1236
|
+
const result = await executeGitCommand(["branch", "--show-current"], { cwd: path7 });
|
|
950
1237
|
return result.trim();
|
|
951
1238
|
} catch {
|
|
952
1239
|
return null;
|
|
953
1240
|
}
|
|
954
1241
|
}
|
|
955
|
-
async function branchExists(branchName,
|
|
1242
|
+
async function branchExists(branchName, path7 = process.cwd(), includeRemote = true) {
|
|
956
1243
|
try {
|
|
957
|
-
const localResult = await executeGitCommand(["branch", "--list", branchName], { cwd:
|
|
1244
|
+
const localResult = await executeGitCommand(["branch", "--list", branchName], { cwd: path7 });
|
|
958
1245
|
if (localResult.trim()) {
|
|
959
1246
|
return true;
|
|
960
1247
|
}
|
|
961
1248
|
if (includeRemote) {
|
|
962
1249
|
const remoteResult = await executeGitCommand(["branch", "-r", "--list", `*/${branchName}`], {
|
|
963
|
-
cwd:
|
|
1250
|
+
cwd: path7
|
|
964
1251
|
});
|
|
965
1252
|
if (remoteResult.trim()) {
|
|
966
1253
|
return true;
|
|
@@ -971,31 +1258,31 @@ async function branchExists(branchName, path6 = process.cwd(), includeRemote = t
|
|
|
971
1258
|
return false;
|
|
972
1259
|
}
|
|
973
1260
|
}
|
|
974
|
-
async function getWorktreeRoot(
|
|
1261
|
+
async function getWorktreeRoot(path7 = process.cwd()) {
|
|
975
1262
|
try {
|
|
976
|
-
const result = await executeGitCommand(["rev-parse", "--show-toplevel"], { cwd:
|
|
1263
|
+
const result = await executeGitCommand(["rev-parse", "--show-toplevel"], { cwd: path7 });
|
|
977
1264
|
return result.trim();
|
|
978
1265
|
} catch {
|
|
979
1266
|
return null;
|
|
980
1267
|
}
|
|
981
1268
|
}
|
|
982
|
-
async function getRepoRoot(
|
|
1269
|
+
async function getRepoRoot(path7 = process.cwd()) {
|
|
983
1270
|
try {
|
|
984
1271
|
const gitCommonDir = await executeGitCommand(
|
|
985
1272
|
["rev-parse", "--path-format=absolute", "--git-common-dir"],
|
|
986
|
-
{ cwd:
|
|
1273
|
+
{ cwd: path7 }
|
|
987
1274
|
);
|
|
988
1275
|
const trimmedPath = gitCommonDir.trim();
|
|
989
1276
|
const repoRoot = trimmedPath.replace(/\/\.git\/worktrees\/[^/]+$/, "").replace(/\/\.git$/, "");
|
|
990
1277
|
return repoRoot;
|
|
991
1278
|
} catch (error) {
|
|
992
|
-
logger.warn(`Failed to determine repo root from git-common-dir: ${
|
|
1279
|
+
logger.warn(`Failed to determine repo root from git-common-dir: ${path7}`, error instanceof Error ? error.message : String(error));
|
|
993
1280
|
return null;
|
|
994
1281
|
}
|
|
995
1282
|
}
|
|
996
|
-
async function findMainWorktreePath(
|
|
1283
|
+
async function findMainWorktreePath(path7 = process.cwd(), options) {
|
|
997
1284
|
try {
|
|
998
|
-
const output = await executeGitCommand(["worktree", "list", "--porcelain"], { cwd:
|
|
1285
|
+
const output = await executeGitCommand(["worktree", "list", "--porcelain"], { cwd: path7 });
|
|
999
1286
|
const worktrees = parseWorktreeList(output, options == null ? void 0 : options.mainBranch);
|
|
1000
1287
|
if (worktrees.length === 0) {
|
|
1001
1288
|
throw new Error("No worktrees found in repository");
|
|
@@ -1025,30 +1312,51 @@ async function findMainWorktreePath(path6 = process.cwd(), options) {
|
|
|
1025
1312
|
throw new Error(`Failed to find main worktree: ${error instanceof Error ? error.message : String(error)}`);
|
|
1026
1313
|
}
|
|
1027
1314
|
}
|
|
1028
|
-
async function findMainWorktreePathWithSettings(
|
|
1315
|
+
async function findMainWorktreePathWithSettings(path7, settingsManager) {
|
|
1029
1316
|
settingsManager ??= new SettingsManager();
|
|
1030
|
-
const settings = await settingsManager.loadSettings(
|
|
1317
|
+
const settings = await settingsManager.loadSettings(path7);
|
|
1031
1318
|
const findOptions = settings.mainBranch ? { mainBranch: settings.mainBranch } : void 0;
|
|
1032
|
-
return findMainWorktreePath(
|
|
1319
|
+
return findMainWorktreePath(path7, findOptions);
|
|
1033
1320
|
}
|
|
1034
|
-
async function
|
|
1321
|
+
async function findWorktreeForBranch(branchName, path7 = process.cwd()) {
|
|
1035
1322
|
try {
|
|
1036
|
-
const
|
|
1323
|
+
const output = await executeGitCommand(["worktree", "list", "--porcelain"], { cwd: path7 });
|
|
1324
|
+
const worktrees = parseWorktreeList(output, branchName);
|
|
1325
|
+
if (worktrees.length === 0) {
|
|
1326
|
+
throw new Error("No worktrees found in repository");
|
|
1327
|
+
}
|
|
1328
|
+
const targetWorktree = worktrees.find((wt) => wt.branch === branchName);
|
|
1329
|
+
if (!(targetWorktree == null ? void 0 : targetWorktree.path)) {
|
|
1330
|
+
throw new Error(
|
|
1331
|
+
`No worktree found with branch '${branchName}' checked out. Available worktrees: ${worktrees.map((wt) => `${wt.path} (${wt.branch})`).join(", ")}`
|
|
1332
|
+
);
|
|
1333
|
+
}
|
|
1334
|
+
return targetWorktree.path;
|
|
1335
|
+
} catch (error) {
|
|
1336
|
+
if (error instanceof Error && (error.message.includes("No worktree found with branch") || error.message.includes("No worktrees found"))) {
|
|
1337
|
+
throw error;
|
|
1338
|
+
}
|
|
1339
|
+
throw new Error(`Failed to find worktree for branch '${branchName}': ${error instanceof Error ? error.message : String(error)}`);
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
async function hasUncommittedChanges(path7 = process.cwd()) {
|
|
1343
|
+
try {
|
|
1344
|
+
const result = await executeGitCommand(["status", "--porcelain"], { cwd: path7 });
|
|
1037
1345
|
return result.trim().length > 0;
|
|
1038
1346
|
} catch {
|
|
1039
1347
|
return false;
|
|
1040
1348
|
}
|
|
1041
1349
|
}
|
|
1042
|
-
async function getDefaultBranch(
|
|
1350
|
+
async function getDefaultBranch(path7 = process.cwd()) {
|
|
1043
1351
|
try {
|
|
1044
1352
|
const remoteResult = await executeGitCommand(["symbolic-ref", "refs/remotes/origin/HEAD"], {
|
|
1045
|
-
cwd:
|
|
1353
|
+
cwd: path7
|
|
1046
1354
|
});
|
|
1047
1355
|
const match = remoteResult.match(/refs\/remotes\/origin\/(.+)/);
|
|
1048
1356
|
if (match) return match[1] ?? "main";
|
|
1049
1357
|
const commonDefaults = ["main", "master", "develop"];
|
|
1050
1358
|
for (const branch of commonDefaults) {
|
|
1051
|
-
if (await branchExists(branch,
|
|
1359
|
+
if (await branchExists(branch, path7)) {
|
|
1052
1360
|
return branch;
|
|
1053
1361
|
}
|
|
1054
1362
|
}
|
|
@@ -1057,13 +1365,13 @@ async function getDefaultBranch(path6 = process.cwd()) {
|
|
|
1057
1365
|
return "main";
|
|
1058
1366
|
}
|
|
1059
1367
|
}
|
|
1060
|
-
async function findAllBranchesForIssue(issueNumber,
|
|
1368
|
+
async function findAllBranchesForIssue(issueNumber, path7 = process.cwd(), settingsManager) {
|
|
1061
1369
|
if (!settingsManager) {
|
|
1062
1370
|
const { SettingsManager: SM } = await Promise.resolve().then(() => (init_SettingsManager(), SettingsManager_exports));
|
|
1063
1371
|
settingsManager = new SM();
|
|
1064
1372
|
}
|
|
1065
|
-
const protectedBranches = await settingsManager.getProtectedBranches(
|
|
1066
|
-
const output = await executeGitCommand(["branch", "-a"], { cwd:
|
|
1373
|
+
const protectedBranches = await settingsManager.getProtectedBranches(path7);
|
|
1374
|
+
const output = await executeGitCommand(["branch", "-a"], { cwd: path7 });
|
|
1067
1375
|
const branches = [];
|
|
1068
1376
|
const lines = output.split("\n").filter(Boolean);
|
|
1069
1377
|
for (const line of lines) {
|
|
@@ -1120,18 +1428,18 @@ async function findAllBranchesForIssue(issueNumber, path6 = process.cwd(), setti
|
|
|
1120
1428
|
}
|
|
1121
1429
|
return branches;
|
|
1122
1430
|
}
|
|
1123
|
-
async function isEmptyRepository(
|
|
1431
|
+
async function isEmptyRepository(path7 = process.cwd()) {
|
|
1124
1432
|
try {
|
|
1125
|
-
await executeGitCommand(["rev-parse", "--verify", "HEAD"], { cwd:
|
|
1433
|
+
await executeGitCommand(["rev-parse", "--verify", "HEAD"], { cwd: path7 });
|
|
1126
1434
|
return false;
|
|
1127
1435
|
} catch {
|
|
1128
1436
|
return true;
|
|
1129
1437
|
}
|
|
1130
1438
|
}
|
|
1131
|
-
async function ensureRepositoryHasCommits(
|
|
1132
|
-
const isEmpty = await isEmptyRepository(
|
|
1439
|
+
async function ensureRepositoryHasCommits(path7 = process.cwd()) {
|
|
1440
|
+
const isEmpty = await isEmptyRepository(path7);
|
|
1133
1441
|
if (isEmpty) {
|
|
1134
|
-
await executeGitCommand(["commit", "--no-verify", "--allow-empty", "-m", "Initial commit"], { cwd:
|
|
1442
|
+
await executeGitCommand(["commit", "--no-verify", "--allow-empty", "-m", "Initial commit"], { cwd: path7 });
|
|
1135
1443
|
}
|
|
1136
1444
|
}
|
|
1137
1445
|
async function pushBranchToRemote(branchName, worktreePath, options) {
|
|
@@ -1199,6 +1507,122 @@ async function isFileGitignored(filePath, cwd = process.cwd()) {
|
|
|
1199
1507
|
return false;
|
|
1200
1508
|
}
|
|
1201
1509
|
}
|
|
1510
|
+
async function isBranchMergedIntoMain(branchName, mainBranch = "main", cwd = process.cwd()) {
|
|
1511
|
+
try {
|
|
1512
|
+
await executeGitCommand(["merge-base", "--is-ancestor", branchName, mainBranch], { cwd });
|
|
1513
|
+
return true;
|
|
1514
|
+
} catch {
|
|
1515
|
+
return false;
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
async function isRemoteBranchUpToDate(branchName, cwd) {
|
|
1519
|
+
try {
|
|
1520
|
+
const remoteResult = await executeGitCommand(["ls-remote", "--heads", "origin", branchName], { cwd });
|
|
1521
|
+
if (remoteResult.trim().length === 0) {
|
|
1522
|
+
return false;
|
|
1523
|
+
}
|
|
1524
|
+
const remoteCommit = remoteResult.trim().split(" ")[0];
|
|
1525
|
+
const localCommit = await executeGitCommand(["rev-parse", branchName], { cwd });
|
|
1526
|
+
return localCommit.trim() === remoteCommit;
|
|
1527
|
+
} catch {
|
|
1528
|
+
return false;
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
async function checkRemoteBranchStatus(branchName, cwd) {
|
|
1532
|
+
try {
|
|
1533
|
+
try {
|
|
1534
|
+
await executeGitCommand(["fetch", "origin", branchName], { cwd, timeout: 3e4 });
|
|
1535
|
+
} catch (fetchError) {
|
|
1536
|
+
const fetchErrorMessage = fetchError instanceof Error ? fetchError.message : String(fetchError);
|
|
1537
|
+
if (fetchErrorMessage.includes("Could not resolve host") || fetchErrorMessage.includes("unable to access") || fetchErrorMessage.includes("network") || fetchErrorMessage.includes("Connection refused") || fetchErrorMessage.includes("Connection timed out")) {
|
|
1538
|
+
return {
|
|
1539
|
+
exists: false,
|
|
1540
|
+
remoteAhead: false,
|
|
1541
|
+
localAhead: false,
|
|
1542
|
+
networkError: true,
|
|
1543
|
+
errorMessage: fetchErrorMessage
|
|
1544
|
+
};
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
const remoteResult = await executeGitCommand(["ls-remote", "--heads", "origin", branchName], { cwd });
|
|
1548
|
+
if (remoteResult.trim().length === 0) {
|
|
1549
|
+
return {
|
|
1550
|
+
exists: false,
|
|
1551
|
+
remoteAhead: false,
|
|
1552
|
+
localAhead: false,
|
|
1553
|
+
networkError: false
|
|
1554
|
+
};
|
|
1555
|
+
}
|
|
1556
|
+
const remoteCommit = remoteResult.trim().split(" ")[0];
|
|
1557
|
+
if (!remoteCommit) {
|
|
1558
|
+
return {
|
|
1559
|
+
exists: false,
|
|
1560
|
+
remoteAhead: false,
|
|
1561
|
+
localAhead: false,
|
|
1562
|
+
networkError: false
|
|
1563
|
+
};
|
|
1564
|
+
}
|
|
1565
|
+
const localCommit = await executeGitCommand(["rev-parse", branchName], { cwd });
|
|
1566
|
+
const localCommitTrimmed = localCommit.trim();
|
|
1567
|
+
if (remoteCommit === localCommitTrimmed) {
|
|
1568
|
+
return {
|
|
1569
|
+
exists: true,
|
|
1570
|
+
remoteAhead: false,
|
|
1571
|
+
localAhead: false,
|
|
1572
|
+
networkError: false
|
|
1573
|
+
};
|
|
1574
|
+
}
|
|
1575
|
+
try {
|
|
1576
|
+
await executeGitCommand(["merge-base", "--is-ancestor", localCommitTrimmed, remoteCommit], { cwd });
|
|
1577
|
+
return {
|
|
1578
|
+
exists: true,
|
|
1579
|
+
remoteAhead: true,
|
|
1580
|
+
localAhead: false,
|
|
1581
|
+
networkError: false
|
|
1582
|
+
};
|
|
1583
|
+
} catch {
|
|
1584
|
+
return {
|
|
1585
|
+
exists: true,
|
|
1586
|
+
remoteAhead: false,
|
|
1587
|
+
localAhead: true,
|
|
1588
|
+
networkError: false
|
|
1589
|
+
};
|
|
1590
|
+
}
|
|
1591
|
+
} catch (error) {
|
|
1592
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1593
|
+
if (errorMessage.includes("Could not resolve host") || errorMessage.includes("unable to access") || errorMessage.includes("network") || errorMessage.includes("Connection refused") || errorMessage.includes("Connection timed out")) {
|
|
1594
|
+
return {
|
|
1595
|
+
exists: false,
|
|
1596
|
+
remoteAhead: false,
|
|
1597
|
+
localAhead: false,
|
|
1598
|
+
networkError: true,
|
|
1599
|
+
errorMessage
|
|
1600
|
+
};
|
|
1601
|
+
}
|
|
1602
|
+
return {
|
|
1603
|
+
exists: false,
|
|
1604
|
+
remoteAhead: false,
|
|
1605
|
+
localAhead: false,
|
|
1606
|
+
networkError: false
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
async function getMergeTargetBranch(worktreePath = process.cwd(), options) {
|
|
1611
|
+
var _a;
|
|
1612
|
+
const settingsManager = (options == null ? void 0 : options.settingsManager) ?? new SettingsManager();
|
|
1613
|
+
const metadataManager = (options == null ? void 0 : options.metadataManager) ?? new MetadataManager();
|
|
1614
|
+
logger.debug(`Checking for parent loom metadata at: ${worktreePath}`);
|
|
1615
|
+
const metadata = await metadataManager.readMetadata(worktreePath);
|
|
1616
|
+
if ((_a = metadata == null ? void 0 : metadata.parentLoom) == null ? void 0 : _a.branchName) {
|
|
1617
|
+
logger.debug(`Using parent branch as merge target: ${metadata.parentLoom.branchName}`);
|
|
1618
|
+
return metadata.parentLoom.branchName;
|
|
1619
|
+
}
|
|
1620
|
+
logger.debug("No parent loom metadata found, falling back to settings");
|
|
1621
|
+
const settings = await settingsManager.loadSettings(worktreePath);
|
|
1622
|
+
const mainBranch = settings.mainBranch ?? "main";
|
|
1623
|
+
logger.debug(`Using configured main branch as merge target: ${mainBranch}`);
|
|
1624
|
+
return mainBranch;
|
|
1625
|
+
}
|
|
1202
1626
|
|
|
1203
1627
|
// src/lib/GitWorktreeManager.ts
|
|
1204
1628
|
var GitWorktreeManager = class {
|
|
@@ -1266,12 +1690,12 @@ var GitWorktreeManager = class {
|
|
|
1266
1690
|
if (!options.branch) {
|
|
1267
1691
|
throw new Error("Branch name is required");
|
|
1268
1692
|
}
|
|
1269
|
-
const absolutePath =
|
|
1270
|
-
if (await
|
|
1693
|
+
const absolutePath = path4.resolve(options.path);
|
|
1694
|
+
if (await fs2.pathExists(absolutePath)) {
|
|
1271
1695
|
if (!options.force) {
|
|
1272
1696
|
throw new Error(`Path already exists: ${absolutePath}`);
|
|
1273
1697
|
}
|
|
1274
|
-
await
|
|
1698
|
+
await fs2.remove(absolutePath);
|
|
1275
1699
|
}
|
|
1276
1700
|
const args = ["worktree", "add"];
|
|
1277
1701
|
if (options.createBranch) {
|
|
@@ -1298,11 +1722,10 @@ var GitWorktreeManager = class {
|
|
|
1298
1722
|
const worktrees = await this.listWorktrees({ porcelain: true });
|
|
1299
1723
|
const worktree = worktrees.find((wt) => wt.path === worktreePath);
|
|
1300
1724
|
if (!worktree) {
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
logger4.debug(`Found ${worktrees.length} worktrees:`);
|
|
1725
|
+
getLogger().debug(`Looking for worktree path: ${worktreePath}`);
|
|
1726
|
+
getLogger().debug(`Found ${worktrees.length} worktrees:`);
|
|
1304
1727
|
worktrees.forEach((wt, i) => {
|
|
1305
|
-
|
|
1728
|
+
getLogger().debug(` ${i}: path="${wt.path}", branch="${wt.branch}"`);
|
|
1306
1729
|
});
|
|
1307
1730
|
throw new Error(`Worktree not found: ${worktreePath}`);
|
|
1308
1731
|
}
|
|
@@ -1322,8 +1745,8 @@ var GitWorktreeManager = class {
|
|
|
1322
1745
|
if (options.force) args.push("--force");
|
|
1323
1746
|
args.push(worktreePath);
|
|
1324
1747
|
await executeGitCommand(args, { cwd: this._workingDirectory });
|
|
1325
|
-
if (options.removeDirectory && await
|
|
1326
|
-
await
|
|
1748
|
+
if (options.removeDirectory && await fs2.pathExists(worktreePath)) {
|
|
1749
|
+
await fs2.remove(worktreePath);
|
|
1327
1750
|
}
|
|
1328
1751
|
if (options.removeBranch && !worktree.bare) {
|
|
1329
1752
|
try {
|
|
@@ -1346,7 +1769,7 @@ var GitWorktreeManager = class {
|
|
|
1346
1769
|
let isValidRepo = false;
|
|
1347
1770
|
let hasValidBranch = false;
|
|
1348
1771
|
try {
|
|
1349
|
-
existsOnDisk = await
|
|
1772
|
+
existsOnDisk = await fs2.pathExists(worktreePath);
|
|
1350
1773
|
if (!existsOnDisk) {
|
|
1351
1774
|
issues.push("Worktree directory does not exist on disk");
|
|
1352
1775
|
}
|
|
@@ -1764,42 +2187,35 @@ async function createIssue(title, body, options) {
|
|
|
1764
2187
|
};
|
|
1765
2188
|
}
|
|
1766
2189
|
|
|
1767
|
-
// src/lib/GitHubService.ts
|
|
1768
|
-
init_logger();
|
|
1769
|
-
|
|
1770
2190
|
// src/utils/prompt.ts
|
|
1771
2191
|
init_logger();
|
|
1772
2192
|
import * as readline from "readline";
|
|
1773
2193
|
async function promptConfirmation(message, defaultValue = false) {
|
|
1774
|
-
const rl = readline.createInterface({
|
|
1775
|
-
input: process.stdin,
|
|
1776
|
-
output: process.stdout
|
|
1777
|
-
});
|
|
1778
2194
|
const suffix = defaultValue ? "[Y/n]" : "[y/N]";
|
|
1779
2195
|
const fullMessage = `${message} ${suffix}: `;
|
|
1780
|
-
|
|
1781
|
-
rl.
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
resolve(true);
|
|
1790
|
-
return;
|
|
1791
|
-
}
|
|
1792
|
-
if (normalized === "n" || normalized === "no") {
|
|
1793
|
-
resolve(false);
|
|
1794
|
-
return;
|
|
1795
|
-
}
|
|
1796
|
-
logger.warn("Invalid input, using default value", {
|
|
1797
|
-
input: answer,
|
|
1798
|
-
defaultValue
|
|
2196
|
+
while (true) {
|
|
2197
|
+
const rl = readline.createInterface({
|
|
2198
|
+
input: process.stdin,
|
|
2199
|
+
output: process.stdout
|
|
2200
|
+
});
|
|
2201
|
+
const answer = await new Promise((resolve) => {
|
|
2202
|
+
rl.question(fullMessage, (ans) => {
|
|
2203
|
+
rl.close();
|
|
2204
|
+
resolve(ans);
|
|
1799
2205
|
});
|
|
1800
|
-
resolve(defaultValue);
|
|
1801
2206
|
});
|
|
1802
|
-
|
|
2207
|
+
const normalized = answer.trim().toLowerCase();
|
|
2208
|
+
if (normalized === "") {
|
|
2209
|
+
return defaultValue;
|
|
2210
|
+
}
|
|
2211
|
+
if (normalized === "y" || normalized === "yes") {
|
|
2212
|
+
return true;
|
|
2213
|
+
}
|
|
2214
|
+
if (normalized === "n" || normalized === "no") {
|
|
2215
|
+
return false;
|
|
2216
|
+
}
|
|
2217
|
+
logger.warn("Invalid input. Please enter y/yes or n/no.");
|
|
2218
|
+
}
|
|
1803
2219
|
}
|
|
1804
2220
|
|
|
1805
2221
|
// src/lib/GitHubService.ts
|
|
@@ -1829,12 +2245,12 @@ var GitHubService = class {
|
|
|
1829
2245
|
return { type: "unknown", identifier: null, rawInput: input };
|
|
1830
2246
|
}
|
|
1831
2247
|
const number = parseInt(numberMatch[1], 10);
|
|
1832
|
-
|
|
2248
|
+
getLogger().debug("Checking if input is a PR", { number });
|
|
1833
2249
|
const pr = await this.isValidPR(number, repo);
|
|
1834
2250
|
if (pr) {
|
|
1835
2251
|
return { type: "pr", identifier: number.toString(), rawInput: input };
|
|
1836
2252
|
}
|
|
1837
|
-
|
|
2253
|
+
getLogger().debug("Checking if input is an issue", { number });
|
|
1838
2254
|
const issue = await this.isValidIssue(number, repo);
|
|
1839
2255
|
if (issue) {
|
|
1840
2256
|
return { type: "issue", identifier: number.toString(), rawInput: input };
|
|
@@ -1938,17 +2354,17 @@ var GitHubService = class {
|
|
|
1938
2354
|
return createIssue(title, body, { repo: repository, labels });
|
|
1939
2355
|
}
|
|
1940
2356
|
async getIssueUrl(issueNumber, repo) {
|
|
1941
|
-
|
|
2357
|
+
getLogger().debug("Fetching issue URL", { issueNumber, repo });
|
|
1942
2358
|
const issue = await fetchGhIssue(issueNumber, repo);
|
|
1943
2359
|
return issue.url;
|
|
1944
2360
|
}
|
|
1945
2361
|
// GitHub Projects integration
|
|
1946
2362
|
async moveIssueToInProgress(issueNumber) {
|
|
1947
|
-
|
|
2363
|
+
getLogger().info("Moving issue to In Progress in GitHub Projects", {
|
|
1948
2364
|
issueNumber
|
|
1949
2365
|
});
|
|
1950
2366
|
if (!await hasProjectScope()) {
|
|
1951
|
-
|
|
2367
|
+
getLogger().warn("Missing project scope in GitHub CLI auth");
|
|
1952
2368
|
throw new GitHubError(
|
|
1953
2369
|
"MISSING_SCOPE" /* MISSING_SCOPE */,
|
|
1954
2370
|
"GitHub CLI lacks project scope. Run: gh auth refresh -s project"
|
|
@@ -1959,18 +2375,18 @@ var GitHubService = class {
|
|
|
1959
2375
|
const repoInfo = await executeGhCommand(["repo", "view", "--json", "owner,name"]);
|
|
1960
2376
|
owner = repoInfo.owner.login;
|
|
1961
2377
|
} catch (error) {
|
|
1962
|
-
|
|
2378
|
+
getLogger().warn("Could not determine repository info", { error });
|
|
1963
2379
|
return;
|
|
1964
2380
|
}
|
|
1965
2381
|
let projects;
|
|
1966
2382
|
try {
|
|
1967
2383
|
projects = await fetchProjectList(owner);
|
|
1968
2384
|
} catch (error) {
|
|
1969
|
-
|
|
2385
|
+
getLogger().warn("Could not fetch projects", { owner, error });
|
|
1970
2386
|
return;
|
|
1971
2387
|
}
|
|
1972
2388
|
if (!projects.length) {
|
|
1973
|
-
|
|
2389
|
+
getLogger().warn("No projects found", { owner });
|
|
1974
2390
|
return;
|
|
1975
2391
|
}
|
|
1976
2392
|
for (const project of projects) {
|
|
@@ -1983,14 +2399,14 @@ var GitHubService = class {
|
|
|
1983
2399
|
try {
|
|
1984
2400
|
items = await fetchProjectItems(project.number, owner);
|
|
1985
2401
|
} catch (error) {
|
|
1986
|
-
|
|
2402
|
+
getLogger().debug("Could not fetch project items", { project: project.number, error });
|
|
1987
2403
|
return;
|
|
1988
2404
|
}
|
|
1989
2405
|
const item = items.find(
|
|
1990
2406
|
(i) => i.content.type === "Issue" && i.content.number === issueNumber
|
|
1991
2407
|
);
|
|
1992
2408
|
if (!item) {
|
|
1993
|
-
|
|
2409
|
+
getLogger().debug("Issue not found in project", {
|
|
1994
2410
|
issueNumber,
|
|
1995
2411
|
projectNumber: project.number
|
|
1996
2412
|
});
|
|
@@ -2000,19 +2416,19 @@ var GitHubService = class {
|
|
|
2000
2416
|
try {
|
|
2001
2417
|
fieldsData = await fetchProjectFields(project.number, owner);
|
|
2002
2418
|
} catch (error) {
|
|
2003
|
-
|
|
2419
|
+
getLogger().debug("Could not fetch project fields", { project: project.number, error });
|
|
2004
2420
|
return;
|
|
2005
2421
|
}
|
|
2006
2422
|
const statusField = fieldsData.fields.find((f) => f.name === "Status");
|
|
2007
2423
|
if (!statusField) {
|
|
2008
|
-
|
|
2424
|
+
getLogger().debug("No Status field found in project", { projectNumber: project.number });
|
|
2009
2425
|
return;
|
|
2010
2426
|
}
|
|
2011
2427
|
const inProgressOption = (_a = statusField.options) == null ? void 0 : _a.find(
|
|
2012
2428
|
(o) => o.name === "In Progress" || o.name === "In progress"
|
|
2013
2429
|
);
|
|
2014
2430
|
if (!inProgressOption) {
|
|
2015
|
-
|
|
2431
|
+
getLogger().debug("No In Progress option found in Status field", { projectNumber: project.number });
|
|
2016
2432
|
return;
|
|
2017
2433
|
}
|
|
2018
2434
|
try {
|
|
@@ -2022,12 +2438,12 @@ var GitHubService = class {
|
|
|
2022
2438
|
statusField.id,
|
|
2023
2439
|
inProgressOption.id
|
|
2024
2440
|
);
|
|
2025
|
-
|
|
2441
|
+
getLogger().info("Updated issue status in project", {
|
|
2026
2442
|
issueNumber,
|
|
2027
2443
|
projectNumber: project.number
|
|
2028
2444
|
});
|
|
2029
2445
|
} catch (error) {
|
|
2030
|
-
|
|
2446
|
+
getLogger().debug("Could not update project item", { item: item.id, error });
|
|
2031
2447
|
}
|
|
2032
2448
|
}
|
|
2033
2449
|
// Utility methods
|
|
@@ -2388,7 +2804,6 @@ ${issue.body}`;
|
|
|
2388
2804
|
};
|
|
2389
2805
|
|
|
2390
2806
|
// src/lib/IssueTrackerFactory.ts
|
|
2391
|
-
init_logger();
|
|
2392
2807
|
var IssueTrackerFactory = class {
|
|
2393
2808
|
/**
|
|
2394
2809
|
* Create an IssueTracker instance based on settings configuration
|
|
@@ -2401,11 +2816,11 @@ var IssueTrackerFactory = class {
|
|
|
2401
2816
|
static create(settings) {
|
|
2402
2817
|
var _a, _b;
|
|
2403
2818
|
const provider = ((_a = settings.issueManagement) == null ? void 0 : _a.provider) ?? "github";
|
|
2404
|
-
|
|
2405
|
-
|
|
2819
|
+
getLogger().debug(`IssueTrackerFactory: Creating tracker for provider "${provider}"`);
|
|
2820
|
+
getLogger().debug(`IssueTrackerFactory: issueManagement settings:`, JSON.stringify(settings.issueManagement, null, 2));
|
|
2406
2821
|
switch (provider) {
|
|
2407
2822
|
case "github":
|
|
2408
|
-
|
|
2823
|
+
getLogger().debug("IssueTrackerFactory: Creating GitHubService");
|
|
2409
2824
|
return new GitHubService();
|
|
2410
2825
|
case "linear": {
|
|
2411
2826
|
const linearSettings = (_b = settings.issueManagement) == null ? void 0 : _b.linear;
|
|
@@ -2416,7 +2831,7 @@ var IssueTrackerFactory = class {
|
|
|
2416
2831
|
if (linearSettings == null ? void 0 : linearSettings.branchFormat) {
|
|
2417
2832
|
linearConfig.branchFormat = linearSettings.branchFormat;
|
|
2418
2833
|
}
|
|
2419
|
-
|
|
2834
|
+
getLogger().debug(`IssueTrackerFactory: Creating LinearService with config:`, JSON.stringify(linearConfig, null, 2));
|
|
2420
2835
|
return new LinearService(linearConfig);
|
|
2421
2836
|
}
|
|
2422
2837
|
default:
|
|
@@ -2437,12 +2852,11 @@ var IssueTrackerFactory = class {
|
|
|
2437
2852
|
};
|
|
2438
2853
|
|
|
2439
2854
|
// src/lib/EnvironmentManager.ts
|
|
2440
|
-
|
|
2441
|
-
import fs2 from "fs-extra";
|
|
2855
|
+
import fs3 from "fs-extra";
|
|
2442
2856
|
|
|
2443
2857
|
// src/utils/env.ts
|
|
2444
2858
|
init_logger();
|
|
2445
|
-
import
|
|
2859
|
+
import path5 from "path";
|
|
2446
2860
|
import dotenvFlow from "dotenv-flow";
|
|
2447
2861
|
function parseEnvFile(content) {
|
|
2448
2862
|
const envMap = /* @__PURE__ */ new Map();
|
|
@@ -2509,7 +2923,7 @@ async function buildEnvSourceCommands(workspacePath, fileExists) {
|
|
|
2509
2923
|
const files = getDotenvFlowFiles();
|
|
2510
2924
|
const commands = [];
|
|
2511
2925
|
for (const file of files) {
|
|
2512
|
-
const fullPath =
|
|
2926
|
+
const fullPath = path5.join(workspacePath, file);
|
|
2513
2927
|
const exists = await fileExists(fullPath);
|
|
2514
2928
|
if (exists) {
|
|
2515
2929
|
commands.push(`source ${file}`);
|
|
@@ -2520,7 +2934,7 @@ async function buildEnvSourceCommands(workspacePath, fileExists) {
|
|
|
2520
2934
|
async function findEnvFileContainingVariable(workspacePath, variableName, fileExists, getEnvVariable) {
|
|
2521
2935
|
const files = getDotenvFlowFiles().reverse();
|
|
2522
2936
|
for (const file of files) {
|
|
2523
|
-
const fullPath =
|
|
2937
|
+
const fullPath = path5.join(workspacePath, file);
|
|
2524
2938
|
if (!await fileExists(fullPath)) {
|
|
2525
2939
|
continue;
|
|
2526
2940
|
}
|
|
@@ -2560,7 +2974,6 @@ function calculatePortForBranch(branchName, basePort = 3e3) {
|
|
|
2560
2974
|
}
|
|
2561
2975
|
|
|
2562
2976
|
// src/lib/EnvironmentManager.ts
|
|
2563
|
-
var logger2 = createLogger({ prefix: "\u{1F4DD}" });
|
|
2564
2977
|
var EnvironmentManager = class {
|
|
2565
2978
|
constructor() {
|
|
2566
2979
|
this.backupSuffix = ".backup";
|
|
@@ -2575,15 +2988,15 @@ var EnvironmentManager = class {
|
|
|
2575
2988
|
if (!validation.valid) {
|
|
2576
2989
|
throw new Error(validation.error ?? "Invalid variable name");
|
|
2577
2990
|
}
|
|
2578
|
-
const fileExists = await
|
|
2991
|
+
const fileExists = await fs3.pathExists(filePath);
|
|
2579
2992
|
if (!fileExists) {
|
|
2580
|
-
|
|
2993
|
+
getLogger().info(`Creating ${filePath} with ${key}...`);
|
|
2581
2994
|
const content = formatEnvLine(key, value);
|
|
2582
|
-
await
|
|
2583
|
-
|
|
2995
|
+
await fs3.writeFile(filePath, content, "utf8");
|
|
2996
|
+
getLogger().success(`${filePath} created with ${key}`);
|
|
2584
2997
|
return;
|
|
2585
2998
|
}
|
|
2586
|
-
const existingContent = await
|
|
2999
|
+
const existingContent = await fs3.readFile(filePath, "utf8");
|
|
2587
3000
|
const envMap = parseEnvFile(existingContent);
|
|
2588
3001
|
let backupPath;
|
|
2589
3002
|
if (backup) {
|
|
@@ -2612,15 +3025,15 @@ var EnvironmentManager = class {
|
|
|
2612
3025
|
newLines.push(line);
|
|
2613
3026
|
}
|
|
2614
3027
|
if (!variableUpdated) {
|
|
2615
|
-
|
|
3028
|
+
getLogger().info(`Adding ${key} to ${filePath}...`);
|
|
2616
3029
|
newLines.push(formatEnvLine(key, value));
|
|
2617
|
-
|
|
3030
|
+
getLogger().success(`${key} added successfully`);
|
|
2618
3031
|
} else {
|
|
2619
|
-
|
|
2620
|
-
|
|
3032
|
+
getLogger().info(`Updating ${key} in ${filePath}...`);
|
|
3033
|
+
getLogger().success(`${key} updated successfully`);
|
|
2621
3034
|
}
|
|
2622
3035
|
const newContent = newLines.join("\n");
|
|
2623
|
-
await
|
|
3036
|
+
await fs3.writeFile(filePath, newContent, "utf8");
|
|
2624
3037
|
return backupPath;
|
|
2625
3038
|
}
|
|
2626
3039
|
/**
|
|
@@ -2628,10 +3041,10 @@ var EnvironmentManager = class {
|
|
|
2628
3041
|
*/
|
|
2629
3042
|
async readEnvFile(filePath) {
|
|
2630
3043
|
try {
|
|
2631
|
-
const content = await
|
|
3044
|
+
const content = await fs3.readFile(filePath, "utf8");
|
|
2632
3045
|
return parseEnvFile(content);
|
|
2633
3046
|
} catch (error) {
|
|
2634
|
-
|
|
3047
|
+
getLogger().debug(
|
|
2635
3048
|
`Could not read env file ${filePath}: ${error instanceof Error ? error.message : String(error)}`
|
|
2636
3049
|
);
|
|
2637
3050
|
return /* @__PURE__ */ new Map();
|
|
@@ -2651,13 +3064,13 @@ var EnvironmentManager = class {
|
|
|
2651
3064
|
* @private
|
|
2652
3065
|
*/
|
|
2653
3066
|
async copyIfExists(source, destination) {
|
|
2654
|
-
const sourceExists = await
|
|
3067
|
+
const sourceExists = await fs3.pathExists(source);
|
|
2655
3068
|
if (!sourceExists) {
|
|
2656
|
-
|
|
3069
|
+
getLogger().debug(`Source file ${source} does not exist, skipping copy`);
|
|
2657
3070
|
return;
|
|
2658
3071
|
}
|
|
2659
|
-
await
|
|
2660
|
-
|
|
3072
|
+
await fs3.copy(source, destination, { overwrite: false });
|
|
3073
|
+
getLogger().success(`Copied ${source} to ${destination}`);
|
|
2661
3074
|
}
|
|
2662
3075
|
/**
|
|
2663
3076
|
* Calculate unique port for workspace
|
|
@@ -2717,7 +3130,7 @@ var EnvironmentManager = class {
|
|
|
2717
3130
|
*/
|
|
2718
3131
|
async validateEnvFile(filePath) {
|
|
2719
3132
|
try {
|
|
2720
|
-
const content = await
|
|
3133
|
+
const content = await fs3.readFile(filePath, "utf8");
|
|
2721
3134
|
const envMap = parseEnvFile(content);
|
|
2722
3135
|
const errors = [];
|
|
2723
3136
|
for (const [key, value] of envMap.entries()) {
|
|
@@ -2745,25 +3158,23 @@ var EnvironmentManager = class {
|
|
|
2745
3158
|
async createBackup(filePath) {
|
|
2746
3159
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
2747
3160
|
const backupPath = `${filePath}${this.backupSuffix}-${timestamp}`;
|
|
2748
|
-
await
|
|
2749
|
-
|
|
3161
|
+
await fs3.copy(filePath, backupPath);
|
|
3162
|
+
getLogger().debug(`Created backup at ${backupPath}`);
|
|
2750
3163
|
return backupPath;
|
|
2751
3164
|
}
|
|
2752
3165
|
};
|
|
2753
3166
|
|
|
2754
3167
|
// src/lib/DatabaseManager.ts
|
|
2755
|
-
|
|
2756
|
-
import fs3 from "fs-extra";
|
|
2757
|
-
var logger3 = createLogger({ prefix: "\u{1F5C2}\uFE0F" });
|
|
3168
|
+
import fs4 from "fs-extra";
|
|
2758
3169
|
var DatabaseManager = class {
|
|
2759
3170
|
constructor(provider, environment, databaseUrlEnvVarName = "DATABASE_URL") {
|
|
2760
3171
|
this.provider = provider;
|
|
2761
3172
|
this.environment = environment;
|
|
2762
3173
|
this.databaseUrlEnvVarName = databaseUrlEnvVarName;
|
|
2763
3174
|
if (databaseUrlEnvVarName !== "DATABASE_URL") {
|
|
2764
|
-
|
|
3175
|
+
getLogger().debug(`DatabaseManager configured with custom variable: ${databaseUrlEnvVarName}`);
|
|
2765
3176
|
} else {
|
|
2766
|
-
|
|
3177
|
+
getLogger().debug("DatabaseManager using default variable: DATABASE_URL");
|
|
2767
3178
|
}
|
|
2768
3179
|
}
|
|
2769
3180
|
/**
|
|
@@ -2780,12 +3191,12 @@ var DatabaseManager = class {
|
|
|
2780
3191
|
*/
|
|
2781
3192
|
async shouldUseDatabaseBranching(workspacePath) {
|
|
2782
3193
|
if (!this.provider.isConfigured()) {
|
|
2783
|
-
|
|
3194
|
+
getLogger().debug("Skipping database branching: Database provider not configured");
|
|
2784
3195
|
return false;
|
|
2785
3196
|
}
|
|
2786
3197
|
const hasDatabaseUrl = await this.hasDatabaseUrlInEnv(workspacePath);
|
|
2787
3198
|
if (!hasDatabaseUrl) {
|
|
2788
|
-
|
|
3199
|
+
getLogger().debug(
|
|
2789
3200
|
"Skipping database branching: configured database URL variable not found in any env file"
|
|
2790
3201
|
);
|
|
2791
3202
|
return false;
|
|
@@ -2806,28 +3217,28 @@ var DatabaseManager = class {
|
|
|
2806
3217
|
return null;
|
|
2807
3218
|
}
|
|
2808
3219
|
if (!await this.provider.isCliAvailable()) {
|
|
2809
|
-
|
|
2810
|
-
|
|
3220
|
+
getLogger().warn("Skipping database branch creation: Neon CLI not available");
|
|
3221
|
+
getLogger().warn("Install with: npm install -g neonctl");
|
|
2811
3222
|
return null;
|
|
2812
3223
|
}
|
|
2813
3224
|
try {
|
|
2814
3225
|
const isAuth = await this.provider.isAuthenticated(cwd);
|
|
2815
3226
|
if (!isAuth) {
|
|
2816
|
-
|
|
2817
|
-
|
|
3227
|
+
getLogger().warn("Skipping database branch creation: Not authenticated with Neon CLI");
|
|
3228
|
+
getLogger().warn("Run: neon auth");
|
|
2818
3229
|
return null;
|
|
2819
3230
|
}
|
|
2820
3231
|
} catch (error) {
|
|
2821
3232
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2822
|
-
|
|
3233
|
+
getLogger().error(`Database authentication check failed: ${errorMessage}`);
|
|
2823
3234
|
throw error;
|
|
2824
3235
|
}
|
|
2825
3236
|
try {
|
|
2826
3237
|
const connectionString = await this.provider.createBranch(branchName, fromBranch, cwd);
|
|
2827
|
-
|
|
3238
|
+
getLogger().success(`Database branch ready: ${this.provider.sanitizeBranchName(branchName)}`);
|
|
2828
3239
|
return connectionString;
|
|
2829
3240
|
} catch (error) {
|
|
2830
|
-
|
|
3241
|
+
getLogger().error(
|
|
2831
3242
|
`Failed to create database branch: ${error instanceof Error ? error.message : String(error)}`
|
|
2832
3243
|
);
|
|
2833
3244
|
throw error;
|
|
@@ -2853,7 +3264,7 @@ var DatabaseManager = class {
|
|
|
2853
3264
|
};
|
|
2854
3265
|
}
|
|
2855
3266
|
if (!this.provider.isConfigured()) {
|
|
2856
|
-
|
|
3267
|
+
getLogger().debug("Skipping database branch deletion: Database provider not configured");
|
|
2857
3268
|
return {
|
|
2858
3269
|
success: true,
|
|
2859
3270
|
deleted: false,
|
|
@@ -2862,7 +3273,7 @@ var DatabaseManager = class {
|
|
|
2862
3273
|
};
|
|
2863
3274
|
}
|
|
2864
3275
|
if (!await this.provider.isCliAvailable()) {
|
|
2865
|
-
|
|
3276
|
+
getLogger().info("Skipping database branch deletion: CLI tool not available");
|
|
2866
3277
|
return {
|
|
2867
3278
|
success: false,
|
|
2868
3279
|
deleted: false,
|
|
@@ -2874,7 +3285,7 @@ var DatabaseManager = class {
|
|
|
2874
3285
|
try {
|
|
2875
3286
|
const isAuth = await this.provider.isAuthenticated(cwd);
|
|
2876
3287
|
if (!isAuth) {
|
|
2877
|
-
|
|
3288
|
+
getLogger().warn("Skipping database branch deletion: Not authenticated with DB Provider");
|
|
2878
3289
|
return {
|
|
2879
3290
|
success: false,
|
|
2880
3291
|
deleted: false,
|
|
@@ -2885,7 +3296,7 @@ var DatabaseManager = class {
|
|
|
2885
3296
|
}
|
|
2886
3297
|
} catch (error) {
|
|
2887
3298
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2888
|
-
|
|
3299
|
+
getLogger().error(`Database authentication check failed: ${errorMessage}`);
|
|
2889
3300
|
return {
|
|
2890
3301
|
success: false,
|
|
2891
3302
|
deleted: false,
|
|
@@ -2898,7 +3309,7 @@ var DatabaseManager = class {
|
|
|
2898
3309
|
const result = await this.provider.deleteBranch(branchName, isPreview, cwd);
|
|
2899
3310
|
return result;
|
|
2900
3311
|
} catch (error) {
|
|
2901
|
-
|
|
3312
|
+
getLogger().warn(
|
|
2902
3313
|
`Unexpected error in database deletion: ${error instanceof Error ? error.message : String(error)}`
|
|
2903
3314
|
);
|
|
2904
3315
|
return {
|
|
@@ -2919,13 +3330,13 @@ var DatabaseManager = class {
|
|
|
2919
3330
|
*/
|
|
2920
3331
|
async getBranchNameFromConnectionString(connectionString, cwd) {
|
|
2921
3332
|
if (!this.provider.isConfigured()) {
|
|
2922
|
-
|
|
3333
|
+
getLogger().debug("Provider not configured, skipping reverse lookup");
|
|
2923
3334
|
return null;
|
|
2924
3335
|
}
|
|
2925
3336
|
if ("getBranchNameFromConnectionString" in this.provider && typeof this.provider.getBranchNameFromConnectionString === "function") {
|
|
2926
3337
|
return this.provider.getBranchNameFromConnectionString(connectionString, cwd);
|
|
2927
3338
|
}
|
|
2928
|
-
|
|
3339
|
+
getLogger().debug("Provider does not support reverse lookup");
|
|
2929
3340
|
return null;
|
|
2930
3341
|
}
|
|
2931
3342
|
/**
|
|
@@ -2936,26 +3347,26 @@ var DatabaseManager = class {
|
|
|
2936
3347
|
async hasDatabaseUrlInEnv(workspacePath) {
|
|
2937
3348
|
try {
|
|
2938
3349
|
if (this.databaseUrlEnvVarName !== "DATABASE_URL") {
|
|
2939
|
-
|
|
3350
|
+
getLogger().debug(`Looking for custom database URL variable: ${this.databaseUrlEnvVarName}`);
|
|
2940
3351
|
} else {
|
|
2941
|
-
|
|
3352
|
+
getLogger().debug("Looking for default database URL variable: DATABASE_URL");
|
|
2942
3353
|
}
|
|
2943
3354
|
const hasConfiguredVar = await hasVariableInAnyEnvFile(
|
|
2944
3355
|
workspacePath,
|
|
2945
3356
|
this.databaseUrlEnvVarName,
|
|
2946
|
-
async (p) =>
|
|
3357
|
+
async (p) => fs4.pathExists(p),
|
|
2947
3358
|
async (p, v) => this.environment.getEnvVariable(p, v)
|
|
2948
3359
|
);
|
|
2949
3360
|
if (hasConfiguredVar) {
|
|
2950
3361
|
if (this.databaseUrlEnvVarName !== "DATABASE_URL") {
|
|
2951
|
-
|
|
3362
|
+
getLogger().debug(`\u2705 Found custom database URL variable: ${this.databaseUrlEnvVarName}`);
|
|
2952
3363
|
} else {
|
|
2953
|
-
|
|
3364
|
+
getLogger().debug(`\u2705 Found default database URL variable: DATABASE_URL`);
|
|
2954
3365
|
}
|
|
2955
3366
|
return true;
|
|
2956
3367
|
}
|
|
2957
3368
|
if (this.databaseUrlEnvVarName !== "DATABASE_URL") {
|
|
2958
|
-
|
|
3369
|
+
getLogger().debug(`\u274C Custom database URL variable '${this.databaseUrlEnvVarName}' not found in any env file`);
|
|
2959
3370
|
throw new Error(
|
|
2960
3371
|
`Configured database URL environment variable '${this.databaseUrlEnvVarName}' not found in any dotenv-flow file. Please add it to an .env file or update your iloom configuration.`
|
|
2961
3372
|
);
|
|
@@ -2963,13 +3374,13 @@ var DatabaseManager = class {
|
|
|
2963
3374
|
const hasDefaultVar = await hasVariableInAnyEnvFile(
|
|
2964
3375
|
workspacePath,
|
|
2965
3376
|
"DATABASE_URL",
|
|
2966
|
-
async (p) =>
|
|
3377
|
+
async (p) => fs4.pathExists(p),
|
|
2967
3378
|
async (p, v) => this.environment.getEnvVariable(p, v)
|
|
2968
3379
|
);
|
|
2969
3380
|
if (hasDefaultVar) {
|
|
2970
|
-
|
|
3381
|
+
getLogger().debug("\u2705 Found fallback DATABASE_URL variable");
|
|
2971
3382
|
} else {
|
|
2972
|
-
|
|
3383
|
+
getLogger().debug("\u274C No DATABASE_URL variable found in any env file");
|
|
2973
3384
|
}
|
|
2974
3385
|
return hasDefaultVar;
|
|
2975
3386
|
} catch (error) {
|
|
@@ -2986,6 +3397,7 @@ init_logger();
|
|
|
2986
3397
|
import { execa as execa4 } from "execa";
|
|
2987
3398
|
import { existsSync as existsSync2 } from "fs";
|
|
2988
3399
|
import { join } from "path";
|
|
3400
|
+
import { createHash as createHash3 } from "crypto";
|
|
2989
3401
|
|
|
2990
3402
|
// src/utils/terminal.ts
|
|
2991
3403
|
import { execa as execa3 } from "execa";
|
|
@@ -3062,8 +3474,8 @@ async function buildAppleScript(options) {
|
|
|
3062
3474
|
script += `end tell`;
|
|
3063
3475
|
return script;
|
|
3064
3476
|
}
|
|
3065
|
-
function escapePathForAppleScript(
|
|
3066
|
-
return
|
|
3477
|
+
function escapePathForAppleScript(path7) {
|
|
3478
|
+
return path7.replace(/'/g, "'\\''");
|
|
3067
3479
|
}
|
|
3068
3480
|
function escapeForAppleScript(command) {
|
|
3069
3481
|
return command.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
@@ -3152,7 +3564,8 @@ function parseJsonStreamOutput(output) {
|
|
|
3152
3564
|
}
|
|
3153
3565
|
}
|
|
3154
3566
|
async function launchClaude(prompt, options = {}) {
|
|
3155
|
-
const { model, permissionMode, addDir, headless = false, appendSystemPrompt, mcpConfig, allowedTools, disallowedTools, agents } = options;
|
|
3567
|
+
const { model, permissionMode, addDir, headless = false, appendSystemPrompt, mcpConfig, allowedTools, disallowedTools, agents, sessionId } = options;
|
|
3568
|
+
const log = getLogger();
|
|
3156
3569
|
const args = [];
|
|
3157
3570
|
if (headless) {
|
|
3158
3571
|
args.push("-p");
|
|
@@ -3186,6 +3599,9 @@ async function launchClaude(prompt, options = {}) {
|
|
|
3186
3599
|
if (agents) {
|
|
3187
3600
|
args.push("--agents", JSON.stringify(agents));
|
|
3188
3601
|
}
|
|
3602
|
+
if (sessionId) {
|
|
3603
|
+
args.push("--session-id", sessionId);
|
|
3604
|
+
}
|
|
3189
3605
|
try {
|
|
3190
3606
|
if (headless) {
|
|
3191
3607
|
const isDebugMode = logger.isDebugEnabled();
|
|
@@ -3210,13 +3626,13 @@ async function launchClaude(prompt, options = {}) {
|
|
|
3210
3626
|
const text = chunk.toString();
|
|
3211
3627
|
outputBuffer += text;
|
|
3212
3628
|
if (isDebugMode) {
|
|
3213
|
-
|
|
3629
|
+
log.stdout.write(text);
|
|
3214
3630
|
} else {
|
|
3215
3631
|
if (isFirstProgress) {
|
|
3216
|
-
|
|
3632
|
+
log.stdout.write("\u{1F916} .");
|
|
3217
3633
|
isFirstProgress = false;
|
|
3218
3634
|
} else {
|
|
3219
|
-
|
|
3635
|
+
log.stdout.write(".");
|
|
3220
3636
|
}
|
|
3221
3637
|
}
|
|
3222
3638
|
});
|
|
@@ -3225,36 +3641,137 @@ async function launchClaude(prompt, options = {}) {
|
|
|
3225
3641
|
if (isStreaming) {
|
|
3226
3642
|
const rawOutput = outputBuffer.trim();
|
|
3227
3643
|
if (!isDebugMode) {
|
|
3228
|
-
|
|
3644
|
+
log.stdout.write("\n");
|
|
3229
3645
|
}
|
|
3230
3646
|
return isJsonStreamFormat ? parseJsonStreamOutput(rawOutput) : rawOutput;
|
|
3231
3647
|
} else {
|
|
3232
3648
|
if (isDebugMode) {
|
|
3233
|
-
|
|
3649
|
+
log.stdout.write(result.stdout);
|
|
3234
3650
|
if (result.stdout && !result.stdout.endsWith("\n")) {
|
|
3235
|
-
|
|
3651
|
+
log.stdout.write("\n");
|
|
3236
3652
|
}
|
|
3237
3653
|
} else {
|
|
3238
|
-
|
|
3239
|
-
|
|
3654
|
+
log.stdout.write("\u{1F916} .");
|
|
3655
|
+
log.stdout.write("\n");
|
|
3240
3656
|
}
|
|
3241
3657
|
const rawOutput = result.stdout.trim();
|
|
3242
3658
|
return isJsonStreamFormat ? parseJsonStreamOutput(rawOutput) : rawOutput;
|
|
3243
3659
|
}
|
|
3244
3660
|
} else {
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3661
|
+
try {
|
|
3662
|
+
await execa4("claude", [...args, "--", prompt], {
|
|
3663
|
+
...addDir && { cwd: addDir },
|
|
3664
|
+
stdio: ["inherit", "inherit", "pipe"],
|
|
3665
|
+
// Capture stderr to detect session conflicts
|
|
3666
|
+
timeout: 0,
|
|
3667
|
+
// Disable timeout
|
|
3668
|
+
verbose: logger.isDebugEnabled()
|
|
3669
|
+
});
|
|
3670
|
+
return;
|
|
3671
|
+
} catch (interactiveError) {
|
|
3672
|
+
const interactiveExecaError = interactiveError;
|
|
3673
|
+
const interactiveErrorMessage = interactiveExecaError.stderr ?? interactiveExecaError.message ?? "";
|
|
3674
|
+
const sessionMatch = interactiveErrorMessage.match(/Session ID ([0-9a-f-]+) is already in use/i);
|
|
3675
|
+
const conflictSessionId = sessionMatch == null ? void 0 : sessionMatch[1];
|
|
3676
|
+
if (sessionMatch && sessionId && conflictSessionId) {
|
|
3677
|
+
log.debug(`Session ID ${conflictSessionId} already in use, retrying with --resume`);
|
|
3678
|
+
const resumeArgs = args.filter((arg, idx) => {
|
|
3679
|
+
if (arg === "--session-id") return false;
|
|
3680
|
+
if (idx > 0 && args[idx - 1] === "--session-id") return false;
|
|
3681
|
+
return true;
|
|
3682
|
+
});
|
|
3683
|
+
resumeArgs.push("--resume", conflictSessionId);
|
|
3684
|
+
await execa4("claude", resumeArgs, {
|
|
3685
|
+
...addDir && { cwd: addDir },
|
|
3686
|
+
stdio: "inherit",
|
|
3687
|
+
timeout: 0,
|
|
3688
|
+
verbose: logger.isDebugEnabled()
|
|
3689
|
+
});
|
|
3690
|
+
return;
|
|
3691
|
+
}
|
|
3692
|
+
throw interactiveError;
|
|
3693
|
+
}
|
|
3254
3694
|
}
|
|
3255
3695
|
} catch (error) {
|
|
3256
3696
|
const execaError = error;
|
|
3257
3697
|
const errorMessage = execaError.stderr ?? execaError.message ?? "Unknown Claude CLI error";
|
|
3698
|
+
const sessionInUseMatch = errorMessage.match(/Session ID ([0-9a-f-]+) is already in use/i);
|
|
3699
|
+
const extractedSessionId = sessionInUseMatch == null ? void 0 : sessionInUseMatch[1];
|
|
3700
|
+
if (sessionInUseMatch && sessionId && extractedSessionId) {
|
|
3701
|
+
log.debug(`Session ID ${extractedSessionId} already in use, retrying with --resume`);
|
|
3702
|
+
const resumeArgs = args.filter((arg, idx) => {
|
|
3703
|
+
if (arg === "--session-id") return false;
|
|
3704
|
+
if (idx > 0 && args[idx - 1] === "--session-id") return false;
|
|
3705
|
+
return true;
|
|
3706
|
+
});
|
|
3707
|
+
resumeArgs.push("--resume", extractedSessionId);
|
|
3708
|
+
try {
|
|
3709
|
+
if (headless) {
|
|
3710
|
+
const isDebugMode = logger.isDebugEnabled();
|
|
3711
|
+
const execaOptions = {
|
|
3712
|
+
input: prompt,
|
|
3713
|
+
timeout: 0,
|
|
3714
|
+
...addDir && { cwd: addDir },
|
|
3715
|
+
verbose: isDebugMode,
|
|
3716
|
+
...isDebugMode && { stdio: ["pipe", "pipe", "pipe"] }
|
|
3717
|
+
};
|
|
3718
|
+
const subprocess = execa4("claude", resumeArgs, execaOptions);
|
|
3719
|
+
const isJsonStreamFormat = resumeArgs.includes("--output-format") && resumeArgs.includes("stream-json");
|
|
3720
|
+
let outputBuffer = "";
|
|
3721
|
+
let isStreaming = false;
|
|
3722
|
+
let isFirstProgress = true;
|
|
3723
|
+
if (subprocess.stdout && typeof subprocess.stdout.on === "function") {
|
|
3724
|
+
isStreaming = true;
|
|
3725
|
+
subprocess.stdout.on("data", (chunk) => {
|
|
3726
|
+
const text = chunk.toString();
|
|
3727
|
+
outputBuffer += text;
|
|
3728
|
+
if (isDebugMode) {
|
|
3729
|
+
log.stdout.write(text);
|
|
3730
|
+
} else {
|
|
3731
|
+
if (isFirstProgress) {
|
|
3732
|
+
log.stdout.write("\u{1F916} .");
|
|
3733
|
+
isFirstProgress = false;
|
|
3734
|
+
} else {
|
|
3735
|
+
log.stdout.write(".");
|
|
3736
|
+
}
|
|
3737
|
+
}
|
|
3738
|
+
});
|
|
3739
|
+
}
|
|
3740
|
+
const result = await subprocess;
|
|
3741
|
+
if (isStreaming) {
|
|
3742
|
+
const rawOutput = outputBuffer.trim();
|
|
3743
|
+
if (!isDebugMode) {
|
|
3744
|
+
log.stdout.write("\n");
|
|
3745
|
+
}
|
|
3746
|
+
return isJsonStreamFormat ? parseJsonStreamOutput(rawOutput) : rawOutput;
|
|
3747
|
+
} else {
|
|
3748
|
+
if (isDebugMode) {
|
|
3749
|
+
log.stdout.write(result.stdout);
|
|
3750
|
+
if (result.stdout && !result.stdout.endsWith("\n")) {
|
|
3751
|
+
log.stdout.write("\n");
|
|
3752
|
+
}
|
|
3753
|
+
} else {
|
|
3754
|
+
log.stdout.write("\u{1F916} .");
|
|
3755
|
+
log.stdout.write("\n");
|
|
3756
|
+
}
|
|
3757
|
+
const rawOutput = result.stdout.trim();
|
|
3758
|
+
return isJsonStreamFormat ? parseJsonStreamOutput(rawOutput) : rawOutput;
|
|
3759
|
+
}
|
|
3760
|
+
} else {
|
|
3761
|
+
await execa4("claude", resumeArgs, {
|
|
3762
|
+
...addDir && { cwd: addDir },
|
|
3763
|
+
stdio: "inherit",
|
|
3764
|
+
timeout: 0,
|
|
3765
|
+
verbose: logger.isDebugEnabled()
|
|
3766
|
+
});
|
|
3767
|
+
return;
|
|
3768
|
+
}
|
|
3769
|
+
} catch (retryError) {
|
|
3770
|
+
const retryExecaError = retryError;
|
|
3771
|
+
const retryErrorMessage = retryExecaError.stderr ?? retryExecaError.message ?? "Unknown Claude CLI error";
|
|
3772
|
+
throw new Error(`Claude CLI error: ${retryErrorMessage}`);
|
|
3773
|
+
}
|
|
3774
|
+
}
|
|
3258
3775
|
throw new Error(`Claude CLI error: ${errorMessage}`);
|
|
3259
3776
|
}
|
|
3260
3777
|
}
|
|
@@ -3300,7 +3817,7 @@ async function launchClaudeInNewTerminalWindow(_prompt, options) {
|
|
|
3300
3817
|
init_logger();
|
|
3301
3818
|
import { readFile as readFile2 } from "fs/promises";
|
|
3302
3819
|
import { accessSync } from "fs";
|
|
3303
|
-
import
|
|
3820
|
+
import path6 from "path";
|
|
3304
3821
|
import { fileURLToPath } from "url";
|
|
3305
3822
|
var PromptTemplateManager = class {
|
|
3306
3823
|
constructor(templateDir) {
|
|
@@ -3309,17 +3826,17 @@ var PromptTemplateManager = class {
|
|
|
3309
3826
|
} else {
|
|
3310
3827
|
const currentFileUrl = import.meta.url;
|
|
3311
3828
|
const currentFilePath = fileURLToPath(currentFileUrl);
|
|
3312
|
-
const distDir =
|
|
3313
|
-
let templateDir2 =
|
|
3829
|
+
const distDir = path6.dirname(currentFilePath);
|
|
3830
|
+
let templateDir2 = path6.join(distDir, "prompts");
|
|
3314
3831
|
let currentDir = distDir;
|
|
3315
|
-
while (currentDir !==
|
|
3316
|
-
const candidatePath =
|
|
3832
|
+
while (currentDir !== path6.dirname(currentDir)) {
|
|
3833
|
+
const candidatePath = path6.join(currentDir, "prompts");
|
|
3317
3834
|
try {
|
|
3318
3835
|
accessSync(candidatePath);
|
|
3319
3836
|
templateDir2 = candidatePath;
|
|
3320
3837
|
break;
|
|
3321
3838
|
} catch {
|
|
3322
|
-
currentDir =
|
|
3839
|
+
currentDir = path6.dirname(currentDir);
|
|
3323
3840
|
}
|
|
3324
3841
|
}
|
|
3325
3842
|
this.templateDir = templateDir2;
|
|
@@ -3334,7 +3851,7 @@ var PromptTemplateManager = class {
|
|
|
3334
3851
|
* Load a template file by name
|
|
3335
3852
|
*/
|
|
3336
3853
|
async loadTemplate(templateName) {
|
|
3337
|
-
const templatePath =
|
|
3854
|
+
const templatePath = path6.join(this.templateDir, `${templateName}-prompt.txt`);
|
|
3338
3855
|
logger.debug("Loading template", {
|
|
3339
3856
|
templateName,
|
|
3340
3857
|
templateDir: this.templateDir,
|
|
@@ -3419,6 +3936,18 @@ var PromptTemplateManager = class {
|
|
|
3419
3936
|
if (variables.VSCODE_SETTINGS_GITIGNORED !== void 0) {
|
|
3420
3937
|
result = result.replace(/VSCODE_SETTINGS_GITIGNORED/g, variables.VSCODE_SETTINGS_GITIGNORED);
|
|
3421
3938
|
}
|
|
3939
|
+
if (variables.SESSION_CONTEXT !== void 0) {
|
|
3940
|
+
result = result.replace(/SESSION_CONTEXT/g, variables.SESSION_CONTEXT);
|
|
3941
|
+
}
|
|
3942
|
+
if (variables.BRANCH_NAME !== void 0) {
|
|
3943
|
+
result = result.replace(/BRANCH_NAME/g, variables.BRANCH_NAME);
|
|
3944
|
+
}
|
|
3945
|
+
if (variables.LOOM_TYPE !== void 0) {
|
|
3946
|
+
result = result.replace(/LOOM_TYPE/g, variables.LOOM_TYPE);
|
|
3947
|
+
}
|
|
3948
|
+
if (variables.COMPACT_SUMMARIES !== void 0) {
|
|
3949
|
+
result = result.replace(/COMPACT_SUMMARIES/g, variables.COMPACT_SUMMARIES);
|
|
3950
|
+
}
|
|
3422
3951
|
return result;
|
|
3423
3952
|
}
|
|
3424
3953
|
/**
|
|
@@ -3477,6 +4006,18 @@ var PromptTemplateManager = class {
|
|
|
3477
4006
|
} else {
|
|
3478
4007
|
result = result.replace(firstTimeUserRegex, "");
|
|
3479
4008
|
}
|
|
4009
|
+
const interactiveModeRegex = /\{\{#IF INTERACTIVE_MODE\}\}(.*?)\{\{\/IF INTERACTIVE_MODE\}\}/gs;
|
|
4010
|
+
if (variables.INTERACTIVE_MODE === true) {
|
|
4011
|
+
result = result.replace(interactiveModeRegex, "$1");
|
|
4012
|
+
} else {
|
|
4013
|
+
result = result.replace(interactiveModeRegex, "");
|
|
4014
|
+
}
|
|
4015
|
+
const compactSummariesRegex = /\{\{#IF COMPACT_SUMMARIES\}\}(.*?)\{\{\/IF COMPACT_SUMMARIES\}\}/gs;
|
|
4016
|
+
if (variables.COMPACT_SUMMARIES !== void 0 && variables.COMPACT_SUMMARIES !== "") {
|
|
4017
|
+
result = result.replace(compactSummariesRegex, "$1");
|
|
4018
|
+
} else {
|
|
4019
|
+
result = result.replace(compactSummariesRegex, "");
|
|
4020
|
+
}
|
|
3480
4021
|
return result;
|
|
3481
4022
|
}
|
|
3482
4023
|
/**
|
|
@@ -3502,15 +4043,6 @@ var ClaudeService = class {
|
|
|
3502
4043
|
async isAvailable() {
|
|
3503
4044
|
return detectClaudeCli();
|
|
3504
4045
|
}
|
|
3505
|
-
/**
|
|
3506
|
-
* Get the appropriate model for a workflow type
|
|
3507
|
-
*/
|
|
3508
|
-
getModelForWorkflow(type) {
|
|
3509
|
-
if (type === "issue") {
|
|
3510
|
-
return "claude-sonnet-4-20250514";
|
|
3511
|
-
}
|
|
3512
|
-
return void 0;
|
|
3513
|
-
}
|
|
3514
4046
|
/**
|
|
3515
4047
|
* Get the appropriate permission mode for a workflow type
|
|
3516
4048
|
*/
|
|
@@ -3554,7 +4086,6 @@ var ClaudeService = class {
|
|
|
3554
4086
|
variables.PORT = port;
|
|
3555
4087
|
}
|
|
3556
4088
|
const prompt = await this.templateManager.getPrompt(type, variables);
|
|
3557
|
-
const model = this.getModelForWorkflow(type);
|
|
3558
4089
|
const permissionMode = this.getPermissionModeForWorkflow(type);
|
|
3559
4090
|
if (permissionMode === "bypassPermissions") {
|
|
3560
4091
|
logger.warn(
|
|
@@ -3565,9 +4096,6 @@ var ClaudeService = class {
|
|
|
3565
4096
|
addDir: workspacePath,
|
|
3566
4097
|
headless
|
|
3567
4098
|
};
|
|
3568
|
-
if (model !== void 0) {
|
|
3569
|
-
claudeOptions.model = model;
|
|
3570
|
-
}
|
|
3571
4099
|
if (permissionMode !== void 0 && permissionMode !== "default") {
|
|
3572
4100
|
claudeOptions.permissionMode = permissionMode;
|
|
3573
4101
|
}
|
|
@@ -3585,7 +4113,6 @@ var ClaudeService = class {
|
|
|
3585
4113
|
}
|
|
3586
4114
|
logger.debug("Launching Claude for workflow", {
|
|
3587
4115
|
type,
|
|
3588
|
-
model,
|
|
3589
4116
|
permissionMode,
|
|
3590
4117
|
headless,
|
|
3591
4118
|
workspacePath
|
|
@@ -3987,7 +4514,9 @@ export {
|
|
|
3987
4514
|
UserAbortedCommitError,
|
|
3988
4515
|
WorkspaceManager,
|
|
3989
4516
|
branchExists,
|
|
4517
|
+
checkRemoteBranchStatus,
|
|
3990
4518
|
createLogger,
|
|
4519
|
+
createStderrLogger,
|
|
3991
4520
|
ensureRepositoryHasCommits,
|
|
3992
4521
|
executeGitCommand,
|
|
3993
4522
|
extractIssueNumber,
|
|
@@ -3995,16 +4524,20 @@ export {
|
|
|
3995
4524
|
findAllBranchesForIssue,
|
|
3996
4525
|
findMainWorktreePath,
|
|
3997
4526
|
findMainWorktreePathWithSettings,
|
|
4527
|
+
findWorktreeForBranch,
|
|
3998
4528
|
generateWorktreePath,
|
|
3999
4529
|
getCurrentBranch,
|
|
4000
4530
|
getDefaultBranch,
|
|
4531
|
+
getMergeTargetBranch,
|
|
4001
4532
|
getRepoRoot,
|
|
4002
4533
|
getWorktreeRoot,
|
|
4003
4534
|
hasUncommittedChanges,
|
|
4535
|
+
isBranchMergedIntoMain,
|
|
4004
4536
|
isEmptyRepository,
|
|
4005
4537
|
isFileGitignored,
|
|
4006
4538
|
isFileTrackedByGit,
|
|
4007
4539
|
isPRBranch,
|
|
4540
|
+
isRemoteBranchUpToDate,
|
|
4008
4541
|
isValidGitRepo,
|
|
4009
4542
|
isWorktreePath,
|
|
4010
4543
|
logger,
|