app / skill-pilot
!

Not installable via adompkg

This app has no published release. adompkg install kyle/skill-pilot will not work until a maintainer publishes a tarball with install.sh and uninstall.sh.

See the publishing docs for the package.json schema and tarball layout required to ship this app.

skill-pilot

Stops Claude Code from missing skills. Two halves working together: a lint that audits every SKILL.md for description quality, and a router hook that nudges Claude on every prompt by name-matching the prompt against your skill index.

The problem this solves

Claude Code surfaces every installed skill to the model via its description frontmatter — capped at 1,536 characters per skill, with a global ~8,000-char budget across all skills. With 100+ skills, two things go wrong:

  1. Buried trigger phrases. A description that puts keywords past the first 200 characters effectively hides them. Claude never sees "use this for Mouser searches" if the prose only mentions Mouser at the end.
  2. Soft signal. Even when Claude does see a relevant description, picking it from 100+ candidates is fuzzy. A skill that should fire ~always (like tool-publisher for any "publish" prompt) often gets passed over.

skill-pilot fixes both.

How the router works

skill-pilot on registers a UserPromptSubmit hook in ~/.claude/settings.json. Every time you hit Enter, the hook runs skill-pilot match with your prompt as stdin. The matcher:

  1. Loads (or builds + caches for 5 min) an in-memory keyword index from every SKILL.md in ~/.claude/skills/ and ~/project/gallia/skills/. Triggers come from Trigger words: blocks, comma-separated noun phrases in the description, and proper-noun-ish words like Mouser / KiCad / JLCPCB.

  2. Word-boundary matches the prompt against the index. Single-word triggers require word boundaries (so mouser matches mouser, never mouserate); multi-word phrases use substring match.

  3. Emits one short <system-reminder> listing the top-5 matching skill names, e.g.:

    <system-reminder>skill-pilot: matching skills for this prompt — adom-mouser, adom-parts-search, electrical-engineering. Invoke if relevant; ignore if not.</system-reminder>

  4. Logs to ~/.adom/skill-pilot/matches.log (timestamp, skills, prompt prefix) so you can audit precision/recall.

Claude sees the reminder by name — much louder signal than ambient description matching against 100+ skills.

How the lint works

skill-pilot lint scans every SKILL.md and scores 0–100 with deductions for:

Issue Penalty
Missing name / description −30 / −50
description + when_to_use over 1,536 chars (gets truncated) −20
No detectable trigger phrases −30
Triggers exist but none in first 200 chars −25
Description shorter than 60 chars −15

Output is human-readable by default, --json for machine consumption. Use it before skill-pilot on to find skills the matcher won't be able to see, then front-load their trigger words.

Install (Tier A — auto-installed)

skill-pilot is Tier A: every Adom container gets it automatically when gallia/install.mjs runs (which happens on every gallia auto-update, ~30 min cadence). The router hook is enabled by default during install. New containers will see a one-time notice in the install output explaining what was just turned on.

If you need to (re)install manually:

curl -fsSL https://wiki-ufypy5dpx93o.adom.cloud/static/apps/skill-pilot/skill-pilot \
  -o /tmp/skill-pilot && chmod +x /tmp/skill-pilot \
  && sudo install -m 0755 /tmp/skill-pilot /usr/local/bin/skill-pilot \
  && skill-pilot install \
  && skill-pilot on

Gallia's 30-min wiki-refresh hook keeps the installed binary in sync with the wiki's pub_version.

Don't want it?

skill-pilot off                   # remove hook from settings.json
export ADOM_SKILL_PILOT=0         # or pause per-shell, no settings change

Both off-switches survive future gallia updates — install.mjs runs skill-pilot on only on the initial install (on is idempotent and won't re-enable a hook you've explicitly removed).

CLI

# Lint (do this BEFORE turning the router on)
skill-pilot lint                    # human-readable report
skill-pilot lint --json             # machine output
skill-pilot lint --dir <path>       # custom skill dirs

# Router hook
skill-pilot on                      # register UserPromptSubmit hook
skill-pilot off                     # remove hook
skill-pilot status                  # wired? env-var state? last 10 matches

# Dry-run / debug
skill-pilot match --prompt "find a 10k resistor on mouser"
skill-pilot match --prompt "..." --no-log

# Historical analysis
skill-pilot analyze                 # top-firing skills, false-positive
                                    #   candidates, co-occurrence, zero-matches
