Conversation
Introduce a shared CLI I/O contract (JSON on stdout, structured errors, exit codes 0-5, TTY confirmation policy) and apply it across API commands. Add explicit jobs/trace verbs with backward-compatible aliases, --json/--quiet/ --yes/--dry-run where they apply, and update the v6 CLI reference. Co-authored-by: cody <cody@hud.ai>
Typer treated `hud jobs list --json` as a job id plus a bogus command because the group callback took a positional. Route unknown first tokens to `get` instead. Remember `--json` in a contextvar so structured errors still reach stdout when Click has torn down its context (CliRunner). Co-authored-by: cody <cody@hud.ai>
codyadam
marked this pull request as ready for review
August 31, 2026 14:09
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
Autofix Details
Bugbot Autofix prepared fixes for both issues found in the latest run.
- ✅ Fixed: Dry-run cancel still requires confirmation
- Moved confirm_or_abort calls after the dry-run early return so --dry-run works without --yes in non-interactive environments.
- ✅ Fixed: Deploy-all JSON writes multiple documents
- Changed deploy_all to pass json_output=False to individual deploy_environment calls, emitting only the summary JSON document.
You can send follow-ups to the cloud agent here.
…or deploy --all - cancel: move confirm_or_abort after dry-run check so --dry-run works without --yes in non-interactive environments - deploy: suppress per-environment JSON when deploy_all is invoked, only emit the summary JSON document to stdout
…eable Strip ANSI from CLI help assertions so CI color output still matches --json. Keep cancel --dry-run from prompting (already on this branch) and cover it with a non-TTY test. Emit one deploy --all --json summary that includes per-environment plans instead of concatenated documents. Co-authored-by: cody <cody@hud.ai>
Passing json_output=False is not enough: wants_json still sees the parent --json contextvar. deploy_all now sets emit_result=False so stdout is one summary document. Co-authored-by: cody <cody@hud.ai>
Co-authored-by: cody <cody@hud.ai>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
Bugbot Autofix prepared fixes for both issues found in the latest run.
- ✅ Fixed: Failed deploy details are discarded
- When emit_result=False, deploy_environment now returns the payload before checking result.success, allowing deploy_all to include full details (build_id, status, registry_id, name) in the environments list.
- ✅ Fixed: Deploy-all JSON still duplicated
- Moved require_api_key call to deploy_all before the loop and skipped it in deploy_environment when emit_result=False, ensuring only one JSON document is written.
You can send follow-ups to the cloud agent here.
Reviewed by Cursor Bugbot for commit 2756fd6. Configure here.
…deploy --all Bug 1: When deploy_environment failed, it raised typer.Exit(1) before returning the payload. Now when emit_result=False (called from deploy_all), the payload is returned even on failure so deploy_all can include the full details (build_id, status, registry_id, name) in the environments list. Bug 2: require_api_key calls abort() which emits JSON based on the global --json flag. When called from deploy_all, this caused duplicate JSON output. Now deploy_all calls require_api_key once before the loop, and deploy_environment skips the check when emit_result=False.
Return failed per-env payloads instead of Exit-and-catch so the environments list keeps build_id, status, registry_id, and name. Suppress per-env abort JSON (including a missing API key) while aggregating so stdout is only the summary document. Co-authored-by: cody <cody@hud.ai>
Co-authored-by: cody <cody@hud.ai>
Check settings.api_key locally so ty can treat the return as str, and keep missing_api_key_error as a CliError factory. Co-authored-by: cody <cody@hud.ai>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

What changed
HUD CLI API commands now share one agent-facing I/O contract in
hud/cli/utils/output.py:--json/--output jsonon jobs, models, qa, trace, task, client, cancel, sync, eval, deploy, init, login, set, version, and auth. JSON goes to stdout; progress, warnings, and human error text go to stderr.--quieton list/get commands that return ids (jobs, models, qa, task).--yeson cancel, eval, and sync. Non-TTY sessions require--yes(exit 2) instead of hanging on a prompt.--dry-runnever prompts.--dry-runon cancel, deploy, eval, init, models fork/head, qa run, and sync tasks.{error, message, input, suggestion, transient, existing_id}with exit codes0success,1failure,2usage,3not found,4permission,5conflict. HTTP 404/401-403/409 map to 3/4/5; 429/5xx are markedtransient.hud jobs list|get|cancel,hud trace get,hud auth login|set. Existing invocations stay as aliases (hud jobs,hud jobs <id>,hud cancel,hud login,hud set).hud models fork --if-not-exists;hud initon a non-empty directory exits 5 (conflict).hud deploy --all --jsonemits one summary document (succeeded,failed,dry_run,environments). Per-env progress and error text go to stderr. Failed envs keepbuild_id/status/registry_id/name(anderrorwhen the env aborted). Per-envabort()cannot write extra JSON objects.Human table output remains the default. Agents opt in with
--json. Existing JSON payload shapes for jobs/models/qa/trace are unchanged. CLI reference indocs/v6/reference/cli.mdxis updated.Why
AI agents consume CLIs through
--help, stdout, and$?. The previous surface mixed tables with ad-hoc JSON, used 0/1 for almost everything, and could hang on confirmation prompts in non-TTY sessions. One shared contract is easier for agents than per-command parsing.Impact
Scripts that already pass
--jsonkeep the same payload shapes. New flags are opt-in except:--yes(or they exit 2 instead of prompting).--dry-runskips confirmation.hud initon a non-empty directory is now a conflict (exit 5) rather than a generic failure.permission_denied).hud deploy --allwith no API key still writes one summary and exits 1 if any env failed.hud deploy --all --jsonis a single JSON object, not concatenated per-env documents.No server-side batch job delete was added; the API has no bulk selector.
hud serveis long-running and does not take--json.hud login --quietstill means “don’t open a browser.”Validation
Fail-to-pass:
hud/cli/tests/test_agent_cli.pyandhud/cli/utils/tests/test_output.pycover JSON-on-stdout vs messages-on-stderr, exit codes 2–5,--quietids,--dry-run, non-TTY--yesrequirement, unknown-token dispatch (hud jobs <id>), and structuredabort()errors.testjob).test_cancel_dry_run_json_skips_confirmation: non-TTYhud cancel --dry-run --jsonwithout--yesexits 0 with one plan JSON object.--all --jsontests assert a singlejson.loadsdocument, failed-env details inenvironments, and a missing API key still one document withpermission_deniedper env.require_api_keynarrowssettings.api_keytostrafter abort sotyaccepts the return (invalid-return-typeonhud/cli/utils/api.py).Pass-to-pass:
uv run pytest hud/cli -q(224 passed).uv run ruff format . --checkanduv run ruff check .pass.uv run --extra dev --extra train --extra modal --extra daytona ty check --error-on-warningpasses.