Skip to content

feat(REL-12752): adding TTY detection for auto-JSON output#660

Open
nieblara wants to merge 5 commits intomainfrom
REL-12752-tty-1
Open

feat(REL-12752): adding TTY detection for auto-JSON output#660
nieblara wants to merge 5 commits intomainfrom
REL-12752-tty-1

Conversation

@nieblara
Copy link
Contributor

@nieblara nieblara commented Mar 18, 2026

When stdout is not a terminal, the default output format switches from plaintext to json. Explicit --output or --json flags still override. Follows the pattern used by GitHub CLI (gh) and GitLab CLI (glab). Adds a FORCE_TTY environment variable escape hatch.

Requirements

  • I have added test coverage for new or changed functionality
  • I have followed the repository's pull request submission guidelines
  • I have validated my changes against all supported platform versions

Note

Medium Risk
Changes default output behavior based on TTY detection, which can break existing scripts that relied on plaintext when piping/redirecting; logic is well-covered by new precedence and non-TTY tests but affects all commands globally.

Overview
Default output is now TTY-aware. When stdout is not a TTY, ldcli now defaults --output to json instead of plaintext, with an escape hatch via FORCE_TTY/LD_FORCE_TTY to keep the plaintext default.

Precedence and messaging are clarified and tested. Help text/docs were updated to describe resolution order (--json > --output > LD_OUTPUT/config > TTY default), integration tests were expanded to cover non-TTY defaults and overrides, and error-output tests were adjusted to assert plaintext errors are formatted while JSON errors are wrapped consistently.

Written by Cursor Bugbot for commit 82177a3. This will update automatically on new commits. Configure here.


Related Jira issue: REL-12752: TTY detection for auto-JSON output

@launchdarkly-upra launchdarkly-upra bot changed the title (feat) adding TTY detection for auto-JSON output [REL-12752] (feat) adding TTY detection for auto-JSON output Mar 18, 2026
@nieblara nieblara changed the title [REL-12752] (feat) adding TTY detection for auto-JSON output feat(REL-12752): adding TTY detection for auto-JSON output Mar 18, 2026
Copy link

@JackDanger JackDanger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love that this follows the gh / glab convention — great precedent. This is the PR I spent the most time thinking about because it's the one most likely to bite someone.

1. This is a breaking change — should it bump the major version?
ldcli uses semver via release-please (v1→v2 was an OpenAPI spec change). Today, if a user has a script like:

ldcli flags list --project my-proj | grep "my-flag"

…running in CI (no TTY), it gets plaintext and the grep works. After this PR, it silently gets JSON and the grep matches nothing. The script doesn't fail — it just returns no results. That's the worst kind of breakage: silent.

Research shows that existing LD consumers almost universally pass -o json explicitly (sandbox-data-generator, gonfalon dogfood-flags.sh, agent-prompts guide). So the blast radius is likely small. But "likely small" and "zero" are different things, and this is a public CLI distributed via Homebrew and npm. Worth discussing whether this warrants a v3 bump, or at minimum a deprecation warning period where non-TTY plaintext emits a stderr warning before flipping the default.

2. FORCE_TTY is checked via raw os.Getenv while isTerminal is injected
You went to the effort of making isTerminal a parameter for testability — nice. But FORCE_TTY is read directly from os.Getenv inside NewRootCommand, which means tests use os.Setenv/os.Unsetenv. These are process-global mutations that aren't safe with t.Parallel(). Consider adding FORCE_TTY to the injected function or to the envChecker interface from #659.

3. The LD_OUTPUT env var test implies a contract
The test LD_OUTPUT=plaintext overrides non-TTY default documents that LD_OUTPUT controls the output format. Is this intentional new behavior or pre-existing? If new, it should be in the README/docs. If pre-existing, good to have the test — but make sure this doesn't interact unexpectedly with --output flag and FORCE_TTY (three-way precedence: flag > env > TTY detection).

4. The isTerminal == nil → JSON default is a footgun
If any future caller of NewRootCommand forgets to pass isTerminal, they silently get JSON-only output. Consider making this required (no nil check) or at least panicking on nil in debug builds.

5. Consider a stderr notice during the transition
gh shipped this same change and it was smooth because people expect it. But gh had it from early on. ldcli has been plaintext-default for its entire life. A transitional approach: when non-TTY triggers auto-JSON, emit a one-line stderr notice like note: non-interactive session detected, defaulting to JSON output. Use --output plaintext to override. You can remove it after a release cycle.

Base automatically changed from REL-12753-ldcli-agent-flag to main March 19, 2026 20:20
@nieblara nieblara requested review from a team and cspath1 March 20, 2026 02:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants