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.
- package/.claude-plugin/hooks/hooks.json +2 -0
- package/.claude-plugin/scripts/ruflo-hook.cjs +166 -0
- package/README.md +1 -0
- package/package.json +1 -1
- package/v3/@claude-flow/cli/README.md +1 -0
- package/v3/@claude-flow/cli/dist/src/commands/analyze.js +0 -1
- package/v3/@claude-flow/cli/dist/src/commands/memory.js +37 -15
- package/v3/@claude-flow/cli/dist/src/init/executor.js +22 -1
- package/v3/@claude-flow/cli/dist/src/init/helpers-generator.d.ts +10 -0
- package/v3/@claude-flow/cli/dist/src/init/helpers-generator.js +71 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/coordination-tools.js +1 -1
- package/v3/@claude-flow/cli/dist/src/memory/embedding-quantization.js +9 -0
- package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.d.ts +9 -0
- package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.js +18 -0
- package/v3/@claude-flow/cli/package.json +5 -5
|
@@ -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.
|
|
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
|
|
|
@@ -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
|
|
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
|
|
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
|
|
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.
|
|
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
|
}
|