playwright

MUST USE for any browser-related tasks. Browser automation via agent-browser CLI - verification, browsing, information gathering, web scraping, testing, screenshots, and all browser interactions. (playwright - oh-my-opencode port)

5 stars

Best use case

playwright is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

MUST USE for any browser-related tasks. Browser automation via agent-browser CLI - verification, browsing, information gathering, web scraping, testing, screenshots, and all browser interactions. (playwright - oh-my-opencode port)

Teams using playwright should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/playwright/SKILL.md --create-dirs "https://raw.githubusercontent.com/Lee-SiHyeon/oh-my-copilot/main/.github/skills/playwright/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/playwright/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How playwright Compares

Feature / AgentplaywrightStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

MUST USE for any browser-related tasks. Browser automation via agent-browser CLI - verification, browsing, information gathering, web scraping, testing, screenshots, and all browser interactions. (playwright - oh-my-opencode port)

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Browser Automation with agent-browser

## Quick Start

```bash
agent-browser open <url>        # Navigate to page
agent-browser snapshot -i       # Get interactive elements with refs
agent-browser click @e1         # Click element by ref
agent-browser fill @e2 "text"   # Fill input by ref
agent-browser close             # Close browser
```

## Core Workflow

1. Navigate: `agent-browser open <url>`
2. Snapshot: `agent-browser snapshot -i` (returns elements with refs like `@e1`, `@e2`)
3. Interact using refs from the snapshot
4. Re-snapshot after navigation or significant DOM changes

---

## Commands

### Navigation
```bash
agent-browser open <url>
agent-browser back
agent-browser forward
agent-browser reload
agent-browser close
```

### Snapshot
```bash
agent-browser snapshot            # Full accessibility tree
agent-browser snapshot -i         # Interactive elements only (recommended)
agent-browser snapshot -c         # Compact output
agent-browser snapshot -d 3       # Limit depth to 3
agent-browser snapshot -s "#main" # Scope to CSS selector
```

### Interactions (use @refs from snapshot)
```bash
agent-browser click @e1
agent-browser dblclick @e1
agent-browser fill @e2 "text"     # Clear and type
agent-browser type @e2 "text"     # Type without clearing
agent-browser press Enter
agent-browser press Control+a
agent-browser hover @e1
agent-browser check @e1
agent-browser select @e1 "value"
agent-browser scroll down 500
agent-browser drag @e1 @e2
agent-browser upload @e1 file.pdf
```

### Get Information
```bash
agent-browser get text @e1
agent-browser get html @e1
agent-browser get value @e1
agent-browser get attr @e1 href
agent-browser get title
agent-browser get url
agent-browser get count ".item"
```

### Screenshots & PDF
```bash
agent-browser screenshot
agent-browser screenshot path.png
agent-browser screenshot --full
agent-browser pdf output.pdf
```

### Wait
```bash
agent-browser wait @e1
agent-browser wait 2000
agent-browser wait --text "Success"
agent-browser wait --url "**/dashboard"
agent-browser wait --load networkidle
```

### Cookies & Storage
```bash
agent-browser cookies
agent-browser cookies set name value
agent-browser cookies clear
agent-browser storage local
agent-browser storage local set k v
```

### Network
```bash
agent-browser network route <url>
agent-browser network route <url> --abort
agent-browser network route <url> --body '{}'
agent-browser network requests
```

### Tabs
```bash
agent-browser tab
agent-browser tab new [url]
agent-browser tab 2
agent-browser tab close
```

### JavaScript
```bash
agent-browser eval "document.title"
```

---

## Global Options

| Option | Description |
|--------|-------------|
| `--session <name>` | Isolated browser session |
| `--profile <path>` | Persistent browser profile |
| `--headed` | Show browser window |
| `--cdp <port>` | Connect via Chrome DevTools Protocol |
| `--json` | Machine-readable JSON output |

---

## Example: Form Submission

```bash
agent-browser open https://example.com/form
agent-browser snapshot -i
# Shows: textbox "Email" [ref=e1], textbox "Password" [ref=e2], button "Submit" [ref=e3]

agent-browser fill @e1 "user@example.com"
agent-browser fill @e2 "password123"
agent-browser click @e3
agent-browser wait --load networkidle
agent-browser snapshot -i  # Check result
```

## Example: Authentication with Saved State

```bash
# Login once
agent-browser open https://app.example.com/login
agent-browser fill @e1 "username"
agent-browser fill @e2 "password"
agent-browser click @e3
agent-browser wait --url "**/dashboard"
agent-browser state save auth.json

# Later sessions
agent-browser state load auth.json
agent-browser open https://app.example.com/dashboard
```

