claude-flow 3.59.12 → 3.59.14
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 +228 -11
- package/.claude/proven-config.json +1 -1
- package/package.json +1 -1
- package/v3/@claude-flow/cli/catalog-manifest.json +2 -2
- package/v3/@claude-flow/cli/dist/src/mcp-tools/claims-tools.d.ts +1 -1
- package/v3/@claude-flow/cli/dist/src/mcp-tools/claims-tools.js +4 -3
- package/v3/@claude-flow/cli/dist/src/mcp-tools/memory-tools.js +41 -11
- package/v3/@claude-flow/cli/dist/src/mcp-tools/workflow-tools.js +3 -2
- package/v3/@claude-flow/cli/package.json +1 -1
|
@@ -24,9 +24,10 @@ const helpersDir = __dirname;
|
|
|
24
24
|
const helperRoot = path.resolve(helpersDir, '..', '..');
|
|
25
25
|
|
|
26
26
|
// Resolve an installed @claude-flow/cli (or ruflo) bin — mirrors
|
|
27
|
-
// statusline-generator.ts's resolveCliBin() candidate list
|
|
28
|
-
//
|
|
29
|
-
//
|
|
27
|
+
// statusline-generator.ts's resolveCliBin() candidate list, then looks for a
|
|
28
|
+
// global install (resolveGlobalCliBin() below, #3368). Used only to spawn the
|
|
29
|
+
// detached funnel-refresh helper below; failures are silent (no candidate
|
|
30
|
+
// found just means the refresh falls back to npx).
|
|
30
31
|
//
|
|
31
32
|
// Verifies dist/src/index.js exists alongside bin/cli.js, not just the bin
|
|
32
33
|
// itself — Claude Code's own plugin marketplace mechanism installs by
|
|
@@ -39,7 +40,17 @@ const helperRoot = path.resolve(helpersDir, '..', '..');
|
|
|
39
40
|
// first every time and spawnDetachedFunnelRefresh() below had no fallback,
|
|
40
41
|
// so the promo/disclosure row could never populate for any marketplace
|
|
41
42
|
// install, on any OS.
|
|
43
|
+
// session-restore asks up to three times (first-run enable, then the two
|
|
44
|
+
// refreshes). The answer cannot change inside one short-lived hook process,
|
|
45
|
+
// so resolve once: `undefined` means "not looked up yet", null means "no
|
|
46
|
+
// local or global CLI — use npx".
|
|
47
|
+
let cliBinCache;
|
|
42
48
|
function resolveCliBinForHook() {
|
|
49
|
+
if (cliBinCache === undefined) cliBinCache = resolveCliBinUncached();
|
|
50
|
+
return cliBinCache;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function resolveCliBinUncached() {
|
|
43
54
|
try {
|
|
44
55
|
const home = os.homedir();
|
|
45
56
|
const candidates = [
|
|
@@ -53,16 +64,219 @@ function resolveCliBinForHook() {
|
|
|
53
64
|
path.join(helpersDir, '..', '..', 'bin', 'cli.js'),
|
|
54
65
|
];
|
|
55
66
|
for (const p of candidates) {
|
|
56
|
-
|
|
57
|
-
if (fs.existsSync(p) && fs.existsSync(path.join(path.dirname(p), '..', 'dist', 'src', 'index.js'))) {
|
|
58
|
-
return p;
|
|
59
|
-
}
|
|
60
|
-
} catch (e) { /* try next candidate */ }
|
|
67
|
+
if (isRunnableCli(p)) return p;
|
|
61
68
|
}
|
|
69
|
+
return resolveGlobalCliBin();
|
|
62
70
|
} catch (e) { /* ignore */ }
|
|
63
71
|
return null;
|
|
64
72
|
}
|
|
65
73
|
|
|
74
|
+
function isRunnableCli(p) {
|
|
75
|
+
try {
|
|
76
|
+
return fs.existsSync(p) && fs.existsSync(path.join(path.dirname(p), '..', 'dist', 'src', 'index.js'));
|
|
77
|
+
} catch (e) { return false; }
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// #3368: a global install — the README's `npm install -g ruflo@latest` — keeps
|
|
81
|
+
// the CLI at <prefix>/lib/node_modules/ruflo/node_modules/@claude-flow/cli,
|
|
82
|
+
// which none of the project-relative candidates above cover, so on those
|
|
83
|
+
// machines every refresh fell back to npx: a second, separately cached copy
|
|
84
|
+
// of the CLI that follows the registry's latest rather than the version the
|
|
85
|
+
// user pinned (the same npx-cache skew as #3306), and that cannot start
|
|
86
|
+
// offline on a cold cache.
|
|
87
|
+
//
|
|
88
|
+
// Three lookups, fs calls only (no child process), reached only after every
|
|
89
|
+
// candidate above missed:
|
|
90
|
+
// 1. npm's global prefix, resolved the way npm resolves it
|
|
91
|
+
// (npmGlobalPrefix() below). First because it does not depend on PATH:
|
|
92
|
+
// a hook inherits the host process's PATH, which need not contain the
|
|
93
|
+
// prefix's bin/ even when the user's interactive shell does (observed:
|
|
94
|
+
// the Claude Code process spawning hooks lacked ~/.npm-global/bin while
|
|
95
|
+
// a login zsh on the same machine had it).
|
|
96
|
+
// 2. `ruflo`, then `claude-flow`, on PATH — the order plugins/ruflo-core/
|
|
97
|
+
// scripts/ruflo-hook.cjs uses — for globals npm did not install (yarn
|
|
98
|
+
// classic, bun). Only a symlinked bin counts, and only when it lands in
|
|
99
|
+
// a package that declares one of the three known names, so a wrapper
|
|
100
|
+
// script or a version-manager shim (Volta) is never mistaken for an
|
|
101
|
+
// install. The <dir>/node_modules/<name> layout npm uses on Windows,
|
|
102
|
+
// where ruflo.cmd sits beside node_modules\ruflo (what resolveNpmShim()
|
|
103
|
+
// in ruflo-hook.cjs maps), is only consulted on win32: on POSIX every
|
|
104
|
+
// manager symlinks, and probing it there would follow a Windows install
|
|
105
|
+
// exposed to WSL through the appended Windows PATH.
|
|
106
|
+
// Relative PATH entries (`.` or an empty entry) are skipped, so a bare
|
|
107
|
+
// `./ruflo` in the working directory is never picked up.
|
|
108
|
+
// 3. ~/.npm-global, npm's documented no-sudo prefix, when 1 and 2 both
|
|
109
|
+
// missed: the prefix setting may live where this hook's node never
|
|
110
|
+
// reads it, and a wrapper script on PATH is skipped by 2.
|
|
111
|
+
// `env`/`platform`/`execPath` are parameters so any layout is testable
|
|
112
|
+
// from any OS.
|
|
113
|
+
function resolveGlobalCliBin(env = process.env, platform = process.platform, execPath = process.execPath) {
|
|
114
|
+
const prefix = npmGlobalPrefix(env, platform, execPath);
|
|
115
|
+
if (prefix) {
|
|
116
|
+
const hit = cliUnderPrefix(prefix, platform);
|
|
117
|
+
if (hit) return hit;
|
|
118
|
+
}
|
|
119
|
+
const dirs = String(env.PATH || env.Path || '').split(platform === 'win32' ? ';' : ':');
|
|
120
|
+
const exts = platform === 'win32' ? ['.cmd', ''] : [''];
|
|
121
|
+
for (const name of ['ruflo', 'claude-flow']) {
|
|
122
|
+
for (const dir of dirs) {
|
|
123
|
+
if (!dir || !path.isAbsolute(dir)) continue;
|
|
124
|
+
for (const ext of exts) {
|
|
125
|
+
const shim = path.join(dir, name + ext);
|
|
126
|
+
if (!fs.existsSync(shim)) continue;
|
|
127
|
+
const roots = [];
|
|
128
|
+
try {
|
|
129
|
+
if (fs.lstatSync(shim).isSymbolicLink()) {
|
|
130
|
+
const root = path.dirname(path.dirname(fs.realpathSync(shim)));
|
|
131
|
+
if (isKnownPackageRoot(root)) roots.push(root);
|
|
132
|
+
}
|
|
133
|
+
} catch (e) { /* unreadable link — fall back to the layout roots */ }
|
|
134
|
+
if (platform === 'win32') {
|
|
135
|
+
roots.push(path.join(dir, 'node_modules', name));
|
|
136
|
+
if (name === 'claude-flow') roots.push(path.join(dir, 'node_modules', '@claude-flow', 'cli'));
|
|
137
|
+
}
|
|
138
|
+
for (const root of roots) {
|
|
139
|
+
const hit = cliInPackage(root);
|
|
140
|
+
if (hit) return hit;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// Last resort: ~/.npm-global, the prefix npm's own docs recommend for
|
|
146
|
+
// global installs without sudo ("Resolving EACCES permissions errors").
|
|
147
|
+
// The setting that points npm there can live where this hook's node never
|
|
148
|
+
// looks (another node's builtin npmrc, or a wrapper script that sets it
|
|
149
|
+
// per call), and a `ruflo` wrapper script on PATH is skipped above by
|
|
150
|
+
// design. Without this, such a machine fell back to npx @latest on every
|
|
151
|
+
// hook. Checked only after the configured prefix and PATH both miss, so it
|
|
152
|
+
// can never override either.
|
|
153
|
+
const home = env.HOME || env.USERPROFILE;
|
|
154
|
+
if (home && path.isAbsolute(home)) {
|
|
155
|
+
const hit = cliUnderPrefix(path.join(home, '.npm-global'), platform);
|
|
156
|
+
if (hit) return hit;
|
|
157
|
+
}
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// The CLI of a global install under an npm prefix: <prefix>/lib/node_modules
|
|
162
|
+
// on POSIX, <prefix>\node_modules on Windows, trying the three published
|
|
163
|
+
// package names in order.
|
|
164
|
+
function cliUnderPrefix(prefix, platform) {
|
|
165
|
+
const modules = platform === 'win32'
|
|
166
|
+
? path.join(prefix, 'node_modules')
|
|
167
|
+
: path.join(prefix, 'lib', 'node_modules');
|
|
168
|
+
for (const pkg of ['ruflo', 'claude-flow', path.join('@claude-flow', 'cli')]) {
|
|
169
|
+
const hit = cliInPackage(path.join(modules, pkg));
|
|
170
|
+
if (hit) return hit;
|
|
171
|
+
}
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// A bin on PATH only counts as an install when the package it points into
|
|
176
|
+
// declares one of the three published names. This is a narrower check than
|
|
177
|
+
// resolveNpmShim() in ruflo-hook.cjs, which never reads `name`: that one takes
|
|
178
|
+
// the package name from the shim's own filename, then validates package.json's
|
|
179
|
+
// `bin` entry and that its realpath stays inside the package. Here the bin has
|
|
180
|
+
// already been resolved to a package root, so the manifest `name` is what
|
|
181
|
+
// identifies it.
|
|
182
|
+
function isKnownPackageRoot(root) {
|
|
183
|
+
try {
|
|
184
|
+
const name = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf-8')).name;
|
|
185
|
+
return name === 'ruflo' || name === 'claude-flow' || name === '@claude-flow/cli';
|
|
186
|
+
} catch (e) { return false; }
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Map an installed ruflo / claude-flow / @claude-flow/cli package root to the
|
|
190
|
+
// CLI it runs: <root>/bin/cli.js (@claude-flow/cli itself),
|
|
191
|
+
// <root>/v3/@claude-flow/cli/bin/cli.js (the claude-flow umbrella bundles
|
|
192
|
+
// it), or @claude-flow/cli in the node_modules npm nests inside <root> —
|
|
193
|
+
// or, for the flat layout yarn classic and bun use, in the node_modules
|
|
194
|
+
// folder that holds <root> itself. That is the dependency edge
|
|
195
|
+
// ruflo/bin/ruflo.js's findCliPath() follows. The search stops at that one
|
|
196
|
+
// enclosing node_modules: walking further up would reach unrelated trees,
|
|
197
|
+
// such as a stray ~/node_modules left by an `npm i` in a home directory.
|
|
198
|
+
// Every hit must still pass isRunnableCli().
|
|
199
|
+
function cliInPackage(root) {
|
|
200
|
+
if (!fs.existsSync(root)) return null;
|
|
201
|
+
const candidates = [
|
|
202
|
+
path.join(root, 'bin', 'cli.js'),
|
|
203
|
+
path.join(root, 'v3', '@claude-flow', 'cli', 'bin', 'cli.js'),
|
|
204
|
+
path.join(root, 'node_modules', '@claude-flow', 'cli', 'bin', 'cli.js'),
|
|
205
|
+
];
|
|
206
|
+
for (let dir = path.dirname(root), i = 0; i < 4 && dir !== path.dirname(dir); i++, dir = path.dirname(dir)) {
|
|
207
|
+
if (path.basename(dir) === 'node_modules') {
|
|
208
|
+
candidates.push(path.join(dir, '@claude-flow', 'cli', 'bin', 'cli.js'));
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return candidates.find(isRunnableCli) || null;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// npm's global prefix without spawning npm (`npm prefix -g` measured 70-80 ms
|
|
216
|
+
// per call, paid on every SessionStart that gets this far). Same precedence
|
|
217
|
+
// npm applies:
|
|
218
|
+
// 1. npm_config_prefix in the environment (npm accepts either case);
|
|
219
|
+
// 2. `prefix=` in the user npmrc ($npm_config_userconfig, else ~/.npmrc),
|
|
220
|
+
// where `npm config set prefix ~/.npm-global` (the npm docs' fix for
|
|
221
|
+
// EACCES on global installs) writes it;
|
|
222
|
+
// 3. `prefix=` in npm's builtin npmrc (<npm package>/npmrc), found through
|
|
223
|
+
// the `npm` link beside node: Homebrew writes `prefix = /opt/homebrew`
|
|
224
|
+
// there (its process.execPath is the Cellar path, so step 4 alone would
|
|
225
|
+
// be wrong), and the Node.js Windows installer ships
|
|
226
|
+
// `prefix=${APPDATA}\npm`;
|
|
227
|
+
// 4. npm's default: $PREFIX, else the directory above node's bin/ (POSIX)
|
|
228
|
+
// or node.exe's own directory (Windows).
|
|
229
|
+
// Values follow npm's ini handling for the cases that decide a path: a
|
|
230
|
+
// quoted value is taken literally, an unquoted `;`/`#` starts a comment,
|
|
231
|
+
// keys below a `[section]` header are ignored, `${VAR}` expands when the
|
|
232
|
+
// variable is set (and `${VAR?}` to empty when it is not) but otherwise
|
|
233
|
+
// stays literal like npm — which leaves the value relative, so the
|
|
234
|
+
// isAbsolute() check below rejects it instead of pointing at the drive root.
|
|
235
|
+
// Not emulated: escaped characters and npm's other ini corner cases; each of
|
|
236
|
+
// those produces a miss, never a wrong CLI. The npmrc is read into memory,
|
|
237
|
+
// but only the value of its last top-level `prefix =` line is kept: no other
|
|
238
|
+
// line (an `_authToken`, say) is returned, stored or logged.
|
|
239
|
+
function npmGlobalPrefix(env = process.env, platform = process.platform, execPath = process.execPath) {
|
|
240
|
+
const home = (platform === 'win32' ? env.USERPROFILE : env.HOME) || os.homedir();
|
|
241
|
+
const readPrefix = (file) => {
|
|
242
|
+
let value = null;
|
|
243
|
+
try {
|
|
244
|
+
for (const line of fs.readFileSync(file, 'utf-8').split(/\r?\n/)) {
|
|
245
|
+
if (/^\s*\[/.test(line)) break; // only the top-level section applies
|
|
246
|
+
const m = /^\s*prefix\s*=\s*(.*?)\s*$/.exec(line);
|
|
247
|
+
if (m) value = m[1]; // last one wins, as in npm's ini parser
|
|
248
|
+
}
|
|
249
|
+
} catch (e) { /* no such npmrc */ }
|
|
250
|
+
return value;
|
|
251
|
+
};
|
|
252
|
+
const nodeDir = path.dirname(execPath);
|
|
253
|
+
// npm's builtin npmrc sits in npm's own package dir. On Windows that is
|
|
254
|
+
// node_modules\npm beside node.exe; elsewhere follow the `npm` link beside
|
|
255
|
+
// node (<npm>/bin/npm-cli.js) — on Homebrew it points out of the node keg
|
|
256
|
+
// to /opt/homebrew/lib/node_modules/npm.
|
|
257
|
+
let builtinRc = path.join(nodeDir, 'node_modules', 'npm', 'npmrc');
|
|
258
|
+
if (platform !== 'win32') {
|
|
259
|
+
try {
|
|
260
|
+
builtinRc = path.join(path.dirname(path.dirname(fs.realpathSync(path.join(nodeDir, 'npm')))), 'npmrc');
|
|
261
|
+
} catch (e) { /* no npm beside node */ }
|
|
262
|
+
}
|
|
263
|
+
const raw = env.npm_config_prefix || env.NPM_CONFIG_PREFIX
|
|
264
|
+
|| readPrefix(env.npm_config_userconfig || env.NPM_CONFIG_USERCONFIG || path.join(home, '.npmrc'))
|
|
265
|
+
|| readPrefix(builtinRc)
|
|
266
|
+
|| env.PREFIX
|
|
267
|
+
|| (platform === 'win32' ? nodeDir : path.dirname(nodeDir));
|
|
268
|
+
let value = String(raw).trim();
|
|
269
|
+
const quoted = /^(["'])([\s\S]*)\1$/.exec(value);
|
|
270
|
+
if (quoted) value = quoted[2];
|
|
271
|
+
else value = value.replace(/(^|[^\\])[;#].*$/, '$1').trim();
|
|
272
|
+
value = value
|
|
273
|
+
.replace(/\$\{([^}?]+)(\?)?\}/g, (whole, name, optional) => (
|
|
274
|
+
env[name] !== undefined ? env[name] : (optional ? '' : whole)
|
|
275
|
+
))
|
|
276
|
+
.replace(/^~(?=$|[\\/])/, home);
|
|
277
|
+
return path.isAbsolute(value) ? path.resolve(value) : null;
|
|
278
|
+
}
|
|
279
|
+
|
|
66
280
|
// Fire-and-forget doesn't work when the CALLER is itself a short-lived
|
|
67
281
|
// subprocess (confirmed live: two consecutive statusline renders 5s apart
|
|
68
282
|
// both saw an empty funnel-messages-cache, because the async HTTPS fetch
|
|
@@ -74,8 +288,8 @@ function resolveCliBinForHook() {
|
|
|
74
288
|
// chance to write the cache. Never awaited here — must not add to
|
|
75
289
|
// SessionStart's own timeout budget.
|
|
76
290
|
//
|
|
77
|
-
// No usable local candidate (resolveCliBinForHook() returned null)
|
|
78
|
-
// back to npx: this call is detached/unref'd, so a slower npx cold-start
|
|
291
|
+
// No usable local or global candidate (resolveCliBinForHook() returned null)
|
|
292
|
+
// falls back to npx: this call is detached/unref'd, so a slower npx cold-start
|
|
79
293
|
// costs nothing perceptible — unlike the statusline's own synchronous
|
|
80
294
|
// render path, where local-first exists purely for per-render latency.
|
|
81
295
|
// `--prefer-offline` avoids a registry round trip for the tarball when
|
|
@@ -1060,4 +1274,7 @@ if (require.main === module) {
|
|
|
1060
1274
|
// Which sibling helpers loaded (all CommonJS, shipped as .cjs — #3555).
|
|
1061
1275
|
const loadedHelpers = { router: !!router, session: !!session, memory: !!memory, intelligence: !!intelligence };
|
|
1062
1276
|
|
|
1063
|
-
|
|
1277
|
+
// resolveGlobalCliBin/npmGlobalPrefix are exported for
|
|
1278
|
+
// hook-handler-global-cli.test.ts; resolveCliBinForHook for anyone checking
|
|
1279
|
+
// what a given machine resolves to without running a hook (#3368).
|
|
1280
|
+
module.exports = { runWithTimeout, INTELLIGENCE_TIMEOUT_MS, loadedHelpers, ownedByMod, MOD_OWNABLE_EVENTS, resolveCliBinForHook, resolveGlobalCliBin, npmGlobalPrefix, cliSpawnArgs, helperRoot, scanSessionStartSettingsForRisk, srFormatRiskFindingsForAutomaticBoot, srScanSettingsForRisk, srScanCommandStringForRisk, srScanAllowRuleForRisk, SR_DANGEROUS_COMMAND_WORDS, SR_SHELL_EXEC_WORDS, SR_DOWNLOADER_WORDS, SR_STANDALONE_RISK_PATTERNS };
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module @claude-flow/cli/mcp-tools/claims
|
|
8
8
|
*/
|
|
9
|
-
import type
|
|
9
|
+
import { type MCPTool } from './types.js';
|
|
10
10
|
export declare const claimsTools: MCPTool[];
|
|
11
11
|
export default claimsTools;
|
|
12
12
|
//# sourceMappingURL=claims-tools.d.ts.map
|
|
@@ -6,19 +6,20 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module @claude-flow/cli/mcp-tools/claims
|
|
8
8
|
*/
|
|
9
|
+
import { getProjectCwd } from './types.js';
|
|
9
10
|
import { readClaimsStore } from '../services/claims-store.js';
|
|
10
11
|
import { validateIdentifier, validateText } from './validate-input.js';
|
|
11
12
|
// File-based persistence
|
|
12
13
|
import { existsSync, writeFileSync, mkdirSync } from 'fs';
|
|
13
|
-
import {
|
|
14
|
+
import { resolve } from 'path';
|
|
14
15
|
const CLAIMS_DIR = '.claude-flow/claims';
|
|
15
16
|
const CLAIMS_FILE = 'claims.json';
|
|
16
17
|
const DEFAULT_MAX_CLAIMS = 5;
|
|
17
18
|
function getClaimsPath() {
|
|
18
|
-
return resolve(
|
|
19
|
+
return resolve(getProjectCwd(), CLAIMS_DIR, CLAIMS_FILE);
|
|
19
20
|
}
|
|
20
21
|
function ensureClaimsDir() {
|
|
21
|
-
const dir = resolve(CLAIMS_DIR);
|
|
22
|
+
const dir = resolve(getProjectCwd(), CLAIMS_DIR);
|
|
22
23
|
if (!existsSync(dir)) {
|
|
23
24
|
mkdirSync(dir, { recursive: true });
|
|
24
25
|
}
|
|
@@ -21,6 +21,17 @@ import { DEFAULT_SEARCH_THRESHOLD, nearMisses } from '../memory/search-defaults.
|
|
|
21
21
|
const MEMORY_DIR = '.swarm';
|
|
22
22
|
const LEGACY_MEMORY_FILE = 'store.json';
|
|
23
23
|
const LEGACY_MEMORY_DIR = '.claude-flow/memory';
|
|
24
|
+
/**
|
|
25
|
+
* #3196: allow an MCP caller to select the same explicit store the CLI accepts
|
|
26
|
+
* via `--path`. No path means the existing MCP default is unchanged; this is
|
|
27
|
+
* deliberately not an implicit `CLAUDE_FLOW_DB_PATH` override because the
|
|
28
|
+
* native/encrypted CLI store and the AgentDB sibling are separate backends.
|
|
29
|
+
*/
|
|
30
|
+
function resolveDbPathOption(inputDbPath) {
|
|
31
|
+
return typeof inputDbPath === 'string' && inputDbPath.trim().length > 0
|
|
32
|
+
? resolve(inputDbPath)
|
|
33
|
+
: undefined;
|
|
34
|
+
}
|
|
24
35
|
const MIGRATION_MARKER = '.migrated-to-sqlite';
|
|
25
36
|
function getMemoryDir() {
|
|
26
37
|
return resolve(MEMORY_DIR);
|
|
@@ -439,6 +450,7 @@ export const memoryTools = [
|
|
|
439
450
|
enum: ['user_claim', 'agent_output', 'system_observation', 'tool_result', 'unknown'],
|
|
440
451
|
description: 'ADR-323: who/what produced this value, so shared-namespace retrieval can filter by trust level instead of conflating a user\'s stated claim with an agent\'s own output. Default: "unknown".',
|
|
441
452
|
},
|
|
453
|
+
dbPath: { type: 'string', description: 'Explicit SQLite store to read/write; omit to use the MCP default (the native/AgentDB sibling).' },
|
|
442
454
|
},
|
|
443
455
|
required: ['key', 'value'],
|
|
444
456
|
},
|
|
@@ -447,7 +459,9 @@ export const memoryTools = [
|
|
|
447
459
|
if (missingKey) {
|
|
448
460
|
return { success: false, key: input.key, stored: false, hasEmbedding: false, ...missingKey };
|
|
449
461
|
}
|
|
450
|
-
|
|
462
|
+
const dbPath = resolveDbPathOption(input.dbPath);
|
|
463
|
+
if (!dbPath)
|
|
464
|
+
await ensureInitialized();
|
|
451
465
|
const { storeEntry } = await getMemoryFunctions();
|
|
452
466
|
const key = input.key;
|
|
453
467
|
const namespace = input.namespace || 'default';
|
|
@@ -486,6 +500,7 @@ export const memoryTools = [
|
|
|
486
500
|
ttl,
|
|
487
501
|
upsert,
|
|
488
502
|
provenanceType,
|
|
503
|
+
dbPath,
|
|
489
504
|
});
|
|
490
505
|
const duration = performance.now() - startTime;
|
|
491
506
|
return {
|
|
@@ -522,6 +537,7 @@ export const memoryTools = [
|
|
|
522
537
|
properties: {
|
|
523
538
|
key: { type: 'string', description: 'Memory key' },
|
|
524
539
|
namespace: { type: 'string', description: 'Namespace (default: "default")' },
|
|
540
|
+
dbPath: { type: 'string', description: 'Explicit SQLite store to read/write; omit to use the MCP default (the native/AgentDB sibling).' },
|
|
525
541
|
},
|
|
526
542
|
required: ['key'],
|
|
527
543
|
},
|
|
@@ -530,13 +546,15 @@ export const memoryTools = [
|
|
|
530
546
|
if (missingKey) {
|
|
531
547
|
return { key: input.key, namespace: input.namespace, value: null, found: false, ...missingKey };
|
|
532
548
|
}
|
|
533
|
-
|
|
549
|
+
const dbPath = resolveDbPathOption(input.dbPath);
|
|
550
|
+
if (!dbPath)
|
|
551
|
+
await ensureInitialized();
|
|
534
552
|
const { getEntry } = await getMemoryFunctions();
|
|
535
553
|
const key = input.key;
|
|
536
554
|
const namespace = input.namespace || 'default';
|
|
537
555
|
validateMemoryInput(key, undefined, undefined, namespace);
|
|
538
556
|
try {
|
|
539
|
-
const result = await getEntry({ key, namespace });
|
|
557
|
+
const result = await getEntry({ key, namespace, dbPath });
|
|
540
558
|
if (result.found && result.entry) {
|
|
541
559
|
// Try to parse JSON value
|
|
542
560
|
let value = result.entry.content;
|
|
@@ -594,6 +612,7 @@ export const memoryTools = [
|
|
|
594
612
|
items: { type: 'string', enum: ['user_claim', 'agent_output', 'system_observation', 'tool_result', 'unknown'] },
|
|
595
613
|
description: 'ADR-323: restrict results to these provenance types (e.g. exclude user_claim when fact-checking). Omit for no filtering. Enforced for standard and SmartRetrieval searches.',
|
|
596
614
|
},
|
|
615
|
+
dbPath: { type: 'string', description: 'Explicit SQLite store to read/write; omit to use the MCP default (the native/AgentDB sibling).' },
|
|
597
616
|
},
|
|
598
617
|
required: ['query'],
|
|
599
618
|
},
|
|
@@ -602,7 +621,9 @@ export const memoryTools = [
|
|
|
602
621
|
if (missingQuery) {
|
|
603
622
|
return { query: input.query, results: [], total: 0, ...missingQuery };
|
|
604
623
|
}
|
|
605
|
-
|
|
624
|
+
const dbPath = resolveDbPathOption(input.dbPath);
|
|
625
|
+
if (!dbPath)
|
|
626
|
+
await ensureInitialized();
|
|
606
627
|
const { searchEntries } = await getMemoryFunctions();
|
|
607
628
|
const query = input.query;
|
|
608
629
|
const provenanceFilter = input.provenance_filter;
|
|
@@ -649,6 +670,7 @@ export const memoryTools = [
|
|
|
649
670
|
limit: req.limit || limit * 3,
|
|
650
671
|
threshold: req.threshold ?? threshold,
|
|
651
672
|
provenanceFilter,
|
|
673
|
+
dbPath,
|
|
652
674
|
});
|
|
653
675
|
return {
|
|
654
676
|
results: r.results.map(e => ({
|
|
@@ -693,7 +715,7 @@ export const memoryTools = [
|
|
|
693
715
|
};
|
|
694
716
|
});
|
|
695
717
|
const smartNearMisses = results.length === 0 && thresholdWasDefault
|
|
696
|
-
? await nearMisses(searchEntries, { query, namespace, provenanceFilter })
|
|
718
|
+
? await nearMisses(searchEntries, { query, namespace, provenanceFilter, dbPath })
|
|
697
719
|
: undefined;
|
|
698
720
|
return {
|
|
699
721
|
query,
|
|
@@ -721,6 +743,7 @@ export const memoryTools = [
|
|
|
721
743
|
limit,
|
|
722
744
|
threshold,
|
|
723
745
|
provenanceFilter,
|
|
746
|
+
dbPath,
|
|
724
747
|
});
|
|
725
748
|
if (!result.success) {
|
|
726
749
|
return { query, results: [], total: 0, error: result.error };
|
|
@@ -744,7 +767,7 @@ export const memoryTools = [
|
|
|
744
767
|
};
|
|
745
768
|
});
|
|
746
769
|
const nearMissInfo = results.length === 0 && thresholdWasDefault
|
|
747
|
-
? await nearMisses(searchEntries, { query, namespace, provenanceFilter })
|
|
770
|
+
? await nearMisses(searchEntries, { query, namespace, provenanceFilter, dbPath })
|
|
748
771
|
: undefined;
|
|
749
772
|
return {
|
|
750
773
|
query,
|
|
@@ -775,6 +798,7 @@ export const memoryTools = [
|
|
|
775
798
|
properties: {
|
|
776
799
|
key: { type: 'string', description: 'Memory key' },
|
|
777
800
|
namespace: { type: 'string', description: 'Namespace (default: "default")' },
|
|
801
|
+
dbPath: { type: 'string', description: 'Explicit SQLite store to read/write; omit to use the MCP default (the native/AgentDB sibling).' },
|
|
778
802
|
},
|
|
779
803
|
required: ['key'],
|
|
780
804
|
},
|
|
@@ -783,13 +807,15 @@ export const memoryTools = [
|
|
|
783
807
|
if (missingKey) {
|
|
784
808
|
return { success: false, key: input.key, namespace: input.namespace, deleted: false, ...missingKey };
|
|
785
809
|
}
|
|
786
|
-
|
|
810
|
+
const dbPath = resolveDbPathOption(input.dbPath);
|
|
811
|
+
if (!dbPath)
|
|
812
|
+
await ensureInitialized();
|
|
787
813
|
const { deleteEntry } = await getMemoryFunctions();
|
|
788
814
|
const key = input.key;
|
|
789
815
|
const namespace = input.namespace || 'default';
|
|
790
816
|
validateMemoryInput(key, undefined, undefined, namespace);
|
|
791
817
|
try {
|
|
792
|
-
const result = await deleteEntry({ key, namespace });
|
|
818
|
+
const result = await deleteEntry({ key, namespace, dbPath });
|
|
793
819
|
return {
|
|
794
820
|
success: result.deleted,
|
|
795
821
|
key,
|
|
@@ -825,10 +851,13 @@ export const memoryTools = [
|
|
|
825
851
|
},
|
|
826
852
|
limit: { type: 'number', description: 'Maximum results (default: 50)' },
|
|
827
853
|
offset: { type: 'number', description: 'Offset for pagination (default: 0)' },
|
|
854
|
+
dbPath: { type: 'string', description: 'Explicit SQLite store to read/write; omit to use the MCP default (the native/AgentDB sibling).' },
|
|
828
855
|
},
|
|
829
856
|
},
|
|
830
857
|
handler: async (input) => {
|
|
831
|
-
|
|
858
|
+
const dbPath = resolveDbPathOption(input.dbPath);
|
|
859
|
+
if (!dbPath)
|
|
860
|
+
await ensureInitialized();
|
|
832
861
|
const { listEntries } = await getMemoryFunctions();
|
|
833
862
|
const namespace = input.namespace;
|
|
834
863
|
const tags = Array.isArray(input.tags)
|
|
@@ -847,6 +876,7 @@ export const memoryTools = [
|
|
|
847
876
|
tags,
|
|
848
877
|
limit,
|
|
849
878
|
offset,
|
|
879
|
+
dbPath,
|
|
850
880
|
});
|
|
851
881
|
const entries = result.entries.map(e => ({
|
|
852
882
|
key: e.key,
|
|
@@ -884,14 +914,14 @@ export const memoryTools = [
|
|
|
884
914
|
inputSchema: {
|
|
885
915
|
type: 'object',
|
|
886
916
|
properties: {
|
|
887
|
-
dbPath: { type: 'string', description: '
|
|
917
|
+
dbPath: { type: 'string', description: 'Explicit SQLite store to read/write; omit to use the MCP default (the native/AgentDB sibling).' },
|
|
888
918
|
},
|
|
889
919
|
},
|
|
890
920
|
handler: async (input) => {
|
|
891
921
|
if (input.dbPath !== undefined && (typeof input.dbPath !== 'string' || !input.dbPath.trim())) {
|
|
892
922
|
return memoryStatsUnavailable('dbPath must be a non-empty string');
|
|
893
923
|
}
|
|
894
|
-
const dbPath =
|
|
924
|
+
const dbPath = resolveDbPathOption(input.dbPath);
|
|
895
925
|
// An explicit read must not initialize or migrate the unrelated default store.
|
|
896
926
|
if (!dbPath)
|
|
897
927
|
await ensureInitialized();
|
|
@@ -583,7 +583,8 @@ export const workflowTools = [
|
|
|
583
583
|
}
|
|
584
584
|
// Sort by creation date (newest first)
|
|
585
585
|
workflows.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
586
|
-
//
|
|
586
|
+
// Count the matches before the limit cuts the page down
|
|
587
|
+
const total = workflows.length;
|
|
587
588
|
const limit = input.limit || 20;
|
|
588
589
|
workflows = workflows.slice(0, limit);
|
|
589
590
|
return {
|
|
@@ -595,7 +596,7 @@ export const workflowTools = [
|
|
|
595
596
|
createdAt: w.createdAt,
|
|
596
597
|
completedAt: w.completedAt,
|
|
597
598
|
})),
|
|
598
|
-
total
|
|
599
|
+
total,
|
|
599
600
|
filters: { status: input.status },
|
|
600
601
|
};
|
|
601
602
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-flow/cli",
|
|
3
|
-
"version": "3.59.
|
|
3
|
+
"version": "3.59.14",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Ruflo CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
|
|
6
6
|
"main": "dist/src/index.js",
|