From 054ce847661461c99d692be5f58c4d1f58719345 Mon Sep 17 00:00:00 2001 From: Richard Abrich Date: Wed, 18 Mar 2026 21:18:28 -0400 Subject: [PATCH 1/5] feat: add productization plan and web UI scaffold Add comprehensive productization document covering market analysis, pricing model (Free/Pro/Team tiers), MVP roadmap (6 weeks), technical architecture evolution, go-to-market strategy, and unit economics. Create Next.js web app scaffold at apps/web/ with: - Landing page with features, pricing, and language support sections - Task submission form connected to Supabase via server actions - Job detail page scaffold for real-time status tracking - Supabase client helpers (server + browser) - Tailwind CSS styling with custom Wright color palette Also update turbo.json for .next/ build outputs and .gitignore for Next.js artifacts. Co-Authored-By: Claude Opus 4.6 (1M context) --- .gitignore | 5 + apps/web/.env.example | 7 + apps/web/next.config.mjs | 6 + apps/web/package.json | 30 ++ apps/web/postcss.config.mjs | 9 + apps/web/src/app/globals.css | 25 ++ apps/web/src/app/jobs/[id]/page.tsx | 70 +++++ apps/web/src/app/layout.tsx | 20 ++ apps/web/src/app/new/page.tsx | 236 ++++++++++++++ apps/web/src/app/page.tsx | 458 ++++++++++++++++++++++++++++ apps/web/src/lib/actions.ts | 88 ++++++ apps/web/src/lib/supabase.ts | 40 +++ apps/web/tailwind.config.ts | 29 ++ apps/web/tsconfig.json | 27 ++ docs/PRODUCTIZATION.md | 395 ++++++++++++++++++++++++ turbo.json | 2 +- 16 files changed, 1446 insertions(+), 1 deletion(-) create mode 100644 apps/web/.env.example create mode 100644 apps/web/next.config.mjs create mode 100644 apps/web/package.json create mode 100644 apps/web/postcss.config.mjs create mode 100644 apps/web/src/app/globals.css create mode 100644 apps/web/src/app/jobs/[id]/page.tsx create mode 100644 apps/web/src/app/layout.tsx create mode 100644 apps/web/src/app/new/page.tsx create mode 100644 apps/web/src/app/page.tsx create mode 100644 apps/web/src/lib/actions.ts create mode 100644 apps/web/src/lib/supabase.ts create mode 100644 apps/web/tailwind.config.ts create mode 100644 apps/web/tsconfig.json create mode 100644 docs/PRODUCTIZATION.md diff --git a/.gitignore b/.gitignore index 3eabc76..d3bebc3 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,8 @@ pnpm-debug.log* # Supabase supabase/.temp/ + +# Next.js +.next/ +out/ +next-env.d.ts diff --git a/apps/web/.env.example b/apps/web/.env.example new file mode 100644 index 0000000..67bd392 --- /dev/null +++ b/apps/web/.env.example @@ -0,0 +1,7 @@ +# Supabase +NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co +NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key +SUPABASE_SERVICE_ROLE_KEY=your-service-role-key + +# GitHub (temporary -- will be replaced by GitHub App) +GITHUB_TOKEN=ghp_your-token diff --git a/apps/web/next.config.mjs b/apps/web/next.config.mjs new file mode 100644 index 0000000..81509a0 --- /dev/null +++ b/apps/web/next.config.mjs @@ -0,0 +1,6 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + transpilePackages: ['@wright/shared'], +} + +export default nextConfig diff --git a/apps/web/package.json b/apps/web/package.json new file mode 100644 index 0000000..12b4e05 --- /dev/null +++ b/apps/web/package.json @@ -0,0 +1,30 @@ +{ + "name": "@wright/web", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "dev": "next dev --port 3000", + "build": "next build", + "start": "next start", + "lint": "next lint", + "clean": "rm -rf .next .turbo" + }, + "dependencies": { + "@supabase/supabase-js": "^2.49.0", + "@supabase/ssr": "^0.6.0", + "@wright/shared": "workspace:*", + "next": "^14.2.0", + "react": "^18.3.0", + "react-dom": "^18.3.0" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "@types/react": "^18.3.0", + "@types/react-dom": "^18.3.0", + "autoprefixer": "^10.4.0", + "postcss": "^8.4.0", + "tailwindcss": "^3.4.0", + "typescript": "^5.7.0" + } +} diff --git a/apps/web/postcss.config.mjs b/apps/web/postcss.config.mjs new file mode 100644 index 0000000..d0c615b --- /dev/null +++ b/apps/web/postcss.config.mjs @@ -0,0 +1,9 @@ +/** @type {import('postcss-load-config').Config} */ +const config = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} + +export default config diff --git a/apps/web/src/app/globals.css b/apps/web/src/app/globals.css new file mode 100644 index 0000000..30845d7 --- /dev/null +++ b/apps/web/src/app/globals.css @@ -0,0 +1,25 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --foreground-rgb: 15, 23, 42; + --background-start-rgb: 248, 250, 252; + --background-end-rgb: 241, 245, 249; +} + +body { + color: rgb(var(--foreground-rgb)); + background: linear-gradient( + to bottom, + transparent, + rgb(var(--background-end-rgb)) + ) + rgb(var(--background-start-rgb)); +} + +@layer utilities { + .text-balance { + text-wrap: balance; + } +} diff --git a/apps/web/src/app/jobs/[id]/page.tsx b/apps/web/src/app/jobs/[id]/page.tsx new file mode 100644 index 0000000..e044550 --- /dev/null +++ b/apps/web/src/app/jobs/[id]/page.tsx @@ -0,0 +1,70 @@ +import Link from 'next/link' + +/** + * Job detail page -- shows status, events timeline, and test results. + * + * This is a scaffold. The full implementation will: + * - Fetch job details from Supabase + * - Subscribe to real-time job_events updates + * - Show test result diffs between loops + * - Link to the created PR + */ +export default function JobDetailPage({ + params, +}: { + params: { id: string } +}) { + return ( +
+ {/* Navigation */} + + +
+
+

