@claude-flow/cli 3.11.0 → 3.12.1

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 (192) hide show
  1. package/dist/src/commands/doctor.d.ts.map +1 -1
  2. package/dist/src/commands/doctor.js +175 -1
  3. package/dist/src/commands/doctor.js.map +1 -1
  4. package/dist/src/commands/eject.d.ts +33 -0
  5. package/dist/src/commands/eject.d.ts.map +1 -0
  6. package/dist/src/commands/eject.js +195 -0
  7. package/dist/src/commands/eject.js.map +1 -0
  8. package/dist/src/commands/hooks.d.ts.map +1 -1
  9. package/dist/src/commands/hooks.js +2 -1
  10. package/dist/src/commands/hooks.js.map +1 -1
  11. package/dist/src/commands/index.d.ts.map +1 -1
  12. package/dist/src/commands/index.js +4 -0
  13. package/dist/src/commands/index.js.map +1 -1
  14. package/dist/src/commands/init.d.ts.map +1 -1
  15. package/dist/src/commands/init.js +3 -0
  16. package/dist/src/commands/init.js.map +1 -1
  17. package/dist/src/commands/metaharness.d.ts +39 -0
  18. package/dist/src/commands/metaharness.d.ts.map +1 -0
  19. package/dist/src/commands/metaharness.js +194 -0
  20. package/dist/src/commands/metaharness.js.map +1 -0
  21. package/dist/src/mcp-client.d.ts.map +1 -1
  22. package/dist/src/mcp-client.js +4 -0
  23. package/dist/src/mcp-client.js.map +1 -1
  24. package/dist/src/mcp-tools/index.d.ts +1 -0
  25. package/dist/src/mcp-tools/index.d.ts.map +1 -1
  26. package/dist/src/mcp-tools/index.js +2 -0
  27. package/dist/src/mcp-tools/index.js.map +1 -1
  28. package/dist/src/mcp-tools/metaharness-tools.d.ts +31 -0
  29. package/dist/src/mcp-tools/metaharness-tools.d.ts.map +1 -0
  30. package/dist/src/mcp-tools/metaharness-tools.js +372 -0
  31. package/dist/src/mcp-tools/metaharness-tools.js.map +1 -0
  32. package/dist/src/plugins/store/discovery.d.ts.map +1 -1
  33. package/dist/src/plugins/store/discovery.js +3 -0
  34. package/dist/src/plugins/store/discovery.js.map +1 -1
  35. package/dist/src/plugins/store/types.d.ts +6 -1
  36. package/dist/src/plugins/store/types.d.ts.map +1 -1
  37. package/dist/src/ruvector/model-router.d.ts.map +1 -1
  38. package/dist/src/ruvector/model-router.js +37 -0
  39. package/dist/src/ruvector/model-router.js.map +1 -1
  40. package/dist/src/ruvector/router-parallel-recorder.d.ts +127 -0
  41. package/dist/src/ruvector/router-parallel-recorder.d.ts.map +1 -0
  42. package/dist/src/ruvector/router-parallel-recorder.js +183 -0
  43. package/dist/src/ruvector/router-parallel-recorder.js.map +1 -0
  44. package/dist/tsconfig.tsbuildinfo +1 -1
  45. package/package.json +13 -10
  46. package/plugins/ruflo-metaharness/.claude-flow/data/pending-insights.jsonl +1 -0
  47. package/plugins/ruflo-metaharness/.claude-flow/neural/stats.json +6 -0
  48. package/plugins/ruflo-metaharness/.claude-plugin/plugin.json +32 -0
  49. package/plugins/ruflo-metaharness/README.md +64 -0
  50. package/plugins/ruflo-metaharness/agents/metaharness-architect.md +58 -0
  51. package/plugins/ruflo-metaharness/commands/ruflo-metaharness.md +46 -0
  52. package/plugins/ruflo-metaharness/scripts/_harness.mjs +261 -0
  53. package/plugins/ruflo-metaharness/scripts/_similarity.mjs +161 -0
  54. package/plugins/ruflo-metaharness/scripts/_spike-similarity.mjs +223 -0
  55. package/plugins/ruflo-metaharness/scripts/audit-list.mjs +158 -0
  56. package/plugins/ruflo-metaharness/scripts/audit-trend.mjs +272 -0
  57. package/plugins/ruflo-metaharness/scripts/bench-parse-mcp-scan.mjs +146 -0
  58. package/plugins/ruflo-metaharness/scripts/bench-recordpair-overhead.mjs +186 -0
  59. package/plugins/ruflo-metaharness/scripts/bench-similarity.mjs +177 -0
  60. package/plugins/ruflo-metaharness/scripts/drift-from-history.mjs +363 -0
  61. package/plugins/ruflo-metaharness/scripts/genome.mjs +80 -0
  62. package/plugins/ruflo-metaharness/scripts/mcp-scan.mjs +111 -0
  63. package/plugins/ruflo-metaharness/scripts/mint.mjs +119 -0
  64. package/plugins/ruflo-metaharness/scripts/oia-audit.mjs +228 -0
  65. package/plugins/ruflo-metaharness/scripts/router-parallel-analyze.mjs +250 -0
  66. package/plugins/ruflo-metaharness/scripts/score.mjs +92 -0
  67. package/plugins/ruflo-metaharness/scripts/similarity.mjs +158 -0
  68. package/plugins/ruflo-metaharness/scripts/smoke.sh +2268 -0
  69. package/plugins/ruflo-metaharness/scripts/test-graceful-degradation.mjs +141 -0
  70. package/plugins/ruflo-metaharness/scripts/test-mcp-tools.mjs +437 -0
  71. package/plugins/ruflo-metaharness/scripts/test-parallel-pipeline.mjs +204 -0
  72. package/plugins/ruflo-metaharness/scripts/test-pipeline-roundtrip.mjs +586 -0
  73. package/plugins/ruflo-metaharness/scripts/test-similarity.mjs +334 -0
  74. package/plugins/ruflo-metaharness/scripts/test-with-openrouter.mjs +229 -0
  75. package/plugins/ruflo-metaharness/scripts/threat-model.mjs +59 -0
  76. package/plugins/ruflo-metaharness/skills/harness-drift-from-history/SKILL.md +65 -0
  77. package/plugins/ruflo-metaharness/skills/harness-genome/SKILL.md +54 -0
  78. package/plugins/ruflo-metaharness/skills/harness-mcp-scan/SKILL.md +47 -0
  79. package/plugins/ruflo-metaharness/skills/harness-mint/SKILL.md +72 -0
  80. package/plugins/ruflo-metaharness/skills/harness-oia-audit/SKILL.md +79 -0
  81. package/plugins/ruflo-metaharness/skills/harness-score/SKILL.md +66 -0
  82. package/plugins/ruflo-metaharness/skills/harness-similarity/SKILL.md +67 -0
  83. package/plugins/ruflo-metaharness/skills/harness-threat-model/SKILL.md +39 -0
  84. package/dist/src/benchmarks/capability-tasks.d.ts +0 -126
  85. package/dist/src/benchmarks/capability-tasks.d.ts.map +0 -1
  86. package/dist/src/benchmarks/capability-tasks.js +0 -147
  87. package/dist/src/benchmarks/capability-tasks.js.map +0 -1
  88. package/dist/src/benchmarks/gaia-agent-contrastive.d.ts +0 -53
  89. package/dist/src/benchmarks/gaia-agent-contrastive.d.ts.map +0 -1
  90. package/dist/src/benchmarks/gaia-agent-contrastive.js +0 -177
  91. package/dist/src/benchmarks/gaia-agent-contrastive.js.map +0 -1
  92. package/dist/src/benchmarks/gaia-agent-gemini.d.ts +0 -51
  93. package/dist/src/benchmarks/gaia-agent-gemini.d.ts.map +0 -1
  94. package/dist/src/benchmarks/gaia-agent-gemini.js +0 -352
  95. package/dist/src/benchmarks/gaia-agent-gemini.js.map +0 -1
  96. package/dist/src/benchmarks/gaia-claude-p.d.ts +0 -105
  97. package/dist/src/benchmarks/gaia-claude-p.d.ts.map +0 -1
  98. package/dist/src/benchmarks/gaia-claude-p.js +0 -269
  99. package/dist/src/benchmarks/gaia-claude-p.js.map +0 -1
  100. package/dist/src/benchmarks/gaia-claude-p.smoke.d.ts +0 -20
  101. package/dist/src/benchmarks/gaia-claude-p.smoke.d.ts.map +0 -1
  102. package/dist/src/benchmarks/gaia-claude-p.smoke.js +0 -168
  103. package/dist/src/benchmarks/gaia-claude-p.smoke.js.map +0 -1
  104. package/dist/src/benchmarks/gaia-codeagent-runner.py +0 -556
  105. package/dist/src/benchmarks/gaia-codeagent.d.ts +0 -129
  106. package/dist/src/benchmarks/gaia-codeagent.d.ts.map +0 -1
  107. package/dist/src/benchmarks/gaia-codeagent.js +0 -590
  108. package/dist/src/benchmarks/gaia-codeagent.js.map +0 -1
  109. package/dist/src/benchmarks/gaia-codeagent.smoke.d.ts +0 -33
  110. package/dist/src/benchmarks/gaia-codeagent.smoke.d.ts.map +0 -1
  111. package/dist/src/benchmarks/gaia-codeagent.smoke.js +0 -241
  112. package/dist/src/benchmarks/gaia-codeagent.smoke.js.map +0 -1
  113. package/dist/src/benchmarks/gaia-dag-pilot.d.ts +0 -22
  114. package/dist/src/benchmarks/gaia-dag-pilot.d.ts.map +0 -1
  115. package/dist/src/benchmarks/gaia-dag-pilot.js +0 -158
  116. package/dist/src/benchmarks/gaia-dag-pilot.js.map +0 -1
  117. package/dist/src/benchmarks/gaia-dag.d.ts +0 -110
  118. package/dist/src/benchmarks/gaia-dag.d.ts.map +0 -1
  119. package/dist/src/benchmarks/gaia-dag.js +0 -658
  120. package/dist/src/benchmarks/gaia-dag.js.map +0 -1
  121. package/dist/src/benchmarks/gaia-dag.smoke.d.ts +0 -21
  122. package/dist/src/benchmarks/gaia-dag.smoke.d.ts.map +0 -1
  123. package/dist/src/benchmarks/gaia-dag.smoke.js +0 -290
  124. package/dist/src/benchmarks/gaia-dag.smoke.js.map +0 -1
  125. package/dist/src/benchmarks/gaia-ensemble-pilot.d.ts +0 -17
  126. package/dist/src/benchmarks/gaia-ensemble-pilot.d.ts.map +0 -1
  127. package/dist/src/benchmarks/gaia-ensemble-pilot.js +0 -106
  128. package/dist/src/benchmarks/gaia-ensemble-pilot.js.map +0 -1
  129. package/dist/src/benchmarks/gaia-ensemble.d.ts +0 -103
  130. package/dist/src/benchmarks/gaia-ensemble.d.ts.map +0 -1
  131. package/dist/src/benchmarks/gaia-ensemble.js +0 -462
  132. package/dist/src/benchmarks/gaia-ensemble.js.map +0 -1
  133. package/dist/src/benchmarks/gaia-gemini-pilot.d.ts +0 -17
  134. package/dist/src/benchmarks/gaia-gemini-pilot.d.ts.map +0 -1
  135. package/dist/src/benchmarks/gaia-gemini-pilot.js +0 -168
  136. package/dist/src/benchmarks/gaia-gemini-pilot.js.map +0 -1
  137. package/dist/src/benchmarks/gaia-mode-router.d.ts +0 -67
  138. package/dist/src/benchmarks/gaia-mode-router.d.ts.map +0 -1
  139. package/dist/src/benchmarks/gaia-mode-router.js +0 -126
  140. package/dist/src/benchmarks/gaia-mode-router.js.map +0 -1
  141. package/dist/src/benchmarks/gaia-mode-router.smoke.d.ts +0 -15
  142. package/dist/src/benchmarks/gaia-mode-router.smoke.d.ts.map +0 -1
  143. package/dist/src/benchmarks/gaia-mode-router.smoke.js +0 -131
  144. package/dist/src/benchmarks/gaia-mode-router.smoke.js.map +0 -1
  145. package/dist/src/benchmarks/gaia-tools/grounded_query.smoke.d.ts +0 -24
  146. package/dist/src/benchmarks/gaia-tools/grounded_query.smoke.d.ts.map +0 -1
  147. package/dist/src/benchmarks/gaia-tools/grounded_query.smoke.js +0 -276
  148. package/dist/src/benchmarks/gaia-tools/grounded_query.smoke.js.map +0 -1
  149. package/dist/src/benchmarks/gaia-tools/image_describe.d.ts +0 -45
  150. package/dist/src/benchmarks/gaia-tools/image_describe.d.ts.map +0 -1
  151. package/dist/src/benchmarks/gaia-tools/image_describe.js +0 -222
  152. package/dist/src/benchmarks/gaia-tools/image_describe.js.map +0 -1
  153. package/dist/src/benchmarks/gaia-tools/image_describe.smoke.d.ts +0 -24
  154. package/dist/src/benchmarks/gaia-tools/image_describe.smoke.d.ts.map +0 -1
  155. package/dist/src/benchmarks/gaia-tools/image_describe.smoke.js +0 -169
  156. package/dist/src/benchmarks/gaia-tools/image_describe.smoke.js.map +0 -1
  157. package/dist/src/benchmarks/gaia-tools/pdf_read.d.ts +0 -27
  158. package/dist/src/benchmarks/gaia-tools/pdf_read.d.ts.map +0 -1
  159. package/dist/src/benchmarks/gaia-tools/pdf_read.js +0 -226
  160. package/dist/src/benchmarks/gaia-tools/pdf_read.js.map +0 -1
  161. package/dist/src/benchmarks/gaia-tools/python_exec.d.ts +0 -31
  162. package/dist/src/benchmarks/gaia-tools/python_exec.d.ts.map +0 -1
  163. package/dist/src/benchmarks/gaia-tools/python_exec.js +0 -94
  164. package/dist/src/benchmarks/gaia-tools/python_exec.js.map +0 -1
  165. package/dist/src/benchmarks/gaia-tools/python_exec.smoke.d.ts +0 -13
  166. package/dist/src/benchmarks/gaia-tools/python_exec.smoke.d.ts.map +0 -1
  167. package/dist/src/benchmarks/gaia-tools/python_exec.smoke.js +0 -98
  168. package/dist/src/benchmarks/gaia-tools/python_exec.smoke.js.map +0 -1
  169. package/dist/src/benchmarks/gaia-tools/visit_webpage.d.ts +0 -52
  170. package/dist/src/benchmarks/gaia-tools/visit_webpage.d.ts.map +0 -1
  171. package/dist/src/benchmarks/gaia-tools/visit_webpage.js +0 -304
  172. package/dist/src/benchmarks/gaia-tools/visit_webpage.js.map +0 -1
  173. package/dist/src/benchmarks/gaia-tools/web_browse.d.ts +0 -61
  174. package/dist/src/benchmarks/gaia-tools/web_browse.d.ts.map +0 -1
  175. package/dist/src/benchmarks/gaia-tools/web_browse.js +0 -217
  176. package/dist/src/benchmarks/gaia-tools/web_browse.js.map +0 -1
  177. package/dist/src/benchmarks/gaia-tools/web_browse.smoke.d.ts +0 -26
  178. package/dist/src/benchmarks/gaia-tools/web_browse.smoke.d.ts.map +0 -1
  179. package/dist/src/benchmarks/gaia-tools/web_browse.smoke.js +0 -152
  180. package/dist/src/benchmarks/gaia-tools/web_browse.smoke.js.map +0 -1
  181. package/dist/src/benchmarks/gaia-tools/web_search.smoke.d.ts +0 -24
  182. package/dist/src/benchmarks/gaia-tools/web_search.smoke.d.ts.map +0 -1
  183. package/dist/src/benchmarks/gaia-tools/web_search.smoke.js +0 -311
  184. package/dist/src/benchmarks/gaia-tools/web_search.smoke.js.map +0 -1
  185. package/dist/src/benchmarks/gaia-visit-webpage.smoke.d.ts +0 -25
  186. package/dist/src/benchmarks/gaia-visit-webpage.smoke.d.ts.map +0 -1
  187. package/dist/src/benchmarks/gaia-visit-webpage.smoke.js +0 -304
  188. package/dist/src/benchmarks/gaia-visit-webpage.smoke.js.map +0 -1
  189. package/dist/src/commands/performance-capability.d.ts +0 -30
  190. package/dist/src/commands/performance-capability.d.ts.map +0 -1
  191. package/dist/src/commands/performance-capability.js +0 -349
  192. package/dist/src/commands/performance-capability.js.map +0 -1
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env node
2
+ // mcp-scan.mjs — wrapper around `harness mcp-scan <path>`.
3
+ //
4
+ // Static security scan of the harness's declared MCP surface. Reads
5
+ // .mcp/servers.json + .harness/claims.json. Pure-read, no dispatch.
6
+ //
7
+ // USAGE
8
+ // node scripts/mcp-scan.mjs # current dir
9
+ // node scripts/mcp-scan.mjs --path <dir>
10
+ // node scripts/mcp-scan.mjs --fail-on high # exit 1 if any HIGH finding (default)
11
+ // node scripts/mcp-scan.mjs --fail-on medium # also fail on MEDIUM
12
+ // node scripts/mcp-scan.mjs --format json
13
+ //
14
+ // EXIT CODES
15
+ // 0 no findings at or above --fail-on (or degraded)
16
+ // 1 at least one finding ≥ --fail-on severity
17
+ // 2 config error or scan failure
18
+
19
+ // iter 50 — parseMcpScanText extracted to _harness.mjs so oia-audit
20
+ // can use the same parser without duplicating logic.
21
+ // iter 63 — SEVERITY_RANK also moves to _harness.mjs (single source of
22
+ // truth; previously this file had a literal that diverged from oia-audit's).
23
+ import { runHarness, emitDegradedJsonAndExit, parseMcpScanText, SEVERITY_RANK, rankSeverity } from './_harness.mjs';
24
+
25
+ const ARGS = (() => {
26
+ const a = { path: '.', format: 'json', failOn: 'high' };
27
+ for (let i = 2; i < process.argv.length; i++) {
28
+ const v = process.argv[i];
29
+ if (v === '--path') a.path = process.argv[++i];
30
+ else if (v === '--fail-on') a.failOn = String(process.argv[++i] || 'high').toLowerCase();
31
+ else if (v === '--format') a.format = process.argv[++i];
32
+ }
33
+ return a;
34
+ })();
35
+
36
+ function main() {
37
+ if (!SEVERITY_RANK[ARGS.failOn]) {
38
+ console.error(`mcp-scan: --fail-on must be one of low|medium|high; got ${ARGS.failOn}`);
39
+ process.exit(2);
40
+ }
41
+ const r = runHarness(['mcp-scan', ARGS.path]);
42
+ if (r.degraded) { emitDegradedJsonAndExit(r.reason); return; }
43
+ if (r.exitCode !== 0 && r.exitCode !== 1) {
44
+ // exit 1 from harness can be "findings present"; only treat other
45
+ // non-zero as a real failure.
46
+ console.error(`mcp-scan: harness exited ${r.exitCode}`);
47
+ if (r.stderr) console.error(r.stderr.slice(0, 400));
48
+ process.exit(2);
49
+ }
50
+ // The JSON output shape from `harness mcp-scan` historically didn't
51
+ // include structured findings — it emits text even with --json. Parse
52
+ // the text into findings (iter 50) so audit-trend's introduced/cleared
53
+ // diff actually works. If a future upstream version DOES emit
54
+ // findings[], the parsed-text findings are overridden.
55
+ const parsed = parseMcpScanText(r.stdout);
56
+ // iter 124 — normalize finding shape so consumers see a stable contract.
57
+ // Upstream metaharness@0.1.x JSON path emits {id, severity, title, detail};
58
+ // our parseMcpScanText emits {severity, message}. Project both into a
59
+ // unified {severity, message, title?, detail?} so audit-trend's fingerprint
60
+ // diff and test-mcp-tools' assertions don't care which path produced them.
61
+ const rawFindings = Array.isArray(r.json?.findings) ? r.json.findings : parsed.findings;
62
+ const findings = rawFindings.map((f) => ({
63
+ ...f,
64
+ message: typeof f.message === 'string' && f.message
65
+ ? f.message
66
+ : (f.title || f.detail || ''),
67
+ }));
68
+ const payload = {
69
+ ...(r.json ?? {}),
70
+ findings,
71
+ summary: r.json?.summary ?? parsed.summary,
72
+ rawStdout: r.stdout.slice(0, 400),
73
+ };
74
+ const threshold = SEVERITY_RANK[ARGS.failOn];
75
+ // iter 63 — rankSeverity() safe lookup. Pre-iter-63 `undefined >= 3`
76
+ // was false → unknown-severity findings (e.g., warn / error) were
77
+ // silently excluded from the offending set, weakening --fail-on alerts.
78
+ const offending = findings.filter((f) => rankSeverity(f.severity) >= threshold);
79
+
80
+ const alert = {
81
+ threshold: ARGS.failOn,
82
+ triggered: offending.length > 0,
83
+ offendingCount: offending.length,
84
+ reason: offending.length > 0
85
+ ? `${offending.length} finding(s) at or above ${ARGS.failOn} severity`
86
+ : `no findings at or above ${ARGS.failOn} severity — OK`,
87
+ };
88
+
89
+ if (ARGS.format === 'json') {
90
+ // iter 112 — generatedAt for consistency with other --format json outputs
91
+ console.log(JSON.stringify({
92
+ ...payload, durationMs: r.durationMs, alert,
93
+ generatedAt: new Date().toISOString(),
94
+ }, null, 2));
95
+ } else {
96
+ console.log(`# harness mcp-scan — ${ARGS.path}`);
97
+ console.log('');
98
+ console.log(`Total findings: ${findings.length}`);
99
+ console.log(`| Severity | ID | Server | Tool | Message |`);
100
+ console.log(`|---|---|---|---|---|`);
101
+ for (const f of findings.slice(0, 50)) {
102
+ console.log(`| ${f.severity} | ${f.id ?? '—'} | ${f.server ?? '—'} | ${f.tool ?? '—'} | ${f.message ?? ''} |`);
103
+ }
104
+ console.log('');
105
+ console.log(alert.triggered ? `⚠ **ALERT**: ${alert.reason}` : `✓ ${alert.reason}`);
106
+ }
107
+
108
+ if (alert.triggered) process.exit(1);
109
+ }
110
+
111
+ main();
@@ -0,0 +1,119 @@
1
+ #!/usr/bin/env node
2
+ // mint.mjs — wrapper around `metaharness new <name> --template <id> --host <id>`.
3
+ //
4
+ // SAFETY (ADR-150 architectural constraint + "executing actions with care"):
5
+ // - Target directory MUST be explicitly specified via --target. Defaults to
6
+ // a temp dir under /tmp/ruflo-mint-<timestamp>/ if not given — never the
7
+ // project root (ruflo behavioral rule "never save to root folder").
8
+ // - --confirm flag required. Without it the script prints a dry-run plan
9
+ // and exits 0 without writing files. This honors the "destructive-action
10
+ // confirmation" pattern in ruflo's CLAUDE.md.
11
+ //
12
+ // USAGE
13
+ // node scripts/mint.mjs --name my-harness --template vertical:coding --host claude-code
14
+ // # → prints dry-run plan, exits 0
15
+ // node scripts/mint.mjs --name my-harness --template vertical:coding --host claude-code --target /tmp/foo --confirm
16
+ // # → actually scaffolds
17
+
18
+ import { runMetaharness, emitDegradedJsonAndExit } from './_harness.mjs';
19
+ import { existsSync, mkdirSync } from 'node:fs';
20
+ import { resolve, dirname, basename } from 'node:path';
21
+ import { tmpdir } from 'node:os';
22
+
23
+ const ARGS = (() => {
24
+ const a = { name: null, template: null, host: 'claude-code', target: null, confirm: false, format: 'json' };
25
+ for (let i = 2; i < process.argv.length; i++) {
26
+ const v = process.argv[i];
27
+ if (v === '--name') a.name = process.argv[++i];
28
+ else if (v === '--template') a.template = process.argv[++i];
29
+ else if (v === '--host') a.host = process.argv[++i];
30
+ else if (v === '--target') a.target = process.argv[++i];
31
+ else if (v === '--confirm') a.confirm = true;
32
+ else if (v === '--format') a.format = process.argv[++i];
33
+ }
34
+ return a;
35
+ })();
36
+
37
+ function safetyChecks() {
38
+ if (!ARGS.name) {
39
+ console.error('mint: --name is required');
40
+ process.exit(2);
41
+ }
42
+ if (!ARGS.template) {
43
+ console.error('mint: --template is required (e.g. vertical:coding, minimal)');
44
+ process.exit(2);
45
+ }
46
+ if (!ARGS.target) {
47
+ ARGS.target = resolve(tmpdir(), `ruflo-mint-${Date.now()}-${ARGS.name}`);
48
+ }
49
+ const targetAbs = resolve(ARGS.target);
50
+ const repoRoot = resolve(process.cwd());
51
+ if (targetAbs === repoRoot) {
52
+ console.error(`mint: refusing to write to project root (${repoRoot}). Specify a different --target.`);
53
+ process.exit(2);
54
+ }
55
+ if (targetAbs.startsWith(repoRoot + '/')) {
56
+ console.error(`mint: refusing to write inside the calling repo root. Use a --target outside ${repoRoot}.`);
57
+ process.exit(2);
58
+ }
59
+ }
60
+
61
+ function main() {
62
+ safetyChecks();
63
+ const plan = {
64
+ action: 'metaharness new',
65
+ name: ARGS.name,
66
+ template: ARGS.template,
67
+ host: ARGS.host,
68
+ target: ARGS.target,
69
+ confirm: ARGS.confirm,
70
+ willWrite: ARGS.confirm,
71
+ };
72
+ if (!ARGS.confirm) {
73
+ if (ARGS.format === 'json') console.log(JSON.stringify({ ...plan, dryRun: true }, null, 2));
74
+ else {
75
+ console.log(`# harness-mint (dry-run)`);
76
+ console.log('');
77
+ for (const [k, v] of Object.entries(plan)) console.log(`- ${k}: ${v}`);
78
+ console.log('');
79
+ console.log('Re-run with `--confirm` to actually scaffold.');
80
+ }
81
+ process.exit(0);
82
+ }
83
+ if (existsSync(ARGS.target)) {
84
+ console.error(`mint: target ${ARGS.target} already exists`);
85
+ process.exit(2);
86
+ }
87
+ // ITER 27 FIX (forward-compatible) — at the time of writing,
88
+ // metaharness@0.1.12 silently ignored the --target flag and wrote
89
+ // to $CWD/<name> instead. Upstream issue #9
90
+ // (ruvnet/agent-harness-generator) was filed + fixed in 0.1.13
91
+ // within ~20 min — but this workaround is intentionally
92
+ // forward-compatible: setting `cwd: dirname(target)` + passing
93
+ // `basename(target)` as <name> produces the same correct landing
94
+ // path on BOTH broken (0.1.12) and fixed (0.1.13+) upstream
95
+ // versions. Keeping it as belt-and-braces against any future
96
+ // regression. See `iter 29` for the verification log.
97
+ const parentDir = dirname(ARGS.target);
98
+ const cliName = basename(ARGS.target);
99
+ mkdirSync(parentDir, { recursive: true });
100
+ const r = runMetaharness(
101
+ ['new', cliName, '--template', ARGS.template, '--host', ARGS.host],
102
+ { json: false, cwd: parentDir, timeoutMs: 180_000 },
103
+ );
104
+ if (r.degraded) { emitDegradedJsonAndExit(r.reason); return; }
105
+ if (r.exitCode !== 0) {
106
+ console.error(`mint: metaharness exited ${r.exitCode}`);
107
+ if (r.stderr) console.error(r.stderr.slice(0, 400));
108
+ process.exit(2);
109
+ }
110
+ const result = { ...plan, exitCode: r.exitCode, durationMs: r.durationMs, ok: true };
111
+ if (ARGS.format === 'json') console.log(JSON.stringify(result, null, 2));
112
+ else {
113
+ console.log(`# harness-mint — done`);
114
+ console.log('');
115
+ for (const [k, v] of Object.entries(result)) console.log(`- ${k}: ${v}`);
116
+ }
117
+ }
118
+
119
+ main();
@@ -0,0 +1,228 @@
1
+ #!/usr/bin/env node
2
+ // oia-audit.mjs — composite Phase-2 worker (ADR-150).
3
+ //
4
+ // Bundles three MetaHarness static-analysis surfaces into one timestamped
5
+ // audit record:
6
+ // - harness oia-manifest (Open Infrastructure Architecture L1-L9 alignment)
7
+ // - harness threat-model (categorized MCP-surface threat report)
8
+ // - harness mcp-scan (per-server/tool policy + permissions + deps)
9
+ //
10
+ // The combined record is stored in the `metaharness-audit` memory
11
+ // namespace, keyed by ISO timestamp. Designed to be invoked on a cron
12
+ // schedule (e.g. weekly) so audit drift is visible over time.
13
+ //
14
+ // USAGE
15
+ // node scripts/oia-audit.mjs # run + store
16
+ // node scripts/oia-audit.mjs --path <dir> # audit specific dir
17
+ // node scripts/oia-audit.mjs --dry-run # don't write to memory
18
+ // node scripts/oia-audit.mjs --alert-on-worst high
19
+ // # exit 1 if threat-model worst >= high
20
+ // node scripts/oia-audit.mjs --format json
21
+ //
22
+ // EXIT CODES
23
+ // 0 audit OK (or degraded)
24
+ // 1 --alert-on-worst threshold exceeded
25
+ // 2 config error or audit failure
26
+
27
+ import { spawnSync } from 'node:child_process';
28
+ // iter 63 — SEVERITY_RANK + rankSeverity consolidated to _harness.mjs
29
+ // (was local in iter 62; now shared with audit-trend + mcp-scan).
30
+ import { runHarness, runMetaharness, runHarnessAsync, runMetaharnessAsync, emitDegradedJsonAndExit, parseMcpScanText, SEVERITY_RANK, rankSeverity } from './_harness.mjs';
31
+
32
+ // iter 63 — SEVERITY_RANK + rankSeverity moved to _harness.mjs (single
33
+ // source of truth). The local copy from iter 62 is gone; the imports
34
+ // at the top of this file provide the same names.
35
+ const NS = process.env.OIA_AUDIT_NAMESPACE || 'metaharness-audit';
36
+ const CLI_PKG = process.env.CLI_CORE === '1'
37
+ ? '@claude-flow/cli-core@alpha'
38
+ : '@claude-flow/cli@latest';
39
+
40
+ const ARGS = (() => {
41
+ const a = { path: '.', format: 'json', dryRun: false, alertWorst: null };
42
+ for (let i = 2; i < process.argv.length; i++) {
43
+ const v = process.argv[i];
44
+ if (v === '--path') a.path = process.argv[++i];
45
+ else if (v === '--dry-run') a.dryRun = true;
46
+ else if (v === '--alert-on-worst') a.alertWorst = String(process.argv[++i] || '').toLowerCase();
47
+ else if (v === '--format') a.format = process.argv[++i];
48
+ }
49
+ return a;
50
+ })();
51
+
52
+ // iter 47 — the `harness` and `metaharness` CLIs both have `score` /
53
+ // `genome` subcommands but with DIFFERENT output schemas. For the
54
+ // fingerprint to be consumable by _similarity.mjs (which expects the
55
+ // metaharness CLI shape: harnessFit/compileConfidence/agent_topology),
56
+ // we need to dispatch to runMetaharness for score+genome, not runHarness.
57
+ // iter 56 — annotated runOne result is identical for sync/async paths.
58
+ function annotateOne(r, label) {
59
+ let json = r.json;
60
+ if (label === 'mcp-scan' && !json && !r.degraded && r.stdout) {
61
+ const parsed = parseMcpScanText(r.stdout);
62
+ json = { findings: parsed.findings, summary: parsed.summary, rawStdout: r.stdout.slice(0, 400) };
63
+ }
64
+ return {
65
+ label,
66
+ exitCode: r.exitCode,
67
+ degraded: r.degraded,
68
+ reason: r.degraded ? r.reason : null,
69
+ json,
70
+ durationMs: r.durationMs,
71
+ stderrTail: r.degraded ? (r.stderr || '').slice(-200) : null,
72
+ };
73
+ }
74
+
75
+ function runOne(args, label, engine = 'harness') {
76
+ const r = engine === 'metaharness' ? runMetaharness(args) : runHarness(args);
77
+ return annotateOne(r, label);
78
+ }
79
+
80
+ // iter 56 — parallel variant. The composite audit's 5 subprocess calls
81
+ // are mutually independent (each scans the same path read-only), so
82
+ // they can run concurrently. Worst-case wall-clock drops from
83
+ // 5×DEFAULT_TIMEOUT_MS (sequential) to 1×DEFAULT_TIMEOUT_MS (parallel)
84
+ // — the iter-55-flagged gap (oia-audit timeout under unreachable
85
+ // registry) is closed by this.
86
+ async function runAllParallel(path) {
87
+ const tasks = [
88
+ runHarnessAsync(['oia-manifest', path]).then((r) => ['oiaManifest', annotateOne(r, 'oia-manifest')]),
89
+ runHarnessAsync(['threat-model', path]).then((r) => ['threatModel', annotateOne(r, 'threat-model')]),
90
+ runHarnessAsync(['mcp-scan', path]).then((r) => ['mcpScan', annotateOne(r, 'mcp-scan')]),
91
+ runMetaharnessAsync(['score', path]).then((r) => ['score', annotateOne(r, 'score')]),
92
+ runMetaharnessAsync(['genome', path]).then((r) => ['genome', annotateOne(r, 'genome')]),
93
+ ];
94
+ const results = await Promise.all(tasks);
95
+ return Object.fromEntries(results);
96
+ }
97
+
98
+ function persist(payload) {
99
+ const key = `audit-${new Date().toISOString().replace(/[:.]/g, '-')}`;
100
+ const r = spawnSync('npx', [
101
+ CLI_PKG, 'memory', 'store',
102
+ '--namespace', NS,
103
+ '--key', key,
104
+ '--value', JSON.stringify(payload),
105
+ ], { stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf-8', shell: process.platform === 'win32' });
106
+ return {
107
+ ok: r.status === 0,
108
+ namespace: NS,
109
+ key,
110
+ error: r.status === 0 ? null : (r.stderr || '').slice(0, 200),
111
+ };
112
+ }
113
+
114
+ async function main() {
115
+ if (ARGS.alertWorst !== null && !SEVERITY_RANK.hasOwnProperty(ARGS.alertWorst)) {
116
+ console.error(`oia-audit: --alert-on-worst must be one of clean|low|medium|high; got ${ARGS.alertWorst}`);
117
+ process.exit(2);
118
+ }
119
+
120
+ const startedAt = new Date().toISOString();
121
+ const wallStart = Date.now();
122
+ // iter 56 — run the 5 sub-audits in parallel (was sequential pre-iter-56).
123
+ // Worst-case wall-clock improves from 5×TIMEOUT to 1×TIMEOUT in the
124
+ // unreachable-registry case; the happy path improves from sum-of-durations
125
+ // to max-of-durations (typically ~2-4× faster).
126
+ const all = await runAllParallel(ARGS.path);
127
+ const wallMs = Date.now() - wallStart;
128
+ const { oiaManifest: oia, threatModel: tm, mcpScan: mcp, score, genome } = all;
129
+
130
+ // If all FIVE say "metaharness not available", surface the degraded
131
+ // payload exactly once and exit 0 (architectural constraint #3).
132
+ if (oia.degraded && tm.degraded && mcp.degraded && score.degraded && genome.degraded) {
133
+ emitDegradedJsonAndExit('metaharness-not-available');
134
+ return;
135
+ }
136
+
137
+ // Aggregate the worst-severity signal across mcp-scan + threat-model.
138
+ // iter 62 — safe lookup using `?? 0` so unknown severities don't
139
+ // silently bump composite via NaN-comparison (was: `acc | undefined`
140
+ // → reduce never updated). Now any [WARN]/[CRITICAL]/[ERROR] finding
141
+ // correctly elevates composite worst.
142
+ const tmWorst = String(tm.json?.worst || 'clean').toLowerCase();
143
+ const mcpFindings = Array.isArray(mcp.json?.findings) ? mcp.json.findings : [];
144
+ // iter 63 — rankSeverity() from _harness.mjs handles unknown-severity
145
+ // safely (returns 0 instead of undefined) so the reduce never sees NaN.
146
+ const mcpWorst = mcpFindings.reduce((acc, f) => {
147
+ const s = String(f.severity || 'low').toLowerCase();
148
+ return rankSeverity(s) > rankSeverity(acc) ? s : acc;
149
+ }, 'clean');
150
+ const compositeWorst = rankSeverity(tmWorst) > rankSeverity(mcpWorst) ? tmWorst : mcpWorst;
151
+
152
+ let alertTriggered = false;
153
+ let alertReason = null;
154
+ if (ARGS.alertWorst !== null) {
155
+ const threshold = rankSeverity(ARGS.alertWorst);
156
+ const compositeRank = rankSeverity(compositeWorst);
157
+ if (compositeRank >= threshold && threshold > 0) {
158
+ alertTriggered = true;
159
+ alertReason = `composite worst=${compositeWorst} ≥ ${ARGS.alertWorst}`;
160
+ }
161
+ }
162
+
163
+ // iter 59 — surface parallelization metrics. wallMs is the actual
164
+ // time the 5 subprocess race took (max of components); the sum of
165
+ // component.durationMs is what a SERIAL implementation would have
166
+ // taken. A future smoke gate compares them to catch silent
167
+ // serialization regression.
168
+ const sumComponentMs = Object.values(all).reduce(
169
+ (a, c) => a + (c?.durationMs ?? 0), 0);
170
+ const payload = {
171
+ path: ARGS.path,
172
+ startedAt,
173
+ finishedAt: new Date().toISOString(),
174
+ timing: {
175
+ wallMs,
176
+ sumComponentMs,
177
+ parallelSpeedup: sumComponentMs > 0
178
+ ? Math.round((sumComponentMs / Math.max(wallMs, 1)) * 100) / 100 : 0,
179
+ },
180
+ composite: { worst: compositeWorst, threatModelWorst: tmWorst, mcpScanWorst: mcpWorst },
181
+ components: { oiaManifest: oia, threatModel: tm, mcpScan: mcp, score, genome },
182
+ // iter 38 — denormalized harness fingerprint for cheap similarity().
183
+ // Mirrors the shape `_similarity.mjs::similarity()` expects so
184
+ // audit-trend can call it without reshuffling components.
185
+ fingerprint: {
186
+ score: score?.json && !score.degraded ? score.json : null,
187
+ genome: genome?.json && !genome.degraded ? genome.json : null,
188
+ },
189
+ alert: ARGS.alertWorst !== null ? {
190
+ threshold: ARGS.alertWorst,
191
+ triggered: alertTriggered,
192
+ reason: alertReason || `composite worst=${compositeWorst} < ${ARGS.alertWorst} — OK`,
193
+ } : null,
194
+ persisted: null,
195
+ };
196
+
197
+ if (!ARGS.dryRun) {
198
+ payload.persisted = persist(payload);
199
+ }
200
+
201
+ if (ARGS.format === 'json') {
202
+ console.log(JSON.stringify(payload, null, 2));
203
+ } else {
204
+ console.log(`# oia-audit — ${ARGS.path}`);
205
+ console.log('');
206
+ console.log(`| Component | Exit | Degraded | Duration |`);
207
+ console.log(`|---|---:|:---:|---:|`);
208
+ console.log(`| oia-manifest | ${oia.exitCode} | ${oia.degraded ? '⚠' : '✓'} | ${oia.durationMs}ms |`);
209
+ console.log(`| threat-model | ${tm.exitCode} | ${tm.degraded ? '⚠' : '✓'} | ${tm.durationMs}ms |`);
210
+ console.log(`| mcp-scan | ${mcp.exitCode} | ${mcp.degraded ? '⚠' : '✓'} | ${mcp.durationMs}ms |`);
211
+ console.log('');
212
+ console.log(`Composite worst severity: **${compositeWorst}** (tm=${tmWorst}, mcp=${mcpWorst})`);
213
+ if (payload.persisted) {
214
+ console.log(`Persisted: ${payload.persisted.ok ? `${payload.persisted.namespace}:${payload.persisted.key}` : `FAILED: ${payload.persisted.error}`}`);
215
+ }
216
+ if (payload.alert) {
217
+ console.log('');
218
+ console.log(payload.alert.triggered ? `⚠ **ALERT**: ${payload.alert.reason}` : `✓ ${payload.alert.reason}`);
219
+ }
220
+ }
221
+
222
+ if (alertTriggered) process.exit(1);
223
+ }
224
+
225
+ main().catch((e) => {
226
+ console.error('oia-audit crashed:', e?.message ?? e);
227
+ process.exit(2);
228
+ });