Sandbox
@heal-dev/heal-playwright-tracer

Playwright tracer and viewer for agent diagnosis

This package adds a statement-level tracing layer on top of Playwright so agents can inspect what happened inside test code, not just browser actions. It records NDJSON traces, screenshots for locator actions and assertions, and per-run artifacts under `heal-traces/`. It also includes a local `heal-tracer` viewer that serves the recorded executions in a browser, plus docs for configuring the tracer and the Claude skill starter.

45 stars0 forksTypeScriptUpdated 24d ago
Who it's for

Builders who run Playwright tests and want an agent to analyze failures from richer trace data.

What it delivers

You can give an agent enough test context to explain failures without re-reading the whole suite.

What it does

Statement-level tracing

Records each traced statement with source, timing, status, and nested children in NDJSON.

Locator screenshots

Captures PNGs for locator actions and assertions, with the target element highlighted in the page.

Per-run execution layout

Writes artifacts into `heal-traces/<executionId>/<playwrightTestId>/<attempt>/` with an append-only index.

Local trace viewer

Ships `npx heal-tracer view` to open recorded executions in a browser and switch between runs.

Playwright config wiring

Uses a Babel plugin plus reporter entry in `playwright.config.ts` to instrument tests and collect output.

Claude skill starter

Includes `docs/SKILL.md` as a starter skill for Claude-based workflows.

How to get it

  1. 1Run
    npm install -D @heal-dev/heal-playwright-tracer
  2. 2A small CLI ships alongside the tracer so humans can browse the captured traces in a…
    npx heal-tracer view
  3. 3To consume the REST API from your own frontend during development, run the server on a…
    npx heal-tracer view --api-only --port 3001
    # or, via env (e.g. from a .env loaded with `node --env-file`, direnv, or your shell):
    HEAL_TRACER_PORT=3001 npx heal-tracer view --api-only

README

heal

Open-source statement-level Playwright tracer, purpose-built for AI agents.

SaaS | Website | Docs

CI codecov npm version node playwright OpenSSF Scorecard

@heal-dev/heal-playwright-tracer

heal-playwright-tracer is an agent-first diagnostic layer for your Playwright tests. It gives agents (and humans) everything they need to quickly analyze test results.

👉 Add this to your playwright config, run your tests, point Claude to the improved heal trace, get more accurate test diagnosis

Why

The playwright trace doesn't contain enough data for LLM-based agents such as Claude or Open Code to analyze tests results reliably. That's because the trace ifs focused on locator evaluation, while real-life tests also evaluate non-playwright code. Heal adds the missing instrumentation layer to let LLM agents work their magic. And it's useful for humans in complex test codebases, too!

FeaturePlaywright TraceHeal TracerExample: What Heal Adds
GranularityAction-levelStatement-levelShows let x = calculate() line-by-line, not just the final page.click().
Data FormatZIP/BinaryNDJSON Stream{"type":"step","file":"auth.spec.ts","line":12,"val":{"user":"dev"}}
Visual ContextStandard screenshotsHighlighted locatorsAn image where the target button is outlined in a neon overlay to prove hit-box accuracy.
Variable StateLimited/Debugger onlyFull Variable ValuesCaptures that status_code was 403 inside a hidden helper function.
Error DetailStandard stack traceSerialized ErrorsA JSON object containing the DOM snapshot at the exact millisecond of the throw.
TimingAction durationsPer-statement timingIdentifies that a specific if statement logic took 2.5s to evaluate.
CorrelationsLoose logs/networkAPI CorrelationsLinks Trace_ID_99 directly to Source_Line_45 in the NDJSON stream.

Install

npm install -D @heal-dev/heal-playwright-tracer

Wire the Babel plugin and the reporter in playwright.config.ts:

// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  // @ts-ignore — `babelPlugins` is a supported Playwright option not yet in its public types
  '@playwright/test': {
    babelPlugins: [
      [
        require.resolve('@heal-dev/heal-playwright-tracer/code-hook-injector'),
        { include: [/\/tests\//] },
      ],
    ],
  },
  reporter: [['@heal-dev/heal-playwright-tracer/reporter']],
});

Both are required: if you wire the Babel plugin without the reporter, the fixture fails fast on the first test of every worker. For why the reporter is mandatory (crash rescue, Playwright attachment copy, execution-history index), see docs/configuration.md.

If you prefer to keep the config fully typed, declare the babelPlugins option once at the top of the file instead of using @ts-ignore:

declare module '@playwright/test' {
  interface Config {
    '@playwright/test'?: {
      babelPlugins?: Array<[string, object?]>;
    };
  }
}

Per-test output lands at heal-traces/<executionId>/<playwrightTestId>/<attempt>/heal-traces.ndjson. See Output layout below for the full tree.

Output layout

Every run produces a self-contained execution dir under: heal-traces/<executionId>/<playwrightTestId>/<attempt>/heal-traces.ndjson.

<cwd>/heal-traces/
├── executions.ndjson                                   # append-only run index
└── <executionId>/                                      # auto-generated uuidv4 per run
    ├── execution.json                                  # per-run manifest
    └── <playwrightTestId>/                             # Playwright testInfo.testId
        └── <attempt>/                                  # 1-indexed
            ├── heal-traces.ndjson
            ├── trace.zip                               # if Playwright recorded one
            ├── screenshots/
            │   └── stmt-0001.png
            └── videos/
                └── video.webm

Keys are shaped {executionId}/{testId}/{attempt}/... so the layout maps cleanly onto any object-store key prefix if you ship traces elsewhere later.

History is append-only — runs accumulate, nothing is pruned. Clean up manually with rm -rf heal-traces/<id>/ when needed.

Note: add heal-traces/ to your .gitignore — these are local build artefacts and should never be committed.

<executionId> is a uuidv4 the reporter generates at the start of each run; the trace viewer uses it to scope and group artefacts per run.

Usage

  1. After installing Heal, run your tests with the usual npx playwright test command.
  2. You should see heal-traces.ndjson.
  3. You can ask Claude or another agent to use those to understand your test results.

Viewing traces

A small CLI ships alongside the tracer so humans can browse the captured traces in a local browser without setting up a Heal account:

npx heal-tracer view

The CLI starts a local HTTP server on a free OS-assigned port, reads <cwd>/heal-traces/, lists every recorded execution in the header dropdown (newest first), and opens the latest one in your default browser. Switch executions from the dropdown to inspect history. Press Ctrl+C to stop.

Options

