@dungle-scrubs/skillval 0.1.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/LICENSE +21 -0
- package/README.md +250 -0
- package/dist/cli.js +1009 -0
- package/dist/cli.js.map +1 -0
- package/package.json +70 -0
- package/schemas/config.schema.json +23 -0
- package/schemas/skillval.schema.json +131 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 dungle-scrubs
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# skillval
|
|
2
|
+
|
|
3
|
+
`skillval` evaluates [Agent Skills](https://agentskills.io/) with deterministic graders and no
|
|
4
|
+
model judges. Each case can run a skill arm and a baseline arm, measuring whether a skill changes
|
|
5
|
+
agent behavior instead of merely checking whether the final answer looks acceptable. When the
|
|
6
|
+
baseline also passes, the rule is flagged as a no-op and a possible prune candidate. You can only
|
|
7
|
+
trust what you test.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
pnpm add -g @dungle-scrubs/skillval
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Node.js 22 or newer is required. The Codex CLI must be installed and authenticated for evaluation
|
|
16
|
+
runs. Discovery with `skillval list` does not invoke Codex.
|
|
17
|
+
|
|
18
|
+
## Quickstart
|
|
19
|
+
|
|
20
|
+
Create `~/.config/skillval/config.yml`:
|
|
21
|
+
|
|
22
|
+
```yaml
|
|
23
|
+
roots:
|
|
24
|
+
- ~/dev/agent-skills
|
|
25
|
+
executor: codex
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Given `~/dev/agent-skills/typescript-style/SKILL.md`, add
|
|
29
|
+
`~/dev/agent-skills/typescript-style/skillval.yml`:
|
|
30
|
+
|
|
31
|
+
```yaml
|
|
32
|
+
skill: typescript-style
|
|
33
|
+
class: preference
|
|
34
|
+
cases:
|
|
35
|
+
- id: prefer-const-object
|
|
36
|
+
mode: generation
|
|
37
|
+
type: preference
|
|
38
|
+
rule: enums-as-const
|
|
39
|
+
arms: [skill, baseline]
|
|
40
|
+
prompt: >-
|
|
41
|
+
Create sizes.ts with a fixed set of small, medium, and large values.
|
|
42
|
+
assert:
|
|
43
|
+
must_match: ["as const"]
|
|
44
|
+
must_not_match: ["\\benum\\s"]
|
|
45
|
+
graders: [tsc]
|
|
46
|
+
trials: 1
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Run the case:
|
|
50
|
+
|
|
51
|
+
```console
|
|
52
|
+
$ skillval run typescript-style
|
|
53
|
+
typescript-style (preference, e8342aa91a17):
|
|
54
|
+
prefer-const-object [skill] ...
|
|
55
|
+
prefer-const-object [skill] pass
|
|
56
|
+
prefer-const-object [baseline] ...
|
|
57
|
+
prefer-const-object [baseline] FAIL
|
|
58
|
+
report: /Users/example/.local/state/skillval/reports/0f47c8d4....json
|
|
59
|
+
all cases passed
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Run every discovered skill that has a `skillval.yml` by omitting the skill names. Use `--case <id>`
|
|
63
|
+
to select one case, `--no-cache` to ignore cached arm results, `--skip-baseline` to omit baseline
|
|
64
|
+
arms, and `--json` for the complete report. The command exits with status 1 when any selected
|
|
65
|
+
case's skill arm fails.
|
|
66
|
+
|
|
67
|
+
## Configuration
|
|
68
|
+
|
|
69
|
+
The configuration follows the [configuration JSON Schema](schemas/config.schema.json):
|
|
70
|
+
|
|
71
|
+
```yaml
|
|
72
|
+
roots:
|
|
73
|
+
- ~/dev/skills/skills/standards
|
|
74
|
+
- $HOME/dev/shared/skills/backend
|
|
75
|
+
executor: codex
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`roots` contains directories whose immediate children have the form `<skill>/SKILL.md`. Both `~`
|
|
79
|
+
and `$HOME` are expanded. Missing roots are skipped during `run`; `list` returns them in
|
|
80
|
+
`missingRoots` with JSON output and prints each as `missing root: <path>` in human output.
|
|
81
|
+
|
|
82
|
+
Configuration path precedence is:
|
|
83
|
+
|
|
84
|
+
1. `--config <path>`
|
|
85
|
+
2. `SKILLVAL_CONFIG`
|
|
86
|
+
3. `$XDG_CONFIG_HOME/skillval/config.yml`
|
|
87
|
+
4. `~/.config/skillval/config.yml`
|
|
88
|
+
|
|
89
|
+
There is no legacy `~/.skillval` lookup. State uses `$XDG_STATE_HOME/skillval`, or
|
|
90
|
+
`~/.local/state/skillval` when `XDG_STATE_HOME` is unset:
|
|
91
|
+
|
|
92
|
+
- `cache/` stores arm results.
|
|
93
|
+
- `reports/` stores run reports named by a hash of the participating skills and their content
|
|
94
|
+
hashes. Each report also includes every participating skill's content hash and the executor's
|
|
95
|
+
name, version, and model identity.
|
|
96
|
+
|
|
97
|
+
`skillval list` returns the skill name, configured root, class, case count, whether `skillval.yml`
|
|
98
|
+
exists, and a `missing`, `invalid`, or `ready` status in JSON output. Invalid case files include a
|
|
99
|
+
validation error. Discovery only requires `SKILL.md`; evaluation requires a valid `skillval.yml`.
|
|
100
|
+
|
|
101
|
+
## Case files
|
|
102
|
+
|
|
103
|
+
Only a file named `skillval.yml` next to `SKILL.md` is recognized. There is no `evals.yml`
|
|
104
|
+
fallback. The complete format is described by the
|
|
105
|
+
[case-file JSON Schema](schemas/skillval.schema.json).
|
|
106
|
+
The published configuration and case-file schemas are generated from the same executable TypeBox
|
|
107
|
+
contracts used for runtime validation. Contributors can regenerate them with `pnpm schema` and
|
|
108
|
+
check freshness with `pnpm schema:check`.
|
|
109
|
+
|
|
110
|
+
Top-level fields:
|
|
111
|
+
|
|
112
|
+
- `skill`: the directory and skill name.
|
|
113
|
+
- `class`: `preference` or `capability`.
|
|
114
|
+
- `cases`: an array of deterministic evaluation cases.
|
|
115
|
+
- `fixture`: optional suite-wide workspace fixture applied to every case that does not declare
|
|
116
|
+
its own. See [Fixtures](#fixtures).
|
|
117
|
+
|
|
118
|
+
Case fields:
|
|
119
|
+
|
|
120
|
+
- `id`: unique case identifier.
|
|
121
|
+
- `mode`: `trigger` grades the final agent message; `generation` grades files produced in the
|
|
122
|
+
temporary workspace.
|
|
123
|
+
- `type`: optional `preference` or `capability` classification.
|
|
124
|
+
- `rule`: optional stable rule identifier included in reports.
|
|
125
|
+
- `should_trigger`: optional expected invocation verdict. It is checked only on the skill arm.
|
|
126
|
+
- `arms`: `skill`, or `skill` and `baseline`. The default is `[skill]`.
|
|
127
|
+
- `prompt`: the complete trial prompt.
|
|
128
|
+
- `assert.must_match`: JavaScript regular expressions that must match, with the `m` flag.
|
|
129
|
+
- `assert.must_not_match`: JavaScript regular expressions that must not match, with the `m` flag.
|
|
130
|
+
- `assert.graders`: deterministic graders. `tsc` is supported for generation cases. Unknown
|
|
131
|
+
graders and graders used with an unsupported mode are validation errors.
|
|
132
|
+
- `trials`: an integer from 1 through 5. Results use a strict majority. If configured trials
|
|
133
|
+
disagree, the arm escalates to 5 trials.
|
|
134
|
+
- `fixture`: optional workspace fixture for this case. It replaces the suite-level `fixture`
|
|
135
|
+
entirely; `path` and `setup` never merge across levels.
|
|
136
|
+
|
|
137
|
+
Every trial must also contain a complete executor trace. For generation cases, regex assertions
|
|
138
|
+
see only produced files, prefixed with `=== filename ===`; prose cannot satisfy a file assertion.
|
|
139
|
+
The `tsc` grader injects a module package file when needed and a strict bundler-resolution
|
|
140
|
+
TypeScript configuration, then runs the TypeScript installation shipped with `skillval`.
|
|
141
|
+
|
|
142
|
+
## Fixtures
|
|
143
|
+
|
|
144
|
+
By default every trial starts in an empty temporary workspace. A fixture populates that workspace
|
|
145
|
+
before the trial runs, for cases that need a realistic repository or document tree. A fixture has
|
|
146
|
+
two fields, and at least one is required:
|
|
147
|
+
|
|
148
|
+
- `path`: a directory relative to `skillval.yml`, copied recursively into the workspace before
|
|
149
|
+
the trial. `.git` and `node_modules` directories are never copied. The path must exist and be a
|
|
150
|
+
directory, and it may not contain symbolic links (create links with `setup` commands instead);
|
|
151
|
+
anything else is a validation error at load time.
|
|
152
|
+
- `setup`: shell commands run sequentially inside the workspace after the copy, with a minimal
|
|
153
|
+
environment (`PATH` plus a throwaway `HOME`). A non-zero exit fails the trial with a
|
|
154
|
+
`fixture-setup` error before the agent runs; it is never a grading failure. Each command's
|
|
155
|
+
stdout and stderr are captured into the trial record.
|
|
156
|
+
|
|
157
|
+
A suite-level `fixture` applies to every case; a case-level `fixture` replaces it entirely.
|
|
158
|
+
Fixture directory contents and setup commands are part of the arm cache identity, so editing a
|
|
159
|
+
fixture file or a setup command invalidates cached results for the cases that use it.
|
|
160
|
+
|
|
161
|
+
Generation-mode regex assertions read every workspace file except `.git` and `node_modules`
|
|
162
|
+
contents and the injected `package.json`/`tsconfig.json`, so fixture files are graded alongside
|
|
163
|
+
anything the agent produced. Graders access the workspace directly (`tsc` compiles what it finds
|
|
164
|
+
there). Write `must_match` patterns against the state you expect after the agent acts, not only
|
|
165
|
+
against new files.
|
|
166
|
+
|
|
167
|
+
Nested `.git` directories inside a fixture are not supported. Express git state with `setup`
|
|
168
|
+
commands instead - this example stages a merge conflict for the agent to resolve:
|
|
169
|
+
|
|
170
|
+
```yaml
|
|
171
|
+
skill: resolve-conflicts
|
|
172
|
+
class: capability
|
|
173
|
+
cases:
|
|
174
|
+
- id: merge-conflict
|
|
175
|
+
mode: generation
|
|
176
|
+
prompt: Resolve the merge conflict in notes.md, keeping both sections.
|
|
177
|
+
assert:
|
|
178
|
+
must_not_match: ["^<{7} ", "^={7}$", "^>{7} "]
|
|
179
|
+
fixture:
|
|
180
|
+
path: fixtures/notes-repo
|
|
181
|
+
setup:
|
|
182
|
+
- git init -q -b main
|
|
183
|
+
- git config user.name fixture && git config user.email fixture@skillval.invalid
|
|
184
|
+
- git add -A && git commit -qm base
|
|
185
|
+
- git switch -qc feature
|
|
186
|
+
- printf 'feature section\n' >> notes.md && git commit -qam feature
|
|
187
|
+
- git switch -q main
|
|
188
|
+
- printf 'main section\n' >> notes.md && git commit -qam main
|
|
189
|
+
- git merge feature || true
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
The final `|| true` matters: `git merge` exits non-zero on conflict, which would otherwise fail
|
|
193
|
+
the trial as a fixture-setup error - here the conflict is the point.
|
|
194
|
+
|
|
195
|
+
## Executors
|
|
196
|
+
|
|
197
|
+
Executors are adapters with three responsibilities: report stable metadata for cache keys, prepare
|
|
198
|
+
provider-specific skill and environment state, and run one trial request to return a normalized
|
|
199
|
+
`Trace`. The runner owns temporary workspace lifecycle, grading, caching, majority voting, and
|
|
200
|
+
reports. `codex` is the only adapter today.
|
|
201
|
+
|
|
202
|
+
The Codex adapter runs:
|
|
203
|
+
|
|
204
|
+
```text
|
|
205
|
+
codex exec --json --skip-git-repo-check --ephemeral -s <sandbox> -C <workspace> <prompt>
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Trigger cases use a read-only sandbox. Generation cases use `workspace-write`. Codex has no
|
|
209
|
+
dedicated skill-invocation event, so its adapter detects invocation when a `command_execution`
|
|
210
|
+
command contains `<skill>/SKILL.md`. The adapter also gives the skill arm a workspace-local
|
|
211
|
+
`.agents/skills/<name>` symlink to the evaluated skill.
|
|
212
|
+
|
|
213
|
+
Baseline arms are not seeded. Their `HOME` points to an empty temporary directory so globally
|
|
214
|
+
installed skills are invisible, while `CODEX_HOME` still points to the user's real `~/.codex` for
|
|
215
|
+
authentication and model configuration. A Claude executor is planned.
|
|
216
|
+
|
|
217
|
+
Cached arm results are keyed by runner version, skill content hash, serialized case, arm, executor
|
|
218
|
+
name, executor version, and configured model. A trial has a 15-minute timeout and a 64 MB output
|
|
219
|
+
buffer.
|
|
220
|
+
|
|
221
|
+
## Roadmap
|
|
222
|
+
|
|
223
|
+
- Add a Claude executor, then support multi-executor runs through the same normalized trace
|
|
224
|
+
interface.
|
|
225
|
+
- Run multiple models and emit per-model reports. A passing binding or trigger result on a weaker
|
|
226
|
+
tier is a conservative bound for stronger tiers. Baseline no-op results remain model-specific,
|
|
227
|
+
and a rule is a prune candidate only when every model in normal use passes at baseline.
|
|
228
|
+
- Add contested-boundary cases with `expect_invoked` and `expect_not_invoked` outcomes.
|
|
229
|
+
- Include the discovered skill-listing hash in trigger-case invalidation so description changes in
|
|
230
|
+
neighboring skills invalidate affected results.
|
|
231
|
+
- Add cheap trigger simulations for broad description coverage before expensive executor trials.
|
|
232
|
+
- Add a `lint` subcommand for Agent Skills format, references, case coverage, and regular
|
|
233
|
+
expressions.
|
|
234
|
+
- Evaluate agent instruction files (`CLAUDE.md`, `AGENTS.md`) with the same arm comparison, so
|
|
235
|
+
instruction rules can be proven load-bearing or flagged as no-ops the way skill rules are. The
|
|
236
|
+
shape is undecided: likely instructions-present vs instructions-absent arms over cases derived
|
|
237
|
+
from the file's rules.
|
|
238
|
+
- Harvest missed triggers, false invocations, and behavioral regressions from real session
|
|
239
|
+
transcripts as new cases.
|
|
240
|
+
- Support multi-model interpretation and no-op pruning in report summaries, not only raw reports.
|
|
241
|
+
|
|
242
|
+
## Contributing
|
|
243
|
+
|
|
244
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). Commits follow Conventional Commits, PRs are
|
|
245
|
+
squash-merged, and every change must pass
|
|
246
|
+
`pnpm typecheck && pnpm lint && pnpm schema:check && pnpm test && pnpm build`.
|
|
247
|
+
|
|
248
|
+
## License
|
|
249
|
+
|
|
250
|
+
[MIT](LICENSE)
|