skeptic-cli 0.2.1 → 1.0.1

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 (79) hide show
  1. package/AGENTS.md +20 -53
  2. package/LICENSE +24 -0
  3. package/LICENSES.md +92 -3
  4. package/README.md +25 -49
  5. package/agent-skills/skeptic/SKILL.md +150 -38
  6. package/dist/adb-DUGGW3FV.mjs +2 -0
  7. package/dist/adb-driver-TBOCCKEO.mjs +3 -0
  8. package/dist/adb-session-AVVXL3QQ.mjs +2 -0
  9. package/dist/add-G7JFXU4S.mjs +67 -0
  10. package/dist/audit-ID2BSVYC.mjs +7 -0
  11. package/dist/auto-spawn-4TO4DBO6.mjs +2 -0
  12. package/dist/browser-verbs-R7R6PK2G.mjs +21 -0
  13. package/dist/browsers-install-F2ZWEBOX.mjs +3 -0
  14. package/dist/chokidar-KB6GLGX4.mjs +7 -0
  15. package/dist/chunk-2N64R5DC.mjs +457 -0
  16. package/dist/chunk-2VGKPSCR.mjs +8 -0
  17. package/dist/chunk-2VSGDT7T.mjs +7 -0
  18. package/dist/chunk-2YKSIUIN.mjs +3 -0
  19. package/dist/chunk-2ZORFJJP.mjs +11 -0
  20. package/dist/chunk-42J77CYA.mjs +3 -0
  21. package/dist/chunk-6TLKI7UN.mjs +4 -0
  22. package/dist/chunk-6U6H22OR.mjs +3 -0
  23. package/dist/chunk-7BFRKEFV.mjs +160 -0
  24. package/dist/chunk-7ZUWKIDM.mjs +3 -0
  25. package/dist/chunk-AH75LR2T.mjs +3 -0
  26. package/dist/chunk-B26AZRXU.mjs +41 -0
  27. package/dist/chunk-BIGNULF6.mjs +12 -0
  28. package/dist/chunk-COVGJJ47.mjs +3 -0
  29. package/dist/chunk-CWNYWHJ2.mjs +143 -0
  30. package/dist/chunk-EU3OXJX4.mjs +3 -0
  31. package/dist/chunk-EYVJTUBL.mjs +3 -0
  32. package/dist/chunk-G22LGRZ4.mjs +4 -0
  33. package/dist/chunk-I4JX25Y5.mjs +13 -0
  34. package/dist/chunk-IXBYLSGB.mjs +5 -0
  35. package/dist/chunk-IYLF56WL.mjs +3 -0
  36. package/dist/chunk-K65JNLTT.mjs +3 -0
  37. package/dist/chunk-LPJF33QP.mjs +3 -0
  38. package/dist/chunk-MHNEFL35.mjs +3 -0
  39. package/dist/chunk-N3533BCE.mjs +3 -0
  40. package/dist/chunk-NXTEMSUR.mjs +4 -0
  41. package/dist/chunk-OHVNABCL.mjs +10 -0
  42. package/dist/chunk-QICYK6XT.mjs +10 -0
  43. package/dist/chunk-RU7M6UGM.mjs +5 -0
  44. package/dist/chunk-S3M2RTHJ.mjs +7 -0
  45. package/dist/chunk-U3KRIAEU.mjs +3 -0
  46. package/dist/chunk-YB25SMQ2.mjs +141 -0
  47. package/dist/chunk-ZN6MI2TU.mjs +4 -0
  48. package/dist/client-UR65IKYX.mjs +2 -0
  49. package/dist/comment-F734YE5S.mjs +6 -0
  50. package/dist/cookies-X7W5U3VE.mjs +3 -0
  51. package/dist/daemon-7M5HNFD7.mjs +7 -0
  52. package/dist/device-fixture-MJSDIP75.mjs +2 -0
  53. package/dist/devices-NG4P5UPS.mjs +4 -0
  54. package/dist/devtools-N5AYAR54.mjs +12 -0
  55. package/dist/doctor-Q76JEQQI.mjs +4 -0
  56. package/dist/extractor-Y477MBN6.mjs +2 -0
  57. package/dist/html-reporter-7QHIRHEY.mjs +2 -0
  58. package/dist/index.d.ts +87 -120
  59. package/dist/index.mjs +26 -1741
  60. package/dist/init-6RHO2LGR.mjs +15 -0
  61. package/dist/ink-reporter-4EN7CRMK.mjs +3 -0
  62. package/dist/inspect-44KF3IPH.mjs +19 -0
  63. package/dist/ios-tools-WK66CQ7Q.mjs +2 -0
  64. package/dist/json-reporter-SXWPAXRY.mjs +2 -0
  65. package/dist/junit-reporter-5AT3OFWV.mjs +12 -0
  66. package/dist/mail-GTOZFXJ5.mjs +14 -0
  67. package/dist/observe-EDXYWK6C.mjs +3 -0
  68. package/dist/render-MHOBDOSP.mjs +383 -0
  69. package/dist/run-GUWITPL4.mjs +2 -0
  70. package/dist/scaffold-6ZF6K6Y5.mjs +38 -0
  71. package/dist/screenshot-GUJSIRQB.mjs +2 -0
  72. package/dist/session-daemon-cmd-QHVSSKSL.mjs +4 -0
  73. package/dist/simctl-driver-PNUN7W7G.mjs +5 -0
  74. package/dist/skeptic.mjs +3 -1754
  75. package/dist/slack-reporter-EEFO66V6.mjs +8 -0
  76. package/dist/watch-A7ZLUYR2.mjs +3 -0
  77. package/dist/webhook-reporter-BXJGZS2I.mjs +3 -0
  78. package/dist/worker.mjs +3 -724
  79. package/package.json +13 -11
package/AGENTS.md CHANGED
@@ -27,8 +27,8 @@ spec.
27
27
 
28
28
  The npm package installs a managed `skeptic` skill for Claude Code, Codex,
29
29
  Cursor, and OpenCode into user-level skill directories when `npm install` runs.
30
- Use that skill when an agent needs browser QA, spec authoring, or MCP browser
31
- tool guidance.
30
+ Use that skill when an agent needs browser QA, spec authoring, or interactive
31
+ browser-session driving.
32
32
 
33
33
  Manual install commands:
34
34
 
@@ -78,7 +78,6 @@ Rules for generated or hand-written specs:
78
78
  | `screenshot(name, opts?)` | PNG capture with optional ref annotations |
79
79
  | `settle()` | Best-effort network-idle settle |
80
80
  | `observability` | Performance, network, console, and accessibility assertions |
81
- | `ai` | Vision-backed assertions, defect checks, and text extraction |
82
81
  | `ctx` | Per-test execution context |
83
82
 
84
83
  ### Snapshot Helpers
@@ -185,60 +184,28 @@ Annotated screenshots add numbered labels over interactive refs and return an
185
184
  annotation map without accessible names, so structured metadata does not repeat
186
185
  potentially sensitive page text.
187
186
 
188
- ## AI
187
+ ## Browser Session Verbs
189
188
 
190
- Configure a provider and key before using AI helpers.
191
-
192
- ```yaml
193
- ai:
194
- provider: openai
195
- model: gpt-4o
196
- ```
197
-
198
- ```ts
199
- await ai.assert("the success toast is visible");
200
- await ai.assertNoDefects();
201
- const total = await ai.extract("invoice total");
202
- ```
203
-
204
- Use:
189
+ Skeptic is agent-native — you drive a persistent browser from the shell, no MCP
190
+ server and no built-in AI/keys. A daemon holds the session so `@eN` refs persist
191
+ between commands:
205
192
 
206
193
  ```bash
207
- skeptic generate --message "test checkout"
208
- skeptic generate --diff
209
- skeptic run --analyze
210
- ```
211
-
212
- Generated tests are typechecked and imported before being written.
213
-
214
- ## MCP Browser Tools
215
-
216
- When Skeptic is exposed through MCP, prefer its browser tools for page QA:
217
-
218
- | Tool | Use |
219
- |---|---|
220
- | `browser_open` | Navigate using project browser/auth/safety config |
221
- | `browser_snapshot` | Capture refs and snapshot text |
222
- | `browser_playwright` | Run focused Playwright code with `page`, `context`, `browser`, `ref` |
223
- | `browser_screenshot` | Capture PNG, annotated PNG, or snapshot-only output |
224
- | `browser_console_logs` | Read captured console messages |
225
- | `browser_network_requests` | Read requests and computed issues |
226
- | `browser_performance_metrics` | Capture Web Vitals, LoAF, resources, and `perf-trace.md` |
227
- | `browser_accessibility_audit` | Run axe-core plus IBM Equal Access when available |
228
- | `browser_close` | Close the session |
229
-
230
- MCP browser tools honor:
231
-
232
- ```yaml
233
- safety:
234
- allowedDomains: ["example.com", "*.example.org"]
235
- actionPolicy: .skeptic/action-policy.json
236
- confirmActions: ["browser_playwright"]
237
- maxOutputChars: 120000
238
- contentBoundaries: true
194
+ skeptic open https://app.example.com # opens a session
195
+ skeptic snapshot -i # mints @e1.. refs + selectorHints
196
+ skeptic click @e3
197
+ skeptic fill @e5 "user@test.com"
198
+ skeptic snapshot -i # re-snapshot after the DOM changed
199
+ skeptic console --errors
200
+ skeptic screenshot --full # returns a file path
201
+ skeptic close
239
202
  ```
240
203
 
241
- `confirmActions` fail closed in MCP because stdio tools cannot prompt safely.
204
+ Verbs: `open`, `snapshot` (`-i`/`-c`), `click`, `fill`, `type`, `press`, `hover`,
205
+ `check`, `uncheck`, `select`, `get`, `screenshot`, `console`, `wait`, `list`,
206
+ `close`. Add `--json` to any verb; `--session <name>` for isolated sessions;
207
+ `--headless` on `open` for CI. Re-snapshot after navigation — refs invalidate on
208
+ DOM change and acting on a stale ref returns a clear `[ariaRef:stale]` error.
242
209
 
243
210
  ## Evidence
244
211
 
@@ -284,7 +251,7 @@ output:
284
251
 
285
252
  ```bash
286
253
  skeptic add github-action
287
- skeptic add github-action --ai --provider openai
254
+ skeptic add github-action --dev-command "npm run dev" --dev-url http://localhost:3000
288
255
  skeptic comment --results ./skeptic-output/results.json
289
256
  ```
290
257
 
