From 03a5e5346c08c2db3b422383384243ae736eff06 Mon Sep 17 00:00:00 2001 From: Daniel Cazzulino Date: Wed, 22 Jul 2026 00:31:07 -0300 Subject: [PATCH] Fall back to help when MRU picker cannot run interactively Zero-arg go with history still needs a real terminal for the Spectre picker. Redirected stdin or non-interactive consoles now take the same path as empty history and show help, instead of logging an error and exiting 1. Document the behavior in the readme and help text. --- readme.md | 9 +++++---- src/go/Program.cs | 31 +++++++++++++++++++++++++------ src/go/help.md | 2 +- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/readme.md b/readme.md index 7273798..08d2f67 100644 --- a/readme.md +++ b/readme.md @@ -45,10 +45,11 @@ with smart up-to-date checks of every C# file used to build the app (including Every successful `go` / `go dev` invocation records the entry point (local full path or remote ref) in a shared history file next to the cache root (`dotnet/go/go.toml`). -With at least one history entry, running with **no arguments** opens an interactive -picker (searchable, ordered by use count then recency). After you pick an entry you -can optionally type app arguments (quoted groups supported). With an empty history, -`dnx go` with no args shows help instead. +With at least one history entry, running with **no arguments** in an interactive +terminal opens a searchable picker (ordered by use count then recency). After you +pick an entry you can optionally type app arguments (quoted groups supported). +With an empty history, or when stdin is redirected / non-interactive (CI, pipes), +`dnx go` with no args shows help instead of erroring. Local paths that no longer exist are dropped from the picker; remote refs stay listed even when their download bundle is gone (the next run re-downloads as usual). diff --git a/src/go/Program.cs b/src/go/Program.cs index 1579c96..ea2a555 100644 --- a/src/go/Program.cs +++ b/src/go/Program.cs @@ -16,8 +16,9 @@ app.Add(); var prepared = GoArgs.PrepareArgs(args); -// Zero-arg with no prior runs: show help (same as -h/--help/-?). With history, default command opens the MRU picker. -if (prepared.Length == 0 && RunHistory.List().Count == 0) +// Zero-arg: help when history is empty or the console cannot prompt (redirected stdin / non-interactive). +// With history + an interactive terminal, the default command opens the MRU picker. +if (prepared.Length == 0 && (RunHistory.List().Count == 0 || !CanPromptInteractively())) prepared = ["--help"]; await app.RunAsync(prepared); @@ -235,15 +236,34 @@ static int CleanArtifacts(string? input, bool all, string? missingInputMessage) return AppCleaner.Clean(pdir, stmp, cs); } +/// +/// True when stdin is a real terminal that can drive Spectre prompts. +/// Redirected/piped input falls through to help instead of the MRU picker. +/// +static bool CanPromptInteractively() +{ + try + { + if (Console.IsInputRedirected) + return false; + + return AnsiConsole.Profile.Capabilities.Interactive; + } + catch + { + return false; + } +} + /// /// Interactive MRU picker over go.toml history, then optional app-args prompt. -/// Returns null on empty history, cancel, or non-interactive console. +/// Returns null on empty history, cancel, or if prompts cannot run (caller shows help for zero-arg). /// static string? TryPickFromHistory(out string[] appArgs) { appArgs = []; var history = RunHistory.List(); - if (history.Count == 0) + if (history.Count == 0 || !CanPromptInteractively()) return null; try @@ -267,8 +287,7 @@ static int CleanArtifacts(string? input, bool all, string? missingInputMessage) } catch (Exception) { - // Spectre throws when stdin is redirected / no interactive terminal. - ConsoleApp.LogError("Interactive selection requires a terminal."); + // Cancelled prompt or unexpected non-interactive environment — no error noise. return null; } } diff --git a/src/go/help.md b/src/go/help.md index 2b6ed7d..2bf3141 100644 --- a/src/go/help.md +++ b/src/go/help.md @@ -4,7 +4,7 @@ Usage: [command] [arguments...] [options...] [-h|--help] [--version] Runs a file-based .NET app from a .cs entrypoint. Arguments: - [0] Path to an existing .cs file or remote ref (owner/repo[@ref][:path]). When omitted, selects from previous runs (MRU) then prompts for optional app args. + [0] Path to an existing .cs file or remote ref (owner/repo[@ref][:path]). When omitted in an interactive terminal, selects from previous runs (MRU) then prompts for optional app args; otherwise shows help. [1] Arguments to pass to the app. Options: