Skip to content

[Code Quality] Standardize output routing: Replace raw fmt.Print* with explicit stderr/stdout #13878

@github-actions

Description

@github-actions

Description

The codebase has 46 instances of raw fmt.Println(), fmt.Printf() calls without explicit stderr/stdout routing, violating Unix conventions and making output behavior unpredictable in pipes and redirects.

Problem

Current Anti-Pattern:

// ❌ Raw println - goes to stdout by default
fmt.Println(console.FormatWarningMessage("Warning message"))

Correct Pattern:

// ✅ Explicit stderr routing for diagnostic output
fmt.Fprintln(os.Stderr, console.FormatWarningMessage("Warning message"))

// ✅ Explicit stdout routing for structured data
fmt.Println(string(jsonBytes))  // JSON output

Impact:

  • Diagnostic messages pollute stdout, breaking pipes and output redirection
  • Inconsistent behavior across CLI commands
  • Violates Unix principle: data to stdout, diagnostics to stderr

Files Affected

From Terminal Stylist Analysis (#12488):

  • pkg/workflow/stop_after.go - 10 instances
  • pkg/workflow/mcp_config_custom.go - 1 instance
  • pkg/workflow/engine_firewall_support.go - 3 instances
  • pkg/campaign/command.go - 3 instances (JSON output)
  • pkg/cli/tool_graph.go - 1 instance (mermaid graph)
  • Additional ~28 instances across other files

Suggested Changes

Step 1: Replace Diagnostic Output

Replace all diagnostic/status/warning/error messages with stderr:

// Before
fmt.Println(message)

// After
fmt.Fprintln(os.Stderr, message)

Step 2: Preserve Structured Output

Keep stdout ONLY for structured data (JSON, graphs, hashes):

// JSON output - keep stdout
fmt.Println(string(jsonBytes))

// Mermaid graphs - keep stdout
fmt.Println(mermaidGraph)

// Hashes - keep stdout
fmt.Println(hash)

Step 3: Add Linter Rule

Add golangci-lint rule to prevent future raw fmt.Print* calls:

# .golangci.yml
linters-settings:
  forbidigo:
    forbid:
      - 'fmt\.Println.*# use fmt.Fprintln(os.Stderr, ...) for diagnostics'
      - 'fmt\.Printf.*# use fmt.Fprintf(os.Stderr, ...) for diagnostics'
    exclude-godoc-examples: true

Step 4: Document Convention

Update AGENTS.md with output routing guidelines:

**Output Routing Rules (Unix Conventions):**
- **Diagnostic output** (messages, warnings, errors) → `stderr`
- **Structured data** (JSON, hashes, graphs) → `stdout`
- **Rationale**: Allows users to pipe/redirect data without diagnostic noise

Success Criteria

  • All 46 instances of raw fmt.Print* reviewed and fixed
  • Diagnostic messages routed to stderr
  • JSON/graph/hash output preserved on stdout
  • Linter rule added to prevent regression
  • Documentation updated in AGENTS.md
  • All existing tests pass
  • Output redirection tested manually (e.g., gh aw list | jq)

Testing

# Test that JSON output goes to stdout only
gh aw list --json > output.json 2> diagnostics.txt
# output.json should have JSON, diagnostics.txt should have status messages

# Test that errors go to stderr
gh aw compile invalid.md 2> errors.txt
# errors.txt should contain error messages

Source

Extracted from Terminal Stylist Analysis discussion #12488

Discussion finding: "46 instances of raw fmt.Print* calls without explicit stderr/stdout routing. This violates Unix conventions and makes output unpredictable in pipes."

Priority

Medium - Code consistency issue affecting CLI user experience and Unix convention compliance.

AI generated by Discussion Task Miner - Code Quality Improvement Agent

  • expires on Feb 6, 2026, 9:14 AM UTC

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions