1. Architecture Overview
The anthropics/claude-code repository is the official community collection of plugins, examples, and tooling for extending Claude Code. It is not the Claude Code application itself — it is a plugin and example ecosystem that Claude Code discovers and loads at runtime.
System Architecture
Claude Code Runtime
├── Plugin Discovery
│ └── Scans plugin directories for standard structure
│ ├── commands/ → Registers slash commands
│ ├── agents/ → Registers spawnable agents
│ ├── skills/ → Registers knowledge packs (SKILL.md)
│ ├── hooks/ → Registers event hooks (hooks.json)
│ └── scripts/ → Utility scripts (not auto-discovered)
│
├── Hook Event Pipeline
│ ├── SessionStart → Fires on session init
│ ├── UserPromptSubmit → Fires on user input
│ ├── PreToolUse → Fires before tool execution
│ ├── PostToolUse → Fires after tool execution
│ └── Stop → Fires when Claude stops
│
├── Settings Layer
│ ├── Managed settings (MDM / settings.json)
│ ├── Permission rules (ask/deny/allow)
│ └── Sandbox configuration
│
└── MCP Server Integration
├── stdio (local process)
├── SSE (hosted cloud)
└── HTTP (REST API)
Note
The plugin discovery mechanism uses convention-over-configuration. Plugins need no registration file beyond the standard directory structure — Claude Code auto-discovers components by scanning known subdirectory names.
Component Inventory
| Component | Path | Role |
|---|---|---|
| Hookify core | plugins/hookify/core/ |
Rule engine + config loader (Python) |
| Security guidance | plugins/security-guidance/hooks/ |
Security pattern detection hook (Python) |
| Code review | plugins/code-review/commands/ |
Code review slash command |
| PR review toolkit | plugins/pr-review-toolkit/ |
6 specialized review agents + orchestrator command |
| Commit commands | plugins/commit-commands/commands/ |
Git commit/push/PR workflow commands |
| Feature dev | plugins/feature-dev/ |
3 agents + feature development command |
| Plugin dev | plugins/plugin-dev/ |
Meta-plugin: 7 skills, 3 agents, 1 command |
| Agent SDK dev | plugins/agent-sdk-dev/ |
Agent SDK scaffolding command + verifier agents |
| Ralph Wiggum | plugins/ralph-wiggum/ |
Looping task automation |
| Explanatory style | plugins/explanatory-output-style/ |
SessionStart hook for explanatory output |
| Learning style | plugins/learning-output-style/ |
SessionStart hook for teaching output |
| Claude Opus 4.5 migration | plugins/claude-opus-4-5-migration/ |
Model migration skill pack |
| Frontend design | plugins/frontend-design/ |
Frontend design skill |
| Settings examples | examples/settings/ |
3 settings presets (strict, lax, sandbox) |
| Hook examples | examples/hooks/ |
Bash command validator example |
| MDM examples | examples/mdm/ |
Managed device templates (macOS + Windows) |
| DevContainer script | Script/ |
PowerShell DevContainer automation |
| Issue scripts | scripts/ |
TypeScript/bash issue management scripts |
Technology Stack
| Layer | Technology |
|---|---|
| Hook scripts | Python 3, Bash |
| Rule engine | Python 3 (dataclasses, regex, glob) |
| Commands / Agents / Skills | Markdown with YAML frontmatter |
| Hook configuration | JSON (hooks.json) |
| Settings | JSON |
| MCP configuration | JSON (stdio/SSE/HTTP) |
| DevContainer script | PowerShell |
| Issue management scripts | TypeScript, Bash |
| MDM templates | Apple plist/mobileconfig, Windows ADMX/ADML, PowerShell |
Data Flow: Hook Execution
1. User or Claude triggers a tool call (e.g., Bash "rm -rf /tmp")
2. Claude Code fires PreToolUse event
3. Event pipeline checks hooks.json for matching hooks
└── matcher: "Bash" matches tool_name "Bash"
4. Hook script receives JSON on stdin:
{"tool_name": "Bash", "tool_input": {"command": "rm -rf /tmp"}, "session_id": "..."}
5. Hook evaluates input:
├── Exit 0 → Allow (operation proceeds)
├── Exit 1 → Error (stderr to user, operation proceeds)
└── Exit 2 → Block (stderr JSON with systemMessage to Claude)
6. If blocked: Claude sees the systemMessage and adjusts behavior
Data Flow: Hookify Rule Evaluation
1. PreToolUse/PostToolUse/Stop/UserPromptSubmit event fires
2. Hookify hook script runs (e.g., pretooluse.py)
3. config_loader.py scans .claude/hookify.*.local.md files
4. Each file's YAML frontmatter is parsed into a Rule object
5. rule_engine.py evaluates all enabled rules against input_data
├── Tool matcher check (e.g., "Bash", "Edit|Write", "*")
├── All conditions must match (AND logic)
│ └── Each condition: extract field → apply operator → match pattern
└── Results accumulated: blocking rules take priority over warnings
6. Combined response:
├── Block → {"hookSpecificOutput": {"permissionDecision": "deny"}, "systemMessage": "..."}
├── Warn → {"systemMessage": "..."}
└── None → {} (allow)
2. System Requirements
| Requirement | Version | Notes |
|---|---|---|
| Claude Code | Latest with plugin support | Core runtime — this repo extends it |
| Python | 3.8+ | Required by hookify core and security-guidance hooks |
| jq | Any | Recommended for bash hook scripts that parse JSON |
| Git | Any | For cloning and contributing |
| Docker | 20.10+ | Only for DevContainer script |
| Podman | 4.0+ | Alternative to Docker for DevContainer script |
| devcontainer CLI | Any | Required by DevContainer script |
| Node.js / npm | 18+ | Only for issue management scripts (TypeScript) |
| PowerShell | 5.1+ / 7+ | Only for DevContainer script (Windows) |
3. Configuration Reference
Settings JSON Schema
Settings are configured via JSON files. The repository provides three example presets demonstrating the available fields.
Permission Settings
| Key | Type | Default | Description | Required |
|---|---|---|---|---|
permissions.disableBypassPermissionsMode |
string |
(unset) | Set to "disable" to prevent users from bypassing permission checks |
No |
permissions.ask |
string[] |
[] |
Tool names that require explicit user approval before each use | No |
permissions.deny |
string[] |
[] |
Tool names that are completely blocked | No |
Sandbox Settings
| Key | Type | Default | Description | Required |
|---|---|---|---|---|
sandbox.enabled |
boolean |
false |
Enable command sandboxing | No |
sandbox.autoAllowBashIfSandboxed |
boolean |
false |
Auto-approve Bash commands when sandbox is active | No |
sandbox.allowUnsandboxedCommands |
boolean |
true |
Allow commands to run outside the sandbox | No |
sandbox.excludedCommands |
string[] |
[] |
Commands excluded from sandboxing | No |
sandbox.enableWeakerNestedSandbox |
boolean |
false |
Allow a weaker sandbox for nested operations | No |
Sandbox Network Settings
| Key | Type | Default | Description | Required |
|---|---|---|---|---|
sandbox.network.allowUnixSockets |
string[] |
[] |
Specific Unix socket paths to allow | No |
sandbox.network.allowAllUnixSockets |
boolean |
false |
Allow all Unix socket connections | No |
sandbox.network.allowLocalBinding |
boolean |
false |
Allow binding to local ports | No |
sandbox.network.allowedDomains |
string[] |
[] |
Domains allowed for outbound connections | No |
sandbox.network.httpProxyPort |
integer|null |
null |
HTTP proxy port for sandboxed traffic | No |
sandbox.network.socksProxyPort |
integer|null |
null |
SOCKS proxy port for sandboxed traffic | No |
Management Settings
| Key | Type | Default | Description | Required |
|---|---|---|---|---|
allowManagedPermissionRulesOnly |
boolean |
false |
Only allow permission rules set by managed configuration (MDM) | No |
allowManagedHooksOnly |
boolean |
false |
Only allow hooks set by managed configuration | No |
strictKnownMarketplaces |
string[] |
(unset) | Restrict plugin sources to listed marketplaces | No |
Important
When
allowManagedPermissionRulesOnlyistrue, users cannot add their own permission rules — only centrally managed rules apply. This is the primary enterprise control lever for locking down Claude Code.
Environment Variables
| Variable | Type | Default | Description | Required |
|---|---|---|---|---|
ENABLE_SECURITY_REMINDER |
string |
"1" |
Set to "0" to disable the security-guidance hook entirely |
No |
CLAUDE_PLUGIN_ROOT |
string |
(set by runtime) | Absolute path to the plugin's root directory; use in hooks.json paths | No (auto) |
CLAUDE_PROJECT_DIR |
string |
(set by runtime) | Absolute path to the current project directory | No (auto) |
CLAUDE_ENV_FILE |
string |
(set by runtime) | Path to environment file for persisting variables across hooks | No (auto) |
Hook Configuration (hooks.json)
Each plugin's hooks/hooks.json defines event subscriptions:
{
"description": "Optional description of this hook set",
"hooks": {
"<EventName>": [
{
"matcher": "<ToolName|ToolName2>",
"hooks": [
{
"type": "command",
"command": "<executable> ${CLAUDE_PLUGIN_ROOT}/path/to/script"
}
]
}
]
}
}
Event types and their input data:
| Event | Input Fields | Matcher Applies To |
|---|---|---|
PreToolUse |
tool_name, tool_input, session_id, hook_event_name |
tool_name |
PostToolUse |
tool_name, tool_input, tool_output, session_id |
tool_name |
Stop |
reason, transcript_path, session_id |
N/A |
SessionStart |
session_id |
N/A |
UserPromptSubmit |
user_prompt, session_id |
N/A |
Hook output JSON schema (exit code 2):
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny|ask|allow"
},
"systemMessage": "Message visible to Claude explaining why the operation was blocked",
"decision": "block",
"reason": "For Stop events — reason for blocking"
}
Hookify Rule File Format
Rule files are markdown with YAML frontmatter, stored as .claude/hookify.{name}.local.md:
| Frontmatter Key | Type | Default | Description |
|---|---|---|---|
name |
string |
"unnamed" |
Rule identifier |
enabled |
boolean |
true |
Whether the rule is active |
event |
string |
"all" |
Event filter: bash, file, stop, all |
action |
string |
"warn" |
warn (show message) or block (deny operation) |
pattern |
string |
(none) | Legacy simple pattern (auto-converted to condition) |
tool_matcher |
string |
(none) | Tool name filter: "Bash", "Edit|Write", "*" |
conditions |
list |
[] |
List of condition objects (see below) |
Condition object:
| Key | Type | Description |
|---|---|---|
field |
string |
Field to extract from input (command, file_path, new_text, content, reason, transcript, user_prompt) |
operator |
string |
Match operator (regex_match, contains, equals, not_contains, starts_with, ends_with) |
pattern |
string |
Value to match against |
MCP Server Configuration
Three transport types are supported in plugin MCP configurations:
stdio — local process:
| Key | Type | Description |
|---|---|---|
command |
string |
Executable to run |
args |
string[] |
Command arguments |
env |
object |
Environment variables |
SSE — hosted cloud service:
| Key | Type | Description |
|---|---|---|
type |
"sse" |
Transport type |
url |
string |
SSE endpoint URL |
headers |
object |
Optional HTTP headers |
HTTP — REST API:
| Key | Type | Description |
|---|---|---|
type |
"http" |
Transport type |
url |
string |
HTTP endpoint URL |
headers |
object |
HTTP headers (e.g., Authorization) |
Command Frontmatter Reference
Slash commands are markdown files with optional YAML frontmatter:
| Field | Type | Default | Description |
|---|---|---|---|
description |
string |
(none) | Brief description shown in /help |
allowed-tools |
string |
(all) | Comma-separated tool whitelist (e.g., Read, Bash(git:*)) |
model |
string |
(default) | Model override: sonnet, opus, haiku |
argument-hint |
string |
(none) | Argument documentation (e.g., [pr-number] [priority]) |
disable-model-invocation |
boolean |
false |
When true, command is manual-only (not auto-invoked) |
Dynamic argument substitution:
| Syntax | Description |
|---|---|
$ARGUMENTS |
All arguments as a single string |
$1, $2, $3 |
Positional arguments |
@path/to/file |
Include file contents inline |
!`command` |
Execute bash command and include output |
4. Deployment & Scaling
Enterprise Deployment via MDM
The examples/mdm/ directory provides templates for managed device management deployment on macOS and Windows.
macOS Deployment
| File | Format | Purpose |
|---|---|---|
com.anthropic.claudecode.mobileconfig |
Apple Configuration Profile | Full MDM profile for deployment via MDM solutions (Jamf, Mosyle, etc.) |
com.anthropic.claudecode.plist |
Property List | Preference domain for defaults write or profile manager |
Windows Deployment
| File | Format | Purpose |
|---|---|---|
ClaudeCode.admx |
Group Policy Administrative Template | Defines Claude Code policy settings for Group Policy Editor |
en-US/ClaudeCode.adml |
ADML Language File | English language strings for the ADMX template |
Set-ClaudeCodePolicy.ps1 |
PowerShell Script | Programmatically set Claude Code policies via registry |
Minimal managed settings (prevents permission bypass):
{
"permissions": {
"disableBypassPermissionsMode": "disable"
}
}
Warning
MDM-deployed settings take precedence over user-local settings. Test thoroughly in a staging environment before pushing to production devices.
DevContainer Deployment
The PowerShell script at Script/run_devcontainer_claude_code.ps1 automates containerized Claude Code environments.
Parameters:
| Parameter | Required | Values | Description |
|---|---|---|---|
-Backend |
Yes | docker, podman |
Container backend to use |
Podman flow:
podman machine init claudeVM— creates the VMpodman machine start claudeVM -q— starts the VMpodman system connection default claudeVM— sets default connectiondevcontainer up --workspace-folder . --docker-path podman- Finds container by label
devcontainer.local_folder={cwd} podman exec -it {id} zsh -c 'claude; exec zsh'
Docker flow:
- Verifies Docker Desktop is running via
docker info devcontainer up --workspace-folder .- Finds container by label
devcontainer.local_folder={cwd} docker exec -it {id} zsh -c 'claude; exec zsh'
5. Operations
Issue Management Scripts
The scripts/ directory contains automation for GitHub issue triage:
| Script | Language | Purpose |
|---|---|---|
auto-close-duplicates.ts |
TypeScript | Automatically close duplicate issues |
backfill-duplicate-comments.ts |
TypeScript | Add duplicate markers to existing issues |
comment-on-duplicates.sh |
Bash | Comment on duplicate issues |
edit-issue-labels.sh |
Bash | Batch edit issue labels |
gh.sh |
Bash | GitHub CLI wrapper |
issue-lifecycle.ts |
TypeScript | Manage issue lifecycle transitions |
lifecycle-comment.ts |
TypeScript | Add lifecycle comments to issues |
sweep.ts |
TypeScript | Sweep stale issues |
Tip
These scripts are designed for repository maintainers. They require the
ghCLI to be authenticated and use the GitHub API for issue management.
Plugin Maintenance
Adding a new plugin:
- Create the plugin directory under
plugins/ - Add a
README.mddescribing the plugin - Create component subdirectories as needed (
commands/,hooks/,skills/,agents/) - For hooks: create
hooks/hooks.jsonwith event subscriptions - Test with
claude --debug
Updating hook scripts:
- Edit the script
- Lint with
hook-linter.sh - Test with
test-hook.shusing sample input - Validate
hooks.jsonwithvalidate-hook-schema.sh
Note
The hook development scripts in
plugins/plugin-dev/skills/hook-development/scripts/are the canonical tools for validating and testing hooks across the entire repository.
Security State File Cleanup
The security-guidance hook creates session-scoped state files at ~/.claude/security_warnings_state_{session_id}.json. These are cleaned up automatically:
- 10% chance per hook invocation: scans
~/.claude/for state files older than 30 days and removes them - Manual cleanup:
rm ~/.claude/security_warnings_state_*.json
6. Performance
Hook Execution Performance
Hook scripts run synchronously in the tool execution pipeline. Performance considerations:
- Hookify regex caching: The
RuleEngineusesfunctools.lru_cache(maxsize=128)for compiled regex patterns, avoiding re-compilation on repeated evaluations - Rule file scanning:
config_loader.load_rules()scans.claude/hookify.*.local.mdviaglob.glob()on every hook invocation — keep the number of rule files reasonable (< 50) - Security-guidance state I/O: Each invocation reads/writes a JSON state file; this is fast for typical session sizes but could slow down with thousands of unique file-pattern combinations in a single session
Hook timeout guidelines:
- Hooks should complete within 60 seconds (Claude Code enforces timeouts)
- Avoid
sleep, network calls, or long-running computations in hooks - The hook-linter checks for
sleep [0-9]{3,}andwhile truepatterns
Hookify Condition Evaluation
All conditions within a rule use AND logic — every condition must match for the rule to fire. Rules are evaluated sequentially; the first blocking rule short-circuits warning-only rules.
The field extraction path for each tool type:
| Tool | Fields Available |
|---|---|
Bash |
command |
Write |
file_path, content |
Edit |
file_path, new_string/new_text, old_string/old_text, content |
MultiEdit |
file_path, new_text/content (concatenated from all edits) |
| Stop event | reason, transcript (reads from transcript_path file) |
| UserPromptSubmit | user_prompt |
7. Security
Security-Guidance Hook — Pattern Coverage
The security-guidance plugin implements a PreToolUse hook that intercepts Edit, Write, and MultiEdit operations. It checks both file paths and content against 9 built-in patterns:
| # | Rule Name | Detection Method | Vulnerability Class |
|---|---|---|---|
| 1 | github_actions_workflow |
Path match: .github/workflows/*.yml|yaml |
Command injection in CI/CD |
| 2 | child_process_exec |
Substring: child_process.exec, exec(, execSync( |
Shell injection |
| 3 | new_function_injection |
Substring: new Function |
Code injection |
| 4 | eval_injection |
Substring: eval( |
Arbitrary code execution |
| 5 | react_dangerously_set_html |
Substring: dangerouslySetInnerHTML |
XSS |
| 6 | document_write_xss |
Substring: document.write |
XSS |
| 7 | innerHTML_xss |
Substring: .innerHTML =, .innerHTML= |
XSS |
| 8 | pickle_deserialization |
Substring: pickle |
Deserialization RCE |
| 9 | os_system_injection |
Substring: os.system, from os import system |
Command injection |
Caution
The security-guidance patterns use simple substring matching, not AST analysis. They will produce false positives (e.g., a comment mentioning
eval() and may miss obfuscated patterns. They are a first-line warning system, not a security guarantee.
Session-scoped deduplication: Each warning fires once per unique {file_path}-{rule_name} combination per session. State is stored at ~/.claude/security_warnings_state_{session_id}.json.
Disabling: Set ENABLE_SECURITY_REMINDER=0 in the environment. The hook checks this variable on every invocation and exits immediately if disabled.
Hook Script Security Best Practices
The hook-linter (plugins/plugin-dev/skills/hook-development/scripts/hook-linter.sh) enforces these security checks:
| Check | Risk | Mitigation |
|---|---|---|
| Unquoted variables | Command/path injection | Always use "$variable" |
Missing set -euo pipefail |
Silent failures, undefined variables | Add to every bash hook |
| Hardcoded absolute paths | Portability, path traversal | Use $CLAUDE_PLUGIN_ROOT or $CLAUDE_PROJECT_DIR |
| Missing shebang | Unpredictable interpreter | Always include #!/bin/bash or #!/usr/bin/env python3 |
| Missing input validation | Unexpected null/empty fields | Check for empty values before processing |
| Error output not to stderr | Error messages lost | Use >&2 for all error output |
| Long-running operations | Hook timeout, UX degradation | Avoid sleep, network calls, loops |
Sandbox Configuration Security Model
The sandbox settings control bash command execution:
sandbox.enabled: true— all Bash tool calls run in a sandboxed environmentsandbox.autoAllowBashIfSandboxed: false— even sandboxed commands require approval (defense in depth)sandbox.allowUnsandboxedCommands: false— no escape from the sandboxsandbox.network.*— fine-grained network access controls within the sandbox
Warning
The
enableWeakerNestedSandboxsetting reduces sandbox protections for nested operations. Only enable this if a specific tool requires it and you understand the security implications.
MDM Security Controls
Managed settings pushed via MDM take precedence over user-local settings:
disableBypassPermissionsMode: "disable"— prevents users from switching to a permissive modeallowManagedPermissionRulesOnly: true— users cannot add their own permission rulesallowManagedHooksOnly: true— users cannot register their own hooks
These three settings together create a fully centrally-controlled Claude Code environment suitable for enterprise deployment.
8. Developer Internals
Repository Structure
anthropics/claude-code/
├── plugins/ # Community plugins
│ ├── hookify/ # Rule engine for markdown-based hooks
│ │ ├── core/ # Python rule engine
│ │ │ ├── config_loader.py # Rule file parser (YAML frontmatter + conditions)
│ │ │ └── rule_engine.py # Condition evaluator (regex, contains, equals, etc.)
│ │ ├── commands/ # Slash commands (hookify, configure, list, help)
│ │ ├── hooks/ # Hook entry points (pretooluse.py, posttooluse.py, stop.py, userpromptsubmit.py)
│ │ ├── matchers/ # Tool matching logic
│ │ ├── agents/ # Conversation analyzer agent
│ │ ├── skills/ # Writing rules skill
│ │ ├── utils/ # Shared utilities
│ │ └── examples/ # Example rule files
│ ├── security-guidance/ # Security pattern detection
│ │ └── hooks/ # hooks.json + security_reminder_hook.py
│ ├── code-review/ # Code review command
│ ├── pr-review-toolkit/ # PR review agents + command
│ ├── commit-commands/ # Git workflow commands
│ ├── feature-dev/ # Feature development agents + command
│ ├── plugin-dev/ # Plugin development toolkit
│ │ ├── commands/ # create-plugin command
│ │ ├── agents/ # agent-creator, plugin-validator, skill-reviewer
│ │ └── skills/ # 7 skills covering every plugin component type
│ ├── agent-sdk-dev/ # Agent SDK development
│ ├── ralph-wiggum/ # Looping task automation
│ ├── explanatory-output-style/ # Explanatory output SessionStart hook
│ ├── learning-output-style/ # Learning output SessionStart hook
│ ├── claude-opus-4-5-migration/ # Model migration skill
│ └── frontend-design/ # Frontend design skill
├── examples/
│ ├── hooks/ # Example hook scripts
│ ├── settings/ # Settings presets (strict, lax, sandbox)
│ └── mdm/ # MDM deployment templates
│ ├── macos/ # plist + mobileconfig
│ └── windows/ # ADMX + ADML + PowerShell
├── Script/ # DevContainer automation (PowerShell)
├── scripts/ # Issue management automation (TypeScript/Bash)
├── README.md
├── CHANGELOG.md
├── LICENSE.md
└── SECURITY.md
Module Reference
plugins/hookify/core/config_loader.py
The configuration loader responsible for parsing hookify rule files.
Classes:
| Class | Description |
|---|---|
Condition |
Dataclass representing a single match condition (field, operator, pattern) |
Rule |
Dataclass representing a complete hookify rule (name, enabled, event, conditions, action, message) |
Key functions:
| Function | Signature | Description |
|---|---|---|
extract_frontmatter |
(content: str) → tuple[Dict, str] |
Parses YAML frontmatter from markdown content. Handles simple key-value pairs, list items, and multi-line dictionary items within lists. Returns (frontmatter_dict, message_body). |
load_rules |
(event: Optional[str]) → List[Rule] |
Scans .claude/hookify.*.local.md files via glob.glob(). Filters by event type and enabled status. Returns list of Rule objects. |
load_rule_file |
(file_path: str) → Optional[Rule] |
Loads and parses a single rule file. Returns None on parse errors (with stderr warning). |
Frontmatter parsing details:
The extract_frontmatter function implements a custom YAML parser (not a full YAML library) that handles:
- Simple
key: valuepairs (with boolean coercion fortrue/false) - List items (
- item) - Inline dict list items (
- field: command, operator: regex_match) - Multi-line dict list items (indented continuation lines)
- String unquoting (strips surrounding
"and')
Legacy pattern conversion: When a rule uses the legacy pattern field instead of conditions, the loader auto-converts it into a Condition with operator: regex_match. The inferred field depends on the event type: bash → command, file → new_text, other → content.
plugins/hookify/core/rule_engine.py
The evaluation engine that matches rules against hook input data.
Class: RuleEngine
| Method | Signature | Description |
|---|---|---|
evaluate_rules |
(rules: List[Rule], input_data: Dict) → Dict |
Evaluates all rules against input. Blocking rules take priority. Returns response dict with systemMessage, hookSpecificOutput, and/or decision. Returns {} if no rules match. |
_rule_matches |
(rule: Rule, input_data: Dict) → bool |
Checks tool matcher, then evaluates all conditions (AND logic). Rules with no conditions never match. |
_matches_tool |
(matcher: str, tool_name: str) → bool |
Supports * (match all) and |-separated alternatives. |
_check_condition |
(condition, tool_name, tool_input, input_data) → bool |
Extracts field value and applies operator. |
_extract_field |
(field, tool_name, tool_input, input_data) → Optional[str] |
Field extraction with tool-specific logic (see Performance section). |
_regex_match |
(pattern: str, text: str) → bool |
Cached regex evaluation via compile_regex(). |
Global function:
| Function | Description |
|---|---|
compile_regex(pattern: str) → re.Pattern |
@lru_cache(maxsize=128) — compiles and caches regex patterns with re.IGNORECASE. |
Blocking response format by event type:
| Event | Response Structure |
|---|---|
Stop |
{"decision": "block", "reason": "...", "systemMessage": "..."} |
PreToolUse / PostToolUse |
{"hookSpecificOutput": {"hookEventName": "...", "permissionDecision": "deny"}, "systemMessage": "..."} |
| Other events | {"systemMessage": "..."} |
plugins/security-guidance/hooks/security_reminder_hook.py
Standalone Python hook script (no imports from hookify — self-contained).
Functions:
| Function | Description |
|---|---|
debug_log(message) |
Appends timestamped debug messages to /tmp/security-warnings-log.txt |
get_state_file(session_id) |
Returns ~/.claude/security_warnings_state_{session_id}.json |
cleanup_old_state_files() |
Removes state files older than 30 days from ~/.claude/ |
load_state(session_id) |
Loads shown warning keys from session state file |
save_state(session_id, shown_warnings) |
Persists shown warning keys to session state file |
check_patterns(file_path, content) |
Iterates SECURITY_PATTERNS list — checks path-based then content-based patterns. Returns (rule_name, reminder) or (None, None). |
extract_content_from_input(tool_name, tool_input) |
Extracts checkable content: Write → content, Edit → new_string, MultiEdit → concatenated new_string values |
main() |
Entry point: reads JSON from stdin, checks patterns, manages session state, exits 0 (allow) or 2 (block with warning) |
Pattern matching order: Patterns are evaluated in definition order. The first match wins — subsequent patterns for the same file are not checked in that invocation.
Code Patterns and Conventions
Plugin structure convention:
Every plugin follows this directory layout:
plugin-name/
├── README.md # Plugin documentation (required)
├── commands/ # Slash commands as .md files
├── agents/ # Agent definitions as .md files
├── skills/ # Skill packs
│ └── skill-name/
│ ├── SKILL.md # Core skill content
│ ├── references/ # Deep-dive documentation
│ └── examples/ # Working examples
├── hooks/
│ ├── hooks.json # Event subscriptions
│ └── *.py / *.sh # Hook scripts
└── scripts/ # Utility scripts (not auto-discovered)
Path portability: All hook configurations use ${CLAUDE_PLUGIN_ROOT} for plugin-relative paths rather than hardcoded absolute paths. This makes plugins portable across machines and users.
Hook script patterns:
- All bash hooks start with
set -euo pipefail - Input is read from stdin (
input=$(cat)for bash,sys.stdin.read()for Python) - JSON parsing uses
jqin bash,json.loads()in Python - Exit code 0 = allow, 1 = error (user-visible), 2 = block (Claude-visible)
- Error output goes to stderr (
>&2in bash,file=sys.stderrin Python)
Progressive disclosure in skills:
Skills use a three-tier structure to manage context window usage:
SKILL.md(~1,500-2,500 words) — core concepts, loaded automaticallyreferences/— detailed specifications, loaded on demandexamples/— working code examples, loaded on demand
Hookify conditions inline format:
conditions:
- field: command, operator: regex_match, pattern: "rm\\s+-rf"
Or multi-line:
conditions:
- field: command
operator: regex_match
pattern: "rm\\s+-rf"
Both formats are supported by the custom YAML parser in config_loader.py.
Testing
Hook testing tools:
| Tool | Path | Purpose |
|---|---|---|
hook-linter.sh |
plugins/plugin-dev/skills/hook-development/scripts/ |
Static analysis of hook scripts |
test-hook.sh |
plugins/plugin-dev/skills/hook-development/scripts/ |
Runtime testing with sample input |
validate-hook-schema.sh |
plugins/plugin-dev/skills/hook-development/scripts/ |
Validate hooks.json structure |
Testing workflow:
- Lint:
./hook-linter.sh my-hook.sh - Generate sample input:
./test-hook.sh --create-sample PreToolUse > test-input.json - Test:
./test-hook.sh -v my-hook.sh test-input.json - Validate config:
./validate-hook-schema.sh hooks/hooks.json - Integration test:
claude --debug
Unit testing (hookify core):
Both config_loader.py and rule_engine.py include if __name__ == '__main__': blocks with inline test cases:
config_loader.py: Tests frontmatter parsing with a simple rule and outputs parsed frontmatter, message, and Rule objectrule_engine.py: Tests rule evaluation against matching and non-matching Bash inputs
Potential Improvements
- Full YAML parser: The custom YAML parser in
config_loader.pyhandles a subset of YAML. UsingPyYAMLwould add robustness for edge cases (nested structures, multiline strings, anchors) - Async hook evaluation: Hooks currently run synchronously. Parallel evaluation would reduce latency when multiple plugins register hooks for the same event
- Hook testing framework: The shell-based testing scripts could be complemented by a Python test suite with proper assertions and CI integration
- Plugin manifest (
plugin.json): The plugin-dev skill references aplugin.jsonmanifest format, but the existing plugins in this repository don't use it — they rely on convention-based discovery. Formalizing this would enable dependency management and version pinning - Security pattern extensibility: The security-guidance hook's patterns are hardcoded. A configuration file (similar to hookify rules) would allow admins to add custom patterns without modifying the Python script
Contributing
Plugin contributions:
- Create a new directory under
plugins/following the standard structure - Include a
README.mdwith plugin description, usage, and examples - If adding hooks: include
hooks.jsonand use${CLAUDE_PLUGIN_ROOT}for paths - Lint hook scripts with
hook-linter.sh - Test hooks with
test-hook.sh - Submit a pull request
Code style:
- Python: Standard library only (no external dependencies beyond what Claude Code provides)
- Bash: Always
set -euo pipefail, quote all variables, usejqfor JSON - Markdown: YAML frontmatter for metadata, content body for prompts/messages
- JSON: 2-space indentation, no trailing commas
9. Integration Points
MCP Server Integration
Plugins can bundle MCP server configurations for three transport types:
| Transport | Config Location | Use Case |
|---|---|---|
| stdio | Plugin mcpServers config |
Local tools (filesystem, database, custom scripts) |
| SSE | Plugin mcpServers config |
Hosted cloud services (Asana, GitHub) |
| HTTP | Plugin mcpServers config |
REST APIs with authentication |
Environment variable substitution (${VAR_NAME}) is supported in all MCP config fields, enabling secrets to be injected at runtime without hardcoding.
Claude Code Runtime Integration
| Integration Point | Mechanism | Example |
|---|---|---|
| Slash commands | Markdown files in commands/ |
/code-review, /commit, /review-pr |
| Sub-agents | Markdown files in agents/ |
code-architect, code-reviewer, plugin-validator |
| Knowledge packs | SKILL.md in skills/ |
Plugin structure, hook development, MCP integration |
| Event hooks | hooks.json + scripts in hooks/ |
Security checks, output style, bash validation |
| Environment | $CLAUDE_ENV_FILE writes in SessionStart hooks |
Project type detection, CI detection |
| Settings | JSON files applied via MDM or manual copy | Permission rules, sandbox config |
External Dependencies
This repository has zero runtime package dependencies. All code uses language standard libraries:
- Python hooks:
json,os,sys,re,glob,random,datetime,functools,dataclasses,typing - Bash hooks:
jq(recommended but not strictly required),cat,echo,grep,sed - TypeScript scripts (issue management only): requires Node.js runtime