@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.
- package/.claude/helpers/hook-handler.cjs +6 -2
- package/.claude/helpers/statusline.cjs +31 -2
- package/.claude/helpers/statusline.js +35 -4
- package/README.md +43 -39
- package/bin/cli.js +15 -2
- package/bin/mcp-server.js +1 -1
- package/dist/src/commands/agent-wasm.js +2 -2
- package/dist/src/commands/agent-wasm.js.map +1 -1
- package/dist/src/commands/daemon.d.ts +20 -0
- package/dist/src/commands/daemon.d.ts.map +1 -1
- package/dist/src/commands/daemon.js +338 -3
- package/dist/src/commands/daemon.js.map +1 -1
- package/dist/src/commands/doctor.d.ts.map +1 -1
- package/dist/src/commands/doctor.js +224 -46
- package/dist/src/commands/doctor.js.map +1 -1
- package/dist/src/commands/hive-mind.d.ts.map +1 -1
- package/dist/src/commands/hive-mind.js +25 -7
- package/dist/src/commands/hive-mind.js.map +1 -1
- package/dist/src/commands/hooks.d.ts.map +1 -1
- package/dist/src/commands/hooks.js +56 -29
- package/dist/src/commands/hooks.js.map +1 -1
- package/dist/src/commands/memory.d.ts.map +1 -1
- package/dist/src/commands/memory.js +90 -3
- package/dist/src/commands/memory.js.map +1 -1
- package/dist/src/commands/start.js +1 -1
- package/dist/src/commands/start.js.map +1 -1
- package/dist/src/commands/swarm.js +1 -1
- package/dist/src/commands/swarm.js.map +1 -1
- package/dist/src/commands/task.d.ts.map +1 -1
- package/dist/src/commands/task.js +8 -4
- package/dist/src/commands/task.js.map +1 -1
- package/dist/src/config-adapter.js +1 -1
- package/dist/src/config-adapter.js.map +1 -1
- package/dist/src/index.d.ts +5 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +61 -18
- package/dist/src/index.js.map +1 -1
- package/dist/src/init/executor.d.ts.map +1 -1
- package/dist/src/init/executor.js +92 -0
- package/dist/src/init/executor.js.map +1 -1
- package/dist/src/init/helpers-generator.d.ts.map +1 -1
- package/dist/src/init/helpers-generator.js +6 -2
- package/dist/src/init/helpers-generator.js.map +1 -1
- package/dist/src/init/mcp-generator.js +4 -4
- package/dist/src/init/mcp-generator.js.map +1 -1
- package/dist/src/init/settings-generator.d.ts.map +1 -1
- package/dist/src/init/settings-generator.js +44 -17
- package/dist/src/init/settings-generator.js.map +1 -1
- package/dist/src/init/statusline-generator.d.ts.map +1 -1
- package/dist/src/init/statusline-generator.js +25 -14
- package/dist/src/init/statusline-generator.js.map +1 -1
- package/dist/src/init/types.d.ts +7 -0
- package/dist/src/init/types.d.ts.map +1 -1
- package/dist/src/init/types.js.map +1 -1
- package/dist/src/mcp-client.d.ts.map +1 -1
- package/dist/src/mcp-client.js +12 -0
- package/dist/src/mcp-client.js.map +1 -1
- package/dist/src/mcp-tools/agent-execute-core.d.ts +3 -2
- package/dist/src/mcp-tools/agent-execute-core.d.ts.map +1 -1
- package/dist/src/mcp-tools/agent-execute-core.js +16 -9
- package/dist/src/mcp-tools/agent-execute-core.js.map +1 -1
- package/dist/src/mcp-tools/agent-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/agent-tools.js +88 -11
- package/dist/src/mcp-tools/agent-tools.js.map +1 -1
- package/dist/src/mcp-tools/agentdb-tools.d.ts +3 -0
- package/dist/src/mcp-tools/agentdb-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/agentdb-tools.js +206 -21
- package/dist/src/mcp-tools/agentdb-tools.js.map +1 -1
- package/dist/src/mcp-tools/analyze-tools.js +6 -6
- package/dist/src/mcp-tools/analyze-tools.js.map +1 -1
- package/dist/src/mcp-tools/autopilot-tools.js +10 -10
- package/dist/src/mcp-tools/autopilot-tools.js.map +1 -1
- package/dist/src/mcp-tools/browser-session-tools.js +5 -5
- package/dist/src/mcp-tools/browser-session-tools.js.map +1 -1
- package/dist/src/mcp-tools/browser-tools.js +23 -23
- package/dist/src/mcp-tools/browser-tools.js.map +1 -1
- package/dist/src/mcp-tools/claims-tools.js +12 -12
- package/dist/src/mcp-tools/claims-tools.js.map +1 -1
- package/dist/src/mcp-tools/config-tools.js +6 -6
- package/dist/src/mcp-tools/config-tools.js.map +1 -1
- package/dist/src/mcp-tools/coordination-tools.js +7 -7
- package/dist/src/mcp-tools/coordination-tools.js.map +1 -1
- package/dist/src/mcp-tools/daa-tools.js +8 -8
- package/dist/src/mcp-tools/daa-tools.js.map +1 -1
- package/dist/src/mcp-tools/embeddings-tools.js +10 -10
- package/dist/src/mcp-tools/embeddings-tools.js.map +1 -1
- package/dist/src/mcp-tools/github-tools.js +5 -5
- package/dist/src/mcp-tools/github-tools.js.map +1 -1
- package/dist/src/mcp-tools/guidance-tools.js +21 -21
- package/dist/src/mcp-tools/guidance-tools.js.map +1 -1
- package/dist/src/mcp-tools/hive-mind-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/hive-mind-tools.js +53 -9
- package/dist/src/mcp-tools/hive-mind-tools.js.map +1 -1
- package/dist/src/mcp-tools/hooks-tools.d.ts +2 -0
- package/dist/src/mcp-tools/hooks-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/hooks-tools.js +183 -48
- package/dist/src/mcp-tools/hooks-tools.js.map +1 -1
- package/dist/src/mcp-tools/managed-agent-tools.d.ts +22 -0
- package/dist/src/mcp-tools/managed-agent-tools.d.ts.map +1 -0
- package/dist/src/mcp-tools/managed-agent-tools.js +357 -0
- package/dist/src/mcp-tools/managed-agent-tools.js.map +1 -0
- package/dist/src/mcp-tools/memory-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/memory-tools.js +400 -63
- package/dist/src/mcp-tools/memory-tools.js.map +1 -1
- package/dist/src/mcp-tools/neural-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/neural-tools.js +20 -7
- package/dist/src/mcp-tools/neural-tools.js.map +1 -1
- package/dist/src/mcp-tools/performance-tools.js +6 -6
- package/dist/src/mcp-tools/performance-tools.js.map +1 -1
- package/dist/src/mcp-tools/progress-tools.js +4 -4
- package/dist/src/mcp-tools/progress-tools.js.map +1 -1
- package/dist/src/mcp-tools/ruvllm-tools.js +10 -10
- package/dist/src/mcp-tools/ruvllm-tools.js.map +1 -1
- package/dist/src/mcp-tools/security-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/security-tools.js +34 -9
- package/dist/src/mcp-tools/security-tools.js.map +1 -1
- package/dist/src/mcp-tools/session-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/session-tools.js +130 -6
- package/dist/src/mcp-tools/session-tools.js.map +1 -1
- package/dist/src/mcp-tools/swarm-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/swarm-tools.js +76 -7
- package/dist/src/mcp-tools/swarm-tools.js.map +1 -1
- package/dist/src/mcp-tools/system-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/system-tools.js +91 -18
- package/dist/src/mcp-tools/system-tools.js.map +1 -1
- package/dist/src/mcp-tools/task-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/task-tools.js +55 -7
- package/dist/src/mcp-tools/task-tools.js.map +1 -1
- package/dist/src/mcp-tools/terminal-tools.js +5 -5
- package/dist/src/mcp-tools/terminal-tools.js.map +1 -1
- package/dist/src/mcp-tools/transfer-tools.js +11 -11
- package/dist/src/mcp-tools/transfer-tools.js.map +1 -1
- package/dist/src/mcp-tools/wasm-agent-tools.js +11 -11
- package/dist/src/mcp-tools/wasm-agent-tools.js.map +1 -1
- package/dist/src/mcp-tools/workflow-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/workflow-tools.js +118 -10
- package/dist/src/mcp-tools/workflow-tools.js.map +1 -1
- package/dist/src/memory/intelligence.d.ts.map +1 -1
- package/dist/src/memory/intelligence.js +28 -3
- package/dist/src/memory/intelligence.js.map +1 -1
- package/dist/src/memory/memory-bridge.d.ts +69 -0
- package/dist/src/memory/memory-bridge.d.ts.map +1 -1
- package/dist/src/memory/memory-bridge.js +282 -5
- package/dist/src/memory/memory-bridge.js.map +1 -1
- package/dist/src/memory/memory-initializer.d.ts +8 -0
- package/dist/src/memory/memory-initializer.d.ts.map +1 -1
- package/dist/src/memory/memory-initializer.js +80 -16
- package/dist/src/memory/memory-initializer.js.map +1 -1
- package/dist/src/memory/neural-package-bridge.d.ts +48 -0
- package/dist/src/memory/neural-package-bridge.d.ts.map +1 -0
- package/dist/src/memory/neural-package-bridge.js +87 -0
- package/dist/src/memory/neural-package-bridge.js.map +1 -0
- package/dist/src/memory/sona-optimizer.d.ts.map +1 -1
- package/dist/src/memory/sona-optimizer.js +3 -0
- package/dist/src/memory/sona-optimizer.js.map +1 -1
- package/dist/src/parser.d.ts +9 -0
- package/dist/src/parser.d.ts.map +1 -1
- package/dist/src/parser.js +11 -0
- package/dist/src/parser.js.map +1 -1
- package/dist/src/plugins/store/discovery.d.ts +6 -3
- package/dist/src/plugins/store/discovery.d.ts.map +1 -1
- package/dist/src/plugins/store/discovery.js +11 -8
- package/dist/src/plugins/store/discovery.js.map +1 -1
- package/dist/src/ruvector/agent-wasm.d.ts.map +1 -1
- package/dist/src/ruvector/agent-wasm.js +4 -1
- package/dist/src/ruvector/agent-wasm.js.map +1 -1
- package/dist/src/ruvector/coverage-tools.js +6 -6
- package/dist/src/ruvector/coverage-tools.js.map +1 -1
- package/dist/src/ruvector/index.d.ts +0 -2
- package/dist/src/ruvector/index.d.ts.map +1 -1
- package/dist/src/ruvector/index.js +8 -2
- package/dist/src/ruvector/index.js.map +1 -1
- package/dist/src/ruvector/model-router.d.ts +22 -1
- package/dist/src/ruvector/model-router.d.ts.map +1 -1
- package/dist/src/ruvector/model-router.js +125 -5
- package/dist/src/ruvector/model-router.js.map +1 -1
- package/dist/src/services/headless-worker-executor.d.ts +6 -0
- package/dist/src/services/headless-worker-executor.d.ts.map +1 -1
- package/dist/src/services/headless-worker-executor.js +37 -3
- package/dist/src/services/headless-worker-executor.js.map +1 -1
- package/dist/src/services/worker-daemon.d.ts +80 -2
- package/dist/src/services/worker-daemon.d.ts.map +1 -1
- package/dist/src/services/worker-daemon.js +372 -11
- package/dist/src/services/worker-daemon.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -5
- package/.claude/skills/agentdb-advanced/SKILL.md +0 -550
- package/.claude/skills/agentdb-learning/SKILL.md +0 -545
- package/.claude/skills/agentdb-memory-patterns/SKILL.md +0 -339
- package/.claude/skills/agentdb-optimization/SKILL.md +0 -509
- package/.claude/skills/agentdb-vector-search/SKILL.md +0 -339
- package/.claude/skills/agentic-jujutsu/SKILL.md +0 -645
- package/.claude/skills/aidefence-scan.md +0 -151
- package/.claude/skills/aidefence.yaml +0 -297
- package/.claude/skills/browser/SKILL.md +0 -204
- package/.claude/skills/flow-nexus-neural/SKILL.md +0 -738
- package/.claude/skills/flow-nexus-platform/SKILL.md +0 -1157
- package/.claude/skills/flow-nexus-swarm/SKILL.md +0 -610
- package/.claude/skills/github-code-review/SKILL.md +0 -1140
- package/.claude/skills/github-multi-repo/SKILL.md +0 -874
- package/.claude/skills/github-project-management/SKILL.md +0 -1277
- package/.claude/skills/github-release-management/SKILL.md +0 -1081
- package/.claude/skills/github-workflow-automation/SKILL.md +0 -1065
- package/.claude/skills/hive-mind-advanced/SKILL.md +0 -712
- package/.claude/skills/hooks-automation/SKILL.md +0 -1201
- package/.claude/skills/pair-programming/SKILL.md +0 -1202
- package/.claude/skills/performance-analysis/SKILL.md +0 -563
- package/.claude/skills/reasoningbank-agentdb/SKILL.md +0 -446
- package/.claude/skills/reasoningbank-intelligence/SKILL.md +0 -201
- package/.claude/skills/secure-review.md +0 -181
- package/.claude/skills/skill-builder/SKILL.md +0 -910
- package/.claude/skills/sparc-methodology/SKILL.md +0 -1115
- package/.claude/skills/stream-chain/SKILL.md +0 -563
- package/.claude/skills/swarm-advanced/SKILL.md +0 -973
- package/.claude/skills/swarm-orchestration/SKILL.md +0 -179
- package/.claude/skills/v3-cli-modernization/SKILL.md +0 -872
- package/.claude/skills/v3-core-implementation/SKILL.md +0 -797
- package/.claude/skills/v3-ddd-architecture/SKILL.md +0 -442
- package/.claude/skills/v3-integration-deep/SKILL.md +0 -241
- package/.claude/skills/v3-mcp-optimization/SKILL.md +0 -777
- package/.claude/skills/v3-memory-unification/SKILL.md +0 -174
- package/.claude/skills/v3-performance-optimization/SKILL.md +0 -390
- package/.claude/skills/v3-security-overhaul/SKILL.md +0 -82
- package/.claude/skills/v3-swarm-coordination/SKILL.md +0 -340
- package/.claude/skills/verification-quality/SKILL.md +0 -649
- package/.claude/skills/worker-benchmarks/skill.md +0 -135
- 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
|
-
|
|
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 &&
|
|
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
|
-
//
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
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
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
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
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
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
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
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
|
-
//
|
|
632
|
-
|
|
633
|
-
const
|
|
634
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|