>_

How to Build a Claude Code Plugin: Real Example [2026]

Robin||11 min
claude-codeclaude-code-pluginpluginsevolving-liteopen-source
How to Build a Claude Code Plugin: Real Example [2026]
Listen to this article (11 min)

You set up the same Claude Code commands, hooks, and subagents on every new machine, and you copy them by hand each time. A Claude Code plugin fixes that: it bundles all of it into one installable folder you can clone, share, or publish to a marketplace. This guide builds one from scratch, then shows you the real manifest and hooks from Evolving Lite, my open-source Claude Code plugin, so every snippet here is something you can clone and run, not a toy example.

TL;DR

A Claude Code plugin is a directory with an optional .claude-plugin/plugin.json manifest plus commands/, agents/, skills/, and hooks/ folders. Scaffold it with claude plugin init, drop your components in, then install it from a local directory, a Git repo, or a marketplace. Since v2.1.157 you can also just put it in .claude/skills/ and Claude Code auto-loads it with no marketplace at all.

Anatomy of a Claude Code plugin
.mcp.json / .lsp.jsonBundled MCP servers and language servers
hooks/hooks.jsonLifecycle automation on session + tool events
skills/SKILL.md workflows loaded on demand
agents/Subagent definitions Claude can invoke
commands/Slash commands as Markdown with frontmatter
.claude-plugin/plugin.jsonManifest: name, version, metadata, path overrides

What is a Claude Code plugin?

A Claude Code plugin is a self-contained directory that bundles commands, agents, skills, hooks, and MCP servers into one installable unit, described by an optional .claude-plugin/plugin.json manifest. Instead of pasting a slash command into one project and a hook into another, you package the whole workflow once and install it anywhere with a single command.

The manifest is genuinely optional. If you leave it out, Claude Code auto-discovers the standard folders and derives the plugin name from the directory. You add plugin.json when you want real metadata (version, author, license) or when you plan to publish through a plugin marketplace. That packaging-and-distribution job is the whole point of the plugin layer, and it is what separates a plugin from a loose pile of config files.

This matters more as your setup grows. My Claude Code foundation ships 16 slash commands, 6 subagents, 16 hook scripts, and 2 skills as one plugin. Without the plugin format that is dozens of files to copy and re-wire on every machine. With it, the install is one clone and one command.

What you'll build

You'll build a minimal but real Claude Code plugin called my-plugin with four working parts:

  • A plugin.json manifest with proper metadata.
  • One slash command (/remember) that saves a note to memory.
  • One hook that fires on session start.
  • A subagent and a skill, so you see how every component type slots in.

Then you'll install it three different ways and confirm it loads. By the end you'll have a folder you can push to GitHub and install on any other machine in about 30 seconds.

Prerequisites

You need three things before you start:

  • Claude Code v2.1.157 or newer. Check with claude --version. The claude plugin init scaffolder and .claude/skills auto-loading both landed in that release.
  • A terminal and a text editor. Plugins are plain files: Markdown, JSON, and shell or Python scripts. No build step, no compiler.
  • Basic familiarity with slash commands and hooks. If hooks are new to you, the Claude Code hooks guide walks through the event model first.

That's it. A plugin is just a folder Claude Code knows how to read, so there is nothing to install beyond Claude Code itself.

Step 1: Scaffold the plugin structure

Run the built-in scaffolder. As of v2.1.157, Claude Code ships claude plugin init <name>, which generates the conventional layout for you:

code
claude plugin init my-plugin

That creates a directory with the standard skeleton. The layout every Claude Code plugin follows looks like this:

code
my-plugin/
├── .claude-plugin/
│   └── plugin.json      # manifest (optional but recommended)
├── commands/            # slash commands (.md)
├── agents/              # subagents (.md)
├── skills/              # skills (skill-name/SKILL.md)
├── hooks/
│   └── hooks.json       # hook registrations
├── .mcp.json            # optional: bundled MCP servers
└── README.md

You don't have to use every folder. A plugin can ship only commands, or only hooks, or only skills. Claude Code reads whichever standard directories exist and ignores the rest. If you skip the manifest entirely, the directory name becomes the plugin name and the folders are still auto-discovered.