package/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 iamjr15
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
22
+
23
+ Third-party components redistributed inside the published bundle are listed,
24
+ with their own license terms and attributions, in LICENSES.md.
package/LICENSES.md CHANGED
@@ -1,9 +1,98 @@
1
1
  # Third-party licenses and attributions
2
2
 
3
3
  skeptic-cli is distributed under the [MIT License](#mit-license-skeptic-cli)
4
- (see `package.json:license`). Portions of skeptic are derived from upstream
5
- projects under their own terms; those terms and the corresponding NOTICE
6
- attributions follow.
4
+ (see `package.json:license`). Two kinds of third-party material are covered
5
+ below: (1) npm dependencies that are **inlined into the published bundle**
6
+ (`dist/skeptic.mjs`) at build time, and (2) source code **derived from upstream
7
+ projects**. Their terms and the corresponding NOTICE attributions follow.
8
+
9
+ ## Bundled npm dependencies
10
+
11
+ The published `skeptic-cli` bundle inlines several pure-JS dependencies at build
12
+ time — the `noExternal` list in `tsup.config.ts` — instead of resolving them
13
+ from `node_modules` at runtime. Their license terms are reproduced or pointed to
14
+ here. Runtime-external dependencies (`playwright`, `playwright-core`, and the
15
+ optional `better-sqlite3` and `accessibility-checker-engine`) are installed
16
+ separately and keep their own license files in your `node_modules`.
17
+
18
+ ### Mozilla Public License 2.0 — axe-core, @axe-core/playwright
19
+
20
+ The accessibility audit inlines **axe-core** (via its Playwright wrapper
21
+ **@axe-core/playwright**), © Deque Systems, Inc., licensed under the
22
+ **Mozilla Public License, Version 2.0 (MPL-2.0)**.
23
+
24
+ MPL-2.0 is a file-level copyleft: because skeptic distributes these files in
25
+ executable (bundled) form, the corresponding **source is made available** to
26
+ recipients at:
27
+
28
+ - `axe-core` — https://github.com/dequelabs/axe-core (npm: `axe-core@4.11.x`)
29
+ - `@axe-core/playwright` — https://github.com/dequelabs/axe-core-npm
30
+ (npm: `@axe-core/playwright@4.11.x`)
31
+
32
+ The full MPL-2.0 text is available at https://www.mozilla.org/en-US/MPL/2.0/.
33
+ Per MPL-2.0 Exhibit A, the covered source is governed by this notice:
34
+
35
+ ```
36
+ This Source Code Form is subject to the terms of the Mozilla Public
37
+ License, v. 2.0. If a copy of the MPL was not distributed with this
38
+ file, You can obtain one at https://mozilla.org/MPL/2.0/.
39
+ ```
40
+
41
+ ### Apache License 2.0 — web-vitals
42
+
43
+ **web-vitals** © Google LLC is licensed under the Apache License 2.0 and is
44
+ shipped both inlined and as the standalone `dist/web-vitals.iife.js` asset.
45
+ Source: https://github.com/GoogleChrome/web-vitals. The full Apache 2.0 text is
46
+ reproduced in the [agent-browser section below](#apache-license-20-full-text).
47
+
48
+ ### Apache License 2.0 — accessibility-checker-engine
49
+
50
+ **accessibility-checker-engine** (IBM Equal Access Accessibility Checker)
51
+ © IBM, Inc. is an **optional, runtime-external** dependency — resolved from
52
+ `node_modules` by `src/observability/collectors/accessibility-collector.ts`
53
+ when present (the audit falls back to axe-core only when it is absent), not
54
+ inlined into the bundle — licensed under the **Apache License 2.0**. Source:
55
+ https://github.com/IBMa/equal-access. The full Apache 2.0 text is reproduced in
56
+ the [agent-browser section below](#apache-license-20-full-text).
57
+
58
+ ### Blue Oak Model License 1.0.0 — glob, minimatch
59
+
60
+ **glob** and **minimatch** © Isaac Z. Schlueter and contributors are licensed
61
+ under the **Blue Oak Model License 1.0.0**. Full text:
62
+ https://blueoakcouncil.org/license/1.0.0.
63
+
64
+ ### ISC License — yaml, pixelmatch
65
+
66
+ **yaml** © Eemeli Aro and **pixelmatch** © Mapbox are licensed under the ISC
67
+ license (a permissive, functionally-MIT-equivalent license). Full texts ship in
68
+ each package's `node_modules` distribution.
69
+
70
+ ### MIT License — commander, zod, chokidar, chalk, figures, cli-truncate, string-width, pretty-ms, pngjs, react, ink, ink-spinner
71
+
72
+ The following bundled dependencies are licensed under the MIT License and carry
73
+ their own copyright notices in their respective packages:
74
+
75
+ | Package | Copyright holder |
76
+ |---|---|
77
+ | commander | TJ Holowaychuk and contributors |
78
+ | zod | Colin McDonnell |
79
+ | chokidar | Paul Miller |
80
+ | chalk | Sindre Sorhus |
81
+ | figures | Sindre Sorhus |
82
+ | cli-truncate | Sindre Sorhus |
83
+ | string-width | Sindre Sorhus |
84
+ | pretty-ms | Sindre Sorhus |
85
+ | pngjs | the pngjs contributors |
86
+ | react | Meta Platforms, Inc. and affiliates |
87
+ | ink | Vadim Demedes |
88
+ | ink-spinner | Vadim Demedes |
89
+
90
+ Each MIT/ISC/BlueOak license is permissive and grants redistribution provided
91
+ the copyright notice and permission text are preserved; those texts are
92
+ distributed inside each package and reproduced under the canonical license URLs
93
+ cited above. (skeptic carries no bundled AI/MCP/ACP SDKs — those dependencies
94
+ and their attributions were removed when the AI, MCP, and ACP subsystems were
95
+ deleted.)
7
96
 
8
97
  ## NOTICE — agent-browser (Apache License 2.0)
9
98
 
package/README.md CHANGED
@@ -43,8 +43,8 @@ skeptic observe https://example.com
43
43
  # Run TypeScript specs with full QA evidence
44
44
  skeptic run tests/homepage.spec.ts --observability --video --trace
45
45
 
46
- # Generate a validated TypeScript spec with AI
47
- skeptic generate -m "test the login page"
46
+ # Drive a browser interactively from the shell (persistent session)
47
+ skeptic open https://example.com && skeptic snapshot -i
48
48
 
49
49
  # Check local setup
50
50
  skeptic doctor
@@ -121,7 +121,6 @@ The fixture exposes:
121
121
  | `screenshot` | PNG screenshots, including annotated numbered-ref captures |
122
122
  | `settle` | Network-idle settle helper |
123
123
  | `observability` | Performance, network, console, and accessibility assertions |
124
- | `ai` | Vision-backed assertions, defect checks, and text extraction |
125
124
  | `ctx` | Per-test execution context and artifact paths |
126
125
 
127
126
  `expect` is re-exported from Playwright Test, so matchers like
@@ -249,53 +248,31 @@ It writes an output directory containing an HTML report, JSON report,
249
248
  screenshots, annotated screenshots, snapshot text/JSON, console/network data,
250
249
  performance summary, accessibility JSON, and an accessibility markdown audit.
251
250
 
252
- ## AI Features
251
+ ## Browser Session Verbs
253
252
 
254
- Skeptic supports Gemini, OpenAI, and Anthropic.
253
+ Skeptic is agent-native: a coding agent drives a persistent browser entirely from
254
+ the shell. A daemon holds the session, so `@eN` refs from one `skeptic snapshot`
255
+ stay valid for the next `skeptic click @eN` — across separate commands. There is
256
+ no MCP/ACP server and no built-in AI: the host agent is the intelligence.
255
257
 
256
- ```yaml
257
- ai:
258
- provider: openai
259
- model: gpt-4o
258
+ ```bash
259
+ skeptic open https://app.example.com # opens a session (default name "default")
260
+ skeptic snapshot -i # mints @e1.. refs + stable selectorHints
261
+ skeptic click @e3 # act on a ref from the last snapshot
262
+ skeptic fill @e5 "user@test.com"
263
+ skeptic snapshot -i # re-snapshot after the DOM changed
264
+ skeptic console --errors # check for uncaught errors
265
+ skeptic screenshot --full # returns a file path
266
+ skeptic close
260
267
  ```
261
268
 
262
- Set the matching provider key with `GEMINI_API_KEY`, `OPENAI_API_KEY`, or
263
- `ANTHROPIC_API_KEY`. You can also use `SKEPTIC_AI_PROVIDER` and
264
- `SKEPTIC_AI_API_KEY` to override config in CI.
265
-
266
- Available AI paths:
267
-
268
- - `ai.assert("the dashboard greets the user")`
269
- - `ai.assertNoDefects()`
270
- - `ai.extract("the invoice total")`
271
- - `skeptic generate --message "test checkout"`
272
- - `skeptic generate --diff`
273
- - `skeptic run --analyze`
274
-
275
- Generated specs are typechecked and imported before being written.
276
-
277
- ## MCP And ACP
278
-
279
- `skeptic mcp` exposes testing and browser QA tools over stdio:
280
-
281
- | Tool | Purpose |
282
- |---|---|
283
- | `list_tests` | Discover specs |
284
- | `validate_tests` | Typecheck and import-check specs |
285
- | `generate_test` | Generate a validated TypeScript spec |
286
- | `run_test` | Run specs and stream progress |
287
- | `browser_open` | Open a page with config-driven browser/auth/safety |
288
- | `browser_snapshot` | Capture ARIA/cursor refs |
289
- | `browser_playwright` | Run focused Playwright code |
290
- | `browser_screenshot` | Capture PNG, annotated PNG, or snapshot-only output |
291
- | `browser_console_logs` | Read console messages |
292
- | `browser_network_requests` | Read requests and computed issues |
293
- | `browser_performance_metrics` | Capture Web Vitals, LoAF, resources, and `perf-trace.md` |
294
- | `browser_accessibility_audit` | Run axe-core plus IBM Equal Access when available |
295
- | `browser_close` | Close the browser session |
296
-
297
- `skeptic acp` exposes a testing-focused agent server for editors that support
298
- Agent Client Protocol.
269
+ Verbs: `open`, `snapshot` (`-i`/`-c`), `click`, `fill`, `type`, `press`, `hover`,
270
+ `check`, `uncheck`, `select`, `get <text|box|url|title> [@ref]`, `screenshot`
271
+ (`--full`/`--annotate`), `console` (`--errors`), `wait`, `list`, `close`
272
+ (`--all`). Add `--json` to any verb. Use `--session <name>` for isolated parallel
273
+ sessions; the session browser defaults to headed (`--headless` for CI/containers).
274
+ Refs are minted per snapshot and invalidated by navigation — re-snapshot after any
275
+ navigation or DOM change.
299
276
 
300
277
  ## Configuration
301
278
 
@@ -354,7 +331,7 @@ contexts and are not sent to Skeptic services.
354
331
 
355
332
  ```bash
356
333
  skeptic add github-action
357
- skeptic add github-action --ai --provider openai
334
+ skeptic add github-action --dev-command "npm run dev" --dev-url http://localhost:3000
358
335
  ```
359
336
 
360
337
  The generated workflow installs dependencies, installs Chromium, starts your dev
@@ -393,8 +370,7 @@ skeptic daemon stop
393
370
  ```
394
371
 
395
372
  `skeptic doctor` checks config, output directories, browser installs, optional
396
- accessibility/cookie engines, daemon state, cookie profiles, and AI provider
397
- setup.
373
+ accessibility/cookie engines, daemon state, and cookie profiles.
398
374
 
399
375
  ## License
400
376
 
@@ -1,44 +1,138 @@
1
1
  ---
2
2
  name: skeptic
3
- description: Use Skeptic for CLI-first browser QA and TypeScript E2E tests. Use when asked to inspect pages, write or run skeptic-cli specs, validate UI changes, capture observability evidence, or use Skeptic MCP tools. Not for unit-only logic with no browser behavior.
3
+ description: Use Skeptic for CLI-first browser QA and TypeScript E2E tests. Use when asked to inspect pages, drive a browser interactively, write or run skeptic-cli specs, validate UI changes, or capture observability evidence. Not for unit-only logic with no browser behavior.
4
4
  ---
5
5
 
6
6
  <!-- skeptic-agent-skill: managed by skeptic-cli -->
7
7
 
8
8
  # Skeptic
9
9
 
10
- Use Skeptic when a coding agent needs browser evidence: page inspection, TypeScript E2E specs, one-off QA captures, AI-backed checks, or MCP browser validation. Do not claim a UI/browser change works until you have run a relevant Skeptic command or MCP tool and checked the evidence.
10
+ Use Skeptic when you need browser evidence: interactive page-driving, page inspection, TypeScript E2E specs, one-off QA captures, or observability evidence. Do not claim a UI/browser change works until you have run a relevant Skeptic command and checked the evidence.
11
+
12
+ Skeptic is **agent-native**: it has no model of its own, makes no LLM calls, and needs no API keys. You (the coding agent) are the intelligence; Skeptic is the deterministic hands and eyes. Everything is driven from the shell — there is no MCP server.
11
13
 
12
14
  ## Choose The Surface
13
15
 
14
- - Human interactive test run: run `skeptic tui`.
15
- - One-off QA or bug hunt: run `skeptic observe <url> --full-page`.
16
- - Persistent regression coverage: run `skeptic inspect <url> --interactive --compact --with-playwright-hints`, write a `tests/*.spec.ts`, then run `skeptic run`.
17
- - Changed-code verification: run existing specs with `skeptic run`, or use `skeptic generate --diff` to create one first.
18
- - Agent-integrated browser work: if Skeptic MCP tools are available, use `browser_open`, `browser_snapshot`, `browser_playwright`, `browser_screenshot`, `browser_console_logs`, `browser_network_requests`, `browser_performance_metrics`, `browser_accessibility_audit`, and `browser_close`.
16
+ - **Drive a browser interactively** (click through a flow, check a fix): use the persistent session verbs — `skeptic open <url>`, `skeptic snapshot -i`, `skeptic click @e3`, etc. Refs persist between commands.
17
+ - **One-off discovery** (get stable selectors to write a spec): `skeptic inspect <url> --interactive --compact --with-playwright-hints`.
18
+ - **One-off QA / bug hunt** (full evidence bundle for one page): `skeptic observe <url> --full-page --video --trace`.
19
+ - **Persistent regression coverage**: inspect, write a `tests/*.spec.ts`, then `skeptic run`.
20
+ - **Changed-code verification**: run existing specs with `skeptic run`.
19
21
 
20
22
  If the `skeptic` binary is not on PATH, try `npx skeptic-cli` or `npx --yes skeptic-cli@latest`.
21
23
 
22
- Specs import from the project dependency `skeptic-cli`. A normal `skeptic init`
23
- writes that dependency to `package.json`. If specs fail with `Cannot find
24
- package 'skeptic-cli'`, run `npm install` in the project before re-running
25
- Skeptic.
24
+ Specs import the project dependency `skeptic-cli`. A normal `skeptic init` writes that dependency to `package.json`. If specs fail with `Cannot find package 'skeptic-cli'`: in an initialized project run `npm install`; in a project that never ran `skeptic init`, run `skeptic init` first, then `npm install`.
25
+
26
+ ## Persistent Browser Session
27
+
28
+ A daemon holds the browser, so `@eN` refs from one `skeptic snapshot` stay valid for the next `skeptic click @eN` — across separate commands. The loop:
29
+
30
+ ```bash
31
+ skeptic open https://app.example.com # opens a session (default name "default")
32
+ skeptic snapshot -i # mints @e1.. refs + stable selectorHints
33
+ skeptic click @e3 # act on a ref from the last snapshot
34
+ skeptic fill @e5 "user@test.com"
35
+ skeptic snapshot -i # RE-SNAPSHOT after the DOM changed
36
+ skeptic console --errors # check for uncaught errors
37
+ skeptic screenshot --full # returns a file path
38
+ skeptic close # end the session
39
+ ```
40
+
41
+ Verbs: `open`, `snapshot` (`-i` interactive, `-c` compact), `click`, `fill`, `type`, `press`, `hover`, `check`, `uncheck`, `select`, `get <text|box|url|title> [@ref]`, `screenshot` (`--full`, `--annotate`), `console` (`--errors`), `wait` (`--ms` or `--selector`), `list`, `close` (`--all`). Add `--json` to any verb for machine-readable output.
26
42
 
27
- ## Fast Loop
43
+ Rules:
44
+
45
+ - **Re-snapshot after any navigation, route change, modal open/close, or DOM mutation.** Refs are minted per snapshot and invalidated by navigation; acting on a stale ref returns a clear `[ariaRef:stale]` error — re-run `skeptic snapshot`.
46
+ - Prefer `@eN` refs from the latest snapshot; for selectors, use the `selectorHint` grammar (`role=button:Save`, `text=...`, `css=...`, `testid=...`).
47
+ - Use `--session <name>` for parallel isolated sessions.
48
+ - The session browser defaults to headed for local debugging; pass `--headless` on the first `open` for headless environments (CI/containers).
49
+ - Binary outputs (screenshots) come back as file paths, not inline data.
50
+
51
+ ## Mobile (Android + iOS simulator)
52
+
53
+ The same verbs drive a native app on a device. **Android** (`--platform android`)
54
+ via `adb`; **iOS simulator** (`--platform ios-sim`) via `simctl` + `axe` (the idb
55
+ accessibility-framework redistribution). Both are driver-less — no Appium, no
56
+ WebDriverAgent, no in-app test bundle. `open` takes an app id (Android package /
57
+ iOS bundle id) or a deep link instead of a URL.
58
+
59
+ ### Android (`--platform android`)
28
60
 
29
61
  ```bash
30
- skeptic doctor --quick
31
- skeptic inspect <url> --interactive --compact --with-playwright-hints
32
- skeptic run tests/<scenario>.spec.ts --observability --video --trace
62
+ skeptic open com.example.app --platform android # launches the app (am start -W)
63
+ skeptic snapshot -i # uiautomator tree → @eN refs
64
+ skeptic click @e5 # taps the node's center
65
+ skeptic fill @e3 "user@test.com" # ASCII input
66
+ skeptic is enabled @e5 # element state (visible|enabled)
67
+ skeptic screenshot # device screencap → file path
68
+ skeptic record --duration 5 # screenrecord → .mp4
69
+ skeptic perf # gfxinfo jank + meminfo PSS + launch ms
70
+ skeptic a11y # uiautomator a11y heuristics
71
+ skeptic network # per-uid byte totals (degraded)
72
+ skeptic console --errors # logcat (app-filtered)
73
+ skeptic close
33
74
  ```
34
75
 
35
- For a page with no existing spec:
76
+ Device evidence (parallels the web collectors; read via the verbs above):
77
+
78
+ - **perf** — `dumpsys gfxinfo` (total/janky frames, frame-time percentiles, missed
79
+ vsync) + `meminfo` (PSS/RSS) + `am start -W` launch timings. A distinct shape from
80
+ web vitals (`platform: "android"`).
81
+ - **a11y** — STRUCTURAL uiautomator heuristics only (unlabeled clickables, sub-48dp
82
+ touch targets, NAF nodes). No color-contrast check — the dump has no pixels.
83
+ - **network** — `degraded: true` by default: Android exposes only per-uid byte totals,
84
+ never per-request URLs/status. Per-request capture needs an opt-in proxy.
85
+ - `is checked` / `get value` aren't available on Android (the node doesn't retain them)
86
+ — they return a structured `[adbQuery:*_unsupported]` error; re-`snapshot` to read state.
87
+
88
+ ### iOS simulator (`--platform ios-sim`)
89
+
90
+ Same verbs, against a **booted** simulator (`skeptic devices` lists them; boot with
91
+ `xcrun simctl boot <udid>`). Requires a full **Xcode** (not just Command Line Tools)
92
+ and **`axe`** (`brew install cameroncooke/axe/axe`) — skeptic finds Xcode itself, so
93
+ no `sudo xcode-select` needed.
36
94
 
37
95
  ```bash
38
- skeptic observe <url> --full-page --video --trace
96
+ skeptic open com.apple.Preferences --platform ios-sim # bundle id or scheme:// link
97
+ skeptic snapshot -i # AXe accessibility tree → @eN refs
98
+ skeptic click @e4 # taps the element's center
99
+ skeptic fill @e14 "search text" # focuses + types (Cmd+A clear)
100
+ skeptic screenshot
101
+ skeptic console # bounded unified-log (best-effort)
102
+ skeptic close
39
103
  ```
40
104
 
41
- Use the generated `results.json`, `report.html`, screenshots, videos, traces, `network.json`, `console.json`, `accessibility.json`, and `perf-trace.md` as the evidence source. Reference artifact paths from `results.json` instead of guessing filenames.
105
+ - Refs come from `axe describe-ui` (the accessibility tree). selectorHints prefer the
106
+ stable `id=<accessibility identifier>` › `label=` › `type=`. `snapshot()` auto-settles
107
+ (re-dumps until the layout stops animating) so taps land where the element *is* — iOS
108
+ launch / large-title animations move elements for ~1–2s.
109
+ - iOS evidence is deliberately thin: `console` from the unified log only. perf/a11y/network
110
+ parity is a follow-up (the simulator exposes far less to an unprivileged host than Android).
111
+ - Real iOS devices are out of scope (`axe`/`idb` UI automation is simulator-only).
112
+
113
+ Mobile-specific guidance:
114
+
115
+ - **Emulator GPU mode matters for visual evidence.** A headless emulator launched
116
+ with `-no-window` and the wrong `-gpu` mode returns BLANK screencaps/recordings.
117
+ skeptic detects this (a near-uniform frame) and attaches a `blank-screenshot`
118
+ diagnostic. Fix: relaunch with software rendering —
119
+ `emulator -avd <name> -gpu swiftshader_indirect` (drop `-no-window` if it persists).
120
+ uiautomator dumps and `dumpsys` evidence are unaffected (no GPU needed).
121
+ - Refs come from a `uiautomator` accessibility dump (~1–3s each on a healthy
122
+ device). Re-snapshot after every screen change; prefer `res=` (resource-id)
123
+ and `desc=` (content-description) selectorHints over `text=`/`class=`.
124
+ - **React Native:** `testID` surfaces as `resource-id` on RN ≥ 0.64 → use `res=`.
125
+ **Compose:** needs `Modifier.semantics { testTagsAsResourceId = true }`, else
126
+ nodes are generic `View`s (rely on `desc=`/`text=`). **Flutter:** needs the
127
+ semantics tree enabled, else the app is one opaque node — fall back to a
128
+ screenshot and tap by coordinates.
129
+ - `adb` text input is ASCII-only; non-ASCII `fill`/`type` returns a structured
130
+ `[adbInput:unicode_unsupported]` error — set the value via a deep link or test
131
+ seam instead.
132
+ - WebView contents are invisible to uiautomator; for in-WebView assertions, drive
133
+ the web surface separately.
134
+ - iOS simulator support (`--platform ios-sim`, via `simctl` + `idb`) is planned;
135
+ real iOS devices are out of scope.
42
136
 
43
137
  ## Writing Specs
44
138
 
@@ -52,7 +146,7 @@ test("homepage smoke", async ({ page, snapshot, screenshot, observability }) =>
52
146
  await expect(page).toHaveTitle(/Example Domain/);
53
147
 
54
148
  const tree = await snapshot(page, { interactive: true, compact: true });
55
- await tree.byRole("link", { name: "More information..." }).click();
149
+ await (await tree.byRef("e1")).click();
56
150
 
57
151
  await screenshot("homepage", { fullPage: true });
58
152
  await observability.expectNoConsoleErrors();
@@ -63,11 +157,42 @@ Rules:
63
157
 
64
158
  - Put browser side effects inside `test(...)`, hooks, or helper functions called from tests.
65
159
  - Prefer role, label, text, and test-id locators over CSS.
66
- - Use `snapshot(page)` before interacting through refs or snapshot helpers.
160
+ - `await snapshot(page)` before interacting through refs; `tree.byRef("eN")` is async — always `await` it.
67
161
  - Re-snapshot after navigation, route changes, modal open/close, or major DOM mutation.
68
- - Do not paste CLI `@eN` refs directly into specs. Use `selectorHint` from `inspect`, or use `tree.byRef("eN")` only for refs returned by the same in-test `snapshot(page)` call.
162
+ - Do not paste CLI `@eN` refs into specs. Use `selectorHint` from `inspect`, or `tree.byRef("eN")` only for refs from the same in-test `snapshot(page)` call.
69
163
  - Add `screenshot("name")` for states that would help debug a failure.
70
164
 
165
+ ### Android specs
166
+
167
+ The same `test`/`expect`, runner, and `results.json` drive Android — run with
168
+ `skeptic run <spec> --platform android` (`--target <serial>` to pick a device).
169
+ Scaffold a starting spec with `skeptic scaffold <package> --platform android`.
170
+ Specs get a **`device`** fixture instead of `page` (uiautomator refs, not
171
+ Playwright locators):
172
+
173
+ ```ts
174
+ import { test, expect } from "skeptic-cli";
175
+
176
+ test("sessions screen loads", async ({ device }) => {
177
+ await device.open("app.fieldwork.android"); // package or scheme:// deep link
178
+ let snap = await device.snapshot(); // refs + selectorHints
179
+ if (snap.has("text=OK")) { // dismiss a dialog if present
180
+ await device.click("text=OK");
181
+ snap = await device.snapshot(); // re-snapshot after the change
182
+ }
183
+ expect(snap.has("Search sessions")).toBe(true); // match selectorHint / name / role:name
184
+ await device.screenshot("sessions");
185
+ });
186
+ ```
187
+
188
+ - Targets accept an `@eN` ref from the **last** `device.snapshot()` or a
189
+ selectorHint (`res=`/`desc=`/`text=`). Re-`snapshot()` after every screen change.
190
+ - `device.is("visible"|"enabled"|"checked", target)` and `device.get("text"|"value", target)`
191
+ read state; `device.scroll("@e5")` (into view) or `device.scroll({ dy: 600 })` (pan).
192
+ - The run auto-attaches mobile evidence to `results.json`: `console` (logcat),
193
+ `mobilePerformance` (gfxinfo/meminfo/launch), `mobileAccessibility`, `mobileNetwork`.
194
+ - Using `page` in an android spec (or `device` in a web spec) throws a clear error.
195
+
71
196
  ## Observability Checks
72
197
 
73
198
  Use `--observability` for real QA evidence. In specs, assert the signals that match the risk:
@@ -79,27 +204,14 @@ await observability.expectPerformance({ lcp: "<2500ms", cls: "<0.1" });
79
204
  await observability.expectAccessible({ standard: "WCAG21AA" });
80
205
  ```
81
206
 
82
- If an observability artifact reports a failure, fix the product or the test and re-run the same flow immediately.
83
-
84
- ## MCP Workflow
85
-
86
- When Skeptic is exposed through MCP:
87
-
88
- 1. `browser_open` the target URL.
89
- 2. `browser_snapshot` or `browser_screenshot` with snapshot mode to get refs.
90
- 3. Use one `browser_playwright` call for actions that share the same DOM state. Use the `ref` helper for snapshot refs and `return` structured evidence.
91
- 4. After DOM-changing actions, request a fresh snapshot.
92
- 5. Check `browser_console_logs`, `browser_network_requests`, `browser_accessibility_audit`, and `browser_performance_metrics`.
93
- 6. `browser_close` when done so video and trace artifacts flush.
94
-
95
- Batch fills, clicks, and data collection when the DOM is stable. Do not take a new snapshot between plain text fills unless the page structure changed.
207
+ `skeptic run` always writes `results.json` to the output dir (default `./skeptic-output`). Use it plus screenshots, videos, traces, `network.json`, `console.json`, `accessibility.json`, and `perf-trace.md` as the evidence source. Reference artifact paths from `results.json` instead of guessing filenames. If an observability artifact reports a failure, fix the product or the test and re-run the same flow immediately.
96
208
 
97
209
  ## Verification Standard
98
210
 
99
211
  Before reporting completion for browser-facing work:
100
212
 
101
- - Run the smallest Skeptic command or MCP workflow that actually exercises the changed behavior.
213
+ - Run the smallest Skeptic command (session verbs or a spec) that actually exercises the changed behavior.
102
214
  - Test at least one adjacent or negative path when forms, routing, validation, auth, persistence, or shared components changed.
103
- - Read the full command/tool output. Passing navigation alone is not enough.
215
+ - Read the full command output. Passing navigation alone is not enough.
104
216
  - If there are console errors, network failures, serious accessibility issues, poor Web Vitals, or visible regressions, fix and re-run.
105
- - State the exact command/tool run and the main artifact path in the final report.
217
+ - State the exact command run and the main artifact path in the final report.
@@ -0,0 +1,2 @@
1
+ import {createRequire}from'node:module';export{a as createAdb,c as escapeInputText,b as isAsciiInput,d as listDevices}from'./chunk-G22LGRZ4.mjs';import'./chunk-2YKSIUIN.mjs';/*! @license skeptic-cli — see LICENSES.md for third-party attributions */
2
+ createRequire(import.meta.url);
@@ -0,0 +1,3 @@
1
+ import {createRequire}from'node:module';import {a as a$1}from'./chunk-BIGNULF6.mjs';import {d,a}from'./chunk-G22LGRZ4.mjs';import'./chunk-N3533BCE.mjs';import'./chunk-ZN6MI2TU.mjs';import'./chunk-MHNEFL35.mjs';import'./chunk-S3M2RTHJ.mjs';import'./chunk-2YKSIUIN.mjs';/*! @license skeptic-cli — see LICENSES.md for third-party attributions */
2
+ createRequire(import.meta.url);
3
+ var n=class i{constructor(e,r){this.serial=e;this.adb=r;}serial;adb;static async create(e={}){let r=e.serial;if(!r){let t=(await d(e.adbPath)).filter(b=>b.state==="device");if(t.length===0)throw new Error("no Android device/emulator attached (adb devices is empty); boot one first");r=t[0].serial;}let d$1=e.adb??a({serial:r,...e.adbPath?{adbPath:e.adbPath}:{}});return new i(r,d$1)}static fromAdb(e,r){return new i(e,r)}newSession(e){let r=e?.artifactDir??process.cwd();return Promise.resolve(new a$1(this.adb,this.serial,r))}close(){return Promise.resolve()}};export{n as AdbDriver};
@@ -0,0 +1,2 @@
1
+ import {createRequire}from'node:module';export{a as AndroidAdbDriverSession,b as detectBlankCapture}from'./chunk-BIGNULF6.mjs';import'./chunk-G22LGRZ4.mjs';import'./chunk-N3533BCE.mjs';import'./chunk-ZN6MI2TU.mjs';import'./chunk-MHNEFL35.mjs';import'./chunk-S3M2RTHJ.mjs';import'./chunk-2YKSIUIN.mjs';/*! @license skeptic-cli — see LICENSES.md for third-party attributions */
2
+ createRequire(import.meta.url);