@iloom/cli 0.3.2 → 0.3.4
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 +3 -3
- package/dist/{BranchNamingService-JYO746H7.js → BranchNamingService-A77VI6AI.js} +2 -2
- package/dist/ClaudeContextManager-BN7RE5ZQ.js +15 -0
- package/dist/ClaudeService-DLYLJUPA.js +14 -0
- package/dist/{GitHubService-UAMH7DMF.js → GitHubService-FZHHBOFG.js} +3 -3
- package/dist/{LoomLauncher-FVZECY3C.js → LoomLauncher-ZV3ZZIBA.js} +23 -20
- package/dist/LoomLauncher-ZV3ZZIBA.js.map +1 -0
- package/dist/{PromptTemplateManager-A52RUAMS.js → PromptTemplateManager-6HH3PVXV.js} +2 -2
- package/dist/README.md +3 -3
- package/dist/{SettingsManager-MTVX57WR.js → SettingsManager-I2LRCW2A.js} +2 -2
- package/dist/{SettingsMigrationManager-AGIIIPDQ.js → SettingsMigrationManager-TJ7UWZG5.js} +3 -3
- package/dist/agents/iloom-issue-complexity-evaluator.md +18 -3
- package/dist/agents/iloom-issue-enhancer.md +1 -1
- package/dist/{chunk-OAP6SASD.js → chunk-2CXREBLZ.js} +2 -2
- package/dist/{chunk-KG343GSG.js → chunk-2IJEMXOB.js} +359 -99
- package/dist/chunk-2IJEMXOB.js.map +1 -0
- package/dist/{chunk-FOV7RRQ2.js → chunk-2MAIX45J.js} +4 -4
- package/dist/{chunk-5NTD4MCZ.js → chunk-5Q3NDNNV.js} +29 -5
- package/dist/chunk-5Q3NDNNV.js.map +1 -0
- package/dist/{chunk-GVICXJHW.js → chunk-5VK4NRSF.js} +2 -2
- package/dist/{chunk-C7ASXK6J.js → chunk-AKUJXDNW.js} +2 -2
- package/dist/{chunk-UCZ24SUE.js → chunk-CDZERT7Z.js} +21 -9
- package/dist/chunk-CDZERT7Z.js.map +1 -0
- package/dist/{chunk-JKXJ7BGL.js → chunk-CE26YH2U.js} +42 -3
- package/dist/chunk-CE26YH2U.js.map +1 -0
- package/dist/{chunk-ZZZWQGTS.js → chunk-CFFQ2Z7A.js} +74 -75
- package/dist/chunk-CFFQ2Z7A.js.map +1 -0
- package/dist/{chunk-FXMLNKLT.js → chunk-DLHA5VQ3.js} +174 -5
- package/dist/chunk-DLHA5VQ3.js.map +1 -0
- package/dist/{chunk-GOG3ZB7O.js → chunk-IFB4Z76W.js} +23 -4
- package/dist/chunk-IFB4Z76W.js.map +1 -0
- package/dist/{chunk-F4ENT6AC.js → chunk-M7JJCX53.js} +2 -2
- package/dist/{chunk-KLBYVHPK.js → chunk-OSCLCMDG.js} +2 -2
- package/dist/{chunk-ZLIHIUDQ.js → chunk-OYF4VIFI.js} +2 -2
- package/dist/{chunk-A2P7NZTB.js → chunk-PGPI5LR4.js} +4 -4
- package/dist/{chunk-WEN5C5DM.js → chunk-RIEO2WML.js} +4 -1
- package/dist/chunk-RIEO2WML.js.map +1 -0
- package/dist/{chunk-UERERX6M.js → chunk-SUOXY5WJ.js} +2 -2
- package/dist/{init-7IIM35LQ.js → chunk-UAN4A3YU.js} +343 -46
- package/dist/chunk-UAN4A3YU.js.map +1 -0
- package/dist/{claude-KIZYXTSG.js → claude-W52VKI6L.js} +2 -2
- package/dist/{cleanup-6WYUD5SN.js → cleanup-H4VXU3C3.js} +12 -12
- package/dist/cli.js +188 -91
- package/dist/cli.js.map +1 -1
- package/dist/{color-ZVALX37U.js → color-F7RU6B6Z.js} +10 -4
- package/dist/{contribute-7YJHZTO7.js → contribute-Y7IQV5QY.js} +4 -4
- package/dist/{feedback-752MGDPG.js → feedback-XTUCKJNT.js} +11 -9
- package/dist/{feedback-752MGDPG.js.map → feedback-XTUCKJNT.js.map} +1 -1
- package/dist/{git-TAFVDB3J.js → git-IYA53VIC.js} +7 -3
- package/dist/{ignite-OAMDUN27.js → ignite-T74RYXCA.js} +19 -71
- package/dist/ignite-T74RYXCA.js.map +1 -0
- package/dist/index.d.ts +52 -8
- package/dist/index.js +132 -84
- package/dist/index.js.map +1 -1
- package/dist/init-4FHTAM3F.js +19 -0
- package/dist/{neon-helpers-5HBYO2VP.js → neon-helpers-77PBPGJ5.js} +3 -3
- package/dist/{open-FCHKQ77R.js → open-UMXANW5S.js} +4 -4
- package/dist/{prompt-7INJ7YRU.js → prompt-QALMYTVC.js} +4 -2
- package/dist/prompt-QALMYTVC.js.map +1 -0
- package/dist/prompts/init-prompt.txt +82 -8
- package/dist/prompts/issue-prompt.txt +18 -11
- package/dist/prompts/regular-prompt.txt +373 -14
- package/dist/{rebase-Q7WXK566.js → rebase-VJ2VKR6R.js} +11 -11
- package/dist/rebase-VJ2VKR6R.js.map +1 -0
- package/dist/{run-SJPM6YRI.js → run-MJYY4PUT.js} +4 -4
- package/dist/schema/settings.schema.json +21 -3
- package/dist/{test-git-DEUE656D.js → test-git-IT5EWQ5C.js} +3 -3
- package/dist/{test-prefix-Y6Z6ZHSF.js → test-prefix-NPWDPUUH.js} +3 -3
- package/package.json +2 -1
- package/dist/ClaudeContextManager-5WPRJIIW.js +0 -15
- package/dist/ClaudeService-Z4KA7QOW.js +0 -14
- package/dist/LoomLauncher-FVZECY3C.js.map +0 -1
- package/dist/chunk-5NTD4MCZ.js.map +0 -1
- package/dist/chunk-FXMLNKLT.js.map +0 -1
- package/dist/chunk-GOG3ZB7O.js.map +0 -1
- package/dist/chunk-JKXJ7BGL.js.map +0 -1
- package/dist/chunk-KG343GSG.js.map +0 -1
- package/dist/chunk-PRE7KTM4.js +0 -302
- package/dist/chunk-PRE7KTM4.js.map +0 -1
- package/dist/chunk-UCZ24SUE.js.map +0 -1
- package/dist/chunk-WEN5C5DM.js.map +0 -1
- package/dist/chunk-ZZZWQGTS.js.map +0 -1
- package/dist/ignite-OAMDUN27.js.map +0 -1
- package/dist/init-7IIM35LQ.js.map +0 -1
- package/dist/rebase-Q7WXK566.js.map +0 -1
- /package/dist/{BranchNamingService-JYO746H7.js.map → BranchNamingService-A77VI6AI.js.map} +0 -0
- /package/dist/{ClaudeContextManager-5WPRJIIW.js.map → ClaudeContextManager-BN7RE5ZQ.js.map} +0 -0
- /package/dist/{ClaudeService-Z4KA7QOW.js.map → ClaudeService-DLYLJUPA.js.map} +0 -0
- /package/dist/{GitHubService-UAMH7DMF.js.map → GitHubService-FZHHBOFG.js.map} +0 -0
- /package/dist/{PromptTemplateManager-A52RUAMS.js.map → PromptTemplateManager-6HH3PVXV.js.map} +0 -0
- /package/dist/{SettingsManager-MTVX57WR.js.map → SettingsManager-I2LRCW2A.js.map} +0 -0
- /package/dist/{SettingsMigrationManager-AGIIIPDQ.js.map → SettingsMigrationManager-TJ7UWZG5.js.map} +0 -0
- /package/dist/{chunk-OAP6SASD.js.map → chunk-2CXREBLZ.js.map} +0 -0
- /package/dist/{chunk-FOV7RRQ2.js.map → chunk-2MAIX45J.js.map} +0 -0
- /package/dist/{chunk-GVICXJHW.js.map → chunk-5VK4NRSF.js.map} +0 -0
- /package/dist/{chunk-C7ASXK6J.js.map → chunk-AKUJXDNW.js.map} +0 -0
- /package/dist/{chunk-F4ENT6AC.js.map → chunk-M7JJCX53.js.map} +0 -0
- /package/dist/{chunk-KLBYVHPK.js.map → chunk-OSCLCMDG.js.map} +0 -0
- /package/dist/{chunk-ZLIHIUDQ.js.map → chunk-OYF4VIFI.js.map} +0 -0
- /package/dist/{chunk-A2P7NZTB.js.map → chunk-PGPI5LR4.js.map} +0 -0
- /package/dist/{chunk-UERERX6M.js.map → chunk-SUOXY5WJ.js.map} +0 -0
- /package/dist/{claude-KIZYXTSG.js.map → claude-W52VKI6L.js.map} +0 -0
- /package/dist/{cleanup-6WYUD5SN.js.map → cleanup-H4VXU3C3.js.map} +0 -0
- /package/dist/{color-ZVALX37U.js.map → color-F7RU6B6Z.js.map} +0 -0
- /package/dist/{contribute-7YJHZTO7.js.map → contribute-Y7IQV5QY.js.map} +0 -0
- /package/dist/{git-TAFVDB3J.js.map → git-IYA53VIC.js.map} +0 -0
- /package/dist/{neon-helpers-5HBYO2VP.js.map → init-4FHTAM3F.js.map} +0 -0
- /package/dist/{prompt-7INJ7YRU.js.map → neon-helpers-77PBPGJ5.js.map} +0 -0
- /package/dist/{open-FCHKQ77R.js.map → open-UMXANW5S.js.map} +0 -0
- /package/dist/{run-SJPM6YRI.js.map → run-MJYY4PUT.js.map} +0 -0
- /package/dist/{test-git-DEUE656D.js.map → test-git-IT5EWQ5C.js.map} +0 -0
- /package/dist/{test-prefix-Y6Z6ZHSF.js.map → test-prefix-NPWDPUUH.js.map} +0 -0
|
@@ -18,17 +18,18 @@ import {
|
|
|
18
18
|
findMainWorktreePathWithSettings,
|
|
19
19
|
hasUncommittedChanges,
|
|
20
20
|
isFileTrackedByGit
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-5Q3NDNNV.js";
|
|
22
22
|
import {
|
|
23
23
|
SettingsManager
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-CDZERT7Z.js";
|
|
25
25
|
import {
|
|
26
26
|
calculateForegroundColor,
|
|
27
27
|
generateColorFromBranchName,
|
|
28
28
|
hexToRgb,
|
|
29
29
|
lightenColor,
|
|
30
|
-
rgbToHex
|
|
31
|
-
|
|
30
|
+
rgbToHex,
|
|
31
|
+
selectDistinctColor
|
|
32
|
+
} from "./chunk-CFFQ2Z7A.js";
|
|
32
33
|
import {
|
|
33
34
|
findEnvFileForDatabaseUrl,
|
|
34
35
|
formatEnvLine,
|
|
@@ -42,13 +43,187 @@ import {
|
|
|
42
43
|
logger
|
|
43
44
|
} from "./chunk-GEHQXLEI.js";
|
|
44
45
|
|
|
46
|
+
// src/lib/MetadataManager.ts
|
|
47
|
+
import path from "path";
|
|
48
|
+
import os from "os";
|
|
49
|
+
import fs from "fs-extra";
|
|
50
|
+
var MetadataManager = class {
|
|
51
|
+
constructor() {
|
|
52
|
+
this.loomsDir = path.join(os.homedir(), ".config", "iloom-ai", "looms");
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Convert worktree path to filename slug per spec section 2.2
|
|
56
|
+
*
|
|
57
|
+
* Algorithm:
|
|
58
|
+
* 1. Trim trailing slashes
|
|
59
|
+
* 2. Replace all path separators (/ or \) with __ (double underscore)
|
|
60
|
+
* 3. Replace any other non-alphanumeric characters (except _ and -) with -
|
|
61
|
+
* 4. Append .json
|
|
62
|
+
*
|
|
63
|
+
* Example:
|
|
64
|
+
* - Worktree: /Users/jane/dev/repo
|
|
65
|
+
* - Filename: _Users__jane__dev__repo.json
|
|
66
|
+
*/
|
|
67
|
+
slugifyPath(worktreePath) {
|
|
68
|
+
let slug = worktreePath.replace(/[/\\]+$/, "");
|
|
69
|
+
slug = slug.replace(/[/\\]/g, "___");
|
|
70
|
+
slug = slug.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
71
|
+
return `${slug}.json`;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Get the full path to the metadata file for a worktree
|
|
75
|
+
*/
|
|
76
|
+
getFilePath(worktreePath) {
|
|
77
|
+
const filename = this.slugifyPath(worktreePath);
|
|
78
|
+
return path.join(this.loomsDir, filename);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Write metadata for a worktree (spec section 3.1)
|
|
82
|
+
*
|
|
83
|
+
* @param worktreePath - Absolute path to the worktree (used for file naming)
|
|
84
|
+
* @param input - Metadata to write (description plus additional fields)
|
|
85
|
+
*/
|
|
86
|
+
async writeMetadata(worktreePath, input) {
|
|
87
|
+
try {
|
|
88
|
+
await fs.ensureDir(this.loomsDir, { mode: 493 });
|
|
89
|
+
const content = {
|
|
90
|
+
description: input.description,
|
|
91
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
92
|
+
version: 1,
|
|
93
|
+
branchName: input.branchName,
|
|
94
|
+
worktreePath: input.worktreePath,
|
|
95
|
+
issueType: input.issueType,
|
|
96
|
+
issue_numbers: input.issue_numbers,
|
|
97
|
+
pr_numbers: input.pr_numbers,
|
|
98
|
+
issueTracker: input.issueTracker,
|
|
99
|
+
colorHex: input.colorHex
|
|
100
|
+
};
|
|
101
|
+
const filePath = this.getFilePath(worktreePath);
|
|
102
|
+
await fs.writeFile(filePath, JSON.stringify(content, null, 2), { mode: 420 });
|
|
103
|
+
logger.debug(`Metadata written for worktree: ${worktreePath}`);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
logger.warn(
|
|
106
|
+
`Failed to write metadata for worktree: ${error instanceof Error ? error.message : String(error)}`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Read metadata for a worktree (spec section 3.2)
|
|
112
|
+
*
|
|
113
|
+
* @param worktreePath - Absolute path to the worktree
|
|
114
|
+
* @returns The metadata object with all fields, or null if not found/invalid
|
|
115
|
+
*/
|
|
116
|
+
async readMetadata(worktreePath) {
|
|
117
|
+
try {
|
|
118
|
+
const filePath = this.getFilePath(worktreePath);
|
|
119
|
+
if (!await fs.pathExists(filePath)) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
123
|
+
const data = JSON.parse(content);
|
|
124
|
+
if (!data.description) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
description: data.description,
|
|
129
|
+
created_at: data.created_at ?? null,
|
|
130
|
+
branchName: data.branchName ?? null,
|
|
131
|
+
worktreePath: data.worktreePath ?? null,
|
|
132
|
+
issueType: data.issueType ?? null,
|
|
133
|
+
issue_numbers: data.issue_numbers ?? [],
|
|
134
|
+
pr_numbers: data.pr_numbers ?? [],
|
|
135
|
+
issueTracker: data.issueTracker ?? null,
|
|
136
|
+
colorHex: data.colorHex ?? null
|
|
137
|
+
};
|
|
138
|
+
} catch (error) {
|
|
139
|
+
logger.debug(
|
|
140
|
+
`Could not read metadata for worktree ${worktreePath}: ${error instanceof Error ? error.message : String(error)}`
|
|
141
|
+
);
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* List all stored loom metadata files
|
|
147
|
+
*
|
|
148
|
+
* Returns an array of LoomMetadata objects for all valid metadata files
|
|
149
|
+
* in the looms directory. Invalid or unreadable files are skipped.
|
|
150
|
+
*
|
|
151
|
+
* @returns Array of LoomMetadata objects from all stored files
|
|
152
|
+
*/
|
|
153
|
+
async listAllMetadata() {
|
|
154
|
+
const results = [];
|
|
155
|
+
try {
|
|
156
|
+
if (!await fs.pathExists(this.loomsDir)) {
|
|
157
|
+
return results;
|
|
158
|
+
}
|
|
159
|
+
const files = await fs.readdir(this.loomsDir);
|
|
160
|
+
for (const file of files) {
|
|
161
|
+
if (!file.endsWith(".json")) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
try {
|
|
165
|
+
const filePath = path.join(this.loomsDir, file);
|
|
166
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
167
|
+
const data = JSON.parse(content);
|
|
168
|
+
if (!data.description) {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
results.push({
|
|
172
|
+
description: data.description,
|
|
173
|
+
created_at: data.created_at ?? null,
|
|
174
|
+
branchName: data.branchName ?? null,
|
|
175
|
+
worktreePath: data.worktreePath ?? null,
|
|
176
|
+
issueType: data.issueType ?? null,
|
|
177
|
+
issue_numbers: data.issue_numbers ?? [],
|
|
178
|
+
pr_numbers: data.pr_numbers ?? [],
|
|
179
|
+
issueTracker: data.issueTracker ?? null,
|
|
180
|
+
colorHex: data.colorHex ?? null
|
|
181
|
+
});
|
|
182
|
+
} catch (error) {
|
|
183
|
+
logger.debug(
|
|
184
|
+
`Skipping metadata file ${file}: ${error instanceof Error ? error.message : String(error)}`
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
} catch (error) {
|
|
189
|
+
logger.debug(
|
|
190
|
+
`Could not list metadata files: ${error instanceof Error ? error.message : String(error)}`
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
return results;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Delete metadata for a worktree (spec section 3.3)
|
|
197
|
+
*
|
|
198
|
+
* Idempotent: silently succeeds if file doesn't exist
|
|
199
|
+
* Non-fatal: logs warning on permission errors but doesn't throw
|
|
200
|
+
*
|
|
201
|
+
* @param worktreePath - Absolute path to the worktree
|
|
202
|
+
*/
|
|
203
|
+
async deleteMetadata(worktreePath) {
|
|
204
|
+
try {
|
|
205
|
+
const filePath = this.getFilePath(worktreePath);
|
|
206
|
+
if (!await fs.pathExists(filePath)) {
|
|
207
|
+
logger.debug(`No metadata file to delete for worktree: ${worktreePath}`);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
await fs.unlink(filePath);
|
|
211
|
+
logger.debug(`Metadata deleted for worktree: ${worktreePath}`);
|
|
212
|
+
} catch (error) {
|
|
213
|
+
logger.warn(
|
|
214
|
+
`Failed to delete metadata for worktree: ${error instanceof Error ? error.message : String(error)}`
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
|
|
45
220
|
// src/lib/LoomManager.ts
|
|
46
|
-
import
|
|
47
|
-
import
|
|
221
|
+
import path3 from "path";
|
|
222
|
+
import fs3 from "fs-extra";
|
|
48
223
|
|
|
49
224
|
// src/lib/VSCodeIntegration.ts
|
|
50
|
-
import
|
|
51
|
-
import
|
|
225
|
+
import fs2 from "fs-extra";
|
|
226
|
+
import path2 from "path";
|
|
52
227
|
import { parse, modify, applyEdits } from "jsonc-parser";
|
|
53
228
|
var VSCodeIntegration = class {
|
|
54
229
|
/**
|
|
@@ -58,10 +233,10 @@ var VSCodeIntegration = class {
|
|
|
58
233
|
* @param hexColor - Hex color string (e.g., "#dcebf8")
|
|
59
234
|
*/
|
|
60
235
|
async setTitleBarColor(workspacePath, hexColor) {
|
|
61
|
-
const vscodeDir =
|
|
62
|
-
const settingsPath =
|
|
236
|
+
const vscodeDir = path2.join(workspacePath, ".vscode");
|
|
237
|
+
const settingsPath = path2.join(vscodeDir, "settings.json");
|
|
63
238
|
try {
|
|
64
|
-
await
|
|
239
|
+
await fs2.ensureDir(vscodeDir);
|
|
65
240
|
const settings = await this.readSettings(settingsPath);
|
|
66
241
|
const updatedSettings = this.mergeColorSettings(settings, hexColor);
|
|
67
242
|
await this.writeSettings(settingsPath, updatedSettings);
|
|
@@ -81,10 +256,10 @@ var VSCodeIntegration = class {
|
|
|
81
256
|
*/
|
|
82
257
|
async readSettings(settingsPath) {
|
|
83
258
|
try {
|
|
84
|
-
if (!await
|
|
259
|
+
if (!await fs2.pathExists(settingsPath)) {
|
|
85
260
|
return {};
|
|
86
261
|
}
|
|
87
|
-
const content = await
|
|
262
|
+
const content = await fs2.readFile(settingsPath, "utf8");
|
|
88
263
|
const errors = [];
|
|
89
264
|
const settings = parse(content, errors, { allowTrailingComma: true });
|
|
90
265
|
if (errors.length > 0) {
|
|
@@ -108,8 +283,8 @@ var VSCodeIntegration = class {
|
|
|
108
283
|
async writeSettings(settingsPath, settings) {
|
|
109
284
|
try {
|
|
110
285
|
let content;
|
|
111
|
-
if (await
|
|
112
|
-
const existingContent = await
|
|
286
|
+
if (await fs2.pathExists(settingsPath)) {
|
|
287
|
+
const existingContent = await fs2.readFile(settingsPath, "utf8");
|
|
113
288
|
if (existingContent.includes("//") || existingContent.includes("/*")) {
|
|
114
289
|
content = await this.modifyWithCommentsPreserved(existingContent, settings);
|
|
115
290
|
} else {
|
|
@@ -119,8 +294,8 @@ var VSCodeIntegration = class {
|
|
|
119
294
|
content = JSON.stringify(settings, null, 2) + "\n";
|
|
120
295
|
}
|
|
121
296
|
const tempPath = `${settingsPath}.tmp`;
|
|
122
|
-
await
|
|
123
|
-
await
|
|
297
|
+
await fs2.writeFile(tempPath, content, "utf8");
|
|
298
|
+
await fs2.rename(tempPath, settingsPath);
|
|
124
299
|
} catch (error) {
|
|
125
300
|
throw new Error(
|
|
126
301
|
`Failed to write settings.json: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
@@ -184,6 +359,7 @@ var LoomManager = class {
|
|
|
184
359
|
this.cliIsolation = cliIsolation;
|
|
185
360
|
this.settings = settings;
|
|
186
361
|
this.database = database;
|
|
362
|
+
this.metadataManager = new MetadataManager();
|
|
187
363
|
}
|
|
188
364
|
/**
|
|
189
365
|
* Get database branch name for a loom by reading its .env file
|
|
@@ -197,7 +373,7 @@ var LoomManager = class {
|
|
|
197
373
|
return null;
|
|
198
374
|
}
|
|
199
375
|
try {
|
|
200
|
-
const envFilePath =
|
|
376
|
+
const envFilePath = path3.join(loomPath, ".env");
|
|
201
377
|
const settings = await this.settings.loadSettings();
|
|
202
378
|
const databaseUrlVarName = ((_b = (_a = settings.capabilities) == null ? void 0 : _a.database) == null ? void 0 : _b.databaseUrlEnvVarName) ?? "DATABASE_URL";
|
|
203
379
|
const connectionString = await this.environment.getEnvVariable(envFilePath, databaseUrlVarName);
|
|
@@ -216,7 +392,7 @@ var LoomManager = class {
|
|
|
216
392
|
* NEW: Checks for existing worktrees and reuses them if found
|
|
217
393
|
*/
|
|
218
394
|
async createIloom(input) {
|
|
219
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
395
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
|
|
220
396
|
logger.info("Fetching issue data...");
|
|
221
397
|
const issueData = await this.fetchIssueData(input);
|
|
222
398
|
if (input.type === "issue" || input.type === "pr" || input.type === "branch") {
|
|
@@ -265,11 +441,11 @@ var LoomManager = class {
|
|
|
265
441
|
worktreePath,
|
|
266
442
|
varName,
|
|
267
443
|
isFileTrackedByGit,
|
|
268
|
-
async (p) =>
|
|
444
|
+
async (p) => fs3.pathExists(p),
|
|
269
445
|
async (p, v) => this.environment.getEnvVariable(p, v)
|
|
270
446
|
);
|
|
271
447
|
await this.environment.setEnvVar(
|
|
272
|
-
|
|
448
|
+
path3.join(worktreePath, targetFile),
|
|
273
449
|
varName,
|
|
274
450
|
connectionString
|
|
275
451
|
);
|
|
@@ -298,15 +474,17 @@ var LoomManager = class {
|
|
|
298
474
|
);
|
|
299
475
|
}
|
|
300
476
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
477
|
+
const allMetadata = await this.metadataManager.listAllMetadata();
|
|
478
|
+
const usedHexColors = allMetadata.filter((metadata) => metadata.colorHex !== null).map((metadata) => metadata.colorHex);
|
|
479
|
+
const colorData = selectDistinctColor(branchName, usedHexColors);
|
|
480
|
+
logger.debug(`Selected color ${colorData.hex} for branch ${branchName} (${usedHexColors.length} colors in use globally)`);
|
|
481
|
+
try {
|
|
482
|
+
await this.applyColorSynchronization(worktreePath, branchName, colorData, settingsData, input.options);
|
|
483
|
+
} catch (error) {
|
|
484
|
+
logger.warn(
|
|
485
|
+
`Failed to apply color synchronization: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
486
|
+
error
|
|
487
|
+
);
|
|
310
488
|
}
|
|
311
489
|
if (input.type === "issue") {
|
|
312
490
|
try {
|
|
@@ -321,16 +499,16 @@ var LoomManager = class {
|
|
|
321
499
|
);
|
|
322
500
|
}
|
|
323
501
|
}
|
|
324
|
-
const enableClaude = ((
|
|
325
|
-
const enableCode = ((
|
|
326
|
-
const enableDevServer = ((
|
|
327
|
-
const enableTerminal = ((
|
|
328
|
-
const oneShot = ((
|
|
329
|
-
const setArguments = (
|
|
330
|
-
const executablePath = (
|
|
502
|
+
const enableClaude = ((_f = input.options) == null ? void 0 : _f.enableClaude) !== false;
|
|
503
|
+
const enableCode = ((_g = input.options) == null ? void 0 : _g.enableCode) !== false;
|
|
504
|
+
const enableDevServer = ((_h = input.options) == null ? void 0 : _h.enableDevServer) !== false;
|
|
505
|
+
const enableTerminal = ((_i = input.options) == null ? void 0 : _i.enableTerminal) ?? false;
|
|
506
|
+
const oneShot = ((_j = input.options) == null ? void 0 : _j.oneShot) ?? "default";
|
|
507
|
+
const setArguments = (_k = input.options) == null ? void 0 : _k.setArguments;
|
|
508
|
+
const executablePath = (_l = input.options) == null ? void 0 : _l.executablePath;
|
|
331
509
|
if (enableClaude || enableCode || enableDevServer || enableTerminal) {
|
|
332
|
-
const { LoomLauncher } = await import("./LoomLauncher-
|
|
333
|
-
const { ClaudeContextManager } = await import("./ClaudeContextManager-
|
|
510
|
+
const { LoomLauncher } = await import("./LoomLauncher-ZV3ZZIBA.js");
|
|
511
|
+
const { ClaudeContextManager } = await import("./ClaudeContextManager-BN7RE5ZQ.js");
|
|
334
512
|
const claudeContext = new ClaudeContextManager(void 0, void 0, this.settings);
|
|
335
513
|
const launcher = new LoomLauncher(claudeContext, this.settings);
|
|
336
514
|
await launcher.launchLoom({
|
|
@@ -348,9 +526,25 @@ var LoomManager = class {
|
|
|
348
526
|
oneShot,
|
|
349
527
|
...setArguments && { setArguments },
|
|
350
528
|
...executablePath && { executablePath },
|
|
351
|
-
sourceEnvOnStart: settingsData.sourceEnvOnStart ?? false
|
|
529
|
+
sourceEnvOnStart: settingsData.sourceEnvOnStart ?? false,
|
|
530
|
+
colorTerminal: ((_m = input.options) == null ? void 0 : _m.colorTerminal) ?? ((_n = settingsData.colors) == null ? void 0 : _n.terminal) ?? true,
|
|
531
|
+
colorHex: colorData.hex
|
|
352
532
|
});
|
|
353
533
|
}
|
|
534
|
+
const description = (issueData == null ? void 0 : issueData.title) ?? branchName;
|
|
535
|
+
const issue_numbers = input.type === "issue" ? [String(input.identifier)] : [];
|
|
536
|
+
const pr_numbers = input.type === "pr" ? [String(input.identifier)] : [];
|
|
537
|
+
const metadataInput = {
|
|
538
|
+
description,
|
|
539
|
+
branchName,
|
|
540
|
+
worktreePath,
|
|
541
|
+
issueType: input.type,
|
|
542
|
+
issue_numbers,
|
|
543
|
+
pr_numbers,
|
|
544
|
+
issueTracker: this.issueTracker.providerName,
|
|
545
|
+
colorHex: colorData.hex
|
|
546
|
+
};
|
|
547
|
+
await this.metadataManager.writeMetadata(worktreePath, metadataInput);
|
|
354
548
|
const loom = {
|
|
355
549
|
id: this.generateLoomId(input),
|
|
356
550
|
path: worktreePath,
|
|
@@ -358,6 +552,7 @@ var LoomManager = class {
|
|
|
358
552
|
type: input.type,
|
|
359
553
|
identifier: input.identifier,
|
|
360
554
|
port,
|
|
555
|
+
description,
|
|
361
556
|
createdAt: /* @__PURE__ */ new Date(),
|
|
362
557
|
lastAccessed: /* @__PURE__ */ new Date(),
|
|
363
558
|
...databaseBranch !== void 0 && { databaseBranch },
|
|
@@ -388,6 +583,9 @@ var LoomManager = class {
|
|
|
388
583
|
*/
|
|
389
584
|
async listLooms() {
|
|
390
585
|
const worktrees = await this.gitWorktree.listWorktrees();
|
|
586
|
+
if (!worktrees) {
|
|
587
|
+
return [];
|
|
588
|
+
}
|
|
391
589
|
return await this.mapWorktreesToLooms(worktrees);
|
|
392
590
|
}
|
|
393
591
|
/**
|
|
@@ -432,7 +630,7 @@ var LoomManager = class {
|
|
|
432
630
|
async checkAndWarnChildLooms(branchName) {
|
|
433
631
|
let targetBranch = branchName;
|
|
434
632
|
if (!targetBranch) {
|
|
435
|
-
const { getCurrentBranch } = await import("./git-
|
|
633
|
+
const { getCurrentBranch } = await import("./git-IYA53VIC.js");
|
|
436
634
|
targetBranch = await getCurrentBranch();
|
|
437
635
|
}
|
|
438
636
|
if (!targetBranch) {
|
|
@@ -570,16 +768,16 @@ var LoomManager = class {
|
|
|
570
768
|
];
|
|
571
769
|
for (const pattern of envFilePatterns) {
|
|
572
770
|
try {
|
|
573
|
-
const mainEnvPath =
|
|
574
|
-
const worktreeEnvPath =
|
|
575
|
-
if (!await
|
|
771
|
+
const mainEnvPath = path3.join(mainWorkspacePath, pattern);
|
|
772
|
+
const worktreeEnvPath = path3.join(worktreePath, pattern);
|
|
773
|
+
if (!await fs3.pathExists(mainEnvPath)) {
|
|
576
774
|
continue;
|
|
577
775
|
}
|
|
578
776
|
if (await isFileTrackedByGit(pattern, mainWorkspacePath)) {
|
|
579
777
|
logger.debug(`Skipping ${pattern} (tracked by git, already in worktree)`);
|
|
580
778
|
continue;
|
|
581
779
|
}
|
|
582
|
-
if (await
|
|
780
|
+
if (await fs3.pathExists(worktreeEnvPath)) {
|
|
583
781
|
logger.warn(`${pattern} already exists in worktree, skipping copy`);
|
|
584
782
|
continue;
|
|
585
783
|
}
|
|
@@ -597,12 +795,12 @@ var LoomManager = class {
|
|
|
597
795
|
* @param parentBranchName Optional parent branch name for child looms (sets mainBranch)
|
|
598
796
|
*/
|
|
599
797
|
async copyIloomSettings(worktreePath, parentBranchName) {
|
|
600
|
-
const mainSettingsLocalPath =
|
|
798
|
+
const mainSettingsLocalPath = path3.join(process.cwd(), ".iloom", "settings.local.json");
|
|
601
799
|
try {
|
|
602
|
-
const worktreeIloomDir =
|
|
603
|
-
await
|
|
604
|
-
const worktreeSettingsLocalPath =
|
|
605
|
-
if (await
|
|
800
|
+
const worktreeIloomDir = path3.join(worktreePath, ".iloom");
|
|
801
|
+
await fs3.ensureDir(worktreeIloomDir);
|
|
802
|
+
const worktreeSettingsLocalPath = path3.join(worktreeIloomDir, "settings.local.json");
|
|
803
|
+
if (await fs3.pathExists(worktreeSettingsLocalPath)) {
|
|
606
804
|
logger.warn("settings.local.json already exists in worktree, skipping copy");
|
|
607
805
|
} else {
|
|
608
806
|
await this.environment.copyIfExists(mainSettingsLocalPath, worktreeSettingsLocalPath);
|
|
@@ -610,7 +808,7 @@ var LoomManager = class {
|
|
|
610
808
|
if (parentBranchName) {
|
|
611
809
|
let existingSettings = {};
|
|
612
810
|
try {
|
|
613
|
-
const content = await
|
|
811
|
+
const content = await fs3.readFile(worktreeSettingsLocalPath, "utf8");
|
|
614
812
|
existingSettings = JSON.parse(content);
|
|
615
813
|
} catch {
|
|
616
814
|
}
|
|
@@ -618,7 +816,7 @@ var LoomManager = class {
|
|
|
618
816
|
...existingSettings,
|
|
619
817
|
mainBranch: parentBranchName
|
|
620
818
|
};
|
|
621
|
-
await
|
|
819
|
+
await fs3.writeFile(worktreeSettingsLocalPath, JSON.stringify(updatedSettings, null, 2));
|
|
622
820
|
logger.info(`Set mainBranch to ${parentBranchName} for child loom`);
|
|
623
821
|
}
|
|
624
822
|
} catch (error) {
|
|
@@ -630,7 +828,7 @@ var LoomManager = class {
|
|
|
630
828
|
* Only called when project has web capabilities
|
|
631
829
|
*/
|
|
632
830
|
async setupPortForWeb(worktreePath, input, basePort) {
|
|
633
|
-
const envFilePath =
|
|
831
|
+
const envFilePath = path3.join(worktreePath, ".env.local");
|
|
634
832
|
const options = { basePort };
|
|
635
833
|
if (input.type === "issue") {
|
|
636
834
|
options.issueNumber = input.identifier;
|
|
@@ -687,16 +885,34 @@ var LoomManager = class {
|
|
|
687
885
|
/**
|
|
688
886
|
* Apply color synchronization to both VSCode and terminal
|
|
689
887
|
* Colors are cosmetic - errors are logged but don't block workflow
|
|
888
|
+
* Respects colors settings for independent control
|
|
889
|
+
*
|
|
890
|
+
* DEFAULTS:
|
|
891
|
+
* - terminal: true (always safe, only affects macOS Terminal.app)
|
|
892
|
+
* - vscode: false (safe default, prevents unexpected file modifications)
|
|
893
|
+
*
|
|
894
|
+
* @param colorData - Pre-computed color data (from collision avoidance)
|
|
690
895
|
*/
|
|
691
|
-
async applyColorSynchronization(worktreePath, branchName) {
|
|
692
|
-
|
|
693
|
-
const
|
|
694
|
-
|
|
695
|
-
|
|
896
|
+
async applyColorSynchronization(worktreePath, branchName, colorData, settings, options) {
|
|
897
|
+
var _a, _b;
|
|
898
|
+
const colorVscode = (options == null ? void 0 : options.colorVscode) ?? ((_a = settings.colors) == null ? void 0 : _a.vscode) ?? false;
|
|
899
|
+
const colorTerminal = (options == null ? void 0 : options.colorTerminal) ?? ((_b = settings.colors) == null ? void 0 : _b.terminal) ?? true;
|
|
900
|
+
if (!colorVscode && !colorTerminal) {
|
|
901
|
+
logger.debug("Color synchronization disabled for both VSCode and terminal");
|
|
902
|
+
return;
|
|
903
|
+
}
|
|
904
|
+
if (colorVscode) {
|
|
905
|
+
const vscode = new VSCodeIntegration();
|
|
906
|
+
await vscode.setTitleBarColor(worktreePath, colorData.hex);
|
|
907
|
+
logger.info(`Applied VSCode title bar color: ${colorData.hex} for branch: ${branchName}`);
|
|
908
|
+
} else {
|
|
909
|
+
logger.debug("VSCode color sync disabled (default: false for safety)");
|
|
910
|
+
}
|
|
696
911
|
}
|
|
697
912
|
/**
|
|
698
913
|
* Map worktrees to loom objects
|
|
699
914
|
* This is a simplified conversion - in production we'd store loom metadata
|
|
915
|
+
* Now reads metadata from MetadataManager (spec section 3.2)
|
|
700
916
|
*/
|
|
701
917
|
async mapWorktreesToLooms(worktrees) {
|
|
702
918
|
return await Promise.all(worktrees.map(async (wt) => {
|
|
@@ -709,6 +925,7 @@ var LoomManager = class {
|
|
|
709
925
|
type = "pr";
|
|
710
926
|
identifier = parseInt(wt.branch.replace("pr-", ""), 10);
|
|
711
927
|
}
|
|
928
|
+
const loomMetadata = await this.metadataManager.readMetadata(wt.path);
|
|
712
929
|
return {
|
|
713
930
|
id: `${type}-${identifier}`,
|
|
714
931
|
path: wt.path,
|
|
@@ -716,6 +933,7 @@ var LoomManager = class {
|
|
|
716
933
|
type,
|
|
717
934
|
identifier,
|
|
718
935
|
port: await this.calculatePort({ type, identifier, originalInput: "" }),
|
|
936
|
+
...(loomMetadata == null ? void 0 : loomMetadata.description) && { description: loomMetadata.description },
|
|
719
937
|
createdAt: /* @__PURE__ */ new Date(),
|
|
720
938
|
lastAccessed: /* @__PURE__ */ new Date()
|
|
721
939
|
};
|
|
@@ -744,7 +962,7 @@ var LoomManager = class {
|
|
|
744
962
|
* Ports: handle_existing_worktree() from bash script lines 168-215
|
|
745
963
|
*/
|
|
746
964
|
async reuseIloom(worktree, input, issueData) {
|
|
747
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
965
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
748
966
|
const worktreePath = worktree.path;
|
|
749
967
|
const branchName = worktree.branch;
|
|
750
968
|
this.loadMainEnvFile();
|
|
@@ -759,6 +977,25 @@ var LoomManager = class {
|
|
|
759
977
|
}
|
|
760
978
|
logger.info("Database branch assumed to be already configured for existing worktree");
|
|
761
979
|
const databaseBranch = void 0;
|
|
980
|
+
const existingMetadata = await this.metadataManager.readMetadata(worktreePath);
|
|
981
|
+
let colorHex;
|
|
982
|
+
if (existingMetadata == null ? void 0 : existingMetadata.colorHex) {
|
|
983
|
+
colorHex = existingMetadata.colorHex;
|
|
984
|
+
logger.debug(`Reusing stored color ${colorHex} for branch ${branchName}`);
|
|
985
|
+
} else {
|
|
986
|
+
const colorData = generateColorFromBranchName(branchName);
|
|
987
|
+
colorHex = colorData.hex;
|
|
988
|
+
logger.debug(`No stored color, using hash-based color ${colorHex} for branch ${branchName}`);
|
|
989
|
+
}
|
|
990
|
+
try {
|
|
991
|
+
const colorData = { hex: colorHex, rgb: hexToRgb(colorHex), index: 0 };
|
|
992
|
+
await this.applyColorSynchronization(worktreePath, branchName, colorData, settingsData, input.options);
|
|
993
|
+
} catch (error) {
|
|
994
|
+
logger.warn(
|
|
995
|
+
`Failed to apply color synchronization: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
996
|
+
error
|
|
997
|
+
);
|
|
998
|
+
}
|
|
762
999
|
if (input.type === "issue") {
|
|
763
1000
|
try {
|
|
764
1001
|
logger.info("Moving issue to In Progress...");
|
|
@@ -781,8 +1018,8 @@ var LoomManager = class {
|
|
|
781
1018
|
const executablePath = (_i = input.options) == null ? void 0 : _i.executablePath;
|
|
782
1019
|
if (enableClaude || enableCode || enableDevServer || enableTerminal) {
|
|
783
1020
|
logger.info("Launching workspace components...");
|
|
784
|
-
const { LoomLauncher } = await import("./LoomLauncher-
|
|
785
|
-
const { ClaudeContextManager } = await import("./ClaudeContextManager-
|
|
1021
|
+
const { LoomLauncher } = await import("./LoomLauncher-ZV3ZZIBA.js");
|
|
1022
|
+
const { ClaudeContextManager } = await import("./ClaudeContextManager-BN7RE5ZQ.js");
|
|
786
1023
|
const claudeContext = new ClaudeContextManager(void 0, void 0, this.settings);
|
|
787
1024
|
const launcher = new LoomLauncher(claudeContext, this.settings);
|
|
788
1025
|
await launcher.launchLoom({
|
|
@@ -800,9 +1037,27 @@ var LoomManager = class {
|
|
|
800
1037
|
oneShot,
|
|
801
1038
|
...setArguments && { setArguments },
|
|
802
1039
|
...executablePath && { executablePath },
|
|
803
|
-
sourceEnvOnStart: settingsData.sourceEnvOnStart ?? false
|
|
1040
|
+
sourceEnvOnStart: settingsData.sourceEnvOnStart ?? false,
|
|
1041
|
+
colorTerminal: ((_j = input.options) == null ? void 0 : _j.colorTerminal) ?? ((_k = settingsData.colors) == null ? void 0 : _k.terminal) ?? true,
|
|
1042
|
+
colorHex
|
|
804
1043
|
});
|
|
805
1044
|
}
|
|
1045
|
+
const description = (existingMetadata == null ? void 0 : existingMetadata.description) ?? (issueData == null ? void 0 : issueData.title) ?? branchName;
|
|
1046
|
+
if (!existingMetadata) {
|
|
1047
|
+
const issue_numbers = input.type === "issue" ? [String(input.identifier)] : [];
|
|
1048
|
+
const pr_numbers = input.type === "pr" ? [String(input.identifier)] : [];
|
|
1049
|
+
const metadataInput = {
|
|
1050
|
+
description,
|
|
1051
|
+
branchName,
|
|
1052
|
+
worktreePath,
|
|
1053
|
+
issueType: input.type,
|
|
1054
|
+
issue_numbers,
|
|
1055
|
+
pr_numbers,
|
|
1056
|
+
issueTracker: this.issueTracker.providerName,
|
|
1057
|
+
colorHex
|
|
1058
|
+
};
|
|
1059
|
+
await this.metadataManager.writeMetadata(worktreePath, metadataInput);
|
|
1060
|
+
}
|
|
806
1061
|
const loom = {
|
|
807
1062
|
id: this.generateLoomId(input),
|
|
808
1063
|
path: worktreePath,
|
|
@@ -810,6 +1065,7 @@ var LoomManager = class {
|
|
|
810
1065
|
type: input.type,
|
|
811
1066
|
identifier: input.identifier,
|
|
812
1067
|
port,
|
|
1068
|
+
description,
|
|
813
1069
|
createdAt: /* @__PURE__ */ new Date(),
|
|
814
1070
|
// We don't have actual creation date, use now
|
|
815
1071
|
lastAccessed: /* @__PURE__ */ new Date(),
|
|
@@ -831,7 +1087,7 @@ var LoomManager = class {
|
|
|
831
1087
|
};
|
|
832
1088
|
|
|
833
1089
|
// src/lib/EnvironmentManager.ts
|
|
834
|
-
import
|
|
1090
|
+
import fs4 from "fs-extra";
|
|
835
1091
|
var logger2 = createLogger({ prefix: "\u{1F4DD}" });
|
|
836
1092
|
var EnvironmentManager = class {
|
|
837
1093
|
constructor() {
|
|
@@ -847,15 +1103,15 @@ var EnvironmentManager = class {
|
|
|
847
1103
|
if (!validation.valid) {
|
|
848
1104
|
throw new Error(validation.error ?? "Invalid variable name");
|
|
849
1105
|
}
|
|
850
|
-
const fileExists = await
|
|
1106
|
+
const fileExists = await fs4.pathExists(filePath);
|
|
851
1107
|
if (!fileExists) {
|
|
852
1108
|
logger2.info(`Creating ${filePath} with ${key}...`);
|
|
853
1109
|
const content = formatEnvLine(key, value);
|
|
854
|
-
await
|
|
1110
|
+
await fs4.writeFile(filePath, content, "utf8");
|
|
855
1111
|
logger2.success(`${filePath} created with ${key}`);
|
|
856
1112
|
return;
|
|
857
1113
|
}
|
|
858
|
-
const existingContent = await
|
|
1114
|
+
const existingContent = await fs4.readFile(filePath, "utf8");
|
|
859
1115
|
const envMap = parseEnvFile(existingContent);
|
|
860
1116
|
let backupPath;
|
|
861
1117
|
if (backup) {
|
|
@@ -892,7 +1148,7 @@ var EnvironmentManager = class {
|
|
|
892
1148
|
logger2.success(`${key} updated successfully`);
|
|
893
1149
|
}
|
|
894
1150
|
const newContent = newLines.join("\n");
|
|
895
|
-
await
|
|
1151
|
+
await fs4.writeFile(filePath, newContent, "utf8");
|
|
896
1152
|
return backupPath;
|
|
897
1153
|
}
|
|
898
1154
|
/**
|
|
@@ -900,7 +1156,7 @@ var EnvironmentManager = class {
|
|
|
900
1156
|
*/
|
|
901
1157
|
async readEnvFile(filePath) {
|
|
902
1158
|
try {
|
|
903
|
-
const content = await
|
|
1159
|
+
const content = await fs4.readFile(filePath, "utf8");
|
|
904
1160
|
return parseEnvFile(content);
|
|
905
1161
|
} catch (error) {
|
|
906
1162
|
logger2.debug(
|
|
@@ -923,12 +1179,12 @@ var EnvironmentManager = class {
|
|
|
923
1179
|
* @private
|
|
924
1180
|
*/
|
|
925
1181
|
async copyIfExists(source, destination) {
|
|
926
|
-
const sourceExists = await
|
|
1182
|
+
const sourceExists = await fs4.pathExists(source);
|
|
927
1183
|
if (!sourceExists) {
|
|
928
1184
|
logger2.debug(`Source file ${source} does not exist, skipping copy`);
|
|
929
1185
|
return;
|
|
930
1186
|
}
|
|
931
|
-
await
|
|
1187
|
+
await fs4.copy(source, destination, { overwrite: false });
|
|
932
1188
|
logger2.success(`Copied ${source} to ${destination}`);
|
|
933
1189
|
}
|
|
934
1190
|
/**
|
|
@@ -989,7 +1245,7 @@ var EnvironmentManager = class {
|
|
|
989
1245
|
*/
|
|
990
1246
|
async validateEnvFile(filePath) {
|
|
991
1247
|
try {
|
|
992
|
-
const content = await
|
|
1248
|
+
const content = await fs4.readFile(filePath, "utf8");
|
|
993
1249
|
const envMap = parseEnvFile(content);
|
|
994
1250
|
const errors = [];
|
|
995
1251
|
for (const [key, value] of envMap.entries()) {
|
|
@@ -1017,19 +1273,19 @@ var EnvironmentManager = class {
|
|
|
1017
1273
|
async createBackup(filePath) {
|
|
1018
1274
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1019
1275
|
const backupPath = `${filePath}${this.backupSuffix}-${timestamp}`;
|
|
1020
|
-
await
|
|
1276
|
+
await fs4.copy(filePath, backupPath);
|
|
1021
1277
|
logger2.debug(`Created backup at ${backupPath}`);
|
|
1022
1278
|
return backupPath;
|
|
1023
1279
|
}
|
|
1024
1280
|
};
|
|
1025
1281
|
|
|
1026
1282
|
// src/lib/CLIIsolationManager.ts
|
|
1027
|
-
import
|
|
1028
|
-
import
|
|
1029
|
-
import
|
|
1283
|
+
import fs5 from "fs-extra";
|
|
1284
|
+
import path4 from "path";
|
|
1285
|
+
import os2 from "os";
|
|
1030
1286
|
var CLIIsolationManager = class {
|
|
1031
1287
|
constructor() {
|
|
1032
|
-
this.iloomBinDir =
|
|
1288
|
+
this.iloomBinDir = path4.join(os2.homedir(), ".iloom", "bin");
|
|
1033
1289
|
}
|
|
1034
1290
|
/**
|
|
1035
1291
|
* Setup CLI isolation for a worktree
|
|
@@ -1044,7 +1300,7 @@ var CLIIsolationManager = class {
|
|
|
1044
1300
|
async setupCLIIsolation(worktreePath, identifier, binEntries) {
|
|
1045
1301
|
await this.buildProject(worktreePath);
|
|
1046
1302
|
await this.verifyBinTargets(worktreePath, binEntries);
|
|
1047
|
-
await
|
|
1303
|
+
await fs5.ensureDir(this.iloomBinDir);
|
|
1048
1304
|
const symlinkNames = await this.createVersionedSymlinks(
|
|
1049
1305
|
worktreePath,
|
|
1050
1306
|
identifier,
|
|
@@ -1074,13 +1330,13 @@ var CLIIsolationManager = class {
|
|
|
1074
1330
|
*/
|
|
1075
1331
|
async verifyBinTargets(worktreePath, binEntries) {
|
|
1076
1332
|
for (const binPath of Object.values(binEntries)) {
|
|
1077
|
-
const targetPath =
|
|
1078
|
-
const exists = await
|
|
1333
|
+
const targetPath = path4.resolve(worktreePath, binPath);
|
|
1334
|
+
const exists = await fs5.pathExists(targetPath);
|
|
1079
1335
|
if (!exists) {
|
|
1080
1336
|
throw new Error(`Bin target does not exist: ${targetPath}`);
|
|
1081
1337
|
}
|
|
1082
1338
|
try {
|
|
1083
|
-
await
|
|
1339
|
+
await fs5.access(targetPath, fs5.constants.X_OK);
|
|
1084
1340
|
} catch {
|
|
1085
1341
|
}
|
|
1086
1342
|
}
|
|
@@ -1096,9 +1352,9 @@ var CLIIsolationManager = class {
|
|
|
1096
1352
|
const symlinkNames = [];
|
|
1097
1353
|
for (const [binName, binPath] of Object.entries(binEntries)) {
|
|
1098
1354
|
const versionedName = `${binName}-${identifier}`;
|
|
1099
|
-
const targetPath =
|
|
1100
|
-
const symlinkPath =
|
|
1101
|
-
await
|
|
1355
|
+
const targetPath = path4.resolve(worktreePath, binPath);
|
|
1356
|
+
const symlinkPath = path4.join(this.iloomBinDir, versionedName);
|
|
1357
|
+
await fs5.symlink(targetPath, symlinkPath);
|
|
1102
1358
|
logger.success(`CLI available: ${versionedName}`);
|
|
1103
1359
|
symlinkNames.push(versionedName);
|
|
1104
1360
|
}
|
|
@@ -1151,12 +1407,12 @@ var CLIIsolationManager = class {
|
|
|
1151
1407
|
async cleanupVersionedExecutables(identifier) {
|
|
1152
1408
|
const removed = [];
|
|
1153
1409
|
try {
|
|
1154
|
-
const files = await
|
|
1410
|
+
const files = await fs5.readdir(this.iloomBinDir);
|
|
1155
1411
|
for (const file of files) {
|
|
1156
1412
|
if (this.matchesIdentifier(file, identifier)) {
|
|
1157
|
-
const symlinkPath =
|
|
1413
|
+
const symlinkPath = path4.join(this.iloomBinDir, file);
|
|
1158
1414
|
try {
|
|
1159
|
-
await
|
|
1415
|
+
await fs5.unlink(symlinkPath);
|
|
1160
1416
|
removed.push(file);
|
|
1161
1417
|
} catch (error) {
|
|
1162
1418
|
const isEnoent = error && typeof error === "object" && "code" in error && error.code === "ENOENT";
|
|
@@ -1192,15 +1448,15 @@ var CLIIsolationManager = class {
|
|
|
1192
1448
|
async findOrphanedSymlinks() {
|
|
1193
1449
|
const orphaned = [];
|
|
1194
1450
|
try {
|
|
1195
|
-
const files = await
|
|
1451
|
+
const files = await fs5.readdir(this.iloomBinDir);
|
|
1196
1452
|
for (const file of files) {
|
|
1197
|
-
const symlinkPath =
|
|
1453
|
+
const symlinkPath = path4.join(this.iloomBinDir, file);
|
|
1198
1454
|
try {
|
|
1199
|
-
const stats = await
|
|
1455
|
+
const stats = await fs5.lstat(symlinkPath);
|
|
1200
1456
|
if (stats.isSymbolicLink()) {
|
|
1201
|
-
const target = await
|
|
1457
|
+
const target = await fs5.readlink(symlinkPath);
|
|
1202
1458
|
try {
|
|
1203
|
-
await
|
|
1459
|
+
await fs5.access(target);
|
|
1204
1460
|
} catch {
|
|
1205
1461
|
orphaned.push({
|
|
1206
1462
|
name: file,
|
|
@@ -1235,7 +1491,7 @@ var CLIIsolationManager = class {
|
|
|
1235
1491
|
let removedCount = 0;
|
|
1236
1492
|
for (const symlink of orphaned) {
|
|
1237
1493
|
try {
|
|
1238
|
-
await
|
|
1494
|
+
await fs5.unlink(symlink.path);
|
|
1239
1495
|
removedCount++;
|
|
1240
1496
|
logger.success(`Removed orphaned symlink: ${symlink.name}`);
|
|
1241
1497
|
} catch (error) {
|
|
@@ -1261,7 +1517,7 @@ var CLIIsolationManager = class {
|
|
|
1261
1517
|
};
|
|
1262
1518
|
|
|
1263
1519
|
// src/lib/DatabaseManager.ts
|
|
1264
|
-
import
|
|
1520
|
+
import fs6 from "fs-extra";
|
|
1265
1521
|
var logger3 = createLogger({ prefix: "\u{1F5C2}\uFE0F" });
|
|
1266
1522
|
var DatabaseManager = class {
|
|
1267
1523
|
constructor(provider, environment, databaseUrlEnvVarName = "DATABASE_URL") {
|
|
@@ -1451,7 +1707,7 @@ var DatabaseManager = class {
|
|
|
1451
1707
|
const hasConfiguredVar = await hasVariableInAnyEnvFile(
|
|
1452
1708
|
workspacePath,
|
|
1453
1709
|
this.databaseUrlEnvVarName,
|
|
1454
|
-
async (p) =>
|
|
1710
|
+
async (p) => fs6.pathExists(p),
|
|
1455
1711
|
async (p, v) => this.environment.getEnvVariable(p, v)
|
|
1456
1712
|
);
|
|
1457
1713
|
if (hasConfiguredVar) {
|
|
@@ -1471,7 +1727,7 @@ var DatabaseManager = class {
|
|
|
1471
1727
|
const hasDefaultVar = await hasVariableInAnyEnvFile(
|
|
1472
1728
|
workspacePath,
|
|
1473
1729
|
"DATABASE_URL",
|
|
1474
|
-
async (p) =>
|
|
1730
|
+
async (p) => fs6.pathExists(p),
|
|
1475
1731
|
async (p, v) => this.environment.getEnvVariable(p, v)
|
|
1476
1732
|
);
|
|
1477
1733
|
if (hasDefaultVar) {
|
|
@@ -1490,7 +1746,7 @@ var DatabaseManager = class {
|
|
|
1490
1746
|
};
|
|
1491
1747
|
|
|
1492
1748
|
// src/lib/ResourceCleanup.ts
|
|
1493
|
-
import
|
|
1749
|
+
import path5 from "path";
|
|
1494
1750
|
var ResourceCleanup = class {
|
|
1495
1751
|
constructor(gitWorktree, processManager, database, cliIsolation, settingsManager) {
|
|
1496
1752
|
this.gitWorktree = gitWorktree;
|
|
@@ -1498,6 +1754,7 @@ var ResourceCleanup = class {
|
|
|
1498
1754
|
this.database = database;
|
|
1499
1755
|
this.cliIsolation = cliIsolation;
|
|
1500
1756
|
this.settingsManager = settingsManager ?? new SettingsManager();
|
|
1757
|
+
this.metadataManager = new MetadataManager();
|
|
1501
1758
|
}
|
|
1502
1759
|
/**
|
|
1503
1760
|
* Cleanup a worktree and associated resources
|
|
@@ -1585,7 +1842,7 @@ ${blockerMessage}`);
|
|
|
1585
1842
|
}
|
|
1586
1843
|
let databaseConfig = null;
|
|
1587
1844
|
if (!options.keepDatabase && worktree) {
|
|
1588
|
-
const envFilePath =
|
|
1845
|
+
const envFilePath = path5.join(worktree.path, ".env");
|
|
1589
1846
|
try {
|
|
1590
1847
|
const shouldCleanup = this.database ? await this.database.shouldUseDatabaseBranching(envFilePath) : false;
|
|
1591
1848
|
databaseConfig = { shouldCleanup, envFilePath };
|
|
@@ -1628,6 +1885,8 @@ ${blockerMessage}`);
|
|
|
1628
1885
|
success: true,
|
|
1629
1886
|
message: `Worktree removed: ${worktree.path}`
|
|
1630
1887
|
});
|
|
1888
|
+
await this.metadataManager.deleteMetadata(worktree.path);
|
|
1889
|
+
logger.debug(`Metadata file cleanup attempted for: ${worktree.path}`);
|
|
1631
1890
|
} catch (error) {
|
|
1632
1891
|
const err = error instanceof Error ? error : new Error("Unknown error");
|
|
1633
1892
|
errors.push(err);
|
|
@@ -1888,7 +2147,7 @@ ${blockerMessage}`);
|
|
|
1888
2147
|
return false;
|
|
1889
2148
|
}
|
|
1890
2149
|
try {
|
|
1891
|
-
const envFilePath =
|
|
2150
|
+
const envFilePath = path5.join(worktreePath, ".env");
|
|
1892
2151
|
const shouldCleanup = await this.database.shouldUseDatabaseBranching(envFilePath);
|
|
1893
2152
|
let cwd;
|
|
1894
2153
|
try {
|
|
@@ -2023,10 +2282,11 @@ Please resolve before cleanup - you have some options:
|
|
|
2023
2282
|
};
|
|
2024
2283
|
|
|
2025
2284
|
export {
|
|
2285
|
+
MetadataManager,
|
|
2026
2286
|
LoomManager,
|
|
2027
2287
|
EnvironmentManager,
|
|
2028
2288
|
CLIIsolationManager,
|
|
2029
2289
|
DatabaseManager,
|
|
2030
2290
|
ResourceCleanup
|
|
2031
2291
|
};
|
|
2032
|
-
//# sourceMappingURL=chunk-
|
|
2292
|
+
//# sourceMappingURL=chunk-2IJEMXOB.js.map
|