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.
@@ -1 +1,10 @@
1
- {}
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(mkdir -p /tmp/ruflo-smoke)",
5
+ "Bash(rm -rf /tmp/ruflo-smoke/*)",
6
+ "Read(//tmp/**)",
7
+ "Bash(npm pack *)"
8
+ ]
9
+ }
10
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-flow",
3
- "version": "3.7.0-alpha.13",
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
- // Compact output
3535
- if (ctx.flags.compact) {
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 = path.resolve(process.cwd(), '.swarm');
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 = path.join(process.cwd(), '.swarm');
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 = path.join(process.cwd(), '.swarm');
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 = path.join(process.cwd(), '.swarm');
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 = path.join(process.cwd(), '.swarm');
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 = path.resolve(process.cwd(), '.swarm');
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 = path.resolve(process.cwd(), '.swarm');
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 = path.join(process.cwd(), '.swarm');
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 = path.join(process.cwd(), '.swarm');
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 = path.join(process.cwd(), '.swarm');
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
- const child = spawn('claude', ['--print', prompt], {
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: ['ignore', 'pipe', 'pipe'], // 'ignore' closes stdin at spawn — fixes #1395 where claude --print blocks on EOF
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.13",
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",