@claude-flow/cli 3.41.4 → 3.42.1

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.
Files changed (43) hide show
  1. package/.claude/helpers/.helpers-version +1 -1
  2. package/.claude/helpers/helpers.manifest.json +2 -2
  3. package/catalog-manifest.json +2 -2
  4. package/dist/src/commands/doctor.d.ts +7 -0
  5. package/dist/src/commands/doctor.js +58 -35
  6. package/dist/src/commands/hive-mind.js +4 -3
  7. package/dist/src/commands/swarm.js +89 -18
  8. package/dist/src/init/claudemd-generator.js +2 -2
  9. package/dist/src/mcp-server.js +12 -0
  10. package/dist/src/mcp-tools/agentbbs-tools.d.ts +15 -0
  11. package/dist/src/mcp-tools/agentbbs-tools.js +98 -14
  12. package/dist/src/mcp-tools/agentdb-tools.js +17 -1
  13. package/dist/src/mcp-tools/hive-mind-tools.d.ts +8 -0
  14. package/dist/src/mcp-tools/hive-mind-tools.js +80 -2
  15. package/dist/src/mcp-tools/memory-tools.js +102 -31
  16. package/dist/src/mcp-tools/policy-enforcer.d.ts +126 -0
  17. package/dist/src/mcp-tools/policy-enforcer.js +177 -0
  18. package/dist/src/mcp-tools/seraphina-tools.js +7 -2
  19. package/dist/src/mcp-tools/x-federation-tools.d.ts +12 -0
  20. package/dist/src/mcp-tools/x-federation-tools.js +87 -5
  21. package/dist/src/memory/intelligence.d.ts +14 -2
  22. package/dist/src/memory/intelligence.js +33 -12
  23. package/dist/src/memory/memory-bridge.js +10 -2
  24. package/dist/src/memory/memory-initializer.js +19 -2
  25. package/dist/src/ruvector/graph-backend.js +112 -26
  26. package/dist/src/services/policy-runtime.js +63 -5
  27. package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.d.ts +4 -0
  28. package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.d.ts.map +1 -1
  29. package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.js +38 -1
  30. package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.js.map +1 -1
  31. package/node_modules/@claude-flow/codex/package.json +2 -1
  32. package/node_modules/@claude-flow/security/dist/index.d.ts +1 -1
  33. package/node_modules/@claude-flow/security/dist/index.d.ts.map +1 -1
  34. package/node_modules/@claude-flow/security/dist/index.js +1 -1
  35. package/node_modules/@claude-flow/security/dist/index.js.map +1 -1
  36. package/node_modules/@claude-flow/security/dist/mcp-caller-identity.d.ts +11 -0
  37. package/node_modules/@claude-flow/security/dist/mcp-caller-identity.d.ts.map +1 -1
  38. package/node_modules/@claude-flow/security/dist/mcp-caller-identity.js +0 -0
  39. package/node_modules/@claude-flow/security/dist/mcp-caller-identity.js.map +1 -1
  40. package/node_modules/@claude-flow/security/package.json +1 -0
  41. package/package.json +1 -1
  42. package/dist/src/ruvector/diskann-backend.d.ts +0 -78
  43. package/dist/src/ruvector/diskann-backend.js +0 -310
@@ -1 +1 @@
1
- 3.38.20
1
+ 3.42.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest": {
3
- "version": "3.41.4",
3
+ "version": "3.42.1",
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": "QbsQi7C+pl+Nam2mKSa0FmTWyFKbZnNzLGltRMREMq/BGR4U6klP43oN0eYzVHQCwQiUKz4YOsPo1CEfjVCcDA==",
11
+ "signature": "dp+TQNrg1n2wi3wjdGNFDBLjRnKSN7vuEvm4z8IC6Vmzlfh4DaTHX4sozhmxAt5GeVcLxjEq6urtK8s0vUADCQ==",
12
12
  "algorithm": "ed25519"
