Summoner - Orchestrateur Claude Code

Cinq terminaux ouverts, cinq sessions Claude Code, cinq contextes perdus. La frustration de switcher entre les projets m'a poussé à construire un control center web pour tout centraliser. Et l'IA qui l'orchestre peut s'orchestrer elle-même.

III - Croissance 7 jours 2026-03-10 2026-03-16
XP
95%

Genèse

Cinq terminaux ouverts, cinq sessions Claude Code en parallèle, cinq contextes à garder en tête. En bossant sur ComRenov, Cloud, Totem et Tordu Jardin en même temps, le constat s’impose : on perd plus de temps à switcher entre les terminaux qu’à réfléchir au code. Quel projet était sur quelle branche ? Quelle session avait encore du contexte ? Combien on a dépensé en tokens aujourd’hui ?

Le dimanche soir, après avoir fermé accidentellement un terminal qui contenait 45 minutes de contexte Claude, la décision est prise. Il faut un control center web. Un seul onglet pour voir tous les projets, lancer des sessions, suivre leur output en temps réel, et surtout ne jamais perdre un contexte. Et tant qu’à faire, construire cet outil avec Claude Code, l’outil qui orchestre son propre orchestrateur.

Le plan tient sur un post-it physique. Pas un Notion, pas un Jira. Un post-it qu’on barre au marqueur avec satisfaction. 10 waves numérotées, du scaffold au deploy. Le pari : un week-end. Premier commit le 10 mars à 16h48.

Stack

La question React vs le reste s’est posée pendant exactement vingt minutes. Pour 28 composants sans routing, sans state management global, sans SSR, React était une massue pour enfoncer un clou. Lit 3 pèse six kilooctets, fait des web components natifs avec shadow DOM, et ne demande rien en retour. Côté backend, Fastify 5 parce que je le connais bien depuis Où-est-mon-doc, que le WebSocket est intégré, et que c’est bootstrappable en dix minutes. SQLite en WAL mode pour la persistence : un fichier unique, zéro config, lectures concurrentes gratuites. Le tout pèse 180 Mo de base de données après une semaine d’usage intensif.

Le vrai choix technique, celui qui a conditionné tout le reste, c’était node-pty. C’est la seule manière fiable de spawner un process Claude CLI et de capturer son output en streaming. Pas un child_process.spawn, pas un exec, un vrai pseudo-terminal qui se comporte comme si quelqu’un tapait dans un shell. Sans ça, pas de Summoner.

Le reste de la stack suit la même logique : xterm.js pour le rendu terminal dans le browser, Biome pour le lint (un seul binaire qui remplace ESLint et Prettier), Vitest et Playwright pour les tests.

Comment ça marche

On ouvre localhost:7777. Summoner a déjà scanné ~/lab et repéré tous les projets qui ont un dossier .claude/. On voit tout d’un coup d’oeil : nom, branche git, dernière activité, coût cumulé en dollars.

Dashboard Summoner

On clique sur un projet, on lance une session. L’output de Claude apparaît en temps réel dans un panneau de chat avec coloration syntaxique. Les tool calls sont extraites et affichées à part, on voit ce que Claude lit, ce qu’il exécute, quels diffs il génère. Si on active le mode “Don’t stop”, Summoner relance automatiquement quand Claude s’arrête pour demander confirmation. On peut reprendre une session existante, exporter le transcript en Markdown, ou basculer en vue terminal brute pour le debug.

À côté du chat, un kanban organise les tâches par projet avec du drag-drop. Les cartes sont vivantes, un spinner tourne sur celles en cours d’exécution. On peut empiler des tâches dans une queue, les regrouper en epics, ou les enchaîner dans des workflows YAML. Les workflows fonctionnent comme un petit pipeline CI/CD pour IA : prompt Claude, commande shell, requête HTTP, condition, gate d’approbation manuelle. Les étapes indépendantes tournent en parallèle.

Depuis le 16 mars, un moteur d’alertes surveille en continu les sessions et les coûts. Un moteur de recommandations analyse les patterns d’usage et propose des suggestions. C’est le genre de fonctionnalité qu’on ajoute quand on commence à utiliser son propre outil au quotidien et qu’on se dit “tiens, ça serait bien si…”.

