settings.json et hooks : configuration commentee
Les reglages qui t'evitent de valider dix fois la meme chose, et le garde-fou qui t'evite la catastrophe.
Temps de lecture : 12 min | Niveau : Intermediaire
Ce que tu sauras faire apres
- Savoir dans quel fichier de reglages mettre quoi, et lequel gagne en cas de conflit
- Reduire les demandes de permission sans tout autoriser aveuglement
- Installer un hook qui bloque une commande dangereuse avant son execution
- Formater automatiquement ton code apres chaque modification
Partie 1 - Les fichiers de reglages
Ou ils se trouvent, et qui gagne
Du plus prioritaire au moins prioritaire :
| Rang | Fichier | Qui le decide |
|---|---|---|
| 1 | Reglages geres par l'organisation | Ton entreprise |
| 2 | claude --settings | Toi, pour cette session |
| 3 | .claude/settings.local.json | Toi, pour ce projet, non partage |
| 4 | .claude/settings.json | Toute l'equipe du projet |
| 5 | ~/.claude/settings.json | Toi, pour tous tes projets |
Une cle definie au rang 3 ecrase la meme cle au rang 5. Retiens la regle : plus on est proche du projet, plus on est prioritaire.
.claude/settings.local.jsondoit etre dans ton.gitignore. C'est la qu'on met ce qui est propre a ta machine..claude/settings.json, lui, se versionne : c'est la configuration partagee de l'equipe.
Un settings.json utilisateur commente
Fichier : ~/.claude/settings.json (celui qui s'applique a tous tes projets)
{
"model": "opus",
"permissions": {
"defaultMode": "default",
"allow": [
"Read",
"Grep",
"Glob",
"Bash(git status *)",
"Bash(git diff *)",
"Bash(git log *)",
"Bash(ls *)",
"Bash(python -m pytest *)",
"Bash(ruff check *)",
"Bash(ruff format *)",
"Bash(dbt compile *)",
"Bash(dbt parse *)",
"Bash(dbt test *)"
],
"ask": [
"Bash(git push *)",
"Bash(pip install *)",
"Bash(npm install *)"
],
"deny": [
"Bash(rm -rf *)",
"Bash(dbt run --target prod *)",
"Bash(dbt build --target prod *)",
"Read(./.env)",
"Read(./.env.*)",
"Read(./**/credentials*.json)",
"Read(./**/*.pem)"
]
}
}Ce que fait chaque bloc :
allow: execute sans rien te demander. Mets-y uniquement des commandes de lecture ou d'analyse, jamais une commande qui ecrit en production.ask: demande confirmation a chaque fois, meme si tu es en mode permissif.deny: refuse purement et simplement. C'est le filet de securite.denygagne toujours surallow.- Les regles de type
Read(./.env)empechent Claude de lire tes secrets. Mets-les des maintenant.
Le caractere * fait office de joker : Bash(git diff *) autorise toutes les variantes de git diff, mais pas git push.
La bonne methode pour construire ton allow
Ne remplis pas cette liste a l'avance. Travaille une semaine normalement, note les commandes que tu autorises dix fois par jour, et ajoute celles-la uniquement.
Raccourci utile : quand Claude Code te demande une permission, la reponse du type « oui, et ne plus me demander » ecrit la regle toute seule dans .claude/settings.local.json.
Partie 2 - Les hooks
Le principe
Un hook est une commande de ta machine que Claude Code declenche automatiquement a un moment precis. Contrairement a une instruction du CLAUDE.md, un hook n'est pas une suggestion : c'est du code qui s'execute, toujours.
Les evenements les plus utiles :
| Evenement | Quand il se declenche | Usage typique |
|---|---|---|
PreToolUse | Avant l'execution d'un outil | Bloquer une commande dangereuse |
PostToolUse | Apres la reussite d'un outil | Formater, linter, lancer les tests |
UserPromptSubmit | Avant le traitement de ton message | Injecter du contexte, bloquer un envoi |
SessionStart | Au demarrage d'une session | Rappeler l'etat du projet |
Stop | Quand Claude a fini de repondre | Notification sonore ou visuelle |
Structure generale dans settings.json :
{
"hooks": {
"NomDeLEvenement": [
{
"matcher": "FiltreSurLOutil",
"hooks": [
{ "type": "command", "command": "...", "timeout": 10 }
]
}
]
}
}Le matcher filtre sur le nom de l'outil : "Bash", "Edit|Write", "mcp__memory__.*", ou "*" pour tout.
Le garde-fou anti-catastrophe
Le script. Fichier : .claude/hooks/garde-fou.py
#!/usr/bin/env python3
"""Bloque les commandes destructrices avant leur execution.
Claude Code envoie sur l'entree standard un JSON decrivant l'appel d'outil.
Code de sortie 2 = l'action est bloquee, et le message d'erreur remonte a Claude.
"""
import json
import re
import sys
INTERDITS = [
(r"\brm\s+(-[a-z]*r[a-z]*f|-[a-z]*f[a-z]*r)\b", "suppression recursive forcee"),
(r"--target[= ]\s*prod", "execution sur l'environnement de production"),
(r"\bDROP\s+(TABLE|DATABASE|SCHEMA)\b", "suppression d'objet de base de donnees"),
(r"\bTRUNCATE\s+TABLE\b", "vidage de table"),
(r"\bgit\s+push\s+.*--force\b", "push force"),
(r"\bgit\s+reset\s+--hard\b", "reinitialisation destructive"),
]
try:
donnees = json.load(sys.stdin)
except json.JSONDecodeError:
sys.exit(0) # entree illisible : on ne bloque pas
commande = donnees.get("tool_input", {}).get("command", "")
for motif, raison in INTERDITS:
if re.search(motif, commande, re.IGNORECASE):
print(
"Bloque par le garde-fou du projet : %s.\n"
"Si c'est volontaire, execute cette commande toi-meme dans ton terminal." % raison,
file=sys.stderr,
)
sys.exit(2)
sys.exit(0)Le branchement. Dans .claude/settings.json :
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python",
"args": ["${CLAUDE_PROJECT_DIR}/.claude/hooks/garde-fou.py"],
"timeout": 10,
"statusMessage": "Verification de la commande..."
}
]
}
]
}
}Comment le tester : demande a Claude de lancer git reset --hard HEAD~1. La commande doit etre refusee avec ton message.
Le formatage automatique apres modification
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "ruff",
"args": ["format", "."],
"timeout": 60,
"async": true
}
]
}
]
}
}async: true laisse le formatage tourner en arriere-plan sans bloquer la suite du travail.
Comment un hook bloque une action
| Code de sortie | Effet |
|---|---|
0 | Tout va bien. Si le hook a produit du JSON, ce JSON decide |
2 | Blocage. L'action n'a pas lieu, le message d'erreur remonte a Claude |
| autre | Non bloquant, l'action continue |
Deuxieme methode, plus fine : renvoyer du JSON sur la sortie standard.
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Operation interdite sur l'environnement de production"
}
}Trois regles de prudence
- Un hook s'execute avec tes droits, sans confirmation. Relis toujours un hook trouve sur internet avant de l'installer.
- Mets un
timeoutcourt. Un hook lent ralentit chacune de tes actions. - Commence par un hook qui ne fait qu'observer (afficher un message) avant d'en ecrire un qui bloque. Un hook trop zele est vite insupportable.
Template a remplir
{
"permissions": {
"allow": [
"[COMMANDES_DE_LECTURE_QUE_TU_AUTORISES_SOUVENT]"
],
"deny": [
"Read(./.env)",
"[COMMANDES_QUI_NE_DOIVENT_JAMAIS_PARTIR]"
]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python",
"args": ["${CLAUDE_PROJECT_DIR}/.claude/hooks/garde-fou.py"],
"timeout": 10
}
]
}
]
}
}A retenir
- Le reglage le plus proche du projet gagne ;
denygagne toujours surallow. - Interdis la lecture des fichiers de secrets des aujourd'hui, en une ligne.
- N'autorise automatiquement que des commandes de lecture.
- Un hook n'est pas une consigne, c'est du code qui s'execute : relis-le avant de l'installer.
- Code de sortie 2 = action bloquee.
Sources
- Claude Code - reglages et precedence
- Claude Code - hooks
- Claude Code - permissions et controle d'acces
- disler/claude-code-hooks-mastery - un script par evenement de hook dans
.claude/hooks/, avec lesettings.jsonqui les declare - ChrisWiles/claude-code-showcase - un
.claude/settings.jsonde production : formatage automatique, blocage des edits surmain