project-iris 0.0.12 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +214 -323
- package/bin/cli.js +21 -0
- package/flows/aidlc/README.md +372 -0
- package/flows/aidlc/agents/construction-agent.md +79 -0
- package/flows/aidlc/agents/inception-agent.md +97 -0
- package/flows/aidlc/agents/master-agent.md +61 -0
- package/flows/aidlc/agents/operations-agent.md +89 -0
- package/flows/aidlc/commands/construction-agent.md +63 -0
- package/flows/aidlc/commands/inception-agent.md +55 -0
- package/flows/aidlc/commands/master-agent.md +47 -0
- package/flows/aidlc/commands/operations-agent.md +77 -0
- package/flows/aidlc/context-config.yaml +67 -0
- package/flows/aidlc/memory-bank.yaml +104 -0
- package/flows/aidlc/quick-start.md +322 -0
- package/flows/aidlc/skills/construction/bolt-list.md +163 -0
- package/flows/aidlc/skills/construction/bolt-replan.md +345 -0
- package/flows/aidlc/skills/construction/bolt-start.md +442 -0
- package/flows/aidlc/skills/construction/bolt-status.md +185 -0
- package/flows/aidlc/skills/construction/navigator.md +196 -0
- package/flows/aidlc/skills/inception/bolt-plan.md +372 -0
- package/flows/aidlc/skills/inception/context.md +171 -0
- package/flows/aidlc/skills/inception/intent-create.md +211 -0
- package/flows/aidlc/skills/inception/intent-list.md +124 -0
- package/flows/aidlc/skills/inception/navigator.md +207 -0
- package/flows/aidlc/skills/inception/requirements.md +227 -0
- package/flows/aidlc/skills/inception/review.md +248 -0
- package/flows/aidlc/skills/inception/story-create.md +304 -0
- package/flows/aidlc/skills/inception/units.md +278 -0
- package/flows/aidlc/skills/master/analyze-context.md +239 -0
- package/flows/aidlc/skills/master/answer-question.md +141 -0
- package/flows/aidlc/skills/master/explain-flow.md +158 -0
- package/flows/aidlc/skills/master/project-init.md +281 -0
- package/flows/aidlc/skills/master/route-request.md +126 -0
- package/flows/aidlc/skills/operations/build.md +237 -0
- package/flows/aidlc/skills/operations/deploy.md +259 -0
- package/flows/aidlc/skills/operations/monitor.md +265 -0
- package/flows/aidlc/skills/operations/navigator.md +209 -0
- package/flows/aidlc/skills/operations/verify.md +224 -0
- package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt.md +3 -3
- package/{dist → flows/aidlc}/templates/construction/bolt-types/spike-bolt.md +2 -2
- package/flows/aidlc/templates/construction/construction-log-template.md +129 -0
- package/flows/aidlc/templates/construction/standards/coding-standards.md +29 -0
- package/flows/aidlc/templates/construction/standards/system-architecture.md +22 -0
- package/flows/aidlc/templates/construction/standards/tech-stack.md +19 -0
- package/flows/aidlc/templates/inception/inception-log-template.md +134 -0
- package/flows/aidlc/templates/inception/project/README.md +55 -0
- package/flows/aidlc/templates/standards/catalog.yaml +345 -0
- package/flows/aidlc/templates/standards/coding-standards.guide.md +553 -0
- package/flows/aidlc/templates/standards/data-stack.guide.md +162 -0
- package/flows/aidlc/templates/standards/tech-stack.guide.md +280 -0
- package/lib/InstallerFactory.js +36 -0
- package/lib/analytics/env-detector.js +92 -0
- package/lib/analytics/index.js +22 -0
- package/lib/analytics/machine-id.js +33 -0
- package/lib/analytics/tracker.js +232 -0
- package/lib/cli-utils.js +342 -0
- package/lib/constants.js +32 -0
- package/lib/installer.js +402 -0
- package/lib/installers/AntigravityInstaller.js +22 -0
- package/lib/installers/ClaudeInstaller.js +85 -0
- package/lib/installers/ClineInstaller.js +21 -0
- package/lib/installers/CodexInstaller.js +21 -0
- package/lib/installers/CopilotInstaller.js +113 -0
- package/lib/installers/CursorInstaller.js +63 -0
- package/lib/installers/GeminiInstaller.js +75 -0
- package/lib/installers/KiroInstaller.js +22 -0
- package/lib/installers/OpenCodeInstaller.js +22 -0
- package/lib/installers/RooInstaller.js +22 -0
- package/lib/installers/ToolInstaller.js +73 -0
- package/lib/installers/WindsurfInstaller.js +22 -0
- package/lib/markdown-validator.ts +175 -0
- package/lib/yaml-validator.ts +99 -0
- package/package.json +105 -32
- package/scripts/artifact-validator.js +594 -0
- package/scripts/bolt-complete.js +606 -0
- package/scripts/status-integrity.js +598 -0
- package/dist/bridge/agent-runner.js +0 -190
- package/dist/bridge/connector-factory.js +0 -31
- package/dist/bridge/connectors/antigravity-connector.js +0 -18
- package/dist/bridge/connectors/cursor-connector.js +0 -31
- package/dist/bridge/connectors/in-process-connector.js +0 -29
- package/dist/bridge/connectors/vscode-connector.js +0 -31
- package/dist/bridge/connectors/windsurf-connector.js +0 -23
- package/dist/bridge/filesystem-connector.js +0 -110
- package/dist/bridge/helper.js +0 -203
- package/dist/bridge/types.js +0 -10
- package/dist/cli.js +0 -40
- package/dist/commands/ask.js +0 -259
- package/dist/commands/bridge.js +0 -88
- package/dist/commands/create.js +0 -25
- package/dist/commands/develop.js +0 -141
- package/dist/commands/doctor.js +0 -102
- package/dist/commands/flow.js +0 -301
- package/dist/commands/framework.js +0 -273
- package/dist/commands/generate.js +0 -59
- package/dist/commands/install.js +0 -100
- package/dist/commands/pack.js +0 -33
- package/dist/commands/phase.js +0 -38
- package/dist/commands/run.js +0 -199
- package/dist/commands/status.js +0 -114
- package/dist/commands/uninstall.js +0 -14
- package/dist/commands/use.js +0 -20
- package/dist/commands/validate.js +0 -102
- package/dist/framework/framework-loader.js +0 -97
- package/dist/framework/framework-paths.js +0 -48
- package/dist/framework/framework-types.js +0 -15
- package/dist/iris/artifact-checker.js +0 -78
- package/dist/iris/artifacts/config.js +0 -68
- package/dist/iris/artifacts/generator.js +0 -88
- package/dist/iris/artifacts/types.js +0 -1
- package/dist/iris/bundle.js +0 -44
- package/dist/iris/doctrine/collector.js +0 -124
- package/dist/iris/fixer.js +0 -149
- package/dist/iris/flows/manifest.js +0 -124
- package/dist/iris/framework-context.js +0 -49
- package/dist/iris/framework-manager.js +0 -215
- package/dist/iris/fs/atomic.js +0 -22
- package/dist/iris/guard.js +0 -38
- package/dist/iris/importers/index.js +0 -9
- package/dist/iris/importers/types.js +0 -8
- package/dist/iris/importers/writer.js +0 -139
- package/dist/iris/include.js +0 -49
- package/dist/iris/installer.js +0 -334
- package/dist/iris/interactive/env.js +0 -21
- package/dist/iris/interactive/intent-interview.js +0 -345
- package/dist/iris/interactive/intent-schema.js +0 -28
- package/dist/iris/interactive/interview-io.js +0 -22
- package/dist/iris/interview/config.js +0 -71
- package/dist/iris/interview/types.js +0 -16
- package/dist/iris/interview/utils.js +0 -38
- package/dist/iris/manifest.js +0 -54
- package/dist/iris/packer.js +0 -325
- package/dist/iris/parsers/unit-parser.js +0 -43
- package/dist/iris/paths.js +0 -18
- package/dist/iris/policy.js +0 -133
- package/dist/iris/proc.js +0 -56
- package/dist/iris/report.js +0 -53
- package/dist/iris/resolver.js +0 -66
- package/dist/iris/router.js +0 -114
- package/dist/iris/routes.js +0 -189
- package/dist/iris/run-state.js +0 -146
- package/dist/iris/state.js +0 -113
- package/dist/iris/templates.js +0 -70
- package/dist/iris/tmp.js +0 -24
- package/dist/iris/uninstaller.js +0 -181
- package/dist/iris/utils/interpolate.js +0 -42
- package/dist/iris/validator.js +0 -391
- package/dist/iris/workflow/config.js +0 -51
- package/dist/iris/workflow/engine.js +0 -129
- package/dist/iris/workflow/steps.js +0 -448
- package/dist/iris/workflow/types.js +0 -1
- package/dist/iris_bundle/frameworks/iris-core/framework.yaml +0 -9
- package/dist/iris_bundle/frameworks/iris-core/memory/memory-bank.yaml +0 -1
- package/dist/iris_bundle/frameworks/iris-core/policy.yaml +0 -7
- package/dist/iris_bundle/frameworks/iris-core/templates/config/memory-bank.yaml +0 -1
- package/dist/iris_bundle/frameworks/iris-core/templates/construction/bolt-types/spike-bolt.md +0 -240
- package/dist/lib.js +0 -96
- package/dist/templates/construction/bolt-template.md +0 -226
- package/dist/templates/construction/bolt-types/ddd-construction-bolt/adr-template.md +0 -49
- package/dist/templates/construction/bolt-types/ddd-construction-bolt/ddd-01-domain-model-template.md +0 -55
- package/dist/templates/construction/bolt-types/ddd-construction-bolt/ddd-02-technical-design-template.md +0 -67
- package/dist/templates/construction/bolt-types/ddd-construction-bolt/ddd-03-test-report-template.md +0 -62
- package/dist/templates/construction/bolt-types/ddd-construction-bolt.md +0 -528
- package/dist/templates/construction/bolt-types/simple-construction-bolt.md +0 -347
- package/dist/templates/inception/requirements-template.md +0 -144
- package/dist/templates/inception/stories-template.md +0 -38
- package/dist/templates/inception/story-template.md +0 -147
- package/dist/templates/inception/system-context-template.md +0 -29
- package/dist/templates/inception/unit-brief-template.md +0 -177
- package/dist/templates/inception/units-template.md +0 -52
- package/dist/utils/exit-codes.js +0 -7
- package/dist/utils/logo.js +0 -17
- package/dist/workflows/bolt-execution.js +0 -238
- package/dist/workflows/bolt-plan.js +0 -221
- package/dist/workflows/intent-inception.js +0 -285
- package/dist/workflows/memory-bank-generator.js +0 -180
- package/dist/workflows/reporting.js +0 -74
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt/adr-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt/ddd-01-domain-model-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt/ddd-02-technical-design-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt/ddd-03-test-report-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/simple-construction-bolt.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/requirements-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/stories-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/story-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/system-context-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/unit-brief-template.md +0 -0
- /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/units-template.md +0 -0
package/lib/installer.js
ADDED
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const prompts = require('prompts');
|
|
4
|
+
const yaml = require('js-yaml');
|
|
5
|
+
const CLIUtils = require('./cli-utils');
|
|
6
|
+
const InstallerFactory = require('./InstallerFactory');
|
|
7
|
+
const { FLOWS } = require('./constants');
|
|
8
|
+
const analytics = require('./analytics');
|
|
9
|
+
|
|
10
|
+
// Use theme from CLIUtils for consistent styling
|
|
11
|
+
const { theme } = CLIUtils;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Categorize an error for analytics tracking
|
|
15
|
+
* @param {Error} error - The error to categorize
|
|
16
|
+
* @returns {string} Error category
|
|
17
|
+
*/
|
|
18
|
+
function categorizeError(error) {
|
|
19
|
+
const message = (error.message || '').toLowerCase();
|
|
20
|
+
|
|
21
|
+
if (message.includes('permission') || message.includes('eacces')) {
|
|
22
|
+
return 'file_permission';
|
|
23
|
+
}
|
|
24
|
+
if (message.includes('enoent') || message.includes('not found')) {
|
|
25
|
+
return 'file_not_found';
|
|
26
|
+
}
|
|
27
|
+
if (message.includes('network') || message.includes('enotfound') || message.includes('timeout')) {
|
|
28
|
+
return 'network';
|
|
29
|
+
}
|
|
30
|
+
if (message.includes('enospc') || message.includes('disk')) {
|
|
31
|
+
return 'disk_space';
|
|
32
|
+
}
|
|
33
|
+
return 'unknown';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Count files in a directory recursively
|
|
38
|
+
* @param {string} dir - Directory path
|
|
39
|
+
* @returns {Promise<number>} File count
|
|
40
|
+
*/
|
|
41
|
+
async function countFiles(dir) {
|
|
42
|
+
let count = 0;
|
|
43
|
+
try {
|
|
44
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
45
|
+
for (const entry of entries) {
|
|
46
|
+
if (entry.isDirectory()) {
|
|
47
|
+
count += await countFiles(path.join(dir, entry.name));
|
|
48
|
+
} else {
|
|
49
|
+
count++;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
} catch {
|
|
53
|
+
// Ignore errors (directory might not exist)
|
|
54
|
+
}
|
|
55
|
+
return count;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function detectTools() {
|
|
59
|
+
const detected = [];
|
|
60
|
+
const installers = InstallerFactory.getInstallers();
|
|
61
|
+
|
|
62
|
+
for (const installer of installers) {
|
|
63
|
+
if (await installer.detect()) {
|
|
64
|
+
detected.push(installer.key);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return detected;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function install() {
|
|
71
|
+
// Initialize analytics (respects opt-out env vars)
|
|
72
|
+
analytics.init();
|
|
73
|
+
await analytics.trackInstallerStarted();
|
|
74
|
+
|
|
75
|
+
const installStartTime = Date.now();
|
|
76
|
+
|
|
77
|
+
await CLIUtils.displayLogo();
|
|
78
|
+
CLIUtils.displayHeader('Installation', '');
|
|
79
|
+
|
|
80
|
+
// Step 1: Detect agentic coding tools
|
|
81
|
+
const detectedToolKeys = await detectTools();
|
|
82
|
+
const installers = InstallerFactory.getInstallers();
|
|
83
|
+
const detectedNames = installers
|
|
84
|
+
.filter(i => detectedToolKeys.includes(i.key))
|
|
85
|
+
.map(i => i.name);
|
|
86
|
+
|
|
87
|
+
CLIUtils.displayStep(1, 4, 'Detecting agentic coding tools...');
|
|
88
|
+
if (detectedNames.length > 0) {
|
|
89
|
+
CLIUtils.displayStatus('', `Detected: ${detectedNames.join(', ')}`, 'success');
|
|
90
|
+
} else {
|
|
91
|
+
CLIUtils.displayStatus('', 'No agentic coding tools detected', 'warning');
|
|
92
|
+
}
|
|
93
|
+
console.log('');
|
|
94
|
+
|
|
95
|
+
// Step 2: Select tools
|
|
96
|
+
CLIUtils.displayStep(2, 4, 'Select target tools');
|
|
97
|
+
|
|
98
|
+
// Build choices with descriptive formatting
|
|
99
|
+
const toolChoices = installers.map(installer => ({
|
|
100
|
+
title: installer.name + (detectedToolKeys.includes(installer.key) ? theme.dim(' (detected)') : ''),
|
|
101
|
+
value: installer.key,
|
|
102
|
+
selected: detectedToolKeys.includes(installer.key)
|
|
103
|
+
}));
|
|
104
|
+
|
|
105
|
+
console.log(theme.dim(' [Space] toggle [Enter] confirm [a] toggle all'));
|
|
106
|
+
console.log(theme.dim(` ${theme.success('[x]')} = selected ${theme.dim('[ ]')} = not selected\n`));
|
|
107
|
+
|
|
108
|
+
const { selectedToolKeys } = await prompts({
|
|
109
|
+
type: 'multiselect',
|
|
110
|
+
name: 'selectedToolKeys',
|
|
111
|
+
message: 'Choose tools:',
|
|
112
|
+
choices: toolChoices,
|
|
113
|
+
min: 1,
|
|
114
|
+
instructions: false
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
if (!selectedToolKeys || selectedToolKeys.length === 0) {
|
|
118
|
+
CLIUtils.displayError('Installation cancelled - no tools selected');
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Track IDE selection (await to ensure delivery before potential cancel)
|
|
123
|
+
await analytics.trackIdesConfirmed(selectedToolKeys);
|
|
124
|
+
|
|
125
|
+
// Step 3: Select Flow
|
|
126
|
+
console.log('');
|
|
127
|
+
CLIUtils.displayStep(3, 4, 'Select SDLC flow');
|
|
128
|
+
const flowChoices = Object.entries(FLOWS).map(([key, flow]) => ({
|
|
129
|
+
title: `${flow.name} - ${flow.description}${flow.message || ''}`,
|
|
130
|
+
value: key,
|
|
131
|
+
disabled: flow.disabled
|
|
132
|
+
}));
|
|
133
|
+
|
|
134
|
+
const { selectedFlow } = await prompts({
|
|
135
|
+
type: 'select',
|
|
136
|
+
name: 'selectedFlow',
|
|
137
|
+
message: 'Which SDLC flow would you like to install?',
|
|
138
|
+
choices: flowChoices
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
if (!selectedFlow) {
|
|
142
|
+
CLIUtils.displayError('Installation cancelled');
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Track flow selection (await to ensure delivery before potential cancel)
|
|
147
|
+
await analytics.trackFlowSelected(selectedFlow);
|
|
148
|
+
|
|
149
|
+
// Step 4: Install flow files
|
|
150
|
+
console.log('');
|
|
151
|
+
CLIUtils.displayStep(4, 4, `Installing ${FLOWS[selectedFlow].name} flow...`);
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
const filesCreated = await installFlow(selectedFlow, selectedToolKeys);
|
|
155
|
+
|
|
156
|
+
// Track successful installation for each tool
|
|
157
|
+
const durationMs = Date.now() - installStartTime;
|
|
158
|
+
for (const toolKey of selectedToolKeys) {
|
|
159
|
+
analytics.trackInstallationCompleted(toolKey, selectedFlow, durationMs, filesCreated);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
CLIUtils.displaySuccess(`${FLOWS[selectedFlow].name} flow installed successfully!`, 'Installation Complete');
|
|
163
|
+
|
|
164
|
+
// Get selected tool names for next steps message
|
|
165
|
+
const selectedToolNames = installers
|
|
166
|
+
.filter(i => selectedToolKeys.includes(i.key))
|
|
167
|
+
.map(i => i.name);
|
|
168
|
+
|
|
169
|
+
const nextSteps = [
|
|
170
|
+
`Read .iris/${selectedFlow}/quick-start.md for getting started`,
|
|
171
|
+
`Open ${selectedToolNames.join(' or ')} and run /iris-master-agent`
|
|
172
|
+
];
|
|
173
|
+
CLIUtils.displayNextSteps(nextSteps);
|
|
174
|
+
} catch (error) {
|
|
175
|
+
// Track installation failure
|
|
176
|
+
const errorCategory = categorizeError(error);
|
|
177
|
+
for (const toolKey of selectedToolKeys) {
|
|
178
|
+
analytics.trackInstallationFailed(toolKey, errorCategory, selectedFlow);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
CLIUtils.displayError(`Installation failed: ${error.message}`);
|
|
182
|
+
console.log(theme.dim('\nRolling back changes...'));
|
|
183
|
+
await rollback(selectedFlow, selectedToolKeys);
|
|
184
|
+
CLIUtils.displayStatus('', 'Installation rolled back', 'warning');
|
|
185
|
+
process.exit(1);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async function installFlow(flowKey, toolKeys) {
|
|
190
|
+
const flowPath = path.join(__dirname, '..', 'flows', FLOWS[flowKey].path);
|
|
191
|
+
|
|
192
|
+
// Step 1: Install commands for each tool
|
|
193
|
+
// Pass empty config since config.yaml is removed
|
|
194
|
+
const dummyConfig = {};
|
|
195
|
+
for (const toolKey of toolKeys) {
|
|
196
|
+
const installer = InstallerFactory.getInstaller(toolKey);
|
|
197
|
+
if (installer) {
|
|
198
|
+
await installer.installCommands(flowPath, dummyConfig);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Step 2: Install shared flow config
|
|
203
|
+
const irisDir = '.iris';
|
|
204
|
+
const targetFlowDir = path.join(irisDir, flowKey);
|
|
205
|
+
|
|
206
|
+
console.log(theme.dim(` Installing flow resources to ${targetFlowDir}/...`));
|
|
207
|
+
await fs.ensureDir(targetFlowDir);
|
|
208
|
+
|
|
209
|
+
// Copy agents
|
|
210
|
+
await fs.copy(path.join(flowPath, 'agents'), path.join(targetFlowDir, 'agents'));
|
|
211
|
+
|
|
212
|
+
// Copy internal agent capabilities (legacy check)
|
|
213
|
+
if (await fs.pathExists(path.join(flowPath, 'agent-capabilities'))) {
|
|
214
|
+
await fs.copy(path.join(flowPath, 'agent-capabilities'), path.join(targetFlowDir, 'agent-capabilities'));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Copy bolt-types if they exist (legacy check)
|
|
218
|
+
if (await fs.pathExists(path.join(flowPath, 'bolt-types'))) {
|
|
219
|
+
await fs.copy(path.join(flowPath, 'bolt-types'), path.join(targetFlowDir, 'bolt-types'));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Copy skills, templates, shared (now at flow root level, not nested in .iris)
|
|
223
|
+
if (await fs.pathExists(path.join(flowPath, 'skills'))) {
|
|
224
|
+
await fs.copy(path.join(flowPath, 'skills'), path.join(targetFlowDir, 'skills'));
|
|
225
|
+
}
|
|
226
|
+
if (await fs.pathExists(path.join(flowPath, 'templates'))) {
|
|
227
|
+
await fs.copy(path.join(flowPath, 'templates'), path.join(targetFlowDir, 'templates'));
|
|
228
|
+
}
|
|
229
|
+
if (await fs.pathExists(path.join(flowPath, 'shared'))) {
|
|
230
|
+
await fs.copy(path.join(flowPath, 'shared'), path.join(targetFlowDir, 'shared'));
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Copy config files
|
|
234
|
+
if (await fs.pathExists(path.join(flowPath, 'memory-bank.yaml'))) {
|
|
235
|
+
await fs.copy(path.join(flowPath, 'memory-bank.yaml'), path.join(targetFlowDir, 'memory-bank.yaml'));
|
|
236
|
+
}
|
|
237
|
+
if (await fs.pathExists(path.join(flowPath, 'context-config.yaml'))) {
|
|
238
|
+
await fs.copy(path.join(flowPath, 'context-config.yaml'), path.join(targetFlowDir, 'context-config.yaml'));
|
|
239
|
+
}
|
|
240
|
+
if (await fs.pathExists(path.join(flowPath, 'quick-start.md'))) {
|
|
241
|
+
await fs.copy(path.join(flowPath, 'quick-start.md'), path.join(targetFlowDir, 'quick-start.md'));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Copy docs
|
|
245
|
+
await fs.copy(path.join(flowPath, 'README.md'), path.join(targetFlowDir, 'README.md'));
|
|
246
|
+
|
|
247
|
+
if (await fs.pathExists(path.join(flowPath, 'constitution.md'))) {
|
|
248
|
+
await fs.copy(path.join(flowPath, 'constitution.md'), path.join(targetFlowDir, 'constitution.md'));
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
CLIUtils.displayStatus('', 'Installed flow resources', 'success');
|
|
252
|
+
|
|
253
|
+
// Step 2.5: Install local scripts for deterministic operations
|
|
254
|
+
// These scripts are version-matched to the installed iris version
|
|
255
|
+
const scriptsDir = path.join(irisDir, 'scripts');
|
|
256
|
+
await fs.ensureDir(scriptsDir);
|
|
257
|
+
|
|
258
|
+
const sourceScriptsDir = path.join(__dirname, '..', 'scripts');
|
|
259
|
+
if (await fs.pathExists(sourceScriptsDir)) {
|
|
260
|
+
await fs.copy(sourceScriptsDir, scriptsDir);
|
|
261
|
+
CLIUtils.displayStatus('', 'Installed local scripts', 'success');
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Note: Scripts are invoked directly via relative path (e.g., node .iris/scripts/bolt-complete.js)
|
|
265
|
+
// No npm scripts added to package.json to avoid dependency on package.json for execution
|
|
266
|
+
|
|
267
|
+
// NOTE: memory-bank/ is NOT created during installation
|
|
268
|
+
// It will be created when user runs project-init
|
|
269
|
+
// This allows us to detect if project is initialized by checking for memory-bank/standards/
|
|
270
|
+
|
|
271
|
+
// Step 3: Create manifest
|
|
272
|
+
const manifest = {
|
|
273
|
+
flow: flowKey,
|
|
274
|
+
version: require('../package.json').version,
|
|
275
|
+
installed_at: new Date().toISOString(),
|
|
276
|
+
tools: toolKeys
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
await fs.writeFile(
|
|
280
|
+
path.join(irisDir, 'manifest.yaml'),
|
|
281
|
+
yaml.dump(manifest),
|
|
282
|
+
'utf8'
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
CLIUtils.displayStatus('', 'Created installation manifest', 'success');
|
|
286
|
+
|
|
287
|
+
// Count files created for analytics
|
|
288
|
+
const filesCreated = await countFiles(irisDir);
|
|
289
|
+
return filesCreated;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
async function rollback(flowKey, toolKeys) {
|
|
293
|
+
// Remove tool command files
|
|
294
|
+
for (const toolKey of toolKeys) {
|
|
295
|
+
const installer = InstallerFactory.getInstaller(toolKey);
|
|
296
|
+
if (installer) {
|
|
297
|
+
const commandsDir = installer.commandsDir;
|
|
298
|
+
if (await fs.pathExists(commandsDir)) {
|
|
299
|
+
const files = await fs.readdir(commandsDir);
|
|
300
|
+
for (const file of files) {
|
|
301
|
+
if (file.startsWith('iris-')) {
|
|
302
|
+
await fs.remove(path.join(commandsDir, file));
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Remove .iris directory
|
|
310
|
+
if (await fs.pathExists('.iris')) {
|
|
311
|
+
await fs.remove('.iris');
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
async function uninstall() {
|
|
316
|
+
CLIUtils.displayHeader('Uninstall', '');
|
|
317
|
+
|
|
318
|
+
// Check if iris is installed
|
|
319
|
+
if (!await fs.pathExists('.iris/manifest.yaml')) {
|
|
320
|
+
CLIUtils.displayWarning('iris is not installed in this project');
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Read manifest
|
|
325
|
+
const manifestContent = await fs.readFile('.iris/manifest.yaml', 'utf8');
|
|
326
|
+
const manifest = yaml.load(manifestContent);
|
|
327
|
+
|
|
328
|
+
const installers = InstallerFactory.getInstallers();
|
|
329
|
+
// Support both old 'ides' key and new 'tools' key for backward compatibility
|
|
330
|
+
const installedToolKeys = manifest.tools || manifest.ides || [];
|
|
331
|
+
const installedNames = installers
|
|
332
|
+
.filter(i => installedToolKeys.includes(i.key))
|
|
333
|
+
.map(i => i.name);
|
|
334
|
+
|
|
335
|
+
console.log(theme.dim(`Found installation: ${FLOWS[manifest.flow].name} flow`));
|
|
336
|
+
console.log(theme.dim(`Installed for: ${installedNames.join(', ')}\n`));
|
|
337
|
+
|
|
338
|
+
const { confirm } = await prompts({
|
|
339
|
+
type: 'confirm',
|
|
340
|
+
name: 'confirm',
|
|
341
|
+
message: 'Are you sure you want to uninstall iris?',
|
|
342
|
+
initial: false
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
if (!confirm) {
|
|
346
|
+
CLIUtils.displayStatus('', 'Uninstall cancelled', 'warning');
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Ask about memory bank
|
|
351
|
+
const { keepMemoryBank } = await prompts({
|
|
352
|
+
type: 'confirm',
|
|
353
|
+
name: 'keepMemoryBank',
|
|
354
|
+
message: 'Keep memory-bank folder? (Contains your project artifacts)',
|
|
355
|
+
initial: true
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
console.log(theme.primary('\nUninstalling...\n'));
|
|
359
|
+
|
|
360
|
+
try {
|
|
361
|
+
// Remove command files
|
|
362
|
+
for (const toolKey of installedToolKeys) {
|
|
363
|
+
const installer = InstallerFactory.getInstaller(toolKey);
|
|
364
|
+
if (installer) {
|
|
365
|
+
const commandsDir = installer.commandsDir;
|
|
366
|
+
if (await fs.pathExists(commandsDir)) {
|
|
367
|
+
console.log(theme.dim(` Removing commands from ${commandsDir}/...`));
|
|
368
|
+
const files = await fs.readdir(commandsDir);
|
|
369
|
+
for (const file of files) {
|
|
370
|
+
if (file.startsWith('iris-')) {
|
|
371
|
+
await fs.remove(path.join(commandsDir, file));
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Remove .iris directory
|
|
379
|
+
console.log(theme.dim(' Removing .iris/...'));
|
|
380
|
+
await fs.remove('.iris');
|
|
381
|
+
|
|
382
|
+
// Optionally remove memory-bank
|
|
383
|
+
if (!keepMemoryBank && await fs.pathExists('memory-bank')) {
|
|
384
|
+
console.log(theme.dim(' Removing memory-bank/...'));
|
|
385
|
+
await fs.remove('memory-bank');
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
CLIUtils.displaySuccess('Uninstall complete!', 'Complete');
|
|
389
|
+
if (keepMemoryBank) {
|
|
390
|
+
console.log(theme.dim('memory-bank/ was preserved\n'));
|
|
391
|
+
}
|
|
392
|
+
} catch (error) {
|
|
393
|
+
CLIUtils.displayError(`Uninstall failed: ${error.message}`);
|
|
394
|
+
process.exit(1);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
module.exports = {
|
|
399
|
+
install,
|
|
400
|
+
uninstall
|
|
401
|
+
};
|
|
402
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const ToolInstaller = require('./ToolInstaller');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
class AntigravityInstaller extends ToolInstaller {
|
|
5
|
+
get key() {
|
|
6
|
+
return 'antigravity';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
get name() {
|
|
10
|
+
return 'Google Antigravity';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
get commandsDir() {
|
|
14
|
+
return path.join('.agent', 'workflows');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
get detectPath() {
|
|
18
|
+
return '.agent';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = AntigravityInstaller;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const ToolInstaller = require('./ToolInstaller');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const CLIUtils = require('../cli-utils');
|
|
5
|
+
const { theme } = CLIUtils;
|
|
6
|
+
|
|
7
|
+
class ClaudeInstaller extends ToolInstaller {
|
|
8
|
+
get key() {
|
|
9
|
+
return 'claude';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
get name() {
|
|
13
|
+
return 'Claude Code';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get commandsDir() {
|
|
17
|
+
return path.join('.claude', 'commands');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get agentsDir() {
|
|
21
|
+
return path.join('.claude', 'agents');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get detectPath() {
|
|
25
|
+
return '.claude';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Override to install both commands and agents
|
|
30
|
+
*/
|
|
31
|
+
async installCommands(flowPath, config) {
|
|
32
|
+
// Install commands (default behavior)
|
|
33
|
+
const installedCommands = await super.installCommands(flowPath, config);
|
|
34
|
+
|
|
35
|
+
// Install agents
|
|
36
|
+
const installedAgents = await this.installAgents(flowPath, config);
|
|
37
|
+
|
|
38
|
+
return [...installedCommands, ...installedAgents];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Install agents to .claude/agents/
|
|
43
|
+
* Uses the commands folder as source (same files serve as both commands and agents)
|
|
44
|
+
*/
|
|
45
|
+
async installAgents(flowPath, config) {
|
|
46
|
+
const targetAgentsDir = this.agentsDir;
|
|
47
|
+
console.log(theme.dim(` Installing agents to ${targetAgentsDir}/...`));
|
|
48
|
+
await fs.ensureDir(targetAgentsDir);
|
|
49
|
+
|
|
50
|
+
const commandsSourceDir = path.join(flowPath, 'commands');
|
|
51
|
+
|
|
52
|
+
if (!await fs.pathExists(commandsSourceDir)) {
|
|
53
|
+
console.log(theme.dim(` No commands folder found at ${commandsSourceDir}`));
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const agentFiles = await fs.readdir(commandsSourceDir);
|
|
58
|
+
const installedFiles = [];
|
|
59
|
+
|
|
60
|
+
for (const agentFile of agentFiles) {
|
|
61
|
+
if (agentFile.endsWith('.md')) {
|
|
62
|
+
try {
|
|
63
|
+
const sourcePath = path.join(commandsSourceDir, agentFile);
|
|
64
|
+
const prefix = (config && config.command && config.command.prefix) ? `${config.command.prefix}-` : '';
|
|
65
|
+
const targetFileName = `iris-${prefix}${agentFile}`;
|
|
66
|
+
const targetPath = path.join(targetAgentsDir, targetFileName);
|
|
67
|
+
|
|
68
|
+
const content = await fs.readFile(sourcePath, 'utf8');
|
|
69
|
+
await fs.outputFile(targetPath, content, 'utf8');
|
|
70
|
+
installedFiles.push(targetFileName);
|
|
71
|
+
} catch (err) {
|
|
72
|
+
console.log(theme.warning(` Failed to install agent ${agentFile}: ${err.message}`));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (installedFiles.length > 0) {
|
|
78
|
+
CLIUtils.displayStatus('', `Installed ${installedFiles.length} agents for ${this.name}`, 'success');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return installedFiles;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
module.exports = ClaudeInstaller;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const ToolInstaller = require('./ToolInstaller');
|
|
2
|
+
|
|
3
|
+
class ClineInstaller extends ToolInstaller {
|
|
4
|
+
get key() {
|
|
5
|
+
return 'cline';
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
get name() {
|
|
9
|
+
return 'Cline';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
get commandsDir() {
|
|
13
|
+
return '.clinerules';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get detectPath() {
|
|
17
|
+
return '.clinerules';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = ClineInstaller;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const ToolInstaller = require('./ToolInstaller');
|
|
2
|
+
|
|
3
|
+
class CodexInstaller extends ToolInstaller {
|
|
4
|
+
get key() {
|
|
5
|
+
return 'codex';
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
get name() {
|
|
9
|
+
return 'Codex';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
get commandsDir() {
|
|
13
|
+
return '.codex';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get detectPath() {
|
|
17
|
+
return '.codex';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = CodexInstaller;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
const ToolInstaller = require('./ToolInstaller');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const CLIUtils = require('../cli-utils');
|
|
5
|
+
const { theme } = CLIUtils;
|
|
6
|
+
|
|
7
|
+
class CopilotInstaller extends ToolInstaller {
|
|
8
|
+
get key() {
|
|
9
|
+
return 'copilot';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
get name() {
|
|
13
|
+
return 'GitHub Copilot';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get commandsDir() {
|
|
17
|
+
return path.join('.github', 'prompts');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get agentsDir() {
|
|
21
|
+
return path.join('.github', 'agents');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get detectPath() {
|
|
25
|
+
return '.github';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Override to install both commands and agents
|
|
30
|
+
*/
|
|
31
|
+
async installCommands(flowPath, config) {
|
|
32
|
+
const installedCommands = await this.installCommandFiles(flowPath, config);
|
|
33
|
+
const installedAgents = await this.installAgentFiles(flowPath, config);
|
|
34
|
+
return [...installedCommands, ...installedAgents];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Install prompts to .github/prompts/ with .prompt.md extension
|
|
39
|
+
*/
|
|
40
|
+
async installCommandFiles(flowPath, config) {
|
|
41
|
+
const targetDir = this.commandsDir;
|
|
42
|
+
console.log(theme.dim(` Installing prompts to ${targetDir}/...`));
|
|
43
|
+
await fs.ensureDir(targetDir);
|
|
44
|
+
|
|
45
|
+
const sourceDir = path.join(flowPath, 'commands');
|
|
46
|
+
|
|
47
|
+
if (!await fs.pathExists(sourceDir)) {
|
|
48
|
+
console.log(theme.dim(` No commands folder found at ${sourceDir}`));
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const files = await fs.readdir(sourceDir);
|
|
53
|
+
const installedFiles = [];
|
|
54
|
+
|
|
55
|
+
for (const file of files) {
|
|
56
|
+
if (file.endsWith('.md')) {
|
|
57
|
+
const sourcePath = path.join(sourceDir, file);
|
|
58
|
+
const prefix = (config && config.command && config.command.prefix) ? `${config.command.prefix}-` : '';
|
|
59
|
+
// Transform .md to .prompt.md for Copilot prompts
|
|
60
|
+
const targetFileName = `iris-${prefix}${file}`.replace(/\.md$/, '.prompt.md');
|
|
61
|
+
const targetPath = path.join(targetDir, targetFileName);
|
|
62
|
+
|
|
63
|
+
await fs.copy(sourcePath, targetPath);
|
|
64
|
+
installedFiles.push(targetFileName);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (installedFiles.length > 0) {
|
|
69
|
+
CLIUtils.displayStatus('', `Installed ${installedFiles.length} prompts for ${this.name}`, 'success');
|
|
70
|
+
}
|
|
71
|
+
return installedFiles;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Install agents to .github/agents/ with .agent.md extension
|
|
76
|
+
* Uses the commands folder as source (same files serve as both commands and agents)
|
|
77
|
+
*/
|
|
78
|
+
async installAgentFiles(flowPath, config) {
|
|
79
|
+
const targetDir = this.agentsDir;
|
|
80
|
+
console.log(theme.dim(` Installing agents to ${targetDir}/...`));
|
|
81
|
+
await fs.ensureDir(targetDir);
|
|
82
|
+
|
|
83
|
+
const sourceDir = path.join(flowPath, 'commands');
|
|
84
|
+
|
|
85
|
+
if (!await fs.pathExists(sourceDir)) {
|
|
86
|
+
console.log(theme.dim(` No commands folder found at ${sourceDir}`));
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const files = await fs.readdir(sourceDir);
|
|
91
|
+
const installedFiles = [];
|
|
92
|
+
|
|
93
|
+
for (const file of files) {
|
|
94
|
+
if (file.endsWith('.md')) {
|
|
95
|
+
const sourcePath = path.join(sourceDir, file);
|
|
96
|
+
const prefix = (config && config.command && config.command.prefix) ? `${config.command.prefix}-` : '';
|
|
97
|
+
// Transform .md to .agent.md for Copilot agents
|
|
98
|
+
const targetFileName = `iris-${prefix}${file}`.replace(/\.md$/, '.agent.md');
|
|
99
|
+
const targetPath = path.join(targetDir, targetFileName);
|
|
100
|
+
|
|
101
|
+
await fs.copy(sourcePath, targetPath);
|
|
102
|
+
installedFiles.push(targetFileName);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (installedFiles.length > 0) {
|
|
107
|
+
CLIUtils.displayStatus('', `Installed ${installedFiles.length} agents for ${this.name}`, 'success');
|
|
108
|
+
}
|
|
109
|
+
return installedFiles;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
module.exports = CopilotInstaller;
|