@claude-flow/cli 3.57.0 → 3.57.2

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.
Files changed (33) hide show
  1. package/.claude/helpers/.helpers-version +1 -1
  2. package/.claude/helpers/helpers.manifest.json +3 -3
  3. package/.claude/helpers/intelligence.cjs +3 -5
  4. package/README.md +12 -3
  5. package/catalog-manifest.json +2 -2
  6. package/dist/src/commands/hooks.js +20 -1
  7. package/dist/src/commands/memory.js +25 -1
  8. package/dist/src/commands/workflow.js +55 -15
  9. package/dist/src/init/executor.js +9 -5
  10. package/dist/src/init/helper-companions.d.ts +1 -1
  11. package/dist/src/init/helper-companions.js +4 -3
  12. package/dist/src/mcp-tools/hooks-tools.js +9 -3
  13. package/dist/src/mcp-tools/memory-tools.js +58 -9
  14. package/dist/src/mcp-tools/workflow-tools.js +2 -1
  15. package/dist/src/memory/graph-edge-writer.d.ts +16 -4
  16. package/dist/src/memory/graph-edge-writer.js +19 -7
  17. package/dist/src/memory/intelligence.js +4 -0
  18. package/dist/src/memory/memory-initializer.js +12 -5
  19. package/dist/src/missions/schemas.d.ts +113 -113
  20. package/dist/src/mods/command-registry/schema.d.ts +193 -193
  21. package/dist/src/services/memory-backup.d.ts +6 -0
  22. package/dist/src/services/memory-backup.js +109 -81
  23. package/dist/src/services/policy-ledger-anchor.d.ts +2 -0
  24. package/dist/src/services/policy-ledger-anchor.js +4 -4
  25. package/dist/src/services/policy-runtime.js +54 -3
  26. package/dist/src/services/worker-daemon.js +4 -0
  27. package/node_modules/@claude-flow/codex/README.md +9 -9
  28. package/node_modules/@claude-flow/codex/dist/cli.js +0 -0
  29. package/node_modules/@claude-flow/codex/dist/dual-mode/cli.js +3 -3
  30. package/node_modules/@claude-flow/codex/dist/dual-mode/cli.js.map +1 -1
  31. package/node_modules/@claude-flow/plugin-agent-federation/dist/bin.js +0 -0
  32. package/package.json +5 -4
  33. package/plugins/ruflo-console/dist/tui.mjs +158 -158
@@ -1 +1 @@
1
- 3.57.0
1
+ 3.57.1
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "manifest": {
3
- "version": "3.57.0",
3
+ "version": "3.57.2",
4
4
  "files": {
5
5
  "auto-memory-hook.mjs": "631fac9d518835e2852a406888316bbbddddb49e7ed8e406d2e971f46833df9a",
6
6
  "hook-handler.cjs": "bc12b3cae92d5a7c8deef699fa00d0ef07b57625c18dbeac4387f15725b8e09f",
7
- "intelligence.cjs": "a95ac9ee2759c340b14f9ff656765bcb05e0cefe2f018116bd26b3ba9987f3e4",
7
+ "intelligence.cjs": "1d981d6c6a35d70a66450e96458e26169e0cede11523ea246a2c129c197a5b4e",
8
8
  "statusline.cjs": "d72417ecb38634011a19f095a30e07b9929e6f97663520172ece9be7491b2eaa",
9
9
  "router.cjs": "16648b3c0d73b6681ac2d8d13def6a68286c57f5a11004de5b19a0fa088c833d"
10
10
  }
11
11
  },
12
- "signature": "Ht+AuqnXP25KmeeDXXSoiQCKrwm7upYx89CGQajBLMpPEsiqHY93UUQthma2Jr2hhHZxys+ui0i4ZxvdAGcFBg==",
12
+ "signature": "1NC9a0vJ78KCh8TIZfhExMq0923sXMkAej7Dxg4hlTqmOBzS1Gn9ex7z1lpkCTOlVafXVVXXMsEa1BSdsvQ7CQ==",
13
13
  "algorithm": "ed25519"
14
14
  }
@@ -277,11 +277,9 @@ function sessionGet(key) {
277
277
 
278
278
  function sessionSet(key, value) {
279
279
  try {
280
- if (!fs.existsSync(SESSION_DIR)) fs.mkdirSync(SESSION_DIR, { recursive: true });
281
- let session = {};
282
- if (fs.existsSync(SESSION_FILE)) {
283
- session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
284
- }
280
+ // Session creation belongs to session.cjs; recall may run before SessionStart.
281
+ if (!fs.existsSync(SESSION_FILE)) return;
282
+ const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
285
283
  if (!session.context) session.context = {};
286
284
  session.context[key] = value;
287
285
  session.updatedAt = new Date().toISOString();
package/README.md CHANGED
@@ -10,10 +10,11 @@
10
10
  [![Star on GitHub](https://img.shields.io/github/stars/ruvnet/claude-flow?style=for-the-badge&logo=github&color=gold)](https://github.com/ruvnet/claude-flow)
11
11
 
12
12
  <p align="center">
13
- <a href="data/npm-downloads.latest.json"><img src="docs/assets/readme/badges/downloads.svg?v=mobile-readable-3" width="300" alt="Ecosystem npm downloads: 12.52M over 12 months through October 4, 2026"></a>
13
+ <a href="docs/harness-origins.md"><img src="https://raw.githubusercontent.com/ruvnet/ruflo/e69e4b95aa08d65ea0f7715489bbf8e62a83a0d2/docs/assets/readme/badges/first-commit.svg" width="300" height="76" alt="Claude Flow lineage: first commit June 2, 2025 by rUv; inspect the evidence"></a>
14
+ <a href="data/npm-downloads.latest.json"><img src="https://raw.githubusercontent.com/ruvnet/ruflo/275aff65a198af758923669acff36f03d1f41623/docs/assets/readme/badges/downloads.svg" width="300" height="76" alt="Illustrative download counter starts at 98,000,000 and adds varied amounts each second in a 30 second loop. Verified dataset: 97,022,120 download events."></a>
14
15
  <br>
15
- <a href="https://github.com/ruvnet/claude-flow"><img src="docs/assets/readme/badges/claude.svg?v=mobile-readable-3" width="300" alt="Claude Code"></a>
16
- <a href="https://www.npmjs.com/package/@claude-flow/codex"><img src="docs/assets/readme/badges/codex.svg?v=mobile-readable-3" width="300" alt="Codex Plugin"></a>
16
+ <a href="https://github.com/ruvnet/claude-flow"><img src="https://raw.githubusercontent.com/ruvnet/ruflo/d1e80400bdefe01d3b13e70395f88fffab76bb18/docs/assets/readme/badges/claude.svg" width="300" height="76" alt="Claude Code plugin"></a>
17
+ <a href="https://www.npmjs.com/package/@claude-flow/codex"><img src="https://raw.githubusercontent.com/ruvnet/ruflo/d1e80400bdefe01d3b13e70395f88fffab76bb18/docs/assets/readme/badges/codex.svg" width="300" height="76" alt="Codex plugin"></a>
17
18
  </p>
18
19
 
19
20
  </div>
@@ -109,6 +110,14 @@ User → Ruflo (CLI/MCP) → Router → Swarm → Agents → Memory → LLM prov
109
110
 
110
111
  > **New to Ruflo?** You don't need to learn 314 MCP tools or 26 CLI commands. After `init`, just use Claude Code normally — the hooks system automatically routes tasks, learns from successful patterns, and coordinates agents in the background.
111
112
 
113
+ ### An origin you can inspect
114
+
115
+ [![Claude Flow to Ruflo: founding idea on June 2, 2025, orchestration source on June 10, and the continuing Ruflo lineage.](docs/assets/readme/harness-origins.svg)](docs/harness-origins.md)
116
+
117
+ **Built by [Reuven Cohen (rUv)](https://github.com/ruvnet), originally as Claude Flow.** The [first commit, June 2, 2025](https://github.com/ruvnet/ruflo/commit/7c1f9402d70e7af56c01a879073cbac92564dc4e), describes a code orchestration layer for recursive agent cycles: Claude writes, edits, tests and improves. The [June 10 source snapshot](https://github.com/ruvnet/ruflo/tree/dba545f012ea92a2375a26332bbf3797495e93c9) adds orchestration, task scheduling, memory and terminal modules.
118
+
119
+ A harness is the workbench around the model: it organizes tasks, carries context between steps and helps check results. **This establishes Ruflo's lineage, not a universal first across every Claude Code tool.** [See the illustrated explainer, exact commits and earlier ecosystem context](docs/harness-origins.md).
120
+
112
121
  <details>
113
122
  <summary><strong>📖 Background — where the name comes from</strong></summary>
114
123
 
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "generation": 7,
4
- "generatedAt": "2026-10-17T22:34:44.747Z",
5
- "gitSha": "e4fb889e",
4
+ "generatedAt": "2026-10-19T23:34:04.095Z",
5
+ "gitSha": "7be330bc",
6
6
  "catalog": {
7
7
  "agents": 176,
8
8
  "tools": 423,
@@ -2333,6 +2333,19 @@ const workerListCommand = {
2333
2333
  }
2334
2334
  }
2335
2335
  };
2336
+ // Render the status hooks_worker-dispatch actually returned. Only a status that
2337
+ // means work finished may read as "completed" (#4196).
2338
+ function dispatchStatusLabel(status) {
2339
+ switch (status) {
2340
+ case 'dispatched': return output.highlight('dispatched (background)');
2341
+ case 'queued': return output.highlight('queued (daemon)');
2342
+ case 'no-daemon': return output.warning('no-daemon (not run)');
2343
+ case 'mcp-only': return output.warning('mcp-only (not queued)');
2344
+ case 'synthetic-completed': return output.warning('recorded (no work executed)');
2345
+ case 'completed': return output.success('completed');
2346
+ default: return output.dim(String(status));
2347
+ }
2348
+ }
2336
2349
  const workerDispatchCommand = {
2337
2350
  name: 'dispatch',
2338
2351
  description: 'Dispatch a background worker for analysis/optimization',
@@ -2386,9 +2399,15 @@ const workerDispatchCommand = {
2386
2399
  { field: 'Description', value: result.config.description },
2387
2400
  { field: 'Est. Duration', value: result.config.estimatedDuration },
2388
2401
  { field: 'Capabilities', value: result.config.capabilities.join(', ') },
2389
- { field: 'Status', value: result.status === 'dispatched' ? output.highlight('dispatched (background)') : output.success('completed') },
2402
+ { field: 'Status', value: dispatchStatusLabel(result.status) },
2390
2403
  ],
2391
2404
  });
2405
+ // The tool says why it could not run the worker (e.g. no daemon); show it
2406
+ // rather than letting a recorded-only dispatch read as finished work (#4196).
2407
+ if (result.note) {
2408
+ output.writeln();
2409
+ output.printWarning(result.note);
2410
+ }
2392
2411
  if (background) {
2393
2412
  output.writeln();
2394
2413
  output.writeln(output.dim(`Check status: claude-flow hooks worker status --id ${result.workerId}`));
@@ -659,7 +659,9 @@ const searchCommand = {
659
659
  });
660
660
  return {
661
661
  results: r.results.map(e => ({
662
- id: e.id,
662
+ // #2271: searchEntries() ids are truncated to 12 chars and collide
663
+ // for entries written within ~2.8h; SmartRetrieval dedupes on id.
664
+ id: JSON.stringify([e.namespace, e.key]),
663
665
  key: e.key,
664
666
  content: e.content,
665
667
  score: e.score,
@@ -1592,6 +1594,13 @@ const exportCommand = {
1592
1594
  namespace: ctx.flags.namespace,
1593
1595
  includeVectors: ctx.flags.includeVectors ?? true,
1594
1596
  });
1597
+ // memory_export reports failure as { error } rather than throwing; without
1598
+ // this check the command printed "Exported to undefined" and then crashed
1599
+ // reading result.exported.entries (#4204).
1600
+ if (!result || result.error || !result.exported) {
1601
+ output.printError(`Export error: ${result?.error ?? 'memory_export returned no export summary'}`);
1602
+ return { success: false, exitCode: 1 };
1603
+ }
1595
1604
  output.printSuccess(`Exported to ${result.outputPath}`);
1596
1605
  output.printList([
1597
1606
  `Entries: ${result.exported.entries}`,
@@ -1657,6 +1666,21 @@ const importCommand = {
1657
1666
  namespace: ctx.flags.namespace,
1658
1667
  dbPath: resolveDbPath(ctx.flags.path),
1659
1668
  });
1669
+ // memory_import reports a bad file as { error } rather than throwing, and
1670
+ // a partial write as { success: false, error, imported }. Without this the
1671
+ // command printed "Imported from undefined" and crashed on result.imported,
1672
+ // or reported success for an import that dropped entries.
1673
+ const failure = result?.error;
1674
+ if (!result || failure || result.success === false || !result.imported) {
1675
+ output.printError(`Import error: ${failure ?? 'memory_import returned no import summary'}`);
1676
+ if (result?.imported) {
1677
+ output.printList([
1678
+ `Entries imported: ${result.imported.entries}`,
1679
+ `Failed: ${result.failed ?? 'unknown'}`,
1680
+ ]);
1681
+ }
1682
+ return { success: false, exitCode: 1 };
1683
+ }
1660
1684
  output.printSuccess(`Imported from ${result.inputPath}`);
1661
1685
  output.printList([
1662
1686
  `Entries: ${result.imported.entries}`,
@@ -111,6 +111,12 @@ const runCommand = {
111
111
  dryRun,
112
112
  },
113
113
  });
114
+ const runError = toolError(result);
115
+ if (runError || !result.metrics) {
116
+ spinner.fail('Workflow failed');
117
+ output.printError(`Workflow error: ${runError ?? 'workflow_run returned no workflow summary'}`);
118
+ return { success: false, exitCode: 1 };
119
+ }
114
120
  if (dryRun) {
115
121
  spinner.succeed('Workflow validated successfully');
116
122
  }
@@ -277,6 +283,11 @@ const listCommand = {
277
283
  status: status || 'all',
278
284
  limit,
279
285
  });
286
+ const listError = toolError(result);
287
+ if (listError || !Array.isArray(result.workflows)) {
288
+ output.printError(`Failed to list workflows: ${listError ?? 'workflow_list returned no workflows'}`);
289
+ return { success: false, exitCode: 1 };
290
+ }
280
291
  if (ctx.flags.format === 'json') {
281
292
  output.printJson(result);
282
293
  return { success: true, data: result };
@@ -290,13 +301,19 @@ const listCommand = {
290
301
  }
291
302
  output.printTable({
292
303
  columns: [
293
- { key: 'id', header: 'ID', width: 15 },
294
- { key: 'template', header: 'Template', width: 15 },
304
+ { key: 'id', header: 'ID', width: 30 },
305
+ { key: 'name', header: 'Name', width: 24 },
295
306
  { key: 'status', header: 'Status', width: 12, format: formatStageStatus },
296
- { key: 'progress', header: 'Progress', width: 10, align: 'right', format: (v) => `${v}%` },
297
- { key: 'startedAt', header: 'Started', width: 20, format: (v) => new Date(String(v)).toLocaleString() }
307
+ { key: 'steps', header: 'Steps', width: 6, align: 'right' },
308
+ { key: 'createdAt', header: 'Created', width: 20, format: (v) => new Date(String(v)).toLocaleString() }
298
309
  ],
299
- data: result.workflows
310
+ data: result.workflows.map(w => ({
311
+ id: w.workflowId,
312
+ name: w.name,
313
+ status: w.status,
314
+ steps: w.stepCount,
315
+ createdAt: w.createdAt,
316
+ }))
300
317
  });
301
318
  output.writeln();
302
319
  output.printInfo(`Total: ${result.total} workflows`);
@@ -335,30 +352,37 @@ const statusCommand = {
335
352
  try {
336
353
  const result = await callMCPTool('workflow_status', {
337
354
  workflowId,
355
+ verbose: true,
338
356
  });
357
+ const statusError = toolError(result);
358
+ if (statusError) {
359
+ output.printError(`Failed to get status: ${statusError}`);
360
+ return { success: false, exitCode: 1 };
361
+ }
339
362
  if (ctx.flags.format === 'json') {
340
363
  output.printJson(result);
341
364
  return { success: true, data: result };
342
365
  }
366
+ const started = result.startedAt ? Date.parse(result.startedAt) : NaN;
367
+ const ended = result.completedAt ? Date.parse(result.completedAt) : Date.now();
343
368
  output.writeln();
344
369
  output.printBox([
345
- `ID: ${result.id}`,
346
- `Template: ${result.template}`,
370
+ `ID: ${result.workflowId}`,
371
+ `Name: ${result.name}`,
347
372
  `Status: ${formatStageStatus(result.status)}`,
348
- `Progress: ${result.progress}%`,
349
- `Duration: ${(result.metrics.duration / 1000).toFixed(1)}s`,
350
- `Tokens: ${result.metrics.tokensUsed.toLocaleString()}`,
351
- `Agents: ${result.metrics.agentsSpawned}`
373
+ `Progress: ${Math.round(result.progress)}% (${result.completedSteps}/${result.totalSteps} steps)`,
374
+ ...(Number.isNaN(started) ? [] : [`Duration: ${((ended - started) / 1000).toFixed(1)}s`]),
375
+ ...(result.error ? [`Error: ${result.error}`] : []),
352
376
  ].join('\n'), 'Workflow Status');
353
377
  output.writeln();
354
- output.writeln(output.bold('Stage Progress'));
378
+ output.writeln(output.bold('Step Progress'));
355
379
  output.printTable({
356
380
  columns: [
357
- { key: 'name', header: 'Stage', width: 20 },
381
+ { key: 'name', header: 'Step', width: 24 },
382
+ { key: 'type', header: 'Type', width: 10 },
358
383
  { key: 'status', header: 'Status', width: 12, format: formatStageStatus },
359
- { key: 'agents', header: 'Agents', width: 25, format: (v) => Array.isArray(v) ? v.length.toString() : '0' }
360
384
  ],
361
- data: result.stages
385
+ data: result.steps ?? []
362
386
  });
363
387
  return { success: true, data: result };
364
388
  }
@@ -408,6 +432,11 @@ const stopCommand = {
408
432
  workflowId,
409
433
  graceful: !force,
410
434
  });
435
+ const stopError = toolError(result);
436
+ if (stopError || result.stopped !== true) {
437
+ output.printError(`Failed to stop workflow: ${stopError ?? 'the workflow was not stopped'}`);
438
+ return { success: false, exitCode: 1 };
439
+ }
411
440
  output.printSuccess(`Workflow ${workflowId} stopped`);
412
441
  return { success: true, data: result };
413
442
  }
@@ -556,6 +585,17 @@ export const workflowCommand = {
556
585
  }
557
586
  };
558
587
  // Helper functions
588
+ /**
589
+ * The workflow MCP tools report failure as `{ error }` (or `{ success: false, error }`)
590
+ * instead of throwing, so a command that reads the result without checking it prints
591
+ * a false success or crashes on a field the error result does not have.
592
+ */
593
+ function toolError(result) {
594
+ const r = result;
595
+ if (!r)
596
+ return 'the workflow tool returned no result';
597
+ return typeof r.error === 'string' && r.error.length > 0 ? r.error : undefined;
598
+ }
559
599
  function formatStageStatus(status) {
560
600
  const statusStr = String(status);
561
601
  switch (statusStr) {
@@ -18,7 +18,7 @@ import { generatePreCommitHook, generatePostCommitHook, generateSessionManager,
18
18
  import { getInstalledCliVersion, HELPERS_STAMP_FILE } from './helper-refresh.js';
19
19
  import { generateClaudeMd } from './claudemd-generator.js';
20
20
  import { recordMemoryPackagePath } from './memory-package-resolver.js';
21
- import { ensureCommonJsCompanions } from './helper-companions.js';
21
+ import { COMMONJS_COMPANION_HELPERS, ensureCommonJsCompanions } from './helper-companions.js';
22
22
  import { scanSettingsForRisk, formatRiskFindingsAsWarnings } from './settings-risk-scanner.js';
23
23
  import { AUTO_MEMORY_HOOK_TIMEOUT_S, helperHookCommand, helperScopeFor, helperStatusLineCommand, pinToUserHelpers } from './helper-commands.js';
24
24
  /**
@@ -548,10 +548,14 @@ export async function executeUpgrade(targetDir, upgradeSettings = false) {
548
548
  catch { }
549
549
  }
550
550
  }
551
- // #3555: the refreshed hook-handler requires the .cjs companions; older
552
- // installs only have session.js / memory.js, which can't load in ESM.
553
- for (const name of await ensureCommonJsCompanions(path.join(targetDir, '.claude', 'helpers'))) {
554
- result.created.push(`.claude/helpers/${name}`);
551
+ // #4198: refresh existing companions too, so upgraded hook handlers never
552
+ // keep obsolete session/memory logic. Generate from the CLI trust root.
553
+ const helpersDir = path.join(targetDir, '.claude', 'helpers');
554
+ const existingCompanions = new Set(COMMONJS_COMPANION_HELPERS.filter((name) => fs.existsSync(path.join(helpersDir, name))));
555
+ for (const name of await ensureCommonJsCompanions(helpersDir, true)) {
556
+ const changes = existingCompanions.has(name)
557
+ ? result.updated : result.created;
558
+ changes.push(`.claude/helpers/${name}`);
555
559
  }
556
560
  // Stamp the installed version so the startup auto-refresh treats these as
557
561
  // current (no redundant re-copy on the next command).
@@ -1,3 +1,3 @@
1
1
  export declare const COMMONJS_COMPANION_HELPERS: readonly ["session.cjs", "memory.cjs"];
2
- export declare function ensureCommonJsCompanions(helpersDir: string): Promise<string[]>;
2
+ export declare function ensureCommonJsCompanions(helpersDir: string, force?: boolean): Promise<string[]>;
3
3
  //# sourceMappingURL=helper-companions.d.ts.map
@@ -7,13 +7,14 @@
7
7
  * `.js` copies, which the refreshed hook-handler no longer requires, so the
8
8
  * refresh writes any missing `.cjs` companion from the CLI's own compiled
9
9
  * generators (the trust root; no external file to verify). Stale `.js` copies
10
- * are left in place and ignored. Existing `.cjs` files are never overwritten.
10
+ * are left in place and ignored. Existing `.cjs` files are preserved during
11
+ * startup refresh; an explicit init upgrade replaces them from the generators.
11
12
  */
12
13
  import * as fs from 'fs';
13
14
  import * as path from 'path';
14
15
  export const COMMONJS_COMPANION_HELPERS = ['session.cjs', 'memory.cjs'];
15
- export async function ensureCommonJsCompanions(helpersDir) {
16
- const missing = COMMONJS_COMPANION_HELPERS.filter((name) => !fs.existsSync(path.join(helpersDir, name)));
16
+ export async function ensureCommonJsCompanions(helpersDir, force = false) {
17
+ const missing = COMMONJS_COMPANION_HELPERS.filter((name) => force || !fs.existsSync(path.join(helpersDir, name)));
17
18
  if (missing.length === 0)
18
19
  return [];
19
20
  const gen = await import('./helpers-generator.js');
@@ -4175,12 +4175,12 @@ export const hooksIntelligenceLearn = {
4175
4175
  // Intelligence attention hook
4176
4176
  export const hooksIntelligenceAttention = {
4177
4177
  name: 'hooks_intelligence_attention',
4178
- description: 'Compute attention-weighted similarity using MoE/Flash/Hyperbolic Use when native Bash hooks (via Claude Code\'s settings.json) are wrong because you need Ruflo-side state — pattern persistence, neural training signals, model-routing learning, cost tracking, audit chain. For one-off shell commands, plain Bash hooks are fine.',
4178
+ description: 'Compute attention-weighted similarity using MoE/Flash. Use when native Bash hooks (via Claude Code\'s settings.json) are wrong because you need Ruflo-side state — pattern persistence, neural training signals, model-routing learning, cost tracking, audit chain. For one-off shell commands, plain Bash hooks are fine.',
4179
4179
  inputSchema: {
4180
4180
  type: 'object',
4181
4181
  properties: {
4182
4182
  query: { type: 'string', description: 'Query for attention computation' },
4183
- mode: { type: 'string', description: 'Attention mode (flash, moe, hyperbolic)' },
4183
+ mode: { type: 'string', description: 'Attention mode (flash, moe)' },
4184
4184
  topK: { type: 'number', description: 'Top-k results' },
4185
4185
  },
4186
4186
  required: ['query'],
@@ -4195,6 +4195,12 @@ export const hooksIntelligenceAttention = {
4195
4195
  if (!v.valid)
4196
4196
  return { success: false, error: v.error };
4197
4197
  }
4198
+ // #3548: only flash and moe have an implementation. Any other mode
4199
+ // (including the previously advertised 'hyperbolic') used to return an
4200
+ // empty stub whose note blamed a missing package; say so instead.
4201
+ if (mode !== 'flash' && mode !== 'moe') {
4202
+ return { success: false, error: `Attention mode "${mode}" is not implemented; supported modes: flash, moe` };
4203
+ }
4198
4204
  let implementation = 'placeholder';
4199
4205
  let embeddingSource = 'none';
4200
4206
  const results = [];
@@ -4370,7 +4376,7 @@ export const hooksIntelligenceAttention = {
4370
4376
  implementation,
4371
4377
  _embeddingSource: embeddingSource,
4372
4378
  _stub: implementation === 'none',
4373
- _note: implementation === 'none' ? 'No attention backend available. Install @ruvector/attention for real computation.' : undefined,
4379
+ _note: implementation === 'none' ? 'No attention backend available: flash/moe attention from @claude-flow/neural could not be loaded or failed.' : undefined,
4374
4380
  ...(embeddingSource === 'hash-fallback' && implementation !== 'none'
4375
4381
  ? { _embeddingNote: 'Query embeddings are hash-based (not semantic). Install @claude-flow/embeddings for real ONNX embeddings.' }
4376
4382
  : {}),
@@ -227,15 +227,31 @@ function loadLegacyStore() {
227
227
  /**
228
228
  * Mark migration as complete
229
229
  */
230
- function markMigrationComplete() {
230
+ function markMigrationComplete(counts) {
231
231
  const legacyDir = resolve(LEGACY_MEMORY_DIR);
232
232
  if (!existsSync(legacyDir))
233
233
  mkdirSync(legacyDir, { recursive: true });
234
234
  writeFileSync(resolve(join(LEGACY_MEMORY_DIR, MIGRATION_MARKER)), JSON.stringify({
235
235
  migratedAt: new Date().toISOString(),
236
236
  version: '3.0.0',
237
+ ...(counts ?? {}),
237
238
  }), 'utf-8');
238
239
  }
240
+ /**
241
+ * Counts recorded by the migration that wrote the marker, or null when no
242
+ * marker (or an older marker without counts) is present.
243
+ */
244
+ function readMigrationCounts() {
245
+ try {
246
+ const marker = JSON.parse(readFileSync(resolve(join(LEGACY_MEMORY_DIR, MIGRATION_MARKER)), 'utf-8'));
247
+ return typeof marker?.migrated === 'number' && typeof marker?.total === 'number'
248
+ ? { migrated: marker.migrated, total: marker.total }
249
+ : null;
250
+ }
251
+ catch {
252
+ return null;
253
+ }
254
+ }
239
255
  /**
240
256
  * Lazy-load memory initializer functions to avoid circular deps
241
257
  */
@@ -391,7 +407,7 @@ async function ensureInitialized(dbPath) {
391
407
  }
392
408
  }
393
409
  console.error(`[MCP Memory] Migrated ${migrated}/${Object.keys(legacyStore.entries).length} entries`);
394
- markMigrationComplete();
410
+ markMigrationComplete({ migrated, total: Object.keys(legacyStore.entries).length });
395
411
  }
396
412
  }
397
413
  }
@@ -636,7 +652,11 @@ export const memoryTools = [
636
652
  });
637
653
  return {
638
654
  results: r.results.map(e => ({
639
- id: e.id,
655
+ // #2271: searchEntries() truncates ids to 12 chars ("entry_" +
656
+ // the leading digits of Date.now()), so entries written within
657
+ // ~2.8h share one id and RRF fused them into a single result.
658
+ // SmartRetrieval dedupes on id: give it the (namespace, key) tuple.
659
+ id: JSON.stringify([e.namespace, e.key]),
640
660
  key: e.key,
641
661
  content: e.content,
642
662
  score: e.score,
@@ -966,11 +986,17 @@ export const memoryTools = [
966
986
  },
967
987
  handler: async (input) => {
968
988
  const force = input.force;
969
- // Remove migration marker if forcing
989
+ // Remove the migration marker if forcing. hasLegacyStore() checks the marker
990
+ // beside the legacy store, so that is the one force must clear (#4194);
991
+ // the old .swarm marker path is cleared too for compatibility.
970
992
  if (force) {
971
- const markerPath = getMigrationMarkerPath();
972
- if (existsSync(markerPath)) {
973
- unlinkSync(markerPath);
993
+ for (const markerPath of [
994
+ resolve(join(LEGACY_MEMORY_DIR, MIGRATION_MARKER)),
995
+ getMigrationMarkerPath(),
996
+ ]) {
997
+ if (existsSync(markerPath)) {
998
+ unlinkSync(markerPath);
999
+ }
974
1000
  }
975
1001
  }
976
1002
  // Check for legacy data
@@ -982,12 +1008,35 @@ export const memoryTools = [
982
1008
  migrated: 0,
983
1009
  };
984
1010
  }
1011
+ // A marker beside the store means a migration already ran; ensureInitialized
1012
+ // would move nothing, so say so instead of reporting the store's size.
1013
+ if (!hasLegacyStore()) {
1014
+ return {
1015
+ success: true,
1016
+ message: 'Legacy store already migrated (marker present); nothing was moved. Pass force: true to re-run.',
1017
+ migrated: 0,
1018
+ total: Object.keys(legacyStore.entries).length,
1019
+ };
1020
+ }
985
1021
  // Run migration via ensureInitialized
986
1022
  await ensureInitialized();
1023
+ // ensureInitialized swallows errors and records what it moved in the marker.
1024
+ const counts = readMigrationCounts();
1025
+ if (!counts) {
1026
+ return {
1027
+ success: false,
1028
+ message: 'Migration did not complete; no entries were recorded as migrated. See the MCP server log.',
1029
+ migrated: 0,
1030
+ total: Object.keys(legacyStore.entries).length,
1031
+ };
1032
+ }
987
1033
  return {
988
1034
  success: true,
989
- message: 'Migration completed',
990
- migrated: Object.keys(legacyStore.entries).length,
1035
+ message: counts.migrated === counts.total
1036
+ ? 'Migration completed'
1037
+ : `Migration completed with failures: ${counts.migrated} of ${counts.total} entries migrated`,
1038
+ migrated: counts.migrated,
1039
+ total: counts.total,
991
1040
  backend: await describeBackend(),
992
1041
  };
993
1042
  },
@@ -577,7 +577,8 @@ export const workflowTools = [
577
577
  const store = loadWorkflowStore();
578
578
  let workflows = Object.values(store.workflows);
579
579
  // Apply filters
580
- if (input.status) {
580
+ // 'all' is the CLI's explicit "no filter" value (`workflow list` sends it by default).
581
+ if (input.status && input.status !== 'all') {
581
582
  workflows = workflows.filter(w => w.status === input.status);
582
583
  }
583
584
  // Sort by creation date (newest first)
@@ -30,10 +30,12 @@
30
30
  * @module v3/cli/memory/graph-edge-writer
31
31
  */
32
32
  /**
33
- * True iff the memory.db bridge at `customDbPath` (or the default location)
34
- * is currently RFE1-encrypted-at-rest. Callers that receive `null` from
35
- * getBridgeDb()/insertGraphEdge() can use this to avoid recommending the
36
- * destructive `memory init` command for this specific, non-corrupt case.
33
+ * True iff the memory.db bridge at `customDbPath` — or, when omitted, the
34
+ * path `resolveDbPath()` resolves from the `CLAUDE_FLOW_DB_PATH` env binding
35
+ * / memory root default (#4291) — is currently RFE1-encrypted-at-rest.
36
+ * Callers that receive `null` from getBridgeDb()/insertGraphEdge() can use
37
+ * this to avoid recommending the destructive `memory init` command for this
38
+ * specific, non-corrupt case.
37
39
  */
38
40
  export declare function isBridgeDbEncryptedAtRest(customDbPath?: string): boolean;
39
41
  /** Test-only: clear the once-per-path warning dedup set. */
@@ -60,6 +62,16 @@ export declare function releaseBridgeDb(dbPath?: string): boolean;
60
62
  * #2431 fix: better-sqlite3 + WAL mode replaces sql.js + whole-file
61
63
  * writeFileSync. Eliminates the dual-write race that corrupted memory.db
62
64
  * when called alongside the memory bridge's better-sqlite3 writer.
65
+ *
66
+ * #4291 fix: the file is resolved via `resolveDbPath()` — the project's one
67
+ * documented path-priority rule (explicit `customDbPath` argument wins, then
68
+ * the `CLAUDE_FLOW_DB_PATH` env binding, then the plain `memory.db` default
69
+ * under the memory root) — instead of a local `customDbPath ?? <default>`
70
+ * expression that silently ignored `CLAUDE_FLOW_DB_PATH`. A caller that
71
+ * explicitly selected a store via that env var no longer gets answers read
72
+ * from a different, unselected database file. When the resolved file is
73
+ * missing (and `createIfMissing` is false) or encrypted at rest, this
74
+ * refuses (returns null) rather than falling back to any other store.
63
75
  */
64
76
  export declare function getBridgeDb(customDbPath?: string, opts?: {
65
77
  createIfMissing?: boolean;
@@ -33,7 +33,7 @@ import * as path from 'path';
33
33
  import * as fs from 'fs';
34
34
  import * as crypto from 'crypto';
35
35
  import { execFileSync } from 'child_process';
36
- import { getMemoryRoot } from './memory-initializer.js';
36
+ import { resolveDbPath } from './memory-initializer.js';
37
37
  import { encodeEmbedding } from './embedding-quantization.js';
38
38
  import { loadBetterSqlite3 } from './shared-sqlite.js';
39
39
  import { isEncryptedBlob } from '../encryption/vault.js';
@@ -125,13 +125,15 @@ function warnRfe1EncryptedOnce(dbPath) {
125
125
  'See #2889 (https://github.com/ruvnet/ruflo/issues/2889) for details and recovery options.');
126
126
  }
127
127
  /**
128
- * True iff the memory.db bridge at `customDbPath` (or the default location)
129
- * is currently RFE1-encrypted-at-rest. Callers that receive `null` from
130
- * getBridgeDb()/insertGraphEdge() can use this to avoid recommending the
131
- * destructive `memory init` command for this specific, non-corrupt case.
128
+ * True iff the memory.db bridge at `customDbPath` — or, when omitted, the
129
+ * path `resolveDbPath()` resolves from the `CLAUDE_FLOW_DB_PATH` env binding
130
+ * / memory root default (#4291) — is currently RFE1-encrypted-at-rest.
131
+ * Callers that receive `null` from getBridgeDb()/insertGraphEdge() can use
132
+ * this to avoid recommending the destructive `memory init` command for this
133
+ * specific, non-corrupt case.
132
134
  */
133
135
  export function isBridgeDbEncryptedAtRest(customDbPath) {
134
- const dbPath = customDbPath ?? path.join(getMemoryRoot(), 'memory.db');
136
+ const dbPath = resolveDbPath(customDbPath);
135
137
  return fs.existsSync(dbPath) && isRfe1EncryptedAtRest(dbPath);
136
138
  }
137
139
  /** Test-only: clear the once-per-path warning dedup set. */
@@ -222,9 +224,19 @@ export function releaseBridgeDb(dbPath) {
222
224
  * #2431 fix: better-sqlite3 + WAL mode replaces sql.js + whole-file
223
225
  * writeFileSync. Eliminates the dual-write race that corrupted memory.db
224
226
  * when called alongside the memory bridge's better-sqlite3 writer.
227
+ *
228
+ * #4291 fix: the file is resolved via `resolveDbPath()` — the project's one
229
+ * documented path-priority rule (explicit `customDbPath` argument wins, then
230
+ * the `CLAUDE_FLOW_DB_PATH` env binding, then the plain `memory.db` default
231
+ * under the memory root) — instead of a local `customDbPath ?? <default>`
232
+ * expression that silently ignored `CLAUDE_FLOW_DB_PATH`. A caller that
233
+ * explicitly selected a store via that env var no longer gets answers read
234
+ * from a different, unselected database file. When the resolved file is
235
+ * missing (and `createIfMissing` is false) or encrypted at rest, this
236
+ * refuses (returns null) rather than falling back to any other store.
225
237
  */
226
238
  export async function getBridgeDb(customDbPath, opts) {
227
- const dbPath = customDbPath ?? path.join(getMemoryRoot(), 'memory.db');
239
+ const dbPath = resolveDbPath(customDbPath);
228
240
  const createIfMissing = opts?.createIfMissing === true;
229
241
  if (_db && _dbPath === dbPath) {
230
242
  armIdleRelease();
@@ -1077,6 +1077,10 @@ export async function recordTrajectory(steps, verdict) {
1077
1077
  }
1078
1078
  bumpStat('trajectoriesRecorded');
1079
1079
  globalStats.lastAdaptation = Date.now();
1080
+ // #4283: the bank's own save is debounced, and the one-shot bins exit as
1081
+ // soon as the command resolves, so write the patterns now. Otherwise
1082
+ // stats.json counts patterns that never reach patterns.json.
1083
+ reasoningBank?.flushToDisk();
1080
1084
  savePersistedStats();
1081
1085
  return true;
1082
1086
  }