@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.
- package/dist/src/commands/doctor.d.ts.map +1 -1
- package/dist/src/commands/doctor.js +175 -1
- package/dist/src/commands/doctor.js.map +1 -1
- package/dist/src/commands/eject.d.ts +33 -0
- package/dist/src/commands/eject.d.ts.map +1 -0
- package/dist/src/commands/eject.js +195 -0
- package/dist/src/commands/eject.js.map +1 -0
- package/dist/src/commands/hooks.d.ts.map +1 -1
- package/dist/src/commands/hooks.js +2 -1
- package/dist/src/commands/hooks.js.map +1 -1
- package/dist/src/commands/index.d.ts.map +1 -1
- package/dist/src/commands/index.js +4 -0
- package/dist/src/commands/index.js.map +1 -1
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +3 -0
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/metaharness.d.ts +39 -0
- package/dist/src/commands/metaharness.d.ts.map +1 -0
- package/dist/src/commands/metaharness.js +194 -0
- package/dist/src/commands/metaharness.js.map +1 -0
- package/dist/src/mcp-client.d.ts.map +1 -1
- package/dist/src/mcp-client.js +4 -0
- package/dist/src/mcp-client.js.map +1 -1
- package/dist/src/mcp-tools/index.d.ts +1 -0
- package/dist/src/mcp-tools/index.d.ts.map +1 -1
- package/dist/src/mcp-tools/index.js +2 -0
- package/dist/src/mcp-tools/index.js.map +1 -1
- package/dist/src/mcp-tools/metaharness-tools.d.ts +31 -0
- package/dist/src/mcp-tools/metaharness-tools.d.ts.map +1 -0
- package/dist/src/mcp-tools/metaharness-tools.js +372 -0
- package/dist/src/mcp-tools/metaharness-tools.js.map +1 -0
- package/dist/src/plugins/store/discovery.d.ts.map +1 -1
- package/dist/src/plugins/store/discovery.js +3 -0
- package/dist/src/plugins/store/discovery.js.map +1 -1
- package/dist/src/plugins/store/types.d.ts +6 -1
- package/dist/src/plugins/store/types.d.ts.map +1 -1
- package/dist/src/ruvector/model-router.d.ts.map +1 -1
- package/dist/src/ruvector/model-router.js +37 -0
- package/dist/src/ruvector/model-router.js.map +1 -1
- package/dist/src/ruvector/router-parallel-recorder.d.ts +127 -0
- package/dist/src/ruvector/router-parallel-recorder.d.ts.map +1 -0
- package/dist/src/ruvector/router-parallel-recorder.js +183 -0
- package/dist/src/ruvector/router-parallel-recorder.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +13 -10
- package/plugins/ruflo-metaharness/.claude-flow/data/pending-insights.jsonl +1 -0
- package/plugins/ruflo-metaharness/.claude-flow/neural/stats.json +6 -0
- package/plugins/ruflo-metaharness/.claude-plugin/plugin.json +32 -0
- package/plugins/ruflo-metaharness/README.md +64 -0
- package/plugins/ruflo-metaharness/agents/metaharness-architect.md +58 -0
- package/plugins/ruflo-metaharness/commands/ruflo-metaharness.md +46 -0
- package/plugins/ruflo-metaharness/scripts/_harness.mjs +261 -0
- package/plugins/ruflo-metaharness/scripts/_similarity.mjs +161 -0
- package/plugins/ruflo-metaharness/scripts/_spike-similarity.mjs +223 -0
- package/plugins/ruflo-metaharness/scripts/audit-list.mjs +158 -0
- package/plugins/ruflo-metaharness/scripts/audit-trend.mjs +272 -0
- package/plugins/ruflo-metaharness/scripts/bench-parse-mcp-scan.mjs +146 -0
- package/plugins/ruflo-metaharness/scripts/bench-recordpair-overhead.mjs +186 -0
- package/plugins/ruflo-metaharness/scripts/bench-similarity.mjs +177 -0
- package/plugins/ruflo-metaharness/scripts/drift-from-history.mjs +363 -0
- package/plugins/ruflo-metaharness/scripts/genome.mjs +80 -0
- package/plugins/ruflo-metaharness/scripts/mcp-scan.mjs +111 -0
- package/plugins/ruflo-metaharness/scripts/mint.mjs +119 -0
- package/plugins/ruflo-metaharness/scripts/oia-audit.mjs +228 -0
- package/plugins/ruflo-metaharness/scripts/router-parallel-analyze.mjs +250 -0
- package/plugins/ruflo-metaharness/scripts/score.mjs +92 -0
- package/plugins/ruflo-metaharness/scripts/similarity.mjs +158 -0
- package/plugins/ruflo-metaharness/scripts/smoke.sh +2268 -0
- package/plugins/ruflo-metaharness/scripts/test-graceful-degradation.mjs +141 -0
- package/plugins/ruflo-metaharness/scripts/test-mcp-tools.mjs +437 -0
- package/plugins/ruflo-metaharness/scripts/test-parallel-pipeline.mjs +204 -0
- package/plugins/ruflo-metaharness/scripts/test-pipeline-roundtrip.mjs +586 -0
- package/plugins/ruflo-metaharness/scripts/test-similarity.mjs +334 -0
- package/plugins/ruflo-metaharness/scripts/test-with-openrouter.mjs +229 -0
- package/plugins/ruflo-metaharness/scripts/threat-model.mjs +59 -0
- package/plugins/ruflo-metaharness/skills/harness-drift-from-history/SKILL.md +65 -0
- package/plugins/ruflo-metaharness/skills/harness-genome/SKILL.md +54 -0
- package/plugins/ruflo-metaharness/skills/harness-mcp-scan/SKILL.md +47 -0
- package/plugins/ruflo-metaharness/skills/harness-mint/SKILL.md +72 -0
- package/plugins/ruflo-metaharness/skills/harness-oia-audit/SKILL.md +79 -0
- package/plugins/ruflo-metaharness/skills/harness-score/SKILL.md +66 -0
- package/plugins/ruflo-metaharness/skills/harness-similarity/SKILL.md +67 -0
- package/plugins/ruflo-metaharness/skills/harness-threat-model/SKILL.md +39 -0
- package/dist/src/benchmarks/capability-tasks.d.ts +0 -126
- package/dist/src/benchmarks/capability-tasks.d.ts.map +0 -1
- package/dist/src/benchmarks/capability-tasks.js +0 -147
- package/dist/src/benchmarks/capability-tasks.js.map +0 -1
- package/dist/src/benchmarks/gaia-agent-contrastive.d.ts +0 -53
- package/dist/src/benchmarks/gaia-agent-contrastive.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-agent-contrastive.js +0 -177
- package/dist/src/benchmarks/gaia-agent-contrastive.js.map +0 -1
- package/dist/src/benchmarks/gaia-agent-gemini.d.ts +0 -51
- package/dist/src/benchmarks/gaia-agent-gemini.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-agent-gemini.js +0 -352
- package/dist/src/benchmarks/gaia-agent-gemini.js.map +0 -1
- package/dist/src/benchmarks/gaia-claude-p.d.ts +0 -105
- package/dist/src/benchmarks/gaia-claude-p.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-claude-p.js +0 -269
- package/dist/src/benchmarks/gaia-claude-p.js.map +0 -1
- package/dist/src/benchmarks/gaia-claude-p.smoke.d.ts +0 -20
- package/dist/src/benchmarks/gaia-claude-p.smoke.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-claude-p.smoke.js +0 -168
- package/dist/src/benchmarks/gaia-claude-p.smoke.js.map +0 -1
- package/dist/src/benchmarks/gaia-codeagent-runner.py +0 -556
- package/dist/src/benchmarks/gaia-codeagent.d.ts +0 -129
- package/dist/src/benchmarks/gaia-codeagent.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-codeagent.js +0 -590
- package/dist/src/benchmarks/gaia-codeagent.js.map +0 -1
- package/dist/src/benchmarks/gaia-codeagent.smoke.d.ts +0 -33
- package/dist/src/benchmarks/gaia-codeagent.smoke.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-codeagent.smoke.js +0 -241
- package/dist/src/benchmarks/gaia-codeagent.smoke.js.map +0 -1
- package/dist/src/benchmarks/gaia-dag-pilot.d.ts +0 -22
- package/dist/src/benchmarks/gaia-dag-pilot.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-dag-pilot.js +0 -158
- package/dist/src/benchmarks/gaia-dag-pilot.js.map +0 -1
- package/dist/src/benchmarks/gaia-dag.d.ts +0 -110
- package/dist/src/benchmarks/gaia-dag.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-dag.js +0 -658
- package/dist/src/benchmarks/gaia-dag.js.map +0 -1
- package/dist/src/benchmarks/gaia-dag.smoke.d.ts +0 -21
- package/dist/src/benchmarks/gaia-dag.smoke.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-dag.smoke.js +0 -290
- package/dist/src/benchmarks/gaia-dag.smoke.js.map +0 -1
- package/dist/src/benchmarks/gaia-ensemble-pilot.d.ts +0 -17
- package/dist/src/benchmarks/gaia-ensemble-pilot.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-ensemble-pilot.js +0 -106
- package/dist/src/benchmarks/gaia-ensemble-pilot.js.map +0 -1
- package/dist/src/benchmarks/gaia-ensemble.d.ts +0 -103
- package/dist/src/benchmarks/gaia-ensemble.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-ensemble.js +0 -462
- package/dist/src/benchmarks/gaia-ensemble.js.map +0 -1
- package/dist/src/benchmarks/gaia-gemini-pilot.d.ts +0 -17
- package/dist/src/benchmarks/gaia-gemini-pilot.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-gemini-pilot.js +0 -168
- package/dist/src/benchmarks/gaia-gemini-pilot.js.map +0 -1
- package/dist/src/benchmarks/gaia-mode-router.d.ts +0 -67
- package/dist/src/benchmarks/gaia-mode-router.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-mode-router.js +0 -126
- package/dist/src/benchmarks/gaia-mode-router.js.map +0 -1
- package/dist/src/benchmarks/gaia-mode-router.smoke.d.ts +0 -15
- package/dist/src/benchmarks/gaia-mode-router.smoke.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-mode-router.smoke.js +0 -131
- package/dist/src/benchmarks/gaia-mode-router.smoke.js.map +0 -1
- package/dist/src/benchmarks/gaia-tools/grounded_query.smoke.d.ts +0 -24
- package/dist/src/benchmarks/gaia-tools/grounded_query.smoke.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-tools/grounded_query.smoke.js +0 -276
- package/dist/src/benchmarks/gaia-tools/grounded_query.smoke.js.map +0 -1
- package/dist/src/benchmarks/gaia-tools/image_describe.d.ts +0 -45
- package/dist/src/benchmarks/gaia-tools/image_describe.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-tools/image_describe.js +0 -222
- package/dist/src/benchmarks/gaia-tools/image_describe.js.map +0 -1
- package/dist/src/benchmarks/gaia-tools/image_describe.smoke.d.ts +0 -24
- package/dist/src/benchmarks/gaia-tools/image_describe.smoke.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-tools/image_describe.smoke.js +0 -169
- package/dist/src/benchmarks/gaia-tools/image_describe.smoke.js.map +0 -1
- package/dist/src/benchmarks/gaia-tools/pdf_read.d.ts +0 -27
- package/dist/src/benchmarks/gaia-tools/pdf_read.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-tools/pdf_read.js +0 -226
- package/dist/src/benchmarks/gaia-tools/pdf_read.js.map +0 -1
- package/dist/src/benchmarks/gaia-tools/python_exec.d.ts +0 -31
- package/dist/src/benchmarks/gaia-tools/python_exec.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-tools/python_exec.js +0 -94
- package/dist/src/benchmarks/gaia-tools/python_exec.js.map +0 -1
- package/dist/src/benchmarks/gaia-tools/python_exec.smoke.d.ts +0 -13
- package/dist/src/benchmarks/gaia-tools/python_exec.smoke.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-tools/python_exec.smoke.js +0 -98
- package/dist/src/benchmarks/gaia-tools/python_exec.smoke.js.map +0 -1
- package/dist/src/benchmarks/gaia-tools/visit_webpage.d.ts +0 -52
- package/dist/src/benchmarks/gaia-tools/visit_webpage.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-tools/visit_webpage.js +0 -304
- package/dist/src/benchmarks/gaia-tools/visit_webpage.js.map +0 -1
- package/dist/src/benchmarks/gaia-tools/web_browse.d.ts +0 -61
- package/dist/src/benchmarks/gaia-tools/web_browse.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-tools/web_browse.js +0 -217
- package/dist/src/benchmarks/gaia-tools/web_browse.js.map +0 -1
- package/dist/src/benchmarks/gaia-tools/web_browse.smoke.d.ts +0 -26
- package/dist/src/benchmarks/gaia-tools/web_browse.smoke.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-tools/web_browse.smoke.js +0 -152
- package/dist/src/benchmarks/gaia-tools/web_browse.smoke.js.map +0 -1
- package/dist/src/benchmarks/gaia-tools/web_search.smoke.d.ts +0 -24
- package/dist/src/benchmarks/gaia-tools/web_search.smoke.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-tools/web_search.smoke.js +0 -311
- package/dist/src/benchmarks/gaia-tools/web_search.smoke.js.map +0 -1
- package/dist/src/benchmarks/gaia-visit-webpage.smoke.d.ts +0 -25
- package/dist/src/benchmarks/gaia-visit-webpage.smoke.d.ts.map +0 -1
- package/dist/src/benchmarks/gaia-visit-webpage.smoke.js +0 -304
- package/dist/src/benchmarks/gaia-visit-webpage.smoke.js.map +0 -1
- package/dist/src/commands/performance-capability.d.ts +0 -30
- package/dist/src/commands/performance-capability.d.ts.map +0 -1
- package/dist/src/commands/performance-capability.js +0 -349
- package/dist/src/commands/performance-capability.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-flow/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.12.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Ruflo CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -78,6 +78,7 @@
|
|
|
78
78
|
"bin",
|
|
79
79
|
"scripts",
|
|
80
80
|
".claude",
|
|
81
|
+
"plugins",
|
|
81
82
|
"README.md"
|
|
82
83
|
],
|
|
83
84
|
"scripts": {
|
|
@@ -86,7 +87,7 @@
|
|
|
86
87
|
"test:plugin-store": "npx tsx src/plugins/tests/standalone-test.ts",
|
|
87
88
|
"test:pattern-store": "npx tsx src/transfer/store/tests/standalone-test.ts",
|
|
88
89
|
"postinstall": "node ./scripts/postinstall.cjs",
|
|
89
|
-
"prepublishOnly": "cp ../../../README.md ./README.md",
|
|
90
|
+
"prepublishOnly": "cp ../../../README.md ./README.md && rm -rf plugins && mkdir -p plugins && cp -r ../../../plugins/ruflo-metaharness plugins/",
|
|
90
91
|
"release": "npm version prerelease --preid=alpha && npm run publish:all",
|
|
91
92
|
"publish:all": "./scripts/publish.sh"
|
|
92
93
|
},
|
|
@@ -95,13 +96,13 @@
|
|
|
95
96
|
"vitest": "^4.0.16"
|
|
96
97
|
},
|
|
97
98
|
"dependencies": {
|
|
98
|
-
"@claude-flow/cli-core": "
|
|
99
|
-
"@claude-flow/mcp": "
|
|
100
|
-
"@claude-flow/neural": "
|
|
101
|
-
"@claude-flow/shared": "
|
|
102
|
-
"@noble/ed25519": "
|
|
103
|
-
"@ruvector/rabitq-wasm": "
|
|
104
|
-
"semver": "
|
|
99
|
+
"@claude-flow/cli-core": "3.7.0-alpha.5",
|
|
100
|
+
"@claude-flow/mcp": "3.0.0-alpha.8",
|
|
101
|
+
"@claude-flow/neural": "3.0.0-alpha.9",
|
|
102
|
+
"@claude-flow/shared": "3.0.0-alpha.7",
|
|
103
|
+
"@noble/ed25519": "2.3.0",
|
|
104
|
+
"@ruvector/rabitq-wasm": "0.1.0",
|
|
105
|
+
"semver": "7.7.3",
|
|
105
106
|
"yaml": "^2.8.0"
|
|
106
107
|
},
|
|
107
108
|
"optionalDependencies": {
|
|
@@ -112,7 +113,9 @@
|
|
|
112
113
|
"@claude-flow/memory": "^3.0.0-alpha.20",
|
|
113
114
|
"@claude-flow/plugin-gastown-bridge": "^0.1.3",
|
|
114
115
|
"@claude-flow/security": "^3.0.0-alpha.10",
|
|
115
|
-
"@metaharness/
|
|
116
|
+
"@metaharness/kernel": "~0.1.0",
|
|
117
|
+
"@metaharness/router": "~0.3.2",
|
|
118
|
+
"metaharness": "~0.1.11",
|
|
116
119
|
"@ruvector/attention": "^0.1.32",
|
|
117
120
|
"@ruvector/attention-darwin-arm64": "0.1.32",
|
|
118
121
|
"@ruvector/diskann": "^0.1.0",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"edit","file":"/Users/cohen/Projects/ruflo/plugins/ruflo-metaharness/scripts/test-mcp-tools.mjs","timestamp":1781649922735}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ruflo-metaharness",
|
|
3
|
+
"description": "MetaHarness integration for ruflo — surfaces score/genome/mint/mcp-scan/threat-model via skills; pairs with @metaharness/router (ADR-148/149) for cost-optimal model routing; honors ADR-150's architectural constraint that MetaHarness remains an optional augmentation, never a required runtime dependency",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "ruvnet",
|
|
7
|
+
"url": "https://github.com/ruvnet"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/ruvnet/ruflo",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"ruflo",
|
|
13
|
+
"metaharness",
|
|
14
|
+
"harness",
|
|
15
|
+
"scaffolding",
|
|
16
|
+
"scorecard",
|
|
17
|
+
"genome",
|
|
18
|
+
"mcp-scan",
|
|
19
|
+
"threat-model",
|
|
20
|
+
"router",
|
|
21
|
+
"adr-150",
|
|
22
|
+
"adr-148",
|
|
23
|
+
"adr-149",
|
|
24
|
+
"optional-dependency",
|
|
25
|
+
"graceful-degradation",
|
|
26
|
+
"subprocess",
|
|
27
|
+
"namespace-routing",
|
|
28
|
+
"verified",
|
|
29
|
+
"ci",
|
|
30
|
+
"phase-1-mvp"
|
|
31
|
+
]
|
|
32
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# ruflo-metaharness
|
|
2
|
+
|
|
3
|
+
MetaHarness integration plugin for ruflo. Surfaces the upstream `metaharness` / `harness` CLIs through five ruflo skills, honoring [ADR-150](../../v3/docs/adr/ADR-150-metaharness-integration-surfaces.md)'s architectural constraint that MetaHarness must remain a removable augmentation — never a required runtime dependency.
|
|
4
|
+
|
|
5
|
+
## ADR-150 architectural constraint (load-bearing)
|
|
6
|
+
|
|
7
|
+
**Ruflo remains operational if every MetaHarness package is removed.** Every code path in this plugin satisfies four rules:
|
|
8
|
+
|
|
9
|
+
1. **Removable** — no static `import '@metaharness/*'` outside the optional-router path in `v3/@claude-flow/cli/src/ruvector/neural-router.ts`.
|
|
10
|
+
2. **Optional in package.json** — `metaharness` is in `optionalDependencies`, never `dependencies`.
|
|
11
|
+
3. **Graceful degradation** — every script catches `MODULE_NOT_FOUND`/network failure and emits `{ degraded: true, reason: 'metaharness-not-available' }` JSON, exits 0. The graceful path is the default behavior, not a special case.
|
|
12
|
+
4. **CI gate** — `no-metaharness-smoke.yml` runs the plugin smoke with `npm install --no-optional` and asserts the contract still passes.
|
|
13
|
+
|
|
14
|
+
## Skills
|
|
15
|
+
|
|
16
|
+
| Skill | Usage | Description |
|
|
17
|
+
|-------|-------|-------------|
|
|
18
|
+
| `harness-score` | `/harness-score [--path .] [--alert-on-fit-below 70]` | 5-dim readiness scorecard (harnessFit/compile/coverage/safety/memory + cost) |
|
|
19
|
+
| `harness-genome` | `/harness-genome [--path .] [--alert-on-risk-above 0.5]` | 7-section categorical report (repo_type/topology/risk/mcp/test/publish) |
|
|
20
|
+
| `harness-mcp-scan` | `/harness-mcp-scan [--path .] [--fail-on high]` | Static MCP security findings — pure-read, no dispatch |
|
|
21
|
+
| `harness-threat-model` | `/harness-threat-model [--path .] [--fail-on high]` | Enterprise-grade threat model (clean/low/medium/high + findings) |
|
|
22
|
+
| `harness-mint` | `/harness-mint --name <id> --template <id> [--confirm]` | Scaffold a custom harness; DRY-RUN by default; refuses project-root writes |
|
|
23
|
+
|
|
24
|
+
## Phase-0 baseline (ruflo itself, 2026-06-16)
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"harnessFit": 82,
|
|
29
|
+
"compileConfidence": 100,
|
|
30
|
+
"taskCoverage": 79,
|
|
31
|
+
"toolSafety": 100,
|
|
32
|
+
"memoryUsefulness": 40,
|
|
33
|
+
"estCostPerRunUsd": 0.048,
|
|
34
|
+
"recommendedMode": "CLI + MCP",
|
|
35
|
+
"archetype": "typescript-sdk-harness",
|
|
36
|
+
"template": "vertical:coding",
|
|
37
|
+
"scaffoldReady": true,
|
|
38
|
+
"risk_score": 0.27,
|
|
39
|
+
"publish_readiness": 0.9
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Architecture
|
|
44
|
+
|
|
45
|
+
All skills use subprocess invocation through the `_harness.mjs` shared helper:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
skills/X/SKILL.md → scripts/X.mjs → scripts/_harness.mjs → spawnSync('npx', ['metaharness', …])
|
|
49
|
+
↘ on MODULE_NOT_FOUND → emit degraded JSON, exit 0
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
This means:
|
|
53
|
+
- No library import overhead on ruflo's boot path
|
|
54
|
+
- 60s hard timeout per subprocess (bounded blast radius)
|
|
55
|
+
- `--json` flag forced for structured parsing
|
|
56
|
+
- Graceful degradation is a single helper used by every skill
|
|
57
|
+
|
|
58
|
+
## Cross-links
|
|
59
|
+
|
|
60
|
+
- [ADR-150](../../v3/docs/adr/ADR-150-metaharness-integration-surfaces.md) — decision + architectural constraint
|
|
61
|
+
- [Issue #2399](https://github.com/ruvnet/ruflo/issues/2399) — phase rollout tracker
|
|
62
|
+
- [Research dossier](https://gist.github.com/ruvnet/19d166ff9acf368c9da4172d91ac9113) — full graded-evidence sourcing
|
|
63
|
+
- [Upstream](https://github.com/ruvnet/agent-harness-generator) — `metaharness` source
|
|
64
|
+
- ADR-148/149 — `@metaharness/router` cost-optimal routing (sibling integration)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: metaharness-architect
|
|
3
|
+
description: MetaHarness integration architect for ruflo. Surfaces score/genome/mint/mcp-scan/threat-model upstream capabilities via skills; enforces ADR-150 architectural constraint (MetaHarness as removable augmentation, never required runtime dep); coordinates Phase 1 MVP rollout
|
|
4
|
+
model: haiku
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are the metaharness-architect for ruflo. Your job is to expose the
|
|
8
|
+
`@metaharness/*` ecosystem's capabilities through ruflo's UX while
|
|
9
|
+
keeping ruflo independently operational at all times.
|
|
10
|
+
|
|
11
|
+
## ADR-150 invariants (load-bearing)
|
|
12
|
+
|
|
13
|
+
1. **Removable** — `npm ls --without-deps @metaharness/*` must still
|
|
14
|
+
produce a working CLI.
|
|
15
|
+
2. **Optional in package.json** — every `@metaharness/*` package goes in
|
|
16
|
+
`optionalDependencies` or `peerDependencies` (optional), NEVER
|
|
17
|
+
`dependencies`.
|
|
18
|
+
3. **Graceful degradation** — every code path that imports a
|
|
19
|
+
`@metaharness/*` symbol catches `MODULE_NOT_FOUND` and falls back.
|
|
20
|
+
The `emitDegradedJsonAndExit()` helper in `scripts/_harness.mjs` is
|
|
21
|
+
the reference implementation.
|
|
22
|
+
4. **CI gate** — at least one CI job runs ruflo with no MetaHarness
|
|
23
|
+
packages installed and asserts smoke still passes.
|
|
24
|
+
|
|
25
|
+
If a PR breaks any of these four rules, it is a breaking change and
|
|
26
|
+
needs its own ADR.
|
|
27
|
+
|
|
28
|
+
## Skills (6 — what each does, when to invoke)
|
|
29
|
+
|
|
30
|
+
| Skill | Role | Invoke when |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| `harness-score` | 5-dim numeric scorecard | Pre-mint readiness check; CI regression gate |
|
|
33
|
+
| `harness-genome` | 7-section categorical report | Pre-mint architecture review; drift detection over time |
|
|
34
|
+
| `harness-mcp-scan` | Static MCP security findings | Every PR; enterprise security review |
|
|
35
|
+
| `harness-threat-model` | Categorized threat report | Pre-launch review; periodic OIA-audit cadence |
|
|
36
|
+
| `harness-oia-audit` | Composite weekly audit worker (iter 7) | Cron-scheduled; bundles oia+threat+mcp into one timestamped record in `metaharness-audit` namespace |
|
|
37
|
+
| `harness-mint` | Scaffold a custom harness | User wants to fork; ALWAYS dry-run first, never write to project root |
|
|
38
|
+
|
|
39
|
+
## Tools (subprocess-only, no library imports)
|
|
40
|
+
|
|
41
|
+
- All skills shell out to `npx metaharness ...` or `npx -p metaharness@latest harness ...` via the `_harness.mjs` shared helper.
|
|
42
|
+
- 60s hard timeout per subprocess.
|
|
43
|
+
- Output captured + parsed; `--json` flag forced unless the script opts out.
|
|
44
|
+
- No `@metaharness/*` import statement appears outside the optional-router path in `v3/@claude-flow/cli/src/ruvector/neural-router.ts`.
|
|
45
|
+
|
|
46
|
+
## Phase tracker
|
|
47
|
+
|
|
48
|
+
- ✅ Phase 0 — measurement spike (ruflo's own scorecard captured 2026-06-16: harnessFit 82, risk_score 0.27, publish_readiness 0.9)
|
|
49
|
+
- 🔄 Phase 1 — MVP plugin (this commit + CI gates + KRR retraining)
|
|
50
|
+
- ⏳ Phase 2 — Expansion (eject command, SelfEvolvingRouter parallel-log, harness registry, oia-audit worker)
|
|
51
|
+
- ⏳ Phase 3 — Harness Intelligence Layer (each item gets its own ADR)
|
|
52
|
+
|
|
53
|
+
## Cross-links
|
|
54
|
+
|
|
55
|
+
- [ADR-150](../../../v3/docs/adr/ADR-150-metaharness-integration-surfaces.md) — decision
|
|
56
|
+
- [Issue #2399](https://github.com/ruvnet/ruflo/issues/2399) — phase tracker
|
|
57
|
+
- [Research dossier](https://gist.github.com/ruvnet/19d166ff9acf368c9da4172d91ac9113) — graded evidence
|
|
58
|
+
- [Upstream](https://github.com/ruvnet/agent-harness-generator) — `metaharness` source
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ruflo-metaharness
|
|
3
|
+
description: MetaHarness integration — score, genome, mint, mcp-scan, threat-model — via subprocess invocations honoring ADR-150 architectural constraint
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
MetaHarness integration commands. All shell out to `npx metaharness ...`
|
|
7
|
+
or `npx -p metaharness@latest harness ...` via the `_harness.mjs`
|
|
8
|
+
shared helper; no library imports on ruflo's boot path.
|
|
9
|
+
|
|
10
|
+
**`harness score [--path .] [--alert-on-fit-below N] [--format table|json]`** -- 5-dimension readiness scorecard (harnessFit / compileConfidence / taskCoverage / toolSafety / memoryUsefulness + estCostPerRunUsd + scaffoldReady).
|
|
11
|
+
1. Run `node plugins/ruflo-metaharness/scripts/score.mjs --path <dir>`
|
|
12
|
+
2. Output JSON (default) with all dimensions + recommended template + archetype
|
|
13
|
+
3. `--alert-on-fit-below N` exits 1 when harnessFit < N — CI regression gate
|
|
14
|
+
4. Subprocess + 60s hard timeout; graceful degradation when metaharness unavailable
|
|
15
|
+
|
|
16
|
+
**`harness genome [--path .] [--alert-on-risk-above 0.5] [--format table|json]`** -- 7-section repo readiness report (repo_type / agent_topology / risk_score / mcp_surface / test_confidence / publish_readiness).
|
|
17
|
+
1. Run `node plugins/ruflo-metaharness/scripts/genome.mjs --path <dir>`
|
|
18
|
+
2. Pairs with harness-score for full readiness view — score is numeric, genome is categorical
|
|
19
|
+
3. `--alert-on-risk-above N` exits 1 when risk_score > N
|
|
20
|
+
4. Useful for drift detection: snapshot genome over time, diff to spot agent_topology drift
|
|
21
|
+
|
|
22
|
+
**`harness mcp-scan [--path .] [--fail-on low|medium|high] [--format table|json]`** -- Static security scan of `.mcp/servers.json` + `.harness/claims.json`. Reads only; no dispatch.
|
|
23
|
+
1. Run `node plugins/ruflo-metaharness/scripts/mcp-scan.mjs --path <dir>`
|
|
24
|
+
2. Severity-ranked findings (low/medium/high); default fail-on `high`
|
|
25
|
+
3. CI integration: `mcp-scan --fail-on high` fails the build on any HIGH finding
|
|
26
|
+
4. Pairs with harness-threat-model for enterprise-review-grade categorization
|
|
27
|
+
|
|
28
|
+
**`harness threat-model [--path .] [--fail-on clean|low|medium|high] [--format table|json]`** -- Enterprise-review threat model. Returns `worst` severity + categorized `findings[]`.
|
|
29
|
+
1. Run `node plugins/ruflo-metaharness/scripts/threat-model.mjs --path <dir>`
|
|
30
|
+
2. Default fail-on `high`; tightenable to `medium` for stricter gates
|
|
31
|
+
3. Output suitable for sharing with security/infosec team
|
|
32
|
+
4. Will be auto-fired by the Phase-2 oia-audit background worker on a schedule
|
|
33
|
+
|
|
34
|
+
**`harness oia-audit [--path .] [--dry-run] [--alert-on-worst clean|low|medium|high] [--format table|json]`** -- Phase-2 composite worker (ADR-150). Bundles oia-manifest + threat-model + mcp-scan into one timestamped audit record, stores in `metaharness-audit` memory namespace.
|
|
35
|
+
1. Run `node plugins/ruflo-metaharness/scripts/oia-audit.mjs`
|
|
36
|
+
2. Composite worst-severity = max(threatModel.worst, mcpScan.findings.severity)
|
|
37
|
+
3. `--alert-on-worst high` exits 1 when composite ≥ high — CI weekly drift gate
|
|
38
|
+
4. `--dry-run` skips memory persistence — useful for local checks
|
|
39
|
+
5. Designed for cron schedule: weekly snapshot enables audit drift tracking via memory diff
|
|
40
|
+
|
|
41
|
+
**`harness mint --name <id> --template <vertical:coding|minimal|…> [--host claude-code|codex|…] [--target /abs/path] [--confirm] [--format table|json]`** -- Scaffold a custom AI agent harness. DRY-RUN by default; --confirm required to write.
|
|
42
|
+
1. Run `node plugins/ruflo-metaharness/scripts/mint.mjs --name <id> --template <id> --host <id>`
|
|
43
|
+
2. **Safety**: refuses to write to the calling repo root or any path inside it; defaults to `/tmp/ruflo-mint-<ts>-<name>/`
|
|
44
|
+
3. Without `--confirm`: prints dry-run plan, exits 0 without touching disk
|
|
45
|
+
4. With `--confirm`: shells `npx metaharness new ... --yes`
|
|
46
|
+
5. Templates: minimal + 19 verticals (coding, devops, support, legal, …). Hosts: claude-code, codex, pi-dev, opencode, github-actions, +4 more.
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
// _harness.mjs — shared invocation helper for the metaharness/harness CLIs.
|
|
2
|
+
//
|
|
3
|
+
// All ruflo-metaharness skills shell out to the upstream CLI rather than
|
|
4
|
+
// linking the library — this honors ADR-150's architectural constraint
|
|
5
|
+
// (MetaHarness must remain a removable augmentation, never a required
|
|
6
|
+
// runtime dep) while still giving us "deep integration" through a single
|
|
7
|
+
// vetted bridge that every skill imports from.
|
|
8
|
+
//
|
|
9
|
+
// CONTRACT
|
|
10
|
+
// - `runMetaharness(args, opts)` — invoke `npx metaharness <args>`
|
|
11
|
+
// - `runHarness(args, opts)` — invoke `npx -p metaharness harness <args>`
|
|
12
|
+
// - both return `{ stdout, stderr, exitCode, json|null, durationMs }`
|
|
13
|
+
// - `--json` flag is appended automatically when `opts.json !== false`
|
|
14
|
+
// - subprocess hard timeout (default 60s) — captured in opts.timeoutMs
|
|
15
|
+
// - on MODULE_NOT_FOUND or "not installed", returns degraded result with
|
|
16
|
+
// `degraded: true, reason: 'metaharness-not-available'` — never throws
|
|
17
|
+
// (ADR-150 graceful-degradation rule #3)
|
|
18
|
+
|
|
19
|
+
import { spawnSync, spawn } from 'node:child_process';
|
|
20
|
+
|
|
21
|
+
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* iter 56 — async variant of execCli. Used by oia-audit.mjs to parallelize
|
|
25
|
+
* its 5 subprocess calls, dropping worst-case wall-clock from 5×TIMEOUT
|
|
26
|
+
* (sequential) to 1×TIMEOUT (parallel). Identical return shape to the
|
|
27
|
+
* sync execCli so callers can swap without ceremony.
|
|
28
|
+
*/
|
|
29
|
+
function execCliAsync(npxArgs, opts = {}) {
|
|
30
|
+
return new Promise((resolve) => {
|
|
31
|
+
const start = Date.now();
|
|
32
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
33
|
+
const wantJson = opts.json !== false;
|
|
34
|
+
const argv = wantJson && !npxArgs.includes('--json') ? [...npxArgs, '--json'] : [...npxArgs];
|
|
35
|
+
const p = spawn('npx', argv, {
|
|
36
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
37
|
+
cwd: opts.cwd,
|
|
38
|
+
env: { ...process.env, ...(opts.env || {}) },
|
|
39
|
+
shell: process.platform === 'win32',
|
|
40
|
+
});
|
|
41
|
+
let stdout = '', stderr = '';
|
|
42
|
+
p.stdout?.on('data', (d) => { stdout += d.toString(); });
|
|
43
|
+
p.stderr?.on('data', (d) => { stderr += d.toString(); });
|
|
44
|
+
const timer = setTimeout(() => {
|
|
45
|
+
try { p.kill('SIGTERM'); } catch { /* ignore */ }
|
|
46
|
+
}, timeoutMs);
|
|
47
|
+
p.on('error', (e) => {
|
|
48
|
+
clearTimeout(timer);
|
|
49
|
+
resolve({
|
|
50
|
+
stdout, stderr: stderr + String(e?.message ?? e),
|
|
51
|
+
exitCode: 127, json: null, durationMs: Date.now() - start,
|
|
52
|
+
degraded: true, reason: 'metaharness-not-available',
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
p.on('close', (code) => {
|
|
56
|
+
clearTimeout(timer);
|
|
57
|
+
const durationMs = Date.now() - start;
|
|
58
|
+
if (code === null || /could not determine executable|404|not installed|MODULE_NOT_FOUND|ENOTFOUND|getaddrinfo|ECONNREFUSED|ETIMEDOUT/i.test(stderr)) {
|
|
59
|
+
resolve({
|
|
60
|
+
stdout, stderr,
|
|
61
|
+
exitCode: code ?? 127, json: null, durationMs,
|
|
62
|
+
degraded: true, reason: 'metaharness-not-available',
|
|
63
|
+
});
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
let json = null;
|
|
67
|
+
if (wantJson) {
|
|
68
|
+
const m = /\{[\s\S]*\}/.exec(stdout);
|
|
69
|
+
if (m) { try { json = JSON.parse(m[0]); } catch { /* leave null */ } }
|
|
70
|
+
}
|
|
71
|
+
resolve({ stdout, stderr, exitCode: code ?? 0, json, durationMs, degraded: false });
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function runMetaharnessAsync(args, opts) {
|
|
77
|
+
return execCliAsync(['-y', 'metaharness@latest', ...args], opts);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function runHarnessAsync(args, opts) {
|
|
81
|
+
return execCliAsync(['-y', '-p', 'metaharness@latest', 'harness', ...args], opts);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ITER 27 — npx invocation hardening.
|
|
85
|
+
// The pre-iter-27 implementation passed `'-y metaharness@latest'` as a
|
|
86
|
+
// SINGLE argv element to npx (`spawnSync('npx', [bin, ...argv])` where
|
|
87
|
+
// bin contained two whitespace-separated tokens). spawnSync with
|
|
88
|
+
// shell:false does no word-splitting, so npx received a literal string
|
|
89
|
+
// with an embedded space and either failed silently or treated the
|
|
90
|
+
// whole thing as a package name. The graceful-degradation path then
|
|
91
|
+
// reported `degraded: true` for every skill — masking the bug. Every
|
|
92
|
+
// argv token must now be its own array element.
|
|
93
|
+
function execCli(npxArgs, opts = {}) {
|
|
94
|
+
const start = Date.now();
|
|
95
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
96
|
+
const wantJson = opts.json !== false;
|
|
97
|
+
const argv = wantJson && !npxArgs.includes('--json') ? [...npxArgs, '--json'] : [...npxArgs];
|
|
98
|
+
const r = spawnSync('npx', argv, {
|
|
99
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
100
|
+
encoding: 'utf-8',
|
|
101
|
+
timeout: timeoutMs,
|
|
102
|
+
cwd: opts.cwd, // iter 27 — let callers redirect $CWD (mint.mjs needs this)
|
|
103
|
+
env: { ...process.env, ...(opts.env || {}) },
|
|
104
|
+
shell: process.platform === 'win32',
|
|
105
|
+
});
|
|
106
|
+
const durationMs = Date.now() - start;
|
|
107
|
+
const stdout = r.stdout || '';
|
|
108
|
+
const stderr = r.stderr || '';
|
|
109
|
+
// Graceful degradation — npx couldn't find the binary.
|
|
110
|
+
if (r.status === null || /could not determine executable|404|not installed|MODULE_NOT_FOUND|ENOTFOUND|getaddrinfo|ECONNREFUSED|ETIMEDOUT/i.test(stderr)) {
|
|
111
|
+
return {
|
|
112
|
+
stdout, stderr,
|
|
113
|
+
exitCode: r.status ?? 127,
|
|
114
|
+
json: null,
|
|
115
|
+
durationMs,
|
|
116
|
+
degraded: true,
|
|
117
|
+
reason: 'metaharness-not-available',
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
let json = null;
|
|
121
|
+
if (wantJson) {
|
|
122
|
+
const m = /\{[\s\S]*\}/.exec(stdout);
|
|
123
|
+
if (m) { try { json = JSON.parse(m[0]); } catch { /* leave null */ } }
|
|
124
|
+
}
|
|
125
|
+
return { stdout, stderr, exitCode: r.status ?? 0, json, durationMs, degraded: false };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function runMetaharness(args, opts) {
|
|
129
|
+
// iter 27 — explicit argv tokens (was: '-y metaharness@latest' as one
|
|
130
|
+
// string, which silently degraded every skill).
|
|
131
|
+
return execCli(['-y', 'metaharness@latest', ...args], opts);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function runHarness(args, opts) {
|
|
135
|
+
// The `harness` binary ships inside the `metaharness` package, so we
|
|
136
|
+
// need `npx -p metaharness@latest harness <args>`. spawnSync receives
|
|
137
|
+
// a single argv array, so encode the `-p` flag as its own argument.
|
|
138
|
+
const start = Date.now();
|
|
139
|
+
const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
140
|
+
const wantJson = opts?.json !== false;
|
|
141
|
+
const argv = wantJson && !args.includes('--json') ? [...args, '--json'] : [...args];
|
|
142
|
+
const r = spawnSync('npx', ['-y', '-p', 'metaharness@latest', 'harness', ...argv], {
|
|
143
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
144
|
+
encoding: 'utf-8',
|
|
145
|
+
timeout: timeoutMs,
|
|
146
|
+
cwd: opts?.cwd, // iter 27 — same cwd-redirect support as runMetaharness
|
|
147
|
+
env: { ...process.env, ...(opts?.env || {}) },
|
|
148
|
+
shell: process.platform === 'win32',
|
|
149
|
+
});
|
|
150
|
+
const durationMs = Date.now() - start;
|
|
151
|
+
const stdout = r.stdout || '';
|
|
152
|
+
const stderr = r.stderr || '';
|
|
153
|
+
if (r.status === null || /could not determine executable|404|not installed|MODULE_NOT_FOUND|ENOTFOUND|getaddrinfo|ECONNREFUSED|ETIMEDOUT/i.test(stderr)) {
|
|
154
|
+
return {
|
|
155
|
+
stdout, stderr,
|
|
156
|
+
exitCode: r.status ?? 127,
|
|
157
|
+
json: null,
|
|
158
|
+
durationMs,
|
|
159
|
+
degraded: true,
|
|
160
|
+
reason: 'metaharness-not-available',
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
let json = null;
|
|
164
|
+
if (wantJson) {
|
|
165
|
+
const m = /\{[\s\S]*\}/.exec(stdout);
|
|
166
|
+
if (m) { try { json = JSON.parse(m[0]); } catch { /* leave null */ } }
|
|
167
|
+
}
|
|
168
|
+
return { stdout, stderr, exitCode: r.status ?? 0, json, durationMs, degraded: false };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* iter 63 — single source of truth for severity ranks across the
|
|
173
|
+
* metaharness plugin family. Pre-iter-63, three scripts (oia-audit,
|
|
174
|
+
* audit-trend, mcp-scan) maintained their own SEVERITY_RANK literal,
|
|
175
|
+
* each missing different keys, each producing different NaN-compare
|
|
176
|
+
* behavior on unknown severities. Iter 62 fixed oia-audit; iter 63
|
|
177
|
+
* propagates the fix and consolidates.
|
|
178
|
+
*
|
|
179
|
+
* Mapping rationale:
|
|
180
|
+
* clean / info → 0 (no harm)
|
|
181
|
+
* low → 1
|
|
182
|
+
* medium / warn → 2
|
|
183
|
+
* high / error → 3
|
|
184
|
+
* critical → 4 (explicit elevation above high)
|
|
185
|
+
*
|
|
186
|
+
* `rankSeverity(s)` is the safe accessor — returns 0 for any unknown
|
|
187
|
+
* string instead of `undefined`, eliminating the NaN-compare hazard
|
|
188
|
+
* (`undefined > 3` evaluates to false → unknown severities silently
|
|
189
|
+
* ignored in reduce expressions).
|
|
190
|
+
*/
|
|
191
|
+
export const SEVERITY_RANK = Object.freeze({
|
|
192
|
+
clean: 0, info: 0,
|
|
193
|
+
low: 1,
|
|
194
|
+
medium: 2, warn: 2,
|
|
195
|
+
high: 3, error: 3,
|
|
196
|
+
critical: 4,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
export function rankSeverity(s) {
|
|
200
|
+
if (s == null) return 0;
|
|
201
|
+
return SEVERITY_RANK[String(s).toLowerCase()] ?? 0;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* iter 50 — parse `harness mcp-scan` text output into structured findings.
|
|
206
|
+
*
|
|
207
|
+
* Upstream `harness mcp-scan` emits plain text even with --json:
|
|
208
|
+
*
|
|
209
|
+
* harness mcp-scan — <path>
|
|
210
|
+
*
|
|
211
|
+
* [INFO] No MCP security issues found
|
|
212
|
+
* Policy is default-deny with safe capability grants and an audit log.
|
|
213
|
+
*
|
|
214
|
+
* Result: INFO (1 finding, 0 high)
|
|
215
|
+
*
|
|
216
|
+
* Closes the iter-49-flagged gap where audit-trend.mjs reads
|
|
217
|
+
* `json.findings` expecting an array, but mcp-scan's r.json was null.
|
|
218
|
+
* Used by BOTH mcp-scan.mjs (the wrapper) and oia-audit.mjs (composite
|
|
219
|
+
* audit) so the structured-findings invariant holds across the pipeline.
|
|
220
|
+
*/
|
|
221
|
+
export function parseMcpScanText(stdout) {
|
|
222
|
+
const findings = [];
|
|
223
|
+
const lines = (stdout || '').split('\n');
|
|
224
|
+
let current = null;
|
|
225
|
+
for (const line of lines) {
|
|
226
|
+
const m = /^\s*\[([A-Z]+)\]\s+(.+?)\s*$/.exec(line);
|
|
227
|
+
if (m) {
|
|
228
|
+
if (current) findings.push(current);
|
|
229
|
+
current = { severity: m[1].toLowerCase(), message: m[2] };
|
|
230
|
+
} else if (current && /^\s{6,}\S/.test(line)) {
|
|
231
|
+
const cont = line.trim();
|
|
232
|
+
if (cont) current.message += ' ' + cont;
|
|
233
|
+
} else if (current && line.trim() === '') {
|
|
234
|
+
findings.push(current);
|
|
235
|
+
current = null;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (current) findings.push(current);
|
|
239
|
+
const resultMatch = /Result:\s+([A-Z]+)\s+\((\d+)\s+finding/i.exec(stdout);
|
|
240
|
+
const summary = resultMatch ? {
|
|
241
|
+
overallSeverity: resultMatch[1].toLowerCase(),
|
|
242
|
+
totalCount: parseInt(resultMatch[2], 10),
|
|
243
|
+
} : null;
|
|
244
|
+
return { findings, summary };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Convenience emitters for skill scripts — keep the boilerplate out of
|
|
248
|
+
// each skill so they focus on argument parsing + exit-code semantics.
|
|
249
|
+
export function emitDegradedJsonAndExit(reason) {
|
|
250
|
+
const payload = {
|
|
251
|
+
degraded: true,
|
|
252
|
+
reason,
|
|
253
|
+
hint: 'Install metaharness manually with `npm i -D metaharness` or run `npx metaharness@latest --version` to verify network access.',
|
|
254
|
+
generatedAt: new Date().toISOString(),
|
|
255
|
+
};
|
|
256
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
257
|
+
// Exit 0 — ADR-150 architectural constraint says ruflo continues to
|
|
258
|
+
// function when MetaHarness is absent. Skills emit a structured
|
|
259
|
+
// degraded payload rather than failing.
|
|
260
|
+
process.exit(0);
|
|
261
|
+
}
|