Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,12 +154,13 @@ A command that identifies a service takes a **ref** — its ID, a read replica s
- **stdout** gets the command's primary output: the data payload (table, JSON, YAML, env vars), and in the plain-text path the result text itself.
- **stderr** gets everything else: errors and warnings; interactive UI (confirmation prompts, password prompts, spinners); and progress/status messages that accompany structured output, so a piped stdout stays clean. `tiger service create` is the model — every status line is `cmd.PrintErrf`, and only the final service payload goes to stdout.

Helper functions inside `internal/cmd` take the `*cobra.Command` and print through it. A helper whose whole job is rendering into a writer (the `output*Table` functions) takes an `io.Writer` instead, since `tablewriter` needs one anyway. Code in other packages (`internal/common`, `internal/version`) must not import cobra — it takes an `io.Writer` (e.g. `common.WaitForServiceArgs.Output`) and the caller in `internal/cmd` passes `cmd.ErrOrStderr()` or `cmd.OutOrStdout()`.
Helper functions inside `internal/cmd` take the `*cobra.Command` and print through it. A helper whose whole job is rendering into a writer (the `output*Table` functions) takes an `io.Writer` instead, since `tablewriter` needs one anyway — as its first parameter, matching `util.SerializeToJSON` and `util.SerializeToYAML`. Code in other packages (`internal/common`, `internal/version`) must not import cobra — it takes an `io.Writer` (e.g. `common.WaitForServiceArgs.Output`) and the caller in `internal/cmd` passes `cmd.ErrOrStderr()` or `cmd.OutOrStdout()`.

### Formatting

- Commands with structured output take `-o`/`--output` with `json`, `yaml`, and `table` (the default); some commands support extras (`env`, `bare`). Register the flag with the validating flag types in `flag_helper.go` (`new(outputFlag)` and friends, with no bound variable) and read the value from `cfg.Output` — `output` is a config value.
- Serialize with `util.SerializeToJSON` and `util.SerializeToYAML`. **Don't add `yaml:` struct tags to output types** — `SerializeToYAML` encodes to JSON first and converts, so only `json:` tags matter and the two formats stay consistent (including for generated types that only carry `json:` tags).
- **Structured output is unconditional.** When a command succeeds with a structured format (`json`, `yaml`, `env`), stdout carries a payload in that format and nothing else, whatever the result, so it can always be piped into `jq` and the like. An empty list is `[]`, never a message and never empty output. A message for an empty or fallback result ("No services found.") belongs only in the output function's table case, printed to stderr before the table function would run. `tiger service list` is the model.
- Colored output uses lipgloss styles, restricted to the eight base ANSI colors (`lipgloss.Blue`, `lipgloss.Green`, …) rather than their bright variants or 256-color and true-color values, and without bold; de-emphasize text with `Faint` rather than a grey. That way output follows the user's terminal theme (some themes render bright colors and bold as greys). Render styles unconditionally: the output writers strip colors when the stream isn't a terminal, and the lifecycle disables color on them when `cfg.Color` is off, so commands need no per-command wiring. The `tiger skills install` picker is the model.

### Tone
Expand Down Expand Up @@ -195,7 +196,7 @@ Interactive menus and spinners use BubbleTea v2 (`charm.land/bubbletea/v2`). Alw
- Cleanup that can fail (closing a reader, removing a temporary directory) is called explicitly on the success path, with its error returned, and also deferred as best-effort cleanup for the error paths, where a more important error is already being returned. This relies on the cleanup being safe to call twice, so use it only for idempotent operations. `writeSkill` in `skills_install.go` is the model.
- Return errors from `RunE`; never call `os.Exit` in a command. For specific exit codes, wrap with `common.ExitWithCode`, and map API errors to exit codes with `common.ExitWithErrorFromStatusCode`. The exit codes are defined in `internal/common/errors.go`, and `main.go` honors a `common.ExitCodeError` anywhere in the error chain.
- After checking an API response's status code, nil-check the parsed body (`resp.JSON200`, etc.) before using it — a nil body indicates a bug and should return `errors.New("empty response from API")`.
- Operations that connect to a service should call `common.CheckServiceReady` first and translate `common.ErrPaused`/`common.ErrNotReady` into actionable messages.
- Operations that connect to a database should check `ConnectionTarget.CheckReady` first and translate `common.ErrPaused`/`common.ErrNotReady` into actionable messages.

## HTTP Requests

Expand Down
Loading
Loading