context-vault 3.1.0 → 3.1.2

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/README.md CHANGED
@@ -10,13 +10,16 @@ Persistent memory for AI agents — saves and searches knowledge across sessions
10
10
  ## Quick Start
11
11
 
12
12
  ```bash
13
- npx context-vault
13
+ npm install -g context-vault
14
+ context-vault setup
14
15
  ```
15
16
 
16
- One command — no global install required. Setup detects your AI tools (Claude Code, Codex, Claude Desktop, Cursor, Windsurf, Cline, and more), downloads the embedding model (~22MB), seeds your vault, and configures MCP.
17
+ Setup detects your AI tools (Claude Code, Codex, Claude Desktop, Cursor, Windsurf, Cline, and more), downloads the embedding model (~22MB), seeds your vault, and configures MCP.
17
18
 
18
19
  Then open your AI tool and try: **"Search my vault for getting started"**
19
20
 
21
+ > **Quick try without installing?** `npx context-vault` works too, but a global install is recommended for reliable MCP server startup.
22
+
20
23
  ## What It Does
21
24
 
22
25
  - **Save** — insights, decisions, patterns, contacts. Your AI agent writes them as you work.
@@ -73,7 +76,7 @@ Claude Code exposes shell hooks that fire on session events. context-vault integ
73
76
  "hooks": [
74
77
  {
75
78
  "type": "command",
76
- "command": "npx context-vault flush",
79
+ "command": "context-vault flush",
77
80
  "timeout": 10
78
81
  }
79
82
  ]
package/bin/cli.js CHANGED
@@ -375,30 +375,49 @@ async function runSetup() {
375
375
  } catch {}
376
376
 
377
377
  if (latestVersion === VERSION) {
378
- // Even when "up to date", ensure the launcher points to a valid server
379
- const dataDir = join(HOME, ".context-mcp");
380
- const launcherPath = join(dataDir, "server.mjs");
381
- let launcherOk = false;
382
- if (existsSync(launcherPath)) {
383
- const content = readFileSync(launcherPath, "utf-8");
384
- const m = content.match(/import "(.+?)"/);
385
- if (m && existsSync(m[1])) launcherOk = true;
386
- }
387
- if (!launcherOk && !isNpx()) {
388
- mkdirSync(dataDir, { recursive: true });
389
- writeFileSync(launcherPath, `import "${SERVER_PATH}";\n`);
390
- console.log(
391
- green(` ✓ context-vault v${VERSION} is up to date`) +
392
- dim(` (vault: ${existingVault})`),
393
- );
394
- console.log(dim(` ↳ Repaired server launcher → ${SERVER_PATH}`));
395
- console.log();
396
- return;
397
- }
398
378
  console.log(
399
379
  green(` ✓ context-vault v${VERSION} is up to date`) +
400
380
  dim(` (vault: ${existingVault})`),
401
381
  );
382
+
383
+ // Check for stale tool configs using hardcoded node paths
384
+ const staleConfigs = findStaleToolConfigs();
385
+ if (staleConfigs.length > 0) {
386
+ console.log();
387
+ console.log(
388
+ yellow(` ! ${staleConfigs.length} tool config(s) using legacy hardcoded paths`),
389
+ );
390
+ for (const s of staleConfigs) {
391
+ console.log(dim(` ${s.name}: ${s.command}`));
392
+ }
393
+ console.log();
394
+ const fix = await prompt(
395
+ ` Auto-fix to use context-vault binary? (Y/n):`,
396
+ "Y",
397
+ );
398
+ if (fix.toLowerCase() !== "n") {
399
+ let customVaultDir = null;
400
+ try {
401
+ const cfg = JSON.parse(readFileSync(existingConfig, "utf-8"));
402
+ const defaultVDir = join(HOME, "vault");
403
+ if (cfg.vaultDir && resolve(cfg.vaultDir) !== resolve(defaultVDir)) {
404
+ customVaultDir = cfg.vaultDir;
405
+ }
406
+ } catch {}
407
+ for (const s of staleConfigs) {
408
+ try {
409
+ repairToolConfig(s, customVaultDir);
410
+ console.log(` ${green("+")} ${s.name} — fixed`);
411
+ } catch (e) {
412
+ console.log(` ${red("x")} ${s.name} — ${e.message}`);
413
+ }
414
+ }
415
+ console.log();
416
+ console.log(green(" ✓ Tool configs updated."));
417
+ console.log(dim(" Restart your AI tools to apply the changes."));
418
+ }
419
+ }
420
+
402
421
  console.log();
403
422
  return;
404
423
  }
