Read-only commands (pr checks/files/commits/diff, secret/variable list) have no --json output #109

Closed
opened 2026-06-11 00:01:28 +00:00 by stephen · 2 comments
Owner

Observation

Several read-only commands that an automation or agent would naturally script against have no --json output at all. They emit only a human table or free text, with no structured escape hatch, even though sibling commands like pr view and issue view do support --json.

Reproduced (all reject the flag):

$ fj pr checks 1 --json -R rasterstate/fj
error: unexpected argument '--json' found
$ fj pr files 1 --json -R rasterstate/fj
error: unexpected argument '--json' found
$ fj pr commits 1 --json -R rasterstate/fj
error: unexpected argument '--json' found
$ fj pr diff 1 --json -R rasterstate/fj
error: unexpected argument '--json' found
$ fj secret list --json -R rasterstate/fj
error: unexpected argument '--json' found
$ fj variable list --json -R rasterstate/fj
error: unexpected argument '--json' found

In each handler the data is already structured in memory and then flattened into a table or text, with no JSON branch:

  • pr checks (src/cli/pr_inspect.rs:73-119) fetches combined_status (state, total_count, per-check statuses[]) and prints a table. This is the canonical CI-gating query.
  • pr files (src/cli/pr_inspect.rs:47-71) has status, filename, additions, deletions per file and prints a table.
  • pr commits (src/cli/pr_inspect.rs:19-45) has sha/subject/author and prints a table.
  • pr diff (src/cli/pr_inspect.rs:11-17) prints raw unified-diff text.
  • secret list (src/cli/workflow_secret.rs:30-34, SecretListArgs has no json field) and variable list (src/cli/workflow_variable.rs) print plain tables.

Because the global --json-fields projection only takes effect on commands that call print_json (src/output/mod.rs), advertising it as a global flag on these commands is meaningless: there is no JSON for it to project.

Why it matters

These are not obscure corners; they are exactly the surfaces automation reaches for:

  • A merge gate or agent asking "is this PR green?" wants fj pr checks --json | jq '.state', not to scrape a colored table and re-derive state from pills.
  • A file-scoped policy ("did this PR touch migrations/?") wants fj pr files --json.
  • Actions tooling auditing configuration wants fj secret list --json / fj variable list --json to diff against a desired set.

gh returns JSON for all of these (gh pr checks --json, gh pr view --json files,commits, gh secret list machine output). The inconsistency is also internal: pr view and issue view already accept --json (src/cli/pr.rs:99-101), so a scripter reasonably assumes the rest of pr does too, builds it into a wrapper, and hits error: unexpected argument '--json' at runtime. For a tool whose headline is JSON for scripts and agents, the checks/files/secrets gaps are the ones most likely to be scripted first.

Possible directions (sketches)

  • (sketch) Add a --json flag to each inspect command (pr diff, pr commits, pr files, pr checks) and route through output::print_json over the already-fetched structs, exactly as pr view does. The API responses are already typed; this is mostly surfacing them.
  • (sketch) Add --json to secret list / variable list (src/cli/workflow_secret.rs:30, src/cli/workflow_variable.rs); emit [{name, updated_at, ...}] so config can be diffed.
  • (sketch) For pr diff, if a structured form is undesirable, at least support --json returning { "diff": "<text>" } or document that diff is intentionally text-only, so the boundary is explicit rather than a flag rejection.
  • (sketch) Add a test asserting every view/inspect/list subcommand either accepts --json or is explicitly allow-listed as text-only, so coverage cannot silently regress.

Confidence

High. All six rejections reproduced live, and each handler verified to build only a table/text with no JSON path (src/cli/pr_inspect.rs:11-119, src/cli/workflow_secret.rs:30-34, src/cli/workflow_variable.rs). The contrast that pr view/issue view do support --json is verified in source. Which commands deserve JSON first is a prioritization call; that the gaps exist is not.

## Observation Several read-only commands that an automation or agent would naturally script against have no `--json` output at all. They emit only a human table or free text, with no structured escape hatch, even though sibling commands like `pr view` and `issue view` do support `--json`. Reproduced (all reject the flag): ``` $ fj pr checks 1 --json -R rasterstate/fj error: unexpected argument '--json' found $ fj pr files 1 --json -R rasterstate/fj error: unexpected argument '--json' found $ fj pr commits 1 --json -R rasterstate/fj error: unexpected argument '--json' found $ fj pr diff 1 --json -R rasterstate/fj error: unexpected argument '--json' found $ fj secret list --json -R rasterstate/fj error: unexpected argument '--json' found $ fj variable list --json -R rasterstate/fj error: unexpected argument '--json' found ``` In each handler the data is already structured in memory and then flattened into a table or text, with no JSON branch: - `pr checks` (`src/cli/pr_inspect.rs:73-119`) fetches `combined_status` (state, total_count, per-check `statuses[]`) and prints a table. This is the canonical CI-gating query. - `pr files` (`src/cli/pr_inspect.rs:47-71`) has `status`, `filename`, `additions`, `deletions` per file and prints a table. - `pr commits` (`src/cli/pr_inspect.rs:19-45`) has sha/subject/author and prints a table. - `pr diff` (`src/cli/pr_inspect.rs:11-17`) prints raw unified-diff text. - `secret list` (`src/cli/workflow_secret.rs:30-34`, `SecretListArgs` has no `json` field) and `variable list` (`src/cli/workflow_variable.rs`) print plain tables. Because the global `--json-fields` projection only takes effect on commands that call `print_json` (`src/output/mod.rs`), advertising it as a global flag on these commands is meaningless: there is no JSON for it to project. ## Why it matters These are not obscure corners; they are exactly the surfaces automation reaches for: - A merge gate or agent asking "is this PR green?" wants `fj pr checks --json | jq '.state'`, not to scrape a colored table and re-derive state from pills. - A file-scoped policy ("did this PR touch `migrations/`?") wants `fj pr files --json`. - Actions tooling auditing configuration wants `fj secret list --json` / `fj variable list --json` to diff against a desired set. `gh` returns JSON for all of these (`gh pr checks --json`, `gh pr view --json files,commits`, `gh secret list` machine output). The inconsistency is also internal: `pr view` and `issue view` already accept `--json` (`src/cli/pr.rs:99-101`), so a scripter reasonably assumes the rest of `pr` does too, builds it into a wrapper, and hits `error: unexpected argument '--json'` at runtime. For a tool whose headline is JSON for scripts and agents, the checks/files/secrets gaps are the ones most likely to be scripted first. ## Possible directions (sketches) - *(sketch)* Add a `--json` flag to each inspect command (`pr diff`, `pr commits`, `pr files`, `pr checks`) and route through `output::print_json` over the already-fetched structs, exactly as `pr view` does. The API responses are already typed; this is mostly surfacing them. - *(sketch)* Add `--json` to `secret list` / `variable list` (`src/cli/workflow_secret.rs:30`, `src/cli/workflow_variable.rs`); emit `[{name, updated_at, ...}]` so config can be diffed. - *(sketch)* For `pr diff`, if a structured form is undesirable, at least support `--json` returning `{ "diff": "<text>" }` or document that diff is intentionally text-only, so the boundary is explicit rather than a flag rejection. - *(sketch)* Add a test asserting every `view`/inspect/`list` subcommand either accepts `--json` or is explicitly allow-listed as text-only, so coverage cannot silently regress. ## Confidence High. All six rejections reproduced live, and each handler verified to build only a table/text with no JSON path (`src/cli/pr_inspect.rs:11-119`, `src/cli/workflow_secret.rs:30-34`, `src/cli/workflow_variable.rs`). The contrast that `pr view`/`issue view` do support `--json` is verified in source. Which commands deserve JSON first is a prioritization call; that the gaps exist is not.
Author
Owner

Converted to backlog item rasterstate/fj#112 (p1, size M).

Tracked there with task / priority / reason / acceptance / dependencies. Keeping this open with the converted label as the originating opportunity.

Converted to backlog item `rasterstate/fj#112` (p1, size M). Tracked there with task / priority / reason / acceptance / dependencies. Keeping this open with the `converted` label as the originating opportunity.
Author
Owner

Derived backlog item rasterstate/fj#112 is merged. Closing this opportunity per the issue state machine.

Derived backlog item rasterstate/fj#112 is merged. Closing this opportunity per the issue state machine.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
rasterstate/fj#109
No description provided.