context-mode 1.0.97 → 1.0.98

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.
@@ -6,14 +6,14 @@
6
6
  },
7
7
  "metadata": {
8
8
  "description": "Claude Code plugins by Mert Koseoğlu",
9
- "version": "1.0.97"
9
+ "version": "1.0.98"
10
10
  },
11
11
  "plugins": [
12
12
  {
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": "1.0.97",
16
+ "version": "1.0.98",
17
17
  "author": {
18
18
  "name": "Mert Koseoğlu"
19
19
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "context-mode",
3
- "version": "1.0.97",
3
+ "version": "1.0.98",
4
4
  "description": "MCP server that saves 98% of your context window with session continuity. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and automatic state restore across compactions.",
5
5
  "author": {
6
6
  "name": "Mert Koseoğlu",
@@ -3,7 +3,7 @@
3
3
  "name": "Context Mode",
4
4
  "kind": "tool",
5
5
  "description": "OpenClaw plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
6
- "version": "1.0.97",
6
+ "version": "1.0.98",
7
7
  "sandbox": {
8
8
  "mode": "permissive",
9
9
  "filesystem_access": "full",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "context-mode",
3
- "version": "1.0.97",
3
+ "version": "1.0.98",
4
4
  "description": "OpenClaw 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/server.js CHANGED
@@ -2154,8 +2154,11 @@ async function main() {
2154
2154
  if (cleaned > 0) {
2155
2155
  console.error(`Cleaned up ${cleaned} stale DB file(s) from previous sessions`);
2156
2156
  }
2157
- // MCP readiness sentinel path (#230)
2158
- const mcpSentinel = join(tmpdir(), `context-mode-mcp-ready-${process.ppid}`);
2157
+ // MCP readiness sentinel path (#230, #347)
2158
+ // Uses process.pid (not ppid) — hooks use directory-scan to find any live sentinel.
2159
+ // Hardcoded /tmp on Unix to avoid TMPDIR mismatch (#347).
2160
+ const mcpSentinelDir = process.platform === "win32" ? tmpdir() : "/tmp";
2161
+ const mcpSentinel = join(mcpSentinelDir, `context-mode-mcp-ready-${process.pid}`);
2159
2162
  // Clean up own DB + backgrounded processes + preload script on shutdown
2160
2163
  const shutdown = () => {
2161
2164
  executor.cleanupBackgrounded();
package/cli.bundle.mjs CHANGED
@@ -396,7 +396,7 @@ async function main() {
396
396
  emit('text', text);
397
397
  }
398
398
  main();
399
- `}function Uk(){return{prepare:()=>({run:()=>{},get:(...t)=>({cnt:0,compact_count:0,minutes:null,rate:0,avg:0,outcome:"exploratory"}),all:()=>[]})}}async function aM(){let t=Ah();t>0&&console.error(`Cleaned up ${t} stale DB file(s) from previous sessions`);let e=_e(Vh(),`context-mode-mcp-ready-${process.ppid}`),r=()=>{yi.cleanupBackgrounded(),fr&&fr.close();try{Tr(Wh)}catch{}try{Tr(e)}catch{}if(Pr&&Pr.pid&&!Pr.killed)try{Pr.kill("SIGTERM")}catch{}},n=async()=>{r(),process.exit(0)};process.on("exit",r),process.on("SIGINT",()=>{n()}),process.on("SIGTERM",()=>{n()}),Rk({onShutdown:()=>n()});let o=new Dc;await Ke.connect(o);try{Hk(e,String(process.pid))}catch{}try{let{detectPlatform:s,getAdapter:i}=await Promise.resolve().then(()=>(nl(),O_)),a=Ke.server.getClientVersion(),c=s(a??void 0);Yc=await i(c.platform),a&&console.error(`MCP client: ${a.name} v${a.version} \u2192 ${c.platform}`)}catch{}Bj().then(s=>{s!=="unknown"&&(nn=s)}),console.error(`Context Mode MCP server v${tn} running on stdio`),console.error(`Detected runtimes:
399
+ `}function Uk(){return{prepare:()=>({run:()=>{},get:(...t)=>({cnt:0,compact_count:0,minutes:null,rate:0,avg:0,outcome:"exploratory"}),all:()=>[]})}}async function aM(){let t=Ah();t>0&&console.error(`Cleaned up ${t} stale DB file(s) from previous sessions`);let e=process.platform==="win32"?Vh():"/tmp",r=_e(e,`context-mode-mcp-ready-${process.pid}`),n=()=>{yi.cleanupBackgrounded(),fr&&fr.close();try{Tr(Wh)}catch{}try{Tr(r)}catch{}if(Pr&&Pr.pid&&!Pr.killed)try{Pr.kill("SIGTERM")}catch{}},o=async()=>{n(),process.exit(0)};process.on("exit",n),process.on("SIGINT",()=>{o()}),process.on("SIGTERM",()=>{o()}),Rk({onShutdown:()=>o()});let s=new Dc;await Ke.connect(s);try{Hk(r,String(process.pid))}catch{}try{let{detectPlatform:i,getAdapter:a}=await Promise.resolve().then(()=>(nl(),O_)),c=Ke.server.getClientVersion(),u=i(c??void 0);Yc=await a(u.platform),c&&console.error(`MCP client: ${c.name} v${c.version} \u2192 ${u.platform}`)}catch{}Bj().then(i=>{i!=="unknown"&&(nn=i)}),console.error(`Context Mode MCP server v${tn} running on stdio`),console.error(`Detected runtimes:
400
400
  ${Oi(ru)}`),Jn()||(console.error(`
401
401
  Performance tip: Install Bun for 3-5x faster JS/TS execution`),console.error(" curl -fsSL https://bun.sh/install | bash"))}var bt,tn,ru,Kc,Ke,yi,Wh,fr,Yc,Pr,je,nn,Gc,Mk,Hj,qj,Dk,Xj,Qj,eM,tM,Qc,eu,Vn,Uh,rM,Lk,Fk,Zh,Hh,Kk=v(()=>{"use strict";XS();rk();$h();Ph();xk();Tk();Ii();Pk();Ok();Ck();gi();zk();wo();bt=Qt(Lj(import.meta.url)),tn=(()=>{for(let t of["../package.json","./package.json"]){let e=vt(bt,t);if(Ie(e))try{return JSON.parse(Bh(e,"utf8")).version}catch{}}return"unknown"})();process.on("unhandledRejection",t=>{process.stderr.write(`[context-mode] unhandledRejection: ${t}
402
402
  `)});process.on("uncaughtException",t=>{process.stderr.write(`[context-mode] uncaughtException: ${t?.message??t}
@@ -1,30 +1,73 @@
1
1
  /**
2
2
  * MCP readiness sentinel — checks if MCP server has started.
3
- * Server writes sentinel (containing its PID) after connect(),
4
- * hooks check before denying tools that redirect to MCP alternatives.
5
3
  *
6
- * Sentinel path: ${tmpdir()}/context-mode-mcp-ready-${process.ppid}
7
- * Both hooks and MCP server share the same ppid (Claude Code process).
4
+ * Server writes sentinel (containing its PID) after connect().
5
+ * Hooks scan for any live sentinel to detect MCP readiness.
6
+ *
7
+ * Fix for #347: Claude Code spawns hooks via `bash -c "node ..."` on Linux/WSL2.
8
+ * The intermediate shell makes process.ppid point to a transient bash PID, not
9
+ * Claude Code. Directory-scan + PID liveness probe works regardless of spawn topology.
10
+ *
11
+ * Sentinel path: <tmpRoot>/context-mode-mcp-ready-<MCP_PID>
12
+ * Scan: glob all context-mode-mcp-ready-* files, probe each PID.
8
13
  */
9
- import { readFileSync } from "node:fs";
14
+ import { readFileSync, readdirSync, unlinkSync } from "node:fs";
10
15
  import { tmpdir } from "node:os";
11
- import { resolve } from "node:path";
16
+ import { join } from "node:path";
17
+
18
+ const SENTINEL_PREFIX = "context-mode-mcp-ready-";
19
+
20
+ /** Resolve the temp root — hardcoded /tmp on Unix to avoid TMPDIR mismatch. */
21
+ export function sentinelDir() {
22
+ return process.platform === "win32" ? tmpdir() : "/tmp";
23
+ }
24
+
25
+ /**
26
+ * Build sentinel path for a given PID.
27
+ * Used by server.ts to write its own sentinel.
28
+ */
29
+ export function sentinelPathForPid(pid) {
30
+ return join(sentinelDir(), `${SENTINEL_PREFIX}${pid}`);
31
+ }
12
32
 
13
- /** Compute the sentinel file path, scoped to session via parent PID. */
33
+ /**
34
+ * @deprecated Use sentinelPathForPid(process.pid) from server.ts.
35
+ * Kept for backward compat during migration — tests that still
36
+ * write sentinels with process.ppid will work for one release cycle.
37
+ */
14
38
  export function sentinelPath() {
15
- return resolve(tmpdir(), `context-mode-mcp-ready-${process.ppid}`);
39
+ return join(sentinelDir(), `${SENTINEL_PREFIX}${process.ppid}`);
16
40
  }
17
41
 
18
42
  /**
19
- * Check if MCP server is alive by reading sentinel PID and probing it.
20
- * Handles stale sentinels from crashed servers (SIGKILL, OOM) — if the
21
- * PID in the sentinel is dead, returns false so hooks allow fallback.
43
+ * Check if any MCP server is alive by scanning sentinel files.
44
+ *
45
+ * Scans sentinelDir() for context-mode-mcp-ready-* files, reads the PID
46
+ * from each, and probes with kill(pid, 0). Cleans up stale sentinels
47
+ * from crashed servers.
48
+ *
49
+ * Handles:
50
+ * - PPID mismatch (WSL2 shell wrappers) — no ppid dependency
51
+ * - Stale sentinels (SIGKILL, OOM) — PID liveness check
52
+ * - TMPDIR mismatch — hardcoded /tmp on Unix
22
53
  */
23
54
  export function isMCPReady() {
24
55
  try {
25
- const pid = parseInt(readFileSync(sentinelPath(), "utf8"), 10);
26
- process.kill(pid, 0); // throws if process doesn't exist
27
- return true;
56
+ const dir = sentinelDir();
57
+ const files = readdirSync(dir).filter(f => f.startsWith(SENTINEL_PREFIX));
58
+ for (const f of files) {
59
+ const fullPath = join(dir, f);
60
+ try {
61
+ const pid = parseInt(readFileSync(fullPath, "utf8"), 10);
62
+ if (isNaN(pid)) continue;
63
+ process.kill(pid, 0); // throws if process doesn't exist
64
+ return true;
65
+ } catch {
66
+ // Dead PID or unreadable — clean up stale sentinel
67
+ try { unlinkSync(fullPath); } catch {}
68
+ }
69
+ }
70
+ return false;
28
71
  } catch {
29
72
  return false;
30
73
  }
@@ -3,7 +3,7 @@
3
3
  "name": "Context Mode",
4
4
  "kind": "tool",
5
5
  "description": "OpenClaw plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
6
- "version": "1.0.97",
6
+ "version": "1.0.98",
7
7
  "sandbox": {
8
8
  "mode": "permissive",
9
9
  "filesystem_access": "full",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "context-mode",
3
- "version": "1.0.97",
3
+ "version": "1.0.98",
4
4
  "type": "module",
5
5
  "description": "MCP plugin that saves 98% of your context window. Works with Claude Code, Gemini CLI, VS Code Copilot, OpenCode, and Codex CLI. Sandboxed code execution, FTS5 knowledge base, and intent-driven search.",
6
6
  "author": "Mert Koseoğlu",
package/server.bundle.mjs CHANGED
@@ -568,6 +568,6 @@ To fix:
568
568
  `)+`
569
569
 
570
570
  Open: ${p}
571
- PID: ${m.pid} \xB7 Stop: ${process.platform==="win32"?`taskkill /PID ${m.pid} /F`:`kill ${m.pid}`}`}]})}catch(i){let a=i instanceof Error?i.message:String(i);return D("ctx_insight",{content:[{type:"text",text:`Insight setup failed: ${a}`}]})}});async function DN(){let t=Cp();t>0&&console.error(`Cleaned up ${t} stale DB file(s) from previous sessions`);let e=le($f(),`context-mode-mcp-ready-${process.ppid}`),r=()=>{Is.cleanupBackgrounded(),Xt&&Xt.close();try{ur(Pf)}catch{}try{ur(e)}catch{}if(lr&&lr.pid&&!lr.killed)try{lr.kill("SIGTERM")}catch{}},n=async()=>{r(),process.exit(0)};process.on("exit",r),process.on("SIGINT",()=>{n()}),process.on("SIGTERM",()=>{n()}),Gx({onShutdown:()=>n()});let o=new ya;await De.connect(o);try{hS(e,String(process.pid))}catch{}try{let{detectPlatform:s,getAdapter:i}=await Promise.resolve().then(()=>(aS(),iS)),a=De.server.getClientVersion(),c=s(a??void 0);Ma=await i(c.platform),a&&console.error(`MCP client: ${a.name} v${a.version} \u2192 ${c.platform}`)}catch{}vN().then(s=>{s!=="unknown"&&(Ar=s)}),console.error(`Context Mode MCP server v${Nr} running on stdio`),console.error(`Detected runtimes:
571
+ PID: ${m.pid} \xB7 Stop: ${process.platform==="win32"?`taskkill /PID ${m.pid} /F`:`kill ${m.pid}`}`}]})}catch(i){let a=i instanceof Error?i.message:String(i);return D("ctx_insight",{content:[{type:"text",text:`Insight setup failed: ${a}`}]})}});async function DN(){let t=Cp();t>0&&console.error(`Cleaned up ${t} stale DB file(s) from previous sessions`);let e=process.platform==="win32"?$f():"/tmp",r=le(e,`context-mode-mcp-ready-${process.pid}`),n=()=>{Is.cleanupBackgrounded(),Xt&&Xt.close();try{ur(Pf)}catch{}try{ur(r)}catch{}if(lr&&lr.pid&&!lr.killed)try{lr.kill("SIGTERM")}catch{}},o=async()=>{n(),process.exit(0)};process.on("exit",n),process.on("SIGINT",()=>{o()}),process.on("SIGTERM",()=>{o()}),Gx({onShutdown:()=>o()});let s=new ya;await De.connect(s);try{hS(r,String(process.pid))}catch{}try{let{detectPlatform:i,getAdapter:a}=await Promise.resolve().then(()=>(aS(),iS)),c=De.server.getClientVersion(),u=i(c??void 0);Ma=await a(u.platform),c&&console.error(`MCP client: ${c.name} v${c.version} \u2192 ${u.platform}`)}catch{}vN().then(i=>{i!=="unknown"&&(Ar=i)}),console.error(`Context Mode MCP server v${Nr} running on stdio`),console.error(`Detected runtimes:
572
572
  ${Tx(Ha)}`),Sa()||(console.error(`
573
573
  Performance tip: Install Bun for 3-5x faster JS/TS execution`),console.error(" curl -fsSL https://bun.sh/install | bash"))}DN().catch(t=>{console.error("Fatal:",t),process.exit(1)});export{_S as extractSnippet,IN as formatBatchQueryResults,CN as positionsFromHighlight};