Trust, but verify.
Coding agents say the work is done. Skeptic runs it — drives your real, live app the way a user would — and proves each claim is true, or shows you exactly where it breaks.
A claim is not a result.
“Done,” “fixed,” “all tests green” — a coding agent writes the report on its own work, and reading its own source code is not proof. Skeptic settles it the only way that counts: it starts your actual app, drives it, and reports back with evidence on disk. CONFIRMED, REFUTED, or UNVERIFIABLE — never a shrug.
Five stages, one verdict.
Wired into Claude Code as a Stop hook, it runs on every turn the agent calls done — no command to remember.
- 01
The Stop hook fires
Claude Code finishes a turn and runs skeptic run, handing over the transcript and project directory. Verification starts the moment the agent says it’s done.
- 02
Claims get extracted
A small model reads the turn’s final message and the files it touched, and lists every verifiable claim about the running app — UI, API, test, or build — marking which ones are actually checkable.
- 03
An independent agent tries to refute them
A fresh, isolated run (launched with --safe-mode, so it never inherits the project’s own hooks or config) is told to disprove each claim against the live app: start it, drive the UI with skeptic probe on Playwright, curlthe API, run the project’s own tests. Reading source doesn’t count as verification.
- 04
The evidence gate rules
A verdict only stands if it cites a real artifact on disk inside .skeptic/evidence/— no file, no verdict, it downgrades to UNVERIFIABLE. And a CONFIRMED that the probe’s own captured facts contradict — a 4xx/5xx response, a console error, a failed step — is downgraded to REFUTED.
- 05
You get a verdict with receipts
Results are written to .skeptic/last-run.json and .skeptic/report.md, and a terminal summary prints in the same Stop-hook output you already watch — each claim with the repro steps and evidence files behind it.
- ✓ CONFIRMED
- ✗ REFUTED
- ? UNVERIFIABLE
- ⏱ TIMEOUT
Global install, one browser.
A single global CLI plus Playwright’s Chromium. Then let skeptic doctor confirm the machine is ready.
The package isn’t on npm yet — until it publishes, install from source: clone the repo, then npm i && npm run build && npm link.
- Node 20 or newer — the engine the CLI targets.
- The claude CLI, installed and authenticated, supporting --safe-mode (verified on 2.1.193). Skeptic shells out to it both to read claims and to run the skeptic agent — that spend is your own subscription or API budget; Skeptic makes no network calls of its own.
- Playwright’s Chromium — installed once with npx playwright install chromium, used by skeptic probe to drive the page.
- macOS or Linux. Windows is untested.
Trust boundary.Skeptic runs the target repository’s own code — it starts the app and runs its test and build commands — and launches an agent with shell and file tools against it. Only run Skeptic on repositories you trust.
One init, then it just runs.
initwires the hook and writes the config; from there every “done” gets checked automatically.
Run skeptic init inside your project. It installs a Stop hook in .claude/settings.json, writes a skeptic.json with the dev command and URL autodetected from your package.json (Vite :5173, Astro :4321, Next :3000, and more — it prints the URL it chose so you can confirm it), and adds .skeptic/ to your .gitignore. Then work with Claude Code as usual.
One-time setup — installs the Stop hook, writes skeptic.json, and ignores the .skeptic/ evidence directory.
What the hook calls on every turn. It reads the transcript on stdin, extracts and verifies the claims, and prints the verdict. It never gates — it always exits 0, so a failed check reports loudly but never blocks your agent.
Re-print the last run’s verdict in the terminal — the same summary the hook showed.
The same run as machine-readable JSON on stdout for CI. Exits non-zero if there’s no run or the report is corrupt, so a pipeline gets a clean signal.
Preflight check: the claude CLI with --safe-mode, Playwright’s Chromium, and whether your dev-server URL is reachable. Exits non-zero if a blocking dependency is missing.
Drive a page by hand. Opens <url> in headless Chromium, runs your --steps steps.json, and prints facts as JSON — final URL, console errors, request statuses — plus a full-page screenshot into --out as --name.png.
Drive a page and capture the facts
$ skeptic probe http://localhost:4321 --steps steps.json --out .skeptic/evidence --name claim-1 $ skeptic probe --help # step syntax: goto / fill / click / waitFor
skeptic.json — the config init writes
{
"startCommand": "npm run dev",
"url": "http://localhost:4321",
"timeoutMs": 300000,
"claudeArgs": ["--allowedTools", "Bash(node:*) Bash(npm:*) …"]
}The headless verifier runs non-interactively, so nobody is there to approve commands. claudeArgs grants it — in plain sight — exactly what verification needs: common runtimes and package managers to start your app and run its tests, curl/wget to hit it, skeptic probe to drive the page, and read-only inspection. Deliberately absent: a bare Bash rule, rm, sudo, chmod, Docker, and any git write. Edit the list for your stack; [] opts out entirely.
Gate a CI job on the verdicts
$ skeptic doctor || exit 1 $ skeptic report --json > report.json $ node -e 'const r=require("./report.json");process.exit(r.verdicts.every(v=>v.verdict==="CONFIRMED")?0:1)'
Read it. Fork it. Trust it because you can see it.
Skeptic is MIT-licensed and public — the whole verifier, the evidence gate, the permission defaults, all of it. A tool that judges your agent’s honesty shouldn’t ask you to take its own on faith.
An agent that doesn’t take your agent’s word for it.
Skeptic is open source today. Join the waitlist and we’ll reach out first with the managed verifier and what’s next.