Submitting a plugin
August 26, 2026 · View on GitHub
A submission is a pull request that adds one line to plugins.json.
The plugin itself stays in your own repository, under your own licence. This
registry only carries the pointer and the metadata the app needs to show and
install it.
Work through the checklist before you open the PR. A reviewer works through the same twelve points, in the same order.
The checklist
-
The plugin lives in a public GitHub repository you own. The registry points at your repository; it never vendors a copy of your code.
-
The repository has a
plugin.jsonat the path you submit, and it validates againstschema/plugin.schema.json. The entry you add toplugins.jsoncarries the same values. -
The id is
com.github.<owner>/<slug>, with<owner>spelled exactly as the GitHub owner of that repository, and<slug>lowercase with hyphens. CI rejects an id whose owner does not match the repository owner. -
The id is new. Ids in
removed.jsonare retired for good and are never handed out again — not for a rename, not for a new owner. If you rename your plugin, you take a new id and the old entry is removed. -
The name does not say "Skales". That name is reserved for plugins published by
skalesapp. "for Skales" in your description is fine; a plugin called "Skales Something" is not. -
versionis semver, no leadingv, and it matches a tag or release in your repository.minAppVersionnames the lowest Skales version it runs on; do not guess — install that version and try it. -
descriptionis one sentence about what it does. Not what it will do, not what it promises, no marketing. 200 characters is the ceiling. -
classis honest.pagerenders a surface,agentanswers with a model,automationruns on a schedule or a trigger. Pick the one that describes the plugin's main job. -
permissionsis the smallest set that works.network: falseunless the plugin genuinely reaches a host outside the app;filesystem: "pluginRoot"rather than anything wider; list only the tools you actually call. A permission you do not need is a permission a reviewer will ask you to drop.What you may ask for. The calendar, the address book, e-mail and the messengers are all open to a plugin: a small CRM that reads the week's appointments, writes a follow-up to a contact and ticks it off is exactly what this surface is for. The protection is not a ban, it is the approval — any tool the app classes as
confirmproduces a real card naming the recipient and the content, and a person has to press it. Nothing auto-approves that card, on any machine, in any mode, including a run on a schedule.A tool that sends outward but carries no
confirmclass is refused by name rather than granted quietly, because there would be no card to press. Solist_calendar_eventsruns freely,create_calendar_event,send_email,whatsapp_send,slack_send_message,signal_send_messageandsend_discord_messagerun behind a card, and the Telegram send tools (which post to the paired chat without asking) are not available to plugins at all.What no plugin gets, whatever the manifest says. The shell and arbitrary code (
execute_command,run_script,register_tool); anything that writes skills or plugins, or lists them (create_skill,create_plugin,update_plugin,delete_plugin,list_plugins, and their siblings); delegation to a child run (dispatch_subtasks,delegate_swarm_task); remote control of the screen and keyboard (computer_*,screenshot_desktop); andask_user, because a schedule has nobody at the screen to answer. Listing one of these does not fail your submission, it simply never runs — but a reviewer will ask you to take it off the line. -
No secrets, no telemetry, no remote code loading. No API keys in the repository, no phoning home, and nothing that downloads and executes code at runtime. A plugin that fetches its own logic at runtime is out of scope for this registry.
-
If money is involved, say so in
business. Paid, freemium, who the vendor is, where the price is written. A paid plugin is welcome; a paid plugin that reads as free is not. -
You commit to maintaining it. You keep it working against current Skales releases, you answer issues in your repository, and you tell us when you stop. An unmaintained entry gets removed, and its id is retired with it.
-
If you sign your releases, name your public key. Optional
publicKeyfield on your entry (ed25519, base64 SPKI DER). Signing works without it - but the key is what turns "not modified since signing" into "published by you": with it, Skales shows your name on the green signature line at install time. Generate the pair with the Skales DevKit or any ed25519 tool; the private half never leaves your machine.
Opening the PR
- Add your entry at the end of
plugins.json. One entry per PR. addedAtis the date you open the PR,YYYY-MM-DD.- Do not touch
featured.json— that list is curated by maintainers. - Do not touch
removed.json— it is append-only and maintainer-only. - CI runs
scripts/validate.js. Run it yourself first:
npm install --no-save ajv@8
node scripts/validate.js
What review looks at
CI covers the mechanical rules: schema, id shape, owner match, retired ids, reserved name, one entry per PR. A human reads the plugin: what it does, what it touches, whether the permissions match the code, and whether the repository looks maintained. Expect questions. Expect to be asked for less than you asked for, on permissions.
After it is merged
The app picks the registry up on its next refresh. If you ship a new version,
open a small PR that bumps version (and minAppVersion, if it moved) on your
entry — everything else on the line stays as it is.