ai newsSa., 24. Okt.
EilmeldungDeepSeek V4.1-Flash-1017 überzeugt in Benchmarks, Zweifel an Bewertungsmethodik bleiben

Claude Code Hooks auf dem Stream Deck melden fertige Sessions

Wer mehrere Claude Code Sessions parallel betreibt, verliert schnell den Überblick, welche gerade auf eine Freigabe wartet oder fertig ist. Mit Hooks und einem Stream Deck lässt sich der Status jeder Session sichtbar machen, sodass keine Anfrage mehr in anderen Fenstern untergeht.

dev.toSamstag 05:14 · 24.10.DE3 Min. Lesezeit
Bild: dev.to

Ich habe meistens mehrere Claude Code Sessions parallel laufen – hier ein Dokument, dort eine kleine Korrektur, dazu etwas Recherche. An einem vollen Tag sind es etwa fünf.

Das Delegieren der Arbeit war nicht das Problem. Das Wahrnehmen schon.

Die KI war nicht der langsame Teil. Die Zeit, bis ich es bemerkte, war es. Je mehr Sessions ich laufen ließ, desto schlimmer wurde es.

Ich habe den Zustand jeder Session auf eine kleine Tastenbox auf meinem Schreibtisch gelegt (ein Stream Deck Neo: 8 Tasten und ein schmaler Info-Balken).

Auf der Hook-Seite ist es hauptsächlich eine Abbildung von Hook-Events auf vier Zustände:

// hook events -> key state function stateFor ( d ) { switch ( d. hook_event_name ) { case " UserPromptSubmit ": case " PostToolUse ": return " running "; // blue case " PreToolUse ": // asking me a question also counts as waiting on me return [ " AskUserQuestion ", " ExitPlanMode " ]. includes ( d. tool_name )? " ask ": " running "; case " PermissionRequest ": return " ask "; // red case " Notification ": if ( d. notification_type === " permission_prompt " ) return " ask "; if ( d. notification_type === " idle_prompt " ) return " waiting "; return null; case " Stop ": return " waiting "; // orange: done, please look case " SessionStart ": return " idle "; // gray default: return null; } } Die Farben:

Drückt man eine Taste, wird das Fenster der entsprechenden Session in den Vordergrund geholt. Rot gesehen, Taste gedrückt, im Fenster freigegeben. Kein Suchen mehr.

Der Bug, der am längsten gedauert hat: Eine Session war fertig, aber ihre Taste blieb blau.

Der Hook schreibt in eine temporäre Datei und benennt sie über die echte um. Das Plugin liest diese Datei jede Sekunde. Unter Windows kann die Umbenennung fehlschlagen und einen EPERM-Fehler werfen, wenn sie genau in dem Moment landet, in dem das Plugin liest. Meine erste Version hat diesen Fehler still verschluckt – der Zustand „fertig“ wurde also nie geschrieben, und die Taste blieb blau. Als ich das gefunden hatte, hatten sich bereits 28 verwaiste temporäre Dateien angesammelt.

Die Lösung ist unspektakulär: ein paar Mal mit kurzer Wartezeit erneut versuchen.

function replaceFile ( tmp, file ) { for ( let i = 0;; i ++ ) { try { fs. renameSync ( tmp, file ); return; } catch ( e ) { if ( i >= 9 ) { try { fs. unlinkSync ( tmp ); } catch {} throw e; } Atomics. wait ( new Int32Array ( new SharedArrayBuffer ( 4 )), 0, 0, 30 ); // ~30 ms } } } Unspektakulär, aber notwendig. Eine Statusleuchte, der man nicht vertrauen kann, ist nur Dekoration.

Ein kleinerer Fehler: Nach /clear läuft die Session unter einer neuen ID weiter, sodass ich kurzzeitig zwei Tasten mit demselben Namen hatte. Jetzt entfernt der Hook den alten Eintrag, wenn eine Session aus einem Clear startet, und hält eine Map von alter ID zu neuer ID für das Plugin vor.

Nur Farben wirkten etwas trocken, also habe ich ein Pixel-Art-Küken in die Tasten gezeichnet. Es heißt Aqui (nach Aquila, dem Adler-Sternbild).

Wenn die nächste Taste ebenfalls arbeitet, gibt sie den Ball weiter. Ich wollte eine Box, die ich gern anschaue, nicht nur eine weitere Benachrichtigung.

Daraus ist eine Reihe von YouTube Shorts entstanden – inzwischen über 20 Episoden. Heutzutage fährt sie in einem Pixel-Art-WRX S4 durch die Jahreszeiten.

Legt „wartet auf Freigabe“ an einer Stelle ab, an der ihr es seht. Dafür braucht es kein Stream Deck – eine farbige Bildschirmecke oder ein Ton funktioniert auch. Für mich war die Zeit, die eine Session auf mich gewartet hat, die am wenigsten sichtbare und die am meisten verschwendete.

Ich habe nicht gemessen, wie viel Zeit das spart, daher werde ich keine Zahl behaupten. Was ich sagen kann: Ich suche nicht mehr durch Fenster.

Ein privates Projekt eines Claude Code Fans. Keine Verbindung zu Anthropic oder Elgato.

Automatisch übersetzt. Das Original auf Englisch ist unten aufklappbar.

Original (Englisch) anzeigen

I usually have several Claude Code sessions running side by side — a document here, a small fix there, some research. On a busy day, around five.

Delegating the work wasn't the problem. Noticing was:

The AI wasn't the slow part. The time until I noticed was. The more sessions I ran, the worse it got.

I put the state of each session on a small button box on my desk (a Stream Deck Neo: 8 keys and a thin info bar).

The hook side is mostly a mapping from hook events to four states:

// hook events -> key state function stateFor ( d ) { switch ( d. hook_event_name ) { case " UserPromptSubmit ": case " PostToolUse ": return " running "; // blue case " PreToolUse ": // asking me a question also counts as waiting on me return [ " AskUserQuestion ", " ExitPlanMode " ]. includes ( d. tool_name )? " ask ": " running "; case " PermissionRequest ": return " ask "; // red case " Notification ": if ( d. notification_type === " permission_prompt " ) return " ask "; if ( d. notification_type === " idle_prompt " ) return " waiting "; return null; case " Stop ": return " waiting "; // orange: done, please look case " SessionStart ": return " idle "; // gray default: return null; } } Enter fullscreen mode Exit fullscreen mode The colors:

Pressing a key brings that session's window to the front. See red, press it, approve in that window. No more hunting.

The bug that took longest: a session finished, but its key stayed blue.

The hook writes to a temp file and renames it over the real one. The plugin reads that file every second. On Windows, if the rename lands at the exact moment the plugin is reading, the rename can fail with EPERM. My first version silently swallowed that error — so the "done" state was never written, and the key stayed blue. By the time I found it, 28 orphaned temp files had piled up.

The fix is boring: retry a few times with a short wait.

function replaceFile ( tmp, file ) { for ( let i = 0;; i ++ ) { try { fs. renameSync ( tmp, file ); return; } catch ( e ) { if ( i >= 9 ) { try { fs. unlinkSync ( tmp ); } catch {} throw e; } Atomics. wait ( new Int32Array ( new SharedArrayBuffer ( 4 )), 0, 0, 30 ); // ~30 ms } } } Enter fullscreen mode Exit fullscreen mode Boring, but necessary. A status light you can't trust is just decoration.

Smaller one: after /clear, the session continues under a new ID, so I briefly had two keys with the same name. Now the hook removes the old record when a session starts from a clear, and keeps an old-ID → new-ID map for the plugin.

Colors alone felt a bit dry, so I drew a pixel-art chick into the keys. Her name is Aqui (from Aquila, the eagle constellation).

If the next key is also working, she passes the ball over. I wanted a box I like looking at, not just another notification.

That turned into a series of YouTube Shorts — over 20 episodes now. These days she rides around in a pixel-art WRX S4 through the seasons.

Put "waiting for permission" somewhere you'll see it. It doesn't need a Stream Deck — a colored corner of the screen or a sound works. For me, the time a session spent waiting on me was the least visible and the most wasted.

I haven't measured how much time this saves, so I won't claim a number. What I can say is that I stopped hunting through windows.

A personal project by a Claude Code fan. Not affiliated with Anthropic or Elgato.

Originaltitel: Which Session Is Waiting for Me? Claude Code Hooks on a Stream DeckQuelle: dev.to (dev.to). Original öffnen