This week I shipped Forge AI -- a web app that optimizes your AI prompts and lets you compare responses from multiple models side-by-side. I also built a Chrome extension that puts a one-click optimize button directly on ChatGPT, Gemini, and Claude. Both are live in production right now.
This post is about how it went from idea to deployed product, what the stack looks like, and what I learned along the way.
The Problem
If you use AI tools regularly, you've probably noticed that the quality of the response depends heavily on how you write the prompt. A vague prompt gets a vague answer. A structured prompt with context, constraints, and clear intent gets something you can actually use.
But most people don't want to learn prompt engineering. They just want to type what they're thinking and get a good result. That's the gap Forge AI fills -- you paste your rough prompt, and it rewrites it for you.
The second problem is model selection. GPT-4o, Gemini, and Claude all have different strengths. For any given prompt, one model might nail it while another gives you generic filler. But switching between tabs to compare is tedious. So I built a comparison view that sends the same prompt to all three and streams the responses side-by-side.
The Stack
I wanted something that would be fast to build, cheap to run, and scale without me managing servers. Here's what I landed on:
- Frontend: React 19 + Tailwind CSS v4, built with Vite, hosted on Cloudflare Pages
- Backend: Hono framework on Cloudflare Workers -- serverless API at the edge
- Database: Cloudflare D1 (SQLite at the edge)
- Auth: Email/password with JWT + PBKDF2 hashing, refresh token rotation
- AI: Google Gemini 2.5 Flash for optimization, Gemini + Claude for comparison
- Extension: Chrome Manifest V3, built with Vite
The entire thing runs on Cloudflare's free tier. No AWS bill, no server maintenance, no cold starts. The API responds from the nearest edge node, and the database is SQLite so there's nothing to provision.
How the Landing Page Loads Instantly
One thing I'm proud of is the landing page performance. Most React apps make you download the entire JavaScript bundle before you see anything -- even if the page is just static marketing content.
I solved this by embedding the landing page as plain HTML directly in index.html with inlined CSS. When a visitor hits the site, they see the full landing page immediately -- zero JavaScript required. React then loads in the background, mounts into a separate div, and takes over routing. A CSS rule hides the static content once React is ready.
The result: the landing page paints in under 100ms from Cloudflare's edge. The React bundle (283KB gzipped) only loads when you actually click through to sign up or log in.
The Chrome Extension
The extension injects a small button next to the prompt input on ChatGPT, Gemini, and Claude. When you click it, it grabs your typed prompt, sends it to the Forge AI API, and shows the optimized version in an overlay. You can use it with one click or copy it to clipboard.
The tricky part was making it work with different input mechanisms. ChatGPT uses a contenteditable div with React's synthetic event system. Gemini uses Quill editor. Claude uses ProseMirror. Each one needs a different approach to programmatically set text without breaking the framework's state tracking.
chrome.storage.local (sandboxed per-extension) and proxies all API calls through a background service worker. Your API keys never touch the content script or the host page.
Security Audit
Before shipping, I ran a full security review across the entire codebase. Some things I caught and fixed:
- SQL helper functions that interpolated table names directly into queries -- added an allowlist of valid tables
- Upstream API error messages from Gemini and Anthropic were being forwarded to the client, potentially leaking rate limit info. Switched to generic error messages.
- The compare endpoint used raw
fetch()instead of the app'sapiFetch()wrapper, so it bypassed automatic token refresh on 401. Fixed it to retry with a fresh token. - When tokens expired, the Zustand auth store kept the user object in state, creating "zombie sessions" where the user saw errors but was never redirected to login. Wired up a callback to clear state on token expiry.
What's Next
The Chrome extension is currently in review with the Chrome Web Store. Once approved, the full loop works: users discover the extension, install it, sign up on the web app, and use both together.
After that, the roadmap is:
- Stripe integration for a paid tier (more optimizations per day, priority model access)
- Prompt templates and community sharing
- OpenAI GPT-4o integration (API key not yet configured)
If you want to try it, head to forge.pinchelee.com. It's free to start.