Read OSS

The Plugin System — Commands, Agents, Skills, and Hooks

Intermediate

Prerequisites

  • Article 1: Architecture Overview
  • Understanding of YAML and Markdown frontmatter
  • Basic shell scripting knowledge

The Plugin System — Commands, Agents, Skills, and Hooks

As we saw in Part 1, the claude-code repository houses 13 official plugins built on a convention-over-configuration architecture. But understanding the directory layout is different from understanding how these components actually work. Each of the four component types — commands, agents, skills, and hooks — has its own discovery mechanism, configuration surface, and execution semantics.

This article examines each type using real examples from the official plugins, covers the critical distinction between plugin and settings hook formats, and synthesizes everything into a unified lifecycle model.

Commands: User-Invoked Workflows

Commands are the primary entry point for plugin functionality. They're Markdown files with YAML frontmatter that become slash commands in Claude Code. When a user types /feature-dev, Claude Code loads the corresponding .md file and uses its content as the prompt instruction.

The frontmatter is where things get interesting. Here's the feature-dev command:

plugins/feature-dev/commands/feature-dev.md#L1-L4

---
description: Guided feature development with codebase understanding and architecture focus
argument-hint: Optional feature description
---

The description appears in command listings. The argument-hint tells users what to pass after the command name. But look at code-review's frontmatter for more powerful configuration:

plugins/code-review/commands/code-review.md#L1-L4

---
allowed-tools: Bash(gh issue view:*), Bash(gh search:*), Bash(gh issue list:*), Bash(gh pr comment:*), Bash(gh pr diff:*), Bash(gh pr view:*), Bash(gh pr list:*), mcp__github_inline_comment__create_inline_comment
description: Code review a pull request
---

The allowed-tools field is a whitelist with pattern matching. Bash(gh pr view:*) means "allow Bash tool calls, but only when the command starts with gh pr view." This is how plugins constrain their agents to specific operations — a code review plugin shouldn't be writing files or running arbitrary commands.

The ralph-loop command demonstrates two additional frontmatter fields:

plugins/ralph-wiggum/commands/ralph-loop.md#L1-L5

---
description: "Start Ralph Wiggum loop in current session"
argument-hint: "PROMPT [--max-iterations N] [--completion-promise TEXT]"
allowed-tools: ["Bash(${CLAUDE_PLUGIN_ROOT}/scripts/setup-ralph-loop.sh:*)"]
hide-from-slash-command-tool: "true"
---

The hide-from-slash-command-tool prevents Claude from autonomously invoking this command — it must be user-initiated. And notice how allowed-tools uses ${CLAUDE_PLUGIN_ROOT} to restrict Bash execution to a specific script within the plugin.

Tip: The $ARGUMENTS variable in command body text is replaced with whatever the user types after the slash command. This is how commands accept input without complex argument parsing.

Agents: Specialized AI Workers

Agents are subagent definitions — specialized AI workers that commands orchestrate. Like commands, they're Markdown files with YAML frontmatter, but the frontmatter configures the model and capabilities of the subagent rather than user-facing metadata.

Here's the code-explorer agent from feature-dev:

plugins/feature-dev/agents/code-explorer.md#L1-L7

---
name: code-explorer
description: Deeply analyzes existing codebase features by tracing execution paths...
tools: Glob, Grep, LS, Read, NotebookRead, WebFetch, TodoWrite, WebSearch, KillShell, BashOutput
model: sonnet
color: yellow
---

Each frontmatter field serves a distinct purpose:

Field Purpose Example
name Display name in logs/UI code-explorer
description When to use this agent Detailed capability description
tools Allowed tool whitelist Glob, Grep, LS, Read, ...
model Model tier selection sonnet, haiku, opus
color Visual identification in logs yellow, green, red

Compare the three feature-dev agents to see how model selection and tool restrictions create focused roles:

Agent Model Tools Role
code-explorer sonnet Read-heavy (Glob, Grep, Read...) Fast codebase analysis
code-architect sonnet Same read-heavy set Architecture design
code-reviewer sonnet Same read-heavy set Quality review with confidence scoring

The code-reviewer agent at plugins/feature-dev/agents/code-reviewer.md#L25-L33 introduces a confidence scoring system (0–100 scale) where only issues scoring ≥80 are reported. This filtering mechanism is implemented purely through the agent's prompt instructions — no special runtime support needed. The prompt is the program.

flowchart LR
    CMD["/feature-dev command"] --> EXPLORE["code-explorer<br/>model: sonnet<br/>color: yellow"]
    CMD --> ARCH["code-architect<br/>model: sonnet<br/>color: green"]
    CMD --> REVIEW["code-reviewer<br/>model: sonnet<br/>color: red"]

    EXPLORE --> |"Read-only tools"| ANALYSIS["Codebase<br/>Analysis"]
    ARCH --> |"Read-only tools"| DESIGN["Architecture<br/>Design"]
    REVIEW --> |"Read-only tools"| QUALITY["Quality<br/>Review"]

    style EXPLORE fill:#FDD835
    style ARCH fill:#4CAF50,color:#fff
    style REVIEW fill:#E53935,color:#fff

Skills: Context-Activated Knowledge

Skills are the most passive component type. They provide knowledge that Claude Code automatically activates based on context matching — no explicit invocation required. Each skill lives in its own subdirectory with a SKILL.md file.

Here's the frontend-design skill's frontmatter:

plugins/frontend-design/skills/frontend-design/SKILL.md#L1-L5

---
name: frontend-design
description: Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications.
license: Complete terms in LICENSE.txt
---

The description field does double duty: it documents the skill and tells Claude Code when to activate it. When a user asks about building a web component, Claude Code matches that context against skill descriptions and injects the relevant skill content.

The plugin-structure skill at plugins/plugin-dev/skills/plugin-structure/SKILL.md#L1-L5 has a much more explicit description:

description: This skill should be used when the user asks to "create a plugin", "scaffold a plugin", "understand plugin structure"...

This longer description casts a wider activation net. The skill author controls specificity — a vague description activates broadly, a precise one activates narrowly.

Skills can include supporting material in subdirectories: references/, examples/, scripts/. The plugin-dev plugin has seven skills, each with its own reference material. This is where skills differ fundamentally from commands: a command is a single-shot instruction, but a skill is a knowledge base that persists across the session.

Hooks: Event-Driven Interceptors

Hooks are the most powerful component type — and the most complex. They execute in response to nine lifecycle events, and they can modify Claude Code's behavior by blocking operations, injecting context, or enforcing policies.

The nine hook events, documented at plugins/plugin-dev/skills/hook-development/SKILL.md#L634-L644:

Event When Common Use
PreToolUse Before tool executes Validation, blocking
PostToolUse After tool completes Feedback, logging
Stop Agent considers stopping Completeness checks
SubagentStop Subagent considers stopping Task validation
SessionStart Session begins Context loading
SessionEnd Session ends Cleanup
UserPromptSubmit User sends prompt Input validation
PreCompact Before context compaction Preserve critical info
Notification Notification sent Reactions, logging

Hooks come in two types: command hooks (shell scripts for deterministic checks) and prompt hooks (LLM-driven for context-aware decisions). The exit code protocol is critical:

  • Exit 0 — Allow the operation (stdout shown in transcript)
  • Exit 2 — Block the operation (stderr fed back to Claude)
  • Other — Non-blocking error

Here's a sequence showing how a PreToolUse hook intercepts a file write:

sequenceDiagram
    participant C as Claude Code
    participant H as PreToolUse Hook
    participant T as Write Tool

    C->>H: JSON via stdin (tool_name, tool_input)
    H->>H: Check patterns/rules
    alt Pattern matches (new warning)
        H-->>C: stderr: warning message
        H->>C: exit 2 (BLOCK)
        Note over C: Tool call blocked
    else No match or already warned
        H->>C: exit 0 (ALLOW)
        C->>T: Execute Write tool
        T->>C: Tool result
    end

