Skip to main content
Warden can load skills from remote GitHub repositories, enabling skill sharing across projects.

Quick Start

1

Configure remote skill

warden.toml
[[skills]]
name = "code-simplifier"
remote = "getsentry/sentry-skills"

[[skills.triggers]]
type = "pull_request"
2

Run Warden

warden run
Warden automatically:
  • Clones the remote repository
  • Caches it locally
  • Discovers available skills
  • Loads the specified skill

Remote Formats

Warden supports multiple remote reference formats:
[[skills]]
name = "code-simplifier"
remote = "getsentry/sentry-skills"

Cache Behavior

Remote skills are cached locally to avoid repeated network calls:

Cache Location

  • Default: ~/.local/warden/skills/
  • Override: Set WARDEN_STATE_DIR environment variable
# Custom cache location
export WARDEN_STATE_DIR=/tmp/warden-cache
warden run

Cache Structure

~/.local/warden/skills/
├── getsentry/
│   ├── sentry-skills/              # Unpinned remote
│   └── sentry-skills@a1b2c3d4/     # Pinned to SHA
├── myorg/
│   └── shared-skills/
└── state.json                       # Metadata (SHAs, fetch times)

Refresh Behavior

TTL: 24 hours (default)Warden automatically fetches updates when:
  • Cache is older than TTL
  • Cache doesn’t exist
Override TTL:
# Set custom TTL (in seconds)
export WARDEN_SKILL_CACHE_TTL=3600  # 1 hour
warden run
Force refresh:
warden skills update getsentry/sentry-skills
TTL: Never expires (SHA is immutable)Pinned remotes are cached forever. Only refetched if:
  • Cache is manually deleted
  • Forced via --force flag
Use pinned remotes for:
  • Production environments
  • Reproducible CI builds
  • Avoiding unexpected skill changes

Private Repositories

For private repositories, use SSH URLs with configured SSH keys:
1

Set up SSH authentication

# Add your SSH key to GitHub
ssh-keygen -t ed25519 -C "[email protected]"
cat ~/.ssh/id_ed25519.pub  # Add to GitHub Settings > SSH Keys
2

Use SSH URL in config

warden.toml
[[skills]]
name = "internal-security-checks"
remote = "[email protected]:myorg/private-skills.git"
3

Run Warden

warden run
# Authenticates via SSH key
HTTPS URLs for private repositories will fail in non-interactive environments (CI) because Git cannot prompt for credentials. Always use SSH URLs for private repos in CI.

Remote Skill Discovery

Warden discovers skills in remote repositories using two methods:

Traditional Layout

Scans conventional directories:
remote-repo/
├── skills/                    # Found: skills/ subdirectory
│   ├── skill-one/
│   │   └── SKILL.md
│   └── skill-two/
│       └── SKILL.md
├── .agents/skills/            # Found: .agents/skills/
│   └── skill-three/
│       └── SKILL.md
└── .claude/skills/            # Found: .claude/skills/
    └── skill-four/
        └── SKILL.md
Search order:
  1. Root directory
  2. skills/
  3. .agents/skills/
  4. .claude/skills/
  5. .warden/skills/
First occurrence of a skill name wins.

Marketplace Format

For repositories with multiple plugins:
remote-repo/
├── .claude-plugin/
│   └── marketplace.json       # Marketplace manifest
└── plugins/
    ├── sentry-skills/
    │   └── skills/
    │       ├── code-simplifier/
    │       │   └── SKILL.md
    │       └── find-bugs/
    │           └── SKILL.md
    └── security-skills/
        └── skills/
            └── check-xss/
                └── SKILL.md
marketplace.json:
{
  "$schema": "https://skills.com/schema.json",
  "name": "Sentry Skills Collection",
  "plugins": [
    {
      "name": "sentry-skills",
      "source": "./plugins/sentry-skills",
      "description": "Code quality and bug detection"
    },
    {
      "name": "security-skills",
      "source": "./plugins/security-skills",
      "description": "Security analysis"
    }
  ]
}
Warden searches plugins/{plugin}/skills/ for each plugin defined.

Listing Available Skills

Discover what skills a remote provides:
# List skills in a remote
warden skills list getsentry/sentry-skills

Output:
# Available skills from getsentry/sentry-skills:
#  - code-simplifier: Simplify and refine code
#  - find-bugs: Detect common bug patterns
#  - security-review: Security vulnerability analysis

Managing Remote Cache

Update a Remote

