claude-flow 3.6.5 → 3.6.7

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.
@@ -0,0 +1 @@
1
+ {"sessionId":"ea43bca2-cc37-44fb-97ff-f1c914a31fb1","pid":12017,"procStart":"Tue Apr 28 01:25:58 2026","acquiredAt":1777350938971}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-flow",
3
- "version": "3.6.5",
3
+ "version": "3.6.7",
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",
@@ -8,47 +8,97 @@ import { validateIdentifier, validateText } from './validate-input.js';
8
8
  // Session registry for multi-session support
9
9
  const browserSessions = new Map();
10
10
  /**
11
- * Execute agent-browser CLI command
11
+ * Execute agent-browser CLI command.
12
+ * Tries global agent-browser first, falls back to npx if ENOENT.
12
13
  */
13
14
  async function execBrowserCommand(args, session = 'default') {
14
15
  const { execFileSync } = await import('child_process');
16
+ const fullArgs = ['--session', session, '--json', ...args];
17
+ let result;
15
18
  try {
16
- const fullArgs = ['--session', session, '--json', ...args];
17
- const result = execFileSync('agent-browser', fullArgs, {
19
+ result = execFileSync('agent-browser', fullArgs, {
18
20
  encoding: 'utf-8',
19
21
  timeout: 30000,
20
22
  });
21
- let data;
22
- try {
23
- data = JSON.parse(result);
24
- }
25
- catch {
26
- data = result.trim();
23
+ }
24
+ catch (error) {
25
+ const err = error;
26
+ if (err.code === 'ENOENT') {
27
+ try {
28
+ result = execFileSync('npx', ['--yes', 'agent-browser', ...fullArgs], {
29
+ encoding: 'utf-8',
30
+ timeout: 60000,
31
+ });
32
+ }
33
+ catch (npxError) {
34
+ const npxErr = npxError;
35
+ return {
36
+ content: [{
37
+ type: 'text',
38
+ text: JSON.stringify({
39
+ success: false,
40
+ error: npxErr.code === 'ENOENT'
41
+ ? 'Neither agent-browser nor npx found. Install with: npm i -g agent-browser'
42
+ : npxErr instanceof Error ? npxErr.message : String(npxError),
43
+ }),
44
+ }],
45
+ isError: true,
46
+ };
47
+ }
27
48
  }
28
- // Update session activity
29
- const sessionInfo = browserSessions.get(session);
30
- if (sessionInfo) {
31
- sessionInfo.lastActivity = new Date().toISOString();
49
+ else {
50
+ return {
51
+ content: [{
52
+ type: 'text',
53
+ text: JSON.stringify({
54
+ success: false,
55
+ error: err instanceof Error ? err.message : String(error),
56
+ }),
57
+ }],
58
+ isError: true,
59
+ };
32
60
  }
33
- return {
34
- content: [{
35
- type: 'text',
36
- text: JSON.stringify(data, null, 2),
37
- }],
38
- };
39
61
  }
40
- catch (error) {
41
- return {
42
- content: [{
43
- type: 'text',
44
- text: JSON.stringify({
45
- success: false,
46
- error: error instanceof Error ? error.message : String(error),
47
- }),
48
- }],
49
- isError: true,
50
- };
62
+ let data;
63
+ try {
64
+ data = JSON.parse(result);
51
65
  }
66
+ catch {
67
+ data = result.trim();
68
+ }
69
+ const sessionInfo = browserSessions.get(session);
70
+ if (sessionInfo) {
71
+ sessionInfo.lastActivity = new Date().toISOString();
72
+ }
73
+ return {
74
+ content: [{
75
+ type: 'text',
76
+ text: JSON.stringify(data, null, 2),
77
+ }],
78
+ };
79
+ }
80
+ /**
81
+ * Read a sysctl value, returning the trimmed string or null.
82
+ */
83
+ function readSysctl(name) {
84
+ try {
85
+ const { readFileSync, existsSync } = require('fs');
86
+ const p = `/proc/sys/kernel/${name}`;
87
+ if (existsSync(p))
88
+ return readFileSync(p, 'utf-8').trim();
89
+ }
90
+ catch { /* not Linux or can't read */ }
91
+ return null;
92
+ }
93
+ /**
94
+ * Detect if Linux needs --no-sandbox for Chrome.
95
+ * Checks both legacy userns flag and Ubuntu 24.04+ AppArmor restriction.
96
+ */
97
+ function needsNoSandbox() {
98
+ if (process.platform !== 'linux')
99
+ return false;
100
+ return readSysctl('unprivileged_userns_clone') === '0' ||
101
+ readSysctl('apparmor_restrict_unprivileged_userns') === '1';
52
102
  }
53
103
  /**
54
104
  * Browser MCP Tools
@@ -72,6 +122,11 @@ export const browserTools = [
72
122
  enum: ['load', 'domcontentloaded', 'networkidle'],
73
123
  description: 'Wait condition',
74
124
  },
125
+ args: {
126
+ type: 'array',
127
+ items: { type: 'string' },
128
+ description: 'Additional Chrome launch args (e.g., ["--no-sandbox", "--disable-dev-shm-usage"])',
129
+ },
75
130
  },
76
131
  required: ['url'],
77
132
  },
@@ -85,9 +140,15 @@ export const browserTools = [
85
140
  return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: vS.error }) }], isError: true };
86
141
  }
87
142
  const { url, session, waitUntil } = input;
143
+ const launchArgs = input.args || [];
144
+ if (needsNoSandbox() && !launchArgs.includes('--no-sandbox')) {
145
+ launchArgs.push('--no-sandbox');
146
+ }
88
147
  const args = ['open', url];
89
148
  if (waitUntil)
90
149
  args.push('--wait-until', waitUntil);
150
+ if (launchArgs.length > 0)
151
+ args.push('--args', launchArgs.join(' '));
91
152
  // Create session if new
92
153
  const sessionId = session || 'default';
93
154
  if (!browserSessions.has(sessionId)) {
@@ -369,13 +430,13 @@ export const browserTools = [
369
430
  },
370
431
  {
371
432
  name: 'browser_hover',
372
- description: 'Hover over an element',
433
+ description: 'Hover over an element using ref (@e1) or CSS selector',
373
434
  category: 'browser',
374
435
  tags: ['interaction'],
375
436
  inputSchema: {
376
437
  type: 'object',
377
438
  properties: {
378
- target: { type: 'string', description: 'Element ref or CSS selector' },
439
+ target: { type: 'string', description: 'Element ref (@e1) or CSS selector' },
379
440
  session: { type: 'string', description: 'Session ID' },
380
441
  },
381
442
  required: ['target'],
@@ -412,7 +412,7 @@ export const embeddingsTools = [
412
412
  query,
413
413
  limit: topK,
414
414
  threshold,
415
- namespace: namespace || 'default'
415
+ namespace: namespace || 'all'
416
416
  });
417
417
  const searchTime = (performance.now() - startTime).toFixed(2);
418
418
  return {
@@ -428,7 +428,7 @@ export const embeddingsTools = [
428
428
  model: config.model,
429
429
  topK,
430
430
  threshold,
431
- namespace: namespace || 'default',
431
+ namespace: namespace || 'all',
432
432
  searchTime: `${searchTime}ms`,
433
433
  indexType: config.hyperbolic.enabled ? 'HNSW (hyperbolic)' : 'HNSW (euclidean)',
434
434
  resultCount: searchResult.results.length
@@ -446,7 +446,7 @@ export const embeddingsTools = [
446
446
  model: config.model,
447
447
  topK,
448
448
  threshold,
449
- namespace: namespace || 'default',
449
+ namespace: namespace || 'all',
450
450
  searchTime: `${searchTime}ms`,
451
451
  indexType: config.hyperbolic.enabled ? 'HNSW (hyperbolic)' : 'HNSW (euclidean)',
452
452
  },
@@ -405,4 +405,19 @@ export declare function bridgeContextSynthesize(params: {
405
405
  export declare function bridgeSemanticRoute(params: {
406
406
  input: string;
407
407
  }): Promise<any>;
408
+ /**
409
+ * Export all embeddings from the bridge's better-sqlite3 connection.
410
+ * Used by RaBitQ to build its index from the same data that memory_store writes.
411
+ * Returns null if bridge is unavailable (caller falls back to sql.js).
412
+ */
413
+ export declare function bridgeGetAllEmbeddings(options?: {
414
+ dimensions?: number;
415
+ limit?: number;
416
+ dbPath?: string;
417
+ }): Promise<Array<{
418
+ id: string;
419
+ key: string;
420
+ namespace: string;
421
+ embedding: number[];
422
+ }> | null>;
408
423
  //# sourceMappingURL=memory-bridge.d.ts.map
@@ -1554,6 +1554,51 @@ export async function bridgeSemanticRoute(params) {
1554
1554
  return { route: null, error: e.message };
1555
1555
  }
1556
1556
  }
1557
+ // ===== RaBitQ data export =====
1558
+ /**
1559
+ * Export all embeddings from the bridge's better-sqlite3 connection.
1560
+ * Used by RaBitQ to build its index from the same data that memory_store writes.
1561
+ * Returns null if bridge is unavailable (caller falls back to sql.js).
1562
+ */
1563
+ export async function bridgeGetAllEmbeddings(options) {
1564
+ const registry = await getRegistry(options?.dbPath);
1565
+ if (!registry)
1566
+ return null;
1567
+ const ctx = getDb(registry);
1568
+ if (!ctx)
1569
+ return null;
1570
+ try {
1571
+ const dims = options?.dimensions ?? 384;
1572
+ const maxRows = options?.limit ?? 50000;
1573
+ const rows = ctx.db.prepare(`
1574
+ SELECT id, key, namespace, embedding
1575
+ FROM memory_entries
1576
+ WHERE status = 'active' AND embedding IS NOT NULL
1577
+ LIMIT ?
1578
+ `).all(maxRows);
1579
+ const results = [];
1580
+ for (const row of rows) {
1581
+ if (!row.embedding)
1582
+ continue;
1583
+ try {
1584
+ const emb = JSON.parse(row.embedding);
1585
+ if (emb.length !== dims)
1586
+ continue;
1587
+ results.push({
1588
+ id: String(row.id),
1589
+ key: row.key || String(row.id),
1590
+ namespace: row.namespace || 'default',
1591
+ embedding: emb,
1592
+ });
1593
+ }
1594
+ catch { /* skip invalid */ }
1595
+ }
1596
+ return results;
1597
+ }
1598
+ catch {
1599
+ return null;
1600
+ }
1601
+ }
1557
1602
  // ===== Utility =====
1558
1603
  function cosineSim(a, b) {
1559
1604
  if (!a || !b || a.length === 0 || b.length === 0)
@@ -55,41 +55,57 @@ export async function buildRabitqIndex(options) {
55
55
  const dimensions = options?.dimensions ?? 384;
56
56
  const swarmDir = path.resolve(process.cwd(), '.swarm');
57
57
  const dbPath = options?.dbPath ? path.resolve(options.dbPath) : path.join(swarmDir, 'memory.db');
58
- if (!fs.existsSync(dbPath)) {
59
- rabitqInitializing = false;
60
- return { success: false, vectorCount: 0, dimensions, compressionRatio: 0, buildTimeMs: 0, error: 'Database not found' };
61
- }
62
- // Load embeddings from SQLite
63
- const initSqlJs = (await import('sql.js')).default;
64
- const SQL = await initSqlJs();
65
- const fileBuffer = fs.readFileSync(dbPath);
66
- const db = new SQL.Database(fileBuffer);
67
- const result = db.exec(`
68
- SELECT id, key, namespace, embedding
69
- FROM memory_entries
70
- WHERE status = 'active' AND embedding IS NOT NULL
71
- LIMIT 50000
72
- `);
73
58
  const entries = [];
74
59
  const vectors = [];
75
- if (result[0]?.values) {
76
- for (const row of result[0].values) {
77
- const [id, key, ns, embeddingJson] = row;
78
- if (!embeddingJson)
79
- continue;
80
- try {
81
- const embedding = JSON.parse(embeddingJson);
82
- if (embedding.length !== dimensions)
83
- continue;
84
- entries.push({ id: String(id), key: key || String(id), namespace: ns || 'default' });
85
- vectors.push(...embedding);
60
+ // Try bridge first (reads via better-sqlite3, sees WAL data)
61
+ let usedBridge = false;
62
+ try {
63
+ const { bridgeGetAllEmbeddings } = await import('./memory-bridge.js');
64
+ const bridgeRows = await bridgeGetAllEmbeddings({ dimensions, dbPath: options?.dbPath });
65
+ if (bridgeRows && bridgeRows.length > 0) {
66
+ for (const row of bridgeRows) {
67
+ entries.push({ id: row.id, key: row.key, namespace: row.namespace });
68
+ vectors.push(...row.embedding);
86
69
  }
87
- catch {
88
- // skip invalid
70
+ usedBridge = true;
71
+ }
72
+ }
73
+ catch { /* bridge unavailable, fall through */ }
74
+ // Fallback: read .swarm/memory.db via sql.js
75
+ if (!usedBridge) {
76
+ if (!fs.existsSync(dbPath)) {
77
+ rabitqInitializing = false;
78
+ return { success: false, vectorCount: 0, dimensions, compressionRatio: 0, buildTimeMs: 0, error: 'Database not found' };
79
+ }
80
+ const initSqlJs = (await import('sql.js')).default;
81
+ const SQL = await initSqlJs();
82
+ const fileBuffer = fs.readFileSync(dbPath);
83
+ const db = new SQL.Database(fileBuffer);
84
+ const result = db.exec(`
85
+ SELECT id, key, namespace, embedding
86
+ FROM memory_entries
87
+ WHERE status = 'active' AND embedding IS NOT NULL
88
+ LIMIT 50000
89
+ `);
90
+ if (result[0]?.values) {
91
+ for (const row of result[0].values) {
92
+ const [id, key, ns, embeddingJson] = row;
93
+ if (!embeddingJson)
94
+ continue;
95
+ try {
96
+ const embedding = JSON.parse(embeddingJson);
97
+ if (embedding.length !== dimensions)
98
+ continue;
99
+ entries.push({ id: String(id), key: key || String(id), namespace: ns || 'default' });
100
+ vectors.push(...embedding);
101
+ }
102
+ catch {
103
+ // skip invalid
104
+ }
89
105
  }
90
106
  }
107
+ db.close();
91
108
  }
92
- db.close();
93
109
  if (entries.length < 2) {
94
110
  rabitqInitializing = false;
95
111
  return { success: false, vectorCount: entries.length, dimensions, compressionRatio: 0, buildTimeMs: Date.now() - startTime, error: 'Need at least 2 vectors to build RaBitQ index' };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-flow/cli",
3
- "version": "3.6.5",
3
+ "version": "3.6.7",
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",
@@ -336,11 +336,32 @@ function isContentAwareExecutor(executor) {
336
336
  return 'setContext' in executor && typeof executor.setContext === 'function';
337
337
  }
338
338
  class DefaultHeadlessExecutor {
339
+ contextContent = null;
340
+ setContext(claudeMdContent) {
341
+ this.contextContent = claudeMdContent;
342
+ }
339
343
  async execute(prompt, workDir) {
340
344
  const { execFile } = await import('node:child_process');
341
345
  const { promisify } = await import('node:util');
346
+ const fs = await import('node:fs/promises');
347
+ const { join } = await import('node:path');
342
348
  const execFileAsync = promisify(execFile);
343
- // Pass prompt as a direct argument array — no shell interpretation.
349
+ const claudeMdPath = join(workDir, 'CLAUDE.md');
350
+ const backupPath = join(workDir, '.CLAUDE.md.ab-backup');
351
+ let swapped = false;
352
+ if (this.contextContent !== null) {
353
+ try {
354
+ await fs.copyFile(claudeMdPath, backupPath);
355
+ }
356
+ catch { /* no file to back up */ }
357
+ if (this.contextContent.length > 0) {
358
+ await fs.writeFile(claudeMdPath, this.contextContent, 'utf-8');
359
+ }
360
+ else {
361
+ await fs.unlink(claudeMdPath).catch(() => { });
362
+ }
363
+ swapped = true;
364
+ }
344
365
  try {
345
366
  const { stdout, stderr } = await execFileAsync('claude', ['-p', prompt, '--output-format', 'json'], { timeout: 60000, maxBuffer: 10 * 1024 * 1024, encoding: 'utf-8', cwd: workDir });
346
367
  return { stdout, stderr, exitCode: 0 };
@@ -348,6 +369,17 @@ class DefaultHeadlessExecutor {
348
369
  catch (error) {
349
370
  return { stdout: error.stdout ?? '', stderr: error.stderr ?? '', exitCode: error.code ?? 1 };
350
371
  }
372
+ finally {
373
+ if (swapped) {
374
+ try {
375
+ await fs.copyFile(backupPath, claudeMdPath);
376
+ await fs.unlink(backupPath);
377
+ }
378
+ catch {
379
+ await fs.unlink(claudeMdPath).catch(() => { });
380
+ }
381
+ }
382
+ }
351
383
  }
352
384
  }
353
385
  function getDefaultBenchmarkTasks() {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-flow/guidance",
3
- "version": "3.0.0-alpha.1",
3
+ "version": "3.0.0-alpha.2",
4
4
  "description": "Guidance Control Plane - Compiles, retrieves, enforces, and evolves guidance rules for Claude Code sessions",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",