context-vault 2.2.0 → 2.4.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 +393 -63
- package/node_modules/@context-vault/core/LICENSE +21 -0
- package/node_modules/@context-vault/core/package.json +36 -0
- package/{src → node_modules/@context-vault/core/src}/capture/index.js +3 -2
- package/{src → node_modules/@context-vault/core/src}/core/categories.js +1 -0
- package/{src → node_modules/@context-vault/core/src}/core/config.js +5 -0
- package/{src → node_modules/@context-vault/core/src}/core/files.js +1 -0
- package/{src → node_modules/@context-vault/core/src}/core/status.js +32 -8
- package/node_modules/@context-vault/core/src/index/db.js +245 -0
- package/node_modules/@context-vault/core/src/index/embed.js +87 -0
- package/{src → node_modules/@context-vault/core/src}/index/index.js +51 -12
- package/node_modules/@context-vault/core/src/index.js +29 -0
- package/{src → node_modules/@context-vault/core/src}/retrieve/index.js +52 -43
- package/{src → node_modules/@context-vault/core/src}/server/tools.js +195 -31
- package/package.json +15 -15
- package/scripts/postinstall.js +45 -0
- package/scripts/prepack.js +31 -0
- package/src/server/index.js +127 -68
- package/ui/serve.js +7 -6
- package/README.md +0 -431
- 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/index/db.js +0 -139
- package/src/index/embed.js +0 -57
- package/src/retrieve/README.md +0 -19
- package/src/server/README.md +0 -44
- /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/frontmatter.js +0 -0
- /package/{src → node_modules/@context-vault/core/src}/server/helpers.js +0 -0
package/bin/cli.js
CHANGED
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
unlinkSync,
|
|
21
21
|
} from "node:fs";
|
|
22
22
|
import { join, resolve, dirname } from "node:path";
|
|
23
|
-
import { homedir } from "node:os";
|
|
23
|
+
import { homedir, platform } from "node:os";
|
|
24
24
|
import { execSync, fork } from "node:child_process";
|
|
25
25
|
import { fileURLToPath } from "node:url";
|
|
26
26
|
|
|
@@ -73,6 +73,36 @@ function prompt(question, defaultVal) {
|
|
|
73
73
|
});
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
// ─── Platform Helpers ────────────────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
const PLATFORM = platform();
|
|
79
|
+
|
|
80
|
+
/** Get the platform-specific application data directory */
|
|
81
|
+
function appDataDir() {
|
|
82
|
+
switch (PLATFORM) {
|
|
83
|
+
case "win32":
|
|
84
|
+
return process.env.APPDATA || join(HOME, "AppData", "Roaming");
|
|
85
|
+
case "darwin":
|
|
86
|
+
return join(HOME, "Library", "Application Support");
|
|
87
|
+
case "linux":
|
|
88
|
+
default:
|
|
89
|
+
return process.env.XDG_CONFIG_HOME || join(HOME, ".config");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Get the platform-specific VS Code extensions directory */
|
|
94
|
+
function vscodeDataDir() {
|
|
95
|
+
switch (PLATFORM) {
|
|
96
|
+
case "win32":
|
|
97
|
+
return join(appDataDir(), "Code", "User", "globalStorage");
|
|
98
|
+
case "darwin":
|
|
99
|
+
return join(appDataDir(), "Code", "User", "globalStorage");
|
|
100
|
+
case "linux":
|
|
101
|
+
default:
|
|
102
|
+
return join(HOME, ".config", "Code", "User", "globalStorage");
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
76
106
|
// ─── Tool Detection ──────────────────────────────────────────────────────────
|
|
77
107
|
|
|
78
108
|
const TOOLS = [
|
|
@@ -81,7 +111,8 @@ const TOOLS = [
|
|
|
81
111
|
name: "Claude Code",
|
|
82
112
|
detect: () => {
|
|
83
113
|
try {
|
|
84
|
-
|
|
114
|
+
const cmd = PLATFORM === "win32" ? "where claude" : "which claude";
|
|
115
|
+
execSync(cmd, { stdio: "pipe" });
|
|
85
116
|
return true;
|
|
86
117
|
} catch {
|
|
87
118
|
return false;
|
|
@@ -92,16 +123,9 @@ const TOOLS = [
|
|
|
92
123
|
{
|
|
93
124
|
id: "claude-desktop",
|
|
94
125
|
name: "Claude Desktop",
|
|
95
|
-
detect: () =>
|
|
96
|
-
existsSync(join(HOME, "Library", "Application Support", "Claude")),
|
|
126
|
+
detect: () => existsSync(join(appDataDir(), "Claude")),
|
|
97
127
|
configType: "json",
|
|
98
|
-
configPath: join(
|
|
99
|
-
HOME,
|
|
100
|
-
"Library",
|
|
101
|
-
"Application Support",
|
|
102
|
-
"Claude",
|
|
103
|
-
"claude_desktop_config.json"
|
|
104
|
-
),
|
|
128
|
+
configPath: join(appDataDir(), "Claude", "claude_desktop_config.json"),
|
|
105
129
|
configKey: "mcpServers",
|
|
106
130
|
},
|
|
107
131
|
{
|
|
@@ -124,26 +148,10 @@ const TOOLS = [
|
|
|
124
148
|
id: "cline",
|
|
125
149
|
name: "Cline (VS Code)",
|
|
126
150
|
detect: () =>
|
|
127
|
-
existsSync(
|
|
128
|
-
join(
|
|
129
|
-
HOME,
|
|
130
|
-
"Library",
|
|
131
|
-
"Application Support",
|
|
132
|
-
"Code",
|
|
133
|
-
"User",
|
|
134
|
-
"globalStorage",
|
|
135
|
-
"saoudrizwan.claude-dev",
|
|
136
|
-
"settings"
|
|
137
|
-
)
|
|
138
|
-
),
|
|
151
|
+
existsSync(join(vscodeDataDir(), "saoudrizwan.claude-dev", "settings")),
|
|
139
152
|
configType: "json",
|
|
140
153
|
configPath: join(
|
|
141
|
-
|
|
142
|
-
"Library",
|
|
143
|
-
"Application Support",
|
|
144
|
-
"Code",
|
|
145
|
-
"User",
|
|
146
|
-
"globalStorage",
|
|
154
|
+
vscodeDataDir(),
|
|
147
155
|
"saoudrizwan.claude-dev",
|
|
148
156
|
"settings",
|
|
149
157
|
"cline_mcp_settings.json"
|
|
@@ -168,6 +176,9 @@ ${bold("Commands:")}
|
|
|
168
176
|
${cyan("ui")} [--port 3141] Launch web dashboard
|
|
169
177
|
${cyan("reindex")} Rebuild search index from knowledge files
|
|
170
178
|
${cyan("status")} Show vault diagnostics
|
|
179
|
+
${cyan("update")} Check for and install updates
|
|
180
|
+
${cyan("uninstall")} Remove MCP configs and optionally data
|
|
181
|
+
${cyan("migrate")} Migrate vault between local and hosted
|
|
171
182
|
|
|
172
183
|
${bold("Options:")}
|
|
173
184
|
--help Show this help
|
|
@@ -185,6 +196,102 @@ async function runSetup() {
|
|
|
185
196
|
console.log(dim(" Persistent memory for AI agents"));
|
|
186
197
|
console.log();
|
|
187
198
|
|
|
199
|
+
// Check for existing installation
|
|
200
|
+
const existingConfig = join(HOME, ".context-mcp", "config.json");
|
|
201
|
+
if (existsSync(existingConfig) && !isNonInteractive) {
|
|
202
|
+
let existingVault = "(unknown)";
|
|
203
|
+
try {
|
|
204
|
+
const cfg = JSON.parse(readFileSync(existingConfig, "utf-8"));
|
|
205
|
+
existingVault = cfg.vaultDir || existingVault;
|
|
206
|
+
} catch {}
|
|
207
|
+
|
|
208
|
+
console.log(yellow(` Existing installation detected`));
|
|
209
|
+
console.log(dim(` Vault: ${existingVault}`));
|
|
210
|
+
console.log(dim(` Config: ${existingConfig}`));
|
|
211
|
+
console.log();
|
|
212
|
+
console.log(` 1) Full reconfigure`);
|
|
213
|
+
console.log(` 2) Update tool configs only ${dim("(skip vault setup)")}`);
|
|
214
|
+
console.log(` 3) Cancel`);
|
|
215
|
+
console.log();
|
|
216
|
+
const choice = await prompt(" Select:", "1");
|
|
217
|
+
|
|
218
|
+
if (choice === "3") {
|
|
219
|
+
console.log(dim(" Cancelled."));
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (choice === "2") {
|
|
224
|
+
// Skip vault setup, just reconfigure tools
|
|
225
|
+
console.log();
|
|
226
|
+
console.log(dim(` [1/2]`) + bold(" Detecting tools...\n"));
|
|
227
|
+
const detected = [];
|
|
228
|
+
for (const tool of TOOLS) {
|
|
229
|
+
const found = tool.detect();
|
|
230
|
+
if (found) {
|
|
231
|
+
detected.push(tool);
|
|
232
|
+
console.log(` ${green("+")} ${tool.name}`);
|
|
233
|
+
} else {
|
|
234
|
+
console.log(` ${dim("-")} ${dim(tool.name)} ${dim("(not found)")}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
console.log();
|
|
238
|
+
|
|
239
|
+
if (detected.length === 0) {
|
|
240
|
+
console.log(yellow(" No supported tools detected."));
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
let selected;
|
|
245
|
+
console.log(bold(" Which tools should context-mcp connect to?\n"));
|
|
246
|
+
for (let i = 0; i < detected.length; i++) {
|
|
247
|
+
console.log(` ${i + 1}) ${detected[i].name}`);
|
|
248
|
+
}
|
|
249
|
+
console.log();
|
|
250
|
+
const answer = await prompt(
|
|
251
|
+
` Select (${dim("1,2,3")} or ${dim('"all"')}):`,
|
|
252
|
+
"all"
|
|
253
|
+
);
|
|
254
|
+
if (answer === "all" || answer === "") {
|
|
255
|
+
selected = detected;
|
|
256
|
+
} else {
|
|
257
|
+
const nums = answer.split(/[,\s]+/).map((n) => parseInt(n, 10) - 1).filter((n) => n >= 0 && n < detected.length);
|
|
258
|
+
selected = nums.map((n) => detected[n]);
|
|
259
|
+
if (selected.length === 0) selected = detected;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Read vault dir from existing config
|
|
263
|
+
let customVaultDir = null;
|
|
264
|
+
try {
|
|
265
|
+
const cfg = JSON.parse(readFileSync(existingConfig, "utf-8"));
|
|
266
|
+
const defaultVDir = join(HOME, "vault");
|
|
267
|
+
if (cfg.vaultDir && resolve(cfg.vaultDir) !== resolve(defaultVDir)) {
|
|
268
|
+
customVaultDir = cfg.vaultDir;
|
|
269
|
+
}
|
|
270
|
+
} catch {}
|
|
271
|
+
|
|
272
|
+
console.log(`\n ${dim("[2/2]")}${bold(" Configuring tools...\n")}`);
|
|
273
|
+
for (const tool of selected) {
|
|
274
|
+
try {
|
|
275
|
+
if (tool.configType === "cli") {
|
|
276
|
+
await configureClaude(tool, customVaultDir);
|
|
277
|
+
} else {
|
|
278
|
+
configureJsonTool(tool, customVaultDir);
|
|
279
|
+
}
|
|
280
|
+
console.log(` ${green("+")} ${tool.name} — configured`);
|
|
281
|
+
} catch (e) {
|
|
282
|
+
console.log(` ${red("x")} ${tool.name} — ${e.message}`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
console.log();
|
|
287
|
+
console.log(green(" ✓ Tool configs updated."));
|
|
288
|
+
console.log();
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
// choice === "1" falls through to full setup below
|
|
292
|
+
console.log();
|
|
293
|
+
}
|
|
294
|
+
|
|
188
295
|
// Detect tools
|
|
189
296
|
console.log(dim(` [1/5]`) + bold(" Detecting tools...\n"));
|
|
190
297
|
const detected = [];
|
|
@@ -302,7 +409,7 @@ async function runSetup() {
|
|
|
302
409
|
console.log(`\n ${dim("[3/5]")}${bold(" Downloading embedding model...")}`);
|
|
303
410
|
console.log(dim(" all-MiniLM-L6-v2 (~22MB, one-time download)\n"));
|
|
304
411
|
try {
|
|
305
|
-
const { embed } = await import("
|
|
412
|
+
const { embed } = await import("@context-vault/core/index/embed");
|
|
306
413
|
await embed("warmup");
|
|
307
414
|
console.log(` ${green("+")} Embedding model ready`);
|
|
308
415
|
} catch (e) {
|
|
@@ -340,9 +447,9 @@ async function runSetup() {
|
|
|
340
447
|
}
|
|
341
448
|
|
|
342
449
|
// Seed entry
|
|
343
|
-
const seeded =
|
|
344
|
-
if (seeded) {
|
|
345
|
-
console.log(`\n ${green("+")} Created starter entry in vault`);
|
|
450
|
+
const seeded = createSeedEntries(resolvedVaultDir);
|
|
451
|
+
if (seeded > 0) {
|
|
452
|
+
console.log(`\n ${green("+")} Created ${seeded} starter ${seeded === 1 ? "entry" : "entries"} in vault`);
|
|
346
453
|
}
|
|
347
454
|
|
|
348
455
|
// Offer to launch UI
|
|
@@ -377,8 +484,15 @@ async function runSetup() {
|
|
|
377
484
|
const boxLines = [
|
|
378
485
|
` ✓ Setup complete — ${passed}/${checks.length} checks passed`,
|
|
379
486
|
``,
|
|
380
|
-
`
|
|
487
|
+
` ${bold("AI Tools")} — open ${toolName} and try:`,
|
|
381
488
|
` "Search my vault for getting started"`,
|
|
489
|
+
` "Save an insight about [topic]"`,
|
|
490
|
+
` "Show my vault status"`,
|
|
491
|
+
``,
|
|
492
|
+
` ${bold("CLI Commands:")}`,
|
|
493
|
+
` context-mcp status Show vault health`,
|
|
494
|
+
` context-mcp ui Launch web dashboard`,
|
|
495
|
+
` context-mcp update Check for updates`,
|
|
382
496
|
];
|
|
383
497
|
const innerWidth = Math.max(...boxLines.map((l) => l.length)) + 2;
|
|
384
498
|
const pad = (s) => s + " ".repeat(Math.max(0, innerWidth - s.length));
|
|
@@ -466,18 +580,21 @@ function configureJsonTool(tool, vaultDir) {
|
|
|
466
580
|
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
467
581
|
}
|
|
468
582
|
|
|
469
|
-
// ─── Seed
|
|
470
|
-
|
|
471
|
-
function
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
const
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
583
|
+
// ─── Seed Entries ────────────────────────────────────────────────────────────
|
|
584
|
+
|
|
585
|
+
function createSeedEntries(vaultDir) {
|
|
586
|
+
let created = 0;
|
|
587
|
+
|
|
588
|
+
// Entry 1: Getting started (improved)
|
|
589
|
+
const insightDir = join(vaultDir, "knowledge", "insights");
|
|
590
|
+
const insightPath = join(insightDir, "getting-started.md");
|
|
591
|
+
if (!existsSync(insightPath)) {
|
|
592
|
+
mkdirSync(insightDir, { recursive: true });
|
|
593
|
+
const id1 = Date.now().toString(36).toUpperCase().padStart(10, "0");
|
|
594
|
+
const now = new Date().toISOString();
|
|
595
|
+
writeFileSync(insightPath, `---
|
|
596
|
+
id: ${id1}
|
|
597
|
+
tags: ["getting-started", "vault"]
|
|
481
598
|
source: context-mcp-setup
|
|
482
599
|
created: ${now}
|
|
483
600
|
---
|
|
@@ -486,16 +603,48 @@ Welcome to your context vault! This is a seed entry created during setup.
|
|
|
486
603
|
Your vault stores knowledge as plain markdown files with YAML frontmatter.
|
|
487
604
|
AI agents search it using hybrid full-text + semantic search.
|
|
488
605
|
|
|
489
|
-
|
|
490
|
-
- "Search my vault for getting started"
|
|
491
|
-
- "Save an insight
|
|
492
|
-
- "Show my vault status"
|
|
606
|
+
**Quick start:**
|
|
607
|
+
- "Search my vault for getting started" — find this entry
|
|
608
|
+
- "Save an insight about [topic]" — add knowledge
|
|
609
|
+
- "Show my vault status" — check health
|
|
610
|
+
- "List my recent entries" — browse your vault
|
|
493
611
|
|
|
494
612
|
You can edit or delete this file anytime — it lives at:
|
|
495
|
-
${
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
613
|
+
${insightPath}
|
|
614
|
+
`);
|
|
615
|
+
created++;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// Entry 2: Example decision
|
|
619
|
+
const decisionDir = join(vaultDir, "knowledge", "decisions");
|
|
620
|
+
const decisionPath = join(decisionDir, "example-local-first-data.md");
|
|
621
|
+
if (!existsSync(decisionPath)) {
|
|
622
|
+
mkdirSync(decisionDir, { recursive: true });
|
|
623
|
+
const id2 = (Date.now() + 1).toString(36).toUpperCase().padStart(10, "0");
|
|
624
|
+
const now = new Date().toISOString();
|
|
625
|
+
writeFileSync(decisionPath, `---
|
|
626
|
+
id: ${id2}
|
|
627
|
+
tags: ["example", "architecture"]
|
|
628
|
+
source: context-mcp-setup
|
|
629
|
+
created: ${now}
|
|
630
|
+
---
|
|
631
|
+
Example decision: Use local-first data storage (SQLite + files) over cloud databases.
|
|
632
|
+
|
|
633
|
+
**Context:** For personal knowledge management, local storage provides better privacy,
|
|
634
|
+
offline access, and zero ongoing cost. The vault uses plain markdown files as the
|
|
635
|
+
source of truth with a SQLite index for fast search.
|
|
636
|
+
|
|
637
|
+
**Trade-offs:**
|
|
638
|
+
- Pro: Full data ownership, git-versioned, human-editable
|
|
639
|
+
- Pro: No cloud dependency, works offline
|
|
640
|
+
- Con: No built-in sync across devices (use git or Syncthing)
|
|
641
|
+
|
|
642
|
+
This is an example entry showing the decision format. Feel free to delete it.
|
|
643
|
+
`);
|
|
644
|
+
created++;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
return created;
|
|
499
648
|
}
|
|
500
649
|
|
|
501
650
|
// ─── UI Command ──────────────────────────────────────────────────────────────
|
|
@@ -517,10 +666,10 @@ function runUi() {
|
|
|
517
666
|
async function runReindex() {
|
|
518
667
|
console.log(dim("Loading vault..."));
|
|
519
668
|
|
|
520
|
-
const { resolveConfig } = await import("
|
|
521
|
-
const { initDatabase, prepareStatements, insertVec, deleteVec } = await import("
|
|
522
|
-
const { embed } = await import("
|
|
523
|
-
const { reindex } = await import("
|
|
669
|
+
const { resolveConfig } = await import("@context-vault/core/core/config");
|
|
670
|
+
const { initDatabase, prepareStatements, insertVec, deleteVec } = await import("@context-vault/core/index/db");
|
|
671
|
+
const { embed } = await import("@context-vault/core/index/embed");
|
|
672
|
+
const { reindex } = await import("@context-vault/core/index");
|
|
524
673
|
|
|
525
674
|
const config = resolveConfig();
|
|
526
675
|
if (!config.vaultDirExists) {
|
|
@@ -531,7 +680,7 @@ async function runReindex() {
|
|
|
531
680
|
process.exit(1);
|
|
532
681
|
}
|
|
533
682
|
|
|
534
|
-
const db = initDatabase(config.dbPath);
|
|
683
|
+
const db = await initDatabase(config.dbPath);
|
|
535
684
|
const stmts = prepareStatements(db);
|
|
536
685
|
const ctx = {
|
|
537
686
|
db,
|
|
@@ -555,12 +704,12 @@ async function runReindex() {
|
|
|
555
704
|
// ─── Status Command ──────────────────────────────────────────────────────────
|
|
556
705
|
|
|
557
706
|
async function runStatus() {
|
|
558
|
-
const { resolveConfig } = await import("
|
|
559
|
-
const { initDatabase } = await import("
|
|
560
|
-
const { gatherVaultStatus } = await import("
|
|
707
|
+
const { resolveConfig } = await import("@context-vault/core/core/config");
|
|
708
|
+
const { initDatabase } = await import("@context-vault/core/index/db");
|
|
709
|
+
const { gatherVaultStatus } = await import("@context-vault/core/core/status");
|
|
561
710
|
|
|
562
711
|
const config = resolveConfig();
|
|
563
|
-
const db = initDatabase(config.dbPath);
|
|
712
|
+
const db = await initDatabase(config.dbPath);
|
|
564
713
|
|
|
565
714
|
const status = gatherVaultStatus({ db, config });
|
|
566
715
|
|
|
@@ -620,6 +769,178 @@ async function runStatus() {
|
|
|
620
769
|
console.log();
|
|
621
770
|
}
|
|
622
771
|
|
|
772
|
+
// ─── Update Command ─────────────────────────────────────────────────────────
|
|
773
|
+
|
|
774
|
+
async function runUpdate() {
|
|
775
|
+
console.log();
|
|
776
|
+
console.log(` ${bold("◇ context-vault")} ${dim(`v${VERSION}`)}`);
|
|
777
|
+
console.log();
|
|
778
|
+
|
|
779
|
+
let latest;
|
|
780
|
+
try {
|
|
781
|
+
latest = execSync("npm view context-vault version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
782
|
+
} catch {
|
|
783
|
+
console.error(red(" Could not check for updates. Verify your network connection."));
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
if (latest === VERSION) {
|
|
788
|
+
console.log(green(" Already up to date."));
|
|
789
|
+
console.log();
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
console.log(` Current: ${dim(VERSION)}`);
|
|
794
|
+
console.log(` Latest: ${green(latest)}`);
|
|
795
|
+
console.log();
|
|
796
|
+
|
|
797
|
+
if (!isNonInteractive) {
|
|
798
|
+
const answer = await prompt(` Update to v${latest}? (Y/n):`, "Y");
|
|
799
|
+
if (answer.toLowerCase() === "n") {
|
|
800
|
+
console.log(dim(" Cancelled."));
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
console.log(dim(" Installing..."));
|
|
806
|
+
try {
|
|
807
|
+
execSync("npm install -g context-vault@latest", { stdio: "inherit" });
|
|
808
|
+
console.log();
|
|
809
|
+
console.log(green(` ✓ Updated to v${latest}`));
|
|
810
|
+
} catch {
|
|
811
|
+
console.error(red(" Update failed. Try manually: npm install -g context-vault@latest"));
|
|
812
|
+
}
|
|
813
|
+
console.log();
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
// ─── Uninstall Command ──────────────────────────────────────────────────────
|
|
817
|
+
|
|
818
|
+
async function runUninstall() {
|
|
819
|
+
console.log();
|
|
820
|
+
console.log(` ${bold("◇ context-vault")} ${dim("uninstall")}`);
|
|
821
|
+
console.log();
|
|
822
|
+
|
|
823
|
+
// Remove from Claude Code
|
|
824
|
+
try {
|
|
825
|
+
const env = { ...process.env };
|
|
826
|
+
delete env.CLAUDECODE;
|
|
827
|
+
execSync("claude mcp remove context-mcp -s user", { stdio: "pipe", env });
|
|
828
|
+
console.log(` ${green("+")} Removed from Claude Code`);
|
|
829
|
+
} catch {
|
|
830
|
+
console.log(` ${dim("-")} Claude Code — not configured or not installed`);
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
// Remove from JSON-configured tools
|
|
834
|
+
for (const tool of TOOLS.filter((t) => t.configType === "json")) {
|
|
835
|
+
if (!existsSync(tool.configPath)) continue;
|
|
836
|
+
try {
|
|
837
|
+
const config = JSON.parse(readFileSync(tool.configPath, "utf-8"));
|
|
838
|
+
if (config[tool.configKey]?.["context-mcp"]) {
|
|
839
|
+
delete config[tool.configKey]["context-mcp"];
|
|
840
|
+
writeFileSync(tool.configPath, JSON.stringify(config, null, 2) + "\n");
|
|
841
|
+
console.log(` ${green("+")} Removed from ${tool.name}`);
|
|
842
|
+
}
|
|
843
|
+
} catch {
|
|
844
|
+
console.log(` ${dim("-")} ${tool.name} — could not update config`);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// Optionally remove data directory
|
|
849
|
+
const dataDir = join(HOME, ".context-mcp");
|
|
850
|
+
if (existsSync(dataDir)) {
|
|
851
|
+
console.log();
|
|
852
|
+
const answer = isNonInteractive
|
|
853
|
+
? "n"
|
|
854
|
+
: await prompt(` Remove data directory (${dataDir})? (y/N):`, "N");
|
|
855
|
+
if (answer.toLowerCase() === "y") {
|
|
856
|
+
const { rmSync } = await import("node:fs");
|
|
857
|
+
rmSync(dataDir, { recursive: true, force: true });
|
|
858
|
+
console.log(` ${green("+")} Removed ${dataDir}`);
|
|
859
|
+
} else {
|
|
860
|
+
console.log(` ${dim("Kept")} ${dataDir}`);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
console.log();
|
|
865
|
+
console.log(dim(" Vault directory was not touched (your knowledge files are safe)."));
|
|
866
|
+
console.log(` To fully remove: ${cyan("npm uninstall -g context-vault")}`);
|
|
867
|
+
console.log();
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// ─── Migrate Command ─────────────────────────────────────────────────────────
|
|
871
|
+
|
|
872
|
+
async function runMigrate() {
|
|
873
|
+
const direction = args.includes("--to-hosted") ? "to-hosted"
|
|
874
|
+
: args.includes("--to-local") ? "to-local"
|
|
875
|
+
: null;
|
|
876
|
+
|
|
877
|
+
if (!direction) {
|
|
878
|
+
console.log(`\n ${bold("context-mcp migrate")}\n`);
|
|
879
|
+
console.log(` Usage:`);
|
|
880
|
+
console.log(` context-mcp migrate --to-hosted Upload local vault to hosted service`);
|
|
881
|
+
console.log(` context-mcp migrate --to-local Download hosted vault to local files`);
|
|
882
|
+
console.log(`\n Options:`);
|
|
883
|
+
console.log(` --url <url> Hosted server URL (default: https://vault.contextvault.dev)`);
|
|
884
|
+
console.log(` --key <key> API key (cv_...)`);
|
|
885
|
+
console.log();
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
const hostedUrl = getFlag("--url") || "https://vault.contextvault.dev";
|
|
890
|
+
const apiKey = getFlag("--key");
|
|
891
|
+
|
|
892
|
+
if (!apiKey) {
|
|
893
|
+
console.error(red(" Error: --key <api_key> is required for migration."));
|
|
894
|
+
console.error(` Get your API key at ${cyan(hostedUrl + "/dashboard")}`);
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
const { resolveConfig } = await import("@context-vault/core/core/config");
|
|
899
|
+
const config = resolveConfig();
|
|
900
|
+
|
|
901
|
+
if (direction === "to-hosted") {
|
|
902
|
+
const { migrateToHosted } = await import("@context-vault/hosted/migration/migrate");
|
|
903
|
+
console.log(`\n ${bold("Migrating to hosted")}...`);
|
|
904
|
+
console.log(dim(` Vault: ${config.vaultDir}`));
|
|
905
|
+
console.log(dim(` Target: ${hostedUrl}\n`));
|
|
906
|
+
|
|
907
|
+
const results = await migrateToHosted({
|
|
908
|
+
vaultDir: config.vaultDir,
|
|
909
|
+
hostedUrl,
|
|
910
|
+
apiKey,
|
|
911
|
+
log: (msg) => console.log(` ${dim(msg)}`),
|
|
912
|
+
});
|
|
913
|
+
|
|
914
|
+
console.log(`\n ${green("+")} ${results.uploaded} entries uploaded`);
|
|
915
|
+
if (results.failed > 0) {
|
|
916
|
+
console.log(` ${red("-")} ${results.failed} failed`);
|
|
917
|
+
for (const err of results.errors.slice(0, 5)) {
|
|
918
|
+
console.log(` ${dim(err)}`);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
console.log(dim("\n Your local vault was not modified (safe backup)."));
|
|
922
|
+
} else {
|
|
923
|
+
const { migrateToLocal } = await import("@context-vault/hosted/migration/migrate");
|
|
924
|
+
console.log(`\n ${bold("Migrating to local")}...`);
|
|
925
|
+
console.log(dim(` Source: ${hostedUrl}`));
|
|
926
|
+
console.log(dim(` Target: ${config.vaultDir}\n`));
|
|
927
|
+
|
|
928
|
+
const results = await migrateToLocal({
|
|
929
|
+
vaultDir: config.vaultDir,
|
|
930
|
+
hostedUrl,
|
|
931
|
+
apiKey,
|
|
932
|
+
log: (msg) => console.log(` ${dim(msg)}`),
|
|
933
|
+
});
|
|
934
|
+
|
|
935
|
+
console.log(`\n ${green("+")} ${results.downloaded} entries restored`);
|
|
936
|
+
if (results.failed > 0) {
|
|
937
|
+
console.log(` ${red("-")} ${results.failed} failed`);
|
|
938
|
+
}
|
|
939
|
+
console.log(dim("\n Run `context-mcp reindex` to rebuild the search index."));
|
|
940
|
+
}
|
|
941
|
+
console.log();
|
|
942
|
+
}
|
|
943
|
+
|
|
623
944
|
// ─── Serve Command ──────────────────────────────────────────────────────────
|
|
624
945
|
|
|
625
946
|
async function runServe() {
|
|
@@ -659,6 +980,15 @@ async function main() {
|
|
|
659
980
|
case "status":
|
|
660
981
|
await runStatus();
|
|
661
982
|
break;
|
|
983
|
+
case "update":
|
|
984
|
+
await runUpdate();
|
|
985
|
+
break;
|
|
986
|
+
case "uninstall":
|
|
987
|
+
await runUninstall();
|
|
988
|
+
break;
|
|
989
|
+
case "migrate":
|
|
990
|
+
await runMigrate();
|
|
991
|
+
break;
|
|
662
992
|
default:
|
|
663
993
|
console.error(red(`Unknown command: ${command}`));
|
|
664
994
|
console.error(`Run ${cyan("context-mcp --help")} for usage.`);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Felix Hellstrom
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@context-vault/core",
|
|
3
|
+
"version": "2.4.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": "git+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
|
+
}
|
|
@@ -15,7 +15,7 @@ import { parseFrontmatter, formatFrontmatter } from "../core/frontmatter.js";
|
|
|
15
15
|
import { formatBody } from "./formatters.js";
|
|
16
16
|
import { writeEntryFile } from "./file-ops.js";
|
|
17
17
|
|
|
18
|
-
export function writeEntry(ctx, { kind, title, body, meta, tags, source, folder, identity_key, expires_at }) {
|
|
18
|
+
export function writeEntry(ctx, { kind, title, body, meta, tags, source, folder, identity_key, expires_at, userId }) {
|
|
19
19
|
if (!kind || typeof kind !== "string") {
|
|
20
20
|
throw new Error("writeEntry: kind is required (non-empty string)");
|
|
21
21
|
}
|
|
@@ -59,7 +59,7 @@ export function writeEntry(ctx, { kind, title, body, meta, tags, source, folder,
|
|
|
59
59
|
category, identity_key, expires_at,
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
-
return { id, filePath, kind, category, title, body, meta, tags, source, createdAt, identity_key, expires_at };
|
|
62
|
+
return { id, filePath, kind, category, title, body, meta, tags, source, createdAt, identity_key, expires_at, userId: userId || null };
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
/**
|
|
@@ -123,6 +123,7 @@ export function updateEntryFile(ctx, existing, updates) {
|
|
|
123
123
|
createdAt: fmMeta.created || existing.created_at,
|
|
124
124
|
identity_key: existing.identity_key,
|
|
125
125
|
expires_at,
|
|
126
|
+
userId: existing.user_id || null,
|
|
126
127
|
};
|
|
127
128
|
}
|
|
128
129
|
|