Initial commit
This commit is contained in:
164
hooks/session_end_recorder.py
Executable file
164
hooks/session_end_recorder.py
Executable file
@@ -0,0 +1,164 @@
|
||||
#!/usr/bin/env -S uv run --script
|
||||
# /// script
|
||||
# requires-python = ">=3.10"
|
||||
# dependencies = ["pyyaml>=6.0"]
|
||||
# ///
|
||||
"""
|
||||
SessionEnd Recorder - Capture Session Metadata for Next Session
|
||||
|
||||
Records lightweight session metadata at session end:
|
||||
- Session ID and timestamp
|
||||
- Last commit hash (for git diff baseline)
|
||||
- Current branch
|
||||
- Files worked on
|
||||
- Brief summary
|
||||
|
||||
**Token Overhead:** ~100 tokens (write only, no injection)
|
||||
**Blocking:** No
|
||||
|
||||
Hook Protocol:
|
||||
- Input: JSON via stdin with session data
|
||||
- Output: JSON via stdout
|
||||
- IMPORTANT: Never blocks (always {"continue": true})
|
||||
"""
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
import yaml
|
||||
|
||||
|
||||
def get_current_commit_hash() -> str:
|
||||
"""Get current git commit hash."""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["git", "rev-parse", "HEAD"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=1,
|
||||
)
|
||||
return result.stdout.strip()
|
||||
except Exception as e:
|
||||
print(f"DEBUG: Failed to get commit hash: {e}", file=sys.stderr)
|
||||
return "unknown"
|
||||
|
||||
|
||||
def get_current_branch() -> str:
|
||||
"""Get current git branch."""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["git", "branch", "--show-current"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=1,
|
||||
)
|
||||
return result.stdout.strip()
|
||||
except Exception as e:
|
||||
print(f"DEBUG: Failed to get branch: {e}", file=sys.stderr)
|
||||
return "unknown"
|
||||
|
||||
|
||||
def get_files_changed_in_session(start_hash: str) -> list[str]:
|
||||
"""Get files changed between start_hash and HEAD."""
|
||||
try:
|
||||
# Get files changed since session start
|
||||
result = subprocess.run(
|
||||
["git", "diff", "--name-only", f"{start_hash}..HEAD"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=2,
|
||||
)
|
||||
|
||||
files = [f.strip() for f in result.stdout.split("\n") if f.strip()]
|
||||
|
||||
# Also include uncommitted changes
|
||||
result2 = subprocess.run(
|
||||
["git", "status", "--short"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=1,
|
||||
)
|
||||
|
||||
for line in result2.stdout.split("\n"):
|
||||
if line.strip():
|
||||
# Extract filename (format: "M file.py")
|
||||
parts = line.split(maxsplit=1)
|
||||
if len(parts) == 2:
|
||||
files.append(parts[1])
|
||||
|
||||
# Deduplicate
|
||||
return list(set(files))
|
||||
|
||||
except Exception as e:
|
||||
print(f"DEBUG: Failed to get changed files: {e}", file=sys.stderr)
|
||||
return []
|
||||
|
||||
|
||||
def main():
|
||||
"""SessionEnd recorder entry point."""
|
||||
try:
|
||||
# Read hook data
|
||||
hook_data = json.loads(sys.stdin.read())
|
||||
|
||||
session_id = hook_data.get("session_id", "unknown")
|
||||
|
||||
print(f"DEBUG: SessionEnd recorder triggered", file=sys.stderr)
|
||||
print(f"DEBUG: Session: {session_id}", file=sys.stderr)
|
||||
|
||||
# Get git state
|
||||
commit_hash = get_current_commit_hash()
|
||||
branch = get_current_branch()
|
||||
|
||||
# Load previous session data to calculate files worked on
|
||||
cache_dir = Path.home() / ".claude" / "plugins" / "contextune" / ".cache"
|
||||
cache_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
last_session_file = cache_dir / "last_session.yaml"
|
||||
|
||||
# Get session start commit hash (if tracked)
|
||||
session_start_hash = commit_hash
|
||||
if last_session_file.exists():
|
||||
try:
|
||||
with open(last_session_file) as f:
|
||||
prev_session = yaml.safe_load(f) or {}
|
||||
session_start_hash = prev_session.get("last_commit", commit_hash)
|
||||
except:
|
||||
pass
|
||||
|
||||
# Get files changed during this session
|
||||
files_worked_on = get_files_changed_in_session(session_start_hash)
|
||||
|
||||
# Create session record
|
||||
session_record = {
|
||||
"session_id": session_id,
|
||||
"ended_at": datetime.now().isoformat() + "Z",
|
||||
"last_commit": commit_hash,
|
||||
"branch": branch,
|
||||
"files_worked_on": files_worked_on[:20], # Limit to 20 files
|
||||
"file_count": len(files_worked_on),
|
||||
}
|
||||
|
||||
# Write to cache
|
||||
with open(last_session_file, "w") as f:
|
||||
yaml.dump(session_record, f, default_flow_style=False)
|
||||
|
||||
print(
|
||||
f"DEBUG: ✅ Recorded session metadata ({len(files_worked_on)} files)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
print(f"DEBUG: SessionEnd recorder error: {e}", file=sys.stderr)
|
||||
import traceback
|
||||
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
|
||||
# Always continue (don't block session end)
|
||||
response = {"continue": True}
|
||||
print(json.dumps(response))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user