Course · Codex · Skills
Author a bank-import procedure as a SKILL.md Codex can run
You’ve decided the six-turn re-narration from the chapter opening is worth writing down. Good instinct - the test for “this should be a Skill” is exactly the one you just applied: you keep re-typing the same multi-step procedure into the chat. That’s the signal. Let’s turn it into a directory Codex can follow without you in the loop.
A Skill is a directory, not a line of config
Section titled “A Skill is a directory, not a line of config”A Skill isn’t a setting you toggle. It’s a folder on disk with one required file inside it: SKILL.md. The folder’s name is the Skill’s name, and the SKILL.md is the entrypoint Codex reads. For a Skill you want available in this project, it lives in the repo under .agents/skills/:
budgetcli/└── .agents/ └── skills/ └── import-bank-csv/ └── SKILL.mdThat path matters and it’s easy to get wrong: Codex reads Skills from .agents/skills/, not from inside .codex/. The .agents/ directory is the cross-tool convention - the same place other agents following the Agent Skills standard look - which is what lets one SKILL.md be reused across tools. Inside a repo, Codex scans .agents/skills from your working directory up to the repository root.
Where the directory lives decides who gets the Skill: .agents/skills/ in the repo means everyone who clones budgetcli (the course’s running example, a small self-hosted personal-finance API in Python and FastAPI) has it; ~/.agents/skills/ in your home directory would make it personal, available across all your projects but yours alone. Since the import procedure references budgetcli’s own fields and conventions, it belongs in the repo, committed and shared like any other project file. Admin and system locations exist too; the full list is on the Codex Skills page.
> mkdir -p .agents/skills/import-bank-csvThe two halves of SKILL.md
Section titled “The two halves of SKILL.md”Every SKILL.md has two parts: YAML frontmatter between --- markers, and a markdown body below it. They do completely different jobs, and keeping that distinction straight is the whole skill of writing Skills:
- Frontmatter is the label on the outside of the box. Codex reads it to decide whether to open the Skill at all. Two fields are required -
nameanddescription- and thedescriptionis what your request gets matched against. - Body is what’s inside the box. It only loads once the Skill actually fires. This is where the procedure lives: the actual steps.
That split is deliberate, and worth pricing once instead of taking on faith. At session start Codex loads the metadata of every Skill it can see - the initial skills list carries each one’s name, description, and path, capped at roughly two percent of the context window. For our file that stub is about 90 estimated tokens at four characters per token. 90 tokens, paid every session, importing or not.
The body below runs to roughly 300 more, and those tokens never enter the window until the Skill is judged relevant. Call it about 380 for the whole file, with over three-quarters of it staying on disk most sessions. Tokens, and only tokens, is the instrument this lesson is pricing. Turns are the next lesson’s job, once the Skill actually fires on a real import.
Here’s the whole thing for our import procedure:
---name: import-bank-csvdescription: >- Import a new bank or credit-card CSV export into budgetcli when the file's column layout isn't one budgetcli already handles. Use when the user wants to onboard a new bank, add support for a statement export, or import transactions from an unfamiliar CSV shape.---
Follow these steps to onboard a new bank's CSV export. Stop and ask theuser if a sample file isn't available - do not guess a layout.
1. Read the first few rows of the sample export. Identify which column holds the transaction date, which holds the amount, and which holds the description/merchant. Note the formats: the date shape, a single signed amount column or separate debit and credit columns, and any thousands separator or currency symbol in the amounts.2. Map those columns onto budgetcli's transaction fields. Normalize every date to ISO 8601 UTC.3. Convert each amount to integer cents without passing through a float - use Decimal or string math, exactly as the hardened parse path in app/importers/csv.py does. A value of "0.29" must become 29, never 28.4. Write a small importer for this bank's layout under app/importers/, following the structure of app/importers/csv.py - the one importer the repo has today.5. Run it against the sample and report a few parsed rows back, showing the raw line and the resulting integer-cents transaction, so the user can confirm the mapping before a full import. Add a test that pins this bank's amount conversion.The description is doing the heavy lifting: it names the situation - “onboard a new bank… add support for a statement export… an unfamiliar CSV shape” - rather than just the action. That phrasing is what Codex pattern-matches against when you say “I’ve got a statement from a new card to load in” - we’ll tune it deliberately in the next lesson. For now, notice that name and description are the only required fields; everything else about a Skill is optional.
Keep the folder name and the frontmatter name aligned - import-bank-csv here - since the $ invocation, the /skills picker, and the listing all read that name. Two skills sharing a name are not merged; both appear in the selectors. And a vague description remains the single most common reason a Skill never fires, which is the next lesson’s whole subject.
Keep the body a procedure, not an essay
Section titled “Keep the body a procedure, not an essay”The body is a set of standing instructions, and once a Skill fires its content stays in the conversation for the rest of the turn - so every line is a recurring cost, paid only in the sessions that earn it, but paid in full there. Write it the way you’d write AGENTS.md: state what to do, skip the why. Numbered steps, concrete checks, clear failure behavior. No narration about why cents matter - just the moves, in order, with the rules that matter stated once.
Two details in that body are worth seeing for what they are:
- The “stop and ask if a sample isn’t available” line. A procedure run by a human has an implicit “obviously don’t invent a column layout I can’t see.” Codex has no such instinct unless you write it. Bake the guardrails into the body; a Skill is only as safe as its instructions.
- The cents-without-float step, restated. The rule already lives in
AGENTS.mdas a standing fact, and the hardened parse path implements it in code - but the import moment is exactly where a new format detail slips past it, so step three repeats the conversion at the precise moment a fresh bank’s decimal string is in front of Codex.AGENTS.mdholds the general rule, and the Skill puts the check at the point of maximum risk. (Body versus rules file is the last lesson’s question.)
Written down once, that step-three line stops being something you have to remember to say - it’s just there, every time the Skill fires, at the exact moment it matters.
Putting heavier material in subdirectories
Section titled “Putting heavier material in subdirectories”The procedure above is self-contained, which is the right place to start. But a Skill directory can hold supporting material that loads only when the body points at it. A scripts/ folder for a helper the steps tell Codex to run; a references/ folder for a long-form note; an assets/ folder for a template importer to copy. None of it loads at session start, and none of it loads when the Skill fires - it loads at the moment an instruction reaches for it.
That three-tier loading - metadata always, body on activation, extra files on demand - is what keeps a rich Skill cheap until it’s actually working. For our import procedure a single SKILL.md is plenty; reach for subdirectories only when the body genuinely outgrows itself. The docs’ own guidance leans the same way: prefer instructions over scripts unless you need deterministic behavior or external tooling.
Aligning with the standard
Section titled “Aligning with the standard”One reason to write this in .agents/skills/ rather than somewhere Codex-specific: the SKILL.md you just wrote - name + description frontmatter, a markdown procedure below - is the Agent Skills standard, the same shape other agents read. The procedure for turning a bank export into integer-cents transactions isn’t a Codex secret; it’s budgetcli knowledge, and the standard is what lets it travel.
Codex layers its own optional config on top for invocation policy and tool dependencies - that’s the next lesson’s territory - but the core file is portable by design.
The file exists on disk now, priced at about 380 estimated tokens and mostly dormant. What it can’t tell you yet is whether Codex actually reaches for it when a new format shows up unannounced - whether the six turns genuinely become two, or whether the description is too vague to earn its keep. That recognition lives entirely in one field. Let’s make it fire at the right time, and only the right time.