Skip to content

fix(export): stop shadowing the global -o/--output format flag - #51

Merged
timimsms merged 1 commit into
mainfrom
fix/export-output-flag
Aug 29, 2026
Merged

timimsms merged 1 commit into
mainfrom
fix/export-output-flag

Conversation

@timimsms

Copy link
Copy Markdown
Owner

Summary

export tasks defined its own -o/--output as the destination file, shadowing the global format flag every other command uses. So the flag a user learned everywhere else silently did something different here:

# before
$ cu export tasks -o json
$ ls
json          # a file named "json", containing CSV

The destination file moves to --file/-F, which frees -o.

Freeing -o is only half the fix

Simply not reading -o would trade one silent failure for another — a user who asked for JSON would receive the default CSV with no warning. That is the same class of bug, just quieter. So export honours it as a synonym for --format, since for this command they select the same thing:

# after
$ cu export tasks -o json
[ { "id": "86dz2je4e", ... } ]        # JSON on stdout, no file created

$ cu export tasks -o tasks.csv
--output "tasks.csv" is not an export format (csv, json, markdown)
If you meant a destination file, that is now --file tasks.csv

$ cu export tasks -o yaml
--output "yaml" is not an export format (csv, json, markdown)

$ cu export tasks --format csv -o json
conflicting formats: --format csv and --output json select the same setting; pass only one

Three deliberate calls in there:

Anything export cannot produce is a loud error, not a fallback. table and yaml are valid globally but meaningless for a task export; silently degrading to CSV is what this release is trying to stamp out.

A path-shaped value points at --file. Reaching for the old -o <file> is the predictable mistake this rename creates, so the error says where the flag went instead of just rejecting the value.

Disagreeing --format and --output is an error, not a precedence rule. There is no reading of --format csv -o json that is obviously intended, and picking a winner is precisely how silently-wrong output happens.

Verification

Every case above was run against a built binary, not just unit-tested — including confirming that -o json now creates no file and that --file out.json writes one. Those runs went through a live workspace, so the "26 tasks exported" path is real rather than mocked.

Ten unit tests cover resolveExportFormat (defaults, both spellings, the md alias, case-insensitivity, agreement, conflict, unsupported values, the --file hint). The existing flag test asserted the local output flag existed, encoding the bug — it now asserts the opposite, and I confirmed it fails if the shadow is reintroduced rather than passing vacuously.

Docs regenerated; only cu_export_tasks.md carries substantive changes, and the datestamp-only pages were reverted per the convention from #47. The page now correctly shows -o under Global Flags as the format flag.

Found while verifying — not fixed here

--file rejects absolute paths:

$ cu export tasks --file /tmp/report.csv
Invalid output file path: /tmp/report.csv

export.go refuses any path that filepath.IsAbs or contains .., which also rules out --file ~/exports/tasks.csv. There is no untrusted input here — the user is naming a file on their own machine — so the traversal check is guarding nothing while blocking the most natural usage. It predates this PR, but the rename makes it more prominent, since --file is now the advertised way to write a file.

I left it out because removing the check means handling gosec G304, which deserves its own decision rather than riding along on a flag rename. The repo already has the pattern for it (// #nosec G304 - <justification>, used in cache.go and config.go), so the fix is small. Happy to do it as a follow-up.

Checklist

  • go build, go vet, gofmt, full suite pass
  • Behavior verified end-to-end against a built binary
  • Commit messages use conventional prefixes
  • CLI docs regenerated (cu docs markdown --dir docs/site/commands)

`export tasks` defined its own -o/--output as the destination *file*, which
shadowed the global format flag every other command uses. `cu export tasks
-o json` therefore wrote a file literally named "json" instead of emitting
JSON — the flag a user learned everywhere else silently did something else.

The destination file moves to --file/-F, freeing -o.

Freeing it is only half the fix. Leaving -o unread would trade one silent
failure for another: a user asking for JSON would get the default CSV with no
warning. So export now honours it as a synonym for --format, since they select
the same thing. Anything export cannot produce — "table", "yaml", a filename —
is a loud error naming the supported set, and a path-shaped value additionally
points at --file, which is the predictable mistake this rename creates.

Disagreeing --format and --output is an error rather than a precedence rule.
Picking a winner is exactly how silently-wrong output happens, and there is no
reading of `--format csv -o json` that is obviously intended.

The existing flag test asserted the local "output" flag existed, encoding the
bug; it now asserts the opposite and fails if the shadow is reintroduced.

Fixes #28

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015ZEGsLHBQ2GzP6v48i4hXz
@timimsms
timimsms merged commit aeb272a into main Aug 29, 2026
16 checks passed
@timimsms
timimsms deleted the fix/export-output-flag branch August 29, 2026 22:53
timimsms added a commit that referenced this pull request Aug 30, 2026
Brings in #50 and #51. One conflict, in the export flag block, where this
branch's --include-closed/--subtasks met #51 moving the destination file off
-o/--output onto --file/-F. Both sides kept: the new filters stay, and -o is
left to the global format flag as #51 intends.

Resolved as a merge rather than a rebase because another session owns this
branch; force-pushing rewritten history over it is the one operation that could
destroy work in progress.

Docs regenerated for the combined flag set — the auto-merged page still
advertised -o as the output file, which is no longer true.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015ZEGsLHBQ2GzP6v48i4hXz
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