Boring Plugin Authoring
SkillFiles & storageCreate, extend, update, or install boring-ui workspace plugins — authoring new hot-reloadable user plugins and app-default plugins (React panels, file visualizers, surface resolvers, static server integrations, Pi/agent contributions), and installing existing or published plugins from npm/git/local sources. Use when the user asks to build, extend, configure, modify, add, or install a boring-ui plugin.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Boring Plugin Authoring skill
What this skill tells your AI
The instructions your AI receives, as published by hachej/boring-ui in packages/pi/skills/boring-plugin-authoring/SKILL.md and read by ahel’s review.
Triage — pick the path before doing anything
| Situation | Path |
|---|---|
| User wants to add a plugin that already exists (published npm package, git repo, local package) | Install it — do NOT scaffold. See references/install.md. |
| User wants a new plugin | Author it — scaffold first, then customize (the workflow below). |
| User wants to extend/compose an existing plugin in a new one | Author it (scaffold), then compose — see references/pi-extensions.md. |
If the new-plugin request is broad or underspecified ("make me a plugin", "build a
dashboard plugin"), ask for the missing product details before scaffolding or editing.
Do not silently invent the domain, data source, navigation behavior, or visual direction.
If the ask_user tool is installed, prefer it (structured form, with a final free-text
remarks textarea); otherwise ask in chat plus a final "Anything else / remarks?". Ask
only for decisions that affect implementation: purpose and target user; data source or
sample data; whether it needs a workspace page, a shared Dockview panel, a slash command, a file
opener/surface resolver, or some combination; main panels/views; visual tone; must-have
interactions or constraints.
Routing table — read the reference BEFORE writing code
| Task type | Read first |
|---|---|
| Install an existing/published plugin | references/install.md |
definePlugin surface, package.json shape, plugin-local deps, canonical front, design defaults | references/api.md |
| Slash commands / Ctrl+K commands that open a panel | references/patterns-commands.md |
Clickable links that open files/surfaces/panels (WorkspaceLink) | references/patterns-links.md |
| Choosing a nav surface (workspace-page vs shared-dockview vs resolver); file visualizers/readers | references/patterns-visualizers.md |
| Hot-reloadable agent tools (Pi extensions); extending/composing an existing plugin | references/pi-extensions.md |
Static boot-time server routes (boring.server); app-default package plugins | references/server.md |
Authoring workflow — always scaffold first
Don't write plugin files from scratch. The CLI scaffold produces a known-correct
package.json + front/index.tsx skeleton under .pi/extensions/<name>/.
Run it, then read the generated files, then customize. This guarantees the file
layout, API surface (definePlugin, registerPanel, etc.), and import paths are correct
— the parts agents most often invent or get wrong. Do NOT rewrite scaffolded files from
scratch — edit them in place.
The workspace agent puts the provisioned Node bin directory on PATH, provides the
boring-ui-plugin command, and exports BORING_AGENT_WORKSPACE_ROOT. Use
$BORING_AGENT_WORKSPACE_ROOT instead of host paths such as /home/...; in sandboxed
modes the runtime-visible workspace is /workspace. Outside the agent workspace without
that binary, use npx @hachej/boring-ui-plugin-cli scaffold <kebab-name> <workspace-root>.
Default to workspace-local. Never ask the user to choose. Always scaffold into
.pi/extensions/<name>/; only use ~/.pi/agent/extensions/ when the user explicitly asks
for a global plugin. Hot-reloadable agent behavior belongs in
pi.extensions / pi.skills / pi.systemPrompt. The scaffold does not create
server/index.ts: boring.server is advanced boot-time/static integration, not activated
by /reload for .pi/extensions user plugins (see references/server.md).
Steps:
- Run
boring-ui-plugin status --jsonvia the bash tool and stop ifworkspaceLocalPluginRootsisfalse— this runtime does not support local plugin roots; explain that and do not scaffold a hot-reloadable plugin. - Run the scaffold via the bash tool. Always target the current workspace root; the
second arg prevents writing into a parent repo if your shell cwd drifts:
If the scaffold says the plugin already exists, read the existing files directly and skip this step. The scaffold writes:boring-ui-plugin scaffold <kebab-name> "$BORING_AGENT_WORKSPACE_ROOT".pi/extensions/<name>/package.json— manifest withboring.frontandpi.systemPrompt.pi/extensions/<name>/front/index.tsx—definePluginconfig registering one workspace-page panel + command.pi/extensions/<name>/.gitignore— ignores runtime verifier/signature sidecars
- Read the generated files with the read tool.
- Edit them in place with the edit tool — do NOT rewrite from scratch. (Read the relevant routing-table reference before writing code.)
- If you add package dependencies, add them to this plugin's own
package.jsonand install inside the plugin directory (e.g.cd "$BORING_AGENT_WORKSPACE_ROOT/.pi/extensions/<kebab-name>" && npm install <dep>) — never from the workspace root. - Run
boring-ui-plugin verify <kebab-name> "$BORING_AGENT_WORKSPACE_ROOT". If it reports missing dependencies, install them in.pi/extensions/<kebab-name>/and re-run untilOK. - If the workspace UI is open, run
boring-ui-plugin test <kebab-name>(add--workspace <id>in workspaces mode and--panel-id <id>if the main panel is not<kebab-name>.panel). Fix render failures and re-run untilOK. If it reportsNO_BROWSER_CONNECTED, ask the user to open the workspace UI and rerun. - Tell the user to run
/reloadfor front/Pi asset changes. If you addedboring.server,/reloadis not enough: the workspace process must be statically composed with that package and restarted.
Mandatory verification loop (after every /reload)
/reload is driven by the user, not the agent and not Vite HMR. After each /reload:
- Call the
plugin_diagnosticstool to check for plugin/skill load errors./reloadsurfaces silent load failures (badSKILL.md, extension import errors, missingpi.skills/pi.extensionspaths) there. - Also re-run
boring-ui-plugin test <name>. Front import/render failures do not always show up the same way — they surface only as a diagnostic with source"plugin-front"/ aPLUGIN_FRONT_ERROR. A cleanplugin_diagnosticsalone does not prove the front loaded. - Read the reported errors, fix them, ask the user to
/reloadagain, and iterate until bothplugin_diagnosticsandboring-ui-plugin testcome back clean.
If the user reports a page reload, Invalid hook call, or resolveDispatcher() is null
after editing a plugin, suspect host Vite config first: .pi/extensions files must be
excluded from React Refresh and ignored by Vite HMR so the /reload bridge owns runtime
plugin updates.
File layout (do not put files elsewhere)
User-added plugins live under <workspace>/.pi/extensions/<name>/. Discovery roots:
- Workspace-local boring/Pi plugins:
$BORING_AGENT_WORKSPACE_ROOT/.pi/extensions/<name>/ - Global Pi plugins:
~/.pi/agent/extensions/(only when explicitly requested)
.pi/extensions/<name>/
├── package.json # manifest (boring.front, pi.systemPrompt, pi.extensions)
├── front/index.tsx # front factory (boring.front)
└── agent/index.ts # OPTIONAL — Pi extension, declared in pi.extensions
For .pi/extensions/<name>/ plugins (the hot-reload path this skill teaches), do NOT:
- Put files at the package root (
index.ts,index.js,index.tsxat the same level aspackage.json). - Create
src/,dist/,lib/,build/subdirectories — there is no compile step; the dev server transforms.tsxon the fly via Vite. - Run
npm init,tsc, or any build command inside the plugin dir. The scaffold'spackage.jsonalready hasprivate: trueand no scripts. - Run dependency installs from the workspace root. If a plugin needs a package, install it
inside
.pi/extensions/<name>/so the dependency is plugin-local. - Create a
tsconfig.jsoninside the plugin dir. - Create a
README.mdunless the user asks for one.
These rules apply to the hot-reload layout under
.pi/extensions/<name>/. Full npm-package plugins (for publishing — e.g.@hachej/boring-ask-user) live underplugins/<name>/and DO usesrc/+tsup+dist/; repo contributors useboring-ui-plugin create <name> --path pluginsinstead ofscaffold. Author/test global plugins workspace-local first. Only use.pi/extensionswhenboring-ui-plugin status --jsonreportsworkspaceLocalPluginRoots: true.
Minimal canonical definePlugin
import React from "react"
import { definePlugin } from "@hachej/boring-workspace/plugin"
function MyPane() {
return <div style={{ padding: 16 }}>Hello from my-plugin</div>
}
export default definePlugin({
id: "my-plugin", // contribution namespace; matching package name is recommended
label: "My Plugin",
panels: [
{ id: "my-plugin.panel", label: "My Plugin", placement: "workspace-page", component: MyPane },
],
commands: [
{ id: "my-plugin.open", title: "Open My Plugin", panelId: "my-plugin.panel" },
],
// Use placement: "shared-dockview" for artifact/detail/result panels.
})
Do NOT use defineFrontPlugin or createPlugin (they don't exist). For the full
definePlugin field table, package.json shape, plugin-local deps, and design-system
defaults, read references/api.md.
More detail
When the routing-table references don't cover your case, read:
- Plugin authoring reference — full package shape, conventions, hot-reload internals.
- Panel/front API reference —
PaneProps, parameter updates, workspace-page/shared-dockview layout API. - Agent/UI bridge reference — UI bridge commands and state.
Signals
- GitHub stars
- 46
- Forks
- 2
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
boring-plugin-authoring- Source
- github.com/hachej/boring-ui