skill-creator: run_eval.py writes skills to .claude/commands/, so description optimization always measures recall=0%

Status Open
Maintainer reply None cached
Activity 1 comment · opened Aug 19, 2026

Summary

The skill-creator plugin's description-optimization harness (scripts/run_eval.py, driven by scripts/run_loop.py) writes the skill under test to .claude/commands/<name>.md. In current Claude Code builds, files in .claude/commands/ are slash commands (invoked explicitly via /name) and do not appear in the model's autonomously-selectable skills list. As a result, the skill under test can never trigger, every candidate description scores recall=0%, and the optimization loop silently returns the initial description as "best".

Environment

  • Claude Code CLI (macOS, darwin 25.2.0)
  • skill-creator invoked from ~/.claude/skills/skill-creator
  • Eval model: claude-sonnet-5

Repro

  1. Create any skill with a broad description (ours: a "goal-first execution" discipline skill).
  2. Build a 20-query eval set (10 should-trigger / 10 should-not).
  3. Run:

``bash
python3 -m scripts.run_loop \
--eval-set eval_set.json \
--skill-path <skill> \
--model claude-sonnet-5 --max-iterations 5 --verbose
``

  1. Observe every iteration reports precision=100% recall=0% — should-trigger queries never trigger, across all candidate descriptions (each query run 3x).

Isolation

  • claude -p "hello" works fine — CLI itself is healthy.
  • A query that names the skill explicitly ("use the <name> skill for this") still scores as not-triggered under the harness's .claude/commands/ placement.
  • Moving the same content to .claude/skills/<name>/SKILL.md (proper skill placement) and re-running the explicit-name query: the skill triggers (verified with a marker word in the skill body appearing in the response).

So the harness's placement is the bug: it measures triggering of a file that is not in the triggering surface at all.

Suggested fix

In run_single_query(), write the skill as .claude/skills/<clean_name>/SKILL.md with name + description frontmatter (and clean up the directory afterward) instead of a command file in .claude/commands/.

Related caveat (maybe worth documenting rather than fixing)

Even with correct placement, single-shot claude -p runs rarely trigger skills from natural-language queries (0/3 in our test with a realistic debugging query) — consistent with the documented behavior that skills are only consulted for tasks the model can't handle directly, which seems amplified in non-interactive mode. This makes -p-based trigger rates a weak proxy for interactive-session triggering even after the placement fix; the docs for the description-optimization loop may want to note this.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