@iloom/cli 0.3.1 → 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.
- package/README.md +8 -6
- package/dist/{BranchNamingService-OMWKUYMM.js → BranchNamingService-A77VI6AI.js} +2 -2
- package/dist/ClaudeContextManager-BN7RE5ZQ.js +15 -0
- package/dist/ClaudeService-DLYLJUPA.js +14 -0
- package/dist/{GitHubService-EBOETDIW.js → GitHubService-FZHHBOFG.js} +3 -3
- package/dist/{LoomLauncher-JF7JZMTZ.js → LoomLauncher-ZV3ZZIBA.js} +40 -26
- package/dist/LoomLauncher-ZV3ZZIBA.js.map +1 -0
- package/dist/{PromptTemplateManager-A52RUAMS.js → PromptTemplateManager-6HH3PVXV.js} +2 -2
- package/dist/README.md +8 -6
- package/dist/{SettingsManager-ZCWJ56WP.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-TSKY3JI7.js → chunk-2CXREBLZ.js} +2 -2
- package/dist/{chunk-HBYZH6GD.js → chunk-2IJEMXOB.js} +431 -128
- package/dist/chunk-2IJEMXOB.js.map +1 -0
- package/dist/{chunk-IXKLYTWO.js → chunk-2MAIX45J.js} +8 -8
- package/dist/{chunk-4BGK7T6X.js → chunk-5Q3NDNNV.js} +48 -8
- package/dist/chunk-5Q3NDNNV.js.map +1 -0
- package/dist/{chunk-JQFO7QQN.js → chunk-5VK4NRSF.js} +3 -3
- package/dist/{chunk-JQFO7QQN.js.map → chunk-5VK4NRSF.js.map} +1 -1
- package/dist/{chunk-XPKDPZ5D.js → chunk-AKUJXDNW.js} +2 -2
- package/dist/{chunk-O5OH5MRX.js → chunk-CDZERT7Z.js} +23 -11
- 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-RO26VS3W.js → chunk-DLHA5VQ3.js} +174 -5
- package/dist/chunk-DLHA5VQ3.js.map +1 -0
- package/dist/{chunk-ZBQVSHVT.js → chunk-IFB4Z76W.js} +35 -10
- package/dist/chunk-IFB4Z76W.js.map +1 -0
- package/dist/{chunk-G2IEYOLQ.js → chunk-M7JJCX53.js} +17 -2
- package/dist/chunk-M7JJCX53.js.map +1 -0
- package/dist/{chunk-KLBYVHPK.js → chunk-OSCLCMDG.js} +2 -2
- package/dist/chunk-OXAM2WVC.js +68 -0
- package/dist/chunk-OXAM2WVC.js.map +1 -0
- package/dist/{chunk-ZWFBBPJI.js → chunk-OYF4VIFI.js} +5 -3
- package/dist/chunk-OYF4VIFI.js.map +1 -0
- package/dist/{chunk-U5QDY7ZD.js → chunk-PGPI5LR4.js} +8 -8
- package/dist/{chunk-WEN5C5DM.js → chunk-RIEO2WML.js} +4 -1
- package/dist/chunk-RIEO2WML.js.map +1 -0
- package/dist/{chunk-ZE74H5BR.js → chunk-RW54ZMBM.js} +26 -20
- package/dist/chunk-RW54ZMBM.js.map +1 -0
- package/dist/{chunk-INW24J2W.js → chunk-SUOXY5WJ.js} +2 -2
- package/dist/{init-L55Q73H4.js → chunk-UAN4A3YU.js} +345 -45
- package/dist/chunk-UAN4A3YU.js.map +1 -0
- package/dist/{chunk-IP7SMKIF.js → chunk-UJL4HI2R.js} +59 -60
- package/dist/chunk-UJL4HI2R.js.map +1 -0
- package/dist/{claude-LUZ35IMK.js → claude-W52VKI6L.js} +4 -2
- package/dist/{cleanup-3MONU4PU.js → cleanup-H4VXU3C3.js} +19 -17
- package/dist/{cleanup-3MONU4PU.js.map → cleanup-H4VXU3C3.js.map} +1 -1
- package/dist/cli.js +347 -114
- package/dist/cli.js.map +1 -1
- package/dist/{color-ZVALX37U.js → color-F7RU6B6Z.js} +10 -4
- package/dist/{contribute-UWJAGIG7.js → contribute-Y7IQV5QY.js} +4 -3
- package/dist/{contribute-UWJAGIG7.js.map → contribute-Y7IQV5QY.js.map} +1 -1
- package/dist/{feedback-W3BXTGIM.js → feedback-XTUCKJNT.js} +16 -12
- package/dist/{feedback-W3BXTGIM.js.map → feedback-XTUCKJNT.js.map} +1 -1
- package/dist/{git-34Z6QVDS.js → git-IYA53VIC.js} +9 -2
- package/dist/{ignite-KVJEFXNO.js → ignite-T74RYXCA.js} +25 -75
- package/dist/ignite-T74RYXCA.js.map +1 -0
- package/dist/index.d.ts +71 -14
- package/dist/index.js +407 -377
- package/dist/index.js.map +1 -1
- package/dist/init-4FHTAM3F.js +19 -0
- package/dist/mcp/issue-management-server.js +8 -1
- package/dist/mcp/issue-management-server.js.map +1 -1
- package/dist/{neon-helpers-WPUACUVC.js → neon-helpers-77PBPGJ5.js} +3 -3
- package/dist/{open-LNRZL3UU.js → open-UMXANW5S.js} +27 -14
- package/dist/open-UMXANW5S.js.map +1 -0
- package/dist/{prompt-7INJ7YRU.js → prompt-QALMYTVC.js} +4 -2
- package/dist/prompts/init-prompt.txt +89 -9
- package/dist/prompts/issue-prompt.txt +18 -11
- package/dist/{rebase-C4WNCVGM.js → rebase-VJ2VKR6R.js} +15 -13
- package/dist/rebase-VJ2VKR6R.js.map +1 -0
- package/dist/{run-IOGNIOYN.js → run-MJYY4PUT.js} +27 -14
- package/dist/run-MJYY4PUT.js.map +1 -0
- package/dist/schema/settings.schema.json +22 -4
- package/dist/{test-git-J7I5MFYH.js → test-git-IT5EWQ5C.js} +5 -5
- package/dist/{test-prefix-ZCONBCBX.js → test-prefix-NPWDPUUH.js} +5 -5
- package/dist/{test-tabs-RXDBZ6J7.js → test-tabs-PRMRSHKI.js} +3 -2
- package/dist/{test-tabs-RXDBZ6J7.js.map → test-tabs-PRMRSHKI.js.map} +1 -1
- package/package.json +2 -1
- package/dist/ClaudeContextManager-3VXA6UPR.js +0 -13
- package/dist/ClaudeService-6CPK43N4.js +0 -12
- package/dist/LoomLauncher-JF7JZMTZ.js.map +0 -1
- package/dist/chunk-4BGK7T6X.js.map +0 -1
- package/dist/chunk-4E4LD3QR.js +0 -302
- package/dist/chunk-4E4LD3QR.js.map +0 -1
- package/dist/chunk-G2IEYOLQ.js.map +0 -1
- package/dist/chunk-HBYZH6GD.js.map +0 -1
- package/dist/chunk-IP7SMKIF.js.map +0 -1
- package/dist/chunk-JKXJ7BGL.js.map +0 -1
- package/dist/chunk-O5OH5MRX.js.map +0 -1
- package/dist/chunk-RO26VS3W.js.map +0 -1
- package/dist/chunk-WEN5C5DM.js.map +0 -1
- package/dist/chunk-ZBQVSHVT.js.map +0 -1
- package/dist/chunk-ZE74H5BR.js.map +0 -1
- package/dist/chunk-ZWFBBPJI.js.map +0 -1
- package/dist/chunk-ZZZWQGTS.js.map +0 -1
- package/dist/ignite-KVJEFXNO.js.map +0 -1
- package/dist/init-L55Q73H4.js.map +0 -1
- package/dist/open-LNRZL3UU.js.map +0 -1
- package/dist/rebase-C4WNCVGM.js.map +0 -1
- package/dist/run-IOGNIOYN.js.map +0 -1
- package/dist/terminal-BIRBZ4AZ.js +0 -16
- /package/dist/{BranchNamingService-OMWKUYMM.js.map → BranchNamingService-A77VI6AI.js.map} +0 -0
- /package/dist/{ClaudeContextManager-3VXA6UPR.js.map → ClaudeContextManager-BN7RE5ZQ.js.map} +0 -0
- /package/dist/{ClaudeService-6CPK43N4.js.map → ClaudeService-DLYLJUPA.js.map} +0 -0
- /package/dist/{GitHubService-EBOETDIW.js.map → GitHubService-FZHHBOFG.js.map} +0 -0
- /package/dist/{PromptTemplateManager-A52RUAMS.js.map → PromptTemplateManager-6HH3PVXV.js.map} +0 -0
- /package/dist/{SettingsManager-ZCWJ56WP.js.map → SettingsManager-I2LRCW2A.js.map} +0 -0
- /package/dist/{SettingsMigrationManager-AGIIIPDQ.js.map → SettingsMigrationManager-TJ7UWZG5.js.map} +0 -0
- /package/dist/{chunk-TSKY3JI7.js.map → chunk-2CXREBLZ.js.map} +0 -0
- /package/dist/{chunk-IXKLYTWO.js.map → chunk-2MAIX45J.js.map} +0 -0
- /package/dist/{chunk-XPKDPZ5D.js.map → chunk-AKUJXDNW.js.map} +0 -0
- /package/dist/{chunk-KLBYVHPK.js.map → chunk-OSCLCMDG.js.map} +0 -0
- /package/dist/{chunk-U5QDY7ZD.js.map → chunk-PGPI5LR4.js.map} +0 -0
- /package/dist/{chunk-INW24J2W.js.map → chunk-SUOXY5WJ.js.map} +0 -0
- /package/dist/{claude-LUZ35IMK.js.map → claude-W52VKI6L.js.map} +0 -0
- /package/dist/{color-ZVALX37U.js.map → color-F7RU6B6Z.js.map} +0 -0
- /package/dist/{git-34Z6QVDS.js.map → git-IYA53VIC.js.map} +0 -0
- /package/dist/{neon-helpers-WPUACUVC.js.map → init-4FHTAM3F.js.map} +0 -0
- /package/dist/{prompt-7INJ7YRU.js.map → neon-helpers-77PBPGJ5.js.map} +0 -0
- /package/dist/{terminal-BIRBZ4AZ.js.map → prompt-QALMYTVC.js.map} +0 -0
- /package/dist/{test-git-J7I5MFYH.js.map → test-git-IT5EWQ5C.js.map} +0 -0
- /package/dist/{test-prefix-ZCONBCBX.js.map → test-prefix-NPWDPUUH.js.map} +0 -0
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
formatEnvLine,
|
|
4
|
-
loadEnvIntoProcess,
|
|
5
|
-
parseEnvFile,
|
|
6
|
-
validateEnvVariable
|
|
7
|
-
} from "./chunk-IP7SMKIF.js";
|
|
8
2
|
import {
|
|
9
3
|
calculatePortForBranch
|
|
10
4
|
} from "./chunk-VU3QMIP2.js";
|
|
@@ -16,36 +10,220 @@ import {
|
|
|
16
10
|
hasScript,
|
|
17
11
|
readPackageJson
|
|
18
12
|
} from "./chunk-2ZPFJQ3B.js";
|
|
19
|
-
import {
|
|
20
|
-
SettingsManager
|
|
21
|
-
} from "./chunk-O5OH5MRX.js";
|
|
22
13
|
import {
|
|
23
14
|
branchExists,
|
|
24
15
|
ensureRepositoryHasCommits,
|
|
25
16
|
executeGitCommand,
|
|
26
17
|
extractIssueNumber,
|
|
27
18
|
findMainWorktreePathWithSettings,
|
|
28
|
-
hasUncommittedChanges
|
|
29
|
-
|
|
19
|
+
hasUncommittedChanges,
|
|
20
|
+
isFileTrackedByGit
|
|
21
|
+
} from "./chunk-5Q3NDNNV.js";
|
|
22
|
+
import {
|
|
23
|
+
SettingsManager
|
|
24
|
+
} from "./chunk-CDZERT7Z.js";
|
|
30
25
|
import {
|
|
31
26
|
calculateForegroundColor,
|
|
32
27
|
generateColorFromBranchName,
|
|
33
28
|
hexToRgb,
|
|
34
29
|
lightenColor,
|
|
35
|
-
rgbToHex
|
|
36
|
-
|
|
30
|
+
rgbToHex,
|
|
31
|
+
selectDistinctColor
|
|
32
|
+
} from "./chunk-CFFQ2Z7A.js";
|
|
33
|
+
import {
|
|
34
|
+
findEnvFileForDatabaseUrl,
|
|
35
|
+
formatEnvLine,
|
|
36
|
+
hasVariableInAnyEnvFile,
|
|
37
|
+
loadEnvIntoProcess,
|
|
38
|
+
parseEnvFile,
|
|
39
|
+
validateEnvVariable
|
|
40
|
+
} from "./chunk-UJL4HI2R.js";
|
|
37
41
|
import {
|
|
38
42
|
createLogger,
|
|
39
43
|
logger
|
|
40
44
|
} from "./chunk-GEHQXLEI.js";
|
|
41
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
|
+
|
|
42
220
|
// src/lib/LoomManager.ts
|
|
43
|
-
import
|
|
44
|
-
import
|
|
221
|
+
import path3 from "path";
|
|
222
|
+
import fs3 from "fs-extra";
|
|
45
223
|
|
|
46
224
|
// src/lib/VSCodeIntegration.ts
|
|
47
|
-
import
|
|
48
|
-
import
|
|
225
|
+
import fs2 from "fs-extra";
|
|
226
|
+
import path2 from "path";
|
|
49
227
|
import { parse, modify, applyEdits } from "jsonc-parser";
|
|
50
228
|
var VSCodeIntegration = class {
|
|
51
229
|
/**
|
|
@@ -55,10 +233,10 @@ var VSCodeIntegration = class {
|
|
|
55
233
|
* @param hexColor - Hex color string (e.g., "#dcebf8")
|
|
56
234
|
*/
|
|
57
235
|
async setTitleBarColor(workspacePath, hexColor) {
|
|
58
|
-
const vscodeDir =
|
|
59
|
-
const settingsPath =
|
|
236
|
+
const vscodeDir = path2.join(workspacePath, ".vscode");
|
|
237
|
+
const settingsPath = path2.join(vscodeDir, "settings.json");
|
|
60
238
|
try {
|
|
61
|
-
await
|
|
239
|
+
await fs2.ensureDir(vscodeDir);
|
|
62
240
|
const settings = await this.readSettings(settingsPath);
|
|
63
241
|
const updatedSettings = this.mergeColorSettings(settings, hexColor);
|
|
64
242
|
await this.writeSettings(settingsPath, updatedSettings);
|
|
@@ -78,10 +256,10 @@ var VSCodeIntegration = class {
|
|
|
78
256
|
*/
|
|
79
257
|
async readSettings(settingsPath) {
|
|
80
258
|
try {
|
|
81
|
-
if (!await
|
|
259
|
+
if (!await fs2.pathExists(settingsPath)) {
|
|
82
260
|
return {};
|
|
83
261
|
}
|
|
84
|
-
const content = await
|
|
262
|
+
const content = await fs2.readFile(settingsPath, "utf8");
|
|
85
263
|
const errors = [];
|
|
86
264
|
const settings = parse(content, errors, { allowTrailingComma: true });
|
|
87
265
|
if (errors.length > 0) {
|
|
@@ -105,8 +283,8 @@ var VSCodeIntegration = class {
|
|
|
105
283
|
async writeSettings(settingsPath, settings) {
|
|
106
284
|
try {
|
|
107
285
|
let content;
|
|
108
|
-
if (await
|
|
109
|
-
const existingContent = await
|
|
286
|
+
if (await fs2.pathExists(settingsPath)) {
|
|
287
|
+
const existingContent = await fs2.readFile(settingsPath, "utf8");
|
|
110
288
|
if (existingContent.includes("//") || existingContent.includes("/*")) {
|
|
111
289
|
content = await this.modifyWithCommentsPreserved(existingContent, settings);
|
|
112
290
|
} else {
|
|
@@ -116,8 +294,8 @@ var VSCodeIntegration = class {
|
|
|
116
294
|
content = JSON.stringify(settings, null, 2) + "\n";
|
|
117
295
|
}
|
|
118
296
|
const tempPath = `${settingsPath}.tmp`;
|
|
119
|
-
await
|
|
120
|
-
await
|
|
297
|
+
await fs2.writeFile(tempPath, content, "utf8");
|
|
298
|
+
await fs2.rename(tempPath, settingsPath);
|
|
121
299
|
} catch (error) {
|
|
122
300
|
throw new Error(
|
|
123
301
|
`Failed to write settings.json: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
@@ -181,6 +359,7 @@ var LoomManager = class {
|
|
|
181
359
|
this.cliIsolation = cliIsolation;
|
|
182
360
|
this.settings = settings;
|
|
183
361
|
this.database = database;
|
|
362
|
+
this.metadataManager = new MetadataManager();
|
|
184
363
|
}
|
|
185
364
|
/**
|
|
186
365
|
* Get database branch name for a loom by reading its .env file
|
|
@@ -194,7 +373,7 @@ var LoomManager = class {
|
|
|
194
373
|
return null;
|
|
195
374
|
}
|
|
196
375
|
try {
|
|
197
|
-
const envFilePath =
|
|
376
|
+
const envFilePath = path3.join(loomPath, ".env");
|
|
198
377
|
const settings = await this.settings.loadSettings();
|
|
199
378
|
const databaseUrlVarName = ((_b = (_a = settings.capabilities) == null ? void 0 : _a.database) == null ? void 0 : _b.databaseUrlEnvVarName) ?? "DATABASE_URL";
|
|
200
379
|
const connectionString = await this.environment.getEnvVariable(envFilePath, databaseUrlVarName);
|
|
@@ -213,7 +392,7 @@ var LoomManager = class {
|
|
|
213
392
|
* NEW: Checks for existing worktrees and reuses them if found
|
|
214
393
|
*/
|
|
215
394
|
async createIloom(input) {
|
|
216
|
-
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;
|
|
217
396
|
logger.info("Fetching issue data...");
|
|
218
397
|
const issueData = await this.fetchIssueData(input);
|
|
219
398
|
if (input.type === "issue" || input.type === "pr" || input.type === "branch") {
|
|
@@ -249,16 +428,25 @@ var LoomManager = class {
|
|
|
249
428
|
try {
|
|
250
429
|
const connectionString = await this.database.createBranchIfConfigured(
|
|
251
430
|
branchName,
|
|
252
|
-
|
|
431
|
+
worktreePath,
|
|
432
|
+
// workspace path - checks all dotenv-flow files
|
|
253
433
|
void 0,
|
|
254
434
|
// cwd
|
|
255
435
|
(_e = input.parentLoom) == null ? void 0 : _e.databaseBranch
|
|
256
436
|
// fromBranch - use parent's database branch for child looms
|
|
257
437
|
);
|
|
258
438
|
if (connectionString) {
|
|
439
|
+
const varName = this.database.getConfiguredVariableName();
|
|
440
|
+
const targetFile = await findEnvFileForDatabaseUrl(
|
|
441
|
+
worktreePath,
|
|
442
|
+
varName,
|
|
443
|
+
isFileTrackedByGit,
|
|
444
|
+
async (p) => fs3.pathExists(p),
|
|
445
|
+
async (p, v) => this.environment.getEnvVariable(p, v)
|
|
446
|
+
);
|
|
259
447
|
await this.environment.setEnvVar(
|
|
260
|
-
|
|
261
|
-
|
|
448
|
+
path3.join(worktreePath, targetFile),
|
|
449
|
+
varName,
|
|
262
450
|
connectionString
|
|
263
451
|
);
|
|
264
452
|
logger.success("Database branch configured");
|
|
@@ -286,15 +474,17 @@ var LoomManager = class {
|
|
|
286
474
|
);
|
|
287
475
|
}
|
|
288
476
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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
|
+
);
|
|
298
488
|
}
|
|
299
489
|
if (input.type === "issue") {
|
|
300
490
|
try {
|
|
@@ -309,16 +499,16 @@ var LoomManager = class {
|
|
|
309
499
|
);
|
|
310
500
|
}
|
|
311
501
|
}
|
|
312
|
-
const enableClaude = ((
|
|
313
|
-
const enableCode = ((
|
|
314
|
-
const enableDevServer = ((
|
|
315
|
-
const enableTerminal = ((
|
|
316
|
-
const oneShot = ((
|
|
317
|
-
const setArguments = (
|
|
318
|
-
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;
|
|
319
509
|
if (enableClaude || enableCode || enableDevServer || enableTerminal) {
|
|
320
|
-
const { LoomLauncher } = await import("./LoomLauncher-
|
|
321
|
-
const { ClaudeContextManager } = await import("./ClaudeContextManager-
|
|
510
|
+
const { LoomLauncher } = await import("./LoomLauncher-ZV3ZZIBA.js");
|
|
511
|
+
const { ClaudeContextManager } = await import("./ClaudeContextManager-BN7RE5ZQ.js");
|
|
322
512
|
const claudeContext = new ClaudeContextManager(void 0, void 0, this.settings);
|
|
323
513
|
const launcher = new LoomLauncher(claudeContext, this.settings);
|
|
324
514
|
await launcher.launchLoom({
|
|
@@ -336,9 +526,25 @@ var LoomManager = class {
|
|
|
336
526
|
oneShot,
|
|
337
527
|
...setArguments && { setArguments },
|
|
338
528
|
...executablePath && { executablePath },
|
|
339
|
-
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
|
|
340
532
|
});
|
|
341
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);
|
|
342
548
|
const loom = {
|
|
343
549
|
id: this.generateLoomId(input),
|
|
344
550
|
path: worktreePath,
|
|
@@ -346,6 +552,7 @@ var LoomManager = class {
|
|
|
346
552
|
type: input.type,
|
|
347
553
|
identifier: input.identifier,
|
|
348
554
|
port,
|
|
555
|
+
description,
|
|
349
556
|
createdAt: /* @__PURE__ */ new Date(),
|
|
350
557
|
lastAccessed: /* @__PURE__ */ new Date(),
|
|
351
558
|
...databaseBranch !== void 0 && { databaseBranch },
|
|
@@ -376,6 +583,9 @@ var LoomManager = class {
|
|
|
376
583
|
*/
|
|
377
584
|
async listLooms() {
|
|
378
585
|
const worktrees = await this.gitWorktree.listWorktrees();
|
|
586
|
+
if (!worktrees) {
|
|
587
|
+
return [];
|
|
588
|
+
}
|
|
379
589
|
return await this.mapWorktreesToLooms(worktrees);
|
|
380
590
|
}
|
|
381
591
|
/**
|
|
@@ -420,7 +630,7 @@ var LoomManager = class {
|
|
|
420
630
|
async checkAndWarnChildLooms(branchName) {
|
|
421
631
|
let targetBranch = branchName;
|
|
422
632
|
if (!targetBranch) {
|
|
423
|
-
const { getCurrentBranch } = await import("./git-
|
|
633
|
+
const { getCurrentBranch } = await import("./git-IYA53VIC.js");
|
|
424
634
|
targetBranch = await getCurrentBranch();
|
|
425
635
|
}
|
|
426
636
|
if (!targetBranch) {
|
|
@@ -543,19 +753,39 @@ var LoomManager = class {
|
|
|
543
753
|
}
|
|
544
754
|
/**
|
|
545
755
|
* Copy user application environment files (.env) from main repo to worktree
|
|
756
|
+
* Copies all dotenv-flow patterns: .env, .env.local, .env.{NODE_ENV}, .env.{NODE_ENV}.local
|
|
757
|
+
* Only copies files that exist and are NOT tracked by git (tracked files exist via worktree)
|
|
546
758
|
* Always called regardless of project capabilities
|
|
547
759
|
*/
|
|
548
760
|
async copyEnvironmentFiles(worktreePath) {
|
|
549
|
-
const
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
}
|
|
555
|
-
|
|
761
|
+
const mainWorkspacePath = this.gitWorktree.workingDirectory;
|
|
762
|
+
const nodeEnv = process.env.DOTENV_FLOW_NODE_ENV ?? "development";
|
|
763
|
+
const envFilePatterns = [
|
|
764
|
+
".env",
|
|
765
|
+
".env.local",
|
|
766
|
+
`.env.${nodeEnv}`,
|
|
767
|
+
`.env.${nodeEnv}.local`
|
|
768
|
+
];
|
|
769
|
+
for (const pattern of envFilePatterns) {
|
|
770
|
+
try {
|
|
771
|
+
const mainEnvPath = path3.join(mainWorkspacePath, pattern);
|
|
772
|
+
const worktreeEnvPath = path3.join(worktreePath, pattern);
|
|
773
|
+
if (!await fs3.pathExists(mainEnvPath)) {
|
|
774
|
+
continue;
|
|
775
|
+
}
|
|
776
|
+
if (await isFileTrackedByGit(pattern, mainWorkspacePath)) {
|
|
777
|
+
logger.debug(`Skipping ${pattern} (tracked by git, already in worktree)`);
|
|
778
|
+
continue;
|
|
779
|
+
}
|
|
780
|
+
if (await fs3.pathExists(worktreeEnvPath)) {
|
|
781
|
+
logger.warn(`${pattern} already exists in worktree, skipping copy`);
|
|
782
|
+
continue;
|
|
783
|
+
}
|
|
784
|
+
await this.environment.copyIfExists(mainEnvPath, worktreeEnvPath);
|
|
785
|
+
logger.debug(`Copied ${pattern} to worktree`);
|
|
786
|
+
} catch (error) {
|
|
787
|
+
logger.warn(`Warning: Failed to copy ${pattern}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
556
788
|
}
|
|
557
|
-
} catch (error) {
|
|
558
|
-
logger.warn(`Warning: Failed to copy main .env file: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
559
789
|
}
|
|
560
790
|
}
|
|
561
791
|
/**
|
|
@@ -565,12 +795,12 @@ var LoomManager = class {
|
|
|
565
795
|
* @param parentBranchName Optional parent branch name for child looms (sets mainBranch)
|
|
566
796
|
*/
|
|
567
797
|
async copyIloomSettings(worktreePath, parentBranchName) {
|
|
568
|
-
const mainSettingsLocalPath =
|
|
798
|
+
const mainSettingsLocalPath = path3.join(process.cwd(), ".iloom", "settings.local.json");
|
|
569
799
|
try {
|
|
570
|
-
const worktreeIloomDir =
|
|
571
|
-
await
|
|
572
|
-
const worktreeSettingsLocalPath =
|
|
573
|
-
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)) {
|
|
574
804
|
logger.warn("settings.local.json already exists in worktree, skipping copy");
|
|
575
805
|
} else {
|
|
576
806
|
await this.environment.copyIfExists(mainSettingsLocalPath, worktreeSettingsLocalPath);
|
|
@@ -578,7 +808,7 @@ var LoomManager = class {
|
|
|
578
808
|
if (parentBranchName) {
|
|
579
809
|
let existingSettings = {};
|
|
580
810
|
try {
|
|
581
|
-
const content = await
|
|
811
|
+
const content = await fs3.readFile(worktreeSettingsLocalPath, "utf8");
|
|
582
812
|
existingSettings = JSON.parse(content);
|
|
583
813
|
} catch {
|
|
584
814
|
}
|
|
@@ -586,7 +816,7 @@ var LoomManager = class {
|
|
|
586
816
|
...existingSettings,
|
|
587
817
|
mainBranch: parentBranchName
|
|
588
818
|
};
|
|
589
|
-
await
|
|
819
|
+
await fs3.writeFile(worktreeSettingsLocalPath, JSON.stringify(updatedSettings, null, 2));
|
|
590
820
|
logger.info(`Set mainBranch to ${parentBranchName} for child loom`);
|
|
591
821
|
}
|
|
592
822
|
} catch (error) {
|
|
@@ -598,7 +828,7 @@ var LoomManager = class {
|
|
|
598
828
|
* Only called when project has web capabilities
|
|
599
829
|
*/
|
|
600
830
|
async setupPortForWeb(worktreePath, input, basePort) {
|
|
601
|
-
const envFilePath =
|
|
831
|
+
const envFilePath = path3.join(worktreePath, ".env.local");
|
|
602
832
|
const options = { basePort };
|
|
603
833
|
if (input.type === "issue") {
|
|
604
834
|
options.issueNumber = input.identifier;
|
|
@@ -655,16 +885,34 @@ var LoomManager = class {
|
|
|
655
885
|
/**
|
|
656
886
|
* Apply color synchronization to both VSCode and terminal
|
|
657
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)
|
|
658
895
|
*/
|
|
659
|
-
async applyColorSynchronization(worktreePath, branchName) {
|
|
660
|
-
|
|
661
|
-
const
|
|
662
|
-
|
|
663
|
-
|
|
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
|
+
}
|
|
664
911
|
}
|
|
665
912
|
/**
|
|
666
913
|
* Map worktrees to loom objects
|
|
667
914
|
* This is a simplified conversion - in production we'd store loom metadata
|
|
915
|
+
* Now reads metadata from MetadataManager (spec section 3.2)
|
|
668
916
|
*/
|
|
669
917
|
async mapWorktreesToLooms(worktrees) {
|
|
670
918
|
return await Promise.all(worktrees.map(async (wt) => {
|
|
@@ -677,6 +925,7 @@ var LoomManager = class {
|
|
|
677
925
|
type = "pr";
|
|
678
926
|
identifier = parseInt(wt.branch.replace("pr-", ""), 10);
|
|
679
927
|
}
|
|
928
|
+
const loomMetadata = await this.metadataManager.readMetadata(wt.path);
|
|
680
929
|
return {
|
|
681
930
|
id: `${type}-${identifier}`,
|
|
682
931
|
path: wt.path,
|
|
@@ -684,6 +933,7 @@ var LoomManager = class {
|
|
|
684
933
|
type,
|
|
685
934
|
identifier,
|
|
686
935
|
port: await this.calculatePort({ type, identifier, originalInput: "" }),
|
|
936
|
+
...(loomMetadata == null ? void 0 : loomMetadata.description) && { description: loomMetadata.description },
|
|
687
937
|
createdAt: /* @__PURE__ */ new Date(),
|
|
688
938
|
lastAccessed: /* @__PURE__ */ new Date()
|
|
689
939
|
};
|
|
@@ -712,7 +962,7 @@ var LoomManager = class {
|
|
|
712
962
|
* Ports: handle_existing_worktree() from bash script lines 168-215
|
|
713
963
|
*/
|
|
714
964
|
async reuseIloom(worktree, input, issueData) {
|
|
715
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
965
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
716
966
|
const worktreePath = worktree.path;
|
|
717
967
|
const branchName = worktree.branch;
|
|
718
968
|
this.loadMainEnvFile();
|
|
@@ -727,6 +977,25 @@ var LoomManager = class {
|
|
|
727
977
|
}
|
|
728
978
|
logger.info("Database branch assumed to be already configured for existing worktree");
|
|
729
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
|
+
}
|
|
730
999
|
if (input.type === "issue") {
|
|
731
1000
|
try {
|
|
732
1001
|
logger.info("Moving issue to In Progress...");
|
|
@@ -749,8 +1018,8 @@ var LoomManager = class {
|
|
|
749
1018
|
const executablePath = (_i = input.options) == null ? void 0 : _i.executablePath;
|
|
750
1019
|
if (enableClaude || enableCode || enableDevServer || enableTerminal) {
|
|
751
1020
|
logger.info("Launching workspace components...");
|
|
752
|
-
const { LoomLauncher } = await import("./LoomLauncher-
|
|
753
|
-
const { ClaudeContextManager } = await import("./ClaudeContextManager-
|
|
1021
|
+
const { LoomLauncher } = await import("./LoomLauncher-ZV3ZZIBA.js");
|
|
1022
|
+
const { ClaudeContextManager } = await import("./ClaudeContextManager-BN7RE5ZQ.js");
|
|
754
1023
|
const claudeContext = new ClaudeContextManager(void 0, void 0, this.settings);
|
|
755
1024
|
const launcher = new LoomLauncher(claudeContext, this.settings);
|
|
756
1025
|
await launcher.launchLoom({
|
|
@@ -768,9 +1037,27 @@ var LoomManager = class {
|
|
|
768
1037
|
oneShot,
|
|
769
1038
|
...setArguments && { setArguments },
|
|
770
1039
|
...executablePath && { executablePath },
|
|
771
|
-
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
|
|
772
1043
|
});
|
|
773
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
|
+
}
|
|
774
1061
|
const loom = {
|
|
775
1062
|
id: this.generateLoomId(input),
|
|
776
1063
|
path: worktreePath,
|
|
@@ -778,6 +1065,7 @@ var LoomManager = class {
|
|
|
778
1065
|
type: input.type,
|
|
779
1066
|
identifier: input.identifier,
|
|
780
1067
|
port,
|
|
1068
|
+
description,
|
|
781
1069
|
createdAt: /* @__PURE__ */ new Date(),
|
|
782
1070
|
// We don't have actual creation date, use now
|
|
783
1071
|
lastAccessed: /* @__PURE__ */ new Date(),
|
|
@@ -799,7 +1087,7 @@ var LoomManager = class {
|
|
|
799
1087
|
};
|
|
800
1088
|
|
|
801
1089
|
// src/lib/EnvironmentManager.ts
|
|
802
|
-
import
|
|
1090
|
+
import fs4 from "fs-extra";
|
|
803
1091
|
var logger2 = createLogger({ prefix: "\u{1F4DD}" });
|
|
804
1092
|
var EnvironmentManager = class {
|
|
805
1093
|
constructor() {
|
|
@@ -815,15 +1103,15 @@ var EnvironmentManager = class {
|
|
|
815
1103
|
if (!validation.valid) {
|
|
816
1104
|
throw new Error(validation.error ?? "Invalid variable name");
|
|
817
1105
|
}
|
|
818
|
-
const fileExists = await
|
|
1106
|
+
const fileExists = await fs4.pathExists(filePath);
|
|
819
1107
|
if (!fileExists) {
|
|
820
1108
|
logger2.info(`Creating ${filePath} with ${key}...`);
|
|
821
1109
|
const content = formatEnvLine(key, value);
|
|
822
|
-
await
|
|
1110
|
+
await fs4.writeFile(filePath, content, "utf8");
|
|
823
1111
|
logger2.success(`${filePath} created with ${key}`);
|
|
824
1112
|
return;
|
|
825
1113
|
}
|
|
826
|
-
const existingContent = await
|
|
1114
|
+
const existingContent = await fs4.readFile(filePath, "utf8");
|
|
827
1115
|
const envMap = parseEnvFile(existingContent);
|
|
828
1116
|
let backupPath;
|
|
829
1117
|
if (backup) {
|
|
@@ -860,7 +1148,7 @@ var EnvironmentManager = class {
|
|
|
860
1148
|
logger2.success(`${key} updated successfully`);
|
|
861
1149
|
}
|
|
862
1150
|
const newContent = newLines.join("\n");
|
|
863
|
-
await
|
|
1151
|
+
await fs4.writeFile(filePath, newContent, "utf8");
|
|
864
1152
|
return backupPath;
|
|
865
1153
|
}
|
|
866
1154
|
/**
|
|
@@ -868,7 +1156,7 @@ var EnvironmentManager = class {
|
|
|
868
1156
|
*/
|
|
869
1157
|
async readEnvFile(filePath) {
|
|
870
1158
|
try {
|
|
871
|
-
const content = await
|
|
1159
|
+
const content = await fs4.readFile(filePath, "utf8");
|
|
872
1160
|
return parseEnvFile(content);
|
|
873
1161
|
} catch (error) {
|
|
874
1162
|
logger2.debug(
|
|
@@ -891,12 +1179,12 @@ var EnvironmentManager = class {
|
|
|
891
1179
|
* @private
|
|
892
1180
|
*/
|
|
893
1181
|
async copyIfExists(source, destination) {
|
|
894
|
-
const sourceExists = await
|
|
1182
|
+
const sourceExists = await fs4.pathExists(source);
|
|
895
1183
|
if (!sourceExists) {
|
|
896
1184
|
logger2.debug(`Source file ${source} does not exist, skipping copy`);
|
|
897
1185
|
return;
|
|
898
1186
|
}
|
|
899
|
-
await
|
|
1187
|
+
await fs4.copy(source, destination, { overwrite: false });
|
|
900
1188
|
logger2.success(`Copied ${source} to ${destination}`);
|
|
901
1189
|
}
|
|
902
1190
|
/**
|
|
@@ -957,7 +1245,7 @@ var EnvironmentManager = class {
|
|
|
957
1245
|
*/
|
|
958
1246
|
async validateEnvFile(filePath) {
|
|
959
1247
|
try {
|
|
960
|
-
const content = await
|
|
1248
|
+
const content = await fs4.readFile(filePath, "utf8");
|
|
961
1249
|
const envMap = parseEnvFile(content);
|
|
962
1250
|
const errors = [];
|
|
963
1251
|
for (const [key, value] of envMap.entries()) {
|
|
@@ -985,19 +1273,19 @@ var EnvironmentManager = class {
|
|
|
985
1273
|
async createBackup(filePath) {
|
|
986
1274
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
987
1275
|
const backupPath = `${filePath}${this.backupSuffix}-${timestamp}`;
|
|
988
|
-
await
|
|
1276
|
+
await fs4.copy(filePath, backupPath);
|
|
989
1277
|
logger2.debug(`Created backup at ${backupPath}`);
|
|
990
1278
|
return backupPath;
|
|
991
1279
|
}
|
|
992
1280
|
};
|
|
993
1281
|
|
|
994
1282
|
// src/lib/CLIIsolationManager.ts
|
|
995
|
-
import
|
|
996
|
-
import
|
|
997
|
-
import
|
|
1283
|
+
import fs5 from "fs-extra";
|
|
1284
|
+
import path4 from "path";
|
|
1285
|
+
import os2 from "os";
|
|
998
1286
|
var CLIIsolationManager = class {
|
|
999
1287
|
constructor() {
|
|
1000
|
-
this.iloomBinDir =
|
|
1288
|
+
this.iloomBinDir = path4.join(os2.homedir(), ".iloom", "bin");
|
|
1001
1289
|
}
|
|
1002
1290
|
/**
|
|
1003
1291
|
* Setup CLI isolation for a worktree
|
|
@@ -1012,7 +1300,7 @@ var CLIIsolationManager = class {
|
|
|
1012
1300
|
async setupCLIIsolation(worktreePath, identifier, binEntries) {
|
|
1013
1301
|
await this.buildProject(worktreePath);
|
|
1014
1302
|
await this.verifyBinTargets(worktreePath, binEntries);
|
|
1015
|
-
await
|
|
1303
|
+
await fs5.ensureDir(this.iloomBinDir);
|
|
1016
1304
|
const symlinkNames = await this.createVersionedSymlinks(
|
|
1017
1305
|
worktreePath,
|
|
1018
1306
|
identifier,
|
|
@@ -1042,13 +1330,13 @@ var CLIIsolationManager = class {
|
|
|
1042
1330
|
*/
|
|
1043
1331
|
async verifyBinTargets(worktreePath, binEntries) {
|
|
1044
1332
|
for (const binPath of Object.values(binEntries)) {
|
|
1045
|
-
const targetPath =
|
|
1046
|
-
const exists = await
|
|
1333
|
+
const targetPath = path4.resolve(worktreePath, binPath);
|
|
1334
|
+
const exists = await fs5.pathExists(targetPath);
|
|
1047
1335
|
if (!exists) {
|
|
1048
1336
|
throw new Error(`Bin target does not exist: ${targetPath}`);
|
|
1049
1337
|
}
|
|
1050
1338
|
try {
|
|
1051
|
-
await
|
|
1339
|
+
await fs5.access(targetPath, fs5.constants.X_OK);
|
|
1052
1340
|
} catch {
|
|
1053
1341
|
}
|
|
1054
1342
|
}
|
|
@@ -1064,9 +1352,9 @@ var CLIIsolationManager = class {
|
|
|
1064
1352
|
const symlinkNames = [];
|
|
1065
1353
|
for (const [binName, binPath] of Object.entries(binEntries)) {
|
|
1066
1354
|
const versionedName = `${binName}-${identifier}`;
|
|
1067
|
-
const targetPath =
|
|
1068
|
-
const symlinkPath =
|
|
1069
|
-
await
|
|
1355
|
+
const targetPath = path4.resolve(worktreePath, binPath);
|
|
1356
|
+
const symlinkPath = path4.join(this.iloomBinDir, versionedName);
|
|
1357
|
+
await fs5.symlink(targetPath, symlinkPath);
|
|
1070
1358
|
logger.success(`CLI available: ${versionedName}`);
|
|
1071
1359
|
symlinkNames.push(versionedName);
|
|
1072
1360
|
}
|
|
@@ -1119,12 +1407,12 @@ var CLIIsolationManager = class {
|
|
|
1119
1407
|
async cleanupVersionedExecutables(identifier) {
|
|
1120
1408
|
const removed = [];
|
|
1121
1409
|
try {
|
|
1122
|
-
const files = await
|
|
1410
|
+
const files = await fs5.readdir(this.iloomBinDir);
|
|
1123
1411
|
for (const file of files) {
|
|
1124
1412
|
if (this.matchesIdentifier(file, identifier)) {
|
|
1125
|
-
const symlinkPath =
|
|
1413
|
+
const symlinkPath = path4.join(this.iloomBinDir, file);
|
|
1126
1414
|
try {
|
|
1127
|
-
await
|
|
1415
|
+
await fs5.unlink(symlinkPath);
|
|
1128
1416
|
removed.push(file);
|
|
1129
1417
|
} catch (error) {
|
|
1130
1418
|
const isEnoent = error && typeof error === "object" && "code" in error && error.code === "ENOENT";
|
|
@@ -1160,15 +1448,15 @@ var CLIIsolationManager = class {
|
|
|
1160
1448
|
async findOrphanedSymlinks() {
|
|
1161
1449
|
const orphaned = [];
|
|
1162
1450
|
try {
|
|
1163
|
-
const files = await
|
|
1451
|
+
const files = await fs5.readdir(this.iloomBinDir);
|
|
1164
1452
|
for (const file of files) {
|
|
1165
|
-
const symlinkPath =
|
|
1453
|
+
const symlinkPath = path4.join(this.iloomBinDir, file);
|
|
1166
1454
|
try {
|
|
1167
|
-
const stats = await
|
|
1455
|
+
const stats = await fs5.lstat(symlinkPath);
|
|
1168
1456
|
if (stats.isSymbolicLink()) {
|
|
1169
|
-
const target = await
|
|
1457
|
+
const target = await fs5.readlink(symlinkPath);
|
|
1170
1458
|
try {
|
|
1171
|
-
await
|
|
1459
|
+
await fs5.access(target);
|
|
1172
1460
|
} catch {
|
|
1173
1461
|
orphaned.push({
|
|
1174
1462
|
name: file,
|
|
@@ -1203,7 +1491,7 @@ var CLIIsolationManager = class {
|
|
|
1203
1491
|
let removedCount = 0;
|
|
1204
1492
|
for (const symlink of orphaned) {
|
|
1205
1493
|
try {
|
|
1206
|
-
await
|
|
1494
|
+
await fs5.unlink(symlink.path);
|
|
1207
1495
|
removedCount++;
|
|
1208
1496
|
logger.success(`Removed orphaned symlink: ${symlink.name}`);
|
|
1209
1497
|
} catch (error) {
|
|
@@ -1229,6 +1517,7 @@ var CLIIsolationManager = class {
|
|
|
1229
1517
|
};
|
|
1230
1518
|
|
|
1231
1519
|
// src/lib/DatabaseManager.ts
|
|
1520
|
+
import fs6 from "fs-extra";
|
|
1232
1521
|
var logger3 = createLogger({ prefix: "\u{1F5C2}\uFE0F" });
|
|
1233
1522
|
var DatabaseManager = class {
|
|
1234
1523
|
constructor(provider, environment, databaseUrlEnvVarName = "DATABASE_URL") {
|
|
@@ -1251,17 +1540,17 @@ var DatabaseManager = class {
|
|
|
1251
1540
|
* Check if database branching should be used
|
|
1252
1541
|
* Requires BOTH conditions:
|
|
1253
1542
|
* 1. Database provider is properly configured (checked via provider.isConfigured())
|
|
1254
|
-
* 2.
|
|
1543
|
+
* 2. Any dotenv-flow file contains the configured database URL variable
|
|
1255
1544
|
*/
|
|
1256
|
-
async shouldUseDatabaseBranching(
|
|
1545
|
+
async shouldUseDatabaseBranching(workspacePath) {
|
|
1257
1546
|
if (!this.provider.isConfigured()) {
|
|
1258
1547
|
logger3.debug("Skipping database branching: Database provider not configured");
|
|
1259
1548
|
return false;
|
|
1260
1549
|
}
|
|
1261
|
-
const hasDatabaseUrl = await this.hasDatabaseUrlInEnv(
|
|
1550
|
+
const hasDatabaseUrl = await this.hasDatabaseUrlInEnv(workspacePath);
|
|
1262
1551
|
if (!hasDatabaseUrl) {
|
|
1263
1552
|
logger3.debug(
|
|
1264
|
-
"Skipping database branching: configured database URL variable not found in
|
|
1553
|
+
"Skipping database branching: configured database URL variable not found in any env file"
|
|
1265
1554
|
);
|
|
1266
1555
|
return false;
|
|
1267
1556
|
}
|
|
@@ -1272,12 +1561,12 @@ var DatabaseManager = class {
|
|
|
1272
1561
|
* Returns connection string if branch was created, null if skipped
|
|
1273
1562
|
*
|
|
1274
1563
|
* @param branchName - Name of the branch to create
|
|
1275
|
-
* @param
|
|
1564
|
+
* @param workspacePath - Path to workspace for configuration checks (checks all dotenv-flow files)
|
|
1276
1565
|
* @param cwd - Optional working directory to run commands from
|
|
1277
1566
|
* @param fromBranch - Optional parent branch to create from (for child looms)
|
|
1278
1567
|
*/
|
|
1279
|
-
async createBranchIfConfigured(branchName,
|
|
1280
|
-
if (!await this.shouldUseDatabaseBranching(
|
|
1568
|
+
async createBranchIfConfigured(branchName, workspacePath, cwd, fromBranch) {
|
|
1569
|
+
if (!await this.shouldUseDatabaseBranching(workspacePath)) {
|
|
1281
1570
|
return null;
|
|
1282
1571
|
}
|
|
1283
1572
|
if (!await this.provider.isCliAvailable()) {
|
|
@@ -1404,19 +1693,24 @@ var DatabaseManager = class {
|
|
|
1404
1693
|
return null;
|
|
1405
1694
|
}
|
|
1406
1695
|
/**
|
|
1407
|
-
* Check if
|
|
1696
|
+
* Check if any dotenv-flow file has the configured database URL variable
|
|
1408
1697
|
* CRITICAL: If user explicitly configured a custom variable name (not default),
|
|
1409
|
-
* throw an error if it's missing from
|
|
1698
|
+
* throw an error if it's missing from all env files
|
|
1410
1699
|
*/
|
|
1411
|
-
async hasDatabaseUrlInEnv(
|
|
1700
|
+
async hasDatabaseUrlInEnv(workspacePath) {
|
|
1412
1701
|
try {
|
|
1413
|
-
const envMap = await this.environment.readEnvFile(envFilePath);
|
|
1414
1702
|
if (this.databaseUrlEnvVarName !== "DATABASE_URL") {
|
|
1415
1703
|
logger3.debug(`Looking for custom database URL variable: ${this.databaseUrlEnvVarName}`);
|
|
1416
1704
|
} else {
|
|
1417
1705
|
logger3.debug("Looking for default database URL variable: DATABASE_URL");
|
|
1418
1706
|
}
|
|
1419
|
-
|
|
1707
|
+
const hasConfiguredVar = await hasVariableInAnyEnvFile(
|
|
1708
|
+
workspacePath,
|
|
1709
|
+
this.databaseUrlEnvVarName,
|
|
1710
|
+
async (p) => fs6.pathExists(p),
|
|
1711
|
+
async (p, v) => this.environment.getEnvVariable(p, v)
|
|
1712
|
+
);
|
|
1713
|
+
if (hasConfiguredVar) {
|
|
1420
1714
|
if (this.databaseUrlEnvVarName !== "DATABASE_URL") {
|
|
1421
1715
|
logger3.debug(`\u2705 Found custom database URL variable: ${this.databaseUrlEnvVarName}`);
|
|
1422
1716
|
} else {
|
|
@@ -1425,20 +1719,25 @@ var DatabaseManager = class {
|
|
|
1425
1719
|
return true;
|
|
1426
1720
|
}
|
|
1427
1721
|
if (this.databaseUrlEnvVarName !== "DATABASE_URL") {
|
|
1428
|
-
logger3.debug(`\u274C Custom database URL variable '${this.databaseUrlEnvVarName}' not found in
|
|
1722
|
+
logger3.debug(`\u274C Custom database URL variable '${this.databaseUrlEnvVarName}' not found in any env file`);
|
|
1429
1723
|
throw new Error(
|
|
1430
|
-
`Configured database URL environment variable '${this.databaseUrlEnvVarName}' not found in
|
|
1724
|
+
`Configured database URL environment variable '${this.databaseUrlEnvVarName}' not found in any dotenv-flow file. Please add it to an .env file or update your iloom configuration.`
|
|
1431
1725
|
);
|
|
1432
1726
|
}
|
|
1433
|
-
const hasDefaultVar =
|
|
1727
|
+
const hasDefaultVar = await hasVariableInAnyEnvFile(
|
|
1728
|
+
workspacePath,
|
|
1729
|
+
"DATABASE_URL",
|
|
1730
|
+
async (p) => fs6.pathExists(p),
|
|
1731
|
+
async (p, v) => this.environment.getEnvVariable(p, v)
|
|
1732
|
+
);
|
|
1434
1733
|
if (hasDefaultVar) {
|
|
1435
1734
|
logger3.debug("\u2705 Found fallback DATABASE_URL variable");
|
|
1436
1735
|
} else {
|
|
1437
|
-
logger3.debug("\u274C No DATABASE_URL variable found in
|
|
1736
|
+
logger3.debug("\u274C No DATABASE_URL variable found in any env file");
|
|
1438
1737
|
}
|
|
1439
1738
|
return hasDefaultVar;
|
|
1440
1739
|
} catch (error) {
|
|
1441
|
-
if (error instanceof Error && error.message.includes("not found in
|
|
1740
|
+
if (error instanceof Error && error.message.includes("not found in")) {
|
|
1442
1741
|
throw error;
|
|
1443
1742
|
}
|
|
1444
1743
|
return false;
|
|
@@ -1447,7 +1746,7 @@ var DatabaseManager = class {
|
|
|
1447
1746
|
};
|
|
1448
1747
|
|
|
1449
1748
|
// src/lib/ResourceCleanup.ts
|
|
1450
|
-
import
|
|
1749
|
+
import path5 from "path";
|
|
1451
1750
|
var ResourceCleanup = class {
|
|
1452
1751
|
constructor(gitWorktree, processManager, database, cliIsolation, settingsManager) {
|
|
1453
1752
|
this.gitWorktree = gitWorktree;
|
|
@@ -1455,6 +1754,7 @@ var ResourceCleanup = class {
|
|
|
1455
1754
|
this.database = database;
|
|
1456
1755
|
this.cliIsolation = cliIsolation;
|
|
1457
1756
|
this.settingsManager = settingsManager ?? new SettingsManager();
|
|
1757
|
+
this.metadataManager = new MetadataManager();
|
|
1458
1758
|
}
|
|
1459
1759
|
/**
|
|
1460
1760
|
* Cleanup a worktree and associated resources
|
|
@@ -1542,7 +1842,7 @@ ${blockerMessage}`);
|
|
|
1542
1842
|
}
|
|
1543
1843
|
let databaseConfig = null;
|
|
1544
1844
|
if (!options.keepDatabase && worktree) {
|
|
1545
|
-
const envFilePath =
|
|
1845
|
+
const envFilePath = path5.join(worktree.path, ".env");
|
|
1546
1846
|
try {
|
|
1547
1847
|
const shouldCleanup = this.database ? await this.database.shouldUseDatabaseBranching(envFilePath) : false;
|
|
1548
1848
|
databaseConfig = { shouldCleanup, envFilePath };
|
|
@@ -1585,6 +1885,8 @@ ${blockerMessage}`);
|
|
|
1585
1885
|
success: true,
|
|
1586
1886
|
message: `Worktree removed: ${worktree.path}`
|
|
1587
1887
|
});
|
|
1888
|
+
await this.metadataManager.deleteMetadata(worktree.path);
|
|
1889
|
+
logger.debug(`Metadata file cleanup attempted for: ${worktree.path}`);
|
|
1588
1890
|
} catch (error) {
|
|
1589
1891
|
const err = error instanceof Error ? error : new Error("Unknown error");
|
|
1590
1892
|
errors.push(err);
|
|
@@ -1845,7 +2147,7 @@ ${blockerMessage}`);
|
|
|
1845
2147
|
return false;
|
|
1846
2148
|
}
|
|
1847
2149
|
try {
|
|
1848
|
-
const envFilePath =
|
|
2150
|
+
const envFilePath = path5.join(worktreePath, ".env");
|
|
1849
2151
|
const shouldCleanup = await this.database.shouldUseDatabaseBranching(envFilePath);
|
|
1850
2152
|
let cwd;
|
|
1851
2153
|
try {
|
|
@@ -1980,10 +2282,11 @@ Please resolve before cleanup - you have some options:
|
|
|
1980
2282
|
};
|
|
1981
2283
|
|
|
1982
2284
|
export {
|
|
2285
|
+
MetadataManager,
|
|
1983
2286
|
LoomManager,
|
|
1984
2287
|
EnvironmentManager,
|
|
1985
2288
|
CLIIsolationManager,
|
|
1986
2289
|
DatabaseManager,
|
|
1987
2290
|
ResourceCleanup
|
|
1988
2291
|
};
|
|
1989
|
-
//# sourceMappingURL=chunk-
|
|
2292
|
+
//# sourceMappingURL=chunk-2IJEMXOB.js.map
|