oh-my-opencode-slim 1.0.6 → 1.0.7

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
@@ -44,7 +44,7 @@ the `plugin` array in both `opencode.json` and `tui.json`.
44
44
 
45
45
  ### Getting Started
46
46
 
47
- The installer generates both OpenAI and OpenCode Go presets, with OpenAI active by default. OpenAI uses `openai/gpt-5.5` for the higher-judgment agents and `openai/gpt-5.4-mini` for the faster scoped agents. To make OpenCode Go active during install, run `bunx oh-my-opencode-slim@latest install --preset=opencode-go`.
47
+ The installer generates both OpenAI and OpenCode Go presets, with OpenAI active by default. OpenAI uses `openai/gpt-5.5` for the higher-judgment agents and `openai/gpt-5.4-mini` for the faster scoped agents. To make OpenCode Go active during install, run `bunx oh-my-opencode-slim@latest install --preset=opencode-go` or change the default preset name in `~/.config/opencode/oh-my-opencode-slim.json` after installation.
48
48
 
49
49
  Then:
50
50
 
@@ -63,9 +63,9 @@ Then:
63
63
  4. **Update the models you want for each agent**
64
64
 
65
65
  > [!TIP]
66
- > Want to understand how automatic delegation works in practice? Review the **[Orchestrator prompt](https://github.com/alvinunreal/oh-my-opencode-slim/blob/master/src/agents/orchestrator.ts#L28)** — it contains the delegation rules, specialist routing logic, and the thresholds for when the main agent should hand work off to subagents.
66
+ > It's **recommended** to understand how automatic delegation works. The **[Orchestrator prompt](https://github.com/alvinunreal/oh-my-opencode-slim/blob/master/src/agents/orchestrator.ts#L28)** contains the delegation rules, specialist routing logic, and the thresholds for when the main agent should hand work off to subagents. You can alway delegate manually by calling a subagent via: `@agentName <task>`
67
67
 
68
- The default generated configuration includes both `openai` and `opencode-go` presets. Abbreviated:
68
+ The default generated configuration includes both `openai` and `opencode-go` presets.
69
69
 
70
70
  ```jsonc
71
71
  {
@@ -79,24 +79,29 @@ The default generated configuration includes both `openai` and `opencode-go` pre
79
79
  "explorer": { "model": "openai/gpt-5.4-mini", "variant": "low", "skills": [], "mcps": [] },
80
80
  "designer": { "model": "openai/gpt-5.4-mini", "variant": "medium", "skills": ["agent-browser"], "mcps": [] },
81
81
  "fixer": { "model": "openai/gpt-5.4-mini", "variant": "low", "skills": [], "mcps": [] }
82
+ },
83
+ "opencode-go": {
84
+ "orchestrator": { "model": "opencode-go/glm-5.1", "skills": [ "*" ], "mcps": [ "*", "!context7" ] },
85
+ "oracle": { "model": "opencode-go/deepseek-v4-pro", "variant": "max", "skills": ["simplify"], "mcps": [] },
86
+ "council": { "model": "opencode-go/deepseek-v4-pro", "variant": "high", "skills": [], "mcps": [] },
87
+ "librarian": { "model": "opencode-go/minimax-m2.7", "skills": [], "mcps": [ "websearch", "context7", "grep_app" ] },
88
+ "explorer": { "model": "opencode-go/minimax-m2.7", "skills": [], "mcps": [] },
89
+ "designer": { "model": "opencode-go/kimi-k2.6", "variant": "medium", "skills": [ "agent-browser" ], "mcps": [] },
90
+ "fixer": { "model": "opencode-go/deepseek-v4-flash", "variant": "high", "skills": [], "mcps": [] }
82
91
  }
83
92
  }
84
93
  }
85
94
  ```
86
95
 
87
- Session management is enabled by default even though it is not shown in the
88
- starter config. See **[Session Management](docs/session-management.md)** if you
89
- want to customize how many resumable child-agent sessions are remembered.
90
-
91
96
  ### For Alternative Providers
92
97
 
93
- To use OpenCode Go, Kimi, GitHub Copilot, ZAI Coding Plan, or a mixed-provider setup, use **[Configuration](docs/configuration.md)** for the full reference. If you want a ready-made starting point, check the **[Author's Preset](docs/authors-preset.md)** and **[$30 Preset](docs/thirty-dollars-preset.md)** - the `$30` preset is the best cheap setup.
98
+ To use custom providers or a mixed-provider setup, use **[Configuration](docs/configuration.md)** for the full reference. If you want a ready-made starting point, check the **[Author's Preset](docs/authors-preset.md)** and **[$30 Preset](docs/thirty-dollars-preset.md)** - the `$30` preset is the best cheap setup.
94
99
 
95
100
  The configuration guide also covers custom subagents via `agents.<name>`, where
96
101
  you can define both a normal `prompt` and an `orchestratorPrompt` block for
97
102
  delegation.
98
103
 
99
- You can also mix and match any models per agent. For model suggestions, see the **Recommended Models** listed under each agent below.
104
+ For model suggestions, see the **Recommended Models** listed under each agent below.
100
105
 
101
106
  ### ✅ Verify Your Setup
102
107
 
@@ -425,7 +430,7 @@ If any agent fails to respond, check your provider authentication and config fil
425
430
  ### Observer: The Silent Witness
426
431
 
427
432
  > [!NOTE]
428
- > **Why a separate agent?** If your Orchestrator model is not multimodal, enable Observer to handle images, screenshots, PDFs, and other visual files. Observer is disabled by default and gives the Orchestrator a dedicated multimodal reader without forcing you to change your main reasoning model. Set `disabled_agents: []` and an `observer` model in your configuration.
433
+ > **Why a separate agent?** If your Orchestrator model is not multimodal, enable Observer to handle images, screenshots, PDFs, and other visual files. Observer is disabled by default and gives the Orchestrator a dedicated multimodal reader without forcing you to change your main reasoning model. Set `disabled_agents: []` and an `observer` model in your configuration. The bundled `opencode-go` install preset does this automatically because its GLM Orchestrator is not multimodal.
429
434
 
430
435
  <table>
431
436
  <tr>
@@ -439,7 +444,7 @@ If any agent fails to respond, check your provider authentication and config fil
439
444
 
440
445
  - Images, screenshots, diagrams → `read` tool (native image support)
441
446
  - PDFs and binary documents → `read` tool (text + structure extraction)
442
- - **Disabled by default** — enable with `"disabled_agents": []` and configure a vision-capable model
447
+ - **Disabled by default** — enable with `"disabled_agents": []` and configure a vision-capable model; installing with `--preset=opencode-go` enables it with `opencode-go/kimi-k2.6`
443
448
 
444
449
  </td>
445
450
  </tr>
@@ -479,12 +484,13 @@ Use this section as a map: start with installation, then jump to features, confi
479
484
  | Doc | What it covers |
480
485
  |-----|----------------|
481
486
  | **[Council](docs/council.md)** | Run multiple models in parallel and synthesize a single answer with `@council` |
482
- | **[Interview](docs/interview.md)** | Turn rough ideas into a structured markdown spec through a browser-based Q&A flow |
483
487
  | **[Multiplexer Integration](docs/multiplexer-integration.md)** | Watch agents work live in Tmux or Zellij panes |
484
488
  | **[Session Management](docs/session-management.md)** | Reuse recent child-agent sessions with short aliases instead of starting over |
485
489
  | **[Todo Continuation](docs/todo-continuation.md)** | Auto-continue orchestrator sessions with cooldowns and safety checks |
486
490
  | **[Preset Switching](docs/preset-switching.md)** | Switch agent model presets at runtime with `/preset` |
487
491
  | **[Codemap](docs/codemap.md)** | Generate hierarchical codemaps to understand large codebases faster |
492
+ | **[Interview](docs/interview.md)** | Turn rough ideas into a structured markdown spec through a browser-based Q&A flow |
493
+ | **[Divoom Display](docs/divoom.md)** | Mirror orchestrator and specialist-agent activity to a Divoom MiniToo Bluetooth display |
488
494
 
489
495
  ### ⚙️ Config & Reference
490
496
 
@@ -496,12 +502,13 @@ Use this section as a map: start with installation, then jump to features, confi
496
502
  | **[MCPs](docs/mcps.md)** | `websearch`, `context7`, `grep_app`, and how MCP permissions work per agent |
497
503
  | **[Tools](docs/tools.md)** | Built-in tool capabilities like `webfetch`, LSP tools, code search, and formatters |
498
504
 
499
- ### 💡 Example Presets
505
+ ### 💡 Presets
500
506
 
501
507
  | Doc | What it covers |
502
508
  |-----|----------------|
503
509
  | **[Author's Preset](docs/authors-preset.md)** | The author's daily mixed-provider setup |
504
510
  | **[$30 Preset](docs/thirty-dollars-preset.md)** | A budget mixed-provider setup for around $30/month |
511
+ | **[OpenCode Go Preset](docs/opencode-go-preset.md)** | The bundled `opencode-go` preset generated by the installer |
505
512
 
506
513
  ---
507
514
 
@@ -512,7 +519,7 @@ Use this section as a map: start with installation, then jump to features, confi
512
519
  <p><sub>Every merged contribution leaves a mark on the realm.</sub></p>
513
520
 
514
521
  <!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
515
- [![All Contributors](https://img.shields.io/badge/all_contributors-44-orange.svg?style=flat-square)](#contributors-)
522
+ [![All Contributors](https://img.shields.io/badge/all_contributors-45-orange.svg?style=flat-square)](#contributors-)
516
523
  <!-- ALL-CONTRIBUTORS-BADGE:END -->
517
524
  </div>
518
525
 
@@ -582,6 +589,7 @@ Use this section as a map: start with installation, then jump to features, confi
582
589
  <tr>
583
590
  <td align="center" valign="top" width="16.66%"><a href="https://github.com/gustavocaiano"><img src="https://avatars.githubusercontent.com/u/104129313?v=4?s=100" width="100px;" alt="Gustavo Caiano"/><br /><sub><b>Gustavo Caiano</b></sub></a><br /><a href="https://github.com/alvinunreal/oh-my-opencode-slim/commits?author=gustavocaiano" title="Code">💻</a></td>
584
591
  <td align="center" valign="top" width="16.66%"><a href="https://github.com/ThomasMldr"><img src="https://avatars.githubusercontent.com/u/6631765?v=4?s=100" width="100px;" alt="Thomas Mulder"/><br /><sub><b>Thomas Mulder</b></sub></a><br /><a href="https://github.com/alvinunreal/oh-my-opencode-slim/commits?author=ThomasMldr" title="Code">💻</a></td>
592
+ <td align="center" valign="top" width="16.66%"><a href="https://github.com/maou-shonen"><img src="https://avatars.githubusercontent.com/u/22576780?v=4?s=100" width="100px;" alt="魔王少年(maou shonen)"/><br /><sub><b>魔王少年(maou shonen)</b></sub></a><br /><a href="https://github.com/alvinunreal/oh-my-opencode-slim/commits?author=maou-shonen" title="Code">💻</a></td>
585
593
  </tr>
586
594
  </tbody>
587
595
  </table>
@@ -0,0 +1,23 @@
1
+ import type { DivoomStatus } from '../integrations/divoom/types';
2
+ export type DivoomCliResult = {
3
+ exitCode: number;
4
+ message?: string;
5
+ error?: string;
6
+ };
7
+ export type DivoomCliOptions = {
8
+ cwd?: string;
9
+ dryRun?: boolean;
10
+ detector?: () => Promise<string | null>;
11
+ transportFactory?: (config: {
12
+ status: DivoomStatus;
13
+ directory: string;
14
+ }) => {
15
+ sendStatus(update: {
16
+ status: DivoomStatus;
17
+ reason: string;
18
+ timestamp: number;
19
+ }): Promise<void>;
20
+ };
21
+ };
22
+ export declare function runDivoomCli(args: string[], options?: DivoomCliOptions): Promise<DivoomCliResult>;
23
+ export declare function detectDivoomAddress(): Promise<string | null>;
@@ -0,0 +1,38 @@
1
+ import { z } from 'zod';
2
+ import { type PluginConfig } from '../config/schema';
3
+ export type DoctorArgs = {
4
+ json?: boolean;
5
+ error?: string;
6
+ help?: boolean;
7
+ };
8
+ export declare function parseDoctorArgs(args: string[]): DoctorArgs;
9
+ export type ConfigCheckResult = {
10
+ scope: 'user' | 'project';
11
+ path: string | null;
12
+ exists: boolean;
13
+ ok: boolean;
14
+ config?: PluginConfig;
15
+ error?: {
16
+ kind: 'invalid-json' | 'invalid-schema' | 'read-error';
17
+ message: string;
18
+ issues?: z.ZodIssue[];
19
+ };
20
+ };
21
+ export type PresetCheckResult = {
22
+ preset: string;
23
+ ok: boolean;
24
+ error?: {
25
+ kind: 'missing-preset';
26
+ message: string;
27
+ };
28
+ };
29
+ export type DoctorResult = {
30
+ ok: boolean;
31
+ project: string;
32
+ configs: ConfigCheckResult[];
33
+ presetCheck?: PresetCheckResult;
34
+ };
35
+ export declare function runDoctorCheck(cwd: string): DoctorResult;
36
+ export declare function formatHumanDoctorResult(result: DoctorResult): string;
37
+ export declare function formatJsonDoctorResult(result: DoctorResult): string;
38
+ export declare function doctor(args: DoctorArgs): Promise<number>;
package/dist/cli/index.js CHANGED
@@ -3,9 +3,13 @@
3
3
  import { createRequire } from "node:module";
4
4
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
5
5
 
6
- // src/cli/install.ts
7
- import { existsSync as existsSync4 } from "node:fs";
8
- import { createInterface } from "node:readline/promises";
6
+ // src/cli/doctor.ts
7
+ import * as fs2 from "node:fs";
8
+ import { z as z3 } from "zod";
9
+
10
+ // src/config/loader.ts
11
+ import * as fs from "node:fs";
12
+ import * as path from "node:path";
9
13
 
10
14
  // src/cli/config-io.ts
11
15
  import {
@@ -41,6 +45,12 @@ function getConfigDir() {
41
45
  }
42
46
  return getDefaultOpenCodeConfigDir();
43
47
  }
48
+ function getConfigSearchDirs() {
49
+ const dirs = [getCustomOpenCodeConfigDir(), getDefaultOpenCodeConfigDir()];
50
+ return dirs.filter((dir, index) => {
51
+ return Boolean(dir) && dirs.indexOf(dir) === index;
52
+ });
53
+ }
44
54
  function getOpenCodeConfigPaths() {
45
55
  const configDir = getDefaultOpenCodeConfigDir();
46
56
  return [join(configDir, "opencode.json"), join(configDir, "opencode.jsonc")];
@@ -298,6 +308,17 @@ var SessionManagerConfigSchema = z2.object({
298
308
  readContextMinLines: z2.number().int().min(0).max(1000).default(10),
299
309
  readContextMaxFiles: z2.number().int().min(0).max(50).default(8)
300
310
  });
311
+ var DivoomConfigSchema = z2.object({
312
+ enabled: z2.boolean().default(false),
313
+ python: z2.string().min(1).default("/Applications/Divoom MiniToo.app/Contents/Resources/.venv/bin/python"),
314
+ script: z2.string().min(1).default("/Applications/Divoom MiniToo.app/Contents/Resources/tools/divoom_send.py"),
315
+ size: z2.number().int().min(1).max(1024).default(128),
316
+ fps: z2.number().int().min(1).max(60).default(8),
317
+ speed: z2.number().int().min(1).max(1e4).default(125),
318
+ maxFrames: z2.number().int().min(1).max(500).default(24),
319
+ posterizeBits: z2.number().int().min(1).max(8).default(3),
320
+ gifs: z2.record(z2.string(), z2.string().min(1)).optional()
321
+ });
301
322
  var TodoContinuationConfigSchema = z2.object({
302
323
  maxContinuations: z2.number().int().min(1).max(50).default(5).describe("Maximum consecutive auto-continuations before stopping to ask user"),
303
324
  cooldownMs: z2.number().int().min(0).max(30000).default(3000).describe("Delay in ms before auto-continuing (gives user time to abort)"),
@@ -349,6 +370,7 @@ var PluginConfigSchema = z2.object({
349
370
  websearch: WebsearchConfigSchema.optional(),
350
371
  interview: InterviewConfigSchema.optional(),
351
372
  sessionManager: SessionManagerConfigSchema.optional(),
373
+ divoom: DivoomConfigSchema.optional(),
352
374
  todoContinuation: TodoContinuationConfigSchema.optional(),
353
375
  fallback: FailoverConfigSchema.optional(),
354
376
  council: CouncilConfigSchema.optional()
@@ -535,7 +557,8 @@ var MODEL_MAPPINGS = {
535
557
  librarian: { model: "opencode-go/minimax-m2.7" },
536
558
  explorer: { model: "opencode-go/minimax-m2.7" },
537
559
  designer: { model: "opencode-go/kimi-k2.6", variant: "medium" },
538
- fixer: { model: "opencode-go/deepseek-v4-flash", variant: "high" }
560
+ fixer: { model: "opencode-go/deepseek-v4-flash", variant: "high" },
561
+ observer: { model: "opencode-go/kimi-k2.6" }
539
562
  }
540
563
  };
541
564
  function isGeneratedPresetName(value) {
@@ -554,6 +577,9 @@ function generateLiteConfig(installConfig) {
554
577
  preset,
555
578
  presets: {}
556
579
  };
580
+ if (preset === "opencode-go") {
581
+ config.disabled_agents = [];
582
+ }
557
583
  const createAgentConfig = (agentName, modelInfo) => {
558
584
  const isOrchestrator = agentName === "orchestrator";
559
585
  const skills = isOrchestrator ? ["*"] : [
@@ -919,9 +945,266 @@ function detectCurrentConfig() {
919
945
  }
920
946
  return result;
921
947
  }
948
+
949
+ // src/config/loader.ts
950
+ function findConfigPath(basePath) {
951
+ const jsoncPath = `${basePath}.jsonc`;
952
+ const jsonPath = `${basePath}.json`;
953
+ if (fs.existsSync(jsoncPath)) {
954
+ return jsoncPath;
955
+ }
956
+ if (fs.existsSync(jsonPath)) {
957
+ return jsonPath;
958
+ }
959
+ return null;
960
+ }
961
+ function findConfigPathInDirs(configDirs, baseName) {
962
+ for (const configDir of configDirs) {
963
+ const configPath = findConfigPath(path.join(configDir, baseName));
964
+ if (configPath) {
965
+ return configPath;
966
+ }
967
+ }
968
+ return null;
969
+ }
970
+ function findPluginConfigPaths(directory) {
971
+ const userConfigPath = findConfigPathInDirs(getConfigSearchDirs(), "oh-my-opencode-slim");
972
+ const projectConfigBasePath = path.join(directory, ".opencode", "oh-my-opencode-slim");
973
+ const projectConfigPath = findConfigPath(projectConfigBasePath);
974
+ return { userConfigPath, projectConfigPath };
975
+ }
976
+ function mergePluginConfigs(base, override) {
977
+ return {
978
+ ...base,
979
+ ...override,
980
+ agents: deepMerge(base.agents, override.agents),
981
+ tmux: deepMerge(base.tmux, override.tmux),
982
+ multiplexer: deepMerge(base.multiplexer, override.multiplexer),
983
+ interview: deepMerge(base.interview, override.interview),
984
+ sessionManager: deepMerge(base.sessionManager, override.sessionManager),
985
+ divoom: deepMerge(base.divoom, override.divoom),
986
+ fallback: deepMerge(base.fallback, override.fallback),
987
+ council: deepMerge(base.council, override.council)
988
+ };
989
+ }
990
+ function deepMerge(base, override) {
991
+ if (!base)
992
+ return override;
993
+ if (!override)
994
+ return base;
995
+ const result = { ...base };
996
+ for (const key of Object.keys(override)) {
997
+ const baseVal = base[key];
998
+ const overrideVal = override[key];
999
+ if (typeof baseVal === "object" && baseVal !== null && typeof overrideVal === "object" && overrideVal !== null && !Array.isArray(baseVal) && !Array.isArray(overrideVal)) {
1000
+ result[key] = deepMerge(baseVal, overrideVal);
1001
+ } else {
1002
+ result[key] = overrideVal;
1003
+ }
1004
+ }
1005
+ return result;
1006
+ }
1007
+
1008
+ // src/cli/doctor.ts
1009
+ function parseDoctorArgs(args) {
1010
+ const result = {};
1011
+ for (const arg of args) {
1012
+ if (arg === "--json") {
1013
+ result.json = true;
1014
+ } else if (arg === "--help" || arg === "-h") {
1015
+ result.help = true;
1016
+ } else {
1017
+ result.error ??= `Unknown doctor option: ${arg}`;
1018
+ }
1019
+ }
1020
+ return result;
1021
+ }
1022
+ function checkConfigFile(scope, configPath) {
1023
+ if (configPath === null) {
1024
+ return { scope, path: null, exists: false, ok: true };
1025
+ }
1026
+ try {
1027
+ const stat = fs2.statSync(configPath);
1028
+ if (stat.size === 0) {
1029
+ return {
1030
+ scope,
1031
+ path: configPath,
1032
+ exists: true,
1033
+ ok: false,
1034
+ error: {
1035
+ kind: "invalid-json",
1036
+ message: "Empty file is not valid JSON"
1037
+ }
1038
+ };
1039
+ }
1040
+ const content = fs2.readFileSync(configPath, "utf-8");
1041
+ const rawConfig = JSON.parse(stripJsonComments(content));
1042
+ const parseResult = PluginConfigSchema.safeParse(rawConfig);
1043
+ if (!parseResult.success) {
1044
+ return {
1045
+ scope,
1046
+ path: configPath,
1047
+ exists: true,
1048
+ ok: false,
1049
+ error: {
1050
+ kind: "invalid-schema",
1051
+ message: z3.prettifyError(parseResult.error),
1052
+ issues: parseResult.error.issues
1053
+ }
1054
+ };
1055
+ }
1056
+ return {
1057
+ scope,
1058
+ path: configPath,
1059
+ exists: true,
1060
+ ok: true,
1061
+ config: parseResult.data
1062
+ };
1063
+ } catch (err) {
1064
+ if (err instanceof SyntaxError) {
1065
+ return {
1066
+ scope,
1067
+ path: configPath,
1068
+ exists: true,
1069
+ ok: false,
1070
+ error: {
1071
+ kind: "invalid-json",
1072
+ message: err.message
1073
+ }
1074
+ };
1075
+ } else if (err instanceof Error && "code" in err && err.code === "ENOENT") {
1076
+ return {
1077
+ scope,
1078
+ path: configPath,
1079
+ exists: false,
1080
+ ok: false,
1081
+ error: {
1082
+ kind: "read-error",
1083
+ message: "File was not found while reading"
1084
+ }
1085
+ };
1086
+ }
1087
+ return {
1088
+ scope,
1089
+ path: configPath,
1090
+ exists: true,
1091
+ ok: false,
1092
+ error: {
1093
+ kind: "read-error",
1094
+ message: err instanceof Error ? err.message : String(err)
1095
+ }
1096
+ };
1097
+ }
1098
+ }
1099
+ function checkPreset(mergedConfig) {
1100
+ const envPreset = process.env.OH_MY_OPENCODE_SLIM_PRESET;
1101
+ const presetName = envPreset || mergedConfig.preset;
1102
+ if (presetName === undefined) {
1103
+ return;
1104
+ }
1105
+ if (!mergedConfig.presets?.[presetName]) {
1106
+ return {
1107
+ preset: presetName,
1108
+ ok: false,
1109
+ error: {
1110
+ kind: "missing-preset",
1111
+ message: `Preset "${presetName}" not found in config`
1112
+ }
1113
+ };
1114
+ }
1115
+ return { preset: presetName, ok: true };
1116
+ }
1117
+ function getMergedConfig(userConfig, projectConfig) {
1118
+ return projectConfig ? mergePluginConfigs(userConfig ?? {}, projectConfig) : userConfig ?? {};
1119
+ }
1120
+ function runDoctorCheck(cwd) {
1121
+ const { userConfigPath, projectConfigPath } = findPluginConfigPaths(cwd);
1122
+ const userCheck = checkConfigFile("user", userConfigPath);
1123
+ const projectCheck = checkConfigFile("project", projectConfigPath);
1124
+ const configs = [userCheck, projectCheck];
1125
+ const hasInvalidConfig = configs.some((c) => !c.ok);
1126
+ let presetCheckResult;
1127
+ if (!hasInvalidConfig) {
1128
+ const mergedConfig = getMergedConfig(userCheck.config, projectCheck.config);
1129
+ presetCheckResult = checkPreset(mergedConfig);
1130
+ }
1131
+ return {
1132
+ ok: configs.every((c) => c.ok) && (!presetCheckResult || presetCheckResult.ok),
1133
+ project: cwd,
1134
+ configs,
1135
+ presetCheck: presetCheckResult
1136
+ };
1137
+ }
1138
+ function formatHumanDoctorResult(result) {
1139
+ const lines = [];
1140
+ lines.push(`Project: ${result.project}`);
1141
+ lines.push("");
1142
+ for (const config of result.configs) {
1143
+ if (config.path === null) {
1144
+ lines.push(`[${config.scope}] No config file found`);
1145
+ } else {
1146
+ const status = config.ok ? "✓" : "✗";
1147
+ lines.push(`[${config.scope}] ${config.path} ${status}`);
1148
+ if (!config.ok && config.error) {
1149
+ if (config.error.kind === "invalid-json") {
1150
+ lines.push(` Invalid JSON: ${config.error.message}`);
1151
+ } else if (config.error.kind === "invalid-schema") {
1152
+ lines.push(" Schema error:");
1153
+ for (const line of config.error.message.split(`
1154
+ `)) {
1155
+ lines.push(` ${line}`);
1156
+ }
1157
+ } else if (config.error.kind === "read-error") {
1158
+ lines.push(` Read error: ${config.error.message}`);
1159
+ }
1160
+ }
1161
+ }
1162
+ }
1163
+ if (result.presetCheck) {
1164
+ lines.push("");
1165
+ const status = result.presetCheck.ok ? "✓" : "✗";
1166
+ lines.push(`[preset] ${result.presetCheck.preset} ${status}`);
1167
+ if (result.presetCheck.error) {
1168
+ lines.push(` ${result.presetCheck.error.message}`);
1169
+ }
1170
+ }
1171
+ return lines.join(`
1172
+ `);
1173
+ }
1174
+ function formatJsonDoctorResult(result) {
1175
+ return JSON.stringify({
1176
+ ...result,
1177
+ configs: result.configs.map(({ config: _config, ...config }) => config)
1178
+ }, null, 2);
1179
+ }
1180
+ async function doctor(args) {
1181
+ if (args.help) {
1182
+ console.log(`Usage: oh-my-opencode-slim doctor [OPTIONS]
1183
+
1184
+ Options:
1185
+ --json Print diagnostics as JSON
1186
+ -h, --help Show this help message`);
1187
+ return 0;
1188
+ }
1189
+ if (args.error) {
1190
+ console.error(args.error);
1191
+ return 1;
1192
+ }
1193
+ const result = runDoctorCheck(process.cwd());
1194
+ if (args.json) {
1195
+ console.log(formatJsonDoctorResult(result));
1196
+ } else {
1197
+ console.log(formatHumanDoctorResult(result));
1198
+ }
1199
+ return result.ok ? 0 : 1;
1200
+ }
1201
+
1202
+ // src/cli/install.ts
1203
+ import { existsSync as existsSync5 } from "node:fs";
1204
+ import { createInterface } from "node:readline/promises";
922
1205
  // src/cli/system.ts
923
1206
  import { spawnSync as spawnSync2 } from "node:child_process";
924
- import { statSync as statSync3 } from "node:fs";
1207
+ import { statSync as statSync4 } from "node:fs";
925
1208
 
926
1209
  // src/utils/compat.ts
927
1210
  import { spawn as nodeSpawn } from "node:child_process";
@@ -1042,7 +1325,7 @@ function resolveOpenCodePath() {
1042
1325
  if (opencodePath === "opencode")
1043
1326
  continue;
1044
1327
  try {
1045
- const stat = statSync3(opencodePath);
1328
+ const stat = statSync4(opencodePath);
1046
1329
  if (stat.isFile()) {
1047
1330
  cachedOpenCodePath = opencodePath;
1048
1331
  return opencodePath;
@@ -1091,8 +1374,8 @@ async function getOpenCodeVersion() {
1091
1374
  return null;
1092
1375
  }
1093
1376
  function getOpenCodePath() {
1094
- const path = resolveOpenCodePath();
1095
- return path === "opencode" ? null : path;
1377
+ const path2 = resolveOpenCodePath();
1378
+ return path2 === "opencode" ? null : path2;
1096
1379
  }
1097
1380
  // src/cli/install.ts
1098
1381
  var GREEN = "\x1B[32m";
@@ -1172,11 +1455,11 @@ async function checkOpenCodeInstalled() {
1172
1455
  return { ok: false };
1173
1456
  }
1174
1457
  const version = await getOpenCodeVersion();
1175
- const path = getOpenCodePath();
1458
+ const path2 = getOpenCodePath();
1176
1459
  const detectedVersion = version ?? "";
1177
- const pathInfo = path ? ` (${DIM}${path}${RESET})` : "";
1460
+ const pathInfo = path2 ? ` (${DIM}${path2}${RESET})` : "";
1178
1461
  printSuccess(`OpenCode ${detectedVersion} detected${pathInfo}`);
1179
- return { ok: true, version: version ?? undefined, path: path ?? undefined };
1462
+ return { ok: true, version: version ?? undefined, path: path2 ?? undefined };
1180
1463
  }
1181
1464
  function handleStepResult(result, successMsg) {
1182
1465
  if (!result.success) {
@@ -1248,7 +1531,7 @@ ${JSON.stringify(liteConfig, null, 2)}
1248
1531
  `);
1249
1532
  } else {
1250
1533
  const configPath2 = getExistingLiteConfigPath();
1251
- const configExists = existsSync4(configPath2);
1534
+ const configExists = existsSync5(configPath2);
1252
1535
  if (configExists && !config.reset) {
1253
1536
  printInfo(`Configuration already exists at ${configPath2}. Use --reset to overwrite.`);
1254
1537
  } else {
@@ -1386,7 +1669,9 @@ function printHelp() {
1386
1669
  console.log(`
1387
1670
  oh-my-opencode-slim installer
1388
1671
 
1389
- Usage: bunx oh-my-opencode-slim install [OPTIONS]
1672
+ Usage:
1673
+ bunx oh-my-opencode-slim install [OPTIONS]
1674
+ bunx oh-my-opencode-slim doctor [OPTIONS]
1390
1675
 
1391
1676
  Options:
1392
1677
  --skills=yes|no Install recommended and bundled skills (default: yes)
@@ -1396,6 +1681,9 @@ Options:
1396
1681
  --reset Force overwrite of existing configuration
1397
1682
  -h, --help Show this help message
1398
1683
 
1684
+ Doctor options:
1685
+ --json Print diagnostics as JSON
1686
+
1399
1687
  Available presets: ${getGeneratedPresetNames2().join(", ")}
1400
1688
 
1401
1689
  The installer generates OpenAI and OpenCode Go presets by default.
@@ -1407,6 +1695,7 @@ Examples:
1407
1695
  bunx oh-my-opencode-slim install --no-tui --skills=yes
1408
1696
  bunx oh-my-opencode-slim install --preset=opencode-go
1409
1697
  bunx oh-my-opencode-slim install --reset
1698
+ bunx oh-my-opencode-slim doctor
1410
1699
  `);
1411
1700
  }
1412
1701
  async function main() {
@@ -1416,6 +1705,10 @@ async function main() {
1416
1705
  const installArgs = parseArgs(args.slice(hasSubcommand ? 1 : 0));
1417
1706
  const exitCode = await install(installArgs);
1418
1707
  process.exit(exitCode);
1708
+ } else if (args[0] === "doctor") {
1709
+ const doctorArgs = parseDoctorArgs(args.slice(1));
1710
+ const exitCode = await doctor(doctorArgs);
1711
+ process.exit(exitCode);
1419
1712
  } else if (args[0] === "-h" || args[0] === "--help") {
1420
1713
  printHelp();
1421
1714
  process.exit(0);
@@ -127,6 +127,9 @@ export declare const MODEL_MAPPINGS: {
127
127
  readonly model: "opencode-go/deepseek-v4-flash";
128
128
  readonly variant: "high";
129
129
  };
130
+ readonly observer: {
131
+ readonly model: "opencode-go/kimi-k2.6";
132
+ };
130
133
  };
131
134
  };
132
135
  export type PresetName = keyof typeof MODEL_MAPPINGS;
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import { z } from 'zod';
2
2
  /**
3
3
  * Configuration for a single councillor within a preset.
4
4
  * Each councillor is an independent LLM that processes the same prompt.
@@ -120,7 +120,7 @@ export interface CouncilResult {
120
120
  councillorResults: Array<{
121
121
  name: string;
122
122
  model: string;
123
- status: "completed" | "failed" | "timed_out";
123
+ status: 'completed' | 'failed' | 'timed_out';
124
124
  result?: string;
125
125
  error?: string;
126
126
  }>;
@@ -1,5 +1,5 @@
1
1
  export * from './constants';
2
2
  export * from './council-schema';
3
- export { deepMerge, loadAgentPrompt, loadPluginConfig } from './loader';
3
+ export { deepMerge, loadAgentPrompt, loadPluginConfig, } from './loader';
4
4
  export * from './schema';
5
5
  export { getAgentOverride, getCustomAgentNames } from './utils';