feat(output): add --table, --quiet, --json=false flags (#12)#13
Conversation
Resolves the "no way to grep dhq projects list" pain point: piping to grep
auto-switches to JSON, the table is gone, and a small permalink can match
many noisy lines.
--table force table output even when piped
--quiet, -q print only the canonical identifier, one per line, no header
(works on projects, servers, server-groups, deployments,
agents — i.e. the lists you usually grep)
--json=false explicit opt-out of JSON for shell aliases that default to
--json; previously it set JSONFields=["false"] and quietly
returned {} because "false" was treated as a field selector
Refactored the duplicated `env.JSONMode || !env.IsTTY` check across 37 command
files into a single `Envelope.WantsJSON()` method so the new overrides have
one place to live.
Closes #12
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
Disabled knowledge base sources:
WalkthroughThis PR centralizes output-mode selection behind Envelope.WantsJSON(), adds persistent ChangesCLI Output-Mode Control and Quiet-Mode Support
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b32d7a7a15
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| func IsJSONMode() bool { | ||
| if cliCtx != nil { | ||
| return cliCtx.Envelope.JSONMode || !cliCtx.Envelope.IsTTY | ||
| return cliCtx.Envelope.WantsJSON() | ||
| } | ||
| return flagJSON != "" |
There was a problem hiding this comment.
Respect --json=false in IsJSONMode fallback
When command setup fails before cliCtx is initialized (for example, invalid --cwd or config load errors in PersistentPreRunE), IsJSONMode() falls back to flagJSON != "", so --json=false is still treated as JSON mode and main writes a JSON error envelope. That contradicts the new --json=false opt-out semantics and produces machine output in cases where users explicitly asked to disable JSON.
Useful? React with 👍 / 👎.
Pulls the inline --json flag parsing out of PersistentPreRunE into a pure
function so the falsy opt-out (--json=false/0/no/off) and field-selection
paths are testable without spinning up the cobra command tree.
Table covers 14 cases including case-insensitivity and the bug-fix path
(previously --json=false set JSONFields=["false"] and silently returned
{} from every command).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
internal/commands/assist.go (1)
99-99: ⚡ Quick winAlign stream gating with
WantsJSON()precedence.Line 88 still checks
!env.JSONMode, so--table/--quietoverrides are not fully reflected in stream vs non-stream behavior. Prefer using!env.WantsJSON()for consistency.Proposed fix
- if env.IsTTY && !env.JSONMode && !noStream { + if env.IsTTY && !env.WantsJSON() && !noStream { env.Status("") fmt.Fprint(env.Stderr, "✨ ") //nolint:errcheck return ollama.ChatStream(cliCtx.Background(), messages, env.Stderr) }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/commands/assist.go` at line 99, Replace direct checks of the JSONMode boolean with the WantsJSON() accessor so stream gating respects flag precedence; specifically, change occurrences like "!env.JSONMode" to "!env.WantsJSON()" (and any inverted uses) in the assist command flow (the code paths around the existing if env.WantsJSON() check) so that --table/--quiet overrides are honored when deciding stream vs non-stream behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@internal/commands/assist.go`:
- Line 99: Replace direct checks of the JSONMode boolean with the WantsJSON()
accessor so stream gating respects flag precedence; specifically, change
occurrences like "!env.JSONMode" to "!env.WantsJSON()" (and any inverted uses)
in the assist command flow (the code paths around the existing if
env.WantsJSON() check) so that --table/--quiet overrides are honored when
deciding stream vs non-stream behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 65ab63b2-8fa1-4e10-9b31-bccd47eb3edd
📒 Files selected for processing (37)
internal/commands/activity.gointernal/commands/assist.gointernal/commands/auto_deploys.gointernal/commands/build_cache_files.gointernal/commands/build_commands.gointernal/commands/build_configs.gointernal/commands/build_known_hosts.gointernal/commands/build_languages.gointernal/commands/config_files.gointernal/commands/deploy.gointernal/commands/deployment_checks.gointernal/commands/deployments.gointernal/commands/doctor.gointernal/commands/env_vars.gointernal/commands/excluded_files.gointernal/commands/global_config_files.gointernal/commands/global_servers.gointernal/commands/insights.gointernal/commands/language_versions.gointernal/commands/network_agents.gointernal/commands/phase3_test.gointernal/commands/projects.gointernal/commands/repos.gointernal/commands/root.gointernal/commands/scheduled_deploys.gointernal/commands/server_groups.gointernal/commands/servers.gointernal/commands/signup.gointernal/commands/ssh_commands.gointernal/commands/ssh_keys.gointernal/commands/status.gointernal/commands/templates.gointernal/commands/test_access.gointernal/commands/url.gointernal/commands/zones.gointernal/output/envelope.gointernal/output/envelope_test.go
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@internal/commands/root.go`:
- Around line 265-272: In the switch default branch that currently does return
true, strings.Split(raw, ",") you must split raw by commas then trim and
sanitize each token (use strings.TrimSpace on each element), filter out empty
tokens produced by stray spaces or trailing commas, and return the cleaned
slice; if the cleaned slice is empty (no valid field names) return an error
instead of silently accepting the input. Locate the default case using the raw
variable in the switch and replace the simple strings.Split call with this
trim/filter/validate flow.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 75cf9080-d4d8-41ff-bde6-13890adbf35d
📒 Files selected for processing (2)
internal/commands/phase3_test.gointernal/commands/root.go
…itespace CodeRabbit review feedback on #13: 1. assist.go and watch.go still read env.JSONMode directly to gate TTY-only streaming/TUI paths. Switched to !env.WantsJSON() so --table and --quiet participate in the same precedence as the rest of the CLI. 2. parseJSONFlag's field-selection branch did a bare strings.Split, so --json="name, permalink" lost the second field to a leading space. Trim each token, drop empties, and fall back to bare --json semantics when the input is all whitespace/commas. Extended TestParseJSONFlag with 6 new cases covering the whitespace, trailing-comma, and all-whitespace edge cases. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Resolves #12 —
dhq projects list | grepwas unusable because piping auto-switched to verbose JSON, and--json=falsesilently filtered to{}.Three new global flags:
--table— force table output, even when piped.dhq projects list --table | grep prodnow does what you'd expect.-q/--quiet— print only the canonical identifier (permalink for projects, identifier elsewhere), one per line, no header. Pairs withxargs:dhq projects list -q | xargs -I {} dhq deployments list -p {}. Supported onprojects,servers,server-groups,deployments, andagentslist commands.--json=false(also0,no,off) — explicit opt-out of JSON. Previously setJSONFields=["false"]and silently returned{}because"false"was treated as a field selector. Now it's a real opt-out, useful for shell aliases that default to--json.Also refactored the duplicated
env.JSONMode || !env.IsTTYcheck across 33 command files into a singleEnvelope.WantsJSON()method, so future output-mode overrides live in one place.Test plan
go test ./...— passes (addedWantsJSONtruth-table test,WriteQuiettests, andGlobalOutputFlags_Registeredsmoke test)golangci-lint run ./...— cleandhq projects list --table | grep <name>returns matching rows in human formatdhq projects list -q | xargs -I {} dhq deployments list -p {}works end-to-enddhq projects list --json=falseshows the human table even when pipeddhq projects list(TTY) still shows table; piping still defaults to JSON🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Improvements
Tests