Publishing
The pivit manifest, packaging a plugin for distribution, declaring permissions honestly, and getting it in front of users.
A Pivit plugin is an ordinary npm package with one extra field in its package.json. Nothing about distributing it is special until the store opens.
The manifest
Pivit reads a pivit block from your package.json to know what your plugin is called and where its entry point lives.
{
"name": "pivit-plugin-github",
"version": "1.0.0",
"type": "module",
"description": "Search repositories and issues from the command bar.",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": ["dist"],
"pivit": {
"id": "yourname.github",
"name": "GitHub",
"description": "Search repositories and issues",
"entry": "./dist/index.js",
"icon": "github",
"colour": "#6d50ed"
},
"dependencies": {
"@pivit/api": "^1.0.0"
},
"peerDependencies": {
"react": "^19.0.0"
}
}| Field | Required | Purpose |
|---|---|---|
pivit.id | Yes | Stable, globally unique identifier. Namespace it — yourname.plugin |
pivit.name | Yes | What users see in settings and above your commands |
pivit.entry | Yes | Path to the built entry point, relative to the package root |
pivit.description | No | One line, shown in settings |
pivit.icon | No | Icon name from Pivit's icon set |
pivit.colour | No | Tints your plugin's commands in the results list |
pivit.id is what Pivit keys everything on — installs, settings, per-command shortcuts. Changing it in a later version orphans everything a user configured, so pick it once.
Build output
Ship compiled JavaScript, not TypeScript sources. pivit.entry must point at a real file inside the files you publish, and type: "module" matters — plugins are loaded as ES modules.
npx tsc -p tsconfig.jsonDo not bundle react or @pivit/api into your output. Pivit supplies both at load time, and a second copy of React renders nothing at all. Keep React a peer dependency and leave @pivit/api external.
Versioning
Use semantic versioning against the behaviour users depend on. A command's id is part of your public surface — someone has bound a shortcut to it. Renaming or removing one is a breaking change; adding a command is not.
Permissions
Declare on each command what it reaches for outside Pivit:
permissions: ["internet", "clipboard.read"];Pivit shows these to the user before installing, and it also scans your code for the host calls each permission gates, adding anything it finds to what you declared. An omission does not become a silent capability — it becomes a discrepancy the user can see. Declare accurately.
Permissions are hierarchical: granting clipboard covers clipboard.read and clipboard.write, so ask for the narrowest thing that works.
Distributing today
The Pivit Store is not open yet. Until it is, users install plugins from a folder:
Build the plugin so dist contains the entry point named in your manifest.
Give users the folder — a release archive of the built package, or a repository they clone and build themselves.
They point Pivit at it under Settings → Plugins, and it loads like any other plugin.
Publishing to npm is worth doing now regardless. It gives you a version history and a canonical location, and it is what store installs will resolve against when the store opens.
Getting ready for the store
Nothing about your plugin needs to change for the store, but a few things make the transition painless:
- A namespaced, final
pivit.id - Accurate
descriptionandiconin the manifest - Honest permissions
- A README covering what the commands do and any setup, such as an API token
- Semantic versions, with breaking changes in major releases
A checklist before you ship
- The plugin loads from a clean folder, not just your development checkout
- Every command has a distinct
idand anamea stranger would understand resolvereturnsnullfor input your commands have nothing to do with- Settings have sensible defaults, so the plugin works before it is configured
- Failures surface through
showToastor anEmptyState, not a silent nothing - Output goes through
Lograther thanconsole