P1: Non-Interactive by Default
Definition
Every automation path MUST run without human input. A CLI tool that blocks on an interactive prompt is invisible to an agent — the agent hangs, the user sees nothing, and the operation times out silently.
Why Agents Need It
An agent calling a CLI cannot type. When the tool prompts for a confirmation or a credential, the agent's process stalls until timeout: no tokens recovered, no structured signal that interaction was requested, and no way to distinguish "waiting for input" from "still processing." Interactive prompts in automation paths are the single most common cause of agent-tool deadlock.
Requirements
MUST:
-
Every flag settable via environment variable. Use a falsey-value parser for booleans so that
TOOL_QUIET=0andTOOL_QUIET=falsecorrectly disable the flag rather than being treated as truthy non-empty strings. In Rust / clap:#[arg(long, env = "TOOL_QUIET", global = true, value_parser = FalseyValueParser::new())] quiet: bool, -
A
--no-interactiveflag gating every prompt library call (dialoguer,inquire,read_line,TTY::Prompt,inquirer, equivalents in other frameworks). When the flag is set, or when stdin is not a TTY, the tool uses defaults, reads from stdin, or exits with an actionable error. It never blocks. -
A headless authentication path if the CLI authenticates. The canonical flag is
--no-browser, which triggers the OAuth 2.0 Device Authorization Grant (RFC 8628): the CLI prints a URL and a code; the user authorizes on another device. Agents cannot open browsers. Non-canonical alternatives (--device-code,--remote,--headless) are acceptable but should migrate toward--no-browser.
SHOULD:
- Auto-detect non-interactive context via TTY detection (
std::io::IsTerminalin Rust 1.70+,process.stdin.isTTYin Node,sys.stdout.isatty()in Python) and suppress prompts when stderr is not a terminal, even without an explicit--no-interactiveflag. - Document default values for prompted inputs in
--helpoutput so agents can pass them explicitly instead of accepting whatever default ships.
MAY:
- Offer rich interactive experiences — spinners, progress bars, multi-select menus — when a TTY is detected and
--no-interactiveis not set, provided the non-interactive path remains fully functional.
Evidence
--no-interactiveflag in the CLI struct with an env-var binding.- Boolean env vars parsed with a falsey-value parser (not the default string parser).
- TTY guard wrapping every
dialoguer,inquire, or equivalent prompt call. --no-browserflag present on authenticated CLIs.env = "TOOL_..."attribute on every flag that takes user input.
Anti-Patterns
- Bare
dialoguer::Confirm::new().interact()with no TTY check and no--no-interactiveoverride — agents hang indefinitely. - Boolean environment variables parsed as plain strings, so
TOOL_QUIET=falseis truthy because the string is non-empty. stdin().read_line()in a code path reached during normal operation without a TTY check first.- Hard-coded credentials prompts with no env-var or config-file alternative.
- OAuth flow that unconditionally opens a browser with no headless escape hatch.
Measured by check IDs p1-non-interactive (behavioral) and p1-non-interactive-source (source). Run agentnative check --principle 1 . against your CLI to see both.