From 27f8feda042fdd3fc940bbeebac7db9d64469521 Mon Sep 17 00:00:00 2001 From: Jonas Herrmansdsoerfer Date: Fri, 13 Feb 2026 11:15:38 +0100 Subject: [PATCH] feat(browser-automation): add playwright-cli as browser automation provider - Add playwright-cli to BrowserAutomationProviderSchema enum - Add playwright-cli to BuiltinSkillNameSchema - Create playwrightCliSkill with official Microsoft template - Update skill selection logic to handle 3 providers - Add comprehensive tests for schema and skill selection - Regenerate JSON schema Closes # --- src/config/schema.test.ts | 35 +++ src/config/schema/browser-automation.ts | 2 + src/features/builtin-skills/skills.test.ts | 31 ++ src/features/builtin-skills/skills.ts | 10 +- src/features/builtin-skills/skills/index.ts | 1 + .../builtin-skills/skills/playwright-cli.ts | 268 ++++++++++++++++++ 6 files changed, 346 insertions(+), 1 deletion(-) create mode 100644 src/features/builtin-skills/skills/playwright-cli.ts diff --git a/src/config/schema.test.ts b/src/config/schema.test.ts index 2d151ec53..2efccaa37 100644 --- a/src/config/schema.test.ts +++ b/src/config/schema.test.ts @@ -553,6 +553,18 @@ describe("BrowserAutomationProviderSchema", () => { // then expect(result.success).toBe(false) }) + + test("accepts 'playwright-cli' as valid provider", () => { + // given + const input = "playwright-cli" + + // when + const result = BrowserAutomationProviderSchema.safeParse(input) + + // then + expect(result.success).toBe(true) + expect(result.data).toBe("playwright-cli") + }) }) describe("BrowserAutomationConfigSchema", () => { @@ -577,6 +589,17 @@ describe("BrowserAutomationConfigSchema", () => { // then expect(result.provider).toBe("agent-browser") }) + + test("accepts playwright-cli provider in config", () => { + // given + const input = { provider: "playwright-cli" } + + // when + const result = BrowserAutomationConfigSchema.parse(input) + + // then + expect(result.provider).toBe("playwright-cli") + }) }) describe("OhMyOpenCodeConfigSchema - browser_automation_engine", () => { @@ -607,6 +630,18 @@ describe("OhMyOpenCodeConfigSchema - browser_automation_engine", () => { expect(result.success).toBe(true) expect(result.data?.browser_automation_engine).toBeUndefined() }) + + test("accepts browser_automation_engine with playwright-cli", () => { + // given + const input = { browser_automation_engine: { provider: "playwright-cli" } } + + // when + const result = OhMyOpenCodeConfigSchema.safeParse(input) + + // then + expect(result.success).toBe(true) + expect(result.data?.browser_automation_engine?.provider).toBe("playwright-cli") + }) }) describe("ExperimentalConfigSchema feature flags", () => { diff --git a/src/config/schema/browser-automation.ts b/src/config/schema/browser-automation.ts index 294dcb965..f07edd4b0 100644 --- a/src/config/schema/browser-automation.ts +++ b/src/config/schema/browser-automation.ts @@ -4,6 +4,7 @@ export const BrowserAutomationProviderSchema = z.enum([ "playwright", "agent-browser", "dev-browser", + "playwright-cli", ]) export const BrowserAutomationConfigSchema = z.object({ @@ -12,6 +13,7 @@ export const BrowserAutomationConfigSchema = z.object({ * - "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 + * - "playwright-cli": Uses Playwright CLI (@playwright/cli) - token-efficient CLI alternative */ provider: BrowserAutomationProviderSchema.default("playwright"), }) diff --git a/src/features/builtin-skills/skills.test.ts b/src/features/builtin-skills/skills.test.ts index 33f0cb56f..59a4198d1 100644 --- a/src/features/builtin-skills/skills.test.ts +++ b/src/features/builtin-skills/skills.test.ts @@ -140,4 +140,35 @@ describe("createBuiltinSkills", () => { // #then expect(skills.length).toBe(4) }) + + test("returns playwright-cli skill when browserProvider is 'playwright-cli'", () => { + // given + const options = { browserProvider: "playwright-cli" as const } + + // when + const skills = createBuiltinSkills(options) + + // then + const playwrightSkill = skills.find((s) => s.name === "playwright") + const agentBrowserSkill = skills.find((s) => s.name === "agent-browser") + expect(playwrightSkill).toBeDefined() + expect(playwrightSkill!.description).toContain("browser") + expect(playwrightSkill!.allowedTools).toContain("Bash(playwright-cli:*)") + expect(playwrightSkill!.mcpConfig).toBeUndefined() + expect(agentBrowserSkill).toBeUndefined() + }) + + test("playwright-cli skill template contains CLI commands", () => { + // given + const options = { browserProvider: "playwright-cli" as const } + + // when + const skills = createBuiltinSkills(options) + const skill = skills.find((s) => s.name === "playwright") + + // then + expect(skill!.template).toContain("playwright-cli open") + expect(skill!.template).toContain("playwright-cli snapshot") + expect(skill!.template).toContain("playwright-cli click") + }) }) diff --git a/src/features/builtin-skills/skills.ts b/src/features/builtin-skills/skills.ts index 2f872698f..d0405f600 100644 --- a/src/features/builtin-skills/skills.ts +++ b/src/features/builtin-skills/skills.ts @@ -4,6 +4,7 @@ import type { BrowserAutomationProvider } from "../../config/schema" import { playwrightSkill, agentBrowserSkill, + playwrightCliSkill, frontendUiUxSkill, gitMasterSkill, devBrowserSkill, @@ -17,7 +18,14 @@ export interface CreateBuiltinSkillsOptions { export function createBuiltinSkills(options: CreateBuiltinSkillsOptions = {}): BuiltinSkill[] { const { browserProvider = "playwright", disabledSkills } = options - const browserSkill = browserProvider === "agent-browser" ? agentBrowserSkill : playwrightSkill + let browserSkill: BuiltinSkill + if (browserProvider === "agent-browser") { + browserSkill = agentBrowserSkill + } else if (browserProvider === "playwright-cli") { + browserSkill = playwrightCliSkill + } else { + browserSkill = playwrightSkill + } const skills = [browserSkill, frontendUiUxSkill, gitMasterSkill, devBrowserSkill] diff --git a/src/features/builtin-skills/skills/index.ts b/src/features/builtin-skills/skills/index.ts index fdd79d253..073930865 100644 --- a/src/features/builtin-skills/skills/index.ts +++ b/src/features/builtin-skills/skills/index.ts @@ -1,4 +1,5 @@ export { playwrightSkill, agentBrowserSkill } from "./playwright" +export { playwrightCliSkill } from "./playwright-cli" export { frontendUiUxSkill } from "./frontend-ui-ux" export { gitMasterSkill } from "./git-master" export { devBrowserSkill } from "./dev-browser" diff --git a/src/features/builtin-skills/skills/playwright-cli.ts b/src/features/builtin-skills/skills/playwright-cli.ts new file mode 100644 index 000000000..728ae380e --- /dev/null +++ b/src/features/builtin-skills/skills/playwright-cli.ts @@ -0,0 +1,268 @@ +import type { BuiltinSkill } from "../types" + +/** + * Playwright CLI skill — token-efficient CLI alternative to the MCP-based playwright skill. + * + * Uses name "playwright" (not "playwright-cli") because agents hardcode "playwright" as the + * canonical browser skill name. The browserProvider config swaps the implementation behind + * the same name: "playwright" gives MCP, "playwright-cli" gives this CLI variant. + * The binary is still called `playwright-cli` (see allowedTools). + */ +export const playwrightCliSkill: BuiltinSkill = { + name: "playwright", + description: "MUST USE for any browser-related tasks. Browser automation via playwright-cli - verification, browsing, information gathering, web scraping, testing, screenshots, and all browser interactions.", + template: `# Browser Automation with playwright-cli + +## Quick start + +\`\`\`bash +# open new browser +playwright-cli open +# navigate to a page +playwright-cli goto https://playwright.dev +# interact with the page using refs from the snapshot +playwright-cli click e15 +playwright-cli type "page.click" +playwright-cli press Enter +# take a screenshot +playwright-cli screenshot +# close the browser +playwright-cli close +\`\`\` + +## Commands + +### Core + +\`\`\`bash +playwright-cli open +# open and navigate right away +playwright-cli open https://example.com/ +playwright-cli goto https://playwright.dev +playwright-cli type "search query" +playwright-cli click e3 +playwright-cli dblclick e7 +playwright-cli fill e5 "user@example.com" +playwright-cli drag e2 e8 +playwright-cli hover e4 +playwright-cli select e9 "option-value" +playwright-cli upload ./document.pdf +playwright-cli check e12 +playwright-cli uncheck e12 +playwright-cli snapshot +playwright-cli snapshot --filename=after-click.yaml +playwright-cli eval "document.title" +playwright-cli eval "el => el.textContent" e5 +playwright-cli dialog-accept +playwright-cli dialog-accept "confirmation text" +playwright-cli dialog-dismiss +playwright-cli resize 1920 1080 +playwright-cli close +\`\`\` + +### Navigation + +\`\`\`bash +playwright-cli go-back +playwright-cli go-forward +playwright-cli reload +\`\`\` + +### Keyboard + +\`\`\`bash +playwright-cli press Enter +playwright-cli press ArrowDown +playwright-cli keydown Shift +playwright-cli keyup Shift +\`\`\` + +### Mouse + +\`\`\`bash +playwright-cli mousemove 150 300 +playwright-cli mousedown +playwright-cli mousedown right +playwright-cli mouseup +playwright-cli mouseup right +playwright-cli mousewheel 0 100 +\`\`\` + +### Save as + +\`\`\`bash +playwright-cli screenshot +playwright-cli screenshot e5 +playwright-cli screenshot --filename=page.png +playwright-cli pdf --filename=page.pdf +\`\`\` + +### Tabs + +\`\`\`bash +playwright-cli tab-list +playwright-cli tab-new +playwright-cli tab-new https://example.com/page +playwright-cli tab-close +playwright-cli tab-close 2 +playwright-cli tab-select 0 +\`\`\` + +### Storage + +\`\`\`bash +playwright-cli state-save +playwright-cli state-save auth.json +playwright-cli state-load auth.json + +# Cookies +playwright-cli cookie-list +playwright-cli cookie-list --domain=example.com +playwright-cli cookie-get session_id +playwright-cli cookie-set session_id abc123 +playwright-cli cookie-set session_id abc123 --domain=example.com --httpOnly --secure +playwright-cli cookie-delete session_id +playwright-cli cookie-clear + +# LocalStorage +playwright-cli localstorage-list +playwright-cli localstorage-get theme +playwright-cli localstorage-set theme dark +playwright-cli localstorage-delete theme +playwright-cli localstorage-clear + +# SessionStorage +playwright-cli sessionstorage-list +playwright-cli sessionstorage-get step +playwright-cli sessionstorage-set step 3 +playwright-cli sessionstorage-delete step +playwright-cli sessionstorage-clear +\`\`\` + +### Network + +\`\`\`bash +playwright-cli route "**/*.jpg" --status=404 +playwright-cli route "https://api.example.com/**" --body='{"mock": true}' +playwright-cli route-list +playwright-cli unroute "**/*.jpg" +playwright-cli unroute +\`\`\` + +### DevTools + +\`\`\`bash +playwright-cli console +playwright-cli console warning +playwright-cli network +playwright-cli run-code "async page => await page.context().grantPermissions(['geolocation'])" +playwright-cli tracing-start +playwright-cli tracing-stop +playwright-cli video-start +playwright-cli video-stop video.webm +\`\`\` + +### Install + +\`\`\`bash +playwright-cli install --skills +playwright-cli install-browser +\`\`\` + +### Configuration +\`\`\`bash +# Use specific browser when creating session +playwright-cli open --browser=chrome +playwright-cli open --browser=firefox +playwright-cli open --browser=webkit +playwright-cli open --browser=msedge +# Connect to browser via extension +playwright-cli open --extension + +# Use persistent profile (by default profile is in-memory) +playwright-cli open --persistent +# Use persistent profile with custom directory +playwright-cli open --profile=/path/to/profile + +# Start with config file +playwright-cli open --config=my-config.json + +# Close the browser +playwright-cli close +# Delete user data for the default session +playwright-cli delete-data +\`\`\` + +### Browser Sessions + +\`\`\`bash +# create new browser session named "mysession" with persistent profile +playwright-cli -s=mysession open example.com --persistent +# same with manually specified profile directory (use when requested explicitly) +playwright-cli -s=mysession open example.com --profile=/path/to/profile +playwright-cli -s=mysession click e6 +playwright-cli -s=mysession close # stop a named browser +playwright-cli -s=mysession delete-data # delete user data for persistent session + +playwright-cli list +# Close all browsers +playwright-cli close-all +# Forcefully kill all browser processes +playwright-cli kill-all +\`\`\` + +## Example: Form submission + +\`\`\`bash +playwright-cli open https://example.com/form +playwright-cli snapshot + +playwright-cli fill e1 "user@example.com" +playwright-cli fill e2 "password123" +playwright-cli click e3 +playwright-cli snapshot +playwright-cli close +\`\`\` + +## Example: Multi-tab workflow + +\`\`\`bash +playwright-cli open https://example.com +playwright-cli tab-new https://example.com/other +playwright-cli tab-list +playwright-cli tab-select 0 +playwright-cli snapshot +playwright-cli close +\`\`\` + +## Example: Debugging with DevTools + +\`\`\`bash +playwright-cli open https://example.com +playwright-cli click e4 +playwright-cli fill e7 "test" +playwright-cli console +playwright-cli network +playwright-cli close +\`\`\` + +\`\`\`bash +playwright-cli open https://example.com +playwright-cli tracing-start +playwright-cli click e4 +playwright-cli fill e7 "test" +playwright-cli tracing-stop +playwright-cli close +\`\`\` + +## Specific tasks + +* **Request mocking** [references/request-mocking.md](references/request-mocking.md) +* **Running Playwright code** [references/running-code.md](references/running-code.md) +* **Browser session management** [references/session-management.md](references/session-management.md) +* **Storage state (cookies, localStorage)** [references/storage-state.md](references/storage-state.md) +* **Test generation** [references/test-generation.md](references/test-generation.md) +* **Tracing** [references/tracing.md](references/tracing.md) +* **Video recording** [references/video-recording.md](references/video-recording.md)`, + allowedTools: ["Bash(playwright-cli:*)"], +}