Repository navigation
Add plugin-with-script example - #23
Conversation
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
Docs preview✅ Docs preview is ready: https://allhandsai-cookbook-preview-pr-23.mintlify.site
Docs PR: OpenHands/docs#919 (draft preview; never merged, closes with this PR). |
Test evidence: the agent runs the bundled scriptI started two conversations on our OpenHands Enterprise Beta instance, loading the plugin from this branch: python load-plugin/load_plugin.py --ref plugin-with-script \
--repo-path plugin-with-script/city-weather --message "<message>"They ran at
In both, the agent built the script's absolute path from the Event trace:
|
Test results on OpenHands Enterprise Beta, with the plugin pinned to this PR's headI ran three conversations on our OpenHands Enterprise Beta instance through its public REST API. Each loaded the plugin with Versions: OpenHands app Request ( {
"plugins": [{
"source": "github:OpenHands/enterprise-cookbook",
"ref": "ce056661b25d3b79ec9e1d6d2a6ccc4f2fbf7403",
"repo_path": "plugin-with-script/city-weather",
"parameters": {"city": "Tokyo"}
}],
"initial_message": {"role": "user", "content": [{"type": "text", "text": "/city-weather:now Tokyo"}]}
}The first case is the "Get the weather in Tokyo" badge's plugin spec and message, with All three cases behaved as expected.
The error path is a deliberate negative test. For an unknown city, Every case ran the script at its absolute path in the plugin cache. The path was built from the python3 /home/openhands/.openhands/cache/plugins/enterprise-cookbook-3c5c88fa6e51c50a/plugin-with-script/city-weather/skills/city-weather/scripts/weather.py "Tokyo"Event-log verification: the script really ran and the agent didn't invent the answerI pulled each conversation's full event log from the agent server (
All three logs have the same event order: user message → terminal action → terminal result → finish action → finish result. The model issued the script command before any weather data existed in the conversation. The data first appears in the sandbox's result event, and the next model response only repeats it. So the reports came from the bundled script, not from the model. Trimmed event log: badge payload (
|
There was a problem hiding this comment.
This is a well-executed example that fills a real documentation gap. The plugin demonstrates how to bundle and reference scripts in a way that works across Claude Code and OpenHands plugin systems.
Structure & Documentation
- README follows STYLEGUIDE.md precisely: proper sections, Title Case headings, tabs for alternatives, cards for Related
- Embedded code block (line 139) matches
city-weather/commands/now.mdexactly - Launch badges correctly use
source: github:OpenHands/enterprise-cookbook,ref: main - Added to root README in the correct category position
Code Quality
weather.pyis clean: standard library only, proper error handling, Python 3.10+ syntax- No security concerns: uses free public API, no credentials required
- Plugin structure follows Claude Code layout with OpenHands-specific path resolution explained
Educational Value
- Clearly explains the
Skill location:mechanism and why${CLAUDE_SKILL_DIR}doesn't work in OpenHands - Shows both command and skill patterns referencing the same script
- Documents the hook limitation (can't reference bundled scripts) with rationale
Validation Evidence (from PR description)
- Tested on staging deployment with both command and skill triggers
- MDX rendering passes
- Script verified with pyright
[RISK ASSESSMENT]
Risk Level: LOW
Reasoning: Pure additive documentation example with no impact on existing functionality. Script is self-contained and uses only public APIs.
Was this automated review useful? React with 👍 or 👎 to this review to help us measure review quality.
Workflow run: https://github.com/OpenHands/enterprise-cookbook/actions/runs/37967778981
Why
None of the cookbook's plugins has a command or skill that runs a script shipped inside the plugin. The scripts they do have are hook scripts, pasted inline into
hooks.json. The usual question is how the instructions can refer to the script when the plugin lands at a path nobody knows in advance.This example answers that with a self-contained
city-weatherplugin. Itscommands/now.md(the/city-weather:nowentry command) and its keyword-triggered skill both tell the agent to runskills/city-weather/scripts/weather.py. That script uses only the Python standard library and the free Open-Meteo API, which needs no key.It works because OpenHands gives the agent a
Skill location:line with the absolute path of the command or skill file. Each file writes the script's path relative to itself. The README explains this, shows where Claude Code's plugin docs put scripts, and explains why${CLAUDE_SKILL_DIR}andbin/don't apply in OpenHands.The example is published to the Plugins, skills & MCP category with
example.yaml, and its launch badges were generated withbuild_launch_url.py.Validation
load_plugin.py --ref plugin-with-script— For/city-weather:now Tokyoand for "What's the weather in Paris right now?", the agent took one action: it ranpython3 ~/.openhands/cache/plugins/…/city-weather/skills/city-weather/scripts/weather.py "<city>", then finished with the script's report word for word. The first message triggered only the command and the second only the skill.plugin-with-scriptcompiles with@mdx-js/mdx, including the${CLAUDE_SKILL_DIR}accordion title and the nested code fence in thecommands/now.mdblock.weather.pyunder pyright — Clean when checked directly (the repo's pyright config only coversconversation-metrics). An unknown city or an unreachable API prints an error and exits with status 1.This PR was drafted by an AI agent on behalf of the user.
@jpshackelford can click here to continue refining the PR