Break Your SKILL.md Monolith with @include and @delegate
If you've been building AI agents with Claude Code, you've probably hit this wall: your SKILL.md starts as a clean 50-line file, and three months later it's a 400-line monolith that nobody wants to touch.
There's a better way.
The Problem with Monolithic Prompts
As your Claude Code projects grow, a single SKILL.md tends to accumulate everything:
- Role definitions
- Domain knowledge (account codes, product catalogs, compliance rules)
- Output format specs
- Conditional logic for edge cases
- Changelog notes
The result is a file that's hard to read, hard to test, and impossible to share across skills. When your "accounting assistant" and your "invoice generator" both need the same tax code definitions, you end up copy-pasting — and then maintaining two diverging copies.
The Solution: @include and @delegate
dotmd-parser introduces two directives that bring modular design to prompt engineering.
@include — inline composition
@include path/to/file.md
At runtime, the contents of the referenced file are inserted inline — exactly like #include in C or import in Python. The final prompt seen by the model is the fully expanded text.
Example:
# Invoice Generator
@include shared/role-accountant.md
@include shared/tax-codes.md
@include shared/output-format.md
## Task
Generate an invoice for the following items: {{items}}
Each shared file stays focused on one thing. role-accountant.md defines who the model is. tax-codes.md is your master reference for tax rates. output-format.md specifies the JSON schema you expect back.
When tax rates change, you update one file — and every skill that @includes it picks up the change automatically.
@delegate — agent composition
@delegate path/to/agent.md
@delegate path/to/agent.md --parallel
Where @include expands content, @delegate hands off execution to a sub-agent. The referenced file is not expanded inline — it's a separate agent that runs independently (or in parallel with --parallel).
Example:
# Document Processing Pipeline
@include shared/role-orchestrator.md
## Steps
1. Extract text from uploaded document
@delegate agents/ocr-extractor.md
2. Classify document type
@delegate agents/doc-classifier.md --parallel
3. Route to appropriate handler
@delegate agents/routing-agent.md
This is how you build multi-agent workflows without everything living in one file.
A Real-World Example: Customer Support Skill
Here's how a customer support skill looks when broken into modules:
support/
├── SKILL.md # Root — orchestrates everything
├── shared/
│ ├── role.md # "You are a support specialist..."
│ ├── product-catalog.md # Product names, SKUs, pricing
│ ├── escalation-rules.md # When to escalate to human
│ └── response-format.md # JSON output schema
└── agents/
├── intent-classifier.md # Classifies ticket intent
└── kb-search.md # Searches knowledge base
SKILL.md ties it together:
# Customer Support Skill
@include shared/role.md
@include shared/product-catalog.md
@include shared/escalation-rules.md
@include shared/response-format.md
## Workflow
Classify the customer intent first:
@delegate agents/intent-classifier.md
Then search for relevant articles:
@delegate agents/kb-search.md --parallel
Clean. Readable. Each file has a single responsibility.
When It Gets Complex: dotmd-parser
Once you have 10+ files referencing each other, questions start coming up:
- "I want to update
shared/escalation-rules.md— what else will break?" - "Is there a circular reference somewhere in this skill tree?"
- "What
{{variables}}are left unresolved after expansion?"
This is where dotmd-parser comes in.
pip install dotmd-parser
Build the dependency graph
from dotmd_parser import build_graph, summary
graph = build_graph("./support/")
print(summary(graph))
# Nodes: 7 (skill:1, shared:4, agent:2)
# Edges: 6 (include:4, delegate:2)
# Warnings: 0
Find what breaks before you edit
from dotmd_parser import build_graph, dependents_of
graph = build_graph("./support/")
affected = dependents_of(graph, "/abs/path/to/shared/product-catalog.md")
# → ["/abs/path/to/support/SKILL.md"]
One call tells you the full blast radius of your change.
Detect circular references
graph = build_graph("./bad-skill/")
# Warnings: 1
# [CIRCULAR] 循環参照: bad-skill/SKILL.md -> b.md -> bad-skill/SKILL.md
Circular references are caught at parse time — not when your agent starts looping at runtime.
Expand @include to final text
from dotmd_parser import resolve
result = resolve("./support/SKILL.md", variables={"items": "laptop, mouse"})
print(result["content"]) # Fully expanded prompt
print(result["placeholders"]) # Any unresolved {{variables}}
The @include Convention
There's no official spec for @include in Claude Code — this is a convention we've been building on top of SKILL.md. The format is intentionally simple:
@include path/to/file.md
No quotes, no parentheses, no extra syntax. Just a path relative to the current file.
Getting Started Today
- Pick one skill that's grown too large — your most painful SKILL.md
- Identify reusable chunks — role definitions, domain knowledge, output formats
- Move them to shared/ — one file per responsibility
- Replace with @include — update your SKILL.md to reference them
- Add dotmd-parser — install it to validate your structure
You'll immediately notice:
- Changes propagate — update a shared file, all skills get the fix
- Skills become composable — mix and match modules for new capabilities
- Testing gets easier — you can test individual modules in isolation
- Collaboration improves — different team members can own different modules
This isn't just about cleaner files — it's about building skills that scale with your team and your ambitions.




