'use client'; import { use, useState } from 'react'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Separator } from '@/components/ui/separator'; import { Loader2, FileText, TrendingUp, DollarSign, Code, Calendar, Clock } from 'lucide-react'; interface AuditReport { projectId: string; generatedAt: string; timeline: { firstActivity: string | null; lastActivity: string | null; totalDays: number; activeDays: number; totalSessions: number; sessions: Array<{ sessionId: string; date: string; startTime: string; endTime: string; duration: number; messageCount: number; userMessages: number; aiMessages: number; topics: string[]; filesWorkedOn: string[]; }>; velocity: { messagesPerDay: number; averageSessionLength: number; peakProductivityHours: number[]; }; }; costs: { messageStats: { totalMessages: number; userMessages: number; aiMessages: number; avgMessageLength: number; }; estimatedTokens: { input: number; output: number; total: number; }; costs: { inputCost: number; outputCost: number; totalCost: number; currency: string; }; model: string; pricing: { inputPer1M: number; outputPer1M: number; }; }; features: Array<{ name: string; description: string; pages: string[]; apis: string[]; status: string; }>; techStack: { frontend: Record; backend: Record; integrations: string[]; }; extensionActivity: { totalSessions: number; uniqueFilesEdited: number; topFiles: Array<{ file: string; editCount: number }>; earliestActivity: string | null; latestActivity: string | null; } | null; gitHistory: { totalCommits: number; firstCommit: string | null; lastCommit: string | null; totalFilesChanged: number; totalInsertions: number; totalDeletions: number; commits: Array<{ hash: string; date: string; author: string; message: string; filesChanged: number; insertions: number; deletions: number; }>; topFiles: Array<{ filePath: string; changeCount: number }>; commitsByDay: Record; authors: Array<{ name: string; commitCount: number }>; } | null; unifiedTimeline: { projectId: string; dateRange: { earliest: string; latest: string; totalDays: number; }; days: Array<{ date: string; dayOfWeek: string; gitCommits: any[]; extensionSessions: any[]; cursorMessages: any[]; summary: { totalGitCommits: number; totalExtensionSessions: number; totalCursorMessages: number; linesAdded: number; linesRemoved: number; uniqueFilesModified: number; }; }>; dataSources: { git: { available: boolean; firstDate: string | null; lastDate: string | null; totalRecords: number }; extension: { available: boolean; firstDate: string | null; lastDate: string | null; totalRecords: number }; cursor: { available: boolean; firstDate: string | null; lastDate: string | null; totalRecords: number }; }; } | null; summary: { totalConversations: number; totalMessages: number; developmentPeriod: number; estimatedCost: number; extensionSessions: number; filesEdited: number; gitCommits: number; linesAdded: number; linesRemoved: number; timelineDays: number; }; } export default function ProjectAuditPage({ params, }: { params: Promise<{ workspace: string; projectId: string }>; }) { const { workspace, projectId } = use(params); const [report, setReport] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const generateReport = async () => { setLoading(true); setError(null); try { const response = await fetch(`/api/projects/${projectId}/audit/generate`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({}) }); if (!response.ok) { const data = await response.json(); throw new Error(data.error || 'Failed to generate report'); } const data = await response.json(); setReport(data); } catch (err) { setError(err instanceof Error ? err.message : 'Unknown error'); } finally { setLoading(false); } }; const formatDate = (dateStr: string | null) => { if (!dateStr) return 'N/A'; return new Date(dateStr).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }); }; const formatCurrency = (amount: number) => { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(amount); }; const formatNumber = (num: number) => { return new Intl.NumberFormat('en-US').format(num); }; return (

Project Audit Report

Comprehensive analysis of development history, costs, and architecture

{error && ( Error

{error}

{error.includes('No conversations found') && (

Import Cursor conversations first to generate an audit report.

)}
)} {!report && !loading && !error && ( Ready to Generate Click the button above to analyze your project's development history, calculate costs, and document your architecture.

Timeline Analysis

Work sessions & velocity

Cost Estimation

AI & developer costs

Architecture

Features & tech stack

)} {report && (
{/* Summary Section */}
Total Messages
{formatNumber(report.summary.totalMessages)}

{report.summary.totalConversations} conversations

Development Period
{report.summary.developmentPeriod} days

{report.timeline.activeDays} active days

Work Sessions
{report.timeline.totalSessions}

Avg {report.timeline.velocity.averageSessionLength} min

AI Cost
{formatCurrency(report.summary.estimatedCost)}

{report.costs.model}

Git Commits
{formatNumber(report.summary.gitCommits)}

Code changes

Lines Changed
+{formatNumber(report.summary.linesAdded)} {' / '} -{formatNumber(report.summary.linesRemoved)}

Total modifications

{/* Unified Timeline Section */} {report.unifiedTimeline && ( Complete Project Timeline Day-by-day history combining Git commits, Extension activity, and Cursor messages {/* Data Source Overview */}

📊 Git Commits

{report.unifiedTimeline.dataSources.git.available ? ( <> {report.unifiedTimeline.dataSources.git.totalRecords} commits
{formatDate(report.unifiedTimeline.dataSources.git.firstDate)} to {formatDate(report.unifiedTimeline.dataSources.git.lastDate)} ) : 'No data'}

💻 Extension Activity

{report.unifiedTimeline.dataSources.extension.available ? ( <> {report.unifiedTimeline.dataSources.extension.totalRecords} sessions
{formatDate(report.unifiedTimeline.dataSources.extension.firstDate)} to {formatDate(report.unifiedTimeline.dataSources.extension.lastDate)} ) : 'No data'}

🤖 Cursor Messages

{report.unifiedTimeline.dataSources.cursor.available ? ( <> {report.unifiedTimeline.dataSources.cursor.totalRecords} messages
{formatDate(report.unifiedTimeline.dataSources.cursor.firstDate)} to {formatDate(report.unifiedTimeline.dataSources.cursor.lastDate)} ) : 'No data'}

{/* Timeline Days */}
{report.unifiedTimeline.days.filter(day => day.summary.totalGitCommits > 0 || day.summary.totalExtensionSessions > 0 || day.summary.totalCursorMessages > 0 ).reverse().map((day, index) => (

{formatDate(day.date)}

{day.dayOfWeek}

{day.summary.totalGitCommits > 0 && ( 📊 {day.summary.totalGitCommits} )} {day.summary.totalExtensionSessions > 0 && ( 💻 {day.summary.totalExtensionSessions} )} {day.summary.totalCursorMessages > 0 && ( 🤖 {day.summary.totalCursorMessages} )}
{/* Git Commits */} {day.gitCommits.length > 0 && (

Git Commits:

{day.gitCommits.map((commit: any, idx: number) => (
• {commit.message} (+{commit.insertions}/-{commit.deletions})
))}
)} {/* Extension Sessions */} {day.extensionSessions.length > 0 && (

Extension Sessions: {day.summary.totalExtensionSessions} ({day.summary.uniqueFilesModified} files modified)

{day.extensionSessions.slice(0, 3).map((session: any, idx: number) => (
• {session.duration} min session {session.conversationSummary && ( - {session.conversationSummary.substring(0, 50)}... )}
))} {day.extensionSessions.length > 3 && (

+{day.extensionSessions.length - 3} more sessions

)}
)} {/* Cursor Messages */} {day.cursorMessages.length > 0 && (

AI Conversations: {day.summary.totalCursorMessages} messages

• Active in: {[...new Set(day.cursorMessages.map((m: any) => m.conversationName))].join(', ')}
)}
{/* Day Summary */} {(day.summary.linesAdded > 0 || day.summary.linesRemoved > 0) && (
Total changes: +{day.summary.linesAdded} / -{day.summary.linesRemoved} lines
)}
))}
)} {/* Timeline Section */} Development Timeline Work sessions and development velocity

Development Period

{formatDate(report.timeline.firstActivity)}

to {formatDate(report.timeline.lastActivity)}

Peak Productivity Hours

{report.timeline.velocity.peakProductivityHours.map(h => `${h}:00`).join(', ')}

Most active times

Velocity Metrics

Messages per day: {report.timeline.velocity.messagesPerDay.toFixed(1)}
Average session length: {report.timeline.velocity.averageSessionLength} minutes
Total sessions: {report.timeline.totalSessions}

Recent Sessions

{report.timeline.sessions.slice(-5).reverse().map((session) => (
{formatDate(session.date)} {session.duration} min
{session.messageCount} messages • {session.topics.slice(0, 2).join(', ')}
))}
{/* Extension Activity Section */} {report.extensionActivity && ( File Edit Activity Files you've edited tracked by the Cursor Monitor extension

Extension Sessions

{report.extensionActivity.totalSessions}

Work sessions logged

Files Edited

{report.extensionActivity.uniqueFilesEdited}

Unique files modified

Activity Period

{report.extensionActivity.earliestActivity ? formatDate(report.extensionActivity.earliestActivity) : 'N/A'}

to {report.extensionActivity.latestActivity ? formatDate(report.extensionActivity.latestActivity) : 'N/A'}

Most Edited Files (Top 20)

{report.extensionActivity.topFiles.map((item, index) => (
{item.file.split('/').pop()} {item.editCount} {item.editCount === 1 ? 'edit' : 'edits'}
))}
)} {/* Git Commit History Section */} {report.gitHistory && ( Git Commit History Complete development history from Git repository

Total Commits

{report.gitHistory.totalCommits}

Code changes tracked

Lines of Code

+{formatNumber(report.gitHistory.totalInsertions)}

-{formatNumber(report.gitHistory.totalDeletions)}

Repository Period

{report.gitHistory.firstCommit ? formatDate(report.gitHistory.firstCommit) : 'N/A'}

to {report.gitHistory.lastCommit ? formatDate(report.gitHistory.lastCommit) : 'N/A'}

{/* Authors */} {report.gitHistory.authors.length > 0 && ( <>

Contributors

{report.gitHistory.authors.map((author, index) => ( {author.name} ({author.commitCount} {author.commitCount === 1 ? 'commit' : 'commits'}) ))}
)} {/* Top Files */}

Most Changed Files (Top 20)

{report.gitHistory.topFiles.map((item, index) => (
{item.filePath.split('/').pop()} {item.changeCount} {item.changeCount === 1 ? 'change' : 'changes'}
))}
{/* Recent Commits */}

Recent Commits (Last 20)

{report.gitHistory.commits.slice(0, 20).map((commit, index) => (

{commit.message}

{commit.author} • {formatDate(commit.date)} • {commit.hash}

+{commit.insertions} / -{commit.deletions}
))}
)} {/* Cost Analysis Section */} AI Cost Analysis Estimated costs based on {report.costs.model} usage

Message Statistics

Total messages: {formatNumber(report.costs.messageStats.totalMessages)}
User messages: {formatNumber(report.costs.messageStats.userMessages)}
AI messages: {formatNumber(report.costs.messageStats.aiMessages)}
Avg length: {report.costs.messageStats.avgMessageLength} chars

Token Usage

Input tokens: {formatNumber(report.costs.estimatedTokens.input)}
Output tokens: {formatNumber(report.costs.estimatedTokens.output)}
Total tokens: {formatNumber(report.costs.estimatedTokens.total)}

Cost Breakdown

Input cost ({formatCurrency(report.costs.pricing.inputPer1M)}/1M tokens): {formatCurrency(report.costs.costs.inputCost)}
Output cost ({formatCurrency(report.costs.pricing.outputPer1M)}/1M tokens): {formatCurrency(report.costs.costs.outputCost)}

Total AI Cost

{report.costs.model}

{formatCurrency(report.costs.costs.totalCost)}

* Token estimation: ~4 characters per token

* Costs are estimates based on message content length

{/* Features Section */} Features Implemented Current project capabilities and status
{report.features.map((feature, index) => (

{feature.name}

{feature.status}

{feature.description}

{feature.pages.length > 0 && (
Pages:{' '} {feature.pages.join(', ')}
)} {feature.apis.length > 0 && (
APIs:{' '} {feature.apis.join(', ')}
)}
))}
{/* Tech Stack Section */} Technology Stack Frameworks, libraries, and integrations

Frontend

{Object.entries(report.techStack.frontend).map(([key, value]) => (
{key.replace(/([A-Z])/g, ' $1')}: {value}
))}

Backend

{Object.entries(report.techStack.backend).map(([key, value]) => (
{key}: {value}
))}

Integrations

{report.techStack.integrations.map((integration) => ( {integration} ))}
Report generated at {new Date(report.generatedAt).toLocaleString()}
)}
); }