14 KiB
hook.simulate
Version: 0.1.0 Status: Active Tags: hook, simulation, testing, validation, development
Overview
The hook.simulate skill allows developers to test a hook manifest before registering it in the Betty Framework. It validates the manifest structure, simulates hook triggers based on event types, and optionally executes the hook commands in a controlled environment.
This skill is essential for:
- Testing hooks before deployment - Catch errors early in development
- Understanding hook behavior - See which files match patterns and how commands execute
- Debugging hook issues - Validate patterns, commands, and execution flow
- Safe experimentation - Test hooks without affecting the live system
Features
- ✅ Manifest Validation - Validates all required and optional fields
- ✅ Pattern Matching - Shows which files match the hook's pattern
- ✅ Event Simulation - Simulates different hook events (on_file_edit, on_commit, etc.)
- ✅ Command Execution - Runs hook commands with dry-run or actual execution
- ✅ Timeout Testing - Validates timeout behavior
- ✅ Blocking Simulation - Shows how blocking hooks would behave
- ✅ JSON Output - Structured results for automation and analysis
Usage
Command Line
# Basic validation and simulation
python skills/hook.simulate/hook_simulate.py examples/test-hook.yaml
# Simulate with dry-run command execution
python skills/hook.simulate/hook_simulate.py examples/test-hook.yaml --execute --dry-run
# Actually execute the command
python skills/hook.simulate/hook_simulate.py examples/test-hook.yaml --execute --no-dry-run
# Get JSON output for scripting
python skills/hook.simulate/hook_simulate.py examples/test-hook.yaml --output json
As a Skill
from skills.hook.simulate.hook_simulate import simulate_hook
# Simulate a hook
results = simulate_hook(
manifest_path="examples/test-hook.yaml",
dry_run=True,
execute=True
)
# Check validation
if results["valid"]:
print("✅ Hook is valid")
else:
print("❌ Validation errors:", results["validation_errors"])
# Check if hook would trigger
if results["trigger_simulation"]["would_trigger"]:
print("Hook would trigger!")
print("Matching files:", results["trigger_simulation"]["matching_files"])
Input Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
manifest_path |
string | Yes | - | Path to the hook.yaml manifest file |
execute |
boolean | No | false | Whether to execute the hook command |
dry_run |
boolean | No | true | If true, simulate without actually running commands |
Output Schema
{
"manifest_path": "path/to/hook.yaml",
"timestamp": "2025-10-23 12:34:56",
"valid": true,
"validation_errors": [],
"manifest": {
"name": "hook-name",
"version": "0.1.0",
"description": "Hook description",
"event": "on_file_edit",
"command": "python script.py {file_path}",
"when": {
"pattern": "*.yaml"
},
"blocking": true,
"timeout": 30000
},
"trigger_simulation": {
"would_trigger": true,
"reason": "Found 5 file(s) matching pattern: *.yaml",
"matching_files": ["file1.yaml", "file2.yaml"],
"pattern": "*.yaml"
},
"command_executions": [
{
"command": "python script.py file1.yaml",
"executed": true,
"dry_run": false,
"stdout": "Validation passed",
"stderr": "",
"return_code": 0,
"execution_time_ms": 123.45,
"success": true,
"file": "file1.yaml"
}
],
"blocking": true,
"timeout_ms": 30000
}
Examples
Example 1: Validate OpenAPI Hook
Create a hook manifest openapi-validator.yaml:
name: validate-openapi
version: 0.1.0
description: "Validate OpenAPI specifications on edit"
event: on_file_edit
command: "python betty/skills/api.validate/api_validate.py {file_path} zalando"
when:
pattern: "**/*.openapi.yaml"
blocking: true
timeout: 30000
on_failure: show_errors
status: draft
tags: [api, validation, openapi]
Simulate it:
python skills/hook.simulate/hook_simulate.py openapi-validator.yaml
Output:
=== Hook Simulation Results ===
Manifest: openapi-validator.yaml
Timestamp: 2025-10-23 12:34:56
✅ VALIDATION PASSED
Hook: validate-openapi v0.1.0
Event: on_file_edit
Command: python betty/skills/api.validate/api_validate.py {file_path} zalando
Blocking: True
Timeout: 30000ms
✅ WOULD TRIGGER
Reason: Found 3 file(s) matching pattern: **/*.openapi.yaml
Matching files (3):
- specs/petstore.openapi.yaml
- specs/users.openapi.yaml
- api/products.openapi.yaml
Example 2: Test Commit Hook
Create pre-commit.yaml:
name: pre-commit-linter
version: 1.0.0
description: "Run linter before commits"
event: on_commit
command: "pylint src/"
blocking: true
timeout: 60000
status: active
tags: [lint, quality]
Simulate with execution:
python skills/hook.simulate/hook_simulate.py pre-commit.yaml --execute --dry-run
Output:
=== Hook Simulation Results ===
Manifest: pre-commit.yaml
Timestamp: 2025-10-23 12:35:00
✅ VALIDATION PASSED
Hook: pre-commit-linter v1.0.0
Event: on_commit
Command: pylint src/
Blocking: True
Timeout: 60000ms
✅ WOULD TRIGGER
Reason: on_commit hook would trigger with 5 changed file(s)
Changed files (5):
- src/main.py
- src/utils.py
- tests/test_main.py
- README.md
- setup.py
=== Command Execution Results (1) ===
[1] N/A
Command: pylint src/
Mode: DRY RUN (not executed)
Example 3: Test with Actual Execution
python skills/hook.simulate/hook_simulate.py openapi-validator.yaml --execute --no-dry-run
Output:
=== Hook Simulation Results ===
...
=== Command Execution Results (3) ===
[1] specs/petstore.openapi.yaml
Command: python betty/skills/api.validate/api_validate.py specs/petstore.openapi.yaml zalando
Executed: Yes
Return code: 0
Execution time: 234.56ms
Status: ✅ SUCCESS
Stdout:
✅ OpenAPI spec is valid
[2] specs/users.openapi.yaml
Command: python betty/skills/api.validate/api_validate.py specs/users.openapi.yaml zalando
Executed: Yes
Return code: 1
Execution time: 189.23ms
Status: ❌ FAILED
Stderr:
Error: Missing required field 'info.contact'
[3] api/products.openapi.yaml
Command: python betty/skills/api.validate/api_validate.py api/products.openapi.yaml zalando
Executed: Yes
Return code: 0
Execution time: 201.45ms
Status: ✅ SUCCESS
Example 4: JSON Output for Automation
python skills/hook.simulate/hook_simulate.py my-hook.yaml --output json > results.json
Then process with scripts:
import json
with open("results.json") as f:
results = json.load(f)
if not results["valid"]:
print("Validation failed:")
for error in results["validation_errors"]:
print(f" - {error}")
exit(1)
if results["trigger_simulation"]["would_trigger"]:
files = results["trigger_simulation"]["matching_files"]
print(f"Hook would run on {len(files)} files")
for execution in results["command_executions"]:
if not execution["success"]:
print(f"Failed on {execution['file']}")
print(f"Error: {execution['stderr']}")
Event Type Support
| Event | Simulation Support | Description |
|---|---|---|
on_file_edit |
✅ Full | Matches files by pattern, simulates editing |
on_file_save |
✅ Full | Similar to on_file_edit |
on_commit |
✅ Full | Checks git status, shows changed files |
on_push |
⚠️ Partial | Notes hook would trigger, no git simulation |
on_tool_use |
⚠️ Partial | Notes hook would trigger, no tool simulation |
on_agent_start |
⚠️ Partial | Notes hook would trigger, no agent simulation |
on_workflow_end |
⚠️ Partial | Notes hook would trigger, no workflow simulation |
Validation Rules
The skill validates all Betty Framework hook requirements:
- Required Fields: name, version, description, event, command
- Name Format: Lowercase, starts with letter, allows hyphens/underscores
- Version Format: Semantic versioning (e.g., 1.0.0, 0.1.0-alpha)
- Event: Must be a valid hook event
- Command: Non-empty string
- Blocking: Must be boolean if specified
- Timeout: Must be positive number in milliseconds if specified
- Status: Must be draft, active, disabled, or archived if specified
- Pattern: Must be non-empty string if specified in when.pattern
Pattern Matching
The skill supports glob patterns for file matching:
| Pattern | Description | Example Matches |
|---|---|---|
*.yaml |
Files in current directory | config.yaml, spec.yaml |
**/*.yaml |
YAML files anywhere | src/config.yaml, specs/api/v1.yaml |
src/**/*.py |
Python files in src/ | src/main.py, src/utils/helper.py |
*.openapi.yaml |
OpenAPI files only | petstore.openapi.yaml |
tests/**/*.test.js |
Test files | tests/unit/api.test.js |
Dry-Run vs Execution Modes
Dry-Run Mode (Default)
python skills/hook.simulate/hook_simulate.py hook.yaml --execute --dry-run
- ✅ Validates manifest
- ✅ Shows matching files
- ✅ Shows command that would run
- ❌ Does NOT execute command
- ⚡ Safe for testing
Execution Mode
python skills/hook.simulate/hook_simulate.py hook.yaml --execute --no-dry-run
- ✅ Validates manifest
- ✅ Shows matching files
- ✅ Actually executes command
- ✅ Captures stdout/stderr
- ✅ Reports execution time
- ⚠️ Use with caution
Validation Only (No Execution)
python skills/hook.simulate/hook_simulate.py hook.yaml
- ✅ Validates manifest
- ✅ Shows if hook would trigger
- ✅ Shows matching files
- ❌ Does NOT execute command
- ⚡ Fastest mode
Integration with Hook Workflow
Development Workflow
# 1. Create hook manifest
cat > my-hook.yaml <<EOF
name: my-custom-hook
version: 0.1.0
description: "My hook description"
event: on_file_edit
command: "echo 'Hook triggered on {file_path}'"
when:
pattern: "*.md"
EOF
# 2. Simulate and validate
python skills/hook.simulate/hook_simulate.py my-hook.yaml
# 3. Test with execution
python skills/hook.simulate/hook_simulate.py my-hook.yaml --execute --dry-run
# 4. Test actual execution
python skills/hook.simulate/hook_simulate.py my-hook.yaml --execute --no-dry-run
# 5. Register if tests pass
python skills/hook.register/hook_register.py my-hook.yaml
CI/CD Integration
# .github/workflows/test-hooks.yml
name: Test Hooks
on: [push, pull_request]
jobs:
test-hooks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Validate all hook manifests
run: |
for hook in hooks/*.yaml; do
echo "Testing $hook"
python skills/hook.simulate/hook_simulate.py "$hook" --output json || exit 1
done
- name: Test hook execution
run: |
python skills/hook.simulate/hook_simulate.py hooks/validator.yaml --execute --no-dry-run
Error Handling
Validation Errors
$ python skills/hook.simulate/hook_simulate.py invalid-hook.yaml
=== Hook Simulation Results ===
❌ VALIDATION FAILED
Errors:
- Missing required field: 'version'
- Invalid event: 'on_invalid_event'. Must be one of: on_file_edit, on_file_save, on_commit, on_push, on_tool_use, on_agent_start, on_workflow_end
- timeout must be a positive number (in milliseconds)
Execution Failures
$ python skills/hook.simulate/hook_simulate.py hook.yaml --execute --no-dry-run
...
=== Command Execution Results (1) ===
[1] test.yaml
Command: python nonexistent.py test.yaml
Executed: Yes
Return code: -1
Execution time: 45.23ms
Status: ❌ FAILED
Stderr:
Error executing command: [Errno 2] No such file or directory: 'python nonexistent.py test.yaml'
Timeout Handling
$ python skills/hook.simulate/hook_simulate.py slow-hook.yaml --execute --no-dry-run
...
[1] test.yaml
Command: sleep 100
Executed: Yes
Return code: -1
Execution time: 5000.12ms
Status: ❌ FAILED
Stderr:
Command timed out after 5000ms
Troubleshooting
Hook Doesn't Trigger
Problem: would_trigger: false
Solutions:
- Check pattern matches files:
ls -la **/*.yaml - Verify event type is appropriate
- Ensure files exist in the manifest's directory
- Check git status for on_commit events
Command Fails
Problem: Command returns non-zero exit code
Solutions:
- Test command manually:
python script.py file.yaml - Check file paths and placeholders
- Verify dependencies are installed
- Check timeout is sufficient
Pattern Not Matching
Problem: Expected files not showing in matching_files
Solutions:
- Use
**/*.extfor recursive matching - Check you're in the right directory
- Test pattern with:
ls **/*.yaml - Ensure pattern is in
when.patternfield
Best Practices
- Always simulate before registering - Catch issues early
- Test with dry-run first - Understand what will execute
- Verify pattern matching - Ensure correct files are targeted
- Test timeout values - Make sure commands complete in time
- Use JSON output for automation - Integrate with CI/CD
- Test blocking behavior - Understand impact on workflow
- Validate on representative data - Test with real files
Related Skills
- hook.define - Create hooks dynamically and add to
.claude/hooks.yaml - hook.register - Register validated hooks in the registry
- api.validate - Example validation skill often used in hooks
Version History
- 0.1.0 (2025-10-23) - Initial release
- Manifest validation
- Event simulation (on_file_edit, on_commit)
- Pattern matching
- Command execution (dry-run and actual)
- JSON and summary output formats
License
MIT License - Part of the Betty Framework
Contributing
To improve this skill:
- Add support for more event types
- Enhance simulation accuracy
- Add more validation rules
- Improve error messages
- Add performance benchmarks