ai newsSo., 25. Okt.
EilmeldungGive Claude a read-only straw to your SQLite DB: a tiny MCP server in Python

Finding the processes Claude Code leaves running on Windows

A scan of my machine, 38 hours after boot, found a next dev server that had held port 3001 for 35 hours with nothing connected to it. An ng serve had sat idle for 11 hours. Around them were 20 bare tail.exe , grep.exe and sh.exe rows in Task Manager, with nothing to say which project they belonged

dev.toSonntag 06:15 · 25.10.5 Min. Lesezeit
Bild: dev.to

Ein Scan meines Rechners, 38 Stunden nach dem Booten, fand einen next-Dev-Server, der 35 Stunden lang Port 3001 belegt hatte, ohne dass etwas damit verbunden war. Ein ng serve hatte 11 Stunden lang untätig gewartet. Ringsherum standen 20 nackte Einträge von tail.exe, grep.exe und sh.exe im Task-Manager, ohne dass irgendetwas darüber aussagte, zu welchem Projekt sie gehörten.

Alle stammten aus meinen Claude-Code-Sitzungen: Dev-Server, tail -f-Beobachter auf Task-Ausgabedateien, kleine Skripte. Die Sitzungen waren beendet, und die Prozesse liefen weiter. Dazu gibt es ein Upstream-Issue, #62659, und ein Bot hat es als not planned geschlossen.

Also schrieb ich Sweepling, eine Windows-Tray-Anwendung, die diese Überbleibsel erkennt und sie schließt, sobald man das bestätigt.

Unter Linux wird eine verwaiste Prozess vom init-Prozess oder einem Subreaper adoptiert, und ein Aufräumtool kann eine ganze Prozessgruppe mit einem einzigen Aufruf beenden. cc-reaper erledigt das auf macOS und Linux sehr gut.

Windows bietet beides nicht. Wenn ein Elternprozess endet, laufen seine Kinder weiter und behalten die PID des toten Elternteils in ihrem eigenen Prozessdatensatz. Windows verwendet PIDs außerdem wiederverwendet, sodass diese Nummer wenige Minuten später zu etwas völlig Unbezogenem gehören kann. Der Prozessbaum im Task-Manager verschachtelt einen Prozess nur unter einem noch lebenden Elternteil, daher erscheint ein Waise als ein weiterer node.exe-Eintrag auf oberster Ebene.

Daraus ergibt sich die erste Regel: Der Elternprozess eines Prozesses ist verschwunden, wenn seine PID fehlt oder wenn diese PID inzwischen zu einem Prozess gehört, der nach dem Kind gestartet wurde.

Proc? ParentOf ( Proc p ) => p. ParentPid!= p. Pid && byPid. TryGetValue ( p. ParentPid, out var parent ) && parent. Started <= p. Started? parent: null; Enter fullscreen mode Exit fullscreen mode Dieselbe Prüfung lässt sich auch in PowerShell durchführen, ohne etwas installieren zu müssen:

$all = Get-CimInstance Win32_Process $byId = @{}; $all | ForEach-Object { $byId [ $_. ProcessId ] = $_ } $all | Where-Object { $p = $byId [ $_. ParentProcessId ]; -not $p -or $p. CreationDate -gt $_. CreationDate } | Where-Object Name -in 'node.exe', 'tail.exe', 'grep.exe', 'bash.exe', 'sh.exe' | Select-Object ProcessId, Name, CreationDate Enter fullscreen mode Exit fullscreen mode Die Liste enthält auch Verwaiste, die nichts mit Claude Code zu tun haben. Diese zu unterscheiden ist der eigentliche Knackpunkt.

Viele Programme starten einen Helferprozess und beenden sich selbst absichtlich, daher ist ein verwaister Prozess für sich genommen noch kein Überbleibsel. Sweepling bietet nur Prozesse an, die es einer Claude-Code-Sitzung zuordnen kann, und dafür gibt es drei Wege, geordnet nach Zuverlässigkeit.

Wenn Sweepling nicht erkennen kann, ob eine Sitzung beendet ist, behandelt es die Sitzung als laufend. Ein falsches "laufend" verzögert nur einen Löschvorschlag. Ein falsches "beendet" würde die laufende Arbeit eines Menschen zur Löschung anbieten.

Verwaiste Prozesse, die denselben toten Elternprozess teilen und innerhalb von zehn Sekunden nacheinander gestartet wurden, bilden eine Gruppe, zusammen mit allem, was darunter läuft, und man schließt eine Gruppe als Ganzes.

Allein die Zugehörigkeit zu einer beendeten Sitzung reicht noch nicht aus. Eine Gruppe wartet, bis ihr Elternprozess seit einer Minute verschwunden ist, und sie wird nie angeboten, wenn:

Sweepling schaut sich nie die CPU- oder Speichernutzung an. Ein beschäftigter Prozess ist oft ein Build, auf den jemand wartet, und "töte alles, was viel CPU verbraucht" hat bei anderen Aufräumtools bereits Hintergrund-Builds beendet.

Sweepling schließt niemals etwas, das nicht bestätigt wurde, und es gibt keinen Auto-Modus, keine Einstellung und kein Flag, das daran etwas ändert. Überbleibsel beendeter Sitzungen sind standardmäßig abgehakt. Verwaiste Prozesse einer noch laufenden Sitzung bekommen einen eigenen Bereich und sind standardmäßig nie abgehakt.

Kurz vor dem Schließen einer Gruppe prüft es erneut. Nichts in der Gruppe darf seit der letzten Ansicht eine Verbindung bekommen haben, und jede PID muss noch zum Prozess gehören, der angezeigt wurde, verglichen anhand der Startzeit, denn die PID, die man vor dreißig Sekunden sah, könnte inzwischen ein neuer Prozess sein. Es schließt zuerst die Elternprozesse, damit ein Supervisor nicht gerade verlorene Kinder neu starten kann. Jedes Schließen wird in ein lokales Protokoll geschrieben, wobei Kommandozeilen maskiert werden, da Kommandozeilen Tokens und private Pfade enthalten.

Eine App, die Überbleibsel aufräumt, sollte nicht mehr kosten als diese selbst. Sweepling ist.NET 10 und Avalonia 12, veröffentlicht mit NativeAOT als einzelner Prozess. Avalonias GPU-Renderer belegte ab dem Start konstant etwa 60 MB, egal ob ein Fenster offen war oder nicht, daher rendert Sweepling per Software. Im Tray liegt es bei etwa 17 MB Private Bytes im Leerlauf und bei etwa 23 MB, sobald sein Fenster geöffnet wurde. Sein einziger Netzwerkaufruf ist die Update-Prüfung gegen GitHub Releases, und es gibt keine Telemetrie, weil es jede Kommandozeile auf dem Rechner sehen kann.

Ich nutze es täglich, seit es funktioniert. An seinem arbeitsreichsten Tag fand es 22 Gruppen auf meinem Rechner: 9 Task-Ausgabedateien-Beobachter, 9 ihrer grep-Filter, einen Next.js-Dev-Server, zwei Angular-Dev-Server und ein Shell-Skript. Ich habe alle geschlossen. Einer davon war ein Angular-Dev-Server, den ich vollständig vergessen hatte. Die Zahl, die ich am meisten beobachte, ist, wie viele Vorschläge noch in Benutzung waren, und bisher ist das null.

Man braucht Windows 10 oder 11 auf x64. Lade sweepling-setup-win-x64.exe aus dem neuesten Release herunter. Es installiert nur für den eigenen Benutzer, benötigt keine Admin-Rechte, ist signiert und hält sich selbst auf dem neuesten Stand. Es sitzt als Strohbesen im Tray. Wenn der Besen eine Zahl anzeigt, klickt man darauf, entfernt den Haken bei allem, was man noch braucht, und drückt Close. Um zu sehen, was es anbieten würde, ohne etwas zu schließen, führt man Sweepling.exe --scan > scan.json aus.

Im Moment unterstützt es nur die Claude-Code-CLI. Auf macOS oder Linux verwendet man cc-reaper, das deutlich mehr kann. Wenn man bereits weiß, welcher Port blockiert ist, ist npx kill-port 3001 schneller als jede Tray-Anwendung.

Der Code steht unter Apache-2.0 auf GitHub. Das nützlichste Issue, das man öffnen könnte, beschreibt ein Überbleibsel, das Sweepling übersehen hat, oder eines, das es angeboten hat, während man es noch benutzt hat.

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

Original (Englisch) anzeigen

A scan of my machine, 38 hours after boot, found a next dev server that had held port 3001 for 35 hours with nothing connected to it. An ng serve had sat idle for 11 hours. Around them were 20 bare tail.exe, grep.exe and sh.exe rows in Task Manager, with nothing to say which project they belonged to.

All of them came from my Claude Code sessions: dev servers, tail -f watchers on task output files, small scripts. The sessions had ended and the processes kept running. There's an upstream issue about it, #62659, and a bot closed it as not planned.

So I wrote Sweepling, a Windows tray app that notices these leftovers and closes them once you confirm.

On Linux an orphan is adopted by init or a subreaper, and a cleanup tool can end a whole process group in one call. cc-reaper does this well on macOS and Linux.

Windows does neither. When a parent exits, its children keep running and keep the dead parent's PID in their own process record. Windows also reuses PIDs, so a few minutes later that number can belong to something unrelated. Task Manager's tree only nests a process under a parent that's still alive, so an orphan shows up as one more top-level node.exe.

That gives the first rule: a process's parent is gone when its PID is missing, or when that PID now belongs to a process that started after the child.

Proc? ParentOf ( Proc p ) => p. ParentPid!= p. Pid && byPid. TryGetValue ( p. ParentPid, out var parent ) && parent. Started <= p. Started? parent: null; Enter fullscreen mode Exit fullscreen mode You can run the same check in PowerShell without installing anything:

$all = Get-CimInstance Win32_Process $byId = @{}; $all | ForEach-Object { $byId [ $_. ProcessId ] = $_ } $all | Where-Object { $p = $byId [ $_. ParentProcessId ]; -not $p -or $p. CreationDate -gt $_. CreationDate } | Where-Object Name -in 'node.exe', 'tail.exe', 'grep.exe', 'bash.exe', 'sh.exe' | Select-Object ProcessId, Name, CreationDate Enter fullscreen mode Exit fullscreen mode It will also list orphans that have nothing to do with Claude Code. Telling those apart is the hard part.

Plenty of programs start a helper and exit on purpose, so an orphan on its own isn't a leftover. Sweepling only offers a process it can tie to a Claude Code session, and it has three ways to do that, strongest first.

When Sweepling can't tell whether a session has ended, it treats the session as running. A wrong "running" only delays an offer. A wrong "ended" offers someone's live work.

Strays that share one dead parent and started within ten seconds of each other become a group, together with everything under them, and you close a group as one.

Belonging to an ended session still isn't enough. A group waits until its parent has been gone for a minute, and it's never offered when:

Sweepling never looks at CPU or memory use. A busy process is often a build someone is waiting for, and "kill whatever is using a lot of CPU" has ended background builds in other cleanup tools.

Sweepling never closes anything you haven't confirmed, and there's no auto mode, setting or flag to change that. Leftovers of ended sessions start ticked. Strays of a session that's still running get their own section and are never ticked by default.

Right before closing a group it checks again. Nothing in the group may have gained a connection since you looked, and each PID must still belong to the process you were shown, compared by start time, because the PID you saw thirty seconds ago could be a new process by now. It closes parents first, so a supervisor can't restart a child it just lost. Each close goes into a local log with command lines masked, since command lines carry tokens and private paths.

An app that cleans up leftovers shouldn't cost more than they do. Sweepling is.NET 10 and Avalonia 12, published with NativeAOT as a single process. Avalonia's GPU renderer held about 60 MB from launch whether a window was open or not, so Sweepling renders in software. In the tray it idles at about 17 MB of private bytes, and about 23 MB once its window has been opened. Its only network call is the update check against GitHub Releases, and there's no telemetry, because it can see every command line on the machine.

I've run it every day since it worked. On its busiest day it found 22 groups on my machine: 9 task output watchers, 9 of their grep filters, a Next.js dev server, two Angular dev servers and a shell script. I closed all of them. One was an Angular dev server I had completely forgotten about. The number I watch most is how many it offered that were still in use, and so far that's zero.

You need Windows 10 or 11 on x64. Download sweepling-setup-win-x64.exe from the latest release. It installs for your user only, needs no admin rights, is signed, and keeps itself up to date. It sits in the tray as a straw brush. When the brush shows a count, click it, untick anything you still want, and press Close. To see what it would offer without closing anything, run Sweepling.exe --scan > scan.json.

For now it covers the Claude Code CLI only. On macOS or Linux, use cc-reaper, which does much more. If you already know which port is stuck, npx kill-port 3001 is faster than any tray app.

The code is Apache-2.0 on GitHub. The most useful issue you could open describes a leftover Sweepling missed, or one it offered while you were still using it.

Quelle: dev.to (dev.to). Original öffnen