@iloom/cli 0.5.4 → 0.6.0
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 +116 -4
- package/dist/{ClaudeContextManager-PQ46VILL.js → ClaudeContextManager-6J2EB4QU.js} +5 -5
- package/dist/{ClaudeService-6OMO552H.js → ClaudeService-O2PB22GX.js} +4 -4
- package/dist/{LoomLauncher-ZHDTPKED.js → LoomLauncher-5LFM4LXB.js} +5 -5
- package/dist/ProjectCapabilityDetector-S5FLNCFI.js +11 -0
- package/dist/{PromptTemplateManager-5GNF7FCP.js → PromptTemplateManager-C3DK6XZL.js} +2 -2
- package/dist/README.md +116 -4
- package/dist/{SettingsManager-CNYBGXDT.js → SettingsManager-35F5RUJH.js} +2 -2
- package/dist/agents/iloom-framework-detector.md +366 -0
- package/dist/agents/iloom-issue-analyze-and-plan.md +0 -10
- package/dist/agents/iloom-issue-implementer.md +1 -1
- package/dist/agents/iloom-issue-planner.md +0 -10
- package/dist/build-FJVYP7EV.js +27 -0
- package/dist/build-FJVYP7EV.js.map +1 -0
- package/dist/{chunk-SHVB3EFE.js → chunk-64O2UIWO.js} +44 -19
- package/dist/chunk-64O2UIWO.js.map +1 -0
- package/dist/{chunk-FXJKNVZW.js → chunk-6U6VI4SZ.js} +3 -3
- package/dist/{chunk-QPS6TZUW.js → chunk-7WANFUIK.js} +2 -2
- package/dist/{chunk-LVLRMP7V.js → chunk-AXX3QIKK.js} +58 -14
- package/dist/chunk-AXX3QIKK.js.map +1 -0
- package/dist/chunk-BXCPJJYM.js +133 -0
- package/dist/chunk-BXCPJJYM.js.map +1 -0
- package/dist/{chunk-ESP2FF52.js → chunk-EK3XCAAS.js} +2 -2
- package/dist/{chunk-IDUICCZY.js → chunk-F6WVM437.js} +3 -1
- package/dist/chunk-F6WVM437.js.map +1 -0
- package/dist/{chunk-TKL7RBEF.js → chunk-GEXP5IOF.js} +3 -3
- package/dist/{chunk-J7GHNTYK.js → chunk-K7SEEHKO.js} +3 -3
- package/dist/{chunk-NRDY6XO3.js → chunk-PMVWQBWS.js} +73 -17
- package/dist/chunk-PMVWQBWS.js.map +1 -0
- package/dist/{chunk-NKRQNER7.js → chunk-Q7POFB5Q.js} +1 -55
- package/dist/chunk-Q7POFB5Q.js.map +1 -0
- package/dist/{chunk-N4ZJVATC.js → chunk-SN3Z6EZO.js} +11 -7
- package/dist/chunk-SN3Z6EZO.js.map +1 -0
- package/dist/chunk-TRQ76ISK.js +159 -0
- package/dist/chunk-TRQ76ISK.js.map +1 -0
- package/dist/{chunk-ZXWTOJXA.js → chunk-UB4TFAXJ.js} +38 -9
- package/dist/chunk-UB4TFAXJ.js.map +1 -0
- package/dist/{chunk-YPOG7WY4.js → chunk-UQIXZ3BA.js} +2 -2
- package/dist/{chunk-K5G5SFWY.js → chunk-W6WVRHJ6.js} +13 -1
- package/dist/chunk-W6WVRHJ6.js.map +1 -0
- package/dist/{chunk-E4F7KASE.js → chunk-WIJWIKAN.js} +3 -3
- package/dist/{chunk-EBISESAP.js → chunk-ZPSTA5PR.js} +16 -6
- package/dist/chunk-ZPSTA5PR.js.map +1 -0
- package/dist/{cleanup-H5QUWBWE.js → cleanup-OU2HFOOG.js} +11 -11
- package/dist/cli.js +107 -69
- package/dist/cli.js.map +1 -1
- package/dist/compile-ULNO5F7Q.js +57 -0
- package/dist/compile-ULNO5F7Q.js.map +1 -0
- package/dist/{contribute-VP73TPAL.js → contribute-T7ENST5N.js} +3 -3
- package/dist/{dev-server-H5FFXIVX.js → dev-server-4RCDJ5MU.js} +9 -9
- package/dist/{feedback-567ZH2O7.js → feedback-O4Q55SVS.js} +8 -8
- package/dist/{git-UHUNQZBA.js → git-FVMGBHC2.js} +3 -3
- package/dist/{ignite-VPP4PMF4.js → ignite-VHV65WEZ.js} +12 -8
- package/dist/ignite-VHV65WEZ.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/dist/{init-4ZR2XXZA.js → init-HB34Q5FH.js} +8 -7
- package/dist/lint-5JMCWE4Y.js +27 -0
- package/dist/lint-5JMCWE4Y.js.map +1 -0
- package/dist/{open-6HBQHPUL.js → open-WHVUYGPY.js} +9 -9
- package/dist/prompts/init-prompt.txt +108 -0
- package/dist/prompts/issue-prompt.txt +12 -12
- package/dist/{rebase-SRBOVC4M.js → rebase-5EY3Q6XP.js} +5 -5
- package/dist/{recap-X7FTTKPP.js → recap-VOOUXOGP.js} +5 -5
- package/dist/{run-5FU76FFE.js → run-NCRK5NPR.js} +9 -9
- package/dist/schema/settings.schema.json +8 -0
- package/dist/{shell-UQJDI36V.js → shell-SBLXVOVJ.js} +5 -5
- package/dist/{summary-MOKN7RM2.js → summary-CVFAMDOJ.js} +6 -6
- package/dist/test-3KIVXI6J.js +27 -0
- package/dist/test-3KIVXI6J.js.map +1 -0
- package/dist/{test-git-GB3B6QNT.js → test-git-ZB6AGGRW.js} +3 -3
- package/dist/{test-prefix-YQNNTCY3.js → test-prefix-FBGXKMPA.js} +3 -3
- package/package.json +2 -1
- package/dist/ProjectCapabilityDetector-34LU7JJ4.js +0 -9
- package/dist/chunk-2ZPFJQ3B.js +0 -63
- package/dist/chunk-2ZPFJQ3B.js.map +0 -1
- package/dist/chunk-EBISESAP.js.map +0 -1
- package/dist/chunk-IDUICCZY.js.map +0 -1
- package/dist/chunk-K5G5SFWY.js.map +0 -1
- package/dist/chunk-LVLRMP7V.js.map +0 -1
- package/dist/chunk-N4ZJVATC.js.map +0 -1
- package/dist/chunk-NKRQNER7.js.map +0 -1
- package/dist/chunk-NRDY6XO3.js.map +0 -1
- package/dist/chunk-SHVB3EFE.js.map +0 -1
- package/dist/chunk-ZXWTOJXA.js.map +0 -1
- package/dist/ignite-VPP4PMF4.js.map +0 -1
- /package/dist/{ClaudeContextManager-PQ46VILL.js.map → ClaudeContextManager-6J2EB4QU.js.map} +0 -0
- /package/dist/{ClaudeService-6OMO552H.js.map → ClaudeService-O2PB22GX.js.map} +0 -0
- /package/dist/{LoomLauncher-ZHDTPKED.js.map → LoomLauncher-5LFM4LXB.js.map} +0 -0
- /package/dist/{ProjectCapabilityDetector-34LU7JJ4.js.map → ProjectCapabilityDetector-S5FLNCFI.js.map} +0 -0
- /package/dist/{PromptTemplateManager-5GNF7FCP.js.map → PromptTemplateManager-C3DK6XZL.js.map} +0 -0
- /package/dist/{SettingsManager-CNYBGXDT.js.map → SettingsManager-35F5RUJH.js.map} +0 -0
- /package/dist/{chunk-FXJKNVZW.js.map → chunk-6U6VI4SZ.js.map} +0 -0
- /package/dist/{chunk-QPS6TZUW.js.map → chunk-7WANFUIK.js.map} +0 -0
- /package/dist/{chunk-ESP2FF52.js.map → chunk-EK3XCAAS.js.map} +0 -0
- /package/dist/{chunk-TKL7RBEF.js.map → chunk-GEXP5IOF.js.map} +0 -0
- /package/dist/{chunk-J7GHNTYK.js.map → chunk-K7SEEHKO.js.map} +0 -0
- /package/dist/{chunk-YPOG7WY4.js.map → chunk-UQIXZ3BA.js.map} +0 -0
- /package/dist/{chunk-E4F7KASE.js.map → chunk-WIJWIKAN.js.map} +0 -0
- /package/dist/{cleanup-H5QUWBWE.js.map → cleanup-OU2HFOOG.js.map} +0 -0
- /package/dist/{contribute-VP73TPAL.js.map → contribute-T7ENST5N.js.map} +0 -0
- /package/dist/{dev-server-H5FFXIVX.js.map → dev-server-4RCDJ5MU.js.map} +0 -0
- /package/dist/{feedback-567ZH2O7.js.map → feedback-O4Q55SVS.js.map} +0 -0
- /package/dist/{git-UHUNQZBA.js.map → git-FVMGBHC2.js.map} +0 -0
- /package/dist/{init-4ZR2XXZA.js.map → init-HB34Q5FH.js.map} +0 -0
- /package/dist/{open-6HBQHPUL.js.map → open-WHVUYGPY.js.map} +0 -0
- /package/dist/{rebase-SRBOVC4M.js.map → rebase-5EY3Q6XP.js.map} +0 -0
- /package/dist/{recap-X7FTTKPP.js.map → recap-VOOUXOGP.js.map} +0 -0
- /package/dist/{run-5FU76FFE.js.map → run-NCRK5NPR.js.map} +0 -0
- /package/dist/{shell-UQJDI36V.js.map → shell-SBLXVOVJ.js.map} +0 -0
- /package/dist/{summary-MOKN7RM2.js.map → summary-CVFAMDOJ.js.map} +0 -0
- /package/dist/{test-git-GB3B6QNT.js.map → test-git-ZB6AGGRW.js.map} +0 -0
- /package/dist/{test-prefix-YQNNTCY3.js.map → test-prefix-FBGXKMPA.js.map} +0 -0
|
@@ -72,60 +72,6 @@ var FirstRunManager = class {
|
|
|
72
72
|
return false;
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
|
-
/**
|
|
76
|
-
* Check if a project has local .iloom settings files
|
|
77
|
-
* Returns true if .iloom/settings.json or .iloom/settings.local.json exists with content
|
|
78
|
-
*/
|
|
79
|
-
async hasLocalSettings(projectPath) {
|
|
80
|
-
const iloomDir = path.join(projectPath, ".iloom");
|
|
81
|
-
const settingsPath = path.join(iloomDir, "settings.json");
|
|
82
|
-
const settingsLocalPath = path.join(iloomDir, "settings.local.json");
|
|
83
|
-
const hasSettings = await this.hasNonEmptySettingsFile(settingsPath);
|
|
84
|
-
const hasLocalSettings = await this.hasNonEmptySettingsFile(settingsLocalPath);
|
|
85
|
-
return hasSettings || hasLocalSettings;
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* Check if a settings file exists and has non-empty content
|
|
89
|
-
*/
|
|
90
|
-
async hasNonEmptySettingsFile(filePath) {
|
|
91
|
-
try {
|
|
92
|
-
const exists = await fs.pathExists(filePath);
|
|
93
|
-
if (!exists) return false;
|
|
94
|
-
const content = await fs.readFile(filePath, "utf8");
|
|
95
|
-
const parsed = JSON.parse(content);
|
|
96
|
-
return Object.keys(parsed).length > 0;
|
|
97
|
-
} catch {
|
|
98
|
-
return false;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Fixup legacy projects that have local config but lack the global marker file
|
|
103
|
-
* This handles projects configured before global project tracking was implemented
|
|
104
|
-
*
|
|
105
|
-
* Returns object with:
|
|
106
|
-
* - isConfigured: true if project is configured (either already had marker or fixup created one)
|
|
107
|
-
* - wasFixedUp: true if fixup was performed (marker was created by this call)
|
|
108
|
-
*
|
|
109
|
-
* This combined return avoids duplicate isProjectConfigured() calls in needsFirstRunSetup()
|
|
110
|
-
*/
|
|
111
|
-
async fixupLegacyProject(projectPath) {
|
|
112
|
-
const inputPath = projectPath ?? process.cwd();
|
|
113
|
-
const resolvedPath = await this.resolveProjectPath(inputPath);
|
|
114
|
-
logger.debug("fixupLegacyProject: Checking for legacy project", { projectPath: resolvedPath });
|
|
115
|
-
const hasMarker = await this.isProjectConfigured(resolvedPath);
|
|
116
|
-
if (hasMarker) {
|
|
117
|
-
logger.debug("fixupLegacyProject: Project already has global marker");
|
|
118
|
-
return { isConfigured: true, wasFixedUp: false };
|
|
119
|
-
}
|
|
120
|
-
const hasLocal = await this.hasLocalSettings(resolvedPath);
|
|
121
|
-
if (!hasLocal) {
|
|
122
|
-
logger.debug("fixupLegacyProject: No local settings found, not a legacy project");
|
|
123
|
-
return { isConfigured: false, wasFixedUp: false };
|
|
124
|
-
}
|
|
125
|
-
logger.debug("fixupLegacyProject: Legacy project detected, creating global marker");
|
|
126
|
-
await this.markProjectAsConfigured(resolvedPath);
|
|
127
|
-
return { isConfigured: true, wasFixedUp: true };
|
|
128
|
-
}
|
|
129
75
|
/**
|
|
130
76
|
* Mark a project as configured
|
|
131
77
|
* Creates a marker file with project metadata
|
|
@@ -194,4 +140,4 @@ var FirstRunManager = class {
|
|
|
194
140
|
export {
|
|
195
141
|
FirstRunManager
|
|
196
142
|
};
|
|
197
|
-
//# sourceMappingURL=chunk-
|
|
143
|
+
//# sourceMappingURL=chunk-Q7POFB5Q.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/FirstRunManager.ts"],"sourcesContent":["import os from 'os'\nimport path from 'path'\nimport fs from 'fs-extra'\nimport { logger } from './logger.js'\n\n/**\n * FirstRunManager: Detect and track first-time spin usage via marker file\n *\n * Follows the same pattern as UpdateNotifier for file-based state tracking\n * in ~/.config/iloom-ai/ directory.\n *\n * Also supports project-level tracking for config wizard completion using\n * individual marker files per project in ~/.config/iloom-ai/projects/\n */\nexport class FirstRunManager {\n\tprivate markerFilePath: string\n\tprivate configDir: string\n\n\tconstructor(feature: string = 'spin') {\n\t\tthis.configDir = path.join(os.homedir(), '.config', 'iloom-ai')\n\t\tthis.markerFilePath = path.join(this.configDir, `${feature}-first-run`)\n\t\tlogger.debug('FirstRunManager initialized', {\n\t\t\tfeature,\n\t\t\tmarkerFilePath: this.markerFilePath\n\t\t})\n\t}\n\n\t/**\n\t * Get the directory for project marker files\n\t */\n\tprivate getProjectsDir(): string {\n\t\treturn path.join(this.configDir, 'projects')\n\t}\n\n\t/**\n\t * Resolve symlinks in project path to get canonical path\n\t * Falls back to original path on errors (broken symlinks, permissions, etc.)\n\t */\n\tprivate async resolveProjectPath(projectPath: string): Promise<string> {\n\t\ttry {\n\t\t\treturn await fs.realpath(projectPath)\n\t\t} catch {\n\t\t\tlogger.debug('resolveProjectPath: Failed to resolve symlink, using original path', { projectPath })\n\t\t\treturn projectPath\n\t\t}\n\t}\n\n\t/**\n\t * Convert a project path to a readable filename\n\t * /Users/adam/Projects/my-app -> Users__adam__Projects__my-app\n\t */\n\tprivate projectPathToFileName(projectPath: string): string {\n\t\tconst normalized = path.normalize(projectPath)\n\t\treturn normalized.replace(/^[/\\\\]+/, '').replace(/[/\\\\]+/g, '__')\n\t}\n\n\t/**\n\t * Get full path to a project's marker file\n\t */\n\tprivate getProjectMarkerPath(projectPath: string): string {\n\t\treturn path.join(this.getProjectsDir(), this.projectPathToFileName(projectPath))\n\t}\n\n\t/**\n\t * Extract project name from path\n\t */\n\tprivate getProjectName(projectPath: string): string {\n\t\treturn path.basename(projectPath)\n\t}\n\n\t/**\n\t * Check if a project has been configured\n\t * Returns true if project marker file exists\n\t */\n\tasync isProjectConfigured(projectPath?: string): Promise<boolean> {\n\t\tconst inputPath = projectPath ?? process.cwd()\n\t\tconst resolvedPath = await this.resolveProjectPath(inputPath)\n\t\tconst markerPath = this.getProjectMarkerPath(resolvedPath)\n\t\tlogger.debug('isProjectConfigured: Checking for marker file', { markerPath })\n\t\ttry {\n\t\t\tconst exists = await fs.pathExists(markerPath)\n\t\t\tlogger.debug(`isProjectConfigured: Marker file exists=${exists}`)\n\t\t\treturn exists\n\t\t} catch (error) {\n\t\t\t// On error, treat as not configured to allow wizard to run\n\t\t\tlogger.debug(`isProjectConfigured: Error checking marker file, treating as not configured: ${error}`)\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Mark a project as configured\n\t * Creates a marker file with project metadata\n\t * Idempotent - skips if already configured\n\t */\n\tasync markProjectAsConfigured(projectPath?: string): Promise<void> {\n\t\tconst inputPath = projectPath ?? process.cwd()\n\t\tconst resolvedPath = await this.resolveProjectPath(inputPath)\n\t\tconst markerPath = this.getProjectMarkerPath(resolvedPath)\n\n\t\t// Idempotency check - skip if already configured\n\t\tif (await this.isProjectConfigured(resolvedPath)) {\n\t\t\tlogger.debug('markProjectAsConfigured: Project already configured, skipping', { markerPath })\n\t\t\treturn\n\t\t}\n\n\t\tlogger.debug('markProjectAsConfigured: Creating marker file', { markerPath })\n\t\ttry {\n\t\t\tawait fs.ensureDir(this.getProjectsDir())\n\t\t\tconst markerContent = {\n\t\t\t\tconfiguredAt: new Date().toISOString(),\n\t\t\t\tprojectPath: resolvedPath,\n\t\t\t\tprojectName: this.getProjectName(resolvedPath)\n\t\t\t}\n\t\t\tawait fs.writeFile(markerPath, JSON.stringify(markerContent, null, 2), 'utf8')\n\t\t\tlogger.debug('markProjectAsConfigured: Marker file created successfully')\n\t\t} catch (error) {\n\t\t\t// Don't throw on errors - just log debug message\n\t\t\tlogger.debug(`markProjectAsConfigured: Failed to create marker file: ${error}`)\n\t\t}\n\t}\n\n\t/**\n\t * Check if this is the first run of the feature\n\t * Returns true if marker file doesn't exist\n\t * Handles errors gracefully by returning true (treat as first-run on error)\n\t */\n\tasync isFirstRun(): Promise<boolean> {\n\t\tlogger.debug('isFirstRun: Checking for marker file', { markerFilePath: this.markerFilePath })\n\t\ttry {\n\t\t\tconst exists = await fs.pathExists(this.markerFilePath)\n\t\t\tlogger.debug(`isFirstRun: Marker file exists=${exists}`)\n\t\t\treturn !exists\n\t\t} catch (error) {\n\t\t\t// On error, gracefully degrade by treating as first-run\n\t\t\tlogger.debug(`isFirstRun: Error checking marker file, treating as first-run: ${error}`)\n\t\t\treturn true\n\t\t}\n\t}\n\n\t/**\n\t * Mark the feature as having been run\n\t * Creates the marker file in config directory\n\t * Handles errors gracefully without throwing\n\t */\n\tasync markAsRun(): Promise<void> {\n\t\tlogger.debug('markAsRun: Attempting to create marker file', { markerFilePath: this.markerFilePath })\n\t\ttry {\n\t\t\t// Ensure directory exists\n\t\t\tconst configDir = path.dirname(this.markerFilePath)\n\t\t\tlogger.debug(`markAsRun: Ensuring config directory exists: ${configDir}`)\n\t\t\tawait fs.ensureDir(configDir)\n\n\t\t\t// Write marker file with timestamp for debugging\n\t\t\tconst markerContent = {\n\t\t\t\tfirstRun: new Date().toISOString(),\n\t\t\t}\n\t\t\tawait fs.writeFile(this.markerFilePath, JSON.stringify(markerContent, null, 2), 'utf8')\n\t\t\tlogger.debug('markAsRun: Marker file created successfully')\n\t\t} catch (error) {\n\t\t\t// Don't throw on errors - just log debug message\n\t\t\t// Failing to write marker shouldn't break the workflow\n\t\t\tlogger.debug(`markAsRun: Failed to create marker file: ${error}`)\n\t\t}\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
PromptTemplateManager
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-W6WVRHJ6.js";
|
|
5
5
|
import {
|
|
6
6
|
logger
|
|
7
7
|
} from "./chunk-VT4PDUYT.js";
|
|
@@ -11,6 +11,7 @@ import { readFile } from "fs/promises";
|
|
|
11
11
|
import { accessSync } from "fs";
|
|
12
12
|
import path from "path";
|
|
13
13
|
import { fileURLToPath } from "url";
|
|
14
|
+
import fg from "fast-glob";
|
|
14
15
|
|
|
15
16
|
// src/utils/MarkdownAgentParser.ts
|
|
16
17
|
var MarkdownAgentParser = class {
|
|
@@ -139,16 +140,19 @@ var AgentManager = class {
|
|
|
139
140
|
}
|
|
140
141
|
}
|
|
141
142
|
/**
|
|
142
|
-
* Load
|
|
143
|
+
* Load agent configuration files from markdown (.md) format
|
|
143
144
|
* Optionally apply model overrides from settings and template variable substitution
|
|
144
145
|
* Throws error if agents directory doesn't exist or files are malformed
|
|
145
146
|
* @param settings - Optional project settings with per-agent model overrides
|
|
146
147
|
* @param templateVariables - Optional variables for template substitution in agent prompts
|
|
148
|
+
* @param patterns - Optional glob patterns to filter which agents to load (default: ['*.md'])
|
|
149
|
+
* Supports negation patterns like ['*.md', '!iloom-framework-detector.md']
|
|
147
150
|
*/
|
|
148
|
-
async loadAgents(settings, templateVariables) {
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
151
|
+
async loadAgents(settings, templateVariables, patterns = ["*.md"]) {
|
|
152
|
+
const agentFiles = await fg(patterns, {
|
|
153
|
+
cwd: this.agentDir,
|
|
154
|
+
onlyFiles: true
|
|
155
|
+
});
|
|
152
156
|
const agents = {};
|
|
153
157
|
for (const filename of agentFiles) {
|
|
154
158
|
const agentPath = path.join(this.agentDir, filename);
|
|
@@ -259,4 +263,4 @@ var AgentManager = class {
|
|
|
259
263
|
export {
|
|
260
264
|
AgentManager
|
|
261
265
|
};
|
|
262
|
-
//# sourceMappingURL=chunk-
|
|
266
|
+
//# sourceMappingURL=chunk-SN3Z6EZO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/AgentManager.ts","../src/utils/MarkdownAgentParser.ts"],"sourcesContent":["import { readFile } from 'fs/promises'\nimport { accessSync } from 'fs'\nimport path from 'path'\nimport { fileURLToPath } from 'url'\nimport fg from 'fast-glob'\nimport { MarkdownAgentParser } from '../utils/MarkdownAgentParser.js'\nimport { logger } from '../utils/logger.js'\nimport type { IloomSettings } from './SettingsManager.js'\nimport { PromptTemplateManager, TemplateVariables } from './PromptTemplateManager.js'\n\n// Agent schema interface\nexport interface AgentConfig {\n\tdescription: string\n\tprompt: string\n\ttools: string[]\n\tmodel: string\n\tcolor?: string\n}\n\n// Container for all loaded agents (keyed by agent name without extension)\nexport interface AgentConfigs {\n\t[agentName: string]: AgentConfig\n}\n\nexport class AgentManager {\n\tprivate agentDir: string\n\tprivate templateManager: PromptTemplateManager\n\n\tconstructor(agentDir?: string, templateManager?: PromptTemplateManager) {\n\t\tthis.templateManager = templateManager ?? new PromptTemplateManager()\n\t\tif (agentDir) {\n\t\t\tthis.agentDir = agentDir\n\t\t} else {\n\t\t\t// Find agents relative to package installation\n\t\t\t// Same pattern as PromptTemplateManager\n\t\t\t// When running from dist/, agents are copied to dist/agents/\n\t\t\tconst currentFileUrl = import.meta.url\n\t\t\tconst currentFilePath = fileURLToPath(currentFileUrl)\n\t\t\tconst distDir = path.dirname(currentFilePath)\n\n\t\t\t// Walk up to find the agents directory\n\t\t\tlet agentDirPath = path.join(distDir, 'agents')\n\t\t\tlet currentDir = distDir\n\n\t\t\twhile (currentDir !== path.dirname(currentDir)) {\n\t\t\t\tconst candidatePath = path.join(currentDir, 'agents')\n\t\t\t\ttry {\n\t\t\t\t\taccessSync(candidatePath)\n\t\t\t\t\tagentDirPath = candidatePath\n\t\t\t\t\tbreak\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\tthis.agentDir = agentDirPath\n\t\t\tlogger.debug('AgentManager initialized', { agentDir: this.agentDir })\n\t\t}\n\t}\n\n\t/**\n\t * Load agent configuration files from markdown (.md) format\n\t * Optionally apply model overrides from settings and template variable substitution\n\t * Throws error if agents directory doesn't exist or files are malformed\n\t * @param settings - Optional project settings with per-agent model overrides\n\t * @param templateVariables - Optional variables for template substitution in agent prompts\n\t * @param patterns - Optional glob patterns to filter which agents to load (default: ['*.md'])\n\t * Supports negation patterns like ['*.md', '!iloom-framework-detector.md']\n\t */\n\tasync loadAgents(\n\t\tsettings?: IloomSettings,\n\t\ttemplateVariables?: TemplateVariables,\n\t\tpatterns: string[] = ['*.md']\n\t): Promise<AgentConfigs> {\n\t\t// Use fast-glob to filter agent files based on patterns\n\t\tconst agentFiles = await fg(patterns, {\n\t\t\tcwd: this.agentDir,\n\t\t\tonlyFiles: true,\n\t\t})\n\n\t\tconst agents: AgentConfigs = {}\n\n\t\tfor (const filename of agentFiles) {\n\t\t\tconst agentPath = path.join(this.agentDir, filename)\n\n\t\t\ttry {\n\t\t\t\tconst content = await readFile(agentPath, 'utf-8')\n\n\t\t\t\t// Parse markdown with frontmatter\n\t\t\t\tconst parsed = this.parseMarkdownAgent(content, filename)\n\t\t\t\tconst agentConfig = parsed.config\n\t\t\t\tconst agentName = parsed.name\n\n\t\t\t\t// Validate required fields\n\t\t\t\tthis.validateAgentConfig(agentConfig, agentName)\n\n\t\t\t\tagents[agentName] = agentConfig\n\t\t\t\tlogger.debug(`Loaded agent: ${agentName}`)\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(`Failed to load agent from ${filename}`, { error })\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Failed to load agent from ${filename}: ${error instanceof Error ? error.message : 'Unknown error'}`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\t// Apply template variable substitution to agent prompts if variables provided\n\t\tif (templateVariables) {\n\t\t\tfor (const [agentName, agentConfig] of Object.entries(agents)) {\n\t\t\t\tagents[agentName] = {\n\t\t\t\t\t...agentConfig,\n\t\t\t\t\tprompt: this.templateManager.substituteVariables(agentConfig.prompt, templateVariables),\n\t\t\t\t}\n\t\t\t\tlogger.debug(`Applied template substitution to agent: ${agentName}`)\n\t\t\t}\n\t\t}\n\n\t\t// Apply settings overrides if provided\n\t\tif (settings?.agents) {\n\t\t\tfor (const [agentName, agentSettings] of Object.entries(settings.agents)) {\n\t\t\t\tif (agents[agentName] && agentSettings.model) {\n\t\t\t\t\tlogger.debug(`Overriding model for ${agentName}: ${agents[agentName].model} -> ${agentSettings.model}`)\n\t\t\t\t\tagents[agentName] = {\n\t\t\t\t\t\t...agents[agentName],\n\t\t\t\t\t\tmodel: agentSettings.model,\n\t\t\t\t\t}\n\t\t\t\t} else if (!agents[agentName]) {\n\t\t\t\t\tlogger.warn(`Settings reference unknown agent: ${agentName}`)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn agents\n\t}\n\n\t/**\n\t * Validate agent configuration has required fields\n\t */\n\tprivate validateAgentConfig(config: AgentConfig, agentName: string): void {\n\t\tconst requiredFields: (keyof AgentConfig)[] = ['description', 'prompt', 'tools', 'model']\n\n\t\tfor (const field of requiredFields) {\n\t\t\tif (!config[field]) {\n\t\t\t\tthrow new Error(`Agent ${agentName} missing required field: ${field}`)\n\t\t\t}\n\t\t}\n\n\t\tif (!Array.isArray(config.tools)) {\n\t\t\tthrow new Error(`Agent ${agentName} tools must be an array`)\n\t\t}\n\t}\n\n\t/**\n\t * Parse markdown agent file with YAML frontmatter\n\t * @param content - Raw markdown file content\n\t * @param filename - Original filename for error messages\n\t * @returns Parsed agent config and name\n\t */\n\tprivate parseMarkdownAgent(content: string, filename: string): { config: AgentConfig; name: string } {\n\t\ttry {\n\t\t\t// Parse frontmatter using custom parser\n\t\t\tconst { data, content: markdownBody } = MarkdownAgentParser.parse(content)\n\n\t\t\t// Validate frontmatter has required fields\n\t\t\tif (!data.name) {\n\t\t\t\tthrow new Error('Missing required field: name')\n\t\t\t}\n\t\t\tif (!data.description) {\n\t\t\t\tthrow new Error('Missing required field: description')\n\t\t\t}\n\t\t\tif (!data.tools) {\n\t\t\t\tthrow new Error('Missing required field: tools')\n\t\t\t}\n\t\t\tif (!data.model) {\n\t\t\t\tthrow new Error('Missing required field: model')\n\t\t\t}\n\n\t\t\t// Parse tools from comma-separated string to array\n\t\t\tconst tools = data.tools\n\t\t\t\t.split(',')\n\t\t\t\t.map((tool: string) => tool.trim())\n\t\t\t\t.filter((tool: string) => tool.length > 0)\n\n\t\t\t// Validate model and warn if non-standard\n\t\t\tconst validModels = ['sonnet', 'opus', 'haiku']\n\t\t\tif (!validModels.includes(data.model)) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Agent ${data.name} uses model \"${data.model}\" which may not be recognized by Claude CLI, and your workflow may fail or produce unexpected results. ` +\n\t\t\t\t\t\t`Valid values are: ${validModels.join(', ')}`\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// Construct AgentConfig\n\t\t\tconst config: AgentConfig = {\n\t\t\t\tdescription: data.description,\n\t\t\t\tprompt: markdownBody.trim(),\n\t\t\t\ttools,\n\t\t\t\tmodel: data.model,\n\t\t\t\t...(data.color && { color: data.color }),\n\t\t\t}\n\n\t\t\treturn { config, name: data.name }\n\t\t} catch (error) {\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to parse markdown agent ${filename}: ${error instanceof Error ? error.message : 'Unknown error'}`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Format loaded agents for Claude CLI --agents flag\n\t * Returns object suitable for JSON.stringify\n\t */\n\tformatForCli(agents: AgentConfigs): Record<string, unknown> {\n\t\t// The agents object is already in the correct format\n\t\t// Just return it - launchClaude will JSON.stringify it\n\t\treturn agents as Record<string, unknown>\n\t}\n}\n","/**\n * Custom YAML frontmatter parser for agent markdown files\n * Replaces gray-matter dependency with lightweight custom implementation\n */\n\ninterface ParseResult {\n\tdata: Record<string, string>\n\tcontent: string\n}\n\nexport class MarkdownAgentParser {\n\t/**\n\t * Parse markdown content with YAML frontmatter\n\t * @param content - Raw markdown file content\n\t * @returns Object with parsed frontmatter data and markdown body content\n\t * @throws Error if frontmatter is malformed or missing\n\t */\n\tstatic parse(content: string): ParseResult {\n\t\tconst lines = content.split('\\n')\n\n\t\t// Check for opening frontmatter delimiter\n\t\tif (lines[0]?.trim() !== '---') {\n\t\t\tthrow new Error('Missing opening frontmatter delimiter (---)')\n\t\t}\n\n\t\t// Find closing frontmatter delimiter\n\t\tlet closingDelimiterIndex = -1\n\t\tfor (let i = 1; i < lines.length; i++) {\n\t\t\tif (lines[i]?.trim() === '---') {\n\t\t\t\tclosingDelimiterIndex = i\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif (closingDelimiterIndex === -1) {\n\t\t\tthrow new Error('Missing closing frontmatter delimiter (---)')\n\t\t}\n\n\t\t// Extract frontmatter lines (between the delimiters)\n\t\tconst frontmatterLines = lines.slice(1, closingDelimiterIndex)\n\n\t\t// Extract markdown body (after closing delimiter)\n\t\tconst bodyLines = lines.slice(closingDelimiterIndex + 1)\n\t\tconst markdownBody = bodyLines.join('\\n')\n\n\t\t// Parse YAML frontmatter into key-value pairs\n\t\tconst data = this.parseYaml(frontmatterLines.join('\\n'))\n\n\t\treturn {\n\t\t\tdata,\n\t\t\tcontent: markdownBody,\n\t\t}\n\t}\n\n\t/**\n\t * Parse simplified YAML into key-value object\n\t * Supports:\n\t * - Simple key: value pairs\n\t * - Multiline values with | indicator\n\t * - Values with special characters and newlines\n\t *\n\t * @param yaml - YAML string to parse\n\t * @returns Object with parsed key-value pairs\n\t */\n\tprivate static parseYaml(yaml: string): Record<string, string> {\n\t\tconst result: Record<string, string> = {}\n\t\tconst lines = yaml.split('\\n')\n\t\tlet currentKey: string | null = null\n\t\tlet currentValue: string[] = []\n\t\tlet isMultiline = false\n\n\t\tconst finalizeCurrent = (): void => {\n\t\t\tif (currentKey && currentValue.length > 0) {\n\t\t\t\tresult[currentKey] = currentValue.join('\\n').trim()\n\t\t\t\tcurrentKey = null\n\t\t\t\tcurrentValue = []\n\t\t\t}\n\t\t}\n\n\t\tfor (const line of lines) {\n\t\t\t// Skip empty lines when not in multiline mode\n\t\t\tif (!isMultiline && line.trim() === '') {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Check if this is a new key-value pair\n\t\t\tconst keyValueMatch = line.match(/^([a-zA-Z_][a-zA-Z0-9_-]*)\\s*:\\s*(.*)$/)\n\n\t\t\tif (keyValueMatch && !isMultiline) {\n\t\t\t\t// Finalize previous key if exists\n\t\t\t\tfinalizeCurrent()\n\n\t\t\t\tconst [, key, value] = keyValueMatch\n\t\t\t\tif (!key || value === undefined) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tcurrentKey = key\n\n\t\t\t\t// Check for multiline indicator\n\t\t\t\tif (value.trim() === '|') {\n\t\t\t\t\tisMultiline = true\n\t\t\t\t\tcurrentValue = []\n\t\t\t\t} else {\n\t\t\t\t\t// Single line value\n\t\t\t\t\tcurrentValue = [value]\n\t\t\t\t\tfinalizeCurrent()\n\t\t\t\t\tisMultiline = false\n\t\t\t\t}\n\t\t\t} else if (isMultiline && currentKey) {\n\t\t\t\t// Continuation of multiline value\n\t\t\t\t// Check if we've returned to normal indentation (new key)\n\t\t\t\tif (line.match(/^[a-zA-Z_][a-zA-Z0-9_-]*\\s*:/) && !line.startsWith(' ')) {\n\t\t\t\t\t// End of multiline, this is a new key\n\t\t\t\t\tfinalizeCurrent()\n\t\t\t\t\tisMultiline = false\n\n\t\t\t\t\t// Process this line as a new key\n\t\t\t\t\tconst match = line.match(/^([a-zA-Z_][a-zA-Z0-9_-]*)\\s*:\\s*(.*)$/)\n\t\t\t\t\tif (match) {\n\t\t\t\t\t\tconst [, key, value] = match\n\t\t\t\t\t\tif (key && value !== undefined) {\n\t\t\t\t\t\t\tcurrentKey = key\n\t\t\t\t\t\t\tcurrentValue = [value]\n\t\t\t\t\t\t\tfinalizeCurrent()\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Remove leading spaces (common indentation) from multiline values\n\t\t\t\t\tconst trimmedLine = line.replace(/^ {2}/, '')\n\t\t\t\t\tcurrentValue.push(trimmedLine)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Finalize last key\n\t\tfinalizeCurrent()\n\n\t\treturn result\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
runScript
|
|
4
|
+
} from "./chunk-AXX3QIKK.js";
|
|
5
|
+
import {
|
|
6
|
+
IdentifierParser
|
|
7
|
+
} from "./chunk-UQIXZ3BA.js";
|
|
8
|
+
import {
|
|
9
|
+
getPackageScripts
|
|
10
|
+
} from "./chunk-BXCPJJYM.js";
|
|
11
|
+
import {
|
|
12
|
+
GitWorktreeManager
|
|
13
|
+
} from "./chunk-EK3XCAAS.js";
|
|
14
|
+
import {
|
|
15
|
+
extractIssueNumber
|
|
16
|
+
} from "./chunk-GEXP5IOF.js";
|
|
17
|
+
import {
|
|
18
|
+
logger
|
|
19
|
+
} from "./chunk-VT4PDUYT.js";
|
|
20
|
+
|
|
21
|
+
// src/commands/script-command-base.ts
|
|
22
|
+
import path from "path";
|
|
23
|
+
var ScriptCommandBase = class {
|
|
24
|
+
constructor(gitWorktreeManager) {
|
|
25
|
+
this.gitWorktreeManager = gitWorktreeManager ?? new GitWorktreeManager();
|
|
26
|
+
this.identifierParser = new IdentifierParser(this.gitWorktreeManager);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Execute the script command
|
|
30
|
+
*/
|
|
31
|
+
async execute(input) {
|
|
32
|
+
const parsed = input.identifier ? await this.parseExplicitInput(input.identifier) : await this.autoDetectFromCurrentDirectory();
|
|
33
|
+
logger.debug(`Parsed input: ${JSON.stringify(parsed)}`);
|
|
34
|
+
const worktree = await this.findWorktreeForIdentifier(parsed);
|
|
35
|
+
logger.info(`Found worktree at: ${worktree.path}`);
|
|
36
|
+
const scripts = await getPackageScripts(worktree.path);
|
|
37
|
+
const scriptName = this.getScriptName();
|
|
38
|
+
if (!scripts[scriptName]) {
|
|
39
|
+
throw new Error(`No ${scriptName} script defined in package.json or package.iloom.json`);
|
|
40
|
+
}
|
|
41
|
+
logger.info(`Running ${this.getScriptDisplayName()}...`);
|
|
42
|
+
await runScript(scriptName, worktree.path, []);
|
|
43
|
+
logger.success(`${this.getScriptDisplayName()} completed successfully`);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Parse explicit identifier input
|
|
47
|
+
*/
|
|
48
|
+
async parseExplicitInput(identifier) {
|
|
49
|
+
const parsed = await this.identifierParser.parseForPatternDetection(identifier);
|
|
50
|
+
if (parsed.type === "description") {
|
|
51
|
+
throw new Error("Description input type is not supported in script commands");
|
|
52
|
+
}
|
|
53
|
+
const result = {
|
|
54
|
+
type: parsed.type,
|
|
55
|
+
originalInput: parsed.originalInput,
|
|
56
|
+
autoDetected: false
|
|
57
|
+
};
|
|
58
|
+
if (parsed.number !== void 0) {
|
|
59
|
+
result.number = parsed.number;
|
|
60
|
+
}
|
|
61
|
+
if (parsed.branchName !== void 0) {
|
|
62
|
+
result.branchName = parsed.branchName;
|
|
63
|
+
}
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Auto-detect identifier from current directory
|
|
68
|
+
* Same logic as RunCommand.autoDetectFromCurrentDirectory()
|
|
69
|
+
*/
|
|
70
|
+
async autoDetectFromCurrentDirectory() {
|
|
71
|
+
const currentDir = path.basename(process.cwd());
|
|
72
|
+
const prPattern = /_pr_(\d+)$/;
|
|
73
|
+
const prMatch = currentDir.match(prPattern);
|
|
74
|
+
if (prMatch == null ? void 0 : prMatch[1]) {
|
|
75
|
+
const prNumber = parseInt(prMatch[1], 10);
|
|
76
|
+
logger.debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`);
|
|
77
|
+
return {
|
|
78
|
+
type: "pr",
|
|
79
|
+
number: prNumber,
|
|
80
|
+
originalInput: currentDir,
|
|
81
|
+
autoDetected: true
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
const issueNumber = extractIssueNumber(currentDir);
|
|
85
|
+
if (issueNumber !== null) {
|
|
86
|
+
logger.debug(`Auto-detected issue #${issueNumber} from directory: ${currentDir}`);
|
|
87
|
+
return {
|
|
88
|
+
type: "issue",
|
|
89
|
+
number: issueNumber,
|
|
90
|
+
originalInput: currentDir,
|
|
91
|
+
autoDetected: true
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const repoInfo = await this.gitWorktreeManager.getRepoInfo();
|
|
95
|
+
const currentBranch = repoInfo.currentBranch;
|
|
96
|
+
if (!currentBranch) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
"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"
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
const branchIssueNumber = extractIssueNumber(currentBranch);
|
|
102
|
+
if (branchIssueNumber !== null) {
|
|
103
|
+
logger.debug(`Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`);
|
|
104
|
+
return {
|
|
105
|
+
type: "issue",
|
|
106
|
+
number: branchIssueNumber,
|
|
107
|
+
originalInput: currentBranch,
|
|
108
|
+
autoDetected: true
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
type: "branch",
|
|
113
|
+
branchName: currentBranch,
|
|
114
|
+
originalInput: currentBranch,
|
|
115
|
+
autoDetected: true
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Find worktree for the given identifier
|
|
120
|
+
*/
|
|
121
|
+
async findWorktreeForIdentifier(parsed) {
|
|
122
|
+
let worktree = null;
|
|
123
|
+
if (parsed.type === "issue" && parsed.number !== void 0) {
|
|
124
|
+
worktree = await this.gitWorktreeManager.findWorktreeForIssue(parsed.number);
|
|
125
|
+
} else if (parsed.type === "pr" && parsed.number !== void 0) {
|
|
126
|
+
const prNumber = typeof parsed.number === "number" ? parsed.number : Number(parsed.number);
|
|
127
|
+
if (isNaN(prNumber) || !isFinite(prNumber)) {
|
|
128
|
+
throw new Error(`Invalid PR number: ${parsed.number}. PR numbers must be numeric.`);
|
|
129
|
+
}
|
|
130
|
+
worktree = await this.gitWorktreeManager.findWorktreeForPR(prNumber, "");
|
|
131
|
+
} else if (parsed.type === "branch" && parsed.branchName) {
|
|
132
|
+
worktree = await this.gitWorktreeManager.findWorktreeForBranch(parsed.branchName);
|
|
133
|
+
}
|
|
134
|
+
if (!worktree) {
|
|
135
|
+
throw new Error(
|
|
136
|
+
`No worktree found for ${this.formatParsedInput(parsed)}. Run 'il start ${parsed.originalInput}' to create one.`
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
return worktree;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Format parsed input for display
|
|
143
|
+
*/
|
|
144
|
+
formatParsedInput(parsed) {
|
|
145
|
+
const autoLabel = parsed.autoDetected ? " (auto-detected)" : "";
|
|
146
|
+
if (parsed.type === "issue") {
|
|
147
|
+
return `issue #${parsed.number}${autoLabel}`;
|
|
148
|
+
}
|
|
149
|
+
if (parsed.type === "pr") {
|
|
150
|
+
return `PR #${parsed.number}${autoLabel}`;
|
|
151
|
+
}
|
|
152
|
+
return `branch "${parsed.branchName}"${autoLabel}`;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
export {
|
|
157
|
+
ScriptCommandBase
|
|
158
|
+
};
|
|
159
|
+
//# sourceMappingURL=chunk-TRQ76ISK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/script-command-base.ts"],"sourcesContent":["import path from 'path'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { IdentifierParser } from '../utils/IdentifierParser.js'\nimport { runScript } from '../utils/package-manager.js'\nimport { getPackageScripts } from '../utils/package-json.js'\nimport { extractIssueNumber } from '../utils/git.js'\nimport { logger } from '../utils/logger.js'\nimport type { GitWorktree } from '../types/worktree.js'\n\nexport interface ScriptCommandInput {\n\tidentifier?: string\n}\n\ninterface ParsedScriptInput {\n\ttype: 'issue' | 'pr' | 'branch'\n\tnumber?: string | number\n\tbranchName?: string\n\toriginalInput: string\n\tautoDetected: boolean\n}\n\n/**\n * ScriptCommandBase - shared logic for build/lint/test/compile commands\n * Provides identifier parsing and worktree resolution using the same pattern as RunCommand\n */\nexport abstract class ScriptCommandBase {\n\tprotected gitWorktreeManager: GitWorktreeManager\n\tprotected identifierParser: IdentifierParser\n\n\tconstructor(gitWorktreeManager?: GitWorktreeManager) {\n\t\tthis.gitWorktreeManager = gitWorktreeManager ?? new GitWorktreeManager()\n\t\tthis.identifierParser = new IdentifierParser(this.gitWorktreeManager)\n\t}\n\n\t/**\n\t * Get the script name to run (e.g., 'build', 'lint', 'test')\n\t */\n\tabstract getScriptName(): string\n\n\t/**\n\t * Get the display name for logging (e.g., 'Build', 'Lint', 'Test')\n\t */\n\tabstract getScriptDisplayName(): string\n\n\t/**\n\t * Execute the script command\n\t */\n\tasync execute(input: ScriptCommandInput): 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\t\tlogger.info(`Found worktree at: ${worktree.path}`)\n\n\t\t// 3. Check if script exists\n\t\tconst scripts = await getPackageScripts(worktree.path)\n\t\tconst scriptName = this.getScriptName()\n\n\t\tif (!scripts[scriptName]) {\n\t\t\tthrow new Error(`No ${scriptName} script defined in package.json or package.iloom.json`)\n\t\t}\n\n\t\t// 4. Run the script\n\t\tlogger.info(`Running ${this.getScriptDisplayName()}...`)\n\t\tawait runScript(scriptName, worktree.path, [])\n\t\tlogger.success(`${this.getScriptDisplayName()} completed successfully`)\n\t}\n\n\t/**\n\t * Parse explicit identifier input\n\t */\n\tprotected async parseExplicitInput(identifier: string): Promise<ParsedScriptInput> {\n\t\tconst parsed = await this.identifierParser.parseForPatternDetection(identifier)\n\n\t\t// Description type should never reach script command (converted in start)\n\t\tif (parsed.type === 'description') {\n\t\t\tthrow new Error('Description input type is not supported in script commands')\n\t\t}\n\n\t\tconst result: ParsedScriptInput = {\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 RunCommand.autoDetectFromCurrentDirectory()\n\t */\n\tprotected async autoDetectFromCurrentDirectory(): Promise<ParsedScriptInput> {\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\tprotected async findWorktreeForIdentifier(parsed: ParsedScriptInput): 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(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\tprotected formatParsedInput(parsed: ParsedScriptInput): 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":";;;;;;;;;;;;;;;;;;;;;AAAA,OAAO,UAAU;AAyBV,IAAe,oBAAf,MAAiC;AAAA,EAIvC,YAAY,oBAAyC;AACpD,SAAK,qBAAqB,sBAAsB,IAAI,mBAAmB;AACvE,SAAK,mBAAmB,IAAI,iBAAiB,KAAK,kBAAkB;AAAA,EACrE;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,QAAQ,OAA0C;AAEvD,UAAM,SAAS,MAAM,aAClB,MAAM,KAAK,mBAAmB,MAAM,UAAU,IAC9C,MAAM,KAAK,+BAA+B;AAE7C,WAAO,MAAM,iBAAiB,KAAK,UAAU,MAAM,CAAC,EAAE;AAGtD,UAAM,WAAW,MAAM,KAAK,0BAA0B,MAAM;AAC5D,WAAO,KAAK,sBAAsB,SAAS,IAAI,EAAE;AAGjD,UAAM,UAAU,MAAM,kBAAkB,SAAS,IAAI;AACrD,UAAM,aAAa,KAAK,cAAc;AAEtC,QAAI,CAAC,QAAQ,UAAU,GAAG;AACzB,YAAM,IAAI,MAAM,MAAM,UAAU,uDAAuD;AAAA,IACxF;AAGA,WAAO,KAAK,WAAW,KAAK,qBAAqB,CAAC,KAAK;AACvD,UAAM,UAAU,YAAY,SAAS,MAAM,CAAC,CAAC;AAC7C,WAAO,QAAQ,GAAG,KAAK,qBAAqB,CAAC,yBAAyB;AAAA,EACvE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAgB,mBAAmB,YAAgD;AAClF,UAAM,SAAS,MAAM,KAAK,iBAAiB,yBAAyB,UAAU;AAG9E,QAAI,OAAO,SAAS,eAAe;AAClC,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC7E;AAEA,UAAM,SAA4B;AAAA,MACjC,MAAM,OAAO;AAAA,MACb,eAAe,OAAO;AAAA,MACtB,cAAc;AAAA,IACf;AAEA,QAAI,OAAO,WAAW,QAAW;AAChC,aAAO,SAAS,OAAO;AAAA,IACxB;AACA,QAAI,OAAO,eAAe,QAAW;AACpC,aAAO,aAAa,OAAO;AAAA,IAC5B;AAEA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAgB,iCAA6D;AAC5E,UAAM,aAAa,KAAK,SAAS,QAAQ,IAAI,CAAC;AAG9C,UAAM,YAAY;AAClB,UAAM,UAAU,WAAW,MAAM,SAAS;AAE1C,QAAI,mCAAU,IAAI;AACjB,YAAM,WAAW,SAAS,QAAQ,CAAC,GAAG,EAAE;AACxC,aAAO,MAAM,qBAAqB,QAAQ,oBAAoB,UAAU,EAAE;AAC1E,aAAO;AAAA,QACN,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,eAAe;AAAA,QACf,cAAc;AAAA,MACf;AAAA,IACD;AAGA,UAAM,cAAc,mBAAmB,UAAU;AAEjD,QAAI,gBAAgB,MAAM;AACzB,aAAO,MAAM,wBAAwB,WAAW,oBAAoB,UAAU,EAAE;AAChF,aAAO;AAAA,QACN,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,eAAe;AAAA,QACf,cAAc;AAAA,MACf;AAAA,IACD;AAGA,UAAM,WAAW,MAAM,KAAK,mBAAmB,YAAY;AAC3D,UAAM,gBAAgB,SAAS;AAE/B,QAAI,CAAC,eAAe;AACnB,YAAM,IAAI;AAAA,QACT;AAAA,MAED;AAAA,IACD;AAGA,UAAM,oBAAoB,mBAAmB,aAAa;AAC1D,QAAI,sBAAsB,MAAM;AAC/B,aAAO,MAAM,wBAAwB,iBAAiB,iBAAiB,aAAa,EAAE;AACtF,aAAO;AAAA,QACN,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,eAAe;AAAA,QACf,cAAc;AAAA,MACf;AAAA,IACD;AAGA,WAAO;AAAA,MACN,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,cAAc;AAAA,IACf;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAgB,0BAA0B,QAAiD;AAC1F,QAAI,WAA+B;AAEnC,QAAI,OAAO,SAAS,WAAW,OAAO,WAAW,QAAW;AAC3D,iBAAW,MAAM,KAAK,mBAAmB,qBAAqB,OAAO,MAAM;AAAA,IAC5E,WAAW,OAAO,SAAS,QAAQ,OAAO,WAAW,QAAW;AAE/D,YAAM,WAAW,OAAO,OAAO,WAAW,WAAW,OAAO,SAAS,OAAO,OAAO,MAAM;AACzF,UAAI,MAAM,QAAQ,KAAK,CAAC,SAAS,QAAQ,GAAG;AAC3C,cAAM,IAAI,MAAM,sBAAsB,OAAO,MAAM,+BAA+B;AAAA,MACnF;AAEA,iBAAW,MAAM,KAAK,mBAAmB,kBAAkB,UAAU,EAAE;AAAA,IACxE,WAAW,OAAO,SAAS,YAAY,OAAO,YAAY;AACzD,iBAAW,MAAM,KAAK,mBAAmB,sBAAsB,OAAO,UAAU;AAAA,IACjF;AAEA,QAAI,CAAC,UAAU;AACd,YAAM,IAAI;AAAA,QACT,yBAAyB,KAAK,kBAAkB,MAAM,CAAC,mBACrC,OAAO,aAAa;AAAA,MACvC;AAAA,IACD;AAEA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKU,kBAAkB,QAAmC;AAC9D,UAAM,YAAY,OAAO,eAAe,qBAAqB;AAE7D,QAAI,OAAO,SAAS,SAAS;AAC5B,aAAO,UAAU,OAAO,MAAM,GAAG,SAAS;AAAA,IAC3C;AACA,QAAI,OAAO,SAAS,MAAM;AACzB,aAAO,OAAO,OAAO,MAAM,GAAG,SAAS;AAAA,IACxC;AACA,WAAO,WAAW,OAAO,UAAU,IAAI,SAAS;AAAA,EACjD;AACD;","names":[]}
|
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
FirstRunManager
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-Q7POFB5Q.js";
|
|
5
5
|
import {
|
|
6
6
|
SettingsMigrationManager
|
|
7
7
|
} from "./chunk-F2PWIRV4.js";
|
|
8
|
+
import {
|
|
9
|
+
AgentManager
|
|
10
|
+
} from "./chunk-SN3Z6EZO.js";
|
|
8
11
|
import {
|
|
9
12
|
GitWorktreeManager
|
|
10
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-EK3XCAAS.js";
|
|
11
14
|
import {
|
|
12
15
|
parseGitRemotes
|
|
13
16
|
} from "./chunk-FXDYIV3K.js";
|
|
14
17
|
import {
|
|
15
18
|
PromptTemplateManager
|
|
16
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-W6WVRHJ6.js";
|
|
17
20
|
import {
|
|
18
21
|
getRepoRoot,
|
|
19
22
|
isFileGitignored
|
|
20
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-GEXP5IOF.js";
|
|
21
24
|
import {
|
|
22
25
|
detectClaudeCli,
|
|
23
26
|
launchClaude
|
|
@@ -324,9 +327,10 @@ import path2 from "path";
|
|
|
324
327
|
import os2 from "os";
|
|
325
328
|
import { fileURLToPath } from "url";
|
|
326
329
|
var InitCommand = class {
|
|
327
|
-
constructor(shellCompletion, templateManager) {
|
|
330
|
+
constructor(shellCompletion, templateManager, agentManager) {
|
|
328
331
|
this.shellCompletion = shellCompletion ?? new ShellCompletion();
|
|
329
332
|
this.templateManager = templateManager ?? new PromptTemplateManager();
|
|
333
|
+
this.agentManager = agentManager ?? new AgentManager();
|
|
330
334
|
}
|
|
331
335
|
/**
|
|
332
336
|
* Main entry point for the init command
|
|
@@ -632,6 +636,9 @@ var InitCommand = class {
|
|
|
632
636
|
logger.debug("README content loaded", {
|
|
633
637
|
readmeContentLength: readmeContent.length
|
|
634
638
|
});
|
|
639
|
+
const packageJsonPath = path2.join(process.cwd(), "package.json");
|
|
640
|
+
const hasPackageJson = existsSync2(packageJsonPath);
|
|
641
|
+
logger.debug("Package.json detection", { packageJsonPath, hasPackageJson });
|
|
635
642
|
const variables = {
|
|
636
643
|
SETTINGS_SCHEMA: schemaContent,
|
|
637
644
|
SETTINGS_GLOBAL_JSON: settingsGlobalJson,
|
|
@@ -647,7 +654,10 @@ var InitCommand = class {
|
|
|
647
654
|
SINGLE_REMOTE_URL: singleRemoteUrl,
|
|
648
655
|
NO_REMOTES: noRemotes.toString(),
|
|
649
656
|
README_CONTENT: readmeContent,
|
|
650
|
-
VSCODE_SETTINGS_GITIGNORED: vscodeSettingsGitignored.toString()
|
|
657
|
+
VSCODE_SETTINGS_GITIGNORED: vscodeSettingsGitignored.toString(),
|
|
658
|
+
// Multi-language support - mutually exclusive booleans
|
|
659
|
+
HAS_PACKAGE_JSON: hasPackageJson,
|
|
660
|
+
NO_PACKAGE_JSON: !hasPackageJson
|
|
651
661
|
};
|
|
652
662
|
logger.debug("Building template variables", {
|
|
653
663
|
variableKeys: Object.keys(variables),
|
|
@@ -663,10 +673,28 @@ var InitCommand = class {
|
|
|
663
673
|
containsSchema: prompt.includes("SETTINGS_SCHEMA"),
|
|
664
674
|
containsExistingSettings: prompt.includes("EXISTING_SETTINGS")
|
|
665
675
|
});
|
|
676
|
+
let agents;
|
|
677
|
+
try {
|
|
678
|
+
const loadedAgents = await this.agentManager.loadAgents(
|
|
679
|
+
void 0,
|
|
680
|
+
// No settings overrides for init
|
|
681
|
+
variables,
|
|
682
|
+
["iloom-framework-detector.md"]
|
|
683
|
+
);
|
|
684
|
+
agents = this.agentManager.formatForCli(loadedAgents);
|
|
685
|
+
logger.debug("Loaded framework-detector agent for init", {
|
|
686
|
+
agentCount: Object.keys(agents).length,
|
|
687
|
+
agentNames: Object.keys(agents)
|
|
688
|
+
});
|
|
689
|
+
} catch (error) {
|
|
690
|
+
logger.warn(`Failed to load agents: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
691
|
+
}
|
|
666
692
|
const claudeOptions = {
|
|
693
|
+
model: "opus",
|
|
667
694
|
headless: false,
|
|
668
695
|
appendSystemPrompt: prompt,
|
|
669
|
-
addDir: process.cwd()
|
|
696
|
+
addDir: process.cwd(),
|
|
697
|
+
...agents && { agents }
|
|
670
698
|
};
|
|
671
699
|
logger.debug("Launching Claude with options", {
|
|
672
700
|
optionKeys: Object.keys(claudeOptions),
|
|
@@ -674,7 +702,8 @@ var InitCommand = class {
|
|
|
674
702
|
hasSystemPrompt: !!claudeOptions.appendSystemPrompt,
|
|
675
703
|
addDir: claudeOptions.addDir,
|
|
676
704
|
promptLength: prompt.length,
|
|
677
|
-
hasCustomInitialMessage: !!customInitialMessage
|
|
705
|
+
hasCustomInitialMessage: !!customInitialMessage,
|
|
706
|
+
hasAgents: !!agents
|
|
678
707
|
});
|
|
679
708
|
const initialMessage = customInitialMessage ?? "Help me configure iloom settings.";
|
|
680
709
|
await launchClaude(initialMessage, claudeOptions);
|
|
@@ -718,4 +747,4 @@ export {
|
|
|
718
747
|
ShellCompletion,
|
|
719
748
|
InitCommand
|
|
720
749
|
};
|
|
721
|
-
//# sourceMappingURL=chunk-
|
|
750
|
+
//# sourceMappingURL=chunk-UB4TFAXJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/ShellCompletion.ts","../src/commands/init.ts"],"sourcesContent":["import omelette from 'omelette'\nimport { GitWorktreeManager } from './GitWorktreeManager.js'\nimport { logger } from '../utils/logger.js'\nimport { readFile } from 'fs/promises'\nimport { existsSync } from 'fs'\nimport path from 'path'\nimport os from 'os'\n\nexport type ShellType = 'bash' | 'zsh' | 'fish' | 'unknown'\n\n/**\n * Manages shell autocomplete functionality for the iloom CLI\n * Uses omelette to provide tab-completion for commands in bash/zsh/fish\n */\nexport class ShellCompletion {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private completion: any // omelette instance - no types available\n private readonly COMPLETION_TIMEOUT = 1000 // ms - prevent blocking\n private readonly commandName: string\n\n constructor(commandName?: string) {\n // Detect command name from process.argv[1] if not provided\n this.commandName = commandName ?? this.detectCommandName()\n\n // Initialize omelette with both command names using pipe syntax\n // This registers completion for both 'iloom' and 'il' aliases\n // Template covers: <commandName> <command> <arg>\n // This allows for two-level completion: command completion + argument completion\n this.completion = omelette('iloom|il <command> <arg>')\n this.setupHandlers()\n }\n\n private detectCommandName(): string {\n // Get the actual command name used to invoke this script\n const scriptPath = process.argv[1] ?? 'il'\n const baseName = scriptPath.split('/').pop() ?? 'il'\n\n // Remove .js extension if present\n return baseName.replace(/\\.js$/, '')\n }\n\n private setupHandlers(): void {\n // Handler for command-level completion\n // When user types: il <TAB>\n this.completion.on('command', ({ reply }: { reply: (suggestions: string[]) => void }) => {\n reply([\n 'start',\n 'finish',\n 'spin',\n 'ignite',\n 'open',\n 'run',\n 'cleanup',\n 'list',\n 'init',\n // Intentionally exclude test-* commands from autocomplete\n ])\n })\n\n // Handler for argument-level completion\n // When user types: il <command> <TAB>\n this.completion.on('arg', async ({ line, reply }: { line: string; reply: (suggestions: string[]) => void }) => {\n // Check if the command is 'cleanup' to provide dynamic branch suggestions\n if (line.includes('cleanup')) {\n // Use timeout to prevent blocking if worktree listing is slow\n const suggestions = await this.getBranchSuggestionsWithTimeout()\n reply(suggestions)\n } else {\n // For other commands, no argument suggestions\n reply([])\n }\n })\n }\n\n /**\n * Get branch suggestions with timeout to prevent blocking\n */\n private async getBranchSuggestionsWithTimeout(): Promise<string[]> {\n try {\n return await Promise.race([\n this.getBranchSuggestions(),\n this.timeout(this.COMPLETION_TIMEOUT, []),\n ])\n } catch (error) {\n logger.debug(`Autocomplete branch suggestions failed: ${error}`)\n return []\n }\n }\n\n private async timeout<T>(ms: number, defaultValue: T): Promise<T> {\n return new Promise((resolve) => {\n // eslint-disable-next-line no-undef\n setTimeout(() => resolve(defaultValue), ms)\n })\n }\n\n async getBranchSuggestions(): Promise<string[]> {\n // Retrieve worktree branches for dynamic completion\n // Used by cleanup command autocomplete\n try {\n const manager = new GitWorktreeManager()\n const worktrees = await manager.listWorktrees({ porcelain: true })\n const repoInfo = await manager.getRepoInfo()\n\n // Filter out:\n // 1. Main worktree (at repo root) - can't be cleaned up\n // 2. Current worktree (where we're working) - shouldn't clean up current location\n const repoRoot = repoInfo.root\n const currentBranch = repoInfo.currentBranch\n\n return worktrees\n .filter((wt) => wt.path !== repoRoot) // Not the main worktree\n .filter((wt) => wt.branch !== currentBranch) // Not current worktree\n .map((wt) => wt.branch)\n } catch (error) {\n // Silently fail - autocomplete should never break the CLI\n logger.debug(`Failed to get branch suggestions: ${error}`)\n return []\n }\n }\n\n /**\n * Initialize completion - must be called before program.parseAsync()\n */\n init(): void {\n this.completion.init()\n }\n\n /**\n * Detect user's current shell\n */\n detectShell(): ShellType {\n const shell = process.env.SHELL ?? ''\n\n if (shell.includes('bash')) return 'bash'\n if (shell.includes('zsh')) return 'zsh'\n if (shell.includes('fish')) return 'fish'\n\n return 'unknown'\n }\n\n /**\n * Get completion script for a specific shell\n */\n getCompletionScript(shell: ShellType): string {\n switch (shell) {\n case 'bash':\n return this.completion.setupShellInitFile('bash')\n case 'zsh':\n return this.completion.setupShellInitFile('zsh')\n case 'fish':\n return this.completion.setupShellInitFile('fish')\n default:\n throw new Error(`Unsupported shell type: ${shell}`)\n }\n }\n\n /**\n * Get setup instructions for manual installation\n */\n getSetupInstructions(shell: ShellType): string {\n const binaryName = this.commandName\n\n switch (shell) {\n case 'bash':\n return `\nAdd the following to your ~/.bashrc or ~/.bash_profile:\n\n eval \"$(${binaryName} --completion)\"\n\nThen reload your shell:\n\n source ~/.bashrc\n`\n case 'zsh':\n return `\nAdd the following to your ~/.zshrc:\n\n eval \"$(${binaryName} --completion)\"\n\nThen reload your shell:\n\n source ~/.zshrc\n`\n case 'fish':\n return `\nAdd the following to your ~/.config/fish/config.fish:\n\n ${binaryName} --completion | source\n\nThen reload your shell:\n\n source ~/.config/fish/config.fish\n`\n default:\n return `\nShell autocomplete is supported for bash, zsh, and fish.\nYour current shell (${shell}) may not be supported.\n\nPlease consult your shell's documentation for setting up custom completions.\n`\n }\n }\n\n /**\n * Generate completion script and print to stdout\n * Used by: il --completion\n */\n printCompletionScript(shell?: ShellType): void {\n const detectedShell = shell ?? this.detectShell()\n\n if (detectedShell === 'unknown') {\n logger.error('Could not detect shell type. Please specify --shell bash|zsh|fish')\n process.exit(1)\n }\n\n try {\n const script = this.getCompletionScript(detectedShell)\n // eslint-disable-next-line no-console\n console.log(script)\n } catch (error) {\n logger.error(`Failed to generate completion script: ${error}`)\n process.exit(1)\n }\n }\n\n /**\n * Get the shell configuration file path for the given shell type\n */\n getShellConfigPath(shell: ShellType): string | null {\n const homeDir = os.homedir()\n\n switch (shell) {\n case 'bash': {\n // Prefer .bashrc, fall back to .bash_profile\n const bashrcPath = path.join(homeDir, '.bashrc')\n const bashProfilePath = path.join(homeDir, '.bash_profile')\n\n if (existsSync(bashrcPath)) {\n return bashrcPath\n } else if (existsSync(bashProfilePath)) {\n return bashProfilePath\n }\n // Return .bashrc path even if it doesn't exist (for creation)\n return bashrcPath\n }\n\n case 'zsh':\n return path.join(homeDir, '.zshrc')\n\n case 'fish':\n return path.join(homeDir, '.config', 'fish', 'config.fish')\n\n default:\n return null\n }\n }\n\n /**\n * Read the shell configuration file contents\n */\n async readShellConfig(shell: ShellType): Promise<{ path: string; content: string } | null> {\n const configPath = this.getShellConfigPath(shell)\n\n if (!configPath) {\n return null\n }\n\n try {\n let content = ''\n if (existsSync(configPath)) {\n content = await readFile(configPath, 'utf-8')\n }\n // Return the path and content (empty string if file doesn't exist)\n return {\n path: configPath,\n content\n }\n } catch (error) {\n logger.debug(`Failed to read shell config file ${configPath}: ${error}`)\n return {\n path: configPath,\n content: ''\n }\n }\n }\n\n /**\n * Grep for completion-related content in shell configuration file\n * Returns only lines containing '--completion' with 2 lines of context before and after\n * Properly handles overlapping matches\n */\n async grepCompletionConfig(shell: ShellType): Promise<{ path: string; content: string } | null> {\n const configPath = this.getShellConfigPath(shell)\n\n if (!configPath) {\n return null\n }\n\n try {\n let content = ''\n if (existsSync(configPath)) {\n const fullContent = await readFile(configPath, 'utf-8')\n const lines = fullContent.split(/\\r?\\n/)\n\n // Find all matching line indices\n const matchingIndices: number[] = []\n lines.forEach((line, index) => {\n if (line.includes('--completion')) {\n matchingIndices.push(index)\n }\n })\n\n if (matchingIndices.length === 0) {\n content = ''\n } else {\n // Create ranges with context, handling overlaps\n const ranges: { start: number; end: number }[] = []\n\n matchingIndices.forEach(matchIndex => {\n const start = Math.max(0, matchIndex - 2)\n const end = Math.min(lines.length - 1, matchIndex + 2)\n ranges.push({ start, end })\n })\n\n // Merge overlapping ranges\n const mergedRanges = this.mergeOverlappingRanges(ranges)\n\n // Extract lines for each merged range\n const resultSections = mergedRanges.map(range =>\n lines.slice(range.start, range.end + 1).join('\\n')\n )\n\n content = resultSections.join('\\n--\\n')\n }\n }\n\n return {\n path: configPath,\n content\n }\n } catch (error) {\n logger.debug(`Failed to grep shell config file ${configPath}: ${error}`)\n return {\n path: configPath,\n content: ''\n }\n }\n }\n\n /**\n * Merge overlapping ranges to avoid duplicate lines\n */\n private mergeOverlappingRanges(ranges: { start: number; end: number }[]): { start: number; end: number }[] {\n if (ranges.length === 0) return []\n\n // Sort ranges by start position\n const sorted = [...ranges].sort((a, b) => a.start - b.start)\n const firstRange = sorted[0]\n if (!firstRange) return []\n\n const merged: { start: number; end: number }[] = [firstRange]\n\n for (let i = 1; i < sorted.length; i++) {\n const current = sorted[i]\n const last = merged[merged.length - 1]\n\n // Both current and last should exist, but TypeScript needs explicit checks\n if (!current || !last) continue\n\n // If current range overlaps or is adjacent to the last merged range\n if (current.start <= last.end + 1) {\n // Merge ranges by extending the end\n last.end = Math.max(last.end, current.end)\n } else {\n // No overlap, add as new range\n merged.push(current)\n }\n }\n\n return merged\n }\n\n}\n","import { logger } from '../utils/logger.js'\nimport { ShellCompletion } from '../lib/ShellCompletion.js'\nimport chalk from 'chalk'\nimport { mkdir, writeFile, readFile } from 'fs/promises'\nimport { existsSync } from 'fs'\nimport path from 'path'\nimport os from 'os'\nimport { detectClaudeCli, launchClaude } from '../utils/claude.js'\nimport { PromptTemplateManager } from '../lib/PromptTemplateManager.js'\nimport { AgentManager } from '../lib/AgentManager.js'\nimport { fileURLToPath } from 'url'\nimport { GitRemote, parseGitRemotes } from '../utils/remote.js'\nimport { SettingsMigrationManager } from '../lib/SettingsMigrationManager.js'\nimport { isFileGitignored, getRepoRoot } from '../utils/git.js'\nimport { FirstRunManager } from '../utils/FirstRunManager.js'\n\n/**\n * Initialize iloom configuration\n * Implements the `il init` command requested in issue #94\n */\nexport class InitCommand {\n private readonly shellCompletion: ShellCompletion\n private readonly templateManager: PromptTemplateManager\n private readonly agentManager: AgentManager\n\n constructor(\n shellCompletion?: ShellCompletion,\n templateManager?: PromptTemplateManager,\n agentManager?: AgentManager\n ) {\n this.shellCompletion = shellCompletion ?? new ShellCompletion()\n this.templateManager = templateManager ?? new PromptTemplateManager()\n this.agentManager = agentManager ?? new AgentManager()\n }\n\n /**\n * Main entry point for the init command\n * @param customInitialMessage Optional custom initial message to send to Claude (defaults to \"Help me configure iloom settings.\")\n */\n public async execute(customInitialMessage?: string): Promise<void> {\n try {\n logger.debug('InitCommand.execute() starting', {\n cwd: process.cwd(),\n nodeVersion: process.version,\n hasCustomInitialMessage: !!customInitialMessage\n })\n\n logger.info(chalk.bold('Welcome to iloom setup'))\n\n // Setup project configuration\n logger.info(chalk.bold('Verifying current setup...'))\n\n await this.setupProjectConfiguration()\n\n // Launch guided Claude configuration if available\n const guidedInitSucceeded = await this.launchGuidedInit(customInitialMessage)\n\n // Only mark project as configured if guided init succeeded and not already marked\n // This enables VSCode extension detection and ensures project appears in `il projects` list\n if (guidedInitSucceeded) {\n const projectRoot = await getRepoRoot() ?? process.cwd()\n const firstRunManager = new FirstRunManager()\n const alreadyConfigured = await firstRunManager.isProjectConfigured(projectRoot)\n if (!alreadyConfigured) {\n await firstRunManager.markProjectAsConfigured(projectRoot)\n logger.debug('Project marked as configured', { projectRoot })\n } else {\n logger.debug('Project already marked as configured, skipping', { projectRoot })\n }\n } else {\n logger.debug('Skipping project marker - guided init did not complete successfully')\n }\n\n logger.info(chalk.green('Setup complete! Enjoy using iloom CLI.'))\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error'\n logger.error(`Initialization failed: ${message}`)\n throw error\n }\n }\n\n /**\n * Setup project configuration files\n * Creates settings.local.json and updates .gitignore\n */\n private async setupProjectConfiguration(): Promise<void> {\n logger.debug('setupProjectConfiguration() starting')\n\n // Migrate legacy .hatchbox settings to .iloom (BEFORE creating new files)\n try {\n logger.debug('Loading SettingsMigrationManager for legacy migration')\n const migrationManager = new SettingsMigrationManager()\n logger.debug('Running settings migration check')\n await migrationManager.migrateSettingsIfNeeded()\n logger.debug('Settings migration check completed')\n } catch (error) {\n // Log warning but don't fail\n logger.warn(`Settings migration failed: ${error instanceof Error ? error.message : 'Unknown'}`)\n logger.debug('Settings migration error details', { error })\n }\n\n // Update .gitignore\n logger.debug('Starting .gitignore update')\n await this.updateGitignore()\n logger.debug('setupProjectConfiguration() completed')\n\n // Ensure .iloom directory exists\n const iloomDir = path.join(process.cwd(), '.iloom')\n logger.debug('Creating .iloom directory', { iloomDir })\n await mkdir(iloomDir, { recursive: true })\n logger.debug('.iloom directory created/verified')\n\n // // Create settings.local.json if it doesn't exist\n // const settingsLocalPath = path.join(iloomDir, 'settings.local.json')\n // logger.debug('Checking for existing settings.local.json', { settingsLocalPath })\n\n // if (!existsSync(settingsLocalPath)) {\n // logger.debug('Creating settings.local.json file')\n // await writeFile(settingsLocalPath, '{}\\n', 'utf-8')\n // logger.info('Created .iloom/settings.local.json')\n // logger.debug('settings.local.json file created successfully')\n // } else {\n // logger.debug('settings.local.json file already exists, skipping')\n // }\n }\n\n /**\n * Add settings.local.json to .gitignore if not already present\n */\n private async updateGitignore(): Promise<void> {\n const gitignorePath = path.join(process.cwd(), '.gitignore')\n const entryToAdd = '.iloom/settings.local.json'\n\n logger.debug('updateGitignore() starting', {\n gitignorePath,\n entryToAdd\n })\n\n // Read existing .gitignore or create empty\n let content = ''\n if (existsSync(gitignorePath)) {\n logger.debug('.gitignore file exists, reading content')\n content = await readFile(gitignorePath, 'utf-8')\n logger.debug('Read .gitignore content', {\n contentLength: content.length,\n lineCount: content.split('\\n').length\n })\n } else {\n logger.debug('.gitignore file does not exist, will create new one')\n }\n\n // Check if entry already exists\n const lines = content.split('\\n')\n const entryExists = lines.some(line => line.trim() === entryToAdd)\n logger.debug('Checking if entry already exists', {\n entryExists,\n totalLines: lines.length\n })\n\n if (entryExists) {\n logger.debug('Entry already exists, skipping .gitignore update')\n return\n }\n\n // Add entry with comment\n const commentLine = '\\n# Added by iloom CLI'\n const separator = content.endsWith('\\n') || content === '' ? '' : '\\n'\n const newContent = content + separator + commentLine + '\\n' + entryToAdd + '\\n'\n\n logger.debug('Writing updated .gitignore', {\n originalLength: content.length,\n newLength: newContent.length,\n addedLines: 3 // comment + entry + newline\n })\n\n await writeFile(gitignorePath, newContent, 'utf-8')\n logger.info('Added .iloom/settings.local.json to .gitignore')\n logger.debug('.gitignore update completed successfully')\n }\n\n /**\n * Launch interactive Claude-guided configuration\n * @param customInitialMessage Optional custom initial message to send to Claude\n * @returns true if Claude session completed successfully, false otherwise\n */\n private async launchGuidedInit(customInitialMessage?: string): Promise<boolean> {\n logger.debug('launchGuidedInit() starting', { hasCustomInitialMessage: !!customInitialMessage })\n logger.info(chalk.bold('Starting interactive Claude-guided configuration...'))\n\n // Check if Claude CLI is available\n logger.debug('Checking Claude CLI availability')\n const claudeAvailable = await detectClaudeCli()\n logger.debug('Claude CLI availability check result', { claudeAvailable })\n\n if (!claudeAvailable) {\n logger.warn('Claude Code not detected. Skipping guided configuration.')\n logger.info('iloom won\\'t be able to help you much without Claude Code, so please install it: npm install -g @anthropic-ai/claude-code')\n logger.debug('Exiting launchGuidedInit() due to missing Claude CLI')\n return false\n }\n\n try {\n // Load schema from dist/schema/settings.schema.json\n // Use similar approach to PromptTemplateManager for path resolution\n const __filename = fileURLToPath(import.meta.url)\n const __dirname = path.dirname(__filename)\n\n // Walk up to find the schema directory (in case of chunked files)\n let schemaPath = path.join(__dirname, 'schema', 'settings.schema.json')\n\n logger.debug('Loading settings schema', {\n __filename,\n __dirname,\n schemaPath,\n schemaExists: existsSync(schemaPath)\n })\n\n let schemaContent = ''\n if (existsSync(schemaPath)) {\n logger.debug('Reading schema file')\n schemaContent = await readFile(schemaPath, 'utf-8')\n logger.debug('Schema file loaded', {\n contentLength: schemaContent.length,\n isValidJson: ((): boolean => {\n try {\n JSON.parse(schemaContent)\n return true\n } catch {\n return false\n }\n })()\n })\n } else {\n logger.warn('Schema file not found - Claude will work without schema validation')\n logger.debug('Schema file not found at expected path', { schemaPath })\n }\n\n // Check for existing settings - read ALL three files if they exist (global, project, local)\n const settingsGlobalPath = path.join(os.homedir(), '.config', 'iloom-ai', 'settings.json')\n const settingsLocalPath = path.join(process.cwd(), '.iloom', 'settings.local.json')\n const settingsCommittedPath = path.join(process.cwd(), '.iloom', 'settings.json')\n\n let settingsGlobalJson = ''\n let settingsJson = ''\n let settingsLocalJson = ''\n\n logger.debug('Checking for settings files', {\n settingsGlobalPath,\n settingsLocalPath,\n settingsCommittedPath,\n globalExists: existsSync(settingsGlobalPath),\n localExists: existsSync(settingsLocalPath),\n committedExists: existsSync(settingsCommittedPath)\n })\n\n // Read global settings.json if it exists\n if (existsSync(settingsGlobalPath)) {\n logger.debug('Reading global settings.json')\n const content = await readFile(settingsGlobalPath, 'utf-8')\n const trimmed = content.trim()\n if (trimmed !== '{}' && trimmed !== '') {\n settingsGlobalJson = content\n logger.debug('global settings.json loaded', {\n contentLength: content.length,\n isValidJson: ((): boolean => {\n try {\n JSON.parse(content)\n return true\n } catch {\n return false\n }\n })()\n })\n } else {\n logger.debug('global settings.json is empty, skipping')\n }\n } else {\n logger.debug('global settings.json does not exist')\n }\n\n // Read settings.json if it exists\n if (existsSync(settingsCommittedPath)) {\n logger.debug('Reading settings.json')\n const content = await readFile(settingsCommittedPath, 'utf-8')\n const trimmed = content.trim()\n if (trimmed !== '{}' && trimmed !== '') {\n settingsJson = content\n logger.debug('settings.json loaded', {\n contentLength: content.length,\n isValidJson: ((): boolean => {\n try {\n JSON.parse(content)\n return true\n } catch {\n return false\n }\n })()\n })\n } else {\n logger.debug('settings.json is empty, skipping')\n }\n } else {\n logger.debug('settings.json does not exist')\n }\n\n // Read settings.local.json if it exists\n if (existsSync(settingsLocalPath)) {\n logger.debug('Reading settings.local.json')\n const content = await readFile(settingsLocalPath, 'utf-8')\n const trimmed = content.trim()\n if (trimmed !== '{}' && trimmed !== '') {\n settingsLocalJson = content\n logger.debug('settings.local.json loaded', {\n contentLength: content.length,\n isValidJson: ((): boolean => {\n try {\n JSON.parse(content)\n return true\n } catch {\n return false\n }\n })()\n })\n } else {\n logger.debug('settings.local.json is empty, skipping')\n }\n } else {\n logger.debug('settings.local.json does not exist')\n }\n\n // Log summary\n logger.debug('Settings files summary', {\n hasSettingsGlobalJson: !!settingsGlobalJson,\n hasSettingsJson: !!settingsJson,\n hasSettingsLocalJson: !!settingsLocalJson,\n settingsGlobalJsonLength: settingsGlobalJson.length,\n settingsJsonLength: settingsJson.length,\n settingsLocalJsonLength: settingsLocalJson.length\n })\n\n // Detect shell and read config\n logger.debug('Detecting user shell')\n const shell = this.shellCompletion.detectShell()\n logger.debug('Shell detection result', { shell })\n\n let shellConfigPath = ''\n let shellConfigContent = ''\n\n if (shell !== 'unknown') {\n logger.debug('Grepping shell config for completion setup')\n const shellConfig = await this.shellCompletion.grepCompletionConfig(shell)\n if (shellConfig) {\n shellConfigPath = shellConfig.path\n shellConfigContent = shellConfig.content\n logger.debug('Shell config completion grep completed', {\n path: shellConfigPath,\n contentLength: shellConfigContent.length,\n configExists: existsSync(shellConfigPath),\n hasMatches: shellConfigContent.trim().length > 0\n })\n } else {\n logger.debug('Could not read shell config')\n }\n } else {\n logger.debug('Unknown shell detected, skipping config read')\n }\n\n let remotes: GitRemote[] = []\n try {\n // Detect git remotes for GitHub configuration\n logger.debug('Detecting git remotes for GitHub configuration')\n remotes = await parseGitRemotes()\n logger.debug('Git remotes detected', { count: remotes.length, remotes })\n } catch (error) {\n const message = error instanceof Error ? error.stack : 'Unknown error'\n logger.debug(\"Error occured while getting remote info: \", message)\n }\n\n // Detect if .vscode/settings.json is gitignored\n let vscodeSettingsGitignored = false\n try {\n vscodeSettingsGitignored = await isFileGitignored('.vscode/settings.json')\n logger.debug('VSCode settings gitignore status', { vscodeSettingsGitignored })\n } catch (error) {\n logger.debug('Could not detect gitignore status for .vscode/settings.json', { error })\n }\n\n let remotesInfo = ''\n let multipleRemotes = false\n let singleRemote = false\n let singleRemoteName = ''\n let singleRemoteUrl = ''\n let noRemotes = false\n\n if (remotes.length === 0) {\n noRemotes = true\n remotesInfo = 'No git remotes detected in this repository.'\n } else if (remotes.length === 1 && remotes[0]) {\n singleRemote = true\n singleRemoteName = remotes[0].name\n singleRemoteUrl = remotes[0].url\n remotesInfo = `Detected Remote:\\n- **${remotes[0].name}**: ${remotes[0].url} (${remotes[0].owner}/${remotes[0].repo})`\n } else {\n multipleRemotes = true\n remotesInfo = `Detected Remotes (${remotes.length}):\\n` +\n remotes.map(r => `- **${r.name}**: ${r.url} (${r.owner}/${r.repo})`).join('\\n')\n }\n\n // Load README content for comprehensive documentation\n logger.debug('README content loading...')\n const readmeContent = await this.loadReadmeContent()\n logger.debug('README content loaded', {\n readmeContentLength: readmeContent.length,\n })\n\n // Detect if project has package.json for multi-language support\n const packageJsonPath = path.join(process.cwd(), 'package.json')\n const hasPackageJson = existsSync(packageJsonPath)\n logger.debug('Package.json detection', { packageJsonPath, hasPackageJson })\n\n // Build template variables\n const variables = {\n SETTINGS_SCHEMA: schemaContent,\n SETTINGS_GLOBAL_JSON: settingsGlobalJson,\n SETTINGS_JSON: settingsJson,\n SETTINGS_LOCAL_JSON: settingsLocalJson,\n SHELL_TYPE: shell,\n SHELL_CONFIG_PATH: shellConfigPath,\n SHELL_CONFIG_CONTENT: shellConfigContent,\n REMOTES_INFO: remotesInfo,\n MULTIPLE_REMOTES: multipleRemotes.toString(),\n SINGLE_REMOTE: singleRemote.toString(),\n SINGLE_REMOTE_NAME: singleRemoteName,\n SINGLE_REMOTE_URL: singleRemoteUrl,\n NO_REMOTES: noRemotes.toString(),\n README_CONTENT: readmeContent,\n VSCODE_SETTINGS_GITIGNORED: vscodeSettingsGitignored.toString(),\n // Multi-language support - mutually exclusive booleans\n HAS_PACKAGE_JSON: hasPackageJson,\n NO_PACKAGE_JSON: !hasPackageJson,\n }\n\n logger.debug('Building template variables', {\n variableKeys: Object.keys(variables),\n schemaContentLength: schemaContent.length,\n settingsGlobalJsonLength: settingsGlobalJson.length,\n settingsJsonLength: settingsJson.length,\n settingsLocalJsonLength: settingsLocalJson.length\n })\n\n // Get init prompt\n logger.debug('Loading init prompt template')\n const prompt = await this.templateManager.getPrompt('init', variables)\n\n logger.debug('Init prompt loaded', {\n promptLength: prompt.length,\n containsSchema: prompt.includes('SETTINGS_SCHEMA'),\n containsExistingSettings: prompt.includes('EXISTING_SETTINGS')\n })\n\n // Load framework-detector agent for non-Node.js project setup\n let agents: Record<string, unknown> | undefined\n try {\n const loadedAgents = await this.agentManager.loadAgents(\n undefined, // No settings overrides for init\n variables,\n ['iloom-framework-detector.md']\n )\n agents = this.agentManager.formatForCli(loadedAgents)\n logger.debug('Loaded framework-detector agent for init', {\n agentCount: Object.keys(agents).length,\n agentNames: Object.keys(agents),\n })\n } catch (error) {\n // Log warning but continue without agents\n logger.warn(`Failed to load agents: ${error instanceof Error ? error.message : 'Unknown error'}`)\n }\n\n const claudeOptions = {\n model: 'opus',\n headless: false,\n appendSystemPrompt: prompt,\n addDir: process.cwd(),\n ...(agents && { agents }),\n }\n\n logger.debug('Launching Claude with options', {\n optionKeys: Object.keys(claudeOptions),\n headless: claudeOptions.headless,\n hasSystemPrompt: !!claudeOptions.appendSystemPrompt,\n addDir: claudeOptions.addDir,\n promptLength: prompt.length,\n hasCustomInitialMessage: !!customInitialMessage,\n hasAgents: !!agents,\n })\n\n // Launch Claude in interactive mode with custom initial message if provided\n const initialMessage = customInitialMessage ?? 'Help me configure iloom settings.'\n await launchClaude(initialMessage, claudeOptions)\n logger.debug('Claude session completed successfully')\n return true\n\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error'\n logger.warn(`Guided configuration failed: ${message}`)\n logger.debug('launchGuidedInit() error details', error instanceof Error ? error.stack : {error})\n logger.info('You can manually edit .iloom/settings.json to configure iloom.')\n return false\n }\n }\n\n /**\n * Load README.md content for init prompt\n * Walks up from dist directory to find README.md in project root\n */\n private async loadReadmeContent(): Promise<string> {\n try {\n // Walk up from current file location to find README.md\n // Use same pattern as PromptTemplateManager for finding files\n let currentDir = path.dirname(fileURLToPath(import.meta.url))\n\n // Walk up to find README.md\n while (currentDir !== path.dirname(currentDir)) {\n const readmePath = path.join(currentDir, 'README.md')\n try {\n const content = await readFile(readmePath, 'utf-8')\n logger.debug('Loaded README.md for init prompt', { readmePath })\n return content\n } catch {\n currentDir = path.dirname(currentDir)\n }\n }\n\n logger.debug('README.md not found, returning empty string')\n return ''\n } catch (error) {\n // Graceful degradation - return empty string on error\n logger.debug(`Failed to load README.md: ${error}`)\n return ''\n }\n }\n\n}\n"],"mappings":"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","names":["readFile","existsSync","path","os","path","existsSync","readFile","os"]}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
extractIssueNumber,
|
|
4
4
|
extractPRNumber
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-GEXP5IOF.js";
|
|
6
6
|
|
|
7
7
|
// src/utils/IdentifierParser.ts
|
|
8
8
|
var IdentifierParser = class {
|
|
@@ -85,4 +85,4 @@ var IdentifierParser = class {
|
|
|
85
85
|
export {
|
|
86
86
|
IdentifierParser
|
|
87
87
|
};
|
|
88
|
-
//# sourceMappingURL=chunk-
|
|
88
|
+
//# sourceMappingURL=chunk-UQIXZ3BA.js.map
|
|
@@ -222,6 +222,18 @@ var PromptTemplateManager = class {
|
|
|
222
222
|
} else {
|
|
223
223
|
result = result.replace(standardIssueModeRegex, "");
|
|
224
224
|
}
|
|
225
|
+
const hasPackageJsonRegex = /\{\{#IF HAS_PACKAGE_JSON\}\}(.*?)\{\{\/IF HAS_PACKAGE_JSON\}\}/gs;
|
|
226
|
+
if (variables.HAS_PACKAGE_JSON === true) {
|
|
227
|
+
result = result.replace(hasPackageJsonRegex, "$1");
|
|
228
|
+
} else {
|
|
229
|
+
result = result.replace(hasPackageJsonRegex, "");
|
|
230
|
+
}
|
|
231
|
+
const noPackageJsonRegex = /\{\{#IF NO_PACKAGE_JSON\}\}(.*?)\{\{\/IF NO_PACKAGE_JSON\}\}/gs;
|
|
232
|
+
if (variables.NO_PACKAGE_JSON === true) {
|
|
233
|
+
result = result.replace(noPackageJsonRegex, "$1");
|
|
234
|
+
} else {
|
|
235
|
+
result = result.replace(noPackageJsonRegex, "");
|
|
236
|
+
}
|
|
225
237
|
return result;
|
|
226
238
|
}
|
|
227
239
|
/**
|
|
@@ -236,4 +248,4 @@ var PromptTemplateManager = class {
|
|
|
236
248
|
export {
|
|
237
249
|
PromptTemplateManager
|
|
238
250
|
};
|
|
239
|
-
//# sourceMappingURL=chunk-
|
|
251
|
+
//# sourceMappingURL=chunk-W6WVRHJ6.js.map
|