context-mode 1.0.52 → 1.0.54

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/build/cli.js CHANGED
@@ -13,8 +13,8 @@
13
13
  */
14
14
  import * as p from "@clack/prompts";
15
15
  import color from "picocolors";
16
- import { execSync } from "node:child_process";
17
- import { readFileSync, writeFileSync, cpSync, accessSync, existsSync, readdirSync, rmSync, closeSync, openSync, constants } from "node:fs";
16
+ import { execFileSync } from "node:child_process";
17
+ import { readFileSync, writeFileSync, cpSync, accessSync, existsSync, rmSync, closeSync, openSync, chmodSync, constants } from "node:fs";
18
18
  import { request as httpsRequest } from "node:https";
19
19
  import { resolve, dirname, join } from "node:path";
20
20
  import { tmpdir, devNull } from "node:os";
@@ -351,7 +351,7 @@ async function upgrade() {
351
351
  const tmpDir = join(tmpdir(), `context-mode-upgrade-${Date.now()}`);
352
352
  s.start("Cloning mksglu/context-mode");
353
353
  try {
354
- execSync(`git clone --depth 1 https://github.com/mksglu/context-mode.git "${tmpDir}"`, { stdio: "pipe", timeout: 30000 });
354
+ execFileSync("git", ["clone", "--depth", "1", "https://github.com/mksglu/context-mode.git", tmpDir], { stdio: "pipe", timeout: 30000 });
355
355
  s.stop("Downloaded");
356
356
  const srcDir = tmpDir;
357
357
  const newPkg = JSON.parse(readFileSync(resolve(srcDir, "package.json"), "utf-8"));
@@ -364,12 +364,12 @@ async function upgrade() {
364
364
  }
365
365
  // Step 2: Install dependencies + build
366
366
  s.start("Installing dependencies & building");
367
- execSync("npm install --no-audit --no-fund", {
367
+ execFileSync("npm", ["install", "--no-audit", "--no-fund"], {
368
368
  cwd: srcDir,
369
369
  stdio: "pipe",
370
370
  timeout: 120000,
371
371
  });
372
- execSync("npm run build", {
372
+ execFileSync("npm", ["run", "build"], {
373
373
  cwd: srcDir,
374
374
  stdio: "pipe",
375
375
  timeout: 60000,
@@ -377,24 +377,8 @@ async function upgrade() {
377
377
  s.stop("Built successfully");
378
378
  // Step 3: Update in-place
379
379
  s.start("Updating files in-place");
380
- const cacheParentMatch = pluginRoot.match(/^(.*[\\/]plugins[\\/]cache[\\/][^\\/]+[\\/][^\\/]+[\\/])/);
381
- if (cacheParentMatch) {
382
- const cacheParent = cacheParentMatch[1];
383
- const myDir = pluginRoot.replace(cacheParent, "").replace(/[\\/]/g, "");
384
- try {
385
- const oldDirs = readdirSync(cacheParent).filter(d => d !== myDir);
386
- for (const d of oldDirs) {
387
- try {
388
- rmSync(resolve(cacheParent, d), { recursive: true, force: true });
389
- }
390
- catch { /* skip */ }
391
- }
392
- if (oldDirs.length > 0) {
393
- p.log.info(color.dim(` Cleaned ${oldDirs.length} stale cache dir(s)`));
394
- }
395
- }
396
- catch { /* parent may not exist */ }
397
- }
380
+ // Old version dirs are cleaned lazily by sessionstart.mjs (age-gated >1h)
381
+ // to avoid breaking active sessions that still reference them (#181).
398
382
  const items = [
399
383
  "build", "src", "hooks", "skills", "scripts", ".claude-plugin",
400
384
  "start.mjs", "server.bundle.mjs", "cli.bundle.mjs", "package.json",
@@ -422,7 +406,7 @@ async function upgrade() {
422
406
  p.log.info(color.dim(" Registry synced to " + pluginRoot));
423
407
  // Install production deps
424
408
  s.start("Installing production dependencies");
425
- execSync("npm install --production --no-audit --no-fund", {
409
+ execFileSync("npm", ["install", "--production", "--no-audit", "--no-fund"], {
426
410
  cwd: pluginRoot,
427
411
  stdio: "pipe",
428
412
  timeout: 60000,
@@ -431,7 +415,7 @@ async function upgrade() {
431
415
  // Rebuild native addons for current Node.js ABI (fixes #131)
432
416
  s.start("Rebuilding native addons");
433
417
  try {
434
- execSync("npm rebuild better-sqlite3", {
418
+ execFileSync("npm", ["rebuild", "better-sqlite3"], {
435
419
  cwd: pluginRoot,
436
420
  stdio: "pipe",
437
421
  timeout: 60000,
@@ -449,7 +433,7 @@ async function upgrade() {
449
433
  // Update global npm
450
434
  s.start("Updating npm global package");
451
435
  try {
452
- execSync(`npm install -g "${pluginRoot}" --no-audit --no-fund`, {
436
+ execFileSync("npm", ["install", "-g", pluginRoot, "--no-audit", "--no-fund"], {
453
437
  stdio: "pipe",
454
438
  timeout: 30000,
455
439
  });
@@ -507,7 +491,7 @@ async function upgrade() {
507
491
  const binPath = resolve(pluginRoot, bin);
508
492
  try {
509
493
  accessSync(binPath, constants.F_OK);
510
- execSync(`chmod +x "${binPath}"`, { stdio: "ignore" });
494
+ chmodSync(binPath, 0o755);
511
495
  permSet.push(binPath);
512
496
  }
513
497
  catch { /* not found — skip */ }
@@ -535,7 +519,7 @@ async function upgrade() {
535
519
  const cliBundlePath = resolve(pluginRoot, "cli.bundle.mjs");
536
520
  const cliBuildPath = resolve(pluginRoot, "build", "cli.js");
537
521
  const cliPath = existsSync(cliBundlePath) ? cliBundlePath : cliBuildPath;
538
- execSync(`node "${cliPath}" doctor`, {
522
+ execFileSync("node", [cliPath, "doctor"], {
539
523
  stdio: "inherit",
540
524
  timeout: 30000,
541
525
  cwd: pluginRoot,
package/build/executor.js CHANGED
@@ -1,4 +1,4 @@
1
- import { spawn, execSync } from "node:child_process";
1
+ import { spawn, execSync, execFileSync } from "node:child_process";
2
2
  import { mkdtempSync, writeFileSync, rmSync, existsSync } from "node:fs";
3
3
  import { join, resolve } from "node:path";
4
4
  import { tmpdir } from "node:os";
@@ -50,7 +50,7 @@ export class PolyglotExecutor {
50
50
  }
51
51
  async execute(opts) {
52
52
  const { language, code, timeout = 30_000, background = false } = opts;
53
- const tmpDir = mkdtempSync(join(tmpdir(), "ctx-mode-"));
53
+ const tmpDir = mkdtempSync(join(tmpdir(), ".ctx-mode-"));
54
54
  try {
55
55
  const filePath = this.#writeScript(tmpDir, code, language);
56
56
  const cmd = buildCommand(this.#runtimes, language, filePath);
@@ -127,7 +127,7 @@ export class PolyglotExecutor {
127
127
  const binPath = srcPath.replace(/\.rs$/, "") + binSuffix;
128
128
  // Compile
129
129
  try {
130
- execSync(`rustc ${srcPath} -o ${binPath}`, {
130
+ execFileSync("rustc", [srcPath, "-o", binPath], {
131
131
  cwd,
132
132
  timeout: Math.min(timeout, 60_000),
133
133
  encoding: "utf-8",
@@ -130,17 +130,17 @@ export default {
130
130
  catch {
131
131
  // best effort
132
132
  }
133
- // Async init: load routing module + write AGENTS.md. Hooks await this.
133
+ // Async init: load routing module. Hooks await this.
134
+ // NOTE: writeRoutingInstructions is intentionally NOT called here.
135
+ // process.cwd() at plugin load time is the gateway's working directory, not
136
+ // the agent's workspace. Writing AGENTS.md to cwd() caused the file to be
137
+ // created in arbitrary directories (repo roots, config dirs, $HOME, etc.).
138
+ // The write is now deferred to session_start where the real workspace path
139
+ // is known via the sessionKey → workspace mapping.
134
140
  const initPromise = (async () => {
135
141
  const routingPath = resolve(buildDir, "..", "hooks", "core", "routing.mjs");
136
142
  const routing = await import(pathToFileURL(routingPath).href);
137
143
  await routing.initSecurity(buildDir);
138
- try {
139
- new OpenClawAdapter().writeRoutingInstructions(projectDir, pluginRoot);
140
- }
141
- catch {
142
- // best effort — never break plugin init
143
- }
144
144
  return { routing };
145
145
  })();
146
146
  // ── 1. tool_call:before — Routing enforcement ──────────
@@ -315,6 +315,33 @@ export default {
315
315
  workspaceRouter.registerSession(key, sessionId);
316
316
  }
317
317
  resumeInjected = false;
318
+ // Write routing instructions (AGENTS.md) now that we know the real
319
+ // workspace. Derive the workspace directory from the sessionKey so we
320
+ // only write into recognised /.openclaw/workspace* paths, never into
321
+ // the gateway's cwd or any other arbitrary directory.
322
+ if (key) {
323
+ try {
324
+ const adapter = new OpenClawAdapter();
325
+ const openclawBase = resolve(homedir(), ".openclaw");
326
+ // Resolve workspace dir from sessionKey (pattern: agent:<name>:*)
327
+ // Restrict agent name to safe characters to prevent path traversal (#183)
328
+ const wsMatch = key.match(/^agent:([a-zA-Z0-9_-]+):/);
329
+ let wsDir;
330
+ if (wsMatch) {
331
+ wsDir = resolve(openclawBase, `workspace-${wsMatch[1]}`);
332
+ }
333
+ else {
334
+ wsDir = resolve(openclawBase, "workspace");
335
+ }
336
+ // Containment check: never write outside ~/.openclaw/
337
+ if (wsDir.startsWith(openclawBase)) {
338
+ adapter.writeRoutingInstructions(wsDir, pluginRoot);
339
+ }
340
+ }
341
+ catch {
342
+ // best effort — never break session start
343
+ }
344
+ }
318
345
  }
319
346
  catch {
320
347
  // best effort — never break session start
@@ -402,7 +429,7 @@ export default {
402
429
  info: {
403
430
  id: "context-mode",
404
431
  name: "Context Mode",
405
- ownsCompaction: true,
432
+ ownsCompaction: false,
406
433
  },
407
434
  async ingest() {
408
435
  return { ingested: true };
@@ -410,31 +437,12 @@ export default {
410
437
  async assemble({ messages }) {
411
438
  return { messages, estimatedTokens: 0 };
412
439
  },
413
- async compact({ currentTokenCount } = {}) {
414
- try {
415
- const sid = sessionId;
416
- const events = db.getEvents(sid);
417
- if (events.length === 0)
418
- return { ok: true, compacted: false };
419
- const stats = db.getSessionStats(sid);
420
- const compactCount = (stats?.compact_count ?? 0) + 1;
421
- const snapshot = buildResumeSnapshot(events, { compactCount });
422
- db.upsertResume(sid, snapshot, events.length);
423
- db.incrementCompactCount(sid);
424
- return {
425
- ok: true,
426
- compacted: true,
427
- result: {
428
- summary: snapshot,
429
- firstKeptEntryId: "", // clear all history before this compaction
430
- tokensBefore: currentTokenCount ?? 0,
431
- tokensAfter: 0,
432
- },
433
- };
434
- }
435
- catch {
436
- return { ok: false, compacted: false };
437
- }
440
+ async compact() {
441
+ // No-op: session continuity is handled by before_compaction / after_compaction hooks.
442
+ // Returning ownsCompaction: false + compacted: false lets the host platform (OpenClaw)
443
+ // manage conversation truncation, preserving Anthropic thinking/redacted_thinking blocks.
444
+ // See: https://github.com/mksglu/context-mode/issues/191
445
+ return { ok: true, compacted: false };
438
446
  },
439
447
  }));
440
448
  // ── 10. Auto-reply commands — ctx slash commands ──────
@@ -23,8 +23,11 @@ import { extractEvents } from "./session/extract.js";
23
23
  import { buildResumeSnapshot } from "./session/snapshot.js";
24
24
  import { OpenCodeAdapter } from "./adapters/opencode/index.js";
25
25
  // ── Helpers ───────────────────────────────────────────────
26
+ function getPlatform() {
27
+ return process.env.KILO ? "kilo" : "opencode";
28
+ }
26
29
  function getSessionDir() {
27
- const dir = join(homedir(), ".config", "opencode", "context-mode", "sessions");
30
+ const dir = join(homedir(), ".config", getPlatform(), "context-mode", "sessions");
28
31
  mkdirSync(dir, { recursive: true });
29
32
  return dir;
30
33
  }
@@ -54,7 +57,7 @@ export const ContextModePlugin = async (ctx) => {
54
57
  db.ensureSession(sessionId, projectDir);
55
58
  // Auto-write AGENTS.md on startup for OpenCode projects
56
59
  try {
57
- new OpenCodeAdapter().writeRoutingInstructions(projectDir, resolve(buildDir, ".."));
60
+ new OpenCodeAdapter(getPlatform()).writeRoutingInstructions(projectDir, resolve(buildDir, ".."));
58
61
  }
59
62
  catch {
60
63
  // best effort — never break plugin init
package/build/runtime.js CHANGED
@@ -11,6 +11,23 @@ function commandExists(cmd) {
11
11
  return false;
12
12
  }
13
13
  }
14
+ function bunExists() {
15
+ if (commandExists("bun"))
16
+ return true;
17
+ // Bun installs to ~/.bun/bin which may not be in PATH in MCP server environments
18
+ if (!isWindows) {
19
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
20
+ if (home && existsSync(`${home}/.bun/bin/bun`))
21
+ return true;
22
+ }
23
+ return false;
24
+ }
25
+ function bunCommand() {
26
+ if (commandExists("bun"))
27
+ return "bun";
28
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
29
+ return `${home}/.bun/bin/bun`;
30
+ }
14
31
  /**
15
32
  * On Windows, resolve the first non-WSL bash in PATH.
16
33
  * WSL bash (C:\Windows\System32\bash.exe) cannot handle Windows paths,
@@ -59,11 +76,12 @@ function getVersion(cmd) {
59
76
  }
60
77
  }
61
78
  export function detectRuntimes() {
62
- const hasBun = commandExists("bun");
79
+ const hasBun = bunExists();
80
+ const bun = hasBun ? bunCommand() : null;
63
81
  return {
64
- javascript: hasBun ? "bun" : "node",
65
- typescript: hasBun
66
- ? "bun"
82
+ javascript: bun ?? process.execPath,
83
+ typescript: bun
84
+ ? bun
67
85
  : commandExists("tsx")
68
86
  ? "tsx"
69
87
  : commandExists("ts-node")
@@ -91,11 +109,11 @@ export function detectRuntimes() {
91
109
  };
92
110
  }
93
111
  export function hasBunRuntime() {
94
- return commandExists("bun");
112
+ return bunExists();
95
113
  }
96
114
  export function getRuntimeSummary(runtimes) {
97
115
  const lines = [];
98
- const bunPreferred = runtimes.javascript === "bun";
116
+ const bunPreferred = runtimes.javascript?.endsWith("bun") ?? false;
99
117
  lines.push(` JavaScript: ${runtimes.javascript} (${getVersion(runtimes.javascript)})${bunPreferred ? " ⚡" : ""}`);
100
118
  if (runtimes.typescript) {
101
119
  lines.push(` TypeScript: ${runtimes.typescript} (${getVersion(runtimes.typescript)})`);
@@ -156,15 +174,15 @@ export function getAvailableLanguages(runtimes) {
156
174
  export function buildCommand(runtimes, language, filePath) {
157
175
  switch (language) {
158
176
  case "javascript":
159
- return runtimes.javascript === "bun"
160
- ? ["bun", "run", filePath]
161
- : ["node", filePath];
177
+ return runtimes.javascript.endsWith("bun")
178
+ ? [runtimes.javascript, "run", filePath]
179
+ : [runtimes.javascript, filePath];
162
180
  case "typescript":
163
181
  if (!runtimes.typescript) {
164
182
  throw new Error("No TypeScript runtime available. Install one of: bun (recommended), tsx (npm i -g tsx), or ts-node.");
165
183
  }
166
- if (runtimes.typescript === "bun")
167
- return ["bun", "run", filePath];
184
+ if (runtimes.typescript?.endsWith("bun"))
185
+ return [runtimes.typescript, "run", filePath];
168
186
  if (runtimes.typescript === "tsx")
169
187
  return ["tsx", filePath];
170
188
  return ["ts-node", filePath];
package/build/server.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { ContentStore } from "./store.js";
2
3
  /**
3
4
  * Parse FTS5 highlight markers to find match positions in the
4
5
  * original (marker-free) text. Returns character offsets into the
@@ -6,3 +7,4 @@
6
7
  */
7
8
  export declare function positionsFromHighlight(highlighted: string): number[];
8
9
  export declare function extractSnippet(content: string, query: string, maxLen?: number, highlighted?: string): string;
10
+ export declare function formatBatchQueryResults(store: ContentStore, queries: string[], source: string, maxOutput?: number): string[];
package/build/server.js CHANGED
@@ -321,6 +321,33 @@ export function extractSnippet(content, query, maxLen = 1500, highlighted) {
321
321
  }
322
322
  return parts.join("\n\n");
323
323
  }
324
+ export function formatBatchQueryResults(store, queries, source, maxOutput = 80 * 1024) {
325
+ const sections = [];
326
+ let outputSize = 0;
327
+ for (const query of queries) {
328
+ if (outputSize > maxOutput) {
329
+ sections.push(`## ${query}\n(output cap reached — use search(queries: ["${query}"]) for details)\n`);
330
+ continue;
331
+ }
332
+ const results = store.searchWithFallback(query, 3, source, undefined, "exact");
333
+ sections.push(`## ${query}`);
334
+ sections.push("");
335
+ if (results.length > 0) {
336
+ for (const result of results) {
337
+ const snippet = extractSnippet(result.content, query, 3000, result.highlighted);
338
+ sections.push(`### ${result.title}`);
339
+ sections.push(snippet);
340
+ sections.push("");
341
+ outputSize += snippet.length + result.title.length;
342
+ }
343
+ continue;
344
+ }
345
+ sections.push("No matching sections found.");
346
+ sections.push("");
347
+ }
348
+ sections.push(`\n> **Tip:** Results are scoped to this batch only. To search across all indexed sources, use \`ctx_search(queries: [...])\`.`);
349
+ return sections;
350
+ }
324
351
  // ─────────────────────────────────────────────────────────
325
352
  // Tool: execute
326
353
  // ─────────────────────────────────────────────────────────
@@ -1272,45 +1299,9 @@ server.registerTool("ctx_batch_execute", {
1272
1299
  inventory.push(`- ${s.title} (${(bytes / 1024).toFixed(1)}KB)`);
1273
1300
  sectionTitles.push(s.title);
1274
1301
  }
1275
- // Run all search queries — 3 results each, smart snippets
1276
- // Three-tier fallback: scoped boosted global
1277
- const MAX_OUTPUT = 80 * 1024; // 80KB total output cap
1278
- const queryResults = [];
1279
- let outputSize = 0;
1280
- for (const query of queries) {
1281
- if (outputSize > MAX_OUTPUT) {
1282
- queryResults.push(`## ${query}\n(output cap reached — use search(queries: ["${query}"]) for details)\n`);
1283
- continue;
1284
- }
1285
- // Tier 1: scoped search with fallback (porter → trigram → fuzzy)
1286
- let results = store.searchWithFallback(query, 3, source);
1287
- let crossSource = false;
1288
- // Tier 2: global fallback (no source filter) — warn about cross-source (Issue #61)
1289
- if (results.length === 0) {
1290
- results = store.searchWithFallback(query, 3);
1291
- crossSource = results.length > 0;
1292
- }
1293
- queryResults.push(`## ${query}`);
1294
- if (crossSource) {
1295
- queryResults.push(`> **Note:** No results in current batch output. Showing results from previously indexed content.`);
1296
- }
1297
- queryResults.push("");
1298
- if (results.length > 0) {
1299
- for (const r of results) {
1300
- // Use larger snippet (3KB) for batch_execute to reduce tiny-fragment issue (Issue #61)
1301
- const snippet = extractSnippet(r.content, query, 3000, r.highlighted);
1302
- const sourceTag = crossSource ? ` _(source: ${r.source})_` : "";
1303
- queryResults.push(`### ${r.title}${sourceTag}`);
1304
- queryResults.push(snippet);
1305
- queryResults.push("");
1306
- outputSize += snippet.length + r.title.length;
1307
- }
1308
- }
1309
- else {
1310
- queryResults.push("No matching sections found.");
1311
- queryResults.push("");
1312
- }
1313
- }
1302
+ // Run all search queries — source scoped only.
1303
+ // Cross-source search remains available via explicit search().
1304
+ const queryResults = formatBatchQueryResults(store, queries, source);
1314
1305
  // Get searchable terms for edge cases where follow-up is needed
1315
1306
  const distinctiveTerms = store.getDistinctiveTerms
1316
1307
  ? store.getDistinctiveTerms(indexed.sourceId)
@@ -1608,7 +1599,7 @@ server.registerTool("ctx_upgrade", {
1608
1599
  // Write inline script to a temp .mjs file — avoids quote-escaping issues
1609
1600
  // across cmd.exe, PowerShell, and bash (node -e '...' breaks on Windows).
1610
1601
  const scriptLines = [
1611
- `import{execSync}from"node:child_process";`,
1602
+ `import{execFileSync}from"node:child_process";`,
1612
1603
  `import{cpSync,rmSync,existsSync,mkdtempSync}from"node:fs";`,
1613
1604
  `import{join}from"node:path";`,
1614
1605
  `import{tmpdir}from"node:os";`,
@@ -1616,15 +1607,15 @@ server.registerTool("ctx_upgrade", {
1616
1607
  `const T=mkdtempSync(join(tmpdir(),"ctx-upgrade-"));`,
1617
1608
  `try{`,
1618
1609
  `console.log("- [x] Starting inline upgrade (no CLI found)");`,
1619
- `execSync("git clone --depth 1 ${repoUrl} \\""+T+"\\"",{stdio:"inherit"});`,
1610
+ `execFileSync("git",["clone","--depth","1","${repoUrl}",T],{stdio:"inherit"});`,
1620
1611
  `console.log("- [x] Cloned latest source");`,
1621
- `execSync("npm install",{cwd:T,stdio:"inherit"});`,
1622
- `execSync("npm run build",{cwd:T,stdio:"inherit"});`,
1612
+ `execFileSync("npm",["install"],{cwd:T,stdio:"inherit"});`,
1613
+ `execFileSync("npm",["run","build"],{cwd:T,stdio:"inherit"});`,
1623
1614
  `console.log("- [x] Built from source");`,
1624
1615
  ...copyDirs.map((d) => `if(existsSync(join(T,${JSON.stringify(d)})))cpSync(join(T,${JSON.stringify(d)}),join(P,${JSON.stringify(d)}),{recursive:true,force:true});`),
1625
1616
  ...copyFiles.map((f) => `if(existsSync(join(T,${JSON.stringify(f)})))cpSync(join(T,${JSON.stringify(f)}),join(P,${JSON.stringify(f)}),{force:true});`),
1626
1617
  `console.log("- [x] Copied build artifacts");`,
1627
- `execSync("npm install --production",{cwd:P,stdio:"inherit"});`,
1618
+ `execFileSync("npm",["install","--production"],{cwd:P,stdio:"inherit"});`,
1628
1619
  `console.log("- [x] Installed production dependencies");`,
1629
1620
  `console.log("## context-mode upgrade complete");`,
1630
1621
  `}catch(e){`,
package/build/store.d.ts CHANGED
@@ -7,6 +7,7 @@
7
7
  * Use for documentation, API references, and any content where
8
8
  * you need EXACT text later — not summaries.
9
9
  */
10
+ type SourceMatchMode = "like" | "exact";
10
11
  import type { IndexResult, SearchResult, StoreStats } from "./types.js";
11
12
  export type { IndexResult, SearchResult, StoreStats } from "./types.js";
12
13
  /**
@@ -42,10 +43,10 @@ export declare class ContentStore {
42
43
  * Falls back to `indexPlainText` if the content is not valid JSON.
43
44
  */
44
45
  indexJSON(content: string, source: string, maxChunkBytes?: number): IndexResult;
45
- search(query: string, limit?: number, source?: string, mode?: "AND" | "OR", contentType?: "code" | "prose"): SearchResult[];
46
- searchTrigram(query: string, limit?: number, source?: string, mode?: "AND" | "OR", contentType?: "code" | "prose"): SearchResult[];
46
+ search(query: string, limit?: number, source?: string, mode?: "AND" | "OR", contentType?: "code" | "prose", sourceMatchMode?: SourceMatchMode): SearchResult[];
47
+ searchTrigram(query: string, limit?: number, source?: string, mode?: "AND" | "OR", contentType?: "code" | "prose", sourceMatchMode?: SourceMatchMode): SearchResult[];
47
48
  fuzzyCorrect(query: string): string | null;
48
- searchWithFallback(query: string, limit?: number, source?: string, contentType?: "code" | "prose"): SearchResult[];
49
+ searchWithFallback(query: string, limit?: number, source?: string, contentType?: "code" | "prose", sourceMatchMode?: SourceMatchMode): SearchResult[];
49
50
  getSourceMeta(label: string): {
50
51
  label: string;
51
52
  chunkCount: number;