What to build
Today shipnode env only pushes the local .env to the server. There is no way to detect drift (someone edited the server env during an incident) or to reconcile the remote state with a local file. Add two subcommands:
shipnode env diff [--file path] — compare local .env (or file at --file) against the remote .env currently in use. Output: keys added, removed, and modified. Values must be masked by default (show first 2 + last 2 chars); a --show-values flag reveals them.
shipnode env pull [--file path] — download the remote .env into a local file (default .env.remote) so the operator can merge it manually. Never overwrites .env directly.
The remote .env location is whatever shipnode env already writes to — reuse that resolution logic, don't hard-code a path.
Acceptance criteria
Blocked by
None - can start immediately
What to build
Today
shipnode envonly pushes the local.envto the server. There is no way to detect drift (someone edited the server env during an incident) or to reconcile the remote state with a local file. Add two subcommands:shipnode env diff [--file path]— compare local.env(or file at--file) against the remote.envcurrently in use. Output: keys added, removed, and modified. Values must be masked by default (show first 2 + last 2 chars); a--show-valuesflag reveals them.shipnode env pull [--file path]— download the remote.envinto a local file (default.env.remote) so the operator can merge it manually. Never overwrites.envdirectly.The remote
.envlocation is whatevershipnode envalready writes to — reuse that resolution logic, don't hard-code a path.Acceptance criteria
shipnode env diffon an in-sync env exits 0 with an "in sync" messageshipnode env diffon drift shows added/removed/modified keys, values maskedshipnode env diff --show-valuesreveals values (respect the same permission gate asenvpush)shipnode env pullwrites.env.remoteand refuses to overwrite an existing file without--forceshipnode env pull --file pathwrites to the given pathBlocked by
None - can start immediately