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.
Files changed (189) hide show
  1. package/README.md +214 -323
  2. package/bin/cli.js +21 -0
  3. package/flows/aidlc/README.md +372 -0
  4. package/flows/aidlc/agents/construction-agent.md +79 -0
  5. package/flows/aidlc/agents/inception-agent.md +97 -0
  6. package/flows/aidlc/agents/master-agent.md +61 -0
  7. package/flows/aidlc/agents/operations-agent.md +89 -0
  8. package/flows/aidlc/commands/construction-agent.md +63 -0
  9. package/flows/aidlc/commands/inception-agent.md +55 -0
  10. package/flows/aidlc/commands/master-agent.md +47 -0
  11. package/flows/aidlc/commands/operations-agent.md +77 -0
  12. package/flows/aidlc/context-config.yaml +67 -0
  13. package/flows/aidlc/memory-bank.yaml +104 -0
  14. package/flows/aidlc/quick-start.md +322 -0
  15. package/flows/aidlc/skills/construction/bolt-list.md +163 -0
  16. package/flows/aidlc/skills/construction/bolt-replan.md +345 -0
  17. package/flows/aidlc/skills/construction/bolt-start.md +442 -0
  18. package/flows/aidlc/skills/construction/bolt-status.md +185 -0
  19. package/flows/aidlc/skills/construction/navigator.md +196 -0
  20. package/flows/aidlc/skills/inception/bolt-plan.md +372 -0
  21. package/flows/aidlc/skills/inception/context.md +171 -0
  22. package/flows/aidlc/skills/inception/intent-create.md +211 -0
  23. package/flows/aidlc/skills/inception/intent-list.md +124 -0
  24. package/flows/aidlc/skills/inception/navigator.md +207 -0
  25. package/flows/aidlc/skills/inception/requirements.md +227 -0
  26. package/flows/aidlc/skills/inception/review.md +248 -0
  27. package/flows/aidlc/skills/inception/story-create.md +304 -0
  28. package/flows/aidlc/skills/inception/units.md +278 -0
  29. package/flows/aidlc/skills/master/analyze-context.md +239 -0
  30. package/flows/aidlc/skills/master/answer-question.md +141 -0
  31. package/flows/aidlc/skills/master/explain-flow.md +158 -0
  32. package/flows/aidlc/skills/master/project-init.md +281 -0
  33. package/flows/aidlc/skills/master/route-request.md +126 -0
  34. package/flows/aidlc/skills/operations/build.md +237 -0
  35. package/flows/aidlc/skills/operations/deploy.md +259 -0
  36. package/flows/aidlc/skills/operations/monitor.md +265 -0
  37. package/flows/aidlc/skills/operations/navigator.md +209 -0
  38. package/flows/aidlc/skills/operations/verify.md +224 -0
  39. package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt.md +3 -3
  40. package/{dist → flows/aidlc}/templates/construction/bolt-types/spike-bolt.md +2 -2
  41. package/flows/aidlc/templates/construction/construction-log-template.md +129 -0
  42. package/flows/aidlc/templates/construction/standards/coding-standards.md +29 -0
  43. package/flows/aidlc/templates/construction/standards/system-architecture.md +22 -0
  44. package/flows/aidlc/templates/construction/standards/tech-stack.md +19 -0
  45. package/flows/aidlc/templates/inception/inception-log-template.md +134 -0
  46. package/flows/aidlc/templates/inception/project/README.md +55 -0
  47. package/flows/aidlc/templates/standards/catalog.yaml +345 -0
  48. package/flows/aidlc/templates/standards/coding-standards.guide.md +553 -0
  49. package/flows/aidlc/templates/standards/data-stack.guide.md +162 -0
  50. package/flows/aidlc/templates/standards/tech-stack.guide.md +280 -0
  51. package/lib/InstallerFactory.js +36 -0
  52. package/lib/analytics/env-detector.js +92 -0
  53. package/lib/analytics/index.js +22 -0
  54. package/lib/analytics/machine-id.js +33 -0
  55. package/lib/analytics/tracker.js +232 -0
  56. package/lib/cli-utils.js +342 -0
  57. package/lib/constants.js +32 -0
  58. package/lib/installer.js +402 -0
  59. package/lib/installers/AntigravityInstaller.js +22 -0
  60. package/lib/installers/ClaudeInstaller.js +85 -0
  61. package/lib/installers/ClineInstaller.js +21 -0
  62. package/lib/installers/CodexInstaller.js +21 -0
  63. package/lib/installers/CopilotInstaller.js +113 -0
  64. package/lib/installers/CursorInstaller.js +63 -0
  65. package/lib/installers/GeminiInstaller.js +75 -0
  66. package/lib/installers/KiroInstaller.js +22 -0
  67. package/lib/installers/OpenCodeInstaller.js +22 -0
  68. package/lib/installers/RooInstaller.js +22 -0
  69. package/lib/installers/ToolInstaller.js +73 -0
  70. package/lib/installers/WindsurfInstaller.js +22 -0
  71. package/lib/markdown-validator.ts +175 -0
  72. package/lib/yaml-validator.ts +99 -0
  73. package/package.json +105 -32
  74. package/scripts/artifact-validator.js +594 -0
  75. package/scripts/bolt-complete.js +606 -0
  76. package/scripts/status-integrity.js +598 -0
  77. package/dist/bridge/agent-runner.js +0 -190
  78. package/dist/bridge/connector-factory.js +0 -31
  79. package/dist/bridge/connectors/antigravity-connector.js +0 -18
  80. package/dist/bridge/connectors/cursor-connector.js +0 -31
  81. package/dist/bridge/connectors/in-process-connector.js +0 -29
  82. package/dist/bridge/connectors/vscode-connector.js +0 -31
  83. package/dist/bridge/connectors/windsurf-connector.js +0 -23
  84. package/dist/bridge/filesystem-connector.js +0 -110
  85. package/dist/bridge/helper.js +0 -203
  86. package/dist/bridge/types.js +0 -10
  87. package/dist/cli.js +0 -40
  88. package/dist/commands/ask.js +0 -259
  89. package/dist/commands/bridge.js +0 -88
  90. package/dist/commands/create.js +0 -25
  91. package/dist/commands/develop.js +0 -141
  92. package/dist/commands/doctor.js +0 -102
  93. package/dist/commands/flow.js +0 -301
  94. package/dist/commands/framework.js +0 -273
  95. package/dist/commands/generate.js +0 -59
  96. package/dist/commands/install.js +0 -100
  97. package/dist/commands/pack.js +0 -33
  98. package/dist/commands/phase.js +0 -38
  99. package/dist/commands/run.js +0 -199
  100. package/dist/commands/status.js +0 -114
  101. package/dist/commands/uninstall.js +0 -14
  102. package/dist/commands/use.js +0 -20
  103. package/dist/commands/validate.js +0 -102
  104. package/dist/framework/framework-loader.js +0 -97
  105. package/dist/framework/framework-paths.js +0 -48
  106. package/dist/framework/framework-types.js +0 -15
  107. package/dist/iris/artifact-checker.js +0 -78
  108. package/dist/iris/artifacts/config.js +0 -68
  109. package/dist/iris/artifacts/generator.js +0 -88
  110. package/dist/iris/artifacts/types.js +0 -1
  111. package/dist/iris/bundle.js +0 -44
  112. package/dist/iris/doctrine/collector.js +0 -124
  113. package/dist/iris/fixer.js +0 -149
  114. package/dist/iris/flows/manifest.js +0 -124
  115. package/dist/iris/framework-context.js +0 -49
  116. package/dist/iris/framework-manager.js +0 -215
  117. package/dist/iris/fs/atomic.js +0 -22
  118. package/dist/iris/guard.js +0 -38
  119. package/dist/iris/importers/index.js +0 -9
  120. package/dist/iris/importers/types.js +0 -8
  121. package/dist/iris/importers/writer.js +0 -139
  122. package/dist/iris/include.js +0 -49
  123. package/dist/iris/installer.js +0 -334
  124. package/dist/iris/interactive/env.js +0 -21
  125. package/dist/iris/interactive/intent-interview.js +0 -345
  126. package/dist/iris/interactive/intent-schema.js +0 -28
  127. package/dist/iris/interactive/interview-io.js +0 -22
  128. package/dist/iris/interview/config.js +0 -71
  129. package/dist/iris/interview/types.js +0 -16
  130. package/dist/iris/interview/utils.js +0 -38
  131. package/dist/iris/manifest.js +0 -54
  132. package/dist/iris/packer.js +0 -325
  133. package/dist/iris/parsers/unit-parser.js +0 -43
  134. package/dist/iris/paths.js +0 -18
  135. package/dist/iris/policy.js +0 -133
  136. package/dist/iris/proc.js +0 -56
  137. package/dist/iris/report.js +0 -53
  138. package/dist/iris/resolver.js +0 -66
  139. package/dist/iris/router.js +0 -114
  140. package/dist/iris/routes.js +0 -189
  141. package/dist/iris/run-state.js +0 -146
  142. package/dist/iris/state.js +0 -113
  143. package/dist/iris/templates.js +0 -70
  144. package/dist/iris/tmp.js +0 -24
  145. package/dist/iris/uninstaller.js +0 -181
  146. package/dist/iris/utils/interpolate.js +0 -42
  147. package/dist/iris/validator.js +0 -391
  148. package/dist/iris/workflow/config.js +0 -51
  149. package/dist/iris/workflow/engine.js +0 -129
  150. package/dist/iris/workflow/steps.js +0 -448
  151. package/dist/iris/workflow/types.js +0 -1
  152. package/dist/iris_bundle/frameworks/iris-core/framework.yaml +0 -9
  153. package/dist/iris_bundle/frameworks/iris-core/memory/memory-bank.yaml +0 -1
  154. package/dist/iris_bundle/frameworks/iris-core/policy.yaml +0 -7
  155. package/dist/iris_bundle/frameworks/iris-core/templates/config/memory-bank.yaml +0 -1
  156. package/dist/iris_bundle/frameworks/iris-core/templates/construction/bolt-types/spike-bolt.md +0 -240
  157. package/dist/lib.js +0 -96
  158. package/dist/templates/construction/bolt-template.md +0 -226
  159. package/dist/templates/construction/bolt-types/ddd-construction-bolt/adr-template.md +0 -49
  160. package/dist/templates/construction/bolt-types/ddd-construction-bolt/ddd-01-domain-model-template.md +0 -55
  161. package/dist/templates/construction/bolt-types/ddd-construction-bolt/ddd-02-technical-design-template.md +0 -67
  162. package/dist/templates/construction/bolt-types/ddd-construction-bolt/ddd-03-test-report-template.md +0 -62
  163. package/dist/templates/construction/bolt-types/ddd-construction-bolt.md +0 -528
  164. package/dist/templates/construction/bolt-types/simple-construction-bolt.md +0 -347
  165. package/dist/templates/inception/requirements-template.md +0 -144
  166. package/dist/templates/inception/stories-template.md +0 -38
  167. package/dist/templates/inception/story-template.md +0 -147
  168. package/dist/templates/inception/system-context-template.md +0 -29
  169. package/dist/templates/inception/unit-brief-template.md +0 -177
  170. package/dist/templates/inception/units-template.md +0 -52
  171. package/dist/utils/exit-codes.js +0 -7
  172. package/dist/utils/logo.js +0 -17
  173. package/dist/workflows/bolt-execution.js +0 -238
  174. package/dist/workflows/bolt-plan.js +0 -221
  175. package/dist/workflows/intent-inception.js +0 -285
  176. package/dist/workflows/memory-bank-generator.js +0 -180
  177. package/dist/workflows/reporting.js +0 -74
  178. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-template.md +0 -0
  179. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt/adr-template.md +0 -0
  180. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt/ddd-01-domain-model-template.md +0 -0
  181. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt/ddd-02-technical-design-template.md +0 -0
  182. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/ddd-construction-bolt/ddd-03-test-report-template.md +0 -0
  183. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/construction/bolt-types/simple-construction-bolt.md +0 -0
  184. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/requirements-template.md +0 -0
  185. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/stories-template.md +0 -0
  186. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/story-template.md +0 -0
  187. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/system-context-template.md +0 -0
  188. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/unit-brief-template.md +0 -0
  189. /package/{dist/iris_bundle/frameworks/iris-core → flows/aidlc}/templates/inception/units-template.md +0 -0
@@ -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;