Skip to content
GitHub

`agentbundle` — reference

Look up the supported install, inspection, preview, configuration, source-resolution, networking, and failure behavior of `agentbundle`.

python -m pip install agentbundle

When the wheel is hosted in an internal PyPI repository, Pip, uv, Poetry, or the selected package client owns authentication. Its .netrc, keyring, or login state is not reused when AgentBundle later reads a protected catalogue.

To install from a clone instead — for repo contributors or for users on an offline / corporate network — see ../how-to/install-agentbundle.md#install-from-a-clone.

After install, every subcommand is reachable via agentbundle <verb> or python -m agentbundle <verb>. agentbundle --help lists the full set.

agentbundle install <catalogue-uri> --pack <pack-name>

<catalogue-uri> is a local path to a checked-out pack catalogue, or a git+https://… URL. --pack selects which pack from the catalogue to install.

Common flags:

FlagEffect
--scope {repo,user}Target install scope; default is the pack’s default-scope.
--adapter <name>Override the resolved adapter per-invocation (e.g. claude-code, codex, kiro).
--output <dir>Output root; default depends on scope.
--dry-runPreview the per-file plan (action + tier + target path) to stdout and exit 0 without writing anything. Refused with --force. See Preview before applying.

Run agentbundle install --help for the complete set.

agentbundle list-installed reads your state files (not a catalogue) and reports every installed (pack, adapter) row across both scopes, with its version and whether an upgrade is available:

agentbundle list-installed
PACK ADAPTER SCOPE INSTALLED LATEST STATUS
architect claude-code user 0.9.0 0.10.0 upgrade-available
architect codex user 0.9.0 0.10.0 upgrade-available
core claude-code repo 0.5.0 0.5.0 up-to-date

The STATUS is computed against the resolved catalogue: up-to-date, upgrade-available, or unknown (when the catalogue can’t be resolved, or doesn’t carry that pack). When the catalogue is unreachable the command still lists every row — LATEST shows — and STATUS is unknown — and exits 0; it never fails just because it couldn’t reach a catalogue.

FlagEffect
--scope {repo,user}Limit the listing to one scope; default lists both.
--no-check / --offlineSkip the catalogue check entirely (no network): print only PACK ADAPTER SCOPE INSTALLED.
--check-driftAdd a DRIFT column counting installed files edited locally since install (on-disk SHA differs from the recorded one).

Both agentbundle install and agentbundle upgrade accept --dry-run: it runs the full read-only pre-flight, prints a per-file plan to stdout (one <action> <tier> <target> line each — create / overwrite / companion, with Tier-2 lines naming the .upstream.<ext> companion), and exits 0 without writing anything. Diagnostics and pre-flight failures go to stderr.

agentbundle install <catalogue-uri> --pack core --dry-run
agentbundle upgrade <catalogue-uri> --pack core --dry-run

install --dry-run --force is refused — --force’s destructive cleanup is incompatible with a read-only preview. A preview lists projected files, not governance seeds such as AGENTS.md; a real install still creates them. It exits 0 even with companions; agentbundle diff is the verb that gates on drift.

Profiles. agentbundle list-profiles shows them; agentbundle install --profile <name> installs one. --profile cannot be combined with --pack or --scope (the profile sets its scope). A pack already installed at the other scope blocks the profile. Every pack’s preconditions are checked before anything is written; a failure mid-batch is not rolled back, so run the command again to resume. There is no upgrade --profile or uninstall --profile.

User scope on Codex and Kiro. For Codex, agentbundle looks for ~/.codex/ or ~/.agents/skills/ and writes skills to ~/.agents/skills/. For Kiro, it looks for ~/.kiro/ (run any Kiro command once to create it) and writes to ~/.kiro/skills/. With several agent folders present it takes the first match in the pack’s allowed-adapters order. User-scope state is ~/.agentbundle/state.toml; credentials go to ~/.agentbundle/credentials.env. Codex’s own ~/.codex/plugins/ route keeps separate state, so use one route per pack.

Changing adapter. An upgrade keeps the adapter recorded at install. To change it, agentbundle uninstall --pack <name> --scope <scope>, then install again with --adapter. --adapter must be in the pack’s allowed-adapters; kiro is a deprecated alias for kiro-ide.

Selective upgrade. agentbundle upgrade --pack <name> --skill <skill-name> moves one skill and leaves the rest at the installed version; --agent, --hook, --seed <path>, and --command work the same way. The next whole-pack upgrade reports the mixed state. When a pack is installed for more than one adapter at a scope, pass --adapter.

