Plugins
Every layer takes plugins the same way — a plain object, validated against one published schema, checked by the package's own command before it ships. One object can extend all nine.
Generated by npm run plugins:page from the packages themselves. Do not edit by hand.
Every package in the family takes plugins, and all 9 take them the same way. A plugin
is a plain object. Each package validates it against one published schema — the same
schema.json ships in every package — and each package has a check command that shows what
a plugin contributes, or refuses it with a code and the fix, before it ships.
Each package reads its own key and ignores the others, so one object can extend any subset of the family that is installed, including all of it.
One object, 9 layers
A company's CLI conventions, in one file: brand colours, a spinner, a terminal quirk, a terminal capability, a config source, where its tools live, what to flush on exit, a prompt of its own, and a command every one of its CLIs gets.
// One plugin object, nine layers. A company's CLI conventions in a single file: brand
// colours, a spinner, a terminal quirk, a config source, where its tools live, what to
// flush on exit, a prompt of its own, a terminal capability and a command every CLI
// gets. Each package reads its own key and ignores the rest, so the same object
// registers into any subset of the family that is installed.
//
// Check it against any layer: `npx roundel check examples/plugins/acme.mjs`, and the
// same for flagstaff, linegauge, paratext, seniority, bellpull, closeout, caique and
// `npx burgee check`. `scripts/plugin-example-lock.test.ts` runs all nine.
export default {
name: 'acme',
contract: 1,
// roundel — the brand, as semantic tokens every styled line reads.
tokens: { ok: '#1a7f37', error: '#cf222e', hint: '#0969da' },
// flagstaff — a spinner, with the line a pipe or an agent prints instead.
spinners: { acme: { frames: ['◐', '◓', '◑', '◒'], interval: 100, static: '◐' } },
// linegauge — the Nerd Font glyphs this company's prompt draws two columns wide.
widths: { powerline: { ranges: [[0xe0a0, 0xe0a3], [0xe0b0, 0xe0b3]], columns: 2, why: 'Nerd Font powerline glyphs render wide in the company terminal profile' } },
// paratext — a terminal capability, with what prints when the terminal lacks it.
capabilities: { beep: { name: 'beep', osc: 'BEL', when: { tty: true }, encode: '\u0007', fallback: '' } },
// seniority — a config layer between the flag (0) and the declared default (40).
sources: { vault: { rank: 25, read: () => undefined, location: 'vault://acme/cli' } },
// bellpull — where the company's pinned toolchain lives, searched before PATH.
resolvers: { asdf: { rank: -10, paths: ['{ASDF_DATA_DIR}/shims'], when: { envAny: ['ASDF_DATA_DIR'] } } },
// closeout — flush telemetry before the process exits, whatever ended it.
handlers: [{ name: 'flush-telemetry', phase: 'flush', run() {} }],
// caique — a prompt kind of the company's own, with its non-interactive projection.
widgets: { rating: { static: () => 'rate 1-5', sample: { running: {}, done: {} } } },
// burgee — a command every company CLI gets, declared exactly as a program's own.
commands: [{ path: ['doctor'], description: 'Check this machine against the company toolchain', options: {}, effects: 'read_only', run: () => ({ ok: true }) }],
};This is a committed file, not an illustration: scripts/plugin-example-lock.test.ts runs all
9 check commands against it, and the published schema over it, on every CI run.
What each layer takes
| Package | Key | What a plugin contributes | Check it | What the incumbent offers |
|---|---|---|---|---|
| bellpull | resolvers | bellpull resolvers by name: extra directories searched for an executable, before PATH (negative rank) or after it (positive). Two resolvers with one name shadow; two names both apply, in rank order. | npx bellpull check ./plugin.mjs | cross-spawn — None |
| burgee | commands | burgee commands this plugin contributes. Each is read by exactly the code a program's own command is, and a burgee plugin must declare contract. | npx burgee check ./plugin.mjs | commander — Lifecycle hooks on one program (.hook('preAction')); no plugin shareable across programs — the plugin RFC, #2505, is openyargs — Middleware on one program ( .middleware()), and commands loaded from modules (.commandDir())meow — None cac — Events on one program ( cli.on())citty — setup and cleanup on each command |
hooks | burgee lifecycle hooks. preRun opens around a command, and exactly one of postRun or onError closes. | |||
| caique | widgets | caique prompt widgets by kind. The six built-in kinds — text, confirm, select, multiselect, password, path — cannot be replaced: a plugin adds kinds of its own. | npx caique check ./plugin.mjs | clack — Custom prompts built on @clack/coreinquirer-core — Custom prompts built with createPrompt |
| closeout | handlers | closeout exit handlers. The phase decides the order they run in, not their position in this list. | npx closeout check ./plugin.mjs | restore-cursor — None exit-hook — None — it is the hook signal-exit — None — it is the hook |
| flagstaff | tokens | A roundel theme: semantic token name to a hex colour, contrast-checked when flown. | npx flagstaff check ./plugin.mjs | ora — A custom spinner object, per instance (spinner: { frames, interval })log-update — None boxen — A custom border object, per call ( borderStyle)cli-table3 — Custom border characters, per table ( chars) |
glyphs | Symbols by meaning: ok, fail, warn, info, running. A plugin that ships glyphs changes every built-in that draws one. | |||
spinners | Spinner styles by name, in cli-spinners' shape plus the static projection. | |||
borders | Border styles a box can be drawn with, by name. | |||
components | Components by name: static(state) returns the text a pipe, an agent or a screen reader gets; frame(t, state) is the optional animated form. | |||
| linegauge | widths | linegauge width overrides by name — the measurement section of a plugin, read the way flagstaff reads spinners. | npx linegauge check ./plugin.mjs | string-width — An option per call (ambiguousIsNarrow); no per-code-point overridestrip-ansi — None wrap-ansi — Options per call slice-ansi — None |
| paratext | capabilities | paratext capabilities by name — the OSC section of a plugin, read the way flagstaff reads spinners. | npx paratext check ./plugin.mjs | ansi-escapes — None terminal-link — A fallback function, per callterm-img — A fallback function, per call |
| roundel | tokens | A roundel theme: semantic token name to a hex colour, contrast-checked when flown. | npx roundel check ./plugin.mjs | chalk — None — styles are code; there is no theme to register |
| seniority | sources | seniority configuration sources by name, ranked against the built-in layers: a flag is 0 and a declared default is 40, and a plugin source sits strictly between. | npx seniority check ./plugin.mjs | rc — A custom parse function, per callcosmiconfig — Custom loaders per file extension and searchPlaces, per explorerlilconfig — Custom loaders per file extension, per calldotenv — None |
The schema for every key above ships as <package>/schema.json. An editor, a validator or a
model can read it without running anything.
What a plugin cannot do yet
- burgee's hooks cover the run of a command and nothing around it. A plugin can act at
preRun,postRun,onError— not before arguments are parsed, not inside help, not in config resolution, not at shutdown. Contributing commands has no such limit. - flagstaff's built-in components are not contributions. A plugin adds spinners, borders
and components of its own; it cannot replace
progress,boxortable. - The schema cannot say "function".
static,run,readandhandlerare required and described; that each is a function is checked by the package, not by the schema. - Whether a model writes a working plugin in one turn is unmeasured. Each package has an eval case that asks exactly that, proven to tell a working plugin from a broken one; none has run against a model yet.
burgee
An agent-native CLI framework, drop-in compatible with commander and yargs. One declaration; help, --json, --schema, --mcp and completions all projected from it.
The floor
The requirements every CLI on burgee meets, and who holds each — runtime, lint, or both. 74 of the floor's 114 are tabled here.