Writing principles
Keep it concise
A Skill shares one context window with the system prompt, the conversation history, and the metadata of other Skills. The Agent is already capable enough by default, so write only what it does not know: your team's conventions, steps that easily go wrong, the details of a particular format. For each piece of content, ask: "Without this, would the Agent get it wrong?"
Use short examples rather than long explanations. Write the body in the imperative, stating directly what to do.
Match the detail to how fragile the task is
| Task characteristics | How to write it |
|---|---|
| Several approaches work, depending on context | Prose guidance, giving principles and criteria for judgment |
| There is a recommended approach, with some variation allowed | Pseudocode, or a script with parameters |
| The operations are fragile and order matters strictly | Write a script and put it in scripts/, with as few parameters as possible |
Split content
- Keep
SKILL.mdto the core process and the selection guidance, ideally within 500 lines; - Put details and variants (different platforms, different frameworks) into separate reference documents, and state clearly in the body which file to read in which case;
- Reference documents are referenced from
SKILL.mdone level deep only; do not let reference documents reference one another in chains; - For a reference document longer than 100 lines, put a table of contents at the top;
- Keep each piece of information in one place only, either in
SKILL.mdor in a reference document; - When the body has many topics and each task uses only part of it, switch to the Progressive structure.
Writing process
- Collect a few real usage scenarios: what users will say, and what they expect to get;
- For each scenario, find the reusable parts: code you write again and again goes into
scripts/, material you look up again and again goes into reference documents, templates you use again and again go intoassets/; - Write
SKILL.md: writenameanddescriptionfirst, then the body; - Run the scripts for real and confirm there are no errors;
- Install it once with the installer and confirm it passes preview;
- Use it in real tasks, and iterate on the places where it gets stuck or goes the wrong way.
You can also simply ask StablEdge for help: the built-in skill-creator Skill provides guidance for creating a Skill.
Example 1: a minimal Skill
Only a SKILL.md, suited to one short fixed process.
release-notes/
└── SKILL.md
release-notes/SKILL.md:
---
name: release-notes
description: "Use when organizing release notes: group commits into the three sections New, Improved, and Fixed, and write user-facing notes. Trigger phrases: release notes, version notes, changelog. Not for marketing copy."
version: "1.0"
metadata:
category: "Writing"
tags: "release,docs"
---
# Release notes
1. Read the commit list or change description the user provides; if none is provided, ask for the version and scope first.
2. Drop purely internal changes (refactoring, tests, build scripts) unless they affect users.
3. Output in three sections, ordering each one by impact from high to low:
- **New**: capabilities users can start using;
- **Improved**: existing features that became faster, more stable, or easier to use;
- **Fixed**: problems users noticed that are now fixed.
4. One sentence per item, describing the outcome from the user's point of view, without internal module names.
5. Omit a section when it has no content.
Example 2: a Progressive Skill
A Progressive Skill with reference documents, templates, and scripts, used to generate weekly reports by team conventions.
weekly-report/
├── SKILL.md
├── references/
│ └── style-guide.md
├── assets/
│ └── report-template.md
└── scripts/
└── collect_stats.py
weekly-report/SKILL.md:
---
name: weekly-report
description: "Use when writing a weekly report by team conventions: summarize what was completed this week and the numbers, plus the plan for next week, and output a Markdown report in a consistent format. Trigger phrases: weekly report, work weekly report, this week's summary, weekly report. Not for monthly reports or quarterly summaries."
version: "1.0"
progressive:
always:
- rules
resources:
- path: references/style-guide.md
role: reference
- path: assets/report-template.md
role: template
- path: scripts/collect_stats.py
role: executable
---
# Weekly report
## Rules to follow {#rules}
- A weekly report states facts and numbers only, without summaries padded with adjectives.
- Every item needs an owner and a status (done / in progress / blocked).
- Before output, check against the template whether all sections are present.
## Generation process {#workflow}
1. Copy the template into the Workspace with `skill(action='copy_file', skill_name='weekly-report', path='assets/report-template.md', dest='weekly-report.md')`.
2. When you need statistics, read the `scripts-usage` section and run the statistics script.
3. Fill in the template section by section; see the `style` section for wording requirements.
### Statistics script {#scripts-usage}
Run it with `skill(action='run_script', skill_name='weekly-report', path='scripts/collect_stats.py', argv=['tasks.csv'])`;
the argument is the task export file inside the Workspace. The script writes the statistics as JSON to standard output.
## Wording rules {#style}
The full rules are in `references/style-guide.md`; read them with `skill(action='read_file', skill_name='weekly-report', path='references/style-guide.md')`.
Key points:
- Use the active voice;
- Round numbers to whole numbers, and keep percentages to one decimal place.
weekly-report/scripts/collect_stats.py:
import csv
import json
import sys
def main() -> int:
if len(sys.argv) != 2:
print("Usage: collect_stats.py <tasks.csv>", file=sys.stderr)
return 2
counts: dict[str, int] = {}
with open(sys.argv[1], encoding="utf-8", newline="") as handle:
for row in csv.DictReader(handle):
status = (row.get("status") or "unknown").strip()
counts[status] = counts.get(status, 0) + 1
print(json.dumps({"total": sum(counts.values()), "by_status": counts}, ensure_ascii=False))
return 0
if __name__ == "__main__":
sys.exit(main())
Write references/style-guide.md and assets/report-template.md to match your team's actual conventions. When this Skill is loaded, the cover the Agent sees first is: the hint on reading sections, the bundled file list (with roles), the full text of the rules section, and the automatically generated section index below:
## Sections you can read on demand
- `rules` — Rules to follow
- `workflow` — Generation process
- `scripts-usage` — Statistics script
- `style` — Wording rules
The script reads tasks.csv by a path relative to the Workspace, because the host runs scripts with the current Workspace as the working directory.
Common mistakes
| Symptom | Cause | Fix |
|---|---|---|
| The Skill does not appear in the list, and the diagnostic says frontmatter is missing | The file has a BOM, or the first line is not --- | Save it as UTF-8 without a BOM; the first character must be --- |
| The diagnostic says the frontmatter is not valid YAML | description has no quotes and contains special characters | Add double quotes around description |
| The diagnostic says a field is invalid | name contains characters that are not allowed; or a metadata value is not a string (for example, tags written as a list) | Change it by the Naming rules; write tags as a comma-separated string |
| Your own Skill is ignored | It has the same name as a built-in Skill or another installed Skill | Pick a different name |
| The Skill is never picked | description says only what it does, without trigger phrases | Add the trigger phrases users really say; see Writing a description |
| A Progressive Skill returns the whole body after loading | A heading lacks {#id}, IDs repeat, or always / resources reference something that does not exist | Fix each item by the validation rules |
resources is written but has no effect | The frontmatter has no progressive | Add progressive: {}, or switch to the Progressive structure |
| The Agent cannot find a file mentioned in the body | The body tells the Agent to read it with the general file tools, or gives an absolute path | Write a path relative to the Skill directory, and tell the Agent to read it with the skill tool |
run_script reports a path error | The script is not in the scripts/ directory | Move the script into scripts/ |
run_script reports that arguments or the timeout exceed the limits | More than 64 arguments, a single argument over 8 KB, or a timeout over 300 seconds | Have the script read an input file from the Workspace instead |
| The install preview reports that the Skill is ambiguous | One Skill directory nests another SKILL.md | Give each Skill its own directory, with no nesting |