@iloom/cli 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/README.md +234 -667
  2. package/dist/BranchNamingService-OMWKUYMM.js +13 -0
  3. package/dist/ClaudeContextManager-3VXA6UPR.js +13 -0
  4. package/dist/ClaudeService-6CPK43N4.js +12 -0
  5. package/dist/GitHubService-EBOETDIW.js +11 -0
  6. package/dist/{LoomLauncher-CTSWJL35.js → LoomLauncher-JF7JZMTZ.js} +63 -32
  7. package/dist/LoomLauncher-JF7JZMTZ.js.map +1 -0
  8. package/dist/ProjectCapabilityDetector-34LU7JJ4.js +9 -0
  9. package/dist/{PromptTemplateManager-WII75TKH.js → PromptTemplateManager-A52RUAMS.js} +2 -2
  10. package/dist/README.md +234 -667
  11. package/dist/{SettingsManager-XOYCLH3D.js → SettingsManager-ZCWJ56WP.js} +12 -4
  12. package/dist/SettingsMigrationManager-AGIIIPDQ.js +10 -0
  13. package/dist/agents/iloom-issue-analyze-and-plan.md +125 -35
  14. package/dist/agents/iloom-issue-analyzer.md +284 -32
  15. package/dist/agents/iloom-issue-complexity-evaluator.md +40 -21
  16. package/dist/agents/iloom-issue-enhancer.md +69 -48
  17. package/dist/agents/iloom-issue-implementer.md +36 -25
  18. package/dist/agents/iloom-issue-planner.md +35 -24
  19. package/dist/agents/iloom-issue-reviewer.md +62 -9
  20. package/dist/{chunk-SWCRXDZC.js → chunk-3RUPPQRG.js} +1 -18
  21. package/dist/chunk-3RUPPQRG.js.map +1 -0
  22. package/dist/{chunk-HBVFXN7R.js → chunk-4BGK7T6X.js} +26 -3
  23. package/dist/chunk-4BGK7T6X.js.map +1 -0
  24. package/dist/{chunk-6LEQW46Y.js → chunk-4E4LD3QR.js} +72 -2
  25. package/dist/{chunk-6LEQW46Y.js.map → chunk-4E4LD3QR.js.map} +1 -1
  26. package/dist/{chunk-CWR2SANQ.js → chunk-EBISESAP.js} +1 -1
  27. package/dist/{chunk-TS6DL67T.js → chunk-G2IEYOLQ.js} +11 -38
  28. package/dist/chunk-G2IEYOLQ.js.map +1 -0
  29. package/dist/chunk-HBYZH6GD.js +1989 -0
  30. package/dist/chunk-HBYZH6GD.js.map +1 -0
  31. package/dist/chunk-INW24J2W.js +55 -0
  32. package/dist/chunk-INW24J2W.js.map +1 -0
  33. package/dist/{chunk-ZMNQBJUI.js → chunk-IP7SMKIF.js} +61 -22
  34. package/dist/chunk-IP7SMKIF.js.map +1 -0
  35. package/dist/{chunk-4IV6W4U5.js → chunk-IXKLYTWO.js} +12 -12
  36. package/dist/chunk-IXKLYTWO.js.map +1 -0
  37. package/dist/{chunk-JNKJ7NJV.js → chunk-JKXJ7BGL.js} +6 -2
  38. package/dist/{chunk-JNKJ7NJV.js.map → chunk-JKXJ7BGL.js.map} +1 -1
  39. package/dist/{chunk-LAPY6NAE.js → chunk-JQFO7QQN.js} +68 -12
  40. package/dist/{chunk-LAPY6NAE.js.map → chunk-JQFO7QQN.js.map} +1 -1
  41. package/dist/{SettingsMigrationManager-MTQIMI54.js → chunk-KLBYVHPK.js} +3 -2
  42. package/dist/{chunk-USVVV3FP.js → chunk-MKWYLDFK.js} +5 -5
  43. package/dist/chunk-O5OH5MRX.js +396 -0
  44. package/dist/chunk-O5OH5MRX.js.map +1 -0
  45. package/dist/{chunk-DJUGYNQE.js → chunk-PA6Q6AWM.js} +16 -3
  46. package/dist/chunk-PA6Q6AWM.js.map +1 -0
  47. package/dist/chunk-RO26VS3W.js +444 -0
  48. package/dist/chunk-RO26VS3W.js.map +1 -0
  49. package/dist/{chunk-VETG35MF.js → chunk-TSKY3JI7.js} +3 -3
  50. package/dist/{chunk-VETG35MF.js.map → chunk-TSKY3JI7.js.map} +1 -1
  51. package/dist/{chunk-LHP6ROUM.js → chunk-U5QDY7ZD.js} +4 -16
  52. package/dist/chunk-U5QDY7ZD.js.map +1 -0
  53. package/dist/{chunk-SPYPLHMK.js → chunk-VU3QMIP2.js} +34 -2
  54. package/dist/chunk-VU3QMIP2.js.map +1 -0
  55. package/dist/{chunk-PVAVNJKS.js → chunk-WEN5C5DM.js} +10 -1
  56. package/dist/chunk-WEN5C5DM.js.map +1 -0
  57. package/dist/{chunk-2PLUQT6J.js → chunk-XPKDPZ5D.js} +2 -2
  58. package/dist/{chunk-RF2YI2XJ.js → chunk-ZBQVSHVT.js} +5 -5
  59. package/dist/chunk-ZBQVSHVT.js.map +1 -0
  60. package/dist/{chunk-GZP4UGGM.js → chunk-ZM3CFL5L.js} +2 -2
  61. package/dist/{chunk-BLCTGFZN.js → chunk-ZT3YZB4K.js} +3 -4
  62. package/dist/chunk-ZT3YZB4K.js.map +1 -0
  63. package/dist/{chunk-MFU53H6J.js → chunk-ZWFBBPJI.js} +6 -6
  64. package/dist/{chunk-MFU53H6J.js.map → chunk-ZWFBBPJI.js.map} +1 -1
  65. package/dist/{claude-ZIWDG4XG.js → claude-LUZ35IMK.js} +2 -2
  66. package/dist/{cleanup-FEIVZSIV.js → cleanup-3MONU4PU.js} +88 -27
  67. package/dist/cleanup-3MONU4PU.js.map +1 -0
  68. package/dist/cli.js +2511 -62
  69. package/dist/cli.js.map +1 -1
  70. package/dist/{contribute-EMZKCAC6.js → contribute-UWJAGIG7.js} +6 -6
  71. package/dist/{feedback-LFNMQBAZ.js → feedback-W3BXTGIM.js} +15 -14
  72. package/dist/{feedback-LFNMQBAZ.js.map → feedback-W3BXTGIM.js.map} +1 -1
  73. package/dist/{git-WC6HZLOT.js → git-34Z6QVDS.js} +4 -2
  74. package/dist/{ignite-MQWVJEAB.js → ignite-KVJEFXNO.js} +32 -27
  75. package/dist/ignite-KVJEFXNO.js.map +1 -0
  76. package/dist/index.d.ts +359 -45
  77. package/dist/index.js +1267 -503
  78. package/dist/index.js.map +1 -1
  79. package/dist/{init-GJDYN2IK.js → init-L55Q73H4.js} +104 -40
  80. package/dist/init-L55Q73H4.js.map +1 -0
  81. package/dist/mcp/issue-management-server.js +934 -0
  82. package/dist/mcp/issue-management-server.js.map +1 -0
  83. package/dist/{neon-helpers-ZVIRPKCI.js → neon-helpers-WPUACUVC.js} +3 -3
  84. package/dist/neon-helpers-WPUACUVC.js.map +1 -0
  85. package/dist/{open-NXSN7XOC.js → open-LNRZL3UU.js} +39 -36
  86. package/dist/open-LNRZL3UU.js.map +1 -0
  87. package/dist/{prompt-ANTQWHUF.js → prompt-7INJ7YRU.js} +4 -2
  88. package/dist/prompt-7INJ7YRU.js.map +1 -0
  89. package/dist/prompts/init-prompt.txt +541 -98
  90. package/dist/prompts/issue-prompt.txt +27 -27
  91. package/dist/{rebase-DUNFOJVS.js → rebase-C4WNCVGM.js} +6 -6
  92. package/dist/{remote-ZCXJVVNW.js → remote-VUNCQZ6J.js} +3 -2
  93. package/dist/remote-VUNCQZ6J.js.map +1 -0
  94. package/dist/{run-O7ZK7CKA.js → run-IOGNIOYN.js} +39 -36
  95. package/dist/run-IOGNIOYN.js.map +1 -0
  96. package/dist/schema/settings.schema.json +59 -3
  97. package/dist/{test-git-T76HOTIA.js → test-git-J7I5MFYH.js} +3 -3
  98. package/dist/{test-prefix-6HJUVQMH.js → test-prefix-ZCONBCBX.js} +3 -3
  99. package/dist/{test-webserver-M2I3EV4J.js → test-webserver-DAHONWCS.js} +4 -4
  100. package/dist/test-webserver-DAHONWCS.js.map +1 -0
  101. package/package.json +3 -2
  102. package/dist/ClaudeContextManager-LVCYRM6Q.js +0 -13
  103. package/dist/ClaudeService-WVTWB3DK.js +0 -12
  104. package/dist/GitHubService-7E2S5NNZ.js +0 -11
  105. package/dist/LoomLauncher-CTSWJL35.js.map +0 -1
  106. package/dist/add-issue-OBI325W7.js +0 -69
  107. package/dist/add-issue-OBI325W7.js.map +0 -1
  108. package/dist/chunk-4IV6W4U5.js.map +0 -1
  109. package/dist/chunk-BLCTGFZN.js.map +0 -1
  110. package/dist/chunk-CVLAZRNB.js +0 -54
  111. package/dist/chunk-CVLAZRNB.js.map +0 -1
  112. package/dist/chunk-DJUGYNQE.js.map +0 -1
  113. package/dist/chunk-H4E4THUZ.js +0 -55
  114. package/dist/chunk-H4E4THUZ.js.map +0 -1
  115. package/dist/chunk-H5LDRGVK.js +0 -642
  116. package/dist/chunk-H5LDRGVK.js.map +0 -1
  117. package/dist/chunk-HBVFXN7R.js.map +0 -1
  118. package/dist/chunk-LHP6ROUM.js.map +0 -1
  119. package/dist/chunk-PVAVNJKS.js.map +0 -1
  120. package/dist/chunk-RF2YI2XJ.js.map +0 -1
  121. package/dist/chunk-SPYPLHMK.js.map +0 -1
  122. package/dist/chunk-SWCRXDZC.js.map +0 -1
  123. package/dist/chunk-SYOSCMIT.js +0 -545
  124. package/dist/chunk-SYOSCMIT.js.map +0 -1
  125. package/dist/chunk-T3KEIB4D.js +0 -243
  126. package/dist/chunk-T3KEIB4D.js.map +0 -1
  127. package/dist/chunk-TS6DL67T.js.map +0 -1
  128. package/dist/chunk-ZMNQBJUI.js.map +0 -1
  129. package/dist/cleanup-FEIVZSIV.js.map +0 -1
  130. package/dist/enhance-MNA4ZGXW.js +0 -176
  131. package/dist/enhance-MNA4ZGXW.js.map +0 -1
  132. package/dist/finish-TX5CJICB.js +0 -1749
  133. package/dist/finish-TX5CJICB.js.map +0 -1
  134. package/dist/ignite-MQWVJEAB.js.map +0 -1
  135. package/dist/init-GJDYN2IK.js.map +0 -1
  136. package/dist/mcp/chunk-6SDFJ42P.js +0 -62
  137. package/dist/mcp/chunk-6SDFJ42P.js.map +0 -1
  138. package/dist/mcp/claude-NDFOCQQQ.js +0 -249
  139. package/dist/mcp/claude-NDFOCQQQ.js.map +0 -1
  140. package/dist/mcp/color-QS5BFCNN.js +0 -168
  141. package/dist/mcp/color-QS5BFCNN.js.map +0 -1
  142. package/dist/mcp/github-comment-server.js +0 -168
  143. package/dist/mcp/github-comment-server.js.map +0 -1
  144. package/dist/mcp/terminal-OMNRFWB3.js +0 -227
  145. package/dist/mcp/terminal-OMNRFWB3.js.map +0 -1
  146. package/dist/open-NXSN7XOC.js.map +0 -1
  147. package/dist/run-O7ZK7CKA.js.map +0 -1
  148. package/dist/start-73I5W7WW.js +0 -983
  149. package/dist/start-73I5W7WW.js.map +0 -1
  150. package/dist/test-webserver-M2I3EV4J.js.map +0 -1
  151. /package/dist/{ClaudeContextManager-LVCYRM6Q.js.map → BranchNamingService-OMWKUYMM.js.map} +0 -0
  152. /package/dist/{ClaudeService-WVTWB3DK.js.map → ClaudeContextManager-3VXA6UPR.js.map} +0 -0
  153. /package/dist/{GitHubService-7E2S5NNZ.js.map → ClaudeService-6CPK43N4.js.map} +0 -0
  154. /package/dist/{PromptTemplateManager-WII75TKH.js.map → GitHubService-EBOETDIW.js.map} +0 -0
  155. /package/dist/{SettingsManager-XOYCLH3D.js.map → ProjectCapabilityDetector-34LU7JJ4.js.map} +0 -0
  156. /package/dist/{claude-ZIWDG4XG.js.map → PromptTemplateManager-A52RUAMS.js.map} +0 -0
  157. /package/dist/{git-WC6HZLOT.js.map → SettingsManager-ZCWJ56WP.js.map} +0 -0
  158. /package/dist/{neon-helpers-ZVIRPKCI.js.map → SettingsMigrationManager-AGIIIPDQ.js.map} +0 -0
  159. /package/dist/{chunk-CWR2SANQ.js.map → chunk-EBISESAP.js.map} +0 -0
  160. /package/dist/{SettingsMigrationManager-MTQIMI54.js.map → chunk-KLBYVHPK.js.map} +0 -0
  161. /package/dist/{chunk-USVVV3FP.js.map → chunk-MKWYLDFK.js.map} +0 -0
  162. /package/dist/{chunk-2PLUQT6J.js.map → chunk-XPKDPZ5D.js.map} +0 -0
  163. /package/dist/{chunk-GZP4UGGM.js.map → chunk-ZM3CFL5L.js.map} +0 -0
  164. /package/dist/{prompt-ANTQWHUF.js.map → claude-LUZ35IMK.js.map} +0 -0
  165. /package/dist/{contribute-EMZKCAC6.js.map → contribute-UWJAGIG7.js.map} +0 -0
  166. /package/dist/{remote-ZCXJVVNW.js.map → git-34Z6QVDS.js.map} +0 -0
  167. /package/dist/{rebase-DUNFOJVS.js.map → rebase-C4WNCVGM.js.map} +0 -0
  168. /package/dist/{test-git-T76HOTIA.js.map → test-git-J7I5MFYH.js.map} +0 -0
  169. /package/dist/{test-prefix-6HJUVQMH.js.map → test-prefix-ZCONBCBX.js.map} +0 -0
