@iloom/cli 0.4.1 → 0.5.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.
- package/README.md +24 -0
- package/dist/{ClaudeContextManager-DK77227F.js → ClaudeContextManager-DQFKIMEP.js} +5 -5
- package/dist/{ClaudeService-W3SA7HVG.js → ClaudeService-CJS32WG2.js} +4 -4
- package/dist/{LoomLauncher-S3YGJRJQ.js → LoomLauncher-JNWBMHES.js} +22 -88
- package/dist/LoomLauncher-JNWBMHES.js.map +1 -0
- package/dist/MetadataManager-WXUVXKUS.js +10 -0
- package/dist/PRManager-7DSIMCAD.js +16 -0
- package/dist/{PromptTemplateManager-2TDZAUC6.js → PromptTemplateManager-72FEOGT6.js} +2 -2
- package/dist/README.md +24 -0
- package/dist/{SettingsManager-FJFU6JJD.js → SettingsManager-XPR4TEQL.js} +2 -2
- package/dist/agents/iloom-issue-analyze-and-plan.md +41 -7
- package/dist/agents/iloom-issue-analyzer.md +38 -8
- package/dist/agents/iloom-issue-complexity-evaluator.md +45 -15
- package/dist/agents/iloom-issue-enhancer.md +60 -18
- package/dist/agents/iloom-issue-implementer.md +29 -7
- package/dist/agents/iloom-issue-planner.md +36 -7
- package/dist/agents/iloom-issue-reviewer.md +30 -7
- package/dist/{chunk-JC5HXN75.js → chunk-3CMGCRB5.js} +2 -2
- package/dist/{chunk-G6CIIJLT.js → chunk-4YTILIIH.js} +7 -8
- package/dist/chunk-4YTILIIH.js.map +1 -0
- package/dist/{chunk-IARWMDAX.js → chunk-6KB7R22U.js} +98 -16
- package/dist/chunk-6KB7R22U.js.map +1 -0
- package/dist/{chunk-55TB3FSG.js → chunk-AS2IRKLU.js} +2 -2
- package/dist/{chunk-VTXCGKV5.js → chunk-BVIK2P6P.js} +11 -3
- package/dist/chunk-BVIK2P6P.js.map +1 -0
- package/dist/chunk-CDF7ZX2B.js +72 -0
- package/dist/chunk-CDF7ZX2B.js.map +1 -0
- package/dist/{chunk-POI7KLBH.js → chunk-CDQEK2WD.js} +5 -5
- package/dist/{chunk-74VMN2KC.js → chunk-DKQ4SUII.js} +16 -1
- package/dist/chunk-DKQ4SUII.js.map +1 -0
- package/dist/{chunk-BIIQHEXJ.js → chunk-GVRO4PWE.js} +12 -8
- package/dist/chunk-GVRO4PWE.js.map +1 -0
- package/dist/{chunk-TMZAVPGF.js → chunk-HABINPX2.js} +71 -15
- package/dist/{chunk-TMZAVPGF.js.map → chunk-HABINPX2.js.map} +1 -1
- package/dist/{chunk-2W2FBL5G.js → chunk-LN4H3A6A.js} +66 -7
- package/dist/chunk-LN4H3A6A.js.map +1 -0
- package/dist/{chunk-VWNS6DH5.js → chunk-OOU3DKNT.js} +13 -7
- package/dist/chunk-OOU3DKNT.js.map +1 -0
- package/dist/chunk-P2ZQ5LKB.js +347 -0
- package/dist/chunk-P2ZQ5LKB.js.map +1 -0
- package/dist/{chunk-OF7BNW4D.js → chunk-RJKMF6BC.js} +30 -4
- package/dist/chunk-RJKMF6BC.js.map +1 -0
- package/dist/{chunk-O7WHXLCB.js → chunk-RNZMHJK7.js} +18 -4
- package/dist/chunk-RNZMHJK7.js.map +1 -0
- package/dist/{chunk-UPUAQYAW.js → chunk-S65T4O6I.js} +2 -2
- package/dist/{chunk-HD5SUKI2.js → chunk-TSLKDFAF.js} +55 -6
- package/dist/chunk-TSLKDFAF.js.map +1 -0
- package/dist/{chunk-IJ7IGJT3.js → chunk-YZTDGPFB.js} +18 -1
- package/dist/chunk-YZTDGPFB.js.map +1 -0
- package/dist/{cleanup-KDLVTT7M.js → cleanup-LU6NU2NZ.js} +14 -14
- package/dist/cli.js +283 -363
- package/dist/cli.js.map +1 -1
- package/dist/{contribute-HY372S6F.js → contribute-RS3DO3WP.js} +4 -4
- package/dist/{dev-server-JCJGQ3PV.js → dev-server-ASH7HJVI.js} +30 -16
- package/dist/dev-server-ASH7HJVI.js.map +1 -0
- package/dist/{feedback-7PVBQNLJ.js → feedback-OFVW22UW.js} +11 -6
- package/dist/{feedback-7PVBQNLJ.js.map → feedback-OFVW22UW.js.map} +1 -1
- package/dist/{git-4BVOOOOV.js → git-OQAPUPLP.js} +16 -6
- package/dist/git-OQAPUPLP.js.map +1 -0
- package/dist/{ignite-3B264M7K.js → ignite-NREQ3JRM.js} +57 -22
- package/dist/ignite-NREQ3JRM.js.map +1 -0
- package/dist/index.d.ts +58 -7
- package/dist/index.js +110 -7
- package/dist/index.js.map +1 -1
- package/dist/{init-LBA6NUK2.js → init-F6PFMSU5.js} +7 -7
- package/dist/init-F6PFMSU5.js.map +1 -0
- package/dist/mcp/recap-server.js +264 -0
- package/dist/mcp/recap-server.js.map +1 -0
- package/dist/{open-OGCV32Z4.js → open-KW4NTLXH.js} +16 -17
- package/dist/{open-OGCV32Z4.js.map → open-KW4NTLXH.js.map} +1 -1
- package/dist/{projects-P55273AB.js → projects-QEAEBAT2.js} +2 -2
- package/dist/prompts/init-prompt.txt +31 -72
- package/dist/prompts/issue-prompt.txt +115 -15
- package/dist/prompts/pr-prompt.txt +49 -1
- package/dist/prompts/regular-prompt.txt +80 -20
- package/dist/{rebase-4T5FQHNH.js → rebase-WZHHE5LU.js} +6 -6
- package/dist/recap-33NPZ3ZO.js +117 -0
- package/dist/recap-33NPZ3ZO.js.map +1 -0
- package/dist/{run-HNOP6WE2.js → run-HRYQ7TR7.js} +16 -17
- package/dist/{run-HNOP6WE2.js.map → run-HRYQ7TR7.js.map} +1 -1
- package/dist/schema/settings.schema.json +13 -2
- package/dist/{shell-DE3HKJSM.js → shell-JMU5XTHW.js} +6 -6
- package/dist/{summary-GDT7DTRI.js → summary-4SSGGH7N.js} +17 -9
- package/dist/summary-4SSGGH7N.js.map +1 -0
- package/dist/{test-git-YMAE57UP.js → test-git-6SAIRBUD.js} +4 -4
- package/dist/{test-prefix-YCKL6CMT.js → test-prefix-RLVRK5ZD.js} +4 -4
- package/package.json +1 -1
- package/dist/LoomLauncher-S3YGJRJQ.js.map +0 -1
- package/dist/chunk-2W2FBL5G.js.map +0 -1
- package/dist/chunk-74VMN2KC.js.map +0 -1
- package/dist/chunk-BIIQHEXJ.js.map +0 -1
- package/dist/chunk-G6CIIJLT.js.map +0 -1
- package/dist/chunk-HD5SUKI2.js.map +0 -1
- package/dist/chunk-IARWMDAX.js.map +0 -1
- package/dist/chunk-IJ7IGJT3.js.map +0 -1
- package/dist/chunk-O7WHXLCB.js.map +0 -1
- package/dist/chunk-OF7BNW4D.js.map +0 -1
- package/dist/chunk-QRBOPFAA.js +0 -48
- package/dist/chunk-QRBOPFAA.js.map +0 -1
- package/dist/chunk-VTXCGKV5.js.map +0 -1
- package/dist/chunk-VWNS6DH5.js.map +0 -1
- package/dist/dev-server-JCJGQ3PV.js.map +0 -1
- package/dist/ignite-3B264M7K.js.map +0 -1
- package/dist/summary-GDT7DTRI.js.map +0 -1
- /package/dist/{ClaudeContextManager-DK77227F.js.map → ClaudeContextManager-DQFKIMEP.js.map} +0 -0
- /package/dist/{ClaudeService-W3SA7HVG.js.map → ClaudeService-CJS32WG2.js.map} +0 -0
- /package/dist/{PromptTemplateManager-2TDZAUC6.js.map → MetadataManager-WXUVXKUS.js.map} +0 -0
- /package/dist/{SettingsManager-FJFU6JJD.js.map → PRManager-7DSIMCAD.js.map} +0 -0
- /package/dist/{git-4BVOOOOV.js.map → PromptTemplateManager-72FEOGT6.js.map} +0 -0
- /package/dist/{init-LBA6NUK2.js.map → SettingsManager-XPR4TEQL.js.map} +0 -0
- /package/dist/{chunk-JC5HXN75.js.map → chunk-3CMGCRB5.js.map} +0 -0
- /package/dist/{chunk-55TB3FSG.js.map → chunk-AS2IRKLU.js.map} +0 -0
- /package/dist/{chunk-POI7KLBH.js.map → chunk-CDQEK2WD.js.map} +0 -0
- /package/dist/{chunk-UPUAQYAW.js.map → chunk-S65T4O6I.js.map} +0 -0
- /package/dist/{cleanup-KDLVTT7M.js.map → cleanup-LU6NU2NZ.js.map} +0 -0
- /package/dist/{contribute-HY372S6F.js.map → contribute-RS3DO3WP.js.map} +0 -0
- /package/dist/{projects-P55273AB.js.map → projects-QEAEBAT2.js.map} +0 -0
- /package/dist/{rebase-4T5FQHNH.js.map → rebase-WZHHE5LU.js.map} +0 -0
- /package/dist/{shell-DE3HKJSM.js.map → shell-JMU5XTHW.js.map} +0 -0
- /package/dist/{test-git-YMAE57UP.js.map → test-git-6SAIRBUD.js.map} +0 -0
- /package/dist/{test-prefix-YCKL6CMT.js.map → test-prefix-RLVRK5ZD.js.map} +0 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
IdentifierParser
|
|
4
|
+
} from "./chunk-AS2IRKLU.js";
|
|
5
|
+
import {
|
|
6
|
+
GitWorktreeManager
|
|
7
|
+
} from "./chunk-3CMGCRB5.js";
|
|
8
|
+
import "./chunk-LN4H3A6A.js";
|
|
9
|
+
import "./chunk-OOU3DKNT.js";
|
|
10
|
+
import "./chunk-YZTDGPFB.js";
|
|
11
|
+
import "./chunk-6UIGZD2N.js";
|
|
12
|
+
import "./chunk-UYVWLISQ.js";
|
|
13
|
+
|
|
14
|
+
// src/commands/recap.ts
|
|
15
|
+
import path from "path";
|
|
16
|
+
import os from "os";
|
|
17
|
+
import fs from "fs-extra";
|
|
18
|
+
var RECAPS_DIR = path.join(os.homedir(), ".config", "iloom-ai", "recaps");
|
|
19
|
+
function slugifyPath(loomPath) {
|
|
20
|
+
let slug = loomPath.replace(/[/\\]+$/, "");
|
|
21
|
+
slug = slug.replace(/[/\\]/g, "___");
|
|
22
|
+
slug = slug.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
23
|
+
return `${slug}.json`;
|
|
24
|
+
}
|
|
25
|
+
var RecapCommand = class {
|
|
26
|
+
/**
|
|
27
|
+
* Execute the recap command
|
|
28
|
+
* Returns RecapOutput in JSON mode, void otherwise
|
|
29
|
+
*/
|
|
30
|
+
async execute(input) {
|
|
31
|
+
const loomPath = await this.resolveLoomPath(input.identifier);
|
|
32
|
+
const filePath = path.join(RECAPS_DIR, slugifyPath(loomPath));
|
|
33
|
+
let recap = {};
|
|
34
|
+
try {
|
|
35
|
+
if (await fs.pathExists(filePath)) {
|
|
36
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
37
|
+
recap = JSON.parse(content);
|
|
38
|
+
}
|
|
39
|
+
} catch {
|
|
40
|
+
}
|
|
41
|
+
const goal = recap.goal ?? null;
|
|
42
|
+
const complexity = recap.complexity ?? null;
|
|
43
|
+
const entries = recap.entries ?? [];
|
|
44
|
+
const artifacts = recap.artifacts ?? [];
|
|
45
|
+
const result = { filePath, goal, complexity, entries, artifacts };
|
|
46
|
+
if (input.json) {
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
console.log(`Recap file: ${filePath}`);
|
|
50
|
+
console.log(`Goal: ${goal ?? "(not set)"}`);
|
|
51
|
+
console.log(`Complexity: ${complexity ? `${complexity.level}${complexity.reason ? ` - ${complexity.reason}` : ""}` : "(not set)"}`);
|
|
52
|
+
console.log(`Entries: ${entries.length}`);
|
|
53
|
+
for (const entry of entries) {
|
|
54
|
+
console.log(` [${entry.type}] ${entry.content}`);
|
|
55
|
+
}
|
|
56
|
+
console.log(`Artifacts: ${artifacts.length}`);
|
|
57
|
+
for (const artifact of artifacts) {
|
|
58
|
+
console.log(` [${artifact.type}] ${artifact.description} - ${artifact.primaryUrl}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Resolve identifier to loom path
|
|
63
|
+
* Falls back to cwd when no identifier is provided (backward compatible)
|
|
64
|
+
*/
|
|
65
|
+
async resolveLoomPath(identifier) {
|
|
66
|
+
if (!(identifier == null ? void 0 : identifier.trim())) {
|
|
67
|
+
return process.cwd();
|
|
68
|
+
}
|
|
69
|
+
const trimmedId = identifier.trim();
|
|
70
|
+
const gitWorktreeManager = new GitWorktreeManager();
|
|
71
|
+
const identifierParser = new IdentifierParser(gitWorktreeManager);
|
|
72
|
+
const prPattern = /^(?:pr|PR)[/-](\d+)$/;
|
|
73
|
+
const prMatch = trimmedId.match(prPattern);
|
|
74
|
+
if (prMatch == null ? void 0 : prMatch[1]) {
|
|
75
|
+
const prNumber = parseInt(prMatch[1], 10);
|
|
76
|
+
const worktree = await gitWorktreeManager.findWorktreeForPR(prNumber, "");
|
|
77
|
+
if (worktree) {
|
|
78
|
+
return worktree.path;
|
|
79
|
+
}
|
|
80
|
+
throw new Error(`No worktree found for PR #${prNumber}`);
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
const parsed = await identifierParser.parseForPatternDetection(trimmedId);
|
|
84
|
+
if (parsed.type === "pr" && typeof parsed.number === "number") {
|
|
85
|
+
const worktree = await gitWorktreeManager.findWorktreeForPR(parsed.number, "");
|
|
86
|
+
if (worktree) {
|
|
87
|
+
return worktree.path;
|
|
88
|
+
}
|
|
89
|
+
throw new Error(`No worktree found for PR #${parsed.number}`);
|
|
90
|
+
}
|
|
91
|
+
if (parsed.type === "issue" && parsed.number !== void 0) {
|
|
92
|
+
const worktree = await gitWorktreeManager.findWorktreeForIssue(parsed.number);
|
|
93
|
+
if (worktree) {
|
|
94
|
+
return worktree.path;
|
|
95
|
+
}
|
|
96
|
+
throw new Error(`No worktree found for issue #${parsed.number}`);
|
|
97
|
+
}
|
|
98
|
+
if (parsed.type === "branch" && parsed.branchName) {
|
|
99
|
+
const worktree = await gitWorktreeManager.findWorktreeForBranch(parsed.branchName);
|
|
100
|
+
if (worktree) {
|
|
101
|
+
return worktree.path;
|
|
102
|
+
}
|
|
103
|
+
throw new Error(`No worktree found for branch: ${parsed.branchName}`);
|
|
104
|
+
}
|
|
105
|
+
} catch (error) {
|
|
106
|
+
if (error instanceof Error) {
|
|
107
|
+
throw new Error(`Could not resolve identifier '${identifier}': ${error.message}`);
|
|
108
|
+
}
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
throw new Error(`Could not resolve identifier: ${identifier}`);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
export {
|
|
115
|
+
RecapCommand
|
|
116
|
+
};
|
|
117
|
+
//# sourceMappingURL=recap-33NPZ3ZO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/recap.ts"],"sourcesContent":["/**\n * RecapCommand - Fast read-only command for VS Code extension\n *\n * Reads ~/.config/iloom-ai/recaps/{current-loom}.json and outputs it.\n * Skips config validation for fast startup.\n * Includes filePath in output so extension can set up file watcher.\n */\nimport path from 'path'\nimport os from 'os'\nimport fs from 'fs-extra'\nimport type { RecapFile, RecapOutput } from '../mcp/recap-types.js'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { IdentifierParser } from '../utils/IdentifierParser.js'\n\nconst RECAPS_DIR = path.join(os.homedir(), '.config', 'iloom-ai', 'recaps')\n\n/**\n * Reuse MetadataManager.slugifyPath() algorithm\n *\n * Algorithm:\n * 1. Trim trailing slashes\n * 2. Replace all path separators (/ or \\) with ___ (triple underscore)\n * 3. Replace any other non-alphanumeric characters (except _ and -) with -\n * 4. Append .json\n */\nfunction slugifyPath(loomPath: string): string {\n\tlet slug = loomPath.replace(/[/\\\\]+$/, '')\n\tslug = slug.replace(/[/\\\\]/g, '___')\n\tslug = slug.replace(/[^a-zA-Z0-9_-]/g, '-')\n\treturn `${slug}.json`\n}\n\nexport interface RecapCommandInput {\n\tidentifier?: string | undefined // Optional identifier (issue number, PR number, branch name)\n\tjson?: boolean | undefined\n}\n\nexport class RecapCommand {\n\t/**\n\t * Execute the recap command\n\t * Returns RecapOutput in JSON mode, void otherwise\n\t */\n\tasync execute(input: RecapCommandInput): Promise<RecapOutput | void> {\n\t\t// Resolve loom path from identifier or fall back to cwd\n\t\tconst loomPath = await this.resolveLoomPath(input.identifier)\n\t\tconst filePath = path.join(RECAPS_DIR, slugifyPath(loomPath))\n\n\t\t// Read recap file (return empty object if not found)\n\t\tlet recap: RecapFile = {}\n\t\ttry {\n\t\t\tif (await fs.pathExists(filePath)) {\n\t\t\t\tconst content = await fs.readFile(filePath, 'utf8')\n\t\t\t\trecap = JSON.parse(content) as RecapFile\n\t\t\t}\n\t\t} catch {\n\t\t\t// Graceful degradation - return empty recap on read error\n\t\t\t// This is intentional for fast startup\n\t\t}\n\n\t\t// Build output with filePath for file watching (provide defaults for optional fields)\n\t\tconst goal = recap.goal ?? null\n\t\tconst complexity = recap.complexity ?? null\n\t\tconst entries = recap.entries ?? []\n\t\tconst artifacts = recap.artifacts ?? []\n\t\tconst result: RecapOutput = { filePath, goal, complexity, entries, artifacts }\n\n\t\tif (input.json) {\n\t\t\treturn result\n\t\t}\n\n\t\t// Non-JSON mode: print human-readable format (intentionally using console.log for piping/redirection)\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.log(`Recap file: ${filePath}`)\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.log(`Goal: ${goal ?? '(not set)'}`)\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.log(`Complexity: ${complexity ? `${complexity.level}${complexity.reason ? ` - ${complexity.reason}` : ''}` : '(not set)'}`)\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.log(`Entries: ${entries.length}`)\n\t\tfor (const entry of entries) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.log(` [${entry.type}] ${entry.content}`)\n\t\t}\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.log(`Artifacts: ${artifacts.length}`)\n\t\tfor (const artifact of artifacts) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.log(` [${artifact.type}] ${artifact.description} - ${artifact.primaryUrl}`)\n\t\t}\n\t}\n\n\t/**\n\t * Resolve identifier to loom path\n\t * Falls back to cwd when no identifier is provided (backward compatible)\n\t */\n\tprivate async resolveLoomPath(identifier: string | undefined): Promise<string> {\n\t\t// Default: use current working directory\n\t\tif (!identifier?.trim()) {\n\t\t\treturn process.cwd()\n\t\t}\n\n\t\tconst trimmedId = identifier.trim()\n\t\tconst gitWorktreeManager = new GitWorktreeManager()\n\t\tconst identifierParser = new IdentifierParser(gitWorktreeManager)\n\n\t\t// Check for PR-specific formats: pr/123, PR-123, PR/123\n\t\tconst prPattern = /^(?:pr|PR)[/-](\\d+)$/\n\t\tconst prMatch = trimmedId.match(prPattern)\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tconst worktree = await gitWorktreeManager.findWorktreeForPR(prNumber, '')\n\t\t\tif (worktree) {\n\t\t\t\treturn worktree.path\n\t\t\t}\n\t\t\tthrow new Error(`No worktree found for PR #${prNumber}`)\n\t\t}\n\n\t\t// Use IdentifierParser for pattern-based detection\n\t\ttry {\n\t\t\tconst parsed = await identifierParser.parseForPatternDetection(trimmedId)\n\n\t\t\t// Find worktree based on parsed type\n\t\t\tif (parsed.type === 'pr' && typeof parsed.number === 'number') {\n\t\t\t\tconst worktree = await gitWorktreeManager.findWorktreeForPR(parsed.number, '')\n\t\t\t\tif (worktree) {\n\t\t\t\t\treturn worktree.path\n\t\t\t\t}\n\t\t\t\tthrow new Error(`No worktree found for PR #${parsed.number}`)\n\t\t\t}\n\n\t\t\tif (parsed.type === 'issue' && parsed.number !== undefined) {\n\t\t\t\tconst worktree = await gitWorktreeManager.findWorktreeForIssue(parsed.number)\n\t\t\t\tif (worktree) {\n\t\t\t\t\treturn worktree.path\n\t\t\t\t}\n\t\t\t\tthrow new Error(`No worktree found for issue #${parsed.number}`)\n\t\t\t}\n\n\t\t\tif (parsed.type === 'branch' && parsed.branchName) {\n\t\t\t\tconst worktree = await gitWorktreeManager.findWorktreeForBranch(parsed.branchName)\n\t\t\t\tif (worktree) {\n\t\t\t\t\treturn worktree.path\n\t\t\t\t}\n\t\t\t\tthrow new Error(`No worktree found for branch: ${parsed.branchName}`)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Re-throw IdentifierParser errors with context\n\t\t\tif (error instanceof Error) {\n\t\t\t\tthrow new Error(`Could not resolve identifier '${identifier}': ${error.message}`)\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\n\t\t// Should not reach here, but provide a fallback error\n\t\tthrow new Error(`Could not resolve identifier: ${identifier}`)\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,35 +1,34 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
DevServerManager
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import {
|
|
7
|
-
IdentifierParser
|
|
8
|
-
} from "./chunk-55TB3FSG.js";
|
|
4
|
+
} from "./chunk-RJKMF6BC.js";
|
|
5
|
+
import "./chunk-VBFDVGAE.js";
|
|
9
6
|
import {
|
|
10
7
|
calculatePortForBranch
|
|
11
8
|
} from "./chunk-VU3QMIP2.js";
|
|
12
9
|
import {
|
|
13
|
-
|
|
14
|
-
} from "./chunk-
|
|
10
|
+
IdentifierParser
|
|
11
|
+
} from "./chunk-AS2IRKLU.js";
|
|
12
|
+
import {
|
|
13
|
+
ProjectCapabilityDetector
|
|
14
|
+
} from "./chunk-EBISESAP.js";
|
|
15
|
+
import "./chunk-2ZPFJQ3B.js";
|
|
15
16
|
import {
|
|
16
17
|
GitWorktreeManager
|
|
17
|
-
} from "./chunk-
|
|
18
|
-
import
|
|
18
|
+
} from "./chunk-3CMGCRB5.js";
|
|
19
|
+
import {
|
|
20
|
+
openBrowser
|
|
21
|
+
} from "./chunk-YETJNRQM.js";
|
|
19
22
|
import {
|
|
20
23
|
extractSettingsOverrides
|
|
21
24
|
} from "./chunk-GYCR2LOU.js";
|
|
22
|
-
import {
|
|
23
|
-
ProjectCapabilityDetector
|
|
24
|
-
} from "./chunk-EBISESAP.js";
|
|
25
|
-
import "./chunk-2ZPFJQ3B.js";
|
|
26
25
|
import {
|
|
27
26
|
extractIssueNumber
|
|
28
|
-
} from "./chunk-
|
|
29
|
-
import "./chunk-IJ7IGJT3.js";
|
|
27
|
+
} from "./chunk-LN4H3A6A.js";
|
|
30
28
|
import {
|
|
31
29
|
SettingsManager
|
|
32
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-OOU3DKNT.js";
|
|
31
|
+
import "./chunk-YZTDGPFB.js";
|
|
33
32
|
import {
|
|
34
33
|
extractPort,
|
|
35
34
|
findEnvFileContainingVariable,
|
|
@@ -291,4 +290,4 @@ Make sure the project is built (run 'il start' first)`
|
|
|
291
290
|
export {
|
|
292
291
|
RunCommand
|
|
293
292
|
};
|
|
294
|
-
//# sourceMappingURL=run-
|
|
293
|
+
//# sourceMappingURL=run-HRYQ7TR7.js.map
|
|
@@ -1 +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, findEnvFileContainingVariable } from '../utils/env.js'\nimport { calculatePortForBranch } from '../utils/port.js'\nimport { extractIssueNumber } from '../utils/git.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?: string | 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 issueNumber = extractIssueNumber(currentDir)\n\n\t\tif (issueNumber !== null) {\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 branchIssueNumber = extractIssueNumber(currentBranch)\n\t\tif (branchIssueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: branchIssueNumber,\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// For PRs, ensure the number is numeric (PRs are always numeric per GitHub)\n\t\t\tconst prNumber = typeof parsed.number === 'number' ? parsed.number : Number(parsed.number)\n\t\t\tif (isNaN(prNumber) || !isFinite(prNumber)) {\n\t\t\t\tthrow new Error(`Invalid PR number: ${parsed.number}. PR numbers must be numeric.`)\n\t\t\t}\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(prNumber, '')\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 dotenv-flow files 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 any dotenv-flow file (as override)\n\t\tconst envFile = await findEnvFileContainingVariable(\n\t\t\tworktreePath,\n\t\t\t'PORT',\n\t\t\tasync (p) => fs.pathExists(p),\n\t\t\tasync (p, varName) => {\n\t\t\t\tconst content = await fs.readFile(p, 'utf8')\n\t\t\t\tconst envMap = parseEnvFile(content)\n\t\t\t\treturn envMap.get(varName) ?? null\n\t\t\t}\n\t\t)\n\n\t\tif (envFile) {\n\t\t\tconst envPath = path.join(worktreePath, envFile)\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 ${envFile}: ${port}`)\n\t\t\t\treturn port\n\t\t\t}\n\t\t}\n\n\t\t// PORT not in any dotenv-flow file, calculate based on workspace identifier\n\t\tlogger.debug('PORT not found in any dotenv-flow file, 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 issueId = extractIssueNumber(dirName) ?? extractIssueNumber(worktree.branch)\n\t\tif (issueId !== null) {\n\t\t\tconst issueNumber = parseInt(issueId, 10)\n\t\t\tif (!isNaN(issueNumber)) {\n\t\t\t\tconst port = basePort + issueNumber\n\t\t\t\tlogger.debug(`Calculated PORT for issue #${issueId}: ${port}`)\n\t\t\t\treturn port\n\t\t\t}\n\t\t\t// For alphanumeric IDs, fall through to branch-based hash\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"]}
|
|
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, findEnvFileContainingVariable } from '../utils/env.js'\nimport { calculatePortForBranch } from '../utils/port.js'\nimport { extractIssueNumber } from '../utils/git.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?: string | 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 issueNumber = extractIssueNumber(currentDir)\n\n\t\tif (issueNumber !== null) {\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 branchIssueNumber = extractIssueNumber(currentBranch)\n\t\tif (branchIssueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: branchIssueNumber,\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// For PRs, ensure the number is numeric (PRs are always numeric per GitHub)\n\t\t\tconst prNumber = typeof parsed.number === 'number' ? parsed.number : Number(parsed.number)\n\t\t\tif (isNaN(prNumber) || !isFinite(prNumber)) {\n\t\t\t\tthrow new Error(`Invalid PR number: ${parsed.number}. PR numbers must be numeric.`)\n\t\t\t}\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(prNumber, '')\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 dotenv-flow files 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 any dotenv-flow file (as override)\n\t\tconst envFile = await findEnvFileContainingVariable(\n\t\t\tworktreePath,\n\t\t\t'PORT',\n\t\t\tasync (p) => fs.pathExists(p),\n\t\t\tasync (p, varName) => {\n\t\t\t\tconst content = await fs.readFile(p, 'utf8')\n\t\t\t\tconst envMap = parseEnvFile(content)\n\t\t\t\treturn envMap.get(varName) ?? null\n\t\t\t}\n\t\t)\n\n\t\tif (envFile) {\n\t\t\tconst envPath = path.join(worktreePath, envFile)\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 ${envFile}: ${port}`)\n\t\t\t\treturn port\n\t\t\t}\n\t\t}\n\n\t\t// PORT not in any dotenv-flow file, calculate based on workspace identifier\n\t\tlogger.debug('PORT not found in any dotenv-flow file, 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 issueId = extractIssueNumber(dirName) ?? extractIssueNumber(worktree.branch)\n\t\tif (issueId !== null) {\n\t\t\tconst issueNumber = parseInt(issueId, 10)\n\t\t\tif (!isNaN(issueNumber)) {\n\t\t\t\tconst port = basePort + issueNumber\n\t\t\t\tlogger.debug(`Calculated PORT for issue #${issueId}: ${port}`)\n\t\t\t\treturn port\n\t\t\t}\n\t\t\t// For alphanumeric IDs, fall through to branch-based hash\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"]}
|
|
@@ -355,7 +355,8 @@
|
|
|
355
355
|
"type": "string",
|
|
356
356
|
"enum": [
|
|
357
357
|
"local",
|
|
358
|
-
"github-pr"
|
|
358
|
+
"github-pr",
|
|
359
|
+
"github-draft-pr"
|
|
359
360
|
],
|
|
360
361
|
"default": "local"
|
|
361
362
|
},
|
|
@@ -364,7 +365,7 @@
|
|
|
364
365
|
}
|
|
365
366
|
},
|
|
366
367
|
"additionalProperties": false,
|
|
367
|
-
"description": "Merge behavior configuration: local (merge locally) or github-pr (create PR)"
|
|
368
|
+
"description": "Merge behavior configuration: local (merge locally), github-pr (create PR), or github-draft-pr (create draft PR at start, mark ready on finish)"
|
|
368
369
|
},
|
|
369
370
|
"ide": {
|
|
370
371
|
"type": "object",
|
|
@@ -403,6 +404,16 @@
|
|
|
403
404
|
},
|
|
404
405
|
"additionalProperties": false,
|
|
405
406
|
"description": "Color synchronization settings for workspace identification"
|
|
407
|
+
},
|
|
408
|
+
"attribution": {
|
|
409
|
+
"type": "string",
|
|
410
|
+
"enum": [
|
|
411
|
+
"off",
|
|
412
|
+
"upstreamOnly",
|
|
413
|
+
"on"
|
|
414
|
+
],
|
|
415
|
+
"default": "upstreamOnly",
|
|
416
|
+
"description": "Controls when iloom attribution appears in session summaries. \"off\" - never show attribution. \"upstreamOnly\" - only show for contributions to external repositories (e.g., open source). \"on\" - always show attribution."
|
|
406
417
|
}
|
|
407
418
|
},
|
|
408
419
|
"additionalProperties": false
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
IdentifierParser
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-AS2IRKLU.js";
|
|
5
5
|
import {
|
|
6
6
|
GitWorktreeManager
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-3CMGCRB5.js";
|
|
8
8
|
import {
|
|
9
9
|
extractIssueNumber
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import "./chunk-IJ7IGJT3.js";
|
|
10
|
+
} from "./chunk-LN4H3A6A.js";
|
|
12
11
|
import {
|
|
13
12
|
SettingsManager
|
|
14
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-OOU3DKNT.js";
|
|
14
|
+
import "./chunk-YZTDGPFB.js";
|
|
15
15
|
import {
|
|
16
16
|
getDotenvFlowFiles,
|
|
17
17
|
loadWorkspaceEnv
|
|
@@ -237,4 +237,4 @@ var ShellCommand = class {
|
|
|
237
237
|
export {
|
|
238
238
|
ShellCommand
|
|
239
239
|
};
|
|
240
|
-
//# sourceMappingURL=shell-
|
|
240
|
+
//# sourceMappingURL=shell-JMU5XTHW.js.map
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
SessionSummaryService
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-HABINPX2.js";
|
|
5
5
|
import "./chunk-QHA67Q7A.js";
|
|
6
6
|
import {
|
|
7
7
|
GitWorktreeManager
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-3CMGCRB5.js";
|
|
9
|
+
import "./chunk-PSFVTBM7.js";
|
|
10
|
+
import "./chunk-DKQ4SUII.js";
|
|
9
11
|
import {
|
|
10
12
|
extractIssueNumber
|
|
11
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-LN4H3A6A.js";
|
|
14
|
+
import "./chunk-OOU3DKNT.js";
|
|
12
15
|
import {
|
|
13
16
|
MetadataManager
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import "./chunk-VWNS6DH5.js";
|
|
17
|
+
} from "./chunk-YZTDGPFB.js";
|
|
16
18
|
import "./chunk-KO2FOMHL.js";
|
|
17
19
|
import "./chunk-RUC7OULH.js";
|
|
18
20
|
import "./chunk-VAYGNQTE.js";
|
|
@@ -20,7 +22,6 @@ import "./chunk-Z5NXYJIG.js";
|
|
|
20
22
|
import {
|
|
21
23
|
getLogger
|
|
22
24
|
} from "./chunk-6UIGZD2N.js";
|
|
23
|
-
import "./chunk-74VMN2KC.js";
|
|
24
25
|
import "./chunk-UYVWLISQ.js";
|
|
25
26
|
|
|
26
27
|
// src/commands/summary.ts
|
|
@@ -47,9 +48,16 @@ var SummaryCommand = class {
|
|
|
47
48
|
parsed.loomType,
|
|
48
49
|
parsed.issueNumber
|
|
49
50
|
);
|
|
51
|
+
let displaySummary = result.summary;
|
|
52
|
+
if (input.options.withComment && parsed.loomType !== "branch") {
|
|
53
|
+
displaySummary = await this.sessionSummaryService.applyAttribution(
|
|
54
|
+
result.summary,
|
|
55
|
+
parsed.worktree.path
|
|
56
|
+
);
|
|
57
|
+
}
|
|
50
58
|
if (input.options.json) {
|
|
51
59
|
const jsonResult = {
|
|
52
|
-
summary:
|
|
60
|
+
summary: displaySummary,
|
|
53
61
|
sessionId: result.sessionId,
|
|
54
62
|
branchName: parsed.worktree.branch,
|
|
55
63
|
loomType: parsed.loomType
|
|
@@ -59,7 +67,7 @@ var SummaryCommand = class {
|
|
|
59
67
|
}
|
|
60
68
|
return jsonResult;
|
|
61
69
|
}
|
|
62
|
-
console.log(
|
|
70
|
+
console.log(displaySummary);
|
|
63
71
|
if (input.options.withComment) {
|
|
64
72
|
if (parsed.loomType === "branch") {
|
|
65
73
|
logger.debug("Skipping comment posting: branch type looms have no associated issue");
|
|
@@ -241,4 +249,4 @@ Please provide an issue number, PR number, or branch name.`
|
|
|
241
249
|
export {
|
|
242
250
|
SummaryCommand
|
|
243
251
|
};
|
|
244
|
-
//# sourceMappingURL=summary-
|
|
252
|
+
//# sourceMappingURL=summary-4SSGGH7N.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/summary.ts"],"sourcesContent":["import path from 'path'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { MetadataManager } from '../lib/MetadataManager.js'\nimport { SessionSummaryService } from '../lib/SessionSummaryService.js'\nimport { getLogger } from '../utils/logger-context.js'\nimport { extractIssueNumber } from '../utils/git.js'\nimport type { GitWorktree } from '../types/worktree.js'\nimport type { SummaryResult } from '../types/index.js'\n\n/**\n * Options for the summary command\n */\nexport interface SummaryOptions {\n\twithComment?: boolean\n\tjson?: boolean\n}\n\n/**\n * Input for the summary command\n */\nexport interface SummaryCommandInput {\n\tidentifier?: string | undefined\n\toptions: SummaryOptions\n}\n\n/**\n * Parsed input with loom information\n */\ninterface ParsedSummaryInput {\n\tworktree: GitWorktree\n\tloomType: 'issue' | 'pr' | 'branch'\n\tissueNumber?: string | number | undefined\n}\n\n/**\n * SummaryCommand - Generate and optionally post Claude session summaries\n *\n * This command allows generating the session summary without going through\n * the full `il finish` workflow. It can:\n * 1. Generate a summary and print it to stdout\n * 2. Optionally post the summary as a comment (--with-comment flag)\n */\nexport class SummaryCommand {\n\tconstructor(\n\t\tprivate gitWorktreeManager = new GitWorktreeManager(),\n\t\tprivate metadataManager = new MetadataManager(),\n\t\tprivate sessionSummaryService = new SessionSummaryService()\n\t) {}\n\n\t/**\n\t * Execute the summary command\n\t *\n\t * @param input - Command input containing identifier and options\n\t * @returns SummaryResult when in JSON mode, void otherwise\n\t */\n\tasync execute(input: SummaryCommandInput): Promise<SummaryResult | void> {\n\t\tconst logger = getLogger()\n\n\t\t// 1. Find the loom by identifier (or auto-detect from current directory)\n\t\tconst parsed = input.identifier?.trim()\n\t\t\t? await this.findLoom(input.identifier.trim())\n\t\t\t: await this.autoDetectFromCurrentDirectory()\n\n\t\t// 2. Generate the summary (service handles session ID internally, including deterministic fallback)\n\t\tconst result = await this.sessionSummaryService.generateSummary(\n\t\t\tparsed.worktree.path,\n\t\t\tparsed.worktree.branch,\n\t\t\tparsed.loomType,\n\t\t\tparsed.issueNumber\n\t\t)\n\n\t\t// 4. Apply attribution if --with-comment is used (so output matches what will be posted)\n\t\tlet displaySummary = result.summary\n\t\tif (input.options.withComment && parsed.loomType !== 'branch') {\n\t\t\tdisplaySummary = await this.sessionSummaryService.applyAttribution(\n\t\t\t\tresult.summary,\n\t\t\t\tparsed.worktree.path\n\t\t\t)\n\t\t}\n\n\t\t// 5. In JSON mode, return the structured result\n\t\tif (input.options.json) {\n\t\t\tconst jsonResult: SummaryResult = {\n\t\t\t\tsummary: displaySummary,\n\t\t\t\tsessionId: result.sessionId,\n\t\t\t\tbranchName: parsed.worktree.branch,\n\t\t\t\tloomType: parsed.loomType,\n\t\t\t}\n\t\t\t// Only include issueNumber if defined\n\t\t\tif (parsed.issueNumber !== undefined) {\n\t\t\t\tjsonResult.issueNumber = parsed.issueNumber\n\t\t\t}\n\t\t\treturn jsonResult\n\t\t}\n\n\t\t// 6. Print the summary to stdout (intentionally using console.log for piping/redirection)\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.log(displaySummary)\n\n\t\t// 7. Optionally post the summary as a comment\n\t\tif (input.options.withComment) {\n\t\t\t// Skip posting for branch type looms (no issue to comment on)\n\t\t\tif (parsed.loomType === 'branch') {\n\t\t\t\tlogger.debug('Skipping comment posting: branch type looms have no associated issue')\n\t\t\t} else if (parsed.issueNumber !== undefined) {\n\t\t\t\tawait this.sessionSummaryService.postSummary(\n\t\t\t\t\tparsed.issueNumber,\n\t\t\t\t\tresult.summary,\n\t\t\t\t\tparsed.worktree.path\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Find a loom by identifier\n\t *\n\t * Supports:\n\t * - Numeric identifiers (issue numbers): \"123\", \"#123\"\n\t * - PR identifiers: \"pr/123\"\n\t * - Branch names: \"my-feature-branch\"\n\t */\n\tprivate async findLoom(identifier: string): Promise<ParsedSummaryInput> {\n\t\t// Remove # prefix if present and trim whitespace\n\t\tconst cleanId = identifier.replace(/^#/, '').trim()\n\n\t\t// Check for PR pattern: pr/123 or PR/123\n\t\tconst prMatch = cleanId.match(/^pr\\/(\\d+)$/i)\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tconst worktree = await this.gitWorktreeManager.findWorktreeForPR(prNumber, '')\n\t\t\tif (worktree) {\n\t\t\t\tconst metadata = await this.metadataManager.readMetadata(worktree.path)\n\t\t\t\treturn {\n\t\t\t\t\tworktree,\n\t\t\t\t\tloomType: 'pr',\n\t\t\t\t\tissueNumber: metadata?.pr_numbers?.[0] ?? String(prNumber),\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new Error(`No loom found for identifier: ${identifier}`)\n\t\t}\n\n\t\t// Check if input is numeric (issue number)\n\t\tconst numericMatch = cleanId.match(/^(\\d+)$/)\n\t\tif (numericMatch?.[1]) {\n\t\t\tconst issueNumber = parseInt(numericMatch[1], 10)\n\n\t\t\t// Try issue first\n\t\t\tconst issueWorktree = await this.gitWorktreeManager.findWorktreeForIssue(issueNumber)\n\t\t\tif (issueWorktree) {\n\t\t\t\tconst metadata = await this.metadataManager.readMetadata(issueWorktree.path)\n\t\t\t\treturn {\n\t\t\t\t\tworktree: issueWorktree,\n\t\t\t\t\tloomType: metadata?.issueType ?? 'issue',\n\t\t\t\t\tissueNumber: metadata?.issue_numbers?.[0] ?? String(issueNumber),\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Then try PR\n\t\t\tconst prWorktree = await this.gitWorktreeManager.findWorktreeForPR(issueNumber, '')\n\t\t\tif (prWorktree) {\n\t\t\t\tconst metadata = await this.metadataManager.readMetadata(prWorktree.path)\n\t\t\t\treturn {\n\t\t\t\t\tworktree: prWorktree,\n\t\t\t\t\tloomType: 'pr',\n\t\t\t\t\tissueNumber: metadata?.pr_numbers?.[0] ?? String(issueNumber),\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthrow new Error(`No loom found for identifier: ${identifier}`)\n\t\t}\n\n\t\t// Check for alphanumeric issue identifier (Linear/Jira style: ABC-123)\n\t\tconst alphanumericMatch = cleanId.match(/^([A-Za-z]+-\\d+)$/)\n\t\tif (alphanumericMatch?.[1]) {\n\t\t\tconst alphanumericId = alphanumericMatch[1]\n\t\t\tconst issueWorktree = await this.gitWorktreeManager.findWorktreeForIssue(alphanumericId)\n\t\t\tif (issueWorktree) {\n\t\t\t\tconst metadata = await this.metadataManager.readMetadata(issueWorktree.path)\n\t\t\t\treturn {\n\t\t\t\t\tworktree: issueWorktree,\n\t\t\t\t\tloomType: metadata?.issueType ?? 'issue',\n\t\t\t\t\tissueNumber: metadata?.issue_numbers?.[0] ?? alphanumericId,\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new Error(`No loom found for identifier: ${identifier}`)\n\t\t}\n\n\t\t// Treat as branch name\n\t\tconst branchWorktree = await this.gitWorktreeManager.findWorktreeForBranch(cleanId)\n\t\tif (branchWorktree) {\n\t\t\tconst metadata = await this.metadataManager.readMetadata(branchWorktree.path)\n\t\t\tconst loomType = metadata?.issueType ?? 'branch'\n\n\t\t\t// For branch looms, try to get issue number from metadata\n\t\t\tlet issueNumber: string | number | undefined\n\t\t\tif (loomType === 'issue' && metadata?.issue_numbers?.[0]) {\n\t\t\t\tissueNumber = metadata.issue_numbers[0]\n\t\t\t} else if (loomType === 'pr' && metadata?.pr_numbers?.[0]) {\n\t\t\t\tissueNumber = metadata.pr_numbers[0]\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tworktree: branchWorktree,\n\t\t\t\tloomType,\n\t\t\t\tissueNumber,\n\t\t\t}\n\t\t}\n\n\t\tthrow new Error(`No loom found for identifier: ${identifier}`)\n\t}\n\n\t/**\n\t * Auto-detect loom from current working directory\n\t * Ports logic from FinishCommand.autoDetectFromCurrentDirectory()\n\t *\n\t * Detection strategy:\n\t * 1. Check current directory name for PR pattern (_pr_N suffix)\n\t * 2. Check current directory name for issue pattern (issue-N or -N-)\n\t * 3. Get current branch and check for issue pattern\n\t * 4. Fall back to using current branch as branch loom\n\t */\n\tprivate async autoDetectFromCurrentDirectory(): Promise<ParsedSummaryInput> {\n\t\tconst logger = getLogger()\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\n\t\t\tconst worktree = await this.gitWorktreeManager.findWorktreeForPR(prNumber, '')\n\t\t\tif (worktree) {\n\t\t\t\tconst metadata = await this.metadataManager.readMetadata(worktree.path)\n\t\t\t\treturn {\n\t\t\t\t\tworktree,\n\t\t\t\t\tloomType: 'pr',\n\t\t\t\t\tissueNumber: metadata?.pr_numbers?.[0] ?? String(prNumber),\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new Error(`No loom found for auto-detected PR #${prNumber}`)\n\t\t}\n\n\t\t// Check for issue pattern in directory name\n\t\tconst issueNumber = extractIssueNumber(currentDir)\n\n\t\tif (issueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${issueNumber} from directory: ${currentDir}`)\n\n\t\t\tconst worktree = await this.gitWorktreeManager.findWorktreeForIssue(issueNumber)\n\t\t\tif (worktree) {\n\t\t\t\tconst metadata = await this.metadataManager.readMetadata(worktree.path)\n\t\t\t\treturn {\n\t\t\t\t\tworktree,\n\t\t\t\t\tloomType: metadata?.issueType ?? 'issue',\n\t\t\t\t\tissueNumber: metadata?.issue_numbers?.[0] ?? String(issueNumber),\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new Error(`No loom found for auto-detected issue #${issueNumber}`)\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 loom. Please provide an issue number, PR number, or branch name.\\n' +\n\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 branchIssueNumber = extractIssueNumber(currentBranch)\n\t\tif (branchIssueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`)\n\n\t\t\tconst worktree = await this.gitWorktreeManager.findWorktreeForIssue(branchIssueNumber)\n\t\t\tif (worktree) {\n\t\t\t\tconst metadata = await this.metadataManager.readMetadata(worktree.path)\n\t\t\t\treturn {\n\t\t\t\t\tworktree,\n\t\t\t\t\tloomType: metadata?.issueType ?? 'issue',\n\t\t\t\t\tissueNumber: metadata?.issue_numbers?.[0] ?? String(branchIssueNumber),\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Last resort: use current branch as branch loom\n\t\tconst branchWorktree = await this.gitWorktreeManager.findWorktreeForBranch(currentBranch)\n\t\tif (branchWorktree) {\n\t\t\tconst metadata = await this.metadataManager.readMetadata(branchWorktree.path)\n\t\t\tconst loomType = metadata?.issueType ?? 'branch'\n\n\t\t\t// For branch looms, try to get issue number from metadata\n\t\t\tlet resolvedIssueNumber: string | number | undefined\n\t\t\tif (loomType === 'issue' && metadata?.issue_numbers?.[0]) {\n\t\t\t\tresolvedIssueNumber = metadata.issue_numbers[0]\n\t\t\t} else if (loomType === 'pr' && metadata?.pr_numbers?.[0]) {\n\t\t\t\tresolvedIssueNumber = metadata.pr_numbers[0]\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tworktree: branchWorktree,\n\t\t\t\tloomType,\n\t\t\t\tissueNumber: resolvedIssueNumber,\n\t\t\t}\n\t\t}\n\n\t\tthrow new Error(\n\t\t\t`Could not auto-detect loom from current directory or branch: ${currentBranch}\\n` +\n\t\t\t'Please provide an issue number, PR number, or branch name.'\n\t\t)\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
findMainWorktreePath
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-IJ7IGJT3.js";
|
|
4
|
+
} from "./chunk-LN4H3A6A.js";
|
|
6
5
|
import {
|
|
7
6
|
SettingsManager
|
|
8
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-OOU3DKNT.js";
|
|
8
|
+
import "./chunk-YZTDGPFB.js";
|
|
9
9
|
import "./chunk-6UIGZD2N.js";
|
|
10
10
|
import {
|
|
11
11
|
logger
|
|
@@ -51,4 +51,4 @@ var TestGitCommand = class {
|
|
|
51
51
|
export {
|
|
52
52
|
TestGitCommand
|
|
53
53
|
};
|
|
54
|
-
//# sourceMappingURL=test-git-
|
|
54
|
+
//# sourceMappingURL=test-git-6SAIRBUD.js.map
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
generateWorktreePath
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-IJ7IGJT3.js";
|
|
4
|
+
} from "./chunk-LN4H3A6A.js";
|
|
6
5
|
import {
|
|
7
6
|
SettingsManager
|
|
8
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-OOU3DKNT.js";
|
|
8
|
+
import "./chunk-YZTDGPFB.js";
|
|
9
9
|
import "./chunk-6UIGZD2N.js";
|
|
10
10
|
import {
|
|
11
11
|
logger
|
|
@@ -67,4 +67,4 @@ var TestPrefixCommand = class {
|
|
|
67
67
|
export {
|
|
68
68
|
TestPrefixCommand
|
|
69
69
|
};
|
|
70
|
-
//# sourceMappingURL=test-prefix-
|
|
70
|
+
//# sourceMappingURL=test-prefix-RLVRK5ZD.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iloom/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Control plane for maintaining alignment between you and Claude Code as you work across multiple issues using isolated environments, visible context, and multi-agent workflows to scale understanding, not just output",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/LoomLauncher.ts","../src/utils/ide.ts"],"sourcesContent":["import { existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { openTerminalWindow, openMultipleTerminalWindows } from '../utils/terminal.js'\nimport type { TerminalWindowOptions } from '../utils/terminal.js'\nimport { openIdeWindow } from '../utils/ide.js'\nimport { getDevServerLaunchCommand } from '../utils/dev-server.js'\nimport { generateColorFromBranchName, hexToRgb } from '../utils/color.js'\nimport { getLogger } from '../utils/logger-context.js'\nimport { ClaudeContextManager } from './ClaudeContextManager.js'\nimport type { SettingsManager } from './SettingsManager.js'\nimport type { Capability } from '../types/loom.js'\nimport { getDotenvFlowFiles } from '../utils/env.js'\nimport { getExecutablePath } from '../utils/cli-overrides.js'\n\nexport interface LaunchLoomOptions {\n\tenableClaude: boolean\n\tenableCode: boolean\n\tenableDevServer: boolean\n\tenableTerminal: boolean\n\tworktreePath: string\n\tbranchName: string\n\tport?: number\n\tcapabilities: Capability[]\n\tworkflowType: 'issue' | 'pr' | 'regular'\n\tidentifier: string | number\n\ttitle?: string\n\toneShot?: import('../types/index.js').OneShotMode\n\tsetArguments?: string[] // Raw --set arguments to forward\n\texecutablePath?: string // Executable path to use for spin command\n\tsourceEnvOnStart?: boolean // defaults to false if undefined\n\tcolorTerminal?: boolean // defaults to true if undefined\n\tcolorHex?: string // Pre-calculated hex color from metadata, avoids recalculation\n}\n\n/**\n * LoomLauncher orchestrates opening loom components\n */\nexport class LoomLauncher {\n\tprivate claudeContext: ClaudeContextManager\n\tprivate settings?: SettingsManager\n\n\tconstructor(claudeContext?: ClaudeContextManager, settings?: SettingsManager) {\n\t\tthis.claudeContext = claudeContext ?? new ClaudeContextManager()\n\t\tif (settings !== undefined) {\n\t\t\tthis.settings = settings\n\t\t}\n\t}\n\n\t/**\n\t * Launch loom components based on individual flags\n\t */\n\tasync launchLoom(options: LaunchLoomOptions): Promise<void> {\n\t\tconst { enableClaude, enableCode, enableDevServer, enableTerminal } = options\n\n\t\tgetLogger().debug(`Launching loom components: Claude=${enableClaude}, Code=${enableCode}, DevServer=${enableDevServer}, Terminal=${enableTerminal}`)\n\n\t\tconst launchPromises: Promise<void>[] = []\n\n\t\t// Launch VSCode if enabled\n\t\tif (enableCode) {\n\t\t\tgetLogger().debug('Launching VSCode')\n\t\t\tlaunchPromises.push(this.launchVSCode(options))\n\t\t}\n\n\t\t// Build array of terminals to launch\n\t\tconst terminalsToLaunch: Array<{\n\t\t\ttype: 'claude' | 'devServer' | 'terminal'\n\t\t\toptions: TerminalWindowOptions\n\t\t}> = []\n\n\t\tif (enableDevServer) {\n\t\t\tterminalsToLaunch.push({\n\t\t\t\ttype: 'devServer',\n\t\t\t\toptions: await this.buildDevServerTerminalOptions(options),\n\t\t\t})\n\t\t}\n\n\t\tif (enableTerminal) {\n\t\t\tterminalsToLaunch.push({\n\t\t\t\ttype: 'terminal',\n\t\t\t\toptions: this.buildStandaloneTerminalOptions(options),\n\t\t\t})\n\t\t}\n\n\t\tif (enableClaude) {\n\t\t\tterminalsToLaunch.push({\n\t\t\t\ttype: 'claude',\n\t\t\t\toptions: await this.buildClaudeTerminalOptions(options),\n\t\t\t})\n\t\t}\n\n\t\t// Launch terminals based on count\n\t\tif (terminalsToLaunch.length > 1) {\n\t\t\t// Multiple terminals - launch as tabs in single window\n\t\t\tgetLogger().debug(`Launching ${terminalsToLaunch.length} terminals in single window`)\n\t\t\tlaunchPromises.push(this.launchMultipleTerminals(terminalsToLaunch, options))\n\t\t} else if (terminalsToLaunch.length === 1) {\n\t\t\t// Single terminal - launch standalone\n\t\t\tconst terminal = terminalsToLaunch[0]\n\t\t\tif (!terminal) {\n\t\t\t\tthrow new Error('Terminal configuration is undefined')\n\t\t\t}\n\t\t\tconst terminalType = terminal.type\n\t\t\tgetLogger().debug(`Launching single ${terminalType} terminal`)\n\n\t\t\tif (terminalType === 'claude') {\n\t\t\t\tlaunchPromises.push(this.launchClaudeTerminal(options))\n\t\t\t} else if (terminalType === 'devServer') {\n\t\t\t\tlaunchPromises.push(this.launchDevServerTerminal(options))\n\t\t\t} else {\n\t\t\t\tlaunchPromises.push(this.launchStandaloneTerminal(options))\n\t\t\t}\n\t\t}\n\n\t\t// Wait for all components to launch\n\t\tawait Promise.all(launchPromises)\n\n\t\tgetLogger().success('loom launched successfully')\n\t}\n\n\t/**\n\t * Launch IDE (VSCode or configured alternative)\n\t */\n\tprivate async launchVSCode(options: LaunchLoomOptions): Promise<void> {\n\t\tconst ideConfig = await this.settings?.loadSettings().then((s) => s.ide)\n\t\tawait openIdeWindow(options.worktreePath, ideConfig)\n\t\tgetLogger().info('IDE opened')\n\t}\n\n\t/**\n\t * Launch Claude terminal\n\t */\n\tprivate async launchClaudeTerminal(options: LaunchLoomOptions): Promise<void> {\n\t\tawait this.claudeContext.launchWithContext({\n\t\t\tworkspacePath: options.worktreePath,\n\t\t\ttype: options.workflowType,\n\t\t\tidentifier: options.identifier,\n\t\t\tbranchName: options.branchName,\n\t\t\t...(options.title && { title: options.title }),\n\t\t\t...(options.port !== undefined && { port: options.port }),\n\t\t\toneShot: options.oneShot ?? 'default',\n\t\t\t...(options.setArguments && { setArguments: options.setArguments }),\n\t\t\t...(options.executablePath && { executablePath: options.executablePath }),\n\t\t})\n\t\tgetLogger().info('Claude terminal opened')\n\t}\n\n\t/**\n\t * Launch dev server terminal\n\t */\n\tprivate async launchDevServerTerminal(options: LaunchLoomOptions): Promise<void> {\n\t\tconst devServerCommand = await getDevServerLaunchCommand(\n\t\t\toptions.worktreePath,\n\t\t\toptions.port,\n\t\t\toptions.capabilities\n\t\t)\n\n\t\t// Only generate color if terminal coloring is enabled (default: true)\n\t\tconst backgroundColor = (options.colorTerminal ?? true)\n\t\t\t? options.colorHex\n\t\t\t\t? hexToRgb(options.colorHex)\n\t\t\t\t: generateColorFromBranchName(options.branchName).rgb\n\t\t\t: undefined\n\n\t\tawait openTerminalWindow({\n\t\t\tworkspacePath: options.worktreePath,\n\t\t\tcommand: devServerCommand,\n\t\t\t...(backgroundColor && { backgroundColor }),\n\t\t\tincludeEnvSetup: (options.sourceEnvOnStart ?? false) && this.hasAnyEnvFiles(options.worktreePath),\n\t\t\tincludePortExport: options.capabilities.includes('web'),\n\t\t\t...(options.port !== undefined && { port: options.port }),\n\t\t})\n\t\tgetLogger().info('Dev server terminal opened')\n\t}\n\n\t/**\n\t * Launch standalone terminal running `il shell <identifier>`\n\t */\n\tprivate async launchStandaloneTerminal(options: LaunchLoomOptions): Promise<void> {\n\t\t// Build shell command with identifier\n\t\tconst executable = options.executablePath ?? getExecutablePath()\n\t\tconst shellIdentifier = String(options.identifier)\n\t\tconst shellCommand = `${executable} shell ${shellIdentifier}`\n\n\t\t// Only generate color if terminal coloring is enabled (default: true)\n\t\tconst backgroundColor = (options.colorTerminal ?? true)\n\t\t\t? options.colorHex\n\t\t\t\t? hexToRgb(options.colorHex)\n\t\t\t\t: generateColorFromBranchName(options.branchName).rgb\n\t\t\t: undefined\n\n\t\tawait openTerminalWindow({\n\t\t\tworkspacePath: options.worktreePath,\n\t\t\tcommand: shellCommand,\n\t\t\t...(backgroundColor && { backgroundColor }),\n\t\t\t// il shell handles env loading internally, so we don't need includeEnvSetup\n\t\t\tincludeEnvSetup: false,\n\t\t\tincludePortExport: options.capabilities.includes('web'),\n\t\t\t...(options.port !== undefined && { port: options.port }),\n\t\t})\n\t\tgetLogger().info('Standalone terminal opened')\n\t}\n\n\t/**\n\t * Build terminal options for Claude\n\t */\n\tprivate async buildClaudeTerminalOptions(\n\t\toptions: LaunchLoomOptions\n\t): Promise<TerminalWindowOptions> {\n\t\tconst hasEnvFile = this.hasAnyEnvFiles(options.worktreePath)\n\t\tconst claudeTitle = `Claude - ${this.formatIdentifier(options.workflowType, options.identifier)}`\n\n\t\tconst executable = options.executablePath ?? 'iloom'\n\t\tlet claudeCommand = `${executable} spin`\n\t\tif (options.oneShot !== undefined && options.oneShot !== 'default') {\n\t\t\tclaudeCommand += ` --one-shot=${options.oneShot}`\n\t\t}\n\t\tif (options.setArguments && options.setArguments.length > 0) {\n\t\t\tfor (const setArg of options.setArguments) {\n\t\t\t\tclaudeCommand += ` --set ${setArg}`\n\t\t\t}\n\t\t}\n\n\t\t// Only generate color if terminal coloring is enabled (default: true)\n\t\tconst backgroundColor = (options.colorTerminal ?? true)\n\t\t\t? options.colorHex\n\t\t\t\t? hexToRgb(options.colorHex)\n\t\t\t\t: generateColorFromBranchName(options.branchName).rgb\n\t\t\t: undefined\n\n\t\treturn {\n\t\t\tworkspacePath: options.worktreePath,\n\t\t\tcommand: claudeCommand,\n\t\t\t...(backgroundColor && { backgroundColor }),\n\t\t\ttitle: claudeTitle,\n\t\t\tincludeEnvSetup: (options.sourceEnvOnStart ?? false) && hasEnvFile,\n\t\t\t...(options.port !== undefined && { port: options.port, includePortExport: true }),\n\t\t}\n\t}\n\n\t/**\n\t * Build terminal options for dev server\n\t */\n\tprivate async buildDevServerTerminalOptions(\n\t\toptions: LaunchLoomOptions\n\t): Promise<TerminalWindowOptions> {\n\t\tconst devServerCommand = await getDevServerLaunchCommand(\n\t\t\toptions.worktreePath,\n\t\t\toptions.port,\n\t\t\toptions.capabilities\n\t\t)\n\t\tconst hasEnvFile = this.hasAnyEnvFiles(options.worktreePath)\n\t\tconst devServerTitle = `Dev Server - ${this.formatIdentifier(options.workflowType, options.identifier)}`\n\n\t\t// Only generate color if terminal coloring is enabled (default: true)\n\t\tconst backgroundColor = (options.colorTerminal ?? true)\n\t\t\t? options.colorHex\n\t\t\t\t? hexToRgb(options.colorHex)\n\t\t\t\t: generateColorFromBranchName(options.branchName).rgb\n\t\t\t: undefined\n\n\t\treturn {\n\t\t\tworkspacePath: options.worktreePath,\n\t\t\tcommand: devServerCommand,\n\t\t\t...(backgroundColor && { backgroundColor }),\n\t\t\ttitle: devServerTitle,\n\t\t\tincludeEnvSetup: (options.sourceEnvOnStart ?? false) && hasEnvFile,\n\t\t\tincludePortExport: options.capabilities.includes('web'),\n\t\t\t...(options.port !== undefined && { port: options.port }),\n\t\t}\n\t}\n\n\t/**\n\t * Build terminal options for standalone terminal\n\t * Runs `il shell <identifier>` which handles env loading internally\n\t */\n\tprivate buildStandaloneTerminalOptions(\n\t\toptions: LaunchLoomOptions\n\t): TerminalWindowOptions {\n\t\tconst terminalTitle = `Terminal - ${this.formatIdentifier(options.workflowType, options.identifier)}`\n\n\t\t// Build shell command with identifier\n\t\t// Use the same executable path pattern as buildClaudeTerminalOptions\n\t\tconst executable = options.executablePath ?? getExecutablePath()\n\t\tconst shellIdentifier = String(options.identifier)\n\t\tconst shellCommand = `${executable} shell ${shellIdentifier}`\n\n\t\t// Only generate color if terminal coloring is enabled (default: true)\n\t\tconst backgroundColor = (options.colorTerminal ?? true)\n\t\t\t? options.colorHex\n\t\t\t\t? hexToRgb(options.colorHex)\n\t\t\t\t: generateColorFromBranchName(options.branchName).rgb\n\t\t\t: undefined\n\n\t\treturn {\n\t\t\tworkspacePath: options.worktreePath,\n\t\t\tcommand: shellCommand,\n\t\t\t...(backgroundColor && { backgroundColor }),\n\t\t\ttitle: terminalTitle,\n\t\t\t// il shell handles env loading internally, so we don't need includeEnvSetup\n\t\t\tincludeEnvSetup: false,\n\t\t\tincludePortExport: options.capabilities.includes('web'),\n\t\t\t...(options.port !== undefined && { port: options.port }),\n\t\t}\n\t}\n\n\t/**\n\t * Launch multiple terminals (2+) as tabs in single window\n\t */\n\tprivate async launchMultipleTerminals(\n\t\tterminals: Array<{ type: string; options: TerminalWindowOptions }>,\n\t\t_options: LaunchLoomOptions\n\t): Promise<void> {\n\t\tconst terminalOptions = terminals.map((t) => t.options)\n\n\t\tawait openMultipleTerminalWindows(terminalOptions)\n\n\t\tconst terminalTypes = terminals.map((t) => t.type).join(' + ')\n\t\tgetLogger().info(`Multiple terminals opened: ${terminalTypes}`)\n\t}\n\n\t/**\n\t * Check if any dotenv-flow files exist in the workspace\n\t * Checks all files: .env, .env.local, .env.{NODE_ENV}, .env.{NODE_ENV}.local\n\t */\n\tprivate hasAnyEnvFiles(workspacePath: string): boolean {\n\t\tconst envFiles = getDotenvFlowFiles()\n\t\treturn envFiles.some(file => existsSync(join(workspacePath, file)))\n\t}\n\n\t/**\n\t * Format identifier for terminal tab titles\n\t */\n\tprivate formatIdentifier(workflowType: 'issue' | 'pr' | 'regular', identifier: string | number): string {\n\t\tif (workflowType === 'issue') {\n\t\t\treturn `Issue #${identifier}`\n\t\t} else if (workflowType === 'pr') {\n\t\t\treturn `PR #${identifier}`\n\t\t} else {\n\t\t\treturn `Branch: ${identifier}`\n\t\t}\n\t}\n}\n","import { execa } from 'execa'\nimport { logger } from './logger.js'\nimport type { IdeSettings } from '../lib/SettingsManager.js'\n\n// IDE preset configuration\nconst IDE_PRESETS = {\n\tvscode: { command: 'code', name: 'Visual Studio Code', args: [] },\n\tcursor: { command: 'cursor', name: 'Cursor', args: [] },\n\twebstorm: { command: 'webstorm', name: 'WebStorm', args: ['--nosplash'] },\n\tsublime: { command: 'subl', name: 'Sublime Text', args: [] },\n\tintellij: { command: 'idea', name: 'IntelliJ IDEA', args: ['--nosplash'] },\n\twindsurf: { command: 'surf', name: 'Windsurf', args: [] },\n\tantigravity: { command: 'agy', name: 'Antigravity', args: [] },\n} as const\n\ntype IdePreset = keyof typeof IDE_PRESETS\n\n// Resolve IDE configuration to command and args\nexport function getIdeConfig(ideSettings?: IdeSettings): {\n\tcommand: string\n\targs: string[]\n\tname: string\n} {\n\t// Default to vscode if not configured\n\tconst type = ideSettings?.type ?? 'vscode'\n\n\tconst preset = IDE_PRESETS[type as IdePreset]\n\treturn {\n\t\tcommand: preset.command,\n\t\targs: [...preset.args],\n\t\tname: preset.name,\n\t}\n}\n\n// Check if IDE is available\nexport async function isIdeAvailable(command: string): Promise<boolean> {\n\ttry {\n\t\tawait execa('command', ['-v', command], { shell: true, timeout: 5000 })\n\t\treturn true\n\t} catch {\n\t\treturn false\n\t}\n}\n\n// Get installation hint for IDE\nfunction getInstallHint(type: string): string {\n\tconst hints: Record<string, string> = {\n\t\tvscode:\n\t\t\t'Install command-line tools: Open VSCode > Command Palette > \"Shell Command: Install \\'code\\' command in PATH\"',\n\t\tcursor:\n\t\t\t'Install command-line tools: Open Cursor > Command Palette > \"Install \\'cursor\\' command in PATH\"',\n\t\twebstorm: 'Install via JetBrains Toolbox > Settings > Shell Scripts > Enable',\n\t\tsublime:\n\t\t\t'Create symlink: ln -s \"/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl\" /usr/local/bin/subl',\n\t\tintellij: 'Install via JetBrains Toolbox > Settings > Shell Scripts > Enable',\n\t\twindsurf:\n\t\t\t'Install command-line tools during Windsurf installation or create symlink manually',\n\t\tantigravity:\n\t\t\t'Install command-line tools during Antigravity installation or create symlink manually',\n\t}\n\treturn hints[type] ?? `Ensure the IDE command is available in your PATH`\n}\n\n// Open IDE window for workspace\nexport async function openIdeWindow(\n\tworkspacePath: string,\n\tideSettings?: IdeSettings\n): Promise<void> {\n\tconst config = getIdeConfig(ideSettings)\n\tconst available = await isIdeAvailable(config.command)\n\n\tif (!available) {\n\t\tconst type = ideSettings?.type ?? 'vscode'\n\t\tthrow new Error(\n\t\t\t`${config.name} is not available. The \"${config.command}\" command was not found in PATH.\\n` +\n\t\t\t\tgetInstallHint(type)\n\t\t)\n\t}\n\n\ttry {\n\t\tawait execa(config.command, [...config.args, workspacePath])\n\t\tlogger.debug(`Opened ${config.name} for workspace: ${workspacePath}`)\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Failed to open ${config.name}: ${error instanceof Error ? error.message : 'Unknown error'}`\n\t\t)\n\t}\n}\n"],"mappings":"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","names":[]}
|