@claude-flow/cli 3.32.20 → 3.32.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.
@@ -1 +1 @@
1
- 3.32.19
1
+ 3.32.9
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest": {
3
- "version": "3.32.20",
3
+ "version": "3.32.22",
4
4
  "files": {
5
5
  "auto-memory-hook.mjs": "e3e1033b24704992ddef6b31c7fa9dd7fcd9e1af7935dd77ef73402b916b31e6",
6
6
  "hook-handler.cjs": "f87ec28684bfc5fd0a54c46bd2a53a3fb037defe71d0ea4b8a5cb88a2cd87a3f",
@@ -8,6 +8,6 @@
8
8
  "statusline.cjs": "10f74a1aa9af217d0623c0a9839d82825b0eed31a4707a7531a85c6116869a6c"
9
9
  }
10
10
  },
11
- "signature": "bY9ZP2Fd6NCPqd/ECMOGZ1yhiR2yIlRP/anY6HcLPuIKFiR1upJK07TUWQUMS7Eac2Ys+CjxCr2KEvkb4iJsDA==",
11
+ "signature": "x+a6i2vOgGoyYNLlXKQR5fpycPB+vO4DJtKMljxX4ZNpY8ED0AQ2XoMToVJn/2XWA+70yerxmytfVBDP546WCg==",
12
12
  "algorithm": "ed25519"
13
13
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "adoptedAt": 1785103598198,
2
+ "adoptedAt": 1785161585700,
3
3
  "championId": "sha256:6141a8ea990c5063b77e090ae8f37f9c539d8aa8f58dcceb30f3a82f97e57319",
