context-mode 0.4.0 → 0.5.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.
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "claude-context-mode",
3
+ "owner": {
4
+ "name": "Mert Koseoğlu",
5
+ "email": "code.bm.ksglu@gmail.com"
6
+ },
7
+ "metadata": {
8
+ "description": "Claude Code plugins by Mert Koseoğlu",
9
+ "version": "1.0.0"
10
+ },
11
+ "plugins": [
12
+ {
13
+ "name": "context-mode",
14
+ "source": "./",
15
+ "description": "Claude Code MCP plugin that saves 94% of your context window. Sandboxed code execution in 10 languages, FTS5 knowledge base with BM25 ranking, and smart truncation.",
16
+ "version": "0.5.0",
17
+ "author": {
18
+ "name": "Mert Koseoğlu"
19
+ },
20
+ "category": "development",
21
+ "keywords": [
22
+ "mcp",
23
+ "context-window",
24
+ "sandbox",
25
+ "code-execution",
26
+ "fts5",
27
+ "bm25",
28
+ "playwright",
29
+ "context7"
30
+ ]
31
+ }
32
+ ]
33
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "context-mode",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Claude Code MCP plugin that saves 94% of your context window. Sandboxed code execution in 10 languages, FTS5 knowledge base with BM25 ranking, and smart truncation.",
5
5
  "author": {
6
6
  "name": "Mert Koseoğlu",
package/README.md CHANGED
@@ -10,16 +10,16 @@ Context Mode intercepts these operations, processes data in isolated subprocesse
10
10
 
11
11
  Claude Code has a 200K token context window. Here's how fast popular MCP servers eat through it:
12
12
 
13
- | MCP Server | Tool | Output per Call | Source |
14
- |---|---|---|---|
15
- | **Playwright** | `browser_snapshot` | 10K-135K tokens (50-540 KB) | [playwright-mcp#1233](https://github.com/microsoft/playwright-mcp/issues/1233) |
16
- | **Context7** | `query-docs` | 4K-10K tokens per query | [upstash/context7](https://github.com/upstash/context7) |
17
- | **GitHub** | `list_commits` (30) | 29K-64K tokens | [github-mcp-server#142](https://github.com/github/github-mcp-server/issues/142) |
18
- | **Sentry** | full mode tools | 14K tokens (definitions only) | [getsentry/sentry-mcp](https://github.com/getsentry/sentry-mcp) |
19
- | **Supabase** | database tools | 4.2K tokens (definitions only) | [supabase-community/supabase-mcp](https://github.com/supabase-community/supabase-mcp) |
20
- | **Firecrawl** | `scrape` / `crawl` | 5K-50K+ tokens per page | [firecrawl](https://github.com/mendableai/firecrawl) |
21
- | **Chrome DevTools** | all tools | 17K tokens (definitions only) | Community benchmark |
22
- | **Fetch** | `fetch` | 5K-50K tokens per page | Official reference server |
13
+ | MCP Server | Tool | Without Context Mode | With Context Mode | Savings | Source |
14
+ |---|---|---|---|---|---|
15
+ | **Playwright** | `browser_snapshot` | 10K-135K tokens | ~20 tokens | **99%** | [playwright-mcp#1233](https://github.com/microsoft/playwright-mcp/issues/1233) |
16
+ | **Context7** | `query-docs` | 4K-10K tokens | ~70 tokens | **98%** | [upstash/context7](https://github.com/upstash/context7) |
17
+ | **GitHub** | `list_commits` (30) | 29K-64K tokens | ~10 tokens | **99%** | [github-mcp-server#142](https://github.com/github/github-mcp-server/issues/142) |
18
+ | **Sentry** | issue analysis | 5K-30K tokens | ~25 tokens | **99%** | [getsentry/sentry-mcp](https://github.com/getsentry/sentry-mcp) |
19
+ | **Supabase** | schema queries | 2K-30K tokens | ~30 tokens | **99%** | [supabase-community/supabase-mcp](https://github.com/supabase-community/supabase-mcp) |
20
+ | **Firecrawl** | `scrape` / `crawl` | 5K-50K+ tokens | ~70 tokens | **99%** | [firecrawl](https://github.com/mendableai/firecrawl) |
21
+ | **Chrome DevTools** | DOM / network | 5K-50K+ tokens | ~25 tokens | **99%** | Community benchmark |
22
+ | **Fetch** | `fetch` | 5K-50K tokens | ~70 tokens | **99%** | Official reference server |
23
23
 
24
24
  **Real measurement** ([Scott Spence, 2025](https://scottspence.com/posts/optimising-mcp-server-context-usage-in-claude-code)): With 81+ MCP tools enabled across multiple servers, **143K of 200K tokens (72%) consumed** — 82K tokens just for MCP tool definitions. Only 28% left for actual work.
25
25
 
@@ -44,10 +44,11 @@ Claude Code has a 200K token context window. Here's how fast popular MCP servers
44
44
  ### Option 1: Claude Code Plugin (Recommended)
45
45
 
46
46
  ```bash
47
- /plugin install context-mode@claude-plugin-directory
47
+ /plugin marketplace add mksglu/claude-context-mode
48
+ /plugin install context-mode@claude-context-mode
48
49
  ```
49
50
 
50
- Installs as a Claude Code plugin with skills and MCP server bundled together.
51
+ Installs as a Claude Code plugin with MCP server + skills bundled. The skill automatically guides Claude to route large outputs through Context Mode.
51
52
 
52
53
  ### Option 2: MCP Server Only
53
54
 
@@ -57,6 +58,12 @@ claude mcp add context-mode -- npx -y context-mode
57
58
 
58
59
  Restart Claude Code. 5 tools are now available.
59
60
 
61
+ ### Option 3: Local Development
62
+
63
+ ```bash
64
+ claude --plugin-dir ./path/to/context-mode
65
+ ```
66
+
60
67
  ## Tools
61
68
 
62
69
  ### `execute` — Run Code in Sandbox
@@ -68,11 +75,22 @@ Claude calls: execute({ language: "shell", code: "gh pr list --json title,state
68
75
  Returns: "3" ← 2 bytes instead of 8KB JSON
69
76
  ```
70
77
 
78
+ **Intent-driven search** (v0.5.0): When you provide an `intent` parameter and output exceeds 5KB, Context Mode uses BM25 search to return only the relevant sections — instead of blind head/tail truncation.
79
+
80
+ ```
81
+ Claude calls: execute({
82
+ language: "shell",
83
+ code: "cat /var/log/app.log",
84
+ intent: "connection refused database error"
85
+ })
86
+ Returns: only the 3 matching log sections (1.5KB) ← instead of 100KB truncated log
87
+ ```
88
+
71
89
  Authenticated CLIs work out of the box — `gh`, `aws`, `gcloud`, `kubectl`, `docker` credentials are passed through securely. Bun auto-detected for 3-5x faster JS/TS.
72
90
 
73
91
  ### `execute_file` — Process Files Without Loading
74
92
 
75
- File contents never enter context. The file is read into a `FILE_CONTENT` variable inside the sandbox.
93
+ File contents never enter context. The file is read into a `FILE_CONTENT` variable inside the sandbox. Also supports `intent` parameter for intent-driven search on large outputs.
76
94
 
77
95
  ```
78
96
  Claude calls: execute_file({ path: "access.log", language: "python", code: "..." })
@@ -207,6 +225,31 @@ Tail (40%): Final output with errors/results
207
225
 
208
226
  Line-boundary snapping — never cuts mid-line. Error messages at the bottom are always preserved.
209
227
 
228
+ ### Intent-Driven Search (v0.5.0)
229
+
230
+ When `execute` or `execute_file` is called with an `intent` parameter and output exceeds 5KB, Context Mode replaces blind truncation with intelligent BM25 search:
231
+
232
+ ```
233
+ Traditional truncation:
234
+ stdout (100KB) → head(60%) + tail(40%) → ~100KB in context
235
+ Problem: relevant info in the middle is lost
236
+
237
+ Intent-driven search:
238
+ stdout (100KB) → chunk by lines → in-memory FTS5 → search(intent) → 2-5KB relevant sections
239
+ Result: only what you need enters context
240
+ ```
241
+
242
+ Tested across 4 real-world scenarios:
243
+
244
+ | Scenario | Smart Truncation | Intent Search | Intent Size | Truncation Size |
245
+ |---|---|---|---|---|
246
+ | Server log error (line 347/500) | **missed** | **found** | 1.5 KB | 5.0 KB |
247
+ | 3 test failures among 200 tests | found 2/3 | **found 3/3** | 2.4 KB | 5.0 KB |
248
+ | 2 build warnings among 300 lines | **missed both** | **found both** | 2.1 KB | 5.0 KB |
249
+ | API auth error (line 743/1000) | **missed** | **found** | 1.2 KB | 4.9 KB |
250
+
251
+ Smart truncation fails on 3 of 4 scenarios because relevant content is in the dropped middle section. Intent search finds the target every time while using 50-75% fewer bytes.
252
+
210
253
  ### HTML to Markdown Conversion
211
254
 
212
255
  `fetch_and_index` converts HTML in a subprocess (raw HTML never enters context):
@@ -264,6 +307,72 @@ Typical 45-minute debugging session:
264
307
  | Source code to edit | Plain `Read` tool | Need full content for edits |
265
308
  | Small files (<20 lines) | Plain `Read` tool | Minimal overhead |
266
309
 
310
+ ## Example Prompts
311
+
312
+ Just ask naturally — Claude automatically routes through Context Mode when it saves tokens.
313
+
314
+ ### Git & GitHub
315
+
316
+ ```
317
+ "Analyze the last 50 commits and find the most frequently changed files"
318
+ "List all open PRs on this repo and summarize their status"
319
+ "Show contributors ranked by commit count this month"
320
+ "Find all commits that touched the auth module in the last 30 days"
321
+ ```
322
+
323
+ ### Code Analysis
324
+
325
+ ```
326
+ "Analyze all TypeScript files in src/ and report function counts per file"
327
+ "Find all TODO and FIXME comments across the codebase"
328
+ "Count lines of code per language in this project"
329
+ "List all exported functions from src/utils/ and their parameter signatures"
330
+ ```
331
+
332
+ ### Logs & Debugging
333
+
334
+ ```
335
+ "Read the access log and break down requests by HTTP status code"
336
+ "Find the top 10 slowest API endpoints from the request log"
337
+ "Parse the error log and group exceptions by type with frequency"
338
+ "Analyze the build output and list all warnings with file locations"
339
+ ```
340
+
341
+ ### Test & CI
342
+
343
+ ```
344
+ "Run the test suite and give me a pass/fail summary"
345
+ "Analyze test coverage output and find untested files"
346
+ "Check which tests have been flaky in the last 10 CI runs"
347
+ ```
348
+
349
+ ### Data & Config
350
+
351
+ ```
352
+ "Analyze package-lock.json and find the 10 largest dependencies by size"
353
+ "Parse the CSV export and compute average response time per endpoint"
354
+ "Read the Kubernetes manifests and summarize resource limits per pod"
355
+ "Compare tsconfig.json across packages in this monorepo"
356
+ ```
357
+
358
+ ### Documentation Lookup
359
+
360
+ ```
361
+ "Fetch the React useEffect docs and find the cleanup pattern"
362
+ "Index the Next.js App Router documentation and search for loading states"
363
+ "Look up the Zod docs and find string validation examples"
364
+ "Fetch the Tailwind docs and search for responsive breakpoint utilities"
365
+ ```
366
+
367
+ ### Cloud & Infrastructure
368
+
369
+ ```
370
+ "List all S3 buckets and their sizes using AWS CLI"
371
+ "Show running Kubernetes pods and their restart counts"
372
+ "List all Docker containers with their memory and CPU usage"
373
+ "Check the status of all Cloudflare Workers in this account"
374
+ ```
375
+
267
376
  ## Requirements
268
377
 
269
378
  - **Node.js 18+**
@@ -279,12 +388,13 @@ Typical 45-minute debugging session:
279
388
 
280
389
  ## Test Suite
281
390
 
282
- 113 tests across 3 suites:
391
+ 99+ tests across 4 suites:
283
392
 
284
393
  | Suite | Tests | Coverage |
285
394
  |---|---|---|
286
395
  | Executor | 55 | 10 languages, sandbox, truncation, concurrency, timeouts |
287
- | ContentStore | 34 | FTS5 schema, BM25 ranking, chunking, stemming, fixtures |
396
+ | ContentStore | 40 | FTS5 schema, BM25 ranking, chunking, stemming, plain text indexing |
397
+ | Intent Search | 4 | Smart truncation vs intent-driven search across 4 real-world scenarios |
288
398
  | MCP Integration | 24 | JSON-RPC protocol, all 5 tools, fetch_and_index, errors |
289
399
 
290
400
  ## Development
@@ -295,8 +405,8 @@ cd claude-context-mode
295
405
  npm install
296
406
  npm run build
297
407
  npm test # executor (55 tests)
298
- npm run test:store # FTS5/BM25 (34 tests)
299
- npm run test:all # all suites (113 tests)
408
+ npm run test:store # FTS5/BM25 (40 tests)
409
+ npm run test:all # all suites (99+ tests)
300
410
  ```
301
411
 
302
412
  ## License
package/build/server.js CHANGED
@@ -9,7 +9,7 @@ const runtimes = detectRuntimes();
9
9
  const available = getAvailableLanguages(runtimes);
10
10
  const server = new McpServer({
11
11
  name: "context-mode",
12
- version: "0.4.0",
12
+ version: "0.5.0",
13
13
  });
14
14
  const executor = new PolyglotExecutor({ runtimes });
15
15
  // Lazy singleton — no DB overhead unless index/search is used
@@ -53,8 +53,14 @@ server.registerTool("execute", {
53
53
  .optional()
54
54
  .default(30000)
55
55
  .describe("Max execution time in ms"),
56
+ intent: z
57
+ .string()
58
+ .optional()
59
+ .describe("What you're looking for in the output. When provided and output is large (>5KB), " +
60
+ "returns only matching sections via BM25 search instead of truncated output. " +
61
+ "Example: 'find failing tests', 'HTTP 500 errors', 'memory usage statistics'."),
56
62
  }),
57
- }, async ({ language, code, timeout }) => {
63
+ }, async ({ language, code, timeout, intent }) => {
58
64
  try {
59
65
  const result = await executor.execute({ language, code, timeout });
60
66
  if (result.timedOut) {
@@ -69,19 +75,34 @@ server.registerTool("execute", {
69
75
  };
70
76
  }
71
77
  if (result.exitCode !== 0) {
78
+ const output = `Exit code: ${result.exitCode}\n\nstdout:\n${result.stdout}\n\nstderr:\n${result.stderr}`;
79
+ if (intent && intent.trim().length > 0 && Buffer.byteLength(output) > INTENT_SEARCH_THRESHOLD) {
80
+ return {
81
+ content: [
82
+ { type: "text", text: intentSearch(output, intent) },
83
+ ],
84
+ isError: true,
85
+ };
86
+ }
72
87
  return {
73
88
  content: [
74
- {
75
- type: "text",
76
- text: `Exit code: ${result.exitCode}\n\nstdout:\n${result.stdout}\n\nstderr:\n${result.stderr}`,
77
- },
89
+ { type: "text", text: output },
78
90
  ],
79
91
  isError: true,
80
92
  };
81
93
  }
94
+ const stdout = result.stdout || "(no output)";
95
+ // Intent-driven search: if intent provided and output is large enough
96
+ if (intent && intent.trim().length > 0 && Buffer.byteLength(stdout) > INTENT_SEARCH_THRESHOLD) {
97
+ return {
98
+ content: [
99
+ { type: "text", text: intentSearch(stdout, intent) },
100
+ ],
101
+ };
102
+ }
82
103
  return {
83
104
  content: [
84
- { type: "text", text: result.stdout || "(no output)" },
105
+ { type: "text", text: stdout },
85
106
  ],
86
107
  };
87
108
  }
@@ -111,6 +132,36 @@ function indexStdout(stdout, source) {
111
132
  };
112
133
  }
113
134
  // ─────────────────────────────────────────────────────────
135
+ // Helper: intent-driven search on execution output
136
+ // ─────────────────────────────────────────────────────────
137
+ const INTENT_SEARCH_THRESHOLD = 5_000; // bytes — ~80-100 lines
138
+ function intentSearch(stdout, intent, maxResults = 5) {
139
+ const store = new ContentStore(":memory:");
140
+ try {
141
+ const totalLines = stdout.split("\n").length;
142
+ const totalBytes = Buffer.byteLength(stdout);
143
+ store.indexPlainText(stdout, "exec-output");
144
+ const results = store.search(intent, maxResults);
145
+ if (results.length === 0) {
146
+ return (`[Intent search: no matches for "${intent}" in ${totalLines}-line output. Returning full output.]\n\n` +
147
+ stdout);
148
+ }
149
+ const totalChunks = store.getStats().chunks;
150
+ const header = `[Intent search: ${results.length} of ${totalChunks} sections matched "${intent}" from ${totalLines}-line output (${(totalBytes / 1024).toFixed(1)}KB)]`;
151
+ const formatted = results
152
+ .map((r, i) => {
153
+ const matchLabel = i === 0 ? " (best match)" : "";
154
+ return `--- ${r.title}${matchLabel} ---\n${r.content}`;
155
+ })
156
+ .join("\n\n");
157
+ const footer = `[Full output: ${totalLines} lines / ${(totalBytes / 1024).toFixed(1)}KB. Re-run without intent to see raw output.]`;
158
+ return `${header}\n\n${formatted}\n\n${footer}`;
159
+ }
160
+ finally {
161
+ store.close();
162
+ }
163
+ }
164
+ // ─────────────────────────────────────────────────────────
114
165
  // Tool: execute_file
115
166
  // ─────────────────────────────────────────────────────────
116
167
  server.registerTool("execute_file", {
@@ -142,8 +193,13 @@ server.registerTool("execute_file", {
142
193
  .optional()
143
194
  .default(30000)
144
195
  .describe("Max execution time in ms"),
196
+ intent: z
197
+ .string()
198
+ .optional()
199
+ .describe("What you're looking for in the output. When provided and output is large (>5KB), " +
200
+ "returns only matching sections via BM25 search instead of truncated output."),
145
201
  }),
146
- }, async ({ path, language, code, timeout }) => {
202
+ }, async ({ path, language, code, timeout, intent }) => {
147
203
  try {
148
204
  const result = await executor.executeFile({
149
205
  path,
@@ -163,19 +219,33 @@ server.registerTool("execute_file", {
163
219
  };
164
220
  }
165
221
  if (result.exitCode !== 0) {
222
+ const output = `Error processing ${path} (exit ${result.exitCode}):\n${result.stderr || result.stdout}`;
223
+ if (intent && intent.trim().length > 0 && Buffer.byteLength(output) > INTENT_SEARCH_THRESHOLD) {
224
+ return {
225
+ content: [
226
+ { type: "text", text: intentSearch(output, intent) },
227
+ ],
228
+ isError: true,
229
+ };
230
+ }
166
231
  return {
167
232
  content: [
168
- {
169
- type: "text",
170
- text: `Error processing ${path} (exit ${result.exitCode}):\n${result.stderr || result.stdout}`,
171
- },
233
+ { type: "text", text: output },
172
234
  ],
173
235
  isError: true,
174
236
  };
175
237
  }
238
+ const stdout = result.stdout || "(no output)";
239
+ if (intent && intent.trim().length > 0 && Buffer.byteLength(stdout) > INTENT_SEARCH_THRESHOLD) {
240
+ return {
241
+ content: [
242
+ { type: "text", text: intentSearch(stdout, intent) },
243
+ ],
244
+ };
245
+ }
176
246
  return {
177
247
  content: [
178
- { type: "text", text: result.stdout || "(no output)" },
248
+ { type: "text", text: stdout },
179
249
  ],
180
250
  };
181
251
  }
package/build/store.d.ts CHANGED
@@ -33,6 +33,12 @@ export declare class ContentStore {
33
33
  path?: string;
34
34
  source?: string;
35
35
  }): IndexResult;
36
+ /**
37
+ * Index plain-text output (logs, build output, test results) by splitting
38
+ * into fixed-size line groups. Unlike markdown indexing, this does not
39
+ * look for headings — it chunks by line count with overlap.
40
+ */
41
+ indexPlainText(content: string, source: string, linesPerChunk?: number): IndexResult;
36
42
  search(query: string, limit?: number): SearchResult[];
37
43
  getStats(): StoreStats;
38
44
  close(): void;
package/build/store.js CHANGED
@@ -94,6 +94,42 @@ export class ContentStore {
94
94
  codeChunks,
95
95
  };
96
96
  }
97
+ // ── Index Plain Text ──
98
+ /**
99
+ * Index plain-text output (logs, build output, test results) by splitting
100
+ * into fixed-size line groups. Unlike markdown indexing, this does not
101
+ * look for headings — it chunks by line count with overlap.
102
+ */
103
+ indexPlainText(content, source, linesPerChunk = 20) {
104
+ if (!content || content.trim().length === 0) {
105
+ const insertSource = this.#db.prepare("INSERT INTO sources (label, chunk_count, code_chunk_count) VALUES (?, 0, 0)");
106
+ const info = insertSource.run(source);
107
+ return {
108
+ sourceId: Number(info.lastInsertRowid),
109
+ label: source,
110
+ totalChunks: 0,
111
+ codeChunks: 0,
112
+ };
113
+ }
114
+ const chunks = this.#chunkPlainText(content, linesPerChunk);
115
+ const insertSource = this.#db.prepare("INSERT INTO sources (label, chunk_count, code_chunk_count) VALUES (?, ?, ?)");
116
+ const insertChunk = this.#db.prepare("INSERT INTO chunks (title, content, source_id, content_type) VALUES (?, ?, ?, ?)");
117
+ const transaction = this.#db.transaction(() => {
118
+ const info = insertSource.run(source, chunks.length, 0);
119
+ const sourceId = Number(info.lastInsertRowid);
120
+ for (const chunk of chunks) {
121
+ insertChunk.run(chunk.title, chunk.content, sourceId, "prose");
122
+ }
123
+ return sourceId;
124
+ });
125
+ const sourceId = transaction();
126
+ return {
127
+ sourceId,
128
+ label: source,
129
+ totalChunks: chunks.length,
130
+ codeChunks: 0,
131
+ };
132
+ }
97
133
  // ── Search ──
98
134
  search(query, limit = 3) {
99
135
  const sanitized = sanitizeQuery(query);
@@ -203,6 +239,41 @@ export class ContentStore {
203
239
  flush();
204
240
  return chunks;
205
241
  }
242
+ #chunkPlainText(text, linesPerChunk) {
243
+ // Try blank-line splitting first for naturally-sectioned output
244
+ const sections = text.split(/\n\s*\n/);
245
+ if (sections.length >= 3 &&
246
+ sections.length <= 200 &&
247
+ sections.every((s) => Buffer.byteLength(s) < 5000)) {
248
+ return sections
249
+ .map((section, i) => ({
250
+ title: `Section ${i + 1}`,
251
+ content: section.trim(),
252
+ }))
253
+ .filter((s) => s.content.length > 0);
254
+ }
255
+ const lines = text.split("\n");
256
+ // Small enough for a single chunk
257
+ if (lines.length <= linesPerChunk) {
258
+ return [{ title: "Output", content: text }];
259
+ }
260
+ // Fixed-size line groups with 2-line overlap
261
+ const chunks = [];
262
+ const overlap = 2;
263
+ const step = Math.max(linesPerChunk - overlap, 1);
264
+ for (let i = 0; i < lines.length; i += step) {
265
+ const slice = lines.slice(i, i + linesPerChunk);
266
+ if (slice.length === 0)
267
+ break;
268
+ const startLine = i + 1;
269
+ const endLine = Math.min(i + slice.length, lines.length);
270
+ chunks.push({
271
+ title: `Lines ${startLine}-${endLine}`,
272
+ content: slice.join("\n"),
273
+ });
274
+ }
275
+ return chunks;
276
+ }
206
277
  #buildTitle(headingStack, currentHeading) {
207
278
  if (headingStack.length === 0) {
208
279
  return currentHeading || "Untitled";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "context-mode",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "description": "Claude Code MCP plugin that saves 94% of your context window. Sandboxed code execution, FTS5 knowledge base, and smart truncation.",
6
6
  "author": "Mert Koseoğlu",
@@ -1,120 +1,139 @@
1
1
  ---
2
2
  name: context-mode
3
3
  description: |
4
- Use context-mode tools (execute, execute_file) instead of Bash/cat when processing
5
- large outputs. Trigger phrases: "analyze logs", "summarize output", "process data",
6
- "parse JSON", "filter results", "extract errors", "check build output",
7
- "analyze dependencies", "process API response", "large file analysis".
4
+ ALWAYS use context-mode tools instead of Bash/curl/cat when output MIGHT exceed 20 lines.
5
+ This includes: API requests (curl, fetch, gh, aws), file reading (cat, head, tail),
6
+ command output (npm test, git log, docker ps, kubectl), log analysis, data processing,
7
+ and ANY operation where you cannot guarantee the output will be small.
8
+ When in doubt, use context-mode — the cost of routing through it is near zero,
9
+ but the cost of flooding context with 50KB+ of raw data is catastrophic.
8
10
  ---
9
11
 
10
- # Context Mode: execute & execute_file
12
+ # Context Mode: Default for All Large Output
11
13
 
12
- ## When to Use (Decision Tree)
14
+ ## MANDATORY RULE
13
15
 
14
- ```
15
- Will the command output > 20 lines?
16
- ├── YES → Will you process/filter/summarize that output?
17
- │ ├── YES → Use execute or execute_file
18
- │ └── NO → Use Bash (you need raw output)
19
- └── NO → Use Bash (small output fits in context)
20
- ```
21
-
22
- **Rule of thumb:** If you would pipe Bash output through grep/awk/jq to reduce it,
23
- use `execute` or `execute_file` instead — the LLM summary is better.
24
-
25
- ## Quick Reference
26
-
27
- | Tool | Purpose | Key Parameters |
28
- |------|---------|---------------|
29
- | `execute` | Run inline code, get LLM summary | `code`, `language`, `timeout_ms`, `summary_prompt` |
30
- | `execute_file` | Run a script file, get LLM summary | `file_path`, `args`, `timeout_ms`, `summary_prompt` |
16
+ **Before using Bash, curl, cat, or any command that produces output, ask yourself:**
31
17
 
32
- Both tools execute code and return an **LLM-generated summary** instead of raw stdout.
33
- The raw output never enters your context window — only the summary does.
18
+ > "Could this output be more than 20 lines?"
34
19
 
35
- ## Language Selection Guide
20
+ If YES or UNCERTAIN → use `execute` or `execute_file`. Not Bash. Not curl. Not cat.
36
21
 
37
- | Scenario | Language | Why |
38
- |----------|----------|-----|
39
- | HTTP requests, JSON APIs | `javascript` | Native fetch, JSON.parse |
40
- | Data analysis, CSV, math | `python` | pandas, csv module, statistics |
41
- | Piping commands, grep, find | `shell` | Native OS tools |
42
- | TypeScript project analysis | `javascript` | Can require/import project files |
43
- | Log file filtering | `shell` | grep/awk are purpose-built |
44
- | File comparison | `python` | difflib is excellent |
22
+ **This is not optional.** Every KB of unnecessary context reduces the quality and speed of the entire session.
45
23
 
46
- ## Usage Pattern
47
-
48
- ### execute — inline code
24
+ ## Decision Tree
49
25
 
50
26
  ```
51
- Tool: execute
52
- Parameters:
53
- code: |
54
- const data = require('fs').readFileSync('package.json', 'utf8');
55
- const pkg = JSON.parse(data);
56
- console.log(`Name: ${pkg.name}`);
57
- console.log(`Dependencies: ${Object.keys(pkg.dependencies || {}).length}`);
58
- console.log(`DevDependencies: ${Object.keys(pkg.devDependencies || {}).length}`);
59
- Object.entries(pkg.dependencies || {}).forEach(([k, v]) => console.log(` ${k}: ${v}`));
60
- language: javascript
61
- timeout_ms: 10000
62
- summary_prompt: "List the package name, dependency count, and any outdated patterns"
27
+ About to run a command / read a file / call an API?
28
+
29
+ ├── Output is GUARANTEED small (<20 lines)?
30
+ │ └── Use Bash (git status, pwd, ls, echo, etc.)
31
+
32
+ ├── Output MIGHT be large or you're UNSURE?
33
+ │ └── Use context-mode execute or execute_file
34
+
35
+ ├── Fetching web documentation or HTML page?
36
+ │ └── Use fetch_and_index → search
37
+
38
+ ├── Processing output from another MCP tool (Playwright, Context7, etc.)?
39
+ │ └── Use index → search
40
+
41
+ └── Reading a file to analyze/summarize (not edit)?
42
+ └── Use execute_file (file loads into FILE_CONTENT, not context)
63
43
  ```
64
44
 
65
- ### execute_file run existing script
66
-
67
- ```
68
- Tool: execute_file
69
- Parameters:
70
- file_path: ./scripts/analyze-bundle.js
71
- args: ["--format", "summary"]
72
- timeout_ms: 30000
73
- summary_prompt: "Report bundle size changes and any chunks exceeding 500KB"
74
- ```
45
+ ## When to Use Each Tool
46
+
47
+ | Situation | Tool | Example |
48
+ |-----------|------|---------|
49
+ | Hit an API endpoint | `execute` | `fetch('http://localhost:3000/api/orders')` |
50
+ | Run CLI that returns data | `execute` | `gh pr list`, `aws s3 ls`, `kubectl get pods` |
51
+ | Run tests | `execute` | `npm test`, `pytest`, `go test ./...` |
52
+ | Git operations | `execute` | `git log --oneline -50`, `git diff HEAD~5` |
53
+ | Docker/K8s inspection | `execute` | `docker stats --no-stream`, `kubectl describe pod` |
54
+ | Read a log file | `execute_file` | Parse access.log, error.log, build output |
55
+ | Read a data file | `execute_file` | Analyze CSV, JSON, YAML, XML |
56
+ | Read source code to analyze | `execute_file` | Count functions, find patterns, extract metrics |
57
+ | Fetch web docs | `fetch_and_index` | Index React/Next.js/Zod docs, then search |
58
+ | Process large MCP output | `index` → `search` | Index Playwright snapshot, then query elements |
59
+
60
+ ## Automatic Triggers
61
+
62
+ Use context-mode for ANY of these, without being asked:
63
+
64
+ - **API debugging**: "hit this endpoint", "call the API", "check the response", "find the bug in the response"
65
+ - **Log analysis**: "check the logs", "what errors", "read access.log", "debug the 500s"
66
+ - **Test runs**: "run the tests", "check if tests pass", "test suite output"
67
+ - **Git history**: "show recent commits", "git log", "what changed", "diff between branches"
68
+ - **Data inspection**: "look at the CSV", "parse the JSON", "analyze the config"
69
+ - **Infrastructure**: "list containers", "check pods", "S3 buckets", "show running services"
70
+ - **Dependency audit**: "check dependencies", "outdated packages", "security audit"
71
+ - **Build output**: "build the project", "check for warnings", "compile errors"
72
+ - **Code metrics**: "count lines", "find TODOs", "function count", "analyze codebase"
73
+ - **Web docs lookup**: "look up the docs", "check the API reference", "find examples"
74
+
75
+ ## Language Selection
76
+
77
+ | Situation | Language | Why |
78
+ |-----------|----------|-----|
79
+ | HTTP/API calls, JSON | `javascript` | Native fetch, JSON.parse, async/await |
80
+ | Data analysis, CSV, stats | `python` | csv, statistics, collections, re |
81
+ | Shell commands with pipes | `shell` | grep, awk, jq, native tools |
82
+ | File pattern matching | `shell` | find, wc, sort, uniq |
75
83
 
76
84
  ## Critical Rules
77
85
 
78
- 1. **Always print/log output.** The tool captures stdout. No output = empty summary.
79
- 2. **Use `summary_prompt`** to guide what the LLM extracts from the output.
80
- 3. **Set appropriate `timeout_ms`** network calls need 15000+, file ops need 5000+.
81
- 4. **Print structured data** JSON.stringify or formatted tables summarize better.
82
- 5. **Don't use for < 20 lines** Bash is simpler and wastes no LLM call.
86
+ 1. **Always console.log/print your findings.** stdout is all that enters context. No output = wasted call.
87
+ 2. **Write analysis code, not just data dumps.** Don't `console.log(JSON.stringify(data))` analyze first, print findings.
88
+ 3. **Be specific in output.** Print bug details with IDs, line numbers, exact values — not just counts.
89
+ 4. **For files you need to EDIT**: Use the normal Read tool. context-mode is for analysis, not editing.
90
+ 5. **For tiny outputs (<5 lines guaranteed)**: Use Bash. Don't over-engineer `git status` through context-mode.
83
91
 
84
- ## Examples by Language
92
+ ## Examples
85
93
 
86
- ### JavaScript: API response analysis
94
+ ### Debug an API endpoint
87
95
  ```javascript
88
- const resp = await fetch('https://api.example.com/status');
89
- const data = await resp.json();
90
- console.log(JSON.stringify(data, null, 2));
96
+ const resp = await fetch('http://localhost:3000/api/orders');
97
+ const { orders } = await resp.json();
98
+
99
+ const bugs = [];
100
+ const negQty = orders.filter(o => o.quantity < 0);
101
+ if (negQty.length) bugs.push(`Negative qty: ${negQty.map(o => o.id).join(', ')}`);
102
+
103
+ const nullFields = orders.filter(o => !o.product || !o.customer);
104
+ if (nullFields.length) bugs.push(`Null fields: ${nullFields.map(o => o.id).join(', ')}`);
105
+
106
+ console.log(`${orders.length} orders, ${bugs.length} bugs found:`);
107
+ bugs.forEach(b => console.log(`- ${b}`));
91
108
  ```
92
- > summary_prompt: "Report service health, any degraded components, and error rates"
93
109
 
94
- ### Python: Log analysis
95
- ```python
96
- import re
97
- with open('/var/log/app.log') as f:
98
- errors = [l for l in f if 'ERROR' in l]
99
- for e in errors[-50:]:
100
- print(e.strip())
101
- print(f"\nTotal errors: {len(errors)}")
110
+ ### Analyze test output
111
+ ```shell
112
+ npm test 2>&1
113
+ echo "EXIT=$?"
102
114
  ```
103
- > summary_prompt: "Categorize errors by type and report frequency of each"
104
115
 
105
- ### Shell: Build output filtering
116
+ ### Check GitHub PRs
106
117
  ```shell
107
- npm run build 2>&1
108
- echo "EXIT_CODE=$?"
118
+ gh pr list --json number,title,state,reviewDecision --jq '.[] | "\(.number) [\(.state)] \(.title) — \(.reviewDecision // "no review")"'
119
+ ```
120
+
121
+ ### Read and analyze a large file
122
+ ```python
123
+ # FILE_CONTENT is pre-loaded by execute_file
124
+ import json
125
+ data = json.loads(FILE_CONTENT)
126
+ print(f"Records: {len(data)}")
127
+ # ... analyze and print findings
109
128
  ```
110
- > summary_prompt: "Report success/failure, list any errors or warnings with file locations"
111
129
 
112
- ## Anti-Patterns (Avoid These)
130
+ ## Anti-Patterns
113
131
 
114
- - Using `execute` for `git status` (small output use Bash)
115
- - Forgetting `console.log()` / `print()` (produces empty summary)
116
- - Setting `timeout_ms: 5000` for network requests (will timeout)
117
- - Loading a 10K-line file into context then asking to summarize (use execute instead)
132
+ - Using `curl http://api/endpoint` via Bash 50KB floods context. Use `execute` with fetch instead.
133
+ - Using `cat large-file.json` via Bash → entire file in context. Use `execute_file` instead.
134
+ - Using `gh pr list` via Bash raw JSON in context. Use `execute` with `--jq` filter instead.
135
+ - Piping Bash output through `| head -20` you lose the rest. Use `execute` to analyze ALL data and print summary.
136
+ - Running `npm test` via Bash → full test output in context. Use `execute` to capture and summarize.
118
137
 
119
138
  ## Reference Files
120
139