@claude-flow/cli 3.10.19 → 3.10.21
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/mcp-tools/neural-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/neural-tools.js +51 -4
- package/dist/src/mcp-tools/neural-tools.js.map +1 -1
- package/dist/src/memory/cross-encoder-rerank.d.ts +33 -0
- package/dist/src/memory/cross-encoder-rerank.d.ts.map +1 -0
- package/dist/src/memory/cross-encoder-rerank.js +123 -0
- package/dist/src/memory/cross-encoder-rerank.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/scripts/benchmark-pretrained-retrieval.mjs +151 -39
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-flow/cli",
|
|
3
|
-
"version": "3.10.
|
|
3
|
+
"version": "3.10.21",
|
|
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",
|
|
@@ -20,23 +20,86 @@ const CLI_ROOT = resolve(SCRIPT_DIR, '..');
|
|
|
20
20
|
const REPO_ROOT = resolve(SCRIPT_DIR, '../../../..');
|
|
21
21
|
const RUNS_DIR = join(REPO_ROOT, 'docs', 'benchmarks', 'runs');
|
|
22
22
|
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
23
|
+
// ADR-081 labelled held-out corpus. Each query has hand-curated
|
|
24
|
+
// `expectedSubstrings` — case-insensitive substring matches against the
|
|
25
|
+
// pattern's name. A result is "relevant" if its name contains ANY of the
|
|
26
|
+
// substrings. Tighter than the regex proxy used in ADRs 077-080 (which
|
|
27
|
+
// matched related-but-not-exact commits).
|
|
28
|
+
//
|
|
29
|
+
// `expect` (regex) is kept for backwards compatibility with the older
|
|
30
|
+
// numbers in ADRs 077-080. New metrics use expectedSubstrings.
|
|
27
31
|
const QUERIES = [
|
|
28
|
-
{
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
{
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
{
|
|
33
|
+
q: 'how was the Opus model alias fixed',
|
|
34
|
+
expect: /opus|2232|model.*alias|4\.8/i,
|
|
35
|
+
expectedSubstrings: ['opus 4.8', 'opus alias', 'opus model alias', '#2232'],
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
q: 'self-learning wiring task-completed pretrain',
|
|
39
|
+
expect: /self.?learning|task.?completed|pretrain|2245|074/i,
|
|
40
|
+
expectedSubstrings: ['self-learning', 'adr-074', 'self learning', '#2245', 'task-completed'],
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
q: 'deterministic codemod engine var-to-const',
|
|
44
|
+
expect: /codemod|var.?to.?const|143|deterministic/i,
|
|
45
|
+
expectedSubstrings: ['deterministic tier-1 codemod', 'adr-143', 'codemod', 'var-to-const'],
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
q: 'MCP server orphan leak parent-death',
|
|
49
|
+
expect: /mcp.*orphan|orphan.*mcp|parent.?death|leak/i,
|
|
50
|
+
expectedSubstrings: ['mcp orphan', 'mcp servers orphan', 'parent-death', '#2234', 'orphan on every claude'],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
q: 'unified learning stats aggregator',
|
|
54
|
+
expect: /unified|stats|aggregator|075/i,
|
|
55
|
+
expectedSubstrings: ['unified learning-stats', 'adr-075', 'unified learning stats'],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
q: 'structured distillation 4-field schema',
|
|
59
|
+
expect: /distillation|structured|076|4.?field/i,
|
|
60
|
+
expectedSubstrings: ['structured distillation', 'adr-076', '4-field schema'],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
q: 'SQL injection migrate.ts table identifier',
|
|
64
|
+
expect: /sql.?injection|migrate|table|identifier/i,
|
|
65
|
+
expectedSubstrings: ['sql injection', 'shell injection', 'migrate.ts', 'agentdb', 'cve'],
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
q: 'recall@k HNSW benchmark harness',
|
|
69
|
+
expect: /recall|hnsw|benchmark|harness/i,
|
|
70
|
+
expectedSubstrings: ['hnsw', 'memory-recall', 'benchmark suite', 'recall@k', 'benchmark intelligence'],
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
q: 'Q-learning encoder keyword block',
|
|
74
|
+
expect: /q.?learning|encoder|keyword|2239/i,
|
|
75
|
+
expectedSubstrings: ['q-state encoder', 'route q-state', 'keyword block', '#2239', 'q-encoder'],
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
q: 'security hardening crypto random IDs',
|
|
79
|
+
expect: /security|hardening|crypto|random/i,
|
|
80
|
+
expectedSubstrings: ['cwe-347', 'crypto.randomuuid', 'security fix', 'random id', 'crypto random'],
|
|
81
|
+
},
|
|
38
82
|
];
|
|
39
83
|
|
|
84
|
+
/** Returns true if name contains ANY of the labelled substrings (case-insensitive). */
|
|
85
|
+
function isRelevant(name, expectedSubstrings) {
|
|
86
|
+
if (!name || !expectedSubstrings?.length) return false;
|
|
87
|
+
const lower = String(name).toLowerCase();
|
|
88
|
+
return expectedSubstrings.some((s) => lower.includes(s.toLowerCase()));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** nDCG@k with binary relevance (each relevant item contributes 1 / log2(i+1)). */
|
|
92
|
+
function ndcgAtK(rankedRelevance, k) {
|
|
93
|
+
const arr = rankedRelevance.slice(0, k);
|
|
94
|
+
const dcg = arr.reduce((acc, rel, i) => acc + (rel ? 1 / Math.log2(i + 2) : 0), 0);
|
|
95
|
+
const numRelevant = arr.filter(Boolean).length;
|
|
96
|
+
if (numRelevant === 0) return 0;
|
|
97
|
+
// Ideal: all relevant items packed at positions 1..numRelevant
|
|
98
|
+
let idcg = 0;
|
|
99
|
+
for (let i = 0; i < numRelevant; i++) idcg += 1 / Math.log2(i + 2);
|
|
100
|
+
return idcg > 0 ? dcg / idcg : 0;
|
|
101
|
+
}
|
|
102
|
+
|
|
40
103
|
const TOP_K = 5;
|
|
41
104
|
|
|
42
105
|
async function main() {
|
|
@@ -56,6 +119,8 @@ async function main() {
|
|
|
56
119
|
// Default runs hybrid (cosine + BM25 + MMR per ADR-078).
|
|
57
120
|
const listTool = neural.neuralTools.find((t) => t.name === 'neural_patterns');
|
|
58
121
|
const mode = process.env.HYBRID === '0' ? 'cosine' : 'hybrid';
|
|
122
|
+
// ADR-080 opt-in cross-encoder rerank — set RERANK=1 to enable.
|
|
123
|
+
const useRerank = process.env.RERANK === '1';
|
|
59
124
|
|
|
60
125
|
// top-1-uniqueness — fraction of queries whose top-1 result is NOT the
|
|
61
126
|
// same pattern ID as another query's top-1. Catches the "everyone gets
|
|
@@ -64,32 +129,45 @@ async function main() {
|
|
|
64
129
|
|
|
65
130
|
const tQuery0 = performance.now();
|
|
66
131
|
const results = [];
|
|
67
|
-
for (const { q, expect } of QUERIES) {
|
|
68
|
-
const r = await listTool.handler({ action: 'search', query: q, mode, limit: TOP_K });
|
|
132
|
+
for (const { q, expect, expectedSubstrings } of QUERIES) {
|
|
133
|
+
const r = await listTool.handler({ action: 'search', query: q, mode, limit: TOP_K, rerank: useRerank });
|
|
69
134
|
const matches = (r.patterns || r.results || r.matches || []).slice(0, TOP_K);
|
|
70
135
|
if (matches.length > 0) {
|
|
71
136
|
const top1 = matches[0].id;
|
|
72
137
|
top1Ids.set(top1, (top1Ids.get(top1) ?? 0) + 1);
|
|
73
138
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
139
|
+
|
|
140
|
+
// Two relevance signals per result:
|
|
141
|
+
// regexRel — old proxy (kept for back-compat with ADR-077-080 numbers)
|
|
142
|
+
// labelRel — ADR-081 hand-curated label match
|
|
143
|
+
const regexRel = matches.map((m) => expect.test(m?.name ?? ''));
|
|
144
|
+
const labelRel = matches.map((m) => isRelevant(m?.name, expectedSubstrings));
|
|
145
|
+
|
|
146
|
+
// First rank where a labelled-relevant doc appears.
|
|
147
|
+
let firstLabelRank = -1;
|
|
148
|
+
for (let i = 0; i < matches.length; i++) {
|
|
149
|
+
if (labelRel[i]) { firstLabelRank = i + 1; break; }
|
|
85
150
|
}
|
|
151
|
+
let firstRegexRank = -1;
|
|
152
|
+
for (let i = 0; i < matches.length; i++) {
|
|
153
|
+
if (regexRel[i]) { firstRegexRank = i + 1; break; }
|
|
154
|
+
}
|
|
155
|
+
|
|
86
156
|
results.push({
|
|
87
157
|
query: q,
|
|
88
158
|
matched: matches.length > 0,
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
159
|
+
// Regex-proxy metrics (back-compat with ADR-077-080)
|
|
160
|
+
top1Relevant: regexRel[0] || false,
|
|
161
|
+
top3Relevant: regexRel.slice(0, 3).some(Boolean),
|
|
162
|
+
firstRelevantRank: firstRegexRank,
|
|
163
|
+
// ADR-081 labelled metrics
|
|
164
|
+
label_top1: labelRel[0] || false,
|
|
165
|
+
label_top3_count: labelRel.slice(0, 3).filter(Boolean).length,
|
|
166
|
+
label_top5_count: labelRel.slice(0, 5).filter(Boolean).length,
|
|
167
|
+
label_firstRank: firstLabelRank,
|
|
168
|
+
label_ndcg3: ndcgAtK(labelRel, 3),
|
|
169
|
+
label_ndcg5: ndcgAtK(labelRel, 5),
|
|
170
|
+
topK: matches.map((m, i) => ({
|
|
93
171
|
id: m.id,
|
|
94
172
|
name: m.name?.slice(0, 100),
|
|
95
173
|
type: m.type,
|
|
@@ -97,20 +175,38 @@ async function main() {
|
|
|
97
175
|
cosineScore: m.cosineScore,
|
|
98
176
|
bm25Score: m.bm25Score,
|
|
99
177
|
mmrScore: m.mmrScore,
|
|
178
|
+
labelRelevant: labelRel[i],
|
|
100
179
|
})),
|
|
101
180
|
});
|
|
102
181
|
}
|
|
103
182
|
const queryMs = performance.now() - tQuery0;
|
|
104
183
|
|
|
105
184
|
const matchedQueries = results.filter((r) => r.matched).length;
|
|
185
|
+
// Regex-proxy metrics (back-compat)
|
|
106
186
|
const top1Hits = results.filter((r) => r.top1Relevant).length;
|
|
107
187
|
const top3Hits = results.filter((r) => r.top3Relevant).length;
|
|
108
188
|
const ranks = results.filter((r) => r.firstRelevantRank > 0).map((r) => r.firstRelevantRank);
|
|
109
|
-
// MRR over the top-3 window (rank-of-first-relevant). Queries with no
|
|
110
|
-
// relevant result in top-3 contribute 0 to the mean.
|
|
111
189
|
const mrr3 = QUERIES.length > 0
|
|
112
190
|
? Number((ranks.reduce((s, r) => s + 1 / r, 0) / QUERIES.length).toFixed(4))
|
|
113
191
|
: 0;
|
|
192
|
+
|
|
193
|
+
// ADR-081 labelled metrics — tighter ground truth than regex proxy.
|
|
194
|
+
const label_top1Hits = results.filter((r) => r.label_top1).length;
|
|
195
|
+
const label_top3HitsBinary = results.filter((r) => r.label_top3_count > 0).length;
|
|
196
|
+
const label_ranks = results.filter((r) => r.label_firstRank > 0).map((r) => r.label_firstRank);
|
|
197
|
+
const label_mrr3 = QUERIES.length > 0
|
|
198
|
+
? Number((label_ranks.reduce((s, r) => s + 1 / r, 0) / QUERIES.length).toFixed(4))
|
|
199
|
+
: 0;
|
|
200
|
+
const label_ndcg3_mean = QUERIES.length > 0
|
|
201
|
+
? Number((results.reduce((s, r) => s + r.label_ndcg3, 0) / QUERIES.length).toFixed(4))
|
|
202
|
+
: 0;
|
|
203
|
+
const label_ndcg5_mean = QUERIES.length > 0
|
|
204
|
+
? Number((results.reduce((s, r) => s + r.label_ndcg5, 0) / QUERIES.length).toFixed(4))
|
|
205
|
+
: 0;
|
|
206
|
+
// Mean precision@3 — fraction of top-3 that's relevant per query, averaged.
|
|
207
|
+
const label_precision3 = QUERIES.length > 0
|
|
208
|
+
? Number((results.reduce((s, r) => s + r.label_top3_count / 3, 0) / QUERIES.length).toFixed(4))
|
|
209
|
+
: 0;
|
|
114
210
|
// top-1 collision: number of distinct top-1 IDs over the query count.
|
|
115
211
|
const uniqueTop1 = top1Ids.size;
|
|
116
212
|
const top1Diversity = Number((uniqueTop1 / QUERIES.length).toFixed(4));
|
|
@@ -134,13 +230,22 @@ async function main() {
|
|
|
134
230
|
runAt: new Date().toISOString(),
|
|
135
231
|
benchmark: 'pretrained-retrieval',
|
|
136
232
|
mode, // ADR-078: which retrieval path was used
|
|
233
|
+
rerank: useRerank, // ADR-080: cross-encoder rerank on/off
|
|
137
234
|
storeSize: total,
|
|
138
235
|
queries: QUERIES.length,
|
|
139
236
|
matchedQueries,
|
|
140
237
|
matchRate: Number((matchedQueries / QUERIES.length).toFixed(4)),
|
|
141
|
-
|
|
238
|
+
// Regex-proxy metrics (ADR-077-080 back-compat)
|
|
239
|
+
top1HitRate: Number((top1Hits / QUERIES.length).toFixed(4)),
|
|
142
240
|
top3HitRate: Number((top3Hits / QUERIES.length).toFixed(4)),
|
|
143
|
-
mrr3,
|
|
241
|
+
mrr3,
|
|
242
|
+
// ADR-081 labelled metrics — tighter ground truth
|
|
243
|
+
label_top1HitRate: Number((label_top1Hits / QUERIES.length).toFixed(4)),
|
|
244
|
+
label_top3HitRate: Number((label_top3HitsBinary / QUERIES.length).toFixed(4)),
|
|
245
|
+
label_mrr3,
|
|
246
|
+
label_precision3,
|
|
247
|
+
label_ndcg3: label_ndcg3_mean,
|
|
248
|
+
label_ndcg5: label_ndcg5_mean,
|
|
144
249
|
top1Diversity, // 1.0 = every query gets a distinct top-1
|
|
145
250
|
top3DupRate, // 0.0 = no duplicate IDs inside any top-3
|
|
146
251
|
avgQueryLatencyMs: Number((queryMs / QUERIES.length).toFixed(2)),
|
|
@@ -153,13 +258,20 @@ async function main() {
|
|
|
153
258
|
console.log(JSON.stringify(summary, null, 2));
|
|
154
259
|
} else {
|
|
155
260
|
console.log(`# Pretrained-retrieval benchmark — proof of learning`);
|
|
156
|
-
console.log(`Mode: ${mode}${mode === 'hybrid' ? ' (cosine + BM25 + MMR, ADR-078)' : ' (cosine-only, pre-3.10.18)'}`);
|
|
261
|
+
console.log(`Mode: ${mode}${mode === 'hybrid' ? ' (cosine + BM25 + MMR, ADR-078)' : ' (cosine-only, pre-3.10.18)'}${useRerank ? ' + cross-encoder rerank (ADR-080)' : ''}`);
|
|
157
262
|
console.log(`Store size: ${total} patterns`);
|
|
158
263
|
console.log(`Queries: ${QUERIES.length}`);
|
|
159
264
|
console.log(`Match rate: ${(summary.matchRate * 100).toFixed(0)}% (${matchedQueries}/${QUERIES.length})`);
|
|
160
|
-
console.log(`Top-1 hit rate (
|
|
161
|
-
console.log(`Top-3 hit rate (
|
|
162
|
-
console.log(`MRR@3:
|
|
265
|
+
console.log(`Top-1 hit rate (regex proxy): ${(summary.top1HitRate * 100).toFixed(0)}% (${top1Hits}/${QUERIES.length})`);
|
|
266
|
+
console.log(`Top-3 hit rate (regex proxy): ${(summary.top3HitRate * 100).toFixed(0)}% (${top3Hits}/${QUERIES.length})`);
|
|
267
|
+
console.log(`MRR@3 (regex proxy): ${summary.mrr3}`);
|
|
268
|
+
console.log('');
|
|
269
|
+
console.log(`Top-1 hit rate (ADR-081 labelled): ${(summary.label_top1HitRate * 100).toFixed(0)}% (${label_top1Hits}/${QUERIES.length})`);
|
|
270
|
+
console.log(`Top-3 hit rate (ADR-081 labelled): ${(summary.label_top3HitRate * 100).toFixed(0)}% (${label_top3HitsBinary}/${QUERIES.length})`);
|
|
271
|
+
console.log(`MRR@3 (labelled): ${summary.label_mrr3}`);
|
|
272
|
+
console.log(`Precision@3 (labelled, mean): ${summary.label_precision3}`);
|
|
273
|
+
console.log(`nDCG@3 (labelled, mean): ${summary.label_ndcg3}`);
|
|
274
|
+
console.log(`nDCG@5 (labelled, mean): ${summary.label_ndcg5}`);
|
|
163
275
|
console.log(`Top-1 diversity: ${(summary.top1Diversity * 100).toFixed(0)}% (${uniqueTop1} distinct top-1 IDs across ${QUERIES.length} queries)`);
|
|
164
276
|
console.log(`Top-3 dup rate: ${(summary.top3DupRate * 100).toFixed(0)}%`);
|
|
165
277
|
console.log(`Avg query latency: ${summary.avgQueryLatencyMs} ms`);
|