@claude-flow/cli 3.32.19 → 3.32.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.
@@ -1 +1 @@
1
- 3.32.18
1
+ 3.32.19
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest": {
3
- "version": "3.32.19",
3
+ "version": "3.32.21",
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": "zNv+zmXpSZI/3JYym+FY2sEhWuFeR7k2L4pKiQGIcjFUQ/hcd3p5VVkqdO1+EsX9/JSDP6TVLIjrW6IqQq1vAg==",
11
+ "signature": "/u6cu5Rvxtw6aRDpqL1ZUGcCxrzoRUfDRtDI13Yd8RQres08uHFd1tSWJgDDw+fYYNHvmR/yfJS3Hb9KCccTAQ==",
12
12
  "algorithm": "ed25519"
13
13
  }
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "generation": 1,
4
- "generatedAt": "2026-07-27T03:04:01.949Z",
5
- "gitSha": "7e76906f",
4
+ "generatedAt": "2026-07-27T03:27:12.952Z",
5
+ "gitSha": "6b6eef90",
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...' });
@@ -369,7 +369,13 @@ const searchCommand = {
369
369
  const query = ctx.flags.query || ctx.args[0];
370
370
  let namespace = ctx.flags.namespace || 'all';
371
371
  const limit = ctx.flags.limit || 10;
372
- const threshold = ctx.flags.threshold || 0.3;
372
+ // #2790 fix — `||` discards an explicit `--threshold 0` (falsy) and
373
+ // silently uses the fallback; that made `--threshold 0` return
374
+ // FEWER results than `--threshold 0.01` (non-monotonic). Nullish
375
+ // coalescing preserves an explicit zero. Fallback aligned with the
376
+ // option's declared `default: 0.7` (was `0.3` — the two disagreed
377
+ // and --help advertised a default the code did not honor).
378
+ const threshold = ctx.flags.threshold ?? 0.7;
373
379
  const searchType = ctx.flags.type || 'semantic';
374
380
  const buildHnsw = (ctx.flags['build-hnsw'] || ctx.flags.buildHnsw);
375
381
  const requestedIntent = ctx.flags.intent || 'mixed';
@@ -430,9 +436,58 @@ const searchCommand = {
430
436
  output.writeln();
431
437
  // Use direct sql.js search with vector similarity
432
438
  try {
433
- const { searchEntries, resolveDbPath: _rdbSearch } = await import('../memory/memory-initializer.js');
439
+ const { searchEntries, listEntries, resolveDbPath: _rdbSearch } = await import('../memory/memory-initializer.js');
434
440
  const dbPathSearch = _rdbSearch(ctx.flags.path);
435
441
  const useSmart = (ctx.flags.smart || ctx.flags.s);
442
+ // #2790 fix — keyword mode was declared, echoed, and IGNORED (every
443
+ // search ran semantic regardless). Wire it here: list entries in
444
+ // the requested namespace (or all) and substring-match against key
445
+ // + content. Hybrid unions the keyword hits with the semantic hits
446
+ // and dedups by key + namespace.
447
+ let keywordResults = [];
448
+ if (searchType === 'keyword' || searchType === 'hybrid') {
449
+ const list = await listEntries({
450
+ namespace: namespace === 'all' ? undefined : namespace,
451
+ limit: 5000,
452
+ includeContent: true,
453
+ dbPath: dbPathSearch,
454
+ });
455
+ if (list.success) {
456
+ const q = query.toLowerCase();
457
+ keywordResults = list.entries
458
+ .filter((e) => {
459
+ const c = e.content ?? '';
460
+ return e.key.toLowerCase().includes(q) || c.toLowerCase().includes(q);
461
+ })
462
+ .map((e) => {
463
+ const c = e.content ?? '';
464
+ const inKey = e.key.toLowerCase().includes(q);
465
+ // Keyword scoring: 1.0 for key hit, 0.7 for content hit
466
+ return {
467
+ key: e.key,
468
+ score: inKey ? 1.0 : 0.7,
469
+ namespace: e.namespace,
470
+ preview: c.slice(0, 200),
471
+ };
472
+ })
473
+ .sort((a, b) => b.score - a.score)
474
+ .slice(0, limit);
475
+ }
476
+ if (searchType === 'keyword') {
477
+ // Pure-keyword mode returns directly; skip the semantic path entirely.
478
+ if (ctx.flags.format === 'json') {
479
+ output.printJson({ query, searchType, results: keywordResults, searchTime: '0ms' });
480
+ return { success: true, data: keywordResults };
481
+ }
482
+ output.writeln();
483
+ output.printSuccess(`Found ${keywordResults.length} result(s) via keyword substring match`);
484
+ for (const r of keywordResults) {
485
+ output.writeln(` ${r.key} (${r.namespace}, score=${r.score.toFixed(2)}) — ${r.preview.slice(0, 80)}${r.preview.length > 80 ? '…' : ''}`);
486
+ }
487
+ return { success: true, data: keywordResults };
488
+ }
489
+ // Hybrid mode: keyword hits will be MERGED after semantic runs below.
490
+ }
436
491
  let results;
437
492
  let searchTimeMs;
438
493
  let smartStats;
@@ -513,6 +568,22 @@ const searchCommand = {
513
568
  }));
514
569
  searchTimeMs = searchResult.searchTime;
515
570
  }
