hezaohezao/skill-authoring
Author SKILL.md: frontmatter, structure, writing principles.
npx skills add https://github.com/HezaoHezao/poirot --skill skill-authoring
A SKILL.md can live in two places:
poirot/backend/agents/skill/builtin_skills/<category>/<name>/SKILL.md— committed, shipped with the package. Use write_file + git add.
skills/<name>/SKILL.md — personal, gitignored. Created via/skill install <path> or by writing directly.
This skill covers authoring for both, with emphasis on builtin skills.
builtin_skills/Source of truth: poirot/backend/agents/skill/parser.py::parse_skill_file.
Hard requirements:
--- as the first bytes (no leading blank line).\n---\n before the body.name field present (lowercase, hyphens).description field present.Peer-matched shape:
---
name: my-skill-name # lowercase, hyphens
description: Use when <trigger>. <one-line behavior>.
allowed-tools: # Poirot tools this skill may invoke
- bash
- read_file
- write_file
- list_dir
- str_replace
- web_search
- browse_page
- present_files
- read_snapshot
enabled: true
related-skills: [other-skill] # optional cross-references
license: MIT # recommended for contributed skills
author: <human contributor or source attribution>
---
allowed-tools / enabled / related-skills / license / author are NOT
enforced by the parser (it reads name/description/allowed-tools/enabled), but
every peer has them — omit and your skill sticks out.
description ≤ 60 characters, one sentence, ends with a period. State the
capability, not the implementation. No marketing words ("powerful",
"comprehensive", "seamless"). Don't repeat the skill name.
Verify:
import re, pathlib
m = re.search(r'^description: (.*)$',
pathlib.Path('builtin_skills/<cat>/<name>/SKILL.md').read_text(),
re.MULTILINE)
assert len(m.group(1)) <= 60, len(m.group(1))
references/*.md and reference them fromSKILL.md.
A skill exists to make the agent's process more predictable. Predictability
does not mean identical output every run; it means the agent reliably
follows the same useful discipline.
when this skill loads? If a line does not change behavior, cut it.
description every turn. Keep descriptions focused on trigger classes and the
skill's distinctive behavior. Put details in the body or linked references.
SKILL.md; putbranch-specific or bulky reference material in references/, templates/,
or scripts/ and point to it only when needed.
agent knows it is done. Good criteria are checkable: "every modified file
accounted for" beats "summarize changes."
across the file.
knows — "tight loop," "tracer bullet," "root cause," "regression test" —
over long repeated explanations.
If a sentence doesn't change agent behavior vs the default, delete it.
that step's completion criterion.
Common quality failures:
Tools referenced in SKILL.md prose must be native Poirot tools (listed in
allowed-tools) or MCP servers the skill explicitly expects. Do NOT name shell
utilities the agent already has wrapped:
grep → bash (run grep via bash)cat/head/tail → read_filesed/awk → str_replacefind/ls → list_dir# <Title>
## Overview
One or two paragraphs: what and why.
## When to Use
- Bulleted triggers
- "Don't use for:" counter-triggers
## <Topic sections specific to the skill>
- Quick-reference tables are common
- Code blocks with exact commands
## Common Pitfalls
Numbered list of mistakes and their fixes.
## Verification Checklist
- [ ] Checkbox list of post-action verifications
Not every section is mandatory, but Overview + When to Use + actionable
body + pitfalls are the minimum.
builtin_skills/<category>/<skill-name>/SKILL.md # builtin
skills/<skill-name>/SKILL.md # user-local
Builtin categories: core, research, software-development, creative,
productivity. Pick the closest existing category. Don't invent new top-level
categories casually.
list_dir("poirot/backend/agents/skill/builtin_skills/<category>/")
Read 2-3 peer SKILL.md files to match tone and structure.
write_file to builtin_skills/<category>/<name>/SKILL.md. import yaml, re, pathlib
content = pathlib.Path("builtin_skills/<category>/<name>/SKILL.md").read_text()
assert content.startswith("---")
m = re.search(r'\n---\s*\n', content[3:])
fm = yaml.safe_load(content[3:m.start()+3])
assert "name" in fm and "description" in fm
assert len(fm["description"]) <= 60
/skill list willnot see the new skill until restart. This is expected.
related-skills is documentation-only (parser ignores it). You can reference
any skill, but prefer referencing only builtin skills from builtin skills —
user-local skills won't resolve for other users.
str_replace on the SKILL.md.write_file the whole SKILL.md.write_file tobuiltin_skills/<category>/<name>/references/<file>.md,
templates/<file>, or scripts/<file>.
---. Parser requires content.startswith("---");any leading blank line or BOM fails.
not the one task. "Use when debugging X" > "Debug X".
attention. Trim ruthlessly.
grep/cat/sed/find → use Poirot toolnames (bash/read_file/str_replace/list_dir). Otherwise the model
hallucinates calls to non-existent tools.
list_dir thecategory and open 2-3 peers. Prefer extending an existing skill to creating
a narrow sibling.
loader initializes at startup. Verify in a fresh session.
over time. When adding a rule, remove the old wording it replaces.
rarely change model behavior. Replace with a checkable completion criterion.
builtin_skills/<category>/<name>/SKILL.md (or skills/<name>/)---, closes with \n---\nname, description, allowed-tools, enabled presentlicense, author present (attribution)# Title → ## Overview → ## When to Use → body → ## Pitfalls → ## Verificationgit add && git commit completedTake hezaohezao/skill-authoring from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.