Add a Skill
Corresponding source
| Extension step | Source |
|---|---|
| Skill discovery and loading | nanobot/agent/skills.py::SkillsLoader |
| Writing Skills into model context | nanobot/agent/context.py::ContextBuilder.build_system_prompt |
| Skill file contract | nanobot/skills/*/SKILL.md, nanobot/skills/README.md |
| Skill commands and Tool discovery | nanobot/command/builtin.py, nanobot/agent/tools/search.py |
| Verification | tests/agent/test_skills_loader.py, tests/command/test_skill_command.py |
Learning objectives
After this lesson, you should be able to:
- explain the permission boundary between a Skill and a Tool;
- create a directory discoverable by
SkillsLoader; - understand workspace override, requirements,
always, and progressive loading; - verify behavior rather than checking only that Markdown exists.
Prerequisites
- Know that a system prompt influences model decisions but grants no new execution permission.
- Understand ContextBuilder composition.
- Write explicit steps, stop conditions, and verification criteria.
1. A Skill is loadable operational guidance
Its core file is:
<workspace>/skills/<skill-name>/SKILL.mdA built-in Skill lives at:
nanobot/skills/<skill-name>/SKILL.mdIt teaches the Agent how to combine existing Tools, when to ask the user, and how to verify a result. It does not execute Python and cannot bypass Tool security boundaries.
2. Discovery and override
SkillsLoader.list_skills():
- scans workspace Skills;
- scans built-in Skills;
- lets a same-named workspace Skill replace the built-in Skill;
- removes names in
disabledSkills; - filters Skills whose requirements are unmet by default.
The runtime identity of a Skill comes from its directory name. Frontmatter name is useful for description and compatibility, but the current loader locates <directory-name>/SKILL.md.
flowchart TD A["workspace/skills"] --> C["Merge by directory name"] B["nanobot/skills"] --> C C --> D["Workspace name wins"] D --> E["Filter disabledSkills"] E --> F["Filter requirements"] F --> G["ContextBuilder"]
3. Minimal frontmatter
---
name: explain-session
description: Guide the agent to inspect and explain one nanobot Session safely.
metadata:
nanobot:
requires:
bins:
- rg
env: []
---The body should state at least:
- when to use it;
- required inputs;
- procedure order;
- which steps are read-only;
- when user input is mandatory;
- completion criteria;
- how failure converges.
4. Current requirements boundary
SkillsLoader._check_requirements() currently checks:
requires.binswithshutil.which();requires.envfor environment-variable presence.
Built-in Skill metadata may contain fields such as os, but the current _check_requirements() does not prove OS filtering. A claim about OS gating requires implementation and tests, not only frontmatter.
get_skill_requirements() returns:
- bins/env;
- missing_bins/missing_env.
5. always and progressive loading
ContextBuilder handles two groups:
Always-on Skill
When top-level always: true or metadata.nanobot.always is true and requirements pass, get_always_skills() places the full body directly in the system prompt.
Ordinary Skill
build_skills_summary() includes only:
- name;
- description;
- path;
- availability.
The Agent reads the complete SKILL.md through filesystem capability when needed. This is progressive loading.
alwaysconsumes context on every TurnReserve it for short rules that must apply every time. Domain workflows, rare platform operations, and long tutorials should load on demand.
6. Writing structure
An Agent-facing Skill is usually more explicit than a long human tutorial:
# Skill name
## Use when
## Do not use when
## Inputs
## Procedure
## Safety boundaries
## Verification
## Failure handlingImportant principles:
- make steps observable; do not say only “handle appropriately”;
- mark read-only and mutating work;
- identify actions requiring approval;
- never encourage bypassing a Tool error;
- use placeholders, never real credentials;
- specify result verification, not only execution.
7. When to include a script
A Skill directory can contain scripts/, references/, and related resources. A script is appropriate when:
- a mechanical operation is complex and easy to mistype;
- repeated execution should remain consistent;
- a small, testable helper can express it.
The existing Tools still execute the script, so workspace, Shell, permissions, and dependency constraints remain. A Skill does not make bundled code trusted.
8. Verify a Skill
Structural verification
SKILL.mdis readable;- YAML parses;
- description is nonempty and distinguishes the trigger;
- unmet requirements report unavailable correctly;
- a same-named workspace Skill overrides as expected.
Behavioral verification
Use a fixed task and observe:
- whether the Skill appears in the system-prompt summary;
- whether the model reads the full Skill in an applicable case;
- whether it uses the correct Tools in the stated order;
- whether it pauses before a write to ask;
- whether it verifies the result;
- whether it stays inactive in an inapplicable case.
Model behavior is stochastic. Separate deterministic loader/context tests from a real-model smoke test.
9. Source and tests
Source:
nanobot/agent/skills.py::SkillsLoadernanobot/agent/context.py::ContextBuilder.build_system_promptnanobot/skills/README.mdnanobot/skills/*/SKILL.md
Tests:
tests/agent/test_skills_loader.pytests/command/test_skill_command.py- disabled-Skill cases in
tests/tools/test_search_tools.py
10. Minimum modification scope
A workspace Skill normally needs only:
workspace/
└── skills/
└── <name>/
└── SKILL.mdOnly a Skill shipped with nanobot belongs under nanobot/skills, with wheel includes, built-in documentation, and adjacent tests checked.
Self-check
- Why can a Skill not grant new filesystem-read permission?
- Which wins when workspace and built-in Skills share a name?
- Does the current loader enforce
metadata.os? - Which kind of Skill should not use
always: true?
Next: Integrate MCP