Entrada y dispatch del CLI
main.ts es la entrada de proceso del comando pi. Parsea los argumentos de línea de comandos a AppMode, ensambla AgentSessionRuntime, y despacha a tres modos: TUI interactivo, print one-shot, y JSON-RPC. Además del dispatch, se ocupa de migraciones, resolución de sesión, selección de modelo y preprocesamiento de argumentos tipo archivo. Se puede entender como la capa previa que "traduce invocaciones del shell en invocaciones del runtime".
Responsabilidades
- Parseo y dispatch:
parseArgsproduceArgs,resolveAppModecalculaAppMode, y en función de eso se eligerunRpcMode/InteractiveMode/runPrintMode. Verpackages/coding-agent/src/main.ts:98-109ypackages/coding-agent/src/main.ts:673-726. - Resolución de sesión: validación de conflictos y resolución de rutas entre
--continue,--resume,--session <id|path>,--fork <id>,--no-session, localizando el identificador de sesión al.jsonlen disco. Verpackages/coding-agent/src/main.ts:188-212ypackages/coding-agent/src/main.ts:147-172. - Ensamblaje de servicios: invoca
createAgentSessionServices+createAgentSessionRuntimepara atar cwd, agentDir, authStorage y rutas de extensiones/skills/temas. Verpackages/coding-agent/src/main.ts:522-560. - Cortocircuito pre-run:
pi --version,pi --export,pi package ...,pi config ...no entran al runtime y hacenprocess.exitdirectamente. Verpackages/coding-agent/src/main.ts:431-478.
Motivación de diseño
¿Por qué separar la entrada en su propio archivo? Porque el parseo del CLI, la selección de sesión y las migraciones ocurren una sola vez, y sólo al arrancar el proceso. Meter todo eso en createAgentSession (entrada del SDK) contaminaría la reutilización del SDK: el llamador del SDK suele ser un programa que ya tiene contexto de runtime, no necesita parsear argv ni tomar control del stdout a nivel de proceso. main.ts sólo carga "tareas de proceso": toma de stdout, gestión de señales, migraciones, comprobación de versión, validación de fork; el resto se delega al SDK y a las fábricas de runtime.
Archivos clave
packages/coding-agent/src/main.ts:54-71—readPipedStdin, lee stdin pipeada cuando no es TTY, como mensaje inicial de print mode.packages/coding-agent/src/main.ts:98-109—resolveAppMode, combinaArgs.mode/Args.printconstdin.isTTYpara calcular uno de los cuatroAppMode.packages/coding-agent/src/main.ts:423-478— Inicio demain: migraciones, offline, cortocircuito package/config, diagnósticos de args.packages/coding-agent/src/main.ts:522-560— Closure de fábricacreateRuntime, captura las rutas de extensiones/skills/temas parseadas por el CLI y las pasa acreateAgentSessionServices.packages/coding-agent/src/main.ts:673-726— Tres bifurcaciones de dispatch:runRpcMode/new InteractiveMode/runPrintMode.packages/coding-agent/src/cli/— Submódulo CLI, contieneargs.ts,file-processor.ts,initial-message.ts,session-picker.ts,list-models.ts.
resolveAppMode prioriza el flag --mode y cae a si stdin es TTY, de modo que el uso con pipe echo x | pi entra automáticamente en print:
// 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";
}La bifurcación elige rama según appMode; print y json comparten runPrintMode, la diferencia está en 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,
});
// ...
}Flujo de datos
Camino de dispatch al entrar argv:
Límites y fallos
- Conflicto de fork:
--forkno se puede combinar con--session,--continue,--resume,--no-session. Verpackages/coding-agent/src/main.ts:188-202. - Toma de stdout: en modo no interactivo se invoca
takeOverStdout(), redirigiendoconsole.loga un pipe interno para evitar que el escape TUI contamine la salida print/rpc; al salir,restoreStdout. - Cwd de sesión ausente: si
--sessionapunta a otro proyecto, interactive abre un prompt para que el usuario elija; print/rpc fallan y salen. Verpackages/coding-agent/src/main.ts:502-514. - Modo benchmark: en
startupBenchmarksólo se llamainteractiveMode.init()y luegostop(), sin entrar al bucle principal.
Resumen
main.ts es una capa fina: traduce argv y stdin en un closure de fábrica de runtime, y se lo pasa a uno de los tres modos. Cómo se ensambla el runtime por dentro en ensamblaje createAgentSession; el modo interactivo en modo interactivo TUI; los modos no interactivos en modos print y rpc.