Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions app/api/analyses/[id]/run/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,13 +196,15 @@ export async function POST(
await updateAnalysisStatus(id, 'scanning')
await deleteBlueprintsByAnalysis(id)
send({ status: 'scanning', progress: 10 })
send({ thought: 'Connecting to GitHub repositories...' })

// Fetch file trees from GitHub for each repository
const allFiles: { repo: string; path: string; type: string }[] = []
const repoErrors: string[] = []

for (const repo of repositories) {
try {
send({ thought: `Scanning file tree for ${repo.full_name}...` })
let treeData: Awaited<ReturnType<typeof getGitHubRepositoryTreeFromBranch>>
try {
treeData = await getGitHubRepositoryTreeFromBranch(
Expand Down Expand Up @@ -261,16 +263,18 @@ export async function POST(
return
}

send({ status: 'scanning', progress: 40 })
send({ status: 'scanning', progress: 40, thought: `Found ${allFiles.length} source files across ${repositories.length} repositories` })

// Update to analyzing
await updateAnalysisStatus(id, 'analyzing', { total_files: allFiles.length })
send({ status: 'analyzing', progress: 50 })
send({ status: 'analyzing', progress: 50, thought: 'Evaluating architecture patterns and reusable modules...' })

// Build file summary for AI (cap at 400 files to keep prompt reasonable)
const filesToSend = allFiles.slice(0, 400)
const fileSummary = filesToSend.map(f => `- ${f.repo}: ${f.path}`).join('\n')

send({ thought: 'Identifying component boundaries and shared dependencies...' })

// Use Claude to analyze and discover app blueprints (structured tool output)
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY })

Expand Down Expand Up @@ -374,7 +378,7 @@ Constraints:
],
})

send({ status: 'analyzing', progress: 80 })
send({ status: 'analyzing', progress: 80, thought: 'Ranking blueprints by opportunity score and feasibility...' })

