Extend: Plugins, Config, Tools

Plugins extend fledge with community-built commands. They’re external executables distributed as GitHub repos, following the git-style subcommand pattern. fledge-<name> becomes fledge <name>.

Installing

All plugin commands live under fledge plugins.

# From GitHub
fledge plugins install someone/fledge-deploy

# Pin to a specific version (tag, branch, or commit)
fledge plugins install someone/fledge-deploy@v1.2.0

# Full URL works too
fledge plugins install https://github.com/someone/fledge-deploy.git

# Full URL with version pin
fledge plugins install https://github.com/someone/fledge-deploy.git@v1.2.0

# Reinstall (or upgrade a pinned plugin)
fledge plugins install someone/fledge-deploy@v2.0.0 --force

What happens when you install:

  1. Repo gets cloned to the platform plugin directory (e.g. ~/Library/Application Support/fledge/plugins/<name>/ on macOS, ~/.config/fledge/plugins/<name>/ on Linux)
  2. If @ref was specified, that tag/branch/commit is checked out
  3. fledge reads plugin.toml
  4. Build hook runs (or auto-detects Rust/Swift/Go/Node)
  5. Command binaries get symlinked to the plugins/bin/ directory
  6. Plugin is registered in plugins.toml (with pinned_ref if pinned)

Using Plugins

# Via fledge
fledge plugins run deploy --target production

# Or directly if the binary is on PATH
fledge deploy --target production

Managing

fledge plugins list              # what's installed
fledge plugins search deploy     # find plugins on GitHub
fledge plugins update            # update all unpinned plugins
fledge plugins update fledge-deploy  # update a specific plugin
fledge plugins remove fledge-deploy
fledge plugins list --json       # for scripting

Version Pinning

Use @ref to pin a plugin to a specific version:

fledge plugins install someone/fledge-deploy@v1.2.0

Pinned plugins behave differently on update:

  • Unpinned plugins get git pull and rebuild automatically
  • Pinned plugins check for newer tags and suggest an upgrade command, but don’t change automatically

To upgrade a pinned plugin:

fledge plugins install someone/fledge-deploy@v2.0.0 --force

Discovery

Plugins use the fledge-plugin topic on GitHub. To make yours findable:

  1. Add fledge-plugin as a topic on your repo
  2. Include a plugin.toml manifest
fledge plugins search            # browse all plugins
fledge plugins search deploy     # search by keyword

WASM Plugins

WASM plugins run in a sandboxed Wasmtime runtime with no host access by default. They’re ideal for pure-computation tasks (linting, formatting, analysis) where you want strong isolation.

Creating

fledge plugins create fledge-my-tool --wasm
cd fledge-my-tool

This scaffolds a Rust project targeting wasm32-wasip1 with plugin.toml, Cargo.toml, and src/main.rs.

Building

cargo build --target wasm32-wasip1 --release

The build hook in plugin.toml runs this automatically during install.

Capabilities

WASM plugins support two additional capability fields beyond exec/store/metadata:

FieldValuesDefaultDescription
filesystem"none", "project", "plugin""none""project" mounts project root read-only at /project. "plugin" adds read-write plugin dir at /plugin.
networktrue/falsefalseInherits host network stack when true
[plugin]
name = "fledge-analyze"
version = "0.1.0"
protocol = "fledge-v1"
runtime = "wasm"

[[commands]]
name = "analyze"
binary = "target/wasm32-wasip1/release/fledge-analyze.wasm"

[hooks]
build = "cargo build --target wasm32-wasip1 --release"

[capabilities]
filesystem = "project"
network = false

Limitations

  • Interactive UI (prompt/confirm/select) is not supported. Use non-interactive output
  • Compute is bounded (fuel limit + 60s wall-clock timeout)
  • Memory is limited to 256 MB

When to use WASM vs native

Use WASM when…Use native when…
Pure computation over project filesNeed to shell out to external tools
Untrusted/community pluginsNeed unrestricted filesystem access
Cross-platform single binaryNeed pipes, redirects, or shell features

Building a Plugin

1. Create the repo

The fastest way to start a plugin:

fledge plugins create fledge-deploy
cd fledge-deploy

For a WASM plugin, add --wasm:

fledge plugins create fledge-deploy --wasm
cd fledge-deploy

This scaffolds plugin.toml, a starter executable in bin/ (or src/main.rs for WASM), a README, and a .gitignore. Or create one manually:

mkdir fledge-deploy && cd fledge-deploy

2. Write plugin.toml

[plugin]
name = "fledge-deploy"
version = "0.1.0"
description = "Deploy to cloud providers"
author = "Your Name"

[capabilities]
exec = true
store = true

[[commands]]
name = "deploy"
description = "Deploy the project"
binary = "fledge-deploy"

[[commands]]
name = "rollback"
description = "Rollback to previous deployment"
binary = "fledge-rollback"

[hooks]
build = "cargo build --release"
post_install = "echo 'fledge-deploy installed'"
post_work_start = "scripts/setup-env.sh"

3. Add the executables

Each [[commands]] entry points to an executable in the repo. Can be compiled binaries, shell scripts, whatever:

#!/usr/bin/env bash
# fledge-deploy
echo "Deploying $(basename $(pwd))..."

Make them executable:

chmod +x fledge-deploy fledge-rollback

4. Validate

Check your plugin before publishing:

fledge plugins validate

This checks: plugin.toml is valid, name/version are set, binaries exist (or a build hook will create them), and commands are well-formed. Use --strict to fail on warnings, --json for machine-readable output.

5. Publish

Publish to GitHub (validates automatically before pushing):

fledge plugins publish

Or push manually and add the fledge-plugin topic. Users install with:

fledge plugins install yourname/fledge-deploy

plugin.toml Reference

[plugin]

FieldTypeRequiredDescription
namestringYesPlugin name
versionstringYesSemver
descriptionstringNoShort description (warned about if missing on validate)
authorstringNoWho made it
protocolstringNoSet to "fledge-v1" to opt into the structured plugin protocol. Without it, the plugin runs with inherited stdio.
runtimestringNoSet to "wasm" for sandboxed WASM plugins. Omit or set to "native" for standard executable plugins.

[[commands]]

Each entry registers a subcommand.

FieldTypeRequiredDescription
namestringYesCommand name (fledge plugins run <name>)
descriptionstringNoWhat it does
binarystringYesPath to executable (relative to plugin root)

[capabilities]

Capabilities declare what protocol features the plugin uses. All default to false. Plugins must opt in.

FieldTypeDefaultDescription
execboolfalseExecute shell commands on the host
storeboolfalsePersist key-value data between runs
metadataboolfalseRead project metadata (language, name, git info)
filesystemstring"none"WASM only. "project" for read-only project access, "plugin" for read-write plugin dir
networkboolfalseWASM only. Inherit host network stack
[capabilities]
exec = true
store = true
metadata = false

During installation, fledge displays the requested capabilities and the user must approve them. Granted capabilities are recorded in plugins.toml and enforced at runtime:

  • Blocked exec → returns exit code 126
  • Blocked store → silently dropped
  • Blocked metadata → returns empty object

Plugins without a [capabilities] section work fine but cannot use exec, store, or metadata protocol features.

[hooks]

Hooks fire in response to fledge lifecycle events. All fields are optional, plugins only participate in events they declare.

FieldTypeDescription
buildstringRuns after clone, before binary check
post_installstringRuns after fledge plugins install
post_removestringRuns before fledge plugins remove deletes files
pre_initstringRuns before fledge templates init starts
post_work_startstringRuns after fledge work start creates a branch
pre_pushstringRuns before fledge work push pushes to origin

Values can be a path to a script (relative to plugin root) or an inline shell command.

Using Plugins in Lanes

Plugin commands can be called from lane steps as inline commands:

[lanes.deploy]
description = "Test, build, and deploy"
steps = [
  "test",
  { run = "cargo build --release" },
  { run = "fledge deploy --target production" },
]

You can also run plugin commands in parallel with other tasks:

[lanes.ci]
steps = [
  { parallel = ["lint", "test"] },
  "build",
  { parallel = [{ run = "fledge deploy --target staging" }, { run = "fledge notify --channel ci" }] },
]

See Run: Tasks and Lanes for full step type documentation.

Plugin Protocol (fledge-v1)

Plugins that opt into the protocol (protocol = "fledge-v1" in [plugin]) communicate with fledge via newline-delimited JSON over stdin/stdout. When a plugin starts, fledge sends an init message with project context and granted capabilities, then the plugin sends outbound messages and fledge replies to anything that includes an id.

Message types at a glance

Outbound (plugin → fledge):

TypeReplyRequires
promptresponse with stringnone
confirmresponse with booleannone
selectresponse with selected stringnone
multi_selectresponse with array of stringsnone
execresponse with {code, stdout, stderr}exec
store(fire-and-forget)store
loadresponse with string or nullstore
metadataresponse with object of requested keysmetadata
progress(fire-and-forget)none
log(fire-and-forget; level: debug/info/warn/error)none
output(fire-and-forget; printed verbatim to stdout)none

Reply messages always have shape {"type": "response", "id": "<echoed>", "value": <type-specific>}. There is no exec_result / store_ack / load_result envelope. Every reply uses the generic response type.

See the plugin protocol spec for full schemas, lifecycle, security model, and worked examples.

Authentication

Plugin install, update, and search operations use your GitHub token when available. This enables installing plugins from private repositories. See Configuration: GitHub for the full token resolution order and required scopes.

The easiest setup is gh auth login. Fledge uses it automatically as a fallback. The token is injected via git’s http.extraheader mechanism and is never embedded in remote URLs or persisted to disk.

Security Model

Warning (native plugins): Native plugins run as unsandboxed processes with your full user permissions. A plugin can read any file you can read, write to any directory you can write to, and make network requests, regardless of its declared capabilities. Capabilities gate the fledge-v1 protocol (exec/store/metadata RPC messages), not the process itself. Review plugin source before installing, especially from unknown authors.

WASM plugins run in a sandboxed Wasmtime runtime with no host access by default. Filesystem and network access are opt-in, scoped, and prompted at install time. See WASM Plugins above.

Fledge has several safeguards:

  • Install confirmation: before cloning, fledge warns that plugins can execute arbitrary code and asks for confirmation. Pass --force to skip (CI/scripts).
  • Plugin name validation: repo names are checked for path traversal (.., /, \, leading .)
  • Command name validation: command names that become symlinks (fledge-<name>) are validated to reject /, \, . prefix, - prefix, and null bytes
  • Binary path traversal: plugin binaries cannot reference paths outside the plugin directory (both sides are canonicalized to defeat symlink bypass)
  • Hook execution: hooks run as direct processes, not via a shell. This prevents shell injection but means pipes, redirects, and shell expansions won’t work in hook commands. Use a wrapper script if you need shell features.

CI / Non-Interactive Usage

FlagWhereWhat it does
--forcefledge plugins installSkips the install confirmation prompt
--yesfledge templates initSkips the post-create hook confirmation prompt

Without these flags, interactive prompts will cause CI pipelines to hang.

File Locations

Plugin storage uses the platform config directory:

PlatformBase path
macOS~/Library/Application Support/fledge/
Linux~/.config/fledge/
Windows%APPDATA%\fledge\

Under that base:

PathWhat’s there
plugins/Installed plugin directories
plugins/bin/Symlinked binaries
plugins.tomlPlugin registry