I Built a GitHub Action That Writes Your PR Descriptions A developer has released StandupBot, a GitHub Action that automatically writes pull request descriptions by running the diff and commit messages through any OpenAI-compatible LLM endpoint. The action generates structured Summary, Changes, and Testing sections, plus up to three labels, and is available on the GitHub Marketplace under an MIT license. The developer built it to eliminate the unrewarded work of writing PR descriptions, which often leads to vague entries like 'fixed stuff'. Yep, this does what the title says. StandupBot reads the actual PR diff and commit messages, runs them through any OpenAI-compatible LLM endpoint, and writes a structured Summary / Changes / Testing description — so you never have to write "fixed stuff" again. I built this for my own team because I was tired of staring at empty PR boxes after every fix. Then I put it on GitHub Marketplace so anyone can use it. MIT licensed, no strings. A GitHub Action that writes your PR descriptions for you. It runs the real diff and commits through an LLM you choose, and fills in a structured Summary / Changes / Testing description plus up to three labels — so you stop hand-writing PR bodies and standup updates. Bring your own model: it talks to any OpenAI-compatible endpoint OpenAI, OpenRouter, Ollama, LM Studio, … . No provider, URL, or model is hardcoded. Generated end-to-end by StandupBot for a real PR nodejs/node 64573 https://github.com/nodejs/node/pull/64573 from its actual diff, using a real LLM endpoint. This is the description it produced verbatim: Summary Add lchownSync to the VFS implementation so that symbolic link ownership can be changed without following the link, matching the behavior of fs.lchownSync . Changes - doc/api/vfs.md: added lchownSync path, uid, gid to the list of VFS API signatures.- lib/internal/vfs/file system.js: added synchronous lchownSync method to VirtualFileSystem and updated the async… You open a PR. Forty files changed. Two hours of focused work. The description field is empty. So you write: fixed stuff . Or updated code . Or changes requested by reviewer . Three months later, someone you is running git log -p trying to figure out why the config format changed in April. The PR description was supposed to save that investigation. Nobody wrote it. We tried before: | Approach | Why it failed | |---|---| | PR templates | Everyone types "see title" into the template | | Checklist bots | Nagging doesn't scale; people ignore them | | Reviewer enforcement | Becomes the team's most hated job | The gap isn't discipline. It's that writing PR descriptions is unrewarded work . You get the same merge button whether you write a detailed description or "fixed stuff." So I built StandupBot https://github.com/marketplace/actions/standupbot-pr-describer . This is verbatim output from nodejs/node 64573 https://github.com/nodejs/node/pull/64573 , generated from the actual diff: Summary Add lchownSync to the VFS implementation so that symbolic link ownership can be changed without following the link, matching the behavior of fs.lchownSync . Changes - doc/api/vfs.md: added lchownSync path, uid, gid to VFS API signatures.- lib/internal/vfs/file system.js: added synchronous lchownSync method and updated the async wrapper.- lib/internal/vfs/provider.js: added default lchownSync method with JSDoc comment.- lib/internal/vfs/providers/memory.js: implemented lchownSync that updates uid/gid of the link entry.- lib/internal/vfs/setup.js: changed handler to invoke vfs.lchownSync instead of vfs.chownSync .- test/parallel/test-vfs-lchown-symlink.js: new test verifying sync, callback, and promise variants. Testing The new test exercises fs.lchownSync , fs.lchown callback , and fsp.lchown promise on symlinks inside a VFS mount, asserting correct uid/gid changes. Nobody edited that. It went straight from the model into the PR body. The action also returns a normalized title and labels clamped to bug | feature | chore | docs | refactor — so your label taxonomy stays clean. Add .github/workflows/standupbot.yml : name: StandupBot on: pull request: types: opened, synchronize permissions: pull-requests: write contents: read jobs: describe: runs-on: ubuntu-latest steps: - uses: XenoCyber0/StandUpBot@v1 with: llm-base-url: ${{ secrets.LLM BASE URL }} llm-api-key: ${{ secrets.LLM API KEY }} model: ${{ vars.LLM MODEL }} Set LLM API KEY as a secret , LLM MODEL as a variable . That's the whole thing. The action talks to any OpenAI-compatible chat API. Nothing is hardcoded. All of these work: OpenAI → https://api.openai.com/v1 OpenRouter → https://openrouter.ai/api/v1 Ollama → http://localhost:11434/v1 LM Studio → http://localhost:1234/v1 LocalAI → http://localai:8080/v1 You decide. You pick the model. You control the data path. Switching providers later is one secret update. If you want your code to never leave your network: GitHub repo ──webhook──▶ self-hosted Actions runner ──HTTP──▶ Ollama on your LAN your LLM box llm-base-url at your Ollama/LM Studio instance llm-api-key can be any non-empty string — Ollama ignores itYour diff never crosses the internet. The only outbound call is writing the PR body back to GitHub, which... GitHub already owns. Model notes: In my testing, Qwen2.5-Coder 7B and similar 7–9B instruction-tuned models work well. Below ~3B params, file-name hallucinations start appearing. The rule is simple: Your hand-written descriptions are safe. If you ever overwrite what it generated, it takes the hint and stays out. Three parts worth stealing: 1. Diff budget. Fetches the PR diff, applies gitignore-style exclusions from .standupbot.yml before anything hits a prompt: exclude: - package-lock.json - ' / .lock' - ' /dist/ ' The diff is hard-capped at ~24KB — a monster PR can't blow up your LLM context window. 2. Map-reduce for big diffs. Under the budget, one call. Over it? Each file gets summarized individually max 8 LLM calls , then merged into the final description. 3. Structured output enforcement. The prompt requires a rigid schema — title , summary , changes , testing , labels — and the parser clamps labels to the allowed set. If the model invents urgent-pls , it gets dropped, not shipped. Marketplace: github.com/marketplace/actions/standupbot-pr-describer https://github.com/marketplace/actions/standupbot-pr-describer Source: github.com/XenoCyber0/StandUpBot https://github.com/XenoCyber0/StandUpBot MIT Issues/feedback: github.com/XenoCyber0/StandUpBot/issues https://github.com/XenoCyber0/StandUpBot/issues Written by the person who finally read "fixed stuff" one too many times and decided to do something about it.