context-mode 0.5.4 → 0.5.6

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.
@@ -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.4",
16
+ "version": "0.5.6",
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.4",
3
+ "version": "0.5.6",
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",
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.4";
8
+ const VERSION = "0.5.6";
9
9
  const runtimes = detectRuntimes();
10
10
  const available = getAvailableLanguages(runtimes);
11
11
  const server = new McpServer({
@@ -128,7 +128,7 @@ function indexStdout(stdout, source) {
128
128
  content: [
129
129
  {
130
130
  type: "text",
131
- text: `Indexed ${indexed.totalChunks} sections (${indexed.codeChunks} with code) from: ${indexed.label}\nUse search() to query this content.`,
131
+ text: `Indexed ${indexed.totalChunks} sections (${indexed.codeChunks} with code) from: ${indexed.label}\nUse search() to query this content. Use source: "${indexed.label}" to scope results.`,
132
132
  },
133
133
  ],
134
134
  };
@@ -360,7 +360,7 @@ server.registerTool("index", {
360
360
  content: [
361
361
  {
362
362
  type: "text",
363
- text: `Indexed ${result.totalChunks} sections (${result.codeChunks} with code) from: ${result.label}\nUse search() to query this content.`,
363
+ text: `Indexed ${result.totalChunks} sections (${result.codeChunks} with code) from: ${result.label}\nUse search() to query this content. Use source: "${result.label}" to scope results.`,
364
364
  },
365
365
  ],
366
366
  };
@@ -389,9 +389,11 @@ server.registerTool("search", {
389
389
  "- Get configuration details ('Tailwind responsive breakpoints')\n" +
390
390
  "- Find migration steps ('App Router data fetching')\n\n" +
391
391
  "SEARCH TIPS:\n" +
392
- "- Use specific technical terms, not concepts ('__proto__' not 'security')\n" +
392
+ "- Queries use OR semantics — results matching more terms rank higher via BM25\n" +
393
+ "- Use 2-4 specific technical terms per query for best results\n" +
394
+ "- Use 'source' parameter to scope search to a specific indexed source (partial match)\n" +
393
395
  "- Check 'Searchable terms' from execute/execute_file results for available vocabulary\n" +
394
- "- Combine multiple specific terms for better results\n\n" +
396
+ "- For broad topics, send multiple focused searches in parallel\n\n" +
395
397
  "Returns exact content — not summaries. Each result includes heading hierarchy and full section text.",
396
398
  inputSchema: z.object({
397
399
  query: z.string().describe("Natural language search query"),
@@ -400,17 +402,26 @@ server.registerTool("search", {
400
402
  .optional()
401
403
  .default(3)
402
404
  .describe("Maximum results to return (default: 3)"),
405
+ source: z
406
+ .string()
407
+ .optional()
408
+ .describe("Filter results to a specific indexed source (partial match). " +
409
+ "Use the source label from index/fetch_and_index response."),
403
410
  }),
404
- }, async ({ query, limit }) => {
411
+ }, async ({ query, limit, source }) => {
405
412
  try {
406
413
  const store = getStore();
407
- const results = store.search(query, limit);
414
+ const results = store.search(query, limit, source);
408
415
  if (results.length === 0) {
416
+ const sources = store.listSources();
417
+ const sourceList = sources.length > 0
418
+ ? `\nIndexed sources: ${sources.map((s) => `"${s.label}" (${s.chunkCount} sections)`).join(", ")}`
419
+ : "";
409
420
  return {
410
421
  content: [
411
422
  {
412
423
  type: "text",
413
- text: `No results found for: "${query}". Make sure content has been indexed first.`,
424
+ text: `No results found for: "${query}"${source ? ` in source "${source}"` : ""}.${sourceList}`,
414
425
  },
415
426
  ],
416
427
  };
package/build/store.d.ts CHANGED
@@ -39,7 +39,11 @@ export declare class ContentStore {
39
39
  * look for headings — it chunks by line count with overlap.
40
40
  */
41
41
  indexPlainText(content: string, source: string, linesPerChunk?: number): IndexResult;
42
- search(query: string, limit?: number): SearchResult[];
42
+ search(query: string, limit?: number, source?: string): SearchResult[];
43
+ listSources(): Array<{
44
+ label: string;
45
+ chunkCount: number;
46
+ }>;
43
47
  getDistinctiveTerms(sourceId: number, maxTerms?: number): string[];
44
48
  getStats(): StoreStats;
45
49
  close(): void;
package/build/store.js CHANGED
@@ -40,7 +40,7 @@ function sanitizeQuery(query) {
40
40
  !["AND", "OR", "NOT", "NEAR"].includes(w.toUpperCase()));
41
41
  if (words.length === 0)
42
42
  return '""';
43
- return words.map((w) => `"${w}"`).join(" ");
43
+ return words.map((w) => `"${w}"`).join(" OR ");
44
44
  }
45
45
  // ─────────────────────────────────────────────────────────
46
46
  // ContentStore
@@ -149,8 +149,9 @@ export class ContentStore {
149
149
  };
150
150
  }
151
151
  // ── Search ──
152
- search(query, limit = 3) {
152
+ search(query, limit = 3, source) {
153
153
  const sanitized = sanitizeQuery(query);
154
+ const sourceFilter = source ? "AND sources.label LIKE ?" : "";
154
155
  const stmt = this.#db.prepare(`
155
156
  SELECT
156
157
  chunks.title,
@@ -160,11 +161,14 @@ export class ContentStore {
160
161
  bm25(chunks, 2.0, 1.0) AS rank
161
162
  FROM chunks
162
163
  JOIN sources ON sources.id = chunks.source_id
163
- WHERE chunks MATCH ?
164
+ WHERE chunks MATCH ? ${sourceFilter}
164
165
  ORDER BY rank
165
166
  LIMIT ?
166
167
  `);
167
- const rows = stmt.all(sanitized, limit);
168
+ const params = source
169
+ ? [sanitized, `%${source}%`, limit]
170
+ : [sanitized, limit];
171
+ const rows = stmt.all(...params);
168
172
  return rows.map((r) => ({
169
173
  title: r.title,
170
174
  content: r.content,
@@ -173,6 +177,12 @@ export class ContentStore {
173
177
  contentType: r.content_type,
174
178
  }));
175
179
  }
180
+ // ── Sources ──
181
+ listSources() {
182
+ return this.#db
183
+ .prepare("SELECT label, chunk_count as chunkCount FROM sources ORDER BY id DESC")
184
+ .all();
185
+ }
176
186
  // ── Vocabulary ──
177
187
  getDistinctiveTerms(sourceId, maxTerms = 40) {
178
188
  const stats = this.#db
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "context-mode",
3
- "version": "0.5.4",
3
+ "version": "0.5.6",
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",
@@ -92,6 +92,23 @@ Use context-mode for ANY of these, without being asked:
92
92
  | Shell commands with pipes | `shell` | grep, awk, jq, native tools |
93
93
  | File pattern matching | `shell` | find, wc, sort, uniq |
94
94
 
95
+ ## Search Query Strategy
96
+
97
+ - BM25 uses **OR semantics** — results matching more terms rank higher automatically
98
+ - Use 2-4 specific technical terms per query: `search("transform refine pipe")`
99
+ - **Always use `source` parameter** when multiple docs are indexed to avoid cross-source contamination
100
+ - After `fetch_and_index` returns `source: "Zod API docs"`, use `search("refine", source: "Zod")`
101
+ - Partial match works: `source: "Node"` matches `"Node.js v22 CHANGELOG"`
102
+ - Send multiple `search()` calls **in parallel** for different aspects of a topic
103
+ - Example: instead of one broad search, send 3 focused parallel queries:
104
+ - `search("transform pipe", source: "Zod")` + `search("refine superRefine", source: "Zod")` + `search("coerce codec", source: "Zod")`
105
+
106
+ ## External Documentation
107
+
108
+ - **Always use `fetch_and_index`** for external docs — NEVER `cat` or `execute` with local paths for packages you don't own
109
+ - For GitHub-hosted projects, use the raw URL: `https://raw.githubusercontent.com/org/repo/main/CHANGELOG.md`
110
+ - After indexing, use the `source` parameter in search to scope results to that specific document
111
+
95
112
  ## Critical Rules
96
113
 
97
114
  1. **Always console.log/print your findings.** stdout is all that enters context. No output = wasted call.