@@ -513,12 +532,12 @@ async function runSetup() {
513
532
  if (detected.length === 0) {
514
533
  console.log(yellow(" No supported tools detected.\n"));
515
534
  console.log(" To manually configure, add to your tool's MCP config:\n");
516
- if (isInstalledPackage()) {
535
+ if (isInstalledPackage() || isNpx()) {
517
536
  console.log(` ${dim("{")}
518
537
  ${dim('"mcpServers": {')}
519
538
  ${dim('"context-vault": {')}
520
- ${dim('"command": "npx",')}
521
- ${dim(`"args": ["-y", "context-vault", "serve", "--vault-dir", "/path/to/vault"]`)}
539
+ ${dim('"command": "context-vault",')}
540
+ ${dim(`"args": ["serve", "--vault-dir", "/path/to/vault"]`)}
522
541
  ${dim("}")}
523
542
  ${dim("}")}
524
543
  ${dim("}")}\n`);
@@ -665,11 +684,6 @@ async function runSetup() {
665
684
  const dataDir = join(HOME, ".context-mcp");
666
685
  mkdirSync(dataDir, { recursive: true });
667
686
 
668
- // Keep server.mjs launcher up to date so it always resolves to the current installation
669
- if (isInstalledPackage()) {
670
- writeFileSync(join(dataDir, "server.mjs"), `import "${SERVER_PATH}";\n`);
671
- }
672
-
673
687
  // Write config.json to data dir (persistent, survives reinstalls)
674
688
  const configPath = join(dataDir, "config.json");
675
689
  const vaultConfig = {};
@@ -1137,11 +1151,25 @@ async function configureClaude(tool, vaultDir) {
1137
1151
  ],
1138
1152
  { stdio: "pipe", env },
1139
1153
  );
1154
+ } else if (isInstalledPackage()) {
1155
+ const serverArgs = ["serve"];
1156
+ if (vaultDir) serverArgs.push("--vault-dir", vaultDir);
1157
+ execFileSync(
1158
+ "claude",
1159
+ [
1160
+ "mcp",
1161
+ "add",
1162
+ "-s",
1163
+ "user",
1164
+ "context-vault",
1165
+ "--",
1166
+ "context-vault",
1167
+ ...serverArgs,
1168
+ ],
1169
+ { stdio: "pipe", env },
1170
+ );
1140
1171
  } else {
1141
- const serverPath = isInstalledPackage()
1142
- ? join(HOME, ".context-mcp", "server.mjs")
1143
- : SERVER_PATH;
1144
- const nodeArgs = [serverPath];
1172
+ const nodeArgs = [SERVER_PATH];
1145
1173
  if (vaultDir) nodeArgs.push("--vault-dir", vaultDir);
1146
1174
  execFileSync(
1147
1175
  "claude",
@@ -1183,11 +1211,16 @@ async function configureCodex(tool, vaultDir) {
1183
1211
  ["mcp", "add", "context-vault", "--", "npx", ...serverArgs],
1184
1212
  { stdio: "pipe" },
1185
1213
  );
1214
+ } else if (isInstalledPackage()) {
1215
+ const serverArgs = ["serve"];
1216
+ if (vaultDir) serverArgs.push("--vault-dir", vaultDir);
1217
+ execFileSync(
1218
+ "codex",
1219
+ ["mcp", "add", "context-vault", "--", "context-vault", ...serverArgs],
1220
+ { stdio: "pipe" },
1221
+ );
1186
1222
  } else {
1187
- const serverPath = isInstalledPackage()
1188
- ? join(HOME, ".context-mcp", "server.mjs")
1189
- : SERVER_PATH;
1190
- const nodeArgs = [serverPath];
1223
+ const nodeArgs = [SERVER_PATH];
1191
1224
  if (vaultDir) nodeArgs.push("--vault-dir", vaultDir);
1192
1225
  execFileSync(
1193
1226
  "codex",
@@ -1201,6 +1234,77 @@ async function configureCodex(tool, vaultDir) {
1201
1234
  }
1202
1235
  }
1203
1236
 
1237
+ function findStaleToolConfigs() {
1238
+ const stale = [];
1239
+
1240
+ // Check Claude Code (~/.claude.json)
1241
+ const claudePath = join(HOME, ".claude.json");
1242
+ if (existsSync(claudePath)) {
1243
+ try {
1244
+ const cfg = JSON.parse(readFileSync(claudePath, "utf-8"));
1245
+ const srv = cfg?.mcpServers?.["context-vault"];
1246
+ if (srv && srv.command !== "context-vault" && srv.command !== "npx") {
1247
+ stale.push({
1248
+ name: "Claude Code",
1249
+ id: "claude-code",
1250
+ configType: "cli",
1251
+ configPath: claudePath,
1252
+ command: [srv.command, ...(srv.args || [])].join(" "),
1253
+ });
1254
+ }
1255
+ } catch {}
1256
+ }
1257
+
1258
+ // Check JSON-configured tools
1259
+ for (const tool of TOOLS.filter((t) => t.configType === "json")) {
1260
+ const cfgPath = tool.configPath;
1261
+ if (!cfgPath || !existsSync(cfgPath)) continue;
1262
+ try {
1263
+ const cfg = JSON.parse(readFileSync(cfgPath, "utf-8"));
1264
+ const srv = cfg?.[tool.configKey]?.["context-vault"];
1265
+ if (srv && srv.command !== "context-vault" && srv.command !== "npx") {
1266
+ stale.push({
1267
+ name: tool.name,
1268
+ id: tool.id,
1269
+ configType: "json",
1270
+ configPath: cfgPath,
1271
+ configKey: tool.configKey,
1272
+ command: [srv.command, ...(srv.args || [])].join(" "),
1273
+ });
1274
+ }
1275
+ } catch {}
1276
+ }
1277
+
1278
+ return stale;
1279
+ }
1280
+
1281
+ function repairToolConfig(staleEntry, vaultDir) {
1282
+ const serverArgs = ["serve"];
1283
+ if (vaultDir) serverArgs.push("--vault-dir", vaultDir);
1284
+ const newConfig = { command: "context-vault", args: serverArgs };
1285
+
1286
+ if (staleEntry.id === "claude-code") {
1287
+ // Use `claude mcp remove` + `claude mcp add`
1288
+ try {
1289
+ execFileSync("claude", ["mcp", "remove", "context-vault"], {
1290
+ stdio: "pipe",
1291
+ });
1292
+ } catch {}
1293
+ execFileSync(
1294
+ "claude",
1295
+ ["mcp", "add", "context-vault", "--", "context-vault", ...serverArgs],
1296
+ { stdio: "pipe" },
1297
+ );
1298
+ return;
1299
+ }
1300
+
1301
+ // JSON config tools
1302
+ const cfgPath = staleEntry.configPath;
1303
+ const cfg = JSON.parse(readFileSync(cfgPath, "utf-8"));
1304
+ cfg[staleEntry.configKey]["context-vault"] = newConfig;
1305
+ writeFileSync(cfgPath, JSON.stringify(cfg, null, 2) + "\n");
1306
+ }
1307
+
1204
1308
  function configureJsonTool(tool, vaultDir) {
1205
1309
  const configPath = tool.configPath;
1206
1310
  const configDir = dirname(configPath);
@@ -1237,13 +1341,11 @@ function configureJsonTool(tool, vaultDir) {
1237
1341
  env: { NODE_OPTIONS: "--no-warnings=ExperimentalWarning" },
1238
1342
  };
1239
1343
  } else if (isInstalledPackage()) {
1240
- const launcherPath = join(HOME, ".context-mcp", "server.mjs");
1241
- const serverArgs = [];
1344
+ const serverArgs = ["serve"];
1242
1345
  if (vaultDir) serverArgs.push("--vault-dir", vaultDir);
1243
1346
  config[tool.configKey]["context-vault"] = {
1244
- command: process.execPath,
1245
- args: [launcherPath, ...serverArgs],
1246
- env: { NODE_OPTIONS: "--no-warnings=ExperimentalWarning" },
1347
+ command: "context-vault",
1348
+ args: serverArgs,
1247
1349
  };
1248
1350
  } else {
1249
1351
  const serverArgs = [SERVER_PATH];
@@ -1598,13 +1700,6 @@ async function runSwitch() {
1598
1700
  const { detected } = await detectAllTools();
1599
1701
 
1600
1702
  if (target === "local") {
1601
- const launcherPath = join(dataDir, "server.mjs");
1602
- if (!existsSync(launcherPath)) {
1603
- const serverAbs = resolve(ROOT, "src", "server.js");
1604
- mkdirSync(dataDir, { recursive: true });
1605
- writeFileSync(launcherPath, `import "${serverAbs}";\n`);
1606
- }
1607
-
1608
1703
  vaultConfig.mode = "local";
1609
1704
  mkdirSync(dataDir, { recursive: true });
1610
1705
  writeFileSync(configPath, JSON.stringify(vaultConfig, null, 2) + "\n");
@@ -1636,7 +1731,7 @@ async function runSwitch() {
1636
1731
  }
1637
1732
  console.log();
1638
1733
  console.log(green(" ✓ Switched to local mode."));
1639
- console.log(dim(` Server: node ${launcherPath}`));
1734
+ console.log(dim(` Server: context-vault serve`));
1640
1735
  console.log();
1641
1736
  } else {
1642
1737
  const hostedUrl = getFlag("--url") || vaultConfig.hostedUrl || API_URL;
@@ -2044,8 +2139,7 @@ async function runStatus() {
2044
2139
  const email = raw.email ? ` · ${raw.email}` : "";
2045
2140
  modeDetail = ` (${raw.hostedUrl}${email})`;
2046
2141
  } else {
2047
- const launcherPath = join(HOME, ".context-mcp", "server.mjs");
2048
- modeDetail = ` (node ${launcherPath})`;
2142
+ modeDetail = ` (context-vault serve)`;
2049
2143
  }
2050
2144
  } catch {}
2051
2145
  }
@@ -4687,35 +4781,34 @@ async function runDoctor() {
4687
4781
  db?.close();
4688
4782
  } catch {}
4689
4783
 
4690
- // ── Launcher (server.mjs) ─────────────────────────────────────────────
4691
- const launcherPath = join(HOME, ".context-mcp", "server.mjs");
4692
- if (existsSync(launcherPath)) {
4693
- const launcherContent = readFileSync(launcherPath, "utf-8");
4694
- const match = launcherContent.match(/import "(.+?)"/);
4695
- if (match) {
4696
- const serverEntryPath = match[1];
4697
- if (existsSync(serverEntryPath)) {
4698
- console.log(
4699
- ` ${green("✓")} Launcher ${dim(`→ ${serverEntryPath}`)}`,
4700
- );
4701
- } else {
4702
- console.log(
4703
- ` ${red("✘")} Launcher points to missing server: ${serverEntryPath}`,
4704
- );
4705
- console.log(
4706
- ` ${dim("Fix: run context-vault setup to reinstall")}`,
4707
- );
4708
- allOk = false;
4709
- }
4710
- } else {
4711
- console.log(` ${green("✓")} Launcher exists ${dim(launcherPath)}`);
4712
- }
4713
- } else {
4714
- console.log(` ${yellow("!")} Launcher not found at ${launcherPath}`);
4715
- console.log(` ${dim("Fix: run context-vault setup")}`);
4784
+ // ── CLI binary ──────────────────────────────────────────────────────
4785
+ try {
4786
+ const binVersion = execSync("context-vault --version", {
4787
+ encoding: "utf-8",
4788
+ timeout: 5000,
4789
+ }).trim();
4790
+ console.log(
4791
+ ` ${green("✓")} CLI binary ${dim(`(${binVersion})`)}`,
4792
+ );
4793
+ } catch {
4794
+ console.log(
4795
+ ` ${red("✘")} CLI binary not found in PATH`,
4796
+ );
4797
+ console.log(
4798
+ ` ${dim("Fix: npm install -g context-vault")}`,
4799
+ );
4716
4800
  allOk = false;
4717
4801
  }
4718
4802
 
4803
+ // Clean up legacy launcher if it exists
4804
+ const legacyLauncher = join(HOME, ".context-mcp", "server.mjs");
4805
+ if (existsSync(legacyLauncher)) {
4806
+ try {
4807
+ unlinkSync(legacyLauncher);
4808
+ console.log(` ${green("✓")} Removed legacy launcher ${dim(legacyLauncher)}`);
4809
+ } catch {}
4810
+ }
4811
+
4719
4812
  // ── Error log ─────────────────────────────────────────────────────────
4720
4813
  const logPath = errorLogPath(config.dataDir);
4721
4814
  const logCount = errorLogCount(config.dataDir);
@@ -4746,6 +4839,8 @@ async function runDoctor() {
4746
4839
  console.log(bold(" Tool Configurations"));
4747
4840
  let anyToolConfigured = false;
4748
4841
 
4842
+ const isStaleCmd = (cmd) => cmd !== "context-vault" && cmd !== "npx";
4843
+
4749
4844
  // Check Claude Code
4750
4845
  const claudeConfigPath = join(HOME, ".claude.json");
4751
4846
  if (existsSync(claudeConfigPath)) {
@@ -4755,7 +4850,13 @@ async function runDoctor() {
4755
4850
  if (servers["context-vault"]) {
4756
4851
  const srv = servers["context-vault"];
4757
4852
  const cmd = [srv.command, ...(srv.args || [])].join(" ");
4758
- console.log(` ${green("+")} Claude Code: ${dim(cmd)}`);
4853
+ if (isStaleCmd(srv.command)) {
4854
+ console.log(` ${yellow("!")} Claude Code: ${dim(cmd)}`);
4855
+ console.log(` ${dim("Fix: run context-vault setup to update")}`);
4856
+ allOk = false;
4857
+ } else {
4858
+ console.log(` ${green("+")} Claude Code: ${dim(cmd)}`);
4859
+ }
4759
4860
  anyToolConfigured = true;
4760
4861
  } else {
4761
4862
  console.log(` ${dim("-")} Claude Code: not configured`);
@@ -4781,7 +4882,13 @@ async function runDoctor() {
4781
4882
  if (servers["context-vault"]) {
4782
4883
  const srv = servers["context-vault"];
4783
4884
  const cmd = [srv.command, ...(srv.args || [])].join(" ");
4784
- console.log(` ${green("+")} ${tool.name}: ${dim(cmd)}`);
4885
+ if (isStaleCmd(srv.command)) {
4886
+ console.log(` ${yellow("!")} ${tool.name}: ${dim(cmd)}`);
4887
+ console.log(` ${dim("Fix: run context-vault setup to update")}`);
4888
+ allOk = false;
4889
+ } else {
4890
+ console.log(` ${green("+")} ${tool.name}: ${dim(cmd)}`);
4891
+ }
4785
4892
  anyToolConfigured = true;
4786
4893
  } else if (servers["context-mcp"]) {
4787
4894
  console.log(
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@context-vault/core",
3
- "version": "3.1.0",
3
+ "version": "3.1.2",
4
4
  "type": "module",
5
5
  "description": "Pure local engine: capture, index, search, and utilities for context-vault",
6
6
  "main": "dist/main.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "context-vault",
3
- "version": "3.1.0",
3
+ "version": "3.1.2",
4
4
  "type": "module",
5
5
  "description": "Persistent memory for AI agents — saves and searches knowledge across sessions",
6
6
  "bin": {
@@ -57,7 +57,7 @@
57
57
  "@context-vault/core"
58
58
  ],
59
59
  "dependencies": {
60
- "@context-vault/core": "^3.1.0",
60
+ "@context-vault/core": "^3.1.2",
61
61
  "@modelcontextprotocol/sdk": "^1.26.0",
62
62
  "adm-zip": "^0.5.16",
63
63
  "sqlite-vec": "^0.1.0"
@@ -6,11 +6,11 @@
6
6
  * 1. Installs @huggingface/transformers with --ignore-scripts to avoid sharp's
7
7
  * broken install lifecycle in global contexts. Semantic search degrades
8
8
  * gracefully if this step fails.
9
- * 2. Writes local server launcher (global installs only).
9
+ * 2. Ensures data directory exists.
10
10
  */
11
11
 
12
12
  import { execSync } from "node:child_process";
13
- import { existsSync, writeFileSync, mkdirSync } from "node:fs";
13
+ import { existsSync, mkdirSync } from "node:fs";
14
14
  import { join, dirname } from "node:path";
15
15
  import { fileURLToPath } from "node:url";
16
16
  import { homedir } from "node:os";
@@ -51,18 +51,9 @@ async function main() {
51
51
  }
52
52
  }
53
53
 
54
- // ── 2. Write local server launcher (global installs only) ────────────
55
- // Under npx the path would be stale after cache eviction — configs use
56
- // `npx context-vault serve` instead, so skip writing the launcher.
57
- const isNpx = PKG_ROOT.includes("/_npx/") || PKG_ROOT.includes("\\_npx\\");
58
- if (!isNpx) {
59
- const SERVER_ABS = join(PKG_ROOT, "src", "server.js");
60
- const DATA_DIR = join(homedir(), ".context-mcp");
61
- const LAUNCHER = join(DATA_DIR, "server.mjs");
62
- mkdirSync(DATA_DIR, { recursive: true });
63
- writeFileSync(LAUNCHER, `import "${SERVER_ABS}";\n`);
64
- console.log("[context-vault] Local server launcher written to " + LAUNCHER);
65
- }
54
+ // ── 2. Ensure data dir exists ────────────────────────────────────────
55
+ const DATA_DIR = join(homedir(), ".context-mcp");
56
+ mkdirSync(DATA_DIR, { recursive: true });
66
57
  }
67
58
 
68
59
  main().catch(() => {});