Skip to content

feat(output): honour -o json/yaml/csv across every command - #52

Open
timimsms wants to merge 1 commit into
mainfrom
feat/consistent-json-output
Open

timimsms wants to merge 1 commit into
mainfrom
feat/consistent-json-output

Conversation

@timimsms

Copy link
Copy Markdown
Owner

Summary

Partially addresses #18 — the consistency half. I audited every command empirically (running each with -o json) rather than by reading the source, which is how the gaps showed up:

Command Before
cu version plain text only
cu auth status plain text only
cu config list key=value only
cu config get raw value only
cu bulk update/close/delete progress lines on stdout, no structured result
cu field set/clear confirmation line only — a gap I left in #44

All of these now honour -o json|yaml|csv.

$ cu auth status -o json
{ "authenticated": true, "workspace": "default", "token_source": "keyring" }

$ cu bulk close bogus1 bogus2 --yes -o json
{
  "operation": "close",
  "succeeded": 0,
  "failed": 2,
  "results": [
    { "task_id": "bogus1", "ok": false, "error": "GET …/task/bogus1: 401 Team not authorized OAUTH_027" },
    …
  ]
}
$ echo $?
1

Decisions worth reviewing

  • auth status reports being unauthenticated as data ("authenticated": false) while still exiting non-zero. For a scripted caller that is a state to branch on, not a failure to parse out of stderr.
  • Progress lines are suppressed outside table output. bulk printed ✓ taskid to stdout as it went; leaving that in would interleave with the JSON document and make it unparseable. The information is preserved as results[], with failure reasons per task.
  • Redaction is applied before rendering, not per format, so -o json cannot route around the credential redaction added in fix(config): refuse plaintext credentials and redact them in config list #46/fix(config): --config help advertised a filename the code never writes #47. Verified.
  • config get emits a bare value ("json", not {"output":"json"}) — it asks for one setting, so the answer should be directly usable.
  • Table output is byte-identical, verified by diffing every changed command against v0.2.1.

Breaking change

cu bulk delete -o json previously emitted a bare array of deleted task ids. It now returns the same summary object as the other bulk commands, for consistency — which is the point of the issue, but it will break anyone parsing that array. The ids are still available as results[] entries where ok is true.

Not included

Two items from the issue remain open, so this does not close it:

  • Structured errors — errors still go to stderr as prose. Doing this properly means an error envelope and a review of every os.Exit(1) path, which is its own change.
  • --jq filtering, and the top-level --json alias.

Tests

TestBulkSummaryRecord covers the accounting and that failure reasons survive into structured output. version's existing test asserted on cmd.Run; the command now uses RunE so a formatting failure exits non-zero instead of being swallowed, and the test checks runnability rather than which field holds the closure.

Checklist

  • ./scripts/ci.sh passes locally — except errcheck, which reports the same pre-existing findings on main, none in files this PR touches
  • Commit messages use conventional prefixes
  • CLI docs regenerated — no help text changed, so no doc diff
  • Docs updated if user-facing behavior changed — README gains a machine-readable output section

Audited each command empirically rather than by reading: version, auth status,
config list and config get ignored the output flag entirely, and bulk
update/close/delete printed progress to stdout with no structured result. Those
are exactly the commands a script or agent needs most.

- version gains structured build details.
- auth status reports the unauthenticated case as data (authenticated: false)
  while still exiting non-zero — it is a state, not a crash.
- config list and get emit structured values, with the credential redaction from
  #46/#47 applied before rendering so no format can route around it. config get
  emits a bare value, since it asks for one setting.
- field set/clear report which field was resolved, closing a gap I left in #44.
- bulk operations return per-task results with failure reasons. Progress lines
  are suppressed outside table output, where they would interleave with the
  structured document on stdout and make it unparseable.

Table output is byte-identical; verified by diffing against v0.2.1.

Note a breaking change for anyone parsing `bulk delete -o json`: it emitted a
bare array of deleted ids and now returns the same summary object as the other
bulk commands. The deleted ids are still there as results[].task_id where ok is
true.

version switches Run to RunE so a formatting failure exits non-zero rather than
being swallowed; its test asserted on Run specifically and now checks the
command is runnable.

Addresses #18. Structured *errors* and a --jq filter remain open.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014aqbmccWm1tqttmBUCR5rv
ClickUp: 86dxbeqyt

This branch has not been deployed

No deployments
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.

1 participant