Step 2: Write the plugin.json manifest

The manifest lives at .claude-plugin/plugin.json and holds your plugin's identity. Here is the real, complete manifest that ships with Evolving Lite, copied straight from the repo:

code
{
  "name": "evolving-lite",
  "version": "1.1.0",
  "description": "Self-evolving Claude Code system that learns from corrections, manages context, and improves every session",
  "author": {
    "name": "PrimeLine AI",
    "email": "hello@primeline.cc",
    "url": "https://primeline.cc"
  },
  "homepage": "https://primeline.cc/products/evolving-lite",
  "repository": "https://github.com/primeline-ai/evolving-lite",
  "license": "MIT",
  "keywords": ["memory", "self-evolving", "context-management", "delegation", "hooks", "automation"]
}

The core fields are name, version, and description for identity, plus an author object, homepage, repository, license, and keywords for discovery. You can also add component-path overrides (commands, agents, skills, hooks, mcpServers, lspServers) when your files live somewhere other than the defaults. Newer releases added a defaultEnabled: false flag so a plugin can ship disabled until the user turns it on, with its dependencies auto-enabled alongside it.

Keep the version honest and bump it on every release. The marketplace install flow keys off name@version, so a stale version number is the fastest way to hand users an old plugin.

Step 3: Add a slash command and a hook

Now the two component types you'll reach for most: a slash command and a hook.

A slash command

Commands live in commands/ as Markdown files with YAML frontmatter. The filename becomes the command name, so commands/remember.md registers /remember. Here is the real /remember command from Evolving Lite, trimmed to its shape:

code
---
description: Explicitly save something to memory as an experience
argument-hint: [What to remember]
---

Save an explicit memory/experience to the Evolving Lite knowledge base.

## Input: $ARGUMENTS

If empty: "What should I remember? Describe what you
learned, decided, or want to keep."

## Process

1. **Classify the type** (solution, gotcha, pattern,
   technique, decision, preference)
2. **Extract key fields** (summary, problem, solution, tags)
3. **Save as experience file**:
   Write to `${CLAUDE_PLUGIN_ROOT}/_memory/experiences/exp-{timestamp}.json`
4. **Confirm**: "Saved: {summary}"

Two things to notice. $ARGUMENTS is where whatever the user typed after /remember lands. And ${CLAUDE_PLUGIN_ROOT} is a variable Claude Code expands to your plugin's install path, so the command writes inside the plugin no matter where it was cloned. That variable is what makes a plugin portable.

A hook

Hooks live in hooks/hooks.json and fire on lifecycle events. The events you can hook are SessionStart, SessionEnd, PreToolUse, PostToolUse, PermissionRequest, UserPromptSubmit, and Stop. Here is a real slice of the Evolving Lite hooks.json, wiring a health check to run when a session starts:

code
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup|resume|clear|compact",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PLUGIN_ROOT}/hooks/scripts/health-sentinel.sh",
            "timeout": 10
          }
        ]
      }
    ]
  }
}

The matcher decides which sub-events trigger the hook (here, any session that starts, resumes, clears, or compacts). The command runs your script, again resolved through ${CLAUDE_PLUGIN_ROOT}. Hook types go beyond command: you can also use http, mcp_tool, prompt, and agent. Evolving Lite alone wires 16 hook scripts across 6 events, which is how it warns you at 70% context and blocks dangerous bash before it runs. The full event model is covered in my Claude Code hooks guide.

This lives in primeline-ai/evolving-lite - the self-evolving Claude Code plugin. Free, MIT, no build step.

Step 4: Bundle agents and skills

The last two component types are subagents and skills, and they are where a plugin starts to feel like a real system rather than a config bundle.

Agents

Agents live in agents/ as Markdown files with frontmatter describing the subagent's behavior, tools, and which model it should run on. Claude Code invokes them for scoped tasks, which keeps the main context clean. Evolving Lite ships 6 of them, including a planner and a health monitor. If you want the reasoning behind routing work to subagents automatically, I wrote that up in score-based auto-delegation.

