@webywz/eval-smoke 0.3.1 → 0.5.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 CHANGED
@@ -1,8 +1,22 @@
1
1
  # eval-smoke
2
2
 
3
- One-minute smoke evals for prompts and models.
3
+ [![npm](https://img.shields.io/npm/v/@webywz/eval-smoke?style=flat-square&color=111111&labelColor=eeeeee)](https://www.npmjs.com/package/@webywz/eval-smoke)
4
+ [![node](https://img.shields.io/node/v/@webywz/eval-smoke?style=flat-square&color=111111&labelColor=eeeeee)](https://www.npmjs.com/package/@webywz/eval-smoke)
5
+ [![license](https://img.shields.io/npm/l/@webywz/eval-smoke?style=flat-square&color=111111&labelColor=eeeeee)](./LICENSE)
4
6
 
5
- Full eval harnesses are heavy. `eval-smoke` is the tiny local suite you run before a ship: a handful of cases, pass/fail, exit code 1 on regression. YAML or JSON in, a CI-friendly report out — including optional JUnit XML.
7
+ > **One-minute smoke evals for prompts and models.**
8
+ >
9
+ > **一分钟烟雾测试:在发版前拦住 prompt / 模型回归。**
10
+
11
+ YAML or JSON in. Pass/fail out. Exit `1` on regression. Optional JUnit for CI.
12
+
13
+ YAML 或 JSON 进,通过/失败出。回归时退出码 `1`。可选 JUnit 对接 CI。
14
+
15
+ ---
16
+
17
+ ## English
18
+
19
+ ### Demo
6
20
 
7
21
  ```bash
8
22
  npm i -g @webywz/eval-smoke
@@ -10,8 +24,8 @@ eval-smoke init
10
24
  eval-smoke suite.yaml --mock
11
25
  ```
12
26
 
13
- ```
14
- eval-smoke 0.3.0 · mock · gpt-4o-mini · smoke · 4 cases
27
+ ```text
28
+ eval-smoke 0.5.0 · mock · gpt-4o-mini · smoke · 4 cases
15
29
 
16
30
  pass greeting 0ms
17
31
  pass capital-france 0ms
@@ -21,48 +35,60 @@ eval-smoke 0.3.0 · mock · gpt-4o-mini · smoke · 4 cases
21
35
  4 passed 0 failed 1ms
22
36
  ```
23
37
 
24
- `--mock` is offline and deterministic. Contributors and CI do not need API keys.
38
+ `--mock` is offline and deterministic. CI and contributors do not need API keys.
39
+
40
+ ### Features
25
41
 
26
- ## Install
42
+ | | |
43
+ | --- | --- |
44
+ | **Tiny suite** | A handful of cases — not a full harness |
45
+ | **Matchers** | `equals`, `contains`, `not_contains`, `regex`, `json`, `min_tokens` / `max_tokens` |
46
+ | **init** | `eval-smoke init` scaffolds a mock-friendly `suite.yaml` |
47
+ | **Filter** | `--only` by id substring or `/regex/` |
48
+ | **CI** | `--junit`, `--html`, `--github-summary`, exit codes, `--fail-fast`, `--concurrency` |
49
+ | **Baseline** | `--baseline` / `--update-baseline` catch pass→fail regressions |
50
+ | **Providers** | Mock offline; live OpenAI / Anthropic when keyed |
51
+
52
+ ### Install
27
53
 
28
54
  ```bash
29
55
  npm i -g @webywz/eval-smoke
30
- # or, from a project:
56
+ # or one-off:
31
57
  npx @webywz/eval-smoke suite.yaml --mock
32
58
  ```
33
59
 
34
- Requires Node 18+.
60
+ Node 18+.
35
61
 
36
- ## 60-second start
37
-
38
- 1. Scaffold a suite with `eval-smoke init`, or copy the example.
39
- 2. Run mock mode until the runner and matchers look right.
40
- 3. Point the same file at a live model when you are ready.
62
+ ### Quick start
41
63
 
42
64
  ```bash
43
65
  eval-smoke init
44
66
  eval-smoke suite.yaml --mock
45
67
  eval-smoke suite.yaml --mock --only greeting
46
68
  eval-smoke suite.yaml --mock --junit junit.xml
69
+ eval-smoke suite.yaml --mock --html report.html
47
70
  eval-smoke examples/suite.yaml --mock --json
48
71
  eval-smoke examples/suite.yaml --mock --concurrency 4
72
+ eval-smoke examples/suite.yaml --mock --update-baseline baseline.json
73
+ eval-smoke examples/suite.yaml --mock --baseline baseline.json
74
+ eval-smoke examples/suite.yaml --mock --github-summary
49
75
  OPENAI_API_KEY=sk-... eval-smoke examples/suite.yaml
50
76
  ANTHROPIC_API_KEY=sk-ant-... eval-smoke examples/suite.yaml --model claude-3-5-haiku-20241022
51
77
  ```
52
78
 
53
- ## `eval-smoke init`
79
+ ### `eval-smoke init`
54
80
 
55
81
  ```bash
56
82
  eval-smoke init # writes ./suite.yaml
57
83
  eval-smoke init ./prompts # writes ./prompts/suite.yaml
58
- eval-smoke init --force # overwrite an existing suite.yaml
84
+ eval-smoke init --force # overwrite
59
85
  ```
60
86
 
61
- Creates a small mock-friendly suite (greeting, capital, arithmetic, refusal). Refuses to overwrite unless you pass `--force`.
87
+ Creates a small mock-friendly suite (greeting, capital, arithmetic, refusal). Refuses to overwrite unless `--force`.
62
88
 
63
- ## Suite format
89
+ ### Suite format
64
90
 
65
- A suite is a YAML or JSON list of cases, or an object with a `cases` array.
91
+ YAML or JSON — a list of cases, or an object with `cases`:
66
92
 
67
93
  ```yaml
68
94
  name: demo
@@ -87,132 +113,222 @@ cases:
87
113
  city: Paris
88
114
  ```
89
115
 
90
- A bare list is also valid:
116
+ | Field | Notes |
117
+ | --- | --- |
118
+ | `id` / `prompt` | Required |
119
+ | `expect.contains` | Substring, case-insensitive; string or list |
120
+ | `expect.not_contains` | Must be absent |
121
+ | `expect.regex` | `/pattern/i`, `(?i)pattern`, or raw |
122
+ | `expect.equals` | Exact after NFC + LF normalize |
123
+ | `expect.json` | `true` = parseable; object/array = deep-equal |
124
+ | `expect.min_tokens` / `max_tokens` | Approx via `ceil(chars / 4)` |
125
+ | `system` / `model` | Suite or case |
126
+ | `provider` | `openai`, `anthropic`, or `mock` |
127
+
128
+ At least one matcher per case. Failures print expected/actual (and a first-diff for `equals`).
129
+
130
+ ### CLI
91
131
 
92
- ```yaml
93
- - id: capital-france
94
- prompt: What is the capital of France? Answer with the city name only.
95
- expect:
96
- contains: Paris
97
132
  ```
133
+ eval-smoke <suite.yaml|suite.json> [options]
134
+ eval-smoke init [dir] [--force]
135
+ ```
136
+
137
+ | Flag | Meaning |
138
+ | --- | --- |
139
+ | `--mock` | Offline deterministic runner |
140
+ | `--json` | Machine-readable JSON report |
141
+ | `--junit <path>` | Write JUnit XML (still prints human report unless `--json`) |
142
+ | `--html <path>` | Write self-contained HTML report (offline, no CDN) |
143
+ | `--baseline <path>` | Exit `1` if a baseline-passing case now fails or is missing |
144
+ | `--update-baseline <path>` | Write current report JSON (pretty) to path |
145
+ | `--github-summary [path]` | Append pass/fail Markdown to `$GITHUB_STEP_SUMMARY` or path |
146
+ | `--only <pattern>` | Cases whose id contains pattern, or matches `/regex/` |
147
+ | `--fail-fast` | Stop after first failure |
148
+ | `--concurrency <n>` | Parallel cases (default 1; ignored with `--fail-fast`) |
149
+ | `--timeout <ms>` | Per-case timeout (default 30000) |
150
+ | `--model <name>` | Override suite model |
151
+ | `--provider <name>` | `openai` \| `anthropic` \| `mock` |
152
+ | `--force` | Overwrite for `init` |
153
+ | `--no-color` | Disable ANSI (`NO_COLOR` respected) |
154
+
155
+ | Exit | Meaning |
156
+ | --- | --- |
157
+ | `0` | All passed |
158
+ | `1` | One or more failed |
159
+ | `2` | Usage / config error (missing file, bad YAML, no key, `--only` matched nothing) |
160
+
161
+ ### Runners
98
162
 
99
- | Field | Where | Notes |
100
- | --- | --- | --- |
101
- | `id` | case | Required, unique. |
102
- | `prompt` | case | Required. |
103
- | `expect.contains` | case | Substring, case-insensitive. String or list; all must match. |
104
- | `expect.not_contains` | case | Substring must be absent. String or list. |
105
- | `expect.regex` | case | JavaScript regex. `/pattern/i`, `(?i)pattern`, or a raw pattern. |
106
- | `expect.equals` | case | Exact match after NFC + LF normalize; one trailing newline ignored. |
107
- | `expect.json` | case | `true` = must parse as JSON; object/array = deep-equal after parse. |
108
- | `expect.min_tokens` | case | Approximate floor using `ceil(chars / 4)`. |
109
- | `expect.max_tokens` | case | Approximate cap using `ceil(chars / 4)`. |
110
- | `system` | suite or case | Optional system prompt for live providers. |
111
- | `model` | suite or case | Live default: `gpt-4o-mini` or `claude-3-5-haiku-20241022`. |
112
- | `provider` | suite | `openai`, `anthropic`, or `mock`. |
163
+ **Mock** never calls the network. Canned replies keep the example suite green in CI:
113
164
 
114
- At least one matcher is required on every case. Failed checks print expected/actual (and a first-diff pointer for `equals`).
165
+ | Prompt contains | Mock reply |
166
+ | --- | --- |
167
+ | `hello` | `Hello` |
168
+ | `capital of France` | `Paris` |
169
+ | `2 + 2` | `4` |
170
+ | `ignore previous` / `PWNED` | a refusal without `PWNED` |
115
171
 
116
- ## CLI
172
+ **Live OpenAI** with `OPENAI_API_KEY` (optional `OPENAI_BASE_URL`). **Live Anthropic** with `ANTHROPIC_API_KEY`, or when the model name starts with `claude`.
117
173
 
174
+ ### Library
175
+
176
+ ```ts
177
+ import { loadSuite, runSuite, formatJUnit, initSuite } from "@webywz/eval-smoke";
178
+
179
+ initSuite(".", { force: false });
180
+ const suite = loadSuite("suite.yaml");
181
+ const report = await runSuite(suite, { mock: true, concurrency: 4 });
182
+ console.log(formatJUnit(report));
183
+ if (!report.ok) process.exit(1);
118
184
  ```
119
- eval-smoke <suite.yaml|suite.json> [options]
120
- eval-smoke init [dir] [--force]
121
185
 
122
- --mock Offline runner with deterministic fake answers
123
- --json Machine-readable JSON report
124
- --junit <path> Write JUnit XML to path (still prints human report unless --json)
125
- --only <pattern> Run cases whose id contains pattern, or matches /regex/
126
- --fail-fast Stop after the first failing case
127
- --concurrency <n> Run up to N cases in parallel (default: 1; ignored with --fail-fast)
128
- --timeout <ms> Per-case timeout (default: 30000)
129
- --model <name> Override suite model
130
- --provider <name> openai | anthropic | mock
131
- --force Overwrite existing suite.yaml for init
132
- --no-color Disable ANSI colors (also respects NO_COLOR)
133
- -h, --help
134
- -v, --version
186
+ ### CI snippet
187
+
188
+ ```yaml
189
+ - run: npx @webywz/eval-smoke@0.5.0 prompts/smoke.yaml --mock --junit junit.xml
190
+ - uses: actions/upload-artifact@v4
191
+ if: always()
192
+ with:
193
+ name: junit
194
+ path: junit.xml
135
195
  ```
136
196
 
137
- | Exit | Meaning |
197
+ ### Links
198
+
199
+ | | |
138
200
  | --- | --- |
139
- | 0 | Every case passed |
140
- | 1 | One or more cases failed |
141
- | 2 | Usage or configuration error (missing file, bad YAML, no API key, `--only` matched nothing) |
201
+ | npm | [npmjs.com/package/@webywz/eval-smoke](https://www.npmjs.com/package/@webywz/eval-smoke) |
202
+ | Issues | [github.com/webywz/eval-smoke/issues](https://github.com/webywz/eval-smoke/issues) |
203
+ | License | [MIT](./LICENSE) © webywz |
204
+
205
+ ---
142
206
 
143
- ### `--only`
207
+ ## 中文
144
208
 
145
- Filter cases by `id` before running:
209
+ ### 演示
146
210
 
147
211
  ```bash
148
- eval-smoke suite.yaml --mock --only greeting # substring
149
- eval-smoke suite.yaml --mock --only '/^capital/' # /regex/ (same style as matchers)
150
- eval-smoke suite.yaml --mock --only '/hello/i'
212
+ npm i -g @webywz/eval-smoke
213
+ eval-smoke init
214
+ eval-smoke suite.yaml --mock
151
215
  ```
152
216
 
153
- Exits `2` if zero cases match.
217
+ `--mock` 离线、确定。CI 与贡献者不需要 API key。
154
218
 
155
- ### `--junit`
219
+ ### 功能
156
220
 
157
- After a run, write a JUnit XML report for CI while still printing the human (or `--json`) report to stdout:
221
+ | | |
222
+ | --- | --- |
223
+ | **轻量套件** | 少量用例,不是完整评测框架 |
224
+ | **匹配器** | `equals`、`contains`、`not_contains`、`regex`、`json`、token 上下限 |
225
+ | **init** | `eval-smoke init` 生成可 mock 的 `suite.yaml` |
226
+ | **过滤** | `--only`:id 子串或 `/regex/` |
227
+ | **CI** | `--junit`、`--html`、`--github-summary`、退出码、`--fail-fast`、`--concurrency` |
228
+ | **基线** | `--baseline` / `--update-baseline` 拦住 pass→fail 回归 |
229
+ | **提供商** | Mock 离线;有 key 时走 OpenAI / Anthropic |
230
+
231
+ ### 安装
158
232
 
159
233
  ```bash
160
- eval-smoke suite.yaml --mock --junit reports/junit.xml
234
+ npm i -g @webywz/eval-smoke
235
+ # 或一次性:
236
+ npx @webywz/eval-smoke suite.yaml --mock
161
237
  ```
162
238
 
163
- Failures include matcher detail (`expected` / `actual` / `detail`). Skipped cases (e.g. `--fail-fast`) become `<skipped/>`.
239
+ 需要 Node 18+。
240
+
241
+ ### 快速开始
164
242
 
165
- ## Runners
243
+ ```bash
244
+ eval-smoke init
245
+ eval-smoke suite.yaml --mock
246
+ eval-smoke suite.yaml --mock --only greeting
247
+ eval-smoke suite.yaml --mock --junit junit.xml
248
+ eval-smoke suite.yaml --mock --html report.html
249
+ eval-smoke examples/suite.yaml --mock --json
250
+ eval-smoke examples/suite.yaml --mock --concurrency 4
251
+ eval-smoke examples/suite.yaml --mock --update-baseline baseline.json
252
+ eval-smoke examples/suite.yaml --mock --baseline baseline.json
253
+ eval-smoke examples/suite.yaml --mock --github-summary
254
+ OPENAI_API_KEY=sk-... eval-smoke examples/suite.yaml
255
+ ANTHROPIC_API_KEY=sk-ant-... eval-smoke examples/suite.yaml --model claude-3-5-haiku-20241022
256
+ ```
166
257
 
167
- **Mock** (`--mock` or `--provider mock`) never calls a network. It returns canned replies matched from the prompt so the example suite stays green in CI:
258
+ ### `eval-smoke init`
168
259
 
169
- | Prompt contains | Mock reply |
260
+ ```bash
261
+ eval-smoke init # 写入 ./suite.yaml
262
+ eval-smoke init ./prompts # 写入 ./prompts/suite.yaml
263
+ eval-smoke init --force # 覆盖已有文件
264
+ ```
265
+
266
+ 生成小型 mock 友好套件(问候、首都、算术、拒答)。不加 `--force` 不会覆盖已有文件。
267
+
268
+ ### 套件格式
269
+
270
+ YAML 或 JSON —— 用例列表,或带 `cases` 的对象:
271
+
272
+ ```yaml
273
+ name: demo
274
+ model: gpt-4o-mini
275
+ cases:
276
+ - id: greeting
277
+ prompt: Reply with exactly the word Hello.
278
+ expect:
279
+ equals: Hello
280
+ contains: Hello
281
+ not_contains: Goodbye
282
+ regex: "(?i)^hello\\b"
283
+ min_tokens: 1
284
+ max_tokens: 32
285
+ ```
286
+
287
+ | 字段 | 说明 |
170
288
  | --- | --- |
171
- | `hello` | `Hello` |
172
- | `capital of France` | `Paris` |
173
- | `2 + 2` | `4` |
174
- | `ignore previous` / `PWNED` | a refusal that does not contain `PWNED` |
289
+ | `id` / `prompt` | 必填 |
290
+ | `expect.contains` | 子串,忽略大小写;字符串或列表 |
291
+ | `expect.not_contains` | 不得出现 |
292
+ | `expect.regex` | `/pattern/i`、`(?i)pattern` 或裸模式 |
293
+ | `expect.equals` | NFC + LF 规范化后精确匹配 |
294
+ | `expect.json` | `true` = 可解析;对象/数组 = 深相等 |
295
+ | `expect.min_tokens` / `max_tokens` | 约 `ceil(chars / 4)` |
296
+ | `system` / `model` | 套件级或用例级 |
297
+ | `provider` | `openai` / `anthropic` / `mock` |
175
298
 
176
- Anything else returns `[mock] No canned reply for this prompt.` — useful as a deliberate fail.
299
+ 每个用例至少要有一个匹配器。失败时打印期望/实际(`equals` 还有首差位置)。
177
300
 
178
- **Live OpenAI** when `OPENAI_API_KEY` is set. Optional `OPENAI_BASE_URL` for compatible servers.
301
+ ### 命令行要点
179
302
 
180
- **Live Anthropic** when `ANTHROPIC_API_KEY` is set, or when the model name starts with `claude`.
303
+ | 参数 | 含义 |
304
+ | --- | --- |
305
+ | `--mock` | 离线确定性运行 |
306
+ | `--json` | JSON 报告 |
307
+ | `--junit <path>` | 写出 JUnit XML(仍打印人类可读报告,除非同时 `--json`) |
308
+ | `--html <path>` | 写入自包含 HTML 报告(离线、无 CDN) |
309
+ | `--baseline <path>` | 基线里曾通过的用例若失败或缺失则退出 `1` |
310
+ | `--update-baseline <path>` | 把当前报告 JSON(pretty)写入路径 |
311
+ | `--github-summary [path]` | 追加通过/失败 Markdown 到 `$GITHUB_STEP_SUMMARY` 或指定路径 |
312
+ | `--only <pattern>` | 按 id 子串或 `/regex/` 过滤 |
313
+ | `--fail-fast` | 首个失败即停 |
314
+ | `--concurrency <n>` | 并行用例数(默认 1) |
315
+ | `--timeout <ms>` | 单用例超时(默认 30000) |
316
+ | `--model` / `--provider` | 覆盖模型 / 提供商 |
317
+ | `--force` | `init` 时覆盖 |
318
+
319
+ | 退出码 | 含义 |
320
+ | --- | --- |
321
+ | `0` | 全部通过 |
322
+ | `1` | 有失败 |
323
+ | `2` | 用法/配置错误(缺文件、坏 YAML、无 key、`--only` 无匹配) |
181
324
 
182
- If no key is set and you did not pass `--mock`, the CLI exits 2 with a short hint.
325
+ ### 运行器
183
326
 
184
- ## JSON report
327
+ **Mock** 永不联网。内置 canned 回复保证示例套件在 CI 里绿。
185
328
 
186
- ```bash
187
- eval-smoke examples/suite.yaml --mock --json
188
- ```
329
+ **Live OpenAI**:`OPENAI_API_KEY`(可选 `OPENAI_BASE_URL`)。**Live Anthropic**:`ANTHROPIC_API_KEY`,或模型名以 `claude` 开头。
189
330
 
190
- ```json
191
- {
192
- "ok": true,
193
- "version": "0.3.0",
194
- "runner": "mock",
195
- "model": "gpt-4o-mini",
196
- "suite": "demo",
197
- "passed": 5,
198
- "failed": 0,
199
- "skipped": 0,
200
- "duration_ms": 1,
201
- "cases": [
202
- {
203
- "id": "greeting",
204
- "ok": true,
205
- "duration_ms": 1,
206
- "output": "Hello",
207
- "checks": [{ "name": "contains", "ok": true, "expected": "Hello" }]
208
- }
209
- ]
210
- }
211
- ```
212
-
213
- Failed checks may include `actual` plus a multi-line `detail` (first-diff for `equals`).
214
-
215
- ## Programmatic API
331
+ ### 库用法
216
332
 
217
333
  ```ts
218
334
  import { loadSuite, runSuite, formatJUnit, initSuite } from "@webywz/eval-smoke";
@@ -224,21 +340,10 @@ console.log(formatJUnit(report));
224
340
  if (!report.ok) process.exit(1);
225
341
  ```
226
342
 
227
- `runSuite` also accepts a custom `complete` function, which is how the tests inject fakes without keys.
228
-
229
- ## CI
343
+ ### 链接
230
344
 
231
- GitHub Actions in this repo runs `npm test`, `npm run build`, and `eval-smoke examples/suite.yaml --mock` on Node 18/20/22. Drop this into another project:
232
-
233
- ```yaml
234
- - run: npx @webywz/eval-smoke@0.3.1 prompts/smoke.yaml --mock --junit junit.xml
235
- - uses: actions/upload-artifact@v4
236
- if: always()
237
- with:
238
- name: junit
239
- path: junit.xml
240
- ```
241
-
242
- ## License
243
-
244
- MIT
345
+ | | |
346
+ | --- | --- |
347
+ | npm | [npmjs.com/package/@webywz/eval-smoke](https://www.npmjs.com/package/@webywz/eval-smoke) |
348
+ | Issues | [github.com/webywz/eval-smoke/issues](https://github.com/webywz/eval-smoke/issues) |
349
+ | License | [MIT](./LICENSE) © webywz |
@@ -0,0 +1,25 @@
1
+ import type { Report } from "./types.js";
2
+ export interface BaselineCase {
3
+ id: string;
4
+ ok: boolean;
5
+ }
6
+ export interface BaselineFile {
7
+ cases: BaselineCase[];
8
+ }
9
+ /** Minimal shape: case id → ok. Accepts a full Report or a slim baseline. */
10
+ export declare function loadBaseline(path: string): Map<string, boolean>;
11
+ export interface BaselineCheck {
12
+ ok: boolean;
13
+ regressions: string[];
14
+ /** Human-readable lines */
15
+ report: string;
16
+ }
17
+ /**
18
+ * Fail if any case that passed in baseline now fails, or a baseline case is missing
19
+ * from the current run. New cases are OK. Skipped current cases that passed in
20
+ * baseline count as missing/regression.
21
+ */
22
+ export declare function checkBaseline(report: Report, baseline: Map<string, boolean>): BaselineCheck;
23
+ /** Pretty-print current report JSON for --update-baseline. */
24
+ export declare function writeBaseline(path: string, report: Report): void;
25
+ //# sourceMappingURL=baseline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baseline.d.ts","sourceRoot":"","sources":["../src/baseline.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAGzC,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,OAAO,CAAC;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,6EAA6E;AAC7E,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAmC/D;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,OAAO,CAAC;IACZ,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,2BAA2B;IAC3B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,aAAa,CAgC3F;AAED,8DAA8D;AAC9D,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAEhE"}
@@ -0,0 +1,86 @@
1
+ import { readFileSync, writeFileSync } from "node:fs";
2
+ import { UsageError } from "./types.js";
3
+ /** Minimal shape: case id → ok. Accepts a full Report or a slim baseline. */
4
+ export function loadBaseline(path) {
5
+ let raw;
6
+ try {
7
+ raw = readFileSync(path, "utf8");
8
+ }
9
+ catch (error) {
10
+ const code = error && typeof error === "object" && "code" in error
11
+ ? String(error.code)
12
+ : "";
13
+ if (code === "ENOENT")
14
+ throw new UsageError(`baseline not found: ${path}`);
15
+ throw error;
16
+ }
17
+ let parsed;
18
+ try {
19
+ parsed = JSON.parse(raw);
20
+ }
21
+ catch {
22
+ throw new UsageError(`baseline is not valid JSON: ${path}`);
23
+ }
24
+ if (!parsed || typeof parsed !== "object") {
25
+ throw new UsageError(`baseline must be a JSON object: ${path}`);
26
+ }
27
+ const cases = parsed.cases;
28
+ if (!Array.isArray(cases)) {
29
+ throw new UsageError(`baseline missing cases[]: ${path}`);
30
+ }
31
+ const map = new Map();
32
+ for (const entry of cases) {
33
+ if (!entry || typeof entry !== "object")
34
+ continue;
35
+ const id = entry.id;
36
+ const ok = entry.ok;
37
+ if (typeof id !== "string" || !id)
38
+ continue;
39
+ if (typeof ok !== "boolean")
40
+ continue;
41
+ map.set(id, ok);
42
+ }
43
+ return map;
44
+ }
45
+ /**
46
+ * Fail if any case that passed in baseline now fails, or a baseline case is missing
47
+ * from the current run. New cases are OK. Skipped current cases that passed in
48
+ * baseline count as missing/regression.
49
+ */
50
+ export function checkBaseline(report, baseline) {
51
+ const byId = new Map(report.cases.map((c) => [c.id, c]));
52
+ const regressions = [];
53
+ for (const [id, wasOk] of baseline) {
54
+ if (!wasOk)
55
+ continue; // only guard formerly-passing cases
56
+ const current = byId.get(id);
57
+ if (!current) {
58
+ regressions.push(`${id}: missing from current run (passed in baseline)`);
59
+ continue;
60
+ }
61
+ if (current.skipped) {
62
+ regressions.push(`${id}: skipped now (passed in baseline)`);
63
+ continue;
64
+ }
65
+ if (!current.ok) {
66
+ regressions.push(`${id}: was pass, now fail`);
67
+ }
68
+ }
69
+ const lines = ["baseline check:"];
70
+ if (regressions.length === 0) {
71
+ lines.push(" ok — no regressions vs baseline");
72
+ }
73
+ else {
74
+ lines.push(` ${regressions.length} regression(s):`);
75
+ for (const line of regressions) {
76
+ lines.push(` - ${line}`);
77
+ }
78
+ }
79
+ lines.push("");
80
+ return { ok: regressions.length === 0, regressions, report: lines.join("\n") };
81
+ }
82
+ /** Pretty-print current report JSON for --update-baseline. */
83
+ export function writeBaseline(path, report) {
84
+ writeFileSync(path, `${JSON.stringify(report, null, 2)}\n`, "utf8");
85
+ }
86
+ //# sourceMappingURL=baseline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baseline.js","sourceRoot":"","sources":["../src/baseline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export declare const DEFAULT_GITHUB_SUMMARY = "github-step-summary.md";
2
+ export declare function resolveGithubSummaryPath(flagPath: string | true | undefined, env?: NodeJS.ProcessEnv, cwd?: string): string | undefined;
3
+ export declare function appendGithubSummary(path: string, markdown: string): void;
4
+ //# sourceMappingURL=github-summary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-summary.d.ts","sourceRoot":"","sources":["../src/github-summary.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,sBAAsB,2BAA2B,CAAC;AAE/D,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACnC,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,GAAG,GAAE,MAAsB,GAC1B,MAAM,GAAG,SAAS,CAWpB;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAoBxE"}
@@ -0,0 +1,39 @@
1
+ import { appendFileSync, mkdirSync, writeFileSync } from "node:fs";
2
+ import { dirname, resolve } from "node:path";
3
+ export const DEFAULT_GITHUB_SUMMARY = "github-step-summary.md";
4
+ export function resolveGithubSummaryPath(flagPath, env = process.env, cwd = process.cwd()) {
5
+ if (flagPath === undefined) {
6
+ const fromEnv = env.GITHUB_STEP_SUMMARY;
7
+ return fromEnv && fromEnv.trim() ? fromEnv : undefined;
8
+ }
9
+ if (flagPath === true || flagPath === "") {
10
+ const fromEnv = env.GITHUB_STEP_SUMMARY;
11
+ if (fromEnv && fromEnv.trim())
12
+ return fromEnv;
13
+ return resolve(cwd, DEFAULT_GITHUB_SUMMARY);
14
+ }
15
+ return resolve(cwd, flagPath);
16
+ }
17
+ export function appendGithubSummary(path, markdown) {
18
+ const body = markdown.endsWith("\n") ? markdown : `${markdown}\n`;
19
+ try {
20
+ mkdirSync(dirname(path), { recursive: true });
21
+ }
22
+ catch {
23
+ // ignore
24
+ }
25
+ try {
26
+ appendFileSync(path, body, "utf8");
27
+ }
28
+ catch (error) {
29
+ const code = error && typeof error === "object" && "code" in error
30
+ ? String(error.code)
31
+ : "";
32
+ if (code === "ENOENT") {
33
+ writeFileSync(path, body, "utf8");
34
+ return;
35
+ }
36
+ throw error;
37
+ }
38
+ }
39
+ //# sourceMappingURL=github-summary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-summary.js","sourceRoot":"","sources":["../src/github-summary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE7C,MAAM,CAAC,MAAM,sBAAsB,GAAG,wBAAwB,CAAC;AAE/D,MAAM,UAAU,wBAAwB,CACtC,QAAmC,EACnC,MAAyB,OAAO,CAAC,GAAG,EACpC,MAAc,OAAO,CAAC,GAAG,EAAE;IAE3B,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,GAAG,CAAC,mBAAmB,CAAC;QACxC,OAAO,OAAO,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IACzD,CAAC;IACD,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,EAAE,EAAE,CAAC;QACzC,MAAM,OAAO,GAAG,GAAG,CAAC,mBAAmB,CAAC;QACxC,IAAI,OAAO,IAAI,OAAO,CAAC,IAAI,EAAE;YAAE,OAAO,OAAO,CAAC;QAC9C,OAAO,OAAO,CAAC,GAAG,EAAE,sBAAsB,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAE,QAAgB;IAChE,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,IAAI,CAAC;IAClE,IAAI,CAAC;QACH,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;IACD,IAAI,CAAC;QACH,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IACrC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,GACR,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,IAAI,KAAK;YACnD,CAAC,CAAC,MAAM,CAAE,KAA2B,CAAC,IAAI,CAAC;YAC3C,CAAC,CAAC,EAAE,CAAC;QACT,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;YAClC,OAAO;QACT,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { Report } from "./types.js";
2
+ /** Escape text for safe insertion into HTML text/attr contexts. */
3
+ export declare function escapeHtml(text: string): string;
4
+ /**
5
+ * Self-contained dark HTML report for an eval-smoke run (inline CSS+JS, no CDN).
6
+ */
7
+ export declare function formatHtmlReport(report: Report): string;
8
+ //# sourceMappingURL=html-report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-report.d.ts","sourceRoot":"","sources":["../src/html-report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA2B,MAAM,EAAE,MAAM,YAAY,CAAC;AAElE,mEAAmE;AACnE,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO/C;AA8CD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAgJvD"}
@@ -0,0 +1,196 @@
1
+ /** Escape text for safe insertion into HTML text/attr contexts. */
2
+ export function escapeHtml(text) {
3
+ return text
4
+ .replace(/&/g, "&amp;")
5
+ .replace(/</g, "&lt;")
6
+ .replace(/>/g, "&gt;")
7
+ .replace(/"/g, "&quot;")
8
+ .replace(/'/g, "&#39;");
9
+ }
10
+ function truncate(text, max = 400) {
11
+ const compact = text.replace(/\s+/g, " ").trim();
12
+ if (compact.length <= max)
13
+ return compact;
14
+ return `${compact.slice(0, max - 1)}…`;
15
+ }
16
+ function failedChecks(result) {
17
+ return (result.checks ?? []).filter((check) => !check.ok);
18
+ }
19
+ function statusLabel(result) {
20
+ if (result.skipped)
21
+ return "skip";
22
+ return result.ok ? "pass" : "fail";
23
+ }
24
+ function statusClass(result) {
25
+ if (result.skipped)
26
+ return "skip";
27
+ return result.ok ? "pass" : "fail";
28
+ }
29
+ function failureDetails(result) {
30
+ const parts = [];
31
+ if (result.error)
32
+ parts.push(result.error);
33
+ for (const check of failedChecks(result)) {
34
+ const detail = check.detail ?? "failed";
35
+ parts.push(`${check.name}: ${detail}`);
36
+ if (check.expected != null && !detail.includes("expected:")) {
37
+ parts.push(`expected: ${truncate(check.expected, 200)}`);
38
+ }
39
+ if (check.actual != null && !detail.includes("actual:")) {
40
+ parts.push(`actual: ${truncate(check.actual, 200)}`);
41
+ }
42
+ }
43
+ if (result.output &&
44
+ !result.ok &&
45
+ failedChecks(result).every((c) => c.name !== "equals")) {
46
+ parts.push(`output: ${truncate(result.output)}`);
47
+ }
48
+ if (!parts.length)
49
+ return "";
50
+ return `<details><summary>details</summary><pre>${escapeHtml(parts.join("\n"))}</pre></details>`;
51
+ }
52
+ /**
53
+ * Self-contained dark HTML report for an eval-smoke run (inline CSS+JS, no CDN).
54
+ */
55
+ export function formatHtmlReport(report) {
56
+ const suite = escapeHtml(report.suite ?? "suite");
57
+ const version = escapeHtml(report.version);
58
+ const runner = escapeHtml(report.runner);
59
+ const model = escapeHtml(report.model);
60
+ const okClass = report.ok ? "ok" : "bad";
61
+ const rows = report.cases
62
+ .map((result) => {
63
+ const st = statusClass(result);
64
+ const details = failureDetails(result);
65
+ return `<tr class="${st}">
66
+ <td><span class="badge ${st}">${statusLabel(result)}</span></td>
67
+ <td><code>${escapeHtml(result.id)}</code></td>
68
+ <td class="num">${result.duration_ms}ms</td>
69
+ <td class="detail">${details}</td>
70
+ </tr>`;
71
+ })
72
+ .join("\n");
73
+ return `<!DOCTYPE html>
74
+ <html lang="en">
75
+ <head>
76
+ <meta charset="utf-8"/>
77
+ <meta name="viewport" content="width=device-width, initial-scale=1"/>
78
+ <title>eval-smoke · ${suite}</title>
79
+ <style>
80
+ :root {
81
+ --bg: #0a0a0a;
82
+ --panel: #111111;
83
+ --border: #222222;
84
+ --text: #eeeeee;
85
+ --muted: #888888;
86
+ --pass: #7ee787;
87
+ --fail: #ff7b72;
88
+ --skip: #d2a8ff;
89
+ --font: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
90
+ }
91
+ * { box-sizing: border-box; }
92
+ html, body {
93
+ margin: 0; padding: 0;
94
+ background: var(--bg); color: var(--text);
95
+ font: 13px/1.45 var(--font);
96
+ }
97
+ body { padding: 24px 20px 48px; max-width: 960px; margin: 0 auto; }
98
+ header.top {
99
+ border: 1px solid var(--border); background: var(--panel);
100
+ padding: 16px 18px; margin-bottom: 16px;
101
+ }
102
+ header.top h1 { margin: 0 0 6px; font-size: 15px; font-weight: 600; }
103
+ header.top .meta { color: var(--muted); font-size: 12px; }
104
+ header.top .summary { margin-top: 10px; font-size: 13px; }
105
+ header.top.ok .summary { color: var(--pass); }
106
+ header.top.bad .summary { color: var(--fail); }
107
+ .stats {
108
+ display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
109
+ gap: 8px; margin: 0 0 18px;
110
+ }
111
+ .stat {
112
+ border: 1px solid var(--border); background: var(--panel);
113
+ padding: 10px 12px;
114
+ }
115
+ .stat .label { color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; }
116
+ .stat .value { margin-top: 4px; font-size: 16px; }
117
+ table {
118
+ width: 100%; border-collapse: collapse;
119
+ border: 1px solid var(--border); background: var(--panel);
120
+ }
121
+ th, td {
122
+ text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--border);
123
+ vertical-align: top;
124
+ }
125
+ th { color: var(--muted); font-weight: 500; font-size: 11px; text-transform: uppercase; }
126
+ td.num { text-align: right; color: var(--muted); white-space: nowrap; }
127
+ .badge {
128
+ display: inline-block; min-width: 3.2em; text-align: center;
129
+ padding: 1px 6px; border: 1px solid var(--border);
130
+ }
131
+ .badge.pass { color: var(--pass); border-color: #1e3d28; }
132
+ .badge.fail { color: var(--fail); border-color: #5a1d1d; }
133
+ .badge.skip { color: var(--skip); border-color: #3a2a55; }
134
+ tr.fail { background: #140a0a; }
135
+ details { margin-top: 4px; }
136
+ summary { cursor: pointer; color: var(--muted); user-select: none; }
137
+ pre {
138
+ margin: 6px 0 0; padding: 10px; overflow: auto;
139
+ background: #050505; border: 1px solid var(--border);
140
+ white-space: pre-wrap; word-break: break-word; color: #d8d8d8;
141
+ }
142
+ .toolbar { margin: 0 0 12px; display: flex; gap: 8px; }
143
+ .toolbar button {
144
+ background: var(--panel); color: var(--text); border: 1px solid var(--border);
145
+ padding: 6px 10px; font: inherit; cursor: pointer;
146
+ }
147
+ .toolbar button:hover { border-color: #444; }
148
+ footer {
149
+ margin-top: 28px; color: var(--muted); font-size: 11px;
150
+ border-top: 1px solid var(--border); padding-top: 12px;
151
+ }
152
+ code { font: inherit; }
153
+ @media (max-width: 640px) {
154
+ .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
155
+ }
156
+ </style>
157
+ </head>
158
+ <body>
159
+ <header class="top ${okClass}">
160
+ <h1>eval-smoke ${version}</h1>
161
+ <div class="meta">${runner} · ${model} · ${suite} · ${report.cases.length} cases</div>
162
+ <div class="summary">${report.passed} passed · ${report.failed} failed${report.skipped ? ` · ${report.skipped} skipped` : ""} · ${report.duration_ms}ms</div>
163
+ </header>
164
+ <section class="stats" aria-label="stats">
165
+ <div class="stat"><div class="label">passed</div><div class="value">${report.passed}</div></div>
166
+ <div class="stat"><div class="label">failed</div><div class="value">${report.failed}</div></div>
167
+ <div class="stat"><div class="label">skipped</div><div class="value">${report.skipped}</div></div>
168
+ <div class="stat"><div class="label">duration</div><div class="value">${report.duration_ms}ms</div></div>
169
+ </section>
170
+ <div class="toolbar">
171
+ <button type="button" id="expand-all">Expand failures</button>
172
+ <button type="button" id="collapse-all">Collapse</button>
173
+ </div>
174
+ <table>
175
+ <thead><tr><th>Status</th><th>Case</th><th>Duration</th><th>Details</th></tr></thead>
176
+ <tbody>
177
+ ${rows || `<tr><td colspan="4" style="color:var(--muted)">no cases</td></tr>`}
178
+ </tbody>
179
+ </table>
180
+ <footer>Generated by eval-smoke · self-contained · works offline (file://)</footer>
181
+ <script>
182
+ (function () {
183
+ var expand = document.getElementById("expand-all");
184
+ var collapse = document.getElementById("collapse-all");
185
+ function setAll(open) {
186
+ document.querySelectorAll("details").forEach(function (d) { d.open = open; });
187
+ }
188
+ if (expand) expand.addEventListener("click", function () { setAll(true); });
189
+ if (collapse) collapse.addEventListener("click", function () { setAll(false); });
190
+ })();
191
+ </script>
192
+ </body>
193
+ </html>
194
+ `;
195
+ }
196
+ //# sourceMappingURL=html-report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-report.js","sourceRoot":"","sources":["../src/html-report.ts"],"names":[],"mappings":"AAEA,mEAAmE;AACnE,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,IAAI;SACR,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY,EAAE,GAAG,GAAG,GAAG;IACvC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACjD,IAAI,OAAO,CAAC,MAAM,IAAI,GAAG;QAAE,OAAO,OAAO,CAAC;IAC1C,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC;AACzC,CAAC;AAED,SAAS,YAAY,CAAC,MAAkB;IACtC,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AAC5D,CAAC;AAED,SAAS,WAAW,CAAC,MAAkB;IACrC,IAAI,MAAM,CAAC,OAAO;QAAE,OAAO,MAAM,CAAC;IAClC,OAAO,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;AACrC,CAAC;AAED,SAAS,WAAW,CAAC,MAAkB;IACrC,IAAI,MAAM,CAAC,OAAO;QAAE,OAAO,MAAM,CAAC;IAClC,OAAO,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;AACrC,CAAC;AAED,SAAS,cAAc,CAAC,MAAkB;IACxC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,MAAM,CAAC,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC3C,KAAK,MAAM,KAAK,IAAI,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC;QACzC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,QAAQ,CAAC;QACxC,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC,CAAC;QACvC,IAAI,KAAK,CAAC,QAAQ,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YAC5D,KAAK,CAAC,IAAI,CAAC,aAAa,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QAC3D,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YACxD,KAAK,CAAC,IAAI,CAAC,WAAW,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IACD,IACE,MAAM,CAAC,MAAM;QACb,CAAC,MAAM,CAAC,EAAE;QACV,YAAY,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,EACtD,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,WAAW,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACnD,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IAC7B,OAAO,2CAA2C,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,kBAAkB,CAAC;AACnG,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,IAAI,OAAO,CAAC,CAAC;IAClD,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvC,MAAM,OAAO,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;IAEzC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK;SACtB,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;QACd,MAAM,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;QAC/B,MAAM,OAAO,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;QACvC,OAAO,cAAc,EAAE;2BACF,EAAE,KAAK,WAAW,CAAC,MAAM,CAAC;cACvC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;oBACf,MAAM,CAAC,WAAW;uBACf,OAAO;MACxB,CAAC;IACH,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO;;;;;sBAKa,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAiFN,OAAO;mBACT,OAAO;sBACJ,MAAM,MAAM,KAAK,MAAM,KAAK,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM;yBAClD,MAAM,CAAC,MAAM,aAAa,MAAM,CAAC,MAAM,UAC5D,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC,EACpD,MAAM,MAAM,CAAC,WAAW;;;wEAG8C,MAAM,CAAC,MAAM;wEACb,MAAM,CAAC,MAAM;yEACZ,MAAM,CAAC,OAAO;0EACb,MAAM,CAAC,WAAW;;;;;;;;;EAS1F,IAAI,IAAI,mEAAmE;;;;;;;;;;;;;;;;;CAiB5E,CAAC;AACF,CAAC"}
package/dist/index.d.ts CHANGED
@@ -4,9 +4,14 @@ export { formatJUnit } from "./junit.js";
4
4
  export { evaluateCase, evaluateExpect, equalsDiff, normalizeEquals } from "./matchers.js";
5
5
  export { createComplete, mockComplete, resolveProvider } from "./providers.js";
6
6
  export { formatReport } from "./report.js";
7
+ export { formatHtmlReport, escapeHtml } from "./html-report.js";
7
8
  export { runSuite } from "./run.js";
8
9
  export { estimateTokens } from "./tokens.js";
9
10
  export { VERSION } from "./version.js";
10
11
  export type { Case, CaseResult, CheckName, CheckResult, CompleteFn, CompleteRequest, Expect, JsonExpect, ProviderName, Report, RunOptions, Suite, } from "./types.js";
11
12
  export { UsageError } from "./types.js";
13
+ export { checkBaseline, loadBaseline, writeBaseline } from "./baseline.js";
14
+ export type { BaselineCheck, BaselineCase, BaselineFile } from "./baseline.js";
15
+ export { appendGithubSummary, resolveGithubSummaryPath, DEFAULT_GITHUB_SUMMARY } from "./github-summary.js";
16
+ export { formatGithubSummary } from "./summary-md.js";
12
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,YAAY,EACV,IAAI,EACJ,UAAU,EACV,SAAS,EACT,WAAW,EACX,UAAU,EACV,eAAe,EACf,MAAM,EACN,UAAU,EACV,YAAY,EACZ,MAAM,EACN,UAAU,EACV,KAAK,GACN,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,YAAY,EACV,IAAI,EACJ,UAAU,EACV,SAAS,EACT,WAAW,EACX,UAAU,EACV,eAAe,EACf,MAAM,EACN,UAAU,EACV,YAAY,EACZ,MAAM,EACN,UAAU,EACV,KAAK,GACN,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC3E,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC/E,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC5G,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -4,8 +4,12 @@ export { formatJUnit } from "./junit.js";
4
4
  export { evaluateCase, evaluateExpect, equalsDiff, normalizeEquals } from "./matchers.js";
5
5
  export { createComplete, mockComplete, resolveProvider } from "./providers.js";
6
6
  export { formatReport } from "./report.js";
7
+ export { formatHtmlReport, escapeHtml } from "./html-report.js";
7
8
  export { runSuite } from "./run.js";
8
9
  export { estimateTokens } from "./tokens.js";
9
10
  export { VERSION } from "./version.js";
10
11
  export { UsageError } from "./types.js";
12
+ export { checkBaseline, loadBaseline, writeBaseline } from "./baseline.js";
13
+ export { appendGithubSummary, resolveGithubSummaryPath, DEFAULT_GITHUB_SUMMARY } from "./github-summary.js";
14
+ export { formatGithubSummary } from "./summary-md.js";
11
15
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAevC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAevC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE3E,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC5G,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/run-cli.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type Suite } from "./types.js";
2
- export declare const HELP = "eval-smoke \u2014 one-minute smoke evals for prompts and models\n\nUsage:\n eval-smoke <suite.yaml|suite.json> [options]\n eval-smoke init [dir] [--force]\n\nOptions:\n --mock Offline runner with deterministic fake answers\n --json Machine-readable JSON report\n --junit <path> Write JUnit XML report to path (still prints human report unless --json)\n --only <pattern> Run cases whose id contains pattern, or matches /regex/\n --fail-fast Stop after the first failing case\n --concurrency <n> Run up to N cases in parallel (default: 1; ignored with --fail-fast)\n --timeout <ms> Per-case timeout (default: 30000)\n --model <name> Override suite model\n --provider <name> openai | anthropic | mock\n --force Overwrite existing suite.yaml when using init\n --no-color Disable ANSI colors (also respects NO_COLOR)\n -h, --help Show this help\n -v, --version Show version\n\nExit codes:\n 0 all cases passed\n 1 one or more cases failed\n 2 usage or configuration error\n\nExamples:\n eval-smoke init\n eval-smoke init ./prompts --force\n eval-smoke examples/suite.yaml --mock\n eval-smoke examples/suite.yaml --mock --only greeting\n eval-smoke examples/suite.yaml --mock --only '/^capital/'\n eval-smoke examples/suite.yaml --mock --junit junit.xml\n eval-smoke examples/suite.yaml --json --fail-fast\n eval-smoke examples/suite.yaml --mock --concurrency 4\n OPENAI_API_KEY=sk-... eval-smoke examples/suite.yaml\n";
2
+ export declare const HELP = "eval-smoke \u2014 one-minute smoke evals for prompts and models\n\nUsage:\n eval-smoke <suite.yaml|suite.json> [options]\n eval-smoke init [dir] [--force]\n\nOptions:\n --mock Offline runner with deterministic fake answers\n --json Machine-readable JSON report\n --junit <path> Write JUnit XML report to path (still prints human report unless --json)\n --html <path> Write self-contained HTML report to path (offline, no CDN)\n --only <pattern> Run cases whose id contains pattern, or matches /regex/\n --fail-fast Stop after the first failing case\n --concurrency <n> Run up to N cases in parallel (default: 1; ignored with --fail-fast)\n --timeout <ms> Per-case timeout (default: 30000)\n --model <name> Override suite model\n --provider <name> openai | anthropic | mock\n --baseline <path> Fail if any baseline-passing case now fails or is missing\n --update-baseline <path> Write current report JSON to path (pretty)\n --github-summary [path] Append Markdown to GITHUB_STEP_SUMMARY or path\n --force Overwrite existing suite.yaml when using init\n --no-color Disable ANSI colors (also respects NO_COLOR)\n -h, --help Show this help\n -v, --version Show version\n\nExit codes:\n 0 all cases passed (and baseline clean when --baseline is set)\n 1 one or more cases failed, or baseline regression\n 2 usage or configuration error\n\nExamples:\n eval-smoke init\n eval-smoke init ./prompts --force\n eval-smoke examples/suite.yaml --mock\n eval-smoke examples/suite.yaml --mock --only greeting\n eval-smoke examples/suite.yaml --mock --only '/^capital/'\n eval-smoke examples/suite.yaml --mock --junit junit.xml\n eval-smoke examples/suite.yaml --mock --html report.html\n eval-smoke examples/suite.yaml --json --fail-fast\n eval-smoke examples/suite.yaml --mock --concurrency 4\n eval-smoke examples/suite.yaml --mock --update-baseline baseline.json\n eval-smoke examples/suite.yaml --mock --baseline baseline.json\n eval-smoke examples/suite.yaml --mock --github-summary\n OPENAI_API_KEY=sk-... eval-smoke examples/suite.yaml\n";
3
3
  export interface CliIo {
4
4
  stdout: {
5
5
  write(text: string): void;
@@ -14,5 +14,11 @@ export interface CliIo {
14
14
  /** Match case ids by substring, or by /regex/ flags (same style as matchers). */
15
15
  export declare function matchOnly(id: string, pattern: string): boolean;
16
16
  export declare function filterSuiteByOnly(suite: Suite, pattern: string): Suite;
17
+ /**
18
+ * Rewrite bare `--github-summary` into `--github-summary=__auto__` so node:util
19
+ * parseArgs (string option) accepts it. `--github-summary=path` and
20
+ * `--github-summary path` stay as-is.
21
+ */
22
+ export declare function normalizeGithubSummaryArgv(argv: string[]): string[];
17
23
  export declare function runCli(argv: string[], io?: CliIo): Promise<number>;
18
24
  //# sourceMappingURL=run-cli.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"run-cli.d.ts","sourceRoot":"","sources":["../src/run-cli.ts"],"names":[],"mappings":"AASA,OAAO,EAAiC,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAGvE,eAAO,MAAM,IAAI,mgDAoChB,CAAC;AAEF,MAAM,WAAW,KAAK;IACpB,MAAM,EAAE;QAAE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACtC,MAAM,EAAE;QAAE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACtC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,OAAO,CAAC;CAChB;AAkCD,iFAAiF;AACjF,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAQ9D;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,GAAG,KAAK,CAMtE;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAE,KAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CA6FrF"}
1
+ {"version":3,"file":"run-cli.d.ts","sourceRoot":"","sources":["../src/run-cli.ts"],"names":[],"mappings":"AAaA,OAAO,EAAiC,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAMvE,eAAO,MAAM,IAAI,+nEA4ChB,CAAC;AAEF,MAAM,WAAW,KAAK;IACpB,MAAM,EAAE;QAAE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACtC,MAAM,EAAE;QAAE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACtC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,OAAO,CAAC;CAChB;AAkCD,iFAAiF;AACjF,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAQ9D;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,GAAG,KAAK,CAMtE;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAiBnE;AAQD,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAE,KAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAsIrF"}
package/dist/run-cli.js CHANGED
@@ -1,14 +1,20 @@
1
1
  import { writeFileSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
3
  import { parseArgs } from "node:util";
4
+ import { checkBaseline, loadBaseline, writeBaseline } from "./baseline.js";
5
+ import { appendGithubSummary, resolveGithubSummaryPath } from "./github-summary.js";
4
6
  import { initSuite } from "./init.js";
5
7
  import { formatJUnit } from "./junit.js";
6
8
  import { loadSuite } from "./load-suite.js";
7
9
  import { compileRegex } from "./matchers.js";
10
+ import { formatHtmlReport } from "./html-report.js";
8
11
  import { formatReport } from "./report.js";
9
12
  import { DEFAULT_TIMEOUT_MS, runSuite } from "./run.js";
13
+ import { formatGithubSummary } from "./summary-md.js";
10
14
  import { UsageError } from "./types.js";
11
15
  import { VERSION } from "./version.js";
16
+ /** Sentinel so node:util accepts a bare --github-summary flag. */
17
+ const GITHUB_SUMMARY_AUTO = "__auto__";
12
18
  export const HELP = `eval-smoke — one-minute smoke evals for prompts and models
13
19
 
14
20
  Usage:
@@ -19,20 +25,24 @@ Options:
19
25
  --mock Offline runner with deterministic fake answers
20
26
  --json Machine-readable JSON report
21
27
  --junit <path> Write JUnit XML report to path (still prints human report unless --json)
28
+ --html <path> Write self-contained HTML report to path (offline, no CDN)
22
29
  --only <pattern> Run cases whose id contains pattern, or matches /regex/
23
30
  --fail-fast Stop after the first failing case
24
31
  --concurrency <n> Run up to N cases in parallel (default: 1; ignored with --fail-fast)
25
32
  --timeout <ms> Per-case timeout (default: ${DEFAULT_TIMEOUT_MS})
26
33
  --model <name> Override suite model
27
34
  --provider <name> openai | anthropic | mock
35
+ --baseline <path> Fail if any baseline-passing case now fails or is missing
36
+ --update-baseline <path> Write current report JSON to path (pretty)
37
+ --github-summary [path] Append Markdown to GITHUB_STEP_SUMMARY or path
28
38
  --force Overwrite existing suite.yaml when using init
29
39
  --no-color Disable ANSI colors (also respects NO_COLOR)
30
40
  -h, --help Show this help
31
41
  -v, --version Show version
32
42
 
33
43
  Exit codes:
34
- 0 all cases passed
35
- 1 one or more cases failed
44
+ 0 all cases passed (and baseline clean when --baseline is set)
45
+ 1 one or more cases failed, or baseline regression
36
46
  2 usage or configuration error
37
47
 
38
48
  Examples:
@@ -42,8 +52,12 @@ Examples:
42
52
  eval-smoke examples/suite.yaml --mock --only greeting
43
53
  eval-smoke examples/suite.yaml --mock --only '/^capital/'
44
54
  eval-smoke examples/suite.yaml --mock --junit junit.xml
55
+ eval-smoke examples/suite.yaml --mock --html report.html
45
56
  eval-smoke examples/suite.yaml --json --fail-fast
46
57
  eval-smoke examples/suite.yaml --mock --concurrency 4
58
+ eval-smoke examples/suite.yaml --mock --update-baseline baseline.json
59
+ eval-smoke examples/suite.yaml --mock --baseline baseline.json
60
+ eval-smoke examples/suite.yaml --mock --github-summary
47
61
  OPENAI_API_KEY=sk-... eval-smoke examples/suite.yaml
48
62
  `;
49
63
  const defaultIo = () => ({
@@ -95,23 +109,58 @@ export function filterSuiteByOnly(suite, pattern) {
95
109
  }
96
110
  return { ...suite, cases };
97
111
  }
112
+ /**
113
+ * Rewrite bare `--github-summary` into `--github-summary=__auto__` so node:util
114
+ * parseArgs (string option) accepts it. `--github-summary=path` and
115
+ * `--github-summary path` stay as-is.
116
+ */
117
+ export function normalizeGithubSummaryArgv(argv) {
118
+ const out = [];
119
+ for (let i = 0; i < argv.length; i += 1) {
120
+ const token = argv[i];
121
+ if (token === "--github-summary") {
122
+ const next = argv[i + 1];
123
+ if (!next || next.startsWith("-")) {
124
+ out.push(`--github-summary=${GITHUB_SUMMARY_AUTO}`);
125
+ }
126
+ else {
127
+ out.push("--github-summary", next);
128
+ i += 1;
129
+ }
130
+ continue;
131
+ }
132
+ out.push(token);
133
+ }
134
+ return out;
135
+ }
136
+ function githubSummaryFlag(raw) {
137
+ if (raw == null)
138
+ return undefined;
139
+ if (raw === GITHUB_SUMMARY_AUTO)
140
+ return true;
141
+ return raw;
142
+ }
98
143
  export async function runCli(argv, io = defaultIo()) {
99
144
  let parsed;
100
145
  try {
101
146
  parsed = parseArgs({
102
- args: argv,
147
+ args: normalizeGithubSummaryArgv(argv),
103
148
  allowPositionals: true,
104
149
  strict: true,
105
150
  options: {
106
151
  mock: { type: "boolean", default: false },
107
152
  json: { type: "boolean", default: false },
108
153
  junit: { type: "string" },
154
+ html: { type: "string" },
109
155
  only: { type: "string" },
110
156
  "fail-fast": { type: "boolean", default: false },
111
157
  concurrency: { type: "string" },
112
158
  timeout: { type: "string" },
113
159
  model: { type: "string" },
114
160
  provider: { type: "string" },
161
+ baseline: { type: "string" },
162
+ "update-baseline": { type: "string" },
163
+ "github-summary": { type: "string" },
115
164
  force: { type: "boolean", default: false },
116
165
  "no-color": { type: "boolean", default: false },
117
166
  help: { type: "boolean", short: "h", default: false },
@@ -158,6 +207,10 @@ export async function runCli(argv, io = defaultIo()) {
158
207
  const json = asBool(values.json);
159
208
  const only = asString(values.only);
160
209
  const junitPath = asString(values.junit);
210
+ const htmlPath = asString(values.html);
211
+ const baselinePath = asString(values.baseline);
212
+ const updateBaselinePath = asString(values["update-baseline"]);
213
+ const githubSummary = githubSummaryFlag(asString(values["github-summary"]));
161
214
  let suite = loadSuite(suitePath, io.cwd);
162
215
  if (only != null) {
163
216
  suite = filterSuiteByOnly(suite, only);
@@ -175,9 +228,40 @@ export async function runCli(argv, io = defaultIo()) {
175
228
  const out = resolve(io.cwd, junitPath);
176
229
  writeFileSync(out, formatJUnit(report), "utf8");
177
230
  }
231
+ if (htmlPath != null) {
232
+ const out = resolve(io.cwd, htmlPath);
233
+ writeFileSync(out, formatHtmlReport(report), "utf8");
234
+ }
235
+ if (updateBaselinePath != null) {
236
+ const out = resolve(io.cwd, updateBaselinePath);
237
+ writeBaseline(out, report);
238
+ io.stderr.write(`Wrote baseline ${out}\n`);
239
+ }
240
+ let baselineFailed = false;
241
+ if (baselinePath != null) {
242
+ const baseline = loadBaseline(resolve(io.cwd, baselinePath));
243
+ const check = checkBaseline(report, baseline);
244
+ io.stderr.write(check.report);
245
+ if (!check.ok)
246
+ baselineFailed = true;
247
+ }
248
+ const outSummary = githubSummary === undefined
249
+ ? resolveGithubSummaryPath(undefined, io.env, io.cwd)
250
+ : resolveGithubSummaryPath(githubSummary, io.env, io.cwd);
251
+ if (outSummary) {
252
+ try {
253
+ appendGithubSummary(outSummary, formatGithubSummary(report));
254
+ }
255
+ catch (error) {
256
+ const message = error instanceof Error ? error.message : String(error);
257
+ io.stderr.write(`could not write github summary: ${message}\n`);
258
+ }
259
+ }
178
260
  const noColor = asBool(values["no-color"]) || Boolean(io.env.NO_COLOR);
179
261
  const color = io.isTTY && !json && !noColor;
180
262
  io.stdout.write(formatReport(report, { json, color }));
263
+ if (baselineFailed)
264
+ return 1;
181
265
  return report.ok ? 0 : 1;
182
266
  }
183
267
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"run-cli.js","sourceRoot":"","sources":["../src/run-cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"run-cli.js","sourceRoot":"","sources":["../src/run-cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { Report } from "./types.js";
2
+ /** Concise pass/fail Markdown table for GitHub Actions step summary. */
3
+ export declare function formatGithubSummary(report: Report): string;
4
+ //# sourceMappingURL=summary-md.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summary-md.d.ts","sourceRoot":"","sources":["../src/summary-md.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC,wEAAwE;AACxE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAoB1D"}
@@ -0,0 +1,22 @@
1
+ /** Concise pass/fail Markdown table for GitHub Actions step summary. */
2
+ export function formatGithubSummary(report) {
3
+ const lines = [];
4
+ lines.push(`### eval-smoke · ${escape(report.suite ?? "suite")}`);
5
+ lines.push("");
6
+ lines.push(`**${report.passed} passed · ${report.failed} failed${report.skipped ? ` · ${report.skipped} skipped` : ""} · ${report.duration_ms}ms** · \`${report.runner}\` / \`${escape(report.model)}\``);
7
+ lines.push("");
8
+ lines.push("| Case | Result | ms |");
9
+ lines.push("| --- | --- | ---: |");
10
+ for (const c of report.cases) {
11
+ const status = c.skipped ? "skip" : c.ok ? "✅ pass" : "❌ fail";
12
+ lines.push(`| \`${escape(c.id)}\` | ${status} | ${c.duration_ms} |`);
13
+ }
14
+ lines.push("");
15
+ lines.push(report.ok ? "**Suite: PASS**" : "**Suite: FAIL**");
16
+ lines.push("");
17
+ return lines.join("\n");
18
+ }
19
+ function escape(text) {
20
+ return text.replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
21
+ }
22
+ //# sourceMappingURL=summary-md.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summary-md.js","sourceRoot":"","sources":["../src/summary-md.ts"],"names":[],"mappings":"AAEA,wEAAwE;AACxE,MAAM,UAAU,mBAAmB,CAAC,MAAc;IAChD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,oBAAoB,MAAM,CAAC,MAAM,CAAC,KAAK,IAAI,OAAO,CAAC,EAAE,CAAC,CAAC;IAClE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CACR,KAAK,MAAM,CAAC,MAAM,aAAa,MAAM,CAAC,MAAM,UAC1C,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC,EACpD,MAAM,MAAM,CAAC,WAAW,YAAY,MAAM,CAAC,MAAM,UAAU,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CACpF,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IACrC,KAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACnC,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC/D,KAAK,CAAC,IAAI,CAAC,OAAO,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,MAAM,MAAM,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC;IACvE,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC;IAC9D,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,MAAM,CAAC,IAAY;IAC1B,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;AAC3D,CAAC"}
@@ -0,0 +1,41 @@
1
+ # Copy-paste: run eval-smoke in GitHub Actions (mock offline + JUnit + step summary).
2
+ # No clone of this repo required — npx pulls @webywz/eval-smoke from npm.
3
+ #
4
+ # Human report → stdout (job log). JUnit → file. Markdown → $GITHUB_STEP_SUMMARY.
5
+
6
+ name: Smoke eval with eval-smoke
7
+
8
+ on:
9
+ workflow_dispatch:
10
+ # Or: push / pull_request when you have a suite.yaml in the repo.
11
+
12
+ jobs:
13
+ eval:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - uses: actions/setup-node@v4
19
+ with:
20
+ node-version: 22
21
+
22
+ # Offline smoke against the published example suite (no API key):
23
+ - name: Mock smoke + JUnit + step summary
24
+ run: |
25
+ npx --yes @webywz/eval-smoke --help >/dev/null
26
+ # Prefer a suite checked into YOUR repo; this uses the package example via init:
27
+ npx --yes @webywz/eval-smoke init ./eval-smoke-ci --force
28
+ npx --yes @webywz/eval-smoke ./eval-smoke-ci/suite.yaml \
29
+ --mock \
30
+ --junit junit-eval-smoke.xml \
31
+ --github-summary \
32
+ --update-baseline eval-smoke-baseline.json
33
+
34
+ - name: Upload JUnit
35
+ if: always()
36
+ uses: actions/upload-artifact@v4
37
+ with:
38
+ name: eval-smoke-junit
39
+ path: |
40
+ junit-eval-smoke.xml
41
+ eval-smoke-baseline.json
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webywz/eval-smoke",
3
- "version": "0.3.1",
3
+ "version": "0.5.0",
4
4
  "description": "One-minute smoke evals for prompts and models — equals/json matchers, diffs, concurrency, JUnit, init.",
5
5
  "type": "module",
6
6
  "bin": {