Writing Custom Skills
Published May 22, 2026 · Last updated May 22, 2026 · 4 min read
Overview
Agents come with general-purpose knowledge, but they don't know your codebase. Custom skills let you teach them. A skill is a directory in your repository that contains instructions, context, and helper files — anything an agent needs to work confidently in your specific environment.
Common uses include:
- Step-by-step database migration procedures
- Login and authentication flows for staging environments
- Deployment checklists and runbook references
- Log analysis scripts and trace investigation procedures
How Skills Work
Skills live in .obvious/skills/ at the root of your repository. Each skill is a subdirectory containing a SKILL.md file that defines the skill and its instructions.
When an agent encounters a task that matches a skill — either by keyword or by direct request — it loads the skill to gain that context. The skill's instructions become part of the agent's working knowledge for that task.
Creating a Skill
Directory Structure
Each skill is a subdirectory of .obvious/skills/. At minimum, it needs a SKILL.md file:
.obvious/skills/
└── db-migrations/
├── SKILL.md
└── SKILL_CORE.md # optional
You can have multiple skills:
.obvious/skills/
├── db-migrations/
│ ├── SKILL.md
│ └── SKILL_CORE.md
├── local-testing/
│ └── SKILL.md
└── login/
└── SKILL.md
SKILL.md — The Skill Definition
SKILL.md is required. It must begin with YAML frontmatter that describes the skill, followed by the actual instructions in markdown.
---
name: Database Migrations
description: Procedures for creating and running database migrations
triggers:
- migration
- schema change
- database
---
## Migration Workflow
1. Create a new migration file in `db/migrations/` using the naming convention `YYYYMMDD_description.sql`
2. Run the migration locally with `bun run db:migrate` before committing
3. Verify the migration applied cleanly with `bun run db:status`
4. Never modify an existing migration file — create a new one instead
Frontmatter fields:
| Field | Required | Description |
|---|---|---|
name | Yes | Human-readable skill name displayed in the agent's available skills list |
description | Yes | A short description of what this skill covers |
triggers | No | Keywords that hint to the agent when it should consider loading this skill |
The body of SKILL.md is plain markdown. Write it as you would write instructions for a new engineer: concrete steps, commands to run, things to watch out for.
SKILL_CORE.md — Persistent Context
SKILL_CORE.md is optional. When present, its content is injected directly into the agent's preamble every turn — it stays in working memory for the entire task, not just when explicitly referenced.
Use SKILL_CORE.md for small pieces of information the agent must always have available:
- Staging credentials or environment variables
- Invariants that must never be violated
- Key commands the agent will use repeatedly
- A short reference table the agent needs to consult often
## Staging Credentials
- URL: https://staging.example.com
- Admin user: admin@example.com
- Password: stored in 1Password under \"Staging Admin\"
## Key Commands
| Task | Command |
| --- | --- |
| Start dev server | `bun run dev` |
| Run tests | `bun test` |
| Apply migrations | `bun run db:migrate` |
Keep SKILL_CORE.md concise. Its content is loaded every turn for the duration of the task, which consumes context window space. Reserve it for genuinely critical reference material. Put longer procedural instructions in SKILL.md instead, where they're loaded once on demand.
Supplementary Files
Skills can include additional files alongside SKILL.md — helper scripts, templates, or reference data that the agent can read and execute.
.obvious/skills/
└── logs-braintrust/
├── SKILL.md
├── SKILL_CORE.md
└── scripts/
├── fetch-trace.sh
├── analyze-trace.py
└── generate-report.py
Your SKILL.md can reference these files directly:
## Analyzing a Trace
Run the fetch script to pull the trace from Braintrust:
```bash
bash .obvious/skills/logs-braintrust/scripts/fetch-trace.sh <trace_id>
Then analyze it with:
python3 .obvious/skills/logs-braintrust/scripts/analyze-trace.py trace.json
Agents that load the skill can read and execute these files as part of their workflow.
## How Agents Discover and Load Skills
### Discovery from the Default Branch Only
**This is a security constraint, not a limitation.** Skills are discovered by reading your repository's default branch — agents use `git ls-tree` and `git show` against `origin/<defaultBranch>`. Skills on unmerged PR branches are never loaded.
This means a pull request cannot inject skill instructions into an agent reviewing that same PR. Skill content must be reviewed and merged before any agent can use it.
### The `repo-` Namespace Prefix
When your skills appear in an agent's available skills list, they're automatically prefixed with `repo-` to avoid naming collisions with built-in skills. A skill directory named `db-migrations` appears as `repo-db-migrations`.
repo-db-migrations [repo] (v1.0.0)
Procedures for creating and running database migrations
### Loading a Skill
Agents load skills with `skills_operations`:
```javascript
skills_operations({ operation: \"load\", skillName: \"repo-db-migrations\" })
Always use the repo- prefixed name when loading.
15-Minute Cache
Discovered skills are cached per sandbox for up to 15 minutes. After you merge a new skill or update an existing one on the default branch, agents may take up to 15 minutes to see the change.
Constraints
| Rule | Detail |
|---|---|
| Flat directories only | Skill directories must be directly under .obvious/skills/ — no nesting. .obvious/skills/db-migrations/ ✅ · .obvious/skills/db/migrations/ ❌ |
| Naming format | Directory names must match ^[a-z0-9][a-z0-9_-]*$ — lowercase letters, digits, hyphens, underscores. Must start with a letter or digit. |
| Loose files are ignored | A .md file placed directly in .obvious/skills/ (not inside a subdirectory) will not be discovered. Every skill needs its own directory. |
Complete Example
Here's a complete login skill that gives agents step-by-step instructions for accessing a staging environment:
.obvious/skills/login/SKILL_CORE.md
## Staging Environment
- URL: https://staging.example.com
- Default test account: testuser@example.com / password in 1Password \"Staging Test User\"
.obvious/skills/login/SKILL.md
---
name: Staging Login
description: How to log in to the staging environment for manual verification
triggers:
- login
- staging
- authenticate
---
## Logging In to Staging
1. Navigate to https://staging.example.com
2. Use the test account credentials from SKILL_CORE.md
3. If the account is locked, use the admin panel at /admin/users to unlock it
4. MFA is disabled on staging — you will not be prompted for a second factor
## Creating a Fresh Test Account
If you need a clean account without existing data:
1. Go to /admin/users/new
2. Set email to `test+<timestamp>@example.com`
3. Set role to \"member\"
4. Skip email verification — staging does not send real emails
An agent working on a staging verification task loads this skill with:
skills_operations({ operation: \"load\", skillName: \"repo-login\" })
And immediately has access to the login procedure and credentials reference for the duration of the task.