Skip to content

Conversation

@jpshackelford
Copy link
Contributor

@jpshackelford jpshackelford commented Jan 9, 2026

Summary

Plugin commands from the commands/ directory are now automatically converted to keyword-triggered skills and merged into the agent context.

This enables Claude Code format plugins that use commands/ (which is the standard location for slash commands) to work correctly with OpenHands - the agent now receives the command instructions as skills.

Changes

  • Add CommandDefinition.to_skill() method that converts a command to a Skill with a KeywordTrigger using Claude Code namespace format: /<plugin-name>:<command-name>
  • Add Plugin.get_all_skills() method that returns all skills from both skills/ and commands/ directories
  • Update _merge_plugin_into_request() to use get_all_skills() so commands are included when merging plugin content
  • Add comprehensive tests for the new functionality

Example

For a plugin named city-weather with a command now.md:

city-weather/
├── .claude-plugin/
│   └── plugin.json
└── commands/
    └── now.md          # Weather command instructions
  • Trigger keyword: /city-weather:now
  • When user types /city-weather:now Tokyo, the skill activates with $ARGUMENTS = Tokyo

Testing

  • Added unit tests for CommandDefinition.to_skill()
  • Added unit tests for Plugin.get_all_skills()
  • Added integration tests for _merge_plugin_into_request() with commands

All tests pass:

tests/sdk/plugin/test_plugin_loading.py - 15 passed
tests/agent_server/test_conversation_service.py::TestPluginLoading - 10 passed

Closes #1674

@jpshackelford 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.12-nodejs22 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:038f92e-python

Run

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

All tags pushed for this build

ghcr.io/openhands/agent-server:038f92e-golang-amd64
ghcr.io/openhands/agent-server:038f92e-golang_tag_1.21-bookworm-amd64
ghcr.io/openhands/agent-server:038f92e-golang-arm64
ghcr.io/openhands/agent-server:038f92e-golang_tag_1.21-bookworm-arm64
ghcr.io/openhands/agent-server:038f92e-java-amd64
ghcr.io/openhands/agent-server:038f92e-eclipse-temurin_tag_17-jdk-amd64
ghcr.io/openhands/agent-server:038f92e-java-arm64
ghcr.io/openhands/agent-server:038f92e-eclipse-temurin_tag_17-jdk-arm64
ghcr.io/openhands/agent-server:038f92e-python-amd64
ghcr.io/openhands/agent-server:038f92e-nikolaik_s_python-nodejs_tag_python3.12-nodejs22-amd64
ghcr.io/openhands/agent-server:038f92e-python-arm64
ghcr.io/openhands/agent-server:038f92e-nikolaik_s_python-nodejs_tag_python3.12-nodejs22-arm64
ghcr.io/openhands/agent-server:038f92e-golang
ghcr.io/openhands/agent-server:038f92e-java
ghcr.io/openhands/agent-server:038f92e-python

About Multi-Architecture Support

  • Each variant tag (e.g., 038f92e-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., 038f92e-python-amd64) are also available if needed

Implements #1645 - adds the ability to fetch plugins from remote sources
(GitHub repositories, git URLs) and cache them locally.

Changes:
- Add Plugin.fetch() classmethod to fetch from remote sources
- Add parse_plugin_source() to parse various source formats:
  - GitHub shorthand: 'github:owner/repo'
  - Git URLs: HTTPS, SSH, git:// protocol
  - Local paths (returned as-is)
- Add PluginFetchError exception for fetch failures
- Implement caching at ~/.openhands/cache/plugins/
- Support shallow clones for efficiency
- Support specific ref (branch/tag/commit) checkout
- Add comprehensive unit tests (34 new tests)

Co-authored-by: openhands <[email protected]>
- Add blank line after imports in fetch.py
- Remove unused import PluginFetchError in plugin.py
- Reformat long lines in test file

Co-authored-by: openhands <[email protected]>
- Add plugin_source and plugin_ref fields to StartConversationRequest
- Add _load_and_merge_plugin() method to fetch and load plugins
- Add _merge_plugin_into_request() method to merge plugin skills and MCP config
- Plugin skills override existing skills with the same name
- Plugin MCP config is merged with existing config
- Add comprehensive tests for plugin loading scenarios

Closes #1650
Plugin commands from the commands/ directory are now converted to
keyword-triggered skills and merged into the agent context.

Changes:
- Add CommandDefinition.to_skill() method that converts a command to
  a Skill with a KeywordTrigger using Claude Code namespace format:
  /<plugin-name>:<command-name>
- Add Plugin.get_all_skills() method that returns all skills from both
  skills/ and commands/ directories
- Update _merge_plugin_into_request() to use get_all_skills() so
  commands are included when merging plugin content

Example:
For a plugin 'city-weather' with command 'now.md':
- Trigger keyword: '/city-weather:now'
- When user types '/city-weather:now Tokyo', the skill activates

Closes #1674

Co-authored-by: openhands <[email protected]>
@github-actions
Copy link
Contributor

github-actions bot commented Jan 9, 2026

Coverage

Coverage Report •
FileStmtsMissCoverMissing
openhands-agent-server/openhands/agent_server
   conversation_service.py38225134%65, 68, 79–80, 83–86, 88, 92, 94, 97–104, 107–108, 111–115, 118–120, 122–125, 127, 134–135, 137–139, 142, 146, 148, 150, 157, 163, 171–172, 181–184, 193, 216–217, 219, 221–222, 228–229, 231, 239, 241, 243–244, 246, 267–268, 271, 274–276, 280–283, 286, 288, 290–291, 295, 297–298, 306–307, 309–311, 316–317, 323–325, 327, 336, 341–342, 345, 358–359, 375, 381, 384, 395–399, 401–404, 407–412, 415–418, 420–422, 425, 428–430, 435–438, 446, 451–453, 467–471, 474, 476, 479–481, 483, 487, 491, 498–502, 505–506, 510–514, 517–518, 522–526, 529–530, 536–541, 548–549, 553, 555–556, 561–562, 568–569, 575–577, 595, 619, 647, 649–650, 676, 678, 680–683, 688, 690–691, 695–696, 698–699, 702–704, 707, 713, 718–721, 728–729, 733–737, 739, 744, 748–750, 754–755, 757–759, 761, 763, 776–778, 781, 784, 787–790, 797–798, 802–804, 807–808, 810
   models.py1201488%56–57, 192–193, 195–197, 199, 203–204, 206, 212, 215, 217
openhands-sdk/openhands/sdk/plugin
   fetch.py1192182%90, 202, 216, 227–228, 243, 283, 307–310, 323, 326–327, 333–334, 337, 346–347, 353–354
   plugin.py1612981%206–207, 216–217, 244, 249–250, 282–284, 286–291, 307–310, 319–323, 339–340, 358–359
   types.py1001189%26–30, 117, 121, 197, 201, 206, 210
TOTAL15164447370% 

@openhands-ai
Copy link

openhands-ai bot commented Jan 9, 2026

Looks like there are a few issues preventing this PR from being merged!

  • GitHub Actions are failing:
    • Pre-commit checks

If you'd like me to help, just leave a comment, like

@OpenHands please fix the failing actions on PR #1676 at branch `feat/plugin-commands-as-skills`

Feel free to include any additional details that might help me get this PR into a better state.

You can manage your notification settings

- Fix line too long in log message
- Add None check for allowed_tools in test

Co-authored-by: openhands <[email protected]>
@jpshackelford jpshackelford changed the base branch from feat/agent-server-plugin-loading to main January 9, 2026 19:46
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.

Plugin commands should be loaded as keyword-triggered skills

3 participants