[STG-2487] feat: static multi-marketplace manifest catalog (browse-only, CLI-only)#3
Merged
Merged
Conversation
…MCP) Repurpose browse-plugin into a dedicated, static (no-codebase) repo that external agent marketplaces can SHA-pin. Ships only the `browse` plugin, in all 5 marketplace formats, pointed at the hosted Browserbase MCP. Add root marketplace manifests for every format: - .claude-plugin/marketplace.json (Claude Code) - .codex-plugin/plugin.json (Codex) - .cursor-plugin/marketplace.json (Cursor, browse-only) - .agents/plugins/marketplace.json (generic .agents) - .grok-plugin/plugin.json (Grok) - gemini-extension.json (Gemini CLI) - .mcp.json + server.json (hosted MCP: https://mcp.browserbase.com/mcp) Per-plugin manifests under plugins/browse/{.claude-plugin,.codex-plugin, .cursor-plugin,.grok-plugin}/plugin.json + plugins/browse/.mcp.json, all referencing ./skills/, ./.mcp.json, and assets/logo.svg. De-Playwright: remove the custom TypeScript MCP server, npm build, agent runtime dirs, the browser-automation skill, rules, and the entire functions plugin. Ship the canonical browse CLI SKILL.md instead. Add .github/CODEOWNERS (* @browserbase/gtm), rewrite README + add-a-plugin docs for the static hosted-MCP layout, drop build steps from release.yml. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Every manifest shipped a bare https://mcp.browserbase.com/mcp URL with no auth (no header, no query param). Live-tested via real marketplace installs in Claude Code and Gemini CLI: the model reaches the hosted MCP tool but every call is rejected server-side with "Invalid query parameters" (no browserbaseApiKey in header or query string; full OAuth 2.1 isn't deployed either). A static, git-pinned public repo can't safely embed a real API key, so there's no one-click fix available for this distribution path today. This audience (devs pulling the marketplace into Claude Code, Codex, Cursor, Gemini CLI) already has shell access, so MCP added no reach CLI doesn't already cover. Scope to CLI-only: strip mcpServers from all plugin manifests, delete .mcp.json/server.json, and give Gemini a real CLI path via a GEMINI.md context file (Gemini's SKILL.md equivalent) instead of its MCP-only extension. Live end-to-end verified against the real repo files post-change: Claude Code (zero MCP tools registered, real browse CLI call, correct result) and Gemini CLI (real shell calls via GEMINI.md, correct result). Codex install/registration verified clean; invocation untestable on this machine due to an unrelated expired local OpenAI API key. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The nested plugins/browse/ layout was scaffolding for when this repo held two plugins (browse + functions). Now that it's committed to shipping a single browse plugin, the nesting has no remaining beneficiary and breaks third-party tools with a simpler root-scan convention — confirmed with Hermes Agent's skill "tap" mechanism, which hardcodes looking for skills/ at repo root and can't find our skill nested under plugins/browse/skills/. Moves skills/browse/SKILL.md and the per-format plugin.json files up to repo root, updates every marketplace file's source/path from "./plugins/browse" to ".", and removes the now-redundant duplicate Codex/Grok plugin.json files that existed at both root and nested paths. Matches link-cli's own flat layout, the reference this repo was modeled on from the start. node scripts/validate-template.mjs passes clean (same benign hooks/mcp warnings as before). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This repo's copy was from 2026-07-07 (pre-STG-2513) and was missing: - --local vs --remote guidance for Chrome-less containers/CI/sandboxes - BROWSE_SESSION env-var precedence rule (don't pass --session if it's already set) - Named contexts (cloud contexts create --name / list), which shipped in browse@0.9.1 and is live in the current published browse@0.9.4 -- this repo still had the old ID-only surface Pulled verbatim from origin/main (stagehand#82c489bd). Auto-sync (stagehand#2330) is still a draft proposal, so this is a manual, one-time pull -- re-sync by hand until that lands.
ziruihao
approved these changes
Jul 9, 2026
Leftover from before the CLI-only scoping (a1b3d03) deleted .mcp.json and server.json -- this repo doesn't ship an MCP config anymore.
Lets vendor-neutral installers (e.g. npx plugins) discover this repo without relying on the skills/ fallback heuristic.
shrey150
commented
Jul 14, 2026
shrey150
commented
Jul 14, 2026
shrey150
commented
Jul 14, 2026
Addresses PR review: drop the "Browserbase" prefix from "Browserbase
browse CLI" everywhere (just Browse CLI), rename .agents marketplace
from browserbase-plugins to browserbase to match the Claude/Cursor
marketplace names, and align Cursor's plugin displayName ("Browse")
with Codex's.
Surveyed Exa, Kernel, Parallel, and AgentMail's own MCP/plugin manifest descriptions: all lead with the capability noun and list concrete verbs, rather than opening with an imperative like "Automate...". Replaces the long description everywhere with the same pattern.
Rewrites a few passages that read as "how we got here" development narrative (earlier revisions, dated sync status, "the reference this repo is modeled on") into present-tense statements of the current layout and constraints. History like this belongs in the PR/commit log, not in a doc that's permanent once merged.
…edence, named contexts GEMINI.md lagged skills/browse/SKILL.md on three points that affect correctness in sandboxes: no guidance to fall back to --remote when no local Chrome is available, no BROWSE_SESSION override-precedence rule, and no named-context alias workflow. Ports the same text verbatim from SKILL.md.
Wasn't pulling any weight for a contributor trying to understand this repo's layout.
Validation only ran on tag push (release), so a broken manifest or a stale GEMINI.md had no automated signal on a PR — the GEMINI.md gap Bugbot just caught here was only found because a reviewer happened to read the diff, not because anything enforced it. Adds a pull_request-triggered workflow that runs the existing validator on every PR, plus a new check: GEMINI.md's body is generated verbatim from skills/browse/SKILL.md (confirmed byte-identical), so scripts/sync-gemini.mjs regenerates it and the validator fails if they diverge.
The comparison normalized line endings on the SKILL.md side only, so a CRLF GEMINI.md would report false-positive drift, and sync-gemini would needlessly rewrite it.
README is the human landing point for this repo; em dashes read as AI-generated. Rewrote as separate clauses/commas instead.
Compared against browserbase/stagehand's top-level README and the
browse CLI's own README: both stay benefit-first (what it does, how
to use it) and never defend a design decision or reference a
tracked-elsewhere roadmap item.
Drops the "Why CLI-only, not MCP" rationale (redundant with the plain
fact already stated a line above, and out of place in a README).
Trims the repo-layout tree and third-party-tool caveat that
duplicated docs/add-a-plugin.md verbatim, linking there instead so
there's one source for that detail rather than two that can drift.
Leads with what the repo lets you do, not its internal
classification ("static, no-codebase catalog").
gemini-extension.json requires GEMINI.md as its context file, but validateGeminiSync silently no-op'd when the file was absent instead of erroring, so deleting it would leave CI green.
Both were added earlier in this PR but never made it into the documented layout, so the tree was already out of sync with the repo it describes.
plugin.json's version tracks the browse CLI's own npm version, since the plugin has no independent feature surface beyond the CLI it wraps. All five manifests were stuck at 0.9.1, matching neither the CLI's current published version (0.9.5) nor this repo's own release tags. Same fix shape as the GEMINI.md sync check: plugin.json is the source of truth, scripts/sync-version.mjs propagates it to the other four manifests, and validate-template.mjs fails CI if they drift.
Corrected course from the previous commit: Cursor's marketplace is confirmed (via a prior live audit) to be driven strictly by this repo's own git tag pushes -- release.yml only runs on push: tags, so merging to main alone never updates what Cursor serves. A prior audit had already flagged the old repo's manifest version (2.x) disagreeing with its own git tag (v0.2.x) as confusing -- pointing plugin.json at browse's npm version would have reintroduced exactly that. xAI's and Anthropic's marketplace formats don't use a version field at all in any meaningful way (verified against their actual draft PR content), so this only mattered for Cursor. plugin.json now tracks this repo's own release line (0.3.0, next after v0.2.4). Added a tag-release job to validate.yml, mirroring the proven pattern already used in stagehand's own release.yml for browse-cli: on push to main, diff plugin.json's version against the parent commit, and if it changed, push a matching vX.Y.Z tag -- never a direct commit to main, so it works regardless of branch protection. That tag push is what triggers the existing release.yml. Verified the diff/tag-existence logic directly against real commits in this repo's history before relying on it in CI.
The previous version only compared plugin.json's version against its immediate parent commit, so a version bump split across multiple commits (e.g. an unsquashed merge, or separate bump/sync commits) could land on main without ever getting tagged. Checking the current committed version against tag existence directly is simpler and can't have that gap -- it doesn't matter which commit introduced the final state.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 40bcef3. Configure here.
validateVersionSync silently skipped a deleted .claude-plugin/plugin.json, .cursor-plugin/plugin.json, .grok-plugin/plugin.json, or gemini-extension.json instead of erroring, so CI could pass with a required manifest missing entirely. Same class of bug as the earlier GEMINI.md fix.
shrey150
commented
Jul 15, 2026
shrey150
commented
Jul 15, 2026
…ntity field Every marketplace-level identity field in this repo (Claude/Cursor marketplace owner, .agents displayName, root plugin.json author) already says Browserbase. These two plugin-level displayName fields were the only outliers saying "Browse" instead -- fixes that, and matches how other real entries in these marketplaces (Vercel, Sentry, MongoDB, Cloudflare) lead with the company brand in the title slot, not a sub-product name.
shrey150
added a commit
that referenced
this pull request
Jul 16, 2026
Confirmed live: merging PR #3 pushed tag v0.3.0 correctly, but release.yml never ran and no GitHub Release was created. Root cause is a documented GitHub Actions behavior -- a tag pushed using the default GITHUB_TOKEN from within a workflow run does not trigger other workflows' push events, to prevent infinite loops. That exemption does not apply to workflow_dispatch, so tag-release now explicitly dispatches release.yml instead of relying on push:tags. release.yml's workflow_dispatch trigger only exists on main, not on the tag itself, and softprops/action-gh-release can't infer the right tag from a workflow_dispatch context (it falls back to github.ref_name, which would be "main"). So the dispatch passes an explicit tag input, and both the checkout step and the release step use it to check out and tag the correct ref.
shrey150
added a commit
that referenced
this pull request
Jul 16, 2026
#4) Confirmed live: merging PR #3 pushed tag v0.3.0 correctly, but release.yml never ran and no GitHub Release was created. Root cause is a documented GitHub Actions behavior -- a tag pushed using the default GITHUB_TOKEN from within a workflow run does not trigger other workflows' push events, to prevent infinite loops. That exemption does not apply to workflow_dispatch, so tag-release now explicitly dispatches release.yml instead of relying on push:tags. release.yml's workflow_dispatch trigger only exists on main, not on the tag itself, and softprops/action-gh-release can't infer the right tag from a workflow_dispatch context (it falls back to github.ref_name, which would be "main"). So the dispatch passes an explicit tag input, and both the checkout step and the release step use it to check out and tag the correct ref.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

