context-mode 0.5.24 → 0.5.26

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,16 @@
1
+ {
2
+ "description": "Context-mode tool routing — injects MCP tool instructions into subagent prompts",
3
+ "hooks": {
4
+ "PreToolUse": [
5
+ {
6
+ "matcher": "Task",
7
+ "hooks": [
8
+ {
9
+ "type": "command",
10
+ "command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/task-inject.sh"
11
+ }
12
+ ]
13
+ }
14
+ ]
15
+ }
16
+ }
@@ -13,7 +13,7 @@
13
13
  "name": "context-mode",
14
14
  "source": "./",
15
15
  "description": "Claude Code MCP plugin that saves 98% of your context window. Sandboxed code execution in 10 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
16
- "version": "0.5.24",
16
+ "version": "0.5.26",
17
17
  "author": {
18
18
  "name": "Mert Koseoğlu"
19
19
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "context-mode",
3
- "version": "0.5.24",
3
+ "version": "0.5.26",
4
4
  "description": "Claude Code MCP plugin that saves 98% of your context window. Sandboxed code execution in 10 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
5
5
  "author": {
6
6
  "name": "Mert Koseoğlu",
@@ -25,27 +25,5 @@
25
25
  "args": ["${CLAUDE_PLUGIN_ROOT}/start.sh"]
26
26
  }
27
27
  },
28
- "skills": "./skills/",
29
- "hooks": {
30
- "PreToolUse": [
31
- {
32
- "matcher": "Bash",
33
- "hooks": [
34
- {
35
- "type": "prompt",
36
- "prompt": "STOP. Default to mcp__context-mode__execute for ALL Bash commands. ONLY use Bash for these guaranteed-small-output operations: file mutations (mkdir, mv, cp, rm, touch, chmod), git writes (git add, git commit, git push, git checkout, git branch, git merge), navigation (cd, pwd, which), process control (kill, pkill), package management (npm install/publish, pip install), and simple echo/printf. Everything else — any command that reads, queries, fetches, lists, logs, tests, builds, diffs, inspects, or calls an external service — MUST use mcp__context-mode__execute instead. When uncertain, use context-mode."
37
- }
38
- ]
39
- },
40
- {
41
- "matcher": "Task",
42
- "hooks": [
43
- {
44
- "type": "prompt",
45
- "prompt": "IMPORTANT: The subagent inherits MCP tools but NOT skill instructions. You MUST include these tool routing rules in the subagent prompt: 'Use mcp__context-mode__execute (language, code, intent) instead of Bash for ANY command that produces more than a few lines of output. Use mcp__context-mode__fetch_and_index (url, source) instead of WebFetch to fetch web pages. Use mcp__context-mode__search (query, source) to search indexed content. Use mcp__context-mode__execute_file (path, language, code) instead of Read for large file analysis. Do NOT use Bash, cat, curl, Read, or WebFetch for large outputs.'"
46
- }
47
- ]
48
- }
49
- ]
50
- }
28
+ "skills": "./skills/"
51
29
  }
package/build/server.js CHANGED
@@ -5,7 +5,7 @@ import { z } from "zod";
5
5
  import { PolyglotExecutor } from "./executor.js";
6
6
  import { ContentStore } from "./store.js";
7
7
  import { detectRuntimes, getRuntimeSummary, getAvailableLanguages, hasBunRuntime, } from "./runtime.js";
8
- const VERSION = "0.5.24";
8
+ const VERSION = "0.5.26";
9
9
  const runtimes = detectRuntimes();
10
10
  const available = getAvailableLanguages(runtimes);
11
11
  const server = new McpServer({
@@ -30,7 +30,7 @@ const bunNote = hasBunRuntime()
30
30
  // ─────────────────────────────────────────────────────────
31
31
  server.registerTool("execute", {
32
32
  title: "Execute Code",
33
- description: `Execute code in a sandboxed subprocess. Only stdout enters context — raw data stays in the subprocess. Use instead of bash/cat when output would exceed 20 lines.${bunNote} Available: ${langList}.`,
33
+ description: `Execute code in a sandboxed subprocess. Only stdout enters context — raw data stays in the subprocess. Use instead of bash/cat when output would exceed 20 lines.${bunNote} Available: ${langList}.\n\nPREFER THIS OVER BASH for: API calls (gh, curl, aws), test runners (npm test, pytest), git queries (git log, git diff), data processing, and ANY CLI command that may produce large output. Bash should only be used for file mutations, git writes, and navigation.`,
34
34
  inputSchema: z.object({
35
35
  language: z
36
36
  .enum([
@@ -217,7 +217,7 @@ function intentSearch(stdout, intent, source, maxResults = 5) {
217
217
  // ─────────────────────────────────────────────────────────
218
218
  server.registerTool("execute_file", {
219
219
  title: "Execute File Processing",
220
- description: "Read a file and process it without loading contents into context. The file is read into a FILE_CONTENT variable inside the sandbox. Only your printed summary enters context.",
220
+ description: "Read a file and process it without loading contents into context. The file is read into a FILE_CONTENT variable inside the sandbox. Only your printed summary enters context.\n\nPREFER THIS OVER Read/cat for: log files, data files (CSV, JSON, XML), large source files for analysis, and any file where you need to extract specific information rather than read the entire content.",
221
221
  inputSchema: z.object({
222
222
  path: z
223
223
  .string()
@@ -521,7 +521,8 @@ server.registerTool("fetch_and_index", {
521
521
  title: "Fetch & Index URL",
522
522
  description: "Fetches URL content, converts HTML to markdown, and indexes into the searchable knowledge base. " +
523
523
  "Raw content never enters context — only a brief confirmation is returned.\n\n" +
524
- "Use INSTEAD of WebFetch/Context7 when you need to reference web documentation later via search.\n\n" +
524
+ "PREFER THIS OVER WebFetch when you need to reference web documentation later via search. " +
525
+ "WebFetch loads entire page content into context; this tool indexes it and lets you search() on-demand.\n\n" +
525
526
  "After fetching, use 'search' to retrieve specific sections on-demand.",
526
527
  inputSchema: z.object({
527
528
  url: z.string().describe("The URL to fetch and index"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "context-mode",
3
- "version": "0.5.24",
3
+ "version": "0.5.26",
4
4
  "type": "module",
5
5
  "description": "Claude Code MCP plugin that saves 98% of your context window. Sandboxed code execution, FTS5 knowledge base, and intent-driven search.",
6
6
  "author": "Mert Koseoğlu",
package/server.bundle.mjs CHANGED
@@ -132,7 +132,9 @@ ${n}`}}};import{createRequire as aT}from"node:module";import{readFileSync as cT}
132
132
  `).trim();u.length!==0&&(r.push({title:this.#o(o,i),content:u,hasCode:s.some(l=>/^`{3,}/.test(l))}),s=[])},c=0;for(;c<n.length;){let u=n[c];if(/^[-_*]{3,}\s*$/.test(u)){a(),c++;continue}let l=u.match(/^(#{1,4})\s+(.+)$/);if(l){a();let m=l[1].length,f=l[2].trim();for(;o.length>0&&o[o.length-1].level>=m;)o.pop();o.push({level:m,text:f}),i=f,s.push(u),c++;continue}let d=u.match(/^(`{3,})(.*)?$/);if(d){let m=d[1],f=[u];for(c++;c<n.length;){if(f.push(n[c]),n[c].startsWith(m)&&n[c].trim()===m){c++;break}c++}s.push(...f);continue}s.push(u),c++}return a(),r}#n(e,r){let n=e.split(/\n\s*\n/);if(n.length>=3&&n.length<=200&&n.every(c=>Buffer.byteLength(c)<5e3))return n.map((c,u)=>{let l=c.trim();return{title:l.split(`
133
133
  `)[0].slice(0,80)||`Section ${u+1}`,content:l}}).filter(c=>c.content.length>0);let o=e.split(`
134
134
  `);if(o.length<=r)return[{title:"Output",content:e}];let s=[],a=Math.max(r-2,1);for(let c=0;c<o.length;c+=a){let u=o.slice(c,c+r);if(u.length===0)break;let l=c+1,d=Math.min(c+u.length,o.length),m=u[0]?.trim().slice(0,80);s.push({title:m||`Lines ${l}-${d}`,content:u.join(`
135
- `)})}return s}#o(e,r){return e.length===0?r||"Untitled":e.map(n=>n.text).join(" > ")}};var Qg="0.5.23",Wl=ai(),mT=Jg(Wl),dn=new oi({name:"context-mode",version:Qg}),Xl=new ci({runtimes:Wl}),Jl=null;function di(){return Jl||(Jl=new _o),Jl}var hT=mT.join(", "),gT=Kl()?" (Bun detected \u2014 JS/TS runs 3-5x faster)":"";dn.registerTool("execute",{title:"Execute Code",description:`Execute code in a sandboxed subprocess. Only stdout enters context \u2014 raw data stays in the subprocess. Use instead of bash/cat when output would exceed 20 lines.${gT} Available: ${hT}.`,inputSchema:ie.object({language:ie.enum(["javascript","typescript","python","shell","ruby","go","rust","php","perl","r"]).describe("Runtime language"),code:ie.string().describe("Source code to execute. Use console.log (JS/TS), print (Python/Ruby/Perl/R), echo (Shell), echo (PHP), or fmt.Println (Go) to output a summary to context."),timeout:ie.number().optional().default(3e4).describe("Max execution time in ms"),intent:ie.string().optional().describe(`What you're looking for in the output. When provided and output is large (>5KB), indexes output into knowledge base and returns section titles + previews \u2014 not full content. Use search() to retrieve specific sections. Example: 'failing tests', 'HTTP 500 errors'.
135
+ `)})}return s}#o(e,r){return e.length===0?r||"Untitled":e.map(n=>n.text).join(" > ")}};var Qg="0.5.26",Wl=ai(),mT=Jg(Wl),dn=new oi({name:"context-mode",version:Qg}),Xl=new ci({runtimes:Wl}),Jl=null;function di(){return Jl||(Jl=new _o),Jl}var hT=mT.join(", "),gT=Kl()?" (Bun detected \u2014 JS/TS runs 3-5x faster)":"";dn.registerTool("execute",{title:"Execute Code",description:`Execute code in a sandboxed subprocess. Only stdout enters context \u2014 raw data stays in the subprocess. Use instead of bash/cat when output would exceed 20 lines.${gT} Available: ${hT}.
136
+
137
+ PREFER THIS OVER BASH for: API calls (gh, curl, aws), test runners (npm test, pytest), git queries (git log, git diff), data processing, and ANY CLI command that may produce large output. Bash should only be used for file mutations, git writes, and navigation.`,inputSchema:ie.object({language:ie.enum(["javascript","typescript","python","shell","ruby","go","rust","php","perl","r"]).describe("Runtime language"),code:ie.string().describe("Source code to execute. Use console.log (JS/TS), print (Python/Ruby/Perl/R), echo (Shell), echo (PHP), or fmt.Println (Go) to output a summary to context."),timeout:ie.number().optional().default(3e4).describe("Max execution time in ms"),intent:ie.string().optional().describe(`What you're looking for in the output. When provided and output is large (>5KB), indexes output into knowledge base and returns section titles + previews \u2014 not full content. Use search() to retrieve specific sections. Example: 'failing tests', 'HTTP 500 errors'.
136
138
 
137
139
  TIP: Use specific technical terms, not just concepts. Check 'Searchable terms' in the response for available vocabulary.`)})},async({language:t,code:e,timeout:r,intent:n})=>{try{let o=await Xl.execute({language:t,code:e,timeout:r});if(o.timedOut)return{content:[{type:"text",text:`Execution timed out after ${r}ms
138
140
 
@@ -151,7 +153,9 @@ Use search() to query this content. Use source: "${n.label}" to scope results.`}
151
153
  `).length,s=Buffer.byteLength(t),i=di(),a=i.indexPlainText(t,r),c=new _o(":memory:");try{c.indexPlainText(t,r);let u=c.search(e,n);if(u.length===0){let m=e.trim().split(/\s+/).filter(f=>f.length>2).slice(0,20);if(m.length>0){let f=new Map;for(let p of m){let h=c.search(p,10);for(let g of h){let v=f.get(g.title);v?(v.score+=1,g.rank<v.bestRank&&(v.bestRank=g.rank,v.result=g)):f.set(g.title,{result:g,score:1,bestRank:g.rank})}}u=Array.from(f.values()).sort((p,h)=>h.score-p.score||p.bestRank-h.bestRank).slice(0,n).map(p=>p.result)}}let l=i.getDistinctiveTerms(a.sourceId);if(u.length===0){let m=[`Indexed ${a.totalChunks} sections from "${r}" into knowledge base.`,`No sections matched intent "${e}" in ${o}-line output (${(s/1024).toFixed(1)}KB).`];return l.length>0&&(m.push(""),m.push(`Searchable terms: ${l.join(", ")}`)),m.push(""),m.push("Use search() to explore the indexed content."),m.join(`
152
154
  `)}let d=[`Indexed ${a.totalChunks} sections from "${r}" into knowledge base.`,`${u.length} sections matched "${e}" (${o} lines, ${(s/1024).toFixed(1)}KB):`,""];for(let m of u){let f=m.content.split(`
153
155
  `)[0].slice(0,120);d.push(` - ${m.title}: ${f}`)}return l.length>0&&(d.push(""),d.push(`Searchable terms: ${l.join(", ")}`)),d.push(""),d.push("Use search() to retrieve full content of any section."),d.join(`
154
- `)}finally{c.close()}}dn.registerTool("execute_file",{title:"Execute File Processing",description:"Read a file and process it without loading contents into context. The file is read into a FILE_CONTENT variable inside the sandbox. Only your printed summary enters context.",inputSchema:ie.object({path:ie.string().describe("Absolute file path or relative to project root"),language:ie.enum(["javascript","typescript","python","shell","ruby","go","rust","php","perl","r"]).describe("Runtime language"),code:ie.string().describe("Code to process FILE_CONTENT. Print summary via console.log/print/echo."),timeout:ie.number().optional().default(3e4).describe("Max execution time in ms"),intent:ie.string().optional().describe("What you're looking for in the output. When provided and output is large (>5KB), returns only matching sections via BM25 search instead of truncated output.")})},async({path:t,language:e,code:r,timeout:n,intent:o})=>{try{let s=await Xl.executeFile({path:t,language:e,code:r,timeout:n});if(s.timedOut)return{content:[{type:"text",text:`Timed out processing ${t} after ${n}ms`}],isError:!0};if(s.exitCode!==0){let a=`Error processing ${t} (exit ${s.exitCode}):
156
+ `)}finally{c.close()}}dn.registerTool("execute_file",{title:"Execute File Processing",description:`Read a file and process it without loading contents into context. The file is read into a FILE_CONTENT variable inside the sandbox. Only your printed summary enters context.
157
+
158
+ PREFER THIS OVER Read/cat for: log files, data files (CSV, JSON, XML), large source files for analysis, and any file where you need to extract specific information rather than read the entire content.`,inputSchema:ie.object({path:ie.string().describe("Absolute file path or relative to project root"),language:ie.enum(["javascript","typescript","python","shell","ruby","go","rust","php","perl","r"]).describe("Runtime language"),code:ie.string().describe("Code to process FILE_CONTENT. Print summary via console.log/print/echo."),timeout:ie.number().optional().default(3e4).describe("Max execution time in ms"),intent:ie.string().optional().describe("What you're looking for in the output. When provided and output is large (>5KB), returns only matching sections via BM25 search instead of truncated output.")})},async({path:t,language:e,code:r,timeout:n,intent:o})=>{try{let s=await Xl.executeFile({path:t,language:e,code:r,timeout:n});if(s.timedOut)return{content:[{type:"text",text:`Timed out processing ${t} after ${n}ms`}],isError:!0};if(s.exitCode!==0){let a=`Error processing ${t} (exit ${s.exitCode}):
155
159
  ${s.stderr||s.stdout}`;return o&&o.trim().length>0&&Buffer.byteLength(a)>ui?{content:[{type:"text",text:li(a,o,`file:${t}:error`)}],isError:!0}:{content:[{type:"text",text:a}],isError:!0}}let i=s.stdout||"(no output)";return o&&o.trim().length>0&&Buffer.byteLength(i)>ui?{content:[{type:"text",text:li(i,o,`file:${t}`)}]}:{content:[{type:"text",text:i}]}}catch(s){return{content:[{type:"text",text:`Runtime error: ${s instanceof Error?s.message:String(s)}`}],isError:!0}}});dn.registerTool("index",{title:"Index Content",description:`Index documentation or knowledge content into a searchable BM25 knowledge base. Chunks markdown by headings (keeping code blocks intact) and stores in ephemeral FTS5 database. The full content does NOT stay in context \u2014 only a brief summary is returned.
156
160
 
157
161
  WHEN TO USE:
@@ -253,7 +257,7 @@ function decodeEntities(s) {
253
257
  main();
254
258
  `;dn.registerTool("fetch_and_index",{title:"Fetch & Index URL",description:`Fetches URL content, converts HTML to markdown, and indexes into the searchable knowledge base. Raw content never enters context \u2014 only a brief confirmation is returned.
255
259
 
256
- Use INSTEAD of WebFetch/Context7 when you need to reference web documentation later via search.
260
+ PREFER THIS OVER WebFetch when you need to reference web documentation later via search. WebFetch loads entire page content into context; this tool indexes it and lets you search() on-demand.
257
261
 
258
262
  After fetching, use 'search' to retrieve specific sections on-demand.`,inputSchema:ie.object({url:ie.string().describe("The URL to fetch and index"),source:ie.string().optional().describe("Label for the indexed content (e.g., 'React useEffect docs', 'Supabase Auth API')")})},async({url:t,source:e})=>{try{let r=`process.argv[1] = ${JSON.stringify(t)};
259
263
  ${yT}`,n=await Xl.execute({language:"javascript",code:r,timeout:3e4});return n.exitCode!==0?{content:[{type:"text",text:`Failed to fetch ${t}: ${n.stderr||n.stdout}`}],isError:!0}:!n.stdout||n.stdout.trim().length===0?{content:[{type:"text",text:`Fetched ${t} but got empty content after HTML conversion`}],isError:!0}:_T(n.stdout,e??t)}catch(r){return{content:[{type:"text",text:`Fetch error: ${r instanceof Error?r.message:String(r)}`}],isError:!0}}});async function vT(){let t=new ii;await dn.connect(t),console.error(`Context Mode MCP server v${Qg} running on stdio`),console.error(`Detected runtimes: