{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreic7c4z3dwnnfx2lmtwqh5jpovrrp7u6u3ioomnfj3ilyazwtvclwu",
    "uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3mos3fqn3nyc2"
  },
  "coverImage": {
    "$type": "blob",
    "ref": {
      "$link": "bafkreif6w6noeezlvogexuwgym6heazafd62mv4dep7obq7nmbipqdu5ja"
    },
    "mimeType": "image/webp",
    "size": 67326
  },
  "path": "/enjoy_kumawat/i-fixed-the-ai-commit-messages-problem-in-50-lines-of-python-3a5a",
  "publishedAt": "2026-06-21T09:14:30.000Z",
  "site": "https://dev.to",
  "tags": [
    "git",
    "python",
    "ai",
    "devtools",
    "Claude Code",
    "github.com/enjoy-kumawat/my-git-manager",
    "@mcp.tool"
  ],
  "textContent": "You've probably seen that trending post — _\"I Asked AI to Write My Commit Messages and It Was Embarrassing.\"_\n\nSame. But instead of accepting embarrassing output, I fixed it.\n\nHere's the thing: the problem isn't AI writing commit messages. The problem is _how_ you ask it. One clear system prompt + the actual diff = surprisingly good results.\n\n##  The Setup\n\nNo new packages. No API key. If you have Claude Code, you're already set.\n\n\n\n    #!/usr/bin/env python3\n    import subprocess\n\n    SYSTEM = (\n        \"You are a git commit message generator. \"\n        \"Output ONLY the commit message — no explanation, no markdown, no quotes. \"\n        \"Follow Conventional Commits: type(scope): subject. \"\n        \"Types: feat, fix, docs, style, refactor, test, chore. \"\n        \"Subject: imperative, lowercase, max 72 chars.\"\n    )\n\n    diff = subprocess.check_output([\"git\", \"diff\", \"--staged\"], text=True)\n    if not diff.strip():\n        print(\"Nothing staged. Run `git add` first.\")\n        raise SystemExit(1)\n\n    msg = subprocess.check_output(\n        [\"claude\", \"-p\", SYSTEM + \"\\n\\n\" + diff],\n        text=True,\n    ).strip()\n    print(msg)\n\n\nThat's it. 20 lines. Uses the `claude` CLI under the hood — no API key, no config, just your existing Claude Code OAuth session.\n\n##  Why It Works\n\n**The system prompt does the heavy lifting.** Three constraints:\n\n  1. `Output ONLY the commit message` — no preamble, no explanation\n  2. `Follow Conventional Commits` — `feat`, `fix`, `chore`, etc.\n  3. `max 72 chars` — keeps it readable in git log\n\n\n\n**The diff is the context.** You're not asking \"write a commit message\". You're asking \"given these exact changes, what happened?\" That's a much more answerable question.\n\n##  Usage\n\n\n    # No setup needed if you have Claude Code. Just:\n    git add .\n    python /path/to/git_commit.py\n    # → feat(server): add AI commit message generator via Claude CLI\n\n\nOr wire it into a git alias:\n\n\n\n    git config --global alias.ai '!python /path/to/git_commit.py'\n    # git ai\n\n\n##  The Results\n\nBefore:\n\n\n\n    update stuff\n    fix bug\n    WIP\n    added the thing\n\n\nAfter:\n\n\n\n    feat(api): add generate_commit_message tool to MCP server\n    fix(auth): handle expired token on refresh\n    refactor(db): extract query builder into separate module\n\n\n##  As an MCP Tool Too\n\nI also wrapped it as an MCP tool so Claude Code can call it directly from any conversation:\n\n\n\n    @mcp.tool()\n    def generate_commit_message(diff: str) -> str:\n        \"\"\"Generate a Conventional Commits message from a git diff string.\"\"\"\n        full = SYSTEM + \"\\n\\n\" + diff\n        return subprocess.check_output([\"claude\", \"-p\", full], text=True).strip()\n\n\nFull project: github.com/enjoy-kumawat/my-git-manager\n\n20 lines. No new dependencies. No API key. Conventional Commits every time.\n\nThe embarrassing part was waiting this long to build it.",
  "title": "I Fixed the \"AI Commit Messages\" Problem in 20 Lines of Python"
}