@iloom/cli 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/LICENSE +33 -0
  2. package/README.md +711 -0
  3. package/dist/ClaudeContextManager-XOSXQ67R.js +13 -0
  4. package/dist/ClaudeContextManager-XOSXQ67R.js.map +1 -0
  5. package/dist/ClaudeService-YSZ6EXWP.js +12 -0
  6. package/dist/ClaudeService-YSZ6EXWP.js.map +1 -0
  7. package/dist/GitHubService-F7Z3XJOS.js +11 -0
  8. package/dist/GitHubService-F7Z3XJOS.js.map +1 -0
  9. package/dist/LoomLauncher-MODG2SEM.js +263 -0
  10. package/dist/LoomLauncher-MODG2SEM.js.map +1 -0
  11. package/dist/NeonProvider-PAGPUH7F.js +12 -0
  12. package/dist/NeonProvider-PAGPUH7F.js.map +1 -0
  13. package/dist/PromptTemplateManager-7FINLRDE.js +9 -0
  14. package/dist/PromptTemplateManager-7FINLRDE.js.map +1 -0
  15. package/dist/SettingsManager-VAZF26S2.js +19 -0
  16. package/dist/SettingsManager-VAZF26S2.js.map +1 -0
  17. package/dist/SettingsMigrationManager-MTQIMI54.js +146 -0
  18. package/dist/SettingsMigrationManager-MTQIMI54.js.map +1 -0
  19. package/dist/add-issue-22JBNOML.js +54 -0
  20. package/dist/add-issue-22JBNOML.js.map +1 -0
  21. package/dist/agents/iloom-issue-analyze-and-plan.md +580 -0
  22. package/dist/agents/iloom-issue-analyzer.md +290 -0
  23. package/dist/agents/iloom-issue-complexity-evaluator.md +224 -0
  24. package/dist/agents/iloom-issue-enhancer.md +266 -0
  25. package/dist/agents/iloom-issue-implementer.md +262 -0
  26. package/dist/agents/iloom-issue-planner.md +358 -0
  27. package/dist/agents/iloom-issue-reviewer.md +63 -0
  28. package/dist/chunk-2ZPFJQ3B.js +63 -0
  29. package/dist/chunk-2ZPFJQ3B.js.map +1 -0
  30. package/dist/chunk-37DYYFVK.js +29 -0
  31. package/dist/chunk-37DYYFVK.js.map +1 -0
  32. package/dist/chunk-BLCTGFZN.js +121 -0
  33. package/dist/chunk-BLCTGFZN.js.map +1 -0
  34. package/dist/chunk-CP2NU2JC.js +545 -0
  35. package/dist/chunk-CP2NU2JC.js.map +1 -0
  36. package/dist/chunk-CWR2SANQ.js +39 -0
  37. package/dist/chunk-CWR2SANQ.js.map +1 -0
  38. package/dist/chunk-F3XBU2R7.js +110 -0
  39. package/dist/chunk-F3XBU2R7.js.map +1 -0
  40. package/dist/chunk-GEHQXLEI.js +130 -0
  41. package/dist/chunk-GEHQXLEI.js.map +1 -0
  42. package/dist/chunk-GYCR2LOU.js +143 -0
  43. package/dist/chunk-GYCR2LOU.js.map +1 -0
  44. package/dist/chunk-GZP4UGGM.js +48 -0
  45. package/dist/chunk-GZP4UGGM.js.map +1 -0
  46. package/dist/chunk-H4E4THUZ.js +55 -0
  47. package/dist/chunk-H4E4THUZ.js.map +1 -0
  48. package/dist/chunk-HPJJSYNS.js +644 -0
  49. package/dist/chunk-HPJJSYNS.js.map +1 -0
  50. package/dist/chunk-JBH2ZYYZ.js +220 -0
  51. package/dist/chunk-JBH2ZYYZ.js.map +1 -0
  52. package/dist/chunk-JNKJ7NJV.js +78 -0
  53. package/dist/chunk-JNKJ7NJV.js.map +1 -0
  54. package/dist/chunk-JQ7VOSTC.js +437 -0
  55. package/dist/chunk-JQ7VOSTC.js.map +1 -0
  56. package/dist/chunk-KQDEK2ZW.js +199 -0
  57. package/dist/chunk-KQDEK2ZW.js.map +1 -0
  58. package/dist/chunk-O2QWO64Z.js +179 -0
  59. package/dist/chunk-O2QWO64Z.js.map +1 -0
  60. package/dist/chunk-OC4H6HJD.js +248 -0
  61. package/dist/chunk-OC4H6HJD.js.map +1 -0
  62. package/dist/chunk-PR7FKQBG.js +120 -0
  63. package/dist/chunk-PR7FKQBG.js.map +1 -0
  64. package/dist/chunk-PXZBAC2M.js +250 -0
  65. package/dist/chunk-PXZBAC2M.js.map +1 -0
  66. package/dist/chunk-QEPVTTHD.js +383 -0
  67. package/dist/chunk-QEPVTTHD.js.map +1 -0
  68. package/dist/chunk-RSRO7564.js +203 -0
  69. package/dist/chunk-RSRO7564.js.map +1 -0
  70. package/dist/chunk-SJUQ2NDR.js +146 -0
  71. package/dist/chunk-SJUQ2NDR.js.map +1 -0
  72. package/dist/chunk-SPYPLHMK.js +177 -0
  73. package/dist/chunk-SPYPLHMK.js.map +1 -0
  74. package/dist/chunk-SSCQCCJ7.js +75 -0
  75. package/dist/chunk-SSCQCCJ7.js.map +1 -0
  76. package/dist/chunk-SSR5AVRJ.js +41 -0
  77. package/dist/chunk-SSR5AVRJ.js.map +1 -0
  78. package/dist/chunk-T7QPXANZ.js +315 -0
  79. package/dist/chunk-T7QPXANZ.js.map +1 -0
  80. package/dist/chunk-U3WU5OWO.js +203 -0
  81. package/dist/chunk-U3WU5OWO.js.map +1 -0
  82. package/dist/chunk-W3DQTW63.js +124 -0
  83. package/dist/chunk-W3DQTW63.js.map +1 -0
  84. package/dist/chunk-WKEWRSDB.js +151 -0
  85. package/dist/chunk-WKEWRSDB.js.map +1 -0
  86. package/dist/chunk-Y7SAGNUT.js +66 -0
  87. package/dist/chunk-Y7SAGNUT.js.map +1 -0
  88. package/dist/chunk-YETJNRQM.js +39 -0
  89. package/dist/chunk-YETJNRQM.js.map +1 -0
  90. package/dist/chunk-YYSKGAZT.js +384 -0
  91. package/dist/chunk-YYSKGAZT.js.map +1 -0
  92. package/dist/chunk-ZZZWQGTS.js +169 -0
  93. package/dist/chunk-ZZZWQGTS.js.map +1 -0
  94. package/dist/claude-7LUVDZZ4.js +17 -0
  95. package/dist/claude-7LUVDZZ4.js.map +1 -0
  96. package/dist/cleanup-3LUWPSM7.js +412 -0
  97. package/dist/cleanup-3LUWPSM7.js.map +1 -0
  98. package/dist/cli-overrides-XFZWY7CM.js +16 -0
  99. package/dist/cli-overrides-XFZWY7CM.js.map +1 -0
  100. package/dist/cli.js +603 -0
  101. package/dist/cli.js.map +1 -0
  102. package/dist/color-ZVALX37U.js +21 -0
  103. package/dist/color-ZVALX37U.js.map +1 -0
  104. package/dist/enhance-XJIQHVPD.js +166 -0
  105. package/dist/enhance-XJIQHVPD.js.map +1 -0
  106. package/dist/env-MDFL4ZXL.js +23 -0
  107. package/dist/env-MDFL4ZXL.js.map +1 -0
  108. package/dist/feedback-23CLXKFT.js +158 -0
  109. package/dist/feedback-23CLXKFT.js.map +1 -0
  110. package/dist/finish-CY4CIH6O.js +1608 -0
  111. package/dist/finish-CY4CIH6O.js.map +1 -0
  112. package/dist/git-LVRZ57GJ.js +43 -0
  113. package/dist/git-LVRZ57GJ.js.map +1 -0
  114. package/dist/ignite-WXEF2ID5.js +359 -0
  115. package/dist/ignite-WXEF2ID5.js.map +1 -0
  116. package/dist/index.d.ts +1341 -0
  117. package/dist/index.js +3058 -0
  118. package/dist/index.js.map +1 -0
  119. package/dist/init-RHACUR4E.js +123 -0
  120. package/dist/init-RHACUR4E.js.map +1 -0
  121. package/dist/installation-detector-VARGFFRZ.js +11 -0
  122. package/dist/installation-detector-VARGFFRZ.js.map +1 -0
  123. package/dist/logger-MKYH4UDV.js +12 -0
  124. package/dist/logger-MKYH4UDV.js.map +1 -0
  125. package/dist/mcp/chunk-6SDFJ42P.js +62 -0
  126. package/dist/mcp/chunk-6SDFJ42P.js.map +1 -0
  127. package/dist/mcp/claude-YHHHLSXH.js +249 -0
  128. package/dist/mcp/claude-YHHHLSXH.js.map +1 -0
  129. package/dist/mcp/color-QS5BFCNN.js +168 -0
  130. package/dist/mcp/color-QS5BFCNN.js.map +1 -0
  131. package/dist/mcp/github-comment-server.js +165 -0
  132. package/dist/mcp/github-comment-server.js.map +1 -0
  133. package/dist/mcp/terminal-SDCMDVD7.js +202 -0
  134. package/dist/mcp/terminal-SDCMDVD7.js.map +1 -0
  135. package/dist/open-X6BTENPV.js +278 -0
  136. package/dist/open-X6BTENPV.js.map +1 -0
  137. package/dist/prompt-ANTQWHUF.js +13 -0
  138. package/dist/prompt-ANTQWHUF.js.map +1 -0
  139. package/dist/prompts/issue-prompt.txt +230 -0
  140. package/dist/prompts/pr-prompt.txt +35 -0
  141. package/dist/prompts/regular-prompt.txt +14 -0
  142. package/dist/run-2JCPQAX3.js +278 -0
  143. package/dist/run-2JCPQAX3.js.map +1 -0
  144. package/dist/schema/settings.schema.json +221 -0
  145. package/dist/start-LWVRBJ6S.js +982 -0
  146. package/dist/start-LWVRBJ6S.js.map +1 -0
  147. package/dist/terminal-3D6TUAKJ.js +16 -0
  148. package/dist/terminal-3D6TUAKJ.js.map +1 -0
  149. package/dist/test-git-XPF4SZXJ.js +52 -0
  150. package/dist/test-git-XPF4SZXJ.js.map +1 -0
  151. package/dist/test-prefix-XGFXFAYN.js +68 -0
  152. package/dist/test-prefix-XGFXFAYN.js.map +1 -0
  153. package/dist/test-tabs-JRKY3QMM.js +69 -0
  154. package/dist/test-tabs-JRKY3QMM.js.map +1 -0
  155. package/dist/test-webserver-M2I3EV4J.js +62 -0
  156. package/dist/test-webserver-M2I3EV4J.js.map +1 -0
  157. package/dist/update-3ZT2XX2G.js +79 -0
  158. package/dist/update-3ZT2XX2G.js.map +1 -0
  159. package/dist/update-notifier-QSSEB5KC.js +11 -0
  160. package/dist/update-notifier-QSSEB5KC.js.map +1 -0
  161. package/package.json +113 -0
@@ -0,0 +1,278 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ DevServerManager
4
+ } from "./chunk-W3DQTW63.js";
5
+ import "./chunk-GZP4UGGM.js";
6
+ import {
7
+ extractSettingsOverrides
8
+ } from "./chunk-GYCR2LOU.js";
9
+ import {
10
+ openBrowser
11
+ } from "./chunk-YETJNRQM.js";
12
+ import {
13
+ IdentifierParser
14
+ } from "./chunk-H4E4THUZ.js";
15
+ import "./chunk-SPYPLHMK.js";
16
+ import {
17
+ calculatePortForBranch
18
+ } from "./chunk-37DYYFVK.js";
19
+ import "./chunk-BLCTGFZN.js";
20
+ import {
21
+ ProjectCapabilityDetector
22
+ } from "./chunk-CWR2SANQ.js";
23
+ import "./chunk-2ZPFJQ3B.js";
24
+ import {
25
+ extractPort,
26
+ parseEnvFile
27
+ } from "./chunk-SJUQ2NDR.js";
28
+ import {
29
+ GitWorktreeManager
30
+ } from "./chunk-QEPVTTHD.js";
31
+ import {
32
+ SettingsManager
33
+ } from "./chunk-JBH2ZYYZ.js";
34
+ import "./chunk-JQ7VOSTC.js";
35
+ import {
36
+ logger
37
+ } from "./chunk-GEHQXLEI.js";
38
+
39
+ // src/commands/run.ts
40
+ import path from "path";
41
+ import fs from "fs-extra";
42
+ import { execa } from "execa";
43
+ var RunCommand = class {
44
+ constructor(gitWorktreeManager = new GitWorktreeManager(), capabilityDetector = new ProjectCapabilityDetector(), identifierParser = new IdentifierParser(new GitWorktreeManager()), devServerManager = new DevServerManager(), settingsManager = new SettingsManager()) {
45
+ this.gitWorktreeManager = gitWorktreeManager;
46
+ this.capabilityDetector = capabilityDetector;
47
+ this.identifierParser = identifierParser;
48
+ this.devServerManager = devServerManager;
49
+ this.settingsManager = settingsManager;
50
+ }
51
+ async execute(input) {
52
+ const parsed = input.identifier ? await this.parseExplicitInput(input.identifier) : await this.autoDetectFromCurrentDirectory();
53
+ logger.debug(`Parsed input: ${JSON.stringify(parsed)}`);
54
+ const worktree = await this.findWorktreeForIdentifier(parsed);
55
+ logger.info(`Found worktree at: ${worktree.path}`);
56
+ const { capabilities, binEntries } = await this.capabilityDetector.detectCapabilities(worktree.path);
57
+ logger.debug(`Detected capabilities: ${capabilities.join(", ")}`);
58
+ if (capabilities.includes("cli")) {
59
+ await this.runCLITool(worktree.path, binEntries, input.args ?? []);
60
+ } else if (capabilities.includes("web")) {
61
+ await this.openWebBrowser(worktree.path);
62
+ } else {
63
+ throw new Error(
64
+ `No CLI or web capabilities detected for workspace at ${worktree.path}`
65
+ );
66
+ }
67
+ }
68
+ /**
69
+ * Parse explicit identifier input
70
+ */
71
+ async parseExplicitInput(identifier) {
72
+ const parsed = await this.identifierParser.parseForPatternDetection(identifier);
73
+ if (parsed.type === "description") {
74
+ throw new Error("Description input type is not supported in run command");
75
+ }
76
+ const result = {
77
+ type: parsed.type,
78
+ originalInput: parsed.originalInput,
79
+ autoDetected: false
80
+ };
81
+ if (parsed.number !== void 0) {
82
+ result.number = parsed.number;
83
+ }
84
+ if (parsed.branchName !== void 0) {
85
+ result.branchName = parsed.branchName;
86
+ }
87
+ return result;
88
+ }
89
+ /**
90
+ * Auto-detect identifier from current directory
91
+ * Same logic as FinishCommand.autoDetectFromCurrentDirectory()
92
+ */
93
+ async autoDetectFromCurrentDirectory() {
94
+ const currentDir = path.basename(process.cwd());
95
+ const prPattern = /_pr_(\d+)$/;
96
+ const prMatch = currentDir.match(prPattern);
97
+ if (prMatch == null ? void 0 : prMatch[1]) {
98
+ const prNumber = parseInt(prMatch[1], 10);
99
+ logger.debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`);
100
+ return {
101
+ type: "pr",
102
+ number: prNumber,
103
+ originalInput: currentDir,
104
+ autoDetected: true
105
+ };
106
+ }
107
+ const issuePattern = /issue-(\d+)/;
108
+ const issueMatch = currentDir.match(issuePattern);
109
+ if (issueMatch == null ? void 0 : issueMatch[1]) {
110
+ const issueNumber = parseInt(issueMatch[1], 10);
111
+ logger.debug(`Auto-detected issue #${issueNumber} from directory: ${currentDir}`);
112
+ return {
113
+ type: "issue",
114
+ number: issueNumber,
115
+ originalInput: currentDir,
116
+ autoDetected: true
117
+ };
118
+ }
119
+ const repoInfo = await this.gitWorktreeManager.getRepoInfo();
120
+ const currentBranch = repoInfo.currentBranch;
121
+ if (!currentBranch) {
122
+ throw new Error(
123
+ "Could not auto-detect identifier. Please provide an issue number, PR number, or branch name.\nExpected directory pattern: feat/issue-XX-description OR worktree with _pr_N suffix"
124
+ );
125
+ }
126
+ const branchIssueMatch = currentBranch.match(issuePattern);
127
+ if (branchIssueMatch == null ? void 0 : branchIssueMatch[1]) {
128
+ const issueNumber = parseInt(branchIssueMatch[1], 10);
129
+ logger.debug(`Auto-detected issue #${issueNumber} from branch: ${currentBranch}`);
130
+ return {
131
+ type: "issue",
132
+ number: issueNumber,
133
+ originalInput: currentBranch,
134
+ autoDetected: true
135
+ };
136
+ }
137
+ return {
138
+ type: "branch",
139
+ branchName: currentBranch,
140
+ originalInput: currentBranch,
141
+ autoDetected: true
142
+ };
143
+ }
144
+ /**
145
+ * Find worktree for the given identifier
146
+ */
147
+ async findWorktreeForIdentifier(parsed) {
148
+ let worktree = null;
149
+ if (parsed.type === "issue" && parsed.number !== void 0) {
150
+ worktree = await this.gitWorktreeManager.findWorktreeForIssue(parsed.number);
151
+ } else if (parsed.type === "pr" && parsed.number !== void 0) {
152
+ worktree = await this.gitWorktreeManager.findWorktreeForPR(parsed.number, "");
153
+ } else if (parsed.type === "branch" && parsed.branchName) {
154
+ worktree = await this.gitWorktreeManager.findWorktreeForBranch(
155
+ parsed.branchName
156
+ );
157
+ }
158
+ if (!worktree) {
159
+ throw new Error(
160
+ `No worktree found for ${this.formatParsedInput(parsed)}. Run 'il start ${parsed.originalInput}' to create one.`
161
+ );
162
+ }
163
+ return worktree;
164
+ }
165
+ /**
166
+ * Format parsed input for display
167
+ */
168
+ formatParsedInput(parsed) {
169
+ const autoLabel = parsed.autoDetected ? " (auto-detected)" : "";
170
+ if (parsed.type === "issue") {
171
+ return `issue #${parsed.number}${autoLabel}`;
172
+ }
173
+ if (parsed.type === "pr") {
174
+ return `PR #${parsed.number}${autoLabel}`;
175
+ }
176
+ return `branch "${parsed.branchName}"${autoLabel}`;
177
+ }
178
+ /**
179
+ * Run CLI tool directly from worktree bin path (NO SYMLINKS!)
180
+ */
181
+ async runCLITool(worktreePath, binEntries, args) {
182
+ if (Object.keys(binEntries).length === 0) {
183
+ throw new Error("No bin entries found in package.json");
184
+ }
185
+ const firstEntry = Object.entries(binEntries)[0];
186
+ if (!firstEntry) {
187
+ throw new Error("No bin entries found in package.json");
188
+ }
189
+ const [binName, binPath] = firstEntry;
190
+ logger.debug(`Using bin entry: ${binName} -> ${binPath}`);
191
+ const binFilePath = path.resolve(worktreePath, binPath);
192
+ logger.debug(`Resolved bin file path: ${binFilePath}`);
193
+ if (!await fs.pathExists(binFilePath)) {
194
+ throw new Error(
195
+ `CLI executable not found: ${binFilePath}
196
+ Make sure the project is built (run 'il start' first)`
197
+ );
198
+ }
199
+ logger.info(`Running CLI: node ${binFilePath} ${args.join(" ")}`);
200
+ await execa("node", [binFilePath, ...args], {
201
+ stdio: "inherit",
202
+ // Allow interactive CLIs (prompts, colors, etc.)
203
+ cwd: worktreePath,
204
+ // Execute in worktree context
205
+ env: process.env
206
+ // Inherit environment
207
+ });
208
+ }
209
+ /**
210
+ * Open web browser with workspace URL
211
+ * Auto-starts dev server if not already running
212
+ */
213
+ async openWebBrowser(worktreePath) {
214
+ const port = await this.getWorkspacePort(worktreePath);
215
+ const serverReady = await this.devServerManager.ensureServerRunning(
216
+ worktreePath,
217
+ port
218
+ );
219
+ if (!serverReady) {
220
+ logger.warn(
221
+ `Dev server failed to start on port ${port}. Opening browser anyway...`
222
+ );
223
+ }
224
+ const url = `http://localhost:${port}`;
225
+ logger.info(`Opening browser: ${url}`);
226
+ await openBrowser(url);
227
+ logger.success("Browser opened");
228
+ }
229
+ /**
230
+ * Get port for workspace - reads from .env or calculates based on workspace type
231
+ */
232
+ async getWorkspacePort(worktreePath) {
233
+ var _a, _b;
234
+ const cliOverrides = extractSettingsOverrides();
235
+ const settings = await this.settingsManager.loadSettings(void 0, cliOverrides);
236
+ const basePort = ((_b = (_a = settings.capabilities) == null ? void 0 : _a.web) == null ? void 0 : _b.basePort) ?? 3e3;
237
+ const envPath = path.join(worktreePath, ".env");
238
+ if (await fs.pathExists(envPath)) {
239
+ const envContent = await fs.readFile(envPath, "utf8");
240
+ const envMap = parseEnvFile(envContent);
241
+ const port2 = extractPort(envMap);
242
+ if (port2) {
243
+ logger.debug(`Using PORT from .env: ${port2}`);
244
+ return port2;
245
+ }
246
+ }
247
+ logger.debug("PORT not found in .env, calculating from workspace identifier");
248
+ const worktrees = await this.gitWorktreeManager.listWorktrees();
249
+ const worktree = worktrees.find((wt) => wt.path === worktreePath);
250
+ if (!worktree) {
251
+ throw new Error(`Could not find worktree for path: ${worktreePath}`);
252
+ }
253
+ const dirName = path.basename(worktreePath);
254
+ const prPattern = /_pr_(\d+)$/;
255
+ const prMatch = dirName.match(prPattern);
256
+ if (prMatch == null ? void 0 : prMatch[1]) {
257
+ const prNumber = parseInt(prMatch[1], 10);
258
+ const port2 = basePort + prNumber;
259
+ logger.debug(`Calculated PORT for PR #${prNumber}: ${port2}`);
260
+ return port2;
261
+ }
262
+ const issuePattern = /issue-(\d+)/;
263
+ const issueMatch = dirName.match(issuePattern) ?? worktree.branch.match(issuePattern);
264
+ if (issueMatch == null ? void 0 : issueMatch[1]) {
265
+ const issueNumber = parseInt(issueMatch[1], 10);
266
+ const port2 = basePort + issueNumber;
267
+ logger.debug(`Calculated PORT for issue #${issueNumber}: ${port2}`);
268
+ return port2;
269
+ }
270
+ const port = calculatePortForBranch(worktree.branch, basePort);
271
+ logger.debug(`Calculated PORT for branch "${worktree.branch}": ${port}`);
272
+ return port;
273
+ }
274
+ };
275
+ export {
276
+ RunCommand
277
+ };
278
+ //# sourceMappingURL=run-2JCPQAX3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/run.ts"],"sourcesContent":["import path from 'path'\nimport fs from 'fs-extra'\nimport { execa } from 'execa'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { ProjectCapabilityDetector } from '../lib/ProjectCapabilityDetector.js'\nimport { DevServerManager } from '../lib/DevServerManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { IdentifierParser } from '../utils/IdentifierParser.js'\nimport { openBrowser } from '../utils/browser.js'\nimport { parseEnvFile, extractPort } from '../utils/env.js'\nimport { calculatePortForBranch } from '../utils/port.js'\nimport { logger } from '../utils/logger.js'\nimport { extractSettingsOverrides } from '../utils/cli-overrides.js'\nimport type { GitWorktree } from '../types/worktree.js'\n\nexport interface RunCommandInput {\n\tidentifier?: string\n\targs?: string[]\n}\n\ninterface ParsedRunInput {\n\ttype: 'issue' | 'pr' | 'branch'\n\tnumber?: number // For issues and PRs\n\tbranchName?: string // For branches\n\toriginalInput: string\n\tautoDetected: boolean\n}\n\n/**\n * RunCommand - Runs CLI tool or opens workspace in browser\n * Priority: CLI first, Web fallback\n */\nexport class RunCommand {\n\tconstructor(\n\t\tprivate gitWorktreeManager = new GitWorktreeManager(),\n\t\tprivate capabilityDetector = new ProjectCapabilityDetector(),\n\t\tprivate identifierParser = new IdentifierParser(new GitWorktreeManager()),\n\t\tprivate devServerManager = new DevServerManager(),\n\t\tprivate settingsManager = new SettingsManager()\n\t) {}\n\n\tasync execute(input: RunCommandInput): Promise<void> {\n\t\t// 1. Parse or auto-detect identifier\n\t\tconst parsed = input.identifier\n\t\t\t? await this.parseExplicitInput(input.identifier)\n\t\t\t: await this.autoDetectFromCurrentDirectory()\n\n\t\tlogger.debug(`Parsed input: ${JSON.stringify(parsed)}`)\n\n\t\t// 2. Find worktree path based on identifier\n\t\tconst worktree = await this.findWorktreeForIdentifier(parsed)\n\n\t\tlogger.info(`Found worktree at: ${worktree.path}`)\n\n\t\t// 3. Detect project capabilities\n\t\tconst { capabilities, binEntries } =\n\t\t\tawait this.capabilityDetector.detectCapabilities(worktree.path)\n\n\t\tlogger.debug(`Detected capabilities: ${capabilities.join(', ')}`)\n\n\t\t// 4. Execute based on capabilities (CLI first, web fallback)\n\t\tif (capabilities.includes('cli')) {\n\t\t\tawait this.runCLITool(worktree.path, binEntries, input.args ?? [])\n\t\t} else if (capabilities.includes('web')) {\n\t\t\tawait this.openWebBrowser(worktree.path)\n\t\t} else {\n\t\t\tthrow new Error(\n\t\t\t\t`No CLI or web capabilities detected for workspace at ${worktree.path}`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Parse explicit identifier input\n\t */\n\tprivate async parseExplicitInput(identifier: string): Promise<ParsedRunInput> {\n\t\tconst parsed = await this.identifierParser.parseForPatternDetection(identifier)\n\n\t\t// Description type should never reach run command (converted in start)\n\t\tif (parsed.type === 'description') {\n\t\t\tthrow new Error('Description input type is not supported in run command')\n\t\t}\n\n\t\tconst result: ParsedRunInput = {\n\t\t\ttype: parsed.type,\n\t\t\toriginalInput: parsed.originalInput,\n\t\t\tautoDetected: false,\n\t\t}\n\n\t\tif (parsed.number !== undefined) {\n\t\t\tresult.number = parsed.number\n\t\t}\n\t\tif (parsed.branchName !== undefined) {\n\t\t\tresult.branchName = parsed.branchName\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Auto-detect identifier from current directory\n\t * Same logic as FinishCommand.autoDetectFromCurrentDirectory()\n\t */\n\tprivate async autoDetectFromCurrentDirectory(): Promise<ParsedRunInput> {\n\t\tconst currentDir = path.basename(process.cwd())\n\n\t\t// Check for PR worktree pattern: _pr_N suffix\n\t\tconst prPattern = /_pr_(\\d+)$/\n\t\tconst prMatch = currentDir.match(prPattern)\n\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tlogger.debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'pr',\n\t\t\t\tnumber: prNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Check for issue pattern in directory\n\t\tconst issuePattern = /issue-(\\d+)/\n\t\tconst issueMatch = currentDir.match(issuePattern)\n\n\t\tif (issueMatch?.[1]) {\n\t\t\tconst issueNumber = parseInt(issueMatch[1], 10)\n\t\t\tlogger.debug(`Auto-detected issue #${issueNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: issueNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Fallback: get current branch name\n\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\tconst currentBranch = repoInfo.currentBranch\n\n\t\tif (!currentBranch) {\n\t\t\tthrow new Error(\n\t\t\t\t'Could not auto-detect identifier. Please provide an issue number, PR number, or branch name.\\n' +\n\t\t\t\t\t'Expected directory pattern: feat/issue-XX-description OR worktree with _pr_N suffix'\n\t\t\t)\n\t\t}\n\n\t\t// Try to extract issue from branch name\n\t\tconst branchIssueMatch = currentBranch.match(issuePattern)\n\t\tif (branchIssueMatch?.[1]) {\n\t\t\tconst issueNumber = parseInt(branchIssueMatch[1], 10)\n\t\t\tlogger.debug(`Auto-detected issue #${issueNumber} from branch: ${currentBranch}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: issueNumber,\n\t\t\t\toriginalInput: currentBranch,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Last resort: use branch name\n\t\treturn {\n\t\t\ttype: 'branch',\n\t\t\tbranchName: currentBranch,\n\t\t\toriginalInput: currentBranch,\n\t\t\tautoDetected: true,\n\t\t}\n\t}\n\n\t/**\n\t * Find worktree for the given identifier\n\t */\n\tprivate async findWorktreeForIdentifier(parsed: ParsedRunInput): Promise<GitWorktree> {\n\t\tlet worktree: GitWorktree | null = null\n\n\t\tif (parsed.type === 'issue' && parsed.number !== undefined) {\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForIssue(parsed.number)\n\t\t} else if (parsed.type === 'pr' && parsed.number !== undefined) {\n\t\t\t// Pass empty string for branch name since we don't know it yet\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForPR(parsed.number, '')\n\t\t} else if (parsed.type === 'branch' && parsed.branchName) {\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForBranch(\n\t\t\t\tparsed.branchName\n\t\t\t)\n\t\t}\n\n\t\tif (!worktree) {\n\t\t\tthrow new Error(\n\t\t\t\t`No worktree found for ${this.formatParsedInput(parsed)}. ` +\n\t\t\t\t\t`Run 'il start ${parsed.originalInput}' to create one.`\n\t\t\t)\n\t\t}\n\n\t\treturn worktree\n\t}\n\n\t/**\n\t * Format parsed input for display\n\t */\n\tprivate formatParsedInput(parsed: ParsedRunInput): string {\n\t\tconst autoLabel = parsed.autoDetected ? ' (auto-detected)' : ''\n\n\t\tif (parsed.type === 'issue') {\n\t\t\treturn `issue #${parsed.number}${autoLabel}`\n\t\t}\n\t\tif (parsed.type === 'pr') {\n\t\t\treturn `PR #${parsed.number}${autoLabel}`\n\t\t}\n\t\treturn `branch \"${parsed.branchName}\"${autoLabel}`\n\t}\n\n\t/**\n\t * Run CLI tool directly from worktree bin path (NO SYMLINKS!)\n\t */\n\tprivate async runCLITool(\n\t\tworktreePath: string,\n\t\tbinEntries: Record<string, string>,\n\t\targs: string[]\n\t): Promise<void> {\n\t\t// Validate binEntries exist\n\t\tif (Object.keys(binEntries).length === 0) {\n\t\t\tthrow new Error('No bin entries found in package.json')\n\t\t}\n\n\t\t// Get first bin entry (deterministic)\n\t\tconst firstEntry = Object.entries(binEntries)[0]\n\t\tif (!firstEntry) {\n\t\t\tthrow new Error('No bin entries found in package.json')\n\t\t}\n\t\tconst [binName, binPath] = firstEntry\n\t\tlogger.debug(`Using bin entry: ${binName} -> ${binPath}`)\n\n\t\t// CRITICAL: Construct absolute path (NO SYMLINKS!)\n\t\tconst binFilePath = path.resolve(worktreePath, binPath)\n\t\tlogger.debug(`Resolved bin file path: ${binFilePath}`)\n\n\t\t// Verify file exists\n\t\tif (!(await fs.pathExists(binFilePath))) {\n\t\t\tthrow new Error(\n\t\t\t\t`CLI executable not found: ${binFilePath}\\n` +\n\t\t\t\t\t`Make sure the project is built (run 'il start' first)`\n\t\t\t)\n\t\t}\n\n\t\t// Execute with Node.js\n\t\tlogger.info(`Running CLI: node ${binFilePath} ${args.join(' ')}`)\n\t\tawait execa('node', [binFilePath, ...args], {\n\t\t\tstdio: 'inherit', // Allow interactive CLIs (prompts, colors, etc.)\n\t\t\tcwd: worktreePath, // Execute in worktree context\n\t\t\tenv: process.env, // Inherit environment\n\t\t})\n\t}\n\n\t/**\n\t * Open web browser with workspace URL\n\t * Auto-starts dev server if not already running\n\t */\n\tprivate async openWebBrowser(worktreePath: string): Promise<void> {\n\t\tconst port = await this.getWorkspacePort(worktreePath)\n\n\t\t// Ensure dev server is running on the port\n\t\tconst serverReady = await this.devServerManager.ensureServerRunning(\n\t\t\tworktreePath,\n\t\t\tport\n\t\t)\n\n\t\tif (!serverReady) {\n\t\t\tlogger.warn(\n\t\t\t\t`Dev server failed to start on port ${port}. Opening browser anyway...`\n\t\t\t)\n\t\t}\n\n\t\t// Construct URL and open browser\n\t\tconst url = `http://localhost:${port}`\n\t\tlogger.info(`Opening browser: ${url}`)\n\t\tawait openBrowser(url)\n\t\tlogger.success('Browser opened')\n\t}\n\n\t/**\n\t * Get port for workspace - reads from .env or calculates based on workspace type\n\t */\n\tprivate async getWorkspacePort(worktreePath: string): Promise<number> {\n\t\t// Load base port from settings with CLI overrides\n\t\tconst cliOverrides = extractSettingsOverrides()\n\t\tconst settings = await this.settingsManager.loadSettings(undefined, cliOverrides)\n\t\tconst basePort = settings.capabilities?.web?.basePort ?? 3000\n\n\t\t// Try to read PORT from .env file (as override)\n\t\tconst envPath = path.join(worktreePath, '.env')\n\t\tif (await fs.pathExists(envPath)) {\n\t\t\tconst envContent = await fs.readFile(envPath, 'utf8')\n\t\t\tconst envMap = parseEnvFile(envContent)\n\t\t\tconst port = extractPort(envMap)\n\n\t\t\tif (port) {\n\t\t\t\tlogger.debug(`Using PORT from .env: ${port}`)\n\t\t\t\treturn port\n\t\t\t}\n\t\t}\n\n\t\t// PORT not in .env, calculate based on workspace identifier\n\t\tlogger.debug('PORT not found in .env, calculating from workspace identifier')\n\n\t\t// Get worktree to determine type\n\t\tconst worktrees = await this.gitWorktreeManager.listWorktrees()\n\t\tconst worktree = worktrees.find(wt => wt.path === worktreePath)\n\n\t\tif (!worktree) {\n\t\t\tthrow new Error(`Could not find worktree for path: ${worktreePath}`)\n\t\t}\n\n\t\t// Extract identifier from worktree path/branch\n\t\tconst dirName = path.basename(worktreePath)\n\n\t\t// Check for PR pattern: _pr_N\n\t\tconst prPattern = /_pr_(\\d+)$/\n\t\tconst prMatch = dirName.match(prPattern)\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tconst port = basePort + prNumber\n\t\t\tlogger.debug(`Calculated PORT for PR #${prNumber}: ${port}`)\n\t\t\treturn port\n\t\t}\n\n\t\t// Check for issue pattern: issue-N\n\t\tconst issuePattern = /issue-(\\d+)/\n\t\tconst issueMatch = dirName.match(issuePattern) ?? worktree.branch.match(issuePattern)\n\t\tif (issueMatch?.[1]) {\n\t\t\tconst issueNumber = parseInt(issueMatch[1], 10)\n\t\t\tconst port = basePort + issueNumber\n\t\t\tlogger.debug(`Calculated PORT for issue #${issueNumber}: ${port}`)\n\t\t\treturn port\n\t\t}\n\n\t\t// Branch-based workspace - use deterministic hash\n\t\tconst port = calculatePortForBranch(worktree.branch, basePort)\n\t\tlogger.debug(`Calculated PORT for branch \"${worktree.branch}\": ${port}`)\n\t\treturn port\n\t}\n}\n"],"mappings":"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","names":["port"]}
@@ -0,0 +1,221 @@
1
+ {
2
+ "$ref": "#/definitions/IloomSettings",
3
+ "definitions": {
4
+ "IloomSettings": {
5
+ "type": "object",
6
+ "properties": {
7
+ "mainBranch": {
8
+ "type": "string",
9
+ "minLength": 1,
10
+ "description": "Name of the main/primary branch for the repository"
11
+ },
12
+ "worktreePrefix": {
13
+ "type": "string",
14
+ "description": "Prefix for worktree directories. Empty string disables prefix. Defaults to <repo-name>-looms if not set."
15
+ },
16
+ "protectedBranches": {
17
+ "type": "array",
18
+ "items": {
19
+ "type": "string",
20
+ "minLength": 1
21
+ },
22
+ "description": "List of branches that cannot be deleted (defaults to [mainBranch, \"main\", \"master\", \"develop\"])"
23
+ },
24
+ "workflows": {
25
+ "type": "object",
26
+ "properties": {
27
+ "issue": {
28
+ "type": "object",
29
+ "properties": {
30
+ "permissionMode": {
31
+ "type": "string",
32
+ "enum": [
33
+ "plan",
34
+ "acceptEdits",
35
+ "bypassPermissions",
36
+ "default"
37
+ ],
38
+ "description": "Permission mode for Claude CLI in this workflow type"
39
+ },
40
+ "noVerify": {
41
+ "type": "boolean",
42
+ "description": "Skip pre-commit hooks (--no-verify) when committing during finish workflow"
43
+ },
44
+ "startIde": {
45
+ "type": "boolean",
46
+ "default": true,
47
+ "description": "Launch IDE (code) when starting this workflow type"
48
+ },
49
+ "startDevServer": {
50
+ "type": "boolean",
51
+ "default": true,
52
+ "description": "Launch development server when starting this workflow type"
53
+ },
54
+ "startAiAgent": {
55
+ "type": "boolean",
56
+ "default": true,
57
+ "description": "Launch Claude AI agent when starting this workflow type"
58
+ },
59
+ "startTerminal": {
60
+ "type": "boolean",
61
+ "default": false,
62
+ "description": "Launch terminal window without dev server when starting this workflow type"
63
+ }
64
+ },
65
+ "additionalProperties": false
66
+ },
67
+ "pr": {
68
+ "type": "object",
69
+ "properties": {
70
+ "permissionMode": {
71
+ "type": "string",
72
+ "enum": [
73
+ "plan",
74
+ "acceptEdits",
75
+ "bypassPermissions",
76
+ "default"
77
+ ],
78
+ "description": "Permission mode for Claude CLI in this workflow type"
79
+ },
80
+ "noVerify": {
81
+ "type": "boolean",
82
+ "description": "Skip pre-commit hooks (--no-verify) when committing during finish workflow"
83
+ },
84
+ "startIde": {
85
+ "type": "boolean",
86
+ "default": true,
87
+ "description": "Launch IDE (code) when starting this workflow type"
88
+ },
89
+ "startDevServer": {
90
+ "type": "boolean",
91
+ "default": true,
92
+ "description": "Launch development server when starting this workflow type"
93
+ },
94
+ "startAiAgent": {
95
+ "type": "boolean",
96
+ "default": true,
97
+ "description": "Launch Claude AI agent when starting this workflow type"
98
+ },
99
+ "startTerminal": {
100
+ "type": "boolean",
101
+ "default": false,
102
+ "description": "Launch terminal window without dev server when starting this workflow type"
103
+ }
104
+ },
105
+ "additionalProperties": false
106
+ },
107
+ "regular": {
108
+ "type": "object",
109
+ "properties": {
110
+ "permissionMode": {
111
+ "type": "string",
112
+ "enum": [
113
+ "plan",
114
+ "acceptEdits",
115
+ "bypassPermissions",
116
+ "default"
117
+ ],
118
+ "description": "Permission mode for Claude CLI in this workflow type"
119
+ },
120
+ "noVerify": {
121
+ "type": "boolean",
122
+ "description": "Skip pre-commit hooks (--no-verify) when committing during finish workflow"
123
+ },
124
+ "startIde": {
125
+ "type": "boolean",
126
+ "default": true,
127
+ "description": "Launch IDE (code) when starting this workflow type"
128
+ },
129
+ "startDevServer": {
130
+ "type": "boolean",
131
+ "default": true,
132
+ "description": "Launch development server when starting this workflow type"
133
+ },
134
+ "startAiAgent": {
135
+ "type": "boolean",
136
+ "default": true,
137
+ "description": "Launch Claude AI agent when starting this workflow type"
138
+ },
139
+ "startTerminal": {
140
+ "type": "boolean",
141
+ "default": false,
142
+ "description": "Launch terminal window without dev server when starting this workflow type"
143
+ }
144
+ },
145
+ "additionalProperties": false
146
+ }
147
+ },
148
+ "additionalProperties": false,
149
+ "description": "Per-workflow-type permission configurations"
150
+ },
151
+ "agents": {
152
+ "anyOf": [
153
+ {
154
+ "anyOf": [
155
+ {
156
+ "not": {}
157
+ },
158
+ {
159
+ "type": "object",
160
+ "additionalProperties": {
161
+ "type": "object",
162
+ "properties": {
163
+ "model": {
164
+ "type": "string",
165
+ "enum": [
166
+ "sonnet",
167
+ "opus",
168
+ "haiku"
169
+ ],
170
+ "description": "Claude model shorthand: sonnet, opus, or haiku"
171
+ }
172
+ },
173
+ "additionalProperties": false
174
+ }
175
+ }
176
+ ]
177
+ },
178
+ {
179
+ "type": "null"
180
+ }
181
+ ],
182
+ "description": "Per-agent configuration overrides"
183
+ },
184
+ "capabilities": {
185
+ "type": "object",
186
+ "properties": {
187
+ "web": {
188
+ "type": "object",
189
+ "properties": {
190
+ "basePort": {
191
+ "type": "number",
192
+ "minimum": 1,
193
+ "maximum": 65535,
194
+ "description": "Base port for web workspace port calculations (default: 3000)"
195
+ }
196
+ },
197
+ "additionalProperties": false
198
+ },
199
+ "database": {
200
+ "type": "object",
201
+ "properties": {
202
+ "databaseUrlEnvVarName": {
203
+ "type": "string",
204
+ "minLength": 1,
205
+ "pattern": "^[A-Z_][A-Z0-9_]*$",
206
+ "default": "DATABASE_URL",
207
+ "description": "Name of environment variable for database connection URL"
208
+ }
209
+ },
210
+ "additionalProperties": false
211
+ }
212
+ },
213
+ "additionalProperties": false,
214
+ "description": "Project capability configurations"
215
+ }
216
+ },
217
+ "additionalProperties": false
218
+ }
219
+ },
220
+ "$schema": "http://json-schema.org/draft-07/schema#"
221
+ }