spacehub

surshar / claude-code

Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.

Built by Anthropic · github.com/anthropics

claude-code / plugins / plugin-dev / skills / plugin-settings / examples / read-settings-hook.sh
65 lines · 2.2 KBRaw
1#!/bin/bash
2# Example hook that reads plugin settings from .claude/my-plugin.local.md
3# Demonstrates the complete pattern for settings-driven hook behavior
4
5set -euo pipefail
6
7# Define settings file path
8SETTINGS_FILE=".claude/my-plugin.local.md"
9
10# Quick exit if settings file doesn't exist
11if [[ ! -f "$SETTINGS_FILE" ]]; then
12 # Plugin not configured - use defaults or skip
13 exit 0
14fi
15
16# Parse YAML frontmatter (everything between --- markers)
17FRONTMATTER=$(sed -n '/^---$/,/^---$/{ /^---$/d; p; }' "$SETTINGS_FILE")
18
19# Extract configuration fields
20ENABLED=$(echo "$FRONTMATTER" | grep '^enabled:' | sed 's/enabled: *//' | sed 's/^"\(.*\)"$/\1/')
21STRICT_MODE=$(echo "$FRONTMATTER" | grep '^strict_mode:' | sed 's/strict_mode: *//' | sed 's/^"\(.*\)"$/\1/')
22MAX_SIZE=$(echo "$FRONTMATTER" | grep '^max_file_size:' | sed 's/max_file_size: *//')
23
24# Quick exit if disabled
25if [[ "$ENABLED" != "true" ]]; then
26 exit 0
27fi
28
29# Read hook input
30input=$(cat)
31file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty')
32
33# Apply configured validation
34if [[ "$STRICT_MODE" == "true" ]]; then
35 # Strict mode: apply all checks
36 if [[ "$file_path" == *".."* ]]; then
37 echo '{"hookSpecificOutput": {"permissionDecision": "deny"}, "systemMessage": "Path traversal blocked (strict mode)"}' >&2
38 exit 2
39 fi
40
41 if [[ "$file_path" == *".env"* ]] || [[ "$file_path" == *"secret"* ]]; then
42 echo '{"hookSpecificOutput": {"permissionDecision": "deny"}, "systemMessage": "Sensitive file blocked (strict mode)"}' >&2
43 exit 2
44 fi
45else
46 # Standard mode: basic checks only
47 if [[ "$file_path" == "/etc/"* ]] || [[ "$file_path" == "/sys/"* ]]; then
48 echo '{"hookSpecificOutput": {"permissionDecision": "deny"}, "systemMessage": "System path blocked"}' >&2
49 exit 2
50 fi
51fi
52
53# Check file size if configured
54if [[ -n "$MAX_SIZE" ]] && [[ "$MAX_SIZE" =~ ^[0-9]+$ ]]; then
55 content=$(echo "$input" | jq -r '.tool_input.content // empty')
56 content_size=${#content}
57
58 if [[ $content_size -gt $MAX_SIZE ]]; then
59 echo '{"hookSpecificOutput": {"permissionDecision": "deny"}, "systemMessage": "File exceeds configured max size: '"$MAX_SIZE"' bytes"}' >&2
60 exit 2
61 fi
62fi
63
64# All checks passed
65exit 0