Skip to content

Feature: Automatic Skill Import from Remote Repositories #1044

@neubig

Description

@neubig

Automatic Skill Import from Remote Repositories

Problem Statement

Currently, OpenHands users must manually manage skills by:

  • Placing skill files in local directories (.openhands/skills/ or ~/.openhands/skills/)
  • Cloning entire repositories to access skill definitions
  • Copying skill files between projects

This manual approach creates several challenges:

  • No versioning: Users can't pin skills to specific versions or track updates
  • Difficult sharing: Organizations can't easily share and maintain common skills across teams
  • No discoverability: Users must know where skills exist before they can use them
  • Dependency management: No way to declare "this project requires these skills"
  • Update friction: Keeping skills up-to-date requires manual work

When starting a new project, developers want a declarative way to specify skill dependencies, similar to:

# .openhands/skills.yaml
skills:
  - source: github:OpenHands/software-agent-sdk
    version: 1.0.0
    path: examples/skills/python-best-practices
  - source: github:kubernetes/kubernetes
    version: 12.0.3
    path: .openhands/skills
  - source: local
    path: ./custom-skills

Current Implementation

How OpenHands Loads Skills Today

Skills are currently loaded from:

  1. Local project skills: .openhands/skills/ directory (relative to project root)
  2. User-level skills: ~/.openhands/skills/ or ~/.openhands/microagents/ (legacy)
  3. Third-party files: .cursorrules, agents.md, agent.md in project root
  4. Programmatic skills: Defined directly in code via Skill() constructor

Skills are markdown files with YAML frontmatter:

---
name: my-skill
triggers: [python, testing]
---
# Python Testing Skill

When writing Python tests, always...

See: openhands/sdk/context/skills/skill.py - load_skills_from_dir() and load_user_skills()

Skill Types

  • Repo skills (trigger=None): Always active, provide repository-specific context
  • Knowledge skills (KeywordTrigger): Activated when keywords appear in messages
  • Task skills (TaskTrigger): Activated for specific tasks, may require user input

Research: Similar Systems

1. Gemini CLI Extensions (Google) ⭐ Recommended Reference

Repository: https://github.com/google-gemini/gemini-cli
Documentation: https://geminicli.com/docs/extensions/

Gemini CLI has a mature, production-tested extension system that closely aligns with OpenHands' needs:

Key Architecture:

  • Extensions are directories with a manifest file (gemini-extension.json)
  • Can include context files (GEMINI.md), MCP servers, and custom commands
  • Installed via CLI with support for GitHub URLs and local paths
  • Automatic caching in ~/.gemini/extensions/
  • Symbolic linking for development workflows

Installation Examples:

# Install from GitHub with version
gemini extensions install https://github.com/org/repo --ref v1.0.0

# Install with auto-update enabled
gemini extensions install https://github.org/repo --auto-update

# Link local extension for development
gemini extensions link ./my-extension

# Update all extensions
gemini extensions update --all

Manifest Format (gemini-extension.json):

{
  "name": "my-extension",
  "version": "1.0.0",
  "contextFileName": "GEMINI.md",
  "mcpServers": {
    "my-server": {
      "command": "node",
      "args": ["${extensionPath}/server.js"],
      "cwd": "${extensionPath}"
    }
  },
  "excludeTools": ["tool_name"],
  "settings": [
    {
      "name": "API Key",
      "description": "Your API key for the service",
      "envVar": "MY_API_KEY",
      "sensitive": true
    }
  ]
}

Key Features:

  • Variable substitution: ${extensionPath}, ${workspacePath}, ${/} for OS-agnostic paths
  • Scoped enable/disable: Per-user or per-workspace activation
  • Settings management: Prompts users for required configuration (API keys, etc.)
  • Security: Consent prompts for extension installation
  • Conflict resolution: Extension commands get prefixed if conflicts occur
  • Custom commands: Extensions can provide their own slash commands
  • Browse/discover: Central gallery at https://geminicli.com/extensions/browse/

Why This Is Relevant:

  • Similar context-providing mechanism (GEMINI.md ≈ OpenHands skills)
  • Proven git-based installation workflow
  • Good developer experience with link/update commands
  • Handles versioning, caching, and updates elegantly
  • Scalable architecture used in production

2. Claude Skills (Anthropic)

Repository: https://github.com/anthropics/skills

Approach:

  • Skills stored in GitHub repositories with SKILL.md files
  • YAML frontmatter with name and description fields
  • Plugin marketplace system where repos can be registered
  • Installation via commands:
    /plugin marketplace add anthropics/skills
    /plugin install document-skills@anthropic-agent-skills
    

