context-mode 1.0.67 → 1.0.68
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/opencode-plugin.js +1 -1
- package/build/session/extract.d.ts +1 -1
- package/build/session/extract.js +39 -42
- package/build/session/snapshot.d.ts +15 -48
- package/build/session/snapshot.js +267 -208
- package/build/truncate.d.ts +0 -10
- package/build/truncate.js +0 -17
- package/hooks/codex/sessionstart.mjs +5 -13
- package/hooks/cursor/sessionstart.mjs +5 -13
- package/hooks/gemini-cli/sessionstart.mjs +6 -14
- package/hooks/session-directive.mjs +3 -2
- package/hooks/session-extract.bundle.mjs +1 -1
- package/hooks/session-snapshot.bundle.mjs +29 -14
- package/hooks/sessionstart.mjs +6 -18
- package/hooks/vscode-copilot/sessionstart.mjs +6 -14
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
|
@@ -8,7 +8,8 @@ import "../ensure-deps.mjs";
|
|
|
8
8
|
import { createRoutingBlock } from "../routing-block.mjs";
|
|
9
9
|
import { createToolNamer } from "../core/tool-naming.mjs";
|
|
10
10
|
|
|
11
|
-
const
|
|
11
|
+
const toolNamer = createToolNamer("codex");
|
|
12
|
+
const ROUTING_BLOCK = createRoutingBlock(toolNamer);
|
|
12
13
|
import {
|
|
13
14
|
writeSessionEventsFile,
|
|
14
15
|
buildSessionDirective,
|
|
@@ -25,7 +26,7 @@ import {
|
|
|
25
26
|
CODEX_OPTS,
|
|
26
27
|
} from "../session-helpers.mjs";
|
|
27
28
|
import { join } from "node:path";
|
|
28
|
-
import {
|
|
29
|
+
import { unlinkSync } from "node:fs";
|
|
29
30
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
30
31
|
|
|
31
32
|
const HOOK_DIR = fileURLToPath(new URL(".", import.meta.url));
|
|
@@ -60,7 +61,7 @@ try {
|
|
|
60
61
|
: getLatestSessionEvents(db);
|
|
61
62
|
if (events.length > 0) {
|
|
62
63
|
const eventMeta = writeSessionEventsFile(events, getSessionEventsPath(OPTS));
|
|
63
|
-
additionalContext += buildSessionDirective(source, eventMeta);
|
|
64
|
+
additionalContext += buildSessionDirective(source, eventMeta, toolNamer);
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
db.close();
|
|
@@ -70,17 +71,8 @@ try {
|
|
|
70
71
|
const db = new SessionDB({ dbPath });
|
|
71
72
|
try { unlinkSync(getSessionEventsPath(OPTS)); } catch { /* no stale file */ }
|
|
72
73
|
|
|
73
|
-
|
|
74
|
-
let previousWasFresh = false;
|
|
75
|
-
try { readFileSync(cleanupFlag); previousWasFresh = true; } catch { /* no flag */ }
|
|
76
|
-
|
|
77
|
-
if (previousWasFresh) {
|
|
78
|
-
db.cleanupOldSessions(0);
|
|
79
|
-
} else {
|
|
80
|
-
db.cleanupOldSessions(7);
|
|
81
|
-
}
|
|
74
|
+
db.cleanupOldSessions(7);
|
|
82
75
|
db.db.exec(`DELETE FROM session_events WHERE session_id NOT IN (SELECT session_id FROM session_meta)`);
|
|
83
|
-
writeFileSync(cleanupFlag, new Date().toISOString(), "utf-8");
|
|
84
76
|
|
|
85
77
|
const sessionId = getSessionId(input, OPTS);
|
|
86
78
|
db.ensureSession(sessionId, projectDir);
|
|
@@ -8,7 +8,8 @@ import "../ensure-deps.mjs";
|
|
|
8
8
|
import { createRoutingBlock } from "../routing-block.mjs";
|
|
9
9
|
import { createToolNamer } from "../core/tool-naming.mjs";
|
|
10
10
|
|
|
11
|
-
const
|
|
11
|
+
const toolNamer = createToolNamer("cursor");
|
|
12
|
+
const ROUTING_BLOCK = createRoutingBlock(toolNamer);
|
|
12
13
|
import {
|
|
13
14
|
writeSessionEventsFile,
|
|
14
15
|
buildSessionDirective,
|
|
@@ -25,7 +26,7 @@ import {
|
|
|
25
26
|
CURSOR_OPTS,
|
|
26
27
|
} from "../session-helpers.mjs";
|
|
27
28
|
import { join } from "node:path";
|
|
28
|
-
import {
|
|
29
|
+
import { unlinkSync } from "node:fs";
|
|
29
30
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
30
31
|
|
|
31
32
|
const HOOK_DIR = fileURLToPath(new URL(".", import.meta.url));
|
|
@@ -64,7 +65,7 @@ try {
|
|
|
64
65
|
: getLatestSessionEvents(db);
|
|
65
66
|
if (events.length > 0) {
|
|
66
67
|
const eventMeta = writeSessionEventsFile(events, getSessionEventsPath(OPTS));
|
|
67
|
-
additionalContext += buildSessionDirective(source, eventMeta);
|
|
68
|
+
additionalContext += buildSessionDirective(source, eventMeta, toolNamer);
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
db.close();
|
|
@@ -74,17 +75,8 @@ try {
|
|
|
74
75
|
const db = new SessionDB({ dbPath });
|
|
75
76
|
try { unlinkSync(getSessionEventsPath(OPTS)); } catch { /* no stale file */ }
|
|
76
77
|
|
|
77
|
-
|
|
78
|
-
let previousWasFresh = false;
|
|
79
|
-
try { readFileSync(cleanupFlag); previousWasFresh = true; } catch { /* no flag */ }
|
|
80
|
-
|
|
81
|
-
if (previousWasFresh) {
|
|
82
|
-
db.cleanupOldSessions(0);
|
|
83
|
-
} else {
|
|
84
|
-
db.cleanupOldSessions(7);
|
|
85
|
-
}
|
|
78
|
+
db.cleanupOldSessions(7);
|
|
86
79
|
db.db.exec(`DELETE FROM session_events WHERE session_id NOT IN (SELECT session_id FROM session_meta)`);
|
|
87
|
-
writeFileSync(cleanupFlag, new Date().toISOString(), "utf-8");
|
|
88
80
|
|
|
89
81
|
const sessionId = getSessionId(input, OPTS);
|
|
90
82
|
db.ensureSession(sessionId, projectDir);
|
|
@@ -14,14 +14,15 @@ import "../ensure-deps.mjs";
|
|
|
14
14
|
import { createRoutingBlock } from "../routing-block.mjs";
|
|
15
15
|
import { createToolNamer } from "../core/tool-naming.mjs";
|
|
16
16
|
|
|
17
|
-
const
|
|
17
|
+
const toolNamer = createToolNamer("gemini-cli");
|
|
18
|
+
const ROUTING_BLOCK = createRoutingBlock(toolNamer);
|
|
18
19
|
import { writeSessionEventsFile, buildSessionDirective, getSessionEvents, getLatestSessionEvents } from "../session-directive.mjs";
|
|
19
20
|
import {
|
|
20
21
|
readStdin, getSessionId, getSessionDBPath, getSessionEventsPath, getCleanupFlagPath,
|
|
21
22
|
getProjectDir, GEMINI_OPTS,
|
|
22
23
|
} from "../session-helpers.mjs";
|
|
23
24
|
import { join, dirname } from "node:path";
|
|
24
|
-
import { readFileSync,
|
|
25
|
+
import { readFileSync, unlinkSync } from "node:fs";
|
|
25
26
|
import { homedir } from "node:os";
|
|
26
27
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
27
28
|
|
|
@@ -50,7 +51,7 @@ try {
|
|
|
50
51
|
const events = getSessionEvents(db, sessionId);
|
|
51
52
|
if (events.length > 0) {
|
|
52
53
|
const eventMeta = writeSessionEventsFile(events, getSessionEventsPath(OPTS));
|
|
53
|
-
additionalContext += buildSessionDirective("compact", eventMeta);
|
|
54
|
+
additionalContext += buildSessionDirective("compact", eventMeta, toolNamer);
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
db.close();
|
|
@@ -64,7 +65,7 @@ try {
|
|
|
64
65
|
const events = getLatestSessionEvents(db);
|
|
65
66
|
if (events.length > 0) {
|
|
66
67
|
const eventMeta = writeSessionEventsFile(events, getSessionEventsPath(OPTS));
|
|
67
|
-
additionalContext += buildSessionDirective("resume", eventMeta);
|
|
68
|
+
additionalContext += buildSessionDirective("resume", eventMeta, toolNamer);
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
db.close();
|
|
@@ -74,17 +75,8 @@ try {
|
|
|
74
75
|
const db = new SessionDB({ dbPath });
|
|
75
76
|
try { unlinkSync(getSessionEventsPath(OPTS)); } catch { /* no stale file */ }
|
|
76
77
|
|
|
77
|
-
|
|
78
|
-
let previousWasFresh = false;
|
|
79
|
-
try { readFileSync(cleanupFlag); previousWasFresh = true; } catch { /* no flag */ }
|
|
80
|
-
|
|
81
|
-
if (previousWasFresh) {
|
|
82
|
-
db.cleanupOldSessions(0);
|
|
83
|
-
} else {
|
|
84
|
-
db.cleanupOldSessions(7);
|
|
85
|
-
}
|
|
78
|
+
db.cleanupOldSessions(7);
|
|
86
79
|
db.db.exec(`DELETE FROM session_events WHERE session_id NOT IN (SELECT session_id FROM session_meta)`);
|
|
87
|
-
writeFileSync(cleanupFlag, new Date().toISOString(), "utf-8");
|
|
88
80
|
|
|
89
81
|
const sessionId = getSessionId(input, OPTS);
|
|
90
82
|
const projectDir = getProjectDir(OPTS);
|
|
@@ -211,7 +211,7 @@ export function writeSessionEventsFile(events, eventsPath) {
|
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
// ── Build session guide — actionable narrative for LLM to continue from ──
|
|
214
|
-
export function buildSessionDirective(source, eventMeta) {
|
|
214
|
+
export function buildSessionDirective(source, eventMeta, toolNamer) {
|
|
215
215
|
const { grouped, lastPrompt, fileNames } = eventMeta;
|
|
216
216
|
const isCompact = source === "compact";
|
|
217
217
|
|
|
@@ -417,7 +417,8 @@ export function buildSessionDirective(source, eventMeta) {
|
|
|
417
417
|
// Search on demand — detailed data lives in FTS5
|
|
418
418
|
block += `\n<session_search>`;
|
|
419
419
|
block += `\nDetailed session data is indexed in context-mode FTS5 (source: "session-events").`;
|
|
420
|
-
|
|
420
|
+
const searchTool = toolNamer ? toolNamer("ctx_search") : "ctx_search";
|
|
421
|
+
block += `\nUse ${searchTool}(queries: [...], source: "session-events") when you need specifics.`;
|
|
421
422
|
block += `\nDo NOT call ctx_index() — data is already indexed.`;
|
|
422
423
|
block += `\n</session_search>`;
|
|
423
424
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function o(t
|
|
1
|
+
function o(t){return t==null?"":String(t)}function l(t){return t==null?"":typeof t=="string"?t:JSON.stringify(t)}function c(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),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),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)),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: ${l(n.map(i=>typeof i=="object"&&i!==null&&"prompt"in i?String(i.prompt):String(i)).join(", "))})`:"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??""}`),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??"")),n=t.tool_response?o(String(t.tool_response)):"",s=n.length>0;return[{type:s?"subagent_completed":"subagent_launched",category:"subagent",data:o(s?`[completed] ${e} \u2192 ${n}`:`[launched] ${e}`),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))}`:"";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??"")),r=n?`Q: ${o(n)} \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),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),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),priority:4}]}function P(t){try{let e=[];return e.push(...c(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};
|
|
@@ -1,14 +1,29 @@
|
|
|
1
|
-
function
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
`
|
|
7
|
-
`)}function
|
|
8
|
-
`)}function
|
|
9
|
-
`)}function
|
|
10
|
-
`),
|
|
11
|
-
${
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
function a(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}var N=10;function h(t,r=4){return[...new Set(t.filter(o=>o.length>0))].slice(0,r).map(o=>o.length>80?o.slice(0,80):o)}function m(t,r){if(r.length===0)return"";let s=r.map(n=>`"${a(n)}"`).join(", ");return`
|
|
2
|
+
For full details:
|
|
3
|
+
${a(t)}(
|
|
4
|
+
queries: [${s}],
|
|
5
|
+
source: "session-events"
|
|
6
|
+
)`}function x(t,r){if(t.length===0)return"";let s=new Map;for(let l of t){let b=l.data,p=s.get(b);p||(p={ops:new Map},s.set(b,p));let g;l.type==="file_write"?g="write":l.type==="file_read"?g="read":l.type==="file_edit"?g="edit":g=l.type,p.ops.set(g,(p.ops.get(g)??0)+1)}let o=Array.from(s.entries()).slice(-N),u=[],i=[];for(let[l,{ops:b}]of o){let p=Array.from(b.entries()).map(([S,y])=>`${S}\xD7${y}`).join(", "),g=l.split("/").pop()??l;u.push(` ${a(g)} (${a(p)})`),i.push(`${g} ${Array.from(b.keys()).join(" ")}`)}let e=h(i);return[` <files count="${s.size}">`,...u,m(r,e)," </files>"].join(`
|
|
7
|
+
`)}function M(t,r){if(t.length===0)return"";let s=[],n=[];for(let i of t)s.push(` ${a(i.data)}`),n.push(i.data);let o=h(n);return[` <errors count="${t.length}">`,...s,m(r,o)," </errors>"].join(`
|
|
8
|
+
`)}function A(t,r){if(t.length===0)return"";let s=new Set,n=[],o=[];for(let e of t)s.has(e.data)||(s.add(e.data),n.push(` ${a(e.data)}`),o.push(e.data));if(n.length===0)return"";let u=h(o);return[` <decisions count="${n.length}">`,...n,m(r,u)," </decisions>"].join(`
|
|
9
|
+
`)}function D(t,r){if(t.length===0)return"";let s=new Set,n=[],o=[];for(let e of t)s.has(e.data)||(s.add(e.data),e.type==="rule_content"?n.push(` ${a(e.data)}`):n.push(` ${a(e.data)}`),o.push(e.data));if(n.length===0)return"";let u=h(o);return[` <rules count="${n.length}">`,...n,m(r,u)," </rules>"].join(`
|
|
10
|
+
`)}function F(t,r){if(t.length===0)return"";let s=[],n=[];for(let i of t)s.push(` ${a(i.data)}`),n.push(i.data);let o=h(n);return[` <git count="${t.length}">`,...s,m(r,o)," </git>"].join(`
|
|
11
|
+
`)}function R(t){if(t.length===0)return"";let r=[],s={};for(let e of t)try{let c=JSON.parse(e.data);typeof c.subject=="string"?r.push(c.subject):typeof c.taskId=="string"&&typeof c.status=="string"&&(s[c.taskId]=c.status)}catch{}if(r.length===0)return"";let n=new Set(["completed","deleted","failed"]),o=Object.keys(s).sort((e,c)=>Number(e)-Number(c)),u=[];for(let e=0;e<r.length;e++){let c=o[e],l=c?s[c]??"pending":"pending";n.has(l)||u.push(r[e])}if(u.length===0)return"";let i=[];for(let e of u)i.push(` [pending] ${a(e)}`);return i.join(`
|
|
12
|
+
`)}function J(t,r){let s=R(t);if(!s)return"";let n=[];for(let e of t)try{let c=JSON.parse(e.data);typeof c.subject=="string"&&n.push(c.subject)}catch{}let o=h(n);return[` <task_state count="${s.split(`
|
|
13
|
+
`).length}">`,s,m(r,o)," </task_state>"].join(`
|
|
14
|
+
`)}function X(t,r,s){if(t.length===0&&r.length===0)return"";let n=[],o=[];if(t.length>0){let e=t[t.length-1];n.push(` cwd: ${a(e.data)}`),o.push("working directory")}for(let e of r)n.push(` ${a(e.data)}`),o.push(e.data);let u=h(o);return[" <environment>",...n,m(s,u)," </environment>"].join(`
|
|
15
|
+
`)}function z(t,r){if(t.length===0)return"";let s=[],n=[];for(let i of t){let e=i.type==="subagent_completed"?"completed":i.type==="subagent_launched"?"launched":"unknown";s.push(` [${e}] ${a(i.data)}`),n.push(`subagent ${i.data}`)}let o=h(n);return[` <subagents count="${t.length}">`,...s,m(r,o)," </subagents>"].join(`
|
|
16
|
+
`)}function G(t,r){if(t.length===0)return"";let s=new Map;for(let e of t){let c=e.data.split(":")[0].trim();s.set(c,(s.get(c)??0)+1)}let n=[],o=[];for(let[e,c]of s)n.push(` ${a(e)} (${c}\xD7)`),o.push(`skill ${e} invocation`);let u=h(o);return[` <skills count="${t.length}">`,...n,m(r,u)," </skills>"].join(`
|
|
17
|
+
`)}function Q(t){if(t.length===0)return"";let r=t[t.length-1];return` <intent mode="${a(r.data)}"/>`}function H(t,r){let s=r?.compactCount??1,n=r?.searchTool??"ctx_search",o=new Date().toISOString(),u=[],i=[],e=[],c=[],l=[],b=[],p=[],g=[],S=[],y=[],k=[];for(let d of t)switch(d.category){case"file":u.push(d);break;case"task":i.push(d);break;case"rule":e.push(d);break;case"decision":c.push(d);break;case"cwd":l.push(d);break;case"error":b.push(d);break;case"env":p.push(d);break;case"git":g.push(d);break;case"subagent":S.push(d);break;case"intent":y.push(d);break;case"skill":k.push(d);break}let f=[];f.push(` <how_to_search>
|
|
18
|
+
Each section below contains a summary of prior work.
|
|
19
|
+
For FULL DETAILS, run the exact tool call shown under each section.
|
|
20
|
+
Do NOT ask the user to re-explain prior work. Search first.
|
|
21
|
+
Do NOT invent your own queries \u2014 use the ones provided.
|
|
22
|
+
</how_to_search>`);let $=x(u,n);$&&f.push($);let v=M(b,n);v&&f.push(v);let w=A(c,n);w&&f.push(w);let E=D(e,n);E&&f.push(E);let q=F(g,n);q&&f.push(q);let L=J(i,n);L&&f.push(L);let _=X(l,p,n);_&&f.push(_);let j=z(S,n);j&&f.push(j);let T=G(k,n);T&&f.push(T);let O=Q(y);O&&f.push(O);let B=`<session_resume events="${t.length}" compact_count="${s}" generated_at="${o}">`,C="</session_resume>",I=f.join(`
|
|
23
|
+
|
|
24
|
+
`);return I?`${B}
|
|
25
|
+
|
|
26
|
+
${I}
|
|
27
|
+
|
|
28
|
+
${C}`:`${B}
|
|
29
|
+
${C}`}export{H as buildResumeSnapshot,R as renderTaskState};
|
package/hooks/sessionstart.mjs
CHANGED
|
@@ -18,13 +18,14 @@ import "./ensure-deps.mjs";
|
|
|
18
18
|
import { createRoutingBlock } from "./routing-block.mjs";
|
|
19
19
|
import { createToolNamer } from "./core/tool-naming.mjs";
|
|
20
20
|
|
|
21
|
-
const
|
|
21
|
+
const toolNamer = createToolNamer("claude-code");
|
|
22
|
+
const ROUTING_BLOCK = createRoutingBlock(toolNamer);
|
|
22
23
|
import { readStdin, getSessionId, getSessionDBPath, getSessionEventsPath, getCleanupFlagPath } from "./session-helpers.mjs";
|
|
23
24
|
import { writeSessionEventsFile, buildSessionDirective, getSessionEvents, getLatestSessionEvents } from "./session-directive.mjs";
|
|
24
25
|
import { createSessionLoaders } from "./session-loaders.mjs";
|
|
25
26
|
import { join, dirname } from "node:path";
|
|
26
27
|
import { fileURLToPath } from "node:url";
|
|
27
|
-
import { readFileSync,
|
|
28
|
+
import { readFileSync, unlinkSync, readdirSync, rmSync, statSync } from "node:fs";
|
|
28
29
|
import { homedir } from "node:os";
|
|
29
30
|
|
|
30
31
|
// Resolve absolute path for imports (fileURLToPath for Windows compat)
|
|
@@ -53,7 +54,7 @@ try {
|
|
|
53
54
|
const events = getSessionEvents(db, sessionId);
|
|
54
55
|
if (events.length > 0) {
|
|
55
56
|
const eventMeta = writeSessionEventsFile(events, getSessionEventsPath());
|
|
56
|
-
additionalContext += buildSessionDirective("compact", eventMeta);
|
|
57
|
+
additionalContext += buildSessionDirective("compact", eventMeta, toolNamer);
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
db.close();
|
|
@@ -68,7 +69,7 @@ try {
|
|
|
68
69
|
const events = getLatestSessionEvents(db);
|
|
69
70
|
if (events.length > 0) {
|
|
70
71
|
const eventMeta = writeSessionEventsFile(events, getSessionEventsPath());
|
|
71
|
-
additionalContext += buildSessionDirective("resume", eventMeta);
|
|
72
|
+
additionalContext += buildSessionDirective("resume", eventMeta, toolNamer);
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
db.close();
|
|
@@ -82,22 +83,9 @@ try {
|
|
|
82
83
|
// Detect true fresh start vs --continue (which fires startup→resume).
|
|
83
84
|
// If cleanup flag exists from a PREVIOUS startup that was never followed by
|
|
84
85
|
// resume, that was a true fresh start — aggressively wipe all data.
|
|
85
|
-
|
|
86
|
-
let previousWasFresh = false;
|
|
87
|
-
try { readFileSync(cleanupFlag); previousWasFresh = true; } catch { /* no flag */ }
|
|
88
|
-
|
|
89
|
-
if (previousWasFresh) {
|
|
90
|
-
// Previous session was a true fresh start (no --continue) — clean slate
|
|
91
|
-
db.cleanupOldSessions(0);
|
|
92
|
-
} else {
|
|
93
|
-
// First startup or --continue will follow — only clean old sessions
|
|
94
|
-
db.cleanupOldSessions(7);
|
|
95
|
-
}
|
|
86
|
+
db.cleanupOldSessions(7);
|
|
96
87
|
db.db.exec(`DELETE FROM session_events WHERE session_id NOT IN (SELECT session_id FROM session_meta)`);
|
|
97
88
|
|
|
98
|
-
// Write cleanup flag — resume will delete it if --continue follows
|
|
99
|
-
writeFileSync(cleanupFlag, new Date().toISOString(), "utf-8");
|
|
100
|
-
|
|
101
89
|
// Proactively capture CLAUDE.md files — Claude Code loads them as system
|
|
102
90
|
// context at startup, invisible to PostToolUse hooks. We read them from
|
|
103
91
|
// disk so they survive compact/resume via the session events pipeline.
|
|
@@ -15,14 +15,15 @@ import { createSessionLoaders } from "../session-loaders.mjs";
|
|
|
15
15
|
import { createRoutingBlock } from "../routing-block.mjs";
|
|
16
16
|
import { createToolNamer } from "../core/tool-naming.mjs";
|
|
17
17
|
|
|
18
|
-
const
|
|
18
|
+
const toolNamer = createToolNamer("vscode-copilot");
|
|
19
|
+
const ROUTING_BLOCK = createRoutingBlock(toolNamer);
|
|
19
20
|
import { writeSessionEventsFile, buildSessionDirective, getSessionEvents, getLatestSessionEvents } from "../session-directive.mjs";
|
|
20
21
|
import {
|
|
21
22
|
readStdin, getSessionId, getSessionDBPath, getSessionEventsPath, getCleanupFlagPath,
|
|
22
23
|
getProjectDir, VSCODE_OPTS,
|
|
23
24
|
} from "../session-helpers.mjs";
|
|
24
25
|
import { join } from "node:path";
|
|
25
|
-
import { readFileSync,
|
|
26
|
+
import { readFileSync, unlinkSync } from "node:fs";
|
|
26
27
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
27
28
|
import { homedir } from "node:os";
|
|
28
29
|
|
|
@@ -51,7 +52,7 @@ try {
|
|
|
51
52
|
const events = getSessionEvents(db, sessionId);
|
|
52
53
|
if (events.length > 0) {
|
|
53
54
|
const eventMeta = writeSessionEventsFile(events, getSessionEventsPath(OPTS));
|
|
54
|
-
additionalContext += buildSessionDirective("compact", eventMeta);
|
|
55
|
+
additionalContext += buildSessionDirective("compact", eventMeta, toolNamer);
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
db.close();
|
|
@@ -65,7 +66,7 @@ try {
|
|
|
65
66
|
const events = getLatestSessionEvents(db);
|
|
66
67
|
if (events.length > 0) {
|
|
67
68
|
const eventMeta = writeSessionEventsFile(events, getSessionEventsPath(OPTS));
|
|
68
|
-
additionalContext += buildSessionDirective("resume", eventMeta);
|
|
69
|
+
additionalContext += buildSessionDirective("resume", eventMeta, toolNamer);
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
db.close();
|
|
@@ -75,17 +76,8 @@ try {
|
|
|
75
76
|
const db = new SessionDB({ dbPath });
|
|
76
77
|
try { unlinkSync(getSessionEventsPath(OPTS)); } catch { /* no stale file */ }
|
|
77
78
|
|
|
78
|
-
|
|
79
|
-
let previousWasFresh = false;
|
|
80
|
-
try { readFileSync(cleanupFlag); previousWasFresh = true; } catch { /* no flag */ }
|
|
81
|
-
|
|
82
|
-
if (previousWasFresh) {
|
|
83
|
-
db.cleanupOldSessions(0);
|
|
84
|
-
} else {
|
|
85
|
-
db.cleanupOldSessions(7);
|
|
86
|
-
}
|
|
79
|
+
db.cleanupOldSessions(7);
|
|
87
80
|
db.db.exec(`DELETE FROM session_events WHERE session_id NOT IN (SELECT session_id FROM session_meta)`);
|
|
88
|
-
writeFileSync(cleanupFlag, new Date().toISOString(), "utf-8");
|
|
89
81
|
|
|
90
82
|
const sessionId = getSessionId(input, OPTS);
|
|
91
83
|
const projectDir = getProjectDir(OPTS);
|
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.68",
|
|
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.68",
|
|
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",
|