scenescout 1.0.0 โ†’ 1.2.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # scenescout
2
2
 
3
+ ## 1.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 2932164: `scenescout install --browsers <list>` chooses what to download: `chromium` (the default, unchanged), `chromium-headless-shell` for the smallest working setup, `firefox`, `webkit`, or `all`. `scout_attach` takes a `browser` option, and `SCENESCOUT_BROWSER` sets the default. In Firefox and WebKit the engine keeps service workers from registering, because a request issued by one cannot be intercepted there and would pass the write policy. The missing-browser message and `doctor` now name the build that is actually missing, and the sizes quoted are the sizes on disk.
8
+
9
+ Pages are no longer given shared workers unless the mode is `destructive`. A request a shared worker sends cannot be intercepted in any browser, and a `DELETE` sent from one reached the server in read-only mode.
10
+
11
+ Hover no longer reports text that was already on the page and only moved to a new line, and the keyboard focus audit uses Option+Tab in WebKit on macOS, where plain Tab skips buttons and links.
12
+ - ad0f31d: `scenescout install --client <list>` registers the server with clients other than Claude Code: `cursor`, `vscode`, `codex`, `gemini`, `copilot` and `windsurf`. Clients that have a command for adding a server are registered through it; Cursor and Windsurf get an entry added to their JSON server list, with every other entry kept and an unreadable file left untouched. For VS Code, a `code` command that belongs to another editor is not used. The skill is installed only when `claude-code` is among the clients.
13
+ - 5504c63: The testing method now reaches every MCP client, not only Claude Code. A new `scout_playbook` tool returns it, the server's instructions tell an agent to call that tool before its first attach, and an `explore` prompt loads the method together with the target URL for clients that list server prompts as commands. It is the same text Claude Code loads as a skill, read from the same file.
14
+
15
+ ### Patch Changes
16
+
17
+ - d0aa6aa: The package description, keywords and README now present SceneScout as a tool for any MCP client, with Claude Code as one of them. `doctor --engine` ends with what to ask an agent instead of a Claude Code command.
18
+
19
+ ## 1.1.0
20
+
21
+ ### Minor Changes
22
+
23
+ - d3ebd73: Snapshots now list images that failed to load, under `BROKEN IMAGES`, read from the DOM. This catches an image whose URL answers 200 with something that is not an image, which the HTTP oracle cannot see because no request failed. Images that occupy no space (inside a closed panel, tracking pixels) are not reported.
24
+
25
+ An `<img>` is now named by its alt text and listed with the role `image`; it previously appeared as `generic "(unnamed)"`. For the uncommon `<img>` that is collected without a `data-testid` (one with `onclick` or an explicit role), this changes its element key, so states containing it are seen as new once.
26
+ - 5706a55: `scout_scan` now reads routes from source for React Router, Vue Router and Angular projects, including nested children, `<Route>` elements and Angular `loadChildren` files. These projects previously started with an empty route list and relied on link discovery alone, so a page nothing linked to was outside the completion contract. The reader is static and skips anything it cannot resolve: computed paths, spreads, identifiers, and relative paths whose parent is unknown.
27
+ - 6b6b786: The geometry oracle now reports a pinned control that sits underneath other pinned chrome, for example a sticky Save row covered by a fixed bar. Box overlap cannot tell which of two pinned elements is on top, so that pair was skipped; the new check hit-tests the control's centre in the page. It stays quiet for controls inside a scrollable pane, for dialogs, and for overlays covering half the viewport.
28
+ - 0d320b1: New write mode `observe` (`--observe`): nothing but `GET`, `HEAD` and `OPTIONS` requests leaves the page, except logging in, logging out and refreshing a token. Signing up and password changes are blocked. WebSocket frames are not inspected, and the engine says so when the app opens one. `read-only` lets an ordinary form `POST` through, which on a target holding real data creates a record. The skill now attaches in `observe` for a remote URL with no source unless told that form submissions are acceptable. Forms that could not be submitted stay in the gap ledger, worded as the mode's doing.
29
+
30
+ ### Patch Changes
31
+
32
+ - 0d320b1: The login exemption in the write policy no longer applies to destructive-looking requests in any mode. A path that merely contained a word such as `session` or `auth` previously carried a request like `POST /api/session/123/delete` through `read-only`. A form navigation blocked by the write policy is now reported as blocked; it was reported as an off-origin navigation, and the follow-up note blamed the app for discarding data.
33
+ - 4107696: `scenescout doctor` now suggests `npx -y scenescout install` when the tool was installed from npm. It previously suggested `npm run setup`, which exists only in a source checkout.
34
+
3
35
  ## 1.0.0
4
36
 
5
37
  ### Major Changes
package/README.md CHANGED
@@ -2,9 +2,12 @@
2
2
 
3
3
  # ๐Ÿ”ญ SceneScout
4
4
 
