fez
Extension API

Building extensions

Choose the tools, views, and service parts your package needs.

fez's rule is simple: if a feature can live behind a seam, it must. Core grows only when a new seam is needed — never a new feature. Extensions are how everything user-facing ships, and this section is the contract you build them against.

The surface model

A fez extension is one npm package with optional attachment points, declared under the fez key in package.json. Executable parts are bundled files installed under ~/.fez/; their hosts load them with an injected API. Declarative GUI parts are JSON rendered by the host. Skill definitions and background flags configure how a package attaches rather than providing another executable bundle.

PartInstalls toLoaded by
headless~/.fez/extensions/the TUI; permitted background parts also load in the desktop worker or optional sentinel
gui~/.fez/packages/<name>/ on the current desktopdeclarative host rendering or the declared GUI runtime; legacy CLI installs may also stage gui-extensions/
miner~/.fez/miners/the mining harness
relay~/.fez/relay-extensions/a relay started with --extensions
workspace~/.fez/workspace-providers/the agent runtime, for repo: personas
skillsettings.jsonmcpServersagents at spawn (an MCP server definition)
background: truesettings.json allowlistopts scheduled tasks into the desktop worker or optional sentinel

Plus npm's own bin map (→ ~/.fez/bin/) and fez.personas for shipping persona packs — @fezchat/score-studio is a complete extension that ships only a persona.

One fez install places every part; each host picks up its own. In the current source implementation, the desktop selects a GUI runtime from the manifest and its separate fez-background process loads permitted background headless parts. Headless commands still belong to the TUI; desktop commands need a gui part.

Choose a GUI runtime before building a desktop part. Declarative data covers standard settings and themes; isolated child views cover custom UI. An omitted runtime selects the legacy shared webview.

The contract package

npm install -D @fezchat/extension-api
import type { FezExtensionAPI } from "@fezchat/extension-api/headless";
import type { GuiExtensionApi } from "@fezchat/extension-api/gui";
import type { RelayExtensionAPI } from "@fezchat/extension-api/relay";
import type { WorkspaceProvider } from "@fezchat/extension-api/workspace";

It is types only — every export erases at bundle time, so your shipped part carries zero runtime dependency on fez. (Some first-party packages instead carry a hand-written structural mirror in src/api-types.ts; a conformance eval keeps every mirror honest against the host. For new extensions, use the package.)

The published contract is a deliberate subset of what hosts implement; a few host-only registration seams (harnesses, themes, system-prompt sections) are noted on each surface's page.

Which surface do I need?

  • A slash command or a scheduled job → headless
  • A card under messages, a panel, a view over a doc → gui
  • Serving HTTP from the relay (git, media) → relay
  • Giving a repo: persona its checkout → workspace
  • A tool agents call → the skill part (an MCP server your package ships — see the manifest)

Worked examples in the repo: packages/fez-polls (headless + gui + skill sharing one vote-logic module), packages/fez-git (all four surfaces plus two binaries), packages/fez-score-studio (personas only, no code).

Start with the dev loop: fez create, fez link, publish.

On this page