Skip to content

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).

~/.letscode/skills/<name>/SKILL.md
~/.agents/skills/<name>/SKILL.md      # cross-tool standard
~/.pi/agent/skills/<name>/SKILL.md    # additional path
~/.claude/skills/<name>/SKILL.md      # additional path
.letscode/skills/<name>/SKILL.md       # walked up from cwd
.agents/skills/<name>/SKILL.md
.pi/skills/<name>/SKILL.md
.claude/skills/<name>/SKILL.md

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

> /skill:code-review do the same for src/auth.py

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

> /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:

  1. Lead with the trigger. Your description should say when the skill applies. The model uses this to decide whether to invoke.
  2. 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.