> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agentfacets.io/llms.txt
> Use this file to discover all available pages before exploring further.

# facet install

> Install every declared facet

## Usage

```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
facet install
```

Reads `facets.json`, fetches and materializes every facet declared there, and writes a `facets.lock` recording the exact resolved versions and integrity hashes. Used after a fresh `git clone`, after pulling teammate changes that updated the manifest, or to reapply assets after manual edits to adapter directories.

`facet install` does not accept positional arguments  -- to add a new facet, use [`facet add`](/cli/add).

## Flags

<ResponseField name="--verbose" type="boolean">
  Show detailed step output on stderr.
</ResponseField>

<ResponseField name="--frozen-lockfile" type="boolean">
  Treat the lockfile as the source of truth; fail on any manifest/lockfile drift.

  Setting this flag makes the **lockfile authoritative**: no re-resolution, no lockfile writes, and every facet — including local sources — must reproduce its locked integrity. It also requires the lockfile's recorded [materialization](/specification/materialization) intent to match `facets.json` exactly. The full preflight and behavior matrix are in the [Frozen lockfile specification](/specification/commit#frozen-lockfile).

  Frozen mode **never prompts**, even in a fully interactive terminal. It reproduces recorded intent, so it must not collect a new decision. A name collision under `--frozen-lockfile` is reported and fails.

  A `0.2` lockfile has no field in which to record a materialization disposition. If `facets.json` declares any override against one, the frozen install fails rather than silently ignoring it — run one non-frozen `facet install` to migrate the lockfile.

  <Tip>
    Use this in CI to guarantee `facets.json` and `facets.lock` are in agreement. Mirrors `--frozen-lockfile` from `npm` and `bun`.
  </Tip>
</ResponseField>

## Exit codes