Key features:

  • GitHub-based distribution
  • Namespace/organization support (@anthropic-agent-skills)
  • Pre-built skills available in marketplace
  • Skills can include scripts, resources, and instructions
  • Simple markdown + frontmatter format

Reference:

3. Tessl Spec Registry

Website: https://tessl.io

Approach:

  • "Usage specs" treated as project dependencies
  • Specs describe how to use libraries/APIs correctly
  • Registry of 10,000+ usage specs for open-source libraries
  • Prevents common agent mistakes by providing structured guidance
  • Spec-driven development methodology

Philosophy: Specs as dependencies make more sense than ad-hoc seeking of online docs

4. AutoGPT Plugins

Repository: https://github.com/Significant-Gravitas/Auto-GPT-Plugins

Approach:

  • plugins_config.yaml for enabling/disabling plugins
  • Plugins must be downloaded and zipped into plugins directory
  • Configuration specifies allowlisted/denylisted plugins

Limitations: Manual download/installation required, no versioning system

5. LangChain

Approach:

  • Toolkit-based system (e.g., GitHubToolkit, SlackToolkit)
  • Installed via package managers (pip/npm)
  • MCP adapter support for Model Context Protocol servers
  • Configuration in code or via MCP config files

6. NPM/Package Managers

Approach:

  • Semantic versioning (semver) with git tags
  • Support for multiple dependency sources:
    {
      "dependencies": {
        "package": "1.0.0",                    // registry
        "repo-dep": "github:user/repo#v1.0",   // git + tag
        "local-dep": "file:../local-package"   // local path
      }
    }
  • Lock files for reproducibility
  • Transitive dependency resolution

Proposed Solution

Design Philosophy (Inspired by Gemini CLI)

We should adopt a CLI-first, extension-like approach rather than purely declarative configuration:

  1. Extensions/Skills as self-contained packages with manifest files
  2. Explicit installation commands rather than auto-installing from YAML
  3. Caching and lifecycle management handled by the SDK
  4. Developer-friendly workflows (link, update, list commands)
  5. Gradual adoption - works alongside existing skill loading

Phase 1: Core Extension System

Extension Manifest Format

Create openhands-skill.json (or .yaml) as the manifest for skill packages:

{
  "name": "python-testing",
  "version": "1.0.0",
  "description": "Python testing best practices and patterns",
  "skills": [
    {
      "file": "pytest.md",
      "type": "knowledge"
    },
    {
      "file": "unittest.md",
      "type": "knowledge"
    }
  ],
  "settings": [
    {
      "name": "Test Framework",
      "description": "Preferred testing framework",
      "envVar": "OH_TESTING_FRAMEWORK",
      "default": "pytest"
    }
  ],
  "mcp_servers": {
    "test-runner": {
      "command": "python",
      "args": ["${extensionPath}/mcp_server.py"]
    }
  }
}

CLI Commands

# Install a skill package from GitHub
openhands skills install github:OpenHands/openhands-skills --ref v1.0.0

# Install with auto-update
openhands skills install github:org/repo --auto-update

# Install from local path (for development)
openhands skills install ./my-skills

# Link local skills for development (symlink)
openhands skills link ./my-skills

# List installed skills
openhands skills list

# Update a specific skill package
openhands skills update python-testing

# Update all skills
openhands skills update --all

# Uninstall a skill package
openhands skills uninstall python-testing

# Enable/disable skills (per-user or per-workspace)
openhands skills disable python-testing --scope workspace
openhands skills enable python-testing --scope user

# Browse available skills (future: registry)
openhands skills browse

File Structure

~/.openhands/
├── skills/                    # User-local skills (existing, unchanged)
├── microagents/              # Legacy user skills (existing, unchanged)
└── skill-packages/           # Installed skill packages (new)
    ├── python-testing/
    │   ├── openhands-skill.json
    │   ├── pytest.md
    │   ├── unittest.md
    │   └── mcp_server.py
    └── kubernetes-ops/
        ├── openhands-skill.json
        └── skills/
            └── k8s.md

.openhands/
├── skills/                   # Project-local skills (existing, unchanged)
├── skill-packages/          # Project-specific skill packages (new)
│   └── company-standards -> ~/.openhands/skill-packages/company-standards
└── skills-config.json       # Installed packages metadata (new)

Skills Config File (Auto-generated)

Rather than requiring users to manually edit a YAML file, the CLI commands maintain a skills-config.json:

{
  "version": "1.0",
  "packages": [
    {
      "name": "python-testing",
      "source": "github:OpenHands/openhands-skills",
      "ref": "v1.0.0",
      "installed_at": "2025-01-15T10:30:00Z",
      "auto_update": false,
      "enabled": true,
      "scope": "user"
    },
    {
      "name": "company-standards",
      "source": "github:myorg/company-skills",
      "branch": "main",
      "auto_update": true,
      "enabled": true,
      "scope": "workspace"
    }
  ]
}

