Publishing works like Raycast's store: fork this repository, add your extension folder, open a pull request. CI validates the structure, a maintainer reviews the code — plugins are programs that run on people's Macs, so the public review is the trust model — and the merge publishes it. The store index regenerates within minutes of merging.
extensions/<your-slug>/
├── store.json # {"categories": ["Developer Tools"], "keywords": [...]}
├── icon.png # 512×512 PNG, legible on light AND dark backgrounds
├── metadata/ # optional: 1–6 screenshots (PNG/JPEG)
├── README.md # required when your plugin needs setup (tokens, CLIs)
├── CHANGELOG.md # recommended — see below
└── plugin/ # the installable payload — ONLY this gets downloaded
└── my-plugin.tsx # one file (any supported language), or a directory
# plugin (package.json + index.tsx)
- Slug: lowercase letters, digits, dashes (
my-plugin). Unique in the repo. - Metadata lives in your plugin source, not in store.json: the
<notcho.title>,<notcho.desc>, and<notcho.author>comment tags (or thenotchoobject in a directory plugin'spackage.json). The store index extracts them — one source of truth for the store listing and the installed plugin. - Categories (1–2) from: Productivity, Developer Tools, System, Media, Communication, Finance, Data, Design, Fun, Other.
- Single-file plugins keep their filename on install (including
refresh intervals like
battery.30s.py) — filenames must be unique across the whole store. - Directory plugins: commit source only, never
node_modules. Declare dependencies inpackage.json; Notcho runs abun installafter download. Mention any external CLIs your plugin shells out to in the README. - Settings: declare user-configurable values as
<notcho.var>tags — users get a native settings form; never ask people to edit your source.
There are no version numbers. Every merged change to your folder is a release — users see an Update button automatically. To ship an update, open another PR.
Add entries under a ## {PR_MERGE_DATE} heading — CI replaces the
placeholder with the merge date when publishing:
## {PR_MERGE_DATE}
- Added a quiet mode setting.Valid slug + unique install name · store.json categories from the list · icon is a real 512×512 PNG · exactly one payload (allowlisted file type or a valid directory plugin) · title/desc/author tags present · no node_modules, symlinks, or dotfiles · ≤1 MB per file, ≤10 MB total.
Run them yourself before pushing: bun scripts/validate.ts
By submitting an extension you license it under MIT.