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 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-codex`). No provider questions asked.
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-codex</code>
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-codex</code>
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-codex</code>
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-codex</code>
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-codex</code>
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-codex</code>
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>
@@ -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 ~/.config/opencode/skills/
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 ~/.config/opencode/skills/
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 getConfigDir() {
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 join(getConfigDir(), "opencode.json");
51
+ return getOpenCodeConfigPaths()[0];
34
52
  }
35
53
  function getConfigJsonc() {
36
- return join(getConfigDir(), "opencode.jsonc");
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: 5
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 && keyResult.issues.some((iss) => iss.code === "invalid_type" && iss.expected === "number");
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 { homedir as homedir2 } from "os";
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(homedir2(), ".config", "opencode", "skills");
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 = dirname(destPath);
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-codex", variant: "low" },
13823
- explorer: { model: "openai/gpt-5-codex", variant: "low" },
13824
- designer: { model: "openai/gpt-5-codex", variant: "medium" },
13825
- fixer: { model: "openai/gpt-5-codex", variant: "low" }
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: { model: "github-copilot/gemini-3.1-pro-preview", variant: "medium" },
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
- ensureConfigDir();
13977
+ ensureOpenCodeConfigDir();
13937
13978
  } catch (err) {
13938
13979
  return {
13939
13980
  success: false,
13940
- configPath: getConfigDir(),
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
- ensureConfigDir();
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
- printSuccess(`OpenCode ${version2 ?? ""} detected${path ? ` (${DIM}${path}${RESET})` : ""}`);
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
- lines.push(` ${DIM}\u25CB Kimi \u2014 see docs/provider-configurations.md${RESET}`);
14228
- lines.push(` ${DIM}\u25CB GitHub Copilot \u2014 see docs/provider-configurations.md${RESET}`);
14229
- lines.push(` ${DIM}\u25CB ZAI Coding Plan \u2014 see docs/provider-configurations.md${RESET}`);
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
- printStep(step++, totalSteps, "Adding oh-my-opencode-slim plugin...");
14253
- if (config2.dryRun) {
14254
- printInfo("Dry run mode - skipping plugin installation");
14255
- } else {
14256
- const pluginResult = await addPluginToOpenCodeConfig();
14257
- if (!handleStepResult(pluginResult, "Plugin added"))
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
- printStep(step++, totalSteps, "Disabling OpenCode default agents...");
14263
- if (config2.dryRun) {
14264
- printInfo("Dry run mode - skipping agent disabling");
14265
- } else {
14266
- const agentResult = disableDefaultAgents();
14267
- if (!handleStepResult(agentResult, "Default agents disabled"))
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 liteResult = writeLiteConfig(config2);
14280
- if (!handleStepResult(liteResult, "Config written"))
14281
- return 1;
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
- printSuccess(`${customSkillsInstalled}/${CUSTOM_SKILLS.length} custom skills processed`);
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
- console.log(`${SYMBOLS.star} ${BOLD}${GREEN}${isUpdate ? "Configuration updated!" : "Installation complete!"}${RESET}`);
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
- console.log(`${BOLD}Default configuration uses OpenAI models (gpt-5.4 / gpt-5-codex).${RESET}`);
14336
- console.log(`${BOLD}For alternative providers (Kimi, GitHub Copilot, ZAI Coding Plan), see:${RESET}`);
14337
- console.log(` ${BLUE}https://github.com/alvinunreal/oh-my-opencode-slim/blob/main/docs/provider-configurations.md${RESET}`);
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() {
@@ -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;
@@ -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-codex";
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-codex";
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-codex";
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-codex";
24
+ readonly model: "openai/gpt-5.4-mini";
25
25
  readonly variant: "low";
26
26
  };
27
27
  };
@@ -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;
@@ -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: ~/.config/opencode/oh-my-opencode-slim.jsonc or .json (or $XDG_CONFIG_HOME)
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).
@@ -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 {};
@@ -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';