Retour aux articles
12 MIN READ

Claude Code Headless et Programmatique : SDK et Automatisation

By Learnia Team

Claude Code Headless et Programmatique : SDK et Automatisation

Le mode headless transforme Claude Code d'un outil interactif en un moteur d'automatisation programmable. Construisez des scripts, des pipelines et des applications qui exploitent les capacités de Claude sans intervention humaine.


Qu'est-ce que le Mode Headless ?

Le mode headless exécute Claude Code sans prompts interactifs :

Mode Interactif (Par Défaut)

$ claude
> Comment puis-je vous aider aujourd'hui ?
_

Mode Headless

$ claude --print -p "Analyse cette base de code" > report.md
$ echo $?  # Code de sortie : 0

Aucune interaction nécessaire. L'entrée est fournie, la sortie est produite.


Learn AI — From Prompts to Agents

10 Free Interactive Guides120+ Hands-On Exercises100% Free

Options CLI du Mode Headless

OptionDescription
--print / -pSortie vers stdout au lieu de l'interface interactive
--prompt / -pSpécifier le prompt (combiné avec --print)
--output-formatFormat de sortie : text, json, stream-json
--dangerously-skip-permissionsIgnorer tous les prompts de permission
--max-tokensLimiter les tokens de sortie
--modelSpécifier le modèle : sonnet, opus, haiku
--no-colorDésactiver la sortie colorée
--quiet / -qSupprimer les sorties non essentielles

Utilisation Basique du Mode Headless

Prompt Simple

claude --print -p "Explique cette fonction" < src/utils.ts

Sortie vers Fichier

claude --print -p "Génère la documentation API" > docs/api.md

Sortie JSON

claude --print --output-format json -p "Liste tous les commentaires TODO en tableau JSON"

Entrée par Pipe

cat error.log | claude --print -p "Analyse ce journal d'erreurs et suggère des correctifs"

Fichiers Multiples

claude --print -p "Compare ces implémentations" < <(cat file1.ts file2.ts)

Modèles d'Automatisation

Intégration dans les Scripts

#!/bin/bash
# analyze.sh

# Obtenir les fichiers modifiés
changed=$(git diff --name-only HEAD~1)

# Analyser chaque fichier
for file in $changed; do
  echo "Analyse de $file..."
  claude --print --quiet -p "Revois $file pour détecter les problèmes" > "reviews/${file}.md"
done

echo "Analyse terminée"

Gestion des Erreurs

#!/bin/bash

output=$(claude --print -p "Génère la migration" 2>&1)
exit_code=$?

if [ $exit_code -ne 0 ]; then
  echo "Erreur : $output"
  exit 1
fi

echo "$output" > migration.sql

Logique Conditionnelle

#!/bin/bash

# Demander à Claude de catégoriser
category=$(claude --print --output-format json \
  -p "Catégorise ce ticket. Retourne du JSON : {\"category\": \"bug|feature|docs\"}" \
  < issue.txt | jq -r '.category')

case $category in
  bug)
    echo "Routage vers le triage de bugs..."
    ;;
  feature)
    echo "Ajout au backlog de fonctionnalités..."
    ;;
  docs)
    echo "Attribution à l'équipe de documentation..."
    ;;
esac

Le SDK Anthropic

Pour une intégration plus poussée, utilisez le SDK officiel :

Installation

npm install @anthropic-ai/sdk

Utilisation Basique

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

async function main() {
  const message = await client.messages.create({
    model: "claude-sonnet-4-20250514",
    max_tokens: 1024,
    messages: [
      {
        role: "user",
        content: "Explique le concept d'injection de dépendances"
      }
    ]
  });
  
  console.log(message.content[0].text);
}

main();

Avec un Prompt Système

const message = await client.messages.create({
  model: "claude-sonnet-4-20250514",
  max_tokens: 1024,
  system: "Tu es un ingénieur logiciel senior. Fournis des conseils détaillés et pratiques.",
  messages: [
    { role: "user", content: "Comment structurer un projet de microservices ?" }
  ]
});

Réponses en Streaming

const stream = await client.messages.stream({
  model: "claude-sonnet-4-20250514",
  max_tokens: 1024,
  messages: [
    { role: "user", content: "Rédige un guide complet sur les tests" }
  ]
});

for await (const chunk of stream) {
  if (chunk.type === "content_block_delta" && chunk.delta.type === "text_delta") {
    process.stdout.write(chunk.delta.text);
  }
}

Conversations Multi-Tours

const conversation = [
  { role: "user", content: "Je construis une API REST en Node.js" },
  { role: "assistant", content: "Super ! Quel framework utilisez-vous ?" },
  { role: "user", content: "Express. Comment structurer mes routes ?" }
];

const response = await client.messages.create({
  model: "claude-sonnet-4-20250514",
  max_tokens: 1024,
  messages: conversation
});

Utilisation d'Outils avec le SDK

Claude peut utiliser des outils programmatiquement :

Définir les Outils

const tools = [
  {
    name: "get_weather",
    description: "Obtenir la météo actuelle pour un lieu",
    input_schema: {
      type: "object",
      properties: {
        location: {
          type: "string",
          description: "Nom de la ville"
        }
      },
      required: ["location"]
    }
  },
  {
    name: "search_code",
    description: "Rechercher des modèles dans la base de code",
    input_schema: {
      type: "object",
      properties: {
        query: { type: "string" },
        file_pattern: { type: "string" }
      },
      required: ["query"]
    }
  }
];

Exécuter les Outils

async function runWithTools(prompt: string) {
  let messages = [{ role: "user", content: prompt }];
  
  while (true) {
    const response = await client.messages.create({
      model: "claude-sonnet-4-20250514",
      max_tokens: 1024,
      tools,
      messages
    });
    
    // Vérifier si Claude veut utiliser un outil
    const toolUse = response.content.find(block => block.type === "tool_use");
    
    if (!toolUse) {
      // Plus d'outils, retourner la réponse finale
      return response.content.find(block => block.type === "text")?.text;
    }
    
    // Exécuter l'outil
    const result = await executeToolCall(toolUse.name, toolUse.input);
    
    // Ajouter le résultat de l'outil à la conversation
    messages.push({
      role: "assistant",
      content: response.content
    });
    messages.push({
      role: "user",
      content: [{
        type: "tool_result",
        tool_use_id: toolUse.id,
        content: result
      }]
    });
  }
}

async function executeToolCall(name: string, input: any): Promise<string> {
  switch (name) {
    case "get_weather":
      return JSON.stringify({ temp: 72, condition: "ensoleillé" });
    case "search_code":
      return `5 correspondances trouvées pour "${input.query}"`;
    default:
      return "Outil non trouvé";
  }
}

Traitement par Lots

Traitez efficacement plusieurs éléments :

Traitement Séquentiel

import { readdir, readFile, writeFile } from "fs/promises";

async function processFiles(directory: string) {
  const files = await readdir(directory);
  
  for (const file of files) {
    if (!file.endsWith(".ts")) continue;
    
    const content = await readFile(`${directory}/${file}`, "utf-8");
    
    const response = await client.messages.create({
      model: "claude-sonnet-4-20250514",
      max_tokens: 2048,
      messages: [{
        role: "user",
        content: `Génère des commentaires JSDoc pour ce fichier :\n\n${content}`
      }]
    });
    
    const documented = response.content[0].text;
    await writeFile(`${directory}/${file}`, documented);
    
    console.log(`Traité : ${file}`);
  }
}

Traitement Parallèle (avec limitation de débit)

import pLimit from "p-limit";

const limit = pLimit(5); // Maximum 5 requêtes simultanées

async function processFilesParallel(files: string[]) {
  const tasks = files.map(file =>
    limit(async () => {
      const content = await readFile(file, "utf-8");
      
      const response = await client.messages.create({
        model: "claude-sonnet-4-20250514",
        max_tokens: 1024,
        messages: [{
          role: "user",
          content: `Analyse : ${content}`
        }]
      });
      
      return { file, analysis: response.content[0].text };
    })
  );
  
  return Promise.all(tasks);
}

API Batch (pour les gros volumes)

// Pour les très gros lots, utilisez l'API Batch
const batch = await client.batches.create({
  requests: files.map((file, i) => ({
    custom_id: `file-${i}`,
    params: {
      model: "claude-sonnet-4-20250514",
      max_tokens: 1024,
      messages: [{ role: "user", content: `Analyse : ${file}` }]
    }
  }))
});

