context-mode 1.0.88 → 1.0.89
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.openclaw-plugin/openclaw.plugin.json +1 -1
- package/.openclaw-plugin/package.json +1 -1
- package/build/adapters/claude-code/index.js +15 -1
- package/build/cli.js +100 -47
- package/build/pi-extension.js +24 -7
- package/build/server.js +88 -12
- package/build/store.d.ts +3 -0
- package/build/store.js +59 -9
- package/build/truncate.d.ts +6 -0
- package/build/truncate.js +51 -29
- package/cli.bundle.mjs +139 -132
- package/hooks/pretooluse.mjs +38 -16
- package/hooks/session-snapshot.bundle.mjs +14 -14
- package/insight/server.mjs +25 -7
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server.bundle.mjs +99 -95
package/hooks/pretooluse.mjs
CHANGED
|
@@ -96,24 +96,46 @@ try {
|
|
|
96
96
|
const allHooks = settings.hooks || {};
|
|
97
97
|
let changed = false;
|
|
98
98
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
99
|
+
// If hooks.json is present, the plugin system owns hook registration.
|
|
100
|
+
// Remove any settings.json context-mode entries to prevent duplicate concurrent
|
|
101
|
+
// hook processes that cause "non-blocking hook error" on every tool call.
|
|
102
|
+
const hooksJsonPath = resolve(myRoot, "hooks", "hooks.json");
|
|
103
|
+
if (existsSync(hooksJsonPath)) {
|
|
104
|
+
for (const hookType of Object.keys(allHooks)) {
|
|
105
|
+
const entries = allHooks[hookType];
|
|
106
|
+
if (!Array.isArray(entries)) continue;
|
|
107
|
+
const filtered = entries.filter(
|
|
108
|
+
(entry) =>
|
|
109
|
+
!entry.hooks?.some(
|
|
110
|
+
(h) => h.command?.includes(".mjs") && h.command?.includes("context-mode"),
|
|
111
|
+
),
|
|
112
|
+
);
|
|
113
|
+
if (filtered.length !== entries.length) {
|
|
114
|
+
allHooks[hookType] = filtered;
|
|
107
115
|
changed = true;
|
|
108
116
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
// Legacy: hooks.json absent — rewrite stale paths to current version dir.
|
|
120
|
+
for (const hookType of Object.keys(allHooks)) {
|
|
121
|
+
const entries = allHooks[hookType];
|
|
122
|
+
if (!Array.isArray(entries)) continue;
|
|
123
|
+
|
|
124
|
+
for (const entry of entries) {
|
|
125
|
+
// Fix deprecated Task-only matcher (PreToolUse only)
|
|
126
|
+
if (hookType === "PreToolUse" && entry.matcher?.includes("Task") && !entry.matcher.includes("Agent")) {
|
|
127
|
+
entry.matcher = entry.matcher.replace("Task", "Agent|Task");
|
|
128
|
+
changed = true;
|
|
129
|
+
}
|
|
130
|
+
// Rewrite stale context-mode hook paths to point to current version
|
|
131
|
+
for (const h of (entry.hooks || [])) {
|
|
132
|
+
if (h.command && h.command.includes(".mjs") && h.command.includes("context-mode") && !h.command.includes(targetDir)) {
|
|
133
|
+
// Extract the script filename (e.g., sessionstart.mjs, pretooluse.mjs)
|
|
134
|
+
const scriptMatch = h.command.match(/([a-z]+\.mjs)\s*"?\s*$/);
|
|
135
|
+
if (scriptMatch) {
|
|
136
|
+
h.command = "node " + resolve(targetDir, "hooks", scriptMatch[1]);
|
|
137
|
+
changed = true;
|
|
138
|
+
}
|
|
117
139
|
}
|
|
118
140
|
}
|
|
119
141
|
}
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
function a(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}var
|
|
1
|
+
function a(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}var M=10;function h(t,r=4){return[...new Set(t.filter(o=>o.length>0))].slice(0,r).map(o=>o.length>80?o.slice(0,80):o)}function m(t,r){if(r.length===0)return"";let s=r.map(n=>`"${a(n)}"`).join(", ");return`
|
|
2
2
|
For full details:
|
|
3
3
|
${a(t)}(
|
|
4
4
|
queries: [${s}],
|
|
5
5
|
source: "session-events"
|
|
6
|
-
)`}function
|
|
7
|
-
`)}function
|
|
8
|
-
`)}function
|
|
9
|
-
`)}function
|
|
10
|
-
`)}function
|
|
11
|
-
`)}function
|
|
12
|
-
`)}function J(t,r){let s=
|
|
6
|
+
)`}function A(t,r){if(t.length===0)return"";let s=new Map;for(let l of t){let b=l.data,p=s.get(b);p||(p={ops:new Map},s.set(b,p));let g;l.type==="file_write"?g="write":l.type==="file_read"?g="read":l.type==="file_edit"?g="edit":g=l.type,p.ops.set(g,(p.ops.get(g)??0)+1)}let o=Array.from(s.entries()).slice(-M),u=[],i=[];for(let[l,{ops:b}]of o){let p=Array.from(b.entries()).map(([S,y])=>`${S}\xD7${y}`).join(", "),g=l.split("/").pop()??l;u.push(` ${a(g)} (${a(p)})`),i.push(`${g} ${Array.from(b.keys()).join(" ")}`)}let e=h(i);return[` <files count="${s.size}">`,...u,m(r,e)," </files>"].join(`
|
|
7
|
+
`)}function x(t,r){if(t.length===0)return"";let s=[],n=[];for(let i of t)s.push(` ${a(i.data)}`),n.push(i.data);let o=h(n);return[` <errors count="${t.length}">`,...s,m(r,o)," </errors>"].join(`
|
|
8
|
+
`)}function D(t,r){if(t.length===0)return"";let s=new Set,n=[],o=[];for(let e of t)s.has(e.data)||(s.add(e.data),n.push(` ${a(e.data)}`),o.push(e.data));if(n.length===0)return"";let u=h(o);return[` <decisions count="${n.length}">`,...n,m(r,u)," </decisions>"].join(`
|
|
9
|
+
`)}function F(t,r){if(t.length===0)return"";let s=new Set,n=[],o=[];for(let e of t)s.has(e.data)||(s.add(e.data),e.type==="rule_content"?n.push(` ${a(e.data)}`):n.push(` ${a(e.data)}`),o.push(e.data));if(n.length===0)return"";let u=h(o);return[` <rules count="${n.length}">`,...n,m(r,u)," </rules>"].join(`
|
|
10
|
+
`)}function R(t,r){if(t.length===0)return"";let s=[],n=[];for(let i of t)s.push(` ${a(i.data)}`),n.push(i.data);let o=h(n);return[` <git count="${t.length}">`,...s,m(r,o)," </git>"].join(`
|
|
11
|
+
`)}function B(t){if(t.length===0)return"";let r=[],s={};for(let e of t)try{let c=JSON.parse(e.data);typeof c.subject=="string"?r.push(c.subject):typeof c.taskId=="string"&&typeof c.status=="string"&&(s[c.taskId]=c.status)}catch{}if(r.length===0)return"";let n=new Set(["completed","deleted","failed"]),o=Object.keys(s).sort((e,c)=>Number(e)-Number(c)),u=[];for(let e=0;e<r.length;e++){let c=o[e],l=c?s[c]??"pending":"pending";n.has(l)||u.push(r[e])}if(u.length===0)return"";let i=[];for(let e of u)i.push(` [pending] ${a(e)}`);return i.join(`
|
|
12
|
+
`)}function J(t,r){let s=B(t);if(!s)return"";let n=[];for(let e of t)try{let c=JSON.parse(e.data);typeof c.subject=="string"&&n.push(c.subject)}catch{}let o=h(n);return[` <task_state count="${s.split(`
|
|
13
13
|
`).length}">`,s,m(r,o)," </task_state>"].join(`
|
|
14
14
|
`)}function X(t,r,s){if(t.length===0&&r.length===0)return"";let n=[],o=[];if(t.length>0){let e=t[t.length-1];n.push(` cwd: ${a(e.data)}`),o.push("working directory")}for(let e of r)n.push(` ${a(e.data)}`),o.push(e.data);let u=h(o);return[" <environment>",...n,m(s,u)," </environment>"].join(`
|
|
15
15
|
`)}function z(t,r){if(t.length===0)return"";let s=[],n=[];for(let i of t){let e=i.type==="subagent_completed"?"completed":i.type==="subagent_launched"?"launched":"unknown";s.push(` [${e}] ${a(i.data)}`),n.push(`subagent ${i.data}`)}let o=h(n);return[` <subagents count="${t.length}">`,...s,m(r,o)," </subagents>"].join(`
|
|
16
16
|
`)}function G(t,r){if(t.length===0)return"";let s=new Map;for(let e of t){let c=e.data.split(":")[0].trim();s.set(c,(s.get(c)??0)+1)}let n=[],o=[];for(let[e,c]of s)n.push(` ${a(e)} (${c}\xD7)`),o.push(`skill ${e} invocation`);let u=h(o);return[` <skills count="${t.length}">`,...n,m(r,u)," </skills>"].join(`
|
|
17
|
-
`)}function
|
|
17
|
+
`)}function P(t){if(t.length===0)return"";let r=t[t.length-1];return` <intent mode="${a(r.data)}"/>`}function V(t,r){let s=r?.compactCount??1,n=r?.searchTool??"ctx_search",o=new Date().toISOString(),u=[],i=[],e=[],c=[],l=[],b=[],p=[],g=[],S=[],y=[],k=[];for(let d of t)switch(d.category){case"file":u.push(d);break;case"task":i.push(d);break;case"rule":e.push(d);break;case"decision":c.push(d);break;case"cwd":l.push(d);break;case"error":b.push(d);break;case"env":p.push(d);break;case"git":g.push(d);break;case"subagent":S.push(d);break;case"intent":y.push(d);break;case"skill":k.push(d);break}let f=[];f.push(` <how_to_search>
|
|
18
18
|
Each section below contains a summary of prior work.
|
|
19
19
|
For FULL DETAILS, run the exact tool call shown under each section.
|
|
20
20
|
Do NOT ask the user to re-explain prior work. Search first.
|
|
21
21
|
Do NOT invent your own queries \u2014 use the ones provided.
|
|
22
|
-
</how_to_search>`);let $=
|
|
22
|
+
</how_to_search>`);let $=A(u,n);$&&f.push($);let v=x(b,n);v&&f.push(v);let w=D(c,n);w&&f.push(w);let E=F(e,n);E&&f.push(E);let q=R(g,n);q&&f.push(q);let L=J(i,n);L&&f.push(L);let _=X(l,p,n);_&&f.push(_);let j=z(S,n);j&&f.push(j);let T=G(k,n);T&&f.push(T);let C=P(y);C&&f.push(C);let O=`<session_resume events="${t.length}" compact_count="${s}" generated_at="${o}">`,I="</session_resume>",N=f.join(`
|
|
23
23
|
|
|
24
|
-
`);return
|
|
24
|
+
`);return N?`${O}
|
|
25
25
|
|
|
26
|
-
${
|
|
26
|
+
${N}
|
|
27
27
|
|
|
28
|
-
${
|
|
29
|
-
${
|
|
28
|
+
${I}`:`${O}
|
|
29
|
+
${I}`}export{V as buildResumeSnapshot,B as renderTaskState};
|
package/insight/server.mjs
CHANGED
|
@@ -28,18 +28,28 @@ if (isBun) {
|
|
|
28
28
|
} else {
|
|
29
29
|
try {
|
|
30
30
|
Database = (await import("better-sqlite3")).default;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
// Verify native addon loads correctly (catches arch mismatch: x86_64 vs arm64)
|
|
32
|
+
const testDb = new Database(":memory:");
|
|
33
|
+
testDb.close();
|
|
34
|
+
} catch (err) {
|
|
35
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
36
|
+
console.error("\n Error: better-sqlite3 failed to load.");
|
|
37
|
+
console.error(` ${msg}`);
|
|
38
|
+
if (msg.includes("incompatible architecture") || msg.includes("dlopen")) {
|
|
39
|
+
const cacheHint = process.env.INSIGHT_SESSION_DIR
|
|
40
|
+
? join(dirname(process.env.INSIGHT_SESSION_DIR), "insight-cache", "node_modules")
|
|
41
|
+
: join("~", ".claude", "context-mode", "insight-cache", "node_modules");
|
|
42
|
+
console.error(`\n Fix: rm -rf ${cacheHint} && context-mode insight`);
|
|
43
|
+
} else {
|
|
44
|
+
console.error(" Install it: npm install better-sqlite3");
|
|
45
|
+
}
|
|
35
46
|
process.exit(1);
|
|
36
47
|
}
|
|
37
48
|
}
|
|
38
49
|
|
|
39
50
|
// ── Paths ────────────────────────────────────────────────
|
|
40
|
-
const
|
|
41
|
-
const CONTENT_DIR = join(
|
|
42
|
-
const SESSION_DIR = join(BASE, "sessions");
|
|
51
|
+
const SESSION_DIR = process.env.INSIGHT_SESSION_DIR || join(homedir(), ".claude", "context-mode", "sessions");
|
|
52
|
+
const CONTENT_DIR = process.env.INSIGHT_CONTENT_DIR || join(homedir(), ".claude", "context-mode", "content");
|
|
43
53
|
const DIST_DIR = join(__dirname, "dist");
|
|
44
54
|
|
|
45
55
|
// ── SQLite helpers ───────────────────────────────────────
|
|
@@ -103,6 +113,11 @@ function mergeByKey(arr, key, mergeFn) {
|
|
|
103
113
|
return [...map.values()];
|
|
104
114
|
}
|
|
105
115
|
|
|
116
|
+
// ── Input validation ────────────────────────────────────
|
|
117
|
+
function isValidHash(hash) {
|
|
118
|
+
return /^[a-f0-9_]+$/.test(hash);
|
|
119
|
+
}
|
|
120
|
+
|
|
106
121
|
// ── API Handlers ─────────────────────────────────────────
|
|
107
122
|
|
|
108
123
|
function apiOverview() {
|
|
@@ -530,6 +545,7 @@ function route(method, pathname, params) {
|
|
|
530
545
|
|
|
531
546
|
if (pathname.startsWith("/api/content/") && pathname.includes("/chunks/")) {
|
|
532
547
|
const parts = pathname.split("/");
|
|
548
|
+
if (!isValidHash(parts[3])) return { error: "invalid hash" };
|
|
533
549
|
return apiSourceChunks(parts[3], Number(parts[5]));
|
|
534
550
|
}
|
|
535
551
|
if (pathname === "/api/search") {
|
|
@@ -539,10 +555,12 @@ function route(method, pathname, params) {
|
|
|
539
555
|
}
|
|
540
556
|
if (pathname.startsWith("/api/sessions/") && pathname.includes("/events/")) {
|
|
541
557
|
const parts = pathname.split("/");
|
|
558
|
+
if (!isValidHash(parts[3])) return { error: "invalid hash" };
|
|
542
559
|
return apiSessionEvents(parts[3], decodeURIComponent(parts[5]));
|
|
543
560
|
}
|
|
544
561
|
if (method === "DELETE" && pathname.startsWith("/api/content/")) {
|
|
545
562
|
const parts = pathname.split("/");
|
|
563
|
+
if (!isValidHash(parts[3])) return { error: "invalid hash" };
|
|
546
564
|
return apiDeleteSource(parts[3], Number(parts[5]));
|
|
547
565
|
}
|
|
548
566
|
return null;
|
package/openclaw.plugin.json
CHANGED
|
@@ -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.
|
|
6
|
+
"version": "1.0.89",
|
|
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.
|
|
3
|
+
"version": "1.0.89",
|
|
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",
|