Plugins
Preview docs describe unreleased preview builds. Stable docs remain at /docs/.
Nagi plugins are shareable, executable workflow packages. Manifest v2 plugins are WebAssembly components hosted by Nagi. Legacy plugins can be a Bash script, JavaScript app, Lua script, Rust binary, or any other argv command your machine can run. Nagi owns the host surface: installation, manifest validation, keybindings, terminal panes, events, invocation context, and socket access. The plugin owns its implementation language, dependencies, files, and durable state.
Plugins exist so Nagi can stay lean. The core stays focused on terminal workspaces, panes, agents, and a stable CLI/socket API. Plugins turn that existing extension surface into reusable workflows that people can build, install, and share without adding every workflow to Nagi itself.
A plugin is not an SDK integration. It is a directory with a
nagi-plugin.toml manifest and commands Nagi can launch. Nagi validates the
manifest, injects runtime context, starts the declared commands, and records
logs. The commands call back into Nagi through the CLI or socket when they need
to do more work.
For legacy native plugins, there is no separate restricted command set. The entire Nagi CLI
is the plugin API: every command in the CLI reference is
available to a plugin, and anything you can run as nagi ... yourself a plugin
can run too. Most plugins should call Nagi through NAGI_BIN_PATH, which
points at the running Nagi binary. That keeps plugins portable across Unix
sockets and Windows named pipes. Use the socket API when
you want to send raw JSON requests yourself.
Runtime action registration and native non-terminal plugin UI are not part of plugin v1. Actions, event hooks, panes, and link handlers are all declared in the manifest.
Trust and security
Section titled “Trust and security”Manifest v2 components run in Wasmtime using the WASI Component Model. Nagi
bounds component size, linear memory, tables, instances, output, fuel and wall
time. It does not inherit the host environment, preopen filesystem paths, or
inherit network access. A component can run without capabilities. Requested
capabilities require nagi plugin approve <id> before enablement and are bound
to the exact plugin version, manifest checksum and package checksum. A changed
package or manifest is blocked until reviewed again. nagi plugin revoke <id>
revokes the grant and disables the plugin.
Approval does not synthesize host APIs. Capabilities whose broker binding is not implemented remain unavailable to the component. This keeps new capability families fail-closed.
A native plugin is ordinary code that runs on your machine. Its build and runtime commands run as your user and can call the full Nagi CLI, just like an extension added to an editor, shell, or coding agent.
Install plugins from authors and repositories you trust, and skim what a new one
does first: the nagi-plugin.toml manifest and the scripts or binaries it runs.
nagi plugin install shows the source, revision, build commands, and the
unrestricted-access warning before asking for confirmation. In an interactive
terminal, accepting that prompt records native trust. Non-interactive installs
must pass both --yes and --trust-native. Local links use
nagi plugin link <path> --trust-native; without that flag they are registered
disabled and cannot be enabled. Relink an existing disabled plugin with the
flag to grant trust. Pin --ref when you want a specific revision.
Nagi validates the manifest, requires this explicit trust gate, scrubs inherited environment variables, and keeps each plugin’s config and state in its own directory. It does not sandbox a native plugin. Existing registry entries that predate the trust field migrate to disabled and untrusted.
Manifest v2: sandboxed component
Section titled “Manifest v2: sandboxed component”manifest_version = 2id = "example.review"name = "Review current mission"version = "1.0.0"min_nagi_version = "0.7.4"runtime = "wasi-component"entrypoint = "plugin.wasm"capabilities = []
[[contributions.commands]]id = "review"title = "Review current mission"contexts = ["mission"]Link a zero-capability component directly:
nagi plugin link ./example-reviewIf the manifest requests capabilities, Nagi links remote installs disabled. Review the source and declared capabilities, then run:
nagi plugin approve example.reviewnagi plugin enable example.reviewLegacy manifest: trusted native
Section titled “Legacy manifest: trusted native”The manifest is the contract between Nagi and the plugin. It declares package metadata, supported platforms, optional build commands, and the entrypoints Nagi can run.
id = "example.layout"name = "Layout"version = "0.1.0"min_nagi_version = "0.7.0"description = "Apply project layouts"platforms = ["linux", "macos", "windows"]
[[build]]command = ["npm", "ci"]
[[build]]command = ["npm", "run", "build"]platforms = ["linux", "macos"]
[[actions]]id = "apply"title = "Apply layout"contexts = ["workspace"]command = ["node", "dist/apply.js"]
[[events]]on = "worktree.created"command = ["nagi", "workspace", "list"]
[[panes]]id = "board"title = "Project board"placement = "overlay"command = ["nagi-board"]
[[link_handlers]]id = "github-issue"title = "Open GitHub issue"pattern = "^https://github\\.com/[^/]+/[^/]+/(issues|pull)/[0-9]+$"action = "apply"Top-level id, name, version, and min_nagi_version are required.
Set min_nagi_version to the oldest Nagi version that supports the plugin
APIs, event names, and manifest fields your plugin uses. Nagi refuses to link
or install a plugin when its minimum version is newer than the current binary.
description is optional. Plugin ids may use ASCII letters, digits, dot,
colon, underscore, and hyphen.
Action ids, pane ids, and link handler ids are local ids inside the plugin.
They may use ASCII letters, digits, colon, underscore, and hyphen, but not
dots. Each id type must be unique inside a plugin. Nagi qualifies action ids
as plugin.id.action when it needs a globally unique name.
Use platforms = ["linux", "macos", "windows"] to declare where the plugin
can run. Build commands, actions, event hooks, panes, and link handlers can
also declare their own platforms; item-level platforms override the top-level
list. Local plugins without top-level platforms link with a warning.
command values are argv arrays. Nagi does not run them through a shell, so
there is no shell expansion unless your command starts a shell itself. Put
language-specific behavior in your script or binary.
First plugin
Section titled “First plugin”Start with a directory that contains nagi-plugin.toml and one executable
script or program:
my-plugin/ nagi-plugin.toml index.jsid = "example.workspace-tools"name = "Workspace Tools"version = "0.1.0"min_nagi_version = "0.7.0"description = "Small workspace helpers"platforms = ["linux", "macos", "windows"]
[[actions]]id = "list-workspaces"title = "List workspaces"contexts = ["workspace"]command = ["node", "index.js"]Inside the command, call back into Nagi with NAGI_BIN_PATH:
const { spawnSync } = require("node:child_process");
const nagi = process.env.NAGI_BIN_PATH ?? "nagi";const result = spawnSync(nagi, ["workspace", "list"], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"],});
process.stdout.write(result.stdout);process.stderr.write(result.stderr);process.exit(result.status ?? 1);This example uses Node, but nothing about plugins requires Node. The manifest could launch Bash, PowerShell, Python, Rust, Go, Lua, Bun, or any other command available on the user’s machine.
Install and link
Section titled “Install and link”Install an example plugin:
nagi plugin install owner/nagi-plugin/agent-telegram-notifynagi plugin config-dir examples.agent-telegram-notifynagi plugin listnagi plugin action list --plugin examples.agent-telegram-notifyWhen you are authoring a local plugin, link the working directory instead:
nagi plugin link /path/to/pluginnagi plugin config-dir example.layoutnagi plugin action list --plugin example.layoutnagi plugin action invoke example.layout.applynagi plugin pane open --plugin example.layout --entrypoint boardnagi plugin log list --plugin example.layoutplugin install accepts GitHub shorthand only, such as
owner/repo/subdir. It clones with git, shows a preview in interactive
terminals, runs supported build commands, then stores the checkout under
Nagi-managed plugin data and registers it. Use --yes for noninteractive
installs. Reinstalling a GitHub-managed plugin replaces that managed checkout.
Installing over a locally linked plugin is refused; unlink or uninstall the
local plugin first. plugin install and plugin link create the plugin’s
config and state directories, and plugin config-dir <id> prints the config
directory for setup docs and shell scripts.
plugin uninstall <id-or-source> unregisters the plugin. For GitHub-managed
installs it also removes the managed checkout, and it accepts either the plugin
id or the same owner/repo[/subdir...] shorthand used by install.
plugin unlink <id> only unregisters a plugin and leaves files alone, which is
useful for local development. There is no separate plugin update in v1;
reinstall from GitHub to refresh a managed plugin.
The example cookbook repo is owner/nagi-plugin. It contains
separate example plugins in subdirectories, including agent-telegram-notify,
github-link-preview, and dev-layout-bootstrap. These are examples to copy,
not maintained official plugins.
Build commands
Section titled “Build commands”Build commands run during GitHub plugin install after confirmation and before
Nagi registers the plugin. If a build command fails, install aborts and the
plugin is not registered. plugin link does not run build commands; local
authors build their working tree themselves. Build commands may generate files,
but changing nagi-plugin.toml after the install preview aborts install. Build
failures show the plugin id, build index, working directory, command, exit
status or spawn error, and capped stdout/stderr without interpreting tool output.
Build commands are plain argv commands too, but they do not receive runtime
plugin context or Nagi socket env. Plugin authors should document required
system tools such as cargo, npm, bun, or lua; Nagi reports build
failures but does not install missing toolchains.
Commands and environment
Section titled “Commands and environment”Runtime commands run with the plugin directory as their working directory. Nagi
injects NAGI_SOCKET_PATH, NAGI_BIN_PATH, NAGI_ENV=1,
NAGI_PLUGIN_ID, NAGI_PLUGIN_ROOT, NAGI_PLUGIN_CONFIG_DIR,
NAGI_PLUGIN_STATE_DIR, NAGI_PLUGIN_CONTEXT_JSON, and any available
NAGI_WORKSPACE_ID, NAGI_TAB_ID, and NAGI_PANE_ID. Action commands also
receive NAGI_PLUGIN_ACTION_ID; event hooks receive NAGI_PLUGIN_EVENT and
NAGI_PLUGIN_EVENT_JSON; pane commands receive NAGI_PLUGIN_ENTRYPOINT_ID.
NAGI_PLUGIN_ROOT is the installed or linked plugin directory. Do not store
user credentials or durable state there, because GitHub-installed plugin roots
are managed source checkouts. Put user-editable config such as .env files
under NAGI_PLUGIN_CONFIG_DIR, and put local runtime state under
NAGI_PLUGIN_STATE_DIR. Nagi creates those directories and seeds
NAGI_PLUGIN_CONFIG_DIR from the legacy plugin config locations when present,
but it does not validate, sync, or delete their contents. The plugin owns the
file format and lifecycle.
NAGI_PLUGIN_CONTEXT_JSON can include workspace, tab, focused pane, worktree,
agent, selected text, clicked URL, and link handler fields when they are
available for that invocation. Shell plugins can read the individual env vars
for common ids, or parse the context JSON for the full shape.
Use NAGI_BIN_PATH when a plugin needs to call Nagi portably from Node,
PowerShell, Bash, or another runtime. The raw socket transport behind
NAGI_SOCKET_PATH is OS-specific: Unix clients connect to a Unix socket path,
while Windows clients connect to a named pipe. CLI calls through
NAGI_BIN_PATH avoid that transport difference. See the
CLI reference for available commands and
socket API for raw request shapes.
Manifest pane placement defaults to overlay, which opens a temporary zoomed
overlay over the active pane and restores the previous focus and zoom when it
closes. A plugin.pane.open request can override the manifest placement with
overlay, popup, split, tab, or zoomed.
placement = "popup" opens a session-modal terminal popup without changing the
tiled layout. It accepts optional width and height fields in the manifest or
open request; omit them for the default half-size popup, use numbers for outer
terminal-cell dimensions, or use strings like "80%" for a percentage of the
terminal area. It receives all terminal input, including Escape, and closes
when the command exits or a popup.close request is sent. Dimensions smaller
than the popup minimum are clamped.
Declare the placement directly on a plugin pane entrypoint when the pane should always be transient:
[[panes]]id = "picker"title = "Picker"platforms = ["linux", "macos"]placement = "popup"width = "80%"height = 20command = ["sh", "picker.sh"]Split, tab, zoomed, and overlay plugin panes are normal Nagi panes after they
open. Plugins can call standard pane APIs such as pane.move, pane.swap,
pane.resize, and pane.zoom through the socket or CLI; Nagi keeps plugin
pane ownership attached to the underlying pane when it moves across tabs or
workspaces. A popup is a singleton session resource rather than a Nagi pane:
it has no pane ID, does not change plugin focus context, emits no pane lifecycle
events, and does not participate in pane, layout, persistence, or agent APIs.
Its process does not receive NAGI_PANE_ID; the underlying tiled pane remains
available through NAGI_PLUGIN_CONTEXT_JSON.
Opening a popup returns ui_busy while Settings, Copy mode, or another Nagi
modal is active, and plugin.pane.open returns an ok result after launch.
On Windows, build commands, action commands, and event commands resolve common
PATHEXT shims such as npm.cmd, bun.cmd, and pnpm.cmd when the bare
command is on PATH. Pane commands use Nagi’s normal Windows pane launcher and
must still be valid Windows argv commands.
Keybindings
Section titled “Keybindings”Bind a key to an installed plugin action:
[[keys.command]]key = "prefix+l"type = "plugin_action"command = "example.layout.apply"description = "apply layout"Link handlers
Section titled “Link handlers”Use [[link_handlers]] to route modified clicks on matching terminal URLs to a
plugin action instead of opening the URL in the browser. The modified-click
modifier is Control on every platform, including macOS, because captured
terminal mouse reports do not expose Command/Super separately from a plain
click. pattern is a Rust regular expression matched against the clicked URL,
and action must
name an action declared by the same plugin. Link handler actions receive
invocation_source = "link_click", clicked_url, and link_handler_id in
NAGI_PLUGIN_CONTEXT_JSON; shell plugins can also read
NAGI_PLUGIN_CLICKED_URL and NAGI_PLUGIN_LINK_HANDLER_ID. Handlers are
checked in manifest order inside each plugin.
Storage
Section titled “Storage”There is no Nagi-managed plugin storage API in v1. Plugins that need durable state should own their files or database.
Marketplace
Section titled “Marketplace”The public registry is planned and does not list plugins yet. Direct GitHub
installation and local linking work now, so authors can publish a repository
with nagi-plugin.toml and share nagi plugin install owner/repo[/subdir].
Adding the nagi-plugin topic does not create a listing today. See
Marketplace for the current boundary and launch work.