Phase 2: Implementation Details

1. Extension Resolver & Installer

Location: openhands/sdk/context/skills/extensions/

# openhands/sdk/context/skills/extensions/manifest.py
class SkillManifest(BaseModel):
    name: str
    version: str
    description: str
    skills: list[SkillFile]
    settings: list[SettingDefinition] | None = None
    mcp_servers: dict | None = None

class SkillFile(BaseModel):
    file: str  # Path relative to manifest
    type: Literal["repo", "knowledge", "task"] = "knowledge"

# openhands/sdk/context/skills/extensions/installer.py
class SkillPackageInstaller:
    def __init__(self, cache_dir: Path = Path.home() / ".openhands" / "skill-packages"):
        self.cache_dir = cache_dir
    
    def install(
        self,
        source: str,  # "github:org/repo" or local path
        ref: str | None = None,
        auto_update: bool = False,
    ) -> SkillPackage:
        """Install a skill package from source."""
        ...
    
    def link(self, local_path: Path) -> SkillPackage:
        """Create symlink for local development."""
        ...
    
    def update(self, package_name: str) -> SkillPackage:
        """Update a skill package to latest version."""
        ...
    
    def uninstall(self, package_name: str) -> None:
        """Uninstall a skill package."""
        ...
    
    def list(self) -> list[SkillPackage]:
        """List all installed skill packages."""
        ...

2. Git Operations

Use GitPython or subprocess for git operations:

# openhands/sdk/context/skills/extensions/git_utils.py
def clone_repo(url: str, dest: Path, ref: str | None = None) -> None:
    """Clone a git repository and checkout specific ref."""
    ...

def pull_repo(repo_path: Path) -> None:
    """Pull latest changes from git repository."""
    ...

def get_repo_info(repo_path: Path) -> RepoInfo:
    """Get current commit SHA, branch, etc."""
    ...

3. Integration with Existing Skill Loading

Modified: openhands/sdk/context/skills/skill.py

def load_all_skills(
    skill_dir: str | Path,
    load_packages: bool = True,  # New parameter
    scope: Literal["user", "workspace"] = "workspace",
) -> tuple[dict[str, Skill], dict[str, Skill]]:
    """Load skills from directory and installed packages."""
    repo_skills = {}
    knowledge_skills = {}
    
    # 1. Load traditional skills from .openhands/skills/
    local_repo, local_knowledge = load_skills_from_dir(skill_dir)
    repo_skills.update(local_repo)
    knowledge_skills.update(local_knowledge)
    
    # 2. Load skills from installed packages (if enabled)
    if load_packages:
        package_repo, package_knowledge = load_skills_from_packages(scope)
        # Local skills take precedence over package skills
        for name, skill in package_repo.items():
            if name not in repo_skills:
                repo_skills[name] = skill
        for name, skill in package_knowledge.items():
            if name not in knowledge_skills:
                knowledge_skills[name] = skill
    
    return repo_skills, knowledge_skills

def load_skills_from_packages(
    scope: Literal["user", "workspace"] = "workspace"
) -> tuple[dict[str, Skill], dict[str, Skill]]:
    """Load skills from installed skill packages."""
    config = load_skills_config()
    repo_skills = {}
    knowledge_skills = {}
    
    for package in config.packages:
        if not package.enabled:
            continue
        if package.scope != scope and package.scope != "user":
            continue
            
        package_path = get_package_path(package.name)
        manifest = load_manifest(package_path)
        
        for skill_file in manifest.skills:
            skill_path = package_path / skill_file.file
            skill = Skill.load(skill_path)
            skill.source = f"package:{package.name}"
            
            if skill.trigger is None:
                repo_skills[skill.name] = skill
            else:
                knowledge_skills[skill.name] = skill
    
    return repo_skills, knowledge_skills

Phase 3: CLI Implementation

CLI Tool

Create openhands/cli/skills.py (or extend existing CLI):

import typer
from openhands.sdk.context.skills.extensions import SkillPackageInstaller

app = typer.Typer(help="Manage OpenHands skill packages")

@app.command()
def install(
    source: str,
    ref: str | None = None,
    auto_update: bool = False,
    consent: bool = False,
):
    """Install a skill package."""
    ...

@app.command()
def link(path: str):
    """Link a local skill package for development."""
    ...

@app.command()
def update(name: str | None = None, all: bool = False):
    """Update skill package(s)."""
    ...

@app.command()
def uninstall(name: str):
    """Uninstall a skill package."""
    ...

