OpenHands Extensions
September 4, 2026 · View on GitHub
This repository (OpenHands/extensions) is the public extensions registry for OpenHands.
It contains shareable skills and plugins that can be loaded by OpenHands (CLI/GUI/Cloud) and by client code using the Software Agent SDK.
Cross-Repository Boundaries
This repository owns the public registry of reusable OpenHands skills, plugins, automations, and integrations. These extensions are consumed by OpenHands applications and SDK-based clients.
Related repositories have distinct responsibilities:
OpenHands/software-agent-sdkowns the Python SDK, Agent Server, agent/tool behavior, conversations, workspaces, events, and canonical API.OpenHands/typescript-clientowns the browser-compatible typed Agent Server client.OpenHands/OpenHandsowns Agent Canvas UI and local-stack orchestration.OpenHands/automationowns scheduling, webhooks, run history, dispatch, and sandbox lifecycle orchestration.
Put reusable skills, plugins, automations, and integrations here; put backend execution behavior in the SDK, typed API access in typescript-client, application UI in Agent Canvas, and scheduling/dispatch lifecycle code in automation. If a PR is opened in the wrong repository, explicitly recommend closing and moving it to the owning repository. PRs must follow this repository's applicable contribution and code-review guidance.
What this repo contains
-
skills/— a catalog of skills, one directory per skill.skills/<skill-name>/SKILL.md— the skill definition (AgentSkills-style progressive disclosure)skills/<skill-name>/README.md— optional extra docs/examples for humans
-
plugins/— a catalog of plugins with executable code components.plugins/<plugin-name>/SKILL.md— the plugin definitionplugins/<plugin-name>/hooks/— lifecycle hooks (optional)plugins/<plugin-name>/scripts/— utility scripts (optional)
There is no application code here; the primary artifacts are Markdown skill definitions and plugin configurations, which can contain scripts/, hooks/ sub-directories.
How client code uses this repo
OpenHands Applications
OpenHands can load skills from a project directory (repo-level) and from user-level locations. This repository is the global/public registry referenced by the docs.
Skill loading models to know
OpenHands supports two complementary mechanisms:
-
Always-on context (repository rules)
- Loaded at conversation start.
- Prefer a root
AGENTS.md(and optionally model-specific variants likeCLAUDE.md/GEMINI.md).
-
AgentSkills (progressive disclosure), with an OpenHands extension for keyword triggers
- Each skill lives in its own directory with a
SKILL.mdentry point. - The agent is shown a catalog (name/description/location) and decides when to open/read the full content.
- OpenHands extension: the
SKILL.mdmay include optionaltriggers:frontmatter to enable keyword-based activation.
- Each skill lives in its own directory with a
This registry primarily provides (2). Client repositories typically add (1) for repo-specific, always-on instructions.
Software Agent SDK
SDK consumers typically load skills either:
- as always-loaded context (e.g.,
AGENTS.md), and/or - as trigger-loaded keyword skills, and/or
- as progressive-disclosure AgentSkills by discovering
SKILL.mdfiles under a directory.
See: https://docs.openhands.dev/sdk/guides/skill
AgentSkills / Skill authoring rules (follow these)
OpenHands uses an extended AgentSkills standard:
- Compatible with the AgentSkills specification (https://agentskills.io/specification)
- Extended with optional
triggers:frontmatter for keyword-based activation
When editing or adding skills in this repo, follow these rules (and add new skills to marketplaces/openhands-extensions.json):
-
One skill per directory
- Create
skills/<skill-name>/SKILL.md. - Keep the directory name stable; it is used as the skill identifier/location.
- Create
-
SKILL.md should be progressive disclosure
- Put a concise summary/description first.
- Include only the information needed for an agent to decide whether to open/read the skill.
- If the skill needs large references, keep them in the same directory (e.g.,
references/) and point to them.
-
Be specific and operational
- Prefer checklists, steps, and concrete examples.
- Avoid vague guidance like “be careful” without actionable criteria.
-
Avoid repo-local assumptions
- Skills here are public and reusable.
- Don’t reference private paths, secrets, or company-specific URLs.
-
Do not include secrets or sensitive data
- Never commit API keys, tokens, credentials, private endpoints, or internal customer data.
-
Prefer minimal, composable skills
- Keep a skill focused on a single domain/task.
- If it grows large, split it into multiple skills.
-
Compatibility notes
- The legacy
.openhands/microagents/location may still exist in user repos, but this registry uses the current skills layout.
- The legacy
Repository conventions
-
Punctuation style: Use plain hyphens (
-) instead of em dashes (—/\u2014) in skill descriptions, SKILL.md content, and marketplace JSON entries. -
defaultEnabledon marketplace skill entries: a skill entry may carry"defaultEnabled": true, which means the skill is enabled for every new workspace. Omit the field for everything else - absence already means off, so"defaultEnabled": falseis not written. Keep the set small and provider-agnostic; anything language-, vendor- or workflow-specific should start off and be opted into from the catalog UI.npm run build:skillsjoins the flag intoskills/index.jsand exportsDEFAULT_ENABLED_SKILL_NAMESfor hosts to seed a workspace from. Seeding is all it does: the contract hosts implement is that a workspace which already saved a selection keeps it, so adding the flag to a skill later will not retroactively enable it for existing users. -
Keep formatting consistent across skills.
-
If you change a skill’s behavior or scope, update its
README.md(if present) accordingly. -
If you change top-level documentation, ensure links still resolve.
-
integrations/catalog/*.jsonis the single hand-authored source of truth consumed by@openhands/extensions; adding or editing an integration should require changing exactly one JSON file in that directory. Do not reintroduceintegrations/integration-catalog.json, separate provider files, per-language catalog duplicates, or provider-specific runtime code. Runnpm run build:integrationsafter catalog edits to regenerateintegrations/catalog-index.js, which statically imports each individual JSON file for the JS package. The Python package includes the same individual JSON files via wheel data and reads them directly. Agent-canvas and integrations-hub import this package directly, so integration marketplace fixes belong here rather than in app-local constants. When upstream MCP projects move repos, verify bothdocsUrland the connection option (transport,command/args, or URL), not just links. JS exposeslistIntegrationCatalog({ mcp, oauth })/getIntegrationCatalogEntry; Python mirrors withlist_integration_catalog_models(mcp=, oauth=)/get_integration_catalog_entry_model, which return validated Pydantic models (the raw-dict accessors were deprecated in 0.10.0 and removed in 0.12.0). The legacy provider-catalog compatibility layer andmanagedConnectorMigration/legacyScopeBundles/canonicalServerUrl/errorHintsmechanisms were removed intentionally; providers declare only standard OAuth config as integration data. -
automations/catalog/<id>/follows the same rule, as a directory per automation so an automation can ship the scripts it uploads to the automations service alongside its metadata.manifest.jsonis the single hand-authored file, carrying both the card metadata and an optional nestedsetupblock (the extension-owned configuration experience). Every entry is validated againstautomations/catalog.schema.json. Runnpm run build:automationsafter catalog edits to regenerateautomations/catalog-index.js. JS exposeslistAutomationCatalog()/getAutomationCatalogEntry(id)alongsideAUTOMATION_CATALOG. The governing rule is that a manifest states only what varies between automations, and states it once; anything derivable is absent and the host generates it. Derived, so never written in a manifest: the command that launches the card (read from thetriggers:frontmatter of the skill named byskill, which defaults toidand is stated only where the two differ - editing an automation must never require editing a skill), the setup route (/automations/new/<id>), the trigger variants a deployment can offer (the keys ofsetup.form.triggers; multiple keys mean any supported selected variant can run), schedule limits, timezone lists, event choices, and model profiles (thecron,timezone,event-source,event-type, andllm-profilefield types), the preflight call, the create request'sname/repos/model/timeout/trigger(rebuilt from same-named form fields when present, the repo-picker field, and the selected key and fields undersetup.form.triggers, where a field is named after the property it fills), the mapping from a rejected payload path back to the input at fault, the review screen, the create endpoint, the post-success navigation, and the analytics stages.requires.integrationsis keyed by integration id and lives on the entry, not insetup, because a card lists its integrations whether or not it ships a setup flow; there is no key anywhere for a credential, because the credential comes with the connection.setup.formsplits inputs intotriggers(keyed by trigger kind - what decides when the automation runs) andargs(everything else), both keyed by field name.setupdeclares aprompt(plus afilterfor an event trigger) whenmodeisdirect, and amessagewhen it isassisted. Contract fixtures are test vectors, not catalog data: they live intests/fixtures/automations/, are optional per automation, and are reachable only through the@openhands/extensions/testing/automations/*.jsonsubpath - never from the runtime entry point. -
For Python test runs, prefer
uv sync --group testfollowed byuv run pytest -q; the full suite depends onopenhands-sdk, which is not available in the base environment. -
Agent-driven plugins (for example
plugins/pr-reviewandplugins/release-notes) useuv run --with openhands-sdk --with openhands-tools ...and require anLLM_API_KEYin addition toGITHUB_TOKEN. -
For OpenHands Cloud API guidance, automations, and CLI integration, use
plugins/openhands. It is the canonical unified OpenHands plugin covering the V1 Cloud API, Automations API, and CLI. The individual skills (skills/openhands-api,skills/openhands-automation) are also available standalone. -
When reviewing or editing
skills/openhands-sdk, validate copy-paste imports against the released packages withuv run --with openhands-tools --with openhands-workspace --with openhands-agent-server python .... In the current released workspace package, the exported remote workspace classes areAPIRemoteWorkspace/OpenHandsCloudWorkspace;RemoteAPIWorkspaceis not available. -
For agent-driven plugin scripts, prefer
from openhands.sdk.plugin import PluginSourceand passplugins=[PluginSource(source=...)]intoConversation. In the current released SDK (openhands-sdk1.18.x),Pluginis not exported fromopenhands.sdk.plugin, so directPlugin.load(...)imports can break CI. -
plugins/qa-changes/action.ymlnow has a preflight guard for fork PRs inpull_requestcontext: if the PR comes from a fork andLLM_API_KEYis unavailable (normal for forks), the action exits successfully with a clear skip notice instead of failing. -
skills/bitbucketshould not tell agents to rewrite remotes proactively. In OpenHands,BITBUCKET_TOKENis commonly kept in unencodeduser:tokenform for API calls likecurl --user "$BITBUCKET_TOKEN" ...; only split and URL-encode it when constructing a non-interactive HTTPS Git remote URL. -
plugins/release-notesnow has a standalone validator atplugins/release-notes/scripts/validate_release_notes.py; it rebuilds the deterministic tag-range context, fails if a change bullet omits explicit PR/commit refs or matching author handles, and enforces full PR/author coverage by appending a compact### 🔎 Small Fixes/Internal Changesappendix grouped by author when the agent omits lower-signal items. New contributor detection ingenerate_release_notes.pyshould use merged PR history for human authors (excluding bots) rather than commit-author lookup.
CI / validation gotchas
-
The test suite expects every directory under
skills/to be listed in a marketplace. If you add a new skill (or rebase onto a main branch that added skills), update the appropriate marketplace file or CI will fail withSkills missing from marketplace: [...]. -
scripts/sync_extensions.pykeeps generated artifacts in sync: Claude Code command files, README catalog section, coverage checks, and vendor symlinks. Runpython scripts/sync_extensions.py --check(or just push — CI runs it) to verify everything is consistent. Run without--checkto auto-fix. The "Quick Start" section inREADME.md(OpenHands SDK, Claude Code, and Codex setup instructions) is manually maintained above the auto-generated catalog markers and is intentionally not generated by the sync script. -
The sync script uses PyYAML to parse SKILL.md frontmatter. If you add a skill with a slash trigger (e.g.,
triggers: ["/mycommand"]), the script auto-generatescommands/mycommand.md. Note: Slash triggers in SKILL.md frontmatter are deprecated — prefer adding acommands/command-name.mdfile to the plugin'scommands/directory instead. Keyword triggers (non-slash) remain the recommended way to activate skills by topic. -
The
ready-for-devgates live in.github/workflows/issue-readiness-check.ymland.github/workflows/pr-description-check.yml, backed by.github/scripts/check_issue_readiness.py,check_pr_description.py,refresh_linked_pr_checks.py, andpost-readiness-comment.mjs. Issues labeledbugorenhancementmust meet type-specific readiness criteria (reproducible command plus Acceptance Criteria checklist for bugs; Desired Behavior plus Acceptance Criteria checklist for enhancements) to receive theready-for-devlabel. Non-draft PRs must keep the## Why,## Summary, and## How to Testtemplate sections filled, and any issue linked via a closing keyword or the## Issue Numbersection must carryready-for-devunless it predates the 2026-08-25 rollout. Label transitions on an issue re-run the PR Description Check for linked open PRs. Tests live intests/test_check_issue_readiness.py,tests/test_check_pr_description.py, andtests/test_refresh_linked_pr_checks.py.
OpenHands SDK documentation policy
- Do NOT add SDK-specific or SDK-related documentation to this repo. The canonical source of truth for SDK documentation is the OpenHands docs site and its structured index at https://docs.openhands.dev/llms.txt.
- The
skills/openhands-sdk/SKILL.mdis auto-generated byscripts/sync_openhands_sdk_skill.py. It pulls class names, guides, examples, and the hello-world snippet directly from the docs site and the SDK repo. Do not edit SKILL.md by hand - run the script to regenerate it. - CI runs
python scripts/sync_openhands_sdk_skill.py --checkon every PR. If the skill is out of date, regenerate it withpython scripts/sync_openhands_sdk_skill.py. - If a PR adds or modifies SDK-specific documentation in this repo, push back: ask the submitter to contribute those changes to OpenHands/docs instead.
PR review plugin notes
- The
code-reviewandcodereview-roastedskills have been merged into a singlecode-reviewskill. The/codereview-roastedtrigger is kept as an alias for backward compatibility. Thereview-styleaction input is deprecated and ignored. plugins/pr-reviewsupports an optionalrequire-evidenceaction input that tells the reviewer to require end-to-end proof in the PR description that the code works; test output alone is not sufficient evidence.- The corresponding
REQUIRE_EVIDENCEenv flag is consumed byplugins/pr-review/scripts/agent_script.pyand injected into the review prompt viaplugins/pr-review/scripts/prompt.py. plugins/pr-reviewexposes anenable-uv-cacheinput (default'false') that togglessetup-uv's GitHub Actions cache. Default stays off because a prompt-injected reviewer could poison a shared cache that higher-privilege workflows later consume; opt in only on single-tenant self-hosted runners. The README's "Caching and Security" section documents the threat model and recommends a host-level uv cache volume as the preferred alternative for self-hosted setups.- GitHub review suggestions that only delete lines can look empty in
PullRequestReviewComment.body; the rendered content is available viabodyText/bodyHTML, so review-context formatting should fall back there before treating a suggestion as empty. - Prompt coverage for this behavior lives in
tests/test_pr_review_prompt.py. plugins/pr-review'scollect-feedbackinput should append a short thumbs up/down footer to the main GitHub review body viaagent_script.py/prompt.py, rather than posting a separate PR comment.evaluate_review.pyshould read feedback from review-body reactions while still tolerating legacy issue-comment markers.
When uncertain
- Prefer the official OpenHands docs on skills: https://docs.openhands.dev/overview/skills
- Prefer the SDK skill guide: https://docs.openhands.dev/sdk/guides/skill