Fetch latest changes:
# Update a specific remote
warden skills update getsentry/sentry-skills

# Update all cached remotes
warden skills update --all

Remove a Remote

Delete cached repository:
warden skills remove getsentry/sentry-skills

List Cached Remotes

See what’s cached locally:
warden skills list-cache

Output:
# Cached remotes:
# - getsentry/sentry-skills
#   SHA: a1b2c3d4e5f6
#   Fetched: 2026-03-05 10:30:00 UTC
#   Stale: No
# 
# - myorg/private-skills
#   SHA: f6e5d4c3b2a1
#   Fetched: 2026-03-01 08:15:00 UTC
#   Stale: Yes (>24h old)

Offline Mode

Use only cached skills without network calls:
# Run with cached skills only
warden run --offline

# Will fail if skill is not cached

Publishing Skills

Share your skills with others:
1

Organize skills in your repo

your-repo/
├── .agents/skills/
│   ├── your-skill-one/
│   │   └── SKILL.md
│   └── your-skill-two/
│       └── SKILL.md
└── README.md              # Document available skills
2

Document usage in README

README.md
# My Shared Skills

## Available Skills

- **your-skill-one**: Description of what it does
- **your-skill-two**: Description of what it does

## Usage

Add to your `warden.toml`:

```toml
[[skills]]
name = "your-skill-one"
remote = "yourorg/your-repo"
</Step>

<Step title="Create releases (optional)">
Tag releases for stable versions:

```bash
git tag v1.0.0
git push --tags
Users can pin to tags:
[[skills]]
name = "your-skill-one"
remote = "yourorg/[email protected]"

Implementation Details

Resolution Logic

From src/skills/loader.ts:487-493:
export async function resolveSkillAsync(
  nameOrPath: string,
  repoRoot?: string,
  options?: ResolveSkillOptions
): Promise<SkillDefinition> {
  // Resolution order:
  // 1. Remote repository (if remote option is set)
  // 2. Direct path (if nameOrPath contains / or \ or starts with .)
  // 3. Conventional directories (if repoRoot provided)
}

Fetch Logic

From src/skills/remote.ts:318-423:
export async function fetchRemote(
  ref: string,
  options: FetchRemoteOptions = {}
): Promise<string> {
  // 1. Check if cache is valid
  // 2. Clone if not cached
  // 3. Update if stale (unpinned only)
  // 4. Return SHA
}
Clone strategy:
  • Shallow clone (--depth=1) for speed
  • Pinned SHAs: Fetch specific commit, unshallow if needed
  • Unpinned: Clone default branch, pull updates when stale

Security

Path traversal protectionWarden validates remote refs to prevent:
  • Git flag injection (rejects refs starting with -)
  • Path traversal (sanitizes owner/repo names)
  • Malicious marketplace.json (ensures plugin paths stay within repo)
From src/skills/remote.ts:142-162:
// Security checks
if (owner.startsWith('-') || repo.startsWith('-')) {
  throw new SkillLoaderError('Invalid characters');
}

const safeNamePattern = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
if (!safeNamePattern.test(owner) || !safeNamePattern.test(repo)) {
  throw new SkillLoaderError('Invalid characters');
}

Best Practices

Pin remotes in productionUse SHA-pinned remotes in CI/production:
# Development: unpinned (get updates)
[[skills]]
name = "code-simplifier"
remote = "getsentry/sentry-skills"

# Production: pinned (stable)
[[skills]]
name = "code-simplifier"
remote = "getsentry/sentry-skills@a1b2c3d4"
Namespace collisionsIf you have a local skill with the same name as a remote skill, the local skill takes precedence (unless remote is explicitly specified in the config).

Troubleshooting

Skill Not Found

Error: Skill 'my-skill' not found in remote: getsentry/sentry-skills
Available skills: code-simplifier, find-bugs
Solution: Check skill name matches exactly. List available skills:
warden skills list getsentry/sentry-skills

Clone Failure (Private Repos)

Error: Failed to clone getsentry/private-skills via HTTPS.
For private repos, use SSH URL: [email protected]:getsentry/private-skills.git
Solution: Use SSH URL with configured SSH key.

Stale Cache

Warning: Cached remote getsentry/sentry-skills is stale (>24h old)
Solution: Force update:
warden skills update getsentry/sentry-skills

Next Steps

Creating Skills

Write skills to share with others

Configuration

Configure skill triggers and paths

Build docs developers (and LLMs) love