571
+ // #2790 fix (hybrid mode): union keyword results with semantic
572
+ // results, dedup by (key, namespace), then take top `limit`.
573
+ if (searchType === 'hybrid' && keywordResults.length > 0) {
574
+ const seen = new Set(results.map((r) => `${r.namespace}::${r.key}`));
575
+ for (const k of keywordResults) {
576
+ const id = `${k.namespace}::${k.key}`;
577
+ if (!seen.has(id)) {
578
+ results.push(k);
579
+ seen.add(id);
580
+ }
581
+ }
582
+ // Rerank by score
583
+ results.sort((a, b) => b.score - a.score);
584
+ results = results.slice(0, limit);
585
+ backendLabel = `${backendLabel} + keyword union (#2790)`;
586
+ }
516
587
  if (ctx.flags.format === 'json') {
517
588
  output.printJson({ query, searchType, results, searchTime: `${searchTimeMs}ms`, ...(smartStats ? { stats: smartStats } : {}) });
518
589
  return { success: true, data: results };
@@ -843,10 +843,68 @@ const coordinateCommand = {
843
843
  }
844
844
  };
845
845
  // Main swarm command
846
+ // #2727 dream-cycle — inter-agent message compressor (IB+VQ-inspired
847
+ // MVP). Advisory tool: takes a message + token budget, returns a
848
+ // compressed variant that preserves must-see spans (code, URLs, paths)
849
+ // and keeps the top-scored sentences by TF-IDF-ish keyword density.
850
+ // v2 wires a real VQ codec once a training pipeline exists.
851
+ const compressMessageCommand = {
852
+ name: 'compress-message',
853
+ description: 'Compress an inter-agent message to a token budget (IB+VQ-inspired, dream-cycle #2727)',
854
+ options: [
855
+ { name: 'message', short: 'm', type: 'string', description: 'Message text (or use --message-file)' },
856
+ { name: 'message-file', type: 'string', description: 'Path to a file whose contents will be compressed' },
857
+ { name: 'budget-tokens', short: 'b', type: 'number', default: 200, description: 'Target token budget (approximate, ~4 chars per token)' },
858
+ { name: 'mode', type: 'string', choices: ['keyword', 'sentence', 'hybrid'], default: 'hybrid', description: 'Scoring mode' },
859
+ ],
860
+ examples: [
861
+ { command: 'claude-flow swarm compress-message -m "…" --budget-tokens 100', description: 'Compress inline message to 100 tokens' },
862
+ { command: 'claude-flow swarm compress-message --message-file ./msg.md -b 300 --format json', description: 'JSON for pipelines' },
863
+ ],
864
+ action: async (ctx) => {
865
+ const inlineMsg = ctx.flags.message;
866
+ const msgFile = ctx.flags.messageFile;
867
+ const budgetTokens = ctx.flags.budgetTokens || 200;
868
+ const mode = ctx.flags.mode || 'hybrid';
869
+ let message = inlineMsg ?? '';
870
+ if (msgFile) {
871
+ try {
872
+ const fsMod = await import('node:fs');
873
+ const pathMod = await import('node:path');
874
+ message = fsMod.readFileSync(pathMod.resolve(msgFile), 'utf-8');
875
+ }
876
+ catch (err) {
877
+ output.printError(`Failed to read ${msgFile}: ${err instanceof Error ? err.message : String(err)}`);
878
+ return { success: false, exitCode: 1 };
879
+ }
880
+ }
881
+ if (!message) {
882
+ output.printError('No message provided. Use --message "..." or --message-file <path>.');
883
+ return { success: false, exitCode: 1 };
884
+ }
885
+ const { compressMessage } = await import('../swarm/message-compressor.js');
886
+ const result = compressMessage(message, { budgetTokens, mode: mode });
887
+ if (ctx.flags.format === 'json') {
888
+ output.printJson(result);
889
+ return { success: true, data: result };
890
+ }
891
+ output.writeln();
892
+ output.printBox(`Original: ${result.stats.originalTokens} tokens · Compressed: ${result.stats.compressedTokens} tokens\n` +
893
+ `Ratio: ${(result.stats.compressionRatio * 100).toFixed(1)}%\n` +
894
+ `Sentences kept: ${result.stats.sentencesKept}/${result.stats.sentencesTotal} · Preserved spans: ${result.stats.preservedSpans}\n` +
895
+ `Info retained (top-quartile): ${(result.stats.infoRetainedEstimate * 100).toFixed(1)}%`, 'Message Compressor (#2727 IB+VQ MVP)');
896
+ output.writeln();
897
+ output.writeln(output.bold('Compressed message'));
898
+ output.writeln(output.dim('─'.repeat(60)));
899
+ output.writeln(result.compressed);
900
+ output.writeln(output.dim('─'.repeat(60)));
901
+ return { success: true, data: result };
902
+ },
903
+ };
846
904
  export const swarmCommand = {
847
905
  name: 'swarm',
848
906
  description: 'Swarm coordination commands',
849
- subcommands: [initCommand, startCommand, statusCommand, stopCommand, scaleCommand, coordinateCommand],
907
+ subcommands: [initCommand, startCommand, statusCommand, stopCommand, scaleCommand, coordinateCommand, compressMessageCommand],
850
908
  options: [],
851
909
  examples: [
852
910
  { command: 'claude-flow swarm init --v3-mode', description: 'Initialize V3 swarm' },
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Inter-agent message compressor (#2727 dream-cycle, IB+VQ inspired).
3
+ *
4
+ * Dream-cycle #2727 (IB+VQ messaging 181.8% task gain): variable-
5
+ * bandwidth compression at the inter-agent message boundary breaks the
6
+ * performance/bandwidth tradeoff. Real VQ needs a codebook trained on
7
+ * actual agent-to-agent traffic (own PR, own dataset). This v1 MVP
8
+ * delivers the SPIRIT of the finding with a deterministic pipeline:
9
+ *
10
+ * 1. Extract structured "must-preserve" spans (code fences, inline
11
+ * code, URLs, file paths, tool-call directives) — never compress
12
+ * those; they're load-bearing.
13
+ * 2. Score sentences by keyword density (TF-IDF against a domain
14
+ * stop-list) and keep the top-K sentences that fit `budgetTokens`.
15
+ * 3. Reassemble: preserved spans in original order + top scored
16
+ * sentences.
17
+ *
18
+ * v2 wires a real VQ codec once the training pipeline lands.
19
+ *
20
+ * SCOPE: advisory tool + library. Callers decide whether to use the
21
+ * compressed form. No auto-wire into SendMessage / hooks in v1.
22
+ */
23
+ /** Result of a compression pass. */
24
+ export interface CompressionResult {
25
+ original: string;
26
+ compressed: string;
27
+ stats: {
28
+ originalTokens: number;
29
+ compressedTokens: number;
30
+ compressionRatio: number;
31
+ preservedSpans: number;
32
+ sentencesKept: number;
33
+ sentencesTotal: number;
34
+ /** Rough info-retention proxy: fraction of top-quartile-scored sentences kept. */
35
+ infoRetainedEstimate: number;
36
+ };
37
+ }
38
+ export interface CompressOptions {
39
+ /**
40
+ * Target token budget. The compressor keeps preserved spans first
41
+ * (they're mandatory) and then fills the remaining budget with the
42
+ * highest-scored sentences. Approximate — actual output may exceed
43
+ * budget by up to one sentence if a preserved span is very large.
44
+ */
45
+ budgetTokens?: number;
46
+ /**
47
+ * Mode:
48
+ * 'keyword' → sentence score = sum(TF-IDF-approx) of unique keywords
49
+ * 'sentence' → sentence score = length + position penalty (favor lead)
50
+ * 'hybrid' → 0.7·keyword + 0.3·sentence (default)
51
+ */
52
+ mode?: 'keyword' | 'sentence' | 'hybrid';
53
+ }
54
+ /** ~1 token per 4 chars — matches the OpenAI/Anthropic rule-of-thumb. */
55
+ export declare function estimateTokens(text: string): number;
56
+ export declare function compressMessage(message: string, options?: CompressOptions): CompressionResult;
57
+ //# sourceMappingURL=message-compressor.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-flow/cli",
3
- "version": "3.32.19",
3
+ "version": "3.32.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",