Features Added: • Docker containerization with multi-stage Python 3.12 build • Caddy reverse proxy integration with automatic SSL • File upload interface for .claude.json imports with preview • Comprehensive hook system with 39+ hook types across 9 categories • Complete documentation system with Docker and import guides Technical Improvements: • Enhanced database models with hook tracking capabilities • Robust file validation and error handling for uploads • Production-ready Docker compose configuration • Health checks and resource limits for containers • Database initialization scripts for containerized deployments Documentation: • Docker Deployment Guide with troubleshooting • Data Import Guide with step-by-step instructions • Updated Getting Started guide with new features • Enhanced documentation index with responsive grid layout 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
225 lines
10 KiB
Python
225 lines
10 KiB
Python
"""
|
|
Comprehensive hook tracking models for Claude Code sessions.
|
|
"""
|
|
|
|
from datetime import datetime
|
|
from typing import Optional, Dict, Any
|
|
from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey, Boolean, Float, JSON
|
|
from sqlalchemy.orm import relationship
|
|
|
|
from app.models.base import Base
|
|
|
|
|
|
class HookEvent(Base):
|
|
"""
|
|
Base model for all hook events with common fields.
|
|
"""
|
|
__tablename__ = "hook_events"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
session_id = Column(String, ForeignKey("sessions.id"), nullable=False, index=True)
|
|
hook_type = Column(String(50), nullable=False, index=True)
|
|
timestamp = Column(DateTime, nullable=False, default=datetime.utcnow, index=True)
|
|
data = Column(JSON, nullable=True) # Flexible JSON data for hook-specific information
|
|
|
|
# Relationships
|
|
session = relationship("Session", back_populates="hook_events")
|
|
|
|
def __repr__(self):
|
|
return f"<HookEvent(id={self.id}, type={self.hook_type}, session={self.session_id})>"
|
|
|
|
|
|
class ToolError(Base):
|
|
"""Track failed tool calls with error details."""
|
|
__tablename__ = "tool_errors"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
session_id = Column(String, ForeignKey("sessions.id"), nullable=False, index=True)
|
|
tool_name = Column(String(100), nullable=False, index=True)
|
|
error_type = Column(String(100), nullable=False, index=True)
|
|
error_message = Column(Text, nullable=False)
|
|
stack_trace = Column(Text, nullable=True)
|
|
parameters = Column(JSON, nullable=True)
|
|
timestamp = Column(DateTime, nullable=False, default=datetime.utcnow, index=True)
|
|
|
|
session = relationship("Session")
|
|
|
|
def __repr__(self):
|
|
return f"<ToolError(id={self.id}, tool={self.tool_name}, error={self.error_type})>"
|
|
|
|
|
|
class WaitingPeriodNew(Base):
|
|
"""Track periods when Claude is thinking vs actively working."""
|
|
__tablename__ = "waiting_periods_new"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
session_id = Column(String, ForeignKey("sessions.id"), nullable=False, index=True)
|
|
start_time = Column(DateTime, nullable=False, default=datetime.utcnow, index=True)
|
|
end_time = Column(DateTime, nullable=True)
|
|
duration_ms = Column(Integer, nullable=True)
|
|
reason = Column(String(100), nullable=True, index=True) # thinking, processing, waiting_for_input
|
|
context = Column(Text, nullable=True) # What was happening when waiting started
|
|
|
|
session = relationship("Session")
|
|
|
|
def __repr__(self):
|
|
return f"<WaitingPeriodNew(id={self.id}, reason={self.reason}, duration={self.duration_ms}ms)>"
|
|
|
|
|
|
class PerformanceMetric(Base):
|
|
"""Monitor system performance during development."""
|
|
__tablename__ = "performance_metrics"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
session_id = Column(String, ForeignKey("sessions.id"), nullable=False, index=True)
|
|
metric_type = Column(String(50), nullable=False, index=True) # memory, cpu, disk
|
|
value = Column(Float, nullable=False)
|
|
unit = Column(String(20), nullable=False) # MB, %, seconds
|
|
threshold_exceeded = Column(Boolean, default=False)
|
|
timestamp = Column(DateTime, nullable=False, default=datetime.utcnow, index=True)
|
|
|
|
session = relationship("Session")
|
|
|
|
def __repr__(self):
|
|
return f"<PerformanceMetric(id={self.id}, type={self.metric_type}, value={self.value}{self.unit})>"
|
|
|
|
|
|
class CodeQualityEvent(Base):
|
|
"""Track code quality analysis, linting, and testing."""
|
|
__tablename__ = "code_quality_events"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
session_id = Column(String, ForeignKey("sessions.id"), nullable=False, index=True)
|
|
event_type = Column(String(50), nullable=False, index=True) # lint, format, test, build, analysis
|
|
file_path = Column(Text, nullable=True)
|
|
tool_name = Column(String(100), nullable=True) # eslint, prettier, pytest, etc.
|
|
status = Column(String(20), nullable=False, index=True) # success, warning, error
|
|
issues_count = Column(Integer, default=0)
|
|
details = Column(JSON, nullable=True) # Specific issues, test results, etc.
|
|
duration_ms = Column(Integer, nullable=True)
|
|
timestamp = Column(DateTime, nullable=False, default=datetime.utcnow, index=True)
|
|
|
|
session = relationship("Session")
|
|
|
|
def __repr__(self):
|
|
return f"<CodeQualityEvent(id={self.id}, type={self.event_type}, status={self.status})>"
|
|
|
|
|
|
class WorkflowEvent(Base):
|
|
"""Track development workflow patterns."""
|
|
__tablename__ = "workflow_events"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
session_id = Column(String, ForeignKey("sessions.id"), nullable=False, index=True)
|
|
event_type = Column(String(50), nullable=False, index=True) # context_switch, search_query, browser_tab, etc.
|
|
description = Column(Text, nullable=True)
|
|
event_metadata = Column(JSON, nullable=True) # Event-specific data
|
|
source = Column(String(100), nullable=True) # Where the event came from
|
|
duration_ms = Column(Integer, nullable=True) # For events with duration
|
|
timestamp = Column(DateTime, nullable=False, default=datetime.utcnow, index=True)
|
|
|
|
session = relationship("Session")
|
|
|
|
def __repr__(self):
|
|
return f"<WorkflowEvent(id={self.id}, type={self.event_type})>"
|
|
|
|
|
|
class LearningEvent(Base):
|
|
"""Track learning sessions and knowledge acquisition."""
|
|
__tablename__ = "learning_events"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
session_id = Column(String, ForeignKey("sessions.id"), nullable=False, index=True)
|
|
event_type = Column(String(50), nullable=False, index=True) # tutorial, documentation, experimentation
|
|
topic = Column(String(200), nullable=True, index=True) # What was being learned
|
|
resource_url = Column(Text, nullable=True) # Documentation URL, tutorial link
|
|
confidence_before = Column(Integer, nullable=True) # 1-10 scale
|
|
confidence_after = Column(Integer, nullable=True) # 1-10 scale
|
|
notes = Column(Text, nullable=True)
|
|
duration_ms = Column(Integer, nullable=True)
|
|
timestamp = Column(DateTime, nullable=False, default=datetime.utcnow, index=True)
|
|
|
|
session = relationship("Session")
|
|
|
|
def __repr__(self):
|
|
return f"<LearningEvent(id={self.id}, topic={self.topic})>"
|
|
|
|
|
|
class EnvironmentEvent(Base):
|
|
"""Track environment and configuration changes."""
|
|
__tablename__ = "environment_events"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
session_id = Column(String, ForeignKey("sessions.id"), nullable=False, index=True)
|
|
event_type = Column(String(50), nullable=False, index=True) # env_change, config_update, security_scan
|
|
environment = Column(String(50), nullable=True, index=True) # dev, staging, prod
|
|
config_file = Column(Text, nullable=True)
|
|
changes = Column(JSON, nullable=True) # What changed
|
|
impact_level = Column(String(20), nullable=True) # low, medium, high, critical
|
|
timestamp = Column(DateTime, nullable=False, default=datetime.utcnow, index=True)
|
|
|
|
session = relationship("Session")
|
|
|
|
def __repr__(self):
|
|
return f"<EnvironmentEvent(id={self.id}, type={self.event_type}, env={self.environment})>"
|
|
|
|
|
|
class CollaborationEvent(Base):
|
|
"""Track collaboration and external interactions."""
|
|
__tablename__ = "collaboration_events"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
session_id = Column(String, ForeignKey("sessions.id"), nullable=False, index=True)
|
|
event_type = Column(String(50), nullable=False, index=True) # external_resource, ai_question, review_request
|
|
interaction_type = Column(String(50), nullable=True) # documentation, stackoverflow, api_docs
|
|
query_or_topic = Column(Text, nullable=True)
|
|
resource_url = Column(Text, nullable=True)
|
|
response_quality = Column(Integer, nullable=True) # 1-5 rating
|
|
time_to_resolution = Column(Integer, nullable=True) # minutes
|
|
event_metadata = Column(JSON, nullable=True)
|
|
timestamp = Column(DateTime, nullable=False, default=datetime.utcnow, index=True)
|
|
|
|
session = relationship("Session")
|
|
|
|
def __repr__(self):
|
|
return f"<CollaborationEvent(id={self.id}, type={self.event_type})>"
|
|
|
|
|
|
class ProjectIntelligence(Base):
|
|
"""Track high-level project development patterns."""
|
|
__tablename__ = "project_intelligence"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
session_id = Column(String, ForeignKey("sessions.id"), nullable=False, index=True)
|
|
event_type = Column(String(50), nullable=False, index=True) # refactor, feature_flag, debugging_session
|
|
scope = Column(String(20), nullable=True, index=True) # small, medium, large
|
|
complexity = Column(String(20), nullable=True, index=True) # low, medium, high
|
|
start_time = Column(DateTime, nullable=False, default=datetime.utcnow)
|
|
end_time = Column(DateTime, nullable=True)
|
|
duration_minutes = Column(Integer, nullable=True)
|
|
files_affected = Column(JSON, nullable=True) # List of file paths
|
|
outcome = Column(String(20), nullable=True) # success, partial, failed, abandoned
|
|
notes = Column(Text, nullable=True)
|
|
timestamp = Column(DateTime, nullable=False, default=datetime.utcnow, index=True)
|
|
|
|
session = relationship("Session")
|
|
|
|
def __repr__(self):
|
|
return f"<ProjectIntelligence(id={self.id}, type={self.event_type}, scope={self.scope})>"
|
|
|
|
|
|
# Add hook_events relationship to Session model
|
|
def add_hook_relationships():
|
|
"""Add hook event relationships to the Session model."""
|
|
from app.models.session import Session
|
|
|
|
Session.hook_events = relationship("HookEvent", back_populates="session", cascade="all, delete-orphan")
|
|
Session.tool_errors = relationship("ToolError", cascade="all, delete-orphan")
|
|
Session.waiting_periods_new = relationship("WaitingPeriodNew", cascade="all, delete-orphan")
|
|
Session.performance_metrics = relationship("PerformanceMetric", cascade="all, delete-orphan")
|
|
Session.code_quality_events = relationship("CodeQualityEvent", cascade="all, delete-orphan")
|
|
Session.workflow_events = relationship("WorkflowEvent", cascade="all, delete-orphan")
|
|
Session.learning_events = relationship("LearningEvent", cascade="all, delete-orphan")
|
|
Session.environment_events = relationship("EnvironmentEvent", cascade="all, delete-orphan")
|
|
Session.collaboration_events = relationship("CollaborationEvent", cascade="all, delete-orphan")
|
|
Session.project_intelligence = relationship("ProjectIntelligence", cascade="all, delete-orphan") |