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
@@ -1,139 +0,0 @@
1
- import * as fs from 'fs';
2
- import * as path from 'path';
3
- // Safe extensions to import
4
- const SAFE_EXTENSIONS = ['.md', '.txt', '.yml', '.yaml', '.json'];
5
- const IGNORED_DIRS = ['node_modules', '.git', 'dist', 'build', 'coverage'];
6
- export function ensureDoctrineOnlyFlowInstalled(opts) {
7
- const result = {
8
- flowId: opts.flowId,
9
- installedDir: '', // set below
10
- copiedFiles: 0,
11
- skippedFiles: 0,
12
- overwrittenFiles: 0,
13
- };
14
- const flowDir = path.join(opts.repoRoot, '.iris', 'flows', opts.flowId);
15
- result.installedDir = flowDir;
16
- if (opts.verbose) {
17
- console.log(`Preparing to install flow '${opts.flowId}' into ${flowDir}`);
18
- }
19
- // 1. Check if flow already exists
20
- if (fs.existsSync(flowDir) && !opts.force) {
21
- // If not creating/updating, we might still want to report what happened,
22
- // but typically we'd expect the caller to handle "already exists" checks if they wanted to abort early.
23
- // However, the requirement is "overwrite if force", which implies "skip/merge if not force".
24
- // For simplicity, if it exists and !force, we might just return early if we consider the flow "installed".
25
- // But let's proceed to copy templates with "skip existing" semantics.
26
- }
27
- if (opts.dryRun) {
28
- console.log(`[Dry Run] Would create/update flow directory: ${flowDir}`);
29
- }
30
- else {
31
- fs.mkdirSync(flowDir, { recursive: true });
32
- }
33
- // 2. Ensure flow.yaml exists
34
- const flowYamlPath = path.join(flowDir, 'flow.yaml');
35
- if (opts.dryRun) {
36
- if (!fs.existsSync(flowYamlPath) || opts.force) {
37
- console.log(`[Dry Run] Would write flow.yaml for ${opts.flowId}`);
38
- }
39
- }
40
- else {
41
- if (!fs.existsSync(flowYamlPath) || opts.force) {
42
- const flowYamlContent = `# Generated by iris flow import
43
- id: ${opts.flowId}
44
- name: ${opts.flowId}
45
- description: Imported from ${opts.displayName}
46
- version: imported
47
- `;
48
- fs.writeFileSync(flowYamlPath, flowYamlContent);
49
- }
50
- }
51
- // 3. Ensure overlays exist (empty)
52
- const overlays = ['policy.overlay.yaml', 'routes.overlay.yaml'];
53
- for (const file of overlays) {
54
- const filePath = path.join(flowDir, file);
55
- if (opts.dryRun) {
56
- if (!fs.existsSync(filePath) || opts.force) {
57
- console.log(`[Dry Run] Would ensure empty ${file}`);
58
- }
59
- }
60
- else {
61
- if (!fs.existsSync(filePath) || opts.force) {
62
- fs.writeFileSync(filePath, '{}\n');
63
- }
64
- }
65
- }
66
- // 4. Copy templates
67
- // Destination: .iris/flows/<id>/doctrine/templates/<id>/...
68
- // We nest under <id> again inside templates/ to avoid collision if multiple flows share generic names,
69
- // and to keep it namespaced.
70
- const destTemplateRoot = path.join(flowDir, 'doctrine', 'templates', opts.flowId);
71
- if (opts.dryRun) {
72
- console.log(`[Dry Run] Would copy templates to ${destTemplateRoot}`);
73
- }
74
- else {
75
- fs.mkdirSync(destTemplateRoot, { recursive: true });
76
- }
77
- for (const srcDir of opts.sourceTemplateDirs) {
78
- copyRecursive(srcDir, destTemplateRoot, opts, result);
79
- }
80
- return result;
81
- }
82
- function copyRecursive(src, dest, opts, result, relPath = '') {
83
- if (!fs.existsSync(src))
84
- return;
85
- const stats = fs.statSync(src);
86
- if (stats.isDirectory()) {
87
- // Check ignore list
88
- const dirName = path.basename(src);
89
- if (IGNORED_DIRS.includes(dirName))
90
- return;
91
- if (!opts.dryRun) {
92
- fs.mkdirSync(dest, { recursive: true });
93
- }
94
- const children = fs.readdirSync(src);
95
- for (const child of children) {
96
- copyRecursive(path.join(src, child), path.join(dest, child), opts, result, path.join(relPath, child));
97
- }
98
- }
99
- else if (stats.isFile()) {
100
- // Filter extensions
101
- const ext = path.extname(src).toLowerCase();
102
- if (!SAFE_EXTENSIONS.includes(ext)) {
103
- if (opts.verbose)
104
- console.log(`Skipping unsafe/irrelevant file type: ${src}`);
105
- return;
106
- }
107
- if (opts.dryRun) {
108
- // Just log what would happen
109
- if (fs.existsSync(dest)) {
110
- if (opts.force) {
111
- console.log(`[Dry Run] Would overwrite ${relPath}`);
112
- result.overwrittenFiles++;
113
- }
114
- else {
115
- console.log(`[Dry Run] Would skip existing ${relPath}`);
116
- result.skippedFiles++;
117
- }
118
- }
119
- else {
120
- console.log(`[Dry Run] Would copy ${relPath}`);
121
- result.copiedFiles++;
122
- }
123
- return;
124
- }
125
- if (fs.existsSync(dest)) {
126
- if (opts.force) {
127
- fs.copyFileSync(src, dest);
128
- result.overwrittenFiles++;
129
- }
130
- else {
131
- result.skippedFiles++;
132
- }
133
- }
134
- else {
135
- fs.copyFileSync(src, dest);
136
- result.copiedFiles++;
137
- }
138
- }
139
- }
@@ -1,49 +0,0 @@
1
- import fs from "fs";
2
- import fg from "fast-glob";
3
- import { repoRoot } from "../lib.js";
4
- import { resolveArtifactPath } from "./resolver.js";
5
- export const DEFAULT_EXCLUDES = [
6
- "**/node_modules/**",
7
- "**/.git/**",
8
- "**/dist/**",
9
- "**/build/**",
10
- "**/.iris/cache/**",
11
- "**/.DS_Store"
12
- ];
13
- export async function resolveIncludes(includeGlobs, excludeGlobs = []) {
14
- const root = repoRoot();
15
- if (includeGlobs.length === 0)
16
- return [];
17
- const entries = await fg(includeGlobs, {
18
- cwd: root,
19
- ignore: [...DEFAULT_EXCLUDES, ...excludeGlobs],
20
- dot: true,
21
- absolute: false,
22
- onlyFiles: true
23
- });
24
- // Sort for determinism
25
- return entries.sort();
26
- }
27
- export function isBinary(buffer) {
28
- const chunk = buffer.subarray(0, Math.min(buffer.length, 8000));
29
- return chunk.includes(0);
30
- }
31
- const MAX_INDIVIDUAL_FILE_SIZE = 1_000_000;
32
- export function getFileContent(relPath) {
33
- const root = repoRoot();
34
- const absPath = resolveArtifactPath(root, relPath);
35
- try {
36
- const stats = fs.statSync(absPath);
37
- if (stats.size > MAX_INDIVIDUAL_FILE_SIZE) {
38
- return { path: relPath, size: stats.size, skipped: "too_large" };
39
- }
40
- const buffer = fs.readFileSync(absPath);
41
- if (isBinary(buffer)) {
42
- return { path: relPath, size: stats.size, skipped: "binary" };
43
- }
44
- return { path: relPath, size: stats.size, content: buffer.toString("utf8") };
45
- }
46
- catch (e) {
47
- return { path: relPath, size: 0, skipped: "read_error" };
48
- }
49
- }
@@ -1,334 +0,0 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import kleur from "kleur";
4
- import inquirer from "inquirer";
5
- import { ensureDir, sanitizeFolderName, spawnAsync } from "../lib.js";
6
- import { updateManifest } from "./manifest.js";
7
- import { getBundleRoot, getBundledFrameworksDir } from "./bundle.js";
8
- export const TOOLS = ["claude", "cursor", "gemini", "antigravity", "codex", "auto"];
9
- const BUNDLE_ROOT = getBundleRoot();
10
- /**
11
- * Ensure package.json exists in the target directory.
12
- * If missing, create a minimal one.
13
- */
14
- function ensurePackageJson(root) {
15
- const pkgPath = path.join(root, "package.json");
16
- if (fs.existsSync(pkgPath)) {
17
- console.log(kleur.gray("✓ package.json already exists"));
18
- return;
19
- }
20
- const folderName = path.basename(root);
21
- const sanitizedName = sanitizeFolderName(folderName);
22
- const minimalPackage = {
23
- name: sanitizedName,
24
- version: "0.0.0",
25
- private: true,
26
- description: "Project using IRIS framework",
27
- scripts: {}
28
- };
29
- fs.writeFileSync(pkgPath, JSON.stringify(minimalPackage, null, 2) + "\n", "utf8");
30
- console.log(kleur.green(`✓ Created package.json (name: ${sanitizedName})`));
31
- }
32
- /**
33
- * Run npm install to generate package-lock.json and install any dependencies
34
- */
35
- async function runNpmInstall(root) {
36
- console.log("");
37
- console.log(kleur.bold("Running npm install to generate lockfile..."));
38
- try {
39
- const result = await spawnAsync("npm", ["install"], root);
40
- if (result.code === 0) {
41
- console.log(kleur.green("✓ npm install completed successfully"));
42
- // Verify lockfile was created
43
- const lockPath = path.join(root, "package-lock.json");
44
- if (fs.existsSync(lockPath)) {
45
- console.log(kleur.green("✓ package-lock.json generated"));
46
- }
47
- return true;
48
- }
49
- else {
50
- console.error(kleur.red(`✗ npm install failed with code ${result.code}`));
51
- return false;
52
- }
53
- }
54
- catch (err) {
55
- console.error(kleur.red("✗ Failed to run npm install"));
56
- console.error(kleur.gray(` Error: ${err instanceof Error ? err.message : String(err)}`));
57
- return false;
58
- }
59
- }
60
- export function detectTools(root) {
61
- const checks = {
62
- claude: fs.existsSync(path.join(root, ".claude")),
63
- cursor: fs.existsSync(path.join(root, ".cursor")),
64
- gemini: fs.existsSync(path.join(root, ".gemini")),
65
- antigravity: fs.existsSync(path.join(root, ".antigravity")),
66
- codex: fs.existsSync(path.join(root, "AGENTS.md")),
67
- auto: false,
68
- };
69
- return TOOLS.filter(t => checks[t]);
70
- }
71
- export async function installIris(root, tools, options = {}) {
72
- // Step 0: Ensure package.json exists (for empty folder support)
73
- ensurePackageJson(root);
74
- const installedPaths = [];
75
- const createdDirs = [];
76
- // Bundle locations
77
- const bundleIris = path.join(BUNDLE_ROOT, ".iris");
78
- const bundleDoctrine = path.join(bundleIris, "aidlc");
79
- const bundleTools = path.join(bundleIris, "tools");
80
- // Target locations
81
- const targetIris = path.join(root, ".iris");
82
- const targetDoctrine = path.join(targetIris, "aidlc");
83
- const targetTools = path.join(targetIris, "tools");
84
- // Step C: Framework & Core Configuration
85
- // New (Step 3): Install iris-core framework as the Single Source of Truth
86
- // Then populate legacy mirrors (.iris/aidlc, policy.yaml, etc) from it.
87
- const bundledFrameworksDir = getBundledFrameworksDir();
88
- const sourceIrisCore = path.join(bundledFrameworksDir, "iris-core");
89
- const frameworksDir = path.join(targetIris, "frameworks");
90
- const targetIrisCore = path.join(frameworksDir, "iris-core");
91
- ensureDir(frameworksDir);
92
- // 1. Install Framework (Idempotent: Skip if exists unless --force)
93
- let installedFramework = false;
94
- if (fs.existsSync(sourceIrisCore)) {
95
- if (fs.existsSync(targetIrisCore) && !options.force) {
96
- // Skip
97
- // console.log(kleur.gray("Skipped iris-core (exists)"));
98
- // Check for version mismatch (UX enhancement)
99
- try {
100
- const targetManifestPath = path.join(targetIrisCore, "framework.yaml");
101
- const sourceManifestPath = path.join(sourceIrisCore, "framework.yaml");
102
- if (fs.existsSync(targetManifestPath) && fs.existsSync(sourceManifestPath)) {
103
- const targetManifest = JSON.parse(fs.readFileSync(targetManifestPath, 'utf8')); // Yaml is subset of json? No, need yaml parser or simple regex for version.
104
- // We don't have yaml parser imported here easily (maybe?) inquirer or kleur doesn't help.
105
- // Let's use simple regex since we control the manifest format mostly.
106
- const getVer = (content) => {
107
- const m = content.match(/version:\s*["']?([\w.-]+)["']?/);
108
- return m ? m[1] : null;
109
- };
110
- const targetVer = getVer(fs.readFileSync(targetManifestPath, 'utf8'));
111
- const sourceVer = getVer(fs.readFileSync(sourceManifestPath, 'utf8'));
112
- if (targetVer && sourceVer && targetVer !== sourceVer) {
113
- console.log(kleur.yellow(`Warning: Installed iris-core is v${targetVer}, but bundled is v${sourceVer}.`));
114
- console.log(kleur.yellow(`Run 'iris install --force' to update (will overwrite changes).`));
115
- }
116
- }
117
- }
118
- catch (e) {
119
- // ignore
120
- }
121
- }
122
- else {
123
- // Install / Overwrite
124
- if (fs.existsSync(targetIrisCore)) {
125
- // Force overwrite implies we can clobber.
126
- // Simple recursive copy over top works for updating files.
127
- }
128
- ensureDir(targetIrisCore);
129
- copyDirRec(sourceIrisCore, targetIrisCore, [], root, true);
130
- installedFramework = true;
131
- installedPaths.push({
132
- path: ".iris/frameworks/iris-core",
133
- type: "directory",
134
- status: "installed"
135
- });
136
- }
137
- }
138
- // 2. Compatibility Mirror: .iris/aidlc
139
- // Populated FROM the valid target iris-core framework
140
- // This ensures legacy tools see the same templates as the framework.
141
- const targetAidlc = path.join(targetIris, "aidlc");
142
- ensureDir(targetAidlc);
143
- // Mirror Templates
144
- const frameworkTemplates = path.join(targetIrisCore, "templates");
145
- const aidlcTemplates = path.join(targetAidlc, "templates");
146
- // Only mirror if framework templates exist
147
- if (fs.existsSync(frameworkTemplates)) {
148
- // If aidlc/templates exists, only overwrite if force?
149
- // Or always align mirror to framework?
150
- // "Keep it read-only in spirit". Aligning is safer.
151
- // But if user modified aidlc, we clobber?
152
- // Let's protect user edits in aidlc too unless force.
153
- if (!fs.existsSync(aidlcTemplates) || options.force) {
154
- ensureDir(aidlcTemplates);
155
- copyDirRec(frameworkTemplates, aidlcTemplates, [], root, true);
156
- }
157
- }
158
- // Legacy Config Mirrors (policy.yaml, routes.yaml)
159
- // Copy from framework -> .iris/ root IF MISSING (don't overwrite config by default)
160
- // We do NOT use --force to overwrite these configs usually, as they are user-owned once installed.
161
- // Spec: "Default: Skip if exists (preserve user edits). If --force: Overwrite."
162
- // So we respect that for these files too if we consider them part of "install".
163
- const frameworkPolicy = path.join(targetIrisCore, "policy.yaml");
164
- const targetPolicy = path.join(targetIris, "policy.yaml");
165
- if (fs.existsSync(frameworkPolicy) && (!fs.existsSync(targetPolicy) || options.force)) {
166
- fs.copyFileSync(frameworkPolicy, targetPolicy);
167
- installedPaths.push({ path: ".iris/policy.yaml", type: "file", status: "installed" });
168
- }
169
- const frameworkRoutes = path.join(targetIrisCore, "routes.yaml");
170
- const targetRoutes = path.join(targetIris, "routes.yaml");
171
- if (fs.existsSync(frameworkRoutes) && (!fs.existsSync(targetRoutes) || options.force)) {
172
- fs.copyFileSync(frameworkRoutes, targetRoutes);
173
- installedPaths.push({ path: ".iris/routes.yaml", type: "file", status: "installed" });
174
- }
175
- // Add README to aidlc
176
- const mirrorReadme = path.join(targetAidlc, "README.md");
177
- if (!fs.existsSync(mirrorReadme)) {
178
- fs.writeFileSync(mirrorReadme, "# Legacy Mirror\n\nThis directory is a compatibility mirror of `.iris/frameworks/iris-core`.\nDo not edit files here; they may be overwritten by system updates.\nEdit the framework files instead.\n");
179
- }
180
- // 3. State (only if missing)
181
- if (!fs.existsSync(path.join(targetIris, "state.yaml"))) {
182
- if (fs.existsSync(path.join(bundleIris, "state.yaml"))) {
183
- fs.copyFileSync(path.join(bundleIris, "state.yaml"), path.join(targetIris, "state.yaml"));
184
- installedPaths.push({
185
- path: ".iris/state.yaml",
186
- type: "file",
187
- status: "installed"
188
- });
189
- }
190
- }
191
- // Step D: Memory Bank Skeleton
192
- const memoryDirs = ["intents", "units", "bolts", "standards", "logs"];
193
- for (const d of memoryDirs) {
194
- const p = path.join(root, "memory-bank", d);
195
- if (!fs.existsSync(p)) {
196
- ensureDir(p);
197
- createdDirs.push(path.relative(root, p));
198
- }
199
- }
200
- // Step E: Install Tool Wrappers & F: Mirror
201
- for (const t of tools) {
202
- const toolBundlePath = path.join(bundleTools, t);
203
- if (!fs.existsSync(toolBundlePath)) {
204
- console.warn(kleur.yellow(`Warning: No bundle found for tool '${t}' at ${toolBundlePath}`));
205
- continue;
206
- }
207
- // Mirror first (always safe/overwrite in .iris/tools)
208
- const mirrorPath = path.join(targetTools, t);
209
- ensureDir(mirrorPath);
210
- // We copy the WHOLE bundles content for that tool into mirror.
211
- // Actually, we should mirror what we *attempt* to install.
212
- // Spec: "mirror exactly what was installed (and skipped) into .iris/tools/<tool>"
213
- // "still mirror the bundle version ... and record skipped_existing"
214
- // So: copy bundle -> mirror.
215
- copyDirRec(toolBundlePath, mirrorPath, [], root, true); // Don't track mirror files in manifest individually? Or do we?
216
- // "mirror installed tool payloads... create but only filled for selected tools"
217
- // Uninstall: "Remove .iris/tools/<tool> mirrors ... Always safe"
218
- // So we don't strictly need to track every file inside mirror in manifest, just the tool selection implies it.
219
- // Install to Repo Root
220
- await installToolWrapper(t, toolBundlePath, root, installedPaths);
221
- }
222
- // Step G: Update Manifest
223
- updateManifest({
224
- tools_selected: tools,
225
- paths_installed: installedPaths,
226
- created_dirs: createdDirs,
227
- doctrine_path: ".iris/aidlc",
228
- policy_path: ".iris/policy.yaml"
229
- });
230
- // Step H: Run npm install to generate lockfile
231
- await runNpmInstall(root);
232
- }
233
- async function installToolWrapper(tool, srcDir, root, installedPaths) {
234
- // We need to walk the srcDir and copy to root, respecting collisions.
235
- // For Claude: srcDir is .../tools/claude. contains .claude/...
236
- // So we just copy * recursively from srcDir to root.
237
- // Check for collisions first?
238
- // Recursive walker needed.
239
- const entries = getAllFiles(srcDir);
240
- for (const entry of entries) {
241
- const relPath = path.relative(srcDir, entry);
242
- const targetPath = path.join(root, relPath);
243
- if (fs.existsSync(targetPath)) {
244
- // Prompt? "Prompt per tool: File exists... Overwrite?"
245
- // Actually prompting check-per-file might be spammy.
246
- // "Prompt per tool: File exists... Overwrite?" -> implies one prompt per file clash?
247
- // "If a target file exists... Prompt per tool (maybe once?)... No, spec says: 'Prompt per tool: "File exists: <path>. Overwrite? (y/N)"'"
248
- // This looks like per-file.
249
- // Let's implement per-file prompt for safety.
250
- // Check content difference!
251
- if (areFilesEqual(entry, targetPath)) {
252
- // Identical, skip prompt, mark installed (or skipped? effectively same)
253
- installedPaths.push({ path: relPath, type: "file", status: "installed" });
254
- continue;
255
- }
256
- const { overwrite } = await inquirer.prompt([
257
- {
258
- type: "confirm",
259
- name: "overwrite",
260
- message: `File exists: ${relPath}. Overwrite?`,
261
- default: false
262
- }
263
- ]);
264
- if (overwrite) {
265
- ensureDir(path.dirname(targetPath));
266
- fs.copyFileSync(entry, targetPath);
267
- installedPaths.push({ path: relPath, type: "file", status: "overwritten" });
268
- console.log(kleur.green(`Overwrote ${relPath}`));
269
- }
270
- else {
271
- console.log(kleur.gray(`Skipped ${relPath}`));
272
- installedPaths.push({ path: relPath, type: "file", status: "skipped_existing" });
273
- }
274
- }
275
- else {
276
- ensureDir(path.dirname(targetPath));
277
- fs.copyFileSync(entry, targetPath);
278
- installedPaths.push({ path: relPath, type: "file", status: "installed" });
279
- }
280
- }
281
- }
282
- // Helpers
283
- function getAllFiles(dir, fileList = []) {
284
- const files = fs.readdirSync(dir);
285
- files.forEach(file => {
286
- const filePath = path.join(dir, file);
287
- if (fs.statSync(filePath).isDirectory()) {
288
- getAllFiles(filePath, fileList);
289
- }
290
- else {
291
- fileList.push(filePath);
292
- }
293
- });
294
- return fileList;
295
- }
296
- function copyDirRec(src, dest, trackList, root, silentTrack = false) {
297
- ensureDir(dest);
298
- const entries = fs.readdirSync(src, { withFileTypes: true });
299
- for (const entry of entries) {
300
- const srcPath = path.join(src, entry.name);
301
- const destPath = path.join(dest, entry.name);
302
- if (entry.isDirectory()) {
303
- copyDirRec(srcPath, destPath, trackList, root, silentTrack);
304
- }
305
- else {
306
- fs.copyFileSync(srcPath, destPath);
307
- if (!silentTrack) {
308
- trackList.push({
309
- path: path.relative(root, destPath),
310
- type: "file",
311
- status: "installed"
312
- });
313
- }
314
- }
315
- }
316
- }
317
- function safeCopy(src, dest, trackList, root) {
318
- if (fs.existsSync(dest))
319
- return; // Skip if exists
320
- if (!fs.existsSync(src))
321
- return; // Skip if source missing
322
- ensureDir(path.dirname(dest));
323
- fs.copyFileSync(src, dest);
324
- trackList.push({
325
- path: path.relative(root, dest),
326
- type: "file",
327
- status: "installed"
328
- });
329
- }
330
- function areFilesEqual(a, b) {
331
- const bufA = fs.readFileSync(a);
332
- const bufB = fs.readFileSync(b);
333
- return bufA.equals(bufB);
334
- }
@@ -1,21 +0,0 @@
1
- export function checkInteractiveMode(options) {
2
- // 1. Force Interactive overrides all
3
- if (options.forceInteractive) {
4
- return { isInteractive: true, reason: "Forced by user flag" };
5
- }
6
- // 2. Explicit Non-Interactive
7
- if (options.nonInteractive) {
8
- return { isInteractive: false, reason: "User requested non-interactive mode" };
9
- }
10
- // 3. CI Detection
11
- // Common CI env vars: CI, GITHUB_ACTIONS, GITLAB_CI, CIRCLECI, TRAVIS
12
- if (process.env.CI || process.env.GITHUB_ACTIONS || process.env.GITLAB_CI) {
13
- return { isInteractive: false, reason: "CI environment detected" };
14
- }
15
- // 4. TTY Detection
16
- // process.stdin.isTTY is undefined or false if piped/redirected
17
- if (!process.stdin.isTTY) {
18
- return { isInteractive: false, reason: "No TTY detected (piped input)" };
19
- }
20
- return { isInteractive: true };
21
- }