reeval 0.6.3 → 0.8.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.
Files changed (226) hide show
  1. package/README.md +265 -0
  2. package/THIRD_PARTY_NOTICES.md +5 -5
  3. package/assets/package.json +1 -1
  4. package/assets/pnpm-lock.yaml +30 -307
  5. package/dist/{agent-DKuFnZPM.js → agent-B72lZ6w5.js} +6 -6
  6. package/dist/{agent-DKuFnZPM.js.map → agent-B72lZ6w5.js.map} +1 -1
  7. package/dist/{agent-oirN_8IS.js → agent-Cfj1wD4D.js} +5 -5
  8. package/dist/{agent-oirN_8IS.js.map → agent-Cfj1wD4D.js.map} +1 -1
  9. package/dist/{analysis-BV3TCznv.js → analysis-BTNnKh1w.js} +4 -4
  10. package/dist/{analysis-BV3TCznv.js.map → analysis-BTNnKh1w.js.map} +1 -1
  11. package/dist/{assemble-DYCDgbTq.js → assemble-BbFVjHeS.js} +125 -14
  12. package/dist/assemble-BbFVjHeS.js.map +1 -0
  13. package/dist/assemble-CXiTVyHD.js +2 -0
  14. package/dist/bridge/index.mjs +66 -7
  15. package/dist/bridge/package.json +3 -3
  16. package/dist/bridge/pnpm-lock.yaml +84 -89
  17. package/dist/capture-BXhBfmsM.js +1206 -0
  18. package/dist/capture-BXhBfmsM.js.map +1 -0
  19. package/dist/capture-N7r3yeak.js +2 -0
  20. package/dist/{cli-BIhzl_v_.js → cli-BMjTKY-D.js} +151 -193
  21. package/dist/cli-BMjTKY-D.js.map +1 -0
  22. package/dist/{cli-spec-DUeyuSIQ.js → cli-spec-DSvKsOK4.js} +25 -11
  23. package/dist/cli-spec-DSvKsOK4.js.map +1 -0
  24. package/dist/cli.js +2 -2
  25. package/dist/client-CwzU9CiW.js +256 -0
  26. package/dist/client-CwzU9CiW.js.map +1 -0
  27. package/dist/{command-B8tcwUJk.js → command-BK4kOgkH.js} +15 -15
  28. package/dist/{command-B8tcwUJk.js.map → command-BK4kOgkH.js.map} +1 -1
  29. package/dist/{command-fJpZK205.js → command-BdstSEHu.js} +7 -7
  30. package/dist/{command-fJpZK205.js.map → command-BdstSEHu.js.map} +1 -1
  31. package/dist/{command-DuJBqcwu.js → command-BqJtFiPy.js} +153 -36
  32. package/dist/command-BqJtFiPy.js.map +1 -0
  33. package/dist/command-CTVGuH8y.js +2 -0
  34. package/dist/{command-KoOJ0ntD.js → command-JPGNzFsA.js} +9 -9
  35. package/dist/{command-KoOJ0ntD.js.map → command-JPGNzFsA.js.map} +1 -1
  36. package/dist/{commands-CHdTfPB6.js → commands-B618jg3C.js} +6 -6
  37. package/dist/{commands-CHdTfPB6.js.map → commands-B618jg3C.js.map} +1 -1
  38. package/dist/{commands-DE604ldB.js → commands-Duo2ur3r.js} +1 -1
  39. package/dist/{config-7-gXOgQf.js → config-B-E0xRkf.js} +4 -4
  40. package/dist/{config-7-gXOgQf.js.map → config-B-E0xRkf.js.map} +1 -1
  41. package/dist/db-CnoUy-FZ.js +3 -0
  42. package/dist/{db-DvkzBhM6.js → db-DFbtGIYy.js} +84 -37
  43. package/dist/db-DFbtGIYy.js.map +1 -0
  44. package/dist/{dist-DsZNsoD3.js → dist-CTzThfu_.js} +3 -3
  45. package/dist/{dist-DsZNsoD3.js.map → dist-CTzThfu_.js.map} +1 -1
  46. package/dist/{docker-BBOMThRi.js → docker-2qns7hJ2.js} +4 -4
  47. package/dist/{docker-BBOMThRi.js.map → docker-2qns7hJ2.js.map} +1 -1
  48. package/dist/{docker-CiNVc7di.js → docker-D6t23fZl.js} +1 -1
  49. package/dist/{docker-CzFiN2AZ.js → docker-IFRW8K5f.js} +2 -2
  50. package/dist/{docker-CzFiN2AZ.js.map → docker-IFRW8K5f.js.map} +1 -1
  51. package/dist/{doctor-C06kYs2r.js → doctor-B6oYTPoR.js} +11 -11
  52. package/dist/{doctor-C06kYs2r.js.map → doctor-B6oYTPoR.js.map} +1 -1
  53. package/dist/{egress-De64jpeQ.js → egress-IUKdeayc.js} +17 -10
  54. package/dist/egress-IUKdeayc.js.map +1 -0
  55. package/dist/fake-instructions-CUI33I_F.js +32 -0
  56. package/dist/fake-instructions-CUI33I_F.js.map +1 -0
  57. package/dist/{files-BqIF7iu4.js → files-B6aVVGrk.js} +3 -3
  58. package/dist/{files-BqIF7iu4.js.map → files-B6aVVGrk.js.map} +1 -1
  59. package/dist/files-BpYgF2z0.js +2 -0
  60. package/dist/{files-Cq99wif-.js → files-CMSUXAEg.js} +16 -5
  61. package/dist/{files-Cq99wif-.js.map → files-CMSUXAEg.js.map} +1 -1
  62. package/dist/files-YCCVaTQC.js +2 -0
  63. package/dist/{git-Dnl-kXgD.js → git-EmdFJ4DY.js} +2 -2
  64. package/dist/{git-Dnl-kXgD.js.map → git-EmdFJ4DY.js.map} +1 -1
  65. package/dist/{harness-3QUEWx8G.js → harness-iqWeJQMM.js} +35 -12
  66. package/dist/harness-iqWeJQMM.js.map +1 -0
  67. package/dist/harness.d.ts +22 -11
  68. package/dist/harness.js +1 -1
  69. package/dist/{header-IE-uIvX0.js → header-_F0EWob3.js} +2 -2
  70. package/dist/{header-IE-uIvX0.js.map → header-_F0EWob3.js.map} +1 -1
  71. package/dist/{images-B8Mp_VXZ.js → images-CscddrJU.js} +6 -6
  72. package/dist/{images-B8Mp_VXZ.js.map → images-CscddrJU.js.map} +1 -1
  73. package/dist/index.d.ts +13 -8
  74. package/dist/index.js +99 -24
  75. package/dist/index.js.map +1 -1
  76. package/dist/init-DBHyi92L.js +2 -0
  77. package/dist/{init-B8mw86E6.js → init-NQfIvi-0.js} +4 -4
  78. package/dist/{init-B8mw86E6.js.map → init-NQfIvi-0.js.map} +1 -1
  79. package/dist/{invocations-DHC946b-.js → invocations-nPvrlZPj.js} +3 -3
  80. package/dist/{invocations-DHC946b-.js.map → invocations-nPvrlZPj.js.map} +1 -1
  81. package/dist/{judge-7WjNdU9z.js → judge-CeYPN8P_.js} +2 -2
  82. package/dist/{judge-7WjNdU9z.js.map → judge-CeYPN8P_.js.map} +1 -1
  83. package/dist/judge.d.ts +5 -4
  84. package/dist/judge.js +4 -3
  85. package/dist/judge.js.map +1 -1
  86. package/dist/{live-DZkCtmNP.js → live-S0qODb6o.js} +2 -2
  87. package/dist/{live-DZkCtmNP.js.map → live-S0qODb6o.js.map} +1 -1
  88. package/dist/model-BbLGRtOs.js +2 -0
  89. package/dist/{model-CtrJdFi6.js → model-BndnmcLm.js} +241 -26
  90. package/dist/model-BndnmcLm.js.map +1 -0
  91. package/dist/{model-Cp6NoAT3.js → model-Cl507trk.js} +8 -7
  92. package/dist/model-Cl507trk.js.map +1 -0
  93. package/dist/{model-Bv8uUZ07.js → model-DEQo3FkZ.js} +6 -6
  94. package/dist/model-DEQo3FkZ.js.map +1 -0
  95. package/dist/{model-BeoRxCcJ.js → model-DMOraCcm.js} +1 -1
  96. package/dist/{opencode-providers-DjlA5fuC.js → opencode-providers-ByD85Q0l.js} +21 -7
  97. package/dist/{opencode-providers-DjlA5fuC.js.map → opencode-providers-ByD85Q0l.js.map} +1 -1
  98. package/dist/{options-CWYB613o.js → options-DPUlm4FI.js} +152 -29
  99. package/dist/options-DPUlm4FI.js.map +1 -0
  100. package/dist/pairs-DDsjgm5F.js +149 -0
  101. package/dist/pairs-DDsjgm5F.js.map +1 -0
  102. package/dist/{plan-CsPE24pp.js → plan-PPc4-6xd.js} +35 -18
  103. package/dist/plan-PPc4-6xd.js.map +1 -0
  104. package/dist/{preflight-r0DgibQV.js → preflight-CddycK92.js} +3 -3
  105. package/dist/{preflight-r0DgibQV.js.map → preflight-CddycK92.js.map} +1 -1
  106. package/dist/{preflight-DKZjb7ZH.js → preflight-Zumy0adT.js} +1 -1
  107. package/dist/{prepare-DOkvAa9D.js → prepare-CKdMoC7e.js} +2 -2
  108. package/dist/{prepare-CTLg3nuk.js → prepare-CsP-057p.js} +4 -4
  109. package/dist/{prepare-CTLg3nuk.js.map → prepare-CsP-057p.js.map} +1 -1
  110. package/dist/{provenance--cVNo80L.js → provenance-CsX6PHDR.js} +2 -2
  111. package/dist/{provenance--cVNo80L.js.map → provenance-CsX6PHDR.js.map} +1 -1
  112. package/dist/{providers-DNLQzVnc.js → providers-DkwiLKDS.js} +3 -3
  113. package/dist/{providers-DNLQzVnc.js.map → providers-DkwiLKDS.js.map} +1 -1
  114. package/dist/{providers-B2cSfewm.js → providers-yXZZcPfx.js} +1 -1
  115. package/dist/{query-DTtIZaua.js → query-BUcwCYsI.js} +2 -2
  116. package/dist/{query-DTtIZaua.js.map → query-BUcwCYsI.js.map} +1 -1
  117. package/dist/query-noGbekGF.js +2 -0
  118. package/dist/{questions-ZdT3JDyQ.js → questions-BEPj98fZ.js} +27 -118
  119. package/dist/questions-BEPj98fZ.js.map +1 -0
  120. package/dist/{record-CbuQH4l1.js → record-BRP9vZrj.js} +21 -1
  121. package/dist/record-BRP9vZrj.js.map +1 -0
  122. package/dist/results-BcSHWbgB.js +52 -0
  123. package/dist/results-BcSHWbgB.js.map +1 -0
  124. package/dist/resume-DZ9AmfPv.js +406 -0
  125. package/dist/resume-DZ9AmfPv.js.map +1 -0
  126. package/dist/{matchers-Bp-OH1vj.js → runtime-Dfb2r-cS.js} +2 -179
  127. package/dist/runtime-Dfb2r-cS.js.map +1 -0
  128. package/dist/sandbox.d.ts +1 -1
  129. package/dist/{server-BFlKK2j6.js → server-Da60oe4b.js} +9 -9
  130. package/dist/{server-BFlKK2j6.js.map → server-Da60oe4b.js.map} +1 -1
  131. package/dist/server-DiGR4JxK.js +2 -0
  132. package/dist/{source-B2ASbTdc.js → source-BVOnL3sn.js} +2 -2
  133. package/dist/{source-B2ASbTdc.js.map → source-BVOnL3sn.js.map} +1 -1
  134. package/dist/source-Dqu0jKwx.js +2 -0
  135. package/dist/src-BHNiP-5A.js +4 -0
  136. package/dist/{src-CDiGyoDt.js → src-BT4dB5CO.js} +1 -1
  137. package/dist/src-BT5pwco0.js +3 -0
  138. package/dist/src-BrMbt1B2.js +3 -0
  139. package/dist/{src-s-VffoYK.js → src-C1E9HDWO.js} +5 -3
  140. package/dist/{src-s-VffoYK.js.map → src-C1E9HDWO.js.map} +1 -1
  141. package/dist/{src-CqsERnCZ.js → src-CxTC5kNI.js} +10 -6
  142. package/dist/{src-CqsERnCZ.js.map → src-CxTC5kNI.js.map} +1 -1
  143. package/dist/src-D13vpMh0.js +3 -0
  144. package/dist/{src-DxFJrMUA.js → src-D95O-Smv.js} +12 -7
  145. package/dist/{src-DxFJrMUA.js.map → src-D95O-Smv.js.map} +1 -1
  146. package/dist/{src-CVoCJjEe.js → src-DDNXsD9H.js} +29 -5
  147. package/dist/{src-CVoCJjEe.js.map → src-DDNXsD9H.js.map} +1 -1
  148. package/dist/{src-DSE0FWaU.js → src-DVy58-zZ.js} +2 -2
  149. package/dist/{src-DSE0FWaU.js.map → src-DVy58-zZ.js.map} +1 -1
  150. package/dist/{src-BHFNhTTQ.js → src-D_cXqmnI.js} +59 -49
  151. package/dist/{src-BHFNhTTQ.js.map → src-D_cXqmnI.js.map} +1 -1
  152. package/dist/src-DdRlaiRc.js +3 -0
  153. package/dist/{src-zOeHvrzR.js → src-TdJuDYqB.js} +13 -13
  154. package/dist/{src-zOeHvrzR.js.map → src-TdJuDYqB.js.map} +1 -1
  155. package/dist/src-xczuQbE6.js +3 -0
  156. package/dist/{state-DBKcUJ2u.js → state-B72h_jEw.js} +14 -14
  157. package/dist/{state-DBKcUJ2u.js.map → state-B72h_jEw.js.map} +1 -1
  158. package/dist/state-Bf_Vq_pV.js +2 -0
  159. package/dist/studio-app/assets/{index-WQPq5K3E.js → index-Bgr-efZm.js} +9 -9
  160. package/dist/studio-app/index.html +1 -1
  161. package/dist/{summary-BBABbon9.js → summary-CJdxaRdM.js} +7 -7
  162. package/dist/{summary-BBABbon9.js.map → summary-CJdxaRdM.js.map} +1 -1
  163. package/dist/{summary-CahgSiVt.js → summary-k3mhWWXv.js} +8 -4
  164. package/dist/summary-k3mhWWXv.js.map +1 -0
  165. package/dist/testing.d.ts +14 -0
  166. package/dist/testing.js +2 -0
  167. package/dist/tool-target-BU1JnSE9.js +34 -0
  168. package/dist/tool-target-BU1JnSE9.js.map +1 -0
  169. package/dist/{tools-53YHomyk.js → tools-D0gF2QoV.js} +3 -3
  170. package/dist/{tools-53YHomyk.js.map → tools-D0gF2QoV.js.map} +1 -1
  171. package/dist/{types-CoAfC-_k.d.ts → types-_LOFdPUf.d.ts} +266 -36
  172. package/dist/{usage-DsYUqIUM.js → usage-DDF_6tkV.js} +4 -2
  173. package/dist/{usage-DsYUqIUM.js.map → usage-DDF_6tkV.js.map} +1 -1
  174. package/dist/view/index.d.ts +19 -6
  175. package/dist/view/index.js +6 -6
  176. package/dist/{views-Drl2OXuj.js → views-DZ1YDqey.js} +4 -4
  177. package/dist/{views-Drl2OXuj.js.map → views-DZ1YDqey.js.map} +1 -1
  178. package/dist/views-Ds0GXWtX.js +3 -0
  179. package/dist/{worker-entry-mCctDzig.js → worker-entry-DXeyWY-m.js} +73 -2
  180. package/dist/worker-entry-DXeyWY-m.js.map +1 -0
  181. package/dist/{workspace-BT4jd04k.js → workspace-DpXdrslo.js} +3 -3
  182. package/dist/{workspace-BT4jd04k.js.map → workspace-DpXdrslo.js.map} +1 -1
  183. package/dist/{workspace-U6M1gKzo.js → workspace-DzNL-3iO.js} +1 -1
  184. package/package.json +14 -8
  185. package/dist/assemble-DYCDgbTq.js.map +0 -1
  186. package/dist/assemble-DykFydSU.js +0 -2
  187. package/dist/capture-CisAmy-G.js +0 -587
  188. package/dist/capture-CisAmy-G.js.map +0 -1
  189. package/dist/capture-DquyqAHC.js +0 -2
  190. package/dist/cli-BIhzl_v_.js.map +0 -1
  191. package/dist/cli-spec-DUeyuSIQ.js.map +0 -1
  192. package/dist/command-CwuwuKJD.js +0 -2
  193. package/dist/command-DuJBqcwu.js.map +0 -1
  194. package/dist/db-BVE1kZ2w.js +0 -2
  195. package/dist/db-DvkzBhM6.js.map +0 -1
  196. package/dist/egress-De64jpeQ.js.map +0 -1
  197. package/dist/files-Cs8tbYQY.js +0 -2
  198. package/dist/files-wUtoO5aA.js +0 -2
  199. package/dist/harness-3QUEWx8G.js.map +0 -1
  200. package/dist/init-DDEpamaf.js +0 -2
  201. package/dist/matchers-Bp-OH1vj.js.map +0 -1
  202. package/dist/model-Bv8uUZ07.js.map +0 -1
  203. package/dist/model-C_B5JrhL.js +0 -2
  204. package/dist/model-Cp6NoAT3.js.map +0 -1
  205. package/dist/model-CtrJdFi6.js.map +0 -1
  206. package/dist/options-CWYB613o.js.map +0 -1
  207. package/dist/plan-CsPE24pp.js.map +0 -1
  208. package/dist/query-BWc_5Lhe.js +0 -2
  209. package/dist/questions-ZdT3JDyQ.js.map +0 -1
  210. package/dist/record-CbuQH4l1.js.map +0 -1
  211. package/dist/results-CrFs7_In.js +0 -37
  212. package/dist/results-CrFs7_In.js.map +0 -1
  213. package/dist/server-HRUHVcHc.js +0 -2
  214. package/dist/source-Co7h89WK.js +0 -2
  215. package/dist/src-BE19tji4.js +0 -3
  216. package/dist/src-Bo8GFUXg.js +0 -3
  217. package/dist/src-BoziVhKa.js +0 -3
  218. package/dist/src-CFxHPwQA.js +0 -3
  219. package/dist/src-CPkDaNxp.js +0 -3
  220. package/dist/src-D-go5-qz.js +0 -4
  221. package/dist/state-BaHjYXr1.js +0 -2
  222. package/dist/stats-BNAHlNMD.js +0 -114
  223. package/dist/stats-BNAHlNMD.js.map +0 -1
  224. package/dist/summary-CahgSiVt.js.map +0 -1
  225. package/dist/views-BhGinAz_.js +0 -3
  226. 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,
229
+ agent time and tokens, 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.
@@ -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.144 | Apache-2.0 | dist/bridge |
11
- | @ai-sdk/harness-claude-code | 1.0.148 | Apache-2.0 | dist, dist/bridge |
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.144
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.148
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.148.patch
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.
@@ -4,6 +4,6 @@
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "dependencies": {
7
- "@earendil-works/pi-coding-agent": "1.1.0"
7
+ "@earendil-works/pi-coding-agent": "1.2.0"
8
8
  }
9
9
  }