@claude-flow/cli 3.7.0-alpha.3 → 3.7.0-alpha.30

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 (227) hide show
  1. package/.claude/helpers/hook-handler.cjs +6 -2
  2. package/.claude/helpers/statusline.cjs +31 -2
  3. package/.claude/helpers/statusline.js +35 -4
  4. package/README.md +43 -39
  5. package/bin/cli.js +15 -2
  6. package/bin/mcp-server.js +1 -1
  7. package/dist/src/commands/agent-wasm.js +2 -2
  8. package/dist/src/commands/agent-wasm.js.map +1 -1
  9. package/dist/src/commands/daemon.d.ts +20 -0
  10. package/dist/src/commands/daemon.d.ts.map +1 -1
  11. package/dist/src/commands/daemon.js +338 -3
  12. package/dist/src/commands/daemon.js.map +1 -1
  13. package/dist/src/commands/doctor.d.ts.map +1 -1
  14. package/dist/src/commands/doctor.js +224 -46
  15. package/dist/src/commands/doctor.js.map +1 -1
  16. package/dist/src/commands/hive-mind.d.ts.map +1 -1
  17. package/dist/src/commands/hive-mind.js +25 -7
  18. package/dist/src/commands/hive-mind.js.map +1 -1
  19. package/dist/src/commands/hooks.d.ts.map +1 -1
  20. package/dist/src/commands/hooks.js +56 -29
  21. package/dist/src/commands/hooks.js.map +1 -1
  22. package/dist/src/commands/memory.d.ts.map +1 -1
  23. package/dist/src/commands/memory.js +90 -3
  24. package/dist/src/commands/memory.js.map +1 -1
  25. package/dist/src/commands/start.js +1 -1
  26. package/dist/src/commands/start.js.map +1 -1
  27. package/dist/src/commands/swarm.js +1 -1
  28. package/dist/src/commands/swarm.js.map +1 -1
  29. package/dist/src/commands/task.d.ts.map +1 -1
  30. package/dist/src/commands/task.js +8 -4
  31. package/dist/src/commands/task.js.map +1 -1
  32. package/dist/src/config-adapter.js +1 -1
  33. package/dist/src/config-adapter.js.map +1 -1
  34. package/dist/src/index.d.ts +5 -1
  35. package/dist/src/index.d.ts.map +1 -1
  36. package/dist/src/index.js +61 -18
  37. package/dist/src/index.js.map +1 -1
  38. package/dist/src/init/executor.d.ts.map +1 -1
  39. package/dist/src/init/executor.js +92 -0
  40. package/dist/src/init/executor.js.map +1 -1
  41. package/dist/src/init/helpers-generator.d.ts.map +1 -1
  42. package/dist/src/init/helpers-generator.js +6 -2
  43. package/dist/src/init/helpers-generator.js.map +1 -1
  44. package/dist/src/init/mcp-generator.js +4 -4
  45. package/dist/src/init/mcp-generator.js.map +1 -1
  46. package/dist/src/init/settings-generator.d.ts.map +1 -1
  47. package/dist/src/init/settings-generator.js +44 -17
  48. package/dist/src/init/settings-generator.js.map +1 -1
  49. package/dist/src/init/statusline-generator.d.ts.map +1 -1
  50. package/dist/src/init/statusline-generator.js +25 -14
  51. package/dist/src/init/statusline-generator.js.map +1 -1
  52. package/dist/src/init/types.d.ts +7 -0
  53. package/dist/src/init/types.d.ts.map +1 -1
  54. package/dist/src/init/types.js.map +1 -1
  55. package/dist/src/mcp-client.d.ts.map +1 -1
  56. package/dist/src/mcp-client.js +12 -0
  57. package/dist/src/mcp-client.js.map +1 -1
  58. package/dist/src/mcp-tools/agent-execute-core.d.ts +3 -2
  59. package/dist/src/mcp-tools/agent-execute-core.d.ts.map +1 -1
  60. package/dist/src/mcp-tools/agent-execute-core.js +16 -9
  61. package/dist/src/mcp-tools/agent-execute-core.js.map +1 -1
  62. package/dist/src/mcp-tools/agent-tools.d.ts.map +1 -1
  63. package/dist/src/mcp-tools/agent-tools.js +88 -11
  64. package/dist/src/mcp-tools/agent-tools.js.map +1 -1
  65. package/dist/src/mcp-tools/agentdb-tools.d.ts +3 -0
  66. package/dist/src/mcp-tools/agentdb-tools.d.ts.map +1 -1
  67. package/dist/src/mcp-tools/agentdb-tools.js +206 -21
  68. package/dist/src/mcp-tools/agentdb-tools.js.map +1 -1
  69. package/dist/src/mcp-tools/analyze-tools.js +6 -6
  70. package/dist/src/mcp-tools/analyze-tools.js.map +1 -1
  71. package/dist/src/mcp-tools/autopilot-tools.js +10 -10
  72. package/dist/src/mcp-tools/autopilot-tools.js.map +1 -1
  73. package/dist/src/mcp-tools/browser-session-tools.js +5 -5
  74. package/dist/src/mcp-tools/browser-session-tools.js.map +1 -1
  75. package/dist/src/mcp-tools/browser-tools.js +23 -23
  76. package/dist/src/mcp-tools/browser-tools.js.map +1 -1
  77. package/dist/src/mcp-tools/claims-tools.js +12 -12
  78. package/dist/src/mcp-tools/claims-tools.js.map +1 -1
  79. package/dist/src/mcp-tools/config-tools.js +6 -6
  80. package/dist/src/mcp-tools/config-tools.js.map +1 -1
  81. package/dist/src/mcp-tools/coordination-tools.js +7 -7
  82. package/dist/src/mcp-tools/coordination-tools.js.map +1 -1
  83. package/dist/src/mcp-tools/daa-tools.js +8 -8
  84. package/dist/src/mcp-tools/daa-tools.js.map +1 -1
  85. package/dist/src/mcp-tools/embeddings-tools.js +10 -10
  86. package/dist/src/mcp-tools/embeddings-tools.js.map +1 -1
  87. package/dist/src/mcp-tools/github-tools.js +5 -5
  88. package/dist/src/mcp-tools/github-tools.js.map +1 -1
  89. package/dist/src/mcp-tools/guidance-tools.js +21 -21
  90. package/dist/src/mcp-tools/guidance-tools.js.map +1 -1
  91. package/dist/src/mcp-tools/hive-mind-tools.d.ts.map +1 -1
  92. package/dist/src/mcp-tools/hive-mind-tools.js +53 -9
  93. package/dist/src/mcp-tools/hive-mind-tools.js.map +1 -1
  94. package/dist/src/mcp-tools/hooks-tools.d.ts +2 -0
  95. package/dist/src/mcp-tools/hooks-tools.d.ts.map +1 -1
  96. package/dist/src/mcp-tools/hooks-tools.js +183 -48
  97. package/dist/src/mcp-tools/hooks-tools.js.map +1 -1
  98. package/dist/src/mcp-tools/managed-agent-tools.d.ts +22 -0
  99. package/dist/src/mcp-tools/managed-agent-tools.d.ts.map +1 -0
  100. package/dist/src/mcp-tools/managed-agent-tools.js +357 -0
  101. package/dist/src/mcp-tools/managed-agent-tools.js.map +1 -0
  102. package/dist/src/mcp-tools/memory-tools.d.ts.map +1 -1
  103. package/dist/src/mcp-tools/memory-tools.js +400 -63
  104. package/dist/src/mcp-tools/memory-tools.js.map +1 -1
  105. package/dist/src/mcp-tools/neural-tools.d.ts.map +1 -1
  106. package/dist/src/mcp-tools/neural-tools.js +20 -7
  107. package/dist/src/mcp-tools/neural-tools.js.map +1 -1
  108. package/dist/src/mcp-tools/performance-tools.js +6 -6
  109. package/dist/src/mcp-tools/performance-tools.js.map +1 -1
  110. package/dist/src/mcp-tools/progress-tools.js +4 -4
  111. package/dist/src/mcp-tools/progress-tools.js.map +1 -1
  112. package/dist/src/mcp-tools/ruvllm-tools.js +10 -10
  113. package/dist/src/mcp-tools/ruvllm-tools.js.map +1 -1
  114. package/dist/src/mcp-tools/security-tools.d.ts.map +1 -1
  115. package/dist/src/mcp-tools/security-tools.js +34 -9
  116. package/dist/src/mcp-tools/security-tools.js.map +1 -1
  117. package/dist/src/mcp-tools/session-tools.d.ts.map +1 -1
  118. package/dist/src/mcp-tools/session-tools.js +130 -6
  119. package/dist/src/mcp-tools/session-tools.js.map +1 -1
  120. package/dist/src/mcp-tools/swarm-tools.d.ts.map +1 -1
  121. package/dist/src/mcp-tools/swarm-tools.js +76 -7
  122. package/dist/src/mcp-tools/swarm-tools.js.map +1 -1
  123. package/dist/src/mcp-tools/system-tools.d.ts.map +1 -1
  124. package/dist/src/mcp-tools/system-tools.js +91 -18
  125. package/dist/src/mcp-tools/system-tools.js.map +1 -1
  126. package/dist/src/mcp-tools/task-tools.d.ts.map +1 -1
  127. package/dist/src/mcp-tools/task-tools.js +55 -7
  128. package/dist/src/mcp-tools/task-tools.js.map +1 -1
  129. package/dist/src/mcp-tools/terminal-tools.js +5 -5
  130. package/dist/src/mcp-tools/terminal-tools.js.map +1 -1
  131. package/dist/src/mcp-tools/transfer-tools.js +11 -11
  132. package/dist/src/mcp-tools/transfer-tools.js.map +1 -1
  133. package/dist/src/mcp-tools/wasm-agent-tools.js +11 -11
  134. package/dist/src/mcp-tools/wasm-agent-tools.js.map +1 -1
  135. package/dist/src/mcp-tools/workflow-tools.d.ts.map +1 -1
  136. package/dist/src/mcp-tools/workflow-tools.js +118 -10
  137. package/dist/src/mcp-tools/workflow-tools.js.map +1 -1
  138. package/dist/src/memory/intelligence.d.ts.map +1 -1
  139. package/dist/src/memory/intelligence.js +28 -3
  140. package/dist/src/memory/intelligence.js.map +1 -1
  141. package/dist/src/memory/memory-bridge.d.ts +69 -0
  142. package/dist/src/memory/memory-bridge.d.ts.map +1 -1
  143. package/dist/src/memory/memory-bridge.js +282 -5
  144. package/dist/src/memory/memory-bridge.js.map +1 -1
  145. package/dist/src/memory/memory-initializer.d.ts +8 -0
  146. package/dist/src/memory/memory-initializer.d.ts.map +1 -1
  147. package/dist/src/memory/memory-initializer.js +80 -16
  148. package/dist/src/memory/memory-initializer.js.map +1 -1
  149. package/dist/src/memory/neural-package-bridge.d.ts +48 -0
  150. package/dist/src/memory/neural-package-bridge.d.ts.map +1 -0
  151. package/dist/src/memory/neural-package-bridge.js +87 -0
  152. package/dist/src/memory/neural-package-bridge.js.map +1 -0
  153. package/dist/src/memory/sona-optimizer.d.ts.map +1 -1
  154. package/dist/src/memory/sona-optimizer.js +3 -0
  155. package/dist/src/memory/sona-optimizer.js.map +1 -1
  156. package/dist/src/parser.d.ts +9 -0
  157. package/dist/src/parser.d.ts.map +1 -1
  158. package/dist/src/parser.js +11 -0
  159. package/dist/src/parser.js.map +1 -1
  160. package/dist/src/plugins/store/discovery.d.ts +6 -3
  161. package/dist/src/plugins/store/discovery.d.ts.map +1 -1
  162. package/dist/src/plugins/store/discovery.js +11 -8
  163. package/dist/src/plugins/store/discovery.js.map +1 -1
  164. package/dist/src/ruvector/agent-wasm.d.ts.map +1 -1
  165. package/dist/src/ruvector/agent-wasm.js +4 -1
  166. package/dist/src/ruvector/agent-wasm.js.map +1 -1
  167. package/dist/src/ruvector/coverage-tools.js +6 -6
  168. package/dist/src/ruvector/coverage-tools.js.map +1 -1
  169. package/dist/src/ruvector/index.d.ts +0 -2
  170. package/dist/src/ruvector/index.d.ts.map +1 -1
  171. package/dist/src/ruvector/index.js +8 -2
  172. package/dist/src/ruvector/index.js.map +1 -1
  173. package/dist/src/ruvector/model-router.d.ts +22 -1
  174. package/dist/src/ruvector/model-router.d.ts.map +1 -1
  175. package/dist/src/ruvector/model-router.js +125 -5
  176. package/dist/src/ruvector/model-router.js.map +1 -1
  177. package/dist/src/services/headless-worker-executor.d.ts +6 -0
  178. package/dist/src/services/headless-worker-executor.d.ts.map +1 -1
  179. package/dist/src/services/headless-worker-executor.js +37 -3
  180. package/dist/src/services/headless-worker-executor.js.map +1 -1
  181. package/dist/src/services/worker-daemon.d.ts +80 -2
  182. package/dist/src/services/worker-daemon.d.ts.map +1 -1
  183. package/dist/src/services/worker-daemon.js +372 -11
  184. package/dist/src/services/worker-daemon.js.map +1 -1
  185. package/dist/tsconfig.tsbuildinfo +1 -1
  186. package/package.json +7 -5
  187. package/.claude/skills/agentdb-advanced/SKILL.md +0 -550
  188. package/.claude/skills/agentdb-learning/SKILL.md +0 -545
  189. package/.claude/skills/agentdb-memory-patterns/SKILL.md +0 -339
  190. package/.claude/skills/agentdb-optimization/SKILL.md +0 -509
  191. package/.claude/skills/agentdb-vector-search/SKILL.md +0 -339
  192. package/.claude/skills/agentic-jujutsu/SKILL.md +0 -645
  193. package/.claude/skills/aidefence-scan.md +0 -151
  194. package/.claude/skills/aidefence.yaml +0 -297
  195. package/.claude/skills/browser/SKILL.md +0 -204
  196. package/.claude/skills/flow-nexus-neural/SKILL.md +0 -738
  197. package/.claude/skills/flow-nexus-platform/SKILL.md +0 -1157
  198. package/.claude/skills/flow-nexus-swarm/SKILL.md +0 -610
  199. package/.claude/skills/github-code-review/SKILL.md +0 -1140
  200. package/.claude/skills/github-multi-repo/SKILL.md +0 -874
  201. package/.claude/skills/github-project-management/SKILL.md +0 -1277
  202. package/.claude/skills/github-release-management/SKILL.md +0 -1081
  203. package/.claude/skills/github-workflow-automation/SKILL.md +0 -1065
  204. package/.claude/skills/hive-mind-advanced/SKILL.md +0 -712
  205. package/.claude/skills/hooks-automation/SKILL.md +0 -1201
  206. package/.claude/skills/pair-programming/SKILL.md +0 -1202
  207. package/.claude/skills/performance-analysis/SKILL.md +0 -563
  208. package/.claude/skills/reasoningbank-agentdb/SKILL.md +0 -446
  209. package/.claude/skills/reasoningbank-intelligence/SKILL.md +0 -201
  210. package/.claude/skills/secure-review.md +0 -181
  211. package/.claude/skills/skill-builder/SKILL.md +0 -910
  212. package/.claude/skills/sparc-methodology/SKILL.md +0 -1115
  213. package/.claude/skills/stream-chain/SKILL.md +0 -563
  214. package/.claude/skills/swarm-advanced/SKILL.md +0 -973
  215. package/.claude/skills/swarm-orchestration/SKILL.md +0 -179
  216. package/.claude/skills/v3-cli-modernization/SKILL.md +0 -872
  217. package/.claude/skills/v3-core-implementation/SKILL.md +0 -797
  218. package/.claude/skills/v3-ddd-architecture/SKILL.md +0 -442
  219. package/.claude/skills/v3-integration-deep/SKILL.md +0 -241
  220. package/.claude/skills/v3-mcp-optimization/SKILL.md +0 -777
  221. package/.claude/skills/v3-memory-unification/SKILL.md +0 -174
  222. package/.claude/skills/v3-performance-optimization/SKILL.md +0 -390
  223. package/.claude/skills/v3-security-overhaul/SKILL.md +0 -82
  224. package/.claude/skills/v3-swarm-coordination/SKILL.md +0 -340
  225. package/.claude/skills/verification-quality/SKILL.md +0 -649
  226. package/.claude/skills/worker-benchmarks/skill.md +0 -135
  227. package/.claude/skills/worker-integration/skill.md +0 -154