Architecture

Le monorepo tient en quatre packages. Le core contient toute la logique métier : session manager, task queue, workflow engine, scanner de projets, store SQLite, alertes, recommandations. Aucune dépendance réseau, testable en isolation. Le daemon est un serveur Fastify qui consomme le core et expose tout en REST et WebSocket. Le web regroupe les 28 composants Lit, bundlés par Vite, chacun encapsulé dans son shadow DOM, communiquant par custom events, sans store centralisé. Et le mcp fait le pont avec le Model Context Protocol pour que Claude Code puisse piloter Summoner comme un outil natif.

Le daemon fait 3 517 lignes dans un seul fichier. C’est un choix délibéré du premier jour : la priorité c’était la vitesse, pas l’architecture. Le passage en API versionnée le 16 mars a posé les fondations pour découper proprement, mais ce n’est pas encore fait.

Le MCP : quand l’orchestrateur s’orchestre lui-même

Le premier test valait le détour. Je demande à Claude Code : “Lance les tests sur un projet, puis déploie le jardin si tout passe.” Claude appelle le MCP pour démarrer une session de tests. Le dashboard affiche la session qui démarre. Les tests tournent, le WebSocket streame l’output. Les tests passent, Claude lance le déploiement. Deux projets orchestrés, zéro intervention humaine. On regarde le truc se faire tout seul en buvant son café.

Le risque évident : la récursion. Un workflow qui lance une session Claude, qui décide de lancer une autre session, qui en lance une autre. Chaque session spawne un process node-pty. Sans garde-fou, ça explose. Summoner impose une profondeur maximale de récursion, au-delà il refuse de lancer et retourne une erreur. Sans cette limite, la première boucle accidentelle transforme le laptop en radiateur.

La boucle complète : Claude Code, dans une session Summoner, utilise le serveur MCP de Summoner pour lancer une nouvelle session Claude Code dans un autre projet. Le serpent qui se mord la queue, mais chaque morsure produit du code fonctionnel.

Les cailloux dans le terreau

Le parsing du stream JSON

Le plus gros caillou. Le format --output-format stream-json de Claude CLI découpe les events en lignes JSON. Simple en théorie. En pratique, trois itérations pour comprendre la structure réelle.

Premier piège : les tool_use ne sont pas au top-level de l’event. Ils sont enfouis dans message.content[], dans un tableau où chaque élément peut être du texte ou un tool call. Le parser initial cherchait un champ tool_use à la racine et ne trouvait rien.

Deuxième piège : les longues lignes JSON sont coupées entre deux lectures du pipe. Un objet JSON de 8 Ko arrive en deux morceaux. La première moitié n’est pas du JSON valide. Le JSON.parse() explosait en silence et on perdait des events.

La solution : un lineBuffer de 10 Mo max qui accumule les fragments et ne parse que quand il détecte un saut de ligne complet. Trois heures pour arriver à vingt lignes de code. C’est là le ratio réel du développement.

Les sessions zombies

startOneshot() retournait la session avant que le spawn soit valide. Le code appelant pouvait supposer que la session était vivante alors que le process n’existait pas encore. Il a fallu un flag busy, un waitForSessionEnd, et un double-emit session:end (sur error ET sur close) pour stabiliser le lifecycle. Un ghost reaper tourne toutes les 5 minutes pour nettoyer les sessions orphelines, celles dont le process a crash sans émettre de signal propre.

C’est le genre de bug qui n’apparaît pas dans les tests unitaires. Il faut un vrai dashboard avec de vrais utilisateurs qui cliquent vite pour le voir.

Le WebSocket silencieux

Quand une session était occupée à traiter un prompt, les messages suivants étaient enfilés dans une queue côté serveur. Normal. Sauf que personne ne les envoyait jamais. L’utilisateur tapait un deuxième message, puis un troisième, et rien ne se passait. Pas d’erreur, pas de feedback, juste le silence.

La correction tenait en deux parties : une queue FIFO avec drain automatique quand la session repasse en idle, et un indicateur visuel “session busy” côté UI. Le genre de truc évident quand on le dit, invisible quand on code à 22h un dimanche.

Le rôle de Claude Code

Sur les 66 fichiers TypeScript, Claude en a généré environ 60 en première passe. Mais “générer” ne veut pas dire “terminer”. Chaque fichier a nécessité des ajustements : renommages, corrections de types, refactoring des imports, suppression de code mort.

Le rôle humain était celui d’architecte-revieweur. Décrire la structure voulue, laisser Claude Code produire le code, tester, corriger, relancer. Le ratio approximatif : 30% du temps à décrire, 20% à reviewer, 50% à debugger ce qui ne marchait pas comme prévu.

La partie où Claude Code excelle : le boilerplate. Les routes CRUD, les types TypeScript, les web components avec leurs propriétés réactives. La partie où il galère : l’intégration. Faire marcher node-pty avec le stream JSON, gérer les edge cases du WebSocket, comprendre pourquoi une session spawne mais ne répond pas. Ça, c’est du debug humain.

Le vrai gain de productivité, ce n’est pas d’aller vite. C’est de pouvoir se concentrer sur l’architecture et les décisions pendant que quelqu’un d’autre tape le code répétitif. Un changement de posture, pas de vitesse.

Journal

2026-03-16 : Refonte architecturale : erreurs, alertes, plans, recommandations

Le prototype fonctionnait, mais en aveugle. Quand une session plantait, on le voyait… si on regardait le bon terminal au bon moment. Quand les tokens filaient, on le découvrait sur la facture. Il manquait les fondations pour que l’outil soit réellement fiable.

Le core se dote d’erreurs structurées RFC 7807 (codes machine-readable, fini les messages opaques), d’un logger contextuel avec propagation de requestId et sessionId (fini les console.log éparpillés), et de circuit breakers sur le model routing (fallback automatique quand un modèle timeout, protection contre les cascades de pannes).

L’alert engine surveille en continu : sessions trop longues, coûts qui dépassent les seuils, erreurs récurrentes. Le recommendation engine analyse les patterns d’usage et signale les projets dormants, les sessions à optimiser, les workflows candidats à l’automatisation. L’idée : ne plus avoir à surveiller le dashboard soi-même, laisser l’outil prévenir quand quelque chose dérape.

Côté web, simplification : le kanban et la session list sont allégés, un plan-review et un project-manager apparaissent pour donner une vue d’ensemble par projet. Summoner passe de prototype fonctionnel à outil instrumenté.

2026-03-13 : Fusion Board, MCP, epics

Deux vues séparées pour gérer les tâches et les opérations, c’était un onglet de trop. Elles fusionnent en une vue Board unique avec un run panel collapsible qui affiche en temps réel les sessions actives et un aperçu du chat. Les cartes kanban deviennent vivantes : spinner sur les tâches en cours, badge WAITING, durée de la session liée.

Le serveur MCP arrive dans cette itération. L’idée : permettre à Claude Code de piloter Summoner comme un outil natif. Lister les projets, créer une tâche, lancer un workflow, le tout depuis une conversation Claude. On boucle la boucle : l’IA orchestre son propre orchestrateur.

Les epics regroupent les tâches par objectif, parce que des tickets isolés sans vision d’ensemble, ça ne sert à rien.

2026-03-11 : Nuit blanche : sessions zombies et workflows

La nuit a commencé par trois bugs qui rendaient le dashboard inutilisable en usage réel. Le flag busy qui ne se réinitialisait pas au stop : une session avait l’air bloquée alors que le process était mort. Les messages envoyés dans le vide pendant qu’une session était occupée : l’utilisateur tapait, rien ne se passait, pas d’erreur, juste le silence. Le session-manager qui ne gérait pas les états stuck. Trois bugs liés au même problème : le lifecycle d’une session PTY est plus subtil qu’un simple start/stop.

Ensuite, le workflow engine : le besoin de pouvoir enchaîner des étapes automatisées sans intervention. Prompt Claude, commande shell, requête HTTP, sleep, condition, gate d’approbation. Un pipeline CI/CD pour IA, en quelque sorte.

Le matin, le blue-green deploy : pouvoir redémarrer le daemon sans tuer les sessions en cours. Nouvelle instance sur un port temporaire, migration des sessions actives via l’API de handoff, zero downtime. Parce que perdre une session de 30 minutes à cause d’un redeploy, c’est exactement le problème que Summoner était censé résoudre.

2026-03-10 : Jour zéro : le post-it et le scaffold

Le plan tenait sur un post-it physique. Pas un Notion, pas un Jira. Un post-it qu’on barre au marqueur. 10 waves numérotées, du scaffold au deploy.

L’après-midi : le scaffold du monorepo TypeScript strict (core, daemon, web), le session manager avec node-pty, le dashboard 3 colonnes. La disposition est simple : liste des projets à gauche (auto-découverte des dossiers .claude/ dans ~/lab), sessions actives au centre, chat temps réel à droite.

Le soir : le resume mode picker (pour ne plus jamais perdre un contexte de session, le problème fondateur), la refonte mobile avec navigation 4 onglets, et les bases de l’UI. Le plus dur n’était pas de coder vite, c’était de ne pas se disperser. Le post-it a aidé.

Ce que j’en retiens

Construire un outil complet en un week-end, c’est possible quand on connaît bien sa stack et qu’on a un plan précis. Les 10 waves sur le post-it ont évité l’éparpillement. Claude Code a accéléré le boilerplate d’un facteur 5 ou 10, mais les bugs d’intégration restent des bugs d’intégration, il faut les comprendre soi-même.

La récursion en IA a les mêmes risques que la récursion en programmation : sans condition d’arrêt, ça explose. Le contrôle de profondeur n’est pas optionnel, c’est une décision architecturale fondamentale.

C’est aussi l’outil qui a orchestré le développement de Basalt Beholder et la génération des fiches de Tordu Jardin. Les outils les plus intéressants sont ceux qui peuvent servir à se construire eux-mêmes. Summoner orchestrant Claude Code qui améliore Summoner, c’est le genre de boucle vertueuse qui, avec les bons garde-fous, accélère tout ce qu’elle touche.

Architecture

Lit 3 SPA (:7778 dev)
REST + WebSocketFastify 5 (:7777)
Fastify 5 (:7777)
node-pty spawnClaude CLI (node-pty)better-sqlite3SQLite WAL (~80 Mo)
Claude CLI (node-pty)
SQLite WAL (~80 Mo)
MCP Server
MCP protocolFastify 5 (:7777)
Scanner ~/lab
Auto-détection projetsSQLite WAL (~80 Mo)

Stack technique

Lit 3

Web components SPA

Léger, pas besoin d'un framework lourd pour un dashboard

Fastify 5

API REST + WebSocket

Temps réel pour streamer les sessions Claude

node-pty

Spawn de sessions Claude CLI

Seul moyen fiable de capturer le stream JSON en temps réel

SQLite WAL

Persistence locale

Un fichier, zéro config, lectures concurrentes

Dashboard 3 colonnes - projets, sessions actives, chat temps réel avec tool calls

Dashboard 3 colonnes - projets, sessions actives, chat temps réel avec tool calls

Vue complète : kanban, workflows, session transcript avec syntax highlighting

Vue complète : kanban, workflows, session transcript avec syntax highlighting

Vue mobile responsive avec navigation 4 onglets

Vue mobile responsive avec navigation 4 onglets

Dashboard 3 colonnes - projets, sessions actives, chat temps réel avec tool callsVue complète : kanban, workflows, session transcript avec syntax highlightingVue mobile responsive avec navigation 4 onglets
Refonte architecturale : erreurs, alertes, plans, recommandations+10 commits

Le prototype fonctionnait, mais sans filet. Pas de vrais codes d'erreur, pas de logs structurés, pas de visibilité sur ce qui se passait mal. Cette refonte ajoute les fondations manquantes : erreurs typées RFC 7807, alertes automatiques quand une session dérape, circuit breakers pour survivre aux pannes API, et un moteur de recommandations qui signale les projets dormants ou les sessions à optimiser.

  • Erreurs structurées RFC 7807 : NotFoundError, ValidationError, ConflictError avec codes machine-readable
  • Logger contextuel : propagation de context (requestId, sessionId), niveaux configurables par module
  • Alert engine : détection automatique de sessions longues, coûts élevés, erreurs récurrentes, avec seuils configurables
  • Plan detector : analyse des fichiers PLAN.md / TODO.md des projets, extraction des phases et progression
  • Types enrichis : plans, alertes, contexte projet, métadonnées checklist étendues dans le store
  • Model routing + circuit breakers : fallback automatique entre modèles, protection contre les cascades de timeout
  • Session title generation : titre automatique basé sur le premier prompt de la session
  • API v1 versioning : toutes les routes sous /api/v1, RFC 7807 sur les erreurs HTTP, tests étendus
  • Web : plan-review, project-manager, kanban simplifié, session list allégée, panneau de recommandations
  • Recommendation engine : suggestions proactives (santé projet, optimisations, patterns détectés)
  • Simplification du code web : suppression des vues obsolètes, remplacement par des composants allégés
Fusion Board, MCP, epics+2 commits

Deux vues séparées (Tasks et Operations) pour faire la même chose, c'était un onglet de trop. Fusion en une vue Board unique avec le moniteur de sessions intégré. Les epics arrivent pour regrouper les tâches par objectif, parce que des tickets isolés sans vision d'ensemble ça ne sert à rien. Et le serveur MCP permet enfin à Claude Code de piloter Summoner comme un outil natif.

  • Fusion Operations + Tasks → Board view avec run panel collapsible intégré
  • Epic list + epic detail : regroupement de tâches par objectif avec barre de progression
  • Package MCP : Claude Code peut piloter Summoner comme un outil natif (lister projets, créer tâches, lancer workflows)
  • Kanban enrichi : spinner sur tâches actives, badge WAITING, context menu
  • Synthesis : extraction automatique des données de session (fichiers touchés, outils utilisés, coût, durée)
  • Scanner : checklist 46 items en 11 catégories pour auditer la santé projet
Nuit blanche : sessions zombies et workflows+12 commits

La nuit a commencé par trois bugs qui rendaient le dashboard inutilisable en usage réel : des sessions fantômes qui restaient bloquées, des messages envoyés dans le vide, et un session manager qui ne gérait pas les états stuck. Fixes urgents. Ensuite, le workflow engine : pouvoir enchaîner des étapes automatisées sans intervention. Et le blue-green deploy pour pouvoir redémarrer le daemon sans tuer les sessions en cours.

  • Sessions zombies : flag busy réinitialisé au stop, bail sur project-not-found (00h31)
  • Session manager durci contre les sessions bloquées (00h33)
  • Messages fantômes : queue de messages envoyés quand la session redevient libre (00h38)
  • Workflow engine : 7 types d'étapes + triggers + runs API (00h41)
  • Auto-continue toggle 'Don't stop' dans le chat panel (00h41)
  • Workflow panel UI : liste, éditeur YAML, moniteur de run (07h51)
  • Mobile : barre d'actions projet (07h52)
  • Blue-green deploy : session handoff sur port 7779, zero downtime (08h27)
  • Diff rendering amélioré, refonte task management (10h39)
  • Workflow : gestion du exit code null sur les prompt steps (10h51)
  • Loop engine : gestion gracieuse des signaux kill + seeder endpoint (12h37)
Jour zéro : le post-it et le scaffold+9 commits

Le plan tenait sur un post-it. 10 waves numérotées, du scaffold au deploy. L'après-midi : le monorepo TypeScript, le session manager avec node-pty, le dashboard 3 colonnes. Le soir : le mobile, le resume mode (pour ne plus jamais perdre un contexte de session), et les bases de l'UI. À la fin de la journée, le dashboard tournait et affichait les sessions en temps réel.

  • Scaffold monorepo (core, daemon, web) + TypeScript strict (16h48)
  • Waves 6-9 en rafale : tests, templates, session resume, notifications, batch ops, dashboard (17h06-17h34)
  • Auth bearer, rate limit, métriques, timeline, raccourcis clavier (17h34)
  • Session manager amélioré + couverture de tests étendue (23h43)
  • Icônes, interface Task enrichie, viewport a11y, composants UI (23h43)
  • Resume mode picker + rendu des agent subprocesses dans le chat (23h43)
  • Mobile : refonte UX complète, navigation 4 onglets, project switcher (23h43)