context-mode 0.4.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/build/store.js ADDED
@@ -0,0 +1,212 @@
1
+ /**
2
+ * ContentStore — FTS5 BM25-based knowledge base for context-mode.
3
+ *
4
+ * Chunks markdown content by headings (keeping code blocks intact),
5
+ * stores in SQLite FTS5, and retrieves via BM25-ranked search.
6
+ *
7
+ * Use for documentation, API references, and any content where
8
+ * you need EXACT text later — not summaries.
9
+ */
10
+ import Database from "better-sqlite3";
11
+ import { readFileSync } from "node:fs";
12
+ import { tmpdir } from "node:os";
13
+ import { join } from "node:path";
14
+ // ─────────────────────────────────────────────────────────
15
+ // Helpers
16
+ // ─────────────────────────────────────────────────────────
17
+ function sanitizeQuery(query) {
18
+ const words = query
19
+ .replace(/['"(){}[\]*:^~]/g, " ")
20
+ .split(/\s+/)
21
+ .filter((w) => w.length > 0 &&
22
+ !["AND", "OR", "NOT", "NEAR"].includes(w.toUpperCase()));
23
+ if (words.length === 0)
24
+ return '""';
25
+ return words.map((w) => `"${w}"`).join(" ");
26
+ }
27
+ // ─────────────────────────────────────────────────────────
28
+ // ContentStore
29
+ // ─────────────────────────────────────────────────────────
30
+ export class ContentStore {
31
+ #db;
32
+ constructor(dbPath) {
33
+ const path = dbPath ?? join(tmpdir(), `context-mode-${process.pid}.db`);
34
+ this.#db = new Database(path);
35
+ this.#db.pragma("journal_mode = WAL");
36
+ this.#db.pragma("synchronous = NORMAL");
37
+ this.#initSchema();
38
+ }
39
+ // ── Schema ──
40
+ #initSchema() {
41
+ this.#db.exec(`
42
+ CREATE TABLE IF NOT EXISTS sources (
43
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
44
+ label TEXT NOT NULL,
45
+ chunk_count INTEGER NOT NULL DEFAULT 0,
46
+ code_chunk_count INTEGER NOT NULL DEFAULT 0,
47
+ indexed_at TEXT NOT NULL DEFAULT (datetime('now'))
48
+ );
49
+
50
+ CREATE VIRTUAL TABLE IF NOT EXISTS chunks USING fts5(
51
+ title,
52
+ content,
53
+ source_id UNINDEXED,
54
+ content_type UNINDEXED,
55
+ tokenize='porter unicode61'
56
+ );
57
+ `);
58
+ }
59
+ // ── Index ──
60
+ index(options) {
61
+ const { content, path, source } = options;
62
+ if (!content && !path) {
63
+ throw new Error("Either content or path must be provided");
64
+ }
65
+ const text = content ?? readFileSync(path, "utf-8");
66
+ const label = source ?? path ?? "untitled";
67
+ const chunks = this.#chunkMarkdown(text);
68
+ if (chunks.length === 0) {
69
+ const insertSource = this.#db.prepare("INSERT INTO sources (label, chunk_count, code_chunk_count) VALUES (?, 0, 0)");
70
+ const info = insertSource.run(label);
71
+ return {
72
+ sourceId: Number(info.lastInsertRowid),
73
+ label,
74
+ totalChunks: 0,
75
+ codeChunks: 0,
76
+ };
77
+ }
78
+ const codeChunks = chunks.filter((c) => c.hasCode).length;
79
+ const insertSource = this.#db.prepare("INSERT INTO sources (label, chunk_count, code_chunk_count) VALUES (?, ?, ?)");
80
+ const insertChunk = this.#db.prepare("INSERT INTO chunks (title, content, source_id, content_type) VALUES (?, ?, ?, ?)");
81
+ const transaction = this.#db.transaction(() => {
82
+ const info = insertSource.run(label, chunks.length, codeChunks);
83
+ const sourceId = Number(info.lastInsertRowid);
84
+ for (const chunk of chunks) {
85
+ insertChunk.run(chunk.title, chunk.content, sourceId, chunk.hasCode ? "code" : "prose");
86
+ }
87
+ return sourceId;
88
+ });
89
+ const sourceId = transaction();
90
+ return {
91
+ sourceId,
92
+ label,
93
+ totalChunks: chunks.length,
94
+ codeChunks,
95
+ };
96
+ }
97
+ // ── Search ──
98
+ search(query, limit = 3) {
99
+ const sanitized = sanitizeQuery(query);
100
+ const stmt = this.#db.prepare(`
101
+ SELECT
102
+ chunks.title,
103
+ chunks.content,
104
+ chunks.content_type,
105
+ sources.label,
106
+ bm25(chunks, 2.0, 1.0) AS rank
107
+ FROM chunks
108
+ JOIN sources ON sources.id = chunks.source_id
109
+ WHERE chunks MATCH ?
110
+ ORDER BY rank
111
+ LIMIT ?
112
+ `);
113
+ const rows = stmt.all(sanitized, limit);
114
+ return rows.map((r) => ({
115
+ title: r.title,
116
+ content: r.content,
117
+ source: r.label,
118
+ rank: r.rank,
119
+ contentType: r.content_type,
120
+ }));
121
+ }
122
+ // ── Stats ──
123
+ getStats() {
124
+ const sources = this.#db.prepare("SELECT COUNT(*) as c FROM sources").get()?.c ?? 0;
125
+ const chunks = this.#db
126
+ .prepare("SELECT COUNT(*) as c FROM chunks")
127
+ .get()?.c ?? 0;
128
+ const codeChunks = this.#db
129
+ .prepare("SELECT COUNT(*) as c FROM chunks WHERE content_type = 'code'")
130
+ .get()?.c ?? 0;
131
+ return { sources, chunks, codeChunks };
132
+ }
133
+ // ── Cleanup ──
134
+ close() {
135
+ this.#db.close();
136
+ }
137
+ // ── Chunking ──
138
+ #chunkMarkdown(text) {
139
+ const chunks = [];
140
+ const lines = text.split("\n");
141
+ const headingStack = [];
142
+ let currentContent = [];
143
+ let currentHeading = "";
144
+ const flush = () => {
145
+ const joined = currentContent.join("\n").trim();
146
+ if (joined.length === 0)
147
+ return;
148
+ chunks.push({
149
+ title: this.#buildTitle(headingStack, currentHeading),
150
+ content: joined,
151
+ hasCode: currentContent.some((l) => /^`{3,}/.test(l)),
152
+ });
153
+ currentContent = [];
154
+ };
155
+ let i = 0;
156
+ while (i < lines.length) {
157
+ const line = lines[i];
158
+ // Horizontal rule separator (Context7 uses long dashes)
159
+ if (/^[-_*]{3,}\s*$/.test(line)) {
160
+ flush();
161
+ i++;
162
+ continue;
163
+ }
164
+ // Heading (H1-H4)
165
+ const headingMatch = line.match(/^(#{1,4})\s+(.+)$/);
166
+ if (headingMatch) {
167
+ flush();
168
+ const level = headingMatch[1].length;
169
+ const heading = headingMatch[2].trim();
170
+ // Pop deeper levels from stack
171
+ while (headingStack.length > 0 &&
172
+ headingStack[headingStack.length - 1].level >= level) {
173
+ headingStack.pop();
174
+ }
175
+ headingStack.push({ level, text: heading });
176
+ currentHeading = heading;
177
+ currentContent.push(line);
178
+ i++;
179
+ continue;
180
+ }
181
+ // Code block — collect entire block as a unit
182
+ const codeMatch = line.match(/^(`{3,})(.*)?$/);
183
+ if (codeMatch) {
184
+ const fence = codeMatch[1];
185
+ const codeLines = [line];
186
+ i++;
187
+ while (i < lines.length) {
188
+ codeLines.push(lines[i]);
189
+ if (lines[i].startsWith(fence) && lines[i].trim() === fence) {
190
+ i++;
191
+ break;
192
+ }
193
+ i++;
194
+ }
195
+ currentContent.push(...codeLines);
196
+ continue;
197
+ }
198
+ // Regular line
199
+ currentContent.push(line);
200
+ i++;
201
+ }
202
+ // Flush remaining content
203
+ flush();
204
+ return chunks;
205
+ }
206
+ #buildTitle(headingStack, currentHeading) {
207
+ if (headingStack.length === 0) {
208
+ return currentHeading || "Untitled";
209
+ }
210
+ return headingStack.map((h) => h.text).join(" > ");
211
+ }
212
+ }
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "context-mode",
3
+ "version": "0.4.0",
4
+ "type": "module",
5
+ "description": "Claude Code MCP plugin that saves 94% of your context window. Sandboxed code execution, FTS5 knowledge base, and smart truncation.",
6
+ "author": "Mert Koseoğlu",
7
+ "license": "MIT",
8
+ "keywords": [
9
+ "mcp",
10
+ "model-context-protocol",
11
+ "claude",
12
+ "claude-code",
13
+ "context-window",
14
+ "sandbox",
15
+ "code-execution",
16
+ "fts5",
17
+ "bm25"
18
+ ],
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/mksglu/claude-context-mode"
22
+ },
23
+ "homepage": "https://github.com/mksglu/claude-context-mode#readme",
24
+ "bugs": "https://github.com/mksglu/claude-context-mode/issues",
25
+ "bin": {
26
+ "context-mode": "./build/server.js"
27
+ },
28
+ "files": [
29
+ "build",
30
+ "skills",
31
+ ".claude-plugin",
32
+ ".mcp.json",
33
+ "README.md",
34
+ "LICENSE"
35
+ ],
36
+ "scripts": {
37
+ "build": "tsc",
38
+ "prepublishOnly": "npm run build",
39
+ "dev": "npx tsx src/server.ts",
40
+ "setup": "npx tsx src/cli.ts setup",
41
+ "doctor": "npx tsx src/cli.ts doctor",
42
+ "typecheck": "tsc --noEmit",
43
+ "test": "npx tsx tests/executor.test.ts",
44
+ "benchmark": "npx tsx tests/benchmark.ts",
45
+ "test:use-cases": "npx tsx tests/use-cases.ts",
46
+ "test:compare": "npx tsx tests/context-comparison.ts",
47
+ "test:ecosystem": "npx tsx tests/ecosystem-benchmark.ts",
48
+ "test:store": "npx tsx tests/store.test.ts",
49
+ "test:all": "npx tsx tests/executor.test.ts && npx tsx tests/store.test.ts && npx tsx tests/use-cases.ts && npx tsx tests/context-comparison.ts && npx tsx tests/benchmark.ts && npx tsx tests/ecosystem-benchmark.ts"
50
+ },
51
+ "dependencies": {
52
+ "@clack/prompts": "^1.0.1",
53
+ "@modelcontextprotocol/sdk": "^1.26.0",
54
+ "better-sqlite3": "^12.6.2",
55
+ "picocolors": "^1.1.1",
56
+ "zod": "^3.25.0"
57
+ },
58
+ "devDependencies": {
59
+ "@types/better-sqlite3": "^7.6.13",
60
+ "@types/node": "^22.19.11",
61
+ "tsx": "^4.21.0",
62
+ "typescript": "^5.7.0"
63
+ }
64
+ }
@@ -0,0 +1,124 @@
1
+ ---
2
+ name: context-mode
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".
8
+ ---
9
+
10
+ # Context Mode: execute & execute_file
11
+
12
+ ## When to Use (Decision Tree)
13
+
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` |
31
+
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.
34
+
35
+ ## Language Selection Guide
36
+
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 |
45
+
46
+ ## Usage Pattern
47
+
48
+ ### execute — inline code
49
+
50
+ ```
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"
63
+ ```
64
+
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
+ ```
75
+
76
+ ## Critical Rules
77
+
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.
83
+
84
+ ## Examples by Language
85
+
86
+ ### JavaScript: API response analysis
87
+ ```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));
91
+ ```
92
+ > summary_prompt: "Report service health, any degraded components, and error rates"
93
+
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)}")
102
+ ```
103
+ > summary_prompt: "Categorize errors by type and report frequency of each"
104
+
105
+ ### Shell: Build output filtering
106
+ ```shell
107
+ npm run build 2>&1
108
+ echo "EXIT_CODE=$?"
109
+ ```
110
+ > summary_prompt: "Report success/failure, list any errors or warnings with file locations"
111
+
112
+ ## Anti-Patterns (Avoid These)
113
+
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)
118
+
119
+ ## Reference Files
120
+
121
+ - [JavaScript/TypeScript Patterns](./references/patterns-javascript.md)
122
+ - [Python Patterns](./references/patterns-python.md)
123
+ - [Shell Patterns](./references/patterns-shell.md)
124
+ - [Anti-Patterns & Common Mistakes](./references/anti-patterns.md)
@@ -0,0 +1,257 @@
1
+ # Anti-Patterns: Common Mistakes with execute / execute_file
2
+
3
+ Avoid these pitfalls when using context-mode tools.
4
+
5
+ ---
6
+
7
+ ## 1. Using execute for Small Outputs (< 20 Lines)
8
+
9
+ **Problem:** `execute` adds overhead (LLM summarization call). For small outputs, Bash is faster and cheaper.
10
+
11
+ ```
12
+ BAD — wasteful use of execute:
13
+ Tool: execute
14
+ code: "echo $(node --version)"
15
+ language: shell
16
+
17
+ GOOD — just use Bash:
18
+ Tool: Bash
19
+ command: node --version
20
+ ```
21
+
22
+ **Rule:** If the output fits comfortably in your context window (under ~20 lines), use Bash directly. Reserve `execute` for outputs that would bloat context or need intelligent summarization.
23
+
24
+ More examples of "just use Bash":
25
+ - `git status` — usually 5-10 lines
26
+ - `ls -la` — directory listing
27
+ - `cat .env.example` — small config file
28
+ - `pwd`, `whoami`, `which node`
29
+ - `wc -l src/index.ts` — single line output
30
+
31
+ ---
32
+
33
+ ## 2. Forgetting to Print Output
34
+
35
+ **Problem:** `execute` captures stdout. If your code doesn't print anything, the summary will be empty or meaningless.
36
+
37
+ ```javascript
38
+ // BAD — no output:
39
+ const fs = require('fs');
40
+ const data = JSON.parse(fs.readFileSync('package.json', 'utf8'));
41
+ const deps = Object.keys(data.dependencies);
42
+ // Nothing printed! The LLM sees empty stdout.
43
+
44
+ // GOOD — explicit output:
45
+ const fs = require('fs');
46
+ const data = JSON.parse(fs.readFileSync('package.json', 'utf8'));
47
+ const deps = Object.keys(data.dependencies);
48
+ console.log(`Dependencies (${deps.length}):`);
49
+ deps.forEach(d => console.log(` ${d}: ${data.dependencies[d]}`));
50
+ ```
51
+
52
+ ```python
53
+ # BAD — computes but never prints:
54
+ with open('data.json') as f:
55
+ data = json.load(f)
56
+ result = [x for x in data if x['status'] == 'error']
57
+ # result is lost — never printed
58
+
59
+ # GOOD — always print results:
60
+ with open('data.json') as f:
61
+ data = json.load(f)
62
+ result = [x for x in data if x['status'] == 'error']
63
+ print(f"Found {len(result)} errors:")
64
+ for r in result:
65
+ print(f" {r['id']}: {r['message']}")
66
+ ```
67
+
68
+ **Rule:** Every `execute` script must end with print/console.log of the results you want summarized.
69
+
70
+ ---
71
+
72
+ ## 3. Using Bash When JS/Python Would Be Cleaner
73
+
74
+ **Problem:** Complex data processing in Bash quickly becomes unreadable and error-prone.
75
+
76
+ ```shell
77
+ # BAD — parsing JSON in Bash is fragile:
78
+ cat data.json | python3 -c "
79
+ import sys, json
80
+ data = json.load(sys.stdin)
81
+ for item in data:
82
+ if item['status'] == 'error':
83
+ print(item['id'], item['message'])
84
+ "
85
+ # If you're already using Python inline, just use language: python
86
+ ```
87
+
88
+ ```javascript
89
+ // GOOD — use the right language for the job:
90
+ // language: javascript
91
+ const data = require('./data.json');
92
+ data.filter(x => x.status === 'error')
93
+ .forEach(x => console.log(`${x.id}: ${x.message}`));
94
+ ```
95
+
96
+ **Rule:** If your Bash script contains inline Python/Node or complex `jq`/`awk` chains, switch to `language: python` or `language: javascript` instead.
97
+
98
+ Signs you should switch from shell:
99
+ - Using `python3 -c` or `node -e` inside the shell script
100
+ - More than 3 pipes chained together
101
+ - Using `jq` for complex JSON transformations
102
+ - Nested loops in Bash
103
+ - String manipulation beyond simple `cut`/`sed`
104
+
105
+ ---
106
+
107
+ ## 4. Loading Entire Files into Context Then Processing
108
+
109
+ **Problem:** Reading a 10,000-line file with `Read` tool, then asking about it, wastes your entire context window. Use `execute` to process the file and return only the summary.
110
+
111
+ ```
112
+ BAD workflow:
113
+ 1. Read tool: read 'server.log' (10,000 lines loaded into context)
114
+ 2. "Find all errors in this log"
115
+ → 10,000 lines consumed context for a question that needs ~20 lines of output
116
+
117
+ GOOD workflow:
118
+ 1. execute with language: python
119
+ code: |
120
+ with open('server.log') as f:
121
+ errors = [l for l in f if 'ERROR' in l]
122
+ print(f"Total errors: {len(errors)}")
123
+ for e in errors[-20:]:
124
+ print(e.strip())
125
+ summary_prompt: "Categorize errors and report frequency"
126
+ → Only the summary enters context
127
+ ```
128
+
129
+ ```
130
+ BAD workflow:
131
+ 1. Read tool: read 'package-lock.json' (20,000 lines)
132
+ 2. "What version of lodash is installed?"
133
+
134
+ GOOD workflow:
135
+ 1. execute with language: javascript
136
+ code: |
137
+ const lock = require('./package-lock.json');
138
+ const find = (deps, name) => {
139
+ if (deps[name]) return deps[name].version;
140
+ for (const [, dep] of Object.entries(deps)) {
141
+ if (dep.dependencies) {
142
+ const v = find(dep.dependencies, name);
143
+ if (v) return v;
144
+ }
145
+ }
146
+ };
147
+ console.log(`lodash: ${find(lock.dependencies, 'lodash') || 'not found'}`);
148
+ summary_prompt: "Report the installed version of lodash"
149
+ ```
150
+
151
+ **Rule:** If a file is over 200 lines and you only need specific data from it, use `execute` to extract what you need rather than reading the whole file into context.
152
+
153
+ ---
154
+
155
+ ## 5. Not Using JSON.stringify for Structured Output
156
+
157
+ **Problem:** Printing objects without serialization gives `[object Object]` in JavaScript.
158
+
159
+ ```javascript
160
+ // BAD — prints [object Object]:
161
+ const pkg = require('./package.json');
162
+ console.log(pkg.dependencies);
163
+ // Output: [object Object]
164
+
165
+ // GOOD — serialize properly:
166
+ const pkg = require('./package.json');
167
+ console.log(JSON.stringify(pkg.dependencies, null, 2));
168
+ // Output: { "react": "^18.2.0", "next": "^14.0.0", ... }
169
+ ```
170
+
171
+ ```javascript
172
+ // BAD — loses structure in arrays:
173
+ const items = [{name: 'a', value: 1}, {name: 'b', value: 2}];
174
+ console.log(items);
175
+ // May print unhelpfully
176
+
177
+ // GOOD — format as table:
178
+ const items = [{name: 'a', value: 1}, {name: 'b', value: 2}];
179
+ console.log('Name | Value');
180
+ console.log('------|------');
181
+ items.forEach(i => console.log(`${i.name.padEnd(5)} | ${i.value}`));
182
+ // Or use JSON.stringify:
183
+ console.log(JSON.stringify(items, null, 2));
184
+ ```
185
+
186
+ **Rule:** Always use `JSON.stringify(data, null, 2)` for objects/arrays in JavaScript, or format as a readable table. In Python, use `json.dumps(data, indent=2)` or `pprint.pprint(data)`.
187
+
188
+ ---
189
+
190
+ ## 6. Timeout Too Short for Network Operations
191
+
192
+ **Problem:** Default timeout may be too short for API calls, builds, or test suites.
193
+
194
+ ```
195
+ BAD — will timeout on API calls:
196
+ Tool: execute
197
+ code: |
198
+ const resp = await fetch('https://api.slow-service.com/data');
199
+ console.log(await resp.json());
200
+ language: javascript
201
+ timeout_ms: 5000 ← API may take 10+ seconds
202
+
203
+ GOOD — generous timeout for network:
204
+ Tool: execute
205
+ code: |
206
+ const resp = await fetch('https://api.slow-service.com/data');
207
+ console.log(JSON.stringify(await resp.json(), null, 2));
208
+ language: javascript
209
+ timeout_ms: 30000 ← 30 seconds for network calls
210
+ ```
211
+
212
+ **Recommended timeouts:**
213
+ | Operation | timeout_ms |
214
+ |-----------|-----------|
215
+ | File reading/parsing | 5000 - 10000 |
216
+ | Local computation | 10000 |
217
+ | Single API request | 15000 - 30000 |
218
+ | Paginated API calls | 30000 - 60000 |
219
+ | npm install / build | 120000 |
220
+ | Full test suite | 120000 - 300000 |
221
+
222
+ **Rule:** Always consider what your script does and set `timeout_ms` accordingly. Network calls and builds need significantly more time than file operations.
223
+
224
+ ---
225
+
226
+ ## 7. Not Using summary_prompt Effectively
227
+
228
+ **Problem:** Without a good `summary_prompt`, the LLM summarization may focus on irrelevant details.
229
+
230
+ ```
231
+ BAD — vague or missing summary_prompt:
232
+ summary_prompt: "Summarize this"
233
+ → May focus on the wrong aspects
234
+
235
+ GOOD — specific and actionable:
236
+ summary_prompt: "Report the count of failing tests, list each failure with its file path and error message, and identify any patterns in the failures"
237
+ ```
238
+
239
+ **Tips for effective summary_prompt:**
240
+ - Be specific about what data points you need
241
+ - Ask for counts and metrics, not just descriptions
242
+ - Request actionable insights ("suggest fixes", "identify patterns")
243
+ - Mention the format you want ("list as bullet points", "group by category")
244
+
245
+ ---
246
+
247
+ ## Summary Checklist
248
+
249
+ Before using `execute`, verify:
250
+
251
+ - [ ] Output will be > 20 lines (otherwise use Bash)
252
+ - [ ] Script prints all results to stdout
253
+ - [ ] Objects are serialized with JSON.stringify / json.dumps
254
+ - [ ] Timeout matches the operation type
255
+ - [ ] Language matches the task (JS for JSON/API, Python for data, Shell for pipes)
256
+ - [ ] summary_prompt is specific and actionable
257
+ - [ ] Not loading a file into context that could be processed inside execute