diff --git a/README.md b/README.md index c4ebcc2..0c90ec2 100644 --- a/README.md +++ b/README.md @@ -846,6 +846,22 @@ Or disable via `disabled_agents` in `~/.config/opencode/oh-my-opencode.json` or Available agents: `oracle`, `librarian`, `explore`, `frontend-ui-ux-engineer`, `document-writer`, `multimodal-looker` +### Built-in Skills + +Oh My OpenCode includes built-in skills that provide additional capabilities: + +- **playwright**: Browser automation with Playwright MCP. Use for web scraping, testing, screenshots, and browser interactions. + +Disable built-in skills via `disabled_skills` in `~/.config/opencode/oh-my-opencode.json` or `.opencode/oh-my-opencode.json`: + +```json +{ + "disabled_skills": ["playwright"] +} +``` + +Available built-in skills: `playwright` + ### Sisyphus Agent When enabled (default), Sisyphus provides a powerful orchestrator with optional specialized agents: diff --git a/assets/oh-my-opencode.schema.json b/assets/oh-my-opencode.schema.json index 10ee6a1..41c9c48 100644 --- a/assets/oh-my-opencode.schema.json +++ b/assets/oh-my-opencode.schema.json @@ -34,6 +34,15 @@ ] } }, + "disabled_skills": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "playwright" + ] + } + }, "disabled_hooks": { "type": "array", "items": { diff --git a/src/config/schema.ts b/src/config/schema.ts index 1a8b14d..fa85b31 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -26,6 +26,10 @@ export const BuiltinAgentNameSchema = z.enum([ "multimodal-looker", ]) +export const BuiltinSkillNameSchema = z.enum([ + "playwright", +]) + export const OverridableAgentNameSchema = z.enum([ "build", "plan", @@ -231,6 +235,7 @@ export const OhMyOpenCodeConfigSchema = z.object({ $schema: z.string().optional(), disabled_mcps: z.array(McpNameSchema).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(), agents: AgentOverridesSchema.optional(), @@ -250,6 +255,7 @@ export type AgentOverrides = z.infer export type AgentName = z.infer export type HookName = z.infer export type BuiltinCommandName = z.infer +export type BuiltinSkillName = z.infer export type SisyphusAgentConfig = z.infer export type CommentCheckerConfig = z.infer export type ExperimentalConfig = z.infer diff --git a/src/features/builtin-skills/skills.ts b/src/features/builtin-skills/skills.ts index c29d212..75deae7 100644 --- a/src/features/builtin-skills/skills.ts +++ b/src/features/builtin-skills/skills.ts @@ -1,5 +1,19 @@ import type { BuiltinSkill } from "./types" -export function createBuiltinSkills(): BuiltinSkill[] { - return [] +const playwrightSkill: BuiltinSkill = { + name: "playwright", + description: "Browser automation with Playwright MCP. Use for web scraping, testing, screenshots, and browser interactions.", + template: `# Playwright Browser Automation + +This skill provides browser automation capabilities via the Playwright MCP server.`, + mcpConfig: { + playwright: { + command: "npx", + args: ["@playwright/mcp@latest"], + }, + }, +} + +export function createBuiltinSkills(): BuiltinSkill[] { + return [playwrightSkill] } diff --git a/src/features/builtin-skills/types.ts b/src/features/builtin-skills/types.ts index 095e846..7adc0f9 100644 --- a/src/features/builtin-skills/types.ts +++ b/src/features/builtin-skills/types.ts @@ -1,3 +1,5 @@ +import type { SkillMcpConfig } from "../skill-mcp-manager/types" + export interface BuiltinSkill { name: string description: string @@ -10,4 +12,5 @@ export interface BuiltinSkill { model?: string subtask?: boolean argumentHint?: string + mcpConfig?: SkillMcpConfig } diff --git a/src/features/opencode-skill-loader/merger.ts b/src/features/opencode-skill-loader/merger.ts index bc166e4..07755d7 100644 --- a/src/features/opencode-skill-loader/merger.ts +++ b/src/features/opencode-skill-loader/merger.ts @@ -21,7 +21,7 @@ const SCOPE_PRIORITY: Record = { function builtinToLoaded(builtin: BuiltinSkill): LoadedSkill { const definition: CommandDefinition = { name: builtin.name, - description: `(builtin - Skill) ${builtin.description}`, + description: `(opencode - Skill) ${builtin.description}`, template: builtin.template, model: builtin.model, agent: builtin.agent, @@ -37,6 +37,7 @@ function builtinToLoaded(builtin: BuiltinSkill): LoadedSkill { compatibility: builtin.compatibility, metadata: builtin.metadata as Record | undefined, allowedTools: builtin.allowedTools, + mcpConfig: builtin.mcpConfig, } } diff --git a/src/index.ts b/src/index.ts index 4d0c1e7..f16c63d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -128,6 +128,12 @@ function mergeConfigs( ...(override.disabled_commands ?? []), ]), ], + disabled_skills: [ + ...new Set([ + ...(base.disabled_skills ?? []), + ...(override.disabled_skills ?? []), + ]), + ], claude_code: deepMerge(base.claude_code, override.claude_code), }; } @@ -283,7 +289,10 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => { const callOmoAgent = createCallOmoAgent(ctx, backgroundManager); const lookAt = createLookAt(ctx); - const builtinSkills = createBuiltinSkills(); + const disabledSkills = new Set(pluginConfig.disabled_skills ?? []); + const builtinSkills = createBuiltinSkills().filter( + (skill) => !disabledSkills.has(skill.name as any) + ); const includeClaudeSkills = pluginConfig.claude_code?.skills !== false; const mergedSkills = mergeSkills( builtinSkills,