context-mode 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/build/server.js +1 -1
- package/build/session/snapshot.d.ts +5 -1
- package/build/session/snapshot.js +40 -5
- package/hooks/session-directive.mjs +51 -9
- package/package.json +1 -1
- package/server.bundle.mjs +389 -0
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Claude Code plugins by Mert Koseoğlu",
|
|
9
|
-
"version": "1.0.
|
|
9
|
+
"version": "1.0.1"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
13
13
|
"name": "context-mode",
|
|
14
14
|
"source": "./",
|
|
15
15
|
"description": "Claude Code MCP plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
|
|
16
|
-
"version": "1.0.
|
|
16
|
+
"version": "1.0.1",
|
|
17
17
|
"author": {
|
|
18
18
|
"name": "Mert Koseoğlu"
|
|
19
19
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-mode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "MCP server that saves 98% of your context window with session continuity. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and automatic state restore across compactions.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Mert Koseoğlu",
|
package/build/server.js
CHANGED
|
@@ -12,7 +12,7 @@ import { PolyglotExecutor } from "./executor.js";
|
|
|
12
12
|
import { ContentStore, cleanupStaleDBs } from "./store.js";
|
|
13
13
|
import { readBashPolicies, evaluateCommandDenyOnly, extractShellCommands, readToolDenyPatterns, evaluateFilePath, } from "./security.js";
|
|
14
14
|
import { detectRuntimes, getRuntimeSummary, getAvailableLanguages, hasBunRuntime, } from "./runtime.js";
|
|
15
|
-
const VERSION = "1.0.
|
|
15
|
+
const VERSION = "1.0.1";
|
|
16
16
|
// Prevent silent server death from unhandled async errors
|
|
17
17
|
process.on("unhandledRejection", (err) => {
|
|
18
18
|
process.stderr.write(`[context-mode] unhandledRejection: ${err}\n`);
|
|
@@ -29,7 +29,11 @@ export interface BuildSnapshotOpts {
|
|
|
29
29
|
export declare function renderActiveFiles(fileEvents: StoredEvent[]): string;
|
|
30
30
|
/**
|
|
31
31
|
* Render <task_state> from task events.
|
|
32
|
-
*
|
|
32
|
+
* Reconstructs the full task list from create/update events,
|
|
33
|
+
* filters out completed tasks, and renders only pending/in-progress work.
|
|
34
|
+
*
|
|
35
|
+
* TaskCreate events have `{ subject }`, TaskUpdate events have `{ taskId, status }`.
|
|
36
|
+
* Match by chronological order: creates[0] → lowest taskId from updates.
|
|
33
37
|
*/
|
|
34
38
|
export declare function renderTaskState(taskEvents: StoredEvent[]): string;
|
|
35
39
|
/**
|
|
@@ -61,15 +61,50 @@ export function renderActiveFiles(fileEvents) {
|
|
|
61
61
|
}
|
|
62
62
|
/**
|
|
63
63
|
* Render <task_state> from task events.
|
|
64
|
-
*
|
|
64
|
+
* Reconstructs the full task list from create/update events,
|
|
65
|
+
* filters out completed tasks, and renders only pending/in-progress work.
|
|
66
|
+
*
|
|
67
|
+
* TaskCreate events have `{ subject }`, TaskUpdate events have `{ taskId, status }`.
|
|
68
|
+
* Match by chronological order: creates[0] → lowest taskId from updates.
|
|
65
69
|
*/
|
|
66
70
|
export function renderTaskState(taskEvents) {
|
|
67
71
|
if (taskEvents.length === 0)
|
|
68
72
|
return "";
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
+
const creates = [];
|
|
74
|
+
const updates = {};
|
|
75
|
+
for (const ev of taskEvents) {
|
|
76
|
+
try {
|
|
77
|
+
const parsed = JSON.parse(ev.data);
|
|
78
|
+
if (typeof parsed.subject === "string") {
|
|
79
|
+
creates.push(parsed.subject);
|
|
80
|
+
}
|
|
81
|
+
else if (typeof parsed.taskId === "string" && typeof parsed.status === "string") {
|
|
82
|
+
updates[parsed.taskId] = parsed.status;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch { /* not JSON */ }
|
|
86
|
+
}
|
|
87
|
+
if (creates.length === 0)
|
|
88
|
+
return "";
|
|
89
|
+
// Match creates to updates positionally (creates[0] → lowest taskId)
|
|
90
|
+
const sortedIds = Object.keys(updates).sort((a, b) => Number(a) - Number(b));
|
|
91
|
+
const pending = [];
|
|
92
|
+
for (let i = 0; i < creates.length; i++) {
|
|
93
|
+
const matchedId = sortedIds[i];
|
|
94
|
+
const status = matchedId ? (updates[matchedId] ?? "pending") : "pending";
|
|
95
|
+
if (status !== "completed") {
|
|
96
|
+
pending.push(creates[i]);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// All tasks completed — nothing to render
|
|
100
|
+
if (pending.length === 0)
|
|
101
|
+
return "";
|
|
102
|
+
const lines = [" <task_state>"];
|
|
103
|
+
for (const task of pending) {
|
|
104
|
+
lines.push(` - ${escapeXML(truncateString(task, 100))}`);
|
|
105
|
+
}
|
|
106
|
+
lines.push(" </task_state>");
|
|
107
|
+
return lines.join("\n");
|
|
73
108
|
}
|
|
74
109
|
/**
|
|
75
110
|
* Render <rules> from rule events.
|
|
@@ -62,10 +62,44 @@ export function writeSessionEventsFile(events, eventsPath) {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
if (grouped.task?.length > 0) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
for (const ev of grouped.task)
|
|
68
|
-
|
|
65
|
+
const creates = [];
|
|
66
|
+
const updates = {};
|
|
67
|
+
for (const ev of grouped.task) {
|
|
68
|
+
try {
|
|
69
|
+
const parsed = JSON.parse(ev.data);
|
|
70
|
+
if (parsed.subject) {
|
|
71
|
+
creates.push(parsed.subject);
|
|
72
|
+
} else if (parsed.taskId && parsed.status) {
|
|
73
|
+
updates[parsed.taskId] = parsed.status;
|
|
74
|
+
}
|
|
75
|
+
} catch { /* not JSON — dump as-is */
|
|
76
|
+
creates.push(ev.data);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const sortedIds = Object.keys(updates).sort((a, b) => Number(a) - Number(b));
|
|
80
|
+
const pending = [];
|
|
81
|
+
const completed = [];
|
|
82
|
+
for (let i = 0; i < creates.length; i++) {
|
|
83
|
+
const matchedId = sortedIds[i];
|
|
84
|
+
const status = matchedId ? (updates[matchedId] || "pending") : "pending";
|
|
85
|
+
if (status === "completed") {
|
|
86
|
+
completed.push(creates[i]);
|
|
87
|
+
} else {
|
|
88
|
+
pending.push(creates[i]);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (pending.length > 0) {
|
|
92
|
+
lines.push("## Tasks In Progress");
|
|
93
|
+
lines.push("");
|
|
94
|
+
for (const task of pending) lines.push(`- ${task}`);
|
|
95
|
+
lines.push("");
|
|
96
|
+
}
|
|
97
|
+
if (completed.length > 0) {
|
|
98
|
+
lines.push("## Tasks Completed");
|
|
99
|
+
lines.push("");
|
|
100
|
+
for (const task of completed) lines.push(`- ${task}`);
|
|
101
|
+
lines.push("");
|
|
102
|
+
}
|
|
69
103
|
}
|
|
70
104
|
|
|
71
105
|
if (grouped.decision?.length > 0) {
|
|
@@ -179,10 +213,11 @@ export function buildSessionDirective(source, eventMeta) {
|
|
|
179
213
|
block += `\n`;
|
|
180
214
|
}
|
|
181
215
|
|
|
182
|
-
// 2. Tasks — parsed into readable format
|
|
216
|
+
// 2. Tasks — parsed into readable format, only pending/in-progress shown
|
|
183
217
|
// TaskCreate events have {subject} but no taskId.
|
|
184
218
|
// TaskUpdate events have {taskId, status} but no subject.
|
|
185
219
|
// Match by chronological order: creates[0] → lowest taskId from updates.
|
|
220
|
+
// Completed tasks are excluded — the model should not re-work them.
|
|
186
221
|
if (grouped.task?.length > 0) {
|
|
187
222
|
const creates = [];
|
|
188
223
|
const updates = {};
|
|
@@ -200,14 +235,21 @@ export function buildSessionDirective(source, eventMeta) {
|
|
|
200
235
|
|
|
201
236
|
if (creates.length > 0) {
|
|
202
237
|
const sortedIds = Object.keys(updates).sort((a, b) => Number(a) - Number(b));
|
|
203
|
-
|
|
238
|
+
const pending = [];
|
|
204
239
|
for (let i = 0; i < creates.length; i++) {
|
|
205
240
|
const matchedId = sortedIds[i];
|
|
206
241
|
const status = matchedId ? (updates[matchedId] || "pending") : "pending";
|
|
207
|
-
|
|
208
|
-
|
|
242
|
+
if (status !== "completed") {
|
|
243
|
+
pending.push(creates[i]);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (pending.length > 0) {
|
|
247
|
+
block += `\n## Pending Tasks`;
|
|
248
|
+
for (const task of pending) {
|
|
249
|
+
block += `\n- ${task}`;
|
|
250
|
+
}
|
|
251
|
+
block += `\n`;
|
|
209
252
|
}
|
|
210
|
-
block += `\n`;
|
|
211
253
|
}
|
|
212
254
|
}
|
|
213
255
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-mode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
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",
|