claude-flow 3.21.1 → 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.
Files changed (40) hide show
  1. package/.claude/helpers/.helpers-version +1 -0
  2. package/.claude/helpers/auto-memory-hook.mjs +59 -274
  3. package/.claude/helpers/helpers.manifest.json +12 -0
  4. package/.claude/helpers/hook-handler.cjs +132 -112
  5. package/.claude/helpers/intelligence.cjs +992 -196
  6. package/package.json +1 -1
  7. package/v3/@claude-flow/cli/dist/src/commands/daemon.js +23 -2
  8. package/v3/@claude-flow/cli/dist/src/commands/memory-backup.d.ts +11 -0
  9. package/v3/@claude-flow/cli/dist/src/commands/memory-backup.js +46 -0
  10. package/v3/@claude-flow/cli/dist/src/commands/memory-distill.d.ts +27 -0
  11. package/v3/@claude-flow/cli/dist/src/commands/memory-distill.js +374 -0
  12. package/v3/@claude-flow/cli/dist/src/commands/memory.js +5 -2
  13. package/v3/@claude-flow/cli/dist/src/index.d.ts +1 -1
  14. package/v3/@claude-flow/cli/dist/src/index.js +22 -1
  15. package/v3/@claude-flow/cli/dist/src/init/executor.js +20 -0
  16. package/v3/@claude-flow/cli/dist/src/init/helper-refresh.d.ts +19 -0
  17. package/v3/@claude-flow/cli/dist/src/init/helper-refresh.js +175 -0
  18. package/v3/@claude-flow/cli/dist/src/init/helper-signing.d.ts +30 -0
  19. package/v3/@claude-flow/cli/dist/src/init/helper-signing.js +60 -0
  20. package/v3/@claude-flow/cli/dist/src/init/helpers-generator.d.ts +16 -0
  21. package/v3/@claude-flow/cli/dist/src/init/helpers-generator.js +63 -6
  22. package/v3/@claude-flow/cli/dist/src/init/statusline-generator.js +18 -7
  23. package/v3/@claude-flow/cli/dist/src/mcp-client.js +13 -0
  24. package/v3/@claude-flow/cli/dist/src/mcp-tools/agenticow-speculate-tools.js +9 -2
  25. package/v3/@claude-flow/cli/dist/src/mcp-tools/browser-intent-tools.d.ts +162 -0
  26. package/v3/@claude-flow/cli/dist/src/mcp-tools/browser-intent-tools.js +548 -0
  27. package/v3/@claude-flow/cli/dist/src/mcp-tools/browser-tools.d.ts +9 -1
  28. package/v3/@claude-flow/cli/dist/src/mcp-tools/browser-tools.js +4 -1
  29. package/v3/@claude-flow/cli/dist/src/memory/memory-bridge.js +67 -47
  30. package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.d.ts +71 -0
  31. package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.js +330 -2
  32. package/v3/@claude-flow/cli/dist/src/services/distill-tuning.d.ts +111 -0
  33. package/v3/@claude-flow/cli/dist/src/services/distill-tuning.js +510 -0
  34. package/v3/@claude-flow/cli/dist/src/services/memory-backup.d.ts +24 -0
  35. package/v3/@claude-flow/cli/dist/src/services/memory-backup.js +94 -0
  36. package/v3/@claude-flow/cli/dist/src/services/memory-distillation.d.ts +41 -0
  37. package/v3/@claude-flow/cli/dist/src/services/memory-distillation.js +277 -0
  38. package/v3/@claude-flow/cli/dist/src/services/worker-daemon.d.ts +31 -2
  39. package/v3/@claude-flow/cli/dist/src/services/worker-daemon.js +159 -6
  40. package/v3/@claude-flow/cli/package.json +5 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-flow",
3
- "version": "3.21.1",
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",
@@ -14,6 +14,12 @@ const startCommand = {
14
14
  description: 'Start the worker daemon with all enabled background workers',
15
15
  options: [
16
16
  { name: 'workers', short: 'w', type: 'string', description: 'Comma-separated list of workers to enable (default: map,audit,optimize,consolidate,testgaps)' },
17
+ // ADR-174 M3: consolidate now runs a real memory-distillation pass
18
+ // (memory_entries -> episodes/reasoning_patterns/causal_edges) instead of
19
+ // a no-op stub. This opt-out skips just that pass for the life of this
20
+ // daemon process, without touching persisted worker-enabled state — for
21
+ // permanently disabling the worker use `daemon enable -w consolidate --disable`.
22
+ { name: 'no-distill', type: 'boolean', description: 'Disable the consolidate worker\'s memory-distillation pass (ADR-174)' },
17
23
  { name: 'quiet', short: 'Q', type: 'boolean', description: 'Suppress output' },
18
24
  { name: 'background', short: 'b', type: 'boolean', description: 'Run daemon in background (detached process)', default: true },
19
25
  { name: 'foreground', short: 'f', type: 'boolean', description: 'Run daemon in foreground (blocks terminal)' },
@@ -35,10 +41,19 @@ const startCommand = {
35
41
  { command: 'claude-flow daemon start --foreground', description: 'Start in foreground (blocks terminal)' },
36
42
  { command: 'claude-flow daemon start -w map,audit,optimize', description: 'Start with specific workers' },
37
43
  { command: 'claude-flow daemon start --headless --sandbox strict', description: 'Start with headless workers in strict sandbox' },
44
+ { command: 'claude-flow daemon start --no-distill', description: 'Start with the consolidate worker\'s distillation pass disabled' },
38
45
  ],
39
46
  action: async (ctx) => {
40
47
  const quiet = ctx.flags.quiet;
41
48
  const foreground = ctx.flags.foreground;
49
+ const noDistill = ctx.flags['no-distill'];
50
+ // ADR-174 M3: honored by WorkerDaemon's consolidate worker directly via
51
+ // process.env — set it now so the foreground path (which runs in this
52
+ // same process) picks it up too; the background path forwards it to the
53
+ // forked child's env explicitly (see startBackgroundDaemon below).
54
+ if (noDistill) {
55
+ process.env.RUFLO_DAEMON_NO_DISTILL = '1';
56
+ }
42
57
  // #1914: a forked daemon child receives --workspace <root>; the launcher
43
58
  // and interactive invocations have no flag and fall back to cwd.
44
59
  const projectRoot = resolveWorkspaceFlag(ctx.flags.workspace) ?? process.cwd();
@@ -208,6 +223,7 @@ const startCommand = {
208
223
  headless: ctx.flags.headless,
209
224
  sandbox: ctx.flags.sandbox,
210
225
  ttl: rawTtl,
226
+ noDistill,
211
227
  });
212
228
  }
213
229
  finally {
@@ -404,7 +420,7 @@ export function extractWorkspaceFromDaemonLine(commandLine) {
404
420
  return ws.length > 0 ? ws : null;
405
421
  }
406
422
  async function startBackgroundDaemon(projectRoot, quiet, forwarded = {}) {
407
- const { maxCpuLoad, minFreeMemory, workers, headless, sandbox, ttl } = forwarded;
423
+ const { maxCpuLoad, minFreeMemory, workers, headless, sandbox, ttl, noDistill } = forwarded;
408
424
  // Validate and resolve project root
409
425
  const resolvedRoot = resolve(projectRoot);
410
426
  validatePath(resolvedRoot, 'Project root');
@@ -493,6 +509,11 @@ async function startBackgroundDaemon(projectRoot, quiet, forwarded = {}) {
493
509
  if (typeof sandbox === 'string' && (sandbox === 'strict' || sandbox === 'permissive' || sandbox === 'disabled')) {
494
510
  forkArgs.push('--sandbox', sandbox);
495
511
  }
512
+ // ADR-174 M3: forward the distillation opt-out to the forked foreground
513
+ // child; its own `start` action sets RUFLO_DAEMON_NO_DISTILL from this flag.
514
+ if (noDistill === true) {
515
+ forkArgs.push('--no-distill');
516
+ }
496
517
  // #1914: stamp the workspace into argv (kept LAST) so the foreground daemon
497
518
  // process is self-identifying and `killStaleDaemons` only reaps daemons
498
519
  // belonging to this workspace. resolvedRoot was validatePath()'d above.
@@ -1352,7 +1373,7 @@ export const daemonCommand = {
1352
1373
  `${output.highlight('map')} - Codebase mapping (5 min interval)`,
1353
1374
  `${output.highlight('audit')} - Security analysis (10 min interval)`,
1354
1375
  `${output.highlight('optimize')} - Performance optimization (15 min interval)`,
1355
- `${output.highlight('consolidate')} - Memory consolidation (30 min interval)`,
1376
+ `${output.highlight('consolidate')} - Memory distillation: memory_entries -> episodes/reasoning_patterns/causal_edges (30 min interval, ADR-174; --no-distill to disable)`,
1356
1377
  `${output.highlight('testgaps')} - Test coverage analysis (20 min interval)`,
1357
1378
  `${output.highlight('predict')} - Predictive preloading (2 min, disabled by default)`,
1358
1379
  `${output.highlight('document')} - Auto-documentation (60 min, disabled by default)`,
@@ -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
@@ -0,0 +1,27 @@
1
+ import type { Command, CommandContext } from '../types.js';
2
+ export interface DistillCliConfig {
3
+ dryRun: boolean;
4
+ namespaces?: string[];
5
+ batchSize: number;
6
+ dedupDistance: number;
7
+ maxEntries?: number;
8
+ sinceRowid?: number;
9
+ budgetUsd: number;
10
+ judge: 'structural' | 'fable';
11
+ }
12
+ type ResolveResult = {
13
+ ok: true;
14
+ config: DistillCliConfig;
15
+ } | {
16
+ ok: false;
17
+ error: string;
18
+ };
19
+ /**
20
+ * Resolve the effective distill config from: built-in defaults, an optional
21
+ * `--config <path>` JSON file, an `--aggressive`/`--conservative` preset, and
22
+ * finally explicit CLI flags (highest precedence).
23
+ */
24
+ export declare function resolveDistillConfig(ctx: CommandContext): ResolveResult;
25
+ export declare const distillCommand: Command;
26
+ export default distillCommand;
27
+ //# sourceMappingURL=memory-distill.d.ts.map
@@ -0,0 +1,374 @@
1
+ /**
2
+ * V3 CLI `memory distill` Command Family (ADR-174 Milestone 2)
3
+ *
4
+ * CLI surface over the frozen M1 service (`../services/memory-distillation.js`
5
+ * — `runDistillation` / `defaultMemoryDbPath`). This file does not implement
6
+ * any distillation logic itself; it only resolves flags/config/presets into
7
+ * `DistillOptions`, calls the service, and formats the report.
8
+ *
9
+ * memory distill run — run (or dry-run) a distillation pass
10
+ * memory distill status — read-only report on the target tables + cursor
11
+ * memory distill config — print the effective resolved config as JSON
12
+ *
13
+ * Design notes:
14
+ * - `--judge fable` requires `--budget-usd > 0` (ADR-172 cost-bounded advisor
15
+ * gate). This is enforced here, client-side, as a fail-fast — the service
16
+ * itself also refuses (`skipped: 'judge:fable requires ...'`) but failing
17
+ * fast in the CLI gives a clearer, non-zero-exit error.
18
+ * - `--aggressive` / `--conservative` are preset bundles (ADR-174 param
19
+ * table). Precedence, low to high: built-in defaults < `--config` file <
20
+ * preset < explicit `--batch-size`/`--dedup-distance` flags.
21
+ * - `status` opens the DB read-only via the same "variable-specifier"
22
+ * optional-import pattern used by `../memory/graph-edge-writer.ts` so the
23
+ * TypeScript compiler never statically requires `better-sqlite3` types,
24
+ * and degrades gracefully (no throw) when the DB, tables, or the native
25
+ * module are absent.
26
+ */
27
+ import * as fs from 'fs';
28
+ import * as path from 'path';
29
+ import { output } from '../output.js';
30
+ import { runDistillation, defaultMemoryDbPath, } from '../services/memory-distillation.js';
31
+ const DEFAULT_CONFIG = {
32
+ dryRun: false,
33
+ batchSize: 200,
34
+ dedupDistance: 0.2, // ADR-174 M4-tuned platform default (~37% fewer patterns, retrieval-neutral)
35
+ budgetUsd: 0,
36
+ judge: 'structural',
37
+ };
38
+ // ADR-174 preset bundles — distinct extremes around the 0.2 tuned default.
39
+ const AGGRESSIVE_PRESET = { dedupDistance: 0.3, batchSize: 500 }; // more clustering → fewer, coarser patterns
40
+ const CONSERVATIVE_PRESET = { dedupDistance: 0.1, batchSize: 100 }; // less clustering → more, granular patterns
41
+ const DB_OPTION = {
42
+ name: 'db',
43
+ description: 'Path to the memory DB (default: cwd/.swarm/memory.db)',
44
+ type: 'string',
45
+ };
46
+ /**
47
+ * Resolve the effective distill config from: built-in defaults, an optional
48
+ * `--config <path>` JSON file, an `--aggressive`/`--conservative` preset, and
49
+ * finally explicit CLI flags (highest precedence).
50
+ */
51
+ export function resolveDistillConfig(ctx) {
52
+ let cfg = { ...DEFAULT_CONFIG };
53
+ const configPath = ctx.flags.config;
54
+ if (configPath) {
55
+ try {
56
+ const raw = fs.readFileSync(path.resolve(configPath), 'utf8');
57
+ const parsed = JSON.parse(raw);
58
+ if (parsed && typeof parsed === 'object') {
59
+ cfg = { ...cfg, ...parsed };
60
+ }
61
+ }
62
+ catch (error) {
63
+ return { ok: false, error: `Failed to load --config ${configPath}: ${error instanceof Error ? error.message : String(error)}` };
64
+ }
65
+ }
66
+ const aggressive = ctx.flags.aggressive === true;
67
+ const conservative = ctx.flags.conservative === true;
68
+ if (aggressive && conservative) {
69
+ return { ok: false, error: '--aggressive and --conservative are mutually exclusive' };
70
+ }
71
+ if (aggressive)
72
+ cfg = { ...cfg, ...AGGRESSIVE_PRESET };
73
+ if (conservative)
74
+ cfg = { ...cfg, ...CONSERVATIVE_PRESET };
75
+ // Explicit CLI flags win over the config file and presets.
76
+ if (ctx.flags.dryRun === true)
77
+ cfg.dryRun = true;
78
+ if (typeof ctx.flags.namespace === 'string' && ctx.flags.namespace.length > 0) {
79
+ cfg.namespaces = ctx.flags.namespace.split(',').map((s) => s.trim()).filter(Boolean);
80
+ }
81
+ if (ctx.flags.batchSize !== undefined)
82
+ cfg.batchSize = Number(ctx.flags.batchSize);
83
+ if (ctx.flags.dedupDistance !== undefined)
84
+ cfg.dedupDistance = Number(ctx.flags.dedupDistance);
85
+ if (ctx.flags.maxEntries !== undefined)
86
+ cfg.maxEntries = Number(ctx.flags.maxEntries);
87
+ if (ctx.flags.since !== undefined)
88
+ cfg.sinceRowid = Number(ctx.flags.since);
89
+ if (ctx.flags.budgetUsd !== undefined)
90
+ cfg.budgetUsd = Number(ctx.flags.budgetUsd);
91
+ if (ctx.flags.judge !== undefined) {
92
+ if (ctx.flags.judge !== 'structural' && ctx.flags.judge !== 'fable') {
93
+ return { ok: false, error: `--judge must be 'structural' or 'fable' (got '${String(ctx.flags.judge)}')` };
94
+ }
95
+ cfg.judge = ctx.flags.judge;
96
+ }
97
+ return { ok: true, config: cfg };
98
+ }
99
+ function resolveDbPath(ctx) {
100
+ return ctx.flags.db || defaultMemoryDbPath(ctx.cwd || process.cwd());
101
+ }
102
+ // ============================================================================
103
+ // `memory distill run`
104
+ // ============================================================================
105
+ const runCommand = {
106
+ name: 'run',
107
+ description: 'Distill memory_entries into episodes/reasoning_patterns/pattern_embeddings/causal_edges (ADR-174)',
108
+ options: [
109
+ { name: 'dry-run', description: 'Report counts, write nothing', type: 'boolean', default: false },
110
+ { name: 'namespace', description: 'Comma-separated namespace scope (default: all embedded namespaces)', type: 'string' },
111
+ { name: 'batch-size', description: 'Rows per transaction (default 200)', type: 'number', default: 200 },
112
+ { name: 'dedup-distance', description: 'Cosine distance for pattern clustering (default 0.12)', type: 'number', default: 0.12 },
113
+ { name: 'max-entries', description: 'Per-invocation work cap (default unbounded)', type: 'number' },
114
+ { name: 'since', description: 'Override the incremental cursor and rescan from this rowid', type: 'number' },
115
+ { name: 'budget-usd', description: '$0 by default; >0 required to unlock --judge fable (ADR-172)', type: 'number', default: 0 },
116
+ { name: 'judge', description: "'structural' (default, $0) or 'fable' (requires --budget-usd > 0)", type: 'string', default: 'structural', choices: ['structural', 'fable'] },
117
+ { name: 'aggressive', description: 'Preset: dedup-distance 0.2, batch-size 500', type: 'boolean' },
118
+ { name: 'conservative', description: 'Preset: dedup-distance 0.08, batch-size 100', type: 'boolean' },
119
+ { name: 'config', description: 'Load options from a JSON config file (see: memory distill config)', type: 'string' },
120
+ { ...DB_OPTION },
121
+ { name: 'verbose', short: 'v', description: 'Verbose service logging', type: 'boolean' },
122
+ ],
123
+ examples: [
124
+ { command: 'claude-flow memory distill run --dry-run', description: 'Preview counts without writing anything' },
125
+ { command: 'claude-flow memory distill run --namespace feedback,commands', description: 'Scope the run to specific namespaces' },
126
+ { command: 'claude-flow memory distill run --aggressive', description: 'Larger batches, looser dedup clustering' },
127
+ { command: 'claude-flow memory distill run --judge fable --budget-usd 2', description: 'Opt into the cost-bounded Fable judge (ADR-172)' },
128
+ ],
129
+ action: async (ctx) => {
130
+ const resolved = resolveDistillConfig(ctx);
131
+ if (!resolved.ok) {
132
+ output.printError(resolved.error);
133
+ return { success: false, exitCode: 1 };
134
+ }
135
+ const config = resolved.config;
136
+ // Fail-fast ADR-172 gate: the Fable judge is cost-bounded and opt-in only.
137
+ if (config.judge === 'fable' && !(config.budgetUsd > 0)) {
138
+ output.printError('--judge fable requires --budget-usd > 0 (ADR-172 cost-bounded advisor path). ' +
139
+ 'Refusing to run the LLM judge against a $0 budget.');
140
+ return { success: false, exitCode: 1 };
141
+ }
142
+ const dbPath = resolveDbPath(ctx);
143
+ output.writeln();
144
+ output.writeln(output.bold('Memory Distillation (ADR-174)'));
145
+ output.writeln(output.dim('─'.repeat(55)));
146
+ if (config.dryRun)
147
+ output.writeln(output.warning('DRY RUN — no writes will be made'));
148
+ const spinner = output.createSpinner({ text: 'Distilling memory_entries...', spinner: 'dots' });
149
+ spinner.start();
150
+ const distillOptions = {
151
+ dbPath,
152
+ namespaces: config.namespaces,
153
+ batchSize: config.batchSize,
154
+ maxEntries: config.maxEntries,
155
+ dedupDistance: config.dedupDistance,
156
+ dryRun: config.dryRun,
157
+ judge: config.judge,
158
+ sinceRowid: config.sinceRowid,
159
+ verbose: ctx.flags.verbose === true,
160
+ };
161
+ let report;
162
+ try {
163
+ report = await runDistillation(distillOptions);
164
+ }
165
+ catch (error) {
166
+ spinner.fail('Distillation failed');
167
+ output.printError(error instanceof Error ? error.message : String(error));
168
+ return { success: false, exitCode: 1 };
169
+ }
170
+ if (report.skipped) {
171
+ spinner.succeed('Distillation skipped');
172
+ output.writeln(output.warning(`skipped: ${report.skipped}`));
173
+ return { success: true, data: { dbPath, config, report } };
174
+ }
175
+ spinner.succeed(config.dryRun ? 'Dry-run complete' : 'Distillation complete');
176
+ output.writeln();
177
+ output.printTable({
178
+ columns: [
179
+ { key: 'metric', header: 'Metric', width: 24 },
180
+ { key: 'value', header: 'Value', width: 20 },
181
+ ],
182
+ data: [
183
+ { metric: 'Processed', value: String(report.processed) },
184
+ { metric: 'Episodes', value: String(report.episodes) },
185
+ { metric: 'Patterns', value: String(report.patterns) },
186
+ { metric: 'Pattern Embeddings', value: String(report.patternEmbeddings) },
187
+ { metric: 'Causal Edges', value: String(report.causalEdges) },
188
+ { metric: 'Promoted (ADR-171)', value: String(report.promoted) },
189
+ { metric: 'Namespaces', value: report.namespaces.join(', ') || '(none)' },
190
+ { metric: 'Spend', value: `$${report.spendUsd.toFixed(4)}` },
191
+ { metric: 'Dry Run', value: report.dryRun ? 'yes' : 'no' },
192
+ ],
193
+ });
194
+ const provenanceEntries = Object.entries(report.byProvenance);
195
+ if (provenanceEntries.length > 0) {
196
+ output.writeln();
197
+ output.writeln(output.bold('By Provenance'));
198
+ output.printList(provenanceEntries.map(([tier, count]) => `${output.highlight(tier)}: ${count}`));
199
+ }
200
+ return { success: true, data: { dbPath, config, report } };
201
+ },
202
+ };
203
+ // ============================================================================
204
+ // `memory distill status`
205
+ // ============================================================================
206
+ const statusCommand = {
207
+ name: 'status',
208
+ description: 'Read-only report: target table row counts, per-namespace distill_state cursor, promoted vs proxy breakdown',
209
+ options: [{ ...DB_OPTION }],
210
+ examples: [
211
+ { command: 'claude-flow memory distill status', description: 'Show distillation state for the default DB' },
212
+ { command: 'claude-flow memory distill status --db ./copy.db', description: 'Inspect a specific DB copy' },
213
+ ],
214
+ action: async (ctx) => {
215
+ const dbPath = resolveDbPath(ctx);
216
+ output.writeln();
217
+ output.writeln(output.bold('Memory Distillation Status'));
218
+ output.writeln(output.dim('─'.repeat(55)));
219
+ if (!fs.existsSync(dbPath)) {
220
+ output.writeln(output.warning(`No memory DB found at ${dbPath}`));
221
+ return { success: true, data: { available: false, dbPath, reason: 'no-db' } };
222
+ }
223
+ // Optional native dep — hidden behind a variable specifier so the
224
+ // compiler never statically requires `better-sqlite3` types (same
225
+ // pattern as ../memory/graph-edge-writer.ts).
226
+ let Database;
227
+ try {
228
+ const mod = 'better-sqlite3';
229
+ Database = (await import(mod)).default;
230
+ }
231
+ catch {
232
+ output.writeln(output.warning('better-sqlite3 native module unavailable — cannot read distillation state.'));
233
+ return { success: true, data: { available: false, dbPath, reason: 'better-sqlite3 unavailable' } };
234
+ }
235
+ let db;
236
+ try {
237
+ db = new Database(dbPath, { readonly: true, fileMustExist: true });
238
+ }
239
+ catch (error) {
240
+ output.printError(`Failed to open ${dbPath}: ${error instanceof Error ? error.message : String(error)}`);
241
+ return { success: true, data: { available: false, dbPath, reason: 'open-failed' } };
242
+ }
243
+ try {
244
+ const tableExists = (name) => (db.prepare("SELECT COUNT(*) AS c FROM sqlite_master WHERE type='table' AND name=?").get(name)?.c ?? 0) > 0;
245
+ const targetTables = ['episodes', 'reasoning_patterns', 'pattern_embeddings', 'causal_edges'];
246
+ const presence = {};
247
+ for (const t of [...targetTables, 'distill_state'])
248
+ presence[t] = tableExists(t);
249
+ const counts = {};
250
+ for (const t of targetTables) {
251
+ counts[t] = presence[t] ? (db.prepare(`SELECT COUNT(*) AS c FROM ${t}`).get()?.c ?? 0) : 0;
252
+ }
253
+ let cursors = [];
254
+ if (presence.distill_state) {
255
+ cursors = db.prepare('SELECT namespace AS namespace, last_rowid AS lastRowid, last_run_at AS lastRunAt FROM distill_state ORDER BY namespace').all();
256
+ }
257
+ let promoted = 0;
258
+ let proxy = 0;
259
+ if (presence.reasoning_patterns) {
260
+ promoted = db.prepare("SELECT COUNT(*) AS c FROM reasoning_patterns WHERE json_extract(metadata,'$.promoted')=1").get()?.c ?? 0;
261
+ proxy = db.prepare("SELECT COUNT(*) AS c FROM reasoning_patterns WHERE json_extract(metadata,'$.provenance')='proxy:structural'").get()?.c ?? 0;
262
+ }
263
+ db.close();
264
+ output.printTable({
265
+ columns: [
266
+ { key: 'table', header: 'Table', width: 22 },
267
+ { key: 'rows', header: 'Rows', width: 10 },
268
+ ],
269
+ data: targetTables.map((t) => ({ table: t, rows: String(counts[t]) })),
270
+ });
271
+ output.writeln();
272
+ output.writeln(output.bold('Promote gate (ADR-171)'));
273
+ output.writeln(` promoted: ${promoted} proxy (never promoted): ${proxy} total patterns: ${counts.reasoning_patterns}`);
274
+ if (cursors.length > 0) {
275
+ output.writeln();
276
+ output.writeln(output.bold('distill_state cursor'));
277
+ output.printTable({
278
+ columns: [
279
+ { key: 'namespace', header: 'Namespace', width: 20 },
280
+ { key: 'lastRowid', header: 'Last Rowid', width: 12 },
281
+ { key: 'lastRunAt', header: 'Last Run', width: 26 },
282
+ ],
283
+ data: cursors.map((c) => ({
284
+ namespace: c.namespace,
285
+ lastRowid: String(c.lastRowid),
286
+ lastRunAt: c.lastRunAt ? new Date(c.lastRunAt).toISOString() : 'never',
287
+ })),
288
+ });
289
+ }
290
+ else {
291
+ output.writeln();
292
+ output.writeln(output.dim('No distill_state cursor rows yet (run `memory distill run` first).'));
293
+ }
294
+ if (!presence.reasoning_patterns) {
295
+ output.writeln();
296
+ output.writeln(output.dim('Target tables not present — agentdb schema not initialised, or distillation never run.'));
297
+ }
298
+ return {
299
+ success: true,
300
+ data: { available: true, dbPath, counts, cursors, promoted, proxy, tablesPresent: presence },
301
+ };
302
+ }
303
+ catch (error) {
304
+ try {
305
+ db?.close();
306
+ }
307
+ catch { /* best-effort */ }
308
+ output.printError(error instanceof Error ? error.message : String(error));
309
+ return { success: true, data: { available: false, dbPath, reason: 'error' } };
310
+ }
311
+ },
312
+ };
313
+ // ============================================================================
314
+ // `memory distill config`
315
+ // ============================================================================
316
+ const configCommand = {
317
+ name: 'config',
318
+ description: 'Print the effective distill config (defaults + --config file + preset + explicit flags) as JSON',
319
+ options: [
320
+ { name: 'config', description: 'Load options from a JSON config file', type: 'string' },
321
+ { name: 'aggressive', description: 'Preset: dedup-distance 0.2, batch-size 500', type: 'boolean' },
322
+ { name: 'conservative', description: 'Preset: dedup-distance 0.08, batch-size 100', type: 'boolean' },
323
+ { name: 'namespace', description: 'Comma-separated namespace scope', type: 'string' },
324
+ { name: 'batch-size', description: 'Rows per transaction', type: 'number' },
325
+ { name: 'dedup-distance', description: 'Cosine distance for pattern clustering', type: 'number' },
326
+ { name: 'max-entries', description: 'Per-invocation work cap', type: 'number' },
327
+ { name: 'since', description: 'Override incremental cursor start (rowid)', type: 'number' },
328
+ { name: 'budget-usd', description: '$0 by default', type: 'number' },
329
+ { name: 'judge', description: "'structural' | 'fable'", type: 'string' },
330
+ { ...DB_OPTION },
331
+ ],
332
+ examples: [
333
+ { command: 'claude-flow memory distill config', description: 'Print the platform-default config' },
334
+ { command: 'claude-flow memory distill config --aggressive', description: 'Print the effective config with the aggressive preset applied' },
335
+ ],
336
+ action: async (ctx) => {
337
+ const resolved = resolveDistillConfig(ctx);
338
+ if (!resolved.ok) {
339
+ output.printError(resolved.error);
340
+ return { success: false, exitCode: 1 };
341
+ }
342
+ const dbPath = resolveDbPath(ctx);
343
+ const payload = { dbPath, ...resolved.config };
344
+ output.writeln(JSON.stringify(payload, null, 2));
345
+ return { success: true, data: payload };
346
+ },
347
+ };
348
+ // ============================================================================
349
+ // `memory distill` (parent)
350
+ // ============================================================================
351
+ export const distillCommand = {
352
+ name: 'distill',
353
+ description: 'Memory distillation (ADR-174): mine memory_entries into reasoning_patterns/episodes/causal_edges',
354
+ subcommands: [runCommand, statusCommand, configCommand],
355
+ examples: [
356
+ { command: 'claude-flow memory distill run --dry-run', description: 'Preview a distillation pass' },
357
+ { command: 'claude-flow memory distill status', description: 'Show distilled table counts + cursor' },
358
+ { command: 'claude-flow memory distill config --conservative', description: 'Print the effective conservative-preset config' },
359
+ ],
360
+ action: async () => {
361
+ output.writeln();
362
+ output.writeln(output.bold('Memory Distillation (ADR-174)'));
363
+ output.writeln('Usage: claude-flow memory distill <run|status|config> [options]');
364
+ output.writeln();
365
+ output.printList([
366
+ `${output.highlight('run')} - Distill memory_entries into the structured intelligence tables`,
367
+ `${output.highlight('status')} - Report target table counts, cursor, and promote-gate breakdown`,
368
+ `${output.highlight('config')} - Print the effective resolved config as JSON`,
369
+ ]);
370
+ return { success: true };
371
+ },
372
+ };
373
+ export default distillCommand;
374
+ //# sourceMappingURL=memory-distill.js.map
@@ -5,6 +5,8 @@
5
5
  import { output } from '../output.js';
6
6
  import { select, confirm, input } from '../prompt.js';
7
7
  import { callMCPTool, MCPClientError } from '../mcp-client.js';
8
+ import { distillCommand } from './memory-distill.js';
9
+ import { backupCommand } from './memory-backup.js';
8
10
  // Memory backends
9
11
  const BACKENDS = [
10
12
  { value: 'agentdb', label: 'AgentDB', hint: 'Vector database with HNSW indexing (150x-12,500x faster)' },
@@ -1471,7 +1473,7 @@ const initMemoryCommand = {
1471
1473
  export const memoryCommand = {
1472
1474
  name: 'memory',
1473
1475
  description: 'Memory management commands',
1474
- subcommands: [initMemoryCommand, storeCommand, retrieveCommand, searchCommand, listCommand, deleteCommand, statsCommand, configureCommand, cleanupCommand, compressCommand, exportCommand, importCommand],
1476
+ subcommands: [initMemoryCommand, storeCommand, retrieveCommand, searchCommand, listCommand, deleteCommand, statsCommand, configureCommand, cleanupCommand, compressCommand, exportCommand, importCommand, distillCommand, backupCommand],
1475
1477
  options: [],
1476
1478
  examples: [
1477
1479
  { command: 'claude-flow memory store -k "key" -v "value"', description: 'Store data' },
@@ -1497,7 +1499,8 @@ export const memoryCommand = {
1497
1499
  `${output.highlight('cleanup')} - Clean expired entries`,
1498
1500
  `${output.highlight('compress')} - Compress database`,
1499
1501
  `${output.highlight('export')} - Export memory to file`,
1500
- `${output.highlight('import')} - Import from file`
1502
+ `${output.highlight('import')} - Import from file`,
1503
+ `${output.highlight('distill')} - Distill memory_entries into structured intelligence (ADR-174)`
1501
1504
  ]);
1502
1505
  return { success: true };
1503
1506
  }
@@ -63,7 +63,7 @@ export { OutputFormatter, output, Progress, Spinner, type VerbosityLevel } from
63
63
  export * from './prompt.js';
64
64
  export * from './commands/index.js';
65
65
  export { MCPServerManager, createMCPServerManager, getServerManager, startMCPServer, stopMCPServer, getMCPServerStatus, type MCPServerOptions, type MCPServerStatus, } from './mcp-server.js';
66
- export { initializeMemoryDatabase, generateEmbedding, generateBatchEmbeddings, storeEntry, searchEntries, getHNSWIndex, addToHNSWIndex, searchHNSWIndex, getHNSWStatus, clearHNSWIndex, quantizeInt8, dequantizeInt8, quantizedCosineSim, getQuantizationStats, batchCosineSim, softmaxAttention, topKIndices, flashAttentionSearch, type MemoryInitResult, } from './memory/memory-initializer.js';
66
+ export { initializeMemoryDatabase, repairVectorIndexes, recoverMemoryDatabase, generateEmbedding, generateBatchEmbeddings, storeEntry, searchEntries, getHNSWIndex, addToHNSWIndex, searchHNSWIndex, getHNSWStatus, clearHNSWIndex, quantizeInt8, dequantizeInt8, quantizedCosineSim, getQuantizationStats, batchCosineSim, softmaxAttention, topKIndices, flashAttentionSearch, type MemoryInitResult, } from './memory/memory-initializer.js';
67
67
  export { initializeIntelligence, recordStep, recordTrajectory, findSimilarPatterns, getIntelligenceStats, getSonaCoordinator, getReasoningBank, clearIntelligence, benchmarkAdaptation, endTrajectoryWithVerdict, distillLearning, getAllPatterns, getPatternsByType, flushPatterns, deletePattern, clearAllPatterns, getNeuralDataDir, getPersistenceStatus, type SonaConfig, type TrajectoryStep, type Pattern, type IntelligenceStats, } from './memory/intelligence.js';
68
68
  export { EWCConsolidator, getEWCConsolidator, resetEWCConsolidator, consolidatePatterns, recordPatternOutcome, getEWCStats, type PatternWeights, type EWCConfig, type ConsolidationResult, type EWCStats, } from './memory/ewc-consolidation.js';
69
69
  export { SONAOptimizer, getSONAOptimizer, resetSONAOptimizer, processTrajectory, getSuggestion, getSONAStats, type TrajectoryOutcome, type LearnedPattern, type RoutingSuggestion, type SONAStats, } from './memory/sona-optimizer.js';
@@ -113,6 +113,27 @@ export class CLI {
113
113
  if (!flags.noUpdate && commandPath[0] !== 'update') {
114
114
  this.checkForUpdatesOnStartup().catch(() => { });
115
115
  }
116
+ // Version-stamped helper auto-refresh — propagate hook fixes to an already
117
+ // initialized project without a manual re-init. Skip for init/upgrade
118
+ // (they refresh explicitly). AWAITED (not fire-and-forget) so a fast
119
+ // command can't exit before the copy lands; the fast path is a single
120
+ // stamp read + string compare (sub-ms), and the copy runs at most once per
121
+ // version bump. Best-effort + silent — never blocks or fails a command.
122
+ if (commandPath[0] !== 'init' && commandPath[0] !== 'update') {
123
+ try {
124
+ const { autoRefreshHelpersIfStale } = await import('./init/helper-refresh.js');
125
+ const r = await autoRefreshHelpersIfStale(process.cwd());
126
+ if (r.blocked) {
127
+ // Integrity failure = potential on-disk tampering of hook code. Warn
128
+ // loudly (not silent) — the existing project helpers were left intact.
129
+ this.output.printWarning(`Skipped helper auto-refresh — ${r.blocked}. Reinstall @claude-flow/cli from a trusted source.`);
130
+ }
131
+ else if (r.refreshed && this.output.isVerbose()) {
132
+ this.output.printDebug(`Refreshed .claude/helpers (${r.from} → ${r.to})`);
133
+ }
134
+ }
135
+ catch { /* silent */ }
136
+ }
116
137
  // Handle lazy-loaded commands that weren't recognized by the parser
117
138
  // If commandPath is empty but positional has a command name, check if it's lazy-loadable
118
139
  if (commandPath.length === 0 && positional.length > 0 && !positional[0].startsWith('-')) {
@@ -515,7 +536,7 @@ export * from './commands/index.js';
515
536
  // MCP Server management
516
537
  export { MCPServerManager, createMCPServerManager, getServerManager, startMCPServer, stopMCPServer, getMCPServerStatus, } from './mcp-server.js';
517
538
  // Memory & Intelligence (V3 Performance Features)
518
- export { initializeMemoryDatabase, generateEmbedding, generateBatchEmbeddings, storeEntry, searchEntries, getHNSWIndex, addToHNSWIndex, searchHNSWIndex, getHNSWStatus, clearHNSWIndex, quantizeInt8, dequantizeInt8, quantizedCosineSim, getQuantizationStats,
539
+ export { initializeMemoryDatabase, repairVectorIndexes, recoverMemoryDatabase, generateEmbedding, generateBatchEmbeddings, storeEntry, searchEntries, getHNSWIndex, addToHNSWIndex, searchHNSWIndex, getHNSWStatus, clearHNSWIndex, quantizeInt8, dequantizeInt8, quantizedCosineSim, getQuantizationStats,
519
540
  // Flash Attention-style batch operations
520
541
  batchCosineSim, softmaxAttention, topKIndices, flashAttentionSearch, } from './memory/memory-initializer.js';
521
542
  export { initializeIntelligence, recordStep, recordTrajectory, findSimilarPatterns, getIntelligenceStats, getSonaCoordinator, getReasoningBank, clearIntelligence, benchmarkAdaptation,