Initial commit
This commit is contained in:
442
agents/meta.hook/README.md
Normal file
442
agents/meta.hook/README.md
Normal file
@@ -0,0 +1,442 @@
|
||||
# meta.hook - Hook Creator Meta-Agent
|
||||
|
||||
Generates Claude Code hooks from natural language descriptions.
|
||||
|
||||
## Overview
|
||||
|
||||
**meta.hook** is a meta-agent that creates Claude Code hooks from simple description files. It generates hook configurations that execute commands in response to events like tool calls, errors, or user interactions.
|
||||
|
||||
**What it does:**
|
||||
- Parses hook descriptions (Markdown or JSON)
|
||||
- Generates `.claude/hooks.yaml` configurations
|
||||
- Validates event types and hook structure
|
||||
- Manages hook lifecycle (create, update, enable/disable)
|
||||
- Supports tool-specific filtering
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Create a Hook
|
||||
|
||||
```bash
|
||||
python3 agents/meta.hook/meta_hook.py examples/my_hook.md
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
🪝 meta.hook - Creating hook from examples/my_hook.md
|
||||
|
||||
✨ Hook 'pre-commit-lint' created successfully!
|
||||
|
||||
📄 Created/updated file:
|
||||
- .claude/hooks.yaml
|
||||
|
||||
✅ Hook 'pre-commit-lint' is ready to use
|
||||
Event: before-tool-call
|
||||
Command: npm run lint
|
||||
```
|
||||
|
||||
### Hook Description Format
|
||||
|
||||
Create a Markdown file:
|
||||
|
||||
```markdown
|
||||
# Name: pre-commit-lint
|
||||
|
||||
# Event: before-tool-call
|
||||
|
||||
# Tool Filter: git
|
||||
|
||||
# Description: Run linter before git commits
|
||||
|
||||
# Command: npm run lint
|
||||
|
||||
# Timeout: 30000
|
||||
|
||||
# Enabled: true
|
||||
```
|
||||
|
||||
Or use JSON format:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "pre-commit-lint",
|
||||
"event": "before-tool-call",
|
||||
"tool_filter": "git",
|
||||
"description": "Run linter before git commits",
|
||||
"command": "npm run lint",
|
||||
"timeout": 30000,
|
||||
"enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
## Event Types
|
||||
|
||||
Supported Claude Code events:
|
||||
|
||||
- **before-tool-call** - Before any tool is executed
|
||||
- **after-tool-call** - After any tool completes
|
||||
- **on-error** - When a tool call fails
|
||||
- **user-prompt-submit** - When user submits a prompt
|
||||
- **assistant-response** - After assistant responds
|
||||
|
||||
## Generated Structure
|
||||
|
||||
meta.hook generates or updates `.claude/hooks.yaml`:
|
||||
|
||||
```yaml
|
||||
hooks:
|
||||
- name: pre-commit-lint
|
||||
event: before-tool-call
|
||||
command: npm run lint
|
||||
description: Run linter before git commits
|
||||
enabled: true
|
||||
tool_filter: git
|
||||
timeout: 30000
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Example 1: Pre-commit Linting
|
||||
|
||||
**Description file** (`lint_hook.md`):
|
||||
|
||||
```markdown
|
||||
# Name: pre-commit-lint
|
||||
|
||||
# Event: before-tool-call
|
||||
|
||||
# Tool Filter: git
|
||||
|
||||
# Description: Run linter before git commits to ensure code quality
|
||||
|
||||
# Command: npm run lint
|
||||
|
||||
# Timeout: 30000
|
||||
```
|
||||
|
||||
**Create hook:**
|
||||
|
||||
```bash
|
||||
python3 agents/meta.hook/meta_hook.py lint_hook.md
|
||||
```
|
||||
|
||||
### Example 2: Post-deployment Notification
|
||||
|
||||
**Description file** (`deploy_notify.json`):
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "deploy-notify",
|
||||
"event": "after-tool-call",
|
||||
"tool_filter": "deploy",
|
||||
"description": "Send notification after deployment",
|
||||
"command": "./scripts/notify-team.sh",
|
||||
"timeout": 10000
|
||||
}
|
||||
```
|
||||
|
||||
**Create hook:**
|
||||
|
||||
```bash
|
||||
python3 agents/meta.hook/meta_hook.py deploy_notify.json
|
||||
```
|
||||
|
||||
### Example 3: Error Logging
|
||||
|
||||
**Description file** (`error_logger.md`):
|
||||
|
||||
```markdown
|
||||
# Name: error-logger
|
||||
|
||||
# Event: on-error
|
||||
|
||||
# Description: Log errors to monitoring system
|
||||
|
||||
# Command: ./scripts/log-error.sh "{error}" "{tool}"
|
||||
|
||||
# Timeout: 5000
|
||||
|
||||
# Enabled: true
|
||||
```
|
||||
|
||||
**Create hook:**
|
||||
|
||||
```bash
|
||||
python3 agents/meta.hook/meta_hook.py error_logger.md
|
||||
```
|
||||
|
||||
## Hook Parameters
|
||||
|
||||
### Required
|
||||
|
||||
- **name** - Unique hook identifier
|
||||
- **event** - Trigger event type
|
||||
- **command** - Shell command to execute
|
||||
|
||||
### Optional
|
||||
|
||||
- **description** - What the hook does
|
||||
- **tool_filter** - Only trigger for specific tools (e.g., "git", "npm", "docker")
|
||||
- **enabled** - Whether hook is active (default: true)
|
||||
- **timeout** - Command timeout in milliseconds (default: none)
|
||||
|
||||
## Tool Filters
|
||||
|
||||
Restrict hooks to specific tools:
|
||||
|
||||
```markdown
|
||||
# Tool Filter: git
|
||||
```
|
||||
|
||||
This hook only triggers for git-related tool calls.
|
||||
|
||||
Common tool filters:
|
||||
- `git` - Git operations
|
||||
- `npm` - NPM commands
|
||||
- `docker` - Docker commands
|
||||
- `python` - Python execution
|
||||
- `bash` - Shell commands
|
||||
|
||||
## Managing Hooks
|
||||
|
||||
### Update Existing Hook
|
||||
|
||||
Run meta.hook with the same hook name to update:
|
||||
|
||||
```bash
|
||||
python3 agents/meta.hook/meta_hook.py updated_hook.md
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
⚠️ Warning: Hook 'pre-commit-lint' already exists, updating...
|
||||
✨ Hook 'pre-commit-lint' created successfully!
|
||||
```
|
||||
|
||||
### Disable Hook
|
||||
|
||||
Set `Enabled: false` in description:
|
||||
|
||||
```markdown
|
||||
# Name: my-hook
|
||||
# Event: before-tool-call
|
||||
# Command: echo "test"
|
||||
# Enabled: false
|
||||
```
|
||||
|
||||
### Multiple Hooks
|
||||
|
||||
Create multiple hook descriptions and run meta.hook for each:
|
||||
|
||||
```bash
|
||||
for hook in hooks/*.md; do
|
||||
python3 agents/meta.hook/meta_hook.py "$hook"
|
||||
done
|
||||
```
|
||||
|
||||
## Integration
|
||||
|
||||
### With Claude Code
|
||||
|
||||
Hooks are automatically loaded by Claude Code from `.claude/hooks.yaml`.
|
||||
|
||||
### With meta.agent
|
||||
|
||||
Create agents that use hooks:
|
||||
|
||||
```yaml
|
||||
name: ci.agent
|
||||
description: Continuous integration agent
|
||||
# Hooks will trigger during agent execution
|
||||
```
|
||||
|
||||
## Artifact Types
|
||||
|
||||
### Consumes
|
||||
|
||||
- **hook-description** - Natural language hook requirements
|
||||
- Pattern: `**/hook_description.md`
|
||||
- Format: Markdown or JSON
|
||||
|
||||
### Produces
|
||||
|
||||
- **hook-config** - Claude Code hook configuration
|
||||
- Pattern: `.claude/hooks.yaml`
|
||||
- Schema: `schemas/hook-config.json`
|
||||
|
||||
## Common Workflows
|
||||
|
||||
### Workflow 1: Create and Test Hook
|
||||
|
||||
```bash
|
||||
# 1. Create hook description
|
||||
cat > my_hook.md <<EOF
|
||||
# Name: test-runner
|
||||
# Event: after-tool-call
|
||||
# Tool Filter: git
|
||||
# Description: Run tests after git push
|
||||
# Command: npm test
|
||||
EOF
|
||||
|
||||
# 2. Generate hook
|
||||
python3 agents/meta.hook/meta_hook.py my_hook.md
|
||||
|
||||
# 3. Test hook (trigger the event)
|
||||
git add .
|
||||
git commit -m "test"
|
||||
```
|
||||
|
||||
### Workflow 2: Create Pre-commit Workflow
|
||||
|
||||
```bash
|
||||
# Create linting hook
|
||||
cat > lint_hook.md <<EOF
|
||||
# Name: lint
|
||||
# Event: before-tool-call
|
||||
# Tool Filter: git
|
||||
# Command: npm run lint
|
||||
EOF
|
||||
|
||||
python3 agents/meta.hook/meta_hook.py lint_hook.md
|
||||
|
||||
# Create test hook
|
||||
cat > test_hook.md <<EOF
|
||||
# Name: test
|
||||
# Event: before-tool-call
|
||||
# Tool Filter: git
|
||||
# Command: npm test
|
||||
EOF
|
||||
|
||||
python3 agents/meta.hook/meta_hook.py test_hook.md
|
||||
```
|
||||
|
||||
### Workflow 3: Error Monitoring
|
||||
|
||||
```bash
|
||||
# Create error notification hook
|
||||
cat > error_notify.md <<EOF
|
||||
# Name: error-notify
|
||||
# Event: on-error
|
||||
# Description: Send error notifications
|
||||
# Command: ./scripts/notify.sh
|
||||
# Timeout: 5000
|
||||
EOF
|
||||
|
||||
python3 agents/meta.hook/meta_hook.py error_notify.md
|
||||
```
|
||||
|
||||
## Tips & Best Practices
|
||||
|
||||
### Command Design
|
||||
|
||||
**Use absolute paths for scripts:**
|
||||
```markdown
|
||||
# Good
|
||||
# Command: ./scripts/lint.sh
|
||||
|
||||
# Bad
|
||||
# Command: lint.sh
|
||||
```
|
||||
|
||||
**Set appropriate timeouts:**
|
||||
```markdown
|
||||
# Fast operations: 5-10 seconds
|
||||
# Timeout: 10000
|
||||
|
||||
# Longer operations: 30-60 seconds
|
||||
# Timeout: 60000
|
||||
```
|
||||
|
||||
**Handle errors gracefully:**
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# In your hook script
|
||||
set -e # Exit on error
|
||||
trap 'echo "Hook failed"' ERR
|
||||
```
|
||||
|
||||
### Tool Filters
|
||||
|
||||
Be specific with tool filters to avoid unnecessary executions:
|
||||
|
||||
```markdown
|
||||
# Specific
|
||||
# Tool Filter: git
|
||||
|
||||
# Too broad
|
||||
# (no tool filter - runs for ALL tools)
|
||||
```
|
||||
|
||||
### Testing Hooks
|
||||
|
||||
Test hooks before enabling:
|
||||
|
||||
```markdown
|
||||
# Enabled: false
|
||||
```
|
||||
|
||||
Then manually test the command, and enable once verified.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Hook not triggering
|
||||
|
||||
**Check event type:**
|
||||
```bash
|
||||
# Verify event is correct in .claude/hooks.yaml
|
||||
cat .claude/hooks.yaml
|
||||
```
|
||||
|
||||
**Check tool filter:**
|
||||
```markdown
|
||||
# If using tool filter, ensure it matches the tool being called
|
||||
# Tool Filter: git
|
||||
```
|
||||
|
||||
### Command fails
|
||||
|
||||
**Check command path:**
|
||||
```bash
|
||||
# Test command manually
|
||||
npm run lint
|
||||
|
||||
# If fails, fix path or installation
|
||||
```
|
||||
|
||||
**Check timeout:**
|
||||
```markdown
|
||||
# Increase timeout for slow commands
|
||||
# Timeout: 60000
|
||||
```
|
||||
|
||||
### Hook already exists warning
|
||||
|
||||
This is normal when updating hooks. The old version is replaced with the new one.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
meta.hook
|
||||
├─ Input: hook-description (Markdown/JSON)
|
||||
├─ Parser: extract name, event, command, filters
|
||||
├─ Generator: create/update hooks.yaml
|
||||
├─ Validator: check event types and structure
|
||||
└─ Output: .claude/hooks.yaml configuration
|
||||
```
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [META_AGENTS.md](../../docs/META_AGENTS.md) - Meta-agent ecosystem
|
||||
- [ARTIFACT_STANDARDS.md](../../docs/ARTIFACT_STANDARDS.md) - Artifact system
|
||||
- [hook-description schema](../../schemas/hook-description.json)
|
||||
- [hook-config schema](../../schemas/hook-config.json)
|
||||
|
||||
## How Claude Uses This
|
||||
|
||||
Claude can:
|
||||
1. **Create hooks on demand** - "Create a pre-commit linting hook"
|
||||
2. **Automate workflows** - "Add error logging for all failures"
|
||||
3. **Build CI/CD pipelines** - "Create hooks for test, lint, and deploy"
|
||||
4. **Monitor executions** - "Add notification hooks for important events"
|
||||
|
||||
meta.hook enables powerful event-driven automation in Claude Code!
|
||||
64
agents/meta.hook/agent.yaml
Normal file
64
agents/meta.hook/agent.yaml
Normal file
@@ -0,0 +1,64 @@
|
||||
name: meta.hook
|
||||
version: 0.1.0
|
||||
description: Hook creator meta-agent that generates Claude Code hooks from descriptions
|
||||
status: draft
|
||||
reasoning_mode: iterative
|
||||
capabilities:
|
||||
- Translate natural language specifications into validated hook manifests
|
||||
- Recommend appropriate hook events, commands, and execution patterns
|
||||
- Simulate and document hook behavior for developer adoption
|
||||
type: meta-agent
|
||||
skills_available:
|
||||
- hook.define
|
||||
- hook.register
|
||||
- hook.simulate
|
||||
|
||||
artifact_metadata:
|
||||
consumes:
|
||||
- type: hook-description
|
||||
required: true
|
||||
produces:
|
||||
- type: hook-config
|
||||
|
||||
system_prompt: |
|
||||
You are meta.hook, a specialized meta-agent that creates Claude Code hooks from natural language descriptions.
|
||||
|
||||
Your role:
|
||||
1. Parse hook descriptions (Markdown or JSON format)
|
||||
2. Generate hook configurations (.claude/hooks.yaml)
|
||||
3. Validate hook names and event types
|
||||
4. Document hook usage
|
||||
|
||||
Hook description format:
|
||||
- Name: Descriptive hook identifier
|
||||
- Event: Trigger event (before-tool-call, after-tool-call, on-error, etc.)
|
||||
- Description: What the hook does
|
||||
- Command: Shell command to execute
|
||||
- Tool Filter (optional): Only trigger for specific tools
|
||||
- Enabled (optional): Whether hook is active (default: true)
|
||||
|
||||
Generated hooks.yaml format:
|
||||
```yaml
|
||||
hooks:
|
||||
- name: hook-name
|
||||
event: trigger-event
|
||||
description: What it does
|
||||
command: shell command
|
||||
enabled: true
|
||||
tool_filter: tool-name # optional
|
||||
timeout: 30000 # optional, in milliseconds
|
||||
```
|
||||
|
||||
Event types:
|
||||
- before-tool-call: Before any tool is called
|
||||
- after-tool-call: After any tool completes
|
||||
- on-error: When a tool call fails
|
||||
- user-prompt-submit: When user submits a prompt
|
||||
- assistant-response: After assistant responds
|
||||
|
||||
Always:
|
||||
- Validate event types
|
||||
- Provide clear descriptions
|
||||
- Set reasonable timeouts
|
||||
- Document tool filters
|
||||
- Include usage examples in generated documentation
|
||||
349
agents/meta.hook/meta_hook.py
Executable file
349
agents/meta.hook/meta_hook.py
Executable file
@@ -0,0 +1,349 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
meta.hook - Hook Creator Meta-Agent
|
||||
|
||||
Generates Claude Code hooks from natural language descriptions.
|
||||
|
||||
Usage:
|
||||
python3 agents/meta.hook/meta_hook.py <hook_description_file>
|
||||
|
||||
Examples:
|
||||
python3 agents/meta.hook/meta_hook.py examples/lint_hook.md
|
||||
python3 agents/meta.hook/meta_hook.py examples/notify_hook.json
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import yaml
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Any, Optional
|
||||
|
||||
# Add parent directory to path
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")))
|
||||
|
||||
from betty.config import BASE_DIR
|
||||
from betty.logging_utils import setup_logger
|
||||
from betty.traceability import get_tracer, RequirementInfo
|
||||
|
||||
logger = setup_logger(__name__)
|
||||
|
||||
|
||||
class HookCreator:
|
||||
"""Creates Claude Code hooks from descriptions"""
|
||||
|
||||
VALID_EVENTS = [
|
||||
"before-tool-call",
|
||||
"after-tool-call",
|
||||
"on-error",
|
||||
"user-prompt-submit",
|
||||
"assistant-response"
|
||||
]
|
||||
|
||||
def __init__(self, base_dir: str = BASE_DIR):
|
||||
"""Initialize hook creator"""
|
||||
self.base_dir = Path(base_dir)
|
||||
self.hooks_dir = self.base_dir / ".claude"
|
||||
|
||||
def parse_description(self, description_path: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Parse hook description from Markdown or JSON file
|
||||
|
||||
Args:
|
||||
description_path: Path to description file
|
||||
|
||||
Returns:
|
||||
Dict with hook configuration
|
||||
"""
|
||||
path = Path(description_path)
|
||||
|
||||
if not path.exists():
|
||||
raise FileNotFoundError(f"Description file not found: {description_path}")
|
||||
|
||||
# Read file
|
||||
content = path.read_text()
|
||||
|
||||
# Try JSON first
|
||||
if path.suffix == ".json":
|
||||
return json.loads(content)
|
||||
|
||||
# Parse Markdown format
|
||||
hook_desc = {}
|
||||
|
||||
# Extract fields
|
||||
patterns = {
|
||||
"name": r"#\s*Name:\s*(.+)",
|
||||
"event": r"#\s*Event:\s*(.+)",
|
||||
"description": r"#\s*Description:\s*(.+)",
|
||||
"command": r"#\s*Command:\s*(.+)",
|
||||
"tool_filter": r"#\s*Tool\s*Filter:\s*(.+)",
|
||||
"enabled": r"#\s*Enabled:\s*(.+)",
|
||||
"timeout": r"#\s*Timeout:\s*(\d+)"
|
||||
}
|
||||
|
||||
for field, pattern in patterns.items():
|
||||
match = re.search(pattern, content, re.IGNORECASE)
|
||||
if match:
|
||||
value = match.group(1).strip()
|
||||
|
||||
# Convert types
|
||||
if field == "enabled":
|
||||
value = value.lower() in ("true", "yes", "1")
|
||||
elif field == "timeout":
|
||||
value = int(value)
|
||||
|
||||
hook_desc[field] = value
|
||||
|
||||
# Validate required fields
|
||||
required = ["name", "event", "command"]
|
||||
missing = [f for f in required if f not in hook_desc]
|
||||
if missing:
|
||||
raise ValueError(f"Missing required fields: {', '.join(missing)}")
|
||||
|
||||
# Validate event type
|
||||
if hook_desc["event"] not in self.VALID_EVENTS:
|
||||
raise ValueError(
|
||||
f"Invalid event type: {hook_desc['event']}. "
|
||||
f"Must be one of: {', '.join(self.VALID_EVENTS)}"
|
||||
)
|
||||
|
||||
return hook_desc
|
||||
|
||||
def generate_hooks_yaml(self, hook_desc: Dict[str, Any]) -> str:
|
||||
"""
|
||||
Generate hooks.yaml configuration
|
||||
|
||||
Args:
|
||||
hook_desc: Parsed hook description
|
||||
|
||||
Returns:
|
||||
YAML string
|
||||
"""
|
||||
hook_config = {
|
||||
"name": hook_desc["name"],
|
||||
"event": hook_desc["event"],
|
||||
"command": hook_desc["command"]
|
||||
}
|
||||
|
||||
# Add optional fields
|
||||
if "description" in hook_desc:
|
||||
hook_config["description"] = hook_desc["description"]
|
||||
|
||||
if "enabled" in hook_desc:
|
||||
hook_config["enabled"] = hook_desc["enabled"]
|
||||
else:
|
||||
hook_config["enabled"] = True
|
||||
|
||||
if "tool_filter" in hook_desc:
|
||||
hook_config["tool_filter"] = hook_desc["tool_filter"]
|
||||
|
||||
if "timeout" in hook_desc:
|
||||
hook_config["timeout"] = hook_desc["timeout"]
|
||||
|
||||
# Wrap in hooks array
|
||||
hooks_yaml = {"hooks": [hook_config]}
|
||||
|
||||
return yaml.dump(hooks_yaml, default_flow_style=False, sort_keys=False)
|
||||
|
||||
def create_hook(
|
||||
self,
|
||||
description_path: str,
|
||||
requirement: Optional[RequirementInfo] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Create hook from description file
|
||||
|
||||
Args:
|
||||
description_path: Path to description file
|
||||
requirement: Optional requirement information for traceability
|
||||
|
||||
Returns:
|
||||
Dict with creation results
|
||||
"""
|
||||
try:
|
||||
print(f"🪝 meta.hook - Creating hook from {description_path}\n")
|
||||
|
||||
# Parse description
|
||||
hook_desc = self.parse_description(description_path)
|
||||
|
||||
# Generate hooks.yaml
|
||||
hooks_yaml = self.generate_hooks_yaml(hook_desc)
|
||||
|
||||
# Ensure .claude directory exists
|
||||
self.hooks_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Write hooks.yaml (or append if exists)
|
||||
hooks_file = self.hooks_dir / "hooks.yaml"
|
||||
|
||||
if hooks_file.exists():
|
||||
# Load existing hooks
|
||||
existing = yaml.safe_load(hooks_file.read_text())
|
||||
if not existing or not isinstance(existing, dict):
|
||||
existing = {"hooks": []}
|
||||
if "hooks" not in existing:
|
||||
existing["hooks"] = []
|
||||
if not isinstance(existing["hooks"], list):
|
||||
existing["hooks"] = []
|
||||
|
||||
# Add new hook
|
||||
new_hook = yaml.safe_load(hooks_yaml)["hooks"][0]
|
||||
|
||||
# Check for duplicate
|
||||
hook_names = [h.get("name") for h in existing["hooks"] if isinstance(h, dict)]
|
||||
if new_hook["name"] in hook_names:
|
||||
print(f"⚠️ Warning: Hook '{new_hook['name']}' already exists, updating...")
|
||||
# Remove old version
|
||||
existing["hooks"] = [h for h in existing["hooks"] if h["name"] != new_hook["name"]]
|
||||
|
||||
existing["hooks"].append(new_hook)
|
||||
hooks_yaml = yaml.dump(existing, default_flow_style=False, sort_keys=False)
|
||||
|
||||
# Write file
|
||||
hooks_file.write_text(hooks_yaml)
|
||||
|
||||
print(f"✨ Hook '{hook_desc['name']}' created successfully!\n")
|
||||
print(f"📄 Created/updated file:")
|
||||
print(f" - {hooks_file}\n")
|
||||
print(f"✅ Hook '{hook_desc['name']}' is ready to use")
|
||||
print(f" Event: {hook_desc['event']}")
|
||||
print(f" Command: {hook_desc['command']}")
|
||||
|
||||
result = {
|
||||
"ok": True,
|
||||
"status": "success",
|
||||
"hook_name": hook_desc["name"],
|
||||
"hooks_file": str(hooks_file)
|
||||
}
|
||||
|
||||
# Log traceability if requirement provided
|
||||
trace_id = None
|
||||
if requirement:
|
||||
try:
|
||||
tracer = get_tracer()
|
||||
|
||||
# Create component ID from hook name
|
||||
component_id = f"hook.{hook_desc['name'].replace('-', '_')}"
|
||||
|
||||
trace_id = tracer.log_creation(
|
||||
component_id=component_id,
|
||||
component_name=hook_desc["name"],
|
||||
component_type="hook",
|
||||
component_version="0.1.0",
|
||||
component_file_path=str(hooks_file),
|
||||
input_source_path=description_path,
|
||||
created_by_tool="meta.hook",
|
||||
created_by_version="0.1.0",
|
||||
requirement=requirement,
|
||||
tags=["hook", "auto-generated", hook_desc["event"]],
|
||||
project="Betty Framework"
|
||||
)
|
||||
|
||||
# Log validation check
|
||||
validation_details = {
|
||||
"checks_performed": [
|
||||
{"name": "hook_structure", "status": "passed"},
|
||||
{"name": "event_validation", "status": "passed",
|
||||
"message": f"Valid event type: {hook_desc['event']}"}
|
||||
]
|
||||
}
|
||||
|
||||
# Check for tool filter
|
||||
if hook_desc.get("tool_filter"):
|
||||
validation_details["checks_performed"].append({
|
||||
"name": "tool_filter_validation",
|
||||
"status": "passed",
|
||||
"message": f"Tool filter: {hook_desc['tool_filter']}"
|
||||
})
|
||||
|
||||
tracer.log_verification(
|
||||
component_id=component_id,
|
||||
check_type="validation",
|
||||
tool="meta.hook",
|
||||
result="passed",
|
||||
details=validation_details
|
||||
)
|
||||
|
||||
result["trace_id"] = trace_id
|
||||
result["component_id"] = component_id
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠️ Warning: Could not log traceability: {e}")
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error creating hook: {e}")
|
||||
logger.error(f"Error creating hook: {e}", exc_info=True)
|
||||
return {
|
||||
"ok": False,
|
||||
"status": "failed",
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
"""CLI entry point"""
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description="meta.hook - Create hooks from descriptions"
|
||||
)
|
||||
parser.add_argument(
|
||||
"description",
|
||||
help="Path to hook description file (.md or .json)"
|
||||
)
|
||||
|
||||
# Traceability arguments
|
||||
parser.add_argument(
|
||||
"--requirement-id",
|
||||
help="Requirement identifier (e.g., REQ-2025-001)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--requirement-description",
|
||||
help="What this hook accomplishes"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--requirement-source",
|
||||
help="Source document"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--issue-id",
|
||||
help="Issue tracking ID (e.g., JIRA-123)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--requested-by",
|
||||
help="Who requested this"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--rationale",
|
||||
help="Why this is needed"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Create requirement info if provided
|
||||
requirement = None
|
||||
if args.requirement_id and args.requirement_description:
|
||||
requirement = RequirementInfo(
|
||||
id=args.requirement_id,
|
||||
description=args.requirement_description,
|
||||
source=args.requirement_source,
|
||||
issue_id=args.issue_id,
|
||||
requested_by=args.requested_by,
|
||||
rationale=args.rationale
|
||||
)
|
||||
|
||||
creator = HookCreator()
|
||||
result = creator.create_hook(args.description, requirement=requirement)
|
||||
|
||||
# Display traceability info if available
|
||||
if result.get("trace_id"):
|
||||
print(f"\n📝 Traceability: {result['trace_id']}")
|
||||
print(f" View trace: python3 betty/trace_cli.py show {result['component_id']}")
|
||||
|
||||
sys.exit(0 if result.get("ok") else 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user