> ## Documentation Index
> Fetch the complete documentation index at: https://docs.e2e.army/llms.txt
> Use this file to discover all available pages before exploring further.

# Coding agents

> What e2e gives Claude Code, Codex, Cursor, and friends, and how they should use it.

A coding agent uses the skill, the `e2e` CLI, and the JSON report.
`e2e mcp` also gives it a live session on the app to inspect controls before
writing a test. `e2e init` installs the skill and registers the MCP server.

## The skill

```bash theme={"theme":"catppuccin-mocha"}
npx --no-install e2e init
```

| Path                  | Read by                                                                  | Content                                                                                                                         |
| --------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
| `.agents/skills/e2e/` | Codex, Cursor, Copilot, Gemini CLI, OpenCode, Zed, and most other agents | `SKILL.md` plus `references/setup.md`, `writing-tests.md`, `agent.md`, `running.md`, `explore.md`, `debugging.md`, and `mcp.md` |
| `.claude/skills/e2e/` | Claude Code                                                              | The same files                                                                                                                  |

`SKILL.md` is the entry point: the mental model, a sample test, the rules,
and the list of topics. An agent reads a topic file only when it needs it.
Re-running `init` after an upgrade refreshes the copies.

Without `init`, `npx skills add tester-army/e2e` installs the skill from the
repository, and `npx --no-install e2e guide [topic]` prints it from the
installed package.

`init` does not touch `AGENTS.md`, `CLAUDE.md`, or Cursor rules. Agents that
load skills on demand find this one by its frontmatter description. For an
always-on pointer, one line in the project's instructions file is enough:

```md title="AGENTS.md" theme={"theme":"catppuccin-mocha"}
End-to-end tests use @e2edev/e2e; read .agents/skills/e2e/SKILL.md before writing or running one.
```

## How an agent looks at the app

`e2e mcp` serves a live session on one target. Its catalog includes the
engine's action tools and `locate`, which tries a `screen.*` locator and
returns the call to write when exactly one node matches. The server exposes
`open_session`, `tools`, `call`, and `close_session`; `call` dispatches to the
session's catalog by name.

`init` registers the server in `.mcp.json` for Claude Code and
`.cursor/mcp.json` for Cursor. To register it with Claude Code by hand:

```bash theme={"theme":"catppuccin-mocha"}
claude mcp add e2e -- npx --no-install e2e mcp
```

See the [MCP server reference](/reference/mcp) for its tools, session rules,
and flags. Run tests and read reports through the CLI.

## How an agent writes a test

The skill tells an agent to look before it writes: read `e2e.config.ts` for
the `tests` glob, targets, and credentials, then an existing test for the
project's conventions. Then it writes deterministic steps first and reaches
for the agent only where the flow varies.

```ts title="tests/checkout.e2e.ts" theme={"theme":"catppuccin-mocha"}
import { test, expect, credentials } from '@e2edev/e2e';

test('a signed-in user reaches checkout', async ({ screen, app, agent }) => {
  const user = credentials.user('customer');
  await app.open('/login');
  await screen.getByLabel('Email').fill(user.username);
  await screen.getByLabel('Password').fill(user.password);
  await screen.getByRole('button', { name: 'Sign in' }).click();
  await expect(screen.getByRole('heading', { name: 'Welcome back' })).toBeVisible();

  await agent.act('Add the cheapest item on the page to the cart and open the cart');
  await expect(screen.getByRole('heading', { name: 'Your cart' })).toBeVisible();
});
```

The rules the skill states, and why each is there:

* `npx --no-install e2e`, never `npx e2e`. The bare form fetches whatever
  package is called `e2e` on npm when the runner is not installed locally.
* One `agent.act` per goal, followed by an `expect`. The assertion makes the
  step's cache entry eligible for replay, and it is what fails when the agent
  did the wrong thing.
* No sleeps. Locators poll, actions wait for their target, and `expect`
  retries until its timeout.
* Secrets through `credentials.user(name)`, declared in the config, never
  literal in a test.

## How an agent runs it

```bash theme={"theme":"catppuccin-mocha"}
npx --no-install e2e run tests/checkout.e2e.ts
```

Run one file while iterating. With no executor configured, acquiring `agent`
without a model raises `MODEL_UNAVAILABLE` after the engine attempt starts.
A custom executor can implement `agent.act` and `agent.assert` without a
model. `agent.waitFor` and `agent.extract` still need a configured model when
they request a judgment. Provider credentials are needed only when a model
request is made. Check model configuration when the error reports a missing
model. For other exit-code-2 failures, follow the reported CLI, config, or
collection error.

The exit code says which side failed. 1 is a test. 2 is the CLI, config, or
collection, and retrying it changes nothing. 3 is the engine, the app
process, or the model provider.

## How an agent reads the result

Every run writes `.e2e/report.json`. `--reporter json` prints the same
document on stdout for an agent that would rather parse than open a file.

```json theme={"theme":"catppuccin-mocha"}
{
  "run": {
    "status": "failed",
    "exitCode": 1,
    "errors": [],
    "results": [
      {
        "titlePath": ["a signed-in user reaches checkout"],
        "file": "tests/checkout.e2e.ts",
        "status": "failed",
        "attempts": [
          {
            "status": "failed",
            "steps": [{ "kind": "agent", "api": "agent.act", "label": "Add the cheapest item...", "status": "passed" }],
            "error": { "code": "ASSERTION_FAILED", "message": "expected heading \"Your cart\" to be visible" },
            "artifacts": [{ "kind": "screenshot", "path": ".e2e/artifacts/checkout/failure.png" }]
          }
        ]
      }
    ]
  }
}
```

The reading order the skill teaches: `run.errors[]` first, because a
run-level failure means no test result is meaningful. Then the `error` of the
last attempt of each failed result, and the `artifacts[]` beside it; the
screenshot usually says more than the message. `steps[]` shows how far the
test got. For an agent step that went wrong, `--debug` adds the transcript as
an artifact and `--ai-trace` writes a trace the `unbox-ai` CLI summarizes.
Nothing should read `.e2e/ai-trace.json` directly; it is megabytes of resent
context.

## How an agent treats the cache

Each passing `agent.act` records its actions under `.e2e/cache/` and the next
run replays them. For a coding agent this means three things:

* A test is not proven until it passes with the cache off. Run `--no-cache`
  once before declaring done, and whenever the failure might be the
  recording rather than the app.
* Never edit a cache entry. A failing run evicts the entries it implicates.
* `--no-cache` also makes `--ai-trace` complete, since replayed steps make no
  model calls to trace.

## The bar the skill is written to

An agent given only the skill, a project that `init` scaffolded, and a target
it can reach should write a test that passes on its first run. If your agent
needs a second attempt, the skill is missing something; the fix belongs in
`skills/e2e/` in the e2e repository, not in your project's copy.

<CardGroup cols={2}>
  <Card title="CLI reference" href="/reference/cli">
    init, guide, mcp, and run, flag by flag.
  </Card>

  <Card title="MCP server" href="/reference/mcp">
    Inspect the app before writing a test.
  </Card>

  <Card title="Debugging a run" href="/debugging">
    The report, the artifacts, and the agent flags.
  </Card>
</CardGroup>
