Overview

AI coding agents (Claude Code, and any harness that sets AI_AGENT or CI) run tfui commands as if they were terraform — and get output designed for a context window instead of a scrollback buffer.

agent runs:  tfui plan
agent sees:  + local_file.f00
             + local_file.f01
             ...
             Plan: 50 to add, 0 to change, 0 to destroy.
             Risk: low
exit code:   2 (changes present)

No flags. No configuration. No MCP server. The agent’s environment is detected automatically, the TUI never launches, and every command follows one contract: data on stdout, status on stderr, meaningful exit codes, and a failed run never claims success.

Why this matters for agents

An agent reading raw terraform plan output pays for every line of it — attribute diffs, (known after apply) placeholders, provider noise — in context-window space. Worse, an interactive tool that detects a PTY will launch a full-screen UI the agent cannot drive: the process blocks and the agent sees ANSI escape soup.

tfui addresses both:

  • Concise by construction. One line per change, a plan summary, and a risk classification — the same tree the TUI renders, minus the TUI.
  • Headless by detection. Agent environments are recognized before any rendering decision is made, so tfui plan inside an agent session can never hang on an alt-screen.

Benchmark: context-window footprint

Measured on a 50-resource plan (all creates, local_file resources, Terraform v1.14.9, identical configuration for both commands):

Command Lines Bytes
terraform plan -no-color 763 34,426
tfui plan (headless) 53 905

38× fewer bytes, 14× fewer lines — and the agent still gets every change address, the add/change/destroy summary, and a risk verdict. Output scales with one line per resource, not with attribute count, so the gap widens as plans grow.

Success runs emit zero bytes on stderr. Nothing in the output needs post-processing: no ANSI escapes, no spinners, no progress noise.

Need full detail for a specific decision? The agent can ask for it explicitly with -json:

tfui plan -json     # machine-readable plan JSON on stdout
tfui validate -json # structured diagnostics

How detection works

Headless mode resolves before anything renders:

if -ci flag or CI=1:               → headless
if CLAUDECODE or AI_AGENT set:     → headless (agent environment)
if stderr is not a TTY:            → headless
otherwise:                         → interactive TUI

Claude Code sets CLAUDECODE and AI_AGENT in every shell it spawns, so both bare tfui <command> invocations and PTY-attached ones resolve to headless automatically. Other agents are covered by whichever signal they provide: AI_AGENT, CI=1, or a non-TTY stderr. The full rationale is in ADR-0022.

There is no --agent flag — the explicit spelling already exists (-ci for mode, -json for format), and detection makes it unnecessary.

The failure contract

The most expensive output an agent can receive is a lie. tfui’s headless contract is: terraform’s error on stderr, empty stdout, exit 1 — always.

$ tfui plan        # inside an agent session, broken configuration
$ echo $?
1

stderr (terraform’s own diagnostics, verbatim):

running terraform plan: exit status 1

Error: Reference to undeclared local value

  on main.tf line 15, in resource "local_file" "broken":
  15:   content  = local.nonexistent

A local value with the name "nonexistent" has not been declared.

tfui validate reports diagnostics as data (stdout) and exits 1 when the configuration is invalid:

$ tfui validate
✗ Reference to undeclared local value (main.tf:15)
  A local value with the name "nonexistent" has not been declared.
$ echo $?
1

Exit codes the agent can branch on

Code Meaning
0 Success — or no changes, for plan
1 Error / invalid configuration
2 Plan has changes (terraform -detailed-exitcode convention)

An agent can run tfui plan, branch on the exit code alone, and only read output when it needs the detail.

Every verb, one contract

All commands follow the same rules headlessly — including the state-mutating ones, which skip their interactive confirmation exactly as terraform taint does:

$ tfui taint local_file.alpha
✓ Tainted local_file.alpha        # stderr; exit 0
$ tfui apply -auto-approve
Apply complete. Resources: 1 added, 0 changed, 0 destroyed.   # stderr; exit 0
Command stdout stderr Exit
tfui plan tree view or JSON 0/2
tfui validate diagnostics 0/1
tfui state one address per line 0
tfui output key=value or JSON 0
tfui apply -auto-approve outcome summary 0/1
tfui taint / untaint / import ✓ confirmation 0/1
any command, terraform fails terraform’s error 1

The complete specification is the CLI I/O Contract.

Teach your agent to use it

Add one block to your project’s CLAUDE.md (or AGENTS.md):

## Terraform

Use `tfui` instead of raw `terraform` for read/review operations:

- `tfui plan` — one line per change + summary + risk level (exit 2 = changes present)
- `tfui validate` — compact diagnostics (exit 1 = invalid)
- `tfui state` — resource addresses, one per line
- `tfui output` — key=value pairs

Failures put terraform's error on stderr and exit 1. Add `-json` for
machine-readable output.

The same binary serves the humans on the team as a full interactive TUI — one tool, reviewed by people, driven by agents.

See also


This site uses Just the Docs, a documentation theme for Jekyll.