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:
commit
44ed9936b7
48 changed files with 9707 additions and 0 deletions
530
docs/api-spec.yaml
Normal file
530
docs/api-spec.yaml
Normal 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
252
docs/database-schema.md
Normal 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
562
docs/development.md
Normal 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
279
docs/hook-setup.md
Normal 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).
|
||||
Loading…
Add table
Add a link
Reference in a new issue