reeval 0.6.3 → 0.7.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/README.md +265 -0
- package/THIRD_PARTY_NOTICES.md +5 -5
- package/assets/package.json +1 -1
- package/assets/pnpm-lock.yaml +30 -307
- package/dist/{agent-DKuFnZPM.js → agent-Dhd5FJCZ.js} +4 -4
- package/dist/{agent-DKuFnZPM.js.map → agent-Dhd5FJCZ.js.map} +1 -1
- package/dist/{agent-oirN_8IS.js → agent-gjBiZis8.js} +4 -4
- package/dist/{agent-oirN_8IS.js.map → agent-gjBiZis8.js.map} +1 -1
- package/dist/assemble-D8sZ4Jiv.js +2 -0
- package/dist/{assemble-DYCDgbTq.js → assemble-Dd_mZYnu.js} +109 -7
- package/dist/assemble-Dd_mZYnu.js.map +1 -0
- package/dist/bridge/index.mjs +66 -7
- package/dist/bridge/package.json +3 -3
- package/dist/bridge/pnpm-lock.yaml +84 -89
- package/dist/{cli-BIhzl_v_.js → cli-r9CDqoFL.js} +139 -186
- package/dist/cli-r9CDqoFL.js.map +1 -0
- package/dist/{cli-spec-DUeyuSIQ.js → cli-spec-DOyxAMc2.js} +22 -9
- package/dist/cli-spec-DOyxAMc2.js.map +1 -0
- package/dist/cli.js +2 -2
- package/dist/{matchers-Bp-OH1vj.js → client-LsfDiD-X.js} +152 -76
- package/dist/client-LsfDiD-X.js.map +1 -0
- package/dist/{command-KoOJ0ntD.js → command-Cej2t3y-.js} +9 -9
- package/dist/{command-KoOJ0ntD.js.map → command-Cej2t3y-.js.map} +1 -1
- package/dist/command-Cxfgghqo.js +2 -0
- package/dist/{command-DuJBqcwu.js → command-D6r2pwO5.js} +10 -10
- package/dist/{command-DuJBqcwu.js.map → command-D6r2pwO5.js.map} +1 -1
- package/dist/{command-B8tcwUJk.js → command-DMR69l5G.js} +12 -12
- package/dist/{command-B8tcwUJk.js.map → command-DMR69l5G.js.map} +1 -1
- package/dist/{command-fJpZK205.js → command-l4ZFCeMh.js} +6 -6
- package/dist/{command-fJpZK205.js.map → command-l4ZFCeMh.js.map} +1 -1
- package/dist/{commands-CHdTfPB6.js → commands-BA_NCh2h.js} +6 -6
- package/dist/{commands-CHdTfPB6.js.map → commands-BA_NCh2h.js.map} +1 -1
- package/dist/{commands-DE604ldB.js → commands-DgOUkcSO.js} +1 -1
- package/dist/{config-7-gXOgQf.js → config-DuQDCx0B.js} +3 -3
- package/dist/{config-7-gXOgQf.js.map → config-DuQDCx0B.js.map} +1 -1
- package/dist/{db-DvkzBhM6.js → db-CjX7JF4o.js} +12 -24
- package/dist/{db-DvkzBhM6.js.map → db-CjX7JF4o.js.map} +1 -1
- package/dist/db-o_osRkqr.js +3 -0
- package/dist/{dist-DsZNsoD3.js → dist-CTzThfu_.js} +3 -3
- package/dist/{dist-DsZNsoD3.js.map → dist-CTzThfu_.js.map} +1 -1
- package/dist/{docker-BBOMThRi.js → docker-DaUaG4XZ.js} +4 -4
- package/dist/{docker-BBOMThRi.js.map → docker-DaUaG4XZ.js.map} +1 -1
- package/dist/{docker-CiNVc7di.js → docker-Dnya8eo9.js} +1 -1
- package/dist/{docker-CzFiN2AZ.js → docker-ZaYeKE2w.js} +2 -2
- package/dist/{docker-CzFiN2AZ.js.map → docker-ZaYeKE2w.js.map} +1 -1
- package/dist/{doctor-C06kYs2r.js → doctor-Bk6aJeec.js} +10 -10
- package/dist/{doctor-C06kYs2r.js.map → doctor-Bk6aJeec.js.map} +1 -1
- package/dist/{egress-De64jpeQ.js → egress-BKbyriEa.js} +10 -10
- package/dist/{egress-De64jpeQ.js.map → egress-BKbyriEa.js.map} +1 -1
- package/dist/{files-BqIF7iu4.js → files-BSMSnQMa.js} +2 -2
- package/dist/{files-BqIF7iu4.js.map → files-BSMSnQMa.js.map} +1 -1
- package/dist/files-DmZGPsWK.js +2 -0
- package/dist/{files-Cq99wif-.js → files-TNrZJQ3Y.js} +15 -4
- package/dist/{files-Cq99wif-.js.map → files-TNrZJQ3Y.js.map} +1 -1
- package/dist/files-Tb8wY0Ze.js +2 -0
- package/dist/{git-Dnl-kXgD.js → git-DNGtrOg9.js} +2 -2
- package/dist/{git-Dnl-kXgD.js.map → git-DNGtrOg9.js.map} +1 -1
- package/dist/{harness-3QUEWx8G.js → harness-CDRaaGvR.js} +2 -2
- package/dist/{harness-3QUEWx8G.js.map → harness-CDRaaGvR.js.map} +1 -1
- package/dist/harness.d.ts +1 -1
- package/dist/harness.js +1 -1
- package/dist/{header-IE-uIvX0.js → header-BZF8Xss1.js} +2 -2
- package/dist/{header-IE-uIvX0.js.map → header-BZF8Xss1.js.map} +1 -1
- package/dist/{images-B8Mp_VXZ.js → images-BpwFDT1E.js} +6 -6
- package/dist/{images-B8Mp_VXZ.js.map → images-BpwFDT1E.js.map} +1 -1
- package/dist/index.d.ts +12 -7
- package/dist/index.js +24 -17
- package/dist/index.js.map +1 -1
- package/dist/init-1THeGfut.js +2 -0
- package/dist/{init-B8mw86E6.js → init-CBWt1rZT.js} +4 -4
- package/dist/{init-B8mw86E6.js.map → init-CBWt1rZT.js.map} +1 -1
- package/dist/{invocations-DHC946b-.js → invocations-DArYWyXN.js} +2 -2
- package/dist/{invocations-DHC946b-.js.map → invocations-DArYWyXN.js.map} +1 -1
- package/dist/judge.d.ts +1 -1
- package/dist/model-D_pF3Vcl.js +2 -0
- package/dist/{model-CtrJdFi6.js → model-DyQbu7KF.js} +113 -3
- package/dist/model-DyQbu7KF.js.map +1 -0
- package/dist/{opencode-providers-DjlA5fuC.js → opencode-providers-ByD85Q0l.js} +21 -7
- package/dist/{opencode-providers-DjlA5fuC.js.map → opencode-providers-ByD85Q0l.js.map} +1 -1
- package/dist/{options-CWYB613o.js → options-Ge_x32XE.js} +3 -3
- package/dist/{options-CWYB613o.js.map → options-Ge_x32XE.js.map} +1 -1
- package/dist/{plan-CsPE24pp.js → plan-BqBadHYj.js} +14 -7
- package/dist/plan-BqBadHYj.js.map +1 -0
- package/dist/{preflight-DKZjb7ZH.js → preflight-CFtZNTLt.js} +1 -1
- package/dist/{preflight-r0DgibQV.js → preflight-CyaAZE9r.js} +3 -3
- package/dist/{preflight-r0DgibQV.js.map → preflight-CyaAZE9r.js.map} +1 -1
- package/dist/{prepare-DOkvAa9D.js → prepare-Dl7-t3fO.js} +2 -2
- package/dist/{prepare-CTLg3nuk.js → prepare-Zr1xOMUX.js} +4 -4
- package/dist/{prepare-CTLg3nuk.js.map → prepare-Zr1xOMUX.js.map} +1 -1
- package/dist/{provenance--cVNo80L.js → provenance-Byl2T6LJ.js} +2 -2
- package/dist/{provenance--cVNo80L.js.map → provenance-Byl2T6LJ.js.map} +1 -1
- package/dist/{providers-DNLQzVnc.js → providers-Cflw7Ks-.js} +3 -3
- package/dist/{providers-DNLQzVnc.js.map → providers-Cflw7Ks-.js.map} +1 -1
- package/dist/{providers-B2cSfewm.js → providers-SBMExI53.js} +1 -1
- package/dist/query-CvutWHaL.js +2 -0
- package/dist/{query-DTtIZaua.js → query-DUSLlic0.js} +2 -2
- package/dist/{query-DTtIZaua.js.map → query-DUSLlic0.js.map} +1 -1
- package/dist/results-owJR-NFm.js +52 -0
- package/dist/results-owJR-NFm.js.map +1 -0
- package/dist/resume-BL4Yw7Ld.js +406 -0
- package/dist/resume-BL4Yw7Ld.js.map +1 -0
- package/dist/sandbox.d.ts +1 -1
- package/dist/server-CNPsB0cT.js +2 -0
- package/dist/{server-BFlKK2j6.js → server-yFVUxAm0.js} +6 -6
- package/dist/{server-BFlKK2j6.js.map → server-yFVUxAm0.js.map} +1 -1
- package/dist/source-Cmc5SKFn.js +2 -0
- package/dist/{source-B2ASbTdc.js → source-M3gS517R.js} +2 -2
- package/dist/{source-B2ASbTdc.js.map → source-M3gS517R.js.map} +1 -1
- package/dist/{src-Bo8GFUXg.js → src--L8B4WGa.js} +1 -1
- package/dist/{src-zOeHvrzR.js → src-79JhW-zG.js} +13 -13
- package/dist/{src-zOeHvrzR.js.map → src-79JhW-zG.js.map} +1 -1
- package/dist/{src-BoziVhKa.js → src-BLN459er.js} +1 -1
- package/dist/src-Bz66jDtE.js +3 -0
- package/dist/{src-CPkDaNxp.js → src-CfUwg9UY.js} +1 -1
- package/dist/{src-BHFNhTTQ.js → src-CokPbYCb.js} +45 -45
- package/dist/{src-BHFNhTTQ.js.map → src-CokPbYCb.js.map} +1 -1
- package/dist/{src-CDiGyoDt.js → src-DH0jhpoi.js} +1 -1
- package/dist/{src-CVoCJjEe.js → src-DTcalvuF.js} +2 -2
- package/dist/{src-CVoCJjEe.js.map → src-DTcalvuF.js.map} +1 -1
- package/dist/src-DV_3QDHA.js +4 -0
- package/dist/{src-CqsERnCZ.js → src-IOr1rhEg.js} +4 -4
- package/dist/{src-CqsERnCZ.js.map → src-IOr1rhEg.js.map} +1 -1
- package/dist/src-Ljh2vKXm.js +3 -0
- package/dist/{state-DBKcUJ2u.js → state-DEj_utaE.js} +10 -10
- package/dist/{state-DBKcUJ2u.js.map → state-DEj_utaE.js.map} +1 -1
- package/dist/state-DkqbCVVp.js +2 -0
- package/dist/studio-app/assets/{index-WQPq5K3E.js → index-Bbl0oTxu.js} +1 -1
- package/dist/studio-app/index.html +1 -1
- package/dist/{summary-CahgSiVt.js → summary--FBuB_sr.js} +3 -3
- package/dist/{summary-CahgSiVt.js.map → summary--FBuB_sr.js.map} +1 -1
- package/dist/{summary-BBABbon9.js → summary-D012KlUM.js} +5 -5
- package/dist/{summary-BBABbon9.js.map → summary-D012KlUM.js.map} +1 -1
- package/dist/tool-target-BU1JnSE9.js +34 -0
- package/dist/tool-target-BU1JnSE9.js.map +1 -0
- package/dist/{tools-53YHomyk.js → tools-CJHw-zCo.js} +3 -3
- package/dist/{tools-53YHomyk.js.map → tools-CJHw-zCo.js.map} +1 -1
- package/dist/{types-CoAfC-_k.d.ts → types-BsRSknLe.d.ts} +5 -4
- package/dist/view/index.d.ts +1 -1
- package/dist/view/index.js +2 -2
- package/dist/{views-Drl2OXuj.js → views-jhL9Jb8A.js} +4 -4
- package/dist/{views-Drl2OXuj.js.map → views-jhL9Jb8A.js.map} +1 -1
- package/dist/views-yPmmoo8f.js +3 -0
- package/dist/{worker-entry-mCctDzig.js → worker-entry-OwnsrUdr.js} +72 -2
- package/dist/worker-entry-OwnsrUdr.js.map +1 -0
- package/dist/{workspace-BT4jd04k.js → workspace-C4_J0pqb.js} +3 -3
- package/dist/{workspace-BT4jd04k.js.map → workspace-C4_J0pqb.js.map} +1 -1
- package/dist/{workspace-U6M1gKzo.js → workspace-wudSz_W-.js} +1 -1
- package/package.json +9 -8
- package/dist/assemble-DYCDgbTq.js.map +0 -1
- package/dist/assemble-DykFydSU.js +0 -2
- package/dist/cli-BIhzl_v_.js.map +0 -1
- package/dist/cli-spec-DUeyuSIQ.js.map +0 -1
- package/dist/command-CwuwuKJD.js +0 -2
- package/dist/db-BVE1kZ2w.js +0 -2
- package/dist/files-Cs8tbYQY.js +0 -2
- package/dist/files-wUtoO5aA.js +0 -2
- package/dist/init-DDEpamaf.js +0 -2
- package/dist/matchers-Bp-OH1vj.js.map +0 -1
- package/dist/model-C_B5JrhL.js +0 -2
- package/dist/model-CtrJdFi6.js.map +0 -1
- package/dist/plan-CsPE24pp.js.map +0 -1
- package/dist/query-BWc_5Lhe.js +0 -2
- package/dist/results-CrFs7_In.js +0 -37
- package/dist/results-CrFs7_In.js.map +0 -1
- package/dist/server-HRUHVcHc.js +0 -2
- package/dist/source-Co7h89WK.js +0 -2
- package/dist/src-BE19tji4.js +0 -3
- package/dist/src-CFxHPwQA.js +0 -3
- package/dist/src-D-go5-qz.js +0 -4
- package/dist/state-BaHjYXr1.js +0 -2
- package/dist/stats-BNAHlNMD.js +0 -114
- package/dist/stats-BNAHlNMD.js.map +0 -1
- package/dist/views-BhGinAz_.js +0 -3
- package/dist/worker-entry-mCctDzig.js.map +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# reeval
|
|
2
|
+
|
|
3
|
+
**Find out whether your skill, docs or MCP server makes coding agents better, on real agents.**
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
// evals/api.eval.ts, from examples/hello
|
|
7
|
+
import { defineEnvironment, evaluate, expect } from 'reeval'
|
|
8
|
+
import { claudeCode, codex, cursor } from 'reeval/harness'
|
|
9
|
+
import { appFiles, probeUsers } from './api/app.ts'
|
|
10
|
+
|
|
11
|
+
// create-next-app's API template at a tag. No baseline, so it's the control.
|
|
12
|
+
const nextApi = defineEnvironment('nextjs-api', ({ git }) => ({
|
|
13
|
+
workspace: git('github:vercel/next.js', {
|
|
14
|
+
ref: 'v16.3.7',
|
|
15
|
+
path: 'packages/create-next-app/templates/app-api/ts',
|
|
16
|
+
}),
|
|
17
|
+
files: appFiles, // package.json, and lib/users.ts to list
|
|
18
|
+
setup: ['mv gitignore .gitignore', 'npm install --no-audit --no-fund'],
|
|
19
|
+
}))
|
|
20
|
+
|
|
21
|
+
const withSkill = nextApi.extendEnvironment('with-api-conventions', ({ skill }) => ({
|
|
22
|
+
skills: [
|
|
23
|
+
skill.git('github:invertase/reeval', {
|
|
24
|
+
ref: '052108cae3ca4c9d1bad1e6b527d76bdf11fd4cc',
|
|
25
|
+
path: 'examples/hello/skills',
|
|
26
|
+
only: ['api-conventions'],
|
|
27
|
+
}),
|
|
28
|
+
],
|
|
29
|
+
}))
|
|
30
|
+
|
|
31
|
+
evaluate(
|
|
32
|
+
'how well the api-conventions skill influences the output',
|
|
33
|
+
{
|
|
34
|
+
environment: nextApi,
|
|
35
|
+
harness: [
|
|
36
|
+
cursor({ model: 'composer-2.5' }),
|
|
37
|
+
claudeCode({ model: 'claude-sonnet-5' }),
|
|
38
|
+
codex({ model: 'gpt-5.6-luna' }),
|
|
39
|
+
],
|
|
40
|
+
runs: 3,
|
|
41
|
+
timeout: '20m',
|
|
42
|
+
compare: {
|
|
43
|
+
variants: [withSkill],
|
|
44
|
+
questions: ['Which endpoint follows our API conventions?'],
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
async (c) => {
|
|
48
|
+
await c.send(
|
|
49
|
+
'Add a paginated GET /api/users endpoint (app/api/users/route.ts) ' +
|
|
50
|
+
'over lib/users.ts, with tests that npm test runs.',
|
|
51
|
+
)
|
|
52
|
+
// The route exists and pages by cursor.
|
|
53
|
+
await expect(c).toHaveFile('app/api/users/route.ts', /cursor/i)
|
|
54
|
+
// It builds, types included.
|
|
55
|
+
await expect(c.exec('npm run build')).toSucceed()
|
|
56
|
+
// The agent's own tests pass.
|
|
57
|
+
await expect(c.exec('npm test')).toSucceed()
|
|
58
|
+
// The built route pages as { data, nextCursor } and rejects a bad cursor.
|
|
59
|
+
await expect(c.exec(probeUsers)).toSucceed()
|
|
60
|
+
// A judge for what the checks can't read: do the tests cover it?
|
|
61
|
+
const covered = 'Do the tests follow nextCursor to the end and check invalid_cursor?'
|
|
62
|
+
expect(await c.judge(covered)).toPass()
|
|
63
|
+
},
|
|
64
|
+
)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
- **Real agents in sandboxes.** Cursor, Claude Code, Codex, Antigravity, OpenCode, Goose and pi
|
|
68
|
+
each work in a fresh local Docker container. Their API keys stay on the host: the sandbox gets
|
|
69
|
+
a placeholder, and reeval's egress gateway swaps in the key only on requests to that provider.
|
|
70
|
+
- **Any environment.** Start from a git repository at a pinned ref, then add files, setup steps,
|
|
71
|
+
rules, skills, MCP servers and secrets, on reeval's image or your own `from` or Dockerfile.
|
|
72
|
+
- **The whole matrix.** Each harness × environment is a cell with its own runs, all planned
|
|
73
|
+
before anything starts (`reeval run --dry-run`), with profiles that size a run for a PR or a
|
|
74
|
+
nightly.
|
|
75
|
+
- **Checks and judges.** Deterministic checks on files, commands and the transcript decide pass
|
|
76
|
+
or fail; an LLM judge answers what they can't.
|
|
77
|
+
- **Compare, questions and analysis.** `compare` reports each variant's lift over the control,
|
|
78
|
+
with a 95% interval and its effort; `questions` asks a model which attempt did better, pair by
|
|
79
|
+
pair and blind; `reeval analyze` writes a checked reading of a run.
|
|
80
|
+
- **Results in your repo.** Every run is a JSON record you can commit, diff with `reeval compare`,
|
|
81
|
+
query with SQL (`reeval query`) and browse in `reeval studio`.
|
|
82
|
+
|
|
83
|
+
A real run of the eval, one attempt per cell: no control paged by cursor, and with the skill
|
|
84
|
+
all three agents passed every check.
|
|
85
|
+
|
|
86
|
+
```text
|
|
87
|
+
$ reeval run evals/api.eval.ts --runs 1
|
|
88
|
+
…
|
|
89
|
+
✓ how well the api-conventions skill influences the output 3/6 · 100% gated 6 attempts
|
|
90
|
+
cursor:composer-2.5/nextjs-api/docker control 0/1 0% not gated
|
|
91
|
+
cursor:composer-2.5/with-api-conventions/docker variant 1/1 100% lift +100 pts (95% −12…+100, 1/1 vs 0/1), within noise
|
|
92
|
+
claude-code:claude-sonnet-5/nextjs-api/docker control 0/1 0% not gated
|
|
93
|
+
claude-code:claude-sonnet-5/with-api-conventions/docker variant 1/1 100% lift +100 pts (95% −12…+100, 1/1 vs 0/1), within noise
|
|
94
|
+
codex:gpt-5.6-luna/nextjs-api/docker control 0/1 0% not gated
|
|
95
|
+
codex:gpt-5.6-luna/with-api-conventions/docker variant 1/1 100% lift +100 pts (95% −12…+100, 1/1 vs 0/1), within noise
|
|
96
|
+
question "Which endpoint follows our API conventions?" across: claude-code:claude-sonnet-5 2/4 (50%), codex:gpt-5.6-luna 0/4 (0%), cursor:composer-2.5 4/4 (100%) · 6 votes, 0 ties · gemini-3.8-flash
|
|
97
|
+
…
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
npm i -D reeval # the CLI is `reeval`, or `re` for short
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
You need Node 22.13 or later and, for real runs, a running Docker.
|
|
105
|
+
|
|
106
|
+
| Platform | Fake agent and sandbox | Real runs (Docker sandbox) |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| Linux | ✓ | ✓ Docker Engine or Docker Desktop |
|
|
109
|
+
| macOS | ✓ | ✓ Docker Desktop, OrbStack or Colima |
|
|
110
|
+
| Windows | ✓ with [Git for Windows](https://git-scm.com/download/win) (its `sh`) | ✓ Docker Desktop on the WSL 2 backend, running Linux containers |
|
|
111
|
+
|
|
112
|
+
`reeval doctor` checks Docker for your platform and names the fix. CI runs every tier on Linux and
|
|
113
|
+
everything but the Docker tier on Windows and macOS.
|
|
114
|
+
|
|
115
|
+
## A minimal eval
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
// evals/hello.eval.ts
|
|
119
|
+
import { evaluate, expect, group } from 'reeval'
|
|
120
|
+
|
|
121
|
+
group('greet module', () => {
|
|
122
|
+
evaluate('creates a tested greet module', async (c) => {
|
|
123
|
+
const turn = await c.send(
|
|
124
|
+
'Create greet.js exporting greet(name) that returns "Hello, <name>!", ' +
|
|
125
|
+
'and greet.test.js that tests it with node:test. No dependencies.',
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
await expect(turn).toHaveFile('greet.js')
|
|
129
|
+
await expect(c.exec('node --test')).toSucceed()
|
|
130
|
+
expect(await c.judge("Does the agent's final message accurately describe what it changed?")).toPass()
|
|
131
|
+
})
|
|
132
|
+
})
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
// reeval.config.ts
|
|
137
|
+
import { defineConfig } from 'reeval'
|
|
138
|
+
import { cursor } from 'reeval/harness'
|
|
139
|
+
|
|
140
|
+
export default defineConfig({
|
|
141
|
+
include: ['evals/**/*.eval.ts'],
|
|
142
|
+
harness: cursor({ model: 'composer-2.5' }), // or claudeCode({ model }), antigravity({ model }), opencode({ model }), goose({ model }), pi({ model }), codex({ model }), or a list: each is its own cell
|
|
143
|
+
judge: { model: 'typesafe-ai/jev-1.13.0', threshold: 0.8 }, // optional; pin the version
|
|
144
|
+
timeout: '15m',
|
|
145
|
+
profiles: {
|
|
146
|
+
pr: { limit: { runs: 1, compare: false }, budget: { attempts: 20 } },
|
|
147
|
+
nightly: { defaults: { runs: 3 }, record: 'always' },
|
|
148
|
+
},
|
|
149
|
+
defaultProfile: { ci: 'pr' },
|
|
150
|
+
})
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Compare with a skill
|
|
154
|
+
|
|
155
|
+
Give the agent a skill and check that it helps. `plain` is the baseline. The variant is
|
|
156
|
+
that environment plus the skill, and compare runs the two apart, with the same prompt and
|
|
157
|
+
the same checks:
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
// evals/skill.eval.ts (trimmed from examples/hello)
|
|
161
|
+
import { defineEnvironment, evaluate, expect, group } from 'reeval'
|
|
162
|
+
|
|
163
|
+
const plain = defineEnvironment('plain', {})
|
|
164
|
+
const withHouseGreeting = plain.extendEnvironment(
|
|
165
|
+
'with-house-greeting',
|
|
166
|
+
({ skill }) => ({
|
|
167
|
+
skills: [skill.local('../skills/house-greeting')],
|
|
168
|
+
}),
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
group(
|
|
172
|
+
'house greeting',
|
|
173
|
+
{ compare: { baseline: plain, variants: [withHouseGreeting] } },
|
|
174
|
+
() => {
|
|
175
|
+
evaluate('greets in the house style', async (c) => {
|
|
176
|
+
await c.send(
|
|
177
|
+
'Create greet.js exporting greet(name) that greets the person ' +
|
|
178
|
+
'in our house style.',
|
|
179
|
+
)
|
|
180
|
+
await expect(c.exec(greetAda)).toSucceed() // passes only for "Ahoy, Ada!"
|
|
181
|
+
})
|
|
182
|
+
},
|
|
183
|
+
)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
```text
|
|
187
|
+
$ reeval run skill --runs 2
|
|
188
|
+
plan 1 eval · 2 cells · 4 attempts · ~50s serial (~13s at concurrency 4)
|
|
189
|
+
…
|
|
190
|
+
✓ greets in the house style 2/4 · 100% gated 4 attempts
|
|
191
|
+
cursor:composer-2.5/plain/docker control 0/2 0% not gated
|
|
192
|
+
cursor:composer-2.5/with-house-greeting/docker variant 2/2 100% lift +100 pts (95% +7…+100, 2/2 vs 0/2)
|
|
193
|
+
effort: tools 3 vs 8 (×0.38), agent 9s vs 18s (×0.50): too few attempts to test
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Run it
|
|
197
|
+
|
|
198
|
+
```text
|
|
199
|
+
$ CI=1 reeval run greet
|
|
200
|
+
plan profile pr (auto: CI is set) · 2 evals · 2 cells · 2 attempts · ~40s serial (~20s at concurrency 4) · budget 20 attempts ✓
|
|
201
|
+
reeval 0.0.1 · 3 eval files · harness cursor:composer-2.5 · sandbox docker · concurrency 4 · grouped logs
|
|
202
|
+
[creates#1 cursor] ▶ evals/hello.eval.ts › greet module › creates a tested greet module
|
|
203
|
+
[creates#1 cursor] ✓ evals/hello.eval.ts › greet module › creates a tested greet module 17s
|
|
204
|
+
[creates#1 cursor] +0:00 sandbox ready 1s
|
|
205
|
+
[creates#1 cursor] +0:01 turn 1 done 12s · 1 tool call, 2 file edits
|
|
206
|
+
[creates#1 cursor] +0:13 $ node --test exit 0 129ms
|
|
207
|
+
[creates#1 cursor] +0:13 judge pass 1.00 (≥ 0.8) Does the agent's final message accurately describe what it changed?
|
|
208
|
+
[adds#1 cursor] ▶ evals/hello.eval.ts › greet module › adds a default name in a follow-up turn
|
|
209
|
+
[adds#1 cursor] ✓ evals/hello.eval.ts › greet module › adds a default name in a follow-up turn 23s
|
|
210
|
+
…
|
|
211
|
+
|
|
212
|
+
Summary
|
|
213
|
+
evals/hello.eval.ts
|
|
214
|
+
greet module
|
|
215
|
+
✓ creates a tested greet module 17s judge 1.00 1 turn
|
|
216
|
+
✓ adds a default name in a follow-up turn 23s judge 1.00 2 turns
|
|
217
|
+
|
|
218
|
+
Evals 2 passed (2)
|
|
219
|
+
Duration 40s
|
|
220
|
+
Results .reeval/runs/20260924T213759Z_1ba71aa_ci_4vb0/summary.md
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Under a coding agent, reeval switches to compact output: one block per failure and the path to its
|
|
224
|
+
record. To see what an attempt did step by step, add `-v`: a timestamped line per phase (image,
|
|
225
|
+
container, setup, brokering, each tool call, checks, judge calls, every gateway request).
|
|
226
|
+
`-vv` adds the raw payloads and every `docker` command. Either way each attempt's trace is
|
|
227
|
+
kept locally in `.reeval/debug`, never in the committed history. Commit runs into `evals/.history/` with `reeval record`, then track them with `reeval history`
|
|
228
|
+
and `reeval compare`, or ask across runs with SQL: `reeval query lift`, `reeval query effort` (tool calls and
|
|
229
|
+
agent time, variant against control), `reeval query factor_effects`, or
|
|
230
|
+
`reeval query "SELECT * FROM failing_attempts WHERE skill = 'house-greeting'"`. Where the harness
|
|
231
|
+
reports them (Cursor, Claude Code, Antigravity, pi and Codex do), the record keeps each turn's tokens and the model that
|
|
232
|
+
served it, and totals them per attempt and per run.
|
|
233
|
+
|
|
234
|
+
`reeval analyze` explains a run: one LLM call over a digest of those facts, with every claim's
|
|
235
|
+
citations and every suggested command checked against reeval's own CLI. It's cached in `.reeval/`;
|
|
236
|
+
`--save` commits it next to the run, and `reeval analyze --context` hands the same digest to your own
|
|
237
|
+
coding agent instead, with no key.
|
|
238
|
+
|
|
239
|
+
## Studio
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
reeval studio # browse runs, evals and transcripts at http://127.0.0.1:4984
|
|
243
|
+
reeval studio build --out site # the same thing as a static site for GitHub Pages or any host
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
The studio shows each run's commit, pass counts and drift. For compares it shows per-cell pass
|
|
247
|
+
rates, lift and effort, and a run's saved analyses in its Analysis tab, and for each attempt the transcript, diff, checks and judge input. `reeval studio`
|
|
248
|
+
adds a Debug tab for attempts with local debug data.
|
|
249
|
+
|
|
250
|
+
## Learn more
|
|
251
|
+
|
|
252
|
+
The user docs in [`docs/`](./docs) are a [docs.page](https://use.docs.page) site: a Guide from a
|
|
253
|
+
first eval to CI, Concepts, Recipes, and a Reference tab with every option, command, flag, exit
|
|
254
|
+
code and file format. Preview them with `npx @docs.page/cli preview`. Coding agents writing evals
|
|
255
|
+
can load the reeval skill in [`.agents/skills/reeval/SKILL.md`](./.agents/skills/reeval/SKILL.md).
|
|
256
|
+
[AGENTS.md](./AGENTS.md) covers developing this repo.
|
|
257
|
+
|
|
258
|
+
## License
|
|
259
|
+
|
|
260
|
+
Licensed under the [Apache License, Version 2.0](./LICENSE). Copyright 2026 Invertase Limited.
|
|
261
|
+
Questions about licensing: [oss@invertase.io](mailto:oss@invertase.io).
|
|
262
|
+
|
|
263
|
+
The published package inlines some third-party code (the studio app's React, Radix UI and
|
|
264
|
+
shadcn/ui components, and Claude Code's patched adapter). Its `THIRD_PARTY_NOTICES.md`, generated
|
|
265
|
+
at build time, lists each of those packages with its version, licence and notices.
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -7,8 +7,8 @@ with its own licence.
|
|
|
7
7
|
|
|
8
8
|
| Package | Version | Licence | Inlined into |
|
|
9
9
|
|---|---|---|---|
|
|
10
|
-
| @ai-sdk/harness | 1.0.
|
|
11
|
-
| @ai-sdk/harness-claude-code | 1.0.
|
|
10
|
+
| @ai-sdk/harness | 1.0.148 | Apache-2.0 | dist/bridge |
|
|
11
|
+
| @ai-sdk/harness-claude-code | 1.0.152 | Apache-2.0 | dist, dist/bridge |
|
|
12
12
|
| @floating-ui/core | 1.8.0 | MIT | dist/studio-app |
|
|
13
13
|
| @floating-ui/dom | 1.8.0 | MIT | dist/studio-app |
|
|
14
14
|
| @floating-ui/react-dom | 2.1.9 | MIT | dist/studio-app |
|
|
@@ -72,7 +72,7 @@ with its own licence.
|
|
|
72
72
|
| use-sync-external-store | 1.7.0 | MIT | dist/studio-app |
|
|
73
73
|
| shadcn/ui | | MIT | 16 source files |
|
|
74
74
|
|
|
75
|
-
## @ai-sdk/harness@1.0.
|
|
75
|
+
## @ai-sdk/harness@1.0.148
|
|
76
76
|
|
|
77
77
|
- Licence: Apache-2.0
|
|
78
78
|
- Source: https://github.com/vercel/ai
|
|
@@ -94,12 +94,12 @@ See the License for the specific language governing permissions and
|
|
|
94
94
|
limitations under the License.
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
-
## @ai-sdk/harness-claude-code@1.0.
|
|
97
|
+
## @ai-sdk/harness-claude-code@1.0.152
|
|
98
98
|
|
|
99
99
|
- Licence: Apache-2.0
|
|
100
100
|
- Source: https://github.com/vercel/ai
|
|
101
101
|
- Inlined into: dist, dist/bridge
|
|
102
|
-
- Modified: dist/bridge/index.mjs, with patches/@ai-sdk__harness-claude-code@1.0.
|
|
102
|
+
- Modified: dist/bridge/index.mjs, with patches/@ai-sdk__harness-claude-code@1.0.152.patch
|
|
103
103
|
|
|
104
104
|
```text
|
|
105
105
|
Copyright 2023 Vercel, Inc.
|