Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

agentpack pins the skills, plugins, commands, and rules your project feeds to AI coding agents. You declare them once in agentpack.toml, lock them to exact commits in pack.lock, and launch any supported agent with everything already staged in its native format.

Think of it as cargo or uv for agent configuration: a manifest, a lockfile, a content-addressed cache, and reproducible builds — except the artifacts are skills and prompts instead of crates.

The problem it solves

Agent configuration has become real configuration. A project’s behavior under Claude Code, Cursor, or Codex depends on slash commands, rules files, sub-agent definitions, MCP servers, and skill libraries. Today those artifacts live scattered across GitHub repos and local folders, with no shared versioning story, and each agent expects a different file layout.

So teams copy-paste. The same skill gets pasted into .claude/, then adapted by hand for .cursor/, then forgotten when a third agent shows up. Versions drift between machines. Nothing records which commit of a shared rule set you actually ran. There is no lockfile.

agentpack replaces the copy-paste with a dependency graph.

What it gives you

CapabilityWhat it means
Declarative manifestagentpack.toml lists direct dependencies with version constraints, modes, and MCP servers
Deterministic lockfilepack.lock (v2) pins every package — direct and transitive — to an exact commit and cache_key
Content-addressed cacheEach package is fetched once into $AGENTPACK_HOME/cache/<cache_key>/ and shared across projects
Per-harness stagingArtifacts are materialized into per-harness staging trees outside your repo — never committed, never symlinked into your real ~/.claude or ~/.cursor
Cross-harness conversionA skill, command, agent, or rule is re-rendered into each harness’s native format, not blindly copied
Six launchersclaude, agent (Cursor), opencode, codex, grok, agy (Antigravity) — sync then exec the real binary

A first taste

# agentpack.toml
name = "my-project"
version = "0.1.0"

[dependencies]
"github.com/anthropics/skills/skills/canvas-design" = { branch = "main" }
"github.com/anthropics/claude-plugins-official/plugins/hookify" = { branch = "main" }
agentpack add anthropics/skills/skills/canvas-design   # resolve, lock, sync
agentpack claude                                       # launch Claude Code with both staged

The agent starts with every declared dependency in place — no manual copying, no drift, and nothing leaked into your global agent config.

How the pieces fit

agentpack.toml  ──>  pack.lock  ──>  cache  ──>  staged bundles  ──>  launch
  (you write)        (pinned)      (fetched)     (per-harness)       (agent runs)

Read on through Core Concepts to understand each step, or jump to the Quick Start to get something running first.

Source and license

agentpack is MIT-licensed and developed at github.com/OlegHQ/agentpack. It is pre-release: the CLI, lockfile shape, staging layout, and defaults may change without a migration path until a stable release is declared.

Installation

agentpack ships as a single binary. The recommended path is Homebrew; everything else builds from source with a Rust toolchain.

Homebrew (macOS and Linux)

brew install OlegHQ/tap/agentpack

That one line taps OlegHQ/homebrew-tap and installs the formula. The two-step form works too:

brew tap OlegHQ/tap
brew install agentpack

Upgrade later with:

brew upgrade agentpack

Prebuilt binaries and the shell installer

Each release publishes prebuilt binaries for macOS (arm64, x86_64), Linux (x86_64, arm64), and Windows (x86_64), built by cargo-dist. The release page carries a curl | sh installer:

curl --proto '=https' --tlsv1.2 -LsSf \
  https://github.com/OlegHQ/agentpack/releases/latest/download/agentpack-installer.sh | sh

Pick a specific version from the releases page if you don’t want latest.

From source

You need a Rust toolchain (edition 2021). Install rustup if you don’t have one, then:

git clone https://github.com/OlegHQ/agentpack.git
cd agentpack
cargo install --path .

This drops the agentpack binary in ~/.cargo/bin/; make sure that directory is on your PATH. The repo’s Makefile also offers make install, which builds in release mode and copies the binary to ~/.local/bin (override with make install INSTALL_DIR=/usr/local/bin).

Verify

agentpack --version

Where agentpack keeps its state

All cached content, the metadata index, and per-project bookkeeping live under a single user-wide directory, not inside your repo. When AGENTPACK_HOME is unset, the default is:

  • Linux/macOS$XDG_DATA_HOME/agentpack if XDG_DATA_HOME is set, otherwise $HOME/.local/share/agentpack
  • Windows%LOCALAPPDATA%\agentpack

Override it by exporting the variable in your shell profile:

export AGENTPACK_HOME="$HOME/.local/share/agentpack"

A custom AGENTPACK_HOME is useful for a shared cache on a network mount, or for isolating a project’s state. See Environment Variables for the complete list.

Quick Start

Zero to a running agent in a few minutes. The short version: init, add, launch.

1. Initialize a manifest

From your project directory:

agentpack init

This writes a minimal agentpack.toml (identity is two top-level keys, not a [package] table) and an empty v2 pack.lock:

name = "my-project"
version = "0.0.1"

[dependencies]

init fails if agentpack.toml already exists, so it never clobbers your manifest.

2. Add a dependency

add takes a package spec. The most explicit form is a Go-style module ID, but owner/repo/path shorthand and GitHub tree/blob URLs work too:

agentpack add anthropics/skills/skills/canvas-design

agentpack resolves the spec, appends it under [dependencies], refreshes pack.lock, and runs a sync — all in one command:

[dependencies]
"github.com/anthropics/skills/skills/canvas-design" = {}

To pin a constraint, attach a @ref to the spec or edit the entry into table form ({ version = "^1.0.0" }, { branch = "main" }, { tag = "v2.1.0" }). See Your First Manifest. To stage nothing yet, pass --no-sync.

3. Lock and sync explicitly (optional)

add already locked and synced. You only run these by hand for CI, or after editing the manifest directly:

agentpack lock    # re-resolve agentpack.toml → pack.lock (network)
agentpack sync    # fetch into the cache and rebuild every harness's staging

Commit both agentpack.toml and pack.lock. The first sync downloads everything; later syncs reuse the content-addressed cache and are fast.

4. Launch an agent

Each launcher syncs (fast path when nothing changed), stages for that harness, and execs the real binary:

agentpack claude     # Claude Code
agentpack agent      # Cursor Agent (alias: cursor-agent)
agentpack opencode   # OpenCode
agentpack codex      # Codex
agentpack grok       # Grok
agentpack agy        # Antigravity

Anything after the subcommand is forwarded to the underlying binary:

agentpack codex --model gpt-5-codex

Next steps

Your First Manifest

agentpack.toml lives at your project root and declares two things: the project’s identity and its direct dependencies. It is the source of truth for what to install; pack.lock records the resolved result.

Minimal manifest

Identity is two top-level keys. There is no [package] table.

name = "my-project"
version = "0.0.1"

Module IDs encode the path inside a repo

A dependency key is a module ID — a Go-style path. The in-repo location of the package is part of the ID, not a separate field:

[dependencies]
# Repository root
"github.com/acme/lint-rules" = "^1.2"

# A subdirectory inside the repo (note the trailing path segments)
"github.com/acme/monorepo/agents/python" = "^2.0"

github.com/acme/monorepo/agents/python means the agents/python directory of github.com/acme/monorepo. This is the single most common point of confusion: to pull a subdirectory, put it in the key — do not reach for a path field (that field means something else; see below).

Pinning: short string vs. table

The value can be a short string (a branch, tag, or version constraint) or a table for anything richer:

[dependencies]
# Semver constraint against the repo's tags
"github.com/acme/lint-rules" = "^1.2"

# Track a branch — resolves to its HEAD at lock time (not reproducible over time)
"github.com/acme/experimental" = { branch = "main" }

# Pin an exact tag
"github.com/acme/shared-rules" = { tag = "v2.1.0" }

# Pin an exact commit
"github.com/acme/shared-rules" = { commit = "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2" }

A table accepts version, branch, tag, commit, and path. Identity and cache_key always derive from the resolved commit SHA, never from a branch or tag name.

Local path dependencies

The path field points at a directory on your filesystem, relative to the project root. This is for developing a package against a consumer, not for selecting a subdirectory of a GitHub repo:

[dependencies]
"mcp-retrieval" = { path = "../mcp-retrieval" }

On every lock/sync, path dependencies are re-copied from source and re-hashed, so local edits are picked up. Sync will error on a machine where the path is missing and the cache slot is empty.

Version constraint syntax

Constraints are matched against the dependency’s SemVer tags:

ConstraintMeaning
"1.2.3"Exactly 1.2.3
"^1.2.3">=1.2.3, <2.0.0 (compatible)
"~1.2.3">=1.2.3, <1.3.0 (patch updates)
">=1.0, <2.0"Explicit range
"*"Any version

A fuller example

name = "acme-backend"
version = "0.2.1"

[dependencies]
"github.com/anthropics/skills/skills/canvas-design"      = { branch = "main" }
"github.com/anthropics/claude-plugins-official/plugins/hookify" = { branch = "main" }
"github.com/acme/monorepo/packages/coding-agent"         = "^2.0"
"local-rules" = { path = "../local-rules" }

After editing the manifest by hand, run agentpack lock to refresh pack.lock, then agentpack sync to fetch and stage. (agentpack add does both for you.)

See the Manifest Schema for every field, plus the [modes] and [mcp.servers] sections.

Manifest (agentpack.toml)

agentpack.toml is the human-edited source of truth for a project. It declares direct dependencies, project-local modes, and MCP servers — and nothing else. Transitive dependencies, resolved commits, and cache keys all live in the generated pack.lock.

Project identity

Two top-level keys, written before any table:

name    = "my-project"   # used in lock metadata and diagnostics
version = "0.0.1"        # SemVer string

There is no [package] wrapper. init fills both from the directory name and a default version; you can edit them freely.

[dependencies]

Each key is a module ID; each value is either a short string or a table.

[dependencies]
# Short form: a version constraint, branch, tag, or commit as a bare string
"github.com/acme/lint-rules" = "^1.2"

# Table form: anything richer
"github.com/acme/shared-rules" = { tag = "v2.1.0" }
"local-tools"                  = { path = "../local-tools" }

Table fields

FieldTypeDescription
versionstringSemVer constraint matched against the repo’s tags
branchstringTrack a branch; resolves to its HEAD at lock time (not reproducible over time)
tagstringPin an exact tag
commitstringPin an exact commit SHA
pathstringLocal filesystem directory, relative to the project root — for local development, not for selecting a repo subdirectory

Module ID format

Module IDs are lowercase Go-style paths. The in-repo location of the package is encoded directly in the ID:

github.com/<owner>/<repo>                 # repository root
github.com/<owner>/<repo>/<p1>            # the <p1> subdirectory
github.com/<owner>/<repo>/<p1>/<p2>/...   # nested subdirectory

The host is always github.com for remote packages. To depend on a subdirectory of a monorepo, put the subpath in the key:

"github.com/acme/monorepo/packages/agent-rules" = "^1.0"

A human-typed spec may carry an optional @ref suffix (...@v1.2, ...@main), but identity and cache_key always come from the resolved commit, never the ref name.

Remote vs. local

When a dependency has a path field, it is resolved from your filesystem and never fetched from GitHub. The module ID is still used as the package’s identity for deduplication across the dependency graph.

Commit it

Always commit agentpack.toml together with pack.lock. The pair gives every contributor and every CI run identical resolution. Editing the manifest by hand is fine — run agentpack lock afterward to reconcile the lockfile.

Lockfile (pack.lock)

pack.lock records the exact resolved state of your dependency graph. agentpack generates it; you commit it and never edit it by hand. It uses lockfile version 2 — older [[skills]] / [[plugins]] layouts are rejected.

Why it exists

  • Reproducibility — everyone resolves to the same commit, regardless of when they sync or which tags have since been published.
  • Completeness — the lock lists every package, both the direct dependencies from your agentpack.toml and the transitive ones pulled from nested agentpack.toml files inside fetched packages.
  • Integrity — each package carries a cache_key (a content hash) that identifies its cache slot and lets agentpack detect a corrupted or wrong tree before staging.
  • Auditability — it’s plain TOML, so git diff pack.lock shows exactly what moved.

Structure

A v2 lockfile has a version marker, a [meta] block, an optional [config] block, and a flat list of [[packages]]:

lockfile_version = 2

[meta]
name = "my-project"
version = "0.0.1"

[[packages]]
module    = "github.com/anthropics/skills/skills/canvas-design"
direct    = true
kind      = "skill"
url       = "https://github.com/anthropics/skills/tree/<40-hex>/skills/canvas-design"
owner     = "anthropics"
repo      = "skills"
path      = "skills/canvas-design"
commit    = "<40-hex commit SHA>"
cache_key = "<64-hex content hash>"
name      = ""

Fields per package

FieldDescription
moduleModule ID, matching a key in [dependencies] (or a transitive dependency)
directtrue for a direct dependency, false for a transitive one
kind"skill" or "plugin"
urlGitHub tree URL pinned at the resolved commit
owner, repo, pathGitHub coordinates and in-repo path
commitFull 40-hex commit SHA that was resolved
cache_key64-hex content hash; names the cache slot under $AGENTPACK_HOME/cache/
namePlugin name (empty for skills)

The optional [config] table holds bookkeeping such as disabled_plugins.

Refreshing the lock

agentpack lock            # resolve agentpack.toml → pack.lock, keeping commits already pinned
agentpack lock --update   # re-resolve floating pins (branches, floating semver) from GitHub

lock resolves the full graph and rewrites the file. It does not download package content — that is sync’s job. Note that launching a harness does not advance floating pins: launchers run a fast pre-sync that skips re-resolution when inputs are unchanged. Run lock --update (or sync --update-lock) when you actually want a branch or floating constraint to move.

When to commit it

Always — for application projects and shared agent configurations alike. It is what makes “works on my machine” hold across the team and CI. Reusable packages should commit it too, though their downstream consumers resolve independently.

Drift

If pack.lock falls out of sync with agentpack.toml (typically after a hand edit), reconcile with agentpack lock. With an empty [dependencies] table, sync treats the existing lock as authoritative and leaves it alone — useful for hand-authored or test lockfiles.

Dependency Resolution

agentpack lock turns your manifest into a fully pinned graph: every package, direct and transitive, resolved to one concrete commit. This page covers how that happens.

The steps

  1. Load the manifest. agentpack reads agentpack.toml and collects [dependencies].
  2. List available versions. For each remote dependency it needs to resolve a ref, it queries GitHub for the repo’s tags. Tags may be v1.2.3 or 1.2.3.
  3. Select a version. For a SemVer constraint, agentpack picks the highest tag that satisfies it. A tag/commit pin is taken as-is; a branch resolves to that branch’s current HEAD.
  4. Expand transitively. If a resolved package contains its own agentpack.toml, its [dependencies] are added to the graph and resolved too. This recurses until the graph is complete.
  5. Resolve conflicts. When two packages constrain the same dependency differently, agentpack looks for a version satisfying both. If none exists, lock fails and names the conflicting packages.
  6. Write pack.lock. Each selected ref’s commit SHA is recorded along with its cache_key.

lock resolves; it does not download content. Fetching happens in sync.

Version selection

agentpack picks the highest tag satisfying the constraint:

ConstraintAvailable tagsSelected
"^1.0"1.0.0, 1.2.3, 2.0.01.2.3
"~1.2"1.2.0, 1.2.9, 1.3.01.2.9
"1.0.0"1.0.0, 1.1.01.0.0
">=1.0, <1.5"1.0.0, 1.4.9, 1.5.01.4.9

Branch pins drift

A branch pin resolves to the branch HEAD at lock time. Two people who lock on different days get different commits, so it is not reproducible over time — prefer a version constraint or tag for anything stable:

"github.com/acme/wip" = { branch = "main" }

Because the lockfile stores the resolved commit, a branch pin does not move on its own. It only advances when you explicitly run agentpack lock --update (or sync --update-lock).

Local path dependencies

A path dependency skips version selection entirely. agentpack reads the directory on disk and computes its content hash from the files present at lock time, so local edits are reflected on the next lock/sync.

Package roots and marketplace repositories

Agentpack recognizes a directory as a package root when it contains one of these markers:

  • SKILL.md
  • agentpack.toml
  • .claude-plugin/plugin.json, .cursor-plugin/plugin.json, or .codex-plugin/plugin.json
  • .claude-plugin/marketplace.json, .cursor-plugin/marketplace.json, or .agents/plugins/marketplace.json

A marketplace root can represent one logical plugin through separate local source directories for Claude, Cursor, and Codex. Agentpack resolves those paths relative to the marketplace root, verifies that their plugin names agree, and merges them into one canonical cache package before staging. This supports repositories such as:

.claude-plugin/marketplace.json  -> ./providers/claude/plugin
.cursor-plugin/marketplace.json  -> ./providers/cursor/plugin
.agents/plugins/marketplace.json -> ./providers/codex/plugin

Marketplace source paths must remain inside the repository. A catalog with multiple plugin names is ambiguous for one dependency, so agentpack rejects it with guidance to add the desired plugin source directory directly.

Metadata caching and the offline fallback

GitHub ref→commit and tag-list lookups are cached in $AGENTPACK_HOME/cache/db.reddb and reused across add, lock, and sync, which keeps repeat resolutions off the network. When the GitHub REST API fails or is throttled, agentpack falls back to the Git protocol — an embedded gix ls-refs against https://github.com/<owner>/<repo>.git — before resorting to stale cached metadata. There is no hard dependency on the REST API for ref and tag resolution.

To work entirely from a warm cache without touching the network, keep [dependencies] and pack.lock unchanged and run agentpack sync: with an unchanged lock, sync stages from the cache without re-resolving.

Content-Addressed Cache

agentpack fetches each package once and stores it under a content hash, so the same package at the same commit is never downloaded twice — and is shared across every project on the machine.

Where it lives

Everything sits under the user-wide agentpack home, never inside your repo:

$AGENTPACK_HOME/
  cache/
    <cache_key>/          # one extracted package tree per content hash
    db.reddb              # metadata index + alias map + cached GitHub ref/tag lookups
  local/
    <owner>/<repo>/...     # optional offline mirror (same layout as owner/repo specs)
  projects/
    <project-hash>/        # per-project bookkeeping (overlay manifests, launch-sync state)

AGENTPACK_HOME defaults to $XDG_DATA_HOME/agentpack (or $HOME/.local/share/agentpack) on Unix and %LOCALAPPDATA%\agentpack on Windows. Override it:

export AGENTPACK_HOME=/data/agentpack

Each cache/<cache_key>/ directory is an immutable extracted tree, keyed by the cache_key recorded in pack.lock. agentpack never overwrites a slot: if the key is already present, the fetch is skipped. The same key is reached whenever owner, repo, in-repo path, and commit match — so duplicate content deduplicates automatically.

How sync uses it

agentpack sync walks pack.lock and, for each package:

  1. Checks whether cache/<cache_key>/ already exists.
  2. If missing, downloads the tree from GitHub at the pinned commit (or copies it, for filesystem and local/ mirror specs).
  3. Stores it under the cache key.
  4. Materializes it into the per-harness staging directories, converting artifacts to each harness’s native format.

Path dependencies are the exception to immutability: they are re-copied from source on every lock/sync, and their content hash detects changes.

Sharing across projects and CI

Because the cache lives under AGENTPACK_HOME rather than in the project, every project on the machine shares it. Two projects depending on the same package at the same commit store it once.

In CI, persist the cache between runs to skip redundant downloads. Key on pack.lock so the cache invalidates when dependencies change:

# GitHub Actions
- uses: actions/cache@v4
  with:
    path: ~/.local/share/agentpack/cache   # or $AGENTPACK_HOME/cache
    key: agentpack-${{ hashFiles('pack.lock') }}

Invalidation

The cache is never invalidated automatically: a key identifies its content, so changed upstream content is simply a different key, stored alongside the old one. To force a clean re-fetch, delete the directory and sync again:

rm -rf "$AGENTPACK_HOME/cache"
agentpack sync

Staging and Bundles

Staging materializes cached packages into the directory layout each harness expects. Every harness has its own conventions, so sync builds a separate staging tree per harness and per mode.

Staging stays outside your repo

Staged trees are deliberately kept out of your project’s Git repository. They are machine-specific, regenerated on demand from the cache, and can be large. agentpack also does not symlink pack content into your real ~/.claude or ~/.cursor — that would leak project-specific pins into your global agent config.

Only agentpack.toml and pack.lock belong in version control. (Two opt-in exceptions exist: the agentpack agent and agentpack agy launchers create a single workspace symlink — see their harness guides.)

Staging root

The default staging root is a per-project directory in the system temp location:

<temp_dir>/agentpack-<project-hash>/

Set AGENTPACK_STAGING_ROOT to pin a stable location — useful when your OS rotates temp directories, or in CI for a fast local disk:

export AGENTPACK_STAGING_ROOT=/tmp/agentpack-staging

Each mode gets its own subtree under modes/<mode>/, and each harness gets a directory there:

<staging-root>/
  modes/
    default/
      plugins/agentpack-bundle/   # Claude --plugin-dir bundle
      opencode/                   # OpenCode config root (OPENCODE_CONFIG_DIR)
      codex-home/                 # Codex home (CODEX_HOME)
      cursor/  cursor-home/       # Cursor plugin tree + fake HOME
      grok/    grok-home/         # Grok bundle + home (GROK_HOME)
      agy/                        # Antigravity workspace plugin

A launcher points its harness at the right subtree by setting an environment variable or CLI flag before exec-ing the binary — claude --plugin-dir …, OPENCODE_CONFIG_DIR, CODEX_HOME, GROK_HOME, a fake HOME for Cursor, a workspace --add-dir for Antigravity. The harness guides cover each one.

What a bundle contains

A bundle is what one package contributes to a staging tree. Packages carry some mix of:

  • commands — slash-command definitions
  • agents — sub-agent or mode definitions
  • skills — reusable instruction sets
  • rules — persistent context applied across a session
  • MCP servers — merged into each harness’s native MCP config

Cross-harness conversion happens here: a skill authored in one format is re-rendered into every target harness’s native format rather than copied verbatim. Layering order within a tree is user config first, then plugins (by cache_key), then bare skills, then the project’s ./.agents/ overlay — later layers win on the same relative path.

Re-staging

sync rebuilds every harness’s staging from the current cache state:

agentpack sync

Launchers run a fast pre-sync when agentpack.toml, pack.lock, and ./.agents/ are unchanged since the last successful launch: they verify cache and staging integrity and skip the full lock-resolve, re-download, and rebuild. Because of that, floating pins do not advance on launch alone — run agentpack sync or agentpack lock --update when you need the lock refreshed.

Modes

A mode is a named, project-local preset that selects which staged content is active. The same pack.lock can be staged several ways — a lean writing profile, a full default, a review profile that drops a noisy MCP server — without touching your dependencies.

Modes are declared under [modes] in agentpack.toml and selected with the global --mode flag:

agentpack --mode writing claude
agentpack --mode review sync

When --mode is omitted, the reserved default mode applies. Each mode stages into its own subtree, so switching modes never rebuilds another mode’s staging.

Anatomy of a mode

[modes.default]
base = "all"

[modes.writing]
base    = "none"
enable  = [
  "package:github.com/mccteam/minecraft-console-client/.skills/humanizer",
  "package:github.com/s1mplesonny/technical-writing-skill/technical-writing",
]
disable = ["mcp:linear"]
FieldTypeMeaning
base"all" or "none"The starting point before selectors apply: everything on, or everything off
enablestring arraySelectors to turn on
disablestring arraySelectors to turn off

The model is straightforward: start from base, then apply enable and disable on top. A base = "all" mode with a short disable list is a denylist; a base = "none" mode with an enable list is an allowlist.

Selectors

Selectors name what to toggle:

SelectorTargets
package:<module-id>A whole package
package-path:<module-id>:<relative-path>One file inside a package (e.g. a single command)
mcp:<name>An MCP server by name
.agents:<relative-path>A file from the project’s ./.agents/ overlay
[modes.lean]
base    = "all"
disable = [
  "package-path:github.com/acme/heavy-pack:commands/noise.md",
  "mcp:filesystem",
]

Managing modes from the CLI

You can edit [modes] by hand, or use the mode subcommands, which preserve TOML formatting:

agentpack mode list                 # all declared modes (default is always present)
agentpack mode show writing         # base + enable + disable for one mode
agentpack mode create review
agentpack mode base review none
agentpack mode enable review package:github.com/acme/shared-rules
agentpack mode disable review mcp:filesystem
agentpack mode delete review        # `default` is reserved and cannot be deleted
agentpack mode tui                  # interactive editor

agentpack mode tui opens a terminal UI for toggling selectors visually; its palette follows AGENTPACK_TUI_THEME (light/dark, auto-detected when unset).

MCP Servers

Model Context Protocol servers give agents tools and data sources. agentpack collects MCP definitions from your manifest, your packages, and your ./.agents/ overlay, merges them, and writes the result into each harness’s native MCP config during sync — so one definition reaches every agent.

Declaring servers in the manifest

Add servers under [mcp.servers]. Each key is a server name:

[mcp.servers.filesystem]
command = "npx"
args    = ["-y", "@modelcontextprotocol/server-filesystem"]

[mcp.servers.retrieval]
command = "uvx"
args    = ["mcp-retrieval"]
env     = { API_KEY = "sk-..." }
FieldTypeDescription
commandstringExecutable to launch the server
argsstring arrayArguments passed to the command
envstring mapEnvironment variables for the server process
disabledboolOptional; skip this server when set

Managing servers from the CLI

agentpack mcp add retrieval --command uvx --args mcp-retrieval --env API_KEY=sk-...
agentpack mcp remove retrieval
agentpack mcp list      # every server, with provenance (manifest / plugin / .agents)

add and remove edit [mcp.servers] and then sync, unless you pass --no-sync.

The merge pipeline

After packages and the ./.agents/ overlay are staged, sync gathers MCP definitions from three sources and merges them. Later sources win when the same server name appears more than once:

  1. Plugin mcp.json files — from each staged plugin, ordered by cache_key, filtered through the active mode
  2. Manifest [mcp.servers] — your project-level definitions
  3. ./.agents/mcp.json — the project overlay

The merged set is then written in each harness’s native format:

HarnessOutput
Claude, CursorJSON mcpServers
OpenCodeopencode.json
Codex, Grok[mcp_servers] TOML
Antigravityplugin mcp_config.json (remote servers use serverUrl)

For Cursor’s fake HOME, the merged pack mcp.json is additionally merged with your real ~/.cursor/mcp.json — user-defined entries win on conflict — so agentpack-managed servers coexist with your own.

Toggling servers per mode

Use the mcp:<name> selector in a mode to enable or disable a server without removing it:

[modes.review]
base    = "all"
disable = ["mcp:filesystem"]

See Modes for the full selector syntax.

Claude Code

agentpack claude launches Claude Code with your packages staged as a plugin directory. Claude’s --plugin-dir flag is additive, so this layers on top of your normal configuration rather than replacing it.

What the launcher does

sync builds a single merged plugin bundle, then the launcher runs:

claude --plugin-dir "<staging>/modes/<mode>/plugins/agentpack-bundle" \
       --settings "$AGENTPACK_HOME/claude-settings.json"
  • --plugin-dir points at the staged agentpack-bundle.
  • --settings loads agentpack’s attribution overlay (see below). It is omitted if you keep attribution on.

Extra arguments are forwarded to claude:

agentpack claude --model opus
agentpack --yolo claude          # adds --dangerously-skip-permissions

CLAUDE_CONFIG_DIR is intentionally never set. Claude derives its keychain credential namespace from that variable, so a per-project value would forget your login on every project switch. Your real ~/.claude.json, ~/.claude/settings.json, and keychain entry are reused as-is.

What the bundle contains

agentpack-bundle/
  .claude-plugin/plugin.json
  commands/   agents/   skills/    # converted markdown artifacts
  hooks/  matchers/  core/  ...     # raw Claude support directories
  mcp.json                          # merged MCP servers (see MCP Servers)

agentpack does not copy your user-scoped ~/.claude/commands, agents, or skills into the bundle. Claude already reads those from $HOME, and copying them would produce duplicate slash commands (e.g. both /code-tutor and /agentpack-bundle:code-tutor).

Artifact handling

ArtifactIn the bundle
Commandscommands/<name>.md, Claude command frontmatter
Agentsagents/<name>.md, Claude agent frontmatter
Skillsskills/<name>/, normalized skill
RulesBest-effort skill fallback (Claude has no first-class rule files)
HooksSupported — rendered into the bundle’s hooks/
MCPMerged into mcp.json at the bundle root

Plugin-provided guidance is surfaced through a SessionStart hook that emits it as Claude’s additionalContext, so the model sees it at the start of every session. See Cross-Harness Conversion for the full mapping.

Attribution

By default sync writes $AGENTPACK_HOME/claude-settings.json with AI attribution forced off (includeCoAuthoredBy = false, empty commit/PR attribution) and the launcher passes it via --settings, which loads at flagSettings scope — above your user, project, and local settings. Your real config files are never modified. Set AGENTPACK_KEEP_ATTRIBUTION=1 to keep your existing values. See Overrides and Attribution.

Environment

VariableEffect
CLAUDE_CODE_PATHPath to the claude binary
AGENTPACK_STAGING_ROOTOverride the staging root
AGENTPACK_HOMEOverride the cache/state root (also holds claude-settings.json)

See Environment Variables for the complete list.

Cursor Agent

agentpack agent launches the Cursor CLI (cursor-agent) with your packages staged. The subcommand has the alias cursor-agent.

The binary is cursor-agent, not agent — the bare name agent collides with other tools (Grok ships one). Override the path with CURSOR_AGENT_PATH.

What the launcher does

Cursor resolves user-scoped skills, commands, and agents from a HOME-backed .cursor tree. So agentpack runs cursor-agent with a synthetic HOME at <staging>/modes/<mode>/cursor-home, whose .cursor/ symlinks the staged pack assets alongside your real Cursor login and session paths.

It also sets a few environment variables on the child process:

  • CURSOR_CONFIG_DIR → the fake home’s .cursor.
  • CURSOR_DATA_DIR → your real ~/.cursor when unset, so workspace-trust state survives staging rebuilds.
  • CARGO_HOME, RUSTUP_HOME, DOCKER_CONFIG → bridged from your real home (unless you already set them) so shell tooling keeps working inside the staged HOME.

On Linux, agentpack also links both Cursor’s uppercase Electron profile ($XDG_CONFIG_HOME/Cursor) and the CLI’s lowercase authentication profile ($XDG_CONFIG_HOME/cursor) into the synthetic home. This keeps auth.json durable across projects and staging rebuilds.

agentpack does not install pack content into your real ~/.cursor. It may create the durable Cursor profile directories and staged symlinks needed for first-time login/session files to persist; Cursor manages the actual auth, workspace trust, and MCP approval contents.

--workspace defaults to the current working directory where you invoked agentpack — not the pack root. In a monorepo where agentpack.toml lives in a parent directory, that distinction matters; pass --workspace <path> to override.

Cursor reads sub-agents only from resolve(--workspace)/.cursor/agents. So, only agentpack agent creates ./.cursor/agents as a symlink into the staged pack agents/ (when the pack ships agent markdown) and records it in a per-project overlay manifest for safe cleanup. Bare sync and the other launchers leave the workspace alone and remove any stale symlink from a previous agent run. If ./.cursor/agents already exists as a real directory or file, agentpack leaves it and logs a warning. Add .cursor/agents to .gitignore if you don’t want the symlink tracked.

agentpack agent
agentpack agent --workspace ./packages/api
agentpack --yolo agent              # adds --force

Cursor’s cursor-agent only accepts --trust in --print/headless mode; agentpack prepends it automatically there.

Staged layout

modes/<mode>/
  cursor/                          # Cursor plugins layout
    .cursor-plugin/marketplace.json
    agentpack-bundle/
      .cursor-plugin/plugin.json
      commands/ agents/ skills/ rules/ hooks/ mcp.json
  cursor-home/                     # fake HOME
    .cursor/   # symlinks to pack dirs + your real cli-config/session files
    .config/cursor -> real Cursor Agent CLI profile (Linux auth)

Artifact handling

ArtifactStaged as
Rules.mdc files, Cursor’s native format (preserved)
CommandsPlain markdown
Agents / skillsCursor agent/skill markdown
MCPJSON mcpServers, merged with your real ~/.cursor/mcp.json (your entries win)

Attribution is forced off via a real cli-config.json file in the staged tree (not a symlink), so agentpack’s writes never bleed back into your real Cursor profile.

Environment

VariableEffect
CURSOR_AGENT_PATHPath to the cursor-agent binary
CURSOR_DATA_DIRWorkspace-trust store; agentpack points it at real ~/.cursor when unset
AGENTPACK_STAGING_ROOTOverride the staging root (use this for a stable path when your OS rotates temp dirs)

See Environment Variables for the complete list.

OpenCode

agentpack opencode launches OpenCode with a redirected config root. Unlike Claude’s additive plugin dir, OpenCode is configured by pointing it at a different config directory entirely.

What the launcher does

OpenCode searches its config root — opencode.json plus agents/, commands/, modes/, plugins/, skills/ — like the standard .opencode / ~/.config/opencode locations. agentpack stages a complete root and runs:

OPENCODE_CONFIG_DIR="<staging>/modes/<mode>/opencode" opencode

Extra arguments are forwarded:

agentpack opencode --model anthropic/claude-sonnet-4-6
agentpack --yolo opencode      # stages opencode.json with "permission": "allow"

What the root contains

opencode/
  opencode.json          # seeded config + merged MCP + attribution instruction
  agents/   commands/   skills/    # converted pack artifacts
  plugins/  modes/

The root is seeded from your real ~/.config/opencode/ (opencode.json, agents, commands, modes, plugins, skills) so provider and auth configuration keep working even though the config root is redirected. Converted pack content is then overlaid into OpenCode’s native markdown locations.

Artifact handling

ArtifactStaged as
CommandsOpenCode command markdown
AgentsOpenCode agent markdown
SkillsOpenCode skill
RulesBest-effort skill fallback
MCPMerged into opencode.json

Attribution

OpenCode has no first-class attribution setting. agentpack writes an agentpack-no-attribution.md system-prompt file and adds it to instructions[] in the staged opencode.json — prompt-level guidance, the best available lever. Set AGENTPACK_KEEP_ATTRIBUTION=1 to skip it.

Inspecting the staged root

agentpack sync
ls "$AGENTPACK_STAGING_ROOT/modes/default/opencode/"   # if AGENTPACK_STAGING_ROOT is set

Environment

VariableEffect
OPENCODE_PATHPath to the opencode binary
AGENTPACK_STAGING_ROOTOverride the staging root

See Environment Variables for the complete list.

Codex

agentpack codex launches the OpenAI Codex CLI with a redirected home. Codex reads its config and skills from CODEX_HOME (default ~/.codex), so agentpack stages a complete home and points the variable at it.

What the launcher does

CODEX_HOME="<staging>/modes/<mode>/codex-home" codex

Extra arguments are forwarded:

agentpack codex --model gpt-5-codex
agentpack --yolo codex      # adds --dangerously-bypass-approvals-and-sandbox

The staged home is seeded from your real ~/.codex/ (config.toml, skills, themes) so user config keeps working under the redirect.

Credential bridging

Codex stores OAuth/API material in auth.json or in the OS keychain, keyed by the canonical CODEX_HOME path — so a staged path would otherwise miss your keychain entry and force re-login. agentpack avoids copying credentials per project by linking each staged auth.json to a shared source:

  • your real ~/.codex/auth.json when that file already exists, or
  • $AGENTPACK_HOME/shared/codex/auth.json, which agentpack materializes from the real ~/.codex keychain entry (service Codex Auth) when credentials live in the keychain.

The staged config.toml is forced to cli_auth_credentials_store = "file", so every project shares refresh-token updates through that one file.

Staged layout

codex-home/
  auth.json -> ~/.codex/auth.json | $AGENTPACK_HOME/shared/codex/auth.json
  config.toml          # seeded + attribution off + merged [mcp_servers]
  skills/
    <name>/SKILL.md

Artifact handling

Codex gets the portable skill subset of pack content. agentpack does not synthesize Codex plugin marketplaces from Claude plugins.

ArtifactStaged as
SkillsCodex skill under skills/<name>/
CommandsSkill fallback
AgentsSkill fallback
RulesSkill fallback
MCPMerged into [mcp_servers] in config.toml

Attribution is forced off via commit_attribution = "" in the staged config.toml. Set AGENTPACK_KEEP_ATTRIBUTION=1 to keep your value.

Environment

VariableEffect
CODEX_PATHPath to the codex binary
AGENTPACK_HOMECache/state root; also holds the shared Codex auth file when keychain bridging is needed
AGENTPACK_STAGING_ROOTOverride the staging root

See Environment Variables for the complete list.

Grok

agentpack grok launches the Grok CLI with a redirected home. The installed grok 0.1.219 rejects --plugin-dir and --settings, so agentpack configures it through GROK_HOME and a staged config.toml instead.

What the launcher does

GROK_HOME="<staging>/modes/<mode>/grok-home" grok --cwd <project-root>

agentpack injects --cwd when you don’t supply it. Extra arguments are forwarded:

agentpack grok
agentpack --yolo grok       # adds --always-approve

The staged home is seeded from your real ~/.grok/ (config.toml, skills, agents, commands, plugins) so user config keeps working. Auth survives staging rebuilds: agentpack links your real ~/.grok/auth.json and mcp_credentials.json when present.

Staged layout

grok-home/
  config.toml          # seeded + [plugins].paths + [mcp_servers] + attribution guidance
  auth.json            # linked to real ~/.grok/auth.json
grok/
  agentpack-bundle/
    plugin.json
    commands/ agents/ skills/ rules/

Pack content is rendered into grok/agentpack-bundle/ with a root plugin.json, and the staged config.toml gets a [plugins].paths entry pointing at that bundle. MCP servers are written as Grok-native [mcp_servers] TOML in config.toml.

Hooks are not staged

This is a deliberate limitation, not an omission. Grok’s hooks.json format is Claude-compatible, but grok inspect --json confirms it reads hooks only from the real ~/.grok/hooks — a redirected GROK_HOME is honored for config.toml and MCP but not for hook discovery. Bundle hooks loaded via [plugins].paths come up disabled (they need interactive trust), and grok plugin install --trust writes into the real ~/.grok (pollution). With no isolated, headless path available, Grok’s hook support stays unsupported.

Because Grok also reads Claude-compatible user sources from your real ~/.claude, agentpack’s collision check accounts for both ~/.grok and ~/.claude when removing pack copies that would shadow a user install.

Attribution

grok 0.1.219 has no verified first-class attribution-off setting. agentpack stages prompt-level guidance (AGENTS.md in the staged home) and does not modify your real ~/.grok.

Environment

VariableEffect
GROK_PATHPath to the grok binary
AGENTPACK_STAGING_ROOTOverride the staging root

See Environment Variables for the complete list.

Antigravity

agentpack agy launches the Antigravity CLI (agy). Antigravity has no config-root override and shares auth and settings with the desktop app under your real profile, so agentpack leaves HOME and ~/.gemini untouched and delivers pack content as a workspace plugin instead.

What the launcher does

agy --add-dir <cwd>

agentpack injects --add-dir with the current working directory (where you invoked agentpack — not the pack root) when you don’t supply it. Extra arguments are forwarded:

agentpack agy
agentpack --yolo agy       # adds --dangerously-skip-permissions

Pack content is staged into <staging>/modes/<mode>/agy/agentpack-bundle/ with a root plugin.json. Only agentpack agy creates the workspace symlink that exposes it:

./.agents/plugins/agentpack-bundle  ->  <staging>/.../agy/agentpack-bundle

This follows the same current-working-directory rule as Cursor’s --workspace. The symlink is tracked in a per-project overlay manifest for safe cleanup — bare sync and the other launchers never create it and remove any stale one. Add .agents/plugins/agentpack-bundle to .gitignore if you don’t want it tracked.

Staged layout

agy/agentpack-bundle/
  plugin.json
  skills/ agents/ commands/ rules/
  mcp_config.json      # remote MCP servers use serverUrl

Hooks are not staged

As with Grok, this is a verified limitation. Antigravity has a real hook engine and agy plugin validate statically accepts a Claude-shape hooks.json, but agy does not auto-load hooks from a workspace .agents/plugins/<name>/ overlay — a live session never fires a SessionStart hook from there, and the binary documents .agents/ as agent metadata, not a plugin source. With no config-root redirect, the only way to load hooks is agy plugin import/install into the real ~/.gemini (pollution). Antigravity’s hook support therefore stays unsupported.

Attribution

No verified first-class attribution-off setting. agentpack stages an always-apply plugin rule (rules/agentpack-no-attribution.md) as prompt-level guidance and does not modify your real profile.

Environment

VariableEffect
AGY_PATHPath to the agy binary
AGENTPACK_STAGING_ROOTOverride the staging root

See Environment Variables for the complete list.

Cross-Harness Conversion

Every harness names and formats the same ideas differently. agentpack does not copy artifacts verbatim — during sync it parses each markdown artifact from the cached package and re-renders it into every target harness’s native format.

The four artifact types

TypeWhat it is
commandA named slash command (e.g. /review)
agentA named sub-agent with its own system prompt
skillA reusable instruction set with frontmatter
ruleContext applied across a session (Cursor .mdc, Antigravity rules)

How it works

  1. agentpack reads the artifact from the cached package, detecting its source format — Cursor plain markdown, OpenCode markdown frontmatter, Claude command/agent/skill frontmatter, or skill frontmatter.
  2. For each target harness, it re-renders the artifact in that harness’s native shape: the right directory, filename, and frontmatter.
  3. When a harness lacks a first-class equivalent, agentpack uses a documented fallback rather than dropping the artifact.

Where each artifact lands

SourceClaudeCursorOpenCodeCodexGrokAntigravity
commandcommand frontmatterplain markdowncommand markdownskill fallbackcommandcommand
agentagent frontmatteragent markdownagent markdownskill fallbackagentagent
skillnormalized skillskillskillCodex skillskillskill
ruleskill fallback.mdc (preserved)skill fallbackskill fallbackskill fallbackrule (preserved)

Two harnesses have first-class rules — Cursor and Antigravity — so rules are preserved there. Everywhere else, a rule becomes a best-effort skill, with its original rule scope noted so the intent survives. Codex gets the portable skill subset: commands and agents fall back to skills.

Frontmatter

agentpack writes whatever frontmatter the target format needs (for example, Cursor .mdc metadata), using the artifact’s name and any metadata carried in the source. The instruction body is preserved; only the wrapper changes.

Skill shadowing

A full plugin at repo path P (same owner / repo / commit) shadows any skill whose path is P or sits under P/. An empty P shadows every skill for that repo at that commit. This keeps a plugin and its bundled skills from staging duplicate copies of the same content.

CLI Commands

agentpack <command> [args]. Global flags go before the subcommand; everything after a launcher subcommand is forwarded to the underlying agent binary.

Global flags

FlagDescription
--project-root <path>Project root holding agentpack.toml/pack.lock (default: search upward from cwd)
--mode <name>Select a mode from [modes] (default: the reserved default mode)
--yoloForward each harness’s “skip permission prompts” / full-access flag
-q, --quietOnly print warnings and errors
--no-progressDisable spinners and progress bars
--debugPrint launcher diagnostics (workspace paths, env overrides, fast-sync skip reason)
-h, --helpPrint help
-V, --versionPrint the agentpack version

Lifecycle commands

agentpack init

Create agentpack.toml and a v2 pack.lock in the project root, and ensure AGENTPACK_HOME exists. Fails if agentpack.toml already exists.

agentpack init
agentpack init --name my-project --version 0.1.0

agentpack add <spec>

Resolve a package spec, append it under [dependencies], refresh pack.lock, then sync.

agentpack add anthropics/skills/skills/canvas-design
agentpack add github.com/acme/monorepo/packages/rules@v1.2.0
agentpack add ./local-rules
agentpack add anthropics/skills/skills/canvas-design --no-sync

A spec may be a module ID, owner/repo[/path] shorthand, a GitHub tree/blob URL, a single-segment local/alias name, or a filesystem path. An optional @ref pins a branch, tag, or commit. --no-sync skips the sync step. (Requires a manifest.)

agentpack remove <spec>

Drop a matching [dependencies] entry, prune mode selectors that targeted it, refresh pack.lock, then sync unless --no-sync.

agentpack remove github.com/acme/monorepo/packages/rules

agentpack lock

Resolve agentpack.toml and rewrite pack.lock (direct + transitive). Network calls for ref/tag resolution; no content download.

agentpack lock            # keep commits already pinned
agentpack lock --update   # re-resolve floating pins from GitHub

agentpack sync

Ensure the cache and rebuild staging for every harness. Recomputes pack.lock from the manifest when [dependencies] is non-empty.

agentpack sync
agentpack --mode writing sync
agentpack sync --dry-run        # report actions without writing
agentpack sync --verify-only    # check cache + staging integrity only
agentpack sync --update-lock    # re-resolve floating pins while syncing

Launchers

Each launcher runs a fast pre-sync, stages for its harness, and execs the binary. Trailing arguments are forwarded.

CommandLaunchesMechanism
agentpack claudeClaude Code--plugin-dir + --settings
agentpack agent (alias cursor-agent)Cursor Agentsynthetic HOME
agentpack opencodeOpenCodeOPENCODE_CONFIG_DIR
agentpack codexCodexCODEX_HOME
agentpack grokGrokGROK_HOME (+ injected --cwd)
agentpack agyAntigravityinjected --add-dir
agentpack claude --model opus
agentpack --yolo codex
agentpack --mode writing claude

See the harness guides for what each one stages.

agentpack mcp

Manage [mcp.servers] in the manifest. add/remove sync afterward unless --no-sync.

agentpack mcp add retrieval --command uvx --args mcp-retrieval --env API_KEY=sk-...
agentpack mcp remove retrieval
agentpack mcp list      # all servers with provenance (manifest / plugin / .agents)

agentpack mode

Manage [modes] in the manifest.

