project-iris 0.0.6

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 (109) hide show
  1. package/README.md +384 -0
  2. package/dist/bridge/connector-factory.js +27 -0
  3. package/dist/bridge/connectors/antigravity-connector.js +18 -0
  4. package/dist/bridge/connectors/cursor-connector.js +31 -0
  5. package/dist/bridge/connectors/vscode-connector.js +31 -0
  6. package/dist/bridge/connectors/windsurf-connector.js +23 -0
  7. package/dist/bridge/filesystem-connector.js +100 -0
  8. package/dist/bridge/types.js +10 -0
  9. package/dist/cli.js +30 -0
  10. package/dist/commands/ask.js +232 -0
  11. package/dist/commands/bridge.js +259 -0
  12. package/dist/commands/develop.js +108 -0
  13. package/dist/commands/doctor.js +102 -0
  14. package/dist/commands/install.js +57 -0
  15. package/dist/commands/pack.js +27 -0
  16. package/dist/commands/phase.js +38 -0
  17. package/dist/commands/run.js +17 -0
  18. package/dist/commands/status.js +105 -0
  19. package/dist/commands/uninstall.js +12 -0
  20. package/dist/commands/validate.js +87 -0
  21. package/dist/iris/artifact-checker.js +78 -0
  22. package/dist/iris/fixer.js +143 -0
  23. package/dist/iris/guard.js +38 -0
  24. package/dist/iris/include.js +49 -0
  25. package/dist/iris/installer.js +269 -0
  26. package/dist/iris/manifest.js +54 -0
  27. package/dist/iris/packer.js +303 -0
  28. package/dist/iris/policy.js +28 -0
  29. package/dist/iris/report.js +53 -0
  30. package/dist/iris/resolver.js +63 -0
  31. package/dist/iris/router.js +114 -0
  32. package/dist/iris/routes.js +20 -0
  33. package/dist/iris/run-state.js +143 -0
  34. package/dist/iris/state.js +85 -0
  35. package/dist/iris/uninstaller.js +166 -0
  36. package/dist/iris/validator.js +329 -0
  37. package/dist/lib.js +96 -0
  38. package/dist/utils/exit-codes.js +7 -0
  39. package/dist/workflows/bolt-execution.js +238 -0
  40. package/dist/workflows/bolt-plan.js +192 -0
  41. package/dist/workflows/intent-inception.js +188 -0
  42. package/package.json +41 -0
  43. package/src/iris_bundle/.iris/aidlc/README.md +16 -0
  44. package/src/iris_bundle/.iris/aidlc/agents/iris-construction-agent.md +35 -0
  45. package/src/iris_bundle/.iris/aidlc/agents/iris-inception-agent.md +30 -0
  46. package/src/iris_bundle/.iris/aidlc/agents/iris-master-agent.md +35 -0
  47. package/src/iris_bundle/.iris/aidlc/agents/iris-operations-agent.md +29 -0
  48. package/src/iris_bundle/.iris/aidlc/commands/iris-construction-agent.md +18 -0
  49. package/src/iris_bundle/.iris/aidlc/commands/iris-inception-agent.md +18 -0
  50. package/src/iris_bundle/.iris/aidlc/commands/iris-master-agent.md +18 -0
  51. package/src/iris_bundle/.iris/aidlc/commands/iris-operations-agent.md +18 -0
  52. package/src/iris_bundle/.iris/aidlc/context/context-map.md +25 -0
  53. package/src/iris_bundle/.iris/aidlc/context/exclusion-rules.md +13 -0
  54. package/src/iris_bundle/.iris/aidlc/context/load-order.md +25 -0
  55. package/src/iris_bundle/.iris/aidlc/memory/intent-rules.md +9 -0
  56. package/src/iris_bundle/.iris/aidlc/memory/log-rules.md +5 -0
  57. package/src/iris_bundle/.iris/aidlc/memory/memory-bank.yaml +39 -0
  58. package/src/iris_bundle/.iris/aidlc/memory/unit-rules.md +9 -0
  59. package/src/iris_bundle/.iris/aidlc/quick-start.md +24 -0
  60. package/src/iris_bundle/.iris/aidlc/skills/execution/implementation.md +14 -0
  61. package/src/iris_bundle/.iris/aidlc/skills/execution/refactoring.md +13 -0
  62. package/src/iris_bundle/.iris/aidlc/skills/execution/scaffold-generation.md +15 -0
  63. package/src/iris_bundle/.iris/aidlc/skills/governance/escalation.md +13 -0
  64. package/src/iris_bundle/.iris/aidlc/skills/governance/quality-gates.md +14 -0
  65. package/src/iris_bundle/.iris/aidlc/skills/governance/stop-conditions.md +11 -0
  66. package/src/iris_bundle/.iris/aidlc/skills/reasoning/decomposition.md +23 -0
  67. package/src/iris_bundle/.iris/aidlc/skills/reasoning/risk-analysis.md +14 -0
  68. package/src/iris_bundle/.iris/aidlc/skills/reasoning/verification.md +21 -0
  69. package/src/iris_bundle/.iris/aidlc/standards/artifacts-registry.md +38 -0
  70. package/src/iris_bundle/.iris/aidlc/standards/decision-logging.md +16 -0
  71. package/src/iris_bundle/.iris/aidlc/standards/doctrine-structure.md +31 -0
  72. package/src/iris_bundle/.iris/aidlc/standards/documentation-rules.md +15 -0
  73. package/src/iris_bundle/.iris/aidlc/standards/file-structure.md +21 -0
  74. package/src/iris_bundle/.iris/aidlc/standards/naming-conventions.md +18 -0
  75. package/src/iris_bundle/.iris/aidlc/standards/phases-and-gates.md +25 -0
  76. package/src/iris_bundle/.iris/aidlc/standards/routes-and-routing.md +35 -0
  77. package/src/iris_bundle/.iris/aidlc/standards/tool-wrappers.md +32 -0
  78. package/src/iris_bundle/.iris/aidlc/templates/bolt.md +23 -0
  79. package/src/iris_bundle/.iris/aidlc/templates/doctrine-doc-template.md +33 -0
  80. package/src/iris_bundle/.iris/aidlc/templates/intent.md +23 -0
  81. package/src/iris_bundle/.iris/aidlc/templates/log.md +24 -0
  82. package/src/iris_bundle/.iris/aidlc/templates/review.md +21 -0
  83. package/src/iris_bundle/.iris/aidlc/templates/unit.md +31 -0
  84. package/src/iris_bundle/.iris/aidlc/validation/failure-modes.md +16 -0
  85. package/src/iris_bundle/.iris/aidlc/validation/phase-preconditions.md +21 -0
  86. package/src/iris_bundle/.iris/aidlc/validation/quality-checklist.md +20 -0
  87. package/src/iris_bundle/.iris/policy.yaml +27 -0
  88. package/src/iris_bundle/.iris/routes.yaml +98 -0
  89. package/src/iris_bundle/.iris/state.yaml +7 -0
  90. package/src/iris_bundle/.iris/tools/antigravity/.antigravity/knowledge/IRIS.md +6 -0
  91. package/src/iris_bundle/.iris/tools/antigravity/.antigravity/workflows/iris-construction-agent.md +25 -0
  92. package/src/iris_bundle/.iris/tools/antigravity/.antigravity/workflows/iris-inception-agent.md +25 -0
  93. package/src/iris_bundle/.iris/tools/antigravity/.antigravity/workflows/iris-master-agent.md +25 -0
  94. package/src/iris_bundle/.iris/tools/antigravity/.antigravity/workflows/iris-operations-agent.md +25 -0
  95. package/src/iris_bundle/.iris/tools/claude/.claude/claude.md +9 -0
  96. package/src/iris_bundle/.iris/tools/claude/.claude/commands/compare-specs.md +203 -0
  97. package/src/iris_bundle/.iris/tools/claude/.claude/commands/iris-construction-agent.md +25 -0
  98. package/src/iris_bundle/.iris/tools/claude/.claude/commands/iris-inception-agent.md +25 -0
  99. package/src/iris_bundle/.iris/tools/claude/.claude/commands/iris-master-agent.md +25 -0
  100. package/src/iris_bundle/.iris/tools/claude/.claude/commands/iris-operations-agent.md +25 -0
  101. package/src/iris_bundle/.iris/tools/codex/AGENTS.md +15 -0
  102. package/src/iris_bundle/.iris/tools/cursor/.cursor/commands/iris-construction-agent.md +25 -0
  103. package/src/iris_bundle/.iris/tools/cursor/.cursor/commands/iris-inception-agent.md +25 -0
  104. package/src/iris_bundle/.iris/tools/cursor/.cursor/commands/iris-master-agent.md +25 -0
  105. package/src/iris_bundle/.iris/tools/cursor/.cursor/commands/iris-operations-agent.md +25 -0
  106. package/src/iris_bundle/.iris/tools/gemini/.gemini/commands/iris-construction-agent.toml +29 -0
  107. package/src/iris_bundle/.iris/tools/gemini/.gemini/commands/iris-inception-agent.toml +29 -0
  108. package/src/iris_bundle/.iris/tools/gemini/.gemini/commands/iris-master-agent.toml +29 -0
  109. package/src/iris_bundle/.iris/tools/gemini/.gemini/commands/iris-operations-agent.toml +29 -0
