Skip to content

Unify skills modules: move context/skills to sdk/skills#2774

Merged
csmith49 merged 4 commits intomainfrom
unify-skills-modules
Apr 9, 2026
Merged

Unify skills modules: move context/skills to sdk/skills#2774
csmith49 merged 4 commits intomainfrom
unify-skills-modules

Conversation

@csmith49
Copy link
Copy Markdown
Collaborator

@csmith49 csmith49 commented Apr 9, 2026

  • A human has tested these changes.

Why

The SDK currently has two separate skill modules:

  • openhands.sdk.context.skills - core skill model, loading, and trigger types
  • openhands.sdk.skills - skill fetch/install functionality

This split creates confusion about where skill-related code should live and makes the API harder to understand.

This PR addresses this problem by unifying all skills-related code to openhands.sdk.skills. The original imports still stand, but with a deprecation warning.

Summary

  • Move all skill implementation files from openhands/sdk/context/skills/ to openhands/sdk/skills/ as the canonical location
  • Create comprehensive exports in sdk/skills/__init__.py for all skill-related functionality
  • Add deprecation warnings for the old import path (openhands.sdk.context.skills) with deprecated_in="1.16.0" and removed_in="1.20.0"
  • Update all internal imports throughout the codebase to use the new canonical path

Issue Number

N/A - This is a codebase cleanup/refactoring task.

How to Test

The changes have been verified by running:

  1. Deprecation warning test:
>>> import warnings
>>> warnings.filterwarnings('always', category=DeprecationWarning)
>>> from openhands.sdk.context.skills import Skill
<stdin>:1: DeprecatedWarning: Importing from 'openhands.sdk.context.skills' is deprecated since 1.16.0 and will be removed in 1.20.0. Use 'openhands.sdk.skills' instead.
>>> Skill
<class 'openhands.sdk.skills.skill.Skill'>
  1. New import path works:
>>> from openhands.sdk.skills import Skill, load_skills_from_dir, KeywordTrigger
>>> Skill
<class 'openhands.sdk.skills.skill.Skill'>
  1. All skill-related tests pass (224 tests):
uv run pytest tests/sdk/context/skill/ tests/sdk/skills/ tests/sdk/subagent/test_subagent_registry.py -v
======================== 224 passed, 1 warning in 0.75s ========================

Video/Screenshots

N/A - This is a refactoring change verified through tests.

Type

  • Bug fix
  • Feature
  • Refactor
  • Breaking change
  • Docs / chore

Notes

Migration Guide

# Old (deprecated - emits warning):
from openhands.sdk.context.skills import Skill, load_skills_from_dir

# New (recommended):
from openhands.sdk.skills import Skill, load_skills_from_dir

Exports available from openhands.sdk.skills

  • Core classes: Skill, SkillResources, SkillInfo
  • Triggers: BaseTrigger, KeywordTrigger, TaskTrigger
  • Types: SkillKnowledge, InputMetadata, SkillResponse, SkillContentResponse
  • Loading functions: load_skills_from_dir, load_project_skills, load_user_skills, load_public_skills, load_available_skills
  • Installed skills: install_skill, uninstall_skill, list_installed_skills, load_installed_skills, get_installed_skill, enable_skill, disable_skill, update_skill, get_installed_skills_dir
  • Utilities: discover_skill_resources, validate_skill_name, to_prompt

This PR was created by an AI assistant (OpenHands) on behalf of the user.

@csmith49 can click here to continue refining the PR


Agent Server images for this PR

GHCR package: https://github.com/OpenHands/agent-sdk/pkgs/container/agent-server

Variants & Base Images

Variant Architectures Base Image Docs / Tags
java amd64, arm64 eclipse-temurin:17-jdk Link
python amd64, arm64 nikolaik/python-nodejs:python3.13-nodejs22-slim Link
golang amd64, arm64 golang:1.21-bookworm Link

Pull (multi-arch manifest)

# Each variant is a multi-arch manifest supporting both amd64 and arm64
docker pull ghcr.io/openhands/agent-server:ac22dda-python

Run

docker run -it --rm \
  -p 8000:8000 \
  --name agent-server-ac22dda-python \
  ghcr.io/openhands/agent-server:ac22dda-python

All tags pushed for this build

ghcr.io/openhands/agent-server:ac22dda-golang-amd64
ghcr.io/openhands/agent-server:ac22dda-golang_tag_1.21-bookworm-amd64
ghcr.io/openhands/agent-server:ac22dda-golang-arm64
ghcr.io/openhands/agent-server:ac22dda-golang_tag_1.21-bookworm-arm64
ghcr.io/openhands/agent-server:ac22dda-java-amd64
ghcr.io/openhands/agent-server:ac22dda-eclipse-temurin_tag_17-jdk-amd64
ghcr.io/openhands/agent-server:ac22dda-java-arm64
ghcr.io/openhands/agent-server:ac22dda-eclipse-temurin_tag_17-jdk-arm64
ghcr.io/openhands/agent-server:ac22dda-python-amd64
ghcr.io/openhands/agent-server:ac22dda-nikolaik_s_python-nodejs_tag_python3.13-nodejs22-slim-amd64
ghcr.io/openhands/agent-server:ac22dda-python-arm64
ghcr.io/openhands/agent-server:ac22dda-nikolaik_s_python-nodejs_tag_python3.13-nodejs22-slim-arm64
ghcr.io/openhands/agent-server:ac22dda-golang
ghcr.io/openhands/agent-server:ac22dda-java
ghcr.io/openhands/agent-server:ac22dda-python

About Multi-Architecture Support

  • Each variant tag (e.g., ac22dda-python) is a multi-arch manifest supporting both amd64 and arm64
  • Docker automatically pulls the correct architecture for your platform
  • Individual architecture tags (e.g., ac22dda-python-amd64) are also available if needed

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 9, 2026

Python API breakage checks — ✅ PASSED

Result:PASSED

Action log

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 9, 2026

REST API breakage checks (OpenAPI) — ✅ PASSED

Result:PASSED

Action log

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 9, 2026

Coverage

Coverage Report •
FileStmtsMissCoverMissing
openhands-agent-server/openhands/agent_server
   skills_service.py1333573%117, 151, 154–157, 160–162, 165–166, 169–173, 176–180, 182, 186–187, 189, 200–204, 341–342, 346–348
openhands-sdk/openhands/sdk
   __init__.py27292%95–96
openhands-sdk/openhands/sdk/context
   agent_context.py127695%276–278, 307, 330, 336
openhands-sdk/openhands/sdk/plugin
   plugin.py2101493%432–433, 450–451, 454–456, 474–476, 492–493, 511–512
   types.py2141991%59, 62, 65, 95–103, 106–107, 110, 163, 299, 640, 647
openhands-sdk/openhands/sdk/skills
   execute.py39489%92–95
   installed.py2604184%47, 54, 241–242, 249, 269–270, 276–277, 281, 284, 287, 324–328, 356–358, 362–364, 367, 371, 411, 439, 463, 478–479, 548–551, 553–554, 558–559, 564, 581–582
   skill.py4313392%95–96, 251–252, 438–444, 447, 564–567, 811–812, 871–872, 942–943, 1011, 1039, 1062, 1069–1070, 1114–1115, 1121–1122, 1128–1129
   utils.py1501987%44, 61, 99, 103, 130–131, 134, 149–150, 205, 218, 222, 250, 271, 321–323, 396–397
openhands-sdk/openhands/sdk/subagent
   registry.py130695%117, 242–243, 247, 293–294
TOTAL22903571475% 

@csmith49 csmith49 marked this pull request as ready for review April 9, 2026 20:05
Copy link
Copy Markdown
Collaborator

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Good taste - Clean module consolidation that eliminates confusion without breaking anything.

Analysis: This refactor solves a real problem (two separate skills modules creating API confusion) with the right approach: file consolidation + standard Python deprecation pattern. Backward compatibility is maintained perfectly via __getattr__, all internal imports are updated consistently, and the migration path is crystal clear.

Key insight: Sometimes the best refactor is the one that makes things simpler without causing pain. This is that refactor.

Verdict: Worth merging - solid engineering fundamentals, no issues found.

Copy link
Copy Markdown
Collaborator

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QA Report: Skills Module Unification

✅ PASS

This refactor successfully unifies the skills modules from openhands.sdk.context.skills to openhands.sdk.skills with full backward compatibility and proper deprecation warnings.


Environment Setup

✓ Build successful using make build

  • Dependencies installed via uv sync --dev
  • 232 packages installed
  • Pre-commit hooks configured

CI & Test Status

CI Checks: 18/27 passing (remaining are Docker builds in progress)

✓ Critical checks passing:

  • SDK tests
  • Agent server tests
  • Tools tests
  • Cross tests
  • Pre-commit checks
  • Python API breakage checks
  • REST API breakage checks

Test Suite: All 224 skills-related tests pass in 1.05s

uv run pytest tests/sdk/context/skill/ tests/sdk/skills/ tests/sdk/subagent/test_subagent_registry.py -v
# Result: 224 passed, 1 warning in 1.05s

Functional Verification

✓ Test 1: Old Import Path Shows Deprecation Warning

Command:

import warnings
warnings.filterwarnings('always', category=DeprecationWarning)
from openhands.sdk.context.skills import Skill, load_skills_from_dir, KeywordTrigger

Result: ✓ Proper deprecation warnings emitted:

DeprecatedWarning: Importing 'Skill' from 'openhands.sdk.context.skills' is deprecated as of 1.16.0 
and will be removed in 1.20.0. Use 'from openhands.sdk.skills import Skill' instead.

Verification:

  • Warning includes deprecation version (1.16.0)
  • Warning includes removal version (1.20.0)
  • Warning provides migration guidance
  • Objects correctly resolve to new location: openhands.sdk.skills.skill.Skill

✓ Test 2: New Import Path Works Without Warning

Command:

import warnings
warnings.filterwarnings('error', category=DeprecationWarning)  # Warnings become errors
from openhands.sdk.skills import Skill, load_skills_from_dir, KeywordTrigger, TaskTrigger
from openhands.sdk.skills import SkillResources, to_prompt, discover_skill_resources
from openhands.sdk.skills import load_project_skills, load_user_skills, load_public_skills

Result: ✓ All imports succeed with no warnings

  • Script uses warnings.filterwarnings('error') so any deprecation warning would raise an exception
  • All 10 imports work cleanly

✓ Test 3: Real Usage - Skills Loading Example

Command:

cd examples/05_skills_and_plugins/01_loading_agentskills
uv run python main.py

Result: ✓ Example runs successfully

  • Skills loaded using new import path from openhands.sdk.skills import
  • discover_skill_resources() found scripts, references, and assets
  • load_skills_from_dir() loaded 2 skills from directory
  • Skills correctly categorized as AgentSkills SKILL.md format
  • Skill metadata parsed correctly (name, description, license, compatibility, resources)
  • Agent initialized successfully with loaded skills

Output excerpt:

Loaded skills from directory:
  - Repo skills: []
  - Knowledge skills: []
  - Agent skills (SKILL.md): ['rot13-encryption', 'code-style-guide']

Discovered resources in rot13-encryption/:
  - scripts: ['encrypt.sh']
  - references: ['examples.md']
  - assets: []

✓ Test 4: Comprehensive Test Suite

Command:

uv run pytest tests/sdk/context/skill/ tests/sdk/skills/ tests/sdk/subagent/test_subagent_registry.py -v

Result: ✓ All 224 tests pass

  • Coverage includes: skill loading, validation, resource discovery, triggers, serialization, AgentSkills fields, project/user/public skills, MCP integration, subagent registry
  • Only 1 warning (unrelated deprecation for 'default' agent name)
  • Test execution time: 1.05s

Issues Found

None.


Verdict

PASS

The refactor successfully:

  1. Moves skills implementation to canonical location openhands.sdk.skills
  2. Maintains backward compatibility via deprecation shim
  3. Provides clear migration path with proper deprecation warnings
  4. Updates all internal imports to use new path
  5. Passes all tests with no regressions
  6. Works in real usage scenarios

Recommendation: Ready to merge.

Copy link
Copy Markdown
Collaborator

@enyst enyst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! Yes this makes more sense 😅

This change consolidates the two skill modules into a single canonical
location at openhands.sdk.skills.

Changes:
- Move skill implementation files from context/skills/ to sdk/skills/
- Create comprehensive exports in sdk/skills/__init__.py
- Add deprecation warnings for imports from context.skills (deprecated
  since 1.16.0, removal planned for 1.20.0)
- Update all internal imports to use new canonical path
- Update examples and tests to use new import paths
- Fix circular import by using direct submodule imports in plugin.py

The old import path (openhands.sdk.context.skills) still works but emits
deprecation warnings. Users should migrate to openhands.sdk.skills.

Migration guide:
  # Old (deprecated):
  from openhands.sdk.context.skills import Skill, load_skills_from_dir

  # New:
  from openhands.sdk.skills import Skill, load_skills_from_dir

Co-authored-by: openhands <openhands@all-hands.dev>
Update patch target from deprecated openhands.sdk.context.skills.skill
to new canonical path openhands.sdk.skills.skill

Co-authored-by: openhands <openhands@all-hands.dev>
Move tests from tests/sdk/context/skill/ to tests/sdk/skills/ to align
with the new canonical module location at openhands.sdk.skills.

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
@csmith49 csmith49 force-pushed the unify-skills-modules branch from 1df1877 to bd3fc64 Compare April 9, 2026 21:37
@csmith49 csmith49 merged commit 541e888 into main Apr 9, 2026
25 checks passed
@csmith49 csmith49 deleted the unify-skills-modules branch April 9, 2026 21:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants