claude-flow 3.32.34 → 3.32.36

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 (69) hide show
  1. package/.claude/helpers/hook-handler.cjs +134 -3
  2. package/.claude/helpers/intelligence.cjs +47 -4
  3. package/node_modules/@claude-flow/codex/.agents/skills/github-automation/SKILL.md +32 -0
  4. package/node_modules/@claude-flow/codex/.agents/skills/performance-analysis/SKILL.md +32 -0
  5. package/node_modules/@claude-flow/codex/dist/dual-mode/cli.d.ts +4 -0
  6. package/node_modules/@claude-flow/codex/dist/dual-mode/cli.d.ts.map +1 -1
  7. package/node_modules/@claude-flow/codex/dist/dual-mode/cli.js +20 -1
  8. package/node_modules/@claude-flow/codex/dist/dual-mode/cli.js.map +1 -1
  9. package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.d.ts +4 -0
  10. package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.d.ts.map +1 -1
  11. package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.js +18 -2
  12. package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.js.map +1 -1
  13. package/node_modules/@claude-flow/codex/dist/generators/skill-md.d.ts +11 -5
  14. package/node_modules/@claude-flow/codex/dist/generators/skill-md.d.ts.map +1 -1
  15. package/node_modules/@claude-flow/codex/dist/generators/skill-md.js +84 -849
  16. package/node_modules/@claude-flow/codex/dist/generators/skill-md.js.map +1 -1
  17. package/node_modules/@claude-flow/codex/dist/initializer.d.ts.map +1 -1
  18. package/node_modules/@claude-flow/codex/dist/initializer.js +9 -13
  19. package/node_modules/@claude-flow/codex/dist/initializer.js.map +1 -1
  20. package/package.json +1 -1
  21. package/v3/@claude-flow/cli/bin/cli.js +25 -1
  22. package/v3/@claude-flow/cli/catalog-manifest.json +4 -4
  23. package/v3/@claude-flow/cli/dist/src/commands/agent.js +50 -2
  24. package/v3/@claude-flow/cli/dist/src/commands/doctor.js +159 -11
  25. package/v3/@claude-flow/cli/dist/src/commands/hooks.js +63 -4
  26. package/v3/@claude-flow/cli/dist/src/commands/mcp.js +1 -1
  27. package/v3/@claude-flow/cli/dist/src/commands/memory-distill.js +1 -0
  28. package/v3/@claude-flow/cli/dist/src/commands/metaharness.js +4 -0
  29. package/v3/@claude-flow/cli/dist/src/commands/swarm.js +89 -5
  30. package/v3/@claude-flow/cli/dist/src/config-adapter.js +1 -0
  31. package/v3/@claude-flow/cli/dist/src/index.js +3 -2
  32. package/v3/@claude-flow/cli/dist/src/init/executor.js +16 -10
  33. package/v3/@claude-flow/cli/dist/src/init/helpers-generator.js +15 -4
  34. package/v3/@claude-flow/cli/dist/src/init/statusline-generator.js +18 -5
  35. package/v3/@claude-flow/cli/dist/src/mcp-server.d.ts +25 -0
  36. package/v3/@claude-flow/cli/dist/src/mcp-server.js +57 -2
  37. package/v3/@claude-flow/cli/dist/src/mcp-tools/agent-tools.js +10 -0
  38. package/v3/@claude-flow/cli/dist/src/mcp-tools/embeddings-tools.js +51 -10
  39. package/v3/@claude-flow/cli/dist/src/mcp-tools/hooks-tools.js +120 -60
  40. package/v3/@claude-flow/cli/dist/src/mcp-tools/memory-tools.js +14 -2
  41. package/v3/@claude-flow/cli/dist/src/mcp-tools/metaharness-tools.js +7 -0
  42. package/v3/@claude-flow/cli/dist/src/mcp-tools/swarm-tools.d.ts +16 -0
  43. package/v3/@claude-flow/cli/dist/src/mcp-tools/swarm-tools.js +172 -7
  44. package/v3/@claude-flow/cli/dist/src/memory/memory-bridge.d.ts +2 -0
  45. package/v3/@claude-flow/cli/dist/src/memory/memory-bridge.js +5 -1
  46. package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.d.ts +9 -1
  47. package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.js +66 -19
  48. package/v3/@claude-flow/cli/dist/src/services/daemon-autostart.js +7 -4
  49. package/v3/@claude-flow/cli/dist/src/services/flywheel-receipt.d.ts +3 -0
  50. package/v3/@claude-flow/cli/dist/src/services/flywheel-receipt.js +1 -0
  51. package/v3/@claude-flow/cli/dist/src/services/harness-flywheel-runtime.d.ts +6 -0
  52. package/v3/@claude-flow/cli/dist/src/services/harness-flywheel-runtime.js +19 -19
  53. package/v3/@claude-flow/cli/dist/src/services/harness-flywheel.d.ts +2 -0
  54. package/v3/@claude-flow/cli/dist/src/services/harness-flywheel.js +1 -0
  55. package/v3/@claude-flow/cli/dist/src/services/harness-project-anchor.d.ts +23 -0
  56. package/v3/@claude-flow/cli/dist/src/services/harness-project-anchor.js +129 -0
  57. package/v3/@claude-flow/cli/dist/src/services/learned-routing.d.ts +34 -0
  58. package/v3/@claude-flow/cli/dist/src/services/learned-routing.js +85 -0
  59. package/v3/@claude-flow/cli/dist/src/services/memory-distillation.d.ts +1 -0
  60. package/v3/@claude-flow/cli/dist/src/services/memory-distillation.js +81 -5
  61. package/v3/@claude-flow/cli/dist/src/services/pheromone-adaptive.d.ts +71 -0
  62. package/v3/@claude-flow/cli/dist/src/services/pheromone-adaptive.js +214 -0
  63. package/v3/@claude-flow/cli/dist/src/services/worker-daemon.js +5 -1
  64. package/v3/@claude-flow/cli/package.json +1 -1
  65. package/v3/@claude-flow/shared/dist/core/config/loader.js +1 -1
  66. package/v3/@claude-flow/shared/dist/core/config/schema.d.ts +12 -12
  67. package/v3/@claude-flow/shared/dist/core/config/schema.js +1 -1
  68. package/v3/@claude-flow/shared/dist/core/interfaces/coordinator.interface.d.ts +1 -1
  69. package/v3/@claude-flow/shared/dist/types/swarm.types.js +18 -0
@@ -1218,8 +1218,20 @@ const metricsCommand = {
1218
1218
  const totalPatterns = safeNum(rawMetrics.patterns?.total ?? rawMetrics.summary?.patternsLearned);
1219
1219
  const successfulPatterns = safeNum(rawMetrics.patterns?.successful ?? Math.round(safeNum(rawMetrics.summary?.successRate) * totalPatterns));
1220
1220
  const failedPatterns = Math.max(0, safeNum(rawMetrics.patterns?.failed ?? totalPatterns - successfulPatterns));
1221
+ const unknownPatterns = Math.max(0, safeNum(rawMetrics.patterns?.unknown));
1222
+ const describedPatterns = Math.max(0, safeNum(rawMetrics.patterns?.described));
1223
+ const descriptionCoverage = safeNum(rawMetrics.patterns?.descriptionCoverage ??
1224
+ (totalPatterns > 0 ? describedPatterns / totalPatterns : 0));
1221
1225
  const avgConfidence = safeNum(rawMetrics.patterns?.avgConfidence ?? rawMetrics.summary?.avgQuality);
1222
- const routingAccuracy = safeNum(rawMetrics.agents?.routingAccuracy ?? rawMetrics.routing?.avgConfidence);
1226
+ const routingAccuracy = rawMetrics.agents?.routingAccuracy == null
1227
+ ? null
1228
+ : safeNum(rawMetrics.agents.routingAccuracy);
1229
+ const averageRoutingConfidence = safeNum(rawMetrics.agents?.averageConfidence ??
1230
+ rawMetrics.routing?.avgConfidence ??
1231
+ rawMetrics.patterns?.avgConfidence);
1232
+ const routingOutcomeSuccessRate = rawMetrics.agents?.outcomeSuccessRate == null
1233
+ ? null
1234
+ : safeNum(rawMetrics.agents.outcomeSuccessRate);
1223
1235
  const totalRoutes = safeNum(rawMetrics.agents?.totalRoutes ?? rawMetrics.routing?.totalRoutes);
1224
1236
  const topAgent = rawMetrics.agents?.topAgent ?? rawMetrics.routing?.topAgents?.[0]?.agent ?? 'n/a';
1225
1237
  const totalCommands = safeNum(rawMetrics.commands?.totalExecuted ?? rawMetrics.commands?.totalCommands);
@@ -1227,8 +1239,22 @@ const metricsCommand = {
1227
1239
  const avgRiskScore = safeNum(rawMetrics.commands?.avgRiskScore ?? rawMetrics.commands?.avgExecutionTime);
1228
1240
  const result = {
1229
1241
  ...rawMetrics,
1230
- patterns: { total: totalPatterns, successful: successfulPatterns, failed: failedPatterns, avgConfidence },
1231
- agents: { routingAccuracy, totalRoutes, topAgent },
1242
+ patterns: {
1243
+ total: totalPatterns,
1244
+ successful: successfulPatterns,
1245
+ failed: failedPatterns,
1246
+ unknown: unknownPatterns,
1247
+ described: describedPatterns,
1248
+ descriptionCoverage,
1249
+ avgConfidence,
1250
+ },
1251
+ agents: {
1252
+ routingAccuracy,
1253
+ averageConfidence: averageRoutingConfidence,
1254
+ outcomeSuccessRate: routingOutcomeSuccessRate,
1255
+ totalRoutes,
1256
+ topAgent,
1257
+ },
1232
1258
  commands: { totalExecuted: totalCommands, successRate: commandSuccessRate, avgRiskScore },
1233
1259
  };
1234
1260
  if (ctx.flags.format === 'json') {
@@ -1246,6 +1272,8 @@ const metricsCommand = {
1246
1272
  { metric: 'Total Patterns', value: totalPatterns },
1247
1273
  { metric: 'Successful', value: output.success(String(successfulPatterns)) },
1248
1274
  { metric: 'Failed', value: output.error(String(failedPatterns)) },
1275
+ ...(unknownPatterns > 0 ? [{ metric: 'Unclassified', value: String(unknownPatterns) }] : []),
1276
+ { metric: 'With Task Context', value: `${(descriptionCoverage * 100).toFixed(1)}%` },
1249
1277
  { metric: 'Avg Confidence', value: `${(avgConfidence * 100).toFixed(1)}%` }
1250
1278
  ]
1251
1279
  });
@@ -1258,7 +1286,14 @@ const metricsCommand = {
1258
1286
  { key: 'value', header: 'Value', width: 20, align: 'right' }
1259
1287
  ],
1260
1288
  data: [
1261
- { metric: 'Routing Accuracy', value: `${(routingAccuracy * 100).toFixed(1)}%` },
1289
+ {
1290
+ metric: routingAccuracy === null ? 'Avg Confidence' : 'Routing Accuracy',
1291
+ value: `${((routingAccuracy ?? averageRoutingConfidence) * 100).toFixed(1)}%`,
1292
+ },
1293
+ ...(routingOutcomeSuccessRate === null ? [] : [{
1294
+ metric: 'Outcome Success Rate',
1295
+ value: `${(routingOutcomeSuccessRate * 100).toFixed(1)}%`,
1296
+ }]),
1262
1297
  { metric: 'Total Routes', value: totalRoutes },
1263
1298
  { metric: 'Top Agent', value: output.highlight(topAgent) }
1264
1299
  ]
@@ -1672,6 +1707,26 @@ const postTaskCommand = {
1672
1707
  description: 'Agent that executed the task',
1673
1708
  type: 'string'
1674
1709
  },
1710
+ {
1711
+ name: 'agent-role',
1712
+ description: 'Stable agent role used for role-aware pheromone comparison (for example coder, tester, coordinator)',
1713
+ type: 'string'
1714
+ },
1715
+ {
1716
+ name: 'duration',
1717
+ description: 'Observed task duration in milliseconds for latency fitness',
1718
+ type: 'number'
1719
+ },
1720
+ {
1721
+ name: 'latency-budget-ms',
1722
+ description: 'Expected task duration in milliseconds; paired with --duration to normalize latency fitness',
1723
+ type: 'number'
1724
+ },
1725
+ {
1726
+ name: 'consensus-alignment',
1727
+ description: 'Consensus alignment score from 0 to 1',
1728
+ type: 'number'
1729
+ },
1675
1730
  {
1676
1731
  name: 'task',
1677
1732
  short: 't',
@@ -1716,6 +1771,10 @@ const postTaskCommand = {
1716
1771
  success,
1717
1772
  quality: ctx.flags.quality,
1718
1773
  agent: ctx.flags.agent,
1774
+ agentRole: ctx.flags.agentRole,
1775
+ duration: ctx.flags.duration,
1776
+ latencyBudgetMs: ctx.flags.latencyBudgetMs,
1777
+ consensusAlignment: ctx.flags.consensusAlignment,
1719
1778
  // #2785: forward the task description so routing outcomes actually persist
1720
1779
  // (hooks_post-task requires taskText + agent to write the outcome row that
1721
1780
  // hooks_metrics reads via getIntelligenceStatsFromMemory)
@@ -63,7 +63,7 @@ const startCommand = {
63
63
  },
64
64
  {
65
65
  name: 'tools',
66
- description: 'Tools to enable (comma-separated or "all")',
66
+ description: 'Tools to advertise (comma-separated categories, prefixes, exact names, or "all")',
67
67
  type: 'string',
68
68
  default: 'all'
69
69
  },
@@ -182,6 +182,7 @@ const runCommand = {
182
182
  data: [
183
183
  { metric: 'Processed', value: String(report.processed) },
184
184
  { metric: 'Episodes', value: String(report.episodes) },
185
+ { metric: 'Episode Embeddings', value: String(report.episodeEmbeddings) },
185
186
  { metric: 'Patterns', value: String(report.patterns) },
186
187
  { metric: 'Pattern Embeddings', value: String(report.patternEmbeddings) },
187
188
  { metric: 'Causal Edges', value: String(report.causalEdges) },
@@ -102,12 +102,16 @@ async function dispatchFlywheel(operation, positional, flags) {
102
102
  receiptPublicKeyPem: publicKeyPath ? readFileSync(resolve(publicKeyPath), 'utf8') : undefined,
103
103
  maxConcurrency: Number(flywheelFlag(flags, 'maxConcurrency', 2)),
104
104
  evaluationTimeoutMs: Number(flywheelFlag(flags, 'timeoutMs', 120_000)),
105
+ anchorPath: flywheelFlag(flags, 'anchorPath'),
106
+ anchorHash: flywheelFlag(flags, 'anchorHash'),
107
+ anchorManifestPath: flywheelFlag(flags, 'anchorManifest'),
105
108
  });
106
109
  }
107
110
  }
108
111
  else if (operation === 'receipts') {
109
112
  data = listFlywheelReceipts(projectRoot).map(({ receipt, state }) => ({
110
113
  receiptId: receipt.payload.receiptId,
114
+ anchorRef: receipt.payload.anchorRef,
111
115
  candidateId: receipt.payload.candidateId,
112
116
  baselineRef: receipt.payload.baselineRef,
113
117
  decision: receipt.payload.decision,
@@ -50,6 +50,34 @@ function getSwarmStatus(swarmId) {
50
50
  // Ignore
51
51
  }
52
52
  }
53
+ // The canonical agent registry is the same source used by `agent list`.
54
+ // Prefer it over the swarm-level coordination boolean so idle agents are
55
+ // not reported as active (#2808). Hive agents are merged additively.
56
+ if (totalAgents === 0) {
57
+ try {
58
+ const canonicalPath = path.join(process.cwd(), '.claude-flow', 'agents', 'store.json');
59
+ const hivePath = path.join(process.cwd(), '.claude-flow', 'agents.json');
60
+ const merged = {};
61
+ for (const storePath of [hivePath, canonicalPath]) {
62
+ if (!fs.existsSync(storePath))
63
+ continue;
64
+ const parsed = JSON.parse(fs.readFileSync(storePath, 'utf-8'));
65
+ if (parsed?.agents && typeof parsed.agents === 'object') {
66
+ Object.assign(merged, parsed.agents);
67
+ }
68
+ }
69
+ const agents = Object.values(merged).filter(agent => agent.status !== 'terminated');
70
+ if (agents.length > 0) {
71
+ totalAgents = agents.length;
72
+ activeAgents = agents.filter(agent => agent.status === 'active' ||
73
+ agent.status === 'running' ||
74
+ agent.status === 'busy').length;
75
+ }
76
+ }
77
+ catch {
78
+ // Ignore — the count-only activity file remains the final fallback.
79
+ }
80
+ }
53
81
  // #2799 — `agent spawn` never writes `.swarm/agents/*.json`; it records
54
82
  // the count in `.claude-flow/metrics/swarm-activity.json` (via
55
83
  // updateSwarmActivityMetrics in commands/agent.ts). So when the agents
@@ -271,7 +299,8 @@ const TOPOLOGIES = [
271
299
  { value: 'ring', label: 'Ring', hint: 'Circular communication pattern' },
272
300
  { value: 'star', label: 'Star', hint: 'Central coordinator with spoke agents' },
273
301
  { value: 'hybrid', label: 'Hybrid', hint: 'Hierarchical mesh for maximum flexibility' },
274
- { value: 'hierarchical-mesh', label: 'Hierarchical Mesh', hint: 'V3 15-agent queen + peer communication (recommended)' }
302
+ { value: 'hierarchical-mesh', label: 'Hierarchical Mesh', hint: 'V3 15-agent queen + peer communication (recommended)' },
303
+ { value: 'pheromone-adaptive', label: 'Pheromone Adaptive', hint: 'Role-aware dynamic eligibility with quorum safety (ADR-330)' }
275
304
  ];
276
305
  // Swarm strategies
277
306
  const STRATEGIES = [
@@ -324,6 +353,19 @@ const initCommand = {
324
353
  type: 'boolean',
325
354
  default: false
326
355
  },
356
+ {
357
+ name: 'apsc-live',
358
+ description: 'Apply APSC suspension decisions (default is calibration-only dry run)',
359
+ type: 'boolean',
360
+ default: false
361
+ },
362
+ { name: 'apsc-alpha', description: 'Task-success score weight', type: 'number', default: 0.5 },
363
+ { name: 'apsc-beta', description: 'Latency score weight', type: 'number', default: 0.2 },
364
+ { name: 'apsc-gamma', description: 'Consensus-alignment score weight', type: 'number', default: 0.3 },
365
+ { name: 'apsc-pruning-factor', description: 'Fraction of adaptive threshold below which agents are eligible for suspension', type: 'number', default: 0.6 },
366
+ { name: 'apsc-reactivation-threshold', description: 'Fraction of adaptive threshold required for recovery', type: 'number', default: 0.75 },
367
+ { name: 'apsc-min-active-agents', description: 'Hard quorum floor', type: 'number', default: 3 },
368
+ { name: 'apsc-min-samples', description: 'Warm-up observations before pruning', type: 'number', default: 3 },
327
369
  {
328
370
  // #2768 — dream-cycle SubagentPermissionDelegate. Ships a per-role
329
371
  // capability manifest to `.swarm/permissions.jsonl` + an append-only
@@ -366,6 +408,18 @@ const initCommand = {
366
408
  failureHandling: 'retry',
367
409
  loadBalancing: true,
368
410
  autoScaling: ctx.flags.autoScale ?? true,
411
+ ...(topology === 'pheromone-adaptive' ? {
412
+ apsc: {
413
+ alpha: Number(ctx.flags.apscAlpha ?? 0.5),
414
+ beta: Number(ctx.flags.apscBeta ?? 0.2),
415
+ gamma: Number(ctx.flags.apscGamma ?? 0.3),
416
+ pruningFactor: Number(ctx.flags.apscPruningFactor ?? 0.6),
417
+ reactivationThreshold: Number(ctx.flags.apscReactivationThreshold ?? 0.75),
418
+ minActiveAgents: Number(ctx.flags.apscMinActiveAgents ?? 3),
419
+ minSamples: Number(ctx.flags.apscMinSamples ?? 3),
420
+ dryRun: ctx.flags.apscLive !== true,
421
+ },
422
+ } : {}),
369
423
  },
370
424
  metadata: {
371
425
  v3Mode,
@@ -393,7 +447,10 @@ const initCommand = {
393
447
  { property: 'Max Agents', value: result.config.maxAgents },
394
448
  { property: 'Auto Scale', value: result.config.autoScaling ? 'Enabled' : 'Disabled' },
395
449
  { property: 'Protocol', value: result.config.communicationProtocol || 'N/A' },
396
- { property: 'V3 Mode', value: v3Mode ? 'Enabled' : 'Disabled' }
450
+ { property: 'V3 Mode', value: v3Mode ? 'Enabled' : 'Disabled' },
451
+ ...(topology === 'pheromone-adaptive'
452
+ ? [{ property: 'APSC Mode', value: ctx.flags.apscLive === true ? 'Live' : 'Dry run' }]
453
+ : [])
397
454
  ]
398
455
  });
399
456
  output.writeln();
@@ -924,15 +981,41 @@ const compressMessageCommand = {
924
981
  return { success: true, data: result };
925
982
  },
926
983
  };
984
+ const pheromoneCommand = {
985
+ name: 'pheromone',
986
+ description: 'Inspect or update ADR-330 pheromone-adaptive scheduling state',
987
+ options: [
988
+ { name: 'agent-id', description: 'Agent identifier; omit to show status', type: 'string' },
989
+ { name: 'role', description: 'Agent role for role-local normalization', type: 'string' },
990
+ { name: 'task-success', description: 'Task success in [0,1]', type: 'number' },
991
+ { name: 'normalized-latency', description: 'Latency divided by budget in [0,1]', type: 'number' },
992
+ { name: 'consensus-alignment', description: 'Consensus alignment in [0,1]', type: 'number' },
993
+ ],
994
+ action: async (ctx) => {
995
+ const agentId = ctx.flags.agentId;
996
+ const data = agentId
997
+ ? await callMCPTool('swarm_pheromone_update', {
998
+ agentId,
999
+ role: String(ctx.flags.role ?? 'worker'),
1000
+ taskSuccess: Number(ctx.flags.taskSuccess ?? 1),
1001
+ normalizedLatency: Number(ctx.flags.normalizedLatency ?? 0),
1002
+ consensusAlignment: Number(ctx.flags.consensusAlignment ?? 1),
1003
+ })
1004
+ : await callMCPTool('swarm_pheromone_status', {});
1005
+ output.writeln(JSON.stringify(data, null, 2));
1006
+ return { success: data.active !== false, data };
1007
+ },
1008
+ };
927
1009
  export const swarmCommand = {
928
1010
  name: 'swarm',
929
1011
  description: 'Swarm coordination commands',
930
- subcommands: [initCommand, startCommand, statusCommand, stopCommand, scaleCommand, coordinateCommand, compressMessageCommand],
1012
+ subcommands: [initCommand, startCommand, statusCommand, stopCommand, scaleCommand, coordinateCommand, compressMessageCommand, pheromoneCommand],
931
1013
  options: [],
932
1014
  examples: [
933
1015
  { command: 'claude-flow swarm init --v3-mode', description: 'Initialize V3 swarm' },
934
1016
  { command: 'claude-flow swarm start -o "Build API" -s development', description: 'Start development swarm' },
935
- { command: 'claude-flow swarm coordinate --agents 15', description: 'V3 coordination' }
1017
+ { command: 'claude-flow swarm coordinate --agents 15', description: 'V3 coordination' },
1018
+ { command: 'claude-flow swarm pheromone', description: 'Inspect pheromone-adaptive scheduling state' }
936
1019
  ],
937
1020
  action: async (ctx) => {
938
1021
  output.writeln();
@@ -947,7 +1030,8 @@ export const swarmCommand = {
947
1030
  `${output.highlight('status')} - Show swarm status`,
948
1031
  `${output.highlight('stop')} - Stop swarm execution`,
949
1032
  `${output.highlight('scale')} - Scale swarm agent count`,
950
- `${output.highlight('coordinate')} - V3 15-agent coordination`
1033
+ `${output.highlight('coordinate')} - V3 15-agent coordination`,
1034
+ `${output.highlight('pheromone')} - Inspect/update adaptive pheromone state`
951
1035
  ]);
952
1036
  return { success: true };
953
1037
  }
@@ -147,6 +147,7 @@ function normalizeTopology(topology) {
147
147
  case 'hierarchical-mesh':
148
148
  return topology;
149
149
  case 'adaptive':
150
+ case 'pheromone-adaptive':
150
151
  return 'hybrid';
151
152
  default:
152
153
  return 'hierarchical';
@@ -196,8 +196,9 @@ export class CLI {
196
196
  try {
197
197
  const { ensureDaemonRunning } = await import('./services/daemon-autostart.js');
198
198
  const d = ensureDaemonRunning(process.cwd());
199
- if (d.started && this.output.isVerbose())
200
- this.output.printDebug('Started background daemon (auto)');
199
+ if (d.started) {
200
+ this.output.printInfo(`Started Ruflo background daemon for ${process.cwd()} (stop: ruflo daemon stop)`);
201
+ }
201
202
  }
202
203
  catch { /* silent */ }
203
204
  }
@@ -370,18 +370,19 @@ function mergeSettingsForUpgrade(existing) {
370
370
  // js/redos): a crafted settings.json command string with dozens of
371
371
  // dash-token repetitions can hang this check for minutes.
372
372
  const BROKEN_STATUSLINE_RE = /(?:npx\s+(?:--?\S+\s+){0,10})?@?claude-flow(?:\/cli)?(?:@\S+)?\s+hooks\s+statusline/;
373
+ const BROKEN_NPX_LATEST_RE = /npx\s+(?:--?\S+\s+){0,10}@?claude-flow\/cli@latest\s+\S+/;
373
374
  const existingStatusLine = existing.statusLine;
374
375
  if (existingStatusLine) {
375
376
  const existingCmd = typeof existingStatusLine.command === 'string' ? existingStatusLine.command : '';
376
- const isBroken = BROKEN_STATUSLINE_RE.test(existingCmd);
377
+ const isBroken = BROKEN_STATUSLINE_RE.test(existingCmd) || BROKEN_NPX_LATEST_RE.test(existingCmd);
377
378
  merged.statusLine = {
378
379
  type: 'command',
379
380
  command: isBroken || !existingCmd ? NEW_STATUSLINE_CMD : existingCmd,
380
381
  // Remove invalid fields: refreshMs, enabled (not supported by Claude Code)
381
382
  };
382
383
  }
383
- // #2448 — Detect and REGENERATE per-action hook commands that still use
384
- // the runaway `npx @claude-flow/cli@latest hooks <sub>` form. These fire
384
+ // #2448 / #2677 — Detect and REGENERATE per-action hook commands that still
385
+ // use the runaway `npx @claude-flow/cli@latest hooks <sub>` form. These fire
385
386
  // on every PreToolUse/PostToolUse/UserPromptSubmit, each spawning ~130 MB
386
387
  // of cold Node + npm registry resolution; the storm is what kernel-paniced
387
388
  // the reporter's machine in #2448.
@@ -409,15 +410,20 @@ function mergeSettingsForUpgrade(existing) {
409
410
  for (const group of groups) {
410
411
  if (!Array.isArray(group.hooks))
411
412
  continue;
412
- for (const h of group.hooks) {
413
+ group.hooks = group.hooks.filter((h) => {
413
414
  if (typeof h?.command !== 'string')
414
- continue;
415
+ return true;
415
416
  const m = BROKEN_HOOK_RE.exec(h.command);
416
- if (!m)
417
- continue;
418
- // Subcommand captured (e.g. "pre-bash", "post-edit", "route") — keep it.
419
- h.command = localHookCmd(m[1]);
420
- }
417
+ if (m) {
418
+ // Subcommand captured (e.g. "pre-bash", "post-edit", "route") — keep it.
419
+ h.command = localHookCmd(m[1]);
420
+ return true;
421
+ }
422
+ // Sibling CLI invocations (memory/daemon/swarm/...) have no
423
+ // hook-handler equivalent. Drop the stale extra; the merge above
424
+ // has already installed current local-helper hooks for this event.
425
+ return !BROKEN_NPX_LATEST_RE.test(h.command);
426
+ });
421
427
  }
422
428
  }
423
429
  }
@@ -775,11 +775,22 @@ export function generateIntelligenceStub() {
775
775
  "const path = require('path');",
776
776
  "const os = require('os');",
777
777
  '',
778
- "const DATA_DIR = path.join(process.cwd(), '.claude-flow', 'data');",
778
+ 'function resolveProjectRoot(startDir) {',
779
+ ' if (process.env.CLAUDE_PROJECT_DIR) return path.resolve(process.env.CLAUDE_PROJECT_DIR);',
780
+ ' var dir = path.resolve(startDir || process.cwd());',
781
+ ' while (true) {',
782
+ ' if (fs.existsSync(path.join(dir, ".git")) || fs.existsSync(path.join(dir, ".claude-flow"))) return dir;',
783
+ ' var parent = path.dirname(dir);',
784
+ ' if (parent === dir) return path.resolve(startDir || process.cwd());',
785
+ ' dir = parent;',
786
+ ' }',
787
+ '}',
788
+ "const PROJECT_ROOT = resolveProjectRoot(process.cwd());",
789
+ "const DATA_DIR = path.join(PROJECT_ROOT, '.claude-flow', 'data');",
779
790
  "const STORE_PATH = path.join(DATA_DIR, 'auto-memory-store.json');",
780
791
  "const RANKED_PATH = path.join(DATA_DIR, 'ranked-context.json');",
781
792
  "const PENDING_PATH = path.join(DATA_DIR, 'pending-insights.jsonl');",
782
- "const SESSION_DIR = path.join(process.cwd(), '.claude-flow', 'sessions');",
793
+ "const SESSION_DIR = path.join(PROJECT_ROOT, '.claude-flow', 'sessions');",
783
794
  "const SESSION_FILE = path.join(SESSION_DIR, 'current.json');",
784
795
  '',
785
796
  'function ensureDir(dir) {',
@@ -823,8 +834,8 @@ export function generateIntelligenceStub() {
823
834
  ' var entries = [];',
824
835
  ' var candidates = [',
825
836
  ' path.join(os.homedir(), ".claude", "projects"),',
826
- ' path.join(process.cwd(), ".claude-flow", "memory"),',
827
- ' path.join(process.cwd(), ".claude", "memory"),',
837
+ ' path.join(PROJECT_ROOT, ".claude-flow", "memory"),',
838
+ ' path.join(PROJECT_ROOT, ".claude", "memory"),',
828
839
  ' ];',
829
840
  ' for (var i = 0; i < candidates.length; i++) {',
830
841
  ' try {',
@@ -53,6 +53,16 @@ function getInstalledCliVersionLocal() {
53
53
  return '0.0.0';
54
54
  }
55
55
  }
56
+ function compareVersionsLocal(a, b) {
57
+ const pa = a.split(/[.-]/).map(part => Number.parseInt(part, 10) || 0);
58
+ const pb = b.split(/[.-]/).map(part => Number.parseInt(part, 10) || 0);
59
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
60
+ const delta = (pa[i] ?? 0) - (pb[i] ?? 0);
61
+ if (delta !== 0)
62
+ return delta;
63
+ }
64
+ return 0;
65
+ }
56
66
  /**
57
67
  * Generate optimized statusline script
58
68
  * Output format:
@@ -91,10 +101,12 @@ export function generateStatuslineScript(options) {
91
101
  // getInstalledCliVersionLocal() above — reuse the pattern for the
92
102
  // same reason it exists there.
93
103
  let helperContent = null;
104
+ let helperPackageRoot = '';
94
105
  try {
95
106
  const esmRequire = createRequire(import.meta.url);
96
107
  const pkgJsonPath = esmRequire.resolve('@claude-flow/cli/package.json');
97
- const helperPath = path.join(path.dirname(pkgJsonPath), '.claude', 'helpers', 'statusline.cjs');
108
+ helperPackageRoot = path.dirname(pkgJsonPath);
109
+ const helperPath = path.join(helperPackageRoot, '.claude', 'helpers', 'statusline.cjs');
98
110
  helperContent = fs.readFileSync(helperPath, 'utf-8');
99
111
  }
100
112
  catch {
@@ -105,6 +117,7 @@ export function generateStatuslineScript(options) {
105
117
  if (pkg && pkg.name === '@claude-flow/cli') {
106
118
  const candidate = path.join(dir, '.claude', 'helpers', 'statusline.cjs');
107
119
  if (fs.existsSync(candidate)) {
120
+ helperPackageRoot = dir;
108
121
  helperContent = fs.readFileSync(candidate, 'utf-8');
109
122
  break;
110
123
  }
@@ -128,15 +141,15 @@ export function generateStatuslineScript(options) {
128
141
  // whatever the helper hard-codes) ships. Add a paired test in
129
142
  // statusline-cost-display.test.ts before changing either token.
130
143
  helperContent = helperContent.replace(/maxAgents: \d+,/, `maxAgents: ${maxAgents},`);
144
+ helperContent = helperContent.replace(/const BAKED_INSTALL_ROOT = "[^"]*";/, `const BAKED_INSTALL_ROOT = ${JSON.stringify(helperPackageRoot)};`);
131
145
  // Only overwrite the helper's baked version if OURS resolves higher.
132
146
  // Otherwise the substitution could DOWNGRADE (test environments where
133
147
  // esmRequire.resolve happens to hit an older node_modules install would
134
- // clobber a fresh committed helper). Naive lexicographic compare — works
135
- // for canonical semver strings with same-width digit parts, which is
136
- // reliable at this stage of the version space.
148
+ // clobber a fresh committed helper). Compare numeric components:
149
+ // lexicographic comparison freezes 3.32.24 below 3.32.8 (#2811).
137
150
  const helperVerMatch = helperContent.match(/let ver = "([^"]+)";/);
138
151
  const helperVer = helperVerMatch ? helperVerMatch[1] : '';
139
- if (!helperVer || bakedVersion > helperVer) {
152
+ if (!helperVer || compareVersionsLocal(bakedVersion, helperVer) > 0) {
140
153
  helperContent = helperContent.replace(/let ver = "[^"]+";/, `let ver = ${JSON.stringify(bakedVersion)};`);
141
154
  }
142
155
  return helperContent;
@@ -48,6 +48,31 @@ export interface MCPServerStatus {
48
48
  metrics?: Record<string, number>;
49
49
  };
50
50
  }
51
+ export declare function parseMcpToolSelection(value: string | undefined): string[] | 'all';
52
+ /**
53
+ * Apply the existing `--tools` contract to advertised schemas. A selector can
54
+ * be an exact tool name, a category, or a namespace prefix (`memory` matches
55
+ * `memory_store`). Execution remains registered internally; only the fixed
56
+ * per-request schema catalogue is reduced.
57
+ */
58
+ export declare function filterAdvertisedMcpTools<T extends {
59
+ name: string;
60
+ category?: string;
61
+ }>(tools: T[], selection: string[] | 'all'): T[];
62
+ export interface McpSchemaOverhead {
63
+ toolCount: number;
64
+ bytes: number;
65
+ estimatedTokens: number;
66
+ contextWindowTokens?: number;
67
+ ratio?: number;
68
+ risk: 'normal' | 'high';
69
+ }
70
+ /** Conservative JSON-size estimate for the fixed tools/list catalogue. */
71
+ export declare function assessMcpSchemaOverhead(tools: Array<{
72
+ name: string;
73
+ description?: string;
74
+ inputSchema?: unknown;
75
+ }>, contextWindowTokens?: number): McpSchemaOverhead;
51
76
  /**
52
77
  * MCP Server Manager
53
78
  *
@@ -42,6 +42,54 @@ const DEFAULT_OPTIONS = {
42
42
  daemonize: false,
43
43
  timeout: 30000,
44
44
  };
45
+ export function parseMcpToolSelection(value) {
46
+ if (!value || value.trim().toLowerCase() === 'all')
47
+ return 'all';
48
+ const selectors = value.split(',').map((item) => item.trim()).filter(Boolean);
49
+ return selectors.length > 0 ? selectors : 'all';
50
+ }
51
+ /**
52
+ * Apply the existing `--tools` contract to advertised schemas. A selector can
53
+ * be an exact tool name, a category, or a namespace prefix (`memory` matches
54
+ * `memory_store`). Execution remains registered internally; only the fixed
55
+ * per-request schema catalogue is reduced.
56
+ */
57
+ export function filterAdvertisedMcpTools(tools, selection) {
58
+ if (selection === 'all')
59
+ return tools;
60
+ const selectors = new Set(selection.map((item) => item.toLowerCase()));
61
+ return tools.filter((tool) => {
62
+ const name = tool.name.toLowerCase();
63
+ const category = tool.category?.toLowerCase();
64
+ return selectors.has(name)
65
+ || (category !== undefined && selectors.has(category))
66
+ || Array.from(selectors).some((selector) => name.startsWith(`${selector}_`));
67
+ });
68
+ }
69
+ /** Conservative JSON-size estimate for the fixed tools/list catalogue. */
70
+ export function assessMcpSchemaOverhead(tools, contextWindowTokens) {
71
+ const catalogue = tools.map((tool) => ({
72
+ name: tool.name,
73
+ description: tool.description,
74
+ inputSchema: tool.inputSchema,
75
+ }));
76
+ const bytes = Buffer.byteLength(JSON.stringify(catalogue), 'utf8');
77
+ const estimatedTokens = Math.ceil(bytes / 4);
78
+ const validWindow = Number.isFinite(contextWindowTokens) && Number(contextWindowTokens) > 0
79
+ ? Number(contextWindowTokens)
80
+ : undefined;
81
+ const ratio = validWindow ? estimatedTokens / validWindow : undefined;
82
+ return {
83
+ toolCount: tools.length,
84
+ bytes,
85
+ estimatedTokens,
86
+ ...(validWindow === undefined ? {} : { contextWindowTokens: validWindow }),
87
+ ...(ratio === undefined ? {} : { ratio }),
88
+ risk: (ratio !== undefined ? ratio >= 0.2 : estimatedTokens >= 8_000)
89
+ ? 'high'
90
+ : 'normal',
91
+ };
92
+ }
45
93
  /**
46
94
  * MCP Server Manager
47
95
  *
@@ -55,7 +103,14 @@ export class MCPServerManager extends EventEmitter {
55
103
  healthCheckInterval;
56
104
  constructor(options = {}) {
57
105
  super();
58
- this.options = { ...DEFAULT_OPTIONS, ...options };
106
+ // `options.tools`, populated by the `mcp start --tools` CLI flag, is
107
+ // spread last below and therefore takes precedence over this env fallback.
108
+ const environmentTools = parseMcpToolSelection(process.env.CLAUDE_FLOW_MCP_TOOLS);
109
+ this.options = {
110
+ ...DEFAULT_OPTIONS,
111
+ ...(environmentTools === 'all' ? {} : { tools: environmentTools }),
112
+ ...options,
113
+ };
59
114
  }
60
115
  /**
61
116
  * Start the MCP server
@@ -420,7 +475,7 @@ export class MCPServerManager extends EventEmitter {
420
475
  },
421
476
  };
422
477
  case 'tools/list':
423
- const tools = listMCPTools();
478
+ const tools = filterAdvertisedMcpTools(listMCPTools(), this.options.tools);
424
479
  return {
425
480
  jsonrpc: '2.0',
426
481
  id: message.id,
@@ -9,6 +9,7 @@ import { join } from 'node:path';
9
9
  import { getProjectCwd } from './types.js';
10
10
  import { validateIdentifier, validateText, validateAgentSpawn } from './validate-input.js';
11
11
  import { executeAgentTask } from './agent-execute-core.js';
12
+ import { pheromoneAgentEligibility } from './swarm-tools.js';
12
13
  // Storage paths
13
14
  const STORAGE_DIR = '.claude-flow';
14
15
  const AGENT_DIR = 'agents';
@@ -383,6 +384,15 @@ export const agentTools = [
383
384
  const vP = validateText(input.prompt, 'prompt');
384
385
  if (!vP.valid)
385
386
  return { success: false, error: `Input validation failed: ${vP.error}` };
387
+ const pheromone = pheromoneAgentEligibility(input.agentId);
388
+ if (!pheromone.eligible) {
389
+ return {
390
+ success: false,
391
+ agentId: input.agentId,
392
+ suspended: true,
393
+ error: pheromone.reason,
394
+ };
395
+ }
386
396
  // Delegate to the shared core (also used by the workflow runtime).
387
397
  return executeAgentTask({
388
398
  agentId: input.agentId,