skillgauntlet 1.0.0 → 1.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 +201 -0
- package/LICENSE-DATA +438 -0
- package/README.md +238 -0
- package/bin/skillgauntlet.js +485 -0
- package/contracts/diagnosing-bugs.json +37 -0
- package/contracts/tdd.json +33 -0
- package/contracts/webapp-testing.json +38 -0
- package/contracts/xlsx.json +33 -0
- package/lib/codewhale.js +174 -0
- package/lib/codex.js +318 -0
- package/lib/depth.js +55 -0
- package/lib/discover.js +83 -0
- package/lib/execute.js +153 -0
- package/lib/gain.js +177 -0
- package/lib/i18n.js +754 -0
- package/lib/installed.js +43 -0
- package/lib/queries.js +37 -0
- package/lib/report.js +145 -0
- package/lib/runs.js +167 -0
- package/lib/spawn.js +84 -0
- package/lib/static.js +187 -0
- package/lib/trial.js +413 -0
- package/lib/ui.js +156 -0
- package/package.json +40 -6
- package/index.js +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# SkillGauntlet
|
|
2
|
+
|
|
3
|
+
*Every skill runs the gauntlet.*
|
|
4
|
+
|
|
5
|
+
Agent skills (SKILL.md) keep piling up and changing fast, yet nobody tells you which ones are still worth keeping. SkillGauntlet is an independent, third-party evaluation of popular skills, using public and reproducible methods to answer what you most need to know before installing one.
|
|
6
|
+
|
|
7
|
+
## Three layers, cheapest first
|
|
8
|
+
|
|
9
|
+
| Layer | Question | Cost |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| 0 static | What does it put in my context, what is it allowed to touch, what does its code do, does it try to hijack the model, is the description even usable? | zero |
|
|
12
|
+
| 1 trigger | When I ask for the job it claims, does it actually fire? Does it fire when it shouldn't? | model calls |
|
|
13
|
+
| 2 gain | With it versus without it, on a task a program can grade: does it help? | model calls ×2 |
|
|
14
|
+
|
|
15
|
+
Layer 0 runs first because it is free and because it disqualifies most problems before you spend anything.
|
|
16
|
+
Layers 1 and 2 are opt-in.
|
|
17
|
+
|
|
18
|
+
## Results so far
|
|
19
|
+
|
|
20
|
+
Step 0 tested 18 popular skills on 2026-10-28. Start with the
|
|
21
|
+
[overview](step0/data/OVERVIEW-2026-10-28.md), which says what each report measures and which numbers
|
|
22
|
+
can be compared.
|
|
23
|
+
|
|
24
|
+
- [Haiku batch](step0/data/2026-10-28_all_haiku/BATCH.md) (headline): Claude Code × claude-haiku-5-5,
|
|
25
|
+
1,798 trials. 1 skill triggers well, 6 fair, 11 poorly; across all skills, recall is 41%.
|
|
26
|
+
- [DeepSeek batch](step0/data/2026-10-28_all_deepseek/BATCH.md): the same question sets on
|
|
27
|
+
deepseek-v4-flash. Overall recall is the same, but single skills move a lot, one of them by more than 70 points.
|
|
28
|
+
- [Codex probe](step0/data/2026-10-28_codex_probe/BATCH.md): facts only, no trigger rate (see
|
|
29
|
+
"Hosts and models").
|
|
30
|
+
- [Static layer](step0/data/2026-10-28_all_haiku/static/SUMMARY.md): all 18 skills, no model calls.
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
Needs Node 18 or later; the package has no dependencies.
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
npx skillgauntlet # interactive
|
|
38
|
+
npx skillgauntlet static path/to/skill # layer 0 only, no model calls
|
|
39
|
+
npx skillgauntlet static path/to/repo --json
|
|
40
|
+
npx skillgauntlet resume path/to/run-dir # continue an interrupted or stopped run
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
From a clone, `node bin/skillgauntlet.js` does the same, and
|
|
44
|
+
`node scripts/static-summary.mjs step0/skills.json step0/data/static` runs layer 0 over a whole manifest.
|
|
45
|
+
Add `--lang zh|ja|en`, or set `SG_LANG`, to choose the language without the menu.
|
|
46
|
+
|
|
47
|
+
The interactive flow asks the following, in this order, and takes only arrow keys, `y`/`n` and enter:
|
|
48
|
+
|
|
49
|
+
1. **Language**: 中文, 日本語 or English.
|
|
50
|
+
2. **Where the skill comes from**: already installed (scanned in `.agents/skills`,
|
|
51
|
+
`.claude/skills`, `.codex/skills`, user and project scope), another local directory, or a GitHub
|
|
52
|
+
repository for a skill you have not installed.
|
|
53
|
+
3. **How many**: all discovered skills, or just one picked with the arrow keys.
|
|
54
|
+
4. **Host**: Claude Code, CodeBuddy Code, Codewhale or Codex (see "Hosts and models").
|
|
55
|
+
5. **Depth**, then **backend** and **model**.
|
|
56
|
+
6. **An estimate before anything is spent**: trials and minutes for the whole selection, not just
|
|
57
|
+
per skill. No cash figure is shown, because what a run costs depends on the backend and the plan,
|
|
58
|
+
and a number would be a guess.
|
|
59
|
+
7. **`y`** to run, `n` to cancel.
|
|
60
|
+
|
|
61
|
+
A skill that is not installed is named the way it can be resolved: `owner/repo`, or
|
|
62
|
+
`owner/repo/path/to/skill` when the repository holds several. There is no global name-to-repository
|
|
63
|
+
registry, so a bare name like `tdd` cannot be resolved and the prompt says so.
|
|
64
|
+
|
|
65
|
+
Layer 2 results are a **local comparison and are never uploaded**. Nothing is uploaded at all: there is
|
|
66
|
+
no public leaderboard yet, so the CLI says so instead of offering a submission that would go nowhere.
|
|
67
|
+
|
|
68
|
+
Every run is archived under the skill's `.skillgauntlet/runs/<timestamp>/`:
|
|
69
|
+
|
|
70
|
+
| file | contents |
|
|
71
|
+
|---|---|
|
|
72
|
+
| `raw.jsonl` | one record per trial, appended as it finishes |
|
|
73
|
+
| `meta.json` | host, backend, model, runs, exploration budget, depth, question-set hash, platform, start and end |
|
|
74
|
+
| `summary.json` | the totals the report was rendered from |
|
|
75
|
+
|
|
76
|
+
`meta.json` is written before the first trial and refreshed every 25 records, so a run killed by
|
|
77
|
+
Ctrl-C, a quota limit or a closed laptop keeps everything it already finished and still says what
|
|
78
|
+
conditions produced it. The records are the only artefact that cannot be regenerated cheaply: the
|
|
79
|
+
report is a rendering of them, and keeping them means the statistics can be corrected, two runs
|
|
80
|
+
compared, or a submission assembled later without paying for the trials again.
|
|
81
|
+
|
|
82
|
+
A run stops by itself after 8 errors or timeouts in a row (a quota limit usually looks like that) and
|
|
83
|
+
exits with code 3. `resume <run-dir>` continues it, and retries the failed trials. It refuses to resume
|
|
84
|
+
when the model, backend, runs, exploration budget, question set or SKILL.md has changed, because records
|
|
85
|
+
made under different conditions cannot be pooled.
|
|
86
|
+
|
|
87
|
+
The report follows the language you picked (Chinese, Japanese or English). Every sentence comes from
|
|
88
|
+
one message table, and the scorecard JSON carries message keys rather than prose
|
|
89
|
+
(`skillgauntlet/scorecard/2`), so a stored scorecard can be rendered in any of the three languages
|
|
90
|
+
without re-analysing the skill. The Japanese wording has not yet been reviewed by a native speaker.
|
|
91
|
+
|
|
92
|
+
The static layer reports by group and never sums them: a weighted total cannot be audited or appealed,
|
|
93
|
+
and appeal is the one thing a rating must allow.
|
|
94
|
+
|
|
95
|
+
- **A context cost** — SKILL.md size in tokens, description length (read on every request), whether the
|
|
96
|
+
body references files instead of inlining them, package size.
|
|
97
|
+
- **B permission surface** — whether `allowed-tools` is declared, how wide the declared scope is,
|
|
98
|
+
whether the skill is manual-invocation only, how much executable code it ships.
|
|
99
|
+
- **C code risk** — network calls, command execution, credential access, obfuscation, install hooks.
|
|
100
|
+
Reported as facts; escalated only when the skill ships code *and* declares no scope.
|
|
101
|
+
- **D instruction hijacking** — override phrases, requests for sensitive paths, instructions to send
|
|
102
|
+
data out, zero-width characters, external images.
|
|
103
|
+
- **E description quality** — presence, whether it states when to use it, over-broad quantifiers,
|
|
104
|
+
rough clause count, name/directory agreement.
|
|
105
|
+
|
|
106
|
+
## Status
|
|
107
|
+
|
|
108
|
+
Early stage, and honestly labelled.
|
|
109
|
+
|
|
110
|
+
- **Layer 0: implemented and exercised** on the 18 step 0 skills, twice, with identical output; see
|
|
111
|
+
[`static/SUMMARY.md`](step0/data/2026-10-28_all_haiku/static/SUMMARY.md). Its thresholds (SKILL.md
|
|
112
|
+
token counts, package size) are not yet calibrated against a real distribution, so treat them as a
|
|
113
|
+
ranking aid, not as a pass mark.
|
|
114
|
+
- **Layer 1: two runners, published data.**
|
|
115
|
+
- The Python runner in [`step0/`](step0/README.md) produced every published batch. It runs a whole
|
|
116
|
+
manifest, resumes, and pins each skill to a commit.
|
|
117
|
+
- The Node CLI is the one meant for other people. It follows the same verdict rule and has
|
|
118
|
+
been run end to end on one skill on each path: Claude Code × Haiku, Claude Code × DeepSeek,
|
|
119
|
+
CodeBuddy Code and Codewhale.
|
|
120
|
+
- The two runners draft questions with different prompts: the CLI's negatives must borrow the
|
|
121
|
+
description's wording. Their question sets are therefore not interchangeable.
|
|
122
|
+
- **Layer 2: contract DSL implemented** ([`contracts/`](contracts/)), with per-task contracts for tdd,
|
|
123
|
+
xlsx, webapp-testing and diagnosing-bugs: 14 contracts covering 4 of the 18 skills. No LLM judge, on
|
|
124
|
+
purpose: a judge would be expensive, biased and not reproducible. A skill without a contract is
|
|
125
|
+
reported as "not gradable automatically" rather than guessed. `node scripts/check-contracts.mjs`
|
|
126
|
+
validates every contract file offline. It fails any contract that already passes on an untouched
|
|
127
|
+
fixture, because such a contract is measuring nothing.
|
|
128
|
+
- **Tests**: `npm test` and `python -m unittest discover -s tests` run offline and spend no quota; CI
|
|
129
|
+
runs both.
|
|
130
|
+
|
|
131
|
+
Question sets are cached in `.skillgauntlet/queries-<per-clause>x<negatives>.json` next to the report
|
|
132
|
+
and reused while the skill's SKILL.md is unchanged, so re-running a skill does not pay for drafting
|
|
133
|
+
again. Editing the description invalidates the cache, which is exactly when fresh questions are needed.
|
|
134
|
+
|
|
135
|
+
## Hosts and models
|
|
136
|
+
|
|
137
|
+
| Host | Layer 0 | Trigger rate (layer 1) | Gain (layer 2) | What it spends |
|
|
138
|
+
|---|---|---|---|---|
|
|
139
|
+
| Claude Code | yes | yes | yes | Claude plan quota, or an API key; DeepSeek via `ANTHROPIC_BASE_URL` |
|
|
140
|
+
| CodeBuddy Code | yes | yes | no | the CodeBuddy account's quota |
|
|
141
|
+
| Codewhale | yes | yes | no | DeepSeek API cash (`DEEPSEEK_API_KEY`) |
|
|
142
|
+
| Codex | yes | **no** (not observable) | no; activation probe instead | the ChatGPT plan's quota |
|
|
143
|
+
|
|
144
|
+
A trigger rate is a number for one host and one model. Numbers from two hosts are never merged or
|
|
145
|
+
ranked against each other.
|
|
146
|
+
|
|
147
|
+
Layer 0 is host-agnostic: it reads a skill directory, so it works for a skill installed by any host.
|
|
148
|
+
|
|
149
|
+
Layer 1 drives a CLI, so it measures that CLI's host. The model is whatever `--model` resolves to under
|
|
150
|
+
your credentials: the plan's aliases on a subscription, any model id on an API key. A non-Anthropic
|
|
151
|
+
backend works through `ANTHROPIC_BASE_URL`; DeepSeek's endpoint needs an API key, because there is no
|
|
152
|
+
login it could reuse.
|
|
153
|
+
|
|
154
|
+
**CodeBuddy Code** (the command line behind Tencent's WorkBuddy) is a second host with a measurable trigger rate: a skill call appears in its stream as a `tool_use` named `Skill`, the same shape as in Claude Code, which was confirmed on a live run. It spends the quota of its own account (log in once with `/login` inside `codebuddy`), runs the trigger layer only, and is launched with a restricted tool list because its default one has 60+ tools. A rate measured here is a CodeBuddy number and is never merged with a Claude Code one.
|
|
155
|
+
|
|
156
|
+
**Codewhale** (formerly DeepSeek-TUI, runs DeepSeek models) is a third host with a measurable trigger rate. It reads a skill through a `load_skill` tool, so the first `load_skill` call whose `input.name` is the tested skill counts as a trigger; a search (`input.query`) or a catalogue listing does not. Trials run in a throw-away `HOME` that holds only the tested skill, with `load_skill` as the only tool (its `--auto` mode has no execution sandbox on Windows). It needs `codewhale` on `PATH` (the single exe from the project's release page, renamed `codewhale.exe`) and `DEEPSEEK_API_KEY` in the environment. A rate measured here is a Codewhale number and is never merged with another host's.
|
|
157
|
+
|
|
158
|
+
**Codex is supported for what it exposes, which is a smaller surface:**
|
|
159
|
+
|
|
160
|
+
| Codex emits | Codex does not emit |
|
|
161
|
+
|---|---|
|
|
162
|
+
| `thread.started`, `turn.started`, `turn.completed`, `turn.failed`, `error` | any skill item |
|
|
163
|
+
| `item.started/updated/completed` carrying `command_execution`, `mcp_tool_call`, `file_change`, `todo_list`, `agent_message`, `user_message`, `agent_reasoning`, `error` | a "skill was used" event |
|
|
164
|
+
|
|
165
|
+
That schema was confirmed against a live run (codex-cli 0.159.2, ChatGPT account), which also showed
|
|
166
|
+
two traps worth knowing: with `--sandbox read-only` every `exec_command` is refused by policy, so a
|
|
167
|
+
probe needs `workspace-write`; and without `--ignore-user-config` a stale model id in
|
|
168
|
+
`~/.codex/config.toml` fails every turn for reasons that have nothing to do with the skill.
|
|
169
|
+
|
|
170
|
+
A skill in Codex is host-side context: selected and injected, not called as a tool. `skills_used` exists
|
|
171
|
+
only in the telemetry profile (`unique_skills_used`, `total_skills_used`), never in the stream. So a
|
|
172
|
+
trigger rate cannot be measured here, and reporting one would mean inventing it. What is measurable is
|
|
173
|
+
whether the agent acted and what it changed — which is the **activation probe**: the same request with
|
|
174
|
+
and without the skill, comparing the first action and the changed files. That is evidence of influence,
|
|
175
|
+
not of triggering, and the report labels it that way.
|
|
176
|
+
|
|
177
|
+
The probe path is exercised end to end: two runs of the same request on a task that must produce
|
|
178
|
+
files, one with the skill installed and one without. It reports facts — the first action, the files
|
|
179
|
+
each side produced, and whether those match — and deliberately gives **no influenced/not-influenced
|
|
180
|
+
verdict**. Two runs of the same request never execute identical command strings, so "the action
|
|
181
|
+
differs" is true almost always and would decide nothing.
|
|
182
|
+
|
|
183
|
+
Two things the probe taught us, both now baked in: the skill is installed only into `.agents/skills`,
|
|
184
|
+
because a `.codex/skills` copy in the workspace changed the sandbox outcome of one side and turned the
|
|
185
|
+
comparison into a false positive; and `file_change` items only appear for patch-style edits, so the
|
|
186
|
+
record of what changed comes from a filesystem diff rather than from the stream.
|
|
187
|
+
|
|
188
|
+
## Method principles
|
|
189
|
+
|
|
190
|
+
- Trigger detection follows Anthropic's skill-creator trigger eval: the model's first tool decision decides.
|
|
191
|
+
- "Answered without calling any tool" is counted apart from "called another tool first". On a backend
|
|
192
|
+
with weaker tool-call adherence that difference decides whether the model or the description is at fault.
|
|
193
|
+
- Each skill is tested with **two or more independently AI-drafted question sets**, grouped by the trigger
|
|
194
|
+
clauses in its description. Results are reported per clause and per set, with the **spread between
|
|
195
|
+
sets**; skills whose sets disagree are flagged.
|
|
196
|
+
- Question sets are not reviewed by hand, and neither are grades, including low ones. Independent sets and
|
|
197
|
+
a published spread make results checkable without a reviewer, and a reviewer's own judgement would be the
|
|
198
|
+
one part of the method nobody could reproduce. Mistakes are corrected through public appeals instead
|
|
199
|
+
(see [CONTRIBUTING.md](CONTRIBUTING.md)). Reports say plainly that they are automated results under stated
|
|
200
|
+
conditions.
|
|
201
|
+
- **Negatives must borrow wording, and the claim is checked.** The drafter has to name a fragment of its
|
|
202
|
+
own request that carries the borrowed wording, and the runner warns when it cannot. Without that, a
|
|
203
|
+
drafter writes negatives from a different domain, they never trigger, and the false-positive rate comes
|
|
204
|
+
out at zero for reasons that have nothing to do with the skill -- which is the one finding the report
|
|
205
|
+
is supposed to deliver. This rule is in the CLI. The published 2026-10-28 batches were drafted by the
|
|
206
|
+
Python runner, whose prompt was kept unchanged so earlier batches stay comparable, so their
|
|
207
|
+
false-positive rates are labelled as measured with weak negatives.
|
|
208
|
+
- Coarse grades only, with confidence intervals computed over **queries** (the independent unit), not
|
|
209
|
+
over trials: three runs of one query are not three independent observations.
|
|
210
|
+
- Every result is pinned on four dimensions: skill version (commit plus SKILL.md hash), host version,
|
|
211
|
+
model version and question-set version. A backend other than Anthropic is recorded too.
|
|
212
|
+
- Raw per-trial records and the question sets are published alongside each report so anyone can
|
|
213
|
+
reproduce it, and anyone can point out a bad question in an Issue.
|
|
214
|
+
|
|
215
|
+
## Deeper material
|
|
216
|
+
|
|
217
|
+
- [`step0/data/OVERVIEW-2026-10-28.md`](step0/data/OVERVIEW-2026-10-28.md): the step 0 results in one page.
|
|
218
|
+
- [`step0/README.md`](step0/README.md): the Python trigger-test runner (commands, verdict rules,
|
|
219
|
+
isolation).
|
|
220
|
+
- [`step0/batch_tables.py`](step0/batch_tables.py): recounts every table of the 2026-10-28 reports from the
|
|
221
|
+
raw records.
|
|
222
|
+
- [`step0/experiments/qset-tdd/EXPERIMENT.md`](step0/experiments/qset-tdd/EXPERIMENT.md): how much results
|
|
223
|
+
depend on the question set, and why sets are not reviewed by hand.
|
|
224
|
+
- [`SECURITY.md`](SECURITY.md): what the runner executes, what it isolates and what it does not.
|
|
225
|
+
|
|
226
|
+
## License
|
|
227
|
+
|
|
228
|
+
| Content | License |
|
|
229
|
+
|---|---|
|
|
230
|
+
| Code (runner, scripts) | [Apache-2.0](LICENSE) |
|
|
231
|
+
| Data (`step0/queries/` question packs, test results and reports) | [CC BY-NC-SA 4.0](LICENSE-DATA) |
|
|
232
|
+
|
|
233
|
+
The data is free forever for personal and non-commercial use; for commercial use, contact the maintainers for a separate license. The source code of the skills under test is not in this repository; the runner fetches it from each upstream repository at run time, and copyright stays with the original authors.
|
|
234
|
+
|
|
235
|
+
## Contributing
|
|
236
|
+
|
|
237
|
+
Issues and appeals are open to everyone. Pull requests need a signed Contributor License Agreement; see
|
|
238
|
+
[CONTRIBUTING.md](CONTRIBUTING.md).
|