Skip to content

Commit 0acda80

Browse files
committed
feat(scripts): add multiple diff strategies and debug output
1 parent 3de0378 commit 0acda80

1 file changed

Lines changed: 85 additions & 27 deletions

File tree

scripts/generate_commit_message.sh

Lines changed: 85 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,42 +5,78 @@
55
# bash scripts/generate_commit_message.sh
66
#
77
# Environment variables:
8-
# COMMIT_MODEL_ID - Bedrock model to use (default: amazon.nova-lite-v1:0)
8+
# COMMIT_MODEL_ID - Bedrock model/inference profile to use (default: us.amazon.nova-lite-v1:0)
99
# Examples:
10-
# amazon.nova-lite-v1:0
11-
# anthropic.claude-3-haiku-20240307-v1:0
10+
# us.amazon.nova-lite-v1:0 (Nova Lite - default, cheapest)
11+
# us.amazon.nova-micro-v1:0 (Nova Micro - fastest)
12+
# us.amazon.nova-pro-v1:0 (Nova Pro - most capable)
13+
# us.anthropic.claude-3-haiku-20240307-v1:0 (Claude Haiku)
1214
# AWS_REGION - AWS region for Bedrock (uses default if not set)
15+
# COMMIT_DEBUG - Set to 1 to enable debug output to stderr
1316

1417
set -euo pipefail
1518

16-
MODEL_ID="${COMMIT_MODEL_ID:-amazon.nova-lite-v1:0}"
19+
MODEL_ID="${COMMIT_MODEL_ID:-us.amazon.nova-lite-v1:0}"
20+
DEBUG="${COMMIT_DEBUG:-0}"
1721

18-
# Get the diff - prefer staged changes, fall back to unstaged
19-
DIFF_STAT=$(git diff --cached --stat 2>/dev/null)
20-
DIFF_CONTENT=$(git diff --cached 2>/dev/null)
22+
debug() {
23+
if [ "$DEBUG" = "1" ]; then
24+
echo "[DEBUG] $*" >&2
25+
fi
26+
}
2127

28+
# Collect diff using multiple strategies (in priority order)
29+
# After "git add .", staged changes show in "git diff --cached" or "git diff HEAD"
30+
DIFF_STAT=""
31+
DIFF_CONTENT=""
32+
33+
# Strategy 1: Staged changes (git diff --cached)
34+
DIFF_STAT=$(git diff --cached --stat 2>/dev/null || true)
35+
DIFF_CONTENT=$(git diff --cached 2>/dev/null || true)
36+
37+
# Strategy 2: If no staged changes, try diff against HEAD (catches both staged + unstaged)
38+
if [ -z "$DIFF_STAT" ]; then
39+
debug "No staged diff found, trying HEAD..."
40+
DIFF_STAT=$(git diff HEAD --stat 2>/dev/null || true)
41+
DIFF_CONTENT=$(git diff HEAD 2>/dev/null || true)
42+
fi
43+
44+
# Strategy 3: Unstaged changes
2245
if [ -z "$DIFF_STAT" ]; then
23-
DIFF_STAT=$(git diff --stat 2>/dev/null)
24-
DIFF_CONTENT=$(git diff 2>/dev/null)
46+
debug "No HEAD diff found, trying unstaged..."
47+
DIFF_STAT=$(git diff --stat 2>/dev/null || true)
48+
DIFF_CONTENT=$(git diff 2>/dev/null || true)
2549
fi
2650

27-
# If still empty, try diff against HEAD
51+
# Strategy 4: Last commit diff (if everything is already committed)
2852
if [ -z "$DIFF_STAT" ]; then
29-
DIFF_STAT=$(git diff HEAD --stat 2>/dev/null || echo "no diff available")
30-
DIFF_CONTENT=$(git diff HEAD 2>/dev/null || echo "")
53+
debug "No working tree diff, using last commit..."
54+
DIFF_STAT=$(git diff HEAD~1 --stat 2>/dev/null || echo "no diff available")
55+
DIFF_CONTENT=$(git diff HEAD~1 2>/dev/null || echo "")
3156
fi
3257

33-
# Truncate diff content to ~4000 chars to stay within token limits
34-
DIFF_TRUNCATED=$(echo "$DIFF_CONTENT" | head -c 4000)
58+
debug "Diff stat length: ${#DIFF_STAT}"
59+
debug "Diff content length: ${#DIFF_CONTENT}"
60+
61+
# Truncate diff content to ~6000 chars to stay within token limits while providing good context
62+
DIFF_TRUNCATED=$(echo "$DIFF_CONTENT" | head -c 6000)
3563