Matchers let you filter which tools trigger a hook. The security-guidance plugin uses "matcher": "Edit|Write|MultiEdit" to fire only on file modification tools:

plugins/security-guidance/hooks/hooks.json#L1-L16

Matchers support exact match ("Write"), pipe-separated OR ("Edit|Write"), wildcard ("*"), and regex patterns ("mcp__.*__delete.*").

The Dual Hook Format Gotcha

This is the single most common developer pitfall when working with hooks. There are two different JSON formats depending on where your hooks live, and using the wrong one silently fails.

Plugin format (in hooks/hooks.json inside a plugin) wraps events inside a hooks key:

{
  "description": "Optional description",
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/validate.sh"
          }
        ]
      }
    ]
  }
}

Settings format (in .claude/settings.json or user settings) puts events directly at the top level:

{
  "PreToolUse": [
    {
      "matcher": "Bash",
      "hooks": [
        {
          "type": "command",
          "command": "python3 /path/to/validator.py"
        }
      ]
    }
  ]
}

You can see the plugin format in hookify's registration at plugins/hookify/hooks/hooks.json#L1-L49 — note the outer "hooks" wrapper. The bash_command_validator example at examples/hooks/bash_command_validator_example.py#L13-L27 documents the plugin format in its docstring.

Tip: If your hook isn't firing, check the format first. Plugin hooks.json needs the {"hooks": {...}} wrapper. User settings don't. The hook development skill at plugins/plugin-dev/skills/hook-development/SKILL.md documents both formats side by side.

Component Lifecycle: Discovery to Execution

With all four component types understood, here's how they fit together in the unified lifecycle:

flowchart TD
    INSTALL["Plugin Installed"] --> READ["Read plugin.json manifest"]
    READ --> SCAN["Scan Convention Directories"]

    SCAN --> CMD_SCAN["commands/*.md"]
    SCAN --> AGT_SCAN["agents/*.md"]
    SCAN --> SKL_SCAN["skills/*/SKILL.md"]
    SCAN --> HK_SCAN["hooks/hooks.json"]
    SCAN --> MCP_SCAN[".mcp.json"]

    CMD_SCAN --> CMD_REG["Register as<br/>slash commands"]
    AGT_SCAN --> AGT_REG["Register as<br/>available agents"]
    SKL_SCAN --> SKL_REG["Register for<br/>context matching"]
    HK_SCAN --> HK_REG["Register on<br/>event bus"]
    MCP_SCAN --> MCP_REG["Start MCP<br/>servers"]

    CMD_REG --> USER["User types /command"]
    AGT_REG --> ORCH["Command orchestrates agents"]
    SKL_REG --> AUTO["Auto-activated by context"]
    HK_REG --> EVENT["Fired by lifecycle events"]
    MCP_REG --> TOOL["Available as tools"]

    USER --> EXEC["Execution"]
    ORCH --> EXEC
    AUTO --> EXEC
    EVENT --> EXEC
    TOOL --> EXEC

    style INSTALL fill:#FF5722,color:#fff
    style EXEC fill:#4CAF50,color:#fff

The key insight: commands are user-initiated, agents are command-orchestrated, skills are context-activated, and hooks are event-driven. These four invocation models cover the full spectrum from explicit user control to fully automatic behavior.

Custom paths in plugin.json supplement (not replace) default directories. If you specify "commands": "./custom-commands", Claude Code scans both commands/ and custom-commands/. This additive behavior means you can extend the convention without breaking it.

Changes take effect on the next Claude Code session — no hot-reloading of plugin configurations. Edit your hooks.json, restart Claude Code, and the new hooks load.

What's Next

Now that we understand the component model, we're ready to see it orchestrated at scale. In Part 3, we'll examine two sophisticated multi-agent patterns: feature-dev's seven-phase human-in-the-loop workflow with parallel agent launches, and code-review's multi-pass validation pipeline where findings are individually verified by fresh subagents. We'll also dissect the ralph-wiggum plugin's creative self-referential loop pattern — a Stop hook that reads the transcript and re-injects the original prompt to create iterative agent execution.