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/.claude-plugin/plugin.json +29 -0
- package/.mcp.json +8 -0
- package/LICENSE +21 -0
- package/README.md +304 -0
- package/build/cli.d.ts +10 -0
- package/build/cli.js +193 -0
- package/build/executor.d.ts +27 -0
- package/build/executor.js +255 -0
- package/build/runtime.d.ts +24 -0
- package/build/runtime.js +167 -0
- package/build/server.d.ts +2 -0
- package/build/server.js +457 -0
- package/build/store.d.ts +39 -0
- package/build/store.js +212 -0
- package/package.json +64 -0
- package/skills/context-mode/SKILL.md +124 -0
- package/skills/context-mode/references/anti-patterns.md +257 -0
- package/skills/context-mode/references/patterns-javascript.md +298 -0
- package/skills/context-mode/references/patterns-python.md +304 -0
- package/skills/context-mode/references/patterns-shell.md +277 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "context-mode",
|
|
3
|
+
"version": "0.4.0",
|
|
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
|
+
"author": {
|
|
6
|
+
"name": "Mert Koseoğlu",
|
|
7
|
+
"url": "https://github.com/mksglu"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/mksglu/claude-context-mode#readme",
|
|
10
|
+
"repository": "https://github.com/mksglu/claude-context-mode",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"mcp",
|
|
14
|
+
"context-window",
|
|
15
|
+
"sandbox",
|
|
16
|
+
"code-execution",
|
|
17
|
+
"fts5",
|
|
18
|
+
"bm25",
|
|
19
|
+
"playwright",
|
|
20
|
+
"context7"
|
|
21
|
+
],
|
|
22
|
+
"mcpServers": {
|
|
23
|
+
"context-mode": {
|
|
24
|
+
"command": "node",
|
|
25
|
+
"args": ["${CLAUDE_PLUGIN_ROOT}/build/server.js"]
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"skills": "./skills/"
|
|
29
|
+
}
|
package/.mcp.json
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mert Koseoğlu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
# Context Mode
|
|
2
|
+
|
|
3
|
+
**Claude Code MCP plugin that saves 94% of your context window.**
|
|
4
|
+
|
|
5
|
+
Every tool call in Claude Code consumes context tokens. A single Playwright snapshot burns 10K-135K tokens. A Context7 docs lookup dumps 4K-10K tokens. GitHub's `list_commits` with 30 results costs 29K-64K tokens. With 5+ MCP servers active, you lose ~55K tokens before your first message — and after 30 minutes of real debugging, responses slow to a crawl.
|
|
6
|
+
|
|
7
|
+
Context Mode intercepts these operations, processes data in isolated subprocesses, and returns only what matters.
|
|
8
|
+
|
|
9
|
+
## The Problem: MCP Context Bloat
|
|
10
|
+
|
|
11
|
+
Claude Code has a 200K token context window. Here's how fast popular MCP servers eat through it:
|
|
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 |
|
|
23
|
+
|
|
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
|
+
|
|
26
|
+
**Vercel's finding** ([December 2025](https://www.anthropic.com/engineering/advanced-tool-use)): Removing 80% of tools resulted in 3.5x faster execution, 37% fewer tokens, and 100% success rate (up from 80%).
|
|
27
|
+
|
|
28
|
+
## Before / After
|
|
29
|
+
|
|
30
|
+
| What you're doing | Without Context Mode | With Context Mode | Savings |
|
|
31
|
+
|---|---|---|---|
|
|
32
|
+
| Playwright `browser_snapshot` | 12 KB into context | 50 B summary | **99%** |
|
|
33
|
+
| Context7 `query-docs` (React) | 60 KB raw docs | 285 B search result | **99%** |
|
|
34
|
+
| `gh pr list` / `gh api` | 8 KB JSON response | 40 B summary | **99%** |
|
|
35
|
+
| Read `access.log` (500 req) | 45 KB raw log | 71 B status breakdown | **99%** |
|
|
36
|
+
| `npm test` (30 suites) | 6 KB raw output | 37 B pass/fail | **99%** |
|
|
37
|
+
| Git log (153 commits) | 12 KB raw log | 18 B summary | **99%** |
|
|
38
|
+
| Supabase Edge Functions docs | 4 KB raw docs | 123 B code example | **97%** |
|
|
39
|
+
|
|
40
|
+
**Real aggregate across 13 scenarios: 194 KB raw → 12.6 KB context (94% savings)**
|
|
41
|
+
|
|
42
|
+
## Quick Start
|
|
43
|
+
|
|
44
|
+
### Option 1: Claude Code Plugin (Recommended)
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
/plugin install context-mode@claude-plugin-directory
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Installs as a Claude Code plugin with skills and MCP server bundled together.
|
|
51
|
+
|
|
52
|
+
### Option 2: MCP Server Only
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
claude mcp add context-mode -- npx -y context-mode
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Restart Claude Code. 5 tools are now available.
|
|
59
|
+
|
|
60
|
+
## Tools
|
|
61
|
+
|
|
62
|
+
### `execute` — Run Code in Sandbox
|
|
63
|
+
|
|
64
|
+
Execute code in 10 languages: JavaScript, TypeScript, Python, Shell, Ruby, Go, Rust, PHP, Perl, R. Only stdout enters context — raw data stays in the subprocess.
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
Claude calls: execute({ language: "shell", code: "gh pr list --json title,state | jq length" })
|
|
68
|
+
Returns: "3" ← 2 bytes instead of 8KB JSON
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
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
|
+
|
|
73
|
+
### `execute_file` — Process Files Without Loading
|
|
74
|
+
|
|
75
|
+
File contents never enter context. The file is read into a `FILE_CONTENT` variable inside the sandbox.
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
Claude calls: execute_file({ path: "access.log", language: "python", code: "..." })
|
|
79
|
+
Returns: "200: 312 | 404: 89 | 500: 14" ← 30 bytes instead of 45KB
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### `index` — Build Searchable Knowledge Base
|
|
83
|
+
|
|
84
|
+
Chunks markdown by headings, keeps code blocks intact, stores in ephemeral FTS5 database with BM25 ranking.
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
Claude calls: index({ content: <60KB React docs>, source: "React useEffect" })
|
|
88
|
+
Returns: "Indexed 33 sections (15 with code)" ← 40 bytes
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### `search` — Retrieve Exact Content
|
|
92
|
+
|
|
93
|
+
BM25 full-text search with Porter stemming. Returns exact code blocks — not summaries.
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
Claude calls: search({ query: "useEffect cleanup function" })
|
|
97
|
+
Returns: exact code example with heading context ← 500 bytes instead of 60KB
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### `fetch_and_index` — Fetch & Index URLs
|
|
101
|
+
|
|
102
|
+
Fetches URL in subprocess, converts HTML to markdown, indexes into FTS5. Raw content never enters context.
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
Claude calls: fetch_and_index({ url: "https://react.dev/reference/react/useEffect" })
|
|
106
|
+
Returns: "Indexed 33 sections (15 with code)" ← 40 bytes instead of 60KB
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Use instead of WebFetch or Context7 when you need documentation — index once, search many times.
|
|
110
|
+
|
|
111
|
+
## How It Works
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
┌──────────────────────────────────────────────────────────────────┐
|
|
115
|
+
│ Without Context Mode │
|
|
116
|
+
│ │
|
|
117
|
+
│ Claude Code → Playwright snapshot → 12KB into context │
|
|
118
|
+
│ Claude Code → Context7 docs → 60KB into context │
|
|
119
|
+
│ Claude Code → gh pr list → 8KB into context │
|
|
120
|
+
│ Claude Code → cat access.log → 45KB into context │
|
|
121
|
+
│ │
|
|
122
|
+
│ Total: 125KB consumed = ~32,000 tokens = 16% of context gone │
|
|
123
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
124
|
+
|
|
125
|
+
┌──────────────────────────────────────────────────────────────────┐
|
|
126
|
+
│ With Context Mode │
|
|
127
|
+
│ │
|
|
128
|
+
│ Claude Code → fetch_and_index(url) → "Indexed 8 sections" (50B)│
|
|
129
|
+
│ Claude Code → search("snapshot") → exact element (500B) │
|
|
130
|
+
│ Claude Code → execute("gh pr list") → "3 open PRs" (40B)│
|
|
131
|
+
│ Claude Code → execute_file(log) → "500:14, 404:89" (30B)│
|
|
132
|
+
│ │
|
|
133
|
+
│ Total: 620B consumed = ~160 tokens = 0.08% of context │
|
|
134
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Architecture
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
┌─────────────┐ stdio / JSON-RPC ┌──────────────────────────────┐
|
|
141
|
+
│ │ ◄──────────────────────► │ Context Mode MCP Server │
|
|
142
|
+
│ Claude Code │ tool calls/results │ │
|
|
143
|
+
│ │ │ ┌────────────────────────┐ │
|
|
144
|
+
└─────────────┘ │ │ PolyglotExecutor │ │
|
|
145
|
+
│ │ • 10 language runtimes │ │
|
|
146
|
+
│ │ • Sandboxed subprocess │ │
|
|
147
|
+
│ │ • Auth passthrough │ │
|
|
148
|
+
│ │ • Smart truncation │ │
|
|
149
|
+
│ └────────────────────────┘ │
|
|
150
|
+
│ │
|
|
151
|
+
│ ┌────────────────────────┐ │
|
|
152
|
+
│ │ ContentStore │ │
|
|
153
|
+
│ │ • SQLite FTS5 │ │
|
|
154
|
+
│ │ • BM25 ranking │ │
|
|
155
|
+
│ │ • Porter stemming │ │
|
|
156
|
+
│ │ • Heading-aware chunks │ │
|
|
157
|
+
│ └────────────────────────┘ │
|
|
158
|
+
└──────────────────────────────┘
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Sandbox Isolation
|
|
162
|
+
|
|
163
|
+
Each `execute` call spawns an isolated subprocess with:
|
|
164
|
+
|
|
165
|
+
- **Isolated temp directory** per execution — scripts can't access each other
|
|
166
|
+
- **Real HOME** — so `gh`, `aws`, `gcloud` find their auth configs
|
|
167
|
+
- **Auth passthrough** — GH_TOKEN, AWS credentials, KUBECONFIG, Docker, npm tokens, XDG paths
|
|
168
|
+
- **Clean environment** — PATH, LANG, NO_COLOR, Python unbuffered mode
|
|
169
|
+
|
|
170
|
+
### FTS5 Knowledge Base
|
|
171
|
+
|
|
172
|
+
The `index` and `search` tools use SQLite FTS5 with BM25 ranking:
|
|
173
|
+
|
|
174
|
+
```sql
|
|
175
|
+
CREATE VIRTUAL TABLE chunks USING fts5(
|
|
176
|
+
title, -- heading hierarchy, weighted 2x
|
|
177
|
+
content, -- section text + code blocks
|
|
178
|
+
source_id UNINDEXED,
|
|
179
|
+
content_type UNINDEXED, -- "code" or "prose"
|
|
180
|
+
tokenize='porter unicode61' -- stemming + unicode support
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
SELECT title, content, bm25(chunks, 2.0, 1.0) AS rank
|
|
184
|
+
FROM chunks
|
|
185
|
+
WHERE chunks MATCH ?
|
|
186
|
+
ORDER BY rank LIMIT 3;
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Chunking algorithm:**
|
|
190
|
+
- Splits on H1-H4 headings and `---` separators
|
|
191
|
+
- Tracks heading hierarchy: `"React > Hooks > useEffect > Cleanup"`
|
|
192
|
+
- Keeps code blocks intact — never splits mid-block
|
|
193
|
+
- Marks chunks as `code` or `prose` for content-type filtering
|
|
194
|
+
- Porter stemming: "connecting" matches "connect", "connection", "connected"
|
|
195
|
+
|
|
196
|
+
**Lazy singleton:** Database created only when `index` or `search` is first called — zero overhead for sessions that don't use it.
|
|
197
|
+
|
|
198
|
+
### Smart Truncation
|
|
199
|
+
|
|
200
|
+
When subprocess output exceeds the 100KB buffer, Context Mode preserves both head and tail:
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
Head (60%): Initial output with context
|
|
204
|
+
... [47 lines / 3.2KB truncated — showing first 12 + last 8 lines] ...
|
|
205
|
+
Tail (40%): Final output with errors/results
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Line-boundary snapping — never cuts mid-line. Error messages at the bottom are always preserved.
|
|
209
|
+
|
|
210
|
+
### HTML to Markdown Conversion
|
|
211
|
+
|
|
212
|
+
`fetch_and_index` converts HTML in a subprocess (raw HTML never enters context):
|
|
213
|
+
|
|
214
|
+
1. Strip `<script>`, `<style>`, `<nav>`, `<header>`, `<footer>`
|
|
215
|
+
2. Convert `<h1>`-`<h4>` to `#`-`####` markdown headings
|
|
216
|
+
3. Convert `<pre><code>` to fenced code blocks with language detection
|
|
217
|
+
4. Convert `<a>`, `<li>`, `<p>`, `<br>`, `<hr>` to markdown
|
|
218
|
+
5. Decode HTML entities (`&`, `<`, ` `, etc.)
|
|
219
|
+
6. Collapse excessive whitespace
|
|
220
|
+
|
|
221
|
+
## Benchmarks
|
|
222
|
+
|
|
223
|
+
### Real MCP Ecosystem Comparison
|
|
224
|
+
|
|
225
|
+
Tested with tools from popular MCP servers and Claude Code workflows:
|
|
226
|
+
|
|
227
|
+
| Scenario | Tool | Raw | Context | Savings |
|
|
228
|
+
|---|---|---|---|---|
|
|
229
|
+
| Playwright page snapshot | `execute_file` | 50+ KB | 78 B | **99%** |
|
|
230
|
+
| Context7 React docs | `index + search` | 5.9 KB | 285 B | **95%** |
|
|
231
|
+
| Context7 Supabase docs | `index + search` | 3.9 KB | 123 B | **97%** |
|
|
232
|
+
| Context7 Next.js docs | `index + search` | 6.5 KB | 273 B | **96%** |
|
|
233
|
+
| httpbin.org API docs | `fetch_and_index` | 9.4 KB | 50 B | **99%** |
|
|
234
|
+
| GitHub API response | `execute` | 8+ KB | 40 B | **99%** |
|
|
235
|
+
| Access log (500 req) | `execute_file` | 45.1 KB | 71 B | **100%** |
|
|
236
|
+
| Analytics CSV (500 rows) | `execute_file` | 85.5 KB | 11.5 KB | **87%** |
|
|
237
|
+
| MCP tools manifest (40 tools) | `execute_file` | 17.0 KB | 78 B | **100%** |
|
|
238
|
+
| npm test (30 suites) | `execute_file` | 6.0 KB | 37 B | **99%** |
|
|
239
|
+
| Git log (153 commits) | `execute` | 11.6 KB | 18 B | **100%** |
|
|
240
|
+
|
|
241
|
+
### Session Impact
|
|
242
|
+
|
|
243
|
+
Typical 45-minute debugging session:
|
|
244
|
+
|
|
245
|
+
| Metric | Without | With | Delta |
|
|
246
|
+
|---|---|---|---|
|
|
247
|
+
| Context consumed | 177 KB | 10 KB | **-94%** |
|
|
248
|
+
| Tokens used | ~45,300 | ~2,600 | **-94%** |
|
|
249
|
+
| Context remaining | 77% | 95% | **+18pp** |
|
|
250
|
+
| Time before slowdown | ~30 min | ~3 hours | **+6x** |
|
|
251
|
+
|
|
252
|
+
## Tool Decision Matrix
|
|
253
|
+
|
|
254
|
+
| Data Type | Best Tool | Why |
|
|
255
|
+
|---|---|---|
|
|
256
|
+
| Web documentation | `fetch_and_index` → `search` | Index once, search many times |
|
|
257
|
+
| MCP tool output (large) | `index` → `search` | Keep raw output out of context |
|
|
258
|
+
| Log files | `execute_file` | Aggregate stats |
|
|
259
|
+
| Test output | `execute_file` | Pass/fail summary |
|
|
260
|
+
| CSV / JSON data | `execute_file` | Computed metrics |
|
|
261
|
+
| Git / GitHub operations | `execute` | `gh`, `git` commands with auth |
|
|
262
|
+
| Cloud CLI | `execute` | `aws`, `gcloud`, `kubectl` with auth |
|
|
263
|
+
| Build output | `execute` | Error counts and warnings |
|
|
264
|
+
| Source code to edit | Plain `Read` tool | Need full content for edits |
|
|
265
|
+
| Small files (<20 lines) | Plain `Read` tool | Minimal overhead |
|
|
266
|
+
|
|
267
|
+
## Requirements
|
|
268
|
+
|
|
269
|
+
- **Node.js 18+**
|
|
270
|
+
- **Claude Code** with MCP support
|
|
271
|
+
|
|
272
|
+
### Auto-Detected Runtimes
|
|
273
|
+
|
|
274
|
+
| Runtime | Used For | Speed |
|
|
275
|
+
|---|---|---|
|
|
276
|
+
| Bun (optional) | JS/TS execution | 3-5x faster than Node |
|
|
277
|
+
| Python 3 | Python code | Standard |
|
|
278
|
+
| Ruby, Go, Rust, PHP, Perl, R | Respective languages | Standard |
|
|
279
|
+
|
|
280
|
+
## Test Suite
|
|
281
|
+
|
|
282
|
+
113 tests across 3 suites:
|
|
283
|
+
|
|
284
|
+
| Suite | Tests | Coverage |
|
|
285
|
+
|---|---|---|
|
|
286
|
+
| Executor | 55 | 10 languages, sandbox, truncation, concurrency, timeouts |
|
|
287
|
+
| ContentStore | 34 | FTS5 schema, BM25 ranking, chunking, stemming, fixtures |
|
|
288
|
+
| MCP Integration | 24 | JSON-RPC protocol, all 5 tools, fetch_and_index, errors |
|
|
289
|
+
|
|
290
|
+
## Development
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
git clone https://github.com/mksglu/claude-context-mode.git
|
|
294
|
+
cd claude-context-mode
|
|
295
|
+
npm install
|
|
296
|
+
npm run build
|
|
297
|
+
npm test # executor (55 tests)
|
|
298
|
+
npm run test:store # FTS5/BM25 (34 tests)
|
|
299
|
+
npm run test:all # all suites (113 tests)
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## License
|
|
303
|
+
|
|
304
|
+
MIT
|
package/build/cli.d.ts
ADDED
package/build/cli.js
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* context-mode CLI
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* context-mode → Start MCP server (stdio)
|
|
7
|
+
* context-mode setup → Interactive setup (detect runtimes, install Bun)
|
|
8
|
+
* context-mode doctor → Diagnose runtime issues
|
|
9
|
+
*/
|
|
10
|
+
import * as p from "@clack/prompts";
|
|
11
|
+
import color from "picocolors";
|
|
12
|
+
import { execSync } from "node:child_process";
|
|
13
|
+
import { detectRuntimes, getRuntimeSummary, hasBunRuntime, getAvailableLanguages, } from "./runtime.js";
|
|
14
|
+
const args = process.argv.slice(2);
|
|
15
|
+
if (args[0] === "setup") {
|
|
16
|
+
setup();
|
|
17
|
+
}
|
|
18
|
+
else if (args[0] === "doctor") {
|
|
19
|
+
doctor();
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
// Default: start MCP server
|
|
23
|
+
import("./server.js");
|
|
24
|
+
}
|
|
25
|
+
async function setup() {
|
|
26
|
+
console.clear();
|
|
27
|
+
p.intro(color.bgCyan(color.black(" context-mode setup ")));
|
|
28
|
+
const s = p.spinner();
|
|
29
|
+
// Step 1: Detect runtimes
|
|
30
|
+
s.start("Detecting installed runtimes");
|
|
31
|
+
const runtimes = detectRuntimes();
|
|
32
|
+
const available = getAvailableLanguages(runtimes);
|
|
33
|
+
s.stop("Detected " + available.length + " languages");
|
|
34
|
+
// Show what's available
|
|
35
|
+
p.note(getRuntimeSummary(runtimes), "Detected Runtimes");
|
|
36
|
+
// Step 2: Check Bun
|
|
37
|
+
if (!hasBunRuntime()) {
|
|
38
|
+
p.log.warn(color.yellow("Bun is not installed.") +
|
|
39
|
+
" JS/TS will run with Node.js (3-5x slower).");
|
|
40
|
+
const installBun = await p.confirm({
|
|
41
|
+
message: "Would you like to install Bun for faster execution?",
|
|
42
|
+
initialValue: true,
|
|
43
|
+
});
|
|
44
|
+
if (p.isCancel(installBun)) {
|
|
45
|
+
p.cancel("Setup cancelled.");
|
|
46
|
+
process.exit(0);
|
|
47
|
+
}
|
|
48
|
+
if (installBun) {
|
|
49
|
+
s.start("Installing Bun");
|
|
50
|
+
try {
|
|
51
|
+
execSync("curl -fsSL https://bun.sh/install | bash", {
|
|
52
|
+
stdio: "pipe",
|
|
53
|
+
timeout: 60000,
|
|
54
|
+
});
|
|
55
|
+
s.stop(color.green("Bun installed successfully!"));
|
|
56
|
+
// Re-detect runtimes
|
|
57
|
+
const newRuntimes = detectRuntimes();
|
|
58
|
+
if (hasBunRuntime()) {
|
|
59
|
+
p.log.success("JavaScript and TypeScript will now use Bun " +
|
|
60
|
+
color.dim("(3-5x faster)"));
|
|
61
|
+
}
|
|
62
|
+
p.note(getRuntimeSummary(newRuntimes), "Updated Runtimes");
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
66
|
+
s.stop(color.red("Failed to install Bun"));
|
|
67
|
+
p.log.error("Installation failed: " +
|
|
68
|
+
message +
|
|
69
|
+
"\nYou can install manually: curl -fsSL https://bun.sh/install | bash");
|
|
70
|
+
p.log.info(color.dim("Continuing with Node.js — everything will still work."));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
p.log.info(color.dim("No problem! Using Node.js. You can install Bun later: curl -fsSL https://bun.sh/install | bash"));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
p.log.success(color.green("Bun detected!") +
|
|
79
|
+
" JS/TS will run at maximum speed.");
|
|
80
|
+
}
|
|
81
|
+
// Step 3: Check optional runtimes
|
|
82
|
+
const missing = [];
|
|
83
|
+
if (!runtimes.python)
|
|
84
|
+
missing.push("Python (python3)");
|
|
85
|
+
if (!runtimes.ruby)
|
|
86
|
+
missing.push("Ruby (ruby)");
|
|
87
|
+
if (!runtimes.go)
|
|
88
|
+
missing.push("Go (go)");
|
|
89
|
+
if (!runtimes.php)
|
|
90
|
+
missing.push("PHP (php)");
|
|
91
|
+
if (!runtimes.r)
|
|
92
|
+
missing.push("R (Rscript)");
|
|
93
|
+
if (missing.length > 0) {
|
|
94
|
+
p.log.info(color.dim("Optional runtimes not found: " + missing.join(", ")));
|
|
95
|
+
p.log.info(color.dim("Install them to enable additional language support in context-mode."));
|
|
96
|
+
}
|
|
97
|
+
// Step 4: Installation instructions
|
|
98
|
+
const installMethod = await p.select({
|
|
99
|
+
message: "How would you like to configure context-mode?",
|
|
100
|
+
options: [
|
|
101
|
+
{
|
|
102
|
+
value: "claude-code",
|
|
103
|
+
label: "Claude Code (recommended)",
|
|
104
|
+
hint: "claude mcp add",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
value: "manual",
|
|
108
|
+
label: "Show manual configuration",
|
|
109
|
+
hint: ".mcp.json",
|
|
110
|
+
},
|
|
111
|
+
{ value: "skip", label: "Skip — I'll configure later" },
|
|
112
|
+
],
|
|
113
|
+
});
|
|
114
|
+
if (p.isCancel(installMethod)) {
|
|
115
|
+
p.cancel("Setup cancelled.");
|
|
116
|
+
process.exit(0);
|
|
117
|
+
}
|
|
118
|
+
const serverPath = new URL("./server.js", import.meta.url).pathname;
|
|
119
|
+
if (installMethod === "claude-code") {
|
|
120
|
+
s.start("Adding to Claude Code");
|
|
121
|
+
try {
|
|
122
|
+
execSync(`claude mcp add context-mode -- node ${serverPath}`, { stdio: "pipe", timeout: 10000 });
|
|
123
|
+
s.stop(color.green("Added to Claude Code!"));
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
s.stop(color.yellow("Could not add automatically"));
|
|
127
|
+
p.log.info("Run manually:\n" +
|
|
128
|
+
color.cyan(` claude mcp add context-mode -- node ${serverPath}`));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
else if (installMethod === "manual") {
|
|
132
|
+
p.note(JSON.stringify({
|
|
133
|
+
mcpServers: {
|
|
134
|
+
"context-mode": {
|
|
135
|
+
command: "node",
|
|
136
|
+
args: [serverPath],
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
}, null, 2), "Add to your .mcp.json or Claude Code settings");
|
|
140
|
+
}
|
|
141
|
+
p.outro(color.green("Setup complete!") +
|
|
142
|
+
" " +
|
|
143
|
+
color.dim(available.length + " languages ready."));
|
|
144
|
+
}
|
|
145
|
+
async function doctor() {
|
|
146
|
+
console.clear();
|
|
147
|
+
p.intro(color.bgMagenta(color.white(" context-mode doctor ")));
|
|
148
|
+
const s = p.spinner();
|
|
149
|
+
s.start("Running diagnostics");
|
|
150
|
+
const runtimes = detectRuntimes();
|
|
151
|
+
const available = getAvailableLanguages(runtimes);
|
|
152
|
+
s.stop("Diagnostics complete");
|
|
153
|
+
// Runtime check
|
|
154
|
+
p.note(getRuntimeSummary(runtimes), "Runtimes");
|
|
155
|
+
// Speed tier
|
|
156
|
+
if (hasBunRuntime()) {
|
|
157
|
+
p.log.success(color.green("Performance: FAST") +
|
|
158
|
+
" — Bun detected for JS/TS execution");
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
p.log.warn(color.yellow("Performance: NORMAL") +
|
|
162
|
+
" — Using Node.js (install Bun for 3-5x speed boost)");
|
|
163
|
+
}
|
|
164
|
+
// Language coverage
|
|
165
|
+
const total = 10;
|
|
166
|
+
const pct = ((available.length / total) * 100).toFixed(0);
|
|
167
|
+
p.log.info(`Language coverage: ${available.length}/${total} (${pct}%)` +
|
|
168
|
+
color.dim(` — ${available.join(", ")}`));
|
|
169
|
+
// Server test
|
|
170
|
+
p.log.step("Testing server initialization...");
|
|
171
|
+
try {
|
|
172
|
+
const { PolyglotExecutor } = await import("./executor.js");
|
|
173
|
+
const executor = new PolyglotExecutor({ runtimes });
|
|
174
|
+
const result = await executor.execute({
|
|
175
|
+
language: "javascript",
|
|
176
|
+
code: 'console.log("ok");',
|
|
177
|
+
timeout: 5000,
|
|
178
|
+
});
|
|
179
|
+
if (result.exitCode === 0 && result.stdout.trim() === "ok") {
|
|
180
|
+
p.log.success(color.green("Server test: PASS"));
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
p.log.error(color.red("Server test: FAIL") + ` — exit ${result.exitCode}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch (err) {
|
|
187
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
188
|
+
p.log.error(color.red("Server test: FAIL") + ` — ${message}`);
|
|
189
|
+
}
|
|
190
|
+
p.outro(available.length >= 4
|
|
191
|
+
? color.green("Everything looks good!")
|
|
192
|
+
: color.yellow("Some runtimes missing — install them for full coverage"));
|
|
193
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type RuntimeMap, type Language } from "./runtime.js";
|
|
2
|
+
export interface ExecResult {
|
|
3
|
+
stdout: string;
|
|
4
|
+
stderr: string;
|
|
5
|
+
exitCode: number;
|
|
6
|
+
timedOut: boolean;
|
|
7
|
+
}
|
|
8
|
+
interface ExecuteOptions {
|
|
9
|
+
language: Language;
|
|
10
|
+
code: string;
|
|
11
|
+
timeout?: number;
|
|
12
|
+
}
|
|
13
|
+
interface ExecuteFileOptions extends ExecuteOptions {
|
|
14
|
+
path: string;
|
|
15
|
+
}
|
|
16
|
+
export declare class PolyglotExecutor {
|
|
17
|
+
#private;
|
|
18
|
+
constructor(opts?: {
|
|
19
|
+
maxOutputBytes?: number;
|
|
20
|
+
projectRoot?: string;
|
|
21
|
+
runtimes?: RuntimeMap;
|
|
22
|
+
});
|
|
23
|
+
get runtimes(): RuntimeMap;
|
|
24
|
+
execute(opts: ExecuteOptions): Promise<ExecResult>;
|
|
25
|
+
executeFile(opts: ExecuteFileOptions): Promise<ExecResult>;
|
|
26
|
+
}
|
|
27
|
+
export {};
|