Ich habe aicodebox als Abstraktion gebaut, ein Basis-Image, ein AgentAdapter-Vertrag, du steckst die CLI irgendeines Coding-Agenten hinein und bekommst die API/MCP/Telegram/Cron-Oberfläche geschenkt. Schöne Theorie. Theorien sind einen Scheiß wert, solange du nicht tatsächlich ein zweites, völlig anderes Agenten-Binary an den Vertrag verdrahtet und zugeschaut hast, wo es blutet. Also habe ich mir pi-coding-agent geschnappt, eine CLI, die ich nicht geschrieben habe und nicht kontrolliere, und sie durch den Adapter gezwungen. Das ist pibox. Es ist kein funktionsprotziges Flaggschiff-Image, es ist der Beweis, dass die Abstraktion keine Lüge ist, und inzwischen die Referenz, von der jedes andere Kind-Image abgeschrieben wird, das von Claude Code eingeschlossen.
Wenn du den aicodebox-Beitrag schon gelesen hast, kennst du den Pitch: Modi, Adapter, REST, OpenAI-Kompatibilität, MCP, Telegram, Cron, alles in der Basisschicht, alles kostenlos, sobald du einen Adapter schreibst. Das tippe ich hier nicht noch einmal. Dieser Beitrag handelt davon, was es tatsächlich gekostet hat, ein fremdes Agenten-Binary ohne Schummeln an diesen Vertrag zu schrauben, und davon, wie klein die Oberfläche am Ende wurde, nachdem ich aufgehört hatte, Scheiß hinzuzufügen, der nichts trug.
Was pi Dir Nicht Gibt
pi-coding-agent ist eine völlig ordentliche CLI. Sie ist außerdem, wie jede Agenten-CLI auf diesem Planeten, für einen Menschen an einem Terminal gebaut, nicht für einen Serverprozess, der strukturierte Metadaten zurückbraucht. Sie in aicodebox zu verdrahten hieß, jede einzelne der sehr vernünftigen, sehr terminalförmigen Annahmen von pi zu umgehen:
- Zwei Ausgabemodi, einer davon für eine API unbrauchbar. pi hat
--mode textund--mode json. Der Textmodus gibt dir die Worte des Assistenten und sonst absolut nichts, keine Session-ID, keinen Verbrauch, keine Events pro Turn. Gut für einen Menschen, nutzlos für eine Route, die Tokens abrechnen und Sitzungen fortsetzen muss. - Null native JSON-Schema-Durchsetzung. pi hat kein
--schema-Flag, keinen Modus für strukturierte Ausgabe. Es redet einfach. - Keine native
ANTHROPIC_BASE_URL-Unterstützung. pis eigene Doku sagt „nimm models.json“, das heißt, es liest die Umgebungsvariable nicht, über die ein Anthropic-kompatibler Proxy (Z.AI, OpenRouter, dein eigenes Gateway) zu funktionieren erwartet. - Ein eingebauter Provider, der dir still das Routing kapert. pis
zai-Provider beansprucht automatisch jedenglm-*-Modellnamen, was heißt, dass eine Anfrage, die über deineANTHROPIC_BASE_URL-Überschreibung hätte laufen sollen, leise zu einem Provider umgeleitet werden kann, den du nie verlangt hast. - Keine erstklassige MCP-aus-der-Workspace-Config-Unterstützung so wie Claude Code sie hat, also kein automatisches Aufgreifen einer
.mcp.jsonaus dem Workspace.
Nichts davon ist pis Schuld, es ist eine CLI, sie macht CLI-Dinge. Aber „der Agent kann nichts dafür“ verschafft dir keine API. Irgendjemand muss übersetzen. Dieser Irgendjemand ist eine einzige Python-Klasse.
Drei Umgebungsvariablen Sind Die Ganze Verankerung
Hier ist der Teil, der pibox den Titel „Referenz“ tatsächlich verdient. Zieh das Dockerfile auf das herunter, was es pibox-spezifisch setzt, und du bekommst exakt drei Umgebungsvariablen:
ENV AICODEBOX_ADAPTER=pibox.adapter:PiAdapter
AICODEBOX_AGENT_BINARY=pi
PIBOX_IMAGE_VARIANT=minimalDas war’s. Das ist die gesamte deklarative Oberfläche, die ein Kind-Image braucht: richte AICODEBOX_ADAPTER auf ein importierbares module:ClassName, sag der Basis, welchen Binary-Namen sie aufrufen soll, und die ganze Modus-Maschinerie, REST, OpenAI-Kompatibilität, MCP, Telegram, Cron, erwacht darum herum. PIBOX_IMAGE_VARIANT gehört überhaupt nicht zu diesem Vertrag, es beschriftet nur, in welchem der beiden veröffentlichten Images du gerade sitzt, dem minimalen oder dem vollen. Alles andere im Dockerfile installiert entweder das Agenten-Binary, oder das Python-Paket, das den Adapter implementiert, oder es ist Branding. Wenn du dein eigenes Kind-Image baust und das Dockerfile eine dritte Umgebungsvariable braucht, damit der Agent läuft, tust du etwas, wozu der Vertrag dich nie zwingen wollte, geh und lies die AgentAdapter-Klasse der Basis nochmal.
Der Rest des Builds ist mit Absicht langweilig: npm install -g @earendil-works/[email protected], festgenagelt, nicht @latest, weil „funktioniert heute“ und „funktioniert in sechs Monaten“ nicht dieselbe Behauptung sind, dann uv pip install --system --break-system-packages --no-deps /opt/pibox für das Adapter-Paket selbst (--no-deps, weil aicodebox ohnehin im Basis-Image steckt und es erneut aufzulösen vergeudete Arbeit ist).
Der Adapter: build_argv Ist Pflicht, Alles Andere Ist Eine Wahl
Die AgentAdapter-Klasse der Basis hat genau eine Methode, die NotImplementedError wirft, wenn du sie nicht überschreibst: build_argv. Jeder andere Hook, validate, translate_auth, parse_output, parse_events, parse_stream_event, interactive_argv, passthrough_argv, auth_paths, bringt einen funktionierenden Default mit. PiAdapter überschreibt sie trotzdem alle, weil pis CLI schräg genug ist, dass die Defaults Müll produzieren würden. Hier ist, was dir jeder einzelne tatsächlich bringt, geprüft gegen pibox/pibox/adapter.py:
build_argv(Pflicht): ruft immerpi -p --mode jsonauf, nie--mode text, genau damit der Adapter den vollen Session-Event-Stream bekommt statt nackter Prosa. Die Session-Behandlung verzweigt dreifach:--session <id>beim Fortsetzen,--no-sessionfür Flüchtiges, sonst--continue.validate(optional, die Basis prüft nuroutput_format): weist einenthinking-Wert außerhalb vonoff/minimal/low/medium/high/xhighzurück und weisttools_allowlistin Kombination mitno_toolsals sich gegenseitig ausschließenden Unsinn zurück.translate_auth(optional, der Default der Basis tut nichts): trotzdem überschrieben, gibt weiterhin nichts zurück, weil piANTHROPIC_API_KEY/OPENAI_API_KEY/OPENROUTER_API_KEY/GEMINI_API_KEY/ZAI_API_KEYnativ liest. Die Überschreibung existiert, um diese Tatsache im Code zu dokumentieren, nicht um das Verhalten zu ändern.parse_output(optional, der Default der Basis trimmt stdout bloß als Klartext): läuft durch pis NDJSON, ziehtsessionfür die Session-ID,message_endfür Assistententext und Verbrauch, undturn_endals Verbrauchs-Rückfall. Hier wird auchprovider_errorgefüllt: wenn ein Assistenten-TurnstopReason=errorplus eineerrorMessageträgt, pis Art, eine Ablehnung von oben, ein Rate Limit oder einen Auth-Fehler zu melden, fängt der Adapter das ab und reicht es überRunResult.provider_errorweiter, damit die OpenAI-Route einen echten400zurückgeben kann statt einer200mit leerem Text.parse_events(optional, der Default der Basis gibt eine leere Liste zurück): dekodiert jede NDJSON-Zeile als JSON für den Ausgabemodusjson-verbose; kaputte Zeilen werden mit einer Warnung verworfen, nicht mit einem Absturz.parse_stream_event(optional, der Default der Basis behandelt jede Zeile als rohes Text-Delta): dekodiert pismessage_update.assistantMessageEvent-Stream, leitet nurtext_deltaauf die Leitung weiter und schlucktthinking_*– und Tool-Use-Deltas stillschweigend, damit das interne Nachdenken des Modells nie in das OpenAI-kompatiblecontent-Feld leckt.auth_paths(optional, der Default der Basis gibt nichts zum Persistieren zurück): listet pis echten Zustand auf,~/.pi/agent/auth.json,settings.json,models.jsonund dassessions-Verzeichnis, damit OAuth-Tokens und Sitzungshistorie eindocker startüberleben, statt sich in Luft aufzulösen.
Es steckt außerdem ein Hack in build_argv, auf den ich nicht stolz bin, den ich aber vorbehaltlos verteidigen werde: wenn ANTHROPIC_BASE_URL gesetzt ist und der Aufrufer nicht schon einen Provider in extra_args gewählt hat, injiziert der Adapter zwangsweise --provider anthropic. Warum? Weil pis eingebauter zai-Provider automatisch jeden glm-*-Modellnamen beansprucht und deine Base-URL-Überschreibung komplett umgeht. Ohne das erzwungene Flag ignoriert pibox, auf einen Z.AI-kompatiblen Proxy gerichtet und nach einem glm-4.6-Modell gefragt, still deinen Proxy und redet direkt mit dem, was pis zai-Provider für richtig hält. Das ist die Sorte Bug, die jemanden einen Nachmittag und ein Support-Ticket kostet, bevor irgendwer bemerkt, dass der Verkehr den Proxy nie berührt hat.
Der Schema-Modus: pi Hat Keine Native Durchsetzung, Also Ist Es Ein Anbau am System-Prompt
pi validiert keine JSON-Schemas. Es hat kein Flag dafür. Wenn eine Anfrage also ein jsonSchema trägt, bleibt dem Adapter nur ein Zug: eine Anweisung an den System-Prompt hängen, die dem Modell auf einfachem Englisch sagt „antworte mit einem einzigen JSON-Dokument, das diesem Schema entspricht, keine Prosa, keine Codezäune“, und ihm das rohe Schema-JSON dazu reichen. Die gesamte tatsächliche Durchsetzung, das Ergebnis parsen, es gegen das Schema prüfen, bei Fehlschlag bis zu dreimal mit einem korrigierenden Prompt erneut versuchen, passiert in der aicodebox-Basisschicht, nicht in pibox. Die einzige Aufgabe des Adapters ist, das Modell Richtung Konformität zu lenken; ob das Modell tatsächlich konform ist, hat er nicht mitzureden.
Diese Trennung ist wichtig wegen dem, was danach kam. Der Schema-Retry-Helfer der Basis wurde schrittweise schlauer, ohne dass sich der Adaptercode von pibox überhaupt geändert hätte, und die Changelog-Kommentare im Dockerfile selbst lesen sich wie ein Verhaltenstagebuch der Basis, die sich unter einem völlig stabilen Adaptervertrag weiterentwickelt: Retries, die die ursprüngliche Aufgabe neu formulieren statt nur den Fehler (damit ein Schema, das eine Auswahl aus einem großen Enum verlangt, nicht blind erneut versucht), flüchtige Workspaces pro Anfrage, damit eine 100k-Token-Anfrage, die drei Retries braucht, grob 1,5k Tokens korrigierenden Mehraufwand zahlt, statt die vollen 100k dreimal abzuspielen, und, zuletzt, stream:true in Kombination mit Tool-Calling oder Schema-Modus gibt keinen trockenen 400 mehr zurück. Es berechnet jetzt die vollständige Antwort ungestreamt und spielt sie als einen einzigen gepufferten SSE-Stream ab: ein Rollen-Chunk, ein Content- oder Tool-Calls-Delta, ein Abschluss-Chunk, [DONE]. Normaler Chat streamt weiterhin Token für Token. Nichts davon hat pibox/pibox/adapter.py berührt. Genau das ist der Sinn des Vertrags, das Kind-Image darf gar nicht wissen oder sich darum scheren, dass die Basis unter ihm schlauer geworden ist.
Die mcp-bridge-Erweiterung: pi das Config-Format von jemand anderem geben
pi liest eine Workspace-.mcp.json nicht nativ, so wie Claude Code es tut. pibox liefert eine TypeScript-Erweiterung mit, pibox/extensions/mcp-bridge/index.ts, die das für pi erledigt: beim Sitzungsstart liest sie die .mcp.json aus dem Workspace im claude-code-Schema (mcpServers.<name>.{command,args,env}), startet jeden Server über stdio, ruft listTools() auf und registriert jedes Tool bei pi unter einem bereinigten Namen mcp__<server>__<tool> über pi.registerTool(). Tool-Aufrufe werden an den MCP-Server weitergereicht und das Ergebnis kommt über denselben Kanal zurück, den pis eingebaute Tools benutzen, das Modell merkt keinen Unterschied.
Das npm install für diese Erweiterung passiert zur Build-Zeit, RUN cd /opt/pibox/extensions/mcp-bridge && npm install --omit=dev --no-audit --no-fund, nicht beim ersten Containerstart. Das ist eine bewusste Entscheidung: niemand will, dass sein erster Agentenlauf an einem npm-Resolve hängen bleibt. Ein init.d-Skript, pibox/init.d/10-pi-extensions.sh, verdrahtet den Pfad der vorinstallierten Erweiterung beim ersten Start in das extensions-Array von ~/.pi/agent/settings.json, idempotent, über einen jq-Merge, der mit unique dedupliziert, damit ein erneuter Lauf keine doppelten Einträge anhäuft.
Darin steckt außerdem ein Shutdown-Rennen, das Erwähnung verdient, weil es die Sorte Bug ist, die nur in Produktion auftaucht: pi -p hängt, nachdem es seine finale Antwort ausgegeben hat, wenn die gestarteten MCP-Server-Subprozesse die Event-Loop noch offen halten. Die Erweiterung lauscht auf session_shutdown, lässt das Schließen jedes MCP-Clients gegen einen Timeout von 2 Sekunden antreten und ruft dann, Gürtel und Hosenträger, eine halbe Sekunde später zwangsweise process.exit(0), weil manche Node-Versionen die Loop selbst dann am Leben halten, wenn close() längst aufgelöst hat. Ohne das würde ein einmaliger API-Aufruf einfach dasitzen, bis ihn etwas von außen killt.
pibox-entrypoint: 17 Aliase und eine Config-Neuerzeugung beim Boot
pibox-entrypoint.sh existiert einzig dazu, pibox seine eigene gebrandete Umgebungsvariablen-Oberfläche zu geben, ohne irgendeine Logik der Basis zu duplizieren. Es definiert eine Liste von 17 Suffixen, API_MODE, API_MODE_PORT, API_MODE_TOKEN, TELEGRAM_MODE, TELEGRAM_MODE_TOKEN, TELEGRAM_MODE_CONFIG, TELEGRAM_MODE_OVERRIDES, CRON_MODE, CRON_MODE_FILE, CRON_MODE_HISTORY_DIR, MCP_MODE, MCP_MODE_PORT, MCP_MODE_TOKEN, WORKSPACE, AVAILABLE_MODELS, AVAILABLE_EFFORTS, CONTAINER_NAME, und für jedes davon kopiert es den Wert herüber, falls PIBOX_<suffix> gesetzt ist und AICODEBOX_<suffix> nicht. AICODEBOX_* gewinnt, wenn beide gesetzt sind, damit fortgeschrittene Nutzer nicht von den darunterliegenden Namen ausgesperrt werden. Die beiden Adapter-Auswahlvariablen, ADAPTER und AGENT_BINARY, sind bewusst von dieser Liste ausgenommen; die werden vom Dockerfile festgenagelt und sind nichts, was ein Nutzer zur Laufzeit überschreiben können sollte.
Der Entrypoint führt außerdem setup-provider-env.sh bei jedem einzelnen Boot aus, nicht nur beim ersten, und dieses „bei jedem Boot“ ist die Reparatur eines echten Bugs, keine Stilentscheidung. Das Skript erzeugt pis anthropic-Provider-Eintrag in ~/.pi/agent/models.json aus ANTHROPIC_BASE_URL / ANTHROPIC_MODEL bei jedem Containerstart neu. Früher lief es über init.d, das per Design nur einmal pro Containerleben feuert, in Ordnung für einen Wegwerfcontainer, kaputt in dem Moment, in dem jemand ~/.aicodebox oder ~/.pi als persistentes Volume einhängt, denn dann überlebt der Init-Marker den Neubau und eine geänderte ANTHROPIC_BASE_URL erreicht models.json still nie wieder. Es in den Entrypoint zu verschieben heißt, dass die Base-URL-Konfiguration bei jedem Start aufgefrischt wird, persistentes Volume hin oder her.
v0.16.0: die Anthropic-Route ist jetzt nur eine von vieren
Alles oben ist um eine einzige Form herum geschrieben, pis anthropic-Provider auf den Endpoint zu biegen, auf den du ANTHROPIC_BASE_URL richtest. Das funktioniert weiterhin, und das läuft auch weiterhin, wenn du nur das setzt. Aber es ist jetzt der Altpfad.
v0.16.0 hat den Provider generisch gemacht. Fünf Variablen beschreiben jeden beliebigen HTTP-Endpoint: PIBOX_PROVIDER_BASE_URL, PIBOX_PROVIDER_API, PIBOX_PROVIDER_API_KEY, PIBOX_PROVIDER_MODEL, PIBOX_PROVIDER_NAME. Die mit API wählt die Form auf der Leitung, und davon gibt es vier: openai-completions, openai-responses, anthropic-messages, google-generative-ai. Was LiteLLM und Z.AI abdeckt, ohne so zu tun, als wäre eines von beiden Anthropic.
Der Zwangsinjektions-Hack in build_argv ist entsprechend erwachsen geworden. Er ist jetzt ein Selektor mit zwei Zweigen: nimm den konfigurierten Provider, falls es einen gibt, sonst fall auf anthropic zurück, und injiziere --model gleich mit.
Setz PIBOX_PROVIDER_* und ANTHROPIC_BASE_URL gleichzeitig und pibox steigt aus, statt sich für eines zu entscheiden. Gut. Zwei halb konfigurierte Routing-Schemata, die sich widersprechen, sind genau der Fehlschlag, den du laut und beim Boot willst, nicht leise und drei Tool-Aufrufe tief.
Ein Detail, das es wert ist, geklaut zu werden: der Upstream-Schlüssel lebt ausschließlich in der Umgebung des Pi-Prozesses. Was in models.json landet, ist eine Referenz der Art $OPENAI_API_KEY, nicht das Geheimnis selbst, die Konfigurationsdatei auf der Platte bleibt also langweilig, falls jemand sie liest.
v0.18.0: endlich hat es einen Wrapper bekommen
Alles darunter war früher die einzige Art, es zu starten: handgeschriebene docker run-Zeilen, bei denen die Mounts und die Umgebung jedes Mal ausbuchstabiert wurden. claudebox und codexbox hatten beide einen Host-Wrapper und einen Installer. pibox nicht, weil es das unglamouröse Kind war und niemand dazu kam.
Jetzt hat es einen. install.sh legt einen pibox-Befehl auf deinen PATH, und der Wrapper erledigt die Container-Klempnerei, es in einem Verzeichnis zu starten ist also einfach pibox. Zehn Variablen steuern ihn: PIBOX_DATA_DIR, PIBOX_STATE_DIR und PIBOX_SSH_DIR dafür, wo der Zustand liegt, PIBOX_IMAGE und PIBOX_FULL für welches Image, PIBOX_DETACH für Läufe im Hintergrund, PIBOX_ENV_* und PIBOX_MOUNT_* zum Durchreichen von Umgebung und Mounts, und PIBOX_INSTALL_DIR mit PIBOX_BIN_NAME, um zu entscheiden, wo der Befehl landet und wie er heißt.
v0.17.0 hat ihm außerdem eine psyb0t/pibox:latest-full-Variante gegeben, gebaut auf aicodebox:v0.15.0-full, wohin die gemeinsame Toolchain aus Go, Node, Python, Datenbankclients und Ops-Werkzeug gewandert ist. pibox hat aufgehört, irgendetwas davon selbst zu bauen.
Und die Boxen können sich gegenseitig aufrufen
Installier pibox, claudebox und codexbox in dasselbe Verzeichnis und jeder Wrapper mountet die anderen beiden schreibgeschützt unter /usr/local/bin/<name>, pi kann also eine Aufgabe an einen anderen Agenten weitergeben, ohne seine Sitzung zu verlassen. Verschachtelte Läufe tragen einen versionierten Host-Kontext, AICODEBOX_LAUNCH_CONTEXT_VERSION=1 plus AICODEBOX_HOST_HOME, AICODEBOX_HOST_WORKSPACE, AICODEBOX_HOST_WRAPPER_DIR und je Agent ein Home und einen Wrapper-Pfad, weil ein Pfad innerhalb eines Containers nichts über den Host aussagt. Ein verschachtelter Lauf überspringt die Auth-Dateischreibvorgänge, die ein Lauf auf oberster Ebene macht, ein Kind kann also die Zugangsdaten seines Aufrufers nicht überschreiben. AICODEBOX_ENV_* und AICODEBOX_MOUNT_* reichen an alle Boxen auf einmal weiter, und AICODEBOX_MANAGED_INSTALL=1 ist eine nicht interaktive Installation, die sich weigert, einen vorhandenen SSH-Schlüssel zu überschreiben.
Der Bug, der die Modellliste zur Lüge machte
Erwähnenswert, weil der Abschnitt oben etwas verspricht, was v0.16.0 tatsächlich nicht geliefert hat. Du konntest mehrere Modelle in PIBOX_AVAILABLE_MODELS auflisten, aber nur das in PIBOX_PROVIDER_MODEL genannte war beim Provider registriert. Frag nach irgendeinem der anderen und der Lauf starb mit Stream ended without finish_reason, was dir über die eigentliche Ursache nichts sagt. v0.16.2 registriert jedes beworbene Modell, die Liste bedeutet also, was sie sagt. Der Start warnt jetzt auch, wenn PIBOX_PROVIDER_BASE_URL und PIBOX_PROVIDER_API sich uneinig sind, welches Protokoll du sprichst, was der andere Weg zu einem verwirrenden Fehlschlag drei Aufrufe tief ist.
Noch eines von der Basis geerbt: eventMode an einem Lauf steuert die Event-Aufbewahrung unabhängig davon, ob du ein Schema verlangt hast. full gibt dir die nativen Aufzeichnungen des Providers unangetastet zurück statt der zusammengefassten Fassung.
Benutzung
Einmalig, ohne Server:
docker run --rm
-e ANTHROPIC_AUTH_TOKEN=your-token
-e ANTHROPIC_BASE_URL=https://api.z.ai/api/anthropic
-e ANTHROPIC_MODEL=glm-4.6
psyb0t/pibox:latest
-p "list the files in /workspace"API-Server, dasselbe Umgebungsvariablen-Aliasing, das in jedem psyb0t-Image auftaucht:
docker run -d --network host
-e PIBOX_API_MODE=1
-e PIBOX_API_MODE_TOKEN=your-secret
-e PIBOX_AVAILABLE_MODELS=glm-4.6,glm-4.5-air
-e ANTHROPIC_AUTH_TOKEN=your-token
-e ANTHROPIC_BASE_URL=https://api.z.ai/api/anthropic
-e ANTHROPIC_MODEL=glm-4.6
-v "$PWD/workspace:/workspace"
psyb0t/pibox:latestOder bau es dir selbst auf der festgenagelten Basis:
# derives VERSION from pibox/pyproject.toml, pulls
# psyb0t/aicodebox:v0.14.0, tags :v<VERSION> and :latest
make buildPIBOX_API_MODE=1 fährt FastAPI auf :8080 hoch und weigert sich zu starten, wenn PIBOX_AVAILABLE_MODELS nicht gesetzt ist, denn es gibt keinen sinnvollen Default, da pi die Modellliste jedes beliebigen Providers fahren kann, und eine still leere Modellliste ist schlimmer als ein Startfehler. Es stellt /run, /openai/v1/chat/completions, /files/* bereit, und, wenn PIBOX_MCP_MODE=1, /mcp auf demselben Port eingehängt. Telegram- und Cron-Modus bekommen dieselbe Behandlung, die im aicodebox-Beitrag abgedeckt ist; lies den, wenn du die Modus-Matrix willst statt der pi-spezifischen Klempnerei.
Das Unglamouröse Kind
pibox ist nicht das schillernde Kind-Image. Es ist das, welches beweist, dass der Vertrag der Basis den Kontakt mit einem Agenten-Binary übersteht, das davon ausging, ein Mensch und keine API werde seine Ausgabe lesen. Zwei Umgebungsvariablen verankern den Adapter, eine dritte beschriftet nur das Image, eine Python-Klasse übersetzt pis terminalförmige CLI in etwas, das die Basis fahren kann, und die Feature-Arbeit der Basis selbst, Streaming, Schema-Retries, Tool-Calling, ist darunter gelandet, ohne dass sich eine einzige Zeile Adaptercode bewegt hätte. Genau das soll „Referenzimplementierung“ heißen: nicht die größte, sondern die, die beweist, dass die kleinste Oberfläche immer noch funktioniert.
Wenn du den volleren Agenten willst, dieselbe Basis, Claude Code statt pi, das ist der claudebox-Beitrag. Wenn du piboxs Z.AI-gewürzten Bruder als echten Provider in einem größeren Stack laufen sehen willst statt allein, das steht in dem aigate-Beitrag, pibox-zai ist einer der Provider, zu denen aigate routet. Der Code liegt auf GitHub. Es ist das uninteressanteste Image der Familie und das, auf das ich als erstes zeigen würde, wenn du dein eigenes schreibst.
Wie Du Es In Deinen Agenten Installierst
Das uninteressanteste Image der Familie liefert trotzdem denselben Installationsweg wie der Rest. Alles unter .agents/ ist in einem einzigen Marktplatz katalogisiert, es sind also zwei Befehle:
claude plugin marketplace add psyb0t/agents
claude plugin install pibox@psyb0tCodex benutzt denselben Marktplatz mit einem anderen Verb, codex plugin add pibox@psyb0t, weil es kein codex plugin install gibt. Es findet den Skill in einem Checkout des Repos auch von allein, da es .agents/skills/ nativ scannt, ganz ohne dass irgendetwas installiert ist. Es ist inzwischen auch in der offiziellen MCP Registry gelistet, ein Client, der Server von dort auflöst, kann es also finden, ohne eine URL in die Hand gedrückt zu bekommen.