@@ -1,642 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- calculatePortForBranch,
4
- formatEnvLine,
5
- parseEnvFile,
6
- validateEnvVariable
7
- } from "./chunk-ZMNQBJUI.js";
8
- import {
9
- runScript
10
- } from "./chunk-BLCTGFZN.js";
11
- import {
12
- hasScript,
13
- readPackageJson
14
- } from "./chunk-2ZPFJQ3B.js";
15
- import {
16
- createLogger,
17
- logger
18
- } from "./chunk-GEHQXLEI.js";
19
-
20
- // src/lib/EnvironmentManager.ts
21
- import fs from "fs-extra";
22
- var logger2 = createLogger({ prefix: "\u{1F4DD}" });
23
- var EnvironmentManager = class {
24
- constructor() {
25
- this.backupSuffix = ".backup";
26
- }
27
- /**
28
- * Set or update an environment variable in a .env file
29
- * Ports functionality from bash/utils/env-utils.sh:setEnvVar()
30
- * @returns The backup path if a backup was created
31
- */
32
- async setEnvVar(filePath, key, value, backup = false) {
33
- const validation = validateEnvVariable(key, value);
34
- if (!validation.valid) {
35
- throw new Error(validation.error ?? "Invalid variable name");
36
- }
37
- const fileExists = await fs.pathExists(filePath);
38
- if (!fileExists) {
39
- logger2.info(`Creating ${filePath} with ${key}...`);
40
- const content = formatEnvLine(key, value);
41
- await fs.writeFile(filePath, content, "utf8");
42
- logger2.success(`${filePath} created with ${key}`);
43
- return;
44
- }
45
- const existingContent = await fs.readFile(filePath, "utf8");
46
- const envMap = parseEnvFile(existingContent);
47
- let backupPath;
48
- if (backup) {
49
- backupPath = await this.createBackup(filePath);
50
- }
51
- envMap.set(key, value);
52
- const lines = existingContent.split("\n");
53
- const newLines = [];
54
- let variableUpdated = false;
55
- for (const line of lines) {
56
- const trimmedLine = line.trim();
57
- if (!trimmedLine || trimmedLine.startsWith("#")) {
58
- newLines.push(line);
59
- continue;
60
- }
61
- const cleanLine = trimmedLine.startsWith("export ") ? trimmedLine.substring(7) : trimmedLine;
62
- const equalsIndex = cleanLine.indexOf("=");
63
- if (equalsIndex !== -1) {
64
- const lineKey = cleanLine.substring(0, equalsIndex).trim();
65
- if (lineKey === key) {
66
- newLines.push(formatEnvLine(key, value));
67
- variableUpdated = true;
68
- continue;
69
- }
70
- }
71
- newLines.push(line);
72
- }
73
- if (!variableUpdated) {
74
- logger2.info(`Adding ${key} to ${filePath}...`);
75
- newLines.push(formatEnvLine(key, value));
76
- logger2.success(`${key} added successfully`);
77
- } else {
78
- logger2.info(`Updating ${key} in ${filePath}...`);
79
- logger2.success(`${key} updated successfully`);
80
- }
81
- const newContent = newLines.join("\n");
82
- await fs.writeFile(filePath, newContent, "utf8");
83
- return backupPath;
84
- }
85
- /**
86
- * Read and parse a .env file
87
- */
88
- async readEnvFile(filePath) {
89
- try {
90
- const content = await fs.readFile(filePath, "utf8");
91
- return parseEnvFile(content);
92
- } catch (error) {
93
- logger2.debug(
94
- `Could not read env file ${filePath}: ${error instanceof Error ? error.message : String(error)}`
95
- );
96
- return /* @__PURE__ */ new Map();
97
- }
98
- }
99
- /**
100
- * Generic file copy helper that only copies if source exists
101
- * Does not throw if source file doesn't exist - just logs and returns
102
- * @private
103
- */
104
- async copyIfExists(source, destination) {
105
- const sourceExists = await fs.pathExists(source);
106
- if (!sourceExists) {
107
- logger2.debug(`Source file ${source} does not exist, skipping copy`);
108
- return;
109
- }
110
- await fs.copy(source, destination, { overwrite: false });
111
- logger2.success(`Copied ${source} to ${destination}`);
112
- }
113
- /**
114
- * Calculate unique port for workspace
115
- * Implements:
116
- * - Issue/PR: 3000 + issue/PR number
117
- * - Branch: 3000 + deterministic hash offset (1-999)
118
- */
119
- calculatePort(options) {
120
- const basePort = options.basePort ?? 3e3;
121
- if (options.issueNumber !== void 0) {
122
- const port = basePort + options.issueNumber;
123
- if (port > 65535) {
124
- throw new Error(
125
- `Calculated port ${port} exceeds maximum (65535). Use a lower base port or issue number.`
126
- );
127
- }
128
- return port;
129
- }
130
- if (options.prNumber !== void 0) {
131
- const port = basePort + options.prNumber;
132
- if (port > 65535) {
133
- throw new Error(
134
- `Calculated port ${port} exceeds maximum (65535). Use a lower base port or PR number.`
135
- );
136
- }
137
- return port;
138
- }
139
- if (options.branchName !== void 0) {
140
- return calculatePortForBranch(options.branchName, basePort);
141
- }
142
- return basePort;
143
- }
144
- /**
145
- * Set port environment variable for workspace
146
- */
147
- async setPortForWorkspace(envFilePath, issueNumber, prNumber, branchName) {
148
- const options = {};
149
- if (issueNumber !== void 0) {
150
- options.issueNumber = issueNumber;
151
- }
152
- if (prNumber !== void 0) {
153
- options.prNumber = prNumber;
154
- }
155
- if (branchName !== void 0) {
156
- options.branchName = branchName;
157
- }
158
- const port = this.calculatePort(options);
159
- await this.setEnvVar(envFilePath, "PORT", String(port));
160
- return port;
161
- }
162
- /**
163
- * Validate environment configuration
164
- */
165
- async validateEnvFile(filePath) {
166
- try {
167
- const content = await fs.readFile(filePath, "utf8");
168
- const envMap = parseEnvFile(content);
169
- const errors = [];
170
- for (const [key, value] of envMap.entries()) {
171
- const validation = validateEnvVariable(key, value);
172
- if (!validation.valid) {
173
- errors.push(`${key}: ${validation.error}`);
174
- }
175
- }
176
- return {
177
- valid: errors.length === 0,
178
- errors
179
- };
180
- } catch (error) {
181
- return {
182
- valid: false,
183
- errors: [
184
- `Failed to read or parse file: ${error instanceof Error ? error.message : String(error)}`
185
- ]
186
- };
187
- }
188
- }
189
- /**
190
- * Create backup of existing file
191
- */
192
- async createBackup(filePath) {
193
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
194
- const backupPath = `${filePath}${this.backupSuffix}-${timestamp}`;
195
- await fs.copy(filePath, backupPath);
196
- logger2.debug(`Created backup at ${backupPath}`);
197
- return backupPath;
198
- }
199
- };
200
-
201
- // src/lib/CLIIsolationManager.ts
202
- import fs2 from "fs-extra";
203
- import path from "path";
204
- import os from "os";
205
- var CLIIsolationManager = class {
206
- constructor() {
207
- this.iloomBinDir = path.join(os.homedir(), ".iloom", "bin");
208
- }
209
- /**
210
- * Setup CLI isolation for a worktree
211
- * - Build the project
212
- * - Create versioned symlinks
213
- * - Check PATH configuration
214
- * @param worktreePath Path to the worktree
215
- * @param identifier Issue/PR number or branch identifier
216
- * @param binEntries Bin entries from package.json
217
- * @returns Array of created symlink names
218
- */
219
- async setupCLIIsolation(worktreePath, identifier, binEntries) {
220
- await this.buildProject(worktreePath);
221
- await this.verifyBinTargets(worktreePath, binEntries);
222
- await fs2.ensureDir(this.iloomBinDir);
223
- const symlinkNames = await this.createVersionedSymlinks(
224
- worktreePath,
225
- identifier,
226
- binEntries
227
- );
228
- await this.ensureIloomBinInPath();
229
- return symlinkNames;
230
- }
231
- /**
232
- * Build the project using package.json build script
233
- * @param worktreePath Path to the worktree
234
- */
235
- async buildProject(worktreePath) {
236
- const pkgJson = await readPackageJson(worktreePath);
237
- if (!hasScript(pkgJson, "build")) {
238
- logger.warn("No build script found in package.json - skipping build");
239
- return;
240
- }
241
- logger.info("Building CLI tool...");
242
- await runScript("build", worktreePath);
243
- logger.success("Build completed");
244
- }
245
- /**
246
- * Verify bin targets exist and are executable
247
- * @param worktreePath Path to the worktree
248
- * @param binEntries Bin entries from package.json
249
- */
250
- async verifyBinTargets(worktreePath, binEntries) {
251
- for (const binPath of Object.values(binEntries)) {
252
- const targetPath = path.resolve(worktreePath, binPath);
253
- const exists = await fs2.pathExists(targetPath);
254
- if (!exists) {
255
- throw new Error(`Bin target does not exist: ${targetPath}`);
256
- }
257
- try {
258
- await fs2.access(targetPath, fs2.constants.X_OK);
259
- } catch {
260
- }
261
- }
262
- }
263
- /**
264
- * Create versioned symlinks in ~/.iloom/bin
265
- * @param worktreePath Path to the worktree
266
- * @param identifier Issue/PR number or branch identifier
267
- * @param binEntries Bin entries from package.json
268
- * @returns Array of created symlink names
269
- */
270
- async createVersionedSymlinks(worktreePath, identifier, binEntries) {
271
- const symlinkNames = [];
272
- for (const [binName, binPath] of Object.entries(binEntries)) {
273
- const versionedName = `${binName}-${identifier}`;
274
- const targetPath = path.resolve(worktreePath, binPath);
275
- const symlinkPath = path.join(this.iloomBinDir, versionedName);
276
- await fs2.symlink(targetPath, symlinkPath);
277
- logger.success(`CLI available: ${versionedName}`);
278
- symlinkNames.push(versionedName);
279
- }
280
- return symlinkNames;
281
- }
282
- /**
283
- * Check if ~/.iloom/bin is in PATH and provide setup instructions
284
- */
285
- async ensureIloomBinInPath() {
286
- const currentPath = process.env.PATH ?? "";
287
- if (currentPath.includes(".iloom/bin")) {
288
- return;
289
- }
290
- const shell = this.detectShell();
291
- const rcFile = this.getShellRcFile(shell);
292
- logger.warn("\n\u26A0\uFE0F One-time PATH setup required:");
293
- logger.warn(` Add to ${rcFile}:`);
294
- logger.warn(` export PATH="$HOME/.iloom/bin:$PATH"`);
295
- logger.warn(` Then run: source ${rcFile}
296
- `);
297
- }
298
- /**
299
- * Detect current shell
300
- * @returns Shell name (zsh, bash, fish, etc.)
301
- */
302
- detectShell() {
303
- const shell = process.env.SHELL ?? "";
304
- return shell.split("/").pop() ?? "bash";
305
- }
306
- /**
307
- * Get RC file path for shell
308
- * @param shell Shell name
309
- * @returns RC file path
310
- */
311
- getShellRcFile(shell) {
312
- const rcFiles = {
313
- zsh: "~/.zshrc",
314
- bash: "~/.bashrc",
315
- fish: "~/.config/fish/config.fish"
316
- };
317
- return rcFiles[shell] ?? "~/.bashrc";
318
- }
319
- /**
320
- * Cleanup versioned CLI executables for a specific identifier
321
- * Removes all symlinks matching the pattern: {binName}-{identifier}
322
- *
323
- * @param identifier - Issue/PR number or branch identifier
324
- * @returns Array of removed symlink names
325
- */
326
- async cleanupVersionedExecutables(identifier) {
327
- const removed = [];
328
- try {
329
- const files = await fs2.readdir(this.iloomBinDir);
330
- for (const file of files) {
331
- if (this.matchesIdentifier(file, identifier)) {
332
- const symlinkPath = path.join(this.iloomBinDir, file);
333
- try {
334
- await fs2.unlink(symlinkPath);
335
- removed.push(file);
336
- } catch (error) {
337
- const isEnoent = error && typeof error === "object" && "code" in error && error.code === "ENOENT";
338
- if (isEnoent) {
339
- removed.push(file);
340
- continue;
341
- }
342
- logger.warn(
343
- `Failed to remove symlink ${file}: ${error instanceof Error ? error.message : "Unknown error"}`
344
- );
345
- }
346
- }
347
- }
348
- } catch (error) {
349
- const isEnoent = error && typeof error === "object" && "code" in error && error.code === "ENOENT";
350
- if (isEnoent) {
351
- logger.warn("No CLI executables directory found - nothing to cleanup");
352
- return [];
353
- }
354
- throw error;
355
- }
356
- if (removed.length > 0) {
357
- logger.success(`Removed CLI executables: ${removed.join(", ")}`);
358
- }
359
- return removed;
360
- }
361
- /**
362
- * Find orphaned symlinks in ~/.iloom/bin
363
- * Returns symlinks that point to non-existent targets
364
- *
365
- * @returns Array of orphaned symlink information
366
- */
367
- async findOrphanedSymlinks() {
368
- const orphaned = [];
369
- try {
370
- const files = await fs2.readdir(this.iloomBinDir);
371
- for (const file of files) {
372
- const symlinkPath = path.join(this.iloomBinDir, file);
373
- try {
374
- const stats = await fs2.lstat(symlinkPath);
375
- if (stats.isSymbolicLink()) {
376
- const target = await fs2.readlink(symlinkPath);
377
- try {
378
- await fs2.access(target);
379
- } catch {
380
- orphaned.push({
381
- name: file,
382
- path: symlinkPath,
383
- brokenTarget: target
384
- });
385
- }
386
- }
387
- } catch (error) {
388
- logger.warn(
389
- `Failed to check symlink ${file}: ${error instanceof Error ? error.message : "Unknown error"}`
390
- );
391
- }
392
- }
393
- } catch (error) {
394
- const isEnoent = error && typeof error === "object" && "code" in error && error.code === "ENOENT";
395
- if (isEnoent) {
396
- return [];
397
- }
398
- throw error;
399
- }
400
- return orphaned;
401
- }
402
- /**
403
- * Cleanup all orphaned symlinks
404
- * Removes symlinks that point to non-existent targets
405
- *
406
- * @returns Number of symlinks removed
407
- */
408
- async cleanupOrphanedSymlinks() {
409
- const orphaned = await this.findOrphanedSymlinks();
410
- let removedCount = 0;
411
- for (const symlink of orphaned) {
412
- try {
413
- await fs2.unlink(symlink.path);
414
- removedCount++;
415
- logger.success(`Removed orphaned symlink: ${symlink.name}`);
416
- } catch (error) {
417
- logger.warn(
418
- `Failed to remove orphaned symlink ${symlink.name}: ${error instanceof Error ? error.message : "Unknown error"}`
419
- );
420
- }
421
- }
422
- return removedCount;
423
- }
424
- /**
425
- * Check if a filename matches the versioned pattern for an identifier
426
- * Pattern: {binName}-{identifier}
427
- *
428
- * @param fileName - Name of the file to check
429
- * @param identifier - Issue/PR number or branch identifier
430
- * @returns True if the filename matches the pattern
431
- */
432
- matchesIdentifier(fileName, identifier) {
433
- const suffix = `-${identifier}`;
434
- return fileName.endsWith(suffix);
435
- }
436
- };
437
-
438
- // src/lib/DatabaseManager.ts
439
- var logger3 = createLogger({ prefix: "\u{1F5C2}\uFE0F" });
440
- var DatabaseManager = class {
441
- constructor(provider, environment, databaseUrlEnvVarName = "DATABASE_URL") {
442
- this.provider = provider;
443
- this.environment = environment;
444
- this.databaseUrlEnvVarName = databaseUrlEnvVarName;
445
- if (databaseUrlEnvVarName !== "DATABASE_URL") {
446
- logger3.debug(`\u{1F527} DatabaseManager configured with custom variable: ${databaseUrlEnvVarName}`);
447
- } else {
448
- logger3.debug("\u{1F527} DatabaseManager using default variable: DATABASE_URL");
449
- }
450
- }
451
- /**
452
- * Get the configured database URL environment variable name
453
- */
454
- getConfiguredVariableName() {
455
- return this.databaseUrlEnvVarName;
456
- }
457
- /**
458
- * Check if database branching should be used
459
- * Requires BOTH conditions:
460
- * 1. Database provider is properly configured (checked via provider.isConfigured())
461
- * 2. .env file contains the configured database URL variable
462
- */
463
- async shouldUseDatabaseBranching(envFilePath) {
464
- if (!this.provider.isConfigured()) {
465
- logger3.debug("Skipping database branching: Database provider not configured");
466
- return false;
467
- }
468
- const hasDatabaseUrl = await this.hasDatabaseUrlInEnv(envFilePath);
469
- if (!hasDatabaseUrl) {
470
- logger3.debug(
471
- "Skipping database branching: configured database URL variable not found in .env file"
472
- );
473
- return false;
474
- }
475
- return true;
476
- }
477
- /**
478
- * Create database branch only if configured
479
- * Returns connection string if branch was created, null if skipped
480
- *
481
- * @param branchName - Name of the branch to create
482
- * @param envFilePath - Path to .env file for configuration checks
483
- * @param cwd - Optional working directory to run commands from
484
- */
485
- async createBranchIfConfigured(branchName, envFilePath, cwd) {
486
- if (!await this.shouldUseDatabaseBranching(envFilePath)) {
487
- return null;
488
- }
489
- if (!await this.provider.isCliAvailable()) {
490
- logger3.warn("Skipping database branch creation: Neon CLI not available");
491
- logger3.warn("Install with: npm install -g neonctl");
492
- return null;
493
- }
494
- try {
495
- const isAuth = await this.provider.isAuthenticated(cwd);
496
- if (!isAuth) {
497
- logger3.warn("Skipping database branch creation: Not authenticated with Neon CLI");
498
- logger3.warn("Run: neon auth");
499
- return null;
500
- }
501
- } catch (error) {
502
- const errorMessage = error instanceof Error ? error.message : String(error);
503
- logger3.error(`Database authentication check failed: ${errorMessage}`);
504
- throw error;
505
- }
506
- try {
507
- const connectionString = await this.provider.createBranch(branchName, void 0, cwd);
508
- logger3.success(`Database branch ready: ${this.provider.sanitizeBranchName(branchName)}`);
509
- return connectionString;
510
- } catch (error) {
511
- logger3.error(
512
- `Failed to create database branch: ${error instanceof Error ? error.message : String(error)}`
513
- );
514
- throw error;
515
- }
516
- }
517
- /**
518
- * Delete database branch only if configured
519
- * Returns result object indicating what happened
520
- *
521
- * @param branchName - Name of the branch to delete
522
- * @param shouldCleanup - Boolean indicating if database cleanup should be performed (pre-fetched config)
523
- * @param isPreview - Whether this is a preview database branch
524
- * @param cwd - Optional working directory to run commands from (prevents issues with deleted directories)
525
- */
526
- async deleteBranchIfConfigured(branchName, shouldCleanup, isPreview = false, cwd) {
527
- if (shouldCleanup === false) {
528
- return {
529
- success: true,
530
- deleted: false,
531
- notFound: true,
532
- // Treat "not configured" as "nothing to delete"
533
- branchName
534
- };
535
- }
536
- if (!this.provider.isConfigured()) {
537
- logger3.debug("Skipping database branch deletion: Database provider not configured");
538
- return {
539
- success: true,
540
- deleted: false,
541
- notFound: true,
542
- branchName
543
- };
544
- }
545
- if (!await this.provider.isCliAvailable()) {
546
- logger3.info("Skipping database branch deletion: CLI tool not available");
547
- return {
548
- success: false,
549
- deleted: false,
550
- notFound: true,
551
- error: "CLI tool not available",
552
- branchName
553
- };
554
- }
555
- try {
556
- const isAuth = await this.provider.isAuthenticated(cwd);
557
- if (!isAuth) {
558
- logger3.warn("Skipping database branch deletion: Not authenticated with DB Provider");
559
- return {
560
- success: false,
561
- deleted: false,
562
- notFound: false,
563
- error: "Not authenticated with DB Provider",
564
- branchName
565
- };
566
- }
567
- } catch (error) {
568
- const errorMessage = error instanceof Error ? error.message : String(error);
569
- logger3.error(`Database authentication check failed: ${errorMessage}`);
570
- return {
571
- success: false,
572
- deleted: false,
573
- notFound: false,
574
- error: `Authentication check failed: ${errorMessage}`,
575
- branchName
576
- };
577
- }
578
- try {
579
- const result = await this.provider.deleteBranch(branchName, isPreview, cwd);
580
- return result;
581
- } catch (error) {
582
- logger3.warn(
583
- `Unexpected error in database deletion: ${error instanceof Error ? error.message : String(error)}`
584
- );
585
- return {
586
- success: false,
587
- deleted: false,
588
- notFound: false,
589
- error: error instanceof Error ? error.message : String(error),
590
- branchName
591
- };
592
- }
593
- }
594
- /**
595
- * Check if .env has the configured database URL variable
596
- * CRITICAL: If user explicitly configured a custom variable name (not default),
597
- * throw an error if it's missing from .env
598
- */
599
- async hasDatabaseUrlInEnv(envFilePath) {
600
- try {
601
- const envMap = await this.environment.readEnvFile(envFilePath);
602
- if (this.databaseUrlEnvVarName !== "DATABASE_URL") {
603
- logger3.debug(`Looking for custom database URL variable: ${this.databaseUrlEnvVarName}`);
604
- } else {
605
- logger3.debug("Looking for default database URL variable: DATABASE_URL");
606
- }
607
- if (envMap.has(this.databaseUrlEnvVarName)) {
608
- if (this.databaseUrlEnvVarName !== "DATABASE_URL") {
609
- logger3.debug(`\u2705 Found custom database URL variable: ${this.databaseUrlEnvVarName}`);
610
- } else {
611
- logger3.debug(`\u2705 Found default database URL variable: DATABASE_URL`);
612
- }
613
- return true;
614
- }
615
- if (this.databaseUrlEnvVarName !== "DATABASE_URL") {
616
- logger3.debug(`\u274C Custom database URL variable '${this.databaseUrlEnvVarName}' not found in .env file`);
617
- throw new Error(
618
- `Configured database URL environment variable '${this.databaseUrlEnvVarName}' not found in .env file. Please add it to your .env file or update your iloom configuration.`
619
- );
620
- }
621
- const hasDefaultVar = envMap.has("DATABASE_URL");
622
- if (hasDefaultVar) {
623
- logger3.debug("\u2705 Found fallback DATABASE_URL variable");
624
- } else {
625
- logger3.debug("\u274C No DATABASE_URL variable found in .env file");
626
- }
627
- return hasDefaultVar;
628
- } catch (error) {
629
- if (error instanceof Error && error.message.includes("not found in .env")) {
630
- throw error;
631
- }
632
- return false;
633
- }
634
- }
635
- };
636
-
637
- export {
638
- EnvironmentManager,
639
- CLIIsolationManager,
640
- DatabaseManager
641
- };
642
- //# sourceMappingURL=chunk-H5LDRGVK.js.map