@claude-flow/cli 3.54.1 → 3.56.0

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 (116) hide show
  1. package/.claude/helpers/helpers.manifest.json +2 -2
  2. package/.claude/settings.json +9 -9
  3. package/bin/cli.js +1 -1
  4. package/bin/mcp-server.js +1 -1
  5. package/catalog-manifest.json +2 -2
  6. package/dist/src/auth/client.js +40 -24
  7. package/dist/src/commands/agent.js +12 -0
  8. package/dist/src/commands/analyze.js +18 -14
  9. package/dist/src/commands/autopilot.js +2 -2
  10. package/dist/src/commands/daemon.js +15 -3
  11. package/dist/src/commands/doctor.js +65 -58
  12. package/dist/src/commands/hive-mind.js +7 -0
  13. package/dist/src/commands/mcp.js +35 -1
  14. package/dist/src/commands/memory-backup.d.ts +1 -1
  15. package/dist/src/commands/memory-backup.js +23 -9
  16. package/dist/src/commands/memory.d.ts +0 -4
  17. package/dist/src/commands/memory.js +10 -2
  18. package/dist/src/commands/plugins.js +13 -1
  19. package/dist/src/commands/policy.js +10 -2
  20. package/dist/src/commands/task.js +12 -0
  21. package/dist/src/init/settings-generator.js +3 -3
  22. package/dist/src/mcp-server.d.ts +33 -0
  23. package/dist/src/mcp-server.js +96 -4
  24. package/dist/src/mcp-tools/agent-execute-core.js +244 -188
  25. package/dist/src/mcp-tools/agent-tools.js +15 -16
  26. package/dist/src/mcp-tools/agentdb-tools.js +22 -3
  27. package/dist/src/mcp-tools/claims-tools.js +3 -11
  28. package/dist/src/mcp-tools/hive-mind-tools.d.ts +2 -4
  29. package/dist/src/mcp-tools/hive-mind-tools.js +214 -40
  30. package/dist/src/mcp-tools/hooks-tools.js +47 -2
  31. package/dist/src/mcp-tools/memory-tools.js +29 -10
  32. package/dist/src/mcp-tools/record-store.d.ts +3 -0
  33. package/dist/src/mcp-tools/record-store.js +21 -0
  34. package/dist/src/mcp-tools/session-tools.js +47 -13
  35. package/dist/src/mcp-tools/task-tools.js +5 -11
  36. package/dist/src/mcp-tools/workflow-tools.js +78 -29
  37. package/dist/src/memory/append-conditions.d.ts +28 -0
  38. package/dist/src/memory/append-conditions.js +56 -0
  39. package/dist/src/memory/graph-edge-writer.d.ts +9 -0
  40. package/dist/src/memory/graph-edge-writer.js +90 -0
  41. package/dist/src/memory/memory-bridge.d.ts +4 -0
  42. package/dist/src/memory/memory-bridge.js +50 -9
  43. package/dist/src/memory/memory-initializer.d.ts +4 -0
  44. package/dist/src/memory/memory-initializer.js +95 -61
  45. package/dist/src/memory/memory-root.d.ts +2 -0
  46. package/dist/src/memory/memory-root.js +28 -0
  47. package/dist/src/memory/sona-optimizer.d.ts +2 -0
  48. package/dist/src/memory/sona-optimizer.js +4 -0
  49. package/dist/src/missions/schemas.d.ts +125 -125
  50. package/dist/src/mods/command-registry/schema.d.ts +199 -199
  51. package/dist/src/parser.d.ts +3 -0
  52. package/dist/src/parser.js +71 -13
  53. package/dist/src/plugins/store/discovery.d.ts +6 -0
  54. package/dist/src/plugins/store/discovery.js +7 -3
  55. package/dist/src/production/retry.js +1 -1
  56. package/dist/src/ruvector/neural-router.js +7 -2
  57. package/dist/src/services/bounded-worker-pool.js +3 -0
  58. package/dist/src/services/claim-service.js +39 -18
  59. package/dist/src/services/claims-store.d.ts +3 -0
  60. package/dist/src/services/claims-store.js +40 -0
  61. package/dist/src/services/config-file-manager.d.ts +3 -0
  62. package/dist/src/services/config-file-manager.js +24 -7
  63. package/dist/src/services/distill-tuning.js +17 -10
  64. package/dist/src/services/memory-backup.d.ts +7 -0
  65. package/dist/src/services/memory-backup.js +29 -2
  66. package/dist/src/services/memory-distillation.js +2 -2
  67. package/dist/src/services/policy-ledger-anchor.d.ts +42 -0
  68. package/dist/src/services/policy-ledger-anchor.js +163 -0
  69. package/dist/src/services/policy-runtime.d.ts +11 -5
  70. package/dist/src/services/policy-runtime.js +70 -15
  71. package/dist/src/services/worker-daemon.d.ts +5 -2
  72. package/dist/src/services/worker-daemon.js +26 -6
  73. package/dist/src/services/worker-queue.js +11 -2
  74. package/node_modules/@claude-flow/codex/dist/cli.js +0 -0
  75. package/node_modules/@claude-flow/mcp/dist/connection-pool.d.ts.map +1 -1
  76. package/node_modules/@claude-flow/mcp/dist/connection-pool.js +5 -2
  77. package/node_modules/@claude-flow/mcp/dist/connection-pool.js.map +1 -1
  78. package/node_modules/@claude-flow/mcp/dist/transport/http.d.ts +7 -1
  79. package/node_modules/@claude-flow/mcp/dist/transport/http.d.ts.map +1 -1
  80. package/node_modules/@claude-flow/mcp/dist/transport/http.js +60 -47
  81. package/node_modules/@claude-flow/mcp/dist/transport/http.js.map +1 -1
  82. package/node_modules/@claude-flow/mcp/package.json +1 -1
  83. package/node_modules/@claude-flow/plugin-agent-federation/dist/application/federation-coordinator.js +1 -1
  84. package/node_modules/@claude-flow/plugin-agent-federation/dist/application/federation-coordinator.js.map +1 -1
  85. package/node_modules/@claude-flow/plugin-agent-federation/dist/bin.js +0 -0
  86. package/node_modules/@claude-flow/plugin-agent-federation/dist/domain/services/routing-service.d.ts +3 -0
  87. package/node_modules/@claude-flow/plugin-agent-federation/dist/domain/services/routing-service.d.ts.map +1 -1
  88. package/node_modules/@claude-flow/plugin-agent-federation/dist/domain/services/routing-service.js +4 -1
  89. package/node_modules/@claude-flow/plugin-agent-federation/dist/domain/services/routing-service.js.map +1 -1
  90. package/node_modules/@claude-flow/plugin-agent-federation/dist/plugin.d.ts.map +1 -1
  91. package/node_modules/@claude-flow/plugin-agent-federation/dist/plugin.js +1 -0
  92. package/node_modules/@claude-flow/plugin-agent-federation/dist/plugin.js.map +1 -1
  93. package/node_modules/@claude-flow/plugin-agent-federation/package.json +1 -1
  94. package/node_modules/@claude-flow/security/dist/oauth/callback-server.d.ts +2 -1
  95. package/node_modules/@claude-flow/security/dist/oauth/callback-server.d.ts.map +1 -1
  96. package/node_modules/@claude-flow/security/dist/oauth/callback-server.js +48 -16
  97. package/node_modules/@claude-flow/security/dist/oauth/callback-server.js.map +1 -1
  98. package/node_modules/@claude-flow/security/dist/path-validator.d.ts +2 -2
  99. package/node_modules/@claude-flow/security/dist/path-validator.d.ts.map +1 -1
  100. package/node_modules/@claude-flow/security/dist/path-validator.js +15 -4
  101. package/node_modules/@claude-flow/security/dist/path-validator.js.map +1 -1
  102. package/node_modules/@claude-flow/security/dist/policy/engine.d.ts +11 -1
  103. package/node_modules/@claude-flow/security/dist/policy/engine.d.ts.map +1 -1
  104. package/node_modules/@claude-flow/security/dist/policy/engine.js +19 -3
  105. package/node_modules/@claude-flow/security/dist/policy/engine.js.map +1 -1
  106. package/node_modules/@claude-flow/security/dist/policy/types.d.ts +4 -2
  107. package/node_modules/@claude-flow/security/dist/policy/types.d.ts.map +1 -1
  108. package/node_modules/@claude-flow/security/package.json +1 -1
  109. package/package.json +7 -7
  110. package/plugins/ruflo-metaharness/.claude-plugin/plugin.json +1 -1
  111. package/plugins/ruflo-metaharness/CHANGELOG.md +48 -0
  112. package/plugins/ruflo-metaharness/scripts/_invoke.mjs +80 -7
  113. package/plugins/ruflo-metaharness/scripts/smoke.sh +4 -4
  114. package/plugins/ruflo-metaharness/scripts/test-cli-resolution-3558.mjs +141 -0
  115. package/plugins/ruflo-metaharness/scripts/test-no-tool-time-npx.mjs +5 -0
  116. package/.claude/agents/tmp.json +0 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest": {
