context-mode 1.0.98 → 1.0.100
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/README.md +9 -7
- package/build/adapters/claude-code-base.js +4 -4
- package/build/adapters/codex/index.js +23 -1
- package/build/adapters/qwen-code/index.d.ts +1 -1
- package/build/adapters/qwen-code/index.js +110 -4
- package/build/cli.js +2 -0
- package/build/opencode-plugin.js +1 -1
- package/build/pi-extension.js +1 -1
- package/build/search/auto-memory.d.ts +29 -0
- package/build/search/auto-memory.js +121 -0
- package/build/search/unified.d.ts +41 -0
- package/build/search/unified.js +89 -0
- package/build/server.js +88 -40
- package/build/session/analytics.js +1 -1
- package/build/session/db.d.ts +17 -0
- package/build/session/db.js +28 -0
- package/build/session/extract.d.ts +4 -0
- package/build/session/extract.js +232 -1
- package/build/session/snapshot.js +31 -0
- package/build/store.js +118 -8
- package/build/types.d.ts +1 -0
- package/cli.bundle.mjs +260 -125
- package/configs/claude-code/CLAUDE.md +21 -1
- package/configs/codex/AGENTS.md +23 -2
- package/configs/codex/hooks.json +14 -0
- package/configs/cursor/context-mode.mdc +18 -1
- package/configs/gemini-cli/GEMINI.md +22 -1
- package/configs/jetbrains-copilot/copilot-instructions.md +22 -1
- package/configs/kilo/AGENTS.md +19 -2
- package/configs/kiro/KIRO.md +18 -1
- package/configs/openclaw/AGENTS.md +22 -2
- package/configs/opencode/AGENTS.md +18 -1
- package/configs/pi/AGENTS.md +18 -1
- package/configs/qwen-code/QWEN.md +38 -18
- package/configs/vscode-copilot/copilot-instructions.md +22 -1
- package/hooks/auto-injection.mjs +76 -0
- package/hooks/codex/stop.mjs +43 -0
- package/hooks/codex/userpromptsubmit.mjs +75 -0
- package/hooks/core/mcp-ready.mjs +7 -1
- package/hooks/posttooluse.mjs +50 -1
- package/hooks/precompact.mjs +9 -0
- package/hooks/pretooluse.mjs +27 -0
- package/hooks/routing-block.mjs +7 -1
- package/hooks/session-db.bundle.mjs +19 -13
- package/hooks/session-extract.bundle.mjs +2 -2
- package/hooks/session-snapshot.bundle.mjs +18 -17
- package/hooks/sessionstart.mjs +17 -0
- package/hooks/userpromptsubmit.mjs +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server.bundle.mjs +228 -93
- package/skills/context-mode-ops/agent-teams.md +1 -1
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "../suppress-stderr.mjs";
|
|
3
|
+
import "../ensure-deps.mjs";
|
|
4
|
+
/**
|
|
5
|
+
* Codex CLI UserPromptSubmit hook — capture user prompts for continuity.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readStdin, parseStdin, getSessionId, getSessionDBPath, getInputProjectDir, CODEX_OPTS } from "../session-helpers.mjs";
|
|
9
|
+
import { createSessionLoaders, attributeAndInsertEvents } from "../session-loaders.mjs";
|
|
10
|
+
import { dirname } from "node:path";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
|
|
13
|
+
const HOOK_DIR = dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const { loadSessionDB, loadExtract, loadProjectAttribution } = createSessionLoaders(HOOK_DIR);
|
|
15
|
+
const OPTS = CODEX_OPTS;
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
const raw = await readStdin();
|
|
19
|
+
const input = parseStdin(raw);
|
|
20
|
+
const projectDir = getInputProjectDir(input, OPTS);
|
|
21
|
+
|
|
22
|
+
const prompt = input.prompt ?? input.message ?? "";
|
|
23
|
+
const trimmed = (prompt || "").trim();
|
|
24
|
+
|
|
25
|
+
const isSystemMessage = trimmed.startsWith("<task-notification>")
|
|
26
|
+
|| trimmed.startsWith("<system-reminder>")
|
|
27
|
+
|| trimmed.startsWith("<context_guidance>")
|
|
28
|
+
|| trimmed.startsWith("<tool-result>");
|
|
29
|
+
|
|
30
|
+
if (trimmed.length > 0 && !isSystemMessage) {
|
|
31
|
+
const { SessionDB } = await loadSessionDB();
|
|
32
|
+
const { extractUserEvents } = await loadExtract();
|
|
33
|
+
const { resolveProjectAttributions } = await loadProjectAttribution();
|
|
34
|
+
const dbPath = getSessionDBPath(OPTS);
|
|
35
|
+
const db = new SessionDB({ dbPath });
|
|
36
|
+
const sessionId = getSessionId(input, OPTS);
|
|
37
|
+
|
|
38
|
+
db.ensureSession(sessionId, projectDir);
|
|
39
|
+
|
|
40
|
+
const promptEvent = {
|
|
41
|
+
type: "user_prompt",
|
|
42
|
+
category: "user-prompt",
|
|
43
|
+
data: prompt,
|
|
44
|
+
priority: 1,
|
|
45
|
+
};
|
|
46
|
+
const promptAttributions = attributeAndInsertEvents(
|
|
47
|
+
db, sessionId, [promptEvent], input, projectDir, "UserPromptSubmit", resolveProjectAttributions,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const userEvents = extractUserEvents(trimmed);
|
|
51
|
+
const savedLastKnown = promptAttributions[0]?.projectDir || null;
|
|
52
|
+
const sessionStats = db.getSessionStats(sessionId);
|
|
53
|
+
const lastKnownProjectDir = typeof db.getLatestAttributedProjectDir === "function"
|
|
54
|
+
? db.getLatestAttributedProjectDir(sessionId)
|
|
55
|
+
: null;
|
|
56
|
+
const userAttributions = resolveProjectAttributions(userEvents, {
|
|
57
|
+
sessionOriginDir: sessionStats?.project_dir || projectDir,
|
|
58
|
+
inputProjectDir: projectDir,
|
|
59
|
+
workspaceRoots: Array.isArray(input.workspace_roots) ? input.workspace_roots : [],
|
|
60
|
+
lastKnownProjectDir: savedLastKnown || lastKnownProjectDir,
|
|
61
|
+
});
|
|
62
|
+
for (let i = 0; i < userEvents.length; i++) {
|
|
63
|
+
db.insertEvent(sessionId, userEvents[i], "UserPromptSubmit", userAttributions[i]);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
db.close();
|
|
67
|
+
}
|
|
68
|
+
} catch {
|
|
69
|
+
// Codex hooks must not block the session.
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
process.stdout.write(JSON.stringify({
|
|
73
|
+
hookSpecificOutput: { hookEventName: "UserPromptSubmit", additionalContext: "" },
|
|
74
|
+
}) + "\n");
|
|
75
|
+
|
package/hooks/core/mcp-ready.mjs
CHANGED
|
@@ -17,8 +17,14 @@ import { join } from "node:path";
|
|
|
17
17
|
|
|
18
18
|
const SENTINEL_PREFIX = "context-mode-mcp-ready-";
|
|
19
19
|
|
|
20
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Resolve the temp root — hardcoded /tmp on Unix to avoid TMPDIR mismatch.
|
|
22
|
+
* Tests may override via CONTEXT_MODE_MCP_SENTINEL_DIR to isolate scan from
|
|
23
|
+
* leftover sentinels in the real /tmp.
|
|
24
|
+
*/
|
|
21
25
|
export function sentinelDir() {
|
|
26
|
+
const override = process.env.CONTEXT_MODE_MCP_SENTINEL_DIR;
|
|
27
|
+
if (override && override.length > 0) return override;
|
|
22
28
|
return process.platform === "win32" ? tmpdir() : "/tmp";
|
|
23
29
|
}
|
|
24
30
|
|
package/hooks/posttooluse.mjs
CHANGED
|
@@ -12,8 +12,10 @@ import "./ensure-deps.mjs";
|
|
|
12
12
|
|
|
13
13
|
import { readStdin, parseStdin, getSessionId, getSessionDBPath, getInputProjectDir } from "./session-helpers.mjs";
|
|
14
14
|
import { createSessionLoaders, attributeAndInsertEvents } from "./session-loaders.mjs";
|
|
15
|
-
import { dirname } from "node:path";
|
|
15
|
+
import { dirname, resolve } from "node:path";
|
|
16
16
|
import { fileURLToPath } from "node:url";
|
|
17
|
+
import { readFileSync, unlinkSync } from "node:fs";
|
|
18
|
+
import { tmpdir } from "node:os";
|
|
17
19
|
|
|
18
20
|
// Resolve absolute path for imports — relative dynamic imports can fail
|
|
19
21
|
// when Claude Code invokes hooks from a different working directory.
|
|
@@ -48,6 +50,53 @@ try {
|
|
|
48
50
|
|
|
49
51
|
attributeAndInsertEvents(db, sessionId, events, input, projectDir, "PostToolUse", resolveProjectAttributions);
|
|
50
52
|
|
|
53
|
+
// ─── Category 18: Rejected-approach — read PreToolUse marker ───
|
|
54
|
+
try {
|
|
55
|
+
const rejectedPath = resolve(tmpdir(), `context-mode-rejected-${sessionId}.txt`);
|
|
56
|
+
let rejectedData;
|
|
57
|
+
try {
|
|
58
|
+
rejectedData = readFileSync(rejectedPath, "utf-8").trim();
|
|
59
|
+
unlinkSync(rejectedPath);
|
|
60
|
+
} catch { /* no marker */ }
|
|
61
|
+
if (rejectedData) {
|
|
62
|
+
const colonIdx = rejectedData.indexOf(":");
|
|
63
|
+
const rejTool = colonIdx > 0 ? rejectedData.slice(0, colonIdx) : rejectedData;
|
|
64
|
+
const rejReason = colonIdx > 0 ? rejectedData.slice(colonIdx + 1) : "denied";
|
|
65
|
+
db.insertEvent(sessionId, {
|
|
66
|
+
type: "rejected",
|
|
67
|
+
category: "rejected-approach",
|
|
68
|
+
data: `${rejTool}: ${rejReason}`,
|
|
69
|
+
priority: 2,
|
|
70
|
+
}, "PreToolUse");
|
|
71
|
+
}
|
|
72
|
+
} catch { /* best-effort */ }
|
|
73
|
+
|
|
74
|
+
// ─── Category 27: Latency — read cross-hook marker and emit event if slow ───
|
|
75
|
+
try {
|
|
76
|
+
const toolName = input.tool_name ?? "";
|
|
77
|
+
if (toolName) {
|
|
78
|
+
const markerPath = resolve(tmpdir(), `context-mode-latency-${sessionId}-${toolName}.txt`);
|
|
79
|
+
let startTime;
|
|
80
|
+
try {
|
|
81
|
+
startTime = parseInt(readFileSync(markerPath, "utf-8").trim(), 10);
|
|
82
|
+
unlinkSync(markerPath);
|
|
83
|
+
} catch {
|
|
84
|
+
// No marker — pretooluse didn't write one or already consumed
|
|
85
|
+
}
|
|
86
|
+
if (startTime && !isNaN(startTime)) {
|
|
87
|
+
const duration = Date.now() - startTime;
|
|
88
|
+
if (duration > 5000) {
|
|
89
|
+
db.insertEvent(sessionId, {
|
|
90
|
+
type: "tool_latency",
|
|
91
|
+
category: "latency",
|
|
92
|
+
data: `${toolName}: ${duration}ms`,
|
|
93
|
+
priority: 3,
|
|
94
|
+
}, "PostToolUse");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
} catch { /* latency tracking is best-effort */ }
|
|
99
|
+
|
|
51
100
|
db.close();
|
|
52
101
|
} catch {
|
|
53
102
|
// PostToolUse must never block the session — silent fallback
|
package/hooks/precompact.mjs
CHANGED
|
@@ -42,6 +42,15 @@ try {
|
|
|
42
42
|
|
|
43
43
|
db.upsertResume(sessionId, snapshot, events.length);
|
|
44
44
|
db.incrementCompactCount(sessionId);
|
|
45
|
+
|
|
46
|
+
// Write compaction category event for analytics
|
|
47
|
+
const fileEvents = events.filter(e => e.category === "file");
|
|
48
|
+
db.insertEvent(sessionId, {
|
|
49
|
+
type: "compaction_summary",
|
|
50
|
+
category: "compaction",
|
|
51
|
+
data: `Session compacted. ${events.length} events, ${fileEvents.length} files touched.`,
|
|
52
|
+
priority: 1,
|
|
53
|
+
}, "PreCompact");
|
|
45
54
|
}
|
|
46
55
|
|
|
47
56
|
db.close();
|
package/hooks/pretooluse.mjs
CHANGED
|
@@ -166,6 +166,33 @@ const toolInput = input.tool_input ?? {};
|
|
|
166
166
|
// ─── Route and format response ───
|
|
167
167
|
const decision = routePreToolUse(tool, toolInput, process.env.CLAUDE_PROJECT_DIR, "claude-code", getSessionId(input));
|
|
168
168
|
const response = formatDecision("claude-code", decision);
|
|
169
|
+
|
|
170
|
+
// ─── Write latency marker for cross-hook timing (Category 27) ───
|
|
171
|
+
// Marker writes MUST happen before stdout write — stdout is the last action
|
|
172
|
+
// so the process can exit immediately after, avoiding CI test timeouts.
|
|
173
|
+
try {
|
|
174
|
+
const sessionId = getSessionId(input);
|
|
175
|
+
if (tool) {
|
|
176
|
+
const markerPath = resolve(tmpdir(), `context-mode-latency-${sessionId}-${tool}.txt`);
|
|
177
|
+
writeFileSync(markerPath, String(Date.now()), "utf-8");
|
|
178
|
+
}
|
|
179
|
+
} catch { /* latency tracking is best-effort — never block hook */ }
|
|
180
|
+
|
|
181
|
+
// ─── Write rejected-approach marker for PostToolUse to pick up ───
|
|
182
|
+
// PreToolUse cannot safely load SessionDB (native module loading breaks hook stdout).
|
|
183
|
+
// Write a marker file instead; PostToolUse reads it and writes the event.
|
|
184
|
+
if (decision && (decision.action === "deny" || decision.action === "modify")) {
|
|
185
|
+
try {
|
|
186
|
+
const sessionId = getSessionId(input);
|
|
187
|
+
const reason = decision.action === "deny"
|
|
188
|
+
? (decision.reason || "denied")
|
|
189
|
+
: "Redirected to context-mode sandbox";
|
|
190
|
+
const markerPath = resolve(tmpdir(), `context-mode-rejected-${sessionId}.txt`);
|
|
191
|
+
writeFileSync(markerPath, `${tool}:${reason}`, "utf-8");
|
|
192
|
+
} catch { /* best-effort — never block hook */ }
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ─── stdout write is the LAST action — process exits immediately after ───
|
|
169
196
|
if (response !== null) {
|
|
170
197
|
process.stdout.write(JSON.stringify(response) + "\n");
|
|
171
198
|
}
|
package/hooks/routing-block.mjs
CHANGED
|
@@ -22,12 +22,14 @@ export function createRoutingBlock(t, options = {}) {
|
|
|
22
22
|
</priority_instructions>
|
|
23
23
|
|
|
24
24
|
<tool_selection_hierarchy>
|
|
25
|
+
0. MEMORY: ${t("ctx_search")}(sort: "timeline")
|
|
26
|
+
- After resume, check prior context before asking user.
|
|
25
27
|
1. GATHER: ${t("ctx_batch_execute")}(commands, queries)
|
|
26
28
|
- Primary research tool. Runs commands, auto-indexes, searches. ONE call replaces many steps.
|
|
27
29
|
- Each command: {label: "section header", command: "shell command"}
|
|
28
30
|
- label becomes FTS5 chunk title — descriptive labels improve search.
|
|
29
31
|
2. FOLLOW-UP: ${t("ctx_search")}(queries: ["q1", "q2", ...])
|
|
30
|
-
- All follow-up questions. ONE call, many queries.
|
|
32
|
+
- All follow-up questions. ONE call, many queries (default relevance mode).
|
|
31
33
|
3. PROCESSING: ${t("ctx_execute")}(language, code) | ${t("ctx_execute_file")}(path, language, code)
|
|
32
34
|
- API calls, log analysis, data processing.
|
|
33
35
|
</tool_selection_hierarchy>
|
|
@@ -65,6 +67,10 @@ export function createRoutingBlock(t, options = {}) {
|
|
|
65
67
|
- Key findings
|
|
66
68
|
</response_format>
|
|
67
69
|
</output_constraints>
|
|
70
|
+
<session_continuity>
|
|
71
|
+
Skills, roles, and decisions set during this session remain active until the user revokes them.
|
|
72
|
+
Do not drop behavioral directives as context grows.
|
|
73
|
+
</session_continuity>
|
|
68
74
|
${includeCommands ? `
|
|
69
75
|
<ctx_commands>
|
|
70
76
|
"ctx stats" | "ctx-stats" | "/ctx-stats" | context savings question
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{createRequire as b}from"node:module";import{existsSync as N,unlinkSync as y,renameSync as D}from"node:fs";import{tmpdir as O}from"node:os";import{join as
|
|
1
|
+
import{createRequire as b}from"node:module";import{existsSync as N,unlinkSync as y,renameSync as D}from"node:fs";import{tmpdir as O}from"node:os";import{join as A}from"node:path";var p=class{#t;constructor(t){this.#t=t}pragma(t){let e=this.#t.prepare(`PRAGMA ${t}`).all();if(!e||e.length===0)return;if(e.length>1)return e;let n=Object.values(e[0]);return n.length===1?n[0]:e[0]}exec(t){let s="",e=null;for(let o=0;o<t.length;o++){let a=t[o];if(e)s+=a,a===e&&(e=null);else if(a==="'"||a==='"')s+=a,e=a;else if(a===";"){let c=s.trim();c&&this.#t.prepare(c).run(),s=""}else s+=a}let n=s.trim();return n&&this.#t.prepare(n).run(),this}prepare(t){let s=this.#t.prepare(t);return{run:(...e)=>s.run(...e),get:(...e)=>{let n=s.get(...e);return n===null?void 0:n},all:(...e)=>s.all(...e),iterate:(...e)=>s.iterate(...e)}}transaction(t){return this.#t.transaction(t)}close(){this.#t.close()}},_=class{#t;constructor(t){this.#t=t}pragma(t){let e=this.#t.prepare(`PRAGMA ${t}`).all();if(!e||e.length===0)return;if(e.length>1)return e;let n=Object.values(e[0]);return n.length===1?n[0]:e[0]}exec(t){return this.#t.exec(t),this}prepare(t){let s=this.#t.prepare(t);return{run:(...e)=>s.run(...e),get:(...e)=>s.get(...e),all:(...e)=>s.all(...e),iterate:(...e)=>typeof s.iterate=="function"?s.iterate(...e):s.all(...e)[Symbol.iterator]()}}transaction(t){return(...s)=>{this.#t.exec("BEGIN");try{let e=t(...s);return this.#t.exec("COMMIT"),e}catch(e){throw this.#t.exec("ROLLBACK"),e}}}close(){this.#t.close()}},u=null;function w(){if(!u){let i=b(import.meta.url);if(globalThis.Bun){let t=i(["bun","sqlite"].join(":")).Database;u=function(e,n){let o=new t(e,{readonly:n?.readonly,create:!0}),a=new p(o);return n?.timeout&&a.pragma(`busy_timeout = ${n.timeout}`),a}}else if(process.platform==="linux")try{let{DatabaseSync:t}=i(["node","sqlite"].join(":"));u=function(e,n){let o=new t(e,{readOnly:n?.readonly??!1});return new _(o)}}catch{u=i("better-sqlite3")}else u=i("better-sqlite3")}return u}function T(i){i.pragma("journal_mode = WAL"),i.pragma("synchronous = NORMAL");try{i.pragma("mmap_size = 268435456")}catch{}}function h(i){if(!N(i))for(let t of["-wal","-shm"])try{y(i+t)}catch{}}function C(i){for(let t of["","-wal","-shm"])try{y(i+t)}catch{}}function l(i){try{i.pragma("wal_checkpoint(TRUNCATE)")}catch{}try{i.close()}catch{}}function f(i="context-mode"){return A(O(),`${i}-${process.pid}.db`)}function I(i,t=[100,500,2e3]){let s;for(let e=0;e<=t.length;e++)try{return i()}catch(n){let o=n instanceof Error?n.message:String(n);if(!o.includes("SQLITE_BUSY")&&!o.includes("database is locked"))throw n;if(s=n instanceof Error?n:new Error(o),e<t.length){let a=t[e],c=Date.now();for(;Date.now()-c<a;);}}throw new Error(`SQLITE_BUSY: database is locked after ${t.length} retries. Original error: ${s?.message}`)}function U(i){return i.includes("SQLITE_CORRUPT")||i.includes("SQLITE_NOTADB")||i.includes("database disk image is malformed")||i.includes("file is not a database")}function M(i){let t=Date.now();for(let s of["","-wal","-shm"])try{D(i+s,`${i}${s}.corrupt-${t}`)}catch{}}var d=Symbol.for("__context_mode_live_dbs__"),m=(()=>{let i=globalThis;return i[d]||(i[d]=new Set,process.on("exit",()=>{for(let t of i[d])l(t);i[d].clear()})),i[d]})(),E=class{#t;#e;constructor(t){let s=w();this.#t=t,h(t);let e;try{e=new s(t,{timeout:3e4}),T(e)}catch(n){let o=n instanceof Error?n.message:String(n);if(U(o)){M(t),h(t);try{e=new s(t,{timeout:3e4}),T(e)}catch(a){throw new Error(`Failed to create fresh DB after renaming corrupt file: ${a instanceof Error?a.message:String(a)}`)}}else throw n}this.#e=e,m.add(this.#e),this.initSchema(),this.prepareStatements()}get db(){return this.#e}get dbPath(){return this.#t}close(){m.delete(this.#e),l(this.#e)}withRetry(t){return I(t)}cleanup(){m.delete(this.#e),l(this.#e),C(this.#t)}};import{createHash as R}from"node:crypto";import{execFileSync as x}from"node:child_process";function K(){let i=process.env.CONTEXT_MODE_SESSION_SUFFIX;if(i!==void 0)return i?`__${i}`:"";try{let t=process.cwd(),s=x("git",["worktree","list","--porcelain"],{encoding:"utf-8",timeout:2e3,stdio:["ignore","pipe","ignore"]}).split(/\r?\n/).find(e=>e.startsWith("worktree "))?.replace("worktree ","")?.trim();if(s&&t!==s)return`__${R("sha256").update(t).digest("hex").slice(0,8)}`}catch{}return""}var k=1e3,F=5,r={insertEvent:"insertEvent",getEvents:"getEvents",getEventsByType:"getEventsByType",getEventsByPriority:"getEventsByPriority",getEventsByTypeAndPriority:"getEventsByTypeAndPriority",getEventCount:"getEventCount",getLatestAttributedProject:"getLatestAttributedProject",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",searchEvents:"searchEvents"},L=class extends E{constructor(t){super(t?.dbPath??f("session"))}stmt(t){return this.stmts.get(t)}initSchema(){try{let s=this.db.pragma("table_xinfo(session_events)").find(e=>e.name==="data_hash");s&&s.hidden!==0&&this.db.exec("DROP TABLE session_events")}catch{}this.db.exec(`
|
|
2
2
|
CREATE TABLE IF NOT EXISTS session_events (
|
|
3
3
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
4
4
|
session_id TEXT NOT NULL,
|
|
@@ -35,42 +35,48 @@ import{createRequire as b}from"node:module";import{existsSync as N,unlinkSync as
|
|
|
35
35
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
36
36
|
consumed INTEGER NOT NULL DEFAULT 0
|
|
37
37
|
);
|
|
38
|
-
`);try{let t=this.db.pragma("table_xinfo(session_events)"),s=new Set(t.map(e=>e.name));s.has("project_dir")||this.db.exec("ALTER TABLE session_events ADD COLUMN project_dir TEXT NOT NULL DEFAULT ''"),s.has("attribution_source")||this.db.exec("ALTER TABLE session_events ADD COLUMN attribution_source TEXT NOT NULL DEFAULT 'unknown'"),s.has("attribution_confidence")||this.db.exec("ALTER TABLE session_events ADD COLUMN attribution_confidence REAL NOT NULL DEFAULT 0"),this.db.exec("CREATE INDEX IF NOT EXISTS idx_session_events_project ON session_events(session_id, project_dir)")}catch{}}prepareStatements(){this.stmts=new Map;let t=(s,e)=>{this.stmts.set(s,this.db.prepare(e))};t(
|
|
38
|
+
`);try{let t=this.db.pragma("table_xinfo(session_events)"),s=new Set(t.map(e=>e.name));s.has("project_dir")||this.db.exec("ALTER TABLE session_events ADD COLUMN project_dir TEXT NOT NULL DEFAULT ''"),s.has("attribution_source")||this.db.exec("ALTER TABLE session_events ADD COLUMN attribution_source TEXT NOT NULL DEFAULT 'unknown'"),s.has("attribution_confidence")||this.db.exec("ALTER TABLE session_events ADD COLUMN attribution_confidence REAL NOT NULL DEFAULT 0"),this.db.exec("CREATE INDEX IF NOT EXISTS idx_session_events_project ON session_events(session_id, project_dir)")}catch{}}prepareStatements(){this.stmts=new Map;let t=(s,e)=>{this.stmts.set(s,this.db.prepare(e))};t(r.insertEvent,`INSERT INTO session_events (
|
|
39
39
|
session_id, type, category, priority, data,
|
|
40
40
|
project_dir, attribution_source, attribution_confidence,
|
|
41
41
|
source_hook, data_hash
|
|
42
42
|
)
|
|
43
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),t(
|
|
43
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),t(r.getEvents,`SELECT id, session_id, type, category, priority, data,
|
|
44
44
|
project_dir, attribution_source, attribution_confidence,
|
|
45
45
|
source_hook, created_at, data_hash
|
|
46
|
-
FROM session_events WHERE session_id = ? ORDER BY id ASC LIMIT ?`),t(
|
|
46
|
+
FROM session_events WHERE session_id = ? ORDER BY id ASC LIMIT ?`),t(r.getEventsByType,`SELECT id, session_id, type, category, priority, data,
|
|
47
47
|
project_dir, attribution_source, attribution_confidence,
|
|
48
48
|
source_hook, created_at, data_hash
|
|
49
|
-
FROM session_events WHERE session_id = ? AND type = ? ORDER BY id ASC LIMIT ?`),t(
|
|
49
|
+
FROM session_events WHERE session_id = ? AND type = ? ORDER BY id ASC LIMIT ?`),t(r.getEventsByPriority,`SELECT id, session_id, type, category, priority, data,
|
|
50
50
|
project_dir, attribution_source, attribution_confidence,
|
|
51
51
|
source_hook, created_at, data_hash
|
|
52
|
-
FROM session_events WHERE session_id = ? AND priority >= ? ORDER BY id ASC LIMIT ?`),t(
|
|
52
|
+
FROM session_events WHERE session_id = ? AND priority >= ? ORDER BY id ASC LIMIT ?`),t(r.getEventsByTypeAndPriority,`SELECT id, session_id, type, category, priority, data,
|
|
53
53
|
project_dir, attribution_source, attribution_confidence,
|
|
54
54
|
source_hook, created_at, data_hash
|
|
55
|
-
FROM session_events WHERE session_id = ? AND type = ? AND priority >= ? ORDER BY id ASC LIMIT ?`),t(
|
|
55
|
+
FROM session_events WHERE session_id = ? AND type = ? AND priority >= ? ORDER BY id ASC LIMIT ?`),t(r.getEventCount,"SELECT COUNT(*) AS cnt FROM session_events WHERE session_id = ?"),t(r.getLatestAttributedProject,`SELECT project_dir
|
|
56
56
|
FROM session_events
|
|
57
57
|
WHERE session_id = ? AND project_dir != ''
|
|
58
58
|
ORDER BY id DESC
|
|
59
|
-
LIMIT 1`),t(
|
|
59
|
+
LIMIT 1`),t(r.checkDuplicate,`SELECT 1 FROM (
|
|
60
60
|
SELECT type, data_hash FROM session_events
|
|
61
61
|
WHERE session_id = ? ORDER BY id DESC LIMIT ?
|
|
62
62
|
) AS recent
|
|
63
63
|
WHERE recent.type = ? AND recent.data_hash = ?
|
|
64
|
-
LIMIT 1`),t(
|
|
64
|
+
LIMIT 1`),t(r.evictLowestPriority,`DELETE FROM session_events WHERE id = (
|
|
65
65
|
SELECT id FROM session_events WHERE session_id = ?
|
|
66
66
|
ORDER BY priority ASC, id ASC LIMIT 1
|
|
67
|
-
)`),t(
|
|
67
|
+
)`),t(r.updateMetaLastEvent,`UPDATE session_meta
|
|
68
68
|
SET last_event_at = datetime('now'), event_count = event_count + 1
|
|
69
|
-
WHERE session_id = ?`),t(
|
|
70
|
-
FROM session_meta WHERE session_id = ?`),t(
|
|
69
|
+
WHERE session_id = ?`),t(r.ensureSession,"INSERT OR IGNORE INTO session_meta (session_id, project_dir) VALUES (?, ?)"),t(r.getSessionStats,`SELECT session_id, project_dir, started_at, last_event_at, event_count, compact_count
|
|
70
|
+
FROM session_meta WHERE session_id = ?`),t(r.incrementCompactCount,"UPDATE session_meta SET compact_count = compact_count + 1 WHERE session_id = ?"),t(r.upsertResume,`INSERT INTO session_resume (session_id, snapshot, event_count)
|
|
71
71
|
VALUES (?, ?, ?)
|
|
72
72
|
ON CONFLICT(session_id) DO UPDATE SET
|
|
73
73
|
snapshot = excluded.snapshot,
|
|
74
74
|
event_count = excluded.event_count,
|
|
75
75
|
created_at = datetime('now'),
|
|
76
|
-
consumed = 0`),t(
|
|
76
|
+
consumed = 0`),t(r.getResume,"SELECT snapshot, event_count, consumed FROM session_resume WHERE session_id = ?"),t(r.markResumeConsumed,"UPDATE session_resume SET consumed = 1 WHERE session_id = ?"),t(r.deleteEvents,"DELETE FROM session_events WHERE session_id = ?"),t(r.deleteMeta,"DELETE FROM session_meta WHERE session_id = ?"),t(r.deleteResume,"DELETE FROM session_resume WHERE session_id = ?"),t(r.searchEvents,`SELECT id, session_id, category, type, data, created_at
|
|
77
|
+
FROM session_events
|
|
78
|
+
WHERE project_dir = ?
|
|
79
|
+
AND (data LIKE '%' || ? || '%' ESCAPE '\\' OR category LIKE '%' || ? || '%' ESCAPE '\\')
|
|
80
|
+
AND (? IS NULL OR category = ?)
|
|
81
|
+
ORDER BY id ASC
|
|
82
|
+
LIMIT ?`),t(r.getOldSessions,"SELECT session_id FROM session_meta WHERE started_at < datetime('now', ? || ' days')")}insertEvent(t,s,e="PostToolUse",n){let o=R("sha256").update(s.data).digest("hex").slice(0,16).toUpperCase(),a=String(n?.projectDir??s.project_dir??"").trim(),c=String(n?.source??s.attribution_source??"unknown"),g=Number(n?.confidence??s.attribution_confidence??0),S=Number.isFinite(g)?Math.max(0,Math.min(1,g)):0,v=this.db.transaction(()=>{if(this.stmt(r.checkDuplicate).get(t,F,s.type,o))return;this.stmt(r.getEventCount).get(t).cnt>=k&&this.stmt(r.evictLowestPriority).run(t),this.stmt(r.insertEvent).run(t,s.type,s.category,s.priority,s.data,a,c,S,e,o),this.stmt(r.updateMetaLastEvent).run(t)});this.withRetry(()=>v())}getEvents(t,s){let e=s?.limit??1e3,n=s?.type,o=s?.minPriority;return n&&o!==void 0?this.stmt(r.getEventsByTypeAndPriority).all(t,n,o,e):n?this.stmt(r.getEventsByType).all(t,n,e):o!==void 0?this.stmt(r.getEventsByPriority).all(t,o,e):this.stmt(r.getEvents).all(t,e)}getEventCount(t){return this.stmt(r.getEventCount).get(t).cnt}getLatestAttributedProjectDir(t){return this.stmt(r.getLatestAttributedProject).get(t)?.project_dir||null}searchEvents(t,s,e,n){try{let o=t.replace(/[%_]/g,c=>"\\"+c),a=n??null;return this.stmt(r.searchEvents).all(e,o,o,a,a,s)}catch{return[]}}ensureSession(t,s){this.stmt(r.ensureSession).run(t,s)}getSessionStats(t){return this.stmt(r.getSessionStats).get(t)??null}incrementCompactCount(t){this.stmt(r.incrementCompactCount).run(t)}upsertResume(t,s,e){this.stmt(r.upsertResume).run(t,s,e??0)}getResume(t){return this.stmt(r.getResume).get(t)??null}markResumeConsumed(t){this.stmt(r.markResumeConsumed).run(t)}deleteSession(t){this.db.transaction(()=>{this.stmt(r.deleteEvents).run(t),this.stmt(r.deleteResume).run(t),this.stmt(r.deleteMeta).run(t)})()}cleanupOldSessions(t=7){let s=`-${t}`,e=this.stmt(r.getOldSessions).all(s);for(let{session_id:n}of e)this.deleteSession(n);return e.length}};export{L as SessionDB,K as getWorktreeSuffix};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function
|
|
2
|
-
response: ${
|
|
1
|
+
function i(t){return t==null?"":String(t)}function g(t){return t==null?"":typeof t=="string"?t:JSON.stringify(t)}function b(t){let{tool_name:e,tool_input:o,tool_response:s}=t,r=[];if(e==="Read"){let n=String(o.file_path??"");return/CLAUDE\.md$|\.claude[\\/]/i.test(n)&&(r.push({type:"rule",category:"rule",data:i(n),priority:1}),s&&s.length>0&&r.push({type:"rule_content",category:"rule",data:i(s),priority:1})),r.push({type:"file_read",category:"file",data:i(n),priority:1}),r}if(e==="Edit"){let n=String(o.file_path??"");return r.push({type:"file_edit",category:"file",data:i(n),priority:1}),r}if(e==="NotebookEdit"){let n=String(o.notebook_path??"");return r.push({type:"file_edit",category:"file",data:i(n),priority:1}),r}if(e==="Write"){let n=String(o.file_path??"");return r.push({type:"file_write",category:"file",data:i(n),priority:1}),r}if(e==="Glob"){let n=String(o.pattern??"");return r.push({type:"file_glob",category:"file",data:i(n),priority:3}),r}if(e==="Grep"){let n=String(o.pattern??""),c=String(o.path??"");return r.push({type:"file_search",category:"file",data:i(`${n} in ${c}`),priority:3}),r}return r}function f(t){if(t.tool_name!=="Bash")return[];let o=String(t.tool_input.command??"").match(/\bcd\s+("([^"]+)"|'([^']+)'|(\S+))/);if(!o)return[];let s=o[2]??o[3]??o[4]??"";return[{type:"cwd",category:"cwd",data:i(s),priority:2}]}function y(t){let{tool_name:e,tool_input:o,tool_response:s,tool_output:r}=t,n=String(s??""),c=r?.isError===!0;return!(e==="Bash"&&/exit code [1-9]|error:|Error:|FAIL|failed/i.test(n))&&!c?[]:[{type:"error_tool",category:"error",data:i(n),priority:2}]}var h=[{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 _(t){if(t.tool_name!=="Bash")return[];let e=String(t.tool_input.command??""),o=h.find(s=>s.pattern.test(e));return o?[{type:"git",category:"git",data:i(o.operation),priority:2}]:[]}function m(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:i(JSON.stringify(t.tool_input)),priority:1}]:[]}function S(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=[],o=t.tool_input.allowedPrompts,s=Array.isArray(o)&&o.length>0?`exited plan mode (allowed: ${g(o.map(n=>typeof n=="object"&&n!==null&&"prompt"in n?String(n.prompt):String(n)).join(", "))})`:"exited plan mode";e.push({type:"plan_exit",category:"plan",data:i(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:i(`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:i(`plan file: ${e.split(/[/\\]/).pop()??e}`),priority:2}]}return[]}var E=[/\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 k(t){if(t.tool_name!=="Bash")return[];let e=String(t.tool_input.command??"");if(!E.some(r=>r.test(e)))return[];let s=e.replace(/\bexport\s+(\w+)=\S*/g,"export $1=***");return[{type:"env",category:"env",data:i(s),priority:2}]}function v(t){if(t.tool_name!=="Skill")return[];let e=String(t.tool_input.skill??"");return[{type:"skill",category:"skill",data:i(e),priority:2}]}function x(t){if(!t.tool_response?.includes("Error")&&!t.tool_output?.isError)return[];let e=String(t.tool_response||""),o=[/not supported/i,/cannot/i,/does not support/i,/FAIL/i,/refused/i,/permission denied/i,/incompatible/i];for(let s of o){let r=e.match(s);if(r){let n=e.toLowerCase().indexOf(r[0].toLowerCase()),c=e.slice(Math.max(0,n-50),Math.min(e.length,n+200)).trim();return[{type:"constraint_discovered",category:"constraint",data:i(c),priority:2}]}}return[]}function w(t){if(t.tool_name!=="Agent")return[];let e=i(String(t.tool_input.prompt??t.tool_input.description??"")),o=t.tool_response?i(String(t.tool_response)):"",s=o.length>0;return[{type:s?"subagent_completed":"subagent_launched",category:"subagent",data:i(s?`[completed] ${e} \u2192 ${o}`:`[launched] ${e}`),priority:s?2:3}]}function R(t){let{tool_name:e,tool_input:o,tool_response:s}=t;if(!e.startsWith("mcp__"))return[];let r=e.split("__"),n=r[r.length-1]||e,c=Object.values(o).find(d=>typeof d=="string"),p=c?`: ${i(String(c))}`:"",u=s&&s.length>0?`
|
|
2
|
+
response: ${i(s)}`:"";return[{type:"mcp",category:"mcp",data:i(`${n}${p}${u}`),priority:3}]}function A(t){if(t.tool_name!=="AskUserQuestion")return[];let e=t.tool_input.questions,o=Array.isArray(e)&&e.length>0?String(e[0].question??""):"",s=i(String(t.tool_response??"")),r=o?`Q: ${i(o)} \u2192 A: ${s}`:`answer: ${s}`;return[{type:"decision_question",category:"decision",data:i(r),priority:2}]}function I(t){if(t.tool_name!=="Agent")return[];if(!t.tool_response||t.tool_response.length===0)return[];let e=t.tool_response.length>500?t.tool_response.slice(0,500):t.tool_response;return[{type:"agent_finding",category:"agent-finding",data:i(e),priority:2}]}function T(t){let e=[g(t.tool_input),i(t.tool_response)].join(" ");if(e.length===0)return[];let o=new Set,s=e.match(/https?:\/\/[^\s)]+/g);if(s)for(let n of s)n=n.replace(/["'})\],;.]+$/,""),/localhost|127\.0\.0\.1/i.test(n)||o.add(n);let r=e.match(/(?<!\w)#(\d+)/g);if(r)for(let n of r)o.add(n);return o.size===0?[]:[{type:"external_ref",category:"external-ref",data:i(Array.from(o).join(", ")),priority:3}]}function $(t){if(t.tool_name!=="EnterWorktree")return[];let e=String(t.tool_input.name??"unnamed");return[{type:"worktree",category:"env",data:i(`entered worktree: ${e}`),priority:2}]}var H=[/\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 P(t){return H.some(o=>o.test(t))?[{type:"decision",category:"decision",data:i(t),priority:2}]:[]}var N=[/\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 C(t){return N.some(o=>o.test(t))?[{type:"role",category:"role",data:i(t),priority:3}]:[]}var L=[{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 B(t){let e=L.find(({pattern:o})=>o.test(t));return e?[{type:"intent",category:"intent",data:i(e.mode),priority:4}]:[]}var O=[/\bblocked on\b/i,/\bwaiting for\b/i,/\bneed\s+\S+\s+before\b/i,/\bcan'?t proceed until\b/i,/\bdepends on\b/i,/\bblocked\b/i,/\bbekliyor\b/i,/\bbekliyorum\b/i],F=[/\bunblocked\b/i,/\bresolved\b/i,/\bgot the\s+\S+/i,/\bis ready now\b/i,/\bcan proceed\b/i];function j(t){let e=[];return F.some(r=>r.test(t))?(e.push({type:"blocker_resolved",category:"blocked-on",data:i(t),priority:2}),e):(O.some(r=>r.test(t))&&e.push({type:"blocker",category:"blocked-on",data:i(t),priority:2}),e)}function D(t){return t.length<=1024?[]:[{type:"data",category:"data",data:i(t),priority:4}]}var a=null;function M(t){let{tool_name:e,tool_response:o,tool_output:s}=t,r=String(o??""),n=s?.isError===!0;if(e==="Bash"&&/exit code [1-9]|error:|Error:|FAIL|failed/i.test(r)||n)return a={tool:e,error:r.slice(0,200),callsSince:0},[];if(!a)return[];if(a.callsSince++,a.callsSince>10)return a=null,[];let p=e===a.tool,u=a.tool==="Read"&&(e==="Edit"||e==="Write");if(p||u){let d={type:"error_resolved",category:"error-resolution",data:i(`Error in ${a.tool}: ${a.error} \u2192 Fixed`),priority:2};return a=null,[d]}return[]}function q(){a=null}var l=[];function W(t){return`${t.length}:${t.slice(0,20)}`}function U(t){let{tool_name:e,tool_input:o}=t,s=W(JSON.stringify(o).slice(0,200));if(l.push({tool:e,inputHash:s}),l.length>50&&l.splice(0,l.length-50),l.length<3)return[];let r=0;for(let n=l.length-1;n>=0&&(l[n].tool===e&&l[n].inputHash===s);n--)r++;return r>=3?(l.splice(l.length-r),[{type:"retry_detected",category:"iteration-loop",data:i(`${e} called ${r} times with similar input`),priority:2}]):[]}function G(){l.length=0}function K(t){try{let e=[];return e.push(...b(t)),e.push(...f(t)),e.push(...y(t)),e.push(..._(t)),e.push(...k(t)),e.push(...m(t)),e.push(...S(t)),e.push(...v(t)),e.push(...w(t)),e.push(...R(t)),e.push(...A(t)),e.push(...x(t)),e.push(...$(t)),e.push(...I(t)),e.push(...T(t)),e.push(...M(t)),e.push(...U(t)),e}catch{return[]}}function z(t){try{let e=[];return e.push(...P(t)),e.push(...C(t)),e.push(...B(t)),e.push(...j(t)),e.push(...D(t)),e}catch{return[]}}export{K as extractEvents,z as extractUserEvents,q as resetErrorResolutionState,G as resetIterationLoopState};
|
|
@@ -1,29 +1,30 @@
|
|
|
1
|
-
function a(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}var
|
|
1
|
+
function a(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}var x=10;function h(t,r=4){return[...new Set(t.filter(o=>o.length>0))].slice(0,r).map(o=>o.length>80?o.slice(0,80):o)}function m(t,r){if(r.length===0)return"";let s=r.map(n=>`"${a(n)}"`).join(", ");return`
|
|
2
2
|
For full details:
|
|
3
3
|
${a(t)}(
|
|
4
4
|
queries: [${s}],
|
|
5
5
|
source: "session-events"
|
|
6
|
-
)`}function
|
|
7
|
-
`)}function
|
|
8
|
-
`)}function
|
|
9
|
-
`)}function
|
|
10
|
-
`)}function
|
|
11
|
-
`)}function
|
|
12
|
-
`)}function
|
|
6
|
+
)`}function D(t,r){if(t.length===0)return"";let s=new Map;for(let l of t){let S=l.data,p=s.get(S);p||(p={ops:new Map},s.set(S,p));let d;l.type==="file_write"?d="write":l.type==="file_read"?d="read":l.type==="file_edit"?d="edit":d=l.type,p.ops.set(d,(p.ops.get(d)??0)+1)}let o=Array.from(s.entries()).slice(-x),c=[],i=[];for(let[l,{ops:S}]of o){let p=Array.from(S.entries()).map(([b,y])=>`${b}\xD7${y}`).join(", "),d=l.split("/").pop()??l;c.push(` ${a(d)} (${a(p)})`),i.push(`${d} ${Array.from(S.keys()).join(" ")}`)}let e=h(i);return[` <files count="${s.size}">`,...c,m(r,e)," </files>"].join(`
|
|
7
|
+
`)}function R(t,r){if(t.length===0)return"";let s=[],n=[];for(let i of t)s.push(` ${a(i.data)}`),n.push(i.data);let o=h(n);return[` <errors count="${t.length}">`,...s,m(r,o)," </errors>"].join(`
|
|
8
|
+
`)}function F(t,r){if(t.length===0)return"";let s=new Set,n=[],o=[];for(let e of t)s.has(e.data)||(s.add(e.data),n.push(` ${a(e.data)}`),o.push(e.data));if(n.length===0)return"";let c=h(o);return[` <decisions count="${n.length}">`,...n,m(r,c)," </decisions>"].join(`
|
|
9
|
+
`)}function B(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 c=h(o);return[` <rules count="${n.length}">`,...n,m(r,c)," </rules>"].join(`
|
|
10
|
+
`)}function J(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 X(t){if(t.length===0)return"";let r=[],s={};for(let e of t)try{let u=JSON.parse(e.data);typeof u.subject=="string"?r.push(u.subject):typeof u.taskId=="string"&&typeof u.status=="string"&&(s[u.taskId]=u.status)}catch{}if(r.length===0)return"";let n=new Set(["completed","deleted","failed"]),o=Object.keys(s).sort((e,u)=>Number(e)-Number(u)),c=[];for(let e=0;e<r.length;e++){let u=o[e],l=u?s[u]??"pending":"pending";n.has(l)||c.push(r[e])}if(c.length===0)return"";let i=[];for(let e of c)i.push(` [pending] ${a(e)}`);return i.join(`
|
|
12
|
+
`)}function z(t,r){let s=X(t);if(!s)return"";let n=[];for(let e of t)try{let u=JSON.parse(e.data);typeof u.subject=="string"&&n.push(u.subject)}catch{}let o=h(n);return[` <task_state count="${s.split(`
|
|
13
13
|
`).length}">`,s,m(r,o)," </task_state>"].join(`
|
|
14
|
-
`)}function
|
|
15
|
-
`)}function
|
|
16
|
-
`)}function
|
|
17
|
-
`)}function
|
|
14
|
+
`)}function G(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 c=h(o);return[" <environment>",...n,m(s,c)," </environment>"].join(`
|
|
15
|
+
`)}function P(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 Q(t,r){if(t.length===0)return"";let s=new Map;for(let e of t){let u=e.data.split(":")[0].trim();s.set(u,(s.get(u)??0)+1)}let n=[],o=[];for(let[e,u]of s)n.push(` ${a(e)} (${u}\xD7)`),o.push(`skill ${e} invocation`);let c=h(o);return[` <skills count="${t.length}">`,...n,m(r,c)," </skills>"].join(`
|
|
17
|
+
`)}function U(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 c=h(o);return[` <roles count="${n.length}">`,...n,m(r,c)," </roles>"].join(`
|
|
18
|
+
`)}function V(t){if(t.length===0)return"";let r=t[t.length-1];return` <intent mode="${a(r.data)}"/>`}function W(t,r){let s=r?.compactCount??1,n=r?.searchTool??"ctx_search",o=new Date().toISOString(),c=[],i=[],e=[],u=[],l=[],S=[],p=[],d=[],b=[],y=[],$=[],k=[];for(let f of t)switch(f.category){case"file":c.push(f);break;case"task":i.push(f);break;case"rule":e.push(f);break;case"decision":u.push(f);break;case"cwd":l.push(f);break;case"error":S.push(f);break;case"env":p.push(f);break;case"git":d.push(f);break;case"subagent":b.push(f);break;case"intent":y.push(f);break;case"skill":$.push(f);break;case"role":k.push(f);break}let g=[];g.push(` <how_to_search>
|
|
18
19
|
Each section below contains a summary of prior work.
|
|
19
20
|
For FULL DETAILS, run the exact tool call shown under each section.
|
|
20
21
|
Do NOT ask the user to re-explain prior work. Search first.
|
|
21
22
|
Do NOT invent your own queries \u2014 use the ones provided.
|
|
22
|
-
</how_to_search>`);let
|
|
23
|
+
</how_to_search>`);let v=D(c,n);v&&g.push(v);let w=R(S,n);w&&g.push(w);let E=F(u,n);E&&g.push(E);let q=B(e,n);q&&g.push(q);let L=J(d,n);L&&g.push(L);let j=z(i,n);j&&g.push(j);let _=G(l,p,n);_&&g.push(_);let T=P(b,n);T&&g.push(T);let C=Q($,n);C&&g.push(C);let O=U(k,n);O&&g.push(O);let I=V(y);I&&g.push(I);let N=`<session_resume events="${t.length}" compact_count="${s}" generated_at="${o}">`,M="</session_resume>",A=g.join(`
|
|
23
24
|
|
|
24
|
-
`);return
|
|
25
|
+
`);return A?`${N}
|
|
25
26
|
|
|
26
|
-
${
|
|
27
|
+
${A}
|
|
27
28
|
|
|
28
|
-
${
|
|
29
|
-
${
|
|
29
|
+
${M}`:`${N}
|
|
30
|
+
${M}`}export{W as buildResumeSnapshot,X as renderTaskState};
|
package/hooks/sessionstart.mjs
CHANGED
|
@@ -17,6 +17,7 @@ import "./ensure-deps.mjs";
|
|
|
17
17
|
|
|
18
18
|
import { createRoutingBlock } from "./routing-block.mjs";
|
|
19
19
|
import { createToolNamer } from "./core/tool-naming.mjs";
|
|
20
|
+
import { buildAutoInjection } from "./auto-injection.mjs";
|
|
20
21
|
|
|
21
22
|
const toolNamer = createToolNamer("claude-code");
|
|
22
23
|
const ROUTING_BLOCK = createRoutingBlock(toolNamer);
|
|
@@ -54,6 +55,22 @@ try {
|
|
|
54
55
|
if (events.length > 0) {
|
|
55
56
|
const eventMeta = writeSessionEventsFile(events, getSessionEventsPath());
|
|
56
57
|
additionalContext += buildSessionDirective("compact", eventMeta, toolNamer);
|
|
58
|
+
|
|
59
|
+
// Auto-inject behavioral state on compaction (role, decisions, skills, intent)
|
|
60
|
+
const autoInjection = buildAutoInjection(events);
|
|
61
|
+
if (autoInjection) {
|
|
62
|
+
additionalContext += "\n\n" + autoInjection;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Write session-resume event
|
|
66
|
+
try {
|
|
67
|
+
db.insertEvent(sessionId, {
|
|
68
|
+
type: "resume_completed",
|
|
69
|
+
category: "session-resume",
|
|
70
|
+
data: `Session resumed from ${source}. Prior events loaded.`,
|
|
71
|
+
priority: 1,
|
|
72
|
+
}, "SessionStart");
|
|
73
|
+
} catch { /* best-effort */ }
|
|
57
74
|
}
|
|
58
75
|
|
|
59
76
|
db.close();
|
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.100",
|
|
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.100",
|
|
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",
|