Eine Desktop-App, die viele Claude-Code-Sitzungen gleichzeitig steuert. Sieh jede Sitzung in einer Seitenleiste, tippe in eine beliebige, beobachte den Stream und genehmige ihre Tool-Aufrufe direkt. Gebaut auf dem Agent SDK, in drei isolierten Prozess-Schichten. Ein Experiment, das ich tief vorangetrieben, dann aber zur Seite gelegt habe: zu wartungsintensiv, und ich entwickle es heute nicht mehr aktiv.
Mehrere Claude-Code-Sitzungen gleichzeitig zu fahren bedeutet, zehn Terminal-Fenster zu jonglieren, eines pro Projekt oder Aufgabe. Es gibt keine einzige Oberfläche, um zu sehen, was jede Sitzung tut, in eine bestimmte zu tippen oder ihre Tool-Aufrufe zu genehmigen, ohne zwischen ihnen hin- und herzuwechseln.
Ich wollte ein Cockpit: ein Fenster, das jede Sitzung steuert, den Live-Zustand jeder einzelnen zeigt und es mir erlaubt, einen Tool-Aufruf in genau der Sitzung zu genehmigen, die ihn angefragt hat, ohne die anderen zu verlieren.
Basalt ist ein Desktop-Fenster, das N parallele Claude-Code-Sitzungen steuert, aufgeteilt in drei Prozesse, die je eine Aufgabe erledigen. Eine Tauri-Hülle in Rust besitzt das native Fenster und überwacht den Rest. Ein React-Frontend rendert die Flotte. Ein Node-Sidecar hält die Engine.
Die Rust-Hülle trägt keine Geschäftslogik. Sie öffnet das Fenster, startet das Sidecar mit einem zufälligen Port und Auth-Token, startet es bei einem Absturz neu und beendet es beim Schließen. Das Frontend spricht mit dem Sidecar über einen nur-lokalen WebSocket, abgesichert durch dieses Token, sodass kein anderer Prozess auf der Maschine sich verbinden kann. Tauri statt Electron hält die ganze App bei etwa 10 bis 15 MB statt über 100.
Jede Sitzung ist ein echtes Claude-Agent-SDK-query(), kein Wrapper um die Terminal-Oberfläche. Ich wählte das SDK statt PTY-Scraping der TUI (fragil, keine strukturierten Ergebnisse) und statt headless claude -p (kein Live-Callback für Tool-Genehmigung).
Das SDK liefert, was ein Controller wirklich braucht: einen canUseTool-Callback, um jeden Tool-Aufruf abzufangen, Teil-Nachrichten-Streaming für Live-Ausgabe, Interrupt pro Sitzung, Modell- und Permission-Mode-Steuerung und viele parallele Sitzungen in einem Prozess. Ein SessionManager besitzt das Register; jede Session umhüllt ein query() und hält ihr eigenes Transkript. Eine fehlerhafte Sitzung wird als errored markiert und berührt ihre Geschwister nie.
Weil das SDK strukturierte Events ausgibt, ist die Oberfläche ein sauberes Chat-artiges Transkript statt eines Terminal-Emulators. Text-Deltas verschmelzen zu Blöcken; jeder Tool-Aufruf ist eine Karte; eine Karte, die auf Erlaubnis wartet, wird zur Genehmigungskarte mit Genehmigen, Ablehnen oder Eingabe-bearbeiten.
Eine linke Leiste listet jede Sitzung als Live-Statuszeile: ein farbiger Punkt, das Label und eine einzeilige Aktivität wie bearbeitet oder braucht Genehmigung. Der Hauptbereich ist Transkript und Eingabezeile der gewählten Sitzung. Das Ganze trägt eine Ember-Identität, eine fast schwarze Basalt-Palette mit einem Lava-Amber-Akzent, wobei Amber immer bedeutet, dass ein Tool auf dich wartet.
Das schwierigere Problem ist, mehrere fokussierte Sitzungen gleichzeitig im selben Repository laufen zu lassen, ohne dass sie sich gegenseitig überschreiben. Basalt behält einen Arbeitsbaum und verhindert Kollisionen, indem es Dateien beim Bearbeiten sperrt.
Das Sperr-Gate ist ein PreToolUse-Hook, bewusst nicht der canUseTool-Callback. canUseTool wird in den permissiven Permission-Modes übersprungen, die man für Flottenarbeit tatsächlich nutzt, während ein PreToolUse-Hook in jedem Modus feuert und blockieren kann. Versucht eine Sitzung, eine Datei zu schreiben, die eine andere hält, verweigert Basalt den Aufruf mit einer Begründung, und der blockierte Claude leitet sich selbst um. Null Vertrauen darauf, dass die Modelle kooperieren. Ein Live-Board, ein .basalt/board.md mit einem einzigen Autor, zeigt, wer was anfasst.
Alles ist ausgeliefert: die Sidecar-Engine, das Frontend, die Tauri-Hülle, die Datei-Sperr-Koordination und das verzögerte Wiederaufnehmen von Sitzungen, das das Schließen der App überlebt. Es installiert sich als echte Basalt.app und baut sich mit einem einzigen studio basalt update neu.
Die scharfe Kante war das Packaging. Eine doppelt geklickte .app bekommt einen minimalen PATH, der node des Nutzers ist also nicht darauf; Basalt löst das echte Binary beim Start über die Login-Shell auf und startet es direkt. Ein Fake-Engine-Modus tauscht das SDK gegen einen geskripteten Generator, sodass die gesamte Oberfläche gebaut, vorgeführt und gescreenshottet werden kann, ohne ein einziges Token auszugeben. Vorerst ist es tägliches Studio-Werkzeug und beweist, dass es hält, bevor es offen veröffentlicht wird.
Drei Experimente, ein Studio. Der Harness betreibt es, Lattice erinnert sich daran, und Basalt erkundete, eine ganze Flotte von Agenten gleichzeitig zu steuern: tief gebaut, dann als zu wartungsintensiv zur Seite gelegt. Jedes existiert, weil das Werkzeug, das ich brauchte, es noch nicht gab.