-
Notifications
You must be signed in to change notification settings - Fork 39
Description
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-skillsCurrent Implementation
How OpenHands Loads Skills Today
Skills are currently loaded from:
- Local project skills:
.openhands/skills/directory (relative to project root) - User-level skills:
~/.openhands/skills/or~/.openhands/microagents/(legacy) - Third-party files:
.cursorrules,agents.md,agent.mdin project root - 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 --allManifest 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.mdfiles - YAML frontmatter with
nameanddescriptionfields - 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.yamlfor 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:
- Extensions/Skills as self-contained packages with manifest files
- Explicit installation commands rather than auto-installing from YAML
- Caching and lifecycle management handled by the SDK
- Developer-friendly workflows (link, update, list commands)
- 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 browseFile 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_skillsPhase 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
- Phase 1: Implement core extension system alongside existing skill loading
- Phase 2: No breaking changes - both systems work in parallel
- Phase 3: Add migration command:
openhands skills migrateto convert local skills to packages - 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
Skillclass or loading API - ✅ Skill packages are opt-in, not required
Benefits
- Reusability: Share skills across projects and teams
- Versioning: Pin skills to specific versions, track updates
- Discoverability: Browse and search available skill packages
- Maintenance: Update skills centrally, users pull updates
- Consistency: Ensure all team members use same skills/versions
- Developer Experience: Familiar CLI workflow (like npm, gem, cargo)
- Composability: Build on community skills, reduce duplication
- 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
- Registry hosting: Should we create a central registry or rely on GitHub initially?
- Authentication: How to handle private repositories? Git credentials? SSH keys?
- Transitive dependencies: Should skill packages depend on other packages?
- Versioning scheme: Strict semver or allow other schemes?
- Namespace conflicts: How to resolve if two packages provide skills with same name?
- Performance: How to optimize loading time with many packages?
- Offline support: How to work when network is unavailable?
- MCP server lifecycle: Should skill packages manage MCP server processes?
Implementation Checklist
Phase 1: Core System (MVP)
- Define
SkillManifestschema - Implement
SkillPackageInstallerclass - 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
linkcommand for local development - Add
updatecommand 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
- Gemini CLI Extensions: https://geminicli.com/docs/extensions/ (Primary reference)
- Gemini CLI GitHub: https://github.com/google-gemini/gemini-cli
- Claude Skills: https://github.com/anthropics/skills
- Tessl: https://tessl.io
- OpenHands Skills:
openhands/sdk/context/skills/ - AutoGPT Plugins: https://github.com/Significant-Gravitas/Auto-GPT-Plugins
- LangChain MCP: https://github.com/langchain-ai/langchain-mcp-adapters
Related Issues
- N/A (new feature request)