claude-flow 3.7.0-alpha.13 → 3.7.0-alpha.15
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/.claude/settings.local.json +10 -1
- package/package.json +1 -1
- package/v3/@claude-flow/cli/dist/src/commands/hooks.js +17 -3
- package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.d.ts +3 -0
- package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.js +65 -10
- package/v3/@claude-flow/cli/dist/src/services/headless-worker-executor.js +23 -3
- package/v3/@claude-flow/cli/dist/src/services/worker-daemon.d.ts +9 -0
- package/v3/@claude-flow/cli/dist/src/services/worker-daemon.js +13 -0
- 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.7.0-alpha.
|
|
3
|
+
"version": "3.7.0-alpha.15",
|
|
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",
|
|
@@ -3302,7 +3302,13 @@ const statuslineCommand = {
|
|
|
3302
3302
|
},
|
|
3303
3303
|
{
|
|
3304
3304
|
name: 'compact',
|
|
3305
|
-
description: 'Compact single-line output',
|
|
3305
|
+
description: 'Compact single-line output (auto-enabled when terminal width < 100 cols)',
|
|
3306
|
+
type: 'boolean',
|
|
3307
|
+
default: false
|
|
3308
|
+
},
|
|
3309
|
+
{
|
|
3310
|
+
name: 'full',
|
|
3311
|
+
description: 'Force the full multi-line output even on narrow terminals',
|
|
3306
3312
|
type: 'boolean',
|
|
3307
3313
|
default: false
|
|
3308
3314
|
},
|
|
@@ -3531,8 +3537,16 @@ const statuslineCommand = {
|
|
|
3531
3537
|
output.printJson(statusData);
|
|
3532
3538
|
return { success: true, data: statusData };
|
|
3533
3539
|
}
|
|
3534
|
-
//
|
|
3535
|
-
|
|
3540
|
+
// #1153: auto-collapse to compact on narrow terminals so the full
|
|
3541
|
+
// 6+ line statusline doesn't dominate the screen. Honors:
|
|
3542
|
+
// - explicit --compact → compact
|
|
3543
|
+
// - explicit --full → full (overrides auto-detection)
|
|
3544
|
+
// - else → compact when terminal < 100 cols (full multi-line
|
|
3545
|
+
// output expects ~100 cols of horizontal space)
|
|
3546
|
+
const COMPACT_WIDTH_THRESHOLD = 100;
|
|
3547
|
+
const terminalCols = process.stdout.columns ?? 80;
|
|
3548
|
+
const autoCompact = !ctx.flags.full && terminalCols < COMPACT_WIDTH_THRESHOLD;
|
|
3549
|
+
if (ctx.flags.compact || autoCompact) {
|
|
3536
3550
|
const line = `DDD:${progress.domainsCompleted}/${progress.totalDomains} CVE:${security.cvesFixed}/${security.totalCves} Swarm:${swarm.activeAgents}/${swarm.maxAgents} Ctx:${system.contextPct}% Int:${system.intelligencePct}%`;
|
|
3537
3551
|
output.writeln(line);
|
|
3538
3552
|
return { success: true, data: statusData };
|
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @module v3/cli/memory-initializer
|
|
10
10
|
*/
|
|
11
|
+
export declare function getMemoryRoot(): string;
|
|
12
|
+
/** For tests + the `memory configure` flow that mutates the config at runtime. */
|
|
13
|
+
export declare function _resetMemoryRootCache(): void;
|
|
11
14
|
/**
|
|
12
15
|
* Enhanced schema with pattern confidence, temporal decay, versioning
|
|
13
16
|
* Vector embeddings enabled for semantic search
|
|
@@ -11,6 +11,61 @@
|
|
|
11
11
|
import * as fs from 'fs';
|
|
12
12
|
import * as path from 'path';
|
|
13
13
|
import { readFileMaybeEncrypted, writeFileRestricted } from '../fs-secure.js';
|
|
14
|
+
/**
|
|
15
|
+
* #1854: previously every site that needed the memory directory hardcoded
|
|
16
|
+
* `getMemoryRoot()`, so the documented config entry
|
|
17
|
+
* points (`memory.persistPath` config field, `memory configure --path`,
|
|
18
|
+
* `CLAUDE_FLOW_MEMORY_PATH` env var) all silently no-op'd. This helper
|
|
19
|
+
* is the single source of truth — every `.swarm/memory.db` resolution in
|
|
20
|
+
* this file flows through it.
|
|
21
|
+
*
|
|
22
|
+
* Precedence (highest → lowest):
|
|
23
|
+
* 1. CLAUDE_FLOW_MEMORY_PATH env var
|
|
24
|
+
* 2. memory.persistPath / memory.path in claude-flow.config.json (cwd or
|
|
25
|
+
* the directory the CLI was invoked from)
|
|
26
|
+
* 3. Default: cwd/.swarm
|
|
27
|
+
*
|
|
28
|
+
* Cached per-process so repeated lookups are cheap; reset only by spawning
|
|
29
|
+
* a fresh process (which is how config changes already propagate).
|
|
30
|
+
*/
|
|
31
|
+
let _memoryRootCache;
|
|
32
|
+
export function getMemoryRoot() {
|
|
33
|
+
if (_memoryRootCache !== undefined)
|
|
34
|
+
return _memoryRootCache;
|
|
35
|
+
// 1. Env var
|
|
36
|
+
const envPath = process.env.CLAUDE_FLOW_MEMORY_PATH;
|
|
37
|
+
if (envPath && envPath.trim().length > 0) {
|
|
38
|
+
_memoryRootCache = path.resolve(envPath);
|
|
39
|
+
return _memoryRootCache;
|
|
40
|
+
}
|
|
41
|
+
// 2. Config file (claude-flow.config.json)
|
|
42
|
+
const configCandidates = [
|
|
43
|
+
path.resolve(process.cwd(), 'claude-flow.config.json'),
|
|
44
|
+
path.resolve(process.cwd(), '.claude-flow', 'config.json'),
|
|
45
|
+
];
|
|
46
|
+
for (const configPath of configCandidates) {
|
|
47
|
+
if (!fs.existsSync(configPath))
|
|
48
|
+
continue;
|
|
49
|
+
try {
|
|
50
|
+
const raw = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
51
|
+
const fromConfig = raw?.memory?.persistPath ?? raw?.memory?.path;
|
|
52
|
+
if (typeof fromConfig === 'string' && fromConfig.trim().length > 0) {
|
|
53
|
+
_memoryRootCache = path.resolve(fromConfig);
|
|
54
|
+
return _memoryRootCache;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
/* malformed config — fall through to default */
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// 3. Default
|
|
62
|
+
_memoryRootCache = path.resolve(process.cwd(), '.swarm');
|
|
63
|
+
return _memoryRootCache;
|
|
64
|
+
}
|
|
65
|
+
/** For tests + the `memory configure` flow that mutates the config at runtime. */
|
|
66
|
+
export function _resetMemoryRootCache() {
|
|
67
|
+
_memoryRootCache = undefined;
|
|
68
|
+
}
|
|
14
69
|
// ADR-053: Lazy import of AgentDB v3 bridge
|
|
15
70
|
let _bridge;
|
|
16
71
|
async function getBridge() {
|
|
@@ -358,7 +413,7 @@ export async function getHNSWIndex(options) {
|
|
|
358
413
|
}
|
|
359
414
|
const { VectorDb } = ruvectorCore;
|
|
360
415
|
// Persistent storage paths — resolve to absolute to survive CWD changes
|
|
361
|
-
const swarmDir =
|
|
416
|
+
const swarmDir = getMemoryRoot();
|
|
362
417
|
if (!fs.existsSync(swarmDir)) {
|
|
363
418
|
fs.mkdirSync(swarmDir, { recursive: true });
|
|
364
419
|
}
|
|
@@ -459,7 +514,7 @@ function saveHNSWMetadata() {
|
|
|
459
514
|
if (!hnswIndex?.entries)
|
|
460
515
|
return;
|
|
461
516
|
try {
|
|
462
|
-
const swarmDir =
|
|
517
|
+
const swarmDir = getMemoryRoot();
|
|
463
518
|
const metadataPath = path.join(swarmDir, 'hnsw.metadata.json');
|
|
464
519
|
const metadata = Array.from(hnswIndex.entries.entries());
|
|
465
520
|
fs.writeFileSync(metadataPath, JSON.stringify(metadata));
|
|
@@ -974,7 +1029,7 @@ async function activateControllerRegistry(dbPath, verbose) {
|
|
|
974
1029
|
*/
|
|
975
1030
|
export async function initializeMemoryDatabase(options) {
|
|
976
1031
|
const { backend = 'hybrid', dbPath: customPath, force = false, verbose = false, migrate = true } = options;
|
|
977
|
-
const swarmDir =
|
|
1032
|
+
const swarmDir = getMemoryRoot();
|
|
978
1033
|
const dbPath = customPath || path.join(swarmDir, 'memory.db');
|
|
979
1034
|
const dbDir = path.dirname(dbPath);
|
|
980
1035
|
try {
|
|
@@ -1162,7 +1217,7 @@ export async function initializeMemoryDatabase(options) {
|
|
|
1162
1217
|
* Check if memory database is properly initialized
|
|
1163
1218
|
*/
|
|
1164
1219
|
export async function checkMemoryInitialization(dbPath) {
|
|
1165
|
-
const swarmDir =
|
|
1220
|
+
const swarmDir = getMemoryRoot();
|
|
1166
1221
|
const path_ = dbPath || path.join(swarmDir, 'memory.db');
|
|
1167
1222
|
if (!fs.existsSync(path_)) {
|
|
1168
1223
|
return { initialized: false };
|
|
@@ -1211,7 +1266,7 @@ export async function checkMemoryInitialization(dbPath) {
|
|
|
1211
1266
|
* Reduces confidence of patterns that haven't been used recently
|
|
1212
1267
|
*/
|
|
1213
1268
|
export async function applyTemporalDecay(dbPath) {
|
|
1214
|
-
const swarmDir =
|
|
1269
|
+
const swarmDir = getMemoryRoot();
|
|
1215
1270
|
const path_ = dbPath || path.join(swarmDir, 'memory.db');
|
|
1216
1271
|
try {
|
|
1217
1272
|
const initSqlJs = (await import('sql.js')).default;
|
|
@@ -1737,7 +1792,7 @@ export async function storeEntry(options) {
|
|
|
1737
1792
|
}
|
|
1738
1793
|
// Fallback: raw sql.js
|
|
1739
1794
|
const { key, value, namespace = 'default', generateEmbeddingFlag = true, tags = [], ttl, dbPath: customPath, upsert = false } = options;
|
|
1740
|
-
const swarmDir =
|
|
1795
|
+
const swarmDir = getMemoryRoot();
|
|
1741
1796
|
const dbPath = customPath ? path.resolve(customPath) : path.join(swarmDir, 'memory.db');
|
|
1742
1797
|
try {
|
|
1743
1798
|
if (!fs.existsSync(dbPath)) {
|
|
@@ -1830,7 +1885,7 @@ export async function searchEntries(options) {
|
|
|
1830
1885
|
// Fallback: raw sql.js
|
|
1831
1886
|
const { query, namespace, limit = 10, threshold = 0.3, dbPath: customPath } = options;
|
|
1832
1887
|
const effectiveNamespace = namespace || 'all';
|
|
1833
|
-
const swarmDir =
|
|
1888
|
+
const swarmDir = getMemoryRoot();
|
|
1834
1889
|
const dbPath = customPath ? path.resolve(customPath) : path.join(swarmDir, 'memory.db');
|
|
1835
1890
|
const startTime = Date.now();
|
|
1836
1891
|
try {
|
|
@@ -2001,7 +2056,7 @@ export async function listEntries(options) {
|
|
|
2001
2056
|
}
|
|
2002
2057
|
// Fallback: raw sql.js
|
|
2003
2058
|
const { namespace, limit = 20, offset = 0, dbPath: customPath } = options;
|
|
2004
|
-
const swarmDir =
|
|
2059
|
+
const swarmDir = getMemoryRoot();
|
|
2005
2060
|
const dbPath = customPath || path.join(swarmDir, 'memory.db');
|
|
2006
2061
|
try {
|
|
2007
2062
|
if (!fs.existsSync(dbPath)) {
|
|
@@ -2086,7 +2141,7 @@ export async function getEntry(options) {
|
|
|
2086
2141
|
}
|
|
2087
2142
|
// Fallback: raw sql.js
|
|
2088
2143
|
const { key, namespace = 'default', dbPath: customPath } = options;
|
|
2089
|
-
const swarmDir =
|
|
2144
|
+
const swarmDir = getMemoryRoot();
|
|
2090
2145
|
const dbPath = customPath || path.join(swarmDir, 'memory.db');
|
|
2091
2146
|
try {
|
|
2092
2147
|
if (!fs.existsSync(dbPath)) {
|
|
@@ -2190,7 +2245,7 @@ export async function deleteEntry(options) {
|
|
|
2190
2245
|
}
|
|
2191
2246
|
// Fallback: raw sql.js
|
|
2192
2247
|
const { key, namespace = 'default', dbPath: customPath } = options;
|
|
2193
|
-
const swarmDir =
|
|
2248
|
+
const swarmDir = getMemoryRoot();
|
|
2194
2249
|
const dbPath = customPath || path.join(swarmDir, 'memory.db');
|
|
2195
2250
|
try {
|
|
2196
2251
|
if (!fs.existsSync(dbPath)) {
|
|
@@ -831,13 +831,33 @@ Analyze the above codebase context and provide your response following the forma
|
|
|
831
831
|
// Set model
|
|
832
832
|
// Resolve model: user env override > config override > default alias
|
|
833
833
|
env.ANTHROPIC_MODEL = process.env.ANTHROPIC_MODEL || MODEL_IDS[options.model];
|
|
834
|
-
// Spawn claude CLI process
|
|
835
|
-
|
|
834
|
+
// Spawn claude CLI process. #1852: previously the prompt was passed
|
|
835
|
+
// as a positional CLI arg. On Windows `claude` resolves to
|
|
836
|
+
// `claude.cmd`, which Node refuses to exec directly (CVE-2024-27980
|
|
837
|
+
// mitigation) — it routes through `cmd.exe /d /s /c`, which then
|
|
838
|
+
// re-tokenizes the entire command line including the prompt.
|
|
839
|
+
// Source-code prompts contain `>` `<` `&` `|` (arrow functions,
|
|
840
|
+
// comparisons, redirections) — cmd.exe parses those as redirects
|
|
841
|
+
// and creates zero-byte files in cwd named after the next token
|
|
842
|
+
// (`controller.abort()`, `{const`, `0`, `HTTP`, etc.).
|
|
843
|
+
//
|
|
844
|
+
// Fix: pipe the prompt via stdin instead. `child.stdin.end(prompt)`
|
|
845
|
+
// writes the prompt and closes stdin atomically — the EOF still
|
|
846
|
+
// unblocks `claude --print` (the original concern in #1395) but no
|
|
847
|
+
// shell tokenization touches the prompt.
|
|
848
|
+
const child = spawn('claude', ['--print'], {
|
|
836
849
|
cwd: this.projectRoot,
|
|
837
850
|
env,
|
|
838
|
-
stdio: ['
|
|
851
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
839
852
|
windowsHide: true, // Prevent phantom console windows on Windows
|
|
840
853
|
});
|
|
854
|
+
try {
|
|
855
|
+
child.stdin?.end(prompt);
|
|
856
|
+
}
|
|
857
|
+
catch {
|
|
858
|
+
// stdin already closed (e.g. spawn failed) — `error` handler below
|
|
859
|
+
// will surface the real cause.
|
|
860
|
+
}
|
|
841
861
|
// Setup timeout
|
|
842
862
|
const timeoutHandle = setTimeout(() => {
|
|
843
863
|
if (this.processPool.has(options.executionId)) {
|
|
@@ -128,6 +128,15 @@ export declare class WorkerDaemon extends EventEmitter {
|
|
|
128
128
|
/**
|
|
129
129
|
* Check if another daemon instance is already running.
|
|
130
130
|
* Returns the existing PID if alive, or null if no daemon is running.
|
|
131
|
+
*
|
|
132
|
+
* #1853: ignore self-PID matches. The detached-spawn path in
|
|
133
|
+
* `commands/daemon.ts` writes the child's PID into the file as a
|
|
134
|
+
* fallback after a 500ms wait. If the child reaches `start()` slower
|
|
135
|
+
* than the parent's 500ms wait (observed on Node 25 / macOS 26), the
|
|
136
|
+
* child reads its own PID back from the file and concludes "another
|
|
137
|
+
* daemon is already running" — so it exits before scheduling workers
|
|
138
|
+
* and `daemon status` reports STOPPED forever. A daemon process is
|
|
139
|
+
* never "another instance" of itself; treat self-match as absence.
|
|
131
140
|
*/
|
|
132
141
|
private checkExistingDaemon;
|
|
133
142
|
/**
|
|
@@ -370,6 +370,15 @@ export class WorkerDaemon extends EventEmitter {
|
|
|
370
370
|
/**
|
|
371
371
|
* Check if another daemon instance is already running.
|
|
372
372
|
* Returns the existing PID if alive, or null if no daemon is running.
|
|
373
|
+
*
|
|
374
|
+
* #1853: ignore self-PID matches. The detached-spawn path in
|
|
375
|
+
* `commands/daemon.ts` writes the child's PID into the file as a
|
|
376
|
+
* fallback after a 500ms wait. If the child reaches `start()` slower
|
|
377
|
+
* than the parent's 500ms wait (observed on Node 25 / macOS 26), the
|
|
378
|
+
* child reads its own PID back from the file and concludes "another
|
|
379
|
+
* daemon is already running" — so it exits before scheduling workers
|
|
380
|
+
* and `daemon status` reports STOPPED forever. A daemon process is
|
|
381
|
+
* never "another instance" of itself; treat self-match as absence.
|
|
373
382
|
*/
|
|
374
383
|
checkExistingDaemon() {
|
|
375
384
|
if (!existsSync(this.pidFile))
|
|
@@ -378,6 +387,10 @@ export class WorkerDaemon extends EventEmitter {
|
|
|
378
387
|
const pid = parseInt(readFileSync(this.pidFile, 'utf-8').trim(), 10);
|
|
379
388
|
if (isNaN(pid))
|
|
380
389
|
return null;
|
|
390
|
+
// #1853: a PID file containing our own PID is not "another daemon".
|
|
391
|
+
// Treat as absent so the start() path proceeds normally.
|
|
392
|
+
if (pid === process.pid)
|
|
393
|
+
return null;
|
|
381
394
|
// Check if process is alive (signal 0 = existence check)
|
|
382
395
|
process.kill(pid, 0);
|
|
383
396
|
return pid; // Process is alive
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-flow/cli",
|
|
3
|
-
"version": "3.7.0-alpha.
|
|
3
|
+
"version": "3.7.0-alpha.15",
|
|
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",
|