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 +1 -1
- package/v3/@claude-flow/cli/dist/src/commands/security.js +17 -6
- package/v3/@claude-flow/cli/dist/src/init/executor.js +79 -9
- package/v3/@claude-flow/cli/dist/src/mcp-tools/agent-tools.js +1 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/task-tools.js +1 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/terminal-tools.d.ts +1 -6
- package/v3/@claude-flow/cli/dist/src/mcp-tools/terminal-tools.js +25 -12
- package/v3/@claude-flow/cli/dist/src/memory/memory-bridge.js +4 -3
- package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.js +13 -12
- package/v3/@claude-flow/cli/package.json +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-flow",
|
|
3
|
-
"version": "3.5.
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
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
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Terminal MCP Tools for CLI
|
|
3
3
|
*
|
|
4
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
|
|
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:
|
|
147
|
+
success: exitCode === 0,
|
|
135
148
|
sessionId: session.id,
|
|
136
149
|
command,
|
|
137
150
|
output,
|
|
138
151
|
exitCode,
|
|
139
152
|
executedAt: timestamp,
|
|
140
|
-
duration
|
|
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
|
|
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 =
|
|
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 =
|
|
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.
|
|
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
|
|
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.
|
|
1698
|
-
const dbPath = customPath
|
|
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
|
|
1789
|
-
const
|
|
1790
|
-
const
|
|
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(
|
|
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 (
|
|
1822
|
-
searchStmt.bind([
|
|
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.
|
|
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",
|