diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 59a0f050..f847a342 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -13,6 +13,7 @@ ### Fixed +- Piped stdin now works correctly: `echo foo | pi` is equivalent to `pi -p foo`. When stdin is piped, print mode is automatically enabled since interactive mode requires a TTY ([#708](https://github.com/badlogic/pi-mono/issues/708)) - Session tree now preserves branch connectors and indentation when filters hide intermediate entries so descendants attach to the nearest visible ancestor and sibling branches align. Fixed in both TUI and HTML export ([#739](https://github.com/badlogic/pi-mono/pull/739) by [@w-winter](https://github.com/w-winter)) - Added `upstream connect`, `connection refused`, and `reset before headers` patterns to auto-retry error detection ([#733](https://github.com/badlogic/pi-mono/issues/733)) diff --git a/packages/coding-agent/src/main.ts b/packages/coding-agent/src/main.ts index 996b0922..abcc838c 100644 --- a/packages/coding-agent/src/main.ts +++ b/packages/coding-agent/src/main.ts @@ -30,6 +30,29 @@ import { runMigrations, showDeprecationWarnings } from "./migrations.js"; import { InteractiveMode, runPrintMode, runRpcMode } from "./modes/index.js"; import { initTheme, stopThemeWatcher } from "./modes/interactive/theme/theme.js"; +/** + * Read all content from piped stdin. + * Returns undefined if stdin is a TTY (interactive terminal). + */ +async function readPipedStdin(): Promise { + // If stdin is a TTY, we're running interactively - don't read stdin + if (process.stdin.isTTY) { + return undefined; + } + + return new Promise((resolve) => { + let data = ""; + process.stdin.setEncoding("utf8"); + process.stdin.on("data", (chunk) => { + data += chunk; + }); + process.stdin.on("end", () => { + resolve(data.trim() || undefined); + }); + process.stdin.resume(); + }); +} + async function prepareInitialMessage( parsed: Args, autoResizeImages: boolean, @@ -310,6 +333,18 @@ export async function main(args: string[]) { return; } + // Read piped stdin content (if any) - skip for RPC mode which uses stdin for JSON-RPC + if (parsed.mode !== "rpc") { + const stdinContent = await readPipedStdin(); + if (stdinContent !== undefined) { + // Force print mode since interactive mode requires a TTY for keyboard input + parsed.print = true; + // Prepend stdin content to messages + parsed.messages.unshift(stdinContent); + } + time("readPipedStdin"); + } + if (parsed.export) { try { const outputPath = parsed.messages.length > 0 ? parsed.messages[0] : undefined;