claude-flow 3.10.40 → 3.10.41

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-flow",
3
- "version": "3.10.40",
3
+ "version": "3.10.41",
4
4
  "description": "Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -306,7 +306,20 @@ async function spawnClaudeCodeInstance(swarmId, swarmName, objective, workers, f
306
306
  output.printSuccess('Claude Code launched with Hive Mind coordination');
307
307
  output.printInfo('The Queen coordinator will orchestrate all worker agents');
308
308
  output.writeln(output.dim(`Prompt file saved at: ${promptFile}`));
309
- return { success: true, promptFile };
309
+ // #2297: await child exit before returning. Without this, the CLI
310
+ // process resolves immediately, finishes, and the still-initializing
311
+ // `claude` child loses its controlling terminal and is killed mid-launch
312
+ // — visible as a stray XTVERSION reply leaking onto the next shell
313
+ // prompt (the terminal queried for capabilities, but the child died
314
+ // before reading the answer). Awaiting also makes the existing
315
+ // claudeProcess.on('exit', ...) log lines actually print, and lets the
316
+ // non-interactive (-p / --non-interactive) path complete only after
317
+ // Claude Code finishes.
318
+ const claudeExitCode = await new Promise((resolve) => {
319
+ claudeProcess.on('exit', (c) => resolve(c ?? 0));
320
+ claudeProcess.on('error', () => resolve(1));
321
+ });
322
+ return { success: claudeExitCode === 0, promptFile };
310
323
  }
311
324
  else if (dryRun) {
312
325
  output.writeln();
@@ -1560,6 +1560,19 @@ const postTaskCommand = {
1560
1560
  short: 'a',
1561
1561
  description: 'Agent that executed the task',
1562
1562
  type: 'string'
1563
+ },
1564
+ {
1565
+ // ADR-147 P2: nested-subagent spawn-tree capture
1566
+ name: 'parent-agent-id',
1567
+ description: 'ID of the parent agent (from Claude Code\'s parent_agent_id OTel span tag). Omit for top-level work.',
1568
+ type: 'string',
1569
+ required: false
1570
+ },
1571
+ {
1572
+ name: 'depth',
1573
+ description: 'Chain depth from root lead session (0 = lead, 1+ = subagent). Used by ADR-147 P3 depth-aware guardrail.',
1574
+ type: 'number',
1575
+ required: false
1563
1576
  }
1564
1577
  ],
1565
1578
  examples: [
@@ -1579,6 +1592,9 @@ const postTaskCommand = {
1579
1592
  quality: ctx.flags.quality,
1580
1593
  agent: ctx.flags.agent,
1581
1594
  timestamp: Date.now(),
1595
+ // ADR-147 P2: forward spawn-tree lineage if caller supplied it
1596
+ parentAgentId: ctx.flags.parentAgentId,
1597
+ depth: ctx.flags.depth,
1582
1598
  });
1583
1599
  if (ctx.flags.format === 'json') {
1584
1600
  output.printJson(result);
@@ -65,10 +65,51 @@ const CONFIG = {
65
65
  const CWD = process.cwd();
66
66
 
67
67
  // ─── Delegation cache ───────────────────────────────────────────
68
- // Cache the CLI JSON result for 10s so rapid prompt re-renders
69
- // (e.g. every keypress in some shells) don't re-invoke npx each time.
68
+ // Cache the CLI JSON result for 60s so rapid prompt re-renders
69
+ // (Claude Code refreshes the statusline several times a second while
70
+ // streaming) don't re-invoke the CLI each time. #2337: bumped 10s→60s
71
+ // because 10s was far too short for how often Claude Code re-renders.
70
72
  const CACHE_FILE = path.join(os.tmpdir(), 'ruflo-statusline-cache-' + require('crypto').createHash('md5').update(CWD).digest('hex').slice(0, 8) + '.json');
71
- const CACHE_TTL_MS = 10000;
73
+ const CACHE_TTL_MS = 60000;
74
+
75
+ // #2337: resolve an already-installed @claude-flow/cli (or ruflo) bin so we
76
+ // can invoke it directly via \`node\`. The previous version called
77
+ // \`npx --yes @claude-flow/cli@latest\` on every uncached render, which forces
78
+ // a registry resolution + cold-start of the entire CLI per render. With
79
+ // multiple concurrent Claude Code sessions this storms the host (reporter
80
+ // saw load average 40-65 on a 12-core box).
81
+ //
82
+ // Returns the absolute path to bin/cli.js or null. Mirrors getPkgVersion()'s
83
+ // path probing (project, monorepo, plugin marketplace, global node_modules
84
+ // including custom-prefix layouts like ~/.npm-global).
85
+ function resolveCliBin() {
86
+ try {
87
+ const home = os.homedir();
88
+ const candidates = [
89
+ path.join(home, '.claude', 'plugins', 'marketplaces', 'ruflo', 'bin', 'cli.js'),
90
+ path.join(CWD, 'node_modules', '@claude-flow', 'cli', 'bin', 'cli.js'),
91
+ path.join(CWD, 'node_modules', 'ruflo', 'bin', 'cli.js'),
92
+ path.join(CWD, 'v3', '@claude-flow', 'cli', 'bin', 'cli.js'),
93
+ ];
94
+ try {
95
+ const binDir = path.dirname(process.execPath);
96
+ const globalModuleDirs = [path.join(binDir, '..', 'lib', 'node_modules'), path.join(binDir, 'node_modules')];
97
+ for (const prefix of [process.env.npm_config_prefix, process.env.PREFIX, path.join(home, '.npm-global')]) {
98
+ if (prefix) globalModuleDirs.push(path.join(prefix, 'lib', 'node_modules'));
99
+ }
100
+ for (const gm of globalModuleDirs) {
101
+ candidates.push(
102
+ path.join(gm, 'ruflo', 'bin', 'cli.js'),
103
+ path.join(gm, '@claude-flow', 'cli', 'bin', 'cli.js'),
104
+ );
105
+ }
106
+ } catch { /* ignore */ }
107
+ for (const p of candidates) {
108
+ if (fs.existsSync(p)) return p;
109
+ }
110
+ } catch { /* ignore */ }
111
+ return null;
112
+ }
72
113
 
73
114
  function readCache() {
74
115
  try {
@@ -99,8 +140,16 @@ function getStatuslineData() {
99
140
  if (cached) return cached;
100
141
 
101
142
  try {
143
+ // #2337: prefer an already-installed CLI bin via direct \`node\` invocation
144
+ // — no npx, no registry round-trip, no @latest re-resolve per render.
145
+ // Fall back to \`npx --prefer-offline @claude-flow/cli\` (no @latest) only
146
+ // when nothing is installed locally, so a cold environment still works.
147
+ const cliBin = resolveCliBin();
148
+ const cmd = cliBin
149
+ ? '"' + process.execPath + '" "' + cliBin + '" hooks statusline --json 2>/dev/null'
150
+ : 'npx --prefer-offline @claude-flow/cli hooks statusline --json 2>/dev/null';
102
151
  const raw = execSync(
103
- 'npx --yes @claude-flow/cli@latest hooks statusline --json 2>/dev/null',
152
+ cmd,
104
153
  { encoding: 'utf-8', timeout: 8000, stdio: ['pipe', 'pipe', 'pipe'], cwd: CWD }
105
154
  ).trim();
106
155
  // The CLI may emit preamble lines before the JSON — find the first '{'.
@@ -1239,6 +1239,9 @@ export const hooksPostTask = {
1239
1239
  quality: { type: 'number', description: 'Quality score (0-1)' },
1240
1240
  task: { type: 'string', description: 'Task description text (used for learning keyword extraction)' },
1241
1241
  storeDecisions: { type: 'boolean', description: 'Also store routing decision in memory DB' },
1242
+ // ADR-147 P2: nested-subagent spawn-tree capture
1243
+ parentAgentId: { type: 'string', description: 'ID of the parent agent (from Claude Code\'s parent_agent_id OTel span tag / x-claude-code-parent-agent-id header). Omit for top-level work.' },
1244
+ depth: { type: 'number', description: 'Chain depth from root lead session (0 = lead, 1+ = subagent). Used by ADR-147 P3 depth-aware guardrail.' },
1242
1245
  },
1243
1246
  required: ['taskId'],
1244
1247
  },
@@ -1258,6 +1261,22 @@ export const hooksPostTask = {
1258
1261
  if (!v.valid)
1259
1262
  return { success: false, error: v.error };
1260
1263
  }
1264
+ // ADR-147 P2: validate spawn-tree lineage if provided
1265
+ const parentAgentId = params.parentAgentId;
1266
+ if (parentAgentId !== undefined) {
1267
+ const v = validateIdentifier(parentAgentId, 'parentAgentId');
1268
+ if (!v.valid)
1269
+ return { success: false, error: v.error };
1270
+ }
1271
+ const depthRaw = params.depth;
1272
+ let depth;
1273
+ if (depthRaw !== undefined && depthRaw !== null) {
1274
+ const n = Number(depthRaw);
1275
+ if (!Number.isInteger(n) || n < 0 || n > 32) {
1276
+ return { success: false, error: 'depth must be a non-negative integer ≤ 32' };
1277
+ }
1278
+ depth = n;
1279
+ }
1261
1280
  // Phase 3: Wire recordFeedback through bridge → LearningSystem + ReasoningBank
1262
1281
  let feedbackResult = null;
1263
1282
  try {
@@ -1269,6 +1288,9 @@ export const hooksPostTask = {
1269
1288
  agent,
1270
1289
  duration: params.duration || undefined,
1271
1290
  patterns: params.patterns || undefined,
1291
+ // ADR-147 P2: forward spawn-tree lineage so it lands in feedback + memory
1292
+ parentAgentId,
1293
+ depth,
1272
1294
  });
1273
1295
  }
1274
1296
  catch {
@@ -265,6 +265,8 @@ export declare function bridgeRecordFeedback(options: {
265
265
  duration?: number;
266
266
  patterns?: string[];
267
267
  dbPath?: string;
268
+ parentAgentId?: string;
269
+ depth?: number;
268
270
  }): Promise<{
269
271
  success: boolean;
270
272
  controller: string;
@@ -1422,6 +1422,8 @@ export async function bridgeRecordFeedback(options) {
1422
1422
  await learningSystem.recordFeedback({
1423
1423
  taskId: options.taskId, success: options.success, quality: options.quality,
1424
1424
  agent: options.agent, duration: options.duration, timestamp: Date.now(),
1425
+ // ADR-147 P2: forward spawn-tree lineage if present
1426
+ parentAgentId: options.parentAgentId, depth: options.depth,
1425
1427
  });
1426
1428
  controller = 'learningSystem';
1427
1429
  updated++;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-flow/cli",
3
- "version": "3.10.40",
3
+ "version": "3.10.41",
4
4
  "type": "module",
5
5
  "description": "Ruflo CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
6
6
  "main": "dist/src/index.js",