---

Install: `bun add -g agent-browser && agent-browser install`

---

## Advanced: Google OAuth Stealth Login

### 왜 일반 Playwright가 Google에 의해 차단되는가

Google은 자동화 브라우저를 여러 신호로 감지하여 로그인을 차단한다:

| 감지 신호 | 일반 Playwright 값 | 실제 Chrome 값 |
|-----------|-------------------|---------------|
| `navigator.webdriver` | `true` | `undefined` |
| `window.chrome` | `undefined` | `{ runtime: {}, ... }` |
| 타이핑 속도 | 즉시 (0ms) | 30~100ms 딜레이 |
| User-Agent | headless 문자열 포함 | 일반 Chrome UA |
| Permission API | 자동화 시그니처 | 정상 응답 |

이 중 하나라도 감지되면 "이 브라우저 또는 앱은 안전하지 않을 수 있습니다" 오류로 로그인이 차단된다.

---

### 3가지 우회 기법

#### 기법 1: `navigator.webdriver` 숨기기 (CDP via Page.addScriptToEvaluateOnNewDocument)

가장 중요한 우회 기법. 페이지 로드 전에 JS를 주입하여 webdriver 플래그를 제거한다.

```python
await page.add_init_script("""
    Object.defineProperty(navigator, 'webdriver', {
        get: () => undefined,
    });
""")
```

> ⚠️ `evaluate()`로는 부족하다 — 페이지 로드 후 실행되어 이미 감지된 이후다.
> `add_init_script()`는 모든 페이지·프레임 로드 **이전**에 주입된다.

#### 기법 2: `window.chrome` 주입

Google 로그인 페이지는 `window.chrome` 객체의 존재 여부로 실제 Chrome인지 확인한다.

```python
await page.add_init_script("""
    window.chrome = {
        runtime: {
            connect: () => {},
            sendMessage: () => {},
        },
    };
""")
```

#### 기법 3: Human-like 타이핑 (slow typing with delay)

키 입력 속도가 0ms이면 봇으로 감지된다. `type()`에 `delay` 옵션을 주어 사람처럼 타이핑한다.

```python
# fill() 대신 반드시 type() + delay 사용
await page.locator('input[type="email"]').type("user@example.com", delay=50)
await page.locator('input[type="password"]').type("password", delay=50)
```

---

### 실제 검증된 Python 스크립트 예시

아래는 `notebooklm.google.com` Google OAuth 로그인에 **실제 성공**한 패턴이다 (pseudocode 수준):

```python
import asyncio
import json
from pathlib import Path
from playwright.async_api import async_playwright

COOKIE_DIR = Path.home() / ".notebooklm-mcp-cli" / "profiles" / "default"
COOKIE_FILE = COOKIE_DIR / "cookies.json"

STEALTH_SCRIPTS = [
    # 1. webdriver 플래그 제거
    "Object.defineProperty(navigator, 'webdriver', { get: () => undefined });",
    # 2. window.chrome 주입
    "window.chrome = { runtime: { connect: ()=>{}, sendMessage: ()=>{} } };",
    # 3. permissions API 정상화
    """
    const originalQuery = window.navigator.permissions.query;
    window.navigator.permissions.query = (parameters) =>
        parameters.name === 'notifications'
            ? Promise.resolve({ state: Notification.permission })
            : originalQuery(parameters);
    """,
    # 4. plugins 배열 fake (비어있으면 headless로 감지)
    "Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3] });",
]

async def google_stealth_login(email: str, password: str) -> bool:
    async with async_playwright() as p:
        browser = await p.chromium.launch(
            headless=False,  # Google은 headless 모드를 감지하므로 headed 권장
            args=["--disable-blink-features=AutomationControlled"],
        )
        context = await browser.new_context(
            user_agent=(
                "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
                "AppleWebKit/537.36 (KHTML, like Gecko) "
                "Chrome/131.0.0.0 Safari/537.36"
            ),
        )
        page = await context.new_page()

        # 모든 stealth 스크립트를 페이지 로드 전에 주입
        for script in STEALTH_SCRIPTS:
            await page.add_init_script(script)

        # Google 로그인 시작
        await page.goto("https://accounts.google.com/signin")
        await page.wait_for_load_state("networkidle")

        # 이메일 입력 — slow typing 필수
        await page.locator('input[type="email"]').type(email, delay=50)
        await page.locator("#identifierNext").click()
        await page.wait_for_timeout(2000)  # Google 서버 응답 대기

        # 비밀번호 입력 — slow typing 필수
        await page.locator('input[type="password"]').type(password, delay=50)
        await page.locator("#passwordNext").click()
        await page.wait_for_url("**/myaccount**", timeout=15000)

        # NotebookLM으로 이동하여 쿠키 수집
        await page.goto("https://notebooklm.google.com")
        await page.wait_for_load_state("networkidle")

        # 쿠키 저장
        cookies = await context.cookies()
        COOKIE_DIR.mkdir(parents=True, exist_ok=True)
        COOKIE_FILE.write_text(json.dumps(cookies, indent=2))
        print(f"✓ 쿠키 {len(cookies)}개 저장 → {COOKIE_FILE}")

        await browser.close()
        return True

asyncio.run(google_stealth_login("shyeon0528@gmail.com", "YOUR_PASSWORD"))
```

