Using the same jira-buddy from earlier
and combining it with claude code, we can stop writing commits entirely and let the LLMs do it.
This gist
suggests two bash functions to add to your bashrc to never write commits again!
This first function, ai-commit, passes:
the staged changes (whatever you git add‘ed)
the current directory’s name (you’ll want to run this at the root of the repo, so this would likely be your project name, which adds some context to your change)
anything you pass to the command as extra context
to claude code and asks it for a concise commit message.
For example:
host:~/dev/superapp> ai-commit ai powered security
ai-commit(){ local profile="${DEFAULT_CLAUDE_COMMIT_PROFILE:-claude}" local context="$@" echo "Generating commit message with Claude using profile '$profile'... with extra context: '$context'" local project_dir_name="${PWD##*/}" local commit_message
local prompt="Analyze the staged git changes for the '$project_dir_name' project and generate a concise commit message following conventional commit format (type: description). Keep the message under 72 characters for the first line. Only output the commit message, nothing else - the output goes directly to git commit -m."if[[ -n "$context"]]; then prompt="$promptAdditional context: $context"fi commit_message=$(docker run --rm \
--volume "$PWD:/home/node/dev/$project_dir_name"\
-w "/home/node/dev/$project_dir_name"\
--volume "$HOME/.${profile}:/home/node/.claude"\
--volume "$HOME/.${profile}.json:/home/node/.claude.json"\
nbr23/claudecode -p "${prompt}" | awk '{$1=$1;print}')if[[ -z "$commit_message"]]; then echo "Error: Failed to generate a commit message from Claude. Aborting."return1fi echo "Committing..." git commit -m "$commit_message"&& git commit --amend
}
ai-jira-commit works similarly to ai-commit but also calls jira-buddy to fetch the list of open tickets assigned to ourselves on Jira. It passes this list as extra context to claude code, and asks it to delivery a commit message prepended with the most appropriate ticket ID.
ai-jira-commit(){ local profile="${DEFAULT_CLAUDE_COMMIT_PROFILE:-claude}" local context="$@" echo "Generating Jira-aware commit message with Claude using profile '$profile'... with extra context: '$context'" local project_dir_name="${PWD##*/}" local commit_message
local jira_tickets
jira_tickets=$(uvx jira-buddy --own --json --fields key,summary,status,issuetype)if[[ -z "$jira_tickets"]]; then echo "Error: Failed to fetch Jira tickets."returnfi local prompt="CRITICAL INSTRUCTIONS: You MUST respond with ONLY a single line containing the commit message. NO explanations, NO reasoning, NO additional text, NO line breaks.
Analyze the staged git changes for the '$project_dir_name' project. Based on the changes and the following Jira tickets JSON, pick the most appropriate ticket and generate a commit message in the format 'TICKET-KEY - commit message'. Keep under 72 characters total.
IMPORTANT: Only pick a ticket if you have confidence it matches the changes or that the changes could be a subtask of. If no ticket clearly matches, respond with exactly 'None' instead."if[[ -n "$context"]]; then prompt="$promptAdditional context: $context"fi prompt="$promptJira tickets:
$jira_ticketsRESPOND WITH ONLY THE COMMIT MESSAGE LINE. NOTHING ELSE." local claude_response
claude_response=$(docker run --rm \
--volume "$PWD:/home/node/dev/$project_dir_name"\
-w "/home/node/dev/$project_dir_name"\
--volume "$HOME/.${profile}:/home/node/.claude"\
--volume "$HOME/.${profile}.json:/home/node/.claude.json"\
nbr23/claudecode -p "$prompt" | awk '{$1=$1;print}')if[[ -z "$claude_response"]]; then echo "Error: Failed to generate a commit message from Claude. Aborting."return1fiif[["$claude_response"=="None"]]; then echo "No appropriate Jira ticket found."returnfi commit_message=$(echo "$claude_response" | grep -oE '^[A-Z]+-[0-9]+ - .*$' | head -n 1)if[[ -z "$commit_message"]]; then echo "Error: Claude response doesn't match expected commit message format." echo "Claude response: $claude_response"returnfi local line_count
line_count=$(echo "$commit_message" | wc -l)if[[ $line_count -gt 1]]; then echo "Error: Commit message must be only one line long." echo "Generated message:" echo "$commit_message"return1fi local ticket_key
ticket_key=$(echo "$commit_message" | grep -oE '^[A-Z]+-[0-9]+' | head -n 1)if[[ -z "$ticket_key"]]; then echo "Error: Commit message must start with a ticket number (e.g., ABC-123)." echo "Generated message: $commit_message"return1fi echo "Selected Jira ticket: $ticket_key" echo "$jira_tickets" | jq -r ".[] | select(.key == \"$ticket_key\") | \"Key: \(.key)\nSummary: \(.summary)\nStatus: \(.status)\nIssue Type: \(.issuetype)\"" echo
echo "Committing..." git commit -m "$commit_message"}
Both run claude inside docker
, but can be easily modified to run natively.
YMMV, and sometimes you need to run it a couple of times before it accepts to deliver a commit message and not a monologue on git (despite the ALL CAPS instructions!!!), or figures out which LLM-generated ticket your PM created matches the LLM-generated code you staged.