Limits
What the plugin API cannot do today.
What a plugin can and cannot put on screen
| Where | How |
|---|---|
A slash command under / | commands.add |
| A row in the Actions menu on a page’s own screen | commands.add, from a plugin the marketplace files under Actions |
| Something inside a block | macros.add, as {{name args}} |
| A panel in the sidebar | panels.add, shown with panels.open |
A screen of its own at #/x/<slug> | views.add; with page: true, also a tab beside Outline on a page’s own screen |
| A link under Plugins in the More sheet | nav.add |
| A line at the bottom of the screen | notice |
There is no API for a toolbar button, a settings section, a dialog, an entry in the search palette, or how an ordinary block is rendered. A macro’s element is discarded without notice unless it carries a dispose function.
What it cannot do to the graph
- Delete, move or reparent a block, or delete or rename a page. A plugin that needs a block gone sets its content to nothing.
- Touch whiteboard shapes or attachments; neither has an API.
- Run a
{{query}}, or search: walk blocks.all and filter.
Not exported, not sandboxed
- Installed plugins live in localStorage on each device and are not in a backup export. Sync installed plugins keeps the list itself the same across devices; what a plugin keeps for itself stays where it was written.
- There is no sandbox and no permission prompt: a plugin can do anything the page can. The marketplace’s reads/changes lines are a disclosure, not a fence.
- A plugin has nowhere of its own to keep state; use localStorage under your own key.
- The API is not versioned. A plugin written against today’s API may break on a later Twig; the ones in the catalog are rebuilt with every release, which is the one way to be sure.
If a plugin fails to load
A failure is recorded against the entry on the Plugins screen rather than thrown away. The entry stays, switched on, with the error beside it; fixing the URL means removing it and adding it again.
The API as it stands
One member has been added since 0.17.0, blocks.words, and everything in these pages is what they do today. Two were corrected after 0.48.0: pages.backlinks could return a block twice and now returns each once, and blocks.insertChild now clamps a position outside the list. Where it is awkward — no storage, styles that outlive the plugin that added them, a panel that needs three listeners to follow the page, no delete or move — the examples show the way around it. What to add is proposed in the repository, in docs/superpowers/specs/2026-09-23-plugin-api-v2.md.