@claude-flow/cli 3.47.1 → 3.48.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/auto-memory-hook.mjs +6 -2
- package/.claude/helpers/helpers.manifest.json +3 -3
- package/.claude/helpers/learning-service.mjs +4 -2
- package/.claude/helpers/metrics-db.mjs +4 -2
- package/bin/cli.js +4 -1
- package/catalog-manifest.json +2 -2
- package/dist/src/commands/analyze.js +2 -2
- package/dist/src/commands/config.js +5 -7
- package/dist/src/commands/hooks.js +15 -5
- package/dist/src/commands/memory.js +89 -27
- package/dist/src/commands/metaharness.js +1 -0
- package/dist/src/commands/security.js +113 -55
- package/dist/src/commands/session.js +6 -6
- package/dist/src/commands/task.js +5 -4
- package/dist/src/init/helpers-generator.js +4 -2
- package/dist/src/mcp-server.js +10 -3
- package/dist/src/mcp-tools/agentdb-tools.js +29 -62
- package/dist/src/mcp-tools/hooks-tools.d.ts +0 -4
- package/dist/src/mcp-tools/hooks-tools.js +139 -37
- package/dist/src/mcp-tools/memory-tools.js +89 -35
- package/dist/src/mcp-tools/policy-enforcer.d.ts +8 -1
- package/dist/src/mcp-tools/policy-enforcer.js +80 -8
- package/dist/src/mcp-tools/session-tools.js +34 -8
- package/dist/src/memory/feedback-patterns.d.ts +13 -0
- package/dist/src/memory/feedback-patterns.js +14 -0
- package/dist/src/memory/live-memory-row.d.ts +3 -0
- package/dist/src/memory/live-memory-row.js +5 -0
- package/dist/src/memory/memory-bridge.d.ts +11 -20
- package/dist/src/memory/memory-bridge.js +348 -134
- package/dist/src/memory/memory-initializer.d.ts +9 -1
- package/dist/src/memory/memory-initializer.js +41 -31
- package/dist/src/ruvector/graph-analyzer.js +4 -1
- package/dist/src/services/config-file-manager.js +30 -8
- package/dist/src/services/memory-backup.d.ts +1 -1
- package/dist/src/services/memory-backup.js +67 -27
- package/dist/src/services/worker-daemon.d.ts +2 -2
- package/dist/src/services/worker-daemon.js +23 -14
- package/node_modules/@claude-flow/codex/package.json +1 -1
- package/node_modules/@claude-flow/mcp/dist/tool-registry.d.ts.map +1 -1
- package/node_modules/@claude-flow/mcp/dist/tool-registry.js +11 -3
- package/node_modules/@claude-flow/mcp/dist/tool-registry.js.map +1 -1
- package/node_modules/@claude-flow/mcp/package.json +6 -3
- package/node_modules/@claude-flow/plugin-agent-federation/package.json +3 -3
- package/node_modules/@claude-flow/security/package.json +2 -3
- package/package.json +9 -9
- package/plugins/ruflo-metaharness/scripts/_cli-args.mjs +37 -0
- package/plugins/ruflo-metaharness/scripts/audit-list.mjs +8 -0
- package/plugins/ruflo-metaharness/scripts/audit-trend.mjs +12 -0
- package/plugins/ruflo-metaharness/scripts/bench-parse-mcp-scan.mjs +8 -0
- package/plugins/ruflo-metaharness/scripts/bench-recordpair-overhead.mjs +8 -0
- package/plugins/ruflo-metaharness/scripts/bench-similarity.mjs +8 -0
- package/plugins/ruflo-metaharness/scripts/bench.mjs +10 -0
- package/plugins/ruflo-metaharness/scripts/drift-from-history.mjs +13 -0
- package/plugins/ruflo-metaharness/scripts/evolve.mjs +27 -0
- package/plugins/ruflo-metaharness/scripts/genome.mjs +8 -0
- package/plugins/ruflo-metaharness/scripts/gepa.mjs +12 -0
- package/plugins/ruflo-metaharness/scripts/learn.mjs +13 -0
- package/plugins/ruflo-metaharness/scripts/mcp-scan.mjs +8 -0
- package/plugins/ruflo-metaharness/scripts/mint.mjs +11 -0
- package/plugins/ruflo-metaharness/scripts/oia-audit.mjs +9 -0
- package/plugins/ruflo-metaharness/scripts/router-parallel-analyze.mjs +8 -0
- package/plugins/ruflo-metaharness/scripts/score.mjs +8 -0
- package/plugins/ruflo-metaharness/scripts/security-bench.mjs +11 -0
- package/plugins/ruflo-metaharness/scripts/similarity.mjs +12 -0
- package/plugins/ruflo-metaharness/scripts/test-cli-help.mjs +72 -0
- package/plugins/ruflo-metaharness/scripts/threat-model.mjs +8 -0
|
@@ -12,12 +12,16 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
15
|
-
import { join, dirname } from 'path';
|
|
15
|
+
import { join, dirname, resolve } from 'path';
|
|
16
16
|
import { fileURLToPath } from 'url';
|
|
17
17
|
|
|
18
18
|
const __filename = fileURLToPath(import.meta.url);
|
|
19
19
|
const __dirname = dirname(__filename);
|
|
20
|
-
|
|
20
|
+
// Home-level helpers can serve a different project. Keep the memory writer on
|
|
21
|
+
// the same project root as intelligence.cjs, which reads CLAUDE_PROJECT_DIR.
|
|
22
|
+
const PROJECT_ROOT = process.env.CLAUDE_PROJECT_DIR
|
|
23
|
+
? resolve(process.env.CLAUDE_PROJECT_DIR)
|
|
24
|
+
: join(__dirname, '../..');
|
|
21
25
|
const DATA_DIR = join(PROJECT_ROOT, '.claude-flow', 'data');
|
|
22
26
|
const STORE_PATH = join(DATA_DIR, 'auto-memory-store.json');
|
|
23
27
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest": {
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.48.0",
|
|
4
4
|
"files": {
|
|
5
|
-
"auto-memory-hook.mjs": "
|
|
5
|
+
"auto-memory-hook.mjs": "c8d218526a3c34034c2a69de23f507f0eb0f70d59c16426a8edc849e6ac22529",
|
|
6
6
|
"hook-handler.cjs": "209d9fafe10e17d1be0866727f6f9cf9ac66f9a0793f1c793a4f58319e8e4583",
|
|
7
7
|
"intelligence.cjs": "30e42ed7ec4ca5a94ac54fdb1330d2d47ac5f3fdeeef207753574723a9e77b5c",
|
|
8
8
|
"statusline.cjs": "4a48353b4f1566fa4379b00fd0321b6676a22b6cc91fbbd8380ac5183d619468",
|
|
9
9
|
"router.js": "b6998397e7883191b62229ccdc66fb87f1ccca1d5b2039ecb8c1e686d1ad81f8"
|
|
10
10
|
}
|
|
11
11
|
},
|
|
12
|
-
"signature": "
|
|
12
|
+
"signature": "Y5fY+vJvnledNFp6edS2V1gxJ9P7llOQuq860/gCMvxr9DAzrlDuuKMuGgVP/Tb/25FStYm9+WyBVuOTDos0BQ==",
|
|
13
13
|
"algorithm": "ed25519"
|
|
14
14
|
}
|
|
@@ -19,14 +19,16 @@
|
|
|
19
19
|
|
|
20
20
|
import { createRequire } from 'module';
|
|
21
21
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
22
|
-
import { join, dirname } from 'path';
|
|
22
|
+
import { join, dirname, resolve } from 'path';
|
|
23
23
|
import { fileURLToPath } from 'url';
|
|
24
24
|
import { execSync, spawn } from 'child_process';
|
|
25
25
|
import Database from 'better-sqlite3';
|
|
26
26
|
|
|
27
27
|
const __filename = fileURLToPath(import.meta.url);
|
|
28
28
|
const __dirname = dirname(__filename);
|
|
29
|
-
const PROJECT_ROOT =
|
|
29
|
+
const PROJECT_ROOT = process.env.CLAUDE_PROJECT_DIR
|
|
30
|
+
? resolve(process.env.CLAUDE_PROJECT_DIR)
|
|
31
|
+
: join(__dirname, '../..');
|
|
30
32
|
const DATA_DIR = join(PROJECT_ROOT, '.claude-flow/learning');
|
|
31
33
|
const DB_PATH = join(DATA_DIR, 'patterns.db');
|
|
32
34
|
const METRICS_PATH = join(DATA_DIR, 'learning-metrics.json');
|
|
@@ -7,12 +7,14 @@
|
|
|
7
7
|
|
|
8
8
|
import initSqlJs from 'sql.js';
|
|
9
9
|
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, statSync, openSync, writeSync, fsyncSync, closeSync, renameSync, rmSync } from 'fs';
|
|
10
|
-
import { dirname, join, basename } from 'path';
|
|
10
|
+
import { dirname, join, basename, resolve } from 'path';
|
|
11
11
|
import { fileURLToPath } from 'url';
|
|
12
12
|
import { execSync } from 'child_process';
|
|
13
13
|
|
|
14
14
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
|
-
const PROJECT_ROOT =
|
|
15
|
+
const PROJECT_ROOT = process.env.CLAUDE_PROJECT_DIR
|
|
16
|
+
? resolve(process.env.CLAUDE_PROJECT_DIR)
|
|
17
|
+
: join(__dirname, '../..');
|
|
16
18
|
const V3_DIR = join(PROJECT_ROOT, 'v3');
|
|
17
19
|
const DB_PATH = join(PROJECT_ROOT, '.claude-flow', 'metrics.db');
|
|
18
20
|
|
package/bin/cli.js
CHANGED
|
@@ -302,10 +302,13 @@ if (isMCPMode) {
|
|
|
302
302
|
|
|
303
303
|
try {
|
|
304
304
|
const result = await callMCPTool(toolName, toolParams, { sessionId });
|
|
305
|
+
// Failed task outcomes are data; an explicit handler error is a tool failure.
|
|
306
|
+
const isError = result !== null && typeof result === 'object'
|
|
307
|
+
&& typeof result.error === 'string' && result.error.trim().length > 0;
|
|
305
308
|
return {
|
|
306
309
|
jsonrpc: '2.0',
|
|
307
310
|
id: message.id,
|
|
308
|
-
result: { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] },
|
|
311
|
+
result: { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }], isError },
|
|
309
312
|
};
|
|
310
313
|
} catch (error) {
|
|
311
314
|
return {
|
package/catalog-manifest.json
CHANGED
|
@@ -1480,7 +1480,7 @@ const boundariesCommand = {
|
|
|
1480
1480
|
output.writeln();
|
|
1481
1481
|
for (const cycle of result.circularDependencies.slice(0, 5)) {
|
|
1482
1482
|
const severityColor = cycle.severity === 'high' ? output.error : cycle.severity === 'medium' ? output.warning : output.dim;
|
|
1483
|
-
output.writeln(`${severityColor(`[${cycle.severity.toUpperCase()}]`)} ${cycle.cycle.join(' -> ')}`);
|
|
1483
|
+
output.writeln(`${severityColor.call(output, `[${cycle.severity.toUpperCase()}]`)} ${cycle.cycle.join(' -> ')}`);
|
|
1484
1484
|
output.writeln(output.dim(` ${cycle.suggestion}`));
|
|
1485
1485
|
output.writeln();
|
|
1486
1486
|
}
|
|
@@ -1905,7 +1905,7 @@ const circularCommand = {
|
|
|
1905
1905
|
if (items.length === 0)
|
|
1906
1906
|
continue;
|
|
1907
1907
|
const color = severity === 'high' ? output.error : severity === 'medium' ? output.warning : output.dim;
|
|
1908
|
-
output.writeln(color(output.bold(`${severity.toUpperCase()} SEVERITY (${items.length})`)));
|
|
1908
|
+
output.writeln(color.call(output, output.bold(`${severity.toUpperCase()} SEVERITY (${items.length})`)));
|
|
1909
1909
|
output.writeln();
|
|
1910
1910
|
for (const cycle of items.slice(0, 5)) {
|
|
1911
1911
|
output.writeln(` ${cycle.cycle.join(' -> ')}`);
|
|
@@ -124,15 +124,13 @@ const setCommand = {
|
|
|
124
124
|
name: 'key',
|
|
125
125
|
short: 'k',
|
|
126
126
|
description: 'Configuration key',
|
|
127
|
-
type: 'string'
|
|
128
|
-
required: true
|
|
127
|
+
type: 'string'
|
|
129
128
|
},
|
|
130
129
|
{
|
|
131
130
|
name: 'value',
|
|
132
131
|
short: 'v',
|
|
133
132
|
description: 'Configuration value',
|
|
134
|
-
type: 'string'
|
|
135
|
-
required: true
|
|
133
|
+
type: 'string'
|
|
136
134
|
}
|
|
137
135
|
],
|
|
138
136
|
examples: [
|
|
@@ -140,14 +138,14 @@ const setCommand = {
|
|
|
140
138
|
{ command: 'claude-flow config set -k memory.backend -v agentdb', description: 'Set memory backend' }
|
|
141
139
|
],
|
|
142
140
|
action: async (ctx) => {
|
|
143
|
-
const key = ctx.flags.key
|
|
144
|
-
const value = ctx.flags.value
|
|
141
|
+
const key = (ctx.flags.key ?? ctx.args[0]);
|
|
142
|
+
const value = ctx.flags.value ?? ctx.args[1];
|
|
145
143
|
if (!key || value === undefined) {
|
|
146
144
|
output.printError('Both key and value are required');
|
|
147
145
|
return { success: false, exitCode: 1 };
|
|
148
146
|
}
|
|
149
147
|
try {
|
|
150
|
-
const parsedValue = parseConfigValue(value);
|
|
148
|
+
const parsedValue = parseConfigValue(String(value));
|
|
151
149
|
configManager.set(ctx.cwd, key, parsedValue);
|
|
152
150
|
output.writeln(`Set ${key} = ${value}`);
|
|
153
151
|
return { success: true };
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* V3 CLI Hooks Command
|
|
3
|
-
* Self-learning hooks system for intelligent workflow automation
|
|
4
|
-
*/
|
|
1
|
+
import { validateFeedbackPatterns } from '../memory/feedback-patterns.js';
|
|
5
2
|
import { output } from '../output.js';
|
|
6
3
|
import { confirm } from '../prompt.js';
|
|
7
4
|
import { callMCPTool, MCPClientError } from '../mcp-client.js';
|
|
@@ -572,6 +569,11 @@ const postCommandCommand = {
|
|
|
572
569
|
short: 'd',
|
|
573
570
|
description: 'Execution duration in milliseconds',
|
|
574
571
|
type: 'number'
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
name: 'ttl',
|
|
575
|
+
description: 'Command history lifetime in seconds (default: 30 days)',
|
|
576
|
+
type: 'number'
|
|
575
577
|
}
|
|
576
578
|
],
|
|
577
579
|
examples: [
|
|
@@ -594,11 +596,16 @@ const postCommandCommand = {
|
|
|
594
596
|
success,
|
|
595
597
|
exitCode: ctx.flags.exitCode || 0,
|
|
596
598
|
duration: ctx.flags.duration,
|
|
599
|
+
ttl: ctx.flags.ttl,
|
|
597
600
|
timestamp: Date.now(),
|
|
598
601
|
});
|
|
599
602
|
if (ctx.flags.format === 'json') {
|
|
600
603
|
output.printJson(result);
|
|
601
|
-
return { success:
|
|
604
|
+
return { success: result.recorded, exitCode: result.recorded ? 0 : 1, data: result };
|
|
605
|
+
}
|
|
606
|
+
if (!result.recorded) {
|
|
607
|
+
output.printError('Command outcome could not be recorded');
|
|
608
|
+
return { success: false, exitCode: 1, data: result };
|
|
602
609
|
}
|
|
603
610
|
output.writeln();
|
|
604
611
|
output.printSuccess('Command outcome recorded');
|
|
@@ -1699,6 +1706,7 @@ const postTaskCommand = {
|
|
|
1699
1706
|
name: 'post-task',
|
|
1700
1707
|
description: 'Record task completion for learning',
|
|
1701
1708
|
options: [
|
|
1709
|
+
{ name: 'patterns', description: 'JSON array of learned pattern strings for feedback and skill creation', type: 'string' },
|
|
1702
1710
|
{
|
|
1703
1711
|
name: 'task-id',
|
|
1704
1712
|
short: 'i',
|
|
@@ -1784,8 +1792,10 @@ const postTaskCommand = {
|
|
|
1784
1792
|
const success = ctx.flags.success !== undefined ? ctx.flags.success : true;
|
|
1785
1793
|
output.printInfo(`Recording outcome for task: ${output.highlight(taskId)}`);
|
|
1786
1794
|
try {
|
|
1795
|
+
const patterns = validateFeedbackPatterns(ctx.flags.patterns === undefined ? undefined : JSON.parse(ctx.flags.patterns));
|
|
1787
1796
|
const result = await callMCPTool('hooks_post-task', {
|
|
1788
1797
|
taskId,
|
|
1798
|
+
patterns,
|
|
1789
1799
|
success,
|
|
1790
1800
|
quality: ctx.flags.quality,
|
|
1791
1801
|
agent: ctx.flags.agent,
|
|
@@ -9,6 +9,8 @@ import { distillCommand } from './memory-distill.js';
|
|
|
9
9
|
import { backupCommand } from './memory-backup.js';
|
|
10
10
|
import { countSiblingStoreRows } from '../memory/sibling-store.js';
|
|
11
11
|
import { resolveDbPath } from '../memory/memory-initializer.js';
|
|
12
|
+
import { existsSync } from 'node:fs';
|
|
13
|
+
import { siblingAgentDbPath } from '../memory/memory-bridge.js';
|
|
12
14
|
/**
|
|
13
15
|
* #3228: a miss in one store is not a miss in the memory.
|
|
14
16
|
*
|
|
@@ -26,6 +28,24 @@ async function warnIfSiblingHasRows(pathFlag) {
|
|
|
26
28
|
`That store is written by the MCP/AgentDB path; read it with --path ${unread.path}.`);
|
|
27
29
|
}
|
|
28
30
|
}
|
|
31
|
+
/** Default CLI writes may be mirrored into AgentDB; remove both copies. */
|
|
32
|
+
function removalDbTargets(pathFlag) {
|
|
33
|
+
const primary = resolveDbPath(pathFlag);
|
|
34
|
+
// An explicit file selection is a single-store operation, as documented by
|
|
35
|
+
// --path. Do not widen a caller's requested destructive scope.
|
|
36
|
+
if (pathFlag || process.env.CLAUDE_FLOW_DB_PATH)
|
|
37
|
+
return [{ dbPath: primary }];
|
|
38
|
+
const sibling = siblingAgentDbPath(primary);
|
|
39
|
+
// The AgentDB mirror is consumed by native SQLite and must stay plaintext.
|
|
40
|
+
// Pass this per operation, including preview migrations; never toggle the
|
|
41
|
+
// process encryption setting while asynchronous operations are in flight.
|
|
42
|
+
const targets = [
|
|
43
|
+
{ dbPath: primary },
|
|
44
|
+
...(sibling ? [{ dbPath: sibling, encryptWrites: false }] : []),
|
|
45
|
+
].filter(target => existsSync(target.dbPath));
|
|
46
|
+
// Preserve the existing missing-database error when neither store exists.
|
|
47
|
+
return targets.length ? targets : [{ dbPath: primary }];
|
|
48
|
+
}
|
|
29
49
|
// Memory backends
|
|
30
50
|
const BACKENDS = [
|
|
31
51
|
{ value: 'agentdb', label: 'AgentDB', hint: 'Vector database with HNSW indexing (150x-12,500x faster)' },
|
|
@@ -820,7 +840,7 @@ function formatRelativeTime(isoDate) {
|
|
|
820
840
|
const deleteCommand = {
|
|
821
841
|
name: 'delete',
|
|
822
842
|
aliases: ['rm'],
|
|
823
|
-
description: 'Delete memory entry',
|
|
843
|
+
description: 'Delete a memory entry from the default store and its AgentDB mirror (use --path to select one store)',
|
|
824
844
|
options: [
|
|
825
845
|
{
|
|
826
846
|
name: 'key',
|
|
@@ -870,16 +890,24 @@ const deleteCommand = {
|
|
|
870
890
|
}
|
|
871
891
|
// Use sql.js directly for consistent data access (Issue #980)
|
|
872
892
|
try {
|
|
873
|
-
const { deleteEntry
|
|
874
|
-
const
|
|
875
|
-
const
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
893
|
+
const { deleteEntry } = await import('../memory/memory-initializer.js');
|
|
894
|
+
const paths = removalDbTargets(ctx.flags.path);
|
|
895
|
+
const stores = [];
|
|
896
|
+
for (const target of paths) {
|
|
897
|
+
const { dbPath } = target;
|
|
898
|
+
const entry = await deleteEntry({ key, namespace, ...target });
|
|
899
|
+
if (!entry.success)
|
|
900
|
+
throw new Error(`${dbPath}: ${entry.error || 'Failed to delete'}`);
|
|
901
|
+
stores.push({ dbPath, ...entry });
|
|
879
902
|
}
|
|
903
|
+
const result = {
|
|
904
|
+
success: true, key, namespace, stores,
|
|
905
|
+
deleted: stores.some(entry => entry.deleted),
|
|
906
|
+
remainingEntries: stores.reduce((total, entry) => total + entry.remainingEntries, 0),
|
|
907
|
+
};
|
|
880
908
|
if (result.deleted) {
|
|
881
909
|
output.printSuccess(`Deleted "${key}" from namespace "${namespace}"`);
|
|
882
|
-
output.printInfo(`Remaining entries: ${result.remainingEntries}`);
|
|
910
|
+
output.printInfo(`Remaining entries across ${paths.length} store(s): ${result.remainingEntries}`);
|
|
883
911
|
}
|
|
884
912
|
else {
|
|
885
913
|
output.printWarning(`Key not found: "${key}" in namespace "${namespace}"`);
|
|
@@ -901,7 +929,7 @@ const deleteCommand = {
|
|
|
901
929
|
// either interactive confirmation or --force.
|
|
902
930
|
const purgeCommand = {
|
|
903
931
|
name: 'purge',
|
|
904
|
-
description: 'Permanently delete
|
|
932
|
+
description: 'Permanently delete a namespace from the default store and its AgentDB mirror (use --path to select one store)',
|
|
905
933
|
options: [
|
|
906
934
|
{
|
|
907
935
|
name: 'namespace',
|
|
@@ -939,17 +967,22 @@ const purgeCommand = {
|
|
|
939
967
|
return { success: false, exitCode: 1 };
|
|
940
968
|
}
|
|
941
969
|
try {
|
|
942
|
-
const { listEntries, purgeNamespace
|
|
943
|
-
const
|
|
944
|
-
|
|
945
|
-
const
|
|
970
|
+
const { listEntries, purgeNamespace } = await import('../memory/memory-initializer.js');
|
|
971
|
+
const paths = removalDbTargets(dbPath);
|
|
972
|
+
let previewCount = 0;
|
|
973
|
+
for (const target of paths) {
|
|
974
|
+
const preview = await listEntries({ namespace, limit: 1, ...target });
|
|
975
|
+
if (!preview.success)
|
|
976
|
+
throw new Error(`${target.dbPath}: ${preview.error || 'Failed to read purge target'}`);
|
|
977
|
+
previewCount += preview.total ?? preview.entries?.length ?? 0;
|
|
978
|
+
}
|
|
946
979
|
if (dryRun) {
|
|
947
|
-
output.printInfo(`Would permanently delete ${previewCount} entr${previewCount === 1 ? 'y' : 'ies'} from namespace "${namespace}" (dry run — nothing deleted)`);
|
|
980
|
+
output.printInfo(`Would permanently delete ${previewCount} entr${previewCount === 1 ? 'y' : 'ies'} from namespace "${namespace}" across ${paths.length} store(s) (dry run — nothing deleted)`);
|
|
948
981
|
return { success: true, data: { namespace, wouldDelete: previewCount } };
|
|
949
982
|
}
|
|
950
983
|
if (!force && ctx.interactive) {
|
|
951
984
|
const confirmed = await confirm({
|
|
952
|
-
message: `Permanently delete ${previewCount} entr${previewCount === 1 ? 'y' : 'ies'} from namespace "${namespace}"? This is a hard delete — not reversible with \`memory delete\`'s soft-undo.`,
|
|
985
|
+
message: `Permanently delete ${previewCount} entr${previewCount === 1 ? 'y' : 'ies'} from namespace "${namespace}" across ${paths.length} store(s)? This is a hard delete — not reversible with \`memory delete\`'s soft-undo.`,
|
|
953
986
|
default: false
|
|
954
987
|
});
|
|
955
988
|
if (!confirmed) {
|
|
@@ -961,13 +994,20 @@ const purgeCommand = {
|
|
|
961
994
|
output.printError(`Refusing to purge namespace "${namespace}" without --force in non-interactive mode`);
|
|
962
995
|
return { success: false, exitCode: 1 };
|
|
963
996
|
}
|
|
964
|
-
const
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
997
|
+
const stores = [];
|
|
998
|
+
for (const target of paths) {
|
|
999
|
+
const purged = await purgeNamespace({ namespace, ...target });
|
|
1000
|
+
if (!purged.success)
|
|
1001
|
+
throw new Error(`${target.dbPath}: ${purged.error || 'Failed to purge'}`);
|
|
1002
|
+
stores.push({ dbPath: target.dbPath, ...purged });
|
|
968
1003
|
}
|
|
1004
|
+
const result = {
|
|
1005
|
+
success: true, stores,
|
|
1006
|
+
deletedCount: stores.reduce((total, store) => total + store.deletedCount, 0),
|
|
1007
|
+
remainingEntries: stores.reduce((total, store) => total + store.remainingEntries, 0),
|
|
1008
|
+
};
|
|
969
1009
|
output.printSuccess(`Purged ${result.deletedCount} entr${result.deletedCount === 1 ? 'y' : 'ies'} from namespace "${namespace}"`);
|
|
970
|
-
output.printInfo(`Remaining entries (all namespaces): ${result.remainingEntries}`);
|
|
1010
|
+
output.printInfo(`Remaining entries (all namespaces, ${paths.length} store(s)): ${result.remainingEntries}`);
|
|
971
1011
|
return { success: true, data: result };
|
|
972
1012
|
}
|
|
973
1013
|
catch (error) {
|
|
@@ -984,8 +1024,16 @@ const statsCommand = {
|
|
|
984
1024
|
action: async (ctx) => {
|
|
985
1025
|
// Call MCP memory/stats tool for real statistics
|
|
986
1026
|
try {
|
|
987
|
-
const
|
|
1027
|
+
const dbPath = resolveDbPath(ctx.flags.path);
|
|
1028
|
+
const statsResult = await callMCPTool('memory_stats', { dbPath });
|
|
1029
|
+
if (statsResult.available === false) {
|
|
1030
|
+
output.printError(`Failed to get stats: ${statsResult.error || 'Store unavailable'}`);
|
|
1031
|
+
return { success: false, exitCode: 1 };
|
|
1032
|
+
}
|
|
1033
|
+
const unreadStore = await countSiblingStoreRows(dbPath);
|
|
988
1034
|
const stats = {
|
|
1035
|
+
...(unreadStore ? { unreadStore } : {}),
|
|
1036
|
+
...(statsResult.truncated ? { truncated: true } : {}),
|
|
989
1037
|
backend: statsResult.backend,
|
|
990
1038
|
entries: {
|
|
991
1039
|
total: statsResult.totalEntries,
|
|
@@ -993,7 +1041,7 @@ const statsCommand = {
|
|
|
993
1041
|
text: statsResult.totalEntries
|
|
994
1042
|
},
|
|
995
1043
|
storage: {
|
|
996
|
-
total: statsResult.totalSize,
|
|
1044
|
+
total: statsResult.totalSize ?? 'unknown',
|
|
997
1045
|
location: statsResult.location
|
|
998
1046
|
},
|
|
999
1047
|
version: statsResult.version,
|
|
@@ -1004,6 +1052,9 @@ const statsCommand = {
|
|
|
1004
1052
|
output.printJson(stats);
|
|
1005
1053
|
return { success: true, data: stats };
|
|
1006
1054
|
}
|
|
1055
|
+
if (unreadStore) {
|
|
1056
|
+
output.printWarning(`${unreadStore.rows} entries are in ${unreadStore.path} and were not read here. Read them with --path ${unreadStore.path}.`);
|
|
1057
|
+
}
|
|
1007
1058
|
output.writeln();
|
|
1008
1059
|
output.writeln(output.bold('Memory Statistics'));
|
|
1009
1060
|
output.writeln();
|
|
@@ -1245,19 +1296,27 @@ const cleanupCommand = {
|
|
|
1245
1296
|
{ command: 'claude-flow memory cleanup --expired-only', description: 'Clean expired entries' }
|
|
1246
1297
|
],
|
|
1247
1298
|
action: async (ctx) => {
|
|
1248
|
-
const dryRun = ctx.flags.dryRun;
|
|
1249
|
-
const force = ctx.flags.force;
|
|
1299
|
+
const dryRun = ctx.flags.dryRun === true;
|
|
1300
|
+
const force = ctx.flags.force === true;
|
|
1301
|
+
if (ctx.flags.format === 'json' && !dryRun && !force) {
|
|
1302
|
+
output.printJson({ success: false, error: 'Use --dry-run to preview or --force to authorize cleanup with --format json.' });
|
|
1303
|
+
return { success: false, exitCode: 1 };
|
|
1304
|
+
}
|
|
1250
1305
|
if (dryRun) {
|
|
1251
1306
|
output.writeln(output.warning('DRY RUN - No changes will be made'));
|
|
1252
1307
|
}
|
|
1253
1308
|
output.printInfo('Analyzing memory for cleanup...');
|
|
1254
1309
|
try {
|
|
1255
|
-
const
|
|
1256
|
-
dryRun,
|
|
1310
|
+
const selectors = {
|
|
1257
1311
|
olderThan: ctx.flags.olderThan,
|
|
1258
1312
|
expiredOnly: ctx.flags.expiredOnly,
|
|
1259
1313
|
lowQualityThreshold: ctx.flags.lowQuality,
|
|
1260
1314
|
namespace: ctx.flags.namespace,
|
|
1315
|
+
};
|
|
1316
|
+
// Preview before asking: dryRun:false executes deletion inside the tool.
|
|
1317
|
+
let result = await callMCPTool('memory_cleanup', {
|
|
1318
|
+
...selectors,
|
|
1319
|
+
dryRun: dryRun || !force,
|
|
1261
1320
|
});
|
|
1262
1321
|
if (ctx.flags.format === 'json') {
|
|
1263
1322
|
output.printJson(result);
|
|
@@ -1286,6 +1345,7 @@ const cleanupCommand = {
|
|
|
1286
1345
|
output.printInfo('Cleanup cancelled');
|
|
1287
1346
|
return { success: true, data: result };
|
|
1288
1347
|
}
|
|
1348
|
+
result = await callMCPTool('memory_cleanup', { ...selectors, dryRun: false });
|
|
1289
1349
|
}
|
|
1290
1350
|
if (!dryRun) {
|
|
1291
1351
|
output.writeln();
|
|
@@ -1525,7 +1585,8 @@ const importCommand = {
|
|
|
1525
1585
|
short: 'n',
|
|
1526
1586
|
description: 'Import into specific namespace',
|
|
1527
1587
|
type: 'string'
|
|
1528
|
-
}
|
|
1588
|
+
},
|
|
1589
|
+
DB_PATH_OPTION
|
|
1529
1590
|
],
|
|
1530
1591
|
examples: [
|
|
1531
1592
|
{ command: 'claude-flow memory import -i ./backup.json', description: 'Import from file' },
|
|
@@ -1543,6 +1604,7 @@ const importCommand = {
|
|
|
1543
1604
|
inputPath,
|
|
1544
1605
|
merge: ctx.flags.merge ?? true,
|
|
1545
1606
|
namespace: ctx.flags.namespace,
|
|
1607
|
+
dbPath: resolveDbPath(ctx.flags.path),
|
|
1546
1608
|
});
|
|
1547
1609
|
output.printSuccess(`Imported from ${result.inputPath}`);
|
|
1548
1610
|
output.printList([
|
|
@@ -279,6 +279,7 @@ export const metaharnessCommand = {
|
|
|
279
279
|
'verbose', 'v',
|
|
280
280
|
'quiet', 'q',
|
|
281
281
|
'help', 'h',
|
|
282
|
+
'interactive', 'i', // CLI-only default; plugin scripts are non-interactive
|
|
282
283
|
]);
|
|
283
284
|
// Parser normalizes kebab-case → camelCase (--per-dimension → perDimension).
|
|
284
285
|
// Plugin scripts expect kebab-case at argv, so we re-kebab here.
|