context-vault 2.17.0 → 3.0.1
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/bin/cli.js +783 -108
- package/node_modules/@context-vault/core/dist/capture.d.ts +21 -0
- package/node_modules/@context-vault/core/dist/capture.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/capture.js +269 -0
- package/node_modules/@context-vault/core/dist/capture.js.map +1 -0
- package/node_modules/@context-vault/core/dist/categories.d.ts +6 -0
- package/node_modules/@context-vault/core/dist/categories.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/categories.js +50 -0
- package/node_modules/@context-vault/core/dist/categories.js.map +1 -0
- package/node_modules/@context-vault/core/dist/config.d.ts +4 -0
- package/node_modules/@context-vault/core/dist/config.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/config.js +190 -0
- package/node_modules/@context-vault/core/dist/config.js.map +1 -0
- package/node_modules/@context-vault/core/dist/constants.d.ts +33 -0
- package/node_modules/@context-vault/core/dist/constants.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/constants.js +23 -0
- package/node_modules/@context-vault/core/dist/constants.js.map +1 -0
- package/node_modules/@context-vault/core/dist/db.d.ts +13 -0
- package/node_modules/@context-vault/core/dist/db.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/db.js +191 -0
- package/node_modules/@context-vault/core/dist/db.js.map +1 -0
- package/node_modules/@context-vault/core/dist/embed.d.ts +5 -0
- package/node_modules/@context-vault/core/dist/embed.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/embed.js +78 -0
- package/node_modules/@context-vault/core/dist/embed.js.map +1 -0
- package/node_modules/@context-vault/core/dist/files.d.ts +13 -0
- package/node_modules/@context-vault/core/dist/files.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/files.js +66 -0
- package/node_modules/@context-vault/core/dist/files.js.map +1 -0
- package/node_modules/@context-vault/core/dist/formatters.d.ts +8 -0
- package/node_modules/@context-vault/core/dist/formatters.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/formatters.js +18 -0
- package/node_modules/@context-vault/core/dist/formatters.js.map +1 -0
- package/node_modules/@context-vault/core/dist/frontmatter.d.ts +12 -0
- package/node_modules/@context-vault/core/dist/frontmatter.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/frontmatter.js +101 -0
- package/node_modules/@context-vault/core/dist/frontmatter.js.map +1 -0
- package/node_modules/@context-vault/core/dist/index.d.ts +10 -0
- package/node_modules/@context-vault/core/dist/index.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/index.js +297 -0
- package/node_modules/@context-vault/core/dist/index.js.map +1 -0
- package/node_modules/@context-vault/core/dist/ingest-url.d.ts +20 -0
- package/node_modules/@context-vault/core/dist/ingest-url.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/ingest-url.js +113 -0
- package/node_modules/@context-vault/core/dist/ingest-url.js.map +1 -0
- package/node_modules/@context-vault/core/dist/main.d.ts +14 -0
- package/node_modules/@context-vault/core/dist/main.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/main.js +25 -0
- package/node_modules/@context-vault/core/dist/main.js.map +1 -0
- package/node_modules/@context-vault/core/dist/search.d.ts +18 -0
- package/node_modules/@context-vault/core/dist/search.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/search.js +238 -0
- package/node_modules/@context-vault/core/dist/search.js.map +1 -0
- package/node_modules/@context-vault/core/dist/types.d.ts +176 -0
- package/node_modules/@context-vault/core/dist/types.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/types.js +2 -0
- package/node_modules/@context-vault/core/dist/types.js.map +1 -0
- package/node_modules/@context-vault/core/package.json +66 -16
- package/node_modules/@context-vault/core/src/capture.ts +308 -0
- package/node_modules/@context-vault/core/src/categories.ts +54 -0
- package/node_modules/@context-vault/core/src/{core/config.js → config.ts} +34 -33
- package/node_modules/@context-vault/core/src/{constants.js → constants.ts} +6 -3
- package/node_modules/@context-vault/core/src/db.ts +229 -0
- package/node_modules/@context-vault/core/src/{index/embed.js → embed.ts} +10 -35
- package/node_modules/@context-vault/core/src/files.ts +80 -0
- package/node_modules/@context-vault/core/src/{capture/formatters.js → formatters.ts} +13 -11
- package/node_modules/@context-vault/core/src/{core/frontmatter.js → frontmatter.ts} +27 -33
- package/node_modules/@context-vault/core/src/index.ts +351 -0
- package/node_modules/@context-vault/core/src/ingest-url.ts +99 -0
- package/node_modules/@context-vault/core/src/main.ts +111 -0
- package/node_modules/@context-vault/core/src/search.ts +285 -0
- package/node_modules/@context-vault/core/src/types.ts +166 -0
- package/package.json +12 -7
- package/scripts/postinstall.js +1 -1
- package/{node_modules/@context-vault/core/src/core → src}/error-log.js +1 -15
- package/{node_modules/@context-vault/core/src/server → src}/helpers.js +9 -4
- package/src/linking.js +100 -0
- package/{node_modules/@context-vault/core/src/server/tools.js → src/register-tools.js} +14 -15
- package/src/{server/index.js → server.js} +8 -35
- package/src/status.js +235 -0
- package/{node_modules/@context-vault/core/src/core → src}/telemetry.js +9 -19
- package/src/temporal.js +97 -0
- package/{node_modules/@context-vault/core/src/server → src}/tools/context-status.js +3 -4
- package/{node_modules/@context-vault/core/src/server → src}/tools/create-snapshot.js +43 -75
- package/{node_modules/@context-vault/core/src/server → src}/tools/delete-context.js +0 -2
- package/{node_modules/@context-vault/core/src/server → src}/tools/get-context.js +118 -35
- package/{node_modules/@context-vault/core/src/server → src}/tools/ingest-project.js +5 -6
- package/{node_modules/@context-vault/core/src/server → src}/tools/ingest-url.js +3 -4
- package/{node_modules/@context-vault/core/src/server → src}/tools/list-buckets.js +4 -5
- package/{node_modules/@context-vault/core/src/server → src}/tools/list-context.js +3 -6
- package/{node_modules/@context-vault/core/src/server → src}/tools/save-context.js +41 -21
- package/{node_modules/@context-vault/core/src/server → src}/tools/session-start.js +9 -16
- package/node_modules/@context-vault/core/src/capture/file-ops.js +0 -97
- package/node_modules/@context-vault/core/src/capture/import-pipeline.js +0 -46
- package/node_modules/@context-vault/core/src/capture/importers.js +0 -387
- package/node_modules/@context-vault/core/src/capture/index.js +0 -236
- package/node_modules/@context-vault/core/src/capture/ingest-url.js +0 -252
- package/node_modules/@context-vault/core/src/consolidation/index.js +0 -112
- package/node_modules/@context-vault/core/src/core/categories.js +0 -72
- package/node_modules/@context-vault/core/src/core/files.js +0 -108
- package/node_modules/@context-vault/core/src/core/status.js +0 -350
- package/node_modules/@context-vault/core/src/index/db.js +0 -416
- package/node_modules/@context-vault/core/src/index/index.js +0 -522
- package/node_modules/@context-vault/core/src/index.js +0 -66
- package/node_modules/@context-vault/core/src/retrieve/index.js +0 -500
- package/node_modules/@context-vault/core/src/server/tools/submit-feedback.js +0 -55
- package/node_modules/@context-vault/core/src/sync/sync.js +0 -235
- package/src/hooks/post-tool-call.mjs +0 -62
- package/src/hooks/session-end.mjs +0 -492
- /package/{node_modules/@context-vault/core/src/server → src}/tools/clear-context.js +0 -0
package/bin/cli.js
CHANGED
|
@@ -34,7 +34,7 @@ const HOME = homedir();
|
|
|
34
34
|
|
|
35
35
|
const pkg = JSON.parse(readFileSync(join(ROOT, "package.json"), "utf-8"));
|
|
36
36
|
const VERSION = pkg.version;
|
|
37
|
-
const SERVER_PATH = resolve(ROOT, "src", "server
|
|
37
|
+
const SERVER_PATH = resolve(ROOT, "src", "server.js");
|
|
38
38
|
|
|
39
39
|
/** Detect if running as an npm-installed package (global or local) vs local dev clone */
|
|
40
40
|
function isInstalledPackage() {
|
|
@@ -282,7 +282,7 @@ function printDetectionResults(results) {
|
|
|
282
282
|
}
|
|
283
283
|
}
|
|
284
284
|
|
|
285
|
-
function showHelp() {
|
|
285
|
+
function showHelp(showAll = false) {
|
|
286
286
|
console.log(`
|
|
287
287
|
${bold("◇ context-vault")} ${dim(`v${VERSION}`)}
|
|
288
288
|
${dim("Persistent memory for AI agents")}
|
|
@@ -293,37 +293,50 @@ ${bold("Usage:")}
|
|
|
293
293
|
${dim("No command → runs setup (first time) or shows status (existing vault)")}
|
|
294
294
|
|
|
295
295
|
${bold("Commands:")}
|
|
296
|
-
${cyan("setup")}
|
|
297
|
-
${cyan("connect")} --key cv_...
|
|
298
|
-
${cyan("switch")} local|hosted
|
|
299
|
-
${cyan("serve")}
|
|
300
|
-
${cyan("hooks")} install|uninstall
|
|
301
|
-
${cyan("claude")} install|uninstall
|
|
302
|
-
${cyan("skills")} install
|
|
303
|
-
${cyan("health")}
|
|
304
|
-
${cyan("
|
|
305
|
-
${cyan("
|
|
306
|
-
${cyan("
|
|
307
|
-
${cyan("
|
|
308
|
-
${cyan("
|
|
309
|
-
${cyan("
|
|
310
|
-
${cyan("
|
|
311
|
-
${cyan("
|
|
312
|
-
${cyan("
|
|
313
|
-
${cyan("
|
|
314
|
-
${cyan("
|
|
315
|
-
${cyan("
|
|
316
|
-
${cyan("
|
|
317
|
-
${cyan("
|
|
318
|
-
${cyan("
|
|
319
|
-
${cyan("
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
${
|
|
296
|
+
${cyan("setup")} Interactive MCP server installer
|
|
297
|
+
${cyan("connect")} --key cv_... Connect AI tools to hosted vault
|
|
298
|
+
${cyan("switch")} local|hosted Switch between local and hosted MCP modes
|
|
299
|
+
${cyan("serve")} Start the MCP server (used by AI clients)
|
|
300
|
+
${cyan("hooks")} install|uninstall Install or remove Claude Code memory hook
|
|
301
|
+
${cyan("claude")} install|uninstall Alias for hooks install|uninstall
|
|
302
|
+
${cyan("skills")} install Install bundled Claude Code skills
|
|
303
|
+
${cyan("health")} Quick health check — vault, DB, entry count
|
|
304
|
+
${cyan("status")} Show vault diagnostics
|
|
305
|
+
${cyan("doctor")} Diagnose and repair common issues
|
|
306
|
+
${cyan("restart")} Stop running MCP server processes (client auto-restarts)
|
|
307
|
+
${cyan("search")} Search vault entries from CLI
|
|
308
|
+
${cyan("save")} Save an entry to the vault from CLI
|
|
309
|
+
${cyan("import")} <path> Import entries from file, directory, or .zip archive
|
|
310
|
+
${cyan("export")} Export vault entries (JSON, CSV, or portable ZIP)
|
|
311
|
+
${cyan("ingest")} <url> Fetch URL and save as vault entry
|
|
312
|
+
${cyan("ingest-project")} <path> Scan project directory and register as project entity
|
|
313
|
+
${cyan("reindex")} Rebuild search index from knowledge files
|
|
314
|
+
${cyan("migrate-dirs")} [--dry-run] Rename plural vault dirs to singular (post-2.18.0)
|
|
315
|
+
${cyan("archive")} Archive old ephemeral/event entries (use --dry-run to preview)
|
|
316
|
+
${cyan("restore")} <id> Restore an archived entry back into the vault
|
|
317
|
+
${cyan("prune")} Remove expired entries (use --dry-run to preview)
|
|
318
|
+
${cyan("update")} Check for and install updates
|
|
319
|
+
${cyan("uninstall")} Remove MCP configs and optionally data
|
|
320
|
+
`);
|
|
321
|
+
|
|
322
|
+
if (showAll) {
|
|
323
|
+
console.log(`${bold("Plumbing")} ${dim("(internal — hook implementations and maintenance utilities):")}
|
|
324
|
+
${cyan("recall")} Search vault from a Claude Code hook (reads stdin)
|
|
325
|
+
${cyan("session-capture")} Save a session summary entry (reads JSON from stdin)
|
|
326
|
+
${cyan("session-end")} Run session-end hook (parse transcript + capture)
|
|
327
|
+
${cyan("post-tool-call")} Run post-tool-call hook (log tool usage)
|
|
328
|
+
${cyan("flush")} Check vault health and confirm DB is accessible
|
|
329
|
+
${cyan("consolidate")} Find hot tags and cold entries for maintenance
|
|
330
|
+
${cyan("migrate")} Migrate vault between local and hosted
|
|
331
|
+
`);
|
|
332
|
+
} else {
|
|
333
|
+
console.log(` ${dim("Run")} ${dim("context-vault --help --all")} ${dim("to show internal plumbing commands.")}
|
|
334
|
+
`);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
console.log(`${bold("Options:")}
|
|
326
338
|
--help Show this help
|
|
339
|
+
--help --all Show all commands including internal plumbing
|
|
327
340
|
--version Show version
|
|
328
341
|
--vault-dir <path> Set vault directory (setup/serve)
|
|
329
342
|
--yes Non-interactive mode (accept all defaults)
|
|
@@ -361,6 +374,26 @@ async function runSetup() {
|
|
|
361
374
|
} catch {}
|
|
362
375
|
|
|
363
376
|
if (latestVersion === VERSION) {
|
|
377
|
+
// Even when "up to date", ensure the launcher points to a valid server
|
|
378
|
+
const dataDir = join(HOME, ".context-mcp");
|
|
379
|
+
const launcherPath = join(dataDir, "server.mjs");
|
|
380
|
+
let launcherOk = false;
|
|
381
|
+
if (existsSync(launcherPath)) {
|
|
382
|
+
const content = readFileSync(launcherPath, "utf-8");
|
|
383
|
+
const m = content.match(/import "(.+?)"/);
|
|
384
|
+
if (m && existsSync(m[1])) launcherOk = true;
|
|
385
|
+
}
|
|
386
|
+
if (!launcherOk && !isNpx()) {
|
|
387
|
+
mkdirSync(dataDir, { recursive: true });
|
|
388
|
+
writeFileSync(launcherPath, `import "${SERVER_PATH}";\n`);
|
|
389
|
+
console.log(
|
|
390
|
+
green(` ✓ context-vault v${VERSION} is up to date`) +
|
|
391
|
+
dim(` (vault: ${existingVault})`),
|
|
392
|
+
);
|
|
393
|
+
console.log(dim(` ↳ Repaired server launcher → ${SERVER_PATH}`));
|
|
394
|
+
console.log();
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
364
397
|
console.log(
|
|
365
398
|
green(` ✓ context-vault v${VERSION} is up to date`) +
|
|
366
399
|
dim(` (vault: ${existingVault})`),
|
|
@@ -788,7 +821,7 @@ async function runSetup() {
|
|
|
788
821
|
}, 100);
|
|
789
822
|
|
|
790
823
|
try {
|
|
791
|
-
const { embed } = await import("@context-vault/core/
|
|
824
|
+
const { embed } = await import("@context-vault/core/embed");
|
|
792
825
|
let timeoutHandle;
|
|
793
826
|
const timeout = new Promise((_, reject) => {
|
|
794
827
|
timeoutHandle = setTimeout(
|
|
@@ -1023,7 +1056,7 @@ async function runSetup() {
|
|
|
1023
1056
|
// Verify DB is accessible
|
|
1024
1057
|
let dbAccessible = false;
|
|
1025
1058
|
try {
|
|
1026
|
-
const { initDatabase } = await import("@context-vault/core/
|
|
1059
|
+
const { initDatabase } = await import("@context-vault/core/db");
|
|
1027
1060
|
const db = await initDatabase(vaultConfig.dbPath);
|
|
1028
1061
|
db.prepare("SELECT 1").get();
|
|
1029
1062
|
db.close();
|
|
@@ -1566,7 +1599,7 @@ async function runSwitch() {
|
|
|
1566
1599
|
if (target === "local") {
|
|
1567
1600
|
const launcherPath = join(dataDir, "server.mjs");
|
|
1568
1601
|
if (!existsSync(launcherPath)) {
|
|
1569
|
-
const serverAbs = resolve(ROOT, "src", "server
|
|
1602
|
+
const serverAbs = resolve(ROOT, "src", "server.js");
|
|
1570
1603
|
mkdirSync(dataDir, { recursive: true });
|
|
1571
1604
|
writeFileSync(launcherPath, `import "${serverAbs}";\n`);
|
|
1572
1605
|
}
|
|
@@ -1662,10 +1695,10 @@ async function runSwitch() {
|
|
|
1662
1695
|
async function runReindex() {
|
|
1663
1696
|
console.log(dim("Loading vault..."));
|
|
1664
1697
|
|
|
1665
|
-
const { resolveConfig } = await import("@context-vault/core/
|
|
1698
|
+
const { resolveConfig } = await import("@context-vault/core/config");
|
|
1666
1699
|
const { initDatabase, prepareStatements, insertVec, deleteVec } =
|
|
1667
|
-
await import("@context-vault/core/
|
|
1668
|
-
const { embed } = await import("@context-vault/core/
|
|
1700
|
+
await import("@context-vault/core/db");
|
|
1701
|
+
const { embed } = await import("@context-vault/core/embed");
|
|
1669
1702
|
const { reindex } = await import("@context-vault/core/index");
|
|
1670
1703
|
|
|
1671
1704
|
const config = resolveConfig();
|
|
@@ -1696,12 +1729,86 @@ async function runReindex() {
|
|
|
1696
1729
|
console.log(` ${dim("·")} ${stats.unchanged} unchanged`);
|
|
1697
1730
|
}
|
|
1698
1731
|
|
|
1732
|
+
async function runMigrateDirs() {
|
|
1733
|
+
const dryRun = flags.has("--dry-run");
|
|
1734
|
+
|
|
1735
|
+
// Vault dir: positional arg (skip --flags), or fall back to configured vault
|
|
1736
|
+
const positional = args.slice(1).find((a) => !a.startsWith("--"));
|
|
1737
|
+
let vaultDir = positional;
|
|
1738
|
+
|
|
1739
|
+
if (!vaultDir) {
|
|
1740
|
+
const { resolveConfig } = await import("@context-vault/core/config");
|
|
1741
|
+
const config = resolveConfig();
|
|
1742
|
+
if (!config.vaultDirExists) {
|
|
1743
|
+
console.error(red(`Vault directory not found: ${config.vaultDir}`));
|
|
1744
|
+
console.error("Run " + cyan("context-vault setup") + " to configure.");
|
|
1745
|
+
process.exit(1);
|
|
1746
|
+
}
|
|
1747
|
+
vaultDir = config.vaultDir;
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
if (!existsSync(vaultDir) || !statSync(vaultDir).isDirectory()) {
|
|
1751
|
+
console.error(red(`Error: ${vaultDir} is not a directory`));
|
|
1752
|
+
process.exit(1);
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
const { planMigration, executeMigration } =
|
|
1756
|
+
await import("@context-vault/core/migrate-dirs");
|
|
1757
|
+
|
|
1758
|
+
const ops = planMigration(vaultDir);
|
|
1759
|
+
|
|
1760
|
+
if (ops.length === 0) {
|
|
1761
|
+
console.log(green("✓ No plural directories found — vault is up to date."));
|
|
1762
|
+
return;
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
if (dryRun) {
|
|
1766
|
+
console.log(dim("Dry run — no files will be moved.\n"));
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
for (const op of ops) {
|
|
1770
|
+
const fileLabel = `${op.fileCount} ${op.fileCount === 1 ? "file" : "files"}`;
|
|
1771
|
+
const actionLabel = op.action === "rename" ? "RENAME" : "MERGE";
|
|
1772
|
+
const suffix = dryRun ? dim(" [dry-run]") : "";
|
|
1773
|
+
console.log(
|
|
1774
|
+
` ${cyan(actionLabel)}: ${op.pluralName}/ → ${op.singularName}/ (${fileLabel})${suffix}`,
|
|
1775
|
+
);
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
if (dryRun) {
|
|
1779
|
+
console.log();
|
|
1780
|
+
console.log(
|
|
1781
|
+
dim(
|
|
1782
|
+
` ${ops.length} ${ops.length === 1 ? "directory" : "directories"} would be renamed/merged.`,
|
|
1783
|
+
),
|
|
1784
|
+
);
|
|
1785
|
+
console.log(dim(" Remove --dry-run to apply."));
|
|
1786
|
+
return;
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
const { renamed, merged, errors } = executeMigration(ops);
|
|
1790
|
+
|
|
1791
|
+
console.log();
|
|
1792
|
+
if (renamed > 0) console.log(green(`✓ Renamed: ${renamed}`));
|
|
1793
|
+
if (merged > 0) console.log(green(`✓ Merged: ${merged}`));
|
|
1794
|
+
if (errors.length > 0) {
|
|
1795
|
+
for (const e of errors) console.log(red(` ✗ ${e}`));
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
if (renamed + merged > 0) {
|
|
1799
|
+
console.log();
|
|
1800
|
+
console.log(
|
|
1801
|
+
dim("Run `context-vault reindex` to rebuild the search index."),
|
|
1802
|
+
);
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1699
1806
|
async function runPrune() {
|
|
1700
1807
|
const dryRun = flags.has("--dry-run");
|
|
1701
1808
|
|
|
1702
|
-
const { resolveConfig } = await import("@context-vault/core/
|
|
1809
|
+
const { resolveConfig } = await import("@context-vault/core/config");
|
|
1703
1810
|
const { initDatabase, prepareStatements, insertVec, deleteVec } =
|
|
1704
|
-
await import("@context-vault/core/
|
|
1811
|
+
await import("@context-vault/core/db");
|
|
1705
1812
|
const { pruneExpired } = await import("@context-vault/core/index");
|
|
1706
1813
|
|
|
1707
1814
|
const config = resolveConfig();
|
|
@@ -1759,12 +1866,169 @@ async function runPrune() {
|
|
|
1759
1866
|
}
|
|
1760
1867
|
}
|
|
1761
1868
|
|
|
1869
|
+
async function runArchive() {
|
|
1870
|
+
const dryRun = flags.has("--dry-run");
|
|
1871
|
+
|
|
1872
|
+
const { resolveConfig } = await import("@context-vault/core/config");
|
|
1873
|
+
const { initDatabase, prepareStatements, insertVec, deleteVec } =
|
|
1874
|
+
await import("@context-vault/core/db");
|
|
1875
|
+
const { findArchiveCandidates, archiveEntries } =
|
|
1876
|
+
await import("@context-vault/core/archive");
|
|
1877
|
+
|
|
1878
|
+
const config = resolveConfig();
|
|
1879
|
+
if (!config.vaultDirExists) {
|
|
1880
|
+
console.error(red(`Vault directory not found: ${config.vaultDir}`));
|
|
1881
|
+
console.error("Run " + cyan("context-vault setup") + " to configure.");
|
|
1882
|
+
process.exit(1);
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
const db = await initDatabase(config.dbPath);
|
|
1886
|
+
|
|
1887
|
+
if (dryRun) {
|
|
1888
|
+
const ctx = {
|
|
1889
|
+
db,
|
|
1890
|
+
config,
|
|
1891
|
+
stmts: prepareStatements(db),
|
|
1892
|
+
embed: async () => null,
|
|
1893
|
+
insertVec: () => {},
|
|
1894
|
+
deleteVec: () => {},
|
|
1895
|
+
};
|
|
1896
|
+
const candidates = findArchiveCandidates(ctx);
|
|
1897
|
+
db.close();
|
|
1898
|
+
|
|
1899
|
+
if (candidates.length === 0) {
|
|
1900
|
+
console.log(green(" No entries eligible for archiving."));
|
|
1901
|
+
const lifecycle = config.lifecycle || {};
|
|
1902
|
+
console.log(dim("\n Retention windows:"));
|
|
1903
|
+
for (const [tier, rules] of Object.entries(lifecycle)) {
|
|
1904
|
+
if (rules?.archiveAfterDays) {
|
|
1905
|
+
console.log(dim(` ${tier}: archive after ${rules.archiveAfterDays} days`));
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
return;
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
console.log(
|
|
1912
|
+
`\n ${bold(String(candidates.length))} ${candidates.length === 1 ? "entry" : "entries"} eligible for archiving:\n`,
|
|
1913
|
+
);
|
|
1914
|
+
for (const e of candidates) {
|
|
1915
|
+
const label = e.title ? `${e.kind}: ${e.title}` : `${e.kind} (${e.id})`;
|
|
1916
|
+
const age = e.updated_at || e.created_at;
|
|
1917
|
+
console.log(
|
|
1918
|
+
` ${dim("-")} ${label} ${dim(`(tier=${e.tier}, last updated ${age})`)}`,
|
|
1919
|
+
);
|
|
1920
|
+
}
|
|
1921
|
+
console.log(dim("\n Dry run — no entries were archived."));
|
|
1922
|
+
console.log(dim(" Remove --dry-run to archive."));
|
|
1923
|
+
return;
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
const stmts = prepareStatements(db);
|
|
1927
|
+
const ctx = {
|
|
1928
|
+
db,
|
|
1929
|
+
config,
|
|
1930
|
+
stmts,
|
|
1931
|
+
embed: async () => null,
|
|
1932
|
+
insertVec: (r, e) => insertVec(stmts, r, e),
|
|
1933
|
+
deleteVec: (r) => deleteVec(stmts, r),
|
|
1934
|
+
};
|
|
1935
|
+
|
|
1936
|
+
const result = await archiveEntries(ctx);
|
|
1937
|
+
db.close();
|
|
1938
|
+
|
|
1939
|
+
if (result.count === 0) {
|
|
1940
|
+
console.log(green(" No entries eligible for archiving."));
|
|
1941
|
+
} else {
|
|
1942
|
+
console.log(
|
|
1943
|
+
green(
|
|
1944
|
+
` ✓ Archived ${result.count} ${result.count === 1 ? "entry" : "entries"} to _archive/`,
|
|
1945
|
+
),
|
|
1946
|
+
);
|
|
1947
|
+
console.log(
|
|
1948
|
+
dim(` Files moved to: ${join(config.vaultDir, "_archive")}`),
|
|
1949
|
+
);
|
|
1950
|
+
console.log(
|
|
1951
|
+
dim(" Restore with: context-vault restore <id>"),
|
|
1952
|
+
);
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
async function runRestore() {
|
|
1957
|
+
const entryId = args[1];
|
|
1958
|
+
|
|
1959
|
+
if (!entryId || entryId.startsWith("--")) {
|
|
1960
|
+
const { resolveConfig } = await import("@context-vault/core/config");
|
|
1961
|
+
const { listArchivedEntries } =
|
|
1962
|
+
await import("@context-vault/core/archive");
|
|
1963
|
+
|
|
1964
|
+
const config = resolveConfig();
|
|
1965
|
+
|
|
1966
|
+
console.log(`\n ${bold("context-vault restore")} <id>\n`);
|
|
1967
|
+
console.log(` Restore an archived entry back into the active vault.\n`);
|
|
1968
|
+
|
|
1969
|
+
if (config.vaultDirExists) {
|
|
1970
|
+
const entries = listArchivedEntries(config.vaultDir);
|
|
1971
|
+
if (entries.length > 0) {
|
|
1972
|
+
console.log(` ${bold("Archived entries")} (${entries.length}):\n`);
|
|
1973
|
+
for (const e of entries.slice(0, 20)) {
|
|
1974
|
+
const label = e.title
|
|
1975
|
+
? `${e.kind}: ${e.title.slice(0, 60)}`
|
|
1976
|
+
: `${e.kind} (${e.id})`;
|
|
1977
|
+
console.log(` ${dim(e.id || "?")} ${label}`);
|
|
1978
|
+
}
|
|
1979
|
+
if (entries.length > 20) {
|
|
1980
|
+
console.log(dim(`\n ... and ${entries.length - 20} more`));
|
|
1981
|
+
}
|
|
1982
|
+
} else {
|
|
1983
|
+
console.log(dim(" No archived entries found."));
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
console.log();
|
|
1987
|
+
return;
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
const { resolveConfig } = await import("@context-vault/core/config");
|
|
1991
|
+
const { initDatabase, prepareStatements, insertVec, deleteVec } =
|
|
1992
|
+
await import("@context-vault/core/db");
|
|
1993
|
+
const { embed } = await import("@context-vault/core/embed");
|
|
1994
|
+
const { restoreEntry } = await import("@context-vault/core/archive");
|
|
1995
|
+
|
|
1996
|
+
const config = resolveConfig();
|
|
1997
|
+
if (!config.vaultDirExists) {
|
|
1998
|
+
console.error(red(`Vault directory not found: ${config.vaultDir}`));
|
|
1999
|
+
console.error("Run " + cyan("context-vault setup") + " to configure.");
|
|
2000
|
+
process.exit(1);
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
const db = await initDatabase(config.dbPath);
|
|
2004
|
+
const stmts = prepareStatements(db);
|
|
2005
|
+
const ctx = {
|
|
2006
|
+
db,
|
|
2007
|
+
config,
|
|
2008
|
+
stmts,
|
|
2009
|
+
embed,
|
|
2010
|
+
insertVec: (r, e) => insertVec(stmts, r, e),
|
|
2011
|
+
deleteVec: (r) => deleteVec(stmts, r),
|
|
2012
|
+
};
|
|
2013
|
+
|
|
2014
|
+
const result = await restoreEntry(ctx, entryId);
|
|
2015
|
+
db.close();
|
|
2016
|
+
|
|
2017
|
+
if (result.restored) {
|
|
2018
|
+
console.log(green(` ✓ Restored ${result.kind} entry: ${result.id}`));
|
|
2019
|
+
console.log(dim(` File: ${result.filePath}`));
|
|
2020
|
+
} else {
|
|
2021
|
+
console.error(red(` ✗ ${result.reason}`));
|
|
2022
|
+
process.exit(1);
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
|
|
1762
2026
|
async function runStatus() {
|
|
1763
|
-
const { resolveConfig } = await import("@context-vault/core/
|
|
1764
|
-
const { initDatabase } = await import("@context-vault/core/
|
|
1765
|
-
const { gatherVaultStatus } = await import("
|
|
2027
|
+
const { resolveConfig } = await import("@context-vault/core/config");
|
|
2028
|
+
const { initDatabase } = await import("@context-vault/core/db");
|
|
2029
|
+
const { gatherVaultStatus } = await import("../src/status.js");
|
|
1766
2030
|
const { errorLogPath, errorLogCount } =
|
|
1767
|
-
await import("
|
|
2031
|
+
await import("../src/error-log.js");
|
|
1768
2032
|
|
|
1769
2033
|
const config = resolveConfig();
|
|
1770
2034
|
|
|
@@ -1827,7 +2091,7 @@ async function runStatus() {
|
|
|
1827
2091
|
const filled = maxCount > 0 ? Math.round((c / maxCount) * BAR_WIDTH) : 0;
|
|
1828
2092
|
const bar = "█".repeat(filled) + "░".repeat(BAR_WIDTH - filled);
|
|
1829
2093
|
const countStr = String(c).padStart(4);
|
|
1830
|
-
console.log(`
|
|
2094
|
+
console.log(` ${dim(bar)} ${countStr} ${kind}s`);
|
|
1831
2095
|
}
|
|
1832
2096
|
} else {
|
|
1833
2097
|
console.log(`\n ${dim("(empty — no entries indexed)")}`);
|
|
@@ -1853,6 +2117,15 @@ async function runStatus() {
|
|
|
1853
2117
|
}
|
|
1854
2118
|
}
|
|
1855
2119
|
|
|
2120
|
+
if (status.archivedCount > 0) {
|
|
2121
|
+
console.log();
|
|
2122
|
+
console.log(
|
|
2123
|
+
dim(
|
|
2124
|
+
` ${status.archivedCount} archived ${status.archivedCount === 1 ? "entry" : "entries"} in _archive/ (excluded from search)`,
|
|
2125
|
+
),
|
|
2126
|
+
);
|
|
2127
|
+
}
|
|
2128
|
+
|
|
1856
2129
|
if (status.stalePaths) {
|
|
1857
2130
|
console.log();
|
|
1858
2131
|
console.log(yellow(" Stale paths detected in DB."));
|
|
@@ -2044,7 +2317,7 @@ async function runMigrate() {
|
|
|
2044
2317
|
return;
|
|
2045
2318
|
}
|
|
2046
2319
|
|
|
2047
|
-
const { resolveConfig } = await import("@context-vault/core/
|
|
2320
|
+
const { resolveConfig } = await import("@context-vault/core/config");
|
|
2048
2321
|
const config = resolveConfig();
|
|
2049
2322
|
|
|
2050
2323
|
if (direction === "to-hosted") {
|
|
@@ -2098,20 +2371,33 @@ async function runImport() {
|
|
|
2098
2371
|
const target = args[1];
|
|
2099
2372
|
if (!target) {
|
|
2100
2373
|
console.log(`\n ${bold("context-vault import")} <path>\n`);
|
|
2101
|
-
console.log(` Import entries from a file or
|
|
2102
|
-
console.log(` Supported formats: .md, .csv, .tsv, .json, .txt\n`);
|
|
2374
|
+
console.log(` Import entries from a file, directory, or portable archive.\n`);
|
|
2375
|
+
console.log(` Supported formats: .md, .csv, .tsv, .json, .txt, .zip\n`);
|
|
2103
2376
|
console.log(` Options:`);
|
|
2104
2377
|
console.log(` --kind <kind> Default kind (default: insight)`);
|
|
2105
2378
|
console.log(` --source <src> Default source (default: cli-import)`);
|
|
2106
2379
|
console.log(` --dry-run Show parsed entries without importing`);
|
|
2380
|
+
console.log(` --vault <path> Target vault directory (default: configured vault)`);
|
|
2107
2381
|
console.log();
|
|
2108
2382
|
return;
|
|
2109
2383
|
}
|
|
2110
2384
|
|
|
2111
|
-
const
|
|
2385
|
+
const dryRun = flags.has("--dry-run");
|
|
2386
|
+
const targetPath = resolve(target);
|
|
2387
|
+
|
|
2388
|
+
if (!existsSync(targetPath)) {
|
|
2389
|
+
console.error(red(` Path not found: ${targetPath}`));
|
|
2390
|
+
process.exit(1);
|
|
2391
|
+
}
|
|
2392
|
+
|
|
2393
|
+
if (targetPath.endsWith(".zip")) {
|
|
2394
|
+
return runImportZip(targetPath, dryRun);
|
|
2395
|
+
}
|
|
2396
|
+
|
|
2397
|
+
const { resolveConfig } = await import("@context-vault/core/config");
|
|
2112
2398
|
const { initDatabase, prepareStatements, insertVec, deleteVec } =
|
|
2113
|
-
await import("@context-vault/core/
|
|
2114
|
-
const { embed } = await import("@context-vault/core/
|
|
2399
|
+
await import("@context-vault/core/db");
|
|
2400
|
+
const { embed } = await import("@context-vault/core/embed");
|
|
2115
2401
|
const { parseFile, parseDirectory } =
|
|
2116
2402
|
await import("@context-vault/core/capture/importers");
|
|
2117
2403
|
const { importEntries } =
|
|
@@ -2120,13 +2406,6 @@ async function runImport() {
|
|
|
2120
2406
|
|
|
2121
2407
|
const kind = getFlag("--kind") || undefined;
|
|
2122
2408
|
const source = getFlag("--source") || "cli-import";
|
|
2123
|
-
const dryRun = flags.has("--dry-run");
|
|
2124
|
-
|
|
2125
|
-
const targetPath = resolve(target);
|
|
2126
|
-
if (!existsSync(targetPath)) {
|
|
2127
|
-
console.error(red(` Path not found: ${targetPath}`));
|
|
2128
|
-
process.exit(1);
|
|
2129
|
-
}
|
|
2130
2409
|
|
|
2131
2410
|
const stat = statSync(targetPath);
|
|
2132
2411
|
let entries;
|
|
@@ -2197,17 +2476,215 @@ async function runImport() {
|
|
|
2197
2476
|
console.log();
|
|
2198
2477
|
}
|
|
2199
2478
|
|
|
2479
|
+
async function runImportZip(zipPath, dryRun) {
|
|
2480
|
+
const AdmZip = (await import("adm-zip")).default;
|
|
2481
|
+
const { resolveConfig } = await import("@context-vault/core/config");
|
|
2482
|
+
const { initDatabase, prepareStatements, insertVec, deleteVec } =
|
|
2483
|
+
await import("@context-vault/core/db");
|
|
2484
|
+
const { embed } = await import("@context-vault/core/embed");
|
|
2485
|
+
const { indexEntry } = await import("@context-vault/core/index");
|
|
2486
|
+
const { parseFrontmatter } = await import("@context-vault/core/frontmatter");
|
|
2487
|
+
const { categoryDirFor } = await import("@context-vault/core/categories");
|
|
2488
|
+
const { mkdirSync, writeFileSync, existsSync: existsFn } = await import("node:fs");
|
|
2489
|
+
const { join: joinPath, basename: baseName } = await import("node:path");
|
|
2490
|
+
|
|
2491
|
+
let zip;
|
|
2492
|
+
try {
|
|
2493
|
+
zip = new AdmZip(zipPath);
|
|
2494
|
+
} catch (e) {
|
|
2495
|
+
console.error(red(`\n Failed to open archive: ${e.message}\n`));
|
|
2496
|
+
process.exit(1);
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
const manifestEntry = zip.getEntry("manifest.json");
|
|
2500
|
+
if (!manifestEntry) {
|
|
2501
|
+
console.error(red("\n Invalid archive: missing manifest.json\n"));
|
|
2502
|
+
process.exit(1);
|
|
2503
|
+
}
|
|
2504
|
+
|
|
2505
|
+
let manifest;
|
|
2506
|
+
try {
|
|
2507
|
+
manifest = JSON.parse(zip.readAsText("manifest.json"));
|
|
2508
|
+
} catch {
|
|
2509
|
+
console.error(red("\n Invalid archive: corrupt manifest.json\n"));
|
|
2510
|
+
process.exit(1);
|
|
2511
|
+
}
|
|
2512
|
+
|
|
2513
|
+
const indexEntry_ = zip.getEntry("index.json");
|
|
2514
|
+
if (!indexEntry_) {
|
|
2515
|
+
console.error(red("\n Invalid archive: missing index.json\n"));
|
|
2516
|
+
process.exit(1);
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2519
|
+
let index;
|
|
2520
|
+
try {
|
|
2521
|
+
index = JSON.parse(zip.readAsText("index.json"));
|
|
2522
|
+
} catch {
|
|
2523
|
+
console.error(red("\n Invalid archive: corrupt index.json\n"));
|
|
2524
|
+
process.exit(1);
|
|
2525
|
+
}
|
|
2526
|
+
|
|
2527
|
+
const entries = index.entries || [];
|
|
2528
|
+
if (entries.length === 0) {
|
|
2529
|
+
console.log(yellow("\n Archive contains no entries.\n"));
|
|
2530
|
+
return;
|
|
2531
|
+
}
|
|
2532
|
+
|
|
2533
|
+
console.log(`\n ${bold("◇ context-vault import")} ${dim(baseName(zipPath))}`);
|
|
2534
|
+
console.log(dim(` Archive: v${manifest.version} · ${manifest.entry_count} entries · ${manifest.context_vault_version || "?"}`));
|
|
2535
|
+
|
|
2536
|
+
const kindCounts = {};
|
|
2537
|
+
for (const e of entries) {
|
|
2538
|
+
kindCounts[e.kind] = (kindCounts[e.kind] || 0) + 1;
|
|
2539
|
+
}
|
|
2540
|
+
console.log();
|
|
2541
|
+
for (const [k, count] of Object.entries(kindCounts).sort((a, b) => b[1] - a[1])) {
|
|
2542
|
+
console.log(` ${k}: ${count}`);
|
|
2543
|
+
}
|
|
2544
|
+
|
|
2545
|
+
const vaultDirOverride = getFlag("--vault");
|
|
2546
|
+
const config = (await import("@context-vault/core/config")).resolveConfig();
|
|
2547
|
+
const targetVaultDir = vaultDirOverride ? resolve(vaultDirOverride) : config.vaultDir;
|
|
2548
|
+
|
|
2549
|
+
if (!existsFn(targetVaultDir)) {
|
|
2550
|
+
console.error(red(`\n Vault directory not found: ${targetVaultDir}`));
|
|
2551
|
+
console.error(` Run ${cyan("context-vault setup")} to configure.`);
|
|
2552
|
+
process.exit(1);
|
|
2553
|
+
}
|
|
2554
|
+
|
|
2555
|
+
const db = await initDatabase(config.dbPath);
|
|
2556
|
+
const stmts = prepareStatements(db);
|
|
2557
|
+
const ctx = {
|
|
2558
|
+
db,
|
|
2559
|
+
config: { ...config, vaultDir: targetVaultDir },
|
|
2560
|
+
stmts,
|
|
2561
|
+
embed,
|
|
2562
|
+
insertVec: (r, e) => insertVec(stmts, r, e),
|
|
2563
|
+
deleteVec: (r) => deleteVec(stmts, r),
|
|
2564
|
+
};
|
|
2565
|
+
|
|
2566
|
+
const existingIds = new Set();
|
|
2567
|
+
const allIds = db.prepare("SELECT id FROM vault").all();
|
|
2568
|
+
for (const row of allIds) existingIds.add(row.id);
|
|
2569
|
+
|
|
2570
|
+
let imported = 0;
|
|
2571
|
+
let skippedDuplicate = 0;
|
|
2572
|
+
let skippedMissing = 0;
|
|
2573
|
+
let failed = 0;
|
|
2574
|
+
const errors = [];
|
|
2575
|
+
|
|
2576
|
+
if (dryRun) {
|
|
2577
|
+
for (let i = 0; i < Math.min(entries.length, 25); i++) {
|
|
2578
|
+
const e = entries[i];
|
|
2579
|
+
const isDuplicate = existingIds.has(e.id);
|
|
2580
|
+
const tagStr = e.tags?.length ? ` ${dim(`[${e.tags.join(", ")}]`)}` : "";
|
|
2581
|
+
const statusIcon = isDuplicate ? yellow("~") : green("+");
|
|
2582
|
+
const statusText = isDuplicate ? dim(" (duplicate, would skip)") : "";
|
|
2583
|
+
console.log(`\n ${statusIcon} ${dim(`[${i + 1}]`)} ${e.kind} — ${e.title || e.id}${tagStr}${statusText}`);
|
|
2584
|
+
}
|
|
2585
|
+
if (entries.length > 25) {
|
|
2586
|
+
console.log(dim(`\n ... and ${entries.length - 25} more`));
|
|
2587
|
+
}
|
|
2588
|
+
const wouldSkip = entries.filter((e) => existingIds.has(e.id)).length;
|
|
2589
|
+
console.log(`\n ${dim(`Would import ${entries.length - wouldSkip}, skip ${wouldSkip} duplicates.`)}`);
|
|
2590
|
+
console.log(dim(" Dry run — no entries were imported.\n"));
|
|
2591
|
+
db.close();
|
|
2592
|
+
return;
|
|
2593
|
+
}
|
|
2594
|
+
|
|
2595
|
+
for (let i = 0; i < entries.length; i++) {
|
|
2596
|
+
const entryMeta = entries[i];
|
|
2597
|
+
process.stdout.write(`\r Importing... ${i + 1}/${entries.length}`);
|
|
2598
|
+
|
|
2599
|
+
if (existingIds.has(entryMeta.id)) {
|
|
2600
|
+
skippedDuplicate++;
|
|
2601
|
+
continue;
|
|
2602
|
+
}
|
|
2603
|
+
|
|
2604
|
+
const zipEntry = zip.getEntry(entryMeta.file);
|
|
2605
|
+
if (!zipEntry) {
|
|
2606
|
+
skippedMissing++;
|
|
2607
|
+
continue;
|
|
2608
|
+
}
|
|
2609
|
+
|
|
2610
|
+
const mdContent = zip.readAsText(entryMeta.file);
|
|
2611
|
+
const { meta: fmMeta, body: rawBody } = parseFrontmatter(mdContent);
|
|
2612
|
+
|
|
2613
|
+
const kind = entryMeta.kind || fmMeta.kind || "insight";
|
|
2614
|
+
const categoryDir = categoryDirFor(kind);
|
|
2615
|
+
const targetDir = joinPath(targetVaultDir, categoryDir, kind);
|
|
2616
|
+
|
|
2617
|
+
try {
|
|
2618
|
+
mkdirSync(targetDir, { recursive: true });
|
|
2619
|
+
|
|
2620
|
+
const fileName = baseName(entryMeta.file);
|
|
2621
|
+
const filePath = joinPath(targetDir, fileName);
|
|
2622
|
+
writeFileSync(filePath, mdContent);
|
|
2623
|
+
|
|
2624
|
+
const id = fmMeta.id || entryMeta.id;
|
|
2625
|
+
const tags = Array.isArray(fmMeta.tags) ? fmMeta.tags : entryMeta.tags || [];
|
|
2626
|
+
const title = fmMeta.title || entryMeta.title || null;
|
|
2627
|
+
const source = fmMeta.source || entryMeta.source || "archive-import";
|
|
2628
|
+
const identity_key = fmMeta.identity_key || entryMeta.identity_key || null;
|
|
2629
|
+
const expires_at = fmMeta.expires_at || entryMeta.expires_at || null;
|
|
2630
|
+
const createdAt = fmMeta.created || entryMeta.created_at || new Date().toISOString();
|
|
2631
|
+
|
|
2632
|
+
await indexEntry(ctx, {
|
|
2633
|
+
id,
|
|
2634
|
+
kind,
|
|
2635
|
+
category: entryMeta.category || undefined,
|
|
2636
|
+
title,
|
|
2637
|
+
body: rawBody,
|
|
2638
|
+
meta: null,
|
|
2639
|
+
tags,
|
|
2640
|
+
source,
|
|
2641
|
+
filePath,
|
|
2642
|
+
createdAt,
|
|
2643
|
+
identity_key,
|
|
2644
|
+
expires_at,
|
|
2645
|
+
});
|
|
2646
|
+
|
|
2647
|
+
imported++;
|
|
2648
|
+
} catch (e) {
|
|
2649
|
+
failed++;
|
|
2650
|
+
errors.push({ id: entryMeta.id, error: e.message });
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
|
|
2654
|
+
db.close();
|
|
2655
|
+
|
|
2656
|
+
console.log(`\r ${green("✓")} Import complete `);
|
|
2657
|
+
console.log(` ${green("+")} ${imported} imported`);
|
|
2658
|
+
if (skippedDuplicate > 0) {
|
|
2659
|
+
console.log(` ${dim("~")} ${skippedDuplicate} skipped (already exist)`);
|
|
2660
|
+
}
|
|
2661
|
+
if (skippedMissing > 0) {
|
|
2662
|
+
console.log(` ${yellow("!")} ${skippedMissing} skipped (file missing in archive)`);
|
|
2663
|
+
}
|
|
2664
|
+
if (failed > 0) {
|
|
2665
|
+
console.log(` ${red("x")} ${failed} failed`);
|
|
2666
|
+
for (const e of errors.slice(0, 5)) {
|
|
2667
|
+
console.log(` ${dim(e.error)}`);
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
console.log();
|
|
2671
|
+
}
|
|
2672
|
+
|
|
2200
2673
|
async function runExport() {
|
|
2201
2674
|
const format = getFlag("--format") || "json";
|
|
2202
|
-
const output = getFlag("--output");
|
|
2675
|
+
const output = getFlag("--output") || getFlag("-o");
|
|
2203
2676
|
const rawPageSize = getFlag("--page-size");
|
|
2204
2677
|
const pageSize = rawPageSize
|
|
2205
2678
|
? Math.max(1, parseInt(rawPageSize, 10) || 100)
|
|
2206
2679
|
: null;
|
|
2207
2680
|
|
|
2208
|
-
|
|
2681
|
+
if (format === "zip") {
|
|
2682
|
+
return runExportZip();
|
|
2683
|
+
}
|
|
2684
|
+
|
|
2685
|
+
const { resolveConfig } = await import("@context-vault/core/config");
|
|
2209
2686
|
const { initDatabase, prepareStatements } =
|
|
2210
|
-
await import("@context-vault/core/
|
|
2687
|
+
await import("@context-vault/core/db");
|
|
2211
2688
|
const { writeFileSync } = await import("node:fs");
|
|
2212
2689
|
|
|
2213
2690
|
const config = resolveConfig();
|
|
@@ -2223,7 +2700,6 @@ async function runExport() {
|
|
|
2223
2700
|
|
|
2224
2701
|
let entries;
|
|
2225
2702
|
if (pageSize) {
|
|
2226
|
-
// Paginated: fetch in chunks to avoid loading everything into memory
|
|
2227
2703
|
entries = [];
|
|
2228
2704
|
let offset = 0;
|
|
2229
2705
|
const stmt = db.prepare(
|
|
@@ -2293,6 +2769,188 @@ async function runExport() {
|
|
|
2293
2769
|
}
|
|
2294
2770
|
}
|
|
2295
2771
|
|
|
2772
|
+
async function runExportZip() {
|
|
2773
|
+
const output = getFlag("--output") || getFlag("-o");
|
|
2774
|
+
const dryRun = flags.has("--dry-run");
|
|
2775
|
+
const tagsRaw = getFlag("--tags");
|
|
2776
|
+
const kindRaw = getFlag("--kind");
|
|
2777
|
+
const since = getFlag("--since");
|
|
2778
|
+
const until = getFlag("--until");
|
|
2779
|
+
const exportAll = flags.has("--all");
|
|
2780
|
+
|
|
2781
|
+
const tagsFilter = tagsRaw
|
|
2782
|
+
? tagsRaw.split(",").map((t) => t.trim()).filter(Boolean)
|
|
2783
|
+
: null;
|
|
2784
|
+
const kindFilter = kindRaw
|
|
2785
|
+
? kindRaw.split(",").map((k) => k.trim()).filter(Boolean)
|
|
2786
|
+
: null;
|
|
2787
|
+
|
|
2788
|
+
if (!exportAll && !tagsFilter && !kindFilter && !since && !until) {
|
|
2789
|
+
console.log(`\n ${bold("context-vault export --format zip")} [options]\n`);
|
|
2790
|
+
console.log(` Export vault entries as a portable ZIP archive.\n`);
|
|
2791
|
+
console.log(` ${bold("Filters (at least one required, or use --all):")}`);
|
|
2792
|
+
console.log(` --tags <t1,t2> Filter by tags (comma-separated)`);
|
|
2793
|
+
console.log(` --kind <k1,k2> Filter by kind (comma-separated)`);
|
|
2794
|
+
console.log(` --since <YYYY-MM-DD> Entries created on or after date`);
|
|
2795
|
+
console.log(` --until <YYYY-MM-DD> Entries created on or before date`);
|
|
2796
|
+
console.log(` --all Export all entries\n`);
|
|
2797
|
+
console.log(` ${bold("Options:")}`);
|
|
2798
|
+
console.log(` --output, -o <path> Output file path`);
|
|
2799
|
+
console.log(` --dry-run Show what would be exported\n`);
|
|
2800
|
+
console.log(` ${bold("Examples:")}`);
|
|
2801
|
+
console.log(` context-vault export --tags stormfors --format zip -o stormfors.zip`);
|
|
2802
|
+
console.log(` context-vault export --kind decision,pattern --format zip`);
|
|
2803
|
+
console.log(` context-vault export --since 2026-01-01 --until 2026-02-28 --format zip`);
|
|
2804
|
+
console.log(` context-vault export --all --format zip --dry-run\n`);
|
|
2805
|
+
return;
|
|
2806
|
+
}
|
|
2807
|
+
|
|
2808
|
+
const { resolveConfig } = await import("@context-vault/core/config");
|
|
2809
|
+
const { initDatabase } = await import("@context-vault/core/db");
|
|
2810
|
+
const { readFileSync: readFs, existsSync: existsFn } = await import("node:fs");
|
|
2811
|
+
const { basename } = await import("node:path");
|
|
2812
|
+
|
|
2813
|
+
const config = resolveConfig();
|
|
2814
|
+
if (!config.vaultDirExists) {
|
|
2815
|
+
console.error(red(` Vault directory not found: ${config.vaultDir}`));
|
|
2816
|
+
process.exit(1);
|
|
2817
|
+
}
|
|
2818
|
+
|
|
2819
|
+
const db = await initDatabase(config.dbPath);
|
|
2820
|
+
|
|
2821
|
+
const conditions = ["(expires_at IS NULL OR expires_at > datetime('now'))"];
|
|
2822
|
+
const params = [];
|
|
2823
|
+
|
|
2824
|
+
if (tagsFilter) {
|
|
2825
|
+
const tagClauses = tagsFilter.map(() =>
|
|
2826
|
+
"EXISTS (SELECT 1 FROM json_each(vault.tags) WHERE json_each.value = ?)"
|
|
2827
|
+
);
|
|
2828
|
+
conditions.push(`(${tagClauses.join(" OR ")})`);
|
|
2829
|
+
params.push(...tagsFilter);
|
|
2830
|
+
}
|
|
2831
|
+
|
|
2832
|
+
if (kindFilter) {
|
|
2833
|
+
const placeholders = kindFilter.map(() => "?").join(", ");
|
|
2834
|
+
conditions.push(`kind IN (${placeholders})`);
|
|
2835
|
+
params.push(...kindFilter);
|
|
2836
|
+
}
|
|
2837
|
+
|
|
2838
|
+
if (since) {
|
|
2839
|
+
conditions.push("created_at >= ?");
|
|
2840
|
+
params.push(since.includes("T") ? since : `${since}T00:00:00.000Z`);
|
|
2841
|
+
}
|
|
2842
|
+
|
|
2843
|
+
if (until) {
|
|
2844
|
+
conditions.push("created_at <= ?");
|
|
2845
|
+
params.push(until.includes("T") ? until : `${until}T23:59:59.999Z`);
|
|
2846
|
+
}
|
|
2847
|
+
|
|
2848
|
+
const sql = `SELECT * FROM vault WHERE ${conditions.join(" AND ")} ORDER BY created_at DESC`;
|
|
2849
|
+
const rows = db.prepare(sql).all(...params);
|
|
2850
|
+
db.close();
|
|
2851
|
+
|
|
2852
|
+
if (rows.length === 0) {
|
|
2853
|
+
console.log(yellow("\n No entries match the given filters.\n"));
|
|
2854
|
+
return;
|
|
2855
|
+
}
|
|
2856
|
+
|
|
2857
|
+
const kindCounts = {};
|
|
2858
|
+
for (const row of rows) {
|
|
2859
|
+
kindCounts[row.kind] = (kindCounts[row.kind] || 0) + 1;
|
|
2860
|
+
}
|
|
2861
|
+
|
|
2862
|
+
console.log(`\n ${bold(String(rows.length))} entries match filters:\n`);
|
|
2863
|
+
for (const [k, count] of Object.entries(kindCounts).sort((a, b) => b[1] - a[1])) {
|
|
2864
|
+
console.log(` ${k}: ${count}`);
|
|
2865
|
+
}
|
|
2866
|
+
|
|
2867
|
+
const earliest = rows[rows.length - 1]?.created_at;
|
|
2868
|
+
const latest = rows[0]?.created_at;
|
|
2869
|
+
console.log(dim(`\n Date range: ${earliest?.slice(0, 10) || "?"} → ${latest?.slice(0, 10) || "?"}`));
|
|
2870
|
+
|
|
2871
|
+
if (dryRun) {
|
|
2872
|
+
console.log();
|
|
2873
|
+
for (let i = 0; i < Math.min(rows.length, 25); i++) {
|
|
2874
|
+
const r = rows[i];
|
|
2875
|
+
const tags = safeJsonParse(r.tags, []);
|
|
2876
|
+
const tagStr = tags.length ? ` ${dim(`[${tags.join(", ")}]`)}` : "";
|
|
2877
|
+
console.log(` ${dim(`[${i + 1}]`)} ${r.kind} — ${r.title || (r.body || "").slice(0, 60)}${tagStr}`);
|
|
2878
|
+
}
|
|
2879
|
+
if (rows.length > 25) {
|
|
2880
|
+
console.log(dim(` ... and ${rows.length - 25} more`));
|
|
2881
|
+
}
|
|
2882
|
+
console.log(dim("\n Dry run — no archive created.\n"));
|
|
2883
|
+
return;
|
|
2884
|
+
}
|
|
2885
|
+
|
|
2886
|
+
const AdmZip = (await import("adm-zip")).default;
|
|
2887
|
+
const zip = new AdmZip();
|
|
2888
|
+
|
|
2889
|
+
const indexEntries = [];
|
|
2890
|
+
let filesSkipped = 0;
|
|
2891
|
+
|
|
2892
|
+
for (const row of rows) {
|
|
2893
|
+
const entryPath = `entries/${row.kind}/${basename(row.file_path || `${row.id}.md`)}`;
|
|
2894
|
+
|
|
2895
|
+
let fileContent = null;
|
|
2896
|
+
if (row.file_path && existsFn(row.file_path)) {
|
|
2897
|
+
fileContent = readFs(row.file_path);
|
|
2898
|
+
}
|
|
2899
|
+
|
|
2900
|
+
if (!fileContent) {
|
|
2901
|
+
filesSkipped++;
|
|
2902
|
+
continue;
|
|
2903
|
+
}
|
|
2904
|
+
|
|
2905
|
+
zip.addFile(entryPath, fileContent);
|
|
2906
|
+
|
|
2907
|
+
indexEntries.push({
|
|
2908
|
+
id: row.id,
|
|
2909
|
+
kind: row.kind,
|
|
2910
|
+
category: row.category,
|
|
2911
|
+
title: row.title || null,
|
|
2912
|
+
tags: safeJsonParse(row.tags, []),
|
|
2913
|
+
source: row.source || null,
|
|
2914
|
+
identity_key: row.identity_key || null,
|
|
2915
|
+
expires_at: row.expires_at || null,
|
|
2916
|
+
created_at: row.created_at,
|
|
2917
|
+
file: entryPath,
|
|
2918
|
+
});
|
|
2919
|
+
}
|
|
2920
|
+
|
|
2921
|
+
const manifest = {
|
|
2922
|
+
version: 1,
|
|
2923
|
+
created_at: new Date().toISOString(),
|
|
2924
|
+
context_vault_version: VERSION,
|
|
2925
|
+
entry_count: indexEntries.length,
|
|
2926
|
+
date_range: { earliest, latest },
|
|
2927
|
+
filters: {
|
|
2928
|
+
tags: tagsFilter || null,
|
|
2929
|
+
kind: kindFilter || null,
|
|
2930
|
+
since: since || null,
|
|
2931
|
+
until: until || null,
|
|
2932
|
+
all: exportAll || false,
|
|
2933
|
+
},
|
|
2934
|
+
};
|
|
2935
|
+
|
|
2936
|
+
zip.addFile("manifest.json", Buffer.from(JSON.stringify(manifest, null, 2)));
|
|
2937
|
+
zip.addFile("index.json", Buffer.from(JSON.stringify({ entries: indexEntries }, null, 2)));
|
|
2938
|
+
|
|
2939
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
2940
|
+
const defaultName = tagsFilter
|
|
2941
|
+
? `vault-${tagsFilter[0]}-${today}.zip`
|
|
2942
|
+
: `vault-export-${today}.zip`;
|
|
2943
|
+
const outputPath = resolve(output || defaultName);
|
|
2944
|
+
|
|
2945
|
+
zip.writeZip(outputPath);
|
|
2946
|
+
|
|
2947
|
+
console.log(`\n ${green("✓")} Exported ${indexEntries.length} entries to ${outputPath}`);
|
|
2948
|
+
if (filesSkipped > 0) {
|
|
2949
|
+
console.log(yellow(` ⚠ ${filesSkipped} entries skipped (file not found on disk)`));
|
|
2950
|
+
}
|
|
2951
|
+
console.log();
|
|
2952
|
+
}
|
|
2953
|
+
|
|
2296
2954
|
function safeJsonParse(str, fallback) {
|
|
2297
2955
|
if (!str) return fallback;
|
|
2298
2956
|
try {
|
|
@@ -2361,10 +3019,10 @@ async function runIngest() {
|
|
|
2361
3019
|
return;
|
|
2362
3020
|
}
|
|
2363
3021
|
|
|
2364
|
-
const { resolveConfig } = await import("@context-vault/core/
|
|
3022
|
+
const { resolveConfig } = await import("@context-vault/core/config");
|
|
2365
3023
|
const { initDatabase, prepareStatements, insertVec, deleteVec } =
|
|
2366
|
-
await import("@context-vault/core/
|
|
2367
|
-
const { embed } = await import("@context-vault/core/
|
|
3024
|
+
await import("@context-vault/core/db");
|
|
3025
|
+
const { embed } = await import("@context-vault/core/embed");
|
|
2368
3026
|
const { captureAndIndex } = await import("@context-vault/core/capture");
|
|
2369
3027
|
|
|
2370
3028
|
const config = resolveConfig();
|
|
@@ -2428,10 +3086,10 @@ async function runIngestProject() {
|
|
|
2428
3086
|
|
|
2429
3087
|
console.log(dim(` Scanning ${projectPath}...`));
|
|
2430
3088
|
|
|
2431
|
-
const { resolveConfig } = await import("@context-vault/core/
|
|
3089
|
+
const { resolveConfig } = await import("@context-vault/core/config");
|
|
2432
3090
|
const { initDatabase, prepareStatements, insertVec, deleteVec } =
|
|
2433
|
-
await import("@context-vault/core/
|
|
2434
|
-
const { embed } = await import("@context-vault/core/
|
|
3091
|
+
await import("@context-vault/core/db");
|
|
3092
|
+
const { embed } = await import("@context-vault/core/embed");
|
|
2435
3093
|
const { captureAndIndex } = await import("@context-vault/core/capture");
|
|
2436
3094
|
const { existsSync: fsExists, readFileSync: fsRead } =
|
|
2437
3095
|
await import("node:fs");
|
|
@@ -2642,21 +3300,21 @@ async function runRecall() {
|
|
|
2642
3300
|
|
|
2643
3301
|
let db;
|
|
2644
3302
|
try {
|
|
2645
|
-
const { resolveConfig } = await import("@context-vault/core/
|
|
3303
|
+
const { resolveConfig } = await import("@context-vault/core/config");
|
|
2646
3304
|
const config = resolveConfig();
|
|
2647
3305
|
|
|
2648
3306
|
if (!config.vaultDirExists) return;
|
|
2649
3307
|
|
|
2650
3308
|
const { initDatabase, prepareStatements } =
|
|
2651
|
-
await import("@context-vault/core/
|
|
2652
|
-
const { embed } = await import("@context-vault/core/
|
|
3309
|
+
await import("@context-vault/core/db");
|
|
3310
|
+
const { embed } = await import("@context-vault/core/embed");
|
|
2653
3311
|
const { hybridSearch } = await import("@context-vault/core/retrieve/index");
|
|
2654
3312
|
|
|
2655
3313
|
db = await initDatabase(config.dbPath);
|
|
2656
3314
|
const stmts = prepareStatements(db);
|
|
2657
3315
|
const ctx = { db, config, stmts, embed };
|
|
2658
3316
|
|
|
2659
|
-
const { categoryFor } = await import("@context-vault/core/
|
|
3317
|
+
const { categoryFor } = await import("@context-vault/core/categories");
|
|
2660
3318
|
const recall = config.recall;
|
|
2661
3319
|
|
|
2662
3320
|
const results = await hybridSearch(ctx, query, {
|
|
@@ -2696,8 +3354,8 @@ async function runRecall() {
|
|
|
2696
3354
|
}
|
|
2697
3355
|
|
|
2698
3356
|
async function runFlush() {
|
|
2699
|
-
const { resolveConfig } = await import("@context-vault/core/
|
|
2700
|
-
const { initDatabase } = await import("@context-vault/core/
|
|
3357
|
+
const { resolveConfig } = await import("@context-vault/core/config");
|
|
3358
|
+
const { initDatabase } = await import("@context-vault/core/db");
|
|
2701
3359
|
|
|
2702
3360
|
let db;
|
|
2703
3361
|
try {
|
|
@@ -2743,12 +3401,12 @@ async function runSessionCapture() {
|
|
|
2743
3401
|
}
|
|
2744
3402
|
const { kind, title, body, tags, source } = payload;
|
|
2745
3403
|
if (!kind || !body) return;
|
|
2746
|
-
const { resolveConfig } = await import("@context-vault/core/
|
|
3404
|
+
const { resolveConfig } = await import("@context-vault/core/config");
|
|
2747
3405
|
const config = resolveConfig();
|
|
2748
3406
|
if (!config.vaultDirExists) return;
|
|
2749
3407
|
const { initDatabase, prepareStatements, insertVec, deleteVec } =
|
|
2750
|
-
await import("@context-vault/core/
|
|
2751
|
-
const { embed } = await import("@context-vault/core/
|
|
3408
|
+
await import("@context-vault/core/db");
|
|
3409
|
+
const { embed } = await import("@context-vault/core/embed");
|
|
2752
3410
|
const { captureAndIndex } = await import("@context-vault/core/capture");
|
|
2753
3411
|
db = await initDatabase(config.dbPath);
|
|
2754
3412
|
const stmts = prepareStatements(db);
|
|
@@ -2827,7 +3485,7 @@ async function runSave() {
|
|
|
2827
3485
|
|
|
2828
3486
|
let db;
|
|
2829
3487
|
try {
|
|
2830
|
-
const { resolveConfig } = await import("@context-vault/core/
|
|
3488
|
+
const { resolveConfig } = await import("@context-vault/core/config");
|
|
2831
3489
|
const config = resolveConfig();
|
|
2832
3490
|
if (!config.vaultDirExists) {
|
|
2833
3491
|
console.error(
|
|
@@ -2836,8 +3494,8 @@ async function runSave() {
|
|
|
2836
3494
|
process.exit(1);
|
|
2837
3495
|
}
|
|
2838
3496
|
const { initDatabase, prepareStatements, insertVec, deleteVec } =
|
|
2839
|
-
await import("@context-vault/core/
|
|
2840
|
-
const { embed } = await import("@context-vault/core/
|
|
3497
|
+
await import("@context-vault/core/db");
|
|
3498
|
+
const { embed } = await import("@context-vault/core/embed");
|
|
2841
3499
|
const { captureAndIndex } = await import("@context-vault/core/capture");
|
|
2842
3500
|
db = await initDatabase(config.dbPath);
|
|
2843
3501
|
const stmts = prepareStatements(db);
|
|
@@ -2881,6 +3539,7 @@ async function runSearch() {
|
|
|
2881
3539
|
const sort = getFlag("--sort") || "relevance";
|
|
2882
3540
|
const format = getFlag("--format") || "plain";
|
|
2883
3541
|
const showFull = flags.has("--full");
|
|
3542
|
+
const scopeArg = getFlag("--scope"); // "hot" | "events" | "all"
|
|
2884
3543
|
|
|
2885
3544
|
const valuedFlags = new Set([
|
|
2886
3545
|
"--kind",
|
|
@@ -2888,6 +3547,7 @@ async function runSearch() {
|
|
|
2888
3547
|
"--limit",
|
|
2889
3548
|
"--sort",
|
|
2890
3549
|
"--format",
|
|
3550
|
+
"--scope",
|
|
2891
3551
|
]);
|
|
2892
3552
|
|
|
2893
3553
|
const queryParts = [];
|
|
@@ -2909,7 +3569,7 @@ async function runSearch() {
|
|
|
2909
3569
|
|
|
2910
3570
|
let db;
|
|
2911
3571
|
try {
|
|
2912
|
-
const { resolveConfig } = await import("@context-vault/core/
|
|
3572
|
+
const { resolveConfig } = await import("@context-vault/core/config");
|
|
2913
3573
|
const config = resolveConfig();
|
|
2914
3574
|
if (!config.vaultDirExists) {
|
|
2915
3575
|
console.error(red("No vault found. Run: context-vault setup"));
|
|
@@ -2917,8 +3577,8 @@ async function runSearch() {
|
|
|
2917
3577
|
}
|
|
2918
3578
|
|
|
2919
3579
|
const { initDatabase, prepareStatements } =
|
|
2920
|
-
await import("@context-vault/core/
|
|
2921
|
-
const { embed } = await import("@context-vault/core/
|
|
3580
|
+
await import("@context-vault/core/db");
|
|
3581
|
+
const { embed } = await import("@context-vault/core/embed");
|
|
2922
3582
|
const { hybridSearch } = await import("@context-vault/core/retrieve/index");
|
|
2923
3583
|
|
|
2924
3584
|
db = await initDatabase(config.dbPath);
|
|
@@ -2927,8 +3587,18 @@ async function runSearch() {
|
|
|
2927
3587
|
|
|
2928
3588
|
let results;
|
|
2929
3589
|
|
|
3590
|
+
// Resolve scope → category/exclude filter
|
|
3591
|
+
const validScopes = new Set(["hot", "events", "all"]);
|
|
3592
|
+
const resolvedScope = validScopes.has(scopeArg) ? scopeArg : "hot";
|
|
3593
|
+
const scopeCategoryFilter = resolvedScope === "events" ? "event" : null;
|
|
3594
|
+
const scopeExcludeEvents = resolvedScope === "hot";
|
|
3595
|
+
|
|
2930
3596
|
if (query) {
|
|
2931
|
-
results = await hybridSearch(ctx, query, {
|
|
3597
|
+
results = await hybridSearch(ctx, query, {
|
|
3598
|
+
limit: limit * 2,
|
|
3599
|
+
categoryFilter: scopeCategoryFilter,
|
|
3600
|
+
excludeEvents: scopeExcludeEvents,
|
|
3601
|
+
});
|
|
2932
3602
|
|
|
2933
3603
|
if (kind) {
|
|
2934
3604
|
results = results.filter((r) => r.kind === kind);
|
|
@@ -2941,6 +3611,10 @@ async function runSearch() {
|
|
|
2941
3611
|
sql += " AND kind = ?";
|
|
2942
3612
|
params.push(kind);
|
|
2943
3613
|
}
|
|
3614
|
+
if (scopeCategoryFilter) {
|
|
3615
|
+
sql += " AND category = ?";
|
|
3616
|
+
params.push(scopeCategoryFilter);
|
|
3617
|
+
}
|
|
2944
3618
|
sql += " ORDER BY COALESCE(updated_at, created_at) DESC LIMIT ?";
|
|
2945
3619
|
params.push(limit);
|
|
2946
3620
|
results = db.prepare(sql).all(...params);
|
|
@@ -3662,9 +4336,9 @@ ${bold("Commands:")}
|
|
|
3662
4336
|
}
|
|
3663
4337
|
|
|
3664
4338
|
async function runDoctor() {
|
|
3665
|
-
const { resolveConfig } = await import("@context-vault/core/
|
|
4339
|
+
const { resolveConfig } = await import("@context-vault/core/config");
|
|
3666
4340
|
const { errorLogPath, errorLogCount } =
|
|
3667
|
-
await import("
|
|
4341
|
+
await import("../src/error-log.js");
|
|
3668
4342
|
|
|
3669
4343
|
console.log();
|
|
3670
4344
|
console.log(` ${bold("◇ context-vault doctor")} ${dim(`v${VERSION}`)}`);
|
|
@@ -3738,7 +4412,7 @@ async function runDoctor() {
|
|
|
3738
4412
|
let db;
|
|
3739
4413
|
if (existsSync(config.dbPath)) {
|
|
3740
4414
|
try {
|
|
3741
|
-
const { initDatabase } = await import("@context-vault/core/
|
|
4415
|
+
const { initDatabase } = await import("@context-vault/core/db");
|
|
3742
4416
|
db = await initDatabase(config.dbPath);
|
|
3743
4417
|
const schemaRow = db.prepare("PRAGMA user_version").get();
|
|
3744
4418
|
const schemaVersion = schemaRow?.user_version ?? "unknown";
|
|
@@ -3760,7 +4434,7 @@ async function runDoctor() {
|
|
|
3760
4434
|
|
|
3761
4435
|
// ── Embedding model ──────────────────────────────────────────────────
|
|
3762
4436
|
try {
|
|
3763
|
-
const { embed } = await import("@context-vault/core/
|
|
4437
|
+
const { embed } = await import("@context-vault/core/embed");
|
|
3764
4438
|
const vec = await embed("doctor check");
|
|
3765
4439
|
if (vec && vec.length > 0) {
|
|
3766
4440
|
console.log(
|
|
@@ -3840,7 +4514,7 @@ async function runDoctor() {
|
|
|
3840
4514
|
console.log(` ${dim(`${row.created_at} — ${row.title}`)}`);
|
|
3841
4515
|
}
|
|
3842
4516
|
console.log(
|
|
3843
|
-
` ${dim(
|
|
4517
|
+
` ${dim("Review: context-vault search --kind feedback --tag auto-captured")}`,
|
|
3844
4518
|
);
|
|
3845
4519
|
}
|
|
3846
4520
|
} catch {
|
|
@@ -3953,9 +4627,7 @@ async function runDoctor() {
|
|
|
3953
4627
|
console.log(
|
|
3954
4628
|
` ${yellow("!")} ${tool.name}: using old name "context-mcp"`,
|
|
3955
4629
|
);
|
|
3956
|
-
console.log(
|
|
3957
|
-
` ${dim("Fix: run context-vault setup to update")}`,
|
|
3958
|
-
);
|
|
4630
|
+
console.log(` ${dim("Fix: run context-vault setup to update")}`);
|
|
3959
4631
|
anyToolConfigured = true;
|
|
3960
4632
|
}
|
|
3961
4633
|
} catch {
|
|
@@ -3979,9 +4651,7 @@ async function runDoctor() {
|
|
|
3979
4651
|
}
|
|
3980
4652
|
|
|
3981
4653
|
if (!anyToolConfigured) {
|
|
3982
|
-
console.log(
|
|
3983
|
-
` ${yellow("!")} No AI tools have context-vault configured`,
|
|
3984
|
-
);
|
|
4654
|
+
console.log(` ${yellow("!")} No AI tools have context-vault configured`);
|
|
3985
4655
|
console.log(` ${dim("Fix: run context-vault setup")}`);
|
|
3986
4656
|
allOk = false;
|
|
3987
4657
|
}
|
|
@@ -4106,14 +4776,10 @@ async function runDoctor() {
|
|
|
4106
4776
|
|
|
4107
4777
|
if (hookCount === 0) {
|
|
4108
4778
|
console.log(` ${dim("-")} No context-vault hooks installed`);
|
|
4109
|
-
console.log(
|
|
4110
|
-
` ${dim("Optional: run context-vault hooks install")}`,
|
|
4111
|
-
);
|
|
4779
|
+
console.log(` ${dim("Optional: run context-vault hooks install")}`);
|
|
4112
4780
|
}
|
|
4113
4781
|
} catch {
|
|
4114
|
-
console.log(
|
|
4115
|
-
` ${yellow("!")} Could not read ${settingsPath}`,
|
|
4116
|
-
);
|
|
4782
|
+
console.log(` ${yellow("!")} Could not read ${settingsPath}`);
|
|
4117
4783
|
}
|
|
4118
4784
|
} else {
|
|
4119
4785
|
console.log(` ${dim("-")} No Claude Code settings found`);
|
|
@@ -4134,9 +4800,9 @@ async function runDoctor() {
|
|
|
4134
4800
|
}
|
|
4135
4801
|
|
|
4136
4802
|
async function runHealth() {
|
|
4137
|
-
const { resolveConfig } = await import("@context-vault/core/
|
|
4803
|
+
const { resolveConfig } = await import("@context-vault/core/config");
|
|
4138
4804
|
const { initDatabase, testConnection } =
|
|
4139
|
-
await import("@context-vault/core/
|
|
4805
|
+
await import("@context-vault/core/db");
|
|
4140
4806
|
|
|
4141
4807
|
let config;
|
|
4142
4808
|
let healthy = true;
|
|
@@ -4325,8 +4991,8 @@ async function runConsolidate() {
|
|
|
4325
4991
|
const dryRun = flags.has("--dry-run");
|
|
4326
4992
|
const tagArg = getFlag("--tag");
|
|
4327
4993
|
|
|
4328
|
-
const { resolveConfig } = await import("@context-vault/core/
|
|
4329
|
-
const { initDatabase } = await import("@context-vault/core/
|
|
4994
|
+
const { resolveConfig } = await import("@context-vault/core/config");
|
|
4995
|
+
const { initDatabase } = await import("@context-vault/core/db");
|
|
4330
4996
|
const { findHotTags, findColdEntries } =
|
|
4331
4997
|
await import("@context-vault/core/consolidation/index");
|
|
4332
4998
|
|
|
@@ -4445,7 +5111,7 @@ async function runConsolidate() {
|
|
|
4445
5111
|
);
|
|
4446
5112
|
console.log(
|
|
4447
5113
|
dim(
|
|
4448
|
-
` To archive:
|
|
5114
|
+
` To archive: run context-vault archive (or --dry-run to preview).`,
|
|
4449
5115
|
),
|
|
4450
5116
|
);
|
|
4451
5117
|
}
|
|
@@ -4455,7 +5121,7 @@ async function runConsolidate() {
|
|
|
4455
5121
|
}
|
|
4456
5122
|
|
|
4457
5123
|
async function runServe() {
|
|
4458
|
-
await import("../src/server
|
|
5124
|
+
await import("../src/server.js");
|
|
4459
5125
|
}
|
|
4460
5126
|
|
|
4461
5127
|
async function main() {
|
|
@@ -4465,7 +5131,7 @@ async function main() {
|
|
|
4465
5131
|
}
|
|
4466
5132
|
|
|
4467
5133
|
if (flags.has("--help") || command === "help") {
|
|
4468
|
-
showHelp();
|
|
5134
|
+
showHelp(flags.has("--all"));
|
|
4469
5135
|
return;
|
|
4470
5136
|
}
|
|
4471
5137
|
|
|
@@ -4537,6 +5203,15 @@ async function main() {
|
|
|
4537
5203
|
case "reindex":
|
|
4538
5204
|
await runReindex();
|
|
4539
5205
|
break;
|
|
5206
|
+
case "migrate-dirs":
|
|
5207
|
+
await runMigrateDirs();
|
|
5208
|
+
break;
|
|
5209
|
+
case "archive":
|
|
5210
|
+
await runArchive();
|
|
5211
|
+
break;
|
|
5212
|
+
case "restore":
|
|
5213
|
+
await runRestore();
|
|
5214
|
+
break;
|
|
4540
5215
|
case "prune":
|
|
4541
5216
|
await runPrune();
|
|
4542
5217
|
break;
|