Uninstall. agentbundle uninstall --pack <name> removes the files the pack owns. Files you edited and files outside the pack’s paths stay.

After the install. Installing core at repo scope ends with a Next: line asking you to run adapt-to-project. Repo scope writes seeds when the pack declares them, writes .adapt-install-marker.toml, and chains agentbundle adapt. Local scope deliberately omits seeds. No marker, layout section, or chained adapt. Plugin and APM installs print no Next: line. A lifecycle hook may repeat that nudge. It is never the only path: the installer guarantees the projected files, the state for the scope you chose, and the printed line. It does not guarantee hook execution or context injection. A SessionStart hook derived into each published pack’s .claude-plugin/plugin.json can run the marker writer.

Other routes. APM and Claude plugin installs follow their own update verbs (apm update <pack>, /plugin update <pack>@<catalogue>) and print no Next: line; run agentbundle init-state once so agentbundle has a baseline. Catalogue maintainers can emit the APM and plugin dist trees with agentbundle install --pack <name> --scope repo --emit-install-routes .; the plugin tree is written only for packs whose allowed-scopes admits user, and the flag cannot be combined with --adapter.

Once installed, agentbundle resolves the target adapter on every install via a fixed cascade:

  1. The --adapter flag, if passed.
  2. The state-hint from a prior install of the same pack (so an upgrade stays on the adapter it was originally installed under).
  3. The user-config file, if you’ve set one. Then your organization’s packaged default, [organization].preferred_adapter, when one is set.
  4. At user scope only: an on-disk IDE probe — if you have ~/.claude/, ~/.codex/ (or ~/.agents/skills/), or ~/.kiro/ populated, the first match in the pack’s allowed-adapters order is picked. ~/.kiro/ cannot tell Kiro IDE from Kiro CLI, and Cursor and Gemini CLI have no probe. At repo scope there is no probe: without a flag, state, or config the install targets claude-code when the pack allows it, otherwise the pack’s first allowed adapter.
  5. The built-in default (scope.DEFAULT_ADAPTER, currently claude-code).

agentbundle config reads and writes layer 3. Four actions:

agentbundle config path # where the file lives
agentbundle config get [<key>] # show effective value + provenance
agentbundle config set <key> <value> # validate and write
agentbundle config unset <key> # remove (deletes file if empty)

The registered keys are adapter and source. source overrides every organization default, so use it only when a user needs a different catalogue. Future keys would be added by the framework; the commands stay the same.

$ agentbundle config path
/Users/username/Library/Application Support/agentbundle/config.toml
$ agentbundle config get adapter
adapter claude-code (builtin)
$ agentbundle config set adapter codex
$ agentbundle config get adapter
adapter codex (file)
$ agentbundle install ./my-catalogue --pack demo
# resolves to codex unless overridden by --adapter or a state-hint.
$ agentbundle config unset adapter
$ agentbundle config get adapter
adapter claude-code (builtin)
PlatformPath
macOS~/Library/Application Support/agentbundle/config.toml
Linux${XDG_CONFIG_HOME:-~/.config}/agentbundle/config.toml
Windows%APPDATA%\agentbundle\config.toml

The file is plain TOML with a single [settings] table:

[settings]
adapter = "codex"

You can hand-edit it; agentbundle config is a convenience over the file, not a gate.

If you have adapter = "<name>" configured and either:

  • <name> is not supported at the install scope (e.g. copilot configured but installing at user scope — Copilot is repo-only), or
  • the pack’s [pack.install] allowed-adapters doesn’t include <name>,

then agentbundle install refuses with a message naming the conflict and listing the escape hatches (--scope, --adapter, agentbundle config set, agentbundle config unset). The configured value is preserved — the install just doesn’t proceed under a value you didn’t pick.

Upgrades preserve their existing adapter regardless of user-config. If you installed a pack under claude-code, then ran agentbundle config set adapter codex, then upgraded that pack, the upgrade stays on claude-code — agentbundle config shapes fresh installs, not relayouts of existing ones.

