Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Pattern documentation for configuring settings in Claude Code plugins (from the official Anthropic claude-code repo).
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
SKILL.md
1---2name: Plugin Settings3description: This skill should be used when the user asks about "plugin settings", "store plugin configuration", "user-configurable plugin", ".local.md files", "plugin state files", "read YAML frontmatter", "per-project plugin settings", or wants to make plugin behavior configurable. Documents the .claude/plugin-name.local.md pattern for storing plugin-specific configuration with YAML frontmatter and markdown content.4version: 0.1.05---67# Plugin Settings Pattern for Claude Code Plugins89## Overview1011Plugins can store user-configurable settings and state in `.claude/plugin-name.local.md` files within the project directory. This pattern uses YAML frontmatter for structured configuration and markdown content for prompts or additional context.1213**Key characteristics:**14- File location: `.claude/plugin-name.local.md` in project root15- Structure: YAML frontmatter + markdown body16- Purpose: Per-project plugin configuration and state17- Usage: Read from hooks, commands, and agents18- Lifecycle: User-managed (not in git, should be in `.gitignore`)1920## File Structure2122### Basic Template2324```markdown25---26enabled: true27setting1: value128setting2: value229numeric_setting: 4230list_setting: ["item1", "item2"]31---3233# Additional Context3435This markdown body can contain:36- Task descriptions37- Additional instructions38- Prompts to feed back to Claude39- Documentation or notes40```4142### Example: Plugin State File4344**.claude/my-plugin.local.md:**45```markdown46---47enabled: true48strict_mode: false49max_retries: 350notification_level: info51coordinator_session: team-leader52---5354# Plugin Configuration5556This plugin is configured for standard validation mode.57Contact @team-lead with questions.58```5960## Reading Settings Files6162### From Hooks (Bash Scripts)6364**Pattern: Check existence and parse frontmatter**6566```bash67#!/bin/bash68set -euo pipefail6970# Define state file path71STATE_FILE=".claude/my-plugin.local.md"7273# Quick exit if file doesn't exist74if [[ ! -f "$STATE_FILE" ]]; then75exit 0 # Plugin not configured, skip76fi7778# Parse YAML frontmatter (between --- markers)79FRONTMATTER=$(sed -n '/^---$/,/^---$/{ /^---$/d; p; }' "$STATE_FILE")8081# Extract individual fields82ENABLED=$(echo "$FRONTMATTER" | grep '^enabled:' | sed 's/enabled: *//' | sed 's/^"\(.*\)"$/\1/')83STRICT_MODE=$(echo "$FRONTMATTER" | grep '^strict_mode:' | sed 's/strict_mode: *//' | sed 's/^"\(.*\)"$/\1/')8485# Check if enabled86if [[ "$ENABLED" != "true" ]]; then87exit 0 # Disabled88fi8990# Use configuration in hook logic91if [[ "$STRICT_MODE" == "true" ]]; then92# Apply strict validation93# ...94fi95```9697See `examples/read-settings-hook.sh` for complete working example.9899### From Commands100101Commands can read settings files to customize behavior:102103```markdown104---105description: Process data with plugin106allowed-tools: ["Read", "Bash"]107---108109# Process Command110111Steps:1121. Check if settings exist at `.claude/my-plugin.local.md`1132. Read configuration using Read tool1143. Parse YAML frontmatter to extract settings1154. Apply settings to processing logic1165. Execute with configured behavior117```118119### From Agents120121Agents can reference settings in their instructions:122123```markdown124---125name: configured-agent126description: Agent that adapts to project settings127---128129Check for plugin settings at `.claude/my-plugin.local.md`.130If present, parse YAML frontmatter and adapt behavior according to:131- enabled: Whether plugin is active132- mode: Processing mode (strict, standard, lenient)133- Additional configuration fields134```135136## Parsing Techniques137138### Extract Frontmatter139140```bash141# Extract everything between --- markers142FRONTMATTER=$(sed -n '/^---$/,/^---$/{ /^---$/d; p; }' "$FILE")143```144145### Read Individual Fields146147**String fields:**148```bash149VALUE=$(echo "$FRONTMATTER" | grep '^field_name:' | sed 's/field_name: *//' | sed 's/^"\(.*\)"$/\1/')150```151152**Boolean fields:**153```bash154ENABLED=$(echo "$FRONTMATTER" | grep '^enabled:' | sed 's/enabled: *//')155# Compare: if [[ "$ENABLED" == "true" ]]; then156```157158**Numeric fields:**159```bash160MAX=$(echo "$FRONTMATTER" | grep '^max_value:' | sed 's/max_value: *//')161# Use: if [[ $MAX -gt 100 ]]; then162```163164### Read Markdown Body165166Extract content after second `---`:167168```bash169# Get everything after closing ---170BODY=$(awk '/^---$/{i++; next} i>=2' "$FILE")171```172173## Common Patterns174175### Pattern 1: Temporarily Active Hooks176177Use settings file to control hook activation:178179```bash180#!/bin/bash181STATE_FILE=".claude/security-scan.local.md"182183# Quick exit if not configured184if [[ ! -f "$STATE_FILE" ]]; then185exit 0186fi187188# Read enabled flag189FRONTMATTER=$(sed -n '/^---$/,/^---$/{ /^---$/d; p; }' "$STATE_FILE")190ENABLED=$(echo "$FRONTMATTER" | grep '^enabled:' | sed 's/enabled: *//')191192if [[ "$ENABLED" != "true" ]]; then193exit 0 # Disabled194fi195196# Run hook logic197# ...198```199200**Use case:** Enable/disable hooks without editing hooks.json (requires restart).201202### Pattern 2: Agent State Management203204Store agent-specific state and configuration:205206**.claude/multi-agent-swarm.local.md:**207```markdown208---209agent_name: auth-agent210task_number: 3.5211pr_number: 1234212coordinator_session: team-leader213enabled: true214dependencies: ["Task 3.4"]215---216217# Task Assignment218219Implement JWT authentication for the API.220221**Success Criteria:**222- Authentication endpoints created223- Tests passing224- PR created and CI green225```226227Read from hooks to coordinate agents:228229```bash230AGENT_NAME=$(echo "$FRONTMATTER" | grep '^agent_name:' | sed 's/agent_name: *//')231COORDINATOR=$(echo "$FRONTMATTER" | grep '^coordinator_session:' | sed 's/coordinator_session: *//')232233# Send notification to coordinator234tmux send-keys -t "$COORDINATOR" "Agent $AGENT_NAME completed task" Enter235```236237### Pattern 3: Configuration-Driven Behavior238239**.claude/my-plugin.local.md:**240```markdown241---242validation_level: strict243max_file_size: 1000000244allowed_extensions: [".js", ".ts", ".tsx"]245enable_logging: true246---247248# Validation Configuration249250Strict mode enabled for this project.251All writes validated against security policies.252```253254Use in hooks or commands:255256```bash257LEVEL=$(echo "$FRONTMATTER" | grep '^validation_level:' | sed 's/validation_level: *//')258259case "$LEVEL" in260strict)261# Apply strict validation262;;263standard)264# Apply standard validation265;;266lenient)267# Apply lenient validation268;;269esac270```271272## Creating Settings Files273274### From Commands275276Commands can create settings files:277278```markdown279# Setup Command280281Steps:2821. Ask user for configuration preferences2832. Create `.claude/my-plugin.local.md` with YAML frontmatter2843. Set appropriate values based on user input2854. Inform user that settings are saved2865. Remind user to restart Claude Code for hooks to recognize changes287```288289### Template Generation290291Provide template in plugin README:292293```markdown294## Configuration295296Create `.claude/my-plugin.local.md` in your project:297298\`\`\`markdown299---300enabled: true301mode: standard302max_retries: 3303---304305# Plugin Configuration306307Your settings are active.308\`\`\`309310After creating or editing, restart Claude Code for changes to take effect.311```312313## Best Practices314315### File Naming316317✅ **DO:**318- Use `.claude/plugin-name.local.md` format319- Match plugin name exactly320- Use `.local.md` suffix for user-local files321322❌ **DON'T:**323- Use different directory (not `.claude/`)324- Use inconsistent naming325- Use `.md` without `.local` (might be committed)326327### Gitignore328329Always add to `.gitignore`:330331```gitignore332.claude/*.local.md333.claude/*.local.json334```335336Document this in plugin README.337338### Defaults339340Provide sensible defaults when settings file doesn't exist:341342```bash343if [[ ! -f "$STATE_FILE" ]]; then344# Use defaults345ENABLED=true346MODE=standard347else348# Read from file349# ...350fi351```352353### Validation354355Validate settings values:356357```bash358MAX=$(echo "$FRONTMATTER" | grep '^max_value:' | sed 's/max_value: *//')359360# Validate numeric range361if ! [[ "$MAX" =~ ^[0-9]+$ ]] || [[ $MAX -lt 1 ]] || [[ $MAX -gt 100 ]]; then362echo "⚠️ Invalid max_value in settings (must be 1-100)" >&2363MAX=10 # Use default364fi365```366367### Restart Requirement368369**Important:** Settings changes require Claude Code restart.370371Document in your README:372373```markdown374## Changing Settings375376After editing `.claude/my-plugin.local.md`:3771. Save the file3782. Exit Claude Code3793. Restart: `claude` or `cc`3804. New settings will be loaded381```382383Hooks cannot be hot-swapped within a session.384385## Security Considerations386387### Sanitize User Input388389When writing settings files from user input:390391```bash392# Escape quotes in user input393SAFE_VALUE=$(echo "$USER_INPUT" | sed 's/"/\\"/g')394395# Write to file396cat > "$STATE_FILE" <<EOF397---398user_setting: "$SAFE_VALUE"399---400EOF401```402403### Validate File Paths404405If settings contain file paths:406407```bash408FILE_PATH=$(echo "$FRONTMATTER" | grep '^data_file:' | sed 's/data_file: *//')409410# Check for path traversal411if [[ "$FILE_PATH" == *".."* ]]; then412echo "⚠️ Invalid path in settings (path traversal)" >&2413exit 2414fi415```416417### Permissions418419Settings files should be:420- Readable by user only (`chmod 600`)421- Not committed to git422- Not shared between users423424## Real-World Examples425426### multi-agent-swarm Plugin427428**.claude/multi-agent-swarm.local.md:**429```markdown430---431agent_name: auth-implementation432task_number: 3.5433pr_number: 1234434coordinator_session: team-leader435enabled: true436dependencies: ["Task 3.4"]437additional_instructions: Use JWT tokens, not sessions438---439440# Task: Implement Authentication441442Build JWT-based authentication for the REST API.443Coordinate with auth-agent on shared types.444```445446**Hook usage (agent-stop-notification.sh):**447- Checks if file exists (line 15-18: quick exit if not)448- Parses frontmatter to get coordinator_session, agent_name, enabled449- Sends notifications to coordinator if enabled450- Allows quick activation/deactivation via `enabled: true/false`451452### ralph-wiggum Plugin453454**.claude/ralph-loop.local.md:**455```markdown456---457iteration: 1458max_iterations: 10459completion_promise: "All tests passing and build successful"460---461462Fix all the linting errors in the project.463Make sure tests pass after each fix.464```465466**Hook usage (stop-hook.sh):**467- Checks if file exists (line 15-18: quick exit if not active)468- Reads iteration count and max_iterations469- Extracts completion_promise for loop termination470- Reads body as the prompt to feed back471- Updates iteration count on each loop472473## Quick Reference474475### File Location476477```478project-root/479└── .claude/480└── plugin-name.local.md481```482483### Frontmatter Parsing484485```bash486# Extract frontmatter487FRONTMATTER=$(sed -n '/^---$/,/^---$/{ /^---$/d; p; }' "$FILE")488489# Read field490VALUE=$(echo "$FRONTMATTER" | grep '^field:' | sed 's/field: *//' | sed 's/^"\(.*\)"$/\1/')491```492493### Body Parsing494495```bash496# Extract body (after second ---)497BODY=$(awk '/^---$/{i++; next} i>=2' "$FILE")498```499500### Quick Exit Pattern501502```bash503if [[ ! -f ".claude/my-plugin.local.md" ]]; then504exit 0 # Not configured505fi506```507508## Additional Resources509510### Reference Files511512For detailed implementation patterns:513514- **`references/parsing-techniques.md`** - Complete guide to parsing YAML frontmatter and markdown bodies515- **`references/real-world-examples.md`** - Deep dive into multi-agent-swarm and ralph-wiggum implementations516517### Example Files518519Working examples in `examples/`:520521- **`read-settings-hook.sh`** - Hook that reads and uses settings522- **`create-settings-command.md`** - Command that creates settings file523- **`example-settings.md`** - Template settings file524525### Utility Scripts526527Development tools in `scripts/`:528529- **`validate-settings.sh`** - Validate settings file structure530- **`parse-frontmatter.sh`** - Extract frontmatter fields531532## Implementation Workflow533534To add settings to a plugin:5355361. Design settings schema (which fields, types, defaults)5372. Create template file in plugin documentation5383. Add gitignore entry for `.claude/*.local.md`5394. Implement settings parsing in hooks/commands5405. Use quick-exit pattern (check file exists, check enabled field)5416. Document settings in plugin README with template5427. Remind users that changes require Claude Code restart543544Focus on keeping settings simple and providing good defaults when settings file doesn't exist.545