agentpack mode list
agentpack mode show writing
agentpack mode create review
agentpack mode base review none
agentpack mode enable review package:github.com/acme/shared-rules
agentpack mode disable review mcp:filesystem
agentpack mode delete review        # `default` is reserved
agentpack mode tui                  # interactive editor

See Modes for selector syntax.

Manifest Schema

Complete reference for agentpack.toml.

Top-level shape

name    = "my-project"   # required
version = "0.0.1"        # required

[dependencies]   # optional
[modes]          # optional
[mcp.servers]    # optional

Identity is two top-level keys, not a [package] table.

KeyTypeRequiredDescription
namestringyesProject name; used in diagnostics and lock metadata
versionstringyesSemVer string

[dependencies]

Each entry is a key (module ID) mapped to a value (short string or inline table).

Key format

github.com/<owner>/<repo>
github.com/<owner>/<repo>/<p1>/<p2>/...

Module IDs are lowercase Go-style paths. The host is always github.com for remote packages. A subdirectory inside a repo is part of the key — there is no field for it.

Value: short string

A bare string is a version constraint, branch, tag, or commit:

"github.com/acme/rules" = "^1.2"

Value: inline table

"github.com/acme/rules" = { version = "^1.2" }
FieldTypeDescription
versionstringSemVer constraint matched against the repo’s tags
branchstringTrack a branch; resolves to its HEAD at lock time (not reproducible over time)
tagstringPin an exact tag
commitstringPin an exact commit SHA
pathstringLocal filesystem directory relative to the project root (local development)

Local path dependency

A path field makes the dependency local — read from disk, never fetched from GitHub. No version/branch/tag is needed:

"local-rules" = { path = "../local-rules" }

The key is still used as the package’s identity for deduplication.

Version constraint syntax

SyntaxSemantics
"1.2.3"Exactly 1.2.3
"^1.2.3">=1.2.3, <2.0.0
"~1.2.3">=1.2.3, <1.3.0
">=1.0.0"At or above 1.0.0
">=1.0, <2.0"Explicit range
"*"Any version

[modes.<name>]

Project-local staging presets. The reserved default mode applies when --mode is omitted. See Modes.

[modes.default]
base = "all"

[modes.writing]
base    = "none"
enable  = ["package:github.com/s1mplesonny/technical-writing-skill/technical-writing"]
disable = ["mcp:linear"]
FieldTypeDescription
base"all" or "none"Baseline before selectors are applied
enablestring arraySelectors to turn on
disablestring arraySelectors to turn off

Selectors: package:<module>, package-path:<module>:<relative-path>, mcp:<name>, .agents:<relative-path>.

[mcp.servers.<name>]

Project-level MCP server definitions, merged into each harness’s native MCP config. See MCP Servers.

[mcp.servers.retrieval]
command = "uvx"
args    = ["mcp-retrieval"]
env     = { API_KEY = "sk-..." }
FieldTypeDescription
commandstringExecutable that launches the server
argsstring arrayCommand arguments
envstring mapEnvironment variables for the server process
disabledboolOptional; skip this server when true

Complete example

name    = "acme-backend"
version = "0.5.0"

[dependencies]
"github.com/anthropics/skills/skills/canvas-design" = { branch = "main" }
"github.com/acme/shared-rules"                      = { tag = "v2.1.0" }
"github.com/acme/monorepo/packages/agent"           = "^2.0"
"local-dev" = { path = "../local-dev" }

[modes.default]
base    = "all"
disable = ["package-path:github.com/acme/shared-rules:commands/noisy.md"]

[modes.review]
base   = "none"
enable = ["package:github.com/acme/shared-rules", ".agents:rules/backend.mdc"]

[mcp.servers.filesystem]
command = "npx"
args    = ["-y", "@modelcontextprotocol/server-filesystem"]

Environment Variables

Set these in your shell profile (.bashrc, .zshrc, config.fish) or in CI.

State and staging

AGENTPACK_HOME

The user-wide root for cache, the metadata index, the local/ mirror, and per-project bookkeeping.

Default: $XDG_DATA_HOME/agentpack (or $HOME/.local/share/agentpack) on Unix; %LOCALAPPDATA%\agentpack on Windows.

export AGENTPACK_HOME=/data/agentpack

Point it at a network mount for a shared cache, or somewhere project-specific for isolation.

AGENTPACK_STAGING_ROOT

Root for per-harness, per-mode staging trees.

Default: <temp_dir>/agentpack-<project-hash>.

export AGENTPACK_STAGING_ROOT=/tmp/agentpack-staging

Set this for a stable path when your OS rotates temp directories, or to put staging on a fast local disk in CI.

Behavior toggles

AGENTPACK_KEEP_ATTRIBUTION

Default: unset. Set to 1 / true / yes to keep your existing AI-attribution settings (Co-Authored-By trailers, “Generated with X” footers) in staged harness configs. By default agentpack forces attribution off in staging. See Overrides and Attribution.

AGENTPACK_DOT_AGENTS

Default: enabled. Set to 0 to skip merging the project’s ./.agents/ overlay into harness staging.

AGENTPACK_TUI_THEME

Default: auto-detected from the terminal background (OSC 11 luma / COLORFGBG), falling back to dark. Set to light or dark to force the agentpack mode tui palette.

Claude proxy diagnostics

AGENTPACK_PROXY_LOG_DIR

Directory for agentpack --proxy claude JSONL diagnostics.

Default: $AGENTPACK_HOME/projects/<project-hash>/proxy-logs.

AGENTPACK_PROXY_LOG_PAYLOADS

Default: unset. Set to 1 / true / yes to include truncated upstream error bodies and payload snippets in proxy logs. Leave unset for sanitized metadata-only logs.

AGENTPACK_PROXY_LOG_MAX_BODY_BYTES

Maximum bytes retained for any payload snippet when payload logging is enabled.

Default: 4096.

AGENTPACK_PROXY_WS_CONNECT_TIMEOUT_SECS

WebSocket TCP connect timeout for proxy upstream connections.

Default: 15.

AGENTPACK_PROXY_WS_IDLE_TIMEOUT_SECS

WebSocket read/write idle timeout for proxy upstream connections.

Default: 300.

GitHub access

GITHUB_TOKEN / GH_TOKEN

Default: unset. When set, agentpack sends it as a bearer token for GitHub ref/tag lookups and authenticated downloads. Set one to resolve private repositories or to avoid anonymous API rate limits during heavy resolution. GITHUB_TOKEN takes precedence over GH_TOKEN.

Binary paths

Override the path to each harness’s executable when it isn’t on PATH or you need a specific build:

VariableBinary
CLAUDE_CODE_PATHclaude
OPENCODE_PATHopencode
CODEX_PATHcodex
CURSOR_AGENT_PATHcursor-agent
GROK_PATHgrok
AGY_PATHagy

Summary

VariableDefaultPurpose
AGENTPACK_HOMEXDG / %LOCALAPPDATA%Cache and state root
AGENTPACK_STAGING_ROOT<temp>/agentpack-<hash>Staging root
AGENTPACK_KEEP_ATTRIBUTIONunsetKeep AI attribution in staging
AGENTPACK_DOT_AGENTSenabledMerge ./.agents/ overlay
AGENTPACK_TUI_THEMEautoForce mode tui palette
AGENTPACK_PROXY_LOG_DIRproject state dirClaude proxy JSONL diagnostics
AGENTPACK_PROXY_LOG_PAYLOADSunsetInclude truncated payload snippets in proxy logs
AGENTPACK_PROXY_LOG_MAX_BODY_BYTES4096Payload snippet byte cap
GITHUB_TOKEN / GH_TOKENunsetGitHub auth for private repos and rate limits
CLAUDE_CODE_PATHAGY_PATHOverride harness binary paths

Publishing Packages

There is no registry. agentpack resolves packages straight from GitHub, using Git tags as versions. Publishing a package is just pushing a repo and tagging it.

