Skip to main content

🎒 loadout

The package manager for AI coding agent artifacts.

Stop manually copying skills, rules, agents, and commands into every coding agent's config directory. loadout handles discovery, transformation, and installation across Claude Code, Cursor, and OpenCode — so your CLI or project setup script doesn't have to.

📦 Installation

# uv (recommended)
uv add loadout

# pip
pip install loadout

# With interactive agent selection prompt
uv add "loadout[interactive]"
pip install "loadout[interactive]"

🤔 Why loadout?

Every team building on top of AI coding agents hits the same problem: distributing custom skills, rules, and commands to developers' machines. The config directories differ, the file formats differ, and the logic for "install this skill to that agent" gets copy-pasted across tooling.

loadout extracts that logic into a single, typed, tested library with an extensible adapter pattern. You write your artifacts once. loadout puts them where they belong.

  • 🌐 Agent-agnostic — one artifact definition works across all supported agents
  • 🔍 Auto-detection — discovers which agents are installed on the system
  • 📁 Convention + configuration — scan for marker files, or define a manifest
  • 🔌 Adapter pattern — add support for new agents without touching core logic
  • 🪝 Lifecycle hooks — plug in your own logging, progress bars, or analytics
  • 🛡️ Fully typed — strict mypy, Pydantic models, protocol classes

⚡ Quick start

🚀 Install everything to every detected agent

from loadout import install_all

summary = install_all("./my-artifacts", force=True)

print(f"✅ Installed: {len(summary.installed)}")
print(f"⏭️  Skipped:   {len(summary.skipped)}")
print(f"❌ Failed:    {len(summary.failed)}")

💬 Interactive mode (checkbox prompt)

uv add "loadout[interactive]"
from loadout import install_interactive

summary = install_interactive("./my-artifacts")

🎛️ Full control

from loadout import discover_artifacts, detect_agents, install

artifacts = discover_artifacts("./my-artifacts")
agents = detect_agents()
summary = install(artifacts, agents, force=True)

Three tiers — pick the one that fits your UX. 🎯


🤖 Supported agents

Agent Config dir Skills Rules Agents Commands
Claude Code ~/.claude/ ✅ ✅ ✅ ✅
Cursor ~/.cursor/ ✅ ✅ (.mdc) — —
OpenCode ~/.opencode/ ✅ — — ✅

loadout auto-detects which agents are present and only installs to agents that support each artifact type. Unsupported combinations are cleanly skipped. ✨


🔎 Artifact discovery

📂 Convention-based (marker files)

Drop marker files into your artifact directories and loadout will find them:

my-artifacts/
  login-skill/
    SKILL.md              # ← marker: this directory is a skill
    helper.py
    utils.py
  security/
    auth-rule/
      RULE.md             # ← marker: this file is a rule
  setup-agent/
    AGENT.md              # ← marker: this file is an agent
  deploy/
    COMMAND.md            # ← marker: this file is a command

Marker files double as the artifact content — the SKILL.md is the skill. Categories are derived from directory structure (security/auth-rule/ → category security).

📋 Manifest-based (loadout.yaml)

For explicit control, add a loadout.yaml to the root of your artifacts directory:

artifacts:
  - name: login-skill
    type: skill
    path: login-skill

  - name: auth-rule
    type: rule
    path: security/auth-rule/RULE.md
    category: security
    description: "Enforces authentication checks"

When a manifest is present, marker-file scanning is skipped entirely — you have full control over what gets installed.

📝 Frontmatter support

Artifact files can include YAML frontmatter for metadata:

---
description: Handles user login flows
globs:
  - "src/auth/**"
always_apply: true
---

# Login Skill

Your skill content here...

🔌 Custom adapters

Need to support a new coding agent? Implement the AgentAdapter interface and register it:

from pathlib import Path
from loadout import (
    AgentAdapter,
    Artifact,
    ArtifactType,
    DetectedAgent,
    InstallResult,
    get_default_registry,
    install_all,
)

class WindsurfAdapter(AgentAdapter):
    @property
    def agent_name(self) -> str:
        return "windsurf"

    @property
    def display_name(self) -> str:
        return "Windsurf"

    @property
    def config_dir_name(self) -> str:
        return ".windsurf"

    def supported_artifact_types(self) -> set[ArtifactType]:
        return {ArtifactType.SKILL, ArtifactType.RULE}

    def detect(self) -> DetectedAgent | None:
        config_dir = Path.home() / self.config_dir_name
        if config_dir.is_dir():
            return DetectedAgent(
                name=self.agent_name,
                config_dir=config_dir,
                display_name=self.display_name,
            )
        return None

    def get_target_path(self, artifact: Artifact, config_dir: Path) -> Path:
        # Your path resolution logic
        ...

    def transform_content(self, artifact: Artifact, content: str) -> str:
        # Your content transformation logic
        return content

    def transform_filename(self, artifact: Artifact, filename: str) -> str:
        return filename

    def install(self, artifact: Artifact, agent: DetectedAgent, force: bool = False) -> InstallResult:
        # Your install logic
        ...

# Register and use 🎉
registry = get_default_registry()
registry.register(WindsurfAdapter())

summary = install_all("./my-artifacts", registry=registry)

The adapter pattern means core loadout never needs to change when new agents appear. 🧩


🪝 Lifecycle callbacks

Hook into every stage of the installation process for logging, progress bars, analytics, or custom error handling:

from loadout import LoadoutCallbacks, Artifact, DetectedAgent, InstallResult, install_all

class RichCallbacks:
    """Example: pretty-print progress with Rich."""

    def on_artifact_discovered(self, artifact: Artifact) -> None:
        print(f"  🔎 Found {artifact.artifact_type.value}: {artifact.name}")

    def on_agent_detected(self, agent: DetectedAgent) -> None:
        print(f"  🤖 Detected agent: {agent.display_name}")

    def on_install_started(self, artifact: Artifact, agent: DetectedAgent) -> None:
        print(f"  ⏳ Installing {artifact.name} → {agent.display_name}...")

    def on_install_complete(self, result: InstallResult) -> None:
        print(f"  ✅ Installed to {result.target_path}")

    def on_install_skipped(self, result: InstallResult) -> None:
        print(f"  ⏭️  Skipped: {result.error}")

    def on_install_failed(self, result: InstallResult) -> None:
        print(f"  💥 FAILED: {result.error}")

summary = install_all("./my-artifacts", callbacks=RichCallbacks())

Only override the hooks you care about — the LoadoutCallbacks protocol defines the full interface, and NoOpCallbacks provides a ready-made base with no-op defaults.


📖 API reference

⚙️ Top-level functions

Function Description
install_all(source_dir, force, registry, callbacks) Discover artifacts, detect agents, install everything
install_interactive(source_dir, force, registry, callbacks) Same as above with interactive agent selection
install(artifacts, agents, force, registry, callbacks) Install specific artifacts to specific agents
discover_artifacts(source_dir) Scan a directory and return a list of Artifact objects
detect_agents(registry) Detect installed coding agents
get_default_registry() Get the built-in adapter registry

🧱 Models

Model Description
Artifact A discovered artifact (name, type, source path, category, frontmatter)
ArtifactType Enum: SKILL, RULE, AGENT, COMMAND
DetectedAgent An agent found on the system (name, config dir, display name)
InstallResult Result of a single artifact install (status, target path, error)
InstallSummary Batch result with .installed, .skipped, .failed, .already_existed
Manifest Parsed loadout.yaml manifest

🚨 Exceptions

Exception Description
LoadoutError Base exception for all loadout errors
ArtifactNotFoundError Source artifact path does not exist
ManifestError Invalid loadout.yaml
InstallError Installation failed
AdapterNotFoundError No adapter registered for the given agent
AdapterAlreadyRegisteredError Adapter name collision
TransformError Content transformation failed

🛠️ Development

# Clone and install with all extras
git clone https://github.com/nickmaccarthy/loadout.git
cd loadout

# uv (recommended)
uv sync --all-extras

# pip
pip install -e ".[dev,interactive]"

# Set up pre-commit hooks
uv run pre-commit install

# Run all pre-commit checks (ruff, mypy, formatting, etc.)
uv run pre-commit run --all-files

# Run tests
uv run pytest

# Run tests with coverage
uv run pytest --cov=loadout --cov-report=term-missing

💡 Pre-commit hooks run automatically on every git commit, catching lint errors, type issues, and formatting problems before they hit CI.


📋 Requirements

📄 License

MIT — see LICENSE for details.

Metadata

Release files for loadout 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for loadout 0.5.0
File Size Uploaded Provenance
loadout-0.5.0.tar.gz 22.4 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for loadout 0.5.0
File Interpreter ABI Platform
loadout-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 43.8 kB

Release files / loadout-0.5.0.tar.gz

Download URL loadout-0.5.0.tar.gz
Size 22.4 kB
Tags Source
SHA-256 checksum
How to use checksums
3c5e419ec8ba63fc35015d0d375f814696ec27252fdd48df6c0a3487fad14d7f
BLAKE2b-256 checksum
How to use checksums
958d250bd679a4d0ea091906c408ae81b24a6b02d130995b013e6a81281e06c1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 17, 2026.

Transparency log

Release files / loadout-0.5.0-py3-none-any.whl

Download URL loadout-0.5.0-py3-none-any.whl
Size 21.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
22bb31a6b69046226d6b3acb23bc84602951aea23713841fb09935d785fc1bed
BLAKE2b-256 checksum
How to use checksums
c6ecea589ce95aae4e185b5b9454f05c24f6ad15e5150854437946e07f55bbac
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 17, 2026.

Transparency log

Security for loadout 0.5.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for loadout-0.5.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 17, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page