Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .muse-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"schemaVersion": 1,
"name": "browse",
"displayName": "Browserbase",
"version": "0.3.0",
"description": "Browser automation for AI agents: navigate, extract, screenshot, and interact with real web pages via Browse CLI.",
"compat": {
"source": "native",
"manifestDir": ".muse-plugin"
},
"capabilities": {
"skills": [
{
"id": "browse",
"path": "skills/browse/SKILL.md",
"enabledDefault": true
}
],
"commands": [],
"hooks": [],
"mcpServers": [],
"reminders": []
}
}
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# browse plugin marketplace

Install the [`browse`](https://github.com/browserbase/stagehand/tree/main/packages/cli) CLI as a native plugin in Claude Code, Cursor, Codex, Grok, and Gemini CLI.
Install the [`browse`](https://github.com/browserbase/stagehand/tree/main/packages/cli) CLI as a native plugin in Claude Code, Cursor, Codex, Grok, Muse Code, and Gemini CLI.

This repo has no application code. It's a set of static JSON manifests that let each agent marketplace install and SHA-pin the `browse` plugin, plus the skill that teaches the agent to drive `browse` from the shell.

Expand All @@ -13,6 +13,7 @@ This repo has no application code. It's a set of static JSON manifests that let
| `.cursor-plugin/marketplace.json` | Cursor marketplace |
| `.agents/plugins/marketplace.json` | Generic `.agents` marketplace |
| `.grok-plugin/plugin.json` | Grok plugin |
| `.muse-plugin/plugin.json` | Muse Code native plugin (uses the `.agents` marketplace above) |
| `gemini-extension.json` | Gemini CLI extension (`GEMINI.md` context file) |

See [`docs/add-a-plugin.md`](docs/add-a-plugin.md) for the full repo layout and how to update the plugin.
Expand All @@ -22,6 +23,7 @@ See [`docs/add-a-plugin.md`](docs/add-a-plugin.md) for the full repo layout and
- **Claude Code**: add this repo as a plugin marketplace, then install the `browse` plugin.
- **Cursor**: add the marketplace, then install `browse`.
- **Codex / Grok**: add the repo as a plugin marketplace and install `browse`.
- **Muse Code**: install the native plugin from a local checkout or the marketplace. See [Muse Code setup](docs/muse-code.md).
- **Gemini CLI**: install this repo as an extension (`gemini-extension.json` + `GEMINI.md`).

Then just ask your agent:
Expand Down
7 changes: 4 additions & 3 deletions docs/add-a-plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ If Browserbase ever needs a second distinct plugin, it belongs in its own dedica
├── .cursor-plugin/plugin.json # Cursor plugin manifest
├── .agents/plugins/marketplace.json # Generic .agents marketplace (path: ".")
├── .grok-plugin/plugin.json # Grok plugin
├── .muse-plugin/plugin.json # Muse Code native plugin (uses .agents marketplace)
├── gemini-extension.json # Gemini CLI extension
├── GEMINI.md # Gemini context file (CLI-only, no mcpServers)
├── plugin.json # Open Plugin spec manifest (vendor-neutral, e.g. `npx plugins add`)
Expand All @@ -28,7 +29,7 @@ If Browserbase ever needs a second distinct plugin, it belongs in its own dedica
└── sync-version.mjs # propagates plugin.json's version to the others; --check fails without writing
```

Every per-format `plugin.json`'s `"skills"` and `"logo"` fields are relative to repo root (`./skills/`, `assets/logo.svg`), and every root marketplace file's `"source"`/`"path"` is `"."`. The root `plugin.json` is a separate, vendor-neutral manifest ([Open Plugin spec](https://github.com/vercel-labs/open-plugin-spec) v1.0.0); it doesn't replace or override any per-client manifest and only needs updating when the plugin's name, version, or metadata changes.
Every per-format `plugin.json`'s `"skills"` and `"logo"` fields are relative to repo root (`./skills/`, `assets/logo.svg`), and every root marketplace file's `"source"`/`"path"` is `"."`. Muse uses `capabilities.skills[].path` to reference `skills/browse/SKILL.md` from the same repo root; see [Muse Code setup](muse-code.md). The root `plugin.json` is a separate, vendor-neutral manifest ([Open Plugin spec](https://github.com/vercel-labs/open-plugin-spec) v1.0.0); it doesn't replace or override any per-client manifest and only needs updating when the plugin's name, version, or metadata changes.

## Updating the skill

Expand All @@ -38,15 +39,15 @@ Every per-format `plugin.json`'s `"skills"` and `"logo"` fields are relative to

## Bumping the version

`plugin.json`'s `version` tracks this repo's own release tags (`v0.1.0`, `v0.2.0`, ...), the same as the git tags already used for GitHub Releases. The published Cursor marketplace listing is built by `release.yml`, which only runs on a tag push — merging to `main` alone doesn't update it. `plugin.json` is the single source of truth for the other four: `.claude-plugin/plugin.json`, `.cursor-plugin/plugin.json`, `.grok-plugin/plugin.json`, and `gemini-extension.json` must all match it exactly.
`plugin.json`'s `version` tracks this repo's own release tags (`v0.1.0`, `v0.2.0`, ...), the same as the git tags already used for GitHub Releases. The published Cursor marketplace listing is built by `release.yml`, which only runs on a tag push — merging to `main` alone doesn't update it. `plugin.json` is the single source of truth for the other five: `.claude-plugin/plugin.json`, `.cursor-plugin/plugin.json`, `.grok-plugin/plugin.json`, `.muse-plugin/plugin.json`, and `gemini-extension.json` must all match it exactly.

To cut a release, bump `plugin.json`'s `version` and run:

```bash
node scripts/sync-version.mjs
```

CI fails if any of the five drift out of sync. Once that PR merges to `main`, a second CI job (`tag-release`, in `.github/workflows/validate.yml`) detects the version change and pushes the matching `vX.Y.Z` tag automatically — no one runs `git tag` by hand. That tag push is what triggers `release.yml` to actually validate, package, and publish the release. If `plugin.json`'s version didn't change on a given push to `main`, or a tag for that version already exists, `tag-release` is a no-op.
CI fails if any of the six drift out of sync. Once that PR merges to `main`, a second CI job (`tag-release`, in `.github/workflows/validate.yml`) detects the version change and pushes the matching `vX.Y.Z` tag automatically — no one runs `git tag` by hand. That tag push is what triggers `release.yml` to actually validate, package, and publish the release. If `plugin.json`'s version didn't change on a given push to `main`, or a tag for that version already exists, `tag-release` is a no-op.

## Validate

Expand Down
112 changes: 112 additions & 0 deletions docs/muse-code.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Browse CLI for Muse Code

Give Muse Code a browser through [Browse CLI](https://github.com/browserbase/stagehand/tree/main/packages/cli). The plugin loads the shared Browse skill, which teaches Muse to navigate, inspect pages, fill forms, extract data, and take screenshots using shell commands. It also covers Browserbase cloud APIs, Functions, templates, and Browse.sh skills.

The plugin declares one skill and no hooks, MCP servers, commands, or reminders. Installing the plugin loads instructions; install the CLI separately. Local browsing needs Chrome or Chromium. Browserbase cloud browsing uses `BROWSERBASE_API_KEY` inherited from your shell or secret manager.

## Requirements

- Muse Code with plugin support. Tested with **1.3.0 (1.3.0-R3233.1)** and **Muse Spark 1.3 Contributor**.
- Node.js and npm, with `browse` available on the `PATH` used to launch Muse. The end-to-end test used **Browse 0.9.6** and **Node.js 24.19.0**.

```bash
npm install -g browse
browse --version
```

Authenticate Muse before asking it to run a task:

```bash
muse login
```

An existing `META_API_KEY` also works. Muse authentication is separate from the Browserbase API key used for remote browsers.

The tested Muse build gates plugin management behind an environment variable. If `muse plugins --help` reports that plugins are unavailable, enable it in your current shell:

```bash
export MUSE_EXPERIMENTAL_PLUGINS=1
```

## Install a local checkout

From this repository's root:

```bash
muse plugins validate . --json
muse plugins install . --scope user --json
muse plugins inspect browse --json
muse skills list --source plugin --json
```

The plugin should report `manifest_family: "native"`, `active: true`, and the effective skill `plugin:browse:browse`. The skills list should show that skill with `activation: "on"`.

Start a new Muse session after installation and ask:

> Use the Browse skill to open https://example.com in a local browser, read the page title, and close the browser session.

Or try:

- "Take a screenshot of localhost:3000."
- "Use Browserbase to extract the top five stories from Hacker News."
- "Find a Browse.sh skill for this website."

## Install from the marketplace

Once the Muse manifest is available on the repository's default branch:

```bash
muse plugins marketplace add browserbase https://github.com/browserbase/browse-plugin.git --json
muse plugins install browse@browserbase --json
muse plugins inspect browse --json
```

Muse accepts the existing `.agents/plugins/marketplace.json` catalog. The native `.muse-plugin/plugin.json` manifest points at the same `skills/browse/SKILL.md` used by the other clients.

To exercise marketplace installation before publishing, use the checkout's absolute path as the source instead of the Git URL:

```bash
muse plugins marketplace add browserbase "$PWD" --json
muse plugins install browse@browserbase --json
```

## Manage the plugin

```bash
muse plugins disable browse --json
muse plugins enable browse --json
muse plugins update browse --json
muse plugins remove browse --json
```

For marketplace installs, explicitly refresh the catalog before updating:

```bash
muse plugins marketplace update browserbase --json
muse plugins update browse --json
```

These operations manage the plugin package. Update the separately installed CLI with `npm install -g browse@latest`.

## Validation notes

Verified local and marketplace installation, skill discovery, inspect, enable/disable, update, and removal. Installation and discovery also passed in a fresh `node:24-bookworm` Docker container with fresh CLI installations and no mounted host home, config, or skill directories.

The authenticated end-to-end test ran Muse Spark 1.3 Contributor through Muse Code. The trace records a successful `read_skill` of `plugin:browse:browse`, followed by Browse CLI commands that opened the public Selenium web form in Browserbase, filled and read back a unique text value, submitted the form, verified `Form submitted` / `Received!`, and saved a screenshot. An independent verifier checked the submitted URL and confirmation before stopping the browser session.

Docker supplied isolation for this unattended test; Muse's nested shell sandbox and interactive approvals were disabled for that run. Normal installations retain Muse's usual approval and sandbox settings. The model-driven test covers remote Browserbase browsing; local browser launching was not tested in the container.

Muse 1.3.0 accepts this repository with `valid: true` and full skill compatibility. It reports two warnings because the repository carries several clients' manifests: `ignored-root-manifest` for the Open Plugin manifest, and `multiple-manifests` when it selects the native Muse manifest ahead of Claude/Codex. These do not prevent installation or skill discovery.

`muse skills validate skills/browse --json` also reports that `allowed-tools: Bash` is advisory. It grants no additional tool permissions; Muse's shell approval and sandbox settings still apply. Use Muse's normal approval flow if a browser command needs additional access.

If the CLI cannot start a browser, run `browse doctor --json` and follow its diagnostics. Use `--local` for localhost and local Chrome/Chromium, or `--remote` for Browserbase cloud sessions. Stop only the named session created for your task when finished.

For repository checks, run:

```bash
node scripts/validate-template.mjs
node scripts/sync-version.mjs --check
```

The native manifest participates in the repository's version synchronization and validation. The shared skill remains the single source of browser instructions.
49 changes: 49 additions & 0 deletions scripts/validate-template.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -357,12 +357,61 @@ async function main() {
}
}

await validateMusePlugin();
await validateGeminiSync();
await validateVersionSync();

summarizeAndExit();
}

async function validateMusePlugin() {
const manifest = await readJsonFile(
path.join(repoRoot, ".muse-plugin", "plugin.json"), "Muse plugin manifest"
);
if (!manifest) return;

if (manifest.schemaVersion !== 1 || manifest.name !== "browse") {
addError('Muse plugin must use schemaVersion 1 and name "browse".');
}
if (typeof manifest.displayName !== "string" || !manifest.displayName.trim()) {
addError("Muse plugin displayName is required.");
}
if (manifest.compat?.source !== "native" || manifest.compat?.manifestDir !== ".muse-plugin") {
addError('Muse plugin compat must declare source "native" and manifestDir ".muse-plugin".');
}

const capabilities = manifest.capabilities;
const skills = capabilities?.skills;
if (!Array.isArray(skills) || skills.length !== 1 ||
skills[0]?.id !== "browse" || skills[0]?.path !== "skills/browse/SKILL.md" ||
skills[0]?.enabledDefault !== true) {
addError("Muse plugin must enable the shared skills/browse/SKILL.md as its browse skill.");
} else {
await validateReferencedPath(repoRoot, "capabilities.skills", skills[0].path, "Muse browse");
}

const emptyFamilies = ["commands", "hooks", "mcpServers", "reminders"];
for (const family of emptyFamilies) {
if (!Array.isArray(capabilities?.[family]) || capabilities[family].length !== 0) {
addError(`Muse plugin capabilities.${family} must be an empty array for this skill-only plugin.`);
}
}
for (const family of Object.keys(capabilities ?? {})) {
if (family !== "skills" && !emptyFamilies.includes(family)) {
addError(`Muse plugin has an unexpected capability family: ${family}.`);
}
}

const marketplace = await readJsonFile(
path.join(repoRoot, ".agents", "plugins", "marketplace.json"), "Muse-compatible marketplace"
);
const entry = Array.isArray(marketplace?.plugins)
? marketplace.plugins.find((plugin) => plugin?.name === "browse") : null;
if (marketplace?.name !== "browserbase" || entry?.source?.source !== "local" || entry?.source?.path !== ".") {
addError('The .agents marketplace must list browse at the repository root (source "local", path ".").');
}
}

async function validateVersionSync() {
let sourceVersion;
try {
Expand Down
1 change: 1 addition & 0 deletions scripts/version-sync.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const VERSION_TARGET_PATHS = [
path.join(".claude-plugin", "plugin.json"),
path.join(".cursor-plugin", "plugin.json"),
path.join(".grok-plugin", "plugin.json"),
path.join(".muse-plugin", "plugin.json"),
"gemini-extension.json",
];

Expand Down
Loading