@pcircle/memesh 4.8.5 → 4.9.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 +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/mcp.json +6 -4
- package/.codex-plugin/plugin.json +1 -1
- package/AGENTS.md +60 -14
- package/README.de.md +83 -556
- package/README.md +83 -582
- package/README.zh-TW.md +84 -573
- package/dashboard/dist/index.html +10 -10
- package/dist/cli/view-live.d.ts.map +1 -1
- package/dist/cli/view-live.js +154 -384
- package/dist/cli/view-live.js.map +1 -1
- package/dist/core/agent-router.d.ts +11 -10
- package/dist/core/agent-router.d.ts.map +1 -1
- package/dist/core/agent-router.js +29 -14
- package/dist/core/agent-router.js.map +1 -1
- package/dist/core/agent-scope-id.d.ts.map +1 -1
- package/dist/core/agent-scope-id.js.map +1 -1
- package/dist/core/analytics.d.ts.map +1 -1
- package/dist/core/analytics.js.map +1 -1
- package/dist/core/config.d.ts +4 -40
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +75 -141
- package/dist/core/config.js.map +1 -1
- package/dist/core/demo.d.ts.map +1 -1
- package/dist/core/demo.js +6 -6
- package/dist/core/demo.js.map +1 -1
- package/dist/core/doctor.d.ts +1 -6
- package/dist/core/doctor.d.ts.map +1 -1
- package/dist/core/doctor.js +55 -227
- package/dist/core/doctor.js.map +1 -1
- package/dist/core/dreamer.d.ts +32 -47
- package/dist/core/dreamer.d.ts.map +1 -1
- package/dist/core/dreamer.js +211 -703
- package/dist/core/dreamer.js.map +1 -1
- package/dist/core/install-channel.d.ts.map +1 -1
- package/dist/core/install-channel.js +4 -47
- package/dist/core/install-channel.js.map +1 -1
- package/dist/core/install-id.d.ts.map +1 -1
- package/dist/core/install-id.js.map +1 -1
- package/dist/core/kg-backfill.d.ts.map +1 -1
- package/dist/core/kg-backfill.js.map +1 -1
- package/dist/core/lesson-engine.d.ts +0 -5
- package/dist/core/lesson-engine.d.ts.map +1 -1
- package/dist/core/lesson-engine.js +0 -25
- package/dist/core/lesson-engine.js.map +1 -1
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +54 -48
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/core/memory-tool.d.ts.map +1 -1
- package/dist/core/memory-tool.js +17 -17
- package/dist/core/memory-tool.js.map +1 -1
- package/dist/core/operations.d.ts +2 -26
- package/dist/core/operations.d.ts.map +1 -1
- package/dist/core/operations.js +9 -244
- package/dist/core/operations.js.map +1 -1
- package/dist/core/paths.d.ts +4 -1
- package/dist/core/paths.d.ts.map +1 -1
- package/dist/core/paths.js +88 -14
- package/dist/core/paths.js.map +1 -1
- package/dist/core/product-improvements.js +2 -2
- package/dist/core/product-improvements.js.map +1 -1
- package/dist/core/schema-export.d.ts.map +1 -1
- package/dist/core/schema-export.js +10 -0
- package/dist/core/schema-export.js.map +1 -1
- package/dist/core/semver.d.ts +7 -0
- package/dist/core/semver.d.ts.map +1 -0
- package/dist/core/semver.js +49 -0
- package/dist/core/semver.js.map +1 -0
- package/dist/core/serializer.d.ts.map +1 -1
- package/dist/core/serializer.js +69 -57
- package/dist/core/serializer.js.map +1 -1
- package/dist/core/signal-scorer.d.ts.map +1 -1
- package/dist/core/signal-scorer.js.map +1 -1
- package/dist/core/transcript-extractor.d.ts +1 -85
- package/dist/core/transcript-extractor.d.ts.map +1 -1
- package/dist/core/transcript-extractor.js +5 -364
- package/dist/core/transcript-extractor.js.map +1 -1
- package/dist/core/transcript-source.d.ts +22 -6
- package/dist/core/transcript-source.d.ts.map +1 -1
- package/dist/core/transcript-source.js +108 -69
- package/dist/core/transcript-source.js.map +1 -1
- package/dist/core/types.d.ts +1 -17
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/version-check.d.ts.map +1 -1
- package/dist/core/version-check.js +11 -1
- package/dist/core/version-check.js.map +1 -1
- package/dist/db.d.ts +0 -34
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +3 -287
- package/dist/db.js.map +1 -1
- package/dist/host-runtime/acp.d.ts.map +1 -1
- package/dist/host-runtime/acp.js +2 -2
- package/dist/host-runtime/acp.js.map +1 -1
- package/dist/host-runtime/claude.d.ts.map +1 -1
- package/dist/host-runtime/claude.js +9 -7
- package/dist/host-runtime/claude.js.map +1 -1
- package/dist/host-runtime/codex-session.d.ts +9 -1
- package/dist/host-runtime/codex-session.d.ts.map +1 -1
- package/dist/host-runtime/codex-session.js +474 -29
- package/dist/host-runtime/codex-session.js.map +1 -1
- package/dist/host-runtime/codex.d.ts.map +1 -1
- package/dist/host-runtime/codex.js +2 -2
- package/dist/host-runtime/codex.js.map +1 -1
- package/dist/host-runtime/config.d.ts +1 -0
- package/dist/host-runtime/config.d.ts.map +1 -1
- package/dist/host-runtime/config.js +4 -0
- package/dist/host-runtime/config.js.map +1 -1
- package/dist/host-runtime/router-client.d.ts.map +1 -1
- package/dist/host-runtime/router-client.js +62 -15
- package/dist/host-runtime/router-client.js.map +1 -1
- package/dist/host-runtime/router.js +2 -2
- package/dist/host-runtime/router.js.map +1 -1
- package/dist/knowledge-graph.d.ts.map +1 -1
- package/dist/knowledge-graph.js +54 -45
- package/dist/knowledge-graph.js.map +1 -1
- package/dist/mcp/THIRD_PARTY_NOTICES.txt +217 -0
- package/dist/mcp/server.js +30685 -38
- package/dist/mcp/server.js.map +6 -1
- package/dist/skills-manifest.json +22 -22
- package/dist/storage/entity-index.d.ts +0 -1
- package/dist/storage/entity-index.d.ts.map +1 -1
- package/dist/storage/entity-index.js +0 -7
- package/dist/storage/entity-index.js.map +1 -1
- package/dist/storage/fts-index.d.ts.map +1 -1
- package/dist/storage/fts-index.js +6 -7
- package/dist/storage/fts-index.js.map +1 -1
- package/dist/storage/graph-repairs.d.ts +0 -5
- package/dist/storage/graph-repairs.d.ts.map +1 -1
- package/dist/storage/graph-repairs.js +4 -91
- package/dist/storage/graph-repairs.js.map +1 -1
- package/dist/storage/schema.d.ts +1 -1
- package/dist/storage/schema.d.ts.map +1 -1
- package/dist/storage/schema.js +1 -2
- package/dist/storage/schema.js.map +1 -1
- package/dist/storage/sqlite.d.ts +0 -1
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/transports/agent-messaging.d.ts.map +1 -1
- package/dist/transports/agent-messaging.js +5 -7
- package/dist/transports/agent-messaging.js.map +1 -1
- package/dist/transports/cli/cli.d.ts.map +1 -1
- package/dist/transports/cli/cli.js +52 -674
- package/dist/transports/cli/cli.js.map +1 -1
- package/dist/transports/http/retired-routes.js +1 -1
- 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 +13 -232
- package/dist/transports/http/server.js.map +1 -1
- package/dist/transports/mcp/handlers.d.ts +127 -1
- package/dist/transports/mcp/handlers.d.ts.map +1 -1
- package/dist/transports/mcp/handlers.js +53 -4
- package/dist/transports/mcp/handlers.js.map +1 -1
- package/dist/transports/schemas.d.ts +52 -0
- package/dist/transports/schemas.d.ts.map +1 -1
- package/dist/transports/schemas.js +39 -0
- package/dist/transports/schemas.js.map +1 -1
- package/docs/platforms/README.md +5 -4
- package/docs/platforms/agent-messaging.md +124 -60
- package/hooks/hooks.json +13 -2
- package/llms-install.md +62 -30
- package/package.json +4 -5
- package/scripts/hooks/_generated/core-paths.js +88 -14
- package/scripts/hooks/_generated/fts-index.js +6 -7
- package/scripts/hooks/_generated/schema.js +1 -2
- package/scripts/hooks/_shared.js +14 -20
- package/scripts/hooks/session-start.js +8 -6
- package/scripts/hooks/session-summary.js +19 -374
- package/skills/memesh/SKILL.md +19 -14
- package/skills/memesh-review/SKILL.md +7 -6
- package/dist/core/auto-tagger.d.ts +0 -10
- package/dist/core/auto-tagger.d.ts.map +0 -1
- package/dist/core/auto-tagger.js +0 -63
- package/dist/core/auto-tagger.js.map +0 -1
- package/dist/core/conflict-candidates.d.ts +0 -20
- package/dist/core/conflict-candidates.d.ts.map +0 -1
- package/dist/core/conflict-candidates.js +0 -71
- package/dist/core/conflict-candidates.js.map +0 -1
- package/dist/core/conflict-judge.d.ts +0 -58
- package/dist/core/conflict-judge.d.ts.map +0 -1
- package/dist/core/conflict-judge.js +0 -189
- package/dist/core/conflict-judge.js.map +0 -1
- package/dist/core/digest-validator.d.ts +0 -18
- package/dist/core/digest-validator.d.ts.map +0 -1
- package/dist/core/digest-validator.js +0 -85
- package/dist/core/digest-validator.js.map +0 -1
- package/dist/core/embedder.d.ts +0 -20
- package/dist/core/embedder.d.ts.map +0 -1
- package/dist/core/embedder.js +0 -245
- package/dist/core/embedder.js.map +0 -1
- package/dist/core/failure-analyzer.d.ts +0 -19
- package/dist/core/failure-analyzer.d.ts.map +0 -1
- package/dist/core/failure-analyzer.js +0 -83
- package/dist/core/failure-analyzer.js.map +0 -1
- package/dist/core/json-utils.d.ts +0 -3
- package/dist/core/json-utils.d.ts.map +0 -1
- package/dist/core/json-utils.js +0 -46
- package/dist/core/json-utils.js.map +0 -1
- package/dist/core/llm-client.d.ts +0 -22
- package/dist/core/llm-client.d.ts.map +0 -1
- package/dist/core/llm-client.js +0 -203
- package/dist/core/llm-client.js.map +0 -1
- package/dist/core/llm-telemetry.d.ts +0 -47
- package/dist/core/llm-telemetry.d.ts.map +0 -1
- package/dist/core/llm-telemetry.js +0 -117
- package/dist/core/llm-telemetry.js.map +0 -1
- package/dist/core/llm-validator.d.ts +0 -20
- package/dist/core/llm-validator.d.ts.map +0 -1
- package/dist/core/llm-validator.js +0 -231
- package/dist/core/llm-validator.js.map +0 -1
- package/dist/core/ollama-host.d.ts +0 -6
- package/dist/core/ollama-host.d.ts.map +0 -1
- package/dist/core/ollama-host.js +0 -62
- package/dist/core/ollama-host.js.map +0 -1
- package/dist/core/output-language.d.ts +0 -6
- package/dist/core/output-language.d.ts.map +0 -1
- package/dist/core/output-language.js +0 -25
- package/dist/core/output-language.js.map +0 -1
- package/dist/core/prompt-safety.d.ts +0 -4
- package/dist/core/prompt-safety.d.ts.map +0 -1
- package/dist/core/prompt-safety.js +0 -20
- package/dist/core/prompt-safety.js.map +0 -1
- package/dist/storage/vector-index.d.ts +0 -3
- package/dist/storage/vector-index.d.ts.map +0 -1
- package/dist/storage/vector-index.js +0 -13
- package/dist/storage/vector-index.js.map +0 -1
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { Command
|
|
2
|
+
import { Command } from 'commander';
|
|
3
3
|
import { randomBytes } from 'crypto';
|
|
4
4
|
import fs from 'fs';
|
|
5
5
|
import path from 'path';
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
7
|
-
import { openDatabase, closeDatabase, getDatabase, reindexFts,
|
|
8
|
-
import { remember, recallWithConflicts, forget, exportMemories, importMemories, learn,
|
|
9
|
-
import { readConfig,
|
|
10
|
-
import {
|
|
11
|
-
import { getDbPath, getProjectName, homeDir, redactSecrets, redactUserPaths } from '../../core/paths.js';
|
|
7
|
+
import { openDatabase, closeDatabase, getDatabase, reindexFts, } from '../../db.js';
|
|
8
|
+
import { remember, recallWithConflicts, forget, exportMemories, importMemories, learn, setPinned } from '../../core/operations.js';
|
|
9
|
+
import { readConfig, updateConfig } from '../../core/config.js';
|
|
10
|
+
import { getAgentRouterSocketPath, getDbPath, getProjectName, homeDir, redactSecrets, redactUserPaths } from '../../core/paths.js';
|
|
12
11
|
import { agentScopeIdRejection, canonicalAgentScopeId } from '../../core/agent-scope-id.js';
|
|
13
|
-
import { flushPendingEmbeddings, canRefillVectorIndex } from '../../core/embedder.js';
|
|
14
12
|
import { NAMESPACES } from '../../core/types.js';
|
|
15
13
|
import { assembleBriefing } from '../../core/briefing.js';
|
|
16
14
|
import { inspectHosts, allWired } from '../../core/setup.js';
|
|
@@ -37,7 +35,6 @@ async function withDatabase(fn) {
|
|
|
37
35
|
return await fn();
|
|
38
36
|
}
|
|
39
37
|
finally {
|
|
40
|
-
await flushPendingEmbeddings();
|
|
41
38
|
closeDatabase();
|
|
42
39
|
}
|
|
43
40
|
}
|
|
@@ -243,27 +240,12 @@ program
|
|
|
243
240
|
console.log(JSON.stringify(conflicts.length > 0 ? { entities, retrieval, conflicts } : { entities, retrieval }));
|
|
244
241
|
}
|
|
245
242
|
else if (entities.length === 0) {
|
|
246
|
-
|
|
247
|
-
console.log('No results found. Semantic search is configured but could not run for this query (provider or index issue) — this was keyword-only. Run `memesh doctor` to check.');
|
|
248
|
-
}
|
|
249
|
-
else if (query && retrieval.mode === 'fts') {
|
|
250
|
-
console.log('No results found. This was a keyword-only search — no semantic search is configured. See `memesh doctor` for Smart Mode.');
|
|
251
|
-
}
|
|
252
|
-
else {
|
|
253
|
-
console.log('No results found.');
|
|
254
|
-
}
|
|
243
|
+
console.log(query ? 'No results found in the keyword index.' : 'No results found.');
|
|
255
244
|
}
|
|
256
245
|
else {
|
|
257
|
-
const allSemantic = query && entities.every((e) => e.match?.source === 'semantic');
|
|
258
|
-
if (allSemantic) {
|
|
259
|
-
console.log('No keyword matches. Closest memories by meaning — may be unrelated:');
|
|
260
|
-
}
|
|
261
246
|
for (const e of entities) {
|
|
262
247
|
const badge = e.archived ? ' [archived]' : '';
|
|
263
|
-
|
|
264
|
-
? ` (~${Math.round((e.match.relevance ?? 0) * 100)}% semantic)`
|
|
265
|
-
: '';
|
|
266
|
-
console.log(` ${e.name}${badge} (${e.type})${semantic}`);
|
|
248
|
+
console.log(` ${e.name}${badge} (${e.type})`);
|
|
267
249
|
for (const obs of e.observations.slice(0, 3)) {
|
|
268
250
|
let shown = obs;
|
|
269
251
|
if (obs.length > 500) {
|
|
@@ -280,9 +262,6 @@ program
|
|
|
280
262
|
}
|
|
281
263
|
const truncatedNote = retrieval.truncated ? ' (limit reached — more may exist)' : '';
|
|
282
264
|
console.log(`\n${entities.length} result(s)${truncatedNote}`);
|
|
283
|
-
if (retrieval.degraded) {
|
|
284
|
-
console.log('Warning: semantic search unavailable right now — keyword-only results (degraded). Run `memesh doctor` to see why.');
|
|
285
|
-
}
|
|
286
265
|
if (conflicts.length > 0) {
|
|
287
266
|
console.log('\nWarning: Conflicts detected:');
|
|
288
267
|
for (const c of conflicts) {
|
|
@@ -344,54 +323,8 @@ function registerPinCommand(name, description, pinned, onFound) {
|
|
|
344
323
|
});
|
|
345
324
|
});
|
|
346
325
|
}
|
|
347
|
-
registerPinCommand('pin', '
|
|
348
|
-
registerPinCommand('unpin', 'Allow
|
|
349
|
-
program
|
|
350
|
-
.command('consolidate')
|
|
351
|
-
.description('(retired) Use `memesh dream` — see the message this prints')
|
|
352
|
-
.allowUnknownOption()
|
|
353
|
-
.action(() => {
|
|
354
|
-
console.error('`memesh consolidate` has been retired.');
|
|
355
|
-
console.error('');
|
|
356
|
-
console.error('It rewrote a memory with an LLM summary and deleted the originals on the spot —');
|
|
357
|
-
console.error('no proposal, no review, and nothing to restore from if the summary was wrong.');
|
|
358
|
-
console.error('');
|
|
359
|
-
console.error('`memesh dream` is the reviewed version of the same idea:');
|
|
360
|
-
console.error(' memesh dream run propose digests for clusters of noisy memories');
|
|
361
|
-
console.error(' memesh dream list see what it proposed');
|
|
362
|
-
console.error(' memesh dream accept <id> / reject <id>');
|
|
363
|
-
console.error('');
|
|
364
|
-
console.error('Nothing is changed until you accept a proposal, and sources are archived');
|
|
365
|
-
console.error('rather than deleted. It works on episodic memories (commits, session notes)');
|
|
366
|
-
console.error('and never touches lessons, decisions, architecture notes, or pinned entities —');
|
|
367
|
-
console.error('so it is not a like-for-like replacement for compressing one named entity.');
|
|
368
|
-
process.exitCode = 1;
|
|
369
|
-
});
|
|
370
|
-
program
|
|
371
|
-
.command('verify')
|
|
372
|
-
.description('(retired) Removed with the agentic-orchestration experiment — see the message this prints')
|
|
373
|
-
.allowUnknownOption()
|
|
374
|
-
.action(() => {
|
|
375
|
-
console.error('`memesh verify` has been retired, along with the agentic-orchestration experiment.');
|
|
376
|
-
console.error('');
|
|
377
|
-
console.error('It recorded a verification report for background-agent work. The protocol it');
|
|
378
|
-
console.error('served was removed without ever leaving opt-in. Run your own verification');
|
|
379
|
-
console.error('(typecheck / tests / lint) and store conclusions with `memesh remember` if you');
|
|
380
|
-
console.error('want them remembered. Existing verification_record entities are untouched.');
|
|
381
|
-
process.exitCode = 1;
|
|
382
|
-
});
|
|
383
|
-
program
|
|
384
|
-
.command('patterns')
|
|
385
|
-
.description('(retired) Removed with the agentic-orchestration experiment — see the message this prints')
|
|
386
|
-
.allowUnknownOption()
|
|
387
|
-
.action(() => {
|
|
388
|
-
console.error('`memesh patterns` has been retired, along with the agentic-orchestration experiment.');
|
|
389
|
-
console.error('');
|
|
390
|
-
console.error('It displayed the experiment\'s local skill-usage telemetry, which is no longer');
|
|
391
|
-
console.error('written. A leftover ~/.memesh/skill-usage.jsonl is inert and safe to delete.');
|
|
392
|
-
console.error('For work-pattern insights, use the `user_patterns` MCP tool or GET /v1/patterns.');
|
|
393
|
-
process.exitCode = 1;
|
|
394
|
-
});
|
|
326
|
+
registerPinCommand('pin', 'Exclude an entity from digest work packages', true, (e) => `📌 Pinned "${e}" — excluded from digest work packages`);
|
|
327
|
+
registerPinCommand('unpin', 'Allow an entity in digest work packages again', false, (e) => `📍 Unpinned "${e}"`);
|
|
395
328
|
program
|
|
396
329
|
.command('export')
|
|
397
330
|
.description('Export memories as JSON. Defaults to stdout (pipe-friendly); use `-o <file>` to write directly.')
|
|
@@ -824,7 +757,7 @@ agentCmd
|
|
|
824
757
|
const routerTokenFile = path.join(messageDir, 'agent-router.token');
|
|
825
758
|
ensureRouterTokenFile(routerTokenFile);
|
|
826
759
|
const common = {
|
|
827
|
-
router_socket:
|
|
760
|
+
router_socket: getAgentRouterSocketPath(),
|
|
828
761
|
token_file: routerTokenFile,
|
|
829
762
|
project: requireAgentScopeArg(opts.project, 'project', '--project'),
|
|
830
763
|
principal_id: requireAgentScopeArg(opts.principal, 'recipient', '--principal'),
|
|
@@ -856,16 +789,19 @@ agentCmd
|
|
|
856
789
|
? 'ordinary-session-native-queue'
|
|
857
790
|
: host === 'claude' ? 'session-owned-channel' : 'memesh-managed-session',
|
|
858
791
|
session_identity: host === 'codex-session' ? 'codex-thread-id-at-session-start' : 'generated-per-process',
|
|
859
|
-
ordinary_sessions: host === 'codex-session' ? '
|
|
792
|
+
ordinary_sessions: host === 'codex-session' ? 'automatic-thread-scoped-with-workspace-override' : 'presence-only/inbound-unavailable',
|
|
860
793
|
registration_command: registrationCommand,
|
|
861
794
|
launch_command: launchCommand,
|
|
862
|
-
next_command: registrationCommand ?? launchCommand ?? 'Restart Codex in the configured workspace',
|
|
795
|
+
next_command: registrationCommand ?? launchCommand ?? 'Restart Codex in the configured workspace to apply the identity override',
|
|
863
796
|
};
|
|
864
797
|
console.log(opts.json ? JSON.stringify(result) : [
|
|
865
798
|
`Created owner-private ${host} config: ${configPath}`,
|
|
866
799
|
'No active or stopped ordinary session was attached.',
|
|
867
800
|
...(registrationCommand ? [`Register once: ${registrationCommand}`] : []),
|
|
868
|
-
...(launchCommand ? [`Launch: ${launchCommand}`] : [
|
|
801
|
+
...(launchCommand ? [`Launch: ${launchCommand}`] : [
|
|
802
|
+
'This optional override gives that workspace a stable named principal.',
|
|
803
|
+
'Restart Codex in the configured workspace to apply it.',
|
|
804
|
+
]),
|
|
869
805
|
].join('\n'));
|
|
870
806
|
});
|
|
871
807
|
program
|
|
@@ -1110,7 +1046,6 @@ configCmd
|
|
|
1110
1046
|
.description('Show current configuration')
|
|
1111
1047
|
.action(() => {
|
|
1112
1048
|
const config = readConfig();
|
|
1113
|
-
const caps = detectCapabilities(config);
|
|
1114
1049
|
console.log('Configuration (~/.memesh/config.json):');
|
|
1115
1050
|
const rows = buildConfigListing(config);
|
|
1116
1051
|
if (rows.length === 0) {
|
|
@@ -1120,126 +1055,29 @@ configCmd
|
|
|
1120
1055
|
for (const { key, value } of rows)
|
|
1121
1056
|
console.log(` ${key}: ${value}`);
|
|
1122
1057
|
}
|
|
1123
|
-
console.log(`\nSearch level: ${caps.searchLevel} (${caps.searchLevel === 1 ? 'Smart Mode' : 'Core'})`);
|
|
1124
1058
|
});
|
|
1125
|
-
const
|
|
1126
|
-
'llm.api-key': 'llm.apiKey',
|
|
1127
|
-
};
|
|
1128
|
-
const ALLOWED_KEYS = new Set([
|
|
1129
|
-
'llm.provider',
|
|
1130
|
-
'llm.apiKey',
|
|
1131
|
-
'llm.model',
|
|
1132
|
-
'embedder.provider',
|
|
1133
|
-
'autoUpdate',
|
|
1134
|
-
'sessionLimit',
|
|
1135
|
-
'autoCapture',
|
|
1136
|
-
'llmFallbacks',
|
|
1137
|
-
'language',
|
|
1138
|
-
'transcriptMining',
|
|
1139
|
-
]);
|
|
1059
|
+
const ALLOWED_KEYS = new Set(['autoUpdate', 'sessionLimit', 'autoCapture']);
|
|
1140
1060
|
const KEY_VALIDATORS = {
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
if (v.trim().length === 0)
|
|
1144
|
-
return 'must not be blank — use `memesh config unset language` to clear it';
|
|
1145
|
-
if (v.length > MAX_LANGUAGE_LENGTH)
|
|
1146
|
-
return `must be ${MAX_LANGUAGE_LENGTH} characters or fewer (a language name or locale code)`;
|
|
1147
|
-
return languageValueError(v);
|
|
1148
|
-
},
|
|
1149
|
-
'embedder.provider': (v) => ['openai', 'ollama'].includes(v) ? null : `must be one of: openai, ollama`,
|
|
1150
|
-
'autoUpdate': (v) => ['off', 'patch', 'minor', 'major'].includes(v) ? null : `must be one of: off, patch, minor, major`,
|
|
1151
|
-
'autoCapture': (v) => ['true', 'false', '1', '0'].includes(v) ? null : `must be one of: true, false, 1, 0`,
|
|
1152
|
-
'transcriptMining': (v) => ['true', 'false', '1', '0'].includes(v) ? null : `must be one of: true, false, 1, 0`,
|
|
1153
|
-
'llmFallbacks': (v) => {
|
|
1154
|
-
let parsed;
|
|
1155
|
-
try {
|
|
1156
|
-
parsed = JSON.parse(v);
|
|
1157
|
-
}
|
|
1158
|
-
catch {
|
|
1159
|
-
return 'must be a JSON array, e.g. \'[{"provider":"openai","model":"gpt-4o-mini","apiKey":"sk-..."}]\'';
|
|
1160
|
-
}
|
|
1161
|
-
if (!Array.isArray(parsed))
|
|
1162
|
-
return 'must be a JSON ARRAY of provider objects';
|
|
1163
|
-
for (const entry of parsed) {
|
|
1164
|
-
if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) {
|
|
1165
|
-
return 'every entry must be an object like {"provider":"openai"}';
|
|
1166
|
-
}
|
|
1167
|
-
const provider = entry.provider;
|
|
1168
|
-
if (!['anthropic', 'openai', 'ollama'].includes(String(provider))) {
|
|
1169
|
-
return `every entry needs provider = anthropic | openai | ollama (got ${JSON.stringify(provider)})`;
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
return null;
|
|
1173
|
-
},
|
|
1061
|
+
autoUpdate: (v) => ['off', 'patch', 'minor', 'major'].includes(v) ? null : 'must be one of: off, patch, minor, major',
|
|
1062
|
+
autoCapture: (v) => ['true', 'false', '1', '0'].includes(v) ? null : 'must be one of: true, false, 1, 0',
|
|
1174
1063
|
};
|
|
1175
|
-
function setNested(obj, path, value) {
|
|
1176
|
-
let cur = obj;
|
|
1177
|
-
for (let i = 0; i < path.length - 1; i++) {
|
|
1178
|
-
const part = path[i];
|
|
1179
|
-
if (typeof cur[part] !== 'object' || cur[part] === null) {
|
|
1180
|
-
cur[part] = {};
|
|
1181
|
-
}
|
|
1182
|
-
cur = cur[part];
|
|
1183
|
-
}
|
|
1184
|
-
cur[path[path.length - 1]] = value;
|
|
1185
|
-
}
|
|
1186
|
-
function getNested(obj, path) {
|
|
1187
|
-
return path.reduce((cur, part) => (cur && typeof cur === 'object' ? cur[part] : undefined), obj);
|
|
1188
|
-
}
|
|
1189
|
-
const REDACTED = '***';
|
|
1190
|
-
function formatConfigValue(key, raw) {
|
|
1191
|
-
if (key.toLowerCase().includes('key'))
|
|
1192
|
-
return REDACTED;
|
|
1193
|
-
if (key === 'llmFallbacks' && Array.isArray(raw)) {
|
|
1194
|
-
const redacted = raw.map((fb) => {
|
|
1195
|
-
if (fb && typeof fb === 'object') {
|
|
1196
|
-
const { apiKey, ...rest } = fb;
|
|
1197
|
-
return apiKey ? { ...rest, apiKey: REDACTED } : rest;
|
|
1198
|
-
}
|
|
1199
|
-
return fb;
|
|
1200
|
-
});
|
|
1201
|
-
return JSON.stringify(redacted);
|
|
1202
|
-
}
|
|
1203
|
-
if (typeof raw === 'object')
|
|
1204
|
-
return JSON.stringify(raw);
|
|
1205
|
-
return String(raw);
|
|
1206
|
-
}
|
|
1207
1064
|
function buildConfigListing(config) {
|
|
1208
1065
|
const rows = [];
|
|
1209
1066
|
for (const key of Array.from(ALLOWED_KEYS).sort()) {
|
|
1210
|
-
const raw =
|
|
1067
|
+
const raw = config[key];
|
|
1211
1068
|
if (raw === undefined || raw === null)
|
|
1212
1069
|
continue;
|
|
1213
|
-
rows.push({ key, value:
|
|
1070
|
+
rows.push({ key, value: String(raw) });
|
|
1214
1071
|
}
|
|
1215
1072
|
return rows;
|
|
1216
1073
|
}
|
|
1217
|
-
function deleteNested(obj, path) {
|
|
1218
|
-
if (path.length === 0)
|
|
1219
|
-
return false;
|
|
1220
|
-
if (path.length === 1) {
|
|
1221
|
-
if (path[0] in obj) {
|
|
1222
|
-
delete obj[path[0]];
|
|
1223
|
-
return true;
|
|
1224
|
-
}
|
|
1225
|
-
return false;
|
|
1226
|
-
}
|
|
1227
|
-
const head = path[0];
|
|
1228
|
-
if (typeof obj[head] !== 'object' || obj[head] === null)
|
|
1229
|
-
return false;
|
|
1230
|
-
const child = obj[head];
|
|
1231
|
-
const removed = deleteNested(child, path.slice(1));
|
|
1232
|
-
if (removed && Object.keys(child).length === 0)
|
|
1233
|
-
delete obj[head];
|
|
1234
|
-
return removed;
|
|
1235
|
-
}
|
|
1236
1074
|
configCmd
|
|
1237
1075
|
.command('set')
|
|
1238
|
-
.description('Set
|
|
1076
|
+
.description('Set an ordinary config value (autoCapture, sessionLimit, autoUpdate)')
|
|
1239
1077
|
.argument('<key>', 'Config key — see `memesh config list` for valid keys')
|
|
1240
1078
|
.argument('<value>', 'Config value')
|
|
1241
1079
|
.action((key, value) => {
|
|
1242
|
-
const canonical =
|
|
1080
|
+
const canonical = key;
|
|
1243
1081
|
if (!ALLOWED_KEYS.has(canonical)) {
|
|
1244
1082
|
console.error(`Unknown key: ${key}`);
|
|
1245
1083
|
console.error(`Allowed keys: ${Array.from(ALLOWED_KEYS).sort().join(', ')}`);
|
|
@@ -1257,42 +1095,30 @@ configCmd
|
|
|
1257
1095
|
if (canonical === 'sessionLimit') {
|
|
1258
1096
|
coerced = wholeNumber('sessionLimit')(value);
|
|
1259
1097
|
}
|
|
1260
|
-
if (canonical === 'llmFallbacks')
|
|
1261
|
-
coerced = JSON.parse(value);
|
|
1262
1098
|
if (canonical === 'autoCapture') {
|
|
1263
1099
|
coerced = value === 'true' || value === '1';
|
|
1264
1100
|
}
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
}
|
|
1268
|
-
const config = readConfig();
|
|
1269
|
-
setNested(config, canonical.split('.'), coerced);
|
|
1270
|
-
writeConfig(config);
|
|
1271
|
-
const displayValue = canonical.toLowerCase().includes('key') ? maskApiKey(String(value)) : String(value);
|
|
1101
|
+
updateConfig({ [canonical]: coerced });
|
|
1102
|
+
const displayValue = String(value);
|
|
1272
1103
|
console.log(`✅ Set ${canonical} = ${displayValue}`);
|
|
1273
|
-
if (canonical === 'llm.apiKey' && !config.llm?.provider) {
|
|
1274
|
-
console.log('⚠️ No llm.provider is set, so this key is not used yet and LLM features stay off.');
|
|
1275
|
-
console.log(' Set one with: memesh config set llm.provider <anthropic|openai|ollama>');
|
|
1276
|
-
}
|
|
1277
1104
|
});
|
|
1278
1105
|
configCmd
|
|
1279
1106
|
.command('unset')
|
|
1280
|
-
.description('Remove a config value (
|
|
1107
|
+
.description('Remove a config value (ordinary settings only)')
|
|
1281
1108
|
.argument('<key>', 'Config key — see `memesh config list` for valid keys')
|
|
1282
1109
|
.action((key) => {
|
|
1283
|
-
const canonical =
|
|
1110
|
+
const canonical = key;
|
|
1284
1111
|
if (!ALLOWED_KEYS.has(canonical)) {
|
|
1285
1112
|
console.error(`Unknown key: ${key}`);
|
|
1286
1113
|
console.error(`Allowed keys: ${Array.from(ALLOWED_KEYS).sort().join(', ')}`);
|
|
1287
1114
|
process.exit(1);
|
|
1288
1115
|
}
|
|
1289
|
-
const
|
|
1290
|
-
|
|
1116
|
+
const removed = canonical in readConfig();
|
|
1117
|
+
updateConfig({ [canonical]: undefined });
|
|
1291
1118
|
if (!removed) {
|
|
1292
1119
|
console.log(`(no change — ${canonical} was not set)`);
|
|
1293
1120
|
return;
|
|
1294
1121
|
}
|
|
1295
|
-
writeConfig(config);
|
|
1296
1122
|
console.log(`✅ Removed ${canonical}`);
|
|
1297
1123
|
});
|
|
1298
1124
|
program
|
|
@@ -1450,77 +1276,12 @@ program
|
|
|
1450
1276
|
}
|
|
1451
1277
|
process.exit(run.status ?? 1);
|
|
1452
1278
|
});
|
|
1453
|
-
program
|
|
1454
|
-
.command('telemetry')
|
|
1455
|
-
.description('Show LLM call telemetry (per-flow scorecard for the last N days)')
|
|
1456
|
-
.option('--window <days>', 'Look-back window in days (default 30)', wholeNumber('--window'), 30)
|
|
1457
|
-
.option('--prune <days>', 'Delete rows older than N days BEFORE rendering (closes v4.2.0 retention gap)', wholeNumber('--prune', 0))
|
|
1458
|
-
.option('--json', 'Output as JSON')
|
|
1459
|
-
.action(async (opts) => {
|
|
1460
|
-
await withDatabase(async () => {
|
|
1461
|
-
const { summariseTelemetry, pruneTelemetry } = await import('../../core/llm-telemetry.js');
|
|
1462
|
-
let pruneResult = null;
|
|
1463
|
-
if (typeof opts.prune === 'number' && Number.isFinite(opts.prune) && opts.prune >= 0) {
|
|
1464
|
-
pruneResult = pruneTelemetry({ olderThanDays: opts.prune });
|
|
1465
|
-
}
|
|
1466
|
-
const summaries = summariseTelemetry(opts.window);
|
|
1467
|
-
if (opts.json) {
|
|
1468
|
-
console.log(JSON.stringify({ pruned: pruneResult, summaries }, null, 2));
|
|
1469
|
-
return;
|
|
1470
|
-
}
|
|
1471
|
-
if (pruneResult) {
|
|
1472
|
-
console.log(`Pruned ${pruneResult.deletedRows} row${pruneResult.deletedRows === 1 ? '' : 's'} older than ${opts.prune} days.`);
|
|
1473
|
-
console.log('');
|
|
1474
|
-
}
|
|
1475
|
-
if (summaries.length === 0) {
|
|
1476
|
-
console.log(`No LLM telemetry recorded in the last ${opts.window} days.`);
|
|
1477
|
-
console.log(`(Smart-Mode flows write rows automatically — run \`memesh dream run\`, \`memesh dream patterns\`, or trigger a session with errors to populate.)`);
|
|
1478
|
-
return;
|
|
1479
|
-
}
|
|
1480
|
-
console.log(`LLM telemetry — last ${opts.window} days`);
|
|
1481
|
-
console.log('');
|
|
1482
|
-
for (const s of summaries) {
|
|
1483
|
-
const successRate = s.total_attempts > 0 ? Math.round((s.successes / s.total_attempts) * 100) : 0;
|
|
1484
|
-
console.log(`▸ ${s.flow}`);
|
|
1485
|
-
console.log(` calls: ${s.total_calls} (${s.total_attempts} provider attempts)`);
|
|
1486
|
-
console.log(` success rate: ${successRate}% (${s.successes} ok, ${s.failures} failed)`);
|
|
1487
|
-
if (s.fallback_used > 0) {
|
|
1488
|
-
console.log(` fallback used: ${s.fallback_used} time${s.fallback_used === 1 ? '' : 's'} ⚠️ primary failed`);
|
|
1489
|
-
}
|
|
1490
|
-
if (s.median_latency_ms != null) {
|
|
1491
|
-
console.log(` median latency: ${s.median_latency_ms}ms`);
|
|
1492
|
-
}
|
|
1493
|
-
const okFail = (rec) => Object.entries(rec).map(([k, v]) => `${k}=${v.ok}/${v.ok + v.fail}`).join(', ');
|
|
1494
|
-
const providers = okFail(s.by_provider);
|
|
1495
|
-
if (providers)
|
|
1496
|
-
console.log(` by provider: ${providers}`);
|
|
1497
|
-
const models = okFail(s.by_model);
|
|
1498
|
-
if (models)
|
|
1499
|
-
console.log(` by model: ${models}`);
|
|
1500
|
-
const projects = okFail(s.by_project);
|
|
1501
|
-
if (projects)
|
|
1502
|
-
console.log(` by project: ${projects}`);
|
|
1503
|
-
const errors = Object.entries(s.by_error_class);
|
|
1504
|
-
if (errors.length > 0) {
|
|
1505
|
-
const errStr = errors.sort((a, b) => b[1] - a[1]).map(([c, n]) => `${c}=${n}`).join(', ');
|
|
1506
|
-
console.log(` error classes: ${errStr}`);
|
|
1507
|
-
}
|
|
1508
|
-
if (s.sample_errors.length > 0) {
|
|
1509
|
-
console.log(` recent errors:`);
|
|
1510
|
-
for (const e of s.sample_errors) {
|
|
1511
|
-
console.log(` • [${e.error_class ?? 'unknown'}] ${e.message.slice(0, 100)}`);
|
|
1512
|
-
}
|
|
1513
|
-
}
|
|
1514
|
-
console.log('');
|
|
1515
|
-
}
|
|
1516
|
-
});
|
|
1517
|
-
});
|
|
1518
1279
|
const kgCmd = program
|
|
1519
1280
|
.command('kg')
|
|
1520
1281
|
.description('Knowledge graph maintenance');
|
|
1521
1282
|
kgCmd
|
|
1522
1283
|
.command('backfill-relations')
|
|
1523
|
-
.description('Propose / apply
|
|
1284
|
+
.description('Propose / apply deterministic relations to connect orphan entities')
|
|
1524
1285
|
.option('--project <name>', 'Restrict to one project')
|
|
1525
1286
|
.option('--dry-run', 'Show proposals without writing (default off — use to preview)')
|
|
1526
1287
|
.option('--max-per-source <n>', 'Max edges per orphan (default 3)', wholeNumber('--max-per-source'), 3)
|
|
@@ -1682,7 +1443,6 @@ program
|
|
|
1682
1443
|
.description('Verify local install health and show actionable fixes')
|
|
1683
1444
|
.option('--json', 'Output machine-readable diagnostics as JSON')
|
|
1684
1445
|
.option('--probe-http', 'Also probe the local HTTP server health endpoint')
|
|
1685
|
-
.option('--probe', 'Make one small live call to the configured LLM to confirm it actually answers')
|
|
1686
1446
|
.option('--url <url>', 'Base URL for --probe-http', 'http://127.0.0.1:3737')
|
|
1687
1447
|
.option('--fix', 'Apply the whitelisted fixes doctor prescribes (asks per fix; --yes skips asking)')
|
|
1688
1448
|
.option('--yes', 'With --fix: apply without asking')
|
|
@@ -1692,7 +1452,6 @@ program
|
|
|
1692
1452
|
packageRoot,
|
|
1693
1453
|
packageVersion: pkg.version,
|
|
1694
1454
|
probeHttp: opts.probeHttp,
|
|
1695
|
-
probeCapabilities: opts.probe,
|
|
1696
1455
|
httpBaseUrl: opts.url,
|
|
1697
1456
|
});
|
|
1698
1457
|
if (opts.fix) {
|
|
@@ -1750,7 +1509,6 @@ program
|
|
|
1750
1509
|
packageRoot,
|
|
1751
1510
|
packageVersion: pkg.version,
|
|
1752
1511
|
probeHttp: false,
|
|
1753
|
-
probeCapabilities: false,
|
|
1754
1512
|
httpBaseUrl: opts.url,
|
|
1755
1513
|
});
|
|
1756
1514
|
for (const c of result.checks) {
|
|
@@ -1776,207 +1534,7 @@ program
|
|
|
1776
1534
|
process.exitCode = 1;
|
|
1777
1535
|
}
|
|
1778
1536
|
});
|
|
1779
|
-
const dreamCmd = program.command('dream').description('
|
|
1780
|
-
dreamCmd
|
|
1781
|
-
.command('run', { isDefault: true })
|
|
1782
|
-
.description('Run a dream pass — propose digests for clusters of compactable entities')
|
|
1783
|
-
.option('--project <name>', 'Restrict to one project')
|
|
1784
|
-
.option('--dry-run', 'Compute proposals without writing to dream_proposals')
|
|
1785
|
-
.option('--max-llm-calls <n>', 'Hard cap on LLM calls (default 100)', wholeNumber('--max-llm-calls'))
|
|
1786
|
-
.option('--window-days <n>', 'Look-back window in days (default 56 = 8 weeks)', wholeNumber('--window-days'))
|
|
1787
|
-
.option('--validate', 'Run a second LLM pass to cross-check each digest against its sources (doubles LLM calls per proposal; surfaces under flow=digest_validator in `memesh telemetry`)')
|
|
1788
|
-
.option('--from-transcripts', 'EXPERIMENTAL: mine Claude Code session transcripts for this project (decisions/lessons/facts hidden in the conversation) and STAGE them as proposals for `dream accept`, instead of clustering existing entities. Scoped to the current project only — --project does not apply here. With --dry-run, lists sessions and conversation-turn counts without calling an LLM.')
|
|
1789
|
-
.option('--if-due', 'For a scheduler (cron/launchd): only mine if `transcriptMining` is enabled in config AND at least --min-interval-hours have passed since this project was last mined; otherwise exit 0 doing nothing. Lets one frequently-firing entry self-throttle. Only meaningful with --from-transcripts.')
|
|
1790
|
-
.option('--min-interval-hours <n>', 'With --if-due: minimum hours between mined runs for this project (default 24).', wholeNumber('--min-interval-hours'))
|
|
1791
|
-
.action(async (opts) => {
|
|
1792
|
-
if (opts.fromTranscripts) {
|
|
1793
|
-
const windowDays = typeof opts.windowDays === 'number' && !Number.isNaN(opts.windowDays) ? opts.windowDays : 3;
|
|
1794
|
-
if (opts.dryRun) {
|
|
1795
|
-
const { scanTranscripts } = await import('../../core/transcript-source.js');
|
|
1796
|
-
const { countConversationTurns } = await import('../../core/transcript-extractor.js');
|
|
1797
|
-
const sessions = scanTranscripts({ windowDays });
|
|
1798
|
-
console.log(`[dry-run] transcript sessions for this project in the last ${windowDays} day(s): ${sessions.length}`);
|
|
1799
|
-
let totalTurns = 0;
|
|
1800
|
-
for (const s of sessions) {
|
|
1801
|
-
const turns = countConversationTurns(s.path);
|
|
1802
|
-
totalTurns += turns;
|
|
1803
|
-
console.log(` ${s.sessionId} ${turns} conversation turns ${s.lineCount} lines ${(s.sizeBytes / 1024).toFixed(0)}KB ${s.modifiedAt}`);
|
|
1804
|
-
}
|
|
1805
|
-
console.log(` total: ${totalTurns} conversation turns across ${sessions.length} session(s)`);
|
|
1806
|
-
console.log('');
|
|
1807
|
-
console.log('Run without --dry-run to extract high-value memories and stage them as proposals.');
|
|
1808
|
-
return;
|
|
1809
|
-
}
|
|
1810
|
-
if (opts.ifDue) {
|
|
1811
|
-
const { isTranscriptMiningEnabled } = await import('../../core/config.js');
|
|
1812
|
-
if (!isTranscriptMiningEnabled(readConfig())) {
|
|
1813
|
-
console.log('Scheduled transcript mining is off (opt-in). Enable it with `memesh config set transcriptMining true`; this scheduled run will then start mining when due.');
|
|
1814
|
-
return;
|
|
1815
|
-
}
|
|
1816
|
-
const { getProjectName } = await import('../../core/paths.js');
|
|
1817
|
-
const { lastTranscriptMineAt, transcriptMiningDue } = await import('../../core/transcript-source.js');
|
|
1818
|
-
const projectKey = getProjectName(process.cwd());
|
|
1819
|
-
const intervalH = typeof opts.minIntervalHours === 'number' && !Number.isNaN(opts.minIntervalHours) ? opts.minIntervalHours : 24;
|
|
1820
|
-
const last = lastTranscriptMineAt(projectKey);
|
|
1821
|
-
if (!transcriptMiningDue(Date.now(), last, intervalH)) {
|
|
1822
|
-
const agoH = last === null ? null : (Date.now() - last) / 3600_000;
|
|
1823
|
-
console.log(`Not due yet: this project was mined ${agoH === null ? 'recently' : `${agoH.toFixed(1)}h ago`}; interval is ${intervalH}h. Nothing to do.`);
|
|
1824
|
-
return;
|
|
1825
|
-
}
|
|
1826
|
-
}
|
|
1827
|
-
if (opts.project) {
|
|
1828
|
-
console.log('note: --project does not scope --from-transcripts — the transcript source is always the current project. Ignoring --project.');
|
|
1829
|
-
}
|
|
1830
|
-
await withDatabase(async () => {
|
|
1831
|
-
const { runTranscriptSource } = await import('../../core/transcript-extractor.js');
|
|
1832
|
-
const { getDatabase } = await import('../../db.js');
|
|
1833
|
-
const cfg = readConfig();
|
|
1834
|
-
const llm = detectCapabilities().llm;
|
|
1835
|
-
if (!llm) {
|
|
1836
|
-
console.error('No LLM configured. Run `memesh config set llm.provider <anthropic|openai|ollama>` first (or set ANTHROPIC_API_KEY / OPENAI_API_KEY).');
|
|
1837
|
-
console.error('LLM is required for `--from-transcripts` because extracting durable memory from prose is a semantic decision, not a rule.');
|
|
1838
|
-
process.exit(1);
|
|
1839
|
-
}
|
|
1840
|
-
const result = await runTranscriptSource(getDatabase(), llm, {
|
|
1841
|
-
windowDays,
|
|
1842
|
-
maxLlmCalls: opts.maxLlmCalls,
|
|
1843
|
-
fallbacks: cfg.llmFallbacks,
|
|
1844
|
-
});
|
|
1845
|
-
const { getProjectName } = await import('../../core/paths.js');
|
|
1846
|
-
const { recordTranscriptMine } = await import('../../core/transcript-source.js');
|
|
1847
|
-
recordTranscriptMine(getProjectName(process.cwd()), Date.now());
|
|
1848
|
-
console.log(`Transcript mining complete in ${result.durationMs}ms`);
|
|
1849
|
-
console.log(` sessions scanned: ${result.sessionsScanned}`);
|
|
1850
|
-
console.log(` LLM calls: ${result.llmCalls}`);
|
|
1851
|
-
console.log(` candidates extracted: ${result.candidatesExtracted}`);
|
|
1852
|
-
console.log(` proposals created: ${result.proposalsCreated}`);
|
|
1853
|
-
if (result.duplicatesSkipped > 0)
|
|
1854
|
-
console.log(` duplicates skipped: ${result.duplicatesSkipped} (already a pending proposal)`);
|
|
1855
|
-
if (result.nearDuplicatesSkipped > 0) {
|
|
1856
|
-
console.log(` near-duplicates skipped: ${result.nearDuplicatesSkipped} candidate(s) skipped as near-duplicates of existing memories`);
|
|
1857
|
-
for (const d of result.nearDuplicates) {
|
|
1858
|
-
console.log(` - "${d.candidateName}" ~= existing "${d.matchedEntityName}" (distance ${d.distance.toFixed(3)})`);
|
|
1859
|
-
}
|
|
1860
|
-
}
|
|
1861
|
-
if (result.secretsDropped > 0)
|
|
1862
|
-
console.log(` secret-bearing candidates dropped: ${result.secretsDropped}`);
|
|
1863
|
-
if (result.llmFailures > 0)
|
|
1864
|
-
console.log(` LLM call failures: ${result.llmFailures} (sessions not mined — retry when the provider is reachable)`);
|
|
1865
|
-
if (result.parseFailures > 0)
|
|
1866
|
-
console.log(` unparsable replies: ${result.parseFailures} (chunk reply not valid JSON — likely truncated; those candidates were lost, retry)`);
|
|
1867
|
-
if (result.cappedTurns > 0) {
|
|
1868
|
-
console.log(` per-turn cap: ${result.cappedTurns} turn(s) were analysed only in part `
|
|
1869
|
-
+ `(the first 4,000 characters); the rest of each was not sent`);
|
|
1870
|
-
}
|
|
1871
|
-
if (result.truncatedTurns > 0) {
|
|
1872
|
-
console.log(` size-cap truncation: ${result.truncatedTurns} conversation turn(s) beyond the cap were NOT analysed`);
|
|
1873
|
-
for (const t of result.truncatedSessions) {
|
|
1874
|
-
console.log(` - session ${t.sessionId}: ${t.truncatedTurns} tail turn(s) not analysed`);
|
|
1875
|
-
}
|
|
1876
|
-
}
|
|
1877
|
-
if (result.skipped.length > 0) {
|
|
1878
|
-
const reasonCounts = new Map();
|
|
1879
|
-
for (const s of result.skipped)
|
|
1880
|
-
reasonCounts.set(s.reason, (reasonCounts.get(s.reason) ?? 0) + 1);
|
|
1881
|
-
console.log(` skipped: ${result.skipped.length}`);
|
|
1882
|
-
for (const [reason, n] of reasonCounts)
|
|
1883
|
-
console.log(` - ${reason}${n > 1 ? ` (×${n})` : ''}`);
|
|
1884
|
-
}
|
|
1885
|
-
if (result.proposalsCreated > 0) {
|
|
1886
|
-
console.log('');
|
|
1887
|
-
console.log('Review with: memesh dream list');
|
|
1888
|
-
console.log('Accept: memesh dream accept <id>');
|
|
1889
|
-
}
|
|
1890
|
-
});
|
|
1891
|
-
return;
|
|
1892
|
-
}
|
|
1893
|
-
await withDatabase(async () => {
|
|
1894
|
-
const { runDreamer } = await import('../../core/dreamer.js');
|
|
1895
|
-
const { getDatabase } = await import('../../db.js');
|
|
1896
|
-
const cfg = readConfig();
|
|
1897
|
-
const llm = detectCapabilities().llm;
|
|
1898
|
-
if (!llm) {
|
|
1899
|
-
console.error('No LLM configured. Run `memesh config set llm.provider <anthropic|openai|ollama>` first (or set ANTHROPIC_API_KEY / OPENAI_API_KEY).');
|
|
1900
|
-
console.error('LLM is required for `memesh dream` because consolidation is a semantic decision, not a rule.');
|
|
1901
|
-
process.exit(1);
|
|
1902
|
-
}
|
|
1903
|
-
const result = await runDreamer(getDatabase(), llm, {
|
|
1904
|
-
project: opts.project,
|
|
1905
|
-
dryRun: !!opts.dryRun,
|
|
1906
|
-
maxLlmCalls: opts.maxLlmCalls,
|
|
1907
|
-
windowDays: opts.windowDays,
|
|
1908
|
-
fallbacks: cfg.llmFallbacks,
|
|
1909
|
-
validateBeforeStage: !!opts.validate,
|
|
1910
|
-
});
|
|
1911
|
-
console.log(`${opts.dryRun ? '[dry-run] ' : ''}Dream pass complete in ${result.durationMs}ms`);
|
|
1912
|
-
console.log(` clusters scanned: ${result.clustersScanned}`);
|
|
1913
|
-
if (result.clusteringMode) {
|
|
1914
|
-
console.log(` grouped by: ${result.clusteringMode === 'semantic' ? 'meaning (embeddings)' : 'calendar week (no embeddings)'}`);
|
|
1915
|
-
}
|
|
1916
|
-
if (result.clusteringNote)
|
|
1917
|
-
console.log(` note: ${result.clusteringNote}`);
|
|
1918
|
-
console.log(` LLM calls: ${result.llmCalls}`);
|
|
1919
|
-
console.log(` proposals created: ${result.proposalsCreated}`);
|
|
1920
|
-
if (result.skipped.length > 0) {
|
|
1921
|
-
console.log(` skipped: ${result.skipped.length}`);
|
|
1922
|
-
const reasonCounts = new Map();
|
|
1923
|
-
for (const s of result.skipped) {
|
|
1924
|
-
reasonCounts.set(s.reason, (reasonCounts.get(s.reason) ?? 0) + 1);
|
|
1925
|
-
}
|
|
1926
|
-
for (const [reason, n] of reasonCounts) {
|
|
1927
|
-
console.log(` - ${reason}${n > 1 ? ` (×${n})` : ''}`);
|
|
1928
|
-
}
|
|
1929
|
-
}
|
|
1930
|
-
if (!opts.dryRun && result.proposalsCreated > 0) {
|
|
1931
|
-
console.log('');
|
|
1932
|
-
console.log(`Review with: memesh dream list`);
|
|
1933
|
-
console.log(`Accept all: memesh dream accept --all`);
|
|
1934
|
-
}
|
|
1935
|
-
});
|
|
1936
|
-
});
|
|
1937
|
-
dreamCmd
|
|
1938
|
-
.command('patterns')
|
|
1939
|
-
.description('Run pattern detector — surface emerging patterns/conventions/repeated mistakes per project (Phase 3)')
|
|
1940
|
-
.option('--project <name>', 'Restrict to one project (default: all projects)')
|
|
1941
|
-
.option('--dry-run', 'Compute proposals without writing to dream_proposals')
|
|
1942
|
-
.option('--max-llm-calls <n>', 'Hard cap on LLM calls (default 10)', wholeNumber('--max-llm-calls'))
|
|
1943
|
-
.option('--window-days <n>', 'Look-back window in days (default 30)', wholeNumber('--window-days'))
|
|
1944
|
-
.option('--min-signal <n>', 'Minimum signal_score to include in scan (default 0.3)', unitFraction('--min-signal'))
|
|
1945
|
-
.action(async (opts) => {
|
|
1946
|
-
await withDatabase(async () => {
|
|
1947
|
-
const { runPatternDetector } = await import('../../core/dreamer.js');
|
|
1948
|
-
const { getDatabase } = await import('../../db.js');
|
|
1949
|
-
const cfg = readConfig();
|
|
1950
|
-
const llm = detectCapabilities().llm;
|
|
1951
|
-
if (!llm) {
|
|
1952
|
-
console.error('No LLM configured. Pattern detection requires an LLM.');
|
|
1953
|
-
console.error('Run `memesh config set llm.provider <anthropic|openai|ollama>` first (or set ANTHROPIC_API_KEY / OPENAI_API_KEY).');
|
|
1954
|
-
process.exit(1);
|
|
1955
|
-
}
|
|
1956
|
-
const result = await runPatternDetector(getDatabase(), llm, {
|
|
1957
|
-
project: opts.project,
|
|
1958
|
-
dryRun: !!opts.dryRun,
|
|
1959
|
-
maxLlmCalls: opts.maxLlmCalls,
|
|
1960
|
-
windowDays: opts.windowDays,
|
|
1961
|
-
minSignal: opts.minSignal,
|
|
1962
|
-
fallbacks: cfg.llmFallbacks,
|
|
1963
|
-
});
|
|
1964
|
-
console.log(`${opts.dryRun ? '[dry-run] ' : ''}Pattern detector complete in ${result.durationMs}ms`);
|
|
1965
|
-
console.log(` entities scanned: ${result.entitiesScanned}`);
|
|
1966
|
-
console.log(` LLM calls: ${result.llmCalls}`);
|
|
1967
|
-
console.log(` patterns proposed: ${result.proposalsCreated}`);
|
|
1968
|
-
if (result.skipped.length > 0) {
|
|
1969
|
-
console.log(` skipped: ${result.skipped.length}`);
|
|
1970
|
-
for (const s of result.skipped.slice(0, 5)) {
|
|
1971
|
-
console.log(` - ${s.project ?? '?'}: ${s.reason}`);
|
|
1972
|
-
}
|
|
1973
|
-
}
|
|
1974
|
-
if (!opts.dryRun && result.proposalsCreated > 0) {
|
|
1975
|
-
console.log('');
|
|
1976
|
-
console.log(`Review with: memesh dream list`);
|
|
1977
|
-
}
|
|
1978
|
-
});
|
|
1979
|
-
});
|
|
1537
|
+
const dreamCmd = program.command('dream').description('Review agent-submitted proposals: list, show, accept, or reject');
|
|
1980
1538
|
dreamCmd
|
|
1981
1539
|
.command('list')
|
|
1982
1540
|
.description('List dream proposals (pending by default)')
|
|
@@ -2011,49 +1569,6 @@ dreamCmd
|
|
|
2011
1569
|
console.log(`Inspect: memesh dream show <id> | Apply: memesh dream accept <id> | Reject: memesh dream reject <id>`);
|
|
2012
1570
|
});
|
|
2013
1571
|
});
|
|
2014
|
-
dreamCmd
|
|
2015
|
-
.command('conflicts')
|
|
2016
|
-
.description('Judge semantically-close memory pairs for contradiction / supersession / duplication (LLM) and stage relation proposals for review')
|
|
2017
|
-
.option('--max-pairs <n>', 'Judge at most N of the tightest candidate pairs this run (default 20)', wholeNumber('--max-pairs'))
|
|
2018
|
-
.option('--dry-run', 'Show how many candidates are queued without calling an LLM or writing anything')
|
|
2019
|
-
.action(async (opts) => {
|
|
2020
|
-
await withDatabase(async () => {
|
|
2021
|
-
const cfg = readConfig();
|
|
2022
|
-
const llm = detectCapabilities().llm;
|
|
2023
|
-
if (!llm) {
|
|
2024
|
-
console.error('No LLM configured. Run `memesh config set llm.provider <anthropic|openai|ollama>` first (or set ANTHROPIC_API_KEY / OPENAI_API_KEY).');
|
|
2025
|
-
console.error('LLM is required for `memesh dream conflicts` because "do these two entries disagree" is a semantic judgement, not a rule.');
|
|
2026
|
-
process.exit(1);
|
|
2027
|
-
}
|
|
2028
|
-
const { judgeConflicts, CONFLICT_JUDGE_MAX_PAIRS } = await import('../../core/conflict-judge.js');
|
|
2029
|
-
const { getDatabase } = await import('../../db.js');
|
|
2030
|
-
const result = await judgeConflicts(getDatabase(), llm, {
|
|
2031
|
-
maxPairs: typeof opts.maxPairs === 'number' && !Number.isNaN(opts.maxPairs) ? opts.maxPairs : CONFLICT_JUDGE_MAX_PAIRS,
|
|
2032
|
-
dryRun: !!opts.dryRun,
|
|
2033
|
-
fallbacks: cfg.llmFallbacks,
|
|
2034
|
-
});
|
|
2035
|
-
console.log(`${opts.dryRun ? '[dry-run] ' : ''}Conflict pass complete in ${result.durationMs}ms`);
|
|
2036
|
-
console.log(` candidate pairs available: ${result.candidatesAvailable}`);
|
|
2037
|
-
if (opts.dryRun) {
|
|
2038
|
-
console.log(' (dry run — no LLM called, nothing judged or written)');
|
|
2039
|
-
return;
|
|
2040
|
-
}
|
|
2041
|
-
console.log(` LLM calls: ${result.llmCalls}`);
|
|
2042
|
-
console.log(` judged: ${result.judged} (${result.unrelated} unrelated, remembered so they are never re-bought)`);
|
|
2043
|
-
console.log(` staged: ${result.staged} relation proposal(s)`);
|
|
2044
|
-
if (result.llmFailures > 0) {
|
|
2045
|
-
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)`);
|
|
2046
|
-
}
|
|
2047
|
-
if (result.aborted) {
|
|
2048
|
-
console.error(` ABORTED after the work above: ${result.aborted}`);
|
|
2049
|
-
process.exit(1);
|
|
2050
|
-
}
|
|
2051
|
-
if (result.staged > 0) {
|
|
2052
|
-
console.log('');
|
|
2053
|
-
console.log('Review: memesh dream list | Apply: memesh dream accept <id> | Reject: memesh dream reject <id>');
|
|
2054
|
-
}
|
|
2055
|
-
});
|
|
2056
|
-
});
|
|
2057
1572
|
dreamCmd
|
|
2058
1573
|
.command('show <id>')
|
|
2059
1574
|
.description('Show a proposal in full — name, type, ALL observations, tags, source — so you can review the whole thing before accepting')
|
|
@@ -2315,184 +1830,47 @@ program
|
|
|
2315
1830
|
const { command, args } = feedbackBrowserOpenCommand(process.platform, url);
|
|
2316
1831
|
try {
|
|
2317
1832
|
const child = spawn(command, args, { stdio: 'ignore', detached: true });
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
1833
|
+
const opened = await new Promise((resolve) => {
|
|
1834
|
+
child.once('spawn', () => resolve(true));
|
|
1835
|
+
child.once('error', () => resolve(false));
|
|
1836
|
+
});
|
|
1837
|
+
if (opened) {
|
|
1838
|
+
child.unref();
|
|
1839
|
+
console.log(`Opened browser to file ${fbType} issue.`);
|
|
1840
|
+
console.log('Edit the title + body before submitting.');
|
|
1841
|
+
}
|
|
1842
|
+
else {
|
|
1843
|
+
console.log('Could not open browser. URL:');
|
|
1844
|
+
console.log(url);
|
|
1845
|
+
}
|
|
2321
1846
|
}
|
|
2322
1847
|
catch {
|
|
2323
1848
|
console.log('Could not open browser. URL:');
|
|
2324
1849
|
console.log(url);
|
|
2325
1850
|
}
|
|
2326
1851
|
});
|
|
2327
|
-
program
|
|
2328
|
-
.
|
|
2329
|
-
.
|
|
2330
|
-
.option('--namespace <namespace>', 'Reindex only entities in this namespace')
|
|
2331
|
-
.option('--fts', 'Rebuild the full-text keyword index instead of the vector index')
|
|
2332
|
-
.option('--discard-generation', 'Throw away a half-built vector index left by an interrupted rebuild, without rebuilding')
|
|
1852
|
+
program.command('reindex')
|
|
1853
|
+
.description('Rebuild the full-text keyword index')
|
|
1854
|
+
.requiredOption('--fts', 'Rebuild the full-text keyword index')
|
|
2333
1855
|
.option('--json', 'Output as JSON')
|
|
2334
|
-
.addOption(new Option('--vectors').hideHelp())
|
|
2335
1856
|
.action(async (opts) => {
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
reason: 'the --vectors flag is retired',
|
|
2341
|
-
fix: 'run plain memesh reindex; to change provider first: memesh config set embedder.provider ollama|openai',
|
|
2342
|
-
indexTouched: false,
|
|
2343
|
-
}));
|
|
2344
|
-
process.exit(1);
|
|
2345
|
-
}
|
|
2346
|
-
console.error('`memesh reindex --vectors` has been retired.');
|
|
2347
|
-
console.error('');
|
|
2348
|
-
console.error('It existed to consent to dropping every stored embedding before a refill.');
|
|
2349
|
-
console.error('A rebuild now happens beside the live index and replaces it only when');
|
|
2350
|
-
console.error('complete, so there is nothing left to consent to.');
|
|
2351
|
-
console.error('');
|
|
2352
|
-
console.error('Run plain `memesh reindex`. To change embedding provider first:');
|
|
2353
|
-
console.error(' memesh config set embedder.provider ollama (or openai)');
|
|
2354
|
-
process.exit(1);
|
|
2355
|
-
}
|
|
2356
|
-
requireOneOf(opts.namespace, NAMESPACES, '--namespace');
|
|
2357
|
-
if (opts.discardGeneration) {
|
|
2358
|
-
await withDatabase(async () => {
|
|
2359
|
-
const read = readVectorGeneration();
|
|
2360
|
-
const staged = generationRowIds().size;
|
|
2361
|
-
if (read.state === 'none' && staged === 0) {
|
|
2362
|
-
if (opts.json)
|
|
2363
|
-
console.log(JSON.stringify({ discarded: false, staged: 0 }));
|
|
2364
|
-
else
|
|
2365
|
-
console.log('Nothing to discard: there is no half-built vector index.');
|
|
2366
|
-
return;
|
|
2367
|
-
}
|
|
2368
|
-
const describe = read.state === 'open'
|
|
2369
|
-
? `${read.info.dimension}-dim, provider ${read.info.provider}, started ${read.info.startedAt}`
|
|
2370
|
-
: read.state === 'unreadable'
|
|
2371
|
-
? `marker unreadable (${read.detail})`
|
|
2372
|
-
: 'no marker';
|
|
2373
|
-
discardVectorGeneration();
|
|
2374
|
-
if (opts.json) {
|
|
2375
|
-
console.log(JSON.stringify({ discarded: true, staged, generation: read, liveIndexTouched: false }));
|
|
2376
|
-
}
|
|
2377
|
-
else {
|
|
2378
|
-
console.log(`Discarded a half-built vector index: ${staged} staged vectors (${describe}).\n` +
|
|
2379
|
-
' Your live index was not touched. Run `memesh reindex` to build a new one.');
|
|
2380
|
-
}
|
|
2381
|
-
});
|
|
2382
|
-
return;
|
|
2383
|
-
}
|
|
2384
|
-
try {
|
|
2385
|
-
if (opts.fts) {
|
|
2386
|
-
await withDatabase(async () => {
|
|
2387
|
-
const { entities } = reindexFts();
|
|
2388
|
-
if (opts.json) {
|
|
2389
|
-
console.log(JSON.stringify({ rebuilt: 'fts', entities }));
|
|
2390
|
-
}
|
|
2391
|
-
else {
|
|
2392
|
-
console.log(`✅ Keyword index rebuilt from ${entities} active memories.`);
|
|
2393
|
-
}
|
|
2394
|
-
});
|
|
2395
|
-
return;
|
|
2396
|
-
}
|
|
2397
|
-
if (!opts.namespace && !(await canRefillVectorIndex())) {
|
|
2398
|
-
const embedder = detectCapabilities().embeddings;
|
|
2399
|
-
const written = readConfig().embedder?.provider;
|
|
2400
|
-
const known = embedder === 'openai' || embedder === 'ollama';
|
|
2401
|
-
const invalid = written !== undefined && !known;
|
|
2402
|
-
const unconfigured = written === undefined && !known;
|
|
2403
|
-
const reason = invalid
|
|
2404
|
-
? `embedder.provider is set to '${written}', which is not a provider MeMesh knows`
|
|
2405
|
-
: unconfigured
|
|
2406
|
-
? 'no embedding provider is configured, so there is nothing to build vectors with'
|
|
2407
|
-
: 'could not produce a test embedding at the configured vector width';
|
|
2408
|
-
const fix = invalid || unconfigured
|
|
2409
|
-
? (invalid ? 'Set it to one MeMesh knows: ' : 'Pick one first: ') +
|
|
2410
|
-
'`memesh config set embedder.provider ollama` (local, needs `ollama serve`) ' +
|
|
2411
|
-
'or `memesh config set embedder.provider openai` (needs OPENAI_API_KEY). Until then, recall ' +
|
|
2412
|
-
'runs on keyword search, which needs no rebuild.'
|
|
2413
|
-
: 'Check that Ollama is running (or that your OpenAI API key is valid), then run this again. ' +
|
|
2414
|
-
'`memesh doctor` reports which provider is configured.';
|
|
2415
|
-
if (opts.json) {
|
|
2416
|
-
console.log(JSON.stringify({ refused: true, reason, fix, indexTouched: false }));
|
|
2417
|
-
}
|
|
2418
|
-
else {
|
|
2419
|
-
console.error(`❌ Nothing was rebuilt: ${reason}.\n` +
|
|
2420
|
-
' Your existing index is untouched and still answering queries.\n' +
|
|
2421
|
-
` ${fix}`);
|
|
2422
|
-
}
|
|
2423
|
-
process.exit(1);
|
|
2424
|
-
}
|
|
2425
|
-
await withDatabase(async () => {
|
|
2426
|
-
const result = await reindex({ namespace: opts.namespace });
|
|
2427
|
-
const incomplete = result.missingVectors > 0
|
|
2428
|
-
|| result.failed > 0
|
|
2429
|
-
|| result.generationSwapped === false
|
|
2430
|
-
|| result.abortedAfter !== null;
|
|
2431
|
-
if (opts.json) {
|
|
2432
|
-
console.log(JSON.stringify(result));
|
|
2433
|
-
}
|
|
2434
|
-
else {
|
|
2435
|
-
console.log(incomplete ? `⚠️ Reindex incomplete:` : `✅ Reindex complete:`);
|
|
2436
|
-
console.log(` Processed: ${result.processed}`);
|
|
2437
|
-
console.log(` Embedded: ${result.embedded}`);
|
|
2438
|
-
console.log(` Skipped: ${result.skipped}`);
|
|
2439
|
-
if (incomplete) {
|
|
2440
|
-
if (result.abortedAfter !== null) {
|
|
2441
|
-
console.log(` Stopped early after ${result.abortedAfter} entities: the provider failed ` +
|
|
2442
|
-
`repeatedly. Everything embedded so far is kept — run this again to continue.`);
|
|
2443
|
-
}
|
|
2444
|
-
if (result.generationSwapped === false) {
|
|
2445
|
-
console.log(` The new index was NOT switched in, so your existing index is untouched ` +
|
|
2446
|
-
`and still answering queries.`);
|
|
2447
|
-
}
|
|
2448
|
-
if (result.missingVectors > 0) {
|
|
2449
|
-
console.log(` Still without a vector: ${result.missingVectors}`);
|
|
2450
|
-
}
|
|
2451
|
-
if (result.failed > 0 && result.missingVectors === 0) {
|
|
2452
|
-
console.log(` Could not be regenerated: ${result.failed} ` +
|
|
2453
|
-
`(these still hold their previous embedding)`);
|
|
2454
|
-
}
|
|
2455
|
-
for (const [outcome, count] of Object.entries(result.outcomes)) {
|
|
2456
|
-
if (outcome !== 'stored' && count > 0)
|
|
2457
|
-
console.log(` ${outcome}: ${count}`);
|
|
2458
|
-
}
|
|
2459
|
-
}
|
|
2460
|
-
else if (!result.pendingReindexCleared) {
|
|
2461
|
-
console.log(` Note: ${result.missingVectorsDatabaseWide} memories in other namespaces still ` +
|
|
2462
|
-
`have no vector, so the reindex-needed flag stays set.`);
|
|
2463
|
-
}
|
|
2464
|
-
}
|
|
2465
|
-
if (incomplete)
|
|
2466
|
-
process.exitCode = 1;
|
|
2467
|
-
});
|
|
2468
|
-
}
|
|
2469
|
-
catch (err) {
|
|
2470
|
-
if (err instanceof Error) {
|
|
2471
|
-
if (opts.json)
|
|
2472
|
-
console.log(JSON.stringify({ refused: true, reason: err.message, indexTouched: false }));
|
|
2473
|
-
else
|
|
2474
|
-
console.error(`❌ Reindex failed: ${err.message}`);
|
|
2475
|
-
process.exit(1);
|
|
2476
|
-
}
|
|
2477
|
-
throw err;
|
|
2478
|
-
}
|
|
1857
|
+
await withDatabase(() => {
|
|
1858
|
+
const result = reindexFts();
|
|
1859
|
+
console.log(opts.json ? JSON.stringify(result) : `Keyword index rebuilt (${result.entities} entities).`);
|
|
1860
|
+
});
|
|
2479
1861
|
});
|
|
2480
1862
|
program
|
|
2481
1863
|
.command('status')
|
|
2482
|
-
.description('Show MeMesh status
|
|
1864
|
+
.description('Show MeMesh status')
|
|
2483
1865
|
.option('--cached', 'Use cached update info only (skip fresh npm lookup)')
|
|
2484
1866
|
.action(async (opts) => {
|
|
2485
1867
|
await withDatabase(() => { });
|
|
2486
|
-
const caps = detectCapabilities();
|
|
2487
1868
|
const { getCurrentInstallChannel, getInstallChannelSupport } = await import('../../core/install-channel.js');
|
|
2488
1869
|
const install = getCurrentInstallChannel({ packageRoot });
|
|
2489
1870
|
const installSupport = getInstallChannelSupport(install, packageRoot);
|
|
2490
1871
|
const { getUpdateCheck, formatUpdateCheckStatus } = await import('../../core/version-check.js');
|
|
2491
1872
|
const update = await getUpdateCheck(pkg.version, { preferFresh: !opts.cached });
|
|
2492
1873
|
console.log(`MeMesh v${pkg.version}`);
|
|
2493
|
-
console.log(`Search level: ${caps.searchLevel} (${caps.searchLevel === 1 ? 'Smart Mode' : 'Core'})`);
|
|
2494
|
-
console.log(`Embeddings: ${caps.embeddings}`);
|
|
2495
|
-
console.log(`LLM: ${caps.llm ? `${caps.llm.provider} (${caps.llm.model ?? 'default'})` : 'not configured'}`);
|
|
2496
1874
|
console.log(`Install method: ${installSupport.label}`);
|
|
2497
1875
|
for (const line of formatUpdateCheckStatus(update)) {
|
|
2498
1876
|
console.log(`\n${line}`);
|