CLI-Eingang und Verteilung
main.ts ist der Prozess-Eingang des pi-Befehls. Es parst die Kommandozeilen-Argumente in einen AppMode, baut das AgentSessionRuntime zusammen und verteilt an drei Laufmodi: interaktives TUI, einmaliges print, JSON-RPC. Außer der Modus-Verteilung übernimmt es Migrationen, Session-Auflösung, Modellauswahl und Vorverarbeitung dateibasierter Argumente. Man kann es als vorgelagerte Schicht verstehen, die einen Shell-Aufruf in einen Runtime-Aufruf übersetzt.
Verantwortung
- Parse und verteilen:
parseArgserzeugtArgs,resolveAppModeberechnetAppMode, danach wirdrunRpcMode/InteractiveMode/runPrintModegewählt. Siehepackages/coding-agent/src/main.ts:98-109,packages/coding-agent/src/main.ts:673-726. - Session-Auflösung: Konflikt-Prüfung und Pfad-Auflösung zwischen
--continue,--resume,--session <id|path>,--fork <id>,--no-session, zeigt die Session-Kennung auf die.jsonlauf der Festplatte. Siehepackages/coding-agent/src/main.ts:188-212,packages/coding-agent/src/main.ts:147-172. - Service-Zusammenbau: Ruft
createAgentSessionServices+createAgentSessionRuntimeauf, verdrahtet cwd, agentDir, authStorage und Extensions-/Skills-/Theme-Pfade. Siehepackages/coding-agent/src/main.ts:522-560. - Vorlauf-Kurzschluss:
pi --version,pi --export,pi package ...,pi config ...gehen nicht in die Runtime, sondern direktprocess.exit. Siehepackages/coding-agent/src/main.ts:431-478.
Entwurfsmotivation
Warum den Eingang in eine eigene Datei auslagern? Weil CLI-Parsing, Session-Auswahl und Migrationen nur einmal passieren, und nur beim Prozess-Start. Wenn man das in createAgentSession (SDK-Eingang) steckt, verschmutzt man die Wiederverwendbarkeit des SDK - SDK-Aufrufer sind meistens Programme, die schon einen Runtime-Kontext haben, müssen kein argv parsen und brauchen keine prozessweite stdout-Übernahme. main.ts übernimmt deshalb nur "Prozess-Sachen": stdout-Übernahme, Signal-Behandlung, migrations, Versions-Check, fork-Prüfung, der Rest geht an die SDK- und Runtime-Fabriken.
Wichtige Dateien
packages/coding-agent/src/main.ts:54-71—readPipedStdin, liest bei nicht-TTY den Pipe-stdin als initiale Nachricht des print-Modus.packages/coding-agent/src/main.ts:98-109—resolveAppMode, berechnet ausArgs.mode/Args.printplusstdin.isTTYdie vierAppMode-Werte.packages/coding-agent/src/main.ts:423-478— Anfang dermain-Funktion: migrations, offline, package/config-Kurzschluss, Argument-Diagnose.packages/coding-agent/src/main.ts:522-560—createRuntime-Fabrik-Closure, fängt die CLI-geparsten Extensions-/Skills-/Theme-Pfade ein und übergibt sie ancreateAgentSessionServices.packages/coding-agent/src/main.ts:673-726— Verteilungs-Drehscheibe:runRpcMode/new InteractiveMode/runPrintMode.packages/coding-agent/src/cli/— CLI-Submodul-Verzeichnis, enthältargs.ts,file-processor.ts,initial-message.ts,session-picker.ts,list-models.ts.
resolveAppMode schaut zuerst auf das --mode-Flag und fällt dann auf "ist stdin ein TTY" zurück, damit die Pipe-Nutzung echo x | pi automatisch in den print-Modus geht:
// packages/coding-agent/src/main.ts:98-109
function resolveAppMode(parsed: Args, stdinIsTTY: boolean): AppMode {
if (parsed.mode === "rpc") {
return "rpc";
}
if (parsed.mode === "json") {
return "json";
}
if (parsed.print || !stdinIsTTY) {
return "print";
}
return "interactive";
}An der Drehscheibe wird direkt per appMode der Zweig gewählt, print und json teilen sich runPrintMode, der Unterschied liegt in toPrintOutputMode(appMode):
// packages/coding-agent/src/main.ts:673-726
if (appMode === "rpc") {
printTimings();
await runRpcMode(runtime);
} else if (appMode === "interactive") {
// ...
const interactiveMode = new InteractiveMode(runtime, {
migratedProviders,
modelFallbackMessage,
initialMessage,
initialImages,
initialMessages: parsed.messages,
verbose: parsed.verbose,
});
// ...
await interactiveMode.run();
} else {
printTimings();
const exitCode = await runPrintMode(runtime, {
mode: toPrintOutputMode(appMode),
messages: parsed.messages,
initialMessage,
initialImages,
});
// ...
}Datenfluss
Verteilungspfad, nachdem argv reinkommt:
Grenzen und Fehler
- fork-Konflikt:
--forkkann nicht mit--session,--continue,--resume,--no-sessiongleichzeitig verwendet werden, siehepackages/coding-agent/src/main.ts:188-202. - stdout-Uebernahme: Im nicht-interaktiven Modus wird
takeOverStdout()aufgerufen, dasconsole.logauf eine interne Pipe umleitet, um zu verhindern, dass TUI-Escapes den print/rpc-Output verschmutzen, beim BeendenrestoreStdout. - session cwd fehlt: Wenn
--sessionauf ein anderes Projekt zeigt, poppingt interactive einen Prompt zur Auswahl, print/rpc beendet sich direkt mit Fehler, siehepackages/coding-agent/src/main.ts:502-514. - benchmark-Modus: Bei
startupBenchmarkwird nurinteractiveMode.init()ausgeführt und sofortstop(), ohne in die Hauptschleife zu gehen.
Zusammenfassung
main.ts ist eine dünne Hülle: Es übersetzt argv und stdin in einen Runtime-Fabrik-Closure und übergibt ihn an einen der drei Modi. Wie die Runtime-Fabrik intern zusammenbaut, siehe createAgentSession Zusammenbau; die interaktiven Modus-Details siehe TUI interaktiver Modus; die nicht-interaktiven Modi siehe print und rpc Modi.