Research Standard

Interceptor

Real Chrome/Brave browser automation + full macOS Computer Use via the Interceptor extension and bridge — controls the actual browser from inside (zero CDP fingerprint, passes BrowserScan, Pixelscan, CreepJS, Fingerprint.com) and drives native macOS apps when the bridge is installed.

13
Workflows
00
References
49
Triggers
medium
Effort

The Problem

Headless browsers leave fingerprints. Selenium, Playwright, CDP-based tools all set flags that bot detection identifies immediately — and even if they sneak through, they're not logged into anything, so they can't verify your actual deployed app in your actual authenticated state. Meanwhile, raw OS tools like screencapture and osascript pull focus, disturb the operator's working window, and are trivially blocked on modern macOS. You end up with verification that either doesn't pass bot detection, can't reach authenticated surfaces, or actively disrupts the human running it.

How This Skill Approaches It

Interceptor is a Chrome extension that controls the real browser from inside — no debugger, no CDP, no automation flags. It passes BrowserScan, Pixelscan, CreepJS, and Fingerprint.com because it IS Chrome. It stays logged in because it's the operator's actual profile. A mandatory Preflight Isolation Gate runs before every browser workflow: it checks that the binary is >=0.15.0 and that the dedicated interceptor-test profile is connected — if either fails, the workflow stops rather than falling back to the operator's Default window. Background-first by contract means routine automation never moves focus. The optional macOS bridge extends the same control surface to native apps, OS-level trusted input (bypassing isTrusted checks), VM lifecycle, AX tree reads, ScreenCaptureKit capture, Vision OCR, and Apple Events — all without the raw OS tools that are explicitly forbidden. Compound commands (open, read, act, inspect) collapse multi-step flows into single invocations.

  • **Six capability classes, each its own verb tree:** (1) **VISUAL** — `screenshot` to PNG/WebP at any size, region, selector, or full-pixel; (2) **DOM READ** — `read [--markdown] [--include-frames]`, `tree`, `text`, `html`, `find` to extract structured prose, accessibility refs, or raw markup; (3) **JS EVAL** — `eval <code> [--main]` runs arbitrary JavaScript in isolated or main world for runtime inspection, error capture, state dumps; (4) **NETWORK** — `net log`, `net headers`, `net export --format har|pcapng|json`, `override`, `headers add/remove` for passive request capture with CDP fingerprint-free observation; (5) **INPUT** — `click`, `type`, `keys`, `act <ref> [--trusted]`, `drag`, `scroll`, `select`, `focus` for both browser and native macOS; (6) **RECORD/REPLAY** — `monitor start/stop`, `monitor export --plan` for capturing real user flows as deterministic replay scripts (browser + macOS AX events)
  • **Console messages and runtime errors are read via `eval`** — inject `console.error`/`window.error` capture, then poll back
  • **Hydration failures, blank-page debugging, JS exceptions, mismatch warnings all surfaced through eval + net.** Stays logged in, uses real sessions
  • **Profile isolation by default — all live testing routes through a dedicated `interceptor-test` Chrome profile (separate window, separate user data) so the operator's main browser is never disturbed.** Compound commands (open, read, act, inspect) collapse multi-step flows
  • Background-first by contract — only `--activate` / `app activate` / explicit `tab switch` move focus
  • Multi-context routing (`--context <id>`) routes commands to specific browser profiles
  • Native VM lifecycle (`macos vm *`) replaces Lume/Tart/UTM for Linux + macOS guests with the same AX/click/type verbs as the host
  • `read --markdown` for structured prose
  • macOS bridge: AX tree, OS-level trusted input (`--trusted`, was `--os`), ScreenCaptureKit, Vision OCR, Speech, NLP, Apple Events, OSLogStore, file watching, container runtime
  • Workflows: LaunchTestProfile, VerifyDeploy, Reproduce, RecordFlow, ReplayFlow, TestForm, ReadAndExtract, DriveRichEditor, OverrideXhr, ScreenshotForVlm, MultiPageCompare, CaptureBackgroundedApp, DriveBackgroundedApp, DispatchAppleEvent, ReadAxTree, TrustedInputGate, VmLifecycle, RecordAndReplayMacFlow, Update
  • MANDATORY for visual verification — never use agent-browser for deploy confirmation
  • **When one verb wedges on a WebSocket timeout (most often `screenshot`), the other capability classes use independent WebSocket message types and usually work — never bail on Interceptor entirely until you've tried verbs from at least three capability classes.**
Not for batch headless automation (use Browser), residential-proxy crawling (use BrightData), or social platform actor scraping (use Apify)

In Action

What you say to your DA, and what the Interceptor skill actually does.

  • You say "verify the deploy looks right"
    Runs VerifyDeploy: checks preflight isolation gate, opens the URL in the interceptor-test Chrome profile via --context interceptor-test, does a structured read plus screenshot, checks for console errors and network failures, and returns evidence of the actual deployed state — logged in, real browser, zero bot detection.
  • You say "reproduce this bug i'm seeing on the checkout page"
    Runs Reproduce: opens the affected page before touching any code, captures console errors and network 404s via interceptor inspect, screenshots the broken state — so you have real evidence of what's actually wrong before you start guessing.
  • You say "record this flow so i can replay it as a regression check"
    Runs RecordFlow via monitor start, captures every browser action as AX events, then exports a deterministic plan script via monitor export --plan that ReplayFlow can execute step-by-step to verify nothing regressed.

Inside the Skill

The thinking, frameworks, and architecture that distinguish this skill from a generic version of the same task.

What It Does

Interceptor drives the real Chrome/Brave browser from inside it, and drives native macOS apps when the bridge is installed. Six capability classes, each its own verb tree: visual capture, DOM read, JS eval, network capture, input, and record/replay. It stays logged into your real sessions, passes every major bot-detection check, and is the mandatory tool for visual deploy verification.

The Problem

Headless browser tools speak CDP, which sites can fingerprint and block — so the automated browser sees a different page than a logged-in human does, or gets blocked outright. They also run as a separate browser instance with no auth, so they can't reach anything you're signed into. And when a page breaks — a blank screen after mount, a hydration mismatch, cards that flash and vanish — a screenshot tells you nothing about why. Interceptor solves all three: it operates through the actual browser UI (zero CDP fingerprint), uses your real signed-in sessions, and reads the live DOM, console errors, and network traffic to explain failures a screenshot can't.

How It Works

Interceptor is a Chrome extension that operates through the actual browser UI plus an optional macOS bridge that extends the same control surface to native apps, OS-level input, and on-device VMs.

Tool: interceptor CLI — Chrome/Brave extension that controls the real browser from inside, plus a macOS bridge that drives native apps, OS-level input, and full VM lifecycle. Repo: https://github.com/Hacker-Valley-Media/slop-browser Install: ~/Projects/interceptor (built from source — see Workflows/Update.md) Chrome "Load unpacked" target: ~/.claude/skills/Interceptor/Extension/ (symlink → upstream extension/dist/) Pinned upstream: v0.15.1 — interceptor --version reports the build SHA + date.

Capabilities Overview — Six Verb Trees

Each row is an independent capability class. Each one uses a different WebSocket message type at the daemon→extension boundary, so a wedge on one rarely affects the others. When debugging a broken page, every row is a separate diagnostic path — never bail on Interceptor because screenshot hangs without trying eval, net log, or monitor first.

Verb tree Top-level verbs What you get
VISUAL screenshot, screenshot --region, screenshot --pixel --full PNG/WebP at any size, selector, region, or scroll-and-stitch full page
DOM READ read [--markdown], tree, text, html <ref>, find Accessibility tree, structured markdown, raw markup, refs
JS EVAL eval <code>, eval --main Run JavaScript in isolated or main world — the way you read console errors, runtime exceptions, hydration warnings, and DOM state at runtime
NETWORK net log, net headers, net export --format har|pcapng|json, override, headers add/remove Passive request capture with zero CDP fingerprint; HAR 1.2 + pcapng for Wireshark
INPUT click, type, keys, act <ref> [--trusted], drag, scroll, select, focus Browser + native macOS input; --trusted for OS-level HID source state
RECORD/REPLAY monitor start/stop, monitor export --plan, monitor export --format har Real user-flow capture as deterministic replay scripts; multi-session, browser + macOS AX events

Reading console errors is a recipe, not a separate verb: inject a console.error / window.error listener via eval --main, store events on window.__errs, then eval again to read them back. Useful for hydration mismatches, React error boundaries, JS exceptions on load, and any silent runtime failure. Triggering a reload between install and read loses the captured errors — capture happens after load, so reproduce-by-reload doesn't help; instead capture forward from the next user action, or poll getEventListeners(window) / DOM mutation observers for evidence of failure.

Why this matters in practice. Hydration failures on Astro pages, blank-page after React mount, "cards flash and disappear" — all of these surface through eval reading the live DOM state and console captures, not through screenshots. A screenshot wedge does not block diagnosis; the page is still inspectable through every other verb tree.

Why Interceptor?

agent-browser (the Browser skill) uses CDP — sites can detect it. Interceptor is a Chrome extension that operates through the actual browser UI. No debugger, no automation flags, no separate browser instance. You stay logged in, you pass bot detection, the agent sees what you see. The optional macOS bridge extends the same control surface to native applications, OS-level input, and on-device VMs — that combination is what "Computer Use" means in this skill.

Hard Prohibitions — Operative on Every Invocation

Visual verification goes through Interceptor only. The following are FORBIDDEN with zero exceptions:

  • screencapture — the raw macOS screenshot binary. Not as a primary tool, not as a fallback when Interceptor wedges, not "just for one screenshot." Forbidden.
  • osascript for Chrome control — no tell application "Google Chrome" to activate, no set frontmost of process, no set bounds of window, no set active tab index, no set index of window, no other window-state mutation. Forbidden.
  • osascript System Events keystrokes — no key code, no keystroke, no key down. These send input to whatever is focused, which steals from the operator. Forbidden.
  • Any focus pull in service of automation — bringing Chrome (or any app) to the front so a screenshot will land is forbidden. The bridge's CGS / DOM-render paths capture without focus change.
  • Any window-state mutation — moving, resizing, repositioning, or reordering Chrome windows is forbidden. The operator owns their window arrangement; the agent never touches it.
  • AppleScript-driven tab switchingset active tab index of window N is doubly forbidden: it both pulls focus and changes which tab the operator is looking at.

These rules survive Interceptor failures. A wedged Interceptor is NOT a license to use raw OS tools. When Interceptor cannot deliver evidence, the recovery is to fix Interceptor (see WebSocket-wedge gotcha below) or to STOP and tell the operator the verification cannot be captured this run — never to fall back.

Bridge-routed Computer Use is separate. interceptor macos open <app>, interceptor macos act <ref>, interceptor act <ref> --trusted (formerly --os) and other bridge-routed actions go through the sanctioned bridge surface and are allowed when a workflow explicitly requires native app control. The prohibition above is on (a) raw OS-level paths that bypass Interceptor entirely AND (b) focus-pulling purely in service of a screenshot.

Preflight Isolation Gate (MANDATORY)

Every browser workflow's first step. No exceptions.

Before any interceptor open|read|act|inspect|screenshot|navigate|tab|monitor|net|cookies|scroll|click|type lands in Chrome, the workflow runs:

bash ~/.claude/skills/Interceptor/Tools/PreflightIsolation.sh

The script asserts two invariants:

  1. Binary version >= 0.15.0 — older builds silently ignore --context and fall back to whichever Chrome connection the daemon can find. That fallback is how a tab lands in the operator's Default window.
  2. interceptor-test context is connected — the dedicated test profile must be live and registered with the daemon. Without it, the operator's Default profile is the only available target.

If either check fails, the script exits non-zero with a structured remediation message to stderr. The workflow MUST STOP on a non-zero exit. Surface the message to the operator. Do not fall back to operating against the Default profile, ever. Do not "try anyway." Do not use screencapture or osascript as a substitute. The cost of one stray tab in the operator's working window is higher than the cost of skipping verification this run.

Exit codes (for handlers that need to discriminate):

  • 2 — interceptor binary not on PATH
  • 3 — version string unparseable
  • 4 — version below minimum (upgrade via Workflows/Update.md)
  • 5 — no browser contexts connected (Chrome closed or extension dead)
  • 6interceptor-test context missing (one-time profile setup needed)

The gate is doctrine. It runs unconditionally — for read-only public-page fetches, for authenticated tooling verification, for screenshot capture, for everything. There is no "safe to skip" case, because every silent fallback to Default is a violation of the operator's window.

Profile Isolation — Default Behavior (CRITICAL)

All live testing routes through a dedicated Chrome profile in its own window, never the operator's Default profile. This is a constitutional rule, not a preference.

  • The isolation boundary is Chrome PROFILE, not user-data-dir. The test profile lives inside the same Chrome installation as the operator's Default profile but with separate cookies, separate tabs, separate window. It IS signed into the operator's accounts (Google, GitHub, Cloudflare, blog admin, ULAdmin) — that's the whole point. A --user-data-dir sandbox would be useless because it has zero auth and can't reach any of the operator's signed-in tooling.
  • Default context for every live-test command: --context interceptor-test. Launched via Tools/LaunchTestProfile.sh, which uses Chrome's --profile-directory="Profile N" to open the test profile's window.
  • The operator's Default profile is read-only by default. Never open a tab, click, type, navigate, or record in the Default profile unless the operator explicitly says so ("verify in my Default profile", "use the main window"). When they do, route via --context <default-id> after interceptor contexts confirms the connection.
  • One-time setup lives in Workflows/LaunchTestProfile.md — operator clicks Chrome's avatar menu → Add profile → signs the new profile into their accounts → loads the Interceptor extension → names the context interceptor-test in the popup.
  • If interceptor-test is not connected (the test window is closed), surface the gap and ask the operator which profile to relaunch. Do not silently fall back to the Default profile.
  • Multi-context backstop. With two contexts connected, bare commands (no --context) fail fast per AGENTS.md. That's a structural safety net, not the primary protection — the primary is the rule above.
  • Context ID is per-machine and lives in preferences.env. The friendly literal interceptor-test is the default when no preferences file exists. The actual binding lives in ~/.claude/PAI/USER/CUSTOMIZATIONS/SKILLS/Interceptor/preferences.env as INTERCEPTOR_TEST_CONTEXT_ID=<friendly-name-or-uuid>. PreflightIsolation.sh sources that file and checks interceptor contexts for the configured ID. When the binding is a raw UUID, expect rot on every extension reload — set a friendly name in the extension popup once and the UUID rot stops mattering.

Why this is doctrine, not preference. The operator's Default profile holds the tabs they're actively working in and the tabs their DA has been driving. The cost of one extra flag on every command is zero. The cost of opening a test tab in the Default profile, clicking something, or triggering an unexpected redirect in a tab the operator was about to read is permanent and disruptive.

NEVER auto-run LaunchTestProfile.sh on preflight failure. When PreflightIsolation.sh exits non-zero with context-not-connected or context-name-mismatch, surface the error to the operator and stop. Do NOT auto-run LaunchTestProfile.sh to "fix" it. The INTERCEPTOR_TEST_CHROME_PROFILE value in preferences.env is a literal --profile-directory argument (e.g., Profile 4). If that value is stale, auto-launching it would open the wrong Chrome profile — potentially the operator's monitoring profile where they have live windows open. The only safe path is operator-confirmed launch. Same principle for context-name-mismatch: when the live UUID differs from the configured ID, the fix is editing preferences.env, not launching anything new.

Install Modes (v0.15.x)

Two install modes, same CLI binary. Confirm with interceptor status and read the mode: line:

Mode What's installed What unlocks
mode: full (default for this skill) CLI + daemon + extension + Swift bridge .app + LaunchAgent Browser automation plus Computer Use: AX tree, OS-level trusted input, ScreenCaptureKit, Vision OCR, Speech, NLP, Apple Events, OSLogStore, file watching, container runtime, VM lifecycle
mode: browser-only CLI + daemon + extension Browser automation only. interceptor macos * returns a structured setup_required error in under 1s. No TCC prompts.

Promote a browser-only install with interceptor upgrade --full. Downgrade with bash scripts/uninstall.sh --bridge-only.

Install channels (pkg installers landed v0.11+):

  • Interceptor-Browser-<v>.pkgmode: browser-only
  • Interceptor-Full-<v>.pkgmode: full
  • bash scripts/install.sh --browser-only|--full → dev path
  • Linux browser-only supported (Microsoft Edge + Vivaldi also recognized as of v0.13.4)

