Initial commit: Claude Code Project Tracker

Add comprehensive development intelligence system that tracks:
- Development sessions with automatic start/stop
- Full conversation history with semantic search
- Tool usage and file operation analytics
- Think time and engagement analysis
- Git activity correlation
- Learning pattern recognition
- Productivity insights and metrics

Features:
- FastAPI backend with SQLite database
- Modern web dashboard with interactive charts
- Claude Code hook integration for automatic tracking
- Comprehensive test suite with 100+ tests
- Complete API documentation (OpenAPI/Swagger)
- Privacy-first design with local data storage

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Ryan Malloy 2025-08-11 02:59:21 -06:00
commit 44ed9936b7
48 changed files with 9707 additions and 0 deletions

530
docs/api-spec.yaml Normal file
View file

@ -0,0 +1,530 @@
openapi: 3.0.3
info:
title: Claude Code Project Tracker API
description: |
REST API for tracking Claude Code development sessions, conversations, and productivity metrics.
This API is designed to be called by Claude Code hooks to automatically capture development workflow data.
version: 1.0.0
license:
name: MIT
servers:
- url: http://localhost:8000
description: Local development server
paths:
# Session Management
/api/session/start:
post:
summary: Start a new development session
description: Called by SessionStart hook to initialize project tracking
tags:
- Sessions
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SessionStart'
responses:
'201':
description: Session created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/SessionResponse'
'400':
description: Invalid request data
/api/session/end:
post:
summary: End current development session
description: Called by Stop hook to finalize session tracking
tags:
- Sessions
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SessionEnd'
responses:
'200':
description: Session ended successfully
content:
application/json:
schema:
$ref: '#/components/schemas/SessionResponse'
# Conversation Tracking
/api/conversation:
post:
summary: Log conversation exchange
description: Called by UserPromptSubmit and Stop hooks to capture dialogue
tags:
- Conversations
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ConversationEntry'
responses:
'201':
description: Conversation logged successfully
# Activity Tracking
/api/activity:
post:
summary: Record development activity
description: Called by PostToolUse hooks to track tool usage and file operations
tags:
- Activities
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Activity'
responses:
'201':
description: Activity recorded successfully
# Waiting Period Tracking
/api/waiting/start:
post:
summary: Start waiting period
description: Called by Notification hook when Claude is waiting for input
tags:
- Waiting
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/WaitingStart'
responses:
'201':
description: Waiting period started
/api/waiting/end:
post:
summary: End waiting period
description: Called when user submits new input
tags:
- Waiting
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/WaitingEnd'
responses:
'200':
description: Waiting period ended
# Git Operations
/api/git:
post:
summary: Record git operation
description: Track git commands and repository state changes
tags:
- Git
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/GitOperation'
responses:
'201':
description: Git operation recorded
# Data Retrieval
/api/projects:
get:
summary: List all tracked projects
description: Get overview of all projects with summary statistics
tags:
- Projects
parameters:
- name: limit
in: query
schema:
type: integer
default: 50
- name: offset
in: query
schema:
type: integer
default: 0
responses:
'200':
description: List of projects
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/ProjectSummary'
/api/projects/{project_id}/timeline:
get:
summary: Get detailed project timeline
description: Retrieve chronological history of project development
tags:
- Projects
parameters:
- name: project_id
in: path
required: true
schema:
type: integer
- name: start_date
in: query
schema:
type: string
format: date
- name: end_date
in: query
schema:
type: string
format: date
responses:
'200':
description: Project timeline
content:
application/json:
schema:
$ref: '#/components/schemas/ProjectTimeline'
# Analytics
/api/analytics/productivity:
get:
summary: Get productivity analytics
description: Retrieve engagement metrics and output analysis
tags:
- Analytics
parameters:
- name: project_id
in: query
schema:
type: integer
- name: days
in: query
schema:
type: integer
default: 30
responses:
'200':
description: Productivity metrics
content:
application/json:
schema:
$ref: '#/components/schemas/ProductivityMetrics'
/api/conversations/search:
get:
summary: Search conversations
description: Semantic search through conversation history
tags:
- Conversations
parameters:
- name: query
in: query
required: true
schema:
type: string
- name: project_id
in: query
schema:
type: integer
- name: limit
in: query
schema:
type: integer
default: 20
responses:
'200':
description: Search results
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/ConversationSearchResult'
components:
schemas:
# Session Schemas
SessionStart:
type: object
required:
- session_type
- working_directory
properties:
session_type:
type: string
enum: [startup, resume, clear]
working_directory:
type: string
git_branch:
type: string
git_repo:
type: string
environment:
type: object
SessionEnd:
type: object
required:
- session_id
properties:
session_id:
type: integer
end_reason:
type: string
enum: [normal, interrupted, timeout]
SessionResponse:
type: object
properties:
session_id:
type: integer
project_id:
type: integer
status:
type: string
# Conversation Schemas
ConversationEntry:
type: object
required:
- session_id
- timestamp
properties:
session_id:
type: integer
timestamp:
type: string
format: date-time
user_prompt:
type: string
claude_response:
type: string
tools_used:
type: array
items:
type: string
files_affected:
type: array
items:
type: string
context:
type: object
# Activity Schemas
Activity:
type: object
required:
- session_id
- tool_name
- timestamp
properties:
session_id:
type: integer
tool_name:
type: string
enum: [Edit, Write, Read, Bash, Grep, Glob, Task, WebFetch]
action:
type: string
file_path:
type: string
timestamp:
type: string
format: date-time
metadata:
type: object
success:
type: boolean
error_message:
type: string
# Waiting Period Schemas
WaitingStart:
type: object
required:
- session_id
- timestamp
properties:
session_id:
type: integer
timestamp:
type: string
format: date-time
context_before:
type: string
WaitingEnd:
type: object
required:
- session_id
- timestamp
properties:
session_id:
type: integer
timestamp:
type: string
format: date-time
duration_seconds:
type: number
context_after:
type: string
# Git Schemas
GitOperation:
type: object
required:
- session_id
- operation
- timestamp
properties:
session_id:
type: integer
operation:
type: string
enum: [commit, push, pull, branch, merge, rebase, status]
command:
type: string
result:
type: string
timestamp:
type: string
format: date-time
files_changed:
type: array
items:
type: string
lines_added:
type: integer
lines_removed:
type: integer
# Response Schemas
ProjectSummary:
type: object
properties:
id:
type: integer
name:
type: string
path:
type: string
git_repo:
type: string
languages:
type: array
items:
type: string
total_sessions:
type: integer
total_time_minutes:
type: integer
last_activity:
type: string
format: date-time
files_modified:
type: integer
lines_changed:
type: integer
ProjectTimeline:
type: object
properties:
project:
$ref: '#/components/schemas/ProjectSummary'
timeline:
type: array
items:
type: object
properties:
timestamp:
type: string
format: date-time
type:
type: string
enum: [session_start, session_end, conversation, activity, git_operation]
data:
type: object
ProductivityMetrics:
type: object
properties:
engagement_score:
type: number
description: Overall engagement level (0-100)
average_session_length:
type: number
description: Minutes per session
think_time_average:
type: number
description: Average waiting time between interactions
files_per_session:
type: number
tools_most_used:
type: array
items:
type: object
properties:
tool:
type: string
count:
type: integer
productivity_trends:
type: array
items:
type: object
properties:
date:
type: string
format: date
score:
type: number
ConversationSearchResult:
type: object
properties:
id:
type: integer
project_name:
type: string
timestamp:
type: string
format: date-time
user_prompt:
type: string
claude_response:
type: string
relevance_score:
type: number
context:
type: array
items:
type: string
tags:
- name: Sessions
description: Development session management
- name: Conversations
description: Dialogue tracking and search
- name: Activities
description: Tool usage and file operations
- name: Waiting
description: Think time and engagement tracking
- name: Git
description: Repository operations
- name: Projects
description: Project data retrieval
- name: Analytics
description: Insights and metrics

252
docs/database-schema.md Normal file
View file

@ -0,0 +1,252 @@
# Database Schema Documentation
This document describes the SQLite database schema for the Claude Code Project Tracker.
## Overview
The database is designed to capture comprehensive development workflow data through a normalized relational structure. All timestamps are stored in UTC format.
## Entity Relationship Diagram
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ projects │ │ sessions │ │conversations│
│ │ │ │ │ │
│ id (PK) │◀──┤ project_id │ │ session_id │
│ name │ │ id (PK) │◀──┤ id (PK) │
│ path │ │ start_time │ │ timestamp │
│ git_repo │ │ end_time │ │ user_prompt │
│ created_at │ │ type │ │ claude_resp │
│ ... │ │ ... │ │ ... │
└─────────────┘ └─────────────┘ └─────────────┘
┌─────────────┐ ┌─────────────┐
│ activities │ │waiting_perds│
│ │ │ │
│ session_id │ │ session_id │
│ id (PK) │ │ id (PK) │
│ tool_name │ │ start_time │
│ file_path │ │ end_time │
│ timestamp │ │ duration │
│ ... │ │ ... │
└─────────────┘ └─────────────┘
┌─────────────┐
│git_operations│
│ │
│ session_id │
│ id (PK) │
│ operation │
│ command │
│ timestamp │
│ ... │
└─────────────┘
```
## Table Definitions
### projects
Stores metadata about tracked projects.
| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | INTEGER | PRIMARY KEY | Unique project identifier |
| name | VARCHAR(255) | NOT NULL | Project display name |
| path | TEXT | NOT NULL UNIQUE | Absolute filesystem path |
| git_repo | VARCHAR(500) | NULL | Git repository URL if applicable |
| languages | JSON | NULL | Array of detected programming languages |
| created_at | TIMESTAMP | NOT NULL DEFAULT NOW() | First time project was tracked |
| last_session | TIMESTAMP | NULL | Most recent session timestamp |
| total_sessions | INTEGER | NOT NULL DEFAULT 0 | Count of development sessions |
| total_time_minutes | INTEGER | NOT NULL DEFAULT 0 | Cumulative session duration |
| files_modified_count | INTEGER | NOT NULL DEFAULT 0 | Total unique files changed |
| lines_changed_count | INTEGER | NOT NULL DEFAULT 0 | Total lines added + removed |
**Indexes:**
- `idx_projects_path` ON (path)
- `idx_projects_last_session` ON (last_session)
### sessions
Individual development sessions within projects.
| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | INTEGER | PRIMARY KEY | Unique session identifier |
| project_id | INTEGER | NOT NULL FK(projects.id) | Associated project |
| start_time | TIMESTAMP | NOT NULL | Session start timestamp |
| end_time | TIMESTAMP | NULL | Session end timestamp (NULL if active) |
| session_type | VARCHAR(50) | NOT NULL | startup, resume, clear |
| working_directory | TEXT | NOT NULL | Directory path when session started |
| git_branch | VARCHAR(255) | NULL | Active git branch |
| environment | JSON | NULL | System environment details |
| duration_minutes | INTEGER | NULL | Calculated session length |
| activity_count | INTEGER | NOT NULL DEFAULT 0 | Number of tool uses |
| conversation_count | INTEGER | NOT NULL DEFAULT 0 | Number of exchanges |
| files_touched | JSON | NULL | Array of file paths accessed |
**Indexes:**
- `idx_sessions_project_start` ON (project_id, start_time)
- `idx_sessions_active` ON (end_time) WHERE end_time IS NULL
### conversations
Dialogue exchanges between user and Claude.
| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | INTEGER | PRIMARY KEY | Unique conversation identifier |
| session_id | INTEGER | NOT NULL FK(sessions.id) | Associated session |
| timestamp | TIMESTAMP | NOT NULL | When exchange occurred |
| user_prompt | TEXT | NULL | User's input message |
| claude_response | TEXT | NULL | Claude's response |
| tools_used | JSON | NULL | Array of tools used in response |
| files_affected | JSON | NULL | Array of files mentioned/modified |
| context | JSON | NULL | Additional context metadata |
| tokens_input | INTEGER | NULL | Estimated input token count |
| tokens_output | INTEGER | NULL | Estimated output token count |
| exchange_type | VARCHAR(50) | NOT NULL | user_prompt, claude_response |
**Indexes:**
- `idx_conversations_session_time` ON (session_id, timestamp)
- `idx_conversations_search` ON (user_prompt, claude_response) USING fts5
### activities
Tool usage and file operations during development.
| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | INTEGER | PRIMARY KEY | Unique activity identifier |
| session_id | INTEGER | NOT NULL FK(sessions.id) | Associated session |
| conversation_id | INTEGER | NULL FK(conversations.id) | Associated exchange |
| timestamp | TIMESTAMP | NOT NULL | When activity occurred |
| tool_name | VARCHAR(50) | NOT NULL | Edit, Write, Read, Bash, etc. |
| action | VARCHAR(100) | NOT NULL | Specific action taken |
| file_path | TEXT | NULL | Target file path if applicable |
| metadata | JSON | NULL | Tool-specific data |
| success | BOOLEAN | NOT NULL DEFAULT true | Whether operation succeeded |
| error_message | TEXT | NULL | Error details if failed |
| lines_added | INTEGER | NULL | Lines added (for Edit/Write) |
| lines_removed | INTEGER | NULL | Lines removed (for Edit) |
**Indexes:**
- `idx_activities_session_time` ON (session_id, timestamp)
- `idx_activities_tool_file` ON (tool_name, file_path)
### waiting_periods
Time intervals when Claude is waiting for user input.
| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | INTEGER | PRIMARY KEY | Unique waiting period identifier |
| session_id | INTEGER | NOT NULL FK(sessions.id) | Associated session |
| start_time | TIMESTAMP | NOT NULL | When waiting began |
| end_time | TIMESTAMP | NULL | When user responded |
| duration_seconds | INTEGER | NULL | Calculated wait duration |
| context_before | TEXT | NULL | Claude's last message |
| context_after | TEXT | NULL | User's next message |
| likely_activity | VARCHAR(50) | NULL | thinking, research, external_work, break |
**Indexes:**
- `idx_waiting_session_start` ON (session_id, start_time)
- `idx_waiting_duration` ON (duration_seconds)
### git_operations
Git commands and repository state changes.
| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | INTEGER | PRIMARY KEY | Unique git operation identifier |
| session_id | INTEGER | NOT NULL FK(sessions.id) | Associated session |
| timestamp | TIMESTAMP | NOT NULL | When operation occurred |
| operation | VARCHAR(50) | NOT NULL | commit, push, pull, branch, etc. |
| command | TEXT | NOT NULL | Full git command executed |
| result | TEXT | NULL | Command output |
| success | BOOLEAN | NOT NULL | Whether command succeeded |
| files_changed | JSON | NULL | Array of affected files |
| lines_added | INTEGER | NULL | Lines added in commit |
| lines_removed | INTEGER | NULL | Lines removed in commit |
| commit_hash | VARCHAR(40) | NULL | Git commit SHA |
| branch_from | VARCHAR(255) | NULL | Source branch |
| branch_to | VARCHAR(255) | NULL | Target branch |
**Indexes:**
- `idx_git_session_time` ON (session_id, timestamp)
- `idx_git_operation` ON (operation)
- `idx_git_commit` ON (commit_hash)
## Analytics Views
### project_productivity_summary
Aggregated productivity metrics per project.
```sql
CREATE VIEW project_productivity_summary AS
SELECT
p.id,
p.name,
p.path,
COUNT(DISTINCT s.id) as total_sessions,
SUM(s.duration_minutes) as total_time_minutes,
AVG(s.duration_minutes) as avg_session_minutes,
COUNT(DISTINCT a.file_path) as unique_files_modified,
SUM(a.lines_added + a.lines_removed) as total_lines_changed,
AVG(wp.duration_seconds) as avg_think_time_seconds,
MAX(s.start_time) as last_activity
FROM projects p
LEFT JOIN sessions s ON p.id = s.project_id
LEFT JOIN activities a ON s.id = a.session_id
LEFT JOIN waiting_periods wp ON s.id = wp.session_id
GROUP BY p.id, p.name, p.path;
```
### daily_productivity_metrics
Daily productivity trends across all projects.
```sql
CREATE VIEW daily_productivity_metrics AS
SELECT
DATE(s.start_time) as date,
COUNT(DISTINCT s.id) as sessions_count,
SUM(s.duration_minutes) as total_time_minutes,
COUNT(DISTINCT a.file_path) as files_modified,
SUM(a.lines_added + a.lines_removed) as lines_changed,
AVG(wp.duration_seconds) as avg_think_time,
COUNT(DISTINCT s.project_id) as projects_worked_on
FROM sessions s
LEFT JOIN activities a ON s.id = a.session_id AND a.tool_name IN ('Edit', 'Write')
LEFT JOIN waiting_periods wp ON s.id = wp.session_id
WHERE s.end_time IS NOT NULL
GROUP BY DATE(s.start_time)
ORDER BY date DESC;
```
## Data Retention
- **Conversation Full Text**: Stored indefinitely for search and analysis
- **Activity Details**: Kept for all operations to maintain complete audit trail
- **Analytics Aggregations**: Computed on-demand from source data
- **Cleanup**: Manual cleanup tools provided, no automatic data expiration
## Performance Considerations
- Database file size grows approximately 1-5MB per day of active development
- Full-text search indexes require periodic optimization (`PRAGMA optimize`)
- Analytics queries use covering indexes to avoid table scans
- Large file content is not stored, only file paths and change metrics
## Migration Strategy
Schema migrations are handled through versioned SQL scripts in `/migrations/`:
- Each migration has up/down scripts
- Version tracking in `schema_versions` table
- Automatic backup before migrations
- Rollback capability for failed migrations

562
docs/development.md Normal file
View file

@ -0,0 +1,562 @@
# Development Setup Guide
This guide covers setting up a local development environment for the Claude Code Project Tracker.
## Prerequisites
- **Python 3.8+** with pip
- **Git** for version control
- **Node.js 16+** (for web dashboard development)
- **SQLite3** (usually included with Python)
## Quick Setup
```bash
# Clone the repository
git clone <repository-url>
cd claude-tracker
# Create virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
pip install -r requirements-dev.txt
# Initialize database
python -m app.database.init_db
# Run tests
pytest
# Start development server
uvicorn main:app --reload --host 0.0.0.0 --port 8000
```
## Project Structure
```
claude-tracker/
├── main.py # FastAPI application entry point
├── requirements.txt # Production dependencies
├── requirements-dev.txt # Development dependencies
├── pytest.ini # Pytest configuration
├── .env.example # Environment variable template
├── app/ # Main application code
│ ├── __init__.py
│ ├── models/ # Database models
│ │ ├── __init__.py
│ │ ├── base.py # Base model class
│ │ ├── project.py # Project model
│ │ ├── session.py # Session model
│ │ └── ... # Other models
│ ├── api/ # API route handlers
│ │ ├── __init__.py
│ │ ├── dependencies.py # FastAPI dependencies
│ │ ├── sessions.py # Session endpoints
│ │ ├── conversations.py # Conversation endpoints
│ │ └── ... # Other endpoints
│ ├── database/ # Database management
│ │ ├── __init__.py
│ │ ├── connection.py # Database connection
│ │ ├── init_db.py # Database initialization
│ │ └── migrations/ # Schema migrations
│ ├── analytics/ # Analytics and insights engine
│ │ ├── __init__.py
│ │ ├── productivity.py # Productivity metrics
│ │ ├── patterns.py # Pattern analysis
│ │ └── reports.py # Report generation
│ └── dashboard/ # Web dashboard
│ ├── static/ # CSS, JS, images
│ ├── templates/ # HTML templates
│ └── routes.py # Dashboard routes
├── tests/ # Test suite
│ ├── __init__.py
│ ├── conftest.py # Pytest fixtures
│ ├── test_models.py # Model tests
│ ├── test_api.py # API tests
│ ├── test_analytics.py # Analytics tests
│ └── integration/ # Integration tests
├── config/ # Configuration files
├── docs/ # Documentation
├── data/ # Database files (created at runtime)
└── migrations/ # Database migrations
```
## Dependencies
### Core Dependencies (`requirements.txt`)
```
fastapi==0.104.1 # Web framework
uvicorn[standard]==0.24.0 # ASGI server
sqlalchemy==2.0.23 # ORM
sqlite3 # Database (built into Python)
pydantic==2.5.0 # Data validation
jinja2==3.1.2 # Template engine
python-multipart==0.0.6 # Form parsing
python-jose[cryptography]==3.3.0 # JWT tokens
passlib[bcrypt]==1.7.4 # Password hashing
```
### Development Dependencies (`requirements-dev.txt`)
```
pytest==7.4.3 # Testing framework
pytest-asyncio==0.21.1 # Async testing
pytest-cov==4.1.0 # Coverage reporting
httpx==0.25.2 # HTTP client for testing
faker==20.1.0 # Test data generation
black==23.11.0 # Code formatting
isort==5.12.0 # Import sorting
flake8==6.1.0 # Linting
mypy==1.7.1 # Type checking
pre-commit==3.6.0 # Git hooks
```
## Environment Configuration
Copy the example environment file:
```bash
cp .env.example .env
```
Configure these variables in `.env`:
```bash
# Database
DATABASE_URL=sqlite:///./data/tracker.db
# API Configuration
API_HOST=0.0.0.0
API_PORT=8000
DEBUG=true
# Security (generate with: openssl rand -hex 32)
SECRET_KEY=your-secret-key-here
ACCESS_TOKEN_EXPIRE_MINUTES=30
# Analytics
ENABLE_ANALYTICS=true
ANALYTICS_BATCH_SIZE=1000
# Logging
LOG_LEVEL=INFO
LOG_FILE=tracker.log
```
## Database Setup
### Initialize Database
```bash
# Create database and tables
python -m app.database.init_db
# Verify database creation
sqlite3 data/tracker.db ".tables"
```
### Database Migrations
```bash
# Create a new migration
python -m app.database.migrate create "add_new_field"
# Apply migrations
python -m app.database.migrate up
# Rollback migration
python -m app.database.migrate down
```
### Sample Data
Load test data for development:
```bash
# Load sample projects and sessions
python -m app.database.seed_data
# Clear all data
python -m app.database.clear_data
```
## Testing
### Run Tests
```bash
# Run all tests
pytest
# Run with coverage
pytest --cov=app --cov-report=html
# Run specific test file
pytest tests/test_api.py
# Run tests matching pattern
pytest -k "test_session"
# Run tests with verbose output
pytest -v
```
### Test Database
Tests use a separate in-memory database:
```python
# tests/conftest.py
@pytest.fixture
async def test_db():
# Create in-memory SQLite database for testing
engine = create_async_engine("sqlite+aiosqlite:///:memory:")
# ... setup code
```
### Writing Tests
```python
# tests/test_api.py
import pytest
from httpx import AsyncClient
from app.main import app
@pytest.mark.asyncio
async def test_create_session(test_db, test_client):
response = await test_client.post(
"/api/session/start",
json={
"session_type": "startup",
"working_directory": "/test/path"
}
)
assert response.status_code == 201
data = response.json()
assert data["session_id"] is not None
```
## Development Workflow
### Code Style
We use Black for formatting and isort for import sorting:
```bash
# Format code
black app/ tests/
# Sort imports
isort app/ tests/
# Check formatting without changes
black --check app/ tests/
```
### Linting
```bash
# Run flake8 linting
flake8 app/ tests/
# Type checking with mypy
mypy app/
```
### Pre-commit Hooks
Install pre-commit hooks to run checks automatically:
```bash
# Install hooks
pre-commit install
# Run hooks manually
pre-commit run --all-files
```
### Git Workflow
1. **Create feature branch:**
```bash
git checkout -b feature/new-analytics-endpoint
```
2. **Make changes and test:**
```bash
# Make your changes
pytest # Run tests
black app/ tests/ # Format code
```
3. **Commit changes:**
```bash
git add .
git commit -m "Add new analytics endpoint for productivity metrics"
```
4. **Push and create PR:**
```bash
git push origin feature/new-analytics-endpoint
```
## API Development
### Adding New Endpoints
1. **Define Pydantic schemas** in `app/models/schemas.py`:
```python
class NewFeatureRequest(BaseModel):
name: str
description: Optional[str] = None
```
2. **Create route handler** in appropriate module:
```python
@router.post("/api/new-feature", response_model=NewFeatureResponse)
async def create_new_feature(
request: NewFeatureRequest,
db: AsyncSession = Depends(get_db)
):
# Implementation
```
3. **Add tests** in `tests/test_api.py`:
```python
async def test_create_new_feature(test_client):
# Test implementation
```
### Database Queries
Use SQLAlchemy with async/await:
```python
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from app.models.project import Project
async def get_projects(db: AsyncSession, limit: int = 10):
result = await db.execute(
select(Project).limit(limit)
)
return result.scalars().all()
```
## Frontend Development
### Web Dashboard
The dashboard uses vanilla HTML/CSS/JavaScript:
```
app/dashboard/
├── static/
│ ├── css/
│ │ └── dashboard.css
│ ├── js/
│ │ ├── dashboard.js
│ │ ├── charts.js
│ │ └── api-client.js
│ └── images/
└── templates/
├── base.html
├── dashboard.html
└── projects.html
```
### Adding New Dashboard Pages
1. **Create HTML template:**
```html
<!-- app/dashboard/templates/new-page.html -->
{% extends "base.html" %}
{% block content %}
<!-- Page content -->
{% endblock %}
```
2. **Add route handler:**
```python
@dashboard_router.get("/new-page")
async def new_page(request: Request):
return templates.TemplateResponse("new-page.html", {"request": request})
```
3. **Add JavaScript if needed:**
```javascript
// app/dashboard/static/js/new-page.js
class NewPageController {
// Page logic
}
```
## Debugging
### Logging
Configure logging in `main.py`:
```python
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[
logging.FileHandler("tracker.log"),
logging.StreamHandler()
]
)
```
### Database Debugging
Enable SQL query logging:
```python
# In development.py
engine = create_async_engine(
DATABASE_URL,
echo=True # This logs all SQL queries
)
```
### API Debugging
Use FastAPI's automatic documentation:
- **Swagger UI**: http://localhost:8000/docs
- **ReDoc**: http://localhost:8000/redoc
### Hook Debugging
Test hooks manually:
```bash
# Test session start
curl -X POST http://localhost:8000/api/session/start \
-H "Content-Type: application/json" \
-d '{"session_type":"startup","working_directory":"'$(pwd)'"}'
# Check logs
tail -f tracker.log
```
## Performance
### Database Optimization
```bash
# Analyze query performance
sqlite3 data/tracker.db "EXPLAIN QUERY PLAN SELECT ..."
# Rebuild indexes
sqlite3 data/tracker.db "REINDEX;"
# Vacuum database
sqlite3 data/tracker.db "VACUUM;"
```
### API Performance
```bash
# Profile API endpoints
pip install line_profiler
kernprof -l -v app/api/sessions.py
```
## Deployment
### Production Setup
1. **Environment variables:**
```bash
DEBUG=false
API_HOST=127.0.0.1
```
2. **Database backup:**
```bash
cp data/tracker.db data/tracker.db.backup
```
3. **Run with Gunicorn:**
```bash
pip install gunicorn
gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker
```
### Docker Setup
```dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
```
## Contributing
1. **Fork the repository**
2. **Create a feature branch**
3. **Add tests for new features**
4. **Ensure all tests pass**
5. **Follow code style guidelines**
6. **Submit a pull request**
### Pull Request Checklist
- [ ] Tests added/updated
- [ ] Documentation updated
- [ ] Code formatted with Black
- [ ] Type hints added
- [ ] No linting errors
- [ ] Database migrations created if needed
## Troubleshooting
### Common Issues
1. **Import errors:**
```bash
# Ensure you're in the virtual environment
source venv/bin/activate
# Check Python path
python -c "import sys; print(sys.path)"
```
2. **Database locked:**
```bash
# Check for hanging processes
ps aux | grep python
# Remove lock files
rm data/tracker.db-wal data/tracker.db-shm
```
3. **Port already in use:**
```bash
# Find process using port 8000
lsof -i :8000
# Use different port
uvicorn main:app --port 8001
```
### Getting Help
- Check the [API documentation](api-spec.yaml)
- Review test files for usage examples
- Open an issue for bugs or feature requests
- Join development discussions in issues

279
docs/hook-setup.md Normal file
View file

@ -0,0 +1,279 @@
# Claude Code Hook Setup Guide
This guide explains how to configure Claude Code hooks to automatically track your development sessions with the Project Tracker API.
## Overview
Claude Code hooks are shell commands that execute in response to specific events. We'll configure hooks to send HTTP requests to our tracking API whenever key development events occur.
## Prerequisites
1. **Claude Code Project Tracker** server running on `http://localhost:8000`
2. **curl** or **httpie** available in your shell
3. **jq** for JSON processing (recommended)
## Hook Configuration Location
Claude Code hooks are configured in your settings file:
- **Linux/macOS**: `~/.config/claude-code/settings.json`
- **Windows**: `%APPDATA%\claude-code\settings.json`
## Complete Hook Configuration
Add this hooks section to your Claude Code settings:
```json
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"command": "curl -s -X POST http://localhost:8000/api/session/start -H 'Content-Type: application/json' -d '{\"session_type\":\"startup\",\"working_directory\":\"'\"$PWD\"'\",\"git_branch\":\"'$(git branch --show-current 2>/dev/null || echo \"unknown\")'\",\"git_repo\":\"'$(git config --get remote.origin.url 2>/dev/null || echo \"null\")'\",\"environment\":{\"pwd\":\"'\"$PWD\"'\",\"user\":\"'\"$USER\"'\",\"timestamp\":\"'$(date -Iseconds)'\"}}' > /dev/null 2>&1 &"
},
{
"matcher": "resume",
"command": "curl -s -X POST http://localhost:8000/api/session/start -H 'Content-Type: application/json' -d '{\"session_type\":\"resume\",\"working_directory\":\"'\"$PWD\"'\",\"git_branch\":\"'$(git branch --show-current 2>/dev/null || echo \"unknown\")'\",\"git_repo\":\"'$(git config --get remote.origin.url 2>/dev/null || echo \"null\")'\",\"environment\":{\"pwd\":\"'\"$PWD\"'\",\"user\":\"'\"$USER\"'\",\"timestamp\":\"'$(date -Iseconds)'\"}}' > /dev/null 2>&1 &"
},
{
"matcher": "clear",
"command": "curl -s -X POST http://localhost:8000/api/session/start -H 'Content-Type: application/json' -d '{\"session_type\":\"clear\",\"working_directory\":\"'\"$PWD\"'\",\"git_branch\":\"'$(git branch --show-current 2>/dev/null || echo \"unknown\")'\",\"git_repo\":\"'$(git config --get remote.origin.url 2>/dev/null || echo \"null\")'\",\"environment\":{\"pwd\":\"'\"$PWD\"'\",\"user\":\"'\"$USER\"'\",\"timestamp\":\"'$(date -Iseconds)'\"}}' > /dev/null 2>&1 &"
}
],
"UserPromptSubmit": [
{
"command": "echo '{\"session_id\":1,\"timestamp\":\"'$(date -Iseconds)'\",\"user_prompt\":\"'\"$CLAUDE_USER_PROMPT\"'\",\"exchange_type\":\"user_prompt\"}' | curl -s -X POST http://localhost:8000/api/conversation -H 'Content-Type: application/json' -d @- > /dev/null 2>&1 &"
}
],
"Notification": [
{
"command": "curl -s -X POST http://localhost:8000/api/waiting/start -H 'Content-Type: application/json' -d '{\"session_id\":1,\"timestamp\":\"'$(date -Iseconds)'\",\"context_before\":\"Claude is waiting for input\"}' > /dev/null 2>&1 &"
}
],
"PostToolUse": [
{
"matcher": "Edit",
"command": "echo '{\"session_id\":1,\"tool_name\":\"Edit\",\"action\":\"file_edit\",\"file_path\":\"'\"$CLAUDE_TOOL_FILE_PATH\"'\",\"timestamp\":\"'$(date -Iseconds)'\",\"metadata\":{\"success\":true},\"success\":true}' | curl -s -X POST http://localhost:8000/api/activity -H 'Content-Type: application/json' -d @- > /dev/null 2>&1 &"
},
{
"matcher": "Write",
"command": "echo '{\"session_id\":1,\"tool_name\":\"Write\",\"action\":\"file_write\",\"file_path\":\"'\"$CLAUDE_TOOL_FILE_PATH\"'\",\"timestamp\":\"'$(date -Iseconds)'\",\"metadata\":{\"success\":true},\"success\":true}' | curl -s -X POST http://localhost:8000/api/activity -H 'Content-Type: application/json' -d @- > /dev/null 2>&1 &"
},
{
"matcher": "Read",
"command": "echo '{\"session_id\":1,\"tool_name\":\"Read\",\"action\":\"file_read\",\"file_path\":\"'\"$CLAUDE_TOOL_FILE_PATH\"'\",\"timestamp\":\"'$(date -Iseconds)'\",\"metadata\":{\"success\":true},\"success\":true}' | curl -s -X POST http://localhost:8000/api/activity -H 'Content-Type: application/json' -d @- > /dev/null 2>&1 &"
},
{
"matcher": "Bash",
"command": "echo '{\"session_id\":1,\"tool_name\":\"Bash\",\"action\":\"command_execution\",\"timestamp\":\"'$(date -Iseconds)'\",\"metadata\":{\"command\":\"'\"$CLAUDE_BASH_COMMAND\"'\",\"success\":true},\"success\":true}' | curl -s -X POST http://localhost:8000/api/activity -H 'Content-Type: application/json' -d @- > /dev/null 2>&1 &"
},
{
"matcher": "Grep",
"command": "echo '{\"session_id\":1,\"tool_name\":\"Grep\",\"action\":\"search\",\"timestamp\":\"'$(date -Iseconds)'\",\"metadata\":{\"pattern\":\"'\"$CLAUDE_GREP_PATTERN\"'\",\"success\":true},\"success\":true}' | curl -s -X POST http://localhost:8000/api/activity -H 'Content-Type: application/json' -d @- > /dev/null 2>&1 &"
},
{
"matcher": "Glob",
"command": "echo '{\"session_id\":1,\"tool_name\":\"Glob\",\"action\":\"file_search\",\"timestamp\":\"'$(date -Iseconds)'\",\"metadata\":{\"pattern\":\"'\"$CLAUDE_GLOB_PATTERN\"'\",\"success\":true},\"success\":true}' | curl -s -X POST http://localhost:8000/api/activity -H 'Content-Type: application/json' -d @- > /dev/null 2>&1 &"
},
{
"matcher": "Task",
"command": "echo '{\"session_id\":1,\"tool_name\":\"Task\",\"action\":\"subagent_call\",\"timestamp\":\"'$(date -Iseconds)'\",\"metadata\":{\"task_type\":\"'\"$CLAUDE_TASK_TYPE\"'\",\"success\":true},\"success\":true}' | curl -s -X POST http://localhost:8000/api/activity -H 'Content-Type: application/json' -d @- > /dev/null 2>&1 &"
}
],
"Stop": [
{
"command": "echo '{\"session_id\":1,\"timestamp\":\"'$(date -Iseconds)'\",\"claude_response\":\"Response completed\",\"exchange_type\":\"claude_response\"}' | curl -s -X POST http://localhost:8000/api/conversation -H 'Content-Type: application/json' -d @- > /dev/null 2>&1 && curl -s -X POST http://localhost:8000/api/waiting/end -H 'Content-Type: application/json' -d '{\"session_id\":1,\"timestamp\":\"'$(date -Iseconds)'\"}' > /dev/null 2>&1 &"
}
]
}
}
```
## Simplified Hook Configuration
For easier setup, you can use our provided configuration file:
```bash
# Copy the hook configuration to Claude Code settings
cp config/claude-hooks.json ~/.config/claude-code/
```
## Hook Details
### SessionStart Hooks
Triggered when starting Claude Code or resuming a session:
- **startup**: Fresh start of Claude Code
- **resume**: Resuming after --resume flag
- **clear**: Starting after clearing history
**Data Captured:**
- Working directory
- Git branch and repository
- System environment info
- Session type
### UserPromptSubmit Hook
Triggered when you submit a prompt to Claude:
**Data Captured:**
- Your input message
- Timestamp
- Session context
### Notification Hook
Triggered when Claude is waiting for input:
**Data Captured:**
- Wait start time
- Context before waiting
### PostToolUse Hooks
Triggered after successful tool execution:
**Tools Tracked:**
- **Edit/Write**: File modifications
- **Read**: File examinations
- **Bash**: Command executions
- **Grep/Glob**: Search operations
- **Task**: Subagent usage
**Data Captured:**
- Tool name and action
- Target files
- Success/failure status
- Tool-specific metadata
### Stop Hook
Triggered when Claude finishes responding:
**Data Captured:**
- Response completion
- End of waiting period
- Session activity summary
## Testing Hook Configuration
1. **Start the tracker server:**
```bash
python main.py
```
2. **Verify hooks are working:**
```bash
# Check server logs for incoming requests
tail -f tracker.log
# Test a simple operation in Claude Code
# You should see API calls in the logs
```
3. **Manual hook testing:**
```bash
# Test session start hook
curl -X POST http://localhost:8000/api/session/start \
-H 'Content-Type: application/json' \
-d '{"session_type":"startup","working_directory":"'$(pwd)'"}'
```
## Environment Variables
The hooks can use these Claude Code environment variables:
- `$CLAUDE_USER_PROMPT` - User's input message
- `$CLAUDE_TOOL_FILE_PATH` - File path for file operations
- `$CLAUDE_BASH_COMMAND` - Bash command being executed
- `$CLAUDE_GREP_PATTERN` - Search pattern for Grep
- `$CLAUDE_GLOB_PATTERN` - File pattern for Glob
- `$CLAUDE_TASK_TYPE` - Type of Task/subagent
## Troubleshooting
### Hooks Not Firing
1. **Check Claude Code settings syntax:**
```bash
# Validate JSON syntax
python -m json.tool ~/.config/claude-code/settings.json
```
2. **Verify tracker server is running:**
```bash
curl http://localhost:8000/api/projects
```
3. **Check hook command syntax:**
```bash
# Test commands manually in shell
echo "Testing hook command..."
```
### Missing Data
1. **Session ID issues**: The static `session_id: 1` in examples needs dynamic generation
2. **JSON escaping**: Special characters in prompts/paths may break JSON
3. **Network issues**: Hooks may fail if server is unreachable
### Performance Impact
- Hooks run asynchronously (`&` at end of commands)
- Failed hook calls don't interrupt Claude Code operation
- Network timeouts are handled gracefully
## Advanced Configuration
### Dynamic Session IDs
For production use, implement session ID management:
```bash
# Store session ID in temp file
CLAUDE_SESSION_FILE="/tmp/claude-session-id"
# In SessionStart hook:
SESSION_ID=$(curl -s ... | jq -r '.session_id')
echo $SESSION_ID > $CLAUDE_SESSION_FILE
# In other hooks:
SESSION_ID=$(cat $CLAUDE_SESSION_FILE 2>/dev/null || echo "1")
```
### Conditional Hook Execution
Skip tracking for certain directories:
```bash
# Only track in specific directories
if [[ "$PWD" =~ "/projects/" ]]; then
curl -X POST ...
fi
```
### Error Handling
Add error logging to hooks:
```bash
curl ... 2>> ~/claude-tracker-errors.log || echo "Hook failed: $(date)" >> ~/claude-tracker-errors.log
```
## Security Considerations
- Hooks execute with your shell privileges
- API calls are made to localhost only
- No sensitive data is transmitted externally
- Hook commands are logged in shell history
## Next Steps
After setting up hooks:
1. Start using Claude Code normally
2. Check the web dashboard at http://localhost:8000
3. Review captured data and analytics
4. Adjust hook configuration as needed
For detailed API documentation, see [API Specification](api-spec.yaml).