Your companion through long PRs: it carries the review when the diff is 900 lines of AI-written code.
A Plasmo (React + TypeScript) extension. Adds a categorized view (Core changes, Cosmetic only, Database & migrations, Config/CI, Tests, Docs, AI tooling, Generated & lockfiles) to the GitHub PR file tree, with a “X% of changed lines are core” stat, auto-collapsed padding files, folded comment-only hunks, and a keyboard-driven guided “review story” that walks the core files, skips the ones you have already marked viewed, and marks each as you go.
npm install
npm run dev # build with HMR into build/chrome-mv3-dev
npm run build # production build into build/chrome-mv3-prod
npm run package # zip the production build
npm test # vitest: categorize + content-controller behavior (jsdom)
npm run typecheck
Install from the Chrome Web Store,
or from source: chrome://extensions → Developer mode → Load unpacked →
build/chrome-mv3-dev (or -prod after npm run build). To update a source build,
rerun the build, then hit reload on the Pepper card.
Optional keys live in the toolbar popup (pin it from the puzzle-piece menu): TypeSafe (Jev reclassifies core/config files) and OpenAI (per-file summaries, PR TL;DR + glossary, reading order; cached by prompt + diff). Without keys the extension is a fully local, deterministic categorizer.
src/
categorize.ts diff parser: .diff -> per-file records with a category
categorize.test.ts categorizer/parser tests
rules/ category RULES AS DATA (this is where contributors work)
types.ts CategoryId, Category, Ruleset types
default.ts the baseline ruleset (web/TypeScript + Elixir/Phoenix)
index.ts ruleset registry + merged category list + display order
rules.test.ts invariants that keep ruleset PRs safe
background.ts service worker: .diff fetch, Jev classify, OpenAI summarize/TL;DR
panel.tsx React panel: groups, summary stat, TL;DR box, story button
popup.tsx popup: default view + API keys
contents/github-pr.ts content script: mount loop, collapse/fold, story mode (test hooks exported)
contents/github-pr.css panel/story styles
contents/github-pr.test.ts behavior tests against fake GitHub DOM (both diff UIs)
scripts/
test-jev.mjs live API-contract check for the Jev classifier (key passed per call)
make-logo.mjs regenerates assets/logo.svg; icon*.png are sips renders of it
npm test ports the original suites: category rules, comment-only detection, doc-heredoc
folding, and the full content-controller behavior (toggle persistence, padding collapse,
folding, viewed marking, story ordering, keyboard shortcuts) against fixtures for both the
React and classic GitHub diff UIs.
PEPPER_DIFF=/path/to/file.diff npm test prints the category breakdown for a real diff.
JEV_KEY=... node scripts/test-jev.mjs checks the live Jev endpoint against sample diffs
(core/cosmetic/migrations/config/docs) — it mirrors the request in src/background.ts,
so use it to verify the API contract after any change. The key is passed per call; nothing
is stored or committed.
File categories are data, not code. A category is an id, a label, a blurb, and a path regex; the parser walks the merged list first-match-wins and you never touch it.
To add an ecosystem (Go, Python, Rust, Java, iOS…):
src/rules/default.ts to src/rules/<name>.ts, rename the export, and edit the
regexes for that ecosystem’s conventions.src/rules/index.ts: add it to RULESETS. Order is precedence —
with one category id defined in several rulesets, the earlier ruleset’s definition
wins for that id.npm test — src/rules/rules.test.ts enforces the invariants: one category per id,
display order and ids agree, no /g flags (a stateful regex would silently break
first-match-wins), and the list ends with a core catch-all.Rules that depend on what the diff did (comment-only → cosmetic) stay in the parser; only path-based classification lives in rulesets.
See CONTRIBUTING.md for the full workflow.
Pushes to main run the gates (lint, typecheck, tests) and build the package. When
package.json’s version changed, the Chrome Web Store publish step submits the new
zip for review automatically — the store API rejects a package whose version isn’t greater
than the published one, so a version bump is what cuts a release. workflow_dispatch runs
the same workflow manually (tick force_publish to publish without a version change).
Repository settings (Settings → Secrets and variables → Actions):
| Kind | Name | Where to find it |
|---|---|---|
| Variable | CHROME_EXTENSION_ID |
Developer Dashboard → the item’s ID (also in the dashboard URL) |
| Variable | CHROME_PUBLISHER_ID |
Developer Dashboard → Account → publisher ID |
| Secret | CHROME_CLIENT_ID |
Google Cloud project (below) |
| Secret | CHROME_CLIENT_SECRET |
Google Cloud project (below) |
| Secret | CHROME_REFRESH_TOKEN |
Google Cloud project (below) |
One-time credential setup:
https://www.googleapis.com/auth/chromewebstore, and add yourself as a test user.http://localhost:8080/oauth2callback (or any loopback port you use below).chrome-webstore-upload CLI:
npx chrome-webstore-upload init (it walks the browser consent flow and prints the
refresh token), or follow
developer.chrome.com/docs/webstore/using-api.Pepper is a character from Becky Chambers’ Wayfarers novels — the grease-and-wrench mechanic aboard the tunneling ship Wayfarer, and the human who spends a whole book (A Closed and Common Orbit) teaching a ship’s AI, Lovelace, what it means to be a person. A franchise built on a human patiently understanding a machine felt like the right namesake for a tool built to help humans read what machines wrote. The chili-and-gear logo is the same idea: a mechanic’s take on a bell pepper. An homage, unaffiliated with the author or publisher.