Issue 15 | Advanced: Plugin Hooks Extension, Performance Benchmarks, and Configuration Reference
15.1 Plugin Hooks System
Hooks are Claude Code's event-driven extension mechanism. Plugins can register Hooks to automatically execute scripts when specific events occur. This tutorial project uses two Plugins: claude-mem (persistent memory) and caveman (token compression).
15.1.1 Hook Event Types
Claude Code supports the following Hook events:
| Event | Trigger Time | Typical Use |
|---|---|---|
Setup |
When plugin is first installed | Install dependencies, initialize data |
SessionStart |
When a new session starts | Load context, inject rules |
UserPromptSubmit |
When user sends a message | Track status, inject reminders |
PreToolUse |
Before tool call | Intercept, log, modify parameters |
PostToolUse |
After tool call | Log observation |
Stop |
When agent stops generating | Session summary |
SessionEnd |
When session ends | Clean up resources |
15.1.2 Hook Execution Flow
sequenceDiagram
participant User as User
participant CC as Claude Code
participant Hook as Hook Script
participant Plugin as Plugin Service
Note over CC: SessionStart Event
CC->>Hook: Execute SessionStart hooks
Hook->>Plugin: Start background service
Hook-->>CC: Inject context into system-reminder
loop Each turn of conversation
User->>CC: Send message
Note over CC: UserPromptSubmit Event
CC->>Hook: Execute UserPromptSubmit hooks
Hook-->>CC: Inject reminder (e.g. caveman mode active)
Note over CC: Agent calls tool
CC->>Hook: PreToolUse hooks (e.g. Read)
Hook->>Plugin: Log file access
Hook-->>CC: Continue execution
Note over CC: Tool execution complete
CC->>Hook: PostToolUse hooks
Hook->>Plugin: Log observation
Hook-->>CC: Continue
end
Note over CC: Stop Event
CC->>Hook: Execute Stop hooks
Hook->>Plugin: Generate session summary15.1.3 The Two Plugins in This Project
claude-mem β Cross-session Persistent Memory
Function: Automatically logs observations from each tool call, loading historical context at the start of the next session.
Hook Configuration (hooks.json):
{
"hooks": {
"Setup": [
{ "command": "node scripts/smart-install.js", "timeout": 300 }
],
"SessionStart": [
{ "command": "node scripts/smart-install.js", "timeout": 300 },
{ "command": "node scripts/worker-service.cjs start + health check", "timeout": 60 },
{ "command": "node scripts/worker-service.cjs hook context", "timeout": 60 }
],
"UserPromptSubmit": [
{ "command": "node scripts/worker-service.cjs hook session-init", "timeout": 60 }
],
"PostToolUse": [
{ "matcher": "*", "command": "node scripts/worker-service.cjs hook observation", "timeout": 120 }
],
"PreToolUse": [
{ "matcher": "Read", "command": "node scripts/worker-service.cjs hook file-context", "timeout": 2000 }
],
"Stop": [
{ "command": "node scripts/worker-service.cjs hook summarize", "timeout": 120 }
],
"SessionEnd": [
{ "command": "node scripts/worker-service.cjs hook session-complete", "timeout": 30 }
]
}
}
What Each Hook Does in This Project:
| Hook | Execution Content | Impact on Teams Development |
|---|---|---|
SessionStart |
Starts local service (localhost:37777), loads historical observations into context | team-lead can see decisions and progress from previous sessions |
UserPromptSubmit |
Initialize session tracking | Logs at the start of each conversation turn |
PostToolUse(*) |
Logs observation after each tool call | Automatically logs all agent operations (Edit, Write, Bash, etc.) |
PreToolUse(Read) |
Logs file context before reading a file | Helps understand code structure across sessions |
Stop |
Generates session summary | Quickly restores context on next startup |
MCP Tools Provided by claude-mem:
| Tool | Purpose |
|---|---|
search |
Search historical observations |
timeline |
Get context before and after a specific time |
get_observations |
Get observation details |
smart_search |
AST-level code search |
smart_outline |
File structure overview (without reading full content) |
build_corpus |
Build topic knowledge base |
query_corpus |
Query knowledge base |
Usage in Teams Development:
# Search historical decisions in team-lead session
/mem-search "δΈΊδ»δΉη¨ Shunting-yard"
# Build project knowledge base
/knowledge-agent "calculator architecture"
# Smart code search (saves tokens)
/smart-explore "evaluate function"
caveman β Token Compression Mode
Function: Injects compression rules into the system prompt, allowing Claude to output with fewer tokens while maintaining technical accuracy.
Hook Configuration (plugin.json):
{
"hooks": {
"SessionStart": [
{
"command": "node hooks/caveman-activate.js",
"timeout": 5,
"statusMessage": "Loading caveman mode..."
}
],
"UserPromptSubmit": [
{
"command": "node hooks/caveman-mode-tracker.js",
"timeout": 5,
"statusMessage": "Tracking caveman mode..."
}
]
}
}
How caveman Works:
SessionStart hook execution:
1. Writes flag file ~/.claude/.caveman-active (value "full")
2. Reads compression rules from SKILL.md
3. Injects the complete rule set into system-reminder
UserPromptSubmit hook execution:
1. Tracks current caveman status
2. Injects a brief reminder each turn: "CAVEMAN MODE ACTIVE"
caveman Output Comparison:
| Mode | Example Output |
|---|---|
| Normal | "Sure! I'd be happy to help you with that. The issue you're experiencing is likely caused by..." |
| caveman full | "Bug in auth middleware. Token expiry check uses < not <=. Fix:" |
Suggestions for Teams Development:
- team-lead session: Keep caveman active to save tokens
- Sub-agent: caveman rules will indirectly affect via CLAUDE.md, but sub-agents have their own context
- When writing code, commit messages, PRs: caveman automatically degrades to normal output
caveman Mode Switching:
/caveman # View current mode
/caveman full # Full compression (default)
/caveman lite # Light compression
/caveman ultra # Extreme compression
stop caveman # Turn off
normal mode # Turn off
15.1.4 Plugin Installation and Management
# View installed plugins
cat ~/.claude/plugins/installed_plugins.json
# Plugin file structure
~/.claude/plugins/
βββ installed_plugins.json # Installation records
βββ cache/ # Plugin code cache
β βββ thedotmack/claude-mem/12.3.2/
β β βββ .claude-plugin/plugin.json # Plugin definition
β β βββ hooks/hooks.json # Hook configuration
β β βββ .mcp.json # MCP service definition
β β βββ scripts/ # Hook scripts
β βββ caveman/caveman/84cc3c14fa1e/
β βββ .claude-plugin/plugin.json # Plugin definition
β βββ hooks/ # Hook scripts
βββ blocklist.json
βββ marketplaces/
15.1.5 Plugin and Teams Interaction
flowchart TD
subgraph "Plugin Hooks (team-lead session only)"
CM["claude-mem hooks"]
CV["caveman hooks"]
end
subgraph "Sub-Agents (ui-dev / logic-dev / qa-engineer)"
A1["Independent Claude Code process"]
A2["Has independent context window"]
A3["Do Plugin hooks trigger?"]
end
CM -.->|"SessionStart logs context"| DB[(observation DB)]
CM -.->|"PostToolUse logs operations"| DB
CV -.->|"Injects compression rules"| TL["team-lead context"]
A3 -->|"Depends on whether the agent process
has loaded the plugin"| A1
style CM fill:#e1f5fe
style CV fill:#fff3e0
style DB fill:#e8f5e9Key Facts:
- Plugin hooks run in the team-lead's Claude Code process
- Sub-agents are independent Claude Code processes and do not inherit the team-lead's plugin configuration
- This means claude-mem's automatic logging will not capture sub-agent operations
- Sub-agent operations can only be perceived by the team-lead via inbox files and the shared file system
15.1.6 Custom Hooks (Project Level)
In addition to Plugin hooks, project-level hooks can also be defined in .claude/settings.json:
{
"permissions": { "allow": ["..."] },
"hooks": {
"PreToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "echo 'File being written: $TOOL_INPUT' >> /tmp/write-log.txt"
}
]
}
]
}
}
| Hook Field | Description |
|---|---|
matcher |
Matching tool name (e.g., Write, Edit, Bash), * matches all |
type |
Fixed as command |
command |
Shell command to execute |
timeout |
Timeout (seconds) |
15.1.7 Context Length Issues
Each agent has an independent context window but shares the file system.
team-lead context: Contains all agent notifications + user conversations + planning files
ui-dev context: Contains CLAUDE.md + requirement.md + its own code
logic-dev context: Contains CLAUDE.md + requirement.md + its own code
qa-engineer context: Contains all source code + test code
Common Issues:
- team-lead context is the longest (because it tracks all agents), triggers autocompact first
- After autocompact, historical messages are compressed, potentially leading to "forgetting" certain agent notifications
- Solution: Write important status to files (task_plan.md, progress.md), do not rely on context memory
15.1.8 Task Dependency Setting Errors
// β Incorrect: Dependency direction is reversed
// Task #1 is Phase B, Task #2 is Phase A
// Result: Phase A is blocked by Phase B
TaskUpdate({ taskId: "2", addBlockedBy: ["1"] })
// β
Correct
// Confirm the correspondence between Task ID and Phase before setting dependencies
TaskUpdate({ taskId: "2", addBlockedBy: ["1"] }) // Phase B blocked by Phase A
Once blockedBy is set, it cannot be removed! You can only tell the agent to ignore it via SendMessage.
15.1.9 Agent Restart After Failure
When an agent stops due to permission issues or other reasons, it needs to be re-spawned:
// A new spawn will create a new agent (e.g., ui-dev-2, ui-dev-3)
// Old agent records remain in team config
// Suggestion: Explicitly state to ignore blockedBy in the new agent prompt
Agent({
name: "ui-dev-3", // New name
mode: "bypassPermissions",
team_name: "calc-dev",
prompt: `You are ui-dev, taking over previous work.
Do not call any Skills. Write code directly.
Ignore any blockedBy tags and start directly.
...`
})
15.1.10 Complete Pitfall Checklist
| Pitfall | Symptom | Solution |
|---|---|---|
| Permission format error | Agent gets stuck when executing Bash commands | Bash(cmd:*) uses a colon, not a space |
| Skill call popup | Agent stops after calling a Skill | Prohibit Skill calls in the prompt |
| tmux cannot find agent | calc-dev session panes are all zsh | Use tmux list-panes -a to find the actual session |
| Notifications not received | Agent completes but team-lead doesn't receive notification | Read inbox file to verify |
| Task dependency reversed | Phase A shows as blocked | Carefully check Task ID and Phase correspondence before creation |
| blockedBy cannot be removed | Cannot clear after mistakenly setting dependency | Tell agent to ignore via SendMessage |
| Context compression | Long sessions lose historical information | Write important status to files |
| Multiple spawn remnants | Multiple dead agents in team config | Clean up with TeamDelete after completion |
15.2 Configuration Reference
15.2.1 Full settings.json
{
"permissions": {
"allow": [
"Bash(ls:*)",
"Bash(cat:*)",
"Bash(head:*)",
"Bash(tail:*)",
"Bash(find:*)",
"Bash(grep:*)",
"Bash(wc:*)",
"Bash(open:*)",
"Bash(which:*)",
"Bash(node:*)",
"Bash(npx:*)",
"Bash(npm:*)",
"Bash(python3:*)",
"Bash(git:*)",
"Bash(mkdir:*)",
"Bash(cp:*)",
"Bash(mv:*)",
"Bash(touch:*)",
"Bash(rm:*)",
"Bash(echo:*)",
"Bash(tmux:*)",
"Bash(bash:*)",
"Edit",
"Write",
"Read",
"WebSearch",
"Skill",
"Agent",
"SendMessage",
"TeamCreate",
"TeamDelete",
"TaskCreate",
"TaskGet",
"TaskList",
"TaskUpdate"
]
}
}
15.2.2 Agent Prompt Template
You are {agent-name}, responsible for {description of duties}.
Important rules:
- Do not call any Skills (e.g., no /planning-with-files)
- Write code directly, do not create plan files
- Planning is already done in task_plan.md, you are only responsible for implementation
First read requirement.md to understand the requirements.
Task 1: {description}
- {specific requirements}
Task 2: {description}
- {specific requirements}
When each task is completed:
1. TaskUpdate to mark as completed
2. SendMessage to notify team-lead
Working directory: {path}
15.2.3 tmux Common Commands Quick Reference
# Create/Attach
tmux new -s calc-dev # Create session
tmux attach -t calc-dev # Attach
# Pane Operations
Ctrl+B β Arrow keys # Switch pane
Ctrl+B β z # Fullscreen current pane (press again to restore)
Ctrl+B β [ # Enter copy mode (scroll)
q # Exit copy mode
# Monitoring
tmux list-sessions # List all sessions
tmux list-panes -a -F '#{session_name}.#{pane_index} #{pane_current_command}'
tmux switch-client -t 2 # Switch to session 2
# Mouse
:set -g mouse on # Enable mouse scroll
15.3 Appendix: Teams File System
~/.claude/
βββ teams/calc-dev/
β βββ config.json # Team configuration (members, agent ID, prompt)
β βββ inboxes/ # Message delivery
β βββ team-lead.json # team-lead's inbox
β βββ ui-dev.json # ui-dev's inbox
β βββ logic-dev.json # logic-dev's inbox
β βββ qa-engineer.json # qa-engineer's inbox
βββ tasks/calc-dev/ # Shared task list
βββ (task files)
Key fields in config.json:
{
"name": "calc-dev",
"leadAgentId": "team-lead@calc-dev",
"members": [
{
"name": "ui-dev",
"agentId": "ui-dev@calc-dev",
"model": "claude-opus-4-7",
"tmuxPaneId": "%12",
"isActive": false
}
]
}
isActive: Whether agent is still running (process alive)tmuxPaneId: Agent's tmux pane identifier (but actually runs in another session)
This tutorial is based on a real development session review from 2026-04-25. Project successfully delivered: dual-mode web calculator, 48+ unit tests passed, DOM integration tests + accessibility tests covered.