@claude-flow/cli 3.47.1 → 3.48.0

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 (66) hide show
  1. package/.claude/helpers/auto-memory-hook.mjs +6 -2
  2. package/.claude/helpers/helpers.manifest.json +3 -3
  3. package/.claude/helpers/learning-service.mjs +4 -2
  4. package/.claude/helpers/metrics-db.mjs +4 -2
  5. package/bin/cli.js +4 -1
  6. package/catalog-manifest.json +2 -2
  7. package/dist/src/commands/analyze.js +2 -2
  8. package/dist/src/commands/config.js +5 -7
  9. package/dist/src/commands/hooks.js +15 -5
  10. package/dist/src/commands/memory.js +89 -27
  11. package/dist/src/commands/metaharness.js +1 -0
  12. package/dist/src/commands/security.js +113 -55
  13. package/dist/src/commands/session.js +6 -6
  14. package/dist/src/commands/task.js +5 -4
  15. package/dist/src/init/helpers-generator.js +4 -2
  16. package/dist/src/mcp-server.js +10 -3
  17. package/dist/src/mcp-tools/agentdb-tools.js +29 -62
  18. package/dist/src/mcp-tools/hooks-tools.d.ts +0 -4
  19. package/dist/src/mcp-tools/hooks-tools.js +139 -37
  20. package/dist/src/mcp-tools/memory-tools.js +89 -35
  21. package/dist/src/mcp-tools/policy-enforcer.d.ts +8 -1
  22. package/dist/src/mcp-tools/policy-enforcer.js +80 -8
  23. package/dist/src/mcp-tools/session-tools.js +34 -8
  24. package/dist/src/memory/feedback-patterns.d.ts +13 -0
  25. package/dist/src/memory/feedback-patterns.js +14 -0
  26. package/dist/src/memory/live-memory-row.d.ts +3 -0
  27. package/dist/src/memory/live-memory-row.js +5 -0
  28. package/dist/src/memory/memory-bridge.d.ts +11 -20
  29. package/dist/src/memory/memory-bridge.js +348 -134
  30. package/dist/src/memory/memory-initializer.d.ts +9 -1
  31. package/dist/src/memory/memory-initializer.js +41 -31
  32. package/dist/src/ruvector/graph-analyzer.js +4 -1
  33. package/dist/src/services/config-file-manager.js +30 -8
  34. package/dist/src/services/memory-backup.d.ts +1 -1
  35. package/dist/src/services/memory-backup.js +67 -27
  36. package/dist/src/services/worker-daemon.d.ts +2 -2
  37. package/dist/src/services/worker-daemon.js +23 -14
  38. package/node_modules/@claude-flow/codex/package.json +1 -1
  39. package/node_modules/@claude-flow/mcp/dist/tool-registry.d.ts.map +1 -1
  40. package/node_modules/@claude-flow/mcp/dist/tool-registry.js +11 -3
  41. package/node_modules/@claude-flow/mcp/dist/tool-registry.js.map +1 -1
  42. package/node_modules/@claude-flow/mcp/package.json +6 -3
  43. package/node_modules/@claude-flow/plugin-agent-federation/package.json +3 -3
  44. package/node_modules/@claude-flow/security/package.json +2 -3
  45. package/package.json +9 -9
  46. package/plugins/ruflo-metaharness/scripts/_cli-args.mjs +37 -0
  47. package/plugins/ruflo-metaharness/scripts/audit-list.mjs +8 -0
  48. package/plugins/ruflo-metaharness/scripts/audit-trend.mjs +12 -0
  49. package/plugins/ruflo-metaharness/scripts/bench-parse-mcp-scan.mjs +8 -0
  50. package/plugins/ruflo-metaharness/scripts/bench-recordpair-overhead.mjs +8 -0
  51. package/plugins/ruflo-metaharness/scripts/bench-similarity.mjs +8 -0
  52. package/plugins/ruflo-metaharness/scripts/bench.mjs +10 -0
  53. package/plugins/ruflo-metaharness/scripts/drift-from-history.mjs +13 -0
  54. package/plugins/ruflo-metaharness/scripts/evolve.mjs +27 -0
  55. package/plugins/ruflo-metaharness/scripts/genome.mjs +8 -0
  56. package/plugins/ruflo-metaharness/scripts/gepa.mjs +12 -0
  57. package/plugins/ruflo-metaharness/scripts/learn.mjs +13 -0
  58. package/plugins/ruflo-metaharness/scripts/mcp-scan.mjs +8 -0
  59. package/plugins/ruflo-metaharness/scripts/mint.mjs +11 -0
  60. package/plugins/ruflo-metaharness/scripts/oia-audit.mjs +9 -0
  61. package/plugins/ruflo-metaharness/scripts/router-parallel-analyze.mjs +8 -0
  62. package/plugins/ruflo-metaharness/scripts/score.mjs +8 -0
  63. package/plugins/ruflo-metaharness/scripts/security-bench.mjs +11 -0
  64. package/plugins/ruflo-metaharness/scripts/similarity.mjs +12 -0
  65. package/plugins/ruflo-metaharness/scripts/test-cli-help.mjs +72 -0
  66. package/plugins/ruflo-metaharness/scripts/threat-model.mjs +8 -0
@@ -12,12 +12,16 @@
12
12
  */
13
13
 
14
14
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
15
- import { join, dirname } from 'path';
15
+ import { join, dirname, resolve } from 'path';
16
16
  import { fileURLToPath } from 'url';
17
17
 
18
18
  const __filename = fileURLToPath(import.meta.url);
19
19
  const __dirname = dirname(__filename);
20
- const PROJECT_ROOT = join(__dirname, '../..');
20
+ // Home-level helpers can serve a different project. Keep the memory writer on
21
+ // the same project root as intelligence.cjs, which reads CLAUDE_PROJECT_DIR.
22
+ const PROJECT_ROOT = process.env.CLAUDE_PROJECT_DIR
23
+ ? resolve(process.env.CLAUDE_PROJECT_DIR)
24
+ : join(__dirname, '../..');
21
25
  const DATA_DIR = join(PROJECT_ROOT, '.claude-flow', 'data');
22
26
  const STORE_PATH = join(DATA_DIR, 'auto-memory-store.json');
23
27
 
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "manifest": {
3
- "version": "3.47.1",
3
+ "version": "3.48.0",
4
4
  "files": {
5
- "auto-memory-hook.mjs": "85fe05c757421c52137c0bc8545a0896bab6b4714538c2a11d1d0835bfcc8c1c",
5
+ "auto-memory-hook.mjs": "c8d218526a3c34034c2a69de23f507f0eb0f70d59c16426a8edc849e6ac22529",
6
6
  "hook-handler.cjs": "209d9fafe10e17d1be0866727f6f9cf9ac66f9a0793f1c793a4f58319e8e4583",
7
7
  "intelligence.cjs": "30e42ed7ec4ca5a94ac54fdb1330d2d47ac5f3fdeeef207753574723a9e77b5c",
8
8
  "statusline.cjs": "4a48353b4f1566fa4379b00fd0321b6676a22b6cc91fbbd8380ac5183d619468",
9
9
  "router.js": "b6998397e7883191b62229ccdc66fb87f1ccca1d5b2039ecb8c1e686d1ad81f8"
10
10
  }
11
11
  },
12
- "signature": "HRKTz2ZUQJg/tmC/+MkrltmjtQT7hPeFmn3ejc719DC3vfKwYlpFb42h8/JrOTqass4rrUQFy96hB40gQNQwBA==",
12
+ "signature": "Y5fY+vJvnledNFp6edS2V1gxJ9P7llOQuq860/gCMvxr9DAzrlDuuKMuGgVP/Tb/25FStYm9+WyBVuOTDos0BQ==",
13
13
  "algorithm": "ed25519"
14
14
  }
@@ -19,14 +19,16 @@
19
19
 
20
20
  import { createRequire } from 'module';
21
21
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
22
- import { join, dirname } from 'path';
22
+ import { join, dirname, resolve } from 'path';
23
23
  import { fileURLToPath } from 'url';
24
24
  import { execSync, spawn } from 'child_process';
25
25
  import Database from 'better-sqlite3';
26
26
 
27
27
  const __filename = fileURLToPath(import.meta.url);
28
28
  const __dirname = dirname(__filename);
29
- const PROJECT_ROOT = join(__dirname, '../..');
29
+ const PROJECT_ROOT = process.env.CLAUDE_PROJECT_DIR
30
+ ? resolve(process.env.CLAUDE_PROJECT_DIR)
31
+ : join(__dirname, '../..');
30
32
  const DATA_DIR = join(PROJECT_ROOT, '.claude-flow/learning');
31
33
  const DB_PATH = join(DATA_DIR, 'patterns.db');
32
34
  const METRICS_PATH = join(DATA_DIR, 'learning-metrics.json');
@@ -7,12 +7,14 @@
7
7
 
8
8
  import initSqlJs from 'sql.js';
9
9
  import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, statSync, openSync, writeSync, fsyncSync, closeSync, renameSync, rmSync } from 'fs';
10
- import { dirname, join, basename } from 'path';
10
+ import { dirname, join, basename, resolve } from 'path';
11
11
  import { fileURLToPath } from 'url';
12
12
  import { execSync } from 'child_process';
13
13
 
14
14
  const __dirname = dirname(fileURLToPath(import.meta.url));
15
- const PROJECT_ROOT = join(__dirname, '../..');
15
+ const PROJECT_ROOT = process.env.CLAUDE_PROJECT_DIR
16
+ ? resolve(process.env.CLAUDE_PROJECT_DIR)
17
+ : join(__dirname, '../..');
16
18
  const V3_DIR = join(PROJECT_ROOT, 'v3');
17
19
  const DB_PATH = join(PROJECT_ROOT, '.claude-flow', 'metrics.db');
18
20
 
package/bin/cli.js CHANGED
@@ -302,10 +302,13 @@ if (isMCPMode) {
302
302
 
303
303
  try {
304
304
  const result = await callMCPTool(toolName, toolParams, { sessionId });
305
+ // Failed task outcomes are data; an explicit handler error is a tool failure.
306
+ const isError = result !== null && typeof result === 'object'
307
+ && typeof result.error === 'string' && result.error.trim().length > 0;
305
308
  return {
306
309
  jsonrpc: '2.0',
307
310
  id: message.id,
308
- result: { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] },
311
+ result: { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }], isError },
309
312
  };
310
313
  } catch (error) {
311
314
  return {
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "generation": 6,
4
- "generatedAt": "2026-09-28T15:44:58.000Z",
5
- "gitSha": "bbbd7241",
4
+ "generatedAt": "2026-09-28T23:12:33.113Z",
5
+ "gitSha": "bd42d873",
6
6
  "catalog": {
7
7
  "agents": 167,
8
8
  "tools": 418,
@@ -1480,7 +1480,7 @@ const boundariesCommand = {
1480
1480
  output.writeln();
1481
1481
  for (const cycle of result.circularDependencies.slice(0, 5)) {
1482
1482
  const severityColor = cycle.severity === 'high' ? output.error : cycle.severity === 'medium' ? output.warning : output.dim;
1483
- output.writeln(`${severityColor(`[${cycle.severity.toUpperCase()}]`)} ${cycle.cycle.join(' -> ')}`);
1483
+ output.writeln(`${severityColor.call(output, `[${cycle.severity.toUpperCase()}]`)} ${cycle.cycle.join(' -> ')}`);
1484
1484
  output.writeln(output.dim(` ${cycle.suggestion}`));
1485
1485
  output.writeln();
1486
1486
  }
@@ -1905,7 +1905,7 @@ const circularCommand = {
1905
1905
  if (items.length === 0)
1906
1906
  continue;
1907
1907
  const color = severity === 'high' ? output.error : severity === 'medium' ? output.warning : output.dim;
1908
- output.writeln(color(output.bold(`${severity.toUpperCase()} SEVERITY (${items.length})`)));
1908
+ output.writeln(color.call(output, output.bold(`${severity.toUpperCase()} SEVERITY (${items.length})`)));
1909
1909
  output.writeln();
1910
1910
  for (const cycle of items.slice(0, 5)) {
1911
1911
  output.writeln(` ${cycle.cycle.join(' -> ')}`);
@@ -124,15 +124,13 @@ const setCommand = {
124
124
  name: 'key',
125
125
  short: 'k',
126
126
  description: 'Configuration key',
127
- type: 'string',
128
- required: true
127
+ type: 'string'
129
128
  },
130
129
  {
131
130
  name: 'value',
132
131
  short: 'v',
133
132
  description: 'Configuration value',
134
- type: 'string',
135
- required: true
133
+ type: 'string'
136
134
  }
137
135
  ],
138
136
  examples: [
@@ -140,14 +138,14 @@ const setCommand = {
140
138
  { command: 'claude-flow config set -k memory.backend -v agentdb', description: 'Set memory backend' }
141
139
  ],
142
140
  action: async (ctx) => {
143
- const key = ctx.flags.key || ctx.args[0];
144
- const value = ctx.flags.value || ctx.args[1];
141
+ const key = (ctx.flags.key ?? ctx.args[0]);
142
+ const value = ctx.flags.value ?? ctx.args[1];
145
143
  if (!key || value === undefined) {
146
144
  output.printError('Both key and value are required');
147
145
  return { success: false, exitCode: 1 };
148
146
  }
149
147
  try {
150
- const parsedValue = parseConfigValue(value);
148
+ const parsedValue = parseConfigValue(String(value));
151
149
  configManager.set(ctx.cwd, key, parsedValue);
152
150
  output.writeln(`Set ${key} = ${value}`);
153
151
  return { success: true };
@@ -1,7 +1,4 @@
1
- /**
2
- * V3 CLI Hooks Command
3
- * Self-learning hooks system for intelligent workflow automation
4
- */
1
+ import { validateFeedbackPatterns } from '../memory/feedback-patterns.js';
5
2
  import { output } from '../output.js';
6
3
  import { confirm } from '../prompt.js';
7
4
  import { callMCPTool, MCPClientError } from '../mcp-client.js';
@@ -572,6 +569,11 @@ const postCommandCommand = {
572
569
  short: 'd',
573
570
  description: 'Execution duration in milliseconds',
574
571
  type: 'number'
572
+ },
573
+ {
574
+ name: 'ttl',
575
+ description: 'Command history lifetime in seconds (default: 30 days)',
576
+ type: 'number'
575
577
  }
576
578
  ],
577
579
  examples: [
@@ -594,11 +596,16 @@ const postCommandCommand = {
594
596
  success,
595
597
  exitCode: ctx.flags.exitCode || 0,
596
598
  duration: ctx.flags.duration,
599
+ ttl: ctx.flags.ttl,
597
600
  timestamp: Date.now(),
598
601
  });
599
602
  if (ctx.flags.format === 'json') {
600
603
  output.printJson(result);
601
- return { success: true, data: result };
604
+ return { success: result.recorded, exitCode: result.recorded ? 0 : 1, data: result };
605
+ }
606
+ if (!result.recorded) {
607
+ output.printError('Command outcome could not be recorded');
608
+ return { success: false, exitCode: 1, data: result };
602
609
  }
603
610
  output.writeln();
604
611
  output.printSuccess('Command outcome recorded');
@@ -1699,6 +1706,7 @@ const postTaskCommand = {
1699
1706
  name: 'post-task',
1700
1707
  description: 'Record task completion for learning',
1701
1708
  options: [
1709
+ { name: 'patterns', description: 'JSON array of learned pattern strings for feedback and skill creation', type: 'string' },
1702
1710
  {
1703
1711
  name: 'task-id',
1704
1712
  short: 'i',
@@ -1784,8 +1792,10 @@ const postTaskCommand = {
1784
1792
  const success = ctx.flags.success !== undefined ? ctx.flags.success : true;
1785
1793
  output.printInfo(`Recording outcome for task: ${output.highlight(taskId)}`);
1786
1794
  try {
1795
+ const patterns = validateFeedbackPatterns(ctx.flags.patterns === undefined ? undefined : JSON.parse(ctx.flags.patterns));
1787
1796
  const result = await callMCPTool('hooks_post-task', {
1788
1797
  taskId,
1798
+ patterns,
1789
1799
  success,
1790
1800
  quality: ctx.flags.quality,
1791
1801
  agent: ctx.flags.agent,
@@ -9,6 +9,8 @@ import { distillCommand } from './memory-distill.js';
9
9
  import { backupCommand } from './memory-backup.js';
10
10
  import { countSiblingStoreRows } from '../memory/sibling-store.js';
11
11
  import { resolveDbPath } from '../memory/memory-initializer.js';
12
+ import { existsSync } from 'node:fs';
13
+ import { siblingAgentDbPath } from '../memory/memory-bridge.js';
12
14
  /**
13
15
  * #3228: a miss in one store is not a miss in the memory.
14
16
  *
@@ -26,6 +28,24 @@ async function warnIfSiblingHasRows(pathFlag) {
26
28
  `That store is written by the MCP/AgentDB path; read it with --path ${unread.path}.`);
27
29
  }
28
30
  }
31
+ /** Default CLI writes may be mirrored into AgentDB; remove both copies. */
32
+ function removalDbTargets(pathFlag) {
33
+ const primary = resolveDbPath(pathFlag);
34
+ // An explicit file selection is a single-store operation, as documented by
35
+ // --path. Do not widen a caller's requested destructive scope.
36
+ if (pathFlag || process.env.CLAUDE_FLOW_DB_PATH)
37
+ return [{ dbPath: primary }];
38
+ const sibling = siblingAgentDbPath(primary);
39
+ // The AgentDB mirror is consumed by native SQLite and must stay plaintext.
40
+ // Pass this per operation, including preview migrations; never toggle the
41
+ // process encryption setting while asynchronous operations are in flight.
42
+ const targets = [
43
+ { dbPath: primary },
44
+ ...(sibling ? [{ dbPath: sibling, encryptWrites: false }] : []),
45
+ ].filter(target => existsSync(target.dbPath));
46
+ // Preserve the existing missing-database error when neither store exists.
47
+ return targets.length ? targets : [{ dbPath: primary }];
48
+ }
29
49
  // Memory backends
30
50
  const BACKENDS = [
31
51
  { value: 'agentdb', label: 'AgentDB', hint: 'Vector database with HNSW indexing (150x-12,500x faster)' },
@@ -820,7 +840,7 @@ function formatRelativeTime(isoDate) {
820
840
  const deleteCommand = {
821
841
  name: 'delete',
822
842
  aliases: ['rm'],
823
- description: 'Delete memory entry',
843
+ description: 'Delete a memory entry from the default store and its AgentDB mirror (use --path to select one store)',
824
844
  options: [
825
845
  {
826
846
  name: 'key',
@@ -870,16 +890,24 @@ const deleteCommand = {
870
890
  }
871
891
  // Use sql.js directly for consistent data access (Issue #980)
872
892
  try {
873
- const { deleteEntry, resolveDbPath: _rdbDelete } = await import('../memory/memory-initializer.js');
874
- const dbPathDelete = _rdbDelete(ctx.flags.path);
875
- const result = await deleteEntry({ key, namespace, dbPath: dbPathDelete });
876
- if (!result.success) {
877
- output.printError(result.error || 'Failed to delete');
878
- return { success: false, exitCode: 1 };
893
+ const { deleteEntry } = await import('../memory/memory-initializer.js');
894
+ const paths = removalDbTargets(ctx.flags.path);
895
+ const stores = [];
896
+ for (const target of paths) {
897
+ const { dbPath } = target;
898
+ const entry = await deleteEntry({ key, namespace, ...target });
899
+ if (!entry.success)
900
+ throw new Error(`${dbPath}: ${entry.error || 'Failed to delete'}`);
901
+ stores.push({ dbPath, ...entry });
879
902
  }
903
+ const result = {
904
+ success: true, key, namespace, stores,
905
+ deleted: stores.some(entry => entry.deleted),
906
+ remainingEntries: stores.reduce((total, entry) => total + entry.remainingEntries, 0),
907
+ };
880
908
  if (result.deleted) {
881
909
  output.printSuccess(`Deleted "${key}" from namespace "${namespace}"`);
882
- output.printInfo(`Remaining entries: ${result.remainingEntries}`);
910
+ output.printInfo(`Remaining entries across ${paths.length} store(s): ${result.remainingEntries}`);
883
911
  }
884
912
  else {
885
913
  output.printWarning(`Key not found: "${key}" in namespace "${namespace}"`);
@@ -901,7 +929,7 @@ const deleteCommand = {
901
929
  // either interactive confirmation or --force.
902
930
  const purgeCommand = {
903
931
  name: 'purge',
904
- description: 'Permanently delete every entry in a namespace (hard delete — not the soft delete/tombstone that `memory delete` uses)',
932
+ description: 'Permanently delete a namespace from the default store and its AgentDB mirror (use --path to select one store)',
905
933
  options: [
906
934
  {
907
935
  name: 'namespace',
@@ -939,17 +967,22 @@ const purgeCommand = {
939
967
  return { success: false, exitCode: 1 };
940
968
  }
941
969
  try {
942
- const { listEntries, purgeNamespace, resolveDbPath: _rdbPurge } = await import('../memory/memory-initializer.js');
943
- const resolvedDbPath = _rdbPurge(dbPath);
944
- const preview = await listEntries({ namespace, limit: 1, dbPath: resolvedDbPath });
945
- const previewCount = preview.total ?? preview.entries?.length ?? 0;
970
+ const { listEntries, purgeNamespace } = await import('../memory/memory-initializer.js');
971
+ const paths = removalDbTargets(dbPath);
972
+ let previewCount = 0;
973
+ for (const target of paths) {
974
+ const preview = await listEntries({ namespace, limit: 1, ...target });
975
+ if (!preview.success)
976
+ throw new Error(`${target.dbPath}: ${preview.error || 'Failed to read purge target'}`);
977
+ previewCount += preview.total ?? preview.entries?.length ?? 0;
978
+ }
946
979
  if (dryRun) {
947
- output.printInfo(`Would permanently delete ${previewCount} entr${previewCount === 1 ? 'y' : 'ies'} from namespace "${namespace}" (dry run — nothing deleted)`);
980
+ output.printInfo(`Would permanently delete ${previewCount} entr${previewCount === 1 ? 'y' : 'ies'} from namespace "${namespace}" across ${paths.length} store(s) (dry run — nothing deleted)`);
948
981
  return { success: true, data: { namespace, wouldDelete: previewCount } };
949
982
  }
950
983
  if (!force && ctx.interactive) {
951
984
  const confirmed = await confirm({
952
- message: `Permanently delete ${previewCount} entr${previewCount === 1 ? 'y' : 'ies'} from namespace "${namespace}"? This is a hard delete — not reversible with \`memory delete\`'s soft-undo.`,
985
+ message: `Permanently delete ${previewCount} entr${previewCount === 1 ? 'y' : 'ies'} from namespace "${namespace}" across ${paths.length} store(s)? This is a hard delete — not reversible with \`memory delete\`'s soft-undo.`,
953
986
  default: false
954
987
  });
955
988
  if (!confirmed) {
@@ -961,13 +994,20 @@ const purgeCommand = {
961
994
  output.printError(`Refusing to purge namespace "${namespace}" without --force in non-interactive mode`);
962
995
  return { success: false, exitCode: 1 };
963
996
  }
964
- const result = await purgeNamespace({ namespace, dbPath: resolvedDbPath });
965
- if (!result.success) {
966
- output.printError(result.error || 'Failed to purge');
967
- return { success: false, exitCode: 1 };
997
+ const stores = [];
998
+ for (const target of paths) {
999
+ const purged = await purgeNamespace({ namespace, ...target });
1000
+ if (!purged.success)
1001
+ throw new Error(`${target.dbPath}: ${purged.error || 'Failed to purge'}`);
1002
+ stores.push({ dbPath: target.dbPath, ...purged });
968
1003
  }
1004
+ const result = {
1005
+ success: true, stores,
1006
+ deletedCount: stores.reduce((total, store) => total + store.deletedCount, 0),
1007
+ remainingEntries: stores.reduce((total, store) => total + store.remainingEntries, 0),
1008
+ };
969
1009
  output.printSuccess(`Purged ${result.deletedCount} entr${result.deletedCount === 1 ? 'y' : 'ies'} from namespace "${namespace}"`);
970
- output.printInfo(`Remaining entries (all namespaces): ${result.remainingEntries}`);
1010
+ output.printInfo(`Remaining entries (all namespaces, ${paths.length} store(s)): ${result.remainingEntries}`);
971
1011
  return { success: true, data: result };
972
1012
  }
973
1013
  catch (error) {
@@ -984,8 +1024,16 @@ const statsCommand = {
984
1024
  action: async (ctx) => {
985
1025
  // Call MCP memory/stats tool for real statistics
986
1026
  try {
987
- const statsResult = await callMCPTool('memory_stats', {});
1027
+ const dbPath = resolveDbPath(ctx.flags.path);
1028
+ const statsResult = await callMCPTool('memory_stats', { dbPath });
1029
+ if (statsResult.available === false) {
1030
+ output.printError(`Failed to get stats: ${statsResult.error || 'Store unavailable'}`);
1031
+ return { success: false, exitCode: 1 };
1032
+ }
1033
+ const unreadStore = await countSiblingStoreRows(dbPath);
988
1034
  const stats = {
1035
+ ...(unreadStore ? { unreadStore } : {}),
1036
+ ...(statsResult.truncated ? { truncated: true } : {}),
989
1037
  backend: statsResult.backend,
990
1038
  entries: {
991
1039
  total: statsResult.totalEntries,
@@ -993,7 +1041,7 @@ const statsCommand = {
993
1041
  text: statsResult.totalEntries
994
1042
  },
995
1043
  storage: {
996
- total: statsResult.totalSize,
1044
+ total: statsResult.totalSize ?? 'unknown',
997
1045
  location: statsResult.location
998
1046
  },
999
1047
  version: statsResult.version,
@@ -1004,6 +1052,9 @@ const statsCommand = {
1004
1052
  output.printJson(stats);
1005
1053
  return { success: true, data: stats };
1006
1054
  }
1055
+ if (unreadStore) {
1056
+ output.printWarning(`${unreadStore.rows} entries are in ${unreadStore.path} and were not read here. Read them with --path ${unreadStore.path}.`);
1057
+ }
1007
1058
  output.writeln();
1008
1059
  output.writeln(output.bold('Memory Statistics'));
1009
1060
  output.writeln();
@@ -1245,19 +1296,27 @@ const cleanupCommand = {
1245
1296
  { command: 'claude-flow memory cleanup --expired-only', description: 'Clean expired entries' }
1246
1297
  ],
1247
1298
  action: async (ctx) => {
1248
- const dryRun = ctx.flags.dryRun;
1249
- const force = ctx.flags.force;
1299
+ const dryRun = ctx.flags.dryRun === true;
1300
+ const force = ctx.flags.force === true;
1301
+ if (ctx.flags.format === 'json' && !dryRun && !force) {
1302
+ output.printJson({ success: false, error: 'Use --dry-run to preview or --force to authorize cleanup with --format json.' });
1303
+ return { success: false, exitCode: 1 };
1304
+ }
1250
1305
  if (dryRun) {
1251
1306
  output.writeln(output.warning('DRY RUN - No changes will be made'));
1252
1307
  }
1253
1308
  output.printInfo('Analyzing memory for cleanup...');
1254
1309
  try {
1255
- const result = await callMCPTool('memory_cleanup', {
1256
- dryRun,
1310
+ const selectors = {
1257
1311
  olderThan: ctx.flags.olderThan,
1258
1312
  expiredOnly: ctx.flags.expiredOnly,
1259
1313
  lowQualityThreshold: ctx.flags.lowQuality,
1260
1314
  namespace: ctx.flags.namespace,
1315
+ };
1316
+ // Preview before asking: dryRun:false executes deletion inside the tool.
1317
+ let result = await callMCPTool('memory_cleanup', {
1318
+ ...selectors,
1319
+ dryRun: dryRun || !force,
1261
1320
  });
1262
1321
  if (ctx.flags.format === 'json') {
1263
1322
  output.printJson(result);
@@ -1286,6 +1345,7 @@ const cleanupCommand = {
1286
1345
  output.printInfo('Cleanup cancelled');
1287
1346
  return { success: true, data: result };
1288
1347
  }
1348
+ result = await callMCPTool('memory_cleanup', { ...selectors, dryRun: false });
1289
1349
  }
1290
1350
  if (!dryRun) {
1291
1351
  output.writeln();
@@ -1525,7 +1585,8 @@ const importCommand = {
1525
1585
  short: 'n',
1526
1586
  description: 'Import into specific namespace',
1527
1587
  type: 'string'
1528
- }
1588
+ },
1589
+ DB_PATH_OPTION
1529
1590
  ],
1530
1591
  examples: [
1531
1592
  { command: 'claude-flow memory import -i ./backup.json', description: 'Import from file' },
@@ -1543,6 +1604,7 @@ const importCommand = {
1543
1604
  inputPath,
1544
1605
  merge: ctx.flags.merge ?? true,
1545
1606
  namespace: ctx.flags.namespace,
1607
+ dbPath: resolveDbPath(ctx.flags.path),
1546
1608
  });
1547
1609
  output.printSuccess(`Imported from ${result.inputPath}`);
1548
1610
  output.printList([
@@ -279,6 +279,7 @@ export const metaharnessCommand = {
279
279
  'verbose', 'v',
280
280
  'quiet', 'q',
281
281
  'help', 'h',
282
+ 'interactive', 'i', // CLI-only default; plugin scripts are non-interactive
282
283
  ]);
283
284
  // Parser normalizes kebab-case → camelCase (--per-dimension → perDimension).
284
285
  // Plugin scripts expect kebab-case at argv, so we re-kebab here.