Pinchtab
by @pinchtab
Use this skill when a task needs browser automation through PinchTab: open a website, inspect interactive elements, click through flows, fill out forms, scra...
clawhub install pinchtabπ About This Skill
name: pinchtab description: "Use this skill when a task needs browser automation through PinchTab: open a website, inspect interactive elements, click through flows, fill out forms, scrape page text, log into sites with a persistent profile, export screenshots or PDFs, manage multiple browser instances, or fall back to the HTTP API when the CLI is unavailable. Prefer this skill for token-efficient browser work driven by stable accessibility refs such as
e5 and e12."
metadata:
openclaw:
requires:
bins:
- pinchtab
anyBins:
- google-chrome
- google-chrome-stable
- chromium
- chromium-browser
homepage: https://github.com/pinchtab/pinchtab
install:
- kind: brew
formula: pinchtab/tap/pinchtab
bins: [pinchtab]
- kind: npm
package: pinchtab
bins: [pinchtab]
Browser Automation with PinchTab
CLI-first browser skill. Use pinchtab commands.
Core Workflow
1. Create a session: export PINCHTAB_SESSION=$(pinchtab session create --agent-id myagent) β do this once before any browser command.
2. Navigate: pinchtab nav β auto-starts the local server if needed, then returns tab ID + interactive snapshot in one call.
3. Interact: pinchtab click --snap-diff β returns OK + only changed elements (most token-efficient).
4. For read-only observation: pinchtab text when you won't act on refs.
Key optimization: Use --snap-diff on click, fill, select, back, forward, reload to get only added/changed/removed elements β most token-efficient for multi-step flows. Use --snap when you need the full snapshot (e.g., first navigation, or after major page changes). Use --text when you need prose content for verification (skips snap, returns page text directly).
--snap-diff returns the same compact format as snap, but with change markers and a header showing counts:
# Page Title | URL | 57 nodes | +2 ~1 -0
e0:link "Home"
e5:button "Submit" [+]
e12:textbox val="updated" [~]
removed: e99
[+] = added, [~] = changed, removed refs listed at end. All valid refs are shown β no need to remember previous snapshot. Do not follow with redundant snap; only call text when you need prose content.Fallback observation (when --snap wasn't used):
pinchtab snap β interactive elements + headings in compact format (default).pinchtab snap [selector] β scope the current-tab snapshot to one element.pinchtab snap --full β all nodes as JSON (for debugging).pinchtab text β content only (use when snap is missing prose you need).Rules: only nav auto-starts the default local server; snap, text, html, find, and action commands operate on an already-running server/current tab. Explicit --server targets are never auto-started. Never act on stale refs; screenshots only for visual/debug; choose the instance/profile up front for parallel or multi-site work.
Safety Defaults
text, snap, find. Only use eval, download, upload when a simpler command cannot accomplish the task.pinchtab cookies) contains session credentials β do not log, copy, or expose cookie values to untrusted contexts. Use only when the task specifically requires cookie inspection.pinchtab network-export) may contain private URLs, auth tokens, and response bodies. Omit --body for sensitive sessions. Delete or redact export files after use.Selectors
Unified selectors accepted by any element-targeting command:
e5 β from snapshot cache (fastest).#login, .btn, [data-testid="x"] β document.querySelector.xpath://button[@id="submit"] β CDP search.text:Sign In β visible text match.find:login button β natural language via /find.Auto-detection: bare eNβref, #/./[...]βCSS, //βXPath. Use explicit css:/xpath:/text:/find: prefixes when ambiguous. HTTP API uses the same syntax in the selector field (legacy ref still accepted).
Command Chaining
&& when you don't need intermediate output (pinchtab nav ). Run separately when you must read refs before acting.
Challenge Solving
Pages showing "Just a moment..." etc.: POST /solve {"maxAttempts":3} (or /tabs/TAB_ID/solve). Returns immediately if no challenge is present. See api.md.
Requires explicit user approval. Do not call /solve or enable stealth features without the user confirming that challenge-solving is needed for the current task. Never enable stealthLevel without user consent.
Authentication and State
Patterns: (1) one-off pinchtab instance start; (2) reuse profile instance start --profile work --mode headed, switch to headless after login; (3) HTTP POST /profiles then POST /profiles/; (4) human-assisted headed login, agent reuses headless. Agent sessions: pinchtab session create --agent-id or POST /sessions β set PINCHTAB_SESSION=ses_....
Session reuse safety: When reusing authenticated browser sessions established by a human, use a dedicated low-privilege profile β not the user's personal browsing profile. Confirm with the user before performing account-changing actions (password changes, payment, deletion, permissions) in a reused session. Restrict navigation to the sites needed for the task.
Configuration
Config file: ~/.pinchtab/config.json. Edit it directly to change settings β no need for PINCHTAB_CONFIG or temp files.
pinchtab config show # view current config
pinchtab security # review security posture
Key settings agents may need to change:
security.allowEvaluate: enable eval command (true/false)security.allowedDomains: list of allowed hostnames (e.g. ["localhost", "127.0.0.1"])instanceDefaults.headless: run Chrome headless (true) or headed (false)Essential Commands
Server and targeting
pinchtab server | daemon install | health
pinchtab instances | profiles
pinchtab --server http://localhost:9868 snap -i -c # target a specific instance
pinchtab server prints READY to stdout when the browser instance is up and ready to accept commands. Read its output β it includes hints on how to get started (session creation, first nav).
Navigation and tabs
pinchtab nav # auto-starts default local server; flags: --snap, --new-tab, --tab , --block-images, --block-ads, --dismiss-banners, --print-tab-id
pinchtab back | forward | reload # all support --snap, --snap-diff, --text, --dismiss-banners
pinchtab tab # list tabs
pinchtab tab # focus tab
pinchtab nav --new-tab # force another tab
pinchtab tab close
pinchtab instance navigate
Anonymous commands share a single current tab β if anything else navigates that tab, your next command hits the wrong page. Always create a session before your first nav:
export PINCHTAB_SESSION=$(pinchtab session create --agent-id myagent)
All subsequent commands use that session's dedicated tab automatically β no --new-tab or --tab needed.
Observation
pinchtab snap [selector] # default: compact + interactive; flags: --full (JSON), -d (diff), --selector , --max-tokens
pinchtab text # Readability-filtered page text
pinchtab text --full # raw document.body.innerText (alias: --raw)
pinchtab text # ref / -s CSS / xpath:... β text from one element
pinchtab text --json # full JSON (url/title/truncated)
pinchtab find # semantic search; --ref-only for just the ref
Guidance:
snap β default observation (compact + interactive). Returns interactive elements + headings. Prefer this over separate text calls.snap --full β all nodes as JSON; for debugging or when you need the full tree.snap -d β standalone diff from previous snapshot. Use only when you need a diff without performing an action; for any click/fill/select/back/forward/reload, --snap-diff on the action itself already gives you the authoritative post-action state.text β reading articles/dashboards when you won't act on refs. Falls back to --full when Readability drops content you need.text β read one element without pulling the whole page.find β skip the snapshot when you can describe the target in a phrase. --ref-only pipes straight into click/fill/type.snap -i and full snap are numbered differently β do not mix; re-snapshot before acting if you switched modes.--block-images on nav for read-heavy tasks. Reserve screenshots/PDFs for visual verification.Interaction
All interaction commands accept unified selectors (see Selectors above).
pinchtab click # flags: --snap, --snap-diff, --text, --wait-nav, --dismiss-banners (with --wait-nav), --x/--y (coords), --dialog-action accept|dismiss [--dialog-text "..."]
pinchtab dblclick
pinchtab mouse move|down|up # --button left|middle|right
pinchtab mouse wheel --dx --dy
pinchtab drag # or: drag --drag-x --drag-y
pinchtab type # keystroke events
pinchtab fill # set value directly; flags: --snap, --snap-diff, --text
pinchtab press # Enter, Tab, Escape, ...
pinchtab hover
pinchtab select # flags: --snap, --snap-diff, --text; matches value attr, falls back to visible text
pinchtab scroll # scroll 1500, scroll down, scroll '#footer'
Rules:
OK; use --json for recovery metadata. Errors go to stderr as ERROR: : .--snap-diff with click, fill, select, back, forward, reload β returns OK + only changed elements. Use --snap when you need the full snapshot (first nav, major page change).fill for form entry; type only when the site depends on keystroke events.click --wait-nav when a click navigates. May return {"success":true} or Error 409: unexpected page navigation β treat 409 as success and verify with fresh snap/text.--dismiss-banners on nav/back/forward/reload (and on click --wait-nav) runs a best-effort pass that clicks a visible Accept all / Got it / OK / Close / Dismiss button, or removes obvious cookie/consent/dialog/overlay containers. Use when a fresh page-load shows a modal that blocks interaction (typical symptom: Error 500: action click: element is occluded). Heuristic β can misfire on pages that label legitimate UI as overlay or modal; not a substitute for an explicit selector when one is known.mouse only for drag handles, canvas widgets, or exact pointer sequences.--dialog-action accept|dismiss, --dialog-text for prompt() responses."scrollX"/"scrollY" for pixel deltas, "selector" to scroll into view β x/y are viewport coords, not deltas.GET /download?url=... returns JSON {contentType, data (base64), size, url}; only http/https; private/internal hosts blocked unless in security.downloadAllowedDomains.Waiting
Use for async DOM settling (spinners, toasts, XHR).
pinchtab wait # default: visible; --state hidden to wait for disappear
pinchtab wait --text "..." | --not-text "..." # text appear / disappear
pinchtab wait --url "/dashboard" # glob: , *, ?
pinchtab wait --load ready-state|content-loaded|network-idle
pinchtab wait --fn "window.dataReady === true" # requires security.allowEvaluate
pinchtab wait 500 # fixed ms delay (last resort, max 30000ms)
Timeout 10s default, 30s max via --timeout . Prefer --not-text/--state hidden over polling.
Export, debug, verification
pinchtab screenshot [-o path.png] [-q ] # format by extension
pinchtab pdf [-o path.pdf] [--landscape]
Advanced (explicit opt-in only)
These operations are high-impact and gated by security policy. Do not use unless the task specifically requires them and simpler commands are insufficient.
pinchtab eval "document.title" # --await-promise for async; requires security.allowEvaluate: true
pinchtab download -o /tmp/out.bin # requires security.allowDownloads: true
pinchtab upload /absolute/path -s # requires security.allowUploads: true
eval: narrow read-only DOM inspection unless user asks for mutation. Blocked by default (security.allowEvaluate: false).download: prefer temp/workspace path over arbitrary filesystem. Blocked by default.upload: path must be user-provided or clearly approved. Blocked by default.HTTP API fallback
Use curl only when the CLI is unavailable. See api.md for full endpoint reference.
Common Patterns
nav --snap β fill --snap-diff per field β click --wait-nav --snap-diff submit β verify with text. Always click submit; never press Enter.click --snap-diff to get only changed refs with each action β most token-efficient for flows with many steps.click "text:Accept", fill "#search" "q".Verification & Gotchas
text confirms success messages / navigation outcomes. Default is Readability-filtered; may drop nav, repeated headlines, short-text nodes, or collapse lists. Use text --full (raw document.body.innerText) when verifying list/grid/tab/accordion pages, the marker is short, or a default read came back missing content you saw in snap.{"clicked":true,"submitted":true} means the event fired, not that the server accepted or HTML validation passed. Verify via snap/text β or use --snap-diff on the action itself, which already reflects the post-event page state.pinchtab frame sets a stateful scope inherited by subsequent selector-based snap/action/text calls. Target accepts main, an iframe ref, CSS for the iframe, a frame name, or a URL. Nested iframes need multiple hops. Full snap (no -i) flattens same-origin iframe descendants and ref-based actions work across the boundary. Cross-origin iframes aren't exposed as scopes β fall back to eval against iframe.contentDocument. text --frame takes a 32-char hex frameId (from pinchtab frame output), not a CSS selector. One-shot read idiom: FID=$(pinchtab frame '#f' | jq -r .current.frameId); pinchtab frame main; pinchtab text --full --frame "$FID".eval β always IIFE when introducing identifiers. Top-level const/let/class collide across calls in the shared realm (SyntaxError: Identifier 'x' has already been declared). Also needed to project DOMRect into a JSON-serializable object: pinchtab eval "(() => { const r = document.querySelector('#x').getBoundingClientRect(); return {x: r.x, y: r.y, w: r.width, h: r.height}; })()". Single expressions without identifiers (document.title) are fine bare.text reads hidden nodes: both default and --full include display:none / visibility:hidden content because they read raw DOM. To confirm something is *actually visible*, use snap (accessibility tree respects visibility) or eval against offsetHeight / getComputedStyle().display. Common trap: pre-seeded hidden success reported by text before submission.
Compact snap shows by visible text, not value. select accepts either; only eval + Array.from(select.options) to debug a no-match.
text: selectors use JS-level search and can flake with DOM Error / context deadline exceeded on large pages. Prefer refs from a fresh snap -i -c β they resolve by backend node IDs.
snap -i -c skips non-interactive descendants. For iframe interiors set a frame scope or use full snap.
aria-expanded is usually on the outer container of accordions/menus, not the click trigger. Verify via the wrapper's attribute.References
Full API: api.md
Minimal env vars: env.md
Agent optimization: agent-optimization.md
Profiles: profiles.md
MCP: mcp.md
Security model: TRUST.md
βοΈ Configuration
Config file: ~/.pinchtab/config.json. Edit it directly to change settings β no need for PINCHTAB_CONFIG or temp files.
pinchtab config show # view current config
pinchtab security # review security posture
Key settings agents may need to change:
security.allowEvaluate: enable eval command (true/false)
security.allowedDomains: list of allowed hostnames (e.g. ["localhost", "127.0.0.1"])
instanceDefaults.headless: run Chrome headless (true) or headed (false)