Add NPM distribution support with hybrid installation approach
Major changes: - Add package.json with NPM packaging configuration - Create Node.js CLI interface (bin/claude-hooks.js) with full command set - Convert bash scripts to Python for better npm integration - Add npm postinstall/preuninstall hooks for automatic setup - Update bootstrap prompt to recommend NPM method with git fallback - Enhance README with NPM-first documentation - Maintain backward compatibility with existing git installation Features: - npm install -g claude-hooks for easy distribution - claude-hooks init/status/test/backup/uninstall commands - Automatic Python dependency installation - Conflict detection and prevention - Hybrid approach supporting both npm and git workflows This resolves installation complexity while maintaining developer flexibility. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
1e169a5d45
commit
9445e09c48
98
README.md
98
README.md
@ -1,58 +1,84 @@
|
||||
# Claude Code Hooks
|
||||
# Claude Hooks
|
||||
|
||||
Intelligent hooks system that makes Claude Code smarter, safer, and more reliable through learning and automation.
|
||||
Intelligent hooks system for Claude Code that provides command validation, session continuity, and learning capabilities using Claude Code's native hooks architecture.
|
||||
|
||||
## What Is This?
|
||||
|
||||
Claude Code Hooks transforms your Claude experience by adding:
|
||||
|
||||
- **🧠 Learning Intelligence** - Remembers what works in your environment and suggests alternatives for commands that typically fail
|
||||
- **🛡️ Safety Protection** - Blocks dangerous operations and warns about risky commands before they execute
|
||||
- **💾 Automatic Backups** - Preserves your work before context limits are reached, with seamless session restoration
|
||||
- **🔄 Session Continuity** - Maintains context across Claude restarts with detailed session summaries
|
||||
|
||||
## Quick Install
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
git clone https://github.com/your-org/claude-hooks.git
|
||||
cd claude-hooks
|
||||
./scripts/install.sh
|
||||
# Install globally via npm
|
||||
npm install -g claude-hooks
|
||||
|
||||
# Initialize hooks
|
||||
claude-hooks init
|
||||
|
||||
# Test installation
|
||||
claude-hooks test
|
||||
|
||||
# Restart Claude Code to activate
|
||||
```
|
||||
|
||||
The installer automatically configures Claude Code. Restart Claude and the hooks will start working immediately.
|
||||
## Features
|
||||
|
||||
## See It in Action
|
||||
🧠 **Shadow Learning** - Learns from command failures and suggests working alternatives
|
||||
📊 **Context Monitoring** - Automatically backs up work before context limits
|
||||
⚡ **Smart Validation** - Blocks dangerous commands with intelligent suggestions
|
||||
🔄 **Session Continuity** - Maintains history and state across Claude restarts
|
||||
|
||||
## Usage
|
||||
|
||||
After installation, Claude Hooks runs automatically:
|
||||
|
||||
- **Command Learning**: Try `pip install requests` → suggests `pip3 install requests`
|
||||
- **Auto Backup**: Triggers before context limits or manually with `claude-hooks backup`
|
||||
- **Status Check**: Run `claude-hooks status` to verify installation
|
||||
- **Manual Test**: Use `claude-hooks test` to verify all components
|
||||
|
||||
## Commands
|
||||
|
||||
Try a command that often fails:
|
||||
```bash
|
||||
pip install requests
|
||||
claude-hooks init # Initialize after npm install
|
||||
claude-hooks status # Check installation status
|
||||
claude-hooks test # Run test suite
|
||||
claude-hooks backup # Create manual backup
|
||||
claude-hooks uninstall # Remove hooks configuration
|
||||
claude-hooks --help # Show all commands
|
||||
```
|
||||
|
||||
With hooks enabled, you'll see:
|
||||
```
|
||||
⚠️ Warning: pip commands often fail (confidence: 88%)
|
||||
💡 Suggestion: Use "pip3 install requests"
|
||||
## How It Works
|
||||
|
||||
Claude Hooks uses Claude Code's native hook system with four key integration points:
|
||||
|
||||
- **UserPromptSubmit**: Monitors context usage and triggers backups
|
||||
- **PreToolUse[Bash]**: Validates commands and suggests alternatives
|
||||
- **PostToolUse[*]**: Logs activity and learns from patterns
|
||||
- **Stop**: Finalizes sessions and creates continuation docs
|
||||
|
||||
## Installation Methods
|
||||
|
||||
### NPM (Recommended)
|
||||
```bash
|
||||
npm install -g claude-hooks
|
||||
claude-hooks init
|
||||
```
|
||||
|
||||
The system learned this pattern from your environment and is preventing you from repeating a known failure.
|
||||
### Git (Development)
|
||||
```bash
|
||||
git clone https://git.supported.systems/rsp2k/claude-hooks.git
|
||||
cd claude-hooks && ./scripts/install.sh
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
**📚 [Complete Documentation](docs/README.md)** - Organized by your current needs
|
||||
|
||||
**🎓 New to Claude Hooks?** → [Tutorial](docs/tutorial/getting-started.md) (30 minutes)
|
||||
|
||||
**🛠️ Need to solve a problem?** → [How-To Guides](docs/README.md#-how-to-guides)
|
||||
|
||||
**📖 Looking up commands?** → [Reference](docs/README.md#-reference)
|
||||
|
||||
**💡 Want to understand how it works?** → [Explanations](docs/README.md#-explanation)
|
||||
- [Getting Started Tutorial](docs/tutorial/getting-started.md)
|
||||
- [Architecture Explanation](docs/explanation/architecture.md)
|
||||
- [How-to Guides](docs/how-to/)
|
||||
- [Reference Documentation](docs/reference/)
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python 3.8+
|
||||
- Claude Code
|
||||
- Git (optional, for enhanced backup features)
|
||||
- **Node.js** 16+ (for npm installation)
|
||||
- **Python** 3.8+ (for hook scripts)
|
||||
- **Claude Code** (hooks integrate with Claude's native system)
|
||||
|
||||
## License
|
||||
|
||||
|
127
bin/claude-hooks.js
Normal file
127
bin/claude-hooks.js
Normal file
@ -0,0 +1,127 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { Command } = require('commander');
|
||||
const chalk = require('chalk');
|
||||
const { execSync, spawn } = require('child_process');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
|
||||
const program = new Command();
|
||||
const packageRoot = path.dirname(__dirname);
|
||||
|
||||
// Helper to run Python scripts
|
||||
function runPythonScript(scriptName, args = []) {
|
||||
const scriptPath = path.join(packageRoot, 'scripts', scriptName);
|
||||
return spawn('python3', [scriptPath, ...args], {
|
||||
stdio: 'inherit',
|
||||
cwd: packageRoot
|
||||
});
|
||||
}
|
||||
|
||||
// Helper to check installation status
|
||||
function checkStatus() {
|
||||
const hooksConfig = path.join(os.homedir(), '.config', 'claude', 'hooks.json');
|
||||
const hasHooks = fs.existsSync(hooksConfig);
|
||||
|
||||
console.log(chalk.blue('Claude Hooks Status:'));
|
||||
console.log(hasHooks ? chalk.green('✓ Hooks configuration installed') : chalk.red('✗ No hooks configuration found'));
|
||||
|
||||
try {
|
||||
execSync('python3 --version', { stdio: 'pipe' });
|
||||
console.log(chalk.green('✓ Python 3 available'));
|
||||
} catch {
|
||||
console.log(chalk.red('✗ Python 3 not found'));
|
||||
}
|
||||
|
||||
return hasHooks;
|
||||
}
|
||||
|
||||
program
|
||||
.name('claude-hooks')
|
||||
.description('Intelligent hooks system for Claude Code')
|
||||
.version(require('../package.json').version);
|
||||
|
||||
program
|
||||
.command('init')
|
||||
.description('Initialize Claude Hooks (run after npm install)')
|
||||
.option('--force', 'Force reinstallation even if already configured')
|
||||
.action((options) => {
|
||||
console.log(chalk.blue('Initializing Claude Hooks...'));
|
||||
|
||||
if (!options.force && checkStatus()) {
|
||||
console.log(chalk.yellow('⚠️ Claude Hooks already configured. Use --force to reinstall.'));
|
||||
return;
|
||||
}
|
||||
|
||||
const initScript = runPythonScript('install.py');
|
||||
initScript.on('close', (code) => {
|
||||
if (code === 0) {
|
||||
console.log(chalk.green('\n🎉 Claude Hooks initialized successfully!'));
|
||||
console.log(chalk.blue('Next steps:'));
|
||||
console.log('1. Restart Claude Code to activate hooks');
|
||||
console.log('2. Try: claude-hooks test');
|
||||
} else {
|
||||
console.log(chalk.red('❌ Initialization failed'));
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
program
|
||||
.command('status')
|
||||
.description('Show Claude Hooks installation status')
|
||||
.action(() => {
|
||||
checkStatus();
|
||||
});
|
||||
|
||||
program
|
||||
.command('test')
|
||||
.description('Run Claude Hooks tests')
|
||||
.action(() => {
|
||||
console.log(chalk.blue('Running Claude Hooks tests...'));
|
||||
const testScript = runPythonScript('test.py');
|
||||
testScript.on('close', (code) => {
|
||||
if (code === 0) {
|
||||
console.log(chalk.green('✅ All tests passed!'));
|
||||
} else {
|
||||
console.log(chalk.red('❌ Some tests failed'));
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
program
|
||||
.command('uninstall')
|
||||
.description('Remove Claude Hooks configuration')
|
||||
.action(() => {
|
||||
console.log(chalk.blue('Uninstalling Claude Hooks...'));
|
||||
const uninstallScript = runPythonScript('uninstall.py');
|
||||
uninstallScript.on('close', (code) => {
|
||||
if (code === 0) {
|
||||
console.log(chalk.green('✅ Claude Hooks uninstalled successfully'));
|
||||
console.log(chalk.yellow('Note: To completely remove, run: npm uninstall -g claude-hooks'));
|
||||
} else {
|
||||
console.log(chalk.red('❌ Uninstall failed'));
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
program
|
||||
.command('backup')
|
||||
.description('Manually trigger a backup')
|
||||
.action(() => {
|
||||
console.log(chalk.blue('Creating manual backup...'));
|
||||
const backupScript = runPythonScript('manual-backup.py');
|
||||
backupScript.on('close', (code) => {
|
||||
if (code === 0) {
|
||||
console.log(chalk.green('✅ Backup created successfully'));
|
||||
} else {
|
||||
console.log(chalk.red('❌ Backup failed'));
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
program.parse();
|
@ -6,23 +6,42 @@
|
||||
|
||||
I want to install Claude Hooks - an intelligent system that will make you smarter and safer by learning from my environment and preventing common mistakes.
|
||||
|
||||
**⚠️ IMPORTANT: Claude Hooks installs user-wide and affects ALL Claude Code instances. If you have other Claude sessions with hooks already installed, this will create conflicts. Check first!**
|
||||
|
||||
**What it does:**
|
||||
- Learns which commands fail in my environment and suggests working alternatives
|
||||
- Automatically backs up my work before context limits
|
||||
- Blocks dangerous commands before they execute
|
||||
- Maintains session history across Claude restarts
|
||||
|
||||
**Installation steps:**
|
||||
**Pre-installation check:**
|
||||
First, verify no hooks are already installed:
|
||||
1. Check: `claude-hooks status 2>/dev/null || echo "No claude-hooks found - safe to proceed"`
|
||||
2. Check: `ls ~/.config/claude/hooks.json 2>/dev/null || echo "No hooks config found - safe to proceed"`
|
||||
|
||||
If either check shows existing hooks, **STOP** and run the uninstall process first:
|
||||
- `claude-hooks uninstall` (if available)
|
||||
- Or manually remove: `rm -rf ~/.config/claude/hooks.json`
|
||||
|
||||
**Installation (choose one method):**
|
||||
|
||||
**Method 1 - NPM (Recommended):**
|
||||
1. `npm install -g claude-hooks`
|
||||
2. `claude-hooks init`
|
||||
3. `claude-hooks test`
|
||||
|
||||
**Method 2 - Git (For developers):**
|
||||
1. `git clone https://git.supported.systems/rsp2k/claude-hooks.git`
|
||||
2. `cd claude-hooks`
|
||||
3. `./scripts/install.sh`
|
||||
4. `./scripts/test.sh`
|
||||
2. `cd claude-hooks && ./scripts/install.sh`
|
||||
3. `./scripts/test.sh`
|
||||
|
||||
**After installation:**
|
||||
- Restart Claude Code to activate the hooks
|
||||
- Try `claude-hooks status` to see it working
|
||||
- Try `claude-hooks status` to verify it's working
|
||||
- Try `pip install requests` to see intelligent command suggestions
|
||||
|
||||
Please guide me through this installation step-by-step. 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.
|
||||
**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.
|
||||
|
||||
The goal is to have a working Claude Hooks system that starts learning from my environment immediately.
|
47
package.json
Normal file
47
package.json
Normal file
@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "claude-hooks",
|
||||
"version": "1.0.0",
|
||||
"description": "Intelligent hooks system for Claude Code with shadow learning and automatic backup",
|
||||
"keywords": ["claude", "hooks", "ai", "automation", "backup", "learning"],
|
||||
"author": "Claude Hooks Contributors",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://git.supported.systems/rsp2k/claude-hooks.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://git.supported.systems/rsp2k/claude-hooks/issues"
|
||||
},
|
||||
"homepage": "https://git.supported.systems/rsp2k/claude-hooks",
|
||||
"engines": {
|
||||
"node": ">=16.0.0",
|
||||
"npm": ">=8.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"claude-hooks": "./bin/claude-hooks.js"
|
||||
},
|
||||
"files": [
|
||||
"bin/",
|
||||
"hooks/",
|
||||
"lib/",
|
||||
"config/",
|
||||
"scripts/",
|
||||
"docs/",
|
||||
"requirements.txt",
|
||||
"README.md"
|
||||
],
|
||||
"scripts": {
|
||||
"postinstall": "node scripts/npm-postinstall.js",
|
||||
"preuninstall": "node scripts/npm-preuninstall.js",
|
||||
"test": "python3 scripts/test.sh",
|
||||
"start": "claude-hooks"
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": "^11.0.0",
|
||||
"chalk": "^5.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"python3": ">=3.8.0"
|
||||
},
|
||||
"preferGlobal": true
|
||||
}
|
133
scripts/install.py
Normal file
133
scripts/install.py
Normal file
@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Claude Hooks Installation Script for NPM Distribution"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
# Colors for terminal output
|
||||
class Colors:
|
||||
RED = '\033[0;31m'
|
||||
GREEN = '\033[0;32m'
|
||||
YELLOW = '\033[1;33m'
|
||||
BLUE = '\033[0;34m'
|
||||
NC = '\033[0m' # No Color
|
||||
|
||||
def print_colored(message, color):
|
||||
print(f"{color}{message}{Colors.NC}")
|
||||
|
||||
def print_status(message, status="INFO"):
|
||||
colors = {
|
||||
"SUCCESS": Colors.GREEN,
|
||||
"ERROR": Colors.RED,
|
||||
"WARNING": Colors.YELLOW,
|
||||
"INFO": Colors.BLUE
|
||||
}
|
||||
print_colored(f"[{status}] {message}", colors.get(status, Colors.NC))
|
||||
|
||||
def main():
|
||||
print_colored("Claude Code Hooks Initialization", Colors.BLUE)
|
||||
print("=" * 40)
|
||||
|
||||
# Get package root (npm package location)
|
||||
package_root = Path(__file__).parent.parent
|
||||
claude_config_dir = Path.home() / ".config" / "claude"
|
||||
hooks_config_file = claude_config_dir / "hooks.json"
|
||||
|
||||
# Check for existing installation
|
||||
print("Checking for existing Claude Hooks installation...")
|
||||
if hooks_config_file.exists():
|
||||
print_status("Claude Hooks already configured!", "WARNING")
|
||||
response = input("Overwrite existing configuration? (y/N): ").lower()
|
||||
if response != 'y':
|
||||
print("Installation cancelled.")
|
||||
return 1
|
||||
|
||||
# Check Python version
|
||||
print("Checking Python version...")
|
||||
if sys.version_info < (3, 8):
|
||||
print_status(f"Python 3.8+ required, found {sys.version_info.major}.{sys.version_info.minor}", "ERROR")
|
||||
return 1
|
||||
print_status(f"Python {sys.version_info.major}.{sys.version_info.minor} found", "SUCCESS")
|
||||
|
||||
# Create Claude config directory
|
||||
print("Creating Claude config directory...")
|
||||
claude_config_dir.mkdir(parents=True, exist_ok=True)
|
||||
print_status("Claude config directory ready", "SUCCESS")
|
||||
|
||||
# Generate hooks configuration
|
||||
print("Generating hooks configuration...")
|
||||
hooks_template = package_root / "config" / "hooks.json.template"
|
||||
|
||||
if hooks_template.exists():
|
||||
with open(hooks_template, 'r') as f:
|
||||
config_content = f.read()
|
||||
|
||||
# Replace template variables
|
||||
config_content = config_content.replace("{{INSTALL_PATH}}", str(package_root))
|
||||
|
||||
with open(hooks_config_file, 'w') as f:
|
||||
f.write(config_content)
|
||||
|
||||
print_status("Hooks configuration generated", "SUCCESS")
|
||||
else:
|
||||
# Fallback: create basic configuration
|
||||
hooks_config = {
|
||||
"hooks": {
|
||||
"UserPromptSubmit": f"python3 {package_root}/hooks/context_monitor.py",
|
||||
"PreToolUse": {
|
||||
"Bash": f"python3 {package_root}/hooks/command_validator.py"
|
||||
},
|
||||
"PostToolUse": {
|
||||
"*": f"python3 {package_root}/hooks/session_logger.py"
|
||||
},
|
||||
"Stop": f"python3 {package_root}/hooks/session_finalizer.py"
|
||||
}
|
||||
}
|
||||
|
||||
with open(hooks_config_file, 'w') as f:
|
||||
json.dump(hooks_config, f, indent=2)
|
||||
|
||||
print_status("Basic hooks configuration created", "SUCCESS")
|
||||
|
||||
# Test hook scripts
|
||||
print("Testing hook scripts...")
|
||||
test_hook = package_root / "hooks" / "context_monitor.py"
|
||||
|
||||
if test_hook.exists():
|
||||
try:
|
||||
result = subprocess.run([
|
||||
sys.executable, str(test_hook)
|
||||
], input='{"prompt": "test"}', text=True, capture_output=True, timeout=10)
|
||||
|
||||
if result.returncode == 0:
|
||||
print_status("Hook scripts working", "SUCCESS")
|
||||
else:
|
||||
print_status("Hook scripts may have issues", "WARNING")
|
||||
except subprocess.TimeoutExpired:
|
||||
print_status("Hook script test timeout", "WARNING")
|
||||
except Exception as e:
|
||||
print_status(f"Hook test error: {e}", "WARNING")
|
||||
else:
|
||||
print_status("Hook scripts not found", "WARNING")
|
||||
|
||||
print()
|
||||
print_colored("Initialization Complete!", Colors.GREEN)
|
||||
print()
|
||||
print_colored("Next Steps:", Colors.BLUE)
|
||||
print("1. Restart Claude Code to activate the hooks")
|
||||
print("2. Test with: claude-hooks test")
|
||||
print("3. Try commands that commonly fail (pip vs pip3) to see learning in action")
|
||||
print()
|
||||
print(f"📁 Installation directory: {package_root}")
|
||||
print(f"⚙️ Configuration file: {hooks_config_file}")
|
||||
print()
|
||||
print_colored("The hooks will start learning from your usage patterns automatically.", Colors.YELLOW)
|
||||
|
||||
return 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -18,6 +18,24 @@ HOOKS_CONFIG_FILE="$CLAUDE_CONFIG_DIR/hooks.json"
|
||||
echo -e "${BLUE}Claude Code Hooks Installation${NC}"
|
||||
echo "=================================="
|
||||
|
||||
# Check for existing installation
|
||||
echo -n "Checking for existing Claude Hooks installation... "
|
||||
if [ -f "$HOOKS_CONFIG_FILE" ] || command -v claude-hooks >/dev/null 2>&1; then
|
||||
echo -e "${RED}CONFLICT DETECTED${NC}"
|
||||
echo ""
|
||||
echo -e "${YELLOW}⚠️ Claude Hooks is already installed!${NC}"
|
||||
echo " This would create conflicts since hooks are user-wide."
|
||||
echo ""
|
||||
echo -e "${BLUE}To proceed:${NC}"
|
||||
echo "1. Uninstall existing hooks: claude-hooks uninstall"
|
||||
echo "2. Or manually remove: rm -rf ~/.config/claude/hooks.json ~/.local/bin/claude-hooks"
|
||||
echo "3. Then run this installer again"
|
||||
echo ""
|
||||
exit 1
|
||||
else
|
||||
echo -e "${GREEN}No conflicts found${NC}"
|
||||
fi
|
||||
|
||||
# Check Python version
|
||||
echo -n "Checking Python version... "
|
||||
if ! python3 --version >/dev/null 2>&1; then
|
||||
@ -67,6 +85,70 @@ echo -n "Creating runtime directories... "
|
||||
mkdir -p "$CLAUDE_HOOKS_DIR/.claude_hooks/"{backups,logs,patterns}
|
||||
echo -e "${GREEN}SUCCESS${NC}"
|
||||
|
||||
# Create claude-hooks command
|
||||
echo -n "Creating claude-hooks command... "
|
||||
mkdir -p "$HOME/.local/bin"
|
||||
cat > "$HOME/.local/bin/claude-hooks" << 'EOF'
|
||||
#!/bin/bash
|
||||
# Claude Hooks Command Line Interface
|
||||
|
||||
CLAUDE_HOOKS_DIR="$(dirname "$(readlink -f "$0")")/../.."
|
||||
if [ ! -d "$CLAUDE_HOOKS_DIR/claude-hooks" ]; then
|
||||
# Fallback: look for installation in common locations
|
||||
for dir in "/opt/claude-hooks" "$HOME/claude-hooks" "$HOME/.local/share/claude-hooks"; do
|
||||
if [ -d "$dir" ]; then
|
||||
CLAUDE_HOOKS_DIR="$dir"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
"status")
|
||||
echo "Claude Hooks Status:"
|
||||
if [ -f "$HOME/.config/claude/hooks.json" ]; then
|
||||
echo "✓ Hooks configuration installed"
|
||||
else
|
||||
echo "✗ No hooks configuration found"
|
||||
fi
|
||||
if command -v python3 >/dev/null 2>&1; then
|
||||
echo "✓ Python 3 available"
|
||||
else
|
||||
echo "✗ Python 3 not found"
|
||||
fi
|
||||
;;
|
||||
"uninstall")
|
||||
if [ -f "$CLAUDE_HOOKS_DIR/claude-hooks/scripts/uninstall.sh" ]; then
|
||||
exec "$CLAUDE_HOOKS_DIR/claude-hooks/scripts/uninstall.sh"
|
||||
else
|
||||
echo "Error: Uninstall script not found"
|
||||
echo "Manual removal: rm -rf ~/.config/claude/hooks.json ~/.local/bin/claude-hooks"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
"test")
|
||||
if [ -f "$CLAUDE_HOOKS_DIR/claude-hooks/scripts/test.sh" ]; then
|
||||
exec "$CLAUDE_HOOKS_DIR/claude-hooks/scripts/test.sh"
|
||||
else
|
||||
echo "Error: Test script not found"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Claude Hooks CLI"
|
||||
echo "Usage: claude-hooks {status|uninstall|test}"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " status - Show installation status"
|
||||
echo " uninstall - Remove Claude Hooks"
|
||||
echo " test - Run hook tests"
|
||||
;;
|
||||
esac
|
||||
EOF
|
||||
|
||||
chmod +x "$HOME/.local/bin/claude-hooks"
|
||||
echo -e "${GREEN}SUCCESS${NC}"
|
||||
|
||||
# Test hook scripts
|
||||
echo -n "Testing hook scripts... "
|
||||
if python3 "$CLAUDE_HOOKS_DIR/hooks/context_monitor.py" <<< '{"prompt": "test"}' >/dev/null 2>&1; then
|
||||
|
114
scripts/manual-backup.py
Normal file
114
scripts/manual-backup.py
Normal file
@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Manual Backup Script for Claude Hooks"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
# Add lib directory to path
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent / "lib"))
|
||||
|
||||
try:
|
||||
from backup_manager import BackupManager
|
||||
from session_state import SessionState
|
||||
except ImportError as e:
|
||||
print(f"Error importing backup modules: {e}")
|
||||
print("Please ensure Claude Hooks is properly installed.")
|
||||
sys.exit(1)
|
||||
|
||||
# Colors for terminal output
|
||||
class Colors:
|
||||
RED = '\033[0;31m'
|
||||
GREEN = '\033[0;32m'
|
||||
YELLOW = '\033[1;33m'
|
||||
BLUE = '\033[0;34m'
|
||||
NC = '\033[0m' # No Color
|
||||
|
||||
def print_colored(message, color):
|
||||
print(f"{color}{message}{Colors.NC}")
|
||||
|
||||
def print_status(message, status="INFO"):
|
||||
colors = {
|
||||
"SUCCESS": Colors.GREEN,
|
||||
"ERROR": Colors.RED,
|
||||
"WARNING": Colors.YELLOW,
|
||||
"INFO": Colors.BLUE
|
||||
}
|
||||
print_colored(f"[{status}] {message}", colors.get(status, Colors.NC))
|
||||
|
||||
def main():
|
||||
print_colored("Claude Hooks Manual Backup", Colors.BLUE)
|
||||
print("=" * 30)
|
||||
|
||||
# Get package root and setup paths
|
||||
package_root = Path(__file__).parent.parent
|
||||
runtime_dir = package_root / ".claude_hooks"
|
||||
|
||||
try:
|
||||
# Initialize backup manager
|
||||
backup_manager = BackupManager(str(runtime_dir / "backups"))
|
||||
session_state = SessionState(str(runtime_dir))
|
||||
|
||||
print("Creating manual backup...")
|
||||
|
||||
# Get current working directory (where user is working)
|
||||
cwd = Path.cwd()
|
||||
print(f"Backing up from: {cwd}")
|
||||
|
||||
# Create backup context
|
||||
backup_context = {
|
||||
"trigger": "manual",
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"cwd": str(cwd),
|
||||
"session_info": session_state.get_session_summary()
|
||||
}
|
||||
|
||||
# Perform backup
|
||||
backup_id = backup_manager.create_backup(
|
||||
project_path=str(cwd),
|
||||
context=backup_context,
|
||||
force=True # Allow manual backup even if recent backup exists
|
||||
)
|
||||
|
||||
if backup_id:
|
||||
print_status(f"Backup created successfully: {backup_id}", "SUCCESS")
|
||||
|
||||
# Show backup details
|
||||
backup_info = backup_manager.get_backup_info(backup_id)
|
||||
if backup_info:
|
||||
print()
|
||||
print_colored("Backup Details:", Colors.BLUE)
|
||||
print(f"📁 Backup ID: {backup_id}")
|
||||
print(f"📅 Created: {backup_info.get('timestamp', 'Unknown')}")
|
||||
print(f"📂 Location: {backup_info.get('path', 'Unknown')}")
|
||||
|
||||
if 'files_backed_up' in backup_info:
|
||||
file_count = len(backup_info['files_backed_up'])
|
||||
print(f"📄 Files: {file_count}")
|
||||
|
||||
if file_count > 0 and file_count <= 10:
|
||||
print(" Files backed up:")
|
||||
for file_path in backup_info['files_backed_up'][:10]:
|
||||
print(f" - {file_path}")
|
||||
elif file_count > 10:
|
||||
print(" Sample files backed up:")
|
||||
for file_path in backup_info['files_backed_up'][:5]:
|
||||
print(f" - {file_path}")
|
||||
print(f" ... and {file_count - 5} more")
|
||||
|
||||
print()
|
||||
print_colored("Backup completed successfully! 🎉", Colors.GREEN)
|
||||
else:
|
||||
print_status("Backup failed - check logs for details", "ERROR")
|
||||
return 1
|
||||
|
||||
except Exception as e:
|
||||
print_status(f"Backup error: {e}", "ERROR")
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
93
scripts/npm-postinstall.js
Normal file
93
scripts/npm-postinstall.js
Normal file
@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { execSync, spawn } = require('child_process');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
|
||||
console.log('📦 Claude Hooks post-install setup...');
|
||||
|
||||
// Check Python availability
|
||||
try {
|
||||
execSync('python3 --version', { stdio: 'pipe' });
|
||||
console.log('✓ Python 3 found');
|
||||
} catch {
|
||||
console.error('❌ Python 3 is required but not found');
|
||||
console.error('Please install Python 3.8+ and try again');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Install Python dependencies
|
||||
console.log('📋 Installing Python dependencies...');
|
||||
const packageRoot = path.dirname(__dirname);
|
||||
const requirementsPath = path.join(packageRoot, 'requirements.txt');
|
||||
|
||||
if (fs.existsSync(requirementsPath)) {
|
||||
try {
|
||||
execSync(`python3 -m pip install -r "${requirementsPath}" --user --quiet`, {
|
||||
stdio: 'pipe',
|
||||
cwd: packageRoot
|
||||
});
|
||||
console.log('✓ Python dependencies installed');
|
||||
} catch (error) {
|
||||
console.log('⚠️ Some Python dependencies may not have installed');
|
||||
console.log('You can install them manually: pip3 install -r requirements.txt');
|
||||
}
|
||||
} else {
|
||||
console.log('⚠️ requirements.txt not found, skipping Python dependencies');
|
||||
}
|
||||
|
||||
// Make scripts executable
|
||||
console.log('🔧 Setting up permissions...');
|
||||
const scriptsDir = path.join(packageRoot, 'scripts');
|
||||
const hooksDir = path.join(packageRoot, 'hooks');
|
||||
|
||||
[scriptsDir, hooksDir].forEach(dir => {
|
||||
if (fs.existsSync(dir)) {
|
||||
const files = fs.readdirSync(dir);
|
||||
files.forEach(file => {
|
||||
if (file.endsWith('.py') || file.endsWith('.sh')) {
|
||||
const filePath = path.join(dir, file);
|
||||
try {
|
||||
fs.chmodSync(filePath, 0o755);
|
||||
} catch (e) {
|
||||
// Ignore chmod errors on systems that don't support it
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
console.log('✓ Permissions configured');
|
||||
|
||||
// Create runtime directories
|
||||
console.log('📁 Creating runtime directories...');
|
||||
const runtimeDir = path.join(packageRoot, '.claude_hooks');
|
||||
const subdirs = ['backups', 'logs', 'patterns'];
|
||||
|
||||
try {
|
||||
if (!fs.existsSync(runtimeDir)) {
|
||||
fs.mkdirSync(runtimeDir, { recursive: true });
|
||||
}
|
||||
|
||||
subdirs.forEach(subdir => {
|
||||
const subdirPath = path.join(runtimeDir, subdir);
|
||||
if (!fs.existsSync(subdirPath)) {
|
||||
fs.mkdirSync(subdirPath, { recursive: true });
|
||||
}
|
||||
});
|
||||
|
||||
console.log('✓ Runtime directories created');
|
||||
} catch (error) {
|
||||
console.log('⚠️ Could not create runtime directories:', error.message);
|
||||
}
|
||||
|
||||
console.log('');
|
||||
console.log('🎉 Claude Hooks installed successfully!');
|
||||
console.log('');
|
||||
console.log('Next steps:');
|
||||
console.log('1. Run: claude-hooks init');
|
||||
console.log('2. Restart Claude Code to activate hooks');
|
||||
console.log('3. Test with: claude-hooks test');
|
||||
console.log('');
|
||||
console.log('For help: claude-hooks --help');
|
33
scripts/npm-preuninstall.js
Normal file
33
scripts/npm-preuninstall.js
Normal file
@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
|
||||
console.log('🗑️ Claude Hooks pre-uninstall cleanup...');
|
||||
|
||||
// Check if hooks are configured
|
||||
const hooksConfig = path.join(os.homedir(), '.config', 'claude', 'hooks.json');
|
||||
|
||||
if (fs.existsSync(hooksConfig)) {
|
||||
console.log('⚠️ Claude Hooks configuration detected');
|
||||
console.log('');
|
||||
console.log('IMPORTANT: This will remove the NPM package but leave hooks active!');
|
||||
console.log('');
|
||||
console.log('To properly uninstall:');
|
||||
console.log('1. Run: claude-hooks uninstall');
|
||||
console.log('2. Then run: npm uninstall -g claude-hooks');
|
||||
console.log('');
|
||||
console.log('Or to force removal of hooks configuration:');
|
||||
console.log(`rm -f "${hooksConfig}"`);
|
||||
console.log('');
|
||||
|
||||
// Give user a moment to see the message
|
||||
setTimeout(() => {
|
||||
console.log('Continuing with NPM package removal...');
|
||||
}, 2000);
|
||||
} else {
|
||||
console.log('✓ No active hooks configuration found');
|
||||
}
|
||||
|
||||
console.log('✓ Pre-uninstall check complete');
|
172
scripts/test.py
Normal file
172
scripts/test.py
Normal file
@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Claude Hooks Test Script for NPM Distribution"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import subprocess
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
# Colors for terminal output
|
||||
class Colors:
|
||||
RED = '\033[0;31m'
|
||||
GREEN = '\033[0;32m'
|
||||
YELLOW = '\033[1;33m'
|
||||
BLUE = '\033[0;34m'
|
||||
NC = '\033[0m' # No Color
|
||||
|
||||
def print_colored(message, color):
|
||||
print(f"{color}{message}{Colors.NC}")
|
||||
|
||||
def print_status(message, status="INFO"):
|
||||
colors = {
|
||||
"SUCCESS": Colors.GREEN,
|
||||
"ERROR": Colors.RED,
|
||||
"WARNING": Colors.YELLOW,
|
||||
"INFO": Colors.BLUE
|
||||
}
|
||||
print_colored(f"[{status}] {message}", colors.get(status, Colors.NC))
|
||||
|
||||
def test_hook_script(hook_path, test_input=None):
|
||||
"""Test a single hook script"""
|
||||
if not hook_path.exists():
|
||||
return False, "Script not found"
|
||||
|
||||
try:
|
||||
cmd = [sys.executable, str(hook_path)]
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
input=test_input or "",
|
||||
text=True,
|
||||
capture_output=True,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
return True, "Success"
|
||||
else:
|
||||
return False, f"Exit code {result.returncode}: {result.stderr.strip()}"
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
return False, "Timeout"
|
||||
except Exception as e:
|
||||
return False, str(e)
|
||||
|
||||
def main():
|
||||
print_colored("Claude Hooks Test Suite", Colors.BLUE)
|
||||
print("=" * 30)
|
||||
|
||||
# Get package root
|
||||
package_root = Path(__file__).parent.parent
|
||||
hooks_dir = package_root / "hooks"
|
||||
claude_config_dir = Path.home() / ".config" / "claude"
|
||||
hooks_config_file = claude_config_dir / "hooks.json"
|
||||
|
||||
tests_passed = 0
|
||||
tests_total = 0
|
||||
|
||||
# Test 1: Check configuration exists
|
||||
print("Testing configuration...")
|
||||
tests_total += 1
|
||||
if hooks_config_file.exists():
|
||||
print_status("✓ Hooks configuration found", "SUCCESS")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print_status("✗ Hooks configuration missing", "ERROR")
|
||||
print(" Run: claude-hooks init")
|
||||
|
||||
# Test 2: Check Python version
|
||||
print("Testing Python compatibility...")
|
||||
tests_total += 1
|
||||
if sys.version_info >= (3, 8):
|
||||
print_status(f"✓ Python {sys.version_info.major}.{sys.version_info.minor} compatible", "SUCCESS")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print_status(f"✗ Python 3.8+ required, found {sys.version_info.major}.{sys.version_info.minor}", "ERROR")
|
||||
|
||||
# Test 3: Test hook scripts
|
||||
hook_tests = [
|
||||
("context_monitor.py", '{"prompt": "test prompt", "context_size": 1000}'),
|
||||
("command_validator.py", '{"command": "pip install requests", "cwd": "/tmp"}'),
|
||||
("session_logger.py", '{"tool": "Bash", "args": {"command": "echo test"}}'),
|
||||
("session_finalizer.py", '{"session_duration": 300, "tools_used": ["Bash", "Edit"]}')
|
||||
]
|
||||
|
||||
for script_name, test_input in hook_tests:
|
||||
print(f"Testing {script_name}...")
|
||||
tests_total += 1
|
||||
|
||||
hook_path = hooks_dir / script_name
|
||||
success, message = test_hook_script(hook_path, test_input)
|
||||
|
||||
if success:
|
||||
print_status(f"✓ {script_name} working", "SUCCESS")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print_status(f"✗ {script_name} failed: {message}", "ERROR")
|
||||
|
||||
# Test 4: Check runtime directories
|
||||
print("Testing runtime directories...")
|
||||
tests_total += 1
|
||||
runtime_dir = package_root / ".claude_hooks"
|
||||
required_dirs = ["backups", "logs", "patterns"]
|
||||
|
||||
missing_dirs = []
|
||||
for dirname in required_dirs:
|
||||
if not (runtime_dir / dirname).exists():
|
||||
missing_dirs.append(dirname)
|
||||
|
||||
if not missing_dirs:
|
||||
print_status("✓ Runtime directories present", "SUCCESS")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print_status(f"✗ Missing directories: {', '.join(missing_dirs)}", "ERROR")
|
||||
|
||||
# Test 5: Test shadow learner
|
||||
print("Testing shadow learner...")
|
||||
tests_total += 1
|
||||
try:
|
||||
# Import test
|
||||
sys.path.insert(0, str(package_root))
|
||||
from lib.shadow_learner import ShadowLearner
|
||||
|
||||
learner = ShadowLearner(str(package_root / ".claude_hooks" / "patterns"))
|
||||
|
||||
# Basic functionality test
|
||||
learner.learn_command_failure("pip", "pip3", 0.9)
|
||||
suggestion = learner.get_suggestion("pip")
|
||||
|
||||
if suggestion and suggestion.get('suggestion') == 'pip3':
|
||||
print_status("✓ Shadow learner functional", "SUCCESS")
|
||||
tests_passed += 1
|
||||
else:
|
||||
print_status("✗ Shadow learner not learning correctly", "ERROR")
|
||||
|
||||
except Exception as e:
|
||||
print_status(f"✗ Shadow learner error: {e}", "ERROR")
|
||||
|
||||
# Summary
|
||||
print()
|
||||
print_colored("Test Results", Colors.BLUE)
|
||||
print("-" * 20)
|
||||
|
||||
if tests_passed == tests_total:
|
||||
print_status(f"All {tests_total} tests passed! 🎉", "SUCCESS")
|
||||
print()
|
||||
print_colored("Claude Hooks is ready to use!", Colors.GREEN)
|
||||
print("Try running some commands to see the learning in action:")
|
||||
print("- pip install requests (should suggest pip3)")
|
||||
print("- python script.py (should suggest python3)")
|
||||
return 0
|
||||
else:
|
||||
print_status(f"{tests_passed}/{tests_total} tests passed", "WARNING")
|
||||
print()
|
||||
print_colored("Some issues detected. Check installation:", Colors.YELLOW)
|
||||
print("1. Run: claude-hooks init")
|
||||
print("2. Restart Claude Code")
|
||||
print("3. Run tests again")
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
110
scripts/uninstall.py
Normal file
110
scripts/uninstall.py
Normal file
@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Claude Hooks Uninstallation Script for NPM Distribution"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
# Colors for terminal output
|
||||
class Colors:
|
||||
RED = '\033[0;31m'
|
||||
GREEN = '\033[0;32m'
|
||||
YELLOW = '\033[1;33m'
|
||||
BLUE = '\033[0;34m'
|
||||
NC = '\033[0m' # No Color
|
||||
|
||||
def print_colored(message, color):
|
||||
print(f"{color}{message}{Colors.NC}")
|
||||
|
||||
def print_status(message, status="INFO"):
|
||||
colors = {
|
||||
"SUCCESS": Colors.GREEN,
|
||||
"ERROR": Colors.RED,
|
||||
"WARNING": Colors.YELLOW,
|
||||
"INFO": Colors.BLUE
|
||||
}
|
||||
print_colored(f"[{status}] {message}", colors.get(status, Colors.NC))
|
||||
|
||||
def main():
|
||||
print_colored("Claude Code Hooks Uninstallation", Colors.BLUE)
|
||||
print("=" * 40)
|
||||
|
||||
# Configuration paths
|
||||
claude_config_dir = Path.home() / ".config" / "claude"
|
||||
hooks_config_file = claude_config_dir / "hooks.json"
|
||||
settings_file = claude_config_dir / "settings.json"
|
||||
|
||||
# Check if hooks are installed
|
||||
if not hooks_config_file.exists():
|
||||
print_status("No Claude Hooks configuration found", "WARNING")
|
||||
print("Nothing to uninstall.")
|
||||
return 0
|
||||
|
||||
print_colored("This will remove Claude Hooks configuration:", Colors.YELLOW)
|
||||
print(f"- Remove hooks configuration: {hooks_config_file}")
|
||||
print("- Remove hooks from Claude settings (if present)")
|
||||
print("- Preserve hook data and backups")
|
||||
print()
|
||||
|
||||
response = input("Continue with uninstallation? (y/n): ").lower()
|
||||
if response != 'y':
|
||||
print("Uninstallation cancelled.")
|
||||
return 0
|
||||
|
||||
# Remove hooks configuration
|
||||
print("Removing hooks configuration...")
|
||||
try:
|
||||
hooks_config_file.unlink()
|
||||
print_status("Hooks configuration removed", "SUCCESS")
|
||||
except FileNotFoundError:
|
||||
print_status("Hooks configuration not found", "WARNING")
|
||||
except Exception as e:
|
||||
print_status(f"Error removing hooks configuration: {e}", "ERROR")
|
||||
return 1
|
||||
|
||||
# Remove from Claude settings if it exists
|
||||
if settings_file.exists():
|
||||
print("Removing hooks from Claude settings...")
|
||||
try:
|
||||
with open(settings_file, 'r') as f:
|
||||
settings = json.load(f)
|
||||
|
||||
if 'hooks' in settings:
|
||||
# Backup settings
|
||||
backup_file = settings_file.with_suffix('.json.backup')
|
||||
shutil.copy2(settings_file, backup_file)
|
||||
|
||||
# Remove hooks key
|
||||
del settings['hooks']
|
||||
|
||||
# Write back
|
||||
with open(settings_file, 'w') as f:
|
||||
json.dump(settings, f, indent=2)
|
||||
|
||||
print_status("Hooks removed from Claude settings", "SUCCESS")
|
||||
else:
|
||||
print_status("No hooks found in Claude settings", "WARNING")
|
||||
|
||||
except json.JSONDecodeError:
|
||||
print_status("Could not parse Claude settings file", "WARNING")
|
||||
except Exception as e:
|
||||
print_status(f"Error updating Claude settings: {e}", "WARNING")
|
||||
else:
|
||||
print_status("No Claude settings file found", "WARNING")
|
||||
|
||||
print()
|
||||
print_colored("Uninstallation Complete!", Colors.GREEN)
|
||||
print()
|
||||
print_colored("Next Steps:", Colors.BLUE)
|
||||
print("1. Restart Claude Code to deactivate hooks")
|
||||
print("2. Hook data and backups are preserved in case you reinstall")
|
||||
print("3. To completely remove the package: npm uninstall -g claude-hooks")
|
||||
print()
|
||||
print_colored("Note: To completely remove all data, delete hook directories manually", Colors.YELLOW)
|
||||
|
||||
return 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -10,103 +10,94 @@ YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
CLAUDE_CONFIG_DIR="$HOME/.config/claude"
|
||||
HOOKS_CONFIG_FILE="$CLAUDE_CONFIG_DIR/hooks.json"
|
||||
CLAUDE_HOOKS_CMD="$HOME/.local/bin/claude-hooks"
|
||||
|
||||
echo -e "${BLUE}Claude Code Hooks Uninstallation${NC}"
|
||||
echo "===================================="
|
||||
|
||||
# Warn user about data loss
|
||||
echo -e "${YELLOW}WARNING:${NC} This will remove:"
|
||||
echo "- Hook configuration from Claude Code"
|
||||
echo "- All learned patterns and session data"
|
||||
echo "- Backup files (if in project directory)"
|
||||
# Check if hooks are installed
|
||||
if [ ! -f "$HOOKS_CONFIG_FILE" ] && [ ! -f "$CLAUDE_HOOKS_CMD" ]; then
|
||||
echo -e "${YELLOW}No Claude Hooks installation found${NC}"
|
||||
echo "Nothing to uninstall."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo -e "${YELLOW}This will remove Claude Hooks from your system:${NC}"
|
||||
echo "- Remove hooks configuration: $HOOKS_CONFIG_FILE"
|
||||
echo "- Remove claude-hooks command: $CLAUDE_HOOKS_CMD"
|
||||
echo "- Preserve hook data and backups"
|
||||
echo ""
|
||||
read -p "Are you sure you want to continue? (y/N): " -n 1 -r
|
||||
|
||||
read -p "Continue with uninstallation? (y/n): " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo "Uninstallation cancelled."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Remove hooks from Claude settings
|
||||
# Remove hooks configuration
|
||||
echo -n "Removing hooks configuration... "
|
||||
if [ -f "$HOOKS_CONFIG_FILE" ]; then
|
||||
rm -f "$HOOKS_CONFIG_FILE"
|
||||
echo -e "${GREEN}SUCCESS${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}Not found${NC}"
|
||||
fi
|
||||
|
||||
# Remove claude-hooks command
|
||||
echo -n "Removing claude-hooks command... "
|
||||
if [ -f "$CLAUDE_HOOKS_CMD" ]; then
|
||||
rm -f "$CLAUDE_HOOKS_CMD"
|
||||
echo -e "${GREEN}SUCCESS${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}Not found${NC}"
|
||||
fi
|
||||
|
||||
# Remove from Claude settings if it exists
|
||||
if [ -f "$HOME/.config/claude/settings.json" ]; then
|
||||
echo -n "Removing hooks from Claude settings... "
|
||||
|
||||
if grep -q "hooks" "$HOME/.config/claude/settings.json" 2>/dev/null; then
|
||||
# Backup settings
|
||||
cp "$HOME/.config/claude/settings.json" "$HOME/.config/claude/settings.json.backup"
|
||||
|
||||
# Remove hooks configuration
|
||||
python3 << 'EOF'
|
||||
# Remove hooks from settings
|
||||
python3 << EOF
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
|
||||
try:
|
||||
with open('/home/usr/.config/claude/settings.json', 'r') as f:
|
||||
settings_path = os.path.expanduser('~/.config/claude/settings.json')
|
||||
with open(settings_path, 'r') as f:
|
||||
settings = json.load(f)
|
||||
|
||||
# Remove hooks section
|
||||
# Remove hooks key if it exists
|
||||
if 'hooks' in settings:
|
||||
del settings['hooks']
|
||||
|
||||
with open('/home/usr/.config/claude/settings.json', 'w') as f:
|
||||
with open(settings_path, 'w') as f:
|
||||
json.dump(settings, f, indent=2)
|
||||
|
||||
print("SUCCESS")
|
||||
else:
|
||||
print("NO HOOKS FOUND")
|
||||
|
||||
print("Not found in settings")
|
||||
except Exception as e:
|
||||
print(f"ERROR: {e}")
|
||||
sys.exit(1)
|
||||
EOF
|
||||
|
||||
echo -e "${GREEN}Hooks removed from Claude settings${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}No Claude settings file found${NC}"
|
||||
fi
|
||||
|
||||
# Remove hooks configuration file
|
||||
if [ -f "$HOOKS_CONFIG_FILE" ]; then
|
||||
echo -n "Removing hooks configuration file... "
|
||||
rm -f "$HOOKS_CONFIG_FILE"
|
||||
echo -e "${GREEN}SUCCESS${NC}"
|
||||
fi
|
||||
|
||||
# Ask about removing data
|
||||
echo ""
|
||||
read -p "Remove learned patterns and session data? (y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo -n "Removing hook data... "
|
||||
rm -rf ".claude_hooks"
|
||||
rm -f "LAST_SESSION.md" "ACTIVE_TODOS.md" "RECOVERY_GUIDE.md"
|
||||
echo -e "${GREEN}SUCCESS${NC}"
|
||||
fi
|
||||
|
||||
# Ask about removing project files
|
||||
echo ""
|
||||
read -p "Remove project files (hooks, scripts, etc.)? This cannot be undone! (y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
CLAUDE_HOOKS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
echo -n "Removing project files... "
|
||||
cd ..
|
||||
rm -rf "$CLAUDE_HOOKS_DIR"
|
||||
echo -e "${GREEN}SUCCESS${NC}"
|
||||
echo ""
|
||||
echo -e "${GREEN}Complete uninstallation finished!${NC}"
|
||||
echo "All files have been removed."
|
||||
else
|
||||
echo ""
|
||||
echo -e "${GREEN}Partial uninstallation finished!${NC}"
|
||||
echo "Hooks disabled, but project files remain."
|
||||
else
|
||||
echo -e "${YELLOW}Not found in settings${NC}"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${BLUE}Post-uninstallation:${NC}"
|
||||
echo "1. Restart Claude Code to ensure hooks are disabled"
|
||||
echo "2. Check that no hook-related errors appear"
|
||||
echo "3. Your backup files (if any) remain in git history"
|
||||
echo -e "${GREEN}Uninstallation Complete!${NC}"
|
||||
echo ""
|
||||
echo -e "${GREEN}Claude Code Hooks successfully removed.${NC}"
|
||||
echo -e "${BLUE}Next Steps:${NC}"
|
||||
echo "1. Restart Claude Code to deactivate hooks"
|
||||
echo "2. Hook data and backups are preserved in case you reinstall"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Note:${NC} To completely remove all data, delete the claude-hooks directory manually"
|
1
test-clone
Submodule
1
test-clone
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 1e169a5d459fe1c1ef42da0bffcb485059efe46a
|
Loading…
x
Reference in New Issue
Block a user