@@ -12,6 +12,7 @@
12
12
  import { existsSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from 'fs';
13
13
  import { homedir } from 'os';
14
14
  import { join, resolve } from 'path';
15
+ import { createHash } from 'crypto';
15
16
  import { validateIdentifier } from './validate-input.js';
16
17
  // #1604: Align with memory-initializer.ts — single source of truth is .swarm/memory.db
17
18
  const MEMORY_DIR = '.swarm';
@@ -37,6 +38,11 @@ function ensureMemoryDir() {
37
38
  const MAX_KEY_LENGTH = 1024;
38
39
  const MAX_VALUE_SIZE = 1024 * 1024; // 1MB
39
40
  const MAX_QUERY_LENGTH = 4096;
41
+ // #1425 — single source of truth for the dangerous-character set rejected by
42
+ // validateMemoryInput. Imported by sanitizeMemoryKey so write-side sanitization
43
+ // and read-side rejection can never drift apart (the symmetry bug behind #1884).
44
+ const DANGEROUS_KEY_CHARS = /[;&|`$(){}[\]<>!#\\\0]|\.\.[/\\]/g;
45
+ const DANGEROUS_KEY_PATTERN = /[;&|`$(){}[\]<>!#\\\0]|\.\.[/\\]/;
40
46
  function validateMemoryInput(key, value, query, namespace) {
41
47
  if (key && key.length > MAX_KEY_LENGTH) {
42
48
  throw new Error(`Key exceeds maximum length of ${MAX_KEY_LENGTH} characters`);
@@ -48,14 +54,73 @@ function validateMemoryInput(key, value, query, namespace) {
48
54
  throw new Error(`Query exceeds maximum length of ${MAX_QUERY_LENGTH} characters`);
49
55
  }
50
56
  // Reject path traversal and shell metacharacters in keys/namespaces (#1425)
51
- const dangerousPattern = /[;&|`$(){}[\]<>!#\\\0]|\.\.[/\\]/;
52
- if (key && dangerousPattern.test(key)) {
57
+ if (key && DANGEROUS_KEY_PATTERN.test(key)) {
53
58
  throw new Error('Key contains disallowed characters');
54
59
  }
55
- if (namespace && dangerousPattern.test(namespace)) {
60
+ if (namespace && DANGEROUS_KEY_PATTERN.test(namespace)) {
56
61
  throw new Error('Namespace contains disallowed characters');
57
62
  }
58
63
  }
64
+ // #1884 — sanitize a key produced from arbitrary input (markdown headings,
65
+ // frontmatter names, file names) so it survives validateMemoryInput on the
66
+ // read/delete path. Replaces every dangerous char with `_`. Truncates to
67
+ // MAX_KEY_LENGTH so the bound check in validateMemoryInput also passes.
68
+ // Keep this in sync with DANGEROUS_KEY_PATTERN — they share DANGEROUS_KEY_CHARS.
69
+ function sanitizeMemoryKey(key) {
70
+ const safe = key.replace(DANGEROUS_KEY_CHARS, '_');
71
+ return safe.length > MAX_KEY_LENGTH ? safe.slice(0, MAX_KEY_LENGTH) : safe;
72
+ }
73
+ // #1883 — resolve the Claude-Code project memory directory for the *current*
74
+ // project. Claude Code hashes the project path differently per host OS, and
75
+ // our previous logic only POSIX-slash-replaced cwd, which breaks for:
76
+ // - WSL bridges where cwd is `/mnt/<drive>/...` but Claude Code is on Windows
77
+ // - paths containing spaces (Claude Code replaces spaces with dashes)
78
+ // - any leading slash on POSIX (Claude Code strips it)
79
+ // Strategy: try several candidate hashes and return the first one with a
80
+ // memory dir that exists. An explicit `projectPathOverride` short-circuits
81
+ // the heuristics for callers that know the canonical project path.
82
+ function resolveProjectMemoryDir(claudeProjectsDir, projectPathOverride) {
83
+ const candidates = new Set();
84
+ const sources = [];
85
+ if (projectPathOverride && projectPathOverride.length > 0) {
86
+ sources.push(projectPathOverride);
87
+ }
88
+ else {
89
+ sources.push(process.cwd());
90
+ }
91
+ for (const source of sources) {
92
+ // Candidate 1: legacy POSIX hash — what shipped before #1883
93
+ candidates.add(source.replace(/\//g, '-'));
94
+ // Candidate 2: WSL `/mnt/<drive>/...` translated to Claude-Code Windows hash
95
+ // e.g. `/mnt/c/Users/x/Project Name` → `C--Users-x-Project-Name`
96
+ const wsl = source.match(/^\/mnt\/([a-z])(\/.*)?$/i);
97
+ if (wsl) {
98
+ const drive = wsl[1].toUpperCase();
99
+ const rest = (wsl[2] ?? '').replace(/\//g, '-').replace(/ /g, '-');
100
+ candidates.add(`${drive}-${rest}`);
101
+ }
102
+ // Candidate 3: POSIX hash with leading dash stripped (Claude Code on macOS/Linux)
103
+ const stripped = source.replace(/\//g, '-').replace(/^-+/, '');
104
+ candidates.add(stripped);
105
+ // Candidate 4: spaces replaced with dashes (Claude Code's space rule)
106
+ candidates.add(source.replace(/\//g, '-').replace(/ /g, '-'));
107
+ // Candidate 5 (#1939): native Win32 path on a Win32 Claude Code install.
108
+ // `C:\Users\tobia\OneDrive\Desktop\Claude Stuff` →
109
+ // `C--Users-tobia-OneDrive-Desktop-Claude-Stuff`. Claude Code's on-disk
110
+ // slug replaces drive-colon AND backslashes AND whitespace with `-`.
111
+ // The earlier candidates only handled forward slashes, so a Win32+Win32
112
+ // setup never matched.
113
+ if (/^[A-Za-z]:[\\/]/.test(source)) {
114
+ candidates.add(source.replace(/[:\\/]/g, '-').replace(/\s+/g, '-'));
115
+ }
116
+ }
117
+ for (const projectHash of candidates) {
118
+ const memDir = join(claudeProjectsDir, projectHash, 'memory');
119
+ if (existsSync(memDir))
120
+ return { memDir, projectHash };
121
+ }
122
+ return null;
123
+ }
59
124
  /**
60
125
  * Check if legacy JSON store exists in old .claude-flow/memory/ location
61
126
  */
@@ -310,57 +375,81 @@ export const memoryTools = [
310
375
  validateMemoryInput(undefined, undefined, query);
311
376
  const startTime = performance.now();
312
377
  try {
378
+ // #1846: feature-detect smartSearch on the resolved memory package.
379
+ // The export landed in @claude-flow/memory@>3.0.0-alpha.14 — older
380
+ // installs pin to a build that exposes search/store/retrieve but
381
+ // not smartSearch. Throwing `is not a function` is hostile; instead
382
+ // detect at runtime and gracefully fall through to plain semantic
383
+ // search with an explicit fallback note.
384
+ let smartFallbackReason;
313
385
  if (input.smart) {
314
- // SmartRetrieval pipeline (ADR-090)
315
- const { smartSearch } = await import('@claude-flow/memory');
316
- // Adapt searchEntries to the SearchFn interface
317
- const rawSearch = async (req) => {
318
- const r = await searchEntries({
319
- query: req.query,
320
- namespace: req.namespace || namespace,
321
- limit: req.limit || limit * 3,
322
- threshold: req.threshold ?? threshold,
323
- });
324
- return {
325
- results: r.results.map(e => ({
326
- id: e.id,
327
- key: e.key,
328
- content: e.content,
329
- score: e.score,
330
- namespace: e.namespace,
331
- })),
386
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
387
+ let memMod;
388
+ try {
389
+ memMod = await import('@claude-flow/memory');
390
+ }
391
+ catch (err) {
392
+ smartFallbackReason = `@claude-flow/memory failed to load: ${err.message}`;
393
+ }
394
+ const smartSearch = memMod && typeof memMod.smartSearch === 'function'
395
+ ? memMod.smartSearch
396
+ : undefined;
397
+ if (smartSearch) {
398
+ // SmartRetrieval pipeline (ADR-090)
399
+ const rawSearch = async (req) => {
400
+ const r = await searchEntries({
401
+ query: req.query,
402
+ namespace: req.namespace || namespace,
403
+ limit: req.limit || limit * 3,
404
+ threshold: req.threshold ?? threshold,
405
+ });
406
+ return {
407
+ results: r.results.map(e => ({
408
+ id: e.id,
409
+ key: e.key,
410
+ content: e.content,
411
+ score: e.score,
412
+ namespace: e.namespace,
413
+ })),
414
+ };
332
415
  };
333
- };
334
- const smartResult = await smartSearch(rawSearch, {
335
- query,
336
- namespace,
337
- limit,
338
- threshold,
339
- });
340
- const duration = performance.now() - startTime;
341
- const results = smartResult.results.map(r => {
342
- let value = r.content;
343
- try {
344
- value = JSON.parse(r.content);
345
- }
346
- catch { /* keep as string */ }
416
+ const smartResult = await smartSearch(rawSearch, {
417
+ query,
418
+ namespace,
419
+ limit,
420
+ threshold,
421
+ });
422
+ const duration = performance.now() - startTime;
423
+ const results = smartResult.results.map((r) => {
424
+ let value = r.content;
425
+ try {
426
+ value = JSON.parse(r.content);
427
+ }
428
+ catch { /* keep as string */ }
429
+ return {
430
+ key: r.key,
431
+ namespace: r.namespace,
432
+ value,
433
+ similarity: r.score,
434
+ };
435
+ });
347
436
  return {
348
- key: r.key,
349
- namespace: r.namespace,
350
- value,
351
- similarity: r.score,
437
+ query,
438
+ results,
439
+ total: results.length,
440
+ searchTime: `${duration.toFixed(2)}ms`,
441
+ backend: 'SmartRetrieval (RRF + MMR + Recency)',
442
+ stats: smartResult.stats,
352
443
  };
353
- });
354
- return {
355
- query,
356
- results,
357
- total: results.length,
358
- searchTime: `${duration.toFixed(2)}ms`,
359
- backend: 'SmartRetrieval (RRF + MMR + Recency)',
360
- stats: smartResult.stats,
361
- };
444
+ }
445
+ // smart=true but smartSearch unavailable on installed package.
446
+ // Fall through to plain search with an explicit warning.
447
+ smartFallbackReason = smartFallbackReason
448
+ ?? 'smartSearch is not exported by the installed @claude-flow/memory build (likely a release lag — see #1846). Falling back to standard semantic search.';
362
449
  }
363
- // Original non-smart path (unchanged)
450
+ // Original non-smart path (unchanged) — also reached when smart was
451
+ // requested but unavailable. We attach `smartFallback` to the
452
+ // response so callers can see the degradation explicitly.
364
453
  const result = await searchEntries({
365
454
  query,
366
455
  namespace,
@@ -390,6 +479,7 @@ export const memoryTools = [
390
479
  total: results.length,
391
480
  searchTime: `${duration.toFixed(2)}ms`,
392
481
  backend: 'HNSW + sql.js',
482
+ ...(smartFallbackReason ? { smartFallback: smartFallbackReason } : {}),
393
483
  };
394
484
  }
395
485
  catch (error) {
@@ -501,7 +591,7 @@ export const memoryTools = [
501
591
  },
502
592
  {
503
593
  name: 'memory_stats',
504
- description: 'Get memory storage statistics including HNSW index status',
594
+ description: 'Get memory storage statistics including HNSW index status Use when native Read/Write is wrong because you need (a) cross-session retrieval by semantic similarity (vector embeddings) not by file path, (b) namespacing across projects without managing directory layout, or (c) the .swarm/memory.db audit trail. For one-shot file I/O, native Read/Write is fine.',
505
595
  category: 'memory',
506
596
  inputSchema: {
507
597
  type: 'object',
@@ -548,7 +638,7 @@ export const memoryTools = [
548
638
  },
549
639
  {
550
640
  name: 'memory_migrate',
551
- description: 'Manually trigger migration from legacy JSON store to sql.js',
641
+ description: 'Manually trigger migration from legacy JSON store to sql.js Use when native Read/Write is wrong because you need (a) cross-session retrieval by semantic similarity (vector embeddings) not by file path, (b) namespacing across projects without managing directory layout, or (c) the .swarm/memory.db audit trail. For one-shot file I/O, native Read/Write is fine.',
552
642
  category: 'memory',
553
643
  inputSchema: {
554
644
  type: 'object',
@@ -587,13 +677,14 @@ export const memoryTools = [
587
677
  // ===== Claude Code Memory Bridge Tools =====
588
678
  {
589
679
  name: 'memory_import_claude',
590
- description: 'Import Claude Code auto-memory files into AgentDB with ONNX vector embeddings. Reads ~/.claude/projects/*/memory/*.md files, parses YAML frontmatter, splits into sections, and stores with 384-dim embeddings for semantic search. Use allProjects=true to import from ALL Claude projects.',
680
+ description: 'Import Claude Code auto-memory files into AgentDB with ONNX vector embeddings. Reads ~/.claude/projects/*/memory/*.md files, parses YAML frontmatter, splits into sections, and stores with 384-dim embeddings for semantic search. Use allProjects=true to import from ALL Claude projects. Pass projectPath to override cwd-based detection (#1883 — required when Ruflo runs in WSL but Claude Code is on Windows). Use when native Read/Write is wrong because you need (a) cross-session retrieval by semantic similarity (vector embeddings) not by file path, (b) namespacing across projects without managing directory layout, or (c) the .swarm/memory.db audit trail. For one-shot file I/O, native Read/Write is fine.',
591
681
  category: 'memory',
592
682
  inputSchema: {
593
683
  type: 'object',
594
684
  properties: {
595
685
  allProjects: { type: 'boolean', description: 'Import from all Claude projects (default: current project only)' },
596
686
  namespace: { type: 'string', description: 'Target namespace (default: "claude-memories")' },
687
+ projectPath: { type: 'string', description: '#1883 — explicit project path to hash, used when cwd does not match Claude Code\'s view (e.g. WSL bridge to Windows host). Pass the canonical project root as Claude Code sees it.' },
597
688
  },
598
689
  },
599
690
  handler: async (input) => {
@@ -606,6 +697,7 @@ export const memoryTools = [
606
697
  return { success: false, imported: 0, error: vNs.error };
607
698
  }
608
699
  const allProjects = input.allProjects;
700
+ const projectPathOverride = input.projectPath;
609
701
  const claudeProjectsDir = join(homedir(), '.claude', 'projects');
610
702
  // Find memory files
611
703
  const memoryFiles = [];
@@ -628,14 +720,13 @@ export const memoryTools = [
628
720
  }
629
721
  }
630
722
  else {
631
- // Current project only find by CWD hash
632
- const cwd = process.cwd();
633
- const projectHash = cwd.replace(/\//g, '-');
634
- const memDir = join(claudeProjectsDir, projectHash, 'memory');
635
- if (existsSync(memDir)) {
723
+ // #1883 — current project: try multiple candidate hashes (POSIX, WSL-translated,
724
+ // leading-dash-stripped, space-replaced). Caller can pass projectPath to override.
725
+ const resolved = resolveProjectMemoryDir(claudeProjectsDir, projectPathOverride);
726
+ if (resolved) {
636
727
  try {
637
- for (const file of readdirSync(memDir).filter((f) => f.endsWith('.md'))) {
638
- memoryFiles.push({ path: join(memDir, file), project: projectHash, file });
728
+ for (const file of readdirSync(resolved.memDir).filter((f) => f.endsWith('.md'))) {
729
+ memoryFiles.push({ path: join(resolved.memDir, file), project: resolved.projectHash, file });
639
730
  }
640
731
  }
641
732
  catch { /* scan error */ }
@@ -646,11 +737,28 @@ export const memoryTools = [
646
737
  }
647
738
  let imported = 0;
648
739
  let skipped = 0;
740
+ // #1791.8 — Claude Code's `~/.claude/projects/` accumulates historical
741
+ // project_id directories (truncated forms, sandbox cwds, renamed
742
+ // workspaces) that all contain copies of the same memory files. The
743
+ // previous import indexed each copy under a different `project_id`
744
+ // prefix, producing 5–8x duplication on long-lived homes. Dedupe by
745
+ // file content hash so the same memory is imported once even if it
746
+ // appears under several project directories.
747
+ const seenContentHashes = new Set();
748
+ let duplicatesSkipped = 0;
649
749
  const projects = new Set();
650
750
  for (const memFile of memoryFiles) {
651
751
  projects.add(memFile.project);
652
752
  try {
653
753
  const content = readFileSync(memFile.path, 'utf-8');
754
+ // #1791.8 — Skip if we've already imported this exact content under
755
+ // a different project_id directory.
756
+ const contentHash = createHash('sha256').update(content).digest('hex').slice(0, 16);
757
+ if (seenContentHashes.has(contentHash)) {
758
+ duplicatesSkipped++;
759
+ continue;
760
+ }
761
+ seenContentHashes.add(contentHash);
654
762
  const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
655
763
  let name = memFile.file.replace('.md', '');
656
764
  let body = content;
@@ -664,7 +772,10 @@ export const memoryTools = [
664
772
  // Split into sections for granular search
665
773
  const sections = body.split(/^(?=## )/m).filter(s => s.trim().length > 20);
666
774
  if (sections.length === 0 && body.length > 10) {
667
- await storeEntry({ key: `claude:${memFile.project}:${name}`, value: body.slice(0, 4096), namespace: ns, generateEmbeddingFlag: true });
775
+ // #1884 sanitize key so memory_delete can later remove it. Without
776
+ // this, dangerous chars from frontmatter `name` strand the key.
777
+ const key = sanitizeMemoryKey(`claude:${memFile.project}:${name}`);
778
+ await storeEntry({ key, value: body.slice(0, 4096), namespace: ns, generateEmbeddingFlag: true });
668
779
  imported++;
669
780
  }
670
781
  else {
@@ -674,7 +785,10 @@ export const memoryTools = [
674
785
  const sectionBody = section.replace(/^##\s+.+\n/, '').trim();
675
786
  if (sectionBody.length < 10)
676
787
  continue;
677
- await storeEntry({ key: `claude:${memFile.project}:${name}:${sectionTitle.slice(0, 50)}`, value: sectionBody.slice(0, 4096), namespace: ns, generateEmbeddingFlag: true });
788
+ // #1884 sanitize so any dangerous chars in the heading don't
789
+ // produce keys memory_delete will reject.
790
+ const key = sanitizeMemoryKey(`claude:${memFile.project}:${name}:${sectionTitle.slice(0, 50)}`);
791
+ await storeEntry({ key, value: sectionBody.slice(0, 4096), namespace: ns, generateEmbeddingFlag: true });
678
792
  imported++;
679
793
  }
680
794
  }
@@ -687,6 +801,7 @@ export const memoryTools = [
687
801
  success: true,
688
802
  imported,
689
803
  skipped,
804
+ duplicatesSkipped,
690
805
  files: memoryFiles.length,
691
806
  projects: projects.size,
692
807
  namespace: ns,
@@ -696,7 +811,7 @@ export const memoryTools = [
696
811
  },
697
812
  {
698
813
  name: 'memory_bridge_status',
699
- description: 'Show Claude Code memory bridge status — AgentDB vectors, SONA learning, intelligence patterns, and connection health.',
814
+ description: 'Show Claude Code memory bridge status — AgentDB vectors, SONA learning, intelligence patterns, and connection health. Use when native Read/Write is wrong because you need (a) cross-session retrieval by semantic similarity (vector embeddings) not by file path, (b) namespacing across projects without managing directory layout, or (c) the .swarm/memory.db audit trail. For one-shot file I/O, native Read/Write is fine.',
700
815
  category: 'memory',
701
816
  inputSchema: { type: 'object', properties: {} },
702
817
  handler: async () => {
@@ -752,7 +867,7 @@ export const memoryTools = [
752
867
  },
753
868
  {
754
869
  name: 'memory_search_unified',
755
- description: 'Search across both Claude Code memories and AgentDB entries using semantic vector similarity. Returns merged, deduplicated results from all namespaces.',
870
+ description: 'Search across both Claude Code memories and AgentDB entries using semantic vector similarity. Returns merged, deduplicated results from all namespaces. Use when native Read/Write is wrong because you need (a) cross-session retrieval by semantic similarity (vector embeddings) not by file path, (b) namespacing across projects without managing directory layout, or (c) the .swarm/memory.db audit trail. For one-shot file I/O, native Read/Write is fine.',
756
871
  category: 'memory',
757
872
  inputSchema: {
758
873
  type: 'object',
@@ -814,5 +929,227 @@ export const memoryTools = [
814
929
  };
815
930
  },
816
931
  },
932
+ {
933
+ // #1916: `ruflo status memory` (the detailed view) referenced an
934
+ // unregistered `memory_detailed-stats` tool. memory_stats returns a
935
+ // different shape; this returns what the CLI renders.
936
+ name: 'memory_detailed-stats',
937
+ description: 'Detailed memory-store report — backend, entry count, total bytes, per-namespace counts, and (placeholder) perf metrics. Use when native Read/Glob is wrong because the data lives in .swarm/memory.db, not files, and you want an aggregate health view. For a quick count use memory_stats; for "what is in memory" use memory_list.',
938
+ category: 'memory',
939
+ inputSchema: { type: 'object', properties: {} },
940
+ handler: async () => {
941
+ await ensureInitialized();
942
+ const { listEntries } = await getMemoryFunctions();
943
+ const all = await listEntries({ limit: 100000 });
944
+ const nsCounts = {};
945
+ let bytes = 0;
946
+ for (const e of all.entries) {
947
+ nsCounts[e.namespace] = (nsCounts[e.namespace] || 0) + 1;
948
+ bytes += e.size || 0;
949
+ }
950
+ return {
951
+ backend: 'sql.js + HNSW',
952
+ entries: all.total ?? all.entries.length,
953
+ size: bytes,
954
+ namespaces: Object.entries(nsCounts).map(([name, entries]) => ({ name, entries })),
955
+ performance: { avgSearchTime: 0, avgWriteTime: 0, cacheHitRate: 0, hnswEnabled: true },
956
+ note: 'perf metrics are placeholders; HNSW is always enabled in the sql.js backend',
957
+ };
958
+ },
959
+ },
960
+ {
961
+ // #1916: `ruflo memory cleanup` referenced an unregistered `memory_cleanup`
962
+ // tool. Removes entries whose TTL has expired. Defaults to a dry run —
963
+ // pass dryRun:false to actually delete.
964
+ name: 'memory_cleanup',
965
+ description: 'Prune memory entries whose TTL has expired (dry run by default; pass dryRun:false to delete). Use when native rm is wrong because the entries are rows in .swarm/memory.db, not files. For removing a specific known key use memory_delete. Stale/low-quality pruning is delegated to the agentdb consolidation curator (#1916 follow-up).',
966
+ category: 'memory',
967
+ inputSchema: {
968
+ type: 'object',
969
+ properties: {
970
+ dryRun: { type: 'boolean', description: 'Only report candidates, do not delete (default true)' },
971
+ namespace: { type: 'string', description: 'Limit cleanup to one namespace' },
972
+ },
973
+ },
974
+ handler: async (input) => {
975
+ await ensureInitialized();
976
+ const { listEntries, deleteEntry } = await getMemoryFunctions();
977
+ const dryRun = input.dryRun !== false; // default true
978
+ const namespace = input.namespace ? String(input.namespace) : undefined;
979
+ if (namespace) {
980
+ const v = validateIdentifier(namespace, 'namespace');
981
+ if (!v.valid)
982
+ throw new Error(v.error);
983
+ }
984
+ const all = await listEntries({ limit: 100000, namespace });
985
+ const now = Date.now();
986
+ const expired = all.entries.filter(e => {
987
+ const exp = e.expiresAt;
988
+ if (!exp)
989
+ return false;
990
+ const t = typeof exp === 'number' ? exp : Date.parse(String(exp));
991
+ return Number.isFinite(t) && t < now;
992
+ });
993
+ let freedBytes = 0;
994
+ let deleted = 0;
995
+ if (!dryRun) {
996
+ for (const e of expired) {
997
+ try {
998
+ await deleteEntry({ key: e.key, namespace: e.namespace });
999
+ freedBytes += e.size || 0;
1000
+ deleted++;
1001
+ }
1002
+ catch { /* ignore individual delete errors */ }
1003
+ }
1004
+ }
1005
+ else {
1006
+ freedBytes = expired.reduce((s, e) => s + (e.size || 0), 0);
1007
+ }
1008
+ return {
1009
+ dryRun,
1010
+ candidates: { expired: expired.length, stale: 0, lowQuality: 0, total: expired.length },
1011
+ deleted: { entries: dryRun ? 0 : deleted, vectors: 0, patterns: 0 },
1012
+ freed: { bytes: freedBytes },
1013
+ note: dryRun ? 'dry run — re-run with dryRun:false to delete' : undefined,
1014
+ };
1015
+ },
1016
+ },
1017
+ {
1018
+ // #1916: `ruflo memory compress` referenced an unregistered tool. The
1019
+ // sql.js backend has no on-disk compression; this reports current sizes.
1020
+ name: 'memory_compress',
1021
+ description: 'Report memory-store size breakdown (the sql.js backend has no on-disk compression — entries are already stored compactly; quantized embeddings via RaBitQ are configured elsewhere). Use when native du is wrong because the data is in .swarm/memory.db. For pruning expired entries use memory_cleanup.',
1022
+ category: 'memory',
1023
+ inputSchema: { type: 'object', properties: {} },
1024
+ handler: async () => {
1025
+ await ensureInitialized();
1026
+ const { listEntries } = await getMemoryFunctions();
1027
+ const all = await listEntries({ limit: 100000 });
1028
+ const bytes = all.entries.reduce((s, e) => s + (e.size || 0), 0);
1029
+ const human = `${bytes}B`;
1030
+ const sizes = { totalSize: human, vectorsSize: 'n/a', textSize: human, patternsSize: 'n/a', indexSize: 'n/a' };
1031
+ return {
1032
+ before: sizes,
1033
+ after: sizes,
1034
+ compression: { ratio: 1, savedBytes: 0, method: 'none' },
1035
+ note: 'sql.js backend has no on-disk compression; nothing to compress. (RaBitQ embedding quantization is a separate feature.)',
1036
+ };
1037
+ },
1038
+ },
1039
+ {
1040
+ // #1916: `ruflo memory export -o <file>` referenced an unregistered tool.
1041
+ // Dumps entry metadata (and values when the backend returns them) to JSON.
1042
+ name: 'memory_export',
1043
+ description: 'Export memory entries to a JSON file (keys, namespaces, timestamps, and values when available). Use when native Write is wrong because the data is rows in .swarm/memory.db, not a file you can copy. For ingesting an export elsewhere use memory_import. (CSV output and embedding-vector export are follow-ups.)',
1044
+ category: 'memory',
1045
+ inputSchema: {
1046
+ type: 'object',
1047
+ properties: {
1048
+ outputPath: { type: 'string', description: 'File path to write the JSON export to' },
1049
+ format: { type: 'string', enum: ['json', 'csv'], description: 'Export format (csv falls back to json today)' },
1050
+ namespace: { type: 'string', description: 'Limit export to one namespace' },
1051
+ includeVectors: { type: 'boolean', description: 'Include embedding vectors (advisory — not exported yet)' },
1052
+ },
1053
+ required: ['outputPath'],
1054
+ },
1055
+ handler: async (input) => {
1056
+ await ensureInitialized();
1057
+ const { listEntries } = await getMemoryFunctions();
1058
+ const outputPath = String(input.outputPath ?? '');
1059
+ if (!outputPath)
1060
+ return { error: 'outputPath is required' };
1061
+ const namespace = input.namespace ? String(input.namespace) : undefined;
1062
+ if (namespace) {
1063
+ const v = validateIdentifier(namespace, 'namespace');
1064
+ if (!v.valid)
1065
+ throw new Error(v.error);
1066
+ }
1067
+ const all = await listEntries({ limit: 100000, namespace });
1068
+ const payload = {
1069
+ schema: 'ruflo-memory-export/v1',
1070
+ exportedAt: new Date().toISOString(),
1071
+ namespace: namespace ?? null,
1072
+ count: all.entries.length,
1073
+ entries: all.entries.map(e => ({
1074
+ key: e.key, namespace: e.namespace, value: e.value ?? null,
1075
+ createdAt: e.createdAt, updatedAt: e.updatedAt, accessCount: e.accessCount, hasEmbedding: e.hasEmbedding, size: e.size,
1076
+ })),
1077
+ };
1078
+ try {
1079
+ writeFileSync(outputPath, JSON.stringify(payload, null, 2), 'utf-8');
1080
+ }
1081
+ catch (e) {
1082
+ return { error: `Could not write ${outputPath}: ${e.message}` };
1083
+ }
1084
+ const vectorsWithEmb = all.entries.filter(e => e.hasEmbedding).length;
1085
+ return {
1086
+ outputPath,
1087
+ format: input.format || 'json',
1088
+ exported: { entries: all.entries.length, vectors: vectorsWithEmb, patterns: 0 },
1089
+ fileSize: `${Buffer.byteLength(JSON.stringify(payload))}B`,
1090
+ note: input.format === 'csv' ? 'CSV not implemented yet — wrote JSON' : undefined,
1091
+ };
1092
+ },
1093
+ },
1094
+ {
1095
+ // #1916: `ruflo memory import <file>` referenced an unregistered tool.
1096
+ // Reads a ruflo-memory-export JSON and re-stores each entry.
1097
+ name: 'memory_import',
1098
+ description: 'Import memory entries from a JSON export file (produced by memory_export) into .swarm/memory.db, re-embedding values. Use when native Read is wrong because the data must be re-stored as memory rows (with new embeddings), not just read. For importing Claude Code\'s own memory files use memory_import_claude. Pair with memory_export on the source.',
1099
+ category: 'memory',
1100
+ inputSchema: {
1101
+ type: 'object',
1102
+ properties: {
1103
+ inputPath: { type: 'string', description: 'Path to the JSON export file' },
1104
+ merge: { type: 'boolean', description: 'Merge into existing entries (upsert) vs. fail on conflict (default true)' },
1105
+ namespace: { type: 'string', description: 'Override the namespace for all imported entries' },
1106
+ },
1107
+ required: ['inputPath'],
1108
+ },
1109
+ handler: async (input) => {
1110
+ await ensureInitialized();
1111
+ const { storeEntry } = await getMemoryFunctions();
1112
+ const t0 = Date.now();
1113
+ const inputPath = String(input.inputPath ?? '');
1114
+ if (!inputPath || !existsSync(inputPath))
1115
+ return { error: `File not found: ${inputPath || '(empty)'}` };
1116
+ let doc;
1117
+ try {
1118
+ doc = JSON.parse(readFileSync(inputPath, 'utf-8'));
1119
+ }
1120
+ catch (e) {
1121
+ return { error: `Invalid export JSON: ${e.message}` };
1122
+ }
1123
+ const entries = Array.isArray(doc.entries) ? doc.entries : [];
1124
+ const nsOverride = input.namespace ? String(input.namespace) : undefined;
1125
+ if (nsOverride) {
1126
+ const v = validateIdentifier(nsOverride, 'namespace');
1127
+ if (!v.valid)
1128
+ throw new Error(v.error);
1129
+ }
1130
+ let imported = 0;
1131
+ let skipped = 0;
1132
+ for (const e of entries) {
1133
+ if (!e || typeof e.key !== 'string') {
1134
+ skipped++;
1135
+ continue;
1136
+ }
1137
+ const value = typeof e.value === 'string' ? e.value : JSON.stringify(e.value ?? null);
1138
+ try {
1139
+ await storeEntry({ key: e.key, value, namespace: nsOverride ?? e.namespace ?? 'default', upsert: input.merge !== false });
1140
+ imported++;
1141
+ }
1142
+ catch {
1143
+ skipped++;
1144
+ }
1145
+ }
1146
+ return {
1147
+ inputPath,
1148
+ imported: { entries: imported, vectors: 0, patterns: 0 },
1149
+ skipped,
1150
+ duration: Date.now() - t0,
1151
+ };
1152
+ },
1153
+ },
817
1154
  ];
818
1155
  //# sourceMappingURL=memory-tools.js.map