context-mode 0.5.5 → 0.5.7

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.5",
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.5",
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.5";
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
  };
@@ -391,6 +391,7 @@ server.registerTool("search", {
391
391
  "SEARCH TIPS:\n" +
392
392
  "- Queries use OR semantics — results matching more terms rank higher via BM25\n" +
393
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" +
394
395
  "- Check 'Searchable terms' from execute/execute_file results for available vocabulary\n" +
395
396
  "- For broad topics, send multiple focused searches in parallel\n\n" +
396
397
  "Returns exact content — not summaries. Each result includes heading hierarchy and full section text.",
@@ -401,17 +402,26 @@ server.registerTool("search", {
401
402
  .optional()
402
403
  .default(3)
403
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."),
404
410
  }),
405
- }, async ({ query, limit }) => {
411
+ }, async ({ query, limit, source }) => {
406
412
  try {
407
413
  const store = getStore();
408
- const results = store.search(query, limit);
414
+ const results = store.search(query, limit, source);
409
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
+ : "";
410
420
  return {
411
421
  content: [
412
422
  {
413
423
  type: "text",
414
- 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}`,
415
425
  },
416
426
  ],
417
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
@@ -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.5",
3
+ "version": "0.5.7",
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",
@@ -96,9 +96,18 @@ Use context-mode for ANY of these, without being asked:
96
96
 
97
97
  - BM25 uses **OR semantics** — results matching more terms rank higher automatically
98
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"`
99
102
  - Send multiple `search()` calls **in parallel** for different aspects of a topic
100
103
  - Example: instead of one broad search, send 3 focused parallel queries:
101
- - `search("transform pipe preprocess")` + `search("refine superRefine check")` + `search("coerce codec")`
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
102
111
 
103
112
  ## Critical Rules
104
113