context-mode 1.0.21 → 1.0.22
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/build/executor.js +1 -1
- package/build/server.js +1 -1
- package/cli.bundle.mjs +3 -2
- package/hooks/core/routing.mjs +4 -1
- package/hooks/posttooluse.mjs +6 -5
- package/hooks/precompact.mjs +5 -4
- package/hooks/session-db.bundle.mjs +57 -0
- package/hooks/session-extract.bundle.mjs +1 -0
- package/hooks/session-loaders.mjs +28 -0
- package/hooks/session-snapshot.bundle.mjs +14 -0
- package/hooks/sessionstart.mjs +6 -5
- package/hooks/userpromptsubmit.mjs +6 -5
- package/hooks/vscode-copilot/posttooluse.mjs +5 -4
- package/hooks/vscode-copilot/precompact.mjs +5 -4
- package/hooks/vscode-copilot/sessionstart.mjs +5 -4
- package/package.json +2 -2
- package/server.bundle.mjs +3 -2
|
@@ -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.22"
|
|
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.22",
|
|
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.22",
|
|
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",
|
package/build/executor.js
CHANGED
|
@@ -416,7 +416,7 @@ export class PolyglotExecutor {
|
|
|
416
416
|
case "go":
|
|
417
417
|
return `package main\n\nimport (\n\t"fmt"\n\t"os"\n)\n\nvar FILE_CONTENT_PATH = ${escaped}\nvar file_path = FILE_CONTENT_PATH\n\nfunc main() {\n\tb, _ := os.ReadFile(FILE_CONTENT_PATH)\n\tFILE_CONTENT := string(b)\n\t_ = FILE_CONTENT\n\t_ = fmt.Sprint()\n${code}\n}\n`;
|
|
418
418
|
case "rust":
|
|
419
|
-
return
|
|
419
|
+
return `#[allow(unused_variables)]\nuse std::fs;\n\nfn main() {\n let file_content_path = ${escaped};\n let file_path = file_content_path;\n let file_content = fs::read_to_string(file_content_path).unwrap();\n${code}\n}\n`;
|
|
420
420
|
case "php":
|
|
421
421
|
return `<?php\n$FILE_CONTENT_PATH = ${escaped};\n$file_path = $FILE_CONTENT_PATH;\n$FILE_CONTENT = file_get_contents($FILE_CONTENT_PATH);\n${code}`;
|
|
422
422
|
case "perl":
|
package/build/server.js
CHANGED
|
@@ -14,7 +14,7 @@ import { readBashPolicies, evaluateCommandDenyOnly, extractShellCommands, readTo
|
|
|
14
14
|
import { detectRuntimes, getRuntimeSummary, getAvailableLanguages, hasBunRuntime, } from "./runtime.js";
|
|
15
15
|
import { classifyNonZeroExit } from "./exit-classify.js";
|
|
16
16
|
import { startLifecycleGuard } from "./lifecycle.js";
|
|
17
|
-
const VERSION = "1.0.
|
|
17
|
+
const VERSION = "1.0.22";
|
|
18
18
|
// Prevent silent server death from unhandled async errors
|
|
19
19
|
process.on("unhandledRejection", (err) => {
|
|
20
20
|
process.stderr.write(`[context-mode] unhandledRejection: ${err}\n`);
|
package/cli.bundle.mjs
CHANGED
|
@@ -110,7 +110,8 @@ func main() {
|
|
|
110
110
|
_ = fmt.Sprint()
|
|
111
111
|
${n}
|
|
112
112
|
}
|
|
113
|
-
`;case"rust":return
|
|
113
|
+
`;case"rust":return`#[allow(unused_variables)]
|
|
114
|
+
use std::fs;
|
|
114
115
|
|
|
115
116
|
fn main() {
|
|
116
117
|
let file_content_path = ${o};
|
|
@@ -293,7 +294,7 @@ async function main() {
|
|
|
293
294
|
main();
|
|
294
295
|
`}async function sN(){let t=S0();t>0&&console.error(`Cleaned up ${t} stale DB file(s) from previous sessions`);let e=()=>{Ds.cleanupBackgrounded(),mo&&mo.cleanup()},r=async()=>{e(),process.exit(0)};process.on("exit",e),process.on("SIGINT",()=>{r()}),process.on("SIGTERM",()=>{r()}),N0({onShutdown:()=>r()});let n=new lc;await Qt.connect(n);try{let{detectPlatform:o,getAdapter:s}=await Promise.resolve().then(()=>(eu(),jh)),i=o(),a=await s(i.platform);if(!a.capabilities.sessionStart){let c=Bz(Ms(Am(import.meta.url)),".."),u=process.env.CLAUDE_PROJECT_DIR??process.env.CODEX_HOME??process.cwd(),l=a.writeRoutingInstructions(u,c);l&&console.error(`Wrote routing instructions: ${l}`)}}catch{}console.error(`Context Mode MCP server v${L0} running on stdio`),console.error(`Detected runtimes:
|
|
295
296
|
${Js(jm)}`),_o()||(console.error(`
|
|
296
|
-
Performance tip: Install Bun for 3-5x faster JS/TS execution`),console.error(" curl -fsSL https://bun.sh/install | bash"))}var L0,jm,Kz,Qt,Ds,mo,Ye,Yz,Xz,Qz,eN,mc,$n,Om,tN,j0,M0,Cm,zm,H0=b(()=>{"use strict";o0();c0();$m();wm();k0();C0();Xs();z0();A0();L0="1.0.
|
|
297
|
+
Performance tip: Install Bun for 3-5x faster JS/TS execution`),console.error(" curl -fsSL https://bun.sh/install | bash"))}var L0,jm,Kz,Qt,Ds,mo,Ye,Yz,Xz,Qz,eN,mc,$n,Om,tN,j0,M0,Cm,zm,H0=b(()=>{"use strict";o0();c0();$m();wm();k0();C0();Xs();z0();A0();L0="1.0.21";process.on("unhandledRejection",t=>{process.stderr.write(`[context-mode] unhandledRejection: ${t}
|
|
297
298
|
`)});process.on("uncaughtException",t=>{process.stderr.write(`[context-mode] uncaughtException: ${t?.message??t}
|
|
298
299
|
`)});jm=wn(),Kz=Ys(jm),Qt=new cc({name:"context-mode",version:L0}),Ds=new js({runtimes:jm,projectRoot:process.env.CLAUDE_PROJECT_DIR}),mo=null;Ye={calls:{},bytesReturned:{},bytesIndexed:0,bytesSandboxed:0,sessionStart:Date.now()};Yz=Kz.join(", "),Xz=_o()?" (Bun detected \u2014 JS/TS runs 3-5x faster)":"",Qz="",eN="";Qt.registerTool("ctx_execute",{title:"Execute Code",description:`MANDATORY: Use for any command where output exceeds 20 lines. Execute code in a sandboxed subprocess. Only stdout enters context \u2014 raw data stays in the subprocess.${Xz} Available: ${Yz}.
|
|
299
300
|
|
package/hooks/core/routing.mjs
CHANGED
|
@@ -22,7 +22,10 @@ import { resolve } from "node:path";
|
|
|
22
22
|
// (Claude Code, Gemini, Cursor, VS Code Copilot)
|
|
23
23
|
// Session scoped via process.ppid (= host PID, constant for session lifetime).
|
|
24
24
|
const _guidanceShown = new Set();
|
|
25
|
-
const
|
|
25
|
+
const _guidanceId = process.env.VITEST_WORKER_ID
|
|
26
|
+
? `${process.ppid}-w${process.env.VITEST_WORKER_ID}`
|
|
27
|
+
: String(process.ppid);
|
|
28
|
+
const _guidanceDir = resolve(tmpdir(), `context-mode-guidance-${_guidanceId}`);
|
|
26
29
|
|
|
27
30
|
function guidanceOnce(type, content) {
|
|
28
31
|
// Fast path: in-memory (same process)
|
package/hooks/posttooluse.mjs
CHANGED
|
@@ -10,20 +10,21 @@ import "./suppress-stderr.mjs";
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { readStdin, getSessionId, getSessionDBPath } from "./session-helpers.mjs";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
13
|
+
import { createSessionLoaders } from "./session-loaders.mjs";
|
|
14
|
+
import { dirname } from "node:path";
|
|
15
|
+
import { fileURLToPath } from "node:url";
|
|
15
16
|
|
|
16
17
|
// Resolve absolute path for imports — relative dynamic imports can fail
|
|
17
18
|
// when Claude Code invokes hooks from a different working directory.
|
|
18
19
|
const HOOK_DIR = dirname(fileURLToPath(import.meta.url));
|
|
19
|
-
const
|
|
20
|
+
const { loadSessionDB, loadExtract } = createSessionLoaders(HOOK_DIR);
|
|
20
21
|
|
|
21
22
|
try {
|
|
22
23
|
const raw = await readStdin();
|
|
23
24
|
const input = JSON.parse(raw);
|
|
24
25
|
|
|
25
|
-
const { extractEvents } = await
|
|
26
|
-
const { SessionDB } = await
|
|
26
|
+
const { extractEvents } = await loadExtract();
|
|
27
|
+
const { SessionDB } = await loadSessionDB();
|
|
27
28
|
|
|
28
29
|
const dbPath = getSessionDBPath();
|
|
29
30
|
const db = new SessionDB({ dbPath });
|
package/hooks/precompact.mjs
CHANGED
|
@@ -9,22 +9,23 @@ import "./suppress-stderr.mjs";
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { readStdin, getSessionId, getSessionDBPath } from "./session-helpers.mjs";
|
|
12
|
+
import { createSessionLoaders } from "./session-loaders.mjs";
|
|
12
13
|
import { appendFileSync } from "node:fs";
|
|
13
14
|
import { join, dirname } from "node:path";
|
|
14
15
|
import { homedir } from "node:os";
|
|
15
|
-
import { fileURLToPath
|
|
16
|
+
import { fileURLToPath } from "node:url";
|
|
16
17
|
|
|
17
18
|
// Resolve absolute path for imports
|
|
18
19
|
const HOOK_DIR = dirname(fileURLToPath(import.meta.url));
|
|
19
|
-
const
|
|
20
|
+
const { loadSessionDB, loadSnapshot } = createSessionLoaders(HOOK_DIR);
|
|
20
21
|
const DEBUG_LOG = join(homedir(), ".claude", "context-mode", "precompact-debug.log");
|
|
21
22
|
|
|
22
23
|
try {
|
|
23
24
|
const raw = await readStdin();
|
|
24
25
|
const input = JSON.parse(raw);
|
|
25
26
|
|
|
26
|
-
const { buildResumeSnapshot } = await
|
|
27
|
-
const { SessionDB } = await
|
|
27
|
+
const { buildResumeSnapshot } = await loadSnapshot();
|
|
28
|
+
const { SessionDB } = await loadSessionDB();
|
|
28
29
|
|
|
29
30
|
const dbPath = getSessionDBPath();
|
|
30
31
|
const db = new SessionDB({ dbPath });
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import{createRequire as m}from"node:module";import{unlinkSync as p}from"node:fs";import{tmpdir as _}from"node:os";import{join as T}from"node:path";var E=null;function g(){return E||(E=m(import.meta.url)("better-sqlite3")),E}function R(i){i.pragma("journal_mode = WAL"),i.pragma("synchronous = NORMAL")}function h(i){for(let t of["","-wal","-shm"])try{p(i+t)}catch{}}function d(i){try{i.pragma("wal_checkpoint(TRUNCATE)")}catch{}try{i.close()}catch{}}function c(i="context-mode"){return T(_(),`${i}-${process.pid}.db`)}var a=class{#e;#t;constructor(t){let s=g();this.#e=t,this.#t=new s(t,{timeout:5e3}),R(this.#t),this.initSchema(),this.prepareStatements()}get db(){return this.#t}get dbPath(){return this.#e}close(){d(this.#t)}cleanup(){d(this.#t),h(this.#e)}};import{createHash as y}from"node:crypto";var v=1e3,l=5,e={insertEvent:"insertEvent",getEvents:"getEvents",getEventsByType:"getEventsByType",getEventsByPriority:"getEventsByPriority",getEventsByTypeAndPriority:"getEventsByTypeAndPriority",getEventCount:"getEventCount",checkDuplicate:"checkDuplicate",evictLowestPriority:"evictLowestPriority",updateMetaLastEvent:"updateMetaLastEvent",ensureSession:"ensureSession",getSessionStats:"getSessionStats",incrementCompactCount:"incrementCompactCount",upsertResume:"upsertResume",getResume:"getResume",markResumeConsumed:"markResumeConsumed",deleteEvents:"deleteEvents",deleteMeta:"deleteMeta",deleteResume:"deleteResume",getOldSessions:"getOldSessions"},u=class extends a{constructor(t){super(t?.dbPath??c("session"))}stmt(t){return this.stmts.get(t)}initSchema(){try{let s=this.db.pragma("table_xinfo(session_events)").find(n=>n.name==="data_hash");s&&s.hidden!==0&&this.db.exec("DROP TABLE session_events")}catch{}this.db.exec(`
|
|
2
|
+
CREATE TABLE IF NOT EXISTS session_events (
|
|
3
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
4
|
+
session_id TEXT NOT NULL,
|
|
5
|
+
type TEXT NOT NULL,
|
|
6
|
+
category TEXT NOT NULL,
|
|
7
|
+
priority INTEGER NOT NULL DEFAULT 2,
|
|
8
|
+
data TEXT NOT NULL,
|
|
9
|
+
source_hook TEXT NOT NULL,
|
|
10
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
11
|
+
data_hash TEXT NOT NULL DEFAULT ''
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
CREATE INDEX IF NOT EXISTS idx_session_events_session ON session_events(session_id);
|
|
15
|
+
CREATE INDEX IF NOT EXISTS idx_session_events_type ON session_events(session_id, type);
|
|
16
|
+
CREATE INDEX IF NOT EXISTS idx_session_events_priority ON session_events(session_id, priority);
|
|
17
|
+
|
|
18
|
+
CREATE TABLE IF NOT EXISTS session_meta (
|
|
19
|
+
session_id TEXT PRIMARY KEY,
|
|
20
|
+
project_dir TEXT NOT NULL,
|
|
21
|
+
started_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
22
|
+
last_event_at TEXT,
|
|
23
|
+
event_count INTEGER NOT NULL DEFAULT 0,
|
|
24
|
+
compact_count INTEGER NOT NULL DEFAULT 0
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
CREATE TABLE IF NOT EXISTS session_resume (
|
|
28
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
29
|
+
session_id TEXT NOT NULL UNIQUE,
|
|
30
|
+
snapshot TEXT NOT NULL,
|
|
31
|
+
event_count INTEGER NOT NULL,
|
|
32
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
33
|
+
consumed INTEGER NOT NULL DEFAULT 0
|
|
34
|
+
);
|
|
35
|
+
`)}prepareStatements(){this.stmts=new Map;let t=(s,n)=>{this.stmts.set(s,this.db.prepare(n))};t(e.insertEvent,`INSERT INTO session_events (session_id, type, category, priority, data, source_hook, data_hash)
|
|
36
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`),t(e.getEvents,`SELECT id, session_id, type, category, priority, data, source_hook, created_at, data_hash
|
|
37
|
+
FROM session_events WHERE session_id = ? ORDER BY id ASC LIMIT ?`),t(e.getEventsByType,`SELECT id, session_id, type, category, priority, data, source_hook, created_at, data_hash
|
|
38
|
+
FROM session_events WHERE session_id = ? AND type = ? ORDER BY id ASC LIMIT ?`),t(e.getEventsByPriority,`SELECT id, session_id, type, category, priority, data, source_hook, created_at, data_hash
|
|
39
|
+
FROM session_events WHERE session_id = ? AND priority >= ? ORDER BY id ASC LIMIT ?`),t(e.getEventsByTypeAndPriority,`SELECT id, session_id, type, category, priority, data, source_hook, created_at, data_hash
|
|
40
|
+
FROM session_events WHERE session_id = ? AND type = ? AND priority >= ? ORDER BY id ASC LIMIT ?`),t(e.getEventCount,"SELECT COUNT(*) AS cnt FROM session_events WHERE session_id = ?"),t(e.checkDuplicate,`SELECT 1 FROM (
|
|
41
|
+
SELECT type, data_hash FROM session_events
|
|
42
|
+
WHERE session_id = ? ORDER BY id DESC LIMIT ?
|
|
43
|
+
) AS recent
|
|
44
|
+
WHERE recent.type = ? AND recent.data_hash = ?
|
|
45
|
+
LIMIT 1`),t(e.evictLowestPriority,`DELETE FROM session_events WHERE id = (
|
|
46
|
+
SELECT id FROM session_events WHERE session_id = ?
|
|
47
|
+
ORDER BY priority ASC, id ASC LIMIT 1
|
|
48
|
+
)`),t(e.updateMetaLastEvent,`UPDATE session_meta
|
|
49
|
+
SET last_event_at = datetime('now'), event_count = event_count + 1
|
|
50
|
+
WHERE session_id = ?`),t(e.ensureSession,"INSERT OR IGNORE INTO session_meta (session_id, project_dir) VALUES (?, ?)"),t(e.getSessionStats,`SELECT session_id, project_dir, started_at, last_event_at, event_count, compact_count
|
|
51
|
+
FROM session_meta WHERE session_id = ?`),t(e.incrementCompactCount,"UPDATE session_meta SET compact_count = compact_count + 1 WHERE session_id = ?"),t(e.upsertResume,`INSERT INTO session_resume (session_id, snapshot, event_count)
|
|
52
|
+
VALUES (?, ?, ?)
|
|
53
|
+
ON CONFLICT(session_id) DO UPDATE SET
|
|
54
|
+
snapshot = excluded.snapshot,
|
|
55
|
+
event_count = excluded.event_count,
|
|
56
|
+
created_at = datetime('now'),
|
|
57
|
+
consumed = 0`),t(e.getResume,"SELECT snapshot, event_count, consumed FROM session_resume WHERE session_id = ?"),t(e.markResumeConsumed,"UPDATE session_resume SET consumed = 1 WHERE session_id = ?"),t(e.deleteEvents,"DELETE FROM session_events WHERE session_id = ?"),t(e.deleteMeta,"DELETE FROM session_meta WHERE session_id = ?"),t(e.deleteResume,"DELETE FROM session_resume WHERE session_id = ?"),t(e.getOldSessions,"SELECT session_id FROM session_meta WHERE started_at < datetime('now', ? || ' days')")}insertEvent(t,s,n="PostToolUse"){let o=y("sha256").update(s.data).digest("hex").slice(0,16).toUpperCase();this.db.transaction(()=>{if(this.stmt(e.checkDuplicate).get(t,l,s.type,o))return;this.stmt(e.getEventCount).get(t).cnt>=v&&this.stmt(e.evictLowestPriority).run(t),this.stmt(e.insertEvent).run(t,s.type,s.category,s.priority,s.data,n,o),this.stmt(e.updateMetaLastEvent).run(t)})()}getEvents(t,s){let n=s?.limit??1e3,o=s?.type,r=s?.minPriority;return o&&r!==void 0?this.stmt(e.getEventsByTypeAndPriority).all(t,o,r,n):o?this.stmt(e.getEventsByType).all(t,o,n):r!==void 0?this.stmt(e.getEventsByPriority).all(t,r,n):this.stmt(e.getEvents).all(t,n)}getEventCount(t){return this.stmt(e.getEventCount).get(t).cnt}ensureSession(t,s){this.stmt(e.ensureSession).run(t,s)}getSessionStats(t){return this.stmt(e.getSessionStats).get(t)??null}incrementCompactCount(t){this.stmt(e.incrementCompactCount).run(t)}upsertResume(t,s,n){this.stmt(e.upsertResume).run(t,s,n??0)}getResume(t){return this.stmt(e.getResume).get(t)??null}markResumeConsumed(t){this.stmt(e.markResumeConsumed).run(t)}deleteSession(t){this.db.transaction(()=>{this.stmt(e.deleteEvents).run(t),this.stmt(e.deleteResume).run(t),this.stmt(e.deleteMeta).run(t)})()}cleanupOldSessions(t=7){let s=`-${t}`,n=this.stmt(e.getOldSessions).all(s);for(let{session_id:o}of n)this.deleteSession(o);return n.length}};export{u as SessionDB};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function o(t,e=300){return t==null?"":t.length<=e?t:t.slice(0,e)}function c(t,e=300){if(t==null)return"";let n=typeof t=="string"?t:JSON.stringify(t);return o(n,e)}function l(t){let{tool_name:e,tool_input:n,tool_response:s}=t,r=[];if(e==="Read"){let i=String(n.file_path??"");return/CLAUDE\.md$|\.claude[\\/]/i.test(i)&&(r.push({type:"rule",category:"rule",data:o(i),priority:1}),s&&s.length>0&&r.push({type:"rule_content",category:"rule",data:o(s,5e3),priority:1})),r.push({type:"file_read",category:"file",data:o(i),priority:1}),r}if(e==="Edit"){let i=String(n.file_path??"");return r.push({type:"file_edit",category:"file",data:o(i),priority:1}),r}if(e==="NotebookEdit"){let i=String(n.notebook_path??"");return r.push({type:"file_edit",category:"file",data:o(i),priority:1}),r}if(e==="Write"){let i=String(n.file_path??"");return r.push({type:"file_write",category:"file",data:o(i),priority:1}),r}if(e==="Glob"){let i=String(n.pattern??"");return r.push({type:"file_glob",category:"file",data:o(i),priority:3}),r}if(e==="Grep"){let i=String(n.pattern??""),a=String(n.path??"");return r.push({type:"file_search",category:"file",data:o(`${i} in ${a}`),priority:3}),r}return r}function u(t){if(t.tool_name!=="Bash")return[];let n=String(t.tool_input.command??"").match(/\bcd\s+("([^"]+)"|'([^']+)'|(\S+))/);if(!n)return[];let s=n[2]??n[3]??n[4]??"";return[{type:"cwd",category:"cwd",data:o(s),priority:2}]}function d(t){let{tool_name:e,tool_input:n,tool_response:s,tool_output:r}=t,i=String(s??""),a=r?.isError===!0;return!(e==="Bash"&&/exit code [1-9]|error:|Error:|FAIL|failed/i.test(i))&&!a?[]:[{type:"error_tool",category:"error",data:o(i,300),priority:2}]}var g=[{pattern:/\bgit\s+checkout\b/,operation:"branch"},{pattern:/\bgit\s+commit\b/,operation:"commit"},{pattern:/\bgit\s+merge\s+\S+/,operation:"merge"},{pattern:/\bgit\s+rebase\b/,operation:"rebase"},{pattern:/\bgit\s+stash\b/,operation:"stash"},{pattern:/\bgit\s+push\b/,operation:"push"},{pattern:/\bgit\s+pull\b/,operation:"pull"},{pattern:/\bgit\s+log\b/,operation:"log"},{pattern:/\bgit\s+diff\b/,operation:"diff"},{pattern:/\bgit\s+status\b/,operation:"status"},{pattern:/\bgit\s+branch\b/,operation:"branch"},{pattern:/\bgit\s+reset\b/,operation:"reset"},{pattern:/\bgit\s+add\b/,operation:"add"},{pattern:/\bgit\s+cherry-pick\b/,operation:"cherry-pick"},{pattern:/\bgit\s+tag\b/,operation:"tag"},{pattern:/\bgit\s+fetch\b/,operation:"fetch"},{pattern:/\bgit\s+clone\b/,operation:"clone"},{pattern:/\bgit\s+worktree\b/,operation:"worktree"}];function b(t){if(t.tool_name!=="Bash")return[];let e=String(t.tool_input.command??""),n=g.find(s=>s.pattern.test(e));return n?[{type:"git",category:"git",data:o(n.operation),priority:2}]:[]}function y(t){return new Set(["TodoWrite","TaskCreate","TaskUpdate"]).has(t.tool_name)?[{type:t.tool_name==="TaskUpdate"?"task_update":t.tool_name==="TaskCreate"?"task_create":"task",category:"task",data:o(JSON.stringify(t.tool_input),300),priority:1}]:[]}function f(t){if(t.tool_name==="EnterPlanMode")return[{type:"plan_enter",category:"plan",data:"entered plan mode",priority:2}];if(t.tool_name==="ExitPlanMode"){let e=[],n=t.tool_input.allowedPrompts,s=Array.isArray(n)&&n.length>0?`exited plan mode (allowed: ${c(n.map(i=>typeof i=="object"&&i!==null&&"prompt"in i?String(i.prompt):String(i)).join(", "),200)})`:"exited plan mode";e.push({type:"plan_exit",category:"plan",data:o(s),priority:2});let r=String(t.tool_response??"").toLowerCase();return r.includes("approved")||r.includes("approve")?e.push({type:"plan_approved",category:"plan",data:"plan approved by user",priority:1}):(r.includes("rejected")||r.includes("decline")||r.includes("denied"))&&e.push({type:"plan_rejected",category:"plan",data:o(`plan rejected: ${t.tool_response??""}`,300),priority:2}),e}if(t.tool_name==="Write"||t.tool_name==="Edit"){let e=String(t.tool_input.file_path??"");if(/[/\\]\.claude[/\\]plans[/\\]/.test(e))return[{type:"plan_file_write",category:"plan",data:o(`plan file: ${e.split(/[/\\]/).pop()??e}`),priority:2}]}return[]}var h=[/\bsource\s+\S*activate\b/,/\bexport\s+\w+=/,/\bnvm\s+use\b/,/\bpyenv\s+(shell|local|global)\b/,/\bconda\s+activate\b/,/\brbenv\s+(shell|local|global)\b/,/\bnpm\s+install\b/,/\bnpm\s+ci\b/,/\bpip\s+install\b/,/\bbun\s+install\b/,/\byarn\s+(add|install)\b/,/\bpnpm\s+(add|install)\b/,/\bcargo\s+(install|add)\b/,/\bgo\s+(install|get)\b/,/\brustup\b/,/\basdf\b/,/\bvolta\b/,/\bdeno\s+install\b/];function _(t){if(t.tool_name!=="Bash")return[];let e=String(t.tool_input.command??"");if(!h.some(r=>r.test(e)))return[];let s=e.replace(/\bexport\s+(\w+)=\S*/g,"export $1=***");return[{type:"env",category:"env",data:o(s),priority:2}]}function m(t){if(t.tool_name!=="Skill")return[];let e=String(t.tool_input.skill??"");return[{type:"skill",category:"skill",data:o(e),priority:3}]}function S(t){if(t.tool_name!=="Agent")return[];let e=o(String(t.tool_input.prompt??t.tool_input.description??""),200),n=t.tool_response?o(String(t.tool_response),300):"",s=n.length>0;return[{type:s?"subagent_completed":"subagent_launched",category:"subagent",data:o(s?`[completed] ${e} \u2192 ${n}`:`[launched] ${e}`,300),priority:s?2:3}]}function k(t){let{tool_name:e,tool_input:n}=t;if(!e.startsWith("mcp__"))return[];let s=e.split("__"),r=s[s.length-1]||e,i=Object.values(n).find(p=>typeof p=="string"),a=i?`: ${o(String(i),100)}`:"";return[{type:"mcp",category:"mcp",data:o(`${r}${a}`),priority:3}]}function E(t){if(t.tool_name!=="AskUserQuestion")return[];let e=t.tool_input.questions,n=Array.isArray(e)&&e.length>0?String(e[0].question??""):"",s=o(String(t.tool_response??""),150),r=n?`Q: ${o(n,120)} \u2192 A: ${s}`:`answer: ${s}`;return[{type:"decision_question",category:"decision",data:o(r),priority:2}]}function v(t){if(t.tool_name!=="EnterWorktree")return[];let e=String(t.tool_input.name??"unnamed");return[{type:"worktree",category:"env",data:o(`entered worktree: ${e}`),priority:2}]}var x=[/\b(don'?t|do not|never|always|instead|rather|prefer)\b/i,/\b(use|switch to|go with|pick|choose)\s+\w+\s+(instead|over|not)\b/i,/\b(no,?\s+(use|do|try|make))\b/i,/\b(hayır|hayir|evet|böyle|boyle|degil|değil|yerine|kullan)\b/i];function w(t){return x.some(n=>n.test(t))?[{type:"decision",category:"decision",data:o(t,300),priority:2}]:[]}var T=[/\b(act as|you are|behave like|pretend|role of|persona)\b/i,/\b(senior|staff|principal|lead)\s+(engineer|developer|architect)\b/i,/\b(gibi davran|rolünde|olarak çalış)\b/i];function R(t){return T.some(n=>n.test(t))?[{type:"role",category:"role",data:o(t,300),priority:3}]:[]}var A=[{mode:"investigate",pattern:/\b(why|how does|explain|understand|what is|analyze|debug|look into)\b/i},{mode:"implement",pattern:/\b(create|add|build|implement|write|make|develop|fix)\b/i},{mode:"discuss",pattern:/\b(think about|consider|should we|what if|pros and cons|opinion)\b/i},{mode:"review",pattern:/\b(review|check|audit|verify|test|validate)\b/i}];function I(t){let e=A.find(({pattern:n})=>n.test(t));return e?[{type:"intent",category:"intent",data:o(e.mode),priority:4}]:[]}function $(t){return t.length<=1024?[]:[{type:"data",category:"data",data:o(t,200),priority:4}]}function P(t){try{let e=[];return e.push(...l(t)),e.push(...u(t)),e.push(...d(t)),e.push(...b(t)),e.push(..._(t)),e.push(...y(t)),e.push(...f(t)),e.push(...m(t)),e.push(...S(t)),e.push(...k(t)),e.push(...E(t)),e.push(...v(t)),e}catch{return[]}}function H(t){try{let e=[];return e.push(...w(t)),e.push(...R(t)),e.push(...I(t)),e.push(...$(t)),e}catch{return[]}}export{P as extractEvents,H as extractUserEvents};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session module loaders — bundle-only.
|
|
3
|
+
*
|
|
4
|
+
* All session modules are loaded from esbuild bundles (hooks/session-*.bundle.mjs).
|
|
5
|
+
* Bundles are built by CI (bundle.yml) and shipped with every release.
|
|
6
|
+
* No fallback to build/ — if the bundle is missing, the error surfaces immediately.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { pathToFileURL } from "node:url";
|
|
11
|
+
|
|
12
|
+
export function createSessionLoaders(hookDir) {
|
|
13
|
+
const bundleDir = hookDir.endsWith("vscode-copilot")
|
|
14
|
+
? join(hookDir, "..")
|
|
15
|
+
: hookDir;
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
async loadSessionDB() {
|
|
19
|
+
return await import(pathToFileURL(join(bundleDir, "session-db.bundle.mjs")).href);
|
|
20
|
+
},
|
|
21
|
+
async loadExtract() {
|
|
22
|
+
return await import(pathToFileURL(join(bundleDir, "session-extract.bundle.mjs")).href);
|
|
23
|
+
},
|
|
24
|
+
async loadSnapshot() {
|
|
25
|
+
return await import(pathToFileURL(join(bundleDir, "session-snapshot.bundle.mjs")).href);
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +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,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}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(`
|
|
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};
|
package/hooks/sessionstart.mjs
CHANGED
|
@@ -17,14 +17,15 @@ import "./suppress-stderr.mjs";
|
|
|
17
17
|
import { ROUTING_BLOCK } from "./routing-block.mjs";
|
|
18
18
|
import { readStdin, getSessionId, getSessionDBPath, getSessionEventsPath, getCleanupFlagPath } from "./session-helpers.mjs";
|
|
19
19
|
import { writeSessionEventsFile, buildSessionDirective, getSessionEvents, getLatestSessionEvents } from "./session-directive.mjs";
|
|
20
|
+
import { createSessionLoaders } from "./session-loaders.mjs";
|
|
20
21
|
import { join, dirname } from "node:path";
|
|
21
|
-
import { fileURLToPath
|
|
22
|
+
import { fileURLToPath } from "node:url";
|
|
22
23
|
import { readFileSync, writeFileSync, unlinkSync } from "node:fs";
|
|
23
24
|
import { homedir } from "node:os";
|
|
24
25
|
|
|
25
26
|
// Resolve absolute path for imports (fileURLToPath for Windows compat)
|
|
26
27
|
const HOOK_DIR = dirname(fileURLToPath(import.meta.url));
|
|
27
|
-
const
|
|
28
|
+
const { loadSessionDB } = createSessionLoaders(HOOK_DIR);
|
|
28
29
|
|
|
29
30
|
let additionalContext = ROUTING_BLOCK;
|
|
30
31
|
|
|
@@ -35,7 +36,7 @@ try {
|
|
|
35
36
|
|
|
36
37
|
if (source === "compact") {
|
|
37
38
|
// Session was compacted — write events to file for auto-indexing, inject directive only
|
|
38
|
-
const { SessionDB } = await
|
|
39
|
+
const { SessionDB } = await loadSessionDB();
|
|
39
40
|
const dbPath = getSessionDBPath();
|
|
40
41
|
const db = new SessionDB({ dbPath });
|
|
41
42
|
const sessionId = getSessionId(input);
|
|
@@ -56,7 +57,7 @@ try {
|
|
|
56
57
|
// User used --continue — clear cleanup flag so startup doesn't wipe data
|
|
57
58
|
try { unlinkSync(getCleanupFlagPath()); } catch { /* no flag */ }
|
|
58
59
|
|
|
59
|
-
const { SessionDB } = await
|
|
60
|
+
const { SessionDB } = await loadSessionDB();
|
|
60
61
|
const dbPath = getSessionDBPath();
|
|
61
62
|
const db = new SessionDB({ dbPath });
|
|
62
63
|
|
|
@@ -69,7 +70,7 @@ try {
|
|
|
69
70
|
db.close();
|
|
70
71
|
} else if (source === "startup") {
|
|
71
72
|
// Fresh session (no --continue) — clean slate, capture CLAUDE.md rules.
|
|
72
|
-
const { SessionDB } = await
|
|
73
|
+
const { SessionDB } = await loadSessionDB();
|
|
73
74
|
const dbPath = getSessionDBPath();
|
|
74
75
|
const db = new SessionDB({ dbPath });
|
|
75
76
|
try { unlinkSync(getSessionEventsPath()); } catch { /* no stale file */ }
|
|
@@ -10,11 +10,12 @@ import "./suppress-stderr.mjs";
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { readStdin, getSessionId, getSessionDBPath } from "./session-helpers.mjs";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
13
|
+
import { createSessionLoaders } from "./session-loaders.mjs";
|
|
14
|
+
import { dirname } from "node:path";
|
|
15
|
+
import { fileURLToPath } from "node:url";
|
|
15
16
|
|
|
16
17
|
const HOOK_DIR = dirname(fileURLToPath(import.meta.url));
|
|
17
|
-
const
|
|
18
|
+
const { loadSessionDB, loadExtract } = createSessionLoaders(HOOK_DIR);
|
|
18
19
|
|
|
19
20
|
try {
|
|
20
21
|
const raw = await readStdin();
|
|
@@ -30,8 +31,8 @@ try {
|
|
|
30
31
|
|| trimmed.startsWith("<tool-result>");
|
|
31
32
|
|
|
32
33
|
if (trimmed.length > 0 && !isSystemMessage) {
|
|
33
|
-
const { SessionDB } = await
|
|
34
|
-
const { extractUserEvents } = await
|
|
34
|
+
const { SessionDB } = await loadSessionDB();
|
|
35
|
+
const { extractUserEvents } = await loadExtract();
|
|
35
36
|
const dbPath = getSessionDBPath();
|
|
36
37
|
const db = new SessionDB({ dbPath });
|
|
37
38
|
const sessionId = getSessionId(input);
|
|
@@ -9,14 +9,15 @@ import "../suppress-stderr.mjs";
|
|
|
9
9
|
* Must be fast (<20ms). No network, no LLM, just SQLite writes.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
import { createSessionLoaders } from "../session-loaders.mjs";
|
|
12
13
|
import { readStdin, getSessionId, getSessionDBPath, getProjectDir, VSCODE_OPTS } from "../session-helpers.mjs";
|
|
13
14
|
import { appendFileSync } from "node:fs";
|
|
14
15
|
import { join, dirname } from "node:path";
|
|
15
|
-
import { fileURLToPath
|
|
16
|
+
import { fileURLToPath } from "node:url";
|
|
16
17
|
import { homedir } from "node:os";
|
|
17
18
|
|
|
18
19
|
const HOOK_DIR = dirname(fileURLToPath(import.meta.url));
|
|
19
|
-
const
|
|
20
|
+
const { loadSessionDB, loadExtract } = createSessionLoaders(HOOK_DIR);
|
|
20
21
|
const OPTS = VSCODE_OPTS;
|
|
21
22
|
const DEBUG_LOG = join(homedir(), ".vscode", "context-mode", "posttooluse-debug.log");
|
|
22
23
|
|
|
@@ -26,8 +27,8 @@ try {
|
|
|
26
27
|
|
|
27
28
|
appendFileSync(DEBUG_LOG, `[${new Date().toISOString()}] CALL: ${input.tool_name}\n`);
|
|
28
29
|
|
|
29
|
-
const { extractEvents } = await
|
|
30
|
-
const { SessionDB } = await
|
|
30
|
+
const { extractEvents } = await loadExtract();
|
|
31
|
+
const { SessionDB } = await loadSessionDB();
|
|
31
32
|
|
|
32
33
|
const dbPath = getSessionDBPath(OPTS);
|
|
33
34
|
const db = new SessionDB({ dbPath });
|
|
@@ -8,14 +8,15 @@ import "../suppress-stderr.mjs";
|
|
|
8
8
|
* snapshot (<2KB XML), and stores it for injection after compact.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import { createSessionLoaders } from "../session-loaders.mjs";
|
|
11
12
|
import { readStdin, getSessionId, getSessionDBPath, VSCODE_OPTS } from "../session-helpers.mjs";
|
|
12
13
|
import { appendFileSync } from "node:fs";
|
|
13
14
|
import { join, dirname } from "node:path";
|
|
14
|
-
import { fileURLToPath
|
|
15
|
+
import { fileURLToPath } from "node:url";
|
|
15
16
|
import { homedir } from "node:os";
|
|
16
17
|
|
|
17
18
|
const HOOK_DIR = dirname(fileURLToPath(import.meta.url));
|
|
18
|
-
const
|
|
19
|
+
const { loadSessionDB, loadSnapshot } = createSessionLoaders(HOOK_DIR);
|
|
19
20
|
const OPTS = VSCODE_OPTS;
|
|
20
21
|
const DEBUG_LOG = join(homedir(), ".vscode", "context-mode", "precompact-debug.log");
|
|
21
22
|
|
|
@@ -23,8 +24,8 @@ try {
|
|
|
23
24
|
const raw = await readStdin();
|
|
24
25
|
const input = JSON.parse(raw);
|
|
25
26
|
|
|
26
|
-
const { buildResumeSnapshot } = await
|
|
27
|
-
const { SessionDB } = await
|
|
27
|
+
const { buildResumeSnapshot } = await loadSnapshot();
|
|
28
|
+
const { SessionDB } = await loadSessionDB();
|
|
28
29
|
|
|
29
30
|
const dbPath = getSessionDBPath(OPTS);
|
|
30
31
|
const db = new SessionDB({ dbPath });
|
|
@@ -10,6 +10,7 @@ import "../suppress-stderr.mjs";
|
|
|
10
10
|
* - "clear" → No action needed
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import { createSessionLoaders } from "../session-loaders.mjs";
|
|
13
14
|
import { ROUTING_BLOCK } from "../routing-block.mjs";
|
|
14
15
|
import { writeSessionEventsFile, buildSessionDirective, getSessionEvents, getLatestSessionEvents } from "../session-directive.mjs";
|
|
15
16
|
import {
|
|
@@ -22,7 +23,7 @@ import { fileURLToPath, pathToFileURL } from "node:url";
|
|
|
22
23
|
import { homedir } from "node:os";
|
|
23
24
|
|
|
24
25
|
const HOOK_DIR = fileURLToPath(new URL(".", import.meta.url));
|
|
25
|
-
const
|
|
26
|
+
const { loadSessionDB } = createSessionLoaders(HOOK_DIR);
|
|
26
27
|
const OPTS = VSCODE_OPTS;
|
|
27
28
|
|
|
28
29
|
let additionalContext = ROUTING_BLOCK;
|
|
@@ -33,7 +34,7 @@ try {
|
|
|
33
34
|
const source = input.source ?? "startup";
|
|
34
35
|
|
|
35
36
|
if (source === "compact") {
|
|
36
|
-
const { SessionDB } = await
|
|
37
|
+
const { SessionDB } = await loadSessionDB();
|
|
37
38
|
const dbPath = getSessionDBPath(OPTS);
|
|
38
39
|
const db = new SessionDB({ dbPath });
|
|
39
40
|
const sessionId = getSessionId(input, OPTS);
|
|
@@ -53,7 +54,7 @@ try {
|
|
|
53
54
|
} else if (source === "resume") {
|
|
54
55
|
try { unlinkSync(getCleanupFlagPath(OPTS)); } catch { /* no flag */ }
|
|
55
56
|
|
|
56
|
-
const { SessionDB } = await
|
|
57
|
+
const { SessionDB } = await loadSessionDB();
|
|
57
58
|
const dbPath = getSessionDBPath(OPTS);
|
|
58
59
|
const db = new SessionDB({ dbPath });
|
|
59
60
|
|
|
@@ -65,7 +66,7 @@ try {
|
|
|
65
66
|
|
|
66
67
|
db.close();
|
|
67
68
|
} else if (source === "startup") {
|
|
68
|
-
const { SessionDB } = await
|
|
69
|
+
const { SessionDB } = await loadSessionDB();
|
|
69
70
|
const dbPath = getSessionDBPath(OPTS);
|
|
70
71
|
const db = new SessionDB({ dbPath });
|
|
71
72
|
try { unlinkSync(getSessionEventsPath(OPTS)); } catch { /* no stale file */ }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-mode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.22",
|
|
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",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
],
|
|
51
51
|
"scripts": {
|
|
52
52
|
"build": "tsc && chmod +x build/cli.js",
|
|
53
|
-
"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",
|
|
53
|
+
"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",
|
|
54
54
|
"prepublishOnly": "npm run build",
|
|
55
55
|
"dev": "npx tsx src/server.ts",
|
|
56
56
|
"setup": "npx tsx src/cli.ts setup",
|
package/server.bundle.mjs
CHANGED
|
@@ -110,7 +110,8 @@ func main() {
|
|
|
110
110
|
_ = fmt.Sprint()
|
|
111
111
|
${n}
|
|
112
112
|
}
|
|
113
|
-
`;case"rust":return
|
|
113
|
+
`;case"rust":return`#[allow(unused_variables)]
|
|
114
|
+
use std::fs;
|
|
114
115
|
|
|
115
116
|
fn main() {
|
|
116
117
|
let file_content_path = ${o};
|
|
@@ -238,7 +239,7 @@ stdout:
|
|
|
238
239
|
${n}
|
|
239
240
|
|
|
240
241
|
stderr:
|
|
241
|
-
${o}`}}var rO=process.ppid;function nO(){let t=process.ppid;return!(t!==rO||t===0||t===1)}function Hy(t){let e=t.checkIntervalMs??3e4,r=t.isParentAlive??nO,n=!1,o=()=>{n||(n=!0,t.onShutdown())},s=setInterval(()=>{r()||o()},e);s.unref();let i=()=>o();process.stdin.resume(),process.stdin.on("end",i),process.stdin.on("close",i),process.stdin.on("error",i);let a=["SIGTERM","SIGINT"];process.platform!=="win32"&&a.push("SIGHUP");for(let c of a)process.on(c,o);return()=>{n=!0,clearInterval(s),process.stdin.removeListener("end",i),process.stdin.removeListener("close",i),process.stdin.removeListener("error",i);for(let c of a)process.removeListener(c,o)}}var Cv="1.0.
|
|
242
|
+
${o}`}}var rO=process.ppid;function nO(){let t=process.ppid;return!(t!==rO||t===0||t===1)}function Hy(t){let e=t.checkIntervalMs??3e4,r=t.isParentAlive??nO,n=!1,o=()=>{n||(n=!0,t.onShutdown())},s=setInterval(()=>{r()||o()},e);s.unref();let i=()=>o();process.stdin.resume(),process.stdin.on("end",i),process.stdin.on("close",i),process.stdin.on("error",i);let a=["SIGTERM","SIGINT"];process.platform!=="win32"&&a.push("SIGHUP");for(let c of a)process.on(c,o);return()=>{n=!0,clearInterval(s),process.stdin.removeListener("end",i),process.stdin.removeListener("close",i),process.stdin.removeListener("error",i);for(let c of a)process.removeListener(c,o)}}var Cv="1.0.21";process.on("unhandledRejection",t=>{process.stderr.write(`[context-mode] unhandledRejection: ${t}
|
|
242
243
|
`)});process.on("uncaughtException",t=>{process.stderr.write(`[context-mode] uncaughtException: ${t?.message??t}
|
|
243
244
|
`)});var $p=Yi(),DO=ky($p),Mt=new Gi({name:"context-mode",version:Cv}),ss=new Qi({runtimes:$p,projectRoot:process.env.CLAUDE_PROJECT_DIR}),Hn=null;function ZO(t){try{let e=da(Iv(),".claude","context-mode","sessions");if(!Ov(e))return;let r=zO(e).filter(n=>n.endsWith("-events.md"));for(let n of r){let o=da(e,n);try{t.index({path:o,source:"session-events"}),CO(o)}catch{}}}catch{}}function is(){return Hn||(Hn=new ea),ZO(Hn),Hn}var qe={calls:{},bytesReturned:{},bytesIndexed:0,bytesSandboxed:0,sessionStart:Date.now()};function G(t,e){let r=e.content.reduce((n,o)=>n+Buffer.byteLength(o.text),0);return qe.calls[t]=(qe.calls[t]||0)+1,qe.bytesReturned[t]=(qe.bytesReturned[t]||0)+r,e}function yr(t){qe.bytesIndexed+=t}function Tp(t,e){try{let r=Xd(process.env.CLAUDE_PROJECT_DIR),n=Qd(t,r);if(n.decision==="deny")return G(e,{content:[{type:"text",text:`Command blocked by security policy: matches deny pattern ${n.matchedPattern}`}],isError:!0})}catch{}return null}function zv(t,e,r){try{let n=qy(t,e);if(n.length===0)return null;let o=Xd(process.env.CLAUDE_PROJECT_DIR);for(let s of n){let i=Qd(s,o);if(i.decision==="deny")return G(r,{content:[{type:"text",text:`Command blocked by security policy: embedded shell command "${s}" matches deny pattern ${i.matchedPattern}`}],isError:!0})}}catch{}return null}function LO(t,e){try{let r=Ly("Read",process.env.CLAUDE_PROJECT_DIR),n=Uy(t,r);if(n.denied)return G(e,{content:[{type:"text",text:`File access blocked by security policy: path matches Read deny pattern ${n.matchedPattern}`}],isError:!0})}catch{}return null}var UO=DO.join(", "),qO=Gd()?" (Bun detected \u2014 JS/TS runs 3-5x faster)":"",HO="",FO="";function VO(t){let e=[],r=0,n=0;for(;n<t.length;)if(t[n]===HO){for(e.push(r),n++;n<t.length&&t[n]!==FO;)r++,n++;n<t.length&&n++}else r++,n++;return e}function Nv(t,e,r=1500,n){if(t.length<=r)return t;let o=[];if(n)for(let u of VO(n))o.push(u);if(o.length===0){let u=e.toLowerCase().split(/\s+/).filter(d=>d.length>2),l=t.toLowerCase();for(let d of u){let f=l.indexOf(d);for(;f!==-1;)o.push(f),f=l.indexOf(d,f+1)}}if(o.length===0)return t.slice(0,r)+`
|
|
244
245
|
\u2026`;o.sort((u,l)=>u-l);let s=300,i=[];for(let u of o){let l=Math.max(0,u-s),d=Math.min(t.length,u+s);i.length>0&&l<=i[i.length-1][1]?i[i.length-1][1]=d:i.push([l,d])}let a=[],c=0;for(let[u,l]of i){if(c>=r)break;let d=t.slice(u,Math.min(l,u+(r-c)));a.push((u>0?"\u2026":"")+d+(l<t.length?"\u2026":"")),c+=d.length}return a.join(`
|