@@ -0,0 +1,303 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import kleur from "kleur";
4
+ import { repoRoot, writeFile, ensureDir } from "../lib.js";
5
+ import { loadState } from "./state.js";
6
+ import { loadPolicy } from "./policy.js";
7
+ import { validate } from "./validator.js";
8
+ import { resolveIncludes, getFileContent } from "./include.js";
9
+ import { checkArtifact } from "./artifact-checker.js";
10
+ import { EXIT_CODES } from "../utils/exit-codes.js";
11
+ export async function generatePack(options) {
12
+ const root = repoRoot();
13
+ // 1. Validate Repo (Guardrail)
14
+ try {
15
+ const result = await validate({
16
+ apply: false,
17
+ strict: false,
18
+ writeBack: false
19
+ });
20
+ if (!result.valid) {
21
+ console.error(kleur.red("Repo is INVALID. Fix issues before packing."));
22
+ process.exit(EXIT_CODES.INVALID); // Exit 2
23
+ }
24
+ if (options.strict && result.warnings) {
25
+ console.error(kleur.red("Repo has warnings and strict mode is on."));
26
+ process.exit(EXIT_CODES.INVALID);
27
+ }
28
+ }
29
+ catch (e) {
30
+ console.error("Validation failed:", e);
31
+ process.exit(EXIT_CODES.INVALID);
32
+ }
33
+ // 2. Load State & Determine Context
34
+ const state = loadState();
35
+ const phase = options.phase || state.phase.current;
36
+ const agent = options.agent || inferAgent(phase);
37
+ // 3. Collect Candidates
38
+ // Doctrine
39
+ const doctrineFiles = collectDoctrine(agent);
40
+ // Required Artifacts
41
+ const requiredArtifacts = collectRequiredArtifacts(phase, state);
42
+ // Optional Artifacts
43
+ const optionalArtifacts = collectOptionalArtifacts(phase);
44
+ // Included Artifacts
45
+ const includedFiles = options.includes && options.includes.length > 0
46
+ ? await resolveIncludes(options.includes, options.excludes)
47
+ : [];
48
+ // 4. Read & Assemble
49
+ // Core (Doctrine + Required) - Must be included
50
+ const coreContent = [];
51
+ for (const p of [...doctrineFiles, ...requiredArtifacts]) {
52
+ // Required files generally SHOULD exist, but if missing, we record them
53
+ // Doctrine: "If a file is missing, skip it but record in the bundle '(missing)' list."
54
+ // Required Artifacts: "If required file missing -> pack must fail (exit 2)"
55
+ const res = getFileContent(p);
56
+ // Check requirement for artifacts (not doctrine)
57
+ if (requiredArtifacts.includes(p)) {
58
+ // Use artifact checker for proper validation
59
+ const check = checkArtifact(root, p);
60
+ if (!check.exists) {
61
+ console.error(kleur.red(`Missing required artifact: ${check.normalized}`));
62
+ process.exit(EXIT_CODES.INVALID);
63
+ }
64
+ // For files, also check if content was readable
65
+ if (check.isFile && res.skipped === "read_error") {
66
+ console.error(kleur.red(`Cannot read required artifact: ${check.normalized}`));
67
+ process.exit(EXIT_CODES.INVALID);
68
+ }
69
+ }
70
+ coreContent.push(res);
71
+ }
72
+ // Optional - Can be dropped
73
+ const optionalContent = [];
74
+ for (const p of optionalArtifacts) {
75
+ // Optional artifacts usually are logs/bolts which are repo-local.
76
+ // getFileContent resolves them. If they don't exist, we skip.
77
+ const res = getFileContent(p);
78
+ if (res.skipped === "read_error" || res.size === 0)
79
+ continue;
80
+ optionalContent.push(res);
81
+ }
82
+ // Includes - Can be dropped
83
+ const extraContent = [];
84
+ for (const p of includedFiles) {
85
+ extraContent.push(getFileContent(p));
86
+ }
87
+ // 5. Enforce Limits
88
+ const maxBytes = options.maxBytes || 1_500_000;
89
+ const finalSelection = enforceLimits(coreContent, optionalContent, extraContent, maxBytes);
90
+ // 6. Format
91
+ const markdown = formatBundle(finalSelection, {
92
+ repo: path.basename(root),
93
+ phase,
94
+ agent,
95
+ generatedAt: new Date().toISOString()
96
+ });
97
+ // 7. Output
98
+ if (options.stdout) {
99
+ console.log(markdown);
100
+ if (options.output) {
101
+ writeFile(options.output, markdown);
102
+ }
103
+ }
104
+ else {
105
+ const outFile = options.output || path.join(root, `.iris/inbox/context-pack.${agent}.${phase}.md`);
106
+ writeFile(outFile, markdown);
107
+ // Write LATEST
108
+ const latestFile = path.join(root, ".iris/inbox/LATEST.md");
109
+ ensureDir(path.dirname(latestFile)); // Ensure dir exists for LATEST too
110
+ const relPath = path.relative(path.dirname(latestFile), outFile);
111
+ writeFile(latestFile, relPath);
112
+ console.log(kleur.green(`Packed context bundle to: ${outFile}`));
113
+ console.log(kleur.gray(`Size: ${(markdown.length / 1024).toFixed(1)} KB`));
114
+ }
115
+ }
116
+ function inferAgent(phase) {
117
+ switch (phase) {
118
+ case "inception": return "inception";
119
+ case "construction": return "construction";
120
+ case "operations": return "operations";
121
+ default: return "master";
122
+ }
123
+ }
124
+ function collectDoctrine(agent) {
125
+ const list = [
126
+ ".iris/aidlc/quick-start.md",
127
+ ".iris/aidlc/context/load-order.md",
128
+ ".iris/aidlc/standards/doctrine-structure.md",
129
+ ".iris/aidlc/standards/phases-and-gates.md",
130
+ ".iris/aidlc/validation/phase-preconditions.md",
131
+ ".iris/aidlc/standards/artifacts-registry.md",
132
+ ".iris/aidlc/standards/tool-wrappers.md",
133
+ `.iris/aidlc/agents/iris-${agent}-agent.md`,
134
+ `.iris/aidlc/commands/iris-${agent}-agent.md`
135
+ ];
136
+ return list;
137
+ }
138
+ function collectRequiredArtifacts(phase, state) {
139
+ const list = [
140
+ ".iris/state.yaml",
141
+ ".iris/policy.yaml"
142
+ ];
143
+ if (fs.existsSync(path.join(repoRoot(), ".iris/routes.yaml")))
144
+ list.push(".iris/routes.yaml");
145
+ if (fs.existsSync(path.join(repoRoot(), ".iris/manifest.yaml")))
146
+ list.push(".iris/manifest.yaml");
147
+ // Use Policy Source of Truth
148
+ try {
149
+ const policy = loadPolicy();
150
+ if (policy.phases[phase] && policy.phases[phase].requires) {
151
+ for (const req of policy.phases[phase].requires) {
152
+ list.push(req.path);
153
+ }
154
+ }
155
+ }
156
+ catch (e) {
157
+ // Fallback or ignore if policy broken (validator should have caught it)
158
+ }
159
+ return list;
160
+ }
161
+ function collectOptionalArtifacts(phase) {
162
+ const list = [];
163
+ const root = repoRoot();
164
+ if (phase === "inception") {
165
+ if (fs.existsSync(path.join(root, "memory-bank/intents/intent.md"))) {
166
+ list.push("memory-bank/intents/intent.md");
167
+ }
168
+ list.push(...getLatestFiles(path.join(root, "memory-bank/logs"), 5));
169
+ }
170
+ else if (phase === "construction") {
171
+ list.push(...getLatestFiles(path.join(root, "memory-bank/bolts"), 5));
172
+ list.push(...getLatestFiles(path.join(root, "memory-bank/logs"), 5));
173
+ }
174
+ else if (phase === "operations") {
175
+ list.push(...getLatestFiles(path.join(root, "memory-bank/logs"), 10));
176
+ list.push(...getLatestFiles(path.join(root, "memory-bank/standards"), 5));
177
+ }
178
+ return list;
179
+ }
180
+ function getLatestFiles(dir, count) {
181
+ const root = repoRoot();
182
+ if (!fs.existsSync(dir))
183
+ return [];
184
+ const files = fs.readdirSync(dir)
185
+ .map(f => path.join(dir, f))
186
+ .filter(f => fs.statSync(f).isFile())
187
+ .map(f => ({ path: path.relative(root, f), mtime: fs.statSync(f).mtime.getTime() }))
188
+ .sort((a, b) => {
189
+ if (b.mtime !== a.mtime)
190
+ return b.mtime - a.mtime; // Descending time
191
+ return a.path.localeCompare(b.path); // Tie-break name
192
+ });
193
+ return files.slice(0, count).map(f => f.path);
194
+ }
195
+ function enforceLimits(core, optional, extra, maxBytes) {
196
+ let currentSize = sumSize(core) + sumSize(optional) + sumSize(extra);
197
+ const errors = [];
198
+ const skipped = [];
199
+ // 1. Reduce Optional
200
+ while (currentSize > maxBytes && optional.length > 0) {
201
+ const removed = optional.pop(); // Remove from end (which are older logs/bolts usually!)
202
+ // Wait, collectOptionalArtifacts sorted by latest first.
203
+ // So pop() removes the OLDEST (least relevant). Correct.
204
+ if (removed) {
205
+ currentSize -= (removed.size || 0);
206
+ skipped.push(removed.path);
207
+ }
208
+ }
209
+ // 2. Reduce Extra
210
+ while (currentSize > maxBytes && extra.length > 0) {
211
+ const removed = extra.pop(); // Remove from end of glob list (arbitrary?)
212
+ if (removed) {
213
+ currentSize -= (removed.size || 0);
214
+ skipped.push(removed.path);
215
+ }
216
+ }
217
+ if (currentSize > maxBytes) {
218
+ // Hard fail if Core is too big?
219
+ console.error(kleur.red(`Bundle size (${currentSize}) exceeds --max-bytes limit (${maxBytes}) even after trimming optional content.`));
220
+ console.error("Required content is too large. Increase limit or clean up artifacts.");
221
+ process.exit(EXIT_CODES.INVALID);
222
+ }
223
+ return {
224
+ core,
225
+ optional,
226
+ extra,
227
+ skipped
228
+ };
229
+ }
230
+ function sumSize(files) {
231
+ return files.reduce((acc, f) => acc + (f.content ? f.content.length : 0), 0);
232
+ }
233
+ function formatBundle(selection, meta) {
234
+ const { core, optional, extra, skipped } = selection;
235
+ let out = `# IRIS Context Pack\n\n`;
236
+ out += `## Metadata\n`;
237
+ out += `- Generated: ${meta.generatedAt}\n`;
238
+ out += `- Repo: ${meta.repo}\n`;
239
+ out += `- Phase: ${meta.phase}\n`;
240
+ out += `- Agent: ${meta.agent}\n`;
241
+ out += `- Navi: 0.0.07\n\n`; // Hardcoded version for now or read pkg
242
+ out += `## How to use\n`;
243
+ out += `- Run: /iris-${meta.agent}-agent\n`;
244
+ out += `- This bundle contains doctrine + required artifacts.\n\n`;
245
+ // Groups
246
+ const all = [...core, ...optional, ...extra];
247
+ // Separate Doctrine from others?
248
+ // Core has doctrine + state + required.
249
+ // Let's group by type roughly using path analysis
250
+ const doctrine = all.filter(f => f.path.startsWith(".iris/aidlc"));
251
+ const stateFiles = all.filter(f => f.path.startsWith(".iris/") && !f.path.startsWith(".iris/aidlc"));
252
+ const memory = all.filter(f => f.path.startsWith("memory-bank/"));
253
+ const others = all.filter(f => !f.path.startsWith(".iris/") && !f.path.startsWith("memory-bank/"));
254
+ const printer = (title, files) => {
255
+ if (files.length === 0)
256
+ return;
257
+ out += `## ${title}\n\n`;
258
+ files.forEach((f, idx) => {
259
+ if (f.skipped) {
260
+ // Should list in skipped section?
261
+ return;
262
+ }
263
+ if (!f.content && !f.skipped) {
264
+ // Missing file (e.g. Doctrine missing)
265
+ out += `### [${idx + 1}] ${f.path} (MISSING)\n\n`;
266
+ return;
267
+ }
268
+ out += `### [${idx + 1}] ${f.path}\n`;
269
+ out += "```" + detectLang(f.path) + "\n";
270
+ out += f.content + "\n";
271
+ out += "```\n\n";
272
+ });
273
+ };
274
+ printer("Doctrine (Load Order)", doctrine);
275
+ printer("Repo State", stateFiles);
276
+ printer("Memory Bank", memory);
277
+ printer("Included Context", others);
278
+ // Missing / Skipped
279
+ const missing = all.filter(f => !f.content && !f.skipped).map(f => f.path);
280
+ const skippedReasons = all.filter(f => f.skipped).map(f => `${f.path} (${f.skipped})`);
281
+ const droppedLimits = skipped;
282
+ if (missing.length > 0 || skippedReasons.length > 0 || droppedLimits.length > 0) {
283
+ out += `## Missing / Skipped Files\n\n`;
284
+ missing.forEach(p => out += `* ${p} (missing)\n`);
285
+ skippedReasons.forEach(p => out += `* ${p}\n`);
286
+ droppedLimits.forEach(p => out += `* ${p} (dropped for size)\n`);
287
+ }
288
+ return out;
289
+ }
290
+ function detectLang(p) {
291
+ const ext = path.extname(p);
292
+ if (ext === ".md")
293
+ return "markdown";
294
+ if (ext === ".yaml")
295
+ return "yaml";
296
+ if (ext === ".json")
297
+ return "json";
298
+ if (ext === ".ts")
299
+ return "typescript";
300
+ if (ext === ".js")
301
+ return "javascript";
302
+ return "";
303
+ }
@@ -0,0 +1,28 @@
1
+ import fs from "fs";
2
+ import yaml from "js-yaml";
3
+ import { EXIT_CODES } from "../utils/exit-codes.js";
4
+ import { resolveArtifactPath } from "./resolver.js";
5
+ import { repoRoot } from "../lib.js";
6
+ export function loadPolicy() {
7
+ try {
8
+ const root = repoRoot();
9
+ const policyPath = resolveArtifactPath(root, ".iris/policy.yaml");
10
+ if (!fs.existsSync(policyPath)) {
11
+ // Policy is REQUIRED.
12
+ console.error(`Missing required policy file: ${policyPath}`);
13
+ process.exit(EXIT_CODES.POLICY_ERROR);
14
+ }
15
+ const content = fs.readFileSync(policyPath, "utf8");
16
+ const doc = yaml.load(content);
17
+ // Basic structural check
18
+ if (!doc || !doc.phases || !doc.transitions) {
19
+ console.error("Invalid policy file format: 'phases' and 'transitions' are required.");
20
+ process.exit(EXIT_CODES.POLICY_ERROR);
21
+ }
22
+ return doc;
23
+ }
24
+ catch (error) {
25
+ console.error("Failed to load .iris/policy.yaml:", error);
26
+ process.exit(EXIT_CODES.POLICY_ERROR);
27
+ }
28
+ }
@@ -0,0 +1,53 @@
1
+ import kleur from "kleur";
2
+ export function printReport(result, json = false, fixResult, strict = false) {
3
+ if (json) {
4
+ console.log(JSON.stringify({
5
+ valid: result.valid,
6
+ warnings: result.warnings,
7
+ errors: result.errors,
8
+ fix_result: fixResult // Optional
9
+ }, null, 2));
10
+ return;
11
+ }
12
+ if (fixResult) {
13
+ console.log("");
14
+ console.log(kleur.bold().green("Applied fixes:"));
15
+ if (fixResult.paths.length === 0) {
16
+ console.log(kleur.gray(" (None needed)"));
17
+ }
18
+ else {
19
+ for (const p of fixResult.paths) {
20
+ console.log(kleur.green(` + Created: ${p}`));
21
+ }
22
+ }
23
+ console.log("");
24
+ console.log(kleur.bold(`Remaining issues (${result.errors.length}):`));
25
+ }
26
+ else {
27
+ if (result.valid && !result.warnings) {
28
+ console.log(kleur.green("✓ Repository is valid."));
29
+ return;
30
+ }
31
+ if (!result.valid) {
32
+ console.log(kleur.red(`✗ Validation failed with ${result.errors.length} error(s):`));
33
+ }
34
+ else {
35
+ if (strict) {
36
+ console.log(kleur.red("✗ Validation FAILED (strict) due to warnings:"));
37
+ }
38
+ else {
39
+ console.log(kleur.yellow("! Validation PASSED with warnings:"));
40
+ }
41
+ }
42
+ }
43
+ // Print errors
44
+ for (const err of result.errors) {
45
+ const icon = err.severity === "warning" ? kleur.yellow("!") : kleur.red("x");
46
+ const code = kleur.dim(`[${err.code}]`);
47
+ // If fixed, skip? No, this function prints current result.
48
+ // If called after fix, these are remaining errors.
49
+ console.log(` ${icon} ${code} ${err.message}`);
50
+ if (err.path)
51
+ console.log(kleur.dim(` Path: ${err.path}`));
52
+ }
53
+ }
@@ -0,0 +1,63 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { fileURLToPath } from "url";
4
+ // Get __dirname equivalent in ESM
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+ /**
8
+ * Resolves the root directory of the active IRIS configuration (.iris folder).
9
+ * Strategy:
10
+ * 1. Check <repoRoot>/.iris
11
+ * 2. Fallback to bundled <package>/.iris (e.g. ../iris_bundle/.iris)
12
+ */
13
+ export function resolveIrisRoot(repoRoot) {
14
+ const repoIris = path.join(repoRoot, ".iris");
15
+ // Check for policy.yaml to confirm this is a valid IRIS configuration authority
16
+ if (fs.existsSync(path.join(repoIris, "policy.yaml"))) {
17
+ return { path: repoIris, source: "repo" };
18
+ }
19
+ // Fallback to bundled
20
+ // We assume this file is in .../iris/resolver.js|ts
21
+ // And bundle is in .../iris_bundle/.iris
22
+ let bundledIris = path.resolve(__dirname, "../iris_bundle/.iris");
23
+ // Safety check (bundled should always exist, but in dev vs prod checks...)
24
+ if (!fs.existsSync(bundledIris)) {
25
+ // Try finding it in src if we are in dist (dev environment fallback)
26
+ const srcBundle = path.resolve(__dirname, "../../src/iris_bundle/.iris");
27
+ if (fs.existsSync(srcBundle)) {
28
+ bundledIris = srcBundle;
29
+ }
30
+ else {
31
+ console.warn(`[IRIS] Warning: Bundled IRIS not found at ${bundledIris}`);
32
+ }
33
+ }
34
+ return { path: bundledIris, source: "bundled" };
35
+ }
36
+ /**
37
+ * Resolves a relative path (like ".iris/policy.yaml" or "memory-bank/foo.md")
38
+ * to its absolute physical location on disk, respecting the IRIS source authority.
39
+ */
40
+ export function resolveArtifactPath(repoRoot, relativePath) {
41
+ if (relativePath.startsWith(".iris/") || relativePath === ".iris") {
42
+ const root = resolveIrisRoot(repoRoot);
43
+ if (relativePath === ".iris")
44
+ return root.path;
45
+ // internal path inside .iris
46
+ const subPath = relativePath.substring(6); // remove ".iris/"
47
+ return path.join(root.path, subPath);
48
+ }
49
+ // Standard repo file
50
+ return path.join(repoRoot, relativePath);
51
+ }
52
+ /**
53
+ * Debug helper to show resolution status
54
+ */
55
+ export function getIrisDebugInfo(repoRoot) {
56
+ const root = resolveIrisRoot(repoRoot);
57
+ return {
58
+ repoRoot,
59
+ irisRoot: root.path,
60
+ source: root.source,
61
+ bundledPath: path.resolve(__dirname, "../iris_bundle/.iris")
62
+ };
63
+ }
@@ -0,0 +1,114 @@
1
+ export function routeIntent(intent, routesConfig) {
2
+ const candidates = [];
3
+ for (const route of routesConfig.routes) {
4
+ // 1. Check exclusions
5
+ if (checkExclusion(intent, route.exclude)) {
6
+ continue;
7
+ }
8
+ // 2. Score Match
9
+ const { score, matches } = scoreRoute(intent, route);
10
+ // Qualification:
11
+ if (score > 0) {
12
+ candidates.push({ route, score, matches });
13
+ }
14
+ }
15
+ // Sort: 1. Score desc, 2. Longest match count desc, 3. Route order (stable sort)
16
+ candidates.sort((a, b) => {
17
+ if (b.score !== a.score)
18
+ return b.score - a.score;
19
+ if (b.matches.length !== a.matches.length)
20
+ return b.matches.length - a.matches.length;
21
+ return 0;
22
+ });
23
+ if (candidates.length > 0) {
24
+ return {
25
+ route: candidates[0].route,
26
+ score: candidates[0].score,
27
+ matches: candidates[0].matches
28
+ };
29
+ }
30
+ // Default
31
+ return {
32
+ route: "default",
33
+ score: 0,
34
+ matches: []
35
+ };
36
+ }
37
+ function checkExclusion(intent, exclude) {
38
+ if (!exclude)
39
+ return false;
40
+ const lower = intent.toLowerCase();
41
+ // Any / Any Keywords
42
+ const anyList = [...(exclude.any || []), ...(exclude.any_keywords || [])];
43
+ if (anyList.length > 0) {
44
+ for (const kw of anyList) {
45
+ if (lower.includes(kw.toLowerCase()))
46
+ return true;
47
+ }
48
+ }
49
+ // Regex
50
+ if (exclude.regex) {
51
+ for (const pattern of exclude.regex) {
52
+ try {
53
+ if (new RegExp(pattern, "i").test(intent))
54
+ return true;
55
+ }
56
+ catch (e) {
57
+ console.warn(`Invalid exclude regex: ${pattern}`);
58
+ }
59
+ }
60
+ }
61
+ return false;
62
+ }
63
+ function scoreRoute(intent, route) {
64
+ let score = 0;
65
+ const matches = [];
66
+ const lower = intent.toLowerCase();
67
+ const rules = route.match;
68
+ // 1. Check ALL
69
+ const allList = [...(rules.all || []), ...(rules.all_keywords || [])];
70
+ if (allList.length > 0) {
71
+ for (const kw of allList) {
72
+ if (lower.includes(kw.toLowerCase())) {
73
+ score += 1;
74
+ matches.push(`all:${kw}`);
75
+ }
76
+ else {
77
+ return { score: 0, matches: [] }; // Failed ALL condition
78
+ }
79
+ }
80
+ }
81
+ // 2. Check ANY + Phrases
82
+ const anyList = [
83
+ ...(rules.any || []),
84
+ ...(rules.any_keywords || []),
85
+ ...(rules.phrases || [])
86
+ ];
87
+ if (anyList.length > 0) {
88
+ for (const kw of anyList) {
89
+ if (lower.includes(kw.toLowerCase())) {
90
+ score += 1;
91
+ matches.push(`any:${kw}`);
92
+ }
93
+ }
94
+ }
95
+ // 3. Check Regex
96
+ if (rules.regex) {
97
+ for (const pattern of rules.regex) {
98
+ try {
99
+ if (new RegExp(pattern, "i").test(intent)) {
100
+ score += 2;
101
+ matches.push(`regex:${pattern}`);
102
+ }
103
+ }
104
+ catch (e) {
105
+ console.warn(`Invalid match regex in route ${route.route_id}: ${pattern}`);
106
+ }
107
+ }
108
+ }
109
+ // Boost
110
+ if (score > 0 && route.boost) {
111
+ score += route.boost;
112
+ }
113
+ return { score, matches };
114
+ }
@@ -0,0 +1,20 @@
1
+ import fs from "fs";
2
+ import yaml from "js-yaml";
3
+ import { repoRoot } from "../lib.js";
4
+ import { resolveArtifactPath } from "./resolver.js";
5
+ import { EXIT_CODES } from "../utils/exit-codes.js";
6
+ export function loadRoutes() {
7
+ const root = repoRoot();
8
+ const routesPath = resolveArtifactPath(root, ".iris/routes.yaml");
9
+ if (!fs.existsSync(routesPath)) {
10
+ return null;
11
+ }
12
+ try {
13
+ const content = fs.readFileSync(routesPath, "utf8");
14
+ return yaml.load(content);
15
+ }
16
+ catch (e) {
17
+ console.error("Failed to parse .iris/routes.yaml:", e);
18
+ process.exit(EXIT_CODES.POLICY_ERROR);
19
+ }
20
+ }