@claude-flow/cli 3.47.0 → 3.48.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 (70) hide show
  1. package/.claude/helpers/auto-memory-hook.mjs +6 -2
  2. package/.claude/helpers/helpers.manifest.json +3 -3
  3. package/.claude/helpers/learning-service.mjs +4 -2
  4. package/.claude/helpers/metrics-db.mjs +4 -2
  5. package/bin/cli.js +4 -1
  6. package/catalog-manifest.json +2 -2
  7. package/dist/src/commands/analyze.js +2 -2
  8. package/dist/src/commands/config.js +5 -7
  9. package/dist/src/commands/hive-mind-worker.d.ts +88 -0
  10. package/dist/src/commands/hive-mind-worker.js +242 -0
  11. package/dist/src/commands/hive-mind.js +24 -41
  12. package/dist/src/commands/hooks.js +15 -5
  13. package/dist/src/commands/memory.js +89 -27
  14. package/dist/src/commands/metaharness.js +1 -0
  15. package/dist/src/commands/security.js +113 -55
  16. package/dist/src/commands/session.js +6 -6
  17. package/dist/src/commands/task.js +5 -4
  18. package/dist/src/init/helpers-generator.js +4 -2
  19. package/dist/src/mcp-server.js +10 -3
  20. package/dist/src/mcp-tools/agentdb-tools.js +29 -62
  21. package/dist/src/mcp-tools/hooks-tools.d.ts +0 -4
  22. package/dist/src/mcp-tools/hooks-tools.js +139 -37
  23. package/dist/src/mcp-tools/memory-tools.js +89 -35
  24. package/dist/src/mcp-tools/policy-enforcer.d.ts +8 -1
  25. package/dist/src/mcp-tools/policy-enforcer.js +80 -8
  26. package/dist/src/mcp-tools/session-tools.js +34 -8
  27. package/dist/src/mcp-tools/task-tools.js +73 -53
  28. package/dist/src/memory/feedback-patterns.d.ts +13 -0
  29. package/dist/src/memory/feedback-patterns.js +14 -0
  30. package/dist/src/memory/live-memory-row.d.ts +3 -0
  31. package/dist/src/memory/live-memory-row.js +5 -0
  32. package/dist/src/memory/memory-bridge.d.ts +11 -20
  33. package/dist/src/memory/memory-bridge.js +348 -134
  34. package/dist/src/memory/memory-initializer.d.ts +9 -1
  35. package/dist/src/memory/memory-initializer.js +41 -31
  36. package/dist/src/ruvector/graph-analyzer.js +4 -1
  37. package/dist/src/services/config-file-manager.js +30 -8
  38. package/dist/src/services/memory-backup.d.ts +1 -1
  39. package/dist/src/services/memory-backup.js +67 -27
  40. package/dist/src/services/worker-daemon.d.ts +2 -2
  41. package/dist/src/services/worker-daemon.js +23 -14
  42. package/node_modules/@claude-flow/codex/package.json +1 -1
  43. package/node_modules/@claude-flow/mcp/dist/tool-registry.d.ts.map +1 -1
  44. package/node_modules/@claude-flow/mcp/dist/tool-registry.js +11 -3
  45. package/node_modules/@claude-flow/mcp/dist/tool-registry.js.map +1 -1
  46. package/node_modules/@claude-flow/mcp/package.json +6 -3
  47. package/node_modules/@claude-flow/plugin-agent-federation/package.json +3 -3
  48. package/node_modules/@claude-flow/security/package.json +2 -3
  49. package/package.json +9 -9
  50. package/plugins/ruflo-metaharness/scripts/_cli-args.mjs +37 -0
  51. package/plugins/ruflo-metaharness/scripts/audit-list.mjs +8 -0
  52. package/plugins/ruflo-metaharness/scripts/audit-trend.mjs +12 -0
  53. package/plugins/ruflo-metaharness/scripts/bench-parse-mcp-scan.mjs +8 -0
  54. package/plugins/ruflo-metaharness/scripts/bench-recordpair-overhead.mjs +8 -0
  55. package/plugins/ruflo-metaharness/scripts/bench-similarity.mjs +8 -0
  56. package/plugins/ruflo-metaharness/scripts/bench.mjs +10 -0
  57. package/plugins/ruflo-metaharness/scripts/drift-from-history.mjs +13 -0
  58. package/plugins/ruflo-metaharness/scripts/evolve.mjs +27 -0
  59. package/plugins/ruflo-metaharness/scripts/genome.mjs +8 -0
  60. package/plugins/ruflo-metaharness/scripts/gepa.mjs +12 -0
  61. package/plugins/ruflo-metaharness/scripts/learn.mjs +13 -0
  62. package/plugins/ruflo-metaharness/scripts/mcp-scan.mjs +8 -0
  63. package/plugins/ruflo-metaharness/scripts/mint.mjs +11 -0
  64. package/plugins/ruflo-metaharness/scripts/oia-audit.mjs +9 -0
  65. package/plugins/ruflo-metaharness/scripts/router-parallel-analyze.mjs +8 -0
  66. package/plugins/ruflo-metaharness/scripts/score.mjs +8 -0
  67. package/plugins/ruflo-metaharness/scripts/security-bench.mjs +11 -0
  68. package/plugins/ruflo-metaharness/scripts/similarity.mjs +12 -0
  69. package/plugins/ruflo-metaharness/scripts/test-cli-help.mjs +72 -0
  70. package/plugins/ruflo-metaharness/scripts/threat-model.mjs +8 -0
@@ -12,12 +12,16 @@
12
12
  */
13
13
 
14
14
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
15
- import { join, dirname } from 'path';
15
+ import { join, dirname, resolve } from 'path';
16
16
  import { fileURLToPath } from 'url';
17
17
 
18
18
  const __filename = fileURLToPath(import.meta.url);
19
19
  const __dirname = dirname(__filename);
20
- const PROJECT_ROOT = join(__dirname, '../..');
20
+ // Home-level helpers can serve a different project. Keep the memory writer on
21
+ // the same project root as intelligence.cjs, which reads CLAUDE_PROJECT_DIR.
22
+ const PROJECT_ROOT = process.env.CLAUDE_PROJECT_DIR
23
+ ? resolve(process.env.CLAUDE_PROJECT_DIR)
24
+ : join(__dirname, '../..');
21
25
  const DATA_DIR = join(PROJECT_ROOT, '.claude-flow', 'data');
22
26
  const STORE_PATH = join(DATA_DIR, 'auto-memory-store.json');
23
27
 
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "manifest": {
3
- "version": "3.47.0",
3
+ "version": "3.48.0",
4
4
  "files": {
5
- "auto-memory-hook.mjs": "85fe05c757421c52137c0bc8545a0896bab6b4714538c2a11d1d0835bfcc8c1c",
5
+ "auto-memory-hook.mjs": "c8d218526a3c34034c2a69de23f507f0eb0f70d59c16426a8edc849e6ac22529",
6
6
  "hook-handler.cjs": "209d9fafe10e17d1be0866727f6f9cf9ac66f9a0793f1c793a4f58319e8e4583",
7
7
  "intelligence.cjs": "30e42ed7ec4ca5a94ac54fdb1330d2d47ac5f3fdeeef207753574723a9e77b5c",
8
8
  "statusline.cjs": "4a48353b4f1566fa4379b00fd0321b6676a22b6cc91fbbd8380ac5183d619468",
9
9
  "router.js": "b6998397e7883191b62229ccdc66fb87f1ccca1d5b2039ecb8c1e686d1ad81f8"
10
10
  }
11
11
  },
12
- "signature": "Dt6+NVWu0FDiQVMr0HjwdVCJc3H4WfcUcOVHX4y0HnhoQA2Qj0lAWjU5jKpv2RGX74/jnwfHGRpyIsT1jlXLBA==",
12
+ "signature": "Y5fY+vJvnledNFp6edS2V1gxJ9P7llOQuq860/gCMvxr9DAzrlDuuKMuGgVP/Tb/25FStYm9+WyBVuOTDos0BQ==",
13
13
  "algorithm": "ed25519"
14
14
  }
@@ -19,14 +19,16 @@
19
19
 
20
20
  import { createRequire } from 'module';
21
21
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
22
- import { join, dirname } from 'path';
22
+ import { join, dirname, resolve } from 'path';
23
23
  import { fileURLToPath } from 'url';
24
24
  import { execSync, spawn } from 'child_process';
25
25
  import Database from 'better-sqlite3';
26
26
 
27
27
  const __filename = fileURLToPath(import.meta.url);
28
28
  const __dirname = dirname(__filename);
29
- const PROJECT_ROOT = join(__dirname, '../..');
29
+ const PROJECT_ROOT = process.env.CLAUDE_PROJECT_DIR
30
+ ? resolve(process.env.CLAUDE_PROJECT_DIR)
31
+ : join(__dirname, '../..');
30
32
  const DATA_DIR = join(PROJECT_ROOT, '.claude-flow/learning');
31
33
  const DB_PATH = join(DATA_DIR, 'patterns.db');
32
34
  const METRICS_PATH = join(DATA_DIR, 'learning-metrics.json');
@@ -7,12 +7,14 @@
7
7
 
8
8
  import initSqlJs from 'sql.js';
9
9
  import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, statSync, openSync, writeSync, fsyncSync, closeSync, renameSync, rmSync } from 'fs';
10
- import { dirname, join, basename } from 'path';
10
+ import { dirname, join, basename, resolve } from 'path';
11
11
  import { fileURLToPath } from 'url';
12
12
  import { execSync } from 'child_process';
13
13
 
14
14
  const __dirname = dirname(fileURLToPath(import.meta.url));
15
- const PROJECT_ROOT = join(__dirname, '../..');
15
+ const PROJECT_ROOT = process.env.CLAUDE_PROJECT_DIR
16
+ ? resolve(process.env.CLAUDE_PROJECT_DIR)
17
+ : join(__dirname, '../..');
16
18
  const V3_DIR = join(PROJECT_ROOT, 'v3');
17
19
  const DB_PATH = join(PROJECT_ROOT, '.claude-flow', 'metrics.db');
18
20
 
package/bin/cli.js CHANGED
@@ -302,10 +302,13 @@ if (isMCPMode) {
302
302
 
303
303
  try {
304
304
  const result = await callMCPTool(toolName, toolParams, { sessionId });
305
+ // Failed task outcomes are data; an explicit handler error is a tool failure.
306
+ const isError = result !== null && typeof result === 'object'
307
+ && typeof result.error === 'string' && result.error.trim().length > 0;
305
308
  return {
306
309
  jsonrpc: '2.0',
307
310
  id: message.id,
308
- result: { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] },
311
+ result: { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }], isError },
309
312
  };
310
313
  } catch (error) {
311
314
  return {
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "generation": 6,
4
- "generatedAt": "2026-09-27T19:39:37.000Z",
5
- "gitSha": "09595b70",
4
+ "generatedAt": "2026-09-28T23:12:33.113Z",
5
+ "gitSha": "bd42d873",
6
6
  "catalog": {
7
7
  "agents": 167,
8
8
  "tools": 418,
@@ -1480,7 +1480,7 @@ const boundariesCommand = {
1480
1480
  output.writeln();
1481
1481
  for (const cycle of result.circularDependencies.slice(0, 5)) {
1482
1482
  const severityColor = cycle.severity === 'high' ? output.error : cycle.severity === 'medium' ? output.warning : output.dim;
1483
- output.writeln(`${severityColor(`[${cycle.severity.toUpperCase()}]`)} ${cycle.cycle.join(' -> ')}`);
1483
+ output.writeln(`${severityColor.call(output, `[${cycle.severity.toUpperCase()}]`)} ${cycle.cycle.join(' -> ')}`);
1484
1484
  output.writeln(output.dim(` ${cycle.suggestion}`));
1485
1485
  output.writeln();
1486
1486
  }
@@ -1905,7 +1905,7 @@ const circularCommand = {
1905
1905
  if (items.length === 0)
1906
1906
  continue;
1907
1907
  const color = severity === 'high' ? output.error : severity === 'medium' ? output.warning : output.dim;
1908
- output.writeln(color(output.bold(`${severity.toUpperCase()} SEVERITY (${items.length})`)));
1908
+ output.writeln(color.call(output, output.bold(`${severity.toUpperCase()} SEVERITY (${items.length})`)));
1909
1909
  output.writeln();
1910
1910
  for (const cycle of items.slice(0, 5)) {
1911
1911
  output.writeln(` ${cycle.cycle.join(' -> ')}`);
@@ -124,15 +124,13 @@ const setCommand = {
124
124
  name: 'key',
125
125
  short: 'k',
126
126
  description: 'Configuration key',
127
- type: 'string',
128
- required: true
127
+ type: 'string'
129
128
  },
130
129
  {
131
130
  name: 'value',
132
131
  short: 'v',
133
132
  description: 'Configuration value',
134
- type: 'string',
135
- required: true
133
+ type: 'string'
136
134
  }
137
135
  ],
138
136
  examples: [
@@ -140,14 +138,14 @@ const setCommand = {
140
138
  { command: 'claude-flow config set -k memory.backend -v agentdb', description: 'Set memory backend' }
141
139
  ],
142
140
  action: async (ctx) => {
143
- const key = ctx.flags.key || ctx.args[0];
144
- const value = ctx.flags.value || ctx.args[1];
141
+ const key = (ctx.flags.key ?? ctx.args[0]);
142
+ const value = ctx.flags.value ?? ctx.args[1];
145
143
  if (!key || value === undefined) {
146
144
  output.printError('Both key and value are required');
147
145
  return { success: false, exitCode: 1 };
148
146
  }
149
147
  try {
150
- const parsedValue = parseConfigValue(value);
148
+ const parsedValue = parseConfigValue(String(value));
151
149
  configManager.set(ctx.cwd, key, parsedValue);
152
150
  output.writeln(`Set ${key} = ${value}`);
153
151
  return { success: true };
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Hive-mind worker execution: the MCP config handed to a spawned Claude, and
3
+ * `hive-mind work`, which actually runs a queued task.
4
+ *
5
+ * Before this, `hive-mind task` only wrote a `pending` row to the task store.
6
+ * Workers are records, not processes, and nothing ever claimed the row: the
7
+ * task stayed pending and the worker stayed idle forever. `work` is the missing
8
+ * consumer — claim one task, assign it to an idle hive worker, run it through
9
+ * `claude -p`, and record the outcome.
10
+ */
11
+ import type { Command } from '../types.js';
12
+ export interface WorkerMcpConfigOptions {
13
+ /** `--mcp-config` from the caller; used verbatim when given. */
14
+ explicit?: string;
15
+ cwd: string;
16
+ home: string;
17
+ /** Where the generated config is written (e.g. `.hive-mind/sessions`). */
18
+ outDir: string;
19
+ swarmId: string;
20
+ /** The ruflo MCP server to register when no source config provides one. */
21
+ rufloServer: {
22
+ command: string;
23
+ args: string[];
24
+ };
25
+ }
26
+ /**
27
+ * The MCP config file to pass to a spawned Claude.
28
+ *
29
+ * A source file is never passed through as-is. `~/.claude.json` is Claude
30
+ * Code's whole state file; on a machine where it has no top-level
31
+ * `mcpServers` (a fresh desktop), Claude exits at startup with "Invalid MCP
32
+ * configuration: mcpServers: Invalid input", before any task can run. And a
33
+ * config lacking a `ruflo` server leaves the worker without the tools its
34
+ * prompt names. So the servers are extracted, a `ruflo` server is guaranteed,
35
+ * and the result is written as a config Claude is known to accept.
36
+ */
37
+ export declare function resolveWorkerMcpConfig(opts: WorkerMcpConfigOptions): string;
38
+ /**
39
+ * The ruflo MCP server as launched by THIS cli, so a worker and the CLI that
40
+ * dispatched it share one version and one store format. `@latest` could
41
+ * resolve to a different release mid-session.
42
+ */
43
+ export declare function currentRufloServer(): {
44
+ command: string;
45
+ args: string[];
46
+ };
47
+ export interface RunOutcome {
48
+ ok: boolean;
49
+ output: string;
50
+ exitCode: number | null;
51
+ sessionId?: string;
52
+ costUsd?: number;
53
+ durationMs: number;
54
+ }
55
+ export type TaskRunner = (prompt: string, opts: {
56
+ mcpConfig: string;
57
+ timeoutMs: number;
58
+ cwd: string;
59
+ }) => Promise<RunOutcome>;
60
+ /** Run one prompt through `claude -p` and parse its JSON result. */
61
+ export declare const runWithClaude: TaskRunner;
62
+ export interface WorkOnceOptions {
63
+ cwd: string;
64
+ mcpConfig: string;
65
+ runner: TaskRunner;
66
+ taskId?: string;
67
+ workerId?: string;
68
+ defaultTimeoutMs?: number;
69
+ }
70
+ export type WorkOnceResult = {
71
+ dispatched: false;
72
+ reason: string;
73
+ } | {
74
+ dispatched: true;
75
+ taskId: string;
76
+ workerId: string;
77
+ status: 'completed' | 'failed';
78
+ outcome: RunOutcome;
79
+ };
80
+ /**
81
+ * One dispatch cycle: the oldest pending hive task goes to an idle hive worker,
82
+ * runs, and its outcome is recorded on the task. Every state change goes
83
+ * through the MCP task tools, so the store is the same one `hive-mind status`
84
+ * and `task status` read.
85
+ */
86
+ export declare function workOnce(opts: WorkOnceOptions): Promise<WorkOnceResult>;
87
+ export declare const workCommand: Command;
88
+ //# sourceMappingURL=hive-mind-worker.d.ts.map
@@ -0,0 +1,242 @@
1
+ /**
2
+ * Hive-mind worker execution: the MCP config handed to a spawned Claude, and
3
+ * `hive-mind work`, which actually runs a queued task.
4
+ *
5
+ * Before this, `hive-mind task` only wrote a `pending` row to the task store.
6
+ * Workers are records, not processes, and nothing ever claimed the row: the
7
+ * task stayed pending and the worker stayed idle forever. `work` is the missing
8
+ * consumer — claim one task, assign it to an idle hive worker, run it through
9
+ * `claude -p`, and record the outcome.
10
+ */
11
+ import { output } from '../output.js';
12
+ import { callMCPTool, MCPClientError } from '../mcp-client.js';
13
+ import { spawn as childSpawn } from 'child_process';
14
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
15
+ import { join } from 'path';
16
+ import { resolveClaudeLaunchCommand } from '../runtime/claude-command.js';
17
+ /** The server key the hive-mind prompt addresses (`mcp__ruflo__*`). */
18
+ const RUFLO_SERVER = 'ruflo';
19
+ function readServers(path, cwd) {
20
+ try {
21
+ if (!existsSync(path))
22
+ return null;
23
+ const parsed = JSON.parse(readFileSync(path, 'utf-8'));
24
+ const isMap = (v) => !!v && typeof v === 'object' && !Array.isArray(v) && Object.keys(v).length > 0;
25
+ if (isMap(parsed.mcpServers))
26
+ return parsed.mcpServers;
27
+ // ~/.claude.json keeps project-scoped servers under projects[<cwd>].
28
+ const project = parsed.projects?.[cwd];
29
+ if (isMap(project?.mcpServers))
30
+ return project.mcpServers;
31
+ }
32
+ catch {
33
+ // Unreadable or not JSON: not a usable config.
34
+ }
35
+ return null;
36
+ }
37
+ /**
38
+ * The MCP config file to pass to a spawned Claude.
39
+ *
40
+ * A source file is never passed through as-is. `~/.claude.json` is Claude
41
+ * Code's whole state file; on a machine where it has no top-level
42
+ * `mcpServers` (a fresh desktop), Claude exits at startup with "Invalid MCP
43
+ * configuration: mcpServers: Invalid input", before any task can run. And a
44
+ * config lacking a `ruflo` server leaves the worker without the tools its
45
+ * prompt names. So the servers are extracted, a `ruflo` server is guaranteed,
46
+ * and the result is written as a config Claude is known to accept.
47
+ */
48
+ export function resolveWorkerMcpConfig(opts) {
49
+ if (opts.explicit)
50
+ return opts.explicit;
51
+ const candidates = [join(opts.cwd, '.mcp.json')];
52
+ if (opts.home)
53
+ candidates.push(join(opts.home, '.claude.json'), join(opts.home, '.claude', 'mcp.json'));
54
+ let servers = {};
55
+ for (const c of candidates) {
56
+ const found = readServers(c, opts.cwd);
57
+ if (found) {
58
+ servers = found;
59
+ break;
60
+ }
61
+ }
62
+ if (!servers[RUFLO_SERVER]) {
63
+ servers = {
64
+ ...servers,
65
+ [RUFLO_SERVER]: {
66
+ command: opts.rufloServer.command,
67
+ args: opts.rufloServer.args,
68
+ // The MCP server keys its stores off this, so the worker's tool calls
69
+ // land in the same task/agent stores as the CLI that dispatched it.
70
+ env: { CLAUDE_FLOW_CWD: opts.cwd },
71
+ },
72
+ };
73
+ }
74
+ mkdirSync(opts.outDir, { recursive: true });
75
+ const path = join(opts.outDir, `mcp-${opts.swarmId}.json`);
76
+ writeFileSync(path, JSON.stringify({ mcpServers: servers }, null, 2), 'utf-8');
77
+ return path;
78
+ }
79
+ /**
80
+ * The ruflo MCP server as launched by THIS cli, so a worker and the CLI that
81
+ * dispatched it share one version and one store format. `@latest` could
82
+ * resolve to a different release mid-session.
83
+ */
84
+ export function currentRufloServer() {
85
+ const entry = process.argv[1];
86
+ return entry
87
+ ? { command: process.execPath, args: [entry, 'mcp', 'start'] }
88
+ : { command: 'npx', args: ['-y', '@claude-flow/cli@latest', 'mcp', 'start'] };
89
+ }
90
+ /** Run one prompt through `claude -p` and parse its JSON result. */
91
+ export const runWithClaude = (prompt, opts) => new Promise((resolve) => {
92
+ const started = Date.now();
93
+ const launch = resolveClaudeLaunchCommand();
94
+ if (!launch) {
95
+ resolve({ ok: false, output: 'Claude Code CLI not found in PATH', exitCode: null, durationMs: 0 });
96
+ return;
97
+ }
98
+ // The prompt goes on stdin, not argv: a task description has no length
99
+ // bound and `--mcp-config` is variadic (#1780).
100
+ const child = childSpawn(launch.command, [...launch.argsPrefix, '-p', '--output-format', 'json', `--mcp-config=${opts.mcpConfig}`], { cwd: opts.cwd, stdio: ['pipe', 'pipe', 'pipe'] });
101
+ let stdout = '';
102
+ let stderr = '';
103
+ child.stdout.on('data', (d) => (stdout += d));
104
+ child.stderr.on('data', (d) => (stderr += d));
105
+ const timer = setTimeout(() => child.kill('SIGTERM'), opts.timeoutMs);
106
+ child.on('error', (e) => {
107
+ clearTimeout(timer);
108
+ resolve({ ok: false, output: e.message, exitCode: null, durationMs: Date.now() - started });
109
+ });
110
+ child.on('close', (code, signal) => {
111
+ clearTimeout(timer);
112
+ const durationMs = Date.now() - started;
113
+ try {
114
+ const r = JSON.parse(stdout);
115
+ resolve({
116
+ ok: code === 0 && !r.is_error,
117
+ output: r.result ?? '',
118
+ exitCode: code,
119
+ sessionId: r.session_id,
120
+ costUsd: r.total_cost_usd,
121
+ durationMs,
122
+ });
123
+ }
124
+ catch {
125
+ const why = signal === 'SIGTERM' ? `timed out after ${opts.timeoutMs}ms` : (stderr || stdout).trim().slice(-2000);
126
+ resolve({ ok: false, output: why, exitCode: code, durationMs });
127
+ }
128
+ });
129
+ child.stdin.end(prompt);
130
+ });
131
+ function timeoutFromTags(tags, fallback) {
132
+ const tag = tags?.find((t) => /^timeout:\d+s$/.test(t));
133
+ return tag ? Number(tag.slice('timeout:'.length, -1)) * 1000 : fallback;
134
+ }
135
+ /**
136
+ * One dispatch cycle: the oldest pending hive task goes to an idle hive worker,
137
+ * runs, and its outcome is recorded on the task. Every state change goes
138
+ * through the MCP task tools, so the store is the same one `hive-mind status`
139
+ * and `task status` read.
140
+ */
141
+ export async function workOnce(opts) {
142
+ let taskId = opts.taskId;
143
+ if (!taskId) {
144
+ const listed = await callMCPTool('task_list', { status: 'pending', type: 'hive-mind', limit: 1000 });
145
+ const oldest = [...(listed.tasks ?? [])].sort((a, b) => a.createdAt.localeCompare(b.createdAt))[0];
146
+ if (!oldest)
147
+ return { dispatched: false, reason: 'No pending hive-mind tasks' };
148
+ taskId = oldest.taskId;
149
+ }
150
+ const task = await callMCPTool('task_status', { taskId });
151
+ if (task.status !== 'pending') {
152
+ return { dispatched: false, reason: `Task ${taskId} is ${task.status}, not pending` };
153
+ }
154
+ let workerId = opts.workerId;
155
+ if (!workerId) {
156
+ const hive = await callMCPTool('hive-mind_status', {});
157
+ workerId = hive.workers?.find((w) => w.status === 'idle')?.id;
158
+ if (!workerId)
159
+ return { dispatched: false, reason: 'No idle hive worker — run "hive-mind spawn" first' };
160
+ }
161
+ await callMCPTool('task_assign', { taskId, agentIds: [workerId] });
162
+ const prompt = `You are hive-mind worker ${workerId}. Complete this task, then reply with ` +
163
+ `the result only.\n\nTask ${taskId}:\n${task.description}`;
164
+ const outcome = await opts.runner(prompt, {
165
+ mcpConfig: opts.mcpConfig,
166
+ timeoutMs: timeoutFromTags(task.tags, opts.defaultTimeoutMs ?? 300_000),
167
+ cwd: opts.cwd,
168
+ });
169
+ const result = {
170
+ worker: workerId,
171
+ output: outcome.output,
172
+ exitCode: outcome.exitCode,
173
+ durationMs: outcome.durationMs,
174
+ ...(outcome.sessionId ? { sessionId: outcome.sessionId } : {}),
175
+ ...(outcome.costUsd !== undefined ? { costUsd: outcome.costUsd } : {}),
176
+ };
177
+ if (outcome.ok) {
178
+ await callMCPTool('task_complete', { taskId, result });
179
+ return { dispatched: true, taskId, workerId, status: 'completed', outcome };
180
+ }
181
+ await callMCPTool('task_update', { taskId, status: 'failed', result });
182
+ return { dispatched: true, taskId, workerId, status: 'failed', outcome };
183
+ }
184
+ export const workCommand = {
185
+ name: 'work',
186
+ description: 'Run the next pending hive task on an idle worker via Claude Code and record the result',
187
+ options: [
188
+ { name: 'task', description: 'Run this task id instead of the oldest pending one', type: 'string' },
189
+ { name: 'worker', description: 'Assign to this worker id instead of the first idle one', type: 'string' },
190
+ { name: 'mcp-config', description: 'MCP config for the worker (default: generated, with the ruflo server)', type: 'string' },
191
+ ],
192
+ examples: [
193
+ { command: 'claude-flow hive-mind work', description: 'Run the oldest pending hive task' },
194
+ { command: 'claude-flow hive-mind work --task task-123', description: 'Run a specific task' },
195
+ ],
196
+ action: async (ctx) => {
197
+ const cwd = ctx.cwd || process.cwd();
198
+ try {
199
+ const mcpConfig = resolveWorkerMcpConfig({
200
+ explicit: ctx.flags.mcpConfig,
201
+ cwd,
202
+ home: process.env.HOME || process.env.USERPROFILE || '',
203
+ outDir: join(cwd, '.hive-mind', 'sessions'),
204
+ swarmId: 'worker',
205
+ rufloServer: currentRufloServer(),
206
+ });
207
+ const r = await workOnce({
208
+ cwd,
209
+ mcpConfig,
210
+ runner: runWithClaude,
211
+ taskId: ctx.flags.task,
212
+ workerId: ctx.flags.worker,
213
+ });
214
+ if (ctx.flags.format === 'json') {
215
+ output.printJson(r);
216
+ }
217
+ else if (!r.dispatched) {
218
+ output.printInfo(r.reason);
219
+ }
220
+ else {
221
+ output.printBox([
222
+ `Task ID: ${r.taskId}`,
223
+ `Worker: ${r.workerId}`,
224
+ `Status: ${r.status}`,
225
+ `Duration: ${r.outcome.durationMs}ms`,
226
+ '',
227
+ r.outcome.output.slice(0, 2000),
228
+ ].join('\n'), r.status === 'completed' ? 'Task Completed' : 'Task Failed');
229
+ output.writeln(output.dim(` Full record: claude-flow task status ${r.taskId}`));
230
+ }
231
+ // Nothing to do is not an error; a task that ran and failed is.
232
+ const failed = r.dispatched && r.status === 'failed';
233
+ return { success: !failed, exitCode: failed ? 1 : 0, data: r };
234
+ }
235
+ catch (error) {
236
+ const msg = error instanceof MCPClientError ? error.message : String(error);
237
+ output.printError(`Work error: ${msg}`);
238
+ return { success: false, exitCode: 1 };
239
+ }
240
+ },
241
+ };
242
+ //# sourceMappingURL=hive-mind-worker.js.map
@@ -10,7 +10,7 @@ import { select, confirm, input } from '../prompt.js';
10
10
  import { callMCPTool, MCPClientError } from '../mcp-client.js';
11
11
  import { spawn as childSpawn } from 'child_process';
12
12
  import { mkdir, writeFile } from 'fs/promises';
13
- import { existsSync } from 'fs';
13
+ import { resolveWorkerMcpConfig, currentRufloServer, workCommand } from './hive-mind-worker.js';
14
14
  import { join } from 'path';
15
15
  import { resolveClaudeLaunchCommand } from '../runtime/claude-command.js';
16
16
  import { getHiveTokenForCli } from '../mcp-tools/hive-mind-tools.js';
@@ -202,44 +202,25 @@ async function spawnClaudeCodeInstance(swarmId, swarmName, objective, workers, f
202
202
  // Build arguments - flags first, then prompt
203
203
  const claudeArgs = [];
204
204
  // #1748 Issue 2 — pass --mcp-config so the spawned worker actually has
205
- // mcp__ruflo__* tools registered. Before this, the coordination prompt
206
- // referenced tools the worker didn't know about and exited silently.
207
- // Resolution order:
208
- // 1. explicit --mcp-config <path> flag passed by the caller
209
- // 2. ./.mcp.json in cwd (project-local Ruflo MCP config)
210
- // 3. ~/.claude.json or ~/.claude/mcp.json (user-global)
211
- // If none found, we still spawn but warn — that's the pre-fix behavior
212
- // and the user's debug log will surface the missing tools.
213
- const explicitMcpConfig = flags['mcp-config'];
214
- let mcpConfigPath = explicitMcpConfig;
215
- if (!mcpConfigPath) {
216
- const candidates = [
217
- join(process.cwd(), '.mcp.json'),
218
- join(process.env.HOME || process.env.USERPROFILE || '', '.claude.json'),
219
- join(process.env.HOME || process.env.USERPROFILE || '', '.claude', 'mcp.json'),
220
- ];
221
- for (const c of candidates) {
222
- try {
223
- if (c && existsSync(c)) {
224
- mcpConfigPath = c;
225
- break;
226
- }
227
- }
228
- catch { /* continue */ }
229
- }
230
- }
231
- if (mcpConfigPath) {
232
- // #1780 — Claude Code's `--mcp-config` is variadic; passing it as two
233
- // argv tokens (`--mcp-config`, `<path>`) lets a later positional (the
234
- // hive-mind prompt) be slurped as a second config file, producing
235
- // `ENAMETOOLONG: name too long, open` once the prompt exceeds PATH_MAX.
236
- // Use `=`-syntax so the flag stays attached to its single value.
237
- claudeArgs.push(`--mcp-config=${mcpConfigPath}`);
238
- output.printInfo(`Spawned worker MCP config: ${mcpConfigPath}`);
239
- }
240
- else {
241
- output.printWarning('No .mcp.json or ~/.claude.json found — spawned worker will not have mcp__ruflo__* tools (#1748 Issue 2). Pass --mcp-config <path> or run "ruflo init" to generate one.');
242
- }
205
+ // mcp__ruflo__* tools registered. The config is always a generated file
206
+ // that Claude accepts; see resolveWorkerMcpConfig for why a source
207
+ // config (notably ~/.claude.json) is never passed through as-is.
208
+ // The parser stores flags camelCased (#2269), so read both forms.
209
+ const mcpConfigPath = resolveWorkerMcpConfig({
210
+ explicit: (flags['mcp-config'] ?? flags.mcpConfig),
211
+ cwd: process.cwd(),
212
+ home: process.env.HOME || process.env.USERPROFILE || '',
213
+ outDir: sessionsDir,
214
+ swarmId,
215
+ rufloServer: currentRufloServer(),
216
+ });
217
+ // #1780 — Claude Code's `--mcp-config` is variadic; passing it as two
218
+ // argv tokens (`--mcp-config`, `<path>`) lets a later positional (the
219
+ // hive-mind prompt) be slurped as a second config file, producing
220
+ // `ENAMETOOLONG: name too long, open` once the prompt exceeds PATH_MAX.
221
+ // Use `=`-syntax so the flag stays attached to its single value.
222
+ claudeArgs.push(`--mcp-config=${mcpConfigPath}`);
223
+ output.printInfo(`Spawned worker MCP config: ${mcpConfigPath}`);
243
224
  // Check for non-interactive mode
244
225
  const isNonInteractive = flags['non-interactive'] || flags.nonInteractive;
245
226
  if (isNonInteractive) {
@@ -882,7 +863,8 @@ const taskCommand = {
882
863
  ].join('\n'), 'Task Submitted');
883
864
  output.writeln();
884
865
  output.printSuccess('Task submitted to hive');
885
- output.writeln(output.dim(` Track with: claude-flow hive-mind task-status ${result.taskId}`));
866
+ output.writeln(output.dim(` Run it with: claude-flow hive-mind work --task ${result.taskId}`));
867
+ output.writeln(output.dim(` Track with: claude-flow task status ${result.taskId}`));
886
868
  return { success: true, data: result };
887
869
  }
888
870
  catch (error) {
@@ -1210,7 +1192,7 @@ export const hiveMindCommand = {
1210
1192
  name: 'hive-mind',
1211
1193
  aliases: ['hive'],
1212
1194
  description: 'Queen-led consensus-based multi-agent coordination',
1213
- subcommands: [initCommand, spawnCommand, statusCommand, taskCommand, joinCommand, leaveCommand, consensusCommand, broadcastCommand, memorySubCommand, optimizeMemoryCommand, shutdownCommand],
1195
+ subcommands: [initCommand, spawnCommand, statusCommand, taskCommand, workCommand, joinCommand, leaveCommand, consensusCommand, broadcastCommand, memorySubCommand, optimizeMemoryCommand, shutdownCommand],
1214
1196
  options: [],
1215
1197
  examples: [
1216
1198
  { command: 'claude-flow hive-mind init -t hierarchical-mesh', description: 'Initialize hive' },
@@ -1230,6 +1212,7 @@ export const hiveMindCommand = {
1230
1212
  `${output.highlight('spawn')} - Spawn worker agents (use --claude to launch Claude Code)`,
1231
1213
  `${output.highlight('status')} - Show hive status`,
1232
1214
  `${output.highlight('task')} - Submit task to hive`,
1215
+ `${output.highlight('work')} - Run the next pending task on an idle worker`,
1233
1216
  `${output.highlight('join')} - Join an agent to the hive`,
1234
1217
  `${output.highlight('leave')} - Remove an agent from the hive`,
1235
1218
  `${output.highlight('consensus')} - Manage consensus proposals`,
@@ -1,7 +1,4 @@
1
- /**
2
- * V3 CLI Hooks Command
3
- * Self-learning hooks system for intelligent workflow automation
4
- */
1
+ import { validateFeedbackPatterns } from '../memory/feedback-patterns.js';
5
2
  import { output } from '../output.js';
6
3
  import { confirm } from '../prompt.js';
7
4
  import { callMCPTool, MCPClientError } from '../mcp-client.js';
@@ -572,6 +569,11 @@ const postCommandCommand = {
572
569
  short: 'd',
573
570
  description: 'Execution duration in milliseconds',
574
571
  type: 'number'
572
+ },
573
+ {
574
+ name: 'ttl',
575
+ description: 'Command history lifetime in seconds (default: 30 days)',
576
+ type: 'number'
575
577
  }
576
578
  ],
577
579
  examples: [
@@ -594,11 +596,16 @@ const postCommandCommand = {
594
596
  success,
595
597
  exitCode: ctx.flags.exitCode || 0,
596
598
  duration: ctx.flags.duration,
599
+ ttl: ctx.flags.ttl,
597
600
  timestamp: Date.now(),
598
601
  });
599
602
  if (ctx.flags.format === 'json') {
600
603
  output.printJson(result);
601
- return { success: true, data: result };
604
+ return { success: result.recorded, exitCode: result.recorded ? 0 : 1, data: result };
605
+ }
606
+ if (!result.recorded) {
607
+ output.printError('Command outcome could not be recorded');
608
+ return { success: false, exitCode: 1, data: result };
602
609
  }
603
610
  output.writeln();
604
611
  output.printSuccess('Command outcome recorded');
@@ -1699,6 +1706,7 @@ const postTaskCommand = {
1699
1706
  name: 'post-task',
1700
1707
  description: 'Record task completion for learning',
1701
1708
  options: [
1709
+ { name: 'patterns', description: 'JSON array of learned pattern strings for feedback and skill creation', type: 'string' },
1702
1710
  {
1703
1711
  name: 'task-id',
1704
1712
  short: 'i',
@@ -1784,8 +1792,10 @@ const postTaskCommand = {
1784
1792
  const success = ctx.flags.success !== undefined ? ctx.flags.success : true;
1785
1793
  output.printInfo(`Recording outcome for task: ${output.highlight(taskId)}`);
1786
1794
  try {
1795
+ const patterns = validateFeedbackPatterns(ctx.flags.patterns === undefined ? undefined : JSON.parse(ctx.flags.patterns));
1787
1796
  const result = await callMCPTool('hooks_post-task', {
1788
1797
  taskId,
1798
+ patterns,
1789
1799
  success,
1790
1800
  quality: ctx.flags.quality,
1791
1801
  agent: ctx.flags.agent,