Operating rule: if the user asks for native and status reports mode: browser-only, respond "I'm on a browser-only install. Run interceptor upgrade --full to enable that." Don't run the macos command anyway to see what happens — the preflight short-circuits, but it wastes turns.

Prerequisites

  • Chrome or Brave (or Edge/Vivaldi on supported platforms) running with the Interceptor extension loaded — load it once via chrome://extensions/ → Developer Mode → "Load unpacked" → ~/.claude/skills/Interceptor/Extension/
  • interceptor CLI in PATH (/opt/homebrew/bin/interceptor)
  • interceptor-daemon in PATH (/opt/homebrew/bin/interceptor-daemon)
  • Native messaging manifest registered (bash ~/Projects/interceptor/scripts/install.sh --chrome --skip-extension)
  • macOS bridge as a LaunchAgent (full mode only) — see Workflows/Update.md
  • Sparkle.framework at /usr/local/Frameworks/Sparkle.framework (full mode, v0.10.0+) — bridge depends on it for auto-update

Quick health check:

interceptor --version          # → "interceptor 0.15.x (sha, date)"
interceptor status             # → daemon: running, bridge: running, mode: full|browser-only
interceptor status --verbose   # → adds extension reachability + browser-default mismatch warning
interceptor contexts           # → list of connected browser contexts (multi-profile)
interceptor init               # → one-time write of ~/.config/interceptor/config.toml

Background-First Contract (v0.14.2+)

The whole product is background-first. Routine work never moves the user's focus.

Surface Verbs that move focus Everything else
Browser open --activate, tab new --activate, tab switch <id>, window focus <id> Stays on whatever the operator was looking at — click, type, read, inspect, screenshot, net, cookies, scroll, act. New tabs land in the background by default.
macOS app activate <app>, open <app> --activate Stays on whatever was frontmost — open (no --activate), all input verbs, AX reads, capture, menu, intent dispatch, vision, overlays.

If you call any verb not listed in the "moves focus" column and the frontmost changes, that's a bug.

Reuse path: open --reuse navigates the existing managed tab without leaving dead tabs behind. Preserves the reused tab's focus state — pair with --activate only when the user explicitly says to bring it forward.

Multi-Context Routing (v0.15.x)

When multiple browser profiles are connected (e.g., personal Chrome + isolated test profile + work Brave), commands need to know which one to drive.

interceptor contexts                              # List connected context IDs
interceptor open <url> --context interceptor-test # Route to the isolated test profile (DEFAULT)
interceptor open <url> --context <main-id>        # Route to the operator's personal Chrome (only when explicitly requested)

Without --context, browser commands auto-route only when exactly one context is connected. Zero or multiple contexts fail fast with a structured error — that's the multi-context backstop for the Profile Isolation rule above.

Context IDs are set via the Interceptor extension popup (click the toolbar icon → Context ID field → Save). One-time setup; the daemon remembers across restarts.

Standing default: --context interceptor-test. See Workflows/LaunchTestProfile.md for one-time setup.

Computer Use — macOS Native Helper

The bridge is a Swift LaunchAgent that runs as the user and exposes capabilities the Chrome extension cannot provide on its own:

  • OS-level trusted input (interceptor act <ref> --trusted, macos type --trusted, macos keys --trusted — bypasses isTrusted checks via HID source state)
  • Native macOS app control (interceptor macos open/read/act/inspect — same surface as the browser, against any running app)
  • Accessibility tree of any running app for inspection without screenshots
  • Screen capture beyond Chrome (full-screen, off-tab, multi-display, occluded windows)
  • VM lifecycle (interceptor macos vm create/clone/start/exec/snapshot/restore/stop/delete — Linux + macOS guests, replaces Lume/Tart/UTM)
  • Clipboard r/w, audio listen + speech recognition, system notifications, Vision OCR, NLP, Apple Intelligence, HealthKit, display info
  • Apple Events dispatch to named bundle IDs without activation
  • OSLogStore predicate queries, filesystem search/watching, URL fetch
  • Monitor (cross-app workflow recording with optional clipboard/files/network/log/notifications/speech channels and --frames screenshot capture)

Status check: interceptor status reports bridge: running with PID + socket when it's up, or bridge: not running with a hint when it isn't.

Lifecycle (install / verify / troubleshoot / uninstall) lives in Workflows/Update.md. The Update workflow handles binary placement, Sparkle framework install, LaunchAgent plist, launchctl bootstrap, and TCC prompts in the right order.

Security model — read before installing:

  • Transport is a UNIX domain socket at /tmp/interceptor-bridge.sock. Local-only; no network listener.
  • No authentication on the socket. Any local process running as your user can connect and execute every bridge action. macOS TCC permissions (Accessibility, Input Monitoring, Screen Recording, Microphone) are granted to the bridge once and inherited by every socket client.
  • Marginal risk is supply-chain: a malicious local package gains a one-step path to OS-level input/screen/clipboard without needing its own permission grants.
  • Single-user Mac threat model: acceptable, since anything running as you can already do this with effort. Multi-user Macs need socket hardening.

Compound Commands (Preferred)

These collapse multi-step patterns into single invocations — fewer tool calls, fewer tokens:

interceptor open <url>                              # Open + wait + return tree + text
interceptor open <url> --reuse                      # Navigate existing managed tab
interceptor open <url> --activate                   # Bring new/reused tab to front (explicit opt-in)
interceptor open <url> --tree-only|--text-only|--full|--no-wait|--include-frames
interceptor read                                    # Tree + text for active tab
interceptor read <ref>                              # Subtree
interceptor read --markdown                         # Render page as markdown (preserves headings/tables/emphasis)
interceptor read --markdown --text-only             # Markdown prose only, no tree
interceptor read --tree-only --tree-format compact  # Actionable refs only
interceptor read --include-style|--include-frames
interceptor act <ref>                               # Click + wait + return updated tree + diff
interceptor act <ref> "value"                       # Type + wait + return updated tree
interceptor act <ref> --trusted                     # OS-level HID-sourced input (was --os; --os is deprecated alias)
interceptor act <ref> --keys "Enter"                # Send keyboard shortcut
interceptor act <ref> --no-read                     # Skip post-action tree read
interceptor inspect                                 # Tree + text + network log + headers
interceptor inspect --net-only|--filter <pattern>

--trusted vs --os: v0.13.3 renamed --os to --trusted (canonical). --os is kept as a deprecated alias and emits a warning. Lead with --trusted in new code.

macOS Native (Computer Use)

Same compound surface against any native app the bridge can see:

interceptor macos open "Safari"                     # Return AX tree (background — does NOT raise app)
interceptor macos open "Safari" --activate          # Bring to front (explicit opt-in)
interceptor macos read                              # AX tree of front-most app
interceptor macos read <ref>                        # Subtree
interceptor macos act <ref>                         # AX press — pure AX, no event posting
interceptor macos act <ref> "value"                 # AX value-set on text-bearing roles
interceptor macos type <ref> "text"
interceptor macos type "text" --trusted             # HID-sourced typing to current frontmost
interceptor macos keys "Cmd+Shift+T"
interceptor macos keys "Cmd+S" --trusted            # HID-sourced keys
interceptor macos click <ref>
interceptor macos click <ref> --app "X"             # Per-PID delivery (CGEvent.postToPid)
interceptor macos scroll down 400 --app "Mail"      # Backgrounded scroll
interceptor macos drag --from X,Y --to X,Y --app "X"
interceptor macos screenshot --app "Brave Browser" --save --target-max-long-edge 1568
interceptor macos windows --app "X"                 # List windows of an app (occluded too)
interceptor macos frontmost                         # Current frontmost (proof-of-no-focus-change pattern)
interceptor macos focused --app "X"                 # Currently-focused AX element of an app
interceptor macos apps                              # All running apps + bundle IDs
interceptor macos menu --app "X"                    # Menu-bar AX tree
interceptor macos tree --app "X" --filter interactive --depth 6
interceptor macos find "Send" --app "Slack" --role button
interceptor macos value <ref>                       # Current AX value of a field
interceptor macos trust                             # Probe TCC grants (Accessibility, Input Monitoring, Screen Recording, Microphone)
interceptor macos trust --walkthrough               # Deep-link to System Settings for missing grants
interceptor macos intent dispatch --bundle <id> --script '<applescript>'   # Apple Events
interceptor macos intent warmup <bundle1> <bundle2> ...                    # Pre-prompt TCC for several apps
interceptor macos vision ocr <image-path>           # On-device OCR
interceptor macos speech transcribe --file <path>   # On-device Speech
interceptor macos nlp <action> "<text>"             # On-device NLP
interceptor macos log --predicate "<NSPredicate>"   # OSLogStore query
interceptor macos fs search <pattern> --in <dir>    # Spotlight-backed file search
interceptor macos fs watch <path>                   # Filesystem event stream
interceptor macos overlay show "<text>" --at X,Y    # NSPanel above compositor

If interceptor macos trust reports a missing grant, macOS will prompt the first time the bridge exercises that capability — accept in System Settings → Privacy & Security.

VM Lifecycle (v0.13+, Computer Use only)

interceptor macos vm * replaces Lume / Tart / UTM for both Linux (Apple Containerization package) and macOS (raw Virtualization.framework) guests. State lives under $CWD/.interceptor/vms/<name>.bundle/ by default — Apple's VM.bundle spec, external tools can read it.

interceptor macos vm create lin1 --kind linux --cpu 2 --memory 1G --disk 4G \
    --image docker.io/library/alpine:3 --network nat
interceptor macos vm start lin1 --wait-for-vsock
interceptor macos vm exec lin1 -- uname -a
interceptor macos vm stop lin1
interceptor macos vm delete lin1 --force

# macOS guest: install gold once, clone many
interceptor macos vm install macos-gold --from-latest --cpu 4 --memory 8G --disk 60G
interceptor macos vm snapshot macos-gold baseline --paused-state
interceptor macos vm clone macos-gold macos-test                # APFS clonefile, instant, inherits TCC
interceptor macos vm start macos-test --wait-for-vsock --headless
interceptor macos vm screenshot macos-test --out /tmp/before.png
interceptor macos vm read-ax macos-test --filter 'AXButton'
interceptor macos vm click macos-test 800 600
interceptor macos vm type macos-test "hello"
interceptor macos vm cp <src> macos-test:<dst>
interceptor macos vm port-forward macos-test 8080:80
interceptor macos vm stop macos-test
interceptor macos vm delete macos-test --force

Full lifecycle + Lume migration table: Workflows/VmLifecycle.md.

Core Browser Commands

# State + discovery
interceptor state [--full]              # DOM tree + metadata
interceptor tree [--filter all] [--depth N] [--max-chars N]
interceptor diff                         # Changes since last state/tree read
interceptor find "query" [--role button]
interceptor text [<index|ref>] [--markdown]
interceptor html <index|ref>

