claude-flow 3.10.0 → 3.10.3

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,5 +1,7 @@
1
1
  {
2
2
  "_note": "#1921 — hook commands invoke scripts/ruflo-hook.sh (resilient shim): prefers a locally-installed `ruflo`/`claude-flow` binary, falls back to `npx --prefer-offline`, and always exits 0 so a CLI/install failure (e.g. arborist `Invalid Version` on npm 10.8.x) never surfaces an error in Claude Code or blocks a turn. The trailing `|| true` guards the case where $CLAUDE_PLUGIN_ROOT is unset (older Claude Code) — the hook then no-ops silently. DO NOT revert to a bare `npx <pkg>@alpha hooks …` per fire.",
3
+ "_platform": "posix",
4
+ "_platform_note": "#2132 — This hooks.json uses /bin/bash, POSIX pipelines (jq, xargs, tr), and .sh scripts. It is intentionally POSIX-only (Mac/Linux). On Windows, ruflo init writes a .claude/settings.json that overrides these entries with node-based equivalents via plugins/ruflo-core/scripts/ruflo-hook.cjs. The audit exempts files with _platform:posix from the cross-platform check.",
3
5
  "hooks": {
4
6
  "PreToolUse": [
5
7
  {
@@ -0,0 +1,166 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ruflo-hook.cjs — cross-platform Node.js port of ruflo-hook.sh (#2132)
4
+ *
5
+ * The bash shim (ruflo-hook.sh) works on Mac/Linux but fails on native
6
+ * Windows (exit 126 — "cannot execute binary file"). This .cjs shim
7
+ * provides identical behaviour via Node.js child_process so Windows users
8
+ * get working hooks without WSL or Git Bash.
9
+ *
10
+ * Mac/Linux continue to use ruflo-hook.sh via the plugin hooks.json files
11
+ * (unchanged). On Windows, ruflo init writes a .claude/settings.json that
12
+ * overrides those entries with node-based equivalents pointing here.
13
+ *
14
+ * Behaviour mirrors ruflo-hook.sh:
15
+ * 1. Reads hook JSON payload from stdin.
16
+ * 2. Prefers a locally installed `ruflo` or `claude-flow` binary.
17
+ * 3. Falls back to `npx --prefer-offline ruflo@latest`.
18
+ * 4. Always exits 0 — hook subcommands are best-effort telemetry.
19
+ * 5. Swallows all stderr — nothing should surface to Claude Code.
20
+ *
21
+ * Usage: node ruflo-hook.cjs <hook-subcommand> [args...]
22
+ * e.g. node ruflo-hook.cjs post-edit --file "x.ts" --train-patterns
23
+ */
24
+
25
+ 'use strict';
26
+
27
+ const { spawnSync, execSync } = require('child_process');
28
+ const fs = require('fs');
29
+ const path = require('path');
30
+
31
+ /** Exit 0 unconditionally — hooks must never block a turn */
32
+ function done() {
33
+ process.exit(0);
34
+ }
35
+
36
+ /** Resolve stdin to a JSON object, or null if not parseable */
37
+ function readStdinJson() {
38
+ try {
39
+ let buf = '';
40
+ // Read synchronously — hooks fire synchronously in Claude Code
41
+ const fd = fs.openSync('/dev/stdin', 'r');
42
+ const chunk = Buffer.alloc(64 * 1024);
43
+ let bytesRead;
44
+ while ((bytesRead = fs.readSync(fd, chunk, 0, chunk.length, null)) > 0) {
45
+ buf += chunk.slice(0, bytesRead).toString('utf8');
46
+ }
47
+ fs.closeSync(fd);
48
+ return buf.trim() ? JSON.parse(buf) : null;
49
+ } catch {
50
+ return null;
51
+ }
52
+ }
53
+
54
+ /** Read stdin via process.stdin in sync mode (Windows-safe alternative) */
55
+ function readStdinSync() {
56
+ try {
57
+ // On Windows /dev/stdin doesn't exist; use fd 0 directly
58
+ const chunk = Buffer.alloc(64 * 1024);
59
+ let buf = '';
60
+ let bytesRead;
61
+ while (true) {
62
+ try {
63
+ bytesRead = fs.readSync(0 /* STDIN_FILENO */, chunk, 0, chunk.length, null);
64
+ if (bytesRead === 0) break;
65
+ buf += chunk.slice(0, bytesRead).toString('utf8');
66
+ } catch {
67
+ break;
68
+ }
69
+ }
70
+ return buf.trim() ? JSON.parse(buf) : null;
71
+ } catch {
72
+ return null;
73
+ }
74
+ }
75
+
76
+ /** Check if a binary is available on PATH */
77
+ function commandExists(cmd) {
78
+ try {
79
+ const result = execSync(
80
+ process.platform === 'win32' ? `where ${cmd}` : `command -v ${cmd}`,
81
+ { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }
82
+ );
83
+ return result.trim().length > 0;
84
+ } catch {
85
+ return false;
86
+ }
87
+ }
88
+
89
+ /** Build the argv for the ruflo/claude-flow/npx invocation */
90
+ function buildArgs(subcommand, extraArgs) {
91
+ // The `hooks` word is prepended here, matching ruflo-hook.sh convention.
92
+ return ['hooks', subcommand, ...extraArgs];
93
+ }
94
+
95
+ /**
96
+ * Spawn the CLI with the hook subcommand.
97
+ * Passes the raw stdin payload as the child's stdin so the CLI can read
98
+ * the hook event JSON if needed (same as the bash pipe).
99
+ *
100
+ * Returns true on success (exit 0), false otherwise.
101
+ */
102
+ function invokeHook(bin, binArgs, hookArgs, stdinData) {
103
+ const args = [...binArgs, ...hookArgs];
104
+
105
+ // On Windows, shell: true is needed to resolve .cmd shims in node_modules
106
+ const useShell = process.platform === 'win32';
107
+
108
+ const result = spawnSync(bin, args, {
109
+ shell: useShell,
110
+ input: stdinData || '',
111
+ encoding: 'utf8',
112
+ stdio: ['pipe', 'ignore', 'ignore'], // swallow all output
113
+ timeout: 30_000,
114
+ });
115
+
116
+ return result.status === 0;
117
+ }
118
+
119
+ function main() {
120
+ const args = process.argv.slice(2);
121
+ if (args.length === 0) {
122
+ // No subcommand — no-op, same as bash version
123
+ done();
124
+ }
125
+
126
+ const [subcommand, ...rest] = args;
127
+
128
+ // Read stdin (the hook event payload) — best effort
129
+ let stdinData = '';
130
+ try {
131
+ stdinData = fs.readFileSync(0 /* fd 0 = stdin */, 'utf8');
132
+ } catch {
133
+ // stdin may not be available when invoked directly for testing
134
+ stdinData = '';
135
+ }
136
+
137
+ const hookArgs = buildArgs(subcommand, rest);
138
+
139
+ // Priority 1: locally installed ruflo binary
140
+ if (commandExists('ruflo')) {
141
+ invokeHook('ruflo', [], hookArgs, stdinData);
142
+ done();
143
+ }
144
+
145
+ // Priority 2: locally installed claude-flow binary
146
+ if (commandExists('claude-flow')) {
147
+ invokeHook('claude-flow', [], hookArgs, stdinData);
148
+ done();
149
+ }
150
+
151
+ // Priority 3: npx --prefer-offline fallback (avoids cold registry resolve).
152
+ //
153
+ // SKIP this when RUFLO_HOOK_SKIP_NPX=1 — used by CI smokes that test
154
+ // the shim's *control flow* without exercising npm install network paths.
155
+ // Without the skip, npx can take 30+s on a cold runner (no warm cache,
156
+ // no offline tarball), exceeding the smoke's 15s timeout and producing
157
+ // a spurious failure even though the shim itself works correctly.
158
+ // The bash version doesn't hit this because it backgrounded the work.
159
+ if (process.env.RUFLO_HOOK_SKIP_NPX !== '1') {
160
+ invokeHook('npx', ['--prefer-offline', '--yes', 'ruflo@latest'], hookArgs, stdinData);
161
+ }
162
+
163
+ done();
164
+ }
165
+
166
+ main();
package/README.md CHANGED
@@ -382,6 +382,7 @@ Four docs for four audiences:
382
382
  | **[User Guide](docs/USERGUIDE.md)** | Daily reference — every command, every config flag, every plugin. The *how-do-I* doc. |
383
383
  | **[Benchmarks](https://gist.github.com/ruvnet/298f8c668c8859b369f91734a0e9cbbe)** | v3.8.0 SOTA matrix vs LangGraph / AutoGen / CrewAI on darwin-arm64 + linux-x64. ruflo wins cold start, single turn, RSS by 1.3×–1953×. The *is-it-fast* doc. |
384
384
  | **[Verification](verification.md)** | Cryptographically prove your installed bytes match the signed witness — `ruflo verify`. The *trust-but-verify* doc. |
385
+ | **[Team Gateway Checklist](docs/TEAM-GATEWAY-CHECKLIST.md)** | Before-merge gates, dual-mode handoff, memory namespace sharing, and witness manifest entry per merge. The *safer-team-workflows* doc. |
385
386
 
386
387
  Benchmark internals (for reproduction): [`sota-workload-spec.md`](https://github.com/ruvnet/ruflo/blob/perf/sota-comparator-benchmarks/docs/benchmarks/sota-workload-spec.md) · [`SOTA-PROGRESS.md`](https://github.com/ruvnet/ruflo/blob/perf/sota-comparator-benchmarks/docs/benchmarks/SOTA-PROGRESS.md) · [raw matrix JSON: darwin](https://github.com/ruvnet/ruflo/blob/perf/sota-comparator-benchmarks/docs/benchmarks/sota-matrix.json) · [linux](https://github.com/ruvnet/ruflo/blob/perf/sota-comparator-benchmarks/docs/benchmarks/sota-matrix-linux.json)
387
388
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-flow",
3
- "version": "3.10.0",
3
+ "version": "3.10.3",
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",
@@ -382,6 +382,7 @@ Four docs for four audiences:
382
382
  | **[User Guide](docs/USERGUIDE.md)** | Daily reference — every command, every config flag, every plugin. The *how-do-I* doc. |
383
383
  | **[Benchmarks](https://gist.github.com/ruvnet/298f8c668c8859b369f91734a0e9cbbe)** | v3.8.0 SOTA matrix vs LangGraph / AutoGen / CrewAI on darwin-arm64 + linux-x64. ruflo wins cold start, single turn, RSS by 1.3×–1953×. The *is-it-fast* doc. |
384
384
  | **[Verification](verification.md)** | Cryptographically prove your installed bytes match the signed witness — `ruflo verify`. The *trust-but-verify* doc. |
385
+ | **[Team Gateway Checklist](docs/TEAM-GATEWAY-CHECKLIST.md)** | Before-merge gates, dual-mode handoff, memory namespace sharing, and witness manifest entry per merge. The *safer-team-workflows* doc. |
385
386
 
386
387
  Benchmark internals (for reproduction): [`sota-workload-spec.md`](https://github.com/ruvnet/ruflo/blob/perf/sota-comparator-benchmarks/docs/benchmarks/sota-workload-spec.md) · [`SOTA-PROGRESS.md`](https://github.com/ruvnet/ruflo/blob/perf/sota-comparator-benchmarks/docs/benchmarks/SOTA-PROGRESS.md) · [raw matrix JSON: darwin](https://github.com/ruvnet/ruflo/blob/perf/sota-comparator-benchmarks/docs/benchmarks/sota-matrix.json) · [linux](https://github.com/ruvnet/ruflo/blob/perf/sota-comparator-benchmarks/docs/benchmarks/sota-matrix-linux.json)
387
388
 
@@ -238,7 +238,6 @@ const diffCommand = {
238
238
  }
239
239
  },
240
240
  };
241
- // Code subcommand (placeholder for future code analysis)
242
241
  const codeCommand = {
243
242
  name: 'code',
244
243
  description: 'Static code analysis and quality assessment',
@@ -12,6 +12,14 @@ const BACKENDS = [
12
12
  { value: 'hybrid', label: 'Hybrid', hint: 'SQLite + AgentDB (recommended)' },
13
13
  { value: 'memory', label: 'In-Memory', hint: 'Fast but non-persistent' }
14
14
  ];
15
+ // #2105: shared --path option for memory subcommands.
16
+ // Precedence: --path > CLAUDE_FLOW_DB_PATH env var > default root
17
+ const DB_PATH_OPTION = {
18
+ name: 'path',
19
+ description: 'Override DB file path (also: CLAUDE_FLOW_DB_PATH env var). ' +
20
+ 'Precedence: --path > CLAUDE_FLOW_DB_PATH > CLAUDE_FLOW_MEMORY_PATH/memory.db > cwd/.swarm/memory.db',
21
+ type: 'string',
22
+ };
15
23
  // Store command
16
24
  const storeCommand = {
17
25
  name: 'store',
@@ -59,7 +67,8 @@ const storeCommand = {
59
67
  description: 'Update if key exists (insert or replace)',
60
68
  type: 'boolean',
61
69
  default: false
62
- }
70
+ },
71
+ DB_PATH_OPTION
63
72
  ],
64
73
  examples: [
65
74
  { command: 'claude-flow memory store -k "api/auth" -v "JWT implementation"', description: 'Store text' },
@@ -101,7 +110,8 @@ const storeCommand = {
101
110
  output.printInfo(`Storing in ${namespace}/${key}...`);
102
111
  // Use direct sql.js storage with automatic embedding generation
103
112
  try {
104
- const { storeEntry } = await import('../memory/memory-initializer.js');
113
+ const { storeEntry, resolveDbPath: _rdbStore } = await import('../memory/memory-initializer.js');
114
+ const dbPath = _rdbStore(ctx.flags.path);
105
115
  if (asVector) {
106
116
  output.writeln(output.dim(' Generating embedding vector...'));
107
117
  }
@@ -112,7 +122,8 @@ const storeCommand = {
112
122
  generateEmbeddingFlag: true, // Always generate embeddings for semantic search
113
123
  tags,
114
124
  ttl,
115
- upsert
125
+ upsert,
126
+ dbPath
116
127
  });
117
128
  if (!result.success) {
118
129
  output.printError(result.error || 'Failed to store');
@@ -175,7 +186,8 @@ const retrieveCommand = {
175
186
  description: 'Print only the stored value to stdout (no wrapper)',
176
187
  type: 'boolean',
177
188
  default: false
178
- }
189
+ },
190
+ DB_PATH_OPTION
179
191
  ],
180
192
  action: async (ctx) => {
181
193
  const key = ctx.flags.key || ctx.args[0];
@@ -186,8 +198,9 @@ const retrieveCommand = {
186
198
  }
187
199
  // Use sql.js directly for consistent data access
188
200
  try {
189
- const { getEntry } = await import('../memory/memory-initializer.js');
190
- const result = await getEntry({ key, namespace });
201
+ const { getEntry, resolveDbPath: _rdbRetrieve } = await import('../memory/memory-initializer.js');
202
+ const dbPathRetrieve = _rdbRetrieve(ctx.flags.path);
203
+ const result = await getEntry({ key, namespace, dbPath: dbPathRetrieve });
191
204
  if (!result.success) {
192
205
  output.printError(`Failed to retrieve: ${result.error}`);
193
206
  return { success: false, exitCode: 1 };
@@ -283,7 +296,8 @@ const searchCommand = {
283
296
  description: 'Use SmartRetrieval pipeline (query expansion, RRF, MMR, recency)',
284
297
  type: 'boolean',
285
298
  default: false
286
- }
299
+ },
300
+ DB_PATH_OPTION
287
301
  ],
288
302
  examples: [
289
303
  { command: 'claude-flow memory search -q "authentication patterns"', description: 'Semantic search' },
@@ -331,7 +345,8 @@ const searchCommand = {
331
345
  output.writeln();
332
346
  // Use direct sql.js search with vector similarity
333
347
  try {
334
- const { searchEntries } = await import('../memory/memory-initializer.js');
348
+ const { searchEntries, resolveDbPath: _rdbSearch } = await import('../memory/memory-initializer.js');
349
+ const dbPathSearch = _rdbSearch(ctx.flags.path);
335
350
  const useSmart = (ctx.flags.smart || ctx.flags.s);
336
351
  let results;
337
352
  let searchTimeMs;
@@ -365,6 +380,7 @@ const searchCommand = {
365
380
  namespace: req.namespace || namespace,
366
381
  limit: req.limit || limit * 3,
367
382
  threshold: req.threshold ?? threshold,
383
+ dbPath: dbPathSearch,
368
384
  });
369
385
  return {
370
386
  results: r.results.map(e => ({
@@ -397,7 +413,8 @@ const searchCommand = {
397
413
  query,
398
414
  namespace,
399
415
  limit,
400
- threshold
416
+ threshold,
417
+ dbPath: dbPathSearch
401
418
  });
402
419
  if (!searchResult.success) {
403
420
  output.printError(searchResult.error || 'Search failed');
@@ -470,15 +487,17 @@ const listCommand = {
470
487
  description: 'Maximum entries',
471
488
  type: 'number',
472
489
  default: 20
473
- }
490
+ },
491
+ DB_PATH_OPTION
474
492
  ],
475
493
  action: async (ctx) => {
476
494
  const namespace = ctx.flags.namespace;
477
495
  const limit = ctx.flags.limit;
478
496
  // Use sql.js directly for consistent data access
479
497
  try {
480
- const { listEntries } = await import('../memory/memory-initializer.js');
481
- const listResult = await listEntries({ namespace, limit, offset: 0 });
498
+ const { listEntries, resolveDbPath: _rdbList } = await import('../memory/memory-initializer.js');
499
+ const dbPathList = _rdbList(ctx.flags.path);
500
+ const listResult = await listEntries({ namespace, limit, offset: 0, dbPath: dbPathList });
482
501
  if (!listResult.success) {
483
502
  output.printError(`Failed to list: ${listResult.error}`);
484
503
  return { success: false, exitCode: 1 };
@@ -567,7 +586,8 @@ const deleteCommand = {
567
586
  description: 'Skip confirmation',
568
587
  type: 'boolean',
569
588
  default: false
570
- }
589
+ },
590
+ DB_PATH_OPTION
571
591
  ],
572
592
  examples: [
573
593
  { command: 'claude-flow memory delete -k "mykey"', description: 'Delete entry with default namespace' },
@@ -595,8 +615,9 @@ const deleteCommand = {
595
615
  }
596
616
  // Use sql.js directly for consistent data access (Issue #980)
597
617
  try {
598
- const { deleteEntry } = await import('../memory/memory-initializer.js');
599
- const result = await deleteEntry({ key, namespace });
618
+ const { deleteEntry, resolveDbPath: _rdbDelete } = await import('../memory/memory-initializer.js');
619
+ const dbPathDelete = _rdbDelete(ctx.flags.path);
620
+ const result = await deleteEntry({ key, namespace, dbPath: dbPathDelete });
600
621
  if (!result.success) {
601
622
  output.printError(result.error || 'Failed to delete');
602
623
  return { success: false, exitCode: 1 };
@@ -620,6 +641,7 @@ const deleteCommand = {
620
641
  const statsCommand = {
621
642
  name: 'stats',
622
643
  description: 'Show memory statistics',
644
+ options: [DB_PATH_OPTION],
623
645
  action: async (ctx) => {
624
646
  // Call MCP memory/stats tool for real statistics
625
647
  try {
@@ -14,7 +14,7 @@ import { detectPlatform, DEFAULT_INIT_OPTIONS } from './types.js';
14
14
  import { generateSettingsJson, generateSettings } from './settings-generator.js';
15
15
  import { generateMCPJson } from './mcp-generator.js';
16
16
  import { generateStatuslineScript } from './statusline-generator.js';
17
- import { generatePreCommitHook, generatePostCommitHook, generateSessionManager, generateAgentRouter, generateMemoryHelper, generateHookHandler, generateIntelligenceStub, generateAutoMemoryHook, } from './helpers-generator.js';
17
+ import { generatePreCommitHook, generatePostCommitHook, generateSessionManager, generateAgentRouter, generateMemoryHelper, generateHookHandler, generateIntelligenceStub, generateAutoMemoryHook, generateRufloHookCjs, } from './helpers-generator.js';
18
18
  import { generateClaudeMd } from './claudemd-generator.js';
19
19
  /**
20
20
  * Skills to copy based on configuration
@@ -1057,6 +1057,11 @@ async function writeHelpers(targetDir, options, result) {
1057
1057
  const helpersDir = path.join(targetDir, '.claude', 'helpers');
1058
1058
  // Find source helpers directory (works for npm package and local dev)
1059
1059
  const sourceHelpersDir = findSourceHelpersDir(options.sourceBaseDir);
1060
+ // On Windows: emit a notice before writing helpers — the settings.json
1061
+ // hooks will use node-based commands instead of bash shims (#2132).
1062
+ if (process.platform === 'win32') {
1063
+ console.log('Detected Windows — adding cross-platform hook overrides to .claude/settings.json (#2132)');
1064
+ }
1060
1065
  // Try to copy existing helpers from source first
1061
1066
  if (sourceHelpersDir && fs.existsSync(sourceHelpersDir)) {
1062
1067
  const helperFiles = fs.readdirSync(sourceHelpersDir);
@@ -1080,6 +1085,18 @@ async function writeHelpers(targetDir, options, result) {
1080
1085
  result.skipped.push(`.claude/helpers/${file}`);
1081
1086
  }
1082
1087
  }
1088
+ // #2132: Always generate ruflo-hook.cjs regardless of source copy path.
1089
+ // The source helpers dir may not contain this file (it lives in
1090
+ // plugins/ruflo-core/scripts/, not .claude/helpers/), but it must
1091
+ // always be present so Windows users can use the node-based shim.
1092
+ const rufloHookDest = path.join(helpersDir, 'ruflo-hook.cjs');
1093
+ if (!fs.existsSync(rufloHookDest) || options.force) {
1094
+ fs.writeFileSync(rufloHookDest, generateRufloHookCjs(), 'utf-8');
1095
+ result.created.files.push('.claude/helpers/ruflo-hook.cjs');
1096
+ }
1097
+ else {
1098
+ result.skipped.push('.claude/helpers/ruflo-hook.cjs');
1099
+ }
1083
1100
  if (copiedCount > 0) {
1084
1101
  return; // Skip generating if we copied from source
1085
1102
  }
@@ -1094,6 +1111,10 @@ async function writeHelpers(targetDir, options, result) {
1094
1111
  'hook-handler.cjs': generateHookHandler(),
1095
1112
  'intelligence.cjs': generateIntelligenceStub(),
1096
1113
  'auto-memory-hook.mjs': generateAutoMemoryHook(),
1114
+ // #2132: cross-platform Node.js port of ruflo-hook.sh — always deployed so
1115
+ // Windows users have a working shim even if the plugin's hooks.json bash
1116
+ // commands are overridden via settings.json.
1117
+ 'ruflo-hook.cjs': generateRufloHookCjs(),
1097
1118
  };
1098
1119
  for (const [name, content] of Object.entries(helpers)) {
1099
1120
  const filePath = path.join(helpersDir, name);
@@ -58,4 +58,14 @@ export declare function generateCrossPlatformSessionManager(): string;
58
58
  * Generate all helper files
59
59
  */
60
60
  export declare function generateHelpers(options: InitOptions): Record<string, string>;
61
+ /**
62
+ * Generate cross-platform Node.js port of ruflo-hook.sh (#2132).
63
+ *
64
+ * The bash shim works on Mac/Linux but fails on native Windows (exit 126).
65
+ * This .cjs version is always deployed to .claude/helpers/ so:
66
+ * - Windows: settings.json overrides plugin bash hooks with node-based cmds
67
+ * - Mac/Linux: plugin hooks.json still uses .sh (faster, battle-tested)
68
+ * - Both: .claude/helpers/ruflo-hook.cjs available as a canonical cross-platform shim
69
+ */
70
+ export declare function generateRufloHookCjs(): string;
61
71
  //# sourceMappingURL=helpers-generator.d.ts.map
@@ -1195,4 +1195,75 @@ export function generateHelpers(options) {
1195
1195
  }
1196
1196
  return helpers;
1197
1197
  }
1198
+ /**
1199
+ * Generate cross-platform Node.js port of ruflo-hook.sh (#2132).
1200
+ *
1201
+ * The bash shim works on Mac/Linux but fails on native Windows (exit 126).
1202
+ * This .cjs version is always deployed to .claude/helpers/ so:
1203
+ * - Windows: settings.json overrides plugin bash hooks with node-based cmds
1204
+ * - Mac/Linux: plugin hooks.json still uses .sh (faster, battle-tested)
1205
+ * - Both: .claude/helpers/ruflo-hook.cjs available as a canonical cross-platform shim
1206
+ */
1207
+ export function generateRufloHookCjs() {
1208
+ return `#!/usr/bin/env node
1209
+ /**
1210
+ * ruflo-hook.cjs — cross-platform Node.js port of ruflo-hook.sh (#2132)
1211
+ *
1212
+ * Deployed to .claude/helpers/ during ruflo init. On Windows, the
1213
+ * generated .claude/settings.json hooks point here instead of the
1214
+ * plugin's bash-only ruflo-hook.sh.
1215
+ *
1216
+ * Always exits 0 — hook subcommands are best-effort telemetry and must
1217
+ * never block a Claude Code turn.
1218
+ */
1219
+
1220
+ 'use strict';
1221
+
1222
+ const { spawnSync, execSync } = require('child_process');
1223
+ const fs = require('fs');
1224
+
1225
+ function done() { process.exit(0); }
1226
+
1227
+ function commandExists(cmd) {
1228
+ try {
1229
+ const r = execSync(
1230
+ process.platform === 'win32' ? 'where ' + cmd : 'command -v ' + cmd,
1231
+ { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }
1232
+ );
1233
+ return r.trim().length > 0;
1234
+ } catch { return false; }
1235
+ }
1236
+
1237
+ function invokeHook(bin, binArgs, hookArgs, stdinData) {
1238
+ const args = [...binArgs, ...hookArgs];
1239
+ const result = spawnSync(bin, args, {
1240
+ shell: process.platform === 'win32',
1241
+ input: stdinData || '',
1242
+ encoding: 'utf8',
1243
+ stdio: ['pipe', 'ignore', 'ignore'],
1244
+ timeout: 30_000,
1245
+ });
1246
+ return result.status === 0;
1247
+ }
1248
+
1249
+ function main() {
1250
+ const args = process.argv.slice(2);
1251
+ if (args.length === 0) done();
1252
+
1253
+ const [subcommand, ...rest] = args;
1254
+
1255
+ let stdinData = '';
1256
+ try { stdinData = fs.readFileSync(0, 'utf8'); } catch { stdinData = ''; }
1257
+
1258
+ const hookArgs = ['hooks', subcommand, ...rest];
1259
+
1260
+ if (commandExists('ruflo')) { invokeHook('ruflo', [], hookArgs, stdinData); done(); }
1261
+ if (commandExists('claude-flow')) { invokeHook('claude-flow', [], hookArgs, stdinData); done(); }
1262
+ invokeHook('npx', ['--prefer-offline', '--yes', 'ruflo@latest'], hookArgs, stdinData);
1263
+ done();
1264
+ }
1265
+
1266
+ main();
1267
+ `;
1268
+ }
1198
1269
  //# sourceMappingURL=helpers-generator.js.map
@@ -667,7 +667,7 @@ export const coordinationTools = [
667
667
  topology: store.topology.type,
668
668
  // Honest stub: no executor wired up yet. Don't lie about completion time.
669
669
  executor: 'none',
670
- _note: 'coordination_orchestrate currently records the orchestration request but does not execute it. For real multi-agent execution use agent_spawn + the Task tool, or hive-mind_spawn for queen-led coordination.',
670
+ _note: 'coordination_orchestrate currently records the orchestration request but does not execute it. For real multi-agent execution use agent_spawn + the Task tool, or hive-mind_spawn for queen-led coordination. Real executor tracked in issue #2140.',
671
671
  };
672
672
  },
673
673
  },
@@ -87,9 +87,18 @@ export function decodeEmbedding(embeddingRef) {
87
87
  const b64 = embeddingRef.slice(INLINE_PREFIX.length);
88
88
  const raw = Buffer.from(b64, 'base64');
89
89
  const view = new DataView(raw.buffer, raw.byteOffset, raw.byteLength);
90
+ if (raw.byteLength < 16)
91
+ return null; // too short for the header
90
92
  if (view.getUint32(0, true) !== PQ_MAGIC)
91
93
  return null;
92
94
  const dims = view.getUint32(4, true);
95
+ // Validate claimed dims against actual buffer size (#security-review-v3.10):
96
+ // (a) dims=0 or buffer too short -> malformed blob, reject.
97
+ // (b) dims > 8192 -> oversized allocation guard (DoS via crafted blob).
98
+ // Normal production blobs are 384-dim; 8192 is a generous upper bound
99
+ // for any supported model without allowing unbounded allocations.
100
+ if (dims === 0 || dims > 8192 || raw.byteLength < 16 + dims)
101
+ return null;
93
102
  const gMin = view.getFloat32(8, true);
94
103
  const gMax = view.getFloat32(12, true);
95
104
  const range = gMax - gMin;
@@ -11,6 +11,15 @@
11
11
  export declare function getMemoryRoot(): string;
12
12
  /** For tests + the `memory configure` flow that mutates the config at runtime. */
13
13
  export declare function _resetMemoryRootCache(): void;
14
+ /**
15
+ * #2105: Resolve the full path to the SQLite memory database.
16
+ * Precedence (highest to lowest):
17
+ * 1. cliFlag - explicit --path flag passed by a subcommand
18
+ * 2. CLAUDE_FLOW_DB_PATH - full file-path override (new in #2105)
19
+ * 3. getMemoryRoot()/memory.db - directory from CLAUDE_FLOW_MEMORY_PATH /
20
+ * config / default cwd/.swarm
21
+ */
22
+ export declare function resolveDbPath(cliFlag?: string): string;
14
23
  /**
15
24
  * Enhanced schema with pattern confidence, temporal decay, versioning
16
25
  * Vector embeddings enabled for semantic search
@@ -66,6 +66,24 @@ export function getMemoryRoot() {
66
66
  export function _resetMemoryRootCache() {
67
67
  _memoryRootCache = undefined;
68
68
  }
69
+ /**
70
+ * #2105: Resolve the full path to the SQLite memory database.
71
+ * Precedence (highest to lowest):
72
+ * 1. cliFlag - explicit --path flag passed by a subcommand
73
+ * 2. CLAUDE_FLOW_DB_PATH - full file-path override (new in #2105)
74
+ * 3. getMemoryRoot()/memory.db - directory from CLAUDE_FLOW_MEMORY_PATH /
75
+ * config / default cwd/.swarm
76
+ */
77
+ export function resolveDbPath(cliFlag) {
78
+ if (cliFlag && cliFlag.trim().length > 0) {
79
+ return path.resolve(cliFlag);
80
+ }
81
+ const envDb = process.env.CLAUDE_FLOW_DB_PATH;
82
+ if (envDb && envDb.trim().length > 0) {
83
+ return path.resolve(envDb);
84
+ }
85
+ return path.join(getMemoryRoot(), 'memory.db');
86
+ }
69
87
  // ADR-053: Lazy import of AgentDB v3 bridge
70
88
  let _bridge;
71
89
  async function getBridge() {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-flow/cli",
3
- "version": "3.10.0",
3
+ "version": "3.10.3",
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",
@@ -102,10 +102,7 @@
102
102
  "@noble/ed25519": "^2.1.0",
103
103
  "@ruvector/rabitq-wasm": "^0.1.0",
104
104
  "semver": "^7.6.0",
105
- "yaml": "^2.8.0",
106
- "@claude-flow/memory": "^3.0.0-alpha.17",
107
- "@claude-flow/embeddings": "^3.0.0-alpha.18",
108
- "@claude-flow/security": "^3.0.0-alpha.8"
105
+ "yaml": "^2.8.0"
109
106
  },
110
107
  "optionalDependencies": {
111
108
  "@claude-flow/aidefence": "^3.0.2",
@@ -129,5 +126,8 @@
129
126
  "publishConfig": {
130
127
  "access": "public",
131
128
  "tag": "latest"
129
+ },
130
+ "overrides": {
131
+ "protobufjs": ">=7.5.6"
132
132
  }
133
133
  }