context-vault 2.1.0 → 2.3.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/bin/cli.js +408 -61
- package/node_modules/@context-vault/core/package.json +36 -0
- package/{src → node_modules/@context-vault/core/src}/capture/index.js +66 -1
- package/{src → node_modules/@context-vault/core/src}/core/categories.js +1 -0
- package/{src → node_modules/@context-vault/core/src}/core/files.js +1 -0
- package/{src → node_modules/@context-vault/core/src}/index/db.js +1 -0
- package/{src → node_modules/@context-vault/core/src}/index/embed.js +10 -1
- package/node_modules/@context-vault/core/src/index.js +29 -0
- package/node_modules/@context-vault/core/src/server/tools.js +433 -0
- package/package.json +8 -8
- package/src/server/index.js +21 -4
- package/ui/serve.js +7 -6
- package/LICENSE +0 -21
- package/README.md +0 -395
- package/smithery.yaml +0 -10
- package/src/capture/README.md +0 -23
- package/src/core/README.md +0 -20
- package/src/index/README.md +0 -28
- package/src/retrieve/README.md +0 -19
- package/src/server/README.md +0 -44
- package/src/server/tools.js +0 -211
- /package/{src → node_modules/@context-vault/core/src}/capture/file-ops.js +0 -0
- /package/{src → node_modules/@context-vault/core/src}/capture/formatters.js +0 -0
- /package/{src → node_modules/@context-vault/core/src}/core/config.js +0 -0
- /package/{src → node_modules/@context-vault/core/src}/core/frontmatter.js +0 -0
- /package/{src → node_modules/@context-vault/core/src}/core/status.js +0 -0
- /package/{src → node_modules/@context-vault/core/src}/index/index.js +0 -0
- /package/{src → node_modules/@context-vault/core/src}/retrieve/index.js +0 -0
- /package/{src → node_modules/@context-vault/core/src}/server/helpers.js +0 -0
package/bin/cli.js
CHANGED
|
@@ -156,7 +156,8 @@ const TOOLS = [
|
|
|
156
156
|
|
|
157
157
|
function showHelp() {
|
|
158
158
|
console.log(`
|
|
159
|
-
${bold("context-
|
|
159
|
+
${bold("◇ context-vault")} ${dim(`v${VERSION}`)}
|
|
160
|
+
${dim("Persistent memory for AI agents")}
|
|
160
161
|
|
|
161
162
|
${bold("Usage:")}
|
|
162
163
|
context-mcp <command> [options]
|
|
@@ -167,6 +168,9 @@ ${bold("Commands:")}
|
|
|
167
168
|
${cyan("ui")} [--port 3141] Launch web dashboard
|
|
168
169
|
${cyan("reindex")} Rebuild search index from knowledge files
|
|
169
170
|
${cyan("status")} Show vault diagnostics
|
|
171
|
+
${cyan("update")} Check for and install updates
|
|
172
|
+
${cyan("uninstall")} Remove MCP configs and optionally data
|
|
173
|
+
${cyan("migrate")} Migrate vault between local and hosted
|
|
170
174
|
|
|
171
175
|
${bold("Options:")}
|
|
172
176
|
--help Show this help
|
|
@@ -180,14 +184,108 @@ ${bold("Options:")}
|
|
|
180
184
|
async function runSetup() {
|
|
181
185
|
// Banner
|
|
182
186
|
console.log();
|
|
183
|
-
console.log(bold("
|
|
184
|
-
console.log(dim(" Persistent memory for AI agents
|
|
185
|
-
console.log();
|
|
186
|
-
console.log(dim(" Setup will: detect tools, configure MCP, download embeddings, and verify."));
|
|
187
|
+
console.log(` ${bold("◇ context-vault")} ${dim(`v${VERSION}`)}`);
|
|
188
|
+
console.log(dim(" Persistent memory for AI agents"));
|
|
187
189
|
console.log();
|
|
188
190
|
|
|
191
|
+
// Check for existing installation
|
|
192
|
+
const existingConfig = join(HOME, ".context-mcp", "config.json");
|
|
193
|
+
if (existsSync(existingConfig) && !isNonInteractive) {
|
|
194
|
+
let existingVault = "(unknown)";
|
|
195
|
+
try {
|
|
196
|
+
const cfg = JSON.parse(readFileSync(existingConfig, "utf-8"));
|
|
197
|
+
existingVault = cfg.vaultDir || existingVault;
|
|
198
|
+
} catch {}
|
|
199
|
+
|
|
200
|
+
console.log(yellow(` Existing installation detected`));
|
|
201
|
+
console.log(dim(` Vault: ${existingVault}`));
|
|
202
|
+
console.log(dim(` Config: ${existingConfig}`));
|
|
203
|
+
console.log();
|
|
204
|
+
console.log(` 1) Full reconfigure`);
|
|
205
|
+
console.log(` 2) Update tool configs only ${dim("(skip vault setup)")}`);
|
|
206
|
+
console.log(` 3) Cancel`);
|
|
207
|
+
console.log();
|
|
208
|
+
const choice = await prompt(" Select:", "1");
|
|
209
|
+
|
|
210
|
+
if (choice === "3") {
|
|
211
|
+
console.log(dim(" Cancelled."));
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (choice === "2") {
|
|
216
|
+
// Skip vault setup, just reconfigure tools
|
|
217
|
+
console.log();
|
|
218
|
+
console.log(dim(` [1/2]`) + bold(" Detecting tools...\n"));
|
|
219
|
+
const detected = [];
|
|
220
|
+
for (const tool of TOOLS) {
|
|
221
|
+
const found = tool.detect();
|
|
222
|
+
if (found) {
|
|
223
|
+
detected.push(tool);
|
|
224
|
+
console.log(` ${green("+")} ${tool.name}`);
|
|
225
|
+
} else {
|
|
226
|
+
console.log(` ${dim("-")} ${dim(tool.name)} ${dim("(not found)")}`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
console.log();
|
|
230
|
+
|
|
231
|
+
if (detected.length === 0) {
|
|
232
|
+
console.log(yellow(" No supported tools detected."));
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
let selected;
|
|
237
|
+
console.log(bold(" Which tools should context-mcp connect to?\n"));
|
|
238
|
+
for (let i = 0; i < detected.length; i++) {
|
|
239
|
+
console.log(` ${i + 1}) ${detected[i].name}`);
|
|
240
|
+
}
|
|
241
|
+
console.log();
|
|
242
|
+
const answer = await prompt(
|
|
243
|
+
` Select (${dim("1,2,3")} or ${dim('"all"')}):`,
|
|
244
|
+
"all"
|
|
245
|
+
);
|
|
246
|
+
if (answer === "all" || answer === "") {
|
|
247
|
+
selected = detected;
|
|
248
|
+
} else {
|
|
249
|
+
const nums = answer.split(/[,\s]+/).map((n) => parseInt(n, 10) - 1).filter((n) => n >= 0 && n < detected.length);
|
|
250
|
+
selected = nums.map((n) => detected[n]);
|
|
251
|
+
if (selected.length === 0) selected = detected;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Read vault dir from existing config
|
|
255
|
+
let customVaultDir = null;
|
|
256
|
+
try {
|
|
257
|
+
const cfg = JSON.parse(readFileSync(existingConfig, "utf-8"));
|
|
258
|
+
const defaultVDir = join(HOME, "vault");
|
|
259
|
+
if (cfg.vaultDir && resolve(cfg.vaultDir) !== resolve(defaultVDir)) {
|
|
260
|
+
customVaultDir = cfg.vaultDir;
|
|
261
|
+
}
|
|
262
|
+
} catch {}
|
|
263
|
+
|
|
264
|
+
console.log(`\n ${dim("[2/2]")}${bold(" Configuring tools...\n")}`);
|
|
265
|
+
for (const tool of selected) {
|
|
266
|
+
try {
|
|
267
|
+
if (tool.configType === "cli") {
|
|
268
|
+
await configureClaude(tool, customVaultDir);
|
|
269
|
+
} else {
|
|
270
|
+
configureJsonTool(tool, customVaultDir);
|
|
271
|
+
}
|
|
272
|
+
console.log(` ${green("+")} ${tool.name} — configured`);
|
|
273
|
+
} catch (e) {
|
|
274
|
+
console.log(` ${red("x")} ${tool.name} — ${e.message}`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
console.log();
|
|
279
|
+
console.log(green(" ✓ Tool configs updated."));
|
|
280
|
+
console.log();
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
// choice === "1" falls through to full setup below
|
|
284
|
+
console.log();
|
|
285
|
+
}
|
|
286
|
+
|
|
189
287
|
// Detect tools
|
|
190
|
-
console.log(bold("
|
|
288
|
+
console.log(dim(` [1/5]`) + bold(" Detecting tools...\n"));
|
|
191
289
|
const detected = [];
|
|
192
290
|
for (const tool of TOOLS) {
|
|
193
291
|
const found = tool.detect();
|
|
@@ -254,6 +352,7 @@ async function runSetup() {
|
|
|
254
352
|
}
|
|
255
353
|
|
|
256
354
|
// Vault directory (content files)
|
|
355
|
+
console.log(dim(` [2/5]`) + bold(" Configuring vault...\n"));
|
|
257
356
|
const defaultVaultDir = join(HOME, "vault");
|
|
258
357
|
const vaultDir = isNonInteractive
|
|
259
358
|
? defaultVaultDir
|
|
@@ -299,10 +398,10 @@ async function runSetup() {
|
|
|
299
398
|
console.log(`\n ${green("+")} Wrote ${configPath}`);
|
|
300
399
|
|
|
301
400
|
// Pre-download embedding model
|
|
302
|
-
console.log(`\n${bold("
|
|
401
|
+
console.log(`\n ${dim("[3/5]")}${bold(" Downloading embedding model...")}`);
|
|
303
402
|
console.log(dim(" all-MiniLM-L6-v2 (~22MB, one-time download)\n"));
|
|
304
403
|
try {
|
|
305
|
-
const { embed } = await import("
|
|
404
|
+
const { embed } = await import("@context-vault/core/index/embed");
|
|
306
405
|
await embed("warmup");
|
|
307
406
|
console.log(` ${green("+")} Embedding model ready`);
|
|
308
407
|
} catch (e) {
|
|
@@ -319,7 +418,7 @@ async function runSetup() {
|
|
|
319
418
|
}
|
|
320
419
|
|
|
321
420
|
// Configure each tool — pass vault dir as arg if non-default
|
|
322
|
-
console.log(`\n${bold("
|
|
421
|
+
console.log(`\n ${dim("[4/5]")}${bold(" Configuring tools...\n")}`);
|
|
323
422
|
const results = [];
|
|
324
423
|
const defaultVDir = join(HOME, "vault");
|
|
325
424
|
const customVaultDir = resolvedVaultDir !== resolve(defaultVDir) ? resolvedVaultDir : null;
|
|
@@ -340,9 +439,9 @@ async function runSetup() {
|
|
|
340
439
|
}
|
|
341
440
|
|
|
342
441
|
// Seed entry
|
|
343
|
-
const seeded =
|
|
344
|
-
if (seeded) {
|
|
345
|
-
console.log(`\n ${green("+")} Created starter entry in vault`);
|
|
442
|
+
const seeded = createSeedEntries(resolvedVaultDir);
|
|
443
|
+
if (seeded > 0) {
|
|
444
|
+
console.log(`\n ${green("+")} Created ${seeded} starter ${seeded === 1 ? "entry" : "entries"} in vault`);
|
|
346
445
|
}
|
|
347
446
|
|
|
348
447
|
// Offer to launch UI
|
|
@@ -360,26 +459,41 @@ async function runSetup() {
|
|
|
360
459
|
}
|
|
361
460
|
|
|
362
461
|
// Health check
|
|
363
|
-
|
|
462
|
+
console.log(`\n ${dim("[5/5]")}${bold(" Health check...")}\n`);
|
|
463
|
+
const okResults = results.filter((r) => r.ok);
|
|
364
464
|
const checks = [
|
|
365
465
|
{ label: "Vault directory exists", pass: existsSync(resolvedVaultDir) },
|
|
366
466
|
{ label: "Config file written", pass: existsSync(configPath) },
|
|
367
|
-
{ label: "At least one tool configured", pass:
|
|
467
|
+
{ label: "At least one tool configured", pass: okResults.length > 0 },
|
|
368
468
|
];
|
|
369
469
|
const passed = checks.filter((c) => c.pass).length;
|
|
370
|
-
console.log(bold(`\n Health check: ${passed}/${checks.length} passed\n`));
|
|
371
470
|
for (const c of checks) {
|
|
372
|
-
console.log(` ${c.pass ? green("
|
|
471
|
+
console.log(` ${c.pass ? green("✓") : red("✗")} ${c.label}`);
|
|
373
472
|
}
|
|
374
473
|
|
|
375
|
-
//
|
|
376
|
-
const toolName =
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
474
|
+
// Completion box
|
|
475
|
+
const toolName = okResults.length ? okResults[0].tool.name : "your AI tool";
|
|
476
|
+
const boxLines = [
|
|
477
|
+
` ✓ Setup complete — ${passed}/${checks.length} checks passed`,
|
|
478
|
+
``,
|
|
479
|
+
` ${bold("AI Tools")} — open ${toolName} and try:`,
|
|
480
|
+
` "Search my vault for getting started"`,
|
|
481
|
+
` "Save an insight about [topic]"`,
|
|
482
|
+
` "Show my vault status"`,
|
|
483
|
+
``,
|
|
484
|
+
` ${bold("CLI Commands:")}`,
|
|
485
|
+
` context-mcp status Show vault health`,
|
|
486
|
+
` context-mcp ui Launch web dashboard`,
|
|
487
|
+
` context-mcp update Check for updates`,
|
|
488
|
+
];
|
|
489
|
+
const innerWidth = Math.max(...boxLines.map((l) => l.length)) + 2;
|
|
490
|
+
const pad = (s) => s + " ".repeat(Math.max(0, innerWidth - s.length));
|
|
491
|
+
console.log();
|
|
492
|
+
console.log(` ${dim("┌" + "─".repeat(innerWidth) + "┐")}`);
|
|
493
|
+
for (const line of boxLines) {
|
|
494
|
+
console.log(` ${dim("│")}${pad(line)}${dim("│")}`);
|
|
495
|
+
}
|
|
496
|
+
console.log(` ${dim("└" + "─".repeat(innerWidth) + "┘")}`);
|
|
383
497
|
console.log();
|
|
384
498
|
}
|
|
385
499
|
|
|
@@ -458,18 +572,21 @@ function configureJsonTool(tool, vaultDir) {
|
|
|
458
572
|
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
459
573
|
}
|
|
460
574
|
|
|
461
|
-
// ─── Seed
|
|
462
|
-
|
|
463
|
-
function
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
const
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
575
|
+
// ─── Seed Entries ────────────────────────────────────────────────────────────
|
|
576
|
+
|
|
577
|
+
function createSeedEntries(vaultDir) {
|
|
578
|
+
let created = 0;
|
|
579
|
+
|
|
580
|
+
// Entry 1: Getting started (improved)
|
|
581
|
+
const insightDir = join(vaultDir, "knowledge", "insights");
|
|
582
|
+
const insightPath = join(insightDir, "getting-started.md");
|
|
583
|
+
if (!existsSync(insightPath)) {
|
|
584
|
+
mkdirSync(insightDir, { recursive: true });
|
|
585
|
+
const id1 = Date.now().toString(36).toUpperCase().padStart(10, "0");
|
|
586
|
+
const now = new Date().toISOString();
|
|
587
|
+
writeFileSync(insightPath, `---
|
|
588
|
+
id: ${id1}
|
|
589
|
+
tags: ["getting-started", "vault"]
|
|
473
590
|
source: context-mcp-setup
|
|
474
591
|
created: ${now}
|
|
475
592
|
---
|
|
@@ -478,16 +595,48 @@ Welcome to your context vault! This is a seed entry created during setup.
|
|
|
478
595
|
Your vault stores knowledge as plain markdown files with YAML frontmatter.
|
|
479
596
|
AI agents search it using hybrid full-text + semantic search.
|
|
480
597
|
|
|
481
|
-
|
|
482
|
-
- "Search my vault for getting started"
|
|
483
|
-
- "Save an insight
|
|
484
|
-
- "Show my vault status"
|
|
598
|
+
**Quick start:**
|
|
599
|
+
- "Search my vault for getting started" — find this entry
|
|
600
|
+
- "Save an insight about [topic]" — add knowledge
|
|
601
|
+
- "Show my vault status" — check health
|
|
602
|
+
- "List my recent entries" — browse your vault
|
|
485
603
|
|
|
486
604
|
You can edit or delete this file anytime — it lives at:
|
|
487
|
-
${
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
605
|
+
${insightPath}
|
|
606
|
+
`);
|
|
607
|
+
created++;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// Entry 2: Example decision
|
|
611
|
+
const decisionDir = join(vaultDir, "knowledge", "decisions");
|
|
612
|
+
const decisionPath = join(decisionDir, "example-local-first-data.md");
|
|
613
|
+
if (!existsSync(decisionPath)) {
|
|
614
|
+
mkdirSync(decisionDir, { recursive: true });
|
|
615
|
+
const id2 = (Date.now() + 1).toString(36).toUpperCase().padStart(10, "0");
|
|
616
|
+
const now = new Date().toISOString();
|
|
617
|
+
writeFileSync(decisionPath, `---
|
|
618
|
+
id: ${id2}
|
|
619
|
+
tags: ["example", "architecture"]
|
|
620
|
+
source: context-mcp-setup
|
|
621
|
+
created: ${now}
|
|
622
|
+
---
|
|
623
|
+
Example decision: Use local-first data storage (SQLite + files) over cloud databases.
|
|
624
|
+
|
|
625
|
+
**Context:** For personal knowledge management, local storage provides better privacy,
|
|
626
|
+
offline access, and zero ongoing cost. The vault uses plain markdown files as the
|
|
627
|
+
source of truth with a SQLite index for fast search.
|
|
628
|
+
|
|
629
|
+
**Trade-offs:**
|
|
630
|
+
- Pro: Full data ownership, git-versioned, human-editable
|
|
631
|
+
- Pro: No cloud dependency, works offline
|
|
632
|
+
- Con: No built-in sync across devices (use git or Syncthing)
|
|
633
|
+
|
|
634
|
+
This is an example entry showing the decision format. Feel free to delete it.
|
|
635
|
+
`);
|
|
636
|
+
created++;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
return created;
|
|
491
640
|
}
|
|
492
641
|
|
|
493
642
|
// ─── UI Command ──────────────────────────────────────────────────────────────
|
|
@@ -509,10 +658,10 @@ function runUi() {
|
|
|
509
658
|
async function runReindex() {
|
|
510
659
|
console.log(dim("Loading vault..."));
|
|
511
660
|
|
|
512
|
-
const { resolveConfig } = await import("
|
|
513
|
-
const { initDatabase, prepareStatements, insertVec, deleteVec } = await import("
|
|
514
|
-
const { embed } = await import("
|
|
515
|
-
const { reindex } = await import("
|
|
661
|
+
const { resolveConfig } = await import("@context-vault/core/core/config");
|
|
662
|
+
const { initDatabase, prepareStatements, insertVec, deleteVec } = await import("@context-vault/core/index/db");
|
|
663
|
+
const { embed } = await import("@context-vault/core/index/embed");
|
|
664
|
+
const { reindex } = await import("@context-vault/core/index");
|
|
516
665
|
|
|
517
666
|
const config = resolveConfig();
|
|
518
667
|
if (!config.vaultDirExists) {
|
|
@@ -537,19 +686,19 @@ async function runReindex() {
|
|
|
537
686
|
const stats = await reindex(ctx, { fullSync: true });
|
|
538
687
|
|
|
539
688
|
db.close();
|
|
540
|
-
console.log(green("Reindex complete
|
|
541
|
-
console.log(`
|
|
542
|
-
console.log(`
|
|
543
|
-
console.log(`
|
|
544
|
-
console.log(`
|
|
689
|
+
console.log(green("✓ Reindex complete"));
|
|
690
|
+
console.log(` ${green("+")} ${stats.added} added`);
|
|
691
|
+
console.log(` ${yellow("~")} ${stats.updated} updated`);
|
|
692
|
+
console.log(` ${red("-")} ${stats.removed} removed`);
|
|
693
|
+
console.log(` ${dim("·")} ${stats.unchanged} unchanged`);
|
|
545
694
|
}
|
|
546
695
|
|
|
547
696
|
// ─── Status Command ──────────────────────────────────────────────────────────
|
|
548
697
|
|
|
549
698
|
async function runStatus() {
|
|
550
|
-
const { resolveConfig } = await import("
|
|
551
|
-
const { initDatabase } = await import("
|
|
552
|
-
const { gatherVaultStatus } = await import("
|
|
699
|
+
const { resolveConfig } = await import("@context-vault/core/core/config");
|
|
700
|
+
const { initDatabase } = await import("@context-vault/core/index/db");
|
|
701
|
+
const { gatherVaultStatus } = await import("@context-vault/core/core/status");
|
|
553
702
|
|
|
554
703
|
const config = resolveConfig();
|
|
555
704
|
const db = initDatabase(config.dbPath);
|
|
@@ -559,26 +708,43 @@ async function runStatus() {
|
|
|
559
708
|
db.close();
|
|
560
709
|
|
|
561
710
|
console.log();
|
|
562
|
-
console.log(bold("
|
|
711
|
+
console.log(` ${bold("◇ context-vault")} ${dim(`v${VERSION}`)}`);
|
|
563
712
|
console.log();
|
|
564
|
-
console.log(` Vault: ${config.vaultDir} (
|
|
565
|
-
console.log(` Database: ${config.dbPath} (${status.dbSize})`);
|
|
713
|
+
console.log(` Vault: ${config.vaultDir} ${dim(`(${config.vaultDirExists ? status.fileCount + " files" : "missing"})`)}`);
|
|
714
|
+
console.log(` Database: ${config.dbPath} ${dim(`(${status.dbSize})`)}`);
|
|
566
715
|
console.log(` Dev dir: ${config.devDir}`);
|
|
567
716
|
console.log(` Data dir: ${config.dataDir}`);
|
|
568
|
-
console.log(` Config: ${config.configPath} (
|
|
717
|
+
console.log(` Config: ${config.configPath} ${dim(`(${existsSync(config.configPath) ? "exists" : "missing"})`)}`);
|
|
569
718
|
console.log(` Resolved: ${status.resolvedFrom}`);
|
|
570
719
|
console.log(` Schema: v5 (categories)`);
|
|
571
720
|
|
|
572
721
|
if (status.kindCounts.length) {
|
|
722
|
+
const BAR_WIDTH = 20;
|
|
723
|
+
const maxCount = Math.max(...status.kindCounts.map((k) => k.c));
|
|
573
724
|
console.log();
|
|
574
725
|
console.log(bold(" Indexed"));
|
|
575
726
|
for (const { kind, c } of status.kindCounts) {
|
|
576
|
-
|
|
727
|
+
const filled = maxCount > 0 ? Math.round((c / maxCount) * BAR_WIDTH) : 0;
|
|
728
|
+
const bar = "█".repeat(filled) + "░".repeat(BAR_WIDTH - filled);
|
|
729
|
+
const countStr = String(c).padStart(4);
|
|
730
|
+
console.log(` ${countStr} ${kind}s ${dim(bar)}`);
|
|
577
731
|
}
|
|
578
732
|
} else {
|
|
579
733
|
console.log(`\n ${dim("(empty — no entries indexed)")}`);
|
|
580
734
|
}
|
|
581
735
|
|
|
736
|
+
if (status.embeddingStatus) {
|
|
737
|
+
const { indexed, total, missing } = status.embeddingStatus;
|
|
738
|
+
if (missing > 0) {
|
|
739
|
+
const BAR_WIDTH = 20;
|
|
740
|
+
const filled = total > 0 ? Math.round((indexed / total) * BAR_WIDTH) : 0;
|
|
741
|
+
const bar = "█".repeat(filled) + "░".repeat(BAR_WIDTH - filled);
|
|
742
|
+
const pct = total > 0 ? Math.round((indexed / total) * 100) : 0;
|
|
743
|
+
console.log();
|
|
744
|
+
console.log(` Embeddings ${dim(bar)} ${indexed}/${total} (${pct}%)`);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
582
748
|
if (status.subdirs.length) {
|
|
583
749
|
console.log();
|
|
584
750
|
console.log(bold(" Disk Directories"));
|
|
@@ -595,6 +761,178 @@ async function runStatus() {
|
|
|
595
761
|
console.log();
|
|
596
762
|
}
|
|
597
763
|
|
|
764
|
+
// ─── Update Command ─────────────────────────────────────────────────────────
|
|
765
|
+
|
|
766
|
+
async function runUpdate() {
|
|
767
|
+
console.log();
|
|
768
|
+
console.log(` ${bold("◇ context-vault")} ${dim(`v${VERSION}`)}`);
|
|
769
|
+
console.log();
|
|
770
|
+
|
|
771
|
+
let latest;
|
|
772
|
+
try {
|
|
773
|
+
latest = execSync("npm view context-vault version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
774
|
+
} catch {
|
|
775
|
+
console.error(red(" Could not check for updates. Verify your network connection."));
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
if (latest === VERSION) {
|
|
780
|
+
console.log(green(" Already up to date."));
|
|
781
|
+
console.log();
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
console.log(` Current: ${dim(VERSION)}`);
|
|
786
|
+
console.log(` Latest: ${green(latest)}`);
|
|
787
|
+
console.log();
|
|
788
|
+
|
|
789
|
+
if (!isNonInteractive) {
|
|
790
|
+
const answer = await prompt(` Update to v${latest}? (Y/n):`, "Y");
|
|
791
|
+
if (answer.toLowerCase() === "n") {
|
|
792
|
+
console.log(dim(" Cancelled."));
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
console.log(dim(" Installing..."));
|
|
798
|
+
try {
|
|
799
|
+
execSync("npm install -g context-vault@latest", { stdio: "inherit" });
|
|
800
|
+
console.log();
|
|
801
|
+
console.log(green(` ✓ Updated to v${latest}`));
|
|
802
|
+
} catch {
|
|
803
|
+
console.error(red(" Update failed. Try manually: npm install -g context-vault@latest"));
|
|
804
|
+
}
|
|
805
|
+
console.log();
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
// ─── Uninstall Command ──────────────────────────────────────────────────────
|
|
809
|
+
|
|
810
|
+
async function runUninstall() {
|
|
811
|
+
console.log();
|
|
812
|
+
console.log(` ${bold("◇ context-vault")} ${dim("uninstall")}`);
|
|
813
|
+
console.log();
|
|
814
|
+
|
|
815
|
+
// Remove from Claude Code
|
|
816
|
+
try {
|
|
817
|
+
const env = { ...process.env };
|
|
818
|
+
delete env.CLAUDECODE;
|
|
819
|
+
execSync("claude mcp remove context-mcp -s user", { stdio: "pipe", env });
|
|
820
|
+
console.log(` ${green("+")} Removed from Claude Code`);
|
|
821
|
+
} catch {
|
|
822
|
+
console.log(` ${dim("-")} Claude Code — not configured or not installed`);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// Remove from JSON-configured tools
|
|
826
|
+
for (const tool of TOOLS.filter((t) => t.configType === "json")) {
|
|
827
|
+
if (!existsSync(tool.configPath)) continue;
|
|
828
|
+
try {
|
|
829
|
+
const config = JSON.parse(readFileSync(tool.configPath, "utf-8"));
|
|
830
|
+
if (config[tool.configKey]?.["context-mcp"]) {
|
|
831
|
+
delete config[tool.configKey]["context-mcp"];
|
|
832
|
+
writeFileSync(tool.configPath, JSON.stringify(config, null, 2) + "\n");
|
|
833
|
+
console.log(` ${green("+")} Removed from ${tool.name}`);
|
|
834
|
+
}
|
|
835
|
+
} catch {
|
|
836
|
+
console.log(` ${dim("-")} ${tool.name} — could not update config`);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// Optionally remove data directory
|
|
841
|
+
const dataDir = join(HOME, ".context-mcp");
|
|
842
|
+
if (existsSync(dataDir)) {
|
|
843
|
+
console.log();
|
|
844
|
+
const answer = isNonInteractive
|
|
845
|
+
? "n"
|
|
846
|
+
: await prompt(` Remove data directory (${dataDir})? (y/N):`, "N");
|
|
847
|
+
if (answer.toLowerCase() === "y") {
|
|
848
|
+
const { rmSync } = await import("node:fs");
|
|
849
|
+
rmSync(dataDir, { recursive: true, force: true });
|
|
850
|
+
console.log(` ${green("+")} Removed ${dataDir}`);
|
|
851
|
+
} else {
|
|
852
|
+
console.log(` ${dim("Kept")} ${dataDir}`);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
console.log();
|
|
857
|
+
console.log(dim(" Vault directory was not touched (your knowledge files are safe)."));
|
|
858
|
+
console.log(` To fully remove: ${cyan("npm uninstall -g context-vault")}`);
|
|
859
|
+
console.log();
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
// ─── Migrate Command ─────────────────────────────────────────────────────────
|
|
863
|
+
|
|
864
|
+
async function runMigrate() {
|
|
865
|
+
const direction = args.includes("--to-hosted") ? "to-hosted"
|
|
866
|
+
: args.includes("--to-local") ? "to-local"
|
|
867
|
+
: null;
|
|
868
|
+
|
|
869
|
+
if (!direction) {
|
|
870
|
+
console.log(`\n ${bold("context-mcp migrate")}\n`);
|
|
871
|
+
console.log(` Usage:`);
|
|
872
|
+
console.log(` context-mcp migrate --to-hosted Upload local vault to hosted service`);
|
|
873
|
+
console.log(` context-mcp migrate --to-local Download hosted vault to local files`);
|
|
874
|
+
console.log(`\n Options:`);
|
|
875
|
+
console.log(` --url <url> Hosted server URL (default: https://vault.contextvault.dev)`);
|
|
876
|
+
console.log(` --key <key> API key (cv_...)`);
|
|
877
|
+
console.log();
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
const hostedUrl = getFlag("--url") || "https://vault.contextvault.dev";
|
|
882
|
+
const apiKey = getFlag("--key");
|
|
883
|
+
|
|
884
|
+
if (!apiKey) {
|
|
885
|
+
console.error(red(" Error: --key <api_key> is required for migration."));
|
|
886
|
+
console.error(` Get your API key at ${cyan(hostedUrl + "/dashboard")}`);
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
const { resolveConfig } = await import("@context-vault/core/core/config");
|
|
891
|
+
const config = resolveConfig();
|
|
892
|
+
|
|
893
|
+
if (direction === "to-hosted") {
|
|
894
|
+
const { migrateToHosted } = await import("@context-vault/hosted/migration/migrate");
|
|
895
|
+
console.log(`\n ${bold("Migrating to hosted")}...`);
|
|
896
|
+
console.log(dim(` Vault: ${config.vaultDir}`));
|
|
897
|
+
console.log(dim(` Target: ${hostedUrl}\n`));
|
|
898
|
+
|
|
899
|
+
const results = await migrateToHosted({
|
|
900
|
+
vaultDir: config.vaultDir,
|
|
901
|
+
hostedUrl,
|
|
902
|
+
apiKey,
|
|
903
|
+
log: (msg) => console.log(` ${dim(msg)}`),
|
|
904
|
+
});
|
|
905
|
+
|
|
906
|
+
console.log(`\n ${green("+")} ${results.uploaded} entries uploaded`);
|
|
907
|
+
if (results.failed > 0) {
|
|
908
|
+
console.log(` ${red("-")} ${results.failed} failed`);
|
|
909
|
+
for (const err of results.errors.slice(0, 5)) {
|
|
910
|
+
console.log(` ${dim(err)}`);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
console.log(dim("\n Your local vault was not modified (safe backup)."));
|
|
914
|
+
} else {
|
|
915
|
+
const { migrateToLocal } = await import("@context-vault/hosted/migration/migrate");
|
|
916
|
+
console.log(`\n ${bold("Migrating to local")}...`);
|
|
917
|
+
console.log(dim(` Source: ${hostedUrl}`));
|
|
918
|
+
console.log(dim(` Target: ${config.vaultDir}\n`));
|
|
919
|
+
|
|
920
|
+
const results = await migrateToLocal({
|
|
921
|
+
vaultDir: config.vaultDir,
|
|
922
|
+
hostedUrl,
|
|
923
|
+
apiKey,
|
|
924
|
+
log: (msg) => console.log(` ${dim(msg)}`),
|
|
925
|
+
});
|
|
926
|
+
|
|
927
|
+
console.log(`\n ${green("+")} ${results.downloaded} entries restored`);
|
|
928
|
+
if (results.failed > 0) {
|
|
929
|
+
console.log(` ${red("-")} ${results.failed} failed`);
|
|
930
|
+
}
|
|
931
|
+
console.log(dim("\n Run `context-mcp reindex` to rebuild the search index."));
|
|
932
|
+
}
|
|
933
|
+
console.log();
|
|
934
|
+
}
|
|
935
|
+
|
|
598
936
|
// ─── Serve Command ──────────────────────────────────────────────────────────
|
|
599
937
|
|
|
600
938
|
async function runServe() {
|
|
@@ -634,6 +972,15 @@ async function main() {
|
|
|
634
972
|
case "status":
|
|
635
973
|
await runStatus();
|
|
636
974
|
break;
|
|
975
|
+
case "update":
|
|
976
|
+
await runUpdate();
|
|
977
|
+
break;
|
|
978
|
+
case "uninstall":
|
|
979
|
+
await runUninstall();
|
|
980
|
+
break;
|
|
981
|
+
case "migrate":
|
|
982
|
+
await runMigrate();
|
|
983
|
+
break;
|
|
637
984
|
default:
|
|
638
985
|
console.error(red(`Unknown command: ${command}`));
|
|
639
986
|
console.error(`Run ${cyan("context-mcp --help")} for usage.`);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@context-vault/core",
|
|
3
|
+
"version": "2.3.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Shared core: capture, index, retrieve, tools, and utilities for context-vault",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.js",
|
|
9
|
+
"./capture": "./src/capture/index.js",
|
|
10
|
+
"./capture/formatters": "./src/capture/formatters.js",
|
|
11
|
+
"./capture/file-ops": "./src/capture/file-ops.js",
|
|
12
|
+
"./index/db": "./src/index/db.js",
|
|
13
|
+
"./index/embed": "./src/index/embed.js",
|
|
14
|
+
"./index": "./src/index/index.js",
|
|
15
|
+
"./retrieve": "./src/retrieve/index.js",
|
|
16
|
+
"./server/tools": "./src/server/tools.js",
|
|
17
|
+
"./server/helpers": "./src/server/helpers.js",
|
|
18
|
+
"./core/categories": "./src/core/categories.js",
|
|
19
|
+
"./core/config": "./src/core/config.js",
|
|
20
|
+
"./core/files": "./src/core/files.js",
|
|
21
|
+
"./core/frontmatter": "./src/core/frontmatter.js",
|
|
22
|
+
"./core/status": "./src/core/status.js"
|
|
23
|
+
},
|
|
24
|
+
"files": ["src/"],
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"engines": { "node": ">=20" },
|
|
27
|
+
"author": "Felix Hellstrom",
|
|
28
|
+
"repository": { "type": "git", "url": "https://github.com/fellanH/context-mcp.git", "directory": "packages/core" },
|
|
29
|
+
"homepage": "https://github.com/fellanH/context-mcp",
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@huggingface/transformers": "^3.0.0",
|
|
32
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
33
|
+
"better-sqlite3": "^12.6.2",
|
|
34
|
+
"sqlite-vec": "^0.1.0"
|
|
35
|
+
}
|
|
36
|
+
}
|