Conversation
WalkthroughThis PR documents and implements expanded CLI configuration and behavior: adds Suggested labels
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Snapshot WarningsEnsure 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 FilesNone |
There was a problem hiding this comment.
Code Review
This pull request significantly expands the CLI's configuration and command-line capabilities. It introduces new config subcommands (unset, list, path, edit), supports non-interactive init via flags, and adds numerous flags to the start, stop, status, logs, and root commands. The configuration state has been expanded to include display preferences and auto-behavior settings. Feedback includes addressing missing environment variable mappings in the config list logic and removing dead code in the start command.
| return EnvAutoPull | ||
| case "auto_restart": | ||
| return EnvAutoRestart | ||
| default: |
There was a problem hiding this comment.
The envVarForKey function is missing mappings for some configuration keys that can be set via environment variables, such as sandbox and auto_cleanup. This will cause synthorg config list to report an incorrect source (config or default instead of env) when these settings are configured via environment variables.
Please consider adding the missing keys to ensure the source is resolved correctly.
case "auto_cleanup":
return EnvAutoCleanup
case "sandbox":
return EnvSandbox
default:| return composeRun(ctx, cmd, info, safeDir, "up") | ||
| } | ||
|
|
||
| return startDetached(ctx, info, safeDir, state, out, errOut, healthTimeout) |
There was a problem hiding this comment.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
cli/cmd/logs.go (1)
50-114: 🧹 Nitpick | 🔵 TrivialFunction
runLogsexceeds the 50-line guideline.The function is approximately 65 lines. Consider extracting the validation logic (lines 72-92) into a helper function like
validateLogsArgs.♻️ Proposed extraction of validation logic
+// validateLogsInput validates the --tail, --since, --until, and service name arguments. +func validateLogsInput(tail, since, until string, services []string) error { + t := strings.TrimSpace(tail) + if t != "all" { + if n, err := strconv.Atoi(t); err != nil || n <= 0 { + return fmt.Errorf("--tail must be a positive integer or 'all', got %q", tail) + } + } + for _, tv := range []struct{ flag, val string }{{"--since", since}, {"--until", until}} { + if tv.val != "" && !timeFilterPattern.MatchString(tv.val) { + return fmt.Errorf("%s value %q contains unexpected characters", tv.flag, tv.val) + } + } + for _, svc := range services { + if !serviceNamePattern.MatchString(svc) { + return fmt.Errorf("invalid service name %q: must be alphanumeric, hyphens, or underscores", svc) + } + } + return nil +} + func runLogs(cmd *cobra.Command, args []string) error { // ... setup code ... - // Validate --tail value. - tail := strings.TrimSpace(logTail) - if tail != "all" { - // ... validation ... - } - // ... more validation ... + if err := validateLogsInput(logTail, logSince, logUntil, args); err != nil { + return err + } + tail := strings.TrimSpace(logTail) // ... rest of function ... }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cli/cmd/logs.go` around lines 50 - 114, Extract the validation block in runLogs (the --tail check, the --since/--until pattern check, and the service name loop) into a new helper function named validateLogsArgs that accepts the necessary inputs (e.g., logTail, logSince, logUntil, args) and returns an error; replace the inlined validation in runLogs with a single call to validateLogsArgs and propagate its error, keeping all existing error messages and regex/variable references (timeFilterPattern, serviceNamePattern) unchanged so compile-time references like runLogs, validateLogsArgs, timeFilterPattern, serviceNamePattern, and composeRun remain valid.cli/cmd/status.go (1)
242-293: 🛠️ Refactor suggestion | 🟠 MajorExtract the
--servicesfilter out ofprintContainerStates.This function is now over the 50-line limit and is handling compose exec, filter parsing, and two render paths at once. Pulling the filter/render pieces into helpers will keep future flag work easier to reason about.
As per coding guidelines "Functions should be under 50 lines; files should be under 800 lines".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cli/cmd/status.go` around lines 242 - 293, The --services filtering block in printContainerStates should be extracted into a helper to reduce function size; create a function like filterContainersByServices(out *ui.UI, containers []Container, servicesFlag string) []Container (or similar) that validates each comma-separated service name against serviceNamePattern, calls out.Warn for invalid names (same message used now), builds and returns the filtered slice, and then replace the inline filter code in printContainerStates with a single call to that helper; keep parseContainerJSON, statusServices, serviceNamePattern, and renderContainerTable usage unchanged so behavior is identical.
🤖 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/config_ext_test.go`:
- Around line 294-348: Both TestConfigSetAutoBehaviorKeys and
TestConfigGetNewKeys duplicate the same save/execute/load flow and exceed the
50-line guideline; extract a small test helper (e.g., runConfigSetAndLoad or
execConfigSet) that accepts the test *testing.T, key, value and data dir (or
creates t.TempDir internally), performs config.Save(state) with state.DataDir,
sets rootCmd.SetOut/SetErr to a buffer, sets rootCmd.SetArgs to {"config","set",
key, value, "--data-dir", dir}, calls rootCmd.Execute(), then returns the loaded
config via config.Load(dir) (or returns the loaded state and any error) so both
TestConfigSetAutoBehaviorKeys and TestConfigGetNewKeys can call this helper to
perform the common steps and keep each test under 50 lines; reference rootCmd,
config.Save, config.Load, and t.TempDir when implementing the helper.
- Around line 20-24: Tests TestConfigSetAutoBehaviorKeys and
TestConfigGetNewKeys mutate package-level rootCmd (using
rootCmd.SetOut/SetErr/SetArgs/Execute) and exceed the 50-line limit; this causes
shared Cobra state between tests/fuzzers. Split the large tests into smaller
test functions and ensure command state isolation by either constructing a fresh
command instance for each test (instead of reusing package-level rootCmd) or by
recording previous out/err/args/flags and restoring them with t.Cleanup in every
test and fuzzer (also update FuzzConfigSetBackendPort to perform the same
cleanup). Locate usages of rootCmd, SetOut, SetErr, SetArgs, Execute,
TestConfigSetAutoBehaviorKeys, TestConfigGetNewKeys and FuzzConfigSetBackendPort
and implement per-test command creation or consistent t.Cleanup restores to
avoid cross-test leakage.
In `@cli/cmd/config.go`:
- Around line 633-646: The resolveEditor function can panic when VISUAL or
EDITOR is set to whitespace because strings.Fields returns an empty slice and
the code dereferences parts[0]; update resolveEditor to check that parts has at
least one element before accessing parts[0] (e.g., treat empty parts same as
unset and fall back to platform defaults or to "vi"/"notepad"), and return nil
args when no additional parts exist; reference resolveEditor, the parts
variable, and environment vars VISUAL/EDITOR when making the change.
In `@cli/cmd/init.go`:
- Around line 271-281: Validate the provided --image-tag as soon as it is set
(where a.imageTag is checked) just like --channel: add an early check before
assigning imageTag that rejects invalid tags and returns an error; either call
an existing validator (e.g., config.IsValidImageTag) or add one in config and
use it here to return fmt.Errorf("invalid image tag %q", a.imageTag) on failure,
ensuring the assignment to imageTag only happens after the validation passes.
- Around line 148-164: The buildAnswersFromFlags function currently sets sandbox
with a fragile comparison (sandbox: initSandbox == "true") which silently treats
invalid values as false; update validation so initSandbox is checked with the
shared boolean validator (config.IsValidBool) or parsed with strconv.ParseBool
before calling buildAnswersFromFlags (or validate inside buildAnswersFromFlags),
return or surface an error when initSandbox is not a valid boolean, and only set
sandbox=true when the parsed/validated result is true; reference symbols:
initSandbox, buildAnswersFromFlags, runInit, and IsValidBool/strconv.ParseBool.
In `@cli/cmd/start.go`:
- Around line 55-58: After parsing startTimeout into healthTimeout, add a
validation that rejects non-positive durations when health checking is enabled:
check that healthTimeout > 0 and return a clear error (e.g., "invalid --timeout
%q: must be a positive duration") if not; place this check immediately after the
time.ParseDuration block where healthTimeout and parseErr are set so the
subsequent context.WithTimeout and health.WaitForHealthy calls do not receive a
zero or negative timeout.
In `@cli/cmd/status.go`:
- Around line 251-272: After filtering by statusServices, replace the single “No
containers running” empty-state handling with filter-aware messages: when
containers becomes empty and statusServices != "" check the filter map you built
(the variable named filter) — if filter is empty (all requested names were
invalid) return or print a clear message like “no valid service names provided”
(include the invalid names if you want), otherwise print “no containers match
requested services” to indicate there are containers but none matched; adjust
uses of out.Warn/out.Info accordingly around the containers slice handling
(refer to statusServices, serviceNamePattern, filter, and containers in the
existing block).
- Around line 68-76: The parsed duration from time.ParseDuration(statusInterval)
can be zero or negative which will cause time.NewTicker to panic; after parsing
(interval, parseErr := time.ParseDuration(statusInterval)) add a guard that
rejects non-positive values (interval <= 0) with a clear fmt.Errorf like
"invalid --interval %q: must be > 0", and only call runStatusWatch(cmd, state,
opts, interval) when interval > 0; update any callers or tests as needed to
expect this validation (refer to statusInterval, statusWatch, runStatusWatch,
time.ParseDuration/time.NewTicker).
In `@cli/cmd/stop.go`:
- Around line 58-65: The parsed timeout value may be negative; after calling
time.ParseDuration for stopTimeout (the dur variable in the stop.go block that
builds downArgs), validate that dur is positive (dur > 0) and return a clear
error like "invalid --timeout %q: must be positive" if not; update the branch
that appends "--timeout" to downArgs to only do so when the duration is positive
to avoid passing negative values to docker compose down.
---
Outside diff comments:
In `@cli/cmd/logs.go`:
- Around line 50-114: Extract the validation block in runLogs (the --tail check,
the --since/--until pattern check, and the service name loop) into a new helper
function named validateLogsArgs that accepts the necessary inputs (e.g.,
logTail, logSince, logUntil, args) and returns an error; replace the inlined
validation in runLogs with a single call to validateLogsArgs and propagate its
error, keeping all existing error messages and regex/variable references
(timeFilterPattern, serviceNamePattern) unchanged so compile-time references
like runLogs, validateLogsArgs, timeFilterPattern, serviceNamePattern, and
composeRun remain valid.
In `@cli/cmd/status.go`:
- Around line 242-293: The --services filtering block in printContainerStates
should be extracted into a helper to reduce function size; create a function
like filterContainersByServices(out *ui.UI, containers []Container, servicesFlag
string) []Container (or similar) that validates each comma-separated service
name against serviceNamePattern, calls out.Warn for invalid names (same message
used now), builds and returns the filtered slice, and then replace the inline
filter code in printContainerStates with a single call to that helper; keep
parseContainerJSON, statusServices, serviceNamePattern, and renderContainerTable
usage unchanged so behavior is identical.
🪄 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: e52100cb-30c1-4fcc-b9c0-8e0056f87e17
📒 Files selected for processing (11)
CLAUDE.mdcli/cmd/config.gocli/cmd/config_ext_test.gocli/cmd/init.gocli/cmd/logs.gocli/cmd/root.gocli/cmd/start.gocli/cmd/status.gocli/cmd/stop.gocli/internal/config/state.gocli/internal/config/state_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 (3)
**/*.{py,ts,tsx,go}
📄 CodeRabbit inference engine (CLAUDE.md)
Functions should be under 50 lines; files should be under 800 lines
Files:
cli/cmd/logs.gocli/cmd/root.gocli/cmd/stop.gocli/cmd/init.gocli/internal/config/state_test.gocli/cmd/status.gocli/internal/config/state.gocli/cmd/start.gocli/cmd/config.gocli/cmd/config_ext_test.go
cli/**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
cli/**/*.go: Usego -C clito run Go commands - never usecd cliin shell scripts as it poisons the working directory
Go CLI accepts persistent global flags:--data-dir,--skip-verify,--quiet,--verbose,--no-color,--plain,--json,--yeswith precedence: flag > env var > config > default
Files:
cli/cmd/logs.gocli/cmd/root.gocli/cmd/stop.gocli/cmd/init.gocli/internal/config/state_test.gocli/cmd/status.gocli/internal/config/state.gocli/cmd/start.gocli/cmd/config.gocli/cmd/config_ext_test.go
**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Do not use real vendor names in documentation, use generic names instead - vendor names only appear in design pages,
.claude/files, third-party imports, and provider presets
Files:
CLAUDE.md
🧠 Learnings (18)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T22:44:47.320Z
Learning: Applies to cli/**/*.go : Go CLI accepts persistent global flags: `--data-dir`, `--skip-verify`, `--quiet`, `--verbose`, `--no-color`, `--plain`, `--json`, `--yes` with precedence: flag > env var > config > default
📚 Learning: 2026-03-27T22:44:47.320Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T22:44:47.320Z
Learning: Applies to cli/**/*.go : Go CLI accepts persistent global flags: `--data-dir`, `--skip-verify`, `--quiet`, `--verbose`, `--no-color`, `--plain`, `--json`, `--yes` with precedence: flag > env var > config > default
Applied to files:
cli/cmd/logs.gocli/cmd/root.goCLAUDE.mdcli/cmd/init.gocli/internal/config/state.gocli/cmd/start.gocli/cmd/config.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/logs.gocli/cmd/root.gocli/cmd/init.gocli/cmd/status.gocli/cmd/start.gocli/cmd/config.gocli/cmd/config_ext_test.go
📚 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
📚 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 docker/Dockerfile.sandbox : Docker sandbox: `synthorg-sandbox` — Python 3.14 + Node.js + git, non-root (UID 10001), agent code execution sandbox
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: 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/init.gocli/cmd/start.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/init.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/init.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/config/state_test.gocli/cmd/config_ext_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/config/state_test.gocli/cmd/config_ext_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/config/state_test.gocli/cmd/config_ext_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/config/state_test.gocli/cmd/config_ext_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/cmd/status.gocli/cmd/config.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/start.gocli/cmd/config_ext_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/config_ext_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/config_ext_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/config_ext_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/config_ext_test.go
🪛 GitHub Check: CodeQL
cli/cmd/config.go
[failure] 389-389: Uncontrolled data used in path expression
This path depends on a user-provided value.
This path depends on a user-provided value.
This path depends on a user-provided value.
This path depends on a user-provided value.
[failure] 615-615: Command built from user-controlled sources
This command depends on a user-provided value.
This command depends on a user-provided value.
🔇 Additional comments (9)
cli/cmd/config.go (3)
379-400: Path construction is secure viasafeStateDirvalidation.The CodeQL warning about "uncontrolled data used in path expression" at line 389 is a false positive. The
safeStateDir(state)call on lines 382-385 appliesconfig.SecurePath()validation, which canonicalizes the path and prevents traversal attacks. The subsequentfilepath.Joinwith a constant filename ("compose.yml") is safe.
613-628: Editor command execution is intentionally user-controlled.The CodeQL warning about "command built from user-controlled sources" at line 615 is acknowledged by the
//nolint:goseccomment. Respecting$VISUAL/$EDITORis standard Unix behavior for CLI tools. The user controls their own environment variables, and restricting this would break expected workflows. The implementation correctly passes the config path as a separate argument rather than interpolating it into a shell string.
286-333: LGTM!The
applyConfigValuefunction correctly validates and applies config values using helper functions (setBool,setPort,setEnum) for common patterns while handling special cases (docker_sock, image_tag) inline. The mix of early returns and fall-through to the finalreturn nilis appropriate for the different validation requirements.cli/internal/config/state.go (2)
31-43: LGTM!The new display preference and auto-behavior fields are well-structured with appropriate
omitemptytags for optional string fields. The design correctly uses empty strings for "use default" semantics and boolean defaults (false) for "prompt interactively" behavior.
177-199: LGTM!The validator functions and name helpers follow the established pattern consistently. Each mode validator uses its corresponding map, and the names functions reuse
sortedKeys()for consistent error message formatting.cli/internal/config/state_test.go (1)
585-617: LGTM! Consider adding fuzz tests for the remaining validators.The fuzz tests for
IsValidColorModeandIsValidOutputModefollow the establishedFuzzIsValidBoolpattern correctly. For consistency, consider adding similar fuzz targets forIsValidTimestampModeandIsValidHintsMode.cli/cmd/root.go (1)
110-128: Config-driven overrides correctly maintain precedence.The implementation properly respects
flag > env > config > defaultprecedence. Config values are only applied when the corresponding flag wasn't explicitly set (!flagNoColor,!flagJSON), ensuring flags and env vars (resolved above this block) take priority.One consideration: when
state.Color == "always", the code setsopts.NoColor = falsewhich forces color output. This overrides the env-var-basednoColorFromEnv()check done earlier. This is the documented behavior ("color always forces color on non-TTYs") per CLAUDE.md line 87, so this is correct.CLAUDE.md (1)
113-138: LGTM!The documentation accurately reflects the implementation: 17 settable keys (matching
supportedConfigKeys), 19 gettable keys (matchinggettableConfigKeys), and per-command flags align with the respective command implementations. The compose-affecting keys list matchescomposeAffectingKeysin config.go.cli/cmd/init.go (1)
50-76: LGTM! Non-interactive mode detection is well-designed.The
initAllFlagsSet()function correctly identifies when all required flags are provided for non-interactive execution, while keeping--image-tagand--channeloptional with sensible defaults. The error message on line 75 clearly lists the required flags when interactive mode isn't available.
There was a problem hiding this comment.
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 (2)
cli/cmd/root.go (1)
74-90:⚠️ Potential issue | 🟠 MajorConfig overrides are clobbering higher-precedence choices.
NO_COLORfrom env can still be undone bycolor=alwaysbecause this code only guards onflagNoColor, not the already-resolvedopts.NoColor. Likewiseoutput=jsoncan flipopts.JSONon after the--plain/--jsonconflict check has already passed. Apply config only when no higher-precedence source has already chosen the setting, or rerun conflict validation afterwards. Based on learningscli/**/*.go: Go CLI accepts persistent global flags:--data-dir,--skip-verify,--quiet,--verbose,--no-color,--plain,--json,--yeswith precedence: flag > env var > config > default.Also applies to: 123-140
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cli/cmd/root.go` around lines 74 - 90, The config overrides are currently clobbering higher-precedence choices: modify the applyConfigOverrides usage so it only writes settings into GlobalOpts when a higher-precedence source hasn't already set them (i.e. check the already-resolved fields on opts such as NoColor, JSON, Plain, Yes, DataDir, SkipVerify, Quiet, Verbose before applying config values), or alternatively call applyConfigOverrides as-is but re-run the flag conflict/validation logic afterwards (e.g. re-check the --plain/--json conflict and validHintsMode(opts.Hints) after applyConfigOverrides); specifically update the call site around opts := &GlobalOpts{...} and applyConfigOverrides(opts) to respect resolved values (opts.NoColor, opts.JSON, opts.Plain, flagNoColor, flagJSON, flagPlain are key symbols) so flag > env > config precedence is preserved.cli/cmd/status.go (1)
108-136:⚠️ Potential issue | 🟠 Major
status --jsonstill emits mixed text and JSON.
runStatusOncealways prints version info, Docker/Compose lines, resource usage, and links, while only some callees switch to JSON. The result is not machine-readable. This needs a dedicated JSON renderer instead of reusing the text path.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cli/cmd/status.go` around lines 108 - 136, runStatusOnce currently mixes text output with partial JSON which breaks machine-readable mode; change runStatusOnce to branch on opts.JSON and implement a dedicated JSON rendering path: when opts.JSON is true, do not call printVersionInfo, out.KeyValue lines, printContainerStates, printResourceUsage, printHealthStatus or printLinks; instead gather the same data into a single struct (fields: version info, Docker/Compose info from docker.Detect, container states, resource usage, health status, links) and marshal it to JSON to write to cmd.OutOrStdout(); to support this either reuse or add data-returning helpers (e.g., gatherContainerStates, gatherResourceUsage, gatherHealthStatus, gatherLinks) alongside the existing printer functions so the JSON path can call those to assemble the payload before json.Marshal and writing.
♻️ Duplicate comments (1)
cli/cmd/config_ext_test.go (1)
315-370: 🛠️ Refactor suggestion | 🟠 MajorSplit these table-driven tests behind a small helper.
TestConfigSetAutoBehaviorKeysandTestConfigGetNewKeysare still at/over the repo's 50-line limit and repeat the same save/execute/load/assert flow. Extracting the command execution into a helper will keep each test within the guideline and make future cases cheaper to add. As per coding guidelines**/*.{py,ts,tsx,go}: Functions should be under 50 lines; files should be under 800 lines.Also applies to: 511-560
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cli/cmd/config_ext_test.go` around lines 315 - 370, The tests TestConfigSetAutoBehaviorKeys and TestConfigGetNewKeys duplicate the same save/execute/load/assert flow and exceed the 50-line guideline; extract the repeated steps into a small helper (e.g., execConfigSetAndLoad or runConfigCmd) that accepts parameters like key, value, and data dir and returns the loaded config or error, then refactor both TestConfigSetAutoBehaviorKeys and TestConfigGetNewKeys to call that helper for the set/verify and get/verify logic, keeping each test under 50 lines and removing duplicated code in the test file.
🤖 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/config.go`:
- Around line 278-282: When ImageTag changes (composeAffectingKeys[key] branch
and the unset image_tag path), clear the cached pinned digests so
regenerateCompose doesn't reuse stale values: detect modifications to ImageTag
and set state.VerifiedDigests = nil (or call the clearing helper if one exists)
before calling regenerateCompose in both the composeAffectingKeys handling and
the unset-image_tag handling (affecting the blocks around regenerateCompose and
the paths referenced at lines ~323-327 and ~406-412); ensure the same
invalidation is applied wherever ImageTag is modified so new tags are not
overwritten by previous pinned digests.
- Around line 425-433: The noop currently resets backend_port or web_port via
resetConfigValue(&state, key) and immediately calls regenerateCompose if
composeAffectingKeys[key] is true, which can reintroduce duplicate ports; update
the unset flow to run the same port-conflict check used by setPort (the conflict
validation logic invoked by setPort) before committing the reset: if the key is
a port (backend_port or web_port) simulate applying the default value and call
the setPort conflict check/validator, abort and return the validation error if a
conflict exists, otherwise proceed to call resetConfigValue(&state, key) and
then regenerateCompose(state) as before.
- Around line 529-532: When building the config list entries we currently use
the raw state value from configGetValue(state, key) but then call
resolveSource(key, val, configGetValue(defaults, key)), which can label the row
"default" or "env" while the displayed value remains empty or stale; change the
entry construction so you first compute defaultVal := configGetValue(defaults,
key), call source := resolveSource(key, stateVal, defaultVal), then set
effectiveVal to the value that matches that source (if source == "default" use
defaultVal, if source == "env" use configGetValue(env, key), otherwise use the
original stateVal), and finally append configEntry{Key: key, Value:
effectiveVal, Source: source} so gettableConfigKeys entries show the
resolved/effective value rather than the raw state value.
In `@cli/cmd/init.go`:
- Around line 162-177: Add explicit validation for CLI port flags inside
validateInitFlags: when initBackendPort != 0 check it's within 1..65535 and
return an error if out of range; do the same for initWebPort (when initWebPort
!= 0); and if both initBackendPort and initWebPort are non-zero ensure they are
not equal and return a descriptive error if they are. Update the error messages
to mention the flag names and valid range and ensure these checks run as part of
validateInitFlags before the init mode selection.
- Around line 86-98: The init flow still calls confirmReinit which always
prompts interactively; detect the non-interactive "--yes" option (opts.Yes or
global yes flag) and short-circuit the prompt: if opts.Yes is true, skip calling
confirmReinit and treat the re-init as accepted (set kept to a non-nil pointer
to the empty string so existing state.SettingsKey remains unchanged unless an
explicit key is provided), otherwise call confirmReinit as before; update the
block around config.StatePath/state.SettingsKey to use this branch so re-running
init --yes won't hang.
In `@cli/cmd/status.go`:
- Around line 56-65: The current --check branch treats any 2xx as healthy
without validating the response body; update the logic so after receiving a 2xx
from fetchHealth you parse and validate the response payload (e.g., unmarshal
into a HealthResponse struct and confirm data.status == "ok" and required fields
exist) before returning success. Change or extend fetchHealth (or call a new
helper) to return the response body (or parsed struct) in addition to statusCode
so statusCheck can inspect it; if parsing fails or the health payload is not as
expected, return NewExitError(ExitUnhealthy, nil) (or
NewExitError(ExitUnreachable, err) for network errors) instead of nil. Ensure
you reference the existing symbols statusCheck, fetchHealth, NewExitError,
ExitUnhealthy, and ExitUnreachable when making the change.
---
Outside diff comments:
In `@cli/cmd/root.go`:
- Around line 74-90: The config overrides are currently clobbering
higher-precedence choices: modify the applyConfigOverrides usage so it only
writes settings into GlobalOpts when a higher-precedence source hasn't already
set them (i.e. check the already-resolved fields on opts such as NoColor, JSON,
Plain, Yes, DataDir, SkipVerify, Quiet, Verbose before applying config values),
or alternatively call applyConfigOverrides as-is but re-run the flag
conflict/validation logic afterwards (e.g. re-check the --plain/--json conflict
and validHintsMode(opts.Hints) after applyConfigOverrides); specifically update
the call site around opts := &GlobalOpts{...} and applyConfigOverrides(opts) to
respect resolved values (opts.NoColor, opts.JSON, opts.Plain, flagNoColor,
flagJSON, flagPlain are key symbols) so flag > env > config precedence is
preserved.
In `@cli/cmd/status.go`:
- Around line 108-136: runStatusOnce currently mixes text output with partial
JSON which breaks machine-readable mode; change runStatusOnce to branch on
opts.JSON and implement a dedicated JSON rendering path: when opts.JSON is true,
do not call printVersionInfo, out.KeyValue lines, printContainerStates,
printResourceUsage, printHealthStatus or printLinks; instead gather the same
data into a single struct (fields: version info, Docker/Compose info from
docker.Detect, container states, resource usage, health status, links) and
marshal it to JSON to write to cmd.OutOrStdout(); to support this either reuse
or add data-returning helpers (e.g., gatherContainerStates, gatherResourceUsage,
gatherHealthStatus, gatherLinks) alongside the existing printer functions so the
JSON path can call those to assemble the payload before json.Marshal and
writing.
---
Duplicate comments:
In `@cli/cmd/config_ext_test.go`:
- Around line 315-370: The tests TestConfigSetAutoBehaviorKeys and
TestConfigGetNewKeys duplicate the same save/execute/load/assert flow and exceed
the 50-line guideline; extract the repeated steps into a small helper (e.g.,
execConfigSetAndLoad or runConfigCmd) that accepts parameters like key, value,
and data dir and returns the loaded config or error, then refactor both
TestConfigSetAutoBehaviorKeys and TestConfigGetNewKeys to call that helper for
the set/verify and get/verify logic, keeping each test under 50 lines and
removing duplicated code in the test file.
🪄 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: 21127ede-925f-48f9-8052-680f9b75a849
📒 Files selected for processing (8)
cli/cmd/config.gocli/cmd/config_ext_test.gocli/cmd/init.gocli/cmd/logs.gocli/cmd/root.gocli/cmd/start.gocli/cmd/status.gocli/cmd/stop.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 (2)
**/*.{py,ts,tsx,go}
📄 CodeRabbit inference engine (CLAUDE.md)
Functions should be under 50 lines; files should be under 800 lines
Files:
cli/cmd/stop.gocli/cmd/logs.gocli/cmd/init.gocli/cmd/status.gocli/cmd/config_ext_test.gocli/cmd/root.gocli/cmd/config.gocli/cmd/start.go
cli/**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
cli/**/*.go: Usego -C clito run Go commands - never usecd cliin shell scripts as it poisons the working directory
Go CLI accepts persistent global flags:--data-dir,--skip-verify,--quiet,--verbose,--no-color,--plain,--json,--yeswith precedence: flag > env var > config > default
Files:
cli/cmd/stop.gocli/cmd/logs.gocli/cmd/init.gocli/cmd/status.gocli/cmd/config_ext_test.gocli/cmd/root.gocli/cmd/config.gocli/cmd/start.go
🧠 Learnings (18)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T22:44:47.320Z
Learning: Applies to cli/**/*.go : Go CLI accepts persistent global flags: `--data-dir`, `--skip-verify`, `--quiet`, `--verbose`, `--no-color`, `--plain`, `--json`, `--yes` with precedence: flag > env var > config > default
📚 Learning: 2026-03-27T22:44:47.320Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T22:44:47.320Z
Learning: Applies to cli/**/*.go : Go CLI accepts persistent global flags: `--data-dir`, `--skip-verify`, `--quiet`, `--verbose`, `--no-color`, `--plain`, `--json`, `--yes` with precedence: flag > env var > config > default
Applied to files:
cli/cmd/logs.gocli/cmd/init.gocli/cmd/status.gocli/cmd/config_ext_test.gocli/cmd/root.gocli/cmd/config.gocli/cmd/start.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/init.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/init.gocli/cmd/status.gocli/cmd/config_ext_test.gocli/cmd/root.gocli/cmd/start.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/status.gocli/cmd/root.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/status.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/status.gocli/cmd/root.gocli/cmd/config.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/config_ext_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/config_ext_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/config_ext_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/config_ext_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/config_ext_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/config_ext_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 .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/config_ext_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 : 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:
cli/cmd/config_ext_test.go
📚 Learning: 2026-03-27T22:44:47.320Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T22:44:47.320Z
Learning: Applies to **/*.{py,ts,tsx,go} : Functions should be under 50 lines; files should be under 800 lines
Applied to files:
cli/cmd/config_ext_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/root.go
📚 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 docker/Dockerfile.sandbox : Docker sandbox: `synthorg-sandbox` — Python 3.14 + Node.js + git, non-root (UID 10001), agent code execution sandbox
Applied to files:
cli/cmd/config.go
🪛 GitHub Check: CodeQL
cli/cmd/config.go
[failure] 402-402: Uncontrolled data used in path expression
This path depends on a user-provided value.
This path depends on a user-provided value.
This path depends on a user-provided value.
This path depends on a user-provided value.
[failure] 632-632: Command built from user-controlled sources
This command depends on a user-provided value.
This command depends on a user-provided value.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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/config_ext_test.go`:
- Around line 315-370: Test TestConfigSetAutoBehaviorKeys exceeds the 50-line
guideline; refactor to reduce length by extracting the repeated setup/assert
logic into a helper (e.g., runConfigToggle or setAndVerify) and reuse it inside
the table-driven loop. Specifically, keep the tests table and t.Run in
TestConfigSetAutoBehaviorKeys but move the temp dir/state creation, saving
(config.Save), command invocation via rootCmd (SetOut/SetErr/SetArgs/Execute),
and load/assert steps (config.Load and tt.field checks) into a helper function
referenced from the loop to collapse duplicated blocks and bring the test under
50 lines while preserving behavior.
- Around line 504-514: Test expectation uses config.SecurePath(dir) which
doesn't resolve symlinks, so before computing want call filepath.EvalSymlinks on
the temp dir and use that result to build the expected path with
config.StatePath; also add the "path/filepath" import. Specifically: replace
using dir directly with resolvedDir := filepath.EvalSymlinks(dir) (handle the
error in the test) and then compute want := config.StatePath(resolvedDir) while
keeping the existing call to config.SecurePath(dir) for got, referencing
SecurePath and StatePath to locate the changes.
🪄 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: 4fa2544a-ff19-46e3-98f0-9262fed03bad
📒 Files selected for processing (1)
cli/cmd/config_ext_test.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
cli/**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
cli/**/*.go: Use native testing.F fuzz functions (FuzzXxx) for fuzz testing. Run fuzz with go -C cli test -fuzz=FuzzName -fuzztime=30s ./internal/compose/.
Use go -C cli to run Go commands from the repo root (changes directory internally). Never use cd cli -- it poisons the shell cwd for subsequent commands.
Register golangci-lint as a tool in cli/go.mod and run via go -C cli tool golangci-lint run.
Files:
cli/cmd/config_ext_test.go
🧠 Learnings (10)
📚 Learning: 2026-03-28T09:13:12.113Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T09:13:12.113Z
Learning: Applies to cli/**/*.go : Use native testing.F fuzz functions (FuzzXxx) for fuzz testing. Run fuzz with go -C cli test -fuzz=FuzzName -fuzztime=30s ./internal/compose/.
Applied to files:
cli/cmd/config_ext_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/config_ext_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/config_ext_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/config_ext_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/config_ext_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/config_ext_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/config_ext_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/config_ext_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 : 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:
cli/cmd/config_ext_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 .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/config_ext_test.go
🪛 GitHub Actions: CLI
cli/cmd/config_ext_test.go
[error] 513-513: TestConfigPathPrintsPath failed: config path = "/private/var/folders/tb/y368xp_x10s3ty1b_mtl5mxr0000gn/T/TestConfigPathPrintsPath58644409/001/config.json", want "/var/folders/tb/y368xp_x10s3ty1b_mtl5mxr0000gn/T/TestConfigPathPrintsPath58644409/001/config.json".
🔇 Additional comments (5)
cli/cmd/config_ext_test.go (5)
12-20: Good state isolation helper.This cleanup function addresses the cross-test leakage concern by resetting
rootCmdstate after each test. The use oft.Cleanupensures proper cleanup even if the test fails.
68-94: Port uniqueness tests are correct.The test correctly verifies that
backend_portcannot equalweb_portand vice versa, matching the validation logic insetPort.
568-601: Well-structured fuzz test using nativetesting.F.The fuzz test correctly:
- Uses the native
testing.Fas per coding guidelines- Seeds with representative edge cases (valid ports, boundary values, invalid inputs)
- Validates that the command behavior matches the expected validation rules (1-65535, not equal to default web_port 3000)
- Uses
resetRootCmdfor state isolation
603-634: Correct case-sensitive validation in fuzz test.The fuzz test properly verifies that
colorvalues are case-sensitive (rejecting "Always", "NEVER" while accepting "always", "auto", "never"), matching thesetEnumimplementation that uses a case-sensitive map lookup.
22-46: Good test pattern with proper state isolation.The test demonstrates the correct pattern:
resetRootCmd(t)at the start, isolated temp directory per test, and explicit verification of persisted state viaconfig.Load. This pattern is consistently applied across all tests in the file.
…/status/logs PR 2 of 3 for #886 -- adds ~1,680 lines of Go across 10 files: Config system extensions: - 9 new State fields (color, output, timestamps, hints, 5 auto-behavior bools) - 4 validator pairs with tests and fuzz tests - config set expanded from 3 to 17 settable keys with validation - Compose-affecting keys (ports, sandbox, docker_sock, image_tag, log_level) trigger automatic compose.yml regeneration - New subcommands: config unset, config list, config path, config edit - config list shows resolved value + source (env/config/default) - config edit opens in $VISUAL/$EDITOR with post-edit validation GlobalOpts wiring: - hints/color/output read from config into GlobalOpts - Flag > env > config > default precedence maintained Per-command flags: - init: --backend-port, --web-port, --sandbox, --image-tag, --channel, --log-level (fully non-interactive when all provided) - start: --no-wait, --timeout, --no-pull, --dry-run, --no-detach, --no-verify - stop: --timeout/-t, --volumes (no -v shorthand -- global conflict) - status: --watch/-w, --interval, --wide, --no-trunc, --services, --check - logs: --since, --until, --timestamps/-t, --no-log-prefix Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pre-reviewed by 5 agents (go-reviewer, security-reviewer, conventions-enforcer, docs-consistency, issue-resolution-verifier). 21 findings addressed: - Refactored 7 functions exceeding 50-line limit (applyConfigValue uses setBool/setPort/setEnum helpers, runStart split into validateStartFlags/ printStartDryRun/startContainers/startDetached, runConfigShow extracted printConfigFields, runConfigUnset extracted resetConfigValue, runConfigGet deduped via configGetValue) - Split config_test.go (1144 lines) into config_test.go (548) + config_ext_test.go (602) - Fixed $EDITOR splitting: resolveEditor now returns (bin, args) to handle "code --wait" etc. - Wired color=always into GlobalOpts (forces color on non-TTYs) - Added --since/--until format validation in logs.go - Added --services name validation in status.go - Moved --timeout parse to top of runStart (before Docker work) - Parsed --interval early in status.go (catches invalid values even without --watch) - Passed wide/noTrunc as params to renderContainerTable (not global vars) - Fixed config list to iterate gettableConfigKeys (memory_backend/persistence_backend now visible) - Added configGetValue cases for memory_backend/persistence_backend - Updated CLAUDE.md with config subcommands, per-command flags, and config wiring docs - Used strconv.Itoa/FormatBool consistently in dry-run output - Documented optional vs required init flags Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
368a826 to
f401809
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
cli/cmd/init.go (1)
250-263:⚠️ Potential issue | 🟠 MajorPreserve
opts.DataDirwhen the wizard opens.
runInitalready has the resolved data dir inopts.DataDir, but this helper resetsa.dirback toconfig.DefaultState().DataDir. An explicit--data-dirthen gets ignored as soon as the interactive branch is taken, and a plain Enter can initialize the wrong directory.💡 Proposed fix
- answers, err = runSetupFormWithOverrides() + answers, err = runSetupFormWithOverrides(opts.DataDir) if err != nil { return err }-func runSetupFormWithOverrides() (setupAnswers, error) { +func runSetupFormWithOverrides(dataDir string) (setupAnswers, error) { defaults := config.DefaultState() + if dataDir == "" { + dataDir = defaults.DataDir + } a := setupAnswers{ - dir: defaults.DataDir, + dir: dataDir, backendPortStr: fmt.Sprintf("%d", defaults.BackendPort), webPortStr: fmt.Sprintf("%d", defaults.WebPort), sandbox: defaults.Sandbox, dockerSock: defaultDockerSock(),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cli/cmd/init.go` around lines 250 - 263, The interactive setup resets a.dir to config.DefaultState().DataDir and thereby drops the already-resolved opts.DataDir; change runSetupFormWithOverrides to accept the resolved data dir (e.g., add a parameter resolvedDataDir string or pass opts) and after building the defaults-based setupAnswers (but before applyFlagOverrides) set a.dir = resolvedDataDir if resolvedDataDir != "" so an explicit --data-dir or previously-resolved opts.DataDir is preserved when the wizard opens; keep applyFlagOverrides(&a) as-is.
♻️ Duplicate comments (1)
cli/cmd/config.go (1)
544-548:⚠️ Potential issue | 🟡 Minor
config listshows state value, not resolved effective value.When
resolveSourcereturns"env", the displayed value is stillconfigGetValue(state, key)rather than the environment variable's value. For example, ifSYNTHORG_BACKEND_PORT=9000but the config file hasbackend_port: 3001, the output shows[env] 3001instead of[env] 9000.🐛 Proposed fix to display the effective value
for _, key := range gettableConfigKeys { - val := configGetValue(state, key) - source := resolveSource(key, val, configGetValue(defaults, key)) - entries = append(entries, configEntry{Key: key, Value: val, Source: source}) + stateVal := configGetValue(state, key) + defaultVal := configGetValue(defaults, key) + source := resolveSource(key, stateVal, defaultVal) + effectiveVal := stateVal + if source == "env" { + if envVar := envVarForKey(key); envVar != "" { + effectiveVal = os.Getenv(envVar) + } + } else if source == "default" { + effectiveVal = defaultVal + } + entries = append(entries, configEntry{Key: key, Value: effectiveVal, Source: source}) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cli/cmd/config.go` around lines 544 - 548, The list output is showing the stored state value instead of the effective value when resolveSource returns "env"; update the loop that builds entries so it picks the value corresponding to the resolved source rather than always using configGetValue(state, key). Specifically, after calling source := resolveSource(key, val, configGetValue(defaults, key)), compute an effective value (e.g., use configGetValue on the environment/defaults/state based on source) and append configEntry{Key: key, Value: effectiveValue, Source: source}; update the loop referencing gettableConfigKeys, configGetValue, resolveSource, entries and configEntry accordingly.
🤖 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/init.go`:
- Around line 108-130: handleReinit currently prompts for overwrite even when
the caller intended a non-interactive run; modify handleReinit so that if an
existing config is found and the run is non-interactive (i.e., opts.Yes is false
but the command was invoked in a non-interactive/flag-driven mode), it does not
call confirmReinit but instead returns a clear error asking the user/CI to pass
--yes to force overwrite. Locate handleReinit and change the flow after loading
oldState: if opts.Yes is false and the caller is non-interactive (use the
existing GlobalOpts field that indicates non-interactive runs or detect that
required init flags were provided), return false with an error message like
"existing config found; pass --yes to overwrite" instead of calling
confirmReinit; keep the existing behavior of preserving oldState.SettingsKey
when opts.Yes is true.
In `@cli/cmd/start.go`:
- Around line 106-115: printStartDryRun currently echoes raw flags rather than
the command's effective behavior; update it so "Skip verify" reflects
opts.SkipVerify OR startNoPull, "Skip pull" reflects startNoPull, "Detached"
remains !startNoDetach, and "Health check" is disabled when either startNoWait
OR startNoDetach is true (i.e. health := !(startNoWait || startNoDetach)).
Modify printStartDryRun to compute those booleans before calling out.KeyValue so
the dry-run output matches actual branch behavior (referencing printStartDryRun,
opts.SkipVerify, startNoPull, startNoDetach, startNoWait).
In `@cli/cmd/stop.go`:
- Around line 60-67: The code silently truncates fractional durations when
converting dur to integer seconds for Docker Compose's --timeout; modify the
logic around dur, stopTimeout and downArgs so that after parsing (dur :=
time.ParseDuration(stopTimeout)) you validate the duration is a whole number of
seconds (e.g. dur%time.Second == 0 or dur.Round(time.Second) == dur) and return
a clear error if it contains fractional seconds, keep the existing non-negative
check, and only then append "--timeout" with strconv.Itoa(int(dur.Seconds())) to
downArgs.
---
Outside diff comments:
In `@cli/cmd/init.go`:
- Around line 250-263: The interactive setup resets a.dir to
config.DefaultState().DataDir and thereby drops the already-resolved
opts.DataDir; change runSetupFormWithOverrides to accept the resolved data dir
(e.g., add a parameter resolvedDataDir string or pass opts) and after building
the defaults-based setupAnswers (but before applyFlagOverrides) set a.dir =
resolvedDataDir if resolvedDataDir != "" so an explicit --data-dir or
previously-resolved opts.DataDir is preserved when the wizard opens; keep
applyFlagOverrides(&a) as-is.
---
Duplicate comments:
In `@cli/cmd/config.go`:
- Around line 544-548: The list output is showing the stored state value instead
of the effective value when resolveSource returns "env"; update the loop that
builds entries so it picks the value corresponding to the resolved source rather
than always using configGetValue(state, key). Specifically, after calling source
:= resolveSource(key, val, configGetValue(defaults, key)), compute an effective
value (e.g., use configGetValue on the environment/defaults/state based on
source) and append configEntry{Key: key, Value: effectiveValue, Source: source};
update the loop referencing gettableConfigKeys, configGetValue, resolveSource,
entries and configEntry accordingly.
🪄 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: 5306613c-3782-4d17-a802-61615d549ecf
📒 Files selected for processing (11)
CLAUDE.mdcli/cmd/config.gocli/cmd/config_ext_test.gocli/cmd/init.gocli/cmd/logs.gocli/cmd/root.gocli/cmd/start.gocli/cmd/status.gocli/cmd/stop.gocli/internal/config/state.gocli/internal/config/state_test.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
cli/**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
cli/**/*.go: Use native testing.F fuzz functions (FuzzXxx) for fuzz testing. Run fuzz with go -C cli test -fuzz=FuzzName -fuzztime=30s ./internal/compose/.
Use go -C cli to run Go commands from the repo root (changes directory internally). Never use cd cli -- it poisons the shell cwd for subsequent commands.
Register golangci-lint as a tool in cli/go.mod and run via go -C cli tool golangci-lint run.
Files:
cli/cmd/logs.gocli/cmd/stop.gocli/internal/config/state_test.gocli/cmd/root.gocli/cmd/status.gocli/cmd/init.gocli/internal/config/state.gocli/cmd/start.gocli/cmd/config.gocli/cmd/config_ext_test.go
**/*.{md,yaml,yml,json,toml}
📄 CodeRabbit inference engine (CLAUDE.md)
All configuration files and documentation must be valid according to their format specifications.
Files:
CLAUDE.md
🧠 Learnings (20)
📓 Common learnings
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...
📚 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/logs.gocli/cmd/root.gocli/cmd/status.gocli/cmd/init.gocli/cmd/config_ext_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/cmd/logs.gocli/cmd/root.gocli/cmd/status.gocli/cmd/config.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/logs.gocli/cmd/root.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/logs.gocli/cmd/root.gocli/cmd/status.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/logs.gocli/cmd/root.gocli/cmd/status.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/logs.gocli/cmd/init.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:
cli/cmd/logs.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/config/state_test.gocli/cmd/config_ext_test.go
📚 Learning: 2026-03-28T09:13:12.113Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T09:13:12.113Z
Learning: Applies to cli/**/*.go : Use native testing.F fuzz functions (FuzzXxx) for fuzz testing. Run fuzz with go -C cli test -fuzz=FuzzName -fuzztime=30s ./internal/compose/.
Applied to files:
cli/internal/config/state_test.gocli/cmd/config_ext_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/config/state_test.gocli/cmd/config_ext_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/config/state_test.gocli/cmd/config_ext_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/config/state_test.gocli/cmd/config_ext_test.go
📚 Learning: 2026-03-28T09:13:12.113Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T09:13:12.113Z
Learning: Applies to cli/**/*.go : Use go -C cli to run Go commands from the repo root (changes directory internally). Never use cd cli -- it poisons the shell cwd for subsequent commands.
Applied to files:
cli/cmd/root.go
📚 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 docker/Dockerfile.sandbox : Docker sandbox: `synthorg-sandbox` — Python 3.14 + Node.js + git, non-root (UID 10001), agent code execution sandbox
Applied to files:
cli/cmd/config.goCLAUDE.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/config_ext_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/config_ext_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 .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/config_ext_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 : 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:
cli/cmd/config_ext_test.go
📚 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 (21)
cli/cmd/stop.go (1)
29-30: Nice addition of explicit stop flags.
--timeoutand--volumesare wired cleanly and keepstopusable in non-interactive flows.CLAUDE.md (1)
87-138: LGTM!The documentation updates accurately reflect the new config system behavior and per-command flags. The config subcommand table and compose-affecting keys list are consistent with the implementation in
cli/cmd/config.go.cli/internal/config/state.go (3)
31-44: LGTM!The new
Statefields are well-structured with appropriate JSON tags. Display preference strings useomitemptyto avoid persisting defaults, while auto-behavior bools use Go's zero-value semantics correctly.
177-199: LGTM!The validation helpers follow a consistent pattern and reuse
sortedKeysfor generating allowed value lists. Case-sensitive validation is correct for CLI config values.
224-235: LGTM!The
validate()method correctly validates new fields only when non-empty, allowing empty strings to represent "use default" behavior.cli/internal/config/state_test.go (2)
406-493: LGTM!Comprehensive unit tests covering valid values, case sensitivity, and common invalid inputs for all new mode validators.
585-618: LGTM!Fuzz tests correctly use native
testing.Ffunctions as per coding guidelines and verify thatIsValid*Modereturns true only for exact matches. As per coding guidelines: "Use native testing.F fuzz functions (FuzzXxx) for fuzz testing."cli/cmd/logs.go (2)
99-120: LGTM!
buildLogsArgscorrectly constructs compose arguments with flags before the--separator and services after. This follows Docker Compose conventions.
30-32: Pattern correctly validates all Docker Compose time formats. The regex^[0-9a-zA-Z][0-9a-zA-Z:.+\-TZ]*$properly covers ISO 8601 absolute timestamps (e.g.,2013-01-02T13:23:37Z,2013-01-02T13:23:37) and Go duration strings (e.g.,10m,1h30m,10s) that Docker Compose accepts. The required leading alphanumeric character effectively prevents flag injection while maintaining full compatibility with all valid time input formats.cli/cmd/root.go (1)
119-147: LGTM!The
applyConfigOverridesfunction correctly implements the documented precedence (flag > env > config > default). The color handling at lines 132-141 correctly:
- Skips config when flag forces no-color
- Skips config when env vars set no-color (detected via
opts.NoColor)- Applies
state.Color == "never"only when neither flag nor env set it- Applies
state.Color == "always"only when not already forced to no-colorcli/cmd/config.go (3)
278-280: LGTM!Correctly clears
VerifiedDigestswhenimage_tagchanges, preventing stale digest pins from being used with a new tag.
434-442: LGTM!Port conflict validation after reset and digest clearing on
image_tagunset are correctly implemented, addressing the previously identified issues.
671-684: LGTM!The
resolveEditorfunction now correctly handles whitespace-onlyVISUAL/EDITORvalues by checkinglen(parts) == 0afterstrings.Fields, preventing the index-out-of-range panic.cli/cmd/config_ext_test.go (3)
13-21: LGTM!The
resetRootCmdhelper properly isolates test state usingt.Cleanup, ensuring Cobra command state doesn't leak between tests and fuzz iterations.
504-515: LGTM!The symlink resolution fix correctly applies
filepath.EvalSymlinksto the temp directory before computing the expected path, addressing the macOS/var→/private/varsymlink mismatch.
568-634: LGTM!Fuzz tests properly use native
testing.Ffunctions and includeresetRootCmd(t)for state isolation. The validity oracle in each fuzzer correctly matches the production validation logic.cli/cmd/status.go (4)
56-72: LGTM!The
--checkmode now properly validates both HTTP status code and response body, checking thatenvelope.Data.Status == "ok"before returning success. This prevents false positives from 2xx responses with unhealthy payloads.
80-85: LGTM!Non-positive interval values are now rejected with a clear error message before entering watch mode, preventing the
time.NewTickerpanic.
276-281: LGTM!The empty-state message now correctly distinguishes between "No containers running" (no filter) and "No containers match requested services" (filter active).
289-311: LGTM!The
filterByServicesfunction correctly validates service names againstserviceNamePattern, warns about invalid names, and filters containers. The in-place slice filtering pattern (containers[:0]) is efficient and idiomatic Go.cli/cmd/init.go (1)
294-340: Verify duplicate ports are rejected after form input.The new flag validator only covers values supplied on the CLI. In the code shown here,
buildStatestill validates the backend and web ports independently, so the interactive path can submit the same port twice unless a downstream validator rejects it.Run this to confirm whether
config.Saveor compose generation already enforcesBackendPort != WebPort. If nothing downstream rejects equal ports, add the check inbuildStateimmediately after bothparsePortcalls.#!/bin/bash set -euo pipefail echo "=== buildState in cli/cmd/init.go ===" sed -n '294,340p' cli/cmd/init.go echo echo "=== config validation/save path ===" rg -n -C4 'func .*Save\(|func .*Validate\(|BackendPort|WebPort' cli/internal/config echo echo "=== compose generation path ===" rg -n -C4 'func Generate\(|BackendPort|WebPort|ports:' cli/internal/compose
| func handleReinit(cmd *cobra.Command, state *config.State, opts *GlobalOpts) (bool, error) { | ||
| oldState, loadErr := config.Load(state.DataDir) | ||
| if loadErr != nil { | ||
| return false, fmt.Errorf("existing config at %s is unreadable: %w (delete it manually to force a fresh init)", | ||
| config.StatePath(state.DataDir), loadErr) | ||
| } | ||
| if opts.Yes { | ||
| if oldState.SettingsKey != "" { | ||
| state.SettingsKey = oldState.SettingsKey | ||
| } | ||
| return true, nil | ||
| } | ||
| kept, err := confirmReinit(cmd, oldState, opts) | ||
| if err != nil { | ||
| return false, err | ||
| } | ||
| if kept == nil { | ||
| return false, nil | ||
| } | ||
| if *kept != "" { | ||
| state.SettingsKey = *kept | ||
| } | ||
| return true, nil |
There was a problem hiding this comment.
Don't reintroduce a prompt on non-interactive re-init.
Supplying the required flags takes init down the non-interactive path, but an existing config still falls into the overwrite confirmation flow unless --yes is set. In CI/automation that turns a deterministic rerun into an interactive stop instead of a clear --yes error.
💡 Proposed fix
func handleReinit(cmd *cobra.Command, state *config.State, opts *GlobalOpts) (bool, error) {
oldState, loadErr := config.Load(state.DataDir)
if loadErr != nil {
return false, fmt.Errorf("existing config at %s is unreadable: %w (delete it manually to force a fresh init)",
config.StatePath(state.DataDir), loadErr)
}
if opts.Yes {
if oldState.SettingsKey != "" {
state.SettingsKey = oldState.SettingsKey
}
return true, nil
}
+ if !isInteractive() {
+ return false, fmt.Errorf(
+ "existing config at %s requires confirmation; rerun with --yes to overwrite non-interactively",
+ config.StatePath(state.DataDir),
+ )
+ }
kept, err := confirmReinit(cmd, oldState, opts)
if err != nil {
return false, err
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@cli/cmd/init.go` around lines 108 - 130, handleReinit currently prompts for
overwrite even when the caller intended a non-interactive run; modify
handleReinit so that if an existing config is found and the run is
non-interactive (i.e., opts.Yes is false but the command was invoked in a
non-interactive/flag-driven mode), it does not call confirmReinit but instead
returns a clear error asking the user/CI to pass --yes to force overwrite.
Locate handleReinit and change the flow after loading oldState: if opts.Yes is
false and the caller is non-interactive (use the existing GlobalOpts field that
indicates non-interactive runs or detect that required init flags were
provided), return false with an error message like "existing config found; pass
--yes to overwrite" instead of calling confirmReinit; keep the existing behavior
of preserving oldState.SettingsKey when opts.Yes is true.
| func printStartDryRun(out *ui.UI, state config.State, opts *GlobalOpts) error { | ||
| out.KeyValue("Image tag", state.ImageTag) | ||
| out.KeyValue("Backend port", strconv.Itoa(state.BackendPort)) | ||
| out.KeyValue("Web port", strconv.Itoa(state.WebPort)) | ||
| out.KeyValue("Sandbox", strconv.FormatBool(state.Sandbox)) | ||
| out.KeyValue("Skip verify", strconv.FormatBool(opts.SkipVerify)) | ||
| out.KeyValue("Skip pull", strconv.FormatBool(startNoPull)) | ||
| out.KeyValue("Detached", strconv.FormatBool(!startNoDetach)) | ||
| out.KeyValue("Health check", strconv.FormatBool(!startNoWait)) | ||
| out.Step("Dry run -- no changes made") |
There was a problem hiding this comment.
Report effective behavior in start --dry-run.
printStartDryRun mirrors raw flags, but the real execution path is branch-based: --no-pull skips verification, and --no-detach skips the detached health wait. synthorg start --dry-run --no-pull / --no-detach therefore prints a plan the command will not actually execute.
💡 Proposed fix
func printStartDryRun(out *ui.UI, state config.State, opts *GlobalOpts) error {
+ skipVerify := opts.SkipVerify || startNoPull
+ detached := !startNoDetach
+ healthCheck := detached && !startNoWait
+
out.KeyValue("Image tag", state.ImageTag)
out.KeyValue("Backend port", strconv.Itoa(state.BackendPort))
out.KeyValue("Web port", strconv.Itoa(state.WebPort))
out.KeyValue("Sandbox", strconv.FormatBool(state.Sandbox))
- out.KeyValue("Skip verify", strconv.FormatBool(opts.SkipVerify))
+ out.KeyValue("Skip verify", strconv.FormatBool(skipVerify))
out.KeyValue("Skip pull", strconv.FormatBool(startNoPull))
- out.KeyValue("Detached", strconv.FormatBool(!startNoDetach))
- out.KeyValue("Health check", strconv.FormatBool(!startNoWait))
+ out.KeyValue("Detached", strconv.FormatBool(detached))
+ out.KeyValue("Health check", strconv.FormatBool(healthCheck))
out.Step("Dry run -- no changes made")
return nil
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@cli/cmd/start.go` around lines 106 - 115, printStartDryRun currently echoes
raw flags rather than the command's effective behavior; update it so "Skip
verify" reflects opts.SkipVerify OR startNoPull, "Skip pull" reflects
startNoPull, "Detached" remains !startNoDetach, and "Health check" is disabled
when either startNoWait OR startNoDetach is true (i.e. health := !(startNoWait
|| startNoDetach)). Modify printStartDryRun to compute those booleans before
calling out.KeyValue so the dry-run output matches actual branch behavior
(referencing printStartDryRun, opts.SkipVerify, startNoPull, startNoDetach,
startNoWait).
cli/cmd/stop.go
Outdated
| dur, parseErr := time.ParseDuration(stopTimeout) | ||
| if parseErr != nil { | ||
| return fmt.Errorf("invalid --timeout %q: %w", stopTimeout, parseErr) | ||
| } | ||
| if dur < 0 { | ||
| return fmt.Errorf("invalid --timeout %q: must be non-negative", stopTimeout) | ||
| } | ||
| downArgs = append(downArgs, "--timeout", strconv.Itoa(int(dur.Seconds()))) |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
For Docker Compose v2, what exact value format does docker compose down --timeout accept (integer seconds only vs fractional)?
💡 Result:
For Docker Compose v2, the docker compose down --timeout (or -t) option accepts an integer value representing seconds only. It does not support fractional seconds or other duration formats like "30s" or "1m30s". Official Docker documentation explicitly states: "Specify a shutdown timeout in seconds" (sources 1,6). All examples and usage show integer values, such as --timeout 30 or --timeout=0 (sources 3,4,8,62). In the Go source code for Compose v2 (pkg/compose/down.go), the timeout is parsed via utils.DurationSecondToInt(timeout), confirming it converts seconds to an integer for the Docker API's ContainerStopOptions.Timeout, which expects int seconds (source 20). No evidence supports fractional values; attempting non-integers would likely fail parsing as int.
Citations:
- 1: https://docs.docker.com/reference/cli/docker/compose/down/
- 2: https://docs.docker.com/reference/cli/docker/compose/down
- 3: https://github.com/docker/compose/blob/main/pkg/compose/down.go
- 4: https://linuxcommandlibrary.com/man/docker-compose-down
- 5: https://www.warp.dev/terminus/docker-compose-down
- 6: slow compose down when using depends_on docker/compose#9800
- 7: https://manpages.debian.org/unstable/docker-compose/docker-compose-down.1.en.html
🏁 Script executed:
# Find and examine the stop.go file
fd -t f stop.go cli/Repository: Aureliolo/synthorg
Length of output: 77
🏁 Script executed:
# Check the context around lines 60-67
head -80 cli/cmd/stop.go | tail -30Repository: Aureliolo/synthorg
Length of output: 906
🏁 Script executed:
# Look for tests related to stop command
fd -t f stop_test.go cli/Repository: Aureliolo/synthorg
Length of output: 44
🏁 Script executed:
# Get full context of the stop.go file to understand the function signature and stopTimeout source
cat -n cli/cmd/stop.go | head -70Repository: Aureliolo/synthorg
Length of output: 2278
🏁 Script executed:
# Check if there are any tests in the cli directory for this command
fd -t f "*test.go" cli/cmd/Repository: Aureliolo/synthorg
Length of output: 349
🏁 Script executed:
# Verify the go.mod to check time package availability and go version
head -5 cli/go.modRepository: Aureliolo/synthorg
Length of output: 122
🏁 Script executed:
# Look for test files more carefully
find cli -name "*test.go" -type fRepository: Aureliolo/synthorg
Length of output: 1324
🏁 Script executed:
# Check if there are any references to the stop command in tests
rg -l "stop" cli --type go | grep -i testRepository: Aureliolo/synthorg
Length of output: 99
🏁 Script executed:
# Verify the exact behavior: check if the truncation is actually problematic
# by looking at how the timeout is used downstream
rg "timeout" cli/internal/docker --type go -A 3 -B 3Repository: Aureliolo/synthorg
Length of output: 44
🏁 Script executed:
# Search for composeRunQuiet function to understand how the timeout is passed
rg "composeRunQuiet" cli --type go -B 2 -A 5Repository: Aureliolo/synthorg
Length of output: 5012
🏁 Script executed:
# Check if there's a stop_test.go file
ls -la cli/cmd/stop_test.go 2>/dev/null || echo "No stop_test.go found"Repository: Aureliolo/synthorg
Length of output: 83
🏁 Script executed:
# Verify the behavior by looking at actual command execution
rg "docker.*compose.*down" cli --type go -B 2 -A 2Repository: Aureliolo/synthorg
Length of output: 44
🏁 Script executed:
# Let's examine the composeRunQuiet function definition to understand the full flow
cat -n cli/cmd/start.go | grep -A 20 "func composeRunQuiet"Repository: Aureliolo/synthorg
Length of output: 842
🏁 Script executed:
# Check if there are any examples or documentation about timeout values in the repo
rg "timeout" cli --type go -i | grep -i "example\|doc\|help" | head -20Repository: Aureliolo/synthorg
Length of output: 320
Prevent silent truncation of fractional timeout durations.
The code at line 67 silently truncates fractional durations when converting to integer seconds. For example, 999ms parses successfully but becomes 0 seconds via int(dur.Seconds()), causing unexpectedly aggressive shutdown behavior. Docker Compose v2's --timeout flag accepts only integer seconds, so fractional inputs should be explicitly rejected rather than silently truncated.
Consider validating that the parsed duration is a whole number of seconds and returning an error for fractional inputs:
Suggested approach
if stopTimeout != "" {
dur, parseErr := time.ParseDuration(stopTimeout)
if parseErr != nil {
return fmt.Errorf("invalid --timeout %q: %w", stopTimeout, parseErr)
}
if dur < 0 {
return fmt.Errorf("invalid --timeout %q: must be non-negative", stopTimeout)
}
+ if dur%time.Second != 0 {
+ return fmt.Errorf("invalid --timeout %q: must be a whole number of seconds", stopTimeout)
+ }
- downArgs = append(downArgs, "--timeout", strconv.Itoa(int(dur.Seconds())))
+ secs := int64(dur / time.Second)
+ downArgs = append(downArgs, "--timeout", strconv.FormatInt(secs, 10))
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@cli/cmd/stop.go` around lines 60 - 67, The code silently truncates fractional
durations when converting dur to integer seconds for Docker Compose's --timeout;
modify the logic around dur, stopTimeout and downArgs so that after parsing (dur
:= time.ParseDuration(stopTimeout)) you validate the duration is a whole number
of seconds (e.g. dur%time.Second == 0 or dur.Round(time.Second) == dur) and
return a clear error if it contains fractional seconds, keep the existing
non-negative check, and only then append "--timeout" with
strconv.Itoa(int(dur.Seconds())) to downArgs.
Critical: - init: validate --sandbox, --log-level, --image-tag, --channel, ports early - status: reject non-positive --interval to prevent NewTicker panic Major: - start: reject non-positive --timeout when health checking is enabled - config path: sanitize via SecurePath before printing - config set/unset: add ValidArgsFunction for shell completion - config set image_tag: clear stale VerifiedDigests pins - config unset port: check conflict with the other port before reset - config overrides: fix precedence -- env vars no longer clobbered by config - config list: show effective value matching resolved source (env/default/config) - status --check: validate health response body, not just HTTP 2xx - init --yes: skip interactive re-init confirmation prompt - init: error on re-init without --yes in non-interactive mode - init: preserve resolved --data-dir in interactive form - start dry-run: reflect effective behavior (no-pull implies skip-verify, etc.) - stop: reject fractional-second --timeout values - Extract helpers to bring all functions under 50-line limit - Tests: add resetRootCmd cleanup + extract seedConfig/execConfigSet helpers Medium: - config edit: handle whitespace-only VISUAL/EDITOR without panic - config edit: resolve editor binary via exec.LookPath (CodeQL go/command-injection) - regenerateCompose: inline SecurePath call for CodeQL go/path-injection traceability - status: add -- separator before container IDs in docker stats - config get: use slices.Contains for key lookup Minor: - stop: reject negative --timeout values - logs: require alphanumeric start in timeFilterPattern - config get help: mark memory_backend/persistence_backend as read-only - status: filter-aware empty-state message for --services - config set: include conflicting port name in error message - config path test: resolve symlinks for macOS/Windows compatibility Not fixed (deferred -- large scope): - status: structured JSON output for runStatusOnce (feature addition) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
f401809 to
0c43985
Compare
🤖 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->3 and @types/node 22->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>
Summary
PR 2 of 3 for #886 -- config system extensions and per-command flags for simpler commands.
config setexpanded from 3 to 17 settable keys with validation; compose-affecting keys (ports, sandbox, docker_sock, image_tag, log_level) trigger automaticcompose.ymlregeneration; 4 new subcommands (unset,list,path,edit)color(always/auto/never),output(text/json),timestamps(relative/iso8601),hints(always/auto/never) -- wired into GlobalOptsauto_update_cli,auto_pull,auto_restart,auto_apply_compose,auto_start_after_wipe(config + env var, wiring in PR 3)init(6 flags, non-interactive mode),start(6 flags incl.--dry-run,--no-detach),stop(2 flags),status(6 flags incl.--check,--watch),logs(4 new flags)Test plan
go vet ./...-- cleango test ./...-- all passgolangci-lint run-- 0 issuesPart of #886
🤖 Generated with Claude Code