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
125 changes: 49 additions & 76 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Plugin Library (sidecar browse UI)
# Plugin Library

Three-pane explorer for the Plugin Catalog: plugins → skills / agents / rules → the full `SKILL.md` rendered in a reading pane, sourced live from the on-disk plugin cache. "Send to a bot" picks a target from the live `gbot` roster and confirms before sending it directly.
A local explorer for your agent plugins. Three panes: the plugins on this machine and in the marketplace → a plugin's skills, agents and rules → the full `SKILL.md` rendered in a reading pane, straight from disk. "Send to a bot" picks a target from the live [`gbot`](https://github.com/ScriptedAlchemy/grok-bot-cli) roster and asks before anything is sent.

Ships as a Cursor / Grok Bot plugin: `/plugin-library` starts the server and opens it.

## Tour

Expand All @@ -18,123 +20,94 @@ Click a skill and the full `SKILL.md` opens in the reading pane. Front matter be

![A reference file](screens/04-reference-tab.png)

"Send to a bot" pulls the live roster from `gbot` (bots and groups, with their avatars), asks for confirmation, then calls `gbot send` directly.
"Send to a bot" pulls the live roster from `gbot` (bots and groups) and ends in one sentence you confirm. Nothing is sent until you click Send.

![Pick a bot](screens/05-pick-a-bot.png)

![Confirm](screens/06-confirm.png)

Every installed plugin gets the same treatment, not only pstack.

![Cursor Team Kit](screens/07-cursor-team-kit.png)

Marketplace listings show catalog copy and can be sent to a bot.
Marketplace listings show catalog copy and can be sent to a bot too.

![Marketplace](screens/08-marketplace.png)

Regenerate: the screenshots come from a headless-Chrome tour; keep them at 1600×1000 @2x so they match.
## Run from a checkout

## Start

Requires Node 22.19+, a Cursor plugin cache at `~/.cursor/plugins/cache`, and
the [`gbot`](https://github.com/ScriptedAlchemy/grok-bot-cli) CLI on `PATH` for
the bot picker (the explorer works without it; the picker reports the missing
roster).
Node 22.19+. The explorer reads `~/.cursor/plugins/cache` and `~/.cursor/plugins/local`; the bot picker needs `gbot` on `PATH` (without it the explorer still works and the picker says the roster is unavailable).

```bash
git clone https://github.com/ScriptedAlchemy/plugin-library.git
cd plugin-library
npm start
npm start # http://127.0.0.1:8787/
```

Or let the launcher manage the process:

```bash
node bin/plugin-library.mjs open [query] [--json] [--browser] # start if needed, print or open a deep link
node bin/plugin-library.mjs status --json
node bin/plugin-library.mjs stop
```

Default **8787** (`PORT=8787`). Binds loopback (`127.0.0.1`) by default because it shells out to `gbot` and serves cache files without auth; set `HOST=0.0.0.0` only on a trusted network. Open `http://127.0.0.1:8787/`.
`open pstack` resolves to `#/p/9717366`; `open why` to the skill inside it. `open` is idempotent: it reuses a running server or starts one detached (pid in `logs/server.pid`, output in `logs/server.out`).

Default port **8787** (`PORT`). Binds `127.0.0.1` because it shells out to `gbot` and serves cache files without auth; set `HOST=0.0.0.0` only on a network you trust. `CURSOR_PLUGIN_CACHE`, `CURSOR_PLUGIN_LOCAL` and `GBOT_BIN` override the two plugin directories and the CLI.

## Install
## Install as a plugin

### Cursor from GitHub
The `agent-bundle-artifact` branch is the generated, validated plugin root (built by CI on every push to `main`).

The `agent-bundle-artifact` branch is the generated, validated plugin root.
In Cursor Dashboard → Plugins → Team Marketplaces, choose **Add Marketplace**,
import `https://github.com/ScriptedAlchemy/plugin-library`, and select the
`agent-bundle-artifact` branch. Then install **Plugin Library** from Customize.
Team Marketplaces require a Cursor Teams or Enterprise plan.
If you previously imported `main`, re-import the marketplace and select the
generated branch.
**Cursor, from GitHub.** Dashboard → Plugins → Team Marketplaces → Add Marketplace, import `https://github.com/ScriptedAlchemy/plugin-library`, pick the `agent-bundle-artifact` branch, then install **Plugin Library**. (Team Marketplaces need a Teams or Enterprise plan.)

#### Without team marketplace
**Cursor, without a team marketplace.**

```sh
git clone --branch agent-bundle-artifact --depth 1 \
https://github.com/ScriptedAlchemy/plugin-library.git
git clone --branch agent-bundle-artifact --depth 1 https://github.com/ScriptedAlchemy/plugin-library.git
cd plugin-library && node ./install.mjs
```

Reload Cursor (`Developer: Reload Window`). From that clone directory, verify
with `npx --no-install agent-bundle doctor --from . --host cursor`.
Reload Cursor (`Developer: Reload Window`). `npx --no-install agent-bundle doctor --from . --host cursor` verifies the install.

For a local proof from a checkout:
**Grok Bot.** The same generated root is an Agent Plugins 1.0.0 package: `plugin.json` plus `skills/`. Hand it to the host's plugin flow; the `plugin-library` skill teaches the bot to answer skill questions from the read-only API of an already-running explorer. The `/plugin-library` slash command is Cursor-only.

**From a source checkout**, for a local proof:

```bash
npm ci
npm run build
npm ci && npm run build
node artifact/install.mjs
npx --no-install agent-bundle doctor --from artifact --host cursor
```

### Grok Bot plugin path

This project treats Grok Bot's plugin path as an Agent Plugins 1.0.0 consumer.
Provide the generated root from the `agent-bundle-artifact` branch to that
host-managed flow; its `plugin.json` and `skills/` are the portable projection.
The `gbot` CLI has no plugin-install command, so the local gate proves the
portable pack, not remote Grok Bot activation.
In the installed plugin the launcher lives at `scripts/plugin-library.mjs` (the command file uses `${CURSOR_PLUGIN_ROOT}/scripts/plugin-library.mjs`); in this repo it is `bin/plugin-library.mjs`.

In a Cursor agent session:
Then in an agent session:

- `/plugin-library` opens the explorer (embedded browser when the session has one, OS browser otherwise).
- `/plugin-library pstack` or `/plugin-library why` deep-links to that plugin or skill.

On Grok Bot, load the `plugin-library` skill and use its read-only API against
an already-running explorer; the slash command is Cursor-only.
Agents never call `POST /api/send`; the bundled command and skill both say so. Sending to a bot is a click you make.

The command drives `scripts/plugin-library.mjs`, which is idempotent: it reuses
a running server or starts one detached (pid in `logs/server.pid`, output in
`logs/server.out`) and waits for `/api/library` to answer.

```bash
node artifact/scripts/plugin-library.mjs open [query] [--json] [--browser]
node artifact/scripts/plugin-library.mjs status --json
node artifact/scripts/plugin-library.mjs stop
```

The bundled skill also tells agents how to answer skill questions from the read-only API without opening a window. Sending to a bot stays a user-confirmed click.

`npm test` builds the generated artifact, then runs the `node --test` suite
(installer lifecycle, front matter, cache dedupe, path containment,
direct `gbot send`, and an HTTP smoke test against a throwaway cache).

## Browse API (read-only)
## API

| Route | Returns |
|-------|---------|
| `GET /api/library` | `{ installed, marketplace, groups }`: catalog rows joined to the on-disk plugin cache (`~/.cursor/plugins/cache`), with skills, agents, rules and logo per installed plugin |
| `GET /api/local/<marketplace>/<slug>/doc/<path>` | `{ meta, markdown }` for any `.md`/`.mdc` inside that cached plugin |
| `GET /api/local/<marketplace>/<slug>/file/<path>` | any other file inside that cached plugin (logos, scripts). Symlinks and `..` that leave the plugin root are 404 |
| `GET /api/library` | `{ installed, marketplace, groups }`. *Installed* means present in this machine's plugin cache or local plugin directory; catalog rows supply copy, category and ids. Plugins with no catalog row get `local:<name>`-style ids. |
| `GET /api/local/<marketplace>/<slug>/doc/<path>` | `{ meta, markdown }` for any `.md`/`.mdc` inside that plugin |
| `GET /api/local/<marketplace>/<slug>/file/<path>` | any other file inside that plugin (logos, scripts). Symlinks and `..` that leave the plugin root are 404 |
| `GET /api/bots` | live bots + groups from `gbot bots list` / `gbot groups list` (20s cache; `?refresh=1` bypasses) |
| `POST /api/send` | `{ plugin_id, bot_ref, skill_id? }`, same-origin JSON only. Verifies the plugin, skill and target, then runs `gbot send <target> <message>`. Used by the UI after you confirm. |

Override the cache root with `CURSOR_PLUGIN_CACHE`, the CLI with `GBOT_BIN`.
## Data

## Send contract
`data/unified-catalog.json` is a snapshot of the Cursor marketplace (names, descriptions, categories, ids); `data/pstack.json` is the Catalog's suggested grouping for pstack's skills. Both are read server-side only and copied into the built plugin. Everything else (skills, agents, rules, logos) is read live from disk. When a plugin is cached twice (numeric id and slug), the copy Cursor marked `<hash>.installed` wins, then the highest version.

The UI calls `POST /api/send` only after the user confirms:
## Develop

```json
{ "plugin_id": "…", "bot_ref": "…", "skill_id?": "…" }
```bash
npm ci
npm run check # agent-bundle validate, tsc, build, node --test, artifact validate, packed-install smoke
```

The server verifies the plugin and optional skill against its catalog, then
runs `gbot send <target> <message> --json`. There is no queue, Applier, install
branch, or per-bot attach fallback.

## Data

Catalog JSON in `data/` (from Plugin Catalog) is copied to `assets/data/` in
the built plugin and read server-side only. pstack skill grouping:
`data/pstack.json`. When a plugin is cached twice (numeric id and slug), the
copy marked `<hash>.installed` wins, then the highest version.
Screenshots in `screens/` are 1600×1000 @2x from a headless-Chrome tour with a stub `gbot` roster.
177 changes: 0 additions & 177 deletions data/installed-index.json

This file was deleted.

Loading