3664
# Build the prompt
37-
PROMPT="Based on the following git diff, generate a single-line commit message following conventional commit format (e.g., feat:, fix:, docs:, refactor:, chore:, test:).
38-
Return ONLY the commit message on a single line. No quotes, no explanation, no prefix like 'Commit message:'.
65+
PROMPT="You are a Git commit message expert. Analyze the following git changes and generate a clear, informative commit message.
66+
67+
Rules:
68+
1. Use conventional commit format: type(scope): description
69+
2. Types: feat, fix, docs, refactor, chore, test, style, build, ci, perf
70+
3. The scope should identify the component or area changed (e.g., makefile, config, api, ui)
71+
4. The description should concisely summarize WHAT changed and WHY
72+
5. Return ONLY the commit message on a single line
73+
6. Do NOT wrap in quotes, do NOT add any explanation or preamble
74+
7. Keep it under 100 characters
3975
4076
Files changed:
4177
${DIFF_STAT}
4278
43-
Diff:
79+
Diff details:
4480
${DIFF_TRUNCATED}"
4581

4682
# Escape the prompt for JSON using jq
@@ -49,23 +85,45 @@ ESCAPED_PROMPT=$(echo "$PROMPT" | jq -Rs .)
4985
# Build the messages JSON
5086
MESSAGES="[{\"role\":\"user\",\"content\":[{\"text\":${ESCAPED_PROMPT}}]}]"
5187

52-
# Call Bedrock converse API
53-
COMMIT_MSG=$(aws bedrock-runtime converse \
88+
debug "Calling Bedrock with model: $MODEL_ID"
89+
90+
# Call Bedrock converse API - capture both stdout and stderr separately
91+
BEDROCK_OUTPUT=""
92+
BEDROCK_ERR=""
93+
BEDROCK_OUTPUT=$(aws bedrock-runtime converse \
5494
--model-id "$MODEL_ID" \
5595
--messages "$MESSAGES" \
5696
--inference-config '{"maxTokens":100,"temperature":0.3}' \
5797
--query 'output.message.content[0].text' \
58-
--output text 2>&1) || true
98+
--output text 2>/tmp/bedrock_commit_err) || true
99+
100+
BEDROCK_ERR=$(cat /tmp/bedrock_commit_err 2>/dev/null || true)
101+
rm -f /tmp/bedrock_commit_err
59102

60-
# Fallback if Bedrock call fails or returns empty
61-
if [ -z "$COMMIT_MSG" ] || [ "$COMMIT_MSG" = "None" ] || echo "$COMMIT_MSG" | grep -qi "error"; then
62-
# Generate a basic fallback message from the diff stat
63-
FILE_COUNT=$(echo "$DIFF_STAT" | grep -c "file" || echo "0")
64-
echo "chore: update ${FILE_COUNT} file(s)"
103+
debug "Bedrock output: '$BEDROCK_OUTPUT'"
104+
debug "Bedrock stderr: '$BEDROCK_ERR'"
105+
106+
# Check if we got a valid response
107+
if [ -z "$BEDROCK_OUTPUT" ] || [ "$BEDROCK_OUTPUT" = "None" ] || [ "$BEDROCK_OUTPUT" = "null" ]; then
108+
debug "Bedrock returned empty/None, using fallback"
109+
if [ -n "$BEDROCK_ERR" ]; then
110+
debug "Bedrock error: $BEDROCK_ERR"
111+
fi
112+
# Generate a descriptive fallback from the diff stat
113+
SUMMARY=$(echo "$DIFF_STAT" | head -5 | tr '\n' ', ' | sed 's/, $//')
114+
echo "chore: update files - ${SUMMARY}"
65115
exit 0
66116
fi
67117

68-
# Clean up - remove surrounding quotes if present, trim whitespace
69-
COMMIT_MSG=$(echo "$COMMIT_MSG" | sed 's/^["'"'"']//;s/["'"'"']$//' | xargs)
118+
# Clean up - strip code blocks, remove surrounding quotes, trim whitespace
119+
# Models sometimes wrap output in ```bash ... ``` or ```...```
120+
COMMIT_MSG=$(echo "$BEDROCK_OUTPUT" | \
121+
sed '/^```/d' | \
122+
sed 's/^["'"'"']*//;s/["'"'"']*$//' | \
123+
grep -v '^[[:space:]]*$' | \
124+
head -1 | \
125+
xargs)
126+
127+
debug "Final commit message: '$COMMIT_MSG'"
70128

71129
echo "$COMMIT_MSG"

0 commit comments

Comments
 (0)