case 01 2026 · Shipped · v2 in progress
AI Prompt Optimizer
Most people are one rewrite away from a better answer.
01 , The problem
Watch anyone use ChatGPT for ten minutes and you'll see the same loop: vague question, mediocre answer, frustrated follow-up, slightly better answer, repeat. People blame the model. Most of the time the prompt was the problem.
Power users fix this by iterating , three, four, five turns to converge on what they actually meant. That works, but it's slow, and it assumes you already know what a good prompt looks like. Most people don't, and no chat interface teaches them.
The fix belongs before the model sees the prompt, inside the interface people already use. Not a separate tool you copy-paste into , that breaks flow, and broken flow means nobody uses it twice.
02 , Research & framing
I started by collecting the prompts I and people around me actually wrote. The failure patterns were consistent: missing context, no target format, no audience, buried intent. Fixable, mechanically, almost every time.
Existing tools were either prompt libraries (static, generic) or standalone rewriter sites (copy-paste friction). Nothing lived at the textarea, at the moment of typing, across the tools people actually switch between.
So the constraint set became: inject into ChatGPT, Claude, Gemini, Grok and Perplexity; survive their hostile, ever-changing DOMs; keep the API key off the client; return something richer than a rewrite , a score, and warnings about what the prompt is still missing.
03 , Architecture
A content script per platform finds the prompt box and mounts the UI inside a Shadow DOM, so five wildly different host stylesheets can't touch it. The click goes through a background service worker to a FastAPI backend on Render, which owns the Gemini call and returns one structured JSON object: the optimized prompt, a quality score, and specific warnings.
The extension never talks to Gemini directly. The backend is the single place where prompt analysis, scoring and rate limiting live , which means the intelligence can improve without shipping a new extension version.
04 , Engineering decisions
D1 , Shadow DOM over inline styles
Mount the entire UI inside a shadow root on every host page.
Because
ChatGPT, Claude and Perplexity all ship aggressive global CSS. Inline styles survived one host and broke on the next. A shadow boundary made the UI identical across all five.
The trade
Event handling and focus management get harder across the boundary. Worth it , visual consistency is the product.
D2 , Server-side Gemini calls
All model calls go through the FastAPI backend, never the client.
Because
Shipping an API key inside an extension is shipping it to everyone. Centralizing calls also gave me one place for scoring logic, rate limits and prompt updates.
The trade
An extra network hop, and Render's free tier cold-starts. I masked it with optimistic UI; v2 keeps the backend warm.
D3 , A structured JSON contract, not a text rewrite
The backend returns { optimized_prompt, quality_score, warnings[] } , always.
Because
Free-text responses made the UI unpredictable. A contract made rendering deterministic and turned 'rewriting' into 'analysis': the warnings teach users what was missing, which is the actual point.
The trade
LLMs occasionally break JSON. I had to build a validate-repair-retry layer instead of trusting the model.
D4 , Per-site adapters, not generic heuristics
Each platform gets a small adapter that knows its DOM; a MutationObserver re-mounts when the SPA re-renders.
Because
One 'universal' selector strategy kept silently failing as sites shipped updates. Explicit adapters fail loudly and are five small files instead of one clever one.
The trade
New platform support is manual work. Acceptable at five targets; the roadmap has a config-driven version.
05 , What fought back
The DOM is a moving target
Chat UIs are React apps that re-render constantly and A/B test their markup. The first version broke within days when ChatGPT changed its composer. The fix wasn't smarter selectors , it was accepting churn as a design constraint: observers, re-mount logic, and adapters that degrade gracefully instead of erroring.
Cross-origin everything
Content scripts, service workers, a backend on another origin , every layer added a CORS or messaging boundary. I learned more about the browser security model from this one project than from any course, mostly by violating it and reading the errors.
Optimizing the optimizer
The meta-problem: my backend's own system prompt determines rewrite quality. I iterated on it the same way the extension teaches users to , context, format, constraints , and kept a small test set of bad prompts to check for regressions by hand. Building a proper eval harness is the top of the roadmap.
06 , The numbers, honestly
5
AI platforms, one adapter each
0
API keys shipped to the client
1
JSON contract the whole product hangs off
MV3
service-worker era, no persistent background page
No install counts or DAU charts here , it isn't on the Web Store yet, and I'd rather show you the architecture than a vanity number.
07 , One honest snippet
class OptimizeResponse(BaseModel):
optimized_prompt: str
quality_score: int # 0-100, scored before and after
warnings: list[str] # what the prompt is still missing
reasoning: str # why these changes, shown on demand
@app.post("/optimize", response_model=OptimizeResponse)
async def optimize(req: OptimizeRequest) -> OptimizeResponse:
raw = await gemini_rewrite(req.prompt, platform=req.platform)
return validate_or_repair(raw) # never trust model JSON
A fixed response model means the extension UI is deterministic , and the warnings array is where the product teaches instead of just rewriting.
08 , What it taught me
- 01
The browser is a hostile runtime. Design for the page changing underneath you, because it will.
- 02
The API contract is the product. Once the JSON schema was right, everything downstream got simple.
- 03
Distribution is engineering too. A tool people have to load unpacked is a tool almost nobody uses , the store listing isn't an afterthought, it's the next milestone.
09 , Where it goes next
- Chrome Web Store release
- An eval harness for rewrite quality , measured, not vibes
- Per-platform prompt profiles (what works on Claude ≠ what works on Perplexity)
- Local prompt history with diffs
- Bring-your-own-key mode
Next case , /02