5
- **Exploratory UI testing, driven by an AI agent.**
5
+ **Exploratory UI testing, driven by the AI agent you already use.**
6
+
7
+ Works with Claude Code ยท Cursor ยท VS Code (Copilot) ยท Codex CLI ยท Gemini CLI ยท Copilot CLI ยท Windsurf ยท any [MCP](https://modelcontextprotocol.io) client
6
8
 
7
9
  [![test](https://github.com/brunoboto96/SceneScout/actions/workflows/test.yml/badge.svg)](https://github.com/brunoboto96/SceneScout/actions/workflows/test.yml)
10
+ [![npm](https://img.shields.io/npm/v/scenescout.svg)](https://www.npmjs.com/package/scenescout)
8
11
  [![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
9
12
  ![node >= 20](https://img.shields.io/badge/node-%E2%89%A5%2020-339933?logo=node.js&logoColor=white)
10
13
  ![MCP server](https://img.shields.io/badge/MCP-server-8A2BE2)
@@ -13,11 +16,11 @@
13
16
 
14
17
  </div>
15
18
 
16
- SceneScout is an [MCP](https://modelcontextprotocol.io) server that hands an agent a *structured view* of a running web app โ€” every element, its geometry, and a set of always-on correctness oracles โ€” and lets the agent explore it like a curious user. Claude Code is the brain; SceneScout is the hands, eyes, and memory.
19
+ SceneScout is an [MCP](https://modelcontextprotocol.io) server that hands an agent a *structured view* of a running web app โ€” every element, its geometry, and a set of always-on correctness oracles โ€” and lets the agent explore it like a curious user. Your coding agent is the brain; SceneScout is the hands, eyes, and memory. Any MCP client can drive it, and the testing method comes with the server, so the agent knows how to use the tools wherever it runs.
17
20
 
18
21
  ```
19
22
  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” MCP (stdio) โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
20
- โ”‚ Claude Code + skill โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ โ”‚ SceneScout engine โ”‚
23
+ โ”‚ Your coding agent โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ โ”‚ SceneScout engine โ”‚
21
24
  โ”‚ (intent, judgment, โ”‚ โ—€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚ Playwright ยท oracles ยท memory โ”‚
22
25
  โ”‚ your subscription) โ”‚ tool results โ”‚ findings ยท report โ€” no LLM โ”‚
23
26
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
@@ -28,9 +31,11 @@ Scripted E2E suites answer one question โ€” *"does this exact flow still work?"*
28
31
 
29
32
  ## ๐Ÿ‘€ See it work
30
33
 
31
- This is a real run against the small demo app bundled in this repository. The screenshot was taken by SceneScout; the badge sitting on top of a button and the missing chart are two of the nine findings it filed.
34
+ This is a real run against the small demo app bundled in this repository. The app has bugs planted in it on purpose, and two of them are visible on its dashboard:
35
+
36
+ <p align="center"><img src="examples/screenshots/dashboard-annotated.png" alt="The demo app's dashboard with two defects outlined in red: 1, a yellow badge covering the All orders button; 2, the weekly chart image failing to load" width="760" /></p>
32
37
 
33
- <p align="center"><img src="examples/screenshots/dashboard.png" alt="The demo app's dashboard: a yellow badge covers the All orders button, and the weekly chart image is broken" width="720" /></p>
38
+ **The broken chart is the demo app's bug, not this page's** โ€” it is one of the ten findings SceneScout filed, next to the badge sitting on a button. The red callouts were added for this README; the [unmarked screenshots](examples/screenshots/) are the ones the engine took.
34
39
 
35
40
  An excerpt of the report it wrote โ€” [read the whole thing](examples/report.md):
36
41
 
@@ -41,19 +46,22 @@ An excerpt of the report it wrote โ€” [read the whole thing](examples/report.md)
41
46
  > **๐Ÿ”ด [HIGH] Filtering orders by Archived fails, and the page shows an empty table instead of an error**
42
47
  > Evidence: `GET /api/orders?status=archived โ†’ HTTP 500`
43
48
  >
44
- > **๐ŸŸ  [MEDIUM] The "New: bulk import" badge sits on top of the All orders button**
49
+ > **๐ŸŸ  [MEDIUM] The "New: bulk import" badge sits on top of the All orders button** *(callout 1)*
45
50
  > Evidence: `"All orders" overlaps "New: bulk import" (81%)` โ€” measured from layout boxes, no screenshot needed.
46
51
  >
52
+ > **๐ŸŸก [LOW] The dashboard chart image is missing** *(callout 2)*
53
+ > Evidence: `GET /img/weekly-chart.png โ†’ HTTP 404`
54
+ >
47
55
  > **Gap ledger โ€” what was NOT tested:** 4/7 visited routes never design-audited ยท single-role run, so permission boundaries are untested
48
56
 
49
- Every finding comes with a repro trace and a Playwright regression-test skeleton. Try it yourself in two minutes: `npm run demo:serve`, then `/scenescout --url http://127.0.0.1:4173` โ€” see [demo-app/](demo-app/). It also documents [one defect the tool deliberately does not flag](demo-app/README.md#what-it-deliberately-does-not-catch).
57
+ Every finding comes with a repro trace and a Playwright regression-test skeleton. To try it yourself, clone this repository, run `npm run demo:serve`, then `/scenescout --url http://127.0.0.1:4173` โ€” see [demo-app/](demo-app/). Its README lists every seeded defect and which oracle catches it.
50
58
 
51
59
  ---
52
60
 
53
61
  ## โœจ Why it's different
54
62
 
55
- - ๐Ÿง  **Claude is the brain โ€” no API key.** The engine contains no LLM. Exploration runs on your Claude Code subscription; SceneScout just gives it deterministic tools.
56
- - ๐Ÿ“ **Structured scene, not pixels.** The agent reads element lists *with layout geometry*, not screenshots. Overlap and off-screen bugs are computed from boxes โ€” deterministic, no vision guessing. (Screenshots exist only for pixel-native residue like broken images.)
63
+ - ๐Ÿง  **Your agent is the brain โ€” no API key.** The engine contains no LLM. Exploration runs on the agent and subscription you already have (Claude Code, Cursor, Copilot, Codex, Gemini CLI and others); SceneScout just gives it deterministic tools and the method for using them.
64
+ - ๐Ÿ“ **Structured scene, not pixels.** The agent reads element lists *with layout geometry*, not screenshots. Overlap and off-screen bugs are computed from boxes โ€” deterministic, no vision guessing. Images that failed to load are read from the DOM too. (Screenshots exist only for pixel-native residue like a canvas or a rendering glitch.)
57
65
  - ๐Ÿ›ก๏ธ **Read-only by default, enforced on the wire.** Destructive actions are blocked at the network layer, not by asking the model nicely. Opt into writes only against disposable data.
58
66
  - โœ… **Completion is a contract, not a vibe.** The engine knows the app's routes and *refuses* to file an "extensive" report while any known route is unvisited, unexercised, or un-audited. "Explored a bit and stopped" is structurally impossible.
59
67
  - ๐Ÿงญ **It remembers.** UI states are fingerprinted and stored in the project's `.scenescout/`. Run N+1 skips what run N already covered, and every run starts smarter than the last.
@@ -68,13 +76,13 @@ SceneScout needs only a URL. Give it the source code as well and it gets noticea
68
76
  |---|---|---|
69
77
  | **You run it from** | the app's repository | any folder โ€” an empty `qa/` directory is fine |
70
78
  | **It plays the role of** | a developer-tester who can read the code | a black-box QA tester, like a person with a browser |
71
- | **How it finds pages** | ๐Ÿ“‚ reads file-based routes from the source (Next.js, SvelteKit, Nuxt) **and** follows links. Code-routed apps fall back to links | ๐Ÿ”— follows same-origin links only โ€” pages nothing links to, or on another subdomain, stay unknown |
79
+ | **How it finds pages** | ๐Ÿ“‚ reads routes from the source **and** follows links: file-based routing (Next.js, SvelteKit, Nuxt) and router configuration written in code (React Router, Vue Router, Angular). Routes built at runtime are not seen | ๐Ÿ”— follows same-origin links only โ€” pages nothing links to, or on another subdomain, stay unknown |
72
80
  | **"Did we cover everything?"** | checked against the routes found in source *plus* discovered links โ€” an unvisited one blocks the report | checked against the pages it managed to discover |
73
81
  | **Setup it figures out** | framework, dev command, saved Playwright logins (`playwright/.auth/`), whether the app uses `data-testid` | none โ€” you pass the URL, and the path to a login state if the app needs one |
74
82
  | **What a finding looks like** | the symptom, **plus** the file behind it and a suggested fix | the symptom, a repro trace, and a regression-test skeleton |
75
83
  | **Typical target** | `localhost` while you build | staging, a preview deploy, a client's site |
76
84
 
77
- **Why the codebase helps.** The agent driving SceneScout is Claude Code, which can already read your repository. With the source at hand โ€” and a file-routed framework โ€” it knows the app's static routes before opening the browser, so coverage is measured against the real app instead of whatever happened to be linked. It can also check a suspicion against the code before reporting it: "there is no way to export this table" is a much stronger finding once the agent has confirmed no export handler exists. And when something breaks it can open the component or handler responsible and tell you *where* and *how* to fix it โ€” "the save button does nothing" becomes "`OrderForm` swallows the rejected promise in `onSubmit`; surface the error and re-enable the button".
85
+ **Why the codebase helps.** The agent driving SceneScout is a coding agent, which can already read your repository. With the source at hand it knows the app's static routes before opening the browser, so coverage is measured against the real app instead of whatever happened to be linked. It can also check a suspicion against the code before reporting it: "there is no way to export this table" is a much stronger finding once the agent has confirmed no export handler exists. And when something breaks it can open the component or handler responsible and tell you *where* and *how* to fix it โ€” "the save button does nothing" becomes "`OrderForm` swallows the rejected promise in `onSubmit`; surface the error and re-enable the button".
78
86
 
79
87
  **Why it still works without it.** Everything SceneScout *observes* comes from the running page โ€” elements, layout geometry, console and network errors, design-audit scores, task-ease measurements โ€” and none of that needs source code. Point it at a URL you are allowed to test and it behaves like a thorough QA tester: it explores, reproduces, and files findings with evidence.
80
88
 
@@ -87,7 +95,7 @@ SceneScout needs only a URL. Give it the source code as well and it gets noticea
87
95
  ```
88
96
 
89
97
  > [!IMPORTANT]
90
- > Only test sites you own or are authorized to test. A remote environment is more likely to hold real data. The default **read-only** mode blocks `PUT`/`PATCH`/`DELETE` and destructive-looking requests, but an ordinary form submission (a plain `POST`: contact form, comment, order, signup) still reaches the server and can create a record. On a site with real data, tell the agent which forms not to submit. See the [safety model](#-safety-model).
98
+ > Only test sites you own or are authorized to test. A remote environment is more likely to hold real data, so for a remote URL with no source the skill attaches in **`observe`** mode: nothing but `GET` requests leaves the page. The default **read-only** mode blocks `PUT`/`PATCH`/`DELETE` and destructive-looking requests, but an ordinary form submission (a plain `POST`: contact form, comment, order, signup) still reaches the server and can create a record. Say so when that is acceptable on your target. See the [safety model](#-safety-model).
91
99
 
92
100
  ---
93
101
 
@@ -98,44 +106,37 @@ SceneScout needs only a URL. Give it the source code as well and it gets noticea
98
106
  | | |
99
107
  |---|---|
100
108
  | **Node** | โ‰ฅ 20 |
101
- | **An MCP client** | [Claude Code](https://claude.ai/code) is the first-class one (it loads the skill); [others work too](#-other-mcp-clients) |
109
+ | **An MCP client** | Claude Code, Cursor, VS Code with Copilot, Codex CLI, Gemini CLI, GitHub Copilot CLI, Windsurf, or [any other](#-other-mcp-clients) |
102
110
  | **A web app to test** | SceneScout tests a *live* app: start yours locally first (e.g. `npm run dev`, `make dev-up`), or have the URL of a deployed one you're allowed to test |
103
111
 
104
112
  ### 1๏ธโƒฃ Install
105
113
 
106
- Pick one. All three end with the same 24 tools.
114
+ It is on npm. Nothing to clone:
107
115
 
108
- **A ยท Claude Code plugin** โ€” the skill and the server in one step:
109
-
110
- ```
111
- /plugin marketplace add brunoboto96/SceneScout
112
- /plugin install scenescout@scenescout-marketplace
116
+ ```bash
117
+ npx -y scenescout install # Claude Code: skill + server + Chromium (one-time download)
118
+ npx -y scenescout install --client cursor # or: vscode, codex, gemini, copilot, windsurf (comma-separated for several)
113
119
  ```
114
120
 
115
- Then download the browser once: `npx -y scenescout install --browser-only`. The command is `/scenescout:scenescout`.
121
+ Either way it downloads the browser and registers the server with the client you named. Claude Code also gets the method as a skill; every other client receives the same method from the server. [What each client gets](#-other-mcp-clients).
116
122
 
117
- **B ยท npm, for Claude Code or any other MCP client:**
123
+ **Prefer a Claude Code plugin?** The skill and the server arrive together:
118
124
 
119
- ```bash
120
- npx -y scenescout install # skill + Chromium (~150 MB, one-time) + registers the server with Claude Code
125
+ ```
126
+ /plugin marketplace add brunoboto96/SceneScout
127
+ /plugin install scenescout@scenescout-marketplace
121
128
  ```
122
129
 
123
- Using a different client? Skip the registration and [add the server to its config](#-other-mcp-clients) instead: `npx -y scenescout install --browser-only`.
124
-
125
- **C ยท From source**, to hack on it:
130
+ Then download the browser once with `npx -y scenescout install --browser-only`. The command becomes `/scenescout:scenescout`. A plugin's skill comes from this repository and its server from the latest npm release, so right after a release lands here the two can differ for a short while; `/plugin marketplace update scenescout-marketplace` brings the skill up to date.
126
131
 
127
- ```bash
128
- git clone https://github.com/brunoboto96/SceneScout.git scenescout && cd scenescout
129
- npm install # installs dependencies and builds
130
- npm run setup # same as `scenescout install`, pointed at this checkout
131
- ```
132
+ **A client that is not in that list?** Run `npx -y scenescout install --browser-only` and [add the server to its config by hand](#-other-mcp-clients).
132
133
 
133
134
  <details>
134
- <summary>What <code>install</code> / <code>npm run setup</code> actually does</summary>
135
+ <summary>What <code>install</code> actually does</summary>
135
136
 
136
- 1. links the `/scenescout` skill into `~/.claude/skills/` (or `$CLAUDE_CONFIG_DIR/skills/`) โ€” a `scenescout` folder it didn't create is moved aside to a `.backup-โ€ฆ` copy, never deleted. When run through `npx` it copies instead of linking, because the npx cache is temporary,
137
- 2. downloads the Chromium build SceneScout drives (skipped if you already have it),
138
- 3. registers the MCP server with Claude Code at user scope, using an **absolute** node path so it works under nvm/fnm.
137
+ 1. puts the `/scenescout` skill into `~/.claude/skills/` (or `$CLAUDE_CONFIG_DIR/skills/`) โ€” a `scenescout` folder it didn't create is moved aside to a `.backup-โ€ฆ` copy, never deleted,
138
+ 2. downloads the browser SceneScout drives (skipped if you already have it). By default that is Chromium, as two builds: the full browser for headed runs and the headless shell every other run uses. [Choose something else](#-choosing-browsers) with `--browsers`,
139
+ 3. registers the MCP server with Claude Code at user scope. Run through `npx`, the launcher is `npx -y scenescout serve`, with the absolute path of `npx` where one sits beside node, so it works under nvm/fnm. From a clone or a global install it is the absolute node path plus that install's `dist/mcp-server.js`.
139
140
 
140
141
  Re-run it any time: after moving the folder or switching node versions it refreshes the stored paths. It exits non-zero if any step failed, so it is safe to chain. Opt out of a step with `--no-register` or `--skip-browser`.
141
142
 
@@ -150,25 +151,31 @@ claude mcp add --scope user scenescout -- npx -y scenescout serve
150
151
  ### 2๏ธโƒฃ Check it
151
152
 
152
153
  ```bash
153
- npx -y scenescout doctor # routes B and C (from source: npm run doctor)
154
- npx -y scenescout doctor --engine # route A, or another MCP client: node + build + browser only
154
+ npx -y scenescout doctor --engine # any client: node + build + browser
155
+ npx -y scenescout doctor # Claude Code: the above, plus the skill and the registration
155
156
  ```
156
157
 
157
158
  Every line should be a โœ“. Anything that isn't prints the exact command that fixes it. Then **start a fresh session** in your client so it picks up the new tools.
158
159
 
159
160
  ### 3๏ธโƒฃ Run it
160
161
 
161
- No app handy? `npm run demo:serve` in a source checkout starts the [demo app](demo-app/) on `http://127.0.0.1:4173`.
162
+ No app handy? Clone this repository and run `npm run demo:serve`: the [demo app](demo-app/) starts on `http://127.0.0.1:4173`.
162
163
 
163
- From Claude Code, inside the project you want to test (or, for a [remote URL](#-two-ways-to-use-it), any folder):
164
+ Open your agent inside the project you want to test (or, for a [remote URL](#-two-ways-to-use-it), any folder) and ask:
165
+
166
+ ```
167
+ Use SceneScout to test http://localhost:3000 at medium level
168
+ ```
169
+
170
+ In Claude Code the skill gives you a command with flags for the same thing:
164
171
 
165
172
  ```
166
173
  /scenescout --level medium --url http://localhost:3000 --role qa
167
174
  ```
168
175
 
169
- The skill scans the project (if there is one), attaches read-only, explores, and writes findings to `.scenescout/report.md`. That's it.
176
+ The agent scans the project (if there is one), attaches read-only, explores, and writes findings to `.scenescout/report.md`. That's it.
170
177
 
171
- **Common flags** โ€” `--level minimal|medium|extensive` ยท `--url <app>` ยท `--role <name\|path>` (a Playwright storage-state to explore as: a name found by the scan, or a path to the JSON file) ยท `--safe-write` / `--allow-destructive`.
178
+ **Common flags** โ€” `--level minimal|medium|extensive` ยท `--url <app>` ยท `--role <name\|path>` (a Playwright storage-state to explore as: a name found by the scan, or a path to the JSON file) ยท `--observe` / `--safe-write` / `--allow-destructive`.
172
179
 
173
180
  ---
174
181
 
@@ -196,11 +203,11 @@ Snapshots are cheap: re-snapshotting a route returns only *what changed*, with s
196
203
 
197
204
  ## ๐Ÿงฐ The toolbox
198
205
 
199
- 24 deterministic tools. The agent picks; you rarely call these by hand.
206
+ 25 deterministic tools. The agent picks; you rarely call these by hand.
200
207
 
201
208
  | Phase | Tools | What they do |
202
209
  |---|---|---|
203
- | **Set up** | `scout_scan` `scout_attach` `scout_session` | Discover routes; launch a browser in a write-mode; keep several authenticated roles alive at once |
210
+ | **Set up** | `scout_playbook` `scout_scan` `scout_attach` `scout_session` | Hand the testing method to an agent that has no skill loaded; discover routes; launch a browser in a write-mode; keep several authenticated roles alive at once |
204
211
  | **Explore** | `scout_crawl` `scout_coverage` | Sweep every route in one call; ask what's still untested |
205
212
  | **Look** | `scout_snapshot` `scout_hover` `scout_screenshot` | Read the structured scene (diffed); reveal tooltips/hover cards; capture pixels only when needed |
206
213
  | **Act** | `scout_click` `scout_type` `scout_select` `scout_upload` `scout_press` `scout_scroll` `scout_navigate` `scout_back` `scout_run_plan` | Drive the UI like a user; `scout_run_plan` batches a whole mechanical sequence into one call |
@@ -234,6 +241,7 @@ That refusal *is* the guarantee: an extensive report can only exist when nothing
234
241
 
235
242
  ## ๐Ÿ”’ Safety model
236
243
 
244
+ - ๐Ÿ”ต **`observe`** (`--observe`) lets nothing but `GET` requests leave the page. The one exception is what a session needs in order to exist: logging in, logging out and refreshing a token. Signing up, changing or resetting a password and creating users are blocked like any other write. WebSocket frames are not inspected; the engine says so when the app opens a socket. It is what the skill picks for a remote URL with no source, where an ordinary form POST would create a real record. Forms that could not be submitted are listed in the gap ledger.
237
245
  - ๐ŸŸข **`read-only` by default.** Destructive-labeled elements (delete/revoke/archive/โ€ฆ) **and** all `PUT/PATCH/DELETE` + destructive `POST`s are blocked at the network layer โ€” see [`src/engine/policy.ts`](src/engine/policy.ts). Non-destructive `POST`s are allowed, because submitting forms is how a tester finds validation bugs โ€” so read-only means *nothing existing is changed or removed*, not *nothing is ever created*.
238
246
  - ๐ŸŸก **`safe-write`** (`--safe-write`) lets the agent create data and edit/delete **only what it created** this run โ€” never pre-existing records.
239
247
  - ๐Ÿ”ด **`destructive`** (`--allow-destructive`) allows everything, and only ever when *you* confirm the environment is disposable. The skill will never choose this itself.
@@ -258,37 +266,89 @@ A `๐Ÿ›ก WRITE-POLICY blocked` notice is the safety net doing its job, not an app
258
266
 
259
267
  ## ๐Ÿฉบ Troubleshooting
260
268
 
261
- Run `npm run doctor` first โ€” it checks every setup item below (everything but the last row, which is about your app) and prints the fix.
269
+ Run `npx -y scenescout doctor` first โ€” it checks every setup item below (everything but the last row, which is about your app) and prints the fix.
262
270
 
263
271
  | Symptom | Cause and fix |
264
272
  |---|---|
265
- | `/scenescout` isn't a known command | The skill isn't linked, or the session predates it. `npm run setup`, then start a **fresh** Claude Code session. |
266
- | The `scout_*` tools don't appear | The MCP server isn't registered, or points at an old path. `npm run setup` re-registers it; `claude mcp list` should show `scenescout` as connected. |
267
- | `npm install` fails at the build step | The build needs the dev dependencies (TypeScript). Don't pass `--omit=dev` or set `NODE_ENV=production` when installing from a clone. |
268
- | *"Executable not found in $PATH"* | The server was registered with a bare `node`. `npm run setup` registers the absolute path. |
269
- | *"Executable doesn't exist โ€ฆ chromium"* | The browser download was skipped or failed. `npx playwright install chromium` (on Linux add `--with-deps`). |
270
- | Tools broke after moving the folder or changing node version | The registration stores absolute paths. `npm run setup` refreshes them. |
273
+ | `/scenescout` isn't a known command | The skill isn't linked, or the session predates it. `npx -y scenescout install`, then start a **fresh** Claude Code session. |
274
+ | The `scout_*` tools don't appear | The MCP server isn't registered, or points at an old path. `npx -y scenescout install` re-registers it; `claude mcp list` should show `scenescout` as connected. |
275
+ | *"Executable not found in $PATH"* | The server was registered with a bare `node`. `npx -y scenescout install` registers an absolute path. |
276
+ | Installed as a plugin, and the tools fail with *"Executable not found in $PATH: npx"* | A plugin starts the server with a bare `npx`, which Claude Code can only find if it was launched from an environment that has Node on its `PATH`. Under nvm or fnm that means starting Claude Code from a terminal, not from a dock or launcher. Or use `npx -y scenescout install` instead, which registers the absolute path of `npx`. |
277
+ | *"โ€ฆ build has not been downloaded yet"* on attach | The browser download was skipped or failed, or the run asked for a browser you did not install. Run the command the message names, for example `npx -y scenescout install --browser-only --browsers firefox`. On Linux, system libraries may be missing too: `npx playwright install --with-deps chromium`. |
278
+ | Tools broke after moving the folder or changing node version | The registration stores absolute paths. `npx -y scenescout install` refreshes them. |
271
279
  | Attach fails or every route lands on the login page | Your app isn't running at `--url`, or the `--role` storage state has expired โ€” regenerate it the way your project's Playwright setup does. |
272
280
 
273
281
  ### โฌ†๏ธ Upgrading from an older version
274
282
 
275
- - **Tools are now `scout_*`.** Up to v0.23 they were prefixed `ft_`. The rename happened before the first npm release, with no aliases, so an agent's context carries one tool list rather than two. Re-run `npm run setup` so the installed skill matches the server.
276
- - **Earlier names.** This tool was previously called SceneCraft (and, before that, frontend-tester). `npm run setup` cleans up after both: it removes the old skill link and the old `scenecraft` MCP registration when they point at this install, and the first attach in a project moves its `.scenecraft/` memory folder to `.scenescout/` so earlier coverage and findings carry over.
283
+ - **Tools are now `scout_*`.** Up to v0.23 they were prefixed `ft_`. The rename happened before the first npm release, with no aliases, so an agent's context carries one tool list rather than two. Re-run `npx -y scenescout install` so the installed skill matches the server.
284
+ - **Earlier names.** This tool was previously called SceneCraft (and, before that, frontend-tester). `scenescout install` cleans up after both: it removes the old skill link and the old `scenecraft` MCP registration when they point at this install, and the first attach in a project moves its `.scenecraft/` memory folder to `.scenescout/` so earlier coverage and findings carry over.
277
285
 
278
286
  ### ๐Ÿงน Uninstall
279
287
 
280
288
  ```bash
289
+ # Claude Code
281
290
  claude mcp remove --scope user scenescout
282
291
  rm -rf ~/.claude/skills/scenescout
292
+ # Codex / Gemini / Copilot CLI
293
+ codex mcp remove scenescout # likewise: gemini mcp remove โ€ฆ, copilot mcp remove โ€ฆ
283
294
  ```
284
295
 
285
- Then delete the clone. Per-project memory lives in each tested project's `.scenescout/` folder; delete it there if you want it gone.
296
+ For Cursor, Windsurf and VS Code, delete the `scenescout` entry from the client's MCP server list.
297
+
298
+ Nothing else is installed: `npx` runs the package from npm's cache. Per-project memory lives in each tested project's `.scenescout/` folder; delete it there if you want it gone.
286
299
 
287
300
  ---
288
301
 
302
+ ## ๐ŸŒ Choosing browsers
303
+
304
+ `install` downloads Chromium and nothing else unless you ask. `--browsers` takes one name, a comma-separated list, or `all`:
305
+
306
+ | `--browsers` | What is downloaded | About, on disk |
307
+ |---|---|---|
308
+ | `chromium` *(default)* | the full browser and the headless shell | 550 MB |
309
+ | `chromium-headless-shell` | the headless shell only: every run works except `headed` | 200 MB |
310
+ | `firefox` | Firefox | 270 MB |
311
+ | `webkit` | WebKit, the engine behind Safari | 290 MB |
312
+ | `all` | Chromium, Firefox and WebKit | 1.1 GB |
313
+
314
+ ```bash
315
+ npx -y scenescout install --browsers chromium-headless-shell # the smallest working setup
316
+ npx -y scenescout install --browser-only --browsers firefox,webkit # add two more later
317
+ ```
318
+
319
+ Sizes vary by platform. The builds go to Playwright's shared cache, so a build another tool already fetched is not downloaded again.
320
+
321
+ To drive another browser, pass `browser` when attaching (`scout_attach { browser: "firefox" }`), or set `SCENESCOUT_BROWSER=webkit` in the server's environment to change the default. `scenescout doctor` checks the browser named by that variable in the shell it runs from, so check another one with `SCENESCOUT_BROWSER=webkit scenescout doctor`. Two things differ outside Chromium:
322
+
323
+ - **Service workers are not allowed to register** in Firefox and WebKit. The write policy works by intercepting requests, and only Chromium lets a request issued by a service worker be intercepted. An app that depends on its worker may behave differently there.
324
+ - **A Firefox or WebKit left behind by a crash is not cleaned up** on the next start the way a leftover Chromium is.
325
+
326
+ In every browser, pages are not given shared workers unless the mode is `destructive`: a request a shared worker sends cannot be intercepted anywhere, so the app is made to do that work on the page, where the policy sees it.
327
+
289
328
  ## ๐Ÿ”Œ Other MCP clients
290
329
 
291
- The engine is a plain MCP server over stdio, so any client can drive it. The server entry is always the same command โ€” `npx -y scenescout serve` โ€” only the config file differs. Download the browser once with `npx -y scenescout install --browser-only`.
330
+ The engine is a plain MCP server over stdio, so any client can drive it, and the testing method reaches the agent through the server itself (see the end of this section). `install` can register it for you:
331
+
332
+ ```bash
333
+ npx -y scenescout install --client cursor # one client
334
+ npx -y scenescout install --client vscode,codex # several; add claude-code to keep that one too
335
+ ```
336
+
337
+ | `--client` | How it is registered |
338
+ |---|---|
339
+ | `claude-code` *(default)* | `claude mcp add`, plus the skill |
340
+ | `cursor` | adds an entry to `~/.cursor/mcp.json`, keeping the others |
341
+ | `vscode` | VS Code's own `code --add-mcp`. A `code` command that belongs to another editor is not used |
342
+ | `codex` | `codex mcp add` |
343
+ | `gemini` | `gemini mcp add --scope user` |
344
+ | `copilot` | `copilot mcp add` (GitHub Copilot CLI) |
345
+ | `windsurf` | adds an entry to `~/.codeium/windsurf/mcp_config.json`, keeping the others |
346
+
347
+ A config file that is not valid JSON is left untouched, and the entry to add by hand is printed instead; a config that is a link into a dotfiles repository is written through the link. When a client that is registered through its own command is not installed, `install` says so and prints the command to run later. Cursor and Windsurf are files, so their entry is written whether or not the editor is installed yet. On Windows, a client installed through npm is a `.cmd` shim that `install` cannot start; it prints the command for you to run instead. Then restart the client and ask its agent: *"Use SceneScout to test http://localhost:3000"*.
348
+
349
+ What has been checked: registering through each command above was run against Codex CLI, Gemini CLI, GitHub Copilot CLI and VS Code, and Cursor's command line agent read the entry `install` wrote, connected and listed the tools. The Windsurf path follows its documentation. A full test session has been run in Claude Code, with and without the skill. If a client behaves differently for you, a correction is welcome (say which client version you checked).
350
+
351
+ To register by hand instead, the server entry is always the same command, `npx -y scenescout serve`:
292
352
 
293
353
  <details>
294
354
  <summary><strong>Cursor</strong> โ€” <code>~/.cursor/mcp.json</code> (or <code>.cursor/mcp.json</code> in a project)</summary>
@@ -327,14 +387,71 @@ args = ["-y", "scenescout", "serve"]
327
387
 
328
388
  </details>
329
389
 
390
+ <details>
391
+ <summary><strong>Gemini CLI</strong> โ€” <code>~/.gemini/settings.json</code> (or <code>.gemini/settings.json</code> in a project)</summary>
392
+
393
+ ```json
394
+ {
395
+ "mcpServers": {
396
+ "scenescout": { "command": "npx", "args": ["-y", "scenescout", "serve"] }
397
+ }
398
+ }
399
+ ```
400
+
401
+ </details>
402
+
403
+ <details>
404
+ <summary><strong>Windsurf</strong> โ€” <code>~/.codeium/windsurf/mcp_config.json</code></summary>
405
+
406
+ ```json
407
+ {
408
+ "mcpServers": {
409
+ "scenescout": { "command": "npx", "args": ["-y", "scenescout", "serve"] }
410
+ }
411
+ }
412
+ ```
413
+
414
+ </details>
415
+
416
+ <details>
417
+ <summary><strong>Cline</strong> โ€” MCP Servers โ†’ Configure โ†’ Configure MCP Servers (or <code>~/.cline/mcp.json</code> for the CLI)</summary>
418
+
419
+ ```json
420
+ {
421
+ "mcpServers": {
422
+ "scenescout": { "command": "npx", "args": ["-y", "scenescout", "serve"], "disabled": false, "autoApprove": [] }
423
+ }
424
+ }
425
+ ```
426
+
427
+ </details>
428
+
429
+ <details>
430
+ <summary><strong>Zed</strong> โ€” <code>settings.json</code> (command palette: <code>zed: open settings file</code>)</summary>
431
+
432
+ ```json
433
+ {
434
+ "context_servers": {
435
+ "scenescout": { "command": "npx", "args": ["-y", "scenescout", "serve"], "env": {} }
436
+ }
437
+ }
438
+ ```
439
+
440
+ </details>
441
+
330
442
  <details>
331
443
  <summary><strong>Anything else</strong></summary>
332
444
 
333
- Most clients accept the same `mcpServers` JSON shape shown for Cursor. From a source checkout, the command is `node` with the absolute path to `dist/mcp-server.js`.
445
+ Most clients accept the same `mcpServers` JSON shape shown for Cursor.
334
446
 
335
447
  </details>
336
448
 
337
- **The skill is what makes it good.** The tools are only hands and eyes; [`skills/scenescout/SKILL.md`](skills/scenescout/SKILL.md) is the method โ€” what to look at first, when to stop, what counts as a finding. Claude Code loads it as a skill. In another client, give the agent that file as its instructions (a rule, a custom mode, or pasted into the first message).
449
+ **The method travels with the server.** The tools are only hands and eyes; [`skills/scenescout/SKILL.md`](skills/scenescout/SKILL.md) is the method: what to look at first, when to stop, what counts as a finding. Claude Code loads it as a skill. Every other client gets the same text from the server, with nothing to copy:
450
+
451
+ - the server's instructions tell the agent to call `scout_playbook` before its first attach, and that tool returns the method,
452
+ - clients that list server prompts as commands also get an `explore` prompt, which loads the method and takes an optional URL, level and focus.
453
+
454
+ So in any client, a first message like *"Use SceneScout to test http://localhost:3000"* is enough. If an agent starts clicking without having called `scout_playbook`, tell it to call that first; how closely a model follows server instructions varies by client.
338
455
 
339
456
  The CLI is also useful on its own:
340
457
 
@@ -349,7 +466,7 @@ npx -y scenescout status <path> # what a running engine is doing right now
349
466
 
350
467
  ```
351
468
  src/
352
- mcp-server.ts the 24 tools + per-session dispatch
469
+ mcp-server.ts the 25 tools + per-session dispatch
353
470
  scan.ts project discovery (framework, routes, auth)
354
471
  cli.ts scan ยท serve ยท install ยท doctor ยท status
355
472
  installer.ts setup logic (skill link, MCP registration, diagnostics)
@@ -366,9 +483,9 @@ src/
366
483
  memory.ts cross-run storage + finding dedup
367
484
  report.ts the gap ledger + report generation
368
485
  โ€ฆ collector ยท dispatch ยท fixtures ยท authloss ยท reaper
369
- scripts/ the 11 test suites (smoke/ holds the real-browser ones)
486
+ scripts/ the 12 test suites (smoke/ holds the real-browser ones)
370
487
  test-app/ fixtures for the real-browser smoke tests
371
- skills/scenescout/ the Claude Code skill (SKILL.md)
488
+ skills/scenescout/ the testing method (SKILL.md): a skill in Claude Code, served by the server everywhere else
372
489
  docs/adr/ why it's built this way
373
490
  ```
374
491
 
@@ -391,14 +508,18 @@ The load-bearing choices are recorded as ADRs โ€” read the relevant one before c
391
508
 
392
509
  ## ๐Ÿ”ง Development
393
510
 
511
+ Working on SceneScout itself is the only reason to clone it:
512
+
394
513
  ```bash
395
- npm run build # tsc
396
- npm test # build + 11 suites: scan, oracle, policy, fixture, dispatch, design,
397
- # contract, memory, install, smoke, mcp-check
398
- npm run dev # run the CLI from source (tsx)
514
+ git clone https://github.com/brunoboto96/SceneScout.git scenescout && cd scenescout
515
+ npm install # installs dependencies and builds
516
+ npm run setup # same as `scenescout install`, but registers THIS checkout (the skill is linked, so edits are live)
517
+ npm test # build + 12 suites: scan, oracle, policy, fixture, dispatch, design,
518
+ # contract, memory, install, hygiene, smoke, mcp-check
519
+ npm run demo # regenerate examples/ from the demo app
399
520
  ```
400
521
 
401
- Contributing? Start with [VISION.md](VISION.md) (what is in scope) and [CONTRIBUTING.md](CONTRIBUTING.md) (how changes land), then see [CLAUDE.md](CLAUDE.md) for the house rules โ€” chiefly: bug fixes need a regression test at the cheapest layer that can fail, keep the repo project-agnostic (ADR 6), and `npm run build && npm test` must pass before committing.
522
+ Contributing? Start with [VISION.md](VISION.md) (what is in scope) and [CONTRIBUTING.md](CONTRIBUTING.md) (how changes land), then see [CLAUDE.md](CLAUDE.md) for the house rules โ€” chiefly: bug fixes need a regression test at the cheapest layer that can fail, keep the repo project-agnostic (ADR 6), and `npm test` must pass.
402
523
 
403
524
  ## ๐Ÿ” Security
404
525
 
@@ -411,7 +532,7 @@ Found a way past the write policy, or another security problem? Please report it
411
532
  <details>
412
533
  <summary><strong>Full capability list</strong> โ€” every behavior, for the curious</summary>
413
534
 
414
- - **Structured render-state, not pixels.** Element lists with geometry; screenshots reserved for pixel-native residue (broken images, canvas).
535
+ - **Structured render-state, not pixels.** Element lists with geometry; screenshots reserved for pixel-native residue (canvas, rendering glitches). Images that failed to load are reported from the DOM, including ones whose URL answered 200 with something that is not an image.
415
536
  - **Diff snapshots with stable refs.** Re-snapshots return only what changed (10.7 kB โ†’ 0.7 kB on a 130-element page); old refs stay valid.
416
537
  - **Geometry oracles.** Overlap and off-screen defects computed from layout boxes.
417
538
  - **Oracles after every action.** Console errors, page errors, failed requests, HTTP 4xx/5xx drained into every tool result.