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
package/ReadMe.md CHANGED
@@ -1,424 +1,463 @@
1
- # OpenCodeRAG
2
-
3
- Local-first RAG plugin for OpenCode — semantic code search powered by
4
- embeddings and vector similarity.
5
-
6
- **Published on npm as [`opencode-rag-plugin`](https://www.npmjs.com/package/opencode-rag-plugin).**
7
-
8
- ## Features
9
-
10
- - **AST-aware chunking** — splits code into functions, classes, methods using
11
- tree-sitter for 16 languages, plus regex-based chunking for 4 markup/config/doc
12
- formats (Markdown, Razor, .sln, LaTeX). Falls back to line-based chunking for
13
- unrecognized formats.
14
- - **Incremental indexing** — manifest-backed indexing skips unchanged files,
15
- removes deleted entries, and updates only changed files.
16
- - **Watch mode** — `index --watch` re-indexes on file changes with debounced,
17
- serialized passes.
18
- - **Pluggable chunkers** — add custom language chunkers via config or programmatic API.
19
- - **Configurable embeddings** — Ollama (default) or OpenAI-compatible providers.
20
- Batch embedding with configurable batch size.
21
- - **Local vector store** — LanceDB with L2 distance scoring, memory mode for
22
- testing.
23
- - **CLI** — index, query, clear, status commands.
24
- - **OpenCode plugin** — exposes a chunk retrieval tool and suggests relevant files after each user message via the `chat.message` hook.
25
-
26
- ## Architecture
27
-
28
- ```
29
- Workspace Files
30
-
31
-
32
- ┌──────────────┐
33
- │ Chunker │ AST-based (tree-sitter) or line-based fallback
34
- └──────┬───────┘
35
- │ chunks
36
-
37
- ┌──────────────┐
38
- │ Embedder │ Ollama / OpenAI-compatible API
39
- └──────┬───────┘
40
- │ vectors
41
-
42
- ┌──────────────┐
43
- │ VectorStore │ LanceDB (local files or memory:// for tests)
44
- └──────┬───────┘
45
- │ + manifest.json
46
-
47
- ┌──────────────┐
48
- │ Indexer/Retr.│ incremental index or query/search
49
- └──────┬───────┘
50
- │ results
51
-
52
- LLM Context
53
- ```
54
-
55
- ## Tech Stack
56
-
57
- | Layer | Technology |
58
- | ----------- | --------------------------------------------------- |
59
- | Runtime | Node.js v22 + tsx (ESM) |
60
- | Language | TypeScript 5.8 |
61
- | Chunking | web-tree-sitter (WASM) + tree-sitter-wasm grammars |
62
- | Embeddings | Ollama / OpenAI-compatible (native fetch) |
63
- | Vector DB | LanceDB (`@lancedb/lancedb`) |
64
- | CLI | commander |
65
- | Tests | Node built-in test runner (`node --test`) |
66
- | Package mgr | npm (with `--legacy-peer-deps`) |
67
-
68
- ## Installation
69
-
70
- ```bash
71
- # Install globally
72
- npm install -g opencode-rag-plugin
73
-
74
- # Or in your project
75
- npm install --save-dev opencode-rag-plugin
76
- ```
77
-
78
- ### Dependencies
79
-
80
- - **Node.js v22+** for native ESM and fetch support
81
- - **apache-arrow** — peer dependency for LanceDB (auto-installed)
82
- - **tree-sitter-wasm** — ships pre-built WASM grammars for all supported languages
83
-
84
- ## Configuration
85
-
86
- Create `opencode-rag.json` in the project root (auto-detected) or pass via
87
- `--config`. The repository's own [`opencode-rag.json`](./opencode-rag.json) serves
88
- as a complete example with all available options.
89
-
90
- Config files support partial overrides — missing keys fall back to defaults.
91
- Deep merging is applied per section.
92
-
93
- ### Embedding Providers
94
-
95
- | Provider | `baseUrl` example | Notes |
96
- | -------- | --------------------------------- | ---------------------------- |
97
- | ollama | `http://localhost:11434/api` | Default. No apiKey required. Proxy is disabled when `embedding.proxy.url` is empty. |
98
- | openai | `https://api.openai.com/v1` | Requires apiKey. |
99
-
100
- `embedding.timeoutMs` defaults to 30000 ms. Increase it if your local model has a slow cold start.
101
-
102
- OpenAI provider sends all texts in a single request. Ollama sends one request
103
- per request to `/api/embed`. Set `embedding.proxy.url` to use the standard
104
- proxy-aware HTTP path instead of the direct socket path.
105
-
106
- ## Usage
107
-
108
- This extension consists of two main interfaces:
109
- 1. **CLI** — for manual indexing and querying from the terminal
110
- 2. **OpenCode plugin** — for automatic retrieval and file suggestions within the chat interface
111
-
112
- ### CLI
113
-
114
- ```bash
115
- # Index the workspace incrementally
116
- opencode-rag index
117
-
118
- # Force full re-index (clears existing data first)
119
- opencode-rag index --force
120
-
121
- # Watch workspace and incrementally re-index on changes
122
- opencode-rag index --watch
123
-
124
- # Semantic search
125
- opencode-rag query "How is authentication handled?"
126
-
127
- # Limit results
128
- opencode-rag query "error handling" --top-k 5
129
-
130
- # Show indexing stats
131
- opencode-rag status
132
-
133
- # Example output:
134
- # Indexed chunks: 1247
135
- # Store path: /home/user/project/.opencode/rag_db
136
- # Embedding provider: ollama
137
- # Embedding model: nomic-embed-text
138
- # Manifest status: ok
139
- # Manifest entries: 42
140
- # Last indexed: 2026-05-28 10:45:02
141
- # Up-to-date files: 42
142
- # Pending files: 0
143
- # Watch mode: off
144
-
145
- # Clear all indexed data
146
- opencode-rag clear
147
-
148
- # Use custom config
149
- opencode-rag index --config ./my-config.json
150
- ```
151
-
152
- `index` is incremental by default. A sidecar manifest is stored at
153
- `<vectorStore.path>/manifest.json` and tracks file hashes, chunk counts, and the
154
- last successful index timestamp. If the manifest is missing or corrupt while the
155
- vector store already contains data, the next index pass clears and rebuilds the
156
- store to avoid duplicates.
157
-
158
- ### Watch workflow
159
-
160
- Start a watch session:
161
-
162
- ```bash
163
- opencode-rag index --watch
164
- ```
165
-
166
- The initial pass indexes the workspace, then watches for file changes. On each
167
- `add`, `change`, `unlink`, or `unlinkDir` event, the watch debounces (300 ms)
168
- and triggers a new incremental pass. If a pass is already running, the re-index
169
- queues one follow-up pass and runs it as soon as the current pass finishes.
170
-
171
- The watcher ignores excluded directories, the vector store path, and the
172
- manifest file itself. Press `Ctrl+C` to stop.
173
-
174
- ### OpenCode Plugin
175
-
176
- The plugin registers:
177
-
178
- 1. **`opencode-rag-context`** a custom retrieval tool for chunk-level evidence
179
- 2. **`chat.message`** — after each user message, automatically retrieves relevant indexed files and appends a compact suggestion list to the message text
180
-
181
- #### Chat Message File Suggestions
182
-
183
- After you send a message, the plugin:
184
- 1. Extracts the user's message text
185
- 2. Runs semantic retrieval against the indexed workspace
186
- 3. Groups results by file, sorts by best chunk score, and formats a compact file list:
187
- ```
188
- src/plugin.ts (typescript, lines 10-42)
189
- src/core/config.ts (typescript, lines 66-145)
190
- ```
191
- 4. Appends the list (max 10 files) to your message text
192
-
193
- Only file paths, language, and line ranges are shown — no scores or code snippets. This gives the agent lightweight hints about which files are relevant without inflating the context window.
194
-
195
- **Config:**
196
-
197
- | Option | Default | Description |
198
- | ------ | ------- | ----------- |
199
- | `openCode.overrideRead` | `false` | Set to `true` to restore the legacy RAG-backed `read` tool (deprecated) |
200
- | `openCode.maxContextChunks` | `5` | Maximum chunks per retrieval (affects `opencode-rag-context` tool output) |
201
- | `retrieval.topK` | `10` | Number of chunks fetched per query (controls chat.message file suggestion breadth) |
202
-
203
- Errors during retrieval are silently caught — a failed search won't break the
204
- chat.
205
-
206
- #### Install from source
207
-
208
- After cloning and installing dependencies:
209
-
210
- ```bash
211
- # Option 1: Use the project-local auto-loaded plugin
212
- # The repo already includes .opencode/plugins/rag-plugin.ts
213
-
214
- # Option 2: Build and install via npm pack
215
- npm run build
216
- npm pack
217
- opencode plugin .\opencode-rag-0.1.0.tgz
218
-
219
- # Option 3: Install from npm
220
- opencode plugin opencode-rag-plugin
221
- ```
222
-
223
- The plugin auto-detects configuration from `opencode-rag.json` or
224
- `.opencode/rag.json` in the project root.
225
-
226
- If you use the project-local plugin file, OpenCode auto-loads it from
227
- `.opencode/plugins/` at startup and no `plugin` entry is required in
228
- `.opencode/opencode.json`.
229
-
230
- Restart OpenCode after changing plugin files or plugin configuration.
231
-
232
- ### Logging
233
-
234
- Logging is configured under the `logging` key:
235
-
236
- ```json
237
- {
238
- "logging": {
239
- "level": "info",
240
- "logFilePath": "./.opencode/opencode-rag.log"
241
- }
242
- }
243
- ```
244
-
245
- | Option | Default | Description |
246
- | ------------ | ------------------------------ | -------------------------------------------- |
247
- | `level` | `"info"` | Log level: `"debug"`, `"info"`, or `"error"` |
248
- | `logFilePath` | `"./.opencode/opencode-rag.log"` | Path to the log file (relative paths are resolved against the workspace directory) |
249
-
250
- The resolved log file path also falls back to the `LOG_FILE_PATH` environment variable when the config value is not set. Config takes precedence over the env var when both are provided.
251
-
252
- #### AGENTS.md hints for using the plugin
253
-
254
- Add a section like this to the target workspace's `AGENTS.md` so the agent
255
- knows how to use the plugin correctly:
256
-
257
- ```markdown
258
- ## OpenCodeRAG Plugin
259
-
260
- This workspace has OpenCodeRAG installed for semantic code retrieval.
261
-
262
- ### `opencode-rag-context` tool
263
- Before planning, editing, or answering, use this tool to retrieve relevant code
264
- chunks with file paths, line ranges, and surrounding implementation.
265
- - `query` (required) — narrow, specific search, e.g. `"authentication middleware setup"`
266
- - `pathHints` (optional) up to 10 path filters, e.g. `["src/auth/"]`
267
- - `languageHints` (optional) — up to 10 language filters, e.g. `["typescript"]`
268
- - `topK` (optional) — result count (1-25, default 10)
269
-
270
- ### File suggestions
271
- After each user message, a `chat.message` hook appends up to 10 relevant file
272
- suggestions to the message. Look for lines like
273
- `src/file.ts (typescript, lines 10-42)` at the bottom of user input.
274
-
275
- ### Indexing
276
- - The plugin auto-indexes changed files in the background (debounced 5s)
277
- - If no results come back, the workspace may not be indexed yet —
278
- run `opencode-rag index` from the terminal (or `npx opencode-rag-plugin`)
279
- - Tiny files (under 1 KB), excluded extensions, and excluded directories
280
- (`node_modules`, `.git`, `.opencode`, `dist`, etc.) are silently skipped
281
- ```
282
-
283
- The plugin registers itself in the system prompt via the
284
- `experimental.chat.system.transform` hook, so compliant agents will see a
285
- reminder about the `opencode-rag-context` tool in their system instructions.
286
-
287
- ## Data Model
288
-
289
- ```typescript
290
- interface Chunk {
291
- id: string;
292
- content: string;
293
- embedding?: number[];
294
- metadata: {
295
- filePath: string;
296
- startLine: number;
297
- endLine: number;
298
- language: string;
299
- };
300
- }
301
-
302
- interface SearchResult {
303
- chunk: Chunk;
304
- score: number; // 1 / (1 + L2_distance), range [0, 1]
305
- }
306
- ```
307
-
308
- ## Chunking
309
-
310
- | Language | Strategy | Captures |
311
- | ---------- | ------------------------------ | ----------------------------------------- |
312
- | TypeScript | AST (tree-sitter) | functions, methods, classes, interfaces |
313
- | Python | AST (tree-sitter) | functions, classes, decorated definitions |
314
- | Java | AST (tree-sitter) | methods, classes, interfaces, enums |
315
- | Go | AST (tree-sitter) | functions, methods, type declarations |
316
- | C | AST (tree-sitter) | functions, structs, enums, unions, typedefs |
317
- | C++ | AST (tree-sitter) | functions, classes, structs, enums, namespaces, templates |
318
- | C# | AST (tree-sitter) | classes, interfaces, structs, enums, methods, namespaces, records |
319
- | JavaScript | AST (tree-sitter) | functions, classes, arrow functions, exports |
320
- | JSON | AST (tree-sitter) | key-value pairs |
321
- | XML | AST (tree-sitter) | elements (1 chunk per root element) |
322
- | HTML | AST (tree-sitter) | `<script>` / `<style>` blocks |
323
- | CSS | AST (tree-sitter) | rule sets, at-rules, media, keyframes |
324
- | Razor | Regex (brace matching) | `@code` / `@functions` blocks, template regions |
325
- | Markdown | Regex heading split | h1/h2 sections + trailing content |
326
- | Solution | Regex (section boundary) | project entries and global sections |
327
- | Rust | AST (tree-sitter) | functions, structs, enums, traits, impl blocks, modules, types |
328
- | Ruby | AST (tree-sitter) | methods, classes, modules, singleton methods |
329
- | Kotlin | AST (tree-sitter) | functions, classes, interfaces, objects, properties |
330
- | Swift | AST (tree-sitter) | functions, classes, structs, enums, protocols, extensions, variables |
331
- | LaTeX | Regex section split | chapter/section/subsection/subsubsection boundaries |
332
- | PDF | Paragraph-based (text extraction) | groups small paragraphs, splits oversized |
333
- | (other) | Line-based (100 lines/chunk) | raw text blocks |
334
-
335
- Custom chunkers can be added without modifying the project source code. Two
336
- registration paths are supported:
337
-
338
- ### Config file
339
-
340
- Add a `chunkers` array to `opencode-rag.json`:
341
-
342
- ```json
343
- {
344
- "chunkers": [
345
- { "module": "./path/to/rust-chunker.js", "extensions": [".rs"] }
346
- ]
347
- }
348
- ```
349
-
350
- The module path is resolved relative to the config file. The loaded module must
351
- export (as default or named) an object implementing the `Chunker` interface:
352
-
353
- ```typescript
354
- interface Chunker {
355
- readonly language: string;
356
- readonly fileExtensions?: string[];
357
- chunk(filePath: string, content: string): Promise<Chunk[]>;
358
- }
359
- ```
360
-
361
- ### Programmatic
362
-
363
- ```typescript
364
- import { registerChunker } from "opencode-rag-plugin/library";
365
- registerChunker(myChunker, [".rs"]);
366
- ```
367
-
368
- The optional second argument overrides the chunker's `fileExtensions`. If a
369
- built-in chunker already covers the requested extension, the new registration is
370
- skipped and a warning is emitted.
371
-
372
- ## Vector Store
373
-
374
- LanceDB stores chunks in a `chunks` table with columns: `id`, `content`,
375
- `embedding` (vector), `filePath`, `startLine`, `endLine`, `language`.
376
-
377
- - **Disk mode**: files in `vectorStore.path` (default `.opencode/rag_db`)
378
- - **Memory mode**: `memory://` URI — for tests only, data lost on close
379
- - **Manifest sidecar**: `manifest.json` in the store directory tracks indexed
380
- files for incremental updates
381
- - Schema is auto-inferred from a seed row on first table creation
382
- - L2 distance search, score = `1 / (1 + distance)`
383
- - Stored file paths are normalized to absolute forward-slash paths
384
-
385
- ## Development
386
-
387
- ```bash
388
- # TypeScript typecheck
389
- npm run typecheck
390
-
391
- # Run all tests
392
- npm test
393
-
394
- # Run specific test file
395
- node --import tsx --test src/__tests__/chunker/fallback.test.ts
396
- ```
397
-
398
- Project structure:
399
- ```
400
- src/
401
- core/ — interfaces.ts, config.ts
402
- chunker/ — grammar.ts, base.ts, language chunkers, fallback.ts, factory.ts, uuid.ts
403
- embedder/ — ollama.ts, openai.ts, factory.ts
404
- vectorstore/ — lancedb.ts
405
- retriever/ — retriever.ts
406
- types/ — opencode-plugin.d.ts
407
- indexer.ts — incremental indexing + watch scheduling
408
- watcher.ts — background indexer (chokidar + debounced scheduler + periodic timer)
409
- cli.ts, plugin.ts, plugin-entry.ts, index.ts
410
- __tests__/ — mirrors the module structure
411
- ```
412
-
413
- Test framework is Node's built-in runner (`node:test`) with `tsx` for TypeScript
414
- imports. No test library dependencies.
415
-
416
- ## Limitations
417
-
418
- - Embedding model dimension is auto-probed at startup; falls back to 384 if probing fails.
419
- - 21 built-in chunkers (AST for 16, regex for 4, PDF text for 1) + configurable fallback
420
-
421
- ## Privacy
422
-
423
- All processing is local. Embeddings are generated via local Ollama by default.
424
- No data leaves the machine unless configured to use a remote embedding API.
1
+ # OpenCodeRAG
2
+
3
+ Local-first RAG plugin for OpenCode — semantic code search powered by
4
+ embeddings and vector similarity.
5
+
6
+ **Published on npm as [`opencode-rag-plugin`](https://www.npmjs.com/package/opencode-rag-plugin).**
7
+
8
+ ## Features
9
+
10
+ - **AST-aware chunking** — splits code into functions, classes, methods using
11
+ tree-sitter for 16 languages, plus regex-based chunking for 4 markup/config/doc
12
+ formats (Markdown, Razor, .sln, LaTeX). Falls back to line-based chunking for
13
+ unrecognized formats.
14
+ - **Incremental indexing** — manifest-backed indexing skips unchanged files,
15
+ removes deleted entries, and updates only changed files.
16
+ - **Watch mode** — `index --watch` re-indexes on file changes with debounced,
17
+ serialized passes.
18
+ - **Pluggable chunkers** — add custom language chunkers via config or programmatic API.
19
+ - **Configurable embeddings** — Ollama (default) or OpenAI-compatible providers.
20
+ Batch embedding with configurable batch size.
21
+ - **Local vector store** — LanceDB with L2 distance scoring, memory mode for
22
+ testing.
23
+ - **CLI** — index, query, clear, status commands.
24
+ - **OpenCode plugin** — exposes a chunk retrieval tool and suggests relevant files after each user message via the `chat.message` hook.
25
+
26
+ ## Architecture
27
+
28
+ ```
29
+ Workspace Files
30
+
31
+
32
+ ┌──────────────┐
33
+ │ Chunker │ AST-based (tree-sitter) or line-based fallback
34
+ └──────┬───────┘
35
+ │ chunks
36
+
37
+ ┌──────────────┐
38
+ │ Embedder │ Ollama / OpenAI-compatible API
39
+ └──────┬───────┘
40
+ │ vectors
41
+
42
+ ┌──────────────┐
43
+ │ VectorStore │ LanceDB (local files or memory:// for tests)
44
+ └──────┬───────┘
45
+ │ + manifest.json
46
+
47
+ ┌──────────────┐
48
+ │ Indexer/Retr.│ incremental index or query/search
49
+ └──────┬───────┘
50
+ │ results
51
+
52
+ LLM Context
53
+ ```
54
+
55
+ ## Tech Stack
56
+
57
+ | Layer | Technology |
58
+ | ----------- | --------------------------------------------------- |
59
+ | Runtime | Node.js v22 + tsx (ESM) |
60
+ | Language | TypeScript 5.8 |
61
+ | Chunking | web-tree-sitter (WASM) + tree-sitter-wasm grammars |
62
+ | Embeddings | Ollama / OpenAI-compatible (native fetch) |
63
+ | Vector DB | LanceDB (`@lancedb/lancedb`) |
64
+ | CLI | commander |
65
+ | Tests | Node built-in test runner (`node --test`) |
66
+ | Package mgr | npm (with `--legacy-peer-deps`) |
67
+
68
+ ## Installation
69
+
70
+ ### Global installation (recommended)
71
+
72
+ Clone the repository and run the install script:
73
+
74
+ **Windows:**
75
+ ```powershell
76
+ .\install.ps1
77
+ ```
78
+
79
+ **Linux/macOS:**
80
+ ```bash
81
+ ./install.sh
82
+ ```
83
+
84
+ This will:
85
+ 1. Build the plugin from source (`npm run build`)
86
+ 2. Install it into OpenCode's runtime (`~/.opencode/node_modules/`)
87
+ 3. Register it in the global OpenCode config (`~/.config/opencode/opencode.jsonc`)
88
+
89
+ After installation, restart OpenCode and the plugin is ready.
90
+
91
+ ### Per-workspace setup
92
+
93
+ ```bash
94
+ cd your-workspace
95
+ opencode-rag init
96
+ ```
97
+
98
+ `opencode-rag init` bootstraps the current workspace by creating:
99
+
100
+ - `opencode-rag.json`
101
+ - `.opencode/.gitignore`
102
+ - `.opencode/opencode.json`
103
+ - `.opencode/package.json`
104
+ - `.opencode/plugins/rag-plugin.js` (workspace-local plugin fallback)
105
+ - `.opencode/node_modules/` with the workspace-local plugin dependencies
106
+
107
+ Add `--skip-install` if you only want the files without installing dependencies.
108
+
109
+ ### Dependencies
110
+
111
+ ### Dependencies
112
+
113
+ - **Node.js v22+** for native ESM and fetch support
114
+ - **apache-arrow** — peer dependency for LanceDB (auto-installed)
115
+ - **tree-sitter-wasm** ships pre-built WASM grammars for all supported languages
116
+
117
+ ## Configuration
118
+
119
+ Create `opencode-rag.json` in the project root (auto-detected) or pass via
120
+ `--config`. The repository's own [`opencode-rag.json`](./opencode-rag.json) serves
121
+ as a complete example with all available options.
122
+
123
+ Config files support partial overrides — missing keys fall back to defaults.
124
+ Deep merging is applied per section.
125
+
126
+ ### Embedding Providers
127
+
128
+ | Provider | `baseUrl` example | Notes |
129
+ | -------- | --------------------------------- | ---------------------------- |
130
+ | ollama | `http://localhost:11434/api` | Default. No apiKey required. Proxy is disabled when `embedding.proxy.url` is empty. |
131
+ | openai | `https://api.openai.com/v1` | Requires apiKey. |
132
+
133
+ `embedding.timeoutMs` defaults to 30000 ms. Increase it if your local model has a slow cold start.
134
+
135
+ OpenAI provider sends all texts in a single request. Ollama sends one request
136
+ per request to `/api/embed`. Set `embedding.proxy.url` to use the standard
137
+ proxy-aware HTTP path instead of the direct socket path.
138
+
139
+ ## Usage
140
+
141
+ This extension consists of two main interfaces:
142
+ 1. **CLI** — for manual indexing and querying from the terminal
143
+ 2. **OpenCode plugin** — for automatic retrieval and file suggestions within the chat interface
144
+
145
+ ### CLI
146
+
147
+ ```bash
148
+ # Index the workspace incrementally
149
+ opencode-rag index
150
+
151
+ # Force full re-index (clears existing data first)
152
+ opencode-rag index --force
153
+
154
+ # Watch workspace and incrementally re-index on changes
155
+ opencode-rag index --watch
156
+
157
+ # Semantic search
158
+ opencode-rag query "How is authentication handled?"
159
+
160
+ # Limit results
161
+ opencode-rag query "error handling" --top-k 5
162
+
163
+ # Show indexing stats
164
+ opencode-rag status
165
+
166
+ # Example output:
167
+ # Indexed chunks: 1247
168
+ # Store path: /home/user/project/.opencode/rag_db
169
+ # Embedding provider: ollama
170
+ # Embedding model: nomic-embed-text
171
+ # Manifest status: ok
172
+ # Manifest entries: 42
173
+ # Last indexed: 2026-05-28 10:45:02
174
+ # Up-to-date files: 42
175
+ # Pending files: 0
176
+ # Watch mode: off
177
+
178
+ # Clear all indexed data
179
+ opencode-rag clear
180
+
181
+ # Use custom config
182
+ opencode-rag index --config ./my-config.json
183
+ ```
184
+
185
+ `index` is incremental by default. A sidecar manifest is stored at
186
+ `<vectorStore.path>/manifest.json` and tracks file hashes, chunk counts, and the
187
+ last successful index timestamp. If the manifest is missing or corrupt while the
188
+ vector store already contains data, the next index pass clears and rebuilds the
189
+ store to avoid duplicates.
190
+
191
+ ### Watch workflow
192
+
193
+ Start a watch session:
194
+
195
+ ```bash
196
+ opencode-rag index --watch
197
+ ```
198
+
199
+ The initial pass indexes the workspace, then watches for file changes. On each
200
+ `add`, `change`, `unlink`, or `unlinkDir` event, the watch debounces (300 ms)
201
+ and triggers a new incremental pass. If a pass is already running, the re-index
202
+ queues one follow-up pass and runs it as soon as the current pass finishes.
203
+
204
+ The watcher ignores excluded directories, the vector store path, and the
205
+ manifest file itself. Press `Ctrl+C` to stop.
206
+
207
+ ### OpenCode Plugin
208
+
209
+ The plugin registers:
210
+
211
+ 1. **`opencode-rag-context`** a custom retrieval tool for chunk-level evidence
212
+ 2. **`chat.message`** after each user message, automatically retrieves relevant indexed files and appends a compact suggestion list to the message text
213
+
214
+ #### Chat Message File Suggestions
215
+
216
+ After you send a message, the plugin:
217
+ 1. Extracts the user's message text
218
+ 2. Runs semantic retrieval against the indexed workspace
219
+ 3. Groups results by file, sorts by best chunk score, and formats a compact file list:
220
+ ```
221
+ src/plugin.ts (typescript, lines 10-42)
222
+ src/core/config.ts (typescript, lines 66-145)
223
+ ```
224
+ 4. Appends the list (max 10 files) to your message text
225
+
226
+ Only file paths, language, and line ranges are shown — no scores or code snippets. This gives the agent lightweight hints about which files are relevant without inflating the context window.
227
+
228
+ **Config:**
229
+
230
+ | Option | Default | Description |
231
+ | ------ | ------- | ----------- |
232
+ | `openCode.overrideRead` | `false` | Set to `true` to restore the legacy RAG-backed `read` tool (deprecated) |
233
+ | `openCode.maxContextChunks` | `5` | Maximum chunks per retrieval (affects `opencode-rag-context` tool output) |
234
+ | `retrieval.topK` | `10` | Number of chunks fetched per query (controls chat.message file suggestion breadth) |
235
+
236
+ Errors during retrieval are silently caught — a failed search won't break the
237
+ chat.
238
+
239
+ #### Install from source
240
+
241
+ After cloning and installing dependencies:
242
+
243
+ ```bash
244
+ # Option 1: Install globally via the install script (recommended)
245
+ ./install.sh # Linux/macOS
246
+ .\install.ps1 # Windows
247
+
248
+ # Option 2: Build and pack manually, then register globally
249
+ npm run build
250
+ npm pack
251
+ npm install --prefix ~/.opencode/ opencode-rag-plugin-1.2.0.tgz
252
+ npm install --prefix ~/.config/opencode/ opencode-rag-plugin-1.2.0.tgz
253
+ # Add "opencode-rag-plugin" to the plugin array in ~/.config/opencode/opencode.jsonc
254
+
255
+ # Option 3: Bootstrap workspace only (uses npm version)
256
+ opencode-rag init
257
+ ```
258
+
259
+ The plugin auto-detects configuration from `opencode-rag.json` or
260
+ `.opencode/opencode-rag.json` or `.opencode/rag.json` in the project root.
261
+
262
+ `opencode-rag init` creates a workspace-local plugin file at `.opencode/plugins/rag-plugin.js`
263
+ that re-exports from `node_modules/`, serving as a fallback when global loading fails.
264
+ No `plugin` entry is required in `.opencode/opencode.json`.
265
+
266
+ Restart OpenCode after changing plugin files or plugin configuration.
267
+
268
+ ### Logging
269
+
270
+ Logging is configured under the `logging` key:
271
+
272
+ ```json
273
+ {
274
+ "logging": {
275
+ "level": "info",
276
+ "logFilePath": "./.opencode/opencode-rag.log"
277
+ }
278
+ }
279
+ ```
280
+
281
+ | Option | Default | Description |
282
+ | ------------ | ------------------------------ | -------------------------------------------- |
283
+ | `level` | `"info"` | Log level: `"debug"`, `"info"`, or `"error"` |
284
+ | `logFilePath` | `"./.opencode/opencode-rag.log"` | Path to the log file (relative paths are resolved against the workspace directory) |
285
+
286
+ The resolved log file path also falls back to the `LOG_FILE_PATH` environment variable when the config value is not set. Config takes precedence over the env var when both are provided.
287
+
288
+ #### AGENTS.md hints for using the plugin
289
+
290
+ Add a section like this to the target workspace's `AGENTS.md` so the agent
291
+ knows how to use the plugin correctly:
292
+
293
+ ```markdown
294
+ ## OpenCodeRAG Plugin
295
+
296
+ This workspace has OpenCodeRAG installed for semantic code retrieval.
297
+
298
+ ### `opencode-rag-context` tool
299
+ Before planning, editing, or answering, use this tool to retrieve relevant code
300
+ chunks with file paths, line ranges, and surrounding implementation.
301
+ - `query` (required) — narrow, specific search, e.g. `"authentication middleware setup"`
302
+ - `pathHints` (optional) — up to 10 path filters, e.g. `["src/auth/"]`
303
+ - `languageHints` (optional) — up to 10 language filters, e.g. `["typescript"]`
304
+ - `topK` (optional) result count (1-25, default 10)
305
+
306
+ ### File suggestions
307
+ After each user message, a `chat.message` hook appends up to 10 relevant file
308
+ suggestions to the message. Look for lines like
309
+ `src/file.ts (typescript, lines 10-42)` at the bottom of user input.
310
+
311
+ ### Indexing
312
+ - The plugin auto-indexes changed files in the background (debounced 5s)
313
+ - If no results come back, the workspace may not be indexed yet —
314
+ run `opencode-rag index` from the terminal (or `npx opencode-rag-plugin`)
315
+ - Tiny files (under 1 KB), excluded extensions, and excluded directories
316
+ (`node_modules`, `.git`, `.opencode`, `dist`, etc.) are silently skipped
317
+ ```
318
+
319
+ The plugin registers itself in the system prompt via the
320
+ `experimental.chat.system.transform` hook, so compliant agents will see a
321
+ reminder about the `opencode-rag-context` tool in their system instructions.
322
+
323
+ ## Data Model
324
+
325
+ ```typescript
326
+ interface Chunk {
327
+ id: string;
328
+ content: string;
329
+ embedding?: number[];
330
+ metadata: {
331
+ filePath: string;
332
+ startLine: number;
333
+ endLine: number;
334
+ language: string;
335
+ };
336
+ }
337
+
338
+ interface SearchResult {
339
+ chunk: Chunk;
340
+ score: number; // 1 / (1 + L2_distance), range [0, 1]
341
+ }
342
+ ```
343
+
344
+ ## Chunking
345
+
346
+ | Language | Strategy | Captures |
347
+ | ---------- | ------------------------------ | ----------------------------------------- |
348
+ | TypeScript | AST (tree-sitter) | functions, methods, classes, interfaces |
349
+ | Python | AST (tree-sitter) | functions, classes, decorated definitions |
350
+ | Java | AST (tree-sitter) | methods, classes, interfaces, enums |
351
+ | Go | AST (tree-sitter) | functions, methods, type declarations |
352
+ | C | AST (tree-sitter) | functions, structs, enums, unions, typedefs |
353
+ | C++ | AST (tree-sitter) | functions, classes, structs, enums, namespaces, templates |
354
+ | C# | AST (tree-sitter) | classes, interfaces, structs, enums, methods, namespaces, records |
355
+ | JavaScript | AST (tree-sitter) | functions, classes, arrow functions, exports |
356
+ | JSON | AST (tree-sitter) | key-value pairs |
357
+ | XML | AST (tree-sitter) | elements (1 chunk per root element) |
358
+ | HTML | AST (tree-sitter) | `<script>` / `<style>` blocks |
359
+ | CSS | AST (tree-sitter) | rule sets, at-rules, media, keyframes |
360
+ | Razor | Regex (brace matching) | `@code` / `@functions` blocks, template regions |
361
+ | Markdown | Regex heading split | h1/h2 sections + trailing content |
362
+ | Solution | Regex (section boundary) | project entries and global sections |
363
+ | Rust | AST (tree-sitter) | functions, structs, enums, traits, impl blocks, modules, types |
364
+ | Ruby | AST (tree-sitter) | methods, classes, modules, singleton methods |
365
+ | Kotlin | AST (tree-sitter) | functions, classes, interfaces, objects, properties |
366
+ | Swift | AST (tree-sitter) | functions, classes, structs, enums, protocols, extensions, variables |
367
+ | LaTeX | Regex section split | chapter/section/subsection/subsubsection boundaries |
368
+ | PDF | Paragraph-based (text extraction) | groups small paragraphs, splits oversized |
369
+ | Word (docx) | Paragraph-based (text extraction) | extracts raw text via mammoth, groups small paragraphs, splits oversized |
370
+ | Word (doc) | Paragraph-based (text extraction) | extracts raw text via word-extractor, groups small paragraphs, splits oversized |
371
+ | Excel (xls/xlsx) | Row-batch (text extraction) | extracts CSV per sheet via @e965/xlsx, splits by sheet then by row batches |
372
+ | (other) | Line-based (100 lines/chunk) | raw text blocks |
373
+
374
+ Custom chunkers can be added without modifying the project source code. Two
375
+ registration paths are supported:
376
+
377
+ ### Config file
378
+
379
+ Add a `chunkers` array to `opencode-rag.json`:
380
+
381
+ ```json
382
+ {
383
+ "chunkers": [
384
+ { "module": "./path/to/rust-chunker.js", "extensions": [".rs"] }
385
+ ]
386
+ }
387
+ ```
388
+
389
+ The module path is resolved relative to the config file. The loaded module must
390
+ export (as default or named) an object implementing the `Chunker` interface:
391
+
392
+ ```typescript
393
+ interface Chunker {
394
+ readonly language: string;
395
+ readonly fileExtensions?: string[];
396
+ chunk(filePath: string, content: string): Promise<Chunk[]>;
397
+ }
398
+ ```
399
+
400
+ ### Programmatic
401
+
402
+ ```typescript
403
+ import { registerChunker } from "opencode-rag-plugin/library";
404
+ registerChunker(myChunker, [".rs"]);
405
+ ```
406
+
407
+ The optional second argument overrides the chunker's `fileExtensions`. If a
408
+ built-in chunker already covers the requested extension, the new registration is
409
+ skipped and a warning is emitted.
410
+
411
+ ## Vector Store
412
+
413
+ LanceDB stores chunks in a `chunks` table with columns: `id`, `content`,
414
+ `embedding` (vector), `filePath`, `startLine`, `endLine`, `language`.
415
+
416
+ - **Disk mode**: files in `vectorStore.path` (default `.opencode/rag_db`)
417
+ - **Memory mode**: `memory://` URI — for tests only, data lost on close
418
+ - **Manifest sidecar**: `manifest.json` in the store directory tracks indexed
419
+ files for incremental updates
420
+ - Schema is auto-inferred from a seed row on first table creation
421
+ - L2 distance search, score = `1 / (1 + distance)`
422
+ - Stored file paths are normalized to absolute forward-slash paths
423
+
424
+ ## Development
425
+
426
+ ```bash
427
+ # TypeScript typecheck
428
+ npm run typecheck
429
+
430
+ # Run all tests
431
+ npm test
432
+
433
+ # Run specific test file
434
+ node --import tsx --test src/__tests__/chunker/fallback.test.ts
435
+ ```
436
+
437
+ Project structure:
438
+ ```
439
+ src/
440
+ core/ — interfaces.ts, config.ts
441
+ chunker/ — grammar.ts, base.ts, language chunkers, fallback.ts, factory.ts, uuid.ts
442
+ embedder/ — ollama.ts, openai.ts, factory.ts
443
+ vectorstore/ — lancedb.ts
444
+ retriever/ — retriever.ts
445
+ types/ — opencode-plugin.d.ts
446
+ indexer.ts — incremental indexing + watch scheduling
447
+ watcher.ts — background indexer (chokidar + debounced scheduler + periodic timer)
448
+ cli.ts, plugin.ts, plugin-entry.ts, index.ts
449
+ __tests__/ — mirrors the module structure
450
+ ```
451
+
452
+ Test framework is Node's built-in runner (`node:test`) with `tsx` for TypeScript
453
+ imports. No test library dependencies.
454
+
455
+ ## Limitations
456
+
457
+ - Embedding model dimension is auto-probed at startup; falls back to 384 if probing fails.
458
+ - 21 built-in chunkers (AST for 16, regex for 4, PDF text for 1) + configurable fallback
459
+
460
+ ## Privacy
461
+
462
+ All processing is local. Embeddings are generated via local Ollama by default.
463
+ No data leaves the machine unless configured to use a remote embedding API.