Replaces the previous commit's blind force-overwrite with a real
choice, per user feedback: force-overwriting server config without
asking was the wrong default.
- A tracked config value (real default in .env.example) that already
matches .env is left alone silently — no prompt, no noise.
- A value that DIFFERS is a conflict, shown on one screen (all
conflicts together, not one prompt per key) via gum
(charmbracelet/gum) — single static binary fetched as a release
tarball into .cache/gum/ (gitignored), no build step, no package
manager dependency. Falls back to a plain read-based prompt if gum
can't be fetched (offline, unsupported arch).
- Non-interactive (no TTY — cron, CI, piped): any conflict is a hard
error (exit 1, lists every conflicting key) unless --force is
passed, which accepts every new value automatically — matches how
this PR's own fix needs to land unattended.
- Secrets and host-resolved values are completely unaffected either
way — untouched by this loop, same as before.
Verified in an isolated sandbox against the exact scenario from this
PR (stale LLAMA_FAST_PARALLEL=2 vs git's 1):
- no TTY, no --force: exits 1, prints the diff, doesn't touch .env
- no TTY, --force: LLAMA_FAST_PARALLEL corrected 2 -> 1, an
OMNIROUTE secret confirmed untouched (not regenerated)
docker compose config -q still passes.
Not verified: the interactive gum path itself (needs a real TTY,
couldn't allocate a pty in this sandbox) — worth confirming for real
on the server, including that gum's release asset naming actually
matches what ensure_gum() expects.
Refs #5