Skip to content

CLI 入口與分派

源码版本v0.73.1

main.tspi 命令的行程入口。它把命令列參數解析成 AppMode,組裝 AgentSessionRuntime,再分派給三種執行模式:互動式 TUI、一次性 print、JSON-RPC。除了模式分派,它還負責遷移、session 解析、模型選擇、檔案型參數前置處理。可以理解成「把 shell 呼叫翻譯成 runtime 呼叫」的前置層。

職責

  1. 解析與分派:parseArgsArgs,resolveAppMode 算出 AppMode,據此選 runRpcMode / InteractiveMode / runPrintMode。見 packages/coding-agent/src/main.ts:98-109packages/coding-agent/src/main.ts:673-726
  2. session 解析:--continue--resume--session <id|path>--fork <id>--no-session 之間的衝突校驗和路徑解析,把 session 識別碼定位到磁碟上的 .jsonl。見 packages/coding-agent/src/main.ts:188-212packages/coding-agent/src/main.ts:147-172
  3. 服務組裝:呼叫 createAgentSessionServices + createAgentSessionRuntime,把 cwd、agentDir、authStorage、擴充/技能/主題路徑串起來。見 packages/coding-agent/src/main.ts:522-560
  4. 預執行短路:pi --versionpi --exportpi package ...pi config ... 不進 runtime,直接 process.exit。見 packages/coding-agent/src/main.ts:431-478

設計動機

為什麼把入口單獨成檔案?因為 CLI 解析、session 選擇、遷移這些邏輯只發生一次,且只在行程啟動時發生。把這些塞進 createAgentSession(SDK 入口)會污染 SDK 的重用性——SDK 呼叫方通常已有 runtime 上下文,不需要解析 argv,也不需要行程級 stdout 接管。main.ts 因此只承擔「行程級雜事」:stdout 接管、訊號處理、migrations、版本檢查、fork 校驗,剩下的都委託給 SDK 和 runtime 工廠。

關鍵檔案

resolveAppMode 優先看 --mode 旗標,再退化到 stdin 是否 TTY,這樣管道用法 echo x | pi 自動走 print 模式:

typescript
// 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";
}

三岔分派處直接用 appMode 選分支,print 與 json 共用 runPrintMode,差異在 toPrintOutputMode(appMode):

typescript
// 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,
	});
	// ...
}

資料流

argv 進來後的分派路徑:

邊界與失敗

  • fork 衝突:--fork 不能與 --session--continue--resume--no-session 同時用,見 packages/coding-agent/src/main.ts:188-202
  • stdout 接管:非 interactive 模式呼叫 takeOverStdout(),把 console.log 重導到內部管道,避免 TUI 跳脫序列污染 print/rpc 輸出,退出時 restoreStdout
  • session cwd 缺失:--session 指向別的專案時,interactive 會跳出 prompt 讓使用者選,print/rpc 直接報錯退出,見 packages/coding-agent/src/main.ts:502-514
  • benchmark 模式:startupBenchmark 時只跑 interactiveMode.init() 後立刻 stop(),不進主迴圈。

小結

main.ts 是薄殼:把 argv 和 stdin 翻譯成 runtime 工廠閉包,再交給三種模式之一。runtime 工廠內部怎麼組裝看 createAgentSession 組裝,互動模式細節看 TUI 互動模式,非互動模式看 print 與 rpc 模式