skill-unit
This skill should be used when the user asks to "run skill tests", "test my skill", "evaluate a skill", "check skill quality", "run the test suite", "/skill-unit", "did the last run pass", "why did my skill test fail", "show the transcript for a skill test", "print the conversation for a test", "what did the X test do", "what runs do I have", or mentions skill testing, spec files, test case IDs (e.g. SU-1, EX-2, WG-1), failing tests, test transcripts, or inspecting test-run artifacts. Anything involving a named test case, a past test run, a transcript, or a grader verdict belongs to this skill.
- License 许可
- MIT
- 最近更新
- 2026-09-20
- Stars
- ★ 1
- Forks
- 0
01 前言 Front Matter
- name
- skill-unit
- description
- This skill should be used when the user asks to "run skill tests", "test my skill", "evaluate a skill", "check skill quality", "run the test suite", "/skill-unit", "did the last run pass", "why did my skill test fail", "show the transcript for a skill test", "print the conversation for a test", "what did the X test do", "what runs do I have", or mentions skill testing, spec files, test case IDs (e.g. SU-1, EX-2, WG-1), failing tests, test transcripts, or inspecting test-run artifacts. Anything involving a named test case, a past test run, a transcript, or a grader verdict belongs to this skill.
- argument-hint
- [skill-name | spec-name | test-id | tag | prompt ...]
02 资源文件 5
- 参考文档references/spec-format.md12.9 KB
- 参考文档references/testing-guidelines.md9.6 KB
- 脚本scripts/run-cli.sh1.5 KB
- 参考文档templates/.skill-unit.yml2.3 KB
- 参考文档templates/example.spec.md1.6 KB
03 SKILL.md 正文
Skill Unit - Skill Testing Framework
A structured, reproducible testing framework for AI agent skills. This skill delegates to the skill-unit CLI, which runs the full pipeline: discover spec files, execute prompts in isolated workspaces, grade responses with independent agents, and produce a consolidated report.
Execution Process
Hard rule: never read .workspace/runs/ directly
Troubleshooting queries route through this skill's subcommands, not direct file reads. Never use Read, Glob, or Grep against anything under .workspace/runs/. This applies even when a CLI command mentions or prints a path to a file under .workspace/runs/: do not follow that path with Read. To get the full content of a transcript or grader transcript, re-invoke the relevant subcommand with --full instead. If the information you need is not exposed by a subcommand, surface that gap to the user rather than scraping files. This rule exists so the skill remains the single, predictable entry point for run history, transcripts, and grading.
Classify intent
Pick one of three flows from the user's request, then follow that flow's steps.
| Intent | Signals | Go to |
|---|---|---|
| Run tests | "run", "test", "/skill-unit", "rerun" | Running Tests |
| Troubleshoot | "why did X fail", "show the transcript", "what happened in the last run", "did the last run pass" | Troubleshooting Runs |
| List/inspect | "what tests do I have", "search for X", "list tests" | Advanced Usage (ls) |
Running Tests
Follow these steps in order.
Step 0: Run init First (Once Per Chat Session)
Before the first "run tests" request in a chat session, invoke init. Skip this step on subsequent requests in the same session — if you have already invoked init in this conversation, go straight to Step 1.
bash "${CLAUDE_PLUGIN_ROOT}/skills/skill-unit/scripts/run-cli.sh" init
init is idempotent. If the project is already configured, it prints Project already bootstrapped for skill-unit. No changes made. Otherwise it creates skill-tests/.gitkeep, .skill-unit.yml, appends .workspace to .gitignore, and adds the skill's bootstrap permission to .claude/settings.json. Each step logs whether it was created, updated, or skipped. If init made changes, relay its output to the user. Then continue to Step 1 regardless of what init did.
Do not try to detect the configured state yourself by reading .skill-unit.yml or similar. The init command owns that check. Never use absolute paths when looking for project files: the current working directory is the project root, and the skill must not reach outside it.
Step 1: Map User Intent to CLI Args
Invoke the CLI through the plugin-provided wrapper ${CLAUDE_PLUGIN_ROOT}/skills/skill-unit/scripts/run-cli.sh. The wrapper resolves skill-unit from PATH, falls back to a project-local install via npx --no-install, and errors out with install instructions if neither is available. Do not attempt discovery yourself; call the wrapper.
For brevity, the table below uses run-cli.sh as shorthand for the full path.
The test subcommand requires at least one filter (or --all).
| User says | CLI invocation |
|---|---|
| "Run all the tests" | run-cli.sh test --all |
"Run the tests for the <X> skill" |
run-cli.sh test --skill <X> |
"Run the <name1> and <name2> tests" |
run-cli.sh test --name <name1>,<name2> |
"Run tests tagged <tag>" |
run-cli.sh test --tag <tag> |
"Run test case <ID>" |
run-cli.sh test --test <ID> |
"Run the tests in <path>" |
run-cli.sh test --file <path> |
| "/skill-unit" (no args) | run-cli.sh test --all |
"/skill-unit <X>" |
Treat <X> as a filter target (see "Slash-command arguments" below). |
Slash-command arguments: /skill-unit <X> accepts either a filter target (a skill name, spec name, test id, or tag — typically a single token like report-card) or a natural-language phrase (run my tests, rerun the failed ones). To decide:
- First try resolving
<X>as a filter withrun-cli.sh ls --search <X>. If it returns at least one match, use the appropriate filter and runtest. - If
ls --searchreturns nothing AND<X>reads as a natural-language phrase, treat the whole phrase as the user's request and route through "Classify intent" at the top of this skill. - A bare token like
report-cardis the literal namereport-card, not a noun phrase. Do not parse it as English ("a report card of the test runs"); resolve it as a filter via step 1.
Whichever path you take, do not offer the user a menu of other actions when the intent is clearly to run tests.
Ambiguous targets: when the user's request names a target that could be a skill, a spec, or a test (e.g. "run the tests for <X>", "/skill-unit <X>", "run the <X> tests"), do not guess a filter. First resolve the target with run-cli.sh ls --search <X>. The search does case-insensitive partial matching across spec name, frontmatter skill:, file basename, test case ID, and test case name, and prints each match with its skill and file path. Then pick the right test filter from the match:
- One spec matched via its
skill:field →test --skill <X> - One spec matched via its
namefield →test --name <X> - Specific test cases matched →
test --test <ID1>,<ID2> - Multiple candidates with no clear winner → show the list to the user and ask which to run.
If ls --search <X> returns nothing, relay that to the user and suggest creating tests with /skill-test-design <X> rather than trying other filters.
Pass-through overrides (apply only if the user asks):
- "Use
<model>" →--model <model> - "Give each test
<duration>" →--timeout <duration>(e.g.60s,2m) - "Up to
<N>turns" →--max-turns <N> - "Keep the workspaces so I can debug" →
--keep-workspaces
Step 2: Run the CLI
Invoke the wrapper in the foreground (not a background task). The CLI streams progress to stdout/stderr; surface it to the user as it arrives.
bash "${CLAUDE_PLUGIN_ROOT}/skills/skill-unit/scripts/run-cli.sh" test <filter-args>
The CLI performs the entire pipeline in one call:
- Reads
.skill-unit.yml(applies defaults if missing fields). - Discovers
*.spec.mdfiles undertest-dirand applies filters. - Compiles spec files into manifests at
.workspace/runs/{timestamp}/manifests/. - Runs every test case in an isolated workspace with the configured harness, up to
runner.concurrencyat a time. - Dispatches a grader agent per test case, up to
execution.grader-concurrencyat a time. - Writes per-test transcripts and results to
.workspace/runs/{timestamp}/results/. - Generates
report.mdand prints a summary to stdout. - Records the run in
.skill-unit/stats.json. - Exits 0 if all tests passed, 1 otherwise.
While the CLI runs, do not poll, do not call the grader agent yourself, do not regenerate the manifest. The CLI owns the whole pipeline.
If no spec files are discovered, the CLI logs No spec files found matching filters. Relay this to the user and suggest creating a test case with the skill-test-design skill (/skill-test-design <skill-name>).
Step 3: Present the Report
After the CLI exits, read the generated report at:
.workspace/runs/{timestamp}/results/report.md
The exact path is printed in the CLI's final summary line. Present the report content, then append a brief summary in this format:
**{N} passed** | **{N} failed** | {N} total
Full report: [report.md](.workspace/runs/{timestamp}/results/report.md)
For failing tests, quote the one-phrase failure reason from the report (it already extracts these). Link to individual transcripts and grading files when helpful.
Troubleshooting Runs
When asked to troubleshoot failing tests or inspect past run artifacts, use the following read-only subcommands. These are tools, not a step-by-step workflow — pick the ones that fit the request. All go through the wrapper (run-cli.sh shorthand for ${CLAUDE_PLUGIN_ROOT}/skills/skill-unit/scripts/run-cli.sh).
| Subcommand | What it answers |
|---|---|
run-cli.sh runs [--limit N] [--failed-only] |
Which runs exist, newest first, with pass/fail counts. |
run-cli.sh show <run-id|latest> [--failed-only] [--full] |
What happened in one run: per-test verdicts and failure reasons. |
run-cli.sh transcript <run-id|latest> <test-id> [--full] |
What the agent under test actually did for one test case. |
run-cli.sh grading <run-id|latest> <test-id> [--full] |
Why the grader marked one test case the way it did. |
run-cli.sh ls --search <term> |
Resolve an ambiguous name to concrete test IDs, skills, or spec files. |
Example mappings (illustrations, not a mandatory routing table):
| User says | Reasonable invocation |
|---|---|
| "Did the last run pass?" / "What was the last run?" | runs --limit 1 |
| "Show me recent runs" | runs --limit 10 |
| "Show me only failed runs" | runs --failed-only |
| "Why did the last run fail?" | show latest --failed-only |
"Show run <timestamp>" |
show <timestamp> |
"How did <test-id> go?" / "What happened with <test-id>?" |
grading latest <test-id> |
"Why did <test-id> fail?" |
grading latest <test-id> |
"Show the transcript for <test-id>" |
transcript latest <test-id> |
"Give me the full transcript for <test-id>" |
transcript latest <test-id> --full |
Constraints that apply to every troubleshooting invocation:
- Run identifiers: either the literal string
latest(newest run) or a full timestamp directory name like2026-04-19-18-24-23. There is no prefix matching. - Test identifiers: exact, case-sensitive test IDs as they appear in spec files (e.g.
SU-1). - Ambiguous names — resolve first. If the user names something that could be a skill, spec, or test case (e.g. "the widget tests", "report-card"), your first CLI call must be
ls --search <term>. Only after you see the search result may you call any troubleshooting subcommand (runs,show,transcript,grading). Skipping this and going straight to e.g.show latestbecause "the latest run probably has it" is wrong even when it happens to work — the disambiguation step is what the user is relying on you to do. The only exception is when the user supplied an exact, unambiguous identifier (a literal test ID likeSU-T1, or a full run timestamp).ls --searchdoes case-insensitive partial matching across spec name, frontmatterskill:, file basename, test case ID, and test case name. - Default to summaries: the subcommands print structured summaries by default. Pass
--fullonly when the user explicitly asks for the entire transcript or grader output (e.g. "show me the whole transcript", "give me the complete log", "line-by-line"). Phrases like "how did X go" or "show me the transcript" are summary-intent. - No direct file reads: never fall back to Read/Glob/Grep on
.workspace/runs/. The--fullflag exists so you don't have to. - Always relay verdict AND reason. When answering about a specific test (e.g. "How did
<id>go?", "Why did<id>fail?"), your reply MUST state the verdict (pass/fail) AND, for failing tests, quote the failure reason from the CLI's output. The CLI prints both on aReason:line; do not summarize that line away or drop it. A reply that only says "EX-2 failed" without naming the reason is incomplete.
If the CLI says No runs yet., relay that to the user and suggest skill-unit test --all. If it reports an unknown run or test id, the error lists available ids; pick one from that list.
Advanced Usage
The CLI has additional subcommands for discovery, compilation, and report regeneration. All go through the same wrapper.
| Subcommand | Purpose |
|---|---|
run-cli.sh ls [filters] |
List discovered spec files and their test cases. Also the resolver for ambiguous targets via --search. |
run-cli.sh compile [filters] |
Parse spec files and write manifest JSON without running anything. Useful for inspecting what would run. |
run-cli.sh report --run-dir <path> |
Re-generate report.md from an existing run directory. |
For troubleshooting past runs (runs, show, transcript, grading), see the Troubleshooting Runs section above.
Configuration
The CLI reads .skill-unit.yml at the repository root. Defaults apply when fields are missing:
test-dir: skill-tests
runner:
tool: claude # harness CLI (claude, copilot, codex)
model: sonnet # model for the test agent (optional)
max-turns: 10
concurrency: 5 # max test cases running in parallel
output:
format: interactive
show-passing-details: false
execution:
timeout: 120s
grader-concurrency: 5
The skill does not need to read this file directly. The CLI resolves it.
Tool permissions
The CLI enforces strict tool isolation inside test workspaces using --permission-mode dontAsk. Spec files can override the allowed/disallowed lists via allowed-tools / allowed-tools-extra / disallowed-tools / disallowed-tools-extra frontmatter. The skill never configures permissions itself; the CLI does.
Reference Material
For detailed documentation, consult these files as needed:
references/spec-format.md— complete spec file format reference with examplesreferences/testing-guidelines.md— best practices for writing test cases
04 同仓库更多 Skills
skill-test-troubleshooting
Use when the user wants a failing or flaky skill test fixed, not just inspected. Triggers on "why does X keep failing", "make X pass reliably", "fix the flaky test", "this test keeps timing out", "diagnose the failures from the last run", "fix any failed tests", "/skill-test-troubleshooting", or any request that names a test/skill/run and asks to repair, stabilize, or make-green. Does NOT activate on "run the tests" (that is the skill-unit skill) or on read-only inspection requests like "show the transcript" / "what happened in the last run" (also skill-unit). Does NOT activate on requests to write or add a new test case (that is the skill-test-design skill).
skill-test-design
ALWAYS use this skill when the user mentions writing, designing, creating, or adding test cases for any skill, even if they also describe specific behavior to test. Triggers on "write a test case", "write me a test case", "write test cases", "design tests", "create a spec file", "help me write tests", "add tests", "no tests yet", "/skill-test-design", or any request that involves creating test cases, spec files, or test coverage for a skill. If the user says "write a test case for X that covers Y", this skill handles it, not the skill being tested.
report-card
This skill should be used when the user asks to "summarize grades", "show me the class report", "generate a report card", "how are the students doing", or mentions student grades, class performance, or grade summaries. It reads a students.json file and produces a formatted grade report.
report-card
Use when the user asks for a student progress summary, grade breakdown, or class report. Triggers on "how is X doing in school", "show me the grades", "summarize class performance", "report card for X", "what are my child's grades".
inventory
Use when the user requests an SKU lookup or a warehouse delta report. Triggers on "SKU lookup", "warehouse delta", "WMS variance".
weather
Use when the user asks about temperature or precipitation. Triggers on phrases like "is it raining", "how cold is it", "current temperature".