Lazy Skills
Why this exists
A large skill library is only useful if the model doesn't pay a token tax just for it existing. Every skill's frontmatter is already in context all the time — that's cheap and unavoidable. The expensive part is a skill's body, which only loads once the skill triggers. The risk with a big library isn't the skill count, it's tasks that quietly need two or three skills working together, where only loading the first one leaves you improvising the rest instead of using the tools built for it.
This skill's job is narrow: when a task looks like it needs more than one connected skill, figure out which ones before you start, load only those, and don't touch anything else. If a task only needs one skill, get out of the way — don't add a lookup step to something that was already going to work.
Step 1: Decide if this task needs the graph at all
Most tasks don't. If the task maps cleanly onto one skill you already know
about, just use that skill directly and skip everything below — reading
references/skill_map.json for a single-skill task is pure overhead with
no payoff.
Reach for the graph when the task has any of these shapes:
- It names two or more distinct actions that sound like they'd each be a skill's job ("query this, then format it, then validate it")
- It's the kind of task where you already know the first skill to use, but finishing well obviously requires follow-on steps you don't have a tool for yet
- You're not sure whether a skill you're about to use has companion skills that would improve the result, and the task is substantial enough that it's worth checking
Step 2: Read the dependency graph
Read references/skill_map.json. It's a small, flat map — skill name →
intent, plus which other skills it connects_to and why. Treat the
connection_reason field as the actual signal: it tells you why two
skills are usually used together, so you can judge whether that reasoning
applies to the task in front of you, rather than mechanically pulling in
every connected node.
You can also resolve connections programmatically instead of reading the whole file by eye:
python scripts/check_deps.py <skill-name> [<skill-name> ...]
This walks the graph breadth-first starting from the skill(s) you pass in and prints every connected skill it finds, with its intent. Pass in more than one starting skill if the task already told you it needs several. The script tracks visited nodes as it walks, so a cycle in the graph (skill A connects to B, B connects back to A) can't cause runaway expansion — it just stops re-visiting anything it's already seen. You don't need to reason about cycles yourself; the script handles it.
Step 3: Load connected skills just-in-time, not upfront
Once you know which skills the task needs, don't dump all of their
instructions into context at once "to be safe." Load a connected skill's
SKILL.md only right before you're about to actually use it — the same
way you'd normally invoke any skill. If check_deps.py surfaces five
connected skills but the task only ends up needing two of them, only load
those two.
Finding a skill's SKILL.md follows the normal skill directory layout:
~/.claude/skills/.agents/skills/<skill-name>/SKILL.md (or the project-
local equivalent, if the skill in question is project-scoped rather than
global — check both if you don't find it in one).
If a skill name from the graph doesn't resolve to an actual installed
skill (the map has drifted from what's actually installed), don't stop and
error — just proceed without it and mention the mismatch in your response
so the user can update skill_map.json later. The graph is a hint, not a
hard dependency system.
Step 4: Grow the graph from real usage
references/skill_map.json ships with one small worked example
(db-query → sql-formatter, schema-validator) rather than a
speculative map of every installed skill. That's deliberate: a dependency
you guessed at is often wrong, and a wrong edge is worse than a missing
one because it actively misleads the next task. When you notice yourself
manually chaining two skills together on a real task — the user asked for
X, doing X well meant also reaching for Y — that's the signal to add a
real edge to the graph, not to pre-populate it from skill names alone.
To add one, append an entry under skills following the existing shape:
intent, connects_to, connection_reason. Keep connection_reason
honest and specific — it's what future-you (or future orchestrator runs)
will use to decide whether the edge actually applies to a new task, so
"these are usually used together" is worth less than "Y needs the schema
that X just fetched."
A note on portability across environments
Every skill name in skill_map.json only means something relative to
whichever machine's skill library it was written against. This file
started life pointing at skills installed in one specific environment; if
you install lazy-skills somewhere else — a different machine, a fresh
Claude Code setup, a cloud or remote session — none of those names are
guaranteed to exist there. That's expected, not a bug: Step 3's guidance
above ("if a skill name doesn't resolve... proceed without it and mention
the mismatch") is exactly the mechanism that keeps this from breaking
anything when it happens. You'll just get a mismatch note instead of
useful routing until the graph is adapted.
If you're installing this in a new environment, the fastest fix is to
rewrite skill_map.json against that environment's actual installed
skills rather than assuming the shipped example applies — check what's
really there (list ~/.claude/skills/.agents/skills/ or the project-local
equivalent) before trusting any edge in this file.