Skip to main content
CLINew FeatureImprovement
Private facets appear in facet search when signed in; clearer adapter and git authentication errors

Private facets in facet search results

facet search now includes private facets. Signed out, results show only public facets. Signed in, results also include the private facets your account can access, ranked and paged by the registry like everything else.If a private facet you own is missing from results, run facet login and search again.

Clearer terminology and errors

Alongside open beta, the CLI’s messaging dropped its alpha-era wording:
  • Adapter compatibility errors say “adapter SDK API.” The diagnostics in facet adapter list, the failures that stop facet build, facet add, facet remove, and facet install, and the CLI’s built-in usage guidance all use one term for the adapter SDK API version an adapter declares.
  • Git authentication failures name the real requirement. A clone that needs credentials now fails with private repositories require git authentication and suggests configuring git authentication or using a public URL. The old message claimed only public repos and SSH were supported, which is no longer true.
New Feature
Agent Facets enters open beta with private publishing for individuals and teams

Agent Facets is in open beta

The alpha established the full author, publish, install, and update loop. Agent Facets is now in open beta while we complete registry inspection, facet composition, and the compatibility work required for general availability.
  • Public publishing remains free.
  • Personal private publishing is free during the beta.
  • Private publishing for teams is a paid offering available by request.
See the roadmap for the path to general availability and Private Facets for visibility, scopes, and team access.
CLIFix
agent-facets 0.33.1 fixes the cold-cache crash when installing from a git source

Git-sourced installs no longer crash on a cold cache

The fix ships in agent-facets@0.33.1. Run facet self-update to pick it up.Installing a facet from a git or GitHub source for the first time crashed with TypeError: Object.entries requires that input parameter not be null or undefined instead of completing. The clone and build both succeeded; the run died on the step that commits the built content to the cache. That step is fixed, and git-sourced installs now complete on a cold cache.
  • Only fresh clones were affected. A first install, or one whose cache slot was cold or evicted, hit the crash. Installs served from an audited cache hit, and facets from the registry or a local path, never took this path.
  • Nothing about verification changed. The same entries are audited, and the cache records the complete set, including supplementary files that no asset owns.
If a git-sourced facet add or facet install failed for you with this error, update and run it again.
CLINew FeatureBreaking
agent-facets 0.33.0 ships facet update: move declared facets to newer releases

New command: facet update

agent-facets@0.33.0 is on npm. Run facet self-update to pick it up.facet update (aliased facet upgrade) moves the registry-backed facets a project declares to newer releases. The plan shows what is installed alongside each facet’s range-respecting target and its latest release, so “why is this one not moving?” is answerable from the plan itself.
  • Plain facet update respects your ranges. Every facet moves to the newest version its declared specifier already permits.
  • --latest crosses the range, minimally. It rewrites the specifier by the smallest edit that admits the new version, preserving how you expressed intent: a pin stays a pin, 1.* becomes 2.*, and * and latest are left exactly as authored.
  • --dry-run writes nothing. No manifest, no lockfile, no receipt, no assets, and no adapter installation, so it is safe on a machine with no adapter connected.
  • --interactive picks per facet. Every row starts on its latest release with nothing selected; a facet you never touch is left alone.
Applying an update is an install. It runs the ordinary install pipeline — the same verification, collision handling, MCP approval, and rollback — and re-checks under the project lock that nothing moved since you reviewed the plan, so the version you reviewed is the version installed. Recorded materialization choices survive a version change. Git and local facets are named as unsupported rather than counted as current.

Breaking: facet upgrade is no longer a placeholder

facet upgrade previously printed a not-yet-implemented notice and exited 0 without touching a file. It is now an alias of facet update and applies real updates to facets.json, facets.lock, the install receipt, and your materialized assets. If automation called facet upgrade expecting a no-op, drop the call or make it explicit with facet update --dry-run.Neither name touches the CLI binary. That remains facet self-update.

Version numbers are bounded by exact integer representation

Via @agent-facets/protocol@0.31.1, a version or specifier whose numeric component exceeds 2^53 - 1 is now rejected, with an error that names the magnitude. Above that bound two distinct releases compare equal, so a comparison deciding which release is newer could answer for a version that was never published. No real version is anywhere near the limit.
CLIFix
Rollback no longer leaves empty directories behind

Rollback no longer leaves empty directories behind

When facet add, facet install, or facet remove fails or is interrupted, rollback now removes every directory it leaves empty.Previously, two kinds of directories could survive a rollback:
  • An empty directory recreated at a managed path was left behind, even though nothing was inside it.
  • A pre-existing directory that rollback emptied was never removed at all, because the run had not created it.
Both are now pruned. What this means for you:
  • A failed run leaves a tidy tree. Rollback walks up from each restored file and removes any directory it left empty, so you no longer have to delete hollow folders by hand.
  • Only empty directories are ever removed. A directory holding anything at all — your files or the tool’s — is refused in a single atomic step. There is no window in which content can be lost.
  • Cleanup is bounded. The walk never climbs past a tool’s configuration directory (such as .claude or .opencode). It can remove that directory only when the failed install created it.
No flags to set; the behavior is automatic. See Troubleshooting for what a failed install reports.
CLIFix
OpenCode adapter now writes MCP servers to the document OpenCode actually loads

OpenCode: MCP servers land where OpenCode actually reads them

The OpenCode adapter previously considered only the project root’s opencode.jsonc and opencode.json. OpenCode itself merges four project documents, and the two under .opencode/ outrank both root files. A project keeping its OpenCode configuration under .opencode/ had servers written to a file the higher-precedence one shadowed — the configuration you approved on the MCP approval screen was not the configuration OpenCode loaded.All four documents are now read, classified as one configuration, and disclosed. In decreasing precedence:
  1. .opencode/opencode.jsonc
  2. .opencode/opencode.json
  3. opencode.jsonc
  4. opencode.json
Writes go to exactly one of them, chosen once per run:
  1. the highest-precedence document that already defines an mcp member (an empty {} counts);
  2. otherwise the highest-precedence document that exists;
  3. otherwise .opencode/opencode.jsonc, created.
Following an existing mcp member rather than merely the file that sorts first is what keeps a project whose servers live in a root opencode.json, beside a .opencode/opencode.jsonc holding only agents, from having its servers split across two files that shadow each other.Two behaviors worth calling out:
  • A shadowed copy of a desired server is left alone. The target’s definition already wins, so deleting from a file this run is not otherwise writing is not the adapter’s call. Previously the losing copy was deleted.
  • An obsolete owned entry is still removed from every document that defines it, so a removal cannot promote a shadowed copy and leave the server configured.
Entries the project neither desires nor owns remain untouched everywhere. See facet install and Custom adapters for the surrounding surface.
CLIImprovementBreaking
agent-facets 0.32.0 turns on adapter API 0.3 end-to-end and renames facet adapter install to facet adapter add

agent-facets@0.32.0 on npm

The CLI release that pairs with yesterday’s adapter SDK 0.30.0 is published:
  • agent-facets@0.32.0
Run facet self-update to pick it up.

Adapter API 0.3 end-to-end

Every file facet add, facet install, and facet remove change — assets, native MCP configuration, and the project’s own facets.json, facets.lock, and receipt — now commits through one filesystem transaction:
  • Byte-exact rollback. A failed run restores the exact prior bytes and permission bits. YAML front matter and TOML formatting come back as authored instead of being re-rendered from parsed data.
  • No phantom drift. A file already holding the bytes a plan would write contributes no change, so a re-install touches no modification time.
  • Contested files are preserved, not clobbered. A file this run wrote and something else then changed is reported by path, and rollback continues past it so one contested file cannot strand the rest.
  • Failed runs report what they left on disk. The report distinguishes a file deliberately left alone from one whose restoration genuinely failed, and gives each its own remedy. No file is ever reported as both restored and unrecoverable.

Safer MCP configuration

Two connected adapters may no longer manage the same MCP configuration file. A project whose selected adapters share a file fails before approval and before any write, with every adapter involved named. The remedy is to deselect one, not to upgrade. See Troubleshooting for the resolution path.Every plan is also recomputed immediately before its own commit, including one that concluded nothing needed writing. A document edited while the approval screen was open is reported as changed mid-run rather than silently reported as configured.

facet adapter install is now facet adapter add

The facet adapter add verb matches the split it mirrors at the top level: add takes a specifier and installs what you name, install takes none. Every reinstall command the CLI prints — next to a failing entry in facet adapter list, and in the compatibility diagnostics that stop facet build, facet add, facet remove, and facet install — now names facet adapter add.The old spelling keeps working as a deprecated alias. It runs the same code, produces the same stdout, and exits with the same code; the only difference is one deprecation notice on stderr.

Breaking: reinstall every adapter

The supported adapter API is now exactly {0.3}. Every already-installed adapter — first-party included — reports unsupported in facet adapter list until reinstalled, and the CLI prints the exact command per entry. After facet self-update, run:
Then run the printed facet adapter add <name> command for each entry the list flags. First-party names are claude-code, codex, and opencode.An adapter declaring no MCP support still cannot configure servers, and that answer no longer changes with a newer release. If a selected adapter declares no MCP support, the failure names the adapters whose declarations must be omitted or which must be deselected.The obsolete-bundle-retained outcome is also removed: a skill bundle whose primary is already gone now has its owned companions removed, because each carries its own exact observed state and is individually restorable.
CLIImprovement
Adapter SDK 0.30.0 ships adapter API 0.3: adapters plan, the CLI writes

Adapter SDK 0.30.0 with adapter API 0.3

Adapter authors have new releases on npm:
  • @agent-facets/adapter@0.30.0
  • @agent-facets/adapter-claude-code@0.11.0
  • @agent-facets/adapter-codex@0.9.0
  • @agent-facets/adapter-opencode@0.12.0

Adapters plan, the CLI writes

Under adapter API 0.3, adapters are strictly read-only. An adapter inspects your project, decides what should change, and returns exact per-file transitions — an absolute path, the state it observed that path in, and the bytes to commit. The CLI performs every write through one filesystem transaction. This applies uniformly to assets, native MCP configuration, and the project’s own facets.json, facets.lock, and receipt.What you get in practice:
  • Byte-exact rollback. A failure restores the precise prior bytes and permission bits. Comments, member order, and TOML/YAML formatting come back exactly as authored — restoration no longer depends on re-rendering an asset from parsed data.
  • No phantom drift. A file already holding the bytes a plan would write contributes no change, so a re-install touches no modification time.
  • Inspected files stay untouched on rollback. A file the run only read is never journaled, so a concurrent edit to it survives a rollback.
  • Preconditions are checked immediately before each write. A file something else moved between planning and writing is refused and reported, never clobbered.

Safer MCP configuration

Two connected adapters may no longer manage the same MCP configuration file. The plan contract now returns every document it was computed from, and the CLI fails before approval with both adapters named when two claim the same file. See Troubleshooting for the resolution path.Every plan is also recomputed immediately before its own commit, including one that concluded nothing needed writing. A document edited while the approval screen was open is reported as MCP_NATIVE_STATE_DRIFT mid-run rather than silently reported as configured.

For adapter authors

Adapter API 0.3 is a breaking replacement of the adapter contract, not a widening. If you maintain a custom adapter, the surface has changed:
  • installAsset / readAsset / deleteAsset are replaced by assets: false | { planInstall, planRemoval }.
  • supportsInstall is gone — an adapter states its asset capability the same way it states MCP support.
  • The MCP capability is mcpServers: false | { plan }. apply, the opaque plan type, and the conflict/document-changed reason are removed; concurrency is detected once, by the CLI.
  • Every asset request carries projectRoot.
The Custom adapters guide documents the new planner-based SDK helpers.

Coordinated with the next CLI release

These adapter packages pair with the next CLI release. The current CLI on npm (agent-facets@0.31.0, shipped yesterday) continues to work with the previously released first-party adapters.
CLIImprovement
agent-facets 0.31.0 ships MCP server configuration end-to-end; hardened conflict, terminal, and file-format handling

agent-facets@0.31.0 on npm

