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:

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 definition
    • plugins/<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:

  1. Always-on context (repository rules)

    • Loaded at conversation start.
    • Prefer a root AGENTS.md (and optionally model-specific variants like CLAUDE.md / GEMINI.md).
  2. AgentSkills (progressive disclosure), with an OpenHands extension for keyword triggers

    • Each skill lives in its own directory with a SKILL.md entry point.
    • The agent is shown a catalog (name/description/location) and decides when to open/read the full content.
    • OpenHands extension: the SKILL.md may include optional triggers: frontmatter to enable keyword-based activation.

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.md files under a directory.

See: https://docs.openhands.dev/sdk/guides/skill

AgentSkills / Skill authoring rules (follow these)

OpenHands uses an extended AgentSkills standard:

When editing or adding skills in this repo, follow these rules (and add new skills to marketplaces/openhands-extensions.json):

  1. One skill per directory

    • Create skills/<skill-name>/SKILL.md.
    • Keep the directory name stable; it is used as the skill identifier/location.
  2. 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.
  3. Be specific and operational

    • Prefer checklists, steps, and concrete examples.
    • Avoid vague guidance like “be careful” without actionable criteria.
  4. Avoid repo-local assumptions

    • Skills here are public and reusable.
    • Don’t reference private paths, secrets, or company-specific URLs.
  5. Do not include secrets or sensitive data

    • Never commit API keys, tokens, credentials, private endpoints, or internal customer data.
  6. Prefer minimal, composable skills

    • Keep a skill focused on a single domain/task.
    • If it grows large, split it into multiple skills.
  7. Compatibility notes

    • The legacy .openhands/microagents/ location may still exist in user repos, but this registry uses the current skills layout.

Repository conventions

  • Punctuation style: Use plain hyphens (-) instead of em dashes ( / \u2014) in skill descriptions, SKILL.md content, and marketplace JSON entries.

  • defaultEnabled on 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": false is 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:skills joins the flag into skills/index.js and exports DEFAULT_ENABLED_SKILL_NAMES for 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/*.json is 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 reintroduce integrations/integration-catalog.json, separate provider files, per-language catalog duplicates, or provider-specific runtime code. Run npm run build:integrations after catalog edits to regenerate integrations/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 both docsUrl and the connection option (transport, command/args, or URL), not just links. JS exposes listIntegrationCatalog({ mcp, oauth }) / getIntegrationCatalogEntry; Python mirrors with list_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 and managedConnectorMigration / legacyScopeBundles / canonicalServerUrl / errorHints mechanisms 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.json is the single hand-authored file, carrying both the card metadata and an optional nested setup block (the extension-owned configuration experience). Every entry is validated against automations/catalog.schema.json. Run npm run build:automations after catalog edits to regenerate automations/catalog-index.js. JS exposes listAutomationCatalog() / getAutomationCatalogEntry(id) alongside AUTOMATION_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 the triggers: frontmatter of the skill named by skill, which defaults to id and 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 of setup.form.triggers; multiple keys mean any supported selected variant can run), schedule limits, timezone lists, event choices, and model profiles (the cron, timezone, event-source, event-type, and llm-profile field types), the preflight call, the create request's name/repos/model/timeout/trigger (rebuilt from same-named form fields when present, the repo-picker field, and the selected key and fields under setup.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.integrations is keyed by integration id and lives on the entry, not in setup, 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.form splits inputs into triggers (keyed by trigger kind - what decides when the automation runs) and args (everything else), both keyed by field name. setup declares a prompt (plus a filter for an event trigger) when mode is direct, and a message when it is assisted. Contract fixtures are test vectors, not catalog data: they live in tests/fixtures/automations/, are optional per automation, and are reachable only through the @openhands/extensions/testing/automations/*.json subpath - never from the runtime entry point.

  • For Python test runs, prefer uv sync --group test followed by uv run pytest -q; the full suite depends on openhands-sdk, which is not available in the base environment.

  • Agent-driven plugins (for example plugins/pr-review and plugins/release-notes) use uv run --with openhands-sdk --with openhands-tools ... and require an LLM_API_KEY in addition to GITHUB_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 with uv run --with openhands-tools --with openhands-workspace --with openhands-agent-server python .... In the current released workspace package, the exported remote workspace classes are APIRemoteWorkspace / OpenHandsCloudWorkspace; RemoteAPIWorkspace is not available.

  • For agent-driven plugin scripts, prefer from openhands.sdk.plugin import PluginSource and pass plugins=[PluginSource(source=...)] into Conversation. In the current released SDK (openhands-sdk 1.18.x), Plugin is not exported from openhands.sdk.plugin, so direct Plugin.load(...) imports can break CI.

  • plugins/qa-changes/action.yml now has a preflight guard for fork PRs in pull_request context: if the PR comes from a fork and LLM_API_KEY is unavailable (normal for forks), the action exits successfully with a clear skip notice instead of failing.

  • skills/bitbucket should not tell agents to rewrite remotes proactively. In OpenHands, BITBUCKET_TOKEN is commonly kept in unencoded user:token form for API calls like curl --user "$BITBUCKET_TOKEN" ...; only split and URL-encode it when constructing a non-interactive HTTPS Git remote URL.

  • plugins/release-notes now has a standalone validator at plugins/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 Changes appendix grouped by author when the agent omits lower-signal items. New contributor detection in generate_release_notes.py should 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 with Skills missing from marketplace: [...].

  • scripts/sync_extensions.py keeps generated artifacts in sync: Claude Code command files, README catalog section, coverage checks, and vendor symlinks. Run python scripts/sync_extensions.py --check (or just push — CI runs it) to verify everything is consistent. Run without --check to auto-fix. The "Quick Start" section in README.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-generates commands/mycommand.md. Note: Slash triggers in SKILL.md frontmatter are deprecated — prefer adding a commands/command-name.md file to the plugin's commands/ directory instead. Keyword triggers (non-slash) remain the recommended way to activate skills by topic.

  • The ready-for-dev gates live in .github/workflows/issue-readiness-check.yml and .github/workflows/pr-description-check.yml, backed by .github/scripts/check_issue_readiness.py, check_pr_description.py, refresh_linked_pr_checks.py, and post-readiness-comment.mjs. Issues labeled bug or enhancement must meet type-specific readiness criteria (reproducible command plus Acceptance Criteria checklist for bugs; Desired Behavior plus Acceptance Criteria checklist for enhancements) to receive the ready-for-dev label. Non-draft PRs must keep the ## Why, ## Summary, and ## How to Test template sections filled, and any issue linked via a closing keyword or the ## Issue Number section must carry ready-for-dev unless it predates the 2026-08-25 rollout. Label transitions on an issue re-run the PR Description Check for linked open PRs. Tests live in tests/test_check_issue_readiness.py, tests/test_check_pr_description.py, and tests/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.md is auto-generated by scripts/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 --check on every PR. If the skill is out of date, regenerate it with python 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-review and codereview-roasted skills have been merged into a single code-review skill. The /codereview-roasted trigger is kept as an alias for backward compatibility. The review-style action input is deprecated and ignored.
  • plugins/pr-review supports an optional require-evidence action 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_EVIDENCE env flag is consumed by plugins/pr-review/scripts/agent_script.py and injected into the review prompt via plugins/pr-review/scripts/prompt.py.
  • plugins/pr-review exposes an enable-uv-cache input (default 'false') that toggles setup-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 via bodyText/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's collect-feedback input should append a short thumbs up/down footer to the main GitHub review body via agent_script.py / prompt.py, rather than posting a separate PR comment. evaluate_review.py should read feedback from review-body reactions while still tolerating legacy issue-comment markers.

When uncertain