@iloom/cli 0.3.2 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/README.md +3 -3
  2. package/dist/{BranchNamingService-JYO746H7.js → BranchNamingService-A77VI6AI.js} +2 -2
  3. package/dist/ClaudeContextManager-BN7RE5ZQ.js +15 -0
  4. package/dist/ClaudeService-DLYLJUPA.js +14 -0
  5. package/dist/{GitHubService-UAMH7DMF.js → GitHubService-FZHHBOFG.js} +3 -3
  6. package/dist/{LoomLauncher-FVZECY3C.js → LoomLauncher-ZV3ZZIBA.js} +23 -20
  7. package/dist/LoomLauncher-ZV3ZZIBA.js.map +1 -0
  8. package/dist/{PromptTemplateManager-A52RUAMS.js → PromptTemplateManager-6HH3PVXV.js} +2 -2
  9. package/dist/README.md +3 -3
  10. package/dist/{SettingsManager-MTVX57WR.js → SettingsManager-I2LRCW2A.js} +2 -2
  11. package/dist/{SettingsMigrationManager-AGIIIPDQ.js → SettingsMigrationManager-TJ7UWZG5.js} +3 -3
  12. package/dist/agents/iloom-issue-complexity-evaluator.md +18 -3
  13. package/dist/agents/iloom-issue-enhancer.md +1 -1
  14. package/dist/{chunk-OAP6SASD.js → chunk-2CXREBLZ.js} +2 -2
  15. package/dist/{chunk-KG343GSG.js → chunk-2IJEMXOB.js} +359 -99
  16. package/dist/chunk-2IJEMXOB.js.map +1 -0
  17. package/dist/{chunk-FOV7RRQ2.js → chunk-2MAIX45J.js} +4 -4
  18. package/dist/{chunk-5NTD4MCZ.js → chunk-5Q3NDNNV.js} +29 -5
  19. package/dist/chunk-5Q3NDNNV.js.map +1 -0
  20. package/dist/{chunk-GVICXJHW.js → chunk-5VK4NRSF.js} +2 -2
  21. package/dist/{chunk-C7ASXK6J.js → chunk-AKUJXDNW.js} +2 -2
  22. package/dist/{chunk-UCZ24SUE.js → chunk-CDZERT7Z.js} +21 -9
  23. package/dist/chunk-CDZERT7Z.js.map +1 -0
  24. package/dist/{chunk-JKXJ7BGL.js → chunk-CE26YH2U.js} +42 -3
  25. package/dist/chunk-CE26YH2U.js.map +1 -0
  26. package/dist/{chunk-ZZZWQGTS.js → chunk-CFFQ2Z7A.js} +74 -75
  27. package/dist/chunk-CFFQ2Z7A.js.map +1 -0
  28. package/dist/{chunk-FXMLNKLT.js → chunk-DLHA5VQ3.js} +174 -5
  29. package/dist/chunk-DLHA5VQ3.js.map +1 -0
  30. package/dist/{chunk-GOG3ZB7O.js → chunk-IFB4Z76W.js} +23 -4
  31. package/dist/chunk-IFB4Z76W.js.map +1 -0
  32. package/dist/{chunk-F4ENT6AC.js → chunk-M7JJCX53.js} +2 -2
  33. package/dist/{chunk-KLBYVHPK.js → chunk-OSCLCMDG.js} +2 -2
  34. package/dist/{chunk-ZLIHIUDQ.js → chunk-OYF4VIFI.js} +2 -2
  35. package/dist/{chunk-A2P7NZTB.js → chunk-PGPI5LR4.js} +4 -4
  36. package/dist/{chunk-WEN5C5DM.js → chunk-RIEO2WML.js} +4 -1
  37. package/dist/chunk-RIEO2WML.js.map +1 -0
  38. package/dist/{chunk-UERERX6M.js → chunk-SUOXY5WJ.js} +2 -2
  39. package/dist/{init-7IIM35LQ.js → chunk-UAN4A3YU.js} +343 -46
  40. package/dist/chunk-UAN4A3YU.js.map +1 -0
  41. package/dist/{claude-KIZYXTSG.js → claude-W52VKI6L.js} +2 -2
  42. package/dist/{cleanup-6WYUD5SN.js → cleanup-H4VXU3C3.js} +12 -12
  43. package/dist/cli.js +188 -91
  44. package/dist/cli.js.map +1 -1
  45. package/dist/{color-ZVALX37U.js → color-F7RU6B6Z.js} +10 -4
  46. package/dist/{contribute-7YJHZTO7.js → contribute-Y7IQV5QY.js} +4 -4
  47. package/dist/{feedback-752MGDPG.js → feedback-XTUCKJNT.js} +11 -9
  48. package/dist/{feedback-752MGDPG.js.map → feedback-XTUCKJNT.js.map} +1 -1
  49. package/dist/{git-TAFVDB3J.js → git-IYA53VIC.js} +7 -3
  50. package/dist/{ignite-OAMDUN27.js → ignite-T74RYXCA.js} +19 -71
  51. package/dist/ignite-T74RYXCA.js.map +1 -0
  52. package/dist/index.d.ts +52 -8
  53. package/dist/index.js +132 -84
  54. package/dist/index.js.map +1 -1
  55. package/dist/init-4FHTAM3F.js +19 -0
  56. package/dist/{neon-helpers-5HBYO2VP.js → neon-helpers-77PBPGJ5.js} +3 -3
  57. package/dist/{open-FCHKQ77R.js → open-UMXANW5S.js} +4 -4
  58. package/dist/{prompt-7INJ7YRU.js → prompt-QALMYTVC.js} +4 -2
  59. package/dist/prompt-QALMYTVC.js.map +1 -0
  60. package/dist/prompts/init-prompt.txt +82 -8
  61. package/dist/prompts/issue-prompt.txt +18 -11
  62. package/dist/{rebase-Q7WXK566.js → rebase-VJ2VKR6R.js} +11 -11
  63. package/dist/rebase-VJ2VKR6R.js.map +1 -0
  64. package/dist/{run-SJPM6YRI.js → run-MJYY4PUT.js} +4 -4
  65. package/dist/schema/settings.schema.json +21 -3
  66. package/dist/{test-git-DEUE656D.js → test-git-IT5EWQ5C.js} +3 -3
  67. package/dist/{test-prefix-Y6Z6ZHSF.js → test-prefix-NPWDPUUH.js} +3 -3
  68. package/package.json +2 -1
  69. package/dist/ClaudeContextManager-5WPRJIIW.js +0 -15
  70. package/dist/ClaudeService-Z4KA7QOW.js +0 -14
  71. package/dist/LoomLauncher-FVZECY3C.js.map +0 -1
  72. package/dist/chunk-5NTD4MCZ.js.map +0 -1
  73. package/dist/chunk-FXMLNKLT.js.map +0 -1
  74. package/dist/chunk-GOG3ZB7O.js.map +0 -1
  75. package/dist/chunk-JKXJ7BGL.js.map +0 -1
  76. package/dist/chunk-KG343GSG.js.map +0 -1
  77. package/dist/chunk-PRE7KTM4.js +0 -302
  78. package/dist/chunk-PRE7KTM4.js.map +0 -1
  79. package/dist/chunk-UCZ24SUE.js.map +0 -1
  80. package/dist/chunk-WEN5C5DM.js.map +0 -1
  81. package/dist/chunk-ZZZWQGTS.js.map +0 -1
  82. package/dist/ignite-OAMDUN27.js.map +0 -1
  83. package/dist/init-7IIM35LQ.js.map +0 -1
  84. package/dist/rebase-Q7WXK566.js.map +0 -1
  85. /package/dist/{BranchNamingService-JYO746H7.js.map → BranchNamingService-A77VI6AI.js.map} +0 -0
  86. /package/dist/{ClaudeContextManager-5WPRJIIW.js.map → ClaudeContextManager-BN7RE5ZQ.js.map} +0 -0
  87. /package/dist/{ClaudeService-Z4KA7QOW.js.map → ClaudeService-DLYLJUPA.js.map} +0 -0
  88. /package/dist/{GitHubService-UAMH7DMF.js.map → GitHubService-FZHHBOFG.js.map} +0 -0
  89. /package/dist/{PromptTemplateManager-A52RUAMS.js.map → PromptTemplateManager-6HH3PVXV.js.map} +0 -0
  90. /package/dist/{SettingsManager-MTVX57WR.js.map → SettingsManager-I2LRCW2A.js.map} +0 -0
  91. /package/dist/{SettingsMigrationManager-AGIIIPDQ.js.map → SettingsMigrationManager-TJ7UWZG5.js.map} +0 -0
  92. /package/dist/{chunk-OAP6SASD.js.map → chunk-2CXREBLZ.js.map} +0 -0
  93. /package/dist/{chunk-FOV7RRQ2.js.map → chunk-2MAIX45J.js.map} +0 -0
  94. /package/dist/{chunk-GVICXJHW.js.map → chunk-5VK4NRSF.js.map} +0 -0
  95. /package/dist/{chunk-C7ASXK6J.js.map → chunk-AKUJXDNW.js.map} +0 -0
  96. /package/dist/{chunk-F4ENT6AC.js.map → chunk-M7JJCX53.js.map} +0 -0
  97. /package/dist/{chunk-KLBYVHPK.js.map → chunk-OSCLCMDG.js.map} +0 -0
  98. /package/dist/{chunk-ZLIHIUDQ.js.map → chunk-OYF4VIFI.js.map} +0 -0
  99. /package/dist/{chunk-A2P7NZTB.js.map → chunk-PGPI5LR4.js.map} +0 -0
  100. /package/dist/{chunk-UERERX6M.js.map → chunk-SUOXY5WJ.js.map} +0 -0
  101. /package/dist/{claude-KIZYXTSG.js.map → claude-W52VKI6L.js.map} +0 -0
  102. /package/dist/{cleanup-6WYUD5SN.js.map → cleanup-H4VXU3C3.js.map} +0 -0
  103. /package/dist/{color-ZVALX37U.js.map → color-F7RU6B6Z.js.map} +0 -0
  104. /package/dist/{contribute-7YJHZTO7.js.map → contribute-Y7IQV5QY.js.map} +0 -0
  105. /package/dist/{git-TAFVDB3J.js.map → git-IYA53VIC.js.map} +0 -0
  106. /package/dist/{neon-helpers-5HBYO2VP.js.map → init-4FHTAM3F.js.map} +0 -0
  107. /package/dist/{prompt-7INJ7YRU.js.map → neon-helpers-77PBPGJ5.js.map} +0 -0
  108. /package/dist/{open-FCHKQ77R.js.map → open-UMXANW5S.js.map} +0 -0
  109. /package/dist/{run-SJPM6YRI.js.map → run-MJYY4PUT.js.map} +0 -0
  110. /package/dist/{test-git-DEUE656D.js.map → test-git-IT5EWQ5C.js.map} +0 -0
  111. /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-5NTD4MCZ.js";
21
+ } from "./chunk-5Q3NDNNV.js";
22
22
  import {
23
23
  SettingsManager
24
- } from "./chunk-UCZ24SUE.js";
24
+ } from "./chunk-CDZERT7Z.js";
25
25
  import {
26
26
  calculateForegroundColor,
27
27
  generateColorFromBranchName,
28
28
  hexToRgb,
29
29
  lightenColor,
30
- rgbToHex
31
- } from "./chunk-ZZZWQGTS.js";
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 path2 from "path";
47
- import fs2 from "fs-extra";
221
+ import path3 from "path";
222
+ import fs3 from "fs-extra";
48
223
 
49
224
  // src/lib/VSCodeIntegration.ts
50
- import fs from "fs-extra";
51
- import path from "path";
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 = path.join(workspacePath, ".vscode");
62
- const settingsPath = path.join(vscodeDir, "settings.json");
236
+ const vscodeDir = path2.join(workspacePath, ".vscode");
237
+ const settingsPath = path2.join(vscodeDir, "settings.json");
63
238
  try {
64
- await fs.ensureDir(vscodeDir);
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 fs.pathExists(settingsPath)) {
259
+ if (!await fs2.pathExists(settingsPath)) {
85
260
  return {};
86
261
  }
87
- const content = await fs.readFile(settingsPath, "utf8");
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 fs.pathExists(settingsPath)) {
112
- const existingContent = await fs.readFile(settingsPath, "utf8");
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 fs.writeFile(tempPath, content, "utf8");
123
- await fs.rename(tempPath, settingsPath);
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 = path2.join(loomPath, ".env");
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) => fs2.pathExists(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
- path2.join(worktreePath, targetFile),
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
- if (!((_f = input.options) == null ? void 0 : _f.skipColorSync)) {
302
- try {
303
- await this.applyColorSynchronization(worktreePath, branchName);
304
- } catch (error) {
305
- logger.warn(
306
- `Failed to apply color synchronization: ${error instanceof Error ? error.message : "Unknown error"}`,
307
- error
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 = ((_g = input.options) == null ? void 0 : _g.enableClaude) !== false;
325
- const enableCode = ((_h = input.options) == null ? void 0 : _h.enableCode) !== false;
326
- const enableDevServer = ((_i = input.options) == null ? void 0 : _i.enableDevServer) !== false;
327
- const enableTerminal = ((_j = input.options) == null ? void 0 : _j.enableTerminal) ?? false;
328
- const oneShot = ((_k = input.options) == null ? void 0 : _k.oneShot) ?? "default";
329
- const setArguments = (_l = input.options) == null ? void 0 : _l.setArguments;
330
- const executablePath = (_m = input.options) == null ? void 0 : _m.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-FVZECY3C.js");
333
- const { ClaudeContextManager } = await import("./ClaudeContextManager-5WPRJIIW.js");
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-TAFVDB3J.js");
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 = path2.join(mainWorkspacePath, pattern);
574
- const worktreeEnvPath = path2.join(worktreePath, pattern);
575
- if (!await fs2.pathExists(mainEnvPath)) {
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 fs2.pathExists(worktreeEnvPath)) {
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 = path2.join(process.cwd(), ".iloom", "settings.local.json");
798
+ const mainSettingsLocalPath = path3.join(process.cwd(), ".iloom", "settings.local.json");
601
799
  try {
602
- const worktreeIloomDir = path2.join(worktreePath, ".iloom");
603
- await fs2.ensureDir(worktreeIloomDir);
604
- const worktreeSettingsLocalPath = path2.join(worktreeIloomDir, "settings.local.json");
605
- if (await fs2.pathExists(worktreeSettingsLocalPath)) {
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 fs2.readFile(worktreeSettingsLocalPath, "utf8");
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 fs2.writeFile(worktreeSettingsLocalPath, JSON.stringify(updatedSettings, null, 2));
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 = path2.join(worktreePath, ".env.local");
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
- const colorData = generateColorFromBranchName(branchName);
693
- const vscode = new VSCodeIntegration();
694
- await vscode.setTitleBarColor(worktreePath, colorData.hex);
695
- logger.info(`Applied VSCode title bar color: ${colorData.hex} for branch: ${branchName}`);
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-FVZECY3C.js");
785
- const { ClaudeContextManager } = await import("./ClaudeContextManager-5WPRJIIW.js");
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 fs3 from "fs-extra";
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 fs3.pathExists(filePath);
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 fs3.writeFile(filePath, content, "utf8");
1110
+ await fs4.writeFile(filePath, content, "utf8");
855
1111
  logger2.success(`${filePath} created with ${key}`);
856
1112
  return;
857
1113
  }
858
- const existingContent = await fs3.readFile(filePath, "utf8");
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 fs3.writeFile(filePath, newContent, "utf8");
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 fs3.readFile(filePath, "utf8");
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 fs3.pathExists(source);
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 fs3.copy(source, destination, { overwrite: false });
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 fs3.readFile(filePath, "utf8");
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 fs3.copy(filePath, backupPath);
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 fs4 from "fs-extra";
1028
- import path3 from "path";
1029
- import os from "os";
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 = path3.join(os.homedir(), ".iloom", "bin");
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 fs4.ensureDir(this.iloomBinDir);
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 = path3.resolve(worktreePath, binPath);
1078
- const exists = await fs4.pathExists(targetPath);
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 fs4.access(targetPath, fs4.constants.X_OK);
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 = path3.resolve(worktreePath, binPath);
1100
- const symlinkPath = path3.join(this.iloomBinDir, versionedName);
1101
- await fs4.symlink(targetPath, symlinkPath);
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 fs4.readdir(this.iloomBinDir);
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 = path3.join(this.iloomBinDir, file);
1413
+ const symlinkPath = path4.join(this.iloomBinDir, file);
1158
1414
  try {
1159
- await fs4.unlink(symlinkPath);
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 fs4.readdir(this.iloomBinDir);
1451
+ const files = await fs5.readdir(this.iloomBinDir);
1196
1452
  for (const file of files) {
1197
- const symlinkPath = path3.join(this.iloomBinDir, file);
1453
+ const symlinkPath = path4.join(this.iloomBinDir, file);
1198
1454
  try {
1199
- const stats = await fs4.lstat(symlinkPath);
1455
+ const stats = await fs5.lstat(symlinkPath);
1200
1456
  if (stats.isSymbolicLink()) {
1201
- const target = await fs4.readlink(symlinkPath);
1457
+ const target = await fs5.readlink(symlinkPath);
1202
1458
  try {
1203
- await fs4.access(target);
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 fs4.unlink(symlink.path);
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 fs5 from "fs-extra";
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) => fs5.pathExists(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) => fs5.pathExists(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 path4 from "path";
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 = path4.join(worktree.path, ".env");
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 = path4.join(worktreePath, ".env");
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-KG343GSG.js.map
2292
+ //# sourceMappingURL=chunk-2IJEMXOB.js.map