@claude-flow/cli 3.57.0 → 3.57.2
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/helpers/.helpers-version +1 -1
- package/.claude/helpers/helpers.manifest.json +3 -3
- package/.claude/helpers/intelligence.cjs +3 -5
- package/README.md +12 -3
- package/catalog-manifest.json +2 -2
- package/dist/src/commands/hooks.js +20 -1
- package/dist/src/commands/memory.js +25 -1
- package/dist/src/commands/workflow.js +55 -15
- package/dist/src/init/executor.js +9 -5
- package/dist/src/init/helper-companions.d.ts +1 -1
- package/dist/src/init/helper-companions.js +4 -3
- package/dist/src/mcp-tools/hooks-tools.js +9 -3
- package/dist/src/mcp-tools/memory-tools.js +58 -9
- package/dist/src/mcp-tools/workflow-tools.js +2 -1
- package/dist/src/memory/graph-edge-writer.d.ts +16 -4
- package/dist/src/memory/graph-edge-writer.js +19 -7
- package/dist/src/memory/intelligence.js +4 -0
- package/dist/src/memory/memory-initializer.js +12 -5
- package/dist/src/missions/schemas.d.ts +113 -113
- package/dist/src/mods/command-registry/schema.d.ts +193 -193
- package/dist/src/services/memory-backup.d.ts +6 -0
- package/dist/src/services/memory-backup.js +109 -81
- package/dist/src/services/policy-ledger-anchor.d.ts +2 -0
- package/dist/src/services/policy-ledger-anchor.js +4 -4
- package/dist/src/services/policy-runtime.js +54 -3
- package/dist/src/services/worker-daemon.js +4 -0
- package/node_modules/@claude-flow/codex/README.md +9 -9
- package/node_modules/@claude-flow/codex/dist/cli.js +0 -0
- package/node_modules/@claude-flow/codex/dist/dual-mode/cli.js +3 -3
- package/node_modules/@claude-flow/codex/dist/dual-mode/cli.js.map +1 -1
- package/node_modules/@claude-flow/plugin-agent-federation/dist/bin.js +0 -0
- package/package.json +5 -4
- package/plugins/ruflo-console/dist/tui.mjs +158 -158
|
@@ -1 +1 @@
|
|
|
1
|
-
3.57.
|
|
1
|
+
3.57.1
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest": {
|
|
3
|
-
"version": "3.57.
|
|
3
|
+
"version": "3.57.2",
|
|
4
4
|
"files": {
|
|
5
5
|
"auto-memory-hook.mjs": "631fac9d518835e2852a406888316bbbddddb49e7ed8e406d2e971f46833df9a",
|
|
6
6
|
"hook-handler.cjs": "bc12b3cae92d5a7c8deef699fa00d0ef07b57625c18dbeac4387f15725b8e09f",
|
|
7
|
-
"intelligence.cjs": "
|
|
7
|
+
"intelligence.cjs": "1d981d6c6a35d70a66450e96458e26169e0cede11523ea246a2c129c197a5b4e",
|
|
8
8
|
"statusline.cjs": "d72417ecb38634011a19f095a30e07b9929e6f97663520172ece9be7491b2eaa",
|
|
9
9
|
"router.cjs": "16648b3c0d73b6681ac2d8d13def6a68286c57f5a11004de5b19a0fa088c833d"
|
|
10
10
|
}
|
|
11
11
|
},
|
|
12
|
-
"signature": "
|
|
12
|
+
"signature": "1NC9a0vJ78KCh8TIZfhExMq0923sXMkAej7Dxg4hlTqmOBzS1Gn9ex7z1lpkCTOlVafXVVXXMsEa1BSdsvQ7CQ==",
|
|
13
13
|
"algorithm": "ed25519"
|
|
14
14
|
}
|
|
@@ -277,11 +277,9 @@ function sessionGet(key) {
|
|
|
277
277
|
|
|
278
278
|
function sessionSet(key, value) {
|
|
279
279
|
try {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
284
|
-
}
|
|
280
|
+
// Session creation belongs to session.cjs; recall may run before SessionStart.
|
|
281
|
+
if (!fs.existsSync(SESSION_FILE)) return;
|
|
282
|
+
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
285
283
|
if (!session.context) session.context = {};
|
|
286
284
|
session.context[key] = value;
|
|
287
285
|
session.updatedAt = new Date().toISOString();
|
package/README.md
CHANGED
|
@@ -10,10 +10,11 @@
|
|
|
10
10
|
[](https://github.com/ruvnet/claude-flow)
|
|
11
11
|
|
|
12
12
|
<p align="center">
|
|
13
|
-
<a href="
|
|
13
|
+
<a href="docs/harness-origins.md"><img src="https://raw.githubusercontent.com/ruvnet/ruflo/e69e4b95aa08d65ea0f7715489bbf8e62a83a0d2/docs/assets/readme/badges/first-commit.svg" width="300" height="76" alt="Claude Flow lineage: first commit June 2, 2025 by rUv; inspect the evidence"></a>
|
|
14
|
+
<a href="data/npm-downloads.latest.json"><img src="https://raw.githubusercontent.com/ruvnet/ruflo/275aff65a198af758923669acff36f03d1f41623/docs/assets/readme/badges/downloads.svg" width="300" height="76" alt="Illustrative download counter starts at 98,000,000 and adds varied amounts each second in a 30 second loop. Verified dataset: 97,022,120 download events."></a>
|
|
14
15
|
<br>
|
|
15
|
-
<a href="https://github.com/ruvnet/claude-flow"><img src="docs/assets/readme/badges/claude.svg
|
|
16
|
-
<a href="https://www.npmjs.com/package/@claude-flow/codex"><img src="docs/assets/readme/badges/codex.svg
|
|
16
|
+
<a href="https://github.com/ruvnet/claude-flow"><img src="https://raw.githubusercontent.com/ruvnet/ruflo/d1e80400bdefe01d3b13e70395f88fffab76bb18/docs/assets/readme/badges/claude.svg" width="300" height="76" alt="Claude Code plugin"></a>
|
|
17
|
+
<a href="https://www.npmjs.com/package/@claude-flow/codex"><img src="https://raw.githubusercontent.com/ruvnet/ruflo/d1e80400bdefe01d3b13e70395f88fffab76bb18/docs/assets/readme/badges/codex.svg" width="300" height="76" alt="Codex plugin"></a>
|
|
17
18
|
</p>
|
|
18
19
|
|
|
19
20
|
</div>
|
|
@@ -109,6 +110,14 @@ User → Ruflo (CLI/MCP) → Router → Swarm → Agents → Memory → LLM prov
|
|
|
109
110
|
|
|
110
111
|
> **New to Ruflo?** You don't need to learn 314 MCP tools or 26 CLI commands. After `init`, just use Claude Code normally — the hooks system automatically routes tasks, learns from successful patterns, and coordinates agents in the background.
|
|
111
112
|
|
|
113
|
+
### An origin you can inspect
|
|
114
|
+
|
|
115
|
+
[](docs/harness-origins.md)
|
|
116
|
+
|
|
117
|
+
**Built by [Reuven Cohen (rUv)](https://github.com/ruvnet), originally as Claude Flow.** The [first commit, June 2, 2025](https://github.com/ruvnet/ruflo/commit/7c1f9402d70e7af56c01a879073cbac92564dc4e), describes a code orchestration layer for recursive agent cycles: Claude writes, edits, tests and improves. The [June 10 source snapshot](https://github.com/ruvnet/ruflo/tree/dba545f012ea92a2375a26332bbf3797495e93c9) adds orchestration, task scheduling, memory and terminal modules.
|
|
118
|
+
|
|
119
|
+
A harness is the workbench around the model: it organizes tasks, carries context between steps and helps check results. **This establishes Ruflo's lineage, not a universal first across every Claude Code tool.** [See the illustrated explainer, exact commits and earlier ecosystem context](docs/harness-origins.md).
|
|
120
|
+
|
|
112
121
|
<details>
|
|
113
122
|
<summary><strong>📖 Background — where the name comes from</strong></summary>
|
|
114
123
|
|
package/catalog-manifest.json
CHANGED
|
@@ -2333,6 +2333,19 @@ const workerListCommand = {
|
|
|
2333
2333
|
}
|
|
2334
2334
|
}
|
|
2335
2335
|
};
|
|
2336
|
+
// Render the status hooks_worker-dispatch actually returned. Only a status that
|
|
2337
|
+
// means work finished may read as "completed" (#4196).
|
|
2338
|
+
function dispatchStatusLabel(status) {
|
|
2339
|
+
switch (status) {
|
|
2340
|
+
case 'dispatched': return output.highlight('dispatched (background)');
|
|
2341
|
+
case 'queued': return output.highlight('queued (daemon)');
|
|
2342
|
+
case 'no-daemon': return output.warning('no-daemon (not run)');
|
|
2343
|
+
case 'mcp-only': return output.warning('mcp-only (not queued)');
|
|
2344
|
+
case 'synthetic-completed': return output.warning('recorded (no work executed)');
|
|
2345
|
+
case 'completed': return output.success('completed');
|
|
2346
|
+
default: return output.dim(String(status));
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2336
2349
|
const workerDispatchCommand = {
|
|
2337
2350
|
name: 'dispatch',
|
|
2338
2351
|
description: 'Dispatch a background worker for analysis/optimization',
|
|
@@ -2386,9 +2399,15 @@ const workerDispatchCommand = {
|
|
|
2386
2399
|
{ field: 'Description', value: result.config.description },
|
|
2387
2400
|
{ field: 'Est. Duration', value: result.config.estimatedDuration },
|
|
2388
2401
|
{ field: 'Capabilities', value: result.config.capabilities.join(', ') },
|
|
2389
|
-
{ field: 'Status', value: result.status
|
|
2402
|
+
{ field: 'Status', value: dispatchStatusLabel(result.status) },
|
|
2390
2403
|
],
|
|
2391
2404
|
});
|
|
2405
|
+
// The tool says why it could not run the worker (e.g. no daemon); show it
|
|
2406
|
+
// rather than letting a recorded-only dispatch read as finished work (#4196).
|
|
2407
|
+
if (result.note) {
|
|
2408
|
+
output.writeln();
|
|
2409
|
+
output.printWarning(result.note);
|
|
2410
|
+
}
|
|
2392
2411
|
if (background) {
|
|
2393
2412
|
output.writeln();
|
|
2394
2413
|
output.writeln(output.dim(`Check status: claude-flow hooks worker status --id ${result.workerId}`));
|
|
@@ -659,7 +659,9 @@ const searchCommand = {
|
|
|
659
659
|
});
|
|
660
660
|
return {
|
|
661
661
|
results: r.results.map(e => ({
|
|
662
|
-
|
|
662
|
+
// #2271: searchEntries() ids are truncated to 12 chars and collide
|
|
663
|
+
// for entries written within ~2.8h; SmartRetrieval dedupes on id.
|
|
664
|
+
id: JSON.stringify([e.namespace, e.key]),
|
|
663
665
|
key: e.key,
|
|
664
666
|
content: e.content,
|
|
665
667
|
score: e.score,
|
|
@@ -1592,6 +1594,13 @@ const exportCommand = {
|
|
|
1592
1594
|
namespace: ctx.flags.namespace,
|
|
1593
1595
|
includeVectors: ctx.flags.includeVectors ?? true,
|
|
1594
1596
|
});
|
|
1597
|
+
// memory_export reports failure as { error } rather than throwing; without
|
|
1598
|
+
// this check the command printed "Exported to undefined" and then crashed
|
|
1599
|
+
// reading result.exported.entries (#4204).
|
|
1600
|
+
if (!result || result.error || !result.exported) {
|
|
1601
|
+
output.printError(`Export error: ${result?.error ?? 'memory_export returned no export summary'}`);
|
|
1602
|
+
return { success: false, exitCode: 1 };
|
|
1603
|
+
}
|
|
1595
1604
|
output.printSuccess(`Exported to ${result.outputPath}`);
|
|
1596
1605
|
output.printList([
|
|
1597
1606
|
`Entries: ${result.exported.entries}`,
|
|
@@ -1657,6 +1666,21 @@ const importCommand = {
|
|
|
1657
1666
|
namespace: ctx.flags.namespace,
|
|
1658
1667
|
dbPath: resolveDbPath(ctx.flags.path),
|
|
1659
1668
|
});
|
|
1669
|
+
// memory_import reports a bad file as { error } rather than throwing, and
|
|
1670
|
+
// a partial write as { success: false, error, imported }. Without this the
|
|
1671
|
+
// command printed "Imported from undefined" and crashed on result.imported,
|
|
1672
|
+
// or reported success for an import that dropped entries.
|
|
1673
|
+
const failure = result?.error;
|
|
1674
|
+
if (!result || failure || result.success === false || !result.imported) {
|
|
1675
|
+
output.printError(`Import error: ${failure ?? 'memory_import returned no import summary'}`);
|
|
1676
|
+
if (result?.imported) {
|
|
1677
|
+
output.printList([
|
|
1678
|
+
`Entries imported: ${result.imported.entries}`,
|
|
1679
|
+
`Failed: ${result.failed ?? 'unknown'}`,
|
|
1680
|
+
]);
|
|
1681
|
+
}
|
|
1682
|
+
return { success: false, exitCode: 1 };
|
|
1683
|
+
}
|
|
1660
1684
|
output.printSuccess(`Imported from ${result.inputPath}`);
|
|
1661
1685
|
output.printList([
|
|
1662
1686
|
`Entries: ${result.imported.entries}`,
|
|
@@ -111,6 +111,12 @@ const runCommand = {
|
|
|
111
111
|
dryRun,
|
|
112
112
|
},
|
|
113
113
|
});
|
|
114
|
+
const runError = toolError(result);
|
|
115
|
+
if (runError || !result.metrics) {
|
|
116
|
+
spinner.fail('Workflow failed');
|
|
117
|
+
output.printError(`Workflow error: ${runError ?? 'workflow_run returned no workflow summary'}`);
|
|
118
|
+
return { success: false, exitCode: 1 };
|
|
119
|
+
}
|
|
114
120
|
if (dryRun) {
|
|
115
121
|
spinner.succeed('Workflow validated successfully');
|
|
116
122
|
}
|
|
@@ -277,6 +283,11 @@ const listCommand = {
|
|
|
277
283
|
status: status || 'all',
|
|
278
284
|
limit,
|
|
279
285
|
});
|
|
286
|
+
const listError = toolError(result);
|
|
287
|
+
if (listError || !Array.isArray(result.workflows)) {
|
|
288
|
+
output.printError(`Failed to list workflows: ${listError ?? 'workflow_list returned no workflows'}`);
|
|
289
|
+
return { success: false, exitCode: 1 };
|
|
290
|
+
}
|
|
280
291
|
if (ctx.flags.format === 'json') {
|
|
281
292
|
output.printJson(result);
|
|
282
293
|
return { success: true, data: result };
|
|
@@ -290,13 +301,19 @@ const listCommand = {
|
|
|
290
301
|
}
|
|
291
302
|
output.printTable({
|
|
292
303
|
columns: [
|
|
293
|
-
{ key: 'id', header: 'ID', width:
|
|
294
|
-
{ key: '
|
|
304
|
+
{ key: 'id', header: 'ID', width: 30 },
|
|
305
|
+
{ key: 'name', header: 'Name', width: 24 },
|
|
295
306
|
{ key: 'status', header: 'Status', width: 12, format: formatStageStatus },
|
|
296
|
-
{ key: '
|
|
297
|
-
{ key: '
|
|
307
|
+
{ key: 'steps', header: 'Steps', width: 6, align: 'right' },
|
|
308
|
+
{ key: 'createdAt', header: 'Created', width: 20, format: (v) => new Date(String(v)).toLocaleString() }
|
|
298
309
|
],
|
|
299
|
-
data: result.workflows
|
|
310
|
+
data: result.workflows.map(w => ({
|
|
311
|
+
id: w.workflowId,
|
|
312
|
+
name: w.name,
|
|
313
|
+
status: w.status,
|
|
314
|
+
steps: w.stepCount,
|
|
315
|
+
createdAt: w.createdAt,
|
|
316
|
+
}))
|
|
300
317
|
});
|
|
301
318
|
output.writeln();
|
|
302
319
|
output.printInfo(`Total: ${result.total} workflows`);
|
|
@@ -335,30 +352,37 @@ const statusCommand = {
|
|
|
335
352
|
try {
|
|
336
353
|
const result = await callMCPTool('workflow_status', {
|
|
337
354
|
workflowId,
|
|
355
|
+
verbose: true,
|
|
338
356
|
});
|
|
357
|
+
const statusError = toolError(result);
|
|
358
|
+
if (statusError) {
|
|
359
|
+
output.printError(`Failed to get status: ${statusError}`);
|
|
360
|
+
return { success: false, exitCode: 1 };
|
|
361
|
+
}
|
|
339
362
|
if (ctx.flags.format === 'json') {
|
|
340
363
|
output.printJson(result);
|
|
341
364
|
return { success: true, data: result };
|
|
342
365
|
}
|
|
366
|
+
const started = result.startedAt ? Date.parse(result.startedAt) : NaN;
|
|
367
|
+
const ended = result.completedAt ? Date.parse(result.completedAt) : Date.now();
|
|
343
368
|
output.writeln();
|
|
344
369
|
output.printBox([
|
|
345
|
-
`ID: ${result.
|
|
346
|
-
`
|
|
370
|
+
`ID: ${result.workflowId}`,
|
|
371
|
+
`Name: ${result.name}`,
|
|
347
372
|
`Status: ${formatStageStatus(result.status)}`,
|
|
348
|
-
`Progress: ${result.progress}
|
|
349
|
-
`Duration: ${(
|
|
350
|
-
`
|
|
351
|
-
`Agents: ${result.metrics.agentsSpawned}`
|
|
373
|
+
`Progress: ${Math.round(result.progress)}% (${result.completedSteps}/${result.totalSteps} steps)`,
|
|
374
|
+
...(Number.isNaN(started) ? [] : [`Duration: ${((ended - started) / 1000).toFixed(1)}s`]),
|
|
375
|
+
...(result.error ? [`Error: ${result.error}`] : []),
|
|
352
376
|
].join('\n'), 'Workflow Status');
|
|
353
377
|
output.writeln();
|
|
354
|
-
output.writeln(output.bold('
|
|
378
|
+
output.writeln(output.bold('Step Progress'));
|
|
355
379
|
output.printTable({
|
|
356
380
|
columns: [
|
|
357
|
-
{ key: 'name', header: '
|
|
381
|
+
{ key: 'name', header: 'Step', width: 24 },
|
|
382
|
+
{ key: 'type', header: 'Type', width: 10 },
|
|
358
383
|
{ key: 'status', header: 'Status', width: 12, format: formatStageStatus },
|
|
359
|
-
{ key: 'agents', header: 'Agents', width: 25, format: (v) => Array.isArray(v) ? v.length.toString() : '0' }
|
|
360
384
|
],
|
|
361
|
-
data: result.
|
|
385
|
+
data: result.steps ?? []
|
|
362
386
|
});
|
|
363
387
|
return { success: true, data: result };
|
|
364
388
|
}
|
|
@@ -408,6 +432,11 @@ const stopCommand = {
|
|
|
408
432
|
workflowId,
|
|
409
433
|
graceful: !force,
|
|
410
434
|
});
|
|
435
|
+
const stopError = toolError(result);
|
|
436
|
+
if (stopError || result.stopped !== true) {
|
|
437
|
+
output.printError(`Failed to stop workflow: ${stopError ?? 'the workflow was not stopped'}`);
|
|
438
|
+
return { success: false, exitCode: 1 };
|
|
439
|
+
}
|
|
411
440
|
output.printSuccess(`Workflow ${workflowId} stopped`);
|
|
412
441
|
return { success: true, data: result };
|
|
413
442
|
}
|
|
@@ -556,6 +585,17 @@ export const workflowCommand = {
|
|
|
556
585
|
}
|
|
557
586
|
};
|
|
558
587
|
// Helper functions
|
|
588
|
+
/**
|
|
589
|
+
* The workflow MCP tools report failure as `{ error }` (or `{ success: false, error }`)
|
|
590
|
+
* instead of throwing, so a command that reads the result without checking it prints
|
|
591
|
+
* a false success or crashes on a field the error result does not have.
|
|
592
|
+
*/
|
|
593
|
+
function toolError(result) {
|
|
594
|
+
const r = result;
|
|
595
|
+
if (!r)
|
|
596
|
+
return 'the workflow tool returned no result';
|
|
597
|
+
return typeof r.error === 'string' && r.error.length > 0 ? r.error : undefined;
|
|
598
|
+
}
|
|
559
599
|
function formatStageStatus(status) {
|
|
560
600
|
const statusStr = String(status);
|
|
561
601
|
switch (statusStr) {
|
|
@@ -18,7 +18,7 @@ import { generatePreCommitHook, generatePostCommitHook, generateSessionManager,
|
|
|
18
18
|
import { getInstalledCliVersion, HELPERS_STAMP_FILE } from './helper-refresh.js';
|
|
19
19
|
import { generateClaudeMd } from './claudemd-generator.js';
|
|
20
20
|
import { recordMemoryPackagePath } from './memory-package-resolver.js';
|
|
21
|
-
import { ensureCommonJsCompanions } from './helper-companions.js';
|
|
21
|
+
import { COMMONJS_COMPANION_HELPERS, ensureCommonJsCompanions } from './helper-companions.js';
|
|
22
22
|
import { scanSettingsForRisk, formatRiskFindingsAsWarnings } from './settings-risk-scanner.js';
|
|
23
23
|
import { AUTO_MEMORY_HOOK_TIMEOUT_S, helperHookCommand, helperScopeFor, helperStatusLineCommand, pinToUserHelpers } from './helper-commands.js';
|
|
24
24
|
/**
|
|
@@ -548,10 +548,14 @@ export async function executeUpgrade(targetDir, upgradeSettings = false) {
|
|
|
548
548
|
catch { }
|
|
549
549
|
}
|
|
550
550
|
}
|
|
551
|
-
// #
|
|
552
|
-
//
|
|
553
|
-
|
|
554
|
-
|
|
551
|
+
// #4198: refresh existing companions too, so upgraded hook handlers never
|
|
552
|
+
// keep obsolete session/memory logic. Generate from the CLI trust root.
|
|
553
|
+
const helpersDir = path.join(targetDir, '.claude', 'helpers');
|
|
554
|
+
const existingCompanions = new Set(COMMONJS_COMPANION_HELPERS.filter((name) => fs.existsSync(path.join(helpersDir, name))));
|
|
555
|
+
for (const name of await ensureCommonJsCompanions(helpersDir, true)) {
|
|
556
|
+
const changes = existingCompanions.has(name)
|
|
557
|
+
? result.updated : result.created;
|
|
558
|
+
changes.push(`.claude/helpers/${name}`);
|
|
555
559
|
}
|
|
556
560
|
// Stamp the installed version so the startup auto-refresh treats these as
|
|
557
561
|
// current (no redundant re-copy on the next command).
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export declare const COMMONJS_COMPANION_HELPERS: readonly ["session.cjs", "memory.cjs"];
|
|
2
|
-
export declare function ensureCommonJsCompanions(helpersDir: string): Promise<string[]>;
|
|
2
|
+
export declare function ensureCommonJsCompanions(helpersDir: string, force?: boolean): Promise<string[]>;
|
|
3
3
|
//# sourceMappingURL=helper-companions.d.ts.map
|
|
@@ -7,13 +7,14 @@
|
|
|
7
7
|
* `.js` copies, which the refreshed hook-handler no longer requires, so the
|
|
8
8
|
* refresh writes any missing `.cjs` companion from the CLI's own compiled
|
|
9
9
|
* generators (the trust root; no external file to verify). Stale `.js` copies
|
|
10
|
-
* are left in place and ignored. Existing `.cjs` files are
|
|
10
|
+
* are left in place and ignored. Existing `.cjs` files are preserved during
|
|
11
|
+
* startup refresh; an explicit init upgrade replaces them from the generators.
|
|
11
12
|
*/
|
|
12
13
|
import * as fs from 'fs';
|
|
13
14
|
import * as path from 'path';
|
|
14
15
|
export const COMMONJS_COMPANION_HELPERS = ['session.cjs', 'memory.cjs'];
|
|
15
|
-
export async function ensureCommonJsCompanions(helpersDir) {
|
|
16
|
-
const missing = COMMONJS_COMPANION_HELPERS.filter((name) => !fs.existsSync(path.join(helpersDir, name)));
|
|
16
|
+
export async function ensureCommonJsCompanions(helpersDir, force = false) {
|
|
17
|
+
const missing = COMMONJS_COMPANION_HELPERS.filter((name) => force || !fs.existsSync(path.join(helpersDir, name)));
|
|
17
18
|
if (missing.length === 0)
|
|
18
19
|
return [];
|
|
19
20
|
const gen = await import('./helpers-generator.js');
|
|
@@ -4175,12 +4175,12 @@ export const hooksIntelligenceLearn = {
|
|
|
4175
4175
|
// Intelligence attention hook
|
|
4176
4176
|
export const hooksIntelligenceAttention = {
|
|
4177
4177
|
name: 'hooks_intelligence_attention',
|
|
4178
|
-
description: 'Compute attention-weighted similarity using MoE/Flash
|
|
4178
|
+
description: 'Compute attention-weighted similarity using MoE/Flash. Use when native Bash hooks (via Claude Code\'s settings.json) are wrong because you need Ruflo-side state — pattern persistence, neural training signals, model-routing learning, cost tracking, audit chain. For one-off shell commands, plain Bash hooks are fine.',
|
|
4179
4179
|
inputSchema: {
|
|
4180
4180
|
type: 'object',
|
|
4181
4181
|
properties: {
|
|
4182
4182
|
query: { type: 'string', description: 'Query for attention computation' },
|
|
4183
|
-
mode: { type: 'string', description: 'Attention mode (flash, moe
|
|
4183
|
+
mode: { type: 'string', description: 'Attention mode (flash, moe)' },
|
|
4184
4184
|
topK: { type: 'number', description: 'Top-k results' },
|
|
4185
4185
|
},
|
|
4186
4186
|
required: ['query'],
|
|
@@ -4195,6 +4195,12 @@ export const hooksIntelligenceAttention = {
|
|
|
4195
4195
|
if (!v.valid)
|
|
4196
4196
|
return { success: false, error: v.error };
|
|
4197
4197
|
}
|
|
4198
|
+
// #3548: only flash and moe have an implementation. Any other mode
|
|
4199
|
+
// (including the previously advertised 'hyperbolic') used to return an
|
|
4200
|
+
// empty stub whose note blamed a missing package; say so instead.
|
|
4201
|
+
if (mode !== 'flash' && mode !== 'moe') {
|
|
4202
|
+
return { success: false, error: `Attention mode "${mode}" is not implemented; supported modes: flash, moe` };
|
|
4203
|
+
}
|
|
4198
4204
|
let implementation = 'placeholder';
|
|
4199
4205
|
let embeddingSource = 'none';
|
|
4200
4206
|
const results = [];
|
|
@@ -4370,7 +4376,7 @@ export const hooksIntelligenceAttention = {
|
|
|
4370
4376
|
implementation,
|
|
4371
4377
|
_embeddingSource: embeddingSource,
|
|
4372
4378
|
_stub: implementation === 'none',
|
|
4373
|
-
_note: implementation === 'none' ? 'No attention backend available
|
|
4379
|
+
_note: implementation === 'none' ? 'No attention backend available: flash/moe attention from @claude-flow/neural could not be loaded or failed.' : undefined,
|
|
4374
4380
|
...(embeddingSource === 'hash-fallback' && implementation !== 'none'
|
|
4375
4381
|
? { _embeddingNote: 'Query embeddings are hash-based (not semantic). Install @claude-flow/embeddings for real ONNX embeddings.' }
|
|
4376
4382
|
: {}),
|
|
@@ -227,15 +227,31 @@ function loadLegacyStore() {
|
|
|
227
227
|
/**
|
|
228
228
|
* Mark migration as complete
|
|
229
229
|
*/
|
|
230
|
-
function markMigrationComplete() {
|
|
230
|
+
function markMigrationComplete(counts) {
|
|
231
231
|
const legacyDir = resolve(LEGACY_MEMORY_DIR);
|
|
232
232
|
if (!existsSync(legacyDir))
|
|
233
233
|
mkdirSync(legacyDir, { recursive: true });
|
|
234
234
|
writeFileSync(resolve(join(LEGACY_MEMORY_DIR, MIGRATION_MARKER)), JSON.stringify({
|
|
235
235
|
migratedAt: new Date().toISOString(),
|
|
236
236
|
version: '3.0.0',
|
|
237
|
+
...(counts ?? {}),
|
|
237
238
|
}), 'utf-8');
|
|
238
239
|
}
|
|
240
|
+
/**
|
|
241
|
+
* Counts recorded by the migration that wrote the marker, or null when no
|
|
242
|
+
* marker (or an older marker without counts) is present.
|
|
243
|
+
*/
|
|
244
|
+
function readMigrationCounts() {
|
|
245
|
+
try {
|
|
246
|
+
const marker = JSON.parse(readFileSync(resolve(join(LEGACY_MEMORY_DIR, MIGRATION_MARKER)), 'utf-8'));
|
|
247
|
+
return typeof marker?.migrated === 'number' && typeof marker?.total === 'number'
|
|
248
|
+
? { migrated: marker.migrated, total: marker.total }
|
|
249
|
+
: null;
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
239
255
|
/**
|
|
240
256
|
* Lazy-load memory initializer functions to avoid circular deps
|
|
241
257
|
*/
|
|
@@ -391,7 +407,7 @@ async function ensureInitialized(dbPath) {
|
|
|
391
407
|
}
|
|
392
408
|
}
|
|
393
409
|
console.error(`[MCP Memory] Migrated ${migrated}/${Object.keys(legacyStore.entries).length} entries`);
|
|
394
|
-
markMigrationComplete();
|
|
410
|
+
markMigrationComplete({ migrated, total: Object.keys(legacyStore.entries).length });
|
|
395
411
|
}
|
|
396
412
|
}
|
|
397
413
|
}
|
|
@@ -636,7 +652,11 @@ export const memoryTools = [
|
|
|
636
652
|
});
|
|
637
653
|
return {
|
|
638
654
|
results: r.results.map(e => ({
|
|
639
|
-
|
|
655
|
+
// #2271: searchEntries() truncates ids to 12 chars ("entry_" +
|
|
656
|
+
// the leading digits of Date.now()), so entries written within
|
|
657
|
+
// ~2.8h share one id and RRF fused them into a single result.
|
|
658
|
+
// SmartRetrieval dedupes on id: give it the (namespace, key) tuple.
|
|
659
|
+
id: JSON.stringify([e.namespace, e.key]),
|
|
640
660
|
key: e.key,
|
|
641
661
|
content: e.content,
|
|
642
662
|
score: e.score,
|
|
@@ -966,11 +986,17 @@ export const memoryTools = [
|
|
|
966
986
|
},
|
|
967
987
|
handler: async (input) => {
|
|
968
988
|
const force = input.force;
|
|
969
|
-
// Remove migration marker if forcing
|
|
989
|
+
// Remove the migration marker if forcing. hasLegacyStore() checks the marker
|
|
990
|
+
// beside the legacy store, so that is the one force must clear (#4194);
|
|
991
|
+
// the old .swarm marker path is cleared too for compatibility.
|
|
970
992
|
if (force) {
|
|
971
|
-
const markerPath
|
|
972
|
-
|
|
973
|
-
|
|
993
|
+
for (const markerPath of [
|
|
994
|
+
resolve(join(LEGACY_MEMORY_DIR, MIGRATION_MARKER)),
|
|
995
|
+
getMigrationMarkerPath(),
|
|
996
|
+
]) {
|
|
997
|
+
if (existsSync(markerPath)) {
|
|
998
|
+
unlinkSync(markerPath);
|
|
999
|
+
}
|
|
974
1000
|
}
|
|
975
1001
|
}
|
|
976
1002
|
// Check for legacy data
|
|
@@ -982,12 +1008,35 @@ export const memoryTools = [
|
|
|
982
1008
|
migrated: 0,
|
|
983
1009
|
};
|
|
984
1010
|
}
|
|
1011
|
+
// A marker beside the store means a migration already ran; ensureInitialized
|
|
1012
|
+
// would move nothing, so say so instead of reporting the store's size.
|
|
1013
|
+
if (!hasLegacyStore()) {
|
|
1014
|
+
return {
|
|
1015
|
+
success: true,
|
|
1016
|
+
message: 'Legacy store already migrated (marker present); nothing was moved. Pass force: true to re-run.',
|
|
1017
|
+
migrated: 0,
|
|
1018
|
+
total: Object.keys(legacyStore.entries).length,
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
985
1021
|
// Run migration via ensureInitialized
|
|
986
1022
|
await ensureInitialized();
|
|
1023
|
+
// ensureInitialized swallows errors and records what it moved in the marker.
|
|
1024
|
+
const counts = readMigrationCounts();
|
|
1025
|
+
if (!counts) {
|
|
1026
|
+
return {
|
|
1027
|
+
success: false,
|
|
1028
|
+
message: 'Migration did not complete; no entries were recorded as migrated. See the MCP server log.',
|
|
1029
|
+
migrated: 0,
|
|
1030
|
+
total: Object.keys(legacyStore.entries).length,
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
987
1033
|
return {
|
|
988
1034
|
success: true,
|
|
989
|
-
message:
|
|
990
|
-
|
|
1035
|
+
message: counts.migrated === counts.total
|
|
1036
|
+
? 'Migration completed'
|
|
1037
|
+
: `Migration completed with failures: ${counts.migrated} of ${counts.total} entries migrated`,
|
|
1038
|
+
migrated: counts.migrated,
|
|
1039
|
+
total: counts.total,
|
|
991
1040
|
backend: await describeBackend(),
|
|
992
1041
|
};
|
|
993
1042
|
},
|
|
@@ -577,7 +577,8 @@ export const workflowTools = [
|
|
|
577
577
|
const store = loadWorkflowStore();
|
|
578
578
|
let workflows = Object.values(store.workflows);
|
|
579
579
|
// Apply filters
|
|
580
|
-
|
|
580
|
+
// 'all' is the CLI's explicit "no filter" value (`workflow list` sends it by default).
|
|
581
|
+
if (input.status && input.status !== 'all') {
|
|
581
582
|
workflows = workflows.filter(w => w.status === input.status);
|
|
582
583
|
}
|
|
583
584
|
// Sort by creation date (newest first)
|
|
@@ -30,10 +30,12 @@
|
|
|
30
30
|
* @module v3/cli/memory/graph-edge-writer
|
|
31
31
|
*/
|
|
32
32
|
/**
|
|
33
|
-
* True iff the memory.db bridge at `customDbPath`
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
33
|
+
* True iff the memory.db bridge at `customDbPath` — or, when omitted, the
|
|
34
|
+
* path `resolveDbPath()` resolves from the `CLAUDE_FLOW_DB_PATH` env binding
|
|
35
|
+
* / memory root default (#4291) — is currently RFE1-encrypted-at-rest.
|
|
36
|
+
* Callers that receive `null` from getBridgeDb()/insertGraphEdge() can use
|
|
37
|
+
* this to avoid recommending the destructive `memory init` command for this
|
|
38
|
+
* specific, non-corrupt case.
|
|
37
39
|
*/
|
|
38
40
|
export declare function isBridgeDbEncryptedAtRest(customDbPath?: string): boolean;
|
|
39
41
|
/** Test-only: clear the once-per-path warning dedup set. */
|
|
@@ -60,6 +62,16 @@ export declare function releaseBridgeDb(dbPath?: string): boolean;
|
|
|
60
62
|
* #2431 fix: better-sqlite3 + WAL mode replaces sql.js + whole-file
|
|
61
63
|
* writeFileSync. Eliminates the dual-write race that corrupted memory.db
|
|
62
64
|
* when called alongside the memory bridge's better-sqlite3 writer.
|
|
65
|
+
*
|
|
66
|
+
* #4291 fix: the file is resolved via `resolveDbPath()` — the project's one
|
|
67
|
+
* documented path-priority rule (explicit `customDbPath` argument wins, then
|
|
68
|
+
* the `CLAUDE_FLOW_DB_PATH` env binding, then the plain `memory.db` default
|
|
69
|
+
* under the memory root) — instead of a local `customDbPath ?? <default>`
|
|
70
|
+
* expression that silently ignored `CLAUDE_FLOW_DB_PATH`. A caller that
|
|
71
|
+
* explicitly selected a store via that env var no longer gets answers read
|
|
72
|
+
* from a different, unselected database file. When the resolved file is
|
|
73
|
+
* missing (and `createIfMissing` is false) or encrypted at rest, this
|
|
74
|
+
* refuses (returns null) rather than falling back to any other store.
|
|
63
75
|
*/
|
|
64
76
|
export declare function getBridgeDb(customDbPath?: string, opts?: {
|
|
65
77
|
createIfMissing?: boolean;
|
|
@@ -33,7 +33,7 @@ import * as path from 'path';
|
|
|
33
33
|
import * as fs from 'fs';
|
|
34
34
|
import * as crypto from 'crypto';
|
|
35
35
|
import { execFileSync } from 'child_process';
|
|
36
|
-
import {
|
|
36
|
+
import { resolveDbPath } from './memory-initializer.js';
|
|
37
37
|
import { encodeEmbedding } from './embedding-quantization.js';
|
|
38
38
|
import { loadBetterSqlite3 } from './shared-sqlite.js';
|
|
39
39
|
import { isEncryptedBlob } from '../encryption/vault.js';
|
|
@@ -125,13 +125,15 @@ function warnRfe1EncryptedOnce(dbPath) {
|
|
|
125
125
|
'See #2889 (https://github.com/ruvnet/ruflo/issues/2889) for details and recovery options.');
|
|
126
126
|
}
|
|
127
127
|
/**
|
|
128
|
-
* True iff the memory.db bridge at `customDbPath`
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
128
|
+
* True iff the memory.db bridge at `customDbPath` — or, when omitted, the
|
|
129
|
+
* path `resolveDbPath()` resolves from the `CLAUDE_FLOW_DB_PATH` env binding
|
|
130
|
+
* / memory root default (#4291) — is currently RFE1-encrypted-at-rest.
|
|
131
|
+
* Callers that receive `null` from getBridgeDb()/insertGraphEdge() can use
|
|
132
|
+
* this to avoid recommending the destructive `memory init` command for this
|
|
133
|
+
* specific, non-corrupt case.
|
|
132
134
|
*/
|
|
133
135
|
export function isBridgeDbEncryptedAtRest(customDbPath) {
|
|
134
|
-
const dbPath = customDbPath
|
|
136
|
+
const dbPath = resolveDbPath(customDbPath);
|
|
135
137
|
return fs.existsSync(dbPath) && isRfe1EncryptedAtRest(dbPath);
|
|
136
138
|
}
|
|
137
139
|
/** Test-only: clear the once-per-path warning dedup set. */
|
|
@@ -222,9 +224,19 @@ export function releaseBridgeDb(dbPath) {
|
|
|
222
224
|
* #2431 fix: better-sqlite3 + WAL mode replaces sql.js + whole-file
|
|
223
225
|
* writeFileSync. Eliminates the dual-write race that corrupted memory.db
|
|
224
226
|
* when called alongside the memory bridge's better-sqlite3 writer.
|
|
227
|
+
*
|
|
228
|
+
* #4291 fix: the file is resolved via `resolveDbPath()` — the project's one
|
|
229
|
+
* documented path-priority rule (explicit `customDbPath` argument wins, then
|
|
230
|
+
* the `CLAUDE_FLOW_DB_PATH` env binding, then the plain `memory.db` default
|
|
231
|
+
* under the memory root) — instead of a local `customDbPath ?? <default>`
|
|
232
|
+
* expression that silently ignored `CLAUDE_FLOW_DB_PATH`. A caller that
|
|
233
|
+
* explicitly selected a store via that env var no longer gets answers read
|
|
234
|
+
* from a different, unselected database file. When the resolved file is
|
|
235
|
+
* missing (and `createIfMissing` is false) or encrypted at rest, this
|
|
236
|
+
* refuses (returns null) rather than falling back to any other store.
|
|
225
237
|
*/
|
|
226
238
|
export async function getBridgeDb(customDbPath, opts) {
|
|
227
|
-
const dbPath = customDbPath
|
|
239
|
+
const dbPath = resolveDbPath(customDbPath);
|
|
228
240
|
const createIfMissing = opts?.createIfMissing === true;
|
|
229
241
|
if (_db && _dbPath === dbPath) {
|
|
230
242
|
armIdleRelease();
|
|
@@ -1077,6 +1077,10 @@ export async function recordTrajectory(steps, verdict) {
|
|
|
1077
1077
|
}
|
|
1078
1078
|
bumpStat('trajectoriesRecorded');
|
|
1079
1079
|
globalStats.lastAdaptation = Date.now();
|
|
1080
|
+
// #4283: the bank's own save is debounced, and the one-shot bins exit as
|
|
1081
|
+
// soon as the command resolves, so write the patterns now. Otherwise
|
|
1082
|
+
// stats.json counts patterns that never reach patterns.json.
|
|
1083
|
+
reasoningBank?.flushToDisk();
|
|
1080
1084
|
savePersistedStats();
|
|
1081
1085
|
return true;
|
|
1082
1086
|
}
|