@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,250 @@
1
+ #!/usr/bin/env node
2
+ // router-parallel-analyze.mjs — ADR-150 Phase 2 SelfEvolvingRouter
3
+ // promotion gate.
4
+ //
5
+ // Reads paired routing decisions (Thompson bandit pick + SelfEvolvingRouter
6
+ // pick, both with measured outcomes) from a JSONL trajectory file and
7
+ // computes the THREE PROMOTION CRITERIA from ADR-150 review-round-1:
8
+ //
9
+ // (a) qualityScore improvement > 2% (relative)
10
+ // (b) usdPerDecision increase < 1% (relative)
11
+ // (c) p95 routing-decision latency increase < 5% (relative)
12
+ //
13
+ // ALL THREE must hold for promotion. AND, not OR — the OR form let
14
+ // quality gains mask cost regressions, exactly the failure mode ADR-149's
15
+ // Pareto framing was built to prevent.
16
+ //
17
+ // INPUT FORMAT (.swarm/router-parallel.jsonl, one JSON record per line):
18
+ // {
19
+ // "ts": "<iso>",
20
+ // "task": { ... },
21
+ // "bandit": { "pick": "<modelId>", "predictedQuality": 0.84, "predictedCostUsd": 0.003 },
22
+ // "ser": { "pick": "<modelId>", "predictedQuality": 0.87, "predictedCostUsd": 0.004 },
23
+ // "outcome": { "actualModel": "<modelId>", "actualQuality": 0.91, "actualUsd": 0.0035, "actualLatencyMs": 1240 }
24
+ // }
25
+ //
26
+ // USAGE
27
+ // node scripts/router-parallel-analyze.mjs --input .swarm/router-parallel.jsonl
28
+ // node scripts/router-parallel-analyze.mjs --input <file> --format json
29
+ // node scripts/router-parallel-analyze.mjs --input <file> --strict # exit 1 if NOT promotable
30
+ //
31
+ // EXIT CODES
32
+ // 0 analysis complete; in --strict mode → all three criteria passed
33
+ // 1 --strict requested AND at least one criterion failed (NOT promotable)
34
+ // 2 config error or input file missing/malformed
35
+
36
+ import { readFileSync, existsSync } from 'node:fs';
37
+
38
+ const ARGS = (() => {
39
+ const a = { input: '.swarm/router-parallel.jsonl', format: 'table', strict: false };
40
+ for (let i = 2; i < process.argv.length; i++) {
41
+ const v = process.argv[i];
42
+ if (v === '--input') a.input = process.argv[++i];
43
+ else if (v === '--format') a.format = process.argv[++i];
44
+ else if (v === '--strict') a.strict = true;
45
+ }
46
+ return a;
47
+ })();
48
+
49
+ function median(sorted) {
50
+ if (sorted.length === 0) return 0;
51
+ const n = sorted.length;
52
+ return n % 2 === 0 ? (sorted[n / 2 - 1] + sorted[n / 2]) / 2 : sorted[(n - 1) / 2];
53
+ }
54
+
55
+ function pctile(sorted, q) {
56
+ if (sorted.length === 0) return 0;
57
+ const idx = Math.floor(q * (sorted.length - 1));
58
+ return sorted[idx];
59
+ }
60
+
61
+ function mean(arr) {
62
+ if (arr.length === 0) return 0;
63
+ return arr.reduce((s, x) => s + x, 0) / arr.length;
64
+ }
65
+
66
+ function main() {
67
+ if (!existsSync(ARGS.input)) {
68
+ if (ARGS.format === 'json') {
69
+ console.log(JSON.stringify({
70
+ ok: true,
71
+ sufficient: false,
72
+ reason: 'input-file-not-found',
73
+ input: ARGS.input,
74
+ hint: 'Enable parallel logging (CLAUDE_FLOW_ROUTER_PARALLEL_LOG=1) and run a workload first.',
75
+ }, null, 2));
76
+ } else {
77
+ console.log(`# router-parallel-analyze`);
78
+ console.log('');
79
+ console.log(`_Input file not found: ${ARGS.input}_`);
80
+ console.log('');
81
+ console.log('Enable parallel logging by setting `CLAUDE_FLOW_ROUTER_PARALLEL_LOG=1`');
82
+ console.log('and running a real workload, then re-run this analyzer.');
83
+ }
84
+ process.exit(ARGS.strict ? 1 : 0);
85
+ }
86
+
87
+ let rows = [];
88
+ try {
89
+ rows = readFileSync(ARGS.input, 'utf-8')
90
+ .split('\n')
91
+ .filter((l) => l.trim())
92
+ .map((l) => JSON.parse(l));
93
+ } catch (e) {
94
+ console.error(`router-parallel-analyze: cannot parse ${ARGS.input}: ${e.message}`);
95
+ process.exit(2);
96
+ }
97
+
98
+ // Filter to records with both predictions + outcome present.
99
+ const usable = rows.filter((r) => r.bandit && r.ser && r.outcome);
100
+
101
+ if (usable.length < 30) {
102
+ const payload = {
103
+ ok: true,
104
+ sufficient: false,
105
+ reason: `n=${usable.length} < 30 (insufficient sample for AND-gate evaluation)`,
106
+ hint: 'Continue collecting parallel-routing data; statistical significance needs ≥30 paired decisions per arm.',
107
+ sampleSize: usable.length,
108
+ generatedAt: new Date().toISOString(),
109
+ };
110
+ if (ARGS.format === 'json') console.log(JSON.stringify(payload, null, 2));
111
+ else {
112
+ console.log(`# router-parallel-analyze`);
113
+ console.log('');
114
+ console.log(`Sample size: **${usable.length}** paired decisions`);
115
+ console.log('');
116
+ console.log(`_Insufficient — need ≥30 for the 3-criteria AND-gate._`);
117
+ }
118
+ process.exit(ARGS.strict ? 1 : 0);
119
+ }
120
+
121
+ // For each record, attribute the outcome to BOTH arms when they
122
+ // agreed on the model pick; when they disagreed, the actual outcome
123
+ // belongs only to the bandit (which was the executing arm). The SER
124
+ // arm gets its PREDICTED metrics for the counterfactual.
125
+ const banditOutcomes = usable.map((r) => ({
126
+ qualityActual: r.outcome.actualQuality,
127
+ usdActual: r.outcome.actualUsd,
128
+ latencyActual: r.outcome.actualLatencyMs,
129
+ }));
130
+
131
+ // SER counterfactual: use ser.predictedQuality / predictedCostUsd
132
+ // when picks disagreed; use actual when they agreed. Latency: when
133
+ // they agreed, both saw the same actual latency; when they
134
+ // disagreed, the SER pick's latency is unknown — fall back to the
135
+ // mean of agreed-decision latencies for that model.
136
+ const agreedLatencyByModel = {};
137
+ for (const r of usable) {
138
+ if (r.bandit.pick === r.ser.pick) {
139
+ const m = r.outcome.actualModel || r.bandit.pick;
140
+ (agreedLatencyByModel[m] = agreedLatencyByModel[m] || []).push(r.outcome.actualLatencyMs);
141
+ }
142
+ }
143
+ const modelMeanLatency = {};
144
+ for (const [m, arr] of Object.entries(agreedLatencyByModel)) {
145
+ modelMeanLatency[m] = mean(arr);
146
+ }
147
+
148
+ const serOutcomes = usable.map((r) => {
149
+ const agreed = r.bandit.pick === r.ser.pick;
150
+ return {
151
+ qualityActual: agreed ? r.outcome.actualQuality : r.ser.predictedQuality,
152
+ usdActual: agreed ? r.outcome.actualUsd : r.ser.predictedCostUsd,
153
+ latencyActual: agreed
154
+ ? r.outcome.actualLatencyMs
155
+ : (modelMeanLatency[r.ser.pick] ?? r.outcome.actualLatencyMs),
156
+ };
157
+ });
158
+
159
+ // Compute the three criteria.
160
+ const banditQuality = mean(banditOutcomes.map((o) => o.qualityActual));
161
+ const serQuality = mean(serOutcomes.map((o) => o.qualityActual));
162
+ const qualityImprovementPct =
163
+ banditQuality > 0 ? ((serQuality - banditQuality) / banditQuality) * 100 : 0;
164
+
165
+ const banditUsd = mean(banditOutcomes.map((o) => o.usdActual));
166
+ const serUsd = mean(serOutcomes.map((o) => o.usdActual));
167
+ const usdIncreasePct = banditUsd > 0 ? ((serUsd - banditUsd) / banditUsd) * 100 : 0;
168
+
169
+ const banditLatencyP95 = pctile(banditOutcomes.map((o) => o.latencyActual).sort((a, b) => a - b), 0.95);
170
+ const serLatencyP95 = pctile(serOutcomes.map((o) => o.latencyActual).sort((a, b) => a - b), 0.95);
171
+ const latencyIncreasePct =
172
+ banditLatencyP95 > 0 ? ((serLatencyP95 - banditLatencyP95) / banditLatencyP95) * 100 : 0;
173
+
174
+ const passes = {
175
+ quality: qualityImprovementPct > 2,
176
+ cost: usdIncreasePct < 1,
177
+ latency: latencyIncreasePct < 5,
178
+ };
179
+ const allPass = passes.quality && passes.cost && passes.latency;
180
+
181
+ // Disagreement rate (informational; not part of the gate).
182
+ const disagreements = usable.filter((r) => r.bandit.pick !== r.ser.pick).length;
183
+ const disagreementPct = (disagreements / usable.length) * 100;
184
+
185
+ const payload = {
186
+ ok: true,
187
+ sufficient: true,
188
+ sampleSize: usable.length,
189
+ disagreement: {
190
+ count: disagreements,
191
+ pct: Math.round(disagreementPct * 100) / 100,
192
+ },
193
+ bandit: {
194
+ meanQuality: Math.round(banditQuality * 10000) / 10000,
195
+ meanUsd: Math.round(banditUsd * 1e6) / 1e6,
196
+ p95LatencyMs: Math.round(banditLatencyP95),
197
+ },
198
+ ser: {
199
+ meanQuality: Math.round(serQuality * 10000) / 10000,
200
+ meanUsd: Math.round(serUsd * 1e6) / 1e6,
201
+ p95LatencyMs: Math.round(serLatencyP95),
202
+ },
203
+ criteria: {
204
+ qualityImprovementPct: Math.round(qualityImprovementPct * 100) / 100,
205
+ qualityThresholdPct: 2,
206
+ qualityPasses: passes.quality,
207
+ usdIncreasePct: Math.round(usdIncreasePct * 100) / 100,
208
+ usdThresholdPct: 1,
209
+ costPasses: passes.cost,
210
+ latencyIncreasePct: Math.round(latencyIncreasePct * 100) / 100,
211
+ latencyThresholdPct: 5,
212
+ latencyPasses: passes.latency,
213
+ },
214
+ verdict: {
215
+ promotable: allPass,
216
+ reason: allPass
217
+ ? 'All three criteria met (quality > 2% AND cost < 1% AND latency < 5%)'
218
+ : `Blocked by: ${[
219
+ !passes.quality && `quality only +${(qualityImprovementPct).toFixed(2)}% (need >2%)`,
220
+ !passes.cost && `cost +${(usdIncreasePct).toFixed(2)}% (need <1%)`,
221
+ !passes.latency && `latency +${(latencyIncreasePct).toFixed(2)}% (need <5%)`,
222
+ ].filter(Boolean).join(', ')}`,
223
+ },
224
+ generatedAt: new Date().toISOString(),
225
+ };
226
+
227
+ if (ARGS.format === 'json') {
228
+ console.log(JSON.stringify(payload, null, 2));
229
+ } else {
230
+ console.log('# router-parallel-analyze (ADR-150 SelfEvolvingRouter promotion gate)');
231
+ console.log('');
232
+ console.log(`Sample size: **${payload.sampleSize}** paired decisions`);
233
+ console.log(`Disagreement rate: ${payload.disagreement.pct}% (${payload.disagreement.count} / ${payload.sampleSize})`);
234
+ console.log('');
235
+ console.log(`| Metric | Bandit | SER | Delta | Threshold | Passes |`);
236
+ console.log(`|---|---:|---:|---:|---:|:---:|`);
237
+ console.log(`| Mean quality | ${payload.bandit.meanQuality} | ${payload.ser.meanQuality} | ${payload.criteria.qualityImprovementPct >= 0 ? '+' : ''}${payload.criteria.qualityImprovementPct}% | >+2% | ${passes.quality ? '✓' : '⚠'} |`);
238
+ console.log(`| Mean $/decision | $${payload.bandit.meanUsd.toFixed(6)} | $${payload.ser.meanUsd.toFixed(6)} | ${payload.criteria.usdIncreasePct >= 0 ? '+' : ''}${payload.criteria.usdIncreasePct}% | <+1% | ${passes.cost ? '✓' : '⚠'} |`);
239
+ console.log(`| p95 latency | ${payload.bandit.p95LatencyMs}ms | ${payload.ser.p95LatencyMs}ms | ${payload.criteria.latencyIncreasePct >= 0 ? '+' : ''}${payload.criteria.latencyIncreasePct}% | <+5% | ${passes.latency ? '✓' : '⚠'} |`);
240
+ console.log('');
241
+ console.log(`**Verdict**: ${allPass ? '✓ PROMOTABLE' : '⚠ NOT promotable'}`);
242
+ console.log('');
243
+ console.log(`_${payload.verdict.reason}_`);
244
+ }
245
+
246
+ if (ARGS.strict && !allPass) process.exit(1);
247
+ process.exit(0);
248
+ }
249
+
250
+ main();
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env node
2
+ // score.mjs — `cost-style` wrapper around `metaharness score <path>`.
3
+ //
4
+ // Returns the 5-dimension scorecard (harnessFit / compileConfidence /
5
+ // taskCoverage / toolSafety / memoryUsefulness) + estCostPerRunUsd +
6
+ // scaffoldReady boolean. Reads-only; no side effects.
7
+ //
8
+ // USAGE
9
+ // node scripts/score.mjs # current dir
10
+ // node scripts/score.mjs --path <dir> # specific dir
11
+ // node scripts/score.mjs --alert-on-fit-below 70 # exit 1 if harnessFit < 70
12
+ // node scripts/score.mjs --format json
13
+ //
14
+ // EXIT CODES
15
+ // 0 scored OK (or degraded — MetaHarness not available)
16
+ // 1 --alert-on-fit-below threshold breached
17
+ // 2 config error or scoring failure
18
+
19
+ import { runMetaharness, emitDegradedJsonAndExit } from './_harness.mjs';
20
+
21
+ const ARGS = (() => {
22
+ const a = { path: '.', format: 'json', alertFitBelow: null };
23
+ for (let i = 2; i < process.argv.length; i++) {
24
+ const v = process.argv[i];
25
+ if (v === '--path') a.path = process.argv[++i];
26
+ else if (v === '--alert-on-fit-below') a.alertFitBelow = parseFloat(process.argv[++i]);
27
+ else if (v === '--format') a.format = process.argv[++i];
28
+ }
29
+ return a;
30
+ })();
31
+
32
+ function main() {
33
+ const r = runMetaharness(['score', ARGS.path]);
34
+ if (r.degraded) {
35
+ emitDegradedJsonAndExit(r.reason);
36
+ return;
37
+ }
38
+ if (r.exitCode !== 0 || !r.json) {
39
+ console.error(`score: metaharness exited ${r.exitCode}`);
40
+ if (r.stderr) console.error(r.stderr.slice(0, 400));
41
+ process.exit(2);
42
+ }
43
+ const payload = { ...r.json, durationMs: r.durationMs };
44
+
45
+ if (ARGS.alertFitBelow !== null) {
46
+ if (!isFinite(ARGS.alertFitBelow)) {
47
+ console.error(`score: --alert-on-fit-below must be a finite number`);
48
+ process.exit(2);
49
+ }
50
+ payload.alert = {
51
+ threshold: ARGS.alertFitBelow,
52
+ triggered: typeof payload.harnessFit === 'number' && payload.harnessFit < ARGS.alertFitBelow,
53
+ reason: typeof payload.harnessFit === 'number' && payload.harnessFit < ARGS.alertFitBelow
54
+ ? `harnessFit ${payload.harnessFit} < ${ARGS.alertFitBelow}`
55
+ : `harnessFit ${payload.harnessFit ?? 'unknown'} ≥ ${ARGS.alertFitBelow} — OK`,
56
+ };
57
+ }
58
+
59
+ if (ARGS.format === 'json') {
60
+ console.log(JSON.stringify(payload, null, 2));
61
+ } else {
62
+ console.log(`# harness-score — ${payload.repo || ARGS.path}`);
63
+ console.log('');
64
+ console.log(`| Dimension | Value |`);
65
+ console.log(`|---|---:|`);
66
+ console.log(`| harnessFit | ${payload.harnessFit ?? '—'} |`);
67
+ console.log(`| compileConfidence | ${payload.compileConfidence ?? '—'} |`);
68
+ console.log(`| taskCoverage | ${payload.taskCoverage ?? '—'} |`);
69
+ console.log(`| toolSafety | ${payload.toolSafety ?? '—'} |`);
70
+ console.log(`| memoryUsefulness | ${payload.memoryUsefulness ?? '—'} |`);
71
+ console.log(`| estCostPerRunUsd | $${payload.estCostPerRunUsd ?? '—'} |`);
72
+ console.log(`| recommendedMode | ${payload.recommendedMode ?? '—'} |`);
73
+ console.log(`| archetype | ${payload.archetype ?? '—'} |`);
74
+ console.log(`| template | ${payload.template ?? '—'} |`);
75
+ console.log(`| scaffoldReady | ${payload.scaffoldReady ?? '—'} |`);
76
+ console.log(`| hardConstraints | ${payload.hardConstraints ?? '—'} |`);
77
+ console.log(`| **duration** | ${payload.durationMs}ms |`);
78
+ console.log('');
79
+ if (payload.alert) {
80
+ if (payload.alert.triggered) {
81
+ console.log(`⚠ **ALERT**: ${payload.alert.reason}`);
82
+ } else {
83
+ console.log(`✓ ${payload.alert.reason}`);
84
+ }
85
+ console.log('');
86
+ }
87
+ }
88
+
89
+ if (payload.alert?.triggered) process.exit(1);
90
+ }
91
+
92
+ main();
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env node
2
+ // similarity.mjs — ADR-152 §3.1 CLI skill.
3
+ //
4
+ // Computes weighted similarity between two harness fingerprints (genome
5
+ // + score JSON). Production-grade companion to _spike-similarity.mjs.
6
+ //
7
+ // USAGE
8
+ // node scripts/similarity.mjs --a a.json --b b.json
9
+ // node scripts/similarity.mjs --a-key harness-X --b-key harness-Y # memory lookup
10
+ // node scripts/similarity.mjs --a a.json --b b.json --per-dimension # breakdown
11
+ // node scripts/similarity.mjs --a a.json --b b.json --format json
12
+ // node scripts/similarity.mjs --a a.json --b b.json --alert-below 0.5 # exit 1 if low
13
+ //
14
+ // EXIT CODES
15
+ // 0 ok (or overall ≥ alert-below threshold)
16
+ // 1 --alert-below AND overall < threshold
17
+ // 2 config / input error (missing args, file not found, malformed JSON)
18
+ //
19
+ // ADR-150 ARCHITECTURAL CONSTRAINTS PRESERVED
20
+ // - Pure-TS function (`_similarity.mjs`), no `@metaharness/*` import
21
+ // - No new dep; uses node:fs + node:child_process only for memory lookup
22
+ // - Graceful degradation: missing keys / malformed JSON → exit 2 with
23
+ // a structured `{ degraded: true, reason: ... }` payload on stdout
24
+ // - CI-gate ready: smoke step 17y locks this contract
25
+
26
+ import { readFileSync, existsSync } from 'node:fs';
27
+ import { spawnSync } from 'node:child_process';
28
+ import { similarity } from './_similarity.mjs';
29
+
30
+ const NS = process.env.HARNESS_SIMILARITY_NAMESPACE || 'metaharness-audit';
31
+ const CLI_PKG = process.env.CLI_CORE === '1'
32
+ ? '@claude-flow/cli-core@alpha'
33
+ : '@claude-flow/cli@latest';
34
+
35
+ const ARGS = (() => {
36
+ const a = {
37
+ aFile: null, bFile: null,
38
+ aKey: null, bKey: null,
39
+ perDimension: false,
40
+ format: 'table',
41
+ alertBelow: null,
42
+ };
43
+ for (let i = 2; i < process.argv.length; i++) {
44
+ const v = process.argv[i];
45
+ if (v === '--a') a.aFile = process.argv[++i];
46
+ else if (v === '--b') a.bFile = process.argv[++i];
47
+ else if (v === '--a-key') a.aKey = process.argv[++i];
48
+ else if (v === '--b-key') a.bKey = process.argv[++i];
49
+ else if (v === '--per-dimension') a.perDimension = true;
50
+ else if (v === '--format') a.format = process.argv[++i];
51
+ else if (v === '--alert-below') a.alertBelow = Number(process.argv[++i]);
52
+ }
53
+ return a;
54
+ })();
55
+
56
+ function emitDegradedAndExit(reason, code = 2) {
57
+ const payload = {
58
+ degraded: true,
59
+ reason,
60
+ adr: 'ADR-152',
61
+ skill: 'harness-similarity',
62
+ generatedAt: new Date().toISOString(),
63
+ };
64
+ console.log(JSON.stringify(payload, null, 2));
65
+ process.exit(code);
66
+ }
67
+
68
+ function memRetrieve(key) {
69
+ const r = spawnSync('npx', [
70
+ CLI_PKG, 'memory', 'retrieve',
71
+ '--namespace', NS, '--key', key,
72
+ ], { stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf-8', shell: process.platform === 'win32' });
73
+ if (r.status !== 0) return null;
74
+ const m = /\{[\s\S]*\}/.exec(r.stdout || '');
75
+ if (!m) return null;
76
+ try { return JSON.parse(m[0]); } catch { return null; }
77
+ }
78
+
79
+ function loadHarness(label, filePath, memKey) {
80
+ if (filePath) {
81
+ if (!existsSync(filePath)) emitDegradedAndExit(`${label}: file not found: ${filePath}`);
82
+ try { return JSON.parse(readFileSync(filePath, 'utf-8')); }
83
+ catch (e) { emitDegradedAndExit(`${label}: invalid JSON: ${e.message}`); }
84
+ }
85
+ if (memKey) {
86
+ const rec = memRetrieve(memKey);
87
+ if (!rec) emitDegradedAndExit(`${label}: key not found in namespace ${NS}: ${memKey}`);
88
+ return rec;
89
+ }
90
+ emitDegradedAndExit(`${label}: --${label} or --${label}-key required`);
91
+ return null; // unreachable; emitDegradedAndExit exits
92
+ }
93
+
94
+ function main() {
95
+ if (!ARGS.aFile && !ARGS.aKey) emitDegradedAndExit('--a <file> or --a-key <memkey> required');
96
+ if (!ARGS.bFile && !ARGS.bKey) emitDegradedAndExit('--b <file> or --b-key <memkey> required');
97
+
98
+ const a = loadHarness('a', ARGS.aFile, ARGS.aKey);
99
+ const b = loadHarness('b', ARGS.bFile, ARGS.bKey);
100
+
101
+ const result = similarity(a, b, { perDimension: ARGS.perDimension });
102
+
103
+ const payload = {
104
+ adr: 'ADR-152',
105
+ skill: 'harness-similarity',
106
+ inputs: {
107
+ a: ARGS.aFile ?? `mem:${ARGS.aKey}`,
108
+ b: ARGS.bFile ?? `mem:${ARGS.bKey}`,
109
+ },
110
+ ...result,
111
+ alert: ARGS.alertBelow != null ? {
112
+ threshold: ARGS.alertBelow,
113
+ triggered: result.overall < ARGS.alertBelow,
114
+ reason: result.overall < ARGS.alertBelow
115
+ ? `overall ${result.overall} < threshold ${ARGS.alertBelow}`
116
+ : `overall ${result.overall} ≥ threshold ${ARGS.alertBelow}`,
117
+ } : null,
118
+ generatedAt: new Date().toISOString(),
119
+ };
120
+
121
+ if (ARGS.format === 'json') {
122
+ console.log(JSON.stringify(payload, null, 2));
123
+ } else {
124
+ console.log(`# harness similarity (ADR-152)\n`);
125
+ console.log(`Inputs:`);
126
+ console.log(` a: ${payload.inputs.a}`);
127
+ console.log(` b: ${payload.inputs.b}`);
128
+ console.log('');
129
+ console.log(`| Component | Score | Weight | Contribution |`);
130
+ console.log(`|-------------|-------:|-------:|-------------:|`);
131
+ const { weights, components } = payload;
132
+ console.log(`| cosine | ${components.cosine.toFixed(4)} | ${weights.cosine} | ${(components.cosine * weights.cosine).toFixed(4)} |`);
133
+ console.log(`| categorical | ${components.categorical.toFixed(4)} | ${weights.categorical} | ${(components.categorical * weights.categorical).toFixed(4)} |`);
134
+ console.log(`| jaccard | ${components.jaccard.toFixed(4)} | ${weights.jaccard} | ${(components.jaccard * weights.jaccard).toFixed(4)} |`);
135
+ console.log('');
136
+ console.log(`**Overall:** ${payload.overall.toFixed(4)}`);
137
+ if (payload.alert) {
138
+ console.log('');
139
+ console.log(payload.alert.triggered ? `⚠ ALERT: ${payload.alert.reason}` : `✓ ${payload.alert.reason}`);
140
+ }
141
+ if (ARGS.perDimension && payload.perDimension) {
142
+ console.log('');
143
+ console.log('## Per-dimension breakdown');
144
+ console.log('');
145
+ console.log('| Dimension | a | b | contribution |');
146
+ console.log('|---|---|---|---:|');
147
+ for (const [k, v] of Object.entries(payload.perDimension)) {
148
+ const av = Array.isArray(v.a) ? `[${v.a.join(',')}]` : String(v.a ?? '-');
149
+ const bv = Array.isArray(v.b) ? `[${v.b.join(',')}]` : String(v.b ?? '-');
150
+ console.log(`| ${k} | ${av} | ${bv} | ${Number(v.contribution).toFixed(4)} |`);
151
+ }
152
+ }
153
+ }
154
+
155
+ if (payload.alert?.triggered) process.exit(1);
156
+ }
157
+
158
+ main();