Skip to content

Plugins

The sf CLI is extensible through plugins. A plugin is an npm package that exports a cliPlugin object, which registers additional commands and aliases with the CLI.

How plugins work

When sf starts, the plugin loader:

  1. Discovers plugins from two sources:
    • Known first-party packages@stoneforge/smithy is auto-detected
    • User-configured packages — listed in config.yaml under plugins.packages
  2. Imports each package and looks for a named cliPlugin export
  3. Validates the plugin structure (name, version, commands array)
  4. Registers commands and aliases with the CLI

Resolution rules

  • Built-in commands always take precedence over plugin commands
  • Plugin commands can merge subcommands into existing command groups
  • Aliases cannot conflict with existing commands
  • If a package is not found, it’s silently skipped (no error)

Configuring plugins

Add third-party plugins in .stoneforge/config.yaml:

plugins:
packages:
- "@custom/my-plugin"
- "another-stoneforge-plugin"

Authoring a plugin

Each package must export a cliPlugin conforming to the CLIPlugin interface:

import type { CLIPlugin } from '@stoneforge/quarry/cli';
export const cliPlugin: CLIPlugin = {
name: 'my-plugin',
version: '1.0.0',
commands: [/* Command objects */],
aliases: {
'shortcut': 'my-command subcommand',
},
init: async () => { /* optional async setup */ },
};

Plugin structure

FieldTypeRequiredDescription
namestringYesUnique plugin identifier
versionstringYesSemver version string
commandsCommand[]YesArray of command definitions to register
aliasesRecord<string, string>NoMap of alias → target command
init() => Promise<void>NoAsync initialization hook, called once at startup

Command objects

Each command in the commands array registers a CLI subcommand:

{
name: 'my-command',
description: 'Does something useful',
options: [
{ flags: '--input <path>', description: 'Input file path' },
{ flags: '--dry-run', description: 'Preview without executing' },
],
action: async (options, quarry) => {
// options contains parsed flags
// quarry is the initialized Quarry instance
},
}

Commands receive the initialized Quarry instance, giving full access to the data layer — create elements, run queries, manage dependencies, and more.

Subcommand merging

Plugins can add subcommands to existing command groups. For example, a plugin could add new task operations:

export const cliPlugin: CLIPlugin = {
name: 'task-extras',
version: '1.0.0',
commands: [
{
name: 'task',
subcommands: [
{
name: 'estimate',
description: 'Estimate task complexity',
action: async (options, quarry) => { /* ... */ },
},
],
},
],
};

This adds sf task estimate without affecting any existing task commands.

The Smithy plugin

The @stoneforge/smithy package ships a built-in plugin named orchestrator that adds all agent orchestration commands to the CLI. It registers 6 command groups and 2 aliases:

Command groupDescription
sf agentRegister, start, stop, and inspect agents
sf daemonControl the background dispatch daemon
sf dispatchManually trigger task dispatch
sf mergeRun merge operations
sf poolManage agent pools
sf task (orchestration)Handoff, complete, merge, reject, and sync tasks
AliasMaps to
agentsagent list
poolspool list

These commands are documented in the Orchestration commands section of the CLI Reference.