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,149 +0,0 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import kleur from "kleur";
4
- import { repoRoot, ensureDir } from "../lib.js";
5
- import { resolveDoctrinePath } from "./doctrine/collector.js";
6
- import { loadState } from "./state.js";
7
- function toDoctrineRelativeTemplatePath(t) {
8
- const prefix = ".iris/aidlc/";
9
- if (t.startsWith(prefix))
10
- return t.substring(prefix.length);
11
- // If someone already passed doctrine-relative, allow it (but ensure no leading slash if needed, usually robust)
12
- if (!t.startsWith(".iris/"))
13
- return t;
14
- return t; // Unknown path type, return as-is
15
- }
16
- export function collectFixPlan(result) {
17
- const plan = {
18
- actions: [],
19
- skipped: []
20
- };
21
- const root = repoRoot();
22
- const state = loadState();
23
- const activeFlowId = state.active.flow;
24
- for (const error of result.errors) {
25
- if (error.code !== "MISSING_ARTIFACT") {
26
- continue;
27
- }
28
- if (!error.path)
29
- continue;
30
- // Fix: Create Directory
31
- if (error.artifactType === "dir") {
32
- // Check if already in plan
33
- const exists = plan.actions.find(a => a.path === error.path && a.kind === "mkdir");
34
- if (!exists) {
35
- plan.actions.push({
36
- kind: "mkdir",
37
- path: error.path
38
- });
39
- }
40
- continue;
41
- }
42
- // Fix: Create File from Template
43
- if (error.artifactType === "file") {
44
- if (error.template) {
45
- // Normalize template path before resolving
46
- const templateRel = toDoctrineRelativeTemplatePath(error.template);
47
- // Verify template exists (resolving override)
48
- const templatePath = resolveDoctrinePath(root, templateRel, activeFlowId);
49
- if (templatePath && fs.existsSync(templatePath)) {
50
- plan.actions.push({
51
- kind: "createFileFromTemplate",
52
- path: error.path,
53
- template: templateRel // Store NORMALIZED path
54
- });
55
- }
56
- else {
57
- plan.skipped.push({
58
- reason: `Template missing: ${error.template}`,
59
- path: error.path
60
- });
61
- }
62
- }
63
- else {
64
- plan.skipped.push({
65
- reason: "No template defined",
66
- path: error.path
67
- });
68
- }
69
- continue;
70
- }
71
- }
72
- // Sort actions: mkdir first, then files
73
- plan.actions.sort((a, b) => {
74
- if (a.kind === "mkdir" && b.kind !== "mkdir")
75
- return -1;
76
- if (a.kind !== "mkdir" && b.kind === "mkdir")
77
- return 1;
78
- return a.path.localeCompare(b.path);
79
- });
80
- return plan;
81
- }
82
- export async function applyFixPlan(plan, options) {
83
- const result = {
84
- createdDirs: 0,
85
- createdFiles: 0,
86
- paths: []
87
- };
88
- const root = repoRoot();
89
- const state = loadState();
90
- const activeFlowId = state.active.flow;
91
- if (options.dryRun) {
92
- console.log(kleur.bold().blue("Dry Run Fix Plan:"));
93
- }
94
- for (const action of plan.actions) {
95
- const fullPath = path.join(root, action.path);
96
- if (action.kind === "mkdir") {
97
- if (options.dryRun) {
98
- console.log(` [MKDIR] ${action.path}`);
99
- continue;
100
- }
101
- try {
102
- if (!fs.existsSync(fullPath)) {
103
- ensureDir(fullPath);
104
- result.createdDirs++;
105
- result.paths.push(action.path);
106
- }
107
- }
108
- catch (e) {
109
- console.error(kleur.red(`Failed to create dir ${action.path}: ${e}`));
110
- }
111
- continue;
112
- }
113
- if (action.kind === "createFileFromTemplate" && action.template) {
114
- // Re-resolve to get absolute path (base or flow)
115
- // Template should already be normalized in collectFixPlan
116
- const templatePath = resolveDoctrinePath(root, action.template, activeFlowId);
117
- if (options.dryRun) {
118
- console.log(` [COPY] ${action.path} (from ${action.template})`);
119
- continue;
120
- }
121
- // Check existence logic (safe overwrite)
122
- if (fs.existsSync(fullPath)) {
123
- if (options.yes) {
124
- console.log(kleur.yellow(`Skipping ${action.path}: File exists.`));
125
- continue;
126
- }
127
- else {
128
- continue;
129
- }
130
- }
131
- if (!templatePath || !fs.existsSync(templatePath)) {
132
- console.error(kleur.red(`Template not found: ${action.template}`));
133
- continue;
134
- }
135
- try {
136
- // Read template
137
- const content = fs.readFileSync(templatePath, "utf-8");
138
- ensureDir(path.dirname(fullPath));
139
- fs.writeFileSync(fullPath, content);
140
- result.createdFiles++;
141
- result.paths.push(action.path);
142
- }
143
- catch (e) {
144
- console.error(kleur.red(`Failed to create file ${action.path}: ${e}`));
145
- }
146
- }
147
- }
148
- return result;
149
- }
@@ -1,124 +0,0 @@
1
- import path from "path";
2
- import fs from "fs";
3
- import yaml from "js-yaml";
4
- // --- Errors ---
5
- export class FlowNotFoundError extends Error {
6
- flowId;
7
- path;
8
- constructor(flowId, path) {
9
- super(`Flow '${flowId}' not found at ${path}`);
10
- this.flowId = flowId;
11
- this.path = path;
12
- this.name = "FlowNotFoundError";
13
- }
14
- }
15
- export class FlowManifestValidationError extends Error {
16
- flowId;
17
- constructor(flowId, message) {
18
- super(`Invalid manifest for flow '${flowId}': ${message}`);
19
- this.flowId = flowId;
20
- this.name = "FlowManifestValidationError";
21
- }
22
- }
23
- // --- Constants & Defaults ---
24
- const DEFAULT_ENTRYPOINTS = {
25
- doctrine_dir: "doctrine",
26
- policy_overlay: "policy.overlay.yaml",
27
- routes_overlay: "routes.overlay.yaml"
28
- };
29
- // --- Helpers ---
30
- export function getFlowsDir(root) {
31
- return path.join(root, ".iris/flows");
32
- }
33
- export function getFlowDir(root, flowId) {
34
- return path.join(getFlowsDir(root), flowId);
35
- }
36
- export function getFlowDoctrineRoot(root, flowId, manifest) {
37
- return path.join(getFlowDir(root, flowId), manifest.entrypoints.doctrine_dir);
38
- }
39
- export function getFlowPolicyOverlayPath(root, flowId, manifest) {
40
- return path.join(getFlowDir(root, flowId), manifest.entrypoints.policy_overlay);
41
- }
42
- export function getFlowRoutesOverlayPath(root, flowId, manifest) {
43
- return path.join(getFlowDir(root, flowId), manifest.entrypoints.routes_overlay);
44
- }
45
- // --- Loader ---
46
- export function loadFlowManifest(root, flowId) {
47
- const flowDir = getFlowDir(root, flowId);
48
- const manifestPath = path.join(flowDir, "flow.yaml");
49
- if (!fs.existsSync(manifestPath)) {
50
- throw new FlowNotFoundError(flowId, manifestPath);
51
- }
52
- let raw;
53
- try {
54
- raw = yaml.load(fs.readFileSync(manifestPath, "utf8"));
55
- }
56
- catch (e) {
57
- throw new FlowManifestValidationError(flowId, `Failed to parse YAML: ${e.message}`);
58
- }
59
- if (!raw || typeof raw !== "object") {
60
- throw new FlowManifestValidationError(flowId, "Manifest is not a valid object");
61
- }
62
- // Validation: Required Fields
63
- if (!raw.id || typeof raw.id !== "string" || !raw.id.trim()) {
64
- throw new FlowManifestValidationError(flowId, "Missing or empty 'id'");
65
- }
66
- if (!raw.name || typeof raw.name !== "string" || !raw.name.trim()) {
67
- throw new FlowManifestValidationError(flowId, "Missing or empty 'name'");
68
- }
69
- if (!raw.version || typeof raw.version !== "string" || !raw.version.trim()) {
70
- throw new FlowManifestValidationError(flowId, "Missing or empty 'version'");
71
- }
72
- // Validation: ID Match
73
- if (raw.id !== flowId) {
74
- throw new FlowManifestValidationError(flowId, `Manifest ID '${raw.id}' does not match flow ID '${flowId}'`);
75
- }
76
- // Defaults & Entrypoint Validation
77
- const entrypoints = { ...DEFAULT_ENTRYPOINTS, ...(raw.entrypoints || {}) };
78
- // Validate Paths
79
- validateRelativePath(flowId, "doctrine_dir", entrypoints.doctrine_dir);
80
- validateRelativePath(flowId, "policy_overlay", entrypoints.policy_overlay);
81
- validateRelativePath(flowId, "routes_overlay", entrypoints.routes_overlay);
82
- return {
83
- id: raw.id,
84
- name: raw.name,
85
- version: raw.version,
86
- description: raw.description,
87
- entrypoints
88
- };
89
- }
90
- function validateRelativePath(flowId, field, p) {
91
- if (!p || typeof p !== "string" || !p.trim()) {
92
- throw new FlowManifestValidationError(flowId, `Entrypoint '${field}' must be a non-empty string`);
93
- }
94
- if (path.isAbsolute(p)) {
95
- throw new FlowManifestValidationError(flowId, `Entrypoint '${field}' must be relative`);
96
- }
97
- const normalized = path.normalize(p);
98
- if (normalized.startsWith("..") || normalized === ".." || normalized.includes(path.sep + "..")) {
99
- throw new FlowManifestValidationError(flowId, `Entrypoint '${field}' cannot traverse upwards`);
100
- }
101
- }
102
- export function listInstalledFlows(root) {
103
- const flowsDir = getFlowsDir(root);
104
- if (!fs.existsSync(flowsDir))
105
- return [];
106
- const results = [];
107
- const entries = fs.readdirSync(flowsDir, { withFileTypes: true });
108
- for (const entry of entries) {
109
- if (!entry.isDirectory())
110
- continue;
111
- const flowId = entry.name;
112
- try {
113
- // This will validate schema and ID match
114
- const manifest = loadFlowManifest(root, flowId);
115
- results.push(manifest);
116
- }
117
- catch (e) {
118
- // Ignore invalid/partial flows during listing unless specific debug needed?
119
- // User requested: "don't crash on partial folders"
120
- // We sip it.
121
- }
122
- }
123
- return results;
124
- }
@@ -1,49 +0,0 @@
1
- import kleur from "kleur";
2
- import { loadState, getActiveFramework } from "./state.js";
3
- import { loadFramework } from "../framework/framework-loader.js";
4
- import { FrameworkError } from "../framework/framework-types.js";
5
- import { repoRoot } from "../lib.js";
6
- /**
7
- * Resolves the currently active framework from state.
8
- * Centralizes logic for commands to obtain the framework context.
9
- *
10
- * Rules:
11
- * 1. Reads .iris/state.yaml for active ID.
12
- * 2. Attempts to load framework.
13
- * 3. Returns structured context (success or failure).
14
- * 4. Prints warning to stderr if load fails (but returns context so caller can decide fallback).
15
- */
16
- export async function resolveActiveFramework(root) {
17
- const r = root || repoRoot();
18
- const state = loadState(); // loadState() takes no args currently
19
- const activeFw = getActiveFramework(state);
20
- try {
21
- const resolution = await loadFramework(activeFw.current, { repoRoot: r });
22
- return {
23
- activeId: activeFw.current,
24
- activeVersion: activeFw.version,
25
- resolution,
26
- error: null
27
- };
28
- }
29
- catch (err) {
30
- // If it's a known FrameworkError, return it.
31
- // If generic error, wrap it? loadFramework usually throws FrameworkError or Error.
32
- let fwError;
33
- if (err instanceof FrameworkError) {
34
- fwError = err;
35
- }
36
- else {
37
- // Generic error wrapper
38
- fwError = new FrameworkError("INVALID_YAML", activeFw.current, `Failed to load framework '${activeFw.current}': ${err instanceof Error ? err.message : String(err)}`);
39
- }
40
- // Emit Warning as requested
41
- console.error(kleur.yellow(`IRIS_WARNING IRIS_FRAMEWORK_LOAD_FAILED: Could not load active framework '${activeFw.current}'. Reason: ${fwError.message}`));
42
- return {
43
- activeId: activeFw.current,
44
- activeVersion: activeFw.version,
45
- resolution: null,
46
- error: fwError
47
- };
48
- }
49
- }
@@ -1,215 +0,0 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import { FrameworkError } from "../framework/framework-types.js";
4
- import { loadFramework } from "../framework/framework-loader.js";
5
- import { getBundledFrameworksDir } from "./bundle.js";
6
- import { copyDir, ensureDir } from "../lib.js";
7
- import { loadState, getActiveFramework } from "./state.js";
8
- // --- Validation ---
9
- /**
10
- * Validates a framework ID.
11
- * Defaults to strict mode: ^[a-z0-9][a-z0-9-_]*$
12
- * Future: allowScoped option.
13
- */
14
- export function validateFrameworkId(id, options = {}) {
15
- const { allowScoped = false } = options;
16
- if (!id || typeof id !== 'string')
17
- return false;
18
- // Strict mode (current default)
19
- // Starts with alphanumeric, followed by alphanumeric, -, _
20
- // TODO: Support scoped IDs (e.g. @scope/pkg) in future version.
21
- // Suggestion: Store scoped IDs under .iris/frameworks/@scope__pkg (encoded) to keep flat structure.
22
- const strictRegex = /^[a-z0-9][a-z0-9-_]*$/;
23
- return strictRegex.test(id);
24
- }
25
- // --- Listing ---
26
- export async function listInstalledFrameworks(root) {
27
- const frameworksDir = path.join(root, ".iris/frameworks");
28
- const state = loadState();
29
- const activeFw = getActiveFramework(state);
30
- if (!fs.existsSync(frameworksDir)) {
31
- return [];
32
- }
33
- const entries = fs.readdirSync(frameworksDir, { withFileTypes: true });
34
- const results = [];
35
- for (const entry of entries) {
36
- if (!entry.isDirectory())
37
- continue;
38
- // Ignore temp directories from failed atomic installs
39
- if (entry.name.startsWith('.tmp-'))
40
- continue;
41
- const fwPath = path.join(frameworksDir, entry.name);
42
- // Ensure active checking uses the folder name derived ID if logical ID is missing?
43
- // Actually framework state uses the ID.
44
- const isActive = activeFw.current === entry.name;
45
- try {
46
- const resolution = await loadFramework(entry.name, { repoRoot: root });
47
- results.push({
48
- id: resolution.manifest.id || entry.name, // Source of truth fallback
49
- version: resolution.manifest.version,
50
- path: fwPath,
51
- valid: true,
52
- active: isActive,
53
- manifest: resolution.manifest
54
- });
55
- }
56
- catch (error) {
57
- results.push({
58
- id: entry.name, // Fallback to folder name
59
- version: null,
60
- path: fwPath,
61
- valid: false,
62
- active: isActive,
63
- error: {
64
- code: error.code || 'UNKNOWN',
65
- message: error.message || String(error)
66
- }
67
- });
68
- }
69
- }
70
- return results;
71
- }
72
- export async function listBundledFrameworks() {
73
- const bundleDir = getBundledFrameworksDir();
74
- if (!fs.existsSync(bundleDir)) {
75
- return [];
76
- }
77
- const entries = fs.readdirSync(bundleDir, { withFileTypes: true });
78
- const results = [];
79
- for (const entry of entries) {
80
- if (!entry.isDirectory())
81
- continue;
82
- const fwPath = path.join(bundleDir, entry.name);
83
- try {
84
- // Load explicitly from path
85
- const resolution = await loadFramework(fwPath, { repoRoot: "/" });
86
- results.push({
87
- id: resolution.manifest.id || entry.name,
88
- path: fwPath,
89
- version: resolution.manifest.version,
90
- valid: true
91
- });
92
- }
93
- catch (e) {
94
- results.push({
95
- id: entry.name,
96
- path: fwPath,
97
- valid: false,
98
- error: e.message
99
- });
100
- }
101
- }
102
- return results;
103
- }
104
- // --- Installation ---
105
- export async function installFrameworkFromPath(root, sourcePath, options = {}) {
106
- const frameworksDir = path.join(root, ".iris/frameworks");
107
- ensureDir(frameworksDir);
108
- // 1. Validate Source & Get ID
109
- // We load it to ensure it's a valid framework AND to get the canonical ID
110
- let resolution;
111
- try {
112
- resolution = await loadFramework(sourcePath, { repoRoot: root });
113
- }
114
- catch (e) {
115
- throw new FrameworkError('INSTALL_FAILED', sourcePath, `Failed to load source framework: ${e.message}`, e.hint);
116
- }
117
- // Ensure ID is present
118
- const id = resolution.manifest.id;
119
- if (!id) {
120
- throw new FrameworkError('INVALID_SCHEMA', sourcePath, `Framework manifest missing required 'id' field.`);
121
- }
122
- // 2. Validate ID (Strict)
123
- if (!validateFrameworkId(id)) {
124
- throw new FrameworkError('INVALID_ID', id, `Framework ID '${id}' is invalid.`, "IDs must match ^[a-z0-9][a-z0-9-_]*$");
125
- }
126
- // 3. Resolve Target Path & Safety Guard
127
- const targetPath = path.resolve(frameworksDir, id);
128
- // Guard: Path Traversal
129
- if (!targetPath.startsWith(path.resolve(frameworksDir) + path.sep)) {
130
- throw new FrameworkError('SECURITY_VIOLATION', id, `Invalid framework ID '${id}': Resulting path traverses outside frameworks directory.`);
131
- }
132
- // 4. Check Existence
133
- if (fs.existsSync(targetPath)) {
134
- if (!options.force) {
135
- throw new FrameworkError('ALREADY_EXISTS', targetPath, `Framework '${id}' is already installed.`, "Use --force to overwrite.");
136
- }
137
- }
138
- // 5. Atomic Install
139
- // Copy to .tmp-<id>-<random>
140
- const tempName = `.tmp-${id}-${Math.random().toString(36).substring(7)}`;
141
- const tempPath = path.join(frameworksDir, tempName);
142
- try {
143
- // Copy recursive
144
- copyDir(resolution.rootDir, tempPath);
145
- // 6. Rename / Swap
146
- if (fs.existsSync(targetPath)) {
147
- // Remove old (Safe because verified inside frameworksDir)
148
- fs.rmSync(targetPath, { recursive: true, force: true });
149
- }
150
- fs.renameSync(tempPath, targetPath);
151
- return {
152
- id,
153
- version: resolution.manifest.version,
154
- path: targetPath,
155
- valid: true,
156
- active: false,
157
- manifest: resolution.manifest
158
- };
159
- }
160
- catch (e) {
161
- // Cleanup temp
162
- if (fs.existsSync(tempPath)) {
163
- fs.rmSync(tempPath, { recursive: true, force: true });
164
- }
165
- throw new FrameworkError('INSTALL_FAILED', targetPath, `Failed to install framework: ${e.message}`);
166
- }
167
- }
168
- export async function installBundledFramework(root, id, options = {}) {
169
- const bundleDir = getBundledFrameworksDir();
170
- // Fallback logic to find bundled framework
171
- let sourcePath = path.join(bundleDir, id);
172
- if (!fs.existsSync(sourcePath)) {
173
- const allBundled = await listBundledFrameworks();
174
- const found = allBundled.find(b => b.id === id && b.valid);
175
- if (found) {
176
- sourcePath = found.path;
177
- }
178
- else {
179
- throw new FrameworkError('NOT_FOUND', id, `Bundled framework '${id}' not found.`);
180
- }
181
- }
182
- return installFrameworkFromPath(root, sourcePath, options);
183
- }
184
- // --- Removal ---
185
- export function removeFramework(root, id) {
186
- const frameworksDir = path.join(root, ".iris/frameworks");
187
- // 1. Guard: Frameworks dir exists?
188
- if (!fs.existsSync(frameworksDir)) {
189
- throw new FrameworkError('NOT_FOUND', frameworksDir, "No frameworks installed.");
190
- }
191
- // 2. Resolve Target & Safety
192
- // Use regex first
193
- if (!validateFrameworkId(id)) {
194
- throw new FrameworkError('INVALID_ID', id, `Invalid framework ID format.`);
195
- }
196
- const targetPath = path.resolve(frameworksDir, id);
197
- if (!targetPath.startsWith(path.resolve(frameworksDir) + path.sep)) {
198
- throw new FrameworkError('SECURITY_VIOLATION', id, `Path traversal detected.`);
199
- }
200
- if (!fs.existsSync(targetPath)) {
201
- throw new FrameworkError('NOT_FOUND', targetPath, `Framework '${id}' is not installed.`);
202
- }
203
- // 3. Guard: Active
204
- const state = loadState();
205
- if (state.framework.current === id) {
206
- throw new FrameworkError('ACTIVE_FRAMEWORK', id, `Cannot remove active framework '${id}'.`, "Switch to another framework first (e.g. 'iris framework use iris-core').");
207
- }
208
- // 4. Delete
209
- fs.rmSync(targetPath, { recursive: true, force: true });
210
- }
211
- // --- Validation Wrapper ---
212
- export async function validateFrameworkParams(input, root) {
213
- // Just a wrapper for loadFramework but enables consistent error handling for CLI
214
- return loadFramework(input, { repoRoot: root });
215
- }
@@ -1,22 +0,0 @@
1
- import fs from "fs";
2
- import path from "path";
3
- export function writeJsonAtomic(filePath, data) {
4
- const tmpPath = `${filePath}.tmp.${Date.now()}`;
5
- const dir = path.dirname(filePath);
6
- if (!fs.existsSync(dir)) {
7
- fs.mkdirSync(dir, { recursive: true });
8
- }
9
- try {
10
- fs.writeFileSync(tmpPath, JSON.stringify(data, null, 2));
11
- fs.renameSync(tmpPath, filePath);
12
- }
13
- catch (error) {
14
- // Attempt cleanup
15
- try {
16
- if (fs.existsSync(tmpPath))
17
- fs.unlinkSync(tmpPath);
18
- }
19
- catch { }
20
- throw error;
21
- }
22
- }
@@ -1,38 +0,0 @@
1
- import kleur from "kleur";
2
- import { validate } from "./validator.js";
3
- import { EXIT_CODES } from "../utils/exit-codes.js";
4
- export async function requireValidRepoOrExit(options = {}) {
5
- // Run validation in read-only mode first?
6
- // Should we write back validation results for 'run'?
7
- // Specs: "Runs the same core validator used by iris validate"
8
- // If we run 'iris run', it probably SHOULD update the last_validation timestamp to show we checked.
9
- // So writeBack: true (default) is appropriate here.
10
- const result = await validate({
11
- apply: false,
12
- strict: options.strict || false,
13
- writeBack: true
14
- });
15
- if (!result.valid) {
16
- console.log("");
17
- console.log(kleur.red().bold("⛔ Repo invalid; run iris validate for details"));
18
- console.log("");
19
- // List 1-3 most important issues
20
- const topErrors = result.errors.slice(0, 3);
21
- topErrors.forEach((err) => {
22
- console.log(kleur.red(` [${err.code}] ${err.path || ""}`));
23
- });
24
- if (result.errors.length > 3) {
25
- console.log(kleur.dim(` ... and ${result.errors.length - 3} more.`));
26
- }
27
- console.log("");
28
- process.exit(EXIT_CODES.INVALID);
29
- }
30
- // If validated with warnings, we usually proceed unless strict is on.
31
- // Validator handles strict check internally?
32
- // No, validate() returns warnings=true. command/validate.ts handled exit.
33
- if (result.warnings && options.strict) {
34
- console.log("");
35
- console.log(kleur.red().bold("⛔ Repo has warnings (strict mode enabled)"));
36
- process.exit(EXIT_CODES.INVALID);
37
- }
38
- }
@@ -1,9 +0,0 @@
1
- const REGISTRY = {};
2
- export function registerImporter(importer) {
3
- REGISTRY[importer.id] = importer;
4
- }
5
- // No importers registered - IRIS ships only iris-core
6
- export function getImporter(id) {
7
- return REGISTRY[id] || null;
8
- }
9
- export const IMPORTERS = REGISTRY;
@@ -1,8 +0,0 @@
1
- export class ImporterError extends Error {
2
- exitCode;
3
- constructor(message, exitCode = 1) {
4
- super(message);
5
- this.exitCode = exitCode;
6
- this.name = 'ImporterError';
7
- }
8
- }