Day 2

Claude Code: skills and subagents

You ship
3 custom skills, a pull request review by 3 subagents, and a formatting hook.

What you'll build today

After Day 1, WorkBot is a small Next.js app on Vercel with a landing page, a settings page and a health endpoint. Every change reaches production through the same loop, but that loop still relies on you to repeat yourself. Each new component or route starts from a blank prompt, the checks before a deploy live in your head, and formatting happens only when someone remembers it.

Today you teach Claude Code the routines of this project. You add a test runner first, so new code can prove it works. Then you write skills, which are instructions saved under a name and run as a slash command. You build 3 reviewer subagents, separate agents that each start with a fresh context, and run them in parallel behind one command. Last, you add a hook, a shell command that Claude Code runs on its own events, so formatting is enforced rather than requested.

By the end, these tools live in the repository next to the code, and your Day 1 work has had its first review from 3 angles.

By the end of the day you can

  • Know what Claude Code offers beyond prompts: slash commands, hooks, skills and subagents
  • Build custom skills that automate repetitive development tasks
  • Build subagents that review code in parallel

Topics

Anthropic Courses (complete all 3)

Slash Commands, Hooks, and Custom Skills

  • Built-in slash commands: /init, /compact, /review, /help
  • Hooks: shell commands that Claude Code runs on its own events, such as PreToolUse before a tool runs and PostToolUse after it, for example after a file edit
  • Custom skills: your own slash commands, each written as a Markdown file of instructions
  • Skill anatomy: one folder per skill under .claude/skills/, a SKILL.md file with front matter, and the arguments you type, which reach the skill as $ARGUMENTS

Subagents

  • What subagents are: separate agents that Claude Code starts for independent tasks. Each starts with a fresh context, and several can run in parallel
  • When to use them: work that splits into parts that do not depend on each other
  • How results come back: each subagent returns one message to the session that started it
  • Cost and speed: parallel subagents work at the same time, and each one spends its own tokens

Build

Build exercise 1: Custom Skills

Features shipped today

  • WorkBot: 3 skills, a review trio of subagents behind a /pr-review skill, and a formatting hook, all committed in .claude/. The trio reviews a Day 1 pull request

Build 3 skills for the TypeScript project:

/component <name>

  • Creates a React component file at the conventional path
  • Creates a test file beside it with a basic render test
  • Adds the export to the nearest barrel file, an index.ts that re-exports a folder, when the project uses barrel files
  • Follows the project's existing component patterns, and reads the conventions in AGENTS.md, which CLAUDE.md points to

/api-route <name>

  • Creates a Next.js API route handler
  • Validates the input with Zod
  • Handles errors with the right HTTP status codes: 400 for invalid input, 404 for a missing resource, 409 for a conflict
  • Creates a types file for the request and response types

/pre-deploy

  • Runs the TypeScript type check (tsc --noEmit)
  • Runs the test suite
  • Searches the source code for console.log statements
  • Checks that the required environment variables, the ones named in .env.example, are set
  • Reports a pass or fail summary

Build exercise 2: Subagents

Build a pull request review trio: 3 subagents that run in parallel, each reviewing a pull request from a different angle.

  1. Bug hunter: looks for logic errors, edge cases, unhandled null or undefined values, and race conditions
  2. Performance reviewer: looks for N+1 queries (one database query per item in a loop), unnecessary re-renders, missing memoization and bundle size
  3. Security reviewer: looks for injection risks, gaps in authorization, exposed secrets and unsafe data handling

Each subagent writes a focused review. A /pr-review skill runs the 3 and merges their findings into one review. It cannot be named /review, because Claude Code already uses that name for its own bundled review.

Step by step

Add tests with Vitest

The component skill you write later today creates a test beside every component, so the project needs a test runner before it needs that skill. This part sets up Vitest and proves it works with one test on the header you built on Day 1.

Study first

  • Complete the 3 Anthropic courses in the day's topics, Claude Code in Action, Introduction to Agent Skills and Introduction to Subagents. The rest of the day assumes you have done them.
  • Vitest's test and expect, and its 2 ways to run. vitest run runs the tests once, while vitest watches your files and reruns the tests when they change.
  • renderToStaticMarkup from react-dom/server, described in React's reference. It renders a component to an HTML string without a browser, which is enough for a component with no state or events.

Build

  1. Run pnpm add -D vitest. Then add a vitest.config.mts that maps the @/ import alias to src/, and a test script that runs vitest run.
  2. Write src/components/site-header.test.tsx, which renders SiteHeader, the header from Day 1's landing page. It asserts that the markup contains WorkBot, href="/" and href="/settings".
  3. Add pnpm test to pnpm check, between the type check and the build, so CI runs the tests on every pull request.

Done when

  • pnpm test passes, and pnpm check runs the tests before the build.
Technical notes
  • The alias is the whole configuration, so the file stays this short:
import { defineConfig } from "vitest/config";

export default defineConfig({
  test: { alias: { "@/": new URL("./src/", import.meta.url).pathname } },
});
  • Name the file .mts, because Vitest 5 warns when it loads vitest.config.ts as an ECMAScript module (ESM) from a package without "type": "module". The warning suggests .mjs or that package setting, while .mts needs no change to package.json.
  • A Client Component, one marked "use client", may need events or state in a test. When that happens, add jsdom and a DOM testing library.
  • Put each test beside its component, so name.test.tsx sits next to name.tsx.

Write 3 skills

With tests in place, you can teach Claude Code the tasks you would otherwise brief by hand each time. A skill is a SKILL.md file in its own folder under .claude/skills/. It starts with front matter, a block of settings between --- lines, followed by the instructions Claude follows when you type the skill's name as a slash command. Today's 3 skills scaffold a component, scaffold an API route, and run the checks before a deploy.

Study first

  • The skills documentation, for where skills live, the front matter fields, $ARGUMENTS and $0, and inline shell commands.
  • The built-in commands you use alongside skills. /init writes a first CLAUDE.md, /compact summarizes the session to free context, and /help lists every command.
  • Your AGENTS.md, because each skill should name an existing file as its style example instead of describing the style in words.

Build

  1. Write /component <Name>, which creates src/components/<kebab-name>.tsx from the shadcn primitives, with src/components/<kebab-name>.test.tsx beside it. Point the skill at site-header.tsx and its test as the pattern to match. The curriculum's version also adds an export to a barrel file, an index.ts that re-exports a folder. This project imports components by path and has no barrel files, so your skill skips that step.
  2. Write /api-route <name> [methods], which creates 3 files in src/app/api/<name>/. types.ts holds schemas for the request and the response in Zod, a library that validates data and infers its TypeScript type. route.ts exports one function per method and answers 400 with the validation issues on bad input. route.test.ts holds one test per method, plus one test with invalid input.
  3. Write /pre-deploy, which runs 4 inline commands, shell commands that Claude Code runs before the model reads the skill. The first 3 are pnpm typecheck (which runs tsc --noEmit), pnpm test, and a grep for console.log under src/. The fourth is a Node one-liner that loads .env.local, reads the variable names from .env.example, and reports which are missing from process.env. The instructions ask the model for a pass or fail table and a verdict, nothing else.
  4. Run each skill with /component Notice, /api-route echo POST and /pre-deploy. Check the files and run the tests, then delete the files the first 2 skills created.

Done when

  • Each skill creates the files it promises, the tests it scaffolds pass, and /pre-deploy prints its table.
The pre-deploy skill's report
/pre-deploy in the reference run, started with claude -p. The 4 checks ran inline, and the model wrote only the table and the verdict.
Technical notes
  • The /api-route instructions also tell the model to answer 404 for a missing resource and 409 for a conflict. An echo route has no resource, so neither case arises in today's test.
  • disable-model-invocation: true in the front matter keeps a skill out of the tools Claude picks on its own, so /pre-deploy runs when you ask and never when Claude decides to.
  • An inline command is an exclamation mark followed by a command in backticks. Claude Code runs it before the model reads the prompt and puts the command's output in its place, which is why /pre-deploy needs no tool calls.
  • $ARGUMENTS is the whole argument string, and $0 is its first word, so for /api-route echo POST, $0 is echo.
  • A skill also runs outside an interactive session. The screenshot above shows the output of claude -p "/pre-deploy", which printed the result and exited.

Review with 3 subagents

Skills package your routines, and subagents let you split one job across several focused agents. A subagent is a separate agent that Claude Code starts for one task, with its own context window, and it reports back with one message. In this part you build 3 reviewers, each reading a diff from one angle, and a /pr-review skill that runs them in parallel and merges what they found.