Skills

Skills live in skills/ as subdirectories, each holding a SKILL.md file. A skill is a reusable prompt-based workflow or chunk of reference knowledge that Claude loads only when it is relevant, so it costs you nothing in baseline context until it fires. Inside a plugin, skills are namespaced as plugin-name:skill-name, which prevents collisions with project-level or user-level skills of the same name.

Bundling all of this together is exactly what the plugin format is for. Commands, agents, skills, hooks, and MCP servers ship as one cohesive unit, install in one step, and update in one step. That is the difference between a knowledge architecture you can hand to someone else and one that only works on your laptop.

Step 5: Install and test your plugin

You have three ways to install a Claude Code plugin, from fastest to most shareable.

Option A: drop it in .claude/skills (no marketplace)

Since v2.1.157, any directory under .claude/skills/ that contains a .claude-plugin/plugin.json manifest is loaded as a plugin automatically, no marketplace required. This is the fastest path while you are building and testing. Move your folder there and restart Claude Code:

code
mv my-plugin ~/.claude/skills/my-plugin
claude

Option B: clone and register a directory

This is how Evolving Lite actually installs. You clone the repo, run a one-time setup script that rewrites ${CLAUDE_PLUGIN_ROOT} to your real path, and register the directory in your settings:

code
git clone https://github.com/primeline-ai/evolving-lite ~/.claude-plugins/evolving-lite
cd ~/.claude-plugins/evolving-lite && bash setup.sh

Then add the directory to ~/.claude/settings.json:

code
{
  "pluginDirectories": ["~/.claude-plugins/evolving-lite"]
}

Option C: install from a marketplace

For sharing, a marketplace is a .claude-plugin/marketplace.json catalog in a Git repo that points to where each plugin lives. Users add the marketplace, then install from it:

code
/plugin marketplace add owner/repo
/plugin install my-plugin@my-marketplace

The /plugin command opens a manager UI with Discover, Installed, and Marketplaces tabs, and the official claude-plugins-official marketplace is available automatically. Whichever option you pick, verify the plugin loaded: run /plugin list to see installed plugins, and check that /remember shows up in slash-command autocomplete. If it does, your plugin works.

Plugins vs skills vs the Agent SDK

People mix these up constantly, so here is the clean split. They are three layers of the same extension stack, not competitors.

Skill (the atom)
  • -A single SKILL.md workflow or knowledge chunk
  • -Loaded on demand, near-zero baseline cost
  • -Auto-discovered from .claude/skills
  • -Best for reusable reference and procedures
Plugin (the package)
  • +Bundles skills + commands + agents + hooks + MCP
  • +One installable, shareable, versioned unit
  • +Distributed via marketplaces or git clone
  • +Best for shipping a whole workflow to others

The Agent SDK is the third layer: a Python or TypeScript library that runs the same Claude Code harness headlessly. It loads skills and plugins from the filesystem just like the CLI does, but it also lets you define subagents and tools programmatically in code instead of as files. Rule of thumb: write a skill for one reusable workflow, package a plugin when you want to ship several of them together, and reach for the Agent SDK only when you are building an automated agent that runs without a human at the terminal.

What changed in Claude Code plugins in 2026?

The plugin system moved fast in the first half of 2026. The changes that actually affect how you build matter more than the version numbers, so here is what shifted, with the releases that introduced them.

  • Auto-loading from .claude/skills (v2.1.157, May 2026). The biggest one. You no longer need a marketplace to test a plugin. Drop it in .claude/skills, and claude plugin init scaffolds the layout for you.
  • defaultEnabled: false and dependencies (v2.1.154). Plugins can ship disabled by default, and a plugin's dependencies auto-enable when you enable it.
  • /plugin list and richer hooks (v2.1.163, June 2026). A CLI command to list installed plugins, plus Stop and SubagentStop hooks that can return additionalContext to keep a turn going.
  • Marketplace search (v2.1.172, June 2026). A search bar inside /plugin for browsing large marketplaces.
  • Portable data and seed dirs (v2.1.78, v2.1.79). ${CLAUDE_PLUGIN_DATA} gives plugins a persistent data directory that survives updates, and CLAUDE_CODE_PLUGIN_SEED_DIR lets you start from a predefined plugin set.
