AnswerQA

How do I keep my skill body short and reference long material on demand?

Answer

A skill is a directory, not a single file. Keep SKILL.md under ~500 lines and move long reference into supporting files (reference.md, examples/, scripts/) that the body links to. Claude reads supporting files only when SKILL.md prose tells it to; the body itself stays in context for the rest of the session.

By Kalle Lamminpää Verified May 7, 2026

A skill is a directory, not a single file: SKILL.md is what stays in context after invocation, while everything else in the directory only loads when SKILL.md prose tells the model to read it. The cheap mistake is dumping every example, reference, and config into one 2,000-line SKILL.md and paying for it on every turn.

The directory anatomy

~/.claude/skills/release-notes/
├── SKILL.md               # the body, stays in context once invoked
├── reference.md           # detailed reference, read only when needed
├── examples/
│   ├── major-release.md
│   ├── minor-release.md
│   └── patch-release.md
└── scripts/
    └── collect-prs.sh     # called via `!` injection or a Bash tool

The directory name (release-notes) becomes the slash-command (/release-notes). Anything in the directory is available; what actually gets loaded depends on whether SKILL.md tells the model to read it.

Body keeps the workflow; supporting files keep the long material

A SKILL.md body that holds up. Save this as ~/.claude/skills/release-notes/SKILL.md:

---
description: Generate release notes from the merged PRs since the last tag.
when_to_use: Use when the user asks to draft release notes, create a changelog entry, or summarize what shipped.
---

Generate release notes for the most recent release.

1. Run the PR collection script bundled with this skill:

   !`bash "${CLAUDE_SKILL_DIR}/scripts/collect-prs.sh"`

2. Categorize the merged PRs into Features, Fixes, Breaking changes, and Internal.

3. Pick the matching template:
   - Major release: see [examples/major-release.md](examples/major-release.md)
   - Minor release: see [examples/minor-release.md](examples/minor-release.md)
   - Patch release: see [examples/patch-release.md](examples/patch-release.md)

4. For specific edge cases (security CVEs, deprecation notices), see [reference.md](reference.md).

The body is short (under 30 lines). The model sees enough to pick the right path; it loads examples/major-release.md only when the release is major; it loads reference.md only when an edge case appears. SKILL.md itself stays in context for the rest of the session, but the long material does not.

What stays in context, what does not

WhatWhen it loadsPersists in context?
SKILL.md bodyWhen the skill is invoked (manually or auto)Yes, until session ends or compaction drops it
Supporting files (any path under the skill dir)When SKILL.md prose tells the model to read oneOnce read, they are ordinary context. They stay until compaction frees them; they do not get re-read on every turn.
description + when_to_use frontmatterAlways in context (subject to truncation)Yes
paths: frontmatter (path-scoped skills)Auto-loads when a matching file is readBody persists once loaded; off-path turns do not load

The 1,536-character cap on combined description + when_to_use is the budget that decides whether your skill auto-invokes. Front-load the most distinguishing nouns and verbs so they survive truncation.

Reference scripts and binaries with ${CLAUDE_SKILL_DIR}

Bash injections in SKILL.md run with the project’s working directory, not the skill directory. To reference a bundled script reliably:

!`bash "${CLAUDE_SKILL_DIR}/scripts/collect-prs.sh"`

${CLAUDE_SKILL_DIR} resolves to the absolute path of the skill directory, regardless of cwd. Without it, bash scripts/collect-prs.sh fails the moment Claude is launched in a sibling directory.

Patterns for common shapes

Templated output. Place the templates in examples/<variant>.md. SKILL.md tells the model which to pick based on context. Each template loads only when chosen. The cheapest way to ship a “fill-in-the-blank” workflow.

Long reference docs. Move API specs, regulatory citations, internal runbooks, anything that is reference material rather than instruction, into reference.md (or split files: reference-auth.md, reference-billing.md). SKILL.md links to them by topic; the model reads only the section that applies.

Helper scripts. Put .sh / .py / .js files under scripts/. Call via ! injection (with ${CLAUDE_SKILL_DIR} for the path) or via a Bash(...) line in SKILL.md. The script itself is not loaded into Claude’s context; only its output is.

Dataset or fixture file. A data/ directory with sample inputs that examples reference. Useful for “show me how to handle a CSV like this”.

Footguns

SKILL.md content stays in context for the whole session. Once invoked, the body is part of the conversation until compaction. A 2,000-line SKILL.md is a 2,000-line tax on every turn, and after compaction it re-injects (truncated to the per-skill cap, with the oldest invoked skills evicted past the budget). Cap SKILL.md under 500 lines; move anything longer to supporting files.

Supporting files load on demand only if SKILL.md prose mentions them. Putting a file in the directory does not make Claude read it. The body has to either link to it ([reference.md](reference.md)) or describe when to read it (“for security details, read reference.md”). A supporting file the body does not reference is dead weight.

${CLAUDE_SKILL_DIR} (or its analog) is non-optional for bundled scripts. A skill that runs bash scripts/collect-prs.sh works in your home directory and breaks the moment a teammate runs it from a sibling repo. Use the documented path variable in every bash injection that reaches into the skill directory.

Description + when-to-use truncates at 1,536 characters. With many skills loaded, descriptions get cut from the back. A description that buries the trigger words gets clipped before they reach the model, and your skill stops auto-invoking. Front-load the verbs and nouns that match user prompts; treat the first 200 characters as load-bearing.

Path-scoped skills (paths: frontmatter) only load when a matching file is in context. A path-scoped skill at paths: ["src/auth/**"] does not auto-load until Claude reads a file under src/auth/. If the user’s prompt is “review the auth tests” but Claude reaches for the test file first (which is not under src/auth/), the skill never triggers. Either widen the path glob or accept that the skill needs explicit invocation in those cases.

Subagents do not preload skills you have invoked in the parent. A skill loaded in the parent stays in the parent context; a subagent gets a fresh context with whatever skills are configured for it. If a subagent needs your house style or workflow, the skill has to be available to that subagent specifically (or set as the default for .claude/agents/).

When NOT to split

  • The body is already under 200 lines. Splitting a tight skill into multiple files adds ceremony without saving tokens.
  • The supporting material is needed every time. If reference.md will load on every invocation, just inline it. The split only saves tokens when the load is conditional.
  • You are sharing config across projects. Cross-project sharing belongs in ~/.claude/CLAUDE.md or shared rule files, not in a skill’s supporting directory.
  • The skill is a wrapper around a one-line prompt. “Run the test suite and report failures” does not need an examples/ folder. Reach for multi-file skills when the work has genuine structural variation.

Sources

  • Extend Claude with skills
    Authoritative: skill directory layout (SKILL.md + supporting files), how Claude decides which supporting files to load, the 1,536-character description + when_to_use cap, the persistence-of-SKILL.md-in-context rule, and the `paths:` frontmatter for path-scoped auto-load.
  • Explore the context window
    Why SKILL.md size matters: skill bodies stay in context after invocation and re-inject after compaction (truncated to per-skill cap). Supporting files do not, unless the model reads them mid-task.

Was this helpful?