// Check if response was truncated (hit max_tokens)
if (aiResponse.stop_reason === 'max_tokens') {
Expand Down
201 changes: 7 additions & 194 deletions app/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { getAllRepositories, getAllAnalyses, getGapSummary, type Analysis, type Repository } from '@/lib/queries'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import { FolderGit2, Sparkles, Code2, Plus, ArrowRight, Zap, AlertCircle, Lightbulb } from 'lucide-react'
import Link from 'next/link'
import { DashboardClient } from '@/components/dashboard-client'

export const dynamic = 'force-dynamic'

Expand All @@ -22,195 +19,11 @@ export default async function DashboardPage() {
const completedAnalyses = analyses.filter((analysis) => analysis.status === 'complete')

return (
<div className="space-y-10">
{/* Header */}
<div className="space-y-2">
<h1 className="text-3xl font-bold text-foreground tracking-tight">Dashboard</h1>
<p className="text-muted-foreground text-lg">Your code intelligence overview</p>
</div>

{/* Quick stats */}
<div className="grid gap-4 md:grid-cols-3">
<Card className="group p-6 hover:shadow-lg hover:shadow-black/5 transition-all duration-300 hover:border-border">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-muted-foreground">Repositories</p>
<p className="text-3xl font-bold text-foreground mt-1 tabular-nums">{repositories.length}</p>
</div>
<div className="h-12 w-12 rounded-xl bg-chart-1/10 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
<FolderGit2 className="h-6 w-6 text-chart-1" />
</div>
</div>
</Card>
<Card className="group p-6 hover:shadow-lg hover:shadow-black/5 transition-all duration-300 hover:border-border">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-muted-foreground">Analyses Run</p>
<p className="text-3xl font-bold text-foreground mt-1 tabular-nums">{analyses.length}</p>
</div>
<div className="h-12 w-12 rounded-xl bg-chart-2/10 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
<Sparkles className="h-6 w-6 text-chart-2" />
</div>
</div>
</Card>
<Card className="group p-6 hover:shadow-lg hover:shadow-black/5 transition-all duration-300 hover:border-border">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-muted-foreground">Completed</p>
<p className="text-3xl font-bold text-foreground mt-1 tabular-nums">{completedAnalyses.length}</p>
</div>
<div className="h-12 w-12 rounded-xl bg-chart-4/10 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
<Code2 className="h-6 w-6 text-chart-4" />
</div>
</div>
</Card>
</div>

{/* Quick Actions */}
<section className="space-y-5">
<h2 className="text-xl font-semibold text-foreground">Quick Actions</h2>

{repositories.length === 0 ? (
<Card className="border-dashed border-2 p-10 text-center hover:border-border transition-colors">
<div className="h-16 w-16 rounded-2xl bg-muted/50 flex items-center justify-center mx-auto mb-4">
<FolderGit2 className="h-8 w-8 text-muted-foreground/50" />
</div>
<h3 className="text-lg font-semibold text-foreground mb-2">Connect your first repository</h3>
<p className="text-sm text-muted-foreground mb-6 max-w-md mx-auto">
Start by adding your GitHub repositories. We&apos;ll scan all files and prepare them for AI analysis.
</p>
<Button size="lg" asChild>
<Link href="/dashboard/repositories">
<Plus className="h-4 w-4 mr-2" />
Add Repository
</Link>
</Button>
</Card>
) : (
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
<Card className="group p-6 hover:shadow-lg hover:shadow-black/5 transition-all duration-300 hover:border-border">
<div className="flex items-start justify-between mb-5">
<div className="h-12 w-12 rounded-xl bg-chart-1/10 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
<FolderGit2 className="h-6 w-6 text-chart-1" />
</div>
<span className="text-xs font-semibold text-muted-foreground bg-muted px-2.5 py-1 rounded-full">
{repositories.length} connected
</span>
</div>
<h3 className="font-semibold text-lg text-foreground mb-1">Repositories</h3>
<p className="text-sm text-muted-foreground mb-5">
Manage your connected GitHub repositories and add new ones.
</p>
<Button variant="outline" size="sm" className="group-hover:bg-foreground/5" asChild>
<Link href="/dashboard/repositories">
Manage Repos
<ArrowRight className="h-4 w-4 ml-2" />
</Link>
</Button>
</Card>

<Card className="group p-6 hover:shadow-lg hover:shadow-black/5 transition-all duration-300 hover:border-border">
<div className="flex items-start justify-between mb-5">
<div className="h-12 w-12 rounded-xl bg-chart-2/10 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
<Zap className="h-6 w-6 text-chart-2" />
</div>
<span className="text-xs font-semibold text-muted-foreground bg-muted px-2.5 py-1 rounded-full">
{completedAnalyses.length} complete
</span>
</div>
<h3 className="font-semibold text-lg text-foreground mb-1">Run Analysis</h3>
<p className="text-sm text-muted-foreground mb-5">
Let AI discover what apps you can build from your existing code.
</p>
<Button size="sm" asChild>
<Link href="/dashboard/analyses">
<Sparkles className="h-4 w-4 mr-2" />
Start Analysis
</Link>
</Button>
</Card>

<Card className="group p-6 hover:shadow-lg hover:shadow-black/5 transition-all duration-300 hover:border-border">
<div className="flex items-start justify-between mb-5">
<div className="h-12 w-12 rounded-xl bg-chart-3/10 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
<Lightbulb className="h-6 w-6 text-chart-3" />
</div>
<span className="text-xs font-semibold text-muted-foreground bg-muted px-2.5 py-1 rounded-full">
Quick ideas
</span>
</div>
<h3 className="font-semibold text-lg text-foreground mb-1">Template Hub</h3>
<p className="text-sm text-muted-foreground mb-5">
Pre-configured combinations you can assemble into products today.
</p>
<Button variant="outline" size="sm" className="group-hover:bg-foreground/5" asChild>
<Link href="/dashboard/templates/browse">
Explore Templates
<ArrowRight className="h-4 w-4 ml-2" />
</Link>
</Button>
</Card>

{gapSummary.total_gaps > 0 && (
<Card className="group p-6 hover:shadow-lg hover:shadow-black/5 transition-all duration-300 hover:border-border border-amber-200 bg-amber-50/50">
<div className="flex items-start justify-between mb-5">
<div className="h-12 w-12 rounded-xl bg-amber-100 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
<AlertCircle className="h-6 w-6 text-amber-600" />
</div>
<span className="text-xs font-semibold text-amber-700 bg-amber-100 px-2.5 py-1 rounded-full">
{gapSummary.total_gaps} open
</span>
</div>
<h3 className="font-semibold text-lg text-foreground mb-1">Missing Code</h3>
<p className="text-sm text-muted-foreground mb-5">
{Math.round(gapSummary.total_hours)} hours of features ready to build
</p>
<Button size="sm" asChild>
<Link href="/dashboard/gaps">
<AlertCircle className="h-4 w-4 mr-2" />
View Dashboard
</Link>
</Button>
</Card>
)}
</div>
)}
</section>

{/* Recent Repositories */}
{repositories.length > 0 && (
<section className="space-y-5">
<div className="flex items-center justify-between">
<h2 className="text-xl font-semibold text-foreground">Recent Repositories</h2>
<Button variant="ghost" size="sm" asChild>
<Link href="/dashboard/repositories">
View All
<ArrowRight className="h-4 w-4 ml-1" />
</Link>
</Button>
</div>
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
{repositories.slice(0, 6).map((repo) => (
<Card key={repo.id} className="group p-5 hover:shadow-md hover:shadow-black/5 transition-all duration-200 hover:border-border">
<div className="flex items-start gap-3">
<div className="h-10 w-10 rounded-xl bg-muted/50 flex items-center justify-center flex-shrink-0 group-hover:bg-muted transition-colors">
<FolderGit2 className="h-5 w-5 text-muted-foreground" />
</div>
<div className="min-w-0 flex-1">
<h3 className="font-semibold text-foreground truncate">{repo.name}</h3>
<p className="text-xs text-muted-foreground truncate mt-0.5">{repo.full_name}</p>
{repo.language && (
<span className="inline-block mt-2 text-xs px-2 py-0.5 rounded-full bg-muted text-muted-foreground font-medium">
{repo.language}
</span>
)}
</div>
</div>
</Card>
))}
</div>
</section>
)}
</div>
<DashboardClient
repositories={repositories}
analyses={analyses}
completedAnalyses={completedAnalyses}
gapSummary={gapSummary}
/>
)
}
55 changes: 39 additions & 16 deletions components/analysis-detail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
Lock,
Crown,
Hammer,
Rocket,
} from 'lucide-react'
import type { Analysis, Repository, AppBlueprint } from '@/lib/queries'
import { BuildAppModal } from '@/components/build-app-modal'
Expand All @@ -35,6 +36,9 @@ import {
getOpportunityScore,
getSuggestedFirstStep,
} from '@/lib/opportunity-metrics'
import { ThoughtStream } from '@/components/thought-stream'
import { InsightRail } from '@/components/insight-rail'
import { useFounderPreferences, getFounderInsights, FounderReasoningBadges } from '@/components/founder-preferences'

interface AnalysisDetailProps {
analysis: Analysis
Expand Down Expand Up @@ -90,6 +94,8 @@ export function AnalysisDetail({
)
const [localBlueprints, setLocalBlueprints] = useState(blueprints)
const [runErrorMessage, setRunErrorMessage] = useState<string | null>(analysis.error_message)
const [thoughts, setThoughts] = useState<string[]>([])
const { prefs: founderPrefs } = useFounderPreferences()

useEffect(() => {
setStatus(analysis.status)
Expand Down Expand Up @@ -224,6 +230,7 @@ export function AnalysisDetail({
setProgress(0)
setRunErrorMessage(null)
setLocalBlueprints([])
setThoughts([])

try {
const response = await fetch(`/api/analyses/${analysis.id}/run`, {
Expand Down Expand Up @@ -263,6 +270,7 @@ export function AnalysisDetail({
progress?: number
blueprints?: AppBlueprint[]
error?: string
thought?: string
}

if (typeof data.error === 'string') {
Expand All @@ -280,6 +288,7 @@ export function AnalysisDetail({
setStatus(data.status)
}
if (data.progress !== undefined) setProgress(data.progress)
if (data.thought) setThoughts(prev => [...prev, data.thought!])
if (data.blueprints) setLocalBlueprints(data.blueprints)
} catch {
/* incomplete JSON chunk — wait for next read */
Expand Down Expand Up @@ -346,19 +355,22 @@ export function AnalysisDetail({
</Card>
)}

{/* Progress */}
{/* Progress + AI Thought Stream */}
{isInProgress && (
<Card className="p-6">
<div className="space-y-3">
<div className="flex items-center justify-between text-sm">
<span className="text-muted-foreground">
{status === 'scanning' ? 'Scanning repositories...' : 'AI analyzing files...'}
</span>
<span className="font-medium">{progress}%</span>
<div className="space-y-4">
<Card className="p-6">
<div className="space-y-3">
<div className="flex items-center justify-between text-sm">
<span className="text-muted-foreground">
{status === 'scanning' ? 'Scanning repositories...' : 'AI analyzing files...'}
</span>
<span className="font-medium">{progress}%</span>
</div>
<Progress value={progress} className="h-2" />
</div>
<Progress value={progress} className="h-2" />
</div>
</Card>
</Card>
<ThoughtStream thoughts={thoughts} isActive={isInProgress} />
</div>
)}

{/* Repositories */}
Expand All @@ -380,8 +392,9 @@ export function AnalysisDetail({
</div>
</section>

{/* App Blueprints */}
<section className="space-y-4">
{/* App Blueprints + Insight Rail */}
<div className="flex flex-col lg:flex-row gap-6">
<section className="space-y-4 flex-1 min-w-0">
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
<h2 className="text-lg font-semibold text-foreground">Discovered App Blueprints</h2>
<div className="flex flex-wrap items-center gap-2">
Expand Down Expand Up @@ -589,12 +602,14 @@ export function AnalysisDetail({
)}
</div>

<FounderReasoningBadges insights={getFounderInsights(founderPrefs, blueprint)} />

<Button
className="mt-4 w-full"
className="mt-4 w-full bg-cyan-600 hover:bg-cyan-500 text-white shadow-lg shadow-cyan-500/20 hover:shadow-cyan-500/30 transition-all"
onClick={() => setBuildModalBlueprint(blueprint)}
>
<Hammer className="h-4 w-4 mr-2" />
Build This App
<Rocket className="h-4 w-4 mr-2" />
Build This
</Button>

{blueprint.missing_files.length > 0 ? (
Expand Down Expand Up @@ -644,6 +659,14 @@ export function AnalysisDetail({
)}
</section>

{/* Right Insight Rail — CTO Whisper */}
{(localBlueprints.length > 0 || isInProgress) && (
<aside className="lg:w-72 xl:w-80 shrink-0 space-y-4">
<InsightRail blueprints={localBlueprints} isAnalyzing={isInProgress} />
</aside>
)}
</div>

{buildModalBlueprint && (
<BuildAppModal
blueprint={buildModalBlueprint}
Expand Down
Loading
Loading