Skills API¶
Nexus provides a comprehensive skills system for managing AI agent capabilities. Skills are markdown-based knowledge units that can be discovered, versioned, shared, and exported across different tiers (agent, tenant, system).
Overview¶
The Skills API provides: - Progressive disclosure: Load metadata first, full content on-demand - Lazy loading: Load skills only when needed - Three-tier hierarchy: agent > tenant > system (with priority override) - Dependency resolution: Automatic DAG resolution with cycle detection - Version control: Leverages Nexus CAS for content deduplication - Export/Import: Package skills for distribution - Search: Text-based and semantic search capabilities
Core Concepts¶
Skill Structure¶
Skills are stored as SKILL.md files with YAML frontmatter:
---
name: analyze-code
description: Analyzes code quality and suggests improvements
version: 1.0.0
author: Alice
created_at: 2025-01-15T10:00:00Z
modified_at: 2025-01-20T15:30:00Z
requires:
- base-parser
- ast-tools
---
# Code Analysis Skill
This skill analyzes code for quality issues...
## Usage
[Skill content in markdown format]
Tier Hierarchy¶
Skills are organized in three tiers with priority ordering:
/workspace/.nexus/skills/ # Agent tier (highest priority)
/shared/skills/ # Tenant tier (shared across team)
/system/skills/ # System tier (global/built-in)
When a skill exists in multiple tiers, the higher priority tier wins: - Agent skills override tenant skills - Tenant skills override system skills
SkillRegistry¶
The SkillRegistry is the primary interface for discovering and loading skills.
Basic Usage¶
from nexus import connect
from nexus.skills import SkillRegistry
nx = connect()
registry = SkillRegistry(nx)
# Discover skills (loads metadata only)
count = await registry.discover()
print(f"Discovered {count} skills")
# Get a skill (loads full content on-demand)
skill = await registry.get_skill("analyze-code")
print(skill.content) # Full markdown content
# List available skills
skill_names = registry.list_skills()
print(f"Available skills: {skill_names}")
# Get metadata without loading full content
metadata = registry.get_metadata("analyze-code")
print(f"{metadata.name}: {metadata.description}")
Discovery¶
Discovery loads only metadata for fast startup:
# Discover from all tiers
count = await registry.discover()
# Discover from specific tiers
count = await registry.discover(tiers=["agent", "tenant"])
# Discover from agent tier only
count = await registry.discover(tiers=["agent"])
Loading Skills¶
Skills are loaded lazily - full content is only loaded when requested:
# Load a skill (loads content + caches it)
skill = await registry.get_skill("analyze-code")
# Access metadata
print(skill.metadata.name)
print(skill.metadata.description)
print(skill.metadata.version)
print(skill.metadata.author)
# Access full content
print(skill.content) # Markdown content
# Load with dependencies
skill = await registry.get_skill("analyze-code", load_dependencies=True)
Listing Skills¶
# List all skills (returns names)
skill_names = registry.list_skills()
# List skills from specific tier
agent_skills = registry.list_skills(tier="agent")
# List with metadata (returns SkillMetadata objects)
metadata_list = registry.list_skills(include_metadata=True)
for metadata in metadata_list:
print(f"{metadata.name} v{metadata.version}")
print(f" {metadata.description}")
print(f" Tier: {metadata.tier}")
Dependency Resolution¶
Resolve dependencies in correct order (dependencies first):
# Resolve all dependencies for a skill
deps = await registry.resolve_dependencies("analyze-code")
# Returns: ['base-parser', 'ast-tools', 'analyze-code']
# Load skills in dependency order
for skill_name in deps:
skill = await registry.get_skill(skill_name)
print(f"Loading: {skill_name}")
Dependency resolution detects circular dependencies:
try:
deps = await registry.resolve_dependencies("skill-with-cycle")
except SkillDependencyError as e:
print(f"Circular dependency: {e}")
# Error: Circular dependency detected: skill-a -> skill-b -> skill-c -> skill-a
Caching¶
The registry caches loaded skills for performance:
# Clear skill cache (keeps metadata)
registry.clear_cache()
# Clear everything (metadata + cache)
registry.clear()
# Check registry state
print(registry)
# Output: SkillRegistry(skills=10, cached=3, tiers=['agent', 'tenant'])
SkillManager¶
The SkillManager handles skill lifecycle operations: create, fork, publish.
Creating Skills¶
Create new skills from templates:
from nexus.skills import SkillManager
manager = SkillManager(nx)
# Create skill from template
path = await manager.create_skill(
name="my-analyzer",
description="Analyzes code quality",
template="code-generation",
tier="agent",
author="Alice",
version="1.0.0"
)
print(f"Created skill at: {path}")
# Available templates:
# - basic: Simple skill template
# - data-analysis: Data analysis skill
# - code-generation: Code generation skill
# - etc.
Create skills from custom content:
# Create from scraped/custom content
path = await manager.create_skill_from_content(
name="stripe-api",
description="Stripe API Documentation",
content="# Stripe API\n\nComplete API reference...",
tier="agent",
author="Auto-generated",
source_url="https://docs.stripe.com/api",
metadata={
"category": "api-docs",
"provider": "stripe"
}
)
Forking Skills¶
Fork existing skills with lineage tracking:
# Fork a skill
path = await manager.fork_skill(
source_name="analyze-code",
target_name="my-code-analyzer",
tier="agent",
author="Bob"
)
print(f"Forked skill at: {path}")
# The forked skill will include:
# - New name
# - Updated metadata (forked_from, parent_skill)
# - Incremented version
# - New timestamps
Publishing Skills¶
Publish skills from one tier to another:
# Publish agent skill to tenant library
path = await manager.publish_skill(
name="my-analyzer",
source_tier="agent",
target_tier="tenant"
)
print(f"Published to: {path}")
# Publish tenant skill to system library
path = await manager.publish_skill(
name="shared-analyzer",
source_tier="tenant",
target_tier="system"
)
Searching Skills¶
Simple text-based search across skill descriptions:
# Search for skills
results = await manager.search_skills("code analysis")
for skill_name, score in results:
print(f"{skill_name}: {score:.2f}")
# Search in specific tier
results = await manager.search_skills(
query="data processing",
tier="tenant",
limit=5
)
# For semantic search, use Nexus semantic_search API
# (requires semantic search to be initialized)
SkillExporter¶
Export skills to various formats for distribution.
Exporting Skills¶
from nexus.skills import SkillExporter
exporter = SkillExporter(registry)
# Export skill to zip file
await exporter.export_skill(
skill_name="analyze-code",
output_path="/tmp/analyze-code.zip",
format="generic", # or "claude", "openai"
include_dependencies=True # Include required skills
)
# Export for Claude API (8MB limit enforced)
await exporter.export_skill(
skill_name="my-skill",
output_path="/tmp/my-skill.zip",
format="claude"
)
# Export to directory (not zip)
await exporter.export_skill(
skill_name="my-skill",
output_path="/tmp/my-skill/",
format="generic"
)
Export Formats¶
Generic Format:
skill-name.zip
├── SKILL.md # Main skill file
├── manifest.json # Export metadata
└── deps/ # Dependencies (if included)
├── dep1/
│ └── SKILL.md
└── dep2/
└── SKILL.md
Claude Format:
skill-name.zip
├── skill-name/
│ ├── SKILL.md # Filtered frontmatter (only Claude-allowed fields)
│ └── manifest.json
└── [dependencies if needed]
Import Skills¶
Import skills from exported packages:
# Import from zip file
await importer.import_skill(
source_path="/tmp/analyze-code.zip",
tier="agent"
)
# Import from directory
await importer.import_skill(
source_path="/tmp/my-skill/",
tier="tenant"
)
Data Models¶
SkillMetadata¶
Lightweight metadata loaded during discovery:
@dataclass
class SkillMetadata:
name: str # Skill name (required)
description: str # Description (required)
version: str | None # Semantic version
author: str | None # Author name
created_at: datetime | None # Creation timestamp
modified_at: datetime | None # Last modified timestamp
requires: list[str] # Skill dependencies
metadata: dict[str, Any] # Additional metadata
file_path: str | None # Path to SKILL.md
tier: str | None # agent, tenant, or system
Skill¶
Complete skill with metadata and content:
@dataclass
class Skill:
metadata: SkillMetadata # Lightweight metadata
content: str # Full markdown content
def validate(self) -> None:
"""Validate skill structure."""
self.metadata.validate()
if not self.content:
raise ValidationError("skill content is required")
Complete Example¶
Here's a complete example using the Skills API:
from nexus import connect
from nexus.skills import SkillRegistry, SkillManager, SkillExporter
# Connect to Nexus
nx = connect()
# === Discovery ===
registry = SkillRegistry(nx)
count = await registry.discover()
print(f"Discovered {count} skills")
# === List Skills ===
skill_names = registry.list_skills()
print(f"Available skills: {', '.join(skill_names)}")
# === Get Skill Details ===
metadata = registry.get_metadata("analyze-code")
print(f"\nSkill: {metadata.name}")
print(f"Description: {metadata.description}")
print(f"Version: {metadata.version}")
print(f"Dependencies: {metadata.requires}")
# === Load Skill Content ===
skill = await registry.get_skill("analyze-code")
print(f"\nContent preview: {skill.content[:200]}...")
# === Resolve Dependencies ===
deps = await registry.resolve_dependencies("analyze-code")
print(f"\nDependency order: {' -> '.join(deps)}")
# === Create New Skill ===
manager = SkillManager(nx, registry)
new_skill_path = await manager.create_skill(
name="my-analyzer",
description="Custom code analyzer",
template="code-generation",
tier="agent",
author="Alice"
)
print(f"\nCreated new skill: {new_skill_path}")
# === Fork Existing Skill ===
forked_path = await manager.fork_skill(
source_name="analyze-code",
target_name="alice-analyzer",
tier="agent",
author="Alice"
)
print(f"Forked skill: {forked_path}")
# === Search Skills ===
results = await manager.search_skills("code analysis", limit=5)
print("\nSearch results:")
for skill_name, score in results:
print(f" {skill_name}: {score:.2f}")
# === Export Skill ===
exporter = SkillExporter(registry)
export_path = "/tmp/my-analyzer.zip"
await exporter.export_skill(
skill_name="my-analyzer",
output_path=export_path,
format="generic",
include_dependencies=True
)
print(f"\nExported to: {export_path}")
# === Publish to Team ===
published_path = await manager.publish_skill(
name="my-analyzer",
source_tier="agent",
target_tier="tenant"
)
print(f"Published to team: {published_path}")
CLI Integration¶
The Skills API is also available via CLI:
# Create a skill
nexus skills create my-skill --description "My skill" --template basic
# List skills
nexus skills list
nexus skills list --tier agent
# Show skill info
nexus skills info analyze-code
# Fork a skill
nexus skills fork analyze-code my-analyzer
# Publish a skill
nexus skills publish my-skill --target-tier tenant
# Export a skill
nexus skills export my-skill --output /tmp/my-skill.zip
# Search skills
nexus skills search "code analysis"
Integration with Plugins¶
Skills work seamlessly with plugins. For example, the Anthropic plugin:
# Upload skill to Claude Skills API
nexus anthropic upload-skill my-skill
# Download skill from Claude Skills API
nexus anthropic download-skill skill_01AbCdEfGhIjKlMnOpQrStUv
# List Claude skills
nexus anthropic list-skills
# Import from GitHub (anthropics/skills repo)
nexus anthropic browse-github
nexus anthropic import-github project-manager
Advanced Usage¶
Custom Filesystem¶
Use skills with any filesystem implementation:
from nexus.skills import SkillRegistry
# Use with remote Nexus server
nx = connect(remote_url="http://localhost:2026")
registry = SkillRegistry(nx)
# Use with GCS backend
nx = connect(backend="gcs", gcs_bucket="my-bucket")
registry = SkillRegistry(nx)
# Use without filesystem (local files only)
registry = SkillRegistry(filesystem=None)
Manual Parsing¶
Parse skills manually using the parser:
from nexus.skills import SkillParser
parser = SkillParser()
# Parse from file
skill = parser.parse_file("/path/to/SKILL.md", tier="agent")
# Parse from content string
content = """---
name: my-skill
description: My skill
---
# Content here
"""
skill = parser.parse_content(content, file_path="/virtual/path", tier="agent")
# Parse metadata only
metadata = parser.parse_metadata_only("/path/to/SKILL.md", tier="agent")
Validation¶
Validate skills and metadata:
from nexus.skills import Skill, SkillMetadata
from nexus.core.exceptions import ValidationError
metadata = SkillMetadata(
name="test-skill",
description="Test skill",
tier="agent"
)
try:
metadata.validate()
except ValidationError as e:
print(f"Validation error: {e}")
skill = Skill(metadata=metadata, content="# Skill content")
skill.validate()
Governance and Analytics¶
Track skill usage and governance:
from nexus.skills import SkillGovernance, SkillAnalytics
# Track skill usage
governance = SkillGovernance(registry)
await governance.track_usage(
skill_name="analyze-code",
user="alice",
action="load"
)
# Get usage analytics
analytics = SkillAnalytics(registry)
stats = await analytics.get_skill_stats("analyze-code")
print(f"Usage count: {stats['usage_count']}")
print(f"Last used: {stats['last_used']}")
# Get popular skills
popular = await analytics.get_popular_skills(limit=10)
for skill_name, usage_count in popular:
print(f"{skill_name}: {usage_count} uses")
Approval Workflow¶
Skills can be submitted for approval before publishing to tenant tier:
from nexus.skills import SkillGovernance
# Initialize with database connection
governance = SkillGovernance(db_connection=db_conn, rebac_manager=rebac)
# Submit skill for approval
approval_id = await governance.submit_for_approval(
skill_name="my-analyzer",
submitted_by="alice",
reviewers=["bob", "charlie"],
comments="Ready for team-wide use"
)
print(f"Submitted for approval: {approval_id}")
# List pending approvals (as reviewer)
pending = await governance.list_approvals(status="pending")
for approval in pending:
print(f"{approval.skill_name} by {approval.submitted_by}")
# Approve skill
await governance.approve_skill(
approval_id=approval_id,
reviewed_by="bob",
reviewer_type="user",
comments="Code quality looks excellent!"
)
# Or reject skill
await governance.reject_skill(
approval_id=approval_id,
reviewed_by="bob",
reviewer_type="user",
comments="Needs more input validation"
)
# Check if skill is approved
is_approved = await governance.is_approved("my-analyzer")
if is_approved:
# Publish to tenant tier
await manager.publish_skill("my-analyzer", tier="tenant")
CLI Commands¶
The approval workflow is also available via CLI:
# Submit skill for approval
nexus skills submit-approval my-analyzer \
--submitted-by alice \
--reviewers bob,charlie \
--comments "Ready for team use"
# List pending approvals
nexus skills list-approvals --status pending
# Approve skill
nexus skills approve <approval-id> \
--reviewed-by bob \
--comments "Excellent work!"
# Reject skill
nexus skills reject <approval-id> \
--reviewed-by bob \
--comments "Needs improvements"
Database Setup¶
The approval workflow requires a database connection:
# Set database URL
export NEXUS_DATABASE_URL="postgresql://user:pass@localhost/nexus"
# Run migrations to create skill_approvals table
alembic upgrade head
ReBAC Integration¶
When ReBAC is enabled, the approval workflow checks permissions:
- Submitters need
writepermission on agent tier skills - Reviewers need
approvepermission on skills - Only approved skills can be published to tenant tier
Error Handling¶
Handle common errors gracefully:
from nexus.skills import (
SkillRegistry,
SkillNotFoundError,
SkillDependencyError,
SkillManagerError,
SkillParseError
)
registry = SkillRegistry(nx)
await registry.discover()
try:
# Try to get a skill
skill = await registry.get_skill("non-existent")
except SkillNotFoundError as e:
print(f"Skill not found: {e}")
try:
# Try to resolve dependencies
deps = await registry.resolve_dependencies("skill-with-cycle")
except SkillDependencyError as e:
print(f"Dependency error: {e}")
try:
# Try to create a skill
manager = SkillManager(nx)
await manager.create_skill("invalid name!", "Description")
except SkillManagerError as e:
print(f"Manager error: {e}")
try:
# Try to parse invalid skill
from nexus.skills import SkillParser
parser = SkillParser()
skill = parser.parse_file("invalid.md", "agent")
except SkillParseError as e:
print(f"Parse error: {e}")
Best Practices¶
1. Progressive Discovery¶
Always discover before accessing skills:
registry = SkillRegistry(nx)
await registry.discover() # Load metadata
# Now access skills
skill = await registry.get_skill("my-skill") # Load content on-demand
2. Dependency Management¶
Declare dependencies in skill frontmatter:
---
name: advanced-analyzer
description: Advanced code analysis
requires:
- base-parser # Base dependency
- ast-tools # Another dependency
---
Load skills with dependencies:
# Resolve and load in correct order
deps = await registry.resolve_dependencies("advanced-analyzer")
for skill_name in deps:
skill = await registry.get_skill(skill_name)
# Use skill...
3. Tier Organization¶
Organize skills by visibility:
- Agent tier (
/workspace/.nexus/skills/): Personal skills, experiments - Tenant tier (
/shared/skills/): Team-shared skills, approved skills - System tier (
/system/skills/): Global skills, built-in capabilities
4. Version Management¶
Use semantic versioning:
Leverage Nexus version control:
# Skills are versioned automatically via Nexus CAS
versions = nx.list_versions("/workspace/.nexus/skills/my-skill/SKILL.md")
old_content = nx.get_version("/workspace/.nexus/skills/my-skill/SKILL.md", version=1)
5. Export and Share¶
Export skills with dependencies for distribution:
exporter = SkillExporter(registry)
await exporter.export_skill(
skill_name="my-skill",
output_path="/tmp/my-skill.zip",
format="generic",
include_dependencies=True # Include all required skills
)
Troubleshooting¶
Skill Not Found¶
# Ensure discovery has been run
await registry.discover()
# Check which tiers were discovered
print(registry) # Shows tiers
# List skills to verify
skills = registry.list_skills()
print(f"Available: {skills}")
# Check specific tier
agent_skills = registry.list_skills(tier="agent")
Invalid Skill Format¶
# Validate SKILL.md format
from nexus.skills import SkillParser
parser = SkillParser()
try:
skill = parser.parse_file("/path/to/SKILL.md", "agent")
skill.validate()
except SkillParseError as e:
print(f"Parse error: {e}")
Circular Dependencies¶
# Detect circular dependencies
try:
deps = await registry.resolve_dependencies("my-skill")
except SkillDependencyError as e:
print(f"Circular dependency: {e}")
# Fix: Update skill frontmatter to remove circular dependency
Export Size Limit¶
# Claude format has 8MB limit
try:
await exporter.export_skill(
"large-skill",
"/tmp/skill.zip",
format="claude"
)
except ValidationError as e:
print(f"Export too large: {e}")
# Fix: Export without dependencies or use generic format
See Also¶
- Plugins API - Plugin system
- CLI Reference - Skills CLI commands
- Semantic Search - Search skills semantically
- Versioning - Version control system
Next Steps¶
- Discover and explore available skills
- Create your first skill using templates
- Fork and customize existing skills
- Share skills with your team via publishing
- Integrate with plugins (e.g., Anthropic plugin)