MCP server configuration is now live end-to-end. The CLI release that turns on the feature previewed in the 2026-08-10 entry is published:
  • agent-facets@0.31.0
  • @agent-facets/protocol@0.31.0
  • @agent-facets/adapter@0.29.0
  • @agent-facets/adapter-claude-code@0.10.0
  • @agent-facets/adapter-codex@0.8.0
  • @agent-facets/adapter-opencode@0.11.0
Run facet self-update to pick it up.With this release, a facet declaring MCP servers in facet.json is configured in every connected tool on install — .mcp.json for Claude Code, opencode.jsonc or opencode.json for OpenCode, .codex/config.toml for Codex. Interactive runs pause on the MCP approval screen with Decline selected by default; non-interactive and frozen runs need --accept-mcp on facet add, facet install, or facet remove.

Hardening across the MCP stack

A round of fixes lands alongside the release:
  • Conflict reports name the exact key. Cross-facet collisions and MCP takeover prompts now identify each conflicting adapter key by name, so a report never leaves you guessing which entry a facet is claiming.
  • Terminal output escapes control characters. Server names, commands, arguments, and environment values shown on the approval screen and in failure reports are escaped, so a hostile declaration cannot rewrite what your terminal renders.
  • BOM and indentation preservation. Rewrites of .mcp.json, opencode.jsonc/opencode.json, and .codex/config.toml keep any existing byte-order mark and reuse the file’s own indentation, instead of forcing a reformat every time an entry is added or removed.
  • opencode.jsonc comments stay where you put them. The JSONC rewriter preserves comment placement across a reconciliation, so a project-owned edit does not shuffle notes attached to unrelated settings.
  • Install failures name the gate. When an install stops before writing, the failure block now names the specific gate that halted it (missing MCP approval, unsupported adapter API, collision, drift) rather than a generic message.
See facet install and Custom adapters for the current surface.
CLIBreakingNew Feature
Facets can declare MCP servers; consent before configuration; adapter API 0.2; breaking format bumps

Facets can declare MCP servers

A facet can now declare MCP servers in facet.json, and installing it configures them in every connected tool. A declaration is portable connection information — not a package reference. There is no server registry, no version range, and no image.
facet.json
Each adapter translates that into its own tool’s project file — .mcp.json, opencode.jsonc, .codex/config.toml — preserving your unrelated settings, comments, and any server entry the project does not own. Only project-scoped files are touched.Declaration objects are closed: an unrecognized member such as headers, cwd, or shell is rejected rather than ignored. Silently dropping a field that changes what executes is exactly the failure this prevents. A facet whose only deliverable is a server is now valid, and installs meaningfully with zero assets.

Approval before anything is configured

Configuring a server lets your coding tool run a command or open a connection, so a new or changed declaration needs your approval first. Interactive runs pause on one screen showing the exact command, arguments, and environment assignments — or the exact URL — with Decline selected by default.Non-interactive runs fail before writing anything and print the full list. The new --accept-mcp flag approves it, on all three pipeline commands:
It is on remove because a removal that must resolve the facets you are keeping re-enters the same consent path — without the flag such a removal has no way to finish in CI.To refuse one server instead of approving everything, record an omission:
facets.json
Approval is machine-local. It lives in the install receipt, never in facets.json or facets.lock, so a teammate who clones fully-approved state approves it on their own machine. A committed file must not be able to claim you consented to running something. A run that fails or rolls back banks nothing.You are not re-asked for a declaration you already approved — including when a second facet declares the same server identically. You are asked again when the command, argument order, an environment value, the transport, the URL, or the effective name changes.If a server name already exists in a tool’s config and this project did not put it there, the same screen says so before anything is written, and whether the existing entry matches or would be replaced.

Aliasing, omission, and collisions

Servers are aliased and omitted per project exactly like assets, under a new servers group in facets.json. Two facets declaring different servers at one effective name is a collision, resolved in the same interactive workspace and reported the same way in CI. Two facets declaring the same server identically is not a collision at all — they agree, and one entry is configured with both recorded as claimants.Servers occupy their own namespace: a server named deploy never collides with a skill, command, or agent named deploy.

Configuration is written, never run

Facets does not install, launch, connect to, health-check, or authenticate to a declared server, and stores no credentials. Whatever sign-in a server needs happens in your coding tool afterwards.Author note: env values are literal strings that ship inside the published archive. Never put a secret in one.

Breaking: old server reference forms are rejected

Breaking: the earlier speculative forms — a version string, or an object with image — now fail validation in both current and legacy manifests. There is no fallback and no skip warning; the previous warn-and-skip path is gone. Republish with a concrete declaration.

Breaking: format versions

Breaking: facets.json advances to manifestVersion 0.2, which adds the servers override group. Version 0.1 and unversioned documents still read, and a successful non-frozen install migrates them. A servers group inside a document declaring 0.1 is rejected rather than tolerated.Breaking: the machine-local install receipt advances to 0.4 to record configuration ownership and approval. Earlier receipts keep their asset authority but confer neither, so the first install after upgrading asks for approval once. Nothing to commit — the receipt is not in your project.Breaking: the adapter API advances to 0.2, adding a required mcpServers: false | McpServerCapability field. The CLI now supports exactly {0.1, 0.2}: a 0.1 adapter still works for a project with no MCP servers, and only blocks one that has them. Custom adapter authors should read the updated adapter guide.facets.lock stays at 0.3, deliberately. A facet’s integrity hash already covers the facet.json its declarations live in, so recording them again would add a second copy that can disagree. See why.

Rollout

Ships in stages: @agent-facets/protocol publishes the declaration schema and manifest 0.2, then the adapter SDK and all three first-party adapters publish API 0.2, then a CLI release turns it on. Existing CLIs keep selecting compatible 0.1 adapter releases until you update.
CLIImprovementFix
agent-facets 0.30.0 ships collision resolution; receipt hardening; facet instructions refresh

agent-facets@0.30.0 on npm

The cross-facet name collision resolution and asset aliasing feature described in the 2026-07-27 entry is now published as agent-facets@0.30.0 and @agent-facets/protocol@0.30.0. Run facet self-update to pick it up.

Install receipt hardening

