✨ Features: - 🧠 Shadow learner that builds intelligence from command patterns - 🛡️ Smart command validation with safety checks - 💾 Automatic context monitoring and backup system - 🔄 Session continuity across Claude restarts 📚 Documentation: - Complete Diátaxis-organized documentation - Learning-oriented tutorial for getting started - Task-oriented how-to guides for specific problems - Information-oriented reference for quick lookup - Understanding-oriented explanations of architecture 🚀 Installation: - One-command installation script - Bootstrap prompt for installation via Claude - Cross-platform compatibility - Comprehensive testing suite 🎯 Ready for real-world use and community feedback! 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
180 lines
5.6 KiB
Python
Executable File
180 lines
5.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Session Finalizer Hook - Stop hook
|
|
Finalizes session, creates documentation, and saves state
|
|
"""
|
|
|
|
import sys
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
|
|
# Add lib directory to path
|
|
sys.path.insert(0, str(Path(__file__).parent.parent / "lib"))
|
|
|
|
from session_state import SessionStateManager
|
|
from shadow_learner import ShadowLearner
|
|
from context_monitor import ContextMonitor
|
|
|
|
|
|
def main():
|
|
"""Main hook entry point"""
|
|
try:
|
|
# Read input from Claude Code (if any)
|
|
try:
|
|
input_data = json.loads(sys.stdin.read())
|
|
except:
|
|
input_data = {}
|
|
|
|
# Initialize components
|
|
session_manager = SessionStateManager()
|
|
shadow_learner = ShadowLearner()
|
|
context_monitor = ContextMonitor()
|
|
|
|
# Create session documentation
|
|
session_manager.create_continuation_docs()
|
|
|
|
# Save all learned patterns
|
|
shadow_learner.save_database()
|
|
|
|
# Get session summary for logging
|
|
session_summary = session_manager.get_session_summary()
|
|
|
|
# Create recovery guide if session was interrupted
|
|
create_recovery_info(session_summary, context_monitor)
|
|
|
|
# Clean up session
|
|
session_manager.cleanup_session()
|
|
|
|
# Log session completion
|
|
log_session_completion(session_summary)
|
|
|
|
# Always allow - this is a cleanup hook
|
|
response = {
|
|
"allow": True,
|
|
"message": f"Session finalized. Modified {len(session_summary.get('modified_files', []))} files, used {session_summary.get('session_stats', {}).get('total_tool_calls', 0)} tools."
|
|
}
|
|
|
|
print(json.dumps(response))
|
|
sys.exit(0)
|
|
|
|
except Exception as e:
|
|
# Session finalization should never block
|
|
response = {
|
|
"allow": True,
|
|
"message": f"Session finalization error: {str(e)}"
|
|
}
|
|
print(json.dumps(response))
|
|
sys.exit(0)
|
|
|
|
|
|
def create_recovery_info(session_summary: dict, context_monitor: ContextMonitor):
|
|
"""Create recovery information if needed"""
|
|
try:
|
|
context_usage = context_monitor.get_context_usage_ratio()
|
|
|
|
# If context was high when session ended, create recovery guide
|
|
if context_usage > 0.8:
|
|
recovery_content = f"""# Session Recovery Information
|
|
|
|
## Context Status
|
|
- **Context Usage**: {context_usage:.1%} when session ended
|
|
- **Reason**: Session ended with high context usage
|
|
|
|
## What This Means
|
|
Your Claude session ended while using a significant amount of context. This could mean:
|
|
1. You were working on a complex task
|
|
2. Context limits were approaching
|
|
3. Session was interrupted
|
|
|
|
## Recovery Steps
|
|
|
|
### 1. Check Your Progress
|
|
Review these recently modified files:
|
|
"""
|
|
|
|
for file_path in session_summary.get('modified_files', []):
|
|
recovery_content += f"- {file_path}\n"
|
|
|
|
recovery_content += f"""
|
|
### 2. Review Last Actions
|
|
Recent commands executed:
|
|
"""
|
|
|
|
recent_commands = session_summary.get('commands_executed', [])[-5:]
|
|
for cmd_info in recent_commands:
|
|
recovery_content += f"- `{cmd_info['command']}`\n"
|
|
|
|
recovery_content += f"""
|
|
### 3. Continue Your Work
|
|
1. Check `ACTIVE_TODOS.md` for pending tasks
|
|
2. Review `LAST_SESSION.md` for complete session history
|
|
3. Use `git status` to see current file changes
|
|
4. Consider committing your progress: `git add -A && git commit -m "Work in progress"`
|
|
|
|
### 4. Available Backups
|
|
"""
|
|
|
|
for backup in session_summary.get('backup_history', []):
|
|
status = "✅" if backup['success'] else "❌"
|
|
recovery_content += f"- {status} {backup['backup_id']} - {backup['reason']}\n"
|
|
|
|
recovery_content += f"""
|
|
## Quick Recovery Commands
|
|
```bash
|
|
# Check current status
|
|
git status
|
|
|
|
# View recent changes
|
|
git diff
|
|
|
|
# List available backups
|
|
ls .claude_hooks/backups/
|
|
|
|
# View active todos
|
|
cat ACTIVE_TODOS.md
|
|
|
|
# View last session summary
|
|
cat LAST_SESSION.md
|
|
```
|
|
|
|
*This recovery guide was created because your session ended with {context_usage:.1%} context usage.*
|
|
"""
|
|
|
|
with open("RECOVERY_GUIDE.md", 'w') as f:
|
|
f.write(recovery_content)
|
|
|
|
except Exception:
|
|
pass # Don't let recovery guide creation break session finalization
|
|
|
|
|
|
def log_session_completion(session_summary: dict):
|
|
"""Log session completion for analysis"""
|
|
try:
|
|
log_dir = Path(".claude_hooks/logs")
|
|
log_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
from datetime import datetime
|
|
|
|
completion_log = {
|
|
"timestamp": datetime.now().isoformat(),
|
|
"type": "session_completion",
|
|
"session_id": session_summary.get("session_id", "unknown"),
|
|
"duration_minutes": session_summary.get("session_stats", {}).get("duration_minutes", 0),
|
|
"total_tools": session_summary.get("session_stats", {}).get("total_tool_calls", 0),
|
|
"files_modified": len(session_summary.get("modified_files", [])),
|
|
"commands_executed": session_summary.get("session_stats", {}).get("total_commands", 0),
|
|
"backups_created": len(session_summary.get("backup_history", []))
|
|
}
|
|
|
|
log_file = log_dir / "session_completions.jsonl"
|
|
|
|
with open(log_file, 'a') as f:
|
|
f.write(json.dumps(completion_log) + "\n")
|
|
|
|
except Exception:
|
|
pass # Don't let logging errors break finalization
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |