ServicesÀ proposNotesContact Me contacter →
EN FR
Note

Hooks stop et session Claude Code

Comment les hooks Stop et SessionStart complètent les hooks par outil — exécution de portes de qualité après la réponse de Claude et chargement du contexte de projet au démarrage de session

Planté
claude codedbtautomationdata quality

La note Hooks Claude Code couvre PreToolUse et PostToolUse — des hooks qui se déclenchent avant et après chaque action individuelle d’un outil. Ces hooks gèrent les préoccupations par opération : formater un fichier SQL après une modification, bloquer une commande dangereuse avant son exécution.

Deux autres points du cycle de vie des hooks servent des objectifs différents. Stop se déclenche lorsque Claude finit de répondre — après que toutes les modifications, commandes et raisonnements sont terminés pour ce tour. SessionStart se déclenche une fois au démarrage d’une session Claude Code. Ensemble, ils gèrent la validation de qualité en fin de tour et l’initialisation de session.

Hooks Stop : portes de qualité après chaque tour

Le hook Stop s’exécute après que la réponse complète de Claude est terminée. Pensez-y comme un mini-CI qui s’exécute localement, immédiatement après chaque tour. C’est le bon endroit pour les vérifications trop lentes ou trop larges pour s’exécuter après chaque modification individuelle de fichier, mais trop importantes pour être ignorées.

Le hook Stop le plus pratique pour le travail dbt : compiler les modèles modifiés pour détecter les erreurs de syntaxe avant que Claude ne passe à la suite.

Créez .claude/hooks/dbt-quality-check.sh :

#!/usr/bin/env bash
set -euo pipefail
# Check if any SQL files were modified
modified_sql=$(git diff --name-only HEAD 2>/dev/null | grep "\.sql$" || true)
if [[ -n "$modified_sql" ]]; then
echo "Checking modified models..." >&2
# Compile to catch syntax errors
if ! dbt compile --select state:modified 2>&1; then
echo "Compilation failed — check the errors above" >&2
exit 2 # Block so Claude can see and fix the issue
fi
# Lint (warning only, don't block)
sqlfluff lint $modified_sql --dialect bigquery 2>&1 || true
echo "All checks passed" >&2
fi

Enregistrez-le avec un matcher vide, ce qui signifie “déclencher à chaque stop, indépendamment des outils utilisés” :

{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/dbt-quality-check.sh"
}
]
}
]
}
}

Lorsque le hook quitte avec le code 2 (compilation échouée), Claude voit l’erreur immédiatement et peut la corriger dans le tour suivant. Lorsqu’il quitte avec le code 0 (toutes les vérifications réussies), Claude continue normalement. Le lint sqlfluff s’exécute comme avertissement — il ne bloque pas, mais Claude voit les problèmes de style dans la sortie.

Pourquoi Stop plutôt que PostToolUse ?

Vous pourriez exécuter dbt compile comme hook PostToolUse après chaque modification de fichier. Le problème est la vitesse. dbt compile --select state:modified prend quelques secondes même sur un petit projet. Si Claude modifie cinq fichiers SQL en un seul tour, cela représente cinq exécutions de compilation — chacune ajoutant de la latence au workflow de Claude.

Le hook Stop s’exécute une fois après que toutes les modifications sont terminées. Une seule vérification de compilation couvre tous les changements du tour entier. Le compromis : vous ne détectez pas les erreurs entre les modifications individuelles dans le même tour. Mais Claude verra les erreurs à la fin du tour et les corrigera, ce qui est généralement suffisant.

La règle générale : les vérifications rapides vont dans PostToolUse, les vérifications lentes vont dans Stop. Formater automatiquement un seul fichier avec sqlfluff est rapide — mettez-le dans PostToolUse. Compiler le projet dbt est plus lent — mettez-le dans Stop.

Extension de la porte de qualité

La vérification de compilation de base est un point de départ. Vous pouvez ajouter des couches de vérifications supplémentaires :

Exécuter des tests dbt sur les modèles modifiés pour détecter les problèmes de données, pas seulement les erreurs de syntaxe :

Terminal window
if [[ -n "$modified_sql" ]]; then
dbt test --select state:modified 2>&1 || true
fi

Gardez cela comme avertissement (pas un blocage) car les échecs de tests sur les données de développement peuvent être attendus lors des itérations.

Vérifier la documentation manquante lors de la création de nouveaux modèles :

Terminal window
new_models=$(git diff --name-only HEAD 2>/dev/null | grep "models/.*\.sql$" | while read f; do
model=$(basename "$f" .sql)
grep -rq "$model" models/ --include='*.yml' || echo "$f"
done)
if [[ -n "$new_models" ]]; then
echo "Warning: new models without YAML documentation:" >&2
echo "$new_models" >&2
fi

Hooks SessionStart : chargement du contexte

Les hooks SessionStart s’exécutent une fois lorsque vous commencez une session Claude Code. Leur objectif est différent des hooks Stop — ils n’appliquent pas la qualité, ils fournissent du contexte.

Un hook SessionStart pour les projets dbt peut vous montrer où vous en étiez :

#!/usr/bin/env bash
echo "" >&2
echo "dbt project context" >&2
echo "──────────────────────" >&2
# Current branch
branch=$(git branch --show-current 2>/dev/null || echo "unknown")
echo "Branch: $branch" >&2
# Modified models
modified=$(git diff --name-only HEAD 2>/dev/null | grep "models/.*\.sql$" | wc -l | tr -d ' ')
echo "Modified models: $modified" >&2
# Last run status
if [[ -f "target/run_results.json" ]]; then
failures=$(jq '[.results[] | select(.status == "error")] | length' target/run_results.json 2>/dev/null || echo "?")
echo "Last run failures: $failures" >&2
fi
echo "──────────────────────" >&2
echo "" >&2

La sortie va sur stderr, que Claude voit comme contexte au début de la conversation. C’est un petit détail, mais utile lorsqu’on passe d’un projet à l’autre ou qu’on reprend le travail le lendemain matin.

Idées pratiques pour SessionStart

Afficher les PR ouvertes pour la branche actuelle :

Terminal window
pr_count=$(gh pr list --head "$(git branch --show-current)" --json number --jq 'length' 2>/dev/null || echo "?")
echo "Open PRs for this branch: $pr_count" >&2

Afficher les packages dbt obsolètes :

Terminal window
if [[ -f "packages.yml" ]]; then
echo "Installed packages:" >&2
dbt deps --dry-run 2>&1 | grep -i "update" || echo "All packages up to date" >&2
fi

Afficher le statut récent du CI :

Terminal window
last_run=$(gh run list --limit 1 --json status,conclusion --jq '.[0] | "\(.status): \(.conclusion // "in progress")"' 2>/dev/null || echo "unknown")
echo "Last CI run: $last_run" >&2

Gardez les hooks SessionStart rapides. Ils s’exécutent au début de chaque session, et un hook lent signifie attendre plusieurs secondes avant de pouvoir commencer à travailler. Si une vérification prend plus d’une à deux secondes, elle n’a probablement pas sa place dans SessionStart.

Combiner les quatre types de hooks

Un .claude/settings.json complet pour un projet dbt pourrait utiliser les quatre hooks de cycle de vie ensemble :

{
"hooks": {
"SessionStart": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/dbt-context.sh"
}
]
}
],
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/dbt-safety.sh"
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/format-sql.sh"
}
]
}
],
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/dbt-quality-check.sh"
}
]
}
]
}
}

Chaque type de hook gère une préoccupation différente :

HookQuandObjectifExigence de vitesse
SessionStartDémarrage de sessionCharger le contexteRapide (s’exécute une fois)
PreToolUseAvant chaque actionBloquer les opérations dangereusesRapide (s’exécute par outil)
PostToolUseAprès chaque actionAuto-formatage, lintRapide (s’exécute par outil)
StopFin de tourCompiler, tester, validerPeut être plus lent (s’exécute une fois par tour)

La progression est délibérée : SessionStart vous oriente, PreToolUse prévient les erreurs, PostToolUse nettoie, et Stop valide l’ensemble du tour. Chaque couche capture ce que les autres manquent.