Skip to content

Explicit --json / --quiet / --no-color flags across mship commands for CI and agent determinism #103

@atomikpanda

Description

@atomikpanda

Problem

Many mship commands already detect TTY and switch output shape: table-ish text for humans, JSON for pipes. This works for interactive use. It is unreliable for:

  • CI: mship invoked from a GitHub Actions step where stdout is a TTY in some runner configs and not in others → inconsistent output shape across runs.
  • Agent loops: a Claude / Codex / Aider subprocess that allocates a pty to capture streaming output gets the TTY-branch output when it wanted JSON, or vice-versa.
  • Log capture: users piping through tee want the JSON shape but also want to watch progress — TTY detection forces a choice.
  • Color bleed: ANSI escape codes leak into log collectors, PR comment bodies, and clipboard paste when TTY detection gets it wrong.

Implicit-behavior-based-on-ambient-environment is the classic source of "works on my machine / fails in CI" friction, and it's exactly the kind of non-determinism a substrate should not force on its callers.

Proposal

Add three flags, consistently available across every output-producing mship subcommand:

  • --json — force JSON output regardless of TTY state. Implies --no-color. For commands that today emit human text with no JSON path, this means defining and documenting one.
  • --quiet / -q — suppress non-essential stderr (progress lines, advisory warnings, informational status). Errors and exit codes unchanged.
  • --no-color — strip ANSI color codes from all output streams. Equivalent to (and overridden by) NO_COLOR=1 env var per https://no-color.org/.

Ordering / precedence:

  • CLI flag wins over env var wins over TTY auto-detection.
  • MSHIP_JSON=1, MSHIP_QUIET=1, NO_COLOR=1 env vars for shell-profile defaults.
  • --json and --no-color are orthogonal (--json already implies --no-color, but --no-color alone doesn't force JSON).

Commands in scope (non-exhaustive — anywhere output is produced):

  • mship status, mship list, mship pr, mship context, mship dispatch
  • mship spawn, mship finish, mship close, mship commit, mship journal, mship bind refresh
  • mship debug hypothesis/rule-out/resolved, mship reconcile
  • mship test (json exit payload + human progress are already split; this just makes it explicit)

Why this is polish, not core leverage — but still worth doing

The substrate thesis is that mship owns the hand-off boundary. Hand-offs into CI and into agent subprocesses are two of the three main boundaries (the third being human). Making the output shape deterministic at those boundaries is a correctness property, not a UX nicety — a flaky output format turns every "if stdout contains X then Y" downstream rule into a source of CI drift.

It's small because the machinery already exists; this issue is a consistency pass to make sure every command exposes the same flag surface and respects the same precedence rules.

Scope cuts

  • No new output shapes. --json emits whatever JSON the command already emits in TTY-off mode. If a command has no JSON path today, define a minimal one (slug + result + error, basically), don't design a rich schema.
  • No per-field selectors (--json --fields=slug,phase). Downstream tooling uses jq; this is not a reinvention of jq.
  • No color themes. --no-color is boolean off, not a theme system.
  • No structured stderr. --quiet suppresses; it doesn't add a stderr JSON channel. If you want structured diagnostics, that's a separate issue.

Acceptance

  • Every mship subcommand accepts --json, --quiet, --no-color (or documents that the flag is a no-op for that command, e.g. --json on a command with no output).
  • Precedence is documented in mship --help and in the skill doc.
  • Integration test: mship <cmd> --json run under a TTY (via pty) produces the same bytes as run without a TTY.
  • CI smoke: one Actions job runs a representative command and asserts on exact output bytes with the flags set.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions