oh-my-opencode-slim 0.8.3 → 0.8.4
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 +33 -7
- package/dist/cli/config-io.d.ts +1 -1
- package/dist/cli/custom-skills.d.ts +2 -2
- package/dist/cli/index.js +108 -53
- package/dist/cli/paths.d.ts +12 -0
- package/dist/cli/providers.d.ts +4 -4
- package/dist/cli/types.d.ts +2 -0
- package/dist/config/loader.d.ts +2 -1
- package/dist/config/schema.d.ts +3 -0
- package/dist/hooks/foreground-fallback/index.d.ts +72 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/index.js +1247 -175
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/lsp/config-store.d.ts +29 -0
- package/dist/tools/lsp/constants.d.ts +18 -2
- package/dist/tools/lsp/index.d.ts +1 -0
- package/dist/tools/lsp/types.d.ts +7 -0
- package/dist/tools/lsp/utils.d.ts +14 -1
- package/oh-my-opencode-slim.schema.json +8 -0
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
bunx oh-my-opencode-slim@latest install
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
The installer generates an OpenAI configuration by default (using `gpt-5.4` and `gpt-5-
|
|
18
|
+
The installer generates an OpenAI configuration by default (using `gpt-5.4` and `gpt-5.4-mini`). No provider questions asked.
|
|
19
19
|
|
|
20
20
|
For non-interactive mode:
|
|
21
21
|
|
|
@@ -23,6 +23,11 @@ For non-interactive mode:
|
|
|
23
23
|
bunx oh-my-opencode-slim@latest install --no-tui --tmux=no --skills=yes
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
+
To force overwrite of an existing configuration:
|
|
27
|
+
```bash
|
|
28
|
+
bunx oh-my-opencode-slim@latest install --reset
|
|
29
|
+
```
|
|
30
|
+
|
|
26
31
|
### For Alternative Providers
|
|
27
32
|
|
|
28
33
|
The default configuration uses OpenAI. To use Kimi, GitHub Copilot, or ZAI Coding Plan, see **[Provider Configurations](docs/provider-configurations.md)** for step-by-step instructions and config examples.
|
|
@@ -55,6 +60,27 @@ https://raw.githubusercontent.com/alvinunreal/oh-my-opencode-slim/refs/heads/mas
|
|
|
55
60
|
- **[Provider Configurations](docs/provider-configurations.md)** - Config examples for all supported providers
|
|
56
61
|
- **[Tmux Integration](docs/tmux-integration.md)** - Real-time agent monitoring with tmux
|
|
57
62
|
|
|
63
|
+
### ✅ Verify Your Setup
|
|
64
|
+
|
|
65
|
+
After installation and authentication, verify all agents are configured and responding:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
opencode
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Then run:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
ping all agents
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
<div align="center">
|
|
78
|
+
<img src="img/ping.png" alt="Ping all agents" width="600">
|
|
79
|
+
<p><i>Confirmation that all six agents are online and ready.</i></p>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
If any agent fails to respond, check your provider authentication and config file.
|
|
83
|
+
|
|
58
84
|
---
|
|
59
85
|
|
|
60
86
|
## 🏛️ Meet the Pantheon
|
|
@@ -119,12 +145,12 @@ https://raw.githubusercontent.com/alvinunreal/oh-my-opencode-slim/refs/heads/mas
|
|
|
119
145
|
</tr>
|
|
120
146
|
<tr>
|
|
121
147
|
<td colspan="2">
|
|
122
|
-
<b>Default Model:</b> <code>openai/gpt-5-
|
|
148
|
+
<b>Default Model:</b> <code>openai/gpt-5.4-mini</code>
|
|
123
149
|
</td>
|
|
124
150
|
</tr>
|
|
125
151
|
<tr>
|
|
126
152
|
<td colspan="2">
|
|
127
|
-
<b>Recommended Models:</b> <code>cerebras/zai-glm-4.7</code> <code>google/gemini-3.1-pro-preview</code> <code>openai/gpt-5-
|
|
153
|
+
<b>Recommended Models:</b> <code>cerebras/zai-glm-4.7</code> <code>google/gemini-3.1-pro-preview</code> <code>openai/gpt-5.4-mini</code>
|
|
128
154
|
</td>
|
|
129
155
|
</tr>
|
|
130
156
|
</table>
|
|
@@ -191,12 +217,12 @@ https://raw.githubusercontent.com/alvinunreal/oh-my-opencode-slim/refs/heads/mas
|
|
|
191
217
|
</tr>
|
|
192
218
|
<tr>
|
|
193
219
|
<td colspan="2">
|
|
194
|
-
<b>Default Model:</b> <code>openai/gpt-5-
|
|
220
|
+
<b>Default Model:</b> <code>openai/gpt-5.4-mini</code>
|
|
195
221
|
</td>
|
|
196
222
|
</tr>
|
|
197
223
|
<tr>
|
|
198
224
|
<td colspan="2">
|
|
199
|
-
<b>Recommended Models:</b> <code>google/gemini-3.1-pro-preview</code> <code>openai/gpt-5-
|
|
225
|
+
<b>Recommended Models:</b> <code>google/gemini-3.1-pro-preview</code> <code>openai/gpt-5.4-mini</code>
|
|
200
226
|
</td>
|
|
201
227
|
</tr>
|
|
202
228
|
</table>
|
|
@@ -263,12 +289,12 @@ https://raw.githubusercontent.com/alvinunreal/oh-my-opencode-slim/refs/heads/mas
|
|
|
263
289
|
</tr>
|
|
264
290
|
<tr>
|
|
265
291
|
<td colspan="2">
|
|
266
|
-
<b>Default Model:</b> <code>openai/gpt-5-
|
|
292
|
+
<b>Default Model:</b> <code>openai/gpt-5.4-mini</code>
|
|
267
293
|
</td>
|
|
268
294
|
</tr>
|
|
269
295
|
<tr>
|
|
270
296
|
<td colspan="2">
|
|
271
|
-
<b>Recommended Models:</b> <code>cerebras/zai-glm-4.7</code> <code>google/gemini-3.1-pro-preview</code> <code>openai/gpt-5-
|
|
297
|
+
<b>Recommended Models:</b> <code>cerebras/zai-glm-4.7</code> <code>google/gemini-3.1-pro-preview</code> <code>openai/gpt-5.4-mini</code>
|
|
272
298
|
</td>
|
|
273
299
|
</tr>
|
|
274
300
|
</table>
|
package/dist/cli/config-io.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export declare function parseConfig(path: string): {
|
|
|
16
16
|
*/
|
|
17
17
|
export declare function writeConfig(configPath: string, config: OpenCodeConfig): void;
|
|
18
18
|
export declare function addPluginToOpenCodeConfig(): Promise<ConfigMergeResult>;
|
|
19
|
-
export declare function writeLiteConfig(installConfig: InstallConfig): ConfigMergeResult;
|
|
19
|
+
export declare function writeLiteConfig(installConfig: InstallConfig, targetPath?: string): ConfigMergeResult;
|
|
20
20
|
export declare function disableDefaultAgents(): ConfigMergeResult;
|
|
21
21
|
export declare function canModifyOpenCodeConfig(): boolean;
|
|
22
22
|
export declare function detectCurrentConfig(): DetectedConfig;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A custom skill bundled in this repository.
|
|
3
|
-
* Unlike npx-installed skills, these are copied from src/skills/ to
|
|
3
|
+
* Unlike npx-installed skills, these are copied from src/skills/ to the OpenCode skills directory
|
|
4
4
|
*/
|
|
5
5
|
export interface CustomSkill {
|
|
6
6
|
/** Skill name (folder name) */
|
|
@@ -21,7 +21,7 @@ export declare const CUSTOM_SKILLS: CustomSkill[];
|
|
|
21
21
|
*/
|
|
22
22
|
export declare function getCustomSkillsDir(): string;
|
|
23
23
|
/**
|
|
24
|
-
* Install a custom skill by copying from src/skills/ to
|
|
24
|
+
* Install a custom skill by copying from src/skills/ to the OpenCode skills directory
|
|
25
25
|
* @param skill - The custom skill to install
|
|
26
26
|
* @param projectRoot - Root directory of oh-my-opencode-slim project
|
|
27
27
|
* @returns True if installation succeeded, false otherwise
|
package/dist/cli/index.js
CHANGED
|
@@ -11,6 +11,9 @@ var __export = (target, all) => {
|
|
|
11
11
|
});
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
+
// src/cli/install.ts
|
|
15
|
+
import { existsSync as existsSync4 } from "fs";
|
|
16
|
+
|
|
14
17
|
// src/cli/config-io.ts
|
|
15
18
|
import {
|
|
16
19
|
copyFileSync as copyFileSync2,
|
|
@@ -24,20 +27,47 @@ import {
|
|
|
24
27
|
// src/cli/paths.ts
|
|
25
28
|
import { existsSync, mkdirSync } from "fs";
|
|
26
29
|
import { homedir } from "os";
|
|
27
|
-
import { join } from "path";
|
|
28
|
-
function
|
|
30
|
+
import { dirname, join } from "path";
|
|
31
|
+
function getDefaultOpenCodeConfigDir() {
|
|
29
32
|
const userConfigDir = process.env.XDG_CONFIG_HOME ? process.env.XDG_CONFIG_HOME : join(homedir(), ".config");
|
|
30
33
|
return join(userConfigDir, "opencode");
|
|
31
34
|
}
|
|
35
|
+
function getCustomOpenCodeConfigDir() {
|
|
36
|
+
const configDir = process.env.OPENCODE_CONFIG_DIR?.trim();
|
|
37
|
+
return configDir || undefined;
|
|
38
|
+
}
|
|
39
|
+
function getConfigDir() {
|
|
40
|
+
const customConfigDir = getCustomOpenCodeConfigDir();
|
|
41
|
+
if (customConfigDir) {
|
|
42
|
+
return customConfigDir;
|
|
43
|
+
}
|
|
44
|
+
return getDefaultOpenCodeConfigDir();
|
|
45
|
+
}
|
|
46
|
+
function getOpenCodeConfigPaths() {
|
|
47
|
+
const configDir = getDefaultOpenCodeConfigDir();
|
|
48
|
+
return [join(configDir, "opencode.json"), join(configDir, "opencode.jsonc")];
|
|
49
|
+
}
|
|
32
50
|
function getConfigJson() {
|
|
33
|
-
return
|
|
51
|
+
return getOpenCodeConfigPaths()[0];
|
|
34
52
|
}
|
|
35
53
|
function getConfigJsonc() {
|
|
36
|
-
return
|
|
54
|
+
return getOpenCodeConfigPaths()[1];
|
|
37
55
|
}
|
|
38
56
|
function getLiteConfig() {
|
|
39
57
|
return join(getConfigDir(), "oh-my-opencode-slim.json");
|
|
40
58
|
}
|
|
59
|
+
function getLiteConfigJsonc() {
|
|
60
|
+
return join(getConfigDir(), "oh-my-opencode-slim.jsonc");
|
|
61
|
+
}
|
|
62
|
+
function getExistingLiteConfigPath() {
|
|
63
|
+
const jsonPath = getLiteConfig();
|
|
64
|
+
if (existsSync(jsonPath))
|
|
65
|
+
return jsonPath;
|
|
66
|
+
const jsoncPath = getLiteConfigJsonc();
|
|
67
|
+
if (existsSync(jsoncPath))
|
|
68
|
+
return jsoncPath;
|
|
69
|
+
return jsonPath;
|
|
70
|
+
}
|
|
41
71
|
function getExistingConfigPath() {
|
|
42
72
|
const jsonPath = getConfigJson();
|
|
43
73
|
if (existsSync(jsonPath))
|
|
@@ -53,6 +83,12 @@ function ensureConfigDir() {
|
|
|
53
83
|
mkdirSync(configDir, { recursive: true });
|
|
54
84
|
}
|
|
55
85
|
}
|
|
86
|
+
function ensureOpenCodeConfigDir() {
|
|
87
|
+
const configDir = dirname(getConfigJson());
|
|
88
|
+
if (!existsSync(configDir)) {
|
|
89
|
+
mkdirSync(configDir, { recursive: true });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
56
92
|
|
|
57
93
|
// src/config/constants.ts
|
|
58
94
|
var SUBAGENT_NAMES = [
|
|
@@ -2305,7 +2341,7 @@ class Doc {
|
|
|
2305
2341
|
var version = {
|
|
2306
2342
|
major: 4,
|
|
2307
2343
|
minor: 3,
|
|
2308
|
-
patch:
|
|
2344
|
+
patch: 6
|
|
2309
2345
|
};
|
|
2310
2346
|
|
|
2311
2347
|
// node_modules/zod/v4/core/schemas.js
|
|
@@ -3591,7 +3627,7 @@ var $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
|
|
|
3591
3627
|
if (keyResult instanceof Promise) {
|
|
3592
3628
|
throw new Error("Async schemas not supported in object keys currently");
|
|
3593
3629
|
}
|
|
3594
|
-
const checkNumericKey = typeof key === "string" && number.test(key) && keyResult.issues.length
|
|
3630
|
+
const checkNumericKey = typeof key === "string" && number.test(key) && keyResult.issues.length;
|
|
3595
3631
|
if (checkNumericKey) {
|
|
3596
3632
|
const retryResult = def.keyType._zod.run({ value: Number(key), issues: [] }, ctx);
|
|
3597
3633
|
if (retryResult instanceof Promise) {
|
|
@@ -10962,7 +10998,7 @@ function finalize(ctx, schema) {
|
|
|
10962
10998
|
}
|
|
10963
10999
|
}
|
|
10964
11000
|
}
|
|
10965
|
-
if (refSchema.$ref) {
|
|
11001
|
+
if (refSchema.$ref && refSeen.def) {
|
|
10966
11002
|
for (const key in schema2) {
|
|
10967
11003
|
if (key === "$ref" || key === "allOf")
|
|
10968
11004
|
continue;
|
|
@@ -13672,10 +13708,12 @@ var BackgroundTaskConfigSchema = exports_external.object({
|
|
|
13672
13708
|
var FailoverConfigSchema = exports_external.object({
|
|
13673
13709
|
enabled: exports_external.boolean().default(true),
|
|
13674
13710
|
timeoutMs: exports_external.number().min(0).default(15000),
|
|
13711
|
+
retryDelayMs: exports_external.number().min(0).default(500),
|
|
13675
13712
|
chains: FallbackChainsSchema.default({})
|
|
13676
13713
|
});
|
|
13677
13714
|
var PluginConfigSchema = exports_external.object({
|
|
13678
13715
|
preset: exports_external.string().optional(),
|
|
13716
|
+
setDefaultAgent: exports_external.boolean().optional(),
|
|
13679
13717
|
scoringEngineVersion: exports_external.enum(["v1", "v2-shadow", "v2"]).optional(),
|
|
13680
13718
|
balanceProviderUsage: exports_external.boolean().optional(),
|
|
13681
13719
|
manualPlan: ManualPlanSchema.optional(),
|
|
@@ -13707,8 +13745,7 @@ import {
|
|
|
13707
13745
|
readdirSync,
|
|
13708
13746
|
statSync
|
|
13709
13747
|
} from "fs";
|
|
13710
|
-
import {
|
|
13711
|
-
import { dirname, join as join2 } from "path";
|
|
13748
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
13712
13749
|
import { fileURLToPath } from "url";
|
|
13713
13750
|
var CUSTOM_SKILLS = [
|
|
13714
13751
|
{
|
|
@@ -13719,7 +13756,7 @@ var CUSTOM_SKILLS = [
|
|
|
13719
13756
|
}
|
|
13720
13757
|
];
|
|
13721
13758
|
function getCustomSkillsDir() {
|
|
13722
|
-
return join2(
|
|
13759
|
+
return join2(getConfigDir(), "skills");
|
|
13723
13760
|
}
|
|
13724
13761
|
function copyDirRecursive(src, dest) {
|
|
13725
13762
|
if (!existsSync2(dest)) {
|
|
@@ -13733,7 +13770,7 @@ function copyDirRecursive(src, dest) {
|
|
|
13733
13770
|
if (stat.isDirectory()) {
|
|
13734
13771
|
copyDirRecursive(srcPath, destPath);
|
|
13735
13772
|
} else {
|
|
13736
|
-
const destDir =
|
|
13773
|
+
const destDir = dirname2(destPath);
|
|
13737
13774
|
if (!existsSync2(destDir)) {
|
|
13738
13775
|
mkdirSync2(destDir, { recursive: true });
|
|
13739
13776
|
}
|
|
@@ -13819,10 +13856,10 @@ var MODEL_MAPPINGS = {
|
|
|
13819
13856
|
openai: {
|
|
13820
13857
|
orchestrator: { model: "openai/gpt-5.4" },
|
|
13821
13858
|
oracle: { model: "openai/gpt-5.4", variant: "high" },
|
|
13822
|
-
librarian: { model: "openai/gpt-5-
|
|
13823
|
-
explorer: { model: "openai/gpt-5-
|
|
13824
|
-
designer: { model: "openai/gpt-5-
|
|
13825
|
-
fixer: { model: "openai/gpt-5-
|
|
13859
|
+
librarian: { model: "openai/gpt-5.4-mini", variant: "low" },
|
|
13860
|
+
explorer: { model: "openai/gpt-5.4-mini", variant: "low" },
|
|
13861
|
+
designer: { model: "openai/gpt-5.4-mini", variant: "medium" },
|
|
13862
|
+
fixer: { model: "openai/gpt-5.4-mini", variant: "low" }
|
|
13826
13863
|
},
|
|
13827
13864
|
kimi: {
|
|
13828
13865
|
orchestrator: { model: "kimi-for-coding/k2p5" },
|
|
@@ -13837,7 +13874,10 @@ var MODEL_MAPPINGS = {
|
|
|
13837
13874
|
oracle: { model: "github-copilot/claude-opus-4.6", variant: "high" },
|
|
13838
13875
|
librarian: { model: "github-copilot/grok-code-fast-1", variant: "low" },
|
|
13839
13876
|
explorer: { model: "github-copilot/grok-code-fast-1", variant: "low" },
|
|
13840
|
-
designer: {
|
|
13877
|
+
designer: {
|
|
13878
|
+
model: "github-copilot/gemini-3.1-pro-preview",
|
|
13879
|
+
variant: "medium"
|
|
13880
|
+
},
|
|
13841
13881
|
fixer: { model: "github-copilot/claude-sonnet-4.6", variant: "low" }
|
|
13842
13882
|
},
|
|
13843
13883
|
"zai-plan": {
|
|
@@ -13932,16 +13972,16 @@ function writeConfig(configPath, config2) {
|
|
|
13932
13972
|
renameSync(tmpPath, configPath);
|
|
13933
13973
|
}
|
|
13934
13974
|
async function addPluginToOpenCodeConfig() {
|
|
13975
|
+
const configPath = getExistingConfigPath();
|
|
13935
13976
|
try {
|
|
13936
|
-
|
|
13977
|
+
ensureOpenCodeConfigDir();
|
|
13937
13978
|
} catch (err) {
|
|
13938
13979
|
return {
|
|
13939
13980
|
success: false,
|
|
13940
|
-
configPath
|
|
13981
|
+
configPath,
|
|
13941
13982
|
error: `Failed to create config directory: ${err}`
|
|
13942
13983
|
};
|
|
13943
13984
|
}
|
|
13944
|
-
const configPath = getExistingConfigPath();
|
|
13945
13985
|
try {
|
|
13946
13986
|
const { config: parsedConfig, error: error48 } = parseConfig(configPath);
|
|
13947
13987
|
if (error48) {
|
|
@@ -13966,8 +14006,8 @@ async function addPluginToOpenCodeConfig() {
|
|
|
13966
14006
|
};
|
|
13967
14007
|
}
|
|
13968
14008
|
}
|
|
13969
|
-
function writeLiteConfig(installConfig) {
|
|
13970
|
-
const configPath = getLiteConfig();
|
|
14009
|
+
function writeLiteConfig(installConfig, targetPath) {
|
|
14010
|
+
const configPath = targetPath ?? getLiteConfig();
|
|
13971
14011
|
try {
|
|
13972
14012
|
ensureConfigDir();
|
|
13973
14013
|
const config2 = generateLiteConfig(installConfig);
|
|
@@ -13992,7 +14032,7 @@ function writeLiteConfig(installConfig) {
|
|
|
13992
14032
|
function disableDefaultAgents() {
|
|
13993
14033
|
const configPath = getExistingConfigPath();
|
|
13994
14034
|
try {
|
|
13995
|
-
|
|
14035
|
+
ensureOpenCodeConfigDir();
|
|
13996
14036
|
const { config: parsedConfig, error: error48 } = parseConfig(configPath);
|
|
13997
14037
|
if (error48) {
|
|
13998
14038
|
return {
|
|
@@ -14207,7 +14247,9 @@ async function checkOpenCodeInstalled() {
|
|
|
14207
14247
|
}
|
|
14208
14248
|
const version2 = await getOpenCodeVersion();
|
|
14209
14249
|
const path = getOpenCodePath();
|
|
14210
|
-
|
|
14250
|
+
const detectedVersion = version2 ?? "";
|
|
14251
|
+
const pathInfo = path ? ` (${DIM}${path}${RESET})` : "";
|
|
14252
|
+
printSuccess(`OpenCode ${detectedVersion} detected${pathInfo}`);
|
|
14211
14253
|
return { ok: true, version: version2 ?? undefined, path: path ?? undefined };
|
|
14212
14254
|
}
|
|
14213
14255
|
function handleStepResult(result, successMsg) {
|
|
@@ -14224,9 +14266,10 @@ function formatConfigSummary() {
|
|
|
14224
14266
|
lines.push("");
|
|
14225
14267
|
lines.push(` ${BOLD}Preset:${RESET} ${BLUE}openai${RESET}`);
|
|
14226
14268
|
lines.push(` ${SYMBOLS.check} OpenAI (default)`);
|
|
14227
|
-
|
|
14228
|
-
lines.push(` ${DIM}\u25CB
|
|
14229
|
-
lines.push(` ${DIM}\u25CB
|
|
14269
|
+
const seeDocs = "see docs/provider-configurations.md";
|
|
14270
|
+
lines.push(` ${DIM}\u25CB Kimi \u2014 ${seeDocs}${RESET}`);
|
|
14271
|
+
lines.push(` ${DIM}\u25CB GitHub Copilot \u2014 ${seeDocs}${RESET}`);
|
|
14272
|
+
lines.push(` ${DIM}\u25CB ZAI Coding Plan \u2014 ${seeDocs}${RESET}`);
|
|
14230
14273
|
return lines.join(`
|
|
14231
14274
|
`);
|
|
14232
14275
|
}
|
|
@@ -14248,25 +14291,21 @@ async function runInstall(config2) {
|
|
|
14248
14291
|
if (!ok)
|
|
14249
14292
|
return 1;
|
|
14250
14293
|
}
|
|
14251
|
-
|
|
14252
|
-
|
|
14253
|
-
|
|
14254
|
-
|
|
14255
|
-
|
|
14256
|
-
|
|
14257
|
-
|
|
14258
|
-
return 1;
|
|
14259
|
-
}
|
|
14294
|
+
printStep(step++, totalSteps, "Adding oh-my-opencode-slim plugin...");
|
|
14295
|
+
if (config2.dryRun) {
|
|
14296
|
+
printInfo("Dry run mode - skipping plugin installation");
|
|
14297
|
+
} else {
|
|
14298
|
+
const pluginResult = await addPluginToOpenCodeConfig();
|
|
14299
|
+
if (!handleStepResult(pluginResult, "Plugin added"))
|
|
14300
|
+
return 1;
|
|
14260
14301
|
}
|
|
14261
|
-
|
|
14262
|
-
|
|
14263
|
-
|
|
14264
|
-
|
|
14265
|
-
|
|
14266
|
-
|
|
14267
|
-
|
|
14268
|
-
return 1;
|
|
14269
|
-
}
|
|
14302
|
+
printStep(step++, totalSteps, "Disabling OpenCode default agents...");
|
|
14303
|
+
if (config2.dryRun) {
|
|
14304
|
+
printInfo("Dry run mode - skipping agent disabling");
|
|
14305
|
+
} else {
|
|
14306
|
+
const agentResult = disableDefaultAgents();
|
|
14307
|
+
if (!handleStepResult(agentResult, "Default agents disabled"))
|
|
14308
|
+
return 1;
|
|
14270
14309
|
}
|
|
14271
14310
|
printStep(step++, totalSteps, "Writing oh-my-opencode-slim configuration...");
|
|
14272
14311
|
if (config2.dryRun) {
|
|
@@ -14276,9 +14315,15 @@ async function runInstall(config2) {
|
|
|
14276
14315
|
${JSON.stringify(liteConfig, null, 2)}
|
|
14277
14316
|
`);
|
|
14278
14317
|
} else {
|
|
14279
|
-
const
|
|
14280
|
-
|
|
14281
|
-
|
|
14318
|
+
const configPath = getExistingLiteConfigPath();
|
|
14319
|
+
const configExists = existsSync4(configPath);
|
|
14320
|
+
if (configExists && !config2.reset) {
|
|
14321
|
+
printInfo(`Configuration already exists at ${configPath}. ` + "Use --reset to overwrite.");
|
|
14322
|
+
} else {
|
|
14323
|
+
const liteResult = writeLiteConfig(config2, configExists ? configPath : undefined);
|
|
14324
|
+
if (!handleStepResult(liteResult, configExists ? "Config reset" : "Config written"))
|
|
14325
|
+
return 1;
|
|
14326
|
+
}
|
|
14282
14327
|
}
|
|
14283
14328
|
if (config2.installSkills) {
|
|
14284
14329
|
printStep(step++, totalSteps, "Installing recommended skills...");
|
|
@@ -14319,22 +14364,27 @@ ${JSON.stringify(liteConfig, null, 2)}
|
|
|
14319
14364
|
printInfo(`Skipped: ${skill.name} (already installed)`);
|
|
14320
14365
|
}
|
|
14321
14366
|
}
|
|
14322
|
-
|
|
14367
|
+
const totalCustom = CUSTOM_SKILLS.length;
|
|
14368
|
+
printSuccess(`${customSkillsInstalled}/${totalCustom} custom skills processed`);
|
|
14323
14369
|
}
|
|
14324
14370
|
}
|
|
14325
14371
|
console.log();
|
|
14326
14372
|
console.log(formatConfigSummary());
|
|
14327
14373
|
console.log();
|
|
14328
|
-
|
|
14374
|
+
const statusMsg = isUpdate ? "Configuration updated!" : "Installation complete!";
|
|
14375
|
+
console.log(`${SYMBOLS.star} ${BOLD}${GREEN}${statusMsg}${RESET}`);
|
|
14329
14376
|
console.log();
|
|
14330
14377
|
console.log(`${BOLD}Next steps:${RESET}`);
|
|
14331
14378
|
console.log();
|
|
14332
14379
|
console.log(` 1. Start OpenCode:`);
|
|
14333
14380
|
console.log(` ${BLUE}$ opencode${RESET}`);
|
|
14334
14381
|
console.log();
|
|
14335
|
-
|
|
14336
|
-
console.log(`${BOLD}
|
|
14337
|
-
|
|
14382
|
+
const modelsInfo = "Default configuration uses OpenAI models (gpt-5.4 / gpt-5.4-mini).";
|
|
14383
|
+
console.log(`${BOLD}${modelsInfo}${RESET}`);
|
|
14384
|
+
const altProviders = "For alternative providers (Kimi, GitHub Copilot, ZAI Coding Plan)";
|
|
14385
|
+
console.log(`${BOLD}${altProviders}, see:${RESET}`);
|
|
14386
|
+
const docsUrl = "https://github.com/alvinunreal/oh-my-opencode-slim/" + "blob/master/docs/provider-configurations.md";
|
|
14387
|
+
console.log(` ${BLUE}${docsUrl}${RESET}`);
|
|
14338
14388
|
console.log();
|
|
14339
14389
|
return 0;
|
|
14340
14390
|
}
|
|
@@ -14343,7 +14393,8 @@ async function install(args) {
|
|
|
14343
14393
|
hasTmux: args.tmux === "yes",
|
|
14344
14394
|
installSkills: args.skills === "yes",
|
|
14345
14395
|
installCustomSkills: args.skills === "yes",
|
|
14346
|
-
dryRun: args.dryRun
|
|
14396
|
+
dryRun: args.dryRun,
|
|
14397
|
+
reset: args.reset ?? false
|
|
14347
14398
|
};
|
|
14348
14399
|
return runInstall(config2);
|
|
14349
14400
|
}
|
|
@@ -14362,6 +14413,8 @@ function parseArgs(args) {
|
|
|
14362
14413
|
result.skills = arg.split("=")[1];
|
|
14363
14414
|
} else if (arg === "--dry-run") {
|
|
14364
14415
|
result.dryRun = true;
|
|
14416
|
+
} else if (arg === "--reset") {
|
|
14417
|
+
result.reset = true;
|
|
14365
14418
|
} else if (arg === "-h" || arg === "--help") {
|
|
14366
14419
|
printHelp();
|
|
14367
14420
|
process.exit(0);
|
|
@@ -14380,6 +14433,7 @@ Options:
|
|
|
14380
14433
|
--skills=yes|no Install recommended skills (yes/no)
|
|
14381
14434
|
--no-tui Non-interactive mode
|
|
14382
14435
|
--dry-run Simulate install without writing files
|
|
14436
|
+
--reset Force overwrite of existing configuration
|
|
14383
14437
|
-h, --help Show this help message
|
|
14384
14438
|
|
|
14385
14439
|
The installer generates an OpenAI configuration by default.
|
|
@@ -14388,6 +14442,7 @@ For alternative providers, see docs/provider-configurations.md.
|
|
|
14388
14442
|
Examples:
|
|
14389
14443
|
bunx oh-my-opencode-slim install
|
|
14390
14444
|
bunx oh-my-opencode-slim install --no-tui --tmux=no --skills=yes
|
|
14445
|
+
bunx oh-my-opencode-slim install --reset
|
|
14391
14446
|
`);
|
|
14392
14447
|
}
|
|
14393
14448
|
async function main() {
|
package/dist/cli/paths.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the OpenCode plugin config directory.
|
|
3
|
+
*
|
|
4
|
+
* Resolution order:
|
|
5
|
+
* 1. OPENCODE_CONFIG_DIR (custom OpenCode directory)
|
|
6
|
+
* 2. XDG_CONFIG_HOME/opencode
|
|
7
|
+
* 3. ~/.config/opencode
|
|
8
|
+
*/
|
|
1
9
|
export declare function getConfigDir(): string;
|
|
2
10
|
export declare function getOpenCodeConfigPaths(): string[];
|
|
3
11
|
export declare function getConfigJson(): string;
|
|
@@ -7,3 +15,7 @@ export declare function getLiteConfigJsonc(): string;
|
|
|
7
15
|
export declare function getExistingLiteConfigPath(): string;
|
|
8
16
|
export declare function getExistingConfigPath(): string;
|
|
9
17
|
export declare function ensureConfigDir(): void;
|
|
18
|
+
/**
|
|
19
|
+
* Ensure the directory for OpenCode's main config file exists.
|
|
20
|
+
*/
|
|
21
|
+
export declare function ensureOpenCodeConfigDir(): void;
|
package/dist/cli/providers.d.ts
CHANGED
|
@@ -9,19 +9,19 @@ export declare const MODEL_MAPPINGS: {
|
|
|
9
9
|
readonly variant: "high";
|
|
10
10
|
};
|
|
11
11
|
readonly librarian: {
|
|
12
|
-
readonly model: "openai/gpt-5-
|
|
12
|
+
readonly model: "openai/gpt-5.4-mini";
|
|
13
13
|
readonly variant: "low";
|
|
14
14
|
};
|
|
15
15
|
readonly explorer: {
|
|
16
|
-
readonly model: "openai/gpt-5-
|
|
16
|
+
readonly model: "openai/gpt-5.4-mini";
|
|
17
17
|
readonly variant: "low";
|
|
18
18
|
};
|
|
19
19
|
readonly designer: {
|
|
20
|
-
readonly model: "openai/gpt-5-
|
|
20
|
+
readonly model: "openai/gpt-5.4-mini";
|
|
21
21
|
readonly variant: "medium";
|
|
22
22
|
};
|
|
23
23
|
readonly fixer: {
|
|
24
|
-
readonly model: "openai/gpt-5-
|
|
24
|
+
readonly model: "openai/gpt-5.4-mini";
|
|
25
25
|
readonly variant: "low";
|
|
26
26
|
};
|
|
27
27
|
};
|
package/dist/cli/types.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export interface InstallArgs {
|
|
|
4
4
|
tmux?: BooleanArg;
|
|
5
5
|
skills?: BooleanArg;
|
|
6
6
|
dryRun?: boolean;
|
|
7
|
+
reset?: boolean;
|
|
7
8
|
}
|
|
8
9
|
export interface OpenCodeConfig {
|
|
9
10
|
plugin?: string[];
|
|
@@ -16,6 +17,7 @@ export interface InstallConfig {
|
|
|
16
17
|
installSkills: boolean;
|
|
17
18
|
installCustomSkills: boolean;
|
|
18
19
|
dryRun?: boolean;
|
|
20
|
+
reset: boolean;
|
|
19
21
|
}
|
|
20
22
|
export interface ConfigMergeResult {
|
|
21
23
|
success: boolean;
|
package/dist/config/loader.d.ts
CHANGED
|
@@ -3,7 +3,8 @@ import { type PluginConfig } from './schema';
|
|
|
3
3
|
* Load plugin configuration from user and project config files, merging them appropriately.
|
|
4
4
|
*
|
|
5
5
|
* Configuration is loaded from two locations:
|
|
6
|
-
* 1. User config:
|
|
6
|
+
* 1. User config: $OPENCODE_CONFIG_DIR/oh-my-opencode-slim.jsonc or .json,
|
|
7
|
+
* or ~/.config/opencode/oh-my-opencode-slim.jsonc or .json (or $XDG_CONFIG_HOME)
|
|
7
8
|
* 2. Project config: <directory>/.opencode/oh-my-opencode-slim.jsonc or .json
|
|
8
9
|
*
|
|
9
10
|
* JSONC format is preferred over JSON (allows comments and trailing commas).
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -109,6 +109,7 @@ export type BackgroundTaskConfig = z.infer<typeof BackgroundTaskConfigSchema>;
|
|
|
109
109
|
export declare const FailoverConfigSchema: z.ZodObject<{
|
|
110
110
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
111
111
|
timeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
112
|
+
retryDelayMs: z.ZodDefault<z.ZodNumber>;
|
|
112
113
|
chains: z.ZodDefault<z.ZodObject<{
|
|
113
114
|
orchestrator: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
114
115
|
oracle: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -121,6 +122,7 @@ export declare const FailoverConfigSchema: z.ZodObject<{
|
|
|
121
122
|
export type FailoverConfig = z.infer<typeof FailoverConfigSchema>;
|
|
122
123
|
export declare const PluginConfigSchema: z.ZodObject<{
|
|
123
124
|
preset: z.ZodOptional<z.ZodString>;
|
|
125
|
+
setDefaultAgent: z.ZodOptional<z.ZodBoolean>;
|
|
124
126
|
scoringEngineVersion: z.ZodOptional<z.ZodEnum<{
|
|
125
127
|
v1: "v1";
|
|
126
128
|
"v2-shadow": "v2-shadow";
|
|
@@ -203,6 +205,7 @@ export declare const PluginConfigSchema: z.ZodObject<{
|
|
|
203
205
|
fallback: z.ZodOptional<z.ZodObject<{
|
|
204
206
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
205
207
|
timeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
208
|
+
retryDelayMs: z.ZodDefault<z.ZodNumber>;
|
|
206
209
|
chains: z.ZodDefault<z.ZodObject<{
|
|
207
210
|
orchestrator: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
208
211
|
oracle: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime model fallback for foreground (interactive) agent sessions.
|
|
3
|
+
*
|
|
4
|
+
* When OpenCode fires a session.error, message.updated, or session.status
|
|
5
|
+
* event containing a rate-limit signal, this manager:
|
|
6
|
+
* 1. Looks up the next untried model in the agent's configured chain
|
|
7
|
+
* 2. Aborts the rate-limited prompt via client.session.abort()
|
|
8
|
+
* 3. Re-queues the last user message via client.session.promptAsync()
|
|
9
|
+
* with the new model — promptAsync returns immediately so we never
|
|
10
|
+
* block the event handler waiting for a full LLM response.
|
|
11
|
+
*
|
|
12
|
+
* This mirrors the BackgroundTaskManager's fallback loop but operates
|
|
13
|
+
* reactively through the event system instead of wrapping prompt() in a
|
|
14
|
+
* try/catch, which is not possible for interactive (foreground) sessions.
|
|
15
|
+
*/
|
|
16
|
+
import type { PluginInput } from '@opencode-ai/plugin';
|
|
17
|
+
type OpencodeClient = PluginInput['client'];
|
|
18
|
+
export declare function isRateLimitError(error: unknown): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Manages runtime model fallback for foreground agent sessions.
|
|
21
|
+
*
|
|
22
|
+
* Constructed at plugin init with the ordered fallback chains for each agent
|
|
23
|
+
* (built from _modelArray entries merged with fallback.chains config).
|
|
24
|
+
*/
|
|
25
|
+
export declare class ForegroundFallbackManager {
|
|
26
|
+
private readonly client;
|
|
27
|
+
/**
|
|
28
|
+
* Ordered fallback chains per agent.
|
|
29
|
+
* e.g. { orchestrator: ['anthropic/claude-opus-4-5', 'openai/gpt-4o'] }
|
|
30
|
+
* The first model that hasn't been tried yet is selected on each fallback.
|
|
31
|
+
*/
|
|
32
|
+
private readonly chains;
|
|
33
|
+
private readonly enabled;
|
|
34
|
+
/** sessionID → last observed model string ("providerID/modelID") */
|
|
35
|
+
private readonly sessionModel;
|
|
36
|
+
/** sessionID → agent name (populated from message.updated info.agent field) */
|
|
37
|
+
private readonly sessionAgent;
|
|
38
|
+
/** sessionID → set of models already attempted this session */
|
|
39
|
+
private readonly sessionTried;
|
|
40
|
+
/** Sessions with an active fallback switch in flight */
|
|
41
|
+
private readonly inProgress;
|
|
42
|
+
/** sessionID → timestamp of last trigger (for deduplication) */
|
|
43
|
+
private readonly lastTrigger;
|
|
44
|
+
constructor(client: OpencodeClient,
|
|
45
|
+
/**
|
|
46
|
+
* Ordered fallback chains per agent.
|
|
47
|
+
* e.g. { orchestrator: ['anthropic/claude-opus-4-5', 'openai/gpt-4o'] }
|
|
48
|
+
* The first model that hasn't been tried yet is selected on each fallback.
|
|
49
|
+
*/
|
|
50
|
+
chains: Record<string, string[]>, enabled: boolean);
|
|
51
|
+
/**
|
|
52
|
+
* Process an OpenCode plugin event.
|
|
53
|
+
* Call this from the plugin's `event` hook for every event received.
|
|
54
|
+
*/
|
|
55
|
+
handleEvent(rawEvent: unknown): Promise<void>;
|
|
56
|
+
private tryFallback;
|
|
57
|
+
/**
|
|
58
|
+
* Determine the fallback chain to use for a session.
|
|
59
|
+
*
|
|
60
|
+
* Priority:
|
|
61
|
+
* 1. Agent name known AND has a configured chain → return it directly
|
|
62
|
+
* 2. Agent name known but NO chain configured → return [] (no fallback;
|
|
63
|
+
* do NOT bleed into other agents' chains which would re-prompt the
|
|
64
|
+
* session with a model belonging to a completely different agent)
|
|
65
|
+
* 3. Agent name unknown, current model known → search all chains for
|
|
66
|
+
* the model to infer which chain to use
|
|
67
|
+
* 4. Nothing matches → flatten all chains as a last resort (only
|
|
68
|
+
* reached when both agent name and current model are unavailable)
|
|
69
|
+
*/
|
|
70
|
+
private resolveChain;
|
|
71
|
+
}
|
|
72
|
+
export {};
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export type { AutoUpdateCheckerOptions } from './auto-update-checker';
|
|
|
2
2
|
export { createAutoUpdateCheckerHook } from './auto-update-checker';
|
|
3
3
|
export { createChatHeadersHook } from './chat-headers';
|
|
4
4
|
export { createDelegateTaskRetryHook } from './delegate-task-retry';
|
|
5
|
+
export { ForegroundFallbackManager, isRateLimitError } from './foreground-fallback';
|
|
5
6
|
export { createJsonErrorRecoveryHook } from './json-error-recovery';
|
|
6
7
|
export { createPhaseReminderHook } from './phase-reminder';
|
|
7
8
|
export { createPostReadNudgeHook } from './post-read-nudge';
|