fix(cli): restored piped session persistence
- Treated undefined stdin TTY state as redirected input. - Honored PI_CODING_AGENT_SESSION_DIR with CLI precedence. Fixes #7378
This commit is contained in:
@@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed redirected stdin being ignored when Bun reports a pipe with an undefined `isTTY`, so JSON/print sessions now process piped prompts and persist them under `--session-dir` or `PI_CODING_AGENT_SESSION_DIR` as requested ([#7378](https://github.com/can1357/oh-my-pi/issues/7378)).
|
||||
|
||||
## [17.2.4] - 2026-08-01
|
||||
|
||||
### Added
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* CLI argument parsing and help display
|
||||
*/
|
||||
import { APP_NAME, CONFIG_DIR_NAME, logger } from "@oh-my-pi/pi-utils";
|
||||
import { $env, APP_NAME, CONFIG_DIR_NAME, logger } from "@oh-my-pi/pi-utils";
|
||||
import chalk from "chalk";
|
||||
import { CLI_THINKING_LEVELS, type ConfiguredThinkingLevel, parseCliThinkingLevel } from "../thinking";
|
||||
import { BUILTIN_TOOL_NAMES, HIDDEN_TOOL_NAMES, normalizeToolNames } from "../tools/builtin-names";
|
||||
@@ -145,6 +145,7 @@ export function parseArgs(inputArgs: string[], extensionFlags?: Map<string, { ty
|
||||
fileArgs: [],
|
||||
unknownFlags: new Map(),
|
||||
unrecognizedFlags: [],
|
||||
sessionDir: $env.PI_CODING_AGENT_SESSION_DIR || undefined,
|
||||
};
|
||||
|
||||
// `--` ends option parsing (POSIX end-of-options). Everything after it is
|
||||
|
||||
@@ -188,8 +188,9 @@ function applyAcpDefaultSettingOverrides(targetSettings: Settings = settings): v
|
||||
applyDefaultSettingOverrides(HOST_DEFAULTED_SETTING_PATHS, targetSettings);
|
||||
}
|
||||
|
||||
async function readPipedInput(): Promise<string | undefined> {
|
||||
if (process.stdin.isTTY !== false) return undefined;
|
||||
/** Reads a non-TTY stdin stream as prompt text. */
|
||||
export async function readPipedInput(): Promise<string | undefined> {
|
||||
if (process.stdin.isTTY === true) return undefined;
|
||||
// stdin is a pipe: a producer that never writes nor closes would block
|
||||
// startup forever with zero output. Say what we're blocked on after 1s.
|
||||
const notice = setTimeout(() => {
|
||||
|
||||
@@ -56,6 +56,23 @@ describe("OPTIONAL_VALUE_FLAGS table is honored by args.ts parseArgs", () => {
|
||||
}
|
||||
});
|
||||
|
||||
describe("--session-dir", () => {
|
||||
it("uses PI_CODING_AGENT_SESSION_DIR unless the CLI flag overrides it", () => {
|
||||
const previous = Bun.env.PI_CODING_AGENT_SESSION_DIR;
|
||||
Bun.env.PI_CODING_AGENT_SESSION_DIR = "/env/sessions";
|
||||
try {
|
||||
expect(parseArgs([]).sessionDir).toBe("/env/sessions");
|
||||
expect(parseArgs(["--session-dir", "/cli/sessions"]).sessionDir).toBe("/cli/sessions");
|
||||
} finally {
|
||||
if (previous === undefined) {
|
||||
delete Bun.env.PI_CODING_AGENT_SESSION_DIR;
|
||||
} else {
|
||||
Bun.env.PI_CODING_AGENT_SESSION_DIR = previous;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("--tools legacy aliases", () => {
|
||||
it("maps search and find to grep and glob", () => {
|
||||
const result = parseArgs(["--tools", "search,find,grep"]);
|
||||
|
||||
@@ -2,7 +2,11 @@ import { afterEach, describe, expect, it, vi } from "bun:test";
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { applyResolvedSystemPromptInputs, submitInteractiveInput } from "@oh-my-pi/pi-coding-agent/main";
|
||||
import {
|
||||
applyResolvedSystemPromptInputs,
|
||||
readPipedInput,
|
||||
submitInteractiveInput,
|
||||
} from "@oh-my-pi/pi-coding-agent/main";
|
||||
import type { SubmittedUserInput } from "@oh-my-pi/pi-coding-agent/modes/types";
|
||||
import type { CreateAgentSessionOptions } from "@oh-my-pi/pi-coding-agent/sdk";
|
||||
import { discoverTitleSystemPromptFile } from "@oh-my-pi/pi-coding-agent/system-prompt";
|
||||
@@ -12,6 +16,7 @@ const cleanupDirs: string[] = [];
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all(cleanupDirs.splice(0).map(dir => removeWithRetries(dir)));
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
function createInput(overrides: Partial<SubmittedUserInput> = {}): SubmittedUserInput {
|
||||
@@ -37,6 +42,21 @@ describe("discoverTitleSystemPromptFile", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("readPipedInput", () => {
|
||||
it("reads redirected stdin when Bun reports isTTY as undefined", async () => {
|
||||
const originalIsTTY = process.stdin.isTTY;
|
||||
const readText = vi.spyOn(Bun.stdin, "text").mockResolvedValue("piped prompt\n");
|
||||
Object.defineProperty(process.stdin, "isTTY", { value: undefined, configurable: true });
|
||||
|
||||
try {
|
||||
expect(await readPipedInput()).toBe("piped prompt\n");
|
||||
expect(readText).toHaveBeenCalledTimes(1);
|
||||
} finally {
|
||||
Object.defineProperty(process.stdin, "isTTY", { value: originalIsTTY, configurable: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("applyResolvedSystemPromptInputs", () => {
|
||||
it("routes SYSTEM.md content through template-aware session options", () => {
|
||||
const options: CreateAgentSessionOptions = {};
|
||||
|
||||
Reference in New Issue
Block a user