4
4
  "manifest": {
5
5
  "schema": "ruflo.proven-config/v1",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "generation": 1,
4
- "generatedAt": "2026-07-27T03:15:06.269Z",
5
- "gitSha": "353d0663",
4
+ "generatedAt": "2026-07-27T14:27:09.930Z",
5
+ "gitSha": "e263c269",
6
6
  "catalog": {
7
7
  "agents": 164,
8
8
  "tools": 387,
@@ -111,8 +111,16 @@ const searchCommand = {
111
111
  action: async (ctx) => {
112
112
  const query = ctx.flags.query;
113
113
  const namespace = ctx.flags.collection || 'default';
114
- const limit = parseInt(ctx.flags.limit || '10', 10);
115
- const threshold = parseFloat(ctx.flags.threshold || '0.5');
114
+ // #2790 fix `||` on a numeric-0 argument returns the fallback,
115
+ // so `--threshold 0` was unreachable. `??` preserves an explicit zero.
116
+ // The flag arrives as a string here (parser produces string for typed
117
+ // options passed via CLI without type coercion in some paths), so we
118
+ // check for undefined explicitly rather than falsy.
119
+ const limit = parseInt(ctx.flags.limit ?? '10', 10);
120
+ const rawThreshold = ctx.flags.threshold;
121
+ const threshold = rawThreshold === undefined || rawThreshold === null
122
+ ? 0.5
123
+ : parseFloat(String(rawThreshold));
116
124
  const dbPath = ctx.flags['db-path'] || '.swarm/memory.db';
117
125
  if (!query) {
118
126
  output.printError('Query is required');
@@ -1006,7 +1014,12 @@ const neuralCommand = {
1006
1014
  action: async (ctx) => {
1007
1015
  const feature = ctx.flags.feature || 'all';
1008
1016
  const init = ctx.flags.init;
1009
- const driftThreshold = parseFloat((ctx.flags['drift-threshold'] || ctx.flags.driftThreshold || '0.3'));
1017
+ // #2790 fix same `0 || fallback` bug: `--drift-threshold 0` was
1018
+ // silently replaced by 0.3. Use nullish coalescing.
1019
+ const rawDriftThreshold = ctx.flags['drift-threshold'] ?? ctx.flags.driftThreshold;
1020
+ const driftThreshold = rawDriftThreshold === undefined || rawDriftThreshold === null
1021
+ ? 0.3
1022
+ : parseFloat(String(rawDriftThreshold));
1010
1023
  const decayRate = parseFloat((ctx.flags['decay-rate'] || ctx.flags.decayRate || '0.01'));
1011
1024
  const consolidationInterval = parseInt((ctx.flags['consolidation-interval'] || ctx.flags.consolidationInterval || '60000'), 10);
1012
1025
  output.writeln();
@@ -1336,7 +1336,7 @@ const transferFromProjectCommand = {
1336
1336
  ],
1337
1337
  action: async (ctx) => {
1338
1338
  const sourcePath = ctx.flags.source || ctx.args[0];
1339
- const minConfidence = ctx.flags.minConfidence || 0.7;
1339
+ const minConfidence = ctx.flags.minConfidence ?? 0.7;
1340
1340
  if (!sourcePath) {
1341
1341
  output.printError('Source project path is required. Use --source or -s flag.');
1342
1342
  return { success: false, exitCode: 1 };
@@ -2671,7 +2671,7 @@ const coverageRouteCommand = {
2671
2671
  ],
2672
2672
  action: async (ctx) => {
2673
2673
  const task = ctx.flags.task || ctx.args[0];
2674
- const threshold = ctx.flags.threshold || 80;
2674
+ const threshold = ctx.flags.threshold ?? 80;
2675
2675
  const useRuvector = !ctx.flags['no-ruvector'];
2676
2676
  if (!task) {
2677
2677
  output.printError('Task description is required. Use --task or -t flag.');
@@ -2891,7 +2891,7 @@ const coverageSuggestCommand = {
2891
2891
  ],
2892
2892
  action: async (ctx) => {
2893
2893
  const targetPath = ctx.flags.path || ctx.args[0];
2894
- const threshold = ctx.flags.threshold || 80;
2894
+ const threshold = ctx.flags.threshold ?? 80;
2895
2895
  const limit = ctx.flags.limit || 20;
2896
2896
  if (!targetPath) {
2897
2897
  output.printError('Path is required. Use --path or -p flag.');
@@ -3080,7 +3080,7 @@ const coverageGapsCommand = {
3080
3080
  { command: 'claude-flow hooks coverage-gaps --threshold 90', description: 'Stricter threshold' }
3081
3081
  ],
3082
3082
  action: async (ctx) => {
3083
- const threshold = ctx.flags.threshold || 80;
3083
+ const threshold = ctx.flags.threshold ?? 80;
3084
3084
  const groupByAgent = ctx.flags['group-by-agent'] !== false;
3085
3085
  const criticalOnly = ctx.flags['critical-only'] || false;
3086
3086
  const spinner = output.createSpinner({ text: 'Analyzing project coverage gaps...' });
@@ -78,10 +78,13 @@ const storeCommand = {
78
78
  // on the value BEFORE persistence and refuse the write if a
79
79
  // finding is present. Opt-in per-call; RUFLO_MEMORY_SCAN_ON_WRITE=1
80
80
  // enables it globally.
81
+ // NOTE: no `default:` here — needed so the ADR-125 CLI-flag-wins
82
+ // precedence (`ctx.flags.scanContent ?? envVar`) can distinguish
83
+ // "user didn't pass the flag" (undefined) from "user explicitly
84
+ // said --no-scan-content" (false). Fixes #2794.
81
85
  name: 'scan-content',
82
86
  description: 'Scan the value for injection payloads before persisting (dream-cycle #2752 MemPoison gate)',
83
87
  type: 'boolean',
84
- default: false
85
88
  },
86
89
  DB_PATH_OPTION
87
90
  ],
@@ -126,9 +129,11 @@ const storeCommand = {
126
129
  return { success: false, exitCode: 1 };
127
130
  }
128
131
  // #2752 MemPoison gate — scan before persist when opted in.
129
- const scanContentFlag = ctx.flags.scanContent;
130
- const scanContentEnv = /^(1|true|yes|on)$/i.test(String(process.env.RUFLO_MEMORY_SCAN_ON_WRITE ?? ''));
131
- if (scanContentFlag || scanContentEnv) {
132
+ // CLI flag ctx.flags.scanContent takes precedence over RUFLO_MEMORY_SCAN_ON_WRITE env var
133
+ // (ADR-125 §"CLI flag wins" / ADR-130 §env-var-config-precedence fix for #2794).
134
+ const scanContent = ctx.flags.scanContent
135
+ ?? /^(1|true|yes|on)$/i.test(String(process.env.RUFLO_MEMORY_SCAN_ON_WRITE ?? ''));
136
+ if (scanContent) {
132
137
  try {
133
138
  const { scanChannelMessage } = await import('../security/channel-guard.js');
134
139
  const scan = scanChannelMessage(value);
@@ -369,7 +374,13 @@ const searchCommand = {
369
374
  const query = ctx.flags.query || ctx.args[0];
370
375
  let namespace = ctx.flags.namespace || 'all';
371
376
  const limit = ctx.flags.limit || 10;
372
- const threshold = ctx.flags.threshold || 0.3;
377
+ // #2790 fix — `||` discards an explicit `--threshold 0` (falsy) and
378
+ // silently uses the fallback; that made `--threshold 0` return
379
+ // FEWER results than `--threshold 0.01` (non-monotonic). Nullish
380
+ // coalescing preserves an explicit zero. Fallback aligned with the
381
+ // option's declared `default: 0.7` (was `0.3` — the two disagreed
382
+ // and --help advertised a default the code did not honor).
383
+ const threshold = ctx.flags.threshold ?? 0.7;
373
384
  const searchType = ctx.flags.type || 'semantic';
374
385
  const buildHnsw = (ctx.flags['build-hnsw'] || ctx.flags.buildHnsw);
375
386
  const requestedIntent = ctx.flags.intent || 'mixed';
@@ -430,9 +441,58 @@ const searchCommand = {
430
441
  output.writeln();
431
442
  // Use direct sql.js search with vector similarity
432
443
  try {
433
- const { searchEntries, resolveDbPath: _rdbSearch } = await import('../memory/memory-initializer.js');
444
+ const { searchEntries, listEntries, resolveDbPath: _rdbSearch } = await import('../memory/memory-initializer.js');
434
445
  const dbPathSearch = _rdbSearch(ctx.flags.path);
435
446
  const useSmart = (ctx.flags.smart || ctx.flags.s);
447
+ // #2790 fix — keyword mode was declared, echoed, and IGNORED (every
448
+ // search ran semantic regardless). Wire it here: list entries in
449
+ // the requested namespace (or all) and substring-match against key
450
+ // + content. Hybrid unions the keyword hits with the semantic hits
451
+ // and dedups by key + namespace.
452
+ let keywordResults = [];
453
+ if (searchType === 'keyword' || searchType === 'hybrid') {
454
+ const list = await listEntries({
455
+ namespace: namespace === 'all' ? undefined : namespace,
456
+ limit: 5000,
457
+ includeContent: true,
458
+ dbPath: dbPathSearch,
459
+ });
460
+ if (list.success) {
461
+ const q = query.toLowerCase();
462
+ keywordResults = list.entries
463
+ .filter((e) => {
464
+ const c = e.content ?? '';
465
+ return e.key.toLowerCase().includes(q) || c.toLowerCase().includes(q);
466
+ })
467
+ .map((e) => {
468
+ const c = e.content ?? '';
469
+ const inKey = e.key.toLowerCase().includes(q);
470
+ // Keyword scoring: 1.0 for key hit, 0.7 for content hit
471
+ return {
472
+ key: e.key,
473
+ score: inKey ? 1.0 : 0.7,
474
+ namespace: e.namespace,
475
+ preview: c.slice(0, 200),
476
+ };
477
+ })
478
+ .sort((a, b) => b.score - a.score)
479
+ .slice(0, limit);
480
+ }
481
+ if (searchType === 'keyword') {
482
+ // Pure-keyword mode returns directly; skip the semantic path entirely.
483
+ if (ctx.flags.format === 'json') {
484
+ output.printJson({ query, searchType, results: keywordResults, searchTime: '0ms' });
485
+ return { success: true, data: keywordResults };
486
+ }
487
+ output.writeln();
488
+ output.printSuccess(`Found ${keywordResults.length} result(s) via keyword substring match`);
489
+ for (const r of keywordResults) {
490
+ output.writeln(` ${r.key} (${r.namespace}, score=${r.score.toFixed(2)}) — ${r.preview.slice(0, 80)}${r.preview.length > 80 ? '…' : ''}`);
491
+ }
492
+ return { success: true, data: keywordResults };
493
+ }
494
+ // Hybrid mode: keyword hits will be MERGED after semantic runs below.
495
+ }
436
496
  let results;
437
497
  let searchTimeMs;
438
498
  let smartStats;
@@ -513,6 +573,22 @@ const searchCommand = {
513
573
  }));
514
574
  searchTimeMs = searchResult.searchTime;
515
575
  }
576
+ // #2790 fix (hybrid mode): union keyword results with semantic
577
+ // results, dedup by (key, namespace), then take top `limit`.
578
+ if (searchType === 'hybrid' && keywordResults.length > 0) {
579
+ const seen = new Set(results.map((r) => `${r.namespace}::${r.key}`));
580
+ for (const k of keywordResults) {
581
+ const id = `${k.namespace}::${k.key}`;
582
+ if (!seen.has(id)) {
583
+ results.push(k);
584
+ seen.add(id);
585
+ }
586
+ }
587
+ // Rerank by score
588
+ results.sort((a, b) => b.score - a.score);
589
+ results = results.slice(0, limit);
590
+ backendLabel = `${backendLabel} + keyword union (#2790)`;
591
+ }
516
592
  if (ctx.flags.format === 'json') {
517
593
  output.printJson({ query, searchType, results, searchTime: `${searchTimeMs}ms`, ...(smartStats ? { stats: smartStats } : {}) });
518
594
  return { success: true, data: results };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-flow/cli",
3
- "version": "3.32.20",
3
+ "version": "3.32.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",