opencode-model-router 1.0.4 → 1.0.6
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 +15 -1
- package/package.json +1 -1
- package/src/index.ts +63 -9
- package/tiers.json +81 -2
package/README.md
CHANGED
|
@@ -95,7 +95,7 @@ All configuration lives in `tiers.json` at the plugin root. Edit it to match you
|
|
|
95
95
|
|
|
96
96
|
### Presets
|
|
97
97
|
|
|
98
|
-
The plugin ships with
|
|
98
|
+
The plugin ships with four presets:
|
|
99
99
|
|
|
100
100
|
**anthropic** (default):
|
|
101
101
|
| Tier | Model | Notes |
|
|
@@ -111,6 +111,20 @@ The plugin ships with two presets:
|
|
|
111
111
|
| medium | `openai/gpt-5.3-codex` | Default settings (no variant/reasoning override) |
|
|
112
112
|
| heavy | `openai/gpt-5.3-codex` | Variant: `xhigh` |
|
|
113
113
|
|
|
114
|
+
**github-copilot**:
|
|
115
|
+
| Tier | Model | Notes |
|
|
116
|
+
|------|-------|-------|
|
|
117
|
+
| fast | `github-copilot/claude-haiku-4-5` | Cheapest, fastest |
|
|
118
|
+
| medium | `github-copilot/claude-sonnet-4-5` | Balanced coding model |
|
|
119
|
+
| heavy | `github-copilot/claude-opus-4-6` | Variant: `thinking` |
|
|
120
|
+
|
|
121
|
+
**google**:
|
|
122
|
+
| Tier | Model | Notes |
|
|
123
|
+
|------|-------|-------|
|
|
124
|
+
| fast | `google/gemini-2.5-flash` | Cheapest, fastest |
|
|
125
|
+
| medium | `google/gemini-2.5-pro` | Balanced coding model |
|
|
126
|
+
| heavy | `google/gemini-3-pro-preview` | Strongest reasoning in default set |
|
|
127
|
+
|
|
114
128
|
Switch presets with the `/preset` command:
|
|
115
129
|
|
|
116
130
|
```
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Plugin, PluginInput } from "@opencode-ai/plugin";
|
|
2
|
-
import { readFileSync, writeFileSync } from "fs";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
3
|
+
import { homedir } from "os";
|
|
3
4
|
import { dirname, join } from "path";
|
|
4
5
|
import { fileURLToPath } from "url";
|
|
5
6
|
|
|
@@ -37,6 +38,10 @@ interface RouterConfig {
|
|
|
37
38
|
defaultTier: string;
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
interface RouterState {
|
|
42
|
+
activePreset?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
40
45
|
// ---------------------------------------------------------------------------
|
|
41
46
|
// Config loader
|
|
42
47
|
// ---------------------------------------------------------------------------
|
|
@@ -50,13 +55,59 @@ function configPath(): string {
|
|
|
50
55
|
return join(getPluginRoot(), "tiers.json");
|
|
51
56
|
}
|
|
52
57
|
|
|
58
|
+
function statePath(): string {
|
|
59
|
+
return join(homedir(), ".config", "opencode", "opencode-model-router.state.json");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function resolvePresetName(cfg: RouterConfig, requestedPreset: string): string | undefined {
|
|
63
|
+
if (cfg.presets[requestedPreset]) {
|
|
64
|
+
return requestedPreset;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const normalized = requestedPreset.trim().toLowerCase();
|
|
68
|
+
if (!normalized) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return Object.keys(cfg.presets).find((name) => name.toLowerCase() === normalized);
|
|
73
|
+
}
|
|
74
|
+
|
|
53
75
|
function loadConfig(): RouterConfig {
|
|
54
|
-
|
|
76
|
+
const cfg = JSON.parse(readFileSync(configPath(), "utf-8")) as RouterConfig;
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
if (existsSync(statePath())) {
|
|
80
|
+
const state = JSON.parse(readFileSync(statePath(), "utf-8")) as RouterState;
|
|
81
|
+
if (state.activePreset) {
|
|
82
|
+
const resolved = resolvePresetName(cfg, state.activePreset);
|
|
83
|
+
if (resolved) {
|
|
84
|
+
cfg.activePreset = resolved;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
} catch {
|
|
89
|
+
// Ignore state read errors and keep tiers.json active preset
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return cfg;
|
|
55
93
|
}
|
|
56
94
|
|
|
57
95
|
function saveActivePreset(presetName: string): void {
|
|
58
96
|
const cfg = loadConfig();
|
|
59
|
-
|
|
97
|
+
const resolved = resolvePresetName(cfg, presetName);
|
|
98
|
+
if (!resolved) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
cfg.activePreset = resolved;
|
|
103
|
+
|
|
104
|
+
// Persist user-selected preset outside package cache so it survives npm updates
|
|
105
|
+
const presetState: RouterState = { activePreset: resolved };
|
|
106
|
+
const p = statePath();
|
|
107
|
+
mkdirSync(dirname(p), { recursive: true });
|
|
108
|
+
writeFileSync(p, JSON.stringify(presetState, null, 2) + "\n", "utf-8");
|
|
109
|
+
|
|
110
|
+
// Keep local tiers.json in sync as best effort
|
|
60
111
|
writeFileSync(configPath(), JSON.stringify(cfg, null, 2) + "\n", "utf-8");
|
|
61
112
|
}
|
|
62
113
|
|
|
@@ -180,19 +231,22 @@ function buildPresetOutput(cfg: RouterConfig, args: string): string {
|
|
|
180
231
|
}
|
|
181
232
|
|
|
182
233
|
// Switch preset
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
234
|
+
const resolvedPreset = resolvePresetName(cfg, requestedPreset);
|
|
235
|
+
if (resolvedPreset) {
|
|
236
|
+
saveActivePreset(resolvedPreset);
|
|
237
|
+
cfg.activePreset = resolvedPreset;
|
|
238
|
+
const tiers = cfg.presets[resolvedPreset]!;
|
|
186
239
|
const models = Object.entries(tiers)
|
|
187
240
|
.map(([tier, t]) => ` @${tier} -> ${t.model}`)
|
|
188
241
|
.join("\n");
|
|
189
242
|
return [
|
|
190
|
-
`Preset switched to **${
|
|
243
|
+
`Preset switched to **${resolvedPreset}**.`,
|
|
191
244
|
"",
|
|
192
245
|
models,
|
|
193
246
|
"",
|
|
194
|
-
"
|
|
195
|
-
"
|
|
247
|
+
"Selection is now persisted in ~/.config/opencode/opencode-model-router.state.json.",
|
|
248
|
+
"Restart OpenCode for subagent model registration to take effect.",
|
|
249
|
+
"System prompt delegation rules update immediately.",
|
|
196
250
|
].join("\n");
|
|
197
251
|
}
|
|
198
252
|
|
package/tiers.json
CHANGED
|
@@ -79,6 +79,85 @@
|
|
|
79
79
|
"Performance optimization"
|
|
80
80
|
]
|
|
81
81
|
}
|
|
82
|
+
},
|
|
83
|
+
"github-copilot": {
|
|
84
|
+
"fast": {
|
|
85
|
+
"model": "github-copilot/claude-haiku-4-5",
|
|
86
|
+
"description": "Claude Haiku 4.5 via GitHub Copilot for fast exploration and simple tasks",
|
|
87
|
+
"steps": 30,
|
|
88
|
+
"prompt": "You are a fast exploration agent. Focus on speed and efficiency. Read files, search code, and return findings concisely. Do NOT make edits unless explicitly asked.",
|
|
89
|
+
"whenToUse": [
|
|
90
|
+
"Codebase exploration and search",
|
|
91
|
+
"Simple file reads and listing",
|
|
92
|
+
"Grep/glob operations",
|
|
93
|
+
"Quick lookups and research"
|
|
94
|
+
]
|
|
95
|
+
},
|
|
96
|
+
"medium": {
|
|
97
|
+
"model": "github-copilot/claude-sonnet-4-5",
|
|
98
|
+
"description": "Claude Sonnet 4.5 via GitHub Copilot for implementation, refactoring, and tests",
|
|
99
|
+
"steps": 50,
|
|
100
|
+
"prompt": "You are an implementation agent. Write clean, production-quality code matching existing project patterns. Run linters/tests after changes when possible.",
|
|
101
|
+
"whenToUse": [
|
|
102
|
+
"Feature implementation",
|
|
103
|
+
"Refactoring",
|
|
104
|
+
"Writing tests",
|
|
105
|
+
"Code review",
|
|
106
|
+
"Bug fixes"
|
|
107
|
+
]
|
|
108
|
+
},
|
|
109
|
+
"heavy": {
|
|
110
|
+
"model": "github-copilot/claude-opus-4-6",
|
|
111
|
+
"variant": "thinking",
|
|
112
|
+
"description": "Claude Opus 4.6 via GitHub Copilot for architecture, complex debugging, and security",
|
|
113
|
+
"steps": 30,
|
|
114
|
+
"prompt": "You are a senior architecture consultant. Analyze deeply, consider tradeoffs, and provide thorough reasoning. Be exhaustive in your analysis.",
|
|
115
|
+
"whenToUse": [
|
|
116
|
+
"Architecture decisions",
|
|
117
|
+
"Complex debugging (after 2+ failures)",
|
|
118
|
+
"Security review",
|
|
119
|
+
"Performance optimization"
|
|
120
|
+
]
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
"google": {
|
|
124
|
+
"fast": {
|
|
125
|
+
"model": "google/gemini-2.5-flash",
|
|
126
|
+
"description": "Gemini 2.5 Flash for fast exploration and simple tasks",
|
|
127
|
+
"steps": 30,
|
|
128
|
+
"prompt": "You are a fast exploration agent. Focus on speed and efficiency. Read files, search code, and return findings concisely. Do NOT make edits unless explicitly asked.",
|
|
129
|
+
"whenToUse": [
|
|
130
|
+
"Codebase exploration and search",
|
|
131
|
+
"Simple file reads and listing",
|
|
132
|
+
"Grep/glob operations",
|
|
133
|
+
"Quick lookups and research"
|
|
134
|
+
]
|
|
135
|
+
},
|
|
136
|
+
"medium": {
|
|
137
|
+
"model": "google/gemini-2.5-pro",
|
|
138
|
+
"description": "Gemini 2.5 Pro for implementation, refactoring, and tests",
|
|
139
|
+
"steps": 50,
|
|
140
|
+
"prompt": "You are an implementation agent. Write clean, production-quality code matching existing project patterns. Run linters/tests after changes when possible.",
|
|
141
|
+
"whenToUse": [
|
|
142
|
+
"Feature implementation",
|
|
143
|
+
"Refactoring",
|
|
144
|
+
"Writing tests",
|
|
145
|
+
"Code review",
|
|
146
|
+
"Bug fixes"
|
|
147
|
+
]
|
|
148
|
+
},
|
|
149
|
+
"heavy": {
|
|
150
|
+
"model": "google/gemini-3-pro-preview",
|
|
151
|
+
"description": "Gemini 3 Pro Preview for architecture, complex debugging, and security",
|
|
152
|
+
"steps": 30,
|
|
153
|
+
"prompt": "You are a senior architecture consultant. Analyze deeply, consider tradeoffs, and provide thorough reasoning. Be exhaustive in your analysis.",
|
|
154
|
+
"whenToUse": [
|
|
155
|
+
"Architecture decisions",
|
|
156
|
+
"Complex debugging (after 2+ failures)",
|
|
157
|
+
"Security review",
|
|
158
|
+
"Performance optimization"
|
|
159
|
+
]
|
|
160
|
+
}
|
|
82
161
|
}
|
|
83
162
|
},
|
|
84
163
|
"rules": [
|
|
@@ -88,9 +167,9 @@
|
|
|
88
167
|
"When a plan says 'use a heavy/powerful model' -> delegate to @heavy",
|
|
89
168
|
"Default to @medium for implementation tasks you could delegate",
|
|
90
169
|
"Use @fast for any read-only exploration or research task",
|
|
91
|
-
"Keep orchestration (planning, decisions, verification) for yourself
|
|
170
|
+
"Keep orchestration (planning, decisions, verification) for yourself - delegate execution",
|
|
92
171
|
"For trivial tasks (single grep, single file read), execute directly without delegation",
|
|
93
|
-
"Never delegate to @heavy if you are already running on an opus-class model
|
|
172
|
+
"Never delegate to @heavy if you are already running on an opus-class model - do it yourself"
|
|
94
173
|
],
|
|
95
174
|
"defaultTier": "medium"
|
|
96
175
|
}
|