Verify version-specific claims

Claude Code ships almost daily, so pin any exact version number against the official Claude Code changelog before you depend on it. The dates above reflect the changelog as of mid-2026.

If you want a worked example of a plugin that uses these newer hook capabilities to reshape its own behavior between turns, that is the whole story of my self-improving Claude Code system.

Troubleshooting common plugin errors

A few failures show up again and again when people build their first Claude Code plugin.

  • Plugin loads but commands don't appear. Your command files are probably outside commands/, or the frontmatter is malformed. Commands must be .md files with valid YAML frontmatter directly in the commands/ directory.
  • Hook runs but can't find its script. You hardcoded an absolute path instead of using ${CLAUDE_PLUGIN_ROOT}. After cloning, the install path changes, so always reference scripts through that variable (and run a setup step if your hooks.json bakes in real paths).
  • Plugin shows as disabled. Check for defaultEnabled: false in the manifest, then enable it through /plugin. This is intended behavior for plugins that ship off by default.
  • Changes don't take effect. Plugins load at startup. Use /reload-plugins to pick up edits without restarting Claude Code.
  • ${CLAUDE_PLUGIN_ROOT} is empty in a script. That variable is only set for hook and command execution. If you call a script outside that context, pass the path in yourself.

Once it loads cleanly, you have a real, portable Claude Code plugin. Clone primeline-ai/evolving-lite to see a production-grade one with 16 commands, 6 agents, and 16 hooks wired together, and use it as the template for your own. It installs in about 30 seconds and it is MIT-licensed, so copy whatever you need.

FAQ

What is a Claude Code plugin?+
A Claude Code plugin is a self-contained directory that bundles slash commands, subagents, skills, hooks, and MCP servers into one installable unit, described by an optional .claude-plugin/plugin.json manifest. It lets you package and share a whole Claude Code workflow instead of copying files between projects.
Do I need a plugin.json manifest?+
No. The manifest is optional. If you omit it, Claude Code auto-discovers the standard folders (commands, agents, skills, hooks) and names the plugin after its directory. You add plugin.json when you want version, author, and license metadata or plan to publish to a marketplace.
How do I install a Claude Code plugin?+
Three ways: drop the folder in .claude/skills for automatic loading (v2.1.157+), clone the repo and register its path in pluginDirectories, or add a marketplace with /plugin marketplace add and run /plugin install name@marketplace. The .claude/skills path needs no marketplace at all.
What is a Claude Code plugin marketplace?+
A marketplace is a .claude-plugin/marketplace.json catalog in a Git repository that lists plugins and points to where each one lives. You register it with /plugin marketplace add owner/repo, then install plugins from it. The official claude-plugins-official marketplace is available by default.
What is the difference between a plugin and a skill?+
A skill is one reusable SKILL.md workflow loaded on demand. A plugin is the packaging layer that bundles skills together with commands, agents, hooks, and MCP servers into a single installable, versioned unit. Use a skill for one workflow, a plugin to ship several together.
How do plugins compare to the Claude Agent SDK?+
Plugins extend the interactive Claude Code CLI through files. The Agent SDK is a Python or TypeScript library that runs the same harness headlessly and can define agents and tools in code. The SDK still loads filesystem skills and plugins, so they complement each other.
What hook events can a Claude Code plugin use?+
Plugins can hook SessionStart, SessionEnd, PreToolUse, PostToolUse, PermissionRequest, UserPromptSubmit, and Stop. Hook types include command, http, mcp_tool, prompt, and agent, registered in hooks/hooks.json or inline in the manifest.
How do I update a plugin after editing it?+
Plugins load at startup, so run /reload-plugins to apply edits without restarting Claude Code. For marketplace-installed plugins, use /plugin marketplace update and /plugin update to pull new versions.

>_ Get the free Claude Code guide

>_ No spam. Unsubscribe anytime.

>_ Related