context-mode 1.0.126 → 1.0.127
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/server.js +7 -0
- package/build/util/project-dir.d.ts +13 -0
- package/build/util/project-dir.js +11 -2
- package/cli.bundle.mjs +3 -3
- package/hooks/core/routing.mjs +114 -22
- package/hooks/gemini-cli/sessionstart.mjs +8 -6
- package/hooks/security.bundle.mjs +1 -0
- package/hooks/sessionstart.mjs +18 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +3 -3
- package/scripts/plugin-cache-integrity.mjs +101 -21
- package/server.bundle.mjs +75 -75
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Claude Code plugins by Mert Koseoğlu",
|
|
9
|
-
"version": "1.0.
|
|
9
|
+
"version": "1.0.127"
|
|
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.
|
|
16
|
+
"version": "1.0.127",
|
|
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.
|
|
3
|
+
"version": "1.0.127",
|
|
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.
|
|
6
|
+
"version": "1.0.127",
|
|
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.
|
|
3
|
+
"version": "1.0.127",
|
|
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
|
@@ -198,6 +198,12 @@ function getProjectDir() {
|
|
|
198
198
|
// path on detected platform so non-Claude hosts skip the heuristic and
|
|
199
199
|
// fall through to PWD/cwd cleanly.
|
|
200
200
|
//
|
|
201
|
+
// The Claude heuristic must also be fresh. Hosts such as Pi can be
|
|
202
|
+
// misdetected as Claude Code solely because ~/.claude exists; without a
|
|
203
|
+
// freshness guard an old Claude transcript can globally hijack ctx shell cwd
|
|
204
|
+
// after reboot. Active Claude sessions update their transcript as the user
|
|
205
|
+
// interacts, so stale transcripts should fall through to PWD/cwd.
|
|
206
|
+
//
|
|
201
207
|
// Issue #545 (v1.0.124): pass strictPlatform for ALL adapters so the
|
|
202
208
|
// env-var cascade is built ALGORITHMICALLY from the platform's own
|
|
203
209
|
// workspace vars + universal escape hatch — foreign workspace vars (e.g.
|
|
@@ -220,6 +226,7 @@ function getProjectDir() {
|
|
|
220
226
|
cwd: process.cwd(),
|
|
221
227
|
pwd: process.env.PWD,
|
|
222
228
|
transcriptsRoot,
|
|
229
|
+
transcriptMaxAgeMs: 5 * 60 * 1000,
|
|
223
230
|
strictPlatform,
|
|
224
231
|
});
|
|
225
232
|
}
|
|
@@ -50,6 +50,15 @@ export declare function isPluginInstallPath(p: string): boolean;
|
|
|
50
50
|
*/
|
|
51
51
|
export declare function resolveProjectDirFromTranscript(opts: {
|
|
52
52
|
projectsRoot: string;
|
|
53
|
+
/**
|
|
54
|
+
* Optional freshness guard. Claude Code updates the active transcript while
|
|
55
|
+
* the session is being used; stale transcripts from previous days must not
|
|
56
|
+
* become a global project-dir signal for other hosts that merely have
|
|
57
|
+
* ~/.claude on disk.
|
|
58
|
+
*/
|
|
59
|
+
maxAgeMs?: number;
|
|
60
|
+
/** Test seam for maxAgeMs. Defaults to Date.now(). */
|
|
61
|
+
nowMs?: number;
|
|
53
62
|
}): string | undefined;
|
|
54
63
|
/**
|
|
55
64
|
* Pure project-dir resolver. Mirror of the env-var chain inside
|
|
@@ -77,6 +86,10 @@ export declare function resolveProjectDir(opts: {
|
|
|
77
86
|
pwd: string | undefined;
|
|
78
87
|
/** Optional override; production code passes `~/.claude/projects`. */
|
|
79
88
|
transcriptsRoot?: string;
|
|
89
|
+
/** Optional freshness guard for Claude Code transcript project recovery. */
|
|
90
|
+
transcriptMaxAgeMs?: number;
|
|
91
|
+
/** Test seam for transcriptMaxAgeMs. Defaults to Date.now(). */
|
|
92
|
+
nowMs?: number;
|
|
80
93
|
/**
|
|
81
94
|
* Issue #545 — opt-in tightening. When set, the candidate list is built
|
|
82
95
|
* algorithmically from `workspaceEnvVarsFor(strictPlatform)` plus the
|
|
@@ -124,6 +124,11 @@ export function resolveProjectDirFromTranscript(opts) {
|
|
|
124
124
|
}
|
|
125
125
|
if (!bestPath)
|
|
126
126
|
return undefined;
|
|
127
|
+
if (typeof opts.maxAgeMs === "number") {
|
|
128
|
+
const nowMs = opts.nowMs ?? Date.now();
|
|
129
|
+
if (nowMs - bestMtime > opts.maxAgeMs)
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
127
132
|
// Read first ~10 lines until we find a cwd field. The jsonl is
|
|
128
133
|
// append-only and can be huge (60+ MB on long sessions) — never load it
|
|
129
134
|
// into memory; stream a small head buffer.
|
|
@@ -172,7 +177,7 @@ export function resolveProjectDirFromTranscript(opts) {
|
|
|
172
177
|
* operation of project-independent tools (sandbox execute, fetch).
|
|
173
178
|
*/
|
|
174
179
|
export function resolveProjectDir(opts) {
|
|
175
|
-
const { env, cwd, pwd, transcriptsRoot, strictPlatform } = opts;
|
|
180
|
+
const { env, cwd, pwd, transcriptsRoot, transcriptMaxAgeMs, nowMs, strictPlatform } = opts;
|
|
176
181
|
// Build candidate list. Strict path: own workspace vars + universal escape
|
|
177
182
|
// hatch — NO foreign workspace vars, in any order, can win. Non-strict
|
|
178
183
|
// path: frozen legacy literal order for backwards compatibility.
|
|
@@ -185,7 +190,11 @@ export function resolveProjectDir(opts) {
|
|
|
185
190
|
return v;
|
|
186
191
|
}
|
|
187
192
|
if (transcriptsRoot) {
|
|
188
|
-
const fromTranscript = resolveProjectDirFromTranscript({
|
|
193
|
+
const fromTranscript = resolveProjectDirFromTranscript({
|
|
194
|
+
projectsRoot: transcriptsRoot,
|
|
195
|
+
maxAgeMs: transcriptMaxAgeMs,
|
|
196
|
+
nowMs,
|
|
197
|
+
});
|
|
189
198
|
if (fromTranscript && !isPluginInstallPath(fromTranscript))
|
|
190
199
|
return fromTranscript;
|
|
191
200
|
}
|
package/cli.bundle.mjs
CHANGED
|
@@ -524,8 +524,8 @@ ${s}`}}var VE=b(()=>{"use strict"});import{execFileSync as hF}from"node:child_pr
|
|
|
524
524
|
`)}}return o.slice(0,e)}function d$(t,e){return e?OF(e)||!t?e:ra(t,e):t??""}var l$,m$=b(()=>{"use strict";Xr();l$=process.env.DEBUG?.includes("context-mode")});function f$(t){let{query:e,limit:r,store:n,sort:s="relevance",source:o,contentType:i,sessionDB:a,projectDir:c,configDir:u,adapter:d}=t,l=[],p=new Date().toISOString();try{let f=n.searchWithFallback(e,r,o,i);l.push(...f.map(m=>({title:m.title,content:m.content,source:m.source,origin:"current-session",timestamp:m.timestamp||p,rank:m.rank,matchLayer:m.matchLayer,highlighted:m.highlighted,contentType:m.contentType})))}catch(f){wy&&process.stderr.write(`[ctx] ContentStore search failed: ${f}
|
|
525
525
|
`)}if(s==="timeline"){try{if(a){let f=a.searchEvents(e,r,c||"",o);l.push(...f.map(m=>({title:`[${m.category}] ${m.type}`,content:m.data,source:"prior-session",origin:"prior-session",timestamp:m.created_at})))}}catch(f){wy&&process.stderr.write(`[ctx] SessionDB search failed: ${f}
|
|
526
526
|
`)}try{let f=p$([e],r,c,u,d);l.push(...f)}catch(f){wy&&process.stderr.write(`[ctx] auto-memory search failed: ${f}
|
|
527
|
-
`)}}for(let f of l)f.timestamp&&!f.timestamp.includes("T")&&(f.timestamp=f.timestamp.replace(" ","T")+"Z");return s==="timeline"&&l.sort((f,m)=>(f.timestamp||"").localeCompare(m.timestamp||"")),l.slice(0,r)}var wy,h$=b(()=>{"use strict";m$();wy=process.env.DEBUG?.includes("context-mode")});import*as Rt from"node:fs";import*as $y from"node:path";function Ey(t){return t?/[/\\]\.claude[/\\]plugins[/\\](cache|marketplaces)[/\\]/.test(t):!1}function NF(t){if(!Rt.existsSync(t.projectsRoot))return;let e,r=0;try{for(let n of Rt.readdirSync(t.projectsRoot)){let s=$y.join(t.projectsRoot,n),o;try{o=Rt.statSync(s)}catch{continue}if(!o.isDirectory())continue;let i;try{i=Rt.readdirSync(s)}catch{continue}for(let a of i){if(!a.endsWith(".jsonl"))continue;let c=$y.join(s,a);try{let u=Rt.statSync(c).mtimeMs;u>r&&(r=u,e=c)}catch{}}}}catch{return}if(e)try{let n=Rt.openSync(e,"r");try{let s=Buffer.alloc(8192),o=Rt.readSync(n,s,0,s.length,0),i=s.subarray(0,o).toString("utf-8");for(let a of i.split(`
|
|
528
|
-
`).slice(0,10))if(a.trim())try{let c=JSON.parse(a);if(typeof c.cwd=="string"&&c.cwd.length>0)return c.cwd}catch{}}finally{Rt.closeSync(n)}}catch{}}function g$(t){let{env:e,cwd:r,pwd:n,transcriptsRoot:s,strictPlatform:
|
|
527
|
+
`)}}for(let f of l)f.timestamp&&!f.timestamp.includes("T")&&(f.timestamp=f.timestamp.replace(" ","T")+"Z");return s==="timeline"&&l.sort((f,m)=>(f.timestamp||"").localeCompare(m.timestamp||"")),l.slice(0,r)}var wy,h$=b(()=>{"use strict";m$();wy=process.env.DEBUG?.includes("context-mode")});import*as Rt from"node:fs";import*as $y from"node:path";function Ey(t){return t?/[/\\]\.claude[/\\]plugins[/\\](cache|marketplaces)[/\\]/.test(t):!1}function NF(t){if(!Rt.existsSync(t.projectsRoot))return;let e,r=0;try{for(let n of Rt.readdirSync(t.projectsRoot)){let s=$y.join(t.projectsRoot,n),o;try{o=Rt.statSync(s)}catch{continue}if(!o.isDirectory())continue;let i;try{i=Rt.readdirSync(s)}catch{continue}for(let a of i){if(!a.endsWith(".jsonl"))continue;let c=$y.join(s,a);try{let u=Rt.statSync(c).mtimeMs;u>r&&(r=u,e=c)}catch{}}}}catch{return}if(e&&!(typeof t.maxAgeMs=="number"&&(t.nowMs??Date.now())-r>t.maxAgeMs))try{let n=Rt.openSync(e,"r");try{let s=Buffer.alloc(8192),o=Rt.readSync(n,s,0,s.length,0),i=s.subarray(0,o).toString("utf-8");for(let a of i.split(`
|
|
528
|
+
`).slice(0,10))if(a.trim())try{let c=JSON.parse(a);if(typeof c.cwd=="string"&&c.cwd.length>0)return c.cwd}catch{}}finally{Rt.closeSync(n)}}catch{}}function g$(t){let{env:e,cwd:r,pwd:n,transcriptsRoot:s,transcriptMaxAgeMs:o,nowMs:i,strictPlatform:a}=t,c=a?[...Ad(a),...IF]:AF;for(let u of c){let d=e[u];if(d&&!Ey(d))return d}if(s){let u=NF({projectsRoot:s,maxAgeMs:o,nowMs:i});if(u&&!Ey(u))return u}return n&&!Ey(n)?n:r}var IF,AF,y$=b(()=>{"use strict";Dn();IF=["CONTEXT_MODE_PROJECT_DIR"],AF=["CLAUDE_PROJECT_DIR","GEMINI_PROJECT_DIR","VSCODE_CWD","OPENCODE_PROJECT_DIR","PI_PROJECT_DIR","IDEA_INITIAL_DIRECTORY","CURSOR_CWD","CONTEXT_MODE_PROJECT_DIR"]});import{execFileSync as DF}from"node:child_process";import{existsSync as ys,readdirSync as wo,statSync as MF}from"node:fs";import{homedir as el}from"node:os";import{join as Dt,sep as jF}from"node:path";function E$(t,e){let r=t.split(".").map(Number),n=e.split(".").map(Number);for(let s=0;s<3;s++){if((r[s]??0)>(n[s]??0))return!0;if((r[s]??0)<(n[s]??0))return!1}return!1}function LF(t){let e=t?.home??el();return[["claude-code",[".claude"]],["gemini-cli",[".gemini"]],["antigravity",[".gemini"]],["openclaw",[".openclaw"]],["codex",[".codex"]],["cursor",[".cursor"]],["vscode-copilot",[".vscode"]],["kiro",[".kiro"]],["pi",[".pi"]],["omp",[".omp"]],["qwen-code",[".qwen"]],["kilo",[".config","kilo"]],["opencode",[".config","opencode"]],["zed",[".config","zed"]],["jetbrains-copilot",[".config","JetBrains"]]].map(([n,s])=>{let o=Dt(e,...s,"context-mode");return{name:n,sessionsDir:Dt(o,"sessions"),contentDir:Dt(o,"content")}})}function FF(t){let r=t.replace(/\.md$/i,"").match(/^([a-z]+)/i);return r?r[1].toLowerCase():"other"}function na(t){let e=Ze(),r=t?.sessionsDir??Dt(e,"context-mode","sessions"),n=t?.memoryRoot??Dt(e,"projects"),s=0,o=0,i=0,a=Number.POSITIVE_INFINITY,c=new Set,u={};if(ys(r)){let f=[];try{f=wo(r).filter(m=>m.endsWith(".db"))}catch{}if(f.length>0){let m=null;try{m=t?.loadDatabase?t.loadDatabase():Pt()}catch{}if(m)for(let h of f){let g=Dt(r,h);try{let _=new m(g,{readonly:!0});try{let x=_.prepare("SELECT COUNT(*) AS cnt FROM session_events").get(),y=_.prepare("SELECT COUNT(*) AS cnt FROM session_meta").get();s+=x?.cnt??0,o+=y?.cnt??0;try{let v=_.prepare("SELECT category, COUNT(*) AS cnt FROM session_events GROUP BY category").all();for(let k of v)k.category&&(u[k.category]=(u[k.category]??0)+(k.cnt??0))}catch{}try{let v=_.prepare("SELECT COALESCE(SUM(length(snapshot)), 0) AS bytes FROM session_resume WHERE consumed = 1").get();v?.bytes&&(i+=v.bytes)}catch{}try{let v=_.prepare("SELECT MIN(created_at) AS t FROM session_events").get();if(v?.t){let k=v.t.endsWith("Z")?v.t:v.t+"Z",R=Date.parse(k);Number.isFinite(R)&&R<a&&(a=R)}}catch{}try{let v=_.prepare("SELECT DISTINCT project_dir AS p FROM session_events WHERE project_dir != ''").all();for(let k of v)k.p&&c.add(k.p)}catch{}}finally{_.close()}}catch{}}}}let d=0,l=0,p={};if(ys(n)){let f=[];try{f=wo(n).filter(m=>{try{return MF(Dt(n,m)).isDirectory()}catch{return!1}})}catch{}for(let m of f){let h=Dt(n,m,"memory");if(!ys(h))continue;let g=[];try{g=wo(h).filter(_=>_.endsWith(".md"))}catch{continue}if(g.length!==0){l++,d+=g.length;for(let _ of g){let x=FF(_);p[x]=(p[x]??0)+1}}}}return{totalEvents:s,totalSessions:o,autoMemoryCount:d,autoMemoryProjects:l,autoMemoryByPrefix:p,categoryCounts:u,rescueBytes:i,firstEventMs:Number.isFinite(a)?a:0,distinctProjects:c.size}}function $$(t){let e=t.sessionsDir??Dt(el(),".claude","context-mode","sessions"),r=t.sessionId,n={sessionId:r,events:0,dbCount:0,daysAlive:0,snapshotBytes:0,snapshotsConsumed:0,byCategory:[]};if(!r||!ys(e))return n;let s=[];try{s=wo(e).filter(y=>!(!y.endsWith(".db")||t.worktreeHash&&!y.startsWith(t.worktreeHash)))}catch{return n}if(s.length===0)return n;let o=null;try{o=t.loadDatabase?t.loadDatabase():Pt()}catch{return n}if(!o)return n;let i={},a=0,c=0,u=0,d=0,l=Number.POSITIVE_INFINITY,p=0,f=0,m=new Map,h=y=>Math.floor(y/864e5)*864e5;for(let y of s){let v=Dt(e,y),k=!1;try{let R=new o(v,{readonly:!0});try{let $=R.prepare("SELECT category, COUNT(*) AS cnt FROM session_events WHERE session_id = ? GROUP BY category").all(r);for(let j of $)j.category&&(i[j.category]=(i[j.category]??0)+(j.cnt??0),a+=j.cnt??0,k=!0);let P=R.prepare("SELECT MIN(created_at) AS mn, MAX(created_at) AS mx FROM session_events WHERE session_id = ?").get(r);if(P?.mn){let j=Date.parse(P.mn+(P.mn.endsWith("Z")?"":"Z"));Number.isFinite(j)&&j<l&&(l=j)}if(P?.mx){let j=Date.parse(P.mx+(P.mx.endsWith("Z")?"":"Z"));Number.isFinite(j)&&j>p&&(p=j)}try{let j=R.prepare("SELECT strftime('%s', created_at) AS sec, COUNT(*) AS cnt FROM session_events WHERE session_id = ? GROUP BY date(created_at)").all(r);for(let T of j){if(!T.sec)continue;let C=parseInt(T.sec,10)*1e3;if(!Number.isFinite(C))continue;let L=h(C),X=m.get(L)??{count:0,rescueBytes:0};X.count+=T.cnt??0,m.set(L,X)}}catch{}try{let j=R.prepare("SELECT COALESCE(SUM(length(snapshot)), 0) AS bytes, COUNT(*) AS n, MAX(strftime('%s', created_at)) AS lastSec FROM session_resume WHERE session_id = ? AND consumed = 1").get(r);if(j?.bytes&&(u+=j.bytes),j?.n&&(d+=j.n),j?.lastSec){let T=parseInt(j.lastSec,10)*1e3;if(Number.isFinite(T)&&T>f&&(f=T),Number.isFinite(T)&&(j?.bytes??0)>0){let C=h(T),L=m.get(C)??{count:0,rescueBytes:0};L.rescueBytes=Math.max(L.rescueBytes,j.bytes),m.set(C,L)}}}catch{}}finally{R.close()}}catch{}k&&c++}let g=l<p?(p-l)/864e5:0,_=Object.entries(i).filter(([,y])=>y>0).map(([y,v])=>({category:y,count:v,label:Yu[y]||y})).sort((y,v)=>v.count-y.count),x=[...m.entries()].sort((y,v)=>y[0]-v[0]).map(([y,v])=>({ms:y,count:v.count,...v.rescueBytes>0?{rescueBytes:v.rescueBytes}:{}}));return{sessionId:r,events:a,dbCount:c,daysAlive:g,snapshotBytes:u,snapshotsConsumed:d,byCategory:_,firstEventMs:Number.isFinite(l)?l:0,lastEventMs:p>0?p:0,lastRescueMs:f>0?f:void 0,byDay:x}}function Ty(t){let e={eventDataBytes:0,bytesAvoided:0,bytesReturned:0,snapshotBytes:0,totalSavedTokens:0},r=t.sessionsDir??Dt(el(),".claude","context-mode","sessions");if(!ys(r))return e;let n=[];try{n=wo(r).filter(d=>!(!d.endsWith(".db")||t.worktreeHash&&!d.startsWith(t.worktreeHash)))}catch{return e}if(n.length===0)return e;let s=null;try{s=t.loadDatabase?t.loadDatabase():Pt()}catch{return e}if(!s)return e;let o=0,i=0,a=0,c=0;for(let d of n){let l=Dt(r,d);try{let p=new s(l,{readonly:!0});try{if(t.sessionId){let f=p.prepare(`SELECT
|
|
529
529
|
COALESCE(SUM(LENGTH(data)), 0) AS data_bytes,
|
|
530
530
|
COALESCE(SUM(bytes_avoided), 0) AS bytes_avoided,
|
|
531
531
|
COALESCE(SUM(bytes_returned), 0) AS bytes_returned
|
|
@@ -536,7 +536,7 @@ ${s}`}}var VE=b(()=>{"use strict"});import{execFileSync as hF}from"node:child_pr
|
|
|
536
536
|
FROM session_events`).get();f&&(o+=Number(f.data_bytes??0),i+=Number(f.bytes_avoided??0),a+=Number(f.bytes_returned??0));try{let m=p.prepare("SELECT COALESCE(SUM(LENGTH(snapshot)), 0) AS bytes FROM session_resume").get();m?.bytes&&(c+=Number(m.bytes))}catch{}}}finally{p.close()}}catch{}}let u=Math.floor((o+i+c)/4);return{eventDataBytes:o,bytesAvoided:i,bytesReturned:a,snapshotBytes:c,totalSavedTokens:u}}function HF(t,e,r){let n={name:t.name,eventCount:0,sessionCount:0,dataBytes:0,rescueBytes:0,contentBytes:0,uuidConvs:0,projectDirs:[],firstMs:Number.POSITIVE_INFINITY,lastMs:0,isReal:!1};if(!ys(t.sessionsDir))return n;let s=[];try{s=wo(t.sessionsDir).filter(d=>d.endsWith(".db"))}catch{return n}if(s.length===0)return n;let o=null;try{o=e()}catch{return n}if(!o)return n;let i=new Set,a=new Set;for(let d of s){let l=Dt(t.sessionsDir,d);try{let p=new o(l,{readonly:!0});try{let f=p.prepare("SELECT COUNT(*) AS cnt, COALESCE(SUM(LENGTH(data)), 0) AS bytes FROM session_events").get();f&&(n.eventCount+=Number(f.cnt??0),n.dataBytes+=Number(f.bytes??0));try{let m=p.prepare("SELECT COUNT(*) AS cnt FROM session_meta").get();n.sessionCount+=Number(m?.cnt??0)}catch{}try{let m=p.prepare("SELECT COALESCE(SUM(length(snapshot)), 0) AS bytes FROM session_resume WHERE consumed = 1").get();m?.bytes&&(n.rescueBytes+=Number(m.bytes))}catch{}try{let m=p.prepare("SELECT MIN(created_at) AS mn, MAX(created_at) AS mx FROM session_events").get();if(m?.mn){let h=Date.parse(m.mn+(m.mn.endsWith("Z")?"":"Z"));Number.isFinite(h)&&h<n.firstMs&&(n.firstMs=h)}if(m?.mx){let h=Date.parse(m.mx+(m.mx.endsWith("Z")?"":"Z"));Number.isFinite(h)&&h>n.lastMs&&(n.lastMs=h)}}catch{}try{let m=p.prepare("SELECT DISTINCT project_dir AS p FROM session_events WHERE project_dir != ''").all();for(let h of m)h.p&&i.add(h.p)}catch{}try{let m=p.prepare("SELECT DISTINCT session_id AS s FROM session_events").all();for(let h of m)h.s&&a.add(h.s)}catch{}}finally{p.close()}}catch{}}n.projectDirs=Array.from(i),n.uuidConvs=a.size;let c=n.eventCount>0?n.dataBytes/n.eventCount:0,u=n.lastMs>0&&r.nowMs-n.lastMs<=r.recencyMs;return n.isReal=n.eventCount>=r.minEvents&&i.size>=r.minProjects&&u&&c>=r.minAvgBytes,n}function tl(t){let e=LF({home:t?.home}),r=t?.loadDatabase??Pt,n={...UF,...t?.filter??{},nowMs:t?.filter?.nowMs??Date.now()},s=[],o=0,i=0,a=0;for(let c of e){if(!ys(c.sessionsDir))continue;let u=HF(c,r,n);s.push(u),o+=u.eventCount,i+=u.sessionCount,a+=u.dataBytes+u.rescueBytes}return{totalEvents:o,totalSessions:i,totalBytes:a,perAdapter:s}}function Xu(t){return ZF[t]??t}function nt(t){if(!Number.isFinite(t)||t<=0)return"0 B";if(t<1024)return`${Math.round(t)} B`;let e=t/1024;if(e<1024)return e<100?`${e.toFixed(1)} KB`:`${Math.round(e)} KB`;let r=e/1024;if(r<1024)return r<100?`${r.toFixed(1)} MB`:`${Math.round(r)} MB`;let n=r/1024;return n<100?`${n.toFixed(2)} GB`:`${n.toFixed(1)} GB`}function BF(t){let e=parseFloat(t);if(isNaN(e)||e<1)return"< 1 min";if(e<60)return`${Math.round(e)} min`;let r=Math.floor(e/60),n=Math.round(e%60);return n>0?`${r}h ${n}m`:`${r}h`}function qF(){let t=process.env??{},e=t.CONTEXT_MODE_LOCALE??"";if(!e){if(process.platform==="darwin")try{let n=DF("defaults",["read","-g","AppleLocale"],{encoding:"utf8",timeout:500}).trim();n&&(e=n.replace(/_/g,"-"))}catch{}if(!e&&(t.LC_TIME||t.LANG)){let n=(t.LC_TIME||t.LANG||"").split(".")[0];n&&(e=n.replace(/_/g,"-"))}if(!e)try{e=new Intl.DateTimeFormat().resolvedOptions().locale}catch{e="en-US"}}let r=t.CONTEXT_MODE_TZ??"";if(!r)try{r=new Intl.DateTimeFormat().resolvedOptions().timeZone}catch{r="UTC"}return{locale:e||"en-US",tz:r||"UTC"}}function _$(t){let e=el();return e?t===e?"~":t.startsWith(e+jF)?"~"+t.slice(e.length):t:t}function VF(t,e,r){if(!Number.isFinite(e)||e<=0)return[];let n=e*15/1e6,s=(h,g=2)=>h.toFixed(g),o=Math.round(n/20),i=(n/200).toFixed(1),a=Math.round(n/73.67),c=Math.round(n*10),u=r>0?Math.round(n*10/r*365):0,d=(e*3/1e6).toFixed(2),l=(e*2.5/1e6).toFixed(2),p=(e*1.25/1e6).toFixed(2),f=(e*.8/1e6).toFixed(2),m=[];return m.push(` $${s(n)} of Opus 4 tokens your team didn't burn.`),m.push(` context-mode kept ${nt(t)} out of context \u2014 that's ${o} months of Cursor Pro paid for itself.`),c>0&&u>0&&(m.push(""),m.push(` Scale across a 10-dev team and that's ~$${u.toLocaleString("en-US")}/year saved.`)),m.push(""),m.push(" (Opus rates shown for context. On cheaper models the dollar number drops; the savings ratio holds.)"),m}function WF(t){let{conversation:e,lifetime:r,multiAdapter:n,realBytes:s,cwd:o,locale:i,tz:a,now:c,version:u,latestVersion:d}=t,l=[],p=e.events*k$,f=Math.round((e.snapshotBytes??0)/4),m=p+f,h=s?.conversation?.totalSavedTokens??0,g=Math.max(m,h),_=(r?.totalEvents??0)*k$,x=Math.round((r?.rescueBytes??0)/4),y=_+x,v=s?.lifetime?.totalSavedTokens??0,k=Math.max(y,v),R=Math.max(1,Math.round(k*.02)),$=n?.totalBytes&&n.totalBytes>0?n.totalBytes:k*4,P=s?.conversation?s.conversation.eventDataBytes+s.conversation.bytesAvoided+s.conversation.snapshotBytes:g*4,j=e.daysAlive>=1?`${e.daysAlive.toFixed(1)} days alive \xB7 still going`:`${Math.max(1,Math.round(e.daysAlive*24))} hr alive \xB7 still going`,T=r?.firstEventMs??n?.perAdapter?.[0]?.firstMs??0,C=T>0?Math.max(1,Math.round((c-T)/864e5)):0,L=n?.totalSessions??r?.totalSessions??1,X=n?.perAdapter.filter(qe=>qe.isReal).length??0,ae;if(n&&X>=2)ae=`across ${X} AI tools`;else if(n&&X===1){let qe=n.perAdapter.find(Kr=>Kr.isReal);ae=`in ${qe?Xu(qe.name):"Claude Code"}`}else ae="in Claude Code";C>0?l.push(` Across ${C} days you ran ${hr(L)} conversations ${ae}.`):l.push(` You ran ${hr(L)} conversations ${ae}.`);let yt=C>0?$/C:0;l.push(` context-mode kept ${nt($)} out of your context window \u2014 about ${nt(yt)} every single day.`),l.push(""),l.push(""),l.push(" \u2500\u2500\u2500 1. Where you are now \u2500\u2500\u2500"),l.push("");let jt=e.firstEventMs&&e.firstEventMs>0?v$(e.firstEventMs,i,a):"";if(jt?l.push(` This conversation started ${jt} in ${_$(o)}.`):l.push(` This conversation lives in ${_$(o)}.`),l.push(` ${j}.`),e.snapshotsConsumed>0&&e.snapshotBytes>0){let qe=e.lastRescueMs&&e.lastRescueMs>0?v$(e.lastRescueMs,i,a):"",Kr=Math.round(e.snapshotBytes/1024);qe?l.push(` On ${qe}, /compact fired \u2014 ${Kr} KB rescued from snapshot.`):l.push(` /compact fired \u2014 ${Kr} KB rescued from snapshot.`),l.push(" Without that, you'd be re-explaining everything to a blank model right now.")}l.push("");let _t=Math.max(1,Math.round(g*.02)),bs=kn(g,g,32),yl=kn(_t,g,32),_l=g>0?(1-_t/g)*100:0;if(l.push(` Without context-mode ${nt(P).padStart(8)} ${bs} ${hr(g).padStart(7)} tokens`),l.push(` With context-mode ${nt(Math.max(1,Math.round(P*.02))).padStart(8)} ${yl} ${hr(_t).padStart(7)} tokens`),l.push(` ${_l.toFixed(0)}% kept out of context \xB7 your AI ran ${Math.max(1,Math.round(g/_t))}\xD7 longer before /compact fired`),l.push(""),e.byDay&&e.byDay.length>0){let qe=e.lastEventMs&&e.firstEventMs?Math.max(1,Math.round((e.lastEventMs-e.firstEventMs)/864e5)+1):e.byDay.length;l.push(` How that ${nt(P)} built up \u2014 ${qe} days, ${e.byDay.length} active:`),l.push(""),l.push(...KF(e.byDay,i,a))}l.push(""),l.push(""),l.push(" \u2500\u2500\u2500 2. What this chat captured (used when you --continue or /resume here) \u2500\u2500\u2500"),l.push("");let pT=e.byCategory.reduce((qe,Kr)=>qe+Kr.count,0).toLocaleString(i);l.push(` ${pT} things \u2014 files, errors, decisions, agent runs:`),l.push("");let mT=e.byCategory[0]?.count??1;for(let qe of e.byCategory)l.push(` ${qe.label.padEnd(26)} ${String(qe.count).padStart(5)} ${kn(qe.count,mT,28)}`);l.push(""),l.push(""),l.push(" \u2500\u2500\u2500 3. The scope, getting wider \u2500\u2500\u2500"),l.push("");let By=e.firstEventMs&&e.firstEventMs>0?new Intl.DateTimeFormat(i,{timeZone:a,year:"numeric",month:"short",day:"numeric"}).format(new Date(e.firstEventMs)):"",qy=T>0?new Intl.DateTimeFormat(i,{timeZone:a,year:"numeric",month:"short",day:"numeric"}).format(new Date(T)):"",Vy=r?.distinctProjects??0,fT=r?.totalEvents??n?.totalEvents??0;if(l.push(` This chat: ${nt(P)} kept out \xB7 ${e.events.toLocaleString(i)} captures${By?` \xB7 started ${By}`:""}.`),l.push(` All your work: ${nt($)} kept out \xB7 ${fT.toLocaleString(i)} captures across ${Vy} project${Vy===1?"":"s"}${qy?` \xB7 since ${qy}`:""}.`),l.push(""),l.push(""),l.push(" \u2500\u2500\u2500 4. The bottom line \u2500\u2500\u2500"),l.push(""),l.push(...VF($,k,C)),l.push(""),l.push(""),l.push(" \u2500\u2500\u2500 5. What context-mode learned about how you work \u2500\u2500\u2500"),l.push(""),r&&r.autoMemoryCount>0){l.push(` ${r.autoMemoryCount} preferences picked up across ${r.autoMemoryProjects} project${r.autoMemoryProjects===1?"":"s"}:`);let qe=Object.entries(r.autoMemoryByPrefix).sort((pa,ma)=>ma[1]-pa[1]),Kr=qe.length>0?qe[0][1]:1;for(let[pa,ma]of qe){let gT=T$[pa]??pa;l.push(` ${gT.padEnd(26)} ${String(ma).padStart(2)} ${kn(ma,Kr,20)}`)}}else l.push(" No preferences learned yet \u2014 context-mode picks them up automatically.");l.push(""),l.push(""),l.push(" Your AI talks less, remembers more, costs less."),l.push(` Locale ${i} \xB7 timezone ${a} \xB7 pricing examples for illustration only.`),l.push("");let hT=u?`v${u}`:"context-mode";return l.push(` ${hT}`),u&&d&&d!=="unknown"&&E$(d,u)&&l.push(` Update available: v${u} -> v${d} | ctx_upgrade`),GF(l)}function GF(t){let e=[],r=0;for(let n of t)n===""?(r++,r<=2&&e.push(n)):(r=0,e.push(n));for(;e.length>0&&e[e.length-1]==="";)e.pop();return e}function KF(t,e,r){if(t.length===0)return[];let n=[...t].sort((p,f)=>p.ms-f.ms),s=n[0],o=n[n.length-1],i=Math.max(1,o.ms-s.ms),a=n[0];for(let p of n)p.count>a.count&&(a=p);let c=56,u=Array.from({length:c},()=>"\u2500");for(let p of n){let f=Math.round((p.ms-s.ms)/i*(c-1)),m="\u25CF";p===a&&(m="\u2588"),(p.rescueBytes??0)>0&&(m="\u25C6"),u[f]=m}let d=p=>{let f=new Intl.DateTimeFormat(e,{timeZone:r,month:"short",day:"numeric"}).formatToParts(new Date(p)),m=(f.find(g=>g.type==="month")?.value??"").toLowerCase(),h=f.find(g=>g.type==="day")?.value??"";return`${m} ${h}`},l=[];l.push(` ${d(s.ms)} ${u.join("")} ${d(o.ms)}`),l.push("");for(let p of n){let f=d(p.ms).padEnd(7),m=`${p.count} captures`,h=p===a?" \u2190 peak":"",g=(p.rescueBytes??0)>0?` \u25C6 /compact rescued ${Math.round((p.rescueBytes??0)/1024)} KB`:"";l.push(` ${f} ${m}${h}${g}`)}return l.push(""),l.push(" \u25CF active day \u2588 peak day \u25C6 /compact rescue"),l}function v$(t,e,r){if(!Number.isFinite(t)||t<=0)return"";let n=new Date(t);if(Number.isNaN(n.getTime()))return"";let s=new Intl.DateTimeFormat(e,{timeZone:r,year:"numeric",month:"short",day:"numeric",hour:"2-digit",minute:"2-digit",hour12:!1}).formatToParts(n),o=l=>s.find(p=>p.type===l)?.value??"",i=o("day"),a=o("month"),c=o("year"),u=o("hour"),d=o("minute");return u==="24"&&(u="00"),`${i} ${a} ${c} at ${u}:${d} (${r})`}function hr(t){return t>=1e6?`${(t/1e6).toFixed(1)}M`:t>=1e3?`${(t/1e3).toFixed(1)}K`:String(t)}function Qu(t){return`$${((Number.isFinite(t)&&t>0?t:0)*rl).toFixed(2)}`}function kn(t,e,r=40){if(e<=0)return"\u2591".repeat(r);let n=Math.max(1,Math.round(t/e*r));return"\u2588".repeat(Math.min(n,r))+"\u2591".repeat(Math.max(0,r-n))}function x$(t,e){let r=e?.sessionTokensSaved??0;if(t.total_events===0&&(e?.lifetime?.totalEvents??0)===0&&r===0&&(e?.multiAdapter?.totalEvents??0)===0)return[];let n=e?.topN??Number.POSITIVE_INFINITY,s=[];s.push("");let o=e?.multiAdapter,i=o?.perAdapter.filter(h=>h.isReal).length??0,a=o?.totalEvents??e?.lifetime?.totalEvents??t.total_events,c=o?.totalSessions??e?.lifetime?.totalSessions??t.session_count,u=e?.lifetime?.distinctProjects;if(a>0&&u&&u>0){let h=i>=2?" everywhere":"";s.push(` All your work${h} \xB7 ${hr(a)} events captured across ${u} project${u===1?"":"s"} \xB7 ${hr(c)} conversations`)}else{s.push("Persistent memory \u2713 preserved across compact, restart & upgrade");let h=c===0&&r>0?1:c,g=h===1?"1 session":`${hr(h)} sessions`,_=a*256+r;s.push(` ${hr(a)} events \xB7 ${g} \xB7 ~${Qu(_)} saved lifetime`)}s.push("");let d=e?.lifetime?.categoryCounts,l;d&&Object.keys(d).length>0?l=Object.entries(d).filter(([,h])=>h>0).map(([h,g])=>({category:h,count:g,label:Yu[h]||h})).sort((h,g)=>g.count-h.count):l=(t.by_category??[]).filter(h=>h&&h.count>0);let p=l.slice(0,n),f=p.length>0?p[0].count:1;for(let h of p)s.push(` ${h.label.padEnd(26)} ${String(h.count).padStart(5)} ${kn(h.count,f,30)}`);let m=Math.max(0,l.length-n);return m>0&&s.push(` ... ${m} more categor${m===1?"y":"ies"}`),s}function b$(t){if(!t||t.autoMemoryCount===0)return[];let e=[];e.push(""),e.push(` Preferences learned \xB7 ${t.autoMemoryCount} across ${t.autoMemoryProjects} project${t.autoMemoryProjects===1?"":"s"}`);let r=Object.entries(t.autoMemoryByPrefix).sort((s,o)=>o[1]-s[1]).slice(0,6),n=r.length>0?r[0][1]:1;for(let[s,o]of r){let i=T$[s]??s;e.push(` ${i.padEnd(26)} ${String(o).padStart(2)} ${kn(o,n,20)}`)}return e}function S$(t,e){let r=[],n=Qu(t),s=(e?.totalEvents??0)*256+t,o=Qu(s);return r.push(""),r.push("\u2500".repeat(65)),r.push("Your AI talks less, remembers more, costs less."),r.push(`${n} this session \xB7 ${o} lifetime`),r.push("\u2500".repeat(65)),r}function w$(t){if(!t)return[];let e=t.perAdapter.filter(s=>s.isReal),r=t.perAdapter.filter(s=>!s.isReal);if(e.length===0&&r.length===0)return[];let n=[];if(e.length>0){n.push(""),n.push("Where it came from (tools you actually used \u2014 fixtures + probes filtered):"),n.push("");let s=16,o=10,i=10,a=16;n.push(` ${"Tool".padEnd(s)}${"Captures".padStart(o)}${"Indexed".padStart(i)}${"Total kept out".padStart(a)}`);let c=[...e].sort((u,d)=>d.dataBytes+d.rescueBytes-(u.dataBytes+u.rescueBytes));for(let u of c){let d=u.dataBytes+u.rescueBytes,l=u.eventCount>0?hr(u.eventCount):"\u2014",p=nt(u.dataBytes),f=nt(d);n.push(` ${Xu(u.name).padEnd(s)}${l.padStart(o)}${p.padStart(i)}${f.padStart(a)}`)}}if(r.length>0){e.length>0&&n.push("");let s=r.map(o=>Xu(o.name)).join(", ");n.push(` Skipped (${r.length}): ${s}`),n.push(" These adapters have DBs on disk but only test fixtures, dev skeletons,"),n.push(" or detection probes \u2014 no real chat activity.")}return n}function nl(t,e,r,n){let s=[],o=BF(t.session.uptime_min),i=n?.lifetime,a=n?.mcpUsage,c=n?.conversation,u=n?.realBytes,d=n?.multiAdapter,l=d?.perAdapter.filter(R=>R.isReal).length??0;if(d&&l>0){let R=d.totalSessions||i?.totalSessions||0,$=i?.firstEventMs??0,P=$>0?Math.max(1,Math.round((Date.now()-$)/864e5)):0,j=P>0?`Across ${P} day${P===1?"":"s"} `:"",T=R>0?`you ran ${hr(R)} conversation${R===1?"":"s"} `:"you ran ",C;if(l>=2)C=`across ${l} AI tools`;else{let L=d.perAdapter.find(X=>X.isReal);C=`in ${L?Xu(L.name):"Claude Code"}`}s.push(`${j}${T}${C}.`),s.push("")}if(c&&c.events>0){s.length>0&&(s.length=0);let R=qF(),$=n?.cwd??process.cwd(),P=n?.now??Date.now(),j=n?.locale??R.locale,T=n?.tz??R.tz;return s.push(...WF({conversation:c,lifetime:i,multiAdapter:d,realBytes:u,cwd:$,locale:j,tz:T,now:P,version:e,latestVersion:r})),s.join(`
|
|
537
537
|
`)}let p=t.savings.kept_out+(t.cache?t.cache.bytes_saved:0),f=t.savings.total_bytes_returned,m=t.savings.total_calls,h=p+f,g=h>0?p/h*100:0,_=Math.round(p/4),x=f>0?Math.max(1,Math.round(h/Math.max(f,1))):0;if(p===0){s.push(`context-mode ${o} ${m} calls`),s.push(""),m===0?s.push("No tool calls yet. Use batch_execute or execute to start saving tokens."):s.push(`${nt(f)} entered context | 0 tokens saved`),s.push(...x$(t.projectMemory,{lifetime:i,multiAdapter:d,sessionTokensSaved:0})),s.push(...w$(d)),s.push(...b$(i)),s.push(...S$(0,i)),s.push("");let R=e?`v${e}`:"context-mode";return s.push(R),e&&r&&r!=="unknown"&&E$(r,e)&&s.push(`Update available: v${e} -> v${r} | ctx_upgrade`),s.join(`
|
|
538
538
|
`)}s.push(`${hr(_)} tokens saved \xB7 ${g.toFixed(1)}% reduction \xB7 ${o} \xB7 ~${Qu(_)} saved (Opus)`),s.push(""),s.push(`Without context-mode |${kn(h,h)}| ${nt(h)}`),s.push(`With context-mode |${kn(f,h)}| ${nt(f)}`),s.push(""),x>=2?s.push(`${nt(p)} kept out of your conversation \u2014 ${x}\xD7 longer sessions before compact.`):s.push(`${nt(p)} kept out of your conversation. Never entered context.`),s.push("");let y=[`${m} calls`];t.cache&&t.cache.hits>0&&y.push(`${t.cache.hits} cache hits (+${nt(t.cache.bytes_saved)})`),s.push(y.join(" \xB7 "));let v=t.savings.by_tool.filter(R=>R.calls>0);if(v.length>=2){s.push("");let R=v.map($=>{let P=$.context_kb*1024,j=g<100?P/(1-g/100):P,T=Math.max(0,j-P);return{...$,returnedBytes:P,estimatedSaved:T}}).sort(($,P)=>P.estimatedSaved-$.estimatedSaved);for(let $ of R){let P=$.tool.length>22?$.tool.slice(0,19)+"...":$.tool;s.push(` ${P.padEnd(22)} ${String($.calls).padStart(4)} calls ${nt($.estimatedSaved).padStart(8)} saved`)}}if(a&&a.length>0){let R=a.filter($=>$.median_concurrency!=null&&($.max_concurrency??1)>1);if(R.length>0){s.push(""),s.push("Parallel I/O \u2713 one call did the work of many \u2014 faster runs, lower bill, same answer.");for(let $ of R){let P=$.tool_name.replace(/^mcp__.*?__/,"");s.push(` ${P.padEnd(22)} ${$.calls} batches \xB7 ${$.median_concurrency} typical, ${$.max_concurrency} peak`)}}}s.push(...x$(t.projectMemory,{lifetime:i,multiAdapter:d,sessionTokensSaved:_})),s.push(...w$(d)),s.push(...b$(i)),s.push(...S$(_,i)),s.push("");let k=e?`v${e}`:"context-mode";return s.push(k),e&&r&&r!=="unknown"&&r!==e&&s.push(`Update available: v${e} -> v${r} | ctx_upgrade`),s.join(`
|
|
539
|
-
`)}var Yu,zF,Eo,UF,T$,ZF,rl,k$,P$=b(()=>{"use strict";gs();Xr();Yu={file:"Files tracked",cwd:"Working directory",rule:"Project rules (CLAUDE.md)",prompt:"Your requests saved",intent:"Session goal",role:"Behavior rules",constraint:"Constraints you set",mcp:"MCP tools called",skill:"Skills used",subagent:"Delegated work",decision:"Your decisions","agent-finding":"Agent insights kept","rejected-approach":"Approaches you rejected","external-ref":"External docs indexed",data:"Data references",git:"Git operations",env:"Environment setup",task:"Tasks in progress",error:"Errors caught",compact:"Compactions weathered",resume:"Sessions resumed cleanly",snapshot:"Snapshots restored",cache:"Cache hits saved",latency:"Slow tools recorded","user-prompt":"Your messages remembered",plan:"Plans drafted","blocked-on":"Blockers logged"},zF={file:"Restored after compact \u2014 no need to re-read",rule:"Your project instructions survive context resets",prompt:"Continues exactly where you left off",decision:"Applied automatically \u2014 won\u2019t ask again",task:"Picks up from where it stopped",error:"Tracked and monitored across compacts",git:"Branch, commit, and repo state preserved",env:"Runtime config carried forward",mcp:"Tool usage patterns remembered",subagent:"Delegation history preserved",skill:"Skill invocations tracked"},Eo=class{db;constructor(e){this.db=e}static contextSavingsTotal(e,r){let n=e-r,s=e>0?Math.round(n/e*1e3)/10:0;return{rawBytes:e,contextBytes:r,savedBytes:n,savedPercent:s}}static thinkInCodeComparison(e,r){let n=r>0?Math.round(e/r*10)/10:0;return{fileBytes:e,outputBytes:r,ratio:n}}static toolSavings(e){return e.map(r=>({...r,savedBytes:r.rawBytes-r.contextBytes}))}static sandboxIO(e,r){return{inputBytes:e,outputBytes:r}}getMcpToolUsage(){let e;try{e=this.db.prepare("SELECT data FROM session_events WHERE category = 'mcp_tool_call'").all()}catch{return[]}let r=new Map;for(let s of e){let o;try{o=JSON.parse(s.data)}catch{continue}let i=typeof o.tool_name=="string"?o.tool_name:null;if(!i)continue;let a=r.get(i)??{calls:0,concurrencies:[]};if(a.calls+=1,o.truncated!==!0&&o.params&&typeof o.params=="object"){let c=o.params.concurrency;typeof c=="number"&&Number.isFinite(c)&&c>0&&a.concurrencies.push(c)}r.set(i,a)}let n=[];for(let[s,o]of r){let i=null,a=null;if(o.concurrencies.length>0){let c=[...o.concurrencies].sort((d,l)=>d-l),u=Math.floor(c.length/2);i=c.length%2===0?(c[u-1]+c[u])/2:c[u],a=c[c.length-1]}n.push({tool_name:s,calls:o.calls,median_concurrency:i,max_concurrency:a})}return n.sort((s,o)=>o.calls-s.calls||s.tool_name.localeCompare(o.tool_name)),n}queryAll(e){let n=this.db.prepare("SELECT session_id FROM session_meta ORDER BY started_at DESC LIMIT 1").get()?.session_id??"",s=Object.values(e.bytesReturned).reduce((C,L)=>C+L,0),o=Object.values(e.calls).reduce((C,L)=>C+L,0),i=e.bytesIndexed+e.bytesSandboxed,a=i+s,c=a/Math.max(s,1),u=a>0?Math.round((1-s/a)*100):0,d=new Set([...Object.keys(e.calls),...Object.keys(e.bytesReturned)]),l=Array.from(d).sort().map(C=>({tool:C,calls:e.calls[C]||0,context_kb:Math.round((e.bytesReturned[C]||0)/1024*10)/10,tokens:Math.round((e.bytesReturned[C]||0)/4)})),f=((Date.now()-e.sessionStart)/6e4).toFixed(1),m;if(e.cacheHits>0||e.cacheBytesSaved>0){let C=a+e.cacheBytesSaved,L=C/Math.max(s,1),X=Math.max(0,24-Math.floor((Date.now()-e.sessionStart)/(3600*1e3)));m={hits:e.cacheHits,bytes_saved:e.cacheBytesSaved,ttl_hours_left:X,total_with_cache:C,total_savings_ratio:L}}let h=this.db.prepare("SELECT COUNT(*) as cnt FROM session_events WHERE session_id = ?").get(n).cnt,g=this.db.prepare("SELECT category, COUNT(*) as cnt FROM session_events WHERE session_id = ? GROUP BY category ORDER BY cnt DESC").all(n),x=this.db.prepare("SELECT compact_count FROM session_meta WHERE session_id = ?").get(n)?.compact_count??0,y=this.db.prepare("SELECT event_count, consumed FROM session_resume WHERE session_id = ? ORDER BY created_at DESC LIMIT 1").get(n),v=y?!y.consumed:!1,k=this.db.prepare("SELECT category, type, data FROM session_events WHERE session_id = ? ORDER BY id DESC").all(n),R=new Map;for(let C of k){R.has(C.category)||R.set(C.category,new Set);let L=R.get(C.category);if(L.size<5){let X=C.data;C.category==="file"?X=C.data.split("/").pop()||C.data:(C.category==="prompt"||C.category==="user-prompt")&&(X=X.length>50?X.slice(0,47)+"...":X),X.length>40&&(X=X.slice(0,37)+"..."),L.add(X)}}let $=g.map(C=>({category:C.category,count:C.cnt,label:Yu[C.category]||C.category,preview:R.get(C.category)?Array.from(R.get(C.category)).join(", "):"",why:zF[C.category]||"Survives context resets"})),P=this.db.prepare("SELECT COUNT(*) as cnt, COUNT(DISTINCT session_id) as sessions FROM session_events").get(),T=this.db.prepare("SELECT category, COUNT(*) as cnt FROM session_events GROUP BY category ORDER BY cnt DESC").all().filter(C=>C.cnt>0).map(C=>({category:C.category,count:C.cnt,label:Yu[C.category]||C.category}));return{savings:{processed_kb:Math.round(a/1024*10)/10,entered_kb:Math.round(s/1024*10)/10,saved_kb:Math.round(i/1024*10)/10,pct:u,savings_ratio:Math.round(c*10)/10,by_tool:l,total_calls:o,total_bytes_returned:s,kept_out:i,total_processed:a},cache:m,session:{id:n,uptime_min:f},continuity:{total_events:h,by_category:$,compact_count:x,resume_ready:v},projectMemory:{total_events:P.cnt,session_count:P.sessions,by_category:T}}}};UF={minEvents:100,minProjects:5,recencyMs:30*864e5,minAvgBytes:50};T$={project:"What you're building",feedback:"How you work",user:"Who you are",reference:"Where to look",memory:"Long-term context",other:"Other notes"},ZF={"claude-code":"Claude Code","gemini-cli":"Gemini CLI",antigravity:"Antigravity",openclaw:"Openclaw",codex:"Codex CLI",cursor:"Cursor","vscode-copilot":"VS Code Copilot",kiro:"Kiro",pi:"Pi",omp:"OMP","qwen-code":"Qwen Code",kilo:"Kilo",opencode:"OpenCode",zed:"Zed","jetbrains-copilot":"JetBrains"};rl=15/1e6;k$=256});var Y$={};Fe(Y$,{browserOpenArgv:()=>J$,buildBatchNodeOptionsPrefix:()=>V$,buildFetchCode:()=>K$,classifyIp:()=>pl,extractSnippet:()=>Fy,formatBatchQueryResults:()=>q$,killProcessOnPort:()=>Hy,openBrowserSync:()=>Ny,positionsFromHighlight:()=>B$,runBatchCommands:()=>W$});import{createRequire as L$}from"node:module";import{existsSync as Le,unlinkSync as oa,readdirSync as JF,readFileSync as Dy,writeFileSync as My,renameSync as YF,rmSync as al,mkdirSync as ia,cpSync as XF,statSync as R$,symlinkSync as QF,lstatSync as e2}from"node:fs";import{execSync as C$,spawnSync as F$}from"node:child_process";import{join as Oe,dirname as tr,resolve as ut,sep as t2,isAbsolute as r2}from"node:path";import{fileURLToPath as n2}from"node:url";import{homedir as dl,tmpdir as jy,cpus as s2}from"node:os";import{request as o2}from"node:https";function i2(t){try{let e=ct();if(!Le(e))return;let r=JF(e).filter(n=>n.endsWith("-events.md"));for(let n of r){let s=Oe(e,n);try{t.index({path:s,source:"session-events"}),oa(s)}catch{}}}catch{}}function Oy(){return Ze()}async function a2(){if(wn)return wn;try{let{getAdapter:t}=await Promise.resolve().then(()=>(Dn(),Fa)),e=sr();return await t(e.platform)}catch{return null}}function ct(){if(wn)return wn.getSessionDir();try{let e=sr(),r=Nd(e.platform);if(r){let n=Oe(dl(),...r);r.length===1&&r[0]===".claude"?n=Oy():r.length===1&&r[0]===".codex"&&(n=Ca());let s=Oe(n,"context-mode","sessions");return ia(s,{recursive:!0}),s}}catch{}let t=Oe(Oy(),"context-mode","sessions");return ia(t,{recursive:!0}),t}function _r(){let t,e;try{let r=sr().platform;e=r,r==="claude-code"&&(t=Oe(dl(),".claude","projects"))}catch{}return g$({env:process.env,cwd:process.cwd(),pwd:process.env.PWD,transcriptsRoot:t,strictPlatform:e})}function c2(t){return r2(t)?t:ut(_r(),t)}function aa(){return Ku({projectDir:_r(),sessionsDir:ct()})}function Iy(){let t=Oe(tr(ct()),"content");return ia(t,{recursive:!0}),QE({projectDir:_r(),contentDir:t})}function vs(){if(!gr){let t=Iy();gr=new Bu(t),gr.setDenyChecker(e=>{try{let r=_r(),n=hy("Read",r);return yy(e,n,process.platform==="win32",r).denied}catch{return!0}});try{let e=tr(Iy());dy(e,14),gr.cleanupStaleSources(14);let r=Oe(dl(),".context-mode","content");Le(r)&&dy(r,0)}catch{}ly()}return i2(gr),gr}async function I$(){return new Promise(t=>{let e=o2("https://registry.npmjs.org/context-mode/latest",{headers:{Connection:"close"}},r=>{let n="";r.on("data",s=>{n+=s}),r.on("end",()=>{try{let s=JSON.parse(n);t(s.version??"unknown")}catch{t("unknown")}})});e.on("error",()=>t("unknown")),e.setTimeout(5e3,()=>{e.destroy(),t("unknown")}),e.end()})}function d2(){let t=wn?.name;return t==="Claude Code"?"/ctx-upgrade":t==="OpenClaw"?"npm run install:openclaw":t==="Pi"?"npm run build":"npm update -g context-mode"}function p2(t,e){let r=t.split(".").map(Number),n=e.split(".").map(Number);for(let s=0;s<3;s++){if((r[s]??0)>(n[s]??0))return!0;if((r[s]??0)<(n[s]??0))return!1}return!1}function m2(){return!Wr||Wr==="unknown"?!1:p2(Wr,Vr)}function f2(){if(!m2())return!1;let t=Date.now();if(sl>=u2){if(t-O$<l2)return!1;sl=0}return sl===0&&(O$=t),sl++,!0}function h2(){if(!A$){A$=!0;try{let t=Ze(),e=ut(t,"plugins","installed_plugins.json");if(!Le(e))return;let r=JSON.parse(Dy(e,"utf-8")),n=ut(t,"plugins","cache"),s=Le(ut(Mt,"package.json"))?Mt:tr(Mt);for(let[o,i]of Object.entries(r.plugins??{}))if(o==="context-mode@context-mode")for(let a of i){let c=a.installPath;if(!c||Le(c)||!ut(c).startsWith(n+t2))continue;try{e2(c).isSymbolicLink()&&oa(c)}catch{}let u=tr(c);Le(u)||ia(u,{recursive:!0}),Le(s)&&QF(s,c,process.platform==="win32"?"junction":void 0)}}catch{}}}function K(t,e){if(h2(),f2()&&e.content.length>0){let n=d2();e.content[0].text=`\u26A0\uFE0F context-mode v${Vr} outdated \u2192 v${Wr} available. Upgrade: ${n}
|
|
539
|
+
`)}var Yu,zF,Eo,UF,T$,ZF,rl,k$,P$=b(()=>{"use strict";gs();Xr();Yu={file:"Files tracked",cwd:"Working directory",rule:"Project rules (CLAUDE.md)",prompt:"Your requests saved",intent:"Session goal",role:"Behavior rules",constraint:"Constraints you set",mcp:"MCP tools called",skill:"Skills used",subagent:"Delegated work",decision:"Your decisions","agent-finding":"Agent insights kept","rejected-approach":"Approaches you rejected","external-ref":"External docs indexed",data:"Data references",git:"Git operations",env:"Environment setup",task:"Tasks in progress",error:"Errors caught",compact:"Compactions weathered",resume:"Sessions resumed cleanly",snapshot:"Snapshots restored",cache:"Cache hits saved",latency:"Slow tools recorded","user-prompt":"Your messages remembered",plan:"Plans drafted","blocked-on":"Blockers logged"},zF={file:"Restored after compact \u2014 no need to re-read",rule:"Your project instructions survive context resets",prompt:"Continues exactly where you left off",decision:"Applied automatically \u2014 won\u2019t ask again",task:"Picks up from where it stopped",error:"Tracked and monitored across compacts",git:"Branch, commit, and repo state preserved",env:"Runtime config carried forward",mcp:"Tool usage patterns remembered",subagent:"Delegation history preserved",skill:"Skill invocations tracked"},Eo=class{db;constructor(e){this.db=e}static contextSavingsTotal(e,r){let n=e-r,s=e>0?Math.round(n/e*1e3)/10:0;return{rawBytes:e,contextBytes:r,savedBytes:n,savedPercent:s}}static thinkInCodeComparison(e,r){let n=r>0?Math.round(e/r*10)/10:0;return{fileBytes:e,outputBytes:r,ratio:n}}static toolSavings(e){return e.map(r=>({...r,savedBytes:r.rawBytes-r.contextBytes}))}static sandboxIO(e,r){return{inputBytes:e,outputBytes:r}}getMcpToolUsage(){let e;try{e=this.db.prepare("SELECT data FROM session_events WHERE category = 'mcp_tool_call'").all()}catch{return[]}let r=new Map;for(let s of e){let o;try{o=JSON.parse(s.data)}catch{continue}let i=typeof o.tool_name=="string"?o.tool_name:null;if(!i)continue;let a=r.get(i)??{calls:0,concurrencies:[]};if(a.calls+=1,o.truncated!==!0&&o.params&&typeof o.params=="object"){let c=o.params.concurrency;typeof c=="number"&&Number.isFinite(c)&&c>0&&a.concurrencies.push(c)}r.set(i,a)}let n=[];for(let[s,o]of r){let i=null,a=null;if(o.concurrencies.length>0){let c=[...o.concurrencies].sort((d,l)=>d-l),u=Math.floor(c.length/2);i=c.length%2===0?(c[u-1]+c[u])/2:c[u],a=c[c.length-1]}n.push({tool_name:s,calls:o.calls,median_concurrency:i,max_concurrency:a})}return n.sort((s,o)=>o.calls-s.calls||s.tool_name.localeCompare(o.tool_name)),n}queryAll(e){let n=this.db.prepare("SELECT session_id FROM session_meta ORDER BY started_at DESC LIMIT 1").get()?.session_id??"",s=Object.values(e.bytesReturned).reduce((C,L)=>C+L,0),o=Object.values(e.calls).reduce((C,L)=>C+L,0),i=e.bytesIndexed+e.bytesSandboxed,a=i+s,c=a/Math.max(s,1),u=a>0?Math.round((1-s/a)*100):0,d=new Set([...Object.keys(e.calls),...Object.keys(e.bytesReturned)]),l=Array.from(d).sort().map(C=>({tool:C,calls:e.calls[C]||0,context_kb:Math.round((e.bytesReturned[C]||0)/1024*10)/10,tokens:Math.round((e.bytesReturned[C]||0)/4)})),f=((Date.now()-e.sessionStart)/6e4).toFixed(1),m;if(e.cacheHits>0||e.cacheBytesSaved>0){let C=a+e.cacheBytesSaved,L=C/Math.max(s,1),X=Math.max(0,24-Math.floor((Date.now()-e.sessionStart)/(3600*1e3)));m={hits:e.cacheHits,bytes_saved:e.cacheBytesSaved,ttl_hours_left:X,total_with_cache:C,total_savings_ratio:L}}let h=this.db.prepare("SELECT COUNT(*) as cnt FROM session_events WHERE session_id = ?").get(n).cnt,g=this.db.prepare("SELECT category, COUNT(*) as cnt FROM session_events WHERE session_id = ? GROUP BY category ORDER BY cnt DESC").all(n),x=this.db.prepare("SELECT compact_count FROM session_meta WHERE session_id = ?").get(n)?.compact_count??0,y=this.db.prepare("SELECT event_count, consumed FROM session_resume WHERE session_id = ? ORDER BY created_at DESC LIMIT 1").get(n),v=y?!y.consumed:!1,k=this.db.prepare("SELECT category, type, data FROM session_events WHERE session_id = ? ORDER BY id DESC").all(n),R=new Map;for(let C of k){R.has(C.category)||R.set(C.category,new Set);let L=R.get(C.category);if(L.size<5){let X=C.data;C.category==="file"?X=C.data.split("/").pop()||C.data:(C.category==="prompt"||C.category==="user-prompt")&&(X=X.length>50?X.slice(0,47)+"...":X),X.length>40&&(X=X.slice(0,37)+"..."),L.add(X)}}let $=g.map(C=>({category:C.category,count:C.cnt,label:Yu[C.category]||C.category,preview:R.get(C.category)?Array.from(R.get(C.category)).join(", "):"",why:zF[C.category]||"Survives context resets"})),P=this.db.prepare("SELECT COUNT(*) as cnt, COUNT(DISTINCT session_id) as sessions FROM session_events").get(),T=this.db.prepare("SELECT category, COUNT(*) as cnt FROM session_events GROUP BY category ORDER BY cnt DESC").all().filter(C=>C.cnt>0).map(C=>({category:C.category,count:C.cnt,label:Yu[C.category]||C.category}));return{savings:{processed_kb:Math.round(a/1024*10)/10,entered_kb:Math.round(s/1024*10)/10,saved_kb:Math.round(i/1024*10)/10,pct:u,savings_ratio:Math.round(c*10)/10,by_tool:l,total_calls:o,total_bytes_returned:s,kept_out:i,total_processed:a},cache:m,session:{id:n,uptime_min:f},continuity:{total_events:h,by_category:$,compact_count:x,resume_ready:v},projectMemory:{total_events:P.cnt,session_count:P.sessions,by_category:T}}}};UF={minEvents:100,minProjects:5,recencyMs:30*864e5,minAvgBytes:50};T$={project:"What you're building",feedback:"How you work",user:"Who you are",reference:"Where to look",memory:"Long-term context",other:"Other notes"},ZF={"claude-code":"Claude Code","gemini-cli":"Gemini CLI",antigravity:"Antigravity",openclaw:"Openclaw",codex:"Codex CLI",cursor:"Cursor","vscode-copilot":"VS Code Copilot",kiro:"Kiro",pi:"Pi",omp:"OMP","qwen-code":"Qwen Code",kilo:"Kilo",opencode:"OpenCode",zed:"Zed","jetbrains-copilot":"JetBrains"};rl=15/1e6;k$=256});var Y$={};Fe(Y$,{browserOpenArgv:()=>J$,buildBatchNodeOptionsPrefix:()=>V$,buildFetchCode:()=>K$,classifyIp:()=>pl,extractSnippet:()=>Fy,formatBatchQueryResults:()=>q$,killProcessOnPort:()=>Hy,openBrowserSync:()=>Ny,positionsFromHighlight:()=>B$,runBatchCommands:()=>W$});import{createRequire as L$}from"node:module";import{existsSync as Le,unlinkSync as oa,readdirSync as JF,readFileSync as Dy,writeFileSync as My,renameSync as YF,rmSync as al,mkdirSync as ia,cpSync as XF,statSync as R$,symlinkSync as QF,lstatSync as e2}from"node:fs";import{execSync as C$,spawnSync as F$}from"node:child_process";import{join as Oe,dirname as tr,resolve as ut,sep as t2,isAbsolute as r2}from"node:path";import{fileURLToPath as n2}from"node:url";import{homedir as dl,tmpdir as jy,cpus as s2}from"node:os";import{request as o2}from"node:https";function i2(t){try{let e=ct();if(!Le(e))return;let r=JF(e).filter(n=>n.endsWith("-events.md"));for(let n of r){let s=Oe(e,n);try{t.index({path:s,source:"session-events"}),oa(s)}catch{}}}catch{}}function Oy(){return Ze()}async function a2(){if(wn)return wn;try{let{getAdapter:t}=await Promise.resolve().then(()=>(Dn(),Fa)),e=sr();return await t(e.platform)}catch{return null}}function ct(){if(wn)return wn.getSessionDir();try{let e=sr(),r=Nd(e.platform);if(r){let n=Oe(dl(),...r);r.length===1&&r[0]===".claude"?n=Oy():r.length===1&&r[0]===".codex"&&(n=Ca());let s=Oe(n,"context-mode","sessions");return ia(s,{recursive:!0}),s}}catch{}let t=Oe(Oy(),"context-mode","sessions");return ia(t,{recursive:!0}),t}function _r(){let t,e;try{let r=sr().platform;e=r,r==="claude-code"&&(t=Oe(dl(),".claude","projects"))}catch{}return g$({env:process.env,cwd:process.cwd(),pwd:process.env.PWD,transcriptsRoot:t,transcriptMaxAgeMs:300*1e3,strictPlatform:e})}function c2(t){return r2(t)?t:ut(_r(),t)}function aa(){return Ku({projectDir:_r(),sessionsDir:ct()})}function Iy(){let t=Oe(tr(ct()),"content");return ia(t,{recursive:!0}),QE({projectDir:_r(),contentDir:t})}function vs(){if(!gr){let t=Iy();gr=new Bu(t),gr.setDenyChecker(e=>{try{let r=_r(),n=hy("Read",r);return yy(e,n,process.platform==="win32",r).denied}catch{return!0}});try{let e=tr(Iy());dy(e,14),gr.cleanupStaleSources(14);let r=Oe(dl(),".context-mode","content");Le(r)&&dy(r,0)}catch{}ly()}return i2(gr),gr}async function I$(){return new Promise(t=>{let e=o2("https://registry.npmjs.org/context-mode/latest",{headers:{Connection:"close"}},r=>{let n="";r.on("data",s=>{n+=s}),r.on("end",()=>{try{let s=JSON.parse(n);t(s.version??"unknown")}catch{t("unknown")}})});e.on("error",()=>t("unknown")),e.setTimeout(5e3,()=>{e.destroy(),t("unknown")}),e.end()})}function d2(){let t=wn?.name;return t==="Claude Code"?"/ctx-upgrade":t==="OpenClaw"?"npm run install:openclaw":t==="Pi"?"npm run build":"npm update -g context-mode"}function p2(t,e){let r=t.split(".").map(Number),n=e.split(".").map(Number);for(let s=0;s<3;s++){if((r[s]??0)>(n[s]??0))return!0;if((r[s]??0)<(n[s]??0))return!1}return!1}function m2(){return!Wr||Wr==="unknown"?!1:p2(Wr,Vr)}function f2(){if(!m2())return!1;let t=Date.now();if(sl>=u2){if(t-O$<l2)return!1;sl=0}return sl===0&&(O$=t),sl++,!0}function h2(){if(!A$){A$=!0;try{let t=Ze(),e=ut(t,"plugins","installed_plugins.json");if(!Le(e))return;let r=JSON.parse(Dy(e,"utf-8")),n=ut(t,"plugins","cache"),s=Le(ut(Mt,"package.json"))?Mt:tr(Mt);for(let[o,i]of Object.entries(r.plugins??{}))if(o==="context-mode@context-mode")for(let a of i){let c=a.installPath;if(!c||Le(c)||!ut(c).startsWith(n+t2))continue;try{e2(c).isSymbolicLink()&&oa(c)}catch{}let u=tr(c);Le(u)||ia(u,{recursive:!0}),Le(s)&&QF(s,c,process.platform==="win32"?"junction":void 0)}}catch{}}}function K(t,e){if(h2(),f2()&&e.content.length>0){let n=d2();e.content[0].text=`\u26A0\uFE0F context-mode v${Vr} outdated \u2192 v${Wr} available. Upgrade: ${n}
|
|
540
540
|
|
|
541
541
|
`+e.content[0].text}let r=e.content.reduce((n,s)=>n+Buffer.byteLength(s.text),0);return oe.calls[t]=(oe.calls[t]||0)+1,oe.bytesReturned[t]=(oe.bytesReturned[t]||0)+r,cl(),setImmediate(()=>a$(aa(),t,r)),(t==="ctx_execute"||t==="ctx_execute_file"||t==="ctx_batch_execute")&&setImmediate(()=>r$({sessionDbPath:aa(),toolName:t,bytesReturned:r})),e}function yr(t,e="unknown"){oe.bytesIndexed+=t,cl(),t>0&&setImmediate(()=>n$({sessionDbPath:aa(),source:e,bytesAvoided:t}))}function U$(){let t=process.env.CLAUDE_SESSION_ID||`pid-${process.ppid}`;return Oe(ct(),`stats-${t}.json`)}function cl(){let t=Date.now();if(!(t-Ay<g2)){Ay=t;try{let e=Object.values(oe.bytesReturned).reduce((l,p)=>l+p,0),r=Object.values(oe.calls).reduce((l,p)=>l+p,0),n=oe.bytesIndexed+oe.bytesSandboxed+oe.cacheBytesSaved,s=n+e,o=s>0?Math.round((1-e/s)*100):0,i=Math.round(n/4),a=ol?.tokens??0;if(!ol||t-ol.computedAt>_2)try{a=(na({sessionsDir:ct()})?.totalEvents??0)*v2,ol={tokens:a,computedAt:t}}catch{}let c={schemaVersion:y2,version:Vr,updated_at:t,session_start:oe.sessionStart,uptime_ms:t-oe.sessionStart,total_calls:r,bytes_returned:e,bytes_indexed:oe.bytesIndexed,bytes_sandboxed:oe.bytesSandboxed,cache_hits:oe.cacheHits,cache_bytes_saved:oe.cacheBytesSaved,kept_out:n,total_processed:s,reduction_pct:o,tokens_saved:i,dollars_saved_session:+(i*rl).toFixed(2),tokens_saved_lifetime:a,dollars_saved_lifetime:+(a*rl).toFixed(2),by_tool:Object.fromEntries(Object.keys({...oe.calls,...oe.bytesReturned}).map(l=>[l,{calls:oe.calls[l]||0,bytes:oe.bytesReturned[l]||0}]))},u=U$(),d=`${u}.tmp`;My(d,JSON.stringify(c)),YF(d,u)}catch{}}}function Ly(t,e){try{let r=fy(process.env.CLAUDE_PROJECT_DIR),n=gy(t,r);if(n.decision==="deny")return K(e,{content:[{type:"text",text:`Command blocked by security policy: matches deny pattern ${n.matchedPattern}`}],isError:!0})}catch{}return null}function H$(t,e,r){try{let n=BE(t,e);if(n.length===0)return null;let s=fy(process.env.CLAUDE_PROJECT_DIR);for(let o of n){let i=gy(o,s);if(i.decision==="deny")return K(r,{content:[{type:"text",text:`Command blocked by security policy: embedded shell command "${o}" matches deny pattern ${i.matchedPattern}`}],isError:!0})}}catch{}return null}function Z$(t,e){try{let r=_r(),n=hy("Read",r),s=yy(t,n,process.platform==="win32",r);if(s.denied)return K(e,{content:[{type:"text",text:`File access blocked by security policy: path matches Read deny pattern ${s.matchedPattern}`}],isError:!0})}catch{}return null}function B$(t){let e=[],r=0,n=0;for(;n<t.length;)if(t[n]===S2){for(e.push(r),n++;n<t.length&&t[n]!==k2;)r++,n++;n<t.length&&n++}else r++,n++;return e}function Fy(t,e,r=1500,n){if(t.length<=r)return t;let s=[];if(n)for(let u of B$(n))s.push(u);if(s.length===0){let u=e.toLowerCase().split(/\s+/).filter(l=>l.length>2),d=t.toLowerCase();for(let l of u){let p=d.indexOf(l);for(;p!==-1;)s.push(p),p=d.indexOf(l,p+1)}}if(s.length===0)return t.slice(0,r)+`
|
|
542
542
|
\u2026`;s.sort((u,d)=>u-d);let o=300,i=[];for(let u of s){let d=Math.max(0,u-o),l=Math.min(t.length,u+o);i.length>0&&d<=i[i.length-1][1]?i[i.length-1][1]=l:i.push([d,l])}let a=[],c=0;for(let[u,d]of i){if(c>=r)break;let l=t.slice(u,Math.min(d,u+(r-c)));a.push((u>0?"\u2026":"")+l+(d<t.length?"\u2026":"")),c+=l.length}return a.join(`
|
package/hooks/core/routing.mjs
CHANGED
|
@@ -249,39 +249,88 @@ let securityInitFailed = false;
|
|
|
249
249
|
/**
|
|
250
250
|
* @returns {boolean} true if security module loaded successfully.
|
|
251
251
|
*
|
|
252
|
-
* Loud fail: if `build/security.js` is
|
|
253
|
-
* clear stderr warning instead of swallowing the error
|
|
254
|
-
* this, user-configured `permissions.deny` patterns
|
|
255
|
-
* with no indication that policy enforcement is
|
|
256
|
-
* security regression.
|
|
252
|
+
* Loud fail: if neither the esbuild bundle nor `build/security.js` is
|
|
253
|
+
* importable, log a clear stderr warning instead of swallowing the error
|
|
254
|
+
* silently. Without this, user-configured `permissions.deny` patterns
|
|
255
|
+
* (#466) become no-ops with no indication that policy enforcement is
|
|
256
|
+
* disabled — a fail-open security regression.
|
|
257
|
+
*
|
|
258
|
+
* ─── Resolution order (#558) ───────────────────────────────────────────
|
|
259
|
+
*
|
|
260
|
+
* 1. `hooks/security.bundle.mjs` — esbuild output, sibling of routing.mjs's
|
|
261
|
+
* parent. Marketplace installs (`git clone` install path) ship this
|
|
262
|
+
* bundle via CI's `git add -f`, so it's the only artifact reliably
|
|
263
|
+
* present across BOTH `npm install` (build/ generated by tsc) AND
|
|
264
|
+
* marketplace install (build/ excluded by .gitignore, never built).
|
|
265
|
+
*
|
|
266
|
+
* 2. `<buildDir>/security.js` — tsc output. Present after `npm run build`.
|
|
267
|
+
* Kept as a fallback so source checkouts that bypass `npm run bundle`
|
|
268
|
+
* still degrade gracefully to the tsc-emitted module.
|
|
269
|
+
*
|
|
270
|
+
* Bundle path is computed from `import.meta.url` (sibling layout:
|
|
271
|
+
* `hooks/core/routing.mjs` → `hooks/security.bundle.mjs`).
|
|
272
|
+
* `CONTEXT_MODE_SECURITY_BUNDLE_PATH` is a test seam — it lets
|
|
273
|
+
* subprocess-based tests stage a bundle in tmpdir without polluting the
|
|
274
|
+
* repo's hooks/ directory.
|
|
257
275
|
*/
|
|
258
276
|
export async function initSecurity(buildDir) {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
277
|
+
const { existsSync } = await import("node:fs");
|
|
278
|
+
const { resolve, dirname } = await import("node:path");
|
|
279
|
+
const { fileURLToPath, pathToFileURL } = await import("node:url");
|
|
280
|
+
|
|
281
|
+
// Default: <hooks/core/ dir>/../security.bundle.mjs → hooks/security.bundle.mjs.
|
|
282
|
+
const defaultBundlePath = resolve(
|
|
283
|
+
dirname(fileURLToPath(import.meta.url)),
|
|
284
|
+
"..",
|
|
285
|
+
"security.bundle.mjs",
|
|
286
|
+
);
|
|
287
|
+
const bundlePath = process.env.CONTEXT_MODE_SECURITY_BUNDLE_PATH || defaultBundlePath;
|
|
288
|
+
const secPath = resolve(buildDir, "security.js");
|
|
289
|
+
|
|
290
|
+
// Bundle-first: marketplace installs ship the bundle, never the build/ dir.
|
|
291
|
+
if (existsSync(bundlePath)) {
|
|
292
|
+
try {
|
|
293
|
+
security = await import(pathToFileURL(bundlePath).href);
|
|
294
|
+
return true;
|
|
295
|
+
} catch (err) {
|
|
265
296
|
if (!securityInitFailed && !process.env.CONTEXT_MODE_SUPPRESS_SECURITY_WARNING) {
|
|
266
297
|
process.stderr.write(
|
|
267
|
-
`[context-mode] WARNING: ${
|
|
268
|
-
`Run \`npm run build\` to generate it. Set CONTEXT_MODE_SUPPRESS_SECURITY_WARNING=1 to silence.\n`,
|
|
298
|
+
`[context-mode] WARNING: failed to load security bundle (${bundlePath}) — deny patterns NOT enforced: ${err?.message ?? err}\n`,
|
|
269
299
|
);
|
|
270
300
|
}
|
|
271
301
|
securityInitFailed = true;
|
|
272
302
|
return false;
|
|
273
303
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Fallback: tsc-emitted build/security.js (source checkout + `npm run build`).
|
|
307
|
+
if (existsSync(secPath)) {
|
|
308
|
+
try {
|
|
309
|
+
security = await import(pathToFileURL(secPath).href);
|
|
310
|
+
return true;
|
|
311
|
+
} catch (err) {
|
|
312
|
+
if (!securityInitFailed && !process.env.CONTEXT_MODE_SUPPRESS_SECURITY_WARNING) {
|
|
313
|
+
process.stderr.write(
|
|
314
|
+
`[context-mode] WARNING: failed to load security module — deny patterns NOT enforced: ${err?.message ?? err}\n`,
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
securityInitFailed = true;
|
|
318
|
+
return false;
|
|
281
319
|
}
|
|
282
|
-
securityInitFailed = true;
|
|
283
|
-
return false;
|
|
284
320
|
}
|
|
321
|
+
|
|
322
|
+
// Neither artifact present — preserve fail-open with an actionable warning
|
|
323
|
+
// that mentions BOTH paths so users on either install model can self-diagnose.
|
|
324
|
+
if (!securityInitFailed && !process.env.CONTEXT_MODE_SUPPRESS_SECURITY_WARNING) {
|
|
325
|
+
process.stderr.write(
|
|
326
|
+
`[context-mode] WARNING: security module not found — security deny patterns will NOT be enforced.\n` +
|
|
327
|
+
` Searched: ${bundlePath} (bundle) and ${secPath} (build).\n` +
|
|
328
|
+
` Marketplace installs ship hooks/security.bundle.mjs via CI; for source checkouts run \`npm run bundle\` (or \`npm run build\`).\n` +
|
|
329
|
+
` Set CONTEXT_MODE_SUPPRESS_SECURITY_WARNING=1 to silence.\n`,
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
securityInitFailed = true;
|
|
333
|
+
return false;
|
|
285
334
|
}
|
|
286
335
|
|
|
287
336
|
/** @returns {boolean} true if a previous initSecurity() call failed to load the module. */
|
|
@@ -289,6 +338,49 @@ export function isSecurityInitFailed() {
|
|
|
289
338
|
return securityInitFailed;
|
|
290
339
|
}
|
|
291
340
|
|
|
341
|
+
/**
|
|
342
|
+
* Build the agent-facing additionalContext block surfacing the security
|
|
343
|
+
* init failure (#558).
|
|
344
|
+
*
|
|
345
|
+
* Pre-558 the only signal of a fail-open security regression was a
|
|
346
|
+
* stderr WARNING line that adapters typically suppress / discard. The
|
|
347
|
+
* user had no in-band signal that `permissions.deny` was no-op'd.
|
|
348
|
+
*
|
|
349
|
+
* Returns a structured XML-ish block when initSecurity() has failed,
|
|
350
|
+
* `null` otherwise. SessionStart hooks append the block to their
|
|
351
|
+
* additionalContext so the agent (and through the agent, the user)
|
|
352
|
+
* sees the warning the next time they view the session — not just in
|
|
353
|
+
* suppressed stderr.
|
|
354
|
+
*
|
|
355
|
+
* The block format intentionally mirrors the `<context_guidance>`
|
|
356
|
+
* shape used elsewhere in routing so existing prompt-template
|
|
357
|
+
* scaffolding picks it up without special-casing.
|
|
358
|
+
*/
|
|
359
|
+
export function buildSecurityWarningContext() {
|
|
360
|
+
if (!securityInitFailed) return null;
|
|
361
|
+
return [
|
|
362
|
+
"<context_mode_security_warning>",
|
|
363
|
+
" <severity>HIGH</severity>",
|
|
364
|
+
" <issue>",
|
|
365
|
+
" The context-mode security module failed to load.",
|
|
366
|
+
" User-configured `permissions.deny` patterns are NOT being enforced.",
|
|
367
|
+
" Bash commands and file operations bypass the deny gate (fail-open).",
|
|
368
|
+
" </issue>",
|
|
369
|
+
" <root_cause>",
|
|
370
|
+
" `hooks/security.bundle.mjs` (and `build/security.js`) are absent or unloadable.",
|
|
371
|
+
" Common on marketplace installs where `build/` is gitignored and the",
|
|
372
|
+
" bundle was missing prior to v1.0.127.",
|
|
373
|
+
" </root_cause>",
|
|
374
|
+
" <fix>",
|
|
375
|
+
" Run `npm run bundle` from the context-mode source checkout, OR",
|
|
376
|
+
" upgrade context-mode to v1.0.127+ (which ships hooks/security.bundle.mjs",
|
|
377
|
+
" via CI). To opt in to fail-CLOSED instead, set CONTEXT_MODE_REQUIRE_SECURITY=1.",
|
|
378
|
+
" To silence this warning while you investigate, set CONTEXT_MODE_SUPPRESS_SECURITY_WARNING=1.",
|
|
379
|
+
" </fix>",
|
|
380
|
+
"</context_mode_security_warning>",
|
|
381
|
+
].join("\n");
|
|
382
|
+
}
|
|
383
|
+
|
|
292
384
|
/**
|
|
293
385
|
* Normalize platform-specific tool names to canonical (Claude Code) names.
|
|
294
386
|
*
|
|
@@ -25,7 +25,7 @@ import { createSessionLoaders } from "../session-loaders.mjs";
|
|
|
25
25
|
import { join, dirname } from "node:path";
|
|
26
26
|
import { readFileSync, unlinkSync } from "node:fs";
|
|
27
27
|
import { homedir } from "node:os";
|
|
28
|
-
import { fileURLToPath
|
|
28
|
+
import { fileURLToPath } from "node:url";
|
|
29
29
|
|
|
30
30
|
const HOOK_DIR = dirname(fileURLToPath(import.meta.url));
|
|
31
31
|
const { loadSessionDB } = createSessionLoaders(HOOK_DIR);
|
|
@@ -88,11 +88,13 @@ try {
|
|
|
88
88
|
const sessionId = getSessionId(input, OPTS);
|
|
89
89
|
db.ensureSession(sessionId, projectDir);
|
|
90
90
|
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
91
|
+
// NOTE (#558): excised the old GEMINI.md auto-write block. It loaded an
|
|
92
|
+
// adapter from build/ (gitignored, missing on marketplace installs) and
|
|
93
|
+
// called a method that was deleted from every adapter in commit 6dae20c.
|
|
94
|
+
// Both layers were silently no-op'd by the surrounding try/catch on every
|
|
95
|
+
// install path for many releases. If routing-instruction auto-write is
|
|
96
|
+
// reintroduced it must come with its own PRD, method spec, and format
|
|
97
|
+
// tests — out of scope for the security regression fix.
|
|
96
98
|
|
|
97
99
|
const ruleFilePaths = [
|
|
98
100
|
join(homedir(), ".gemini", "GEMINI.md"),
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{readFileSync as S,realpathSync as R}from"node:fs";import{resolve as u}from"node:path";import{resolve as a}from"node:path";import{homedir as d}from"node:os";import{createRequire as C}from"node:module";function v(e=process.env){let s=e.CLAUDE_CONFIG_DIR;return s&&s.trim()!==""?s.startsWith("~")?a(d(),s.replace(/^~[/\\]?/,"")):a(s):a(d(),".claude")}function E(e=process.env){return a(v(e),"settings.json")}function h(e=process.env){let s=[],t=null,n=null;try{let r=C(import.meta.url)("../adapters/detect.js");t=r.detectPlatform(),n=r.getSessionDirSegments}catch{}if(t&&n&&t.platform!=="claude-code"){let o=n(t.platform);o&&o.length>0&&s.push(a(d(),...o,"settings.json"))}let i=E(e);return s.includes(i)||s.push(i),s}function w(e){let s=e.match(/^Bash\((.+)\)$/);return s?s[1]:null}function k(e){let s=e.match(/^(\w+)\((.+)\)$/);return s?{tool:s[1],glob:s[2]}:null}function $(e){return e.replace(/[.*+?^${}()|[\]\\\/\-]/g,"\\$&")}function P(e){return e.replace(/[.+?^${}()|[\]\\\/\-]/g,"\\$&").replace(/\*/g,".*")}function A(e,s=!1){let t,n=e.indexOf(":");if(n!==-1){let i=e.slice(0,n),o=e.slice(n+1),r=$(i),l=P(o);t=`^${r}(\\s${l})?$`}else t=`^${P(e)}$`;return new RegExp(t,s?"i":"")}function G(e,s=!1){let t="",n=0;for(;n<e.length;)e[n]==="*"&&e[n+1]==="*"?n+2<e.length&&e[n+2]==="/"?(t+="(.*/)?",n+=3):(t+=".*",n+=2):e[n]==="*"?(t+="[^/]*",n++):e[n]==="?"?(t+="[^/]",n++):(t+=e[n].replace(/[.+^${}()|[\]\\\/\-]/g,"\\$&"),n++);return new RegExp(`^${t}$`,s?"i":"")}function f(e,s,t=!1){for(let n of s){let i=w(n);if(i&&A(i,t).test(e))return n}return null}function b(e){let s=[],t="",n=!1,i=!1,o=!1;for(let r=0;r<e.length;r++){let l=e[r],c=r>0?e[r-1]:"";l==="'"&&!i&&!o&&c!=="\\"?(n=!n,t+=l):l==='"'&&!n&&!o&&c!=="\\"?(i=!i,t+=l):l==="`"&&!n&&!i&&c!=="\\"?(o=!o,t+=l):!n&&!i&&!o?l===";"?(s.push(t.trim()),t=""):l==="|"&&e[r+1]==="|"||l==="&"&&e[r+1]==="&"?(s.push(t.trim()),t="",r++):l==="|"?(s.push(t.trim()),t=""):t+=l:t+=l}return t.trim()&&s.push(t.trim()),s.filter(r=>r.length>0)}function m(e){let s;try{s=S(e,"utf-8")}catch{return null}let t;try{t=JSON.parse(s)}catch{return null}let n=t?.permissions;if(!n||typeof n!="object")return null;let i=o=>Array.isArray(o)?o.filter(r=>typeof r=="string"&&w(r)!==null):[];return{allow:i(n.allow),deny:i(n.deny),ask:i(n.ask)}}function L(e,s){let t=[];if(e){let i=u(e,".claude","settings.local.json"),o=m(i);o&&t.push(o);let r=u(e,".claude","settings.json"),l=m(r);l&&t.push(l)}let n=s!==void 0?[s]:h();for(let i of n){let o=m(i);o&&t.push(o)}return t}function M(e,s,t){let n=[],i=r=>{let l;try{l=S(r,"utf-8")}catch{return null}let c;try{c=JSON.parse(l)}catch{return null}let g=c?.permissions?.deny;if(!Array.isArray(g))return[];let y=[];for(let x of g){if(typeof x!="string")continue;let p=k(x);p&&p.tool===e&&y.push(p.glob)}return y};if(s){let r=i(u(s,".claude","settings.local.json"));r!==null&&n.push(r);let l=i(u(s,".claude","settings.json"));l!==null&&n.push(l)}let o=t!==void 0?[t]:h();for(let r of o){let l=i(r);l!==null&&n.push(l)}return n}function q(e,s,t=process.platform==="win32"){let n=b(e);for(let i of n)for(let o of s){let r=f(i,o.deny,t);if(r)return{decision:"deny",matchedPattern:r}}for(let i of s){let o=f(e,i.ask,t);if(o)return{decision:"ask",matchedPattern:o};let r=f(e,i.allow,t);if(r)return{decision:"allow",matchedPattern:r}}return{decision:"ask"}}function z(e,s,t=process.platform==="win32"){let n=b(e);for(let i of n)for(let o of s){let r=f(i,o.deny,t);if(r)return{decision:"deny",matchedPattern:r}}return{decision:"allow"}}function I(e,s,t=process.platform==="win32",n){let i=r=>r.replace(/\\/g,"/"),o=new Set;if(o.add(i(e)),n){let r=u(n,e);o.add(i(r));try{o.add(i(R(r)))}catch{}}for(let r of s)for(let l of r){let c=G(i(l),t);for(let g of o)if(c.test(g))return{denied:!0,matchedPattern:l}}return{denied:!1}}var _={python:[/os\.system\(\s*(['"])(.*?)\1\s*\)/g,/subprocess\.(?:run|call|Popen|check_output|check_call)\(\s*(['"])(.*?)\1/g],javascript:[/exec(?:Sync|File|FileSync)?\(\s*(['"`])(.*?)\1/g,/spawn(?:Sync)?\(\s*(['"`])(.*?)\1/g],typescript:[/exec(?:Sync|File|FileSync)?\(\s*(['"`])(.*?)\1/g,/spawn(?:Sync)?\(\s*(['"`])(.*?)\1/g],ruby:[/system\(\s*(['"])(.*?)\1/g,/`(.*?)`/g],go:[/exec\.Command\(\s*(['"`])(.*?)\1/g],php:[/shell_exec\(\s*(['"`])(.*?)\1/g,/(?:^|[^.])exec\(\s*(['"`])(.*?)\1/g,/(?:^|[^.])system\(\s*(['"`])(.*?)\1/g,/passthru\(\s*(['"`])(.*?)\1/g,/proc_open\(\s*(['"`])(.*?)\1/g],rust:[/Command::new\(\s*(['"`])(.*?)\1/g]};function F(e){let s=[],t=/subprocess\.(?:run|call|Popen|check_output|check_call)\(\s*\[([^\]]+)\]/g,n;for(;(n=t.exec(e))!==null;){let o=[...n[1].matchAll(/(['"])(.*?)\1/g)].map(r=>r[2]);o.length>0&&s.push(o.join(" "))}return s}function H(e,s){let t=_[s];if(!t&&s!=="python")return[];let n=[];if(t)for(let i of t){i.lastIndex=0;let o;for(;(o=i.exec(e))!==null;){let r=o[o.length-1];r&&n.push(r)}}return s==="python"&&n.push(...F(e)),n}export{q as evaluateCommand,z as evaluateCommandDenyOnly,I as evaluateFilePath,H as extractShellCommands,G as fileGlobToRegex,A as globToRegex,f as matchesAnyPattern,w as parseBashPattern,k as parseToolPattern,L as readBashPolicies,M as readToolDenyPatterns,b as splitChainedCommands};
|
package/hooks/sessionstart.mjs
CHANGED
|
@@ -53,6 +53,24 @@ await runHook(async () => {
|
|
|
53
53
|
|
|
54
54
|
let additionalContext = ROUTING_BLOCK;
|
|
55
55
|
|
|
56
|
+
// ─── #558: surface security init failure as agent-facing context ───
|
|
57
|
+
//
|
|
58
|
+
// Pre-558 the only signal of a fail-open security regression was a
|
|
59
|
+
// stderr WARNING line (suppressed/discarded by most adapters). The
|
|
60
|
+
// SessionStart additionalContext block is the in-band channel — the
|
|
61
|
+
// agent reads it, the user sees it. Idempotent by virtue of
|
|
62
|
+
// SessionStart's once-per-session lifecycle.
|
|
63
|
+
try {
|
|
64
|
+
const { initSecurity, isSecurityInitFailed, buildSecurityWarningContext } =
|
|
65
|
+
await import("./core/routing.mjs");
|
|
66
|
+
const { resolve: _resolve } = await import("node:path");
|
|
67
|
+
await initSecurity(_resolve(HOOK_DIR, "..", "build"));
|
|
68
|
+
if (isSecurityInitFailed()) {
|
|
69
|
+
const warning = buildSecurityWarningContext();
|
|
70
|
+
if (warning) additionalContext = warning + "\n\n" + additionalContext;
|
|
71
|
+
}
|
|
72
|
+
} catch { /* security probe is best-effort — never block session start */ }
|
|
73
|
+
|
|
56
74
|
try {
|
|
57
75
|
const raw = await readStdin();
|
|
58
76
|
const input = parseStdin(raw);
|
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.127",
|
|
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.127",
|
|
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",
|
|
@@ -85,9 +85,9 @@
|
|
|
85
85
|
],
|
|
86
86
|
"scripts": {
|
|
87
87
|
"build": "tsc && node -e \"if(process.platform!=='win32'){require('fs').chmodSync('build/cli.js',0o755)}\" && npm run bundle && npm run assert-bundle && npm run assert-asymmetric-drift",
|
|
88
|
-
"assert-bundle": "node scripts/assert-bundle.mjs server.bundle.mjs cli.bundle.mjs hooks/session-extract.bundle.mjs hooks/session-snapshot.bundle.mjs hooks/session-db.bundle.mjs",
|
|
88
|
+
"assert-bundle": "node scripts/assert-bundle.mjs server.bundle.mjs cli.bundle.mjs hooks/session-extract.bundle.mjs hooks/session-snapshot.bundle.mjs hooks/session-db.bundle.mjs hooks/security.bundle.mjs",
|
|
89
89
|
"assert-asymmetric-drift": "node scripts/assert-asymmetric-drift.mjs",
|
|
90
|
-
"bundle": "esbuild src/server.ts --bundle --platform=node --target=node18 --format=esm --outfile=server.bundle.mjs --external:better-sqlite3 --external:turndown --external:turndown-plugin-gfm --external:@mixmark-io/domino --minify && esbuild src/cli.ts --bundle --platform=node --target=node18 --format=esm --outfile=cli.bundle.mjs --external:better-sqlite3 --minify && esbuild src/session/extract.ts --bundle --platform=node --target=node18 --format=esm --outfile=hooks/session-extract.bundle.mjs --minify && esbuild src/session/snapshot.ts --bundle --platform=node --target=node18 --format=esm --outfile=hooks/session-snapshot.bundle.mjs --minify && esbuild src/session/db.ts --bundle --platform=node --target=node18 --format=esm --outfile=hooks/session-db.bundle.mjs --external:better-sqlite3 --minify",
|
|
90
|
+
"bundle": "esbuild src/server.ts --bundle --platform=node --target=node18 --format=esm --outfile=server.bundle.mjs --external:better-sqlite3 --external:turndown --external:turndown-plugin-gfm --external:@mixmark-io/domino --minify && esbuild src/cli.ts --bundle --platform=node --target=node18 --format=esm --outfile=cli.bundle.mjs --external:better-sqlite3 --minify && esbuild src/session/extract.ts --bundle --platform=node --target=node18 --format=esm --outfile=hooks/session-extract.bundle.mjs --minify && esbuild src/session/snapshot.ts --bundle --platform=node --target=node18 --format=esm --outfile=hooks/session-snapshot.bundle.mjs --minify && esbuild src/session/db.ts --bundle --platform=node --target=node18 --format=esm --outfile=hooks/session-db.bundle.mjs --external:better-sqlite3 --minify && esbuild src/security.ts --bundle --platform=node --target=node18 --format=esm --outfile=hooks/security.bundle.mjs --minify",
|
|
91
91
|
"version-sync": "node scripts/version-sync.mjs",
|
|
92
92
|
"version": "node scripts/version-sync.mjs && git add package.json .claude-plugin/plugin.json .claude-plugin/marketplace.json .cursor-plugin/plugin.json .codex-plugin/plugin.json .openclaw-plugin/openclaw.plugin.json .openclaw-plugin/package.json openclaw.plugin.json .pi/extensions/context-mode/package.json",
|
|
93
93
|
"prepublishOnly": "npm run build",
|