Mode headless : faire travailler Claude Code sans interface
Claude Code devient une commande : claude -p, sortie JSON, outils autorisés, intégration continue. Cas pratique : le dossier de dépôt qui se range tout seul. Module 11.
- Lancer Claude Code sans interface avec claude -p et exploiter sa sortie JSON
- Verrouiller une exécution sans surveillance avec allowedTools, permission-mode et max-turns
- Transformer une tâche répétitive en automatisation qui tourne seule
Jusqu’ici, tout passait par le dialogue : vous demandez, Claude propose, vous validez. Ce module coupe le dialogue. Claude Code devient une commande qu’on lance depuis un script — elle travaille, elle rend un résultat, elle s’arrête. C’est ce qui permet à une tâche de tourner la nuit, ou à chaque fichier déposé, sans personne devant l’écran.
Ce que vous allez apprendre
- Lancer Claude Code sans interface avec
claude -p, et le brancher sur d’autres commandes - Récupérer une sortie JSON qu’un script sait exploiter
- Verrouiller ce que Claude peut faire quand personne ne surveille
- Transformer une corvée hebdomadaire du parcours en automatisation autonome
- Les précautions : clé d’API, permissions, coûts
Une commande, pas une conversation
Headless signifie « sans tête » : sans interface interactive. L’option -p (pour print) active ce mode.
claude -p "Résume ce que contient ce dossier en trois phrases"
Une commande qui lit l’entrée standard et écrit sur la sortie standard : c’est la définition d’un outil en ligne de commande. Il se raccorde donc à tout le reste avec un tube — le symbole |, qui envoie la sortie d’une commande dans l’entrée de la suivante.
# Le journal d'erreurs part dans le tube, la réponse dans un fichier
cat erreurs.log | claude -p "explique la cause racine" > rapport.txt
| Session interactive | claude -p | |
|---|---|---|
| Qui valide les actions | Vous, à chaque étape | Personne : tout se déclare à l’avance |
| Durée de vie | La session entière | Un appel, puis fin |
| Sortie | Affichée à l’écran | Texte ou JSON, réutilisable |
| Contexte | Cumulé au fil du dialogue | Neuf à chaque appel |
La dernière ligne est un avantage : cent appels indépendants valent mieux qu’une conversation qui s’alourdit.
Une sortie que la machine comprend
Du texte libre convient à un lecteur humain, pas à un script. --output-format règle la question.
| Valeur | Ce que vous obtenez | Quand l’utiliser |
|---|---|---|
text (défaut) | La réponse brute | Lecture humaine, redirection simple |
json | Un objet : result (le texte), session_id, total_cost_usd | Dès qu’un script exploite la réponse |
stream-json | Un objet JSON par ligne, au fil de l’eau | Suivre une longue exécution en direct |
# jq lit du JSON en ligne de commande ; -r rend le texte sans guillemets
claude -p "Résume ce projet" --output-format json | jq -r '.result'
💡 Astuce —
total_cost_usddonne le coût exact de chaque appel. Journalisez-le systématiquement : vous saurez ce que coûte votre automatisation sans consulter aucun tableau de bord.
Verrouiller ce que personne ne surveille
En interactif, Claude demande avant d’agir (module 4). Sans interface, il n’y a personne pour répondre : tout se déclare sur la ligne de commande.
| Option | Effet | Exemple |
|---|---|---|
--allowedTools | Liste blanche stricte : seuls ces outils passent, le reste est refusé | --allowedTools "Read,Grep,Edit,Bash(npm run test:*)" |
--permission-mode | Le cadre global ; plan met en lecture seule | --permission-mode plan |
--max-turns | Plafond d’allers-retours : le coupe-circuit | --max-turns 5 |
Reste l’authentification : pas de connexion interactive dans un script. La clé d’API passe par la variable d’environnement ANTHROPIC_API_KEY, rangée dans le coffre à secrets de votre plateforme. Jamais dans le code, jamais dans un fichier versionné.
⚠️ Piège courant — Omettre
--allowedToolsne rend pas Claude tout-puissant, c’est l’inverse : dès qu’il tente une action non autorisée, l’exécution se bloque ou échoue. Votre script rend alors un résultat incomplet sans expliquer pourquoi. Déclarez ce dont la tâche a besoin, et rien de plus.
Cas pratique : le dossier de dépôt qui se range tout seul
La situation. Les papiers de famille sont triés depuis le module 4. Mais chaque semaine, de nouveaux scans tombent dans un dossier _a_classer/. Rouvrir l’application et redire les mêmes consignes n’a plus de sens : les règles ne changent plus.
Le principe. Un appel par fichier, et un partage des rôles strict : Claude lit le document et nomme le dossier de destination, le script déplace. Conséquence directe, --allowedTools "Read" suffit — aucun droit d’écriture, aucune commande.
#!/bin/bash
# classer.sh — un appel par fichier, le script decide du deplacement
for fichier in _a_classer/*; do
reponse=$(claude -p "Lis le document $fichier et applique les regles du fichier CLAUDE.md. Reponds uniquement par le nom du dossier de destination, sans phrase." \
--allowedTools "Read" --max-turns 3 --output-format json)
dossier=$(echo "$reponse" | jq -r '.result' | tr -d ' \r\n')
cout=$(echo "$reponse" | jq -r '.total_cost_usd')
case "$dossier" in
Identite|Domicile|Ecole|Impots) cible="classe/$dossier" ;;
*) cible="classe/A-verifier" ;;
esac
mkdir -p "$cible" && mv "$fichier" "$cible/"
echo "$(date +%F) | $fichier -> $cible | $cout" >> journal.txt
done
Le résultat. Chaque fichier atterrit quelque part, le journal garde la trace de la décision et de son coût, et tout ce que Claude n’a pas su classer part en « À vérifier » — jamais ailleurs. Le samedi matin, vous relisez cinq lignes de journal au lieu de trier trente scans.
Les pièges rencontrés :
- Une réponse bavarde casse la chaîne. « Je pense que ce document va dans Impots » ne correspond à aucun cas prévu. D’où la consigne « réponds uniquement par… » — et le filtre
case, qui ne fait jamais confiance à la sortie du modèle. - Les espaces dans les noms de fichiers. Toujours entre guillemets (
"$fichier"), sinon un scan nommé « carte grise.pdf » devient deux arguments et la commande échoue. - Le renommage reste manuel. Choisir un dossier est réversible ; renommer d’après le contenu mérite une relecture (module 1). Le script classe, vous renommez ensuite en session.
- Le CLAUDE.md fait le travail du prompt. Les règles de la famille vivent dans le fichier de mémoire du projet (module 6), pas dans le script : une règle qui change se corrige à un seul endroit.
Reste à le déclencher tout seul : le Planificateur de tâches sous Windows, cron sous macOS et Linux.
Dans une chaîne d’intégration continue
Une chaîne d’intégration continue exécute des tâches à chaque événement d’un dépôt de code, sur des machines louées à la minute. claude -p y tient sa place, et GitHub propose un câblage prêt à l’emploi : depuis une session Claude Code, à la racine du dépôt, /install-github-app installe l’application, ajoute le workflow et vous guide pour enregistrer la clé dans le secret ANTHROPIC_API_KEY. Il faut être administrateur du dépôt.
Ensuite, mentionner @claude dans une issue ou un commentaire de pull request déclenche l’action anthropics/claude-code-action, qui analyse le contexte et propose une correction.
name: Claude Code
on:
issue_comment:
types: [created]
jobs:
claude:
runs-on: ubuntu-latest
steps:
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
Le paramètre claude_args du workflow accepte les mêmes options que la ligne de commande, --max-turns en tête. Quatre principes valent pour toute exécution automatisée :
| Principe | En pratique |
|---|---|
| Isolation | Un conteneur jetable, sans vos données ni vos accès. C’est le seul contexte où --dangerously-skip-permissions se justifie |
| Permissions minimales | Une relecture se contente de --permission-mode plan ; l’écriture est réservée aux tâches qui en ont besoin |
| Budget | --max-turns, un délai maximum côté workflow, et --model : un tri simple n’a pas besoin du modèle le plus puissant (voir le comparatif des IA) |
| Journaux | Sorties JSON conservées : total_cost_usd pour les coûts, session_id pour retracer une exécution douteuse |
Votre CLAUDE.md versionné s’applique aussi sur la machine distante : les conventions suivent l’automatisation.
⚠️ Piège courant — Ne recopiez jamais
--dangerously-skip-permissionsd’un exemple d’intégration continue vers votre terminal. Sur votre machine, Claude exécuterait n’importe quelle commande sans validation. Conteneur jetable, oui ; poste de travail, jamais.
À vous de jouer
- Le premier appel sans interface. Dans un de vos dossiers :
claude -p "décris ce dossier en trois phrases" --output-format json | jq -r '.result'. Comparez avec la même demande dans l’application. - Sentez l’effet d’une liste blanche. Relancez avec
--allowedTools "Read"une demande qui exige d’écrire un fichier. Observez le blocage : c’est exactement ce qui protège vos exécutions nocturnes. - Écrivez
classer.shsur une copie de votre dossier de dépôt, avec vos propres noms de dossiers. Lisez le journal avant de regarder les fichiers. - Chiffrez la corvée. Totalisez la colonne de coût du journal après dix fichiers : vous connaissez le prix de votre semaine.
- Planifiez-le (Planificateur de tâches ou
cron) seulement quand le résultat est stable sur trois passages.
En résumé
claude -p "demande"fait de Claude Code une commande : elle lit un tube, écrit sur la sortie standard, puis rend la main.- Chaque appel repart d’un contexte neuf — c’est ce qui rend un traitement de masse fiable, fichier par fichier.
--output-format jsondonneresult(le texte),session_idettotal_cost_usd;stream-jsondétaille l’exécution ligne par ligne.- Sans surveillance, tout se déclare :
--allowedToolsen liste blanche,--permission-modepour le cadre,--max-turnsen coupe-circuit, la clé dansANTHROPIC_API_KEY. - Le modèle décide, le script exécute : les actions irréversibles restent dans votre code, avec un filtre sur la réponse et un journal horodaté.
/install-github-appcâble un dépôt GitHub ;--dangerously-skip-permissionsn’appartient qu’aux conteneurs jetables.
Module 12 : construire son propre agent avec le SDK — et savoir reconnaître les cas où une skill ou un hook suffit largement.
Testen Sie Ihr Wissen
Vous voulez qu'un script fasse analyser un fichier de journal par Claude, sans ouvrir de session. Que lancez-vous ?
Avec -p, Claude Code se comporte comme une commande classique : il lit ce qui arrive par le tube, applique la demande, imprime le résultat, puis se termine. Il s'enchaîne donc avec n'importe quelle autre commande.
Votre script doit récupérer uniquement le texte de la réponse pour le traiter automatiquement. Quelle est la bonne approche ?
L'option --output-format json enveloppe la réponse dans un objet : le texte dans le champ result, accompagné de session_id et total_cost_usd. Un outil comme jq extrait ensuite précisément le champ voulu — un script ne devrait jamais analyser du texte libre.
Dans le cas pratique, le script de classement tourne sans personne devant l'écran. Qu'est-ce qui empêche Claude de modifier ou de déplacer quoi que ce soit ?
Le partage des rôles est le cœur du garde-fou : Claude décide, le script exécute. Avec une liste blanche réduite à la lecture, aucune action irréversible ne peut venir du modèle. Le plafond de tours et le journal sont utiles, mais ce ne sont pas eux qui protègent les fichiers.
Dans quel cas l'option --dangerously-skip-permissions est-elle un choix raisonnable ?
Cette option supprime toutes les demandes de permission. Sur votre poste, Claude pourrait exécuter n'importe quelle commande. Dans un conteneur jetable, sans secrets superflus et détruit à la fin du travail, le pire scénario reste confiné : c'est là, et seulement là, qu'elle se justifie.
Vous voulez savoir exactement ce que vous coûte chaque exécution de votre automatisation. Comment procédez-vous ?
La sortie JSON contient le coût de l'appel dans total_cost_usd. Journalisé à chaque exécution, il vous donne le coût réel de votre automatisation, ligne par ligne, sans consulter aucun tableau de bord.