# Element interaction
interceptor click <ref>                  # Click by ref (eN)
interceptor click <ref> --at X,Y
interceptor dblclick <ref> --at X,Y
interceptor rightclick <ref> --at X,Y
interceptor type <ref> <text> [--append]
interceptor type "role:name" <text>      # Semantic selector
interceptor select <ref> <value>         # Dropdown
interceptor focus|hover <ref>
interceptor drag <ref> --from X,Y --to X,Y [--steps N] [--duration MS]
interceptor keys "<combo>"               # e.g. "Control+A"

# Navigation + tabs
interceptor navigate <url>
interceptor back | forward
interceptor scroll <up|down|top|bottom>
interceptor wait <ms> | wait-stable [--ms N] [--timeout N]
interceptor tabs
interceptor tab new [url] [--activate]   # Background by default; --activate to foreground
interceptor tab close [id] | tab switch <id>

# Capture
interceptor screenshot --save --format webp --target-max-long-edge 1568 --quality 85    # Agent-default recipe
interceptor screenshot --selector <css>|--element <ref>|--region X,Y,W,H
interceptor screenshot --pixel [--full]  # Compositor capture (requires focus)
interceptor eval <code> [--main]         # JS in isolated or main world
interceptor capture start | frame | stop # tabCapture stream

# Style injection (test redesigns live)
interceptor style inject --css "<rules>" [--top-only]
interceptor style remove <handle>

# Cookies
interceptor cookies <domain>
interceptor cookies set <json>
interceptor cookies delete <url> <name>

Network — Passive, CDP, and Exports

# Passive capture (always-on, no CDP fingerprint)
interceptor net log [--filter <pat>] [--limit N] [--since <ts>] [--json]
interceptor net headers [--filter <pat>]       # CSRF, auth headers
interceptor net export --format har             # HAR 1.2 export (v0.15.x)
interceptor net export --format pcapng          # pcapng for Wireshark
interceptor net export --format json            # JSON dump
interceptor net export --out <path>             # Write to file
interceptor net clear

# Request override (passive, no CDP banner)
interceptor override "*pattern*" status=500 [delay=1000] [body='<json>'] [params=k:v]
interceptor override clear

# CDP-attached interception (explicit opt-in — leaves debugger banner)
interceptor network on [patterns...]
interceptor network off
interceptor network log
interceptor network override on '<json>'
interceptor network override off

# SSE streams (LLM responses, live feeds)
interceptor sse log [--filter <pat>] [--limit N]
interceptor sse streams
interceptor sse tail [--filter <pat>]

# Header rewriting
interceptor headers add <name> <value>
interceptor headers remove <name>
interceptor headers clear

Recording (Session Monitor)

Record real user actions on the active tab, replay as a deterministic plan script. Multi-session capable as of v0.13.0 — concurrent recordings across tabs are supported.

interceptor monitor start ["instruction"]   # Start recording (returns sid)
interceptor monitor pause <sid> | resume <sid>
interceptor monitor stop <sid>               # End + emit summary
interceptor monitor status [--all]
interceptor monitor list                     # All sessions, active + ended
interceptor monitor tail <sid> [--raw]       # Live pretty stream
interceptor monitor export <sid>             # Aligned text
interceptor monitor export <sid> --plan      # Replay script (highest-value artifact)
interceptor monitor export <sid> --json
interceptor monitor export <sid> --format har|pcapng           # Network-tab export
interceptor monitor export <sid> --with-bodies                 # Include request/response bodies

macOS-flavored recording: interceptor macos monitor * — records AX events, optional --frames N, --vision-text, --include clipboard|files|network|log|notifications|speech, --watch-path <p>, --log-predicate "<NSPredicate>". See Workflows/RecordAndReplayMacFlow.md.

Canvas (Rich Web Apps)

For apps that render to <canvas> (Figma, Excalidraw, in-house editors):

interceptor canvas list | status
interceptor canvas log [N] [--kind fillText]
interceptor canvas objects [N] [--kind text]
interceptor canvas model | routes
interceptor canvas ocr N [--region X,Y,W,H]
interceptor canvas read N [--format png] [--region X,Y,W,H] [--webgl]
interceptor canvas diff <url1> <url2> [--threshold 10] [--image]

Scene Graph (Rich Editors — Google Docs/Slides, Canva)

interceptor scene profile [--verbose]
interceptor scene list [--type shape|text|image|page|embed|slide]
interceptor scene click <id> | dblclick <id> | select <id>
interceptor scene hit <x> <y>                # ID object at coordinates
interceptor scene selected | text [--with-html]
interceptor scene insert "<text>"
interceptor scene cursor-to <x> <y>
interceptor scene slide list | current | goto <index> | notes [--slide N]
interceptor scene render <id> [--save]
interceptor scene zoom
interceptor scene ... --profile <name>       # Force profile, bypass detection

LinkedIn

interceptor linkedin event [url]             # Event + post data via DOM + network
interceptor linkedin attendees [url]         # Attendees with override + enrichment

ChatGPT Agentic Bridge

Drive chatgpt.com from CLI without an API key:

interceptor chatgpt send "<prompt>" [--stream]
interceptor chatgpt read | status
interceptor chatgpt conversations | switch <id>
interceptor chatgpt model [name]
interceptor chatgpt stop

Batch + Meta

interceptor batch '<json_array>' [--stop-on-error] [--timeout MS]
interceptor status [--verbose]        # Daemon + bridge state, mode, browser config, extension probe
interceptor contexts                   # List connected browser contexts
interceptor --version                  # Build SHA + date
interceptor --help                     # Top-level help
interceptor <cmd> --help               # Per-command help (v0.10.8+)
interceptor init                       # Write starter config to ~/.config/interceptor/
interceptor upgrade --full             # Promote browser-only to full

Key Rules

  • Requires Chrome/Brave running for browser commands — it's an extension, not a standalone binary.
  • Requires bridge running for act --trusted, macos *, full-screen capture, VM lifecycle — interceptor status confirms.
  • Refs use eN syntaxe12 not @e12. Treat refs as short-lived; re-read or find after navigation, rerenders, or DOM mutations.
  • Cross-frame refsread --include-frames returns refs like e<frameId>_<n> for non-top frames.
  • Plain text by default. --json only when piping into a script. Prose-trained models comprehend tree/text output better than dense JSON.
  • Daemon auto-starts — first command launches it; no manual start needed.
  • Prefer compound commands (open, read, act, inspect) over manual tab new + wait + tree chains.
  • Prefer structured reads over screenshots unless the task is explicitly visual or pixel-based — tree/text/network/scene/AX data is faster, smaller, and more deterministic.
  • --trusted is canonical; --os is a deprecated alias. v0.13.3+. New code uses --trusted.
  • --markdown is the structured-prose surface — preserves headings, tables, emphasis. Use instead of --text-only when visual hierarchy disambiguates the answer.
  • Background-first by contract. Only --activate / app activate / explicit tab switch move focus.
  • Verify with frontmost before/after. Native workflows that promise no focus change should prove it.
  • Never screencapture. Never osascript for Chrome focus, bounds, tabs, or windows. Forbidden under Hard Prohibitions. Survives Interceptor failures.

Delegating to Agents

When spawning agents for Interceptor work:

