Zum Inhalt springen
SONOYA.com
AI

Claude Code automatisch ausführen: Headless mit launchd

Claude Code ohne Chatfenster: mit claude -p, festen Werkzeugrechten und launchd jeden Morgen einen Projektbericht erstellen und aufs Handy schicken.

Von

Roboter-Symbol auf dunklem Verlauf mit pinkem Schimmer für Claude Code, das per launchd ohne Chatfenster automatisch Aufgaben erledigt

Claude Code kennen die meisten als Agenten, mit dem sie im Terminal chatten. Weniger bekannt ist, dass er auch ohne Chatfenster arbeitet: Du gibst ihm eine Aufgabe als Kommandozeilen-Argument, er erledigt sie, gibt das Ergebnis aus und beendet sich. Damit wird Claude Code zu einem Baustein in Skripten und lässt sich zeitgesteuert starten.

So entsteht zum Beispiel ein Bericht, der jeden Morgen auf Deinem Handy liegt: Was hat sich gestern im Projekt getan, gibt es neue Sicherheitswarnungen bei den Abhängigkeiten, welche TODOs sind liegen geblieben. Diese Anleitung baut genau das auf dem Mac, mit claude -p, eng gefassten Rechten und launchd als Zeitsteuerung. Grundlage ist die offizielle Dokumentation zum Headless-Modus.

Voraussetzungen

So funktioniert der Headless-Modus

Der Schalter -p steht für „print“. Claude Code bearbeitet die Aufgabe, gibt die Antwort aus und beendet sich:

Code
claude -p "Fasse die Datei README.md in drei Sätzen zusammen"

Wenn Du das im Ordner eines Projekts ausführst, liest Claude die Datei und gibt die Zusammenfassung im Terminal aus. Im Headless-Modus gibt es niemanden, der Rückfragen zu Berechtigungen beantworten kann. Deshalb legst Du vorher fest, welche Werkzeuge Claude ohne Nachfrage nutzen darf:

Code
claude -p "Zeig mir die letzten fünf Commits mit kurzer Einordnung" --allowedTools "Bash(git log *)"

--allowedTools folgt der Syntax der Berechtigungsregeln. Bash(git log *) erlaubt jeden Befehl, der mit git log beginnt, und sonst nichts. Laut Doku ist das Leerzeichen vor dem Sternchen wichtig, ohne es würden auch Befehle wie git logsomething passen.

Schritt 1: Einen Token für Skripte erzeugen

Für automatische Läufe ohne Browser-Anmeldung erzeugst Du einen langlebigen Token, der an Dein Abo gebunden ist:

Code
claude setup-token

Der Befehl öffnet eine Anmeldung im Browser und gibt danach einen Token im Terminal aus, ohne ihn zu speichern. Leg ihn im macOS-Schlüsselbund ab, damit er nicht im Klartext in einem Skript steht:

Code
security add-generic-password -a "$USER" -s claude-code-token -w

Der Befehl fragt nach dem Passwort, hier fügst Du den Token ein. Claude Code liest ihn später aus der Umgebungsvariablen CLAUDE_CODE_OAUTH_TOKEN.

Nutzt Du statt eines Abos die API, legst Du stattdessen Deinen API-Schlüssel ab und verwendest später die Variable ANTHROPIC_API_KEY. Dann kannst Du zusätzlich den Schalter --bare setzen, den die Doku für Skripte empfiehlt. Er überspringt Hooks, Plugins und Projektdateien wie CLAUDE.md und startet dadurch schneller. Mit einem Abo-Token funktioniert --bare nicht, weil dieser Modus keine Anmeldedaten aus dem Abo liest.

Schritt 2: Das Skript für den Morgenbericht

Lege das Skript an:

Code
mkdir -p ~/bin ~/Berichte
nano ~/bin/claude-morgen.sh

Füge diesen Inhalt ein und passe den Projektpfad an:

Code
#!/bin/zsh
set -e
export CLAUDE_CODE_OAUTH_TOKEN=$(security find-generic-password -s claude-code-token -w)
cd ~/Projekte/website

BERICHT=~/Berichte/$(date +%F).md

~/.local/bin/claude -p "Erstelle einen kurzen Morgenbericht auf Deutsch für dieses Projekt. \
1. Fasse die Commits der letzten 24 Stunden zusammen. \
2. Prüfe mit npm audit, ob es Sicherheitswarnungen gibt, und nenne nur die mit hoher Dringlichkeit. \
3. Liste TODO-Kommentare im Ordner src auf, die älter als eine Woche sind. \
Antworte in höchstens 15 Zeilen." \
  --allowedTools "Read,Grep,Glob,Bash(git log *),Bash(git blame *),Bash(npm audit *)" \
  --permission-mode dontAsk \
  --max-turns 20 \
  --output-format text > "$BERICHT"

curl -s -H "Title: Morgenbericht" -H "Authorization: Bearer tk_DEIN_TOKEN" \
  -T "$BERICHT" -H "Filename: bericht.md" https://ntfy.example.com/claude

Was die einzelnen Teile tun:

  • Die erste export-Zeile holt den Token aus dem Schlüsselbund.
  • --allowedTools erlaubt nur Lesen, Suchen und drei genau umrissene Befehle. Claude kann damit nichts verändern, nichts installieren und nichts löschen.
  • --permission-mode dontAsk lehnt alles ab, was nicht ausdrücklich erlaubt ist, statt auf eine Antwort zu warten, die nie kommt.
  • --max-turns 20 begrenzt die Zahl der Arbeitsschritte. Ohne Grenze kann sich ein Agent in Ausnahmefällen festbeißen und Kontingent verbrauchen.
  • Die Ausgabe landet als Markdown-Datei mit Datum im Ordner Berichte, und curl schickt sie als Anhang an ntfy.

~/.local/bin/claude ist der Pfad, den der native Installer von Claude Code verwendet. Prüfe Deinen mit which claude und trag ihn vollständig ein, denn launchd kennt Deinen normalen Suchpfad nicht. Mach das Skript ausführbar und probiere es einmal von Hand aus:

Code
chmod +x ~/bin/claude-morgen.sh
~/bin/claude-morgen.sh

Nach ein bis zwei Minuten liegt der Bericht im Ordner und auf dem Handy.

Schritt 3: Jeden Morgen automatisch starten

Am Mac übernimmt launchd die Zeitsteuerung. Es hat gegenüber cron den Vorteil, dass es Läufe nachholt, die der Mac im Ruhezustand verpasst hat. Die vollständige Anleitung mit allen Details steht in Hintergrund-Jobs am Mac mit launchd statt cron. Hier die Kurzfassung: Leg die Datei ~/Library/LaunchAgents/de.deinname.claude-morgen.plist an, mit diesem Inhalt und Deinem Benutzernamen statt deinname:

Code
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>de.deinname.claude-morgen</string>
    <key>ProgramArguments</key>
    <array>
        <string>/bin/zsh</string>
        <string>/Users/deinname/bin/claude-morgen.sh</string>
    </array>
    <key>StartCalendarInterval</key>
    <dict>
        <key>Hour</key>
        <integer>7</integer>
        <key>Minute</key>
        <integer>30</integer>
    </dict>
    <key>StandardErrorPath</key>
    <string>/Users/deinname/Library/Logs/claude-morgen.log</string>
</dict>
</plist>

Lade den Job:

Code
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/de.deinname.claude-morgen.plist

Ab jetzt läuft der Bericht jeden Morgen um 7:30 Uhr, oder direkt nach dem Aufwachen, falls der Mac geschlafen hat.

Weitere Ideen für automatische Läufe

Das Muster lässt sich auf viele Aufgaben übertragen. Wichtig ist, dass sie lesen und berichten, aber nichts unbeaufsichtigt verändern:

  • Logdateien zusammenfassen: Fehler der letzten Nacht aus einem Server-Log herausziehen und in drei Sätzen erklären.
  • Abhängigkeiten prüfen: neue Hauptversionen wichtiger Pakete finden und die Breaking Changes aus den Changelogs zusammenfassen.
  • Dokumentation aktuell halten: prüfen, ob die README noch zu den Befehlen in package.json passt, und Abweichungen auflisten.

Sicherheit: worauf Du achten musst

Ein Agent, der unbeaufsichtigt läuft, braucht engere Grenzen als einer, dem Du zuschaust:

  • Nur lesende Werkzeuge erlauben. Edit, Write und ein pauschales Bash gehören nicht in einen automatischen Lauf. Soll Claude doch etwas ändern, lass ihn einen Vorschlag schreiben, den Du morgens prüfst.
  • Auf fremde Inhalte achten. Liest der Agent Dateien oder Webseiten, die andere geschrieben haben, könnten darin Anweisungen versteckt sein. Mit eng gefassten Werkzeugrechten bleibt der mögliche Schaden klein.
  • Kosten im Blick behalten. Jeder Lauf verbraucht Kontingent Deines Abos oder kostet bei der API Geld. --max-turns und kurze Aufgaben halten das im Rahmen.

Typische Fehler

Im Log steht, dass keine Anmeldung gefunden wurde. Der Token kommt nicht an. Prüfe mit security find-generic-password -s claude-code-token -w, ob er im Schlüsselbund liegt. Läuft der Job, bevor Du Dich angemeldet hast, ist der Schlüsselbund noch gesperrt.

Der Bericht ist leer oder endet mitten im Satz. Claude ist an --max-turns gestoßen oder ein Werkzeug wurde abgelehnt. Starte das Skript von Hand mit --output-format json statt text, dann siehst Du in der Ausgabe, wo es hakte.

command not found: claude. launchd kennt den Pfad nicht. Trag den vollständigen Pfad aus which claude ins Skript ein.

Fazit

Mit claude -p, eng gesetzten Werkzeugrechten und launchd wird Claude Code vom Chatpartner zum stillen Helfer, der jeden Morgen die Routinearbeit erledigt, bevor Du den Rechner aufklappst. Fang mit einem rein lesenden Bericht an, beobachte ein paar Tage, was herauskommt, und erweitere die Aufgaben dann Schritt für Schritt.

Weiterlesen.