Study first

  • The subagents documentation, for the front matter fields name, description, tools and model. A subagent starts with a fresh context and returns one message to the session that started it, so parallel agents multiply the tokens spent.
  • What each reviewer looks for. The bug hunter looks for wrong conditions, unhandled null, unawaited promises and races. The performance reviewer looks for queries in loops, sequential awaits, work repeated on every render, missing memoization and bundle size. It also flags a "use client" component with no state, no event handlers and no browser APIs, which could stay a Server Component. The security reviewer looks for injection, missing authorization, secrets in code or logs, and data shown to the wrong caller. It also looks for prompt injection, where external content is treated as instructions.

Build

  1. Write .claude/agents/bug-hunter.md, performance-reviewer.md and security-reviewer.md. Each file states its angle, says the other angles belong to other reviewers, and lists what to look for. It asks for findings only, most severe first, each with a file, a line and a one-line fix. Give each file the front matter tools: Read, Grep, Glob, Bash and model: sonnet.
  2. Write .claude/skills/pr-review/SKILL.md, with name: pr-review in its front matter. Given a pull request number, the diff comes from gh pr diff <n>, and without one it comes from git diff main...HEAD. The skill tells the 3 agents which command gives the diff, and each agent runs it itself. The skill then writes one review that groups the findings by severity, merges duplicates, and ends with a verdict.
  3. Run /pr-review 1 to review pull request 1, from Day 1, or run /pr-review with no number to review the current branch.

Done when

  • /pr-review returns one review with severities and a verdict, and each finding names its reviewer.
The review trio on pull request 1
The merged review of pull request 1 in the reference run, where the skill still had an earlier name. The bug hunter found that the health route was prerendered at build time, so it served the commit of the build. Day 3 starts by fixing that.
Technical notes
  • Name the skill pr-review, not review. Claude Code already uses /review as an alias for its own bundled /code-review skill, and a project skill can replace a bundled skill's main name but never its aliases. A skill named review would therefore never run, as the skills documentation explains.
  • Every review runs 3 agents, so each agent sets model: sonnet to keep the cost of a review down.
  • The tools list has no Edit or Write, so the reviewers cannot change files, whatever their prompt says.
  • Two instructions keep the merged review short, "Report findings only" and "If nothing is wrong, say so in one sentence". The skill also tells the model not to add findings of its own while merging, so every item traces back to a reviewer.
  • An agent's description tells the session when to delegate to it without being asked, which is why the 3 descriptions say "use when reviewing a pull request or branch".
  • Each agent reads the whole diff, so a review costs about 3 times a single pass. Keep the diff to one pull request.

Format edits with a hook

Skills and subagents run when you ask for them, but some rules should hold on every edit without anyone asking. A hook is a shell command that Claude Code runs on one of its events, whatever Claude intended. AGENTS.md is advice, while a hook is enforcement. Today's one hook runs Prettier, the formatter your pnpm check already runs, on every file Claude edits.

Study first

  • The hooks documentation, for the events, and for PreToolUse and PostToolUse with a matcher that picks the tools a hook applies to. Learn the JSON a command hook reads on stdin, and what its exit codes mean.

Build

  1. Create .claude/settings.json with a PostToolUse hook whose matcher is Edit|Write. Its command reads the edited file's path from stdin and runs Prettier on that file, and the exact entry is in the technical notes.
  2. Add a line to AGENTS.md that names the skills, the reviewers and the hook, so the next session knows they exist.

Done when

  • You ask Claude Code to edit a file with odd spacing, and the saved file comes out formatted, with no prompt and no extra step.
The contents of .claude and the test run
What Day 2 adds to the repository in the reference run: 3 agents, the hook in settings.json and 4 skills, with a test suite that holds 1 test.
Technical notes
  • The hook entry in .claude/settings.json reads the file path with jq, a command-line JSON processor, and passes it to Prettier:
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs pnpm exec prettier --write --ignore-unknown --log-level warn"
          }
        ]
      }
    ]
  }
}
  • PostToolUse runs after the edit, so it can fix or report but not block. A hook that must stop an action goes on PreToolUse instead.
  • When a hook exits with code 2, Claude Code sends its stderr to Claude as feedback, while any other non-zero exit shows the stderr to you only. The formatter exits 0, so Claude sees nothing.
  • --ignore-unknown makes Prettier skip a file it has no parser for, such as a .png, instead of failing the hook.
  • .claude/settings.json is committed and applies to everyone, while .claude/settings.local.json holds your personal settings and git ignores it.

Resources