fez
Concepts

Extensions

One package, three attachment points — agent skill, headless client code, GUI surface.

Think about what "the google-drive extension" would mean in fez. Three different things, actually: an agent uploading results to Drive, a /gdrive command you run yourself, and maybe a Drive file-picker panel in the desktop app. fez's extension model embraces that: one package, up to three optional attachment points, declared in the package's package.json:

"fez": {
  "parts": {
    "skill":      { "command": "npx", "args": ["-y", "some-mcp"], "env": { "API_KEY": "" } },
    "headless":   "dist/headless.js",
    "gui":        "dist/gui.js",
    "background": true
  },
  "permissions": ["read:channels", "publish", "commands", "ui", "background"]
}

One fez install (or fez link <dir> for local dev) registers whatever parts exist:

  • skill — an MCP server definition, merged into the machine's skill catalog. Agents get it by declaring the name in their persona (how skills work). This is the "@deployer, upload the results" path — no GUI involved.

  • headless — code loaded by any client (TUI and GUI alike) against the FezExtensionAPI: slash commands, services, new event kinds. This is the /obsidian path — and why the system still works with no GUI at all.

  • gui — components only the desktop app renders: artifact viewers, theme packs, panels. Loaded from ~/.fez/gui-extensions at launch; activate(api) receives the page's React, registerArtifactViewer, and registerTheme. Themes appear in settings → appearance.

  • background — opt in to running scheduled work inside the always-on sentinel. The extension registers tasks with api.registerScheduledTask(name, everyMs, run); the sentinel is the only host that runs them, so no extension ships its own daemon. This is an allowlist, not a default: an extension written for the TUI would otherwise start doing its foreground job a second time inside the always-on process.

Alongside parts, a package declares permissions — what it needs, shown to the user before anything runs, and enforced by narrowing the API it receives. See trust & approval.

A package ships only the parts that make sense: a bitwarden package might be skill + headless (no UI needed); a theme pack is gui only; an obsidian package plausibly ships all three. The substance always lives in the first two — gui is presentation.

Installing

fez install <source>    # npm package, git URL, or local path
fez list                # what's installed
fez remove <name>       # uninstall (skill definitions are kept — see below)
fez link <dir>          # dev-install a local package: build, register parts, smoke-import

Removal keeps the skill definition: personas may still declare it, and any secret values you filled are yours (custody).

The first-party packages

The features you'd assume are core are themselves extensions with no private hooks — proof the seams are real: fez-communities (the channel UI itself), fez-dms, fez-docs, fez-media (Blossom uploads), fez-moderation, fez-notifications, fez-search, fez-workflows, theme packs.

The rule

If a feature can live behind a seam, it must. Core grows only when a new seam is needed — never a new feature.

On this page