context-mode 0.9.21 → 1.0.0
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/hooks/hooks.json +46 -4
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +4 -4
- package/README.md +377 -191
- package/build/adapters/claude-code/config.d.ts +8 -0
- package/build/adapters/claude-code/config.js +8 -0
- package/build/adapters/claude-code/hooks.d.ts +53 -0
- package/build/adapters/claude-code/hooks.js +88 -0
- package/build/adapters/claude-code/index.d.ts +50 -0
- package/build/adapters/claude-code/index.js +523 -0
- package/build/adapters/codex/config.d.ts +8 -0
- package/build/adapters/codex/config.js +8 -0
- package/build/adapters/codex/hooks.d.ts +21 -0
- package/build/adapters/codex/hooks.js +27 -0
- package/build/adapters/codex/index.d.ts +44 -0
- package/build/adapters/codex/index.js +223 -0
- package/build/adapters/detect.d.ts +26 -0
- package/build/adapters/detect.js +131 -0
- package/build/adapters/gemini-cli/config.d.ts +8 -0
- package/build/adapters/gemini-cli/config.js +8 -0
- package/build/adapters/gemini-cli/hooks.d.ts +44 -0
- package/build/adapters/gemini-cli/hooks.js +64 -0
- package/build/adapters/gemini-cli/index.d.ts +57 -0
- package/build/adapters/gemini-cli/index.js +468 -0
- package/build/adapters/opencode/config.d.ts +8 -0
- package/build/adapters/opencode/config.js +8 -0
- package/build/adapters/opencode/hooks.d.ts +38 -0
- package/build/adapters/opencode/hooks.js +50 -0
- package/build/adapters/opencode/index.d.ts +52 -0
- package/build/adapters/opencode/index.js +386 -0
- package/build/adapters/types.d.ts +218 -0
- package/build/adapters/types.js +13 -0
- package/build/adapters/vscode-copilot/config.d.ts +8 -0
- package/build/adapters/vscode-copilot/config.js +8 -0
- package/build/adapters/vscode-copilot/hooks.d.ts +49 -0
- package/build/adapters/vscode-copilot/hooks.js +76 -0
- package/build/adapters/vscode-copilot/index.d.ts +58 -0
- package/build/adapters/vscode-copilot/index.js +512 -0
- package/build/cli.d.ts +9 -6
- package/build/cli.js +133 -423
- package/build/db-base.d.ts +84 -0
- package/build/db-base.js +128 -0
- package/build/executor.d.ts +6 -7
- package/build/executor.js +111 -51
- package/build/opencode-plugin.d.ts +37 -0
- package/build/opencode-plugin.js +118 -0
- package/build/runtime.js +1 -1
- package/build/server.js +436 -117
- package/build/session/db.d.ts +110 -0
- package/build/session/db.js +285 -0
- package/build/session/extract.d.ts +51 -0
- package/build/session/extract.js +407 -0
- package/build/session/snapshot.d.ts +70 -0
- package/build/session/snapshot.js +309 -0
- package/build/store.d.ts +4 -22
- package/build/store.js +67 -55
- package/build/truncate.d.ts +59 -0
- package/build/truncate.js +157 -0
- package/build/types.d.ts +101 -0
- package/build/types.js +20 -0
- package/configs/claude-code/CLAUDE.md +62 -0
- package/configs/codex/AGENTS.md +58 -0
- package/configs/codex/config.toml +5 -0
- package/configs/gemini-cli/GEMINI.md +58 -0
- package/configs/gemini-cli/mcp.json +7 -0
- package/configs/gemini-cli/settings.json +49 -0
- package/configs/opencode/AGENTS.md +58 -0
- package/configs/opencode/opencode.json +10 -0
- package/configs/vscode-copilot/copilot-instructions.md +58 -0
- package/configs/vscode-copilot/hooks.json +16 -0
- package/configs/vscode-copilot/mcp.json +8 -0
- package/hooks/core/formatters.mjs +86 -0
- package/hooks/core/routing.mjs +262 -0
- package/hooks/core/stdin.mjs +19 -0
- package/hooks/formatters/claude-code.mjs +57 -0
- package/hooks/formatters/gemini-cli.mjs +55 -0
- package/hooks/formatters/vscode-copilot.mjs +55 -0
- package/hooks/gemini-cli/aftertool.mjs +58 -0
- package/hooks/gemini-cli/beforetool.mjs +25 -0
- package/hooks/gemini-cli/precompress.mjs +51 -0
- package/hooks/gemini-cli/sessionstart.mjs +117 -0
- package/hooks/hooks.json +46 -4
- package/hooks/posttooluse.mjs +53 -0
- package/hooks/precompact.mjs +55 -0
- package/hooks/pretooluse.mjs +23 -266
- package/hooks/routing-block.mjs +19 -6
- package/hooks/session-directive.mjs +353 -0
- package/hooks/session-helpers.mjs +112 -0
- package/hooks/sessionstart.mjs +123 -16
- package/hooks/userpromptsubmit.mjs +58 -0
- package/hooks/vscode-copilot/posttooluse.mjs +58 -0
- package/hooks/vscode-copilot/precompact.mjs +51 -0
- package/hooks/vscode-copilot/pretooluse.mjs +25 -0
- package/hooks/vscode-copilot/sessionstart.mjs +115 -0
- package/package.json +20 -17
- package/skills/context-mode/SKILL.md +49 -49
- package/skills/{doctor → ctx-doctor}/SKILL.md +3 -3
- package/skills/{stats → ctx-stats}/SKILL.md +3 -3
- package/skills/{upgrade → ctx-upgrade}/SKILL.md +3 -3
- package/start.mjs +47 -0
- package/hooks/pretooluse.sh +0 -147
- package/server.bundle.mjs +0 -341
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Gemini CLI PreCompress hook — snapshot generation.
|
|
4
|
+
*
|
|
5
|
+
* Triggered when Gemini CLI is about to compress the conversation.
|
|
6
|
+
* Reads all captured session events, builds a priority-sorted resume
|
|
7
|
+
* snapshot (<2KB XML), and stores it for injection after compress.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { readStdin, getSessionId, getSessionDBPath, GEMINI_OPTS } from "../session-helpers.mjs";
|
|
11
|
+
import { appendFileSync } from "node:fs";
|
|
12
|
+
import { join, dirname } from "node:path";
|
|
13
|
+
import { homedir } from "node:os";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
15
|
+
|
|
16
|
+
const HOOK_DIR = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const PKG_SESSION = join(HOOK_DIR, "..", "..", "build", "session");
|
|
18
|
+
const OPTS = GEMINI_OPTS;
|
|
19
|
+
const DEBUG_LOG = join(homedir(), ".gemini", "context-mode", "precompress-debug.log");
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const raw = await readStdin();
|
|
23
|
+
const input = JSON.parse(raw);
|
|
24
|
+
|
|
25
|
+
const { buildResumeSnapshot } = await import(join(PKG_SESSION, "snapshot.js"));
|
|
26
|
+
const { SessionDB } = await import(join(PKG_SESSION, "db.js"));
|
|
27
|
+
|
|
28
|
+
const dbPath = getSessionDBPath(OPTS);
|
|
29
|
+
const db = new SessionDB({ dbPath });
|
|
30
|
+
const sessionId = getSessionId(input, OPTS);
|
|
31
|
+
|
|
32
|
+
const events = db.getEvents(sessionId);
|
|
33
|
+
|
|
34
|
+
if (events.length > 0) {
|
|
35
|
+
const stats = db.getSessionStats(sessionId);
|
|
36
|
+
const snapshot = buildResumeSnapshot(events, {
|
|
37
|
+
compactCount: (stats?.compact_count ?? 0) + 1,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
db.upsertResume(sessionId, snapshot, events.length);
|
|
41
|
+
db.incrementCompactCount(sessionId);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
db.close();
|
|
45
|
+
} catch (err) {
|
|
46
|
+
try {
|
|
47
|
+
appendFileSync(DEBUG_LOG, `[${new Date().toISOString()}] ${err?.message || err}\n`);
|
|
48
|
+
} catch { /* silent */ }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// PreCompress is advisory — no stdout output needed
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Gemini CLI SessionStart hook for context-mode
|
|
4
|
+
*
|
|
5
|
+
* Session lifecycle management:
|
|
6
|
+
* - "startup" → Cleanup old sessions, capture GEMINI.md rules
|
|
7
|
+
* - "compact" → Write events file, inject session knowledge directive
|
|
8
|
+
* - "resume" → Load previous session events, inject directive
|
|
9
|
+
* - "clear" → No action needed
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { ROUTING_BLOCK } from "../routing-block.mjs";
|
|
13
|
+
import { writeSessionEventsFile, buildSessionDirective, getAllProjectEvents } from "../session-directive.mjs";
|
|
14
|
+
import {
|
|
15
|
+
readStdin, getSessionId, getSessionDBPath, getSessionEventsPath, getCleanupFlagPath,
|
|
16
|
+
getProjectDir, GEMINI_OPTS,
|
|
17
|
+
} from "../session-helpers.mjs";
|
|
18
|
+
import { join, dirname } from "node:path";
|
|
19
|
+
import { readFileSync, writeFileSync, unlinkSync } from "node:fs";
|
|
20
|
+
import { homedir } from "node:os";
|
|
21
|
+
import { fileURLToPath } from "node:url";
|
|
22
|
+
|
|
23
|
+
const HOOK_DIR = dirname(fileURLToPath(import.meta.url));
|
|
24
|
+
const PKG_SESSION = join(HOOK_DIR, "..", "..", "build", "session");
|
|
25
|
+
const OPTS = GEMINI_OPTS;
|
|
26
|
+
|
|
27
|
+
let additionalContext = ROUTING_BLOCK;
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const raw = await readStdin();
|
|
31
|
+
const input = JSON.parse(raw);
|
|
32
|
+
const source = input.source ?? "startup";
|
|
33
|
+
|
|
34
|
+
if (source === "compact") {
|
|
35
|
+
const { SessionDB } = await import(join(PKG_SESSION, "db.js"));
|
|
36
|
+
const dbPath = getSessionDBPath(OPTS);
|
|
37
|
+
const db = new SessionDB({ dbPath });
|
|
38
|
+
const sessionId = getSessionId(input, OPTS);
|
|
39
|
+
const resume = db.getResume(sessionId);
|
|
40
|
+
|
|
41
|
+
if (resume && !resume.consumed) {
|
|
42
|
+
db.markResumeConsumed(sessionId);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const events = getAllProjectEvents(db);
|
|
46
|
+
if (events.length > 0) {
|
|
47
|
+
const eventMeta = writeSessionEventsFile(events, getSessionEventsPath(OPTS));
|
|
48
|
+
additionalContext += buildSessionDirective("compact", eventMeta);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
db.close();
|
|
52
|
+
} else if (source === "resume") {
|
|
53
|
+
try { unlinkSync(getCleanupFlagPath(OPTS)); } catch { /* no flag */ }
|
|
54
|
+
|
|
55
|
+
const { SessionDB } = await import(join(PKG_SESSION, "db.js"));
|
|
56
|
+
const dbPath = getSessionDBPath(OPTS);
|
|
57
|
+
const db = new SessionDB({ dbPath });
|
|
58
|
+
|
|
59
|
+
const events = getAllProjectEvents(db);
|
|
60
|
+
if (events.length > 0) {
|
|
61
|
+
const eventMeta = writeSessionEventsFile(events, getSessionEventsPath(OPTS));
|
|
62
|
+
additionalContext += buildSessionDirective("resume", eventMeta);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
db.close();
|
|
66
|
+
} else if (source === "startup") {
|
|
67
|
+
const { SessionDB } = await import(join(PKG_SESSION, "db.js"));
|
|
68
|
+
const dbPath = getSessionDBPath(OPTS);
|
|
69
|
+
const db = new SessionDB({ dbPath });
|
|
70
|
+
try { unlinkSync(getSessionEventsPath(OPTS)); } catch { /* no stale file */ }
|
|
71
|
+
|
|
72
|
+
const cleanupFlag = getCleanupFlagPath(OPTS);
|
|
73
|
+
let previousWasFresh = false;
|
|
74
|
+
try { readFileSync(cleanupFlag); previousWasFresh = true; } catch { /* no flag */ }
|
|
75
|
+
|
|
76
|
+
if (previousWasFresh) {
|
|
77
|
+
db.cleanupOldSessions(0);
|
|
78
|
+
} else {
|
|
79
|
+
db.cleanupOldSessions(7);
|
|
80
|
+
}
|
|
81
|
+
db.db.exec(`DELETE FROM session_events WHERE session_id NOT IN (SELECT session_id FROM session_meta)`);
|
|
82
|
+
writeFileSync(cleanupFlag, new Date().toISOString(), "utf-8");
|
|
83
|
+
|
|
84
|
+
const sessionId = getSessionId(input, OPTS);
|
|
85
|
+
const projectDir = getProjectDir(OPTS);
|
|
86
|
+
db.ensureSession(sessionId, projectDir);
|
|
87
|
+
const ruleFilePaths = [
|
|
88
|
+
join(homedir(), ".gemini", "GEMINI.md"),
|
|
89
|
+
join(projectDir, "GEMINI.md"),
|
|
90
|
+
];
|
|
91
|
+
for (const p of ruleFilePaths) {
|
|
92
|
+
try {
|
|
93
|
+
const content = readFileSync(p, "utf-8");
|
|
94
|
+
if (content.trim()) {
|
|
95
|
+
db.insertEvent(sessionId, { type: "rule", category: "rule", data: p, priority: 1 });
|
|
96
|
+
db.insertEvent(sessionId, { type: "rule_content", category: "rule", data: content, priority: 1 });
|
|
97
|
+
}
|
|
98
|
+
} catch { /* file doesn't exist — skip */ }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
db.close();
|
|
102
|
+
}
|
|
103
|
+
// "clear" — no action needed
|
|
104
|
+
} catch (err) {
|
|
105
|
+
try {
|
|
106
|
+
const { appendFileSync } = await import("node:fs");
|
|
107
|
+
const { join: pjoin } = await import("node:path");
|
|
108
|
+
const { homedir: hd } = await import("node:os");
|
|
109
|
+
appendFileSync(
|
|
110
|
+
pjoin(hd(), ".gemini", "context-mode", "sessionstart-debug.log"),
|
|
111
|
+
`[${new Date().toISOString()}] ${err?.message || err}\n${err?.stack || ""}\n`,
|
|
112
|
+
);
|
|
113
|
+
} catch { /* ignore logging failure */ }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const output = `SessionStart:compact hook success: Success\nSessionStart hook additional context: \n${additionalContext}`;
|
|
117
|
+
process.stdout.write(output);
|
package/hooks/hooks.json
CHANGED
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
{
|
|
2
|
-
"description": "Context-mode
|
|
2
|
+
"description": "Context-mode hooks — PreToolUse routing, PostToolUse session capture, PreCompact snapshot, SessionStart context injection",
|
|
3
3
|
"hooks": {
|
|
4
|
+
"PostToolUse": [
|
|
5
|
+
{
|
|
6
|
+
"matcher": "",
|
|
7
|
+
"hooks": [
|
|
8
|
+
{
|
|
9
|
+
"type": "command",
|
|
10
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/posttooluse.mjs"
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"PreCompact": [
|
|
16
|
+
{
|
|
17
|
+
"matcher": "",
|
|
18
|
+
"hooks": [
|
|
19
|
+
{
|
|
20
|
+
"type": "command",
|
|
21
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/precompact.mjs"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
],
|
|
4
26
|
"PreToolUse": [
|
|
5
27
|
{
|
|
6
28
|
"matcher": "Bash",
|
|
@@ -38,6 +60,15 @@
|
|
|
38
60
|
}
|
|
39
61
|
]
|
|
40
62
|
},
|
|
63
|
+
{
|
|
64
|
+
"matcher": "Agent",
|
|
65
|
+
"hooks": [
|
|
66
|
+
{
|
|
67
|
+
"type": "command",
|
|
68
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/pretooluse.mjs"
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
},
|
|
41
72
|
{
|
|
42
73
|
"matcher": "Task",
|
|
43
74
|
"hooks": [
|
|
@@ -48,7 +79,7 @@
|
|
|
48
79
|
]
|
|
49
80
|
},
|
|
50
81
|
{
|
|
51
|
-
"matcher": "mcp__plugin_context-mode_context-
|
|
82
|
+
"matcher": "mcp__plugin_context-mode_context-mode__ctx_execute",
|
|
52
83
|
"hooks": [
|
|
53
84
|
{
|
|
54
85
|
"type": "command",
|
|
@@ -57,7 +88,7 @@
|
|
|
57
88
|
]
|
|
58
89
|
},
|
|
59
90
|
{
|
|
60
|
-
"matcher": "mcp__plugin_context-mode_context-
|
|
91
|
+
"matcher": "mcp__plugin_context-mode_context-mode__ctx_execute_file",
|
|
61
92
|
"hooks": [
|
|
62
93
|
{
|
|
63
94
|
"type": "command",
|
|
@@ -66,7 +97,7 @@
|
|
|
66
97
|
]
|
|
67
98
|
},
|
|
68
99
|
{
|
|
69
|
-
"matcher": "mcp__plugin_context-mode_context-
|
|
100
|
+
"matcher": "mcp__plugin_context-mode_context-mode__ctx_batch_execute",
|
|
70
101
|
"hooks": [
|
|
71
102
|
{
|
|
72
103
|
"type": "command",
|
|
@@ -75,6 +106,17 @@
|
|
|
75
106
|
]
|
|
76
107
|
}
|
|
77
108
|
],
|
|
109
|
+
"UserPromptSubmit": [
|
|
110
|
+
{
|
|
111
|
+
"matcher": "",
|
|
112
|
+
"hooks": [
|
|
113
|
+
{
|
|
114
|
+
"type": "command",
|
|
115
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/userpromptsubmit.mjs"
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
],
|
|
78
120
|
"SessionStart": [
|
|
79
121
|
{
|
|
80
122
|
"matcher": "",
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* PostToolUse hook for context-mode session continuity.
|
|
4
|
+
*
|
|
5
|
+
* Captures session events from tool calls (13 categories) and stores
|
|
6
|
+
* them in the per-project SessionDB for later resume snapshot building.
|
|
7
|
+
*
|
|
8
|
+
* Must be fast (<20ms). No network, no LLM, just SQLite writes.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { readStdin, getSessionId, getSessionDBPath } from "./session-helpers.mjs";
|
|
12
|
+
import { join, dirname } from "node:path";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
|
|
15
|
+
// Resolve absolute path for imports — relative dynamic imports can fail
|
|
16
|
+
// when Claude Code invokes hooks from a different working directory.
|
|
17
|
+
const HOOK_DIR = dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
const PKG_SESSION = join(HOOK_DIR, "..", "build", "session");
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const raw = await readStdin();
|
|
22
|
+
const input = JSON.parse(raw);
|
|
23
|
+
|
|
24
|
+
const { extractEvents } = await import(join(PKG_SESSION, "extract.js"));
|
|
25
|
+
const { SessionDB } = await import(join(PKG_SESSION, "db.js"));
|
|
26
|
+
|
|
27
|
+
const dbPath = getSessionDBPath();
|
|
28
|
+
const db = new SessionDB({ dbPath });
|
|
29
|
+
const sessionId = getSessionId(input);
|
|
30
|
+
|
|
31
|
+
// Ensure session meta exists
|
|
32
|
+
db.ensureSession(sessionId, process.env.CLAUDE_PROJECT_DIR || process.cwd());
|
|
33
|
+
|
|
34
|
+
// Extract and store events
|
|
35
|
+
const events = extractEvents({
|
|
36
|
+
tool_name: input.tool_name,
|
|
37
|
+
tool_input: input.tool_input ?? {},
|
|
38
|
+
tool_response: typeof input.tool_response === "string"
|
|
39
|
+
? input.tool_response
|
|
40
|
+
: JSON.stringify(input.tool_response ?? ""),
|
|
41
|
+
tool_output: input.tool_output,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
for (const event of events) {
|
|
45
|
+
db.insertEvent(sessionId, event, "PostToolUse");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
db.close();
|
|
49
|
+
} catch {
|
|
50
|
+
// PostToolUse must never block the session — silent fallback
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// PostToolUse hooks don't need hookSpecificOutput
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* PreCompact hook for context-mode session continuity.
|
|
4
|
+
*
|
|
5
|
+
* Triggered when Claude Code is about to compact the conversation.
|
|
6
|
+
* Reads all captured session events, builds a priority-sorted resume
|
|
7
|
+
* snapshot (<2KB XML), and stores it for injection after compact.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { readStdin, getSessionId, getSessionDBPath } from "./session-helpers.mjs";
|
|
11
|
+
import { appendFileSync } from "node:fs";
|
|
12
|
+
import { join, dirname } from "node:path";
|
|
13
|
+
import { homedir } from "node:os";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
15
|
+
|
|
16
|
+
// Resolve absolute path for imports
|
|
17
|
+
const HOOK_DIR = dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
const PKG_SESSION = join(HOOK_DIR, "..", "build", "session");
|
|
19
|
+
const DEBUG_LOG = join(homedir(), ".claude", "context-mode", "precompact-debug.log");
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const raw = await readStdin();
|
|
23
|
+
const input = JSON.parse(raw);
|
|
24
|
+
|
|
25
|
+
const { buildResumeSnapshot } = await import(join(PKG_SESSION, "snapshot.js"));
|
|
26
|
+
const { SessionDB } = await import(join(PKG_SESSION, "db.js"));
|
|
27
|
+
|
|
28
|
+
const dbPath = getSessionDBPath();
|
|
29
|
+
const db = new SessionDB({ dbPath });
|
|
30
|
+
const sessionId = getSessionId(input);
|
|
31
|
+
|
|
32
|
+
// Get all events for this session
|
|
33
|
+
const events = db.getEvents(sessionId);
|
|
34
|
+
|
|
35
|
+
if (events.length > 0) {
|
|
36
|
+
const stats = db.getSessionStats(sessionId);
|
|
37
|
+
const snapshot = buildResumeSnapshot(events, {
|
|
38
|
+
compactCount: (stats?.compact_count ?? 0) + 1,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
db.upsertResume(sessionId, snapshot, events.length);
|
|
42
|
+
db.incrementCompactCount(sessionId);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
db.close();
|
|
46
|
+
} catch (err) {
|
|
47
|
+
try {
|
|
48
|
+
appendFileSync(DEBUG_LOG, `[${new Date().toISOString()}] ${err.message}\n`);
|
|
49
|
+
} catch {
|
|
50
|
+
// Silent fallback
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// PreCompact doesn't need hookSpecificOutput
|
|
55
|
+
console.log(JSON.stringify({}));
|