The receipt is the record of what your machine actually materialized, and is what authorizes facet remove to delete a file. A few edges that used to be silent are now reported:
  • Frozen install that cannot write the receipt. Under --frozen-lockfile, if facet install materializes every asset but the receipt write fails, the run still succeeds — the locked set it reproduced is intact — and it now says the assets it wrote are untracked, instead of reporting a clean success.
  • Unusable receipt. A receipt that exists but cannot be used (unreadable, or recorded against a different project) is reported instead of being silently replaced by a lockfile projection. A single receipt entry that fails validation is reported the same way, and the files it covered are left in place.
  • Removal-resolution report names the gate. When facet remove falls back from the offline fast-path to the ordinary pipeline (which needs the network), the failure now names which gate sent it down that path, so an error naming a facet you are keeping is not a mystery.
  • Ctrl-C is honored during facet remove. Before anything is deleted, nothing is written; after deletion, the deletes are rolled back rather than committed.

facet instructions refresh

facet instructions has been refreshed for the 0.29 and 0.30 CLIs.
  • Generated overview index. The default overview now opens with an index of every instruction topic. The index is generated from a single typed topic registry, so it cannot drift from the topics the command actually serves.
  • Updated topic prompts. Topic content now covers README.md and supplementary-file authoring, single-segment asset naming and the shared skill/command namespace, and adapter API 0.1 compatibility recovery.
See the CLI reference for the full command surface.
CLIBreakingNew Feature
Resolve name collisions between facets; manifest 0.1, lockfile 0.3, and asset aliasing

Two facets, one name

Facets are published independently, so sooner or later two of them ship an asset with the same name. Until now the second one quietly overwrote the first. Now facet add and facet install detect every collision across the whole project before writing anything, and stop.In a terminal, the install pauses and hands you the decision:
Give each contested asset one of three outcomes — Keep it under the published name, Alias it to a name you choose, or Omit it entirely. Only one of them has to change; the names just have to differ from each other.What this means for you:
  • Nothing is written until you decide. A collision leaves facets.json, facets.lock, the receipt, and your materialized assets byte-identical. Cancelling with Esc ends the command without applying anything — it reports the cancellation and exits non-zero, and your project is exactly as you left it.
  • You decide once. Your choices are saved into facets.json, so the next install — and every teammate’s, and CI’s — replays them without prompting.
  • No tool picks a winner. Not the CLI, not the registry, not the facet authors. A silent winner means a silently discarded asset.

Alias or omit any asset

The same mechanism works without a collision. A facet entry can carry a materialization block naming, per asset, what you want:
facets.json
  • Aliasing renames the file, nothing else. Archive paths, per-file integrity, and the lockfile all keep the publisher’s authored name. That is what makes an alias safe: it can never be used to smuggle different content in under a familiar name.
  • Omitted assets are still verified. They are resolved, hashed, and recorded in the lockfile — they are just never written to disk. Dropping them from the lockfile would make an omission indistinguishable from a facet that never published the asset.
  • Skills and commands share one namespace, agents have their own. A skill deploy and a command deploy collide; an agent deploy coexists with both.
See Materialization for the full model.

Breaking: facets.json entries may be objects

facets.json now carries a manifestVersion (currently 0.1), and a facet entry is either a source string or an object with source and materialization:
facets.json
If you have tooling that reads facets.json, it must handle both forms. Code that assumes every value is a string will render an expanded entry as [object Object] — or, worse, destroy a recorded alias when it rewrites the file.A manifest with no manifestVersion is still read as the legacy format and is migrated in place on the next successful install. There is no separate migration command and nothing to run.

Breaking: lockfile 0.3 (and receipt 0.3)

Every asset entry in facets.lock now carries a required materialization disposition — authored, aliased, or omitted. Version 0.2 remains readable and is migrated to 0.3 by any normal install. The withdrawn closed-alpha 1 is no longer read at all: if you still have one, delete facets.lock and run facet install to regenerate it. The number 1 is reserved for the eventual stable v1 format.The machine-local install receipt moves to 0.3 alongside it, so it can record the disposition each asset was materialized under. It migrates itself on the next install and lives outside your repository, so there is nothing to commit and nothing to run.
facets.lock
Note name and files keep the authored name even when aliased. Versions are matched exactly, never by ordering: a version number names a schema rather than a position in a sequence, so a reader recognizes the versions it supports and fails on anything else instead of comparing magnitudes.Each asset’s files are also checked against the asset itself now, not just for path safety: an agent or command lists exactly its own primary file, and every path in a skill lives under that skill’s directory. A lockfile that pointed an asset at an unrelated file no longer validates.In CI: a --frozen-lockfile install fails if facets.json declares any materialization override while facets.lock is still at 0.2, which has no field in which to record it. Run one normal facet install locally and commit the migrated lockfile. Frozen mode also treats a changed alias as drift, exactly like a changed version, and never prompts.

The install pipeline resolves everything before writing anything

Commit used to be an interleaved loop — resolve facet A, write it, then start facet B. Detecting that A and Z want the same name is impossible that way, because A is already on disk.It now runs in three phases: Resolve-all, Compose, then Apply. The first two are read-only, which is why a collision, an invalid alias, or a cancelled resolution leaves the project untouched rather than rolled back. Deletion also moved to the start of Apply, before any write, so an asset name can transfer from one facet to another in a single install without the new owner’s file being deleted behind it.Removing a facet is the exception. When your machine’s install receipt already accounts for everything you are keeping, facet remove refines those lockfile entries in place instead of resolving them, so it still works with a cold cache and an unreachable registry.Two smaller consequences you may notice:
  • Disposition changes report as updated. Aliasing an asset at an unchanged version is a real change to what is on disk, not a no-op and not a repair.
  • Stale choices are pruned and reported. If you upgrade a facet and the publisher removed an asset you had aliased, the override is dropped on a successful install and the CLI tells you which one.

Breaking: only your install receipt authorizes deletion

facets.lock is shared, version-controlled state. It says what should be materialized — not what your machine actually wrote — so it is no longer treated as proof of ownership. Deletion is driven by the machine-local install receipt alone.Two rules replace the old fallback:
  • Your project’s desired state authorizes writes. An install still materializes every declared asset, overwriting an unmanaged file that happens to occupy the same name. (If what is already there matches byte for byte, it is adopted without a rewrite.) Either way the file is tracked from then on.
  • The receipt authorizes deletions. Files the CLI has no record of writing are never deleted.
