eklavya 1.22.0 → 1.24.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/dist/assets/dashboard.html +3 -3
- package/dist/claude-mem.js +70 -12
- package/dist/claude-mem.js.map +1 -1
- package/dist/cli.js +105 -21
- package/dist/cli.js.map +1 -1
- package/dist/config.js +3 -5
- package/dist/config.js.map +1 -1
- package/dist/hooks/memory-lib.js +36 -6
- package/dist/hooks/memory-lib.js.map +1 -1
- package/dist/install.js +98 -37
- package/dist/install.js.map +1 -1
- package/dist/memory/import-worker.js +51 -0
- package/dist/memory/import-worker.js.map +1 -0
- package/dist/memory/import.js +164 -4
- package/dist/memory/import.js.map +1 -1
- package/dist/memory/provider.js +99 -57
- package/dist/memory/provider.js.map +1 -1
- package/dist/memory/store.js +18 -8
- package/dist/memory/store.js.map +1 -1
- package/dist/memory/worker.js +8 -8
- package/dist/memory/worker.js.map +1 -1
- package/dist/onboard.js +166 -42
- package/dist/onboard.js.map +1 -1
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/theme.js +27 -0
- package/dist/theme.js.map +1 -1
- package/dist/tools/config_tools.js +3 -3
- package/dist/tools/config_tools.js.map +1 -1
- package/dist/user-skill/eklavya/SKILL.md +1 -1
- package/package.json +1 -2
|
@@ -1803,11 +1803,11 @@ function viewHealth() {
|
|
|
1803
1803
|
<div><b>Embeddings</b><span>${H.providers.embeddings ? esc(H.providers.embeddings.kind + ' · ' + H.providers.embeddings.model) : 'local embedder'}</span></div>
|
|
1804
1804
|
<div><b>Retrieval</b><span>${esc(H.providers.retrieval_mode)}</span></div>
|
|
1805
1805
|
</div>
|
|
1806
|
-
<p class="slug" style="margin:var(--space-4) 0 0">
|
|
1807
|
-
|
|
1806
|
+
<p class="slug" style="margin:var(--space-4) 0 0">A configured model runs through Claude Code on
|
|
1807
|
+
your subscription.</p>`)}
|
|
1808
1808
|
</div>`,
|
|
1809
1809
|
|
|
1810
|
-
card('Stalled jobs', 'Grouped by why they stopped. Transient retries on its own; auth and
|
|
1810
|
+
card('Stalled jobs', 'Grouped by why they stopped. Transient retries on its own; auth, quota and missing need you.',
|
|
1811
1811
|
H.stalled.length
|
|
1812
1812
|
? table([{t:'Status'},{t:'Why'},{t:'Jobs',n:1},{t:'Attempts',n:1},{t:'Last',n:1}],
|
|
1813
1813
|
H.stalled.map((j) => `<tr>
|
package/dist/claude-mem.js
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
import fs from 'node:fs';
|
|
20
20
|
import os from 'node:os';
|
|
21
21
|
import path from 'node:path';
|
|
22
|
-
import {
|
|
22
|
+
import { spawn } from 'node:child_process';
|
|
23
23
|
import Database from 'better-sqlite3';
|
|
24
24
|
import { findRepoConfig } from './config.js';
|
|
25
25
|
import { projectKey } from './store.js';
|
|
@@ -62,15 +62,20 @@ export function activeClaudeMemPluginIds(claudeHome) {
|
|
|
62
62
|
* it off in settings.json when `claude` is not on PATH. Off is enough to stop
|
|
63
63
|
* the double recording; the fallback just cannot delete the files.
|
|
64
64
|
*/
|
|
65
|
-
export function removeClaudeMemPlugin(claudeHome, ids) {
|
|
65
|
+
export async function removeClaudeMemPlugin(claudeHome, ids) {
|
|
66
66
|
let viaCli = true;
|
|
67
67
|
for (const id of ids) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
// Async so install's spinner turns while `claude` starts up, which is seconds.
|
|
69
|
+
const status = await new Promise((resolve) => {
|
|
70
|
+
const child = spawn('claude', ['plugin', 'uninstall', id], {
|
|
71
|
+
stdio: 'ignore',
|
|
72
|
+
shell: process.platform === 'win32',
|
|
73
|
+
env: { ...process.env, CLAUDE_CONFIG_DIR: claudeHome },
|
|
74
|
+
});
|
|
75
|
+
child.once('error', () => resolve(null));
|
|
76
|
+
child.once('close', resolve);
|
|
72
77
|
});
|
|
73
|
-
if (
|
|
78
|
+
if (status !== 0)
|
|
74
79
|
viaCli = false;
|
|
75
80
|
}
|
|
76
81
|
if (viaCli)
|
|
@@ -152,13 +157,16 @@ function decodeTranscriptDir(name) {
|
|
|
152
157
|
* placed. A project is placed by majority vote over its sessions' transcripts —
|
|
153
158
|
* each session counts for the checkout its `cwd` is in, or, where that path is
|
|
154
159
|
* gone, the checkout its transcript directory now points at. Failing that, by a
|
|
155
|
-
* unique live checkout whose folder carries the same name.
|
|
156
|
-
*
|
|
157
|
-
*
|
|
160
|
+
* unique live checkout whose folder carries the same name. Failing that, a
|
|
161
|
+
* checkout that moved: from the nearest folder of the recorded path that still
|
|
162
|
+
* exists, one carrying the project's name up to two levels down. More than
|
|
163
|
+
* one is not a guess this makes -- the candidates go into `unsure` for the
|
|
164
|
+
* caller to show. Anything else is left out, and the importer keeps it as-is —
|
|
165
|
+
* searchable with `--all-projects`, mappable later with `--map`.
|
|
158
166
|
*
|
|
159
167
|
* ponytail: reads at most 20 transcripts per project, which is plenty to vote.
|
|
160
168
|
*/
|
|
161
|
-
export function guessProjectMap(sourceDb, claudeHome) {
|
|
169
|
+
export function guessProjectMap(sourceDb, claudeHome, unsure = {}) {
|
|
162
170
|
const projectsDir = path.join(claudeHome, 'projects');
|
|
163
171
|
let dirs = [];
|
|
164
172
|
try {
|
|
@@ -210,11 +218,15 @@ export function guessProjectMap(sourceDb, claudeHome) {
|
|
|
210
218
|
const map = {};
|
|
211
219
|
for (const { project, ids } of rows) {
|
|
212
220
|
const votes = new Map();
|
|
221
|
+
let lost = null;
|
|
213
222
|
for (const id of (ids ?? '').split(',').slice(0, 20)) {
|
|
214
223
|
const t = byId.get(id);
|
|
215
|
-
const
|
|
224
|
+
const cwd = t ? transcriptCwd(t.file) : null;
|
|
225
|
+
const root = t ? checkoutOf(cwd) ?? t.dirRoot : null;
|
|
216
226
|
if (root)
|
|
217
227
|
votes.set(root, (votes.get(root) ?? 0) + 1);
|
|
228
|
+
else if (cwd && !fs.existsSync(cwd))
|
|
229
|
+
lost ??= cwd;
|
|
218
230
|
}
|
|
219
231
|
const best = [...votes].sort((a, b) => b[1] - a[1])[0];
|
|
220
232
|
const byName = liveByName.get(path.basename(project));
|
|
@@ -222,9 +234,55 @@ export function guessProjectMap(sourceDb, claudeHome) {
|
|
|
222
234
|
map[project] = best[0];
|
|
223
235
|
else if (byName?.size === 1)
|
|
224
236
|
map[project] = [...byName][0];
|
|
237
|
+
else if (lost) {
|
|
238
|
+
const moved = relocate(lost, path.basename(project));
|
|
239
|
+
if (moved.length === 1)
|
|
240
|
+
map[project] = moved[0];
|
|
241
|
+
else if (moved.length > 1)
|
|
242
|
+
unsure[project] = moved;
|
|
243
|
+
}
|
|
225
244
|
}
|
|
226
245
|
return map;
|
|
227
246
|
}
|
|
247
|
+
/**
|
|
248
|
+
* Checkouts named `name` near where `lost` used to be: from its nearest
|
|
249
|
+
* surviving ancestor, two levels down. `~/Workspace/Personal/PiDom` moved to
|
|
250
|
+
* `~/Workspace/local/PiDom` is found; a folder of that name inside some other
|
|
251
|
+
* repository is not, because it must be a checkout root of its own.
|
|
252
|
+
*/
|
|
253
|
+
function relocate(lost, name) {
|
|
254
|
+
let dir = path.dirname(lost);
|
|
255
|
+
while (!fs.existsSync(dir)) {
|
|
256
|
+
const up = path.dirname(dir);
|
|
257
|
+
if (up === dir)
|
|
258
|
+
return [];
|
|
259
|
+
dir = up;
|
|
260
|
+
}
|
|
261
|
+
const found = new Set();
|
|
262
|
+
const look = (d, depth) => {
|
|
263
|
+
let entries = [];
|
|
264
|
+
try {
|
|
265
|
+
entries = fs.readdirSync(d, { withFileTypes: true });
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
for (const e of entries) {
|
|
271
|
+
if (!e.isDirectory() || e.name.startsWith('.') || e.name === 'node_modules')
|
|
272
|
+
continue;
|
|
273
|
+
const p = path.join(d, e.name);
|
|
274
|
+
if (e.name === name) {
|
|
275
|
+
const root = checkoutOf(p);
|
|
276
|
+
if (root && path.basename(root) === name)
|
|
277
|
+
found.add(root);
|
|
278
|
+
}
|
|
279
|
+
else if (depth > 1)
|
|
280
|
+
look(p, depth - 1);
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
look(dir, 2);
|
|
284
|
+
return [...found].sort();
|
|
285
|
+
}
|
|
228
286
|
/** `~/.claude-mem` → `~/.claude-mem.retired` (or `.retired-2`, …). Never a delete. */
|
|
229
287
|
export function retireClaudeMemDir() {
|
|
230
288
|
const from = claudeMemDir();
|
package/dist/claude-mem.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-mem.js","sourceRoot":"","sources":["../src/claude-mem.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"claude-mem.js","sourceRoot":"","sources":["../src/claude-mem.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -19,8 +19,10 @@ import { isSessionOff } from './session.js';
|
|
|
19
19
|
import { START_LEVEL } from './srs.js';
|
|
20
20
|
import Database from 'better-sqlite3';
|
|
21
21
|
import { startDashboard, openInBrowser } from './dashboard.js';
|
|
22
|
-
import { install, uninstall, health } from './install.js';
|
|
23
|
-
import {
|
|
22
|
+
import { install, uninstall, health, claudeHome } from './install.js';
|
|
23
|
+
import { guessProjectMap } from './claude-mem.js';
|
|
24
|
+
import { check, dim, heading, spin, verdict } from './theme.js';
|
|
25
|
+
import { importOffThread } from './memory/import-worker.js';
|
|
24
26
|
import { identityFor } from './memory/identity.js';
|
|
25
27
|
import { countEntries, entryById, entryEvents, entryTags, pendingEventCount, receiptTotals, resumePaused, timeline, } from './memory/store.js';
|
|
26
28
|
import { search } from './memory/search.js';
|
|
@@ -29,7 +31,7 @@ import { processPending, pruneEvidence, queueDepth, summarizerFor } from './memo
|
|
|
29
31
|
import { replayProject, transcriptDirFor, transcriptsFor } from './memory/replay.js';
|
|
30
32
|
import { droppedCount } from './memory/spool.js';
|
|
31
33
|
import { savingsFrom, savingsLine } from './memory/tokens.js';
|
|
32
|
-
import {
|
|
34
|
+
import { inventory, exportPayload, restoreExport, EXPORT_SCHEMA_VERSION, ImportError, IMPORTED_TABLES, verifyImport, } from './memory/import.js';
|
|
33
35
|
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
34
36
|
const USAGE = `eklavya — local learning state for agent-assisted development
|
|
35
37
|
|
|
@@ -59,8 +61,9 @@ Memory:
|
|
|
59
61
|
eklavya memory show <id> One entry, with the evidence it was built from
|
|
60
62
|
eklavya memory replay [--limit <n>] Backfill from this checkout's Claude Code transcripts
|
|
61
63
|
Covers sessions from before the install, and any a hook missed
|
|
62
|
-
eklavya memory process [--max <n>]
|
|
63
|
-
|
|
64
|
+
eklavya memory process [--max <n>] [--no-resume]
|
|
65
|
+
Drain the observation queue now
|
|
66
|
+
Resumes jobs paused on a login or usage limit — run it once that is sorted
|
|
64
67
|
eklavya memory prune Delete raw evidence past memory.retention_days
|
|
65
68
|
eklavya memory import <source.db> Import a Claude Mem database [--dry-run] [--resume]
|
|
66
69
|
--dry-run reads the source and reports; it writes nothing
|
|
@@ -87,8 +90,8 @@ Config namespaces (nested; edit ~/.eklavya/config.json or this project's file di
|
|
|
87
90
|
memory.{enabled, capture: full|minimal|off, batch_max_events, retention_days}
|
|
88
91
|
privacy.{exclude_paths, exclude_tools, redact_patterns}
|
|
89
92
|
retrieval.{mode: keyword|semantic|hybrid, max_items, max_tokens, cross_project}
|
|
90
|
-
providers.{observer, embeddings} — each null or {kind, model
|
|
91
|
-
|
|
93
|
+
providers.{observer, embeddings} — each null or {kind, model}; the model runs
|
|
94
|
+
through Claude Code on your subscription
|
|
92
95
|
notifications.{enabled, sinks} — sinks are {kind: webhook|command|file, target,
|
|
93
96
|
args, events}; off by default, and a send cannot be recalled
|
|
94
97
|
sync.{enabled, target, device_id} — target is a folder both devices can see
|
|
@@ -414,8 +417,14 @@ function doctor() {
|
|
|
414
417
|
.join(', '), '') || 'unclassified';
|
|
415
418
|
if (queue.paused > 0) {
|
|
416
419
|
memoryOk = false;
|
|
417
|
-
|
|
418
|
-
add('fail', 'memory',
|
|
420
|
+
const paused = classes('paused');
|
|
421
|
+
add('fail', 'memory', `FAILED — ${queue.paused} job(s) paused (${paused}); nothing is being summarised`);
|
|
422
|
+
const fixes = [
|
|
423
|
+
paused.includes('missing') && 'put claude on the PATH Claude Code starts with (an app or IDE launch often has a shorter one)',
|
|
424
|
+
paused.includes('auth') && 'log in to Claude Code (claude, then /login)',
|
|
425
|
+
paused.includes('quota') && 'wait out the usage limit',
|
|
426
|
+
].filter(Boolean);
|
|
427
|
+
add('fail', 'memory', dim(`${fixes.join(', or ') || 'fix what paused it'}, then: eklavya memory process`));
|
|
419
428
|
}
|
|
420
429
|
if (queue.failed > 0) {
|
|
421
430
|
// Not a failure: a permanently failed job is a batch that will never
|
|
@@ -646,6 +655,31 @@ function numberFlag(argv, name, fallback) {
|
|
|
646
655
|
function currentProject() {
|
|
647
656
|
return identityFor({ cwd: process.cwd(), sessionId: 'cli' }).project;
|
|
648
657
|
}
|
|
658
|
+
/**
|
|
659
|
+
* How a migration is checked: what arrived here, and what arrived under a
|
|
660
|
+
* Claude Mem project name no checkout matches -- history that only surfaces
|
|
661
|
+
* under --all-projects until it is placed.
|
|
662
|
+
*/
|
|
663
|
+
function importedLines(db, project) {
|
|
664
|
+
const rows = db
|
|
665
|
+
.prepare(`SELECT project, COUNT(*) AS n FROM memory_entries
|
|
666
|
+
WHERE import_source IS NOT NULL AND deleted_at IS NULL GROUP BY project`)
|
|
667
|
+
.all();
|
|
668
|
+
if (!rows.length)
|
|
669
|
+
return [];
|
|
670
|
+
const here = rows.find((r) => r.project === project)?.n ?? 0;
|
|
671
|
+
const bare = rows.filter((r) => !path.isAbsolute(r.project));
|
|
672
|
+
const unplaced = bare.reduce((sum, r) => sum + r.n, 0);
|
|
673
|
+
return [
|
|
674
|
+
`imported: ${here} here from Claude Mem`,
|
|
675
|
+
...(unplaced
|
|
676
|
+
? [
|
|
677
|
+
`unplaced: ${unplaced} under ${bare.length} name(s) no checkout matches — ${bare.map((r) => r.project).slice(0, 5).join(', ')}${bare.length > 5 ? ', …' : ''}`,
|
|
678
|
+
' place them: eklavya memory import ~/.claude-mem.retired/claude-mem.db [--map <name>=<checkout>]',
|
|
679
|
+
]
|
|
680
|
+
: []),
|
|
681
|
+
];
|
|
682
|
+
}
|
|
649
683
|
function memoryStatus() {
|
|
650
684
|
const db = openDb();
|
|
651
685
|
try {
|
|
@@ -662,6 +696,7 @@ function memoryStatus() {
|
|
|
662
696
|
`project: ${project}`,
|
|
663
697
|
`capture: ${config.memory.enabled ? config.memory.capture : 'off (memory.enabled is false)'}`,
|
|
664
698
|
`entries: ${countEntries(db, project)} here, ${countEntries(db)} in total`,
|
|
699
|
+
...importedLines(db, project),
|
|
665
700
|
`pending: ${pendingEventCount(db, project)} evidence events here, ${pendingEventCount(db)} in total`,
|
|
666
701
|
`queue: ${queue.pending} pending · ${queue.paused} paused · ${queue.failed} failed`,
|
|
667
702
|
`oldest job: ${queue.oldest ?? '—'}`,
|
|
@@ -669,7 +704,7 @@ function memoryStatus() {
|
|
|
669
704
|
// questions: one is "will anything leave this machine", the other is
|
|
670
705
|
// "what is actually writing the observations right now".
|
|
671
706
|
`provider: ${config.providers.observer
|
|
672
|
-
? `${config.providers.observer.kind}:${config.providers.observer.model} (
|
|
707
|
+
? `${config.providers.observer.kind}:${config.providers.observer.model} (via claude -p, on your subscription)`
|
|
673
708
|
: 'none — nothing leaves this machine'}`,
|
|
674
709
|
`summarizer: ${summarizerFor(config).id}`,
|
|
675
710
|
`spool drops: ${droppedCount()}`,
|
|
@@ -782,7 +817,9 @@ function memoryProcess(argv) {
|
|
|
782
817
|
// tell the developer nothing happened while the queue quietly went back to
|
|
783
818
|
// spending a credential that may still be rejected.
|
|
784
819
|
const maxJobs = numberFlag(argv, '--max', 10);
|
|
785
|
-
|
|
820
|
+
// The hooks' background drain passes --no-resume: a hook that resumed by
|
|
821
|
+
// itself is exactly the retry loop the comment above rules out.
|
|
822
|
+
const resumed = argv.includes('--no-resume') ? 0 : resumePaused(db);
|
|
786
823
|
processPending(db, config, { maxJobs }).then((result) => {
|
|
787
824
|
process.stdout.write(`${resumed ? `resumed ${resumed} paused · ` : ''}processed ${result.processed} · entries ${result.entries} · failed ${result.failed} · skipped ${result.skipped}\n`);
|
|
788
825
|
db.close();
|
|
@@ -901,15 +938,54 @@ function projectMapFrom(argv) {
|
|
|
901
938
|
}
|
|
902
939
|
return map;
|
|
903
940
|
}
|
|
904
|
-
|
|
941
|
+
/** The `--verify` report: every source row by id, then where each project landed. */
|
|
942
|
+
function verifyLines(r) {
|
|
943
|
+
const missing = r.tables.reduce((n, t) => n + t.missing.length, 0);
|
|
944
|
+
const width = Math.max(0, ...r.projects.map((p) => p.project.length));
|
|
945
|
+
return [
|
|
946
|
+
`verify: ${r.sourcePath}`,
|
|
947
|
+
...r.tables.map((t) => ` ${t.table.padEnd(18)} ${String(t.source).padStart(6)} in source · ${String(t.present).padStart(6)} in Eklavya · ${t.missing.length} missing${t.missing.length ? ` (ids ${t.missing.slice(0, 10).join(', ')}${t.missing.length > 10 ? ', …' : ''})` : ''}`),
|
|
948
|
+
` changed since import: ${r.changed} entr${r.changed === 1 ? 'y' : 'ies'}`,
|
|
949
|
+
'placement:',
|
|
950
|
+
...r.projects.flatMap((p) => Object.entries(p.filedUnder).map(([to, n]) => {
|
|
951
|
+
const placed = path.isAbsolute(to);
|
|
952
|
+
return ` ${String(n).padStart(6)} ${p.project.padEnd(width)} ${placed ? `→ ${to}` : 'not placed — searchable only with --all-projects; --map it to a checkout'}`;
|
|
953
|
+
})),
|
|
954
|
+
missing
|
|
955
|
+
? `INCOMPLETE: ${missing} source row(s) are not in Eklavya — re-run without --verify to import them.`
|
|
956
|
+
: 'complete: every source row is in Eklavya.',
|
|
957
|
+
];
|
|
958
|
+
}
|
|
959
|
+
async function memoryImport(argv) {
|
|
905
960
|
const flagValues = new Set(argv.flatMap((a, i) => (a === '--map' || a === '--map-here' ? [argv[i + 1] ?? ''] : [])));
|
|
906
961
|
const source = argv.find((a) => !a.startsWith('--') && !flagValues.has(a));
|
|
907
962
|
if (!source)
|
|
908
|
-
fail('Usage: eklavya memory import <path-to-claude-mem.db> [--dry-run] [--resume] [--map <src>=<path>]');
|
|
963
|
+
fail('Usage: eklavya memory import <path-to-claude-mem.db> [--dry-run] [--verify] [--resume] [--map <src>=<path>]');
|
|
909
964
|
const dryRun = argv.includes('--dry-run');
|
|
910
|
-
const
|
|
965
|
+
const explicit = projectMapFrom(argv);
|
|
911
966
|
try {
|
|
967
|
+
if (argv.includes('--verify')) {
|
|
968
|
+
const db = openDb();
|
|
969
|
+
try {
|
|
970
|
+
const report = verifyImport(db, source);
|
|
971
|
+
process.stdout.write(`${verifyLines(report).join('\n')}\n`);
|
|
972
|
+
if (report.tables.some((t) => t.missing.length))
|
|
973
|
+
process.exit(1);
|
|
974
|
+
}
|
|
975
|
+
finally {
|
|
976
|
+
db.close();
|
|
977
|
+
}
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
912
980
|
const found = inventory(source);
|
|
981
|
+
// Placed the way `eklavya install` places them -- off Claude Code's
|
|
982
|
+
// transcripts -- so a re-run by hand files history where install would
|
|
983
|
+
// have. A --map names what the transcripts cannot, and wins.
|
|
984
|
+
const unsure = {};
|
|
985
|
+
const projectMap = { ...guessProjectMap(source, claudeHome(), unsure), ...explicit };
|
|
986
|
+
for (const name of Object.keys(explicit))
|
|
987
|
+
delete unsure[name];
|
|
988
|
+
const unsureLines = Object.entries(unsure).map(([name, paths]) => ` ${name}: ${paths.length} checkouts carry this name — pick one: --map ${name}=<path> (${paths.join(', ')})`);
|
|
913
989
|
const lines = [
|
|
914
990
|
`source: ${found.sourcePath}`,
|
|
915
991
|
`schema: ${found.schemaVersion ?? 'unversioned'} (this importer understands up to ${found.supportedMax})`,
|
|
@@ -930,14 +1006,15 @@ function memoryImport(argv) {
|
|
|
930
1006
|
'',
|
|
931
1007
|
...planned.map(([from, to]) => `would map: ${from} -> ${to}`),
|
|
932
1008
|
...(unmapped.length ? [`would keep as-is: ${unmapped.join(', ')}`] : []),
|
|
1009
|
+
...unsureLines,
|
|
933
1010
|
'Dry run: nothing was written, and the source was opened read-only.',
|
|
934
1011
|
'',
|
|
935
1012
|
].join('\n'));
|
|
936
1013
|
return;
|
|
937
1014
|
}
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
const report =
|
|
1015
|
+
{
|
|
1016
|
+
process.stdout.write('\n');
|
|
1017
|
+
const { report, verified } = await spin('import', 'importing…', () => importOffThread({ dbFile: dbPath(), source, opts: { resume: argv.includes('--resume'), projectMap } }));
|
|
941
1018
|
const rows = IMPORTED_TABLES.map((t) => ` ${t.padEnd(18)} read ${report.read[t]} · imported ${report.imported[t]} · already present ${report.skipped[t]}`);
|
|
942
1019
|
process.stdout.write([
|
|
943
1020
|
'',
|
|
@@ -946,6 +1023,7 @@ function memoryImport(argv) {
|
|
|
946
1023
|
` concept candidates: ${report.candidates} (all unassessed — no mastery, no attempts, no gate touched)`,
|
|
947
1024
|
` evidence links: ${report.links} (drill-down from an entry to the prompts and tool uses behind it)`,
|
|
948
1025
|
` re-indexed: ${report.reindexed} entries`,
|
|
1026
|
+
...(report.rehomed ? [` re-homed: ${report.rehomed} entries an earlier run left under a bare project name`] : []),
|
|
949
1027
|
` validation: ${report.validation.ok ? 'ok' : `FAILED — ${report.validation.notes.join('; ')}`}`,
|
|
950
1028
|
...report.projectsMapped.map((p) => ` mapped: ${p.from} -> ${p.to}`),
|
|
951
1029
|
// The unmapped list is the useful half: those rows only ever surface
|
|
@@ -958,11 +1036,15 @@ function memoryImport(argv) {
|
|
|
958
1036
|
: []),
|
|
959
1037
|
'',
|
|
960
1038
|
].join('\n'));
|
|
1039
|
+
if (unsureLines.length)
|
|
1040
|
+
process.stdout.write(`${unsureLines.join('\n')}\n`);
|
|
961
1041
|
if (!report.validation.ok)
|
|
962
1042
|
process.exit(1);
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1043
|
+
// Counts agreeing is what validation proves; this proves every source
|
|
1044
|
+
// row by id, and shows where each project's history is filed.
|
|
1045
|
+
process.stdout.write(`\n${verifyLines(verified).join('\n')}\n`);
|
|
1046
|
+
if (verified.tables.some((t) => t.missing.length))
|
|
1047
|
+
process.exit(1);
|
|
966
1048
|
}
|
|
967
1049
|
}
|
|
968
1050
|
catch (err) {
|
|
@@ -1101,7 +1183,9 @@ function memoryCommand(argv) {
|
|
|
1101
1183
|
case 'prune':
|
|
1102
1184
|
return memoryPrune();
|
|
1103
1185
|
case 'import':
|
|
1104
|
-
|
|
1186
|
+
// Async only so the spinner turns: the import itself runs on a worker.
|
|
1187
|
+
void memoryImport(rest);
|
|
1188
|
+
return;
|
|
1105
1189
|
case 'export':
|
|
1106
1190
|
return memoryExport(rest);
|
|
1107
1191
|
case 'restore':
|