agentbundle upgrade takes no version — the target is whatever the catalogue you point at declares (to pin a past version, point the catalogue at that git ref). It tells you honestly what it did:

  • A real version change reports upgraded: <pack> @ <scope> <from> -> <to>.
  • Re-running against the version you already have is a re-apply, not an upgrade: it reports re-applied: <pack> @ <scope> <version> (already current) — or names the count of locally edited files it kept as .upstream companions, when there were edits. Before it acts, it tells you up front how many of your edits will be preserved.
  • A pack installed for more than one adapter at a scope needs --adapter to disambiguate; the refusal lists each adapter with its installed version, e.g. pass --adapter to pick one: claude-code (0.9.0), codex (0.9.0). The same applies to diff and uninstall.

Every source verb — install, upgrade, list-packs, list-profiles, list-installed — takes an optional trailing catalogue argument. When you omit it, the CLI resolves one through a five-layer, first-match-wins chain:

LayerSourceSet by
1Explicit catalogue argumentagentbundle install --pack <name> <catalogue> — passed through verbatim, no validation
2User config [settings].sourceagentbundle config set source <catalogue>
3Org Artifactory bootstrap[distribution.agentbundle.artifactory] in catalogue.toml, baked into the wheel by agentbundle catalogue sync-defaults --write
4Editable-install detectionpip install -e <clone> — auto-detected via PEP 610 direct_url.json; walks up to the enclosing .git root
5Packaged default_data/install-defaults.toml [defaults].source — baked into the wheel at publish time

Setting AGENTBUNDLE_NO_REMOTE=1 skips Layers 3 and 4 (the org Artifactory bootstrap and editable-install detection), falling through directly to Layer 5. See AGENTBUNDLE_NO_REMOTE below.

VariableDefaultPurpose
AGENTBUNDLE_HTTP_BEARER_TOKENunsetBearer token sent as Authorization: Bearer <token> on catalogue+https:// and archive+https:// requests. Checked first in the four-provider chain. Secret: inject through an organization-managed launcher or process environment. Do not log it or persist it to version control. An empty value counts as unset; the chain moves on to the next provider.
JFROG_CLI_SERVER_IDunsetName of the JFrog CLI profile to use when multiple profiles match the catalogue origin by prefix. When unset, the profile whose artifactoryUrl has the longest matching prefix is used. Setting this variable makes the JFrog CLI provider required: if jf cannot run, resolution fails with jfrog_cli_not_executable; if the named profile is missing or does not match the catalogue URL, it fails with jfrog_profile_mismatch.
AGENTBUNDLE_CA_BUNDLEunsetAbsolute path to a PEM CA bundle for TLS verification, honoured on every catalogue source form. Raises CatalogueError if the path does not exist — including on git+https://, where the variable was previously ignored. Semantics differ by source form: on git+https:// the bundle is added to the default trust store; on catalogue+https:// and archive+https:// it replaces it, which pins verification to your own authority. Does not reach the jf api subprocess on the JFrog CLI path — use SSL_CERT_FILE / SSL_CERT_DIR on Linux, or the system keychain on macOS, for that — see Protected catalogue authentication. Example: AGENTBUNDLE_CA_BUNDLE=/etc/ssl/corp-ca.pem agentbundle install --pack core
SSL_CERT_FILE, SSL_CERT_DIRunsetStandard OpenSSL-family trust-store paths. Honoured on git+https:// sources and passed through to the jf api subprocess on Linux for the JFrog CLI path. Direct HTTPS fetches (bearer, .netrc, anonymous) also honor these variables through Python’s default trust store while AGENTBUNDLE_CA_BUNDLE is unset; a set AGENTBUNDLE_CA_BUNDLE replaces that store for direct fetches. A stale value is not recoverable: a bad path leaves the trust store empty and fetches fail verification. Unset them rather than pointing them at a missing file.
REQUESTS_CA_BUNDLEunsetStandard path, honoured on git+https:// sources only. Anchors are added to the default store, never substituted for it. A stale value is ignored harmlessly.
AGENTBUNDLE_NO_SYSTEM_TRUSTunsetWhen set to any non-empty value, disables the operating-system trust fallback described in Corporate networks below. The underlying verification error is still reported, with the troubleshooting guidance appended.
AGENTBUNDLE_NO_REMOTEunsetWhen set to any non-empty value, skips Layer 3 (org Artifactory bootstrap) and Layer 4 (editable-install detection), falling through to Layer 5. Use on hosts that cannot reach Artifactory, or in CI pipelines that resolve a local catalogue. Example: AGENTBUNDLE_NO_REMOTE=1 agentbundle install --pack core /path/to/local-catalogue
HTTPS_PROXYunsetProxy URL for outbound HTTPS requests. Read automatically by Python’s urllib.request.ProxyHandler; no agentbundle-specific wiring needed. Example: HTTPS_PROXY=http://proxy.example.com:3128 agentbundle install --pack core
NO_PROXYunsetComma-separated list of hostnames that bypass the HTTPS proxy. Read automatically by Python’s urllib.request.ProxyHandler. Example: NO_PROXY=internal.example.com,localhost

AgentBundle resolves catalogue HTTPS credentials through a four-provider chain, evaluated in this order. The chain stops at the first available provider. When a provider is configured but broken, the resolution fails immediately — no fallback occurs.

PriorityProviderAvailable whenBreaks when
1Bearer tokenAGENTBUNDLE_HTTP_BEARER_TOKEN is set and non-emptyThe value contains whitespace, a control character, or a non-ASCII character
2JFrog CLIjf (2.105.0+) is on PATH and a profile’s Artifactory URL is a path-segment prefix of the catalogue URL on the same originDiscovery fails, two profiles tie, the topology is unsupported, jf is older than 2.105.0, or JFROG_CLI_SERVER_ID names a profile that is missing or does not match
3.netrc~/.netrc (POSIX) or %USERPROFILE%\.netrc (Windows) has a machine key exactly matching host:port or hostThe file is unreadable, has unsafe permissions, is malformed, or the matching record lacks a login or password
4AnonymousAlways available—

The JFrog CLI provider delegates fetches to jf api --server-id=<id>, which uses the stored profile token. AGENTBUNDLE_CA_BUNDLE does not reach the jf api subprocess. On Linux, use SSL_CERT_FILE or SSL_CERT_DIR to trust a private CA for jf api. On macOS, add the corporate CA to the system keychain instead — jf api reads only the system keychain there. jf api ignores ~/.jfrog/security/certs/, so a passing jf rt ping does not prove the catalogue fetch will succeed.

Failure codes reported by AgentBundle (no credential material appears in any error message):

Resolution codes stop the install before any catalogue request:

CodeProviderMeaning
invalid_target_hosttargetThe catalogue URL is not HTTPS, carries user information, or its host cannot be encoded
invalid_bearerbearerAGENTBUNDLE_HTTP_BEARER_TOKEN contains whitespace, a control character, or a non-ASCII character
jfrog_cli_not_executablejfrogJFROG_CLI_SERVER_ID is set but jf is missing, sits in the working directory or a relative PATH entry, or is a .cmd/.bat shim
jfrog_discovery_failed, jfrog_discovery_timeout, jfrog_discovery_too_large, jfrog_discovery_malformedjfrogjf config show --format=json failed, took over 10 seconds, printed too much, or printed unusable JSON
jfrog_profile_ambiguousjfrogTwo profiles match equally well and JFROG_CLI_SERVER_ID does not choose one
jfrog_profile_mismatchjfrogJFROG_CLI_SERVER_ID names a profile that is missing or does not match the catalogue URL
unsupported_jfrog_topologyjfrogThe matching profile’s Artifactory URL is not under its platform URL on the same origin
incompatible_jfrog_clijfrogjf --version reports a version below 2.105.0
jfrog_probe_failed, jfrog_probe_timeout, jfrog_probe_too_largejfrogjf --version failed, took over 5 seconds, or printed too much
netrc_unreadablenetrc.netrc exists but is not a readable regular file, or is over 1 MiB
netrc_unsafenetrc.netrc has group or other permission bits set, or another user owns it
netrc_malformednetrc.netrc cannot be parsed, has an unencodable or ambiguous machine key, or a login with : or a control character
netrc_incompletenetrcThe matching record lacks a login or a password

Fetch codes end the install after a provider was chosen; none of them tries another provider:

CodeMeaning
redirect_not_permittedThe server redirected to another origin or to plain HTTP
descriptor_too_large, archive_too_largeThe descriptor passed 1 MiB or the archive passed 256 MiB
endpoint_not_permittedA JFrog-path URL left the profile’s Artifactory base or carried an unsafe path
jfrog_fetch_failed, jfrog_fetch_timeout, jfrog_fetch_stderr_too_largejf api returned a non-2xx status or failed, took over 30 seconds, or wrote too much error output

Installing the AgentBundle wheel is separate. The Python package client may reuse its own organization-managed credentials for an Artifactory PyPI repository, but those credentials are not available to the catalogue HTTPS client.

On a network that inspects TLS traffic, a proxy re-signs outbound HTTPS with a private root certificate authority. That authority is installed in the operating system’s trust store by your IT department, but Python reads its own certificate file rather than the OS store, so a catalogue fetch fails with:

[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate

agentbundle recovers from this on its own. When verification fails, it retries once against the administrator-controlled trust anchors the operating system already provides, and reports that it did so:

agentbundle: certificate verification failed for github.com; retrying with
operating-system trust anchors

The fallback reads one store: /Library/Keychains/System.keychain, the administrator keychain. Your login keychain is never read, because it is writable without administrator rights and a certificate landing there is not an IT trust decision. Apple’s own root program is not read either — the retry already starts from the certificates Python trusts by default, so importing a second curated root set would widen trust for no gain.

Verification stays strict: hostname checking and full chain validation apply on every attempt, the fallback only ever adds trust anchors, and no flag or variable disables verification. One caveat worth stating plainly: macOS lets an administrator mark a certificate Never Trust, and the fallback does not read those markings, so such a certificate is still used as an anchor. It is limited to a keychain only an administrator can write.

macOS is the only platform where Python ignores the operating system’s trust store. ssl.SSLContext.load_default_certs has a Windows branch and no macOS branch, so:

PlatformTrust sourceNeeds the fallback?
WindowsPython loads the Windows CA and ROOT stores directly, honouring each certificate’s trust settingsNo. A root your IT team pushes by Group Policy or Intune is already trusted.
LinuxOpenSSL reads /etc/ssl/certsNo, provided the authority was installed there (update-ca-certificates).
WSLSame as Linux — a WSL distribution does not inherit the Windows certificate storeYes, and the fallback cannot help. See below.
macOSOpenSSL reads a PEM file; the keychain is invisible to itYes — this is the case the fallback exists for.

On Windows and Linux the install fails with a message saying no operating-system anchors were available, rather than silently retrying against the same trust store. On those platforms a verification failure has some other cause, so reach for the troubleshooting steps in the error rather than assuming a missing corporate root.

A separate failure with the same error text, and the first one reported from the field. A python.org macOS installer build ships without a configured certificate store: until its Install Certificates.command runs, the interpreter trusts zero authorities and every HTTPS request fails — no proxy involved.

Check it directly:

python3 -c "import ssl; print(ssl.get_default_verify_paths())"

cafile=None with no capath means the store is unconfigured. Fix it once:

open "/Applications/Python 3.x/Install Certificates.command"
# or point the interpreter at the system bundle
export SSL_CERT_FILE=/etc/ssl/cert.pem

agentbundle also repairs this case automatically on macOS: when it finds an empty trust store it reads the system public certificate bundle (/etc/ssl/cert.pem) alongside the administrator keychain, because the administrator keychain holds private roots and cannot complete a public chain on its own. That file is Apple’s own TLS-purpose export — deliberately not a dump of SystemRootCertificates.keychain, which additionally carries code-signing and other single-purpose roots that have no business anchoring a TLS chain. The recovery is announced on stderr and names the wider trust set. It rescues the install; it does not fix the interpreter, so run the command above to stop every other tool failing the same way.

WSL is the case most likely to surprise a Windows-standardised organisation. Your IT team pushes the corporate authority to the Windows certificate store, Windows tools pick it up, and anything inside the WSL distribution does not — the two have separate trust stores. Install the authority into the distribution, or point AGENTBUNDLE_CA_BUNDLE at it:

# Debian/Ubuntu, from inside the WSL distribution
sudo cp corporate-ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates

Export the authority from Windows first (Certificate Manager → Trusted Root Certification Authorities → Export as Base-64 X.509).

  • A GitHub archive fetch redirects github.com → codeload.github.com. If your proxy permits only the first host, the fetch fails after certificates are working. Both hosts need to be reachable.

To opt out and see the raw verification error, set AGENTBUNDLE_NO_SYSTEM_TRUST=1.

agentbundle catalogue lint, agentbundle catalogue verify, and agentbundle catalogue package are the portable commands for validating and packaging a catalogue in CI. For the full pipeline contract — publication ordering, exit codes, responsibility boundaries, and JSON output shapes — see the Catalogue CI contract.

agentbundle catalogue contracts list|show|export enumerates, prints, or copies the contract files bundled with the running version, with no network access. See section 12 of the catalogue authoring standards.

See agentbundle --help for the full set (list-packs, list-profiles, list-targets, list-installed, validate, render, adapt, diff, upgrade, uninstall, reconcile, etc.). Each has its own --help page documenting its flags.