docs: make plugin guidance capability-first

Signed-off-by: barlind <tobias@barlind.se>
This commit is contained in:
barlind
2026-06-18 00:40:35 -07:00
committed by Bret Mogilefsky
parent 26dba55d7e
commit 7d0021d04a
12 changed files with 97 additions and 377 deletions
+4 -5
View File
@@ -1,6 +1,6 @@
---
name: slopsmith-reviewer
description: Plugin-aware code reviewer for Slopsmith. USE WHEN reviewing plugin changes, auditing a plugin against the manifest contract, checking that a plugin uses load_sibling / context["log"] / scoped shortcuts correctly, or verifying that a `plugin.json` matches the schema and the directory it lives in. Returns a structured pass/fail report with specific file:line citations.
description: Plugin-aware code reviewer for Slopsmith. USE WHEN reviewing plugin changes, auditing a plugin against the manifest contract, checking that a plugin uses capability metadata / load_sibling / context["log"] correctly, or verifying that a `plugin.json` matches the schema and the directory it lives in. Returns a structured pass/fail report with specific file:line citations.
tools: Read, Grep, Glob, Bash
model: sonnet
---
@@ -39,10 +39,9 @@ Run each item; structure the output as `PASS` / `FAIL` / `N/A` with file:line ci
6. **Backend logging.** Grep `plugins/<id>/*.py` for `print(`, `traceback.print_exc(`, `logging.getLogger(`. Suggest `context["log"]` replacements.
7. **Sibling imports.** If `routes.py` exists and grep finds bare `from <module> import` for any sibling Python file in the plugin dir, flag and recommend `context["load_sibling"]`.
8. **Frontend IIFE.** If `script` exists, check the top of the file isn't running top-level statements that leak to global scope. Wrapping in `(function () { 'use strict'; ... })();` is the convention.
9. **`playSong` wrapper discipline.** If the script reassigns `window.playSong`, confirm it calls the original and `await`s it.
10. **Shortcut scope discipline.** If the script calls `window.registerShortcut`, confirm `scope` is set (not relying on the `'global'` default) and that an `unregisterShortcut` / `panel.clearShortcuts()` cleanup path exists when the plugin can be torn down.
11. **`localStorage` prefix.** Grep for `localStorage.` usage; keys must start with `<plugin_id>`.
12. **`settings.server_files` paths are safe.** Each entry must be a relpath — no leading `/`, no `..`, no backslashes. The schema enforces this but call it out.
9. **Capability-first frontend integration.** If the script wraps host globals or polls another plugin's globals, flag it as a capability gap unless the PR explicitly documents why no active domain can model the integration yet.
10. **`localStorage` prefix.** Grep for `localStorage.` usage; keys must start with `<plugin_id>`.
11. **`settings.server_files` paths are safe.** Each entry must be a relpath — no leading `/`, no `..`, no backslashes. The schema enforces this but call it out.
## Output format
+2 -5
View File
@@ -12,7 +12,7 @@ These rules apply only when editing files under `plugins/**`. They encode the co
## Manifest
- **`plugin.json` is required** and must validate against [`schema/plugin.schema.json`](../../schema/plugin.schema.json). Required fields: `id`, `name`. The `id` must match the parent directory name (the loader keys discovery by directory; drift breaks plugin lookup).
- **Capability-aware plugins declare intent** with `standards: ["capability-pipelines.v1"]` and redaction-safe `capabilities` / `ui` metadata. Legacy fields still work, but don't strip or reject native metadata when editing manifests.
- **Capability-aware plugins declare intent** with `standards: ["capability-pipelines.v1"]` and redaction-safe `capabilities` / `ui` metadata. Treat these declarations as the plugin's primary contract with Slopsmith.
- **License must come from the curated allowlist** if the plugin is intended for the curated list. See [`CONTRIBUTING.md`](../../CONTRIBUTING.md) "Plugin licensing".
- **`type: "visualization"`** requires a `script` field exporting `window.slopsmithViz_<id>`. See [`docs/plugin-visualization-contracts.md`](../../docs/plugin-visualization-contracts.md).
@@ -25,10 +25,7 @@ These rules apply only when editing files under `plugins/**`. They encode the co
## Frontend (`screen.js`)
- **Wrap in an IIFE** — `(function () { 'use strict'; ... })();`. Frontend scripts share global scope; leaking variables collides with other plugins.
- **Hook `window.playSong` carefully** — always call the original, always `await` it. Wrappers run outermost-first; awaiting yields to the event loop and WebSocket messages can arrive before the outer wrapper finishes setup. Use `highway.getSongInfo()` as a fallback rather than relying solely on `_onReady`.
- **Hook `window.showScreen`** — clean up your plugin's state when the user leaves the player screen.
- **Use `window.slopsmith.emit` / `on`** for cross-plugin communication. Don't poll other plugins' globals.
- **Register shortcuts with `window.registerShortcut({ key, scope, handler })`** and clean up with `window.unregisterShortcut(key, scope)` — pass the same scope you registered with (default `'global'` won't match `'player'` / `'plugin-*'`). For panel-scoped registries, prefer `panel.clearShortcuts()`. See [`docs/plugin-keyboard-shortcuts.md`](../../docs/plugin-keyboard-shortcuts.md).
- **Prefer capability commands, events, and provider APIs** for app coordination. Don't add new private global wrappers or cross-plugin polling; if the needed domain is missing, call that out as a capability gap.
## State and config
+4 -4
View File
@@ -1,6 +1,6 @@
---
name: plugin-scaffold
description: Scaffold a new Slopsmith plugin skeleton. USE WHEN the user asks to create a new plugin, scaffold a plugin, bootstrap a plugin, new visualization plugin, new overlay plugin, new settings-only plugin, plugin starter, plugin skeleton. Args needed - plugin slug (snake_case) and type (visualization / overlay / settings-only / routes-only). Generates plugins/<id>/ with plugin.json, screen.js, and optional routes.py / settings.html / Playwright test stub matching the requested type.
description: Scaffold a new capability-aware Slopsmith plugin skeleton. USE WHEN the user asks to create a new plugin, scaffold a plugin, bootstrap a plugin, new visualization plugin, new overlay plugin, new settings-only plugin, plugin starter, plugin skeleton. Args needed - plugin slug (snake_case) and type (visualization / overlay / settings-only / routes-only). Generates plugins/<id>/ with plugin.json, capability-pipelines metadata, screen.js, and optional routes.py / settings.html / Playwright test stub matching the requested type.
---
# plugin-scaffold
@@ -34,15 +34,15 @@ If the plugin slug or type is missing, ask once.
**`type=visualization`** — adds:
- `"type": "visualization"` and `"script": "screen.js"` to manifest
- `"capabilities": { "visualization": { "roles": ["provider"], "operations": ["renderer.create", "renderer.destroy", "renderer.inspect"], "mode": "active", "compatibility": "shim-allowed", "ownership": "multi-provider", "safety": "safe", "version": 1 } }`
- `"capabilities": { "visualization": { "roles": ["provider"], "operations": ["renderer.create", "renderer.destroy", "renderer.inspect"], "mode": "active", "compatibility": "none", "ownership": "multi-provider", "safety": "safe", "version": 1 } }`
- `screen.js` exporting `window.slopsmithViz_<id> = function () { return { contextType: '2d', init(canvas, bundle) { this.ctx = canvas.getContext('2d'); }, draw(bundle) { /* TODO */ }, destroy() {} }; };` plus a static `matchesArrangement` example commented out
- `tests/browser/<id>.spec.ts` — Playwright stub that loads the app and asserts the plugin's factory is registered
**`type=overlay`** — adds:
- `"script": "screen.js"` to manifest (no `type` declared — overlays don't use the picker)
- `"capabilities": { "ui.player-overlays": { "roles": ["provider"], "mode": "active", "compatibility": "shim-allowed", "ownership": "multi-provider", "safety": "safe", "version": 1 } }`
- `"capabilities": { "ui.player-overlays": { "roles": ["provider"], "mode": "active", "compatibility": "none", "ownership": "multi-provider", "safety": "safe", "version": 1 } }`
- a matching `"ui"` contribution with a stable overlay id and redaction-safe label
- `screen.js` scaffolding a navbar toggle, an own-canvas + own-rAF loop reading `highway.getNotes()` / `getChords()` / `getTime()`, and respecting `highway.isDefaultRenderer()` if using `highway.project` / `fretX`
- `screen.js` scaffolding an own-canvas + own-rAF loop for the declared overlay contribution, and respecting renderer ownership if it uses highway geometry helpers
- `tests/browser/<id>.spec.ts` — toggle on / off test
**`type=settings-only`** — adds: