Files
feedBack/.claude/skills/plugin-scaffold/SKILL.md
T
2026-06-18 00:40:43 -07:00

4.9 KiB

name, description
name description
plugin-scaffold Scaffold a Slopsmith plugin skeleton with capability-pipelines metadata. 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

Generates a minimum-viable Slopsmith plugin skeleton matching a requested shape. The output validates against schema/plugin.schema.json.

When to invoke

The user says one of:

  • "scaffold a new plugin called X"
  • "create a visualization plugin"
  • "new overlay plugin"
  • "plugin starter for settings"
  • "bootstrap a routes-only plugin"

If the plugin slug or type is missing, ask once.

Inputs

Arg Required Values Notes
id yes snake_case Becomes plugin's id field and directory name
name optional string Defaults to title-case of id
type yes visualization / overlay / settings-only / routes-only Determines which files get scaffolded

What to generate

Common to all types:

  • plugins/<id>/plugin.json — schema-valid manifest. Set id, name, version: "0.1.0", license: "AGPL-3.0-only", and standards: ["capability-pipelines.v1", "plugin-runtime-idempotent.v1"] by default (ask if a different license is desired).

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": "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": "none", "ownership": "multi-provider", "safety": "safe", "version": 1 } }
  • a matching "ui" contribution with a stable overlay id and redaction-safe label
  • 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:

  • "settings": { "html": "settings.html" } to manifest
  • "settings_schema" with a schema version and an empty packable_keys list
  • a "ui" settings contribution with a stable id
  • settings.html — empty form skeleton with explanatory comments
  • screen.js reading/writing localStorage keys prefixed with <id>_

type=routes-only — adds:

  • "routes": "routes.py" to manifest
  • a conservative "capabilities" declaration for the route's Slopsmith-facing workflow; if the domain is unclear, ask what capability domain the route owns, provides, requests, or observes before finalizing the scaffold
  • routes.py with def setup(app, context): that registers one example route and uses context["log"].info("plugin ready") (never print())
  • tests/test_<id>_routes.py — FastAPI TestClient stub

After scaffolding

Run validation locally:

python -c "import json,jsonschema; s=json.load(open('schema/plugin.schema.json')); jsonschema.validate(json.load(open('plugins/<id>/plugin.json')), s); print('OK')"

Then point the user at docs/PLUGIN_AUTHORING.md and the relevant contract doc for the type they chose.

Don'ts

  • Don't invent plugin-local manifest conventions. If the user wants something custom, either express it through existing capability-pipelines.v1 metadata or ask whether it should become a real schema field.
  • Don't scaffold a requirements.txt without confirming the deps. The plugin loader installs them on first load; an accidental dep slows everyone's startup.
  • Don't pre-fill localStorage keys without a prefix. Collisions across plugins are real.
  • Don't generate hidden side effects at import time (screen.js top level or routes.py top level). Keep all wiring inside the IIFE / setup().

Verification

The scaffolded plugin should pass:

pytest tests/test_plugin_schema.py::test_in_tree_manifest_validates -v
pytest tests/test_plugin_schema.py::test_in_tree_manifest_id_matches_directory -v