@claude-flow/cli 3.41.0 → 3.41.2
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/helpers.manifest.json +2 -2
- package/catalog-manifest.json +2 -2
- package/dist/src/commands/memory.js +31 -0
- package/dist/src/memory/memory-bridge.d.ts +4 -0
- package/dist/src/memory/memory-bridge.js +66 -14
- package/dist/src/memory/sibling-store.d.ts +11 -0
- package/dist/src/memory/sibling-store.js +53 -0
- package/dist/src/services/daemon-autostart.js +25 -7
- package/node_modules/@claude-flow/codex/dist/cli.js +0 -0
- package/node_modules/@claude-flow/plugin-agent-federation/dist/bin.js +0 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest": {
|
|
3
|
-
"version": "3.41.
|
|
3
|
+
"version": "3.41.2",
|
|
4
4
|
"files": {
|
|
5
5
|
"auto-memory-hook.mjs": "85fe05c757421c52137c0bc8545a0896bab6b4714538c2a11d1d0835bfcc8c1c",
|
|
6
6
|
"hook-handler.cjs": "209d9fafe10e17d1be0866727f6f9cf9ac66f9a0793f1c793a4f58319e8e4583",
|
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
"statusline.cjs": "4a48353b4f1566fa4379b00fd0321b6676a22b6cc91fbbd8380ac5183d619468"
|
|
9
9
|
}
|
|
10
10
|
},
|
|
11
|
-
"signature": "
|
|
11
|
+
"signature": "cmz1Yt39LkaWY+8JH1BOtqD27A3TYoFbpHnMGTpKZDzpuhgq1RQCF9P90z1yrnnStOSX0e01meDsHeRoGQgtAA==",
|
|
12
12
|
"algorithm": "ed25519"
|
|
13
13
|
}
|
package/catalog-manifest.json
CHANGED
|
@@ -7,6 +7,25 @@ import { select, confirm, input } from '../prompt.js';
|
|
|
7
7
|
import { callMCPTool, MCPClientError } from '../mcp-client.js';
|
|
8
8
|
import { distillCommand } from './memory-distill.js';
|
|
9
9
|
import { backupCommand } from './memory-backup.js';
|
|
10
|
+
import { countSiblingStoreRows } from '../memory/sibling-store.js';
|
|
11
|
+
import { resolveDbPath } from '../memory/memory-initializer.js';
|
|
12
|
+
/**
|
|
13
|
+
* #3228: a miss in one store is not a miss in the memory.
|
|
14
|
+
*
|
|
15
|
+
* 3.41.1 disclosed the sibling AgentDB store from `memory list` only. `retrieve`,
|
|
16
|
+
* `search` and `stats` kept reporting a clean negative — "Key not found", "No
|
|
17
|
+
* results" — while the rows sat in the file this interface does not read. On the
|
|
18
|
+
* reported Windows install that is 31,673 rows in the other store answering
|
|
19
|
+
* `found:false`. A confident negative is worse than an error, because nothing
|
|
20
|
+
* prompts anyone to look further.
|
|
21
|
+
*/
|
|
22
|
+
async function warnIfSiblingHasRows(pathFlag) {
|
|
23
|
+
const unread = await countSiblingStoreRows(resolveDbPath(pathFlag));
|
|
24
|
+
if (unread && unread.rows > 0) {
|
|
25
|
+
output.printWarning(`This read covered one store. ${unread.rows} entries are in ${unread.path} and were not searched. ` +
|
|
26
|
+
`That store is written by the MCP/AgentDB path; read it with --path ${unread.path}.`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
10
29
|
// Memory backends
|
|
11
30
|
const BACKENDS = [
|
|
12
31
|
{ value: 'agentdb', label: 'AgentDB', hint: 'Vector database with HNSW indexing (150x-12,500x faster)' },
|
|
@@ -285,6 +304,7 @@ const retrieveCommand = {
|
|
|
285
304
|
}
|
|
286
305
|
if (!result.found || !result.entry) {
|
|
287
306
|
output.printWarning(`Key not found: ${key}`);
|
|
307
|
+
await warnIfSiblingHasRows(ctx.flags.path);
|
|
288
308
|
return { success: false, exitCode: 1, data: { key, found: false } };
|
|
289
309
|
}
|
|
290
310
|
const entry = result.entry;
|
|
@@ -665,6 +685,7 @@ const searchCommand = {
|
|
|
665
685
|
output.writeln();
|
|
666
686
|
if (results.length === 0) {
|
|
667
687
|
output.printWarning('No results found');
|
|
688
|
+
await warnIfSiblingHasRows(ctx.flags.path);
|
|
668
689
|
output.writeln(output.dim('Try: claude-flow memory store -k "key" --value "data"'));
|
|
669
690
|
return { success: true, data: [] };
|
|
670
691
|
}
|
|
@@ -744,6 +765,7 @@ const listCommand = {
|
|
|
744
765
|
output.writeln();
|
|
745
766
|
if (entries.length === 0) {
|
|
746
767
|
output.printWarning('No entries found');
|
|
768
|
+
await warnIfSiblingHasRows(ctx.flags.path);
|
|
747
769
|
output.printInfo('Store data: claude-flow memory store -k "key" --value "data"');
|
|
748
770
|
return { success: true, data: [] };
|
|
749
771
|
}
|
|
@@ -760,6 +782,15 @@ const listCommand = {
|
|
|
760
782
|
});
|
|
761
783
|
output.writeln();
|
|
762
784
|
output.printInfo(`Showing ${entries.length} of ${listResult.total} entries`);
|
|
785
|
+
// #3196: AgentDB owns a sibling store next to this one. `total` counts only
|
|
786
|
+
// the file we read, so a bare count reads as "this is everything" while rows
|
|
787
|
+
// sit unreadable next door. Silence would be recoverable; a confident wrong
|
|
788
|
+
// total is not, because nothing prompts anyone to look further.
|
|
789
|
+
const unread = await countSiblingStoreRows(resolveDbPath(ctx.flags.path));
|
|
790
|
+
if (unread && unread.rows > 0) {
|
|
791
|
+
output.printWarning(`${unread.rows} more entries are in ${unread.path} and were not read here. ` +
|
|
792
|
+
`That store is written by the MCP/AgentDB path; read it with --path ${unread.path}.`);
|
|
793
|
+
}
|
|
763
794
|
return { success: true, data: listResult.entries };
|
|
764
795
|
}
|
|
765
796
|
catch (error) {
|
|
@@ -34,6 +34,10 @@ export declare function shouldDisableNativeBridge(platform?: NodeJS.Platform, en
|
|
|
34
34
|
* noise. Suppress the banners, keep the bad news.
|
|
35
35
|
*/
|
|
36
36
|
export declare function shouldSuppressInitLog(msg: string): boolean;
|
|
37
|
+
/** Test seam: forget cached registries so a test can exercise a fresh open. */
|
|
38
|
+
export declare function _resetRegistryCacheForTest(): void;
|
|
39
|
+
/** #3196: the sibling store AgentDB owns next to a given sql.js database. */
|
|
40
|
+
export declare function siblingAgentDbPath(dbPath: string): string | null;
|
|
37
41
|
/**
|
|
38
42
|
* Create/migrate the bridge's `memory_entries` table on `db`.
|
|
39
43
|
*
|
|
@@ -19,9 +19,33 @@
|
|
|
19
19
|
import * as path from 'path';
|
|
20
20
|
import * as crypto from 'crypto';
|
|
21
21
|
import { createRequire } from 'node:module';
|
|
22
|
-
// ===== Lazy
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
// ===== Lazy registry cache, keyed by database path =====
|
|
23
|
+
/**
|
|
24
|
+
* #3196: this cache is keyed by resolved database path, and that is the whole
|
|
25
|
+
* point of it.
|
|
26
|
+
*
|
|
27
|
+
* It used to be a single global instance. The first caller to touch the bridge
|
|
28
|
+
* decided which file the process would use, and every later caller's explicit
|
|
29
|
+
* `dbPath` was accepted and then silently ignored — `getRegistry()` returned the
|
|
30
|
+
* already-built instance without ever comparing paths. A `memory store --path A`
|
|
31
|
+
* following an MCP write therefore read and wrote B, reported success, and left
|
|
32
|
+
* two valid corpora that neither interface could see whole.
|
|
33
|
+
*
|
|
34
|
+
* Keying by path makes an explicit path mean what it says. Two paths in one
|
|
35
|
+
* process are two registries, which is the behaviour the CLI's `--path` flag and
|
|
36
|
+
* `CLAUDE_FLOW_DB_PATH` have always advertised.
|
|
37
|
+
*/
|
|
38
|
+
const registryPromises = new Map();
|
|
39
|
+
const registryInstances = new Map();
|
|
40
|
+
/**
|
|
41
|
+
* Test seam: when set, every path resolves to this registry.
|
|
42
|
+
*
|
|
43
|
+
* Kept separate from the path cache on purpose. A test installs a fake registry
|
|
44
|
+
* and then calls a bridge function with its own temp `dbPath`; keying the
|
|
45
|
+
* override by path would mean the seam only worked for callers that happened to
|
|
46
|
+
* pass the same path the seam guessed, which is how #2968's fixture broke.
|
|
47
|
+
*/
|
|
48
|
+
let testRegistryOverride = null;
|
|
25
49
|
let bridgeAvailable = null;
|
|
26
50
|
// #2652/#2120: rows created before the status column existed receive NULL
|
|
27
51
|
// during migration. They are live rows, not tombstones. Every user-facing
|
|
@@ -172,10 +196,18 @@ async function getRegistry(dbPath) {
|
|
|
172
196
|
: 'AgentDB native bridge disabled by CLAUDE_FLOW_DISABLE_BRIDGE=1';
|
|
173
197
|
return null;
|
|
174
198
|
}
|
|
199
|
+
if (testRegistryOverride)
|
|
200
|
+
return testRegistryOverride;
|
|
175
201
|
if (bridgeAvailable === false)
|
|
176
202
|
return null;
|
|
177
|
-
|
|
178
|
-
|
|
203
|
+
// Resolve first, then cache on the resolved value: `undefined`, a relative
|
|
204
|
+
// path and its absolute form must not become three different registries over
|
|
205
|
+
// the same file.
|
|
206
|
+
const resolvedPath = dbPath ? path.resolve(dbPath) : getAgentDbPath();
|
|
207
|
+
const cached = registryInstances.get(resolvedPath);
|
|
208
|
+
if (cached)
|
|
209
|
+
return cached;
|
|
210
|
+
let registryPromise = registryPromises.get(resolvedPath);
|
|
179
211
|
if (!registryPromise) {
|
|
180
212
|
registryPromise = (async () => {
|
|
181
213
|
try {
|
|
@@ -193,7 +225,7 @@ async function getRegistry(dbPath) {
|
|
|
193
225
|
try {
|
|
194
226
|
await registry.initialize({
|
|
195
227
|
// #2786: use agentdb-memory.db (plaintext) so native better-sqlite3 doesn't hit the encrypted memory.db.
|
|
196
|
-
dbPath:
|
|
228
|
+
dbPath: resolvedPath,
|
|
197
229
|
embeddingModel: 'Xenova/all-MiniLM-L6-v2',
|
|
198
230
|
dimension: 384,
|
|
199
231
|
vectorBackend: 'auto',
|
|
@@ -440,7 +472,7 @@ async function getRegistry(dbPath) {
|
|
|
440
472
|
catch {
|
|
441
473
|
// Top-level catch — registry stays usable even if post-init wiring fails wholesale.
|
|
442
474
|
}
|
|
443
|
-
|
|
475
|
+
registryInstances.set(resolvedPath, registry);
|
|
444
476
|
bridgeAvailable = true;
|
|
445
477
|
bridgeFailureReason = null;
|
|
446
478
|
return registry;
|
|
@@ -451,13 +483,29 @@ async function getRegistry(dbPath) {
|
|
|
451
483
|
// makes the resulting sql.js-fallback refusal undiagnosable.
|
|
452
484
|
bridgeFailureReason = err instanceof Error ? err.message : String(err);
|
|
453
485
|
bridgeAvailable = false;
|
|
454
|
-
|
|
486
|
+
registryPromises.delete(resolvedPath);
|
|
455
487
|
return null;
|
|
456
488
|
}
|
|
457
489
|
})();
|
|
490
|
+
registryPromises.set(resolvedPath, registryPromise);
|
|
458
491
|
}
|
|
459
492
|
return registryPromise;
|
|
460
493
|
}
|
|
494
|
+
/** Test seam: forget cached registries so a test can exercise a fresh open. */
|
|
495
|
+
export function _resetRegistryCacheForTest() {
|
|
496
|
+
registryPromises.clear();
|
|
497
|
+
registryInstances.clear();
|
|
498
|
+
testRegistryOverride = null;
|
|
499
|
+
bridgeAvailable = null;
|
|
500
|
+
bridgeFailureReason = null;
|
|
501
|
+
}
|
|
502
|
+
/** #3196: the sibling store AgentDB owns next to a given sql.js database. */
|
|
503
|
+
export function siblingAgentDbPath(dbPath) {
|
|
504
|
+
if (!dbPath || dbPath === ':memory:')
|
|
505
|
+
return null;
|
|
506
|
+
const sibling = path.join(path.dirname(path.resolve(dbPath)), 'agentdb-memory.db');
|
|
507
|
+
return path.resolve(dbPath) === sibling ? null : sibling;
|
|
508
|
+
}
|
|
461
509
|
// ===== Phase 2: BM25 hybrid scoring =====
|
|
462
510
|
/**
|
|
463
511
|
* BM25 scoring for keyword-based search.
|
|
@@ -1738,8 +1786,9 @@ export function getBridgeFailureReason() {
|
|
|
1738
1786
|
* independent of package build order without changing production startup.
|
|
1739
1787
|
*/
|
|
1740
1788
|
export function __setMemoryBridgeRegistryForTests(registry) {
|
|
1741
|
-
|
|
1742
|
-
|
|
1789
|
+
registryPromises.clear();
|
|
1790
|
+
registryInstances.clear();
|
|
1791
|
+
testRegistryOverride = registry;
|
|
1743
1792
|
bridgeAvailable = registry ? true : null;
|
|
1744
1793
|
bridgeFailureReason = null;
|
|
1745
1794
|
}
|
|
@@ -1753,16 +1802,19 @@ export function __setMemoryBridgeRegistryForTests(registry) {
|
|
|
1753
1802
|
* therefore had no recovery path short of a restart.
|
|
1754
1803
|
*/
|
|
1755
1804
|
export async function shutdownBridge() {
|
|
1756
|
-
|
|
1805
|
+
// #3196: every cached registry owns an open database handle, so shutting down
|
|
1806
|
+
// one of several would leave the rest holding files open.
|
|
1807
|
+
for (const registry of registryInstances.values()) {
|
|
1757
1808
|
try {
|
|
1758
|
-
await
|
|
1809
|
+
await registry.shutdown();
|
|
1759
1810
|
}
|
|
1760
1811
|
catch {
|
|
1761
1812
|
// Best-effort
|
|
1762
1813
|
}
|
|
1763
1814
|
}
|
|
1764
|
-
|
|
1765
|
-
|
|
1815
|
+
registryInstances.clear();
|
|
1816
|
+
registryPromises.clear();
|
|
1817
|
+
testRegistryOverride = null;
|
|
1766
1818
|
bridgeAvailable = null;
|
|
1767
1819
|
bridgeFailureReason = null;
|
|
1768
1820
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface SiblingStoreReport {
|
|
2
|
+
path: string;
|
|
3
|
+
rows: number;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Count rows in the sibling AgentDB store, read-only. Returns null when there
|
|
7
|
+
* is no sibling, it does not exist, or it cannot be read — an unreadable store
|
|
8
|
+
* is not evidence of an empty one, so we stay silent rather than claim zero.
|
|
9
|
+
*/
|
|
10
|
+
export declare function countSiblingStoreRows(dbPath: string): Promise<SiblingStoreReport | null>;
|
|
11
|
+
//# sourceMappingURL=sibling-store.d.ts.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #3196: report the store this interface is NOT reading.
|
|
3
|
+
*
|
|
4
|
+
* `memory.db` (sql.js, encrypted at rest when enabled) and `agentdb-memory.db`
|
|
5
|
+
* (native better-sqlite3, plaintext) are deliberately separate files — see
|
|
6
|
+
* #2786; pointing native at an encrypted file fails and silently disables the
|
|
7
|
+
* learning system. Both are legitimate stores, and a read of one is not a read
|
|
8
|
+
* of the other.
|
|
9
|
+
*
|
|
10
|
+
* The danger is not the split. It is a count that describes one file as though
|
|
11
|
+
* it described the memory. This module exists so the CLI can say what it did
|
|
12
|
+
* not read, without opening, migrating or modifying that file.
|
|
13
|
+
*/
|
|
14
|
+
import { existsSync } from 'node:fs';
|
|
15
|
+
import { siblingAgentDbPath } from './memory-bridge.js';
|
|
16
|
+
/**
|
|
17
|
+
* Count rows in the sibling AgentDB store, read-only. Returns null when there
|
|
18
|
+
* is no sibling, it does not exist, or it cannot be read — an unreadable store
|
|
19
|
+
* is not evidence of an empty one, so we stay silent rather than claim zero.
|
|
20
|
+
*/
|
|
21
|
+
export async function countSiblingStoreRows(dbPath) {
|
|
22
|
+
const sibling = siblingAgentDbPath(dbPath);
|
|
23
|
+
if (!sibling || !existsSync(sibling))
|
|
24
|
+
return null;
|
|
25
|
+
try {
|
|
26
|
+
const require = (await import('node:module')).createRequire(import.meta.url);
|
|
27
|
+
// Optional native dependency: absence must degrade to silence, never throw.
|
|
28
|
+
const Database = require('better-sqlite3');
|
|
29
|
+
const db = new Database(sibling, { readonly: true, fileMustExist: true });
|
|
30
|
+
try {
|
|
31
|
+
const table = db
|
|
32
|
+
.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='memory_entries'")
|
|
33
|
+
.get();
|
|
34
|
+
if (!table)
|
|
35
|
+
return null;
|
|
36
|
+
const row = db
|
|
37
|
+
.prepare("SELECT COUNT(*) AS n FROM memory_entries WHERE (status = 'active' OR status IS NULL)")
|
|
38
|
+
.get();
|
|
39
|
+
const rows = Number(row?.n ?? 0);
|
|
40
|
+
return rows > 0 ? { path: sibling, rows } : null;
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
try {
|
|
44
|
+
db.close();
|
|
45
|
+
}
|
|
46
|
+
catch { /* best effort */ }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=sibling-store.js.map
|
|
@@ -54,14 +54,32 @@ export function isDaemonAlive(projectRoot) {
|
|
|
54
54
|
}
|
|
55
55
|
/** Project-local opt-out: `{ "daemon": { "autostart": false } }` in claude-flow.config.json. */
|
|
56
56
|
function autostartDisabledByProjectConfig(projectRoot) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
57
|
+
// #3278: read BOTH files, and both spellings.
|
|
58
|
+
//
|
|
59
|
+
// This used to consult only `claude-flow.config.json` with the key
|
|
60
|
+
// `daemon.autostart`. But `init` generates `.claude/settings.json` with
|
|
61
|
+
// `claudeFlow.daemon.autoStart` — a different file and a different capital S —
|
|
62
|
+
// and writes it `false` under the comment "Opt-in only — prevents unintended
|
|
63
|
+
// token consumption (#1427, #1330)". So the one setting a fresh project
|
|
64
|
+
// actually ships was never read, and the runtime spent tokens the generated
|
|
65
|
+
// config existed to prevent. Accept either spelling in either file: a user who
|
|
66
|
+
// wrote the word "autostart: false" anywhere sensible meant it.
|
|
67
|
+
const readsFalse = (v) => v === false;
|
|
68
|
+
for (const [file, pick] of [
|
|
69
|
+
['claude-flow.config.json', (c) => c?.daemon],
|
|
70
|
+
[path.join('.claude', 'settings.json'), (c) => c?.claudeFlow?.daemon],
|
|
71
|
+
]) {
|
|
72
|
+
try {
|
|
73
|
+
const cfg = JSON.parse(fs.readFileSync(path.join(projectRoot, file), 'utf-8'));
|
|
74
|
+
const d = pick(cfg);
|
|
75
|
+
if (readsFalse(d?.autostart) || readsFalse(d?.autoStart))
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// absent/malformed config = this file says nothing; keep checking the others
|
|
80
|
+
}
|
|
64
81
|
}
|
|
82
|
+
return false;
|
|
65
83
|
}
|
|
66
84
|
function autostartDisabled(projectRoot) {
|
|
67
85
|
if (/^(0|false|no|off)$/i.test(process.env.RUFLO_DAEMON_AUTOSTART ?? ''))
|
|
File without changes
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-flow/cli",
|
|
3
|
-
"version": "3.41.
|
|
3
|
+
"version": "3.41.2",
|
|
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",
|