Thank you for your interest in contributing! This document provides guidelines and instructions for contributing.
- Python 3.11 or higher
- pip and venv
# Clone the repository
git clone https://github.com/Edmonds-Commerce-Limited/claude-code-hooks-daemon.git
cd claude-code-hooks-daemon
# Create virtual environment
python3 -m venv venv
source venv/bin/activate
# Install in development mode with dev dependencies
pip install -e ".[dev]"# Run all tests with coverage
pytest
# Run specific test file
pytest tests/unit/handlers/test_destructive_git.py
# Run with verbose output
pytest -v
# Run only unit tests
pytest tests/unit/
# Run only integration tests
pytest tests/integration/We use several tools to maintain code quality:
# Run all QA checks
./scripts/qa/run_all.sh
# Or individually:
./scripts/qa/run_lint.sh # Ruff linting
./scripts/qa/run_type_check.sh # MyPy type checking
./scripts/qa/run_format_check.sh # Black formatting
./scripts/qa/run_tests.sh # Pytest with coverageInstall pre-commit hooks to run checks automatically:
pre-commit installHandlers are organised by hook event type:
pre_tool_use/- Before tool execution (most common)post_tool_use/- After tool executionsession_start/- When Claude Code session beginssession_end/- When session endspre_compact/- Before conversation compactionuser_prompt_submit/- When user submits promptpermission_request/- Permission system eventsnotification/- Notification eventsstop/- Stop eventssubagent_stop/- Subagent completion events
Create test file before implementation:
# tests/unit/handlers/test_my_handler.py
import pytest
from claude_code_hooks_daemon.handlers.pre_tool_use.my_handler import MyHandler
class TestMyHandler:
@pytest.fixture
def handler(self):
return MyHandler()
def test_init_sets_correct_properties(self, handler):
assert handler.name == "my-handler"
assert handler.priority == 50
assert handler.terminal is True
def test_matches_target_pattern(self, handler):
hook_input = {"tool_name": "Bash", "tool_input": {"command": "target"}}
assert handler.matches(hook_input) is True
def test_does_not_match_other_commands(self, handler):
hook_input = {"tool_name": "Bash", "tool_input": {"command": "ls"}}
assert handler.matches(hook_input) is False# src/claude_code_hooks_daemon/handlers/pre_tool_use/my_handler.py
from claude_code_hooks_daemon.core import Handler, HookResult
from claude_code_hooks_daemon.core.utils import get_bash_command
class MyHandler(Handler):
"""Short description of what this handler does."""
def __init__(self) -> None:
super().__init__(
name="my-handler",
priority=50,
terminal=True # Set False for advisory/non-blocking handlers
)
def matches(self, hook_input: dict) -> bool:
"""Check if this handler should execute."""
command = get_bash_command(hook_input)
if not command:
return False
return "target" in command
def handle(self, hook_input: dict) -> HookResult:
"""Execute handler logic."""
return HookResult(
decision="deny",
reason="Explanation of why operation was blocked"
)Add to the appropriate entry point module:
src/claude_code_hooks_daemon/hooks/pre_tool_use.py
And to the daemon controller:
src/claude_code_hooks_daemon/daemon/controller.py
Add handler config to default YAML template in install.py.
- 5: Test/debug handlers (hello_world)
- 10-20: Safety handlers (destructive operations)
- 25-35: Code quality handlers (linting, TDD)
- 36-55: Workflow handlers (planning, npm)
- 56-60: Advisory handlers (spelling)
- Terminal (
terminal=True): Stops dispatch on match, returns result immediately - Non-Terminal (
terminal=False): Continues dispatch, accumulates context
Handlers should fail open - never block operations due to internal errors:
def handle(self, hook_input: dict) -> HookResult:
try:
# Handler logic
return HookResult(decision="deny", reason="...")
except Exception:
# Fail open on errors
return HookResult(decision="allow")-
Create Feature Branch
git checkout -b feature/my-feature
-
Make Changes
- Write tests first
- Implement feature
- Update documentation
-
Run QA Checks
./scripts/qa/run_all.sh
-
Commit with Descriptive Message
git commit -m "Add MyHandler for blocking dangerous operations - Implements pattern matching for target commands - Adds comprehensive test coverage - Updates configuration template"
-
Push and Create PR
git push origin feature/my-feature
-
PR Review
- All QA checks must pass
- Test coverage must be maintained (95%+)
- Documentation must be updated
Open an issue on GitHub or check existing documentation in CLAUDE/ directory.