Skills¶
Skills are reusable instructions you want the agent to load on demand. They're plain markdown files with YAML frontmatter — the agentskills.io format. Skills written for other tools that use the same format (see Related projects) load unchanged.
Format¶
---
name: code-review
description: Use when reviewing code for correctness, style, or security.
---
# Code Review
When invoked:
1. Read the changed files.
2. Look for off-by-one errors, race conditions, missing input validation.
3. Suggest improvements with concrete examples.
Two frontmatter fields are required: name and description. The body is whatever instructions you want the model to follow.
Discovery paths¶
letscode scans these paths at startup, in order. Project paths override global ones; later paths shadow earlier ones with identical names (and identical content is silent — only differing content prints a warning).
Cross-tool reuse
Skills written for other agentskills.io-format tools load unchanged. If you already maintain a skills directory for one of them, letscode picks it up automatically. See Related projects.
Invocation¶
There are two ways to invoke a skill — and both end up at the same place.
As a slash command¶
The slash command prepends the skill body to your message and sends the combined text to the LLM.
As a tool (auto-wrapped)¶
Every discovered skill is also synthesised as a tool named skill_<normalised-name>. Hyphens become underscores; non-identifier characters become underscores.
write-a-prd → skill_write_a_prd
my.skill.v2 → skill_my_skill_v2
already_valid_123 → skill_already_valid_123
The model invokes via native tool-calling:
> Let's build a TODO manager. First write a PRD.
assistant:
> skill_write_a_prd({"message": "a TODO manager with Flask and htmx"})
Both invocation paths produce the same content. The model picks one based on its tool-calling behavior — most modern instruction-tuned models prefer the tool form because it's their primary "do this named thing" mechanism.
Why both paths?
The slash command is the human-typed path; the tool wrapping is the model-invoked path. Tool-wrapping was chosen over alternatives like parsing assistant text for /skill:<name> patterns because it generalises across OpenAI-compatible providers without relying on the model emitting a specific text shape.
Listing skills¶
Lists every discovered skill with its description.
Writing a skill¶
A good skill is a focused playbook for one task. Keep the body concise — the entire body is prepended to the user message, so paying tokens for verbose instructions every invocation adds up.
---
name: api-endpoint-from-spec
description: Implement an HTTP endpoint from an OpenAPI spec snippet. Use when given a path + method + spec.
---
# Add an API endpoint
When invoked:
1. Read the OpenAPI spec file (usually `openapi.yml` at repo root).
2. Find the operation matching the requested path + method.
3. Implement the route handler in `src/<project>/api/<resource>.py`.
4. Add a request-validation schema using Pydantic.
5. Write a happy-path test and at least one error-path test.
Keep handlers thin — push business logic to a service module.
Two patterns worth following:
- Lead with the trigger. Your
descriptionshould say when the skill applies. The model uses this to decide whether to invoke. - Number the steps. Short numbered lists are easier for the model to follow than prose.
Companion files¶
The Skill.files mechanism (referencing templates, examples, snippets co-located with SKILL.md) is not implemented yet. If a skill author needs it, it's a candidate item for a future release.