claude-flow 3.7.0-alpha.35 → 3.7.0-alpha.36

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/README.md CHANGED
@@ -156,11 +156,17 @@ This adds slash commands and agent definitions only. The Ruflo MCP server is NOT
156
156
 
157
157
  ### CLI Install
158
158
 
159
+ **macOS / Linux / WSL / Git-Bash:**
160
+
159
161
  ```bash
160
- # One-line install
162
+ # One-line install (POSIX shells only — see Windows note below)
161
163
  curl -fsSL https://cdn.jsdelivr.net/gh/ruvnet/ruflo@main/scripts/install.sh | bash
164
+ ```
165
+
166
+ **All platforms (including native Windows PowerShell / cmd):**
162
167
 
163
- # Or via npx (interactive setup)
168
+ ```bash
169
+ # Interactive setup wizard — runs identically on every platform
164
170
  npx ruflo@latest init wizard
165
171
 
166
172
  # Quick non-interactive init
@@ -170,6 +176,8 @@ npx ruflo@latest init wizard
170
176
  npm install -g ruflo@latest
171
177
  ```
172
178
 
179
+ > 💡 **Windows users:** the `curl ... | bash` form needs a POSIX shell (Git-Bash, WSL, MSYS). The `npx ruflo@latest init wizard` line works natively in PowerShell and cmd. If you hit an `'bash' is not recognized` error, use the `npx` line instead — both end up running the same init flow.
180
+
173
181
  ### MCP Server
174
182
 
175
183
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-flow",
3
- "version": "3.7.0-alpha.35",
3
+ "version": "3.7.0-alpha.36",
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",
@@ -89,7 +89,10 @@
89
89
  "make-fetch-happen": ">=15.0.0",
90
90
  "express-rate-limit": ">=8.4.1",
91
91
  "protobufjs": ">=7.5.5",
92
- "uuid": ">=14.0.0"
92
+ "uuid": ">=14.0.0",
93
+ "@opentelemetry/core": "1.25.1",
94
+ "@opentelemetry/resources": "1.25.1",
95
+ "@opentelemetry/sdk-trace-base": "1.25.1"
93
96
  },
94
97
  "devDependencies": {
95
98
  "@openai/codex": "^0.98.0",
@@ -152,4 +155,4 @@
152
155
  "access": "public",
153
156
  "tag": "latest"
154
157
  }
155
- }
158
+ }
@@ -156,11 +156,17 @@ This adds slash commands and agent definitions only. The Ruflo MCP server is NOT
156
156
 
157
157
  ### CLI Install
158
158
 
159
+ **macOS / Linux / WSL / Git-Bash:**
160
+
159
161
  ```bash
160
- # One-line install
162
+ # One-line install (POSIX shells only — see Windows note below)
161
163
  curl -fsSL https://cdn.jsdelivr.net/gh/ruvnet/ruflo@main/scripts/install.sh | bash
164
+ ```
165
+
166
+ **All platforms (including native Windows PowerShell / cmd):**
162
167
 
163
- # Or via npx (interactive setup)
168
+ ```bash
169
+ # Interactive setup wizard — runs identically on every platform
164
170
  npx ruflo@latest init wizard
165
171
 
166
172
  # Quick non-interactive init
@@ -170,6 +176,8 @@ npx ruflo@latest init wizard
170
176
  npm install -g ruflo@latest
171
177
  ```
172
178
 
179
+ > 💡 **Windows users:** the `curl ... | bash` form needs a POSIX shell (Git-Bash, WSL, MSYS). The `npx ruflo@latest init wizard` line works natively in PowerShell and cmd. If you hit an `'bash' is not recognized` error, use the `npx` line instead — both end up running the same init flow.
180
+
173
181
  ### MCP Server
174
182
 
