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
|
|
@ -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`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue