diff --git a/packages/app/src/components/dialog-settings.tsx b/packages/app/src/components/dialog-settings.tsx index f8892ebbdc8d..cdbb0eb6fbce 100644 --- a/packages/app/src/components/dialog-settings.tsx +++ b/packages/app/src/components/dialog-settings.tsx @@ -8,6 +8,7 @@ import { SettingsGeneral } from "./settings-general" import { SettingsKeybinds } from "./settings-keybinds" import { SettingsProviders } from "./settings-providers" import { SettingsModels } from "./settings-models" +import { SettingsOhMyOpenCode } from "./settings-ohmyopencode" export const DialogSettings: Component = () => { const language = useLanguage() @@ -45,6 +46,10 @@ export const DialogSettings: Component = () => { {language.t("settings.models.title")} + + + {language.t("settings.ohmyopencode.title")} + @@ -67,6 +72,9 @@ export const DialogSettings: Component = () => { + + + {/* */} {/* */} {/* */} diff --git a/packages/app/src/components/settings-ohmyopencode.tsx b/packages/app/src/components/settings-ohmyopencode.tsx new file mode 100644 index 000000000000..facf12317c01 --- /dev/null +++ b/packages/app/src/components/settings-ohmyopencode.tsx @@ -0,0 +1,169 @@ +import { Component, createMemo, type JSX } from "solid-js" +import { Select } from "@opencode-ai/ui/select" +import { Switch } from "@opencode-ai/ui/switch" +import { useLanguage } from "@/context/language" +import { useGlobalSync } from "@/context/global-sync" + +export const SettingsOhMyOpenCode: Component = () => { + const language = useLanguage() + const globalSync = useGlobalSync() + + const config = createMemo(() => globalSync.data.config.oh_my_opencode ?? {}) + + const update = (patch: Record) => { + void globalSync.updateConfig({ + oh_my_opencode: { ...config(), ...patch }, + }) + } + + const tmuxLayoutOptions = [ + { value: "main-vertical", label: "Main Vertical" }, + { value: "main-horizontal", label: "Main Horizontal" }, + { value: "tiled", label: "Tiled" }, + { value: "even-horizontal", label: "Even Horizontal" }, + { value: "even-vertical", label: "Even Vertical" }, + ] as const + const tmuxLayoutList = [...tmuxLayoutOptions] + + const browserOptions = [ + { value: "playwright", label: "Playwright" }, + { value: "agent-browser", label: "Agent Browser" }, + ] as const + const browserList = [...browserOptions] + + return ( +
+
+
+

{language.t("settings.ohmyopencode.title")}

+

{language.t("settings.ohmyopencode.description")}

+
+
+ +
+ {/* Features Section */} +
+

{language.t("settings.ohmyopencode.section.features")}

+ +
+ + {}} /> + + + + + update({ + ralph_loop: { ...config().ralph_loop, enabled: checked }, + }) + } + /> + + + + update({ auto_update: checked })} + /> + + + + o.value === (config().tmux?.layout ?? "main-vertical"), + )} + value={(o) => o.value} + label={(o) => o.label} + onSelect={(option) => + option && + update({ + tmux: { ...config().tmux, layout: option.value }, + }) + } + variant="secondary" + size="small" + triggerVariant="settings" + /> + +
+
+
+
+ ) +} + +interface SettingsRowProps { + title: string | JSX.Element + description: string | JSX.Element + children: JSX.Element +} + +const SettingsRow: Component = (props) => { + return ( +
+
+ {props.title} + {props.description} +
+
{props.children}
+
+ ) +} diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index d650060c4c91..1a2941cc82ee 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -717,6 +717,27 @@ export const dict = { "settings.providers.tag.other": "Other", "settings.models.title": "Models", "settings.models.description": "Model settings will be configurable here.", + "settings.ohmyopencode.title": "Oh My OpenCode", + "settings.ohmyopencode.description": "Configure Oh My OpenCode plugin settings", + "settings.ohmyopencode.section.agents": "Agent overrides", + "settings.ohmyopencode.section.features": "Features", + "settings.ohmyopencode.section.tmux": "Tmux integration", + "settings.ohmyopencode.row.enabled.title": "Enabled", + "settings.ohmyopencode.row.enabled.description": "Oh My OpenCode is integrated as a built-in plugin", + "settings.ohmyopencode.row.tmux.title": "Enable tmux", + "settings.ohmyopencode.row.tmux.description": "Enable tmux integration for visual multi-agent execution", + "settings.ohmyopencode.row.tmuxLayout.title": "Tmux layout", + "settings.ohmyopencode.row.tmuxLayout.description": "Layout for tmux agent panes", + "settings.ohmyopencode.row.ralphLoop.title": "Ralph Loop", + "settings.ohmyopencode.row.ralphLoop.description": "Enable the Ralph Loop for continuous agent execution", + "settings.ohmyopencode.row.autoUpdate.title": "Auto update", + "settings.ohmyopencode.row.autoUpdate.description": "Enable auto-update checking for Oh My OpenCode", + "settings.ohmyopencode.row.browserEngine.title": "Browser engine", + "settings.ohmyopencode.row.browserEngine.description": "Browser automation provider to use", + "settings.ohmyopencode.row.disabledAgents.title": "Disabled agents", + "settings.ohmyopencode.row.disabledAgents.description": "Agents to disable (e.g., oracle, multimodal-looker)", + "settings.ohmyopencode.row.disabledHooks.title": "Disabled hooks", + "settings.ohmyopencode.row.disabledHooks.description": "Hooks to disable (e.g., comment-checker, auto-update-checker)", "settings.agents.title": "Agents", "settings.agents.description": "Agent settings will be configurable here.", "settings.commands.title": "Commands", diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 1e7bf0baaf2a..3447f8e197b5 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -237,6 +237,25 @@ export namespace Config { result.plugin = deduplicatePlugins(result.plugin ?? []) + // Sync oh_my_opencode config to oh-my-opencode.json for the plugin to read. + // Uses deep merge so existing oh-my-opencode.json settings are preserved + // while coli.json settings take precedence. + if (result.oh_my_opencode) { + const omoConfigPath = path.join(Global.Path.config, "oh-my-opencode.json") + const existing = await Bun.file(omoConfigPath) + .json() + .catch((err: unknown) => { + if (err && typeof err === "object" && "code" in err && err.code !== "ENOENT") { + log.warn("failed to read oh-my-opencode.json", { error: err }) + } + return {} + }) + const merged = mergeDeep(existing, result.oh_my_opencode) + await Bun.write(omoConfigPath, JSON.stringify(merged, null, 2)).catch((err) => { + log.warn("failed to sync oh-my-opencode config", { error: err }) + }) + } + return { config: result, directories, @@ -1001,6 +1020,89 @@ export namespace Config { }) export type Provider = z.infer + const OhMyOpenCodeAgentOverride = z + .object({ + model: z.string().optional().describe("Model to use for this agent (e.g., 'anthropic/claude-opus-4-5')"), + temperature: z.number().optional().describe("Temperature for this agent"), + disable: z.boolean().optional().describe("Disable this agent"), + prompt: z.string().optional().describe("Custom system prompt for this agent"), + prompt_append: z.string().optional().describe("Append to the default system prompt"), + stream: z.boolean().optional().describe("Enable/disable streaming for this agent"), + }) + .catchall(z.any()) + + const OhMyOpenCode = z + .object({ + disabled_agents: z + .array(z.string()) + .optional() + .describe("List of oh-my-opencode agents to disable"), + disabled_hooks: z + .array(z.string()) + .optional() + .describe("List of oh-my-opencode hooks to disable"), + disabled_skills: z + .array(z.string()) + .optional() + .describe("List of oh-my-opencode skills to disable"), + disabled_mcps: z + .array(z.string()) + .optional() + .describe("List of oh-my-opencode MCPs to disable"), + disabled_commands: z + .array(z.string()) + .optional() + .describe("List of oh-my-opencode commands to disable"), + disabled_tools: z + .array(z.string()) + .optional() + .describe("List of oh-my-opencode tools to disable"), + agents: z + .record(z.string(), OhMyOpenCodeAgentOverride) + .optional() + .describe("Override settings for oh-my-opencode agents (sisyphus, oracle, librarian, explore, etc.)"), + categories: z + .record(z.string(), z.object({ model: z.string().optional() }).catchall(z.any())) + .optional() + .describe("Override model assignments for task categories"), + tmux: z + .object({ + enabled: z.boolean().optional().describe("Enable tmux integration for visual multi-agent execution"), + layout: z + .enum(["main-vertical", "main-horizontal", "tiled", "even-horizontal", "even-vertical"]) + .optional() + .describe("Tmux layout for agent panes"), + main_pane_size: z.number().optional().describe("Main pane size as percentage (20-80)"), + main_pane_min_width: z.number().optional().describe("Minimum width for main pane in columns"), + agent_pane_min_width: z.number().optional().describe("Minimum width for each agent pane in columns"), + }) + .optional() + .describe("Tmux integration configuration for visual multi-agent execution"), + ralph_loop: z + .object({ + enabled: z.boolean().optional().describe("Enable the Ralph Loop for self-referential development"), + default_max_iterations: z.number().optional().describe("Default max iterations for Ralph Loop"), + }) + .optional() + .describe("Ralph Loop configuration for continuous agent execution"), + auto_update: z.boolean().optional().describe("Enable auto-update checking for oh-my-opencode"), + experimental: z + .object({ + aggressive_truncation: z.boolean().optional().describe("Enable aggressive context truncation"), + }) + .catchall(z.any()) + .optional() + .describe("Experimental oh-my-opencode features"), + browser_automation_engine: z + .object({ + provider: z.enum(["playwright", "agent-browser"]).optional().describe("Browser automation provider to use"), + }) + .optional() + .describe("Browser automation engine configuration"), + }) + .catchall(z.any()) + .describe("Oh My OpenCode plugin configuration — integrated natively for centralized settings management") + export const Info = z .object({ $schema: z.string().optional().describe("JSON schema reference for configuration validation"), @@ -1184,6 +1286,7 @@ export namespace Config { .describe("Timeout in milliseconds for model context protocol (MCP) requests"), }) .optional(), + oh_my_opencode: OhMyOpenCode.optional().describe("Oh My OpenCode plugin configuration"), }) .strict() .meta({ diff --git a/packages/opencode/src/plugin/index.ts b/packages/opencode/src/plugin/index.ts index 5104a81c0e96..8d88ca8e0b57 100644 --- a/packages/opencode/src/plugin/index.ts +++ b/packages/opencode/src/plugin/index.ts @@ -16,7 +16,7 @@ import { gitlabAuthPlugin as GitlabAuthPlugin } from "@gitlab/opencode-gitlab-au export namespace Plugin { const log = Log.create({ service: "plugin" }) - const BUILTIN = ["coli-anthropic-auth@0.0.13"] + const BUILTIN = ["coli-anthropic-auth@0.0.13", "oh-my-opencode@3.4.0"] // Built-in plugins that are directly imported (not installed from npm) const INTERNAL_PLUGINS: PluginInstance[] = [CodexAuthPlugin, CopilotAuthPlugin, GitlabAuthPlugin] diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 9543e5b5796d..60864da24a2c 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -1846,6 +1846,120 @@ export type Config = { */ mcp_timeout?: number } + /** + * Oh My OpenCode plugin configuration + */ + oh_my_opencode?: { + /** + * List of oh-my-opencode agents to disable + */ + disabled_agents?: Array + /** + * List of oh-my-opencode hooks to disable + */ + disabled_hooks?: Array + /** + * List of oh-my-opencode skills to disable + */ + disabled_skills?: Array + /** + * List of oh-my-opencode MCPs to disable + */ + disabled_mcps?: Array + /** + * List of oh-my-opencode commands to disable + */ + disabled_commands?: Array + /** + * List of oh-my-opencode tools to disable + */ + disabled_tools?: Array + /** + * Override settings for oh-my-opencode agents (sisyphus, oracle, librarian, explore, etc.) + */ + agents?: { + [key: string]: { + model?: string + temperature?: number + disable?: boolean + prompt?: string + prompt_append?: string + stream?: boolean + [key: string]: unknown + } + } + /** + * Override model assignments for task categories + */ + categories?: { + [key: string]: { + model?: string + [key: string]: unknown + } + } + /** + * Tmux integration configuration for visual multi-agent execution + */ + tmux?: { + /** + * Enable tmux integration for visual multi-agent execution + */ + enabled?: boolean + /** + * Tmux layout for agent panes + */ + layout?: "main-vertical" | "main-horizontal" | "tiled" | "even-horizontal" | "even-vertical" + /** + * Main pane size as percentage (20-80) + */ + main_pane_size?: number + /** + * Minimum width for main pane in columns + */ + main_pane_min_width?: number + /** + * Minimum width for each agent pane in columns + */ + agent_pane_min_width?: number + } + /** + * Ralph Loop configuration for continuous agent execution + */ + ralph_loop?: { + /** + * Enable the Ralph Loop for self-referential development + */ + enabled?: boolean + /** + * Default max iterations for Ralph Loop + */ + default_max_iterations?: number + } + /** + * Enable auto-update checking for oh-my-opencode + */ + auto_update?: boolean + /** + * Experimental oh-my-opencode features + */ + experimental?: { + /** + * Enable aggressive context truncation + */ + aggressive_truncation?: boolean + [key: string]: unknown + } + /** + * Browser automation engine configuration + */ + browser_automation_engine?: { + /** + * Browser automation provider to use + */ + provider?: "playwright" | "agent-browser" + } + [key: string]: unknown + } } export type BadRequestError = { diff --git a/packages/web/src/content/docs/ecosystem.mdx b/packages/web/src/content/docs/ecosystem.mdx index 9f84c6af17ec..269eb14d45fc 100644 --- a/packages/web/src/content/docs/ecosystem.mdx +++ b/packages/web/src/content/docs/ecosystem.mdx @@ -32,7 +32,7 @@ You can also check out [awesome-opencode](https://github.com/awesome-opencode/aw | [opencode-wakatime](https://github.com/angristan/opencode-wakatime) | Track OpenCode usage with Wakatime | | [opencode-md-table-formatter](https://github.com/franlol/opencode-md-table-formatter/tree/main) | Clean up markdown tables produced by LLMs | | [opencode-morph-fast-apply](https://github.com/JRedeker/opencode-morph-fast-apply) | 10x faster code editing with Morph Fast Apply API and lazy edit markers | -| [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode) | Background agents, pre-built LSP/AST/MCP tools, curated agents, Claude Code compatible | +| [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode) | Background agents, pre-built LSP/AST/MCP tools, curated agents, Claude Code compatible (built-in) | | [opencode-notificator](https://github.com/panta82/opencode-notificator) | Desktop notifications and sound alerts for OpenCode sessions | | [opencode-notifier](https://github.com/mohak34/opencode-notifier) | Desktop notifications and sound alerts for permission, completion, and error events | | [opencode-zellij-namer](https://github.com/24601/opencode-zellij-namer) | AI-powered automatic Zellij session naming based on OpenCode context |