Gepeto : Orchestrer des Agents Claude Code avec Tmux et Python
Gepeto : Orchestrer des Agents Claude Code avec Tmux et Python
Combien de temps votre équipe perd-elle à jongler entre des tâches de code répétitives, des revues interminables et des pipelines de qualité à surveiller manuellement ? Selon GitHub, les développeurs passent en moyenne 45 % de leur temps sur des tâches qui pourraient être automatisées. C’est précisément ce constat qui nous a amenés à construire Gepeto — un orchestrateur open-source pour piloter plusieurs agents Claude Code en parallèle depuis une interface TUI Python.
L’Architecture Hub-and-Spoke : Un Cerveau, Des Bras
Le principe central de Gepeto est une architecture en étoile : un orchestrateur central (le hub) distribue les tâches à des agents enfants, chacun isolé dans sa propre session tmux. Les agents ne se parlent jamais directement — toute communication transite par l’orchestrateur.
┌──────────────┐
│ Orchestrateur│ (session tmux gepeto-dashboard)
│ (hub) │
└──┬───┬───┬───┘
│ │ │
▼ ▼ ▼
agent agent agent
(tmux sessions isolées) Chaque projet enfant reçoit :
- Une session tmux dédiée
- Un fichier
.mcp.jsonrestreint (filesystem limité au dossier projet, sans accès à/opt/workspace/gepeto/ni aux répertoires home) - Un
.claude/settings.jsonavec des règlesdenyexplicites :gepeto/**,rm -rf,sudo,kill,git push,git reset --hard
Cette isolation garantit qu’un agent buggé ou mal configuré ne peut pas casser l’environnement parent. Les ports applicatifs sont alloués dynamiquement par blocs de 10 à partir de 7450, référencés dans registry.json. L’API centrale tourne sur le port 7499 (GEPETO_PORT), et le système supporte jusqu’à 20 agents simultanés (MAX_AGENTS = 20).
PatternDetector : Savoir Si Claude Travaille par Hash MD5
Le premier défi technique a été de détecter l’état d’un agent Claude Code sans modifier Claude lui-même. Notre solution : lire la sortie brute de tmux capture-pane et la hacher.
class PatternDetector:
IDLE_AFTER_S = 3.0 # secondes sans changement → IDLE
_WORKING_RE = re.compile(
r"[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]|Running...",
re.IGNORECASE
)
def analyze(self, terminal_text: str) -> DetectorResult:
clean = _ANSI_RE.sub("", terminal_text)
normalized = " ".join(clean.split())
current_hash = hashlib.md5(
normalized.encode(), usedforsecurity=False
).hexdigest()
if current_hash != self._last_hash:
self._last_hash = current_hash
self._last_change_time = time.time()
# Spinner braille actif → outil en cours d'exécution
if self._WORKING_RE.search(terminal_text[-30:]):
return DetectorResult(action=Action.WAIT,
state="WORKING",
reason="Tool en cours")
idle_duration = time.time() - self._last_change_time
if idle_duration >= self.IDLE_AFTER_S:
return DetectorResult(action=Action.WAIT,
state="IDLE",
reason=f"Aucun changement depuis {idle_duration:.1f}s")
return DetectorResult(action=Action.WAIT, state="WORKING",
reason="Terminal actif") L’astuce clé : normaliser les espaces avant de hacher (" ".join(clean.split())). Un resize tmux peut modifier les sauts de ligne sans changer le contenu réel — sans cette normalisation, le detector signalerait faussement un changement à chaque redimensionnement de fenêtre.
Les spinners braille (⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏) indiquent que Claude exécute un outil. Quand ils apparaissent, le terminal peut être “gelé” du point de vue du hash, mais l’agent travaille effectivement — le regex _WORKING_RE capture ce cas.
Auto-Approve : 8 Règles pour Zéro Intervention Manuelle
Claude Code demande régulièrement des confirmations : “Voulez-vous lire ce fichier ?”, “Trust MCP tools ?”, “Y/n ?“. En mode production avec plusieurs agents, répondre manuellement à chacune est impossible. Gepeto embarque 8 règles d’auto-approve :
_AUTO_APPROVE_RULES: list[tuple[re.Pattern, str, str]] = [
(re.compile(r"❯s*d+.s*(Yes|Oui)", re.MULTILINE), "Enter",
"Menu TUI — accepter option par défaut"),
(re.compile(r"[Y/n]", re.IGNORECASE), "y",
"Confirmation Y/n"),
(re.compile(
r"Allow .* to (read|write|edit|create|execute|run|access|list|delete)",
re.IGNORECASE), "Enter",
"Permission lecture/écriture"),
(re.compile(r"Trust all tools from MCP", re.IGNORECASE), "Enter",
"Trust MCP tools"),
(re.compile(r"Resume Session (d+ of d+)", re.IGNORECASE), "Enter",
"Resume Session picker — sélectionner la première"),
# ... 3 autres règles
] Chaque règle est un triplet (pattern, touche à envoyer, raison). Le detector scanne les 30 dernières lignes du terminal — suffisant pour capturer tout dialogue interactif sans traiter l’intégralité du buffer. Quand une règle correspond, l’action SEND est retournée avec la touche appropriée, et le watcher l’injecte via tmux send-keys.
JsonlDetector : Lire les Vrais Événements Structurés
La détection par hash MD5 fonctionne, mais elle est aveugle au contenu. Pour savoir ce que fait Claude — quel fichier il lit, quel outil il appelle, combien de tokens il a consommés — nous avons développé JsonlDetector.
Claude Code écrit en continu un transcript JSONL dans ~/.claude/projects/. Notre detector le suit en temps réel :
@dataclass
class ActivityInfo:
tool_name: str = ""
tool_input: str = "" # résumé tronqué 80 chars
file_path: str = "" # chemin extrait si Read/Edit/Write
def __str__(self) -> str:
if self.file_path:
return f"{self.tool_name} {Path(self.file_path).name}"
return self.tool_input or self.tool_name
@dataclass
class TokenUsage:
input_tokens: int = 0
output_tokens: int = 0
cache_creation_tokens: int = 0
cache_read_tokens: int = 0
@property
def cost_usd(self) -> float:
# $3/M input, $15/M output, $3.75/M cache creation, $0.30/M cache read
return (
self.input_tokens * 3.0 / 1_000_000
+ self.output_tokens * 15.0 / 1_000_000
+ self.cache_creation_tokens * 3.75 / 1_000_000
+ self.cache_read_tokens * 0.30 / 1_000_000
) Les outils Task, Agent et AskUserQuestion sont exclus du calcul d’activité (_EXEMPT_TOOLS) car ils correspondent à de l’attente, pas à du travail effectif. Un outil sans tool_result depuis plus de 300 secondes est considéré comme un zombie et purgé (TOOL_TIMEOUT_S = 300.0).
Trois Modes de Fonctionnement
Chaque projet peut fonctionner dans l’un des trois modes stockés dans registry.json :
| Mode | Sur IDLE | Sur ASK | Qui pilote |
|---|---|---|---|
manual | Rien | Dashboard affiche | Utilisateur |
orchestrated | Notifie gepeto_main | Notifie gepeto_main | Orchestrateur Claude |
auto | Enchaîne next task | Bloque + affiche | Task tracker |
Le mode orchestrated est le plus puissant : quand un agent termine une tâche (IDLE détecté), le watcher envoie automatiquement une notification à la session tmux orchestrateur, qui peut alors décider de la suite. Les agents ne se connaissent pas — ils travaillent chacun dans leur coin pendant que l’orchestrateur coordonne.
Dashboard Textual : Tout Contrôler depuis un Terminal
L’interface TUI est construite avec Textual, le framework Python pour terminaux riches. Elle affiche en temps réel l’état de tous les agents (WORKING / IDLE / ASK / DEAD), l’activité courante extraite du JSONL (Read watcher.py, Edit constants.py…), et le coût cumulé en dollars.
Les raccourcis principaux :
j/k: naviguer entre les projetss: envoyer un message à l’agent sélectionnén: créer un nouveau projeto: toggle orchestrateurp: toggle watcherl: logs du watcher en live
L’API REST (port 7499) expose ces mêmes données en JSON pour intégrer Gepeto dans des dashboards externes ou des scripts de monitoring :
curl -s http://127.0.0.1:7499/api/v1/summary
# {"total":3,"working":2,"idle":1,"cost_usd":40.54,"top_agent":"demo-agent-2"} Ce Que Gepeto Nous a Appris
Construire un orchestrateur d’agents révèle des problèmes qu’on ne voit pas avec un seul agent :
- La détection d’état est non-triviale : un terminal tmux “stable” ne signifie pas que Claude est inactif — il peut attendre une réponse réseau.
- L’isolation est indispensable : sans
.mcp.jsonrestreint, un agent peut modifier les fichiers d’un autre ou consommer des ressources système critiques. - Le coût est visible : le suivi USD par agent change radicalement les décisions de conception — on évite les appels LLM inutiles quand on voit $0.30 disparaître à chaque exécution.
Gepeto est aujourd’hui utilisé en production pour orchestrer des pipelines de qualité (lint + tests + review), des agents de génération de contenu, et des workers d’annotation de datasets.
Conclusion
L’orchestration d’agents IA n’est plus réservée aux grandes entreprises avec des équipes MLOps dédiées. Avec des outils comme tmux, Python et Textual, il est possible de construire un système robuste qui pilote plusieurs instances Claude Code en parallèle, avec isolation, monitoring en temps réel et calcul de coût automatique.
Vous souhaitez mettre en place une infrastructure similaire pour votre équipe ? Contactez-nous sur contact@brio-novia.eu — nous aidons les entreprises à concevoir et déployer des architectures multi-agents adaptées à leurs besoins réels.