skill-pilot analyze --json          # machine output
skill-pilot analyze --log <path>    # custom log path

Auditing the router with skill-pilot analyze

Every prompt that flows through the hook is appended to
~/.adom/skill-pilot/matches.log — including prompts that matched
zero skills (so you can see what the router is missing, not just
what it found). Format is one line per prompt:

<unix_timestamp>\t<comma_separated_skills>\t<first_120_chars_of_prompt>

skill-pilot analyze reads this log and surfaces:

Section What it tells you
Activity Total prompts logged, time buckets (1h / 24h / 7d), zero-match %, avg matches per matched prompt
Top firing skills Top-25 skills by count + percent of all prompts. flags skills firing on ≥30% (false-positive candidates). · flags skills firing on <2% (rare or niche)
False-positive candidates Skills that fire on ≥30% of prompts — almost certainly matching too aggressively. Narrow their triggers
Top co-occurring pairs Skills that always fire together. Often a sign that one matcher is dragging others along (shared trigger or noise like <task-notification> blobs)
Recent zero-match prompts Recent prompts the router didn't route at all — gaps where you might want to widen a skill's triggers, or accept that no skill should fire there

How to read the output:

  • Avg matches per matched prompt > 3.5 — too liberal. Either narrow triggers, or lower the per-prompt cap (currently 5).
  • Any skill firing on >30% of prompts — almost always a false positive on a common word. Edit its description's trigger phrases to be more specific.
  • A pair of skills with co-occurrence equal to each individual count — they're firing as a block (either correctly because they're always relevant together, or incorrectly because they share a common trigger). Investigate the prompts that fired both.
  • Zero-match prompts that look obviously routable — an opportunity to add a missing trigger phrase to a skill, or write a new skill.

Privacy: the log only stores the first 120 characters of each prompt. Tabs and newlines are flattened. The log lives at ~/.adom/skill-pilot/matches.log — local to your container, never uploaded.

Recommended cadence: run skill-pilot analyze after a week of real use. The first few hours of a fresh log are dominated by whatever conversational topic happens to be active and won't reflect steady-state behavior.

Recommended rollout

  1. Lint first. skill-pilot lint on a fresh box almost always surfaces 5–15 skills with buried triggers. Front-load them. (Score-100 skills tend to have a Trigger words: a, b, c, ... line in the first 200 chars of the description.)
  2. Then enable. Run skill-pilot on. Active from the very next prompt — no Claude Code restart needed. Every session on the same container picks it up on its next message.
  3. Verify in the log, not the UI. The Claude Code VS Code extension renders hook output inconsistently — sometimes as a collapsible ▶ UserPromptSubmit hook success disclosure above your message, sometimes not visible at all. The hook itself fires reliably regardless. Trust these instead:
    • skill-pilot status — last 10 matches inline
    • tail -f ~/.adom/skill-pilot/matches.log — streams every match in real time
  4. Audit weekly. Watch the log for skills firing on >30% of prompts — those are false-positive candidates whose triggers need narrowing.

Three off-switches

The router is opt-in and easy to kill. In order of scope:

Layer Command Effect
Per-shell export ADOM_SKILL_PILOT=0 matcher exits silently for that shell
Per-machine skill-pilot off removes hook entry from ~/.claude/settings.json (backup at .skill-pilot.bak)
Manual edit ~/.claude/settings.json restore from .skill-pilot.bak if anything went sideways

Plus the hook fails closed: if the binary errors or panics, it emits nothing and exits 0. Claude Code is never blocked by skill-pilot.

Performance

  • Hook overhead: ~5–15 ms per prompt (read cached index, word-boundary scan, write log line).
  • Injection: capped at 5 skills, ~200 chars total — invisible against the prompt itself.
  • Index cache: ~/.adom/skill-pilot/index.json, rebuilt every 5 minutes.

Source

Source lives in the gallia monorepo at gallia/skill-pilot/ (adom-inc/gallia, private). Build with cargo build --release from that directory. The compiled binary is published to this wiki page as a docker_binary asset; users install via the install_hint above (Tier B — on-demand fetch from the wiki, no GitHub credentials needed).

Related

  • skill-creator — Anthropic's official skill-authoring skill; use it to write new skills, then run skill-pilot lint to verify discoverability before shipping.
  • tool-publisher — for publishing CLIs and skills to the wiki (this very page).
  • adom-wiki — wiki search / publish CLI.
  • skill-pilot-build — sibling skill for cutting new releases.