context-mode 0.9.9 → 0.9.11

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.
@@ -13,7 +13,7 @@
13
13
  "name": "context-mode",
14
14
  "source": "./",
15
15
  "description": "Claude Code MCP plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
16
- "version": "0.9.9",
16
+ "version": "0.9.11",
17
17
  "author": {
18
18
  "name": "Mert Koseoğlu"
19
19
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "context-mode",
3
- "version": "0.9.9",
3
+ "version": "0.9.11",
4
4
  "description": "Claude Code MCP plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
5
5
  "author": {
6
6
  "name": "Mert Koseoğlu",
package/build/cli.js CHANGED
@@ -390,11 +390,13 @@ async function upgrade() {
390
390
  timeout: 30000,
391
391
  });
392
392
  s.stop("Built successfully");
393
- // Step 3: Clean old cache dirs (not our own - we're running from it), then install fresh
393
+ // Step 3: Update in-place (same directory, no registry changes needed)
394
+ s.start("Updating files in-place");
395
+ // Clean stale version dirs from previous upgrade attempts
394
396
  const cacheParentMatch = pluginRoot.match(/^(.*[\\/]plugins[\\/]cache[\\/][^\\/]+[\\/][^\\/]+[\\/])/);
395
397
  if (cacheParentMatch) {
396
398
  const cacheParent = cacheParentMatch[1];
397
- const myDir = pluginRoot.replace(cacheParent, "");
399
+ const myDir = pluginRoot.replace(cacheParent, "").replace(/[\\/]/g, "");
398
400
  try {
399
401
  const oldDirs = readdirSync(cacheParent).filter(d => d !== myDir);
400
402
  for (const d of oldDirs) {
@@ -404,37 +406,25 @@ async function upgrade() {
404
406
  catch { /* skip */ }
405
407
  }
406
408
  if (oldDirs.length > 0) {
407
- p.log.info(color.dim(` Cleaned ${oldDirs.length} old cache dir(s)`));
409
+ p.log.info(color.dim(` Cleaned ${oldDirs.length} stale cache dir(s)`));
408
410
  }
409
411
  }
410
412
  catch { /* parent may not exist */ }
411
413
  }
412
- s.start("Installing fresh");
413
- const freshDir = cacheParentMatch
414
- ? resolve(cacheParentMatch[1], newVersion)
415
- : pluginRoot;
416
- // If fresh dir is different from current, create it. Otherwise update in-place.
417
- if (freshDir !== pluginRoot) {
418
- rmSync(freshDir, { recursive: true, force: true });
419
- cpSync(srcDir, freshDir, { recursive: true });
420
- pluginRoot = freshDir;
421
- }
422
- else {
423
- // Same dir — copy files in-place
424
- const items = [
425
- "build", "src", "hooks", "skills", ".claude-plugin",
426
- "start.mjs", "server.bundle.mjs", "package.json", ".mcp.json",
427
- ];
428
- for (const item of items) {
429
- try {
430
- rmSync(resolve(pluginRoot, item), { recursive: true, force: true });
431
- cpSync(resolve(srcDir, item), resolve(pluginRoot, item), { recursive: true });
432
- }
433
- catch { /* some files may not exist */ }
414
+ // Copy new files over old ones — same path, no registry update needed
415
+ const items = [
416
+ "build", "src", "hooks", "skills", ".claude-plugin",
417
+ "start.mjs", "server.bundle.mjs", "package.json", ".mcp.json",
418
+ ];
419
+ for (const item of items) {
420
+ try {
421
+ rmSync(resolve(pluginRoot, item), { recursive: true, force: true });
422
+ cpSync(resolve(srcDir, item), resolve(pluginRoot, item), { recursive: true });
434
423
  }
424
+ catch { /* some files may not exist in source */ }
435
425
  }
436
- s.stop(color.green(`Installed to ${freshDir.split("/").pop() ?? newVersion}`));
437
- // Install production deps in fresh dir
426
+ s.stop(color.green(`Updated in-place to v${newVersion}`));
427
+ // Install production deps (rebuild native modules if needed)
438
428
  s.start("Installing production dependencies");
439
429
  execSync("npm install --production --no-audit --no-fund", {
440
430
  cwd: pluginRoot,
@@ -442,58 +432,6 @@ async function upgrade() {
442
432
  timeout: 60000,
443
433
  });
444
434
  s.stop("Dependencies ready");
445
- // Step 4: Update installed_plugins.json via spawned script (works from any old version)
446
- s.start("Updating plugin registry");
447
- try {
448
- const scriptPath = resolve(tmpDir + "-post-upgrade.mjs");
449
- const scriptContent = [
450
- `import { readFileSync, writeFileSync } from "fs";`,
451
- `import { resolve } from "path";`,
452
- `import { homedir } from "os";`,
453
- `const pluginRoot = ${JSON.stringify(pluginRoot)};`,
454
- `const newVersion = ${JSON.stringify(newVersion)};`,
455
- `const ipPath = resolve(homedir(), ".claude", "plugins", "installed_plugins.json");`,
456
- `try {`,
457
- ` const ipRaw = JSON.parse(readFileSync(ipPath, "utf-8"));`,
458
- ` const plugins = ipRaw.plugins || {};`,
459
- ` let updated = false;`,
460
- ` for (const [key, entries] of Object.entries(plugins)) {`,
461
- ` if (!key.toLowerCase().includes("context-mode")) continue;`,
462
- ` for (const entry of entries) {`,
463
- ` entry.installPath = pluginRoot;`,
464
- ` entry.version = newVersion;`,
465
- ` entry.lastUpdated = new Date().toISOString();`,
466
- ` updated = true;`,
467
- ` }`,
468
- ` }`,
469
- ` if (updated) {`,
470
- ` writeFileSync(ipPath, JSON.stringify(ipRaw, null, 2) + "\\n", "utf-8");`,
471
- ` console.log("REGISTRY_UPDATED");`,
472
- ` }`,
473
- `} catch (e) { console.error("REGISTRY_FAILED:" + e.message); }`,
474
- ].join("\n");
475
- writeFileSync(scriptPath, scriptContent, "utf-8");
476
- const result = execSync(`node ${scriptPath}`, {
477
- stdio: "pipe",
478
- timeout: 10000,
479
- encoding: "utf-8",
480
- });
481
- try {
482
- rmSync(scriptPath, { force: true });
483
- }
484
- catch { /* ignore */ }
485
- if (result.includes("REGISTRY_UPDATED")) {
486
- s.stop(color.green("Plugin registry updated"));
487
- changes.push("Updated plugin registry");
488
- }
489
- else {
490
- s.stop(color.yellow("Plugin registry unchanged"));
491
- }
492
- }
493
- catch (err) {
494
- s.stop(color.yellow("Plugin registry update skipped"));
495
- p.log.warn(color.yellow("Could not update plugin registry") + color.dim(` — ${err instanceof Error ? err.message : String(err)}`));
496
- }
497
435
  // Update global npm package from same GitHub source
498
436
  s.start("Updating npm global package");
499
437
  try {
@@ -608,12 +546,11 @@ async function upgrade() {
608
546
  else {
609
547
  p.log.info(color.dim("No changes were needed."));
610
548
  }
611
- // Step 7: Run doctor from NEW pluginRoot (not old __dirname)
549
+ // Step 7: Run doctor from updated pluginRoot
612
550
  p.log.step("Running doctor to verify...");
613
551
  console.log();
614
552
  try {
615
- const doctorScript = resolve(pluginRoot, "build", "cli.js");
616
- execSync(`node "${doctorScript}" doctor`, {
553
+ execSync(`node "${resolve(pluginRoot, "build", "cli.js")}" doctor`, {
617
554
  stdio: "inherit",
618
555
  timeout: 30000,
619
556
  cwd: pluginRoot,
@@ -6,6 +6,71 @@
6
6
  * Cross-platform (Windows/macOS/Linux) — no bash/jq dependency.
7
7
  */
8
8
 
9
+ import { readFileSync, writeFileSync, existsSync, rmSync, cpSync, readdirSync } from "node:fs";
10
+ import { resolve, dirname, basename } from "node:path";
11
+ import { fileURLToPath } from "node:url";
12
+ import { homedir, tmpdir } from "node:os";
13
+
14
+ // ─── Self-heal: copy new code to registry's installPath + nuke stale cache ───
15
+ try {
16
+ const hookDir = dirname(fileURLToPath(import.meta.url));
17
+ const myRoot = resolve(hookDir, "..");
18
+ const myPkg = JSON.parse(readFileSync(resolve(myRoot, "package.json"), "utf-8"));
19
+ const myVersion = myPkg.version ?? "unknown";
20
+ const marker = resolve(tmpdir(), `context-mode-healed-${myVersion}`);
21
+
22
+ if (myVersion !== "unknown" && !existsSync(marker)) {
23
+ const ipPath = resolve(homedir(), ".claude", "plugins", "installed_plugins.json");
24
+ const ip = JSON.parse(readFileSync(ipPath, "utf-8"));
25
+
26
+ for (const [key, entries] of Object.entries(ip.plugins || {})) {
27
+ if (!key.toLowerCase().includes("context-mode")) continue;
28
+ for (const entry of entries) {
29
+ const regPath = entry.installPath;
30
+
31
+ // Copy new code to registry's installPath if it's a different dir
32
+ if (regPath && regPath !== myRoot && existsSync(regPath)) {
33
+ const items = [
34
+ "build", "hooks", "skills", ".claude-plugin",
35
+ "start.mjs", "server.bundle.mjs", "package.json", ".mcp.json",
36
+ ];
37
+ for (const item of items) {
38
+ const src = resolve(myRoot, item);
39
+ if (existsSync(src)) {
40
+ try {
41
+ rmSync(resolve(regPath, item), { recursive: true, force: true });
42
+ cpSync(src, resolve(regPath, item), { recursive: true });
43
+ } catch { /* skip */ }
44
+ }
45
+ }
46
+ }
47
+
48
+ // Update version in registry
49
+ if (entry.version !== myVersion) {
50
+ entry.version = myVersion;
51
+ entry.lastUpdated = new Date().toISOString();
52
+ }
53
+
54
+ // Nuke all stale version dirs (keep only registry's installPath)
55
+ if (regPath) {
56
+ const cacheParent = dirname(regPath);
57
+ const keepDir = basename(regPath);
58
+ try {
59
+ for (const d of readdirSync(cacheParent)) {
60
+ if (d !== keepDir) {
61
+ try { rmSync(resolve(cacheParent, d), { recursive: true, force: true }); } catch { /* skip */ }
62
+ }
63
+ }
64
+ } catch { /* skip */ }
65
+ }
66
+ }
67
+ }
68
+
69
+ writeFileSync(ipPath, JSON.stringify(ip, null, 2) + "\n", "utf-8");
70
+ writeFileSync(marker, Date.now().toString(), "utf-8");
71
+ }
72
+ } catch { /* best effort — don't block hook */ }
73
+
9
74
  let raw = "";
10
75
  process.stdin.setEncoding("utf-8");
11
76
  for await (const chunk of process.stdin) raw += chunk;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "context-mode",
3
- "version": "0.9.9",
3
+ "version": "0.9.11",
4
4
  "type": "module",
5
5
  "description": "Claude Code MCP plugin that saves 98% of your context window. Sandboxed code execution, FTS5 knowledge base, and intent-driven search.",
6
6
  "author": "Mert Koseoğlu",