Agent(subagent_type="general-purpose", prompt="
  Use interceptor CLI for all browser and macOS automation work.
  Browser: open <url> --context interceptor-test, read [--markdown], act eN, inspect, screenshot.
  Native (macOS): macos open <app>, macos read, macos act <ref>, macos inspect, macos vm *.
  Compound commands preferred — they return tree + text in one call.
  Refs use eN syntax (no @ prefix) from tree output. Treat refs as short-lived.
  Background-first: only --activate and app activate move focus.
  PROFILE ISOLATION (MANDATORY GATE): the FIRST action of this task is
    bash ~/.claude/skills/Interceptor/Tools/PreflightIsolation.sh
  If that script exits non-zero, STOP and surface the message verbatim — do NOT
  fall back to operating against the Default profile, and do NOT use screencapture
  or osascript as substitutes. After the preflight returns OK, every browser
  command carries `--context interceptor-test`. Never operate in the operator's
  main profile unless the parent agent explicitly says so.
  `act --trusted` for OS-level HID input (was --os).
  [your specific task instructions here]
")

Gotchas

  • Screenshot ignores scroll position by default. interceptor screenshot (DOM-render path) captures from y=0 of the document — window.scrollTo / scrollIntoView / keys End do not move the screenshot frame. For tall pages, use --region X,Y,W,H, --selector <css>, --element <ref>, or --pixel --full (scroll-and-stitch, requires window visible). (2026-04-27, still relevant 2026-05-23.)
  • Multiple tabs at the same URL confuse routing without --context. When two tabs both load localhost:5180/, tab switch <id> reports ok but the visually-active Chrome tab may not change. Either close duplicates, work from a freshly-opened single tab, or pass --context <id> to scope unambiguously.
  • eval is CSP-blocked on most sites. Use eval --main to run in the page's main world. Even with --main, strict CSP (script-src 'self') still blocks string-eval; pass small expressions, avoid Function-constructor patterns.
  • Bridge needs Sparkle.framework after v0.10.0. When rebuilding from source on Apple Silicon, /usr/local/bin/interceptor-bridge won't load until Sparkle.framework is installed at /usr/local/Frameworks/. The Update workflow handles this; symptom of forgetting is bridge: not running with dyld[*]: Library not loaded: @rpath/Sparkle.framework/... in /tmp/interceptor-bridge.stderr.log. (2026-05-03.)
  • Manifest version bump = manual extension reload required. Chrome does not auto-reload unpacked extensions. When extension/dist/manifest.json version field changes, delete the existing extension card in chrome://extensions and Load Unpacked again from ~/.claude/skills/Interceptor/Extension/. The extension key is deterministic so the extension ID stays stable across reloads.
  • Daemon→extension WebSocket can wedge in a half-alive state — try other verb trees BEFORE declaring Interceptor unusable. Symptom: interceptor tabs, contexts, and status succeed (extension reachable) but a SPECIFIC verb times out at 15s. The fix order is verb-substitution first, infrastructure restart second. Each verb in the Capabilities Overview uses a different WebSocket message type, so a wedge on screenshot rarely affects eval, net log, tree, read --markdown, monitor, or inspect. (1) Try a verb from a different capability class first. Need to know what's on the page? read --markdown or eval --main document.body.innerText instead of screenshot. Debugging a hydration failure? eval --main to read document.querySelectorAll(...) + inject console.error capture. Looking for failed requests? net log instead of inspecting visually. The visual answer is almost never the only path. (2) pkill -f interceptor-daemon and retry — clean respawn fixes the truly stuck cases. (3) Surface to operator: "Interceptor extension is wedged — please reload it from chrome://extensions/." (4) Only after (1)+(2)+(3) all fail, STOP and tell the operator verification cannot be captured — never fall back to screencapture / osascript. The recurring mistake this rule catches: claiming "Interceptor is broken" after a single screenshot timeout when eval would have answered the diagnostic question in one tool call. (2026-05-13, sharpened 2026-05-27.)
  • AXEnhancedUserInterface was removed from the bridge (it foregrounded AppKit apps as a side effect of being interpreted as "VoiceOver active"). The bridge uses AXManualAccessibility exclusively for Electron wake-up. Stale guidance from old code that sets AXEnhancedUserInterface should be ignored.
  • Sensitive-app gate. The bridge rejects type/keys/click x,y/drag when frontmost is a denylisted bundle (Keychain, 1Password, Dashlane, LastPass, Bitwarden, System Settings, Chase, Bank of America, Wells Fargo). Surface the rejection — do not bypass.
  • VM paused-state snapshots are gated. --paused-state requires validateSaveRestoreSupport() to return true on the VM config. macOS guests support it; some Linux configurations don't. --disk-only always works.
  • TCC grants for VM hosts: the bridge needs com.apple.security.virtualization entitlement. Move the bridge out of ~/Documents or ~/Desktop if setup_required complains about the install location.
  • Local extract.ts customization is obsolete. The pre-v0.13 patch that bumped slice limits to 10M is no longer needed — upstream's withTruncationMarker + --full flag + per-action maxChars does it correctly with explicit truncation markers (... (truncated: showed X of Y chars ...)).
  • Stale binary silently routes to Default — the v0.10.3 fallback incident (2026-05-23). A rebuild that lands at ~/Projects/interceptor/dist/ but never gets copied into /opt/homebrew/bin/ leaves the CLI at an old version. Pre-0.15.x binaries do not recognize --context or the contexts subcommand — they accept the flag without error and proceed to route the command through whichever Chrome connection the daemon can find, which is normally the operator's Default profile. Symptom: agent calls interceptor open <url> --context interceptor-test, expects an isolated tab, gets a tab in the operator's working window. Detection: interceptor --version reports a version older than 0.15.0, AND/OR interceptor open --help shows no --context-related flag, AND/OR interceptor contexts returns "unknown command". Mitigation: the Preflight Isolation Gate (above) hard-fails on version mismatch with exit code 4 — every workflow runs it as step zero. Recovery is Workflows/Update.md followed by pkill -f interceptor-daemon so the next call respawns the new daemon.

Stealth Verification

Passes all major bot detection:

Check Result
BrowserScan Normal
Pixelscan Definitely Human
Sannysoft All pass
CreepJS 0% headless
Fingerprint.com notDetected
AreyouHeadless Not headless

Workflows · 13

  1. 01
    `Workflows/VerifyDeploy.md` "verify deploy", "check deploy", "confirm deploy", "deploy verification"

    Open URL in real Chrome (via --context interceptor-test), structured read, check errors, evidence

  2. 02
    `Workflows/Reproduce.md` "reproduce", "reproduce bug", "debug page", "check page", "blank screen"

    Open affected page BEFORE code analysis, capture console errors and network 404s

  3. 03
    `Workflows/DriveRichEditor.md` Workflows/`Workflows/DriveRichEditor.md`.md

    Workflows/DriveRichEditor.md

  4. 04
    `Workflows/ScreenshotForVlm.md` Workflows/`Workflows/ScreenshotForVlm.md`.md

    VLM-budgeted screenshot recipe; 1-command budget; --save --format webp --target-max-long-edge

  5. 05
    `Workflows/MultiPageCompare.md` Workflows/`Workflows/MultiPageCompare.md`.md

    Sequential open --text-only per page, no tab thrashing

  6. 06
    `Workflows/CaptureBackgroundedApp.md` Workflows/`Workflows/CaptureBackgroundedApp.md`.md

    CGS capture of named apps window without activating it

  7. 07
    `Workflows/DriveBackgroundedApp.md` Workflows/`Workflows/DriveBackgroundedApp.md`.md

    AX press + value-set + postToPid for non-frontmost input

  8. 08
    `Workflows/DispatchAppleEvent.md` Workflows/`Workflows/DispatchAppleEvent.md`.md

    intent dispatch --bundle <id> --script — no activate in scripts

  9. 09
    `Workflows/ReadAxTree.md` Workflows/`Workflows/ReadAxTree.md`.md

    macos tree with Electron wake-up via AXManualAccessibility

  10. 10
    `Workflows/TrustedInputGate.md` Workflows/`Workflows/TrustedInputGate.md`.md

    --trusted escalation; browser-side __interceptor_trust marker; when to use which

  11. 11
    `Workflows/VmLifecycle.md` Workflows/`Workflows/VmLifecycle.md`.md

    interceptor macos vm * full lifecycle + Lume migration table

  12. 12
    "replay flow", "replay", "regression check", "run flow" Workflows/"replay flow", "replay", "regression check", "run flow".md

    Workflows/ReplayFlow.md

  13. 13
    "test form", "fill form", "form test", "check form" Workflows/"test form", "fill form", "form test", "check form".md

    Workflows/TestForm.md

How to Invoke

Say any of these to your DA and PAI activates the Interceptor skill automatically:

  • "verify deploy"
  • "confirm UI"
  • "screenshot verification"
  • "computer use"
  • "macos automation"
  • "debug web"
  • "troubleshoot"
  • "visual check"
  • "console logs"
  • "console errors"
  • "runtime errors"
  • "network traffic"
  • "network log"
  • "HAR export"

Or invoke explicitly:

Skill("Interceptor")

Want PAI to do this for you?

Install PAI on your machine — your DA gets the Interceptor skill plus 44 others, all hooked into one Life OS.