Skip to content

feat: CLI global options infrastructure, UI modes, exit codes, env vars#891

Merged
Aureliolo merged 8 commits intomainfrom
feat/add-config-flags-for-more-options
Mar 27, 2026
Merged

feat: CLI global options infrastructure, UI modes, exit codes, env vars#891
Aureliolo merged 8 commits intomainfrom
feat/add-config-flags-for-more-options

Conversation

@Aureliolo
Copy link
Copy Markdown
Owner

@Aureliolo Aureliolo commented Mar 27, 2026

Summary

  • Add GlobalOpts struct threaded via Cobra context with 8 persistent flags: --quiet/-q, --verbose/-v (stackable), --no-color, --plain, --json, --yes/-y, plus existing --data-dir and --skip-verify
  • Extend UI package with Options for plain mode (ASCII-only icons, box-drawing, dividers), quiet mode (suppress non-essential output), and JSON mode
  • Add categorized hint methods (HintError, HintNextStep, HintTip, HintGuidance) with always/auto/never control -- migrate all 37 existing Hint() calls
  • Add ExitError type with typed exit codes (0=success, 1=runtime, 2=usage, 3=unhealthy, 4=unreachable, 10=update-available)
  • Consolidate ChildExitError from update.go to exitcodes.go
  • Define 13 SYNTHORG_* env var constants with NO_COLOR/CLICOLOR/TERM=dumb standard support
  • Migrate all 15+ command files to use GetGlobalOpts/NewUIWithOptions
  • Remove local --json flag from status command (now global)
  • Forward new global flags in reexecUpdate for CLI self-update re-exec
  • Document CLI global flags, env vars, and exit codes in CLAUDE.md and operations.md

PR 1 of 3 for #886

Test plan

  • go -C cli build ./... -- builds clean
  • go -C cli vet ./... -- no issues
  • go -C cli tool golangci-lint run -- 0 issues
  • go -C cli test ./... -- all pass
  • New tests: TestQuietModeSuppresses, TestPlainModeASCIIIcons, TestPlainModeBox, TestHintCategories (auto/always/never), TestSpinnerQuietMode, TestSpinnerPlainMode, TestJSONOutput, TestExitError, TestChildExitCode, TestGetGlobalOptsDefault, TestSetGetGlobalOpts, TestUIOptionsQuietImpliedByJSON
  • Smoke: synthorg --quiet version produces no output
  • Smoke: synthorg --plain version shows ASCII "SynthOrg" not Unicode logo
  • Smoke: synthorg --json version produces valid JSON
  • Verify pre-commit hooks pass (golangci-lint, go vet, commitizen)

Pre-reviewed by 5 agents (go-reviewer, go-conventions, go-security, docs-consistency, issue-resolution), 16 findings addressed.

Part of #886

🤖 Generated with Claude Code

Aureliolo and others added 2 commits March 27, 2026 20:42
…d env vars (#886)

Add GlobalOpts struct (--quiet, --verbose, --no-color, --plain, --json,
--yes) threaded via Cobra context. Extend UI package with Options for
plain mode (ASCII-only icons, box-drawing, dividers), quiet mode
(suppress non-essential output), and categorized hint methods
(HintError, HintNextStep, HintTip, HintGuidance) with auto/always/never
control. Add ExitError type for typed exit codes. Add SYNTHORG_* env var
layer (data-dir, log-level, ports, channel, image-tag, no-verify,
auto-update-cli, auto-pull, auto-restart). Migrate all 15 command files
to use GetGlobalOpts and NewUIWithOptions. Migrate all Hint() calls to
categorized hint methods.

Closes: PR 1 of #886

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…s, reexec

Pre-reviewed by 5 agents, 16 findings addressed:

- Fix cmd.Flags().Changed() querying wrong flag set for persistent flags
  on subcommands -- use flag variables directly instead
- Move SYNTHORG_SKIP_VERIFY env var resolution from init() to
  setupGlobalOpts for consistency with other env var overrides
- Add EnvQuiet and EnvYes constants to envvars.go (were string literals)
- Remove envString wrapper (trivial os.Getenv indirection)
- Fix "Wipe cancelled" hints using HintGuidance (suppressed by default)
  -- changed to HintNextStep so users see confirmation
- Fix non-interactive cleanup hint using HintGuidance -- changed to
  HintNextStep so CI users see why nothing was removed
- Remove local --json flag from status command that shadowed global
  persistent --json flag
- Forward new global flags (--quiet, --verbose, --no-color, --plain,
  --json, --yes) in reexecUpdate for CLI self-update re-exec
- Fix performRestart ignoring global UI options -- accept ui.Options
  parameter instead of using bare ui.NewUI
- Suppress ChildExitError printing in Execute() (internal message)
- Add validHintsMode validation for hints config
- Document CLI global flags, env vars, and exit codes in CLAUDE.md
- Update Package Structure in CLAUDE.md for new cmd/ files
- Update operations.md CLI info-box with output modes and exit codes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 27, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a0f0ff0c-0c71-4294-941a-cec23e1cba67

📥 Commits

Reviewing files that changed from the base of the PR and between 5ab2a86 and 54aa548.

📒 Files selected for processing (1)
  • cli/internal/ui/ui_test.go
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: CLI Test (windows-latest)
  • GitHub Check: CLI Test (macos-latest)
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (1)
cli/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Go CLI: golangci-lint + go vet checked in pre-commit hooks and CI (conditional on cli/**/*.go changes)

Files:

  • cli/internal/ui/ui_test.go
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/cli.yml : CLI workflow: Go lint (golangci-lint + go vet) + test (-race -coverprofile) + build (cross-compile: linux/darwin/windows × amd64/arm64) + govulncheck + fuzz testing (main-only, 30s/target, continue-on-error, matrix over 4 packages). cli-pass gate includes fuzz as informational. GoReleaser release on v* tags. Cosign keyless signing of checksums.txt. SLSA L3 provenance attestations. Sigstore bundle (.sigstore.json) attached. Post-release appends checksums/verification/provenance to draft release notes.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Applies to cli/**/*.go : Use Cobra for CLI command structure, charmbracelet/huh for interactive prompts, and charmbracelet/lipgloss for terminal styling
📚 Learning: 2026-03-27T17:07:16.441Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T17:07:16.441Z
Learning: Applies to cli/**/*_fuzz_test.go : Go CLI uses native 'testing.F' fuzz functions (FuzzYamlStr, etc.)

Applied to files:

  • cli/internal/ui/ui_test.go
📚 Learning: 2026-03-19T11:30:29.217Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:30:29.217Z
Learning: Applies to cli/**/*.go : Run Go lint via `golangci-lint run`, vet via `go vet`, tests via `go test ./...`, and fuzz via `go test -fuzz=FuzzTarget -fuzztime=30s`

Applied to files:

  • cli/internal/ui/ui_test.go
📚 Learning: 2026-03-19T11:19:40.044Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:19:40.044Z
Learning: Applies to cli/**/*.go : Use native Go testing with `testing.F` fuzz functions (`Fuzz*`) for fuzz testing.

Applied to files:

  • cli/internal/ui/ui_test.go
📚 Learning: 2026-03-21T12:54:22.557Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Applies to cli/**/*.go : Use native Go `testing.F` fuzz functions (`Fuzz*`) for property-based testing

Applied to files:

  • cli/internal/ui/ui_test.go
📚 Learning: 2026-03-21T14:12:17.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T14:12:17.848Z
Learning: Applies to cli/**/*.go : Use native `testing.F` fuzz functions (`Fuzz*`) for fuzzing

Applied to files:

  • cli/internal/ui/ui_test.go
🔇 Additional comments (7)
cli/internal/ui/ui_test.go (7)

430-476: LGTM! Comprehensive quiet mode coverage.

The test validates the complete suppression matrix for quiet mode: non-essential methods produce no output, while Error and Plain still emit. Good use of t.Name() for the HintTip message to avoid cross-test interference with the shared sessionTipsSeen map.


478-508: LGTM! Plain mode icon test is now correctly aligned.

The Unicode icon exclusion list now matches the methods under test (Success, Error, Warn, Step), with IconWarning correctly included and IconHint removed since no hint method is called.


510-579: LGTM! Plain mode rendering tests are thorough.

Good coverage of ASCII fallbacks for box drawing, dividers, tables, and logo art. Each test validates both the presence of ASCII alternatives and the absence of Unicode characters.


581-595: LGTM!

Direct equality checks appropriately validate that icon accessors return the exact plain ASCII constants in plain mode.


597-680: LGTM! Hint category test isolation properly addressed.

The use of t.Name() prefixes for all HintTip messages (lines 617, 629, 660) ensures robust isolation across test runs, addressing the previous concern about shared sessionTipsSeen state. The three subtests comprehensively cover the hint mode matrix.


682-709: LGTM!

Spinner mode tests correctly validate quiet mode suppression and plain mode ASCII icon rendering.


711-727: LGTM!

Basic JSON output validation covers the serialization path. The test correctly verifies expected fields appear in the output.


Walkthrough

Centralizes CLI global options into a new GlobalOpts type with context setters/getters and wires root persistent flags (--data-dir, --skip-verify, --quiet, --verbose, --no-color, --plain, --json, --yes). Adds exported environment-variable constants, typed exit codes and error helpers (ExitError, ChildExitError) with tests. Introduces ui.Options, NewUIWithOptions, plain/quiet/JSON UI modes, and categorized hint methods with extensive UI tests. Commands now derive config, prompting behavior, and UI rendering from GlobalOpts; many prompts were standardized to use ShouldPrompt/--yes and hint rendering was adjusted. Documentation updated with global flags and exit-code table.

Suggested labels

autorelease: tagged

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and concisely summarizes the main changes: introducing global options infrastructure, UI modes, exit codes, and environment variables for the CLI. It is specific, focused on the primary work, and avoids vague terms.
Description check ✅ Passed The PR description is well-structured and comprehensively related to the changeset, detailing the GlobalOpts struct, UI extensions, hint method changes, exit code types, environment variables, command migrations, and documentation updates that match the file summaries.
Docstring Coverage ✅ Passed Docstring coverage is 47.25% which is sufficient. The required threshold is 40.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 27, 2026 20:03 — with GitHub Actions Inactive
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 27, 2026

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Snapshot Warnings

⚠️: No snapshots were found for the head SHA 54aa548.
Ensure that dependencies are being submitted on PR branches. Re-running this action after a short time may resolve the issue. See the documentation for more information and troubleshooting advice.

Scanned Files

None

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements a centralized global options system for the CLI, introducing persistent flags for quiet mode, plain text, JSON output, and automatic confirmation. It also standardizes exit codes and environment variable handling while refactoring the UI package to respect these settings. Feedback highlights an opportunity to migrate legacy interactivity checks to the new ShouldPrompt method, ensuring consistent behavior across all commands when the --yes flag is active.

Comment on lines +37 to +46
func (g *GlobalOpts) ShouldPrompt() bool {
if g.Yes {
return false
}
fi, err := os.Stdin.Stat()
if err != nil {
return false
}
return fi.Mode()&os.ModeCharDevice != 0
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This ShouldPrompt method is a great addition for centralizing interactivity checks. However, the legacy isInteractive() function still exists in root.go and is used in several commands (cleanup, init, update, wipe).

Those usages should be migrated to use !GetGlobalOpts(ctx).ShouldPrompt() to ensure consistent behavior, especially for the new --yes flag which isInteractive() does not respect.

For example, synthorg wipe will fail in a non-interactive environment even with --yes because it checks isInteractive() at the beginning. Using ShouldPrompt would correctly handle the --yes flag in this case.

This would require some refactoring in functions like confirmUpdateWithDefault to accept a context.Context, but it would make the CLI behavior more robust and predictable.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
cli/internal/ui/livebox.go (1)

117-128: ⚠️ Potential issue | 🟠 Major

Quiet/JSON failures can leak human output to stdout.

Adding lb.ui.quiet to this immediate-print path routes quiet-mode failures through the stdout-backed UI. --json hits this path because GlobalOpts.UIOptions() coerces JSON into quiet, and pullServicesLive builds the live box from out in cli/cmd/start.go. A failed pull can therefore emit human-readable lines on stdout before the command returns its error. Quiet mode should stay silent here, or LiveBox needs a separate stderr UI for failure lines.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/internal/ui/livebox.go` around lines 117 - 128, The current branch prints
failures to the stdout-backed UI when lb.ui.quiet is true, leaking human output;
modify the logic in the immediate-print path so quiet mode is a no-op: at the
start of the function check if lb.ui.quiet and return early (or skip printing)
instead of including lb.ui.quiet in the if-condition, then keep the existing
behavior for non-TTY and lb.ui.plain; update the condition around the block that
calls lb.ui.Error / lb.ui.Success (and references lb.lines[index].status and
IconError/PlainIconError) to only run for !lb.ui.isTTY || lb.ui.plain so quiet
mode does not route failures to stdout.
cli/internal/ui/ui.go (2)

65-80: ⚠️ Potential issue | 🟡 Minor

Tip dedup resets every time a new UI is constructed.

Because tipsSeen lives on the UI instance, separate stdout/stderr UIs do not share it. That falls short of session-level dedup and will re-show the same tip whenever a command creates another UI. Consider moving the cache out of the instance or threading a shared store through Options.

Also applies to: 229-244

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/internal/ui/ui.go` around lines 65 - 80, The tipsSeen sync.Map on the UI
struct causes tip dedup to reset whenever a new UI is constructed; move the
dedup store out of the UI instance so it is shared for the session (either make
a package-level/shared sync.Map or add a shared TipsStore pointer into the UI
creation Options and have NewUI/FromOptions use that shared map) and update all
places that construct UI (references to UI.tipsSeen and any HintTip logic) to
use the shared store (also adjust related code around the HintTip usage
referenced in the same diff block).

90-119: ⚠️ Potential issue | 🟠 Major

jsonMode is tracked but never actually suppresses human emitters.

NewUIWithOptions records JSON mode, but the normal UI helpers still write human text. Success, Warn, Error, and the existing undecorated output path can therefore leak into a --json stdout stream and make the payload unparseable. Right now JSON mode is effectively quiet mode plus a dead field.

Also applies to: 156-191

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/internal/ui/ui.go` around lines 90 - 119, NewUIWithOptions sets
UI.jsonMode but the human-facing emitters still write plain text into stdout;
update the UI methods (e.g., Success, Warn, Error, Print/Write or any helper
that emits decorated/human output) to check ui.jsonMode and, when true, avoid
emitting human text to stdout (either suppress the human output entirely or emit
only machine/json payloads), and route any non-JSON diagnostics to stderr
instead; touch the methods named Success, Warn, Error, Print (or Write) and
ensure they consult UI.jsonMode (and/or UI.quiet) before writing to the provided
writer so --json no longer leaks human text into the JSON stdout stream.
cli/cmd/update.go (1)

391-417: ⚠️ Potential issue | 🟠 Major

Use the resolved SkipVerify value here, not flagSkipVerify.

pullAndPersist already loads GetGlobalOpts(cmd.Context()), but verifyAndPinForUpdate still branches on the package flag variable. That only reflects the explicit CLI flag, so env/config-driven skip-verify settings will be ignored during the update pull flow.

🛠️ Suggested wiring
 func pullAndPersist(ctx context.Context, cmd *cobra.Command, info docker.Info, state config.State, tag, safeDir string, preserveCompose bool) (config.State, error) {
 	opts := GetGlobalOpts(cmd.Context())
 	out := ui.NewUIWithOptions(cmd.OutOrStdout(), opts.UIOptions())
@@
-	digestPins, err := verifyAndPinForUpdate(ctx, state, tag, safeDir, preserveCompose, out, errOut)
+	digestPins, err := verifyAndPinForUpdate(ctx, state, tag, safeDir, preserveCompose, opts.SkipVerify, out, errOut)
@@
-func verifyAndPinForUpdate(ctx context.Context, state config.State, tag, safeDir string, preserveCompose bool, out *ui.UI, errOut *ui.UI) (map[string]string, error) {
+func verifyAndPinForUpdate(ctx context.Context, state config.State, tag, safeDir string, preserveCompose bool, skipVerify bool, out *ui.UI, errOut *ui.UI) (map[string]string, error) {
@@
-	if flagSkipVerify {
+	if skipVerify {

Also applies to: 447-452

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/cmd/update.go` around lines 391 - 417, The call sites should use the
resolved SkipVerify from GetGlobalOpts(cmd.Context()) instead of the raw CLI
package flag variable; replace uses of flagSkipVerify with opts.SkipVerify
(where opts := GetGlobalOpts(cmd.Context()) is already set) when calling
verifyAndPinForUpdate and the other pull/update helper (e.g., pullAndPersist) so
env/config-driven skip-verify takes effect; locate references to flagSkipVerify
and change them to read opts.SkipVerify or pass opts into the helper so
verifyAndPinForUpdate and related functions observe the resolved SkipVerify
value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cli/cmd/backup.go`:
- Around line 404-407: The missing --confirm path returns a plain error causing
exit code 1; change the return to an ExitUsage error so callers get the usage
exit code. In the restore command handler (the block that calls errOut.Error and
errOut.HintNextStep when !confirm), replace returning errors.New("--confirm flag
is required") with returning NewExitError(ExitUsage, "--confirm flag is
required") (or equivalent constructor used in this codebase) so the CLI exits
with the documented usage code; keep the existing errOut messages intact.

In `@cli/cmd/envvars.go`:
- Around line 27-37: The envBool function fails to handle env values with
surrounding whitespace; update envBool to trim the retrieved env string (use
strings.TrimSpace on the value from os.Getenv) before checking for empty and
before strings.ToLower/switch comparisons (i.e., operate on the trimmed
variable). Keep the same truthy cases ("1","true","yes") but perform them
against the trimmed, lowercased value so inputs like " true " or "yes " are
correctly parsed.

In `@cli/cmd/global_opts.go`:
- Around line 35-46: The confirmation logic currently ignores GlobalOpts.Yes
because callers still call isInteractive() directly; update the confirm code
paths to consume the resolved global option instead: thread GlobalOpts (or at
least its ShouldPrompt() result) into the confirmation helpers used by the
synthorg update flow and replace direct calls to isInteractive() with
g.ShouldPrompt() (or use the passed-in bool) in those helpers (look for
functions named isInteractive, confirmUpdate/confirmAction or the update command
handler that performs the prompt). Ensure the synthorg update command's
run/handler signature accepts the GlobalOpts (or a prompt boolean) and forward
it to the confirmation helper so --yes disables prompts; update associated tests
to pass the GlobalOpts or bool instead of relying on isInteractive().

In `@cli/cmd/root.go`:
- Around line 61-66: Compute the effective option values before running the
mutual-exclusion checks: use the explicit-flag presence indicators together with
environment-derived overrides to produce resolvedQuiet, resolvedVerbose (or an
integer), resolvedPlain, and resolvedJSON, then validate those resolved
variables rather than the raw flag booleans (e.g. replace checks of
flagQuiet/flagVerbose/flagPlain/flagJSON with
resolvedQuiet/resolvedVerbose/resolvedPlain/resolvedJSON). If a conflict is
detected return ExitUsage with a clear error (e.g. "--quiet and --verbose are
mutually exclusive" or "--plain and --json are mutually exclusive"). Apply this
same pattern for the other validation blocks referenced in the comment (lines
~69-91) so all mutual-exclusion checks use the resolved values.

In `@cli/cmd/start.go`:
- Around line 160-163: The helper verifyAndPinImages currently checks the raw
flag variable flagSkipVerify instead of the resolved option from
setupGlobalOpts; update its skip logic to use the resolved value
(state.GlobalOpts.SkipVerify or the equivalent field on the passed config.State)
so SYNTHORG_SKIP_VERIFY/SYNTHORG_NO_VERIFY are honored across start/wipe/update
flows and remove reliance on flagSkipVerify; keep the warning text and behavior
the same but gate it with the resolved GlobalOpts.SkipVerify.

In `@cli/cmd/wipe.go`:
- Around line 91-93: runWipe is still prompting interactively even when the
global --yes flag is set; update runWipe to consult the GetGlobalOpts() result
(opts.Yes) immediately and take a fast path that skips the huh/confirmation
prompt when true. Specifically, in the wipe command flow that creates opts, out
and errOut, add an early check in runWipe (or the confirmation branch) that if
opts.Yes is true it proceeds with the destructive action using the existing
out/errOut/UIs instead of calling huh; otherwise keep the current interactive
prompt path. Ensure references to GetGlobalOpts, opts.Yes, runWipe, and the
huh/confirmation logic are adjusted so non-interactive automation can run with
--yes or SYNTHORG_YES=1.

---

Outside diff comments:
In `@cli/cmd/update.go`:
- Around line 391-417: The call sites should use the resolved SkipVerify from
GetGlobalOpts(cmd.Context()) instead of the raw CLI package flag variable;
replace uses of flagSkipVerify with opts.SkipVerify (where opts :=
GetGlobalOpts(cmd.Context()) is already set) when calling verifyAndPinForUpdate
and the other pull/update helper (e.g., pullAndPersist) so env/config-driven
skip-verify takes effect; locate references to flagSkipVerify and change them to
read opts.SkipVerify or pass opts into the helper so verifyAndPinForUpdate and
related functions observe the resolved SkipVerify value.

In `@cli/internal/ui/livebox.go`:
- Around line 117-128: The current branch prints failures to the stdout-backed
UI when lb.ui.quiet is true, leaking human output; modify the logic in the
immediate-print path so quiet mode is a no-op: at the start of the function
check if lb.ui.quiet and return early (or skip printing) instead of including
lb.ui.quiet in the if-condition, then keep the existing behavior for non-TTY and
lb.ui.plain; update the condition around the block that calls lb.ui.Error /
lb.ui.Success (and references lb.lines[index].status and
IconError/PlainIconError) to only run for !lb.ui.isTTY || lb.ui.plain so quiet
mode does not route failures to stdout.

In `@cli/internal/ui/ui.go`:
- Around line 65-80: The tipsSeen sync.Map on the UI struct causes tip dedup to
reset whenever a new UI is constructed; move the dedup store out of the UI
instance so it is shared for the session (either make a package-level/shared
sync.Map or add a shared TipsStore pointer into the UI creation Options and have
NewUI/FromOptions use that shared map) and update all places that construct UI
(references to UI.tipsSeen and any HintTip logic) to use the shared store (also
adjust related code around the HintTip usage referenced in the same diff block).
- Around line 90-119: NewUIWithOptions sets UI.jsonMode but the human-facing
emitters still write plain text into stdout; update the UI methods (e.g.,
Success, Warn, Error, Print/Write or any helper that emits decorated/human
output) to check ui.jsonMode and, when true, avoid emitting human text to stdout
(either suppress the human output entirely or emit only machine/json payloads),
and route any non-JSON diagnostics to stderr instead; touch the methods named
Success, Warn, Error, Print (or Write) and ensure they consult UI.jsonMode
(and/or UI.quiet) before writing to the provided writer so --json no longer
leaks human text into the JSON stdout stream.
🪄 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: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: b4ae348b-d0ce-4114-8ae3-0d36a929a3fd

📥 Commits

Reviewing files that changed from the base of the PR and between 6cdcd8a and def9787.

📒 Files selected for processing (30)
  • CLAUDE.md
  • cli/cmd/backup.go
  • cli/cmd/cleanup.go
  • cli/cmd/completion_install.go
  • cli/cmd/config.go
  • cli/cmd/doctor.go
  • cli/cmd/doctor_report.go
  • cli/cmd/envvars.go
  • cli/cmd/exitcodes.go
  • cli/cmd/exitcodes_test.go
  • cli/cmd/global_opts.go
  • cli/cmd/global_opts_test.go
  • cli/cmd/init.go
  • cli/cmd/root.go
  • cli/cmd/start.go
  • cli/cmd/status.go
  • cli/cmd/stop.go
  • cli/cmd/uninstall.go
  • cli/cmd/update.go
  • cli/cmd/update_cleanup.go
  • cli/cmd/update_health.go
  • cli/cmd/version.go
  • cli/cmd/wipe.go
  • cli/internal/ui/box.go
  • cli/internal/ui/livebox.go
  • cli/internal/ui/spinner.go
  • cli/internal/ui/ui.go
  • cli/internal/ui/ui_test.go
  • cli/main.go
  • docs/design/operations.md
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: CLI Test (windows-latest)
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (2)
cli/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Go CLI: golangci-lint + go vet checked in pre-commit hooks and CI (conditional on cli/**/*.go changes)

Files:

  • cli/cmd/version.go
  • cli/cmd/doctor.go
  • cli/cmd/stop.go
  • cli/cmd/update_health.go
  • cli/cmd/completion_install.go
  • cli/cmd/doctor_report.go
  • cli/cmd/init.go
  • cli/internal/ui/spinner.go
  • cli/cmd/uninstall.go
  • cli/main.go
  • cli/cmd/start.go
  • cli/cmd/update_cleanup.go
  • cli/cmd/status.go
  • cli/internal/ui/box.go
  • cli/cmd/backup.go
  • cli/cmd/wipe.go
  • cli/cmd/root.go
  • cli/cmd/exitcodes_test.go
  • cli/internal/ui/livebox.go
  • cli/cmd/global_opts_test.go
  • cli/internal/ui/ui_test.go
  • cli/cmd/update.go
  • cli/cmd/config.go
  • cli/cmd/global_opts.go
  • cli/cmd/exitcodes.go
  • cli/cmd/cleanup.go
  • cli/cmd/envvars.go
  • cli/internal/ui/ui.go
docs/design/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Update the relevant docs/design/ page to reflect approved deviations from the spec

Files:

  • docs/design/operations.md
🧠 Learnings (22)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Applies to cli/**/*.go : Use Cobra for CLI command structure, charmbracelet/huh for interactive prompts, and charmbracelet/lipgloss for terminal styling
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/cli.yml : CLI workflow: Go lint (golangci-lint + go vet) + test (-race -coverprofile) + build (cross-compile: linux/darwin/windows × amd64/arm64) + govulncheck + fuzz testing (main-only, 30s/target, continue-on-error, matrix over 4 packages). cli-pass gate includes fuzz as informational. GoReleaser release on v* tags. Cosign keyless signing of checksums.txt. SLSA L3 provenance attestations. Sigstore bundle (.sigstore.json) attached. Post-release appends checksums/verification/provenance to draft release notes.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to cli/** : CLI: Go 1.26+, dependencies in cli/go.mod (Cobra, charmbracelet/huh).
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to cli/**/*.go : Go CLI (Go 1.26+) uses Cobra for commands, charmbracelet/huh for interactive CLI, charmbracelet/lipgloss for styled output. Cross-platform builds (linux/darwin/windows × amd64/arm64). GoReleaser for releases with cosign keyless signing of checksums.txt. SLSA L3 provenance attestations via actions/attest-build-provenance.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Go 1.26+ required; CLI dependencies in `cli/go.mod` (Cobra, charmbracelet/huh, charmbracelet/lipgloss, sigstore-go, go-containerregistry, go-tuf)
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to cli/** : CLI: Go 1.26+, dependencies in cli/go.mod (Cobra, charmbracelet/huh).

Applied to files:

  • cli/cmd/version.go
  • cli/cmd/doctor.go
  • cli/cmd/completion_install.go
  • cli/cmd/uninstall.go
  • cli/main.go
  • cli/cmd/start.go
  • docs/design/operations.md
  • cli/cmd/backup.go
  • cli/cmd/root.go
  • cli/cmd/global_opts_test.go
  • cli/cmd/update.go
  • cli/cmd/config.go
  • cli/cmd/global_opts.go
  • cli/cmd/exitcodes.go
  • cli/cmd/envvars.go
  • CLAUDE.md
  • cli/internal/ui/ui.go
📚 Learning: 2026-03-21T12:54:22.557Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Applies to cli/**/*.go : Use Cobra for CLI command structure, charmbracelet/huh for interactive prompts, and charmbracelet/lipgloss for terminal styling

Applied to files:

  • cli/cmd/doctor.go
  • cli/cmd/uninstall.go
  • cli/cmd/start.go
  • docs/design/operations.md
  • cli/cmd/backup.go
  • cli/cmd/root.go
  • cli/cmd/config.go
  • cli/cmd/global_opts.go
  • cli/cmd/envvars.go
  • CLAUDE.md
  • cli/internal/ui/ui.go
📚 Learning: 2026-03-15T20:45:14.430Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T20:45:14.430Z
Learning: Shell commands: for Go CLI work, cd cli is an exception because Go tooling requires working directory to be the module root. Go commands require `cd cli` for other work, never use `cd`.

Applied to files:

  • cli/main.go
  • cli/cmd/exitcodes.go
  • CLAUDE.md
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to cli/**/*.go : Go CLI (Go 1.26+) uses Cobra for commands, charmbracelet/huh for interactive CLI, charmbracelet/lipgloss for styled output. Cross-platform builds (linux/darwin/windows × amd64/arm64). GoReleaser for releases with cosign keyless signing of checksums.txt. SLSA L3 provenance attestations via actions/attest-build-provenance.

Applied to files:

  • cli/cmd/start.go
  • docs/design/operations.md
  • cli/cmd/backup.go
  • cli/cmd/global_opts_test.go
  • cli/cmd/global_opts.go
  • cli/cmd/envvars.go
  • CLAUDE.md
📚 Learning: 2026-03-19T11:19:40.044Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:19:40.044Z
Learning: Applies to go.mod : Maintain Go 1.26+ requirement. Dependencies: Cobra (CLI framework), charmbracelet/huh and charmbracelet/lipgloss (UI), sigstore-go (code signing), go-containerregistry (container image verification), go-tuf (TUF client for Sigstore).

Applied to files:

  • cli/cmd/start.go
  • docs/design/operations.md
  • cli/cmd/global_opts.go
  • cli/internal/ui/ui.go
📚 Learning: 2026-03-21T12:54:22.557Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Go 1.26+ required; CLI dependencies in `cli/go.mod` (Cobra, charmbracelet/huh, charmbracelet/lipgloss, sigstore-go, go-containerregistry, go-tuf)

Applied to files:

  • cli/cmd/start.go
  • docs/design/operations.md
  • cli/cmd/config.go
  • cli/cmd/global_opts.go
  • cli/cmd/envvars.go
  • CLAUDE.md
  • cli/internal/ui/ui.go
📚 Learning: 2026-03-21T14:12:17.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T14:12:17.848Z
Learning: Applies to cli/go.mod : CLI uses Go 1.26+. Dependencies: Cobra, charmbracelet/huh, charmbracelet/lipgloss, sigstore-go, go-containerregistry, go-tuf

Applied to files:

  • docs/design/operations.md
  • cli/cmd/global_opts.go
  • cli/cmd/exitcodes.go
  • cli/cmd/envvars.go
  • CLAUDE.md
  • cli/internal/ui/ui.go
📚 Learning: 2026-03-21T14:12:17.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T14:12:17.848Z
Learning: CLI workflow (`cli.yml`): Go lint/test/build (cross-compile) + govulncheck + fuzz. GoReleaser release on `v*` tags with cosign signing + SLSA provenance

Applied to files:

  • docs/design/operations.md
  • CLAUDE.md
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to cli/go.mod : Go CLI dependencies: Go 1.26+, Cobra (commands), charmbracelet/huh (interactive CLI), charmbracelet/lipgloss (styled output).

Applied to files:

  • docs/design/operations.md
  • cli/cmd/config.go
  • cli/cmd/global_opts.go
  • cli/cmd/exitcodes.go
  • cli/cmd/envvars.go
  • CLAUDE.md
  • cli/internal/ui/ui.go
📚 Learning: 2026-03-27T17:07:16.441Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T17:07:16.441Z
Learning: Applies to cli/**/*_fuzz_test.go : Go CLI uses native 'testing.F' fuzz functions (FuzzYamlStr, etc.)

Applied to files:

  • cli/cmd/exitcodes_test.go
  • cli/cmd/global_opts_test.go
  • cli/internal/ui/ui_test.go
  • cli/cmd/global_opts.go
  • CLAUDE.md
📚 Learning: 2026-03-16T19:52:03.656Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T19:52:03.656Z
Learning: Applies to cli/**/*.go : Lint CLI Go code with golangci-lint and go vet; test with go test -race; check vulnerabilities with govulncheck

Applied to files:

  • cli/cmd/exitcodes_test.go
  • cli/cmd/global_opts_test.go
  • cli/cmd/envvars.go
  • CLAUDE.md
📚 Learning: 2026-03-19T11:19:40.044Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:19:40.044Z
Learning: Applies to cli/**/*.go : Use native Go testing with `testing.F` fuzz functions (`Fuzz*`) for fuzz testing.

Applied to files:

  • cli/cmd/global_opts_test.go
  • CLAUDE.md
📚 Learning: 2026-03-19T11:30:29.217Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:30:29.217Z
Learning: Applies to cli/**/*.go : Use native `testing.F` fuzz functions (`Fuzz*`) for fuzz testing Go code

Applied to files:

  • cli/cmd/global_opts_test.go
  • CLAUDE.md
📚 Learning: 2026-03-21T14:12:17.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T14:12:17.848Z
Learning: Applies to cli/**/*.go : Use native `testing.F` fuzz functions (`Fuzz*`) for fuzzing

Applied to files:

  • cli/cmd/global_opts_test.go
  • CLAUDE.md
📚 Learning: 2026-03-21T12:54:22.557Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Applies to cli/**/*.go : Use native Go `testing.F` fuzz functions (`Fuzz*`) for property-based testing

Applied to files:

  • cli/cmd/global_opts_test.go
  • CLAUDE.md
📚 Learning: 2026-03-19T11:30:29.217Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:30:29.217Z
Learning: Applies to cli/**/*.go : Run Go lint via `golangci-lint run`, vet via `go vet`, tests via `go test ./...`, and fuzz via `go test -fuzz=FuzzTarget -fuzztime=30s`

Applied to files:

  • cli/cmd/global_opts_test.go
  • CLAUDE.md
📚 Learning: 2026-03-27T17:07:16.441Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T17:07:16.441Z
Learning: Applies to cli/**/*.go : Go CLI: golangci-lint + go vet checked in pre-commit hooks and CI (conditional on cli/**/*.go changes)

Applied to files:

  • cli/cmd/global_opts_test.go
  • cli/cmd/global_opts.go
  • cli/cmd/envvars.go
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/cli.yml : CLI workflow: Go lint (golangci-lint + go vet) + test (-race -coverprofile) + build (cross-compile: linux/darwin/windows × amd64/arm64) + govulncheck + fuzz testing (main-only, 30s/target, continue-on-error, matrix over 4 packages). cli-pass gate includes fuzz as informational. GoReleaser release on v* tags. Cosign keyless signing of checksums.txt. SLSA L3 provenance attestations. Sigstore bundle (.sigstore.json) attached. Post-release appends checksums/verification/provenance to draft release notes.

Applied to files:

  • cli/cmd/global_opts.go
  • CLAUDE.md
📚 Learning: 2026-03-19T11:19:40.044Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:19:40.044Z
Learning: CLI workflow (`.github/workflows/cli.yml`) runs Go lint (golangci-lint + go vet) + test (race, coverage) + build (cross-compile matrix) + vulnerability check (govulncheck) + fuzz testing. Cross-compiles for linux/darwin/windows × amd64/arm64. GoReleaser release on v* tags with cosign keyless signing and SLSA L3 attestations.

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-21T12:54:22.557Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Applies to cli/**/*.go : Run Go lint via `golangci-lint run`, vet via `go vet ./...`, tests via `go test ./...` from the `cli/` directory (Go module root)

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...

Applied to files:

  • CLAUDE.md
🔇 Additional comments (27)
cli/cmd/envvars.go (1)

41-52: Standard color-disable env handling is well implemented.

The NO_COLOR / CLICOLOR=0 / TERM=dumb checks are clear and match expected CLI behavior.

cli/cmd/doctor.go (2)

30-31: Global UI option wiring looks correct.

Using GetGlobalOpts + NewUIWithOptions keeps doctor output aligned with root-level flags.


75-76: Hint categorization update is good.

Switching to HintNextStep improves guidance semantics without changing flow.

cli/cmd/version.go (1)

13-14: Nice alignment with global CLI output modes.

version now respects shared output flags via NewUIWithOptions.

cli/internal/ui/spinner.go (1)

31-45: Spinner mode gating is solid.

The quiet/plain/non-TTY handling and clearLine guard are consistent and prevent noisy terminal control output.

Also applies to: 76-76

cli/cmd/init.go (2)

37-38: Good: both stdout and stderr UIs now honor global options.

This keeps init flow output consistent across modes.

Also applies to: 62-62


99-99: HintNextStep usage is appropriate here.

The post-init guidance now uses the categorized hint path as intended.

cli/cmd/config.go (2)

78-79: Global UI option adoption in config commands looks good.

Both show/set paths now correctly inherit root output behavior.

Also applies to: 178-179


90-90: Good migration to HintNextStep.

This keeps onboarding guidance consistent with the new hint taxonomy.

cli/cmd/doctor_report.go (2)

31-32: UI initialization via global opts is correct.

doctor report now properly respects global output flags.


62-63: Step-oriented hint text migration is clean.

Using HintNextStep here improves consistency with the new guidance model.

cli/cmd/stop.go (1)

43-44: Global output mode wiring in stop is good.

This keeps spinner and status rendering behavior consistent with root flags.

cli/cmd/update_health.go (1)

28-29: LGTM!

The migration to GetGlobalOpts and NewUIWithOptions is correct and consistent with the PR-wide pattern. The UI instance now respects global configuration for quiet/plain/json modes.

docs/design/operations.md (1)

1078-1081: LGTM!

The documentation accurately reflects the new global output modes and typed exit codes introduced in this PR. The exit code semantics (0=success, 1=runtime, 2=usage, 3=unhealthy, 4=unreachable, 10=update-available) are clearly documented and align with the implementation in cli/cmd/exitcodes.go.

cli/cmd/update_cleanup.go (2)

37-38: LGTM!

The migration to GetGlobalOpts and NewUIWithOptions follows the PR-wide pattern correctly.


108-109: LGTM!

The migration to global options is correct, and HintTip is the appropriate hint category for this passive suggestion about disk space cleanup.

Also applies to: 124-125

cli/cmd/uninstall.go (3)

39-41: LGTM!

Creating separate UI instances for stdout (out) and stderr (errUI) with consistent global options is the correct approach for proper output stream separation.


304-308: LGTM!

The migration is correct, and HintNextStep is the appropriate hint category for actionable instructions (manual binary removal).


378-382: LGTM!

Consistent with the pattern used elsewhere in this file. HintNextStep correctly indicates an action the user should take.

cli/cmd/status.go (1)

41-43: LGTM!

The local --json flag removal and migration to global opts.JSON is correct. This promotes consistency across all commands and simplifies the flag management.

cli/main.go (1)

12-22: LGTM!

The error handling cascade is well-structured:

  1. ChildExitCode for re-exec'd binary failures (preserves child's exit code)
  2. ExitError for typed exit codes from commands
  3. Default os.Exit(1) as fallback

Using errors.As is the idiomatic Go approach for unwrapping typed errors.

cli/cmd/cleanup.go (4)

39-40: LGTM!

The migration to GetGlobalOpts and NewUIWithOptions is correct.


66-66: LGTM!

HintTip is the correct category for an optional configuration suggestion.


92-92: LGTM!

HintNextStep is appropriate for providing actionable guidance in non-interactive mode.


138-138: LGTM!

HintError is the correct category for explaining why some images couldn't be removed (error-related context).

cli/cmd/completion_install.go (2)

28-29: LGTM!

The migration to GetGlobalOpts and NewUIWithOptions is correct.


49-49: LGTM!

HintNextStep is the appropriate category for this actionable instruction to restart the shell.

Comment on lines +35 to +46
// ShouldPrompt reports whether the CLI should show interactive prompts.
// Returns false when --yes is active or stdin is not a terminal.
func (g *GlobalOpts) ShouldPrompt() bool {
if g.Yes {
return false
}
fi, err := os.Stdin.Stat()
if err != nil {
return false
}
return fi.Mode()&os.ModeCharDevice != 0
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

The new prompt helper is still disconnected from real confirmations.

GlobalOpts.Yes is only exercised here and in tests. The update confirmation paths still gate on isInteractive() directly, so synthorg update --yes will continue to prompt on a terminal. Please thread the resolved global opts into those confirm paths before relying on this flag.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/cmd/global_opts.go` around lines 35 - 46, The confirmation logic
currently ignores GlobalOpts.Yes because callers still call isInteractive()
directly; update the confirm code paths to consume the resolved global option
instead: thread GlobalOpts (or at least its ShouldPrompt() result) into the
confirmation helpers used by the synthorg update flow and replace direct calls
to isInteractive() with g.ShouldPrompt() (or use the passed-in bool) in those
helpers (look for functions named isInteractive, confirmUpdate/confirmAction or
the update command handler that performs the prompt). Ensure the synthorg update
command's run/handler signature accepts the GlobalOpts (or a prompt boolean) and
forward it to the confirmation helper so --yes disables prompts; update
associated tests to pass the GlobalOpts or bool instead of relying on
isInteractive().

cli/cmd/root.go Outdated
Comment on lines +61 to +66
// Validate mutual exclusivity.
if flagQuiet && flagVerbose > 0 {
return fmt.Errorf("--quiet and --verbose are mutually exclusive")
}
if flagPlain && flagJSON {
return fmt.Errorf("--plain and --json are mutually exclusive")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Resolve the effective flag values before validating them.

These branches use the bool value itself to mean “flag not passed” and perform the conflict check before env resolution. That breaks both precedence and mutual exclusion: SYNTHORG_QUIET=1 synthorg --quiet=false ... still resolves to quiet, and SYNTHORG_QUIET=1 synthorg -v ... slips past the conflict check because the env override is applied later. Compute the effective values first from explicit-flag presence + env, then validate those effective opts and return ExitUsage for invalid combinations.

Also applies to: 69-91

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/cmd/root.go` around lines 61 - 66, Compute the effective option values
before running the mutual-exclusion checks: use the explicit-flag presence
indicators together with environment-derived overrides to produce resolvedQuiet,
resolvedVerbose (or an integer), resolvedPlain, and resolvedJSON, then validate
those resolved variables rather than the raw flag booleans (e.g. replace checks
of flagQuiet/flagVerbose/flagPlain/flagJSON with
resolvedQuiet/resolvedVerbose/resolvedPlain/resolvedJSON). If a conflict is
detected return ExitUsage with a clear error (e.g. "--quiet and --verbose are
mutually exclusive" or "--plain and --json are mutually exclusive"). Apply this
same pattern for the other validation blocks referenced in the comment (lines
~69-91) so all mutual-exclusion checks use the resolved values.

…on, resolveDataDir consolidation

- Use GlobalOpts.SkipVerify instead of raw flagSkipVerify in start.go and
  update.go so SYNTHORG_NO_VERIFY/SYNTHORG_SKIP_VERIFY env vars take effect
- Migrate isInteractive() to ShouldPrompt() in confirmUpdate,
  confirmUpdateWithDefault, restartIfRunning, promptHealthRecover, and
  confirmAndCleanup so --yes/SYNTHORG_YES flag is respected
- Allow --yes to bypass TTY requirement for destructive commands (wipe,
  uninstall) per design spec: "require --yes or interactive"
- Replace redundant resolveDataDir() calls with opts.DataDir from GlobalOpts
  across all 15 command files for single-source-of-truth resolution
- Add filepath.Abs normalization in resolveDataDir for env-sourced paths
- Sanitize execPath/dataDir against double-quote injection in Windows
  .bat cleanup script (uninstall.go)
- Add PersistentPreRunE chaining warning comment on root command
- Document validHintsMode as defensive/forward-looking dead code
- Clarify envBool behavior, NewExitError nil intent, isInteractive usage
- Fix operations.md env var claim accuracy

Addresses 11 findings from go-reviewer, go-conventions, go-security,
docs-consistency agents and gemini-code-assist.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 27, 2026 20:36 — with GitHub Actions Inactive
…sion, livebox quiet, tip dedup

- Return ExitUsage (code 2) for missing --confirm in backup restore
- Trim whitespace in envBool before parsing boolean env values
- Move mutual exclusion checks (--quiet vs --verbose, --plain vs --json)
  after env var resolution so SYNTHORG_QUIET=1 + --verbose is caught
- Fix LiveBox quiet/JSON mode leaking human output to stdout by
  suppressing Error/Success in the immediate-print path when quiet
- Make HintTip dedup store package-level (sessionTipsSeen) so all UI
  instances share session-level deduplication

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 27, 2026 20:41 — with GitHub Actions Inactive
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (3)
cli/cmd/root.go (1)

65-71: ⚠️ Potential issue | 🟠 Major

Mutual exclusivity checks use raw flags, not resolved effective values.

The validation runs before environment variable resolution (lines 77-95). This means combinations like SYNTHORG_QUIET=1 synthorg -v or SYNTHORG_JSON=1 synthorg --plain bypass the conflict check because the env-derived value hasn't been applied yet.

Move the conflict checks after computing effective values, or compute effective values first then validate:

🔧 Suggested fix
 func setupGlobalOpts(cmd *cobra.Command) error {
-	// Validate mutual exclusivity.
-	if flagQuiet && flagVerbose > 0 {
-		return fmt.Errorf("--quiet and --verbose are mutually exclusive")
-	}
-	if flagPlain && flagJSON {
-		return fmt.Errorf("--plain and --json are mutually exclusive")
-	}
-
 	// Resolve env var overrides for flags that were NOT explicitly passed.
 	// ...
 	noColor := flagNoColor
 	if !flagNoColor && noColorFromEnv() {
 		noColor = true
 	}

 	quiet := flagQuiet
 	if !flagQuiet && envBool(EnvQuiet) {
 		quiet = true
 	}

+	verbose := flagVerbose
+	// Note: no env override for verbose count currently
+
+	plain := flagPlain
+	// Note: no env override for plain currently
+
+	jsonMode := flagJSON
+	// Note: no env override for json currently
+
+	// Validate mutual exclusivity after resolving effective values.
+	if quiet && verbose > 0 {
+		return fmt.Errorf("--quiet and --verbose are mutually exclusive")
+	}
+	if plain && jsonMode {
+		return fmt.Errorf("--plain and --json are mutually exclusive")
+	}
+
 	yes := flagYes
 	// ...
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/cmd/root.go` around lines 65 - 71, The mutual-exclusivity validation
currently checks raw flags (flagQuiet, flagVerbose, flagPlain, flagJSON) before
environment-based resolution, so env-derived values can bypass the checks; fix
by computing the effective values first (the resolved variables set after the
env resolution logic) or move the conflict checks to after the code that
resolves environment variables, then validate using the effective values (e.g.,
use the resolvedQuiet/resolvedVerbose or the same variables updated after env
resolution) for the checks involving flagQuiet/flagVerbose and
flagPlain/flagJSON.
cli/cmd/wipe.go (1)

67-70: ⚠️ Potential issue | 🟠 Major

Standard migration applied, but --yes still doesn't fully bypass prompts.

The check now allows --yes to bypass the initial interactive gate (good), but the subsequent huh prompts in confirmWipe, promptForBackup, promptSavePath, etc. still require interactive input. Running synthorg wipe --yes in a non-interactive environment will pass line 68 but then fail at the first prompt.

For --yes to work in automation, each prompt method needs to check opts.Yes and return the affirmative/default value without showing the huh form.

🔧 Conceptual fix pattern for prompts
 func (wc *wipeContext) confirmWipe() (bool, error) {
+	if GetGlobalOpts(wc.ctx).Yes {
+		return true, nil // auto-confirm in non-interactive mode
+	}
 	var confirmed bool
 	err := wc.runForm(huh.NewForm(huh.NewGroup(
 		// ...

Apply similar pattern to promptForBackup, promptStartForBackup, promptSavePath, checkOverwrite, askContinueWithoutBackup, and promptStartAfterWipe.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/cmd/wipe.go` around lines 67 - 70, The various interactive prompt helpers
(promptForBackup, promptStartForBackup, promptSavePath, checkOverwrite,
askContinueWithoutBackup, promptStartAfterWipe) still display "huh" prompts even
when the global opts.Yes is set; update each function to read opts :=
GetGlobalOpts(ctx) (or accept opts) and if opts.Yes return the
affirmative/default response immediately (e.g., treat as confirmed/keep default
path/overwrite allowed) without invoking the interactive prompt or huh form, so
that --yes fully bypasses all prompts during non-interactive runs while
preserving existing behavior when opts.Yes is false.
cli/cmd/backup.go (1)

400-403: ⚠️ Potential issue | 🟠 Major

Missing --confirm should return the usage exit code.

This is a bad invocation (user error), but Line 403 returns a plain error, so main.go falls back to exit code 1 instead of the documented usage code 2. Wrap this path in NewExitError(ExitUsage, ...) so scripts can distinguish user mistakes from runtime failures.

🔧 Suggested fix
 	if !confirm {
 		errOut.Error("Restore requires the --confirm flag as a safety gate")
 		errOut.HintNextStep(fmt.Sprintf("Run 'synthorg backup restore %s --confirm' to proceed", backupID))
-		return errors.New("--confirm flag is required")
+		return NewExitError(ExitUsage, "--confirm flag is required")
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/cmd/backup.go` around lines 400 - 403, The missing --confirm branch
currently returns a plain error; change it to return a usage exit error so
callers get exit code 2. Keep the errOut.Error and errOut.HintNextStep calls,
but replace the final return of errors.New("--confirm flag is required") with
return NewExitError(ExitUsage, errors.New("--confirm flag is required")) (or
NewExitError(ExitUsage, "--confirm flag is required") if that overload exists).
This change affects the restore path where the confirm boolean is checked (look
for the confirm variable and the errOut.Error / errOut.HintNextStep calls) and
uses the NewExitError and ExitUsage symbols to signal a user-usage error.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cli/cmd/global_opts_test.go`:
- Around line 89-99: The test TestShouldPromptDefault currently doesn't assert a
deterministic outcome; update it to inspect the current stdin state and assert
accordingly: call os.Stdin.Stat() or use a terminal check (e.g.,
term.IsTerminal) to determine whether stdin is a TTY, then assert
GlobalOpts{}.ShouldPrompt() == true when stdin is a TTY and == false when it is
not. Modify TestShouldPromptDefault to branch on that runtime check and use
t.Fatalf/t.Errorf to fail when the observed value of ShouldPrompt() doesn't
match the detected stdin terminal state.

In `@cli/cmd/update.go`:
- Around line 392-393: The code calls GetGlobalOpts(cmd.Context()) and
constructs the UI with ui.NewUIWithOptions(...), but it should use the
function's ctx parameter for consistency; change the call to GetGlobalOpts to
pass ctx instead of cmd.Context() and ensure any subsequent uses in this block
(e.g., opts := GetGlobalOpts(...), out := ui.NewUIWithOptions(...)) continue to
use the ctx parameter.
- Around line 187-194: The comment is inaccurate: GlobalOpts is available when
resolveUpdateChannel runs, so either update the comment or (preferred) make
resolveUpdateChannel use GlobalOpts for consistency—change resolveUpdateChannel
to accept ctx (e.g., resolveUpdateChannel(ctx context.Context)), replace
resolveDataDir() with GetGlobalOpts(ctx).DataDir inside resolveUpdateChannel,
and update its callers (runUpdate / updateCLI) to pass the current ctx; also
update the comment to reflect that it reads DataDir from GlobalOpts via the
context.

---

Duplicate comments:
In `@cli/cmd/backup.go`:
- Around line 400-403: The missing --confirm branch currently returns a plain
error; change it to return a usage exit error so callers get exit code 2. Keep
the errOut.Error and errOut.HintNextStep calls, but replace the final return of
errors.New("--confirm flag is required") with return NewExitError(ExitUsage,
errors.New("--confirm flag is required")) (or NewExitError(ExitUsage, "--confirm
flag is required") if that overload exists). This change affects the restore
path where the confirm boolean is checked (look for the confirm variable and the
errOut.Error / errOut.HintNextStep calls) and uses the NewExitError and
ExitUsage symbols to signal a user-usage error.

In `@cli/cmd/root.go`:
- Around line 65-71: The mutual-exclusivity validation currently checks raw
flags (flagQuiet, flagVerbose, flagPlain, flagJSON) before environment-based
resolution, so env-derived values can bypass the checks; fix by computing the
effective values first (the resolved variables set after the env resolution
logic) or move the conflict checks to after the code that resolves environment
variables, then validate using the effective values (e.g., use the
resolvedQuiet/resolvedVerbose or the same variables updated after env
resolution) for the checks involving flagQuiet/flagVerbose and
flagPlain/flagJSON.

In `@cli/cmd/wipe.go`:
- Around line 67-70: The various interactive prompt helpers (promptForBackup,
promptStartForBackup, promptSavePath, checkOverwrite, askContinueWithoutBackup,
promptStartAfterWipe) still display "huh" prompts even when the global opts.Yes
is set; update each function to read opts := GetGlobalOpts(ctx) (or accept opts)
and if opts.Yes return the affirmative/default response immediately (e.g., treat
as confirmed/keep default path/overwrite allowed) without invoking the
interactive prompt or huh form, so that --yes fully bypasses all prompts during
non-interactive runs while preserving existing behavior when opts.Yes is false.
🪄 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: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 07fe8ff3-be25-4f0f-97ea-75e417a22ccc

📥 Commits

Reviewing files that changed from the base of the PR and between def9787 and 69661f4.

📒 Files selected for processing (19)
  • cli/cmd/backup.go
  • cli/cmd/cleanup.go
  • cli/cmd/config.go
  • cli/cmd/doctor.go
  • cli/cmd/doctor_report.go
  • cli/cmd/envvars.go
  • cli/cmd/exitcodes.go
  • cli/cmd/global_opts_test.go
  • cli/cmd/logs.go
  • cli/cmd/root.go
  • cli/cmd/start.go
  • cli/cmd/status.go
  • cli/cmd/stop.go
  • cli/cmd/uninstall.go
  • cli/cmd/update.go
  • cli/cmd/update_compose.go
  • cli/cmd/update_health.go
  • cli/cmd/wipe.go
  • docs/design/operations.md
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: CLI Test (windows-latest)
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (2)
cli/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Go CLI: golangci-lint + go vet checked in pre-commit hooks and CI (conditional on cli/**/*.go changes)

Files:

  • cli/cmd/update_compose.go
  • cli/cmd/status.go
  • cli/cmd/logs.go
  • cli/cmd/update_health.go
  • cli/cmd/doctor_report.go
  • cli/cmd/stop.go
  • cli/cmd/doctor.go
  • cli/cmd/cleanup.go
  • cli/cmd/start.go
  • cli/cmd/root.go
  • cli/cmd/backup.go
  • cli/cmd/global_opts_test.go
  • cli/cmd/uninstall.go
  • cli/cmd/wipe.go
  • cli/cmd/config.go
  • cli/cmd/envvars.go
  • cli/cmd/exitcodes.go
  • cli/cmd/update.go
docs/design/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Update the relevant docs/design/ page to reflect approved deviations from the spec

Files:

  • docs/design/operations.md
🧠 Learnings (18)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Applies to cli/**/*.go : Use Cobra for CLI command structure, charmbracelet/huh for interactive prompts, and charmbracelet/lipgloss for terminal styling
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/cli.yml : CLI workflow: Go lint (golangci-lint + go vet) + test (-race -coverprofile) + build (cross-compile: linux/darwin/windows × amd64/arm64) + govulncheck + fuzz testing (main-only, 30s/target, continue-on-error, matrix over 4 packages). cli-pass gate includes fuzz as informational. GoReleaser release on v* tags. Cosign keyless signing of checksums.txt. SLSA L3 provenance attestations. Sigstore bundle (.sigstore.json) attached. Post-release appends checksums/verification/provenance to draft release notes.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to cli/** : CLI: Go 1.26+, dependencies in cli/go.mod (Cobra, charmbracelet/huh).
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to cli/**/*.go : Go CLI (Go 1.26+) uses Cobra for commands, charmbracelet/huh for interactive CLI, charmbracelet/lipgloss for styled output. Cross-platform builds (linux/darwin/windows × amd64/arm64). GoReleaser for releases with cosign keyless signing of checksums.txt. SLSA L3 provenance attestations via actions/attest-build-provenance.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Go 1.26+ required; CLI dependencies in `cli/go.mod` (Cobra, charmbracelet/huh, charmbracelet/lipgloss, sigstore-go, go-containerregistry, go-tuf)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to cli/go.mod : Go CLI dependencies: Go 1.26+, Cobra (commands), charmbracelet/huh (interactive CLI), charmbracelet/lipgloss (styled output).
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to cli/** : CLI: Go 1.26+, dependencies in cli/go.mod (Cobra, charmbracelet/huh).

Applied to files:

  • cli/cmd/update_compose.go
  • docs/design/operations.md
  • cli/cmd/cleanup.go
  • cli/cmd/start.go
  • cli/cmd/root.go
  • cli/cmd/backup.go
  • cli/cmd/global_opts_test.go
  • cli/cmd/uninstall.go
  • cli/cmd/config.go
  • cli/cmd/envvars.go
  • cli/cmd/exitcodes.go
  • cli/cmd/update.go
📚 Learning: 2026-03-21T14:12:17.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T14:12:17.848Z
Learning: Applies to cli/go.mod : CLI uses Go 1.26+. Dependencies: Cobra, charmbracelet/huh, charmbracelet/lipgloss, sigstore-go, go-containerregistry, go-tuf

Applied to files:

  • cli/cmd/update_compose.go
  • docs/design/operations.md
  • cli/cmd/envvars.go
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to cli/**/*.go : Go CLI (Go 1.26+) uses Cobra for commands, charmbracelet/huh for interactive CLI, charmbracelet/lipgloss for styled output. Cross-platform builds (linux/darwin/windows × amd64/arm64). GoReleaser for releases with cosign keyless signing of checksums.txt. SLSA L3 provenance attestations via actions/attest-build-provenance.

Applied to files:

  • docs/design/operations.md
  • cli/cmd/start.go
  • cli/cmd/envvars.go
📚 Learning: 2026-03-21T12:54:22.557Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Go 1.26+ required; CLI dependencies in `cli/go.mod` (Cobra, charmbracelet/huh, charmbracelet/lipgloss, sigstore-go, go-containerregistry, go-tuf)

Applied to files:

  • docs/design/operations.md
  • cli/cmd/start.go
  • cli/cmd/config.go
  • cli/cmd/envvars.go
📚 Learning: 2026-03-19T11:19:40.044Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:19:40.044Z
Learning: Applies to go.mod : Maintain Go 1.26+ requirement. Dependencies: Cobra (CLI framework), charmbracelet/huh and charmbracelet/lipgloss (UI), sigstore-go (code signing), go-containerregistry (container image verification), go-tuf (TUF client for Sigstore).

Applied to files:

  • docs/design/operations.md
📚 Learning: 2026-03-21T14:12:17.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T14:12:17.848Z
Learning: CLI workflow (`cli.yml`): Go lint/test/build (cross-compile) + govulncheck + fuzz. GoReleaser release on `v*` tags with cosign signing + SLSA provenance

Applied to files:

  • docs/design/operations.md
📚 Learning: 2026-03-21T12:54:22.557Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Applies to cli/**/*.go : Use Cobra for CLI command structure, charmbracelet/huh for interactive prompts, and charmbracelet/lipgloss for terminal styling

Applied to files:

  • docs/design/operations.md
  • cli/cmd/root.go
  • cli/cmd/envvars.go
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to cli/go.mod : Go CLI dependencies: Go 1.26+, Cobra (commands), charmbracelet/huh (interactive CLI), charmbracelet/lipgloss (styled output).

Applied to files:

  • docs/design/operations.md
  • cli/cmd/envvars.go
📚 Learning: 2026-03-27T17:07:16.441Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T17:07:16.441Z
Learning: Applies to cli/**/*.go : Go CLI: golangci-lint + go vet checked in pre-commit hooks and CI (conditional on cli/**/*.go changes)

Applied to files:

  • cli/cmd/start.go
  • cli/cmd/global_opts_test.go
  • cli/cmd/envvars.go
📚 Learning: 2026-03-27T17:07:16.441Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T17:07:16.441Z
Learning: Applies to cli/**/*_fuzz_test.go : Go CLI uses native 'testing.F' fuzz functions (FuzzYamlStr, etc.)

Applied to files:

  • cli/cmd/global_opts_test.go
📚 Learning: 2026-03-19T11:19:40.044Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:19:40.044Z
Learning: Applies to cli/**/*.go : Use native Go testing with `testing.F` fuzz functions (`Fuzz*`) for fuzz testing.

Applied to files:

  • cli/cmd/global_opts_test.go
📚 Learning: 2026-03-21T14:12:17.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T14:12:17.848Z
Learning: Applies to cli/**/*.go : Use native `testing.F` fuzz functions (`Fuzz*`) for fuzzing

Applied to files:

  • cli/cmd/global_opts_test.go
📚 Learning: 2026-03-21T12:54:22.557Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Applies to cli/**/*.go : Use native Go `testing.F` fuzz functions (`Fuzz*`) for property-based testing

Applied to files:

  • cli/cmd/global_opts_test.go
📚 Learning: 2026-03-19T11:30:29.217Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:30:29.217Z
Learning: Applies to cli/**/*.go : Use native `testing.F` fuzz functions (`Fuzz*`) for fuzz testing Go code

Applied to files:

  • cli/cmd/global_opts_test.go
📚 Learning: 2026-03-19T11:30:29.217Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:30:29.217Z
Learning: Applies to cli/**/*.go : Run Go lint via `golangci-lint run`, vet via `go vet`, tests via `go test ./...`, and fuzz via `go test -fuzz=FuzzTarget -fuzztime=30s`

Applied to files:

  • cli/cmd/global_opts_test.go
📚 Learning: 2026-03-16T19:52:03.656Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T19:52:03.656Z
Learning: Applies to cli/**/*.go : Lint CLI Go code with golangci-lint and go vet; test with go test -race; check vulnerabilities with govulncheck

Applied to files:

  • cli/cmd/global_opts_test.go
📚 Learning: 2026-03-19T11:19:40.044Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:19:40.044Z
Learning: Applies to cli/**/*.go : Lint Go code with `golangci-lint` and `go vet`. Run tests with `-race` flag to detect race conditions.

Applied to files:

  • cli/cmd/global_opts_test.go
🔇 Additional comments (52)
cli/cmd/global_opts_test.go (1)

1-22: LGTM — solid test coverage for GlobalOpts defaults and context plumbing.

The tests verify that GetGlobalOpts never returns nil, defaults are correct (Hints == "auto", booleans false), and the set/get round-trip preserves pointer identity. Good use of t.Parallel().

cli/cmd/exitcodes.go (1)

1-63: LGTM — well-designed exit code system with proper error wrapping.

The exit codes follow Unix conventions (0=success, 1=general error, 2=usage). ExitError correctly implements Unwrap() for errors.Is/As chains, and ChildExitError cleanly separates re-exec exit code propagation. The gap between codes 4 and 10 allows future expansion.

cli/cmd/update_compose.go (1)

139-139: LGTM — context propagation for global options access.

Passing cmd.Context() to confirmUpdate enables it to access GlobalOpts for prompt gating via ShouldPrompt(), consistent with the PR's migration pattern.

cli/cmd/logs.go (1)

42-42: LGTM — migrated to global options for data directory resolution.

Using GetGlobalOpts(ctx).DataDir centralizes data directory handling and ensures --data-dir flag and SYNTHORG_DATA_DIR env var are respected consistently.

docs/design/operations.md (1)

1078-1081: LGTM — documentation accurately reflects the new CLI global options and exit codes.

The documented flags (--quiet, --verbose/-v, --plain, --json, --no-color, --yes) and exit codes (0, 1, 2, 3, 4, 10) align with the implementation in exitcodes.go and the PR objectives.

cli/cmd/update_health.go (2)

28-29: LGTM — UI initialization migrated to global options pattern.

Using GetGlobalOpts(cmd.Context()) and opts.UIOptions() ensures health check output respects --quiet, --plain, --json, and --no-color flags.


90-94: LGTM — prompt gating migrated to ShouldPrompt().

Replacing isInteractive() with GetGlobalOpts(cmd.Context()).ShouldPrompt() ensures --yes flag and SYNTHORG_YES env var are respected for non-interactive recovery flows.

cli/cmd/doctor_report.go (2)

30-33: LGTM — migrated to global options for UI and config loading.

Consistent with the PR-wide pattern of using GetGlobalOpts(ctx) for data directory and UI options.


61-62: LGTM — hint rendering migrated to categorized HintNextStep.

Using HintNextStep for actionable instructions aligns with the PR's expanded hint system that supports always/auto/never behavior and categorized methods.

cli/cmd/doctor.go (2)

29-32: LGTM — migrated to global options pattern.

Using GetGlobalOpts(ctx) for both UI options and data directory resolution ensures consistent flag/env var handling across all commands.


74-75: LGTM — hint rendering updated to HintNextStep.

Categorized hints (HintNextStep) for actionable commands like doctor report and logs align with the PR's hint system migration.

cli/cmd/status.go (1)

34-41: LGTM!

The migration to GetGlobalOpts for data directory and JSON mode, along with NewUIWithOptions for UI construction, is consistent with the PR's global options infrastructure. The removal of the local --json flag in favor of the global one simplifies the command interface.

cli/cmd/stop.go (1)

27-42: LGTM!

Standard migration to global options for data directory resolution and UI construction. The control flow remains intact.

cli/cmd/config.go (3)

77-89: LGTM!

The migration correctly uses config.SecurePath(opts.DataDir) for direct path access before checking file existence, and HintNextStep appropriately guides the user when config is not found.


135-135: LGTM!

Consistent use of config.SecurePath for direct path resolution in runConfigGet.


175-178: LGTM!

The migration to global options is correct. Using config.Load(opts.DataDir) directly here is consistent with other commands (start, stop, backup) since config.Load handles path security internally.

cli/cmd/cleanup.go (4)

32-38: LGTM!

Standard migration pattern applied correctly.


64-64: LGTM!

HintTip is the appropriate category for this optional suggestion about auto-cleanup configuration.


89-90: LGTM!

Using ShouldPrompt() here is correct—cleanup is not a destructive command requiring separate TTY/--yes evaluation (unlike wipe/uninstall per the context snippet from root.go). The hint appropriately guides users in non-interactive mode.


136-136: LGTM!

HintError is appropriate for error recovery guidance when images couldn't be removed.

cli/cmd/backup.go (5)

301-308: LGTM!

Standard migration pattern for runBackupCreate with both stdout and stderr UI instances.


342-349: LGTM!

Standard migration pattern for runBackupList.


376-376: LGTM!

HintNextStep is appropriate for guiding the user to create a backup.


392-417: LGTM!

The migration for runBackupRestore correctly uses global options for UI and data directory. The HintNextStep calls provide clear guidance.


475-515: LGTM!

The HintNextStep calls in error handlers and post-restore guidance are appropriate for directing user actions.

cli/cmd/start.go (4)

37-56: LGTM!

Standard migration to global options for data directory and UI construction with both stdout and stderr instances.


108-108: LGTM!

HintError is appropriate for error recovery guidance on health check failure.


159-162: LGTM!

The previous review comment has been addressed—verifyAndPinImages now correctly uses GetGlobalOpts(ctx).SkipVerify instead of the raw flag variable, ensuring that SYNTHORG_NO_VERIFY / SYNTHORG_SKIP_VERIFY environment variables are properly respected.


177-177: LGTM!

HintError appropriately guides users on how to handle transport errors in air-gapped environments.

cli/cmd/root.go (7)

16-26: LGTM!

Clear declaration of persistent flag storage variables.


37-44: LGTM!

The warning comment about Cobra's PersistentPreRunE behavior is important documentation—Cobra does not chain these hooks, so subcommands with their own hooks must explicitly call setupGlobalOpts.


47-60: LGTM!

Persistent flags are correctly registered with appropriate types (CountVarP for stackable verbose, BoolVarP for shortcuts).


77-106: LGTM!

Environment variable resolution correctly applies overrides only when flags weren't explicitly set. The GlobalOpts struct is populated with all resolved values.


120-140: LGTM!

resolveDataDir correctly implements the priority chain (flag → env → default) and normalizes to absolute path with symlink resolution for security.


148-158: LGTM!

The updated comment correctly documents when to use isInteractive() vs ShouldPrompt()—the former is for destructive commands (wipe, uninstall) that need separate TTY/--yes evaluation.


187-194: LGTM!

Correctly suppresses stderr printing for ChildExitError since main.go handles exit code propagation and the internal message isn't user-facing.

cli/cmd/wipe.go (6)

74-91: LGTM!

Standard migration to global options for data directory and UI construction.


127-165: LGTM!

HintNextStep calls provide appropriate guidance for post-wipe actions, browser opening, and manual start instructions.


333-354: LGTM!

HintNextStep calls for cancellation messaging are appropriate.


379-436: LGTM!

HintNextStep calls in path prompts and overwrite handling provide clear user guidance.


457-457: LGTM!

HintError is appropriate here—the backup exists in the container but will be destroyed, which is an error-recovery situation requiring user awareness.


577-583: LGTM!

HintNextStep for cancellation messaging is consistent with other prompt handlers.

cli/cmd/envvars.go (3)

30-40: Trim whitespace before parsing boolean env values.

The envBool function does not handle leading/trailing whitespace in environment variable values. Values like " true " or "yes " will silently fall back to false, which can cause unexpected behavior when users accidentally include whitespace.

Suggested fix
 func envBool(name string) bool {
-	v := os.Getenv(name)
+	v := strings.TrimSpace(os.Getenv(name))
 	if v == "" {
 		return false
 	}
 	switch strings.ToLower(v) {
 	case "1", "true", "yes":
 		return true
 	}
 	return false
 }

10-24: Well-structured environment variable constants.

The constants are clearly documented with the precedence comment and include a backward-compatibility alias for EnvSkipVerify. The naming convention follows the SYNTHORG_* prefix consistently.


44-55: Correct implementation of the NO_COLOR spec and related standards.

The function properly handles:

  • NO_COLOR (any non-empty value per https://no-color.org/)
  • CLICOLOR=0 (the de-facto standard)
  • TERM=dumb (common in non-interactive environments)
cli/cmd/uninstall.go (3)

34-39: Correct guard for destructive operations in non-interactive mode.

The condition !isInteractive() && !opts.Yes correctly requires the --yes flag when stdin is not a TTY. This is intentionally stricter than ShouldPrompt() (which auto-accepts in non-interactive mode) — appropriate for a destructive uninstall operation that should fail-safe rather than auto-proceed.


323-327: Good security hardening against command injection.

Validating that paths don't contain double-quote characters before embedding them in the .bat script prevents potential command injection. The fallback to manual cleanup is the correct mitigation.


240-241: LGTM — consistent migration to global opts pattern.

The UI construction now correctly uses GetGlobalOpts(cmd.Context()) and opts.UIOptions() throughout the file (removeDataDir, removeUnixBinary, scheduleWindowsCleanup, fallbackManualCleanup).

cli/cmd/update.go (4)

49-52: Consistent use of GlobalOpts for config loading.

Using GetGlobalOpts(cmd.Context()).DataDir aligns with the PR-wide migration and ensures the resolved data directory respects flag/env/config precedence.


226-250: Comprehensive forwarding of global flags in re-exec.

All relevant global flags are now reconstructed and forwarded to the re-launched binary: --data-dir, --skip-verify, --quiet, -v (repeated per verbosity level), --no-color, --plain, --json, and --yes. This ensures consistent behavior across the re-exec boundary.


366-384: Correct use of ShouldPrompt for non-destructive confirmations.

Using ShouldPrompt() here is appropriate — unlike the destructive uninstall command, update confirmations can safely auto-accept their defaults in non-interactive mode.


503-506: Appropriate behavior: skip restart in non-interactive mode.

Unlike uninstall (which errors out), skipping the restart prompt and printing guidance is the right choice here — failing to restart is recoverable, and the user is informed how to proceed manually.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cli/internal/ui/ui.go`:
- Around line 182-194: The Success, Warn and Error methods (UI.Success, UI.Warn,
UI.Error) currently always call u.printLine and can leak human text while
u.jsonMode is enabled; update each method to return early when u.jsonMode is
true (i.e. if u.jsonMode { return }) so JSON mode suppresses human output, while
keeping existing behavior otherwise; reference these three methods and ensure
the logic mirrors the Options/jsonMode contract in the Options comment.
🪄 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: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8dec177c-fdde-4d0c-8aed-7c6b75439444

📥 Commits

Reviewing files that changed from the base of the PR and between 69661f4 and 9a73061.

📒 Files selected for processing (5)
  • cli/cmd/backup.go
  • cli/cmd/envvars.go
  • cli/cmd/root.go
  • cli/internal/ui/livebox.go
  • cli/internal/ui/ui.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
cli/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Go CLI: golangci-lint + go vet checked in pre-commit hooks and CI (conditional on cli/**/*.go changes)

Files:

  • cli/cmd/backup.go
  • cli/internal/ui/livebox.go
  • cli/cmd/envvars.go
  • cli/cmd/root.go
  • cli/internal/ui/ui.go
🧠 Learnings (8)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Applies to cli/**/*.go : Use Cobra for CLI command structure, charmbracelet/huh for interactive prompts, and charmbracelet/lipgloss for terminal styling
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/cli.yml : CLI workflow: Go lint (golangci-lint + go vet) + test (-race -coverprofile) + build (cross-compile: linux/darwin/windows × amd64/arm64) + govulncheck + fuzz testing (main-only, 30s/target, continue-on-error, matrix over 4 packages). cli-pass gate includes fuzz as informational. GoReleaser release on v* tags. Cosign keyless signing of checksums.txt. SLSA L3 provenance attestations. Sigstore bundle (.sigstore.json) attached. Post-release appends checksums/verification/provenance to draft release notes.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Go 1.26+ required; CLI dependencies in `cli/go.mod` (Cobra, charmbracelet/huh, charmbracelet/lipgloss, sigstore-go, go-containerregistry, go-tuf)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to cli/** : CLI: Go 1.26+, dependencies in cli/go.mod (Cobra, charmbracelet/huh).
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to cli/**/*.go : Go CLI (Go 1.26+) uses Cobra for commands, charmbracelet/huh for interactive CLI, charmbracelet/lipgloss for styled output. Cross-platform builds (linux/darwin/windows × amd64/arm64). GoReleaser for releases with cosign keyless signing of checksums.txt. SLSA L3 provenance attestations via actions/attest-build-provenance.
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to cli/** : CLI: Go 1.26+, dependencies in cli/go.mod (Cobra, charmbracelet/huh).

Applied to files:

  • cli/cmd/envvars.go
  • cli/internal/ui/ui.go
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to cli/go.mod : Go CLI dependencies: Go 1.26+, Cobra (commands), charmbracelet/huh (interactive CLI), charmbracelet/lipgloss (styled output).

Applied to files:

  • cli/cmd/envvars.go
  • cli/internal/ui/ui.go
📚 Learning: 2026-03-21T12:54:22.557Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Applies to cli/**/*.go : Use Cobra for CLI command structure, charmbracelet/huh for interactive prompts, and charmbracelet/lipgloss for terminal styling

Applied to files:

  • cli/cmd/envvars.go
  • cli/cmd/root.go
  • cli/internal/ui/ui.go
📚 Learning: 2026-03-27T17:07:16.441Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T17:07:16.441Z
Learning: Applies to cli/**/*.go : Go CLI: golangci-lint + go vet checked in pre-commit hooks and CI (conditional on cli/**/*.go changes)

Applied to files:

  • cli/cmd/envvars.go
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to cli/**/*.go : Go CLI (Go 1.26+) uses Cobra for commands, charmbracelet/huh for interactive CLI, charmbracelet/lipgloss for styled output. Cross-platform builds (linux/darwin/windows × amd64/arm64). GoReleaser for releases with cosign keyless signing of checksums.txt. SLSA L3 provenance attestations via actions/attest-build-provenance.

Applied to files:

  • cli/cmd/envvars.go
📚 Learning: 2026-03-21T14:12:17.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T14:12:17.848Z
Learning: Applies to cli/go.mod : CLI uses Go 1.26+. Dependencies: Cobra, charmbracelet/huh, charmbracelet/lipgloss, sigstore-go, go-containerregistry, go-tuf

Applied to files:

  • cli/cmd/envvars.go
📚 Learning: 2026-03-21T12:54:22.557Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Go 1.26+ required; CLI dependencies in `cli/go.mod` (Cobra, charmbracelet/huh, charmbracelet/lipgloss, sigstore-go, go-containerregistry, go-tuf)

Applied to files:

  • cli/cmd/envvars.go
🔇 Additional comments (17)
cli/cmd/envvars.go (2)

30-40: LGTM!

The envBool function correctly trims whitespace before parsing (addressing the prior review feedback) and handles the standard truthy values case-insensitively.


42-55: LGTM!

The noColorFromEnv function correctly implements the standard environment signals for disabling color output: NO_COLOR (any non-empty value per the spec), CLICOLOR=0, and TERM=dumb.

cli/cmd/backup.go (3)

299-308: LGTM!

The migration to GetGlobalOpts(ctx) and ui.NewUIWithOptions follows the new global options pattern consistently.


400-403: LGTM!

The missing --confirm path now correctly returns NewExitError(ExitUsage, ...) so scripts can distinguish user mistakes (exit code 2) from runtime failures (exit code 1).


374-377: LGTM!

The migration from Hint() to HintNextStep() is semantically correct here — this is guidance for the user's next action after seeing "no backups found".

cli/internal/ui/livebox.go (4)

58-67: LGTM!

The width calculation correctly uses PlainIconSuccess when in plain mode to ensure proper column alignment.


80-89: LGTM!

The quiet mode early return and plain mode fallback to Step() are correctly implemented, preventing any live-box rendering in those modes while still allowing the title to be displayed as a step in plain/non-TTY contexts.


117-131: LGTM!

The UpdateLine method correctly gates output based on UI modes: quiet/JSON suppresses human output entirely (with errors propagating through return values), while plain mode uses ASCII icons for finish events.


217-221: LGTM!

The redraw method correctly no-ops for non-TTY, plain, and quiet modes, preventing ANSI cursor movement sequences from corrupting output in those contexts.

cli/cmd/root.go (4)

37-44: Cobra hook behavior documented.

Good documentation of the Cobra limitation where subcommand pre-run hooks silently override the parent's PersistentPreRunE. This warns future developers to call setupGlobalOpts explicitly if they add pre-run hooks to subcommands.


64-96: LGTM!

The mutual exclusivity checks now correctly use the resolved values (after env var overrides are applied), addressing the prior review feedback. The check at line 91 uses the resolved quiet (which incorporates SYNTHORG_QUIET) against flagVerbose, and line 94 uses the raw flags for --plain/--json which have no env var counterparts.


121-141: LGTM!

The resolveDataDir function correctly implements the precedence (flag > env > default), normalizes to an absolute path, and resolves symlinks to prevent path traversal — good defense-in-depth for security.


186-198: LGTM!

The Execute function correctly suppresses ChildExitError from being printed to stderr (since its message "re-launched CLI exited with code N" is internal), while still returning the error so main.go can extract and propagate the exit code.

cli/internal/ui/ui.go (4)

52-64: LGTM!

The Options struct clearly documents each mode, and the package-level sessionTipsSeen ensures tip deduplication is shared across all UI instances (stdout/stderr) within a CLI invocation.


93-123: LGTM!

The NewUIWithOptions constructor correctly:

  • Uses termenv.Ascii profile when NoColor or Plain to disable ANSI styling
  • Defaults hints to "auto" if unset
  • Sets quiet to true when either opts.Quiet or opts.JSON is set, ensuring JSON mode suppresses human-oriented output

232-247: LGTM!

The HintTip implementation correctly uses sync.Map.LoadOrStore for thread-safe session-wide deduplication in "auto" mode, and respects the "never" mode to suppress tips entirely.


373-381: LGTM!

The JSONOutput method uses MarshalIndent for human-readable JSON and correctly propagates marshaling errors to the caller.

…y, wipe --yes, JSON mode

- Make TestShouldPromptDefault deterministic by detecting stdin TTY state
  and asserting accordingly instead of a no-op
- Use ctx parameter instead of cmd.Context() in pullAndPersist for
  consistency with function signature
- Make resolveUpdateChannel accept ctx and use GlobalOpts.DataDir
  instead of calling resolveDataDir directly (GlobalOpts IS available)
- Wire --yes through all 7 wipe prompt helpers via wipeContext.shouldPrompt:
  confirmWipe, promptStartForBackup, promptForBackup, promptSavePath,
  checkOverwrite, askContinueWithoutBackup, promptStartAfterWipe
- Suppress Success/Warn/Error output in JSON mode to prevent human text
  leaking into JSON stdout stream

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 27, 2026 20:49 — with GitHub Actions Inactive
@Aureliolo
Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 27, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
cli/cmd/update.go (2)

151-182: ⚠️ Potential issue | 🟠 Major

update still bypasses the global output modes.

This path, plus several helpers in the same file, still writes directly with fmt.Fprint* instead of going through ui.NewUIWithOptions(...). That means synthorg update --json still emits human text like “Checking for updates...”, and -q still prints status chatter. Please either thread UI objects through the rest of this command or explicitly reject --json here.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/cmd/update.go` around lines 151 - 182, The update command currently
writes directly to stdout/stderr with fmt.Fprint* and ignores global UI modes;
replace those calls by constructing a UI via ui.NewUIWithOptions using
cmd.OutOrStdout()/cmd.ErrOrStderr() and the command context/flags (JSON/quiet
modes) and then use the UI methods (e.g., ui.Info/ ui.Warn/ ui.Error/ ui.Printf)
instead of fmt.Fprintln/Fprintf in this function and its helpers
(resolveUpdateChannel, isDevChannelMismatch reporting, and calls around
selfupdate.CheckForChannel and downloadAndApplyCLI), or alternatively explicitly
detect and reject --json/quiet usage up front; ensure all user-visible writes in
update.go go through the UI instance rather than fmt.* so --json and -q behave
correctly.

501-515: ⚠️ Potential issue | 🟠 Major

Don't turn --yes into “skip restart”.

confirmRestart() defaults to yes, but this branch returns false whenever ShouldPrompt() is false. Since ShouldPrompt() is also false for an explicit --yes, synthorg update --yes will leave already-running containers on the old revision instead of taking the default-yes restart path that cli/cmd/root.go:46-58 promises for “assume yes for all prompts”.

Proposed fix
-	if !GetGlobalOpts(ctx).ShouldPrompt() {
+	opts := GetGlobalOpts(ctx)
+	if opts.Yes {
+		return performRestart(ctx, out, info, safeDir, state, opts.UIOptions())
+	}
+	if !opts.ShouldPrompt() {
 		_, _ = fmt.Fprintln(out, "Non-interactive mode: skipping restart. Run 'synthorg stop && synthorg start' to apply new images.")
 		return false, nil
 	}
@@
-	opts := GetGlobalOpts(cmd.Context())
 	return performRestart(ctx, out, info, safeDir, state, opts.UIOptions())
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/cmd/update.go` around lines 501 - 515, The current branch treats any
non-prompting context as "skip restart", which incorrectly makes --yes skip
restart; instead fetch the global options once (opts := GetGlobalOpts(ctx)) and,
when opts.ShouldPrompt() is false, check opts.AssumeYes() (or the equivalent
flag that represents --yes) — if AssumeYes is true, proceed to call
confirmRestart/performRestart as the default-yes path, otherwise keep the
existing non-interactive message and return; update the code around
GetGlobalOpts/ShouldPrompt/confirmRestart/performRestart accordingly so explicit
--yes triggers the restart.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cli/internal/ui/ui.go`:
- Around line 181-197: Success and Warn currently only suppress output when
u.jsonMode is true, so --quiet/-q still prints; update UI.Success and UI.Warn to
also short-circuit when u.quiet is true (check u.quiet || u.jsonMode) before
calling u.printLine; locate methods Success and Warn on type UI (and verify
NewUIWithOptions populates u.quiet) and return early when quiet is enabled.

---

Outside diff comments:
In `@cli/cmd/update.go`:
- Around line 151-182: The update command currently writes directly to
stdout/stderr with fmt.Fprint* and ignores global UI modes; replace those calls
by constructing a UI via ui.NewUIWithOptions using
cmd.OutOrStdout()/cmd.ErrOrStderr() and the command context/flags (JSON/quiet
modes) and then use the UI methods (e.g., ui.Info/ ui.Warn/ ui.Error/ ui.Printf)
instead of fmt.Fprintln/Fprintf in this function and its helpers
(resolveUpdateChannel, isDevChannelMismatch reporting, and calls around
selfupdate.CheckForChannel and downloadAndApplyCLI), or alternatively explicitly
detect and reject --json/quiet usage up front; ensure all user-visible writes in
update.go go through the UI instance rather than fmt.* so --json and -q behave
correctly.
- Around line 501-515: The current branch treats any non-prompting context as
"skip restart", which incorrectly makes --yes skip restart; instead fetch the
global options once (opts := GetGlobalOpts(ctx)) and, when opts.ShouldPrompt()
is false, check opts.AssumeYes() (or the equivalent flag that represents --yes)
— if AssumeYes is true, proceed to call confirmRestart/performRestart as the
default-yes path, otherwise keep the existing non-interactive message and
return; update the code around
GetGlobalOpts/ShouldPrompt/confirmRestart/performRestart accordingly so explicit
--yes triggers the restart.
🪄 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: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 13bdee03-e1be-47bd-aa3c-596d885297a3

📥 Commits

Reviewing files that changed from the base of the PR and between 9a73061 and 77d544d.

📒 Files selected for processing (4)
  • cli/cmd/global_opts_test.go
  • cli/cmd/update.go
  • cli/cmd/wipe.go
  • cli/internal/ui/ui.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
cli/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Go CLI: golangci-lint + go vet checked in pre-commit hooks and CI (conditional on cli/**/*.go changes)

Files:

  • cli/cmd/global_opts_test.go
  • cli/cmd/wipe.go
  • cli/cmd/update.go
  • cli/internal/ui/ui.go
🧠 Learnings (13)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/cli.yml : CLI workflow: Go lint (golangci-lint + go vet) + test (-race -coverprofile) + build (cross-compile: linux/darwin/windows × amd64/arm64) + govulncheck + fuzz testing (main-only, 30s/target, continue-on-error, matrix over 4 packages). cli-pass gate includes fuzz as informational. GoReleaser release on v* tags. Cosign keyless signing of checksums.txt. SLSA L3 provenance attestations. Sigstore bundle (.sigstore.json) attached. Post-release appends checksums/verification/provenance to draft release notes.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to cli/** : CLI: Go 1.26+, dependencies in cli/go.mod (Cobra, charmbracelet/huh).
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Applies to cli/**/*.go : Use Cobra for CLI command structure, charmbracelet/huh for interactive prompts, and charmbracelet/lipgloss for terminal styling
📚 Learning: 2026-03-27T17:07:16.441Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T17:07:16.441Z
Learning: Applies to cli/**/*_fuzz_test.go : Go CLI uses native 'testing.F' fuzz functions (FuzzYamlStr, etc.)

Applied to files:

  • cli/cmd/global_opts_test.go
📚 Learning: 2026-03-19T11:19:40.044Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:19:40.044Z
Learning: Applies to cli/**/*.go : Use native Go testing with `testing.F` fuzz functions (`Fuzz*`) for fuzz testing.

Applied to files:

  • cli/cmd/global_opts_test.go
📚 Learning: 2026-03-21T12:54:22.557Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Applies to cli/**/*.go : Use native Go `testing.F` fuzz functions (`Fuzz*`) for property-based testing

Applied to files:

  • cli/cmd/global_opts_test.go
📚 Learning: 2026-03-19T11:30:29.217Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:30:29.217Z
Learning: Applies to cli/**/*.go : Use native `testing.F` fuzz functions (`Fuzz*`) for fuzz testing Go code

Applied to files:

  • cli/cmd/global_opts_test.go
📚 Learning: 2026-03-21T14:12:17.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T14:12:17.848Z
Learning: Applies to cli/**/*.go : Use native `testing.F` fuzz functions (`Fuzz*`) for fuzzing

Applied to files:

  • cli/cmd/global_opts_test.go
📚 Learning: 2026-03-19T11:30:29.217Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:30:29.217Z
Learning: Applies to cli/**/*.go : Run Go lint via `golangci-lint run`, vet via `go vet`, tests via `go test ./...`, and fuzz via `go test -fuzz=FuzzTarget -fuzztime=30s`

Applied to files:

  • cli/cmd/global_opts_test.go
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to cli/** : CLI: Go 1.26+, dependencies in cli/go.mod (Cobra, charmbracelet/huh).

Applied to files:

  • cli/cmd/global_opts_test.go
  • cli/cmd/update.go
  • cli/internal/ui/ui.go
📚 Learning: 2026-03-27T17:07:16.441Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T17:07:16.441Z
Learning: Applies to cli/**/*.go : Go CLI: golangci-lint + go vet checked in pre-commit hooks and CI (conditional on cli/**/*.go changes)

Applied to files:

  • cli/cmd/global_opts_test.go
📚 Learning: 2026-03-16T19:52:03.656Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T19:52:03.656Z
Learning: Applies to cli/**/*.go : Lint CLI Go code with golangci-lint and go vet; test with go test -race; check vulnerabilities with govulncheck

Applied to files:

  • cli/cmd/global_opts_test.go
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to cli/**/*.go : Go CLI (Go 1.26+) uses Cobra for commands, charmbracelet/huh for interactive CLI, charmbracelet/lipgloss for styled output. Cross-platform builds (linux/darwin/windows × amd64/arm64). GoReleaser for releases with cosign keyless signing of checksums.txt. SLSA L3 provenance attestations via actions/attest-build-provenance.

Applied to files:

  • cli/cmd/global_opts_test.go
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to cli/go.mod : Go CLI dependencies: Go 1.26+, Cobra (commands), charmbracelet/huh (interactive CLI), charmbracelet/lipgloss (styled output).

Applied to files:

  • cli/internal/ui/ui.go
📚 Learning: 2026-03-21T12:54:22.557Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Applies to cli/**/*.go : Use Cobra for CLI command structure, charmbracelet/huh for interactive prompts, and charmbracelet/lipgloss for terminal styling

Applied to files:

  • cli/internal/ui/ui.go
🔇 Additional comments (1)
cli/cmd/global_opts_test.go (1)

90-104: Deterministic TTY assertion looks good.

This now tracks the same runtime condition that GlobalOpts.ShouldPrompt() uses, so the test no longer depends on a specific CI stdin setup.

…pt, update UI migration

- Suppress Success and Warn in quiet mode (--quiet = errors only);
  Error remains visible. Update tests to match new semantics.
- Fix --yes incorrectly skipping restart/cleanup/recovery: distinguish
  opts.Yes (auto-accept default) from non-interactive (skip) in
  restartIfRunning, confirmAndCleanup, and promptHealthRecover
- Migrate updateCLI and downloadAndApplyCLI from raw fmt.Fprintln to
  UI methods so --quiet and --json modes are respected during updates

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 27, 2026 21:17 — with GitHub Actions Inactive
@Aureliolo
Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 27, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cli/internal/ui/ui_test.go`:
- Around line 597-638: The HintTip deduplication test relies on the
package-level sessionTipsSeen sync.Map, so make the test use unique messages per
run to avoid cross-test interference; update TestHintCategories to call HintTip
with a unique string (e.g., include t.Name() or another per-test identifier)
when asserting deduplication and when sending the "try this" and "different tip"
messages, so HintTip and sessionTipsSeen won’t collide across tests while
keeping the same assertions; locate usages in TestHintCategories and the HintTip
calls that interact with sessionTipsSeen and NewUIWithOptions.
🪄 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: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 43753490-7fb4-4c79-b4cc-83428a7dae79

📥 Commits

Reviewing files that changed from the base of the PR and between 77d544d and a4c2bc0.

📒 Files selected for processing (5)
  • cli/cmd/cleanup.go
  • cli/cmd/update.go
  • cli/cmd/update_health.go
  • cli/internal/ui/ui.go
  • cli/internal/ui/ui_test.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
cli/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Go CLI: golangci-lint + go vet checked in pre-commit hooks and CI (conditional on cli/**/*.go changes)

Files:

  • cli/cmd/cleanup.go
  • cli/cmd/update_health.go
  • cli/internal/ui/ui_test.go
  • cli/internal/ui/ui.go
  • cli/cmd/update.go
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/cli.yml : CLI workflow: Go lint (golangci-lint + go vet) + test (-race -coverprofile) + build (cross-compile: linux/darwin/windows × amd64/arm64) + govulncheck + fuzz testing (main-only, 30s/target, continue-on-error, matrix over 4 packages). cli-pass gate includes fuzz as informational. GoReleaser release on v* tags. Cosign keyless signing of checksums.txt. SLSA L3 provenance attestations. Sigstore bundle (.sigstore.json) attached. Post-release appends checksums/verification/provenance to draft release notes.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to cli/** : CLI: Go 1.26+, dependencies in cli/go.mod (Cobra, charmbracelet/huh).
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Applies to cli/**/*.go : Use Cobra for CLI command structure, charmbracelet/huh for interactive prompts, and charmbracelet/lipgloss for terminal styling
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to cli/**/*.go : Go CLI (Go 1.26+) uses Cobra for commands, charmbracelet/huh for interactive CLI, charmbracelet/lipgloss for styled output. Cross-platform builds (linux/darwin/windows × amd64/arm64). GoReleaser for releases with cosign keyless signing of checksums.txt. SLSA L3 provenance attestations via actions/attest-build-provenance.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Go 1.26+ required; CLI dependencies in `cli/go.mod` (Cobra, charmbracelet/huh, charmbracelet/lipgloss, sigstore-go, go-containerregistry, go-tuf)
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to cli/** : CLI: Go 1.26+, dependencies in cli/go.mod (Cobra, charmbracelet/huh).

Applied to files:

  • cli/cmd/cleanup.go
  • cli/cmd/update.go
📚 Learning: 2026-03-27T17:07:16.441Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T17:07:16.441Z
Learning: Applies to cli/**/*_fuzz_test.go : Go CLI uses native 'testing.F' fuzz functions (FuzzYamlStr, etc.)

Applied to files:

  • cli/internal/ui/ui_test.go
📚 Learning: 2026-03-19T11:19:40.044Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:19:40.044Z
Learning: Applies to cli/**/*.go : Use native Go testing with `testing.F` fuzz functions (`Fuzz*`) for fuzz testing.

Applied to files:

  • cli/internal/ui/ui_test.go
📚 Learning: 2026-03-19T11:30:29.217Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:30:29.217Z
Learning: Applies to cli/**/*.go : Run Go lint via `golangci-lint run`, vet via `go vet`, tests via `go test ./...`, and fuzz via `go test -fuzz=FuzzTarget -fuzztime=30s`

Applied to files:

  • cli/internal/ui/ui_test.go
📚 Learning: 2026-03-21T12:54:22.557Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Applies to cli/**/*.go : Use Cobra for CLI command structure, charmbracelet/huh for interactive prompts, and charmbracelet/lipgloss for terminal styling

Applied to files:

  • cli/internal/ui/ui.go
🔇 Additional comments (20)
cli/cmd/update_health.go (2)

28-29: LGTM!

The UI construction correctly uses GetGlobalOpts and NewUIWithOptions to respect global output modes (quiet/plain/json).


90-97: LGTM!

The prompt gating logic correctly distinguishes between --yes (auto-recover) and non-interactive mode without --yes (abort with guidance). When ShouldPrompt() returns false:

  • If opts.Yes is true → auto-recover (user explicitly requested non-interactive acceptance)
  • Otherwise → print guidance and abort (piped/redirected input without explicit consent)
cli/cmd/cleanup.go (3)

30-38: LGTM!

The migration to GetGlobalOpts and NewUIWithOptions correctly threads global options through the cleanup command.


89-106: LGTM!

The prompt gating logic correctly handles three scenarios:

  1. Non-interactive without --yes → print hint and exit without removal
  2. --yes flag set → auto-confirm removal (skip prompt)
  3. Interactive without --yes → show confirmation form

The && !opts.Yes condition is necessary to distinguish explicit --yes from mere non-TTY input.


64-64: LGTM!

The hint category updates are appropriate:

  • HintTip for the auto-cleanup suggestion (one-time tip, deduplicated in auto mode)
  • HintError for skipped images (error-recovery context, always shown unless quiet)

Also applies to: 140-140

cli/internal/ui/ui_test.go (3)

430-476: LGTM!

Comprehensive test coverage for quiet mode behavior: verifies that non-essential output (Logo, Step, Section, KeyValue, hints, etc.) is suppressed while Error and Plain remain visible. The assertion that Success and Warn are suppressed aligns with the "errors only" contract for --quiet.


478-595: LGTM!

Thorough plain mode tests verify ASCII-only rendering for icons, boxes, dividers, tables, logo, and icon accessors. The negative assertions (no Unicode characters) ensure the plain mode contract is enforced.


681-726: LGTM!

Good coverage for spinner modes and JSON output:

  • Quiet spinner produces no output on start or success
  • Plain spinner uses ASCII icon and prints step message
  • JSON output is properly indented with expected fields
cli/internal/ui/ui.go (5)

52-64: LGTM!

The Options struct clearly documents the purpose of each mode. The sessionTipsSeen sync.Map provides thread-safe deduplication for HintTip messages across all UI instances.


93-123: LGTM!

NewUIWithOptions correctly:

  • Disables ANSI styling when NoColor or Plain is set via termenv.Ascii profile
  • Defaults hints to "auto" when empty
  • Sets quiet to opts.Quiet || opts.JSON to suppress non-essential output in both modes

181-207: LGTM!

The quiet/JSON mode handling is correct:

  • Success and Warn return early when u.quiet is true (suppressed in both --quiet and --json modes)
  • Error returns early only when u.jsonMode is true (visible in --quiet mode, suppressed in --json mode)

This correctly implements "errors only" for --quiet and "no human output" for --json.


221-281: LGTM!

The hint category system is well-designed:

  • Hint() deprecated with clear guidance to use category-specific methods
  • HintError and HintNextStep always shown unless quiet (critical guidance)
  • HintTip deduplicated in "auto" mode, suppressed in "never" mode
  • HintGuidance only shown in "always" mode (for verbose users)

The printHint shared implementation ensures consistent styling across all hint types.


386-394: LGTM!

JSONOutput correctly marshals data with 2-space indentation and writes to the UI writer. The error is properly propagated.

cli/cmd/update.go (7)

49-49: LGTM!

Using GetGlobalOpts(cmd.Context()).DataDir provides consistent data directory resolution through the global options infrastructure.


107-131: LGTM!

downloadAndApplyCLI now uses UI methods (Step, Success, HintNextStep) instead of raw fmt.Fprintf, ensuring output respects global modes (--quiet, --plain, --json).


188-193: LGTM!

resolveUpdateChannel now correctly accepts context and uses GetGlobalOpts(ctx).DataDir for consistent data directory resolution.


225-249: LGTM!

The flag forwarding in reexecUpdate correctly propagates all global output/behavior flags to the re-executed child process:

  • --data-dir, --skip-verify (existing)
  • --quiet, -v (repeated for verbosity level), --no-color, --plain, --json, --yes (new)

Using flag variables directly (flagDataDir, flagVerbose, etc.) ensures consistency with the root command's persistent flag registration.


363-382: LGTM!

The confirm functions correctly use GetGlobalOpts(ctx).ShouldPrompt() to respect both --yes and non-TTY detection. The defaultVal parameter allows callers to specify appropriate defaults for different confirmation contexts (e.g., true for standard updates, false for risky operations after user declined compose changes).


502-519: LGTM!

The restart logic correctly distinguishes:

  • --yes → auto-restart (line 504-506)
  • Non-interactive without --yes → skip with guidance (line 508-509)
  • Interactive → show confirmation prompt (line 512-518)

This matches the semantic pattern established in other commands.


447-447: LGTM!

  • GetGlobalOpts(ctx).SkipVerify replaces the local boolean for consistent global option access
  • HintError is the appropriate category for the air-gapped environment hint (error recovery context)

Also applies to: 466-466

…nterference

sessionTipsSeen is package-level, so parallel tests sharing the same
tip string would collide. Prefix tip messages with t.Name() to ensure
each subtest has its own dedup namespace.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 27, 2026 21:50 — with GitHub Actions Inactive
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cli/internal/ui/ui_test.go`:
- Around line 503-507: The negative Unicode-icon check is missing IconWarning
and is wrongly checking IconHint even though no Hint was invoked; update the
test loop to include IconWarning in the checked set and remove IconHint (or
alternatively call u.Hint("...") if you intended to test Hint), ensuring the
verification covers IconSuccess, IconError, IconInProgress and IconWarning to
match calls like u.Warn("hmm") and the actual methods used.
🪄 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: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 89751394-ac74-4c89-bb08-60949fdcedef

📥 Commits

Reviewing files that changed from the base of the PR and between a4c2bc0 and 5ab2a86.

📒 Files selected for processing (1)
  • cli/internal/ui/ui_test.go
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: CLI Test (windows-latest)
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (1)
cli/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Go CLI: golangci-lint + go vet checked in pre-commit hooks and CI (conditional on cli/**/*.go changes)

Files:

  • cli/internal/ui/ui_test.go
🧠 Learnings (9)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/cli.yml : CLI workflow: Go lint (golangci-lint + go vet) + test (-race -coverprofile) + build (cross-compile: linux/darwin/windows × amd64/arm64) + govulncheck + fuzz testing (main-only, 30s/target, continue-on-error, matrix over 4 packages). cli-pass gate includes fuzz as informational. GoReleaser release on v* tags. Cosign keyless signing of checksums.txt. SLSA L3 provenance attestations. Sigstore bundle (.sigstore.json) attached. Post-release appends checksums/verification/provenance to draft release notes.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Applies to cli/**/*.go : Use Cobra for CLI command structure, charmbracelet/huh for interactive prompts, and charmbracelet/lipgloss for terminal styling
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to cli/** : CLI: Go 1.26+, dependencies in cli/go.mod (Cobra, charmbracelet/huh).
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to cli/**/*.go : Go CLI (Go 1.26+) uses Cobra for commands, charmbracelet/huh for interactive CLI, charmbracelet/lipgloss for styled output. Cross-platform builds (linux/darwin/windows × amd64/arm64). GoReleaser for releases with cosign keyless signing of checksums.txt. SLSA L3 provenance attestations via actions/attest-build-provenance.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Go 1.26+ required; CLI dependencies in `cli/go.mod` (Cobra, charmbracelet/huh, charmbracelet/lipgloss, sigstore-go, go-containerregistry, go-tuf)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T14:12:17.848Z
Learning: Applies to cli/go.mod : CLI uses Go 1.26+. Dependencies: Cobra, charmbracelet/huh, charmbracelet/lipgloss, sigstore-go, go-containerregistry, go-tuf
📚 Learning: 2026-03-27T17:07:16.441Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T17:07:16.441Z
Learning: Applies to cli/**/*_fuzz_test.go : Go CLI uses native 'testing.F' fuzz functions (FuzzYamlStr, etc.)

Applied to files:

  • cli/internal/ui/ui_test.go
📚 Learning: 2026-03-19T11:30:29.217Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:30:29.217Z
Learning: Applies to cli/**/*.go : Run Go lint via `golangci-lint run`, vet via `go vet`, tests via `go test ./...`, and fuzz via `go test -fuzz=FuzzTarget -fuzztime=30s`

Applied to files:

  • cli/internal/ui/ui_test.go
📚 Learning: 2026-03-19T11:19:40.044Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:19:40.044Z
Learning: Applies to cli/**/*.go : Use native Go testing with `testing.F` fuzz functions (`Fuzz*`) for fuzz testing.

Applied to files:

  • cli/internal/ui/ui_test.go
📚 Learning: 2026-03-21T12:54:22.557Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Applies to cli/**/*.go : Use native Go `testing.F` fuzz functions (`Fuzz*`) for property-based testing

Applied to files:

  • cli/internal/ui/ui_test.go
📚 Learning: 2026-03-21T14:12:17.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T14:12:17.848Z
Learning: Applies to cli/**/*.go : Use native `testing.F` fuzz functions (`Fuzz*`) for fuzzing

Applied to files:

  • cli/internal/ui/ui_test.go
📚 Learning: 2026-03-19T11:30:29.217Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:30:29.217Z
Learning: Applies to cli/**/*.go : Use native `testing.F` fuzz functions (`Fuzz*`) for fuzz testing Go code

Applied to files:

  • cli/internal/ui/ui_test.go
📚 Learning: 2026-03-27T17:07:16.441Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T17:07:16.441Z
Learning: Applies to cli/**/*.go : Go CLI: golangci-lint + go vet checked in pre-commit hooks and CI (conditional on cli/**/*.go changes)

Applied to files:

  • cli/internal/ui/ui_test.go
📚 Learning: 2026-03-21T12:54:22.557Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Applies to cli/**/*.go : Use Cobra for CLI command structure, charmbracelet/huh for interactive prompts, and charmbracelet/lipgloss for terminal styling

Applied to files:

  • cli/internal/ui/ui_test.go
🔇 Additional comments (10)
cli/internal/ui/ui_test.go (10)

430-476: LGTM! Comprehensive quiet mode test with proper isolation.

The test correctly validates that quiet mode suppresses non-essential output while preserving Error and Plain output. The use of t.Name() for the HintTip message (line 443) properly addresses the test isolation concern for the session-level deduplication mechanism.


510-530: LGTM!

The test properly validates ASCII-only box rendering in plain mode by checking for + and | characters while ensuring Unicode box-drawing characters are absent.


532-545: LGTM!

Correctly validates ASCII divider rendering in plain mode.


547-560: LGTM!

Correctly validates plain mode table rendering without Unicode separators.


562-579: LGTM!

Correctly validates plain text logo rendering with version string.


581-595: LGTM!

Correctly validates that icon accessor methods return plain constants in plain mode.


597-680: LGTM! Comprehensive hint category testing with proper isolation.

The test thoroughly validates hint visibility rules across all three modes (auto/always/never). The use of t.Name() prefixes for tip messages (lines 617, 629, 660, 661) ensures proper test isolation when the package-level sessionTipsSeen map is shared across parallel tests.


682-694: LGTM!

Correctly validates that spinner output is completely suppressed in quiet mode, including the success message.


696-709: LGTM!

Correctly validates plain mode spinner uses ASCII icon and prints the step message.


711-727: LGTM!

Correctly validates JSON output format with expected field structure. The check for ": " (colon-space) confirms pretty-printed/indented JSON output.

… test

The test calls Warn() but the negative-check loop verified IconHint
(never invoked) instead of IconWarning. Replace to match the actual
methods under test.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 27, 2026 22:20 — with GitHub Actions Inactive
@Aureliolo Aureliolo merged commit fef4fc5 into main Mar 27, 2026
42 checks passed
@Aureliolo Aureliolo deleted the feat/add-config-flags-for-more-options branch March 27, 2026 22:27
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 27, 2026 22:27 — with GitHub Actions Inactive
Aureliolo added a commit that referenced this pull request Mar 30, 2026
🤖 I have created a release *beep* *boop*
---
#MAJOR CHANGES; We got a somewhat working webui :)

##
[0.5.0](v0.4.9...v0.5.0)
(2026-03-30)


### Features

* add analytics trends and budget forecast API endpoints
([#798](#798))
([16b61f5](16b61f5))
* add department policies to default templates
([#852](#852))
([7a41548](7a41548))
* add remaining activity event types (task_started, tool_used,
delegation, cost_incurred)
([#832](#832))
([4252fac](4252fac))
* agent performance, activity, and history API endpoints
([#811](#811))
([9b75c1d](9b75c1d))
* Agent Profiles and Detail pages (biography, career, performance)
([#874](#874))
([62d7880](62d7880))
* app shell, Storybook, and CI/CD pipeline
([#819](#819))
([d4dde90](d4dde90))
* Approvals page with risk grouping, urgency indicators, batch actions
([#889](#889))
([4e9673d](4e9673d))
* Budget Panel page (P&L dashboard, breakdown charts, forecast)
([#890](#890))
([b63b0f1](b63b0f1))
* build infrastructure layer (API client, auth, WebSocket)
([#815](#815))
([9f01d3e](9f01d3e))
* CLI global options infrastructure, UI modes, exit codes, env vars
([#891](#891))
([fef4fc5](fef4fc5))
* CodeMirror editor and theme preferences toggle
([#905](#905),
[#807](#807))
([#909](#909))
([41fbedc](41fbedc))
* Company page (department/agent management)
([#888](#888))
([cfb88b0](cfb88b0))
* comprehensive hint coverage across all CLI commands
([#900](#900))
([937974e](937974e))
* config system extensions, per-command flags for
init/start/stop/status/logs
([#895](#895))
([32f83fe](32f83fe))
* configurable currency system replacing hardcoded USD
([#854](#854))
([b372551](b372551))
* Dashboard page (metric cards, activity feed, budget burn)
([#861](#861))
([7d519d5](7d519d5))
* department health, provider status, and activity feed endpoints
([#818](#818))
([6d5f196](6d5f196))
* design tokens and core UI components
([#833](#833))
([ed887f2](ed887f2))
* extend approval, meeting, and budget API responses
([#834](#834))
([31472bf](31472bf))
* frontend polish -- real-time UX, accessibility, responsive,
performance ([#790](#790),
[#792](#792),
[#791](#791),
[#793](#793))
([#917](#917))
([f04a537](f04a537))
* implement human roles and access control levels
([#856](#856))
([d6d8a06](d6d8a06))
* implement semantic conflict detection in workspace merge
([#860](#860))
([d97283b](d97283b))
* interaction components and animation patterns
([#853](#853))
([82d4b01](82d4b01))
* Login page + first-run bootstrap + Company page
([#789](#789),
[#888](#888))
([#896](#896))
([8758e8d](8758e8d))
* Meetings page with timeline viz, token bars, contribution formatting
([#788](#788))
([#904](#904))
([b207f46](b207f46))
* Messages page with threading, channel badges, sender indicators
([#787](#787))
([#903](#903))
([28293ad](28293ad))
* Org Chart force-directed view and drag-drop reassignment
([#872](#872),
[#873](#873))
([#912](#912))
([a68a938](a68a938))
* Org Chart page (living nodes, status, CRUD, department health)
([#870](#870))
([0acbdae](0acbdae))
* per-command flags for remaining commands, auto-behavior wiring,
help/discoverability
([#897](#897))
([3f7afa2](3f7afa2))
* Providers page with backend rework -- health, CRUD, subscription auth
([#893](#893))
([9f8dd98](9f8dd98))
* scaffold React + Vite + TypeScript + Tailwind project
([#799](#799))
([bd151aa](bd151aa))
* Settings page with search, dependency indicators, grouped rendering
([#784](#784))
([#902](#902))
([a7b9870](a7b9870))
* Setup Wizard rebuild with template comparison, cost estimator, theme
customization ([#879](#879))
([ae8b50b](ae8b50b))
* setup wizard UX -- template filters, card metadata, provider form
reuse ([#910](#910))
([7f04676](7f04676))
* setup wizard UX overhaul -- mode choice, step reorder, provider fixes
([#907](#907))
([ee964c4](ee964c4))
* structured ModelRequirement in template agent configs
([#795](#795))
([7433548](7433548))
* Task Board page (rich Kanban, filtering, dependency viz)
([#871](#871))
([04a19b0](04a19b0))


### Bug Fixes

* align frontend types with backend and debounce WS refetches
([#916](#916))
([134c11b](134c11b))
* auto-cleanup targets newly pulled images instead of old ones
([#884](#884))
([50e6591](50e6591))
* correct wipe backup-skip flow and harden error handling
([#808](#808))
([c05860f](c05860f))
* improve provider setup in wizard, subscription auth, dashboard bugs
([#914](#914))
([87bf8e6](87bf8e6))
* improve update channel detection and add config get command
([#814](#814))
([6b137f0](6b137f0))
* resolve all ESLint warnings, add zero-warnings enforcement
([#899](#899))
([079b46a](079b46a))
* subscription auth uses api_key, base URL optional for cloud providers
([#915](#915))
([f0098dd](f0098dd))


### Refactoring

* semantic analyzer cleanup -- shared filtering, concurrency, extraction
([#908](#908))
([81372bf](81372bf))


### Documentation

* brand identity and UX design system from
[#765](#765) exploration
([#804](#804))
([389a9f4](389a9f4))
* page structure and information architecture for v0.5.0 dashboard
([#809](#809))
([f8d6d4a](f8d6d4a))
* write UX design guidelines with WCAG-verified color system
([#816](#816))
([4a4594e](4a4594e))


### Tests

* add unit tests for agent hooks and page components
([#875](#875))
([#901](#901))
([1d81546](1d81546))


### CI/CD

* bump actions/deploy-pages from 4.0.5 to 5.0.0 in the major group
([#831](#831))
([01c19de](01c19de))
* bump astral-sh/setup-uv from 7.6.0 to 8.0.0 in
/.github/actions/setup-python-uv in the all group
([#920](#920))
([5f6ba54](5f6ba54))
* bump codecov/codecov-action from 5.5.3 to 6.0.0 in the major group
([#868](#868))
([f22a181](f22a181))
* bump github/codeql-action from 4.34.1 to 4.35.0 in the all group
([#883](#883))
([87a4890](87a4890))
* bump sigstore/cosign-installer from 4.1.0 to 4.1.1 in the
minor-and-patch group
([#830](#830))
([7a69050](7a69050))
* bump the all group with 3 updates
([#923](#923))
([ff27c8e](ff27c8e))
* bump wrangler from 4.76.0 to 4.77.0 in /.github in the minor-and-patch
group ([#822](#822))
([07d43eb](07d43eb))
* bump wrangler from 4.77.0 to 4.78.0 in /.github in the all group
([#882](#882))
([f84118d](f84118d))


### Maintenance

* add design system enforcement hook and component inventory
([#846](#846))
([15abc43](15abc43))
* add dev-only auth bypass for frontend testing
([#885](#885))
([6cdcd8a](6cdcd8a))
* add pre-push rebase check hook
([#855](#855))
([b637a04](b637a04))
* backend hardening -- eviction/size-caps and model validation
([#911](#911))
([81253d9](81253d9))
* bump axios from 1.13.6 to 1.14.0 in /web in the all group across 1
directory ([#922](#922))
([b1b0232](b1b0232))
* bump brace-expansion from 5.0.4 to 5.0.5 in /web
([#862](#862))
([ba4a565](ba4a565))
* bump eslint-plugin-react-refresh from 0.4.26 to 0.5.2 in /web
([#801](#801))
([7574bb5](7574bb5))
* bump faker from 40.11.0 to 40.11.1 in the minor-and-patch group
([#803](#803))
([14d322e](14d322e))
* bump https://github.com/astral-sh/ruff-pre-commit from v0.15.7 to
0.15.8 ([#864](#864))
([f52901e](f52901e))
* bump nginxinc/nginx-unprivileged from `6582a34` to `f99cc61` in
/docker/web in the all group
([#919](#919))
([df85e4f](df85e4f))
* bump nginxinc/nginx-unprivileged from `ccbac1a` to `6582a34` in
/docker/web ([#800](#800))
([f4e9450](f4e9450))
* bump node from `44bcbf4` to `71be405` in /docker/sandbox
([#827](#827))
([91bec67](91bec67))
* bump node from `5209bca` to `cf38e1f` in /docker/web
([#863](#863))
([66d6043](66d6043))
* bump picomatch in /site
([#842](#842))
([5f20bcc](5f20bcc))
* bump recharts 2-&gt;3 and @types/node 22-&gt;25 in /web
([#802](#802))
([a908800](a908800))
* Bump requests from 2.32.5 to 2.33.0
([#843](#843))
([41daf69](41daf69))
* bump smol-toml from 1.6.0 to 1.6.1 in /site
([#826](#826))
([3e5dbe4](3e5dbe4))
* bump the all group with 3 updates
([#921](#921))
([7bace0b](7bace0b))
* bump the minor-and-patch group across 1 directory with 2 updates
([#829](#829))
([93e611f](93e611f))
* bump the minor-and-patch group across 1 directory with 3 updates
([#841](#841))
([7010c8e](7010c8e))
* bump the minor-and-patch group across 1 directory with 3 updates
([#869](#869))
([548cee5](548cee5))
* bump the minor-and-patch group in /site with 2 updates
([#865](#865))
([9558101](9558101))
* bump the minor-and-patch group with 2 updates
([#867](#867))
([4830706](4830706))
* consolidate Dependabot groups to 1 PR per ecosystem
([06d2556](06d2556))
* consolidate Dependabot groups to 1 PR per ecosystem
([#881](#881))
([06d2556](06d2556))
* improve worktree skill with full dep sync and status enhancements
([#906](#906))
([772c625](772c625))
* remove Vue remnants and document framework decision
([#851](#851))
([bf2adf6](bf2adf6))
* update web dependencies and fix brace-expansion CVE
([#880](#880))
([a7a0ed6](a7a0ed6))
* upgrade to Storybook 10 and TypeScript 6
([#845](#845))
([52d95f2](52d95f2))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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