Skip to content

feat: add --plugins-dir flag to load plugins from custom directories#640

Draft
csmith49 wants to merge 5 commits intomainfrom
feature/plugins-dir-flag
Draft

feat: add --plugins-dir flag to load plugins from custom directories#640
csmith49 wants to merge 5 commits intomainfrom
feature/plugins-dir-flag

Conversation

@csmith49
Copy link
Copy Markdown
Contributor

@csmith49 csmith49 commented Apr 3, 2026

Summary

Add a --plugins-dir CLI flag similar to Claude Code's --plugin-dir flag, allowing users to load plugins (skills) from custom directories for the current session only.

Changes

New Features

  • New --plugins-dir flag (repeatable) to specify plugin directories
  • Plugins are loaded additively, supplementing default plugin locations (not replacing them)
  • Support for both specific plugin directories and directories containing multiple plugins
  • Graceful handling of nonexistent or invalid paths with warnings
  • Deduplication of skills with the same name

Usage Examples

openhands --plugins-dir ./my-plugin        # Load single plugin
openhands --plugins-dir ./plugins          # Load all plugins in directory  
openhands --plugins-dir ./p1 --plugins-dir ./p2  # Load multiple

Implementation Details

The flag is passed through the full call chain:

entrypoint -> textual_app -> runner_factory -> conversation_runner
  -> setup -> agent_store -> agent_context

New module openhands_cli/plugins.py handles loading skills from custom directories using the SDK's load_skills_from_dir function.

Testing

  • Added comprehensive tests in tests/test_plugins_dir.py:
    • CLI argument parsing tests
    • Plugin loading tests for various scenarios (empty dir, nonexistent path, valid skills, deduplication)

Verification Commands Run

make lint           # ✅ All checks passed
make test           # ✅ 1286 passed
uv run pytest tests/test_plugins_dir.py -v  # ✅ 9 passed

Help Output

--plugins-dir DIR     Load plugins (skills) from directories for this
                      session only (repeatable). Can specify a directory
                      containing plugins or a specific plugin directory.

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


🚀 Try this PR

uvx --python 3.12 git+https://github.com/OpenHands/OpenHands-CLI.git@feature/plugins-dir-flag

Add a --plugins-dir CLI flag similar to Claude Code's --plugin-dir flag,
allowing users to load plugins (skills) from custom directories for the
current session only.

Features:
- New --plugins-dir flag (repeatable) to specify plugin directories
- Plugins are loaded additively, supplementing default plugin locations
- Support for both specific plugin directories and directories containing
  multiple plugins
- Graceful handling of nonexistent or invalid paths with warnings
- Deduplication of skills with the same name

Usage examples:
  openhands --plugins-dir ./my-plugin        # Load single plugin
  openhands --plugins-dir ./plugins          # Load all plugins in directory
  openhands --plugins-dir ./p1 --plugins-dir ./p2  # Load multiple

The flag is passed through the full call chain:
  entrypoint -> textual_app -> runner_factory -> conversation_runner
  -> setup -> agent_store -> agent_context

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 3, 2026

Coverage

Coverage Report •
FileStmtsMissCoverMissing
openhands_cli
   entrypoint.py1234563%32, 52–53, 56, 59, 61–62, 64–65, 68, 70, 72, 76, 81, 83–84, 133, 135–136, 139–143, 146–147, 149, 151, 161, 163–165, 167, 169–171, 174, 176, 180, 189, 191–193, 210, 258
   plugins.py61886%110–112, 115, 132, 136–138
   setup.py45980%70–73, 78–81, 89
openhands_cli/argparsers
   main_parser.py330100% 
   util.py110100% 
openhands_cli/stores
   agent_store.py1902586%98, 116, 119, 158, 271–272, 275, 296, 437, 439–440, 442, 444, 486–487, 489, 496, 503, 505, 514, 517–518, 521–522, 524
openhands_cli/tui
   textual_app.py2174977%124, 263–264, 269, 272, 277, 288, 290–292, 312–313, 320, 328, 334, 338, 343–345, 383, 389, 446, 448, 480, 483, 498, 501, 503–504, 517–519, 521–527, 531, 539–540, 579, 581, 616, 637, 692–693, 723
openhands_cli/tui/core
   conversation_runner.py983960%100–102, 109, 111, 122, 128, 154, 161–162, 165–167, 177, 185, 187–190, 198, 201–202, 206, 213, 217–218, 223, 225, 231–232, 237, 239, 241, 248, 251, 256, 258, 270–271
   runner_factory.py300100% 
TOTAL670392186% 

Use the SDK's openhands.sdk.plugin.Plugin class instead of loading
skills directly. This properly supports the full plugin structure:

- Skills from plugins/skills/ directory
- MCP config from plugins/.mcp.json
- Hooks from plugins/hooks/hooks.json
- Agents from plugins/agents/ directory

The implementation now:
1. Checks for proper plugin manifest (.plugin/plugin.json or
   .claude-plugin/plugin.json) before loading
2. Uses Plugin.load() for single plugin directories
3. Uses Plugin.load_all() for directories containing multiple plugins
4. Merges plugin skills and MCP config into the agent

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
The mock for AgentStore.load_or_create in the snapshot tests was missing
the new plugins_dirs parameter, causing the iterative refinement tests
to fail.

Co-authored-by: openhands <openhands@all-hands.dev>
@csmith49 csmith49 self-assigned this Apr 3, 2026
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.

2 participants