A declarative plugin architecture with a critical two-layer hook system, skills, agents, commands, and MCP server integrations — no build system required.

Claude Code ships with powerful built-in capabilities, but every environment is different. Teams need custom hooks for coding standards, security teams need audit trails, and developers need persistent memory and domain-specific agents. Without an extension mechanism, every customization requires forking Claude Code itself.
The plugin ecosystem provides a declarative, file-based extension model. Drop files in a directory — markdown for agents, YAML for config, shell scripts and Python for hooks. No compilation, no package registry, no build toolchain.
Claude Code reads hooks from settings.json for PostToolUse and Stop events — not from plugin hooks.json. Plugins that only wire hooks in their manifest will silently fail to fire critical lifecycle hooks. The solution: wire hooks in both settings.json (what CC reads) and plugin hooks.json (what the plugin lifecycle manages). This is the single most important implementation detail.
~/.claude/plugins/local/my-plugin/
├── plugin.json # Manifest: name, version, hooks, config schema
├── CLAUDE.md # Plugin-specific instructions
├── hooks/
│ ├── hooks.json # Plugin-layer hook definitions
│ └── *.py / *.sh # Hook implementations
├── skills/
│ └── my-skill/
│ ├── SKILL.md # Skill definition with frontmatter
│ └── references/ # Domain knowledge files
├── agents/
│ └── my-agent.md # Agent persona definition
└── commands/
└── my-command.md # Slash command definition
What Claude Code actually reads for all event types. Required for PostToolUse, Stop, SessionEnd, and Notification events. Scripts referenced here fire reliably.
What the plugin system manages. Works for SessionStart and PreToolUse. Provides plugin-scoped configuration, matchers, and timeouts. Both layers can reference the same script files.
| Event | When | Can Block? | Where to Wire |
|---|---|---|---|
| SessionStart | Session begins | No | Either layer |
| UserPromptSubmit | User sends message | No | settings.json |
| PreToolUse | Before tool execution | Yes | Either layer |
| PostToolUse | After tool execution | No | settings.json (required) |
| PreCompact | Before context compression | No | plugin hooks.json |
| Stop | Session ends | No | settings.json (required) |
| SessionEnd | After session cleanup | No | settings.json |
| Plugin | Version | Agents | Skills | Commands | Focus |
|---|---|---|---|---|---|
| Conductor | 1.0.0 | 29 | 7 | 1 | Multi-agent orchestration with tiered workflows |
| Claude Memory | 1.0.0 | — | 1 | 6 | 60-tool MCP server, 12 hooks, vector memory lifecycle |
| Governance | 0.1.0 | — | — | 3 | Identity manifests, audit bus, policy engine, trust broker |
| Context Guard | 3.0.0 | — | — | — | 4-tier context window monitoring with velocity prediction |
{
"name": "my-plugin",
"description": "What this plugin does",
"version": "1.0.0",
"author": { "name": "Your Name" },
"hooks": "./hooks/hooks.json",
"configuration": {
"threshold": { "type": "number", "default": 0.9 },
"enabled_features": { "type": "array", "default": ["audit", "dedup"] }
}
}
Intercepts search tools (Grep, Glob) and serves memory content as the tool result. Claude gets the answer without needing to decide to check memory. The memory-first-gate.sh script embeds the search query, queries Qdrant, and if a match exists, returns it directly.
Fires on every tool call. Checks actions against session objectives for scope drift, target drift, and destructive operations. Buffers flags to JSONL. Must complete in <2s and never block.
Reads tool chain buffer, classifies task type, calculates success metrics, stores to heuristics collection. Feeds self-assessment at next session start. Must run before flush_insights.py.
Queries Qdrant for memories relevant to the current working directory. Injects top matches into the session context. Also triggers self-assessment and sets constitutional objectives.
Build a Claude Code plugin system:
1. PLUGIN MANIFEST (plugin.json):
- JSON schema: name, description, version, author, hooks ref, config schema
- Validation at startup with clear rejection messages
2. TWO-LAYER HOOK SYSTEM:
- Layer 1 (settings.json): Wire PostToolUse, Stop, SessionEnd, Notification here
because CC only reads these events from settings.json
- Layer 2 (plugin hooks.json): Wire SessionStart, PreToolUse, PreCompact here
for plugin-scoped configuration and matchers
- Both layers can reference the same script files
- 7 events: SessionStart, UserPromptSubmit, PreToolUse, PostToolUse,
PreCompact, Stop, SessionEnd
- Matchers: regex on tool_name, glob on file paths
- PreToolUse returns: allow / block (with reason) / modify (altered params)
- Error isolation: failed hooks log but don't crash
3. SKILLS (skills/*/SKILL.md):
- YAML frontmatter (name, description, version)
- references/ subdirectory for domain knowledge
- Lazy loading: discovered at startup, content loaded on Skill invocation
4. AGENTS (agents/*.md):
- Markdown personas with frontmatter: name, description, tools
- Capability declarations for governance integration
- Dispatched via Agent tool with subagent_type
5. COMMANDS (commands/*.md):
- Slash command definitions with argument schemas
- Invoked by /command-name in prompt
6. MCP INTEGRATION:
- .mcp.json in plugin root for MCP server configuration
- Auto-registered at plugin startup
7. CONFIGURATION CASCADE:
- Global settings.json → Project settings → Plugin plugin.json
- Each level overrides previous
Create a scaffold at ~/.claude/plugins/local/my-plugin/ with examples of each.
CC's hook loading has a gap: PostToolUse and Stop from plugin hooks.json don't reliably fire. Wiring in settings.json guarantees execution. Both layers reference the same scripts — no duplication, just reliable delivery.
No build system, compilation, or package registry. Drop files in a directory. Markdown for agents/skills/commands, JSON for manifests, Python/bash for hooks. Eliminates toolchain friction.
Rather than hoping Claude "decides" to check memory, the Tool Facade pattern intercepts search tools and serves memory results directly. This architectural pattern is reusable for any data source a plugin wants to inject transparently.
Non-blocking hooks that fail (error, timeout) let the tool call proceed. Only PreToolUse hooks returning "block" can halt execution. A buggy plugin can't make CC unusable.
Governance validates hook actions through its policy engine. PreToolUse hooks that allow elevated tools are checked against agent manifests. PostToolUse events feed the governance audit bus.
Memory uses all 7 event types across both hook layers. MCP tools are exposed through the plugin's .mcp.json. The Tool Facade pattern emerged from the memory plugin's need to inject context transparently.
Context monitoring uses PostToolUse (wired in settings.json) to track token consumption. Velocity prediction and escalation levels all depend on reliable PostToolUse firing.
Conductor defines 29 agents as markdown files, orchestrates via state machine hooks, and validates state through PostToolUse schema checks. The plugin architecture makes the entire orchestration system portable.