// Vérifier l'état de complétion
while (batch.status !== "completed") {
  await new Promise(r => setTimeout(r, 60000));
  batch = await client.batches.retrieve(batch.id);
}

// Obtenir les résultats
const results = await client.batches.results(batch.id);

Construction de Pipelines

Pipeline d'Analyse de Code

interface AnalysisResult {
  file: string;
  issues: Issue[];
  complexity: number;
  suggestions: string[];
}

async function analyzeCodebase(directory: string): Promise<AnalysisResult[]> {
  const files = await glob(`${directory}/**/*.ts`);
  const results: AnalysisResult[] = [];
  
  for (const file of files) {
    const content = await readFile(file, "utf-8");
    
    const response = await client.messages.create({
      model: "claude-sonnet-4-20250514",
      max_tokens: 2048,
      messages: [{
        role: "user",
        content: `Analyse ce fichier TypeScript et retourne du JSON :
          {
            "issues": [{"type": "string", "line": number, "message": "string"}],
            "complexity": number (1-10),
            "suggestions": ["string"]
          }
          
          Fichier : ${file}
          \`\`\`typescript
          ${content}
          \`\`\``
      }]
    });
    
    const analysis = JSON.parse(response.content[0].text);
    results.push({ file, ...analysis });
  }
  
  return results;
}

// Utiliser le pipeline
const analysis = await analyzeCodebase("./src");
const highComplexity = analysis.filter(r => r.complexity > 7);
console.log(`Fichiers nécessitant un refactoring : ${highComplexity.length}`);

Pipeline de Documentation

async function generateDocs(sourceDir: string, outputDir: string) {
  // Étape 1 : Analyser la structure
  const structure = await client.messages.create({
    model: "claude-sonnet-4-20250514",
    max_tokens: 2048,
    messages: [{
      role: "user",
      content: `Analyse ${sourceDir} et crée un plan de documentation`
    }]
  });
  
  // Étape 2 : Générer le README
  const readme = await client.messages.create({
    model: "claude-sonnet-4-20250514",
    max_tokens: 4096,
    messages: [{
      role: "user",
      content: `Génère un README.md basé sur : ${structure.content[0].text}`
    }]
  });
  
  await writeFile(`${outputDir}/README.md`, readme.content[0].text);
  
  // Étape 3 : Générer la référence API
  const apiFiles = await glob(`${sourceDir}/api/**/*.ts`);
  
  for (const file of apiFiles) {
    const content = await readFile(file, "utf-8");
    
    const doc = await client.messages.create({
      model: "claude-sonnet-4-20250514",
      max_tokens: 2048,
      messages: [{
        role: "user",
        content: `Génère la documentation API pour :\n${content}`
      }]
    });
    
    const docPath = file.replace(sourceDir, outputDir).replace(".ts", ".md");
    await writeFile(docPath, doc.content[0].text);
  }
}

Gestion des Erreurs et Retentatives

Appels API Robustes

import { setTimeout } from "timers/promises";

async function callWithRetry<T>(
  fn: () => Promise<T>,
  maxRetries = 3,
  baseDelay = 1000
): Promise<T> {
  let lastError: Error;
  
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error as Error;
      
      // Ne pas réessayer les erreurs de validation
      if (error.status === 400) throw error;
      
      // Backoff exponentiel
      const delay = baseDelay * Math.pow(2, attempt);
      console.log(`Tentative ${attempt + 1} échouée, nouvelle tentative dans ${delay}ms`);
      await setTimeout(delay);
    }
  }
  
  throw lastError;
}

// Utilisation
const response = await callWithRetry(() =>
  client.messages.create({
    model: "claude-sonnet-4-20250514",
    max_tokens: 1024,
    messages: [{ role: "user", content: prompt }]
  })
);

Gestion de la Limitation de Débit

import Bottleneck from "bottleneck";

const limiter = new Bottleneck({
  reservoir: 100,      // 100 requêtes
  reservoirRefreshAmount: 100,
  reservoirRefreshInterval: 60 * 1000, // par minute
  maxConcurrent: 5
});

async function rateLimitedCall(prompt: string) {
  return limiter.schedule(() =>
    client.messages.create({
      model: "claude-sonnet-4-20250514",
      max_tokens: 1024,
      messages: [{ role: "user", content: prompt }]
    })
  );
}

Modèles de Production

Gestion de Configuration

// config.ts
interface Config {
  model: string;
  maxTokens: number;
  temperature: number;
  systemPrompt: string;
}

const configs: Record<string, Config> = {
  analysis: {
    model: "claude-sonnet-4-20250514",
    maxTokens: 2048,
    temperature: 0,
    systemPrompt: "Tu es un expert en analyse de code. Sois minutieux et précis."
  },
  creative: {
    model: "claude-sonnet-4-20250514",
    maxTokens: 4096,
    temperature: 0.7,
    systemPrompt: "Tu es un rédacteur technique créatif."
  },
  fast: {
    model: "claude-3-5-haiku-20241022",
    maxTokens: 512,
    temperature: 0,
    systemPrompt: "Sois concis."
  }
};

async function query(prompt: string, configName: keyof typeof configs) {
  const config = configs[configName];
  
  return client.messages.create({
    model: config.model,
    max_tokens: config.maxTokens,
    temperature: config.temperature,
    system: config.systemPrompt,
    messages: [{ role: "user", content: prompt }]
  });
}

Journalisation et Monitoring

import { createLogger, transports, format } from "winston";

const logger = createLogger({
  level: "info",
  format: format.combine(
    format.timestamp(),
    format.json()
  ),
  transports: [
    new transports.File({ filename: "claude.log" })
  ]
});

async function trackedQuery(prompt: string, metadata: Record<string, any>) {
  const startTime = Date.now();
  
  try {
    const response = await client.messages.create({
      model: "claude-sonnet-4-20250514",
      max_tokens: 1024,
      messages: [{ role: "user", content: prompt }]
    });
    
    logger.info("Appel API réussi", {
      ...metadata,
      duration: Date.now() - startTime,
      inputTokens: response.usage.input_tokens,
      outputTokens: response.usage.output_tokens
    });
    
    return response;
  } catch (error) {
    logger.error("Appel API échoué", {
      ...metadata,
      duration: Date.now() - startTime,
      error: error.message
    });
    throw error;
  }
}

Mise en Cache

import { createHash } from "crypto";
import { Redis } from "ioredis";

const redis = new Redis();

function hashPrompt(prompt: string, model: string): string {
  return createHash("sha256")
    .update(`${model}:${prompt}`)
    .digest("hex");
}

async function cachedQuery(prompt: string, ttl = 3600) {
  const cacheKey = hashPrompt(prompt, "claude-sonnet-4-20250514");
  
  // Vérifier le cache
  const cached = await redis.get(cacheKey);
  if (cached) {
    return JSON.parse(cached);
  }
  
  // Faire l'appel API
  const response = await client.messages.create({
    model: "claude-sonnet-4-20250514",
    max_tokens: 1024,
    messages: [{ role: "user", content: prompt }]
  });
  
  // Mettre en cache le résultat
  await redis.setex(cacheKey, ttl, JSON.stringify(response));
  
  return response;
}

Cas d'Utilisation

Service de Revue de Code Automatisée

// review-service.ts
import express from "express";
import { Anthropic } from "@anthropic-ai/sdk";

const app = express();
const client = new Anthropic();

app.post("/review", async (req, res) => {
  const { code, language, rules } = req.body;
  
  const response = await client.messages.create({
    model: "claude-sonnet-4-20250514",
    max_tokens: 2048,
    system: `Tu es un réviseur de code. Revois le code ${language} en suivant ces règles : ${rules}`,
    messages: [{
      role: "user",
      content: `Revois ce code :\n\`\`\`${language}\n${code}\n\`\`\``
    }]
  });
  
  res.json({
    review: response.content[0].text,
    tokens: response.usage
  });
});

app.listen(3000);

Générateur de Documentation

// doc-generator.ts
async function generateModuleDocs(modulePath: string) {
  const files = await glob(`${modulePath}/**/*.ts`);
  const docs: string[] = [];
  
  for (const file of files) {
    const content = await readFile(file, "utf-8");
    
    const response = await client.messages.create({
      model: "claude-sonnet-4-20250514",
      max_tokens: 2048,
      messages: [{
        role: "user",
        content: `Génère la documentation markdown pour :\n${content}`
      }]
    });
    
    docs.push(`## ${file}\n\n${response.content[0].text}`);
  }
  
  return docs.join("\n\n---\n\n");
}

Générateur de Tests

// test-generator.ts
async function generateTests(sourceFile: string, testFramework = "jest") {
  const source = await readFile(sourceFile, "utf-8");
  
  const response = await client.messages.create({
    model: "claude-sonnet-4-20250514",
    max_tokens: 4096,
    system: `Génère des tests ${testFramework} complets. Inclus les cas limites et les scénarios d'erreur.`,
    messages: [{
      role: "user",
      content: `Génère des tests pour :\n\`\`\`typescript\n${source}\n\`\`\``
    }]
  });
  
  const testFile = sourceFile.replace(".ts", ".test.ts");
  await writeFile(testFile, response.content[0].text);
  
  return testFile;
}

Intégration avec les Fonctionnalités de Claude Code

Utiliser les Skills Programmatiquement

// Charger les définitions de skills
const skills = await loadSkills(".claude/skills");

async function runSkill(skillName: string, input: any) {
  const skill = skills[skillName];
  
  return client.messages.create({
    model: "claude-sonnet-4-20250514",
    max_tokens: 4096,
    system: skill.systemPrompt,
    messages: [{
      role: "user",
      content: `Exécute le skill "${skillName}" avec :\n${JSON.stringify(input)}`
    }]
  });
}

Voir Agent Skills dans Claude Code : Étendez les Capacités de Claude.

MCP Programmatique

import { MCPClient } from "@modelcontextprotocol/sdk";

const mcp = new MCPClient();
await mcp.connect("github", "https://api.github.com/mcp/");

// Utiliser les outils MCP dans les appels SDK
const tools = await mcp.listTools();

Voir Model Context Protocol (MCP) pour Claude Code : Guide Complet.


Points Clés à Retenir

  1. --print pour le scripting : Option essentielle pour l'automatisation headless.

  2. SDK pour les workflows complexes : Contrôle total avec le SDK Anthropic.

  3. Gérer les erreurs avec élégance : Implémentez des retentatives et la limitation de débit.

  4. Mettre en cache quand c'est possible : Réduisez les coûts et la latence.

  5. Monitorer en production : Journalisez l'utilisation, les erreurs et les performances.


Construisez des Systèmes IA en Production

Le mode headless de Claude Code est la base des systèmes IA en production. Apprenez à construire des workflows IA fiables et évolutifs.

Dans notre Module 6 — Agents Autonomes, vous apprendrez :

  • Architecture des systèmes IA en production
  • Modèles de fiabilité
  • Stratégies de mise à l'échelle
  • Monitoring et observabilité

Explorer le Module 6 : Agents Autonomes

GO DEEPER — FREE GUIDE

Module 6 — AI Agents & ReAct

Create autonomous agents that reason and take actions.

Newsletter

Weekly AI Insights

Tools, techniques & news — curated for AI practitioners. Free, no spam.

Free, no spam. Unsubscribe anytime.

FAQ

Qu'est-ce que le mode headless dans Claude Code ?+

Le mode headless exécute Claude Code sans interface terminal interactive, permettant le scripting, l'intégration CI/CD et les pipelines automatisés. Utilisez 'claude --print' ou le SDK pour un accès programmatique.

Comment utiliser Claude Code dans des scripts ?+

Utilisez 'claude -p "votre prompt" --print' pour un scripting simple. Pour une automatisation complexe, utilisez le SDK TypeScript/Python avec des réponses en streaming et le contrôle de l'exécution des outils.

Claude Code peut-il être utilisé dans des pipelines CI/CD ?+

Oui. Claude Code s'intègre avec GitHub Actions, GitLab CI, Jenkins via le mode headless. Usages courants : revue de code automatisée, descriptions de PR, génération de tests et documentation.

Quelle est la différence entre le SDK Claude Code et l'API ?+

Le SDK encapsule les capacités d'agent de Claude Code (accès fichiers, outils, mémoire). L'API directe donne un accès brut au modèle. Utilisez le SDK pour les tâches de codage ; l'API pour les intégrations personnalisées.