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 stopfacet build,facet add,facet remove, andfacet 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 authenticationand 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.
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.
Git-sourced installs no longer crash on a cold cache
The fix ships inagent-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.
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 updaterespects your ranges. Every facet moves to the newest version its declared specifier already permits. --latestcrosses 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.*becomes2.*, and*andlatestare left exactly as authored.--dry-runwrites nothing. No manifest, no lockfile, no receipt, no assets, and no adapter installation, so it is safe on a machine with no adapter connected.--interactivepicks per facet. Every row starts on its latest release with nothing selected; a facet you never touch is left alone.
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.Rollback no longer leaves empty directories behind
Whenfacet 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.
- 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
.claudeor.opencode). It can remove that directory only when the failed install created it.
OpenCode: MCP servers land where OpenCode actually reads them
The OpenCode adapter previously considered only the project root’sopencode.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:.opencode/opencode.jsonc.opencode/opencode.jsonopencode.jsoncopencode.json
- the highest-precedence document that already defines an
mcpmember (an empty{}counts); - otherwise the highest-precedence document that exists;
- otherwise
.opencode/opencode.jsonc, created.
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.
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
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: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.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 API0.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. Theplan
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 API0.3 is a breaking replacement of the adapter contract,
not a widening. If you maintain a custom adapter, the surface has
changed:installAsset/readAsset/deleteAssetare replaced byassets: false | { planInstall, planRemoval }.supportsInstallis 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 theconflict/document-changedreason are removed; concurrency is detected once, by the CLI. - Every asset request carries
projectRoot.
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
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.tomlkeep 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.jsonccomments 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.
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 infacet.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
.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: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
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 newservers 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 withimage — 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 authorizesfacet 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, iffacet installmaterializes 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 removefalls 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.mdand supplementary-file authoring, single-segment asset naming and the shared skill/command namespace, and adapter API0.1compatibility recovery.
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. Nowfacet 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:- Nothing is written until you decide. A collision leaves
facets.json,facets.lock, the receipt, and your materialized assets byte-identical. Cancelling withEscends 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 amaterialization 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
deployand a commanddeploycollide; an agentdeploycoexists with both.
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
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
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.
- Pull a teammate’s
facets.lockonto a machine that never ran an install, thenfacet removethe facet:facets.jsonandfacets.lockdrop 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 removemay 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. Runfacet installfirst 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. Butrequest.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 files —
README.md,LICENSE, design notes. They ship in the archive but are never written to disk at install.
files
array or a skill descriptor’s own files array: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 emitsfacetVersion: 0.2with a complete per-entry hash map covering every file, asset or not. Consumers still accept legacy0.1archives 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 — runfacet self-update. - Lockfile
0.2.facets.lockrecords 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/planningare no longer valid in new manifests (useviper-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.0 → 0.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: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 dual0.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.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.
@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
Server-side search for facet search
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.- 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.
facet search reference for usage.Asset names now follow the Agent Skills grammar
Breaking: skill, command, and agent names declared infacet.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
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."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
.facetarchive - scaffolded
facet.json(fromfacet create) - cache integrity sidecars
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.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.--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:--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.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.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.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: theFACET_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: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 whenFACET_TOKENis the active credential.facet logout— removes the saved credentials file. It makes no server call; revoke PATs in the web UI. IfFACET_TOKENis 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: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: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.--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:$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.
@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: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.lockdoesn’t exist yet,facet installbootstraps it — the same waybun installcreatesbun.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.
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-runfacet 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 plainhttps://...gitorgit@host:owner/repoinstead. The new grammar accepts everythinggit+did, just without the prefix.- Caret, tilde, and comparator version ranges are rejected. Use
1.*for major-pinned,1.2.*for minor-pinned,*orlatestfor unpinned, or1.2.3for exact. The@latestalias and bare-name form both produce the same result as*. facet install --dry-runis gone. No replacement;facet installalways commits.facet installrejects positional arguments. To add a new facet to the project, usefacet add.
Publish pipeline fixed across all packages
Recent releases ofagent-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/coreand@agent-facets/adapter— authoring and adapter SDKs@agent-facets/adapter-claude-code,@agent-facets/adapter-opencode, and@agent-facets/adapter-codex— first-party adapters
facet add, facet install, and the adapter commands all behave the same as before.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.
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 atagentfacets.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.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 installruns from interfering with each other. --verbose— full pipeline trace for debugging.
New install URL
The CLI installer has moved to its own home atagentfacets.io/install. If you previously bookmarked the install URL, update it to:Self-contained adapter bundles
Adapters now ship as fully self-contained bundles with all dependencies inlined. When you runfacet 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.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 installis coming soon)
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: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.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.