FlagDescription
--port <port>Bind a fixed port instead of an OS-assigned ephemeral one. Useful when an external frontend needs a stable URL. Falls back to the HEAL_TRACER_PORT env var.
--api-onlyServe only the /api/* routes — no SPA bundle, no browser auto-open. For driving the REST API from a separate frontend dev server.
--verboseMirror spawned subprocess output (heal analyze, heal login) to the tracer console.

To consume the REST API from your own frontend during development, run the server on a fixed port without the bundled UI:

npx heal-tracer view --api-only --port 3001
# or, via env (e.g. from a .env loaded with `node --env-file`, direnv, or your shell):
HEAL_TRACER_PORT=3001 npx heal-tracer view --api-only

CORS is wide-open (Access-Control-Allow-Origin: *), so a frontend on its own dev server can fetch('http://localhost:3001/api/executions') cross-origin with no extra setup. The API is unauthenticated and localhost-bound by design — don't expose it on a public interface.

Claude Skill

See this Claude skill for a starter.

Sample output

heal-data/heal-traces.ndjson — one record per line:

{"kind":"test-header","schemaVersion":1,"test":{"title":"it works","file":"tests/example.spec.ts","context":{"testId":"...","attempt":1}}}
{"kind":"statement","statement":{"loc":{"line":5},"source":"await page.goto('https://example.com')","durationMs":412,"status":"ok","children":[...]}}
{"kind":"statement","statement":{"loc":{"line":6},"source":"await expect(page.getByRole('heading')).toBeVisible()","durationMs":73,"status":"ok"}}
{"kind":"test-result","status":"passed","duration":1234,"stdout":"...","stderr":""}

Schema: src/domain/trace-event-recorder/model/statement-trace-schema.ts (also exported as @heal-dev/heal-playwright-tracer/statement-trace-schema).

Screenshots

Every statement that calls a patched Playwright locator action (click, fill, hover, press, …) or a locator assertion (expect(locator).toBeVisible(), toHaveText(), …) produces a PNG screenshot with the targeted element outlined via an overlay drawn in-page — so the agent sees what Playwright was actually pointing at at the moment the action ran, not just the raw page.

Files are written to the per-test heal-data/ directory and referenced on the corresponding statement via the screenshot field:

{"kind":"statement","statement":{"source":"await page.getByRole('button', { name: 'Submit' }).click()","status":"ok","screenshot":"stmt-0007.png"}}
{"kind":"statement","statement":{"source":"await expect(page.getByRole('alert')).toBeVisible()","status":"ok","screenshot":"stmt-0008.png"}}

Statements that don't touch a locator (plain JS, utility calls, page.goto) have no screenshot field — capture is scoped to the Playwright surface where it adds diagnostic signal.

Architecture, and extending the tracer

See development.md

Caveats

The Babel plugin rewrites every leaf statement with a try/catch/finally and three hook calls — the same shape of transformation Istanbul applies for code coverage. Two consequences to be aware of:

  • Instrumented files are larger. Each statement gains a wrapper, so on-disk size of transformed test files grows noticeably (typically ~2–4×, depending on statement density). This affects the files Playwright loads into workers, not your application bundle.
  • Tests run slightly slower. The per-statement hook overhead is small in absolute terms but not free — expect a modest slowdown on CPU-bound test code. I/O-bound tests (the common case: await page.click(...), network, navigation) are dominated by the browser and barely move.

Scope the include filter in playwright.config.ts so only your tests/ directory is instrumented — never your app code or node_modules — to keep the cost contained.

License

Copyright © 2026 MYIA SAS.

This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0), except for the tracer-viewer-bundle which is a vendored, free to use version of the Heal trace viewer. See the LICENSE file for the full text.

Files in the repo

Repository payload22 top-level entries
  • .github
  • .husky
  • assets
  • docs
  • scripts
  • src
  • tests
  • tracer-viewer-bundle
  • .gitignore
  • .nvmrc
  • .prettierignore
  • .prettierrc.json
  • eslint.config.js
  • LICENSE
  • package-lock.json
  • package.json
  • README.md
  • SECURITY.md
  • tsconfig.json
  • tsconfig.lint.json
  • vitest.config.ts
  • vitest.integration.config.ts

Discussion (0)

Ask about usage, or say what you built with it

Sign in to join the discussion.

No comments yet. Be the first to say what this is good for.

More tools

JuliusBrussee/
caveman

🪨 why use many token when few token do trick — Claude Code skill that cuts 65% of tokens by talking like caveman

105k
1 add
MemPalace/
mempalace

The best-benchmarked open-source AI memory system. And it's free.

59k
stablyai/
orca

Orca is the ADE for working with a fleet of parallel agents. Run any coding agent with your own subscription. Available on desktop, mobile and remote runtime.

66k

A cross-platform desktop All-in-One assistant for Claude Code, Codex, OpenCode, OpenClaw, Grok Build & Hermes Agent. Only official website: ccswitch.io

132k

Never stop coding. Free MIT AI gateway: one endpoint, 352 providers (150+ free), 1200+ models Kimi, Claude, GPT, Gemini, GLM, DeepSeek, MiniMax. Works with Claude Code, Codex, Cursor, OpenCode, Cline & Copilot. Quota-aware auto-fallback, RTK+Caveman compression saves 15-95% tokens, MCP/A2A, Desktop/PWA. Built by 550+ contributors

64k
headroomlabs-ai/
headroom

Compress tool outputs, logs, files, and RAG chunks before they reach the LLM. 20% fewer tokens for coding agents, 60-95% fewer tokens for JSON, same answers. Library, proxy, MCP server.

71k