opencode-rag-plugin 1.1.0 → 1.2.0

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.
Files changed (143) hide show
  1. package/ReadMe.md +463 -424
  2. package/dist/__tests__/chunker/c.test.d.ts +1 -0
  3. package/dist/__tests__/chunker/c.test.js +82 -0
  4. package/dist/__tests__/chunker/c.test.js.map +1 -0
  5. package/dist/__tests__/chunker/cpp.test.d.ts +1 -0
  6. package/dist/__tests__/chunker/cpp.test.js +96 -0
  7. package/dist/__tests__/chunker/cpp.test.js.map +1 -0
  8. package/dist/__tests__/chunker/csharp.test.d.ts +1 -0
  9. package/dist/__tests__/chunker/csharp.test.js +85 -0
  10. package/dist/__tests__/chunker/csharp.test.js.map +1 -0
  11. package/dist/__tests__/chunker/css.test.d.ts +1 -0
  12. package/dist/__tests__/chunker/css.test.js +64 -0
  13. package/dist/__tests__/chunker/css.test.js.map +1 -0
  14. package/dist/__tests__/chunker/factory.test.d.ts +1 -0
  15. package/dist/__tests__/chunker/factory.test.js +202 -0
  16. package/dist/__tests__/chunker/factory.test.js.map +1 -0
  17. package/dist/__tests__/chunker/fallback.test.d.ts +1 -0
  18. package/dist/__tests__/chunker/fallback.test.js +67 -0
  19. package/dist/__tests__/chunker/fallback.test.js.map +1 -0
  20. package/dist/__tests__/chunker/go.test.d.ts +1 -0
  21. package/dist/__tests__/chunker/go.test.js +90 -0
  22. package/dist/__tests__/chunker/go.test.js.map +1 -0
  23. package/dist/__tests__/chunker/grammar.test.d.ts +1 -0
  24. package/dist/__tests__/chunker/grammar.test.js +108 -0
  25. package/dist/__tests__/chunker/grammar.test.js.map +1 -0
  26. package/dist/__tests__/chunker/html.test.d.ts +1 -0
  27. package/dist/__tests__/chunker/html.test.js +64 -0
  28. package/dist/__tests__/chunker/html.test.js.map +1 -0
  29. package/dist/__tests__/chunker/java.test.d.ts +1 -0
  30. package/dist/__tests__/chunker/java.test.js +83 -0
  31. package/dist/__tests__/chunker/java.test.js.map +1 -0
  32. package/dist/__tests__/chunker/javascript.test.d.ts +1 -0
  33. package/dist/__tests__/chunker/javascript.test.js +88 -0
  34. package/dist/__tests__/chunker/javascript.test.js.map +1 -0
  35. package/dist/__tests__/chunker/json.test.d.ts +1 -0
  36. package/dist/__tests__/chunker/json.test.js +54 -0
  37. package/dist/__tests__/chunker/json.test.js.map +1 -0
  38. package/dist/__tests__/chunker/kotlin.test.d.ts +1 -0
  39. package/dist/__tests__/chunker/kotlin.test.js +42 -0
  40. package/dist/__tests__/chunker/kotlin.test.js.map +1 -0
  41. package/dist/__tests__/chunker/markdown.test.d.ts +1 -0
  42. package/dist/__tests__/chunker/markdown.test.js +76 -0
  43. package/dist/__tests__/chunker/markdown.test.js.map +1 -0
  44. package/dist/__tests__/chunker/python.test.d.ts +1 -0
  45. package/dist/__tests__/chunker/python.test.js +81 -0
  46. package/dist/__tests__/chunker/python.test.js.map +1 -0
  47. package/dist/__tests__/chunker/razor.test.d.ts +1 -0
  48. package/dist/__tests__/chunker/razor.test.js +73 -0
  49. package/dist/__tests__/chunker/razor.test.js.map +1 -0
  50. package/dist/__tests__/chunker/register.test.d.ts +1 -0
  51. package/dist/__tests__/chunker/register.test.js +93 -0
  52. package/dist/__tests__/chunker/register.test.js.map +1 -0
  53. package/dist/__tests__/chunker/ruby.test.d.ts +1 -0
  54. package/dist/__tests__/chunker/ruby.test.js +40 -0
  55. package/dist/__tests__/chunker/ruby.test.js.map +1 -0
  56. package/dist/__tests__/chunker/rust.test.d.ts +1 -0
  57. package/dist/__tests__/chunker/rust.test.js +42 -0
  58. package/dist/__tests__/chunker/rust.test.js.map +1 -0
  59. package/dist/__tests__/chunker/sln.test.d.ts +1 -0
  60. package/dist/__tests__/chunker/sln.test.js +45 -0
  61. package/dist/__tests__/chunker/sln.test.js.map +1 -0
  62. package/dist/__tests__/chunker/swift.test.d.ts +1 -0
  63. package/dist/__tests__/chunker/swift.test.js +49 -0
  64. package/dist/__tests__/chunker/swift.test.js.map +1 -0
  65. package/dist/__tests__/chunker/typescript.test.d.ts +1 -0
  66. package/dist/__tests__/chunker/typescript.test.js +92 -0
  67. package/dist/__tests__/chunker/typescript.test.js.map +1 -0
  68. package/dist/__tests__/chunker/uuid.test.d.ts +1 -0
  69. package/dist/__tests__/chunker/uuid.test.js +19 -0
  70. package/dist/__tests__/chunker/uuid.test.js.map +1 -0
  71. package/dist/__tests__/chunker/xml.test.d.ts +1 -0
  72. package/dist/__tests__/chunker/xml.test.js +50 -0
  73. package/dist/__tests__/chunker/xml.test.js.map +1 -0
  74. package/dist/__tests__/core/config.test.d.ts +1 -0
  75. package/dist/__tests__/core/config.test.js +75 -0
  76. package/dist/__tests__/core/config.test.js.map +1 -0
  77. package/dist/__tests__/core/fileLogger.test.d.ts +1 -0
  78. package/dist/__tests__/core/fileLogger.test.js +34 -0
  79. package/dist/__tests__/core/fileLogger.test.js.map +1 -0
  80. package/dist/__tests__/core/manifest.test.d.ts +1 -0
  81. package/dist/__tests__/core/manifest.test.js +56 -0
  82. package/dist/__tests__/core/manifest.test.js.map +1 -0
  83. package/dist/__tests__/embedder/embedBatch.test.d.ts +1 -0
  84. package/dist/__tests__/embedder/embedBatch.test.js +88 -0
  85. package/dist/__tests__/embedder/embedBatch.test.js.map +1 -0
  86. package/dist/__tests__/embedder/factory.test.d.ts +1 -0
  87. package/dist/__tests__/embedder/factory.test.js +71 -0
  88. package/dist/__tests__/embedder/factory.test.js.map +1 -0
  89. package/dist/__tests__/embedder/ollama.test.d.ts +1 -0
  90. package/dist/__tests__/embedder/ollama.test.js +106 -0
  91. package/dist/__tests__/embedder/ollama.test.js.map +1 -0
  92. package/dist/__tests__/embedder/openai.test.d.ts +1 -0
  93. package/dist/__tests__/embedder/openai.test.js +94 -0
  94. package/dist/__tests__/embedder/openai.test.js.map +1 -0
  95. package/dist/__tests__/indexer/indexer.test.d.ts +1 -0
  96. package/dist/__tests__/indexer/indexer.test.js +176 -0
  97. package/dist/__tests__/indexer/indexer.test.js.map +1 -0
  98. package/dist/__tests__/plugin.test.d.ts +1 -0
  99. package/dist/__tests__/plugin.test.js +77 -0
  100. package/dist/__tests__/plugin.test.js.map +1 -0
  101. package/dist/__tests__/retriever/retriever.test.d.ts +1 -0
  102. package/dist/__tests__/retriever/retriever.test.js +97 -0
  103. package/dist/__tests__/retriever/retriever.test.js.map +1 -0
  104. package/dist/__tests__/vectorstore/lancedb.test.d.ts +1 -0
  105. package/dist/__tests__/vectorstore/lancedb.test.js +159 -0
  106. package/dist/__tests__/vectorstore/lancedb.test.js.map +1 -0
  107. package/dist/chunker/doc.d.ts +8 -0
  108. package/dist/chunker/doc.js +79 -0
  109. package/dist/chunker/doc.js.map +1 -0
  110. package/dist/chunker/docx.d.ts +8 -0
  111. package/dist/chunker/docx.js +78 -0
  112. package/dist/chunker/docx.js.map +1 -0
  113. package/dist/chunker/excel.d.ts +8 -0
  114. package/dist/chunker/excel.js +78 -0
  115. package/dist/chunker/excel.js.map +1 -0
  116. package/dist/chunker/factory.d.ts +1 -0
  117. package/dist/chunker/factory.js +9 -0
  118. package/dist/chunker/factory.js.map +1 -1
  119. package/dist/cli.js +272 -1
  120. package/dist/cli.js.map +1 -1
  121. package/dist/core/config.d.ts +4 -0
  122. package/dist/core/config.js +6 -1
  123. package/dist/core/config.js.map +1 -1
  124. package/dist/indexer.js +18 -0
  125. package/dist/indexer.js.map +1 -1
  126. package/dist/opencode/create-read-tool.d.ts +30 -0
  127. package/dist/opencode/create-read-tool.js +248 -0
  128. package/dist/opencode/create-read-tool.js.map +1 -0
  129. package/dist/opencode/read-fallback.d.ts +21 -0
  130. package/dist/opencode/read-fallback.js +90 -0
  131. package/dist/opencode/read-fallback.js.map +1 -0
  132. package/dist/opencode/read-format.d.ts +40 -0
  133. package/dist/opencode/read-format.js +86 -0
  134. package/dist/opencode/read-format.js.map +1 -0
  135. package/dist/opencode/read-query.d.ts +26 -0
  136. package/dist/opencode/read-query.js +39 -0
  137. package/dist/opencode/read-query.js.map +1 -0
  138. package/dist/opencode/tool-args.d.ts +51 -0
  139. package/dist/opencode/tool-args.js +70 -0
  140. package/dist/opencode/tool-args.js.map +1 -0
  141. package/dist/plugin.js +1 -0
  142. package/dist/plugin.js.map +1 -1
  143. package/package.json +89 -82
@@ -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;