context-mode 0.7.3 → 0.8.1

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.
@@ -12,8 +12,8 @@
12
12
  {
13
13
  "name": "context-mode",
14
14
  "source": "./",
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.7.2",
15
+ "description": "Claude Code MCP plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
16
+ "version": "0.8.1",
17
17
  "author": {
18
18
  "name": "Mert Koseoğlu"
19
19
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "context-mode",
3
- "version": "0.7.2",
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.",
3
+ "version": "0.8.1",
4
+ "description": "Claude Code MCP plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
5
5
  "author": {
6
6
  "name": "Mert Koseoğlu",
7
7
  "url": "https://github.com/mksglu"
package/README.md CHANGED
@@ -68,7 +68,7 @@ Code Mode showed that tool definitions can be compressed by 99.9%. Context Mode
68
68
 
69
69
  Each `execute` call spawns an isolated subprocess with its own process boundary. Scripts can't access each other's memory or state. The subprocess runs your code, captures stdout, and only that stdout enters the conversation context. The raw data — log files, API responses, snapshots — never leaves the sandbox.
70
70
 
71
- Ten language runtimes are available: JavaScript, TypeScript, Python, Shell, Ruby, Go, Rust, PHP, Perl, R. Bun is auto-detected for 3-5x faster JS/TS execution.
71
+ Eleven language runtimes are available: JavaScript, TypeScript, Python, Shell, Ruby, Go, Rust, PHP, Perl, R, and Elixir. Bun is auto-detected for 3-5x faster JS/TS execution.
72
72
 
73
73
  Authenticated CLIs work through credential passthrough — `gh`, `aws`, `gcloud`, `kubectl`, `docker` inherit environment variables and config paths without exposing them to the conversation.
74
74
 
@@ -199,6 +199,12 @@ npm test # run tests
199
199
  npm run test:all # full suite
200
200
  ```
201
201
 
202
+ ## Contributors
203
+
204
+ <a href="https://github.com/mksglu/claude-context-mode/graphs/contributors">
205
+ <img src="https://contrib.rocks/image?repo=mksglu/claude-context-mode&columns=8&anon=1" />
206
+ </a>
207
+
202
208
  ## License
203
209
 
204
210
  MIT
package/build/cli.js CHANGED
@@ -123,7 +123,8 @@ function semverGt(a, b) {
123
123
  * Doctor
124
124
  * ------------------------------------------------------- */
125
125
  async function doctor() {
126
- console.clear();
126
+ if (process.stdout.isTTY)
127
+ console.clear();
127
128
  p.intro(color.bgMagenta(color.white(" context-mode doctor ")));
128
129
  const s = p.spinner();
129
130
  s.start("Running diagnostics");
@@ -142,7 +143,7 @@ async function doctor() {
142
143
  " — Using Node.js (install Bun for 3-5x speed boost)");
143
144
  }
144
145
  // Language coverage
145
- const total = 10;
146
+ const total = 11;
146
147
  const pct = ((available.length / total) * 100).toFixed(0);
147
148
  p.log.info(`Language coverage: ${available.length}/${total} (${pct}%)` +
148
149
  color.dim(` — ${available.join(", ")}`));
@@ -299,7 +300,8 @@ async function doctor() {
299
300
  * Upgrade
300
301
  * ------------------------------------------------------- */
301
302
  async function upgrade() {
302
- console.clear();
303
+ if (process.stdout.isTTY)
304
+ console.clear();
303
305
  p.intro(color.bgCyan(color.black(" context-mode upgrade ")));
304
306
  let pluginRoot = getPluginRoot();
305
307
  const settingsPath = getSettingsPath();
@@ -498,7 +500,8 @@ async function upgrade() {
498
500
  * Setup
499
501
  * ------------------------------------------------------- */
500
502
  async function setup() {
501
- console.clear();
503
+ if (process.stdout.isTTY)
504
+ console.clear();
502
505
  p.intro(color.bgCyan(color.black(" context-mode setup ")));
503
506
  const s = p.spinner();
504
507
  // Step 1: Detect runtimes
@@ -17,6 +17,7 @@ export declare class PolyglotExecutor {
17
17
  #private;
18
18
  constructor(opts?: {
19
19
  maxOutputBytes?: number;
20
+ hardCapBytes?: number;
20
21
  projectRoot?: string;
21
22
  runtimes?: RuntimeMap;
22
23
  });
package/build/executor.js CHANGED
@@ -1,15 +1,17 @@
1
1
  var _a;
2
2
  import { spawn, execSync } from "node:child_process";
3
- import { mkdtempSync, writeFileSync, rmSync } from "node:fs";
3
+ import { mkdtempSync, writeFileSync, rmSync, existsSync } from "node:fs";
4
4
  import { join, resolve } from "node:path";
5
5
  import { tmpdir } from "node:os";
6
6
  import { detectRuntimes, buildCommand, } from "./runtime.js";
7
7
  export class PolyglotExecutor {
8
8
  #maxOutputBytes;
9
+ #hardCapBytes;
9
10
  #projectRoot;
10
11
  #runtimes;
11
12
  constructor(opts) {
12
13
  this.#maxOutputBytes = opts?.maxOutputBytes ?? 102_400;
14
+ this.#hardCapBytes = opts?.hardCapBytes ?? 100 * 1024 * 1024; // 100MB
13
15
  this.#projectRoot = opts?.projectRoot ?? process.cwd();
14
16
  this.#runtimes = opts?.runtimes ?? detectRuntimes();
15
17
  }
@@ -50,6 +52,7 @@ export class PolyglotExecutor {
50
52
  php: "php",
51
53
  perl: "pl",
52
54
  r: "R",
55
+ elixir: "exs",
53
56
  };
54
57
  // Go needs a main package wrapper if not present
55
58
  if (language === "go" && !code.includes("package ")) {
@@ -59,6 +62,11 @@ export class PolyglotExecutor {
59
62
  if (language === "php" && !code.trimStart().startsWith("<?")) {
60
63
  code = `<?php\n${code}`;
61
64
  }
65
+ // Elixir: prepend compiled BEAM paths when inside a Mix project
66
+ if (language === "elixir" && existsSync(join(this.#projectRoot, "mix.exs"))) {
67
+ const escaped = JSON.stringify(join(this.#projectRoot, "_build/dev/lib"));
68
+ code = `Path.wildcard(Path.join(${escaped}, "*/ebin"))\n|> Enum.each(&Code.prepend_path/1)\n\n${code}`;
69
+ }
62
70
  const fp = join(tmpDir, `script.${extMap[language]}`);
63
71
  if (language === "shell") {
64
72
  writeFileSync(fp, code, { encoding: "utf-8", mode: 0o700 });
@@ -139,22 +147,41 @@ export class PolyglotExecutor {
139
147
  timedOut = true;
140
148
  proc.kill("SIGKILL");
141
149
  }, timeout);
142
- // Collect ALL output in full smart truncation happens after
143
- // process exits so we can keep head + tail.
144
- // OOM is bounded by timeout (default 30s) and maxOutputBytes
145
- // (used only for truncation threshold, not stream limiting).
150
+ // Stream-level byte cap: kill the process once combined stdout+stderr
151
+ // exceeds hardCapBytes. Without this, a command like `yes` or
152
+ // `cat /dev/urandom | base64` can accumulate gigabytes in memory
153
+ // before the timeout fires.
146
154
  const stdoutChunks = [];
147
155
  const stderrChunks = [];
156
+ let totalBytes = 0;
157
+ let capExceeded = false;
148
158
  proc.stdout.on("data", (chunk) => {
149
- stdoutChunks.push(chunk);
159
+ totalBytes += chunk.length;
160
+ if (totalBytes <= this.#hardCapBytes) {
161
+ stdoutChunks.push(chunk);
162
+ }
163
+ else if (!capExceeded) {
164
+ capExceeded = true;
165
+ proc.kill("SIGKILL");
166
+ }
150
167
  });
151
168
  proc.stderr.on("data", (chunk) => {
152
- stderrChunks.push(chunk);
169
+ totalBytes += chunk.length;
170
+ if (totalBytes <= this.#hardCapBytes) {
171
+ stderrChunks.push(chunk);
172
+ }
173
+ else if (!capExceeded) {
174
+ capExceeded = true;
175
+ proc.kill("SIGKILL");
176
+ }
153
177
  });
154
178
  proc.on("close", (exitCode) => {
155
179
  clearTimeout(timer);
156
180
  const rawStdout = Buffer.concat(stdoutChunks).toString("utf-8");
157
- const rawStderr = Buffer.concat(stderrChunks).toString("utf-8");
181
+ let rawStderr = Buffer.concat(stderrChunks).toString("utf-8");
182
+ if (capExceeded) {
183
+ rawStderr += `\n[output capped at ${(this.#hardCapBytes / 1024 / 1024).toFixed(0)}MB — process killed]`;
184
+ }
158
185
  const max = this.#maxOutputBytes;
159
186
  const stdout = _a.#smartTruncate(rawStdout, max);
160
187
  const stderr = _a.#smartTruncate(rawStderr, max);
@@ -235,8 +262,11 @@ export class PolyglotExecutor {
235
262
  return `const FILE_CONTENT_PATH = ${escaped};\nconst FILE_CONTENT = require("fs").readFileSync(FILE_CONTENT_PATH, "utf-8");\n${code}`;
236
263
  case "python":
237
264
  return `FILE_CONTENT_PATH = ${escaped}\nwith open(FILE_CONTENT_PATH, "r") as _f:\n FILE_CONTENT = _f.read()\n${code}`;
238
- case "shell":
239
- return `FILE_CONTENT_PATH=${escaped}\nFILE_CONTENT=$(cat ${escaped})\n${code}`;
265
+ case "shell": {
266
+ // Single-quote the path to prevent $, backtick, and ! expansion
267
+ const sq = "'" + absolutePath.replace(/'/g, "'\\''") + "'";
268
+ return `FILE_CONTENT_PATH=${sq}\nFILE_CONTENT=$(cat ${sq})\n${code}`;
269
+ }
240
270
  case "ruby":
241
271
  return `FILE_CONTENT_PATH = ${escaped}\nFILE_CONTENT = File.read(FILE_CONTENT_PATH)\n${code}`;
242
272
  case "go":
@@ -249,6 +279,8 @@ export class PolyglotExecutor {
249
279
  return `my $FILE_CONTENT_PATH = ${escaped};\nopen(my $fh, '<', $FILE_CONTENT_PATH) or die "Cannot open: $!";\nmy $FILE_CONTENT = do { local $/; <$fh> };\nclose($fh);\n${code}`;
250
280
  case "r":
251
281
  return `FILE_CONTENT_PATH <- ${escaped}\nFILE_CONTENT <- readLines(FILE_CONTENT_PATH, warn=FALSE)\nFILE_CONTENT <- paste(FILE_CONTENT, collapse="\\n")\n${code}`;
282
+ case "elixir":
283
+ return `file_content_path = ${escaped}\nfile_content = File.read!(file_content_path)\n${code}`;
252
284
  }
253
285
  }
254
286
  }
@@ -1,4 +1,4 @@
1
- export type Language = "javascript" | "typescript" | "python" | "shell" | "ruby" | "go" | "rust" | "php" | "perl" | "r";
1
+ export type Language = "javascript" | "typescript" | "python" | "shell" | "ruby" | "go" | "rust" | "php" | "perl" | "r" | "elixir";
2
2
  export interface RuntimeInfo {
3
3
  command: string;
4
4
  available: boolean;
@@ -16,6 +16,7 @@ export interface RuntimeMap {
16
16
  php: string | null;
17
17
  perl: string | null;
18
18
  r: string | null;
19
+ elixir: string | null;
19
20
  }
20
21
  export declare function detectRuntimes(): RuntimeMap;
21
22
  export declare function hasBunRuntime(): boolean;
package/build/runtime.js CHANGED
@@ -48,6 +48,7 @@ export function detectRuntimes() {
48
48
  : commandExists("r")
49
49
  ? "r"
50
50
  : null,
51
+ elixir: commandExists("elixir") ? "elixir" : null,
51
52
  };
52
53
  }
53
54
  export function hasBunRuntime() {
@@ -83,6 +84,8 @@ export function getRuntimeSummary(runtimes) {
83
84
  lines.push(` Perl: ${runtimes.perl} (${getVersion(runtimes.perl)})`);
84
85
  if (runtimes.r)
85
86
  lines.push(` R: ${runtimes.r} (${getVersion(runtimes.r)})`);
87
+ if (runtimes.elixir)
88
+ lines.push(` Elixir: ${runtimes.elixir} (${getVersion(runtimes.elixir)})`);
86
89
  if (!bunPreferred) {
87
90
  lines.push("");
88
91
  lines.push(" Tip: Install Bun for 3-5x faster JS/TS execution → https://bun.sh");
@@ -107,6 +110,8 @@ export function getAvailableLanguages(runtimes) {
107
110
  langs.push("perl");
108
111
  if (runtimes.r)
109
112
  langs.push("r");
113
+ if (runtimes.elixir)
114
+ langs.push("elixir");
110
115
  return langs;
111
116
  }
112
117
  export function buildCommand(runtimes, language, filePath) {
@@ -163,5 +168,10 @@ export function buildCommand(runtimes, language, filePath) {
163
168
  throw new Error("R not available. Install R / Rscript.");
164
169
  }
165
170
  return [runtimes.r, filePath];
171
+ case "elixir":
172
+ if (!runtimes.elixir) {
173
+ throw new Error("Elixir not available. Install elixir.");
174
+ }
175
+ return ["elixir", filePath];
166
176
  }
167
177
  }
package/build/server.js CHANGED
@@ -5,14 +5,17 @@ import { z } from "zod";
5
5
  import { PolyglotExecutor } from "./executor.js";
6
6
  import { ContentStore, cleanupStaleDBs } from "./store.js";
7
7
  import { detectRuntimes, getRuntimeSummary, getAvailableLanguages, hasBunRuntime, } from "./runtime.js";
8
- const VERSION = "0.7.3";
8
+ const VERSION = "0.8.1";
9
9
  const runtimes = detectRuntimes();
10
10
  const available = getAvailableLanguages(runtimes);
11
11
  const server = new McpServer({
12
12
  name: "context-mode",
13
13
  version: VERSION,
14
14
  });
15
- const executor = new PolyglotExecutor({ runtimes });
15
+ const executor = new PolyglotExecutor({
16
+ runtimes,
17
+ projectRoot: process.env.CLAUDE_PROJECT_DIR,
18
+ });
16
19
  // Lazy singleton — no DB overhead unless index/search is used
17
20
  let _store = null;
18
21
  function getStore() {
@@ -115,11 +118,12 @@ server.registerTool("execute", {
115
118
  "php",
116
119
  "perl",
117
120
  "r",
121
+ "elixir",
118
122
  ])
119
123
  .describe("Runtime language"),
120
124
  code: z
121
125
  .string()
122
- .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."),
126
+ .describe("Source code to execute. Use console.log (JS/TS), print (Python/Ruby/Perl/R), echo (Shell), echo (PHP), fmt.Println (Go), or IO.puts (Elixir) to output a summary to context."),
123
127
  timeout: z
124
128
  .number()
125
129
  .optional()
@@ -239,74 +243,40 @@ function intentSearch(stdout, intent, source, maxResults = 5) {
239
243
  // Index into the PERSISTENT store so user can search() later
240
244
  const persistent = getStore();
241
245
  const indexed = persistent.indexPlainText(stdout, source);
242
- // Search with an ephemeral store to find matching section titles
243
- const ephemeral = new ContentStore(":memory:");
244
- try {
245
- ephemeral.indexPlainText(stdout, source);
246
- let results = ephemeral.search(intent, maxResults);
247
- // Score-based relaxed search: search ALL words, rank by match count
248
- if (results.length === 0) {
249
- const words = intent.trim().split(/\s+/).filter(w => w.length > 2).slice(0, 20);
250
- if (words.length > 0) {
251
- const sectionScores = new Map();
252
- for (const word of words) {
253
- const wordResults = ephemeral.search(word, 10);
254
- for (const r of wordResults) {
255
- const existing = sectionScores.get(r.title);
256
- if (existing) {
257
- existing.score += 1;
258
- if (r.rank < existing.bestRank) {
259
- existing.bestRank = r.rank;
260
- existing.result = r;
261
- }
262
- }
263
- else {
264
- sectionScores.set(r.title, { result: r, score: 1, bestRank: r.rank });
265
- }
266
- }
267
- }
268
- results = Array.from(sectionScores.values())
269
- .sort((a, b) => b.score - a.score || a.bestRank - b.bestRank)
270
- .slice(0, maxResults)
271
- .map(s => s.result);
272
- }
273
- }
274
- // Extract distinctive terms as vocabulary hints for the LLM
275
- const distinctiveTerms = persistent.getDistinctiveTerms(indexed.sourceId);
276
- if (results.length === 0) {
277
- const lines = [
278
- `Indexed ${indexed.totalChunks} sections from "${source}" into knowledge base.`,
279
- `No sections matched intent "${intent}" in ${totalLines}-line output (${(totalBytes / 1024).toFixed(1)}KB).`,
280
- ];
281
- if (distinctiveTerms.length > 0) {
282
- lines.push("");
283
- lines.push(`Searchable terms: ${distinctiveTerms.join(", ")}`);
284
- }
285
- lines.push("");
286
- lines.push("Use search() to explore the indexed content.");
287
- return lines.join("\n");
288
- }
289
- // Return ONLY titles + first-line previews — not full content
246
+ // Search the persistent store directly (porter trigram → fuzzy)
247
+ let results = persistent.searchWithFallback(intent, maxResults, source);
248
+ // Extract distinctive terms as vocabulary hints for the LLM
249
+ const distinctiveTerms = persistent.getDistinctiveTerms(indexed.sourceId);
250
+ if (results.length === 0) {
290
251
  const lines = [
291
252
  `Indexed ${indexed.totalChunks} sections from "${source}" into knowledge base.`,
292
- `${results.length} sections matched "${intent}" (${totalLines} lines, ${(totalBytes / 1024).toFixed(1)}KB):`,
293
- "",
253
+ `No sections matched intent "${intent}" in ${totalLines}-line output (${(totalBytes / 1024).toFixed(1)}KB).`,
294
254
  ];
295
- for (const r of results) {
296
- const preview = r.content.split("\n")[0].slice(0, 120);
297
- lines.push(` - ${r.title}: ${preview}`);
298
- }
299
255
  if (distinctiveTerms.length > 0) {
300
256
  lines.push("");
301
257
  lines.push(`Searchable terms: ${distinctiveTerms.join(", ")}`);
302
258
  }
303
259
  lines.push("");
304
- lines.push("Use search(queries: [...]) to retrieve full content of any section.");
260
+ lines.push("Use search() to explore the indexed content.");
305
261
  return lines.join("\n");
306
262
  }
307
- finally {
308
- ephemeral.close();
263
+ // Return ONLY titles + first-line previews — not full content
264
+ const lines = [
265
+ `Indexed ${indexed.totalChunks} sections from "${source}" into knowledge base.`,
266
+ `${results.length} sections matched "${intent}" (${totalLines} lines, ${(totalBytes / 1024).toFixed(1)}KB):`,
267
+ "",
268
+ ];
269
+ for (const r of results) {
270
+ const preview = r.content.split("\n")[0].slice(0, 120);
271
+ lines.push(` - ${r.title}: ${preview}`);
272
+ }
273
+ if (distinctiveTerms.length > 0) {
274
+ lines.push("");
275
+ lines.push(`Searchable terms: ${distinctiveTerms.join(", ")}`);
309
276
  }
277
+ lines.push("");
278
+ lines.push("Use search(queries: [...]) to retrieve full content of any section.");
279
+ return lines.join("\n");
310
280
  }
311
281
  // ─────────────────────────────────────────────────────────
312
282
  // Tool: execute_file
@@ -330,11 +300,12 @@ server.registerTool("execute_file", {
330
300
  "php",
331
301
  "perl",
332
302
  "r",
303
+ "elixir",
333
304
  ])
334
305
  .describe("Runtime language"),
335
306
  code: z
336
307
  .string()
337
- .describe("Code to process FILE_CONTENT. Print summary via console.log/print/echo."),
308
+ .describe("Code to process FILE_CONTENT (file_content in Elixir). Print summary via console.log/print/echo/IO.puts."),
338
309
  timeout: z
339
310
  .number()
340
311
  .optional()
@@ -561,7 +532,7 @@ server.registerTool("search", {
561
532
  sections.push(`## ${q}\n(output cap reached)\n`);
562
533
  continue;
563
534
  }
564
- const results = store.search(q, effectiveLimit, source);
535
+ const results = store.searchWithFallback(q, effectiveLimit, source);
565
536
  if (results.length === 0) {
566
537
  sections.push(`## ${q}\nNo results found.`);
567
538
  continue;
@@ -841,16 +812,11 @@ server.registerTool("batch_execute", {
841
812
  queryResults.push(`## ${query}\n(output cap reached — use search(queries: ["${query}"]) for details)\n`);
842
813
  continue;
843
814
  }
844
- // Tier 1: scoped search (within this batch's source)
845
- let results = store.search(query, 3, source);
846
- // Tier 2: boosted with section titles
847
- if (results.length === 0 && sectionTitles.length > 0) {
848
- const boosted = `${query} ${sectionTitles.join(" ")}`;
849
- results = store.search(boosted, 3, source);
850
- }
851
- // Tier 3: global fallback (no source filter)
815
+ // Tier 1: scoped search with fallback (porter trigram → fuzzy)
816
+ let results = store.searchWithFallback(query, 3, source);
817
+ // Tier 2: global fallback (no source filter)
852
818
  if (results.length === 0) {
853
- results = store.search(query, 3);
819
+ results = store.searchWithFallback(query, 3);
854
820
  }
855
821
  queryResults.push(`## ${query}`);
856
822
  queryResults.push("");
package/build/store.js CHANGED
@@ -423,12 +423,11 @@ export class ContentStore {
423
423
  const totalChunks = stats.chunk_count;
424
424
  const minAppearances = 2;
425
425
  const maxAppearances = Math.max(3, Math.ceil(totalChunks * 0.4));
426
- const rows = this.#db
427
- .prepare("SELECT content FROM chunks WHERE source_id = ?")
428
- .all(sourceId);
426
+ // Stream chunks one at a time to avoid loading all content into memory
427
+ const stmt = this.#db.prepare("SELECT content FROM chunks WHERE source_id = ?");
429
428
  // Count document frequency (how many sections contain each word)
430
429
  const docFreq = new Map();
431
- for (const row of rows) {
430
+ for (const row of stmt.iterate(sourceId)) {
432
431
  const words = new Set(row.content
433
432
  .toLowerCase()
434
433
  .split(/[^\p{L}\p{N}_-]+/u)
@@ -476,9 +475,11 @@ export class ContentStore {
476
475
  .filter((w) => w.length >= 3 && !STOPWORDS.has(w));
477
476
  const unique = [...new Set(words)];
478
477
  const insert = this.#db.prepare("INSERT OR IGNORE INTO vocabulary (word) VALUES (?)");
479
- for (const word of unique) {
480
- insert.run(word);
481
- }
478
+ this.#db.transaction(() => {
479
+ for (const word of unique) {
480
+ insert.run(word);
481
+ }
482
+ })();
482
483
  }
483
484
  // ── Chunking ──
484
485
  #chunkMarkdown(text) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "context-mode",
3
- "version": "0.7.3",
3
+ "version": "0.8.1",
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",
@@ -52,7 +52,11 @@
52
52
  "test:store": "npx tsx tests/store.test.ts",
53
53
  "test:fuzzy": "npx tsx tests/fuzzy-search.test.ts",
54
54
  "test:hooks": "npx tsx tests/hook-integration.test.ts",
55
- "test:all": "npx tsx tests/executor.test.ts && npx tsx tests/store.test.ts && npx tsx tests/fuzzy-search.test.ts && npx tsx tests/hook-integration.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"
55
+ "test:project-dir": "npx tsx tests/project-dir.test.ts",
56
+ "test:stream-cap": "npx tsx tests/stream-cap.test.ts",
57
+ "test:search-wiring": "npx tsx tests/search-wiring.test.ts",
58
+ "test:search-fallback": "npx tsx tests/search-fallback-integration.test.ts",
59
+ "test:all": "for f in tests/*.test.ts; do npx tsx \"$f\" || exit 1; done"
56
60
  },
57
61
  "dependencies": {
58
62
  "@clack/prompts": "^1.0.1",