add: script commit_msg.sh git/jj et skill commit-message

This commit is contained in:
2026-03-27 11:37:43 +01:00
parent 686fa7c064
commit 7e41166f97
2 changed files with 196 additions and 0 deletions

173
hooks/commit_msg.sh Executable file
View File

@@ -0,0 +1,173 @@
#!/usr/bin/env bash
# commit_msg.sh — génère un message de commit depuis les changements en cours
# Fonctionne avec git (fichiers stagés) et jj (working copy).
#
# Usage : bash .claude/hooks/commit_msg.sh
set -euo pipefail
log() { printf '\033[1;34m==>\033[0m %s\n' "$*" >&2; }
info() { printf ' \033[0;37m%s\033[0m\n' "$*" >&2; }
ok() { printf ' \033[0;32m✓\033[0m %s\n' "$*" >&2; }
# --- Détection du VCS ---
_detect_vcs() {
if jj root &>/dev/null; then
echo "jj"
elif git rev-parse --git-dir &>/dev/null; then
echo "git"
else
echo "none"
fi
}
# --- Fichiers modifiés ---
_changed_files() {
case "$VCS" in
jj) jj diff --name-only ;;
git) git diff --staged --name-only ;;
esac
}
# --- Diff lisible par fichier ---
_readable_diff() {
local file="$1"
local raw_diff
case "$VCS" in
jj) raw_diff=$(jj diff -- "$file") ;;
git) raw_diff=$(git diff --staged -- "$file") ;;
esac
[[ -z "$raw_diff" ]] && return 0
local max_len
max_len=$(grep '^[+-]' <<< "$raw_diff" 2>/dev/null \
| awk '{ if (length > m) m = length } END { print m+0 }')
if (( max_len <= 500 )); then
printf '%s' "$raw_diff"
return
fi
# Pretty-print pour les formats pathologiques (JSON, JS minifié…)
local ext="${file##*.}"
local pretty_old pretty_new
case "$ext" in
json)
case "$VCS" in
jj)
pretty_old=$(jj file show "${file}@-" 2>/dev/null | python3 -m json.tool 2>/dev/null || true)
pretty_new=$(jj file show "$file" 2>/dev/null | python3 -m json.tool 2>/dev/null || true)
;;
git)
pretty_old=$(git show "HEAD:${file}" 2>/dev/null | python3 -m json.tool 2>/dev/null || true)
pretty_new=$(git show ":${file}" 2>/dev/null | python3 -m json.tool 2>/dev/null || true)
;;
esac
;;
js|mjs|cjs|css|ts)
local node_fmt='
const chunks = [];
process.stdin.on("data", d => chunks.push(d));
process.stdin.on("end", () => {
const src = chunks.join("");
const out = src
.replace(/([{(])/g, "$1\n ")
.replace(/([;}])/g, "\n$1\n")
.replace(/,\s*/g, ",\n ");
process.stdout.write(out);
});'
case "$VCS" in
jj)
pretty_old=$(jj file show "${file}@-" 2>/dev/null | node -e "$node_fmt" 2>/dev/null || true)
pretty_new=$(jj file show "$file" 2>/dev/null | node -e "$node_fmt" 2>/dev/null || true)
;;
git)
pretty_old=$(git show "HEAD:${file}" 2>/dev/null | node -e "$node_fmt" 2>/dev/null || true)
pretty_new=$(git show ":${file}" 2>/dev/null | node -e "$node_fmt" 2>/dev/null || true)
;;
esac
;;
*)
case "$VCS" in
jj)
pretty_old=$(jj file show "${file}@-" 2>/dev/null | fold -s -w 120 || true)
pretty_new=$(jj file show "$file" 2>/dev/null | fold -s -w 120 || true)
;;
git)
pretty_old=$(git show "HEAD:${file}" 2>/dev/null | fold -s -w 120 || true)
pretty_new=$(git show ":${file}" 2>/dev/null | fold -s -w 120 || true)
;;
esac
;;
esac
if [[ -n "$pretty_old" && -n "$pretty_new" ]]; then
diff <(printf '%s\n' "$pretty_old") <(printf '%s\n' "$pretty_new") \
--label "a/${file}" --label "b/${file}" -u || true
else
printf '%s' "$raw_diff"
fi
}
# --- Main ---
VCS=$(_detect_vcs)
if [[ "$VCS" == "none" ]]; then
echo "ERREUR : ni git ni jj détecté dans ce répertoire." >&2
exit 1
fi
log "VCS détecté : $VCS"
changed_files=$(_changed_files)
if [[ -z "$changed_files" ]]; then
echo "Aucun fichier modifié." >&2
exit 1
fi
file_count=$(wc -l <<< "$changed_files" | tr -d ' ')
log "Fichiers modifiés : $file_count"
summaries=""
n=0
while IFS= read -r file; do
diff=$(_readable_diff "$file")
[[ -z "$diff" ]] && continue
n=$((n + 1))
log "[$n/$file_count] Résumé de $file"
summary=$(printf '%s' "$diff" \
| aichat "In 2-3 lines, summarise what this diff changes in the file '$file'. Be concise and technical.")
while IFS= read -r line; do info "$line"; done <<< "$summary"
summaries+="### $file
$summary
"
done <<< "$changed_files"
if [[ -z "$summaries" ]]; then
echo "Aucun diff non-vide trouvé." >&2
exit 1
fi
log "Génération du message de commit ($n fichier(s)) …"
result=$(printf '%s' "$summaries" \
| aichat "From these per-file summaries of a diff, write a single conventional git commit message in English. First line: short imperative summary (max 72 chars). Then a blank line. Then a short paragraph with more detail if needed. Output only the commit message, nothing else.")
ok "Terminé"
printf '\n' >&2
printf '%s\n' "$result"