175
183
  ```bash
@@ -694,11 +694,25 @@ const statsCommand = {
694
694
  },
695
695
  {
696
696
  metric: 'HNSW Index',
697
- value: hnsw.available && hnsw.initialized
698
- ? output.success(`active (${hnsw.entryCount.toLocaleString()} entries)`)
699
- : hnsw.available
700
- ? output.warning('available but not initialized')
701
- : output.dim('not active'),
697
+ // ruflo#1989 / #1987: `hnsw.entryCount` is in-process JS state
698
+ // (the live HNSW index of the current Node process). A fresh
699
+ // `memory stats` invocation has never indexed anything, so it
700
+ // reports 0 even when the persistent DB has thousands of
701
+ // entries with embeddings. Use the persistent count from the
702
+ // MCP tool (`entriesWithEmbeddings`, which is the actual
703
+ // count of rows that have a vector) as the source of truth.
704
+ value: (() => {
705
+ const persisted = typeof statsResult.entriesWithEmbeddings === 'number'
706
+ ? statsResult.entriesWithEmbeddings
707
+ : null;
708
+ const live = hnsw.entryCount || 0;
709
+ const total = persisted !== null ? Math.max(persisted, live) : live;
710
+ if (!hnsw.available)
711
+ return output.dim('not active');
712
+ if (total === 0)
713
+ return output.warning('available but not initialized');
714
+ return output.success(`active (${total.toLocaleString()} entries)`);
715
+ })(),
702
716
  },
703
717
  ]
704
718
  });
@@ -200,13 +200,45 @@ function generateStatusLineConfig(_options) {
200
200
  // Claude Code pipes JSON session data to the script via stdin.
201
201
  // Valid fields: type, command, padding (optional).
202
202
  // The script runs after each assistant message (debounced 300ms).
203
- // NOTE: statusline must NOT use `cmd /c` — Claude Code manages its stdin
204
- // directly for statusline commands, and `cmd /c` blocks stdin forwarding.
205
203
  //
206
- // Same project-local / $HOME fallback as `hookCmd()` (see #1943): the
207
- // earlier `${CLAUDE_PROJECT_DIR:-.}` form broke statusline for any
208
- // global-install user. Probe project-local first, fall back to $HOME.
204
+ // ruflo#1948 + #1973: the previous `sh -c 'D="${CLAUDE_PROJECT_DIR:-.}"; …'`
205
+ // form requires a POSIX shell on PATH. On native Windows (no
206
+ // Git-Bash / WSL), `sh` either isn't found or its quoting gets
207
+ // mangled, producing weird artifacts like files named `0)` or
208
+ // `toastr.error('ESD...` from misparsed tokens leaking back into
209
+ // the file system. NEVER use `cmd /c` for statusline — Claude Code
210
+ // manages stdin directly for statusline commands and `cmd /c`
211
+ // blocks the stdin forwarding.
212
+ //
213
+ // Solution: emit a platform-appropriate command at init time.
214
+ // POSIX: `sh -c 'D="…"; … exec node "$D/<script>"'` (existing)
215
+ // Windows: a Node.js one-liner that resolves the path internally
216
+ // using `process.env.CLAUDE_PROJECT_DIR` with a HOME
217
+ // fallback — no shell-quoting hazards because the
218
+ // resolution happens inside node, not in the shell.
209
219
  const script = '.claude/helpers/statusline.cjs';
220
+ if (process.platform === 'win32') {
221
+ // The Node CLI's `-e` flag avoids all shell-quoting pitfalls.
222
+ // We write the path resolution in JS:
223
+ // const fs = require('fs'); const p = require('path');
224
+ // const d = process.env.CLAUDE_PROJECT_DIR || '.';
225
+ // const f = p.join(d, '.claude/helpers/statusline.cjs');
226
+ // const home = process.env.USERPROFILE || process.env.HOME || '.';
227
+ // const h = p.join(home, '.claude/helpers/statusline.cjs');
228
+ // require(fs.existsSync(f) ? f : h);
229
+ // …compressed onto one line. Double-quotes around the -e arg are
230
+ // safe on cmd.exe; the inner JS uses single-quotes for strings.
231
+ const js = "const fs=require('fs'),p=require('path');" +
232
+ `const d=process.env.CLAUDE_PROJECT_DIR||'.';` +
233
+ `const f=p.join(d,'${script}');` +
234
+ `const h=p.join(process.env.USERPROFILE||process.env.HOME||'.', '${script}');` +
235
+ 'require(fs.existsSync(f)?f:h);';
236
+ return {
237
+ type: 'command',
238
+ command: `node -e "${js}"`,
239
+ };
240
+ }
241
+ // Same project-local / $HOME fallback as `hookCmd()` (see #1943).
210
242
  // eslint-disable-next-line no-template-curly-in-string
211
243
  const projVar = '${CLAUDE_PROJECT_DIR:-.}';
212
244
  // eslint-disable-next-line no-template-curly-in-string
@@ -225,8 +225,19 @@ function getLearningStats() {
225
225
  } catch { /* ignore */ }
226
226
  }
227
227
 
228
- // 3. Count patterns from memory.db using row count (sqlite header bytes 28-31)
228
+ // 3. Count patterns from memory.db using row count (sqlite header bytes 28-31).
229
+ //
230
+ // ruflo#1989: when encryption at rest is enabled, memory.db is no
231
+ // longer a SQLite database -- it is an RFE1-magicked ciphertext blob.
232
+ // The original code blindly read bytes 28-31 as a page count and
233
+ // rendered 3.3B patterns (uint32 of random ciphertext). That
234
+ // cascaded into fake DDD 5/5 / 100% indicators downstream.
235
+ //
236
+ // Guard with the SQLite magic ("SQLite format 3\\0", 16 bytes at
237
+ // offset 0). Also clamp implausible page counts (>1M pages ~= 4GB)
238
+ // to avoid reporting nonsense even on plaintext SQLite.
229
239
  if (patterns === 0) {
240
+ const SQLITE_MAGIC = Buffer.from('SQLite format 3\\0', 'binary');
230
241
  const memoryPaths = [
231
242
  path.join(CWD, '.claude-flow', 'memory.db'),
232
243
  path.join(CWD, 'data', 'memory.db'),
@@ -234,18 +245,27 @@ function getLearningStats() {
234
245
  ];
235
246
  for (const dbPath of memoryPaths) {
236
247
  try {
237
- if (fs.existsSync(dbPath)) {
238
- // Read SQLite header: page count at offset 28 (4 bytes big-endian)
239
- const fd = fs.openSync(dbPath, 'r');
240
- const buf = Buffer.alloc(4);
241
- fs.readSync(fd, buf, 0, 4, 28);
248
+ if (!fs.existsSync(dbPath)) continue;
249
+ const fd = fs.openSync(dbPath, 'r');
250
+ const head = Buffer.alloc(16);
251
+ fs.readSync(fd, head, 0, 16, 0);
252
+ if (!head.equals(SQLITE_MAGIC)) {
253
+ // Not plaintext SQLite (likely RFE1 encrypted, an empty
254
+ // file, or some other format). Skip — let the daemon or
255
+ // patterns.json fallback report the real number.
242
256
  fs.closeSync(fd);
243
- const pageCount = buf.readUInt32BE(0);
244
- // Each page typically holds ~10-50 rows; use page count as conservative estimate
245
- // But report 0 if DB exists but has only schema pages (< 3)
246
- patterns = pageCount > 2 ? pageCount - 2 : 0;
247
- break;
257
+ continue;
248
258
  }
259
+ const buf = Buffer.alloc(4);
260
+ fs.readSync(fd, buf, 0, 4, 28);
261
+ fs.closeSync(fd);
262
+ const pageCount = buf.readUInt32BE(0);
263
+ // Sanity: reject implausible counts (> 1M pages ≈ 4 GB DB).
264
+ if (pageCount > 1_000_000) continue;
265
+ // Each page typically holds ~10-50 rows; use page count as
266
+ // conservative estimate. Report 0 if only schema pages (< 3).
267
+ patterns = pageCount > 2 ? pageCount - 2 : 0;
268
+ break;
249
269
  } catch { /* ignore */ }
250
270
  }
251
271
  }
@@ -281,17 +301,24 @@ function getV3Progress() {
281
301
  let domainsCompleted = Math.min(5, Math.floor(dddProgress / 20));
282
302
 
283
303
  // Only derive DDD progress from real ddd-progress.json or real pattern data
284
- // Don't inflate domains from pattern count — 0 means no DDD work tracked
285
- if (dddProgress === 0 && learning.patterns > 0) {
304
+ // Don't inflate domains from pattern count — 0 means no DDD work tracked.
305
+ // ruflo#1989: defensively clamp learning.patterns even though
306
+ // getLearningStats already guards against the RFE1-encrypted case --
307
+ // if any future regression in the upstream reader returns a wild
308
+ // value, we do not want to silently inflate DDD to 5/5 / 100%.
309
+ const realPatterns = Number.isFinite(learning.patterns) && learning.patterns >= 0 && learning.patterns < 1_000_000
310
+ ? learning.patterns
311
+ : 0;
312
+ if (dddProgress === 0 && realPatterns > 0) {
286
313
  // Conservative: only count domains if we have substantial real pattern data
287
314
  // Each domain requires ~100 real stored patterns to claim completion
288
- domainsCompleted = Math.min(5, Math.floor(learning.patterns / 100));
315
+ domainsCompleted = Math.min(5, Math.floor(realPatterns / 100));
289
316
  dddProgress = Math.floor((domainsCompleted / totalDomains) * 100);
290
317
  }
291
318
 
292
319
  return {
293
320
  domainsCompleted, totalDomains, dddProgress,
294
- patternsLearned: learning.patterns,
321
+ patternsLearned: realPatterns,
295
322
  sessionsCompleted: learning.sessions,
296
323
  };
297
324
  }
@@ -377,8 +404,14 @@ function getSystemMetrics() {
377
404
  if (learningData && learningData.intelligence && learningData.intelligence.score !== undefined) {
378
405
  intelligencePct = Math.min(100, Math.floor(learningData.intelligence.score));
379
406
  } else {
380
- // Use real data only — patterns from actual store, vectors from actual DB
381
- const fromPatterns = learning.patterns > 0 ? Math.min(100, Math.floor(learning.patterns / 20)) : 0;
407
+ // Use real data only — patterns from actual store, vectors from actual DB.
408
+ // ruflo#1989: clamp patterns to a sane upper bound. A multi-billion
409
+ // pattern count from a buggy reader would saturate intelligencePct
410
+ // to 100% and silently lie about progress.
411
+ const realPatterns = Number.isFinite(learning.patterns) && learning.patterns >= 0 && learning.patterns < 1_000_000
412
+ ? learning.patterns
413
+ : 0;
414
+ const fromPatterns = realPatterns > 0 ? Math.min(100, Math.floor(realPatterns / 20)) : 0;
382
415
  const fromVectors = agentdb.vectorCount > 0 ? Math.min(100, Math.floor(agentdb.vectorCount / 20)) : 0;
383
416
  intelligencePct = Math.max(fromPatterns, fromVectors);
384
417
  }
@@ -235,6 +235,39 @@ export class MCPServerManager extends EventEmitter {
235
235
  * Handles stdin/stdout directly like V2 implementation
236
236
  */
237
237
  async startStdioServer() {
238
+ // ruflo#1910 — protect the JSON-RPC stdout from any stray
239
+ // console.log/info/debug emitted by lazily-loaded modules
240
+ // (@ruvector/router, @claude-flow/neural, transformers.js, ONNX,
241
+ // semantic-router init, etc.). Codex closes the MCP transport
242
+ // the moment it sees a non-JSON line on stdout, and one such
243
+ // line during a tool batch bricked the whole session.
244
+ //
245
+ // Strategy: replace console.log/info/debug with stderr writers
246
+ // for the rest of the process. JSON-RPC frames go out via the
247
+ // dedicated `writeFrame()` helper below (process.stdout.write
248
+ // with the original native binding, NOT console.log), so the
249
+ // hijack can't accidentally redirect protocol frames too.
250
+ process.env.MCP_STDIO_MODE = '1';
251
+ const originalLog = console.log; // eslint-disable-line no-console
252
+ console.log = (...args) => process.stderr.write('[stdout→stderr] ' + args.map(a => typeof a === 'string' ? a : JSON.stringify(a)).join(' ') + '\n');
253
+ console.info = (...args) => process.stderr.write('[stdout→stderr] ' + args.map(a => typeof a === 'string' ? a : JSON.stringify(a)).join(' ') + '\n');
254
+ console.debug = (...args) => process.stderr.write('[stdout→stderr] ' + args.map(a => typeof a === 'string' ? a : JSON.stringify(a)).join(' ') + '\n');
255
+ /** Send a single JSON-RPC frame to the real stdout. Use this instead
256
+ * of `console.log` so the hijack above can't redirect protocol frames. */
257
+ const writeFrame = (msg) => {
258
+ process.stdout.write(JSON.stringify(msg) + '\n');
259
+ };
260
+ // Reference originalLog to keep the eslint-disable meaningful — also
261
+ // gives us an escape hatch if a test wants to verify it was replaced.
262
+ void originalLog;
263
+ // Catch fatal errors that would otherwise close the transport
264
+ // mid-batch with no JSON-RPC error returned to the client.
265
+ process.on('uncaughtException', (err) => {
266
+ process.stderr.write(`[mcp-stdio] uncaughtException: ${err.stack || err.message}\n`);
267
+ });
268
+ process.on('unhandledRejection', (reason) => {
269
+ process.stderr.write(`[mcp-stdio] unhandledRejection: ${reason instanceof Error ? reason.stack || reason.message : String(reason)}\n`);
270
+ });
238
271
  // Import the tool registry
239
272
  const { listMCPTools, callMCPTool, hasTool } = await import('./mcp-client.js');
240
273
  const VERSION = '3.0.0';
@@ -277,7 +310,7 @@ export class MCPServerManager extends EventEmitter {
277
310
  version: VERSION,
278
311
  }));
279
312
  // Send server initialization notification
280
- console.log(JSON.stringify({
313
+ writeFrame({
281
314
  jsonrpc: '2.0',
282
315
  method: 'server.initialized',
283
316
  params: {
@@ -290,7 +323,7 @@ export class MCPServerManager extends EventEmitter {
290
323
  },
291
324
  },
292
325
  },
293
- }));
326
+ });
294
327
  // Handle stdin messages (S-5: bounded buffer to prevent OOM)
295
328
  const MAX_BUFFER_SIZE = 10 * 1024 * 1024; // 10MB
296
329
  let buffer = '';
@@ -299,10 +332,10 @@ export class MCPServerManager extends EventEmitter {
299
332
  if (buffer.length > MAX_BUFFER_SIZE) {
300
333
  console.error(`[${new Date().toISOString()}] ERROR [claude-flow-mcp] Buffer exceeded ${MAX_BUFFER_SIZE} bytes, rejecting`);
301
334
  buffer = '';
302
- console.log(JSON.stringify({
335
+ writeFrame({
303
336
  jsonrpc: '2.0',
304
337
  error: { code: -32600, message: 'Request too large' },
305
- }));
338
+ });
306
339
  return;
307
340
  }
308
341
  // Process complete JSON messages
@@ -314,7 +347,7 @@ export class MCPServerManager extends EventEmitter {
314
347
  const message = JSON.parse(line);
315
348
  const response = await this.handleMCPMessage(message, sessionId);
316
349
  if (response) {
317
- console.log(JSON.stringify(response));
350
+ writeFrame(response);
318
351
  }
319
352
  }
320
353
  catch (error) {
@@ -70,6 +70,37 @@ function sanitizeMemoryKey(key) {
70
70
  const safe = key.replace(DANGEROUS_KEY_CHARS, '_');
71
71
  return safe.length > MAX_KEY_LENGTH ? safe.slice(0, MAX_KEY_LENGTH) : safe;
72
72
  }
73
+ // #1937 — minimal glob → RegExp helper for memory_import_claude exclusion
74
+ // patterns. Anchored. Supports the three operators the issue's voice-fidelity
75
+ // workflow needs:
76
+ // `**` — any chars including path separators
77
+ // `*` — any chars except path separators
78
+ // `?` — exactly one char except a path separator
79
+ // Everything else is regex-escaped. Used to match absolute file paths.
80
+ function globToRegex(pattern) {
81
+ // Tokenize so we can replace `**` before `*` without overlap.
82
+ let out = '';
83
+ for (let i = 0; i < pattern.length; i++) {
84
+ const c = pattern[i];
85
+ if (c === '*' && pattern[i + 1] === '*') {
86
+ out += '.*';
87
+ i++;
88
+ }
89
+ else if (c === '*') {
90
+ out += '[^/\\\\]*';
91
+ }
92
+ else if (c === '?') {
93
+ out += '[^/\\\\]';
94
+ }
95
+ else if (/[.+^$|(){}\[\]\\]/.test(c)) {
96
+ out += '\\' + c;
97
+ }
98
+ else {
99
+ out += c;
100
+ }
101
+ }
102
+ return new RegExp('^' + out + '$');
103
+ }
73
104
  // #1883 — resolve the Claude-Code project memory directory for the *current*
74
105
  // project. Claude Code hashes the project path differently per host OS, and
75
106
  // our previous logic only POSIX-slash-replaced cwd, which breaks for:
@@ -677,7 +708,7 @@ export const memoryTools = [
677
708
  // ===== Claude Code Memory Bridge Tools =====
678
709
  {
679
710
  name: 'memory_import_claude',
680
- description: 'Import Claude Code auto-memory files into AgentDB with ONNX vector embeddings. Reads ~/.claude/projects/*/memory/*.md files, parses YAML frontmatter, splits into sections, and stores with 384-dim embeddings for semantic search. Use allProjects=true to import from ALL Claude projects. Pass projectPath to override cwd-based detection (#1883 — required when Ruflo runs in WSL but Claude Code is on Windows). Use when native Read/Write is wrong because you need (a) cross-session retrieval by semantic similarity (vector embeddings) not by file path, (b) namespacing across projects without managing directory layout, or (c) the .swarm/memory.db audit trail. For one-shot file I/O, native Read/Write is fine.',
711
+ description: 'Import Claude Code auto-memory files into AgentDB with ONNX vector embeddings. Reads ~/.claude/projects/*/memory/*.md files, parses YAML frontmatter, splits into sections, and stores with 384-dim embeddings for semantic search. Use allProjects=true to import from ALL Claude projects. Pass projectPath to override cwd-based detection (#1883 — required when Ruflo runs in WSL but Claude Code is on Windows). Pass excludeFilePatterns (glob list) or excludeFiles (absolute path list) to skip voice-load-bearing, PII, or persona-restricted files (#1937). Use when native Read/Write is wrong because you need (a) cross-session retrieval by semantic similarity (vector embeddings) not by file path, (b) namespacing across projects without managing directory layout, or (c) the .swarm/memory.db audit trail. For one-shot file I/O, native Read/Write is fine.',
681
712
  category: 'memory',
682
713
  inputSchema: {
683
714
  type: 'object',
@@ -685,6 +716,16 @@ export const memoryTools = [
685
716
  allProjects: { type: 'boolean', description: 'Import from all Claude projects (default: current project only)' },
686
717
  namespace: { type: 'string', description: 'Target namespace (default: "claude-memories")' },
687
718
  projectPath: { type: 'string', description: '#1883 — explicit project path to hash, used when cwd does not match Claude Code\'s view (e.g. WSL bridge to Windows host). Pass the canonical project root as Claude Code sees it.' },
719
+ excludeFilePatterns: {
720
+ type: 'array',
721
+ items: { type: 'string' },
722
+ description: '#1937 — glob patterns matched against the absolute file path. Files matching ANY pattern are skipped. Supports `*` (any chars within a path segment), `**` (any chars including separators), and `?` (single char). Examples: `**/voice-*.md`, `**/persona-*.md`. Combine with excludeFiles for explicit paths.',
723
+ },
724
+ excludeFiles: {
725
+ type: 'array',
726
+ items: { type: 'string' },
727
+ description: '#1937 — absolute file paths to skip verbatim. Faster than a pattern when the list is known ahead of time (operator captured baselines). Combine with excludeFilePatterns.',
728
+ },
688
729
  },
689
730
  },
690
731
  handler: async (input) => {
@@ -699,8 +740,18 @@ export const memoryTools = [
699
740
  const allProjects = input.allProjects;
700
741
  const projectPathOverride = input.projectPath;
701
742
  const claudeProjectsDir = join(homedir(), '.claude', 'projects');
743
+ // #1937 — voice-fidelity / persona-restricted exclusion.
744
+ const excludeFilePatterns = Array.isArray(input.excludeFilePatterns) ? input.excludeFilePatterns : [];
745
+ const excludeFilesList = Array.isArray(input.excludeFiles) ? new Set(input.excludeFiles) : new Set();
746
+ const excludeRegexes = excludeFilePatterns.map(globToRegex);
747
+ const isExcluded = (absPath) => {
748
+ if (excludeFilesList.has(absPath))
749
+ return true;
750
+ return excludeRegexes.some(re => re.test(absPath));
751
+ };
702
752
  // Find memory files
703
753
  const memoryFiles = [];
754
+ let excludedByPattern = 0;
704
755
  if (allProjects) {
705
756
  // Scan all projects
706
757
  if (existsSync(claudeProjectsDir)) {
@@ -712,7 +763,12 @@ export const memoryTools = [
712
763
  if (!existsSync(memDir))
713
764
  continue;
714
765
  for (const file of readdirSync(memDir).filter((f) => f.endsWith('.md'))) {
715
- memoryFiles.push({ path: join(memDir, file), project: project.name, file });
766
+ const absPath = join(memDir, file);
767
+ if (isExcluded(absPath)) {
768
+ excludedByPattern++;
769
+ continue;
770
+ }
771
+ memoryFiles.push({ path: absPath, project: project.name, file });
716
772
  }
717
773
  }
718
774
  }
@@ -726,7 +782,12 @@ export const memoryTools = [
726
782
  if (resolved) {
727
783
  try {
728
784
  for (const file of readdirSync(resolved.memDir).filter((f) => f.endsWith('.md'))) {
729
- memoryFiles.push({ path: join(resolved.memDir, file), project: resolved.projectHash, file });
785
+ const absPath = join(resolved.memDir, file);
786
+ if (isExcluded(absPath)) {
787
+ excludedByPattern++;
788
+ continue;
789
+ }
790
+ memoryFiles.push({ path: absPath, project: resolved.projectHash, file });
730
791
  }
731
792
  }
732
793
  catch { /* scan error */ }
@@ -802,6 +863,7 @@ export const memoryTools = [
802
863
  imported,
803
864
  skipped,
804
865
  duplicatesSkipped,
866
+ excludedByPattern,
805
867
  files: memoryFiles.length,
806
868
  projects: projects.size,
807
869
  namespace: ns,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-flow/cli",
3
- "version": "3.7.0-alpha.35",
3
+ "version": "3.7.0-alpha.36",
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",