Listen to this post:

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, aicommit, 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> aicommit ai powered security
aicommit() {
  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="$prompt

Additional 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."
    return 1
  fi

  echo "Committing..."
  git commit -m "$commit_message" && git commit --amend
}

View Gist (lines 1-27)

aijiracommit works similarly to aicommit 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.

aijiracommit() {
  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."
    return
  fi
  
  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="$prompt

Additional context: $context"
  fi
  
  prompt="$prompt

Jira tickets:
$jira_tickets

RESPOND 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."
    return 1
  fi
  
  if [[ "$claude_response" == "None" ]]; then
    echo "No appropriate Jira ticket found."
    return
  fi
  
  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"
    return
  fi

  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"
    return 1
  fi

  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"
    return 1
  fi

  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"
}

View Gist (lines 29-113)

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.