@claude-flow/cli 3.10.13 → 3.10.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-flow/cli",
3
- "version": "3.10.13",
3
+ "version": "3.10.15",
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",
@@ -0,0 +1,241 @@
1
+ #!/usr/bin/env node
2
+ // benchmark-self-learning.mjs — proof harness for #2245.
3
+ //
4
+ // Replaces the "self-learning reports success but persists nothing" theater
5
+ // with measured deltas: actually runs each entry point N times and prints what
6
+ // each one moved (or didn't). Writes a run JSON to docs/benchmarks/runs/ so
7
+ // future regressions are visible against a committed baseline.
8
+ //
9
+ // Usage:
10
+ // node scripts/benchmark-self-learning.mjs # default N=20 per surface
11
+ // N=50 BENCH_JSON=1 node scripts/benchmark-self-learning.mjs
12
+ // BENCH_NO_WRITE=1 node scripts/benchmark-self-learning.mjs
13
+ //
14
+ // Repro:
15
+ // 1. Clone ruvnet/ruflo, npm install, build the CLI:
16
+ // npm install && (cd v3/@claude-flow/cli && npx tsc -b)
17
+ // 2. Run this script. It prints a "before/after" table per surface.
18
+ // 3. Inspect docs/benchmarks/runs/self-learning-<ts>.json for the persisted
19
+ // proof — diff against previous runs to catch any future regression.
20
+
21
+ import { mkdirSync, writeFileSync, mkdtempSync, rmSync } from 'node:fs';
22
+ import { fileURLToPath } from 'node:url';
23
+ import { dirname, join, resolve } from 'node:path';
24
+ import { tmpdir } from 'node:os';
25
+ import { performance } from 'node:perf_hooks';
26
+
27
+ const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
28
+ const CLI_ROOT = resolve(SCRIPT_DIR, '..');
29
+ const REPO_ROOT = resolve(SCRIPT_DIR, '../../../..');
30
+ const RUNS_DIR = join(REPO_ROOT, 'docs', 'benchmarks', 'runs');
31
+
32
+ const N = Number(process.env.N) || 20;
33
+
34
+ // Run in an isolated temp dir so we don't pollute the repo's neural store.
35
+ const SCRATCH = mkdtempSync(join(tmpdir(), 'learn-bench-'));
36
+ process.chdir(SCRATCH);
37
+
38
+ async function main() {
39
+ const intel = await import(join(CLI_ROOT, 'dist/src/memory/intelligence.js'));
40
+ const hooks = await import(join(CLI_ROOT, 'dist/src/mcp-tools/hooks-tools.js'));
41
+ const neural = await import(join(CLI_ROOT, 'dist/src/mcp-tools/neural-tools.js'));
42
+
43
+ intel.clearIntelligence();
44
+
45
+ // -------------------------------------------------------------------------
46
+ // §A — recordSignalProcessed
47
+ // -------------------------------------------------------------------------
48
+ const A_before = intel.getIntelligenceStats();
49
+ const tA = performance.now();
50
+ for (let i = 0; i < N; i++) intel.recordSignalProcessed();
51
+ intel.flushIntelligenceStats();
52
+ const dtA = performance.now() - tA;
53
+ const A_after = intel.getIntelligenceStats();
54
+ const A_delta = A_after.signalsProcessed - A_before.signalsProcessed;
55
+
56
+ // -------------------------------------------------------------------------
57
+ // §B — hooks_task-completed (trainPatterns:true) → trajectory pipeline
58
+ // -------------------------------------------------------------------------
59
+ const taskCompleted = hooks.hooksTools.find((t) => t.name === 'hooks_task-completed');
60
+ const B_before = intel.getIntelligenceStats();
61
+ const tB = performance.now();
62
+ let B_trained = 0;
63
+ for (let i = 0; i < N; i++) {
64
+ const r = await taskCompleted.handler({
65
+ taskId: `bench-${i}`,
66
+ success: i % 5 !== 4, // 80% success, 20% failure — mixed verdict
67
+ quality: 0.5 + (i % 5) * 0.1,
68
+ trainPatterns: true,
69
+ content: `Benchmark task ${i}: refactor or test or fix`,
70
+ });
71
+ if (r.learningPath === 'trajectory-pipeline') B_trained++;
72
+ }
73
+ const dtB = performance.now() - tB;
74
+ const B_after = intel.getIntelligenceStats();
75
+
76
+ // -------------------------------------------------------------------------
77
+ // §C — hooks_task-completed recorded-only (negative control)
78
+ // -------------------------------------------------------------------------
79
+ const C_before = intel.getIntelligenceStats();
80
+ const tC = performance.now();
81
+ for (let i = 0; i < N; i++) {
82
+ await taskCompleted.handler({ taskId: `bench-no-train-${i}`, success: true, quality: 0.8 });
83
+ }
84
+ const dtC = performance.now() - tC;
85
+ const C_after = intel.getIntelligenceStats();
86
+
87
+ // -------------------------------------------------------------------------
88
+ // §D — storeNeuralPatterns + neural_patterns list reflects them
89
+ // -------------------------------------------------------------------------
90
+ const items = Array.from({ length: N }, (_, i) => ({
91
+ name: `pattern-${i}`,
92
+ type: 'bench-pattern',
93
+ content: `import { thing${i} } from 'module${i}'`,
94
+ }));
95
+ const tD = performance.now();
96
+ const D_store = await neural.storeNeuralPatterns(items);
97
+ const dtD = performance.now() - tD;
98
+ const listTool = neural.neuralTools.find((t) => t.name === 'neural_patterns');
99
+ const D_list = await listTool.handler({ action: 'list' });
100
+
101
+ // -------------------------------------------------------------------------
102
+ // §E — multi-step trajectory pipeline (end-to-end)
103
+ // -------------------------------------------------------------------------
104
+ const trajStart = hooks.hooksTools.find((t) => t.name === 'hooks_intelligence_trajectory-start');
105
+ const trajStep = hooks.hooksTools.find((t) => t.name === 'hooks_intelligence_trajectory-step');
106
+ const trajEnd = hooks.hooksTools.find((t) => t.name === 'hooks_intelligence_trajectory-end');
107
+ // NOTE: the MCP trajectory tools feed sonaCoordinator (queryable via
108
+ // hooks_intelligence_stats), NOT globalStats. That's part of the broader
109
+ // store-fragmentation problem #2245 identifies; we check observable
110
+ // outcomes (persisted + sonaUpdate) instead of a globalStats delta.
111
+ const tE = performance.now();
112
+ let persistedCount = 0;
113
+ let sonaUpdateCount = 0;
114
+ if (trajStart && trajStep && trajEnd) {
115
+ for (let i = 0; i < Math.min(5, N); i++) {
116
+ const s = await trajStart.handler({ task: `multi-step bench ${i}`, agent: 'bench' });
117
+ const id = s.trajectoryId;
118
+ await trajStep.handler({ trajectoryId: id, type: 'observation', content: `obs ${i}` });
119
+ await trajStep.handler({ trajectoryId: id, type: 'action', content: `act ${i}` });
120
+ await trajStep.handler({ trajectoryId: id, type: 'result', content: `done ${i}` });
121
+ const endRes = await trajEnd.handler({ trajectoryId: id, success: true });
122
+ if (endRes.persisted) persistedCount++;
123
+ if (endRes.learning?.sonaUpdate) sonaUpdateCount++;
124
+ }
125
+ }
126
+ const dtE = performance.now() - tE;
127
+
128
+ // -------------------------------------------------------------------------
129
+ // §F — getUnifiedLearningStats (ADR-075)
130
+ // -------------------------------------------------------------------------
131
+ const tF = performance.now();
132
+ const unified = await intel.getUnifiedLearningStats();
133
+ const dtF = performance.now() - tF;
134
+ const unifiedOk = unified.global && unified.sona && unified.memoryBridge && unified.neuralPatterns && unified.consistency;
135
+
136
+ // -------------------------------------------------------------------------
137
+ // Summary
138
+ // -------------------------------------------------------------------------
139
+ const summary = {
140
+ runAt: new Date().toISOString(),
141
+ benchmark: 'self-learning-2245',
142
+ n: N,
143
+ sections: {
144
+ A_recordSignalProcessed: {
145
+ calls: N,
146
+ signalsProcessedDelta: A_delta,
147
+ passed: A_delta === N,
148
+ elapsedMs: Number(dtA.toFixed(2)),
149
+ },
150
+ B_taskCompleted_trainPatterns: {
151
+ calls: N,
152
+ trainedViaPipeline: B_trained,
153
+ trajectoriesDelta: B_after.trajectoriesRecorded - B_before.trajectoriesRecorded,
154
+ patternsLearnedDelta: B_after.patternsLearned - B_before.patternsLearned,
155
+ passed: B_trained === N,
156
+ elapsedMs: Number(dtB.toFixed(2)),
157
+ avgLatencyMsPerCall: Number((dtB / N).toFixed(2)),
158
+ },
159
+ C_taskCompleted_recordedOnly: {
160
+ calls: N,
161
+ trajectoriesDelta: C_after.trajectoriesRecorded - C_before.trajectoriesRecorded,
162
+ passed: (C_after.trajectoriesRecorded - C_before.trajectoriesRecorded) === 0,
163
+ note: 'negative control — should NOT touch trajectories',
164
+ elapsedMs: Number(dtC.toFixed(2)),
165
+ },
166
+ D_pretrain_neuralPatterns: {
167
+ attempted: items.length,
168
+ stored: D_store.stored,
169
+ listTotal: D_list.total,
170
+ passed: D_store.stored === items.length && D_list.total >= items.length,
171
+ elapsedMs: Number(dtD.toFixed(2)),
172
+ },
173
+ E_multiStepTrajectory: {
174
+ cycles: Math.min(5, N),
175
+ persistedCount,
176
+ sonaUpdateCount,
177
+ passed: persistedCount === Math.min(5, N), // persistence is the must-pass; SONA depends on env
178
+ note: 'MCP trajectory tools feed sonaCoordinator (see hooks_intelligence_stats), not globalStats — observable outcomes checked here',
179
+ elapsedMs: Number(dtE.toFixed(2)),
180
+ },
181
+ F_unifiedStats: {
182
+ shape: ['global', 'sona', 'memoryBridge', 'neuralPatterns', 'consistency'],
183
+ observed: {
184
+ 'global.patternsLearned': unified.global.patternsLearned,
185
+ 'global.trajectoriesRecorded': unified.global.trajectoriesRecorded,
186
+ 'global.signalsProcessed': unified.global.signalsProcessed,
187
+ 'memoryBridge.totalEntries': unified.memoryBridge.totalEntries,
188
+ 'memoryBridge.reachable': unified.memoryBridge.reachable,
189
+ 'neuralPatterns.patternCount': unified.neuralPatterns.patternCount,
190
+ 'sona.available': unified.sona.available,
191
+ 'consistency.notes': unified.consistency.notes.length,
192
+ },
193
+ passed: !!unifiedOk,
194
+ note: 'ADR-075 — one aggregator across the 4 stores. Each sub-view names its source.',
195
+ elapsedMs: Number(dtF.toFixed(2)),
196
+ },
197
+ },
198
+ finalState: {
199
+ signalsProcessed: A_after.signalsProcessed,
200
+ trajectoriesRecorded: intel.getIntelligenceStats().trajectoriesRecorded,
201
+ patternsLearned: intel.getIntelligenceStats().patternsLearned,
202
+ },
203
+ scratch: SCRATCH,
204
+ };
205
+
206
+ const allPassed = Object.values(summary.sections).every((s) => s.passed);
207
+ summary.passed = allPassed;
208
+
209
+ if (process.env.BENCH_JSON) {
210
+ console.log(JSON.stringify(summary, null, 2));
211
+ } else {
212
+ console.log(`# Self-learning benchmark (#2245) — N=${N}`);
213
+ console.log('');
214
+ console.log('| Section | Calls | Delta | Passed | Latency (ms) |');
215
+ console.log('|---|---:|---:|:---:|---:|');
216
+ console.log(`| A recordSignalProcessed | ${N} | +${summary.sections.A_recordSignalProcessed.signalsProcessedDelta} | ${summary.sections.A_recordSignalProcessed.passed ? '✅' : '❌'} | ${summary.sections.A_recordSignalProcessed.elapsedMs} |`);
217
+ console.log(`| B task-completed (train) | ${N} | trained=${summary.sections.B_taskCompleted_trainPatterns.trainedViaPipeline}, trajectories+${summary.sections.B_taskCompleted_trainPatterns.trajectoriesDelta} | ${summary.sections.B_taskCompleted_trainPatterns.passed ? '✅' : '❌'} | ${summary.sections.B_taskCompleted_trainPatterns.elapsedMs} (${summary.sections.B_taskCompleted_trainPatterns.avgLatencyMsPerCall}/call) |`);
218
+ console.log(`| C task-completed (record-only) | ${N} | trajectories+${summary.sections.C_taskCompleted_recordedOnly.trajectoriesDelta} (expected 0) | ${summary.sections.C_taskCompleted_recordedOnly.passed ? '✅' : '❌'} | ${summary.sections.C_taskCompleted_recordedOnly.elapsedMs} |`);
219
+ console.log(`| D pretrain → neural_patterns | ${items.length} | stored=${summary.sections.D_pretrain_neuralPatterns.stored}, listed=${summary.sections.D_pretrain_neuralPatterns.listTotal} | ${summary.sections.D_pretrain_neuralPatterns.passed ? '✅' : '❌'} | ${summary.sections.D_pretrain_neuralPatterns.elapsedMs} |`);
220
+ console.log(`| E multi-step trajectory | ${summary.sections.E_multiStepTrajectory.cycles} | persisted=${summary.sections.E_multiStepTrajectory.persistedCount}, sonaUpdate=${summary.sections.E_multiStepTrajectory.sonaUpdateCount} | ${summary.sections.E_multiStepTrajectory.passed ? '✅' : '❌'} | ${summary.sections.E_multiStepTrajectory.elapsedMs} |`);
221
+ const f = summary.sections.F_unifiedStats;
222
+ console.log(`| F unified-stats | 4 stores | bridge.reachable=${f.observed['memoryBridge.reachable']}, sona.available=${f.observed['sona.available']}, neural.count=${f.observed['neuralPatterns.patternCount']}, notes=${f.observed['consistency.notes']} | ${f.passed ? '✅' : '❌'} | ${f.elapsedMs} |`);
223
+ console.log('');
224
+ console.log(`Final state: signalsProcessed=${summary.finalState.signalsProcessed}, trajectoriesRecorded=${summary.finalState.trajectoriesRecorded}, patternsLearned=${summary.finalState.patternsLearned}`);
225
+ console.log(`Overall: ${allPassed ? '✅ ALL PASSED' : '❌ FAILED'}`);
226
+ }
227
+
228
+ if (!process.env.BENCH_NO_WRITE) {
229
+ mkdirSync(RUNS_DIR, { recursive: true });
230
+ const stamp = summary.runAt.replace(/[:.]/g, '-');
231
+ writeFileSync(join(RUNS_DIR, `self-learning-${stamp}.json`), JSON.stringify(summary, null, 2));
232
+ writeFileSync(join(RUNS_DIR, 'self-learning-latest.json'), JSON.stringify(summary, null, 2));
233
+ if (!process.env.BENCH_JSON) console.log(`\nWrote ${join(RUNS_DIR, `self-learning-${stamp}.json`)}`);
234
+ }
235
+
236
+ try { rmSync(SCRATCH, { recursive: true, force: true }); } catch {}
237
+
238
+ if (!allPassed) process.exit(1);
239
+ }
240
+
241
+ main().catch((err) => { console.error(err); process.exit(1); });