Anatomy of a SKILL.md: what skillterm actually writes when it generates a skill
A skill in skillterm is not a function call or a plugin binary. It is a Markdown file. Specifically, a SKILL.md file with YAML frontmatter and a markdown body, optionally accompanied by a references/ directory and a scripts/ directory. The format is the public Claude Agent Skills convention, which makes the file portable to any Claude-compatible agent.
This post unpacks what that file actually looks like, what each part is for, and why the choice of a markdown artefact — rather than, say, a JSON schema or a compiled plugin — is the right call for the layer skillterm operates at.
The file you get back
When you run skillterm generate kubectl, the agent writes a file at ~/.skillterm/skills/kubectl/SKILL.md. The file has two parts.
The first part is YAML frontmatter, delimited by --- lines. It carries two required fields:
---
name: kubectl
description: |
Kubernetes CLI for managing clusters, pods, deployments, and services.
Use for k8s operations, container orchestration, and cluster debugging.
---
name is the unique skill identifier — lowercase, hyphens allowed, capped at 64 characters. description is the field skillterm uses to decide when to load the skill: it captures what the tool does, common use cases, and trigger keywords. The docs cap it at 200 characters, which is deliberate; the description is loaded as agent context every time skillterm has to decide whether this skill is relevant, so a short description keeps that decision fast.
Optional frontmatter fields documented in docs/reference/skill-format.md round out the metadata: argument-hint for the example arguments shown during completion, version for semver, author, and tags for categorisation. None are required to make a skill usable; all are helpful for managing skill collections at scale.
The second part of the file is the markdown body — the actual knowledge content the agent and the human will read. The skill-creator meta-skill defines the convention: an overview, a “Common Operations” section with sub-sections per task category, fenced bash code blocks with inline comments, and a “Best Practices” section at the end. The shape is enforced by the meta-skill, which is why generations come out consistent without any post-processing on skillterm’s side.
What lives alongside SKILL.md
A skill is a directory, not just a file. The kubectl/ folder can contain:
kubectl/
├── SKILL.md # Required: main skill file
├── references/ # Optional: additional docs
│ └── api-resources.md
└── scripts/ # Optional: helper scripts
└── get-contexts.sh
references/ is where long-form documentation lives. The format docs are explicit that SKILL.md should stay under 500 lines — large skills slow down completions because the whole file is loaded as context. When you have more content than that, you split it: the SKILL.md stays focused on what every completion needs, and the rest moves into references/. The agent can read the references on demand; the shell hook only needs the SKILL.md.
scripts/ is for helper scripts the skill describes how to use. A kubectl skill might ship a get-contexts.sh that wraps kubectl config get-contexts in a more agent-friendly output format. Whether the script is invoked depends on the skill content; the directory just gives a stable place to put it.
Why markdown, not JSON
A reasonable architecture critique: “Why use a Markdown file when you could use a JSON schema that defines completions structurally?” There are two good answers.
The first answer is the dual audience. skillterm’s VISION.md is explicit that skills are “tool documentation optimized for AI agent consumption” — but the same artefact is read by humans, who write skills by hand and review skills before publishing. A JSON schema serves the parser; markdown serves the parser and the reader. The Vision document explicitly lists “Replace man pages” as a non-goal, which means the SKILL.md format has to coexist with — and be readable next to — man pages. A markdown file does that; a JSON blob does not.
The second answer is portability. The Claude Agent Skills convention is published as a Markdown-with-YAML-frontmatter format. Choosing the same shape means the skills skillterm generates are immediately loadable in any other Claude agent, with no transformation step. The docs/concepts/skills.md document calls this out: skills “follow the Claude Agent Skills format, making them compatible with Claude Code and other Claude-powered tools.” Portability is a first-class design property, and markdown is the format the rest of the ecosystem is settling on.
A third, smaller answer: markdown is the format the LLM is best at producing. Generation quality is higher when the output format is one the model has seen millions of examples of. Markdown is that format; bespoke JSON schemas are not.
How the file gets used
There are two distinct read paths for the SKILL.md.
At Tab time. The shell hook calls skillterm complete <line>. skillterm finds the SKILL.md for the matching command, parses its YAML frontmatter to extract name and description, and uses the markdown body — in particular the command-and-flag patterns it documents — to generate contextual completions. This path has to be fast (the docs target sub-200ms), which is why the SKILL.md file is kept under 500 lines and why heavier reference content lives in references/ rather than in the main file.
At agent time. Any Claude-compatible agent (Claude Code, Codex with the right loader, a custom harness) can load the SKILL.md into its context the same way it loads any other skill. From the agent’s point of view, this is just a knowledge file. The format being shared means the agent does not need a skillterm-specific loader. That matters: it means the durable thing in your ~/.skillterm/skills/ tree has value even if you switch away from skillterm.
What makes a good SKILL.md
skill-creator encodes the quality bar. Three things matter most:
-
A precise description. The
descriptionfield decides when skills get triggered. “Kubernetes CLI for managing clusters, pods, deployments, and services” tells the agent (and skillterm) exactly which contexts this skill belongs in. “kubectl” alone does not. The description is where most generation-time effort actually pays off. -
Real examples. Real bash invocations with realistic flags and inline comments are far more useful than abstract descriptions of what a tool does. The skill-creator meta-skill emphasises this; the docs in
docs/concepts/skills.mdquote it directly. Acurl -O https://example.com/file.zipexample with a comment is worth ten paragraphs of “curl is a tool for transferring data.” -
Focused scope. One skill per tool. Do not bundle
kubectlanddockerandhelminto a “container ecosystem” skill. The lookup at completion time matches one tool to one skill; mixing tools dilutes the relevance signal. The docs recommend “one skill per command or tool” and “don’t combine unrelated functionality” for exactly this reason.
The artefact, not the binary
What this design adds up to: skillterm’s durable output is the SKILL.md, not the binary that wrote it. If the binary were deleted tomorrow and the skill files preserved, the skills would keep working in any Claude agent. The artefact has independent value.
That is the strongest test of whether a tool’s output is well-designed: does it outlive the tool? For skillterm’s SKILL.md files, the answer is yes. They follow a published format, they are human-readable, and they are short enough to inspect by eye. That is what makes a “skill” a useful unit of knowledge — not the magic of who generated it.