summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDax Raad <[email protected]>2026-04-16 12:47:09 -0400
committerDax Raad <[email protected]>2026-04-16 12:47:09 -0400
commit9bf2dfea353135874e2ba5d284e6eb0cd1b9e35d (patch)
tree2731961108a612d7679bbb5c1551feea09b55fad
parent33bb847a1dfb5e79b4815813739671a40afa0e51 (diff)
downloadopencode-9bf2dfea353135874e2ba5d284e6eb0cd1b9e35d.tar.gz
opencode-9bf2dfea353135874e2ba5d284e6eb0cd1b9e35d.zip
core: refactor config schemas into separate modules for better maintainability
-rw-r--r--packages/opencode/src/config/config.ts288
-rw-r--r--packages/opencode/src/config/index.ts2
-rw-r--r--packages/opencode/src/config/keybinds.ts316
-rw-r--r--packages/opencode/src/config/provider.ts120
-rw-r--r--packages/opencode/src/config/skills.ts13
5 files changed, 296 insertions, 443 deletions
diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts
index 92d66cf2b..7df5dbe2f 100644
--- a/packages/opencode/src/config/config.ts
+++ b/packages/opencode/src/config/config.ts
@@ -40,6 +40,8 @@ import { ConfigPlugin } from "./plugin"
import { ConfigManaged } from "./managed"
import { ConfigCommand } from "./command"
import { ConfigPermission } from "./permission"
+import { ConfigProvider } from "./provider"
+import { ConfigSkills } from "./skills"
const log = Log.create({ service: "config" })
@@ -52,168 +54,6 @@ function mergeConfigConcatArrays(target: Info, source: Info): Info {
return merged
}
-export const Skills = z.object({
- paths: z.array(z.string()).optional().describe("Additional paths to skill folders"),
- urls: z
- .array(z.string())
- .optional()
- .describe("URLs to fetch skills from (e.g., https://example.com/.well-known/skills/)"),
-})
-export type Skills = z.infer<typeof Skills>
-
-export const Keybinds = z
- .object({
- leader: z.string().optional().default("ctrl+x").describe("Leader key for keybind combinations"),
- app_exit: z.string().optional().default("ctrl+c,ctrl+d,<leader>q").describe("Exit the application"),
- editor_open: z.string().optional().default("<leader>e").describe("Open external editor"),
- theme_list: z.string().optional().default("<leader>t").describe("List available themes"),
- sidebar_toggle: z.string().optional().default("<leader>b").describe("Toggle sidebar"),
- scrollbar_toggle: z.string().optional().default("none").describe("Toggle session scrollbar"),
- username_toggle: z.string().optional().default("none").describe("Toggle username visibility"),
- status_view: z.string().optional().default("<leader>s").describe("View status"),
- session_export: z.string().optional().default("<leader>x").describe("Export session to editor"),
- session_new: z.string().optional().default("<leader>n").describe("Create a new session"),
- session_list: z.string().optional().default("<leader>l").describe("List all sessions"),
- session_timeline: z.string().optional().default("<leader>g").describe("Show session timeline"),
- session_fork: z.string().optional().default("none").describe("Fork session from message"),
- session_rename: z.string().optional().default("ctrl+r").describe("Rename session"),
- session_delete: z.string().optional().default("ctrl+d").describe("Delete session"),
- stash_delete: z.string().optional().default("ctrl+d").describe("Delete stash entry"),
- model_provider_list: z.string().optional().default("ctrl+a").describe("Open provider list from model dialog"),
- model_favorite_toggle: z.string().optional().default("ctrl+f").describe("Toggle model favorite status"),
- session_share: z.string().optional().default("none").describe("Share current session"),
- session_unshare: z.string().optional().default("none").describe("Unshare current session"),
- session_interrupt: z.string().optional().default("escape").describe("Interrupt current session"),
- session_compact: z.string().optional().default("<leader>c").describe("Compact the session"),
- messages_page_up: z.string().optional().default("pageup,ctrl+alt+b").describe("Scroll messages up by one page"),
- messages_page_down: z
- .string()
- .optional()
- .default("pagedown,ctrl+alt+f")
- .describe("Scroll messages down by one page"),
- messages_line_up: z.string().optional().default("ctrl+alt+y").describe("Scroll messages up by one line"),
- messages_line_down: z.string().optional().default("ctrl+alt+e").describe("Scroll messages down by one line"),
- messages_half_page_up: z.string().optional().default("ctrl+alt+u").describe("Scroll messages up by half page"),
- messages_half_page_down: z.string().optional().default("ctrl+alt+d").describe("Scroll messages down by half page"),
- messages_first: z.string().optional().default("ctrl+g,home").describe("Navigate to first message"),
- messages_last: z.string().optional().default("ctrl+alt+g,end").describe("Navigate to last message"),
- messages_next: z.string().optional().default("none").describe("Navigate to next message"),
- messages_previous: z.string().optional().default("none").describe("Navigate to previous message"),
- messages_last_user: z.string().optional().default("none").describe("Navigate to last user message"),
- messages_copy: z.string().optional().default("<leader>y").describe("Copy message"),
- messages_undo: z.string().optional().default("<leader>u").describe("Undo message"),
- messages_redo: z.string().optional().default("<leader>r").describe("Redo message"),
- messages_toggle_conceal: z
- .string()
- .optional()
- .default("<leader>h")
- .describe("Toggle code block concealment in messages"),
- tool_details: z.string().optional().default("none").describe("Toggle tool details visibility"),
- model_list: z.string().optional().default("<leader>m").describe("List available models"),
- model_cycle_recent: z.string().optional().default("f2").describe("Next recently used model"),
- model_cycle_recent_reverse: z.string().optional().default("shift+f2").describe("Previous recently used model"),
- model_cycle_favorite: z.string().optional().default("none").describe("Next favorite model"),
- model_cycle_favorite_reverse: z.string().optional().default("none").describe("Previous favorite model"),
- command_list: z.string().optional().default("ctrl+p").describe("List available commands"),
- agent_list: z.string().optional().default("<leader>a").describe("List agents"),
- agent_cycle: z.string().optional().default("tab").describe("Next agent"),
- agent_cycle_reverse: z.string().optional().default("shift+tab").describe("Previous agent"),
- variant_cycle: z.string().optional().default("ctrl+t").describe("Cycle model variants"),
- variant_list: z.string().optional().default("none").describe("List model variants"),
- input_clear: z.string().optional().default("ctrl+c").describe("Clear input field"),
- input_paste: z.string().optional().default("ctrl+v").describe("Paste from clipboard"),
- input_submit: z.string().optional().default("return").describe("Submit input"),
- input_newline: z
- .string()
- .optional()
- .default("shift+return,ctrl+return,alt+return,ctrl+j")
- .describe("Insert newline in input"),
- input_move_left: z.string().optional().default("left,ctrl+b").describe("Move cursor left in input"),
- input_move_right: z.string().optional().default("right,ctrl+f").describe("Move cursor right in input"),
- input_move_up: z.string().optional().default("up").describe("Move cursor up in input"),
- input_move_down: z.string().optional().default("down").describe("Move cursor down in input"),
- input_select_left: z.string().optional().default("shift+left").describe("Select left in input"),
- input_select_right: z.string().optional().default("shift+right").describe("Select right in input"),
- input_select_up: z.string().optional().default("shift+up").describe("Select up in input"),
- input_select_down: z.string().optional().default("shift+down").describe("Select down in input"),
- input_line_home: z.string().optional().default("ctrl+a").describe("Move to start of line in input"),
- input_line_end: z.string().optional().default("ctrl+e").describe("Move to end of line in input"),
- input_select_line_home: z.string().optional().default("ctrl+shift+a").describe("Select to start of line in input"),
- input_select_line_end: z.string().optional().default("ctrl+shift+e").describe("Select to end of line in input"),
- input_visual_line_home: z.string().optional().default("alt+a").describe("Move to start of visual line in input"),
- input_visual_line_end: z.string().optional().default("alt+e").describe("Move to end of visual line in input"),
- input_select_visual_line_home: z
- .string()
- .optional()
- .default("alt+shift+a")
- .describe("Select to start of visual line in input"),
- input_select_visual_line_end: z
- .string()
- .optional()
- .default("alt+shift+e")
- .describe("Select to end of visual line in input"),
- input_buffer_home: z.string().optional().default("home").describe("Move to start of buffer in input"),
- input_buffer_end: z.string().optional().default("end").describe("Move to end of buffer in input"),
- input_select_buffer_home: z
- .string()
- .optional()
- .default("shift+home")
- .describe("Select to start of buffer in input"),
- input_select_buffer_end: z.string().optional().default("shift+end").describe("Select to end of buffer in input"),
- input_delete_line: z.string().optional().default("ctrl+shift+d").describe("Delete line in input"),
- input_delete_to_line_end: z.string().optional().default("ctrl+k").describe("Delete to end of line in input"),
- input_delete_to_line_start: z.string().optional().default("ctrl+u").describe("Delete to start of line in input"),
- input_backspace: z.string().optional().default("backspace,shift+backspace").describe("Backspace in input"),
- input_delete: z.string().optional().default("ctrl+d,delete,shift+delete").describe("Delete character in input"),
- input_undo: z.string().optional().default("ctrl+-,super+z").describe("Undo in input"),
- input_redo: z.string().optional().default("ctrl+.,super+shift+z").describe("Redo in input"),
- input_word_forward: z
- .string()
- .optional()
- .default("alt+f,alt+right,ctrl+right")
- .describe("Move word forward in input"),
- input_word_backward: z
- .string()
- .optional()
- .default("alt+b,alt+left,ctrl+left")
- .describe("Move word backward in input"),
- input_select_word_forward: z
- .string()
- .optional()
- .default("alt+shift+f,alt+shift+right")
- .describe("Select word forward in input"),
- input_select_word_backward: z
- .string()
- .optional()
- .default("alt+shift+b,alt+shift+left")
- .describe("Select word backward in input"),
- input_delete_word_forward: z
- .string()
- .optional()
- .default("alt+d,alt+delete,ctrl+delete")
- .describe("Delete word forward in input"),
- input_delete_word_backward: z
- .string()
- .optional()
- .default("ctrl+w,ctrl+backspace,alt+backspace")
- .describe("Delete word backward in input"),
- history_previous: z.string().optional().default("up").describe("Previous history item"),
- history_next: z.string().optional().default("down").describe("Next history item"),
- session_child_first: z.string().optional().default("<leader>down").describe("Go to first child session"),
- session_child_cycle: z.string().optional().default("right").describe("Go to next child session"),
- session_child_cycle_reverse: z.string().optional().default("left").describe("Go to previous child session"),
- session_parent: z.string().optional().default("up").describe("Go to parent session"),
- terminal_suspend: z.string().optional().default("ctrl+z").describe("Suspend terminal"),
- terminal_title_toggle: z.string().optional().default("none").describe("Toggle terminal title"),
- tips_toggle: z.string().optional().default("<leader>h").describe("Toggle tips on home screen"),
- plugin_manager: z.string().optional().default("none").describe("Open plugin manager dialog"),
- display_thinking: z.string().optional().default("none").describe("Toggle thinking blocks visibility"),
- })
- .strict()
- .meta({
- ref: "KeybindsConfig",
- })
-
export const Server = z
.object({
port: z.number().int().positive().optional().describe("Port to listen on"),
@@ -232,123 +72,6 @@ export const Layout = z.enum(["auto", "stretch"]).meta({
})
export type Layout = z.infer<typeof Layout>
-export const Model = z
- .object({
- id: z.string(),
- name: z.string(),
- family: z.string().optional(),
- release_date: z.string(),
- attachment: z.boolean(),
- reasoning: z.boolean(),
- temperature: z.boolean(),
- tool_call: z.boolean(),
- interleaved: z
- .union([
- z.literal(true),
- z
- .object({
- field: z.enum(["reasoning_content", "reasoning_details"]),
- })
- .strict(),
- ])
- .optional(),
- cost: z
- .object({
- input: z.number(),
- output: z.number(),
- cache_read: z.number().optional(),
- cache_write: z.number().optional(),
- context_over_200k: z
- .object({
- input: z.number(),
- output: z.number(),
- cache_read: z.number().optional(),
- cache_write: z.number().optional(),
- })
- .optional(),
- })
- .optional(),
- limit: z.object({
- context: z.number(),
- input: z.number().optional(),
- output: z.number(),
- }),
- modalities: z
- .object({
- input: z.array(z.enum(["text", "audio", "image", "video", "pdf"])),
- output: z.array(z.enum(["text", "audio", "image", "video", "pdf"])),
- })
- .optional(),
- experimental: z.boolean().optional(),
- status: z.enum(["alpha", "beta", "deprecated"]).optional(),
- provider: z.object({ npm: z.string().optional(), api: z.string().optional() }).optional(),
- options: z.record(z.string(), z.any()),
- headers: z.record(z.string(), z.string()).optional(),
- variants: z
- .record(
- z.string(),
- z
- .object({
- disabled: z.boolean().optional().describe("Disable this variant for the model"),
- })
- .catchall(z.any()),
- )
- .optional()
- .describe("Variant-specific configuration"),
- })
- .partial()
-
-export const Provider = z
- .object({
- api: z.string().optional(),
- name: z.string(),
- env: z.array(z.string()),
- id: z.string(),
- npm: z.string().optional(),
- whitelist: z.array(z.string()).optional(),
- blacklist: z.array(z.string()).optional(),
- options: z
- .object({
- apiKey: z.string().optional(),
- baseURL: z.string().optional(),
- enterpriseUrl: z.string().optional().describe("GitHub Enterprise URL for copilot authentication"),
- setCacheKey: z.boolean().optional().describe("Enable promptCacheKey for this provider (default false)"),
- timeout: z
- .union([
- z
- .number()
- .int()
- .positive()
- .describe(
- "Timeout in milliseconds for requests to this provider. Default is 300000 (5 minutes). Set to false to disable timeout.",
- ),
- z.literal(false).describe("Disable timeout for this provider entirely."),
- ])
- .optional()
- .describe(
- "Timeout in milliseconds for requests to this provider. Default is 300000 (5 minutes). Set to false to disable timeout.",
- ),
- chunkTimeout: z
- .number()
- .int()
- .positive()
- .optional()
- .describe(
- "Timeout in milliseconds between streamed SSE chunks for this provider. If no chunk arrives within this window, the request is aborted.",
- ),
- })
- .catchall(z.any())
- .optional(),
- models: z.record(z.string(), Model).optional(),
- })
- .partial()
- .strict()
- .meta({
- ref: "ProviderConfig",
- })
-
-export type Provider = z.infer<typeof Provider>
-
export const Info = z
.object({
$schema: z.string().optional().describe("JSON schema reference for configuration validation"),
@@ -358,7 +81,7 @@ export const Info = z
.record(z.string(), ConfigCommand.Info)
.optional()
.describe("Command configuration, see https://opencode.ai/docs/commands"),
- skills: Skills.optional().describe("Additional skill folder paths"),
+ skills: ConfigSkills.Info.optional().describe("Additional skill folder paths"),
watcher: z
.object({
ignore: z.array(z.string()).optional(),
@@ -427,7 +150,10 @@ export const Info = z
.catchall(ConfigAgent.Info)
.optional()
.describe("Agent configuration, see https://opencode.ai/docs/agents"),
- provider: z.record(z.string(), Provider).optional().describe("Custom provider configurations and model overrides"),
+ provider: z
+ .record(z.string(), ConfigProvider.Info)
+ .optional()
+ .describe("Custom provider configurations and model overrides"),
mcp: z
.record(
z.string(),
diff --git a/packages/opencode/src/config/index.ts b/packages/opencode/src/config/index.ts
index f1af71867..37665d8c6 100644
--- a/packages/opencode/src/config/index.ts
+++ b/packages/opencode/src/config/index.ts
@@ -7,3 +7,5 @@ export * as ConfigMCP from "./mcp"
export { ConfigModelID } from "./model-id"
export * as ConfigPermission from "./permission"
export * as ConfigPaths from "./paths"
+export * as ConfigProvider from "./provider"
+export * as ConfigSkills from "./skills"
diff --git a/packages/opencode/src/config/keybinds.ts b/packages/opencode/src/config/keybinds.ts
index 9b8d9e283..cb146b7ca 100644
--- a/packages/opencode/src/config/keybinds.ts
+++ b/packages/opencode/src/config/keybinds.ts
@@ -1,164 +1,156 @@
+export * as ConfigKeybinds from "./keybinds"
+
import z from "zod"
-export namespace ConfigKeybinds {
- export const Keybinds = z
- .object({
- leader: z.string().optional().default("ctrl+x").describe("Leader key for keybind combinations"),
- app_exit: z.string().optional().default("ctrl+c,ctrl+d,<leader>q").describe("Exit the application"),
- editor_open: z.string().optional().default("<leader>e").describe("Open external editor"),
- theme_list: z.string().optional().default("<leader>t").describe("List available themes"),
- sidebar_toggle: z.string().optional().default("<leader>b").describe("Toggle sidebar"),
- scrollbar_toggle: z.string().optional().default("none").describe("Toggle session scrollbar"),
- username_toggle: z.string().optional().default("none").describe("Toggle username visibility"),
- status_view: z.string().optional().default("<leader>s").describe("View status"),
- session_export: z.string().optional().default("<leader>x").describe("Export session to editor"),
- session_new: z.string().optional().default("<leader>n").describe("Create a new session"),
- session_list: z.string().optional().default("<leader>l").describe("List all sessions"),
- session_timeline: z.string().optional().default("<leader>g").describe("Show session timeline"),
- session_fork: z.string().optional().default("none").describe("Fork session from message"),
- session_rename: z.string().optional().default("ctrl+r").describe("Rename session"),
- session_delete: z.string().optional().default("ctrl+d").describe("Delete session"),
- stash_delete: z.string().optional().default("ctrl+d").describe("Delete stash entry"),
- model_provider_list: z.string().optional().default("ctrl+a").describe("Open provider list from model dialog"),
- model_favorite_toggle: z.string().optional().default("ctrl+f").describe("Toggle model favorite status"),
- session_share: z.string().optional().default("none").describe("Share current session"),
- session_unshare: z.string().optional().default("none").describe("Unshare current session"),
- session_interrupt: z.string().optional().default("escape").describe("Interrupt current session"),
- session_compact: z.string().optional().default("<leader>c").describe("Compact the session"),
- messages_page_up: z.string().optional().default("pageup,ctrl+alt+b").describe("Scroll messages up by one page"),
- messages_page_down: z
- .string()
- .optional()
- .default("pagedown,ctrl+alt+f")
- .describe("Scroll messages down by one page"),
- messages_line_up: z.string().optional().default("ctrl+alt+y").describe("Scroll messages up by one line"),
- messages_line_down: z.string().optional().default("ctrl+alt+e").describe("Scroll messages down by one line"),
- messages_half_page_up: z.string().optional().default("ctrl+alt+u").describe("Scroll messages up by half page"),
- messages_half_page_down: z
- .string()
- .optional()
- .default("ctrl+alt+d")
- .describe("Scroll messages down by half page"),
- messages_first: z.string().optional().default("ctrl+g,home").describe("Navigate to first message"),
- messages_last: z.string().optional().default("ctrl+alt+g,end").describe("Navigate to last message"),
- messages_next: z.string().optional().default("none").describe("Navigate to next message"),
- messages_previous: z.string().optional().default("none").describe("Navigate to previous message"),
- messages_last_user: z.string().optional().default("none").describe("Navigate to last user message"),
- messages_copy: z.string().optional().default("<leader>y").describe("Copy message"),
- messages_undo: z.string().optional().default("<leader>u").describe("Undo message"),
- messages_redo: z.string().optional().default("<leader>r").describe("Redo message"),
- messages_toggle_conceal: z
- .string()
- .optional()
- .default("<leader>h")
- .describe("Toggle code block concealment in messages"),
- tool_details: z.string().optional().default("none").describe("Toggle tool details visibility"),
- model_list: z.string().optional().default("<leader>m").describe("List available models"),
- model_cycle_recent: z.string().optional().default("f2").describe("Next recently used model"),
- model_cycle_recent_reverse: z.string().optional().default("shift+f2").describe("Previous recently used model"),
- model_cycle_favorite: z.string().optional().default("none").describe("Next favorite model"),
- model_cycle_favorite_reverse: z.string().optional().default("none").describe("Previous favorite model"),
- command_list: z.string().optional().default("ctrl+p").describe("List available commands"),
- agent_list: z.string().optional().default("<leader>a").describe("List agents"),
- agent_cycle: z.string().optional().default("tab").describe("Next agent"),
- agent_cycle_reverse: z.string().optional().default("shift+tab").describe("Previous agent"),
- variant_cycle: z.string().optional().default("ctrl+t").describe("Cycle model variants"),
- variant_list: z.string().optional().default("none").describe("List model variants"),
- input_clear: z.string().optional().default("ctrl+c").describe("Clear input field"),
- input_paste: z.string().optional().default("ctrl+v").describe("Paste from clipboard"),
- input_submit: z.string().optional().default("return").describe("Submit input"),
- input_newline: z
- .string()
- .optional()
- .default("shift+return,ctrl+return,alt+return,ctrl+j")
- .describe("Insert newline in input"),
- input_move_left: z.string().optional().default("left,ctrl+b").describe("Move cursor left in input"),
- input_move_right: z.string().optional().default("right,ctrl+f").describe("Move cursor right in input"),
- input_move_up: z.string().optional().default("up").describe("Move cursor up in input"),
- input_move_down: z.string().optional().default("down").describe("Move cursor down in input"),
- input_select_left: z.string().optional().default("shift+left").describe("Select left in input"),
- input_select_right: z.string().optional().default("shift+right").describe("Select right in input"),
- input_select_up: z.string().optional().default("shift+up").describe("Select up in input"),
- input_select_down: z.string().optional().default("shift+down").describe("Select down in input"),
- input_line_home: z.string().optional().default("ctrl+a").describe("Move to start of line in input"),
- input_line_end: z.string().optional().default("ctrl+e").describe("Move to end of line in input"),
- input_select_line_home: z
- .string()
- .optional()
- .default("ctrl+shift+a")
- .describe("Select to start of line in input"),
- input_select_line_end: z.string().optional().default("ctrl+shift+e").describe("Select to end of line in input"),
- input_visual_line_home: z.string().optional().default("alt+a").describe("Move to start of visual line in input"),
- input_visual_line_end: z.string().optional().default("alt+e").describe("Move to end of visual line in input"),
- input_select_visual_line_home: z
- .string()
- .optional()
- .default("alt+shift+a")
- .describe("Select to start of visual line in input"),
- input_select_visual_line_end: z
- .string()
- .optional()
- .default("alt+shift+e")
- .describe("Select to end of visual line in input"),
- input_buffer_home: z.string().optional().default("home").describe("Move to start of buffer in input"),
- input_buffer_end: z.string().optional().default("end").describe("Move to end of buffer in input"),
- input_select_buffer_home: z
- .string()
- .optional()
- .default("shift+home")
- .describe("Select to start of buffer in input"),
- input_select_buffer_end: z.string().optional().default("shift+end").describe("Select to end of buffer in input"),
- input_delete_line: z.string().optional().default("ctrl+shift+d").describe("Delete line in input"),
- input_delete_to_line_end: z.string().optional().default("ctrl+k").describe("Delete to end of line in input"),
- input_delete_to_line_start: z.string().optional().default("ctrl+u").describe("Delete to start of line in input"),
- input_backspace: z.string().optional().default("backspace,shift+backspace").describe("Backspace in input"),
- input_delete: z.string().optional().default("ctrl+d,delete,shift+delete").describe("Delete character in input"),
- input_undo: z.string().optional().default("ctrl+-,super+z").describe("Undo in input"),
- input_redo: z.string().optional().default("ctrl+.,super+shift+z").describe("Redo in input"),
- input_word_forward: z
- .string()
- .optional()
- .default("alt+f,alt+right,ctrl+right")
- .describe("Move word forward in input"),
- input_word_backward: z
- .string()
- .optional()
- .default("alt+b,alt+left,ctrl+left")
- .describe("Move word backward in input"),
- input_select_word_forward: z
- .string()
- .optional()
- .default("alt+shift+f,alt+shift+right")
- .describe("Select word forward in input"),
- input_select_word_backward: z
- .string()
- .optional()
- .default("alt+shift+b,alt+shift+left")
- .describe("Select word backward in input"),
- input_delete_word_forward: z
- .string()
- .optional()
- .default("alt+d,alt+delete,ctrl+delete")
- .describe("Delete word forward in input"),
- input_delete_word_backward: z
- .string()
- .optional()
- .default("ctrl+w,ctrl+backspace,alt+backspace")
- .describe("Delete word backward in input"),
- history_previous: z.string().optional().default("up").describe("Previous history item"),
- history_next: z.string().optional().default("down").describe("Next history item"),
- session_child_first: z.string().optional().default("<leader>down").describe("Go to first child session"),
- session_child_cycle: z.string().optional().default("right").describe("Go to next child session"),
- session_child_cycle_reverse: z.string().optional().default("left").describe("Go to previous child session"),
- session_parent: z.string().optional().default("up").describe("Go to parent session"),
- terminal_suspend: z.string().optional().default("ctrl+z").describe("Suspend terminal"),
- terminal_title_toggle: z.string().optional().default("none").describe("Toggle terminal title"),
- tips_toggle: z.string().optional().default("<leader>h").describe("Toggle tips on home screen"),
- plugin_manager: z.string().optional().default("none").describe("Open plugin manager dialog"),
- display_thinking: z.string().optional().default("none").describe("Toggle thinking blocks visibility"),
- })
- .strict()
- .meta({
- ref: "KeybindsConfig",
- })
-}
+export const Keybinds = z
+ .object({
+ leader: z.string().optional().default("ctrl+x").describe("Leader key for keybind combinations"),
+ app_exit: z.string().optional().default("ctrl+c,ctrl+d,<leader>q").describe("Exit the application"),
+ editor_open: z.string().optional().default("<leader>e").describe("Open external editor"),
+ theme_list: z.string().optional().default("<leader>t").describe("List available themes"),
+ sidebar_toggle: z.string().optional().default("<leader>b").describe("Toggle sidebar"),
+ scrollbar_toggle: z.string().optional().default("none").describe("Toggle session scrollbar"),
+ username_toggle: z.string().optional().default("none").describe("Toggle username visibility"),
+ status_view: z.string().optional().default("<leader>s").describe("View status"),
+ session_export: z.string().optional().default("<leader>x").describe("Export session to editor"),
+ session_new: z.string().optional().default("<leader>n").describe("Create a new session"),
+ session_list: z.string().optional().default("<leader>l").describe("List all sessions"),
+ session_timeline: z.string().optional().default("<leader>g").describe("Show session timeline"),
+ session_fork: z.string().optional().default("none").describe("Fork session from message"),
+ session_rename: z.string().optional().default("ctrl+r").describe("Rename session"),
+ session_delete: z.string().optional().default("ctrl+d").describe("Delete session"),
+ stash_delete: z.string().optional().default("ctrl+d").describe("Delete stash entry"),
+ model_provider_list: z.string().optional().default("ctrl+a").describe("Open provider list from model dialog"),
+ model_favorite_toggle: z.string().optional().default("ctrl+f").describe("Toggle model favorite status"),
+ session_share: z.string().optional().default("none").describe("Share current session"),
+ session_unshare: z.string().optional().default("none").describe("Unshare current session"),
+ session_interrupt: z.string().optional().default("escape").describe("Interrupt current session"),
+ session_compact: z.string().optional().default("<leader>c").describe("Compact the session"),
+ messages_page_up: z.string().optional().default("pageup,ctrl+alt+b").describe("Scroll messages up by one page"),
+ messages_page_down: z
+ .string()
+ .optional()
+ .default("pagedown,ctrl+alt+f")
+ .describe("Scroll messages down by one page"),
+ messages_line_up: z.string().optional().default("ctrl+alt+y").describe("Scroll messages up by one line"),
+ messages_line_down: z.string().optional().default("ctrl+alt+e").describe("Scroll messages down by one line"),
+ messages_half_page_up: z.string().optional().default("ctrl+alt+u").describe("Scroll messages up by half page"),
+ messages_half_page_down: z.string().optional().default("ctrl+alt+d").describe("Scroll messages down by half page"),
+ messages_first: z.string().optional().default("ctrl+g,home").describe("Navigate to first message"),
+ messages_last: z.string().optional().default("ctrl+alt+g,end").describe("Navigate to last message"),
+ messages_next: z.string().optional().default("none").describe("Navigate to next message"),
+ messages_previous: z.string().optional().default("none").describe("Navigate to previous message"),
+ messages_last_user: z.string().optional().default("none").describe("Navigate to last user message"),
+ messages_copy: z.string().optional().default("<leader>y").describe("Copy message"),
+ messages_undo: z.string().optional().default("<leader>u").describe("Undo message"),
+ messages_redo: z.string().optional().default("<leader>r").describe("Redo message"),
+ messages_toggle_conceal: z
+ .string()
+ .optional()
+ .default("<leader>h")
+ .describe("Toggle code block concealment in messages"),
+ tool_details: z.string().optional().default("none").describe("Toggle tool details visibility"),
+ model_list: z.string().optional().default("<leader>m").describe("List available models"),
+ model_cycle_recent: z.string().optional().default("f2").describe("Next recently used model"),
+ model_cycle_recent_reverse: z.string().optional().default("shift+f2").describe("Previous recently used model"),
+ model_cycle_favorite: z.string().optional().default("none").describe("Next favorite model"),
+ model_cycle_favorite_reverse: z.string().optional().default("none").describe("Previous favorite model"),
+ command_list: z.string().optional().default("ctrl+p").describe("List available commands"),
+ agent_list: z.string().optional().default("<leader>a").describe("List agents"),
+ agent_cycle: z.string().optional().default("tab").describe("Next agent"),
+ agent_cycle_reverse: z.string().optional().default("shift+tab").describe("Previous agent"),
+ variant_cycle: z.string().optional().default("ctrl+t").describe("Cycle model variants"),
+ variant_list: z.string().optional().default("none").describe("List model variants"),
+ input_clear: z.string().optional().default("ctrl+c").describe("Clear input field"),
+ input_paste: z.string().optional().default("ctrl+v").describe("Paste from clipboard"),
+ input_submit: z.string().optional().default("return").describe("Submit input"),
+ input_newline: z
+ .string()
+ .optional()
+ .default("shift+return,ctrl+return,alt+return,ctrl+j")
+ .describe("Insert newline in input"),
+ input_move_left: z.string().optional().default("left,ctrl+b").describe("Move cursor left in input"),
+ input_move_right: z.string().optional().default("right,ctrl+f").describe("Move cursor right in input"),
+ input_move_up: z.string().optional().default("up").describe("Move cursor up in input"),
+ input_move_down: z.string().optional().default("down").describe("Move cursor down in input"),
+ input_select_left: z.string().optional().default("shift+left").describe("Select left in input"),
+ input_select_right: z.string().optional().default("shift+right").describe("Select right in input"),
+ input_select_up: z.string().optional().default("shift+up").describe("Select up in input"),
+ input_select_down: z.string().optional().default("shift+down").describe("Select down in input"),
+ input_line_home: z.string().optional().default("ctrl+a").describe("Move to start of line in input"),
+ input_line_end: z.string().optional().default("ctrl+e").describe("Move to end of line in input"),
+ input_select_line_home: z.string().optional().default("ctrl+shift+a").describe("Select to start of line in input"),
+ input_select_line_end: z.string().optional().default("ctrl+shift+e").describe("Select to end of line in input"),
+ input_visual_line_home: z.string().optional().default("alt+a").describe("Move to start of visual line in input"),
+ input_visual_line_end: z.string().optional().default("alt+e").describe("Move to end of visual line in input"),
+ input_select_visual_line_home: z
+ .string()
+ .optional()
+ .default("alt+shift+a")
+ .describe("Select to start of visual line in input"),
+ input_select_visual_line_end: z
+ .string()
+ .optional()
+ .default("alt+shift+e")
+ .describe("Select to end of visual line in input"),
+ input_buffer_home: z.string().optional().default("home").describe("Move to start of buffer in input"),
+ input_buffer_end: z.string().optional().default("end").describe("Move to end of buffer in input"),
+ input_select_buffer_home: z
+ .string()
+ .optional()
+ .default("shift+home")
+ .describe("Select to start of buffer in input"),
+ input_select_buffer_end: z.string().optional().default("shift+end").describe("Select to end of buffer in input"),
+ input_delete_line: z.string().optional().default("ctrl+shift+d").describe("Delete line in input"),
+ input_delete_to_line_end: z.string().optional().default("ctrl+k").describe("Delete to end of line in input"),
+ input_delete_to_line_start: z.string().optional().default("ctrl+u").describe("Delete to start of line in input"),
+ input_backspace: z.string().optional().default("backspace,shift+backspace").describe("Backspace in input"),
+ input_delete: z.string().optional().default("ctrl+d,delete,shift+delete").describe("Delete character in input"),
+ input_undo: z.string().optional().default("ctrl+-,super+z").describe("Undo in input"),
+ input_redo: z.string().optional().default("ctrl+.,super+shift+z").describe("Redo in input"),
+ input_word_forward: z
+ .string()
+ .optional()
+ .default("alt+f,alt+right,ctrl+right")
+ .describe("Move word forward in input"),
+ input_word_backward: z
+ .string()
+ .optional()
+ .default("alt+b,alt+left,ctrl+left")
+ .describe("Move word backward in input"),
+ input_select_word_forward: z
+ .string()
+ .optional()
+ .default("alt+shift+f,alt+shift+right")
+ .describe("Select word forward in input"),
+ input_select_word_backward: z
+ .string()
+ .optional()
+ .default("alt+shift+b,alt+shift+left")
+ .describe("Select word backward in input"),
+ input_delete_word_forward: z
+ .string()
+ .optional()
+ .default("alt+d,alt+delete,ctrl+delete")
+ .describe("Delete word forward in input"),
+ input_delete_word_backward: z
+ .string()
+ .optional()
+ .default("ctrl+w,ctrl+backspace,alt+backspace")
+ .describe("Delete word backward in input"),
+ history_previous: z.string().optional().default("up").describe("Previous history item"),
+ history_next: z.string().optional().default("down").describe("Next history item"),
+ session_child_first: z.string().optional().default("<leader>down").describe("Go to first child session"),
+ session_child_cycle: z.string().optional().default("right").describe("Go to next child session"),
+ session_child_cycle_reverse: z.string().optional().default("left").describe("Go to previous child session"),
+ session_parent: z.string().optional().default("up").describe("Go to parent session"),
+ terminal_suspend: z.string().optional().default("ctrl+z").describe("Suspend terminal"),
+ terminal_title_toggle: z.string().optional().default("none").describe("Toggle terminal title"),
+ tips_toggle: z.string().optional().default("<leader>h").describe("Toggle tips on home screen"),
+ plugin_manager: z.string().optional().default("none").describe("Open plugin manager dialog"),
+ display_thinking: z.string().optional().default("none").describe("Toggle thinking blocks visibility"),
+ })
+ .strict()
+ .meta({
+ ref: "KeybindsConfig",
+ })
diff --git a/packages/opencode/src/config/provider.ts b/packages/opencode/src/config/provider.ts
new file mode 100644
index 000000000..09efedf49
--- /dev/null
+++ b/packages/opencode/src/config/provider.ts
@@ -0,0 +1,120 @@
+import z from "zod"
+
+export namespace ConfigProvider {
+ export const Model = z
+ .object({
+ id: z.string(),
+ name: z.string(),
+ family: z.string().optional(),
+ release_date: z.string(),
+ attachment: z.boolean(),
+ reasoning: z.boolean(),
+ temperature: z.boolean(),
+ tool_call: z.boolean(),
+ interleaved: z
+ .union([
+ z.literal(true),
+ z
+ .object({
+ field: z.enum(["reasoning_content", "reasoning_details"]),
+ })
+ .strict(),
+ ])
+ .optional(),
+ cost: z
+ .object({
+ input: z.number(),
+ output: z.number(),
+ cache_read: z.number().optional(),
+ cache_write: z.number().optional(),
+ context_over_200k: z
+ .object({
+ input: z.number(),
+ output: z.number(),
+ cache_read: z.number().optional(),
+ cache_write: z.number().optional(),
+ })
+ .optional(),
+ })
+ .optional(),
+ limit: z.object({
+ context: z.number(),
+ input: z.number().optional(),
+ output: z.number(),
+ }),
+ modalities: z
+ .object({
+ input: z.array(z.enum(["text", "audio", "image", "video", "pdf"])),
+ output: z.array(z.enum(["text", "audio", "image", "video", "pdf"])),
+ })
+ .optional(),
+ experimental: z.boolean().optional(),
+ status: z.enum(["alpha", "beta", "deprecated"]).optional(),
+ provider: z.object({ npm: z.string().optional(), api: z.string().optional() }).optional(),
+ options: z.record(z.string(), z.any()),
+ headers: z.record(z.string(), z.string()).optional(),
+ variants: z
+ .record(
+ z.string(),
+ z
+ .object({
+ disabled: z.boolean().optional().describe("Disable this variant for the model"),
+ })
+ .catchall(z.any()),
+ )
+ .optional()
+ .describe("Variant-specific configuration"),
+ })
+ .partial()
+
+ export const Info = z
+ .object({
+ api: z.string().optional(),
+ name: z.string(),
+ env: z.array(z.string()),
+ id: z.string(),
+ npm: z.string().optional(),
+ whitelist: z.array(z.string()).optional(),
+ blacklist: z.array(z.string()).optional(),
+ options: z
+ .object({
+ apiKey: z.string().optional(),
+ baseURL: z.string().optional(),
+ enterpriseUrl: z.string().optional().describe("GitHub Enterprise URL for copilot authentication"),
+ setCacheKey: z.boolean().optional().describe("Enable promptCacheKey for this provider (default false)"),
+ timeout: z
+ .union([
+ z
+ .number()
+ .int()
+ .positive()
+ .describe(
+ "Timeout in milliseconds for requests to this provider. Default is 300000 (5 minutes). Set to false to disable timeout.",
+ ),
+ z.literal(false).describe("Disable timeout for this provider entirely."),
+ ])
+ .optional()
+ .describe(
+ "Timeout in milliseconds for requests to this provider. Default is 300000 (5 minutes). Set to false to disable timeout.",
+ ),
+ chunkTimeout: z
+ .number()
+ .int()
+ .positive()
+ .optional()
+ .describe(
+ "Timeout in milliseconds between streamed SSE chunks for this provider. If no chunk arrives within this window, the request is aborted.",
+ ),
+ })
+ .catchall(z.any())
+ .optional(),
+ models: z.record(z.string(), Model).optional(),
+ })
+ .partial()
+ .strict()
+ .meta({
+ ref: "ProviderConfig",
+ })
+
+ export type Info = z.infer<typeof Info>
+}
diff --git a/packages/opencode/src/config/skills.ts b/packages/opencode/src/config/skills.ts
new file mode 100644
index 000000000..bdc63f5d6
--- /dev/null
+++ b/packages/opencode/src/config/skills.ts
@@ -0,0 +1,13 @@
+import z from "zod"
+
+export namespace ConfigSkills {
+ export const Info = z.object({
+ paths: z.array(z.string()).optional().describe("Additional paths to skill folders"),
+ urls: z
+ .array(z.string())
+ .optional()
+ .describe("URLs to fetch skills from (e.g., https://example.com/.well-known/skills/)"),
+ })
+
+ export type Info = z.infer<typeof Info>
+}