A package is one of two kinds, detected by its layout:

  • Skill — a directory containing a SKILL.md.
  • Plugin — a directory containing a plugin manifest: .claude-plugin/plugin.json, .cursor-plugin/plugin.json, or both.

You do not declare your artifacts in a table. agentpack discovers them by reading the package’s content and re-renders them per harness (see Cross-Harness Conversion).

A skill package

The minimal skill is a directory with a SKILL.md:

canvas-design/
  SKILL.md
  reference.md      # any supporting files the skill references

Consumers depend on the directory’s module ID:

[dependencies]
"github.com/acme/skills/canvas-design" = "^1.0"

A plugin package

A plugin carries a manifest plus whatever artifact directories it ships. Layouts are normalized after fetch, so you can expose Claude and/or Cursor manifests:

hookify/
  .claude-plugin/plugin.json
  commands/   agents/   skills/   rules/   hooks/
  mcp.json

Transitive dependencies

If your package depends on other packages, add an agentpack.toml to its root declaring [dependencies]. When someone depends on your package, agentpack reads that manifest at the resolved commit and pulls your dependencies into their graph. The manifest’s [dependencies] table is the only part consulted for transitive resolution.

Versioning and tags

agentpack resolves SemVer tags. Both v1.0.0 and 1.0.0 are recognized:

git tag v1.0.0
git push origin v1.0.0

Suggested bump policy:

ChangeBump
New artifact, backward compatibleMINOR
Content-only fixPATCH
Artifact removed/renamed, or format breakMAJOR

Monorepo packages

Put the package (its SKILL.md or plugin manifest, and an agentpack.toml if it has dependencies) in a subdirectory, and tag the repo normally. Consumers reference the full subpath in the module ID:

[dependencies]
"github.com/acme/monorepo/packages/my-skills" = "^1.0"

agentpack reads from that subdirectory at the tagged commit.

Test before you tag

Develop against a consumer project using a local path dependency, then switch to a version constraint once you’re happy:

[dependencies]
"my-skills" = { path = "../my-skills" }

Run agentpack sync and inspect the staged output (the harness guides show where each tree lives).

Private repositories and rate limits

agentpack reads GITHUB_TOKEN (or GH_TOKEN) when present and sends it as a bearer token for ref/tag lookups and authenticated downloads. Set one to access private repos, and to avoid anonymous API rate limits on heavy resolution:

export GITHUB_TOKEN="ghp_..."

Team Workflows

Patterns for sharing agent configuration across a team.

Commit the manifest and lockfile

Commit both files together:

git add agentpack.toml pack.lock
git commit -m "chore: add agentpack dependencies"

That pair is what makes resolution identical for everyone. Staging trees are machine-local and live outside the repo by default, so there’s nothing to ignore unless you’ve pointed AGENTPACK_STAGING_ROOT inside the project — in which case ignore that path.

Onboarding

A new contributor clones the repo and runs one command:

agentpack sync     # fetch everything pinned in pack.lock and stage it
agentpack claude   # or any other launcher

No copying config files, no “which version of the rules are we on” — pack.lock answers that.

Updating a dependency

Edit the constraint, re-resolve, commit:

[dependencies]
"github.com/acme/shared-rules" = "^0.4"   # was ^0.3
agentpack lock            # reconcile pack.lock with the changed manifest
git add agentpack.toml pack.lock
git commit -m "chore: bump shared-rules to ^0.4"

lock reconciles manifest edits. To advance a floating pin (a branch, or a constraint whose range hasn’t changed) to its latest matching commit, use agentpack lock --update. Everyone else pulls the commit and runs agentpack sync.

Shared team packages

Keep team-specific skills and rules in a GitHub repo, tag releases, and depend on it from each project:

[dependencies]
"github.com/acme/team-agent-config" = "^1.0"

For a private repo, every machine and CI runner needs GITHUB_TOKEN (or GH_TOKEN) with read access. Projects stay aligned with agentpack lock && agentpack sync.

A committed project overlay (./.agents/)

To ship project-specific agent content that isn’t a published package, drop it in ./.agents/ at the repo root and commit it. On sync, agentpack merges it into the harness trees that don’t natively read it (the Claude bundle and Codex home), layered last so it wins on conflicts. Shape it like a harness layout: rules/, skills/, agents/, commands/, hooks/, mcp.json, top-level AGENTS.md/CLAUDE.md, and optional claude/ and codex/ subtrees.

Cursor, OpenCode, and Antigravity already discover workspace customization natively, so ./.agents/ is not merged into their staging. Set AGENTPACK_DOT_AGENTS=0 to skip the overlay entirely.

CI

Run agentpack sync before any step that needs staged artifacts, and persist the cache between runs. Key on pack.lock so the cache invalidates when dependencies change:

# .github/workflows/ci.yml
- name: Cache agentpack
  uses: actions/cache@v4
  with:
    path: ~/.local/share/agentpack/cache   # or $AGENTPACK_HOME/cache
    key: agentpack-${{ hashFiles('pack.lock') }}

- name: Sync agentpack
  run: agentpack sync

Merge conflicts in pack.lock

pack.lock is plain TOML and usually merges cleanly, but two branches that both change dependencies can collide. The reliable fix:

  1. Resolve the conflict in agentpack.toml (the source of truth).
  2. Run agentpack lock to regenerate a clean pack.lock from the merged manifest.
  3. Commit the regenerated lockfile.

Overrides and Attribution

How to bend agentpack’s defaults without editing shared packages, plus how attribution is handled in staged configs.

Override a dependency with a local copy

To develop a package and test it in a consumer at the same time, swap the remote dependency for a path:

[dependencies]
"shared-rules" = { path = "../shared-rules" }

sync re-copies the directory on every run, so edits show up immediately. Restore the version constraint and run agentpack lock when you’re done.

Pin an exact commit

When you need a commit that isn’t behind a tag, use the commit field directly — no branch tricks required:

[dependencies]
"github.com/acme/shared-rules" = { commit = "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2" }

tag and branch are the other exact-pin options; see Your First Manifest.

Project overlay and per-mode toggles

For project-specific content that isn’t a published package, use the committed ./.agents/ overlay (Team Workflows). To turn whole packages, individual files, or MCP servers on and off per task, use Modes. Both are first-class — reach for them before environment hacks.

Relocate state and staging

export AGENTPACK_STAGING_ROOT="/tmp/agentpack-staging"   # stable staging path
export AGENTPACK_HOME="./.agentpack"                     # project-isolated cache/state

A project-local AGENTPACK_HOME keeps all cached content and bookkeeping inside the repo directory; add .agentpack/ to .gitignore. See Environment Variables for defaults.

Attribution

By default, sync forces AI attribution off in every staged harness config — Co-Authored-By trailers and “Generated with X” footers — so your project doesn’t pick up agent credit lines unintentionally. Your real ~/.claude, ~/.codex, ~/.cursor, and ~/.config/opencode are never modified; only the staged copies and the Claude overlay file at $AGENTPACK_HOME/claude-settings.json are touched.

Set AGENTPACK_KEEP_ATTRIBUTION=1 (or true/yes) to keep your existing values.

How each harness is handled:

HarnessMechanism
Claude Code$AGENTPACK_HOME/claude-settings.json via --settings: includeCoAuthoredBy = false, empty commit/PR attribution (loads at flagSettings scope)
Codexcommit_attribution = "" in the staged config.toml
CursorattributeCommitsToAgent = false, attributePRsToAgent = false in the staged cli-config.json (a real file, never your real one)
OpenCodeNo first-class setting; a system-prompt file is added to instructions[]
GrokNo verified setting; prompt-level guidance only
AntigravityNo verified setting; an always-apply plugin rule

Claude, Codex, and Cursor have real attribution settings, so those are exact. OpenCode, Grok, and Antigravity expose no such setting, so agentpack falls back to prompt-level guidance — best-effort, not guaranteed.