Warden can load skills from remote GitHub repositories, enabling skill sharing across projects.
Quick Start
Configure remote skill
[[ skills ]]
name = "code-simplifier"
remote = "getsentry/sentry-skills"
[[ skills . triggers ]]
type = "pull_request"
Run Warden
Warden automatically:
Clones the remote repository
Caches it locally
Discovers available skills
Loads the specified skill
Warden supports multiple remote reference formats:
Shorthand (GitHub)
Pinned to commit SHA
HTTPS URL
SSH URL (for private repos)
[[ 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
Unpinned Remotes (owner/repo)
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
Pinned Remotes (owner/repo@sha)
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:
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
Use SSH URL in config
[[ skills ]]
name = "internal-security-checks"
remote = "[email protected] :myorg/private-skills.git"
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 :
Root directory
skills/
.agents/skills/
.claude/skills/
.warden/skills/
First occurrence of a skill name wins.
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:
Organize skills in your repo
your-repo/
├── .agents/skills/
│ ├── your-skill-one/
│ │ └── SKILL.md
│ └── your-skill-two/
│ └── SKILL.md
└── README.md # Document available skills
Document usage in README
# 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:
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 protection Warden 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 production Use 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 collisions If 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