@s-berdia/opencode-router 0.0.0-stage → 1.0.1

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/USAGE.md ADDED
@@ -0,0 +1,513 @@
1
+ # opencode-router usage guide
2
+
3
+ This guide gives the full procedure to install, use, change, and remove opencode-router.
4
+ Do the steps in sequence. Each command has a label that tells you which shell to use.
5
+
6
+ ## Contents
7
+
8
+ 1. [Before you start](#1-before-you-start)
9
+ 2. [Install the plugin](#2-install-the-plugin)
10
+ 3. [Do the first refresh](#3-do-the-first-refresh)
11
+ 4. [Read the scorecard](#4-read-the-scorecard)
12
+ 5. [Select your plan](#5-select-your-plan)
13
+ 6. [Change the router settings](#6-change-the-router-settings)
14
+ 7. [Use the terminal tool](#7-use-the-terminal-tool)
15
+ 8. [Test the models](#8-test-the-models)
16
+ 9. [Files and folders](#9-files-and-folders)
17
+ 10. [Troubleshooting](#10-troubleshooting)
18
+ 11. [Known limits](#11-known-limits)
19
+ 12. [Remove the router](#12-remove-the-router)
20
+
21
+ ## 1. Before you start
22
+
23
+ Make sure that you have these items:
24
+
25
+ 1. **OpenCode**, installed and signed in. Start it one time, then close it. This step
26
+ makes the model catalog file `~/.cache/opencode/models.json`.
27
+ 2. **Node.js 20 or later.** Do this check:
28
+
29
+ PowerShell, CMD, Git Bash, macOS, or Linux:
30
+
31
+ ```bash
32
+ node --version
33
+ ```
34
+
35
+ The result must be `v20` or higher. If the command fails, install Node.js from
36
+ <https://nodejs.org> (use the LTS version), then open a new terminal.
37
+ 3. **An OpenCode plan:** Go, Go Plus, or no subscription. With no subscription, the router
38
+ uses only free models.
39
+
40
+ `~` means your home folder. On Windows, this is `C:\Users\<your-user-name>`.
41
+
42
+ ## 2. Install the plugin
43
+
44
+ ### Option A: from npm (recommended)
45
+
46
+ 1. Open the global OpenCode config file.
47
+
48
+ PowerShell (Windows):
49
+
50
+ ```powershell
51
+ New-Item -ItemType Directory -Force "$env:USERPROFILE\.config\opencode" | Out-Null
52
+ notepad "$env:USERPROFILE\.config\opencode\opencode.json"
53
+ ```
54
+
55
+ Git Bash, macOS, or Linux:
56
+
57
+ ```bash
58
+ mkdir -p ~/.config/opencode
59
+ nano ~/.config/opencode/opencode.json
60
+ ```
61
+
62
+ If you already have `opencode.jsonc` in that folder, open that file. If Notepad asks
63
+ to make a new file, click **Yes**.
64
+ 2. Make a backup copy of the file before you continue. The router writes the model keys
65
+ in this file. It keeps your comments and all other settings.
66
+ 3. Add `"@s-berdia/opencode-router"` to the `plugin` list. If the file is empty, use all
67
+ of this text:
68
+
69
+ ```json
70
+ {
71
+ "$schema": "https://opencode.ai/config.json",
72
+ "plugin": ["@s-berdia/opencode-router"]
73
+ }
74
+ ```
75
+
76
+ If the file already has a `plugin` list, add the name to the list. Put a comma
77
+ between the items.
78
+ 4. Save the file. Close the editor.
79
+ 5. Close OpenCode. Start OpenCode again. OpenCode downloads the plugin when it starts.
80
+
81
+ To use the router in one project only, put the `plugin` entry in `opencode.json` in the
82
+ root folder of that project, not in the global file.
83
+
84
+ ### Option B: from source
85
+
86
+ Use this option to test changes to the code.
87
+
88
+ 1. Clone and build the project.
89
+
90
+ PowerShell, Git Bash, macOS, or Linux:
91
+
92
+ ```bash
93
+ git clone https://github.com/s-berdia/Opencode_router.git
94
+ cd Opencode_router
95
+ npm install
96
+ npm run build
97
+ ```
98
+
99
+ 2. Get the absolute path of the folder.
100
+
101
+ PowerShell:
102
+
103
+ ```powershell
104
+ (Get-Location).Path
105
+ ```
106
+
107
+ Git Bash, macOS, or Linux:
108
+
109
+ ```bash
110
+ pwd
111
+ ```
112
+
113
+ 3. In `opencode.json`, add the path as a `file:///` URL. Use forward slashes (`/`).
114
+ - Windows example: `"plugin": ["file:///C:/code/Opencode_router"]`
115
+ - macOS or Linux example: `"plugin": ["file:///home/alex/Opencode_router"]`
116
+ 4. Restart OpenCode. After each code change, run `npm run build` and restart OpenCode.
117
+
118
+ ## 3. Do the first refresh
119
+
120
+ 1. In the OpenCode prompt, type `/` and look at the list of commands. Make sure that
121
+ these commands are in the list: `/refresh-routing`, `/routing-status`,
122
+ `/routing-plan`, and `/routing-eval`. If they are not in the list, go to
123
+ [Troubleshooting](#10-troubleshooting).
124
+ 2. Type this command and push Enter:
125
+
126
+ ```text
127
+ /refresh-routing
128
+ ```
129
+
130
+ 3. The router does these steps:
131
+ 1. It reads the model catalog and your settings.
132
+ 2. It gives a score to each model for each agent.
133
+ 3. It writes the best models to the global `opencode.json`. It changes only the
134
+ `model`, `small_model`, and `agent.<role>.model` and `variant` keys.
135
+ 4. It applies the models to the running session.
136
+ 5. It shows the scorecard.
137
+
138
+ You do not have to restart OpenCode. From now on, the router selects the model for each
139
+ message automatically.
140
+
141
+ ## 4. Read the scorecard
142
+
143
+ The scorecard has one block of rows for each role. The first row of a block is the model
144
+ that the role uses. The other rows are the failover models, in sequence.
145
+
146
+ | Column | Meaning |
147
+ | :-- | :-- |
148
+ | Role | The agent: `plan`, `build`, `general`, `explore`, `small_model`, `title`, `summary`, `compaction` |
149
+ | # | Position in the failover list. 1 is the first model |
150
+ | Model | The model ID, in the form `provider/model` |
151
+ | Var | The reasoning level (`high`, `medium`, `low`). `none` means the model default |
152
+ | Score | The total score, 0 to 100 |
153
+ | Intel | Intelligence score, 0 to 100 |
154
+ | Speed | Speed score, 0 to 100 |
155
+ | Endur | Endurance score, 0 to 100. A high value means that the model can run for a long time on your quota |
156
+ | Tokens/5h | The estimated number of tokens that your quota gives in 5 hours. `unlimited` means no limit |
157
+ | Cap/mo | The monthly dollar allowance for the model on your plan. `inf` means no limit |
158
+ | Class | `go` = included in your Go subscription. `free*` = free (your data can be collected). `paid` = Zen pay-as-you-go |
159
+
160
+ The **Notes** section under the table shows problems, for example a missing catalog.
161
+
162
+ ## 5. Select your plan
163
+
164
+ The router finds your plan from the models in your catalog. If it selects the wrong plan,
165
+ set the plan:
166
+
167
+ In OpenCode, type the command with your plan (`go`, `go-plus`, or `free`):
168
+
169
+ ```text
170
+ /routing-plan go-plus
171
+ ```
172
+
173
+ The router saves the plan in `~/.config/opencode/router.config.json`, calculates the
174
+ routing table again, and applies it. It then shows the new scorecard.
175
+
176
+ If the `router.config.json` file of the current project also sets `plan`, that value wins
177
+ in that project. The command shows a note when this occurs.
178
+
179
+ ## 6. Change the router settings
180
+
181
+ The router works with no settings. To change its behavior, make or edit
182
+ `router.config.json`.
183
+
184
+ | File | Applies to |
185
+ | :-- | :-- |
186
+ | `~/.config/opencode/router.config.json` | All projects |
187
+ | `<project>/.opencode/router.config.json` | One project. Its values replace the global values |
188
+
189
+ After each change, run `/refresh-routing`. If a file has a JSON error, or a value that is
190
+ not valid, the router ignores it and shows a note under the scorecard.
191
+
192
+ Each example below is a complete file. To use more than one example, put all the keys in
193
+ one JSON object.
194
+
195
+ ### Keep your main work off free models
196
+
197
+ Free models can collect your data. This setting keeps the `plan`, `build`, and `general`
198
+ agents on models that you pay for. The `explore` agent and the internal helpers can still
199
+ use free models.
200
+
201
+ ```json
202
+ {
203
+ "privacy": { "freeModelsOk": false }
204
+ }
205
+ ```
206
+
207
+ ### Never use some models
208
+
209
+ `*` matches any text. `?` matches one character.
210
+
211
+ ```json
212
+ {
213
+ "denyModels": ["*-free", "*-preview", "opencode-go/kimi-k3"]
214
+ }
215
+ ```
216
+
217
+ ### Use only some models
218
+
219
+ If `allowModels` is not empty, the router uses only the models in the list.
220
+
221
+ ```json
222
+ {
223
+ "allowModels": ["opencode-go/glm-*", "opencode-go/deepseek-*", "opencode/*-free"]
224
+ }
225
+ ```
226
+
227
+ ### Change models earlier or later
228
+
229
+ `softThreshold` sets when the router changes to the next model. `0.7` changes at 70% of
230
+ the 5-hour limit. The default is `0.85`.
231
+
232
+ ```json
233
+ {
234
+ "softThreshold": 0.7
235
+ }
236
+ ```
237
+
238
+ ### Change what is important for one agent
239
+
240
+ Each role has three weights. Use values that add up to 1.
241
+
242
+ ```json
243
+ {
244
+ "weights": {
245
+ "plan": { "intelligence": 0.8, "speed": 0.05, "endurance": 0.15 },
246
+ "build": { "intelligence": 0.5, "speed": 0.2, "endurance": 0.3 }
247
+ }
248
+ }
249
+ ```
250
+
251
+ ### Correct the score of a model
252
+
253
+ Use this setting when you know that a model is better or worse than its score.
254
+
255
+ ```json
256
+ {
257
+ "scores": {
258
+ "opencode-go/deepseek-v4.1-flash": { "intelligence": 82, "speed": 70 }
259
+ }
260
+ }
261
+ ```
262
+
263
+ ### Change the number of failover models or the reasoning level
264
+
265
+ `chainDepth` sets the number of models in each failover list. The default is 4 for the
266
+ agents and 3 for the internal helpers. A value under `roles.<role>` wins over the
267
+ top-level value.
268
+
269
+ ```json
270
+ {
271
+ "chainDepth": 3,
272
+ "roles": {
273
+ "build": { "chainDepth": 2 },
274
+ "plan": { "variant": "medium" }
275
+ }
276
+ }
277
+ ```
278
+
279
+ The `roles.<role>` key also accepts:
280
+
281
+ | Key | Meaning |
282
+ | :-- | :-- |
283
+ | `requires.toolCall` | `true` = use only models that can call tools |
284
+ | `requires.reasoning` | `true` = use only reasoning models |
285
+ | `requires.minContext` | Minimum context window, in tokens |
286
+ | `requires.minOutput` | Minimum output limit, in tokens |
287
+ | `preferFree` | `true` = put free models first |
288
+ | `diversifyFamily` | `true` = do not put two models of the same family in sequence |
289
+
290
+ ### Use a benchmark feed
291
+
292
+ The router can get intelligence and speed scores from a JSON file on the web. If the
293
+ download fails, the router uses its normal scores.
294
+
295
+ ```json
296
+ {
297
+ "feeds": { "enabled": true, "url": "https://example.com/scores.json", "timeoutMs": 5000 }
298
+ }
299
+ ```
300
+
301
+ The file at the URL must have this format. `id` matches one model. `family` matches all
302
+ models of that family.
303
+
304
+ ```json
305
+ {
306
+ "version": 1,
307
+ "models": [
308
+ { "id": "opencode-go/glm-5.3", "intelligence": 90, "speed": 45 },
309
+ { "family": "qwen", "intelligence": 80 }
310
+ ]
311
+ }
312
+ ```
313
+
314
+ ### Advanced cost settings
315
+
316
+ | Key | Default | Meaning |
317
+ | :-- | :-- | :-- |
318
+ | `blend.input` | `0.75` | Part of the tokens that are input |
319
+ | `blend.output` | `0.25` | Part of the tokens that are output |
320
+ | `blend.cacheRead` | `0.1` | Weight of the cache-read price |
321
+ | `enduranceScale.minTokensPer5h` | `0.25` | Millions of tokens per 5 hours that give endurance 0 |
322
+ | `enduranceScale.maxTokensPer5h` | `40` | Millions of tokens per 5 hours that give endurance 100 |
323
+
324
+ The default scores, weights, and monthly allowances are in
325
+ [`model-scores.json`](./model-scores.json).
326
+
327
+ ## 7. Use the terminal tool
328
+
329
+ The terminal tool does the same calculation as the slash commands. Use it for scripts,
330
+ CI, or scheduled jobs.
331
+
332
+ You can run it with `npx` and no install:
333
+
334
+ ```bash
335
+ npx @s-berdia/opencode-router --help
336
+ ```
337
+
338
+ Or install it one time:
339
+
340
+ ```bash
341
+ npm install -g @s-berdia/opencode-router
342
+ opencode-router --help
343
+ ```
344
+
345
+ These commands work in PowerShell, CMD, Git Bash, macOS, and Linux.
346
+
347
+ | Command | What it does |
348
+ | :-- | :-- |
349
+ | `opencode-router refresh --dry-run` | Show the scorecard. Do not write `opencode.json` |
350
+ | `opencode-router refresh --dry-run --verbose` | Also show the source of each score |
351
+ | `opencode-router refresh --scope global` | Write the routing table to the global `opencode.json` |
352
+ | `opencode-router refresh --scope project` | Write the routing table to `opencode.json` in the current folder |
353
+ | `opencode-router refresh --plan free` | Use the `free` profile for this run only |
354
+ | `opencode-router refresh --json` | Print the result as JSON |
355
+ | `opencode-router status` | Show the model in use for each role (after failover) and its 5-hour use |
356
+ | `opencode-router plan go-plus --scope global` | Save the plan in the global settings, then calculate and write the routing table |
357
+ | `opencode-router cat` | Show the catalog path, its date, and the number of models |
358
+ | `opencode-router install --scope global` | Write the slash commands as files in `~/.config/opencode/command/`. If `router.config.json` does not exist, it also writes an empty one |
359
+
360
+ The terminal tool reads the global `router.config.json` and the project file of the
361
+ current folder, in the same way as the plugin.
362
+
363
+ If you do not give `--scope`, the tool uses `project` in a project folder (a folder with
364
+ `.git`, `package.json`, `opencode.json`, `pyproject.toml`, `Cargo.toml`, or `go.mod`) and
365
+ `global` in other folders.
366
+
367
+ ### Apply to a running OpenCode server
368
+
369
+ When OpenCode runs as a server (`opencode serve`), the terminal tool can send the new
370
+ models to it. Set these environment variables before you run `refresh`:
371
+
372
+ | Variable | Value |
373
+ | :-- | :-- |
374
+ | `OPENCODE_SERVER_URL` | The server URL, for example `http://127.0.0.1:4096` |
375
+ | `OPENCODE_SERVER_USERNAME` | Optional. The basic-auth user name |
376
+ | `OPENCODE_SERVER_PASSWORD` | Optional. The basic-auth password |
377
+
378
+ PowerShell:
379
+
380
+ ```powershell
381
+ $env:OPENCODE_SERVER_URL = "http://127.0.0.1:4096"
382
+ npx @s-berdia/opencode-router refresh --scope global
383
+ ```
384
+
385
+ Git Bash, macOS, or Linux:
386
+
387
+ ```bash
388
+ OPENCODE_SERVER_URL="http://127.0.0.1:4096" npx @s-berdia/opencode-router refresh --scope global
389
+ ```
390
+
391
+ If you do not set the URL, a running OpenCode session calculates the routing again on
392
+ its next message (in 15 seconds or less).
393
+
394
+ ## 8. Test the models
395
+
396
+ `/routing-eval` sends one coding prompt to each model in the `build` list. It then
397
+ writes a report that compares the predicted ranks with the measured results. The prompt
398
+ is in [`evals/prompt.md`](./evals/prompt.md).
399
+
400
+ 1. Get your OpenCode API key.
401
+ 2. Set the key in the terminal. Do not write the key in a file that you commit.
402
+
403
+ PowerShell:
404
+
405
+ ```powershell
406
+ $env:OPENCODE_API_KEY = "<your-api-key>"
407
+ ```
408
+
409
+ Git Bash, macOS, or Linux:
410
+
411
+ ```bash
412
+ export OPENCODE_API_KEY="<your-api-key>"
413
+ ```
414
+
415
+ 3. Run the test from the same terminal:
416
+
417
+ ```bash
418
+ npx @s-berdia/opencode-router eval --plan go
419
+ ```
420
+
421
+ 4. Open the report: `~/.config/opencode/router/eval-report.md`.
422
+
423
+ **Caution:** The test sends up to 4 real requests. Each request can use up to 2000
424
+ output tokens of your quota.
425
+
426
+ If `OPENCODE_API_KEY` is not set, the test sends no requests. It writes only the
427
+ predicted ranks.
428
+
429
+ ## 9. Files and folders
430
+
431
+ The router writes these files:
432
+
433
+ | File | Written by | Contents |
434
+ | :-- | :-- | :-- |
435
+ | `~/.config/opencode/router/ledger.json` | Plugin | Spend and tokens per model for 5 hours, 7 days, and 30 days, and cooldowns |
436
+ | `~/.config/opencode/router/routing-table.json` | Plugin | The last routing table |
437
+ | `~/.config/opencode/router/last-apply.json` | Plugin | The result of the last live apply |
438
+ | `~/.config/opencode/router/eval-report.md` | `/routing-eval` | The test report |
439
+ | `~/.config/opencode/opencode.json` | `/refresh-routing`, `/routing-plan` | `model`, `small_model`, and `agent.<role>.model` and `variant` |
440
+ | `~/.config/opencode/router.config.json` | `/routing-plan`, `install` (only if missing) | Your router settings |
441
+
442
+ The router writes each file through a temporary file, so a crash cannot leave half a
443
+ file. If `ledger.json` is damaged, the router keeps it as `ledger.json.corrupt-<time>`
444
+ and starts a new one.
445
+
446
+ If `XDG_CONFIG_HOME` is set, the router uses `$XDG_CONFIG_HOME/opencode` in place of
447
+ `~/.config/opencode`. If `XDG_CACHE_HOME` is set, the router reads the catalog from
448
+ `$XDG_CACHE_HOME/opencode/models.json`.
449
+
450
+ Do not commit the `router/` folder. It contains your usage data.
451
+
452
+ ## 10. Troubleshooting
453
+
454
+ | Problem | Cause | Action |
455
+ | :-- | :-- | :-- |
456
+ | The `/refresh-routing` command is not in the list | OpenCode did not load the plugin | Make sure that the name in `plugin` is `@s-berdia/opencode-router` and the JSON is valid. Restart OpenCode |
457
+ | The scorecard says "No models found" | The catalog file is missing | Start OpenCode and sign in. Then run `npx @s-berdia/opencode-router cat`. "Routed models" must be more than 0 |
458
+ | A role shows `_no eligible model_` | Your settings removed all models for that role | Make `denyModels` and `allowModels` less strict. With `privacy.freeModelsOk: false` on the `free` plan, `plan` and `build` have no models |
459
+ | A slash command shows an error that `node` is not found | Node.js is not on the `PATH` of OpenCode | Install Node.js 20 or later. Restart the computer or the terminal, then OpenCode |
460
+ | "... has a JSON error (... at line N) ... The file was not changed." | Your `opencode.json` has a syntax error | Open the file, correct the line given, save, and run the command again |
461
+ | A note says "router.config.json has a JSON error" | Your router settings file has a syntax error | Correct the line given. Until then, the router uses its default settings |
462
+ | "live PATCH not attempted (no OPENCODE_SERVER_URL set)" | Normal when you run `refresh` in a terminal | No action. Or see [Apply to a running OpenCode server](#apply-to-a-running-opencode-server) |
463
+ | "hot-apply failed: ..." | OpenCode did not accept the change | Restart OpenCode. The file `opencode.json` has the new models |
464
+ | A model that you selected by hand changes back | The router selects the model at each message | See [Known limits](#11-known-limits) |
465
+ | The scores look wrong | A model has no entry in `model-scores.json` | Run `npx @s-berdia/opencode-router refresh --dry-run --verbose`. If the source is `heuristic` or `default`, add the model to `scores` |
466
+
467
+ ## 11. Known limits
468
+
469
+ These are the limits of version 1.0.1.
470
+
471
+ 1. The router sets the model of the `plan`, `build`, `general`, and `explore` agents at
472
+ each message. If you select a different model by hand, the router replaces it. To
473
+ stop this, remove the plugin.
474
+ 2. The internal helpers (`small_model`, `title`, `summary`, `compaction`) do not change
475
+ to a failover model. They use the first model of their list.
476
+ 3. The slash commands always write the global files (`~/.config/opencode/`). To write the
477
+ files of one project, use the terminal tool with `--scope project`.
478
+ 4. The slash commands need Node.js 20 or later on the `PATH`.
479
+ 5. Use numbers are estimates. The router takes 20% of the monthly allowance as the
480
+ 5-hour limit. The 5-hour period starts at the first message after the last period
481
+ ended. If two OpenCode windows write at the same moment, one record can be lost.
482
+ 6. After a rate-limit or quota error, the model goes into a 1-hour cooldown. A
483
+ rate-limit error for a full session puts only the model of that session into a
484
+ cooldown.
485
+
486
+ ## 12. Remove the router
487
+
488
+ 1. Open `opencode.json` (see [step 2](#2-install-the-plugin)).
489
+ 2. Remove `"@s-berdia/opencode-router"` from the `plugin` list.
490
+ 3. Remove the keys that the router wrote: `model`, `small_model`, and the `model` and
491
+ `variant` keys under `agent.plan`, `agent.build`, `agent.general`, `agent.explore`,
492
+ `agent.title`, `agent.summary`, and `agent.compaction`. If you do not remove them,
493
+ OpenCode continues to use those models.
494
+ 4. Save the file.
495
+ 5. Delete the router data.
496
+
497
+ PowerShell:
498
+
499
+ ```powershell
500
+ Remove-Item -Recurse -Force "$env:USERPROFILE\.config\opencode\router"
501
+ Remove-Item -Force "$env:USERPROFILE\.config\opencode\router.config.json" -ErrorAction SilentlyContinue
502
+ ```
503
+
504
+ Git Bash, macOS, or Linux:
505
+
506
+ ```bash
507
+ rm -rf ~/.config/opencode/router
508
+ rm -f ~/.config/opencode/router.config.json
509
+ ```
510
+
511
+ 6. If you ran `install`, delete these files from `~/.config/opencode/command/`:
512
+ `refresh-routing.md`, `routing-status.md`, `routing-plan.md`, and `routing-eval.md`.
513
+ 7. Restart OpenCode.