browser-automation-skill 0.71.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +144 -0
  3. package/SECURITY.md +39 -0
  4. package/SKILL.md +206 -0
  5. package/bin/cli.mjs +55 -0
  6. package/install.sh +143 -0
  7. package/package.json +54 -0
  8. package/references/adapter-candidates.md +40 -0
  9. package/references/browser-mcp-cheatsheet.md +132 -0
  10. package/references/browser-stats-cheatsheet.md +155 -0
  11. package/references/chrome-devtools-mcp-cheatsheet.md +232 -0
  12. package/references/midscene-integration.md +359 -0
  13. package/references/obscura-cheatsheet.md +103 -0
  14. package/references/playwright-cli-cheatsheet.md +64 -0
  15. package/references/playwright-lib-cheatsheet.md +90 -0
  16. package/references/recipes/add-a-tool-adapter.md +134 -0
  17. package/references/recipes/agent-workflows/README.md +37 -0
  18. package/references/recipes/agent-workflows/cache-driven-bulk-operation.md +110 -0
  19. package/references/recipes/agent-workflows/flow-record-and-replay.md +102 -0
  20. package/references/recipes/agent-workflows/incremental-pattern-discovery.md +125 -0
  21. package/references/recipes/agent-workflows/login-then-scrape.md +100 -0
  22. package/references/recipes/anti-patterns-tool-extension.md +182 -0
  23. package/references/recipes/body-bytes-not-body.md +139 -0
  24. package/references/recipes/cache-write-security.md +210 -0
  25. package/references/recipes/fingerprint-rescue.md +154 -0
  26. package/references/recipes/model-routing.md +143 -0
  27. package/references/recipes/path-security.md +138 -0
  28. package/references/recipes/privacy-canary.md +96 -0
  29. package/references/recipes/visual-rescue-hook.md +182 -0
  30. package/references/stats-prices.json +42 -0
  31. package/references/stats-schema.json +77 -0
  32. package/references/tool-versions.md +8 -0
  33. package/scripts/browser-add-site.sh +113 -0
  34. package/scripts/browser-assert.sh +106 -0
  35. package/scripts/browser-audit.sh +68 -0
  36. package/scripts/browser-baseline.sh +135 -0
  37. package/scripts/browser-click.sh +100 -0
  38. package/scripts/browser-creds-add.sh +254 -0
  39. package/scripts/browser-creds-list.sh +67 -0
  40. package/scripts/browser-creds-migrate.sh +122 -0
  41. package/scripts/browser-creds-remove.sh +69 -0
  42. package/scripts/browser-creds-rotate-totp.sh +109 -0
  43. package/scripts/browser-creds-show.sh +82 -0
  44. package/scripts/browser-creds-totp.sh +94 -0
  45. package/scripts/browser-do.sh +630 -0
  46. package/scripts/browser-doctor.sh +365 -0
  47. package/scripts/browser-drag.sh +90 -0
  48. package/scripts/browser-extract.sh +192 -0
  49. package/scripts/browser-fill.sh +142 -0
  50. package/scripts/browser-flow.sh +316 -0
  51. package/scripts/browser-history.sh +187 -0
  52. package/scripts/browser-hover.sh +92 -0
  53. package/scripts/browser-inspect.sh +188 -0
  54. package/scripts/browser-list-sessions.sh +78 -0
  55. package/scripts/browser-list-sites.sh +42 -0
  56. package/scripts/browser-login.sh +279 -0
  57. package/scripts/browser-mcp.sh +65 -0
  58. package/scripts/browser-migrate.sh +195 -0
  59. package/scripts/browser-open.sh +134 -0
  60. package/scripts/browser-press.sh +80 -0
  61. package/scripts/browser-remove-session.sh +72 -0
  62. package/scripts/browser-remove-site.sh +68 -0
  63. package/scripts/browser-replay.sh +206 -0
  64. package/scripts/browser-route.sh +174 -0
  65. package/scripts/browser-select.sh +122 -0
  66. package/scripts/browser-show-session.sh +57 -0
  67. package/scripts/browser-show-site.sh +37 -0
  68. package/scripts/browser-snapshot.sh +176 -0
  69. package/scripts/browser-stats.sh +522 -0
  70. package/scripts/browser-tab-close.sh +112 -0
  71. package/scripts/browser-tab-list.sh +70 -0
  72. package/scripts/browser-tab-switch.sh +111 -0
  73. package/scripts/browser-upload.sh +132 -0
  74. package/scripts/browser-use.sh +60 -0
  75. package/scripts/browser-vlm.sh +707 -0
  76. package/scripts/browser-wait.sh +97 -0
  77. package/scripts/install-git-hooks.sh +16 -0
  78. package/scripts/lib/capture.sh +356 -0
  79. package/scripts/lib/common.sh +262 -0
  80. package/scripts/lib/credential.sh +237 -0
  81. package/scripts/lib/fingerprint-rescue.js +123 -0
  82. package/scripts/lib/flow.sh +448 -0
  83. package/scripts/lib/flow_record.sh +210 -0
  84. package/scripts/lib/mask.sh +49 -0
  85. package/scripts/lib/memory.sh +427 -0
  86. package/scripts/lib/migrate.sh +390 -0
  87. package/scripts/lib/migrators/README.md +23 -0
  88. package/scripts/lib/migrators/memory/v1_to_v2.sh +15 -0
  89. package/scripts/lib/migrators/recent_urls/README.md +13 -0
  90. package/scripts/lib/migrators/stats/README.md +24 -0
  91. package/scripts/lib/node/chrome-devtools-bridge.mjs +1812 -0
  92. package/scripts/lib/node/mcp-server.mjs +531 -0
  93. package/scripts/lib/node/mcp-tools.json +68 -0
  94. package/scripts/lib/node/playwright-driver.mjs +1104 -0
  95. package/scripts/lib/node/totp-core.mjs +52 -0
  96. package/scripts/lib/node/totp.mjs +52 -0
  97. package/scripts/lib/node/url-pattern-cluster.mjs +102 -0
  98. package/scripts/lib/node/url-pattern-resolver.mjs +77 -0
  99. package/scripts/lib/output.sh +79 -0
  100. package/scripts/lib/router.sh +342 -0
  101. package/scripts/lib/sanitize.sh +107 -0
  102. package/scripts/lib/secret/keychain.sh +91 -0
  103. package/scripts/lib/secret/libsecret.sh +74 -0
  104. package/scripts/lib/secret/plaintext.sh +75 -0
  105. package/scripts/lib/secret_backend_select.sh +57 -0
  106. package/scripts/lib/session.sh +153 -0
  107. package/scripts/lib/site.sh +126 -0
  108. package/scripts/lib/stats.sh +419 -0
  109. package/scripts/lib/tool/.gitkeep +0 -0
  110. package/scripts/lib/tool/chrome-devtools-mcp.sh +349 -0
  111. package/scripts/lib/tool/obscura.sh +249 -0
  112. package/scripts/lib/tool/playwright-cli.sh +155 -0
  113. package/scripts/lib/tool/playwright-lib.sh +106 -0
  114. package/scripts/lib/verb_helpers.sh +222 -0
  115. package/scripts/lib/visual-rescue-default.sh +145 -0
  116. package/scripts/regenerate-docs.sh +99 -0
  117. package/uninstall.sh +51 -0
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "browser-automation-skill",
3
+ "version": "0.71.0",
4
+ "description": "MCP server + bash CLI that drives a real browser from Claude Code by routing across chrome-devtools-mcp, playwright-cli, playwright-lib, and obscura adapters. 42 verbs covering site/session/credential management, snapshot+ref interaction, capture pipelines, declarative flows, and a per-archetype memory cache.",
5
+ "keywords": [
6
+ "claude-code",
7
+ "mcp",
8
+ "model-context-protocol",
9
+ "browser-automation",
10
+ "playwright",
11
+ "chrome-devtools-mcp",
12
+ "anthropic",
13
+ "skill",
14
+ "agent"
15
+ ],
16
+ "homepage": "https://github.com/xicv/browser-automation-skill#readme",
17
+ "bugs": {
18
+ "url": "https://github.com/xicv/browser-automation-skill/issues"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/xicv/browser-automation-skill.git"
23
+ },
24
+ "license": "MIT",
25
+ "author": "Nick Cao <nick.cao@avcrm.com>",
26
+ "type": "module",
27
+ "bin": {
28
+ "browser-automation-skill": "bin/cli.mjs",
29
+ "browser-mcp": "bin/cli.mjs"
30
+ },
31
+ "files": [
32
+ "bin/",
33
+ "scripts/",
34
+ "references/",
35
+ "SKILL.md",
36
+ "SECURITY.md",
37
+ "install.sh",
38
+ "uninstall.sh",
39
+ "LICENSE",
40
+ "README.md"
41
+ ],
42
+ "engines": {
43
+ "node": ">=18"
44
+ },
45
+ "os": [
46
+ "darwin",
47
+ "linux"
48
+ ],
49
+ "scripts": {
50
+ "mcp": "bash scripts/browser-mcp.sh serve",
51
+ "test:smoke": "printf '%s\\n' '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{}}' '{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools/list\",\"params\":{}}' | bash scripts/browser-mcp.sh serve",
52
+ "prepublishOnly": "npm run test:smoke >/dev/null && echo 'smoke OK'"
53
+ }
54
+ }
@@ -0,0 +1,40 @@
1
+ # Adapter candidates
2
+
3
+ Browser-automation tools we have considered for the adapter roster but have **not** committed to. This file is a holding pen so candidates survive handoffs and don't get re-discovered from scratch every six months.
4
+
5
+ The shipped roster lives in `references/tool-versions.md` (autogenerated from each adapter's `tool_metadata`). The planned roster lives in the parent spec Appendix B routing matrix. Candidates here are **neither shipped nor planned** — they're "we looked, we declined, here's why, here's what would change our mind."
6
+
7
+ ## Format
8
+
9
+ Each candidate gets one short section: what it is, why we declined now, and the concrete trigger that would flip the decision.
10
+
11
+ ---
12
+
13
+ ## pinchtab — `https://github.com/pinchtab/pinchtab`
14
+
15
+ **What:** Standalone Go HTTP server (~16MB binary) controlling Chrome via CDP. User-installed daemon model (`pinchtab daemon install`). HTTP API at `127.0.0.1:9867` plus a `pinchtab` CLI with subcommands (`nav`, `snap`, `click`, `fill`, `text`, `press`). First-class profile model, multi-instance, token-efficient text extraction (~800 tokens/page). Local-first security posture (loopback bind, IDPI allowlist).
16
+
17
+ **Considered:** 2026-05-02.
18
+
19
+ **Declined now because:**
20
+ - **No empty roster slot.** Inspect/audit/extract → chrome-devtools-mcp (Phase 5 part 1c). Stealth-scrape → obscura (Phase 8). Navigation primitives → playwright-cli/lib. PinchTab competes in occupied lanes without a knockout differentiator.
21
+ - **Daemon-ownership conflict.** Skill invariant: skill manages all process lifecycle. PinchTab inverts this — daemon is user-installed, persists across sessions, has its own update cadence. Adapting requires a new "attach-to-external-daemon" abstraction we don't have.
22
+ - **Architectural duplication.** PinchTab's HTTP-daemon overlaps with our already-shipped IPC daemon (Phase 4 part 4b). Two daemon models would split the contributor mental model.
23
+ - **Maturity risk.** Brand-new project (~2026 H1), single-org. Roster incumbents (Microsoft Playwright, Chrome team's chrome-devtools-mcp) are bets the ecosystem already made.
24
+ - **Token-efficiency overlap.** Our token-efficient-output spec (`docs/superpowers/specs/2026-05-01-token-efficient-adapter-output-design.md`) already captures the `eN` refs + capture files + summary-only-on-stdout discipline. Marginal pinchtab gain ≈ small.
25
+
26
+ **Triggers that would flip the decision:**
27
+ | Trigger | Action |
28
+ |---|---|
29
+ | Phase 8 obscura design surfaces and pinchtab's multi-instance model is a viable non-stealth scrape alternative | reconsider as alt-path for `extract --scrape` |
30
+ | User has a pinchtab daemon running for *other* agents and wants browser-skill to share it | add as 5th adapter targeting the running daemon (requires "attach-to-external-daemon" abstraction first) |
31
+ | PinchTab matures (~12mo), gets community adapters, or Anthropic ships an MCP wrapper for it | re-evaluate against then-current roster |
32
+ | Need a no-node, no-300MB-browser footprint adapter (e.g. CI minimal images) | pinchtab's ~16MB Go binary becomes attractive |
33
+
34
+ ---
35
+
36
+ ## How to add a new candidate
37
+
38
+ Copy the section template above. Keep it short — what / why-declined / triggers. The point is to make the next consideration a 30-second decision, not a 30-minute re-research.
39
+
40
+ If a trigger fires and a candidate becomes shippable, follow [`recipes/add-a-tool-adapter.md`](recipes/add-a-tool-adapter.md) Path A and remove the section from this file.
@@ -0,0 +1,132 @@
1
+ # browser-mcp — cheatsheet
2
+
3
+ `scripts/browser-mcp.sh serve` starts an MCP (Model Context Protocol) server
4
+ that exposes our verbs as MCP tools. Spawn it from any MCP-capable client
5
+ (Claude Code, Continue, Cline, agent-browser, midscene, Stagehand,
6
+ browser-use, etc.) to drive our cache + telemetry + secrets vault without
7
+ re-implementing them.
8
+
9
+ Phase 14 origin: midscene research showed midscene publishes its own MCP
10
+ server so upper-layer agents can call it via natural language. We mirror that
11
+ pattern so anything that speaks MCP can reuse our entire skill — turning us
12
+ into the shared middleware browser agents delegate to.
13
+
14
+ ## Wire format
15
+
16
+ - Transport: stdio (NDJSON — one JSON object per line)
17
+ - Protocol: MCP 2024-11-05 (matches our existing chrome-devtools-bridge client)
18
+ - Envelope: JSON-RPC 2.0
19
+
20
+ ## Tools exposed (Stage 1 + Stage 2)
21
+
22
+ | Tool | Wraps | Required inputs | Optional |
23
+ |---|---|---|---|
24
+ | `browser_open` | `scripts/browser-open.sh` | `url` | `site`, `tool` |
25
+ | `browser_snapshot` | `scripts/browser-snapshot.sh` | _none_ | `site`, `tool`, `capture` |
26
+ | `browser_click` | `scripts/browser-click.sh` | one of `ref` / `selector` | `site`, `tool` |
27
+ | `browser_fill` | `scripts/browser-fill.sh` | `text` + one of `ref` / `selector` | `site`, `tool` |
28
+ | `browser_extract` | `scripts/browser-extract.sh` | one of `selector` / `eval` | `site`, `tool` |
29
+
30
+ Each `tools/call` response carries `content: [{type: "text", text: "<summary JSON>"}]`
31
+ where `<summary JSON>` is the verb's last stdout line (per the token-efficient
32
+ output spec §3.1). `_meta.exitCode` and `_meta.stderr` are surfaced for
33
+ diagnostics.
34
+
35
+ ### Secrets discipline (AP-7)
36
+
37
+ `browser_fill` deliberately has NO `secret` field. MCP has no stdin channel
38
+ and putting secrets in tool arguments lands them in the request transcript.
39
+ For real secret values, call `scripts/browser-fill.sh --secret-stdin`
40
+ directly (the secret is piped via stdin and never reaches argv). Phase 14
41
+ unit-tests this contract: `tests/browser-mcp.bats` asserts the schema does
42
+ not expose any "secret" property and rejects unknown props via
43
+ `additionalProperties: false`.
44
+
45
+ ### Env-var passthrough (whitelist)
46
+
47
+ The MCP server does NOT inherit the client's full env. Only these prefixes
48
+ + POSIX essentials pass through to spawned bash verbs (see
49
+ `scripts/lib/node/mcp-server.mjs::ENV_WHITELIST_PREFIXES`):
50
+
51
+ | Prefix | Purpose |
52
+ |---|---|
53
+ | `BROWSER_SKILL_*` | skill internals (`BROWSER_SKILL_HOME`, trace ID, etc.) |
54
+ | `BROWSER_STATS_*` | post-condition contract + model-name injection |
55
+ | `CLAUDE_*` | `CLAUDE_MODEL`, `CLAUDE_USAGE_*`, `CLAUDE_SESSION_ID` |
56
+ | `MIDSCENE_MODEL_*` | local-VLM endpoint config (one envvar block reaches BOTH our skill AND midscene) |
57
+ | `PLAYWRIGHT_*` | adapter knobs + test injection |
58
+ | `CHROME_DEVTOOLS_*` | cdt-mcp adapter knobs |
59
+ | `OBSCURA_*` | obscura adapter knobs |
60
+ | `STUB_*` / `FIXTURES_*` | test-only seams |
61
+ | `MCP_*` | reserved for future MCP overrides |
62
+
63
+ Everything else (e.g. arbitrary `OPENAI_API_KEY` or unknown secrets in the
64
+ client process) is filtered out. This is the AP-7-aligned default — opt-in
65
+ later via expanding the whitelist, not via removing it.
66
+
67
+ ## Smoke test
68
+
69
+ ```bash
70
+ printf '%s\n%s\n' \
71
+ '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' \
72
+ '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
73
+ | bash scripts/browser-mcp.sh serve
74
+ ```
75
+
76
+ Expected output (two NDJSON lines): an `initialize` reply with
77
+ `protocolVersion: "2024-11-05"` + a `tools/list` reply enumerating
78
+ `browser_open` and `browser_snapshot`.
79
+
80
+ ## Wiring from Claude Code
81
+
82
+ Add to `~/.claude/config.json` (or per-project):
83
+
84
+ ```json
85
+ {
86
+ "mcpServers": {
87
+ "browser-skill": {
88
+ "command": "bash",
89
+ "args": ["/abs/path/to/scripts/browser-mcp.sh", "serve"]
90
+ }
91
+ }
92
+ }
93
+ ```
94
+
95
+ After Claude Code restart, the `browser_open` / `browser_snapshot` tools
96
+ appear in the tool list. They run against the same `~/.browser-skill/` state
97
+ (sites, sessions, captures, memory) as the bash entry points — one cache,
98
+ two surfaces.
99
+
100
+ ## Why this exists
101
+
102
+ - **Cache reuse**: `browser-do` archetype cache + Phase 13 fingerprint rescue
103
+ apply automatically when called via MCP — the bash verb is the same entry
104
+ point either way.
105
+ - **Telemetry parity**: every MCP `tools/call` results in one `stats.jsonl`
106
+ event (same as a direct bash call), so `browser-stats report` shows MCP
107
+ and direct calls in one table.
108
+ - **Secrets stay local**: the MCP server spawns the same `scripts/browser-*.sh`
109
+ verbs, which honour AP-7 (no secrets in argv). MCP clients NEVER see
110
+ credentials.
111
+
112
+ ## Limitations (Stage 1 + 2)
113
+
114
+ - 5 verbs exposed (open + snapshot + click + fill + extract). The other ~37
115
+ verbs (site / session / credential management, flow runner, capture mgmt,
116
+ stats, baseline, schema migration) are reachable only via direct bash.
117
+ Stage 3 candidates: `browser_wait`, `browser_press`, `browser_select`,
118
+ `browser_assert`.
119
+ - No streaming progress events — request/response only. MCP supports
120
+ `notifications/progress`; wiring it is a Stage 3 task.
121
+ - No tool-side authorization. Anything that can spawn the server can call any
122
+ verb. The skill's existing per-verb typed-phrase confirmations (e.g.
123
+ `--yes-i-know` on destructive ops) still apply at the bash boundary.
124
+ - `browser_extract` does NOT expose `--scrape` (multi-URL batch mode) — too
125
+ many args and the output shape changes. Use `scripts/browser-extract.sh`
126
+ directly for that.
127
+
128
+ ## Environment
129
+
130
+ | Var | Meaning | Default |
131
+ |---|---|---|
132
+ | `BROWSER_SKILL_NODE_BIN` | Node binary used by `browser-mcp.sh serve` | `node` |
@@ -0,0 +1,155 @@
1
+ # browser-stats — telemetry / audit / tuning surface
2
+
3
+ Per-action JSONL audit log under `${BROWSER_SKILL_HOME}/memory/stats.jsonl`
4
+ plus a lazy-built SQLite mirror at `memory/stats.db`. JSONL is the source of
5
+ truth; SQLite is regenerated from cursor (`memory/stats.db::stats_cursor`).
6
+
7
+ ## Mental model — balance triangle
8
+
9
+ ```
10
+ tokens
11
+ \
12
+ \
13
+ \____ accuracy
14
+ /
15
+ /
16
+ latency
17
+ ```
18
+
19
+ Every adapter invocation emits one event. `browser-stats report` rolls events
20
+ up by (route × verb × outcome) and surfaces:
21
+
22
+ - success rate (and post-condition hit-rate — the *real* accuracy signal)
23
+ - p50/avg token proxies (`stdout_bytes`, `stderr_bytes`, `argv_bytes`)
24
+ - avg duration_ms
25
+ - $/event when `CLAUDE_USAGE_*` env vars present (priced via
26
+ [`stats-prices.json`](stats-prices.json))
27
+ - failure-mode histogram (13-value enum — see schema)
28
+ - **`oblivious_success`** count: adapter reported `outcome=success` but
29
+ the post-condition assertion failed. This is the audit's killer signal —
30
+ without it, naive self-reported success rates lie.
31
+
32
+ ## Verbs
33
+
34
+ | Verb | What it does |
35
+ |---|---|
36
+ | `browser-stats rebuild` | Tail `stats.jsonl` from cursor → upsert into `stats.db`. Idempotent. Builds schema on first run. |
37
+ | `browser-stats report [--days N] [--route R] [--verb V] [--pareto]` | Human-readable summary. `--pareto` adds a per-route composite efficiency score. |
38
+ | `browser-stats mark <span_id> success\|fail[:reason]` | Record a user override on one event. Audit-report applies overrides over self-reported outcomes. |
39
+ | `browser-stats tune [--days N] [--route R]` | Surface worst-performing (verb, route) candidates for `/autoresearch` handoff. Human-in-loop — never auto-mutates the skill. |
40
+
41
+ ## Examples
42
+
43
+ ```bash
44
+ # Daily summary (last 7 days, all routes):
45
+ bash scripts/browser-stats.sh report
46
+
47
+ # Per-route Pareto frontier (success_rate × output-byte efficiency):
48
+ bash scripts/browser-stats.sh report --pareto --days 30
49
+
50
+ # Just one route:
51
+ bash scripts/browser-stats.sh report --route chrome-devtools-mcp --days 14
52
+
53
+ # Override an event (e.g. you know the audit miscategorized this):
54
+ bash scripts/browser-stats.sh mark a1b2c3d4e5f6a7b8 fail:wrong_element_acted
55
+
56
+ # Find tuning candidates:
57
+ bash scripts/browser-stats.sh tune --days 30
58
+ ```
59
+
60
+ ## Wiring an adapter call site
61
+
62
+ Every verb script that invokes an adapter should emit one stats event per
63
+ invocation. Pattern (see `scripts/browser-open.sh` for a real example):
64
+
65
+ ```bash
66
+ source "${SCRIPT_DIR}/lib/stats.sh"
67
+
68
+ stats_t0="$(now_ms)"
69
+ set +e
70
+ adapter_out="$(invoke_with_retry open "${verb_argv[@]}")"
71
+ adapter_rc=$?
72
+ set -e
73
+
74
+ # Phase 12 part 2: post-condition contract via env vars (keeps the helper
75
+ # call-site readable — 6 positional args instead of 10). Verb script sets
76
+ # OBSERVED to the verb-specific signal (URL for open, adapter_out for
77
+ # click/extract). End-user sets EXPECT_* via env to assert specific values.
78
+ BROWSER_STATS_OBSERVED="${url}" \
79
+ stats_run_adapter_emit \
80
+ "open" "${tool_name}" "${stats_t0}" "${adapter_rc}" \
81
+ "${adapter_out}" "" \
82
+ -- "${verb_argv[@]}"
83
+ ```
84
+
85
+ ### Post-condition env vars (caller sets before invoking the verb)
86
+
87
+ | Env var | Values | Default |
88
+ |---|---|---|
89
+ | `BROWSER_STATS_EXPECT_TYPE` | `url`, `element_path`, `element_value` | (none — disables check) |
90
+ | `BROWSER_STATS_EXPECT_MATCH` | `exact`, `include`, `semantic` | `include` |
91
+ | `BROWSER_STATS_EXPECT_VALUE` | any string | (none — disables check) |
92
+ | `BROWSER_STATS_OBSERVED` | any string | set by verb script |
93
+
94
+ Example:
95
+ ```bash
96
+ BROWSER_STATS_EXPECT_TYPE=url \
97
+ BROWSER_STATS_EXPECT_MATCH=include \
98
+ BROWSER_STATS_EXPECT_VALUE='/devices/42' \
99
+ bash scripts/browser-open.sh --url https://example.com/devices/42
100
+ # → event will carry post_condition_hit:true; oblivious_success detected on mismatch
101
+ ```
102
+
103
+ ### Contract
104
+
105
+ - Helper is best-effort — failure never taints caller's exit code (warns to stderr).
106
+ - `parent_span_id` is null unless caller exported `BROWSER_SKILL_PARENT_SPAN_ID`
107
+ (used by `browser-flow` to nest step spans inside a run span).
108
+ - `model` + `gen_ai_usage_*` fields populate only when `CLAUDE_USAGE_*` /
109
+ `CLAUDE_MODEL` env vars are set. Outside Claude Code → null.
110
+ - `stats_random_id` is fork-free `$RANDOM` by default (~60 bits, fine for
111
+ correlation). Set `STATS_USE_CRYPTO_ID=1` if you need `openssl rand` strength.
112
+ - Requires **bash 5.0+** (`$EPOCHREALTIME`). Falls back to second precision
113
+ on legacy bash; the skill's other bash-isms already require Homebrew bash
114
+ on macOS.
115
+
116
+ ## Schema
117
+
118
+ See [`stats-schema.json`](stats-schema.json) for the full JSON Schema. Field
119
+ names follow OpenInference + OTel GenAI v1.40 conventions (snake_case
120
+ flattening) for direct compatibility with Langfuse / Phoenix / Jaeger via an
121
+ OTLP exporter.
122
+
123
+ Failure-mode enum (13 values, sourced from WAREX + Agent-E + WebVoyager
124
+ taxonomies):
125
+
126
+ ```
127
+ element_not_found element_ambiguous wrong_element_acted
128
+ stale_ref action_timeout navigation_mismatch
129
+ js_not_ready network_error captcha_blocked
130
+ auth_required popup_intercept extraction_mismatch
131
+ oblivious_success
132
+ ```
133
+
134
+ ## Privacy
135
+
136
+ - All writes are local (`memory/` mode 0700, files mode 0600).
137
+ - chrome-devtools-mcp opt-out of upstream Clearcut telemetry recommended:
138
+ pass `--no-usage-statistics` in the adapter wrapper (or set
139
+ `CDT_MCP_NO_USAGE_STATISTICS=1`).
140
+ - `selector_value` and `post_condition_observed` may contain user data.
141
+ No remote sink ever. Future `--redact` mode can hash these.
142
+
143
+ ## Doctor integration
144
+
145
+ `browser-doctor` surfaces:
146
+ - `ok: stats events recorded: N` (or `warn: stats.jsonl absent`)
147
+ - `ok: stats SQLite indexed: N (delta from JSONL: K)`
148
+ - `warn: stats has N oblivious_success in last 7 days` (when > 0)
149
+
150
+ ## Schema migrations
151
+
152
+ `stats.jsonl` starts at `schema_version: 1`. Future shape changes ship a
153
+ migrator under `scripts/lib/migrators/stats/v1_to_v2.sh` (same pattern as
154
+ `memory/`); `browser-migrate run` applies it. SQLite is rebuilt from JSONL
155
+ on the first `rebuild` after a bump.
@@ -0,0 +1,232 @@
1
+ # chrome-devtools-mcp — cheatsheet
2
+
3
+ The browser-skill's chrome-devtools-mcp adapter is the **inspection / audit /
4
+ extract** path. Upstream is the `chrome-devtools-mcp` MCP server
5
+ (`npx chrome-devtools-mcp@latest`) which exposes the rich set of CDP-backed
6
+ tools — console messages, network requests, lighthouse audits, performance
7
+ traces — that the playwright-* adapters do not.
8
+
9
+ ## Status — Path A introduction (phase-05 part 1) + bridge scaffold (part 1b)
10
+
11
+ This adapter ships **opt-in** via `--tool=chrome-devtools-mcp`. Router
12
+ promotion (Path B — making it the default for capture-flag verbs and for
13
+ `audit` / `inspect` per parent spec Appendix B) is deferred to phase-05
14
+ part 1d after a soak window.
15
+
16
+ Phase-05 part 1b shipped the **node bridge scaffold** at
17
+ `scripts/lib/node/chrome-devtools-bridge.mjs`. The adapter shells to that
18
+ bridge (mirrors `playwright-lib`'s shape: adapter → node bridge → upstream).
19
+
20
+ **Phase-05 part 1c shipped the real MCP stdio transport** for stateless
21
+ verbs. With `${CHROME_DEVTOOLS_MCP_BIN}` pointing at a real
22
+ `chrome-devtools-mcp` (e.g. `npx chrome-devtools-mcp@latest`), the bridge:
23
+ 1. spawns the upstream MCP server with stdio piped,
24
+ 2. sends the `initialize` handshake (protocol version `2024-11-05`),
25
+ 3. translates the verb → MCP `tools/call`,
26
+ 4. shapes the response into the skill's single-line summary JSON,
27
+ 5. exits cleanly.
28
+
29
+ `uid → eN` translation happens at the adapter boundary (per token-efficient-
30
+ output spec §5) for snapshot output. The original `uid` is kept on each ref
31
+ for traceability.
32
+
33
+ | Verb | Real-mode behavior |
34
+ |---|---|
35
+ | `open` | `navigate_page {url}` — works (one-shot, or via daemon when running) |
36
+ | `snapshot` | `take_snapshot` — works; refs translated to `eN`. When daemon is running, refMap is cached server-side so subsequent `click` / `fill` resolve `eN → uid` |
37
+ | `eval` | `evaluate_script {script}` — works |
38
+ | `audit` | `lighthouse_audit` — works (60s timeout) |
39
+ | `click`, `fill` | works **via daemon** (phase-05 part 1c-ii) — `daemon-start` first, then `snapshot`, then `click eN` / `fill eN ...`. Without daemon → exit 41 with hint |
40
+ | `inspect` | works real-mode (phase-05 part 1e-ii). Multi-flag aggregation: `--capture-console` → `list_console_messages`; `--capture-network` → `list_network_requests`; `--screenshot` → `take_screenshot`; `--selector CSS` → `evaluate_script` with querySelectorAll. One-shot or daemon-routed |
41
+ | `extract` | works real-mode (phase-05 part 1e-ii). `--selector CSS` → evaluate_script with querySelectorAll → text join; `--eval JS` → raw evaluate_script. One-shot or daemon-routed |
42
+
43
+ ### Daemon mode (phase-05 part 1c-ii)
44
+
45
+ `daemon-start` spawns a detached node child that holds ONE long-lived MCP
46
+ server child + the `eN ↔ uid` ref map + a TCP loopback IPC server. Verb
47
+ clients connect over loopback (Unix sun_path 104-char cap on macOS bats temp
48
+ paths — TCP loopback with ephemeral port sidesteps it). State written to
49
+ `${BROWSER_SKILL_HOME}/cdt-mcp-daemon.json` (mode 0600, dir 0700).
50
+
51
+ ```bash
52
+ node scripts/lib/node/chrome-devtools-bridge.mjs daemon-start
53
+ node scripts/lib/node/chrome-devtools-bridge.mjs open https://example.com
54
+ node scripts/lib/node/chrome-devtools-bridge.mjs snapshot
55
+ node scripts/lib/node/chrome-devtools-bridge.mjs click e1
56
+ node scripts/lib/node/chrome-devtools-bridge.mjs daemon-stop
57
+ ```
58
+
59
+ `daemon-status` reports `daemon-running` / `daemon-not-running`. `daemon-stop`
60
+ when none is a no-op success. Idempotent `daemon-start` returns
61
+ `daemon-already-running`. Daemon stderr lands at
62
+ `${BROWSER_SKILL_HOME}/cdt-mcp-daemon.log` (mode 0600).
63
+
64
+ Stub mode (`BROWSER_SKILL_LIB_STUB=1`) still works exactly as part-1b — used by the bats suite + CI for adapter contract tests without spawning anything.
65
+
66
+ ## When the router picks this adapter
67
+
68
+ After Phase 5 part 1d, four routing rules promote chrome-devtools-mcp to a default for verbs and flags where it's the only sensible adapter (per parent spec Appendix B):
69
+
70
+ | Verb / flag | Default? | Why |
71
+ |---|---|---|
72
+ | `open` (no flags) | no | router default is playwright-cli |
73
+ | `click` (no flags) | no | playwright-cli |
74
+ | `fill` (no flags) | no | playwright-cli (or playwright-lib for `--secret-stdin`) |
75
+ | `snapshot` (no flags) | no | playwright-cli |
76
+ | `--capture-console` / `--capture-network` (any verb) | **YES** (part 1d) | `rule_capture_flags` — only adapter with console + network MCP tools |
77
+ | `--lighthouse` / `--perf-trace` (any verb) | **YES** (part 1d) | `rule_audit_or_perf` — only adapter with `lighthouse_audit` + `performance_*` |
78
+ | `audit` (any flags) | **YES** (part 1d) | `rule_audit_or_perf` |
79
+ | `inspect` | **YES** (part 1d) | `rule_inspect_default` |
80
+ | `extract` | **YES** (part 1d) | `rule_extract_default`. With `--scrape <urls...>` → obscura (when Phase 8 lands) |
81
+ | `eval` | no — opt-in | playwright-cli/lib both support it |
82
+
83
+ `session_required` (storage-state loaded) still wins above all capture-flag rules; that path keeps routing through playwright-lib, so flag combos like `--site app --capture-console` route to playwright-lib (capture flags silently ignored). Limitation tracked for part 1f (Chrome `--user-data-dir` lets cdt-mcp do session loading too).
84
+
85
+ ## Capabilities declared
86
+
87
+ ```json
88
+ {
89
+ "verbs": {
90
+ "open": { "flags": ["--headed", "--url"] },
91
+ "click": { "flags": ["--ref"] },
92
+ "fill": { "flags": ["--ref", "--text", "--secret-stdin"] },
93
+ "snapshot": { "flags": ["--depth"] },
94
+ "inspect": { "flags": ["--capture-console", "--capture-network", "--screenshot"] },
95
+ "audit": { "flags": ["--lighthouse", "--perf-trace"] },
96
+ "extract": { "flags": ["--selector", "--eval"] },
97
+ "eval": { "flags": ["--expression"] }
98
+ }
99
+ }
100
+ ```
101
+
102
+ All eight verbs are declared so `--tool=chrome-devtools-mcp` makes the full
103
+ surface reachable today (the capability filter in `pick_tool` admits any
104
+ declared verb regardless of router precedence).
105
+
106
+ ## Architecture
107
+
108
+ ```
109
+ bash adapter node bridge upstream MCP server
110
+ ┌─────────────────┐ ┌──────────────────────┐ ┌─────────────────────┐
111
+ │ chrome-devtools-│───▶│ chrome-devtools- │───▶│ chrome-devtools-mcp │
112
+ │ mcp.sh │ │ bridge.mjs │ │ (npx, JSON-RPC over │
113
+ │ (8 tool_* fns) │ │ - stub mode │ │ stdio) — part 1c │
114
+ │ │ │ - real mode (1c) │ │ │
115
+ └─────────────────┘ └──────────────────────┘ └─────────────────────┘
116
+ ```
117
+
118
+ This mirrors the `playwright-lib → playwright-driver.mjs → real Playwright`
119
+ shape. The bridge is the translation boundary between skill verb argv and
120
+ the MCP `tools/call` JSON-RPC envelope (real mode) OR fixture lookup (stub
121
+ mode).
122
+
123
+ ## Doctor check
124
+
125
+ Verifies `node` is on PATH and the bridge file is present (mirror
126
+ `playwright-lib::tool_doctor_check`). Reports node version and the
127
+ `mcp_server_bin` name. Includes a `note` field stating real-mode MCP
128
+ transport is deferred to part 1c.
129
+
130
+ To install (real-mode, once part 1c lands):
131
+
132
+ ```bash
133
+ npm i -g chrome-devtools-mcp
134
+ # or run via npx (no global install):
135
+ # CHROME_DEVTOOLS_MCP_BIN='npx chrome-devtools-mcp@latest' \
136
+ # bash scripts/browser-<verb>.sh --tool=chrome-devtools-mcp ...
137
+ ```
138
+
139
+ ## Version pin
140
+
141
+ - `version_pin: "0.x"` — the upstream package is pre-1.0; capabilities are
142
+ expected to drift. The pin will move once a stable major lands.
143
+
144
+ ## Override
145
+
146
+ Force this adapter even when the router would pick another:
147
+
148
+ ```bash
149
+ bash scripts/browser-<verb>.sh --tool=chrome-devtools-mcp ...
150
+ ```
151
+
152
+ This is the **Path A entry point** — it works without router edits and is
153
+ how every new adapter is introduced (see
154
+ [references/recipes/add-a-tool-adapter.md](recipes/add-a-tool-adapter.md)).
155
+
156
+ ## Stub mode
157
+
158
+ Set `BROWSER_SKILL_LIB_STUB=1` to make the bridge perform a fixture lookup
159
+ instead of spawning the upstream MCP server (which the bridge doesn't yet do
160
+ anyway — that's part 1c). The bridge hashes argv (`sha256` of args
161
+ joined+terminated by NUL — matches `printf '%s\0' "$@" | shasum -a 256`) and
162
+ echoes the corresponding `tests/fixtures/chrome-devtools-mcp/<sha>.json`.
163
+ Misses exit 41 with a JSON error line.
164
+
165
+ To regenerate fixture filenames after changing the adapter's argv translation:
166
+
167
+ ```bash
168
+ printf '%s\0' inspect --capture-console | shasum -a 256 | awk '{print $1}'
169
+ ```
170
+
171
+ The same digest in node (the bridge's path):
172
+
173
+ ```bash
174
+ node -e "const{createHash}=require('crypto'); \
175
+ console.log(createHash('sha256') \
176
+ .update(['inspect','--capture-console'].map(a=>a+'\0').join('')) \
177
+ .digest('hex'))"
178
+ ```
179
+
180
+ ## Environment variables
181
+
182
+ | Var | Meaning | Default |
183
+ |---|---|---|
184
+ | `BROWSER_SKILL_LIB_STUB` | When `=1`, bridge skips MCP transport and reads fixtures | unset |
185
+ | `BROWSER_SKILL_NODE_BIN` | Node binary the adapter invokes | `node` |
186
+ | `CHROME_DEVTOOLS_MCP_BIN` | Upstream MCP server binary the bridge spawns in real mode (part 1c) | `chrome-devtools-mcp` |
187
+ | `CHROME_DEVTOOLS_MCP_FIXTURES_DIR` | Override fixture directory in stub mode | `tests/fixtures/chrome-devtools-mcp` (relative to bridge file) |
188
+ | `STUB_LOG_FILE` | When set, bridge in stub mode appends each invocation's argv (one line per arg) here | unset |
189
+ | `CHROME_USER_DATA_DIR` | When set (phase-5 part 1f), bridge forwards `--user-data-dir DIR` to the spawned upstream MCP child. Chrome reuses the profile (cookies, localStorage, extensions persist). | unset |
190
+
191
+ ## Session loading (phase-5 part 1f)
192
+
193
+ cdt-mcp's session mechanism is **Chrome's native `--user-data-dir`**, NOT
194
+ playwright-lib's `storageState` JSON. To reuse a logged-in profile:
195
+
196
+ ```bash
197
+ # 1. Log in once with real Chrome at a known directory:
198
+ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
199
+ --user-data-dir=/tmp/my-profile https://app.example.com
200
+
201
+ # 2. Now point cdt-mcp at it:
202
+ export CHROME_USER_DATA_DIR=/tmp/my-profile
203
+ bash scripts/browser-snapshot.sh --capture-console
204
+ ```
205
+
206
+ The bridge forwards the directory as `--user-data-dir DIR` to the upstream
207
+ MCP server child. If upstream chrome-devtools-mcp accepts the flag (most
208
+ versions do — it's a standard Chrome arg), Chrome reuses the profile.
209
+
210
+ **Limitations:**
211
+ - User provides the directory. Capture / automation of user-data-dir creation is out of scope (no `bash scripts/browser-login.sh --user-data-dir-mode` yet).
212
+ - Concurrent runs sharing the same profile dir → Chrome lock conflicts. Serialize.
213
+ - Upstream chrome-devtools-mcp version dictates whether the flag is honored.
214
+
215
+ ## Limitations (current state)
216
+
217
+ - **No router promotion.** Per anti-pattern AP-4, this PR ships dark only.
218
+ Promotion is part 1d.
219
+ - **No `inspect` / `extract` verbs yet.** `scripts/browser-audit.sh` and
220
+ `scripts/browser-extract.sh` don't exist; `tests/browser-inspect.bats` is
221
+ still skipped. Verb-side wiring (and daemon dispatch for these two) is
222
+ phase-05 part 1e.
223
+ - **No session loading.** Chrome's `--user-data-dir` mechanism (different
224
+ from playwright-lib's `storageState`) is phase-05 part 1f.
225
+
226
+ ## See also
227
+
228
+ - Parent spec: [`docs/superpowers/specs/2026-04-27-browser-automation-skill-design.md`](../docs/superpowers/specs/2026-04-27-browser-automation-skill-design.md) — Appendix B routing matrix.
229
+ - Add-a-tool-adapter recipe: [`references/recipes/add-a-tool-adapter.md`](recipes/add-a-tool-adapter.md).
230
+ - Anti-patterns: [`references/recipes/anti-patterns-tool-extension.md`](recipes/anti-patterns-tool-extension.md).
231
+ - Token-efficient output spec: [`docs/superpowers/specs/2026-05-01-token-efficient-adapter-output-design.md`](../docs/superpowers/specs/2026-05-01-token-efficient-adapter-output-design.md).
232
+ - Phase 5 part 1b plan: [`docs/superpowers/plans/2026-05-02-phase-05-part-1b-cdt-mcp-bridge.md`](../docs/superpowers/plans/2026-05-02-phase-05-part-1b-cdt-mcp-bridge.md).