@claude-flow/cli 3.7.0-alpha.7 → 3.7.0-alpha.71
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 +12 -4
- package/.claude/helpers/statusline.cjs +31 -2
- package/.claude/helpers/statusline.js +35 -4
- package/README.md +51 -34
- package/bin/cli.js +15 -2
- package/bin/mcp-server.js +1 -1
- package/dist/src/__probe.d.ts +2 -0
- package/dist/src/__probe.d.ts.map +1 -0
- package/dist/src/__probe.js +5 -0
- package/dist/src/__probe.js.map +1 -0
- package/dist/src/commands/agent-wasm.js +2 -2
- package/dist/src/commands/agent-wasm.js.map +1 -1
- package/dist/src/commands/benchmark-cosign.d.ts +29 -0
- package/dist/src/commands/benchmark-cosign.d.ts.map +1 -0
- package/dist/src/commands/benchmark-cosign.js +222 -0
- package/dist/src/commands/benchmark-cosign.js.map +1 -0
- package/dist/src/commands/benchmark-verify.d.ts +21 -0
- package/dist/src/commands/benchmark-verify.d.ts.map +1 -0
- package/dist/src/commands/benchmark-verify.js +202 -0
- package/dist/src/commands/benchmark-verify.js.map +1 -0
- 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 +366 -7
- 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/embeddings.d.ts.map +1 -1
- package/dist/src/commands/embeddings.js +18 -9
- package/dist/src/commands/embeddings.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 +104 -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 +78 -19
- 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 +75 -31
- 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-server.d.ts.map +1 -1
- package/dist/src/mcp-server.js +38 -5
- package/dist/src/mcp-server.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.d.ts.map +1 -1
- package/dist/src/mcp-tools/browser-session-tools.js +18 -7
- 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-consensus-runtime.d.ts +149 -0
- package/dist/src/mcp-tools/hive-consensus-runtime.d.ts.map +1 -0
- package/dist/src/mcp-tools/hive-consensus-runtime.js +296 -0
- package/dist/src/mcp-tools/hive-consensus-runtime.js.map +1 -0
- 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 +179 -46
- 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 +490 -68
- package/dist/src/mcp-tools/memory-tools.js.map +1 -1
- package/dist/src/mcp-tools/neural-tools.js +6 -6
- 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/ann-router-registry.d.ts +61 -0
- package/dist/src/memory/ann-router-registry.d.ts.map +1 -0
- package/dist/src/memory/ann-router-registry.js +72 -0
- package/dist/src/memory/ann-router-registry.js.map +1 -0
- package/dist/src/memory/diskann-registry.d.ts +56 -0
- package/dist/src/memory/diskann-registry.d.ts.map +1 -0
- package/dist/src/memory/diskann-registry.js +88 -0
- package/dist/src/memory/diskann-registry.js.map +1 -0
- 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 +293 -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 +89 -16
- package/dist/src/memory/memory-initializer.js.map +1 -1
- 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 +15 -4
- package/dist/src/plugins/store/discovery.d.ts.map +1 -1
- package/dist/src/plugins/store/discovery.js +40 -18
- 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/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 +12 -8
- 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
- package/dist/src/ruvector/flash-attention.d.ts +0 -195
- package/dist/src/ruvector/flash-attention.d.ts.map +0 -1
- package/dist/src/ruvector/flash-attention.js +0 -643
- package/dist/src/ruvector/flash-attention.js.map +0 -1
- package/dist/src/ruvector/moe-router.d.ts +0 -206
- package/dist/src/ruvector/moe-router.d.ts.map +0 -1
- package/dist/src/ruvector/moe-router.js +0 -626
- package/dist/src/ruvector/moe-router.js.map +0 -1
- package/dist/src/services/event-stream.d.ts +0 -25
- package/dist/src/services/event-stream.d.ts.map +0 -1
- package/dist/src/services/event-stream.js +0 -27
- package/dist/src/services/event-stream.js.map +0 -1
- package/dist/src/services/loop-worker-runner.d.ts +0 -16
- package/dist/src/services/loop-worker-runner.d.ts.map +0 -1
- package/dist/src/services/loop-worker-runner.js +0 -34
- package/dist/src/services/loop-worker-runner.js.map +0 -1
- package/dist/src/services/runtime-capabilities.d.ts +0 -22
- package/dist/src/services/runtime-capabilities.d.ts.map +0 -1
- package/dist/src/services/runtime-capabilities.js +0 -45
- package/dist/src/services/runtime-capabilities.js.map +0 -1
|
@@ -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,104 @@ 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
|
+
// #1937 — minimal glob → RegExp helper for memory_import_claude exclusion
|
|
74
|
+
// patterns. Anchored. Supports the three operators the issue's voice-fidelity
|
|
75
|
+
// workflow needs:
|
|
76
|
+
// `**` — any chars including path separators
|
|
77
|
+
// `*` — any chars except path separators
|
|
78
|
+
// `?` — exactly one char except a path separator
|
|
79
|
+
// Everything else is regex-escaped. Used to match absolute file paths.
|
|
80
|
+
function globToRegex(pattern) {
|
|
81
|
+
// Tokenize so we can replace `**` before `*` without overlap.
|
|
82
|
+
let out = '';
|
|
83
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
84
|
+
const c = pattern[i];
|
|
85
|
+
if (c === '*' && pattern[i + 1] === '*') {
|
|
86
|
+
out += '.*';
|
|
87
|
+
i++;
|
|
88
|
+
}
|
|
89
|
+
else if (c === '*') {
|
|
90
|
+
out += '[^/\\\\]*';
|
|
91
|
+
}
|
|
92
|
+
else if (c === '?') {
|
|
93
|
+
out += '[^/\\\\]';
|
|
94
|
+
}
|
|
95
|
+
else if (/[.+^$|(){}\[\]\\]/.test(c)) {
|
|
96
|
+
out += '\\' + c;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
out += c;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return new RegExp('^' + out + '$');
|
|
103
|
+
}
|
|
104
|
+
// #1883 — resolve the Claude-Code project memory directory for the *current*
|
|
105
|
+
// project. Claude Code hashes the project path differently per host OS, and
|
|
106
|
+
// our previous logic only POSIX-slash-replaced cwd, which breaks for:
|
|
107
|
+
// - WSL bridges where cwd is `/mnt/<drive>/...` but Claude Code is on Windows
|
|
108
|
+
// - paths containing spaces (Claude Code replaces spaces with dashes)
|
|
109
|
+
// - any leading slash on POSIX (Claude Code strips it)
|
|
110
|
+
// Strategy: try several candidate hashes and return the first one with a
|
|
111
|
+
// memory dir that exists. An explicit `projectPathOverride` short-circuits
|
|
112
|
+
// the heuristics for callers that know the canonical project path.
|
|
113
|
+
function resolveProjectMemoryDir(claudeProjectsDir, projectPathOverride) {
|
|
114
|
+
const candidates = new Set();
|
|
115
|
+
const sources = [];
|
|
116
|
+
if (projectPathOverride && projectPathOverride.length > 0) {
|
|
117
|
+
sources.push(projectPathOverride);
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
sources.push(process.cwd());
|
|
121
|
+
}
|
|
122
|
+
for (const source of sources) {
|
|
123
|
+
// Candidate 1: legacy POSIX hash — what shipped before #1883
|
|
124
|
+
candidates.add(source.replace(/\//g, '-'));
|
|
125
|
+
// Candidate 2: WSL `/mnt/<drive>/...` translated to Claude-Code Windows hash
|
|
126
|
+
// e.g. `/mnt/c/Users/x/Project Name` → `C--Users-x-Project-Name`
|
|
127
|
+
const wsl = source.match(/^\/mnt\/([a-z])(\/.*)?$/i);
|
|
128
|
+
if (wsl) {
|
|
129
|
+
const drive = wsl[1].toUpperCase();
|
|
130
|
+
const rest = (wsl[2] ?? '').replace(/\//g, '-').replace(/ /g, '-');
|
|
131
|
+
candidates.add(`${drive}-${rest}`);
|
|
132
|
+
}
|
|
133
|
+
// Candidate 3: POSIX hash with leading dash stripped (Claude Code on macOS/Linux)
|
|
134
|
+
const stripped = source.replace(/\//g, '-').replace(/^-+/, '');
|
|
135
|
+
candidates.add(stripped);
|
|
136
|
+
// Candidate 4: spaces replaced with dashes (Claude Code's space rule)
|
|
137
|
+
candidates.add(source.replace(/\//g, '-').replace(/ /g, '-'));
|
|
138
|
+
// Candidate 5 (#1939): native Win32 path on a Win32 Claude Code install.
|
|
139
|
+
// `C:\Users\tobia\OneDrive\Desktop\Claude Stuff` →
|
|
140
|
+
// `C--Users-tobia-OneDrive-Desktop-Claude-Stuff`. Claude Code's on-disk
|
|
141
|
+
// slug replaces drive-colon AND backslashes AND whitespace with `-`.
|
|
142
|
+
// The earlier candidates only handled forward slashes, so a Win32+Win32
|
|
143
|
+
// setup never matched.
|
|
144
|
+
if (/^[A-Za-z]:[\\/]/.test(source)) {
|
|
145
|
+
candidates.add(source.replace(/[:\\/]/g, '-').replace(/\s+/g, '-'));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
for (const projectHash of candidates) {
|
|
149
|
+
const memDir = join(claudeProjectsDir, projectHash, 'memory');
|
|
150
|
+
if (existsSync(memDir))
|
|
151
|
+
return { memDir, projectHash };
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
59
155
|
/**
|
|
60
156
|
* Check if legacy JSON store exists in old .claude-flow/memory/ location
|
|
61
157
|
*/
|
|
@@ -310,57 +406,81 @@ export const memoryTools = [
|
|
|
310
406
|
validateMemoryInput(undefined, undefined, query);
|
|
311
407
|
const startTime = performance.now();
|
|
312
408
|
try {
|
|
409
|
+
// #1846: feature-detect smartSearch on the resolved memory package.
|
|
410
|
+
// The export landed in @claude-flow/memory@>3.0.0-alpha.14 — older
|
|
411
|
+
// installs pin to a build that exposes search/store/retrieve but
|
|
412
|
+
// not smartSearch. Throwing `is not a function` is hostile; instead
|
|
413
|
+
// detect at runtime and gracefully fall through to plain semantic
|
|
414
|
+
// search with an explicit fallback note.
|
|
415
|
+
let smartFallbackReason;
|
|
313
416
|
if (input.smart) {
|
|
314
|
-
//
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
namespace:
|
|
331
|
-
|
|
417
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
418
|
+
let memMod;
|
|
419
|
+
try {
|
|
420
|
+
memMod = await import('@claude-flow/memory');
|
|
421
|
+
}
|
|
422
|
+
catch (err) {
|
|
423
|
+
smartFallbackReason = `@claude-flow/memory failed to load: ${err.message}`;
|
|
424
|
+
}
|
|
425
|
+
const smartSearch = memMod && typeof memMod.smartSearch === 'function'
|
|
426
|
+
? memMod.smartSearch
|
|
427
|
+
: undefined;
|
|
428
|
+
if (smartSearch) {
|
|
429
|
+
// SmartRetrieval pipeline (ADR-090)
|
|
430
|
+
const rawSearch = async (req) => {
|
|
431
|
+
const r = await searchEntries({
|
|
432
|
+
query: req.query,
|
|
433
|
+
namespace: req.namespace || namespace,
|
|
434
|
+
limit: req.limit || limit * 3,
|
|
435
|
+
threshold: req.threshold ?? threshold,
|
|
436
|
+
});
|
|
437
|
+
return {
|
|
438
|
+
results: r.results.map(e => ({
|
|
439
|
+
id: e.id,
|
|
440
|
+
key: e.key,
|
|
441
|
+
content: e.content,
|
|
442
|
+
score: e.score,
|
|
443
|
+
namespace: e.namespace,
|
|
444
|
+
})),
|
|
445
|
+
};
|
|
332
446
|
};
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
447
|
+
const smartResult = await smartSearch(rawSearch, {
|
|
448
|
+
query,
|
|
449
|
+
namespace,
|
|
450
|
+
limit,
|
|
451
|
+
threshold,
|
|
452
|
+
});
|
|
453
|
+
const duration = performance.now() - startTime;
|
|
454
|
+
const results = smartResult.results.map((r) => {
|
|
455
|
+
let value = r.content;
|
|
456
|
+
try {
|
|
457
|
+
value = JSON.parse(r.content);
|
|
458
|
+
}
|
|
459
|
+
catch { /* keep as string */ }
|
|
460
|
+
return {
|
|
461
|
+
key: r.key,
|
|
462
|
+
namespace: r.namespace,
|
|
463
|
+
value,
|
|
464
|
+
similarity: r.score,
|
|
465
|
+
};
|
|
466
|
+
});
|
|
347
467
|
return {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
468
|
+
query,
|
|
469
|
+
results,
|
|
470
|
+
total: results.length,
|
|
471
|
+
searchTime: `${duration.toFixed(2)}ms`,
|
|
472
|
+
backend: 'SmartRetrieval (RRF + MMR + Recency)',
|
|
473
|
+
stats: smartResult.stats,
|
|
352
474
|
};
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
searchTime: `${duration.toFixed(2)}ms`,
|
|
359
|
-
backend: 'SmartRetrieval (RRF + MMR + Recency)',
|
|
360
|
-
stats: smartResult.stats,
|
|
361
|
-
};
|
|
475
|
+
}
|
|
476
|
+
// smart=true but smartSearch unavailable on installed package.
|
|
477
|
+
// Fall through to plain search with an explicit warning.
|
|
478
|
+
smartFallbackReason = smartFallbackReason
|
|
479
|
+
?? 'smartSearch is not exported by the installed @claude-flow/memory build (likely a release lag — see #1846). Falling back to standard semantic search.';
|
|
362
480
|
}
|
|
363
|
-
// Original non-smart path (unchanged)
|
|
481
|
+
// Original non-smart path (unchanged) — also reached when smart was
|
|
482
|
+
// requested but unavailable. We attach `smartFallback` to the
|
|
483
|
+
// response so callers can see the degradation explicitly.
|
|
364
484
|
const result = await searchEntries({
|
|
365
485
|
query,
|
|
366
486
|
namespace,
|
|
@@ -390,6 +510,7 @@ export const memoryTools = [
|
|
|
390
510
|
total: results.length,
|
|
391
511
|
searchTime: `${duration.toFixed(2)}ms`,
|
|
392
512
|
backend: 'HNSW + sql.js',
|
|
513
|
+
...(smartFallbackReason ? { smartFallback: smartFallbackReason } : {}),
|
|
393
514
|
};
|
|
394
515
|
}
|
|
395
516
|
catch (error) {
|
|
@@ -501,7 +622,7 @@ export const memoryTools = [
|
|
|
501
622
|
},
|
|
502
623
|
{
|
|
503
624
|
name: 'memory_stats',
|
|
504
|
-
description: 'Get memory storage statistics including HNSW index status',
|
|
625
|
+
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
626
|
category: 'memory',
|
|
506
627
|
inputSchema: {
|
|
507
628
|
type: 'object',
|
|
@@ -548,7 +669,7 @@ export const memoryTools = [
|
|
|
548
669
|
},
|
|
549
670
|
{
|
|
550
671
|
name: 'memory_migrate',
|
|
551
|
-
description: 'Manually trigger migration from legacy JSON store to sql.js',
|
|
672
|
+
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
673
|
category: 'memory',
|
|
553
674
|
inputSchema: {
|
|
554
675
|
type: 'object',
|
|
@@ -587,13 +708,24 @@ export const memoryTools = [
|
|
|
587
708
|
// ===== Claude Code Memory Bridge Tools =====
|
|
588
709
|
{
|
|
589
710
|
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.',
|
|
711
|
+
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). Pass excludeFilePatterns (glob list) or excludeFiles (absolute path list) to skip voice-load-bearing, PII, or persona-restricted files (#1937). 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
712
|
category: 'memory',
|
|
592
713
|
inputSchema: {
|
|
593
714
|
type: 'object',
|
|
594
715
|
properties: {
|
|
595
716
|
allProjects: { type: 'boolean', description: 'Import from all Claude projects (default: current project only)' },
|
|
596
717
|
namespace: { type: 'string', description: 'Target namespace (default: "claude-memories")' },
|
|
718
|
+
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.' },
|
|
719
|
+
excludeFilePatterns: {
|
|
720
|
+
type: 'array',
|
|
721
|
+
items: { type: 'string' },
|
|
722
|
+
description: '#1937 — glob patterns matched against the absolute file path. Files matching ANY pattern are skipped. Supports `*` (any chars within a path segment), `**` (any chars including separators), and `?` (single char). Examples: `**/voice-*.md`, `**/persona-*.md`. Combine with excludeFiles for explicit paths.',
|
|
723
|
+
},
|
|
724
|
+
excludeFiles: {
|
|
725
|
+
type: 'array',
|
|
726
|
+
items: { type: 'string' },
|
|
727
|
+
description: '#1937 — absolute file paths to skip verbatim. Faster than a pattern when the list is known ahead of time (operator captured baselines). Combine with excludeFilePatterns.',
|
|
728
|
+
},
|
|
597
729
|
},
|
|
598
730
|
},
|
|
599
731
|
handler: async (input) => {
|
|
@@ -606,9 +738,20 @@ export const memoryTools = [
|
|
|
606
738
|
return { success: false, imported: 0, error: vNs.error };
|
|
607
739
|
}
|
|
608
740
|
const allProjects = input.allProjects;
|
|
741
|
+
const projectPathOverride = input.projectPath;
|
|
609
742
|
const claudeProjectsDir = join(homedir(), '.claude', 'projects');
|
|
743
|
+
// #1937 — voice-fidelity / persona-restricted exclusion.
|
|
744
|
+
const excludeFilePatterns = Array.isArray(input.excludeFilePatterns) ? input.excludeFilePatterns : [];
|
|
745
|
+
const excludeFilesList = Array.isArray(input.excludeFiles) ? new Set(input.excludeFiles) : new Set();
|
|
746
|
+
const excludeRegexes = excludeFilePatterns.map(globToRegex);
|
|
747
|
+
const isExcluded = (absPath) => {
|
|
748
|
+
if (excludeFilesList.has(absPath))
|
|
749
|
+
return true;
|
|
750
|
+
return excludeRegexes.some(re => re.test(absPath));
|
|
751
|
+
};
|
|
610
752
|
// Find memory files
|
|
611
753
|
const memoryFiles = [];
|
|
754
|
+
let excludedByPattern = 0;
|
|
612
755
|
if (allProjects) {
|
|
613
756
|
// Scan all projects
|
|
614
757
|
if (existsSync(claudeProjectsDir)) {
|
|
@@ -620,7 +763,12 @@ export const memoryTools = [
|
|
|
620
763
|
if (!existsSync(memDir))
|
|
621
764
|
continue;
|
|
622
765
|
for (const file of readdirSync(memDir).filter((f) => f.endsWith('.md'))) {
|
|
623
|
-
|
|
766
|
+
const absPath = join(memDir, file);
|
|
767
|
+
if (isExcluded(absPath)) {
|
|
768
|
+
excludedByPattern++;
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
memoryFiles.push({ path: absPath, project: project.name, file });
|
|
624
772
|
}
|
|
625
773
|
}
|
|
626
774
|
}
|
|
@@ -628,14 +776,18 @@ export const memoryTools = [
|
|
|
628
776
|
}
|
|
629
777
|
}
|
|
630
778
|
else {
|
|
631
|
-
//
|
|
632
|
-
|
|
633
|
-
const
|
|
634
|
-
|
|
635
|
-
if (existsSync(memDir)) {
|
|
779
|
+
// #1883 — current project: try multiple candidate hashes (POSIX, WSL-translated,
|
|
780
|
+
// leading-dash-stripped, space-replaced). Caller can pass projectPath to override.
|
|
781
|
+
const resolved = resolveProjectMemoryDir(claudeProjectsDir, projectPathOverride);
|
|
782
|
+
if (resolved) {
|
|
636
783
|
try {
|
|
637
|
-
for (const file of readdirSync(memDir).filter((f) => f.endsWith('.md'))) {
|
|
638
|
-
|
|
784
|
+
for (const file of readdirSync(resolved.memDir).filter((f) => f.endsWith('.md'))) {
|
|
785
|
+
const absPath = join(resolved.memDir, file);
|
|
786
|
+
if (isExcluded(absPath)) {
|
|
787
|
+
excludedByPattern++;
|
|
788
|
+
continue;
|
|
789
|
+
}
|
|
790
|
+
memoryFiles.push({ path: absPath, project: resolved.projectHash, file });
|
|
639
791
|
}
|
|
640
792
|
}
|
|
641
793
|
catch { /* scan error */ }
|
|
@@ -646,11 +798,28 @@ export const memoryTools = [
|
|
|
646
798
|
}
|
|
647
799
|
let imported = 0;
|
|
648
800
|
let skipped = 0;
|
|
801
|
+
// #1791.8 — Claude Code's `~/.claude/projects/` accumulates historical
|
|
802
|
+
// project_id directories (truncated forms, sandbox cwds, renamed
|
|
803
|
+
// workspaces) that all contain copies of the same memory files. The
|
|
804
|
+
// previous import indexed each copy under a different `project_id`
|
|
805
|
+
// prefix, producing 5–8x duplication on long-lived homes. Dedupe by
|
|
806
|
+
// file content hash so the same memory is imported once even if it
|
|
807
|
+
// appears under several project directories.
|
|
808
|
+
const seenContentHashes = new Set();
|
|
809
|
+
let duplicatesSkipped = 0;
|
|
649
810
|
const projects = new Set();
|
|
650
811
|
for (const memFile of memoryFiles) {
|
|
651
812
|
projects.add(memFile.project);
|
|
652
813
|
try {
|
|
653
814
|
const content = readFileSync(memFile.path, 'utf-8');
|
|
815
|
+
// #1791.8 — Skip if we've already imported this exact content under
|
|
816
|
+
// a different project_id directory.
|
|
817
|
+
const contentHash = createHash('sha256').update(content).digest('hex').slice(0, 16);
|
|
818
|
+
if (seenContentHashes.has(contentHash)) {
|
|
819
|
+
duplicatesSkipped++;
|
|
820
|
+
continue;
|
|
821
|
+
}
|
|
822
|
+
seenContentHashes.add(contentHash);
|
|
654
823
|
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
655
824
|
let name = memFile.file.replace('.md', '');
|
|
656
825
|
let body = content;
|
|
@@ -664,7 +833,10 @@ export const memoryTools = [
|
|
|
664
833
|
// Split into sections for granular search
|
|
665
834
|
const sections = body.split(/^(?=## )/m).filter(s => s.trim().length > 20);
|
|
666
835
|
if (sections.length === 0 && body.length > 10) {
|
|
667
|
-
|
|
836
|
+
// #1884 — sanitize key so memory_delete can later remove it. Without
|
|
837
|
+
// this, dangerous chars from frontmatter `name` strand the key.
|
|
838
|
+
const key = sanitizeMemoryKey(`claude:${memFile.project}:${name}`);
|
|
839
|
+
await storeEntry({ key, value: body.slice(0, 4096), namespace: ns, generateEmbeddingFlag: true });
|
|
668
840
|
imported++;
|
|
669
841
|
}
|
|
670
842
|
else {
|
|
@@ -674,7 +846,10 @@ export const memoryTools = [
|
|
|
674
846
|
const sectionBody = section.replace(/^##\s+.+\n/, '').trim();
|
|
675
847
|
if (sectionBody.length < 10)
|
|
676
848
|
continue;
|
|
677
|
-
|
|
849
|
+
// #1884 — sanitize so any dangerous chars in the heading don't
|
|
850
|
+
// produce keys memory_delete will reject.
|
|
851
|
+
const key = sanitizeMemoryKey(`claude:${memFile.project}:${name}:${sectionTitle.slice(0, 50)}`);
|
|
852
|
+
await storeEntry({ key, value: sectionBody.slice(0, 4096), namespace: ns, generateEmbeddingFlag: true });
|
|
678
853
|
imported++;
|
|
679
854
|
}
|
|
680
855
|
}
|
|
@@ -687,6 +862,8 @@ export const memoryTools = [
|
|
|
687
862
|
success: true,
|
|
688
863
|
imported,
|
|
689
864
|
skipped,
|
|
865
|
+
duplicatesSkipped,
|
|
866
|
+
excludedByPattern,
|
|
690
867
|
files: memoryFiles.length,
|
|
691
868
|
projects: projects.size,
|
|
692
869
|
namespace: ns,
|
|
@@ -696,7 +873,7 @@ export const memoryTools = [
|
|
|
696
873
|
},
|
|
697
874
|
{
|
|
698
875
|
name: 'memory_bridge_status',
|
|
699
|
-
description: 'Show Claude Code memory bridge status — AgentDB vectors, SONA learning, intelligence patterns, and connection health.',
|
|
876
|
+
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
877
|
category: 'memory',
|
|
701
878
|
inputSchema: { type: 'object', properties: {} },
|
|
702
879
|
handler: async () => {
|
|
@@ -723,14 +900,34 @@ export const memoryTools = [
|
|
|
723
900
|
catch { /* ignore */ }
|
|
724
901
|
}
|
|
725
902
|
// AgentDB status
|
|
903
|
+
// #1940: previously used `allEntries.entries.length` for the totals,
|
|
904
|
+
// but `listEntries({})` returns the first 20 entries with a separate
|
|
905
|
+
// `total` field for the full row count. So `memory_bridge_status`
|
|
906
|
+
// reported `totalEntries: 0`...20 even when the DB had hundreds of
|
|
907
|
+
// rows. Use `.total` for the count, and surface the namespaces with
|
|
908
|
+
// entries so the report matches what's actually in the store.
|
|
726
909
|
let agentdbEntries = 0;
|
|
727
910
|
let claudeMemoryEntries = 0;
|
|
911
|
+
const namespaceCounts = {};
|
|
728
912
|
try {
|
|
729
913
|
const { listEntries } = await getMemoryFunctions();
|
|
730
914
|
const allEntries = await listEntries({});
|
|
731
|
-
agentdbEntries = allEntries?.
|
|
915
|
+
agentdbEntries = allEntries?.total
|
|
916
|
+
?? allEntries?.entries?.length ?? 0;
|
|
732
917
|
const claudeEntries = await listEntries({ namespace: 'claude-memories' });
|
|
733
|
-
claudeMemoryEntries = claudeEntries?.
|
|
918
|
+
claudeMemoryEntries = claudeEntries?.total
|
|
919
|
+
?? claudeEntries?.entries?.length ?? 0;
|
|
920
|
+
// Per-namespace counts for the namespaces the reporter referenced
|
|
921
|
+
// (#1940). Best-effort — a namespace with 0 entries is omitted.
|
|
922
|
+
for (const ns of ['default', 'patterns', 'claude-memories', 'auto-memory', 'tasks', 'feedback', 'pretrain']) {
|
|
923
|
+
try {
|
|
924
|
+
const r = await listEntries({ namespace: ns });
|
|
925
|
+
const t = r?.total ?? r?.entries?.length ?? 0;
|
|
926
|
+
if (t > 0)
|
|
927
|
+
namespaceCounts[ns] = t;
|
|
928
|
+
}
|
|
929
|
+
catch { /* skip per-namespace failure */ }
|
|
930
|
+
}
|
|
734
931
|
}
|
|
735
932
|
catch { /* ignore */ }
|
|
736
933
|
// Intelligence status
|
|
@@ -744,15 +941,18 @@ export const memoryTools = [
|
|
|
744
941
|
catch { /* not initialized */ }
|
|
745
942
|
return {
|
|
746
943
|
claudeCode: { memoryFiles: claudeFiles, projects: claudeProjects },
|
|
747
|
-
agentdb: { totalEntries: agentdbEntries, claudeMemoryEntries, backend: 'sql.js + ONNX' },
|
|
944
|
+
agentdb: { totalEntries: agentdbEntries, claudeMemoryEntries, namespaces: namespaceCounts, backend: 'sql.js + ONNX' },
|
|
748
945
|
intelligence,
|
|
749
|
-
|
|
946
|
+
// #1940: report 'connected' whenever ANY namespace has imported
|
|
947
|
+
// content, not just `claude-memories` — the bridge can be in active
|
|
948
|
+
// use from other import paths (e.g. plugin namespaces, task memory).
|
|
949
|
+
bridge: { status: agentdbEntries > 0 ? 'connected' : 'not-synced', embedding: 'all-MiniLM-L6-v2 (384-dim)' },
|
|
750
950
|
};
|
|
751
951
|
},
|
|
752
952
|
},
|
|
753
953
|
{
|
|
754
954
|
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.',
|
|
955
|
+
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
956
|
category: 'memory',
|
|
757
957
|
inputSchema: {
|
|
758
958
|
type: 'object',
|
|
@@ -814,5 +1014,227 @@ export const memoryTools = [
|
|
|
814
1014
|
};
|
|
815
1015
|
},
|
|
816
1016
|
},
|
|
1017
|
+
{
|
|
1018
|
+
// #1916: `ruflo status memory` (the detailed view) referenced an
|
|
1019
|
+
// unregistered `memory_detailed-stats` tool. memory_stats returns a
|
|
1020
|
+
// different shape; this returns what the CLI renders.
|
|
1021
|
+
name: 'memory_detailed-stats',
|
|
1022
|
+
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.',
|
|
1023
|
+
category: 'memory',
|
|
1024
|
+
inputSchema: { type: 'object', properties: {} },
|
|
1025
|
+
handler: async () => {
|
|
1026
|
+
await ensureInitialized();
|
|
1027
|
+
const { listEntries } = await getMemoryFunctions();
|
|
1028
|
+
const all = await listEntries({ limit: 100000 });
|
|
1029
|
+
const nsCounts = {};
|
|
1030
|
+
let bytes = 0;
|
|
1031
|
+
for (const e of all.entries) {
|
|
1032
|
+
nsCounts[e.namespace] = (nsCounts[e.namespace] || 0) + 1;
|
|
1033
|
+
bytes += e.size || 0;
|
|
1034
|
+
}
|
|
1035
|
+
return {
|
|
1036
|
+
backend: 'sql.js + HNSW',
|
|
1037
|
+
entries: all.total ?? all.entries.length,
|
|
1038
|
+
size: bytes,
|
|
1039
|
+
namespaces: Object.entries(nsCounts).map(([name, entries]) => ({ name, entries })),
|
|
1040
|
+
performance: { avgSearchTime: 0, avgWriteTime: 0, cacheHitRate: 0, hnswEnabled: true },
|
|
1041
|
+
note: 'perf metrics are placeholders; HNSW is always enabled in the sql.js backend',
|
|
1042
|
+
};
|
|
1043
|
+
},
|
|
1044
|
+
},
|
|
1045
|
+
{
|
|
1046
|
+
// #1916: `ruflo memory cleanup` referenced an unregistered `memory_cleanup`
|
|
1047
|
+
// tool. Removes entries whose TTL has expired. Defaults to a dry run —
|
|
1048
|
+
// pass dryRun:false to actually delete.
|
|
1049
|
+
name: 'memory_cleanup',
|
|
1050
|
+
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).',
|
|
1051
|
+
category: 'memory',
|
|
1052
|
+
inputSchema: {
|
|
1053
|
+
type: 'object',
|
|
1054
|
+
properties: {
|
|
1055
|
+
dryRun: { type: 'boolean', description: 'Only report candidates, do not delete (default true)' },
|
|
1056
|
+
namespace: { type: 'string', description: 'Limit cleanup to one namespace' },
|
|
1057
|
+
},
|
|
1058
|
+
},
|
|
1059
|
+
handler: async (input) => {
|
|
1060
|
+
await ensureInitialized();
|
|
1061
|
+
const { listEntries, deleteEntry } = await getMemoryFunctions();
|
|
1062
|
+
const dryRun = input.dryRun !== false; // default true
|
|
1063
|
+
const namespace = input.namespace ? String(input.namespace) : undefined;
|
|
1064
|
+
if (namespace) {
|
|
1065
|
+
const v = validateIdentifier(namespace, 'namespace');
|
|
1066
|
+
if (!v.valid)
|
|
1067
|
+
throw new Error(v.error);
|
|
1068
|
+
}
|
|
1069
|
+
const all = await listEntries({ limit: 100000, namespace });
|
|
1070
|
+
const now = Date.now();
|
|
1071
|
+
const expired = all.entries.filter(e => {
|
|
1072
|
+
const exp = e.expiresAt;
|
|
1073
|
+
if (!exp)
|
|
1074
|
+
return false;
|
|
1075
|
+
const t = typeof exp === 'number' ? exp : Date.parse(String(exp));
|
|
1076
|
+
return Number.isFinite(t) && t < now;
|
|
1077
|
+
});
|
|
1078
|
+
let freedBytes = 0;
|
|
1079
|
+
let deleted = 0;
|
|
1080
|
+
if (!dryRun) {
|
|
1081
|
+
for (const e of expired) {
|
|
1082
|
+
try {
|
|
1083
|
+
await deleteEntry({ key: e.key, namespace: e.namespace });
|
|
1084
|
+
freedBytes += e.size || 0;
|
|
1085
|
+
deleted++;
|
|
1086
|
+
}
|
|
1087
|
+
catch { /* ignore individual delete errors */ }
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
else {
|
|
1091
|
+
freedBytes = expired.reduce((s, e) => s + (e.size || 0), 0);
|
|
1092
|
+
}
|
|
1093
|
+
return {
|
|
1094
|
+
dryRun,
|
|
1095
|
+
candidates: { expired: expired.length, stale: 0, lowQuality: 0, total: expired.length },
|
|
1096
|
+
deleted: { entries: dryRun ? 0 : deleted, vectors: 0, patterns: 0 },
|
|
1097
|
+
freed: { bytes: freedBytes },
|
|
1098
|
+
note: dryRun ? 'dry run — re-run with dryRun:false to delete' : undefined,
|
|
1099
|
+
};
|
|
1100
|
+
},
|
|
1101
|
+
},
|
|
1102
|
+
{
|
|
1103
|
+
// #1916: `ruflo memory compress` referenced an unregistered tool. The
|
|
1104
|
+
// sql.js backend has no on-disk compression; this reports current sizes.
|
|
1105
|
+
name: 'memory_compress',
|
|
1106
|
+
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.',
|
|
1107
|
+
category: 'memory',
|
|
1108
|
+
inputSchema: { type: 'object', properties: {} },
|
|
1109
|
+
handler: async () => {
|
|
1110
|
+
await ensureInitialized();
|
|
1111
|
+
const { listEntries } = await getMemoryFunctions();
|
|
1112
|
+
const all = await listEntries({ limit: 100000 });
|
|
1113
|
+
const bytes = all.entries.reduce((s, e) => s + (e.size || 0), 0);
|
|
1114
|
+
const human = `${bytes}B`;
|
|
1115
|
+
const sizes = { totalSize: human, vectorsSize: 'n/a', textSize: human, patternsSize: 'n/a', indexSize: 'n/a' };
|
|
1116
|
+
return {
|
|
1117
|
+
before: sizes,
|
|
1118
|
+
after: sizes,
|
|
1119
|
+
compression: { ratio: 1, savedBytes: 0, method: 'none' },
|
|
1120
|
+
note: 'sql.js backend has no on-disk compression; nothing to compress. (RaBitQ embedding quantization is a separate feature.)',
|
|
1121
|
+
};
|
|
1122
|
+
},
|
|
1123
|
+
},
|
|
1124
|
+
{
|
|
1125
|
+
// #1916: `ruflo memory export -o <file>` referenced an unregistered tool.
|
|
1126
|
+
// Dumps entry metadata (and values when the backend returns them) to JSON.
|
|
1127
|
+
name: 'memory_export',
|
|
1128
|
+
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.)',
|
|
1129
|
+
category: 'memory',
|
|
1130
|
+
inputSchema: {
|
|
1131
|
+
type: 'object',
|
|
1132
|
+
properties: {
|
|
1133
|
+
outputPath: { type: 'string', description: 'File path to write the JSON export to' },
|
|
1134
|
+
format: { type: 'string', enum: ['json', 'csv'], description: 'Export format (csv falls back to json today)' },
|
|
1135
|
+
namespace: { type: 'string', description: 'Limit export to one namespace' },
|
|
1136
|
+
includeVectors: { type: 'boolean', description: 'Include embedding vectors (advisory — not exported yet)' },
|
|
1137
|
+
},
|
|
1138
|
+
required: ['outputPath'],
|
|
1139
|
+
},
|
|
1140
|
+
handler: async (input) => {
|
|
1141
|
+
await ensureInitialized();
|
|
1142
|
+
const { listEntries } = await getMemoryFunctions();
|
|
1143
|
+
const outputPath = String(input.outputPath ?? '');
|
|
1144
|
+
if (!outputPath)
|
|
1145
|
+
return { error: 'outputPath is required' };
|
|
1146
|
+
const namespace = input.namespace ? String(input.namespace) : undefined;
|
|
1147
|
+
if (namespace) {
|
|
1148
|
+
const v = validateIdentifier(namespace, 'namespace');
|
|
1149
|
+
if (!v.valid)
|
|
1150
|
+
throw new Error(v.error);
|
|
1151
|
+
}
|
|
1152
|
+
const all = await listEntries({ limit: 100000, namespace });
|
|
1153
|
+
const payload = {
|
|
1154
|
+
schema: 'ruflo-memory-export/v1',
|
|
1155
|
+
exportedAt: new Date().toISOString(),
|
|
1156
|
+
namespace: namespace ?? null,
|
|
1157
|
+
count: all.entries.length,
|
|
1158
|
+
entries: all.entries.map(e => ({
|
|
1159
|
+
key: e.key, namespace: e.namespace, value: e.value ?? null,
|
|
1160
|
+
createdAt: e.createdAt, updatedAt: e.updatedAt, accessCount: e.accessCount, hasEmbedding: e.hasEmbedding, size: e.size,
|
|
1161
|
+
})),
|
|
1162
|
+
};
|
|
1163
|
+
try {
|
|
1164
|
+
writeFileSync(outputPath, JSON.stringify(payload, null, 2), 'utf-8');
|
|
1165
|
+
}
|
|
1166
|
+
catch (e) {
|
|
1167
|
+
return { error: `Could not write ${outputPath}: ${e.message}` };
|
|
1168
|
+
}
|
|
1169
|
+
const vectorsWithEmb = all.entries.filter(e => e.hasEmbedding).length;
|
|
1170
|
+
return {
|
|
1171
|
+
outputPath,
|
|
1172
|
+
format: input.format || 'json',
|
|
1173
|
+
exported: { entries: all.entries.length, vectors: vectorsWithEmb, patterns: 0 },
|
|
1174
|
+
fileSize: `${Buffer.byteLength(JSON.stringify(payload))}B`,
|
|
1175
|
+
note: input.format === 'csv' ? 'CSV not implemented yet — wrote JSON' : undefined,
|
|
1176
|
+
};
|
|
1177
|
+
},
|
|
1178
|
+
},
|
|
1179
|
+
{
|
|
1180
|
+
// #1916: `ruflo memory import <file>` referenced an unregistered tool.
|
|
1181
|
+
// Reads a ruflo-memory-export JSON and re-stores each entry.
|
|
1182
|
+
name: 'memory_import',
|
|
1183
|
+
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.',
|
|
1184
|
+
category: 'memory',
|
|
1185
|
+
inputSchema: {
|
|
1186
|
+
type: 'object',
|
|
1187
|
+
properties: {
|
|
1188
|
+
inputPath: { type: 'string', description: 'Path to the JSON export file' },
|
|
1189
|
+
merge: { type: 'boolean', description: 'Merge into existing entries (upsert) vs. fail on conflict (default true)' },
|
|
1190
|
+
namespace: { type: 'string', description: 'Override the namespace for all imported entries' },
|
|
1191
|
+
},
|
|
1192
|
+
required: ['inputPath'],
|
|
1193
|
+
},
|
|
1194
|
+
handler: async (input) => {
|
|
1195
|
+
await ensureInitialized();
|
|
1196
|
+
const { storeEntry } = await getMemoryFunctions();
|
|
1197
|
+
const t0 = Date.now();
|
|
1198
|
+
const inputPath = String(input.inputPath ?? '');
|
|
1199
|
+
if (!inputPath || !existsSync(inputPath))
|
|
1200
|
+
return { error: `File not found: ${inputPath || '(empty)'}` };
|
|
1201
|
+
let doc;
|
|
1202
|
+
try {
|
|
1203
|
+
doc = JSON.parse(readFileSync(inputPath, 'utf-8'));
|
|
1204
|
+
}
|
|
1205
|
+
catch (e) {
|
|
1206
|
+
return { error: `Invalid export JSON: ${e.message}` };
|
|
1207
|
+
}
|
|
1208
|
+
const entries = Array.isArray(doc.entries) ? doc.entries : [];
|
|
1209
|
+
const nsOverride = input.namespace ? String(input.namespace) : undefined;
|
|
1210
|
+
if (nsOverride) {
|
|
1211
|
+
const v = validateIdentifier(nsOverride, 'namespace');
|
|
1212
|
+
if (!v.valid)
|
|
1213
|
+
throw new Error(v.error);
|
|
1214
|
+
}
|
|
1215
|
+
let imported = 0;
|
|
1216
|
+
let skipped = 0;
|
|
1217
|
+
for (const e of entries) {
|
|
1218
|
+
if (!e || typeof e.key !== 'string') {
|
|
1219
|
+
skipped++;
|
|
1220
|
+
continue;
|
|
1221
|
+
}
|
|
1222
|
+
const value = typeof e.value === 'string' ? e.value : JSON.stringify(e.value ?? null);
|
|
1223
|
+
try {
|
|
1224
|
+
await storeEntry({ key: e.key, value, namespace: nsOverride ?? e.namespace ?? 'default', upsert: input.merge !== false });
|
|
1225
|
+
imported++;
|
|
1226
|
+
}
|
|
1227
|
+
catch {
|
|
1228
|
+
skipped++;
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
return {
|
|
1232
|
+
inputPath,
|
|
1233
|
+
imported: { entries: imported, vectors: 0, patterns: 0 },
|
|
1234
|
+
skipped,
|
|
1235
|
+
duration: Date.now() - t0,
|
|
1236
|
+
};
|
|
1237
|
+
},
|
|
1238
|
+
},
|
|
817
1239
|
];
|
|
818
1240
|
//# sourceMappingURL=memory-tools.js.map
|