repo-roast
A terminal CLI that reads a GitHub profile through the GitHub REST API — repos, language breakdown, stars, abandoned projects, and a sample of recent commit messages — then asks an LLM to roast the developer's coding habits.
It shows its receipts: every roast is preceded by an evidence table, and the model is instructed to only joke about facts that are actually in the data.
$ repo-roast roast torvalds --spice hot
Install
pip install -e .
Configure
cp .env.example .env
Then fill in:
| Variable | What it is |
|---|---|
GITHUB_TOKEN |
A personal access token. public_repo scope is enough for public data; add repo to include your own private repos. |
LLM_API_KEY |
A free Groq key (no credit card; starts with gsk_). |
LLM_BASE_URL |
Defaults to https://api.groq.com/openai/v1. |
LLM_MODEL |
Defaults to llama-3.3-70b-versatile. |
Usage
repo-roast roast # roast yourself (the authenticated user)
repo-roast roast torvalds # roast someone else
repo-roast roast torvalds --spice hot # roast them harder
repo-roast roast torvalds --dry-run # evidence + the exact prompt, no LLM call
repo-roast compare torvalds gvanrossum # roast battle: two profiles, one verdict
repo-roast repo psf/requests # roast a repository, not a person
repo-roast card torvalds -o card.svg # a shareable SVG card, for a README
repo-roast --help # the commands
repo-roast roast --help # the flags below
Breaking change in 0.2.0. The tool now takes a sub-command:
repo-roast torvaldsbecamerepo-roast roast torvalds. This made room forcompareandrepobelow, withoutcomparebeing ambiguous with a user who happens to be called compare. The old form prints the new one rather than a bare "No such command".
Flags
--spice, --model, --commits, --dry-run and --no-cache mean the same
thing on all four commands; --evidence and --format apply to every command
except card, whose only output is the SVG file itself. --version and
--clear-cache belong to the top level (repo-roast --version, not
repo-roast roast --version).
| Flag | Default | Meaning |
|---|---|---|
username (positional, roast, card) |
authenticated user | Which GitHub user to roast. |
username_a / username_b (positional, compare) |
— | The two GitHub users to pit against each other. |
full_name (positional, repo) |
— | A repository, as owner/name. |
--spice / -s |
medium |
mild, medium, or hot. |
--model / -m |
$LLM_MODEL or llama-3.3-70b-versatile |
Model name to send to the provider. |
--repos / -r (roast, compare, card) |
5 |
How many recently-pushed repos to sample commit messages from. |
--prs (repo) |
30 |
Recent pull requests to sample. |
--issues (repo) |
30 |
Recent open issues to sample. |
--commits / -c |
8 |
Commits to sample (per repository, for roast/compare/card; from the target repo, for repo). 1–50. |
--evidence / --no-evidence |
on | Show the stats table. |
--format / -f |
text |
text, json, or markdown. |
--output / -o (card) |
<login>-card.svg |
Where to write the SVG. |
--theme (card) |
dark |
dark or light. |
--dry-run |
off | Gather stats, print the evidence table and the exact prompt, then exit — no LLM call and no LLM_API_KEY required. |
--no-cache |
off | Skip the on-disk cache for this run. |
--version |
off | Print the installed version and exit (top level). |
--clear-cache |
off | Delete all cached responses, then exit (top level). |
--dry-run is the quickest way to check the GitHub half on its own.
repo samples pull requests, issues, the repository's file tree, and a code
search for TODO/FIXME — all bounded, the same way roast's commit sampling
is. It deliberately does not compute mean-time-to-review (would need one extra
API call per sampled PR) or "commits pushed at odd hours" (GitHub normalises
commit timestamps to UTC server-side, so the author's real local hour isn't
recoverable from the API at all — reporting a UTC hour as if it were local
would be presenting a fact that isn't actually in the data).
card asks the LLM for one sentence instead of a full roast — there is only
room for one on a badge — then renders it into a self-contained SVG (no
external fonts, no network requests) with the same evidence table any other
command would show. It is XML, and both the display name and the roast
sentence can contain &, <, >, or quotes (a quoted commit message, an
attacker-chosen GitHub display name); every dynamic value is escaped before
it reaches the document, so none of that can break the markup. Drop the file
straight into a README:

Scripting it
--format json prints one document to stdout and nothing else:
repo-roast roast torvalds -f json | jq '.stats.total_stars'
repo-roast roast torvalds -f json --dry-run | jq -r '.prompt.user'
repo-roast compare torvalds gvanrossum -f json | jq -r '.verdict'
repo-roast repo psf/requests -f json | jq -r '.repo.oldest_open_issue_days'
Progress spinners and error messages go to stderr, so a pipe receives either a valid document or nothing at all — never half of one. Failures still exit non-zero, with the message on stderr where it belongs.
--format markdown prints the evidence table(s) and the roast or verdict as
Markdown, ready to paste into an issue or a README.
Provider
The default backend is Groq: free, no credit card, and OpenAI-compatible.
repo-roast talks to it with the official openai SDK pointed at a custom base
URL, so any OpenAI-compatible endpoint works — switching providers is just three
environment variables.
| Provider | LLM_BASE_URL |
Example LLM_MODEL |
|---|---|---|
| Groq (default) | https://api.groq.com/openai/v1 |
llama-3.3-70b-versatile |
| Google Gemini | https://generativelanguage.googleapis.com/v1beta/openai/ |
gemini-2.0-flash |
| Mistral | https://api.mistral.ai/v1 |
mistral-small-latest |
| OpenRouter | https://openrouter.ai/api/v1 |
meta-llama/llama-3.3-70b-instruct:free |
| Cerebras | https://api.cerebras.ai/v1 |
llama-3.3-70b |
Model strings change over time — if a call 404s, check the provider's current model list.
Running it with Docker
No local Python needed. Images are published to the GitHub Container Registry
on every release, for linux/amd64 and linux/arm64:
docker run --rm -e GITHUB_TOKEN=ghp_... -e LLM_API_KEY=gsk_... \
ghcr.io/amayyas/repo-roast roast torvalds --spice hot
Environment variables, not a mounted .env: simpler, and there is no file to
accidentally bake into a container. LLM_BASE_URL and LLM_MODEL work the
same way if you are pointing at a different provider.
Tags: latest tracks the newest release, vX.Y.Z pins a specific one — same
versions as PyPI.
How it stays polite to the API
Repo metadata (languages, stars, descriptions, push dates) comes from the single
repo listing that PyGithub already paginates. The only per-repo calls are for
commit messages, and they are bounded on both axes: the --repos most recently
pushed originals, up to 8 commits each.
Every run is cached on disk for 15 minutes, keyed on the target and the
sampling flags — roast torvalds --repos 5 and roast torvalds --repos 10 are
different cache entries, since they're genuinely different data. Re-running the
same command while iterating on --spice or --format costs no API calls at
all. Two exceptions: --no-cache skips the cache for one run, and roasting
yourself (roast with no username) is never cached — the cache key has no way
to tell two different tokens' "yourself" apart, and handing one token's profile
back to another would be a real bug. The cache never stores a token, only
GitHub's public responses. repo-roast --clear-cache deletes it.
Before spending any quota, repo-roast checks how much is left — checking itself costs nothing. Below a safety margin it refuses to start, with a clear message instead of burning through what little remains and failing partway through with a raw 403.
Ethical use
repo-roast generates jokes about named, real people from their public GitHub activity. That comes with rules, not just a disclaimer:
- Roast the code and the habits — commit hygiene, abandoned repos, a
suspicious
TODO— never the person. No appearance, no identity, no protected characteristic. The system prompt enforces this on every call, and it's the first hard rule inroast.py. - Don't use the output to harass, dogpile, or target someone who didn't ask
for it. A roast run against a stranger without their knowledge is not the
friendly-jab use case this tool is built for.
cardraises the stakes here: the SVG is meant to be embedded somewhere public, so only put one in your own README, about yourself. - This isn't only a prompt-level promise. SECURITY.md documents the structural defense that keeps a booby-trapped repo from turning the tool into a weapon against whoever is being roasted.
repo-roast repo sidesteps the "named person" concern differently: it targets
a codebase's pull requests, issues, and commits, not an individual. The data it
hands to the model never includes a contributor's name, so the model has
nothing to single anyone out with in the first place.
This project follows a Code of Conduct; the same spirit applies to how the tool itself gets used.
Support
Questions, bugs, or a roast that missed? See SUPPORT.md, or reach out directly at amayyas.aouadene@epitech.eu.
Found a security issue? See SECURITY.md instead of opening a public issue.
Want to contribute? See CONTRIBUTING.md. This project follows a Code of Conduct.
License
MIT © Amayyas Aouadene
Metadata
Release files for repo-roast 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| repo_roast-0.5.0.tar.gz | 50.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| repo_roast-0.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 83.4 kB
Release files / repo_roast-0.5.0.tar.gz
| Download URL | repo_roast-0.5.0.tar.gz |
|---|---|
| Size | 50.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
049967903c3d3ff0e3a1cfa605e7be6dd7475df7d7570a60f15bbb4b9c81b3c5
|
|
BLAKE2b-256 checksum How to use checksums |
17a9d3f823d7f25c852a6a358f6273b2df9c650e56d82b2b086d2f9f20af1fdc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 11, 2026.
Transparency logRelease files / repo_roast-0.5.0-py3-none-any.whl
| Download URL | repo_roast-0.5.0-py3-none-any.whl |
|---|---|
| Size | 33.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9a57b4528bf9378e97b7cec1a8f04034c24e8fa774d7f2e8f58c80f00e05b08e
|
|
BLAKE2b-256 checksum How to use checksums |
8cd1e08be45cbf96d9a6ce0a997ca8a91fd7ea7bafeb471530ef41abc5d3bdbc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 11, 2026.
Transparency log