@iloom/cli 0.1.14
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/LICENSE +33 -0
- package/README.md +711 -0
- package/dist/ClaudeContextManager-XOSXQ67R.js +13 -0
- package/dist/ClaudeContextManager-XOSXQ67R.js.map +1 -0
- package/dist/ClaudeService-YSZ6EXWP.js +12 -0
- package/dist/ClaudeService-YSZ6EXWP.js.map +1 -0
- package/dist/GitHubService-F7Z3XJOS.js +11 -0
- package/dist/GitHubService-F7Z3XJOS.js.map +1 -0
- package/dist/LoomLauncher-MODG2SEM.js +263 -0
- package/dist/LoomLauncher-MODG2SEM.js.map +1 -0
- package/dist/NeonProvider-PAGPUH7F.js +12 -0
- package/dist/NeonProvider-PAGPUH7F.js.map +1 -0
- package/dist/PromptTemplateManager-7FINLRDE.js +9 -0
- package/dist/PromptTemplateManager-7FINLRDE.js.map +1 -0
- package/dist/SettingsManager-VAZF26S2.js +19 -0
- package/dist/SettingsManager-VAZF26S2.js.map +1 -0
- package/dist/SettingsMigrationManager-MTQIMI54.js +146 -0
- package/dist/SettingsMigrationManager-MTQIMI54.js.map +1 -0
- package/dist/add-issue-22JBNOML.js +54 -0
- package/dist/add-issue-22JBNOML.js.map +1 -0
- package/dist/agents/iloom-issue-analyze-and-plan.md +580 -0
- package/dist/agents/iloom-issue-analyzer.md +290 -0
- package/dist/agents/iloom-issue-complexity-evaluator.md +224 -0
- package/dist/agents/iloom-issue-enhancer.md +266 -0
- package/dist/agents/iloom-issue-implementer.md +262 -0
- package/dist/agents/iloom-issue-planner.md +358 -0
- package/dist/agents/iloom-issue-reviewer.md +63 -0
- package/dist/chunk-2ZPFJQ3B.js +63 -0
- package/dist/chunk-2ZPFJQ3B.js.map +1 -0
- package/dist/chunk-37DYYFVK.js +29 -0
- package/dist/chunk-37DYYFVK.js.map +1 -0
- package/dist/chunk-BLCTGFZN.js +121 -0
- package/dist/chunk-BLCTGFZN.js.map +1 -0
- package/dist/chunk-CP2NU2JC.js +545 -0
- package/dist/chunk-CP2NU2JC.js.map +1 -0
- package/dist/chunk-CWR2SANQ.js +39 -0
- package/dist/chunk-CWR2SANQ.js.map +1 -0
- package/dist/chunk-F3XBU2R7.js +110 -0
- package/dist/chunk-F3XBU2R7.js.map +1 -0
- package/dist/chunk-GEHQXLEI.js +130 -0
- package/dist/chunk-GEHQXLEI.js.map +1 -0
- package/dist/chunk-GYCR2LOU.js +143 -0
- package/dist/chunk-GYCR2LOU.js.map +1 -0
- package/dist/chunk-GZP4UGGM.js +48 -0
- package/dist/chunk-GZP4UGGM.js.map +1 -0
- package/dist/chunk-H4E4THUZ.js +55 -0
- package/dist/chunk-H4E4THUZ.js.map +1 -0
- package/dist/chunk-HPJJSYNS.js +644 -0
- package/dist/chunk-HPJJSYNS.js.map +1 -0
- package/dist/chunk-JBH2ZYYZ.js +220 -0
- package/dist/chunk-JBH2ZYYZ.js.map +1 -0
- package/dist/chunk-JNKJ7NJV.js +78 -0
- package/dist/chunk-JNKJ7NJV.js.map +1 -0
- package/dist/chunk-JQ7VOSTC.js +437 -0
- package/dist/chunk-JQ7VOSTC.js.map +1 -0
- package/dist/chunk-KQDEK2ZW.js +199 -0
- package/dist/chunk-KQDEK2ZW.js.map +1 -0
- package/dist/chunk-O2QWO64Z.js +179 -0
- package/dist/chunk-O2QWO64Z.js.map +1 -0
- package/dist/chunk-OC4H6HJD.js +248 -0
- package/dist/chunk-OC4H6HJD.js.map +1 -0
- package/dist/chunk-PR7FKQBG.js +120 -0
- package/dist/chunk-PR7FKQBG.js.map +1 -0
- package/dist/chunk-PXZBAC2M.js +250 -0
- package/dist/chunk-PXZBAC2M.js.map +1 -0
- package/dist/chunk-QEPVTTHD.js +383 -0
- package/dist/chunk-QEPVTTHD.js.map +1 -0
- package/dist/chunk-RSRO7564.js +203 -0
- package/dist/chunk-RSRO7564.js.map +1 -0
- package/dist/chunk-SJUQ2NDR.js +146 -0
- package/dist/chunk-SJUQ2NDR.js.map +1 -0
- package/dist/chunk-SPYPLHMK.js +177 -0
- package/dist/chunk-SPYPLHMK.js.map +1 -0
- package/dist/chunk-SSCQCCJ7.js +75 -0
- package/dist/chunk-SSCQCCJ7.js.map +1 -0
- package/dist/chunk-SSR5AVRJ.js +41 -0
- package/dist/chunk-SSR5AVRJ.js.map +1 -0
- package/dist/chunk-T7QPXANZ.js +315 -0
- package/dist/chunk-T7QPXANZ.js.map +1 -0
- package/dist/chunk-U3WU5OWO.js +203 -0
- package/dist/chunk-U3WU5OWO.js.map +1 -0
- package/dist/chunk-W3DQTW63.js +124 -0
- package/dist/chunk-W3DQTW63.js.map +1 -0
- package/dist/chunk-WKEWRSDB.js +151 -0
- package/dist/chunk-WKEWRSDB.js.map +1 -0
- package/dist/chunk-Y7SAGNUT.js +66 -0
- package/dist/chunk-Y7SAGNUT.js.map +1 -0
- package/dist/chunk-YETJNRQM.js +39 -0
- package/dist/chunk-YETJNRQM.js.map +1 -0
- package/dist/chunk-YYSKGAZT.js +384 -0
- package/dist/chunk-YYSKGAZT.js.map +1 -0
- package/dist/chunk-ZZZWQGTS.js +169 -0
- package/dist/chunk-ZZZWQGTS.js.map +1 -0
- package/dist/claude-7LUVDZZ4.js +17 -0
- package/dist/claude-7LUVDZZ4.js.map +1 -0
- package/dist/cleanup-3LUWPSM7.js +412 -0
- package/dist/cleanup-3LUWPSM7.js.map +1 -0
- package/dist/cli-overrides-XFZWY7CM.js +16 -0
- package/dist/cli-overrides-XFZWY7CM.js.map +1 -0
- package/dist/cli.js +603 -0
- package/dist/cli.js.map +1 -0
- package/dist/color-ZVALX37U.js +21 -0
- package/dist/color-ZVALX37U.js.map +1 -0
- package/dist/enhance-XJIQHVPD.js +166 -0
- package/dist/enhance-XJIQHVPD.js.map +1 -0
- package/dist/env-MDFL4ZXL.js +23 -0
- package/dist/env-MDFL4ZXL.js.map +1 -0
- package/dist/feedback-23CLXKFT.js +158 -0
- package/dist/feedback-23CLXKFT.js.map +1 -0
- package/dist/finish-CY4CIH6O.js +1608 -0
- package/dist/finish-CY4CIH6O.js.map +1 -0
- package/dist/git-LVRZ57GJ.js +43 -0
- package/dist/git-LVRZ57GJ.js.map +1 -0
- package/dist/ignite-WXEF2ID5.js +359 -0
- package/dist/ignite-WXEF2ID5.js.map +1 -0
- package/dist/index.d.ts +1341 -0
- package/dist/index.js +3058 -0
- package/dist/index.js.map +1 -0
- package/dist/init-RHACUR4E.js +123 -0
- package/dist/init-RHACUR4E.js.map +1 -0
- package/dist/installation-detector-VARGFFRZ.js +11 -0
- package/dist/installation-detector-VARGFFRZ.js.map +1 -0
- package/dist/logger-MKYH4UDV.js +12 -0
- package/dist/logger-MKYH4UDV.js.map +1 -0
- package/dist/mcp/chunk-6SDFJ42P.js +62 -0
- package/dist/mcp/chunk-6SDFJ42P.js.map +1 -0
- package/dist/mcp/claude-YHHHLSXH.js +249 -0
- package/dist/mcp/claude-YHHHLSXH.js.map +1 -0
- package/dist/mcp/color-QS5BFCNN.js +168 -0
- package/dist/mcp/color-QS5BFCNN.js.map +1 -0
- package/dist/mcp/github-comment-server.js +165 -0
- package/dist/mcp/github-comment-server.js.map +1 -0
- package/dist/mcp/terminal-SDCMDVD7.js +202 -0
- package/dist/mcp/terminal-SDCMDVD7.js.map +1 -0
- package/dist/open-X6BTENPV.js +278 -0
- package/dist/open-X6BTENPV.js.map +1 -0
- package/dist/prompt-ANTQWHUF.js +13 -0
- package/dist/prompt-ANTQWHUF.js.map +1 -0
- package/dist/prompts/issue-prompt.txt +230 -0
- package/dist/prompts/pr-prompt.txt +35 -0
- package/dist/prompts/regular-prompt.txt +14 -0
- package/dist/run-2JCPQAX3.js +278 -0
- package/dist/run-2JCPQAX3.js.map +1 -0
- package/dist/schema/settings.schema.json +221 -0
- package/dist/start-LWVRBJ6S.js +982 -0
- package/dist/start-LWVRBJ6S.js.map +1 -0
- package/dist/terminal-3D6TUAKJ.js +16 -0
- package/dist/terminal-3D6TUAKJ.js.map +1 -0
- package/dist/test-git-XPF4SZXJ.js +52 -0
- package/dist/test-git-XPF4SZXJ.js.map +1 -0
- package/dist/test-prefix-XGFXFAYN.js +68 -0
- package/dist/test-prefix-XGFXFAYN.js.map +1 -0
- package/dist/test-tabs-JRKY3QMM.js +69 -0
- package/dist/test-tabs-JRKY3QMM.js.map +1 -0
- package/dist/test-webserver-M2I3EV4J.js +62 -0
- package/dist/test-webserver-M2I3EV4J.js.map +1 -0
- package/dist/update-3ZT2XX2G.js +79 -0
- package/dist/update-3ZT2XX2G.js.map +1 -0
- package/dist/update-notifier-QSSEB5KC.js +11 -0
- package/dist/update-notifier-QSSEB5KC.js.map +1 -0
- package/package.json +113 -0
|
@@ -0,0 +1,982 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
extractSettingsOverrides
|
|
4
|
+
} from "./chunk-GYCR2LOU.js";
|
|
5
|
+
import {
|
|
6
|
+
IssueEnhancementService
|
|
7
|
+
} from "./chunk-PR7FKQBG.js";
|
|
8
|
+
import {
|
|
9
|
+
AgentManager
|
|
10
|
+
} from "./chunk-OC4H6HJD.js";
|
|
11
|
+
import "./chunk-YETJNRQM.js";
|
|
12
|
+
import {
|
|
13
|
+
CLIIsolationManager,
|
|
14
|
+
DatabaseManager,
|
|
15
|
+
EnvironmentManager
|
|
16
|
+
} from "./chunk-HPJJSYNS.js";
|
|
17
|
+
import "./chunk-37DYYFVK.js";
|
|
18
|
+
import {
|
|
19
|
+
installDependencies
|
|
20
|
+
} from "./chunk-BLCTGFZN.js";
|
|
21
|
+
import {
|
|
22
|
+
NeonProvider
|
|
23
|
+
} from "./chunk-YYSKGAZT.js";
|
|
24
|
+
import {
|
|
25
|
+
ProjectCapabilityDetector
|
|
26
|
+
} from "./chunk-CWR2SANQ.js";
|
|
27
|
+
import "./chunk-2ZPFJQ3B.js";
|
|
28
|
+
import {
|
|
29
|
+
calculateForegroundColor,
|
|
30
|
+
generateColorFromBranchName,
|
|
31
|
+
hexToRgb,
|
|
32
|
+
lightenColor,
|
|
33
|
+
rgbToHex
|
|
34
|
+
} from "./chunk-ZZZWQGTS.js";
|
|
35
|
+
import {
|
|
36
|
+
GitHubService
|
|
37
|
+
} from "./chunk-T7QPXANZ.js";
|
|
38
|
+
import "./chunk-KQDEK2ZW.js";
|
|
39
|
+
import {
|
|
40
|
+
loadEnvIntoProcess
|
|
41
|
+
} from "./chunk-SJUQ2NDR.js";
|
|
42
|
+
import {
|
|
43
|
+
ClaudeContextManager
|
|
44
|
+
} from "./chunk-Y7SAGNUT.js";
|
|
45
|
+
import "./chunk-WKEWRSDB.js";
|
|
46
|
+
import "./chunk-PXZBAC2M.js";
|
|
47
|
+
import "./chunk-F3XBU2R7.js";
|
|
48
|
+
import {
|
|
49
|
+
GitWorktreeManager
|
|
50
|
+
} from "./chunk-QEPVTTHD.js";
|
|
51
|
+
import {
|
|
52
|
+
SettingsManager
|
|
53
|
+
} from "./chunk-JBH2ZYYZ.js";
|
|
54
|
+
import {
|
|
55
|
+
branchExists,
|
|
56
|
+
ensureRepositoryHasCommits,
|
|
57
|
+
executeGitCommand,
|
|
58
|
+
findMainWorktreePathWithSettings
|
|
59
|
+
} from "./chunk-JQ7VOSTC.js";
|
|
60
|
+
import "./chunk-JNKJ7NJV.js";
|
|
61
|
+
import {
|
|
62
|
+
logger
|
|
63
|
+
} from "./chunk-GEHQXLEI.js";
|
|
64
|
+
|
|
65
|
+
// src/lib/LoomManager.ts
|
|
66
|
+
import path2 from "path";
|
|
67
|
+
import fs2 from "fs-extra";
|
|
68
|
+
|
|
69
|
+
// src/lib/VSCodeIntegration.ts
|
|
70
|
+
import fs from "fs-extra";
|
|
71
|
+
import path from "path";
|
|
72
|
+
import { parse, modify, applyEdits } from "jsonc-parser";
|
|
73
|
+
var VSCodeIntegration = class {
|
|
74
|
+
/**
|
|
75
|
+
* Set VSCode title bar color for a workspace
|
|
76
|
+
*
|
|
77
|
+
* @param workspacePath - Path to workspace directory
|
|
78
|
+
* @param hexColor - Hex color string (e.g., "#dcebf8")
|
|
79
|
+
*/
|
|
80
|
+
async setTitleBarColor(workspacePath, hexColor) {
|
|
81
|
+
const vscodeDir = path.join(workspacePath, ".vscode");
|
|
82
|
+
const settingsPath = path.join(vscodeDir, "settings.json");
|
|
83
|
+
try {
|
|
84
|
+
await fs.ensureDir(vscodeDir);
|
|
85
|
+
const settings = await this.readSettings(settingsPath);
|
|
86
|
+
const updatedSettings = this.mergeColorSettings(settings, hexColor);
|
|
87
|
+
await this.writeSettings(settingsPath, updatedSettings);
|
|
88
|
+
logger.debug(`Set VSCode title bar color to ${hexColor} for ${workspacePath}`);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
`Failed to set VSCode title bar color: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Read VSCode settings from file
|
|
97
|
+
* Supports JSONC (JSON with Comments)
|
|
98
|
+
*
|
|
99
|
+
* @param settingsPath - Path to settings.json file
|
|
100
|
+
* @returns Parsed settings object
|
|
101
|
+
*/
|
|
102
|
+
async readSettings(settingsPath) {
|
|
103
|
+
try {
|
|
104
|
+
if (!await fs.pathExists(settingsPath)) {
|
|
105
|
+
return {};
|
|
106
|
+
}
|
|
107
|
+
const content = await fs.readFile(settingsPath, "utf8");
|
|
108
|
+
const errors = [];
|
|
109
|
+
const settings = parse(content, errors, { allowTrailingComma: true });
|
|
110
|
+
if (errors.length > 0) {
|
|
111
|
+
const firstError = errors[0];
|
|
112
|
+
throw new Error(`Invalid JSON: ${firstError ? firstError.error : "Unknown parse error"}`);
|
|
113
|
+
}
|
|
114
|
+
return settings ?? {};
|
|
115
|
+
} catch (error) {
|
|
116
|
+
throw new Error(
|
|
117
|
+
`Failed to parse settings.json: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Write VSCode settings to file atomically
|
|
123
|
+
* Preserves comments if present (using JSONC parser)
|
|
124
|
+
*
|
|
125
|
+
* @param settingsPath - Path to settings.json file
|
|
126
|
+
* @param settings - Settings object to write
|
|
127
|
+
*/
|
|
128
|
+
async writeSettings(settingsPath, settings) {
|
|
129
|
+
try {
|
|
130
|
+
let content;
|
|
131
|
+
if (await fs.pathExists(settingsPath)) {
|
|
132
|
+
const existingContent = await fs.readFile(settingsPath, "utf8");
|
|
133
|
+
if (existingContent.includes("//") || existingContent.includes("/*")) {
|
|
134
|
+
content = await this.modifyWithCommentsPreserved(existingContent, settings);
|
|
135
|
+
} else {
|
|
136
|
+
content = JSON.stringify(settings, null, 2) + "\n";
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
content = JSON.stringify(settings, null, 2) + "\n";
|
|
140
|
+
}
|
|
141
|
+
const tempPath = `${settingsPath}.tmp`;
|
|
142
|
+
await fs.writeFile(tempPath, content, "utf8");
|
|
143
|
+
await fs.rename(tempPath, settingsPath);
|
|
144
|
+
} catch (error) {
|
|
145
|
+
throw new Error(
|
|
146
|
+
`Failed to write settings.json: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Modify JSONC content while preserving comments
|
|
152
|
+
*
|
|
153
|
+
* @param existingContent - Original JSONC content
|
|
154
|
+
* @param newSettings - New settings to apply
|
|
155
|
+
* @returns Modified JSONC content with comments preserved
|
|
156
|
+
*/
|
|
157
|
+
async modifyWithCommentsPreserved(existingContent, newSettings) {
|
|
158
|
+
let modifiedContent = existingContent;
|
|
159
|
+
for (const [key, value] of Object.entries(newSettings)) {
|
|
160
|
+
const edits = modify(modifiedContent, [key], value, {});
|
|
161
|
+
modifiedContent = applyEdits(modifiedContent, edits);
|
|
162
|
+
}
|
|
163
|
+
return modifiedContent;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Merge color settings into existing settings object
|
|
167
|
+
*
|
|
168
|
+
* @param existing - Existing settings object
|
|
169
|
+
* @param hexColor - Hex color to apply (subtle palette color)
|
|
170
|
+
* @returns Updated settings object with color merged
|
|
171
|
+
*/
|
|
172
|
+
mergeColorSettings(existing, hexColor) {
|
|
173
|
+
const updated = { ...existing };
|
|
174
|
+
updated["workbench.colorCustomizations"] ??= {};
|
|
175
|
+
const colors = updated["workbench.colorCustomizations"];
|
|
176
|
+
const baseRgb = hexToRgb(hexColor);
|
|
177
|
+
const foreground = calculateForegroundColor(baseRgb);
|
|
178
|
+
const foregroundTransparent = foreground.replace("#", "#") + "99";
|
|
179
|
+
const lighterRgb = lightenColor(baseRgb, 0.05);
|
|
180
|
+
const lighterHex = rgbToHex(lighterRgb.r, lighterRgb.g, lighterRgb.b);
|
|
181
|
+
colors["titleBar.activeBackground"] = hexColor;
|
|
182
|
+
colors["titleBar.inactiveBackground"] = hexColor + "99";
|
|
183
|
+
colors["titleBar.activeForeground"] = foreground;
|
|
184
|
+
colors["titleBar.inactiveForeground"] = foregroundTransparent;
|
|
185
|
+
colors["statusBar.background"] = hexColor;
|
|
186
|
+
colors["statusBar.foreground"] = foreground;
|
|
187
|
+
colors["statusBarItem.hoverBackground"] = lighterHex;
|
|
188
|
+
colors["statusBarItem.remoteBackground"] = hexColor;
|
|
189
|
+
colors["statusBarItem.remoteForeground"] = foreground;
|
|
190
|
+
colors["sash.hoverBorder"] = hexColor;
|
|
191
|
+
colors["commandCenter.border"] = foregroundTransparent;
|
|
192
|
+
return updated;
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
// src/lib/LoomManager.ts
|
|
197
|
+
var LoomManager = class {
|
|
198
|
+
constructor(gitWorktree, github, environment, _claude, capabilityDetector, cliIsolation, settings, database) {
|
|
199
|
+
this.gitWorktree = gitWorktree;
|
|
200
|
+
this.github = github;
|
|
201
|
+
this.environment = environment;
|
|
202
|
+
this.capabilityDetector = capabilityDetector;
|
|
203
|
+
this.cliIsolation = cliIsolation;
|
|
204
|
+
this.settings = settings;
|
|
205
|
+
this.database = database;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Create a new loom (isolated workspace)
|
|
209
|
+
* Orchestrates worktree creation, environment setup, and Claude context generation
|
|
210
|
+
* NEW: Checks for existing worktrees and reuses them if found
|
|
211
|
+
*/
|
|
212
|
+
async createIloom(input) {
|
|
213
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
214
|
+
logger.info("Fetching GitHub data...");
|
|
215
|
+
const githubData = await this.fetchGitHubData(input);
|
|
216
|
+
if (input.type === "issue" || input.type === "pr") {
|
|
217
|
+
logger.info("Checking for existing worktree...");
|
|
218
|
+
const existing = await this.findExistingIloom(input, githubData);
|
|
219
|
+
if (existing) {
|
|
220
|
+
logger.success(`Found existing worktree, reusing: ${existing.path}`);
|
|
221
|
+
return await this.reuseIloom(existing, input, githubData);
|
|
222
|
+
}
|
|
223
|
+
logger.info("No existing worktree found, creating new one...");
|
|
224
|
+
}
|
|
225
|
+
logger.info("Preparing branch name...");
|
|
226
|
+
const branchName = await this.prepareBranchName(input, githubData);
|
|
227
|
+
logger.info("Creating git worktree...");
|
|
228
|
+
const worktreePath = await this.createWorktreeOnly(input, branchName);
|
|
229
|
+
this.loadMainEnvFile();
|
|
230
|
+
const { capabilities, binEntries } = await this.capabilityDetector.detectCapabilities(worktreePath);
|
|
231
|
+
await this.copyEnvironmentFiles(worktreePath);
|
|
232
|
+
await this.copyIloomSettings(worktreePath);
|
|
233
|
+
const settingsData = await this.settings.loadSettings();
|
|
234
|
+
const basePort = ((_b = (_a = settingsData.capabilities) == null ? void 0 : _a.web) == null ? void 0 : _b.basePort) ?? 3e3;
|
|
235
|
+
let port = basePort;
|
|
236
|
+
if (capabilities.includes("web")) {
|
|
237
|
+
port = await this.setupPortForWeb(worktreePath, input, basePort);
|
|
238
|
+
}
|
|
239
|
+
try {
|
|
240
|
+
await installDependencies(worktreePath, true);
|
|
241
|
+
} catch (error) {
|
|
242
|
+
logger.warn(`Failed to install dependencies: ${error instanceof Error ? error.message : "Unknown error"}`, error);
|
|
243
|
+
}
|
|
244
|
+
let databaseBranch = void 0;
|
|
245
|
+
if (this.database && !((_c = input.options) == null ? void 0 : _c.skipDatabase)) {
|
|
246
|
+
try {
|
|
247
|
+
const connectionString = await this.database.createBranchIfConfigured(
|
|
248
|
+
branchName,
|
|
249
|
+
path2.join(worktreePath, ".env")
|
|
250
|
+
);
|
|
251
|
+
if (connectionString) {
|
|
252
|
+
await this.environment.setEnvVar(
|
|
253
|
+
path2.join(worktreePath, ".env"),
|
|
254
|
+
this.database.getConfiguredVariableName(),
|
|
255
|
+
connectionString
|
|
256
|
+
);
|
|
257
|
+
logger.success("Database branch configured");
|
|
258
|
+
databaseBranch = branchName;
|
|
259
|
+
}
|
|
260
|
+
} catch (error) {
|
|
261
|
+
logger.error(
|
|
262
|
+
`Failed to setup database branch: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
263
|
+
);
|
|
264
|
+
throw error;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
let cliSymlinks = void 0;
|
|
268
|
+
if (capabilities.includes("cli")) {
|
|
269
|
+
try {
|
|
270
|
+
cliSymlinks = await this.cliIsolation.setupCLIIsolation(
|
|
271
|
+
worktreePath,
|
|
272
|
+
input.identifier,
|
|
273
|
+
binEntries
|
|
274
|
+
);
|
|
275
|
+
} catch (error) {
|
|
276
|
+
logger.warn(
|
|
277
|
+
`Failed to setup CLI isolation: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
278
|
+
error
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (!((_d = input.options) == null ? void 0 : _d.skipColorSync)) {
|
|
283
|
+
try {
|
|
284
|
+
await this.applyColorSynchronization(worktreePath, branchName);
|
|
285
|
+
} catch (error) {
|
|
286
|
+
logger.warn(
|
|
287
|
+
`Failed to apply color synchronization: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
288
|
+
error
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
if (input.type === "issue") {
|
|
293
|
+
try {
|
|
294
|
+
logger.info("Moving issue to In Progress...");
|
|
295
|
+
await this.github.moveIssueToInProgress(input.identifier);
|
|
296
|
+
} catch (error) {
|
|
297
|
+
logger.warn(
|
|
298
|
+
`Failed to move issue to In Progress: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
299
|
+
error
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
const enableClaude = ((_e = input.options) == null ? void 0 : _e.enableClaude) !== false;
|
|
304
|
+
const enableCode = ((_f = input.options) == null ? void 0 : _f.enableCode) !== false;
|
|
305
|
+
const enableDevServer = ((_g = input.options) == null ? void 0 : _g.enableDevServer) !== false;
|
|
306
|
+
const enableTerminal = ((_h = input.options) == null ? void 0 : _h.enableTerminal) ?? false;
|
|
307
|
+
const oneShot = ((_i = input.options) == null ? void 0 : _i.oneShot) ?? "default";
|
|
308
|
+
const setArguments = (_j = input.options) == null ? void 0 : _j.setArguments;
|
|
309
|
+
const executablePath = (_k = input.options) == null ? void 0 : _k.executablePath;
|
|
310
|
+
if (enableClaude || enableCode || enableDevServer || enableTerminal) {
|
|
311
|
+
const { LoomLauncher } = await import("./LoomLauncher-MODG2SEM.js");
|
|
312
|
+
const { ClaudeContextManager: ClaudeContextManager2 } = await import("./ClaudeContextManager-XOSXQ67R.js");
|
|
313
|
+
const claudeContext = new ClaudeContextManager2(void 0, void 0, this.settings);
|
|
314
|
+
const launcher = new LoomLauncher(claudeContext);
|
|
315
|
+
await launcher.launchLoom({
|
|
316
|
+
enableClaude,
|
|
317
|
+
enableCode,
|
|
318
|
+
enableDevServer,
|
|
319
|
+
enableTerminal,
|
|
320
|
+
worktreePath,
|
|
321
|
+
branchName,
|
|
322
|
+
port,
|
|
323
|
+
capabilities,
|
|
324
|
+
workflowType: input.type === "branch" ? "regular" : input.type,
|
|
325
|
+
identifier: input.identifier,
|
|
326
|
+
...(githubData == null ? void 0 : githubData.title) && { title: githubData.title },
|
|
327
|
+
oneShot,
|
|
328
|
+
...setArguments && { setArguments },
|
|
329
|
+
...executablePath && { executablePath }
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
const loom = {
|
|
333
|
+
id: this.generateLoomId(input),
|
|
334
|
+
path: worktreePath,
|
|
335
|
+
branch: branchName,
|
|
336
|
+
type: input.type,
|
|
337
|
+
identifier: input.identifier,
|
|
338
|
+
port,
|
|
339
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
340
|
+
lastAccessed: /* @__PURE__ */ new Date(),
|
|
341
|
+
...databaseBranch !== void 0 && { databaseBranch },
|
|
342
|
+
...capabilities.length > 0 && { capabilities },
|
|
343
|
+
...Object.keys(binEntries).length > 0 && { binEntries },
|
|
344
|
+
...cliSymlinks && cliSymlinks.length > 0 && { cliSymlinks },
|
|
345
|
+
...githubData !== null && {
|
|
346
|
+
githubData: {
|
|
347
|
+
title: githubData.title,
|
|
348
|
+
body: githubData.body,
|
|
349
|
+
url: githubData.url,
|
|
350
|
+
state: githubData.state
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
logger.success(`Created loom: ${loom.id} at ${loom.path}`);
|
|
355
|
+
return loom;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Finish a loom (merge work and cleanup)
|
|
359
|
+
* Not yet implemented - see Issue #7
|
|
360
|
+
*/
|
|
361
|
+
async finishIloom(_identifier) {
|
|
362
|
+
throw new Error("Not implemented - see Issue #7");
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* List all active looms
|
|
366
|
+
*/
|
|
367
|
+
async listLooms() {
|
|
368
|
+
const worktrees = await this.gitWorktree.listWorktrees();
|
|
369
|
+
return await this.mapWorktreesToLooms(worktrees);
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Find a specific loom by identifier
|
|
373
|
+
*/
|
|
374
|
+
async findIloom(identifier) {
|
|
375
|
+
const looms = await this.listLooms();
|
|
376
|
+
return looms.find(
|
|
377
|
+
(h) => h.id === identifier || h.identifier.toString() === identifier || h.branch === identifier
|
|
378
|
+
) ?? null;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Fetch GitHub data based on input type
|
|
382
|
+
*/
|
|
383
|
+
async fetchGitHubData(input) {
|
|
384
|
+
if (input.type === "issue") {
|
|
385
|
+
return await this.github.fetchIssue(input.identifier);
|
|
386
|
+
} else if (input.type === "pr") {
|
|
387
|
+
return await this.github.fetchPR(input.identifier);
|
|
388
|
+
}
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Prepare branch name based on input type and GitHub data
|
|
393
|
+
*/
|
|
394
|
+
async prepareBranchName(input, githubData) {
|
|
395
|
+
if (input.type === "branch") {
|
|
396
|
+
return input.identifier;
|
|
397
|
+
}
|
|
398
|
+
if (input.type === "pr" && githubData && "branch" in githubData) {
|
|
399
|
+
return githubData.branch;
|
|
400
|
+
}
|
|
401
|
+
if (input.type === "issue" && githubData) {
|
|
402
|
+
const branchName = await this.github.generateBranchName({
|
|
403
|
+
issueNumber: input.identifier,
|
|
404
|
+
title: githubData.title
|
|
405
|
+
});
|
|
406
|
+
return branchName;
|
|
407
|
+
}
|
|
408
|
+
if (input.type === "pr") {
|
|
409
|
+
return `pr-${input.identifier}`;
|
|
410
|
+
}
|
|
411
|
+
throw new Error(`Unable to determine branch name for input type: ${input.type}`);
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Create worktree for the loom (without dependency installation)
|
|
415
|
+
*/
|
|
416
|
+
async createWorktreeOnly(input, branchName) {
|
|
417
|
+
logger.info("Ensuring repository has initial commit...");
|
|
418
|
+
await ensureRepositoryHasCommits(this.gitWorktree.workingDirectory);
|
|
419
|
+
const settingsData = await this.settings.loadSettings();
|
|
420
|
+
const worktreePrefix = settingsData.worktreePrefix;
|
|
421
|
+
const pathOptions = input.type === "pr" ? { isPR: true, prNumber: input.identifier } : {};
|
|
422
|
+
if (worktreePrefix !== void 0) {
|
|
423
|
+
pathOptions.prefix = worktreePrefix;
|
|
424
|
+
}
|
|
425
|
+
const worktreePath = this.gitWorktree.generateWorktreePath(
|
|
426
|
+
branchName,
|
|
427
|
+
void 0,
|
|
428
|
+
pathOptions
|
|
429
|
+
);
|
|
430
|
+
if (input.type === "pr") {
|
|
431
|
+
logger.info("Fetching all remote branches...");
|
|
432
|
+
try {
|
|
433
|
+
await executeGitCommand(["fetch", "origin"], { cwd: this.gitWorktree.workingDirectory });
|
|
434
|
+
logger.success("Successfully fetched from remote");
|
|
435
|
+
} catch (error) {
|
|
436
|
+
throw new Error(
|
|
437
|
+
`Failed to fetch from remote: ${error instanceof Error ? error.message : "Unknown error"}. Make sure you have access to the repository.`
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
const branchExistedLocally = await branchExists(branchName);
|
|
442
|
+
if (input.type !== "pr" && branchExistedLocally) {
|
|
443
|
+
throw new Error(
|
|
444
|
+
`Cannot create worktree: branch '${branchName}' already exists. Use 'git branch -D ${branchName}' to delete it first if needed.`
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
await this.gitWorktree.createWorktree({
|
|
448
|
+
path: worktreePath,
|
|
449
|
+
branch: branchName,
|
|
450
|
+
createBranch: input.type !== "pr",
|
|
451
|
+
// PRs use existing branches
|
|
452
|
+
...input.baseBranch && { baseBranch: input.baseBranch }
|
|
453
|
+
});
|
|
454
|
+
if (input.type === "pr" && !branchExistedLocally) {
|
|
455
|
+
logger.info("Resetting new PR branch to match remote exactly...");
|
|
456
|
+
try {
|
|
457
|
+
await executeGitCommand(["reset", "--hard", `origin/${branchName}`], { cwd: worktreePath });
|
|
458
|
+
await executeGitCommand(["branch", "--set-upstream-to", `origin/${branchName}`], { cwd: worktreePath });
|
|
459
|
+
logger.success("Successfully reset to match remote");
|
|
460
|
+
} catch (error) {
|
|
461
|
+
logger.warn(`Failed to reset to match remote: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return worktreePath;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Copy user application environment files (.env) from main repo to worktree
|
|
468
|
+
* Always called regardless of project capabilities
|
|
469
|
+
*/
|
|
470
|
+
async copyEnvironmentFiles(worktreePath) {
|
|
471
|
+
const envFilePath = path2.join(worktreePath, ".env");
|
|
472
|
+
try {
|
|
473
|
+
const mainEnvPath = path2.join(process.cwd(), ".env");
|
|
474
|
+
if (await fs2.pathExists(envFilePath)) {
|
|
475
|
+
logger.warn(".env file already exists in worktree, skipping copy");
|
|
476
|
+
} else {
|
|
477
|
+
await this.environment.copyIfExists(mainEnvPath, envFilePath);
|
|
478
|
+
}
|
|
479
|
+
} catch (error) {
|
|
480
|
+
logger.warn(`Warning: Failed to copy main .env file: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Copy iloom configuration (settings.local.json) from main repo to worktree
|
|
485
|
+
* Always called regardless of project capabilities
|
|
486
|
+
*/
|
|
487
|
+
async copyIloomSettings(worktreePath) {
|
|
488
|
+
const mainSettingsLocalPath = path2.join(process.cwd(), ".iloom", "settings.local.json");
|
|
489
|
+
try {
|
|
490
|
+
const worktreeIloomDir = path2.join(worktreePath, ".iloom");
|
|
491
|
+
await fs2.ensureDir(worktreeIloomDir);
|
|
492
|
+
const worktreeSettingsLocalPath = path2.join(worktreeIloomDir, "settings.local.json");
|
|
493
|
+
if (await fs2.pathExists(worktreeSettingsLocalPath)) {
|
|
494
|
+
logger.warn("settings.local.json already exists in worktree, skipping copy");
|
|
495
|
+
} else {
|
|
496
|
+
await this.environment.copyIfExists(mainSettingsLocalPath, worktreeSettingsLocalPath);
|
|
497
|
+
}
|
|
498
|
+
} catch (error) {
|
|
499
|
+
logger.warn(`Warning: Failed to copy settings.local.json: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Setup PORT environment variable for web projects
|
|
504
|
+
* Only called when project has web capabilities
|
|
505
|
+
*/
|
|
506
|
+
async setupPortForWeb(worktreePath, input, basePort) {
|
|
507
|
+
const envFilePath = path2.join(worktreePath, ".env");
|
|
508
|
+
const options = { basePort };
|
|
509
|
+
if (input.type === "issue") {
|
|
510
|
+
options.issueNumber = input.identifier;
|
|
511
|
+
} else if (input.type === "pr") {
|
|
512
|
+
options.prNumber = input.identifier;
|
|
513
|
+
} else if (input.type === "branch") {
|
|
514
|
+
options.branchName = input.identifier;
|
|
515
|
+
}
|
|
516
|
+
const port = this.environment.calculatePort(options);
|
|
517
|
+
await this.environment.setEnvVar(envFilePath, "PORT", String(port));
|
|
518
|
+
return port;
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Load environment variables from main .env file into process.env
|
|
522
|
+
* Uses dotenv-flow to handle various .env file patterns
|
|
523
|
+
*/
|
|
524
|
+
loadMainEnvFile() {
|
|
525
|
+
const result = loadEnvIntoProcess({ path: process.cwd() });
|
|
526
|
+
if (result.error) {
|
|
527
|
+
logger.warn(`Warning: Could not load .env files: ${result.error.message}`);
|
|
528
|
+
} else {
|
|
529
|
+
logger.info("Loaded environment variables using dotenv-flow");
|
|
530
|
+
if (result.parsed && Object.keys(result.parsed).length > 0) {
|
|
531
|
+
logger.debug(`Loaded ${Object.keys(result.parsed).length} environment variables`);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Generate a unique loom ID
|
|
537
|
+
*/
|
|
538
|
+
generateLoomId(input) {
|
|
539
|
+
const prefix = input.type;
|
|
540
|
+
return `${prefix}-${input.identifier}`;
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Calculate port for the loom
|
|
544
|
+
* Base port: configurable via settings.capabilities.web.basePort (default 3000) + issue/PR number (or deterministic hash for branches)
|
|
545
|
+
*/
|
|
546
|
+
async calculatePort(input) {
|
|
547
|
+
var _a, _b;
|
|
548
|
+
const settingsData = await this.settings.loadSettings();
|
|
549
|
+
const basePort = ((_b = (_a = settingsData.capabilities) == null ? void 0 : _a.web) == null ? void 0 : _b.basePort) ?? 3e3;
|
|
550
|
+
if (input.type === "issue" && typeof input.identifier === "number") {
|
|
551
|
+
return this.environment.calculatePort({ basePort, issueNumber: input.identifier });
|
|
552
|
+
}
|
|
553
|
+
if (input.type === "pr" && typeof input.identifier === "number") {
|
|
554
|
+
return this.environment.calculatePort({ basePort, prNumber: input.identifier });
|
|
555
|
+
}
|
|
556
|
+
if (input.type === "branch" && typeof input.identifier === "string") {
|
|
557
|
+
return this.environment.calculatePort({ basePort, branchName: input.identifier });
|
|
558
|
+
}
|
|
559
|
+
throw new Error(`Unknown input type: ${input.type}`);
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Apply color synchronization to both VSCode and terminal
|
|
563
|
+
* Colors are cosmetic - errors are logged but don't block workflow
|
|
564
|
+
*/
|
|
565
|
+
async applyColorSynchronization(worktreePath, branchName) {
|
|
566
|
+
const colorData = generateColorFromBranchName(branchName);
|
|
567
|
+
const vscode = new VSCodeIntegration();
|
|
568
|
+
await vscode.setTitleBarColor(worktreePath, colorData.hex);
|
|
569
|
+
logger.info(`Applied VSCode title bar color: ${colorData.hex} for branch: ${branchName}`);
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Map worktrees to loom objects
|
|
573
|
+
* This is a simplified conversion - in production we'd store loom metadata
|
|
574
|
+
*/
|
|
575
|
+
async mapWorktreesToLooms(worktrees) {
|
|
576
|
+
return await Promise.all(worktrees.map(async (wt) => {
|
|
577
|
+
let type = "branch";
|
|
578
|
+
let identifier = wt.branch;
|
|
579
|
+
if (wt.branch.startsWith("issue-")) {
|
|
580
|
+
type = "issue";
|
|
581
|
+
identifier = parseInt(wt.branch.replace("issue-", ""), 10);
|
|
582
|
+
} else if (wt.branch.startsWith("pr-")) {
|
|
583
|
+
type = "pr";
|
|
584
|
+
identifier = parseInt(wt.branch.replace("pr-", ""), 10);
|
|
585
|
+
}
|
|
586
|
+
return {
|
|
587
|
+
id: `${type}-${identifier}`,
|
|
588
|
+
path: wt.path,
|
|
589
|
+
branch: wt.branch,
|
|
590
|
+
type,
|
|
591
|
+
identifier,
|
|
592
|
+
port: await this.calculatePort({ type, identifier, originalInput: "" }),
|
|
593
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
594
|
+
lastAccessed: /* @__PURE__ */ new Date()
|
|
595
|
+
};
|
|
596
|
+
}));
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* NEW: Find existing loom for the given input
|
|
600
|
+
* Checks for worktrees matching the issue/PR identifier
|
|
601
|
+
*/
|
|
602
|
+
async findExistingIloom(input, githubData) {
|
|
603
|
+
if (input.type === "issue") {
|
|
604
|
+
return await this.gitWorktree.findWorktreeForIssue(input.identifier);
|
|
605
|
+
} else if (input.type === "pr" && githubData && "branch" in githubData) {
|
|
606
|
+
return await this.gitWorktree.findWorktreeForPR(
|
|
607
|
+
input.identifier,
|
|
608
|
+
githubData.branch
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
return null;
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* NEW: Reuse an existing loom
|
|
615
|
+
* Includes environment setup and database branching for existing worktrees
|
|
616
|
+
* Ports: handle_existing_worktree() from bash script lines 168-215
|
|
617
|
+
*/
|
|
618
|
+
async reuseIloom(worktree, input, githubData) {
|
|
619
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
620
|
+
const worktreePath = worktree.path;
|
|
621
|
+
const branchName = worktree.branch;
|
|
622
|
+
this.loadMainEnvFile();
|
|
623
|
+
const { capabilities, binEntries } = await this.capabilityDetector.detectCapabilities(worktreePath);
|
|
624
|
+
await this.copyEnvironmentFiles(worktreePath);
|
|
625
|
+
await this.copyIloomSettings(worktreePath);
|
|
626
|
+
const settingsData = await this.settings.loadSettings();
|
|
627
|
+
const basePort = ((_b = (_a = settingsData.capabilities) == null ? void 0 : _a.web) == null ? void 0 : _b.basePort) ?? 3e3;
|
|
628
|
+
let port = basePort;
|
|
629
|
+
if (capabilities.includes("web")) {
|
|
630
|
+
port = await this.setupPortForWeb(worktreePath, input, basePort);
|
|
631
|
+
}
|
|
632
|
+
logger.info("Database branch assumed to be already configured for existing worktree");
|
|
633
|
+
const databaseBranch = void 0;
|
|
634
|
+
if (input.type === "issue") {
|
|
635
|
+
try {
|
|
636
|
+
logger.info("Moving issue to In Progress...");
|
|
637
|
+
await this.github.moveIssueToInProgress(input.identifier);
|
|
638
|
+
} catch (error) {
|
|
639
|
+
logger.warn(
|
|
640
|
+
`Failed to move issue to In Progress: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
641
|
+
error
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
const enableClaude = ((_c = input.options) == null ? void 0 : _c.enableClaude) !== false;
|
|
646
|
+
const enableCode = ((_d = input.options) == null ? void 0 : _d.enableCode) !== false;
|
|
647
|
+
const enableDevServer = ((_e = input.options) == null ? void 0 : _e.enableDevServer) !== false;
|
|
648
|
+
const enableTerminal = ((_f = input.options) == null ? void 0 : _f.enableTerminal) ?? false;
|
|
649
|
+
const oneShot = ((_g = input.options) == null ? void 0 : _g.oneShot) ?? "default";
|
|
650
|
+
const setArguments = (_h = input.options) == null ? void 0 : _h.setArguments;
|
|
651
|
+
const executablePath = (_i = input.options) == null ? void 0 : _i.executablePath;
|
|
652
|
+
if (enableClaude || enableCode || enableDevServer || enableTerminal) {
|
|
653
|
+
logger.info("Launching workspace components...");
|
|
654
|
+
const { LoomLauncher } = await import("./LoomLauncher-MODG2SEM.js");
|
|
655
|
+
const { ClaudeContextManager: ClaudeContextManager2 } = await import("./ClaudeContextManager-XOSXQ67R.js");
|
|
656
|
+
const claudeContext = new ClaudeContextManager2(void 0, void 0, this.settings);
|
|
657
|
+
const launcher = new LoomLauncher(claudeContext);
|
|
658
|
+
await launcher.launchLoom({
|
|
659
|
+
enableClaude,
|
|
660
|
+
enableCode,
|
|
661
|
+
enableDevServer,
|
|
662
|
+
enableTerminal,
|
|
663
|
+
worktreePath,
|
|
664
|
+
branchName,
|
|
665
|
+
port,
|
|
666
|
+
capabilities,
|
|
667
|
+
workflowType: input.type === "branch" ? "regular" : input.type,
|
|
668
|
+
identifier: input.identifier,
|
|
669
|
+
...(githubData == null ? void 0 : githubData.title) && { title: githubData.title },
|
|
670
|
+
oneShot,
|
|
671
|
+
...setArguments && { setArguments },
|
|
672
|
+
...executablePath && { executablePath }
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
const loom = {
|
|
676
|
+
id: this.generateLoomId(input),
|
|
677
|
+
path: worktreePath,
|
|
678
|
+
branch: branchName,
|
|
679
|
+
type: input.type,
|
|
680
|
+
identifier: input.identifier,
|
|
681
|
+
port,
|
|
682
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
683
|
+
// We don't have actual creation date, use now
|
|
684
|
+
lastAccessed: /* @__PURE__ */ new Date(),
|
|
685
|
+
...databaseBranch !== void 0 && { databaseBranch },
|
|
686
|
+
...capabilities.length > 0 && { capabilities },
|
|
687
|
+
...Object.keys(binEntries).length > 0 && { binEntries },
|
|
688
|
+
...githubData !== null && {
|
|
689
|
+
githubData: {
|
|
690
|
+
title: githubData.title,
|
|
691
|
+
body: githubData.body,
|
|
692
|
+
url: githubData.url,
|
|
693
|
+
state: githubData.state
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
};
|
|
697
|
+
logger.success(`Reused existing loom: ${loom.id} at ${loom.path}`);
|
|
698
|
+
return loom;
|
|
699
|
+
}
|
|
700
|
+
};
|
|
701
|
+
|
|
702
|
+
// src/commands/start.ts
|
|
703
|
+
var StartCommand = class {
|
|
704
|
+
constructor(gitHubService, loomManager, agentManager, settingsManager) {
|
|
705
|
+
this.loomManager = null;
|
|
706
|
+
this.gitHubService = gitHubService ?? new GitHubService();
|
|
707
|
+
this.agentManager = agentManager ?? new AgentManager();
|
|
708
|
+
this.settingsManager = settingsManager ?? new SettingsManager();
|
|
709
|
+
this.enhancementService = new IssueEnhancementService(
|
|
710
|
+
this.gitHubService,
|
|
711
|
+
this.agentManager,
|
|
712
|
+
this.settingsManager
|
|
713
|
+
);
|
|
714
|
+
this.providedLoomManager = loomManager;
|
|
715
|
+
const envResult = loadEnvIntoProcess();
|
|
716
|
+
if (envResult.error) {
|
|
717
|
+
logger.debug(`Environment loading warning: ${envResult.error.message}`);
|
|
718
|
+
}
|
|
719
|
+
if (envResult.parsed) {
|
|
720
|
+
logger.debug(`Loaded ${Object.keys(envResult.parsed).length} environment variables`);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Initialize LoomManager with the main worktree path
|
|
725
|
+
* Uses lazy initialization to ensure we have the correct path
|
|
726
|
+
*/
|
|
727
|
+
async initializeLoomManager() {
|
|
728
|
+
var _a, _b, _c;
|
|
729
|
+
if (this.loomManager) {
|
|
730
|
+
return this.loomManager;
|
|
731
|
+
}
|
|
732
|
+
if (this.providedLoomManager) {
|
|
733
|
+
this.loomManager = this.providedLoomManager;
|
|
734
|
+
return this.loomManager;
|
|
735
|
+
}
|
|
736
|
+
const mainWorktreePath = await findMainWorktreePathWithSettings();
|
|
737
|
+
const environmentManager = new EnvironmentManager();
|
|
738
|
+
logger.debug("Environment variables for Neon:", {
|
|
739
|
+
NEON_PROJECT_ID: process.env.NEON_PROJECT_ID,
|
|
740
|
+
NEON_PARENT_BRANCH: process.env.NEON_PARENT_BRANCH,
|
|
741
|
+
hasNeonProjectId: !!process.env.NEON_PROJECT_ID,
|
|
742
|
+
hasNeonParentBranch: !!process.env.NEON_PARENT_BRANCH,
|
|
743
|
+
neonProjectIdLength: ((_a = process.env.NEON_PROJECT_ID) == null ? void 0 : _a.length) ?? 0
|
|
744
|
+
});
|
|
745
|
+
const neonProvider = new NeonProvider({
|
|
746
|
+
projectId: process.env.NEON_PROJECT_ID ?? "",
|
|
747
|
+
parentBranch: process.env.NEON_PARENT_BRANCH ?? ""
|
|
748
|
+
});
|
|
749
|
+
const settings = await this.settingsManager.loadSettings();
|
|
750
|
+
const databaseUrlEnvVarName = ((_c = (_b = settings.capabilities) == null ? void 0 : _b.database) == null ? void 0 : _c.databaseUrlEnvVarName) ?? "DATABASE_URL";
|
|
751
|
+
const databaseManager = new DatabaseManager(neonProvider, environmentManager, databaseUrlEnvVarName);
|
|
752
|
+
this.loomManager = new LoomManager(
|
|
753
|
+
new GitWorktreeManager(mainWorktreePath),
|
|
754
|
+
this.gitHubService,
|
|
755
|
+
environmentManager,
|
|
756
|
+
// Reuse same instance
|
|
757
|
+
new ClaudeContextManager(),
|
|
758
|
+
new ProjectCapabilityDetector(),
|
|
759
|
+
new CLIIsolationManager(),
|
|
760
|
+
this.settingsManager,
|
|
761
|
+
// Use same instance with CLI overrides
|
|
762
|
+
databaseManager
|
|
763
|
+
// Add database manager
|
|
764
|
+
);
|
|
765
|
+
return this.loomManager;
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Main entry point for the start command
|
|
769
|
+
*/
|
|
770
|
+
async execute(input) {
|
|
771
|
+
var _a, _b, _c;
|
|
772
|
+
try {
|
|
773
|
+
const loomManager = await this.initializeLoomManager();
|
|
774
|
+
const parsed = await this.parseInput(input.identifier);
|
|
775
|
+
await this.validateInput(parsed);
|
|
776
|
+
if (parsed.type === "description") {
|
|
777
|
+
const issueNumber = await this.enhanceAndCreateIssue(parsed.originalInput);
|
|
778
|
+
parsed.type = "issue";
|
|
779
|
+
parsed.number = issueNumber;
|
|
780
|
+
}
|
|
781
|
+
if (input.options.oneShot === "bypassPermissions") {
|
|
782
|
+
const { promptConfirmation } = await import("./prompt-ANTQWHUF.js");
|
|
783
|
+
const confirmed = await promptConfirmation(
|
|
784
|
+
"\u26A0\uFE0F WARNING: bypassPermissions mode will allow Claude to execute all tool calls without confirmation. This can be dangerous. Do you want to proceed?"
|
|
785
|
+
);
|
|
786
|
+
if (!confirmed) {
|
|
787
|
+
logger.info("Operation cancelled by user");
|
|
788
|
+
process.exit(0);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
const cliOverrides = extractSettingsOverrides();
|
|
792
|
+
const settings = await this.settingsManager.loadSettings(void 0, cliOverrides);
|
|
793
|
+
const workflowType = parsed.type === "branch" ? "regular" : parsed.type;
|
|
794
|
+
const workflowConfig = (_a = settings.workflows) == null ? void 0 : _a[workflowType];
|
|
795
|
+
const { extractRawSetArguments, getExecutablePath } = await import("./cli-overrides-XFZWY7CM.js");
|
|
796
|
+
const setArguments = extractRawSetArguments();
|
|
797
|
+
const executablePath = getExecutablePath();
|
|
798
|
+
logger.info(`\u2705 Validated input: ${this.formatParsedInput(parsed)}`);
|
|
799
|
+
const identifier = parsed.type === "branch" ? parsed.branchName ?? "" : parsed.number ?? 0;
|
|
800
|
+
const enableClaude = input.options.claude ?? (workflowConfig == null ? void 0 : workflowConfig.startAiAgent) ?? true;
|
|
801
|
+
const enableCode = input.options.code ?? (workflowConfig == null ? void 0 : workflowConfig.startIde) ?? true;
|
|
802
|
+
const enableDevServer = input.options.devServer ?? (workflowConfig == null ? void 0 : workflowConfig.startDevServer) ?? true;
|
|
803
|
+
const enableTerminal = input.options.terminal ?? (workflowConfig == null ? void 0 : workflowConfig.startTerminal) ?? false;
|
|
804
|
+
logger.debug("Final workflow config values:", {
|
|
805
|
+
enableClaude,
|
|
806
|
+
enableCode,
|
|
807
|
+
enableDevServer,
|
|
808
|
+
enableTerminal
|
|
809
|
+
});
|
|
810
|
+
const loom = await loomManager.createIloom({
|
|
811
|
+
type: parsed.type,
|
|
812
|
+
identifier,
|
|
813
|
+
originalInput: parsed.originalInput,
|
|
814
|
+
options: {
|
|
815
|
+
enableClaude,
|
|
816
|
+
enableCode,
|
|
817
|
+
enableDevServer,
|
|
818
|
+
enableTerminal,
|
|
819
|
+
...input.options.oneShot && { oneShot: input.options.oneShot },
|
|
820
|
+
...setArguments.length > 0 && { setArguments },
|
|
821
|
+
...executablePath && { executablePath }
|
|
822
|
+
}
|
|
823
|
+
});
|
|
824
|
+
logger.success(`\u2705 Created loom: ${loom.id} at ${loom.path}`);
|
|
825
|
+
logger.info(` Branch: ${loom.branch}`);
|
|
826
|
+
if ((_b = loom.capabilities) == null ? void 0 : _b.includes("web")) {
|
|
827
|
+
logger.info(` Port: ${loom.port}`);
|
|
828
|
+
}
|
|
829
|
+
if ((_c = loom.githubData) == null ? void 0 : _c.title) {
|
|
830
|
+
logger.info(` Title: ${loom.githubData.title}`);
|
|
831
|
+
}
|
|
832
|
+
} catch (error) {
|
|
833
|
+
if (error instanceof Error) {
|
|
834
|
+
logger.error(`\u274C ${error.message}`);
|
|
835
|
+
} else {
|
|
836
|
+
logger.error("\u274C An unknown error occurred");
|
|
837
|
+
}
|
|
838
|
+
throw error;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
/**
|
|
842
|
+
* Parse input to determine type and extract relevant data
|
|
843
|
+
*/
|
|
844
|
+
async parseInput(identifier) {
|
|
845
|
+
const trimmedIdentifier = identifier.trim();
|
|
846
|
+
if (!trimmedIdentifier) {
|
|
847
|
+
throw new Error("Missing required argument: identifier");
|
|
848
|
+
}
|
|
849
|
+
const spaceCount = (trimmedIdentifier.match(/ /g) ?? []).length;
|
|
850
|
+
if (trimmedIdentifier.length > 25 && spaceCount > 2) {
|
|
851
|
+
return {
|
|
852
|
+
type: "description",
|
|
853
|
+
originalInput: trimmedIdentifier
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
const prPattern = /^(?:pr|PR)[/-](\d+)$/;
|
|
857
|
+
const prMatch = trimmedIdentifier.match(prPattern);
|
|
858
|
+
if (prMatch == null ? void 0 : prMatch[1]) {
|
|
859
|
+
return {
|
|
860
|
+
type: "pr",
|
|
861
|
+
number: parseInt(prMatch[1], 10),
|
|
862
|
+
originalInput: trimmedIdentifier
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
const numericPattern = /^#?(\d+)$/;
|
|
866
|
+
const numericMatch = trimmedIdentifier.match(numericPattern);
|
|
867
|
+
if (numericMatch == null ? void 0 : numericMatch[1]) {
|
|
868
|
+
const number = parseInt(numericMatch[1], 10);
|
|
869
|
+
const detection = await this.gitHubService.detectInputType(
|
|
870
|
+
trimmedIdentifier
|
|
871
|
+
);
|
|
872
|
+
if (detection.type === "pr") {
|
|
873
|
+
return {
|
|
874
|
+
type: "pr",
|
|
875
|
+
number: detection.number ?? number,
|
|
876
|
+
originalInput: trimmedIdentifier
|
|
877
|
+
};
|
|
878
|
+
} else if (detection.type === "issue") {
|
|
879
|
+
return {
|
|
880
|
+
type: "issue",
|
|
881
|
+
number: detection.number ?? number,
|
|
882
|
+
originalInput: trimmedIdentifier
|
|
883
|
+
};
|
|
884
|
+
} else {
|
|
885
|
+
throw new Error(`Could not find issue or PR #${number}`);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
return {
|
|
889
|
+
type: "branch",
|
|
890
|
+
branchName: trimmedIdentifier,
|
|
891
|
+
originalInput: trimmedIdentifier
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
* Validate the parsed input based on its type
|
|
896
|
+
*/
|
|
897
|
+
async validateInput(parsed) {
|
|
898
|
+
switch (parsed.type) {
|
|
899
|
+
case "pr": {
|
|
900
|
+
if (!parsed.number) {
|
|
901
|
+
throw new Error("Invalid PR number");
|
|
902
|
+
}
|
|
903
|
+
const pr = await this.gitHubService.fetchPR(parsed.number);
|
|
904
|
+
await this.gitHubService.validatePRState(pr);
|
|
905
|
+
logger.debug(`Validated PR #${parsed.number}`);
|
|
906
|
+
break;
|
|
907
|
+
}
|
|
908
|
+
case "issue": {
|
|
909
|
+
if (!parsed.number) {
|
|
910
|
+
throw new Error("Invalid issue number");
|
|
911
|
+
}
|
|
912
|
+
const issue = await this.gitHubService.fetchIssue(parsed.number);
|
|
913
|
+
await this.gitHubService.validateIssueState(issue);
|
|
914
|
+
logger.debug(`Validated issue #${parsed.number}`);
|
|
915
|
+
break;
|
|
916
|
+
}
|
|
917
|
+
case "branch": {
|
|
918
|
+
if (!parsed.branchName) {
|
|
919
|
+
throw new Error("Invalid branch name");
|
|
920
|
+
}
|
|
921
|
+
if (!this.isValidBranchName(parsed.branchName)) {
|
|
922
|
+
throw new Error(
|
|
923
|
+
"Invalid branch name. Use only letters, numbers, hyphens, underscores, and slashes"
|
|
924
|
+
);
|
|
925
|
+
}
|
|
926
|
+
const exists = await branchExists(parsed.branchName);
|
|
927
|
+
if (exists) {
|
|
928
|
+
throw new Error(`Branch '${parsed.branchName}' already exists`);
|
|
929
|
+
}
|
|
930
|
+
logger.debug(`Validated branch name: ${parsed.branchName}`);
|
|
931
|
+
break;
|
|
932
|
+
}
|
|
933
|
+
case "description": {
|
|
934
|
+
logger.debug("Detected description input", {
|
|
935
|
+
length: parsed.originalInput.length
|
|
936
|
+
});
|
|
937
|
+
break;
|
|
938
|
+
}
|
|
939
|
+
default: {
|
|
940
|
+
const unknownType = parsed;
|
|
941
|
+
throw new Error(`Unknown input type: ${unknownType.type}`);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
/**
|
|
946
|
+
* Validate branch name format
|
|
947
|
+
*/
|
|
948
|
+
isValidBranchName(branch) {
|
|
949
|
+
return /^[a-zA-Z0-9/_-]+$/.test(branch);
|
|
950
|
+
}
|
|
951
|
+
/**
|
|
952
|
+
* Format parsed input for display
|
|
953
|
+
*/
|
|
954
|
+
formatParsedInput(parsed) {
|
|
955
|
+
switch (parsed.type) {
|
|
956
|
+
case "pr":
|
|
957
|
+
return `PR #${parsed.number}`;
|
|
958
|
+
case "issue":
|
|
959
|
+
return `Issue #${parsed.number}`;
|
|
960
|
+
case "branch":
|
|
961
|
+
return `Branch '${parsed.branchName}'`;
|
|
962
|
+
case "description":
|
|
963
|
+
return `Description: ${parsed.originalInput.slice(0, 50)}...`;
|
|
964
|
+
default:
|
|
965
|
+
return "Unknown input";
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* Enhance description using Claude AI and create GitHub issue
|
|
970
|
+
* Returns the new issue number
|
|
971
|
+
*/
|
|
972
|
+
async enhanceAndCreateIssue(description) {
|
|
973
|
+
const enhancedDescription = await this.enhancementService.enhanceDescription(description);
|
|
974
|
+
const result = await this.enhancementService.createEnhancedIssue(description, enhancedDescription);
|
|
975
|
+
await this.enhancementService.waitForReviewAndOpen(result.number, true);
|
|
976
|
+
return result.number;
|
|
977
|
+
}
|
|
978
|
+
};
|
|
979
|
+
export {
|
|
980
|
+
StartCommand
|
|
981
|
+
};
|
|
982
|
+
//# sourceMappingURL=start-LWVRBJ6S.js.map
|