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