From d0150272f7f80f35835b0e610b2ea615ae9c0a8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BF=AE=E9=9B=A8?= <47820304+PeterGuy326@users.noreply.github.com> Date: Mon, 15 Jun 2026 10:17:57 +0800 Subject: [PATCH] fix(cli): return structured error instead of hanging on stdin for destructive ops in non-interactive sessions --- internal/compat/registry.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/internal/compat/registry.go b/internal/compat/registry.go index eb01f959..d3bbe69b 100644 --- a/internal/compat/registry.go +++ b/internal/compat/registry.go @@ -298,6 +298,16 @@ func NewDirectCommand(route Route, runner executor.Runner) *cobra.Command { } } if blocked, _ := params["_blocked"].(bool); blocked { + // Non-interactive stdin (pipe / CI / test harness / AI agent): + // we cannot read a confirmation. Previously the prompt below hit + // EOF immediately, printed "Operation cancelled" to stderr, left + // stdout empty and returned nil with exit 0 — silently no-op'ing + // the destructive command while reporting success, and breaking + // any -f json caller that parses the empty stdout as a failure. + // Refuse with a structured error telling the caller to pass --yes. + if fi, err := os.Stdin.Stat(); err != nil || (fi.Mode()&os.ModeCharDevice) == 0 { + return apperrors.NewValidation("this is a destructive operation; re-run with --yes to confirm (cannot prompt for confirmation in a non-interactive session)") + } // Interactive confirmation for destructive operations (consistent with Helper commands) fmt.Fprintln(cmd.ErrOrStderr(), "⚠️ This is a destructive operation.") fmt.Fprint(cmd.ErrOrStderr(), "Confirm? (yes/no): ")