@claude-flow/cli 3.10.21 → 3.10.22

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.21",
3
+ "version": "3.10.22",
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,205 @@
1
+ #!/usr/bin/env node
2
+ // grid-search-retrieval.mjs — sweep retrieval hyperparameters against the
3
+ // ADR-081 labelled corpus. Reports label nDCG@3, top-1, top-3, precision@3,
4
+ // MRR@3 per configuration. Identifies the best non-rerank and best rerank
5
+ // configs by nDCG@3.
6
+ //
7
+ // Run prerequisites: scripts/pretrain-from-github.mjs first.
8
+ //
9
+ // Usage:
10
+ // node scripts/grid-search-retrieval.mjs # default grid
11
+ // node scripts/grid-search-retrieval.mjs --quick # smaller grid
12
+ // BENCH_JSON=1 node scripts/grid-search-retrieval.mjs # machine-readable
13
+ //
14
+ // Output also goes to docs/benchmarks/runs/grid-search-retrieval-<ts>.json
15
+
16
+ import { writeFileSync, mkdirSync } from 'node:fs';
17
+ import { fileURLToPath } from 'node:url';
18
+ import { dirname, join, resolve } from 'node:path';
19
+ import { performance } from 'node:perf_hooks';
20
+
21
+ const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
22
+ const CLI_ROOT = resolve(SCRIPT_DIR, '..');
23
+ const REPO_ROOT = resolve(SCRIPT_DIR, '../../../..');
24
+ const RUNS_DIR = join(REPO_ROOT, 'docs', 'benchmarks', 'runs');
25
+
26
+ // Same labelled corpus as benchmark-pretrained-retrieval.mjs (ADR-081).
27
+ const QUERIES = [
28
+ { q: 'how was the Opus model alias fixed',
29
+ labels: ['opus 4.8', 'opus alias', 'opus model alias', '#2232'] },
30
+ { q: 'self-learning wiring task-completed pretrain',
31
+ labels: ['self-learning', 'adr-074', 'self learning', '#2245', 'task-completed'] },
32
+ { q: 'deterministic codemod engine var-to-const',
33
+ labels: ['deterministic tier-1 codemod', 'adr-143', 'codemod', 'var-to-const'] },
34
+ { q: 'MCP server orphan leak parent-death',
35
+ labels: ['mcp orphan', 'mcp servers orphan', 'parent-death', '#2234', 'orphan on every claude'] },
36
+ { q: 'unified learning stats aggregator',
37
+ labels: ['unified learning-stats', 'adr-075', 'unified learning stats'] },
38
+ { q: 'structured distillation 4-field schema',
39
+ labels: ['structured distillation', 'adr-076', '4-field schema'] },
40
+ { q: 'SQL injection migrate.ts table identifier',
41
+ labels: ['sql injection', 'shell injection', 'migrate.ts', 'agentdb', 'cve'] },
42
+ { q: 'recall@k HNSW benchmark harness',
43
+ labels: ['hnsw', 'memory-recall', 'benchmark suite', 'recall@k', 'benchmark intelligence'] },
44
+ { q: 'Q-learning encoder keyword block',
45
+ labels: ['q-state encoder', 'route q-state', 'keyword block', '#2239', 'q-encoder'] },
46
+ { q: 'security hardening crypto random IDs',
47
+ labels: ['cwe-347', 'crypto.randomuuid', 'security fix', 'random id', 'crypto random'] },
48
+ ];
49
+
50
+ function isRelevant(name, labels) {
51
+ if (!name || !labels?.length) return false;
52
+ const lower = String(name).toLowerCase();
53
+ return labels.some((s) => lower.includes(s.toLowerCase()));
54
+ }
55
+
56
+ function ndcgAtK(rankedRel, k) {
57
+ const arr = rankedRel.slice(0, k);
58
+ const dcg = arr.reduce((acc, rel, i) => acc + (rel ? 1 / Math.log2(i + 2) : 0), 0);
59
+ const numRelevant = arr.filter(Boolean).length;
60
+ if (numRelevant === 0) return 0;
61
+ let idcg = 0;
62
+ for (let i = 0; i < numRelevant; i++) idcg += 1 / Math.log2(i + 2);
63
+ return idcg > 0 ? dcg / idcg : 0;
64
+ }
65
+
66
+ // ---------------------------------------------------------------------------
67
+ // Grid definitions
68
+ // ---------------------------------------------------------------------------
69
+
70
+ const QUICK = process.argv.includes('--quick');
71
+
72
+ // Non-rerank grid: alpha × subjectWeight × mmrLambda
73
+ const ALPHA_GRID = QUICK ? [0.5, 0.7] : [0.3, 0.5, 0.7];
74
+ const SUBJ_GRID = QUICK ? [3.0, 5.0] : [2.0, 3.0, 5.0];
75
+ const MMR_GRID = QUICK ? [0.5] : [0.3, 0.5, 0.7];
76
+
77
+ // Rerank grid: hybridWeight × ceWeight (mutually constrained — they sum to 1)
78
+ const RERANK_GRID = QUICK
79
+ ? [[0.5, 0.5]]
80
+ : [[0.3, 0.7], [0.4, 0.6], [0.5, 0.5], [0.6, 0.4], [0.7, 0.3]];
81
+
82
+ // ---------------------------------------------------------------------------
83
+ // Eval
84
+ // ---------------------------------------------------------------------------
85
+
86
+ async function evalConfig(tool, params) {
87
+ let top1Hits = 0, top3HitsBin = 0, ranks = [], ndcg3Sum = 0, prec3Sum = 0;
88
+ const tStart = performance.now();
89
+ for (const { q, labels } of QUERIES) {
90
+ const r = await tool.handler({ action: 'search', query: q, mode: 'hybrid', limit: 5, ...params });
91
+ const matches = (r.results || []).slice(0, 5);
92
+ const rel = matches.map((m) => isRelevant(m?.name, labels));
93
+ if (rel[0]) top1Hits++;
94
+ if (rel.slice(0, 3).some(Boolean)) top3HitsBin++;
95
+ const firstRank = rel.findIndex(Boolean);
96
+ if (firstRank >= 0) ranks.push(firstRank + 1);
97
+ ndcg3Sum += ndcgAtK(rel, 3);
98
+ prec3Sum += rel.slice(0, 3).filter(Boolean).length / 3;
99
+ }
100
+ const elapsed = performance.now() - tStart;
101
+ return {
102
+ label_top1HitRate: top1Hits / QUERIES.length,
103
+ label_top3HitRate: top3HitsBin / QUERIES.length,
104
+ label_mrr3: ranks.reduce((s, r) => s + 1 / r, 0) / QUERIES.length,
105
+ label_precision3: prec3Sum / QUERIES.length,
106
+ label_ndcg3: ndcg3Sum / QUERIES.length,
107
+ avgLatencyMs: elapsed / QUERIES.length,
108
+ };
109
+ }
110
+
111
+ async function main() {
112
+ const neural = await import(join(CLI_ROOT, 'dist/src/mcp-tools/neural-tools.js'));
113
+ const tool = neural.neuralTools.find((t) => t.name === 'neural_patterns');
114
+ if (!tool) throw new Error('neural_patterns tool not found');
115
+
116
+ const configs = [];
117
+
118
+ // §A — non-rerank grid (fast, ~3*3*3 = 27 configs at default)
119
+ for (const alpha of ALPHA_GRID) {
120
+ for (const subjectWeight of SUBJ_GRID) {
121
+ for (const mmrLambda of MMR_GRID) {
122
+ configs.push({
123
+ name: `hybrid α=${alpha} sw=${subjectWeight} mmr=${mmrLambda}`,
124
+ rerank: false,
125
+ params: { rerank: false, alpha, subjectWeight, mmrLambda, bodyWeight: 1.0, typePenaltyFactor: 1.0 },
126
+ });
127
+ }
128
+ }
129
+ }
130
+
131
+ // §B — rerank grid (slow, ~5 configs — each query takes ~1s with CE)
132
+ for (const [hybridWeight, ceWeight] of RERANK_GRID) {
133
+ configs.push({
134
+ name: `rerank hw=${hybridWeight} cw=${ceWeight}`,
135
+ rerank: true,
136
+ params: { rerank: true, alpha: 0.6, subjectWeight: 3.0, hybridWeight, ceWeight },
137
+ });
138
+ }
139
+
140
+ console.log(`Grid-search: ${configs.length} configs across ${QUERIES.length} queries\n`);
141
+ const results = [];
142
+ for (const cfg of configs) {
143
+ process.stdout.write(` ${cfg.name.padEnd(40)} … `);
144
+ const m = await evalConfig(tool, cfg.params);
145
+ results.push({ ...cfg, metrics: m });
146
+ console.log(`top1=${(m.label_top1HitRate * 100).toFixed(0)}% top3=${(m.label_top3HitRate * 100).toFixed(0)}% nDCG3=${m.label_ndcg3.toFixed(3)} P3=${m.label_precision3.toFixed(3)} MRR3=${m.label_mrr3.toFixed(3)} ${m.avgLatencyMs.toFixed(0)}ms`);
147
+ }
148
+
149
+ // Rank by label nDCG@3 (the canonical relevance metric).
150
+ const byNdcg = [...results].sort((a, b) => b.metrics.label_ndcg3 - a.metrics.label_ndcg3);
151
+ const byTop1 = [...results].sort((a, b) => b.metrics.label_top1HitRate - a.metrics.label_top1HitRate || b.metrics.label_ndcg3 - a.metrics.label_ndcg3);
152
+ const byPrec3 = [...results].sort((a, b) => b.metrics.label_precision3 - a.metrics.label_precision3 || b.metrics.label_ndcg3 - a.metrics.label_ndcg3);
153
+
154
+ const bestNonRerankByNdcg = byNdcg.find((r) => !r.rerank);
155
+ const bestRerankByNdcg = byNdcg.find((r) => r.rerank);
156
+
157
+ console.log('\n=== Top 5 by label nDCG@3 ===');
158
+ for (const r of byNdcg.slice(0, 5)) {
159
+ console.log(` nDCG=${r.metrics.label_ndcg3.toFixed(3)} top1=${(r.metrics.label_top1HitRate * 100).toFixed(0)}% P3=${r.metrics.label_precision3.toFixed(3)} ${r.name}`);
160
+ }
161
+
162
+ console.log('\n=== Top 5 by label top-1 (ties broken by nDCG) ===');
163
+ for (const r of byTop1.slice(0, 5)) {
164
+ console.log(` top1=${(r.metrics.label_top1HitRate * 100).toFixed(0)}% nDCG=${r.metrics.label_ndcg3.toFixed(3)} P3=${r.metrics.label_precision3.toFixed(3)} ${r.name}`);
165
+ }
166
+
167
+ console.log('\n=== Top 5 by label precision@3 ===');
168
+ for (const r of byPrec3.slice(0, 5)) {
169
+ console.log(` P3=${r.metrics.label_precision3.toFixed(3)} top1=${(r.metrics.label_top1HitRate * 100).toFixed(0)}% nDCG=${r.metrics.label_ndcg3.toFixed(3)} ${r.name}`);
170
+ }
171
+
172
+ console.log(`\n=== WINNERS ===`);
173
+ console.log(`Best non-rerank (by nDCG@3): ${bestNonRerankByNdcg.name} → top1=${(bestNonRerankByNdcg.metrics.label_top1HitRate * 100).toFixed(0)}% nDCG=${bestNonRerankByNdcg.metrics.label_ndcg3.toFixed(3)}`);
174
+ console.log(`Best rerank (by nDCG@3): ${bestRerankByNdcg.name} → top1=${(bestRerankByNdcg.metrics.label_top1HitRate * 100).toFixed(0)}% nDCG=${bestRerankByNdcg.metrics.label_ndcg3.toFixed(3)}`);
175
+
176
+ const summary = {
177
+ runAt: new Date().toISOString(),
178
+ benchmark: 'grid-search-retrieval',
179
+ queries: QUERIES.length,
180
+ configsEvaluated: configs.length,
181
+ grid: { ALPHA_GRID, SUBJ_GRID, MMR_GRID, RERANK_GRID },
182
+ results: results.map((r) => ({ name: r.name, rerank: r.rerank, params: r.params, metrics: r.metrics })),
183
+ winners: {
184
+ nDcg3: byNdcg[0].name,
185
+ top1: byTop1[0].name,
186
+ precision3: byPrec3[0].name,
187
+ bestNonRerank: bestNonRerankByNdcg.name,
188
+ bestRerank: bestRerankByNdcg.name,
189
+ },
190
+ };
191
+
192
+ if (process.env.BENCH_JSON) {
193
+ console.log(JSON.stringify(summary, null, 2));
194
+ } else {
195
+ mkdirSync(RUNS_DIR, { recursive: true });
196
+ const stamp = summary.runAt.replace(/[:.]/g, '-');
197
+ writeFileSync(join(RUNS_DIR, `grid-search-retrieval-${stamp}.json`), JSON.stringify(summary, null, 2));
198
+ writeFileSync(join(RUNS_DIR, 'grid-search-retrieval-latest.json'), JSON.stringify(summary, null, 2));
199
+ console.log(`\nWrote ${join(RUNS_DIR, `grid-search-retrieval-${stamp}.json`)}`);
200
+ }
201
+
202
+ process.exit(0);
203
+ }
204
+
205
+ main().catch((err) => { console.error(err); process.exit(1); });