Claude hooks auto-backup: manual (backup_20250720_091250)
This commit is contained in:
parent
9445e09c48
commit
392833187e
135 changed files with 16151 additions and 3439 deletions
|
|
@ -23,17 +23,17 @@ If either check shows existing hooks, **STOP** and run the uninstall process fir
|
|||
- `claude-hooks uninstall` (if available)
|
||||
- Or manually remove: `rm -rf ~/.config/claude/hooks.json`
|
||||
|
||||
**Installation (choose one method):**
|
||||
**Installation:**
|
||||
|
||||
**Method 1 - NPM (Recommended):**
|
||||
**NPM (Recommended):**
|
||||
1. `npm install -g claude-hooks`
|
||||
2. `claude-hooks init`
|
||||
3. `claude-hooks test`
|
||||
2. Hooks auto-configure during install
|
||||
3. `claude-hooks test` to verify
|
||||
|
||||
**Method 2 - Git (For developers):**
|
||||
**Git (For developers):**
|
||||
1. `git clone https://git.supported.systems/rsp2k/claude-hooks.git`
|
||||
2. `cd claude-hooks && ./scripts/install.sh`
|
||||
3. `./scripts/test.sh`
|
||||
2. `cd claude-hooks && npm install && npm run postinstall`
|
||||
3. `claude-hooks test`
|
||||
|
||||
**After installation:**
|
||||
- Restart Claude Code to activate the hooks
|
||||
|
|
@ -42,6 +42,6 @@ If either check shows existing hooks, **STOP** and run the uninstall process fir
|
|||
|
||||
**To uninstall later:** `claude-hooks uninstall` or `npm uninstall -g claude-hooks`
|
||||
|
||||
Please guide me through this installation step-by-step, including the pre-installation checks. Use the NPM method unless I specifically request git installation. Handle any errors that come up and make sure everything works. After installation, help me test it by trying some commands that commonly fail on different systems (like `pip` vs `pip3`, `python` vs `python3`) so I can see the intelligence in action.
|
||||
Please guide me through this installation step-by-step, including the pre-installation checks. Use the NPM method unless I specifically request git installation. Handle any errors that come up and make sure everything works. The installation now auto-configures during npm install - no manual steps needed! After installation, help me test it by trying some commands that commonly fail on different systems (like `pip` vs `pip3`, `python` vs `python3`) so I can see the intelligence in action.
|
||||
|
||||
The goal is to have a working Claude Hooks system that starts learning from my environment immediately.
|
||||
|
|
@ -150,27 +150,30 @@ This feedback loop is what transforms Claude from a stateless assistant into an
|
|||
|
||||
The shadow learner implements a classic observer pattern, but with sophisticated intelligence:
|
||||
|
||||
```python
|
||||
class ShadowLearner:
|
||||
def observe(self, execution: ToolExecution):
|
||||
# Extract patterns from execution
|
||||
patterns = self.extract_patterns(execution)
|
||||
```javascript
|
||||
class ShadowLearner {
|
||||
observe(execution) {
|
||||
// Extract patterns from execution
|
||||
const patterns = this.extractPatterns(execution);
|
||||
|
||||
# Update confidence scores
|
||||
self.update_confidence(patterns)
|
||||
// Update confidence scores
|
||||
this.updateConfidence(patterns);
|
||||
|
||||
# Store new knowledge
|
||||
self.knowledge_base.update(patterns)
|
||||
// Store new knowledge
|
||||
this.knowledgeBase.update(patterns);
|
||||
}
|
||||
|
||||
def predict(self, proposed_action):
|
||||
# Match against known patterns
|
||||
similar_patterns = self.find_similar(proposed_action)
|
||||
predict(proposedAction) {
|
||||
// Match against known patterns
|
||||
const similarPatterns = this.findSimilar(proposedAction);
|
||||
|
||||
# Calculate confidence
|
||||
confidence = self.calculate_confidence(similar_patterns)
|
||||
// Calculate confidence
|
||||
const confidence = this.calculateConfidence(similarPatterns);
|
||||
|
||||
# Return prediction
|
||||
return Prediction(confidence, similar_patterns)
|
||||
// Return prediction
|
||||
return new Prediction(confidence, similarPatterns);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The key insight is that the learner doesn't just record what happened - it actively builds predictive models that can guide future decisions.
|
||||
|
|
@ -179,26 +182,29 @@ The key insight is that the learner doesn't just record what happened - it activ
|
|||
|
||||
The context monitor implements a resource management pattern, treating Claude's context as a finite resource that must be carefully managed:
|
||||
|
||||
```python
|
||||
class ContextMonitor:
|
||||
def estimate_usage(self):
|
||||
# Multiple estimation strategies
|
||||
estimates = [
|
||||
self.token_based_estimate(),
|
||||
self.activity_based_estimate(),
|
||||
self.time_based_estimate()
|
||||
]
|
||||
```javascript
|
||||
class ContextMonitor {
|
||||
estimateUsage() {
|
||||
// Multiple estimation strategies
|
||||
const estimates = [
|
||||
this.tokenBasedEstimate(),
|
||||
this.activityBasedEstimate(),
|
||||
this.timeBasedEstimate()
|
||||
];
|
||||
|
||||
# Weighted combination
|
||||
return self.combine_estimates(estimates)
|
||||
// Weighted combination
|
||||
return this.combineEstimates(estimates);
|
||||
}
|
||||
|
||||
def should_backup(self):
|
||||
usage = self.estimate_usage()
|
||||
shouldBackup() {
|
||||
const usage = this.estimateUsage();
|
||||
|
||||
# Adaptive thresholds based on session complexity
|
||||
threshold = self.calculate_threshold()
|
||||
// Adaptive thresholds based on session complexity
|
||||
const threshold = this.calculateThreshold();
|
||||
|
||||
return usage > threshold
|
||||
return usage > threshold;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This architectural approach means the system can make intelligent decisions about when to intervene, rather than using simple rule-based triggers.
|
||||
|
|
@ -207,25 +213,31 @@ This architectural approach means the system can make intelligent decisions abou
|
|||
|
||||
The backup manager implements a strategy pattern, using different backup approaches based on circumstances:
|
||||
|
||||
```python
|
||||
class BackupManager:
|
||||
def __init__(self):
|
||||
self.strategies = [
|
||||
GitBackupStrategy(),
|
||||
FilesystemBackupStrategy(),
|
||||
EmergencyBackupStrategy()
|
||||
]
|
||||
```javascript
|
||||
class BackupManager {
|
||||
constructor() {
|
||||
this.strategies = [
|
||||
new GitBackupStrategy(),
|
||||
new FilesystemBackupStrategy(),
|
||||
new EmergencyBackupStrategy()
|
||||
];
|
||||
}
|
||||
|
||||
def execute_backup(self, context):
|
||||
for strategy in self.strategies:
|
||||
try:
|
||||
result = strategy.backup(context)
|
||||
if result.success:
|
||||
return result
|
||||
except Exception:
|
||||
continue # Try next strategy
|
||||
async executeBackup(context) {
|
||||
for (const strategy of this.strategies) {
|
||||
try {
|
||||
const result = await strategy.backup(context);
|
||||
if (result.success) {
|
||||
return result;
|
||||
}
|
||||
} catch (error) {
|
||||
continue; // Try next strategy
|
||||
}
|
||||
}
|
||||
|
||||
return self.emergency_backup(context)
|
||||
return this.emergencyBackup(context);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This ensures that backups almost always succeed, gracefully degrading to simpler approaches when sophisticated methods fail.
|
||||
|
|
|
|||
|
|
@ -55,14 +55,14 @@ Traditional ML models are trained once and deployed. Shadow learners improve inc
|
|||
The shadow learner identifies several types of patterns:
|
||||
|
||||
**Command Patterns**: Which commands tend to succeed or fail in your environment
|
||||
- `pip install` fails 90% of the time → suggest `pip3 install`
|
||||
- `python script.py` fails on your system → suggest `python3 script.py`
|
||||
- `npm install` without `--save` in certain projects → warn about dependency tracking
|
||||
- `npm install` fails 90% of the time → suggest `npm ci` or check package-lock.json
|
||||
- `node script.js` fails on your system → suggest `node --version` check or use `npx`
|
||||
- `npm install` without `--save-dev` for dev dependencies → warn about production vs development packages
|
||||
|
||||
**Sequence Patterns**: Common workflows and command chains
|
||||
- `git add . && git commit` often follows file edits
|
||||
- `npm install` typically precedes `npm test`
|
||||
- Reading config files often precedes configuration changes
|
||||
- Reading package.json often precedes dependency updates
|
||||
|
||||
**Context Patterns**: Environmental factors that affect command success
|
||||
- Commands fail differently in Docker containers vs. native environments
|
||||
|
|
@ -70,9 +70,9 @@ The shadow learner identifies several types of patterns:
|
|||
- Time-of-day patterns (builds failing during peak hours due to resource contention)
|
||||
|
||||
**Error Patterns**: Common failure modes and their solutions
|
||||
- "Permission denied" errors often require sudo or chmod
|
||||
- "Command not found" errors have specific alternative commands
|
||||
- Network timeouts suggest retry strategies
|
||||
- "Permission denied" errors often require sudo or npm config set prefix
|
||||
- "Command not found" errors suggest missing global packages or PATH issues
|
||||
- Network timeouts suggest retry strategies or alternative registries
|
||||
|
||||
### Confidence Building
|
||||
|
||||
|
|
@ -100,8 +100,8 @@ The shadow learner doesn't just record patterns - it builds confidence scores ba
|
|||
|
||||
The shadow learner develops deep knowledge about your specific development environment:
|
||||
|
||||
- Which Python version is actually available
|
||||
- How package managers are configured
|
||||
- Which Node.js version is actually available
|
||||
- How npm/yarn/pnpm package managers are configured
|
||||
- What development tools are installed and working
|
||||
- How permissions are set up
|
||||
- What network restrictions exist
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ This creates a jarring experience: you're deep in a debugging session, making pr
|
|||
|
||||
### The Repetitive Failure Problem
|
||||
|
||||
Human developers naturally learn from mistakes. Try a command that fails, remember not to do it again, adapt. But each new Claude session starts with no memory of previous failures. You find yourself watching Claude repeat the same mistakes - `pip` instead of `pip3`, `python` instead of `python3`, dangerous operations that you know will fail.
|
||||
Human developers naturally learn from mistakes. Try a command that fails, remember not to do it again, adapt. But each new Claude session starts with no memory of previous failures. You find yourself watching Claude repeat the same mistakes - `npm install` instead of `npm ci`, `node` without proper version checks, dangerous operations that you know will fail.
|
||||
|
||||
This isn't Claude's fault - it's a fundamental limitation of the stateless conversation model. But it creates frustration and inefficiency.
|
||||
|
||||
|
|
@ -62,7 +62,7 @@ You might wonder: why not just train Claude to be better at avoiding these probl
|
|||
|
||||
The answer lies in the fundamental difference between general intelligence and environmental adaptation:
|
||||
|
||||
**General intelligence** (what Claude provides) is knowledge that applies across all contexts - how to write Python, how to use git, how to debug problems.
|
||||
**General intelligence** (what Claude provides) is knowledge that applies across all contexts - how to write JavaScript, how to use git, how to debug problems.
|
||||
|
||||
**Environmental adaptation** (what shadow learning provides) is knowledge specific to your setup - which commands work on your system, what your typical workflows are, what mistakes you commonly make.
|
||||
|
||||
|
|
|
|||
|
|
@ -8,31 +8,31 @@ If you have commands that should never be run in your environment:
|
|||
|
||||
1. **Edit the command validator**:
|
||||
```bash
|
||||
nano hooks/command_validator.py
|
||||
nano hooks/command-validator.js
|
||||
```
|
||||
|
||||
2. **Find the dangerous_patterns list** (around line 23):
|
||||
```python
|
||||
self.dangerous_patterns = [
|
||||
r'rm\s+-rf\s+/', # Delete root
|
||||
r'mkfs\.', # Format filesystem
|
||||
# Add your pattern here
|
||||
]
|
||||
2. **Find the dangerousPatterns array** (around line 23):
|
||||
```javascript
|
||||
this.dangerousPatterns = [
|
||||
/rm\s+-rf\s+\//, // Delete root
|
||||
/mkfs\./, // Format filesystem
|
||||
// Add your pattern here
|
||||
];
|
||||
```
|
||||
|
||||
3. **Add your pattern**:
|
||||
```python
|
||||
self.dangerous_patterns = [
|
||||
r'rm\s+-rf\s+/', # Delete root
|
||||
r'mkfs\.', # Format filesystem
|
||||
r'docker\s+system\s+prune\s+--all', # Delete all Docker data
|
||||
r'kubectl\s+delete\s+namespace\s+production', # Delete prod namespace
|
||||
]
|
||||
```javascript
|
||||
this.dangerousPatterns = [
|
||||
/rm\s+-rf\s+\//, // Delete root
|
||||
/mkfs\./, // Format filesystem
|
||||
/docker\s+system\s+prune\s+--all/, // Delete all Docker data
|
||||
/kubectl\s+delete\s+namespace\s+production/, // Delete prod namespace
|
||||
];
|
||||
```
|
||||
|
||||
4. **Test your pattern**:
|
||||
```bash
|
||||
echo '{"tool": "Bash", "parameters": {"command": "docker system prune --all"}}' | python3 hooks/command_validator.py
|
||||
echo '{"tool": "Bash", "parameters": {"command": "docker system prune --all"}}' | node hooks/command-validator.js
|
||||
```
|
||||
|
||||
Should return: `{"allow": false, "message": "⛔ Command blocked: Dangerous command pattern detected"}`
|
||||
|
|
@ -41,23 +41,23 @@ If you have commands that should never be run in your environment:
|
|||
|
||||
For commands that are risky but sometimes legitimate:
|
||||
|
||||
1. **Find the suspicious_patterns list**:
|
||||
```python
|
||||
self.suspicious_patterns = [
|
||||
r'sudo\s+rm', # Sudo with rm
|
||||
r'chmod\s+777', # Overly permissive
|
||||
# Add your pattern here
|
||||
]
|
||||
1. **Find the suspiciousPatterns array**:
|
||||
```javascript
|
||||
this.suspiciousPatterns = [
|
||||
/sudo\s+rm/, // Sudo with rm
|
||||
/chmod\s+777/, // Overly permissive
|
||||
// Add your pattern here
|
||||
];
|
||||
```
|
||||
|
||||
2. **Add patterns that should warn but not block**:
|
||||
```python
|
||||
self.suspicious_patterns = [
|
||||
r'sudo\s+rm', # Sudo with rm
|
||||
r'chmod\s+777', # Overly permissive
|
||||
r'npm\s+install\s+.*--global', # Global npm installs
|
||||
r'pip\s+install.*--user', # User pip installs
|
||||
]
|
||||
```javascript
|
||||
this.suspiciousPatterns = [
|
||||
/sudo\s+rm/, // Sudo with rm
|
||||
/chmod\s+777/, // Overly permissive
|
||||
/npm\s+install\s+.*--global/, // Global npm installs
|
||||
/pip\s+install.*--user/, // User pip installs
|
||||
];
|
||||
```
|
||||
|
||||
## Customize for Your Tech Stack
|
||||
|
|
@ -65,66 +65,66 @@ For commands that are risky but sometimes legitimate:
|
|||
### For Docker Environments
|
||||
|
||||
Add Docker-specific protections:
|
||||
```python
|
||||
# In dangerous_patterns:
|
||||
r'docker\s+rm\s+.*-f.*', # Force remove containers
|
||||
r'docker\s+rmi\s+.*-f.*', # Force remove images
|
||||
```javascript
|
||||
// In dangerousPatterns:
|
||||
/docker\s+rm\s+.*-f.*/, // Force remove containers
|
||||
/docker\s+rmi\s+.*-f.*/, // Force remove images
|
||||
|
||||
# In suspicious_patterns:
|
||||
r'docker\s+run.*--privileged', # Privileged containers
|
||||
r'docker.*-v\s+/:/.*', # Mount root filesystem
|
||||
// In suspiciousPatterns:
|
||||
/docker\s+run.*--privileged/, // Privileged containers
|
||||
/docker.*-v\s+\/:\/.*/, // Mount root filesystem
|
||||
```
|
||||
|
||||
### For Kubernetes
|
||||
|
||||
Protect production namespaces:
|
||||
```python
|
||||
# In dangerous_patterns:
|
||||
r'kubectl\s+delete\s+.*production.*',
|
||||
r'kubectl\s+delete\s+.*prod.*',
|
||||
r'helm\s+delete\s+.*production.*',
|
||||
```javascript
|
||||
// In dangerousPatterns:
|
||||
/kubectl\s+delete\s+.*production.*/,
|
||||
/kubectl\s+delete\s+.*prod.*/,
|
||||
/helm\s+delete\s+.*production.*/,
|
||||
|
||||
# In suspicious_patterns:
|
||||
r'kubectl\s+apply.*production.*',
|
||||
r'kubectl.*--all-namespaces.*delete',
|
||||
// In suspiciousPatterns:
|
||||
/kubectl\s+apply.*production.*/,
|
||||
/kubectl.*--all-namespaces.*delete/,
|
||||
```
|
||||
|
||||
### For Database Operations
|
||||
|
||||
Prevent destructive database commands:
|
||||
```python
|
||||
# In dangerous_patterns:
|
||||
r'DROP\s+DATABASE.*',
|
||||
r'TRUNCATE\s+TABLE.*',
|
||||
r'DELETE\s+FROM.*WHERE\s+1=1',
|
||||
```javascript
|
||||
// In dangerousPatterns:
|
||||
/DROP\s+DATABASE.*/i,
|
||||
/TRUNCATE\s+TABLE.*/i,
|
||||
/DELETE\s+FROM.*WHERE\s+1=1/i,
|
||||
|
||||
# In suspicious_patterns:
|
||||
r'UPDATE.*SET.*WHERE\s+1=1',
|
||||
r'ALTER\s+TABLE.*DROP.*',
|
||||
// In suspiciousPatterns:
|
||||
/UPDATE.*SET.*WHERE\s+1=1/i,
|
||||
/ALTER\s+TABLE.*DROP.*/i,
|
||||
```
|
||||
|
||||
## Environment-Specific Patterns
|
||||
|
||||
### For Production Servers
|
||||
|
||||
```python
|
||||
# In dangerous_patterns:
|
||||
r'systemctl\s+stop\s+(nginx|apache|mysql)',
|
||||
r'service\s+(nginx|apache|mysql)\s+stop',
|
||||
r'killall\s+-9.*',
|
||||
```javascript
|
||||
// In dangerousPatterns:
|
||||
/systemctl\s+stop\s+(nginx|apache|mysql)/,
|
||||
/service\s+(nginx|apache|mysql)\s+stop/,
|
||||
/killall\s+-9.*/,
|
||||
|
||||
# In suspicious_patterns:
|
||||
r'sudo\s+systemctl\s+restart.*',
|
||||
r'sudo\s+service.*restart.*',
|
||||
// In suspiciousPatterns:
|
||||
/sudo\s+systemctl\s+restart.*/,
|
||||
/sudo\s+service.*restart.*/,
|
||||
```
|
||||
|
||||
### For Development Machines
|
||||
|
||||
```python
|
||||
# In suspicious_patterns:
|
||||
r'rm\s+-rf\s+node_modules', # Can break local dev
|
||||
r'git\s+reset\s+--hard\s+HEAD~[0-9]+', # Lose multiple commits
|
||||
r'git\s+push\s+.*--force.*', # Force push
|
||||
```javascript
|
||||
// In suspiciousPatterns:
|
||||
/rm\s+-rf\s+node_modules/, // Can break local dev
|
||||
/git\s+reset\s+--hard\s+HEAD~[0-9]+/, // Lose multiple commits
|
||||
/git\s+push\s+.*--force.*/, // Force push
|
||||
```
|
||||
|
||||
## Test Your Custom Patterns
|
||||
|
|
@ -137,15 +137,15 @@ cat > test_patterns.sh << 'EOF'
|
|||
|
||||
# Test dangerous pattern (should block)
|
||||
echo "Testing dangerous pattern..."
|
||||
echo '{"tool": "Bash", "parameters": {"command": "docker system prune --all"}}' | python3 hooks/command_validator.py
|
||||
echo '{"tool": "Bash", "parameters": {"command": "docker system prune --all"}}' | node hooks/command-validator.js
|
||||
|
||||
# Test suspicious pattern (should warn)
|
||||
echo "Testing suspicious pattern..."
|
||||
echo '{"tool": "Bash", "parameters": {"command": "npm install -g dangerous-package"}}' | python3 hooks/command_validator.py
|
||||
echo '{"tool": "Bash", "parameters": {"command": "npm install -g dangerous-package"}}' | node hooks/command-validator.js
|
||||
|
||||
# Test normal command (should pass)
|
||||
echo "Testing normal command..."
|
||||
echo '{"tool": "Bash", "parameters": {"command": "ls -la"}}' | python3 hooks/command_validator.py
|
||||
echo '{"tool": "Bash", "parameters": {"command": "ls -la"}}' | node hooks/command-validator.js
|
||||
EOF
|
||||
|
||||
chmod +x test_patterns.sh
|
||||
|
|
@ -157,29 +157,34 @@ chmod +x test_patterns.sh
|
|||
For patterns that depend on file context:
|
||||
|
||||
1. **Edit the validation function** to check current directory or files:
|
||||
```python
|
||||
def validate_command_safety(self, command: str) -> ValidationResult:
|
||||
# Your existing patterns...
|
||||
```javascript
|
||||
validateCommandSafety(command) {
|
||||
// Your existing patterns...
|
||||
|
||||
# Context-aware validation
|
||||
if "git push" in command.lower():
|
||||
# Check if we're in a production branch
|
||||
try:
|
||||
current_branch = subprocess.check_output(['git', 'branch', '--show-current'],
|
||||
text=True).strip()
|
||||
if current_branch in ['main', 'master', 'production']:
|
||||
return ValidationResult(
|
||||
allowed=True,
|
||||
reason="⚠️ Pushing to protected branch",
|
||||
severity="warning"
|
||||
)
|
||||
except:
|
||||
pass
|
||||
// Context-aware validation
|
||||
if (command.toLowerCase().includes("git push")) {
|
||||
// Check if we're in a production branch
|
||||
try {
|
||||
const { execSync } = require('child_process');
|
||||
const currentBranch = execSync('git branch --show-current',
|
||||
{ encoding: 'utf8' }).trim();
|
||||
if (['main', 'master', 'production'].includes(currentBranch)) {
|
||||
return {
|
||||
allowed: true,
|
||||
reason: "⚠️ Pushing to protected branch",
|
||||
severity: "warning"
|
||||
};
|
||||
}
|
||||
} catch {
|
||||
// Ignore errors
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Pattern Syntax Reference
|
||||
|
||||
Use Python regex patterns:
|
||||
Use JavaScript regex patterns:
|
||||
|
||||
- `\s+` - One or more whitespace characters
|
||||
- `.*` - Any characters (greedy)
|
||||
|
|
@ -188,11 +193,12 @@ Use Python regex patterns:
|
|||
- `(option1|option2)` - Either option1 or option2
|
||||
- `^` - Start of string
|
||||
- `$` - End of string
|
||||
- `i` flag - Case insensitive matching
|
||||
|
||||
**Examples**:
|
||||
- `r'rm\s+-rf\s+/'` - Matches "rm -rf /"
|
||||
- `r'git\s+push.*--force'` - Matches "git push" followed by "--force" anywhere
|
||||
- `r'^sudo\s+'` - Matches commands starting with "sudo"
|
||||
- `/rm\s+-rf\s+\//` - Matches "rm -rf /"
|
||||
- `/git\s+push.*--force/` - Matches "git push" followed by "--force" anywhere
|
||||
- `/^sudo\s+/` - Matches commands starting with "sudo"
|
||||
|
||||
## Reload Changes
|
||||
|
||||
|
|
|
|||
|
|
@ -103,10 +103,11 @@ Export all hook data to a directory.
|
|||
|
||||
## Hook Scripts
|
||||
|
||||
### context_monitor.py
|
||||
### context-monitor.js
|
||||
|
||||
**Type**: UserPromptSubmit hook
|
||||
**Purpose**: Monitor context usage and trigger backups
|
||||
**Purpose**: Monitor context usage and trigger backups
|
||||
**Runtime**: Node.js
|
||||
|
||||
**Input format**:
|
||||
```json
|
||||
|
|
@ -128,10 +129,11 @@ Export all hook data to a directory.
|
|||
|
||||
---
|
||||
|
||||
### command_validator.py
|
||||
### command-validator.js
|
||||
|
||||
**Type**: PreToolUse[Bash] hook
|
||||
**Purpose**: Validate bash commands for safety and success probability
|
||||
**Purpose**: Validate bash commands for safety and success probability
|
||||
**Runtime**: Node.js
|
||||
|
||||
**Input format**:
|
||||
```json
|
||||
|
|
@ -163,10 +165,11 @@ Export all hook data to a directory.
|
|||
|
||||
---
|
||||
|
||||
### session_logger.py
|
||||
### session-logger.js
|
||||
|
||||
**Type**: PostToolUse[*] hook
|
||||
**Purpose**: Log tool executions and update learning data
|
||||
**Purpose**: Log tool executions and update learning data
|
||||
**Runtime**: Node.js
|
||||
|
||||
**Input format**:
|
||||
```json
|
||||
|
|
@ -192,10 +195,11 @@ Export all hook data to a directory.
|
|||
|
||||
---
|
||||
|
||||
### session_finalizer.py
|
||||
### session-finalizer.js
|
||||
|
||||
**Type**: Stop hook
|
||||
**Purpose**: Create session documentation and save state
|
||||
**Purpose**: Create session documentation and save state
|
||||
**Runtime**: Node.js
|
||||
|
||||
**Input format**:
|
||||
```json
|
||||
|
|
|
|||
|
|
@ -263,52 +263,62 @@ Hooks are designed to fail safely:
|
|||
|
||||
Always validate hook input:
|
||||
|
||||
```python
|
||||
def validate_input(input_data):
|
||||
if not isinstance(input_data, dict):
|
||||
raise ValueError("Input must be JSON object")
|
||||
```javascript
|
||||
function validateInput(inputData) {
|
||||
if (typeof inputData !== 'object' || inputData === null || Array.isArray(inputData)) {
|
||||
throw new Error('Input must be JSON object');
|
||||
}
|
||||
|
||||
tool = input_data.get("tool", "")
|
||||
if not isinstance(tool, str):
|
||||
raise ValueError("Tool must be string")
|
||||
const tool = inputData.tool || '';
|
||||
if (typeof tool !== 'string') {
|
||||
throw new Error('Tool must be string');
|
||||
}
|
||||
|
||||
# Validate other fields...
|
||||
// Validate other fields...
|
||||
}
|
||||
```
|
||||
|
||||
### Output Sanitization
|
||||
|
||||
Ensure hook output is safe:
|
||||
|
||||
```python
|
||||
def safe_message(text):
|
||||
# Remove potential injection characters
|
||||
return text.replace('\x00', '').replace('\r', '').replace('\n', '\\n')
|
||||
|
||||
response = {
|
||||
"allow": True,
|
||||
"message": safe_message(user_input)
|
||||
```javascript
|
||||
function safeMessage(text) {
|
||||
// Remove potential injection characters
|
||||
return text.replace(/\x00/g, '').replace(/\r/g, '').replace(/\n/g, '\\n');
|
||||
}
|
||||
|
||||
const response = {
|
||||
allow: true,
|
||||
message: safeMessage(userInput)
|
||||
};
|
||||
```
|
||||
|
||||
### File Path Validation
|
||||
|
||||
For hooks that access files:
|
||||
|
||||
```python
|
||||
def validate_file_path(path):
|
||||
# Convert to absolute path
|
||||
abs_path = os.path.abspath(path)
|
||||
```javascript
|
||||
const path = require('path');
|
||||
|
||||
function validateFilePath(filePath) {
|
||||
// Convert to absolute path
|
||||
const absPath = path.resolve(filePath);
|
||||
|
||||
# Check if within project boundaries
|
||||
project_root = os.path.abspath(".")
|
||||
if not abs_path.startswith(project_root):
|
||||
raise ValueError("Path outside project directory")
|
||||
// Check if within project boundaries
|
||||
const projectRoot = path.resolve('.');
|
||||
if (!absPath.startsWith(projectRoot)) {
|
||||
throw new Error('Path outside project directory');
|
||||
}
|
||||
|
||||
# Check for system files
|
||||
system_paths = ['/etc', '/usr', '/var', '/sys', '/proc']
|
||||
for sys_path in system_paths:
|
||||
if abs_path.startswith(sys_path):
|
||||
raise ValueError("System file access denied")
|
||||
// Check for system files
|
||||
const systemPaths = ['/etc', '/usr', '/var', '/sys', '/proc'];
|
||||
for (const sysPath of systemPaths) {
|
||||
if (absPath.startsWith(sysPath)) {
|
||||
throw new Error('System file access denied');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
|
@ -319,27 +329,32 @@ def validate_file_path(path):
|
|||
|
||||
Test hooks with sample inputs:
|
||||
|
||||
```python
|
||||
def test_command_validator():
|
||||
import subprocess
|
||||
import json
|
||||
```javascript
|
||||
const { spawn } = require('child_process');
|
||||
|
||||
function testCommandValidator() {
|
||||
// Test dangerous command
|
||||
const inputData = {
|
||||
tool: 'Bash',
|
||||
parameters: { command: 'rm -rf /' }
|
||||
};
|
||||
|
||||
# Test dangerous command
|
||||
input_data = {
|
||||
"tool": "Bash",
|
||||
"parameters": {"command": "rm -rf /"}
|
||||
}
|
||||
const process = spawn('node', ['hooks/command-validator.js'], {
|
||||
stdio: 'pipe'
|
||||
});
|
||||
|
||||
process = subprocess.run(
|
||||
["python3", "hooks/command_validator.py"],
|
||||
input=json.dumps(input_data),
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
process.stdin.write(JSON.stringify(inputData));
|
||||
process.stdin.end();
|
||||
|
||||
assert process.returncode == 1 # Should block
|
||||
response = json.loads(process.stdout)
|
||||
assert response["allow"] == False
|
||||
process.on('exit', (code) => {
|
||||
console.assert(code === 1, 'Should block'); // Should block
|
||||
});
|
||||
|
||||
process.stdout.on('data', (data) => {
|
||||
const response = JSON.parse(data.toString());
|
||||
console.assert(response.allow === false);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Integration Testing
|
||||
|
|
@ -348,7 +363,7 @@ Test with Claude Code directly:
|
|||
|
||||
```bash
|
||||
# Test in development environment
|
||||
echo '{"tool": "Bash", "parameters": {"command": "ls"}}' | python3 hooks/command_validator.py
|
||||
echo '{"tool": "Bash", "parameters": {"command": "ls"}}' | node hooks/command-validator.js
|
||||
|
||||
# Test hook registration
|
||||
claude-hooks status
|
||||
|
|
@ -358,25 +373,33 @@ claude-hooks status
|
|||
|
||||
Measure hook execution time:
|
||||
|
||||
```python
|
||||
import time
|
||||
import subprocess
|
||||
import json
|
||||
```javascript
|
||||
const { spawn } = require('child_process');
|
||||
|
||||
def benchmark_hook(hook_script, input_data, iterations=100):
|
||||
times = []
|
||||
function benchmarkHook(hookScript, inputData, iterations = 100) {
|
||||
const times = [];
|
||||
let completed = 0;
|
||||
|
||||
for _ in range(iterations):
|
||||
start = time.time()
|
||||
subprocess.run(
|
||||
["python3", hook_script],
|
||||
input=json.dumps(input_data),
|
||||
capture_output=True
|
||||
)
|
||||
times.append(time.time() - start)
|
||||
|
||||
avg_time = sum(times) / len(times)
|
||||
max_time = max(times)
|
||||
|
||||
print(f"Average: {avg_time*1000:.1f}ms, Max: {max_time*1000:.1f}ms")
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
const start = Date.now();
|
||||
const process = spawn('node', [hookScript], {
|
||||
stdio: 'pipe'
|
||||
});
|
||||
|
||||
process.stdin.write(JSON.stringify(inputData));
|
||||
process.stdin.end();
|
||||
|
||||
process.on('exit', () => {
|
||||
times.push(Date.now() - start);
|
||||
completed++;
|
||||
|
||||
if (completed === iterations) {
|
||||
const avgTime = times.reduce((a, b) => a + b, 0) / times.length;
|
||||
const maxTime = Math.max(...times);
|
||||
|
||||
console.log(`Average: ${avgTime.toFixed(1)}ms, Max: ${maxTime.toFixed(1)}ms`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -30,12 +30,12 @@ You should see output like this:
|
|||
```
|
||||
Claude Code Hooks Installation
|
||||
==================================
|
||||
Checking Python version... Python 3.11 found
|
||||
✓ Python version is compatible
|
||||
Installing Python dependencies... SUCCESS
|
||||
Checking Node.js version... Node.js v18.17.0 found
|
||||
✓ Node.js version is compatible
|
||||
Installing dependencies... SUCCESS
|
||||
```
|
||||
|
||||
**Notice** that the installer found your Python version and installed dependencies automatically.
|
||||
**Notice** that the installer found your Node.js version and installed dependencies automatically.
|
||||
|
||||
The installer will ask if you want to automatically configure Claude Code. Say **yes** - we want to see this working right away:
|
||||
|
||||
|
|
@ -59,26 +59,24 @@ Let's deliberately try a command that often fails to see the validation in actio
|
|||
|
||||
Start a new Claude conversation and try this:
|
||||
|
||||
> "Run `npm install express` to add the express library"
|
||||
|
||||
Watch what happens. You should see the command execute normally. Now try a command that commonly fails:
|
||||
|
||||
> "Run `pip install requests` to add the requests library"
|
||||
|
||||
Watch what happens. You should see something like:
|
||||
You should see something like:
|
||||
|
||||
```
|
||||
⚠️ Warning: pip commands often fail (confidence: 88%)
|
||||
💡 Suggestion: Use "pip3 install requests"
|
||||
💡 Suggestion: Use "pip3 install requests" or "npm install requests"
|
||||
```
|
||||
|
||||
**Notice** that Claude Hooks warned you about the command before it ran. The system doesn't have any learned patterns yet (it's brand new), but it has built-in knowledge about common failures.
|
||||
|
||||
Now try the suggested command:
|
||||
|
||||
> "Run `pip3 install requests`"
|
||||
|
||||
This time it should work without warnings. The system is learning that `pip3` succeeds where `pip` fails on your system.
|
||||
**Notice** that Claude Hooks warned you about the command before it ran. The system has built-in knowledge about common command failures and suggests working alternatives.
|
||||
|
||||
## Step 4: Experience the Shadow Learner
|
||||
|
||||
Let's make another common mistake and watch the system learn from it.
|
||||
Let's see the system learn from a failure pattern.
|
||||
|
||||
Try this command:
|
||||
|
||||
|
|
@ -92,7 +90,7 @@ If you're on a system where `python` isn't available, you'll see it fail. Now tr
|
|||
|
||||
```
|
||||
⛔ Blocked: python commands often fail (confidence: 95%)
|
||||
💡 Suggestion: Use "python3 --version"
|
||||
💡 Suggestion: Use "python3 --version" or "node --version"
|
||||
```
|
||||
|
||||
The shadow learner observed that `python` failed and is now protecting you from repeating the same mistake. This is intelligence building in real-time.
|
||||
|
|
@ -103,11 +101,11 @@ Let's trigger the context monitoring system. The hooks track how much of Claude'
|
|||
|
||||
Create several files to simulate a longer session:
|
||||
|
||||
> "Create a file called `test1.py` with a simple hello world script"
|
||||
> "Create a file called `test1.js` with a simple hello world script"
|
||||
|
||||
> "Now create `test2.py` with a different example"
|
||||
> "Now create `test2.js` with a different example"
|
||||
|
||||
> "Create `test3.py` with some more code"
|
||||
> "Create `test3.js` with some more code"
|
||||
|
||||
> "Show me the current git status"
|
||||
|
||||
|
|
@ -142,9 +140,9 @@ You should see a file that looks like:
|
|||
**Duration**: 2024-01-15T14:30:00 → 2024-01-15T14:45:00
|
||||
|
||||
## Files Modified (3)
|
||||
- test1.py
|
||||
- test2.py
|
||||
- test3.py
|
||||
- test1.js
|
||||
- test2.js
|
||||
- test3.js
|
||||
|
||||
## Tools Used (8 total)
|
||||
- Write: 3 times
|
||||
|
|
@ -152,8 +150,8 @@ You should see a file that looks like:
|
|||
- Read: 3 times
|
||||
|
||||
## Recent Commands (5)
|
||||
- `pip3 install requests` (2024-01-15T14:32:00)
|
||||
- `python3 --version` (2024-01-15T14:35:00)
|
||||
- `npm install express` (2024-01-15T14:32:00)
|
||||
- `node --version` (2024-01-15T14:35:00)
|
||||
...
|
||||
```
|
||||
|
||||
|
|
@ -190,7 +188,7 @@ You should see entries like:
|
|||
Success Rate: 100%
|
||||
```
|
||||
|
||||
**This is the intelligence you've built**. The system now knows that on your machine, `pip` fails but `pip3` works, and `python3` works better than `python`.
|
||||
**This is the intelligence you've built**. The system now knows command patterns that work reliably in your environment versus ones that commonly fail.
|
||||
|
||||
## What You've Experienced
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue