Skip to main content

Usage

Checks every registry-backed facet in facets.json for a newer release, then installs the ones that move. Applying an update is an ordinary install, so it reports the same outcomes and honors the same approvals. facet upgrade is an alias of this command, with the same flags, output, and exit codes. Both act on the facets your project declares. To update the facet binary, use facet self-update. facet update takes no positional arguments. To update some facets but not others, use --interactive.

Examples

Flags

boolean
Take each facet’s latest release, ignoring the range in facets.json. Short form -L.This is the only mode that rewrites a specifier. See Crossing the declared range.It has no effect alongside --interactive, which already opens every row on its latest release.
boolean
Choose which facets move and which version each takes. Short form -i.Requires a terminal. Without one the command fails before contacting the registry.
boolean
Print the plan and modify nothing. Exits 0 whether or not updates are available.
boolean
Show detailed step output on stderr. Declaration contents never appear here. See where declarations appear.
boolean
Approve the MCP configuration this update would write, without prompting.
There is no --frozen-lockfile here. Frozen mode reproduces what the lockfile records, which is the opposite of what this command does, and it belongs to facet install.

Exit codes

Output

Each moving facet is named with the version it left and the version it took. Recorded aliases and omissions survive a version change, and outcomes match facet install.

Details

Current, Target, and Latest

Every checkable facet has three versions, and the plan shows all three:
installed
The exact version facets.lock records.
range-respecting
The newest release the specifier in facets.json already permits. Taking it never changes what the project declares.
newest published
The registry’s newest release, whatever the specifier says.
All three appear even for a facet that is not moving, because that is what answers why it stayed put:
viper-plans moves to 1.4.0 under a plain update. cowsay does not move, and reaching 3.0.0 needs --latest. Plain facet update takes every Target that advances. --latest takes every Latest that advances.

Crossing the declared range

--latest can select a version the declared specifier forbids, so it rewrites that specifier with the smallest edit that admits the new version:
A pin stays a pin, a wildcard keeps its shape, and a specifier that already floats is left as written. The full table is in the manifest write policy. Taking a Target never rewrites anything.

Choosing interactively

Every row opens on its Latest with nothing selected, so Space takes the newest release of that facet and a facet you never touch is left alone. Move a row to target first to take the version your declared range already permits. Only the digit that moves is coloured, and the column a row would install is also named in words, so the screen works without colour. A row already at the installed version cannot be selected, and confirming needs at least one selection. Selection happens before adapter discovery and before anything is written, so cancelling costs nothing.

Previewing

--dry-run prints the plan and writes nothing: no manifest, no lockfile, no assets, no cache, and no adapter installation. That also means a preview works on a machine with no adapter connected. A preview always prints the plan, including when nothing would move, followed by the reason. A real run prints the reason alone. Combined with --interactive it previews the selection you confirmed.
A preview shows versions, not their effects. Which assets a release materializes, whether it collides, and what it declares are determined during the install a real run performs.

When nothing moves

A run that applies nothing still succeeded and exits 0.

Sources that cannot be checked

Only registry facets have releases to compare. Git and local facets are named in the printed plan rather than counted as current:
Because a plain run prints only the reason, use --dry-run to see these rows. Move one by editing its source in facets.json and running facet install.

Troubleshooting

Cause: facets.json and facets.lock disagree. A declared registry facet is missing from the lockfile, locked to a different source kind, locked at something that is not an exact release, or locked at a version its specifier no longer permits. The message names every affected facet.Fix: repair the drift first, then update:
Cause: an entry’s specifier is malformed, so update cannot tell what it is meant to resolve to.Fix: correct that entry in facets.json by hand. facet install cannot repair a malformed specifier.
Cause: facets.json or facets.lock changed while the plan was being built, or between reading the plan and applying it. A concurrent command, a pull, or an editor save will do it.Fix: re-run facet update. Nothing was written in either case, so nothing needs repairing.
Cause: the picker needs a TTY. The check runs before any registry lookup.Fix: use a non-interactive mode instead:
Cause: the terminal check passed, but the picker could not render, for example because stdin was taken away mid-run.Fix: re-run in a normal terminal, or pick a non-interactive mode. Nothing was applied.
Cause: update considers everything declared in facets.json.Fix: choose per facet with facet update --interactive.
Cause: the registry answered with something that is not an exact release, or resolved a facet outside its declared range. Your project cannot cause this.Fix: retry shortly. If it persists, report it with the facet name from the message.

See also