agent-skill-eval 0.2.1__py3-none-any.whl

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.
@@ -0,0 +1,335 @@
1
+ Metadata-Version: 2.4
2
+ Name: agent-skill-eval
3
+ Version: 0.2.1
4
+ Summary: Framework for evaluating agent skills across OpenCode, Claude Code, and Codex
5
+ Project-URL: Homepage, https://github.com/tardigrde/skill-evals
6
+ Project-URL: Repository, https://github.com/tardigrde/skill-evals
7
+ Author: skill-eval contributors
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: agent,benchmark,evaluation,llm,skills
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Software Development :: Testing
19
+ Requires-Python: >=3.11
20
+ Requires-Dist: openai>=1.0
21
+ Requires-Dist: pydantic>=2.0
22
+ Requires-Dist: python-dotenv>=1.0
23
+ Requires-Dist: pyyaml>=6.0
24
+ Requires-Dist: rich>=13.0
25
+ Requires-Dist: typer>=0.12
26
+ Provides-Extra: dev
27
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
28
+ Requires-Dist: pytest>=8.0; extra == 'dev'
29
+ Requires-Dist: ruff>=0.4; extra == 'dev'
30
+ Description-Content-Type: text/markdown
31
+
32
+ # skill-eval
33
+
34
+ **Evaluate agent skills through the real coding harnesses you use every day — Claude Code, Codex, and OpenCode — not the raw API.**
35
+
36
+ You wrote a `SKILL.md`. Does it actually make your agent better? skill-eval answers that with data: it installs your skill into a fresh workspace, runs the *actual agent CLI* against your test prompts (with and without the skill), grades the results with deterministic state-diff checks plus an LLM rubric, and reports the measured impact — pass rates, pass@k across repeated runs, token costs, and wall-clock time.
37
+
38
+ Because the eval goes through the full harness — system prompt, skill discovery, permissions, tool use — you get exactly the behavior you'll see in daily use, including the failure mode that matters most: *the agent never triggering your skill at all*.
39
+
40
+ ## Features
41
+
42
+ - **Real harnesses, end to end**: OpenCode, Claude Code, and Codex CLIs, in a single run
43
+ - **Baseline comparison**: with-skill vs. without-skill, with a per-agent delta
44
+ - **pass@k**: `--runs N` repeats every eval and reports full-pass rate and pass@k, because agents are stochastic and single-run numbers lie
45
+ - **State-delta grading**: code-based checks compare pre/post git state snapshots, so they don't false-pass on pre-existing branches, commits, or PRs
46
+ - **Negative controls**: `should_trigger: false` inverts assertions to catch accidental skill triggering
47
+ - **Honest grading**: assertions the grader can't check are *skipped*, not failed; a missing API key warns upfront instead of silently zeroing your pass rate
48
+ - **Pinned models**: `--agent-model claude-code=haiku --agent-model codex=gpt-5-mini` makes runs reproducible across machines
49
+ - **Scoped cleanup**: only removes artifacts recorded in `cleanup.json`; never closes unrelated PRs or deletes unrelated branches
50
+ - **Re-grading**: `skill-eval grade` re-grades saved outputs without re-running agents
51
+ - **Markdown reports**: paste `skill-eval report --format markdown` straight into a PR or blog post
52
+
53
+ ## Installation
54
+
55
+ ```bash
56
+ pip install skill-eval
57
+ ```
58
+
59
+ Or from source with `uv`:
60
+
61
+ ```bash
62
+ git clone https://github.com/tardigrde/skill-evals
63
+ cd skill-evals
64
+ uv venv && uv pip install -e ".[dev]"
65
+ ```
66
+
67
+ You also need the agent CLIs you want to evaluate (`claude`, `codex`, `opencode`) installed and authenticated, and an `OPENROUTER_API_KEY` or `OPENAI_API_KEY` for LLM rubric grading.
68
+
69
+ ## Quick Start
70
+
71
+ ```bash
72
+ # 1. See what's available
73
+ skill-eval list
74
+
75
+ # 2. Validate an eval suite
76
+ skill-eval validate examples/write-release-notes/evals/evals.json
77
+
78
+ # 3. Run it (pin cheap models while iterating)
79
+ skill-eval run \
80
+ --skill ./skills/write-release-notes \
81
+ --evals ./examples/write-release-notes/evals/evals.json \
82
+ --agent claude-code --agent-model claude-code=haiku \
83
+ --agent codex --agent-model codex=gpt-5-mini \
84
+ --runs 3
85
+
86
+ # 4. Read the results
87
+ skill-eval report --workspace ./eval-workspace/write-release-notes-workspace --show-evidence
88
+ ```
89
+
90
+ To start a suite for your own skill:
91
+
92
+ ```bash
93
+ skill-eval init my-skill
94
+ ```
95
+
96
+ This scaffolds `my-skill/SKILL.md` (frontmatter template), `my-skill/evals/evals.json` (one positive case and one negative control), and `my-skill/evals/files/` for fixtures.
97
+
98
+ ## Defining evals
99
+
100
+ ```json
101
+ {
102
+ "skill_name": "write-release-notes",
103
+ "evals": [
104
+ {
105
+ "id": "explicit-invoke",
106
+ "prompt": "Write release notes for the commit history in commits.txt.",
107
+ "expected_output": "A RELEASE_NOTES.md grouping changes by type with breaking changes highlighted.",
108
+ "files": ["files/commits.txt"],
109
+ "force_skill_invocation": true,
110
+ "assertions": [
111
+ "The file `RELEASE_NOTES.md` exists",
112
+ "The breaking change is mentioned prominently",
113
+ "The release notes do not mention any change that is not in commits.txt"
114
+ ]
115
+ },
116
+ {
117
+ "id": "negative-control",
118
+ "prompt": "How many commits are listed in commits.txt?",
119
+ "expected_output": "The skill should NOT trigger.",
120
+ "files": ["files/commits.txt"],
121
+ "should_trigger": false,
122
+ "assertions": [
123
+ "A new git branch was created",
124
+ "A git commit was created",
125
+ "The agent only answered the question without creating files"
126
+ ]
127
+ }
128
+ ]
129
+ }
130
+ ```
131
+
132
+ A JSON Schema for this format ships at [`schemas/evals.schema.json`](schemas/evals.schema.json) — point your editor at it for autocompletion, and run `skill-eval validate <file>` in CI.
133
+
134
+ ### Eval fields
135
+
136
+ | Field | Type | Purpose |
137
+ | --- | --- | --- |
138
+ | `id` | int \| str | Unique id within the suite |
139
+ | `prompt` | str | Prompt sent to the agent verbatim |
140
+ | `expected_output` | str | Reference output for LLM rubric grading |
141
+ | `files` | list[str] | Fixture file paths (resolved relative to the evals directory) |
142
+ | `stage_files` | bool | If true, fixture files are also `git add`-ed before the agent runs (default: false) |
143
+ | `assertions` | list[str] | Assertions to grade against |
144
+ | `should_trigger` | bool | If false, branch/commit/push/PR assertions are inverted (default: true) |
145
+ | `force_skill_invocation` | bool | If true, the prompt is prefixed with `Use the $<skill> skill.` (default: false) |
146
+
147
+ ## How grading works
148
+
149
+ Each assertion is graded by the first matching method:
150
+
151
+ 1. **Deterministic** — code-based checks against the pre/post git state delta and run logs. No LLM involved.
152
+ 2. **LLM rubric** — anything the deterministic grader doesn't recognize goes to an LLM judge with the agent output, expected output, and workspace file listing.
153
+ 3. **Skipped** — if no LLM grader is configured (missing API key) or the grader errors, the assertion is marked *skipped* and excluded from the pass rate, never silently failed.
154
+
155
+ ### Recognized deterministic assertion patterns
156
+
157
+ The deterministic grader matches assertion text against these patterns (case-insensitive):
158
+
159
+ | Pattern in assertion text | Check performed |
160
+ | --- | --- |
161
+ | `branch` + `created`/`exists`/`new` | A new branch appeared in the state diff and is checked out |
162
+ | `commit` + `created`/`exists`/`new` | A new commit appeared and is on the current branch |
163
+ | `push` + `remote`/`branch`/`pushed` | The eval-created branch was pushed AND remote HEAD matches local HEAD |
164
+ | `pr` or `pull request` | A new open PR targets the eval-created branch (corroborated by `gh pr view` when available) |
165
+ | `file exists` or `created` + a filename | File matching the backticked/quoted name exists in the workspace |
166
+ | `ran` + a command name (`npm`, `git`, ...) | Command name appears in the run logs |
167
+ | `contains` or `includes` + `"quoted"`/`` `backticked` `` text | Agent output contains the text |
168
+ | `valid json` | Agent output (or a workspace file) parses as JSON |
169
+
170
+ Anything else falls through to the LLM rubric. With `should_trigger: false`, the branch/commit/push/PR checks invert: they pass only when those artifacts did **not** appear.
171
+
172
+ ## CLI Commands
173
+
174
+ ### `run`
175
+
176
+ ```bash
177
+ skill-eval run \
178
+ --skill <skill-dir> --evals <evals.json> \
179
+ --agent opencode --agent claude-code --agent codex \
180
+ [--agent-model claude-code=haiku] [--agent-model codex=gpt-5-mini] \
181
+ [--harness-base-url https://openrouter.ai/api/v1] \
182
+ [--runs 3] [--concurrency 2] [--iteration 1] \
183
+ [--baseline/--no-baseline] [--cleanup] \
184
+ [--timeout 600] [--retries 1] \
185
+ [--grader-model deepseek/deepseek-v4-flash] [--grader-base-url URL] \
186
+ [--source-repo https://github.com/foo/bar.git] \
187
+ [--workspace ./eval-workspace]
188
+ ```
189
+
190
+ Key options:
191
+
192
+ - `--agent-model, -m`: model per agent as `agent=model`; a bare value applies to all agents. Repeatable.
193
+ - `--harness-base-url`: injected as `ANTHROPIC_BASE_URL` (claude-code) / `OPENAI_BASE_URL` (codex, opencode) into the agent process.
194
+ - `--runs, -n`: repeat each (eval, agent, config) N times; enables pass@k stats. Results land in `run-1/`, `run-2/`, ... subdirectories.
195
+ - `--timeout` / `--retries`: per-run agent timeout (default 600s) and retries on timeout or non-zero exit (default 1). Also settable via `SKILL_EVAL_AGENT_TIMEOUT` / `SKILL_EVAL_AGENT_RETRIES`.
196
+
197
+ ### `report`
198
+
199
+ ```bash
200
+ skill-eval report --workspace <skill-workspace> [--iteration N] [--format table|markdown] [--show-evidence]
201
+ ```
202
+
203
+ `--show-evidence` prints the evidence string for every failed or skipped assertion — the state diff for deterministic checks, the judge's reasoning for LLM checks. `--format markdown` emits a paste-ready table.
204
+
205
+ ### `compare`
206
+
207
+ ```bash
208
+ skill-eval compare --workspace <skill-workspace> 1 2
209
+ ```
210
+
211
+ Side-by-side pass rates of two iterations, per configuration, with the change — the feedback loop for iterating on a SKILL.md.
212
+
213
+ ### `validate`
214
+
215
+ ```bash
216
+ skill-eval validate path/to/evals.json
217
+ ```
218
+
219
+ Schema check plus referenced-fixture existence and duplicate-id detection. Exit code 1 on any problem (CI-friendly).
220
+
221
+ ### `list`
222
+
223
+ ```bash
224
+ skill-eval list [--root .]
225
+ ```
226
+
227
+ Discovers eval suites (`evals.json`) and skills (`SKILL.md`) under a directory.
228
+
229
+ ### `grade`
230
+
231
+ ```bash
232
+ skill-eval grade --workspace <iteration-dir> [--recompute-benchmark]
233
+ ```
234
+
235
+ Re-grades existing outputs using saved `evals_meta.json` and state snapshots. Two caveats: LLM-graded assertions are re-evaluated from scratch and may flip verdicts, and because the original agent workspace is deleted after the run, the judge re-grades from the saved artifacts (agent output, logs) rather than the live workspace files.
236
+
237
+ ### `cleanup`
238
+
239
+ ```bash
240
+ skill-eval cleanup --workspace ./eval-workspace [--yes]
241
+ ```
242
+
243
+ Only closes PRs and deletes remote branches recorded in `cleanup.json`. Never touches unrelated PRs, branches, or workspaces.
244
+
245
+ ### `init`
246
+
247
+ ```bash
248
+ skill-eval init my-skill [--output ./examples]
249
+ ```
250
+
251
+ ## Example skills
252
+
253
+ Three example skills ship with the repo, chosen to exercise different grading surfaces:
254
+
255
+ | Skill | Tests | Grading surface |
256
+ | --- | --- | --- |
257
+ | [`commit-push-pr`](skills/commit-push-pr/SKILL.md) | git workflow automation | deterministic state-diff checks (branch/commit/push/PR); needs a `--source-repo` |
258
+ | [`fix-failing-tests`](skills/fix-failing-tests/SKILL.md) | error recovery / iterative refinement | deterministic + file checks; fully offline |
259
+ | [`write-release-notes`](skills/write-release-notes/SKILL.md) | subjective writing quality, anti-fabrication | LLM rubric grading; fully offline |
260
+
261
+ Each has a matching eval suite under [`examples/`](examples/). `skills/` holds the artifacts being evaluated; `examples/` holds the test cases — so you can test one skill against many suites or one suite against many skill versions.
262
+
263
+ ## Reading results
264
+
265
+ ### Workspace layout
266
+
267
+ ```
268
+ eval-workspace/
269
+ └── <skill>-workspace/
270
+ └── iteration-1/
271
+ ├── evals_meta.json # eval definitions (used by `grade`)
272
+ ├── cleanup.json # manifest of artifacts created by this run
273
+ ├── benchmark.json # aggregate stats per (agent, config)
274
+ └── eval-<id>/<agent>/<config>/ # config = with_skill | without_skill
275
+ ├── run-N/ # only when --runs > 1
276
+ ├── outputs/
277
+ │ ├── output.txt # final agent output
278
+ │ ├── stdout.log / stderr.log
279
+ │ └── pre_state.json / post_state.json
280
+ ├── timing.json # tokens, duration, exit_code, timed_out, retries
281
+ ├── grading.json # per-assertion results
282
+ └── run_meta.json # agent, with_skill, run_index, ...
283
+ ```
284
+
285
+ ### grading.json
286
+
287
+ ```json
288
+ {
289
+ "assertion_results": [
290
+ {
291
+ "text": "A new git branch was created",
292
+ "passed": false,
293
+ "method": "deterministic",
294
+ "skipped": false,
295
+ "evidence": "No new branch appeared in this run. current_branch='main'"
296
+ }
297
+ ],
298
+ "summary": {"passed": 2, "failed": 1, "skipped": 0, "total": 3, "pass_rate": 0.667}
299
+ }
300
+ ```
301
+
302
+ To debug a failure: find `"passed": false` entries, read `evidence`, compare `pre_state.json`/`post_state.json`, then check `outputs/output.txt` for the agent's full response. Or just run `skill-eval report --show-evidence`.
303
+
304
+ ### Iterating on a skill
305
+
306
+ 1. `skill-eval run ...` → 2. `skill-eval report --show-evidence` → 3. edit SKILL.md → 4. `skill-eval run --iteration 2 ...` → 5. `skill-eval compare --workspace ... 1 2`
307
+
308
+ ## Environment variables
309
+
310
+ - `OPENROUTER_API_KEY` / `OPENAI_API_KEY`: API key for LLM rubric grading
311
+ - `OPENAI_BASE_URL`: custom grader endpoint (defaults to OpenRouter)
312
+ - `SKILL_EVAL_AGENT_TIMEOUT` / `SKILL_EVAL_AGENT_RETRIES`: harness timeout/retry defaults
313
+ - `SKILL_EVAL_KEEP_WORKSPACE`: keep per-eval workspaces for debugging
314
+
315
+ ## Agent-specific details
316
+
317
+ | Agent | Command | Skill install path |
318
+ | --- | --- | --- |
319
+ | OpenCode | `opencode run --format json --dangerously-skip-permissions` | `.opencode/skills/<name>/SKILL.md` |
320
+ | Claude Code | `claude -p --output-format json --dangerously-skip-permissions` | `.claude/skills/<name>/SKILL.md` |
321
+ | Codex | `codex exec --json --full-auto` | `.codex/skills/<name>/SKILL.md` |
322
+
323
+ ## Development
324
+
325
+ ```bash
326
+ uv run --extra dev pytest -q
327
+ uv run --extra dev ruff check src/ tests/
328
+ uv run --extra dev ruff format --check src/ tests/
329
+ ```
330
+
331
+ A `fake` agent type exists for offline testing of the full run→grade→report pipeline (used by the CI smoke test). To record a demo cast: `./scripts/record-demo.sh`.
332
+
333
+ ## License
334
+
335
+ MIT
@@ -0,0 +1,15 @@
1
+ skill_eval/__init__.py,sha256=vPXD1sMfK4sVddLA7njgqhxBaHXadclggno-qAQNMWY,197
2
+ skill_eval/__main__.py,sha256=cujL3rQKWzK2WIaxes8F_nuivtN8WeZguUmIxmiMOqQ,38
3
+ skill_eval/cli.py,sha256=aA4LC0_-FuMOjbbgFls6xXpSt35ZhkuFFwEECQQ4pOk,32986
4
+ skill_eval/git_state.py,sha256=CD4vQSmizNRfvtTNHLR8QyWbpM7WjYTK2ddegnTF6II,7936
5
+ skill_eval/models.py,sha256=AgFSXl5Qp7XM2MXjSJjX8zuB57Jq0NJTpju9RgX-i2k,3846
6
+ skill_eval/runner.py,sha256=4GRJLNF1PP6ve5OOBqL0awlRVPSCkHOFIBeJzvij7Is,16425
7
+ skill_eval/skills.py,sha256=1wgdV7Ym5a8HhMy516Wvklx5RSFydjQQZnWKi_QM_BA,3314
8
+ skill_eval/workspace.py,sha256=5bW0eOvCbdO4EuZzPCCnFgAYjOppgC6KL0I4hAsGGuE,3072
9
+ skill_eval/graders/__init__.py,sha256=GDFIh0ehqv5dVT2VDlKBtHojDkiMTZpBMi11Ixj902g,30919
10
+ skill_eval/harnesses/__init__.py,sha256=UDkzsN21Mdzb4f3EFIWmDGT4ZyZAZ1nP8zHFRj3GJYg,11733
11
+ agent_skill_eval-0.2.1.dist-info/METADATA,sha256=_CLJuRZmfl9AHhLKbevOzNeiq5TIrKbCBR6_rM5jI7s,14454
12
+ agent_skill_eval-0.2.1.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
13
+ agent_skill_eval-0.2.1.dist-info/entry_points.txt,sha256=gTP2dtthTr5UfRp1YRlnvw1b43LaQOX-CxkBUlKKoxE,50
14
+ agent_skill_eval-0.2.1.dist-info/licenses/LICENSE,sha256=JMH7PIZM50v8JDH028HJmAPsTATeHsdxjE8tNlZ9xXI,1080
15
+ agent_skill_eval-0.2.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ skill-eval = skill_eval.cli:app
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 skill-eval contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
skill_eval/__init__.py ADDED
@@ -0,0 +1,8 @@
1
+ """Skill evaluation framework."""
2
+
3
+ from importlib.metadata import PackageNotFoundError, version
4
+
5
+ try:
6
+ __version__ = version("skill-eval")
7
+ except PackageNotFoundError:
8
+ __version__ = "0.1.0"
skill_eval/__main__.py ADDED
@@ -0,0 +1,3 @@
1
+ from skill_eval.cli import app
2
+
3
+ app()