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
14 changes: 0 additions & 14 deletions .claude-plugin/marketplace.json

This file was deleted.

25 changes: 0 additions & 25 deletions .claude-plugin/plugin.json

This file was deleted.

21 changes: 0 additions & 21 deletions .codex-plugin/plugin.json

This file was deleted.

1 change: 0 additions & 1 deletion .cursor-plugin/marketplace.json

This file was deleted.

25 changes: 0 additions & 25 deletions .cursor-plugin/plugin.json

This file was deleted.

33 changes: 33 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: CI

on:
pull_request:
push:
branches: [main]

permissions:
contents: read

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
check:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7

- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
with:
node-version: "24"
cache: npm

- name: Install dependencies
run: npm ci

- name: Validate, build, and test
run: npm run check
45 changes: 45 additions & 0 deletions .github/workflows/publish-agent-bundle.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Publish Agent Bundle

on:
push:
branches: [main]
workflow_dispatch:

permissions:
contents: write

concurrency:
group: publish-agent-bundle
cancel-in-progress: true

jobs:
publish:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7

- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
with:
node-version: "24"
cache: npm

- name: Install dependencies
run: npm ci

- name: Build and verify artifact
run: npm run check

- name: Publish generated root
env:
GH_TOKEN: ${{ github.token }}
run: |
git -C artifact init --initial-branch agent-bundle-artifact
git -C artifact config user.name github-actions[bot]
git -C artifact config user.email 41898282+github-actions[bot]@users.noreply.github.com
git -C artifact add --all
git -C artifact commit -m "Publish Agent Bundle from ${GITHUB_SHA}"
git -C artifact remote add origin "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
git -C artifact push --force origin HEAD:agent-bundle-artifact
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
node_modules/
artifact/
dist/
.agent-bundle/
logs/*
!logs/.gitkeep
.DS_Store
60 changes: 0 additions & 60 deletions INSTALL.md

This file was deleted.

58 changes: 40 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ Regenerate: the screenshots come from a headless-Chrome tour; keep them at 1600

## Start

Requires Node 18+, 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).
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).

```bash
git clone https://github.com/ScriptedAlchemy/plugin-library.git
Expand All @@ -46,35 +49,51 @@ npm start

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/`.

## Use as a Grok Bot / Cursor plugin
## Install

The repo is itself a plugin (`plugin.json`, `.cursor-plugin/plugin.json`, `commands/`, `skills/`). From a checkout, install it with:
### Cursor from GitHub

```bash
node ./install.mjs
```
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.

That safe-copies the plugin into `~/.cursor/plugins/local/plugin-library` as a real directory, writes a small install receipt, and avoids the external symlink path Cursor skips. The script is portable across macOS and Linux because it uses Node path utilities rather than hard-coded platform paths. Reload Cursor after install.
For a local proof from a checkout:

Nothing in that install path requires the `agent-bundle` package or CLI for end users.
```bash
npm ci
npm run build
node artifact/install.mjs
npx --no-install agent-bundle doctor --from artifact --host cursor
```

If you want to add it directly from GitHub instead of copying a local checkout, use Cursor's Add Plugin flow with:
### Grok Bot plugin path

```text
https://github.com/ScriptedAlchemy/plugin-library
```
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.

Then in any agent session:
In a Cursor 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.

The command drives `bin/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.
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.

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 bin/plugin-library.mjs open [query] [--json] [--browser] # start if needed, print/open deep link
node bin/plugin-library.mjs status --json
node bin/plugin-library.mjs stop
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, and forbids them from calling `POST /api/apply` — applying to a bot stays a user click.
Expand Down Expand Up @@ -113,4 +132,7 @@ Explorer UI confirm counts as the user's Explorer confirm. No silent fleet bot w

## Data

Catalog JSON in `data/` (from Plugin Catalog), read server-side only. `node ./install.mjs` copies `data/` into the local plugin install so the sidecar can run from that install root. 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.
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.
23 changes: 23 additions & 0 deletions agent-bundle.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { defineConfig } from 'agent-bundle/config';

export default defineConfig({
bin: {
'plugin-library': './bin/plugin-library.mjs',
'plugin-library-install': './src/install-bin.ts',
},
assets: ['public', 'data'],
lib: false,
marketplace: true,
output: { distPath: 'artifact' },
plugin: {
description:
'Browse installed and marketplace plugins, read every skill in full, and hand one to a Grok Bot.',
name: 'plugin-library',
},
runtime: { node: '22.19.0' },
scripts: {
'plugin-library': './bin/plugin-library.mjs',
'plugin-library-server': './server.js',
},
targets: ['cursor', 'portable'],
});
6 changes: 4 additions & 2 deletions bin/plugin-library.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env node
/**
* plugin-library <start|stop|status|open [query]> [--json] [--browser] [--port N]
*
Expand All @@ -15,6 +14,9 @@ import { fileURLToPath } from "node:url";
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const PID_FILE = path.join(ROOT, "logs", "server.pid");
const LOG_FILE = path.join(ROOT, "logs", "server.out");
const SERVER_ENTRY = fs.existsSync(path.join(ROOT, "scripts", "plugin-library-server.mjs"))
? path.join(ROOT, "scripts", "plugin-library-server.mjs")
: path.join(ROOT, "server.js");

const args = process.argv.slice(2);
const flag = (name) => {
Expand Down Expand Up @@ -56,7 +58,7 @@ async function start() {
if (await healthy()) return { started: false, pid: readPid() };
fs.mkdirSync(path.dirname(LOG_FILE), { recursive: true });
const log = fs.openSync(LOG_FILE, "a");
const child = spawn(process.execPath, [path.join(ROOT, "server.js")], {
const child = spawn(process.execPath, [SERVER_ENTRY], {
cwd: ROOT,
env: { ...process.env, PORT: String(port) },
detached: true,
Expand Down
Loading