opencode-rag-plugin 1.2.4 → 1.2.10

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