3
- "version": "3.54.1",
3
+ "version": "3.56.0",
4
4
  "files": {
5
5
  "auto-memory-hook.mjs": "c8d218526a3c34034c2a69de23f507f0eb0f70d59c16426a8edc849e6ac22529",
6
6
  "hook-handler.cjs": "34d2aacfb59a73ff94b1467bb57ab9d9c4af89c0e470aadf3bd0d74a503a658d",
@@ -9,6 +9,6 @@
9
9
  "router.cjs": "16648b3c0d73b6681ac2d8d13def6a68286c57f5a11004de5b19a0fa088c833d"
10
10
  }
11
11
  },
12
- "signature": "GORi4ozu4COfL048CD+GWNMiX4hdXVrn2/tvRXWcj9OioRiUknKlfhNwkL6PVtcNar+5xDn7J9BzGFBrPgblCA==",
12
+ "signature": "9T3S9rchP2NnIjrYOL9tV7QvDP3WlsrsR7WlBXpUpoGPCrhe2Mo9bQEU7JOCscuQ+UQsrP211p7vmc93+AlWDg==",
13
13
  "algorithm": "ed25519"
14
14
  }
@@ -11,7 +11,7 @@
11
11
  "hooks": [
12
12
  {
13
13
  "type": "command",
14
- "command": "node "$CLAUDE_PROJECT_DIR/.claude/helpers/hook-handler.cjs" pre-bash",
14
+ "command": "node \"$CLAUDE_PROJECT_DIR/.claude/helpers/hook-handler.cjs\" pre-bash",
15
15
  "timeout": 5000
16
16
  }
17
17
  ]
@@ -23,7 +23,7 @@
23
23
  "hooks": [
24
24
  {
25
25
  "type": "command",
26
- "command": "node "$CLAUDE_PROJECT_DIR/.claude/helpers/hook-handler.cjs" post-edit",
26
+ "command": "node \"$CLAUDE_PROJECT_DIR/.claude/helpers/hook-handler.cjs\" post-edit",
27
27
  "timeout": 10000
28
28
  }
29
29
  ]
@@ -34,7 +34,7 @@
34
34
  "hooks": [
35
35
  {
36
36
  "type": "command",
37
- "command": "node "$CLAUDE_PROJECT_DIR/.claude/helpers/hook-handler.cjs" route",
37
+ "command": "node \"$CLAUDE_PROJECT_DIR/.claude/helpers/hook-handler.cjs\" route",
38
38
  "timeout": 10000
39
39
  }
40
40
  ]
@@ -46,13 +46,13 @@
46
46
  "hooks": [
47
47
  {
48
48
  "type": "command",
49
- "command": "node "$CLAUDE_PROJECT_DIR/.claude/helpers/hook-handler.cjs" session-restore",
49
+ "command": "node \"$CLAUDE_PROJECT_DIR/.claude/helpers/hook-handler.cjs\" session-restore",
50
50
  "timeout": 15000,
51
51
  "continueOnError": true
52
52
  },
53
53
  {
54
54
  "type": "command",
55
- "command": "node "$CLAUDE_PROJECT_DIR/.claude/helpers/auto-memory-hook.mjs" import",
55
+ "command": "node \"$CLAUDE_PROJECT_DIR/.claude/helpers/auto-memory-hook.mjs\" import",
56
56
  "timeout": 8000,
57
57
  "continueOnError": true
58
58
  }
@@ -64,7 +64,7 @@
64
64
  "hooks": [
65
65
  {
66
66
  "type": "command",
67
- "command": "node "$CLAUDE_PROJECT_DIR/.claude/helpers/hook-handler.cjs" session-end",
67
+ "command": "node \"$CLAUDE_PROJECT_DIR/.claude/helpers/hook-handler.cjs\" session-end",
68
68
  "timeout": 10000,
69
69
  "continueOnError": true
70
70
  }
@@ -76,7 +76,7 @@
76
76
  "hooks": [
77
77
  {
78
78
  "type": "command",
79
- "command": "node "$CLAUDE_PROJECT_DIR/.claude/helpers/hook-handler.cjs" status",
79
+ "command": "node \"$CLAUDE_PROJECT_DIR/.claude/helpers/hook-handler.cjs\" status",
80
80
  "timeout": 3000,
81
81
  "continueOnError": true
82
82
  }
@@ -88,7 +88,7 @@
88
88
  "hooks": [
89
89
  {
90
90
  "type": "command",
91
- "command": "node "$CLAUDE_PROJECT_DIR/.claude/helpers/auto-memory-hook.mjs" sync",
91
+ "command": "node \"$CLAUDE_PROJECT_DIR/.claude/helpers/auto-memory-hook.mjs\" sync",
92
92
  "timeout": 10000,
93
93
  "continueOnError": true
94
94
  }
@@ -98,7 +98,7 @@
98
98
  },
99
99
  "statusLine": {
100
100
  "type": "command",
101
- "command": "node "$CLAUDE_PROJECT_DIR/.claude/helpers/statusline.cjs"",
101
+ "command": "node \"$CLAUDE_PROJECT_DIR/.claude/helpers/statusline.cjs\"",
102
102
  "refreshMs": 5000,
103
103
  "enabled": true
104
104
  },
package/bin/cli.js CHANGED
@@ -301,7 +301,7 @@ if (isMCPMode) {
301
301
  }
302
302
 
303
303
  try {
304
- const result = await callMCPTool(toolName, toolParams, { sessionId });
304
+ const result = await callMCPTool(toolName, toolParams, { sessionId, transport: 'stdio' });
305
305
  // Failed task outcomes are data; an explicit handler error is a tool failure.
306
306
  const isError = result !== null && typeof result === 'object'
307
307
  && typeof result.error === 'string' && result.error.trim().length > 0;
package/bin/mcp-server.js CHANGED
@@ -190,7 +190,7 @@ async function handleMessage(message) {
190
190
  }
191
191
 
192
192
  try {
193
- const result = await callMCPTool(toolName, toolParams, { sessionId });
193
+ const result = await callMCPTool(toolName, toolParams, { sessionId, transport: 'stdio' });
194
194
  return {
195
195
  jsonrpc: '2.0',
196
196
  id: message.id,
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "generation": 7,
4
- "generatedAt": "2026-10-07T07:34:48.002Z",
5
- "gitSha": "b9ce891c",
4
+ "generatedAt": "2026-10-08T21:48:51.902Z",
5
+ "gitSha": "527796ae",
6
6
  "catalog": {
7
7
  "agents": 176,
8
8
  "tools": 423,
@@ -19,6 +19,8 @@ import { hasConsent } from '../funnel/index.js';
19
19
  import { domainForScope } from './scopes.js';
20
20
  /** Refresh before there is less than one minute left on the access token. */
21
21
  export const ACCESS_TOKEN_REFRESH_WINDOW_MS = 60_000;
22
+ // A rotating refresh credential may be spent only once per process attempt.
23
+ const pendingRefreshes = new Map();
22
24
  /**
23
25
  * Best-effort headlessness signal — a false negative just means the browser
24
26
  * flow is attempted and a real browser opens fine anyway; a false positive
@@ -213,30 +215,44 @@ export async function getValidAccessToken(profileName = 'default') {
213
215
  const cached = getSessionToken(profileName, ACCESS_TOKEN_REFRESH_WINDOW_MS);
214
216
  if (cached)
215
217
  return cached;
216
- if (!profile.keychainRef)
218
+ const keychainRef = profile.keychainRef;
219
+ if (!keychainRef)
217
220
  throw new SessionOnlyExpiredError(profileName);
218
- const sec = await loadSecurityOAuth();
219
- const keychain = await sec.createKeychainAdapter();
220
- const refreshTokenValue = await keychain.getSecret(KEYCHAIN_SERVICE, profile.keychainRef);
221
- if (!refreshTokenValue)
222
- throw new SessionOnlyExpiredError(profileName);
223
- const refreshed = await refreshAccessToken(refreshTokenValue);
224
- if (!refreshed.access_token)
225
- throw new Error('Cognitum refresh response did not contain an access token');
226
- // Cognitum rotates refresh tokens with reuse detection. Commit the rotated
227
- // credential first; if this write fails, do not publish/cache the access
228
- // token and do not retry the already-spent old refresh token here.
229
- if (refreshed.refresh_token) {
230
- await keychain.setSecret(KEYCHAIN_SERVICE, profile.keychainRef, refreshed.refresh_token);
231
- }
232
- const expiresAtMs = Date.now() + Math.max(0, refreshed.expires_in ?? 0) * 1000;
233
- setSessionToken(profileName, refreshed.access_token, expiresAtMs);
234
- setProfile(profileName, {
235
- ...profile,
236
- accountId: refreshed.account_email ?? profile.accountId,
237
- accessTokenExpiresAt: new Date(expiresAtMs).toISOString(),
238
- linkedAt: new Date().toISOString(),
239
- });
240
- return refreshed.access_token;
221
+ const refreshKey = JSON.stringify([profileName, keychainRef]);
222
+ const pending = pendingRefreshes.get(refreshKey);
223
+ if (pending)
224
+ return pending;
225
+ const refresh = (async () => {
226
+ const sec = await loadSecurityOAuth();
227
+ const keychain = await sec.createKeychainAdapter();
228
+ const refreshTokenValue = await keychain.getSecret(KEYCHAIN_SERVICE, keychainRef);
229
+ if (!refreshTokenValue)
230
+ throw new SessionOnlyExpiredError(profileName);
231
+ const refreshed = await refreshAccessToken(refreshTokenValue);
232
+ if (!refreshed.access_token)
233
+ throw new Error('Cognitum refresh response did not contain an access token');
234
+ // Cognitum rotates refresh tokens with reuse detection. Commit the rotated
235
+ // credential first; if this write fails, do not publish/cache the access
236
+ // token and do not retry the already-spent old refresh token here.
237
+ if (refreshed.refresh_token) {
238
+ await keychain.setSecret(KEYCHAIN_SERVICE, keychainRef, refreshed.refresh_token);
239
+ }
240
+ const expiresAtMs = Date.now() + Math.max(0, refreshed.expires_in ?? 0) * 1000;
241
+ setSessionToken(profileName, refreshed.access_token, expiresAtMs);
242
+ setProfile(profileName, {
243
+ ...profile,
244
+ accountId: refreshed.account_email ?? profile.accountId,
245
+ accessTokenExpiresAt: new Date(expiresAtMs).toISOString(),
246
+ linkedAt: new Date().toISOString(),
247
+ });
248
+ return refreshed.access_token;
249
+ })();
250
+ pendingRefreshes.set(refreshKey, refresh);
251
+ try {
252
+ return await refresh;
253
+ }
254
+ finally {
255
+ pendingRefreshes.delete(refreshKey);
256
+ }
241
257
  }
242
258
  //# sourceMappingURL=client.js.map
@@ -294,6 +294,18 @@ const statusCommand = {
294
294
  includeMetrics: true,
295
295
  includeHistory: false,
296
296
  });
297
+ // The MCP tool answers an unknown id with status "not_found" and no other fields.
298
+ // Rendering that as a record printed "Agent: undefined", "Invalid Date" and
299
+ // "undefined%" and exited 0, so scripts and agents took it as success.
300
+ if ((status.status) === 'not_found') {
301
+ if (ctx.flags.format === 'json') {
302
+ output.printJson(status);
303
+ }
304
+ else {
305
+ output.printError(`Agent not found: ${agentId}`);
306
+ }
307
+ return { success: false, exitCode: 1 };
308
+ }
297
309
  if (ctx.flags.format === 'json') {
298
310
  output.printJson(status);
299
311
  return { success: true, data: status };
@@ -254,14 +254,16 @@ const codeCommand = {
254
254
  const targetPath = resolve(ctx.flags.path || '.');
255
255
  const analysisType = ctx.flags.type || 'quality';
256
256
  const formatJson = ctx.flags.format === 'json';
257
- output.writeln();
258
- output.writeln(output.bold('Code Analysis'));
259
- output.writeln(output.dim('-'.repeat(50)));
257
+ if (!formatJson) {
258
+ output.writeln();
259
+ output.writeln(output.bold('Code Analysis'));
260
+ output.writeln(output.dim('-'.repeat(50)));
261
+ }
260
262
  const spinner = output.createSpinner({ text: `Analyzing ${targetPath}...`, spinner: 'dots' });
261
263
  spinner.start();
262
264
  try {
263
265
  const files = await scanSourceFiles(targetPath);
264
- if (files.length === 0) {
266
+ if (files.length === 0 && !formatJson) {
265
267
  spinner.stop();
266
268
  output.printWarning('No source files found');
267
269
  return { success: true };
@@ -310,16 +312,16 @@ const codeCommand = {
310
312
  const totalTodos = fileStats.reduce((s, f) => s + f.todos, 0);
311
313
  const totalFunctions = fileStats.reduce((s, f) => s + f.functions, 0);
312
314
  const totalImports = fileStats.reduce((s, f) => s + f.imports, 0);
313
- const avgFileSize = Math.round(totalLoc / files.length);
314
- const longestFile = fileStats.reduce((a, b) => a.loc > b.loc ? a : b);
315
- const avgFnPerFile = (totalFunctions / files.length).toFixed(1);
316
- const deepestNesting = fileStats.reduce((a, b) => a.maxNesting > b.maxNesting ? a : b);
317
- const allSecurityIssues = fileStats.filter(f => f.securityIssues.length > 0);
315
+ const avgFileSize = files.length > 0 ? Math.round(totalLoc / files.length) : 0;
318
316
  if (formatJson) {
319
317
  const jsonData = { type: analysisType, path: targetPath, files: files.length, totalLoc, totalTodos, totalFunctions, totalImports, avgFileSize, fileStats: fileStats.map(f => ({ relativePath: path.relative(targetPath, f.file), loc: f.loc, todos: f.todos, functions: f.functions, imports: f.imports, maxNesting: f.maxNesting, securityIssues: f.securityIssues })) };
320
318
  output.printJson(jsonData);
321
319
  return { success: true, data: jsonData };
322
320
  }
321
+ const longestFile = fileStats.reduce((a, b) => a.loc > b.loc ? a : b);
322
+ const avgFnPerFile = (totalFunctions / files.length).toFixed(1);
323
+ const deepestNesting = fileStats.reduce((a, b) => a.maxNesting > b.maxNesting ? a : b);
324
+ const allSecurityIssues = fileStats.filter(f => f.securityIssues.length > 0);
323
325
  if (analysisType === 'quality') {
324
326
  output.printBox([`Files: ${files.length}`, `Lines of Code: ${totalLoc.toLocaleString()}`, `Avg File Size: ${avgFileSize} LOC`, `TODO/FIXME: ${totalTodos}`, `Functions: ${totalFunctions}`, `Imports: ${totalImports}`].join('\n'), 'Quality Summary');
325
327
  output.writeln();
@@ -549,7 +551,7 @@ const astCommand = {
549
551
  }
550
552
  }
551
553
  spinner.stop();
552
- if (results.length === 0) {
554
+ if (results.length === 0 && formatType !== 'json') {
553
555
  output.printWarning('No files analyzed');
554
556
  return { success: true };
555
557
  }
@@ -559,7 +561,7 @@ const astCommand = {
559
561
  functions: results.reduce((sum, r) => sum + r.functions.length, 0),
560
562
  classes: results.reduce((sum, r) => sum + r.classes.length, 0),
561
563
  imports: results.reduce((sum, r) => sum + r.imports.length, 0),
562
- avgComplexity: results.reduce((sum, r) => sum + r.complexity.cyclomatic, 0) / results.length,
564
+ avgComplexity: results.length > 0 ? results.reduce((sum, r) => sum + r.complexity.cyclomatic, 0) / results.length : 0,
563
565
  totalLoc: results.reduce((sum, r) => sum + r.complexity.loc, 0),
564
566
  };
565
567
  // JSON output
@@ -1230,9 +1232,11 @@ const depsCommand = {
1230
1232
  const showOutdated = ctx.flags.outdated;
1231
1233
  const checkSecurity = ctx.flags.security;
1232
1234
  const formatJson = ctx.flags.format === 'json';
1233
- output.writeln();
1234
- output.writeln(output.bold('Dependency Analysis'));
1235
- output.writeln(output.dim('-'.repeat(50)));
1235
+ if (!formatJson || showOutdated || checkSecurity) {
1236
+ output.writeln();
1237
+ output.writeln(output.bold('Dependency Analysis'));
1238
+ output.writeln(output.dim('-'.repeat(50)));
1239
+ }
1236
1240
  try {
1237
1241
  const pkgPath = resolve('package.json');
1238
1242
  let pkgContent;
@@ -181,8 +181,8 @@ const configCommand = {
181
181
  name: 'config',
182
182
  description: 'Configure max iterations, timeout, and task sources',
183
183
  options: [
184
- { name: 'max-iterations', type: 'string', description: 'Max re-engagement iterations (1-1000)' },
185
- { name: 'timeout', type: 'string', description: 'Timeout in minutes (1-1440)' },
184
+ { name: 'max-iterations', type: 'number', description: 'Max re-engagement iterations (1-1000)' },
185
+ { name: 'timeout', type: 'number', description: 'Timeout in minutes (1-1440)' },
186
186
  { name: 'task-sources', type: 'string', description: `Comma-separated task sources: ${[...VALID_TASK_SOURCES].join(', ')}` },
187
187
  ],
188
188
  action: async (ctx) => {
@@ -3,19 +3,22 @@
3
3
  * Manages background worker daemon (Node.js-based, similar to shell helpers)
4
4
  */
5
5
  import { output } from '../output.js';
6
- import { getDaemon, startDaemon, stopDaemon } from '../services/worker-daemon.js';
6
+ import { getDaemon, startDaemon, stopDaemon, parseEnabledWorkers } from '../services/worker-daemon.js';
7
7
  import { resolveDaemonProjectRoot } from '../services/daemon-autostart.js';
8
8
  import { fork } from 'child_process';
9
9
  import { fileURLToPath } from 'url';
10
10
  import { dirname, join, resolve } from 'path';
11
11
  import { homedir } from 'os';
12
12
  import * as fs from 'fs';
13
+ // Syntax guard for worker-name flags forwarded to a background child.
14
+ // The start action validates the names against the daemon's supported workers.
15
+ const WORKERS_RE = /^[a-z][a-z0-9_-]*(,[a-z][a-z0-9_-]*)*$/;
13
16
  // Start daemon subcommand
14
17
  const startCommand = {
15
18
  name: 'start',
16
19
  description: 'Start the worker daemon with all enabled background workers',
17
20
  options: [
18
- { name: 'workers', short: 'w', type: 'string', description: 'Comma-separated list of workers to enable (default: map,audit,optimize,consolidate,testgaps)' },
21
+ { name: 'workers', short: 'w', type: 'string', description: 'Comma-separated list of workers to enable (default: saved selection or map,audit,optimize,consolidate,testgaps,backup,harness)' },
19
22
  // ADR-174 M3: consolidate now runs a real memory-distillation pass
20
23
  // (memory_entries -> episodes/reasoning_patterns/causal_edges) instead of
21
24
  // a no-op stub. This opt-out skips just that pass for the life of this
@@ -119,6 +122,16 @@ const startCommand = {
119
122
  output.printWarning(`Ignoring invalid --ttl value: ${sanitize(rawTtl)}`);
120
123
  }
121
124
  }
125
+ if (ctx.flags.workers !== undefined) {
126
+ try {
127
+ config.enabledWorkers = parseEnabledWorkers(ctx.flags.workers);
128
+ }
129
+ catch (error) {
130
+ if (!quiet)
131
+ output.printError(error.message);
132
+ return { success: false, exitCode: 1 };
133
+ }
134
+ }
122
135
  // Check if background daemon already running (skip if we ARE the daemon process)
123
136
  //
124
137
  // #2407 — without an atomic lockfile, N concurrent `daemon start` calls
@@ -518,7 +531,6 @@ async function startBackgroundDaemon(projectRoot, quiet, forwarded = {}) {
518
531
  // through — argv goes straight to a forked process so reject anything
519
532
  // that doesn't look like a comma-separated worker-name list or one of
520
533
  // the allowed sandbox modes.
521
- const WORKERS_RE = /^[a-z][a-z0-9_-]*(,[a-z][a-z0-9_-]*)*$/;
522
534
  if (typeof workers === 'string' && workers.length > 0 && WORKERS_RE.test(workers)) {
523
535
  forkArgs.push('--workers', workers);
524
536
  }
@@ -7,7 +7,7 @@
7
7
  import { output } from '../output.js';
8
8
  import { existsSync, readFileSync, statSync, openSync, readSync, closeSync } from 'fs';
9
9
  import { join, dirname } from 'path';
10
- import { fileURLToPath } from 'url';
10
+ import { fileURLToPath, pathToFileURL } from 'url';
11
11
  import { createHash } from 'crypto';
12
12
  import { execSync, exec, execFile } from 'child_process';
13
13
  import { promisify } from 'util';
@@ -2117,7 +2117,7 @@ async function checkMetaharnessIntegration() {
2117
2117
  // Runtime smoke: import the similarity module and exercise it
2118
2118
  try {
2119
2119
  const modPath = join(pluginDir, 'scripts', '_similarity.mjs');
2120
- const mod = await import(modPath);
2120
+ const mod = await import(pathToFileURL(modPath).href);
2121
2121
  if (typeof mod.similarity !== 'function') {
2122
2122
  return {
2123
2123
  name: 'MetaHarness integration (ADR-150)',
@@ -2144,7 +2144,7 @@ async function checkMetaharnessIntegration() {
2144
2144
  // parallelization primitives oia-audit depends on; if they're
2145
2145
  // missing, oia-audit's import fails and the whole pipeline breaks.
2146
2146
  const harnessPath = join(pluginDir, 'scripts', '_harness.mjs');
2147
- const harnessMod = await import(harnessPath);
2147
+ const harnessMod = await import(pathToFileURL(harnessPath).href);
2148
2148
  if (typeof harnessMod.parseMcpScanText !== 'function') {
2149
2149
  return {
2150
2150
  name: 'MetaHarness integration (ADR-150)',
@@ -2580,6 +2580,67 @@ export const doctorCommand = {
2580
2580
  const verbose = ctx.flags.verbose;
2581
2581
  // Parser camelCases kebab-case flag names — read via `fixHandles`, not `['fix-handles']`.
2582
2582
  const fixHandles = ctx.flags.fixHandles;
2583
+ // #2677: `--component memory` now runs the whole memory-health suite,
2584
+ // not just the existence check. Values can be a single check or an
2585
+ // array — expanded at execution time. Stuinfla's report showed the
2586
+ // existence-only check reporting PASS on a 99.97%-empty and even a
2587
+ // SQLite-malformed DB; the array here layers integrity → content →
2588
+ // embedding coverage over the existing existence probe; check 6 then
2589
+ // verifies that distilled episodes are actually Reflexion-retrievable and
2590
+ // execution feedback carries a lesson.
2591
+ const componentMap = {
2592
+ 'version': checkVersionFreshness,
2593
+ 'freshness': checkVersionFreshness,
2594
+ 'node': checkNodeVersion,
2595
+ 'npm': checkNpmVersion,
2596
+ 'claude': checkClaudeCode,
2597
+ 'browser': checkAgentBrowserVersion,
2598
+ 'config': checkConfigFile,
2599
+ 'stale-settings': checkStaleSettingsNpx, // #2448
2600
+ 'helpers': () => checkHelperIntegrity(), // #3565
2601
+ 'daemon': checkDaemonStatus,
2602
+ get memory() {
2603
+ return [
2604
+ checkMemoryDatabase, // existing: exists + statable (unchanged)
2605
+ checkMemoryIntegrity, // #2677 check 1: sql.js open + PRAGMA integrity_check
2606
+ ...nativeAgentDbChecks, // #3195: present AgentDB authority, checked independently
2607
+ checkMemoryPersistenceDriver, // #2968/#3321: read-only native capability probe
2608
+ checkMemoryPackageVersion, // #3392: loaded memory package satisfies the declared range
2609
+ checkMemoryContent, // #2677 check 2: memory_entries content coverage
2610
+ checkMemoryEmbeddingCoverage, // #2677 check 3: vector coverage on populated rows
2611
+ checkMemoryReflexionCoverage, // #2677 check 6: episodes are retrievable
2612
+ checkMemoryCritiqueCoverage, // #2677 check 6: feedback carries lessons
2613
+ ];
2614
+ },
2615
+ 'memory-package': checkMemoryPackageVersion, // #3392
2616
+ 'learning': checkLearningBridge, // #2545
2617
+ 'learning-bridge': checkLearningBridge, // #2545
2618
+ 'api': checkApiKeys,
2619
+ 'git': checkGit,
2620
+ 'mcp': checkMcpServers,
2621
+ 'mcp-overhead': checkMcpSchemaOverhead,
2622
+ 'aidefence': checkAIDefence, // #1807
2623
+ 'disk': checkDiskSpace,
2624
+ 'typescript': checkBuildTools,
2625
+ 'agentic-flow': checkAgenticFlow,
2626
+ 'encryption': checkEncryptionAtRest, // ADR-096 Phase 5
2627
+ 'federation': checkFederationBreaker, // ADR-097 Phase 4
2628
+ 'metaharness': [checkMetaharness, checkMetaharnessDeclaredPackages, checkMetaharnessIntegration], // ADR-150 — upstream + declared deps + ruflo-side
2629
+ 'metaharness-integration': checkMetaharnessIntegration, // iter 45 — ruflo-side
2630
+ 'funnel': checkFunnel, // ADR-305
2631
+ // ADR-307 — deep-dive array, same pattern as 'memory' above: the cheap
2632
+ // sponsored-consent check first, then binary/process/bind in the order
2633
+ // a user would actually debug them (is it installed? running? exposed?).
2634
+ 'proxy': [checkProxySponsoredConsent, checkProxyBinary, checkProxyProcess, checkProxyBindAddress],
2635
+ 'auth': checkAuth, // ADR-306
2636
+ 'typesafe': checkTypesafeRouter, // opt-in @ruvector/typesafe task router
2637
+ 'mods': checkMods, // ADR-404 — ruflo as a Claude Code mod
2638
+ };
2639
+ if (component !== undefined && !Object.hasOwn(componentMap, component)) {
2640
+ const message = `Unknown doctor component: ${JSON.stringify(component)}. Valid components: ${Object.keys(componentMap).join(', ')}`;
2641
+ output.printError(message);
2642
+ return { success: false, exitCode: 1, message };
2643
+ }
2583
2644
  // Early-return short-circuit: `--fix-handles` is a targeted mitigation, not
2584
2645
  // part of the health-check flow. Runs, reports, exits.
2585
2646
  if (fixHandles) {
@@ -2684,62 +2745,8 @@ export const doctorCommand = {
2684
2745
  checkAuth, // ADR-306 — Cognitum identity (warn-only; never fails bare `ruflo doctor`)
2685
2746
  checkMods, // ADR-404 — Claude Code mod path (warn-only)
2686
2747
  ];
2687
- // #2677: `--component memory` now runs the whole memory-health suite,
2688
- // not just the existence check. Values can be a single check or an
2689
- // array — expanded at execution time. Stuinfla's report showed the
2690
- // existence-only check reporting PASS on a 99.97%-empty and even a
2691
- // SQLite-malformed DB; the array here layers integrity → content →
2692
- // embedding coverage over the existing existence probe; check 6 then
2693
- // verifies that distilled episodes are actually Reflexion-retrievable and
2694
- // execution feedback carries a lesson.
2695
- const componentMap = {
2696
- 'version': checkVersionFreshness,
2697
- 'freshness': checkVersionFreshness,
2698
- 'node': checkNodeVersion,
2699
- 'npm': checkNpmVersion,
2700
- 'claude': checkClaudeCode,
2701
- 'browser': checkAgentBrowserVersion,
2702
- 'config': checkConfigFile,
2703
- 'stale-settings': checkStaleSettingsNpx, // #2448
2704
- 'helpers': () => checkHelperIntegrity(), // #3565
2705
- 'daemon': checkDaemonStatus,
2706
- 'memory': [
2707
- checkMemoryDatabase, // existing: exists + statable (unchanged)
2708
- checkMemoryIntegrity, // #2677 check 1: sql.js open + PRAGMA integrity_check
2709
- ...nativeAgentDbChecks, // #3195: present AgentDB authority, checked independently
2710
- checkMemoryPersistenceDriver, // #2968/#3321: read-only native capability probe
2711
- checkMemoryPackageVersion, // #3392: loaded memory package satisfies the declared range
2712
- checkMemoryContent, // #2677 check 2: memory_entries content coverage
2713
- checkMemoryEmbeddingCoverage, // #2677 check 3: vector coverage on populated rows
2714
- checkMemoryReflexionCoverage, // #2677 check 6: episodes are retrievable
2715
- checkMemoryCritiqueCoverage, // #2677 check 6: feedback carries lessons
2716
- ],
2717
- 'memory-package': checkMemoryPackageVersion, // #3392
2718
- 'learning': checkLearningBridge, // #2545
2719
- 'learning-bridge': checkLearningBridge, // #2545
2720
- 'api': checkApiKeys,
2721
- 'git': checkGit,
2722
- 'mcp': checkMcpServers,
2723
- 'mcp-overhead': checkMcpSchemaOverhead,
2724
- 'aidefence': checkAIDefence, // #1807
2725
- 'disk': checkDiskSpace,
2726
- 'typescript': checkBuildTools,
2727
- 'agentic-flow': checkAgenticFlow,
2728
- 'encryption': checkEncryptionAtRest, // ADR-096 Phase 5
2729
- 'federation': checkFederationBreaker, // ADR-097 Phase 4
2730
- 'metaharness': [checkMetaharness, checkMetaharnessDeclaredPackages, checkMetaharnessIntegration], // ADR-150 — upstream + declared deps + ruflo-side
2731
- 'metaharness-integration': checkMetaharnessIntegration, // iter 45 — ruflo-side
2732
- 'funnel': checkFunnel, // ADR-305
2733
- // ADR-307 — deep-dive array, same pattern as 'memory' above: the cheap
2734
- // sponsored-consent check first, then binary/process/bind in the order
2735
- // a user would actually debug them (is it installed? running? exposed?).
2736
- 'proxy': [checkProxySponsoredConsent, checkProxyBinary, checkProxyProcess, checkProxyBindAddress],
2737
- 'auth': checkAuth, // ADR-306
2738
- 'typesafe': checkTypesafeRouter, // opt-in @ruvector/typesafe task router
2739
- 'mods': checkMods, // ADR-404 — ruflo as a Claude Code mod
2740
- };
2741
2748
  let checksToRun = allChecks;
2742
- if (component && componentMap[component]) {
2749
+ if (component !== undefined) {
2743
2750
  const entry = componentMap[component];
2744
2751
  checksToRun = Array.isArray(entry) ? entry : [entry];
2745
2752
  }
@@ -1016,6 +1016,13 @@ const consensusCommand = {
1016
1016
  const action = ctx.flags.action || 'list';
1017
1017
  try {
1018
1018
  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() });
1019
+ if (result.error) {
1020
+ if (ctx.flags.format === 'json')
1021
+ output.printJson(result);
1022
+ else
1023
+ output.printError(`Consensus error: ${String(result.error)}`);
1024
+ return { success: false, exitCode: 1, data: result };
1025
+ }
1019
1026
  if (ctx.flags.format === 'json') {
1020
1027
  output.printJson(result);
1021
1028
  return { success: true, data: result };
@@ -8,7 +8,7 @@
8
8
  import { output } from '../output.js';
9
9
  import { confirm } from '../prompt.js';
10
10
  import { installParentDeathWatchdog } from '../runtime/parent-death-watchdog.js';
11
- import { getServerManager, getMCPServerStatus, filterAdvertisedMcpTools, parseMcpToolSelection, } from '../mcp-server.js';
11
+ import { getServerManager, getMCPServerStatus, resolveMcpHttpAuthToken, isLoopbackHost, filterAdvertisedMcpTools, parseMcpToolSelection, } from '../mcp-server.js';
12
12
  import { listMCPTools, callMCPTool, hasTool } from '../mcp-client.js';
13
13
  // MCP tools categories
14
14
  const TOOL_CATEGORIES = [
@@ -71,6 +71,16 @@ const startCommand = {
71
71
  description: 'HTTP request timeout in milliseconds (default 30000; env RUFLO_MCP_REQUEST_TIMEOUT_MS)',
72
72
  type: 'number'
73
73
  },
74
+ {
75
+ name: 'auth-token-file',
76
+ description: 'Read the HTTP bearer token from this file (http transport). Preferred over --auth-token; env RUFLO_MCP_HTTP_TOKEN also works',
77
+ type: 'string'
78
+ },
79
+ {
80
+ name: 'auth-token',
81
+ description: 'HTTP bearer token (16+ printable ASCII chars). Visible in argv/ps - prefer RUFLO_MCP_HTTP_TOKEN or --auth-token-file',
82
+ type: 'string'
83
+ },
74
84
  {
75
85
  name: 'daemon',
76
86
  short: 'd',
@@ -90,6 +100,8 @@ const startCommand = {
90
100
  { command: 'claude-flow mcp start', description: 'Start with defaults (stdio)' },
91
101
  { command: 'claude-flow mcp start -p 8080 -t http', description: 'Start HTTP server' },
92
102
  { command: 'claude-flow mcp start -t http --request-timeout-ms 120000', description: 'Allow slower cold-start HTTP tools' },
103
+ { command: 'RUFLO_MCP_HTTP_TOKEN=<16+ chars> claude-flow mcp start -t http', description: 'HTTP server requiring "Authorization: Bearer <token>" on every request (except GET /health)' },
104
+ { command: 'RUFLO_MCP_HTTP_TOKEN=<16+ chars> claude-flow mcp start -t http --host 0.0.0.0', description: 'Non-loopback bind: allowed only with a token (or RUFLO_MCP_ALLOW_UNAUTHENTICATED_HTTP=1)' },
93
105
  { command: 'claude-flow mcp start -d', description: 'Start as daemon' },
94
106
  { command: 'claude-flow mcp start -f', description: 'Force restart (kill existing)' }
95
107
  ],
@@ -109,6 +121,17 @@ const startCommand = {
109
121
  output.printError('Request timeout must be an integer from 1 to 3600000 milliseconds');
110
122
  return { success: false, exitCode: 1 };
111
123
  }
124
+ let authToken;
125
+ try {
126
+ authToken = resolveMcpHttpAuthToken({
127
+ token: (ctx.flags.authToken ?? ctx.flags['auth-token']),
128
+ tokenFile: (ctx.flags.authTokenFile ?? ctx.flags['auth-token-file']),
129
+ });
130
+ }
131
+ catch (error) {
132
+ output.printError(error.message);
133
+ return { success: false, exitCode: 1 };
134
+ }
112
135
  const daemon = ctx.flags.daemon ?? false;
113
136
  const force = ctx.flags.force ?? false;
114
137
  output.writeln();
@@ -166,6 +189,7 @@ const startCommand = {
166
189
  port,
167
190
  tools: !tools || tools === 'all' ? 'all' : tools.split(','),
168
191
  ...(requestTimeoutMs === undefined ? {} : { requestTimeoutMs }),
192
+ ...(authToken ? { authToken } : {}),
169
193
  daemonize: daemon,
170
194
  };
171
195
  try {
@@ -218,6 +242,15 @@ const startCommand = {
218
242
  if (transport === 'http') {
219
243
  output.writeln(output.dim(` Health: http://${host}:${port}/health`));
220
244
  output.writeln(output.dim(` RPC: http://${host}:${port}/rpc`));
245
+ if (authToken) {
246
+ output.writeln(output.dim(' Auth: bearer token required (Authorization: Bearer <token>); /health is public'));
247
+ }
248
+ else if (isLoopbackHost(host)) {
249
+ output.printWarning('No auth token set: any local process (and DNS-rebinding web pages) can call tools. Set RUFLO_MCP_HTTP_TOKEN to require a bearer token.');
250
+ }
251
+ else {
252
+ output.printWarning('Running UNAUTHENTICATED on a non-loopback host (RUFLO_MCP_ALLOW_UNAUTHENTICATED_HTTP is set).');
253
+ }
221
254
  }
222
255
  else if (transport === 'websocket') {
223
256
  output.writeln(output.dim(` WebSocket: ws://${host}:${port}/ws`));
@@ -596,6 +629,7 @@ const execCommand = {
596
629
  const startTime = performance.now();
597
630
  const result = await callMCPTool(tool, params, {
598
631
  sessionId: `cli-${Date.now().toString(36)}`,
632
+ transport: 'cli',
599
633
  requestId: `exec-${Date.now()}`,
600
634
  });
601
635
  const duration = performance.now() - startTime;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * V3 CLI `memory backup` command.
3
3
  *
4
- * WAL-safe snapshot of the vector-memory DB (`.swarm/memory.db`) with rotation
4
+ * WAL-safe snapshots of the active CLI and AgentDB stores with rotation
5
5
  * and optional GCS offsite. Thin surface over ../services/memory-backup.js; the
6
6
  * daemon's nightly `backup` worker calls the same service.
7
7
  */