@claude-flow/cli 3.12.4 → 3.13.0

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.
@@ -0,0 +1,174 @@
1
+ #!/usr/bin/env node
2
+ // security-bench.mjs — wrapper around `metaharness-darwin security bench`.
3
+ //
4
+ // "Darwin Shield" — upstream's own ADR-155 — evolves a champion harness
5
+ // on a 10-vuln/9-decoy corpus and measures it against four baselines
6
+ // (B0 static-only, B1 LLM single-pass, B2 fixed agent, B3 Darwin champion).
7
+ // The CHAMPION reaches TPR=1, FPR=0; the eight acceptance gates verify
8
+ // reproducibility, statistical significance, and unsafe-output=0.
9
+ //
10
+ // CONNECTION TO RUFLO ADR-155
11
+ // ===========================
12
+ // ruflo's ADR-155 (#2417) proposes a nightly self-learning security harness
13
+ // with three learning loops (per-dimension confidence weighting, severity
14
+ // calibration, auto-fix bid). The upstream Darwin Shield is the closest
15
+ // reference implementation — same shape, different scope (evolves a
16
+ // security-detection harness vs evaluates findings; both grade by realized
17
+ // TPR/FPR vs ground-truth corpus). Running `security bench` periodically
18
+ // gives us the empirical baseline that ruflo's Phase 2 loop A needs before
19
+ // training: if Darwin Shield converges on a known-good corpus, the loop A
20
+ // gradient signal is sound; if it doesn't, the corpus / sandbox is the
21
+ // gap, not the learning algorithm.
22
+ //
23
+ // USAGE
24
+ // node scripts/security-bench.mjs # default population=2 cycles=1
25
+ // node scripts/security-bench.mjs --population 4 --cycles 3 # deeper run
26
+ // node scripts/security-bench.mjs --population 4 --cycles 3 --alert-on-fail
27
+ //
28
+ // EXIT CODES
29
+ // 0 bench passed all gates (or degraded — Darwin not available)
30
+ // 1 --alert-on-fail and any acceptance gate failed
31
+ // 2 config error or bench infrastructure failure
32
+
33
+ import { runDarwinAsync, emitDarwinDegradedJsonAndExit } from './_darwin.mjs';
34
+
35
+ const ARGS = (() => {
36
+ const a = {
37
+ population: 2,
38
+ cycles: 1,
39
+ seed: null,
40
+ alertOnFail: false,
41
+ format: 'json',
42
+ timeoutMs: null,
43
+ };
44
+ for (let i = 2; i < process.argv.length; i++) {
45
+ const v = process.argv[i];
46
+ if (v === '--population') a.population = parseInt(process.argv[++i], 10);
47
+ else if (v === '--cycles') a.cycles = parseInt(process.argv[++i], 10);
48
+ else if (v === '--seed') a.seed = parseInt(process.argv[++i], 10);
49
+ else if (v === '--alert-on-fail') a.alertOnFail = true;
50
+ else if (v === '--format') a.format = process.argv[++i];
51
+ else if (v === '--timeout-ms') a.timeoutMs = parseInt(process.argv[++i], 10);
52
+ }
53
+ return a;
54
+ })();
55
+
56
+ function safetyChecks() {
57
+ if (ARGS.population < 1 || ARGS.population > 20) {
58
+ console.error('security-bench: --population must be 1..20 (ruflo cap)');
59
+ process.exit(2);
60
+ }
61
+ if (ARGS.cycles < 1 || ARGS.cycles > 100) {
62
+ console.error('security-bench: --cycles must be 1..100 (ruflo cap)');
63
+ process.exit(2);
64
+ }
65
+ }
66
+
67
+ function defaultTimeoutMs() {
68
+ // Bench corpus has 10 vulns + 9 decoys = 19 evaluations per cycle.
69
+ // Each evaluation runs the candidate detector + scores patches.
70
+ // Rough budget: ~3s per evaluation × population × cycles + 30s overhead.
71
+ return Math.max(60_000, 3_000 * 19 * ARGS.population * ARGS.cycles + 30_000);
72
+ }
73
+
74
+ // Parse the markdown report that `security bench` emits. The header
75
+ // "Overall: ✅ PASS" or "❌ FAIL" is the rolled-up gate. We also extract
76
+ // each acceptance gate's pass/fail line.
77
+ function parseSecurityBenchMarkdown(stdout) {
78
+ const overallMatch = /\*\*Overall:\s*([✅❌])\s*(PASS|FAIL)\*\*/i.exec(stdout);
79
+ const overall = overallMatch ? { ok: overallMatch[2].toUpperCase() === 'PASS', icon: overallMatch[1] } : null;
80
+
81
+ const gates = [];
82
+ // Match lines like: "- ✅ **TPR improvement ≥ 25% vs fixed harness** — +150%"
83
+ const gateRx = /^-\s+([✅❌])\s+\*\*(.+?)\*\*\s*[—-]\s*(.+)$/gm;
84
+ let m;
85
+ while ((m = gateRx.exec(stdout)) !== null) {
86
+ gates.push({
87
+ ok: m[1] === '✅',
88
+ criterion: m[2].trim(),
89
+ measured: m[3].trim(),
90
+ });
91
+ }
92
+
93
+ // Extract the baselines-vs-champion table — useful for diff over time.
94
+ const baselines = [];
95
+ const tableRx = /^\|\s*B[0-3]\s+([^|]+?)\s*\|\s*([\d.]+)\s*\|\s*([\d.]+)\s*\|\s*([\d.]+)\s*\|\s*([\d.]+)\s*\|\s*([\d.]+)\s*\|\s*(\d+)\s*\|\s*(.+?)\s*\|/gm;
96
+ while ((m = tableRx.exec(stdout)) !== null) {
97
+ baselines.push({
98
+ harness: m[1].trim(),
99
+ fitness: parseFloat(m[2]),
100
+ tpr: parseFloat(m[3]),
101
+ fpr: parseFloat(m[4]),
102
+ patchPass: parseFloat(m[5]),
103
+ repro: parseFloat(m[6]),
104
+ unsafe: parseInt(m[7], 10),
105
+ cost: m[8].trim(),
106
+ });
107
+ }
108
+
109
+ return { overall, gates, baselines };
110
+ }
111
+
112
+ async function main() {
113
+ safetyChecks();
114
+
115
+ const cliArgs = ['security', 'bench',
116
+ '--population', String(ARGS.population),
117
+ '--cycles', String(ARGS.cycles),
118
+ ];
119
+ if (ARGS.seed != null) cliArgs.push('--seed', String(ARGS.seed));
120
+
121
+ const r = await runDarwinAsync(cliArgs, {
122
+ timeoutMs: ARGS.timeoutMs ?? defaultTimeoutMs(),
123
+ json: false, // bench output is markdown, not JSON
124
+ onProgress: (line) => { if (line.trim()) process.stderr.write(`[security-bench] ${line}\n`); },
125
+ });
126
+
127
+ if (r.degraded) {
128
+ emitDarwinDegradedJsonAndExit(r.reason);
129
+ return;
130
+ }
131
+
132
+ if (r.exitCode !== 0 && r.exitCode !== 1) {
133
+ // Exit 1 may be the bench's own "gates failed" signal; treat as data.
134
+ // Anything else is infrastructure failure.
135
+ const payload = {
136
+ success: false,
137
+ data: { exitCode: r.exitCode, stderrTail: r.stderr.slice(-400) },
138
+ generatedAt: new Date().toISOString(),
139
+ };
140
+ console.log(JSON.stringify(payload, null, 2));
141
+ process.exit(2);
142
+ }
143
+
144
+ const parsed = parseSecurityBenchMarkdown(r.stdout);
145
+ const gatesPassed = parsed.gates.filter((g) => g.ok).length;
146
+ const gatesFailed = parsed.gates.length - gatesPassed;
147
+
148
+ const payload = {
149
+ success: true,
150
+ data: {
151
+ overall: parsed.overall,
152
+ gates: {
153
+ total: parsed.gates.length,
154
+ passed: gatesPassed,
155
+ failed: gatesFailed,
156
+ details: parsed.gates,
157
+ },
158
+ baselines: parsed.baselines,
159
+ rawMarkdown: r.stdout,
160
+ shape: { population: ARGS.population, cycles: ARGS.cycles, seed: ARGS.seed },
161
+ durationMs: r.durationMs,
162
+ },
163
+ generatedAt: new Date().toISOString(),
164
+ };
165
+
166
+ console.log(JSON.stringify(payload, null, 2));
167
+ if (ARGS.alertOnFail && parsed.overall && !parsed.overall.ok) process.exit(1);
168
+ process.exit(0);
169
+ }
170
+
171
+ main().catch((e) => {
172
+ console.error(`security-bench: unexpected failure: ${e?.message ?? e}`);
173
+ process.exit(2);
174
+ });
@@ -111,6 +111,14 @@ function main() {
111
111
  { name: 'mcp-scan', args: ['--format', 'json'] },
112
112
  { name: 'threat-model', args: ['--format', 'json'] },
113
113
  { name: 'oia-audit', args: ['--dry-run', '--format', 'json'] },
114
+ // ADR-153 — darwin scripts (separate optional dep @metaharness/darwin).
115
+ // evolve.mjs without --confirm short-circuits to a dry-run plan BEFORE
116
+ // any subprocess call, so it's not exercising the degraded path. Pass
117
+ // --confirm with --sandbox mock + minimal shape so we hit the subprocess
118
+ // path quickly and verify the {degraded: true} emit on registry-unreachable.
119
+ { name: 'evolve', args: ['--repo', '.', '--confirm', '--sandbox', 'mock', '--generations', '1', '--children', '1', '--concurrency', '1', '--timeout-ms', '60000'] },
120
+ { name: 'security-bench', args: ['--population', '1', '--cycles', '1', '--timeout-ms', '60000'] },
121
+ { name: 'bench', args: ['--op', 'verify', '--suite', '/dev/null'] },
114
122
  ];
