{"slug": "i-built-a-github-action-that-writes-your-pr-descriptions", "title": "I Built a GitHub Action That Writes Your PR Descriptions", "summary": "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'.", "body_md": "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.\n\nI 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.\n\nA 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.\n\nBring your own model: it talks to **any OpenAI-compatible endpoint** (OpenAI, OpenRouter, Ollama, LM Studio, …). No provider, URL, or model is hardcoded.\n\nGenerated 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:\n\n## Summary\n\nAdd\n\n`lchownSync`\n\nto the VFS implementation so that symbolic link ownership can be changed without following the link, matching the behavior of`fs.lchownSync`\n\n.## Changes\n\n- doc/api/vfs.md: added\n`lchownSync(path, uid, gid)`\n\nto the list of VFS API signatures.- lib/internal/vfs/file_system.js: added synchronous\n`lchownSync`\n\nmethod to`VirtualFileSystem`\n\nand updated the async…\n\nYou open a PR. Forty files changed. Two hours of focused work. The description field is empty.\n\nSo you write: `fixed stuff`\n\n. Or `updated code`\n\n. Or `changes requested by reviewer`\n\n.\n\nThree months later, someone (you) is running `git log -p`\n\ntrying to figure out why the config format changed in April. The PR description was supposed to save that investigation. Nobody wrote it.\n\nWe tried before:\n\n| Approach | Why it failed |\n|---|---|\n| PR templates | Everyone types \"see title\" into the template |\n| Checklist bots | Nagging doesn't scale; people ignore them |\n| Reviewer enforcement | Becomes the team's most hated job |\n\nThe 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.\"\n\nSo I built [StandupBot](https://github.com/marketplace/actions/standupbot-pr-describer).\n\nThis is verbatim output from [nodejs/node#64573](https://github.com/nodejs/node/pull/64573), generated from the actual diff:\n\n## Summary\n\nAdd\n\n`lchownSync`\n\nto the VFS implementation so that symbolic link ownership can be changed without following the link, matching the behavior of`fs.lchownSync`\n\n.## Changes\n\n- doc/api/vfs.md: added\n`lchownSync(path, uid, gid)`\n\nto VFS API signatures.- lib/internal/vfs/file_system.js: added synchronous\n`lchownSync`\n\nmethod and updated the async wrapper.- lib/internal/vfs/provider.js: added default\n`lchownSync`\n\nmethod with JSDoc comment.- lib/internal/vfs/providers/memory.js: implemented\n`lchownSync`\n\nthat updates uid/gid of the link entry.- lib/internal/vfs/setup.js: changed handler to invoke\n`vfs.lchownSync`\n\ninstead of`vfs.chownSync`\n\n.- test/parallel/test-vfs-lchown-symlink.js: new test verifying sync, callback, and promise variants.\n## Testing\n\nThe new test exercises\n\n`fs.lchownSync`\n\n,`fs.lchown`\n\n(callback), and`fsp.lchown`\n\n(promise) on symlinks inside a VFS mount, asserting correct uid/gid changes.\n\nNobody edited that. It went straight from the model into the PR body. The action also returns a normalized `title`\n\nand `labels`\n\nclamped to `bug | feature | chore | docs | refactor`\n\n— so your label taxonomy stays clean.\n\nAdd `.github/workflows/standupbot.yml`\n\n:\n\n```\nname: StandupBot\non:\n  pull_request:\n    types: [opened, synchronize]\n\npermissions:\n  pull-requests: write\n  contents: read\n\njobs:\n  describe:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: XenoCyber0/StandUpBot@v1\n        with:\n          llm-base-url: ${{ secrets.LLM_BASE_URL }}\n          llm-api-key: ${{ secrets.LLM_API_KEY }}\n          model: ${{ vars.LLM_MODEL }}\n```\n\nSet `LLM_API_KEY`\n\nas a **secret**, `LLM_MODEL`\n\nas a **variable**. That's the whole thing.\n\nThe action talks to **any** OpenAI-compatible chat API. Nothing is hardcoded. All of these work:\n\n```\nOpenAI        → https://api.openai.com/v1\nOpenRouter    → https://openrouter.ai/api/v1\nOllama        → http://localhost:11434/v1\nLM Studio     → http://localhost:1234/v1\nLocalAI       → http://localai:8080/v1\nYou decide.\n```\n\nYou pick the model. You control the data path. Switching providers later is one secret update.\n\nIf you want your code to never leave your network:\n\n```\nGitHub repo ──webhook──▶ self-hosted Actions runner ──HTTP──▶ Ollama\n                             (on your LAN)              (your LLM box)\n```\n\n`llm-base-url`\n\nat your Ollama/LM Studio instance`llm-api-key`\n\ncan 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.\n\n**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.\n\nThe rule is simple:\n\nYour hand-written descriptions are safe. If you ever overwrite what it generated, it takes the hint and stays out.\n\nThree parts worth stealing:\n\n**1. Diff budget.** Fetches the PR diff, applies gitignore-style exclusions from `.standupbot.yml`\n\nbefore anything hits a prompt:\n\n```\nexclude:\n  - package-lock.json\n  - '**/*.lock'\n  - '**/dist/**'\n```\n\nThe diff is hard-capped at ~24KB — a monster PR can't blow up your LLM context window.\n\n**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.\n\n**3. Structured output enforcement.** The prompt requires a rigid schema — `title`\n\n, `summary`\n\n, `changes[]`\n\n, `testing`\n\n, `labels[]`\n\n— and the parser clamps labels to the allowed set. If the model invents `urgent-pls`\n\n, it gets dropped, not shipped.\n\n**Marketplace:** [github.com/marketplace/actions/standupbot-pr-describer](https://github.com/marketplace/actions/standupbot-pr-describer)\n\n**Source:** [github.com/XenoCyber0/StandUpBot](https://github.com/XenoCyber0/StandUpBot) (MIT)\n\n**Issues/feedback:** [github.com/XenoCyber0/StandUpBot/issues](https://github.com/XenoCyber0/StandUpBot/issues)\n\nWritten by the person who finally read \"fixed stuff\" one too many times and decided to do something about it.", "url": "https://wpnews.pro/news/i-built-a-github-action-that-writes-your-pr-descriptions", "canonical_source": "https://dev.to/xenocyber0/i-built-a-github-action-that-writes-your-pr-descriptions-2obp", "published_at": "2026-08-10 21:06:48+00:00", "updated_at": "2026-08-10 21:47:05.175523+00:00", "lang": "en", "topics": ["developer-tools", "generative-ai", "large-language-models"], "entities": ["StandupBot", "GitHub", "OpenAI", "OpenRouter", "Ollama", "LM Studio", "nodejs/node"], "alternates": {"html": "https://wpnews.pro/news/i-built-a-github-action-that-writes-your-pr-descriptions", "markdown": "https://wpnews.pro/news/i-built-a-github-action-that-writes-your-pr-descriptions.md", "text": "https://wpnews.pro/news/i-built-a-github-action-that-writes-your-pr-descriptions.txt", "jsonld": "https://wpnews.pro/news/i-built-a-github-action-that-writes-your-pr-descriptions.jsonld"}}