13
13
  }
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "generation": 6,
4
- "generatedAt": "2026-09-14T18:42:55.817Z",
5
- "gitSha": "dfd65965",
4
+ "generatedAt": "2026-09-16T02:31:10.655Z",
5
+ "gitSha": "6debd741",
6
6
  "catalog": {
7
7
  "agents": 167,
8
8
  "tools": 418,
@@ -5,6 +5,13 @@
5
5
  * Created with ruv.io
6
6
  */
7
7
  import type { Command } from '../types.js';
8
+ interface HealthCheck {
9
+ name: string;
10
+ status: 'pass' | 'warn' | 'fail';
11
+ message: string;
12
+ fix?: string;
13
+ }
14
+ export declare function checkMemoryPersistenceDriver(): Promise<HealthCheck>;
8
15
  export declare const doctorCommand: Command;
9
16
  export default doctorCommand;
10
17
  //# sourceMappingURL=doctor.d.ts.map
@@ -506,23 +506,13 @@ async function checkMemoryStructuralIntegrity() {
506
506
  catch { /* best-effort */ }
507
507
  }
508
508
  }
509
- // #2968 option 1 — read-only doctor check for the active SQLite driver.
510
- //
511
- // Native better-sqlite3 (durable, WAL-capable) can silently degrade to the
512
- // sql.js WASM fallback (non-durable — `wal_checkpoint` calls are rejected
513
- // and the write is lost) when a postinstall script is skipped. `memory
514
- // store` still printed "Data stored successfully" before the persistWarning
515
- // fix in #2983/3.38.1 (see memory-store-persist-warning-2968.test.ts); this
516
- // check gives a standing, read-only signal so the driver split is visible
517
- // any time doctor runs, independent of any single store call.
518
- //
519
- // Table count in the on-disk memory.db is the cheap, reliable signal from
520
- // the issue report: the native driver's schema produces 47 tables, the
521
- // sql.js fallback's produces only 10. Deliberately does NOT change install
522
- // behavior (that's option 2 from #2968, explicitly out of scope here) —
523
- // this only reports, it never repairs.
524
- const MEMORY_DRIVER_NATIVE_TABLE_FLOOR = 20; // roughly midpoint of sql.js's ~10 and native's ~47
525
- async function checkMemoryPersistenceDriver() {
509
+ // #2968/#3321 — read-only native SQLite capability probe. A skipped
510
+ // postinstall can leave the wrapper importable but its binding unavailable.
511
+ // Schema size cannot identify the runtime driver or the database's history:
512
+ // memory init creates its schema with sql.js even when native is available.
513
+ // This probe does not verify schema compatibility or cross-process writes;
514
+ // integrity checks and memory store's persistWarning retain their own roles.
515
+ export async function checkMemoryPersistenceDriver() {
526
516
  const NAME = 'Memory Persistence Driver';
527
517
  const dbPath = await resolveMemoryDbPath();
528
518
  if (!dbPath) {
@@ -549,6 +539,7 @@ async function checkMemoryPersistenceDriver() {
549
539
  let tableCount = null;
550
540
  let nativeUnavailableReason = null;
551
541
  let nativeOpenOtherError = null;
542
+ let nativeQueryError = null;
552
543
  if (Database) {
553
544
  let db;
554
545
  try {
@@ -566,10 +557,15 @@ async function checkMemoryPersistenceDriver() {
566
557
  if (db) {
567
558
  try {
568
559
  const row = db.prepare("SELECT count(*) AS c FROM sqlite_master WHERE type='table'").get();
569
- tableCount = Number(row?.c ?? 0);
560
+ if (typeof row?.c === 'number' && Number.isSafeInteger(row.c) && row.c >= 0) {
561
+ tableCount = row.c;
562
+ }
563
+ else {
564
+ nativeQueryError = 'query returned no reliable table count';
565
+ }
570
566
  }
571
- catch {
572
- // leave null — Memory Integrity above already reports open/query failures
567
+ catch (e) {
568
+ nativeQueryError = e.message || String(e);
573
569
  }
574
570
  finally {
575
571
  try {
@@ -589,7 +585,10 @@ async function checkMemoryPersistenceDriver() {
589
585
  if (sdb) {
590
586
  try {
591
587
  const res = sdb.exec("SELECT count(*) FROM sqlite_master WHERE type='table'");
592
- tableCount = Number(res[0]?.values?.[0]?.[0] ?? 0);
588
+ const count = res[0]?.values?.[0]?.[0];
589
+ if (typeof count === 'number' && Number.isSafeInteger(count) && count >= 0) {
590
+ tableCount = count;
591
+ }
593
592
  }
594
593
  catch {
595
594
  // leave null
@@ -604,12 +603,12 @@ async function checkMemoryPersistenceDriver() {
604
603
  }
605
604
  const tableSummary = tableCount === null
606
605
  ? 'table count unavailable'
607
- : `${tableCount} tables (native schema ~47, sql.js-fallback schema ~10 — #2968)`;
606
+ : `${tableCount} tables`;
608
607
  if (nativeUnavailableReason) {
609
608
  return {
610
609
  name: NAME,
611
610
  status: 'warn',
612
- message: `${dbPath} — active driver: sql.js (WASM fallback, non-durable) — native better-sqlite3 binding unavailable: ${nativeUnavailableReason} — wal_checkpoint calls silently no-op, writes may not persist across processes (#2968/#2867/#2219) [${tableSummary}]`,
611
+ message: `${dbPath} — native better-sqlite3 binding unavailable: ${nativeUnavailableReason} — native read-only probe unavailable; runtime driver and write persistence not verified (#2968) [${tableSummary}; sql.js main-image-only fallback]`,
613
612
  fix: 'reinstall with npm install scripts enabled, or run `npm rebuild better-sqlite3`; then rerun this check',
614
613
  };
615
614
  }
@@ -620,18 +619,17 @@ async function checkMemoryPersistenceDriver() {
620
619
  message: `${dbPath} — native better-sqlite3 module loadable, but could not open this database (${nativeOpenOtherError}) — see Memory Integrity above for the corruption/encryption diagnosis [${tableSummary}]`,
621
620
  };
622
621
  }
623
- if (tableCount !== null && tableCount < MEMORY_DRIVER_NATIVE_TABLE_FLOOR) {
622
+ if (nativeQueryError || tableCount === null) {
624
623
  return {
625
624
  name: NAME,
626
625
  status: 'warn',
627
- message: `${dbPath} — active driver: native better-sqlite3, but this database has only ${tableCount} tables — that matches the sql.js-fallback schema shape (~10), not the native schema (~47); it was likely created before the native binding became available, and durable writes made before then may be missing`,
628
- fix: 'back up .swarm/memory.db then `claude-flow memory init --force` to rebuild under the native driver',
626
+ message: `${dbPath} — native better-sqlite3 opened read-only, but table count unavailable: ${nativeQueryError ?? 'query returned no reliable result'}; runtime driver and write persistence not verified`,
629
627
  };
630
628
  }
631
629
  return {
632
630
  name: NAME,
633
631
  status: 'pass',
634
- message: `${dbPath} — active driver: native better-sqlite3 (durable, WAL-capable) [${tableSummary}]`,
632
+ message: `${dbPath} — native better-sqlite3 read-only open and schema query succeeded [${tableSummary}]; runtime driver, schema compatibility, and write persistence not verified`,
635
633
  };
636
634
  }
637
635
  // Check 1 (--component memory, deep path) — #2737 part 2 strengthens this:
@@ -2011,20 +2009,45 @@ async function checkMetaharnessDeclaredPackages() {
2011
2009
  }
2012
2010
  async function checkMetaharness() {
2013
2011
  try {
2014
- const version = await runCommand('npx -y metaharness@latest --version 2>&1', 15000);
2015
- // metaharness emits multi-line stdout; parse a version-shaped line.
2016
- const versionMatch = version.match(/(\d+\.\d+\.\d+)/);
2017
- if (!versionMatch) {
2012
+ // `metaharness` has no --version flag (it falls through to the usage
2013
+ // banner, which never matches a version regex) and shelling out via
2014
+ // `npx metaharness@latest` ignores the installed version and hits the
2015
+ // network every run. Resolve the version from the installed package's
2016
+ // own package.json instead. `import.meta.resolve` is the reliable route:
2017
+ // `require('metaharness/package.json')` is blocked by the package's
2018
+ // `exports` map, and `createRequire().resolve()` fails on its ESM-only
2019
+ // entry point.
2020
+ const resolved = import.meta.resolve('metaharness');
2021
+ let dir = dirname(fileURLToPath(resolved));
2022
+ let version = null;
2023
+ for (let i = 0; i < 8; i++) {
2024
+ const pj = join(dir, 'package.json');
2025
+ if (existsSync(pj)) {
2026
+ try {
2027
+ const j = JSON.parse(readFileSync(pj, 'utf-8'));
2028
+ if (j.name === 'metaharness') {
2029
+ version = j.version ?? null;
2030
+ break;
2031
+ }
2032
+ }
2033
+ catch { /* keep walking */ }
2034
+ }
2035
+ const parent = dirname(dir);
2036
+ if (parent === dir)
2037
+ break;
2038
+ dir = parent;
2039
+ }
2040
+ if (!version) {
2018
2041
  return {
2019
2042
  name: 'MetaHarness (ADR-150)',
2020
2043
  status: 'warn',
2021
- message: 'Installed but version-string not parseable; integration may still work',
2044
+ message: 'Installed but its package.json was not found while walking up from the resolved module; integration may still work',
2022
2045
  };
2023
2046
  }
2024
2047
  return {
2025
2048
  name: 'MetaHarness (ADR-150)',
2026
2049
  status: 'pass',
2027
- message: `v${versionMatch[1]} — run \`npx ruflo metaharness score\` for the full scorecard`,
2050
+ message: `v${version} — run \`npx ruflo metaharness score\` for the full scorecard`,
2028
2051
  };
2029
2052
  }
2030
2053
  catch {
@@ -2348,7 +2371,7 @@ export const doctorCommand = {
2348
2371
  checkDaemonStatus,
2349
2372
  checkMemoryDatabase,
2350
2373
  checkMemoryStructuralIntegrity, // #2737 — bounded, native quick_check on every default run
2351
- checkMemoryPersistenceDriver, // #2968 — native better-sqlite3 vs sql.js fallback, read-only
2374
+ checkMemoryPersistenceDriver, // #2968/#3321 — read-only native capability probe
2352
2375
  checkLearningBridge, // #2545 — can the auto-memory hook actually load @claude-flow/memory?
2353
2376
  checkApiKeys,
2354
2377
  checkMcpServers,
@@ -2386,7 +2409,7 @@ export const doctorCommand = {
2386
2409
  'memory': [
2387
2410
  checkMemoryDatabase, // existing: exists + statable (unchanged)
2388
2411
  checkMemoryIntegrity, // #2677 check 1: sql.js open + PRAGMA integrity_check
2389
- checkMemoryPersistenceDriver, // #2968: native better-sqlite3 vs sql.js fallback
2412
+ checkMemoryPersistenceDriver, // #2968/#3321: read-only native capability probe
2390
2413
  checkMemoryContent, // #2677 check 2: memory_entries content coverage
2391
2414
  checkMemoryEmbeddingCoverage, // #2677 check 3: vector coverage on populated rows
2392
2415
  checkMemoryReflexionCoverage, // #2677 check 6: episodes are retrievable
@@ -13,6 +13,7 @@ import { mkdir, writeFile } from 'fs/promises';
13
13
  import { existsSync } from 'fs';
14
14
  import { join } from 'path';
15
15
  import { resolveClaudeLaunchCommand } from '../runtime/claude-command.js';
16
+ import { getHiveTokenForCli } from '../mcp-tools/hive-mind-tools.js';
16
17
  // Hive topologies
17
18
  const TOPOLOGIES = [
18
19
  { value: 'hierarchical', label: 'Hierarchical', hint: 'Queen-led with worker agents' },
@@ -977,7 +978,7 @@ const joinCommand = {
977
978
  return { success: false, exitCode: 1 };
978
979
  }
979
980
  try {
980
- const result = await callMCPTool('hive-mind_join', { agentId, role: ctx.flags.role });
981
+ const result = await callMCPTool('hive-mind_join', { agentId, role: ctx.flags.role, hiveToken: getHiveTokenForCli() });
981
982
  if (!result.success) {
982
983
  output.printError(result.error || 'Failed');
983
984
  return { success: false, exitCode: 1 };
@@ -1003,7 +1004,7 @@ const leaveCommand = {
1003
1004
  return { success: false, exitCode: 1 };
1004
1005
  }
1005
1006
  try {
1006
- const result = await callMCPTool('hive-mind_leave', { agentId });
1007
+ const result = await callMCPTool('hive-mind_leave', { agentId, hiveToken: getHiveTokenForCli() });
1007
1008
  if (!result.success) {
1008
1009
  output.printError(result.error || 'Failed');
1009
1010
  return { success: false, exitCode: 1 };
@@ -1032,7 +1033,7 @@ const consensusCommand = {
1032
1033
  action: async (ctx) => {
1033
1034
  const action = ctx.flags.action || 'list';
1034
1035
  try {
1035
- const result = await callMCPTool('hive-mind_consensus', { action, proposalId: ctx.flags.proposalId, type: ctx.flags.type, value: ctx.flags.value, vote: ctx.flags.vote === 'yes', voterId: ctx.flags.voterId });
1036
+ const result = await callMCPTool('hive-mind_consensus', { action, proposalId: ctx.flags.proposalId, type: ctx.flags.type, value: ctx.flags.value, vote: ctx.flags.vote === 'yes', voterId: ctx.flags.voterId, hiveToken: getHiveTokenForCli() });
1036
1037
  if (ctx.flags.format === 'json') {
1037
1038
  output.printJson(result);
1038
1039
  return { success: true, data: result };
@@ -8,6 +8,56 @@ import { callMCPTool, MCPClientError } from '../mcp-client.js';
8
8
  import { swarmJoinCommand } from './agntcy/swarm-join.js';
9
9
  import * as fs from 'fs';
10
10
  import * as path from 'path';
11
+ // Read the CLI-side swarm state file (`.swarm/state.json`), written by
12
+ // `swarm init` and rewritten by `swarm start` / `swarm stop`.
13
+ function readLocalSwarmState() {
14
+ const swarmStateFile = path.join(process.cwd(), '.swarm', 'state.json');
15
+ if (!fs.existsSync(swarmStateFile))
16
+ return null;
17
+ try {
18
+ return JSON.parse(fs.readFileSync(swarmStateFile, 'utf-8'));
19
+ }
20
+ catch {
21
+ // Ignore parse errors
22
+ return null;
23
+ }
24
+ }
25
+ // Resolve the id of the current swarm. Three writers persist it, under two
26
+ // different keys and in two different files:
27
+ //
28
+ // `.swarm/state.json` `id` ← `swarm init`
29
+ // `.swarm/state.json` `swarmId` ← `swarm start`
30
+ // `.claude-flow/swarm/swarm-state.json` `swarms[id].swarmId` ← MCP `swarm_init`
31
+ //
32
+ // The status payload used to read only the first key of the first file, so a
33
+ // Claude Code session (which drives the MCP path) reported an active swarm
34
+ // with agents but no usable id. Check every writer, most local first.
35
+ function resolveSwarmId(swarmState) {
36
+ // A state file left behind by a previous `swarm stop` must not shadow a
37
+ // swarm that is still live in the MCP store — otherwise `stop` names the
38
+ // dead id while `swarm_shutdown`'s own "most recent running" fallback
39
+ // terminates a different swarm.
40
+ if (swarmState?.status !== 'stopped') {
41
+ const fromState = swarmState?.id ?? swarmState?.swarmId;
42
+ if (typeof fromState === 'string' && fromState)
43
+ return fromState;
44
+ }
45
+ try {
46
+ const storePath = path.join(process.cwd(), '.claude-flow', 'swarm', 'swarm-state.json');
47
+ if (!fs.existsSync(storePath))
48
+ return null;
49
+ const store = JSON.parse(fs.readFileSync(storePath, 'utf-8'));
50
+ // Most recently updated swarm that has not been shut down.
51
+ const live = Object.values(store.swarms ?? {})
52
+ .filter(swarm => swarm.status !== 'terminated')
53
+ .sort((a, b) => new Date(b.updatedAt ?? 0).getTime() - new Date(a.updatedAt ?? 0).getTime());
54
+ return live[0]?.swarmId ?? null;
55
+ }
56
+ catch {
57
+ // Ignore — an unreadable MCP store just means no id to resolve.
58
+ return null;
59
+ }
60
+ }
11
61
  // Get dynamic swarm status from memory/session files
12
62
  function getSwarmStatus(swarmId) {
13
63
  const swarmDir = path.join(process.cwd(), '.swarm');
@@ -17,16 +67,7 @@ function getSwarmStatus(swarmId) {
17
67
  path.join(process.cwd(), '.claude', 'memory.db'),
18
68
  ];
19
69
  // Check for active swarm state file
20
- const swarmStateFile = path.join(swarmDir, 'state.json');
21
- let swarmState = null;
22
- if (fs.existsSync(swarmStateFile)) {
23
- try {
24
- swarmState = JSON.parse(fs.readFileSync(swarmStateFile, 'utf-8'));
25
- }
26
- catch {
27
- // Ignore parse errors
28
- }
29
- }
70
+ const swarmState = readLocalSwarmState();
30
71
  // Count active agents from process files
31
72
  let activeAgents = 0;
32
73
  let totalAgents = 0;
@@ -175,10 +216,22 @@ function getSwarmStatus(swarmId) {
175
216
  status = 'completed';
176
217
  }
177
218
  else if (swarmState) {
178
- status = 'ready';
219
+ // The file's own lifecycle state wins over the "a file exists, so we are
220
+ // ready" default — a swarm explicitly recorded as stopped is not ready.
221
+ // Live agents still win above: they are observed reality, whereas the file
222
+ // records an intent that may be stale.
223
+ status = swarmState.status === 'stopped' ? 'stopped' : 'ready';
179
224
  }
225
+ // Resolve once — the id also settles whether there is a swarm to report.
226
+ // An MCP `swarm_init` with no agents spawned yet leaves no `.swarm/state.json`
227
+ // and no agent store, so without this the payload contradicted itself:
228
+ // a real `id` alongside `hasActiveSwarm: false`.
229
+ const resolvedId = swarmId || resolveSwarmId(swarmState);
180
230
  return {
181
- id: swarmId || swarmState?.id || 'no-active-swarm',
231
+ // `null` when genuinely unknown — never a sentinel string. `status` and
232
+ // `hasActiveSwarm` already carry the "no active swarm" fact, and a
233
+ // consumer reading `.id` must get an id or nothing.
234
+ id: resolvedId,
182
235
  topology: swarmState?.topology || 'none',
183
236
  status,
184
237
  objective: swarmState?.objective || 'No active objective',
@@ -290,7 +343,13 @@ function getSwarmStatus(swarmId) {
290
343
  }
291
344
  return { consensusRounds, messagesSent, conflictsResolved };
292
345
  })(),
293
- hasActiveSwarm: !!swarmState || totalAgents > 0
346
+ // A state file left behind by `swarm stop` is not an active swarm — without
347
+ // this it claimed one it could not name (`id: null`, zero agents), which is
348
+ // the same contradiction from the other side. Live agents or a resolvable
349
+ // id still count, so a stopped file never masks real activity.
350
+ hasActiveSwarm: (!!swarmState && swarmState.status !== 'stopped')
351
+ || totalAgents > 0
352
+ || resolvedId !== null
294
353
  };
295
354
  }
296
355
  // Swarm topologies
@@ -630,6 +689,9 @@ const startCommand = {
630
689
  if (!fs.existsSync(swarmDir))
631
690
  fs.mkdirSync(swarmDir, { recursive: true });
632
691
  const executionState = {
692
+ // `id` mirrors what `swarm init` writes so both files share one key;
693
+ // `swarmId` stays for existing files and readers.
694
+ id: swarmId,
633
695
  swarmId,
634
696
  objective,
635
697
  strategy,
@@ -673,7 +735,7 @@ const statusCommand = {
673
735
  output.writeln();
674
736
  return { success: true, data: status };
675
737
  }
676
- output.writeln(output.bold(`Swarm Status: ${status.id}`));
738
+ output.writeln(output.bold(`Swarm Status: ${status.id ?? output.dim('unknown id')}`));
677
739
  output.writeln();
678
740
  // Progress bar
679
741
  output.writeln(`Overall Progress: ${output.progressBar(status.progress, 100, 40)}`);
@@ -747,10 +809,16 @@ const stopCommand = {
747
809
  }
748
810
  ],
749
811
  action: async (ctx) => {
750
- const swarmId = ctx.args[0];
812
+ // Bare `swarm stop` used to hard-fail, and no CLI surface handed out an
813
+ // id to pass (there is no `swarm list`). Default to the persisted swarm;
814
+ // an explicit argument still wins.
815
+ const swarmId = ctx.args[0] || resolveSwarmId(readLocalSwarmState());
751
816
  const force = ctx.flags.force;
752
817
  if (!swarmId) {
753
- output.printError('Swarm ID is required');
818
+ output.printError('No swarm found to stop');
819
+ output.writeln(output.dim(' Find an id with: claude-flow swarm status --format json (the "id" field)'));
820
+ output.writeln(output.dim(' Or pass one: claude-flow swarm stop <swarm-id>'));
821
+ output.writeln(output.dim(' Or start one: claude-flow swarm init'));
754
822
  return { success: false, exitCode: 1 };
755
823
  }
756
824
  if (ctx.interactive && !force) {
@@ -810,11 +878,14 @@ const scaleCommand = {
810
878
  }
811
879
  ],
812
880
  action: async (ctx) => {
813
- const swarmId = ctx.args[0];
881
+ // Same resolution as `stop` — the id is persisted, so don't demand it.
882
+ const swarmId = ctx.args[0] || resolveSwarmId(readLocalSwarmState());
814
883
  const targetAgents = ctx.flags.agents;
815
884
  const agentType = ctx.flags.type;
816
885
  if (!swarmId) {
817
- output.printError('Swarm ID is required');
886
+ output.printError('No swarm found to scale');
887
+ output.writeln(output.dim(' Find an id with: claude-flow swarm status --format json (the "id" field)'));
888
+ output.writeln(output.dim(' Or pass one: claude-flow swarm scale <swarm-id> --agents N'));
818
889
  return { success: false, exitCode: 1 };
819
890
  }
820
891
  if (!targetAgents) {
@@ -258,7 +258,7 @@ function performanceSection() {
258
258
 
259
259
  - Always benchmark before AND after optimization
260
260
  - Always profile before optimizing — never guess bottlenecks
261
- - Use HNSW/DiskANN for vector search, Int8 quantization for memory reduction
261
+ - Use HNSW for vector search, Int8 quantization for memory reduction — measure the actual speedup/recall on your own corpus via \`node scripts/benchmark-intelligence.mjs --only=hnsw\` rather than trusting a hardcoded multiplier (recall@10 varies by corpus size — 0.99 at N=5k, 0.92 at N=20k on the reference host, dream-cycle 2026-08-30)
262
262
 
263
263
  \`\`\`bash
264
264
  npx @claude-flow/cli@latest performance benchmark --suite all
@@ -293,7 +293,7 @@ function intelligenceSystem() {
293
293
  Pipeline: **RETRIEVE** (vector search) → **JUDGE** (success/failure) → **DISTILL** (extract patterns) → **CONSOLIDATE** (persist)
294
294
 
295
295
  - **ONNX Embeddings**: all-MiniLM-L6-v2, 384-dim
296
- - **HNSW/DiskANN**: 150x-12,500x faster search
296
+ - **HNSW**: faster than brute-force search, magnitude scales with corpus size — reproduce with \`node scripts/benchmark-intelligence.mjs --only=hnsw\` rather than a hardcoded multiplier
297
297
  - **SONA**: Sub-millisecond pattern adaptation
298
298
  - **Claude Bridge**: Auto-imports \`~/.claude/projects/*/memory/*.md\` into AgentDB`;
299
299
  }
@@ -26,6 +26,7 @@ import * as os from 'os';
26
26
  import { fileURLToPath } from 'url';
27
27
  import { dirname } from 'path';
28
28
  import { trackRequest } from './mcp-tools/request-tracker.js';
29
+ import { isPolicyEnforcementEnabled, loadMcpPolicy, evaluateToolCall, } from './mcp-tools/policy-enforcer.js';
29
30
  // ESM-compatible __dirname
30
31
  const __filename = fileURLToPath(import.meta.url);
31
32
  const __dirname = dirname(__filename);
@@ -503,6 +504,17 @@ export class MCPServerManager extends EventEmitter {
503
504
  error: { code: -32601, message: `Tool not found: ${toolName}` },
504
505
  };
505
506
  }
507
+ if (isPolicyEnforcementEnabled()) {
508
+ const check = evaluateToolCall(loadMcpPolicy(), sessionId, toolName);
509
+ if (!check.allowed) {
510
+ trackRequest(toolName, false);
511
+ return {
512
+ jsonrpc: '2.0',
513
+ id: message.id,
514
+ error: { code: -32001, message: `Policy denied: ${check.reason}` },
515
+ };
516
+ }
517
+ }
506
518
  try {
507
519
  const result = await callMCPTool(toolName, toolParams, { sessionId });
508
520
  trackRequest(toolName, true);
@@ -35,5 +35,20 @@
35
35
  * @module @claude-flow/cli/mcp-tools/agentbbs
36
36
  */
37
37
  import type { MCPTool } from './types.js';
38
+ /** Last envelope's seq by scanning only the file tail; full-read fallback keeps it exact. */
39
+ export declare function nextSeq(path: string): number;
40
+ /** Timestamp (ms) of the most recent PeerHello for a room, or -Infinity if none. */
41
+ export declare function lastPeerHelloMs(logPath: string): number;
42
+ /**
43
+ * Whether a `register` call should append a PeerHello: only for a genuinely new
44
+ * registration, or once the heartbeat window has elapsed since the last one.
45
+ * A re-register within the window is a retry and must NOT spam a near-identical
46
+ * hello. Pure — the decision the handler makes, exported for test.
47
+ */
48
+ export declare function shouldEmitHello(alreadyRegistered: boolean, nowMs: number, lastHelloMs: number, windowSecs: number): boolean;
49
+ /** Collapse duplicate envelopeIds, preserving order (first occurrence wins). Pure. */
50
+ export declare function dedupEnvelopes<T extends {
51
+ envelopeId: string;
52
+ }>(list: T[]): T[];
38
53
  export declare const agentbbsTools: MCPTool[];
39
54
  //# sourceMappingURL=agentbbs-tools.d.ts.map