talkback — l'entendre au lieu de le lire
Parle le texte à voix haute depuis n'importe quel répertoire de projet, enregistre l'audio, et ne dépense jamais silencieusement un quota limité.
Sur demande par défaut. Il parle quand on le lui demande — l'utilisateur dit "explique ça à voix haute", ou le script est exécuté directement. Il ne raconte pas tout seul jusqu'à ce que quelqu'un active le talk mode, et le talk mode appartient à une seule session à la fois.
Utilisation
S=~/.claude/skills/talkback/scripts
$S/talkback.py "Voici ce qui a changé et pourquoi c'est important." # ElevenLabs (défaut)
$S/talkback.py --fast "Ligne jetable." # local, instantané, gratuit
$S/talkback.py --quota # ce qu'il reste
$S/talkback.py --voices # lister les voix
$S/talkback.py --dry-run "..." # voir le texte parlé, ne rien synthétiser
$S/talkback.py --help # tous les drapeaux
$S/talkback-hook.py --on # talk mode ON, cette session seulement
$S/talkback-hook.py --off # arrêter de parler
$S/talkback-hook.py --status # qui parle, le hook est-il branché
$S/talkback-hook.py --outputs # sorties audio sur cet hôte
Le texte peut aussi être passé par tube : git log -1 --format=%B | $S/talkback.py.
Comment l'agent doit l'utiliser
Deux choses différentes vivent ici. Parler une fois est une prose composée que vous passez à talkback.py. Le talk mode est un paramètre permanent que vous basculez avec talkback-hook.py ; vous ne composez jamais son texte, le hook lit le tour.
Parler une fois
Quand l'utilisateur demande à entendre une explication, écrivez pour l'oreille, puis parlez. Ne passez pas de markdown brut ou de diff à l'outil. Composez deux à cinq phrases de prose parlée simple — ce qui a changé, pourquoi, ce que cela signifie pour lui — et passez cela. L'auditeur ne peut pas faire défiler en arrière, donc commencez par la conclusion.
Utilisez par défaut la bonne voix — le plan la permet confortablement. Utilisez --fast quand le texte est jetable ou que vous voulez une latence réseau nulle. Mentionnez quel backend a été utilisé en cas de repli.
Piloter le talk mode
Ce que l'utilisateur dit correspond à une commande. Exécutez-la ; ne racontez pas aussi le changement à la main, car le hook parlera du tour que vous écrivez.
| L'utilisateur dit | Exécutez |
|---|---|
| "talk mode on", "continue à parler", "raconte cette session" | talkback-hook.py --on |
| "arrête de parler", "mute", "tais-toi" | talkback-hook.py --off |
| "juste les points importants", "moins de détails" | talkback-hook.py --on brief |
| "dis-moi seulement les infos importantes" | talkback-hook.py --on marker |
| "utilise mes AirPods", "joue-le sur X" | talkback-hook.py --outputs, puis --on --output "<nom>" |
| "utilise la voix bon marché", "arrête de dépenser le quota" | talkback-hook.py --on --backend say |
| "est-ce que c'est allumé ?", "pourquoi je n'entends rien ?" | talkback-hook.py --status |
| "est-ce qu'autre chose parle ?" | talkback-hook.py --sessions |
| "rends tout silencieux" (toutes les sessions) | talkback-hook.py --off --all |
Trois choses à vérifier avant de dire à l'utilisateur que ça marche :
--statusindique si le hook est enregistré. S'il ne l'est pas, exécutez--installet dites-lui qu'un redémarrage est nécessaire.- Si
--statusprévient que le hook a été enregistré après le début de cette session, le talk mode est activé mais ne fera aucun son ici. Dites-le — ne le laissez pas découvrir le silence. - Le talk mode ne survit pas à la session. Après un redémarrage ou un
/clear, il est à nouveau désactivé et nécessite--on. C'est délibéré, pas un bug ; dites-le une fois plutôt que de le laisser demander à nouveau.
En mode marker — et à tout moment où vous voulez que la relecture soit un résumé écrit pour l'oreille plutôt que le tour lui-même — terminez le message par un marqueur :
<!-- talkback: Refactorisation de la couche d'authentification, trois sites d'appel, tests au vert. -->
Backends
| Backend | Coût | Qualité | Notes |
|---|---|---|---|
elevenlabs (défaut) | limité | meilleure | protégé par quota, repli automatique sur say |
say (--fast) | gratuit, illimité | correcte | natif macOS, ~instantané, sans réseau |
omnivoice | gratuit, illimité | bonne | local + privé ; nécessite le backend actif. Non vérifié — voir ci-dessous |
La hiérarchie
elevenlabs → omnivoice → say, du meilleur au moins bon. Un échelon qui ne peut pas prendre le job — pas de clé, quota épuisé jusqu'à la réserve, serveur local en panne, erreur de synthèse — passe la main au suivant, donc la voix se dégrade au lieu que l'audio disparaisse. TALKBACK_CHAIN le réordonne.
Demander explicitement un échelon démarre la hiérarchie là et ne fait que descendre : --fast signifie "local maintenant" et ne remonte jamais vers un backend limité. Chaque repli imprime la raison sur stderr et le backend choisi atterrit dans le registre, donc une exécution dégradée n'est jamais silencieuse quant à sa dégradation.
--strict transforme tout repli en échec dur (sortie 1) à la place, pour les scripts qui ne doivent pas se dégrader silencieusement.
Quota
Le compte est au niveau Creator : 130 958 caractères/mois. Une explication parlée de deux minutes fait environ 1 500 caractères, soit environ 87 par mois — assez pour que la bonne voix soit le défaut plutôt qu'une friandise.
Vérifiez au point d'utilisation, jamais de mémoire — talkback.py --quota le lit en direct. Le niveau a déjà changé une fois, et un nombre dans un document est obsolète dès que le plan bouge.
Avant de synthétiser, l'outil lit le quota en direct et conserve une réserve de 250 caractères, donc une seule longue explication ne peut jamais épuiser complètement le solde. Si la demande ne tient pas, il prévient sur stderr et utilise say à la place.
Les identifiants sont résolus dans l'ordre : $ELEVENLABS_API_KEY → ~/.elevenlabs/api_key (écrit par elevenlabs auth login) → ELEVENLABS_API_KEY dans ~/broomva/.env.local. Deux clés distinctes existent sur cette machine et elles résolvent le même compte, donc vérifier l'une revient à vérifier les deux.
Le niveau Creator déverrouille également le clonage vocal instantané et professionnel (30 emplacements vocaux, 1 professionnel). --voices liste ce que le compte peut actuellement utiliser.
Le paquet
@elevenlabs/clin'est pas utilisé à l'exécution et ne peut pas faire cela — toute sa surface estauth · agents · tools · tests · components, qui gère les projets d'agents ConvAI hébergés. Il n'a pas de commande de synthèse. Le CLI est utile ici uniquement pourauth login, qui écrit le fichier de clé.
Le niveau OmniVoice n'est pas vérifié
Le backend omnivoice est implémenté selon la forme documentée mais n'a jamais été testé — le backend local était en panne quand ceci a été livré. Il se dégrade proprement (repli sur say, ou échec sous --strict). Pour le démarrer, voir le skill omnivoice ; le dépôt est déjà à ~/broomva/external/OmniVoice-Studio.
Gestion du texte parlé
La prose de l'agent n'est pas écrite pour être entendue, donc le texte est d'abord préparé :
- blocs de code →
(code omis); URL →(lien) - les titres et les puces deviennent des sauts de phrase, donc les listes ne se confondent pas
- les chemins profonds sont raccourcis au nom de base (
src/lib/engine.py→engine.py) ; passez--keep-pathspour les entendre en entier - l'emphase markdown est supprimée uniquement là où elle délimite une étendue — un trait de soulignement nu à l'intérieur de
backend_chainest laissé intact, car le supprimer transformerait un symbole que l'auditeur pourrait rechercher en un symbole qu'il ne peut pas
Audio sauvegardé
Chaque énoncé atterrit dans ~/.talkback/audio/ en mp3 (converti via ffmpeg quand présent) avec un nom de fichier horodaté et slugifié, et est ajouté à ~/.talkback/ledger.jsonl. Utilisez --no-save pour jeter, --out-dir pour rediriger.
Talk mode — raconter la session en direct
Le talk mode fait parler l'agent d'une courte relecture à la fin de chaque tour, pour toute la durée de la session. Il arrive désactivé, il est activé à la main, et il appartient à une seule session.
$S/talkback-hook.py --on # détails complets, chaque tour, CETTE session
$S/talkback-hook.py --on brief # uniquement les lignes d'ouverture
$S/talkback-hook.py --on marker # uniquement les tours avec un marqueur
$S/talkback-hook.py --on --backend elevenlabs # la bonne voix, limitée
$S/talkback-hook.py --on --output "AirPods Pro" # quel haut-parleur cette session utilise
$S/talkback-hook.py --off # arrêter de parler (cette session)
$S/talkback-hook.py --off --all # interrupteur général, partout
$S/talkback-hook.py --status # mode, backend, qui d'autre parle
$S/talkback-hook.py --sessions # toutes les sessions qui parlent actuellement
Pourquoi limité à la session
Le hook Stop est enregistré une fois, globalement et en permanence — un interrupteur que vous ne pouvez pas basculer pendant une session n'est pas un interrupteur. Donc le hook se déclenche dans chaque session, et le drapeau est ce qui le limite : le talk mode est une propriété d'une session, pas de la machine.
Le drapeau est ~/.talkback/sessions/<session-id> ; une session qui n'a jamais adhéré n'en a pas, donc le hook sort avec 0 sans un son. C'est ce qui garde six agents parallèles dans six worktrees silencieux pendant que la session que vous regardez parle. La conception pré-0.3.0 avait un seul drapeau à l'échelle de la machine, c'est pourquoi il n'a jamais été activé : l'activer rendait chaque agent sur la boîte audible en même temps.
L'ID de session est lu à partir de CLAUDE_CODE_SESSION_ID ou CLAUDE_SESSION_ID ; --session <id> le définit explicitement. Quand aucun d'eux ne répond, la commande imprime pass --session <id> et sort avec 2 plutôt que de deviner — elle utilisait auparavant la transcription la plus récente dans le répertoire de travail, ce qui signifiait qu'avec deux sessions ouvertes dans un seul répertoire --on activait la session la plus récemment touchée, pas celle qui avait demandé.
Du côté du hook, le session_id de la charge utile est autorisé : quand il est présent, c'est la seule clé que la porte autorise. Le nom du fichier de transcription n'est utilisé que lorsque ce champ est absent. Les deux étaient autrefois acceptés ensemble, ce qui permettait à une charge utile nommant la session A et la transcription de la session B de résoudre l'adhésion de B et de parler dans A.
--session <id> ciblant une autre session est délibéré, et ce n'est pas une frontière de sécurité. La session A exécutant talkback-hook.py --session B --off active ou désactive le talk mode de B sans le consentement de B — il n'y a pas de vérification de propriété liant un drapeau à la session qui l'a défini. C'est une fonctionnalité, conservée intentionnellement plutôt que rétablie : chaque session sur la machine s'exécute sous le même utilisateur OS, donc toute session peut déjà écrire le fichier de drapeau d'une autre session directement sous ~/.talkback/sessions/. Une vérification de propriété à l'intérieur du CLI n'arrêterait que le chemin poli via le drapeau, pas le chemin direct — une frontière de même principal ne peut pas arrêter un écrivain qui partage le principal. Le drapeau limite l'audio, donc les agents parallèles restent silencieux par défaut ; il ne limite pas le contrôle, et rien ici ne doit être lu comme s'il le faisait.
Niveaux de détail
| Mode | Parle |
|---|---|
full (défaut session) | le tour entier, chaque tour — commencé par le marqueur quand l'agent en a écrit un |
brief | les TALKBACK_HOOK_CHARS (320) premiers caractères des tours de plus de TALKBACK_HOOK_MIN_CHARS (80) ; un marqueur l'emporte quand présent |
marker | uniquement les tours avec un marqueur <!-- talkback: ... -->, et seulement le texte du marqueur |
off | jamais — écrit par --off quand un drapeau global réactiverait autrement la session |
full est le défaut car le but d'une relecture est de s'éloigner et de revenir en sachant ce qui s'est passé. Un extrait plafonné est un aperçu de la réponse, pas la réponse — vous devriez encore lire l'écran, ce qui est ce que le talk mode existe pour éviter. Ce qui est supprimé n'est que ce qui ne peut pas du tout être entendu (blocs de code, URL, chemins profonds — voir Gestion du texte parlé), jamais ce qui est simplement long. full n'a pas non plus de plancher de longueur : "fini, tests au vert" est un résultat que vous voulez quand vous êtes loin, pas du bruit.
brief et marker sont pour un arc long sans surveillance où vous voulez la forme du progrès plutôt que la transcription.
TALKBACK_FULL_MAX_CHARS met un plafond sur full, désactivé par défaut — un plafond transforme full en brief exactement aux tours qui valent la peine d'être entendus en entier.
Les relectures utilisent toute la hiérarchie des backends — ElevenLabs d'abord, descendant seulement quand un échelon est inutilisable. Des détails complets à chaque tour dépensent donc du vrai quota : un tour de 1 500 caractères est ~1 % du solde mensuel Creator, donc une longue session atteindra la réserve, à quel point elle continue à parler sur l'échelon suivant plutôt que de devenir silencieuse. --on --backend say la maintient basse, --on brief la raccourcit.
Quand une relecture chevauche la suivante
Une relecture complète peut encore jouer quand le tour suivant se termine. TALKBACK_ON_OVERLAP décide ce qui se passe :
| Valeur | Comportement |
|---|---|
interrupt (défaut) | la nouvelle relecture coupe l'ancienne — l'état le plus récent est celui qui vaut la peine d'être entendu |
queue | elles jouent dans l'ordre, donc une longue relecture détaillée n'est jamais tronquée ; vous prenez du retard mais entendez tout |
queue est le bon réglage pour un arc long sans surveillance que vous avez l'intention d'écouter en entier plus tard ; interrupt est correct quand vous êtes à la machine et voulez la dernière information.
Le marqueur
Pour faire adhérer un seul tour — en mode marker, ou pour parler un résumé écrit pour l'oreille à la place des premières lignes du message — terminez le message par :
<!-- talkback: Refactorisation de la couche d'authentification, trois sites d'appel, tests au vert. -->
En mode full, le marqueur devient le titre : il est parlé en premier, puis tout le tour derrière lui, donc la relecture commence par la conclusion sans perdre les détails. En mode brief, le marqueur remplace l'extrait, et un tour sans marqueur retombe sur les premières phrases coupées à une limite de phrase.
Quel haut-parleur
L'audio sort de la machine qui exécute Claude Code. Une session que vous pilotez depuis un téléphone, une tablette ou un autre ordinateur portable sonne toujours sur l'hôte, donc le levier important est de choisir sur laquelle des sorties de l'hôte elle atterrit — AirPods appairés au Mac, une enceinte AirPlay, un écran, les haut-parleurs intégrés.
$S/talkback-hook.py --outputs # sur quoi cet hôte peut jouer
$S/talkback-hook.py --on --output "AirPods Pro"
$S/talkback.py -d "Haut-parleurs MacBook Pro" "ligne ponctuelle sur un périphérique choisi"
La sortie est stockée par session, donc
…
[tronqué pour la traduction]