Skills¶
Skills are reusable markdown documents that inject rich, prose-style instructions into prompts. Where YAML prompts are short and parameterised, skills are long-form — decision frameworks, best practice guides, domain knowledge, step-by-step processes.
File format¶
A skill file is a .md file with a YAML frontmatter block:
skills/code-review/SKILL.md
---
name: code-review
description: Use when reviewing code for quality, bugs, and security.
tags: [code, review, engineering]
version: "1.0"
---
A thorough code review checks correctness, readability, and security.
## Correctness
Check for off-by-one errors, null pointer risks, and unhandled edge cases.
Verify business logic matches the stated requirements.
## Readability
Names should be self-explanatory. Functions should do one thing.
If you need a comment to explain *what* code does, consider renaming instead.
## Security
Look for injection risks, hardcoded secrets, and improper input validation.
Never trust data from external sources without sanitising first.
Frontmatter fields¶
| Field | Required | Description |
|---|---|---|
name |
Display name. Derived from filename if absent | |
description |
What this skill does / when to use it | |
tags |
List of strings | |
version |
Version string |
Directory layouts¶
Both layouts are supported and can coexist:
Loading skills¶
Via SkillRegistry¶
from promptframe import SkillRegistry
registry = SkillRegistry("skills/")
# List all skills (reads frontmatter only — fast)
registry.list()
# → [{"key": "code-review", "name": "code-review", "description": "...", "tags": [...]}]
# Load a skill by key
skill = registry.get("code-review")
# Load all at once
all_skills = registry.load_all()
# → {"code-review": Skill(...), "summarise": Skill(...)}
Via PromptRegistry¶
from promptframe import PromptRegistry
reg = PromptRegistry("prompts/")
# Load a single skill
skill = reg.load_skill("skills/code-review")
# Get a SkillRegistry rooted at a subdirectory
skill_reg = reg.skill_registry("skills")
Direct file load¶
from promptframe import load_skill_from_path
skill = load_skill_from_path("skills/code-review/SKILL.md")
Working with sections¶
Skills are automatically parsed into sections by ## headings:
skill = registry.get("code-review")
# List all sections
list(skill.sections.keys())
# → ["Correctness", "Readability", "Security"]
# Get a specific section's content
skill.get_section("Security")
# → "Look for injection risks..."
Rendering¶
# Full content (default)
skill.render()
# Filter to specific sections
skill.render(sections=["Correctness", "Security"])
# Without the name heading
skill.render(include_name=False)
Using SkillComponent in the builder¶
SkillComponent drops directly into StructuredPromptBuilder:
from promptframe import (
StructuredPromptBuilder,
SimplePromptComponent,
SkillComponent,
SkillRegistry,
)
registry = SkillRegistry("skills/")
skill = registry.get("code-review")
prompt = (
StructuredPromptBuilder()
>> SimplePromptComponent("You are a senior software engineer.")
>> SkillComponent(skill) # all sections
>> SkillComponent(skill, sections=["Security"]) # one section only
>> SimplePromptComponent("Review this code:\n{code}")
).build({"code": "x = input()"})
Wrapping in XML tags¶
Many LLMs respond better when context is wrapped in semantic tags:
SkillComponent parameters¶
| Parameter | Default | Description |
|---|---|---|
skill |
required | A loaded Skill instance |
sections |
None |
List of section headings to include. None = all |
include_name |
True |
Prepend ## {name} heading |
wrapper |
None |
Format string wrapping the rendered skill. Must contain {skill} |
CLI scaffold¶
promptframe skill init code-review
# Creates: skills/code-review/SKILL.md
promptframe skill init data-analysis --path my_skills/
# Creates: my_skills/data-analysis/SKILL.md
Keep skills focused
A skill file works best when it covers one domain well. Prefer many small, focused skills over one large document — then use sections= filtering to inject only what's relevant to each prompt.