@iloom/cli 0.8.1 → 0.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +3 -0
  2. package/dist/{PRManager-H4TUZTZL.js → PRManager-A63LT3NF.js} +3 -3
  3. package/dist/README.md +3 -0
  4. package/dist/{build-H4DK3DMQ.js → build-Z3WCIKPD.js} +3 -3
  5. package/dist/{chunk-A4UQY3M2.js → chunk-3FC3VNEX.js} +2 -2
  6. package/dist/{chunk-EQOFNPEY.js → chunk-44Y5IF7P.js} +4 -4
  7. package/dist/{chunk-ECP77QGE.js → chunk-A7XHHUEV.js} +4 -4
  8. package/dist/{chunk-RODL2HVY.js → chunk-FPNSFP6K.js} +6 -6
  9. package/dist/{chunk-NR64HNF7.js → chunk-GWONJE3X.js} +6 -6
  10. package/dist/{chunk-BCQDYAOJ.js → chunk-O6LECMT6.js} +4 -4
  11. package/dist/{cleanup-OGE7V7AD.js → cleanup-NWNKWPUY.js} +3 -3
  12. package/dist/cli.js +55 -37
  13. package/dist/cli.js.map +1 -1
  14. package/dist/{compile-ZOAODFN2.js → compile-UANHMNTS.js} +3 -3
  15. package/dist/{dev-server-TYYJM3XA.js → dev-server-TO7RLYJI.js} +5 -5
  16. package/dist/{feedback-HZVLOTQJ.js → feedback-7ZZI6RC5.js} +2 -2
  17. package/dist/{ignite-ZO7SGUKP.js → ignite-JBX3BUDE.js} +25 -4
  18. package/dist/ignite-JBX3BUDE.js.map +1 -0
  19. package/dist/{init-MZBIXQ7V.js → init-XXDIB2UJ.js} +4 -4
  20. package/dist/{lint-MDVUV3W2.js → lint-XPODLDVA.js} +3 -3
  21. package/dist/{open-2LPZ7XXW.js → open-M2SUR74Y.js} +5 -5
  22. package/dist/{plan-PIME6UNY.js → plan-FB4AOJ2Q.js} +6 -6
  23. package/dist/prompts/issue-prompt.txt +5 -5
  24. package/dist/prompts/regular-prompt.txt +5 -5
  25. package/dist/{rebase-7YS3N274.js → rebase-4FNRBW3H.js} +3 -3
  26. package/dist/{run-XPGCMFFO.js → run-GZNHRJB2.js} +5 -5
  27. package/dist/{summary-C5VVSJAJ.js → summary-Z4F7YFXE.js} +3 -3
  28. package/dist/{test-N2725YRI.js → test-LBSPYIJW.js} +3 -3
  29. package/dist/vscode-LH3VSQ2W.js +164 -0
  30. package/dist/vscode-LH3VSQ2W.js.map +1 -0
  31. package/dist/vscode-announcement-EQ2SKK3T.js +46 -0
  32. package/dist/vscode-announcement-EQ2SKK3T.js.map +1 -0
  33. package/package.json +2 -1
  34. package/dist/ignite-ZO7SGUKP.js.map +0 -1
  35. /package/dist/{PRManager-H4TUZTZL.js.map → PRManager-A63LT3NF.js.map} +0 -0
  36. /package/dist/{build-H4DK3DMQ.js.map → build-Z3WCIKPD.js.map} +0 -0
  37. /package/dist/{chunk-A4UQY3M2.js.map → chunk-3FC3VNEX.js.map} +0 -0
  38. /package/dist/{chunk-EQOFNPEY.js.map → chunk-44Y5IF7P.js.map} +0 -0
  39. /package/dist/{chunk-ECP77QGE.js.map → chunk-A7XHHUEV.js.map} +0 -0
  40. /package/dist/{chunk-RODL2HVY.js.map → chunk-FPNSFP6K.js.map} +0 -0
  41. /package/dist/{chunk-NR64HNF7.js.map → chunk-GWONJE3X.js.map} +0 -0
  42. /package/dist/{chunk-BCQDYAOJ.js.map → chunk-O6LECMT6.js.map} +0 -0
  43. /package/dist/{cleanup-OGE7V7AD.js.map → cleanup-NWNKWPUY.js.map} +0 -0
  44. /package/dist/{compile-ZOAODFN2.js.map → compile-UANHMNTS.js.map} +0 -0
  45. /package/dist/{dev-server-TYYJM3XA.js.map → dev-server-TO7RLYJI.js.map} +0 -0
  46. /package/dist/{feedback-HZVLOTQJ.js.map → feedback-7ZZI6RC5.js.map} +0 -0
  47. /package/dist/{init-MZBIXQ7V.js.map → init-XXDIB2UJ.js.map} +0 -0
  48. /package/dist/{lint-MDVUV3W2.js.map → lint-XPODLDVA.js.map} +0 -0
  49. /package/dist/{open-2LPZ7XXW.js.map → open-M2SUR74Y.js.map} +0 -0
  50. /package/dist/{plan-PIME6UNY.js.map → plan-FB4AOJ2Q.js.map} +0 -0
  51. /package/dist/{rebase-7YS3N274.js.map → rebase-4FNRBW3H.js.map} +0 -0
  52. /package/dist/{run-XPGCMFFO.js.map → run-GZNHRJB2.js.map} +0 -0
  53. /package/dist/{summary-C5VVSJAJ.js.map → summary-Z4F7YFXE.js.map} +0 -0
  54. /package/dist/{test-N2725YRI.js.map → test-LBSPYIJW.js.map} +0 -0
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ IdentifierParser
4
+ } from "./chunk-TVH67KEO.js";
5
+ import {
6
+ GitWorktreeManager
7
+ } from "./chunk-HSGZW3ID.js";
8
+ import {
9
+ getInstallHint,
10
+ isIdeAvailable
11
+ } from "./chunk-O7VL5N6S.js";
12
+ import {
13
+ extractIssueNumber
14
+ } from "./chunk-GDS2HXSW.js";
15
+ import "./chunk-IWIIOFEB.js";
16
+ import "./chunk-KBEIQP4G.js";
17
+ import {
18
+ waitForKeypress
19
+ } from "./chunk-7JDMYTFZ.js";
20
+ import "./chunk-6MLEBAYZ.js";
21
+ import {
22
+ logger
23
+ } from "./chunk-VT4PDUYT.js";
24
+
25
+ // src/commands/vscode.ts
26
+ import path from "path";
27
+ import { execa } from "execa";
28
+ var EXTENSION_ID = "iloom-ai.iloom-vscode";
29
+ var VSCODE_COMMAND = "code";
30
+ var VSCodeCommand = class {
31
+ constructor(gitWorktreeManager, identifierParser) {
32
+ this.gitWorktreeManager = gitWorktreeManager ?? new GitWorktreeManager();
33
+ this.identifierParser = identifierParser ?? new IdentifierParser(this.gitWorktreeManager);
34
+ }
35
+ async execute(input) {
36
+ const available = await isIdeAvailable(VSCODE_COMMAND);
37
+ if (!available) {
38
+ throw new Error(
39
+ `VS Code CLI is not available. The "${VSCODE_COMMAND}" command was not found in PATH.
40
+ ` + getInstallHint("vscode")
41
+ );
42
+ }
43
+ const parsed = input.identifier ? await this.parseExplicitInput(input.identifier) : await this.autoDetectFromCurrentDirectory();
44
+ logger.debug(`Parsed input: ${JSON.stringify(parsed)}`);
45
+ const worktree = await this.findWorktreeForIdentifier(parsed);
46
+ logger.info(`Found worktree at: ${worktree.path}`);
47
+ const installedExtensions = await execa(VSCODE_COMMAND, ["--list-extensions"]);
48
+ const extensionList = installedExtensions.stdout.split("\n").map((ext) => ext.trim().toLowerCase());
49
+ const isExtensionInstalled = extensionList.includes(EXTENSION_ID.toLowerCase());
50
+ const lightbulb = "\u{1F4A1}";
51
+ if (isExtensionInstalled) {
52
+ logger.info("iloom VS Code extension is already installed");
53
+ } else {
54
+ logger.info("Installing iloom VS Code extension...");
55
+ await execa(VSCODE_COMMAND, ["--install-extension", EXTENSION_ID]);
56
+ logger.success("Extension installed!");
57
+ }
58
+ if (input.wait !== false) {
59
+ await waitForKeypress(`Press any key to open VS Code (look for the ${lightbulb} icon in the activity bar)...`);
60
+ }
61
+ await execa(VSCODE_COMMAND, [worktree.path]);
62
+ }
63
+ /**
64
+ * Parse explicit identifier input
65
+ */
66
+ async parseExplicitInput(identifier) {
67
+ const parsed = await this.identifierParser.parseForPatternDetection(identifier);
68
+ if (parsed.type === "description") {
69
+ throw new Error("Description input type is not supported in vscode command");
70
+ }
71
+ return { ...parsed, autoDetected: false };
72
+ }
73
+ /**
74
+ * Auto-detect identifier from current directory
75
+ */
76
+ async autoDetectFromCurrentDirectory() {
77
+ const currentDir = path.basename(process.cwd());
78
+ const prPattern = /_pr_(\d+)$/;
79
+ const prMatch = currentDir.match(prPattern);
80
+ if (prMatch == null ? void 0 : prMatch[1]) {
81
+ const prNumber = parseInt(prMatch[1], 10);
82
+ logger.debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`);
83
+ return {
84
+ type: "pr",
85
+ number: prNumber,
86
+ originalInput: currentDir,
87
+ autoDetected: true
88
+ };
89
+ }
90
+ const issueNumber = extractIssueNumber(currentDir);
91
+ if (issueNumber !== null) {
92
+ logger.debug(`Auto-detected issue #${issueNumber} from directory: ${currentDir}`);
93
+ return {
94
+ type: "issue",
95
+ number: issueNumber,
96
+ originalInput: currentDir,
97
+ autoDetected: true
98
+ };
99
+ }
100
+ const repoInfo = await this.gitWorktreeManager.getRepoInfo();
101
+ const currentBranch = repoInfo.currentBranch;
102
+ if (!currentBranch) {
103
+ throw new Error(
104
+ "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"
105
+ );
106
+ }
107
+ const branchIssueNumber = extractIssueNumber(currentBranch);
108
+ if (branchIssueNumber !== null) {
109
+ logger.debug(`Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`);
110
+ return {
111
+ type: "issue",
112
+ number: branchIssueNumber,
113
+ originalInput: currentBranch,
114
+ autoDetected: true
115
+ };
116
+ }
117
+ return {
118
+ type: "branch",
119
+ branchName: currentBranch,
120
+ originalInput: currentBranch,
121
+ autoDetected: true
122
+ };
123
+ }
124
+ /**
125
+ * Find worktree for the given identifier
126
+ */
127
+ async findWorktreeForIdentifier(parsed) {
128
+ let worktree = null;
129
+ if (parsed.type === "issue" && parsed.number !== void 0) {
130
+ worktree = await this.gitWorktreeManager.findWorktreeForIssue(parsed.number);
131
+ } else if (parsed.type === "pr" && parsed.number !== void 0) {
132
+ const prNumber = typeof parsed.number === "number" ? parsed.number : Number(parsed.number);
133
+ if (isNaN(prNumber) || !isFinite(prNumber)) {
134
+ throw new Error(`Invalid PR number: ${parsed.number}. PR numbers must be numeric.`);
135
+ }
136
+ worktree = await this.gitWorktreeManager.findWorktreeForPR(prNumber, "");
137
+ } else if (parsed.type === "branch" && parsed.branchName) {
138
+ worktree = await this.gitWorktreeManager.findWorktreeForBranch(parsed.branchName);
139
+ }
140
+ if (!worktree) {
141
+ throw new Error(
142
+ `No worktree found for ${this.formatParsedInput(parsed)}. Run 'il start ${parsed.originalInput}' to create one.`
143
+ );
144
+ }
145
+ return worktree;
146
+ }
147
+ /**
148
+ * Format parsed input for display
149
+ */
150
+ formatParsedInput(parsed) {
151
+ const autoLabel = parsed.autoDetected ? " (auto-detected)" : "";
152
+ if (parsed.type === "issue") {
153
+ return `issue #${parsed.number}${autoLabel}`;
154
+ }
155
+ if (parsed.type === "pr") {
156
+ return `PR #${parsed.number}${autoLabel}`;
157
+ }
158
+ return `branch "${parsed.branchName}"${autoLabel}`;
159
+ }
160
+ };
161
+ export {
162
+ VSCodeCommand
163
+ };
164
+ //# sourceMappingURL=vscode-LH3VSQ2W.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/vscode.ts"],"sourcesContent":["import path from 'path'\nimport { execa } from 'execa'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { IdentifierParser } from '../utils/IdentifierParser.js'\nimport { isIdeAvailable, getInstallHint } from '../utils/ide.js'\nimport { extractIssueNumber } from '../utils/git.js'\nimport { logger } from '../utils/logger.js'\nimport { waitForKeypress } from '../utils/prompt.js'\nimport type { GitWorktree } from '../types/worktree.js'\nimport type { ParsedInput } from './start.js'\n\nexport interface VSCodeCommandInput {\n\tidentifier?: string | undefined\n\twait?: boolean | undefined\n}\n\ninterface ParsedVSCodeInput extends ParsedInput {\n\tautoDetected: boolean\n}\n\nconst EXTENSION_ID = 'iloom-ai.iloom-vscode'\nconst VSCODE_COMMAND = 'code'\n\nexport class VSCodeCommand {\n\tprivate gitWorktreeManager: GitWorktreeManager\n\tprivate identifierParser: IdentifierParser\n\n\tconstructor(gitWorktreeManager?: GitWorktreeManager, identifierParser?: IdentifierParser) {\n\t\tthis.gitWorktreeManager = gitWorktreeManager ?? new GitWorktreeManager()\n\t\tthis.identifierParser = identifierParser ?? new IdentifierParser(this.gitWorktreeManager)\n\t}\n\n\tasync execute(input: VSCodeCommandInput): Promise<void> {\n\t\t// 1. Check VS Code CLI available\n\t\tconst available = await isIdeAvailable(VSCODE_COMMAND)\n\t\tif (!available) {\n\t\t\tthrow new Error(\n\t\t\t\t`VS Code CLI is not available. The \"${VSCODE_COMMAND}\" command was not found in PATH.\\n` +\n\t\t\t\t\tgetInstallHint('vscode')\n\t\t\t)\n\t\t}\n\n\t\t// 2. 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// 3. 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// 4. Check if extension is already installed\n\t\tconst installedExtensions = await execa(VSCODE_COMMAND, ['--list-extensions'])\n\t\tconst extensionList = installedExtensions.stdout.split('\\n').map(ext => ext.trim().toLowerCase())\n\t\tconst isExtensionInstalled = extensionList.includes(EXTENSION_ID.toLowerCase())\n\n\t\t// 5. Install extension if not already installed\n\t\tconst lightbulb = '\\u{1F4A1}'\n\t\tif (isExtensionInstalled) {\n\t\t\tlogger.info('iloom VS Code extension is already installed')\n\t\t} else {\n\t\t\tlogger.info('Installing iloom VS Code extension...')\n\t\t\tawait execa(VSCODE_COMMAND, ['--install-extension', EXTENSION_ID])\n\t\t\tlogger.success('Extension installed!')\n\t\t}\n\n\t\t// 6. Wait for keypress then open VS Code (skip if wait is false)\n\t\tif (input.wait !== false) {\n\t\t\tawait waitForKeypress(`Press any key to open VS Code (look for the ${lightbulb} icon in the activity bar)...`)\n\t\t}\n\t\tawait execa(VSCODE_COMMAND, [worktree.path])\n\t}\n\n\t/**\n\t * Parse explicit identifier input\n\t */\n\tprivate async parseExplicitInput(identifier: string): Promise<ParsedVSCodeInput> {\n\t\tconst parsed = await this.identifierParser.parseForPatternDetection(identifier)\n\n\t\t// Description type should never reach vscode command\n\t\tif (parsed.type === 'description') {\n\t\t\tthrow new Error('Description input type is not supported in vscode command')\n\t\t}\n\n\t\treturn { ...parsed, autoDetected: false }\n\t}\n\n\t/**\n\t * Auto-detect identifier from current directory\n\t */\n\tprivate async autoDetectFromCurrentDirectory(): Promise<ParsedVSCodeInput> {\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: ParsedVSCodeInput): 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\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\tworktree = await this.gitWorktreeManager.findWorktreeForPR(prNumber, '')\n\t\t} else if (parsed.type === 'branch' && parsed.branchName) {\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForBranch(parsed.branchName)\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: ParsedVSCodeInput): 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"],"mappings":"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","names":[]}
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ FirstRunManager
4
+ } from "./chunk-Q7POFB5Q.js";
5
+ import "./chunk-VT4PDUYT.js";
6
+
7
+ // src/utils/vscode-announcement.ts
8
+ import chalk from "chalk";
9
+ import stringWidth from "string-width";
10
+ var FEATURE_NAME = "vscode-announcement";
11
+ async function showVSCodeAnnouncementIfNeeded(commandName) {
12
+ if (commandName === "vscode") return;
13
+ if (!process.stdout.isTTY) return;
14
+ const manager = new FirstRunManager(FEATURE_NAME);
15
+ const isFirstTime = await manager.isFirstRun();
16
+ if (!isFirstTime) return;
17
+ displayAnnouncementBox();
18
+ await manager.markAsRun();
19
+ }
20
+ function displayAnnouncementBox() {
21
+ const rocket = "\u{1F680}";
22
+ const pointRight = "\u{1F449}";
23
+ const lines = [
24
+ ` ${rocket} New: iloom for VS Code is here!`,
25
+ "",
26
+ " \u2022 Real-time Recap Panel: See AI decisions, insights, assumptions, and risks as they happen",
27
+ " \u2022 Loom Explorer: Manage projects/tasks visually",
28
+ "",
29
+ ` ${pointRight} Run 'il vscode' to install`
30
+ ];
31
+ const maxWidth = Math.max(...lines.map((l) => stringWidth(l)));
32
+ const boxWidth = maxWidth + 6;
33
+ console.log("");
34
+ console.log(chalk.cyan("\u256D" + "\u2500".repeat(boxWidth - 2) + "\u256E"));
35
+ for (const line of lines) {
36
+ const displayWidth = stringWidth(line);
37
+ const padding = " ".repeat(maxWidth - displayWidth);
38
+ console.log(chalk.cyan("\u2502") + " " + line + padding + " " + chalk.cyan("\u2502"));
39
+ }
40
+ console.log(chalk.cyan("\u2570" + "\u2500".repeat(boxWidth - 2) + "\u256F"));
41
+ console.log("");
42
+ }
43
+ export {
44
+ showVSCodeAnnouncementIfNeeded
45
+ };
46
+ //# sourceMappingURL=vscode-announcement-EQ2SKK3T.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/vscode-announcement.ts"],"sourcesContent":["import chalk from 'chalk'\nimport stringWidth from 'string-width'\nimport { FirstRunManager } from './FirstRunManager.js'\n\nconst FEATURE_NAME = 'vscode-announcement'\n\n/**\n * Check if announcement should be shown and display if so\n * @param commandName - The name of the command that just completed\n */\nexport async function showVSCodeAnnouncementIfNeeded(commandName: string): Promise<void> {\n // Skip if command is 'vscode'\n if (commandName === 'vscode') return\n\n // Skip if not TTY\n if (!process.stdout.isTTY) return\n\n // Check if already shown\n const manager = new FirstRunManager(FEATURE_NAME)\n const isFirstTime = await manager.isFirstRun()\n if (!isFirstTime) return\n\n // Display announcement\n displayAnnouncementBox()\n\n // Mark as shown\n await manager.markAsRun()\n}\n\nfunction displayAnnouncementBox(): void {\n const rocket = '\\u{1F680}'\n const pointRight = '\\u{1F449}'\n\n const lines = [\n ` ${rocket} New: iloom for VS Code is here!`,\n '',\n ' \\u2022 Real-time Recap Panel: See AI decisions, insights, assumptions, and risks as they happen',\n ' \\u2022 Loom Explorer: Manage projects/tasks visually',\n '',\n ` ${pointRight} Run 'il vscode' to install`,\n ]\n\n const maxWidth = Math.max(...lines.map(l => stringWidth(l)))\n const boxWidth = maxWidth + 6 // 2 spaces padding on each side + 2 border chars\n\n // eslint-disable-next-line no-console\n console.log('')\n // eslint-disable-next-line no-console\n console.log(chalk.cyan('\\u256D' + '\\u2500'.repeat(boxWidth - 2) + '\\u256E'))\n for (const line of lines) {\n // Pad based on display width, not string length\n const displayWidth = stringWidth(line)\n const padding = ' '.repeat(maxWidth - displayWidth)\n // eslint-disable-next-line no-console\n console.log(chalk.cyan('\\u2502') + ' ' + line + padding + ' ' + chalk.cyan('\\u2502'))\n }\n // eslint-disable-next-line no-console\n console.log(chalk.cyan('\\u2570' + '\\u2500'.repeat(boxWidth - 2) + '\\u256F'))\n // eslint-disable-next-line no-console\n console.log('')\n}\n"],"mappings":";;;;;;;AAAA,OAAO,WAAW;AAClB,OAAO,iBAAiB;AAGxB,IAAM,eAAe;AAMrB,eAAsB,+BAA+B,aAAoC;AAEvF,MAAI,gBAAgB,SAAU;AAG9B,MAAI,CAAC,QAAQ,OAAO,MAAO;AAG3B,QAAM,UAAU,IAAI,gBAAgB,YAAY;AAChD,QAAM,cAAc,MAAM,QAAQ,WAAW;AAC7C,MAAI,CAAC,YAAa;AAGlB,yBAAuB;AAGvB,QAAM,QAAQ,UAAU;AAC1B;AAEA,SAAS,yBAA+B;AACtC,QAAM,SAAS;AACf,QAAM,aAAa;AAEnB,QAAM,QAAQ;AAAA,IACZ,KAAK,MAAM;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,KAAK,UAAU;AAAA,EACjB;AAEA,QAAM,WAAW,KAAK,IAAI,GAAG,MAAM,IAAI,OAAK,YAAY,CAAC,CAAC,CAAC;AAC3D,QAAM,WAAW,WAAW;AAG5B,UAAQ,IAAI,EAAE;AAEd,UAAQ,IAAI,MAAM,KAAK,WAAW,SAAS,OAAO,WAAW,CAAC,IAAI,QAAQ,CAAC;AAC3E,aAAW,QAAQ,OAAO;AAExB,UAAM,eAAe,YAAY,IAAI;AACrC,UAAM,UAAU,IAAI,OAAO,WAAW,YAAY;AAElD,YAAQ,IAAI,MAAM,KAAK,QAAQ,IAAI,OAAO,OAAO,UAAU,OAAO,MAAM,KAAK,QAAQ,CAAC;AAAA,EACxF;AAED,UAAQ,IAAI,MAAM,KAAK,WAAW,SAAS,OAAO,WAAW,CAAC,IAAI,QAAQ,CAAC;AAE1E,UAAQ,IAAI,EAAE;AAChB;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iloom/cli",
3
- "version": "0.8.1",
3
+ "version": "0.8.3",
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",
@@ -80,6 +80,7 @@
80
80
  "jsonc-parser": "^3.3.1",