> **실제 성공 케이스**: 위 패턴으로 `notebooklm.google.com` 로그인 성공,
> 쿠키 **27개** 저장 완료.

---

### `playwright-stealth` 라이브러리 방식 (대안)

위 수동 주입 대신 `playwright-stealth` 패키지를 사용할 수 있다:

```bash
pip install playwright-stealth
```

```python
from playwright_stealth import stealth_async

async with async_playwright() as p:
    browser = await p.chromium.launch(headless=False)
    page = await browser.new_page()
    await stealth_async(page)  # 한 줄로 모든 stealth 적용
    await page.goto("https://accounts.google.com/signin")
    # ... 이후 동일
```

> `playwright-stealth`는 내부적으로 위의 기법들을 포함한 20+ 개의 패치를 적용한다.
> 단순 사용에는 편리하나, 실패 시 수동 방식으로 개별 디버깅이 필요하다.

---

### CDP + 기존 Chrome 세션 연결 방식 (주의: 정책 차단됨)

```bash
# Chrome을 디버그 포트로 실행
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
    --remote-debugging-port=9222 --user-data-dir=/tmp/chrome-debug
```

```python
# CDP로 기존 세션에 연결
browser = await p.chromium.connect_over_cdp("http://localhost:9222")
```

> ⚠️ **실패 사례**: Google은 2024년부터 CDP 연결 자체를 감지하여
> 기업 정책(Enterprise policy)으로 차단하는 경우가 있다.
> 이미 로그인된 프로필을 재사용할 때는 동작하나, 신규 로그인에는 비추천.

---

### nlm 쿠키 저장 경로 및 검증

#### 저장 경로

```
~/.notebooklm-mcp-cli/profiles/default/cookies.json
```

#### 쿠키 파일 구조

```json
[
  {
    "name": "__Secure-1PSID",
    "value": "...",
    "domain": ".google.com",
    "path": "/",
    "expires": 1234567890,
    "httpOnly": true,
    "secure": true,
    "sameSite": "None"
  },
  ...
]
```

#### 쿠키 유효성 검증 방법

```bash
# 방법 1: nlm CLI 직접 확인 (권장)
nlm login --check
# 성공 시: ✓ Authentication valid! Notebooks found: 20

# 방법 2: 핵심 쿠키 존재 여부 확인
python3 -c "
import json
from pathlib import Path
cookies = json.loads(Path('~/.notebooklm-mcp-cli/profiles/default/cookies.json').expanduser().read_text())
names = {c['name'] for c in cookies}
required = {'__Secure-1PSID', '__Secure-3PSID', 'SID'}
missing = required - names
print('✓ OK' if not missing else f'✗ Missing: {missing}')
print(f'  총 쿠키: {len(cookies)}개')
"

# 방법 3: 만료 시각 확인
python3 -c "
import json, time
from pathlib import Path
cookies = json.loads(Path('~/.notebooklm-mcp-cli/profiles/default/cookies.json').expanduser().read_text())
expired = [c['name'] for c in cookies if c.get('expires', 0) > 0 and c['expires'] < time.time()]
print(f'만료된 쿠키: {expired if expired else \"없음\"}')
"
```

#### 쿠키 만료 시 재로그인

Google 세션 쿠키는 보통 **1년** 유효하나, 비활성 또는 보안 이벤트 시 무효화된다.
`nlm login --check` 실패 시 위 stealth 로그인 스크립트를 재실행하면 된다.

Related Skills

ultrawork

5
from Lee-SiHyeon/oh-my-copilot

원커맨드 풀 오케스트레이션. Sisyphus + Hephaestus + Prometheus가 모두 활성화됩니다. "ultrawork", "ulw", "ulw-loop", "다 해줘", "전부 해줘" 트리거로 사용합니다. oh-my-opencode의 ultrawork를 Copilot CLI에 포팅한 스킬입니다.

trace

5
from Lee-SiHyeon/oh-my-copilot

경쟁 가설 기반 evidence-driven 디버깅. 애매한 버그, 인과관계 추적, 성능 문제, 2회 이상 재현 실패한 버그에 사용합니다. "trace", "왜 이게", "원인 분석", "debugging", "버그 추적", "root cause", "원인을 모르겠어", "재현이 안 돼" 트리거로 사용합니다. oh-my-claudecode의 trace 스킬 패턴을 Copilot CLI에 포팅한 스킬입니다.

sisyphus

5
from Lee-SiHyeon/oh-my-copilot

메인 오케스트레이터 에이전트. 복잡한 태스크를 원자적 서브태스크로 분해하고 병렬로 실행합니다. "오케스트레이션", "태스크 분해", "sisyphus" 트리거. ultrawork 내부에서도 자동 활성화됩니다.

sisyphus-junior

5
from Lee-SiHyeon/oh-my-copilot

Focused task executor. Completes assigned tasks directly with todo tracking discipline. Use when Atlas delegates atomic work items. (Sisyphus-Junior - oh-my-opencode port)

setup

5
from Lee-SiHyeon/oh-my-copilot

Configure shell profile so that `copilot`, `atlas`, and `cop` commands always launch with --agent oh-my-copilot:atlas --autopilot. Run this once after installing oh-my-copilot.

ralph-loop

5
from Lee-SiHyeon/oh-my-copilot

자기교정 반복 루프. 완료까지 자동으로 반복 실행합니다. "루프", "완료까지", "계속 해줘", "ralph-loop", "ulw-loop" 트리거로 사용합니다. oh-my-opencode의 Ralph Loop를 Copilot CLI에 포팅한 스킬입니다.

prometheus

5
from Lee-SiHyeon/oh-my-copilot

전략 플래닝 에이전트. 코드 짜기 전에 인터뷰로 요구사항을 명확히 하고 실행 계획을 수립합니다. "계획 세워줘", "플래닝", "prometheus", "인터뷰 모드", "deep-interview" 트리거로 사용합니다. 복잡한 태스크 전 항상 실행 권장.

oracle

5
from Lee-SiHyeon/oh-my-copilot

Read-only consultation agent. Hard debugging (2+ failed attempts), complex architecture design, self-review after significant implementation. Strategic technical advisor with deep reasoning. (Oracle - oh-my-opencode port)

oh-my-copilot

5
from Lee-SiHyeon/oh-my-copilot

oh-my-opencode를 Copilot CLI용으로 포팅한 멀티에이전트 오케스트레이션 플러그인. Sisyphus(오케스트레이터) + Hephaestus(딥워커) + Prometheus(플래너) + Ralph Loop. "/ultrawork 태스크설명" 으로 시작하면 모든 에이전트가 자동 활성화됩니다. Use this plugin when asked for: "ultrawork", "orchestrate", "multi-agent", "deep work".

init-deep

5
from Lee-SiHyeon/oh-my-copilot

계층형 AGENTS.md 파일 자동 생성. 프로젝트 전체를 분석해서 루트와 복잡한 서브디렉토리에 AGENTS.md를 생성합니다. "AGENTS.md 만들어줘", "init-deep", "프로젝트 문서화", "코드맵 만들어" 트리거로 사용합니다.

hephaestus

5
from Lee-SiHyeon/oh-my-copilot

자율 딥워커 에이전트. 목표만 주면 스스로 탐색하고 완료까지 실행합니다. "딥워크", "자율 실행", "hephaestus", "알아서 해줘" 트리거로 사용합니다. 레시피가 아닌 목표를 받아서 Senior Staff Engineer처럼 동작합니다.

github-triage

5
from Lee-SiHyeon/oh-my-copilot

GitHub 이슈/PR read-only 트리아지. 모든 오픈 이슈와 PR을 분석해서 보고서를 /tmp/에 저장합니다. GitHub에 어떤 변경도 하지 않습니다. "triage", "이슈 분석", "PR 검토", "github triage" 트리거로 사용합니다. oh-my-opencode github-triage 스킬을 Copilot CLI에 포팅했습니다.