What you may notice:
  • Pull a teammate’s facets.lock onto a machine that never ran an install, then facet remove the facet: facets.json and facets.lock drop it and the files stay exactly where they are. The summary says so instead of reporting a cleanup that did not happen, and those files are then yours to delete by hand.
  • facet remove may need the network. The offline guarantee is a property of tracked state, not of removal. If a facet you are keeping is not fully accounted for by your receipt, the removal runs the ordinary pipeline to materialize it before claiming it — which succeeds when the content is reachable and fails, deleting nothing, when it is not. Run facet install first to take ownership.
  • An unusable receipt is reported. Unreadable, or recorded against a different project: nothing on disk is tracked, so nothing is cleaned up, and the run records only what it materializes.

Breaking: facet remove decides what is declared under the lock

Whether a name is declared is now answered by the commit, holding the project lock, instead of by a read taken before it. A pre-lock read can be stale, and acting on one let a facet added by a concurrent process survive the removal that asked for it.So facet remove ghost in a project with no adapter opens the picker in a terminal and exits non-zero in CI, rather than printing a no-op summary it never verified. Names that really are absent under the lock are still ignored, and a removal whose names are all absent still succeeds. An unreadable facets.json is still reported as a manifest problem, before any adapter is discovered.

Adapter authors

The adapter contract is unchanged — no rebuild, no version bump. But request.name has always been, and now visibly is, the effective name: the name the consuming project chose. Address files by request.name and never re-derive a path from a facet’s manifest. See Custom adapters.
CLIBreakingNew Feature
Facets ship non-asset files; archive 0.2, per-file lockfiles, first-class README, and the 0.1 adapter contract

Facets can ship non-asset files

A facet is no longer just its assets. You can now declare supplementary files that travel inside the .facet archive and are integrity-protected like everything else:
  • Skill companions — files beside a SKILL.md (references, scripts, templates). They install and remove atomically with their skill, and files you add to a skill directory yourself are preserved on removal.
  • Top-level archive-only filesREADME.md, LICENSE, design notes. They ship in the archive but are never written to disk at install.
Declare them with exact paths (no globs) in the manifest’s top-level files array or a skill descriptor’s own files array:
See Supplementary files.

First-class README authoring

facet create now writes and declares an editable README.md by default (skip it with --no-readme), and facet edit has a dedicated README panel for creating, adopting, editing, or removing both README.md and the extensionless README.

Breaking: archive 0.2, per-file lockfiles, and naming

  • Archive format 0.2. Every build now emits facetVersion: 0.2 with a complete per-entry hash map covering every file, asset or not. Consumers still accept legacy 0.1 archives during the compatibility window; the two are dispatched by exact version match, never numeric ordering. An archive whose version your CLI doesn’t support fails with upgrade guidance — run facet self-update.
  • Lockfile 0.2. facets.lock records a per-file { path, integrity } record for every materialized file inside each asset, and install reconciles them before writing, reporting and repairing drift by exact path. A verified legacy alpha lockfile migrates automatically on a normal install. See Lockfile.
  • Asset names. Skill, command, and agent names are now single-segment Agent Skills names — slash-namespaced names like viper-plans/planning are no longer valid in new manifests (use viper-planning). Skills and commands now share one namespace and can’t reuse a name.
  • Primary files carry no front matter. Asset metadata lives in the manifest; a SKILL.md, agent, or command file with YAML front matter is now a build error. Supplementary files still ship verbatim.

Breaking: adapter API 0.00.1

The adapter contract advances from the positional single-file shape (0.0) to the tagged multi-file request/result contract (0.1) that carries skill companions. A 0.1 CLI treats a positional 0.0 adapter as unsupported and fails closed before any write. Rebuild against a 0.1 SDK and reinstall:
The archive facetVersion, the lockfile version, and the adapter API are three independent axes — a consumer classifies each separately. See Custom adapters.

Rollout

The change ships consumer-first: the protocol package publishes dual 0.1/0.2 verification, then the registry adopts it, then the adapter SDK and first-party adapters publish 0.1, and finally the CLI release turns on 0.2 production. Existing 0.0 CLIs keep selecting compatible 0.0 adapters until you update.
CLIImprovement
facet remove now prunes empty adapter directories

facet remove prunes empty adapter directories

Removing a facet now cleans up empty parent directories left behind in each connected adapter. When facet remove (or facet rm) deletes the last asset inside a nested folder — for example skills/<name>/SKILL.md under Claude Code or OpenCode — the CLI walks up and removes empty parent directories, stopping at (but not including) the adapter’s root.What this means for you:
  • Tidier working tree. Uninstalling a facet no longer leaves behind empty namespace folders that you’d otherwise have to delete by hand.
  • No configuration. The behavior is automatic for the Claude Code and OpenCode adapters — no flags, no opt-in.
  • Bounded to each adapter. Pruning stops at the adapter’s own root, so it can never walk into the rest of your project.
Ships in @agent-facets/adapter@0.26.0, @agent-facets/adapter-claude-code@0.7.0, and @agent-facets/adapter-opencode@0.8.0. Run facet self-update to pick it up.
CLIImprovementBreaking
Server-side search for facet search; asset names now follow the Agent Skills grammar
facet search now forwards your query term to the registry as the ?q= parameter on the facets endpoint and renders whatever the server returns, in order. The registry — not the CLI — owns relevance ranking, sorting, and paging.
What this means for you:
  • Results match the web UI. Ordering is consistent with what agentfacets.io shows, because both surfaces read the same server-ranked list.
  • Better matching, no upgrade required. Improvements to registry search (typo tolerance, ranking, new sort orders) take effect immediately — the CLI just displays what the server sends.
  • Old behavior removed. The client-side case-insensitive name substring filter is gone. If a term returns no matches on the server, the CLI now shows an empty result instead of falling back to local filtering.
See the facet search reference for usage.

Asset names now follow the Agent Skills grammar

