opencode-rag-plugin 1.1.0 → 1.2.2
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/ReadMe.md +53 -14
- package/dist/chunker/doc.d.ts +8 -0
- package/dist/chunker/doc.js +79 -0
- package/dist/chunker/doc.js.map +1 -0
- package/dist/chunker/docx.d.ts +8 -0
- package/dist/chunker/docx.js +78 -0
- package/dist/chunker/docx.js.map +1 -0
- package/dist/chunker/excel.d.ts +8 -0
- package/dist/chunker/excel.js +78 -0
- package/dist/chunker/excel.js.map +1 -0
- package/dist/chunker/factory.d.ts +1 -0
- package/dist/chunker/factory.js +9 -0
- package/dist/chunker/factory.js.map +1 -1
- package/dist/cli.d.ts +7 -0
- package/dist/cli.js +291 -5
- package/dist/cli.js.map +1 -1
- package/dist/core/config.d.ts +4 -0
- package/dist/core/config.js +6 -1
- package/dist/core/config.js.map +1 -1
- package/dist/indexer.js +18 -0
- package/dist/indexer.js.map +1 -1
- package/dist/opencode/create-read-tool.d.ts +30 -0
- package/dist/opencode/create-read-tool.js +248 -0
- package/dist/opencode/create-read-tool.js.map +1 -0
- package/dist/opencode/read-fallback.d.ts +21 -0
- package/dist/opencode/read-fallback.js +90 -0
- package/dist/opencode/read-fallback.js.map +1 -0
- package/dist/opencode/read-format.d.ts +40 -0
- package/dist/opencode/read-format.js +86 -0
- package/dist/opencode/read-format.js.map +1 -0
- package/dist/opencode/read-query.d.ts +26 -0
- package/dist/opencode/read-query.js +39 -0
- package/dist/opencode/read-query.js.map +1 -0
- package/dist/opencode/tool-args.d.ts +51 -0
- package/dist/opencode/tool-args.js +70 -0
- package/dist/opencode/tool-args.js.map +1 -0
- package/dist/plugin.js +2 -1
- package/dist/plugin.js.map +1 -1
- package/package.json +11 -4
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { tool } from "@opencode-ai/plugin/tool";
|
|
2
|
+
import { retrieve } from "../retriever/retriever.js";
|
|
3
|
+
import { normalizeReadArgs, resolveWorkspacePath } from "./tool-args.js";
|
|
4
|
+
import { buildReadQuery } from "./read-query.js";
|
|
5
|
+
import { formatReadOutput, formatRelatedFiles } from "./read-format.js";
|
|
6
|
+
import { missingIndexMessage, getNoResultsMessage, retrievalErrorMessage, } from "./read-fallback.js";
|
|
7
|
+
/**
|
|
8
|
+
* Create the RAG-backed read tool for OpenCode plugin registration.
|
|
9
|
+
*
|
|
10
|
+
* The tool accepts read-like arguments (filePath/path/absolutePath,
|
|
11
|
+
* offset/limit, startLine/endLine, query/reason) and returns
|
|
12
|
+
* relevant indexed chunks instead of full file contents.
|
|
13
|
+
*/
|
|
14
|
+
export function createRagReadTool(options) {
|
|
15
|
+
const { worktree, config, embedder, store, sessionLastMessage, sessionRetrievalCache } = options;
|
|
16
|
+
const openCodeCfg = config.openCode;
|
|
17
|
+
const maxContextChunks = openCodeCfg.maxContextChunks;
|
|
18
|
+
const maxReadOutputChars = openCodeCfg.maxReadOutputChars ?? 20000;
|
|
19
|
+
const noResultsBehavior = openCodeCfg.readNoResultsBehavior ?? "hint";
|
|
20
|
+
const retrievalTopK = maxContextChunks * 4;
|
|
21
|
+
const readRelatedFilesMax = openCodeCfg.readRelatedFilesMax ?? 5;
|
|
22
|
+
return tool({
|
|
23
|
+
description: "Read file contents from the workspace. Returns relevant indexed code chunks " +
|
|
24
|
+
"instead of full file contents for token-efficient code understanding. " +
|
|
25
|
+
"Provide a file path and optionally a query/reason, line range, or both.",
|
|
26
|
+
args: {
|
|
27
|
+
filePath: tool.schema.string().optional(),
|
|
28
|
+
path: tool.schema.string().optional(),
|
|
29
|
+
absolutePath: tool.schema.string().optional(),
|
|
30
|
+
offset: tool.schema.number().int().optional(),
|
|
31
|
+
limit: tool.schema.number().int().optional(),
|
|
32
|
+
startLine: tool.schema.number().int().optional(),
|
|
33
|
+
endLine: tool.schema.number().int().optional(),
|
|
34
|
+
query: tool.schema.string().optional(),
|
|
35
|
+
reason: tool.schema.string().optional(),
|
|
36
|
+
},
|
|
37
|
+
async execute(args, ctx) {
|
|
38
|
+
try {
|
|
39
|
+
// 1. Normalize and validate arguments
|
|
40
|
+
const normalized = normalizeReadArgs(args);
|
|
41
|
+
// 2. Resolve workspace path
|
|
42
|
+
const resolvedPath = resolveWorkspacePath(worktree, normalized.filePath);
|
|
43
|
+
// 3. Build retrieval query (chat-context-aware if available)
|
|
44
|
+
const sessionID = ctx?.sessionID;
|
|
45
|
+
const messageText = sessionID ? sessionLastMessage?.get(sessionID) ?? "" : "";
|
|
46
|
+
// 4. Check if index exists
|
|
47
|
+
const count = await store.count();
|
|
48
|
+
if (count === 0) {
|
|
49
|
+
return {
|
|
50
|
+
title: "Read (OpenCodeRAG)",
|
|
51
|
+
output: missingIndexMessage(),
|
|
52
|
+
metadata: {
|
|
53
|
+
tool: "read",
|
|
54
|
+
filePath: resolvedPath,
|
|
55
|
+
indexed: false,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// 5. Get or create cached raw results for this session
|
|
60
|
+
let rawResults;
|
|
61
|
+
let retrievalQuery;
|
|
62
|
+
if (sessionID && sessionRetrievalCache) {
|
|
63
|
+
const cached = sessionRetrievalCache.get(sessionID);
|
|
64
|
+
if (cached && cached.messageText === messageText) {
|
|
65
|
+
// Cache hit — reuse raw results
|
|
66
|
+
rawResults = cached.rawResults;
|
|
67
|
+
retrievalQuery = messageText.length > 0
|
|
68
|
+
? messageText
|
|
69
|
+
: (buildReadQuery({ filePath: resolvedPath }).split("\n")[0] ?? "");
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
// Cache miss or new message — run retrieval
|
|
73
|
+
retrievalQuery = buildSessionQuery(messageText, resolvedPath, normalized);
|
|
74
|
+
rawResults = await retrieve(retrievalQuery, embedder, store, { topK: retrievalTopK });
|
|
75
|
+
sessionRetrievalCache.set(sessionID, { messageText, rawResults: rawResults });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
// No session/context — direct retrieval
|
|
80
|
+
retrievalQuery = buildReadQuery({
|
|
81
|
+
query: normalized.query,
|
|
82
|
+
filePath: resolvedPath,
|
|
83
|
+
startLine: normalized.startLine,
|
|
84
|
+
endLine: normalized.endLine,
|
|
85
|
+
});
|
|
86
|
+
rawResults = await retrieve(retrievalQuery, embedder, store, { topK: retrievalTopK });
|
|
87
|
+
}
|
|
88
|
+
// 6. Collect related files from raw results (before filtering)
|
|
89
|
+
const relatedFiles = collectRelatedFiles(rawResults, resolvedPath, readRelatedFilesMax);
|
|
90
|
+
if (rawResults.length === 0) {
|
|
91
|
+
const output = getNoResultsMessage(noResultsBehavior, resolvedPath);
|
|
92
|
+
return {
|
|
93
|
+
title: "Read (OpenCodeRAG)",
|
|
94
|
+
output,
|
|
95
|
+
metadata: {
|
|
96
|
+
tool: "read",
|
|
97
|
+
filePath: resolvedPath,
|
|
98
|
+
chunks: 0,
|
|
99
|
+
indexed: true,
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
// 7. Filter results to the requested file
|
|
104
|
+
let filtered = rawResults.filter((r) => r.chunk.metadata.filePath === resolvedPath);
|
|
105
|
+
// 8. If file has no results, use no-results behavior
|
|
106
|
+
if (filtered.length === 0) {
|
|
107
|
+
let output = getNoResultsMessage(noResultsBehavior, resolvedPath);
|
|
108
|
+
if (readRelatedFilesMax > 0 && relatedFiles.length > 0) {
|
|
109
|
+
output += "\n\n" + formatRelatedFiles(relatedFiles);
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
title: "Read (OpenCodeRAG)",
|
|
113
|
+
output,
|
|
114
|
+
metadata: {
|
|
115
|
+
tool: "read",
|
|
116
|
+
filePath: resolvedPath,
|
|
117
|
+
chunks: 0,
|
|
118
|
+
indexed: true,
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
// 9. Apply line-range overlap filtering
|
|
123
|
+
const lineFiltered = applyLineRangeFilter(filtered, normalized.startLine, normalized.endLine);
|
|
124
|
+
// Re-sort by score descending
|
|
125
|
+
lineFiltered.sort((a, b) => b.score - a.score);
|
|
126
|
+
// 10. Format output
|
|
127
|
+
let output = formatReadOutput({
|
|
128
|
+
filePath: resolvedPath,
|
|
129
|
+
retrievalQuery,
|
|
130
|
+
results: lineFiltered,
|
|
131
|
+
maxChunks: maxContextChunks,
|
|
132
|
+
maxChars: maxReadOutputChars,
|
|
133
|
+
});
|
|
134
|
+
// 11. Append related files if enabled
|
|
135
|
+
if (readRelatedFilesMax > 0 && relatedFiles.length > 0) {
|
|
136
|
+
output += "\n\n" + formatRelatedFiles(relatedFiles);
|
|
137
|
+
}
|
|
138
|
+
// 12. Return
|
|
139
|
+
return {
|
|
140
|
+
title: `Read (OpenCodeRAG) — ${resolvedPath}`,
|
|
141
|
+
output,
|
|
142
|
+
metadata: {
|
|
143
|
+
tool: "read",
|
|
144
|
+
filePath: resolvedPath,
|
|
145
|
+
chunks: Math.min(lineFiltered.length, maxContextChunks),
|
|
146
|
+
totalResults: lineFiltered.length,
|
|
147
|
+
indexed: true,
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
153
|
+
return {
|
|
154
|
+
title: "Read (OpenCodeRAG)",
|
|
155
|
+
output: retrievalErrorMessage(message),
|
|
156
|
+
metadata: {
|
|
157
|
+
tool: "read",
|
|
158
|
+
filePath: undefined,
|
|
159
|
+
error: message,
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Filter search results by line-range overlap.
|
|
168
|
+
*
|
|
169
|
+
* A chunk overlaps the requested range when:
|
|
170
|
+
* chunk.startLine <= requestedEndLine && chunk.endLine >= requestedStartLine
|
|
171
|
+
*
|
|
172
|
+
* If only startLine is provided: chunk.endLine >= requestedStartLine
|
|
173
|
+
* If only endLine is provided: chunk.startLine <= requestedEndLine
|
|
174
|
+
*/
|
|
175
|
+
function applyLineRangeFilter(results, startLine, endLine) {
|
|
176
|
+
if (startLine === undefined && endLine === undefined) {
|
|
177
|
+
return results;
|
|
178
|
+
}
|
|
179
|
+
return results.filter((r) => {
|
|
180
|
+
const cs = r.chunk.metadata.startLine;
|
|
181
|
+
const ce = r.chunk.metadata.endLine;
|
|
182
|
+
if (startLine !== undefined && endLine !== undefined) {
|
|
183
|
+
return cs <= endLine && ce >= startLine;
|
|
184
|
+
}
|
|
185
|
+
if (startLine !== undefined) {
|
|
186
|
+
return ce >= startLine;
|
|
187
|
+
}
|
|
188
|
+
if (endLine !== undefined) {
|
|
189
|
+
return cs <= endLine;
|
|
190
|
+
}
|
|
191
|
+
return true;
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Build a retrieval query from the session's user message plus file path hints.
|
|
196
|
+
*
|
|
197
|
+
* When message text is available, it becomes the primary semantic query with
|
|
198
|
+
* the file path as a targeted hint. When no message text exists, falls back
|
|
199
|
+
* to the standard file-path-based query.
|
|
200
|
+
*/
|
|
201
|
+
function buildSessionQuery(messageText, resolvedPath, normalized) {
|
|
202
|
+
// Use message text as the semantic query if available
|
|
203
|
+
if (messageText.length > 0) {
|
|
204
|
+
// Include file path info so the embedding narrows to that file
|
|
205
|
+
const parts = [
|
|
206
|
+
messageText,
|
|
207
|
+
`Looking for relevant code in file: ${resolvedPath}`,
|
|
208
|
+
];
|
|
209
|
+
if (normalized.startLine !== undefined && normalized.endLine !== undefined) {
|
|
210
|
+
parts.push(`Focus on lines ${normalized.startLine}-${normalized.endLine}.`);
|
|
211
|
+
}
|
|
212
|
+
else if (normalized.startLine !== undefined) {
|
|
213
|
+
parts.push(`Focus on lines near ${normalized.startLine}.`);
|
|
214
|
+
}
|
|
215
|
+
return parts.join("\n");
|
|
216
|
+
}
|
|
217
|
+
// Fall back to standard query
|
|
218
|
+
return buildReadQuery({
|
|
219
|
+
query: normalized.query,
|
|
220
|
+
filePath: resolvedPath,
|
|
221
|
+
startLine: normalized.startLine,
|
|
222
|
+
endLine: normalized.endLine,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Collect unique related files from raw search results, excluding the
|
|
227
|
+
* requested file. Keeps the best score per file path and returns at most
|
|
228
|
+
* `maxRelated` entries sorted by score descending.
|
|
229
|
+
*/
|
|
230
|
+
function collectRelatedFiles(rawResults, requestedFile, maxRelated) {
|
|
231
|
+
if (maxRelated <= 0 || rawResults.length === 0)
|
|
232
|
+
return [];
|
|
233
|
+
const bestScore = new Map();
|
|
234
|
+
for (const r of rawResults) {
|
|
235
|
+
const fp = r.chunk.metadata.filePath;
|
|
236
|
+
if (fp === requestedFile)
|
|
237
|
+
continue;
|
|
238
|
+
const current = bestScore.get(fp);
|
|
239
|
+
if (current === undefined || r.score > current) {
|
|
240
|
+
bestScore.set(fp, r.score);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return Array.from(bestScore.entries())
|
|
244
|
+
.map(([filePath, score]) => ({ filePath, score }))
|
|
245
|
+
.sort((a, b) => b.score - a.score)
|
|
246
|
+
.slice(0, maxRelated);
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=create-read-tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-read-tool.js","sourceRoot":"","sources":["../../src/opencode/create-read-tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ReadNoResultsBehavior } from "../core/config.js";
|
|
2
|
+
/**
|
|
3
|
+
* Fallback message when no OpenCodeRAG index exists or the index is empty.
|
|
4
|
+
*/
|
|
5
|
+
export declare function missingIndexMessage(): string;
|
|
6
|
+
/**
|
|
7
|
+
* Fallback message when the requested file has no indexed chunks.
|
|
8
|
+
*/
|
|
9
|
+
export declare function fileNotIndexedMessage(filePath: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Fallback message when no relevant chunks matched the read request.
|
|
12
|
+
*/
|
|
13
|
+
export declare function noRelevantChunksMessage(): string;
|
|
14
|
+
/**
|
|
15
|
+
* Error message wrapper for retrieval failures.
|
|
16
|
+
*/
|
|
17
|
+
export declare function retrievalErrorMessage(shortError: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Dispatch to the correct fallback message based on behavior config.
|
|
20
|
+
*/
|
|
21
|
+
export declare function getNoResultsMessage(behavior: ReadNoResultsBehavior, filePath?: string): string;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fallback message when no OpenCodeRAG index exists or the index is empty.
|
|
3
|
+
*/
|
|
4
|
+
export function missingIndexMessage() {
|
|
5
|
+
return [
|
|
6
|
+
"OpenCodeRAG read override active.",
|
|
7
|
+
"Full file read suppressed.",
|
|
8
|
+
"",
|
|
9
|
+
"No OpenCodeRAG index was found or the index is empty.",
|
|
10
|
+
"",
|
|
11
|
+
"Run:",
|
|
12
|
+
"",
|
|
13
|
+
"```bash",
|
|
14
|
+
"npx tsx src/cli.ts index",
|
|
15
|
+
"```",
|
|
16
|
+
"",
|
|
17
|
+
"Then retry the read request.",
|
|
18
|
+
].join("\n");
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Fallback message when the requested file has no indexed chunks.
|
|
22
|
+
*/
|
|
23
|
+
export function fileNotIndexedMessage(filePath) {
|
|
24
|
+
return [
|
|
25
|
+
"OpenCodeRAG read override active.",
|
|
26
|
+
"Full file read suppressed.",
|
|
27
|
+
"",
|
|
28
|
+
"No indexed chunks were found for:",
|
|
29
|
+
`- ${filePath}`,
|
|
30
|
+
"",
|
|
31
|
+
"Possible reasons:",
|
|
32
|
+
"- The file extension is not included.",
|
|
33
|
+
"- The directory is excluded.",
|
|
34
|
+
"- The index is stale.",
|
|
35
|
+
"- The file was created after the last indexing run.",
|
|
36
|
+
"",
|
|
37
|
+
"Run:",
|
|
38
|
+
"",
|
|
39
|
+
"```bash",
|
|
40
|
+
"npx tsx src/cli.ts index",
|
|
41
|
+
"```",
|
|
42
|
+
].join("\n");
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Fallback message when no relevant chunks matched the read request.
|
|
46
|
+
*/
|
|
47
|
+
export function noRelevantChunksMessage() {
|
|
48
|
+
return [
|
|
49
|
+
"OpenCodeRAG read override active.",
|
|
50
|
+
"Full file read suppressed.",
|
|
51
|
+
"",
|
|
52
|
+
"No relevant chunks were found for this read request.",
|
|
53
|
+
"",
|
|
54
|
+
"Try:",
|
|
55
|
+
"- Ask a more specific question.",
|
|
56
|
+
"- Run the index again.",
|
|
57
|
+
"- Request a smaller line range if range fallback is enabled.",
|
|
58
|
+
].join("\n");
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Error message wrapper for retrieval failures.
|
|
62
|
+
*/
|
|
63
|
+
export function retrievalErrorMessage(shortError) {
|
|
64
|
+
return [
|
|
65
|
+
"OpenCodeRAG retrieval failed.",
|
|
66
|
+
"Full file read suppressed.",
|
|
67
|
+
"",
|
|
68
|
+
"Error:",
|
|
69
|
+
shortError,
|
|
70
|
+
].join("\n");
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Dispatch to the correct fallback message based on behavior config.
|
|
74
|
+
*/
|
|
75
|
+
export function getNoResultsMessage(behavior, filePath) {
|
|
76
|
+
switch (behavior) {
|
|
77
|
+
case "error":
|
|
78
|
+
throw new Error("OpenCodeRAG read: no indexed chunks found." +
|
|
79
|
+
(filePath ? ` File: ${filePath}` : ""));
|
|
80
|
+
case "empty":
|
|
81
|
+
return "OpenCodeRAG read override active. Full file read suppressed. No indexed chunks found.";
|
|
82
|
+
case "hint":
|
|
83
|
+
default:
|
|
84
|
+
if (filePath) {
|
|
85
|
+
return fileNotIndexedMessage(filePath);
|
|
86
|
+
}
|
|
87
|
+
return noRelevantChunksMessage();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=read-fallback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-fallback.js","sourceRoot":"","sources":["../../src/opencode/read-fallback.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAO;QACL,mCAAmC;QACnC,4BAA4B;QAC5B,EAAE;QACF,uDAAuD;QACvD,EAAE;QACF,MAAM;QACN,EAAE;QACF,SAAS;QACT,0BAA0B;QAC1B,KAAK;QACL,EAAE;QACF,8BAA8B;KAC/B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAgB;IACpD,OAAO;QACL,mCAAmC;QACnC,4BAA4B;QAC5B,EAAE;QACF,mCAAmC;QACnC,KAAK,QAAQ,EAAE;QACf,EAAE;QACF,mBAAmB;QACnB,uCAAuC;QACvC,8BAA8B;QAC9B,uBAAuB;QACvB,qDAAqD;QACrD,EAAE;QACF,MAAM;QACN,EAAE;QACF,SAAS;QACT,0BAA0B;QAC1B,KAAK;KACN,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,uBAAuB;IACrC,OAAO;QACL,mCAAmC;QACnC,4BAA4B;QAC5B,EAAE;QACF,sDAAsD;QACtD,EAAE;QACF,MAAM;QACN,iCAAiC;QACjC,wBAAwB;QACxB,8DAA8D;KAC/D,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,UAAkB;IACtD,OAAO;QACL,+BAA+B;QAC/B,4BAA4B;QAC5B,EAAE;QACF,QAAQ;QACR,UAAU;KACX,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CACjC,QAA+B,EAC/B,QAAiB;IAEjB,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,OAAO;YACV,MAAM,IAAI,KAAK,CACb,4CAA4C;gBAC1C,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CACzC,CAAC;QACJ,KAAK,OAAO;YACV,OAAO,uFAAuF,CAAC;QACjG,KAAK,MAAM,CAAC;QACZ;YACE,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,qBAAqB,CAAC,QAAQ,CAAC,CAAC;YACzC,CAAC;YACD,OAAO,uBAAuB,EAAE,CAAC;IACrC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { SearchResult } from "../core/interfaces.js";
|
|
2
|
+
/**
|
|
3
|
+
* Options for formatting read tool output.
|
|
4
|
+
*/
|
|
5
|
+
export interface FormatReadOutputOptions {
|
|
6
|
+
/** The requested file path (display-friendly). */
|
|
7
|
+
filePath: string;
|
|
8
|
+
/** The retrieval query used. */
|
|
9
|
+
retrievalQuery: string;
|
|
10
|
+
/** Search results to format. */
|
|
11
|
+
results: SearchResult[];
|
|
12
|
+
/** Maximum results to include. */
|
|
13
|
+
maxChunks: number;
|
|
14
|
+
/** Maximum output character count. */
|
|
15
|
+
maxChars: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Format RAG retrieval results for the read tool output.
|
|
19
|
+
*
|
|
20
|
+
* The output:
|
|
21
|
+
* - Clearly states full-file reading was suppressed.
|
|
22
|
+
* - Includes request metadata (file path, query, chunk count).
|
|
23
|
+
* - Formats each chunk with file path, line range, score, and code block.
|
|
24
|
+
* - Enforces maxChars limit and appends truncation notice.
|
|
25
|
+
*
|
|
26
|
+
* Returns a string ready to return as the tool output.
|
|
27
|
+
*/
|
|
28
|
+
export declare function formatReadOutput(options: FormatReadOutputOptions): string;
|
|
29
|
+
/** A related file entry with path and score. */
|
|
30
|
+
export interface RelatedFileEntry {
|
|
31
|
+
filePath: string;
|
|
32
|
+
score: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Format a list of related files as a lightweight suggestion section.
|
|
36
|
+
*
|
|
37
|
+
* Only includes file paths and scores — no code content — to keep tokens low.
|
|
38
|
+
* Format: "Please consider reading other relevant files:\n1. ./path (Score: 0.92)\n..."
|
|
39
|
+
*/
|
|
40
|
+
export declare function formatRelatedFiles(entries: RelatedFileEntry[]): string;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format RAG retrieval results for the read tool output.
|
|
3
|
+
*
|
|
4
|
+
* The output:
|
|
5
|
+
* - Clearly states full-file reading was suppressed.
|
|
6
|
+
* - Includes request metadata (file path, query, chunk count).
|
|
7
|
+
* - Formats each chunk with file path, line range, score, and code block.
|
|
8
|
+
* - Enforces maxChars limit and appends truncation notice.
|
|
9
|
+
*
|
|
10
|
+
* Returns a string ready to return as the tool output.
|
|
11
|
+
*/
|
|
12
|
+
export function formatReadOutput(options) {
|
|
13
|
+
const { filePath, retrievalQuery, results, maxChunks, maxChars } = options;
|
|
14
|
+
const header = buildHeader(filePath, retrievalQuery, results.length, maxChunks);
|
|
15
|
+
let output = header;
|
|
16
|
+
const limited = results.slice(0, maxChunks);
|
|
17
|
+
for (let i = 0; i < limited.length; i++) {
|
|
18
|
+
const r = limited[i];
|
|
19
|
+
if (!r)
|
|
20
|
+
continue;
|
|
21
|
+
const chunkPart = formatChunk(i + 1, r);
|
|
22
|
+
// Check if adding this chunk would exceed the limit
|
|
23
|
+
if ((output + "\n" + chunkPart).length > maxChars) {
|
|
24
|
+
// If we already have some content, append truncation notice
|
|
25
|
+
const truncationNotice = "\n\n---\nOutput truncated by OpenCodeRAG to stay within maxReadOutputChars.\nUse a more specific query or line range to retrieve narrower context.";
|
|
26
|
+
if ((output + truncationNotice).length <= maxChars) {
|
|
27
|
+
output += truncationNotice;
|
|
28
|
+
}
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
if (i > 0) {
|
|
32
|
+
output += "\n";
|
|
33
|
+
}
|
|
34
|
+
output += chunkPart;
|
|
35
|
+
}
|
|
36
|
+
return output;
|
|
37
|
+
}
|
|
38
|
+
function buildHeader(filePath, retrievalQuery, totalResults, maxChunks) {
|
|
39
|
+
const parts = [
|
|
40
|
+
"OpenCodeRAG read override active.",
|
|
41
|
+
"Full file read suppressed. Returning relevant indexed chunks instead.",
|
|
42
|
+
"",
|
|
43
|
+
"Requested file:",
|
|
44
|
+
`- ${filePath}`,
|
|
45
|
+
"",
|
|
46
|
+
"Retrieval query:",
|
|
47
|
+
`- ${retrievalQuery.split("\n")[0]}` +
|
|
48
|
+
(retrievalQuery.includes("\n") ? "..." : ""),
|
|
49
|
+
"",
|
|
50
|
+
`Returned chunks:`,
|
|
51
|
+
`- ${Math.min(totalResults, maxChunks)} of max ${maxChunks}`,
|
|
52
|
+
"",
|
|
53
|
+
];
|
|
54
|
+
return parts.join("\n");
|
|
55
|
+
}
|
|
56
|
+
function formatChunk(index, result) {
|
|
57
|
+
const { chunk, score } = result;
|
|
58
|
+
const metadata = chunk.metadata;
|
|
59
|
+
const language = metadata.language || "";
|
|
60
|
+
const lines = [];
|
|
61
|
+
lines.push(`## Chunk ${index}`);
|
|
62
|
+
lines.push(`File: ${metadata.filePath}`);
|
|
63
|
+
lines.push(`Lines: ${metadata.startLine}-${metadata.endLine}`);
|
|
64
|
+
lines.push(`Score: ${score.toFixed(4)}`);
|
|
65
|
+
lines.push("");
|
|
66
|
+
lines.push("```" + language);
|
|
67
|
+
lines.push(chunk.content);
|
|
68
|
+
if (!chunk.content.endsWith("\n")) {
|
|
69
|
+
// Ensure code block closes on its own line
|
|
70
|
+
}
|
|
71
|
+
lines.push("```");
|
|
72
|
+
return lines.join("\n");
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Format a list of related files as a lightweight suggestion section.
|
|
76
|
+
*
|
|
77
|
+
* Only includes file paths and scores — no code content — to keep tokens low.
|
|
78
|
+
* Format: "Please consider reading other relevant files:\n1. ./path (Score: 0.92)\n..."
|
|
79
|
+
*/
|
|
80
|
+
export function formatRelatedFiles(entries) {
|
|
81
|
+
if (entries.length === 0)
|
|
82
|
+
return "";
|
|
83
|
+
const lines = entries.map((entry, i) => `${i + 1}. ${entry.filePath} (Score: ${entry.score.toFixed(2)})`);
|
|
84
|
+
return `Please consider reading other relevant files:\n${lines.join("\n")}`;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=read-format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-format.js","sourceRoot":"","sources":["../../src/opencode/read-format.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a retrieval query string for the read tool.
|
|
3
|
+
*
|
|
4
|
+
* Combines user intent (optional query/reason) with file path
|
|
5
|
+
* and optional line range to create a focused retrieval query.
|
|
6
|
+
*/
|
|
7
|
+
export interface QueryBuilderOptions {
|
|
8
|
+
/** The user's explicit query or reason for reading. */
|
|
9
|
+
query?: string;
|
|
10
|
+
/** The normalized absolute file path being read. */
|
|
11
|
+
filePath: string;
|
|
12
|
+
/** Optional start line. */
|
|
13
|
+
startLine?: number;
|
|
14
|
+
/** Optional end line. */
|
|
15
|
+
endLine?: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Build a deterministic retrieval query from read arguments.
|
|
19
|
+
*
|
|
20
|
+
* Rules:
|
|
21
|
+
* - If query is provided: use it as prefix, then append file path instructions.
|
|
22
|
+
* - If no query: use a generic "Relevant implementation details" template.
|
|
23
|
+
* - If line range exists: append focus instruction.
|
|
24
|
+
* - If only startLine: append "at or after" instruction.
|
|
25
|
+
*/
|
|
26
|
+
export declare function buildReadQuery(options: QueryBuilderOptions): string;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a retrieval query string for the read tool.
|
|
3
|
+
*
|
|
4
|
+
* Combines user intent (optional query/reason) with file path
|
|
5
|
+
* and optional line range to create a focused retrieval query.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Build a deterministic retrieval query from read arguments.
|
|
9
|
+
*
|
|
10
|
+
* Rules:
|
|
11
|
+
* - If query is provided: use it as prefix, then append file path instructions.
|
|
12
|
+
* - If no query: use a generic "Relevant implementation details" template.
|
|
13
|
+
* - If line range exists: append focus instruction.
|
|
14
|
+
* - If only startLine: append "at or after" instruction.
|
|
15
|
+
*/
|
|
16
|
+
export function buildReadQuery(options) {
|
|
17
|
+
const { query, filePath, startLine, endLine } = options;
|
|
18
|
+
const parts = [];
|
|
19
|
+
if (query && query.trim().length > 0) {
|
|
20
|
+
parts.push(query.trim());
|
|
21
|
+
parts.push("");
|
|
22
|
+
parts.push(`File: ${filePath}`);
|
|
23
|
+
parts.push("Return relevant indexed code chunks from this file with line numbers.");
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
parts.push(`Relevant implementation details in file ${filePath}.`, "Return indexed code chunks with line numbers.");
|
|
27
|
+
}
|
|
28
|
+
if (startLine !== undefined && endLine !== undefined) {
|
|
29
|
+
parts.push(`Focus on chunks overlapping lines ${startLine}-${endLine}.`);
|
|
30
|
+
}
|
|
31
|
+
else if (startLine !== undefined) {
|
|
32
|
+
parts.push(`Focus on chunks at or after line ${startLine}.`);
|
|
33
|
+
}
|
|
34
|
+
else if (endLine !== undefined) {
|
|
35
|
+
parts.push(`Focus on chunks at or before line ${endLine}.`);
|
|
36
|
+
}
|
|
37
|
+
return parts.join("\n");
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=read-query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-query.js","sourceRoot":"","sources":["../../src/opencode/read-query.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAaH;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,OAA4B;IACzD,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IACxD,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,SAAS,QAAQ,EAAE,CAAC,CAAC;QAChC,KAAK,CAAC,IAAI,CACR,uEAAuE,CACxE,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CACR,2CAA2C,QAAQ,GAAG,EACtD,+CAA+C,CAChD,CAAC;IACJ,CAAC;IAED,IAAI,SAAS,KAAK,SAAS,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QACrD,KAAK,CAAC,IAAI,CACR,qCAAqC,SAAS,IAAI,OAAO,GAAG,CAC7D,CAAC;IACJ,CAAC;SAAM,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QACnC,KAAK,CAAC,IAAI,CAAC,oCAAoC,SAAS,GAAG,CAAC,CAAC;IAC/D,CAAC;SAAM,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QACjC,KAAK,CAAC,IAAI,CAAC,qCAAqC,OAAO,GAAG,CAAC,CAAC;IAC9D,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raw argument variants accepted by the read tool.
|
|
3
|
+
*/
|
|
4
|
+
export interface RawReadArgs {
|
|
5
|
+
filePath?: string;
|
|
6
|
+
path?: string;
|
|
7
|
+
absolutePath?: string;
|
|
8
|
+
offset?: number;
|
|
9
|
+
limit?: number;
|
|
10
|
+
startLine?: number;
|
|
11
|
+
endLine?: number;
|
|
12
|
+
query?: string;
|
|
13
|
+
reason?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Normalized internal shape for read request handling.
|
|
17
|
+
*/
|
|
18
|
+
export interface NormalizedReadArgs {
|
|
19
|
+
filePath: string;
|
|
20
|
+
startLine?: number;
|
|
21
|
+
endLine?: number;
|
|
22
|
+
query?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Normalize raw read tool arguments into a consistent internal shape.
|
|
26
|
+
*
|
|
27
|
+
* Accepts:
|
|
28
|
+
* - filePath / path / absolutePath
|
|
29
|
+
* - offset + limit (converted to startLine + endLine)
|
|
30
|
+
* - startLine / endLine (passthrough)
|
|
31
|
+
* - query / reason (passthrough)
|
|
32
|
+
*
|
|
33
|
+
* Validates:
|
|
34
|
+
* - A file path is required.
|
|
35
|
+
* - startLine/offset must be >= 1 when provided.
|
|
36
|
+
* - endLine must be >= 1 when provided.
|
|
37
|
+
* - endLine must be >= startLine when both are provided.
|
|
38
|
+
*/
|
|
39
|
+
export declare function normalizeReadArgs(args: RawReadArgs): NormalizedReadArgs;
|
|
40
|
+
/**
|
|
41
|
+
* Resolve a file path relative to the workspace root.
|
|
42
|
+
*
|
|
43
|
+
* Rules:
|
|
44
|
+
* - Absolute paths are normalized.
|
|
45
|
+
* - Relative paths are resolved relative to worktree.
|
|
46
|
+
* - The resolved path must be inside the workspace.
|
|
47
|
+
* - Returns absolute path with forward slashes.
|
|
48
|
+
*
|
|
49
|
+
* Throws if the path resolves outside the workspace.
|
|
50
|
+
*/
|
|
51
|
+
export declare function resolveWorkspacePath(worktree: string, inputPath: string): string;
|