What
Repurposes
browserbase/browse-plugininto the dedicated, static (no-codebase) repo that external agent marketplaces SHA-pin. Ships only thebrowseplugin, in all 5 marketplace formats, CLI-only — abrowseCLI skill that shells out viaBash/run_shell_command, no bundled MCP server.Added — root marketplace manifests (one per format)
.claude-plugin/marketplace.json.codex-plugin/plugin.json.cursor-plugin/marketplace.json.agents/plugins/marketplace.json.agentsmarketplace.grok-plugin/plugin.jsongemini-extension.json+GEMINI.mdplugin.json(repo root)npx plugins add browserbase/browse-plugin, Vercel's cross-client CLI) discover this repo directly instead of relying on itsskills/-directory fallback heuristic. Nomcp.jsonadded alongside it — optional per spec, and this plugin intentionally ships no MCP servers (see "Why CLI-only" below).Added — per-plugin manifests under
plugins/browse/.claude-plugin/,.codex-plugin/,.cursor-plugin/,.grok-plugin/plugin.jsoneach referencing./skills/andassets/logo.svg. The canonical browse CLISKILL.md(copied fromstagehand/packages/cli/skills/browse/) lands atskills/browse/SKILL.md(repo root, post-flatten) with itsname/descriptionfrontmatter intact.SKILL.md freshness
There's no auto-sync yet (stagehand#2330 is still a draft proposal), so
skills/browse/SKILL.mdis a manual, point-in-time copy ofstagehand/packages/cli/skills/browse/SKILL.md. Refreshed once more (2026-07-09) to pull in container/CI--remoteguidance,BROWSE_SESSIONprecedence, and the named-contexts surface (cloud contexts create --name) that had shipped inbrowse@0.9.1+ but wasn't in this repo's earlier copy. Re-sync by hand until the auto-sync PR lands.browse@0.9.5published the same day (npm +ghcr.io/browserbase/browse) — diffed this repo'sskills/browse/SKILL.mdagainststagehand'sorigin/maincopy after that release and they're byte-identical, so this file matches whatnpm i -g browseactually installs today, not a bleeding-edgemainthat's ahead of the published package.Naming and copy fixes (review feedback)
plugin.json..agents/plugins/marketplace.jsonmarketplace frombrowserbase-pluginstobrowserbase, matching the Claude and Cursor marketplace names.displayNameto"Browse"(was"Browserbase Browse"), matching Codex's.server.json/plugin.json(not their marketing pages) — all lead with the capability noun and list concrete verbs rather than opening with an imperative. Old:"Automate web browsers with natural language via the Browse CLI."New:"Browser automation for AI agents: navigate, extract, screenshot, and interact with real web pages via a single CLI."stripe/link-clireference from the docs — wasn't pulling any weight for a reader trying to understand this repo's layout.README.md/docs/add-a-plugin.mdthat read as "how we got here" development narrative (earlier revisions, dated sync status) into present-tense statements of the current layout and constraints.CI: validate on every PR, and keep
GEMINI.mdfrom going stalescripts/validate-template.mjspreviously only ran on tag push (release) — a broken manifest or a staleGEMINI.mdhad no automated signal on a PR. That's exactly how the next item was found: a reviewer happened to read the diff, not because anything enforced it..github/workflows/validate.yml: runs the existing validator on everypull_requestand on push tomain.GEMINI.md's body is generated verbatim fromskills/browse/SKILL.md(confirmed byte-identical — Gemini's extension format has no way to reference an external skill file, so the instructions have to live directly inGEMINI.md).scripts/sync-gemini.mjsregenerates it;node scripts/sync-gemini.mjs --checkand the main validator both fail if the two drift, with the fix command in the error message. Both sides of the comparison normalize CRLF, so a Windows-checked-outGEMINI.mddoesn't false-positive.GEMINI.mditself laggingSKILL.mdon this branch (missing container/CI--remotefallback guidance,BROWSE_SESSIONprecedence, and named cloud contexts) — fixed by porting the missing text verbatim fromSKILL.md, which is also what proved the two files are meant to be byte-identical.SKILL.mdside only (fixed, tested with a real CRLF-convertedGEMINI.md), andvalidateGeminiSyncsilently passed ifGEMINI.mdwas missing entirely even thoughgemini-extension.jsonrequires it (fixed to error in that case, tested by actually deleting the file).docs/add-a-plugin.md's layout tree hadn't been updated for either the rootplugin.jsonor the newscripts/files added earlier in this same PR, so it was already out of sync with the repo it documents. Fixed.versionfield: all five were stuck at0.9.1, matching neither thebrowseCLI's actual published npm version (0.9.5) nor this repo's own release tags (v0.1.0→v0.2.4). First fix pointedplugin.json's version at the CLI's npm version — wrong call, reverted. Checked the actual mechanics: a prior live audit confirmed Cursor's official marketplace listing is built byrelease.yml, which only runs on a tag push — merging tomainalone never updates what Cursor serves. That same audit had already flagged the old repo's in-manifest version disagreeing with its own git tag as a source of confusion; pointing at the CLI's version would have reintroduced exactly that. Checked xAI's and Anthropic's actual draft PR content directly: neither's marketplace format uses aversionfield in any load-bearing way (xAI has none at all; 240 of 254 real entries in Anthropic's official directory have none, and the 14 that do are frozen at1.0.0). Soplugin.json'sversionnow tracks this repo's own release line instead, bumped to0.3.0(next afterv0.2.4).scripts/sync-version.mjspropagates it to the other four manifests, and the validator fails CI if they drift or if a required manifest is missing entirely.stagehand's own release workflow forbrowse-cli(including the same branch-protection gotcha it hit there): a newtag-releaseCI job checksplugin.json's currently-committed version against existing tags on every push tomain, and pushes a matchingvX.Y.Ztag if one doesn't exist yet — no one runsgit tagby hand again. It only ever pushes a tag ref, never a commit tomain, so it's safe under branch protection. Bugbot caught a real gap in the first version (diffed against the immediate parent commit only, so a version bump split across multiple commits could land without ever getting tagged) — fixed by checking tag existence against the current state directly instead of any commit diff, which structurally can't have that gap.README rewrite to match house voice
Compared against
browserbase/stagehand's top-level README and thebrowseCLI's own README: both stay benefit-first (what it does, how to use it) and never defend a design decision or reference a tracked-elsewhere roadmap item. Dropped the "Why CLI-only, not MCP" rationale fromREADME.md(redundant with the plain fact already stated a line above it, and out of place in a README aimed at someone installing the plugin). Trimmed the repo-layout tree and third-party-tool caveat that duplicateddocs/add-a-plugin.mdverbatim, linking there instead of maintaining the same content in two places. Also removed em dashes fromREADME.mdspecifically, since it's the human landing point for the repo.Removed — the de-Playwright, and (later) the de-MCP
Deleted the custom TypeScript MCP server and all app code (
plugins/browse/{src,scripts,agent,package.json,package-lock.json,tsconfig.json},plugins/browse/skills/browser-automation/,plugins/browse/rules/,plugins/functions/). Then, after live-testing the hosted-MCP wiring (see "Why CLI-only" below), removed it too:mcpServersstripped from everyplugin.json,.mcp.json(root + per-plugin) andserver.jsondeleted,gemini-extension.jsonrewritten to a plain CLI-context extension. Net across both changes: +926 / −3932.Why CLI-only, not MCP
The plugin originally bundled both a CLI-shelling skill and a hosted-MCP reference (
.mcp.json→https://mcp.browserbase.com/mcp) side by side, letting the agent pick either at runtime. Live-testing that MCP path (real marketplace installs, real headless invocations — not just schema validators) found it's currently broken for every client, and not fixable within this PR's scope:Authorization: Bearer, nox-bb-api-keyheader, no?browserbaseApiKey=query param.initialize/tools/listare unauthenticated by design;start/navigate/etc. are not). With none supplied, every real tool call was rejected:{"jsonrpc":"2.0","error":{"code":-32000,"message":"Invalid query parameters"}}. Confirmed independently through two different real clients (Claude Code, Gemini CLI) — same error, same root cause both times.https://mcp.browserbase.com/.well-known/oauth-protected-resourceandoauth-authorization-server, both 404; zerooauth/authorize|token|registerroutes exist anywhere inbrowserbase/core'sorigin/main.Given that, and that this audience already has shell access (Claude Code, Codex, Cursor, Gemini CLI, Grok all run with a sandbox), a bundled MCP server adds no reach for these clients while introducing a broken/confusing fallback path — in testing, the agent would sometimes pick the (broken) MCP tool over the (working) CLI skill when both were present. CLI-only removes that ambiguity and matches what's actually proven to work. The hosted MCP remains the right integration for shell-less surfaces (ChatGPT, claude.ai) — that's a separate initiative, gated on OAuth for those specific directories, tracked outside this PR.
E2E Test Matrix
Manifest validators (all formats, run against this branch)
node scripts/validate-template.mjsValidation passed.(exit 0) + 2 benign warnings: nohooks/hooks.json, nomcp.json(expected now — CLI-only)owner.nameset, non-emptyplugins[], source-dir/name match, referencedlogo/skillspaths + SKILL.md frontmatter all exist. Re-ran clean after the 2026-07-09 SKILL.md refresh.claude plugin validate ./claude plugin validate . --strict✔ Validation passed(both)claude plugin validate plugins/browse/... --strict✔ Validation passed(both)gemini extensions validate .Extension . has been successfully validated.codex plugin marketplace add <abs>→codex plugin list→ removebrowse@browserbase-pluginsresolved atplugins/browseplugin-marketplaceclone)Catalog OK, generatedbrowseentry resolvesskills: [{browse, ...}], nomcpServerskeygrokCLI itself isn't installed locally — not live-invocation-tested.npx plugins discover .(Vercel's Open Plugin CLI), before and after adding rootplugin.jsonFound 1 local plugin(s) — browse, 1 skillnode scripts/validate-template.mjsstill passes with the same 2 pre-existing benign warnings.GEMINI.md/SKILL.mddrift check: introduced a real content diff, then a CRLF-only diffValidation failed: GEMINI.md is out of sync...(exit 1). CRLF-only diff →Validation passed.andsync-gemini.mjs --checkreports in-sync (no rewrite).github/workflows/validate.yml, triggered by this PR's own commitsvalidatecheck:COMPLETED SUCCESSon every push after it was addedReal install-and-invoke (live, this session — not just validators)
claude plugin marketplace add→install→ headlessclaude -ptask ("navigate to example.com, report the title") against the actual repo filesclaude plugin detailsreportedMCP servers (0). Transcript shows onlySkill/Bashtool calls — the model shelled out tobrowse open ... --local, got back"title": "Example Domain", answered correctly. Zeromcp__*calls anywhere.gemini extensions install ~/Developer/browse-plugin→ headlessgemini --yolotask, same promptGEMINI.md), no MCP servers. Tworun_shell_commandcalls (browse open ... --local,browse stop), correct answer, zero MCP calls.codex plugin marketplace add→codex plugin add→ headlesscodex exec, same promptbrowse@browserbase-pluginsinstalled/enabled). Invocation blocked by this machine's localcodex loginhaving an expired OpenAI API key (401 invalid_api_key) — an environment issue unrelated to the plugin; the turn never started so no tool call was attempted. Needs a valid local Codex login to complete.All three live tests ran against the real, post-change repo files (not scratch copies), with full install → invoke → cleanup, and confirmed the working tree was left unmodified by testing.
Not proven / left for review
grokCLI invocation (not installed locally).codex loginis refreshed.🤖 Generated with Claude Code
Note
Medium Risk
Large behavioral change for installers (MCP and in-repo server removed; agents must use shell + external
browseCLI), but the diff is manifests, skills, and CI with no in-repo runtime code.Overview
This PR restructures the repo from a nested
plugins/browseTypeScript MCP/Playwright package (plus a separatefunctionsplugin) into a static, root-levelbrowsecatalog with no build step or bundled servers.Distribution model: New per-client manifests at the repo root (Claude, Cursor, Codex, Grok,
.agents, Open Pluginplugin.json, and Geminigemini-extension.json+GEMINI.md). Marketplaces now point"source"at"."instead ofplugins/browse; thefunctionsmarketplace entry is removed. Agents are expected to useskills/browse/SKILL.md(allowed-tools: Bash) and the globally installedbrowseCLI, not MCP tools.Removed: The entire
plugins/browseapp (mcp-server,bb-browser,package.json/lockfile,mcp.json, old skills/rules) andplugins/functions/. Release workflow no longer runsnpm ci/build inplugins/browse; it only validates and tarballs static files.CI and consistency: Adds
.github/workflows/validate.yml(validate on every PR andmainpush) plus atag-releasejob that tagsv{plugin.json version}when missing, which triggers release.scripts/validate-template.mjsnow enforcesGEMINI.md↔skills/browse/SKILL.mdsync and version alignment across manifests viasync-gemini.mjs/sync-version.mjs. Plugin release version is set to0.3.0(repo tags, not npm CLI version).Docs:
README.mdanddocs/add-a-plugin.mddescribe the single-plugin, CLI-only layout;CODEOWNERSadded for@browserbase/gtm.Reviewed by Cursor Bugbot for commit 03b38b7. Bugbot is set up for automated code reviews on this repo. Configure here.