a chrome extension for people whose coding agent just shipped the feature

point at what’s h1 > spanbroken. say what’s wrong.

Gripe turns clicking through a half-broken UI into a folder of markdown + screenshots your coding agent can read. Hotkey, click the thing, talk — the note saves itself, with the CSS selector, the screenshots, and whatever the console threw at that exact moment.

no enter key. you stop talking, the bar drains, the note commits.

Your agent just built the feature. You’re clicking through it and a dozen things are subtly off — this button does nothing the second time, that column drifts, this list should be sorted. Typing all that into a chat window is slow and lossy, and you’ve forgotten the first one by the time you hit the third.

gripe turns it into a stream of consciousness →

01 · the output

a folder in your repo, not a chat scroll

Every session writes straight into the repo you connect — markdown built for a model to read, screenshots with the target spotlighted, and a close-up crop cut from the clean capture.

your-app/

└─ gripes/
   └─ 2026-07-25-1432-checkout-flow/
      ├─ report.md        ← hand this to your agent
      ├─ notes.json       ← same thing, machine-readable
      ├─ 01-full.png      ← viewport, target spotlighted
      ├─ 01-target.png    ← close-up crop, upscaled
      └─ 02-full.png

the sleeper feature

A 90-line tap on the page’s own console.error, fetch and XHR rides along with every note. You point at a button; the report already contains the TypeError that broke it.

Then paste the line Gripe put on your clipboard:

Read gripes/2026-07-25-1432-checkout-flow/report.md — it’s a bug report I recorded while clicking through the running app, with screenshots. Look at every image, then fix what’s described.

gripes/2026-07-25-1432-checkout-flow/report.md
## 1. Place order does nothing on the second click - **URL** `http://localhost:3000/checkout`- **Time** 14:32:11- **Element** `#submit`- **Element text** "Place order"- **Attributes** `data-testid="place-order"` `type="submit"`- **Box** 412,680 · 120×40- **Viewport** 1512×860 @2x · scrolled to 0,240 ![Note 1 — full viewport](01-full.png)![Note 1 — target close-up](01-target.png) <button id="submit" class="btn btn-primary">Place order</button> <details><summary>Console / network at capture time (2)</summary> [error] TypeError: Cannot read properties of undefined (reading 'id')        at Checkout.tsx:44[network] 500 Internal Server Error — /api/orders </details>

written for a model to read, not a human to skim

02 · video walkthroughs

your agent can’t watch video. record anyway.

Hit record and just use the app, narrating as you go. Twice a second Gripe fingerprints the screen and throws away every frame where nothing changed — the screenshots take themselves, only at the moments that matter. Your words are stamped onto the timeline where you said them, console and network errors land where they fired, and when you stop, Whisper re-transcribes the audio on your machine and quietly fixes the rough draft. Watch it work:

an hour of poking around distills to ~150 screenshots, a stamped transcript, and one report.md

60 seconds · rendered with remotion

gripes/2026-07-26-1104-walkthrough/
├─ report.md          ← the timeline: what you said over what changed
├─ transcript.txt     ← every word, timestamped
├─ recording.json     ← machine-readable
├─ frames/            ← the deduped keyframes
├─ grids/             ← 3×3 contact sheets
└─ walkthrough.webm   ← the raw video, if anyone's nostalgic

03 · controls

you don’t reach for the keyboard

Dictation starts on its own and survives your pauses — Chrome kills recognition on silence; Gripe restarts it under you. When you stop talking, a bar drains across the composer and the note commits. Keep talking, or touch a key, and it calls it off.

Four ways to point: element hover-highlights DOM nodes and records the selector — clicking a button’s inner label gets you the button, not the throwaway span. Region drags a box. Draw scribbles arrows on the page. Page is just a note about what you’re looking at.

AltShiftBarm the picker
AltShiftNquick note about the whole page
ERDPelement / region / draw / page
“save it”commit the note by voice
Entercommit it by hand
CtrlSpacetoggle the mic
Alt+ clicktake the literal element, no climbing
Escbail out

the panel shows the shortcut chrome actually bound on your machine — click it to rebind

04 · under the hood

nothing leaves your machine

No accounts, no servers, no telemetry. The File System Access API writes straight into the repo folder you pick, Whisper runs in a worker next to your tabs, and the only runtime dependency is React — the ZIP writer, the PNG icon generator, and the screenshot compositor are all hand-rolled.

content script ──┬─ shadow-DOM overlay: picker, marquee, ink canvas, composer
                 ├─ dictation, restarted through silences
                 └─ composites the capture: spotlight + box + strokes + crop
        │  chrome.runtime
        ▼
background SW ───┬─ tab capture (background-only API)
                 └─ IndexedDB: sessions, notes, image blobs
        │
        ▼
side panel ──────┬─ React view over that state
                 └─ File System Access: writes straight into your repo

05 · install

two minutes, no store

  1. 1clone and build — npm install && npm run build
  2. 2chrome://extensions → developer mode → load unpacked → pick dist/
  3. 3click the icon, hit Connect folder, choose your repo — Chrome remembers it across restarts
dested/gripe on GitHubMIT · no runtime deps but react

known edges

  • won’t run on chrome:// pages, the web store, or other extensions — chrome forbids it
  • dictation asks for mic permission per site; grant it once on your dev origin
  • screenshots are the visible viewport — devtools aren’t in frame
  • gripes/ is yours to gitignore or commit. nothing else in the folder you connect is ever touched