Skip to content

Add OpenCode compatibility installer for plugin workflows#112

Open
marthendalnunes wants to merge 4 commits intotrailofbits:mainfrom
marthendalnunes:feat/opencode-support
Open

Add OpenCode compatibility installer for plugin workflows#112
marthendalnunes wants to merge 4 commits intotrailofbits:mainfrom
marthendalnunes:feat/opencode-support

Conversation

@marthendalnunes
Copy link

Summary

  • Add a first-party OpenCode installer (scripts/install_opencode_skills.sh) that installs plugin skills and commands, preserving the same command-first plugin UX (command -> skill) used in Claude.
  • Default to simple no-clone installation from GitHub archives (copy mode), with local symlink mode for contributors; support filters (--bundle, --plugin, --skill, --command) and component scoping (--skills-only, --commands-only).
  • Add compatibility guardrails: validate command-to-skill mapping, skip known Claude-only command flows by default, and add scripts/validate_opencode_compat.py plus CI smoke checks.

Docs

  • Add OpenCode compatibility guide at docs/opencode.md.
  • Update README.md with OpenCode install and command usage examples.

Validation

  • python3 scripts/validate_opencode_compat.py
  • python3 .github/scripts/validate_plugin_metadata.py
  • bash scripts/install_opencode_skills.sh --source local --bundle smart-contracts --dry-run --target /tmp/opencode-skills-smoke --commands-target /tmp/opencode-commands-smoke

@marthendalnunes marthendalnunes requested a review from dguido as a code owner March 1, 2026 13:20
@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@dguido
Copy link
Member

dguido commented Mar 4, 2026

Thanks for this PR! I reviewed it with a focus on OpenCode compatibility accuracy, security, and code quality. I've pushed a fixup branch to origin/feat/opencode-support with all the changes below applied. Happy to discuss any of these.

OpenCode Compatibility Fixes

Researched the actual OpenCode behavior (from opencode.ai/docs/skills and anomalyco/opencode source):

  • Skill name must match directory name — OpenCode enforces this. Added validation in both the installer (warning) and the Python validator (error). All existing skills already comply, but the burpsuite-project-parser plugin has a structural issue (SKILL.md directly in skills/ without a named subdirectory).

  • {baseDir} is not supported by OpenCode — OpenCode does no variable substitution in skill content. The docs previously said "treat {baseDir} as the skill directory root" which was misleading. Rewrote to clearly state OpenCode doesn't substitute this variable and explain the practical workaround.

  • Skills auto-register as slash commands — Since PR #11390 (Jan 2026), OpenCode auto-registers installed skills as /skillname. Added a new docs section explaining how this interacts with the explicitly installed command files (they provide namespaced names like /trailofbits:entry-points and specific prompt templates).

  • Command frontmatter differences — Expanded "What Does Not Translate 1:1" to document that allowed-tools and argument-hint are silently ignored by OpenCode.

  • OpenPackage section — Kept it (it's a real tool with 400+ stars) but added a caveat that this repo has no openpackage.yml manifest, so auto-detection applies.

Security Fixes

  • safe_remove_path guards — Was rm -rf "$path" with no validation. Now validates non-empty, rejects root-like paths (/, /home, /usr, etc.), and requires ≥3 path components. Added comment explaining why rm -rf is used instead of trash (portability on CI).

  • --repo/--ref input validation — These were interpolated into a URL with zero validation. Added regex checks: REPO must match ^[a-zA-Z0-9._-]+/[a-zA-Z0-9._-]+$, REF must match ^[a-zA-Z0-9._/-]+$.

  • Install instructions — Reordered so the two-step download-then-inspect approach is primary in both README and docs. The piped curl | bash is now listed as a convenience alternative.

  • is_command_compatible_with_opencode — Added readability guard: if the file can't be read, treat it as incompatible instead of silently passing.

Code Quality Fixes

  • Consolidated duplicate functionsextract_skill_name_from_file and extract_command_frontmatter_name_from_file were character-for-character identical (84 lines). Merged into single extract_frontmatter_name.

  • Decomposed main body into functions — The ~255-line top-level install/uninstall block is now three functions: install_or_uninstall_skill(), install_or_uninstall_command(), print_summary().

  • Fixed SIGPIPEfind ... | head -n 1 with pipefail causes exit 141 if find returns multiple results. Replaced with mapfile.

  • Fixed process substitution hiding find failureswhile ... done < <(find ...) discards exit codes. Replaced with mapfile + for loops.

  • Wrapped cp/ln with error context — Bare cp -R under set -e would kill the script with raw cp output and no indication of which skill failed. Now increments error counter and continues.

  • Fixed skill "unchanged" for remote source — Previously, re-running the installer after upstream changes would report "unchanged" for skills that matched by name. Now replaces with fresh content when source is remote.

  • Clean shellcheck — Fixed all warnings, added inline suppressions with explanations for intentional patterns.

  • Added shellcheck + shfmt to CI — New workflow steps lint the installer script.

Python Validator Improvements

  • Added try/except around read_text() — Previously crashed on unreadable or non-UTF-8 files.
  • Added command file validation — New validate_command_files() checks for ${CLAUDE_PLUGIN_ROOT} usage and Claude-specific frontmatter fields.
  • Added plugins/ existence check — Prevents silent false-positive if the script is relocated.

Fixup branch

All changes are on origin/feat/opencode-support as a single commit on top of yours.

marthendalnunes and others added 3 commits March 3, 2026 21:09
Install matching commands and skills with a no-clone shell script so OpenCode keeps the same command-to-skill UX as Claude plugins, and add docs plus CI checks to enforce compatibility.
Shell script (install_opencode_skills.sh):
- Add safe_remove_path guards (non-empty, non-root, min depth)
- Add --repo/--ref input validation against URL injection
- Fix SIGPIPE from find|head pipeline (use mapfile)
- Fix process substitution hiding find failures
- Wrap cp/ln with error context instead of silent set -e exit
- Consolidate duplicate extract_*_name functions into one
- Decompose 255-line main body into functions
- Fix skill "unchanged" check for remote source (replace stale)
- Add skill name == directory name validation (OpenCode requires)
- Add readability guard in is_command_compatible_with_opencode
- Fix all shellcheck warnings

Python validator (validate_opencode_compat.py):
- Add try/except around read_text() for unreadable files
- Add skill directory name vs frontmatter name check
- Add command file validation (CLAUDE_PLUGIN_ROOT, Claude fields)
- Add plugins dir existence check

Documentation (docs/opencode.md, README.md):
- Fix {baseDir} docs: OpenCode does not substitute this variable
- Document skill auto-registration as slash commands
- Expand "What Does Not Translate 1:1" with frontmatter details
- Add caveat to OpenPackage section (no openpackage.yml manifest)
- Lead with two-step install (download-then-inspect) over curl|bash

CI (.github/workflows/validate.yml):
- Add shellcheck and shfmt linting steps

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@dguido dguido force-pushed the feat/opencode-support branch from d2a2902 to 28e5f55 Compare March 4, 2026 05:13
The burpsuite-project-parser plugin has a pre-existing structural
issue (SKILL.md directly in skills/ instead of a named subdirectory).
This should not block the PR.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

3 participants