Breaking: skill, command, and agent names declared in facet.json are now validated against the Agent Skills name grammar at both build and install time. Each /-separated segment must be:
  • 1–64 characters long
  • lowercase ASCII letters, digits, and hyphens only
  • no leading, trailing, or consecutive hyphens
Digit-start names like 2fa are now accepted; previously they were rejected. Non-conforming names like MySkill or foo_bar are now rejected — previously they slipped past the looser kebab-case check. Namespaced names like viper-plans/planning validate per segment.
Error messages now cite the specific rule that failed (e.g. "must be at most 64 characters") instead of a generic kebab-case hint.If you have a facet with a legacy non-kebab asset name, facet modify --update and facet modify --remove still accept the old name so you can rename or drop the asset without a chicken-and-egg problem. See the manifest schema and facet modify reference.
CLINew FeatureImprovementFix
Agent-friendly CLI: instructions, modify, headless create, build --verify; TypeScript 7 support; trailing newlines on generated JSON

TypeScript 7 support

The public @agent-facets/* packages now declare their typescript peer dependency as ^5 || ^6 || ^7, so they install cleanly on projects that have upgraded to TypeScript 7. Consumers still on TypeScript 5 or 6 are unaffected — no action needed.This applies to:
  • @agent-facets/protocol
  • @agent-facets/adapter
  • @agent-facets/brand
  • @agent-facets/adapter-claude-code
  • @agent-facets/adapter-codex
  • @agent-facets/adapter-opencode

Trailing newlines on generated JSON

Every JSON file the CLI writes now ends with exactly one trailing newline and uses consistent 2-space indentation:
  • facets.lock
  • install receipts and parallel-install lock payloads
  • build manifests inside the .facet archive
  • scaffolded facet.json (from facet create)
  • cache integrity sidecars
Previously these files ended without a trailing newline, so editors configured to auto-append one produced a one-character diff every time you opened a CLI-generated file. That’s fixed — open a file, save it, no diff.facets.json continues to be written through the comment-preserving serializer (so your hand-edited comments survive) and now upholds the same trailing-newline invariant.

The CLI is now agent-friendly

Every interactive authoring flow now has a non-interactive, scriptable path, and the CLI can describe itself to an AI agent on demand. Three themes: built-in instructions, headless authoring, and machine-readable output.

New command: facet instructions

facet instructions [topic] prints agent-oriented usage guidance — how to author and use facets, written for an autonomous agent to read before it acts. It is workflow guidance, not per-flag help.
The manifest topic appends the facet.json JSON Schema generated live from the schema definition — a single source of truth, never a hand-maintained copy. See facet instructions.

New command: facet modify

facet modify is the scriptable counterpart to the interactive facet edit wizard: one headless, flag-driven edit to a facet.
Per-asset adapter config uses a name-embedded flag whose value is a JSON object: --adapter-<name> '<json>' sets a block (replacing it wholesale), and --remove-adapter-<name> deletes one. Add --json for a structured change summary. Exactly one lifecycle action (--add, --remove, or --rename) is allowed per call; illegal flag combinations are rejected up front. See facet modify.

facet create gains headless flags

Pass authoring flags to scaffold a facet without the interactive wizard:
Repeat --skill/--agent/--command for multiple assets; --private marks the facet private, --force overwrites an existing facet.json (required in headless mode), and --json prints a structured result. See facet create.

facet build --verify and --json

facet build --verify runs the full validation pipeline without writing any output — a no-op build that confirms facet.json and every asset are valid and buildable. --json emits a machine-readable result and works with or without --verify.
See facet build.

Fix: subcommand --version is no longer shadowed

The global --version flag now applies only before the command name, so facet modify facet --version 1.2.3 sets the facet’s version instead of printing the CLI version.
CLINew Feature
New facet remove command (aliased rm)

New command: facet remove

facet remove <facet> [more facets...] is the inverse of facet add: it takes facets back out of a project. In one command it removes the named facets from facets.json, deletes their assets from every connected adapter, and rewrites facets.lock without them.
Removal reuses the same install pipeline as facet add, so it is transactional: any failure restores facets.json byte-for-byte and leaves the project unchanged. Removing multiple facets is all-or-nothing — if any name is not declared in facets.json, nothing is removed. Every facet you don’t name is left untouched.See the facet remove reference for details.
CLIBreakingNew FeatureFix
Bearer-token auth for the registry; new login/whoami/logout commands; FACET_REGISTRY_API_KEY removed; install now re-resolves a stale lockfile; new --frozen-lockfile flag

Registry authentication is now bearer-token based

Breaking: the FACET_REGISTRY_API_KEY environment variable has been removed — there is no fallback or deprecation shim. The CLI now sends an Authorization: Bearer <token> header, where the token is a you mint in the web UI.Provide the token one of two ways:
When FACET_TOKEN is set it takes precedence over the saved file. Read-only commands like facet search and facet add send the token too when one is available (earning a higher rate-limit tier) and work anonymously otherwise.

New commands: login, whoami, logout

  • facet login — guided sign-in. Paste a PAT; the CLI verifies it against the registry before saving it, so a typo or expired token fails fast instead of surfacing later at publish time. A browser sign-in option is shown as “coming soon”.
  • facet whoami — prints the signed-in username, email, and tier, and notes when FACET_TOKEN is the active credential.
  • facet logout — removes the saved credentials file. It makes no server call; revoke PATs in the web UI. If FACET_TOKEN is still set, it tells you so.

facet publish takes an optional directory

facet publish now accepts a directory argument and defaults to the current directory, matching facet build, facet edit, and facet create:
A first-time publish of a reserved or over-budget global facet may be queued for admin review — this is reported as a success, not an error.

Registry errors render verbatim

When the registry rejects a request, the CLI now shows the registry’s own message and suggested fix rather than translating the error code through a local table. The registry is the single source of truth for what an error means — so error guidance stays accurate as the registry evolves, with no CLI release required.See the Publish Flow spec for the full authentication model.

Editing a version in facets.json now takes effect

Fix: facets.json is the source of truth, but facet install was ignoring an edited version when the lockfile still pinned the old one. If you bumped a facet to a version that didn’t exist, install “succeeded” and wrote a self-contradictory lockfile entry instead of failing.Now facet install compares each lockfile entry against its manifest specifier. A locked version that no longer satisfies the manifest is treated as stale and re-resolved:
A wildcard the lock still satisfies (manifest 1.*, lock 1.2.3) is unaffected — it stays pinned and reproducible. See resolution semantics.

New: facet install --frozen-lockfile

For reproducible CI installs, --frozen-lockfile makes the lockfile the source of truth: install never re-resolves or writes the lockfile, and fails if the lockfile is missing, omits a manifest facet, or has drifted out of sync with facets.json.
This mirrors --frozen-lockfile from npm and bun. See frozen lockfile semantics.
CLIBreaking
Consolidate everything under FACET_DIR; new FACET_BIN_OVERRIDE; lock moves out of project root

One directory, one override

The facet CLI used to spread its state across four near-identical locations and five separate environment variables. State for the cache, installed adapters, the curl-installed binary, and parallel- install coordination all lived in different places with different overrides. This release collapses everything into a single root: one directory, one environment variable.

The new shape

Everything the CLI writes to disk now lives under $FACET_DIR:
The default for $FACET_DIR is ~/.facet/. Setting FACET_DIR IS the override — there is no separate variable per subsystem. Curl installs put the binary at $FACET_DIR/bin/facet; facet add and facet install resolve cache and adapters relative to the same root.

What changed

FACET_CLI_REGISTRY (npm registry URL override) and FACET_VERSION (used by install.sh) are unchanged.

FACET_BIN_OVERRIDE, by name

The launcher’s binary override gets a new name that carries its own semantics: FACET_BIN_OVERRIDE. Setting it means you’ve taken control of which binary the launcher executes. facet self-update continues to refuse while it’s set, because if you’ve overridden the binary path, self-update has no business writing over whatever you pointed it at. The refusal is coupled to the override on purpose.Unset FACET_BIN_OVERRIDE to re-enable self-update for a real install.

Project root: clean

The install advisory lock no longer touches your project root. Previously, facet install materialized .facets/.install.lock next to facets.json — a project-local directory that wasn’t tracked anywhere and could be left behind on crashes. The lock now lives at $FACET_DIR/locks/<basename>-<sha256(realpath)[:16]>.lock, keyed by the project’s canonical path so two checkouts of the same repo at different paths (git worktrees, Conductor workspaces) get distinct locks.No .facet.lock file, no .facets/ directory, no untracked entry next to your facets.json. The project root stays as clean as facets.json itself.Breaking: No automatic migration. The new code reads $FACET_DIR only — existing cached payloads and adapters at ~/.facets/ are not detected, copied, or warned about. Old env vars (FACETS_CACHE_DIR, FACETS_ADAPTERS_DIR, FACET_CACHE_DIR, FACET_ADAPTERS_DIR, FACET_INSTALL_DIR, FACET_BIN_PATH) are silently ignored — anyone who had them set in shell rc files or CI configs must rename to FACET_DIR / FACET_BIN_OVERRIDE or the values stop taking effect. Existing ~/.facets/ data can be deleted at any time; the new code will rebuild cache and adapters on first use.See the environment variables reference and the facet install page for the updated paths.
CLIBreakingImprovement
@agent-facets/core split into protocol (public, Node-native) and engine (private, Bun-native)

Three layers, honestly named

@agent-facets/core was always two things: the facet artifact specification (schemas, integrity rules, deterministic archive format, hash algorithm) and the Bun-native CLI implementation of that specification (subprocess-driven adapter bundling, registry HTTP client, install pipeline, scaffold, edit, self-update). The first set is portable Node-runnable data + cryptography that any third party — a registry server, a future alternative CLI, an offline .facet linter — needs to honor. The second is intrinsically Bun-native and runs only on a developer’s machine.Splitting them produces three honest layers:
  • @agent-facets/protocol (NEW, public, Node-native, Node 22+) — the TypeScript reference implementation of the facet artifact specification. Schemas, bytes-validators, integrity verification, content hashing, deterministic tar layout, version-spec grammar, front-matter encoding, build validators. Pure data + cryptography. No subprocesses, no network, no developer-machine state.
  • @agent-facets/engine (RENAMED from @agent-facets/core, made private) — the Bun-native CLI machinery. Install pipeline, registry HTTP client, adapter machinery, source resolvers, manifest mutations, cache, scaffold, edit, self-update, build pipeline orchestrator, gzip compression, path-based loaders. Internal to the monorepo; never published.
  • agent-facets (the CLI binary, unchanged) — argv parsing, Ink TUI, error formatting, exit codes.
See the specification for the full layer description and the design rationale for keeping the registry HTTP API outside the protocol.Breaking: @agent-facets/core is no longer published. The package is frozen at v0.9.1 on npm; existing pins continue to resolve, but there will be no further versions. New consumers (registry servers, third-party tooling) MUST use @agent-facets/protocol. There is no deprecation message on the legacy package — closed-alpha, no known external consumers.No CLI behavior change. Every @agent-facets/core import in the CLI was redirected to either @agent-facets/protocol (data primitives) or @agent-facets/engine (orchestrators). User-visible commands, flags, and output are unchanged.
CLINew FeatureBreaking
facet add now installs in one step; new source grammar; lockfile-driven install

facet add and facet install converge

facet add now does everything end-to-end. Resolve, fetch, verify integrity, materialize into adapters, write the lockfile — all in a single command. There is no separate facet install step after facet add.
facet install is still there, and it’s the right command after a fresh git clone or after pulling teammate changes that updated facets.json. It honors any pinned versions in facets.lock verbatim and only resolves entries that don’t have a lockfile entry yet — making installs reproducible across machines without a separate facet update command.If a project has no adapters installed, both facet add and facet install now auto-launch the adapter picker on a TTY, so first-run experience is a single command from a cold start.

New source grammar

facet add accepts a richer set of sources, aligned with what npm and bun users already expect:
Bare names default to the resolved exact version when written back to facets.json — so facet add viper-plans produces viper-plans@1.2.3 in the manifest, the same way npm install and bun add pin lockable defaults.

Lockfile-driven, with bootstrap

facets.lock is now the single source of truth for what gets installed:
  • When a lockfile entry exists, that exact version is fetched. The manifest’s range is not re-resolved.
  • When a lockfile entry doesn’t exist (first run, or a freshly-added manifest entry), the manifest specifier is resolved fresh.
  • When facets.lock doesn’t exist yet, facet install bootstraps it — the same way bun install creates bun.lock.

Three-check integrity protocol

Every fetched facet is verified before any asset is written:
  • Registry sources run three independent checks: cache vs. registry metadata, archive manifest vs. registry metadata, computed content vs. archive manifest. Each defends against a distinct adversary.
  • Git sources run a single check: computed content vs. lockfile integrity. Defends against tag-move attacks.
  • Local sources are trust-by-path.
Any mismatch is a hard security error. The install aborts before any asset is written; the project is exactly as it was before.

Cache

Resolved facet content is cached at ~/.facets/cache/<name>@<version>/. Subsequent installs of the same identity hit the cache instead of the network. Override with the FACETS_CACHE_DIR environment variable.

Repaired outcome

If you delete a materialized asset by hand and re-run facet install, the affected facet now reports as repaired in the summary — the adapter file is restored without bumping the version. This makes self-heal explicit instead of silent.

Breaking changes

  • git+ prefix is removed. Use plain https://...git or git@host:owner/repo instead. The new grammar accepts everything git+ did, just without the prefix.
  • Caret, tilde, and comparator version ranges are rejected. Use 1.* for major-pinned, 1.2.* for minor-pinned, * or latest for unpinned, or 1.2.3 for exact. The @latest alias and bare-name form both produce the same result as *.
  • facet install --dry-run is gone. No replacement; facet install always commits.
  • facet install rejects positional arguments. To add a new facet to the project, use facet add.
See the facet add and facet install CLI reference for full details.
CLIFix
Publish pipeline fixed across all packages

Publish pipeline fixed across all packages

Recent releases of agent-facets and the supporting packages had been failing or shipping inconsistently due to issues in the publish pipeline. All affected packages have been republished from a known-good state:
  • agent-facets — the CLI
  • @agent-facets/core and @agent-facets/adapter — authoring and adapter SDKs
  • @agent-facets/adapter-claude-code, @agent-facets/adapter-opencode, and @agent-facets/adapter-codex — first-party adapters
If you installed or upgraded the CLI in the last week and ran into install or runtime errors, reinstall:
Or, on any platform with Node.js:
No usage changes — facet add, facet install, and the adapter commands all behave the same as before.
Improvement
Mobile-responsive landing page

Mobile-responsive landing page

The agentfacets.io landing page is now fully responsive. If you previously visited on a phone or tablet, the experience was broken — the scroll-linked demo, navigation, and layout all assumed a desktop viewport. That’s fixed.Here’s what changed:
  • Mobile navigation — the nav bar collapses into a slide-down menu on screens ≤ 1024 px wide, with all links accessible from a single tap.
  • Adapted CLI demo — the interactive terminal demo skips the widest step on small screens so it fits without horizontal scrolling.
  • Stacked sections — the explainer, hero, and footer all reflow into a clean single-column layout on narrow viewports.
  • Registry CTA — on mobile, the install command is replaced with a link to agentfacets.io so you can browse facets without needing a terminal.
The desktop layout is unchanged.
Improvement
Landing page and new docs URL

agentfacets.io landing page

The Agent Facets website now has a proper landing page at agentfacets.io. It walks you through what facets are, shows a live CLI demo, and makes it easy to get started with a single install command:

New docs URL

Documentation has moved to its own subdomain at docs.agentfacets.io. The main domain at agentfacets.io now serves the landing page, and the CLI installer lives at agentfacets.io/install. Existing docs links have been preserved — you’ll just land on the new URL. Update any bookmarks accordingly.
CLINew Feature
Install pipeline and new install URL

facet add and facet install

You can now add facets from external sources and install them into your AI coding tools end-to-end.facet add resolves a facet from GitHub, a Git URL, or a local path and writes it to your project’s facets.json, preserving any hand-edited comments:
facet install reads facets.json, builds each facet, and materializes its assets into every adapter you’ve connected. The pipeline is built for iteration:
  • Lockfile diffing — only changed assets are written on each run.
  • Rollback on failure — if something goes wrong mid-install, changes are reversed automatically.
  • Concurrent safety — an atomic install lock prevents two facet install runs from interfering with each other.
  • --verbose — full pipeline trace for debugging.
See the facet add and facet install CLI reference for details.

New install URL

The CLI installer has moved to its own home at agentfacets.io/install. If you previously bookmarked the install URL, update it to:
This works on macOS and Linux. For Windows or any platform with Node.js, you can install via npm instead:
The download link on the docs site has been updated automatically.
CLIImprovement
Faster adapter installs

Self-contained adapter bundles

Adapters now ship as fully self-contained bundles with all dependencies inlined. When you run facet adapter install, the CLI uses a prebuilt fast path that skips the build step entirely — falling back to a full rebuild only if the prebuilt bundle is missing or incompatible.This also means adapter installs no longer leave build artifacts in your source tree.See the environment variables reference for configuring the adapter install location with FACETS_ADAPTERS_DIR.
CLINew Feature
Adapter system with Claude Code, OpenCode, and Codex

Adapter SDK and first-party adapters

Adapters are the bridge between facets and your AI coding tool — each adapter knows where and how to write assets for a specific tool. The first three first-party adapters ship today:
  • Claude Code
  • OpenCode
  • Codex (early access — installable, but asset materialization via facet install is coming soon)
When you run facet install, the CLI writes your facet assets into the correct locations for every adapter you’ve installed. Third-party adapters use the same installation and loading mechanism as first-party ones — install via npm, Git URL, or local path.Manage adapters with:
If no adapters are installed when you run facet install, an interactive picker appears so you can select which AI tools to connect.See the adapter CLI reference for full usage, including all built-in adapter names.
CLIImprovement
Self-contained archives

Self-contained .facet archives

The .facet build output is now a single self-contained archive. The build manifest is embedded inside the archive rather than shipped as a separate file, making distribution simpler. You can extract the manifest for debugging with --emit-manifest during build.See the facet build CLI reference.