CLI
Commands
Shared option groups
Source commands (build, prepack, validate, eval):
Artifact commands (mcp list, mcp invoke, mcp run, serve-app, hooks list, hooks simulate):
the same --root, --config, --mode, and --json, plus --artifact <path> to use exactly
one built artifact and --target <target> to select the host projection. For mcp list,
mcp invoke, mcp run, and serve-app, --target is optional: when omitted, the artifact's
agent-bundle.manifest.json projections[] supplies the one MCP-capable host that runs the
server, and a root whose projections leave the choice ambiguous fails with an error that names the
candidates. hooks simulate still requires it, because a hook wrapper is compiled per host.
serve-app takes no --json.
dev
dev runs in the foreground and closes the session on a termination signal. See
Developer Workbench.
dev proxy
The proxy inherits --root from the parent dev command and writes diagnostics to stderr.
serve-app
Serves one built MCP App in a plain browser tab, outside any MCP host and without the
Workbench: the command launches the plugin's packed MCP server exactly as mcp run does, binds
the App to it through the same host stack the Workbench App workspace uses (sandbox proxy, consent
authority, bridge), calls the App's tool once so it opens populated, and prints the loopback URL.
Its output is a contract: once the host is listening, stdout carries exactly one line,
MCP App <app> at <url> (tool <tool>; Ctrl-C stops the server), <app> as given on the command
line, <tool> the tool that opened the App, and every diagnostic goes to stderr. The command
then runs in the foreground: SIGINT or SIGTERM closes the host and its server and exits 0;
when the bound MCP server exits on its own, one AB5000 diagnostic is written to stderr and the
command exits 1. The same ready-line contract is printed by <plugin> web.
The host binds to 127.0.0.1 only, serves one document and the authenticated /api/mcp/...
routes (a per-launch token plus same-origin checks; AB8003 / AB8004 on refusal), and exposes
only the selected server through the bridge. The App document itself runs on a second loopback
origin inside the framework's sandbox. It is a local preview host, not a deployment target. The
programmatic form for scripts and tests is serveApp in agent-bundle/api. From an installed
artifact the supported command is <plugin> web, which runs the same host stack
from bin/<plugin>.mjs without the framework on the machine, see
Exposing an App in the browser.
<plugin> web
The framework-owned web command on the composite artifact's bin/<plugin>.mjs. It is emitted
when the project configures web, even if src/cli/** compiled no
commands, and is listed in that bin's --help. Run it from the built artifact or the installed
plugin root, agent-bundle.manifest.json must sit beside bin/ (exit 1 otherwise).
The command launches the plugin's own MCP server (node <root>/mcp/<entry>.mjs,
AGENT_BUNDLE_PLUGIN_ROOT=<root>, declared static env with path tokens expanded, the
plugin-data token resolves outside the artifact, under
~/.agent-bundle/web-data/<plugin>-<digest>/<server>, so a read-only install still launches).
The spawned server's own durable framework state likewise lives outside the artifact under
~/.agent-bundle/state/<plugin>-<digest> (or AGENT_BUNDLE_STATE_ROOT). It calls the opening
tool once and serves the App at http://127.0.0.1:<port>/ on a loopback origin with a
second loopback sandbox origin for the App document, the same host stack and consent behavior as
agent-bundle serve-app and the Workbench. Human mode prints
MCP App <server>/<app> at <url> (tool <tool>; Ctrl-C stops the server) and runs until Ctrl-C /
SIGTERM. An authored src/cli/web.ts (or any generated-mode command or top-level alias spelled
web) is refused as AB4341 when web is configured.
build and prepack
Both take the source option group plus:
Both build package outputs when the project declares bin or lib. The layout of the root is
in Targets and artifacts.
Generated wrappers import project modules by project-relative specifiers, so when every compiled
module lies inside the project root (its own node_modules included), one source built from two
checkout paths emits byte-identical files and an identical manifest. A module resolved outside the
root keeps a path relative to it.
build alone also takes:
The JSON document carries the reports under hostValidation, one per validated projection, each
with a load verdict (loaded, refused, unregistered, or failed) when the load check ran.
Programmatic build() calls never run host validation unless hostValidation: true is passed.
If project source changes while compilation is running, build and build() reject the result
with AB7101 after compilation and validation, before the staged tree replaces live artifact or
package output. Compiler metadata and compiled bytes from different snapshots are never published;
any previous artifact or package output stays in place. Retry the build; the racing change is
already on disk.
install
The emitted standalone install.mjs accepts the same --replace. Cursor copies carry an install
receipt (.agent-bundle-install.json), replacement
touches owned files only, and a directory without a receipt naming this plugin (a copy placed
before receipts existed included) is foreign, refused with or without --replace; Claude replacement runs
claude plugin uninstall --keep-data before reinstalling. Codex replacement is add-only
(codex plugin add) so plugin settings and nested MCP overrides survive. Disabled or
unknown-enablement Codex replacements are refused (AB7004) because the native plugin CLI
has no qualified settings-preserving update API. Every install writes a lifecycle
receipt (format agent-bundle-install-receipt/2:
version, content hash, mode, scope, owned paths, host registrations, timestamps), in-tree for
Cursor and Amp local copies, under <host root>/agent-bundle/receipts/ for Claude, Codex, and Cursor
marketplace mode, that uninstall and doctor consume.
During install, an explicit state root that does not yet exist is created and receives
.agent-bundle-state-owner.json, which records the installation identity. Parent directories may
be created to reach it but are never claimed or recursively removed.
uninstall
Uninstall removes exactly what the receipt owns: the recorded files and installer-created
directories (Cursor local, including the ~/.cursor/plugins[/local] directories the install
created; Amp project/system, inside only .amp/plugins/<name>), the staged repository (Cursor marketplace, after its HEAD matches the recorded
commit), or the host registrations (claude plugin uninstall <id> --scope <scope> --keep-data +
claude plugin marketplace remove, codex plugin remove + codex plugin marketplace remove,
the marketplace is retained while another installed plugin still uses it, including a Claude
install at another scope or in another project known only to Claude's plugins/installed_plugins.json
registry). Unowned entries are retained and listed, a state/ directory beside the plugin
included; a missing store receipt is AB7009 and a mismatch is AB7007 unless --force; a Cursor
local directory without a receipt is foreign (AB7007) even with --force; a
second run is a not-installed no-op. The typed data.outcome says what happened to durable
state per host: Cursor kept / purged / absent; Claude retained-by-host (the cached copy is
orphaned for Claude's ~14-day grace period; a purge removes the owned framework state roots,
derived web-data, and plugins/data/<id>/); Codex keeps external state by default
and removes it on a confirmed purge while codex plugin remove deletes the cached tree. The
emitted install.mjs accepts --uninstall with
--mode, --keep-data, --purge-data --confirm-purge, --force, and --plan.
State receipts keep three facts separate. The installed MCP documents determine each server's
runtime location (including relative overrides resolved from that server's execution directory);
the receipt records the locations observed for this installation; and only independent ownership
evidence authorizes deletion. The default
~/.agent-bundle/state/<plugin>-<digest> (or
$XDG_STATE_HOME/agent-bundle/<plugin>-<digest>) namespace is owned by construction. An explicit
AGENT_BUNDLE_STATE_ROOT is owned only when installation created the previously absent directory
and wrote its install-identity marker. A pre-existing directory is never recursively removed merely
because a server or the current uninstall environment names it. Multiple servers and roots are
recorded and judged independently. A receipt with no state block cannot make the current
environment or home a source of purge authority: Doctor and uninstall report the observed root as
unrecorded / unproven and retain it, including after --keep-data. Only
agent-bundle-install-receipt/2 is read; a copy carrying an older receipt is foreign.
doctor
Doctor exits 1 when it reports any error diagnostic, and never modifies anything. With --from,
it reports the installed copy per host as current, stale (same version, different content,
AB7308), version-mismatch (AB7309), foreign (AB7321), load-failed (AB7325, Claude Code lists
the copy with errors in claude plugin list --json and refused to load it; the message carries the host's
text verbatim), or not-installed (AB7307). For Claude it also reads each row's enabled flag: a
copy listed with enabled: false is disabled in the inventory and carries enabled: false next to its
content verdict, with an AB7327 warning naming the claude plugin enable command; and it runs the
Claude Code developer validator (claude plugin validate --strict against plugin.json, then
marketplace.json) over the --from bundle and every installed copy, reporting findings as
AB6020–AB6022 prefixed with Bundle at … or Installed copy at …. For
Cursor it also proves plugin-scoped hook registration from the installed manifest (AB7322), warns
when ~/.cursor/hooks.json would deliver a plugin hook a second time (AB7323), and tracks a
staged marketplace from unregistered to registered once Cursor imports it (AB7324). For an
Agent Plugins install it proves the emitted installer's placeholder expansion, expanded,
unexpanded (spec forms Cursor 3.18.25 cannot launch), or drifted (AB7326). With
--from it also reports the lifecycle stage per host, placed → registered → enabled → active,
each observed (Claude and Codex plugin list --json rows and enabled flags, the Cursor local
directory, the Cursor marketplace import cache) or typed unavailable with the reason no read-only
surface exposes it (AB7330). It inventories the Agent Bundle receipt store under each host root
and warns about receipts the host no longer honours (AB7328). A Cursor directory
uninstall --keep-data left behind with its remnant receipt is reported missing with an
AB7307 info, not corrupt or foreign; without that receipt, a directory holding only state/ is
corrupt (AB7304) and foreign (AB7321).
For every installed copy Doctor reports every per-server framework state root, its native or
derived source, receipt ownership (derived, marker, unowned, or unrecorded), whether its
evidence is currently purgeable, the servers using it, whether it exists, and whether it is writable.
A state/ directory beside the plugin is an ordinary unowned entry, not durable state.
validate
eval
eval compare <baseline> <candidate> takes two persisted run ids and inherits the source options
from eval.
inspect
inspect takes --root, --config, --mode, --json, and a single --target <target> filter
(not repeatable), plus at most one focus flag. --artifact <path> inspects a copied built
composite root through its agent-bundle.manifest.json instead of a project source tree; it is
mutually exclusive with --root and --config (a usage error). A missing or non-canonical
manifest fails with AB7001.
Passing more than one focus is an error. inspect exits 1 when the normalized model is invalid.
With --json, the document gains an output.manifest summary, the built projections[] and
executables, whenever a built agent-bundle.manifest.json exists at the artifact output, so a
JSON consumer sees what the last build emitted without probing the root itself. The source-side
model and plan view is unchanged.
inspect --artifact <path> does not load project config. --json prints { manifest, application }:
the same InspectManifestSummary as output.manifest, and the same application projection the
Workbench renders (applicationExplorerFor), including distribution.payloads[], each prebuilt
payload's name, hosts, and runtimeDependencies (the row artifact-only consumers read for
packages that must exist at consumer runtime; see definePrebuilt).
Without --json, the human output is the application id/name/version, projections with
builtInHost, servers with tool counts, hooks by host, bins, scripts, and payload rows.
Every inspection plan reports each component with its canonical kind, agent, cli, command,
event-route, hook, lsp, mcp-app, mcp-server, native-diagnostics, native-extension,
rule, script, skill (AgentComponentKind from agent-bundle/api), as selected or
skipped with its cause, plus a kinds matrix: one entry per kind with the target's own four-state
capability judgment and selected/omitted counts. The human output prints one line per target,
each omission and its reason, then kinds this host cannot emit:. Filesystem src/events routes
report as event-route, judged by the host's event:<canonical event> row, not as hook.
mcp
mcp list requires --server <server>. mcp invoke requires --server and --tool <tool>, and
takes exactly one of --input <json> or --input-file <path>; the parsed value must be a JSON
object, not an array or scalar.
All three resolve the server from the artifact's agent-bundle.manifest.json: the compiled entry
(and its Flight worker) from executables.mcpServers[].entry, the Apps from
executables.mcpServers[].apps[], and, when --target is omitted, the host from the one
MCP-capable projections[] row; an ambiguous selection is an error that names the candidates.
mcp run requires --server and runs one stdio server in the foreground with inherited stdio:
Because the server owns the JSON-RPC channel for the whole foreground run, mcp run writes
nothing of its own to stdout. See Runtime environment for the
.env precedence rules and what the plugin-root anchor changes.
hooks
hooks list reports the hooks in an artifact, the executables.hooks[] rows of its
agent-bundle.manifest.json. hooks simulate requires --hook <hook> (an id or a name) and the
same --input / --input-file pair as mcp invoke.
Exit codes
An eval run exits 1 when any trial fails or is inconclusive, an inconclusive trial produced
no evidence, so it cannot report success either.