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,58 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* VS Code Copilot PostToolUse hook — session event capture.
|
|
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, getProjectDir, VSCODE_OPTS } from "../session-helpers.mjs";
|
|
12
|
+
import { appendFileSync } from "node:fs";
|
|
13
|
+
import { join, dirname } from "node:path";
|
|
14
|
+
import { homedir } from "node:os";
|
|
15
|
+
import { fileURLToPath } from "node:url";
|
|
16
|
+
|
|
17
|
+
const HOOK_DIR = dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
const PKG_SESSION = join(HOOK_DIR, "..", "..", "build", "session");
|
|
19
|
+
const OPTS = VSCODE_OPTS;
|
|
20
|
+
const DEBUG_LOG = join(homedir(), ".vscode", "context-mode", "posttooluse-debug.log");
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const raw = await readStdin();
|
|
24
|
+
const input = JSON.parse(raw);
|
|
25
|
+
|
|
26
|
+
appendFileSync(DEBUG_LOG, `[${new Date().toISOString()}] CALL: ${input.tool_name}\n`);
|
|
27
|
+
|
|
28
|
+
const { extractEvents } = await import(join(PKG_SESSION, "extract.js"));
|
|
29
|
+
const { SessionDB } = await import(join(PKG_SESSION, "db.js"));
|
|
30
|
+
|
|
31
|
+
const dbPath = getSessionDBPath(OPTS);
|
|
32
|
+
const db = new SessionDB({ dbPath });
|
|
33
|
+
const sessionId = getSessionId(input, OPTS);
|
|
34
|
+
|
|
35
|
+
db.ensureSession(sessionId, getProjectDir(OPTS));
|
|
36
|
+
|
|
37
|
+
const events = extractEvents({
|
|
38
|
+
tool_name: input.tool_name,
|
|
39
|
+
tool_input: input.tool_input ?? {},
|
|
40
|
+
tool_response: typeof input.tool_response === "string"
|
|
41
|
+
? input.tool_response
|
|
42
|
+
: JSON.stringify(input.tool_response ?? ""),
|
|
43
|
+
tool_output: input.tool_output,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
for (const event of events) {
|
|
47
|
+
db.insertEvent(sessionId, event, "PostToolUse");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
appendFileSync(DEBUG_LOG, `[${new Date().toISOString()}] OK: ${input.tool_name} → ${events.length} events\n`);
|
|
51
|
+
db.close();
|
|
52
|
+
} catch (err) {
|
|
53
|
+
try {
|
|
54
|
+
appendFileSync(DEBUG_LOG, `[${new Date().toISOString()}] ERR: ${err?.message || err}\n`);
|
|
55
|
+
} catch { /* silent */ }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// PostToolUse — no stdout output
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* VS Code Copilot PreCompact hook — snapshot generation.
|
|
4
|
+
*
|
|
5
|
+
* Triggered when VS Code Copilot 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, VSCODE_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 = VSCODE_OPTS;
|
|
19
|
+
const DEBUG_LOG = join(homedir(), ".vscode", "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(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
|
+
// PreCompact — no stdout output needed
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* VS Code Copilot PreToolUse hook for context-mode
|
|
4
|
+
* Thin wrapper — uses shared routing core, no self-heal, no Claude Code-specific logic.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { dirname, resolve } from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { readStdin } from "../core/stdin.mjs";
|
|
10
|
+
import { routePreToolUse, initSecurity } from "../core/routing.mjs";
|
|
11
|
+
import { formatDecision } from "../core/formatters.mjs";
|
|
12
|
+
|
|
13
|
+
const __hookDir = dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
await initSecurity(resolve(__hookDir, "..", "..", "build"));
|
|
15
|
+
|
|
16
|
+
const raw = await readStdin();
|
|
17
|
+
const input = JSON.parse(raw);
|
|
18
|
+
const tool = input.tool_name ?? "";
|
|
19
|
+
const toolInput = input.tool_input ?? {};
|
|
20
|
+
|
|
21
|
+
const decision = routePreToolUse(tool, toolInput, process.env.VSCODE_CWD || process.env.CLAUDE_PROJECT_DIR);
|
|
22
|
+
const response = formatDecision("vscode-copilot", decision);
|
|
23
|
+
if (response !== null) {
|
|
24
|
+
process.stdout.write(JSON.stringify(response) + "\n");
|
|
25
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* VS Code Copilot SessionStart hook for context-mode
|
|
4
|
+
*
|
|
5
|
+
* Session lifecycle management:
|
|
6
|
+
* - "startup" → Cleanup old sessions, capture instruction file 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, VSCODE_OPTS,
|
|
17
|
+
} from "../session-helpers.mjs";
|
|
18
|
+
import { join } from "node:path";
|
|
19
|
+
import { readFileSync, writeFileSync, unlinkSync } from "node:fs";
|
|
20
|
+
import { homedir } from "node:os";
|
|
21
|
+
|
|
22
|
+
const HOOK_DIR = new URL(".", import.meta.url).pathname;
|
|
23
|
+
const PKG_SESSION = join(HOOK_DIR, "..", "..", "build", "session");
|
|
24
|
+
const OPTS = VSCODE_OPTS;
|
|
25
|
+
|
|
26
|
+
let additionalContext = ROUTING_BLOCK;
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const raw = await readStdin();
|
|
30
|
+
const input = JSON.parse(raw);
|
|
31
|
+
const source = input.source ?? "startup";
|
|
32
|
+
|
|
33
|
+
if (source === "compact") {
|
|
34
|
+
const { SessionDB } = await import(join(PKG_SESSION, "db.js"));
|
|
35
|
+
const dbPath = getSessionDBPath(OPTS);
|
|
36
|
+
const db = new SessionDB({ dbPath });
|
|
37
|
+
const sessionId = getSessionId(input, OPTS);
|
|
38
|
+
const resume = db.getResume(sessionId);
|
|
39
|
+
|
|
40
|
+
if (resume && !resume.consumed) {
|
|
41
|
+
db.markResumeConsumed(sessionId);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const events = getAllProjectEvents(db);
|
|
45
|
+
if (events.length > 0) {
|
|
46
|
+
const eventMeta = writeSessionEventsFile(events, getSessionEventsPath(OPTS));
|
|
47
|
+
additionalContext += buildSessionDirective("compact", eventMeta);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
db.close();
|
|
51
|
+
} else if (source === "resume") {
|
|
52
|
+
try { unlinkSync(getCleanupFlagPath(OPTS)); } catch { /* no flag */ }
|
|
53
|
+
|
|
54
|
+
const { SessionDB } = await import(join(PKG_SESSION, "db.js"));
|
|
55
|
+
const dbPath = getSessionDBPath(OPTS);
|
|
56
|
+
const db = new SessionDB({ dbPath });
|
|
57
|
+
|
|
58
|
+
const events = getAllProjectEvents(db);
|
|
59
|
+
if (events.length > 0) {
|
|
60
|
+
const eventMeta = writeSessionEventsFile(events, getSessionEventsPath(OPTS));
|
|
61
|
+
additionalContext += buildSessionDirective("resume", eventMeta);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
db.close();
|
|
65
|
+
} else if (source === "startup") {
|
|
66
|
+
const { SessionDB } = await import(join(PKG_SESSION, "db.js"));
|
|
67
|
+
const dbPath = getSessionDBPath(OPTS);
|
|
68
|
+
const db = new SessionDB({ dbPath });
|
|
69
|
+
try { unlinkSync(getSessionEventsPath(OPTS)); } catch { /* no stale file */ }
|
|
70
|
+
|
|
71
|
+
const cleanupFlag = getCleanupFlagPath(OPTS);
|
|
72
|
+
let previousWasFresh = false;
|
|
73
|
+
try { readFileSync(cleanupFlag); previousWasFresh = true; } catch { /* no flag */ }
|
|
74
|
+
|
|
75
|
+
if (previousWasFresh) {
|
|
76
|
+
db.cleanupOldSessions(0);
|
|
77
|
+
} else {
|
|
78
|
+
db.cleanupOldSessions(7);
|
|
79
|
+
}
|
|
80
|
+
db.db.exec(`DELETE FROM session_events WHERE session_id NOT IN (SELECT session_id FROM session_meta)`);
|
|
81
|
+
writeFileSync(cleanupFlag, new Date().toISOString(), "utf-8");
|
|
82
|
+
|
|
83
|
+
const sessionId = getSessionId(input, OPTS);
|
|
84
|
+
const projectDir = getProjectDir(OPTS);
|
|
85
|
+
db.ensureSession(sessionId, projectDir);
|
|
86
|
+
const ruleFilePaths = [
|
|
87
|
+
join(projectDir, ".github", "copilot-instructions.md"),
|
|
88
|
+
];
|
|
89
|
+
for (const p of ruleFilePaths) {
|
|
90
|
+
try {
|
|
91
|
+
const content = readFileSync(p, "utf-8");
|
|
92
|
+
if (content.trim()) {
|
|
93
|
+
db.insertEvent(sessionId, { type: "rule", category: "rule", data: p, priority: 1 });
|
|
94
|
+
db.insertEvent(sessionId, { type: "rule_content", category: "rule", data: content, priority: 1 });
|
|
95
|
+
}
|
|
96
|
+
} catch { /* file doesn't exist — skip */ }
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
db.close();
|
|
100
|
+
}
|
|
101
|
+
// "clear" — no action needed
|
|
102
|
+
} catch (err) {
|
|
103
|
+
try {
|
|
104
|
+
const { appendFileSync } = await import("node:fs");
|
|
105
|
+
const { join: pjoin } = await import("node:path");
|
|
106
|
+
const { homedir: hd } = await import("node:os");
|
|
107
|
+
appendFileSync(
|
|
108
|
+
pjoin(hd(), ".vscode", "context-mode", "sessionstart-debug.log"),
|
|
109
|
+
`[${new Date().toISOString()}] ${err?.message || err}\n${err?.stack || ""}\n`,
|
|
110
|
+
);
|
|
111
|
+
} catch { /* ignore logging failure */ }
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const output = `SessionStart:compact hook success: Success\nSessionStart hook additional context: \n${additionalContext}`;
|
|
115
|
+
process.stdout.write(output);
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-mode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "
|
|
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",
|
|
7
7
|
"license": "Elastic-2.0",
|
|
8
8
|
"keywords": [
|
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
"model-context-protocol",
|
|
11
11
|
"claude",
|
|
12
12
|
"claude-code",
|
|
13
|
+
"gemini-cli",
|
|
14
|
+
"vscode-copilot",
|
|
15
|
+
"opencode",
|
|
16
|
+
"codex-cli",
|
|
13
17
|
"context-window",
|
|
14
18
|
"sandbox",
|
|
15
19
|
"code-execution",
|
|
@@ -18,16 +22,22 @@
|
|
|
18
22
|
],
|
|
19
23
|
"repository": {
|
|
20
24
|
"type": "git",
|
|
21
|
-
"url": "https://github.com/mksglu/
|
|
25
|
+
"url": "https://github.com/mksglu/context-mode"
|
|
26
|
+
},
|
|
27
|
+
"homepage": "https://github.com/mksglu/context-mode#readme",
|
|
28
|
+
"bugs": "https://github.com/mksglu/context-mode/issues",
|
|
29
|
+
"main": "./build/cli.js",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": "./build/cli.js",
|
|
32
|
+
"./plugin": "./build/opencode-plugin.js"
|
|
22
33
|
},
|
|
23
|
-
"homepage": "https://github.com/mksglu/claude-context-mode#readme",
|
|
24
|
-
"bugs": "https://github.com/mksglu/claude-context-mode/issues",
|
|
25
34
|
"bin": {
|
|
26
35
|
"context-mode": "./build/cli.js"
|
|
27
36
|
},
|
|
28
37
|
"files": [
|
|
29
38
|
"build",
|
|
30
39
|
"hooks",
|
|
40
|
+
"configs",
|
|
31
41
|
"server.bundle.mjs",
|
|
32
42
|
"skills",
|
|
33
43
|
".claude-plugin",
|
|
@@ -44,20 +54,12 @@
|
|
|
44
54
|
"setup": "npx tsx src/cli.ts setup",
|
|
45
55
|
"doctor": "npx tsx src/cli.ts doctor",
|
|
46
56
|
"typecheck": "tsc --noEmit",
|
|
47
|
-
"test": "
|
|
57
|
+
"test": "vitest run",
|
|
58
|
+
"test:watch": "vitest",
|
|
48
59
|
"benchmark": "npx tsx tests/benchmark.ts",
|
|
49
60
|
"test:use-cases": "npx tsx tests/use-cases.ts",
|
|
50
61
|
"test:compare": "npx tsx tests/context-comparison.ts",
|
|
51
|
-
"test:ecosystem": "npx tsx tests/ecosystem-benchmark.ts"
|
|
52
|
-
"test:store": "npx tsx tests/store.test.ts",
|
|
53
|
-
"test:fuzzy": "npx tsx tests/fuzzy-search.test.ts",
|
|
54
|
-
"test:hooks": "npx tsx tests/hook-integration.test.ts",
|
|
55
|
-
"test:project-dir": "npx tsx tests/project-dir.test.ts",
|
|
56
|
-
"test:stream-cap": "npx tsx tests/stream-cap.test.ts",
|
|
57
|
-
"test:search-wiring": "npx tsx tests/search-wiring.test.ts",
|
|
58
|
-
"test:search-fallback": "npx tsx tests/search-fallback-integration.test.ts",
|
|
59
|
-
"test:turndown": "npx tsx tests/turndown.test.ts",
|
|
60
|
-
"test:all": "for f in tests/*.test.ts; do npx tsx \"$f\" || exit 1; done"
|
|
62
|
+
"test:ecosystem": "npx tsx tests/ecosystem-benchmark.ts"
|
|
61
63
|
},
|
|
62
64
|
"dependencies": {
|
|
63
65
|
"@clack/prompts": "^1.0.1",
|
|
@@ -75,6 +77,7 @@
|
|
|
75
77
|
"@types/turndown": "^5.0.5",
|
|
76
78
|
"esbuild": "^0.27.3",
|
|
77
79
|
"tsx": "^4.21.0",
|
|
78
|
-
"typescript": "^5.7.0"
|
|
80
|
+
"typescript": "^5.7.0",
|
|
81
|
+
"vitest": "^4.0.18"
|
|
79
82
|
}
|
|
80
83
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: context-mode
|
|
3
3
|
description: |
|
|
4
|
-
Use context-mode tools (
|
|
4
|
+
Use context-mode tools (ctx_execute, ctx_execute_file) instead of Bash/cat when processing
|
|
5
5
|
large outputs. Trigger phrases: "analyze logs", "summarize output", "process data",
|
|
6
6
|
"parse JSON", "filter results", "extract errors", "check build output",
|
|
7
7
|
"analyze dependencies", "process API response", "large file analysis",
|
|
@@ -37,7 +37,7 @@ Bash whitelist (safe to run directly):
|
|
|
37
37
|
- **Package management**: `npm install`, `npm publish`, `pip install`
|
|
38
38
|
- **Simple output**: `echo`, `printf`
|
|
39
39
|
|
|
40
|
-
**Everything else → `
|
|
40
|
+
**Everything else → `ctx_execute` or `ctx_execute_file`.** Any command that reads, queries, fetches, lists, logs, tests, builds, diffs, inspects, or calls an external service. This includes ALL CLIs (gh, aws, kubectl, docker, terraform, wrangler, fly, heroku, gcloud, etc.) — there are thousands and we cannot list them all.
|
|
41
41
|
|
|
42
42
|
**When uncertain, use context-mode.** Every KB of unnecessary context reduces the quality and speed of the entire session.
|
|
43
43
|
|
|
@@ -50,16 +50,16 @@ About to run a command / read a file / call an API?
|
|
|
50
50
|
│ └── Use Bash
|
|
51
51
|
│
|
|
52
52
|
├── Output MIGHT be large or you're UNSURE?
|
|
53
|
-
│ └── Use context-mode
|
|
53
|
+
│ └── Use context-mode ctx_execute or ctx_execute_file
|
|
54
54
|
│
|
|
55
55
|
├── Fetching web documentation or HTML page?
|
|
56
|
-
│ └── Use
|
|
56
|
+
│ └── Use ctx_fetch_and_index → ctx_search
|
|
57
57
|
│
|
|
58
58
|
├── Using Playwright (navigate, snapshot, console, network)?
|
|
59
59
|
│ └── ALWAYS use filename parameter to save to file, then:
|
|
60
|
-
│ browser_snapshot(filename) →
|
|
61
|
-
│ browser_console_messages(filename) →
|
|
62
|
-
│ browser_network_requests(filename) →
|
|
60
|
+
│ browser_snapshot(filename) → ctx_index(path) or ctx_execute_file(path)
|
|
61
|
+
│ browser_console_messages(filename) → ctx_execute_file(path)
|
|
62
|
+
│ browser_network_requests(filename) → ctx_execute_file(path)
|
|
63
63
|
│ ⚠ browser_navigate returns a snapshot automatically — ignore it,
|
|
64
64
|
│ use browser_snapshot(filename) for any inspection.
|
|
65
65
|
│ ⚠ Playwright MCP uses a SINGLE browser instance — NOT parallel-safe.
|
|
@@ -74,34 +74,34 @@ About to run a command / read a file / call an API?
|
|
|
74
74
|
│
|
|
75
75
|
├── Processing output from another MCP tool (Context7, GitHub API, etc.)?
|
|
76
76
|
│ ├── Output already in context from a previous tool call?
|
|
77
|
-
│ │ └── Use it directly. Do NOT re-index with
|
|
77
|
+
│ │ └── Use it directly. Do NOT re-index with ctx_index(content: ...).
|
|
78
78
|
│ ├── Need to search the output multiple times?
|
|
79
|
-
│ │ └── Save to file via
|
|
79
|
+
│ │ └── Save to file via ctx_execute, then ctx_index(path) → ctx_search
|
|
80
80
|
│ └── One-shot extraction?
|
|
81
|
-
│ └── Save to file via
|
|
81
|
+
│ └── Save to file via ctx_execute, then ctx_execute_file(path)
|
|
82
82
|
│
|
|
83
83
|
└── Reading a file to analyze/summarize (not edit)?
|
|
84
|
-
└── Use
|
|
84
|
+
└── Use ctx_execute_file (file loads into FILE_CONTENT, not context)
|
|
85
85
|
```
|
|
86
86
|
|
|
87
87
|
## When to Use Each Tool
|
|
88
88
|
|
|
89
89
|
| Situation | Tool | Example |
|
|
90
90
|
|-----------|------|---------|
|
|
91
|
-
| Hit an API endpoint | `
|
|
92
|
-
| Run CLI that returns data | `
|
|
93
|
-
| Run tests | `
|
|
94
|
-
| Git operations | `
|
|
95
|
-
| Docker/K8s inspection | `
|
|
96
|
-
| Read a log file | `
|
|
97
|
-
| Read a data file | `
|
|
98
|
-
| Read source code to analyze | `
|
|
99
|
-
| Fetch web docs | `
|
|
100
|
-
| Playwright snapshot | `browser_snapshot(filename)` → `
|
|
101
|
-
| Playwright snapshot (one-shot) | `browser_snapshot(filename)` → `
|
|
102
|
-
| Playwright console/network | `browser_*(filename)` → `
|
|
91
|
+
| Hit an API endpoint | `ctx_execute` | `fetch('http://localhost:3000/api/orders')` |
|
|
92
|
+
| Run CLI that returns data | `ctx_execute` | `gh pr list`, `aws s3 ls`, `kubectl get pods` |
|
|
93
|
+
| Run tests | `ctx_execute` | `npm test`, `pytest`, `go test ./...` |
|
|
94
|
+
| Git operations | `ctx_execute` | `git log --oneline -50`, `git diff HEAD~5` |
|
|
95
|
+
| Docker/K8s inspection | `ctx_execute` | `docker stats --no-stream`, `kubectl describe pod` |
|
|
96
|
+
| Read a log file | `ctx_execute_file` | Parse access.log, error.log, build output |
|
|
97
|
+
| Read a data file | `ctx_execute_file` | Analyze CSV, JSON, YAML, XML |
|
|
98
|
+
| Read source code to analyze | `ctx_execute_file` | Count functions, find patterns, extract metrics |
|
|
99
|
+
| Fetch web docs | `ctx_fetch_and_index` | Index React/Next.js/Zod docs, then search |
|
|
100
|
+
| Playwright snapshot | `browser_snapshot(filename)` → `ctx_index(path)` → `ctx_search` | Save to file, index server-side, query |
|
|
101
|
+
| Playwright snapshot (one-shot) | `browser_snapshot(filename)` → `ctx_execute_file(path)` | Save to file, extract in sandbox |
|
|
102
|
+
| Playwright console/network | `browser_*(filename)` → `ctx_execute_file(path)` | Save to file, analyze in sandbox |
|
|
103
103
|
| MCP output (already in context) | Use directly | Don't re-index — it's already loaded |
|
|
104
|
-
| MCP output (need multi-query) | `
|
|
104
|
+
| MCP output (need multi-query) | `ctx_execute` to save → `ctx_index(path)` → `ctx_search` | Save to file first, index server-side |
|
|
105
105
|
|
|
106
106
|
## Automatic Triggers
|
|
107
107
|
|
|
@@ -134,12 +134,12 @@ Use context-mode for ANY of these, without being asked:
|
|
|
134
134
|
- **Always use `source` parameter** when multiple docs are indexed to avoid cross-source contamination
|
|
135
135
|
- Partial match works: `source: "Node"` matches `"Node.js v22 CHANGELOG"`
|
|
136
136
|
- **Always use `queries` array** — batch ALL search questions in ONE call:
|
|
137
|
-
- `
|
|
138
|
-
- NEVER make multiple separate
|
|
137
|
+
- `ctx_search(queries: ["transform pipe", "refine superRefine", "coerce codec"], source: "Zod")`
|
|
138
|
+
- NEVER make multiple separate ctx_search() calls — put all queries in one array
|
|
139
139
|
|
|
140
140
|
## External Documentation
|
|
141
141
|
|
|
142
|
-
- **Always use `
|
|
142
|
+
- **Always use `ctx_fetch_and_index`** for external docs — NEVER `cat` or `ctx_execute` with local paths for packages you don't own
|
|
143
143
|
- For GitHub-hosted projects, use the raw URL: `https://raw.githubusercontent.com/org/repo/main/CHANGELOG.md`
|
|
144
144
|
- After indexing, use the `source` parameter in search to scope results to that specific document
|
|
145
145
|
|
|
@@ -150,7 +150,7 @@ Use context-mode for ANY of these, without being asked:
|
|
|
150
150
|
3. **Be specific in output.** Print bug details with IDs, line numbers, exact values — not just counts.
|
|
151
151
|
4. **For files you need to EDIT**: Use the normal Read tool. context-mode is for analysis, not editing.
|
|
152
152
|
5. **For Bash whitelist commands only**: Use Bash for file mutations, git writes, navigation, process control, package install, and echo. Everything else goes through context-mode.
|
|
153
|
-
6. **Never use `
|
|
153
|
+
6. **Never use `ctx_index(content: large_data)`.** Use `ctx_index(path: ...)` to read files server-side. The `content` parameter sends data through context as a tool parameter — use it only for small inline text.
|
|
154
154
|
7. **Always use `filename` parameter** on Playwright tools (`browser_snapshot`, `browser_console_messages`, `browser_network_requests`). Without it, the full output enters context.
|
|
155
155
|
8. **Don't re-index data already in context.** If an MCP tool returned data in a previous response, it's already loaded — use it directly or save to file first.
|
|
156
156
|
|
|
@@ -162,7 +162,7 @@ Use context-mode for ANY of these, without being asked:
|
|
|
162
162
|
NEVER return large raw datasets directly to context.
|
|
163
163
|
</critical_rule>
|
|
164
164
|
<workflow>
|
|
165
|
-
LargeDataTool(filename: "path") → mcp__context-
|
|
165
|
+
LargeDataTool(filename: "path") → mcp__context-mode__ctx_index(path: "path") → ctx_search()
|
|
166
166
|
</workflow>
|
|
167
167
|
</sandboxed_data_workflow>
|
|
168
168
|
|
|
@@ -200,7 +200,7 @@ gh pr list --json number,title,state,reviewDecision --jq '.[] | "\(.number) [\(.
|
|
|
200
200
|
|
|
201
201
|
### Read and analyze a large file
|
|
202
202
|
```python
|
|
203
|
-
# FILE_CONTENT is pre-loaded by
|
|
203
|
+
# FILE_CONTENT is pre-loaded by ctx_execute_file
|
|
204
204
|
import json
|
|
205
205
|
data = json.loads(FILE_CONTENT)
|
|
206
206
|
print(f"Records: {len(data)}")
|
|
@@ -211,9 +211,9 @@ print(f"Records: {len(data)}")
|
|
|
211
211
|
|
|
212
212
|
**When a task involves Playwright snapshots, screenshots, or page inspection, ALWAYS route through file → sandbox.**
|
|
213
213
|
|
|
214
|
-
Playwright `browser_snapshot` returns 10K–135K tokens of accessibility tree data. Calling it without `filename` dumps all of that into context. Passing the output to `
|
|
214
|
+
Playwright `browser_snapshot` returns 10K–135K tokens of accessibility tree data. Calling it without `filename` dumps all of that into context. Passing the output to `ctx_index(content: ...)` sends it into context a SECOND time as a parameter. Both are wrong.
|
|
215
215
|
|
|
216
|
-
**The key insight**: `browser_snapshot` has a `filename` parameter that saves to file instead of returning to context. `
|
|
216
|
+
**The key insight**: `browser_snapshot` has a `filename` parameter that saves to file instead of returning to context. `ctx_index` has a `path` parameter that reads files server-side. `ctx_execute_file` processes files in a sandbox. **None of these touch context.**
|
|
217
217
|
|
|
218
218
|
### Workflow A: Snapshot → File → Index → Search (multiple queries)
|
|
219
219
|
|
|
@@ -221,10 +221,10 @@ Playwright `browser_snapshot` returns 10K–135K tokens of accessibility tree da
|
|
|
221
221
|
Step 1: browser_snapshot(filename: "/tmp/playwright-snapshot.md")
|
|
222
222
|
→ saves to file, returns ~50B confirmation (NOT 135K tokens)
|
|
223
223
|
|
|
224
|
-
Step 2:
|
|
224
|
+
Step 2: ctx_index(path: "/tmp/playwright-snapshot.md", source: "Playwright snapshot")
|
|
225
225
|
→ reads file SERVER-SIDE, indexes into FTS5, returns ~80B confirmation
|
|
226
226
|
|
|
227
|
-
Step 3:
|
|
227
|
+
Step 3: ctx_search(queries: ["login form email password"], source: "Playwright")
|
|
228
228
|
→ returns only matching chunks (~300B)
|
|
229
229
|
```
|
|
230
230
|
|
|
@@ -236,7 +236,7 @@ Step 3: search(queries: ["login form email password"], source: "Playwright")
|
|
|
236
236
|
Step 1: browser_snapshot(filename: "/tmp/playwright-snapshot.md")
|
|
237
237
|
→ saves to file, returns ~50B confirmation
|
|
238
238
|
|
|
239
|
-
Step 2:
|
|
239
|
+
Step 2: ctx_execute_file(path: "/tmp/playwright-snapshot.md", language: "javascript", code: "
|
|
240
240
|
const links = [...FILE_CONTENT.matchAll(/- link \"([^\"]+)\"/g)].map(m => m[1]);
|
|
241
241
|
const buttons = [...FILE_CONTENT.matchAll(/- button \"([^\"]+)\"/g)].map(m => m[1]);
|
|
242
242
|
const inputs = [...FILE_CONTENT.matchAll(/- textbox|- checkbox|- radio/g)];
|
|
@@ -252,10 +252,10 @@ Step 2: execute_file(path: "/tmp/playwright-snapshot.md", language: "javascript"
|
|
|
252
252
|
|
|
253
253
|
```
|
|
254
254
|
browser_console_messages(level: "error", filename: "/tmp/console.md")
|
|
255
|
-
→
|
|
255
|
+
→ ctx_execute_file(path: "/tmp/console.md", ...) or ctx_index(path: "/tmp/console.md", ...)
|
|
256
256
|
|
|
257
257
|
browser_network_requests(includeStatic: false, filename: "/tmp/network.md")
|
|
258
|
-
→
|
|
258
|
+
→ ctx_execute_file(path: "/tmp/network.md", ...) or ctx_index(path: "/tmp/network.md", ...)
|
|
259
259
|
```
|
|
260
260
|
|
|
261
261
|
### CRITICAL: Why `filename` + `path` is mandatory
|
|
@@ -263,16 +263,16 @@ browser_network_requests(includeStatic: false, filename: "/tmp/network.md")
|
|
|
263
263
|
| Approach | Context cost | Correct? |
|
|
264
264
|
|----------|-------------|----------|
|
|
265
265
|
| `browser_snapshot()` → raw into context | **135K tokens** | NO |
|
|
266
|
-
| `browser_snapshot()` → `
|
|
267
|
-
| `browser_snapshot(filename)` → `
|
|
268
|
-
| `browser_snapshot(filename)` → `
|
|
266
|
+
| `browser_snapshot()` → `ctx_index(content: raw)` | **270K tokens** (doubled!) | NO |
|
|
267
|
+
| `browser_snapshot(filename)` → `ctx_index(path)` → `ctx_search` | **~430B** | YES |
|
|
268
|
+
| `browser_snapshot(filename)` → `ctx_execute_file(path)` | **~250B** | YES |
|
|
269
269
|
|
|
270
270
|
### Key Rule
|
|
271
271
|
|
|
272
272
|
> **ALWAYS use `filename` parameter when calling `browser_snapshot`, `browser_console_messages`, or `browser_network_requests`.**
|
|
273
|
-
> Then process via `
|
|
273
|
+
> Then process via `ctx_index(path: ...)` or `ctx_execute_file(path: ...)` — never `ctx_index(content: ...)`.
|
|
274
274
|
>
|
|
275
|
-
> Data flow: **Playwright → file → server-side read → context**. Never: **Playwright → context →
|
|
275
|
+
> Data flow: **Playwright → file → server-side read → context**. Never: **Playwright → context → ctx_index(content) → context again**.
|
|
276
276
|
|
|
277
277
|
## Subagent Usage
|
|
278
278
|
|
|
@@ -280,15 +280,15 @@ Subagents automatically receive context-mode tool routing via a PreToolUse hook.
|
|
|
280
280
|
|
|
281
281
|
## Anti-Patterns
|
|
282
282
|
|
|
283
|
-
- Using `curl http://api/endpoint` via Bash → 50KB floods context. Use `
|
|
284
|
-
- Using `cat large-file.json` via Bash → entire file in context. Use `
|
|
285
|
-
- Using `gh pr list` via Bash → raw JSON in context. Use `
|
|
286
|
-
- Piping Bash output through `| head -20` → you lose the rest. Use `
|
|
287
|
-
- Running `npm test` via Bash → full test output in context. Use `
|
|
283
|
+
- Using `curl http://api/endpoint` via Bash → 50KB floods context. Use `ctx_execute` with fetch instead.
|
|
284
|
+
- Using `cat large-file.json` via Bash → entire file in context. Use `ctx_execute_file` instead.
|
|
285
|
+
- Using `gh pr list` via Bash → raw JSON in context. Use `ctx_execute` with `--jq` filter instead.
|
|
286
|
+
- Piping Bash output through `| head -20` → you lose the rest. Use `ctx_execute` to analyze ALL data and print summary.
|
|
287
|
+
- Running `npm test` via Bash → full test output in context. Use `ctx_execute` to capture and summarize.
|
|
288
288
|
- Calling `browser_snapshot()` WITHOUT `filename` parameter → 135K tokens flood context. **Always** use `browser_snapshot(filename: "/tmp/snap.md")`.
|
|
289
289
|
- Calling `browser_console_messages()` or `browser_network_requests()` WITHOUT `filename` → entire output floods context. **Always** use the `filename` parameter.
|
|
290
|
-
- Passing ANY large data to `
|
|
291
|
-
- Calling an MCP tool (Context7 `query-docs`, GitHub API, etc.) then passing the response to `
|
|
290
|
+
- Passing ANY large data to `ctx_index(content: ...)` → data enters context as a parameter. **Always** use `ctx_index(path: ...)` to read server-side. The `content` parameter should only be used for small inline text you're composing yourself.
|
|
291
|
+
- Calling an MCP tool (Context7 `query-docs`, GitHub API, etc.) then passing the response to `ctx_index(content: response)` → **doubles** context usage. The response is already in context — use it directly or save to file first.
|
|
292
292
|
- Ignoring `browser_navigate` auto-snapshot → navigation response includes a full page snapshot. Don't rely on it for inspection — call `browser_snapshot(filename)` separately.
|
|
293
293
|
|
|
294
294
|
## Reference Files
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: doctor
|
|
2
|
+
name: ctx-doctor
|
|
3
3
|
description: |
|
|
4
4
|
Run context-mode diagnostics. Checks runtimes, hooks, FTS5,
|
|
5
5
|
plugin registration, npm and marketplace versions.
|
|
6
|
-
Trigger: /context-mode:doctor
|
|
6
|
+
Trigger: /context-mode:ctx-doctor
|
|
7
7
|
user_invocable: true
|
|
8
8
|
---
|
|
9
9
|
|
|
@@ -13,7 +13,7 @@ Run diagnostics and display results directly in the conversation.
|
|
|
13
13
|
|
|
14
14
|
## Instructions
|
|
15
15
|
|
|
16
|
-
1. Derive the **plugin root** from this skill's base directory (go up 2 levels — remove `/skills/doctor`).
|
|
16
|
+
1. Derive the **plugin root** from this skill's base directory (go up 2 levels — remove `/skills/ctx-doctor`).
|
|
17
17
|
2. Run with Bash:
|
|
18
18
|
```
|
|
19
19
|
npx tsx "<PLUGIN_ROOT>/src/cli.ts" doctor
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: stats
|
|
2
|
+
name: ctx-stats
|
|
3
3
|
description: |
|
|
4
4
|
Show how much context window context-mode saved this session.
|
|
5
5
|
Displays token consumption, context savings ratio, and per-tool breakdown.
|
|
6
|
-
Trigger: /context-mode:stats
|
|
6
|
+
Trigger: /context-mode:ctx-stats
|
|
7
7
|
user_invocable: true
|
|
8
8
|
---
|
|
9
9
|
|
|
@@ -13,7 +13,7 @@ Show context savings for the current session.
|
|
|
13
13
|
|
|
14
14
|
## Instructions
|
|
15
15
|
|
|
16
|
-
1. Call the `mcp__context-
|
|
16
|
+
1. Call the `mcp__context-mode__ctx_stats` MCP tool (no parameters needed).
|
|
17
17
|
2. **CRITICAL**: You MUST copy-paste the ENTIRE tool output as markdown text directly into your response message. Do NOT summarize, do NOT collapse, do NOT paraphrase. The user must see the full tables without pressing ctrl+o. Copy every line exactly as returned by the tool.
|
|
18
18
|
3. After the full output, add ONE sentence highlighting the key savings metric, e.g.:
|
|
19
19
|
- "context-mode saved **12.4x** — 92% of data stayed in sandbox."
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: upgrade
|
|
2
|
+
name: ctx-upgrade
|
|
3
3
|
description: |
|
|
4
4
|
Update context-mode from GitHub and fix hooks/settings.
|
|
5
5
|
Pulls latest, builds, installs, updates npm global, configures hooks.
|
|
6
|
-
Trigger: /context-mode:upgrade
|
|
6
|
+
Trigger: /context-mode:ctx-upgrade
|
|
7
7
|
user_invocable: true
|
|
8
8
|
---
|
|
9
9
|
|
|
@@ -13,7 +13,7 @@ Pull latest from GitHub and reinstall the plugin.
|
|
|
13
13
|
|
|
14
14
|
## Instructions
|
|
15
15
|
|
|
16
|
-
1. Derive the **plugin root** from this skill's base directory (go up 2 levels — remove `/skills/upgrade`).
|
|
16
|
+
1. Derive the **plugin root** from this skill's base directory (go up 2 levels — remove `/skills/ctx-upgrade`).
|
|
17
17
|
2. Run with Bash:
|
|
18
18
|
```
|
|
19
19
|
node "<PLUGIN_ROOT>/build/cli.js" upgrade
|