| Code | Meaning                                                                                                                                                                                                                                                                      |
| ---- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `0`  | Install succeeded (including no-op when nothing has changed).                                                                                                                                                                                                                |
| `1`  | Install failed. The view renders the structured failure inline; stderr carries a three-line block — `error:`, `code=...` for log-grepping, and a `fix:` action. A [name collision](#name-collisions) prints its full report above that block, so `fix:` stays the last line. |

## What it does

<Steps>
  <Step title="Validate the project">
    `facets.json` must exist, and at least one **install-capable** adapter must be installed. If none are, the picker launches when the terminal can prompt; otherwise the command exits pointing at [`facet adapter install`](/cli/adapters/install). Installed-but-incompatible adapters fail here rather than falling through to the picker.
  </Step>

  <Step title="Run the install pipeline">
    Runs the [install pipeline](/specification/install) with an empty delta: acquire the install lock, resolve and [verify](/specification/commit#verify) *every* declared facet ([honoring satisfying lockfile pins](/specification/commit#resolve)), [compose one global plan](/specification/commit#compose) checking for name collisions, then [remove drift](/specification/commit#drift-removal) and materialize assets into every adapter, and finally [write manifest + lockfile + receipt atomically](/specification/commit#transactional-tri-write).

    Deletion runs before any write, which is what lets an asset name move from one facet to another in a single install.
  </Step>
</Steps>

<Note>
  Every facet is verified, and the whole plan composed, before any asset is written. An integrity mismatch, a name collision, or a cancelled resolution all abort with the project **byte-identical** — not rolled back, but never touched. The full semantics — lockfile trust, staleness, the verification chain — live in the [Commit specification](/specification/commit) and the [Integrity Model](/specification/integrity).
</Note>

## Outcomes

The summary line classifies each facet by what happened on disk:

<ResponseField name="installed" type="new">
  Facet was not in the previous lockfile.
</ResponseField>

<ResponseField name="updated" type="state change">
  Facet was in the lockfile at a different version  -- including when a stale entry was re-resolved to match the manifest. The summary shows `(was X → Y)`.

  Also reported when the version is unchanged but its [materialization](/specification/materialization) is not: aliasing or omitting an asset changes what is on disk and what the lockfile records, so it is an update rather than a no-op. It is not a *repair* either — nothing drifted, the project changed its mind.
</ResponseField>

<ResponseField name="repaired" type="self-heal">
  Same version and same materialization intent, but at least one adapter file was missing or had drifted from the lockfile content. Restored. A skill is repaired by replacing its whole bundle atomically, so a single drifted companion pulls the primary with it.
</ResponseField>

<ResponseField name="unchanged" type="no-op">
  Same version, same materialization intent, every asset already in its desired state. Nothing was written — the on-disk bytes were compared and matched, which is enough to record the assets as tracked without rewriting them.
</ResponseField>

<ResponseField name="removed" type="cleanup">
  Facet is no longer declared in `facets.json`, and this machine's [install receipt](/specification/commit#machine-local-install-receipt) tracked it. Its ownership was reconciled: obsolete identities deleted, and any identity another declared facet now claims kept.
</ResponseField>

<ResponseField name="removed-untracked" type="records only">
  Facet is no longer declared, and only the lockfile ever recorded it — you pulled a teammate's `facets.lock` and never installed. The declaration is dropped from `facets.json` and `facets.lock`, but **nothing on disk is deleted**, because no receipt claim proves this machine wrote it. The summary names the facet and says the files were left in place.
</ResponseField>

<Note>
  The `N removed` count covers both: it counts declarations that went away. How many *assets* left disk is the separate asset count — zero, for an untracked removal.
</Note>

If you delete a materialized asset by hand and re-run `facet install`, the affected facet shows up as `repaired`.

Below the counts, the summary names every asset whose materialized name differs from its authored one — the one thing you cannot infer from the file tree:

```
  1 updated · 1 asset written
  + 1 skill
  viper-plans skill planning → team-planning
  cowsay command scratch — omitted
```

Omitted assets are not counted as written.

## Name collisions

Two facets cannot materialize an asset under the same name. Skills and commands share one namespace; agents have their own — see [Namespaces](/specification/materialization#namespaces).

Collisions are detected **globally, before anything is written**, so the install stops with the project untouched.

**In an interactive terminal**, the install pauses and opens a resolver. Each contested asset gets one of three outcomes:

| Key     | Choice                            | Result                               |
| ------- | --------------------------------- | ------------------------------------ |
| `Keep`  | keep the authored name            | records nothing                      |
| `Alias` | materialize under a name you type | `{ "kind": "aliased", "as": "..." }` |
| `Omit`  | do not materialize it at all      | `{ "kind": "omitted" }`              |

Move with `↑↓`, pick with `←→`, apply with `Enter`, and `Esc` to go back. Arrow keys only move the cursor — nothing is applied until you press `Enter`. Each claimant shows a status that pairs an icon with a word, so it stays readable without color: `✕ unresolved`, `⚠ conflict` (your own edit created this one), `✓ resolved`. Confirmation unlocks only when every group is resolved.

Your choices are written into `facets.json` as durable intent, so the next install runs without prompting:

```json facets.json theme={"theme":{"light":"github-light","dark":"github-dark"}}
"viper-plans": {
  "source": "1.*",
  "materialization": {
    "skills": { "planning": { "kind": "aliased", "as": "team-planning" } }
  }
}
```

`Esc` or `Ctrl-C` cancels the whole install. Nothing was written, and re-running lets you make the choices again.

**Without a terminal** — CI, piped output, or `--frozen-lockfile` — nothing is prompted. Every group and every claimant is printed to stderr with the exact `facets.json` location to edit and copy-pasteable alias and omit snippets. No winner is chosen and no alias is invented for you: the placeholder is literally `choose-a-name`. The report ends by stating that `facets.json`, `facets.lock`, the receipt, and your materialized assets were **not** changed.

## Cache

Resolved facet content is stored at `$FACET_DIR/cache/<name>@<version>/` (default `~/.facet/cache/`) so subsequent installs of the same identity don't hit the network. Cache hits are re-verified on every use — see [cache self-audit](/specification/integrity). The cache root is part of the facet directory tree; set `FACET_DIR` to change it. See the [environment variables reference](/cli/env).

## Servers

A facet that declares `servers:` emits a warning during install  -- the server names are listed but not materialized. Server support is on the [roadmap](/roadmap).

## See also

* [`facet add`](/cli/add)  -- adds a new facet to `facets.json` and installs it in one step.
* [`facet adapter install`](/cli/adapters/install)  -- install adapters that `facet install` materializes facets into.
* [Installation specification](/specification/install)  -- the two-phase pipeline this command runs.
