Shipping AIt. Music on; nonsense off.
My agent skills — small, composable, behaviour-focused. Each one addresses a real failure mode I've watched coding agents hit, and steers the agent away from it before the failure happens. Organised the way Matt Pocock organises his.
These are not tool docs. They're behaviour interventions in my voice, encoding the way I work — Result<T, E> over throws, vertical slices over layers, Biome over the ESLint+Prettier stack, node:test over Jest. Forged on real projects: ait (a personal AI platform), mateonunez.co (Next.js 16 + WCAG 2.1 AA), and a fistful of Fastify plugins.
If it doesn't hold up in production, it doesn't make the cut.
npx skills add mateonunez/skillsor
git clone https://github.com/mateonunez/skills.git
cd agentfiles
./scripts/link-skills.shThe script symlinks every SKILL.md under skills/ (excluding deprecated/) into ~/.claude/skills/, where Claude Code picks them up.
I built these for the failure modes I see most often when collaborating with coding agents.
"Errors are values."
— Rob Pike
The problem: agents reflexively throw new Error() in service-layer code, then bury try/catch three layers up at the call site. Control flow becomes invisible. The type system can't help me — every function lies about what it can fail with.
The fix: /result-not-throw. Business logic returns Result<T, E> with discriminated __type errors. Throws are reserved for boundaries (HTTP handlers, CLI entry, framework adapters). The compiler enforces exhaustive error handling.
"The standard library is the first thing you should reach for."
— common sense, often ignored
The problem: every greenfield package gets jest, @types/jest, ts-jest, babel-jest by default. Half a megabyte of dependencies for what node:test ships built into the runtime. Same story with ESLint + Prettier when Biome covers both.
The fix: /node-native-tests and /single-tool-per-job. Use the runtime. Use the one tool that does the job. New dev dependencies have to clear a real bar.
"The best modules are deep. They allow a lot of functionality to be accessed through a simple interface."
— John Ousterhout, A Philosophy of Software Design
The problem: controllers/ here, services/ there, repositories/ over there. Every feature change touches five folders. The horizontal cuts ripple across the codebase on every PR.
The fix: /vertical-slices. Package by feature. The seam between features is a workspace package boundary, not a layer convention. Workspace protocol (workspace:*) and scoped packages (@scope/feature) make the boundary visible at every import site.
"The vendor's wire format is not my domain model."
— every ETL pipeline that ever survived a vendor breaking change
The problem: fetch('https://api.spotify.com/...').then(r => r.json()) and the response goes straight to the database. Three vendors later, every consumer special-cases every shape, and a Spotify API change cascades through six files.
The fix: /entity-normalization. At the boundary, normalise to a typed NormalizedEntity with a __type discriminator. Vendor noise lives in metadata. Downstream code is vendor-agnostic by construction.
"Accessibility is a baseline, not a feature."
— most of the web, getting it wrong
The problem: <div onClick> in place of <button>. Missing alt. outline: none with no replacement. Modals that don't trap focus. The agent ships UI that works for sighted, mouse-using users — and silently breaks for everyone else.
The fix: /a11y-default-review. A short checklist run after every UI change. Keyboard, focus, semantics, ARIA polish. The bar is WCAG 2.1 AA — the same bar I hold mateonunez.co to.
Skills I use daily for code work.
- setup-mateonunez-skills — Scaffold the per-repo conventions my other engineering skills assume — pnpm + corepack, Biome (1.9 legacy or 2.x new), node:test posture, CONTEXT.md, docs/adr/. Run once per repo.
- result-not-throw — I return Result<T, E extends Error> from business logic — never throw. Errors are typed Error subclasses with a code field (AItError pattern).
- entity-normalization — When data crosses a system boundary into my code, I normalise it into a typed entity with a __type discriminator and a fixed common shape. The vendor's wire format is not my domain model.
- vertical-slices — I organise code by feature, not by layer. Every feature is a workspace package; new files live with the feature they belong to.
- node-native-tests — I use node:test + borp + c8, not Jest or Vitest. The runtime ships a test runner — I use it.
- single-tool-per-job — Biome is enough. I don't run ESLint or Prettier. For every other job, one tool per job.
- fastify-plugin-shape — How I write Fastify plugins — fastify-plugin wrapper, idempotency guard, decorator pattern, withX() helper for TS narrowing, module augmentation.
- code-review — Review a diff or PR against my conventions. Findings are triaged by severity (correctness → conventions → polish), each convention finding cites the skill it violates.
General workflow tools, not code-specific.
- conventional-commits-scoped — I write Conventional Commits with scopes. Scope comes from the repo's existing catalogue, never invented.
Tools I keep around but rarely use.
- a11y-default-review — Quick accessibility pass after UI changes — keyboard nav, focus visibility, semantic HTML, aria-live, reduced motion. WCAG 2.1 AA bar.
- types-first-guards — Use strict TypeScript patterns — discriminated unions for domain concepts, exhaustive checks, nominal typing where needed to prevent ID mix-ups at compile time.
Skills tied to my own projects (mateonunez.co, ait) and my dev environment (Colemak + Neovim + Tmux). Linked locally for me but not promoted publicly. See skills/personal/README.md.
agentfiles/
├── CLAUDE.md # bucket governance
├── CONTEXT.md # domain glossary
├── .claude-plugin/
│ └── plugin.json # public skills only
├── skills/
│ ├── engineering/ # daily code discipline
│ ├── productivity/ # daily non-code workflow
│ ├── misc/ # rarely used
│ ├── personal/ # tied to my projects, not promoted
│ ├── in-progress/ # drafts
│ └── deprecated/ # excluded from symlinks
├── scripts/
│ ├── link-skills.sh # symlinks SKILL.md → ~/.claude/skills/
│ └── list-skills.sh # inventory
└── docs/
└── adr/ # repo-level architecture decisions
MIT — Mateo Nunez · mateonunez.co · @mateonunez
I create MIT bugs.