refactor: wave 2 - split atlas, auto-update-checker, session-recovery, todo-enforcer, background-task hooks

- Extract atlas/ into 15 focused modules (hook, event handler, tool policies, types, etc.)
- Split auto-update-checker into checker/ and hook/ subdirectories with single-purpose files
- Decompose session-recovery into separate recovery strategy files per error type
- Extract todo-continuation-enforcer from monolith to directory with dedicated modules
- Split background-task/tools.ts into individual tool creator files
- Extract command-executor, tmux-utils into focused sub-modules
- Split config/schema.ts into domain-specific schema files
- Decompose cli/config-manager.ts into focused modules
- Rollback skill-mcp-manager, model-availability, index.ts splits that broke tests
- Fix all import path depths for moved files (../../ -> ../../../)
- Add explicit type annotations to resolve TS7006 implicit any errors

Typecheck: 0 errors
Tests: 2359 pass, 5 fail (all pre-existing)
This commit is contained in:
YeonGyu-Kim
2026-02-08 15:01:42 +09:00
parent 29155ec7bc
commit 119e18c810
158 changed files with 7806 additions and 7050 deletions
+21 -462
View File
@@ -1,464 +1,23 @@
import { z } from "zod"
import { AnyMcpNameSchema, McpNameSchema } from "../mcp/types"
const PermissionValue = z.enum(["ask", "allow", "deny"])
const BashPermission = z.union([
PermissionValue,
z.record(z.string(), PermissionValue),
])
const AgentPermissionSchema = z.object({
edit: PermissionValue.optional(),
bash: BashPermission.optional(),
webfetch: PermissionValue.optional(),
task: PermissionValue.optional(),
doom_loop: PermissionValue.optional(),
external_directory: PermissionValue.optional(),
})
export const BuiltinAgentNameSchema = z.enum([
"sisyphus",
"hephaestus",
"prometheus",
"oracle",
"librarian",
"explore",
"multimodal-looker",
"metis",
"momus",
"atlas",
])
export const BuiltinSkillNameSchema = z.enum([
"playwright",
"agent-browser",
"dev-browser",
"frontend-ui-ux",
"git-master",
])
export const OverridableAgentNameSchema = z.enum([
"build",
"plan",
"sisyphus",
"hephaestus",
"sisyphus-junior",
"OpenCode-Builder",
"prometheus",
"metis",
"momus",
"oracle",
"librarian",
"explore",
"multimodal-looker",
"atlas",
])
export const AgentNameSchema = BuiltinAgentNameSchema
export const HookNameSchema = z.enum([
"todo-continuation-enforcer",
"context-window-monitor",
"session-recovery",
"session-notification",
"comment-checker",
"grep-output-truncator",
"tool-output-truncator",
"question-label-truncator",
"directory-agents-injector",
"directory-readme-injector",
"empty-task-response-detector",
"think-mode",
"subagent-question-blocker",
"anthropic-context-window-limit-recovery",
"preemptive-compaction",
"rules-injector",
"background-notification",
"auto-update-checker",
"startup-toast",
"keyword-detector",
"agent-usage-reminder",
"non-interactive-env",
"interactive-bash-session",
"thinking-block-validator",
"ralph-loop",
"category-skill-reminder",
"compaction-context-injector",
"compaction-todo-preserver",
"claude-code-hooks",
"auto-slash-command",
"edit-error-recovery",
"delegate-task-retry",
"prometheus-md-only",
"sisyphus-junior-notepad",
"start-work",
"atlas",
"unstable-agent-babysitter",
"task-reminder",
"task-resume-info",
"stop-continuation-guard",
"tasks-todowrite-disabler",
"write-existing-file-guard",
"anthropic-effort",
])
export const BuiltinCommandNameSchema = z.enum([
"init-deep",
"ralph-loop",
"ulw-loop",
"cancel-ralph",
"refactor",
"start-work",
"stop-continuation",
])
export const AgentOverrideConfigSchema = z.object({
/** @deprecated Use `category` instead. Model is inherited from category defaults. */
model: z.string().optional(),
variant: z.string().optional(),
/** Category name to inherit model and other settings from CategoryConfig */
category: z.string().optional(),
/** Skill names to inject into agent prompt */
skills: z.array(z.string()).optional(),
temperature: z.number().min(0).max(2).optional(),
top_p: z.number().min(0).max(1).optional(),
prompt: z.string().optional(),
prompt_append: z.string().optional(),
tools: z.record(z.string(), z.boolean()).optional(),
disable: z.boolean().optional(),
description: z.string().optional(),
mode: z.enum(["subagent", "primary", "all"]).optional(),
color: z
.string()
.regex(/^#[0-9A-Fa-f]{6}$/)
.optional(),
permission: AgentPermissionSchema.optional(),
/** Maximum tokens for response. Passed directly to OpenCode SDK. */
maxTokens: z.number().optional(),
/** Extended thinking configuration (Anthropic). Overrides category and default settings. */
thinking: z.object({
type: z.enum(["enabled", "disabled"]),
budgetTokens: z.number().optional(),
}).optional(),
/** Reasoning effort level (OpenAI). Overrides category and default settings. */
reasoningEffort: z.enum(["low", "medium", "high", "xhigh"]).optional(),
/** Text verbosity level. */
textVerbosity: z.enum(["low", "medium", "high"]).optional(),
/** Provider-specific options. Passed directly to OpenCode SDK. */
providerOptions: z.record(z.string(), z.unknown()).optional(),
})
export const AgentOverridesSchema = z.object({
build: AgentOverrideConfigSchema.optional(),
plan: AgentOverrideConfigSchema.optional(),
sisyphus: AgentOverrideConfigSchema.optional(),
hephaestus: AgentOverrideConfigSchema.optional(),
"sisyphus-junior": AgentOverrideConfigSchema.optional(),
"OpenCode-Builder": AgentOverrideConfigSchema.optional(),
prometheus: AgentOverrideConfigSchema.optional(),
metis: AgentOverrideConfigSchema.optional(),
momus: AgentOverrideConfigSchema.optional(),
oracle: AgentOverrideConfigSchema.optional(),
librarian: AgentOverrideConfigSchema.optional(),
explore: AgentOverrideConfigSchema.optional(),
"multimodal-looker": AgentOverrideConfigSchema.optional(),
atlas: AgentOverrideConfigSchema.optional(),
})
export const ClaudeCodeConfigSchema = z.object({
mcp: z.boolean().optional(),
commands: z.boolean().optional(),
skills: z.boolean().optional(),
agents: z.boolean().optional(),
hooks: z.boolean().optional(),
plugins: z.boolean().optional(),
plugins_override: z.record(z.string(), z.boolean()).optional(),
})
export const SisyphusAgentConfigSchema = z.object({
disabled: z.boolean().optional(),
default_builder_enabled: z.boolean().optional(),
planner_enabled: z.boolean().optional(),
replace_plan: z.boolean().optional(),
})
export const CategoryConfigSchema = z.object({
/** Human-readable description of the category's purpose. Shown in task prompt. */
description: z.string().optional(),
model: z.string().optional(),
variant: z.string().optional(),
temperature: z.number().min(0).max(2).optional(),
top_p: z.number().min(0).max(1).optional(),
maxTokens: z.number().optional(),
thinking: z.object({
type: z.enum(["enabled", "disabled"]),
budgetTokens: z.number().optional(),
}).optional(),
reasoningEffort: z.enum(["low", "medium", "high", "xhigh"]).optional(),
textVerbosity: z.enum(["low", "medium", "high"]).optional(),
tools: z.record(z.string(), z.boolean()).optional(),
prompt_append: z.string().optional(),
/** Mark agent as unstable - forces background mode for monitoring. Auto-enabled for gemini/minimax models. */
is_unstable_agent: z.boolean().optional(),
})
export const BuiltinCategoryNameSchema = z.enum([
"visual-engineering",
"ultrabrain",
"deep",
"artistry",
"quick",
"unspecified-low",
"unspecified-high",
"writing",
])
export const CategoriesConfigSchema = z.record(z.string(), CategoryConfigSchema)
export const CommentCheckerConfigSchema = z.object({
/** Custom prompt to replace the default warning message. Use {{comments}} placeholder for detected comments XML. */
custom_prompt: z.string().optional(),
})
export const DynamicContextPruningConfigSchema = z.object({
/** Enable dynamic context pruning (default: false) */
enabled: z.boolean().default(false),
/** Notification level: off, minimal, or detailed (default: detailed) */
notification: z.enum(["off", "minimal", "detailed"]).default("detailed"),
/** Turn protection - prevent pruning recent tool outputs */
turn_protection: z.object({
enabled: z.boolean().default(true),
turns: z.number().min(1).max(10).default(3),
}).optional(),
/** Tools that should never be pruned */
protected_tools: z.array(z.string()).default([
"task", "todowrite", "todoread",
"lsp_rename",
"session_read", "session_write", "session_search",
]),
/** Pruning strategies configuration */
strategies: z.object({
/** Remove duplicate tool calls (same tool + same args) */
deduplication: z.object({
enabled: z.boolean().default(true),
}).optional(),
/** Prune write inputs when file subsequently read */
supersede_writes: z.object({
enabled: z.boolean().default(true),
/** Aggressive mode: prune any write if ANY subsequent read */
aggressive: z.boolean().default(false),
}).optional(),
/** Prune errored tool inputs after N turns */
purge_errors: z.object({
enabled: z.boolean().default(true),
turns: z.number().min(1).max(20).default(5),
}).optional(),
}).optional(),
})
export const ExperimentalConfigSchema = z.object({
aggressive_truncation: z.boolean().optional(),
auto_resume: z.boolean().optional(),
preemptive_compaction: z.boolean().optional(),
/** Truncate all tool outputs, not just whitelisted tools (default: false). Tool output truncator is enabled by default - disable via disabled_hooks. */
truncate_all_tool_outputs: z.boolean().optional(),
/** Dynamic context pruning configuration */
dynamic_context_pruning: DynamicContextPruningConfigSchema.optional(),
/** Enable experimental task system for Todowrite disabler hook */
task_system: z.boolean().optional(),
/** Timeout in ms for loadAllPluginComponents during config handler init (default: 10000, min: 1000) */
plugin_load_timeout_ms: z.number().min(1000).optional(),
/** Wrap hook creation in try/catch to prevent one failing hook from crashing the plugin (default: true at call site) */
safe_hook_creation: z.boolean().optional(),
})
export const SkillSourceSchema = z.union([
z.string(),
z.object({
path: z.string(),
recursive: z.boolean().optional(),
glob: z.string().optional(),
}),
])
export const SkillDefinitionSchema = z.object({
description: z.string().optional(),
template: z.string().optional(),
from: z.string().optional(),
model: z.string().optional(),
agent: z.string().optional(),
subtask: z.boolean().optional(),
"argument-hint": z.string().optional(),
license: z.string().optional(),
compatibility: z.string().optional(),
metadata: z.record(z.string(), z.unknown()).optional(),
"allowed-tools": z.array(z.string()).optional(),
disable: z.boolean().optional(),
})
export const SkillEntrySchema = z.union([
z.boolean(),
SkillDefinitionSchema,
])
export const SkillsConfigSchema = z.union([
z.array(z.string()),
z.record(z.string(), SkillEntrySchema).and(z.object({
sources: z.array(SkillSourceSchema).optional(),
enable: z.array(z.string()).optional(),
disable: z.array(z.string()).optional(),
}).partial()),
])
export const RalphLoopConfigSchema = z.object({
/** Enable ralph loop functionality (default: false - opt-in feature) */
enabled: z.boolean().default(false),
/** Default max iterations if not specified in command (default: 100) */
default_max_iterations: z.number().min(1).max(1000).default(100),
/** Custom state file directory relative to project root (default: .opencode/) */
state_dir: z.string().optional(),
})
export const BackgroundTaskConfigSchema = z.object({
defaultConcurrency: z.number().min(1).optional(),
providerConcurrency: z.record(z.string(), z.number().min(0)).optional(),
modelConcurrency: z.record(z.string(), z.number().min(0)).optional(),
/** Stale timeout in milliseconds - interrupt tasks with no activity for this duration (default: 180000 = 3 minutes, minimum: 60000 = 1 minute) */
staleTimeoutMs: z.number().min(60000).optional(),
})
export const NotificationConfigSchema = z.object({
/** Force enable session-notification even if external notification plugins are detected (default: false) */
force_enable: z.boolean().optional(),
})
export const BabysittingConfigSchema = z.object({
timeout_ms: z.number().default(120000),
})
export const GitMasterConfigSchema = z.object({
/** Add "Ultraworked with Sisyphus" footer to commit messages (default: true). Can be boolean or custom string. */
commit_footer: z.union([z.boolean(), z.string()]).default(true),
/** Add "Co-authored-by: Sisyphus" trailer to commit messages (default: true) */
include_co_authored_by: z.boolean().default(true),
})
export const BrowserAutomationProviderSchema = z.enum(["playwright", "agent-browser", "dev-browser"])
export const BrowserAutomationConfigSchema = z.object({
/**
* Browser automation provider to use for the "playwright" skill.
* - "playwright": Uses Playwright MCP server (@playwright/mcp) - default
* - "agent-browser": Uses Vercel's agent-browser CLI (requires: bun add -g agent-browser)
* - "dev-browser": Uses dev-browser skill with persistent browser state
*/
provider: BrowserAutomationProviderSchema.default("playwright"),
})
export const WebsearchProviderSchema = z.enum(["exa", "tavily"])
export const WebsearchConfigSchema = z.object({
/**
* Websearch provider to use.
* - "exa": Uses Exa websearch (default, works without API key)
* - "tavily": Uses Tavily websearch (requires TAVILY_API_KEY)
*/
provider: WebsearchProviderSchema.optional(),
})
export const TmuxLayoutSchema = z.enum([
'main-horizontal', // main pane top, agent panes bottom stack
'main-vertical', // main pane left, agent panes right stack (default)
'tiled', // all panes same size grid
'even-horizontal', // all panes horizontal row
'even-vertical', // all panes vertical stack
])
export const TmuxConfigSchema = z.object({
enabled: z.boolean().default(false),
layout: TmuxLayoutSchema.default('main-vertical'),
main_pane_size: z.number().min(20).max(80).default(60),
main_pane_min_width: z.number().min(40).default(120),
agent_pane_min_width: z.number().min(20).default(40),
})
export const SisyphusTasksConfigSchema = z.object({
/** Absolute or relative storage path override. When set, bypasses global config dir. */
storage_path: z.string().optional(),
/** Force task list ID (alternative to env ULTRAWORK_TASK_LIST_ID) */
task_list_id: z.string().optional(),
/** Enable Claude Code path compatibility mode */
claude_code_compat: z.boolean().default(false),
})
export const SisyphusConfigSchema = z.object({
tasks: SisyphusTasksConfigSchema.optional(),
})
export const OhMyOpenCodeConfigSchema = z.object({
$schema: z.string().optional(),
/** Enable new task system (default: false) */
new_task_system_enabled: z.boolean().optional(),
/** Default agent name for `oh-my-opencode run` (env: OPENCODE_DEFAULT_AGENT) */
default_run_agent: z.string().optional(),
disabled_mcps: z.array(AnyMcpNameSchema).optional(),
disabled_agents: z.array(BuiltinAgentNameSchema).optional(),
disabled_skills: z.array(BuiltinSkillNameSchema).optional(),
disabled_hooks: z.array(HookNameSchema).optional(),
disabled_commands: z.array(BuiltinCommandNameSchema).optional(),
/** Disable specific tools by name (e.g., ["todowrite", "todoread"]) */
disabled_tools: z.array(z.string()).optional(),
agents: AgentOverridesSchema.optional(),
categories: CategoriesConfigSchema.optional(),
claude_code: ClaudeCodeConfigSchema.optional(),
sisyphus_agent: SisyphusAgentConfigSchema.optional(),
comment_checker: CommentCheckerConfigSchema.optional(),
experimental: ExperimentalConfigSchema.optional(),
auto_update: z.boolean().optional(),
skills: SkillsConfigSchema.optional(),
ralph_loop: RalphLoopConfigSchema.optional(),
background_task: BackgroundTaskConfigSchema.optional(),
notification: NotificationConfigSchema.optional(),
babysitting: BabysittingConfigSchema.optional(),
git_master: GitMasterConfigSchema.optional(),
browser_automation_engine: BrowserAutomationConfigSchema.optional(),
websearch: WebsearchConfigSchema.optional(),
tmux: TmuxConfigSchema.optional(),
sisyphus: SisyphusConfigSchema.optional(),
/** Migration history to prevent re-applying migrations (e.g., model version upgrades) */
_migrations: z.array(z.string()).optional(),
})
export type OhMyOpenCodeConfig = z.infer<typeof OhMyOpenCodeConfigSchema>
export type AgentOverrideConfig = z.infer<typeof AgentOverrideConfigSchema>
export type AgentOverrides = z.infer<typeof AgentOverridesSchema>
export type BackgroundTaskConfig = z.infer<typeof BackgroundTaskConfigSchema>
export type AgentName = z.infer<typeof AgentNameSchema>
export type HookName = z.infer<typeof HookNameSchema>
export type BuiltinCommandName = z.infer<typeof BuiltinCommandNameSchema>
export type BuiltinSkillName = z.infer<typeof BuiltinSkillNameSchema>
export type SisyphusAgentConfig = z.infer<typeof SisyphusAgentConfigSchema>
export type CommentCheckerConfig = z.infer<typeof CommentCheckerConfigSchema>
export type ExperimentalConfig = z.infer<typeof ExperimentalConfigSchema>
export type DynamicContextPruningConfig = z.infer<typeof DynamicContextPruningConfigSchema>
export type SkillsConfig = z.infer<typeof SkillsConfigSchema>
export type SkillDefinition = z.infer<typeof SkillDefinitionSchema>
export type RalphLoopConfig = z.infer<typeof RalphLoopConfigSchema>
export type NotificationConfig = z.infer<typeof NotificationConfigSchema>
export type BabysittingConfig = z.infer<typeof BabysittingConfigSchema>
export type CategoryConfig = z.infer<typeof CategoryConfigSchema>
export type CategoriesConfig = z.infer<typeof CategoriesConfigSchema>
export type BuiltinCategoryName = z.infer<typeof BuiltinCategoryNameSchema>
export type GitMasterConfig = z.infer<typeof GitMasterConfigSchema>
export type BrowserAutomationProvider = z.infer<typeof BrowserAutomationProviderSchema>
export type BrowserAutomationConfig = z.infer<typeof BrowserAutomationConfigSchema>
export type WebsearchProvider = z.infer<typeof WebsearchProviderSchema>
export type WebsearchConfig = z.infer<typeof WebsearchConfigSchema>
export type TmuxConfig = z.infer<typeof TmuxConfigSchema>
export type TmuxLayout = z.infer<typeof TmuxLayoutSchema>
export type SisyphusTasksConfig = z.infer<typeof SisyphusTasksConfigSchema>
export type SisyphusConfig = z.infer<typeof SisyphusConfigSchema>
export * from "./schema/agent-names"
export * from "./schema/agent-overrides"
export * from "./schema/babysitting"
export * from "./schema/background-task"
export * from "./schema/browser-automation"
export * from "./schema/categories"
export * from "./schema/claude-code"
export * from "./schema/comment-checker"
export * from "./schema/commands"
export * from "./schema/dynamic-context-pruning"
export * from "./schema/experimental"
export * from "./schema/git-master"
export * from "./schema/hooks"
export * from "./schema/notification"
export * from "./schema/oh-my-opencode-config"
export * from "./schema/ralph-loop"
export * from "./schema/skills"
export * from "./schema/sisyphus"
export * from "./schema/sisyphus-agent"
export * from "./schema/tmux"
export * from "./schema/websearch"
export { AnyMcpNameSchema, type AnyMcpName, McpNameSchema, type McpName } from "../mcp/types"
+44
View File
@@ -0,0 +1,44 @@
import { z } from "zod"
export const BuiltinAgentNameSchema = z.enum([
"sisyphus",
"hephaestus",
"prometheus",
"oracle",
"librarian",
"explore",
"multimodal-looker",
"metis",
"momus",
"atlas",
])
export const BuiltinSkillNameSchema = z.enum([
"playwright",
"agent-browser",
"dev-browser",
"frontend-ui-ux",
"git-master",
])
export const OverridableAgentNameSchema = z.enum([
"build",
"plan",
"sisyphus",
"hephaestus",
"sisyphus-junior",
"OpenCode-Builder",
"prometheus",
"metis",
"momus",
"oracle",
"librarian",
"explore",
"multimodal-looker",
"atlas",
])
export const AgentNameSchema = BuiltinAgentNameSchema
export type AgentName = z.infer<typeof AgentNameSchema>
export type BuiltinSkillName = z.infer<typeof BuiltinSkillNameSchema>
+60
View File
@@ -0,0 +1,60 @@
import { z } from "zod"
import { AgentPermissionSchema } from "./internal/permission"
export const AgentOverrideConfigSchema = z.object({
/** @deprecated Use `category` instead. Model is inherited from category defaults. */
model: z.string().optional(),
variant: z.string().optional(),
/** Category name to inherit model and other settings from CategoryConfig */
category: z.string().optional(),
/** Skill names to inject into agent prompt */
skills: z.array(z.string()).optional(),
temperature: z.number().min(0).max(2).optional(),
top_p: z.number().min(0).max(1).optional(),
prompt: z.string().optional(),
prompt_append: z.string().optional(),
tools: z.record(z.string(), z.boolean()).optional(),
disable: z.boolean().optional(),
description: z.string().optional(),
mode: z.enum(["subagent", "primary", "all"]).optional(),
color: z
.string()
.regex(/^#[0-9A-Fa-f]{6}$/)
.optional(),
permission: AgentPermissionSchema.optional(),
/** Maximum tokens for response. Passed directly to OpenCode SDK. */
maxTokens: z.number().optional(),
/** Extended thinking configuration (Anthropic). Overrides category and default settings. */
thinking: z
.object({
type: z.enum(["enabled", "disabled"]),
budgetTokens: z.number().optional(),
})
.optional(),
/** Reasoning effort level (OpenAI). Overrides category and default settings. */
reasoningEffort: z.enum(["low", "medium", "high", "xhigh"]).optional(),
/** Text verbosity level. */
textVerbosity: z.enum(["low", "medium", "high"]).optional(),
/** Provider-specific options. Passed directly to OpenCode SDK. */
providerOptions: z.record(z.string(), z.unknown()).optional(),
})
export const AgentOverridesSchema = z.object({
build: AgentOverrideConfigSchema.optional(),
plan: AgentOverrideConfigSchema.optional(),
sisyphus: AgentOverrideConfigSchema.optional(),
hephaestus: AgentOverrideConfigSchema.optional(),
"sisyphus-junior": AgentOverrideConfigSchema.optional(),
"OpenCode-Builder": AgentOverrideConfigSchema.optional(),
prometheus: AgentOverrideConfigSchema.optional(),
metis: AgentOverrideConfigSchema.optional(),
momus: AgentOverrideConfigSchema.optional(),
oracle: AgentOverrideConfigSchema.optional(),
librarian: AgentOverrideConfigSchema.optional(),
explore: AgentOverrideConfigSchema.optional(),
"multimodal-looker": AgentOverrideConfigSchema.optional(),
atlas: AgentOverrideConfigSchema.optional(),
})
export type AgentOverrideConfig = z.infer<typeof AgentOverrideConfigSchema>
export type AgentOverrides = z.infer<typeof AgentOverridesSchema>
+7
View File
@@ -0,0 +1,7 @@
import { z } from "zod"
export const BabysittingConfigSchema = z.object({
timeout_ms: z.number().default(120000),
})
export type BabysittingConfig = z.infer<typeof BabysittingConfigSchema>
+11
View File
@@ -0,0 +1,11 @@
import { z } from "zod"
export const BackgroundTaskConfigSchema = z.object({
defaultConcurrency: z.number().min(1).optional(),
providerConcurrency: z.record(z.string(), z.number().min(0)).optional(),
modelConcurrency: z.record(z.string(), z.number().min(0)).optional(),
/** Stale timeout in milliseconds - interrupt tasks with no activity for this duration (default: 180000 = 3 minutes, minimum: 60000 = 1 minute) */
staleTimeoutMs: z.number().min(60000).optional(),
})
export type BackgroundTaskConfig = z.infer<typeof BackgroundTaskConfigSchema>
+22
View File
@@ -0,0 +1,22 @@
import { z } from "zod"
export const BrowserAutomationProviderSchema = z.enum([
"playwright",
"agent-browser",
"dev-browser",
])
export const BrowserAutomationConfigSchema = z.object({
/**
* Browser automation provider to use for the "playwright" skill.
* - "playwright": Uses Playwright MCP server (@playwright/mcp) - default
* - "agent-browser": Uses Vercel's agent-browser CLI (requires: bun add -g agent-browser)
* - "dev-browser": Uses dev-browser skill with persistent browser state
*/
provider: BrowserAutomationProviderSchema.default("playwright"),
})
export type BrowserAutomationProvider = z.infer<
typeof BrowserAutomationProviderSchema
>
export type BrowserAutomationConfig = z.infer<typeof BrowserAutomationConfigSchema>
+40
View File
@@ -0,0 +1,40 @@
import { z } from "zod"
export const CategoryConfigSchema = z.object({
/** Human-readable description of the category's purpose. Shown in task prompt. */
description: z.string().optional(),
model: z.string().optional(),
variant: z.string().optional(),
temperature: z.number().min(0).max(2).optional(),
top_p: z.number().min(0).max(1).optional(),
maxTokens: z.number().optional(),
thinking: z
.object({
type: z.enum(["enabled", "disabled"]),
budgetTokens: z.number().optional(),
})
.optional(),
reasoningEffort: z.enum(["low", "medium", "high", "xhigh"]).optional(),
textVerbosity: z.enum(["low", "medium", "high"]).optional(),
tools: z.record(z.string(), z.boolean()).optional(),
prompt_append: z.string().optional(),
/** Mark agent as unstable - forces background mode for monitoring. Auto-enabled for gemini/minimax models. */
is_unstable_agent: z.boolean().optional(),
})
export const BuiltinCategoryNameSchema = z.enum([
"visual-engineering",
"ultrabrain",
"deep",
"artistry",
"quick",
"unspecified-low",
"unspecified-high",
"writing",
])
export const CategoriesConfigSchema = z.record(z.string(), CategoryConfigSchema)
export type CategoryConfig = z.infer<typeof CategoryConfigSchema>
export type CategoriesConfig = z.infer<typeof CategoriesConfigSchema>
export type BuiltinCategoryName = z.infer<typeof BuiltinCategoryNameSchema>
+13
View File
@@ -0,0 +1,13 @@
import { z } from "zod"
export const ClaudeCodeConfigSchema = z.object({
mcp: z.boolean().optional(),
commands: z.boolean().optional(),
skills: z.boolean().optional(),
agents: z.boolean().optional(),
hooks: z.boolean().optional(),
plugins: z.boolean().optional(),
plugins_override: z.record(z.string(), z.boolean()).optional(),
})
export type ClaudeCodeConfig = z.infer<typeof ClaudeCodeConfigSchema>
+13
View File
@@ -0,0 +1,13 @@
import { z } from "zod"
export const BuiltinCommandNameSchema = z.enum([
"init-deep",
"ralph-loop",
"ulw-loop",
"cancel-ralph",
"refactor",
"start-work",
"stop-continuation",
])
export type BuiltinCommandName = z.infer<typeof BuiltinCommandNameSchema>
+8
View File
@@ -0,0 +1,8 @@
import { z } from "zod"
export const CommentCheckerConfigSchema = z.object({
/** Custom prompt to replace the default warning message. Use {{comments}} placeholder for detected comments XML. */
custom_prompt: z.string().optional(),
})
export type CommentCheckerConfig = z.infer<typeof CommentCheckerConfigSchema>
@@ -0,0 +1,55 @@
import { z } from "zod"
export const DynamicContextPruningConfigSchema = z.object({
/** Enable dynamic context pruning (default: false) */
enabled: z.boolean().default(false),
/** Notification level: off, minimal, or detailed (default: detailed) */
notification: z.enum(["off", "minimal", "detailed"]).default("detailed"),
/** Turn protection - prevent pruning recent tool outputs */
turn_protection: z
.object({
enabled: z.boolean().default(true),
turns: z.number().min(1).max(10).default(3),
})
.optional(),
/** Tools that should never be pruned */
protected_tools: z.array(z.string()).default([
"task",
"todowrite",
"todoread",
"lsp_rename",
"session_read",
"session_write",
"session_search",
]),
/** Pruning strategies configuration */
strategies: z
.object({
/** Remove duplicate tool calls (same tool + same args) */
deduplication: z
.object({
enabled: z.boolean().default(true),
})
.optional(),
/** Prune write inputs when file subsequently read */
supersede_writes: z
.object({
enabled: z.boolean().default(true),
/** Aggressive mode: prune any write if ANY subsequent read */
aggressive: z.boolean().default(false),
})
.optional(),
/** Prune errored tool inputs after N turns */
purge_errors: z
.object({
enabled: z.boolean().default(true),
turns: z.number().min(1).max(20).default(5),
})
.optional(),
})
.optional(),
})
export type DynamicContextPruningConfig = z.infer<
typeof DynamicContextPruningConfigSchema
>
+20
View File
@@ -0,0 +1,20 @@
import { z } from "zod"
import { DynamicContextPruningConfigSchema } from "./dynamic-context-pruning"
export const ExperimentalConfigSchema = z.object({
aggressive_truncation: z.boolean().optional(),
auto_resume: z.boolean().optional(),
preemptive_compaction: z.boolean().optional(),
/** Truncate all tool outputs, not just whitelisted tools (default: false). Tool output truncator is enabled by default - disable via disabled_hooks. */
truncate_all_tool_outputs: z.boolean().optional(),
/** Dynamic context pruning configuration */
dynamic_context_pruning: DynamicContextPruningConfigSchema.optional(),
/** Enable experimental task system for Todowrite disabler hook */
task_system: z.boolean().optional(),
/** Timeout in ms for loadAllPluginComponents during config handler init (default: 10000, min: 1000) */
plugin_load_timeout_ms: z.number().min(1000).optional(),
/** Wrap hook creation in try/catch to prevent one failing hook from crashing the plugin (default: true at call site) */
safe_hook_creation: z.boolean().optional(),
})
export type ExperimentalConfig = z.infer<typeof ExperimentalConfigSchema>
+10
View File
@@ -0,0 +1,10 @@
import { z } from "zod"
export const GitMasterConfigSchema = z.object({
/** Add "Ultraworked with Sisyphus" footer to commit messages (default: true). Can be boolean or custom string. */
commit_footer: z.union([z.boolean(), z.string()]).default(true),
/** Add "Co-authored-by: Sisyphus" trailer to commit messages (default: true) */
include_co_authored_by: z.boolean().default(true),
})
export type GitMasterConfig = z.infer<typeof GitMasterConfigSchema>
+51
View File
@@ -0,0 +1,51 @@
import { z } from "zod"
export const HookNameSchema = z.enum([
"todo-continuation-enforcer",
"context-window-monitor",
"session-recovery",
"session-notification",
"comment-checker",
"grep-output-truncator",
"tool-output-truncator",
"question-label-truncator",
"directory-agents-injector",
"directory-readme-injector",
"empty-task-response-detector",
"think-mode",
"subagent-question-blocker",
"anthropic-context-window-limit-recovery",
"preemptive-compaction",
"rules-injector",
"background-notification",
"auto-update-checker",
"startup-toast",
"keyword-detector",
"agent-usage-reminder",
"non-interactive-env",
"interactive-bash-session",
"thinking-block-validator",
"ralph-loop",
"category-skill-reminder",
"compaction-context-injector",
"compaction-todo-preserver",
"claude-code-hooks",
"auto-slash-command",
"edit-error-recovery",
"delegate-task-retry",
"prometheus-md-only",
"sisyphus-junior-notepad",
"start-work",
"atlas",
"unstable-agent-babysitter",
"task-reminder",
"task-resume-info",
"stop-continuation-guard",
"tasks-todowrite-disabler",
"write-existing-file-guard",
"anthropic-effort",
])
export type HookName = z.infer<typeof HookNameSchema>
+20
View File
@@ -0,0 +1,20 @@
import { z } from "zod"
export const PermissionValueSchema = z.enum(["ask", "allow", "deny"])
export type PermissionValue = z.infer<typeof PermissionValueSchema>
const BashPermissionSchema = z.union([
PermissionValueSchema,
z.record(z.string(), PermissionValueSchema),
])
export const AgentPermissionSchema = z.object({
edit: PermissionValueSchema.optional(),
bash: BashPermissionSchema.optional(),
webfetch: PermissionValueSchema.optional(),
task: PermissionValueSchema.optional(),
doom_loop: PermissionValueSchema.optional(),
external_directory: PermissionValueSchema.optional(),
})
export type AgentPermission = z.infer<typeof AgentPermissionSchema>
+8
View File
@@ -0,0 +1,8 @@
import { z } from "zod"
export const NotificationConfigSchema = z.object({
/** Force enable session-notification even if external notification plugins are detected (default: false) */
force_enable: z.boolean().optional(),
})
export type NotificationConfig = z.infer<typeof NotificationConfigSchema>
@@ -0,0 +1,57 @@
import { z } from "zod"
import { AnyMcpNameSchema } from "../../mcp/types"
import { BuiltinAgentNameSchema, BuiltinSkillNameSchema } from "./agent-names"
import { AgentOverridesSchema } from "./agent-overrides"
import { BabysittingConfigSchema } from "./babysitting"
import { BackgroundTaskConfigSchema } from "./background-task"
import { BrowserAutomationConfigSchema } from "./browser-automation"
import { CategoriesConfigSchema } from "./categories"
import { ClaudeCodeConfigSchema } from "./claude-code"
import { CommentCheckerConfigSchema } from "./comment-checker"
import { BuiltinCommandNameSchema } from "./commands"
import { ExperimentalConfigSchema } from "./experimental"
import { GitMasterConfigSchema } from "./git-master"
import { HookNameSchema } from "./hooks"
import { NotificationConfigSchema } from "./notification"
import { RalphLoopConfigSchema } from "./ralph-loop"
import { SkillsConfigSchema } from "./skills"
import { SisyphusConfigSchema } from "./sisyphus"
import { SisyphusAgentConfigSchema } from "./sisyphus-agent"
import { TmuxConfigSchema } from "./tmux"
import { WebsearchConfigSchema } from "./websearch"
export const OhMyOpenCodeConfigSchema = z.object({
$schema: z.string().optional(),
/** Enable new task system (default: false) */
new_task_system_enabled: z.boolean().optional(),
/** Default agent name for `oh-my-opencode run` (env: OPENCODE_DEFAULT_AGENT) */
default_run_agent: z.string().optional(),
disabled_mcps: z.array(AnyMcpNameSchema).optional(),
disabled_agents: z.array(BuiltinAgentNameSchema).optional(),
disabled_skills: z.array(BuiltinSkillNameSchema).optional(),
disabled_hooks: z.array(HookNameSchema).optional(),
disabled_commands: z.array(BuiltinCommandNameSchema).optional(),
/** Disable specific tools by name (e.g., ["todowrite", "todoread"]) */
disabled_tools: z.array(z.string()).optional(),
agents: AgentOverridesSchema.optional(),
categories: CategoriesConfigSchema.optional(),
claude_code: ClaudeCodeConfigSchema.optional(),
sisyphus_agent: SisyphusAgentConfigSchema.optional(),
comment_checker: CommentCheckerConfigSchema.optional(),
experimental: ExperimentalConfigSchema.optional(),
auto_update: z.boolean().optional(),
skills: SkillsConfigSchema.optional(),
ralph_loop: RalphLoopConfigSchema.optional(),
background_task: BackgroundTaskConfigSchema.optional(),
notification: NotificationConfigSchema.optional(),
babysitting: BabysittingConfigSchema.optional(),
git_master: GitMasterConfigSchema.optional(),
browser_automation_engine: BrowserAutomationConfigSchema.optional(),
websearch: WebsearchConfigSchema.optional(),
tmux: TmuxConfigSchema.optional(),
sisyphus: SisyphusConfigSchema.optional(),
/** Migration history to prevent re-applying migrations (e.g., model version upgrades) */
_migrations: z.array(z.string()).optional(),
})
export type OhMyOpenCodeConfig = z.infer<typeof OhMyOpenCodeConfigSchema>
+12
View File
@@ -0,0 +1,12 @@
import { z } from "zod"
export const RalphLoopConfigSchema = z.object({
/** Enable ralph loop functionality (default: false - opt-in feature) */
enabled: z.boolean().default(false),
/** Default max iterations if not specified in command (default: 100) */
default_max_iterations: z.number().min(1).max(1000).default(100),
/** Custom state file directory relative to project root (default: .opencode/) */
state_dir: z.string().optional(),
})
export type RalphLoopConfig = z.infer<typeof RalphLoopConfigSchema>
+10
View File
@@ -0,0 +1,10 @@
import { z } from "zod"
export const SisyphusAgentConfigSchema = z.object({
disabled: z.boolean().optional(),
default_builder_enabled: z.boolean().optional(),
planner_enabled: z.boolean().optional(),
replace_plan: z.boolean().optional(),
})
export type SisyphusAgentConfig = z.infer<typeof SisyphusAgentConfigSchema>
+17
View File
@@ -0,0 +1,17 @@
import { z } from "zod"
export const SisyphusTasksConfigSchema = z.object({
/** Absolute or relative storage path override. When set, bypasses global config dir. */
storage_path: z.string().optional(),
/** Force task list ID (alternative to env ULTRAWORK_TASK_LIST_ID) */
task_list_id: z.string().optional(),
/** Enable Claude Code path compatibility mode */
claude_code_compat: z.boolean().default(false),
})
export const SisyphusConfigSchema = z.object({
tasks: SisyphusTasksConfigSchema.optional(),
})
export type SisyphusTasksConfig = z.infer<typeof SisyphusTasksConfigSchema>
export type SisyphusConfig = z.infer<typeof SisyphusConfigSchema>
+45
View File
@@ -0,0 +1,45 @@
import { z } from "zod"
export const SkillSourceSchema = z.union([
z.string(),
z.object({
path: z.string(),
recursive: z.boolean().optional(),
glob: z.string().optional(),
}),
])
export const SkillDefinitionSchema = z.object({
description: z.string().optional(),
template: z.string().optional(),
from: z.string().optional(),
model: z.string().optional(),
agent: z.string().optional(),
subtask: z.boolean().optional(),
"argument-hint": z.string().optional(),
license: z.string().optional(),
compatibility: z.string().optional(),
metadata: z.record(z.string(), z.unknown()).optional(),
"allowed-tools": z.array(z.string()).optional(),
disable: z.boolean().optional(),
})
export const SkillEntrySchema = z.union([z.boolean(), SkillDefinitionSchema])
export const SkillsConfigSchema = z.union([
z.array(z.string()),
z
.record(z.string(), SkillEntrySchema)
.and(
z
.object({
sources: z.array(SkillSourceSchema).optional(),
enable: z.array(z.string()).optional(),
disable: z.array(z.string()).optional(),
})
.partial()
),
])
export type SkillsConfig = z.infer<typeof SkillsConfigSchema>
export type SkillDefinition = z.infer<typeof SkillDefinitionSchema>
+20
View File
@@ -0,0 +1,20 @@
import { z } from "zod"
export const TmuxLayoutSchema = z.enum([
"main-horizontal", // main pane top, agent panes bottom stack
"main-vertical", // main pane left, agent panes right stack (default)
"tiled", // all panes same size grid
"even-horizontal", // all panes horizontal row
"even-vertical", // all panes vertical stack
])
export const TmuxConfigSchema = z.object({
enabled: z.boolean().default(false),
layout: TmuxLayoutSchema.default("main-vertical"),
main_pane_size: z.number().min(20).max(80).default(60),
main_pane_min_width: z.number().min(40).default(120),
agent_pane_min_width: z.number().min(20).default(40),
})
export type TmuxConfig = z.infer<typeof TmuxConfigSchema>
export type TmuxLayout = z.infer<typeof TmuxLayoutSchema>
+15
View File
@@ -0,0 +1,15 @@
import { z } from "zod"
export const WebsearchProviderSchema = z.enum(["exa", "tavily"])
export const WebsearchConfigSchema = z.object({
/**
* Websearch provider to use.
* - "exa": Uses Exa websearch (default, works without API key)
* - "tavily": Uses Tavily websearch (requires TAVILY_API_KEY)
*/
provider: WebsearchProviderSchema.optional(),
})
export type WebsearchProvider = z.infer<typeof WebsearchProviderSchema>
export type WebsearchConfig = z.infer<typeof WebsearchConfigSchema>