115
123
 
116
124
  for (const s of skills) {
@@ -0,0 +1,64 @@
1
+ ---
2
+ name: harness-bench
3
+ description: Manage `@metaharness/darwin` bench suites — `bench create <repo>` scaffolds a JSON suite from a repo's test corpus; `bench verify <suite.json>` checks suite well-formedness. Bench suites are the fixed evaluation corpora that `harness-evolve --bench <suite.json>` scores variants against, decoupling evolution from the repo's natural tests. Degrades gracefully when @metaharness/darwin is absent.
4
+ argument-hint: "--op create --repo <path> [--out <path>] | --op verify --suite <path>"
5
+ allowed-tools: Bash
6
+ ---
7
+
8
+ Surfaces `metaharness-darwin bench <create|verify>` — the supporting verb
9
+ for `harness-evolve --bench`. Use when you want evolution scored against a
10
+ fixed corpus (independent of `npm test`) so champion fitness is comparable
11
+ across commits or across forks of the same harness.
12
+
13
+ ## When to use
14
+
15
+ - Setting up a new evolution pipeline for a repo whose `npm test` is
16
+ flaky, slow, or undersized — scaffold a deterministic bench suite once,
17
+ then evolve against it repeatedly.
18
+ - CI: `bench verify` the checked-in suite on every PR that touches it
19
+ (cheap; ~5s).
20
+ - Forking a harness to a new domain: copy and edit the suite to retarget
21
+ the evaluation without losing comparability to the parent.
22
+
23
+ ## Algorithm
24
+
25
+ Implementation: [`scripts/bench.mjs`](../../scripts/bench.mjs).
26
+
27
+ ### `--op create`
28
+ 1. Resolve `--repo` path; reject if missing.
29
+ 2. Shell to `metaharness-darwin bench create <repo> [--out <suite.json>]`.
30
+ 3. Default output path: `<repo>/.metaharness/bench/suite.json` (chosen by upstream).
31
+ 4. Suite shape (per upstream): array of `{ input, expectedOutput, weight }` tasks
32
+ derived from existing test cases.
33
+
34
+ ### `--op verify`
35
+ 1. Resolve `--suite` path; reject if missing.
36
+ 2. Shell to `metaharness-darwin bench verify <suite.json>`.
37
+ 3. Exit 1 if any task malformed (upstream's signal).
38
+
39
+ ## Output shape
40
+
41
+ ```json
42
+ {
43
+ "success": true,
44
+ "data": {
45
+ "op": "verify",
46
+ "taskCount": 42,
47
+ "wellFormed": true,
48
+ "durationMs": 870
49
+ }
50
+ }
51
+ ```
52
+
53
+ ## Exit codes
54
+
55
+ | Code | Meaning |
56
+ |---|---|
57
+ | 0 | OK (or degraded — Darwin absent) |
58
+ | 1 | `--op verify` and suite malformed |
59
+ | 2 | Config error or upstream invocation failure |
60
+
61
+ ## Graceful degradation
62
+
63
+ When `@metaharness/darwin` is absent, emits the standard `{degraded: true,
64
+ reason: 'metaharness-darwin-not-available'}` payload and exits 0.
@@ -0,0 +1,92 @@
1
+ ---
2
+ name: harness-evolve
3
+ description: Run `@metaharness/darwin evolve <repo>` to mutate a harness's seven policy surfaces (planner/contextBuilder/reviewer/retryPolicy/toolPolicy/memoryPolicy/scorePolicy), sandbox-score each variant, and promote only measured wins. The model is frozen; the harness evolves. Closes the loop ADR-150 opens (score+genome describe; evolve changes). Degrades gracefully when @metaharness/darwin is absent (ADR-150 + ADR-153 architectural constraints).
4
+ argument-hint: "--repo <path> [--generations 3] [--children 3] [--concurrency 2] [--sandbox real|mock|agent] [--selection pareto|quality-diversity|...] [--mutator deterministic|ruvllm] [--confirm]"
5
+ allowed-tools: Bash
6
+ ---
7
+
8
+ Surfaces the upstream `metaharness-darwin evolve` CLI as a ruflo skill. The
9
+ **write** layer that pairs with ADR-150's read layer (score / genome /
10
+ mcp-scan / threat-model / oia-audit). Use when you have a harness whose
11
+ readiness scores are flat and you want to discover *which* surface mutation
12
+ moves them — without retraining the foundation model.
13
+
14
+ ## When to use
15
+
16
+ - A `harness-score` result is below target and you don't know which policy
17
+ surface is responsible.
18
+ - You're seeding a harness for a new vertical and want to find a good
19
+ starting configuration empirically rather than hand-tuning.
20
+ - You're comparing your hand-tuned harness against an evolved baseline
21
+ (treat darwin's champion as the strawman).
22
+
23
+ ## When NOT to use
24
+
25
+ - For continuous background optimization. Darwin Mode is human-initiated.
26
+ Wire it into CI for one-shot exploration, not for autonomous self-modification.
27
+ - For ruflo itself in CI. ADR-153 §5 explicitly rejects auto-evolving ruflo
28
+ — the CI gate verifies graceful degradation, not convergence.
29
+
30
+ ## Algorithm
31
+
32
+ Implementation: [`scripts/evolve.mjs`](../../scripts/evolve.mjs).
33
+
34
+ 1. Validate args (`--repo` exists, caps on `--generations` ≤ 50, `--children`
35
+ ≤ 20, `--concurrency` ≤ 8, sandbox/selection/mutator are known values).
36
+ 2. Without `--confirm`: print plan + exit 0 (mirrors `harness-mint` safety
37
+ convention; defense in depth over the upstream `safety.ts` checks).
38
+ 3. With `--confirm`: shell to `npx -y @metaharness/darwin@~0.3.1 metaharness-darwin evolve <repo> ...`
39
+ via the shared `_darwin.mjs` async helper. Per-generation progress is
40
+ forwarded to stderr; final champion JSON is captured from stdout.
41
+ 4. Compute timeout from `generations × children × per-variant` (per-variant
42
+ ≈ 60s real, ≈ 2s mock). Caller may override with `--timeout-ms`.
43
+ 5. Honor upstream exit code 99 — propagate as "safety-disqualified", do not
44
+ remap. This is a designed-in tripwire (a variant tripped `inspectVariant`
45
+ for secrets / shell-out / network / dynamic-eval). See ADR-153 §"Safety model".
46
+ 6. Optional `--alert-on-no-improvement`: exit 1 when champion ≤ parent.
47
+
48
+ ## The seven mutation surfaces
49
+
50
+ | Surface | What it owns |
51
+ |---|---|
52
+ | `planner` | task decomposition / step ordering |
53
+ | `contextBuilder` | what gets fed into the prompt |
54
+ | `reviewer` | self-critique / output verification |
55
+ | `retryPolicy` | when + how to retry on failure |
56
+ | `toolPolicy` | which tools the agent may use, under which conditions |
57
+ | `memoryPolicy` | what to persist, recall, forget |
58
+ | `scorePolicy` | how the agent grades its own output |
59
+
60
+ One mutation per variant. Multi-surface mutations are not allowed (causal
61
+ attribution stays clean).
62
+
63
+ ## Output
64
+
65
+ Reports land under `<repo>/.metaharness/`:
66
+
67
+ ```
68
+ .metaharness/
69
+ archive.json # full lineage tree (sampling next gen draws from this)
70
+ lineage.json # parent→child edges only
71
+ variants/<id>/ # per-variant code (kept for audit)
72
+ runs/<id>/ # per-variant sandbox test output
73
+ reports/winner.json # final champion + score delta vs parent
74
+ ```
75
+
76
+ Skill stdout = JSON `{success, data: {champion, plan, durationMs, improved}}`.
77
+
78
+ ## Exit codes
79
+
80
+ | Code | Meaning |
81
+ |---|---|
82
+ | 0 | Evolved OK, or dry-run, or degraded (Darwin absent) |
83
+ | 1 | `--alert-on-no-improvement` and champion did not beat parent |
84
+ | 2 | Config error or evolution infrastructure failure |
85
+ | 99 | Upstream "safety-disqualified" (PROPAGATED, not remapped) |
86
+
87
+ ## Graceful degradation (ADR-150 constraint 3 + ADR-153)
88
+
89
+ When `@metaharness/darwin` is not installed, the script emits
90
+ `{degraded: true, reason: 'metaharness-darwin-not-available', hint: ...}`
91
+ and exits 0. ruflo continues to function. CI's
92
+ `no-metaharness-smoke.yml`-style job asserts this path.
@@ -0,0 +1,101 @@
1
+ ---
2
+ name: harness-security-bench
3
+ description: Run `@metaharness/darwin security bench` (upstream "Darwin Shield" / ADR-155) — evolves a champion security-detection harness against a 10-vuln / 9-decoy corpus and grades it on TPR/FPR/patch-pass/repro/unsafe vs four baselines (B0 static, B1 LLM-single-pass, B2 fixed-agent, B3 Darwin-champion). Closest reference implementation for ruflo's own ADR-155 nightly self-learning security harness (PR #2417). Degrades gracefully when @metaharness/darwin is absent.
4
+ argument-hint: "[--population 2] [--cycles 1] [--seed N] [--alert-on-fail]"
5
+ allowed-tools: Bash
6
+ ---
7
+
8
+ Surfaces the upstream `metaharness-darwin security bench` command. **This is
9
+ the upstream's own ADR-155 — Darwin Shield — and is the closest reference
10
+ implementation for ruflo's nightly self-learning security harness ([#2417](https://github.com/ruvnet/ruflo/pull/2417)).**
11
+
12
+ ## Why this matters for ruflo's ADR-155
13
+
14
+ ruflo's ADR-155 proposes three learning loops (per-dimension confidence,
15
+ severity calibration, auto-fix bid). Loop A trains on accumulated
16
+ `(finding, dimension, human_outcome)` tuples — but the gradient signal is
17
+ only sound if the underlying detection mechanism converges on a known-good
18
+ corpus. Darwin Shield evolves exactly that mechanism on a 10-vuln/9-decoy
19
+ ground-truth set. Running this nightly gives us:
20
+
21
+ - **Empirical floor:** if Darwin Shield's champion can't reach
22
+ TPR=1/FPR=0 on the bench corpus, our Loop A's reward signal is noise.
23
+ - **Drift detection:** week-over-week champion fitness deltas surface
24
+ when the security landscape (or our mutator policy) shifts.
25
+ - **Baseline diversity:** the 4 baselines (B0–B3) give us 4 anchor
26
+ points to weight per-dimension confidence against.
27
+
28
+ ## Algorithm
29
+
30
+ Implementation: [`scripts/security-bench.mjs`](../../scripts/security-bench.mjs).
31
+
32
+ 1. Shell to `npx -y @metaharness/darwin@~0.3.1 metaharness-darwin security bench --population N --cycles N [--seed S]`.
33
+ 2. Default timeout = `3s × 19 evaluations × population × cycles + 30s overhead`.
34
+ At default `--population 2 --cycles 1` ≈ 144s; at `--population 4 --cycles 3` ≈ 12 min.
35
+ 3. Parse the markdown report — overall PASS/FAIL plus per-gate
36
+ pass/fail rows (gate examples: "TPR improvement ≥ 25% vs fixed",
37
+ "FPR reduction ≥ 40%", "Patch-test pass rate ≥ 80%", "Reproduction
38
+ success ≥ 90%", "Unsafe outputs = 0", "Cost increase ≤ 2× fixed",
39
+ "Beyond SOTA: champion statistically beats previous champion",
40
+ "Compounding: false-positive repeat-rate drop ≥ 35%").
41
+ 4. Parse the baselines-vs-champion table (4 rows: fitness/TPR/FPR/patchPass/
42
+ repro/unsafe/cost per harness).
43
+ 5. Emit structured JSON. With `--alert-on-fail`, exit 1 when overall = FAIL.
44
+
45
+ ## Output shape
46
+
47
+ ```json
48
+ {
49
+ "success": true,
50
+ "data": {
51
+ "overall": { "ok": true, "icon": "✅" },
52
+ "gates": {
53
+ "total": 11,
54
+ "passed": 11,
55
+ "failed": 0,
56
+ "details": [{ "ok": true, "criterion": "TPR improvement ≥ 25% vs fixed harness", "measured": "+150% (B2 0.4 → B3 1)" }, ...]
57
+ },
58
+ "baselines": [
59
+ { "harness": "static-only", "fitness": 0.5665, "tpr": 0.3, "fpr": 1, "unsafe": 0, ... },
60
+ { "harness": "LLM single-pass", "fitness": 0.1365, ... },
61
+ { "harness": "fixed agent", "fitness": 0.598, ... },
62
+ { "harness": "Darwin champion", "fitness": 0.93275, "tpr": 1, "fpr": 0, ... }
63
+ ],
64
+ "rawMarkdown": "...",
65
+ "shape": { "population": 2, "cycles": 1, "seed": null },
66
+ "durationMs": 142000
67
+ }
68
+ }
69
+ ```
70
+
71
+ ## Wiring into ADR-155 nightly harness
72
+
73
+ The ADR-155 nightly workflow (per #2418 task `W1.5`) will spawn this as
74
+ one of the active-pentest dimension's calls — its results become a
75
+ trajectory record:
76
+
77
+ ```jsonc
78
+ {
79
+ "dimension": "mcp-pentest",
80
+ "subdimension": "darwin-shield-bench",
81
+ "champion_fitness": 0.93275,
82
+ "champion_tpr": 1, "champion_fpr": 0,
83
+ "gates_passed": 11, "gates_failed": 0,
84
+ "shape": { "population": 4, "cycles": 3 }
85
+ }
86
+ ```
87
+
88
+ Loop A learns: if `darwin-shield-bench` consistently passes on the seeded
89
+ corpus, weight findings caught only by `mcp-pentest` higher.
90
+
91
+ ## Exit codes
92
+
93
+ | Code | Meaning |
94
+ |---|---|
95
+ | 0 | Bench ran (overall PASS or FAIL — distinguish via JSON `overall.ok`), or degraded |
96
+ | 1 | `--alert-on-fail` and `overall.ok === false` |
97
+ | 2 | Config error or upstream infrastructure failure |
98
+
99
+ ## Graceful degradation
100
+
101
+ When `@metaharness/darwin` is absent, emits `{degraded: true, reason: 'metaharness-darwin-not-available'}` and exits 0.