81
81
  "omelette": "^0.4.17",
82
82
  "ora": "^7.0.1",
83
+ "string-width": "^8.1.1",
83
84
  "zod": "^3.23.8"
84
85
  },
85
86
  "devDependencies": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/ignite.ts","../src/lib/ClaudeHookManager.ts"],"sourcesContent":["import path from 'path'\nimport { logger } from '../utils/logger.js'\nimport { ClaudeWorkflowOptions } from '../lib/ClaudeService.js'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { launchClaude, ClaudeCliOptions } from '../utils/claude.js'\nimport { PromptTemplateManager, TemplateVariables } from '../lib/PromptTemplateManager.js'\nimport { generateIssueManagementMcpConfig, generateRecapMcpConfig } from '../utils/mcp.js'\nimport { AgentManager } from '../lib/AgentManager.js'\nimport { IssueTrackerFactory } from '../lib/IssueTrackerFactory.js'\nimport { SettingsManager, type IloomSettings } from '../lib/SettingsManager.js'\nimport { MetadataManager } from '../lib/MetadataManager.js'\nimport { extractSettingsOverrides } from '../utils/cli-overrides.js'\nimport { FirstRunManager } from '../utils/FirstRunManager.js'\nimport { extractIssueNumber, isValidGitRepo, getWorktreeRoot } from '../utils/git.js'\nimport { getWorkspacePort } from '../utils/port.js'\nimport { readFile } from 'fs/promises'\nimport { ClaudeHookManager } from '../lib/ClaudeHookManager.js'\nimport type { OneShotMode } from '../types/index.js'\n\n/**\n * Error thrown when the spin command is run from an invalid location\n */\nexport class WorktreeValidationError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic readonly suggestion: string\n\t) {\n\t\tsuper(message)\n\t\tthis.name = 'WorktreeValidationError'\n\t}\n}\n\n/**\n * IgniteCommand: Auto-detect workspace context and launch Claude\n *\n * This command:\n * 1. Auto-detects context from current directory and git branch\n * 2. Loads appropriate prompt template with variable substitution\n * 3. Launches Claude with existing agent system (NO changes to agent loading)\n * 4. Executes in current terminal (not opening a new window)\n *\n * CRITICAL: This command works with agents exactly as they currently function.\n * NO modifications to agent loading mechanisms.\n */\nexport class IgniteCommand {\n\tprivate templateManager: PromptTemplateManager\n\tprivate gitWorktreeManager: GitWorktreeManager\n\tprivate agentManager: AgentManager\n\tprivate settingsManager: SettingsManager\n\tprivate firstRunManager: FirstRunManager\n\tprivate hookManager: ClaudeHookManager\n\tprivate settings?: IloomSettings\n\n\tconstructor(\n\t\ttemplateManager?: PromptTemplateManager,\n\t\tgitWorktreeManager?: GitWorktreeManager,\n\t\tagentManager?: AgentManager,\n\t\tsettingsManager?: SettingsManager,\n\t\tfirstRunManager?: FirstRunManager,\n\t\thookManager?: ClaudeHookManager\n\t) {\n\t\tthis.templateManager = templateManager ?? new PromptTemplateManager()\n\t\tthis.gitWorktreeManager = gitWorktreeManager ?? new GitWorktreeManager()\n\t\tthis.agentManager = agentManager ?? new AgentManager()\n\t\tthis.settingsManager = settingsManager ?? new SettingsManager()\n\t\tthis.firstRunManager = firstRunManager ?? new FirstRunManager('spin')\n\t\tthis.hookManager = hookManager ?? new ClaudeHookManager()\n\t}\n\n\t/**\n\t * Validate that we're not running from the main worktree\n\t * @throws WorktreeValidationError if running from main worktree\n\t */\n\tprivate async validateNotMainWorktree(): Promise<void> {\n\t\tconst currentDir = process.cwd()\n\n\t\t// Step 1: Check if we're in a git repository at all\n\t\tconst isGitRepo = await isValidGitRepo(currentDir)\n\t\tif (!isGitRepo) {\n\t\t\t// Not a git repo - let detectWorkspaceContext handle this gracefully\n\t\t\treturn\n\t\t}\n\n\t\t// Step 2: Get the worktree root (handles subdirectories)\n\t\tconst worktreeRoot = await getWorktreeRoot(currentDir)\n\t\tif (!worktreeRoot) {\n\t\t\t// Could not determine root - let detectWorkspaceContext handle this\n\t\t\treturn\n\t\t}\n\n\t\t// Step 3: Check if this path is a registered git worktree\n\t\tconst worktrees = await this.gitWorktreeManager.listWorktrees()\n\t\tconst currentWorktree = worktrees.find(wt => wt.path === worktreeRoot)\n\n\t\tif (!currentWorktree) {\n\t\t\t// Not a registered worktree - let detectWorkspaceContext handle this\n\t\t\treturn\n\t\t}\n\n\t\t// Step 4: Check if this is the main worktree\n\t\tconst isMain = await this.gitWorktreeManager.isMainWorktree(currentWorktree, this.settingsManager)\n\t\tif (isMain) {\n\t\t\tthrow new WorktreeValidationError(\n\t\t\t\t'You cannot run the command from the main worktree.',\n\t\t\t\t\"Navigate to a feature worktree created by 'il start <issue>' and run 'il spin' from there.\"\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Main entry point for spin command\n\t */\n\tasync execute(oneShot?: OneShotMode): Promise<void> {\n\t\t// Set ILOOM=1 so hooks know this is an iloom session\n\t\t// This is inherited by the Claude child process\n\t\tprocess.env.ILOOM = '1'\n\n\t\t// Validate we're not in the main worktree first\n\t\ttry {\n\t\t\tawait this.validateNotMainWorktree()\n\t\t} catch (error) {\n\t\t\tif (error instanceof WorktreeValidationError) {\n\t\t\t\tlogger.error(error.message)\n\t\t\t\tlogger.info(error.suggestion)\n\t\t\t\tthrow error\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\n\t\ttry {\n\t\t\tlogger.info('🚀 Your loom is spinning up, please wait...')\n\n\t\t\t// Step 0.5: Check if this is first-time user\n\t\t\tconst isFirstRun = await this.firstRunManager.isFirstRun()\n\t\t\tif (isFirstRun) {\n\t\t\t\tlogger.success('Welcome to iloom! Preparing first-time experience...')\n\t\t\t}\n\n\t\t\t// Step 0.6: Install Claude hooks for VSCode integration (idempotent, quick)\n\t\t\tawait this.hookManager.installHooks()\n\n\t\t\t// Step 1: Auto-detect workspace context\n\t\t\tconst context = await this.detectWorkspaceContext()\n\n\t\t\tlogger.debug('Auto-detected workspace context', { context })\n\n\t\t\t// Inform user what context was detected\n\t\t\tthis.logDetectedContext(context)\n\n\t\t\tlogger.info('📝 Loading prompt template and preparing Claude...')\n\n\t\t\t// Step 2: Read metadata early to get draftPrNumber for templates and MCP config\n\t\t\tconst metadataManager = new MetadataManager()\n\t\t\tconst metadata = await metadataManager.readMetadata(context.workspacePath)\n\t\t\tconst draftPrNumber = metadata?.draftPrNumber ?? undefined\n\n\t\t\t// Step 2.0.4: Determine effective oneShot mode\n\t\t\t// If oneShot is provided (any value including 'default'), use it\n\t\t\t// If oneShot is undefined (not passed), use metadata or fallback to 'default'\n\t\t\t// Note: metadata?.oneShot can be null (for legacy looms), so we need double nullish coalescing\n\t\t\tconst storedOneShot = metadata?.oneShot ?? 'default'\n\t\t\tconst effectiveOneShot: OneShotMode = oneShot ?? storedOneShot\n\n\t\t\t// Step 2.0.5: Load settings early if not cached (needed for port calculation)\n\t\t\tif (!this.settings) {\n\t\t\t\tconst cliOverrides = extractSettingsOverrides()\n\t\t\t\tthis.settings = await this.settingsManager.loadSettings(undefined, cliOverrides)\n\t\t\t}\n\n\t\t\t// Step 2.0.6: Calculate port for web-capable looms\n\t\t\tif (metadata?.capabilities?.includes('web') && context.branchName) {\n\t\t\t\tconst basePort = this.settings?.capabilities?.web?.basePort ?? 3000\n\t\t\t\tcontext.port = await getWorkspacePort({\n\t\t\t\t\tbasePort,\n\t\t\t\t\tworktreePath: context.workspacePath,\n\t\t\t\t\tworktreeBranch: context.branchName,\n\t\t\t\t})\n\t\t\t\tlogger.info(`🌐 Development server port: ${context.port}`)\n\t\t\t}\n\n\t\t\t// Step 2.1: Get prompt template with variable substitution\n\t\t\tconst variables = this.buildTemplateVariables(context, effectiveOneShot, draftPrNumber)\n\n\t\t\t// Step 2.5: Add first-time user context if needed\n\t\t\tif (isFirstRun) {\n\t\t\t\tvariables.FIRST_TIME_USER = true\n\t\t\t\tvariables.README_CONTENT = await this.loadReadmeContent()\n\t\t\t\tvariables.SETTINGS_SCHEMA_CONTENT = await this.loadSettingsSchemaContent()\n\t\t\t}\n\n\t\t\tconst systemInstructions = await this.templateManager.getPrompt(context.type, variables)\n\n\t\t\t// User prompt to trigger the workflow (includes one-shot bypass instructions if needed)\n\t\t\tconst userPrompt = this.buildUserPrompt(effectiveOneShot)\n\n\t\t\t// Step 3: Determine model and permission mode based on workflow type\n\t\t\tconst model = this.settingsManager.getSpinModel(this.settings)\n\t\t\tlet permissionMode = this.getPermissionModeForWorkflow(context.type)\n\n\t\t\t// Override permission mode if bypassPermissions oneShot mode\n\t\t\tif (effectiveOneShot === 'bypassPermissions') {\n\t\t\t\tpermissionMode = 'bypassPermissions'\n\t\t\t}\n\n\t\t\t// Display warning if bypassPermissions is used\n\t\t\tif (permissionMode === 'bypassPermissions') {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t'⚠️ WARNING: Using bypassPermissions mode - Claude will execute all tool calls without confirmation. ' +\n\t\t\t\t\t\t'This can be dangerous. Use with caution.'\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// Step 4: Build Claude CLI options\n\t\t\t// Session ID must come from loom metadata - no fallback generation\n\t\t\tconst sessionId = metadata?.sessionId\n\t\t\tif (!sessionId) {\n\t\t\t\tthrow new Error('No session ID found in loom metadata. This loom may need to be recreated with `il start`.')\n\t\t\t}\n\t\t\tlogger.debug('Using session ID from metadata', { sessionId })\n\n\t\t\tconst claudeOptions: ClaudeCliOptions = {\n\t\t\t\theadless: false, // Enable stdio: 'inherit' for current terminal\n\t\t\t\taddDir: context.workspacePath,\n\t\t\t\tsessionId, // Enable Claude Code session resume\n\t\t\t}\n\n\t\t\t// Add optional model if present\n\t\t\tif (model !== undefined) {\n\t\t\t\tclaudeOptions.model = model\n\t\t\t}\n\n\t\t\t// Add permission mode if not default\n\t\t\tif (permissionMode !== undefined && permissionMode !== 'default') {\n\t\t\t\tclaudeOptions.permissionMode = permissionMode\n\t\t\t}\n\n\t\t\t// Add optional branch name for context\n\t\t\tif (context.branchName !== undefined) {\n\t\t\t\tclaudeOptions.branchName = context.branchName\n\t\t\t}\n\n\t\t\t// Step 4.5: Generate MCP config and tool filtering for issue/PR workflows\n\t\t\tlet mcpConfig: Record<string, unknown>[] | undefined\n\t\t\tlet allowedTools: string[] | undefined\n\t\t\tlet disallowedTools: string[] | undefined\n\n\t\t\tif (context.type === 'issue' || context.type === 'pr') {\n\t\t\t\ttry {\n\t\t\t\t\tconst provider = this.settings ? IssueTrackerFactory.getProviderName(this.settings) : 'github'\n\t\t\t\t\t// Pass draftPrNumber to route comments to PR when in github-draft-pr mode\n\t\t\t\t\tmcpConfig = await generateIssueManagementMcpConfig(context.type, undefined, provider, this.settings, draftPrNumber)\n\t\t\t\t\tlogger.debug('Generated MCP configuration for issue management', { provider, draftPrNumber })\n\n\t\t\t\t\t// Configure tool filtering for issue/PR workflows\n\t\t\t\t\t// Note: set_goal is only allowed for PR workflow (user's purpose unclear)\n\t\t\t\t\t// For issue workflow, the issue title provides context so set_goal is not needed\n\t\t\t\t\tconst baseTools = [\n\t\t\t\t\t\t'mcp__issue_management__get_issue',\n\t\t\t\t\t\t'mcp__issue_management__get_comment',\n\t\t\t\t\t\t'mcp__issue_management__create_comment',\n\t\t\t\t\t\t'mcp__issue_management__update_comment',\n\t\t\t\t\t\t'mcp__issue_management__create_issue',\n\t\t\t\t\t\t'mcp__recap__add_entry',\n\t\t\t\t\t\t'mcp__recap__get_recap',\n\t\t\t\t\t\t'mcp__recap__add_artifact',\n\t\t\t\t\t\t'mcp__recap__set_complexity'\n\t\t\t\t\t]\n\t\t\t\t\tallowedTools = context.type === 'pr'\n\t\t\t\t\t\t? [...baseTools, 'mcp__recap__set_goal']\n\t\t\t\t\t\t: baseTools\n\t\t\t\t\tdisallowedTools = ['Bash(gh api:*), Bash(gh issue view:*), Bash(gh pr view:*), Bash(gh issue comment:*)']\n\n\t\t\t\t\tlogger.debug('Configured tool filtering for issue/PR workflow', { allowedTools, disallowedTools })\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// Log warning but continue without MCP\n\t\t\t\t\tlogger.warn(`Failed to generate MCP config: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Regular/branch workflow - allow recap tools (including set_goal since no issue/PR context)\n\t\t\t\tallowedTools = [\n\t\t\t\t\t'mcp__recap__set_goal',\n\t\t\t\t\t'mcp__recap__add_entry',\n\t\t\t\t\t'mcp__recap__get_recap',\n\t\t\t\t\t'mcp__recap__set_complexity',\n\t\t\t\t]\n\t\t\t\tlogger.debug('Configured tool filtering for regular workflow', { allowedTools })\n\t\t\t}\n\n\t\t\t// Step 4.5.1: Generate recap MCP config (always added for all workflow types)\n\t\t\t// Reuses metadata already read in Step 2\n\t\t\ttry {\n\t\t\t\tif (!metadata) {\n\t\t\t\t\tthrow new Error('No loom metadata found for this workspace')\n\t\t\t\t}\n\t\t\t\tconst recapMcpConfig = generateRecapMcpConfig(context.workspacePath, metadata)\n\t\t\t\tif (mcpConfig) {\n\t\t\t\t\tmcpConfig.push(...recapMcpConfig)\n\t\t\t\t} else {\n\t\t\t\t\tmcpConfig = recapMcpConfig\n\t\t\t\t}\n\t\t\t\tlogger.debug('Generated MCP configuration for recap server')\n\t\t\t} catch (error) {\n\t\t\t\t// Log warning but continue without recap MCP\n\t\t\t\tlogger.warn(`Failed to generate recap MCP config: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t\t\t}\n\n\t\t\t// Step 4.6: Load agent configurations using cached settings\n\t\t\tlet agents: Record<string, unknown> | undefined\n\t\t\ttry {\n\t\t\t\t// Use cached settings from Step 2.5\n\t\t\t\tif (this.settings?.agents && Object.keys(this.settings.agents).length > 0) {\n\t\t\t\t\tlogger.debug('Loaded project settings', {\n\t\t\t\t\t\tagentOverrides: Object.keys(this.settings.agents),\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\t// Load agents with settings overrides and template variables for substitution\n\t\t\t\t// Exclude init-only agents (e.g., framework-detector which is only for il init)\n\t\t\t\tconst loadedAgents = await this.agentManager.loadAgents(\n\t\t\t\t\tthis.settings,\n\t\t\t\t\tvariables,\n\t\t\t\t\t['*.md', '!iloom-framework-detector.md']\n\t\t\t\t)\n\t\t\t\tagents = this.agentManager.formatForCli(loadedAgents)\n\t\t\t\tlogger.debug('Loaded agent configurations', {\n\t\t\t\t\tagentCount: Object.keys(agents).length,\n\t\t\t\t\tagentNames: Object.keys(agents),\n\t\t\t\t})\n\t\t\t} catch (error) {\n\t\t\t\t// Log warning but continue without agents\n\t\t\t\tlogger.warn(`Failed to load agents: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t\t\t}\n\n\t\t\tlogger.debug('Launching Claude in current terminal', {\n\t\t\t\ttype: context.type,\n\t\t\t\tmodel,\n\t\t\t\tpermissionMode,\n\t\t\t\tworkspacePath: context.workspacePath,\n\t\t\t\thasMcpConfig: !!mcpConfig,\n\t\t\t})\n\n\t\t\tlogger.info('✨ Launching Claude in current terminal...')\n\n\t\t\t// Step 5: Launch Claude with system instructions appended and user prompt\n\t\t\tawait launchClaude(userPrompt, {\n\t\t\t\t...claudeOptions,\n\t\t\t\tappendSystemPrompt: systemInstructions,\n\t\t\t\t...(mcpConfig && { mcpConfig }),\n\t\t\t\t...(allowedTools && { allowedTools }),\n\t\t\t\t...(disallowedTools && { disallowedTools }),\n\t\t\t\t...(agents && { agents }),\n\t\t\t})\n\n\t\t\t// Step 6: Mark as run after successful launch\n\t\t\tif (isFirstRun) {\n\t\t\t\tawait this.firstRunManager.markAsRun()\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : 'Unknown error'\n\t\t\tlogger.error(`Failed to launch Claude: ${errorMessage}`)\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Log user-friendly information about detected context\n\t */\n\tprivate logDetectedContext(context: ClaudeWorkflowOptions): void {\n\t\tif (context.type === 'issue') {\n\t\t\tlogger.info(`🎯 Detected issue workflow: Issue #${context.issueNumber}`)\n\t\t} else if (context.type === 'pr') {\n\t\t\tlogger.info(`🔄 Detected PR workflow: PR #${context.prNumber}`)\n\t\t} else {\n\t\t\tlogger.info('🌟 Detected regular workflow')\n\t\t}\n\n\t\tif (context.branchName) {\n\t\t\tlogger.info(`🌿 Working on branch: ${context.branchName}`)\n\t\t}\n\n\t\tif (context.port) {\n\t\t\tlogger.info(`🌐 Development server port: ${context.port}`)\n\t\t}\n\t}\n\n\t/**\n\t * Build template variables from context\n\t */\n\tprivate buildTemplateVariables(\n\t\tcontext: ClaudeWorkflowOptions,\n\t\toneShot: OneShotMode,\n\t\tdraftPrNumber?: number\n\t): TemplateVariables {\n\t\tconst variables: TemplateVariables = {\n\t\t\tWORKSPACE_PATH: context.workspacePath,\n\t\t}\n\n\t\tif (context.issueNumber !== undefined) {\n\t\t\tvariables.ISSUE_NUMBER = context.issueNumber\n\t\t}\n\n\t\tif (context.prNumber !== undefined) {\n\t\t\tvariables.PR_NUMBER = context.prNumber\n\t\t}\n\n\t\tif (context.title !== undefined) {\n\t\t\tif (context.type === 'issue') {\n\t\t\t\tvariables.ISSUE_TITLE = context.title\n\t\t\t} else if (context.type === 'pr') {\n\t\t\t\tvariables.PR_TITLE = context.title\n\t\t\t}\n\t\t}\n\n\t\tif (context.port !== undefined) {\n\t\t\tvariables.PORT = context.port\n\t\t}\n\n\t\t// Set ONE_SHOT_MODE or INTERACTIVE_MODE flag for template conditional sections\n\t\tif (oneShot === 'noReview' || oneShot === 'bypassPermissions') {\n\t\t\tvariables.ONE_SHOT_MODE = true\n\t\t} else {\n\t\t\tvariables.INTERACTIVE_MODE = true\n\t\t}\n\n\t\t// Set draft PR mode flags (mutually exclusive)\n\t\t// When draftPrNumber is set, we're in github-draft-pr mode\n\t\tif (draftPrNumber !== undefined) {\n\t\t\tvariables.DRAFT_PR_MODE = true\n\t\t\tvariables.DRAFT_PR_NUMBER = draftPrNumber\n\t\t} else {\n\t\t\tvariables.STANDARD_ISSUE_MODE = true\n\t\t}\n\n\t\treturn variables\n\t}\n\n\t/**\n\t * Get the appropriate permission mode for a workflow type\n\t * Same logic as ClaudeService.getPermissionModeForWorkflow()\n\t */\n\tprivate getPermissionModeForWorkflow(\n\t\ttype: 'issue' | 'pr' | 'regular'\n\t): ClaudeCliOptions['permissionMode'] {\n\t\t// Check settings for configured permission mode\n\t\tif (this.settings?.workflows) {\n\t\t\tconst workflowConfig =\n\t\t\t\ttype === 'issue'\n\t\t\t\t\t? this.settings.workflows.issue\n\t\t\t\t\t: type === 'pr'\n\t\t\t\t\t\t? this.settings.workflows.pr\n\t\t\t\t\t\t: this.settings.workflows.regular\n\n\t\t\tif (workflowConfig?.permissionMode) {\n\t\t\t\treturn workflowConfig.permissionMode\n\t\t\t}\n\t\t}\n\n\t\t// Fall back to current defaults\n\t\tif (type === 'issue') {\n\t\t\treturn 'acceptEdits'\n\t\t}\n\t\t// For PR and regular workflows, use default permissions\n\t\treturn 'default'\n\t}\n\n\t/**\n\t * Auto-detect workspace context from current directory and git branch\n\t *\n\t * Detection priority:\n\t * 1. Directory name patterns (_pr_N, issue-N)\n\t * 2. Git branch name patterns\n\t * 3. Fallback to 'regular' workflow\n\t *\n\t * This leverages the same logic as FinishCommand.autoDetectFromCurrentDirectory()\n\t */\n\tprivate async detectWorkspaceContext(): Promise<ClaudeWorkflowOptions> {\n\t\tconst workspacePath = process.cwd()\n\t\tconst currentDir = path.basename(workspacePath)\n\n\t\t// Check for PR worktree pattern: _pr_N suffix\n\t\t// Pattern: /.*_pr_(\\d+)$/\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\treturn this.buildContextForPR(prNumber, workspacePath)\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\treturn this.buildContextForIssue(issueNumber, workspacePath)\n\t\t}\n\n\t\t// Fallback: Try to extract from git branch name\n\t\ttry {\n\t\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\t\tconst currentBranch = repoInfo.currentBranch\n\n\t\t\tif (currentBranch) {\n\t\t\t\t// Try to extract issue from branch name\n\t\t\t\tconst branchIssueNumber = extractIssueNumber(currentBranch)\n\t\t\t\tif (branchIssueNumber !== null) {\n\t\t\t\t\tlogger.debug(`Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`)\n\n\t\t\t\t\treturn this.buildContextForIssue(branchIssueNumber, workspacePath, currentBranch)\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Git command failed - not a git repo or other git error\n\t\t\tlogger.debug('Could not detect from git branch', { error })\n\t\t}\n\n\t\t// Last resort: use regular workflow\n\t\tlogger.debug('No specific context detected, using regular workflow')\n\t\treturn this.buildContextForRegular(workspacePath)\n\t}\n\n\t/**\n\t * Build context for issue workflow\n\t */\n\tprivate async buildContextForIssue(\n\t\tissueNumber: string | number,\n\t\tworkspacePath: string,\n\t\tbranchName?: string\n\t): Promise<ClaudeWorkflowOptions> {\n\t\t// Get branch name if not provided\n\t\tif (!branchName) {\n\t\t\ttry {\n\t\t\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\t\t\tbranchName = repoInfo.currentBranch ?? undefined\n\t\t\t} catch {\n\t\t\t\t// Ignore git errors\n\t\t\t}\n\t\t}\n\n\t\tconst context: ClaudeWorkflowOptions = {\n\t\t\ttype: 'issue',\n\t\t\tissueNumber,\n\t\t\tworkspacePath,\n\t\t\theadless: false, // Interactive mode\n\t\t}\n\n\t\tif (branchName !== undefined) {\n\t\t\tcontext.branchName = branchName\n\t\t}\n\n\t\treturn context\n\t}\n\n\t/**\n\t * Build context for PR workflow\n\t */\n\tprivate async buildContextForPR(\n\t\tprNumber: number,\n\t\tworkspacePath: string\n\t): Promise<ClaudeWorkflowOptions> {\n\t\t// Get branch name\n\t\tlet branchName: string | undefined\n\t\ttry {\n\t\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\t\tbranchName = repoInfo.currentBranch ?? undefined\n\t\t} catch {\n\t\t\t// Ignore git errors\n\t\t}\n\n\t\tconst context: ClaudeWorkflowOptions = {\n\t\t\ttype: 'pr',\n\t\t\tprNumber,\n\t\t\tworkspacePath,\n\t\t\theadless: false, // Interactive mode\n\t\t}\n\n\t\tif (branchName !== undefined) {\n\t\t\tcontext.branchName = branchName\n\t\t}\n\n\t\treturn context\n\t}\n\n\t/**\n\t * Build context for regular workflow\n\t */\n\tprivate async buildContextForRegular(workspacePath: string): Promise<ClaudeWorkflowOptions> {\n\t\t// Get branch name\n\t\tlet branchName: string | undefined\n\t\ttry {\n\t\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\t\tbranchName = repoInfo.currentBranch ?? undefined\n\t\t} catch {\n\t\t\t// Ignore git errors\n\t\t}\n\n\t\tconst context: ClaudeWorkflowOptions = {\n\t\t\ttype: 'regular',\n\t\t\tworkspacePath,\n\t\t\theadless: false, // Interactive mode\n\t\t}\n\n\t\tif (branchName !== undefined) {\n\t\t\tcontext.branchName = branchName\n\t\t}\n\n\t\treturn context\n\t}\n\n\n\t/**\n\t * Build user prompt based on one-shot mode\n\t */\n\tprivate buildUserPrompt(oneShot: OneShotMode = 'default'): string {\n\t\t// For one-shot modes, add bypass instructions to override template approval requirements\n\t\tif (oneShot === 'noReview' || oneShot === 'bypassPermissions') {\n\t\t\treturn 'Guide the user through the iloom workflow! The user has requested you move through the workflow without awaiting confirmation. This supersedes any other guidance.'\n\t\t}\n\n\t\t// Default mode: simple \"Go!\" prompt\n\t\treturn 'Guide the user through the iloom workflow!'\n\t}\n\n\t/**\n\t * Load README.md content for first-time users\n\t * Walks up from dist directory to find README.md in project root\n\t */\n\tprivate async loadReadmeContent(): Promise<string> {\n\t\ttry {\n\t\t\t// Walk up from current file location to find README.md\n\t\t\t// Use same pattern as PromptTemplateManager for finding files\n\t\t\tlet currentDir = path.dirname(new URL(import.meta.url).pathname)\n\n\t\t\t// Walk up to find README.md\n\t\t\twhile (currentDir !== path.dirname(currentDir)) {\n\t\t\t\tconst readmePath = path.join(currentDir, 'README.md')\n\t\t\t\ttry {\n\t\t\t\t\tconst content = await readFile(readmePath, 'utf-8')\n\t\t\t\t\tlogger.debug('Loaded README.md for first-time user', { readmePath })\n\t\t\t\t\treturn content\n\t\t\t\t} catch {\n\t\t\t\t\tcurrentDir = path.dirname(currentDir)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlogger.debug('README.md not found, returning empty string')\n\t\t\treturn ''\n\t\t} catch (error) {\n\t\t\t// Graceful degradation - return empty string on error\n\t\t\tlogger.debug(`Failed to load README.md: ${error}`)\n\t\t\treturn ''\n\t\t}\n\t}\n\n\t/**\n\t * Load settings schema content for first-time users\n\t * Walks up from dist directory to find .iloom/README.md\n\t */\n\tprivate async loadSettingsSchemaContent(): Promise<string> {\n\t\ttry {\n\t\t\t// Walk up from current file location to find .iloom/README.md\n\t\t\tlet currentDir = path.dirname(new URL(import.meta.url).pathname)\n\n\t\t\t// Walk up to find .iloom/README.md\n\t\t\twhile (currentDir !== path.dirname(currentDir)) {\n\t\t\t\tconst schemaPath = path.join(currentDir, '.iloom', 'README.md')\n\t\t\t\ttry {\n\t\t\t\t\tconst content = await readFile(schemaPath, 'utf-8')\n\t\t\t\t\tlogger.debug('Loaded .iloom/README.md for first-time user', { schemaPath })\n\t\t\t\t\treturn content\n\t\t\t\t} catch {\n\t\t\t\t\tcurrentDir = path.dirname(currentDir)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlogger.debug('.iloom/README.md not found, returning empty string')\n\t\t\treturn ''\n\t\t} catch (error) {\n\t\t\t// Graceful degradation - return empty string on error\n\t\t\tlogger.debug(`Failed to load .iloom/README.md: ${error}`)\n\t\t\treturn ''\n\t\t}\n\t}\n}\n","import os from 'os'\nimport path from 'path'\nimport fs from 'fs-extra'\nimport { parse, modify, applyEdits, ParseError } from 'jsonc-parser'\nimport { fileURLToPath } from 'url'\nimport { accessSync } from 'fs'\nimport { logger } from '../utils/logger.js'\n\n/**\n * Hook configuration for a single event\n */\ninterface HookEntry {\n\ttype: 'command'\n\tcommand: string\n\ttimeout?: number\n}\n\n/**\n * Hook event configuration\n */\ninterface HookEventConfig {\n\tmatcher?: string\n\thooks: HookEntry[]\n}\n\n/**\n * Claude settings.json structure (partial)\n */\ninterface ClaudeSettings {\n\thooks?: Record<string, HookEventConfig[]>\n\t[key: string]: unknown\n}\n\n/**\n * Manages installation of Claude Code hooks to ~/.claude/\n *\n * Hooks enable real-time monitoring of Claude session state\n * via Unix socket communication with the iloom-vscode extension.\n */\nexport class ClaudeHookManager {\n\tprivate claudeDir: string\n\tprivate hooksDir: string\n\tprivate settingsPath: string\n\tprivate templateDir: string\n\n\tconstructor() {\n\t\t// Initialize paths using os.homedir()\n\t\tthis.claudeDir = path.join(os.homedir(), '.claude')\n\t\tthis.hooksDir = path.join(this.claudeDir, 'hooks')\n\t\tthis.settingsPath = path.join(this.claudeDir, 'settings.json')\n\n\t\t// Find templates relative to the package installation\n\t\t// Same pattern as PromptTemplateManager\n\t\tconst currentFileUrl = import.meta.url\n\t\tconst currentFilePath = fileURLToPath(currentFileUrl)\n\t\tconst distDir = path.dirname(currentFilePath)\n\n\t\t// Walk up to find the hooks template directory\n\t\tlet templateDir = path.join(distDir, 'hooks')\n\t\tlet currentDir = distDir\n\n\t\twhile (currentDir !== path.dirname(currentDir)) {\n\t\t\tconst candidatePath = path.join(currentDir, 'hooks')\n\t\t\ttry {\n\t\t\t\taccessSync(candidatePath)\n\t\t\t\ttemplateDir = candidatePath\n\t\t\t\tbreak\n\t\t\t} catch {\n\t\t\t\tcurrentDir = path.dirname(currentDir)\n\t\t\t}\n\t\t}\n\n\t\tthis.templateDir = templateDir\n\t\tlogger.debug('ClaudeHookManager initialized', {\n\t\t\tclaudeDir: this.claudeDir,\n\t\t\thooksDir: this.hooksDir,\n\t\t\tsettingsPath: this.settingsPath,\n\t\t\ttemplateDir: this.templateDir\n\t\t})\n\t}\n\n\t/**\n\t * Install Claude hooks for VSCode integration\n\t *\n\t * This is idempotent - safe to call on every spin.\n\t * Installs hook script to ~/.claude/hooks/ and merges\n\t * hook configuration into ~/.claude/settings.json\n\t */\n\tasync installHooks(): Promise<void> {\n\t\ttry {\n\t\t\t// 1. Create ~/.claude/hooks if missing\n\t\t\tawait fs.ensureDir(this.hooksDir)\n\n\t\t\t// 2. Install hook script from bundled templates\n\t\t\tawait this.installHookScript()\n\n\t\t\t// 3. Merge hook config into settings.json\n\t\t\tawait this.mergeHookConfig()\n\n\t\t\tlogger.debug('Claude hooks installed successfully')\n\t\t} catch (error) {\n\t\t\t// Log warning but don't fail - hooks are optional enhancement\n\t\t\tlogger.warn(\n\t\t\t\t`Failed to install Claude hooks: ${error instanceof Error ? error.message : 'Unknown error'}`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Check if hooks are already installed\n\t */\n\tasync isHooksInstalled(): Promise<boolean> {\n\t\ttry {\n\t\t\t// Check if hook script exists\n\t\t\tconst hookScriptPath = path.join(this.hooksDir, 'iloom-hook.js')\n\t\t\tif (!(await fs.pathExists(hookScriptPath))) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// Check if settings.json has our hooks\n\t\t\tif (!(await fs.pathExists(this.settingsPath))) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tconst content = await fs.readFile(this.settingsPath, 'utf8')\n\t\t\tconst errors: ParseError[] = []\n\t\t\tconst settings = parse(content, errors, { allowTrailingComma: true }) as ClaudeSettings\n\n\t\t\tif (errors.length > 0 || !settings?.hooks) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// Check if our hooks are registered (check for SessionStart as indicator)\n\t\t\treturn Array.isArray(settings.hooks.SessionStart)\n\t\t} catch {\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Install the hook script from bundled templates\n\t * Skips write if destination already has identical content\n\t */\n\tprivate async installHookScript(): Promise<void> {\n\t\tconst sourcePath = path.join(this.templateDir, 'iloom-hook.js')\n\t\tconst destPath = path.join(this.hooksDir, 'iloom-hook.js')\n\n\t\t// Check if source template exists\n\t\tif (!(await fs.pathExists(sourcePath))) {\n\t\t\tthrow new Error(`Hook template not found at ${sourcePath}`)\n\t\t}\n\n\t\t// Skip if destination exists and content matches\n\t\tif (await fs.pathExists(destPath)) {\n\t\t\tconst [sourceContent, destContent] = await Promise.all([\n\t\t\t\tfs.readFile(sourcePath, 'utf8'),\n\t\t\t\tfs.readFile(destPath, 'utf8')\n\t\t\t])\n\t\t\tif (sourceContent === destContent) {\n\t\t\t\tlogger.debug('Hook script already up to date, skipping')\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\t// Copy hook script (only when content differs or doesn't exist)\n\t\tawait fs.copyFile(sourcePath, destPath)\n\t\tlogger.debug('Hook script installed', { sourcePath, destPath })\n\t}\n\n\t/**\n\t * Merge hook configuration into settings.json\n\t * Preserves existing user hooks and comments\n\t */\n\tprivate async mergeHookConfig(): Promise<void> {\n\t\t// Ensure ~/.claude directory exists\n\t\tawait fs.ensureDir(this.claudeDir)\n\n\t\t// Read existing settings (or create empty)\n\t\tlet existingContent = '{}'\n\t\tlet existingSettings: ClaudeSettings = {}\n\n\t\tif (await fs.pathExists(this.settingsPath)) {\n\t\t\texistingContent = await fs.readFile(this.settingsPath, 'utf8')\n\t\t\tconst errors: ParseError[] = []\n\t\t\texistingSettings = parse(existingContent, errors, { allowTrailingComma: true }) as ClaudeSettings\n\n\t\t\tif (errors.length > 0) {\n\t\t\t\tlogger.warn('Existing settings.json has parse errors, will attempt to merge anyway')\n\t\t\t}\n\t\t}\n\n\t\t// Get our hook configuration\n\t\tconst ourHooks = this.getHookConfig()\n\n\t\t// Merge hooks - preserve user's existing hooks on same events\n\t\tconst mergedHooks: Record<string, HookEventConfig[]> = { ...(existingSettings.hooks ?? {}) }\n\t\tlet hooksAdded = false\n\n\t\tfor (const [eventName, eventConfigs] of Object.entries(ourHooks)) {\n\t\t\tconst existing = mergedHooks[eventName] ?? []\n\n\t\t\t// Check if our hook is already registered\n\t\t\tconst ourConfig = eventConfigs[0]\n\t\t\tconst ourCommand = ourConfig?.hooks?.[0]?.command\n\t\t\tconst existingConfigIndex = existing.findIndex(\n\t\t\t\t(config) => config.hooks?.some((h) => h.command === ourCommand)\n\t\t\t)\n\n\t\t\tif (existingConfigIndex === -1) {\n\t\t\t\t// Add our hook config to the event\n\t\t\t\tmergedHooks[eventName] = [...existing, ...eventConfigs]\n\t\t\t\thooksAdded = true\n\t\t\t} else {\n\t\t\t\t// Hook is already registered - check if we need to update the matcher\n\t\t\t\tconst existingConfig = existing[existingConfigIndex]\n\t\t\t\tconst ourMatcher = ourConfig?.matcher\n\n\t\t\t\t// Update matcher if our config has one and existing doesn't match\n\t\t\t\tif (existingConfig && ourMatcher !== undefined && existingConfig.matcher !== ourMatcher) {\n\t\t\t\t\texisting[existingConfigIndex] = {\n\t\t\t\t\t\t...existingConfig,\n\t\t\t\t\t\tmatcher: ourMatcher\n\t\t\t\t\t}\n\t\t\t\t\thooksAdded = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Skip write if no new hooks were added\n\t\tif (!hooksAdded) {\n\t\t\tlogger.debug('All hooks already registered, skipping settings.json update')\n\t\t\treturn\n\t\t}\n\n\t\t// Write updated settings\n\t\tlet content: string\n\n\t\t// Check if existing content has comments\n\t\tif (existingContent.includes('//') || existingContent.includes('/*')) {\n\t\t\t// Use jsonc-parser to preserve comments\n\t\t\tlet modifiedContent = existingContent\n\t\t\tconst edits = modify(modifiedContent, ['hooks'], mergedHooks, {})\n\t\t\tcontent = applyEdits(modifiedContent, edits)\n\t\t} else {\n\t\t\t// No comments - use JSON.stringify\n\t\t\tconst updatedSettings: ClaudeSettings = {\n\t\t\t\t...existingSettings,\n\t\t\t\thooks: mergedHooks\n\t\t\t}\n\t\t\tcontent = JSON.stringify(updatedSettings, null, 2) + '\\n'\n\t\t}\n\n\t\t// Write atomically using temp file + rename\n\t\tconst tempPath = `${this.settingsPath}.tmp`\n\t\tawait fs.writeFile(tempPath, content, 'utf8')\n\t\tawait fs.rename(tempPath, this.settingsPath)\n\n\t\tlogger.debug('Hook configuration merged into settings.json')\n\t}\n\n\t/**\n\t * Get the hook configuration to register\n\t *\n\t * Each event maps to a hook that runs iloom-hook.js\n\t */\n\tprivate getHookConfig(): Record<string, HookEventConfig[]> {\n\t\tconst hookCommand = `node ${path.join(this.hooksDir, 'iloom-hook.js')}`\n\n\t\treturn {\n\t\t\tNotification: [\n\t\t\t\t{ hooks: [{ type: 'command', command: hookCommand }] }\n\t\t\t],\n\t\t\tStop: [\n\t\t\t\t{ hooks: [{ type: 'command', command: hookCommand }] }\n\t\t\t],\n\t\t\tSubagentStop: [\n\t\t\t\t{ hooks: [{ type: 'command', command: hookCommand }] }\n\t\t\t],\n\t\t\tPermissionRequest: [\n\t\t\t\t{ matcher: '*', hooks: [{ type: 'command', command: hookCommand, timeout: 86400 }] }\n\t\t\t],\n\t\t\tPreToolUse: [\n\t\t\t\t{ matcher: '*', hooks: [{ type: 'command', command: hookCommand }] }\n\t\t\t],\n\t\t\tPostToolUse: [\n\t\t\t\t{ matcher: '*', hooks: [{ type: 'command', command: hookCommand }] }\n\t\t\t],\n\t\t\tSessionStart: [\n\t\t\t\t{ matcher: '*', hooks: [{ type: 'command', command: hookCommand }] }\n\t\t\t],\n\t\t\tSessionEnd: [\n\t\t\t\t{ hooks: [{ type: 'command', command: hookCommand }] }\n\t\t\t],\n\t\t\tUserPromptSubmit: [\n\t\t\t\t{ hooks: [{ type: 'command', command: hookCommand }] }\n\t\t\t]\n\t\t}\n\t}\n}\n"],"mappings":"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","names":["path","_a","path"]}