@pcircle/memesh 4.5.0 → 4.6.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-plugin/marketplace.json +5 -3
- package/.claude-plugin/plugin.json +6 -4
- package/.mcp.json +1 -1
- package/AGENTS.md +95 -0
- package/README.de.md +130 -39
- package/README.md +188 -43
- package/README.zh-TW.md +133 -41
- package/dashboard/dist/index.html +9 -9
- package/dist/cli/view-live.js +3 -3
- package/dist/core/analytics.d.ts +3 -3
- package/dist/core/analytics.d.ts.map +1 -1
- package/dist/core/analytics.js.map +1 -1
- package/dist/core/auto-tagger.d.ts.map +1 -1
- package/dist/core/auto-tagger.js +4 -9
- package/dist/core/auto-tagger.js.map +1 -1
- package/dist/core/briefing.d.ts +8 -0
- package/dist/core/briefing.d.ts.map +1 -0
- package/dist/core/briefing.js +91 -0
- package/dist/core/briefing.js.map +1 -0
- package/dist/core/capture-flag.d.ts +5 -0
- package/dist/core/capture-flag.d.ts.map +1 -0
- package/dist/core/capture-flag.js +10 -0
- package/dist/core/capture-flag.js.map +1 -0
- package/dist/core/config.d.ts +0 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +2 -1
- package/dist/core/config.js.map +1 -1
- package/dist/core/conflict-candidates.d.ts +20 -0
- package/dist/core/conflict-candidates.d.ts.map +1 -0
- package/dist/core/conflict-candidates.js +79 -0
- package/dist/core/conflict-candidates.js.map +1 -0
- package/dist/core/conflict-judge.d.ts +47 -0
- package/dist/core/conflict-judge.d.ts.map +1 -0
- package/dist/core/conflict-judge.js +189 -0
- package/dist/core/conflict-judge.js.map +1 -0
- package/dist/core/demo.d.ts +2 -2
- package/dist/core/demo.d.ts.map +1 -1
- package/dist/core/demo.js.map +1 -1
- package/dist/core/digest-validator.d.ts.map +1 -1
- package/dist/core/digest-validator.js +3 -5
- package/dist/core/digest-validator.js.map +1 -1
- package/dist/core/doctor.d.ts +3 -0
- package/dist/core/doctor.d.ts.map +1 -1
- package/dist/core/doctor.js +207 -85
- package/dist/core/doctor.js.map +1 -1
- package/dist/core/dreamer.d.ts +20 -9
- package/dist/core/dreamer.d.ts.map +1 -1
- package/dist/core/dreamer.js +416 -58
- package/dist/core/dreamer.js.map +1 -1
- package/dist/core/embedder.d.ts +7 -5
- package/dist/core/embedder.d.ts.map +1 -1
- package/dist/core/embedder.js +38 -9
- package/dist/core/embedder.js.map +1 -1
- package/dist/core/extractor.d.ts.map +1 -1
- package/dist/core/extractor.js +2 -1
- package/dist/core/extractor.js.map +1 -1
- package/dist/core/failure-analyzer.d.ts.map +1 -1
- package/dist/core/failure-analyzer.js +7 -12
- package/dist/core/failure-analyzer.js.map +1 -1
- package/dist/core/graph.d.ts +4 -4
- package/dist/core/graph.d.ts.map +1 -1
- package/dist/core/graph.js.map +1 -1
- package/dist/core/install-channel.d.ts +1 -1
- package/dist/core/install-channel.d.ts.map +1 -1
- package/dist/core/install-channel.js +16 -5
- package/dist/core/install-channel.js.map +1 -1
- package/dist/core/install-hooks.d.ts +6 -0
- package/dist/core/install-hooks.d.ts.map +1 -1
- package/dist/core/install-hooks.js +0 -0
- package/dist/core/install-hooks.js.map +1 -1
- package/dist/core/json-utils.d.ts +1 -0
- package/dist/core/json-utils.d.ts.map +1 -1
- package/dist/core/json-utils.js +19 -10
- package/dist/core/json-utils.js.map +1 -1
- package/dist/core/kg-backfill.d.ts +3 -4
- package/dist/core/kg-backfill.d.ts.map +1 -1
- package/dist/core/kg-backfill.js +1 -4
- package/dist/core/kg-backfill.js.map +1 -1
- package/dist/core/lesson-engine.d.ts +1 -0
- package/dist/core/lesson-engine.d.ts.map +1 -1
- package/dist/core/lesson-engine.js +1 -0
- package/dist/core/lesson-engine.js.map +1 -1
- package/dist/core/lifecycle.d.ts +4 -4
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +15 -22
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/core/llm-client.d.ts.map +1 -1
- package/dist/core/llm-client.js +3 -6
- package/dist/core/llm-client.js.map +1 -1
- package/dist/core/llm-telemetry.d.ts +4 -4
- package/dist/core/llm-telemetry.d.ts.map +1 -1
- package/dist/core/llm-telemetry.js +1 -1
- package/dist/core/llm-telemetry.js.map +1 -1
- package/dist/core/memory-tool.d.ts.map +1 -1
- package/dist/core/memory-tool.js +8 -4
- package/dist/core/memory-tool.js.map +1 -1
- package/dist/core/operations.d.ts.map +1 -1
- package/dist/core/operations.js +41 -17
- package/dist/core/operations.js.map +1 -1
- package/dist/core/paths.d.ts +3 -0
- package/dist/core/paths.d.ts.map +1 -1
- package/dist/core/paths.js +67 -1
- package/dist/core/paths.js.map +1 -1
- package/dist/core/patterns.d.ts +2 -2
- package/dist/core/patterns.d.ts.map +1 -1
- package/dist/core/patterns.js.map +1 -1
- package/dist/core/project-tags.d.ts +3 -3
- package/dist/core/project-tags.d.ts.map +1 -1
- package/dist/core/project-tags.js.map +1 -1
- package/dist/core/projects.d.ts +2 -2
- package/dist/core/projects.d.ts.map +1 -1
- package/dist/core/projects.js.map +1 -1
- package/dist/core/prompt-safety.d.ts +1 -0
- package/dist/core/prompt-safety.d.ts.map +1 -1
- package/dist/core/prompt-safety.js +7 -0
- package/dist/core/prompt-safety.js.map +1 -1
- package/dist/core/schema-export.d.ts.map +1 -1
- package/dist/core/schema-export.js +27 -30
- package/dist/core/schema-export.js.map +1 -1
- package/dist/core/serializer.d.ts.map +1 -1
- package/dist/core/serializer.js +45 -4
- package/dist/core/serializer.js.map +1 -1
- package/dist/core/setup.d.ts +29 -0
- package/dist/core/setup.d.ts.map +1 -0
- package/dist/core/setup.js +127 -0
- package/dist/core/setup.js.map +1 -0
- package/dist/core/stats.d.ts +2 -2
- package/dist/core/stats.d.ts.map +1 -1
- package/dist/core/stats.js.map +1 -1
- package/dist/core/task-state-store.d.ts +17 -0
- package/dist/core/task-state-store.d.ts.map +1 -0
- package/dist/core/task-state-store.js +45 -0
- package/dist/core/task-state-store.js.map +1 -0
- package/dist/core/task-state.d.ts +19 -0
- package/dist/core/task-state.d.ts.map +1 -0
- package/dist/core/task-state.js +91 -0
- package/dist/core/task-state.js.map +1 -0
- package/dist/core/time-utils.d.ts +2 -0
- package/dist/core/time-utils.d.ts.map +1 -0
- package/dist/core/time-utils.js +14 -0
- package/dist/core/time-utils.js.map +1 -0
- package/dist/core/title.d.ts +5 -0
- package/dist/core/title.d.ts.map +1 -0
- package/dist/core/title.js +14 -0
- package/dist/core/title.js.map +1 -0
- package/dist/core/transcript-extractor.d.ts +5 -6
- package/dist/core/transcript-extractor.d.ts.map +1 -1
- package/dist/core/transcript-extractor.js +4 -24
- package/dist/core/transcript-extractor.js.map +1 -1
- package/dist/core/transcript-source.d.ts.map +1 -1
- package/dist/core/transcript-source.js +2 -3
- package/dist/core/transcript-source.js.map +1 -1
- package/dist/core/types.d.ts +21 -7
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -1
- package/dist/core/work-topology.d.ts +33 -0
- package/dist/core/work-topology.d.ts.map +1 -0
- package/dist/core/work-topology.js +183 -0
- package/dist/core/work-topology.js.map +1 -0
- package/dist/db.d.ts +5 -10
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +194 -196
- package/dist/db.js.map +1 -1
- package/dist/knowledge-graph.d.ts +4 -2
- package/dist/knowledge-graph.d.ts.map +1 -1
- package/dist/knowledge-graph.js +68 -49
- package/dist/knowledge-graph.js.map +1 -1
- package/dist/mcp/server.js +2 -1
- package/dist/mcp/server.js.map +1 -1
- package/dist/skills-manifest.json +61 -36
- package/dist/storage/conflicts.d.ts +3 -3
- package/dist/storage/conflicts.d.ts.map +1 -1
- package/dist/storage/conflicts.js +2 -7
- package/dist/storage/conflicts.js.map +1 -1
- package/dist/storage/fts-index.d.ts +6 -4
- package/dist/storage/fts-index.d.ts.map +1 -1
- package/dist/storage/fts-index.js +16 -4
- package/dist/storage/fts-index.js.map +1 -1
- package/dist/storage/schema.d.ts +20 -0
- package/dist/storage/schema.d.ts.map +1 -0
- package/dist/storage/schema.js +274 -0
- package/dist/storage/schema.js.map +1 -0
- package/dist/storage/sqlite.d.ts +20 -0
- package/dist/storage/sqlite.d.ts.map +1 -0
- package/dist/storage/sqlite.js +64 -0
- package/dist/storage/sqlite.js.map +1 -0
- package/dist/storage/vector-index.d.ts +3 -0
- package/dist/storage/vector-index.d.ts.map +1 -0
- package/dist/storage/vector-index.js +7 -0
- package/dist/storage/vector-index.js.map +1 -0
- package/dist/transports/cli/cli.d.ts +1 -4
- package/dist/transports/cli/cli.d.ts.map +1 -1
- package/dist/transports/cli/cli.js +494 -76
- package/dist/transports/cli/cli.js.map +1 -1
- package/dist/transports/http/retired-routes.d.ts.map +1 -1
- package/dist/transports/http/retired-routes.js +1 -0
- package/dist/transports/http/retired-routes.js.map +1 -1
- package/dist/transports/http/server.d.ts.map +1 -1
- package/dist/transports/http/server.js +243 -323
- package/dist/transports/http/server.js.map +1 -1
- package/dist/transports/mcp/handlers.d.ts +48 -94
- package/dist/transports/mcp/handlers.d.ts.map +1 -1
- package/dist/transports/mcp/handlers.js +77 -56
- package/dist/transports/mcp/handlers.js.map +1 -1
- package/dist/transports/schemas.d.ts +31 -40
- package/dist/transports/schemas.d.ts.map +1 -1
- package/dist/transports/schemas.js +31 -37
- package/dist/transports/schemas.js.map +1 -1
- package/hooks/hooks.json +0 -10
- package/llms-install.md +138 -0
- package/package.json +19 -18
- package/scripts/hooks/_generated/capture-flag.js +17 -0
- package/scripts/hooks/_generated/core-paths.js +67 -1
- package/scripts/hooks/_generated/fts-index.js +16 -4
- package/scripts/hooks/_generated/schema.js +281 -0
- package/scripts/hooks/_generated/sqlite.js +71 -0
- package/scripts/hooks/_generated/task-state.js +98 -0
- package/scripts/hooks/_generated/time-utils.js +21 -0
- package/scripts/hooks/_generated/title.js +21 -0
- package/scripts/hooks/_generated/work-topology.js +190 -0
- package/scripts/hooks/_shared.js +269 -534
- package/scripts/hooks/post-commit.js +55 -10
- package/scripts/hooks/pre-compact.js +22 -7
- package/scripts/hooks/pre-edit-recall.js +9 -11
- package/scripts/hooks/session-start.js +230 -106
- package/scripts/hooks/session-summary.js +176 -73
- package/scripts/hooks/user-prompt-intent.js +3 -2
- package/skills/memesh/SKILL.md +97 -77
- package/README.es.md +0 -470
- package/README.fr.md +0 -462
- package/README.ja.md +0 -470
- package/README.ko.md +0 -470
- package/README.pt.md +0 -462
- package/README.th.md +0 -463
- package/README.vi.md +0 -462
- package/README.zh-CN.md +0 -469
- package/dist/cli/view.d.ts +0 -3
- package/dist/cli/view.d.ts.map +0 -1
- package/dist/cli/view.js +0 -523
- package/dist/cli/view.js.map +0 -1
- package/dist/core/skill-usage-log.d.ts +0 -11
- package/dist/core/skill-usage-log.d.ts.map +0 -1
- package/dist/core/skill-usage-log.js +0 -125
- package/dist/core/skill-usage-log.js.map +0 -1
- package/dist/core/verifier.d.ts +0 -40
- package/dist/core/verifier.d.ts.map +0 -1
- package/dist/core/verifier.js +0 -206
- package/dist/core/verifier.js.map +0 -1
- package/dist/mcp/launcher.d.ts +0 -3
- package/dist/mcp/launcher.d.ts.map +0 -1
- package/dist/mcp/launcher.js +0 -37
- package/dist/mcp/launcher.js.map +0 -1
- package/scripts/hooks/pre-bash-orchestration-nudge.js +0 -155
- package/scripts/postinstall-rebuild.mjs +0 -41
- package/skills/agentic-orchestration/SKILL.md +0 -399
|
@@ -6,11 +6,16 @@ import path from 'path';
|
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
7
7
|
import { openDatabase, closeDatabase, getDatabase, reindexFts, allowVectorIndexRebuild } from '../../db.js';
|
|
8
8
|
import { remember, recallWithConflicts, forget, exportMemories, importMemories, learn, reindex, setPinned } from '../../core/operations.js';
|
|
9
|
-
import { verifyAgentWork } from '../../core/verifier.js';
|
|
10
9
|
import { readConfig, writeConfig, maskApiKey, detectCapabilities } from '../../core/config.js';
|
|
11
10
|
import { MAX_LANGUAGE_LENGTH, languageValueError } from '../../core/output-language.js';
|
|
12
|
-
import { getDbPath } from '../../core/paths.js';
|
|
11
|
+
import { getDbPath, homeDir, redactSecrets, redactUserPaths } from '../../core/paths.js';
|
|
13
12
|
import { flushPendingEmbeddings, canRefillVectorIndex } from '../../core/embedder.js';
|
|
13
|
+
import { NAMESPACES } from '../../core/types.js';
|
|
14
|
+
import { assembleBriefing } from '../../core/briefing.js';
|
|
15
|
+
import { inspectHosts, allWired } from '../../core/setup.js';
|
|
16
|
+
import { installHooks } from '../../core/install-hooks.js';
|
|
17
|
+
import { getTaskState, setTaskState } from '../../core/task-state-store.js';
|
|
18
|
+
import { TASK_STATE_FIELDS, taskStateLines } from '../../core/task-state.js';
|
|
14
19
|
async function withDatabase(fn) {
|
|
15
20
|
openDatabase();
|
|
16
21
|
try {
|
|
@@ -20,13 +25,40 @@ async function withDatabase(fn) {
|
|
|
20
25
|
closeDatabase();
|
|
21
26
|
}
|
|
22
27
|
}
|
|
28
|
+
function requireOneOf(value, allowed, flag) {
|
|
29
|
+
if (value === undefined || allowed.includes(value))
|
|
30
|
+
return;
|
|
31
|
+
console.error(`Error: ${flag} "${value}" is not valid. Use one of: ${allowed.join(', ')}.`);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
function isOnPath(tool) {
|
|
35
|
+
const exts = process.platform === 'win32'
|
|
36
|
+
? (process.env.PATHEXT ?? '.EXE;.CMD;.BAT;.COM').split(';').filter(Boolean)
|
|
37
|
+
: [''];
|
|
38
|
+
for (const dir of (process.env.PATH ?? '').split(path.delimiter)) {
|
|
39
|
+
if (!dir)
|
|
40
|
+
continue;
|
|
41
|
+
for (const ext of exts) {
|
|
42
|
+
try {
|
|
43
|
+
fs.accessSync(path.join(dir, tool + ext), fs.constants.X_OK);
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
catch { }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
function wireUserHooks() {
|
|
52
|
+
const r = installHooks({ pluginRoot: packageRoot, pluginVersion: pkg.version, scope: 'user' });
|
|
53
|
+
return `hooks: added ${r.added}, skipped ${r.skipped} already-installed${r.backupPath ? ` (backup: ${r.backupPath})` : ''}`;
|
|
54
|
+
}
|
|
23
55
|
const packageJsonPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../package.json');
|
|
24
56
|
const packageRoot = path.dirname(packageJsonPath);
|
|
25
57
|
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
26
58
|
const program = new Command();
|
|
27
59
|
program
|
|
28
60
|
.name('memesh')
|
|
29
|
-
.description('MeMesh —
|
|
61
|
+
.description('MeMesh — Agentic memory for coding agents')
|
|
30
62
|
.version(pkg.version)
|
|
31
63
|
.allowExcessArguments(true)
|
|
32
64
|
.showSuggestionAfterError(true);
|
|
@@ -36,11 +68,15 @@ program
|
|
|
36
68
|
.description('Store knowledge as an entity (use flags for explicit form, or positional text for quick capture)')
|
|
37
69
|
.option('--name <name>', 'Entity name')
|
|
38
70
|
.option('--type <type>', 'Entity type')
|
|
71
|
+
.option('--title <title>', 'Short human-readable label shown as the headline (name stays the stable machine key)')
|
|
39
72
|
.option('--obs <observations...>', 'Observations (space-separated)')
|
|
40
73
|
.option('--tags <tags...>', 'Tags (space-separated)')
|
|
41
|
-
.option('--namespace <namespace>', 'Namespace: personal, team, or global (default
|
|
74
|
+
.option('--namespace <namespace>', 'Namespace: personal, team, or global. On a NEW memory this places it (default personal); on one that already exists it MOVES it out of the scope it is in — omit the flag to leave it alone.')
|
|
75
|
+
.option('--supersedes <name...>', 'This memory replaces the named one — ARCHIVES it immediately (recoverable; nothing is deleted)')
|
|
76
|
+
.option('--contradicts <name...>', 'This memory cannot both be true with the named one — both surface as a conflict on every recall')
|
|
42
77
|
.option('--json', 'Output as JSON')
|
|
43
78
|
.action(async (text, opts) => {
|
|
79
|
+
requireOneOf(opts.namespace, NAMESPACES, '--namespace');
|
|
44
80
|
if (text && !opts.name && !opts.type) {
|
|
45
81
|
const slug = String(text)
|
|
46
82
|
.toLowerCase()
|
|
@@ -51,6 +87,8 @@ program
|
|
|
51
87
|
const suffix = randomBytes(3).toString('hex');
|
|
52
88
|
opts.name = `quick-${date}-${slug || 'note'}-${suffix}`;
|
|
53
89
|
opts.type = 'note';
|
|
90
|
+
if (!opts.title)
|
|
91
|
+
opts.title = String(text).slice(0, 200);
|
|
54
92
|
if (!opts.obs || opts.obs.length === 0)
|
|
55
93
|
opts.obs = [String(text)];
|
|
56
94
|
else
|
|
@@ -68,20 +106,43 @@ program
|
|
|
68
106
|
' memesh remember "Use OAuth 2.0 with PKCE"');
|
|
69
107
|
process.exit(1);
|
|
70
108
|
}
|
|
109
|
+
const relations = [
|
|
110
|
+
...(opts.supersedes ?? []).map(to => ({ to, type: 'supersedes' })),
|
|
111
|
+
...(opts.contradicts ?? []).map(to => ({ to, type: 'contradicts' })),
|
|
112
|
+
];
|
|
71
113
|
await withDatabase(async () => {
|
|
72
114
|
const result = remember({
|
|
73
115
|
name: opts.name,
|
|
74
116
|
type: opts.type,
|
|
117
|
+
title: opts.title,
|
|
75
118
|
observations: opts.obs,
|
|
76
119
|
tags: opts.tags,
|
|
77
120
|
namespace: opts.namespace,
|
|
121
|
+
relations: relations.length > 0 ? relations : undefined,
|
|
122
|
+
sourceHost: 'cli',
|
|
78
123
|
});
|
|
79
124
|
if (opts.json) {
|
|
80
125
|
console.log(JSON.stringify(result));
|
|
81
126
|
}
|
|
82
127
|
else {
|
|
83
128
|
console.log(`✅ Stored "${result.name}" (${result.observations} observations, ${result.tags} tags)`);
|
|
129
|
+
if (result.movedFromNamespace) {
|
|
130
|
+
console.log(` moved: ${result.movedFromNamespace} → ${opts.namespace} (was in ${result.movedFromNamespace}; re-run with --namespace ${result.movedFromNamespace} to put it back)`);
|
|
131
|
+
}
|
|
132
|
+
if (result.superseded?.length) {
|
|
133
|
+
console.log(` archived as superseded: ${result.superseded.join(', ')}`);
|
|
134
|
+
}
|
|
135
|
+
const contradicted = (result.relationsCreated ?? [])
|
|
136
|
+
.filter(r => r.type === 'contradicts')
|
|
137
|
+
.map(r => r.to);
|
|
138
|
+
if (contradicted.length > 0) {
|
|
139
|
+
console.log(` conflicts stated: ${contradicted.join(', ')}`);
|
|
140
|
+
}
|
|
141
|
+
for (const err of result.relationErrors ?? [])
|
|
142
|
+
console.error(` ⚠️ ${err}`);
|
|
84
143
|
}
|
|
144
|
+
if (result.relationErrors?.length)
|
|
145
|
+
process.exitCode = 1;
|
|
85
146
|
await flushPendingEmbeddings();
|
|
86
147
|
});
|
|
87
148
|
});
|
|
@@ -96,6 +157,7 @@ program
|
|
|
96
157
|
.option('--cross-project', 'Search across all project tags (ignores --tag filter)')
|
|
97
158
|
.option('--json', 'Output as JSON')
|
|
98
159
|
.action(async (query, opts) => {
|
|
160
|
+
requireOneOf(opts.namespace, NAMESPACES, '--namespace');
|
|
99
161
|
await withDatabase(async () => {
|
|
100
162
|
const { entities, conflicts } = await recallWithConflicts({
|
|
101
163
|
query: query || undefined,
|
|
@@ -173,8 +235,14 @@ program
|
|
|
173
235
|
else if (result.observation_removed) {
|
|
174
236
|
console.log(`✂️ Removed observation (${result.remaining_observations} remaining)`);
|
|
175
237
|
}
|
|
238
|
+
else if (opts.observation && result.entity_found) {
|
|
239
|
+
console.log(`Entity "${opts.name}" has no observation matching that text (${result.remaining_observations} observation(s) present).`);
|
|
240
|
+
console.log(`See them with: memesh recall "${opts.name}" --json`);
|
|
241
|
+
process.exitCode = 1;
|
|
242
|
+
}
|
|
176
243
|
else {
|
|
177
244
|
console.log(`Entity "${opts.name}" not found`);
|
|
245
|
+
process.exitCode = 1;
|
|
178
246
|
}
|
|
179
247
|
});
|
|
180
248
|
});
|
|
@@ -219,6 +287,31 @@ program
|
|
|
219
287
|
console.error('so it is not a like-for-like replacement for compressing one named entity.');
|
|
220
288
|
process.exitCode = 1;
|
|
221
289
|
});
|
|
290
|
+
program
|
|
291
|
+
.command('verify')
|
|
292
|
+
.description('(retired) Removed with the agentic-orchestration experiment — see the message this prints')
|
|
293
|
+
.allowUnknownOption()
|
|
294
|
+
.action(() => {
|
|
295
|
+
console.error('`memesh verify` has been retired, along with the agentic-orchestration experiment.');
|
|
296
|
+
console.error('');
|
|
297
|
+
console.error('It recorded a verification report for background-agent work. The protocol it');
|
|
298
|
+
console.error('served was removed without ever leaving opt-in. Run your own verification');
|
|
299
|
+
console.error('(typecheck / tests / lint) and store conclusions with `memesh remember` if you');
|
|
300
|
+
console.error('want them remembered. Existing verification_record entities are untouched.');
|
|
301
|
+
process.exitCode = 1;
|
|
302
|
+
});
|
|
303
|
+
program
|
|
304
|
+
.command('patterns')
|
|
305
|
+
.description('(retired) Removed with the agentic-orchestration experiment — see the message this prints')
|
|
306
|
+
.allowUnknownOption()
|
|
307
|
+
.action(() => {
|
|
308
|
+
console.error('`memesh patterns` has been retired, along with the agentic-orchestration experiment.');
|
|
309
|
+
console.error('');
|
|
310
|
+
console.error('It displayed the experiment\'s local skill-usage telemetry, which is no longer');
|
|
311
|
+
console.error('written. A leftover ~/.memesh/skill-usage.jsonl is inert and safe to delete.');
|
|
312
|
+
console.error('For work-pattern insights, use the `user_patterns` MCP tool or GET /v1/patterns.');
|
|
313
|
+
process.exitCode = 1;
|
|
314
|
+
});
|
|
222
315
|
program
|
|
223
316
|
.command('export')
|
|
224
317
|
.description('Export memories as JSON. Defaults to stdout (pipe-friendly); use `-o <file>` to write directly.')
|
|
@@ -227,6 +320,7 @@ program
|
|
|
227
320
|
.option('--limit <n>', 'Max entities to export', '1000')
|
|
228
321
|
.option('-o, --out <file>', 'Write JSON to <file> instead of stdout. Parent directory must exist.')
|
|
229
322
|
.action(async (opts) => {
|
|
323
|
+
requireOneOf(opts.namespace, NAMESPACES, '--namespace');
|
|
230
324
|
await withDatabase(() => {
|
|
231
325
|
const result = exportMemories({
|
|
232
326
|
tag: opts.tag,
|
|
@@ -235,6 +329,12 @@ program
|
|
|
235
329
|
});
|
|
236
330
|
const json = JSON.stringify(result, null, 2);
|
|
237
331
|
if (opts.out) {
|
|
332
|
+
const outDir = path.dirname(path.resolve(opts.out));
|
|
333
|
+
if (!fs.existsSync(outDir)) {
|
|
334
|
+
console.error(`Error: cannot write ${opts.out} — the directory ${outDir} does not exist.`);
|
|
335
|
+
console.error(` Create it first (mkdir -p "${outDir}"), or drop -o to write to stdout.`);
|
|
336
|
+
process.exit(1);
|
|
337
|
+
}
|
|
238
338
|
fs.writeFileSync(opts.out, json + '\n');
|
|
239
339
|
process.stderr.write(`✅ Exported ${result.entity_count} entities to ${opts.out}\n`);
|
|
240
340
|
}
|
|
@@ -250,6 +350,8 @@ program
|
|
|
250
350
|
.option('--namespace <ns>', 'Override namespace for all imported entities')
|
|
251
351
|
.option('--merge <strategy>', 'Merge strategy: skip | overwrite | append', 'skip')
|
|
252
352
|
.action(async (file, opts) => {
|
|
353
|
+
requireOneOf(opts.merge, ['skip', 'overwrite', 'append'], '--merge');
|
|
354
|
+
requireOneOf(opts.namespace, NAMESPACES, '--namespace');
|
|
253
355
|
await withDatabase(() => {
|
|
254
356
|
let raw;
|
|
255
357
|
try {
|
|
@@ -281,11 +383,18 @@ program
|
|
|
281
383
|
console.error(` Try: memesh export > my-export.json && memesh import my-export.json`);
|
|
282
384
|
process.exit(1);
|
|
283
385
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
386
|
+
let result;
|
|
387
|
+
try {
|
|
388
|
+
result = importMemories({
|
|
389
|
+
data: data,
|
|
390
|
+
namespace: opts.namespace,
|
|
391
|
+
merge_strategy: opts.merge,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
catch (err) {
|
|
395
|
+
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
396
|
+
process.exit(1);
|
|
397
|
+
}
|
|
289
398
|
console.log(`Imported: ${result.imported}, Skipped: ${result.skipped}, Appended: ${result.appended}`);
|
|
290
399
|
if (result.errors.length > 0) {
|
|
291
400
|
console.error(`Errors:\n ${result.errors.join('\n ')}`);
|
|
@@ -303,6 +412,7 @@ program
|
|
|
303
412
|
.option('--severity <level>', 'Severity: critical|major|minor', 'minor')
|
|
304
413
|
.option('--json', 'Output as JSON')
|
|
305
414
|
.action(async (opts) => {
|
|
415
|
+
requireOneOf(opts.severity, ['critical', 'major', 'minor'], '--severity');
|
|
306
416
|
await withDatabase(() => {
|
|
307
417
|
const result = learn({
|
|
308
418
|
error: opts.error,
|
|
@@ -310,6 +420,7 @@ program
|
|
|
310
420
|
root_cause: opts.rootCause,
|
|
311
421
|
prevention: opts.prevention,
|
|
312
422
|
severity: opts.severity,
|
|
423
|
+
sourceHost: 'cli',
|
|
313
424
|
});
|
|
314
425
|
if (opts.json) {
|
|
315
426
|
console.log(JSON.stringify(result));
|
|
@@ -320,47 +431,162 @@ program
|
|
|
320
431
|
});
|
|
321
432
|
});
|
|
322
433
|
program
|
|
323
|
-
.command('
|
|
324
|
-
.description('
|
|
325
|
-
'
|
|
326
|
-
.requiredOption('--agent-id <id>', 'Identifier for the agent being verified')
|
|
327
|
-
.option('--base <ref>', 'Git ref to diff against (default: merge-base with origin/main)')
|
|
328
|
-
.option('--expected-files <n>', 'Number of files the agent claimed to change', (v) => parseInt(v, 10))
|
|
329
|
-
.option('--report <path>', 'Path to a JSON file with pre-computed report (typecheck/tests/lint/build)')
|
|
434
|
+
.command('briefing')
|
|
435
|
+
.description('The assembled work topology for a project — task state, decisions, lessons, knowledge, recent activity')
|
|
436
|
+
.option('--project <name>', 'Project name (default: the current directory’s project)')
|
|
330
437
|
.option('--json', 'Output as JSON')
|
|
331
|
-
.action(async (
|
|
438
|
+
.action(async (opts) => {
|
|
332
439
|
await withDatabase(() => {
|
|
333
|
-
|
|
334
|
-
|
|
440
|
+
const result = assembleBriefing(opts.project);
|
|
441
|
+
if (opts.json) {
|
|
442
|
+
console.log(JSON.stringify(result));
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
if (!result.text) {
|
|
446
|
+
console.log(`No memories for "${result.project}" yet.\n` +
|
|
447
|
+
`Capture happens automatically as you work; or set the task state: memesh task --goal "…"`);
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
console.log(result.text);
|
|
451
|
+
});
|
|
452
|
+
});
|
|
453
|
+
program
|
|
454
|
+
.command('setup')
|
|
455
|
+
.description('Detect Claude Code / Codex / Gemini on this machine, wire memesh into each, and verify')
|
|
456
|
+
.option('--check', 'Only report wiring status per host; change nothing (exit 1 if a present host is unwired)')
|
|
457
|
+
.option('--yes', 'Apply every wiring action without asking')
|
|
458
|
+
.action(async (opts) => {
|
|
459
|
+
const { spawnSync, execFileSync } = await import('child_process');
|
|
460
|
+
const runSeam = (cmd, args) => {
|
|
335
461
|
try {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
462
|
+
let target = cmd;
|
|
463
|
+
let useShell = false;
|
|
464
|
+
if (process.platform === 'win32') {
|
|
465
|
+
const resolved = execFileSync('where', [cmd], { encoding: 'utf8' }).split(/\r?\n/)[0]?.trim();
|
|
466
|
+
if (resolved) {
|
|
467
|
+
target = resolved;
|
|
468
|
+
useShell = /\.(cmd|bat)$/i.test(resolved);
|
|
469
|
+
}
|
|
339
470
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
workdir: path.resolve(workdir),
|
|
343
|
-
base: opts.base,
|
|
344
|
-
claim: opts.expectedFiles != null ? { expected_files: opts.expectedFiles } : undefined,
|
|
345
|
-
report: externalReport,
|
|
346
|
-
});
|
|
471
|
+
const r = spawnSync(target, args, { encoding: 'utf8', shell: useShell });
|
|
472
|
+
return { status: r.status, stderr: r.stderr ?? '' };
|
|
347
473
|
}
|
|
348
474
|
catch (err) {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
475
|
+
return { status: null, stderr: err instanceof Error ? err.message : String(err) };
|
|
476
|
+
}
|
|
477
|
+
};
|
|
478
|
+
const seams = { home: () => homeDir(), isOnPath, run: runSeam };
|
|
479
|
+
const render = (statuses) => {
|
|
480
|
+
for (const st of statuses) {
|
|
481
|
+
if (!st.present) {
|
|
482
|
+
console.log(` ${st.title}: not found (looked for ${st.presenceDetail}) — skipped`);
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
const mark = st.wired === true ? '✅' : st.wired === false ? '❌' : '❓';
|
|
486
|
+
console.log(`${mark} ${st.title}: ${st.wiredDetail}`);
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
let statuses = inspectHosts(seams);
|
|
490
|
+
if (opts.check) {
|
|
491
|
+
render(statuses);
|
|
492
|
+
process.exit(allWired(statuses) ? 0 : 1);
|
|
493
|
+
}
|
|
494
|
+
const pending = statuses.filter((st) => st.present && st.actions.length > 0);
|
|
495
|
+
if (pending.length === 0) {
|
|
496
|
+
render(statuses);
|
|
497
|
+
console.log(allWired(statuses)
|
|
498
|
+
? '\nEverything present is wired. Nothing to do.'
|
|
499
|
+
: '\nNothing to wire automatically — see the lines above.');
|
|
500
|
+
process.exit(allWired(statuses) ? 0 : 1);
|
|
501
|
+
}
|
|
502
|
+
render(statuses);
|
|
503
|
+
console.log('\nPlanned actions:');
|
|
504
|
+
for (const st of pending)
|
|
505
|
+
for (const a of st.actions) {
|
|
506
|
+
console.log(` • [${st.title}] ${a.label}${a.cmd ? `\n ${a.cmd} ${(a.args ?? []).join(' ')}` : ''}`);
|
|
507
|
+
}
|
|
508
|
+
if (!opts.yes && !process.stdin.isTTY) {
|
|
509
|
+
console.error('\nNot a terminal and --yes not given — nothing was changed. Re-run with: memesh setup --yes');
|
|
510
|
+
process.exit(1);
|
|
511
|
+
}
|
|
512
|
+
const confirmAll = Boolean(opts.yes);
|
|
513
|
+
let rl = null;
|
|
514
|
+
if (!confirmAll) {
|
|
515
|
+
const { createInterface } = await import('node:readline/promises');
|
|
516
|
+
rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
517
|
+
}
|
|
518
|
+
let failed = false;
|
|
519
|
+
for (const st of pending) {
|
|
520
|
+
for (const action of st.actions) {
|
|
521
|
+
if (!confirmAll && rl) {
|
|
522
|
+
const answer = (await rl.question(`\n[${st.title}] ${action.label} — proceed? [y/N] `)).trim().toLowerCase();
|
|
523
|
+
if (answer !== 'y' && answer !== 'yes') {
|
|
524
|
+
console.log(' skipped');
|
|
525
|
+
continue;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
if (action.kind === 'install-hooks') {
|
|
529
|
+
console.log(` ✅ ${wireUserHooks()}`);
|
|
530
|
+
}
|
|
531
|
+
else if (action.cmd) {
|
|
532
|
+
const r = runSeam(action.cmd, action.args ?? []);
|
|
533
|
+
if (r.status === 0)
|
|
534
|
+
console.log(` ✅ done (${action.cmd} ${(action.args ?? []).join(' ')})`);
|
|
535
|
+
else {
|
|
536
|
+
console.error(` ❌ ${action.cmd} exited ${r.status ?? 'without running'}${r.stderr ? `: ${r.stderr.trim()}` : ''}`);
|
|
537
|
+
failed = true;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
352
540
|
}
|
|
353
|
-
|
|
354
|
-
|
|
541
|
+
}
|
|
542
|
+
rl?.close();
|
|
543
|
+
console.log('\nAfter wiring:');
|
|
544
|
+
statuses = inspectHosts(seams);
|
|
545
|
+
render(statuses);
|
|
546
|
+
process.exit(failed || !allWired(statuses) ? 1 : 0);
|
|
547
|
+
});
|
|
548
|
+
program
|
|
549
|
+
.command('task')
|
|
550
|
+
.description('Show or update where the work stands on this project')
|
|
551
|
+
.option('--project <name>', 'Project name (default: the current directory’s project)')
|
|
552
|
+
.option('--goal <text>', 'What this work is FOR — the outcome being aimed at')
|
|
553
|
+
.option('--next <text>', 'The next concrete step')
|
|
554
|
+
.option('--blocked <text>', 'What is standing in the way (pass "" to clear it once resolved)')
|
|
555
|
+
.option('--done <text>', 'What was just finished')
|
|
556
|
+
.option('--json', 'Output as JSON')
|
|
557
|
+
.action(async (opts) => {
|
|
558
|
+
await withDatabase(() => {
|
|
559
|
+
const patch = {};
|
|
560
|
+
for (const field of TASK_STATE_FIELDS) {
|
|
561
|
+
if (opts[field] !== undefined)
|
|
562
|
+
patch[field] = opts[field];
|
|
355
563
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
564
|
+
if (Object.keys(patch).length === 0) {
|
|
565
|
+
const { project, state } = getTaskState(opts.project);
|
|
566
|
+
if (opts.json) {
|
|
567
|
+
console.log(JSON.stringify({ project, state }));
|
|
568
|
+
return;
|
|
361
569
|
}
|
|
570
|
+
const lines = taskStateLines(state, project);
|
|
571
|
+
if (lines.length === 0) {
|
|
572
|
+
console.log(`Nothing recorded for "${project}" yet.\n` +
|
|
573
|
+
`Set it with: memesh task --goal "…" --next "…"`);
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
console.log(lines.join('\n'));
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
const result = setTaskState({ project: opts.project, patch, sourceHost: 'cli' });
|
|
580
|
+
if (opts.json) {
|
|
581
|
+
console.log(JSON.stringify(result));
|
|
582
|
+
return;
|
|
362
583
|
}
|
|
363
|
-
|
|
584
|
+
if (result.changed.length === 0) {
|
|
585
|
+
console.log(`No change — "${result.project}" already said exactly that.`);
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
console.log(`Updated ${result.changed.join(', ')} for "${result.project}".`);
|
|
589
|
+
console.log(taskStateLines(result.state, result.project).join('\n'));
|
|
364
590
|
});
|
|
365
591
|
});
|
|
366
592
|
const configCmd = program.command('config').description('Manage configuration');
|
|
@@ -392,10 +618,10 @@ const ALLOWED_KEYS = new Set([
|
|
|
392
618
|
'embedder.model',
|
|
393
619
|
'autoUpdate',
|
|
394
620
|
'sessionLimit',
|
|
395
|
-
'enableAgenticOrchestration',
|
|
396
621
|
'autoCapture',
|
|
397
622
|
'llmFallbacks',
|
|
398
623
|
'language',
|
|
624
|
+
'transcriptMining',
|
|
399
625
|
]);
|
|
400
626
|
const KEY_VALIDATORS = {
|
|
401
627
|
'llm.provider': (v) => ['anthropic', 'openai', 'ollama'].includes(v) ? null : `must be one of: anthropic, openai, ollama`,
|
|
@@ -462,7 +688,7 @@ function formatConfigValue(key, raw) {
|
|
|
462
688
|
return JSON.stringify(raw);
|
|
463
689
|
return String(raw);
|
|
464
690
|
}
|
|
465
|
-
|
|
691
|
+
function buildConfigListing(config) {
|
|
466
692
|
const rows = [];
|
|
467
693
|
for (const key of Array.from(ALLOWED_KEYS).sort()) {
|
|
468
694
|
const raw = getNested(config, key.split('.'));
|
|
@@ -516,7 +742,10 @@ configCmd
|
|
|
516
742
|
coerced = parseInt(value, 10);
|
|
517
743
|
if (canonical === 'llmFallbacks')
|
|
518
744
|
coerced = JSON.parse(value);
|
|
519
|
-
if (canonical === '
|
|
745
|
+
if (canonical === 'autoCapture') {
|
|
746
|
+
coerced = value === 'true' || value === '1';
|
|
747
|
+
}
|
|
748
|
+
if (canonical === 'transcriptMining') {
|
|
520
749
|
coerced = value === 'true' || value === '1';
|
|
521
750
|
}
|
|
522
751
|
const config = readConfig();
|
|
@@ -524,6 +753,10 @@ configCmd
|
|
|
524
753
|
writeConfig(config);
|
|
525
754
|
const displayValue = canonical.toLowerCase().includes('key') ? maskApiKey(String(value)) : String(value);
|
|
526
755
|
console.log(`✅ Set ${canonical} = ${displayValue}`);
|
|
756
|
+
if (canonical === 'llm.apiKey' && !config.llm?.provider) {
|
|
757
|
+
console.log('⚠️ No llm.provider is set, so this key is not used yet and LLM features stay off.');
|
|
758
|
+
console.log(' Set one with: memesh config set llm.provider <anthropic|openai|ollama>');
|
|
759
|
+
}
|
|
527
760
|
});
|
|
528
761
|
configCmd
|
|
529
762
|
.command('unset')
|
|
@@ -592,10 +825,16 @@ program
|
|
|
592
825
|
.description('Start the HTTP API server and web dashboard')
|
|
593
826
|
.option('--port <port>', 'Port number', '3737')
|
|
594
827
|
.option('--host <host>', 'Host to bind', '127.0.0.1')
|
|
595
|
-
.option('--allow-remote', '
|
|
828
|
+
.option('--allow-remote', 'Permit binding to a non-loopback host. Pair it with --host; on a non-loopback bind a bearer token is generated and REQUIRED for every /v1 request, and the startup output says where it lives. On the default loopback host this flag changes nothing.')
|
|
596
829
|
.action(async (opts) => {
|
|
597
830
|
const { startServer } = await import('../http/server.js');
|
|
598
|
-
|
|
831
|
+
try {
|
|
832
|
+
startServer(opts.host, parseInt(opts.port, 10), { allowRemote: opts.allowRemote, autoUpdateCheck: true });
|
|
833
|
+
}
|
|
834
|
+
catch (err) {
|
|
835
|
+
console.error(`MeMesh: ${err instanceof Error ? err.message : String(err)}`);
|
|
836
|
+
process.exit(1);
|
|
837
|
+
}
|
|
599
838
|
});
|
|
600
839
|
program
|
|
601
840
|
.command('update')
|
|
@@ -634,6 +873,53 @@ program
|
|
|
634
873
|
process.exit(1);
|
|
635
874
|
}
|
|
636
875
|
});
|
|
876
|
+
program
|
|
877
|
+
.command('upgrade-plugin')
|
|
878
|
+
.description('Upgrade the Claude Code plugin install (finds and runs its bundled upgrade script)')
|
|
879
|
+
.action(async () => {
|
|
880
|
+
const { spawnSync } = await import('child_process');
|
|
881
|
+
const cacheRoot = path.join(homeDir(), '.claude', 'plugins', 'cache', 'pcircle-memesh', 'memesh');
|
|
882
|
+
let versions = [];
|
|
883
|
+
try {
|
|
884
|
+
versions = fs.readdirSync(cacheRoot, { withFileTypes: true })
|
|
885
|
+
.filter((entry) => entry.isDirectory() && /^\d+\.\d+\.\d+/.test(entry.name))
|
|
886
|
+
.map((entry) => entry.name);
|
|
887
|
+
}
|
|
888
|
+
catch { }
|
|
889
|
+
if (versions.length === 0) {
|
|
890
|
+
console.error('No Claude Code plugin install found (looked in ~/.claude/plugins/cache/pcircle-memesh).');
|
|
891
|
+
console.error('If you installed via npm, upgrade with: memesh update');
|
|
892
|
+
process.exit(1);
|
|
893
|
+
}
|
|
894
|
+
versions.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
|
|
895
|
+
const newest = versions[versions.length - 1];
|
|
896
|
+
const script = path.join(cacheRoot, newest, 'scripts', 'upgrade-plugin.sh');
|
|
897
|
+
if (!fs.existsSync(script)) {
|
|
898
|
+
console.error(`Plugin install found (v${newest}), but it has no scripts/upgrade-plugin.sh — plugin versions before 4.2.5 shipped without it.`);
|
|
899
|
+
console.error('Reinstall once from the Claude Code /plugin UI, or run the npm-global copy directly:');
|
|
900
|
+
console.error(' bash "$(npm prefix -g)/lib/node_modules/@pcircle/memesh/scripts/upgrade-plugin.sh"');
|
|
901
|
+
process.exit(1);
|
|
902
|
+
}
|
|
903
|
+
const installHints = {
|
|
904
|
+
node: 'node is required by the upgrade script. Install Node.js from https://nodejs.org',
|
|
905
|
+
npm: 'npm is required by the upgrade script. It ships with Node.js — reinstall from https://nodejs.org',
|
|
906
|
+
rsync: 'rsync is required by the upgrade script. macOS: already installed; Debian/Ubuntu: sudo apt install rsync',
|
|
907
|
+
};
|
|
908
|
+
const missing = Object.keys(installHints).filter((tool) => !isOnPath(tool));
|
|
909
|
+
if (missing.length > 0) {
|
|
910
|
+
for (const tool of missing)
|
|
911
|
+
console.error(installHints[tool]);
|
|
912
|
+
process.exit(1);
|
|
913
|
+
}
|
|
914
|
+
const run = spawnSync('bash', [script], { stdio: 'inherit' });
|
|
915
|
+
if (run.error) {
|
|
916
|
+
console.error(`Could not run the upgrade script: ${run.error.message}`);
|
|
917
|
+
console.error('bash is required to run it. If bash is available under another name, run it yourself:');
|
|
918
|
+
console.error(` bash ${script}`);
|
|
919
|
+
process.exit(1);
|
|
920
|
+
}
|
|
921
|
+
process.exit(run.status ?? 1);
|
|
922
|
+
});
|
|
637
923
|
program
|
|
638
924
|
.command('telemetry')
|
|
639
925
|
.description('Show LLM call telemetry (per-flow scorecard for the last N days)')
|
|
@@ -641,6 +927,12 @@ program
|
|
|
641
927
|
.option('--prune <days>', 'Delete rows older than N days BEFORE rendering (closes v4.2.0 retention gap)', (v) => parseInt(v, 10))
|
|
642
928
|
.option('--json', 'Output as JSON')
|
|
643
929
|
.action(async (opts) => {
|
|
930
|
+
for (const [flag, value] of [['--window', opts.window], ['--prune', opts.prune]]) {
|
|
931
|
+
if (value !== undefined && !Number.isFinite(value)) {
|
|
932
|
+
console.error(`Error: ${flag} needs a number of days.`);
|
|
933
|
+
process.exit(1);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
644
936
|
await withDatabase(async () => {
|
|
645
937
|
const { summariseTelemetry, pruneTelemetry } = await import('../../core/llm-telemetry.js');
|
|
646
938
|
let pruneResult = null;
|
|
@@ -856,15 +1148,82 @@ program
|
|
|
856
1148
|
.option('--probe-http', 'Also probe the local HTTP server health endpoint')
|
|
857
1149
|
.option('--probe', 'Make one small live call to the configured LLM to confirm it actually answers')
|
|
858
1150
|
.option('--url <url>', 'Base URL for --probe-http', 'http://127.0.0.1:3737')
|
|
1151
|
+
.option('--fix', 'Apply the whitelisted fixes doctor prescribes (asks per fix; --yes skips asking)')
|
|
1152
|
+
.option('--yes', 'With --fix: apply without asking')
|
|
859
1153
|
.action(async (opts) => {
|
|
860
1154
|
const { formatDoctorReport, runDoctor } = await import('../../core/doctor.js');
|
|
861
|
-
|
|
1155
|
+
let result = await runDoctor({
|
|
862
1156
|
packageRoot,
|
|
863
1157
|
packageVersion: pkg.version,
|
|
864
1158
|
probeHttp: opts.probeHttp,
|
|
865
1159
|
probeCapabilities: opts.probe,
|
|
866
1160
|
httpBaseUrl: opts.url,
|
|
867
1161
|
});
|
|
1162
|
+
if (opts.fix) {
|
|
1163
|
+
const FIX_ACTIONS = {
|
|
1164
|
+
'install-hooks': wireUserHooks,
|
|
1165
|
+
'fts-rebuild': () => {
|
|
1166
|
+
openDatabase();
|
|
1167
|
+
try {
|
|
1168
|
+
return `keyword index rebuilt (${reindexFts().entities} entities)`;
|
|
1169
|
+
}
|
|
1170
|
+
finally {
|
|
1171
|
+
closeDatabase();
|
|
1172
|
+
}
|
|
1173
|
+
},
|
|
1174
|
+
'chmod-db': () => {
|
|
1175
|
+
fs.chmodSync(getDbPath(), 0o600);
|
|
1176
|
+
return `permissions restored: chmod 600 ${getDbPath()}`;
|
|
1177
|
+
},
|
|
1178
|
+
};
|
|
1179
|
+
const fixable = result.checks.filter((c) => c.fixId && (c.status === 'warn' || c.status === 'fail'));
|
|
1180
|
+
if (fixable.length === 0) {
|
|
1181
|
+
console.log('Nothing on the --fix whitelist to apply.');
|
|
1182
|
+
}
|
|
1183
|
+
else {
|
|
1184
|
+
if (!opts.yes && !process.stdin.isTTY) {
|
|
1185
|
+
console.error('Not a terminal and --yes not given — nothing was changed. Re-run with: memesh doctor --fix --yes');
|
|
1186
|
+
process.exit(1);
|
|
1187
|
+
}
|
|
1188
|
+
let rl = null;
|
|
1189
|
+
if (!opts.yes) {
|
|
1190
|
+
const { createInterface } = await import('node:readline/promises');
|
|
1191
|
+
rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1192
|
+
}
|
|
1193
|
+
for (const check of fixable) {
|
|
1194
|
+
console.log(`\n${check.label}: ${check.summary}`);
|
|
1195
|
+
if (rl) {
|
|
1196
|
+
const answer = (await rl.question(`Apply fix (${check.fixId})? [y/N] `)).trim().toLowerCase();
|
|
1197
|
+
if (answer !== 'y' && answer !== 'yes') {
|
|
1198
|
+
console.log(' skipped');
|
|
1199
|
+
continue;
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
try {
|
|
1203
|
+
console.log(` ✅ ${FIX_ACTIONS[check.fixId]()}`);
|
|
1204
|
+
}
|
|
1205
|
+
catch (err) {
|
|
1206
|
+
console.error(` ❌ ${err instanceof Error ? err.message : String(err)}`);
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
rl?.close();
|
|
1210
|
+
console.log('\nAfter fixes:');
|
|
1211
|
+
const before = new Map(result.checks.map((c) => [c.id, c.status]));
|
|
1212
|
+
const fixedIds = new Set(fixable.map((c) => c.id));
|
|
1213
|
+
result = await runDoctor({
|
|
1214
|
+
packageRoot,
|
|
1215
|
+
packageVersion: pkg.version,
|
|
1216
|
+
probeHttp: false,
|
|
1217
|
+
probeCapabilities: false,
|
|
1218
|
+
httpBaseUrl: opts.url,
|
|
1219
|
+
});
|
|
1220
|
+
for (const c of result.checks) {
|
|
1221
|
+
const was = before.get(c.id);
|
|
1222
|
+
if (fixedIds.has(c.id) && was && was !== c.status)
|
|
1223
|
+
console.log(` ${c.label}: ${was} → ${c.status}`);
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
868
1227
|
if (opts.json) {
|
|
869
1228
|
console.log(JSON.stringify(result, null, 2));
|
|
870
1229
|
}
|
|
@@ -1011,6 +1370,11 @@ dreamCmd
|
|
|
1011
1370
|
});
|
|
1012
1371
|
console.log(`${opts.dryRun ? '[dry-run] ' : ''}Dream pass complete in ${result.durationMs}ms`);
|
|
1013
1372
|
console.log(` clusters scanned: ${result.clustersScanned}`);
|
|
1373
|
+
if (result.clusteringMode) {
|
|
1374
|
+
console.log(` grouped by: ${result.clusteringMode === 'semantic' ? 'meaning (embeddings)' : 'calendar week (no embeddings)'}`);
|
|
1375
|
+
}
|
|
1376
|
+
if (result.clusteringNote)
|
|
1377
|
+
console.log(` note: ${result.clusteringNote}`);
|
|
1014
1378
|
console.log(` LLM calls: ${result.llmCalls}`);
|
|
1015
1379
|
console.log(` proposals created: ${result.proposalsCreated}`);
|
|
1016
1380
|
if (result.skipped.length > 0) {
|
|
@@ -1094,7 +1458,8 @@ dreamCmd
|
|
|
1094
1458
|
console.log(`${proposals.length} ${opts.status} proposal(s):`);
|
|
1095
1459
|
console.log('');
|
|
1096
1460
|
for (const p of proposals) {
|
|
1097
|
-
const srcLabel = p.
|
|
1461
|
+
const srcLabel = p.kind === 'relation' ? ' (conflict)'
|
|
1462
|
+
: p.source_kind === 'transcript' ? ' (transcript)' : '';
|
|
1098
1463
|
console.log(` #${p.id} [${p.project}/${p.cluster_key}]${srcLabel} ${p.source_count} source(s) → "${p.digest_name}"`);
|
|
1099
1464
|
if (p.digest_observations_preview !== null) {
|
|
1100
1465
|
console.log(` ${p.digest_observations_preview}`);
|
|
@@ -1105,6 +1470,49 @@ dreamCmd
|
|
|
1105
1470
|
console.log(`Inspect: memesh dream show <id> | Apply: memesh dream accept <id> | Reject: memesh dream reject <id>`);
|
|
1106
1471
|
});
|
|
1107
1472
|
});
|
|
1473
|
+
dreamCmd
|
|
1474
|
+
.command('conflicts')
|
|
1475
|
+
.description('Judge semantically-close memory pairs for contradiction / supersession / duplication (LLM) and stage relation proposals for review')
|
|
1476
|
+
.option('--max-pairs <n>', 'Judge at most N of the tightest candidate pairs this run (default 20)', (v) => parseInt(v, 10))
|
|
1477
|
+
.option('--dry-run', 'Show how many candidates are queued without calling an LLM or writing anything')
|
|
1478
|
+
.action(async (opts) => {
|
|
1479
|
+
await withDatabase(async () => {
|
|
1480
|
+
const cfg = readConfig();
|
|
1481
|
+
const llm = detectCapabilities().llm;
|
|
1482
|
+
if (!llm) {
|
|
1483
|
+
console.error('No LLM configured. Run `memesh config set llm.provider <anthropic|openai|ollama>` first (or set ANTHROPIC_API_KEY / OPENAI_API_KEY).');
|
|
1484
|
+
console.error('LLM is required for `memesh dream conflicts` because "do these two entries disagree" is a semantic judgement, not a rule.');
|
|
1485
|
+
process.exit(1);
|
|
1486
|
+
}
|
|
1487
|
+
const { judgeConflicts, CONFLICT_JUDGE_MAX_PAIRS } = await import('../../core/conflict-judge.js');
|
|
1488
|
+
const { getDatabase } = await import('../../db.js');
|
|
1489
|
+
const result = await judgeConflicts(getDatabase(), llm, {
|
|
1490
|
+
maxPairs: typeof opts.maxPairs === 'number' && !Number.isNaN(opts.maxPairs) ? opts.maxPairs : CONFLICT_JUDGE_MAX_PAIRS,
|
|
1491
|
+
dryRun: !!opts.dryRun,
|
|
1492
|
+
fallbacks: cfg.llmFallbacks,
|
|
1493
|
+
});
|
|
1494
|
+
console.log(`${opts.dryRun ? '[dry-run] ' : ''}Conflict pass complete in ${result.durationMs}ms`);
|
|
1495
|
+
console.log(` candidate pairs available: ${result.candidatesAvailable}`);
|
|
1496
|
+
if (opts.dryRun) {
|
|
1497
|
+
console.log(' (dry run — no LLM called, nothing judged or written)');
|
|
1498
|
+
return;
|
|
1499
|
+
}
|
|
1500
|
+
console.log(` LLM calls: ${result.llmCalls}`);
|
|
1501
|
+
console.log(` judged: ${result.judged} (${result.unrelated} unrelated, remembered so they are never re-bought)`);
|
|
1502
|
+
console.log(` staged: ${result.staged} relation proposal(s)`);
|
|
1503
|
+
if (result.llmFailures > 0) {
|
|
1504
|
+
console.log(` failed: ${result.llmFailures} (unparseable or errored LLM responses; those pairs stay at the head of the candidate list and are retried next run)`);
|
|
1505
|
+
}
|
|
1506
|
+
if (result.aborted) {
|
|
1507
|
+
console.error(` ABORTED after the work above: ${result.aborted}`);
|
|
1508
|
+
process.exit(1);
|
|
1509
|
+
}
|
|
1510
|
+
if (result.staged > 0) {
|
|
1511
|
+
console.log('');
|
|
1512
|
+
console.log('Review: memesh dream list | Apply: memesh dream accept <id> | Reject: memesh dream reject <id>');
|
|
1513
|
+
}
|
|
1514
|
+
});
|
|
1515
|
+
});
|
|
1108
1516
|
dreamCmd
|
|
1109
1517
|
.command('show <id>')
|
|
1110
1518
|
.description('Show a proposal in full — name, type, ALL observations, tags, source — so you can review the whole thing before accepting')
|
|
@@ -1126,6 +1534,31 @@ dreamCmd
|
|
|
1126
1534
|
console.log(`Proposal #${detail.id} [${detail.project}/${detail.cluster_key}] source: ${detail.source_kind} status: ${detail.status}`);
|
|
1127
1535
|
console.log(`created: ${detail.created_at}`);
|
|
1128
1536
|
console.log('');
|
|
1537
|
+
if (detail.kind === 'relation') {
|
|
1538
|
+
const rel = detail.relation;
|
|
1539
|
+
if (!rel) {
|
|
1540
|
+
console.error('relation payload is corrupt — reject this proposal');
|
|
1541
|
+
process.exit(1);
|
|
1542
|
+
}
|
|
1543
|
+
const [fromE, toE] = rel.relation_type === 'supersedes' && rel.direction === 'b_supersedes_a' ? [rel.b, rel.a] : [rel.a, rel.b];
|
|
1544
|
+
console.log(`verdict: ${rel.verdict} (severity: ${rel.severity ?? 'unknown'})`);
|
|
1545
|
+
console.log(`accepting creates: ${fromE?.name} —${rel.relation_type}→ ${toE?.name}`);
|
|
1546
|
+
if (rel.relation_type === 'supersedes') {
|
|
1547
|
+
console.log(` survivor: ${fromE?.name} (the arrow points from the surviving claim to the obsolete one)`);
|
|
1548
|
+
}
|
|
1549
|
+
console.log(`rationale: ${rel.rationale ?? '(none given)'}`);
|
|
1550
|
+
if (rel.recommended_action)
|
|
1551
|
+
console.log(`recommended action: ${rel.recommended_action}`);
|
|
1552
|
+
if (rel.excerpts?.a || rel.excerpts?.b) {
|
|
1553
|
+
console.log(`excerpt A (${rel.a?.name}): ${rel.excerpts?.a ?? ''}`);
|
|
1554
|
+
console.log(`excerpt B (${rel.b?.name}): ${rel.excerpts?.b ?? ''}`);
|
|
1555
|
+
}
|
|
1556
|
+
if (typeof rel.cosine_distance === 'number')
|
|
1557
|
+
console.log(`cosine distance: ${rel.cosine_distance.toFixed(3)}`);
|
|
1558
|
+
console.log('');
|
|
1559
|
+
console.log(`Accept: memesh dream accept ${detail.id} | Reject: memesh dream reject ${detail.id}`);
|
|
1560
|
+
return;
|
|
1561
|
+
}
|
|
1129
1562
|
console.log(`name: ${detail.digest.name}`);
|
|
1130
1563
|
console.log(`type: ${detail.digest.type}`);
|
|
1131
1564
|
console.log(`observations (${detail.digest.observations.length}):`);
|
|
@@ -1209,6 +1642,9 @@ program
|
|
|
1209
1642
|
}
|
|
1210
1643
|
console.log(`${opts.dryRun ? '[dry-run] ' : ''}Settings: ${result.settingsPath}`);
|
|
1211
1644
|
console.log(`${opts.dryRun ? '[dry-run] Would add ' : 'Added '}${result.added} hook entr${result.added === 1 ? 'y' : 'ies'}, ${opts.dryRun ? 'would skip ' : 'skipped '}${result.skipped} already-installed.`);
|
|
1645
|
+
if (result.pruned > 0) {
|
|
1646
|
+
console.log(`${opts.dryRun ? '[dry-run] Would remove ' : 'Removed '}${result.pruned} retired memesh hook entr${result.pruned === 1 ? 'y' : 'ies'} no longer shipped by this version.`);
|
|
1647
|
+
}
|
|
1212
1648
|
if (result.backupPath)
|
|
1213
1649
|
console.log(`Backup: ${result.backupPath}`);
|
|
1214
1650
|
if (result.conflicts.length > 0) {
|
|
@@ -1289,11 +1725,19 @@ program
|
|
|
1289
1725
|
body += `\n\n---\n**System Info**\n- Version: \`${pkg.version}\`\n- Node: \`${process.version}\`\n- Platform: \`${process.platform} ${process.arch}\`\n_Diagnostics unavailable: doctor probe failed._`;
|
|
1290
1726
|
}
|
|
1291
1727
|
}
|
|
1292
|
-
|
|
1728
|
+
body = redactUserPaths(redactSecrets(body));
|
|
1729
|
+
const url = `https://github.com/PCIRCLE-AI/memesh/issues/new?title=${encodeURIComponent(`[${typeLabel}] `)}&body=${encodeURIComponent(body)}&labels=${encodeURIComponent(labels)}`;
|
|
1293
1730
|
if (opts.open === false) {
|
|
1294
1731
|
console.log(url);
|
|
1295
1732
|
return;
|
|
1296
1733
|
}
|
|
1734
|
+
console.log('This will be pre-filled into a PUBLIC GitHub issue:');
|
|
1735
|
+
console.log('---');
|
|
1736
|
+
console.log(body);
|
|
1737
|
+
console.log('---');
|
|
1738
|
+
if (opts.diagnostics !== false) {
|
|
1739
|
+
console.log('Re-run with --no-diagnostics to leave out the install ID and the doctor report.');
|
|
1740
|
+
}
|
|
1297
1741
|
const { spawn } = await import('child_process');
|
|
1298
1742
|
const cmd = process.platform === 'darwin' ? 'open'
|
|
1299
1743
|
: process.platform === 'win32' ? 'cmd'
|
|
@@ -1319,6 +1763,7 @@ program
|
|
|
1319
1763
|
'stored embedding first. Needed only when switching embedding providers.')
|
|
1320
1764
|
.option('--json', 'Output as JSON')
|
|
1321
1765
|
.action(async (opts) => {
|
|
1766
|
+
requireOneOf(opts.namespace, NAMESPACES, '--namespace');
|
|
1322
1767
|
try {
|
|
1323
1768
|
if (opts.fts) {
|
|
1324
1769
|
if (opts.vectors) {
|
|
@@ -1403,33 +1848,6 @@ program
|
|
|
1403
1848
|
throw err;
|
|
1404
1849
|
}
|
|
1405
1850
|
});
|
|
1406
|
-
program
|
|
1407
|
-
.command('patterns')
|
|
1408
|
-
.description('Show local skill-usage telemetry (agentic-orchestration banner injections, verify_agent_work invocations). Local-only — never uploaded.')
|
|
1409
|
-
.option('--json', 'Output as JSON')
|
|
1410
|
-
.action(async (opts) => {
|
|
1411
|
-
const { summariseSkillUsage } = await import('../../core/skill-usage-log.js');
|
|
1412
|
-
const summary = summariseSkillUsage();
|
|
1413
|
-
if (opts.json) {
|
|
1414
|
-
console.log(JSON.stringify(summary, null, 2));
|
|
1415
|
-
return;
|
|
1416
|
-
}
|
|
1417
|
-
console.log(`Skill usage log: ${summary.log_path}`);
|
|
1418
|
-
console.log(` total events: ${summary.total_events}`);
|
|
1419
|
-
console.log(` log size: ${summary.log_bytes} bytes`);
|
|
1420
|
-
if (summary.first_event)
|
|
1421
|
-
console.log(` first event: ${summary.first_event}`);
|
|
1422
|
-
if (summary.last_event)
|
|
1423
|
-
console.log(` last event: ${summary.last_event}`);
|
|
1424
|
-
if (summary.total_events === 0) {
|
|
1425
|
-
console.log(' (no events recorded yet — open a Claude Code session with memesh installed, or run a verification, to start collecting)');
|
|
1426
|
-
return;
|
|
1427
|
-
}
|
|
1428
|
-
console.log(' events by name:');
|
|
1429
|
-
for (const [name, count] of Object.entries(summary.events_by_name).sort((a, b) => b[1] - a[1])) {
|
|
1430
|
-
console.log(` ${count.toString().padStart(6)} ${name}`);
|
|
1431
|
-
}
|
|
1432
|
-
});
|
|
1433
1851
|
program
|
|
1434
1852
|
.command('status')
|
|
1435
1853
|
.description('Show MeMesh status and capabilities')
|
|
@@ -1444,7 +1862,7 @@ program
|
|
|
1444
1862
|
console.log(`MeMesh v${pkg.version}`);
|
|
1445
1863
|
console.log(`Search level: ${caps.searchLevel} (${caps.searchLevel === 1 ? 'Smart Mode' : 'Core'})`);
|
|
1446
1864
|
console.log(`Embeddings: ${caps.embeddings}`);
|
|
1447
|
-
console.log(`LLM: ${caps.llm ? `${caps.llm.provider} (${caps.llm.model})` : 'not configured'}`);
|
|
1865
|
+
console.log(`LLM: ${caps.llm ? `${caps.llm.provider} (${caps.llm.model ?? 'default'})` : 'not configured'}`);
|
|
1448
1866
|
console.log(`Install method: ${installSupport.label}`);
|
|
1449
1867
|
for (const line of formatUpdateCheckStatus(update)) {
|
|
1450
1868
|
console.log(`\n${line}`);
|