207 lines
6.4 KiB
Python
Executable File
207 lines
6.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import sys
|
|
import subprocess
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
|
|
try:
|
|
from dotenv import load_dotenv
|
|
load_dotenv()
|
|
except ImportError:
|
|
pass # dotenv is optional
|
|
|
|
|
|
def log_session_start(input_data):
|
|
"""Log session start event to logs directory."""
|
|
# Ensure logs directory exists
|
|
log_dir = Path(".claude/logs")
|
|
log_dir.mkdir(parents=True, exist_ok=True)
|
|
log_file = log_dir / 'session_start.json'
|
|
|
|
# Read existing log data or initialize empty list
|
|
if log_file.exists():
|
|
with open(log_file, 'r') as f:
|
|
try:
|
|
log_data = json.load(f)
|
|
except (json.JSONDecodeError, ValueError):
|
|
log_data = []
|
|
else:
|
|
log_data = []
|
|
|
|
# Append the entire input data
|
|
log_data.append(input_data)
|
|
|
|
# Write back to file with formatting
|
|
with open(log_file, 'w') as f:
|
|
json.dump(log_data, f, indent=2)
|
|
|
|
|
|
def get_git_status():
|
|
"""Get current git status information."""
|
|
try:
|
|
# Get current branch
|
|
branch_result = subprocess.run(
|
|
['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=5
|
|
)
|
|
current_branch = branch_result.stdout.strip() if branch_result.returncode == 0 else "unknown"
|
|
|
|
# Get uncommitted changes count
|
|
status_result = subprocess.run(
|
|
['git', 'status', '--porcelain'],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=5
|
|
)
|
|
if status_result.returncode == 0:
|
|
changes = status_result.stdout.strip().split('\n') if status_result.stdout.strip() else []
|
|
uncommitted_count = len(changes)
|
|
else:
|
|
uncommitted_count = 0
|
|
|
|
return current_branch, uncommitted_count
|
|
except Exception:
|
|
return None, None
|
|
|
|
|
|
def get_recent_issues():
|
|
"""Get recent GitHub issues if gh CLI is available."""
|
|
try:
|
|
# Check if gh is available
|
|
gh_check = subprocess.run(['which', 'gh'], capture_output=True)
|
|
if gh_check.returncode != 0:
|
|
return None
|
|
|
|
# Get recent open issues
|
|
result = subprocess.run(
|
|
['gh', 'issue', 'list', '--limit', '5', '--state', 'open'],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=10
|
|
)
|
|
if result.returncode == 0 and result.stdout.strip():
|
|
return result.stdout.strip()
|
|
except Exception:
|
|
pass
|
|
return None
|
|
|
|
|
|
def load_development_context(source):
|
|
"""Load relevant development context based on session source."""
|
|
context_parts = []
|
|
|
|
# Add timestamp
|
|
context_parts.append(f"Session started at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
|
context_parts.append(f"Session source: {source}")
|
|
|
|
# Add git information
|
|
branch, changes = get_git_status()
|
|
if branch:
|
|
context_parts.append(f"Git branch: {branch}")
|
|
if changes > 0:
|
|
context_parts.append(f"Uncommitted changes: {changes} files")
|
|
|
|
# Load project-specific context files if they exist
|
|
context_files = [
|
|
".claude/CONTEXT.md",
|
|
".claude/TODO.md",
|
|
"TODO.md",
|
|
".github/ISSUE_TEMPLATE.md"
|
|
]
|
|
|
|
for file_path in context_files:
|
|
if Path(file_path).exists():
|
|
try:
|
|
with open(file_path, 'r') as f:
|
|
content = f.read().strip()
|
|
if content:
|
|
context_parts.append(f"\n--- Content from {file_path} ---")
|
|
context_parts.append(content[:1000]) # Limit to first 1000 chars
|
|
except Exception:
|
|
pass
|
|
|
|
# Add recent issues if available
|
|
issues = get_recent_issues()
|
|
if issues:
|
|
context_parts.append("\n--- Recent GitHub Issues ---")
|
|
context_parts.append(issues)
|
|
|
|
return "\n".join(context_parts)
|
|
|
|
|
|
def main():
|
|
try:
|
|
# Parse command line arguments
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('--load-context', action='store_true',
|
|
help='Load development context at session start')
|
|
parser.add_argument('--announce', action='store_true',
|
|
help='Announce session start via TTS')
|
|
args = parser.parse_args()
|
|
|
|
# Read JSON input from stdin
|
|
input_data = json.loads(sys.stdin.read())
|
|
|
|
# Extract fields
|
|
session_id = input_data.get('session_id', 'unknown')
|
|
source = input_data.get('source', 'unknown') # "startup", "resume", or "clear"
|
|
|
|
# Log the session start event
|
|
log_session_start(input_data)
|
|
|
|
# Load development context if requested
|
|
if args.load_context:
|
|
context = load_development_context(source)
|
|
if context:
|
|
# Using JSON output to add context
|
|
output = {
|
|
"hookSpecificOutput": {
|
|
"hookEventName": "SessionStart",
|
|
"additionalContext": context
|
|
}
|
|
}
|
|
print(json.dumps(output))
|
|
sys.exit(0)
|
|
|
|
# Announce session start if requested
|
|
if args.announce:
|
|
try:
|
|
# Try to use TTS to announce session start
|
|
script_dir = Path(__file__).parent
|
|
tts_script = script_dir / "utils" / "tts" / "pyttsx3_tts.py"
|
|
|
|
if tts_script.exists():
|
|
messages = {
|
|
"startup": "Claude Code session started",
|
|
"resume": "Resuming previous session",
|
|
"clear": "Starting fresh session"
|
|
}
|
|
message = messages.get(source, "Session started")
|
|
|
|
subprocess.run(
|
|
["python3", str(tts_script), message],
|
|
capture_output=True,
|
|
timeout=5
|
|
)
|
|
except Exception:
|
|
pass
|
|
|
|
# Success
|
|
sys.exit(0)
|
|
|
|
except json.JSONDecodeError:
|
|
# Handle JSON decode errors gracefully
|
|
sys.exit(0)
|
|
except Exception:
|
|
# Handle any other errors gracefully
|
|
sys.exit(0)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main() |