> ## 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 Manifest – facet.json

> The facet.json schema and name grammar

export const AssetDescriptor = ({assetType, promptPath}) => <>
    <ResponseField name="description" type="string" required>
      Human-readable description of the {assetType}.
    </ResponseField>
    <ResponseField name="adapters" type="map of string → adapter config">
      Adapter name → adapter-specific {assetType} configuration. Each installed
      adapter validates its own metadata schema at build time. Unknown adapters
      produce a warning; invalid metadata for an installed adapter is a build
      error.
    </ResponseField>
    {assetType === "skill" && <ResponseField name="files" type="array of string">
        Companion files shipped inside the skill directory, as exact paths
        relative to <code>skills/&lt;name&gt;/</code> (e.g.{" "}
        <code>references/api.md</code>). They install and remove atomically with
        the skill and cannot list <code>SKILL.md</code>. See{" "}
        <a href="/specification/manifest#supplementary-files">Supplementary files</a>.
      </ResponseField>}
    <p>
      The {assetType}'s prompt is the content of <code>{promptPath}</code>,
      resolved by convention at build time — it is not a manifest field.
    </p>
  </>;

The facet manifest (`facet.json`) is the source of truth for a facet's identity, the text assets it contains, and the [supplementary files](#supplementary-files) it ships. The names it declares are **authored** names: they fix every archive path and integrity value, and a consuming project may materialize an asset under a different name without changing any of that. This page defines every field in the manifest schema and is the canonical reference for both the facet name grammar and the asset name grammar.

<Note>
  The facet manifest is distinct from the [project manifest](/specification/project-manifest) (`facets.json`) — the file in a consuming project that declares which facets are installed, and how it wants their assets named. Throughout this page, "the manifest" means the facet manifest.
</Note>

For conceptual guides to each asset type, see [Skills](/docs/learn/skills), [Commands](/docs/learn/commands), and [Agents](/docs/learn/agents).

## Example

```json expandable facet.json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "name": "acme-dev",
  "version": "1.0.0",
  "description": "Acme org developer toolkit",
  "author": "acme-org",
  "skills": {
    "code-standards": { "description": "House coding standards" },
    "pr-template": { "description": "Pull request description template" }
  },
  "agents": {
    "reviewer": {
      "description": "Org code reviewer",
      "adapters": {
        "opencode": {
          "tools": { "grep": true, "bash": true }
        }
      }
    }
  },
  "commands": {
    "review": {
      "description": "Run a code review"
    }
  }
}
```

## Schema

Every top-level field of `facet.json`. The `name` and `version` fields MUST be present; a manifest missing either MUST be rejected. A facet MUST also declare at least one text asset (`skills`, `agents`, `commands`, or `facets`). Consumers MUST tolerate unrecognized top-level fields — unknown fields MUST be ignored, not rejected.

### Fields

<ResponseField name="name" type="string" required>
  Facet identity: an unscoped name (`cowsay`) or a scoped name (`@scope/name`). Validated, never normalized — see [Facet name grammar](#facet-name-grammar). This is the facet's own name; the names of the assets it contains follow the separate [asset name grammar](#asset-names).
</ResponseField>

<ResponseField name="version" type="string" required>
  Semver version string.
</ResponseField>

<ResponseField name="description" type="string">
  Human-readable description.
</ResponseField>

<ResponseField name="author" type="string">
  Author name or identifier.
</ResponseField>

<ResponseField name="private" type="boolean">
  Privacy declaration. `true` declares private publish intent; `false` or omission is public-by-default. See [Privacy](#privacy).
</ResponseField>

<ResponseField name="skills" type="map of string → skill descriptor">
  Skill name → skill descriptor. A skill descriptor MAY declare a `files` array of [companion files](#supplementary-files) shipped inside the skill directory.

  <Expandable title="skill descriptor">
    <AssetDescriptor assetType="skill" promptPath="skills/<name>/SKILL.md" />
  </Expandable>
</ResponseField>

<ResponseField name="agents" type="map of string → agent descriptor">
  Agent name → agent descriptor.

  <Expandable title="agent descriptor">
    <AssetDescriptor assetType="agent" promptPath="agents/<name>.md" />
  </Expandable>
</ResponseField>

<ResponseField name="commands" type="map of string → command descriptor">
  Command name → command descriptor.

  <Expandable title="command descriptor">
    <AssetDescriptor assetType="command" promptPath="commands/<name>.md" />
  </Expandable>
</ResponseField>

<ResponseField name="files" type="array of string">
  Top-level [supplementary files](#supplementary-files): exact repo-relative paths to non-asset files that ship in the archive but never materialize (e.g. `README.md`, `LICENSE`, `docs/design.md`). Exact paths only — no globs or patterns. Every path MUST satisfy the [portable path grammar](/specification/archive#content-rules) and MUST NOT resolve under `skills/` (skill companions are declared in their skill's own `files`).
</ResponseField>

<ResponseField name="facets" type="array of facets entries">
  References to other facets (`name@version` strings or selective entries). Schema-validated only: build does not resolve the references, and [`facet add`](/cli/add) rejects sources that declare a non-empty `facets` array.
</ResponseField>

<ResponseField name="servers" type="map of string → server reference">
  MCP server references (source-mode version string or ref-mode `{ image }`). Declared servers are validated but not installed; the install pipeline SHOULD surface a warning for facets that declare them.
</ResponseField>

### Privacy

The optional `private` field declares the author's publish-visibility intent:

* `private: true` declares that the facet is **private**.
* `private: false`, or **omitting** the field, declares the facet is **public** (public-by-default).

`private` is a recognized schema field, not an unknown extension. It MUST be a boolean: a non-boolean value (string, number, object, array, or null) MUST be rejected with a `private`-identified error rather than tolerated or coerced. Validation MUST NOT inject `private: false` into a manifest that omits the field  -- omission remains omission in the validated data, so tooling sees exactly what the author wrote.

The `private` declaration is part of manifest content: it is embedded verbatim in the built `.facet` artifact and travels to the registry at publish time (see [Publish](/specification/publish)). It expresses author intent; registry-side authorization and visibility enforcement are the registry's responsibility, not the CLI's.

### Facet name grammar

A facet identity is one of exactly two forms:

* **Unscoped:** a single slug, e.g. `cowsay`.
* **Scoped:** `@<scope>/<slug>`, where both the scope and the base name are slugs, e.g. `@julian/cowsay`.

Every slug component (the unscoped name, a scope, and a scoped base name) MUST satisfy the same grammar:

* It MUST be at least 2 and at most 64 characters long.
* It MUST start with a lowercase ASCII letter (`a`-`z`).
* It MUST end with a lowercase ASCII letter or ASCII digit.
* It MUST contain only lowercase ASCII letters, ASCII digits, and hyphens (`-`).
* It MUST NOT contain consecutive hyphens.

Names are validated, never normalized. Uppercase letters, non-ASCII characters, underscores, dots, spaces, and other characters outside the grammar are rejected rather than rewritten.

**Valid:** `ab`, `cowsay`, `admin-tester`, `apple-b34r`, `@julian/cowsay`, `@acme/deploy-tools`.

**Invalid:**

| Example    | Why invalid                  |
| ---------- | ---------------------------- |
| `a`        | shorter than 2 characters    |
| `Cowsay`   | uppercase letters            |
| `1abc`     | does not start with a letter |
| `abc-`     | ends with a hyphen           |
| `abc--def` | consecutive hyphens          |
| `abc_def`  | underscore is not allowed    |

The following scoped shapes are rejected: a bare scope (`@scope`), a missing scope (`@/name`), a missing name (`@scope/`), extra path depth (`@scope/name/extra`), and the legacy un-prefixed form (`scope/name`).

### Asset names

Asset names — skill, command, and agent names — follow the [Agent Skills name grammar](https://agentskills.io/specification#name-field), interpreted by Facets as a **single ASCII segment**: 1–64 characters of lowercase ASCII letters, digits, and hyphens, not starting or ending with a hyphen and with no consecutive hyphens. Unlike the earlier format, a current-format asset name is a single segment: it MUST NOT contain `/`, so slash-namespaced names like `viper-plans/planning` are no longer valid (use `viper-planning`).

Skills and commands share **one namespace**: within a facet, a skill and a command MUST NOT use the same name. Agents are a separate namespace and may reuse a skill or command name.

The manifest is validated against this rule within a single facet. The same rule is applied again **across every facet in a project** at install time, over the names assets are actually materialized under — see [Namespaces](/specification/materialization#namespaces).

A companion file's path may contain `/` for directory depth (`references/api.md`), but those separators are part of the file path, not the skill name — the skill name itself is still a single segment.

<Note>
  Legacy `0.1` archives retain their earlier multi-segment naming during the compatibility window. New manifests use the single-segment grammar above.
</Note>

### Constraints

1. The `name` MUST be a valid facet identity (see [Facet name grammar](#facet-name-grammar)), and the `version` MUST be a semver string.
2. A facet MUST have at least one text asset -- skills, agents, commands, or facets.
3. The `@` character marks a scope (`@scope/name`) and also separates a name from a version when a facet is referenced elsewhere (`name@version`, `@scope/name@version`).
4. Consumers MUST tolerate unrecognized fields. Unknown fields MUST be ignored.
5. If present, `private` MUST be a boolean; non-boolean values are rejected. Omission is not rewritten to `private: false` (see [Privacy](#privacy)).
6. The facet manifest MUST NOT be modified by the build, publish, or install pipelines. (The [project manifest](/specification/project-manifest), `facets.json`, follows different rules: the install pipeline writes it as part of the [tri-write](/specification/commit#transactional-tri-write).)

## Text assets

A facet's content is carried by three text asset types: skills, agents, and commands. Each asset is declared in the manifest under its type's map, and its name maps to a file at a conventional path in the source tree. The file's content is the asset's prompt — it is resolved by convention at [build time](/specification/build#steps), never referenced by a manifest field.

* **Skill** — `skills/<name>/SKILL.md`. Each skill lives in its own directory, per the [Agent Skills](https://agentskills.io/specification) convention; the main file MUST be named `SKILL.md`. A skill directory MAY also contain declared [companion files](#supplementary-files) at any safe depth (`references/api.md`, `scripts/run.ts`).
* **Agent** — `agents/<name>.md`. A single markdown file whose content is the agent's system prompt.
* **Command** — `commands/<name>.md`. A single markdown file whose content is the command's prompt.

A primary asset file (a `SKILL.md`, agent, or command file) MAY carry author-supplied YAML front matter. It ships verbatim in the archive and is reconciled with the manifest only at install time: materialization merges the manifest's `name` and `description`, plus any per-adapter extras, on top of whatever the author wrote. The manifest always wins, so a facet cannot override its own asset identity through front matter.

<Note>
  The `name` written at install time is the asset's **effective** name — the one the consuming project chose. `description` stays as the publisher wrote it. See [Two identities](/specification/materialization#two-identities).
</Note>

An asset descriptor's `adapters` block is validated at [build time](/specification/build#steps) against each installed adapter's schema: unknown adapters produce a warning, and invalid metadata for an installed adapter is a build error.

## Supplementary files

Beyond its assets, a facet can declare **supplementary files** — non-asset files that ship inside the `.facet` archive and are integrity-protected like everything else, but are not independently installable assets. They have no asset type, no adapter metadata, and no lockfile asset tuple.

There are two declaration sites, and every supplementary archive entry MUST be derivable from one of them (there is no auto-discovery):

* **Top-level `files`** — repo-relative paths at the tree root, declared in the manifest's [`files`](#fields) array. Used for `README.md`, `LICENSE`, development notes, and other project files. These ship but **never materialize**: they are not written to disk at install time.
* **Per-skill `files`** — paths relative to a skill's directory, declared in that [skill descriptor's](#fields) `files` array. These are the skill's **companion files** (references, scripts, templates). A companion materializes atomically with its owning skill and cannot list `SKILL.md` itself.

Supplementary files are declared as **exact paths only** — no globs or patterns — and each path MUST satisfy the [portable path grammar](/specification/archive#content-rules). A top-level path MUST NOT resolve under `skills/`, so a skill's files have exactly one declaration site. Supplementary bytes are shipped verbatim: binary and empty files are valid, and front matter is never parsed or stripped.

`README.md` and the extensionless `README` are ordinary top-level `files` declarations — there is no README-specific manifest field. Authoring support for them is described in [Create Your First Facet](/guides/create-your-first-facet) and [`facet edit`](/cli/authoring/edit).

<Warning>
  A CLI older than the first `0.2`-producing release tolerates the `files` fields but omits their bytes when it builds, producing a declared-but-empty facet. Build with a current release so declared files actually ship.
</Warning>
