diff --git a/.env.example b/.env.example index e25292d..5d9b1e9 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,6 @@ -# Neon PostgreSQL Database -DATABASE_URL=postgresql://user:password@host/dbname?sslmode=require +# Supabase PostgreSQL Database +# Find your connection string in the Supabase dashboard: Settings → Database → Connection string → URI +DATABASE_URL=postgresql://postgres.[project-ref]:[password]@aws-0-[region].pooler.supabase.com:6543/postgres # GitHub OAuth App # Create at: https://github.com/settings/developers @@ -11,8 +12,10 @@ GITHUB_CLIENT_SECRET=your_github_oauth_client_secret # Public URL of your app (used for OAuth callback redirect) NEXT_PUBLIC_APP_URL=https://repo-app-architect.vercel.app -# OpenAI API Key (used by Vercel AI SDK for analysis) +# OpenAI API Key (used for GPT-based blueprint analysis) OPENAI_API_KEY=sk-... +# Optional override for OpenAI model (default: gpt-4o) +# OPENAI_ANALYSIS_MODEL=gpt-4o # Anthropic API Key (analysis + scaffold generation) ANTHROPIC_API_KEY=sk-ant-... diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..211febe --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,53 @@ +# AGENTS.md + +## Cursor Cloud specific instructions + +### Overview + +CodeVault is an AI-powered code intelligence platform built with **Next.js 16** (App Router), **TypeScript**, **Tailwind CSS v4**, and **Shadcn UI**. It connects to GitHub repos, scans files, and uses AI to discover application blueprints. + +### Package manager + +This project uses **pnpm**. The lockfile is `pnpm-lock.yaml`. + +### Scripts (from `package.json`) + +| Command | Purpose | +|---------|---------| +| `pnpm dev` | Start Next.js dev server (port 3000) | +| `pnpm build` | Production build | +| `pnpm lint` | Run ESLint | +| `pnpm exec tsc --noEmit` | Type check | + +### Lint and type check + +- **Lint**: `pnpm lint` — runs ESLint. The codebase has a few pre-existing lint warnings/errors (unused vars, `prefer-const`, `set-state-in-effect`). These are not regressions. +- **Type check**: `pnpm exec tsc --noEmit` — clean pass. If you see errors referencing `.next/types/validator.ts` or `.next/dev/types/validator.ts`, run `rm -rf .next` first, as stale generated types can cause false failures. + +### No automated test suite + +There is no test framework (jest, vitest, playwright, etc.) configured in this codebase. CI only runs lint + typecheck. + +### Database + +The app uses `postgres` (postgres.js) to connect to PostgreSQL via standard TCP sockets. Any PostgreSQL-compatible database works (Supabase, local PostgreSQL, etc.). Set `DATABASE_URL` to a valid PostgreSQL connection string. The schema migration is at `scripts/01-create-schema.sql`, or hit `GET /api/setup/init-db` to initialize the DB via the app. + +### Environment variables + +Copy `.env.example` to `.env.local`. Required variables: + +- `DATABASE_URL` — Supabase (or any PostgreSQL) connection string +- `GITHUB_CLIENT_ID` / `GITHUB_CLIENT_SECRET` — GitHub OAuth App credentials +- `NEXT_PUBLIC_APP_URL` — App URL (use the local dev server URL) +- `OPENAI_API_KEY` — For AI analysis features +- `ANTHROPIC_API_KEY` — Optional, for scaffold generation + +### Authentication + +The app uses GitHub OAuth. The middleware at `middleware.ts` blocks unauthenticated access to `/dashboard/*` routes by checking `github_user_id` and `github_access_token` cookies. To test dashboard features, you need a working GitHub OAuth App with the callback URL set to `{NEXT_PUBLIC_APP_URL}/api/auth/github/callback`. + +### Key gotchas + +- The landing page (`/`) works without any database or API credentials — it's a static page. +- All dashboard routes require authentication cookies set by the GitHub OAuth flow. +- The pnpm install may warn about ignored build scripts for `sharp` and `unrs-resolver`. This is fine and doesn't affect functionality. diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 26b2480..18d92ed 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -48,7 +48,7 @@ ┌──────────────────────────────────────────────────────────────────────┐ │ DATA LAYER │ │ ┌────────────────────────────────────────────────────────────────┐ │ -│ │ Neon PostgreSQL │ │ +│ │ Supabase PostgreSQL │ │ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ │ │ user_auth Table - GitHub OAuth users │ │ │ │ │ │ repositories Table - Tracked GitHub repos │ │ │ diff --git a/IMPLEMENTATION_SUMMARY.md b/IMPLEMENTATION_SUMMARY.md index af3b75f..f835566 100644 --- a/IMPLEMENTATION_SUMMARY.md +++ b/IMPLEMENTATION_SUMMARY.md @@ -2,7 +2,7 @@ ## ✅ Completed Architecture -### 1. Database Layer (Neon PostgreSQL) +### 1. Database Layer (Supabase PostgreSQL) - ✅ `user_auth` — GitHub OAuth user records - ✅ `repositories` — Tracked GitHub repositories - ✅ `repo_files` — Scanned files with AI metadata (JSONB fields) @@ -62,7 +62,7 @@ - UI primitives (Shadcn) /lib - - db.ts - Neon database client + - db.ts - Database client (postgres.js) - queries.ts - All database operations - utils.ts - cn() and other helpers @@ -83,7 +83,7 @@ ## 🔑 Key Technologies - **Framework**: Next.js 16 (App Router) -- **Database**: Neon PostgreSQL (`@neondatabase/serverless`) +- **Database**: Supabase PostgreSQL (`postgres` / postgres.js) - **AI**: Vercel AI SDK + OpenAI GPT-4 - **Auth**: Custom GitHub OAuth - **UI**: Shadcn UI + Tailwind CSS v4 diff --git a/QUICK_START.md b/QUICK_START.md index 2a69f89..828796b 100644 --- a/QUICK_START.md +++ b/QUICK_START.md @@ -3,7 +3,7 @@ ## Prerequisites - Node.js 20+ and pnpm -- A [Neon](https://neon.tech) PostgreSQL database +- A [Supabase](https://supabase.com) project (or any PostgreSQL database) - A GitHub OAuth App (for GitHub integration) - An OpenAI API key (for AI analysis) @@ -26,7 +26,7 @@ cp .env.example .env.local Edit `.env.local` with your values: ``` -DATABASE_URL=postgresql://... # From Neon dashboard +DATABASE_URL=postgresql://... # From Supabase dashboard (Settings → Database) GITHUB_CLIENT_ID=... # From GitHub OAuth App GITHUB_CLIENT_SECRET=... # From GitHub OAuth App NEXT_PUBLIC_APP_URL=http://localhost:3000 @@ -43,7 +43,7 @@ OPENAI_API_KEY=sk-... # From OpenAI dashboard ### 4. Set Up the Database -Run the migration in your Neon SQL Editor or with psql: +Run the migration in your Supabase SQL Editor or with psql: ```bash psql $DATABASE_URL -f scripts/01-create-schema.sql @@ -95,7 +95,7 @@ Navigate to **http://localhost:3000** to see the app. **Database connection error?** - Check `DATABASE_URL` is correct -- Verify your Neon project is active +- Verify your Supabase project is active (or your PostgreSQL server is running) **GitHub OAuth not working?** - Check `GITHUB_CLIENT_ID` and `GITHUB_CLIENT_SECRET` diff --git a/README.md b/README.md index 08d0111..2494a92 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ An AI-powered code intelligence platform that scans your GitHub repositories and ## Tech Stack - **Framework**: Next.js 16 with App Router -- **Database**: Neon PostgreSQL with connection pooling +- **Database**: Supabase PostgreSQL (or any PostgreSQL) - **AI**: Vercel AI SDK (OpenAI GPT-4) - **UI Components**: Shadcn UI with Radix primitives - **Styling**: Tailwind CSS v4 @@ -50,7 +50,7 @@ components/ ├── app-suggestions.tsx # App idea cards └── ui/ # Shadcn components lib/ -├── db.ts # Neon database client +├── db.ts # Database client (postgres.js) ├── queries.ts # Database queries └── utils.ts # Utility functions scripts/ @@ -117,7 +117,7 @@ cp .env.example .env.local ``` 4. **Set up the database** -Run the schema migration in your Neon console or with psql: +Run the schema migration in your Supabase SQL Editor or with psql: ```bash psql $DATABASE_URL -f scripts/01-create-schema.sql ``` diff --git a/VERCEL_SETUP.md b/VERCEL_SETUP.md index fd5b05a..71b29d3 100644 --- a/VERCEL_SETUP.md +++ b/VERCEL_SETUP.md @@ -10,7 +10,7 @@ Go to your Vercel project → **Settings** → **Environment Variables** and add | Variable | Environment | Description | |----------|-------------|-------------| -| `DATABASE_URL` | Production, Preview, Development | Neon PostgreSQL connection string | +| `DATABASE_URL` | Production, Preview, Development | Supabase PostgreSQL connection string | | `GITHUB_CLIENT_ID` | Production, Preview, Development | GitHub OAuth App client ID | | `GITHUB_CLIENT_SECRET` | Production, Preview, Development | GitHub OAuth App client secret | | `NEXT_PUBLIC_APP_URL` | Production | Your production URL (e.g. `https://codevault.vercel.app`) | @@ -48,7 +48,7 @@ The workflow pulls env vars from Vercel automatically via `vercel pull`. Set the | Variable | Description | |----------|-------------| -| `DATABASE_URL` | Neon PostgreSQL connection string | +| `DATABASE_URL` | Supabase PostgreSQL connection string | | `GITHUB_CLIENT_ID` | GitHub OAuth App client ID | | `GITHUB_CLIENT_SECRET` | GitHub OAuth App client secret | | `NEXT_PUBLIC_APP_URL` | Your production URL | @@ -68,7 +68,7 @@ Once deployed, update your GitHub OAuth App callback URL: ## Run Database Migration -Run the schema SQL in your Neon console: +Run the schema SQL in your Supabase SQL Editor: ```sql -- Paste contents of scripts/01-create-schema.sql @@ -84,7 +84,7 @@ psql $DATABASE_URL -f scripts/01-create-schema.sql **GitHub OAuth redirects fail** → Check `NEXT_PUBLIC_APP_URL` matches your Vercel URL exactly -**Database errors** → Verify `DATABASE_URL` is correct and Neon project is active +**Database errors** → Verify `DATABASE_URL` is correct and Supabase project is active **AI analysis fails** → Check `OPENAI_API_KEY` has sufficient credits diff --git a/app/api/ai-providers/route.ts b/app/api/ai-providers/route.ts new file mode 100644 index 0000000..0181b39 --- /dev/null +++ b/app/api/ai-providers/route.ts @@ -0,0 +1,11 @@ +import { NextResponse } from 'next/server' +import { AI_PROVIDERS, getAvailableProviders } from '@/lib/ai-providers' + +export async function GET() { + const available = getAvailableProviders() + const providers = AI_PROVIDERS.map((p) => ({ + ...p, + available: available.includes(p.id), + })) + return NextResponse.json(providers) +} diff --git a/app/api/analyses/[id]/run/route.ts b/app/api/analyses/[id]/run/route.ts index ebf52ec..a0457f8 100644 --- a/app/api/analyses/[id]/run/route.ts +++ b/app/api/analyses/[id]/run/route.ts @@ -1,6 +1,4 @@ import { NextRequest } from 'next/server' -import Anthropic from '@anthropic-ai/sdk' -import { z } from 'zod' import { getCurrentAccessToken } from '@/lib/auth' import { getGitHubRepositoryTree, @@ -16,37 +14,7 @@ import { deleteBlueprintsByAnalysis, getBlueprintsByAnalysis } from '@/lib/queries' -import { getAnthropicModel } from '@/lib/anthropic-model' - -// Schema for AI-generated app blueprints -const complexityEnum = z.preprocess((val) => { - if (typeof val !== 'string') return val - const v = val.trim().toLowerCase() - if (v === 'easy') return 'simple' - return v -}, z.enum(['simple', 'moderate', 'complex'])) - -const BlueprintSchema = z.object({ - name: z.string(), - description: z.string(), - app_type: z.string(), - complexity: complexityEnum, - reuse_percentage: z.number().min(0).max(100), - existing_files: z.array(z.object({ - path: z.string(), - purpose: z.string(), - })), - missing_files: z.array(z.object({ - name: z.string(), - purpose: z.string(), - })), - technologies: z.array(z.string()), - explanation: z.string(), -}) - -const AnalysisOutputSchema = z.object({ - blueprints: z.array(BlueprintSchema), -}) +import { runAIAnalysis, getAvailableProviders, type AIProvider } from '@/lib/ai-providers' const CODE_EXTENSIONS = new Set([ 'ts', 'tsx', 'js', 'jsx', 'mjs', 'cjs', @@ -75,8 +43,28 @@ export async function POST( controller.close() return } - if (!process.env.ANTHROPIC_API_KEY) { - send({ error: 'AI analysis is not configured. Missing ANTHROPIC_API_KEY.' }) + + const url = new URL(request.url) + const providersParam = url.searchParams.get('providers') + const available = getAvailableProviders() + + if (available.length === 0) { + send({ error: 'No AI providers configured. Set ANTHROPIC_API_KEY and/or OPENAI_API_KEY.' }) + controller.close() + return + } + + let requestedProviders: AIProvider[] + if (providersParam) { + requestedProviders = providersParam.split(',').filter((p): p is AIProvider => + (p === 'anthropic' || p === 'openai') && available.includes(p as AIProvider) + ) + } else { + requestedProviders = available.slice(0, 1) + } + + if (requestedProviders.length === 0) { + send({ error: `Requested providers not available. Configured: ${available.join(', ')}` }) controller.close() return } @@ -167,177 +155,92 @@ export async function POST( send({ status: 'scanning', progress: 40 }) - // Update to analyzing await updateAnalysisStatus(id, 'analyzing', { total_files: allFiles.length }) send({ status: 'analyzing', progress: 50 }) - // Build file summary for AI const fileSummary = allFiles.map(f => `- ${f.repo}: ${f.path}`).join('\n') + const isMultiProvider = requestedProviders.length > 1 + let anySucceeded = false - // Use Claude to analyze and discover app blueprints (structured tool output) - const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY }) - - const userPrompt = `You are acting as an expert software architect and product strategist. -Analyze these files from GitHub repositories and discover what applications can be built by combining and reusing the existing code. - -REPOSITORIES AND FILES: -${fileSummary} - -Identify 3-6 practical applications that match these buckets: -1) Quick wins (ship-ready or very close), -2) Missing only a few files, -3) Larger but still feasible foundations. - -For each app blueprint: -- Give it a clear, descriptive name -- Describe what the app does -- Estimate complexity (simple/moderate/complex) -- Calculate reuse percentage (how much existing code can be reused) and be realistic -- List existing files that can be reused (with their purpose); prefer highest-value files -- List missing files needed (with their purpose) -- For missing_files.name, provide concrete file paths (e.g. "app/api/billing/route.ts") -- List technologies detected -- Provide a brief explanation of why this app is possible, including a suggested first build step - -Constraints: -- Use ONLY evidence from the provided files; do not invent major subsystems. -- Prefer opportunities that combine code across multiple repositories where possible. -- Keep missing_files concise and implementation-oriented. -- Avoid duplicate ideas that differ only by naming. -- Focus on practical, buildable applications based on the actual code patterns you see.` - - const aiResponse = await client.messages.create({ - model: getAnthropicModel(), - max_tokens: 4096, - system: [ - { - type: 'text', - text: 'You are an expert software architect. Your job is to analyze GitHub repository file structures and identify what new applications can be built by combining and reusing the existing code. Focus on practical, buildable applications based on actual code patterns.', - cache_control: { type: 'ephemeral' }, - }, - ], - tools: [ - { - name: 'report_blueprints', - description: 'Report the discovered app blueprints based on repository file analysis', - input_schema: { - type: 'object' as const, - properties: { - blueprints: { - type: 'array', - items: { - type: 'object', - properties: { - name: { type: 'string', description: 'Clear, descriptive name for the app' }, - description: { type: 'string', description: 'What the app does' }, - app_type: { type: 'string', description: 'Type of application (e.g. web app, CLI tool, API service)' }, - complexity: { type: 'string', enum: ['simple', 'moderate', 'complex'] }, - reuse_percentage: { type: 'number', minimum: 0, maximum: 100, description: 'Percentage of existing code that can be reused' }, - existing_files: { - type: 'array', - items: { - type: 'object', - properties: { - path: { type: 'string' }, - purpose: { type: 'string' }, - }, - required: ['path', 'purpose'], - }, - description: 'Existing files that can be reused', - }, - missing_files: { - type: 'array', - items: { - type: 'object', - properties: { - name: { type: 'string' }, - purpose: { type: 'string' }, - }, - required: ['name', 'purpose'], - }, - description: 'New files that need to be created', - }, - technologies: { type: 'array', items: { type: 'string' }, description: 'Technologies detected' }, - explanation: { type: 'string', description: 'Brief explanation of why this app is feasible' }, - }, - required: ['name', 'description', 'app_type', 'complexity', 'reuse_percentage', 'existing_files', 'missing_files', 'technologies', 'explanation'], - }, - }, - }, - required: ['blueprints'], - }, - }, - ], - tool_choice: { type: 'tool', name: 'report_blueprints' }, - messages: [ - { - role: 'user', - content: userPrompt, - }, - ], - }) + for (let pi = 0; pi < requestedProviders.length; pi++) { + const provider = requestedProviders[pi] + const progressBase = 50 + Math.round((pi / requestedProviders.length) * 40) - send({ status: 'analyzing', progress: 80 }) - - // Extract structured output from tool use response - let toolUseBlock = aiResponse.content.find( - (block) => - block.type === 'tool_use' && - 'name' in block && - (block as { name: string }).name === 'report_blueprints', - ) - if (!toolUseBlock) { - toolUseBlock = aiResponse.content.find((block) => block.type === 'tool_use') - } - const rawInput = toolUseBlock?.type === 'tool_use' ? toolUseBlock.input : null - let parsed = rawInput ? AnalysisOutputSchema.safeParse(rawInput) : null + send({ + status: 'analyzing', + progress: progressBase, + provider_status: { provider, state: 'running' }, + }) - if (rawInput && !parsed?.success) { - console.error('[analysis] Blueprint schema validation failed:', parsed?.error?.flatten()) - } + try { + await deleteBlueprintsByAnalysis(id, provider) - const output = parsed?.success ? parsed.data : null + const blueprintResults = await runAIAnalysis(fileSummary, provider) - if (!output?.blueprints?.length) { - const parseHint = parsed?.success === false - ? 'AI returned blueprints in an unexpected shape. Try again or set ANTHROPIC_ANALYSIS_MODEL to a supported Claude model.' - : 'Model did not return usable blueprints (missing tool output). Check ANTHROPIC_API_KEY and model availability.' - send({ status: 'failed', error: parseHint }) - await updateAnalysisStatus(id, 'failed', { error_message: parseHint }) - controller.close() - return - } + if (blueprintResults.length === 0) { + send({ provider_status: { provider, state: 'empty' } }) + continue + } + + const rankedBlueprints = blueprintResults + .map((bp) => normalizeBlueprint(bp)) + .sort((a, b) => getOpportunityScore(b) - getOpportunityScore(a)) + + for (const bp of rankedBlueprints) { + await createBlueprint({ + analysis_id: id, + name: bp.name, + description: bp.description, + app_type: bp.app_type, + complexity: bp.complexity, + reuse_percentage: bp.reuse_percentage, + existing_files: bp.existing_files, + missing_files: bp.missing_files, + estimated_effort: getEffortEstimate(bp.complexity, bp.missing_files.length), + technologies: bp.technologies, + ai_explanation: bp.explanation, + ai_provider: provider, + }) + } - // Save blueprints to database - if (output?.blueprints) { - const rankedBlueprints = output.blueprints - .map((bp) => normalizeBlueprint(bp)) - .sort((a, b) => getOpportunityScore(b) - getOpportunityScore(a)) - - for (const bp of rankedBlueprints) { - await createBlueprint({ - analysis_id: id, - name: bp.name, - description: bp.description, - app_type: bp.app_type, - complexity: bp.complexity, - reuse_percentage: bp.reuse_percentage, - existing_files: bp.existing_files, - missing_files: bp.missing_files, - estimated_effort: getEffortEstimate(bp.complexity, bp.missing_files.length), - technologies: bp.technologies, - ai_explanation: bp.explanation, + anySucceeded = true + send({ provider_status: { provider, state: 'complete', count: rankedBlueprints.length } }) + } catch (providerError) { + console.error(`[analysis] ${provider} failed:`, providerError) + send({ + provider_status: { + provider, + state: 'failed', + error: providerError instanceof Error ? providerError.message : 'Provider failed', + }, }) + if (!isMultiProvider) { + const msg = providerError instanceof Error ? providerError.message : 'AI analysis failed' + send({ status: 'failed', error: msg }) + await updateAnalysisStatus(id, 'failed', { error_message: msg }) + controller.close() + return + } } } - // Update to complete - await updateAnalysisStatus(id, 'complete', { analyzed_files: allFiles.length }) + if (!anySucceeded) { + const msg = 'All AI providers failed to produce blueprints.' + send({ status: 'failed', error: msg }) + await updateAnalysisStatus(id, 'failed', { error_message: msg }) + controller.close() + return + } - // Get final blueprints + await updateAnalysisStatus(id, 'complete', { analyzed_files: allFiles.length }) const finalBlueprints = await getBlueprintsByAnalysis(id) - send({ status: 'complete', progress: 100, blueprints: finalBlueprints }) + send({ + status: 'complete', + progress: 100, + blueprints: finalBlueprints, + providers_used: requestedProviders, + }) controller.close() } catch (error) { diff --git a/app/api/setup/init-db/route.ts b/app/api/setup/init-db/route.ts index 486a263..ab0b230 100644 --- a/app/api/setup/init-db/route.ts +++ b/app/api/setup/init-db/route.ts @@ -103,10 +103,13 @@ async function run() { estimated_effort VARCHAR(100), technologies JSONB DEFAULT '[]', ai_explanation TEXT, + ai_provider VARCHAR(50) DEFAULT 'anthropic', created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ) ` + await sql`ALTER TABLE app_blueprints ADD COLUMN IF NOT EXISTS ai_provider VARCHAR(50) DEFAULT 'anthropic'` + await sql`CREATE INDEX IF NOT EXISTS idx_user_auth_github_id ON user_auth(github_id)` await sql`CREATE INDEX IF NOT EXISTS idx_repositories_github_id ON repositories(github_id)` await sql`CREATE INDEX IF NOT EXISTS idx_repositories_full_name ON repositories(full_name)` diff --git a/app/dashboard/layout.tsx b/app/dashboard/layout.tsx index 26899f7..8e200a0 100644 --- a/app/dashboard/layout.tsx +++ b/app/dashboard/layout.tsx @@ -16,7 +16,7 @@ export default async function DashboardLayout({ return (
Discover what apps you can build from your existing code.
+Your code intelligence at a glance.
Repositories
-{repositories.length}
+Repositories
+{repositories.length}
+Analyses
-{analyses.length}
+Analyses
+{analyses.length}
+Apps Discovered
-{completedAnalyses.length > 0 ? '—' : '0'}
+Completed
+{completedAnalyses.length}
+
- Start by adding your GitHub repositories. We will scan all files and prepare them for AI analysis.
+ {repositories.length === 0 ? (
+
+ Connect your GitHub repositories and let AI discover the products hiding in your code.
+ Get started with CodeVault
+
+ Manage your connected GitHub repositories.
-- Manage your connected GitHub repositories. -
-- Let AI discover what apps you can build. -
-+ Discover what apps you can build from your code. +
+{repo.full_name}
{repo.language && ( - + {repo.language} )} diff --git a/app/globals.css b/app/globals.css index c8483d8..50443b3 100644 --- a/app/globals.css +++ b/app/globals.css @@ -4,74 +4,74 @@ @custom-variant dark (&:is(.dark *)); :root { - --background: oklch(0.95 0 0); - --foreground: oklch(0.15 0 0); + --background: oklch(0.97 0.002 240); + --foreground: oklch(0.13 0.02 260); --card: oklch(1 0 0); - --card-foreground: oklch(0.15 0 0); + --card-foreground: oklch(0.13 0.02 260); --popover: oklch(1 0 0); - --popover-foreground: oklch(0.15 0 0); - --primary: oklch(0.52 0.194 258); + --popover-foreground: oklch(0.13 0.02 260); + --primary: oklch(0.55 0.17 155); --primary-foreground: oklch(1 0 0); - --secondary: oklch(0.95 0 0); - --secondary-foreground: oklch(0.15 0 0); - --muted: oklch(0.88 0 0); - --muted-foreground: oklch(0.45 0 0); - --accent: oklch(0.52 0.194 258); + --secondary: oklch(0.95 0.005 240); + --secondary-foreground: oklch(0.2 0.02 260); + --muted: oklch(0.93 0.005 240); + --muted-foreground: oklch(0.46 0.015 260); + --accent: oklch(0.55 0.17 155); --accent-foreground: oklch(1 0 0); --destructive: oklch(0.577 0.245 27.325); --destructive-foreground: oklch(1 0 0); - --border: oklch(0.9 0 0); - --input: oklch(0.95 0 0); - --ring: oklch(0.52 0.194 258); - --chart-1: oklch(0.52 0.194 258); - --chart-2: oklch(0.65 0.2 130); - --chart-3: oklch(0.58 0.19 42); - --chart-4: oklch(0.71 0.18 180); - --chart-5: oklch(0.68 0.19 300); + --border: oklch(0.91 0.005 240); + --input: oklch(0.93 0.005 240); + --ring: oklch(0.55 0.17 155); + --chart-1: oklch(0.55 0.17 155); + --chart-2: oklch(0.65 0.14 200); + --chart-3: oklch(0.58 0.16 280); + --chart-4: oklch(0.68 0.15 45); + --chart-5: oklch(0.6 0.16 320); --radius: 0.625rem; - --sidebar: oklch(0.95 0 0); - --sidebar-foreground: oklch(0.15 0 0); - --sidebar-primary: oklch(0.52 0.194 258); + --sidebar: oklch(0.97 0.002 240); + --sidebar-foreground: oklch(0.13 0.02 260); + --sidebar-primary: oklch(0.55 0.17 155); --sidebar-primary-foreground: oklch(1 0 0); - --sidebar-accent: oklch(0.88 0 0); - --sidebar-accent-foreground: oklch(0.15 0 0); - --sidebar-border: oklch(0.9 0 0); - --sidebar-ring: oklch(0.52 0.194 258); + --sidebar-accent: oklch(0.93 0.005 240); + --sidebar-accent-foreground: oklch(0.13 0.02 260); + --sidebar-border: oklch(0.91 0.005 240); + --sidebar-ring: oklch(0.55 0.17 155); } .dark { - --background: oklch(0.08 0 0); - --foreground: oklch(0.98 0 0); - --card: oklch(0.12 0 0); - --card-foreground: oklch(0.98 0 0); - --popover: oklch(0.12 0 0); - --popover-foreground: oklch(0.98 0 0); - --primary: oklch(0.98 0 0); - --primary-foreground: oklch(0.08 0 0); - --secondary: oklch(0.18 0 0); - --secondary-foreground: oklch(0.98 0 0); - --muted: oklch(0.18 0 0); - --muted-foreground: oklch(0.65 0 0); - --accent: oklch(0.18 0 0); - --accent-foreground: oklch(0.98 0 0); + --background: oklch(0.09 0.015 260); + --foreground: oklch(0.96 0.005 240); + --card: oklch(0.13 0.015 260); + --card-foreground: oklch(0.96 0.005 240); + --popover: oklch(0.13 0.015 260); + --popover-foreground: oklch(0.96 0.005 240); + --primary: oklch(0.7 0.19 155); + --primary-foreground: oklch(0.09 0.015 260); + --secondary: oklch(0.17 0.015 260); + --secondary-foreground: oklch(0.96 0.005 240); + --muted: oklch(0.17 0.015 260); + --muted-foreground: oklch(0.6 0.01 260); + --accent: oklch(0.17 0.015 260); + --accent-foreground: oklch(0.96 0.005 240); --destructive: oklch(0.55 0.2 25); - --destructive-foreground: oklch(0.98 0 0); - --border: oklch(0.22 0 0); - --input: oklch(0.18 0 0); - --ring: oklch(0.98 0 0); - --chart-1: oklch(0.7 0.15 150); - --chart-2: oklch(0.65 0.15 200); + --destructive-foreground: oklch(0.96 0.005 240); + --border: oklch(0.2 0.012 260); + --input: oklch(0.17 0.015 260); + --ring: oklch(0.7 0.19 155); + --chart-1: oklch(0.7 0.19 155); + --chart-2: oklch(0.65 0.14 200); --chart-3: oklch(0.6 0.15 280); - --chart-4: oklch(0.75 0.12 50); - --chart-5: oklch(0.7 0.15 320); - --sidebar: oklch(0.1 0 0); - --sidebar-foreground: oklch(0.98 0 0); - --sidebar-primary: oklch(0.98 0 0); - --sidebar-primary-foreground: oklch(0.08 0 0); - --sidebar-accent: oklch(0.18 0 0); - --sidebar-accent-foreground: oklch(0.98 0 0); - --sidebar-border: oklch(0.22 0 0); - --sidebar-ring: oklch(0.98 0 0); + --chart-4: oklch(0.72 0.14 50); + --chart-5: oklch(0.65 0.15 320); + --sidebar: oklch(0.11 0.015 260); + --sidebar-foreground: oklch(0.96 0.005 240); + --sidebar-primary: oklch(0.7 0.19 155); + --sidebar-primary-foreground: oklch(0.09 0.015 260); + --sidebar-accent: oklch(0.17 0.015 260); + --sidebar-accent-foreground: oklch(0.96 0.005 240); + --sidebar-border: oklch(0.2 0.012 260); + --sidebar-ring: oklch(0.7 0.19 155); } @theme inline { diff --git a/app/page.tsx b/app/page.tsx index fde5c59..a3cb7ce 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,6 +1,6 @@ import Link from 'next/link' import { Button } from '@/components/ui/button' -import { Github, Sparkles, Code2, Layers, ArrowRight, AlertCircle } from 'lucide-react' +import { Github, Sparkles, Layers, ArrowRight, AlertCircle, Rocket, ChevronRight, Eye, Puzzle, Zap } from 'lucide-react' const ERROR_MESSAGES: Record- Teams use CodeVault to surface products they have already mostly built — scattered across repos — - then ship them as one coherent app. Connect GitHub and turn existing files into concrete blueprints. -
- -- Read-only GitHub access. Your code stays yours — we analyze structure and patterns to suggest combinations, not to store your source. -
-- Built for builders who already have the hard parts -
-12k+
-repos scanned
-4.1k
-blueprints surfaced
-38
-stacks detected
-New
-cross-repo fusion engine
-+ CodeVault scans your GitHub repositories and finds complete applications hiding in your existing code. + Stop rebuilding what you already have. +
+ ++ Read-only access. We analyze structure and patterns — your source code is never stored. +
- Link your GitHub repos and we will scan all files to understand your codebase structure. -
12k+
+Repos scanned
+4.1k
+Apps discovered
+73%
+Avg. code reuse
+38
+Stacks supported
+- Our AI analyzes each file to identify its purpose, exports, and reusability potential. -
+ Most teams have already built 60-80% of their next product — it's just spread across different repositories. We find it. +
- Get detailed blueprints showing what apps you can build and what files you need to add. -
-- Enter your GitHub repository URLs or connect via OAuth to import multiple repos at once. +
+ Connect your repositories and our AI maps every component, utility, hook, and API across your entire codebase.
+ AI cross-references your files and surfaces complete product blueprints — showing exactly what you can ship today. +
- Our AI examines every file - components, utilities, hooks, APIs - identifying what each piece does and how reusable it is. + +
+ Get a build plan with reusable files, missing pieces to generate, and estimated effort. Go from blueprint to product fast.
+ Connect, scan, ship. CodeVault handles the heavy lifting. +
++ Sign in with GitHub and import your repositories. We scan with read-only access — nothing is modified. +
++ Our AI examines every file — components, utilities, APIs — and identifies reusable building blocks across all your repos. +
++ Receive detailed app blueprints showing what to reuse, what's missing, and a build plan ranked by ship-readiness. +
+- See a list of applications you can build using your existing code. Each blueprint shows which files to reuse and what few extras you might need. -
++ Connect your GitHub and discover the products you've already (mostly) built. Your first analysis is on us. +
+