diff --git a/src/officecli/CommandBuilder.Batch.cs b/src/officecli/CommandBuilder.Batch.cs index a1bdc134d..3f0571390 100644 --- a/src/officecli/CommandBuilder.Batch.cs +++ b/src/officecli/CommandBuilder.Batch.cs @@ -172,6 +172,13 @@ private static Command BuildBatchCommand(Option jsonOption) var bestEffort = result.GetValue(batchBestEffortOpt); string jsonText; + if (inlineCommands != null && inputFile != null) + throw new ArgumentException( + "batch: --commands and --input are mutually exclusive. Pick one source."); + // '--input -' explicitly opts INTO stdin — don't emit the + // "stdin will be ignored" warning in that case, since stdin + // is exactly what will be read. + var inputIsStdinAlias = inputFile != null && inputFile.Name == "-"; // BUG-R7-09 (F-6): previously --commands/--input/stdin were // silently prioritized in that order — passing two of them at // once dropped the lower-priority source with no warning, so @@ -179,6 +186,15 @@ private static Command BuildBatchCommand(Option jsonOption) // command that already had --commands set. Reject the // combination loudly. (Detect stdin via Console.IsInputRedirected // to avoid spurious failures from interactive terminals.) + // + // Only probe stdin when all of these are true: an explicit + // non-stdin source will make us ignore stdin, the warning has not + // been disabled, and stdin is redirected. This is a correctness + // boundary for MCP: its stdin is the JSON-RPC transport. Starting + // a background Peek there and abandoning it after the timeout + // leaves a live reader that consumes the next JSON-RPC request. + // OFFICECLI_BATCH_ALLOW_STDIN_REDIRECT is enabled by McpServer, so + // MCP calls with --commands/--input must never touch stdin here. // IsInputRedirected alone is true for every non-interactive // invocation (cron, CI, `< /dev/null`, systemd), so the warning // below fired on effectively all scripted batch runs with only @@ -186,7 +202,11 @@ private static Command BuildBatchCommand(Option jsonOption) // /dev/null redirect) with zero length carries no second payload // — skip the warning. Pipes (CanSeek=false) still warn: someone // is actively piping data that will be ignored. - bool stdinHasInput = Console.IsInputRedirected; + var hasExplicitNonStdinSource = inlineCommands != null + || (inputFile != null && !inputIsStdinAlias); + var shouldWarnAboutIgnoredStdin = hasExplicitNonStdinSource + && Environment.GetEnvironmentVariable("OFFICECLI_BATCH_ALLOW_STDIN_REDIRECT") == null; + bool stdinHasInput = shouldWarnAboutIgnoredStdin && Console.IsInputRedirected; if (stdinHasInput) { // Peek with a short timeout: /dev/null and closed stdin hit @@ -207,15 +227,7 @@ private static Command BuildBatchCommand(Option jsonOption) } catch { /* keep IsInputRedirected verdict */ } } - if (inlineCommands != null && inputFile != null) - throw new ArgumentException( - "batch: --commands and --input are mutually exclusive. Pick one source."); - // '--input -' explicitly opts INTO stdin — don't emit the - // "stdin will be ignored" warning in that case, since stdin - // is exactly what will be read. - var inputIsStdinAlias = inputFile != null && inputFile.Name == "-"; - if ((inlineCommands != null || (inputFile != null && !inputIsStdinAlias)) && stdinHasInput - && Environment.GetEnvironmentVariable("OFFICECLI_BATCH_ALLOW_STDIN_REDIRECT") == null) + if (stdinHasInput) { Console.Error.WriteLine( "Warning: batch is reading from --commands/--input but stdin is also redirected; "