claude-flow 3.5.49 → 3.5.51

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.49",
3
+ "version": "3.5.51",
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",
@@ -42,11 +42,19 @@ const scanCommand = {
42
42
  try {
43
43
  const packageJsonPath = path.resolve(target, 'package.json');
44
44
  if (fs.existsSync(packageJsonPath)) {
45
- const auditResult = execSync('npm audit --json 2>/dev/null || true', {
46
- cwd: path.resolve(target),
47
- encoding: 'utf-8',
48
- maxBuffer: 10 * 1024 * 1024,
49
- });
45
+ let auditResult;
46
+ try {
47
+ auditResult = execSync('npm audit --json', {
48
+ cwd: path.resolve(target),
49
+ encoding: 'utf-8',
50
+ maxBuffer: 10 * 1024 * 1024,
51
+ stdio: ['pipe', 'pipe', 'pipe'],
52
+ });
53
+ }
54
+ catch (auditErr) {
55
+ // npm audit exits non-zero when vulnerabilities found — stdout still has JSON
56
+ auditResult = auditErr.stdout || '{}';
57
+ }
50
58
  try {
51
59
  const audit = JSON.parse(auditResult);
52
60
  if (audit.vulnerabilities) {
@@ -215,7 +223,10 @@ const scanCommand = {
215
223
  const fixSpinner = output.createSpinner({ text: 'Attempting to fix vulnerabilities...', spinner: 'dots' });
216
224
  fixSpinner.start();
217
225
  try {
218
- execSync('npm audit fix 2>/dev/null || true', { cwd: path.resolve(target), encoding: 'utf-8' });
226
+ try {
227
+ execSync('npm audit fix', { cwd: path.resolve(target), encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
228
+ }
229
+ catch { /* npm audit fix may exit non-zero */ }
219
230
  fixSpinner.succeed('Applied available fixes (run scan again to verify)');
220
231
  }
221
232
  catch {
@@ -642,12 +642,48 @@ async function createDirectories(targetDir, options, result) {
642
642
  */
643
643
  async function writeSettings(targetDir, options, result) {
644
644
  const settingsPath = path.join(targetDir, '.claude', 'settings.json');
645
+ const generated = JSON.parse(generateSettingsJson(options));
645
646
  if (fs.existsSync(settingsPath) && !options.force) {
646
- result.skipped.push('.claude/settings.json');
647
+ // Merge hooks/env/permissions into existing settings instead of skipping
648
+ try {
649
+ const existing = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
650
+ let merged = false;
651
+ // Merge hooks (the critical missing piece — #1484)
652
+ if (generated.hooks && !existing.hooks) {
653
+ existing.hooks = generated.hooks;
654
+ merged = true;
655
+ }
656
+ // Merge env vars (for CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS etc.)
657
+ if (generated.env) {
658
+ existing.env = { ...(existing.env || {}), ...generated.env };
659
+ merged = true;
660
+ }
661
+ // Merge permissions (add ruflo allow rules)
662
+ if (generated.permissions?.allow) {
663
+ const existingAllow = existing.permissions?.allow || [];
664
+ const newRules = generated.permissions.allow.filter((r) => !existingAllow.includes(r));
665
+ if (newRules.length > 0) {
666
+ existing.permissions = existing.permissions || {};
667
+ existing.permissions.allow = [...existingAllow, ...newRules];
668
+ merged = true;
669
+ }
670
+ }
671
+ if (merged) {
672
+ fs.writeFileSync(settingsPath, JSON.stringify(existing, null, 2), 'utf-8');
673
+ result.created.files.push('.claude/settings.json (merged hooks)');
674
+ }
675
+ else {
676
+ result.skipped.push('.claude/settings.json');
677
+ }
678
+ }
679
+ catch {
680
+ // Existing file is corrupt — overwrite
681
+ fs.writeFileSync(settingsPath, JSON.stringify(generated, null, 2), 'utf-8');
682
+ result.created.files.push('.claude/settings.json');
683
+ }
647
684
  return;
648
685
  }
649
- const content = generateSettingsJson(options);
650
- fs.writeFileSync(settingsPath, content, 'utf-8');
686
+ fs.writeFileSync(settingsPath, JSON.stringify(generated, null, 2), 'utf-8');
651
687
  result.created.files.push('.claude/settings.json');
652
688
  }
653
689
  /**
@@ -1618,13 +1654,47 @@ async function writeClaudeMd(targetDir, options, result) {
1618
1654
  const claudeMdPath = path.join(targetDir, 'CLAUDE.md');
1619
1655
  if (fs.existsSync(claudeMdPath) && !options.force) {
1620
1656
  result.skipped.push('CLAUDE.md');
1621
- return;
1622
1657
  }
1623
- // Determine template: explicit option > infer from components > 'standard'
1624
- const inferredTemplate = (!options.components.commands && !options.components.agents) ? 'minimal' : undefined;
1625
- const content = generateClaudeMd(options, inferredTemplate);
1626
- fs.writeFileSync(claudeMdPath, content, 'utf-8');
1627
- result.created.files.push('CLAUDE.md');
1658
+ else {
1659
+ // Determine template: explicit option > infer from components > 'standard'
1660
+ const inferredTemplate = (!options.components.commands && !options.components.agents) ? 'minimal' : undefined;
1661
+ const content = generateClaudeMd(options, inferredTemplate);
1662
+ fs.writeFileSync(claudeMdPath, content, 'utf-8');
1663
+ result.created.files.push('CLAUDE.md');
1664
+ }
1665
+ // Also write/append global ~/.claude/CLAUDE.md so ruflo tools are used automatically (#1497)
1666
+ const homeDir = process.env.HOME || process.env.USERPROFILE || '';
1667
+ if (homeDir) {
1668
+ const globalClaudeDir = path.join(homeDir, '.claude');
1669
+ const globalClaudeMd = path.join(globalClaudeDir, 'CLAUDE.md');
1670
+ const rufloBlock = [
1671
+ '',
1672
+ '# Ruflo Integration (auto-generated by ruflo init)',
1673
+ 'When working on multi-file tasks or complex features, use ToolSearch to find and invoke ruflo MCP tools.',
1674
+ 'Key tools: memory_store, memory_search, hooks_route, swarm_init, agent_spawn.',
1675
+ 'Check system-reminder tags for [INTELLIGENCE] pattern suggestions before starting work.',
1676
+ '',
1677
+ ].join('\n');
1678
+ try {
1679
+ if (!fs.existsSync(globalClaudeDir)) {
1680
+ fs.mkdirSync(globalClaudeDir, { recursive: true });
1681
+ }
1682
+ if (fs.existsSync(globalClaudeMd)) {
1683
+ const existing = fs.readFileSync(globalClaudeMd, 'utf-8');
1684
+ if (!existing.includes('Ruflo Integration')) {
1685
+ fs.appendFileSync(globalClaudeMd, rufloBlock);
1686
+ result.created.files.push('~/.claude/CLAUDE.md (appended ruflo block)');
1687
+ }
1688
+ }
1689
+ else {
1690
+ fs.writeFileSync(globalClaudeMd, rufloBlock.trimStart(), 'utf-8');
1691
+ result.created.files.push('~/.claude/CLAUDE.md');
1692
+ }
1693
+ }
1694
+ catch {
1695
+ // Non-critical — global CLAUDE.md is best-effort
1696
+ }
1697
+ }
1628
1698
  }
1629
1699
  /**
1630
1700
  * Find source directory for skills/commands/agents
@@ -256,6 +256,7 @@ export const agentTools = [
256
256
  taskCount: agent.taskCount,
257
257
  createdAt: agent.createdAt,
258
258
  domain: agent.domain,
259
+ lastResult: agent.lastResult || null,
259
260
  };
260
261
  }
261
262
  return {
@@ -112,6 +112,7 @@ export const taskTools = [
112
112
  createdAt: task.createdAt,
113
113
  startedAt: task.startedAt,
114
114
  completedAt: task.completedAt,
115
+ result: task.result || null,
115
116
  };
116
117
  }
117
118
  return {
@@ -1,12 +1,7 @@
1
1
  /**
2
2
  * Terminal MCP Tools for CLI
3
3
  *
4
- * V2 Compatibility - Terminal session management tools
5
- *
6
- * ⚠️ IMPORTANT: These tools provide STATE MANAGEMENT only.
7
- * - terminal/execute does NOT actually execute commands
8
- * - Commands are recorded for tracking/coordination purposes
9
- * - For real command execution, use Claude Code's Bash tool
4
+ * Terminal session management with real command execution.
10
5
  */
11
6
  import type { MCPTool } from './types.js';
12
7
  export declare const terminalTools: MCPTool[];
@@ -1,15 +1,11 @@
1
1
  /**
2
2
  * Terminal MCP Tools for CLI
3
3
  *
4
- * V2 Compatibility - Terminal session management tools
5
- *
6
- * ⚠️ IMPORTANT: These tools provide STATE MANAGEMENT only.
7
- * - terminal/execute does NOT actually execute commands
8
- * - Commands are recorded for tracking/coordination purposes
9
- * - For real command execution, use Claude Code's Bash tool
4
+ * Terminal session management with real command execution.
10
5
  */
11
6
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
12
7
  import { join } from 'node:path';
8
+ import { execSync } from 'node:child_process';
13
9
  // Storage paths
14
10
  const STORAGE_DIR = '.claude-flow';
15
11
  const TERMINAL_DIR = 'terminals';
@@ -115,10 +111,27 @@ export const terminalTools = [
115
111
  };
116
112
  store.sessions[id] = session;
117
113
  }
118
- // NOTE: This is STATE TRACKING only - does not execute commands
119
- // For real execution, use Claude Code's Bash tool
120
- const output = `[STATE TRACKING] Command recorded: ${command}`;
121
- const exitCode = 0;
114
+ const timeout = input.timeout || 30_000;
115
+ const cwd = session.workingDir || process.cwd();
116
+ const startTime = Date.now();
117
+ let output;
118
+ let exitCode;
119
+ try {
120
+ output = execSync(command, {
121
+ cwd,
122
+ encoding: 'utf-8',
123
+ timeout,
124
+ maxBuffer: 5 * 1024 * 1024,
125
+ stdio: ['pipe', 'pipe', 'pipe'],
126
+ env: { ...process.env, ...session.env },
127
+ });
128
+ exitCode = 0;
129
+ }
130
+ catch (err) {
131
+ output = (err.stdout || '') + (err.stderr ? `\n[stderr] ${err.stderr}` : '');
132
+ exitCode = err.status ?? 1;
133
+ }
134
+ const duration = Date.now() - startTime;
122
135
  const timestamp = new Date().toISOString();
123
136
  // Record in history
124
137
  session.history.push({
@@ -131,13 +144,13 @@ export const terminalTools = [
131
144
  session.status = 'active';
132
145
  saveTerminalStore(store);
133
146
  return {
134
- success: true,
147
+ success: exitCode === 0,
135
148
  sessionId: session.id,
136
149
  command,
137
150
  output,
138
151
  exitCode,
139
152
  executedAt: timestamp,
140
- duration: Math.floor(Math.random() * 100) + 10,
153
+ duration,
141
154
  };
142
155
  },
143
156
  },
@@ -358,7 +358,8 @@ export async function bridgeSearchEntries(options) {
358
358
  if (!ctx)
359
359
  return null;
360
360
  try {
361
- const { query: queryStr, namespace = 'default', limit = 10, threshold = 0.3 } = options;
361
+ const { query: queryStr, namespace, limit = 10, threshold = 0.3 } = options;
362
+ const effectiveNamespace = namespace || 'all';
362
363
  const startTime = Date.now();
363
364
  // Generate query embedding
364
365
  let queryEmbedding = null;
@@ -373,7 +374,7 @@ export async function bridgeSearchEntries(options) {
373
374
  // Fall back to keyword search
374
375
  }
375
376
  // better-sqlite3: .prepare().all() returns array of objects
376
- const nsFilter = namespace !== 'all'
377
+ const nsFilter = effectiveNamespace !== 'all'
377
378
  ? `AND namespace = ?`
378
379
  : '';
379
380
  let rows;
@@ -384,7 +385,7 @@ export async function bridgeSearchEntries(options) {
384
385
  WHERE status = 'active' ${nsFilter}
385
386
  LIMIT 1000
386
387
  `);
387
- rows = namespace !== 'all' ? stmt.all(namespace) : stmt.all();
388
+ rows = effectiveNamespace !== 'all' ? stmt.all(effectiveNamespace) : stmt.all();
388
389
  }
389
390
  catch {
390
391
  return null;
@@ -356,14 +356,14 @@ export async function getHNSWIndex(options) {
356
356
  return null; // VectorDb not found
357
357
  }
358
358
  const { VectorDb } = ruvectorCore;
359
- // Persistent storage paths
360
- const swarmDir = path.join(process.cwd(), '.swarm');
359
+ // Persistent storage paths — resolve to absolute to survive CWD changes
360
+ const swarmDir = path.resolve(process.cwd(), '.swarm');
361
361
  if (!fs.existsSync(swarmDir)) {
362
362
  fs.mkdirSync(swarmDir, { recursive: true });
363
363
  }
364
364
  const hnswPath = path.join(swarmDir, 'hnsw.index');
365
365
  const metadataPath = path.join(swarmDir, 'hnsw.metadata.json');
366
- const dbPath = options?.dbPath || path.join(swarmDir, 'memory.db');
366
+ const dbPath = options?.dbPath ? path.resolve(options.dbPath) : path.join(swarmDir, 'memory.db');
367
367
  // Create HNSW index with persistent storage
368
368
  // @ruvector/core uses string enum for distanceMetric: 'Cosine', 'Euclidean', 'DotProduct', 'Manhattan'
369
369
  const db = new VectorDb({
@@ -1694,8 +1694,8 @@ export async function storeEntry(options) {
1694
1694
  }
1695
1695
  // Fallback: raw sql.js
1696
1696
  const { key, value, namespace = 'default', generateEmbeddingFlag = true, tags = [], ttl, dbPath: customPath, upsert = false } = options;
1697
- const swarmDir = path.join(process.cwd(), '.swarm');
1698
- const dbPath = customPath || path.join(swarmDir, 'memory.db');
1697
+ const swarmDir = path.resolve(process.cwd(), '.swarm');
1698
+ const dbPath = customPath ? path.resolve(customPath) : path.join(swarmDir, 'memory.db');
1699
1699
  try {
1700
1700
  if (!fs.existsSync(dbPath)) {
1701
1701
  return { success: false, id: '', error: 'Database not initialized. Run: claude-flow memory init' };
@@ -1785,9 +1785,10 @@ export async function searchEntries(options) {
1785
1785
  return bridgeResult;
1786
1786
  }
1787
1787
  // Fallback: raw sql.js
1788
- const { query, namespace = 'default', limit = 10, threshold = 0.3, dbPath: customPath } = options;
1789
- const swarmDir = path.join(process.cwd(), '.swarm');
1790
- const dbPath = customPath || path.join(swarmDir, 'memory.db');
1788
+ const { query, namespace, limit = 10, threshold = 0.3, dbPath: customPath } = options;
1789
+ const effectiveNamespace = namespace || 'all';
1790
+ const swarmDir = path.resolve(process.cwd(), '.swarm');
1791
+ const dbPath = customPath ? path.resolve(customPath) : path.join(swarmDir, 'memory.db');
1791
1792
  const startTime = Date.now();
1792
1793
  try {
1793
1794
  if (!fs.existsSync(dbPath)) {
@@ -1799,7 +1800,7 @@ export async function searchEntries(options) {
1799
1800
  const queryEmb = await generateEmbedding(query);
1800
1801
  const queryEmbedding = queryEmb.embedding;
1801
1802
  // Try HNSW search first (150x faster)
1802
- const hnswResults = await searchHNSWIndex(queryEmbedding, { k: limit, namespace });
1803
+ const hnswResults = await searchHNSWIndex(queryEmbedding, { k: limit, namespace: effectiveNamespace });
1803
1804
  if (hnswResults && hnswResults.length > 0) {
1804
1805
  // Filter by threshold
1805
1806
  const filtered = hnswResults.filter(r => r.score >= threshold);
@@ -1815,11 +1816,11 @@ export async function searchEntries(options) {
1815
1816
  const fileBuffer = fs.readFileSync(dbPath);
1816
1817
  const db = new SQL.Database(fileBuffer);
1817
1818
  // Get entries with embeddings
1818
- const searchStmt = db.prepare(namespace !== 'all'
1819
+ const searchStmt = db.prepare(effectiveNamespace !== 'all'
1819
1820
  ? `SELECT id, key, namespace, content, embedding FROM memory_entries WHERE status = 'active' AND namespace = ? LIMIT 1000`
1820
1821
  : `SELECT id, key, namespace, content, embedding FROM memory_entries WHERE status = 'active' LIMIT 1000`);
1821
- if (namespace !== 'all') {
1822
- searchStmt.bind([namespace]);
1822
+ if (effectiveNamespace !== 'all') {
1823
+ searchStmt.bind([effectiveNamespace]);
1823
1824
  }
1824
1825
  const searchRows = [];
1825
1826
  while (searchStmt.step()) {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-flow/cli",
3
- "version": "3.5.49",
3
+ "version": "3.5.51",
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",