@claude-flow/cli 3.41.1 → 3.41.3

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest": {
3
- "version": "3.41.1",
3
+ "version": "3.41.3",
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": "6JrRzvJx81mt9GZLoEwVmk8GNxDCaHMrR/8YywbD947dVkjWUFnTBCemqG1LKoRbfubrOlLr5i71BX3v8W98CQ==",
11
+ "signature": "YKF5dYIr74cL7hG0bQ8z6ygCZF4Atdhj79kRLOoCKLSO+o0pbUvs+4u9qQHN9Jig99FufZYKwvciYrE/85XaBA==",
12
12
  "algorithm": "ed25519"
13
13
  }
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "generation": 6,
4
- "generatedAt": "2026-09-10T14:41:32.208Z",
5
- "gitSha": "cda8b04f",
4
+ "generatedAt": "2026-09-14T17:14:45.365Z",
5
+ "gitSha": "8008bc35",
6
6
  "catalog": {
7
7
  "agents": 167,
8
8
  "tools": 418,
@@ -9,6 +9,23 @@ import { distillCommand } from './memory-distill.js';
9
9
  import { backupCommand } from './memory-backup.js';
10
10
  import { countSiblingStoreRows } from '../memory/sibling-store.js';
11
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
+ }
12
29
  // Memory backends
13
30
  const BACKENDS = [
14
31
  { value: 'agentdb', label: 'AgentDB', hint: 'Vector database with HNSW indexing (150x-12,500x faster)' },
@@ -287,6 +304,7 @@ const retrieveCommand = {
287
304
  }
288
305
  if (!result.found || !result.entry) {
289
306
  output.printWarning(`Key not found: ${key}`);
307
+ await warnIfSiblingHasRows(ctx.flags.path);
290
308
  return { success: false, exitCode: 1, data: { key, found: false } };
291
309
  }
292
310
  const entry = result.entry;
@@ -667,6 +685,7 @@ const searchCommand = {
667
685
  output.writeln();
668
686
  if (results.length === 0) {
669
687
  output.printWarning('No results found');
688
+ await warnIfSiblingHasRows(ctx.flags.path);
670
689
  output.writeln(output.dim('Try: claude-flow memory store -k "key" --value "data"'));
671
690
  return { success: true, data: [] };
672
691
  }
@@ -746,6 +765,7 @@ const listCommand = {
746
765
  output.writeln();
747
766
  if (entries.length === 0) {
748
767
  output.printWarning('No entries found');
768
+ await warnIfSiblingHasRows(ctx.flags.path);
749
769
  output.printInfo('Store data: claude-flow memory store -k "key" --value "data"');
750
770
  return { success: true, data: [] };
751
771
  }
@@ -88,13 +88,18 @@ function publishEvent(ws, nt, sk, tags, content) {
88
88
  ws.send(JSON.stringify(['EVENT', ev]));
89
89
  });
90
90
  }
91
- function reqEvents(ws, filter) {
91
+ function reqEvents(ws, nt, filter) {
92
92
  const out = [];
93
93
  return new Promise((resolve) => {
94
94
  const t = setTimeout(() => resolve(out), 12000);
95
95
  ws.on('message', (d) => {
96
96
  const m = JSON.parse(d.toString());
97
- if (m[0] === 'EVENT')
97
+ // Only a signature-verified event can be trusted as evidence of who sent
98
+ // it. Without this check an unverified EVENT frame — relay bug, MITM, or
99
+ // a compromised relay — would reach callers that treat .pubkey/.id as
100
+ // authenticated (channel_accept's NIP-44 conversation-key derivation,
101
+ // channel_read's returned identity).
102
+ if (m[0] === 'EVENT' && nt.verifyEvent(m[2]))
98
103
  out.push(m[2]);
99
104
  else if (m[0] === 'EOSE') {
100
105
  clearTimeout(t);
@@ -168,7 +173,7 @@ export const xFederationChannelTools = [
168
173
  return degraded();
169
174
  const { sk, pubkey } = loadOrCreateKey(t.nt, KEY_FILE());
170
175
  const relay = RELAY_WS(i.relayWs);
171
- const evs = await relayCall(relay, sk, t.nt, (ws) => reqEvents(ws, {
176
+ const evs = await relayCall(relay, sk, t.nt, (ws) => reqEvents(ws, t.nt, {
172
177
  kinds: [1], '#t': ['ruflo-swarm'], '#k': ['ChannelGrant'], '#p': [pubkey],
173
178
  since: Math.floor(Date.now() / 1000) - (i.sinceSeconds ?? 7 * 86400), limit: 200,
174
179
  }));
@@ -256,7 +261,7 @@ export const xFederationChannelTools = [
256
261
  if (!t)
257
262
  return degraded();
258
263
  const { sk } = loadOrCreateKey(t.nt, KEY_FILE());
259
- const evs = await relayCall(RELAY_WS(i.relayWs), sk, t.nt, (ws) => reqEvents(ws, {
264
+ const evs = await relayCall(RELAY_WS(i.relayWs), sk, t.nt, (ws) => reqEvents(ws, t.nt, {
260
265
  kinds: [1], '#t': ['ruflo-swarm'], '#c': [i.channel],
261
266
  since: Math.floor(Date.now() / 1000) - (i.sinceSeconds ?? 3600), limit: i.limit ?? 100,
262
267
  }));
@@ -266,9 +271,12 @@ export const xFederationChannelTools = [
266
271
  const ev = e;
267
272
  const k = ev.tags.find((x) => x[0] === 'k')?.[1];
268
273
  const base = { id: ev.id, pubkey: ev.pubkey, created_at: ev.created_at };
274
+ // base's verified id/pubkey/created_at must win over the parsed/decrypted
275
+ // body — spread body first so a stray id/pubkey/created_at key in the
276
+ // publisher's own content can't override the authenticated identity.
269
277
  if (k !== 'enc') {
270
278
  try {
271
- return { ...base, ...JSON.parse(ev.content) };
279
+ return { ...JSON.parse(ev.content), ...base };
272
280
  }
273
281
  catch {
274
282
  return { ...base, raw: ev.content };
@@ -277,7 +285,7 @@ export const xFederationChannelTools = [
277
285
  if (!key)
278
286
  return { ...base, encrypted: true, reason: 'no channel key held' };
279
287
  try {
280
- return { ...base, ...JSON.parse(t.nip44.v2.decrypt(ev.content, key)) };
288
+ return { ...JSON.parse(t.nip44.v2.decrypt(ev.content, key)), ...base };
281
289
  }
282
290
  catch {
283
291
  return { ...base, encrypted: true, reason: 'held key does not open this message' };
@@ -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
- try {
58
- const raw = fs.readFileSync(path.join(projectRoot, 'claude-flow.config.json'), 'utf-8');
59
- const cfg = JSON.parse(raw);
60
- return cfg?.daemon?.autostart === false;
61
- }
62
- catch {
63
- return false; // absent/malformed config = not disabled
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 ?? ''))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-flow/cli",
3
- "version": "3.41.1",
3
+ "version": "3.41.3",
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",