@app.command()
def list():
    """List installed skill packages."""
    ...

@app.command()
def enable(name: str, scope: str = "user"):
    """Enable a skill package."""
    ...

@app.command()
def disable(name: str, scope: str = "user"):
    """Disable a skill package."""
    ...

Phase 4: Enhanced Features

1. Skill Package Registry

Future consideration: Central registry (similar to npm or Gemini CLI's gallery)

  • Browse available skill packages
  • Search by category, language, use case
  • Community ratings and downloads
  • Could leverage GitHub as initial backend

2. Variable Substitution

Support template variables in manifest and skill files:

  • ${extensionPath}: Absolute path to the skill package
  • ${workspacePath}: Current workspace path
  • ${/}: OS-appropriate path separator

3. Settings Management

Prompt users for required settings on installation:

  • API keys
  • Preferences
  • Configuration values

Store in .env file within skill package directory or use keychain for sensitive data.

4. Security & Validation

  • Consent prompts for installation (especially from GitHub)
  • Validate manifest schema
  • Optional: Checksums/signatures for packages
  • Sandboxing for MCP servers

5. Documentation & Examples

  • Template repository for creating skill packages
  • Best practices guide
  • Migration guide from standalone skills to packages
  • Example skill packages in OpenHands organization

Comparison: CLI-First vs Declarative YAML

Aspect CLI-First (Recommended) Declarative YAML
Installation Explicit commands Auto-install on project open
Discovery Browse/search commands Must know exact URLs
Version updates Explicit update commands Auto-update or manual edit
Development Link command for symlinks Edit YAML path
Debugging Clear command output Parse errors in YAML
Security Consent prompts per install Trust all in YAML
User control Explicit actions Implicit behavior
Precedent npm, Gemini CLI, VSCode requirements.txt, Cargo.toml

Recommendation: Start with CLI-first approach (like Gemini CLI), optionally add declarative config later for reproducibility (like skills.lock auto-generated file).

Migration Path

  1. Phase 1: Implement core extension system alongside existing skill loading
  2. Phase 2: No breaking changes - both systems work in parallel
  3. Phase 3: Add migration command: openhands skills migrate to convert local skills to packages
  4. Phase 4: Deprecation warnings (if needed), but keep backward compatibility

Backward Compatibility

  • ✅ Existing .openhands/skills/ directory continues to work
  • ~/.openhands/skills/ user skills continue to work
  • ✅ Local skills take precedence over package skills with same name
  • ✅ No breaking changes to Skill class or loading API
  • ✅ Skill packages are opt-in, not required

Benefits

  1. Reusability: Share skills across projects and teams
  2. Versioning: Pin skills to specific versions, track updates
  3. Discoverability: Browse and search available skill packages
  4. Maintenance: Update skills centrally, users pull updates
  5. Consistency: Ensure all team members use same skills/versions
  6. Developer Experience: Familiar CLI workflow (like npm, gem, cargo)
  7. Composability: Build on community skills, reduce duplication
  8. Security: Explicit consent for installations

Success Metrics

  • Number of projects using skill packages
  • Number of skill packages published
  • Community adoption rate
  • Reduction in duplicate skill definitions
  • User satisfaction scores

Open Questions

  1. Registry hosting: Should we create a central registry or rely on GitHub initially?
  2. Authentication: How to handle private repositories? Git credentials? SSH keys?
  3. Transitive dependencies: Should skill packages depend on other packages?
  4. Versioning scheme: Strict semver or allow other schemes?
  5. Namespace conflicts: How to resolve if two packages provide skills with same name?
  6. Performance: How to optimize loading time with many packages?
  7. Offline support: How to work when network is unavailable?
  8. MCP server lifecycle: Should skill packages manage MCP server processes?

Implementation Checklist

Phase 1: Core System (MVP)

  • Define SkillManifest schema
  • Implement SkillPackageInstaller class
  • Add git clone/pull utilities
  • Implement basic CLI commands (install, list, uninstall)
  • Integrate with existing skill loading
  • Add configuration file management

Phase 2: Developer Experience

  • Implement link command for local development
  • Add update command and auto-update logic
  • Implement enable/disable functionality
  • Add workspace vs user scope support
  • Variable substitution in manifests

Phase 3: Enhanced Features

  • Settings management (prompts on install)
  • Security: consent prompts
  • MCP server integration
  • Documentation and templates
  • Example skill packages

Phase 4: Registry & Discovery

  • Skill package registry (GitHub-based or custom)
  • Browse/search functionality
  • Community gallery
  • Package analytics

References

Related Issues

  • N/A (new feature request)

CC

@enyst @smithtim @Himanshu-Singh-Chauhan @amanape

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions