◂ back to m1xl447.home
TUTORIAL · DEV

HOW TO BUILD CLAUDE CODE
PLUGINS & SKILLS

m1xl447 2026.04.09 18 min read ☕ a whole pot of tea

A practical walkthrough of plugins and skills: the manifest format, how skills get discovered, and three tiny plugins I shipped for my own dotfiles last weekend. No fluff.

I spent a rainy Saturday in March learning the plugin system end-to-end. What follows is the post I wish had existed that morning. I'll assume you've used Claude Code before and are comfortable editing a few config files; otherwise, nothing here is tricky.

Plugins vs. Skills

It took me a minute to get these straight, so let me just say it clearly:

  1. A skill is a named capability — a folder with a prompt and optional tools. Think of it as "a thing the model knows how to do and can choose to invoke."
  2. A plugin is a bundle you install. It can contain one or more skills, plus hooks into the app itself (keybindings, status bar, commands).
  3. You almost always start by writing a skill. The plugin wrapper is just how you ship it to other machines.
  4. They discover themselves. Drop the folder in the right place and the model sees it on next launch. No registry call.
★ HEADS UP Before writing a plugin, check if a skill alone does the job. In my three examples below, two of them are skills-only — I only needed a plugin for one.

The Minimum Viable Skill

A skill is a folder with a SKILL.md at the root. That file has YAML frontmatter (the manifest) and a markdown body (the prompt). That's it. No build step.

---
name: "rubber-duck"
description: "Walk through a bug by explaining it to me line by line"
when_to_use: "User says they're stuck, or asks for a rubber duck session"
---

You are a rubber duck. Do not propose fixes on the first turn.
Ask the user to describe, in plain words, what they expect the
code to do. Then ask what it actually does. Then ask what changed
most recently. Only after all three questions, offer a hypothesis.

Drop that at ~/.claude/skills/rubber-duck/SKILL.md and the next session will pick it up. The model decides when to trigger it based on when_to_use. You don't have to call it by name.

Three Plugins I Actually Shipped

Rx

commit-scribe

Reads the staged diff, writes a conventional commit message, and refuses when the diff is too sprawling. Skill only.

readme-shaper

Takes a messy README and restructures it to: one-line pitch, quick install, example, full options. Skill only.

standup-bot

Hooks the "save" event, reads commit messages from today, drafts a 4-line standup note. Full plugin with a hook.

Adding Hooks (the plugin wrapper)

When a skill isn't enough — because you want something to run on an event instead of being invoked by a prompt — you wrap it in a plugin. The plugin manifest lives at plugin.json next to the skill folder.

// standup-bot/plugin.json
{
  "name": "standup-bot",
  "version": "0.1.0",
  "skills": ["./skills/standup"],
  "hooks": {
    "on_session_end": "./hooks/draft-standup.js"
  }
}
◆ GOTCHA Hooks run in a sandbox — no network by default. If you need git, declare it in the capabilities array. I forgot this for about ninety minutes.

Tips from a Weekend

Order of things that actually mattered, in descending importance:

That's most of what I learned. The plugin system is genuinely small — which is the nicest thing I can say about a piece of software. You can read the full spec in an afternoon and remember it in a week. That is rare.

◂ SPEC-DRIVEN DEV HOME THE DESK ▸