Files
BillyOutlastandJohn Smith 86e93031fb feat: one-click /apply-ai-fix for AI review suggested entries (#590)
- renderComment() adds maintainer hint for fix+valid YAML
- new apply-ai-fix.yml workflow (issue_comment trigger, permission-gated)
- new scripts/apply-ai-fix.js (extract, validate, commit to PR branch)
- docs/policy.md documents command
- tests: 16 new, 46 total pass

Co-authored-by: John Smith <[email protected]>
2026-10-19 19:41:45 -04:00

7.7 KiB
Raw Permalink Blame History

Catalog Policy

How entries get into the catalog, how they are kept honest, and when they leave. Automation enforces most of this; this page is the reference it links to.

Admission

An entry must:

  • work with OpenCode;
  • point at a public, non-archived repository root (path for a monorepo folder);
  • be the only entry for that repository and path;
  • not be a cryptocurrency, token or trading project (zero tolerance);
  • pass npm run validate (schema, field limits, kind/category fit).

OpenCode V2 requirement for new plugins

New entries in data/plugins/ must use the V2 plugin API: depend on an @opencode/* package (normally @opencode/plugin) and export Plugin.define({ id, setup(ctx) }). Pre-release V2 builds published under the old name (@opencode-ai/[email protected]*, @next, @beta, or @opencode-ai/client) also count. Packages that ship both V1 and V2 entry points count as V2.

The validate check detects this from, in order: the entry's npm package, package.json files in the repository (at path, the root and packages/* / plugins/*), then the source code. Results:

Detected New plugin PR Meaning
v2 passes @opencode/* dependency or Plugin.define found
v1 blocked, labeled needs-v2 Only @opencode-ai/plugin/sdk 1.x, or a legacy V1 plugin function without Plugin.define
unknown passes with needs-human Nothing conclusive found; a maintainer verifies by hand. Adding npm or path usually resolves it

PRs labeled needs-v2 are re-checked every night. When the target migrates, the bot relabels the PR, fills in any missing fields on same-repo branches, re-runs validate and tells the author. After 60 days still on V1, the PR is closed; reopen it or resubmit through the issue form after migrating.

Themes, agents, projects and resources are not blocked on V2. Their compat is informational (v2/v1 when they use the SDK, otherwise n/a).

Existing V1 plugins: sunset on 2027-01-01

Plugins already in the catalog that are V1-only carry a V1 only badge in the README and on the site. The nightly health job re-detects every plugin not marked v2; when one is detected as V2 it opens a PR (bot/health-compat) marking the entry compat: v2. After 2027-01-01 V1-only plugins are removed by the health job's removal PR, but only when that night's re-check still detects V1: a V2 or inconclusive result keeps the entry, and inconclusive ones are listed in the health report for a maintainer. Plugins with unknown compatibility are listed there too for manual verification.

The bot keeps the list in the V1 sunset tracking issue (label v1-sunset) current, posts one reminder there 31 days before the sunset, and closes the issue once no V1-only plugins remain.

Health checks and removal

A nightly job checks every entry and maintains the Catalog health report issue.

Finding Action
Repository renamed or transferred Bot PR updates the repo URL
Repository archived, deleted (404), or npm package deprecated Listed in the report; removal PR after 30 days if it doesn't recover
Plugin not marked v2 now detected as V2 Bot PR sets compat: v2
V1-only plugin after 2027-01-01, still detected as V1 Removal PR
No push for 6–12 months / 12+ months quiet / stale badge on the site. Never removed for inactivity alone: small, finished tools are fine

Removal PRs are reviewed like any other PR; closing one keeps the entries until the next run proposes them again. Git history is the archive; there is no separate retired list.

Discovery

A weekly scout searches npm for packages published since V2 shipped that depend on @opencode/*, keeps public, licensed, non-archived, non-fork repositories that are not already listed or in an open PR, and ranks them by weekly downloads, then stars. It posts a Scout issue; a maintainer ticking a candidate's box makes the bot open a PR for it. Add a package or repo to discovery/declined.txt to stop suggesting it.

AI review

An OpenCode V2 agent reviews the judgment calls the deterministic checks leave open. It is advisory: it never merges or closes anything.

What When Result
Open PRs labeled needs-human or scout daily; a PR is reviewed again only after it gets a new commit One "AI review" comment (verdict, reasons, suggested placement, and for fix a corrected entry checked against the schema) and one label: ai-approve, ai-fix, ai-close or ai-unsure
Unticked candidates on the open Scout issue weekly, after the scout run Confident, policy-clean accepts get a PR opened by the bot, with the AI's kind, topics and tagline; everything else gets an "AI: …" note on its line

A maintainer still merges every PR, including the ones the AI opened, and the validate check still applies. Delete the comment or remove the label to discard a verdict; dispatching the workflow with force re-reviews.

Maintainers can apply a fix verdict in one click by commenting /apply-ai-fix on the PR. The bot validates the sender, checks the suggestion is fresh and schema-valid, commits it to the PR branch (same-repo PRs only), and validate re-runs.

How it is contained:

  • It runs only on a schedule or manual dispatch, with base-branch code; PR code is never checked out.
  • The model runs in a job whose GitHub token has no permissions and is never in its environment. Its agent (.opencode/agents/catalog-reviewer.md) may only read, glob and grep inside the repository; a self-test before every run proves shell, edit and web access are unavailable.
  • It sees a pre-built evidence bundle (the PR's entries, check results, the target's README and package.json, similar entries), treats it as untrusted data, and must reply with one JSON verdict.
  • A separate job validates that verdict against a strict schema (known verdicts, categories, kinds and topics; matching PR number and commit) and renders all comment text itself, escaping HTML and breaking @mentions. Anything else is dropped.

The model comes from the OPENCODE_MODEL repository variable; credentials from the OPENCODE_GO_API_KEY (or OPENCODE_API_KEY) secret.

Branch protection

main has a ruleset: changes land only through pull requests with a passing validate check (GitHub rulesets do not accept direct pushes while a status check is required), and force-pushes and deletion are blocked. Bots open PRs too; GitHub holds workflow runs on bot-opened PRs for approval, so each bot approves the validate run on its own PR. Repository admins can merge a PR whose check is failing or pending (for a broken CI).

Automation map

Workflow When What
validate-pr.yml every PR Required validate check
triage.yml PR opened/updated Labels and a results comment (base-branch code only). needs-human is only ever removed by a maintainer
generate-readme.yml push to main README.md and dist/registry.json; fails without writing on any invalid entry. The regenerated commit goes through a bot PR (bot/readme-*) that merges itself once validate passes
site.yml push to main, daily Search site on GitHub Pages
health.yml daily Health report and V1 sunset issues; rename, V2-migration and removal PRs
reprobe.yml daily Re-checks needs-v2 PRs
scout.yml weekly Scout issue with V2 plugin candidates
issues.yml issue opened/edited Issue-form submissions and scout acceptances become PRs
ai-review.yml daily (PRs), weekly (scout) Advisory AI review of needs-human and scout PRs; opens PRs for scout candidates it accepts