claude-flow 3.5.19 → 3.5.21

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.5.19",
3
+ "version": "3.5.21",
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",
@@ -420,26 +420,86 @@ const metricsCommand = {
420
420
  action: async (ctx) => {
421
421
  const agentId = ctx.args[0];
422
422
  const period = ctx.flags.period;
423
- // Default metrics (updated by MCP agent/metrics when available)
423
+ // Collect real metrics from .swarm/ state
424
+ const { existsSync, readFileSync, readdirSync, statSync } = await import('fs');
425
+ const { join } = await import('path');
426
+ let totalAgents = 0;
427
+ let activeAgents = 0;
428
+ let tasksCompleted = 0;
429
+ const typeCounts = {};
430
+ // Read swarm agent state
431
+ const swarmDir = join(process.cwd(), '.swarm');
432
+ const agentsDir = join(swarmDir, 'agents');
433
+ if (existsSync(agentsDir)) {
434
+ try {
435
+ const files = readdirSync(agentsDir).filter(f => f.endsWith('.json'));
436
+ for (const file of files) {
437
+ try {
438
+ const data = JSON.parse(readFileSync(join(agentsDir, file), 'utf-8'));
439
+ totalAgents++;
440
+ const agType = data.type || 'unknown';
441
+ if (!typeCounts[agType])
442
+ typeCounts[agType] = { count: 0, tasks: 0, success: 0 };
443
+ typeCounts[agType].count++;
444
+ if (data.status === 'active' || data.status === 'running')
445
+ activeAgents++;
446
+ if (data.tasksCompleted) {
447
+ typeCounts[agType].tasks += data.tasksCompleted;
448
+ tasksCompleted += data.tasksCompleted;
449
+ }
450
+ if (data.successCount)
451
+ typeCounts[agType].success += data.successCount;
452
+ }
453
+ catch { /* skip malformed */ }
454
+ }
455
+ }
456
+ catch { /* no agents dir */ }
457
+ }
458
+ // Read swarm activity for additional state
459
+ const activityFile = join(swarmDir, 'swarm-activity.json');
460
+ if (existsSync(activityFile)) {
461
+ try {
462
+ const activity = JSON.parse(readFileSync(activityFile, 'utf-8'));
463
+ if (activity.totalAgents && totalAgents === 0)
464
+ totalAgents = activity.totalAgents;
465
+ if (activity.activeAgents && activeAgents === 0)
466
+ activeAgents = activity.activeAgents;
467
+ }
468
+ catch { /* ignore */ }
469
+ }
470
+ // Read memory.db stats
471
+ let vectorCount = 0;
472
+ const dbPath = join(swarmDir, 'memory.db');
473
+ if (existsSync(dbPath)) {
474
+ try {
475
+ const dbSize = statSync(dbPath).size;
476
+ vectorCount = Math.floor(dbSize / 2048);
477
+ }
478
+ catch { /* ignore */ }
479
+ }
480
+ const byType = Object.entries(typeCounts).map(([type, data]) => ({
481
+ type,
482
+ count: data.count,
483
+ tasks: data.tasks,
484
+ successRate: data.tasks > 0 ? `${Math.round((data.success / data.tasks) * 100)}%` : 'N/A'
485
+ }));
486
+ const avgSuccessRate = tasksCompleted > 0
487
+ ? `${Math.round(Object.values(typeCounts).reduce((a, d) => a + d.success, 0) / tasksCompleted * 100)}%`
488
+ : 'N/A';
424
489
  const metrics = {
425
490
  period,
426
491
  summary: {
427
- totalAgents: 4,
428
- activeAgents: 3,
429
- tasksCompleted: 127,
430
- avgSuccessRate: '96.2%',
431
- totalTokens: 1234567,
432
- avgResponseTime: '1.45s'
492
+ totalAgents,
493
+ activeAgents,
494
+ tasksCompleted,
495
+ avgSuccessRate,
496
+ vectorCount,
497
+ note: totalAgents === 0 ? 'No agents spawned yet. Use: agent spawn -t coder' : undefined
433
498
  },
434
- byType: [
435
- { type: 'coder', count: 2, tasks: 45, successRate: '97%' },
436
- { type: 'researcher', count: 1, tasks: 32, successRate: '95%' },
437
- { type: 'tester', count: 1, tasks: 50, successRate: '98%' }
438
- ],
499
+ byType,
439
500
  performance: {
440
- flashAttention: '2.8x speedup',
441
- memoryReduction: '52%',
442
- searchImprovement: '150x faster'
501
+ memoryVectors: `${vectorCount} vectors`,
502
+ searchBackend: vectorCount > 0 ? 'HNSW-indexed' : 'none'
443
503
  }
444
504
  };
445
505
  if (ctx.flags.format === 'json') {
@@ -459,8 +519,7 @@ const metricsCommand = {
459
519
  { metric: 'Active Agents', value: metrics.summary.activeAgents },
460
520
  { metric: 'Tasks Completed', value: metrics.summary.tasksCompleted },
461
521
  { metric: 'Success Rate', value: metrics.summary.avgSuccessRate },
462
- { metric: 'Total Tokens', value: metrics.summary.totalTokens.toLocaleString() },
463
- { metric: 'Avg Response Time', value: metrics.summary.avgResponseTime }
522
+ { metric: 'Memory Vectors', value: metrics.summary.vectorCount }
464
523
  ]
465
524
  });
466
525
  output.writeln();
@@ -474,12 +533,15 @@ const metricsCommand = {
474
533
  ],
475
534
  data: metrics.byType
476
535
  });
536
+ if (metrics.summary.note) {
537
+ output.writeln();
538
+ output.writeln(output.dim(metrics.summary.note));
539
+ }
477
540
  output.writeln();
478
- output.writeln(output.bold('V3 Performance Gains'));
541
+ output.writeln(output.bold('Memory'));
479
542
  output.printList([
480
- `Flash Attention: ${output.success(metrics.performance.flashAttention)}`,
481
- `Memory Reduction: ${output.success(metrics.performance.memoryReduction)}`,
482
- `Search: ${output.success(metrics.performance.searchImprovement)}`
543
+ `Vectors: ${output.success(metrics.performance.memoryVectors)}`,
544
+ `Backend: ${output.success(metrics.performance.searchBackend)}`
483
545
  ]);
484
546
  return { success: true, data: metrics };
485
547
  }
@@ -1146,9 +1146,8 @@ const preTaskCommand = {
1146
1146
  {
1147
1147
  name: 'task-id',
1148
1148
  short: 'i',
1149
- description: 'Unique task identifier',
1150
- type: 'string',
1151
- required: true
1149
+ description: 'Unique task identifier (auto-generated if omitted)',
1150
+ type: 'string'
1152
1151
  },
1153
1152
  {
1154
1153
  name: 'description',
@@ -1170,10 +1169,10 @@ const preTaskCommand = {
1170
1169
  { command: 'claude-flow hooks pre-task -i task-456 -d "Implement feature" --auto-spawn', description: 'With auto-spawn' }
1171
1170
  ],
1172
1171
  action: async (ctx) => {
1173
- const taskId = ctx.flags.taskId;
1172
+ const taskId = ctx.flags.taskId || `task-${Date.now().toString(36)}`;
1174
1173
  const description = ctx.args[0] || ctx.flags.description;
1175
- if (!taskId || !description) {
1176
- output.printError('Task ID and description are required.');
1174
+ if (!description) {
1175
+ output.printError('Description is required: --description "your task"');
1177
1176
  return { success: false, exitCode: 1 };
1178
1177
  }
1179
1178
  output.printInfo(`Starting task: ${output.highlight(taskId)}`);
@@ -3649,6 +3648,45 @@ const taskCompletedCommand = {
3649
3648
  }
3650
3649
  }
3651
3650
  };
3651
+ // Notify subcommand
3652
+ const notifyCommand = {
3653
+ name: 'notify',
3654
+ description: 'Send a notification message (logged to session)',
3655
+ options: [
3656
+ { name: 'message', short: 'm', type: 'string', description: 'Notification message', required: true },
3657
+ { name: 'level', short: 'l', type: 'string', description: 'Level: info, warn, error', default: 'info' },
3658
+ { name: 'channel', short: 'c', type: 'string', description: 'Notification channel', default: 'console' },
3659
+ ],
3660
+ examples: [
3661
+ { command: 'claude-flow hooks notify -m "Build complete"', description: 'Send info notification' },
3662
+ { command: 'claude-flow hooks notify -m "Test failed" -l error', description: 'Send error notification' },
3663
+ ],
3664
+ action: async (ctx) => {
3665
+ const message = ctx.args[0] || ctx.flags.message;
3666
+ const level = ctx.flags.level || 'info';
3667
+ if (!message) {
3668
+ output.printError('Message is required: --message "your message"');
3669
+ return { success: false, exitCode: 1 };
3670
+ }
3671
+ const timestamp = new Date().toISOString();
3672
+ if (level === 'error') {
3673
+ output.printError(`[${timestamp}] ${message}`);
3674
+ }
3675
+ else if (level === 'warn') {
3676
+ output.writeln(output.warning(`[${timestamp}] ${message}`));
3677
+ }
3678
+ else {
3679
+ output.printInfo(`[${timestamp}] ${message}`);
3680
+ }
3681
+ // Store notification in memory if available
3682
+ try {
3683
+ const { storeEntry } = await import('../memory/memory-initializer.js');
3684
+ await storeEntry({ key: `notify-${Date.now()}`, value: `[${level}] ${message}`, namespace: 'notifications' });
3685
+ }
3686
+ catch { /* memory not available */ }
3687
+ return { success: true, data: { timestamp, level, message } };
3688
+ }
3689
+ };
3652
3690
  // Main hooks command
3653
3691
  export const hooksCommand = {
3654
3692
  name: 'hooks',
@@ -3670,6 +3708,7 @@ export const hooksCommand = {
3670
3708
  transferCommand,
3671
3709
  listCommand,
3672
3710
  intelligenceCommand,
3711
+ notifyCommand,
3673
3712
  workerCommand,
3674
3713
  progressHookCommand,
3675
3714
  statuslineCommand,
@@ -30,7 +30,7 @@ const trainCommand = {
30
30
  { command: 'claude-flow neural train -p security --wasm --contrastive', description: 'Security patterns with contrastive learning' },
31
31
  ],
32
32
  action: async (ctx) => {
33
- const patternType = ctx.flags.pattern || 'coordination';
33
+ const patternType = (ctx.flags.pattern || ctx.flags.patternType || ctx.flags['pattern-type']) || 'coordination';
34
34
  const epochs = parseInt(ctx.flags.epochs || '50', 10);
35
35
  const learningRate = parseFloat(ctx.flags['learning-rate'] || '0.01');
36
36
  const batchSize = parseInt(ctx.flags['batch-size'] || '32', 10);
@@ -340,21 +340,52 @@ const auditCommand = {
340
340
  output.writeln();
341
341
  output.writeln(output.bold('Security Audit Log'));
342
342
  output.writeln(output.dim('─'.repeat(60)));
343
- output.printTable({
344
- columns: [
345
- { key: 'timestamp', header: 'Timestamp', width: 22 },
346
- { key: 'event', header: 'Event', width: 20 },
347
- { key: 'user', header: 'User', width: 15 },
348
- { key: 'status', header: 'Status', width: 12 },
349
- ],
350
- data: [
351
- { timestamp: '2024-01-15 14:32:01', event: 'AUTH_LOGIN', user: 'admin', status: output.success('Success') },
352
- { timestamp: '2024-01-15 14:30:45', event: 'CONFIG_CHANGE', user: 'system', status: output.success('Success') },
353
- { timestamp: '2024-01-15 14:28:12', event: 'AUTH_FAILED', user: 'unknown', status: output.error('Failed') },
354
- { timestamp: '2024-01-15 14:25:33', event: 'SCAN_COMPLETE', user: 'ci-bot', status: output.success('Success') },
355
- { timestamp: '2024-01-15 14:20:00', event: 'KEY_ROTATE', user: 'admin', status: output.success('Success') },
356
- ],
357
- });
343
+ // Generate real audit entries from .swarm/ state and session history
344
+ const { existsSync, readFileSync, readdirSync, statSync } = await import('fs');
345
+ const { join } = await import('path');
346
+ const auditEntries = [];
347
+ const swarmDir = join(process.cwd(), '.swarm');
348
+ // Check session files for real audit events
349
+ if (existsSync(swarmDir)) {
350
+ try {
351
+ const files = readdirSync(swarmDir).filter(f => f.endsWith('.json'));
352
+ for (const file of files.slice(-10)) {
353
+ try {
354
+ const stat = statSync(join(swarmDir, file));
355
+ const ts = stat.mtime.toISOString().replace('T', ' ').substring(0, 19);
356
+ auditEntries.push({
357
+ timestamp: ts,
358
+ event: file.includes('session') ? 'SESSION_UPDATE' :
359
+ file.includes('swarm') ? 'SWARM_ACTIVITY' :
360
+ file.includes('memory') ? 'MEMORY_WRITE' : 'CONFIG_CHANGE',
361
+ user: 'system',
362
+ status: output.success('Success')
363
+ });
364
+ }
365
+ catch { /* skip */ }
366
+ }
367
+ }
368
+ catch { /* ignore */ }
369
+ }
370
+ // Add current session entry
371
+ const now = new Date().toISOString().replace('T', ' ').substring(0, 19);
372
+ auditEntries.push({ timestamp: now, event: 'AUDIT_RUN', user: 'cli', status: output.success('Success') });
373
+ // Sort by timestamp desc
374
+ auditEntries.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
375
+ if (auditEntries.length === 0) {
376
+ output.writeln(output.dim('No audit events found. Initialize a project first: claude-flow init'));
377
+ }
378
+ else {
379
+ output.printTable({
380
+ columns: [
381
+ { key: 'timestamp', header: 'Timestamp', width: 22 },
382
+ { key: 'event', header: 'Event', width: 20 },
383
+ { key: 'user', header: 'User', width: 15 },
384
+ { key: 'status', header: 'Status', width: 12 },
385
+ ],
386
+ data: auditEntries.slice(0, parseInt(ctx.flags.limit || '20', 10)),
387
+ });
388
+ }
358
389
  return { success: true };
359
390
  },
360
391
  };
@@ -153,6 +153,14 @@ export function generateSettings(options) {
153
153
  * Detect if we're on Windows for platform-aware hook commands.
154
154
  */
155
155
  const IS_WINDOWS = process.platform === 'win32';
156
+ /**
157
+ * Platform-aware project dir variable reference.
158
+ * Claude Code sets $CLAUDE_PROJECT_DIR (Unix) / %CLAUDE_PROJECT_DIR% (Windows)
159
+ * as an environment variable before running hook commands.
160
+ */
161
+ function projectDirVar() {
162
+ return IS_WINDOWS ? '%CLAUDE_PROJECT_DIR%' : '$CLAUDE_PROJECT_DIR';
163
+ }
156
164
  /**
157
165
  * Build a cross-platform hook command.
158
166
  * On Windows, wraps with `cmd /c` to avoid PowerShell stdin/process issues
@@ -171,11 +179,15 @@ function hookCmdEsm(script, subcommand) {
171
179
  }
172
180
  /** Shorthand for CJS hook-handler commands */
173
181
  function hookHandlerCmd(subcommand) {
174
- return hookCmd('"$CLAUDE_PROJECT_DIR/.claude/helpers/hook-handler.cjs"', subcommand);
182
+ const dir = projectDirVar();
183
+ const quote = IS_WINDOWS ? '' : '"';
184
+ return hookCmd(`${quote}${dir}/.claude/helpers/hook-handler.cjs${quote}`, subcommand);
175
185
  }
176
186
  /** Shorthand for ESM auto-memory-hook commands */
177
187
  function autoMemoryCmd(subcommand) {
178
- return hookCmdEsm('"$CLAUDE_PROJECT_DIR/.claude/helpers/auto-memory-hook.mjs"', subcommand);
188
+ const dir = projectDirVar();
189
+ const quote = IS_WINDOWS ? '' : '"';
190
+ return hookCmdEsm(`${quote}${dir}/.claude/helpers/auto-memory-hook.mjs${quote}`, subcommand);
179
191
  }
180
192
  /**
181
193
  * Generate statusLine configuration for Claude Code
@@ -187,9 +199,11 @@ function generateStatusLineConfig(_options) {
187
199
  // The script runs after each assistant message (debounced 300ms).
188
200
  // NOTE: statusline must NOT use `cmd /c` — Claude Code manages its stdin
189
201
  // directly for statusline commands, and `cmd /c` blocks stdin forwarding.
202
+ const dir = projectDirVar();
203
+ const quote = IS_WINDOWS ? '' : '"';
190
204
  return {
191
205
  type: 'command',
192
- command: `node "$CLAUDE_PROJECT_DIR/.claude/helpers/statusline.cjs"`,
206
+ command: `node ${quote}${dir}/.claude/helpers/statusline.cjs${quote}`,
193
207
  };
194
208
  }
195
209
  /**
@@ -545,9 +545,13 @@ export async function findSimilarPatterns(query, options) {
545
545
  const queryResult = await generateEmbedding(query);
546
546
  queryEmbedding = queryResult.embedding;
547
547
  }
548
+ // Hash-fallback embeddings (128-dim) produce lower cosine similarities
549
+ // than ONNX/transformer embeddings, so use a lower default threshold
550
+ const isHashFallback = queryEmbedding.length === 128;
551
+ const defaultThreshold = isHashFallback ? 0.1 : 0.5;
548
552
  const results = reasoningBank.findSimilar(queryEmbedding, {
549
553
  k: options?.k ?? 5,
550
- threshold: options?.threshold ?? 0.5,
554
+ threshold: options?.threshold ?? defaultThreshold,
551
555
  type: options?.type
552
556
  });
553
557
  return results.map((r) => ({
@@ -32,6 +32,16 @@ export declare class CommandParser {
32
32
  private parseValue;
33
33
  private normalizeKey;
34
34
  private buildAliases;
35
+ /**
36
+ * Build aliases scoped to a specific command/subcommand.
37
+ * The resolved command's short flags take priority over global ones,
38
+ * fixing collisions where multiple subcommands use the same short flag (e.g. -t).
39
+ */
40
+ private buildScopedAliases;
41
+ /**
42
+ * Get boolean flags scoped to a specific command/subcommand.
43
+ */
44
+ private getScopedBooleanFlags;
35
45
  private getBooleanFlags;
36
46
  private applyDefaults;
37
47
  validateFlags(flags: ParsedFlags, command?: Command): string[];
@@ -101,9 +101,23 @@ export class CommandParser {
101
101
  positional: [],
102
102
  raw: [...args]
103
103
  };
104
- // Build flag configuration from global options
105
- const aliases = this.buildAliases();
106
- const booleanFlags = this.getBooleanFlags();
104
+ // Pass 1: Identify command and subcommand (skip flags)
105
+ let resolvedCmd;
106
+ let resolvedSub;
107
+ for (const arg of args) {
108
+ if (arg.startsWith('-'))
109
+ continue;
110
+ if (!resolvedCmd && this.commands.has(arg)) {
111
+ resolvedCmd = this.commands.get(arg);
112
+ }
113
+ else if (resolvedCmd && !resolvedSub && resolvedCmd.subcommands) {
114
+ resolvedSub = resolvedCmd.subcommands.find(sc => sc.name === arg || sc.aliases?.includes(arg));
115
+ }
116
+ }
117
+ // Pass 2: Build aliases scoped to the resolved subcommand
118
+ // Subcommand-specific aliases take priority over global ones
119
+ const aliases = this.buildScopedAliases(resolvedSub || resolvedCmd);
120
+ const booleanFlags = this.getScopedBooleanFlags(resolvedSub || resolvedCmd);
107
121
  let i = 0;
108
122
  let parsingFlags = true;
109
123
  while (i < args.length) {
@@ -275,6 +289,38 @@ export class CommandParser {
275
289
  }
276
290
  return { ...aliases, ...this.options.aliases };
277
291
  }
292
+ /**
293
+ * Build aliases scoped to a specific command/subcommand.
294
+ * The resolved command's short flags take priority over global ones,
295
+ * fixing collisions where multiple subcommands use the same short flag (e.g. -t).
296
+ */
297
+ buildScopedAliases(resolvedCmd) {
298
+ // Start with global aliases as base
299
+ const aliases = this.buildAliases();
300
+ // Override with the resolved command's own options (these take priority)
301
+ if (resolvedCmd?.options) {
302
+ for (const opt of resolvedCmd.options) {
303
+ if (opt.short) {
304
+ aliases[opt.short] = opt.name;
305
+ }
306
+ }
307
+ }
308
+ return aliases;
309
+ }
310
+ /**
311
+ * Get boolean flags scoped to a specific command/subcommand.
312
+ */
313
+ getScopedBooleanFlags(resolvedCmd) {
314
+ const flags = this.getBooleanFlags();
315
+ if (resolvedCmd?.options) {
316
+ for (const opt of resolvedCmd.options) {
317
+ if (opt.type === 'boolean') {
318
+ flags.add(this.normalizeKey(opt.name));
319
+ }
320
+ }
321
+ }
322
+ return flags;
323
+ }
278
324
  getBooleanFlags() {
279
325
  const flags = new Set();
280
326
  for (const opt of this.globalOptions) {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-flow/cli",
3
- "version": "3.5.19",
3
+ "version": "3.5.21",
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",