Job Details

+ + {params.id} + +
+ + {/* Placeholder for job details */} +
+
+

+ This page will show real-time job status, event timeline, and test + results once connected to Supabase. +

+
+

+ Coming Soon +

+
    +
  • Real-time status updates via Supabase Realtime
  • +
  • Event timeline (claimed, cloning, editing, testing...)
  • +
  • Test results per loop iteration
  • +
  • Cost tracking (API spend per loop)
  • +
  • Link to created PR
  • +
  • Cancel / retry controls
  • +
+
+
+
+
+
+ ) +} diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx new file mode 100644 index 0000000..d21b271 --- /dev/null +++ b/apps/web/src/app/layout.tsx @@ -0,0 +1,20 @@ +import type { Metadata } from 'next' +import './globals.css' + +export const metadata: Metadata = { + title: 'Wright — AI Dev Automation', + description: + 'Submit a task, get a pull request. Wright uses Claude to edit code, run tests, and create PRs automatically.', +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} diff --git a/apps/web/src/app/new/page.tsx b/apps/web/src/app/new/page.tsx new file mode 100644 index 0000000..807502c --- /dev/null +++ b/apps/web/src/app/new/page.tsx @@ -0,0 +1,236 @@ +'use client' + +import { useState } from 'react' +import Link from 'next/link' +import { submitTask } from '@/lib/actions' + +export default function NewTaskPage() { + const [repoUrl, setRepoUrl] = useState('') + const [task, setTask] = useState('') + const [branch, setBranch] = useState('main') + const [maxLoops, setMaxLoops] = useState(10) + const [maxBudget, setMaxBudget] = useState(5.0) + const [submitting, setSubmitting] = useState(false) + const [result, setResult] = useState<{ + success: boolean + jobId?: string + error?: string + } | null>(null) + + async function handleSubmit(e: React.FormEvent) { + e.preventDefault() + setSubmitting(true) + setResult(null) + + try { + const res = await submitTask({ + repoUrl, + task, + branch, + maxLoops, + maxBudgetUsd: maxBudget, + }) + setResult(res) + } catch (err) { + setResult({ + success: false, + error: err instanceof Error ? err.message : 'Unknown error', + }) + } finally { + setSubmitting(false) + } + } + + return ( +
+ {/* Navigation */} + + +
+

Submit a Task

+

+ Describe what you need done. Wright will clone the repo, make changes, + run tests, and create a PR. +

+ +
+ {/* Repository URL */} +
+ + setRepoUrl(e.target.value)} + className="mt-1 block w-full rounded-lg border border-slate-300 px-4 py-2.5 text-slate-900 placeholder-slate-400 focus:border-wright-500 focus:outline-none focus:ring-1 focus:ring-wright-500" + /> +

+ The GitHub repository to work on. Must be accessible with your + GitHub credentials. +

+
+ + {/* Task Description */} +
+ +