claude-flow 3.22.0 → 3.23.0
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 -0
- package/.claude/helpers/auto-memory-hook.mjs +59 -274
- package/.claude/helpers/helpers.manifest.json +12 -0
- package/.claude/helpers/hook-handler.cjs +132 -112
- package/.claude/helpers/intelligence.cjs +992 -196
- package/package.json +1 -1
- package/v3/@claude-flow/cli/dist/src/commands/memory-backup.d.ts +11 -0
- package/v3/@claude-flow/cli/dist/src/commands/memory-backup.js +46 -0
- package/v3/@claude-flow/cli/dist/src/commands/memory.js +2 -1
- package/v3/@claude-flow/cli/dist/src/services/memory-backup.d.ts +24 -0
- package/v3/@claude-flow/cli/dist/src/services/memory-backup.js +94 -0
- package/v3/@claude-flow/cli/dist/src/services/worker-daemon.d.ts +9 -1
- package/v3/@claude-flow/cli/dist/src/services/worker-daemon.js +42 -0
- package/v3/@claude-flow/cli/package.json +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-flow",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.23.0",
|
|
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",
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* V3 CLI `memory backup` command.
|
|
3
|
+
*
|
|
4
|
+
* WAL-safe snapshot of the vector-memory DB (`.swarm/memory.db`) with rotation
|
|
5
|
+
* and optional GCS offsite. Thin surface over ../services/memory-backup.js; the
|
|
6
|
+
* daemon's nightly `backup` worker calls the same service.
|
|
7
|
+
*/
|
|
8
|
+
import type { Command } from '../types.js';
|
|
9
|
+
export declare const backupCommand: Command;
|
|
10
|
+
export default backupCommand;
|
|
11
|
+
//# sourceMappingURL=memory-backup.d.ts.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { output } from '../output.js';
|
|
2
|
+
import { backupMemoryDb, defaultMemoryDbPath } from '../services/memory-backup.js';
|
|
3
|
+
export const backupCommand = {
|
|
4
|
+
name: 'backup',
|
|
5
|
+
description: 'Snapshot the vector-memory DB (.swarm/memory.db) — WAL-safe, rotated, optional GCS offsite',
|
|
6
|
+
options: [
|
|
7
|
+
{ name: 'db', description: 'Source DB (default: .swarm/memory.db)', type: 'string' },
|
|
8
|
+
{ name: 'dir', description: 'Destination dir (default: .swarm/backups)', type: 'string' },
|
|
9
|
+
{ name: 'keep', description: 'Rotation — keep the newest N snapshots (default 7)', type: 'number', default: 7 },
|
|
10
|
+
{ name: 'gcs', description: 'Also upload the snapshot to a gs://bucket/prefix (offsite)', type: 'string' },
|
|
11
|
+
{ name: 'verbose', short: 'v', description: 'Verbose logging', type: 'boolean' },
|
|
12
|
+
],
|
|
13
|
+
examples: [
|
|
14
|
+
{ command: 'claude-flow memory backup', description: 'Snapshot to .swarm/backups, keep last 7' },
|
|
15
|
+
{ command: 'claude-flow memory backup --keep 30', description: 'Keep a month of nightly snapshots' },
|
|
16
|
+
{ command: 'claude-flow memory backup --gcs gs://my-bucket/ruflo-backups', description: 'Also upload offsite to GCS' },
|
|
17
|
+
],
|
|
18
|
+
action: async (ctx) => {
|
|
19
|
+
const r = await backupMemoryDb({
|
|
20
|
+
dbPath: ctx.flags.db || defaultMemoryDbPath(ctx.cwd),
|
|
21
|
+
destDir: ctx.flags.dir,
|
|
22
|
+
keep: typeof ctx.flags.keep === 'number' ? ctx.flags.keep : 7,
|
|
23
|
+
gcs: ctx.flags.gcs,
|
|
24
|
+
verbose: ctx.flags.verbose === true,
|
|
25
|
+
});
|
|
26
|
+
if (!r.backedUp) {
|
|
27
|
+
// no-db is a benign "nothing to back up yet"; anything else is a real skip.
|
|
28
|
+
if (r.skipped === 'no-db') {
|
|
29
|
+
output.printWarning('No memory DB found to back up (.swarm/memory.db). Nothing to do.');
|
|
30
|
+
return { success: true, data: r };
|
|
31
|
+
}
|
|
32
|
+
output.printError(`Backup skipped: ${r.skipped}`);
|
|
33
|
+
return { success: false, exitCode: 1, data: r };
|
|
34
|
+
}
|
|
35
|
+
output.writeln();
|
|
36
|
+
output.writeln(output.success(`Backed up → ${r.path}`));
|
|
37
|
+
output.printList([
|
|
38
|
+
`Size: ${Math.round((r.sizeBytes ?? 0) / 1024)} KB`,
|
|
39
|
+
`Rotated: ${r.rotatedAway?.length ?? 0} old snapshot(s) removed`,
|
|
40
|
+
...(r.gcsUri ? [`Offsite: ${r.gcsUri}`] : []),
|
|
41
|
+
]);
|
|
42
|
+
return { success: true, data: r };
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
export default backupCommand;
|
|
46
|
+
//# sourceMappingURL=memory-backup.js.map
|
|
@@ -6,6 +6,7 @@ import { output } from '../output.js';
|
|
|
6
6
|
import { select, confirm, input } from '../prompt.js';
|
|
7
7
|
import { callMCPTool, MCPClientError } from '../mcp-client.js';
|
|
8
8
|
import { distillCommand } from './memory-distill.js';
|
|
9
|
+
import { backupCommand } from './memory-backup.js';
|
|
9
10
|
// Memory backends
|
|
10
11
|
const BACKENDS = [
|
|
11
12
|
{ value: 'agentdb', label: 'AgentDB', hint: 'Vector database with HNSW indexing (150x-12,500x faster)' },
|
|
@@ -1472,7 +1473,7 @@ const initMemoryCommand = {
|
|
|
1472
1473
|
export const memoryCommand = {
|
|
1473
1474
|
name: 'memory',
|
|
1474
1475
|
description: 'Memory management commands',
|
|
1475
|
-
subcommands: [initMemoryCommand, storeCommand, retrieveCommand, searchCommand, listCommand, deleteCommand, statsCommand, configureCommand, cleanupCommand, compressCommand, exportCommand, importCommand, distillCommand],
|
|
1476
|
+
subcommands: [initMemoryCommand, storeCommand, retrieveCommand, searchCommand, listCommand, deleteCommand, statsCommand, configureCommand, cleanupCommand, compressCommand, exportCommand, importCommand, distillCommand, backupCommand],
|
|
1476
1477
|
options: [],
|
|
1477
1478
|
examples: [
|
|
1478
1479
|
{ command: 'claude-flow memory store -k "key" -v "value"', description: 'Store data' },
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface BackupOptions {
|
|
2
|
+
/** Source DB (default: <cwd>/.swarm/memory.db). */
|
|
3
|
+
dbPath?: string;
|
|
4
|
+
/** Destination dir (default: <db dir>/backups). */
|
|
5
|
+
destDir?: string;
|
|
6
|
+
/** Rotation: keep the newest N snapshots (default 7 = a week of nightlies). */
|
|
7
|
+
keep?: number;
|
|
8
|
+
/** Optional offsite: a gs://bucket/prefix to also upload the snapshot to. */
|
|
9
|
+
gcs?: string;
|
|
10
|
+
/** Injected epoch millis (tests pass a fixed value; avoids Date.now in logic). */
|
|
11
|
+
timestamp?: number;
|
|
12
|
+
verbose?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface BackupResult {
|
|
15
|
+
backedUp: boolean;
|
|
16
|
+
path?: string;
|
|
17
|
+
sizeBytes?: number;
|
|
18
|
+
rotatedAway?: string[];
|
|
19
|
+
gcsUri?: string;
|
|
20
|
+
skipped?: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function defaultMemoryDbPath(cwd?: string): string;
|
|
23
|
+
export declare function backupMemoryDb(opts?: BackupOptions): Promise<BackupResult>;
|
|
24
|
+
//# sourceMappingURL=memory-backup.d.ts.map
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vector-memory DB backup.
|
|
3
|
+
*
|
|
4
|
+
* Snapshots `.swarm/memory.db` (the sqlite store holding memory_entries +
|
|
5
|
+
* embeddings + the distilled reasoning_patterns) to a timestamped file using
|
|
6
|
+
* better-sqlite3's ONLINE backup API — a consistent, WAL-safe copy that does not
|
|
7
|
+
* block or corrupt a concurrently-written DB (unlike a naive file copy of a
|
|
8
|
+
* WAL-mode DB). Rotates to keep the last N snapshots and, optionally, uploads
|
|
9
|
+
* offsite to Google Cloud Storage.
|
|
10
|
+
*
|
|
11
|
+
* Used by `memory backup` (manual) and the daemon's nightly `backup` worker.
|
|
12
|
+
* Best-effort + non-destructive: it only reads the source DB and writes new
|
|
13
|
+
* files; it never mutates or deletes the live memory DB.
|
|
14
|
+
*/
|
|
15
|
+
import * as fs from 'fs';
|
|
16
|
+
import * as path from 'path';
|
|
17
|
+
export function defaultMemoryDbPath(cwd = process.cwd()) {
|
|
18
|
+
return path.join(cwd, '.swarm', 'memory.db');
|
|
19
|
+
}
|
|
20
|
+
/** ISO timestamp safe for filenames (no ':' or '.'). */
|
|
21
|
+
function fileStamp(ms) {
|
|
22
|
+
return new Date(ms).toISOString().replace(/[:.]/g, '-');
|
|
23
|
+
}
|
|
24
|
+
export async function backupMemoryDb(opts = {}) {
|
|
25
|
+
const dbPath = opts.dbPath ?? defaultMemoryDbPath();
|
|
26
|
+
if (!dbPath || !fs.existsSync(dbPath))
|
|
27
|
+
return { backedUp: false, skipped: 'no-db' };
|
|
28
|
+
let Database;
|
|
29
|
+
try {
|
|
30
|
+
const mod = 'better-sqlite3';
|
|
31
|
+
Database = (await import(mod)).default;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return { backedUp: false, skipped: 'better-sqlite3 unavailable' };
|
|
35
|
+
}
|
|
36
|
+
const destDir = opts.destDir ?? path.join(path.dirname(dbPath), 'backups');
|
|
37
|
+
try {
|
|
38
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
39
|
+
}
|
|
40
|
+
catch { /* */ }
|
|
41
|
+
const destPath = path.join(destDir, `memory-${fileStamp(opts.timestamp ?? Date.now())}.db`);
|
|
42
|
+
// WAL-safe online backup: read-only source, consistent snapshot to destPath.
|
|
43
|
+
let db;
|
|
44
|
+
try {
|
|
45
|
+
db = new Database(dbPath, { readonly: true });
|
|
46
|
+
await db.backup(destPath);
|
|
47
|
+
db.close();
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
try {
|
|
51
|
+
db?.close();
|
|
52
|
+
}
|
|
53
|
+
catch { /* */ }
|
|
54
|
+
return { backedUp: false, skipped: `backup failed: ${e?.message ?? e}` };
|
|
55
|
+
}
|
|
56
|
+
let sizeBytes = 0;
|
|
57
|
+
try {
|
|
58
|
+
sizeBytes = fs.statSync(destPath).size;
|
|
59
|
+
}
|
|
60
|
+
catch { /* */ }
|
|
61
|
+
// Rotation — ISO-stamped names sort chronologically, so keep the tail.
|
|
62
|
+
const keep = typeof opts.keep === 'number' && opts.keep > 0 ? opts.keep : 7;
|
|
63
|
+
const rotatedAway = [];
|
|
64
|
+
try {
|
|
65
|
+
const snaps = fs.readdirSync(destDir).filter(f => /^memory-.*\.db$/.test(f)).sort();
|
|
66
|
+
while (snaps.length > keep) {
|
|
67
|
+
const old = snaps.shift();
|
|
68
|
+
try {
|
|
69
|
+
fs.rmSync(path.join(destDir, old), { force: true });
|
|
70
|
+
rotatedAway.push(old);
|
|
71
|
+
}
|
|
72
|
+
catch { /* */ }
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch { /* */ }
|
|
76
|
+
// Optional offsite to GCS (best-effort; local backup already succeeded).
|
|
77
|
+
let gcsUri;
|
|
78
|
+
if (opts.gcs) {
|
|
79
|
+
try {
|
|
80
|
+
const { execFileSync } = await import('child_process');
|
|
81
|
+
const dest = opts.gcs.replace(/\/+$/, '') + '/' + path.basename(destPath);
|
|
82
|
+
execFileSync('gcloud', ['storage', 'cp', destPath, dest], { stdio: ['ignore', 'ignore', 'inherit'] });
|
|
83
|
+
gcsUri = dest;
|
|
84
|
+
}
|
|
85
|
+
catch { /* offsite failed — local snapshot stands */ }
|
|
86
|
+
}
|
|
87
|
+
if (opts.verbose) {
|
|
88
|
+
console.log(`memory DB backed up → ${destPath} (${Math.round(sizeBytes / 1024)} KB)` +
|
|
89
|
+
(rotatedAway.length ? `, rotated ${rotatedAway.length} old` : '') +
|
|
90
|
+
(gcsUri ? `, offsite ${gcsUri}` : ''));
|
|
91
|
+
}
|
|
92
|
+
return { backedUp: true, path: destPath, sizeBytes, rotatedAway, gcsUri };
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=memory-backup.js.map
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { EventEmitter } from 'events';
|
|
14
14
|
import { HeadlessWorkerExecutor } from './headless-worker-executor.js';
|
|
15
|
-
export type WorkerType = 'ultralearn' | 'optimize' | 'consolidate' | 'predict' | 'audit' | 'map' | 'preload' | 'deepdive' | 'document' | 'refactor' | 'benchmark' | 'testgaps';
|
|
15
|
+
export type WorkerType = 'ultralearn' | 'optimize' | 'consolidate' | 'predict' | 'audit' | 'map' | 'preload' | 'deepdive' | 'document' | 'refactor' | 'benchmark' | 'testgaps' | 'backup';
|
|
16
16
|
interface WorkerConfig {
|
|
17
17
|
type: WorkerType;
|
|
18
18
|
intervalMs: number;
|
|
@@ -319,6 +319,14 @@ export declare class WorkerDaemon extends EventEmitter {
|
|
|
319
319
|
* call defensively — a background worker must never crash the daemon.
|
|
320
320
|
*/
|
|
321
321
|
private runConsolidateWorker;
|
|
322
|
+
/**
|
|
323
|
+
* Nightly memory-DB backup worker (24h interval). Takes a WAL-safe, consistent
|
|
324
|
+
* snapshot of .swarm/memory.db with rotation (keep last N). Never throws — a
|
|
325
|
+
* worker must not crash the daemon; a skip/error is written to the metrics
|
|
326
|
+
* file. Opt-out by omitting `backup` from `-w`; offsite GCS is opt-in via
|
|
327
|
+
* RUFLO_BACKUP_GCS (a gs:// prefix), retention via RUFLO_BACKUP_KEEP.
|
|
328
|
+
*/
|
|
329
|
+
private runBackupWorker;
|
|
322
330
|
/**
|
|
323
331
|
* Local testgaps worker (fallback when headless unavailable)
|
|
324
332
|
*/
|
|
@@ -21,6 +21,7 @@ import { HeadlessWorkerExecutor, isHeadlessWorker, } from './headless-worker-exe
|
|
|
21
21
|
// incremental (rowid cursor), non-destructive, transactional, and
|
|
22
22
|
// quick_check-gated, so it's safe to call unconditionally on every tick.
|
|
23
23
|
import { runDistillation, defaultMemoryDbPath } from './memory-distillation.js';
|
|
24
|
+
import { backupMemoryDb } from './memory-backup.js';
|
|
24
25
|
// Default worker configurations with improved intervals (P0 fix: map 5min -> 15min)
|
|
25
26
|
const DEFAULT_WORKERS = [
|
|
26
27
|
{ type: 'map', intervalMs: 15 * 60 * 1000, offsetMs: 0, priority: 'normal', description: 'Codebase mapping', enabled: true },
|
|
@@ -28,6 +29,7 @@ const DEFAULT_WORKERS = [
|
|
|
28
29
|
{ type: 'optimize', intervalMs: 15 * 60 * 1000, offsetMs: 4 * 60 * 1000, priority: 'high', description: 'Performance optimization', enabled: true },
|
|
29
30
|
{ type: 'consolidate', intervalMs: 30 * 60 * 1000, offsetMs: 6 * 60 * 1000, priority: 'low', description: 'Memory distillation (ADR-174)', enabled: true },
|
|
30
31
|
{ type: 'testgaps', intervalMs: 20 * 60 * 1000, offsetMs: 8 * 60 * 1000, priority: 'normal', description: 'Test coverage analysis', enabled: true },
|
|
32
|
+
{ type: 'backup', intervalMs: 24 * 60 * 60 * 1000, offsetMs: 10 * 60 * 1000, priority: 'low', description: 'Nightly memory DB backup (WAL-safe, rotated)', enabled: true },
|
|
31
33
|
{ type: 'predict', intervalMs: 10 * 60 * 1000, offsetMs: 0, priority: 'low', description: 'Predictive preloading', enabled: false },
|
|
32
34
|
{ type: 'document', intervalMs: 60 * 60 * 1000, offsetMs: 0, priority: 'low', description: 'Auto-documentation', enabled: false },
|
|
33
35
|
];
|
|
@@ -1158,6 +1160,8 @@ export class WorkerDaemon extends EventEmitter {
|
|
|
1158
1160
|
return this.runOptimizeWorkerLocal();
|
|
1159
1161
|
case 'consolidate':
|
|
1160
1162
|
return this.runConsolidateWorker();
|
|
1163
|
+
case 'backup':
|
|
1164
|
+
return this.runBackupWorker();
|
|
1161
1165
|
case 'testgaps':
|
|
1162
1166
|
return this.runTestGapsWorkerLocal();
|
|
1163
1167
|
case 'predict':
|
|
@@ -1395,6 +1399,44 @@ export class WorkerDaemon extends EventEmitter {
|
|
|
1395
1399
|
writeFileSync(consolidateFile, JSON.stringify(result, null, 2));
|
|
1396
1400
|
return result;
|
|
1397
1401
|
}
|
|
1402
|
+
/**
|
|
1403
|
+
* Nightly memory-DB backup worker (24h interval). Takes a WAL-safe, consistent
|
|
1404
|
+
* snapshot of .swarm/memory.db with rotation (keep last N). Never throws — a
|
|
1405
|
+
* worker must not crash the daemon; a skip/error is written to the metrics
|
|
1406
|
+
* file. Opt-out by omitting `backup` from `-w`; offsite GCS is opt-in via
|
|
1407
|
+
* RUFLO_BACKUP_GCS (a gs:// prefix), retention via RUFLO_BACKUP_KEEP.
|
|
1408
|
+
*/
|
|
1409
|
+
async runBackupWorker() {
|
|
1410
|
+
const metricsDir = join(this.projectRoot, '.claude-flow', 'metrics');
|
|
1411
|
+
if (!existsSync(metricsDir))
|
|
1412
|
+
mkdirSync(metricsDir, { recursive: true });
|
|
1413
|
+
const backupFile = join(metricsDir, 'backup.json');
|
|
1414
|
+
let result;
|
|
1415
|
+
try {
|
|
1416
|
+
const keepEnv = Number(process.env.RUFLO_BACKUP_KEEP);
|
|
1417
|
+
const r = await backupMemoryDb({
|
|
1418
|
+
dbPath: defaultMemoryDbPath(this.projectRoot),
|
|
1419
|
+
keep: Number.isFinite(keepEnv) && keepEnv > 0 ? keepEnv : 7,
|
|
1420
|
+
gcs: process.env.RUFLO_BACKUP_GCS || undefined,
|
|
1421
|
+
});
|
|
1422
|
+
result = {
|
|
1423
|
+
timestamp: new Date().toISOString(),
|
|
1424
|
+
backedUp: r.backedUp,
|
|
1425
|
+
path: r.path,
|
|
1426
|
+
sizeBytes: r.sizeBytes ?? 0,
|
|
1427
|
+
rotatedAway: r.rotatedAway?.length ?? 0,
|
|
1428
|
+
gcsUri: r.gcsUri,
|
|
1429
|
+
skipped: r.skipped,
|
|
1430
|
+
};
|
|
1431
|
+
}
|
|
1432
|
+
catch (error) {
|
|
1433
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1434
|
+
this.log('warn', `Backup worker failed: ${message}`);
|
|
1435
|
+
result = { timestamp: new Date().toISOString(), backedUp: false, error: message };
|
|
1436
|
+
}
|
|
1437
|
+
writeFileSync(backupFile, JSON.stringify(result, null, 2));
|
|
1438
|
+
return result;
|
|
1439
|
+
}
|
|
1398
1440
|
/**
|
|
1399
1441
|
* Local testgaps worker (fallback when headless unavailable)
|
|
1400
1442
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-flow/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.23.0",
|
|
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",
|