context-mode 1.0.54 → 1.0.56

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.
Files changed (50) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.openclaw-plugin/openclaw.plugin.json +1 -1
  4. package/.openclaw-plugin/package.json +1 -1
  5. package/README.md +20 -6
  6. package/build/adapters/antigravity/index.d.ts +1 -3
  7. package/build/adapters/antigravity/index.js +0 -30
  8. package/build/adapters/claude-code/index.d.ts +1 -3
  9. package/build/adapters/claude-code/index.js +15 -34
  10. package/build/adapters/codex/index.d.ts +1 -3
  11. package/build/adapters/codex/index.js +1 -31
  12. package/build/adapters/cursor/index.d.ts +1 -3
  13. package/build/adapters/cursor/index.js +0 -11
  14. package/build/adapters/gemini-cli/index.d.ts +1 -3
  15. package/build/adapters/gemini-cli/index.js +0 -30
  16. package/build/adapters/kiro/index.d.ts +1 -3
  17. package/build/adapters/kiro/index.js +0 -30
  18. package/build/adapters/openclaw/index.d.ts +1 -3
  19. package/build/adapters/openclaw/index.js +0 -38
  20. package/build/adapters/opencode/index.d.ts +1 -3
  21. package/build/adapters/opencode/index.js +15 -34
  22. package/build/adapters/types.d.ts +0 -13
  23. package/build/adapters/vscode-copilot/index.d.ts +1 -3
  24. package/build/adapters/vscode-copilot/index.js +0 -32
  25. package/build/adapters/zed/index.d.ts +1 -3
  26. package/build/adapters/zed/index.js +0 -30
  27. package/build/executor.d.ts +0 -1
  28. package/build/executor.js +26 -14
  29. package/build/openclaw-plugin.js +0 -30
  30. package/build/opencode-plugin.d.ts +1 -0
  31. package/build/opencode-plugin.js +1 -8
  32. package/build/server.js +34 -17
  33. package/build/truncate.d.ts +4 -17
  34. package/build/truncate.js +4 -52
  35. package/cli.bundle.mjs +109 -136
  36. package/hooks/ensure-deps.mjs +80 -2
  37. package/hooks/routing-block.mjs +10 -1
  38. package/hooks/session-snapshot.bundle.mjs +13 -13
  39. package/openclaw.plugin.json +1 -1
  40. package/package.json +1 -1
  41. package/server.bundle.mjs +82 -106
  42. package/skills/context-mode-ops/SKILL.md +111 -0
  43. package/skills/context-mode-ops/agent-teams.md +198 -0
  44. package/skills/context-mode-ops/communication.md +224 -0
  45. package/skills/context-mode-ops/release.md +199 -0
  46. package/skills/context-mode-ops/review-pr.md +269 -0
  47. package/skills/context-mode-ops/tdd.md +329 -0
  48. package/skills/context-mode-ops/triage-issue.md +218 -0
  49. package/skills/context-mode-ops/validation.md +238 -0
  50. package/start.mjs +5 -52
@@ -8,13 +8,22 @@
8
8
  * hook that needs native modules. Fast path: existsSync check (~0.1ms).
9
9
  * Slow path: npm install (first run only, ~5-30s).
10
10
  *
11
+ * Also handles ABI compatibility (#148, #203): when the current Node.js
12
+ * version differs from the one better-sqlite3 was compiled against,
13
+ * automatically swaps in a cached binary or rebuilds. This protects
14
+ * both the MCP server AND hooks from ABI mismatch crashes when users
15
+ * have multiple Node versions via mise/volta/fnm/nvm.
16
+ *
17
+ * @see https://github.com/mksglu/context-mode/issues/148
11
18
  * @see https://github.com/mksglu/context-mode/issues/172
19
+ * @see https://github.com/mksglu/context-mode/issues/203
12
20
  */
13
21
 
14
- import { existsSync } from "node:fs";
22
+ import { existsSync, copyFileSync } from "node:fs";
15
23
  import { execSync } from "node:child_process";
16
24
  import { resolve, dirname } from "node:path";
17
25
  import { fileURLToPath } from "node:url";
26
+ import { createRequire } from "node:module";
18
27
 
19
28
  const __dirname = dirname(fileURLToPath(import.meta.url));
20
29
  const root = resolve(__dirname, "..");
@@ -23,7 +32,9 @@ const NATIVE_DEPS = ["better-sqlite3"];
23
32
 
24
33
  export function ensureDeps() {
25
34
  for (const pkg of NATIVE_DEPS) {
26
- if (!existsSync(resolve(root, "node_modules", pkg))) {
35
+ const pkgDir = resolve(root, "node_modules", pkg);
36
+ if (!existsSync(pkgDir)) {
37
+ // Package not installed at all
27
38
  try {
28
39
  execSync(`npm install ${pkg} --no-package-lock --no-save --silent`, {
29
40
  cwd: root,
@@ -31,9 +42,76 @@ export function ensureDeps() {
31
42
  timeout: 120000,
32
43
  });
33
44
  } catch { /* best effort — hook degrades gracefully without DB */ }
45
+ } else if (
46
+ !existsSync(resolve(pkgDir, "build", "Release")) &&
47
+ !existsSync(resolve(pkgDir, "prebuilds"))
48
+ ) {
49
+ // Package installed but native binary missing (e.g., npm ignore-scripts=true)
50
+ try {
51
+ execSync(`npm rebuild ${pkg} --ignore-scripts=false`, {
52
+ cwd: root,
53
+ stdio: "pipe",
54
+ timeout: 120000,
55
+ });
56
+ } catch { /* best effort — hook degrades gracefully without DB */ }
57
+ }
58
+ }
59
+ }
60
+
61
+ /**
62
+ * ABI-aware native binary caching for better-sqlite3 (#148, #203).
63
+ *
64
+ * Users with mise/asdf/volta/fnm may run sessions with different Node
65
+ * versions. Each ABI needs its own compiled binary — cache them
66
+ * side-by-side so switching Node versions doesn't require a rebuild
67
+ * every time.
68
+ *
69
+ * Flow:
70
+ * 1. Check if ABI-specific cache exists → swap in
71
+ * 2. Probe-load better-sqlite3 → if OK, cache current binary
72
+ * 3. If ABI mismatch → npm rebuild, then cache the new binary
73
+ */
74
+ export function ensureNativeCompat(pluginRoot) {
75
+ try {
76
+ const abi = process.versions.modules;
77
+ const nativeDir = resolve(pluginRoot, "node_modules", "better-sqlite3", "build", "Release");
78
+ const binaryPath = resolve(nativeDir, "better_sqlite3.node");
79
+ const abiCachePath = resolve(nativeDir, `better_sqlite3.abi${abi}.node`);
80
+
81
+ if (!existsSync(nativeDir)) return;
82
+
83
+ // Fast path: cached binary for this ABI already exists
84
+ if (existsSync(abiCachePath)) {
85
+ copyFileSync(abiCachePath, binaryPath);
86
+ return;
87
+ }
88
+
89
+ if (!existsSync(binaryPath)) return;
90
+
91
+ // Probe: try loading better-sqlite3 with current Node
92
+ try {
93
+ const req = createRequire(resolve(pluginRoot, "package.json"));
94
+ req("better-sqlite3");
95
+ // Load succeeded — cache the working binary for this ABI
96
+ copyFileSync(binaryPath, abiCachePath);
97
+ } catch (probeErr) {
98
+ if (probeErr?.message?.includes("NODE_MODULE_VERSION")) {
99
+ // ABI mismatch — rebuild for current Node version
100
+ execSync("npm rebuild better-sqlite3", {
101
+ cwd: pluginRoot,
102
+ stdio: "pipe",
103
+ timeout: 60000,
104
+ });
105
+ if (existsSync(binaryPath)) {
106
+ copyFileSync(binaryPath, abiCachePath);
107
+ }
108
+ }
34
109
  }
110
+ } catch {
111
+ /* best effort — caller will report the error on first DB access */
35
112
  }
36
113
  }
37
114
 
38
115
  // Auto-run on import (like suppress-stderr.mjs)
39
116
  ensureDeps();
117
+ ensureNativeCompat(root);
@@ -35,12 +35,21 @@ export function createRoutingBlock(t) {
35
35
  - DO NOT use Read for analysis (use execute_file). Read IS correct for files you intend to Edit.
36
36
  - DO NOT use WebFetch (use ${t("ctx_fetch_and_index")} instead).
37
37
  - Bash is ONLY for git/mkdir/rm/mv/navigation.
38
+ - DO NOT use ${t("ctx_execute")} or ${t("ctx_execute_file")} to create, modify, or overwrite files.
39
+ ctx_execute is for data analysis, log processing, and computation only.
38
40
  </forbidden_actions>
39
41
 
42
+ <file_writing_policy>
43
+ ALWAYS use the native Write tool to create files and Edit tool to modify files.
44
+ NEVER use ${t("ctx_execute")}, ${t("ctx_execute_file")}, or Bash to write file content.
45
+ This applies to all file types: code, configs, plans, specs, YAML, JSON, markdown.
46
+ </file_writing_policy>
47
+
40
48
  <output_constraints>
41
49
  <word_limit>Keep your final response under 500 words.</word_limit>
42
50
  <artifact_policy>
43
- Write artifacts (code, configs, PRDs) to FILES. NEVER return them as inline text.
51
+ Write artifacts (code, configs, PRDs) to FILES using the native Write tool. NEVER return them as inline text.
52
+ Use Edit tool for modifications to existing files.
44
53
  Return only: file path + 1-line description.
45
54
  </artifact_policy>
46
55
  <response_format>
@@ -1,14 +1,14 @@
1
- function p(t,e){return t.length<=e?t:t.slice(0,Math.max(0,e-3))+"..."}function a(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}var G=2048,J=10;function q(t){if(t.length===0)return"";let e=new Map;for(let u of t){let l=u.data,i=e.get(l);i||(i={ops:new Map,last:""},e.set(l,i));let r;u.type==="file_write"?r="write":u.type==="file_read"?r="read":u.type==="file_edit"?r="edit":r=u.type,i.ops.set(r,(i.ops.get(r)??0)+1),i.last=r}let s=Array.from(e.entries()).slice(-J),c=[" <active_files>"];for(let[u,{ops:l,last:i}]of s){let r=Array.from(l.entries()).map(([f,d])=>`${f}:${d}`).join(",");c.push(` <file path="${a(u)}" ops="${a(r)}" last="${a(i)}" />`)}return c.push(" </active_files>"),c.join(`
2
- `)}function z(t){if(t.length===0)return"";let e=[],n={};for(let i of t)try{let r=JSON.parse(i.data);typeof r.subject=="string"?e.push(r.subject):typeof r.taskId=="string"&&typeof r.status=="string"&&(n[r.taskId]=r.status)}catch{}if(e.length===0)return"";let s=new Set(["completed","deleted","failed"]),c=Object.keys(n).sort((i,r)=>Number(i)-Number(r)),u=[];for(let i=0;i<e.length;i++){let r=c[i],f=r?n[r]??"pending":"pending";s.has(f)||u.push(e[i])}if(u.length===0)return"";let l=[" <task_state>"];for(let i of u)l.push(` - ${a(p(i,100))}`);return l.push(" </task_state>"),l.join(`
3
- `)}function K(t){if(t.length===0)return"";let e=new Set,n=[" <rules>"];for(let s of t){let c=s.data;e.has(c)||(e.add(c),s.type==="rule_content"?n.push(` <rule_content>${a(p(s.data,400))}</rule_content>`):n.push(` - ${a(p(s.data,200))}`))}return n.push(" </rules>"),n.join(`
4
- `)}function U(t){if(t.length===0)return"";let e=new Set,n=[" <decisions>"];for(let s of t){let c=s.data;e.has(c)||(e.add(c),n.push(` - ${a(p(s.data,200))}`))}return n.push(" </decisions>"),n.join(`
5
- `)}function V(t,e,n){let s=[];if(!t&&e.length===0&&!n)return"";s.push(" <environment>"),t&&s.push(` <cwd>${a(t.data)}</cwd>`),n&&s.push(` <git op="${a(n.data)}" />`);for(let c of e)s.push(` <env>${a(p(c.data,150))}</env>`);return s.push(" </environment>"),s.join(`
6
- `)}function Y(t){if(t.length===0)return"";let e=[" <errors_encountered>"];for(let n of t)e.push(` - ${a(p(n.data,150))}`);return e.push(" </errors_encountered>"),e.join(`
7
- `)}function H(t){return` <intent mode="${a(t.data)}">${a(p(t.data,100))}</intent>`}function N(t){if(t.length===0)return"";let e=[" <subagents>"];for(let n of t){let s=n.type==="subagent_completed"?"completed":n.type==="subagent_launched"?"launched":"unknown";e.push(` <agent status="${s}">${a(p(n.data,200))}</agent>`)}return e.push(" </subagents>"),e.join(`
1
+ function l(t,e){return t.length<=e?t:t.slice(0,Math.max(0,e-3))+"..."}function a(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}var G=2048,J=10;function q(t){if(t.length===0)return"";let e=new Map;for(let u of t){let p=u.data,i=e.get(p);i||(i={ops:new Map,last:""},e.set(p,i));let r;u.type==="file_write"?r="write":u.type==="file_read"?r="read":u.type==="file_edit"?r="edit":r=u.type,i.ops.set(r,(i.ops.get(r)??0)+1),i.last=r}let s=Array.from(e.entries()).slice(-J),c=[" <active_files>"];for(let[u,{ops:p,last:i}]of s){let r=Array.from(p.entries()).map(([f,g])=>`${f}:${g}`).join(",");c.push(` <file path="${a(u)}" ops="${a(r)}" last="${a(i)}" />`)}return c.push(" </active_files>"),c.join(`
2
+ `)}function z(t){if(t.length===0)return"";let e=[],n={};for(let i of t)try{let r=JSON.parse(i.data);typeof r.subject=="string"?e.push(r.subject):typeof r.taskId=="string"&&typeof r.status=="string"&&(n[r.taskId]=r.status)}catch{}if(e.length===0)return"";let s=new Set(["completed","deleted","failed"]),c=Object.keys(n).sort((i,r)=>Number(i)-Number(r)),u=[];for(let i=0;i<e.length;i++){let r=c[i],f=r?n[r]??"pending":"pending";s.has(f)||u.push(e[i])}if(u.length===0)return"";let p=[" <task_state>"];for(let i of u)p.push(` - ${a(l(i,100))}`);return p.push(" </task_state>"),p.join(`
3
+ `)}function U(t){if(t.length===0)return"";let e=new Set,n=[" <rules>"];for(let s of t){let c=s.data;e.has(c)||(e.add(c),s.type==="rule_content"?n.push(` <rule_content>${a(l(s.data,400))}</rule_content>`):n.push(` - ${a(l(s.data,200))}`))}return n.push(" </rules>"),n.join(`
4
+ `)}function V(t){if(t.length===0)return"";let e=new Set,n=[" <decisions>"];for(let s of t){let c=s.data;e.has(c)||(e.add(c),n.push(` - ${a(l(s.data,200))}`))}return n.push(" </decisions>"),n.join(`
5
+ `)}function Y(t,e,n){let s=[];if(!t&&e.length===0&&!n)return"";s.push(" <environment>"),t&&s.push(` <cwd>${a(t.data)}</cwd>`),n&&s.push(` <git op="${a(n.data)}" />`);for(let c of e)s.push(` <env>${a(l(c.data,150))}</env>`);return s.push(" </environment>"),s.join(`
6
+ `)}function H(t){if(t.length===0)return"";let e=[" <errors_encountered>"];for(let n of t)e.push(` - ${a(l(n.data,150))}`);return e.push(" </errors_encountered>"),e.join(`
7
+ `)}function K(t){return` <intent mode="${a(t.data)}">${a(l(t.data,100))}</intent>`}function P(t){if(t.length===0)return"";let e=[" <subagents>"];for(let n of t){let s=n.type==="subagent_completed"?"completed":n.type==="subagent_launched"?"launched":"unknown";e.push(` <agent status="${s}">${a(l(n.data,200))}</agent>`)}return e.push(" </subagents>"),e.join(`
8
8
  `)}function Q(t){if(t.length===0)return"";let e=new Map;for(let s of t){let c=s.data.split(":")[0].trim();e.set(c,(e.get(c)??0)+1)}let n=[" <mcp_tools>"];for(let[s,c]of e)n.push(` <tool name="${a(s)}" calls="${c}" />`);return n.push(" </mcp_tools>"),n.join(`
9
- `)}function et(t,e){let n=e?.maxBytes??G,s=e?.compactCount??1,c=new Date().toISOString(),u=[],l=[],i=[],r=[],f=[],d=[],_=[],h=[],k=[],b=[],B=[],S=[];for(let o of t)switch(o.category){case"file":u.push(o);break;case"task":l.push(o);break;case"rule":i.push(o);break;case"decision":r.push(o);break;case"cwd":f.push(o);break;case"error":d.push(o);break;case"env":_.push(o);break;case"git":h.push(o);break;case"subagent":k.push(o);break;case"intent":b.push(o);break;case"mcp":B.push(o);break;case"plan":S.push(o);break}let m=[],w=q(u);w&&m.push(w);let x=z(l);x&&m.push(x);let L=K(i);L&&m.push(L);let g=[],j=U(r);j&&g.push(j);let P=f.length>0?f[f.length-1]:void 0,R=h.length>0?h[h.length-1]:void 0,M=V(P,_,R);M&&g.push(M);let I=Y(d);I&&g.push(I);let D=k.filter(o=>o.type==="subagent_completed"),T=N(D);T&&g.push(T),S.length>0&&S[S.length-1].type==="plan_enter"&&g.push(' <plan_mode status="active" />');let v=[];if(b.length>0){let o=b[b.length-1];v.push(H(o))}let O=Q(B);O&&v.push(O);let X=k.filter(o=>o.type==="subagent_launched"),A=N(X);A&&v.push(A);let E=`<session_resume compact_count="${s}" events_captured="${t.length}" generated_at="${c}">`,$="</session_resume>",C=[m,g,v];for(let o=C.length;o>=0;o--){let F=C.slice(0,o).flat().join(`
10
- `),y;if(F?y=`${E}
11
- ${F}
12
- ${$}`:y=`${E}
13
- ${$}`,Buffer.byteLength(y)<=n)return y}return`${E}
14
- ${$}`}export{et as buildResumeSnapshot,q as renderActiveFiles,U as renderDecisions,V as renderEnvironment,Y as renderErrors,H as renderIntent,Q as renderMcpTools,K as renderRules,N as renderSubagents,z as renderTaskState};
9
+ `)}function et(t,e){let n=e?.maxBytes??G,s=e?.compactCount??1,c=new Date().toISOString(),u=[],p=[],i=[],r=[],f=[],g=[],$=[],h=[],E=[],S=[],w=[],m=[];for(let o of t)switch(o.category){case"file":u.push(o);break;case"task":p.push(o);break;case"rule":i.push(o);break;case"decision":r.push(o);break;case"cwd":f.push(o);break;case"error":g.push(o);break;case"env":$.push(o);break;case"git":h.push(o);break;case"subagent":E.push(o);break;case"intent":S.push(o);break;case"mcp":w.push(o);break;case"plan":m.push(o);break}let b=[],x=q(u);x&&b.push(x);let B=z(p);B&&b.push(B);let j=U(i);j&&b.push(j);let d=[],I=V(r);I&&d.push(I);let R=f.length>0?f[f.length-1]:void 0,F=h.length>0?h[h.length-1]:void 0,L=Y(R,$,F);L&&d.push(L);let M=H(g);M&&d.push(M);let D=E.filter(o=>o.type==="subagent_completed"),O=P(D);O&&d.push(O),m.length>0&&m[m.length-1].type==="plan_enter"&&d.push(' <plan_mode status="active" />');let v=[];if(S.length>0){let o=S[S.length-1];v.push(K(o))}let T=Q(w);T&&v.push(T);let X=E.filter(o=>o.type==="subagent_launched"),A=P(X);A&&v.push(A);let _=`<session_resume compact_count="${s}" events_captured="${t.length}" generated_at="${c}">`,k="</session_resume>",C=[b,d,v];for(let o=C.length;o>=0;o--){let N=C.slice(0,o).flat().join(`
10
+ `),y;if(N?y=`${_}
11
+ ${N}
12
+ ${k}`:y=`${_}
13
+ ${k}`,Buffer.byteLength(y)<=n)return y}return`${_}
14
+ ${k}`}export{et as buildResumeSnapshot,q as renderActiveFiles,V as renderDecisions,Y as renderEnvironment,H as renderErrors,K as renderIntent,Q as renderMcpTools,U as renderRules,P as renderSubagents,z as renderTaskState};
@@ -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.54",
6
+ "version": "1.0.56",
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.54",
3
+ "version": "1.0.56",
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",