Usage
facets.json, materializes every facet declared there, and writes facets.lock with the exact resolved versions and integrity hashes. Use it after a clone, after pulling manifest changes, or to restore files someone edited by hand.
Nothing is written until every facet resolves, verifies, and composes without a collision, so a run that fails or is declined before that point leaves the project byte-identical. If a failure happens after writing starts, the run rolls back what it wrote and names any file it could not return to its previous state. See when rollback cannot finish.
facet install takes no positional arguments. To add a facet, use facet add.
Examples
Flags
Exit codes
error:, a code= token for log searching, and a fix: action. Reports for collisions, MCP approval, and unsupported adapters print above that block, so fix: stays last.
Output
assets written counter covers text assets only, and MCP servers are counted beside it, so a facet that only declares servers still reports real work. Server configuration is reported as added, updated, repaired, unchanged, or removed, and a line gains (took over an existing entry) when it adopted or replaced an entry this project did not create.
Below the counts, the summary names every contribution whose materialized name differs from the published one:
Outcomes
The summary classifies each facet by what happened on disk.N removed count covers facets, not the assets they contributed. An untracked removal deletes nothing on disk.
Details
Name collisions
Two facets cannot materialize under the same name. Skills and commands share a namespace, agents have their own, and servers occupy a third. Collisions are also scoped, so a project-scoped text asset never contests a user-scoped one. See Namespaces. Two facets declaring the same server identically is not a collision. They agree, one entry is configured, and both are recorded as claimants. Collisions are detected before anything is written, so the run stops with the project untouched. In a terminal, the install pauses and opens a resolver. Each contested contribution takes one choice:↑↓, choose with ←→, and apply with Enter. Each claimant shows ✕ unresolved, ⚠ conflict, or ✓ resolved. A contested server also shows its declaration summary and a short fingerprint, so you can tell two same-named servers apart. Confirmation unlocks when every group is resolved. Esc cancels the whole install without writing anything.
Only one claimant has to change. Your choices are saved to facets.json:
servers group needs "manifestVersion": 0.2, which a successful non-frozen install writes for you.
Without a terminal, nothing is prompted. Every group and claimant is printed with the exact facets.json location and copy-pasteable snippets. No winner is chosen and no name is invented: the placeholder is literally choose-a-name.
Frozen lockfile
--frozen-lockfile makes the lockfile authoritative. Nothing is re-resolved, the lockfile is never written, and every facet must reproduce its locked integrity. The lockfile’s recorded materialization intent must match facets.json.
Frozen mode never prompts, even in a terminal, because it reproduces recorded intent rather than collecting new decisions. A collision fails, and MCP configuration needing approval fails unless you also pass --accept-mcp.
A 0.2 lockfile has no field for a text-asset disposition, so a frozen run fails if facets.json declares one. Run a plain facet install once to migrate the lockfile. A servers override never causes this, because no lockfile version records server intent.
Frozen mode reads manifestVersion 0.2, 0.1, and unversioned documents and migrates none of them. A stale servers override is reported as drift and is not removed.
Cache
Resolved content is cached under$FACET_DIR/cache/, so repeat installs of the same version skip the network. Cache hits are re-verified on every use. See integrity.
MCP servers
A facet that declares servers contributes configuration to every selected adapter, written into that tool’s own project file. Only project-scoped files are touched. See where configuration lands. Installing a server configures it. Facets never downloads, launches, connects to, health-checks, or authenticates to it.Accept MCP
--accept-mcp is the only non-interactive way to accept MCP configuration. It approves the complete set the run reports, including takeovers of existing entries.
It does not authorize a text-asset takeover and does not resolve a name collision. The same flag exists on facet add, facet update, and facet remove.
Approving a declaration
In a terminal the install pauses on one screen listing every declaration awaiting approval, its claimant facets, and its exact command, arguments, and environment assignments or URL. Decline is selected by default, and approval covers the whole displayed set. Approval is stored per machine, keyed by the effective server name and the declaration’s content:- An already-approved declaration is not asked about again, including when a second facet declares it identically.
- A changed command, argument order, environment value, transport, URL, or effective name asks again.
- A run that fails or rolls back banks nothing.
- Machine records written before MCP support carry no approvals, so the first install after upgrading asks once.
facets.json or facets.lock, so a committed file cannot claim you consented to running something.
In CI and other non-interactive runs
Without a terminal, an unapproved declaration fails before anything is written and prints every server, its claimants, its full declaration, and the exactfacets.json edit that would refuse it. Frozen mode fails the same way even on a terminal, because it never collects a new decision.
Re-run with --accept-mcp to approve everything shown, or omit what you do not want:
Taking over an existing entry
If a desired server name already has an entry this project did not create, it is disclosed on the same approval screen, before any write. Each line names the adapter and says whether the entry already matches and would be adopted, or differs and would be replaced. An equivalent entry is adopted without rewriting the document. Declining costs nothing, because no write has happened yet.Taking over an existing file
Text assets have a separate prompt that appears mid-write, when materialization reaches an occupied destination this machine does not own. It names the adapter, scope, type, and effective name. Continue is the default here, because that is what a non-interactive run already does and because replacing a file is less consequential than authorizing a command. Cancelling rolls back everything the run has already written and exits non-zero.--accept-mcp never answers this prompt.
Adapters that cannot configure MCP servers
When the project has active declarations and a selected adapter declares no MCP support, the run fails before approval and before any write, naming every such adapter. That adapter declares no MCP support as a deliberate answer, so a newer release will not change it. Omit every affected server, or remove the adapter. With no active declarations left, an adapter without MCP support installs normally.Where declarations appear
A declaration’s command, arguments, environment values, and URL appear on exactly two surfaces: the interactive approval screen and the non-interactive report that asks you to approve. They never appear in--verbose output or success summaries, which name servers by identity only. That keeps declaration contents out of scrollback and CI logs.
On both surfaces values are quoted and escaped, such as "srv" "--root" "/w" and "TOKEN"="a b". Quoting keeps one argument containing a space distinguishable from two, and stops a value from issuing a terminal escape.
Troubleshooting
another install is already running
another install is already running
this facet uses an archive format this CLI does not support
this facet uses an archive format this CLI does not support
facet self-update. The message names the minimum supporting release when it knows it.unsupported lockfileVersion
unsupported lockfileVersion
facets.lock declares a version this CLI does not read. The common case is the withdrawn closed-alpha 1.Fix: if it declares 1, delete facets.lock and run facet install to regenerate it. If it declares something newer, run facet self-update instead, because deleting a newer lockfile discards resolutions your teammates committed.lockfile v0.2 cannot record materialization overrides
lockfile v0.2 cannot record materialization overrides
facets.json declares an alias or omission for a text asset, and the lockfile is still at 0.2, which has no field for it. A frozen install refuses rather than ignore your intent.Fix: run one plain facet install to migrate the lockfile, then commit it.facets.json and the lockfile disagree under a frozen install
facets.json and the lockfile disagree under a frozen install
fix: line says which case you are in.- Nothing stale: run
facet installwithout the flag, orfacet add, then commit the lockfile. - Only stale overrides: remove those materialization choices from
facets.json, or re-run without the flag to drop them. - Both: re-run without the flag, which records the drift and drops the stale choices in one run.
integrity mismatch on install
integrity mismatch on install
facets.lock, usually an edit or corruption on disk.Fix: re-run facet install. It repairs a drifted file in place without touching files you added yourself.When the archive itself disagrees with the lockfile, re-running will not help. Delete facets.lock and re-run, or facet add the facet to refresh its entry.cached content does not match the lockfile
cached content does not match the lockfile
$FACET_DIR/cache/ and re-run, which re-downloads and re-verifies it.cannot create a lockfile entry without registry confirmation
cannot create a lockfile entry without registry confirmation
invalid materialization alias
invalid materialization alias
as value does not satisfy the asset-name grammar: 1 to 64 characters of lowercase letters, digits, and hyphens, with no leading, trailing, or repeated hyphen. An alias is a single segment, so team/planning is rejected too.Fix: correct the alias at the facets.json path the error names. Aliases are never auto-corrected.the chosen names still conflict
the chosen names still conflict
more than one selected adapter configures the same file
more than one selected adapter configures the same file
facet adapter remove. Which tool owns the file is your decision.a tool's MCP configuration cannot be read or written
a tool's MCP configuration cannot be read or written
facets.json instead.a tool's MCP configuration changed while the command was running
a tool's MCP configuration changed while the command was running
fix: line reports what was left on disk.a planned change was refused
a planned change was refused
fix: line names the path and the reason. Resolve that one path, then re-run. Nothing is left half-applied.