commitgpt 2.0: Building a Commit Message System, Not a Prompt Trick
I didn't build commitgpt because writing commit messages is hard. I built it because writing good commit messages, every day, at speed, is friction that accumulates.
Conventional commits are useful: they make history queryable, changelogs scriptable, and reviews less chaotic. But in practice, developers either rush them or skip structure when context switching gets expensive. "fix stuff" isn't a morality failure, it's a tooling failure.
commitgpt started as a Git hook that generated one-line conventional commits from staged diffs. That version worked. Then real usage exposed the real problem: this isn't about generation, it's about output control under unreliable model behavior.
How commitgpt works now
commitgpt has two modes, because one mode cannot optimize for both speed and quality.
1) Automatic mode (global Git hook)
Runs on git commit. Reads staged diff. Generates a strict single-line conventional commit. Writes to commit message file. Never blocks commit if generation fails. This is for day-to-day velocity: fast commits, minimal ceremony.
2) Manual detailed mode (CLI)
Explicit command for higher-quality messages. Produces multi-line commit messages with a summary line, key changes, and an optional impact statement. Designed for commits that will be read in PRs, audits, or release prep. This is for communication quality when commit history is a collaboration surface, not just a log.
The evolution: from hook automation to behavioral control
The first version proved the hook integration quickly. That was the easy part.
The second version required system-level thinking: separate workflows for speed and depth, separate prompts and validation rules per workflow, failure handling that protects developer flow, and guardrails for model outputs that are technically valid but operationally useless.
The key shift: I stopped treating the model as a smart text generator and started treating it as an unreliable subsystem behind an interface contract.
Design tradeoffs that actually mattered
Single-line mode must be deterministic enough to run silently in the background. Detailed mode can be richer, but only when explicitly requested. Trying to make one prompt do both led to mediocre results in both.
Automatic mode enforces tight output rules: one line, valid conventional prefix, no commentary. If output violates format, regenerate or fallback. Detailed mode allows structured multiline output with bullets and context — validation is looser because readability matters more than rigid syntax.
If the API fails (rate limit, bad credits, timeout), commit flow still works. A tool that blocks commits during network failure is not a developer tool. It's an outage amplifier.
Real-world LLM failures you have to design for
The obvious failure is "bad commit message." The real failures are weirder: empty responses, reasoning dumps instead of final output, correct analysis wrapped in invalid format, prefix drift with borderline diffs, API failures at exactly the wrong time.
A model might return a thoughtful paragraph explaining architectural impact. Great for a human. Useless for commit-msg hook mode. Another example: model outputs a valid line plus a second line of explanation, instantly breaking strict commit formatting.
The fix is not "better model." The fix is mode-specific prompts, hard output contracts, validation and fallback, and conservative defaults under uncertainty.
The philosophy now
This project is no longer about AI writing commits for me. It's about building a reliable interface between developer workflows and nondeterministic models.
The core lesson: DX beats raw model intelligence in tooling contexts. A brilliant model that occasionally breaks flow is worse than a modest model behind solid constraints. Developers optimize for trust. If a tool is predictable, they keep it. If it surprises them at commit time, they rip it out.
The goal isn't maximum linguistic quality. The goal is controlled behavior under real-world failure conditions.
Closing
commitgpt started as a convenience hook. It became a small case study in production LLM integration: separate modes for separate jobs, strict where automation is implicit, flexible where intent is explicit, fail safely, treat prompt design and reliability as first-class engineering work.
The hardest part was never wiring into Git. The hardest part was making model output dependable enough that developers stop thinking about the tool and trust it in their daily flow.