Every codebase has a line that makes you recoil. A nested ternary, three levels deep, inside an authorization check, returning true : false inside another condition — with a comment above it reading "Do not change without asking me."
The usual next move is git blame, followed by an awkward conversation. Sometimes that conversation is with yourself, because the name that comes back is your own.
git-sorry handles the awkward part for you, and makes it funny enough that nobody gets defensive.
What I built
git-sorry takes a file and a line number, runs git blame, and works out whether the guilty party is you or someone else. Then it asks Gemini to write the appropriate response — a grovelling apology if the line is yours, a roast if it belongs to a teammate — and posts it to your team's Slack or Discord.
Here is an actual run against exactly that kind of line, attributed to a fictional colleague named Marcus:
That output is unedited. The roast is whatever the model came up with that afternoon.
Trying it
You need a free Gemini API key from Google AI Studio. Nothing else — no install, and no webhook if you use --dry-run:
npx git-sorry config --set-key <GEMINI_API_KEY>
npx git-sorry blame src/auth.ts 42 --dry-run
--dry-run prints the verdict in your terminal and tells nobody, which I recommend for your first attempt, because you do not yet know what it is going to say about you.
When you are ready to involve other people:
git-sorry config --set-webhook <SLACK_OR_DISCORD_WEBHOOK_URL>
git-sorry blame src/auth.ts 42
There are two flags for when you want to override fate: --roast roasts the author regardless of who wrote the line, and --apology makes you grovel even when it was not your fault.
How it works
It is a small thing, deliberately. About 250 lines of TypeScript.
git blame -L <n>,<n> --porcelain gives the author, the date, and the source line. That gets compared against your local git config user.name to decide apology or roast. The prompt goes to Gemini Flash, the reply gets wrapped in a box with boxen, and then it is posted to your webhook.
The one detail I am quietly pleased with: Slack reads the text field of a webhook payload and Discord reads content. Send a body containing both and the same single request works for either service, with no branching and no config asking which one you use.
Native fetch handles the request, so there is no HTTP dependency. Config lives in a plain JSON file with owner-only permissions, so there is no config library either. Failed calls retry twice, except a rejected API key, which fails immediately because no amount of retrying will fix a wrong key.
Why bother
The honest answer is that it started as a joke and then turned out to be a slightly useful one.
Blame culture in code review is mostly about tone. A message that says "who wrote this" lands badly. A message that says "Marcus, darling, I didn't know logic gates could express a mid-life crisis" somehow lands fine, and the line still gets fixed. Turning the callout into a bit that the whole team is in on makes the actual conversation about the code much easier to have.
It also makes people read git blame output, which they otherwise never do.
Source is on GitHub, MIT licensed. Pull requests welcome, especially new personalities for the prompt — the current one is stuck somewhere between a theatre critic and a disappointed parent, and I suspect there is a lot of room below that.

Top comments (0)