2 # Rejects a commit message that breaks the rules in AGENTS.md: a subject of 50
3 # characters or fewer without a final period, one blank line, a body, body lines
4 # of 72 characters or fewer, and real line breaks instead of "\n". Prose
5 # paragraphs fill each line before wrapping. Formatted blocks keep their line
6 # breaks. A body line without spaces, such as a URL, may be longer.
10 subject=$(sed -n 1p "$file")
12 "Merge "* | "Revert "* | "fixup! "* | "squash! "*) exit 0 ;;
16 printf 'commit-msg: %s\n' "$1" "write the message to a file and commit with git commit -F." >&2
20 if grep -q '\\n' "$file"; then
21 fail 'the message has a literal "\n"; use real line breaks'
24 [ ${#subject} -le 50 ] || fail "the subject has ${#subject} characters; the limit is 50"
27 *.) fail "the subject ends with a period" ;;
30 [ -z "$(sed -n 2p "$file")" ] || fail "the second line must be blank"
32 sed -n '3,$p' "$file" | grep -q '[^[:space:]]' || fail "the message has no body"
34 long=$(sed -n '3,$p' "$file" | awk 'length($0) > 72 && $0 ~ / / { print NR + 2; exit }')
35 [ -z "$long" ] || fail "line $long is longer than 72 characters"
38 function check_paragraph( i, word) {
39 if (!formatted && !bad_line) {
40 for (i = 1; i < count; i++) {
42 sub(/[[:space:]].*$/, "", word)
43 if (length(lines[i]) + 1 + length(word) <= 72) {
57 if ($0 ~ /^(`{3,}|~{3,})[[:space:]]*$/ && substr($0, 1, 1) == fence) {
59 if (RLENGTH >= fence_length) fence = ""
67 fence = substr($0, 1, 1)
68 fence_length = RLENGTH
78 if ($0 ~ /^[[:space:]>#|]/ ||
79 $0 ~ /^([-*+][[:space:]]|[0-9]+[.)][[:space:]])/ ||
80 $0 ~ /^[-=*_]{3,}$/ ||
81 $0 ~ /^[[:alnum:]][[:alnum:]-]*:[[:space:]]/) {
91 if (bad_line) print bad_line
94 [ -z "$short" ] || fail "unnecessary line break after line $short; reflow the paragraph to 72 characters"