Pivit

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.

package.json
{
  "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"
  }
}
FieldRequiredPurpose
pivit.idYesStable, globally unique identifier. Namespace it — yourname.plugin
pivit.nameYesWhat users see in settings and above your commands
pivit.entryYesPath to the built entry point, relative to the package root
pivit.descriptionNoOne line, shown in settings
pivit.iconNoIcon name from Pivit's icon set
pivit.colourNoTints 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.json

Do 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 description and icon in 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 id and a name a stranger would understand
  • resolve returns null for input your commands have nothing to do with
  • Settings have sensible defaults, so the plugin works before it is configured
  • Failures surface through showToast or an EmptyState, not a silent nothing
  • Output goes through Log rather than console

On this page