opencode-rag-plugin 1.17.0 → 1.17.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ReadMe.md +48 -3
- package/dist/cli/commands/index.d.ts +1 -0
- package/dist/cli/commands/index.js +1 -0
- package/dist/cli/commands/init-helpers.js +3 -0
- package/dist/cli/commands/status.js +3 -3
- package/dist/cli/commands/update.d.ts +15 -0
- package/dist/cli/commands/update.js +65 -0
- package/dist/cli/index.js +2 -1
- package/dist/core/config.d.ts +10 -1
- package/dist/core/config.js +71 -4
- package/dist/core/runtime-overrides.js +3 -1
- package/dist/core/setup-runtime.js +61 -13
- package/dist/core/version-check.d.ts +76 -0
- package/dist/core/version-check.js +113 -2
- package/dist/embedder/cohere.d.ts +1 -1
- package/dist/embedder/cohere.js +2 -2
- package/dist/embedder/factory.js +1 -1
- package/dist/embedder/health.js +1 -1
- package/dist/embedder/ollama.d.ts +1 -1
- package/dist/embedder/ollama.js +2 -2
- package/dist/embedder/openai.d.ts +1 -1
- package/dist/embedder/openai.js +2 -2
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/indexer/pipeline.js +15 -4
- package/dist/plugin.js +84 -16
- package/dist/tui.js +18 -1
- package/dist/watcher.d.ts +2 -0
- package/dist/watcher.js +2 -1
- package/package.json +1 -1
package/ReadMe.md
CHANGED
|
@@ -41,10 +41,14 @@ opencode-rag query "authentication middleware"
|
|
|
41
41
|
| **OpenCode plugin** | Auto-inject context, read-tool override, TUI settings, Ctrl+Enter to add RAG context, MCP registration on `init` |
|
|
42
42
|
| **Incremental indexing** | File-hash manifest, background watcher, auto-rebuild on corruption |
|
|
43
43
|
| **Privacy-first** | All processing stays local (when using Ollama) |
|
|
44
|
-
| **CLI Tools** | `index`, `query`, `status`, `list`, `show`, `dump`, `clear`, `
|
|
44
|
+
| **CLI Tools** | `init`, `index`, `query`, `status`, `list`, `show`, `dump`, `clear`, `describe-image`, `ui`, `mcp`, `setup`, `eval:sessions`, `eval:analyze`, `eval:compare` |
|
|
45
45
|
| **Proxy-aware** | Corporate proxy support with raw-socket localhost bypass |
|
|
46
46
|
| **OpenAI / Anthropic / Cohere** | Use alternate embedding providers with API key auto-resolution |
|
|
47
47
|
| **Evaluation** | Session-level token tracking, RAG-on vs RAG-off comparison, tiktoken BPE counting |
|
|
48
|
+
| **Documentation mode** | `/doc` slash command: agent adds JSDoc/TSDoc to undocumented files, progress tracked per subdirectory |
|
|
49
|
+
| **Wiki mode** | `/wiki` slash command: agent maintains a persistent knowledge wiki at `.opencode/wiki/` (ingest, query, lint, seed) |
|
|
50
|
+
| **Context optimization** | Post-retrieval dedup, per-file chunk limits, adjacent-merge to fit the context window |
|
|
51
|
+
| **AGENTS.md directive** | `opencode-rag init` merges a tool-usage directive into `AGENTS.md` via sentinel markers |
|
|
48
52
|
|
|
49
53
|
## Web UI
|
|
50
54
|
|
|
@@ -80,6 +84,47 @@ OpenCodeRAG can index image files (PNG, JPEG, WebP, etc.) by sending them to a v
|
|
|
80
84
|
|
|
81
85
|
**Disabled by default** — enable in `opencode-rag.json` to opt in (recommended for dedicated GPUs).
|
|
82
86
|
|
|
87
|
+
## Documentation Mode
|
|
88
|
+
|
|
89
|
+
When `documentationMode.enabled` is `true`, the OpenCode plugin exposes a `/doc` slash command that drives the agent to add JSDoc/TSDoc comments to undocumented source files. No agent tools are registered — documentation is driven entirely through the slash command and the documentation system prompt.
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"documentationMode": {
|
|
94
|
+
"enabled": true,
|
|
95
|
+
"batchSize": 5
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Workflow:** Type `/doc` to list all undocumented files grouped by subdirectory. The agent picks a subdirectory, documents every public symbol (preserving existing comments and implementation code), then types `/doc src/auth/` to mark that subdirectory complete. Progress is persisted in `.opencode/rag_db/doc-mode-progress.json` so subsequent sessions resume where you left off.
|
|
101
|
+
|
|
102
|
+
See [Plugin documentation](doc/plugin.md#5-documentation-mode--slash-command-doc) for details.
|
|
103
|
+
|
|
104
|
+
## Wiki Mode
|
|
105
|
+
|
|
106
|
+
When `wikiMode.enabled` is `true`, the OpenCode plugin injects a wiki-maintainer system prompt that instructs the agent to build and maintain a persistent knowledge wiki at `.opencode/wiki/` — a structured, interlinked collection of markdown pages synthesizing knowledge from the codebase, docs, and conversations.
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"wikiMode": {
|
|
111
|
+
"enabled": true
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Slash commands:**
|
|
117
|
+
|
|
118
|
+
| Command | Action |
|
|
119
|
+
|---|---|
|
|
120
|
+
| `/wiki` | Show wiki status (page count, subdirectories, index/log presence, latest log entry) |
|
|
121
|
+
| `/wiki seed` | Generate the initial wiki from README, file structure, and code patterns |
|
|
122
|
+
| `/wiki lint` | Health-check for orphan pages, stale source refs, contradictions, missing cross-links |
|
|
123
|
+
|
|
124
|
+
The agent maintains all wiki pages during normal coding sessions (ingest on learning). The user never writes wiki pages directly. The wiki is git-trackable in `.opencode/` so every edit is a diff.
|
|
125
|
+
|
|
126
|
+
See [Plugin documentation](doc/plugin.md#6-wiki-mode--slash-command-wiki) for the full protocol.
|
|
127
|
+
|
|
83
128
|
## MCP Server
|
|
84
129
|
|
|
85
130
|
OpenCodeRAG ships a CLI-based [MCP (Model Context Protocol)](https://spec.modelcontextprotocol.io/) server that exposes semantic code tools to any MCP-compatible client (Claude Desktop, OpenCode, Cursor, etc.).
|
|
@@ -124,10 +169,10 @@ OpenCodeRAG registers tools that agents can invoke directly. Agents discover the
|
|
|
124
169
|
|
|
125
170
|
When using OpenCode, the plugin enhances your agent with three discovery mechanisms:
|
|
126
171
|
|
|
127
|
-
### 1. Skill-Based Discovery
|
|
172
|
+
### 1. Skill-Based Discovery
|
|
128
173
|
`opencode-rag init` creates `.opencode/skills/opencode-rag/SKILL.md` - an OpenCode skill that teaches agents the tool workflow. Agents load it on demand via the `skill` tool, keeping token overhead minimal.
|
|
129
174
|
|
|
130
|
-
### 2. System Prompt Guidance
|
|
175
|
+
### 2. System Prompt Guidance
|
|
131
176
|
When chunks are indexed, a brief tool list is prepended to the system prompt so agents know the tools exist. This is skipped when no chunks are indexed to save tokens.
|
|
132
177
|
|
|
133
178
|
### 3. On-Demand RAG Context (Ctrl+Enter / Ctrl+Alt+Enter)
|
|
@@ -12,5 +12,6 @@ export { registerInitCommand } from "./init.js";
|
|
|
12
12
|
export { registerUiCommand } from "./ui.js";
|
|
13
13
|
export { registerMcpCommand } from "./mcp.js";
|
|
14
14
|
export { registerSetupCommand } from "./setup.js";
|
|
15
|
+
export { registerUpdateCommand } from "./update.js";
|
|
15
16
|
export { registerEvalSessionsCommand, registerEvalAnalyzeCommand, registerEvalCompareCommand } from "./eval.js";
|
|
16
17
|
export { registerDescribeImageCommand } from "./describe-image.js";
|
|
@@ -12,6 +12,7 @@ export { registerInitCommand } from "./init.js";
|
|
|
12
12
|
export { registerUiCommand } from "./ui.js";
|
|
13
13
|
export { registerMcpCommand } from "./mcp.js";
|
|
14
14
|
export { registerSetupCommand } from "./setup.js";
|
|
15
|
+
export { registerUpdateCommand } from "./update.js";
|
|
15
16
|
export { registerEvalSessionsCommand, registerEvalAnalyzeCommand, registerEvalCompareCommand } from "./eval.js";
|
|
16
17
|
export { registerDescribeImageCommand } from "./describe-image.js";
|
|
17
18
|
//# sourceMappingURL=index.js.map
|
|
@@ -460,6 +460,9 @@ export function generateDefaultConfigJson() {
|
|
|
460
460
|
timeoutMs: DEFAULT_CONFIG.description.timeoutMs,
|
|
461
461
|
maxContentChars: DEFAULT_CONFIG.description.maxContentChars,
|
|
462
462
|
},
|
|
463
|
+
wikiMode: {
|
|
464
|
+
enabled: DEFAULT_CONFIG.wikiMode.enabled,
|
|
465
|
+
},
|
|
463
466
|
mcp: {
|
|
464
467
|
enabled: DEFAULT_CONFIG.mcp.enabled,
|
|
465
468
|
},
|
|
@@ -147,12 +147,12 @@ export function registerStatusCommand(program) {
|
|
|
147
147
|
logCliInfo(logFilePath, "status", `${c.label("Runtime:")} ${c.warn("version unknown — run `opencode-rag setup`")}`);
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
|
-
// Async GitHub update check (fire-and-forget, 5s timeout)
|
|
151
|
-
//
|
|
150
|
+
// Async GitHub update check (fire-and-forget, 5s timeout).
|
|
151
|
+
// Runs unless autoUpdate is explicitly disabled.
|
|
152
152
|
if (config.autoUpdate?.enabled) {
|
|
153
153
|
checkForUpdate(pkg.version).then((info) => {
|
|
154
154
|
if (info.updateAvailable) {
|
|
155
|
-
process.stdout.write(` ${c.label("Update:")} ${c.warn(`v${info.latestVersion} available —
|
|
155
|
+
process.stdout.write(` ${c.label("Update:")} ${c.warn(`v${info.latestVersion} available — run \`opencode-rag update\` to install`)}\n`);
|
|
156
156
|
}
|
|
157
157
|
}).catch(() => { });
|
|
158
158
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview `update` command — checks GitHub for a newer OpenCodeRAG release and installs it.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* `update` command — checks for a newer published version of OpenCodeRAG and,
|
|
6
|
+
* by default, installs it (via `npm install -g ...@latest`) then re-syncs the
|
|
7
|
+
* OpenCode runtime junctions so the new build is picked up on next restart.
|
|
8
|
+
*/
|
|
9
|
+
import type { Command } from "commander";
|
|
10
|
+
/**
|
|
11
|
+
* Register the `update` command on the given Commander program.
|
|
12
|
+
*
|
|
13
|
+
* @param program - The Commander `Command` instance to register on.
|
|
14
|
+
*/
|
|
15
|
+
export declare function registerUpdateCommand(program: Command): void;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview `update` command — checks GitHub for a newer OpenCodeRAG release and installs it.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* `update` command — checks for a newer published version of OpenCodeRAG and,
|
|
6
|
+
* by default, installs it (via `npm install -g ...@latest`) then re-syncs the
|
|
7
|
+
* OpenCode runtime junctions so the new build is picked up on next restart.
|
|
8
|
+
*/
|
|
9
|
+
import { c } from "../format.js";
|
|
10
|
+
import { checkForUpdate, getCurrentVersion, installLatestUpdate } from "../../core/version-check.js";
|
|
11
|
+
/**
|
|
12
|
+
* Register the `update` command on the given Commander program.
|
|
13
|
+
*
|
|
14
|
+
* @param program - The Commander `Command` instance to register on.
|
|
15
|
+
*/
|
|
16
|
+
export function registerUpdateCommand(program) {
|
|
17
|
+
program
|
|
18
|
+
.command("update")
|
|
19
|
+
.description("Check for and install the newest published version of OpenCodeRAG")
|
|
20
|
+
.option("--check", "only check whether an update is available; do not install")
|
|
21
|
+
.option("-v, --verbose", "stream npm/setup output to the console")
|
|
22
|
+
.action(async (options) => {
|
|
23
|
+
const currentVersion = getCurrentVersion();
|
|
24
|
+
console.log(`\n${c.heading("OpenCodeRAG Update")}\n`);
|
|
25
|
+
console.log(` ${c.label("Current version:")} ${c.value(currentVersion)}`);
|
|
26
|
+
console.log(` ${c.label("Checking...")} `);
|
|
27
|
+
let info;
|
|
28
|
+
try {
|
|
29
|
+
info = await checkForUpdate(currentVersion);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
console.log(`\n ${c.warn("Could not reach the update server. Check your network and try again.")}\n`);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (!info.updateAvailable) {
|
|
37
|
+
console.log(`\n ${c.success("Already up-to-date.")} (${c.value(info.latestVersion || currentVersion)})\n`);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
console.log(` ${c.label("Latest version:")} ${c.value(info.latestVersion)}`);
|
|
41
|
+
if (info.publishedAt) {
|
|
42
|
+
console.log(` ${c.label("Published:")} ${c.dim(info.publishedAt)}`);
|
|
43
|
+
}
|
|
44
|
+
if (info.releaseUrl) {
|
|
45
|
+
console.log(` ${c.label("Release notes:")} ${c.file(info.releaseUrl)}`);
|
|
46
|
+
}
|
|
47
|
+
if (options.check) {
|
|
48
|
+
console.log(`\n ${c.warn("Update available.")} Run ${c.file("opencode-rag update")} to install.\n`);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
console.log(`\n ${c.dim("Installing newest version...")}\n`);
|
|
52
|
+
const result = await installLatestUpdate({ verbose: options.verbose });
|
|
53
|
+
if (result.success) {
|
|
54
|
+
console.log(` ${c.success("✓")} ${result.message}`);
|
|
55
|
+
console.log(`\n ${c.dim("Restart OpenCode if it is running to load the new version.")}\n`);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
console.error(` ${c.error("✗")} ${result.message}`);
|
|
59
|
+
console.error(`\n ${c.error("Update failed. You can retry with `opencode-rag update` or install manually:")}\n`);
|
|
60
|
+
console.error(` ${c.dim(" npm install -g opencode-rag-plugin@latest && opencode-rag setup")}\n`);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=update.js.map
|
package/dist/cli/index.js
CHANGED
|
@@ -14,7 +14,7 @@ import { realpathSync } from "node:fs";
|
|
|
14
14
|
import { basename, dirname } from "node:path";
|
|
15
15
|
import { fileURLToPath } from "node:url";
|
|
16
16
|
import { getPackageMetadata } from "./helpers.js";
|
|
17
|
-
import { registerIndexCommand, registerQueryCommand, registerClearCommand, registerStatusCommand, registerListCommand, registerShowCommand, registerDumpCommand, registerInitCommand, registerUiCommand, registerMcpCommand, registerSetupCommand, registerEvalSessionsCommand, registerEvalAnalyzeCommand, registerEvalCompareCommand, registerDescribeImageCommand, } from "./commands/index.js";
|
|
17
|
+
import { registerIndexCommand, registerQueryCommand, registerClearCommand, registerStatusCommand, registerListCommand, registerShowCommand, registerDumpCommand, registerInitCommand, registerUiCommand, registerMcpCommand, registerSetupCommand, registerUpdateCommand, registerEvalSessionsCommand, registerEvalAnalyzeCommand, registerEvalCompareCommand, registerDescribeImageCommand, } from "./commands/index.js";
|
|
18
18
|
/**
|
|
19
19
|
* The top-level Commander program instance that defines the `opencode-rag` CLI.
|
|
20
20
|
*
|
|
@@ -39,6 +39,7 @@ registerDescribeImageCommand(program);
|
|
|
39
39
|
registerUiCommand(program);
|
|
40
40
|
registerMcpCommand(program);
|
|
41
41
|
registerSetupCommand(program);
|
|
42
|
+
registerUpdateCommand(program);
|
|
42
43
|
registerEvalSessionsCommand(program);
|
|
43
44
|
registerEvalAnalyzeCommand(program);
|
|
44
45
|
registerEvalCompareCommand(program);
|
package/dist/core/config.d.ts
CHANGED
|
@@ -114,6 +114,13 @@ export interface DocumentationModeConfig {
|
|
|
114
114
|
/** System prompt for the documentation agent. */
|
|
115
115
|
systemPrompt: string;
|
|
116
116
|
}
|
|
117
|
+
/** Configuration for the wiki mode that instructs the AI agent to maintain a persistent knowledge wiki. */
|
|
118
|
+
export interface WikiModeConfig {
|
|
119
|
+
/** Whether wiki mode is enabled. */
|
|
120
|
+
enabled: boolean;
|
|
121
|
+
/** System prompt for the wiki maintainer agent. */
|
|
122
|
+
systemPrompt: string;
|
|
123
|
+
}
|
|
117
124
|
/** Configuration for the terminal UI (TUI) keybindings. */
|
|
118
125
|
export interface TuiConfig {
|
|
119
126
|
/** Keybinding to toggle the file list panel. */
|
|
@@ -128,7 +135,7 @@ export interface McpConfig {
|
|
|
128
135
|
}
|
|
129
136
|
/** Configuration for automatic self-updates. */
|
|
130
137
|
export interface AutoUpdateConfig {
|
|
131
|
-
/** Whether
|
|
138
|
+
/** Whether update checking is enabled (defaults to true). */
|
|
132
139
|
enabled: boolean;
|
|
133
140
|
}
|
|
134
141
|
/** Configuration for post-retrieval context window optimization (adjacent merge, similarity dedup, file diversity cap). */
|
|
@@ -251,6 +258,8 @@ export interface RagConfig {
|
|
|
251
258
|
imageDescription?: ImageDescriptionConfig;
|
|
252
259
|
/** Automated documentation mode config. */
|
|
253
260
|
documentationMode?: DocumentationModeConfig;
|
|
261
|
+
/** Wiki mode config that instructs the AI agent to maintain a persistent knowledge wiki. */
|
|
262
|
+
wikiMode?: WikiModeConfig;
|
|
254
263
|
/** MCP server integration config. */
|
|
255
264
|
mcp?: McpConfig;
|
|
256
265
|
/** Auto-update checking config. */
|
package/dist/core/config.js
CHANGED
|
@@ -10,7 +10,7 @@ export const DEFAULT_CONFIG = {
|
|
|
10
10
|
provider: "ollama",
|
|
11
11
|
baseUrl: "http://127.0.0.1:11434/api",
|
|
12
12
|
model: "qwen3-embedding:0.6b",
|
|
13
|
-
timeoutMs:
|
|
13
|
+
timeoutMs: 120000,
|
|
14
14
|
},
|
|
15
15
|
indexing: {
|
|
16
16
|
includeExtensions: [
|
|
@@ -104,7 +104,7 @@ export const DEFAULT_CONFIG = {
|
|
|
104
104
|
concurrency: 8,
|
|
105
105
|
embedBatchSize: 100,
|
|
106
106
|
embedConcurrency: 6,
|
|
107
|
-
ollamaMaxBatchSize:
|
|
107
|
+
ollamaMaxBatchSize: 500,
|
|
108
108
|
descriptionConcurrency: 4,
|
|
109
109
|
maxSvgSizeBytes: 1_048_576,
|
|
110
110
|
},
|
|
@@ -183,11 +183,72 @@ export const DEFAULT_CONFIG = {
|
|
|
183
183
|
"## Output format\n\n" +
|
|
184
184
|
"Return your changes as a list of file paths with the full new content of the comment block for each modified symbol. Do NOT output the entire file unless asked.",
|
|
185
185
|
},
|
|
186
|
+
wikiMode: {
|
|
187
|
+
enabled: false,
|
|
188
|
+
systemPrompt: "You are a wiki maintainer for this codebase. You incrementally build and maintain " +
|
|
189
|
+
"a persistent knowledge wiki at `.opencode/wiki/` — a structured, interlinked collection " +
|
|
190
|
+
"of markdown files that synthesizes knowledge from the codebase, documentation, and your conversations.\n\n" +
|
|
191
|
+
"## Three layers\n\n" +
|
|
192
|
+
"- **Raw sources**: code, docs, READMEs (immutable — read with `search_semantic`, `get_file_skeleton`, `find_usages`, `read`)\n" +
|
|
193
|
+
"- **Wiki**: `.opencode/wiki/*.md` (you own this layer — create, update, and maintain ALL pages)\n" +
|
|
194
|
+
"- **Schema**: this prompt (the protocol you follow)\n\n" +
|
|
195
|
+
"## Wiki layout\n\n" +
|
|
196
|
+
"`.opencode/wiki/`\n" +
|
|
197
|
+
"- `index.md` — content catalog: every page listed with a link and one-line summary\n" +
|
|
198
|
+
"- `log.md` — append-only timeline: `## [date] op | title`\n" +
|
|
199
|
+
"- `entities/` — pages for modules, services, key classes, components\n" +
|
|
200
|
+
"- `concepts/` — pages for patterns, conventions, architectural decisions, gotchas\n" +
|
|
201
|
+
"- `sources/` — summaries of external sources (articles, specs, discussions)\n\n" +
|
|
202
|
+
"## Page frontmatter\n\n" +
|
|
203
|
+
"Every page should start with:\n" +
|
|
204
|
+
"```\n" +
|
|
205
|
+
"---\n" +
|
|
206
|
+
"title: <page title>\n" +
|
|
207
|
+
"tags: [relevant tags]\n" +
|
|
208
|
+
"sourceRefs: [file paths or URLs this page synthesizes]\n" +
|
|
209
|
+
"lastReviewed: <date>\n" +
|
|
210
|
+
"---\n" +
|
|
211
|
+
"```\n\n" +
|
|
212
|
+
"## Operations\n\n" +
|
|
213
|
+
"### Ingest (when you learn something new)\n" +
|
|
214
|
+
"1. Read the source (code, doc, external article)\n" +
|
|
215
|
+
"2. Extract key information — what it does, why it exists, how it connects to existing knowledge\n" +
|
|
216
|
+
"3. Create or update relevant entity/concept pages\n" +
|
|
217
|
+
"4. Add cross-references with `[[wiki/page-name]]` links between related pages\n" +
|
|
218
|
+
"5. Update `index.md` with any new pages\n" +
|
|
219
|
+
"6. Append to `log.md`: `## [date] ingest | page title`\n\n" +
|
|
220
|
+
"### Query (when the user asks a question)\n" +
|
|
221
|
+
"1. Read `index.md` first to find relevant wiki pages\n" +
|
|
222
|
+
"2. Read those pages with the `read` tool\n" +
|
|
223
|
+
"3. If the wiki is insufficient, use `search_semantic` for code-level retrieval\n" +
|
|
224
|
+
"4. Synthesize an answer citing wiki pages where possible\n" +
|
|
225
|
+
"5. If the answer produced valuable analysis, file it back as a new wiki page\n\n" +
|
|
226
|
+
"### Lint (periodically, or when `/wiki lint` is invoked)\n" +
|
|
227
|
+
"Check for:\n" +
|
|
228
|
+
"- Orphan pages with no inbound `[[links]]`\n" +
|
|
229
|
+
"- Stale pages whose `sourceRefs` files changed since `lastReviewed`\n" +
|
|
230
|
+
"- Contradictions between pages\n" +
|
|
231
|
+
"- Important concepts mentioned but lacking their own page\n" +
|
|
232
|
+
"Append findings to `log.md`.\n\n" +
|
|
233
|
+
"### Seed (when `/wiki seed` is invoked)\n" +
|
|
234
|
+
"Generate initial wiki from existing sources:\n" +
|
|
235
|
+
"1. Read README and top-level docs for project overview → create overview page\n" +
|
|
236
|
+
"2. Scan file structure with `get_file_skeleton` for major modules → create entity pages\n" +
|
|
237
|
+
"3. Identify common conventions from code patterns → create concept pages\n" +
|
|
238
|
+
"4. Create `index.md` listing all pages\n" +
|
|
239
|
+
"5. Create `log.md` with initial entry\n" +
|
|
240
|
+
"6. Keep pages focused — one concept per page, short prose\n\n" +
|
|
241
|
+
"## Principles\n\n" +
|
|
242
|
+
"- You write and maintain ALL wiki pages — never ask the user to do it\n" +
|
|
243
|
+
"- Good answers and analyses get filed back as new pages — explorations compound\n" +
|
|
244
|
+
"- One concept per page — short, focused, cross-referenced\n" +
|
|
245
|
+
"- The wiki is git-trackable in `.opencode/` — every edit is a diff",
|
|
246
|
+
},
|
|
186
247
|
mcp: {
|
|
187
248
|
enabled: true,
|
|
188
249
|
},
|
|
189
250
|
autoUpdate: {
|
|
190
|
-
enabled:
|
|
251
|
+
enabled: true,
|
|
191
252
|
},
|
|
192
253
|
ui: {
|
|
193
254
|
port: 3210,
|
|
@@ -215,7 +276,7 @@ export function validateConfig(config) {
|
|
|
215
276
|
const KNOWN_TOP_KEYS = new Set([
|
|
216
277
|
"embedding", "indexing", "vectorStore", "retrieval",
|
|
217
278
|
"openCode", "chunkers", "chunking", "description",
|
|
218
|
-
"imageDescription", "documentationMode", "mcp", "autoUpdate", "ui", "tui", "logging",
|
|
279
|
+
"imageDescription", "documentationMode", "wikiMode", "mcp", "autoUpdate", "ui", "tui", "logging",
|
|
219
280
|
]);
|
|
220
281
|
const topKeys = new Set(Object.keys(config));
|
|
221
282
|
for (const key of topKeys) {
|
|
@@ -333,6 +394,8 @@ export function loadConfig(filePath, validate = true) {
|
|
|
333
394
|
let parsed;
|
|
334
395
|
try {
|
|
335
396
|
raw = readFileSync(filePath, "utf-8");
|
|
397
|
+
if (raw.charCodeAt(0) === 0xfeff)
|
|
398
|
+
raw = raw.slice(1);
|
|
336
399
|
parsed = JSON.parse(raw);
|
|
337
400
|
}
|
|
338
401
|
catch (err) {
|
|
@@ -399,6 +462,10 @@ export function loadConfig(filePath, validate = true) {
|
|
|
399
462
|
...DEFAULT_CONFIG.documentationMode,
|
|
400
463
|
...(safeObj(parsed.documentationMode) ?? {}),
|
|
401
464
|
},
|
|
465
|
+
wikiMode: {
|
|
466
|
+
...DEFAULT_CONFIG.wikiMode,
|
|
467
|
+
...(safeObj(parsed.wikiMode) ?? {}),
|
|
468
|
+
},
|
|
402
469
|
mcp: {
|
|
403
470
|
...DEFAULT_CONFIG.mcp,
|
|
404
471
|
...(safeObj(parsed.mcp) ?? {}),
|
|
@@ -11,7 +11,9 @@ export function loadRuntimeOverrides(storePath) {
|
|
|
11
11
|
if (!existsSync(overridePath))
|
|
12
12
|
return {};
|
|
13
13
|
try {
|
|
14
|
-
|
|
14
|
+
const raw = readFileSync(overridePath, "utf-8");
|
|
15
|
+
const stripped = raw.charCodeAt(0) === 0xfeff ? raw.slice(1) : raw;
|
|
16
|
+
return JSON.parse(stripped);
|
|
15
17
|
}
|
|
16
18
|
catch {
|
|
17
19
|
return {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import os from "node:os";
|
|
3
|
-
import { existsSync, mkdirSync, rmSync, writeFileSync, readFileSync, symlinkSync, } from "node:fs";
|
|
3
|
+
import { existsSync, mkdirSync, rmSync, writeFileSync, readFileSync, symlinkSync, lstatSync, } from "node:fs";
|
|
4
4
|
import { execSync } from "node:child_process";
|
|
5
5
|
const PLUGIN_NAME = "opencode-rag-plugin";
|
|
6
6
|
export function getRuntimeDir() {
|
|
@@ -131,38 +131,86 @@ export async function setupRuntime(options) {
|
|
|
131
131
|
writeFileSync(runtimePkg, JSON.stringify({ private: true, type: "module" }, null, 2) + "\n", "utf-8");
|
|
132
132
|
}
|
|
133
133
|
removeIfExists(runtimePluginDir);
|
|
134
|
+
// Ensure stale directory is fully gone before creating junction
|
|
135
|
+
let retries = 3;
|
|
136
|
+
while (retries > 0 && existsSync(runtimePluginDir)) {
|
|
137
|
+
try {
|
|
138
|
+
rmSync(runtimePluginDir, { recursive: true, force: true });
|
|
139
|
+
}
|
|
140
|
+
catch { /* retry */ }
|
|
141
|
+
retries--;
|
|
142
|
+
}
|
|
134
143
|
mkdirSync(path.dirname(runtimePluginDir), { recursive: true });
|
|
144
|
+
let junctionOk = false;
|
|
135
145
|
try {
|
|
136
146
|
createJunction(globalPluginDir, runtimePluginDir);
|
|
147
|
+
junctionOk = process.platform !== "win32";
|
|
148
|
+
if (process.platform === "win32") {
|
|
149
|
+
const stat = lstatSync(runtimePluginDir);
|
|
150
|
+
junctionOk = stat.isSymbolicLink();
|
|
151
|
+
}
|
|
137
152
|
}
|
|
138
153
|
catch {
|
|
154
|
+
// fall through to cpSync
|
|
155
|
+
}
|
|
156
|
+
if (!junctionOk) {
|
|
157
|
+
if (!options?.silent) {
|
|
158
|
+
console.error(" [warn] Junction not supported, falling back to copy...");
|
|
159
|
+
}
|
|
139
160
|
const { cpSync } = await import("node:fs");
|
|
140
161
|
cpSync(globalPluginDir, runtimePluginDir, { recursive: true });
|
|
141
162
|
}
|
|
163
|
+
// Ensure the @opencode-ai/plugin SDK is available globally.
|
|
164
|
+
// We must NOT run `npm install` inside the runtime dir because npm
|
|
165
|
+
// re-resolves all of node_modules/ and replaces the plugin junction
|
|
166
|
+
// with the published npm version (corrupting the local link).
|
|
167
|
+
if (!existsSync(globalSdkPluginDir)) {
|
|
168
|
+
try {
|
|
169
|
+
execSync(`npm install -g @opencode-ai/plugin`, {
|
|
170
|
+
stdio: "pipe",
|
|
171
|
+
timeout: 60_000,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
catch (cause) {
|
|
175
|
+
errors.push(`Failed to install @opencode-ai/plugin SDK globally: ${cause.message}`);
|
|
176
|
+
return { success: false, errors };
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// Create junction from global SDK to runtime (same pattern as plugin above)
|
|
142
180
|
if (existsSync(globalSdkPluginDir)) {
|
|
143
181
|
removeIfExists(runtimeSdkPluginDir);
|
|
182
|
+
retries = 3;
|
|
183
|
+
while (retries > 0 && existsSync(runtimeSdkPluginDir)) {
|
|
184
|
+
try {
|
|
185
|
+
rmSync(runtimeSdkPluginDir, { recursive: true, force: true });
|
|
186
|
+
}
|
|
187
|
+
catch { /* retry */ }
|
|
188
|
+
retries--;
|
|
189
|
+
}
|
|
144
190
|
mkdirSync(runtimeSdkDir, { recursive: true });
|
|
191
|
+
let sdkJunctionOk = false;
|
|
145
192
|
try {
|
|
146
193
|
createJunction(globalSdkPluginDir, runtimeSdkPluginDir);
|
|
194
|
+
sdkJunctionOk = process.platform !== "win32";
|
|
195
|
+
if (process.platform === "win32") {
|
|
196
|
+
const stat = lstatSync(runtimeSdkPluginDir);
|
|
197
|
+
sdkJunctionOk = stat.isSymbolicLink();
|
|
198
|
+
}
|
|
147
199
|
}
|
|
148
200
|
catch {
|
|
201
|
+
// fall through to cpSync
|
|
202
|
+
}
|
|
203
|
+
if (!sdkJunctionOk) {
|
|
204
|
+
if (!options?.silent) {
|
|
205
|
+
console.error(" [warn] SDK junction not supported, falling back to copy...");
|
|
206
|
+
}
|
|
149
207
|
const { cpSync } = await import("node:fs");
|
|
150
208
|
cpSync(globalSdkPluginDir, runtimeSdkPluginDir, { recursive: true });
|
|
151
209
|
}
|
|
152
210
|
}
|
|
153
211
|
else {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
execSync(`npm install @opencode-ai/plugin --no-save`, {
|
|
157
|
-
cwd: runtimeDir,
|
|
158
|
-
stdio: "pipe",
|
|
159
|
-
timeout: 60_000,
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
catch (cause) {
|
|
163
|
-
errors.push(`Failed to install @opencode-ai/plugin SDK: ${cause.message}`);
|
|
164
|
-
return { success: false, errors };
|
|
165
|
-
}
|
|
212
|
+
errors.push(`@opencode-ai/plugin SDK not available after global install`);
|
|
213
|
+
return { success: false, errors };
|
|
166
214
|
}
|
|
167
215
|
writeFileSync(versionFile, pluginVersion, "utf-8");
|
|
168
216
|
patchWindowsWrappers(npmGlobalRoot);
|
|
@@ -1,9 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Version check and self-update functionality. Checks GitHub
|
|
3
|
+
* releases for new versions and installs the newest version via npm, then
|
|
4
|
+
* re-syncs the OpenCode runtime junctions.
|
|
5
|
+
*/
|
|
6
|
+
import { setupRuntime } from "./setup-runtime.js";
|
|
7
|
+
/** Information about an available update. */
|
|
1
8
|
export interface UpdateInfo {
|
|
9
|
+
/** The currently installed version string. */
|
|
2
10
|
currentVersion: string;
|
|
11
|
+
/** The latest available version on GitHub. */
|
|
3
12
|
latestVersion: string;
|
|
13
|
+
/** Whether a newer version than the current one exists. */
|
|
4
14
|
updateAvailable: boolean;
|
|
15
|
+
/** URL to the GitHub release page. */
|
|
5
16
|
releaseUrl: string;
|
|
17
|
+
/** ISO date string of when the release was published. */
|
|
6
18
|
publishedAt: string;
|
|
7
19
|
}
|
|
20
|
+
/** Result of an install-update attempt. */
|
|
21
|
+
export interface InstallUpdateResult {
|
|
22
|
+
/** Whether the install completed successfully. */
|
|
23
|
+
success: boolean;
|
|
24
|
+
/** Human-readable status message. */
|
|
25
|
+
message: string;
|
|
26
|
+
/** Version before the update. */
|
|
27
|
+
fromVersion: string;
|
|
28
|
+
/** Version after the update (undefined if the install failed). */
|
|
29
|
+
toVersion?: string;
|
|
30
|
+
}
|
|
31
|
+
/** Callable shape for the npm runner (a subset of execSync's signature). */
|
|
32
|
+
type NpmRunner = (command: string, options: {
|
|
33
|
+
stdio: "inherit" | "pipe";
|
|
34
|
+
timeout: number;
|
|
35
|
+
}) => unknown;
|
|
36
|
+
/**
|
|
37
|
+
* Read the current version from the package.json sitting next to this module.
|
|
38
|
+
*
|
|
39
|
+
* Resolves the package root via `import.meta.url` so it works both from the
|
|
40
|
+
* source tree and from the compiled `dist/` output. Returns `"0.0.0"` if the
|
|
41
|
+
* file cannot be read or parsed (best-effort — never throws).
|
|
42
|
+
*
|
|
43
|
+
* @returns The current package version string.
|
|
44
|
+
*/
|
|
45
|
+
export declare function getCurrentVersion(): string;
|
|
46
|
+
/**
|
|
47
|
+
* Compare two semver-ish strings.
|
|
48
|
+
* @returns 1 if a > b, -1 if a < b, 0 if equal.
|
|
49
|
+
*/
|
|
8
50
|
export declare function compareVersions(a: string, b: string): number;
|
|
51
|
+
/**
|
|
52
|
+
* Check the GitHub releases API for a newer version of OpenCodeRAG.
|
|
53
|
+
*
|
|
54
|
+
* Uses a 5-second timeout; failures (network errors, non-OK responses, missing
|
|
55
|
+
* `tag_name`) are silently caught and reported as "no update available" so the
|
|
56
|
+
* caller never has to handle a rejection.
|
|
57
|
+
*
|
|
58
|
+
* @param currentVersion - The version string to compare against.
|
|
59
|
+
* @returns UpdateInfo indicating whether an update is available.
|
|
60
|
+
*/
|
|
9
61
|
export declare function checkForUpdate(currentVersion: string): Promise<UpdateInfo>;
|
|
62
|
+
/**
|
|
63
|
+
* Install the newest published version of OpenCodeRAG.
|
|
64
|
+
*
|
|
65
|
+
* Runs `npm install -g <package>@latest` to refresh the global install, then
|
|
66
|
+
* calls {@link setupRuntime} with `force: true` to re-create the
|
|
67
|
+
* `~/.opencode/node_modules/` junctions so OpenCode picks up the new build on
|
|
68
|
+
* the next restart.
|
|
69
|
+
*
|
|
70
|
+
* @param options - Optional verbosity flag. When `verbose` is true, npm output
|
|
71
|
+
* is streamed to the console; otherwise it is captured silently. The
|
|
72
|
+
* `_execSync` and `_setupRuntime` seams are for testing only.
|
|
73
|
+
* @returns An {@link InstallUpdateResult} with success status, message, and the
|
|
74
|
+
* from/to versions.
|
|
75
|
+
*/
|
|
76
|
+
export declare function installLatestUpdate(options?: {
|
|
77
|
+
verbose?: boolean;
|
|
78
|
+
/** Test seam: override the npm runner. */
|
|
79
|
+
_execSync?: NpmRunner;
|
|
80
|
+
/** Test seam: override the runtime sync. */
|
|
81
|
+
_setupRuntime?: typeof setupRuntime;
|
|
82
|
+
/** Test seam: override the version reader for the "to" version. */
|
|
83
|
+
_getCurrentVersion?: typeof getCurrentVersion;
|
|
84
|
+
}): Promise<InstallUpdateResult>;
|
|
85
|
+
export {};
|
|
@@ -1,3 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Version check and self-update functionality. Checks GitHub
|
|
3
|
+
* releases for new versions and installs the newest version via npm, then
|
|
4
|
+
* re-syncs the OpenCode runtime junctions.
|
|
5
|
+
*/
|
|
6
|
+
import { readFileSync } from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { execSync } from "node:child_process";
|
|
10
|
+
import { setupRuntime } from "./setup-runtime.js";
|
|
11
|
+
/** npm package name (must match the `name` field in package.json). */
|
|
12
|
+
const PACKAGE_NAME = "opencode-rag-plugin";
|
|
13
|
+
/** GitHub repo used for release checks (owner/repo). */
|
|
14
|
+
const GITHUB_REPO = "MrDoe/OpenCodeRAG";
|
|
15
|
+
const GITHUB_API_URL = `https://api.github.com/repos/${GITHUB_REPO}/releases/latest`;
|
|
16
|
+
/**
|
|
17
|
+
* Read the current version from the package.json sitting next to this module.
|
|
18
|
+
*
|
|
19
|
+
* Resolves the package root via `import.meta.url` so it works both from the
|
|
20
|
+
* source tree and from the compiled `dist/` output. Returns `"0.0.0"` if the
|
|
21
|
+
* file cannot be read or parsed (best-effort — never throws).
|
|
22
|
+
*
|
|
23
|
+
* @returns The current package version string.
|
|
24
|
+
*/
|
|
25
|
+
export function getCurrentVersion() {
|
|
26
|
+
try {
|
|
27
|
+
const pkgPath = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "package.json");
|
|
28
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
29
|
+
return pkg.version ?? "0.0.0";
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return "0.0.0";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** Strip a leading 'v' or 'V' prefix from a version tag. */
|
|
36
|
+
function normalizeVersion(tag) {
|
|
37
|
+
return tag.replace(/^v/i, "");
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Compare two semver-ish strings.
|
|
41
|
+
* @returns 1 if a > b, -1 if a < b, 0 if equal.
|
|
42
|
+
*/
|
|
1
43
|
export function compareVersions(a, b) {
|
|
2
44
|
const pa = a.split(".").map((s) => parseInt(s, 10));
|
|
3
45
|
const pb = b.split(".").map((s) => parseInt(s, 10));
|
|
@@ -11,11 +53,21 @@ export function compareVersions(a, b) {
|
|
|
11
53
|
}
|
|
12
54
|
return 0;
|
|
13
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Check the GitHub releases API for a newer version of OpenCodeRAG.
|
|
58
|
+
*
|
|
59
|
+
* Uses a 5-second timeout; failures (network errors, non-OK responses, missing
|
|
60
|
+
* `tag_name`) are silently caught and reported as "no update available" so the
|
|
61
|
+
* caller never has to handle a rejection.
|
|
62
|
+
*
|
|
63
|
+
* @param currentVersion - The version string to compare against.
|
|
64
|
+
* @returns UpdateInfo indicating whether an update is available.
|
|
65
|
+
*/
|
|
14
66
|
export async function checkForUpdate(currentVersion) {
|
|
15
67
|
const controller = new AbortController();
|
|
16
68
|
const timeout = setTimeout(() => controller.abort(), 5_000);
|
|
17
69
|
try {
|
|
18
|
-
const response = await fetch(
|
|
70
|
+
const response = await fetch(GITHUB_API_URL, {
|
|
19
71
|
headers: {
|
|
20
72
|
Accept: "application/vnd.github+json",
|
|
21
73
|
"User-Agent": "opencode-rag-updater",
|
|
@@ -30,7 +82,7 @@ export async function checkForUpdate(currentVersion) {
|
|
|
30
82
|
if (!tagName) {
|
|
31
83
|
return { currentVersion, latestVersion: currentVersion, updateAvailable: false, releaseUrl: "", publishedAt: "" };
|
|
32
84
|
}
|
|
33
|
-
const latestVersion = tagName
|
|
85
|
+
const latestVersion = normalizeVersion(tagName);
|
|
34
86
|
return {
|
|
35
87
|
currentVersion,
|
|
36
88
|
latestVersion,
|
|
@@ -46,4 +98,63 @@ export async function checkForUpdate(currentVersion) {
|
|
|
46
98
|
clearTimeout(timeout);
|
|
47
99
|
}
|
|
48
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Install the newest published version of OpenCodeRAG.
|
|
103
|
+
*
|
|
104
|
+
* Runs `npm install -g <package>@latest` to refresh the global install, then
|
|
105
|
+
* calls {@link setupRuntime} with `force: true` to re-create the
|
|
106
|
+
* `~/.opencode/node_modules/` junctions so OpenCode picks up the new build on
|
|
107
|
+
* the next restart.
|
|
108
|
+
*
|
|
109
|
+
* @param options - Optional verbosity flag. When `verbose` is true, npm output
|
|
110
|
+
* is streamed to the console; otherwise it is captured silently. The
|
|
111
|
+
* `_execSync` and `_setupRuntime` seams are for testing only.
|
|
112
|
+
* @returns An {@link InstallUpdateResult} with success status, message, and the
|
|
113
|
+
* from/to versions.
|
|
114
|
+
*/
|
|
115
|
+
export async function installLatestUpdate(options) {
|
|
116
|
+
const verbose = options?.verbose ?? false;
|
|
117
|
+
const stdio = verbose ? "inherit" : "pipe";
|
|
118
|
+
const run = options?._execSync ?? execSync;
|
|
119
|
+
const syncRuntime = options?._setupRuntime ?? setupRuntime;
|
|
120
|
+
const readVersion = options?._getCurrentVersion ?? getCurrentVersion;
|
|
121
|
+
const fromVersion = readVersion();
|
|
122
|
+
try {
|
|
123
|
+
run(`npm install -g ${PACKAGE_NAME}@latest --no-fund --no-audit`, {
|
|
124
|
+
stdio,
|
|
125
|
+
timeout: 120_000,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
return {
|
|
130
|
+
success: false,
|
|
131
|
+
message: `npm install failed: ${err.message}`,
|
|
132
|
+
fromVersion,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const toVersion = readVersion();
|
|
136
|
+
const result = await syncRuntime({ force: true, silent: !verbose, version: toVersion });
|
|
137
|
+
if (!result.success) {
|
|
138
|
+
return {
|
|
139
|
+
success: false,
|
|
140
|
+
message: `installed v${toVersion} but runtime sync failed: ${result.errors.join("; ")}. Run \`opencode-rag setup\` to retry.`,
|
|
141
|
+
fromVersion,
|
|
142
|
+
toVersion,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
if (compareVersions(toVersion, fromVersion) <= 0) {
|
|
146
|
+
return {
|
|
147
|
+
success: true,
|
|
148
|
+
message: `Already up-to-date (v${toVersion}). Runtime re-synced.`,
|
|
149
|
+
fromVersion,
|
|
150
|
+
toVersion,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
success: true,
|
|
155
|
+
message: `Updated v${fromVersion} → v${toVersion}. Restart OpenCode to load the new version.`,
|
|
156
|
+
fromVersion,
|
|
157
|
+
toVersion,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
49
160
|
//# sourceMappingURL=version-check.js.map
|
|
@@ -9,7 +9,7 @@ import type { ProxyConfig } from "../core/config.js";
|
|
|
9
9
|
* @param baseUrl - Cohere API base URL
|
|
10
10
|
* @param model - Model name to use for embedding
|
|
11
11
|
* @param apiKey - API key for authentication
|
|
12
|
-
* @param timeoutMs - Request timeout in milliseconds (default
|
|
12
|
+
* @param timeoutMs - Request timeout in milliseconds (default 120000)
|
|
13
13
|
* @param proxy - Optional proxy configuration
|
|
14
14
|
*/
|
|
15
15
|
export declare class CohereProvider implements EmbeddingProvider {
|
package/dist/embedder/cohere.js
CHANGED
|
@@ -5,7 +5,7 @@ import { postJson } from "./http.js";
|
|
|
5
5
|
* @param baseUrl - Cohere API base URL
|
|
6
6
|
* @param model - Model name to use for embedding
|
|
7
7
|
* @param apiKey - API key for authentication
|
|
8
|
-
* @param timeoutMs - Request timeout in milliseconds (default
|
|
8
|
+
* @param timeoutMs - Request timeout in milliseconds (default 120000)
|
|
9
9
|
* @param proxy - Optional proxy configuration
|
|
10
10
|
*/
|
|
11
11
|
export class CohereProvider {
|
|
@@ -15,7 +15,7 @@ export class CohereProvider {
|
|
|
15
15
|
apiKey;
|
|
16
16
|
timeoutMs;
|
|
17
17
|
proxy;
|
|
18
|
-
constructor(baseUrl, model, apiKey, timeoutMs =
|
|
18
|
+
constructor(baseUrl, model, apiKey, timeoutMs = 120000, proxy) {
|
|
19
19
|
this.baseUrl = baseUrl.replace(/\/+$/, "");
|
|
20
20
|
this.model = model;
|
|
21
21
|
this.apiKey = apiKey;
|
package/dist/embedder/factory.js
CHANGED
|
@@ -16,7 +16,7 @@ import pLimit from "p-limit";
|
|
|
16
16
|
*/
|
|
17
17
|
export function createEmbedder(config) {
|
|
18
18
|
const { provider, baseUrl, model, apiKey, proxy, timeoutMs } = config.embedding;
|
|
19
|
-
const effectiveTimeoutMs = timeoutMs ??
|
|
19
|
+
const effectiveTimeoutMs = timeoutMs ?? 120000;
|
|
20
20
|
if (provider === "ollama") {
|
|
21
21
|
return new OllamaProvider(baseUrl, model, apiKey, effectiveTimeoutMs, proxy, config.logging.level);
|
|
22
22
|
}
|
package/dist/embedder/health.js
CHANGED
|
@@ -4,7 +4,7 @@ import { postJson } from "./http.js";
|
|
|
4
4
|
* Returns one result per configured model (embedding + description + image_description if enabled).
|
|
5
5
|
*/
|
|
6
6
|
export async function checkProviderHealth(config) {
|
|
7
|
-
const timeoutMs = config.embedding.timeoutMs ??
|
|
7
|
+
const timeoutMs = config.embedding.timeoutMs ?? 120000;
|
|
8
8
|
const checks = [
|
|
9
9
|
checkEmbeddingModel(config, timeoutMs),
|
|
10
10
|
];
|
|
@@ -9,7 +9,7 @@ import type { ProxyConfig } from "../core/config.js";
|
|
|
9
9
|
* @param baseUrl - Ollama server base URL (e.g. http://localhost:11434)
|
|
10
10
|
* @param model - Model name to use for embedding
|
|
11
11
|
* @param apiKey - Optional API key for authenticated endpoints
|
|
12
|
-
|
|
12
|
+
* @param timeoutMs - Request timeout in milliseconds (default 120000)
|
|
13
13
|
* @param proxy - Optional proxy configuration
|
|
14
14
|
* @param logLevel - Optional logging level for debug output
|
|
15
15
|
*/
|
package/dist/embedder/ollama.js
CHANGED
|
@@ -7,7 +7,7 @@ import { appendDebugLog } from "../core/fileLogger.js";
|
|
|
7
7
|
* @param baseUrl - Ollama server base URL (e.g. http://localhost:11434)
|
|
8
8
|
* @param model - Model name to use for embedding
|
|
9
9
|
* @param apiKey - Optional API key for authenticated endpoints
|
|
10
|
-
|
|
10
|
+
* @param timeoutMs - Request timeout in milliseconds (default 120000)
|
|
11
11
|
* @param proxy - Optional proxy configuration
|
|
12
12
|
* @param logLevel - Optional logging level for debug output
|
|
13
13
|
*/
|
|
@@ -19,7 +19,7 @@ export class OllamaProvider {
|
|
|
19
19
|
timeoutMs;
|
|
20
20
|
proxy;
|
|
21
21
|
logLevel;
|
|
22
|
-
constructor(baseUrl, model, apiKey, timeoutMs =
|
|
22
|
+
constructor(baseUrl, model, apiKey, timeoutMs = 120000, proxy, logLevel) {
|
|
23
23
|
this.baseUrl = baseUrl.replace(/\/+$/, "");
|
|
24
24
|
this.model = model;
|
|
25
25
|
this.apiKey = apiKey;
|
|
@@ -9,7 +9,7 @@ import type { ProxyConfig } from "../core/config.js";
|
|
|
9
9
|
* @param baseUrl - API base URL (e.g. https://api.openai.com/v1)
|
|
10
10
|
* @param model - Model name to use for embedding
|
|
11
11
|
* @param apiKey - API key for authentication
|
|
12
|
-
* @param timeoutMs - Request timeout in milliseconds (default
|
|
12
|
+
* @param timeoutMs - Request timeout in milliseconds (default 120000)
|
|
13
13
|
* @param proxy - Optional proxy configuration
|
|
14
14
|
*/
|
|
15
15
|
export declare class OpenAIProvider implements EmbeddingProvider {
|
package/dist/embedder/openai.js
CHANGED
|
@@ -16,7 +16,7 @@ function inferProviderName(baseUrl) {
|
|
|
16
16
|
* @param baseUrl - API base URL (e.g. https://api.openai.com/v1)
|
|
17
17
|
* @param model - Model name to use for embedding
|
|
18
18
|
* @param apiKey - API key for authentication
|
|
19
|
-
* @param timeoutMs - Request timeout in milliseconds (default
|
|
19
|
+
* @param timeoutMs - Request timeout in milliseconds (default 120000)
|
|
20
20
|
* @param proxy - Optional proxy configuration
|
|
21
21
|
*/
|
|
22
22
|
export class OpenAIProvider {
|
|
@@ -27,7 +27,7 @@ export class OpenAIProvider {
|
|
|
27
27
|
timeoutMs;
|
|
28
28
|
proxy;
|
|
29
29
|
provider;
|
|
30
|
-
constructor(baseUrl, model, apiKey, timeoutMs =
|
|
30
|
+
constructor(baseUrl, model, apiKey, timeoutMs = 120000, proxy) {
|
|
31
31
|
this.baseUrl = baseUrl.replace(/\/+$/, "");
|
|
32
32
|
this.model = model;
|
|
33
33
|
this.apiKey = apiKey;
|
package/dist/index.d.ts
CHANGED
|
@@ -25,6 +25,9 @@ export type { ContextOptimizationConfig, ContextOptimizationOptions } from "./re
|
|
|
25
25
|
*/
|
|
26
26
|
export { search, indexWorkspace, getContext, validateConfig, scanWorkspace, getIndexStatusSummary } from "./api.js";
|
|
27
27
|
export type { SearchOptions, IndexOptions, ContextResult, ConfigValidationResult, WorkspaceFile, IndexRunStats } from "./api.js";
|
|
28
|
+
/** Version check and self-update API. */
|
|
29
|
+
export { checkForUpdate, getCurrentVersion, installLatestUpdate, compareVersions } from "./core/version-check.js";
|
|
30
|
+
export type { UpdateInfo, InstallUpdateResult } from "./core/version-check.js";
|
|
28
31
|
/** The plugin server configuration object, used to register OpenCodeRAG as an OpenCode plugin. */
|
|
29
32
|
export declare const server: import("@opencode-ai/plugin").Plugin;
|
|
30
33
|
/** Unique identifier for the OpenCodeRAG plugin. */
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,8 @@ export { DescriptionCache } from "./core/desc-cache.js";
|
|
|
21
21
|
* @module
|
|
22
22
|
*/
|
|
23
23
|
export { search, indexWorkspace, getContext, validateConfig, scanWorkspace, getIndexStatusSummary } from "./api.js";
|
|
24
|
+
/** Version check and self-update API. */
|
|
25
|
+
export { checkForUpdate, getCurrentVersion, installLatestUpdate, compareVersions } from "./core/version-check.js";
|
|
24
26
|
/** Plugin entry — only importable inside OpenCode's runtime. */
|
|
25
27
|
import { ragPlugin } from "./plugin.js";
|
|
26
28
|
/** The plugin server configuration object, used to register OpenCodeRAG as an OpenCode plugin. */
|
package/dist/indexer/pipeline.js
CHANGED
|
@@ -210,10 +210,17 @@ async function runIndexPassInner(options, logger) {
|
|
|
210
210
|
effectiveStore = createVectorStore(options.config, tempStorePath, options.dimension);
|
|
211
211
|
logger.debug(`Rebuilding index in temporary store at ${tempStorePath}`);
|
|
212
212
|
}
|
|
213
|
+
else if (existingCount > 0) {
|
|
214
|
+
// NEVER destroy existing data when we can't do an atomic rebuild.
|
|
215
|
+
// Abort and ask the user to run 'opencode-rag index --force' manually.
|
|
216
|
+
logger.warn("Cannot rebuild safely without embedding dimension — aborting to protect existing data. " +
|
|
217
|
+
"Run 'opencode-rag index --force' manually to rebuild.");
|
|
218
|
+
// Restore manifest entries we just deleted so the next pass can retry incrementally
|
|
219
|
+
return createIndexStats(workspaceFiles.length, manifestStatus);
|
|
220
|
+
}
|
|
213
221
|
else {
|
|
214
|
-
//
|
|
215
|
-
logger.
|
|
216
|
-
await options.store.clear();
|
|
222
|
+
// No existing data — safe to proceed with in-place indexing (no clear needed)
|
|
223
|
+
logger.debug("No existing data; indexing from scratch.");
|
|
217
224
|
}
|
|
218
225
|
}
|
|
219
226
|
const stats = createIndexStats(workspaceFiles.length, manifestStatus);
|
|
@@ -518,7 +525,7 @@ async function runIndexPassInner(options, logger) {
|
|
|
518
525
|
}
|
|
519
526
|
// ── Phase 2: Embed all texts in a single batched call ──────────────────
|
|
520
527
|
const batchSize = isOllama
|
|
521
|
-
? Math.min(options.config.indexing.ollamaMaxBatchSize ??
|
|
528
|
+
? Math.min(options.config.indexing.ollamaMaxBatchSize ?? 500, defaultBatchSize)
|
|
522
529
|
: defaultBatchSize;
|
|
523
530
|
let embeddedDone = 0;
|
|
524
531
|
const allTexts = embedQueue.map(item => item.text);
|
|
@@ -556,6 +563,8 @@ async function runIndexPassInner(options, logger) {
|
|
|
556
563
|
}
|
|
557
564
|
}
|
|
558
565
|
// ── Phase 3: Store + manifest update per file (parallel) ──────────────
|
|
566
|
+
const filesToStore = prepared.filter((p) => !earlyWorkerResults.has(prepared.indexOf(p)) && p.chunks && (p.textToEmbed?.length ?? 0) > 0).length;
|
|
567
|
+
let storedFiles = 0;
|
|
559
568
|
const storeLimit = pLimit(options.config.indexing.concurrency);
|
|
560
569
|
const storeResults = await Promise.all(prepared.map((prep, fi) => storeLimit(async () => {
|
|
561
570
|
if (aborted()) {
|
|
@@ -618,6 +627,8 @@ async function runIndexPassInner(options, logger) {
|
|
|
618
627
|
enqueueManifestSave();
|
|
619
628
|
}
|
|
620
629
|
options.progress?.finishFile(prep.fileLabel);
|
|
630
|
+
storedFiles++;
|
|
631
|
+
logChunkProgress("Storing", prep.fileLabel, storedFiles, filesToStore, storedFiles, filesToStore);
|
|
621
632
|
return result;
|
|
622
633
|
})));
|
|
623
634
|
const workerResults = storeResults;
|
package/dist/plugin.js
CHANGED
|
@@ -22,9 +22,9 @@ import { loadDocProgress, markSubdirectoryDocumented } from "./core/doc-progress
|
|
|
22
22
|
import { loadManifest } from "./core/manifest.js";
|
|
23
23
|
import { createSessionLogger } from "./eval/session-logger.js";
|
|
24
24
|
import { countTokens } from "./eval/token-counter.js";
|
|
25
|
-
import { checkForUpdate } from "./core/version-check.js";
|
|
25
|
+
import { checkForUpdate, getCurrentVersion } from "./core/version-check.js";
|
|
26
26
|
import { destroyAllPooledConnections } from "./embedder/http.js";
|
|
27
|
-
import { existsSync, readFileSync, unlinkSync } from "node:fs";
|
|
27
|
+
import { existsSync, readFileSync, readdirSync, unlinkSync } from "node:fs";
|
|
28
28
|
import path from "node:path";
|
|
29
29
|
import { fileURLToPath } from "node:url";
|
|
30
30
|
import { spawn, execSync } from "node:child_process";
|
|
@@ -39,6 +39,8 @@ const backgroundIndexers = new Map();
|
|
|
39
39
|
const mcpServers = new Map();
|
|
40
40
|
/** Pending update notifications keyed by workspace directory. */
|
|
41
41
|
const pendingUpdateInfo = new Map();
|
|
42
|
+
/** Workspace directories that have already been prompted about an update this session. */
|
|
43
|
+
const notifiedUpdateDirs = new Set();
|
|
42
44
|
/** Guard flag to prevent re-entrant shutdown. */
|
|
43
45
|
let shutdownRegistered = false;
|
|
44
46
|
/** Close all active background indexers and MCP servers, then destroy idle sockets. */
|
|
@@ -59,6 +61,7 @@ async function shutdownPluginResources() {
|
|
|
59
61
|
}
|
|
60
62
|
configCache.clear();
|
|
61
63
|
pendingUpdateInfo.clear();
|
|
64
|
+
notifiedUpdateDirs.clear();
|
|
62
65
|
destroyAllPooledConnections();
|
|
63
66
|
}
|
|
64
67
|
/** Register a process.beforeExit handler so that resources are cleaned up
|
|
@@ -685,17 +688,27 @@ export function createRagHooks(options) {
|
|
|
685
688
|
];
|
|
686
689
|
output.system.unshift(guidance.join("\n"));
|
|
687
690
|
}
|
|
688
|
-
// Inject update
|
|
691
|
+
// Inject a one-time update prompt so the agent asks the user to install.
|
|
692
|
+
// Fires only once per workspace per session to avoid nagging on every turn.
|
|
689
693
|
const updateInfo = pendingUpdateInfo.get(options.worktree);
|
|
690
|
-
if (updateInfo) {
|
|
691
|
-
|
|
692
|
-
|
|
694
|
+
if (updateInfo && !notifiedUpdateDirs.has(options.worktree)) {
|
|
695
|
+
notifiedUpdateDirs.add(options.worktree);
|
|
696
|
+
pendingUpdateInfo.delete(options.worktree);
|
|
697
|
+
output.system.unshift(`A newer OpenCodeRAG version is available: ${updateInfo.currentVersion} → ${updateInfo.latestVersion}. ` +
|
|
698
|
+
`At the very start of your next response, briefly tell the user a new version is available and ask whether ` +
|
|
699
|
+
`they would like you to install it. If they agree, run \`opencode-rag update\` via the bash tool, then tell ` +
|
|
700
|
+
`them to restart OpenCode to load the new version. Do not mention this notice again.`);
|
|
693
701
|
}
|
|
694
702
|
// Inject documentation mode system prompt if enabled
|
|
695
703
|
const docMode = getEffectiveCfg().documentationMode;
|
|
696
704
|
if (docMode?.enabled && docMode.systemPrompt) {
|
|
697
705
|
output.system.unshift(docMode.systemPrompt);
|
|
698
706
|
}
|
|
707
|
+
// Inject wiki mode system prompt if enabled
|
|
708
|
+
const wikiMode = getEffectiveCfg().wikiMode;
|
|
709
|
+
if (wikiMode?.enabled && wikiMode.systemPrompt) {
|
|
710
|
+
output.system.unshift(wikiMode.systemPrompt);
|
|
711
|
+
}
|
|
699
712
|
},
|
|
700
713
|
async "chat.message"(input, output) {
|
|
701
714
|
try {
|
|
@@ -797,6 +810,64 @@ export function createRagHooks(options) {
|
|
|
797
810
|
}
|
|
798
811
|
return;
|
|
799
812
|
}
|
|
813
|
+
// Handle /wiki slash command
|
|
814
|
+
if (text.startsWith("/wiki")) {
|
|
815
|
+
const wikiMode = getEffectiveCfg().wikiMode;
|
|
816
|
+
if (!wikiMode?.enabled) {
|
|
817
|
+
const parts = output?.parts ?? output?.message?.parts;
|
|
818
|
+
if (Array.isArray(parts) && parts.length > 0) {
|
|
819
|
+
const first = parts[0];
|
|
820
|
+
if (typeof first.text === "string") {
|
|
821
|
+
parts[0] = { ...first, text: "Wiki mode is not enabled. Set `wikiMode.enabled` to `true` in opencode-rag.json." };
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
const arg = text.slice(5).trim().toLowerCase();
|
|
827
|
+
const wikiDir = path.join(options.worktree, ".opencode", "wiki");
|
|
828
|
+
const lines = [];
|
|
829
|
+
if (arg === "lint") {
|
|
830
|
+
lines.push("## Wiki Lint", "", "Run a health check on the wiki. Check for:", "- Orphan pages with no inbound [[links]]", "- Stale pages whose sourceRefs files changed since lastReviewed", "- Contradictions between pages", "- Important concepts mentioned but lacking their own page", "", "Append findings to .opencode/wiki/log.md and fix any issues found.");
|
|
831
|
+
}
|
|
832
|
+
else if (arg === "seed") {
|
|
833
|
+
lines.push("## Wiki Seed", "", "Generate the initial wiki from existing sources:", "1. Read README and top-level docs for project overview → create overview page", "2. Scan file structure with get_file_skeleton for major modules → create entity pages", "3. Identify common conventions from code patterns → create concept pages", "4. Create .opencode/wiki/index.md listing all pages", "5. Create .opencode/wiki/log.md with initial entry", "6. Keep pages focused — one concept per page, short prose, cross-referenced with [[links]]");
|
|
834
|
+
}
|
|
835
|
+
else {
|
|
836
|
+
if (existsSync(wikiDir)) {
|
|
837
|
+
const entries = readdirSync(wikiDir);
|
|
838
|
+
const pages = entries.filter((e) => e.endsWith(".md") && e !== "log.md").length;
|
|
839
|
+
const hasIndex = entries.includes("index.md");
|
|
840
|
+
const hasLog = entries.includes("log.md");
|
|
841
|
+
const dirs = entries.filter((e) => !e.includes(".")).length;
|
|
842
|
+
let lastLogEntry = "";
|
|
843
|
+
const logPath = path.join(wikiDir, "log.md");
|
|
844
|
+
if (hasLog && existsSync(logPath)) {
|
|
845
|
+
const logContent = readFileSync(logPath, "utf-8");
|
|
846
|
+
const logLines = logContent.trim().split("\n").filter((l) => l.startsWith("## ["));
|
|
847
|
+
if (logLines.length > 0) {
|
|
848
|
+
lastLogEntry = logLines[logLines.length - 1] ?? "";
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
lines.push("## Wiki Status", "", `Wiki: \`.opencode/wiki/\``, `Pages: ~${pages} (.md files)`, `Subdirectories: \`${dirs}\``, `Index: ${hasIndex ? "yes" : "no"} | Log: ${hasLog ? "yes" : "no"}`);
|
|
852
|
+
if (lastLogEntry) {
|
|
853
|
+
lines.push(`Latest log: \`${lastLogEntry}\``);
|
|
854
|
+
}
|
|
855
|
+
lines.push("", "Commands:", "- `/wiki` — show this status", "- `/wiki lint` — health-check the wiki", "- `/wiki seed` — generate initial wiki pages from the codebase");
|
|
856
|
+
}
|
|
857
|
+
else {
|
|
858
|
+
lines.push("## Wiki Status", "", "No wiki yet at `.opencode/wiki/`.", "Use `/wiki seed` to generate the initial wiki from existing sources.");
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
const wikiMsg = lines.join("\n");
|
|
862
|
+
const parts = output?.parts ?? output?.message?.parts;
|
|
863
|
+
if (Array.isArray(parts) && parts.length > 0) {
|
|
864
|
+
const first = parts[0];
|
|
865
|
+
if (typeof first.text === "string") {
|
|
866
|
+
parts[0] = { ...first, text: wikiMsg };
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
800
871
|
// Handle hotkey-triggered RAG injection (Ctrl+Enter / Ctrl+Alt+Enter)
|
|
801
872
|
const pendingInjection = consumePendingRagInjection(options.storePath);
|
|
802
873
|
if (pendingInjection) {
|
|
@@ -1090,6 +1161,7 @@ export const ragPlugin = async (input, _options) => {
|
|
|
1090
1161
|
// Clean up stale config cache and pending update info for this directory
|
|
1091
1162
|
configCache.delete(input.directory);
|
|
1092
1163
|
pendingUpdateInfo.delete(input.directory);
|
|
1164
|
+
notifiedUpdateDirs.delete(input.directory);
|
|
1093
1165
|
// Clean up idle HTTP sockets from previous provider connections
|
|
1094
1166
|
destroyAllPooledConnections();
|
|
1095
1167
|
appendDebugLog(logFilePath, {
|
|
@@ -1165,6 +1237,7 @@ export const ragPlugin = async (input, _options) => {
|
|
|
1165
1237
|
logLevel,
|
|
1166
1238
|
keywordIndex,
|
|
1167
1239
|
descriptionProvider,
|
|
1240
|
+
dimension: vectorDimension,
|
|
1168
1241
|
});
|
|
1169
1242
|
backgroundIndexers.set(input.directory, indexer);
|
|
1170
1243
|
}
|
|
@@ -1187,18 +1260,13 @@ export const ragPlugin = async (input, _options) => {
|
|
|
1187
1260
|
const mcpInstance = startMcpServerProcess(input.directory, logFilePath, logLevel);
|
|
1188
1261
|
mcpServers.set(input.directory, mcpInstance);
|
|
1189
1262
|
}
|
|
1190
|
-
// Auto-update check (non-blocking, best-effort)
|
|
1263
|
+
// Auto-update check (non-blocking, best-effort). On by default; can be
|
|
1264
|
+
// disabled via `autoUpdate.enabled: false` in opencode-rag.json. When a newer
|
|
1265
|
+
// release is found it is stored and surfaced as a one-time install prompt via
|
|
1266
|
+
// the system transform hook (see createRagHooks).
|
|
1191
1267
|
const autoUpdateCfg = effectiveCfg.autoUpdate;
|
|
1192
1268
|
if (autoUpdateCfg?.enabled) {
|
|
1193
|
-
const currentVersion = (
|
|
1194
|
-
try {
|
|
1195
|
-
const pkgPath = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
1196
|
-
return JSON.parse(readFileSync(pkgPath, "utf-8")).version;
|
|
1197
|
-
}
|
|
1198
|
-
catch {
|
|
1199
|
-
return "0.0.0";
|
|
1200
|
-
}
|
|
1201
|
-
})();
|
|
1269
|
+
const currentVersion = getCurrentVersion();
|
|
1202
1270
|
checkForUpdate(currentVersion)
|
|
1203
1271
|
.then((info) => {
|
|
1204
1272
|
if (info.updateAvailable) {
|
package/dist/tui.js
CHANGED
|
@@ -212,7 +212,9 @@ function getConfigPath(worktree) {
|
|
|
212
212
|
/** Read and parse a JSON file, returning undefined on failure. */
|
|
213
213
|
function readJsonFile(filePath) {
|
|
214
214
|
try {
|
|
215
|
-
|
|
215
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
216
|
+
const stripped = raw.charCodeAt(0) === 0xfeff ? raw.slice(1) : raw;
|
|
217
|
+
return JSON.parse(stripped);
|
|
216
218
|
}
|
|
217
219
|
catch {
|
|
218
220
|
return undefined;
|
|
@@ -341,6 +343,8 @@ function buildSettingCategories(cfg, ro, providers) {
|
|
|
341
343
|
const descRo = (ro.description ?? {});
|
|
342
344
|
const docModeCfg = (cfg.documentationMode ?? {});
|
|
343
345
|
const docModeRo = (ro.documentationMode ?? {});
|
|
346
|
+
const wikiModeCfg = (cfg.wikiMode ?? {});
|
|
347
|
+
const wikiModeRo = (ro.wikiMode ?? {});
|
|
344
348
|
const embeddingCfg = (cfg.embedding ?? {});
|
|
345
349
|
const embeddingRo = (ro.embedding ?? {});
|
|
346
350
|
const tuiCfg = (cfg.tui ?? {});
|
|
@@ -471,6 +475,19 @@ function buildSettingCategories(cfg, ro, providers) {
|
|
|
471
475
|
},
|
|
472
476
|
],
|
|
473
477
|
},
|
|
478
|
+
{
|
|
479
|
+
id: "wiki",
|
|
480
|
+
label: "Wiki Mode",
|
|
481
|
+
description: "Configure the AI-maintained knowledge wiki that synthesizes codebase knowledge over time",
|
|
482
|
+
entries: [
|
|
483
|
+
{
|
|
484
|
+
path: ["wikiMode", "enabled"],
|
|
485
|
+
label: "Wiki mode",
|
|
486
|
+
type: "boolean",
|
|
487
|
+
currentValue: wikiModeRo.enabled ?? wikiModeCfg.enabled ?? false,
|
|
488
|
+
},
|
|
489
|
+
],
|
|
490
|
+
},
|
|
474
491
|
{
|
|
475
492
|
id: "keybindings",
|
|
476
493
|
label: "Keybindings",
|
package/dist/watcher.d.ts
CHANGED
|
@@ -30,6 +30,8 @@ export interface CreateBackgroundIndexerOptions {
|
|
|
30
30
|
keywordIndex?: KeywordIndex;
|
|
31
31
|
/** Optional provider for generating LLM-based chunk descriptions. */
|
|
32
32
|
descriptionProvider?: DescriptionProvider;
|
|
33
|
+
/** Embedding vector dimension — enables atomic rebuilds instead of destructive clear. */
|
|
34
|
+
dimension?: number;
|
|
33
35
|
}
|
|
34
36
|
/** The current operational status of the background indexer watcher. */
|
|
35
37
|
export type WatcherStatus = {
|
package/dist/watcher.js
CHANGED
|
@@ -28,7 +28,7 @@ function writeWatcherStatus(storePath, status) {
|
|
|
28
28
|
* @returns A BackgroundIndexer handle with a close() method for shutdown.
|
|
29
29
|
*/
|
|
30
30
|
export function createBackgroundIndexer(options) {
|
|
31
|
-
const { cwd, storePath, config, store, embedder, logFilePath, logLevel, keywordIndex, descriptionProvider } = options;
|
|
31
|
+
const { cwd, storePath, config, store, embedder, logFilePath, logLevel, keywordIndex, descriptionProvider, dimension } = options;
|
|
32
32
|
writeWatcherStatus(storePath, { running: false, lastRunAt: undefined });
|
|
33
33
|
const ac = new AbortController();
|
|
34
34
|
const updateStatus = (partial) => {
|
|
@@ -45,6 +45,7 @@ export function createBackgroundIndexer(options) {
|
|
|
45
45
|
embedder,
|
|
46
46
|
keywordIndex,
|
|
47
47
|
descriptionProvider,
|
|
48
|
+
dimension,
|
|
48
49
|
filterPaths,
|
|
49
50
|
abortSignal: ac.signal,
|
|
50
51
|
logger: {
|