opencode-rag-plugin 1.19.3 → 1.19.5
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/dist/api.d.ts +1 -1
- package/dist/api.js +2 -0
- package/dist/chunker/base.js +19 -5
- package/dist/chunker/factory.js +27 -9
- package/dist/chunker/grammar.d.ts +18 -1
- package/dist/chunker/grammar.js +48 -10
- package/dist/chunker/pdf.js +30 -14
- package/dist/cli/commands/init-helpers.js +15 -2
- package/dist/cli/commands/init.js +31 -21
- package/dist/cli/commands/query.js +2 -0
- package/dist/cli/commands/quirk.js +9 -3
- package/dist/cli/commands/setup.js +5 -2
- package/dist/cli/commands/status.js +14 -5
- package/dist/cli/commands/ui.js +23 -9
- package/dist/cli/commands/update.js +4 -5
- package/dist/cli/format.d.ts +5 -2
- package/dist/cli/format.js +14 -5
- package/dist/content/image.js +33 -11
- package/dist/content/reader.js +79 -17
- package/dist/core/bootstrap.js +10 -3
- package/dist/core/config.js +31 -0
- package/dist/core/desc-cache.d.ts +8 -2
- package/dist/core/desc-cache.js +10 -3
- package/dist/core/doc-progress.js +5 -2
- package/dist/core/interfaces.d.ts +8 -0
- package/dist/core/interfaces.js +8 -1
- package/dist/core/provider-defaults.d.ts +2 -0
- package/dist/core/provider-defaults.js +19 -4
- package/dist/core/runtime-overrides.d.ts +0 -6
- package/dist/core/version-check.d.ts +5 -0
- package/dist/core/version-check.js +8 -2
- package/dist/describer/anthropic.d.ts +2 -2
- package/dist/describer/anthropic.js +19 -5
- package/dist/describer/describer.js +15 -2
- package/dist/describer/gemini.js +25 -10
- package/dist/embedder/factory.d.ts +5 -3
- package/dist/embedder/factory.js +41 -8
- package/dist/embedder/health.js +19 -19
- package/dist/embedder/http.d.ts +14 -1
- package/dist/embedder/http.js +60 -6
- package/dist/eval/session-logger.js +7 -0
- package/dist/eval/storage.js +8 -0
- package/dist/indexer/git-diff.d.ts +1 -1
- package/dist/indexer/git-diff.js +5 -1
- package/dist/indexer/pipeline.js +421 -344
- package/dist/indexer/stats.d.ts +2 -0
- package/dist/indexer/stats.js +1 -0
- package/dist/indexer/watch.js +8 -1
- package/dist/indexer/worker.js +21 -0
- package/dist/mcp/cli.js +4 -0
- package/dist/mcp/handlers.d.ts +1 -1
- package/dist/mcp/handlers.js +23 -6
- package/dist/mcp/server.js +3 -0
- package/dist/opencode/create-read-tool.d.ts +1 -1
- package/dist/opencode/create-read-tool.js +17 -5
- package/dist/opencode/tool-args.js +23 -1
- package/dist/opencode/tools.d.ts +1 -1
- package/dist/opencode/tools.js +3 -1
- package/dist/plugin.d.ts +1 -1
- package/dist/plugin.js +69 -152
- package/dist/quirks/auto-capture.js +5 -0
- package/dist/quirks/quirk-store.d.ts +1 -1
- package/dist/quirks/quirk-store.js +56 -17
- package/dist/retriever/context-optimizer.js +18 -4
- package/dist/retriever/keyword-index.d.ts +2 -0
- package/dist/retriever/keyword-index.js +38 -4
- package/dist/retriever/retriever.js +6 -1
- package/dist/tui.js +41 -4
- package/dist/vectorstore/lancedb.d.ts +25 -1
- package/dist/vectorstore/lancedb.js +157 -11
- package/dist/vectorstore/memory.js +5 -1
- package/dist/watcher.js +30 -4
- package/dist/web/api.d.ts +6 -2
- package/dist/web/api.js +198 -70
- package/dist/web/server.d.ts +2 -0
- package/dist/web/server.js +66 -28
- package/dist/web/static.d.ts +5 -2
- package/dist/web/static.js +9 -5
- package/dist/web/ui/assets/index-BDPYdtA1.js +3 -0
- package/dist/web/ui/index.html +1 -1
- package/package.json +1 -1
- package/dist/web/ui/assets/index-CJBvt6e0.js +0 -3
package/dist/indexer/stats.d.ts
CHANGED
|
@@ -38,6 +38,8 @@ export interface IndexRunStats {
|
|
|
38
38
|
}>;
|
|
39
39
|
/** Number of files where description generation failed. */
|
|
40
40
|
descriptionFailedFiles: number;
|
|
41
|
+
/** True when the pass was skipped because another pass holds the lock. */
|
|
42
|
+
skipped: boolean;
|
|
41
43
|
}
|
|
42
44
|
/** Summary of the current index health without running a full pass. */
|
|
43
45
|
export interface IndexStatusSummary {
|
package/dist/indexer/stats.js
CHANGED
package/dist/indexer/watch.js
CHANGED
|
@@ -120,9 +120,16 @@ export function createWatchIgnore(cwd, config, storePath) {
|
|
|
120
120
|
const manifestPath = manifestPathFor(storePath);
|
|
121
121
|
const dirMatcher = createExcludeMatcher(config.indexing.excludeDirs);
|
|
122
122
|
const fileMatcher = createExcludeMatcher(config.indexing.excludeFiles ?? []);
|
|
123
|
+
// Prefix check with a trailing separator so sibling dirs like
|
|
124
|
+
// `<storePath>2` are NOT ignored; case-insensitive on win32 so a
|
|
125
|
+
// differently-cased store path cannot cause self-triggering watch loops.
|
|
126
|
+
const storePrefix = storePath.endsWith(path.sep) ? storePath : storePath + path.sep;
|
|
127
|
+
const win32 = process.platform === "win32";
|
|
123
128
|
return (watchedPath) => {
|
|
124
129
|
const resolved = path.resolve(watchedPath);
|
|
125
|
-
|
|
130
|
+
const compareResolved = win32 ? resolved.toLowerCase() : resolved;
|
|
131
|
+
const compareStorePrefix = win32 ? storePrefix.toLowerCase() : storePrefix;
|
|
132
|
+
if (resolved === storePath || compareResolved.startsWith(compareStorePrefix))
|
|
126
133
|
return true;
|
|
127
134
|
if (resolved === manifestPath)
|
|
128
135
|
return true;
|
package/dist/indexer/worker.js
CHANGED
|
@@ -128,6 +128,21 @@ export async function prepareFile(file, cwd, previous, config, keywordIndex, des
|
|
|
128
128
|
},
|
|
129
129
|
};
|
|
130
130
|
}
|
|
131
|
+
// No chunks from non-empty content: file may genuinely be chunk-less.
|
|
132
|
+
// BUT if the content is empty and the file is unchanged (hash matches),
|
|
133
|
+
// this is the re-describe fast-path edge — keep the old index entry
|
|
134
|
+
// instead of deleting it (a delete would wipe previously-indexed chunks
|
|
135
|
+
// just because we lacked content to re-chunk).
|
|
136
|
+
if (previous && previous.hash === file.hash && file.content.trim().length === 0) {
|
|
137
|
+
return {
|
|
138
|
+
normalizedPath: file.normalizedPath, hash: file.hash, fileLabel,
|
|
139
|
+
isModified: false,
|
|
140
|
+
earlyResult: {
|
|
141
|
+
normalizedPath: file.normalizedPath, hash: file.hash, chunkCount: previous.chunkCount, fileLabel,
|
|
142
|
+
isNew: false, isModified: false, isUnchanged: true, isEmpty: false, isTooSmall: false, isRemoved: false, hadChunks: previous.chunkCount > 0,
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
131
146
|
return {
|
|
132
147
|
normalizedPath: file.normalizedPath, hash: file.hash, fileLabel,
|
|
133
148
|
isModified: false,
|
|
@@ -138,6 +153,12 @@ export async function prepareFile(file, cwd, previous, config, keywordIndex, des
|
|
|
138
153
|
};
|
|
139
154
|
}
|
|
140
155
|
logger.debug(` ${fileLabel}: ${chunks.length} chunks produced`);
|
|
156
|
+
// Remove stale keyword entries for a previously-indexed revision of this
|
|
157
|
+
// file (new UUIDs are generated for every chunk) so hybrid search never
|
|
158
|
+
// surfaces old content and the persisted index stops growing unboundedly.
|
|
159
|
+
if (previous) {
|
|
160
|
+
keywordIndex?.removeByFilePath(file.normalizedPath);
|
|
161
|
+
}
|
|
141
162
|
keywordIndex?.addChunks(chunks);
|
|
142
163
|
const docPrefix = config.embedding.documentPrefix ?? "";
|
|
143
164
|
const relPath = path.relative(cwd, file.filePath).replace(/\\/g, "/");
|
package/dist/mcp/cli.js
CHANGED
|
@@ -16,6 +16,10 @@ export async function runMcpServer(options) {
|
|
|
16
16
|
}
|
|
17
17
|
process.on("SIGINT", shutdown);
|
|
18
18
|
process.on("SIGTERM", shutdown);
|
|
19
|
+
// If the parent MCP client disconnects, stdin closes — exit instead of
|
|
20
|
+
// becoming a zombie that holds the store open forever.
|
|
21
|
+
process.stdin.on("end", shutdown);
|
|
22
|
+
process.stdin.on("close", shutdown);
|
|
19
23
|
await new Promise(() => { });
|
|
20
24
|
}
|
|
21
25
|
//# sourceMappingURL=cli.js.map
|
package/dist/mcp/handlers.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Handler implementations for all MCP tools: semantic search, file skeleton, symbol usage lookup, and image description.
|
|
3
3
|
*/
|
|
4
|
-
import type
|
|
4
|
+
import { type EmbeddingProvider, type VectorStore, type KeywordIndex, type SearchResult } from "../core/interfaces.js";
|
|
5
5
|
import type { RagConfig } from "../core/config.js";
|
|
6
6
|
import { type ImageVisionProvider } from "../chunker/image.js";
|
|
7
7
|
import { retrieve } from "../retriever/retriever.js";
|
package/dist/mcp/handlers.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Handler implementations for all MCP tools: semantic search, file skeleton, symbol usage lookup, and image description.
|
|
3
|
+
*/
|
|
4
|
+
import { CODE_SEARCH_FILTER } from "../core/interfaces.js";
|
|
1
5
|
import { SUPPORTED_IMAGE_EXTENSIONS } from "../chunker/image.js";
|
|
2
6
|
import { retrieve } from "../retriever/retriever.js";
|
|
3
7
|
import { optimizeContext, DEFAULT_CONTEXT_OPTIMIZATION } from "../retriever/context-optimizer.js";
|
|
4
8
|
import { Parser } from "web-tree-sitter";
|
|
5
9
|
import { initParser, loadLanguage, walkTree } from "../chunker/grammar.js";
|
|
6
10
|
import { readFileSync } from "node:fs";
|
|
7
|
-
import { resolve, isAbsolute, relative } from "node:path";
|
|
11
|
+
import { resolve, isAbsolute, relative, sep as pathSep } from "node:path";
|
|
8
12
|
/**
|
|
9
13
|
* Resolve a user-supplied file path against the worktree root, refusing to
|
|
10
14
|
* escape the worktree. Absolute paths are accepted only if they already reside
|
|
@@ -19,7 +23,9 @@ export function resolveFilePath(filePath, worktree) {
|
|
|
19
23
|
const rel = relative(root, candidate);
|
|
20
24
|
if (rel === "")
|
|
21
25
|
return root;
|
|
22
|
-
|
|
26
|
+
// ".." or "../..." escapes; a sibling literally named "..foo" must NOT be
|
|
27
|
+
// rejected. isAbsolute(rel) covers cross-drive paths on Windows.
|
|
28
|
+
if (rel === ".." || rel.startsWith(`..${pathSep}`) || isAbsolute(rel)) {
|
|
23
29
|
throw new Error(`Path "${filePath}" escapes worktree root "${root}" — access denied.`);
|
|
24
30
|
}
|
|
25
31
|
return candidate;
|
|
@@ -178,6 +184,7 @@ export async function handleSearchSemantic(params, embedder, store, cfg, keyword
|
|
|
178
184
|
keywordWeight: cfg.retrieval.hybridSearch?.keywordWeight,
|
|
179
185
|
hybridEnabled: cfg.retrieval.hybridSearch?.enabled,
|
|
180
186
|
queryPrefix: cfg.embedding.queryPrefix,
|
|
187
|
+
filter: CODE_SEARCH_FILTER,
|
|
181
188
|
};
|
|
182
189
|
const rawResults = await retrieveFn_(query, embedder, store, retrieveOpts);
|
|
183
190
|
if (rawResults.length === 0) {
|
|
@@ -256,7 +263,7 @@ export async function handleFindUsages(params, embedder, store, cfg, keywordInde
|
|
|
256
263
|
const symbolName = params.symbolName.trim();
|
|
257
264
|
const topK = params.topK ?? 30;
|
|
258
265
|
const kwResults = keywordIndex
|
|
259
|
-
? keywordIndex.search(symbolName, topK)
|
|
266
|
+
? keywordIndex.search(symbolName, topK, CODE_SEARCH_FILTER)
|
|
260
267
|
: [];
|
|
261
268
|
const count = await store.count();
|
|
262
269
|
const vsResults = count > 0
|
|
@@ -265,6 +272,7 @@ export async function handleFindUsages(params, embedder, store, cfg, keywordInde
|
|
|
265
272
|
minScore: 0,
|
|
266
273
|
keywordIndex: undefined,
|
|
267
274
|
queryPrefix: cfg.embedding.queryPrefix,
|
|
275
|
+
filter: CODE_SEARCH_FILTER,
|
|
268
276
|
})
|
|
269
277
|
: [];
|
|
270
278
|
const seen = new Set();
|
|
@@ -302,7 +310,10 @@ export async function handleFindUsages(params, embedder, store, cfg, keywordInde
|
|
|
302
310
|
}
|
|
303
311
|
for (let i = 0; i < lines.length; i++) {
|
|
304
312
|
const line = lines[i];
|
|
305
|
-
|
|
313
|
+
// tree-sitter line numbers are absolute file lines (startLine + offset),
|
|
314
|
+
// NOT chunk-relative — chunk-relative numbers were wrong for every
|
|
315
|
+
// chunk not starting at line 1 and broke the definition skip.
|
|
316
|
+
const lineNum = m.startLine + i;
|
|
306
317
|
if (definitionLine !== undefined && lineNum === definitionLine)
|
|
307
318
|
continue;
|
|
308
319
|
const symbolPattern = new RegExp(`\\b${escapeRegex(symbolName)}\\b`);
|
|
@@ -317,7 +328,12 @@ export async function handleFindUsages(params, embedder, store, cfg, keywordInde
|
|
|
317
328
|
fileGroups.set(m.filePath, group);
|
|
318
329
|
}
|
|
319
330
|
if (!group.usages.some((g) => g.line === lineNum)) {
|
|
320
|
-
group.usages.push({
|
|
331
|
+
group.usages.push({
|
|
332
|
+
line: lineNum,
|
|
333
|
+
content: line.trimEnd(),
|
|
334
|
+
context: contextLines,
|
|
335
|
+
matchedIndex: i - ctxStart,
|
|
336
|
+
});
|
|
321
337
|
}
|
|
322
338
|
}
|
|
323
339
|
}
|
|
@@ -340,7 +356,8 @@ export async function handleFindUsages(params, embedder, store, cfg, keywordInde
|
|
|
340
356
|
formattedLines.push("|------|------|");
|
|
341
357
|
for (const usage of group.usages) {
|
|
342
358
|
const ctx = usage.context;
|
|
343
|
-
|
|
359
|
+
// Show the actual matched line (not an arbitrary middle context line)
|
|
360
|
+
const codeLine = ctx[usage.matchedIndex] ?? usage.content;
|
|
344
361
|
const escaped = codeLine.length > 100 ? codeLine.slice(0, 97) + "..." : codeLine;
|
|
345
362
|
formattedLines.push(`| ${usage.line} | \`${escaped}\` |`);
|
|
346
363
|
allMatches.push({ ...usage, filePath, language: group.language });
|
package/dist/mcp/server.js
CHANGED
|
@@ -97,6 +97,9 @@ export async function createMcpServer(options) {
|
|
|
97
97
|
await server.close();
|
|
98
98
|
await ctx.store.close();
|
|
99
99
|
ctx.keywordIndex.close();
|
|
100
|
+
// Release keep-alive sockets held by the embedder/description providers
|
|
101
|
+
const { destroyAllPooledConnections } = await import("../embedder/http.js");
|
|
102
|
+
destroyAllPooledConnections();
|
|
100
103
|
},
|
|
101
104
|
};
|
|
102
105
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @fileoverview Creates the RAG-backed read tool that returns full file contents with supplementary semantic context.
|
|
3
3
|
*/
|
|
4
4
|
import { tool } from "@opencode-ai/plugin/tool";
|
|
5
|
-
import type
|
|
5
|
+
import { type EmbeddingProvider, type KeywordIndex, type VectorStore, type SearchResult } from "../core/interfaces.js";
|
|
6
6
|
import type { RagConfig } from "../core/config.js";
|
|
7
7
|
export interface RagReadToolOptions {
|
|
8
8
|
/** Workspace root directory. */
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import fs from "node:fs/promises";
|
|
5
5
|
import { tool } from "@opencode-ai/plugin/tool";
|
|
6
|
+
import { CODE_SEARCH_FILTER } from "../core/interfaces.js";
|
|
6
7
|
import { retrieve } from "../retriever/retriever.js";
|
|
7
8
|
import { normalizeReadArgs, resolveWorkspacePath } from "./tool-args.js";
|
|
8
9
|
import { buildReadQuery } from "./read-query.js";
|
|
@@ -58,19 +59,23 @@ export function createRagReadTool(options) {
|
|
|
58
59
|
let rawResults;
|
|
59
60
|
if (sessionID && sessionRetrievalCache) {
|
|
60
61
|
const cached = sessionRetrievalCache.get(sessionID);
|
|
61
|
-
|
|
62
|
+
// The cache key must include the FILE and line range: without
|
|
63
|
+
// them, the first read of a message reused its retrieval results
|
|
64
|
+
// for every later read of a DIFFERENT file in the same message.
|
|
65
|
+
const cacheKey = `${messageText}::${resolvedPath}::${normalized.startLine ?? ""}-${normalized.endLine ?? ""}`;
|
|
66
|
+
if (cached && cached.messageText === cacheKey) {
|
|
62
67
|
rawResults = cached.rawResults;
|
|
63
68
|
}
|
|
64
69
|
else {
|
|
65
70
|
const retrievalQuery = buildSessionQuery(messageText, resolvedPath, normalized);
|
|
66
|
-
rawResults = await retrieve(retrievalQuery, embedder, store, { topK: retrievalTopK, keywordIndex, hybridEnabled: config.retrieval.hybridSearch?.enabled, queryPrefix: config.embedding.queryPrefix });
|
|
71
|
+
rawResults = await retrieve(retrievalQuery, embedder, store, { topK: retrievalTopK, keywordIndex, hybridEnabled: config.retrieval.hybridSearch?.enabled, queryPrefix: config.embedding.queryPrefix, filter: CODE_SEARCH_FILTER });
|
|
67
72
|
const maxSize = options.maxSessionCacheSize ?? 50;
|
|
68
73
|
if (!sessionRetrievalCache.has(sessionID) && sessionRetrievalCache.size >= maxSize) {
|
|
69
74
|
const oldest = sessionRetrievalCache.keys().next().value;
|
|
70
75
|
if (oldest !== undefined)
|
|
71
76
|
sessionRetrievalCache.delete(oldest);
|
|
72
77
|
}
|
|
73
|
-
sessionRetrievalCache.set(sessionID, { messageText, rawResults });
|
|
78
|
+
sessionRetrievalCache.set(sessionID, { messageText: cacheKey, rawResults });
|
|
74
79
|
}
|
|
75
80
|
}
|
|
76
81
|
else {
|
|
@@ -80,7 +85,7 @@ export function createRagReadTool(options) {
|
|
|
80
85
|
startLine: normalized.startLine,
|
|
81
86
|
endLine: normalized.endLine,
|
|
82
87
|
});
|
|
83
|
-
rawResults = await retrieve(retrievalQuery, embedder, store, { topK: retrievalTopK, keywordIndex, hybridEnabled: config.retrieval.hybridSearch?.enabled, queryPrefix: config.embedding.queryPrefix });
|
|
88
|
+
rawResults = await retrieve(retrievalQuery, embedder, store, { topK: retrievalTopK, keywordIndex, hybridEnabled: config.retrieval.hybridSearch?.enabled, queryPrefix: config.embedding.queryPrefix, filter: CODE_SEARCH_FILTER });
|
|
84
89
|
}
|
|
85
90
|
// Collect related files from raw results (before filtering)
|
|
86
91
|
relatedFiles = collectRelatedFiles(rawResults, resolvedPath, readRelatedFilesMax);
|
|
@@ -120,9 +125,16 @@ export function createRagReadTool(options) {
|
|
|
120
125
|
}
|
|
121
126
|
catch (err) {
|
|
122
127
|
const message = err instanceof Error ? err.message : String(err);
|
|
128
|
+
const code = err?.code;
|
|
129
|
+
// Distinguish path problems (ENOENT / outside workspace) from RAG
|
|
130
|
+
// retrieval failures — "OpenCodeRAG retrieval failed" was misleading
|
|
131
|
+
// for a simple missing file.
|
|
132
|
+
const isPathProblem = code === "ENOENT" || code === "EACCES" || /ENOENT|outside the workspace|does not exist/i.test(message);
|
|
123
133
|
return {
|
|
124
134
|
title: "Read",
|
|
125
|
-
output:
|
|
135
|
+
output: isPathProblem
|
|
136
|
+
? `Could not read file: ${message}`
|
|
137
|
+
: retrievalErrorMessage(message),
|
|
126
138
|
metadata: {
|
|
127
139
|
tool: "read",
|
|
128
140
|
filePath: resolvedPath,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* @fileoverview Normalizes and validates read tool arguments, and resolves file paths relative to the workspace root.
|
|
3
3
|
*/
|
|
4
4
|
import path from "node:path";
|
|
5
|
+
import { realpathSync } from "node:fs";
|
|
5
6
|
/**
|
|
6
7
|
* Normalize raw read tool arguments into a consistent internal shape.
|
|
7
8
|
*
|
|
@@ -58,10 +59,31 @@ export function resolveWorkspacePath(worktree, inputPath) {
|
|
|
58
59
|
: path.resolve(worktree, inputPath);
|
|
59
60
|
const normalizedWorktree = path.resolve(worktree);
|
|
60
61
|
const normalizedResolved = path.resolve(resolved);
|
|
61
|
-
|
|
62
|
+
// Case-insensitive on win32: an absolute path with different casing must
|
|
63
|
+
// not be falsely rejected as "outside the workspace".
|
|
64
|
+
const cmp = (p) => (process.platform === "win32" ? p.toLowerCase() : p);
|
|
65
|
+
if (!cmp(normalizedResolved).startsWith(cmp(normalizedWorktree) + path.sep) &&
|
|
62
66
|
normalizedResolved !== normalizedWorktree) {
|
|
63
67
|
throw new Error(`read path "${inputPath}" resolves outside the workspace "${normalizedWorktree}"`);
|
|
64
68
|
}
|
|
69
|
+
// Resolve symlinks/junctions: a link INSIDE the workspace pointing outside
|
|
70
|
+
// must not let the agent read arbitrary files (node_modules junctions are
|
|
71
|
+
// common on Windows). realpath failure (e.g. missing file) falls back to
|
|
72
|
+
// the lexical check above — the read itself will fail with ENOENT.
|
|
73
|
+
try {
|
|
74
|
+
const realResolved = realpathSync(normalizedResolved);
|
|
75
|
+
const realWorktree = realpathSync(normalizedWorktree);
|
|
76
|
+
if (!cmp(realResolved).startsWith(cmp(realWorktree) + path.sep) &&
|
|
77
|
+
realResolved !== realWorktree) {
|
|
78
|
+
throw new Error(`read path "${inputPath}" resolves outside the workspace "${normalizedWorktree}" (via symlink "${realResolved}")`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
if (err instanceof Error && /outside the workspace/.test(err.message)) {
|
|
83
|
+
throw err;
|
|
84
|
+
}
|
|
85
|
+
// otherwise: realpath unavailable — keep the lexical result
|
|
86
|
+
}
|
|
65
87
|
return toForwardSlash(normalizedResolved);
|
|
66
88
|
}
|
|
67
89
|
/**
|
package/dist/opencode/tools.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @fileoverview Factory functions for OpenCode autonomous-agent tools (file skeleton, find usages, describe image).
|
|
3
3
|
*/
|
|
4
4
|
import type { ToolDefinition } from "@opencode-ai/plugin";
|
|
5
|
-
import type
|
|
5
|
+
import { type EmbeddingProvider, type VectorStore, type KeywordIndex } from "../core/interfaces.js";
|
|
6
6
|
import type { RagConfig } from "../core/config.js";
|
|
7
7
|
import { type ImageVisionProvider } from "../chunker/image.js";
|
|
8
8
|
import { retrieve } from "../retriever/retriever.js";
|
package/dist/opencode/tools.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* These complement the general-purpose search_semantic tool.
|
|
14
14
|
*/
|
|
15
15
|
import { tool } from "@opencode-ai/plugin/tool";
|
|
16
|
+
import { CODE_SEARCH_FILTER } from "../core/interfaces.js";
|
|
16
17
|
import { SUPPORTED_IMAGE_EXTENSIONS, createImageVisionProvider, getMimeType } from "../chunker/image.js";
|
|
17
18
|
import { resizeImage } from "../content/image.js";
|
|
18
19
|
import { retrieve } from "../retriever/retriever.js";
|
|
@@ -362,7 +363,7 @@ export function createFindUsagesTool(options) {
|
|
|
362
363
|
async function searchViaKeywordIndex(symbol, topK) {
|
|
363
364
|
if (!keywordIndex)
|
|
364
365
|
return [];
|
|
365
|
-
return keywordIndex.search(symbol, topK);
|
|
366
|
+
return keywordIndex.search(symbol, topK, CODE_SEARCH_FILTER);
|
|
366
367
|
}
|
|
367
368
|
async function searchViaVectorStore(symbol, topK) {
|
|
368
369
|
const count = await store.count();
|
|
@@ -373,6 +374,7 @@ export function createFindUsagesTool(options) {
|
|
|
373
374
|
minScore: 0,
|
|
374
375
|
keywordIndex: undefined,
|
|
375
376
|
queryPrefix: cfg.embedding.queryPrefix,
|
|
377
|
+
filter: CODE_SEARCH_FILTER,
|
|
376
378
|
});
|
|
377
379
|
}
|
|
378
380
|
function escapeRegex(s) {
|
package/dist/plugin.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* automatic context injection and documentation mode.
|
|
5
5
|
*/
|
|
6
6
|
import type { Plugin, Hooks } from "@opencode-ai/plugin";
|
|
7
|
-
import type
|
|
7
|
+
import { type EmbeddingProvider, type DescriptionProvider, type KeywordIndex, type VectorStore } from "./core/interfaces.js";
|
|
8
8
|
import { type RagConfig } from "./core/config.js";
|
|
9
9
|
import { createEmbedder } from "./embedder/factory.js";
|
|
10
10
|
import { retrieve } from "./retriever/retriever.js";
|