Fix piped stdin support, auto-enable print mode

When stdin is piped (not a TTY), read the content and treat it as the
first message. Since interactive mode requires a TTY for keyboard input,
print mode is automatically enabled.

- echo foo | pi -> equivalent to pi -p foo
- echo foo | pi -p -> equivalent to pi -p foo
- RPC mode unaffected (uses stdin for JSON-RPC)

fixes #708
This commit is contained in:
Mario Zechner 2026-01-16 03:18:56 +01:00
parent c08801e4c5
commit c50bfec01b
2 changed files with 36 additions and 0 deletions

View file

@ -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))

View file

@ -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<string | undefined> {
// 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;