The plugin registry for BashCut. There is no server: BashCut reads
registry.json from this repo, downloads plugin archives from this repo's GitHub Releases and checks
their SHA-256 before anything runs. Plugins use BashCut's out-of-process plugin API (2–8)
(Writing plugins).
| Plugin | What it does |
|---|---|
| Antigravity | Adds Google's Antigravity CLI as an agent terminal, with BashCut tools, skills, project conversations and its own bypass-permissions setting. Contributed by Luan Tran |
| Silence Markers | Adds a section marker at every quiet stretch in the selected clip |
| Whisper Captions | Captions from speech on Apple Silicon (Whisper large-v3 turbo via MLX, MIT): Vietnamese and about 100 languages, timed per word, split into even lines; captions.transcribe provider |
| VieNeu TTS | Vietnamese voiceover on this Mac (VieNeu-TTS v3 Turbo, Apache-2.0): 25 voices, voice cloning; voice.synthesize provider |
| AI Editor | BashCut's editing agent in the agent dock: chat with Claude, GPT, Gemini and others using your API key; it edits through BashCut's commands. Needs BashCut with plugin API 4; agent.chat provider |
| Pre-production | Agent skills for the work before the edit; scene-prompt turns a plot or scene idea into a cinematic prompt for AI video models (based on cinematic-video-prompt-engineer, MIT) |
registry.json catalog BashCut reads — generated, never edited by hand
publishers.json schemaVersion and publishers (source of registry.json)
plugins/<slug>/versions.json the plugin's listing at its last release and its published versions (source)
plugins/<slug>/plugin.json manifest (bashcut.plugin/1)
plugins/<slug>/bin/… entrypoint and helpers
plugins/<slug>/listing.json store listing: name and summary ({en, vi, …}), category, platforms, minAppVersion
plugins/<slug>/tests/ tests run by CI (not shipped)
plugins/<slug>/build.sh optional: builds compiled helpers or bundles (CI and package.py run it; not shipped)
plugins/<slug>/src/ optional: sources for build.sh (not shipped)
samples/<slug>/ examples for plugin authors (never published): samples/terminal-agent, samples/views-example
scripts/new-plugin.py creates a new plugin from a template (scripts/plugin-templates/)
scripts/plugin_manifest.py the manifest rules BashCut applies, and the category list
scripts/package.py zip + SHA-256 + signature; --register records the version in versions.json
scripts/build-registry.py generates registry.json from the sources (--check in CI)
Each release or yank writes only its own plugins/<slug>/versions.json, so changes to different plugins never touch
the same source file, and BashCut still downloads one registry.json (one request, one consistent snapshot).
scripts/build-registry.py rebuilds it deterministically; CI fails when it is out of date. A merge or rebase
conflict in registry.json is resolved by running the script again — the release workflow does that itself when
two releases race. The listing in versions.json is a snapshot of the released listing.json and manifest, so
unreleased changes on main never reach users.
{
"schemaVersion": 1,
"publishers": {"bashcut": {"name": {"en": "BashCut"}, "keys": [], "verified": true}},
"plugins": [{
"id": "bashcut.silence-markers", "name": {"en": "Silence Markers", "vi": "Đánh dấu im lặng"},
"summary": {"en": "Adds a section marker at every quiet stretch…", "vi": "Thêm mốc tại mọi đoạn im lặng…"},
"publisher": "bashcut", "author": {"name": "BashCut", "url": "https://github.com/dongnguyenvie"}, "category": "audio",
"capabilities": [], "actions": ["bashcut.silence-markers.mark"], "hooks": [],
"versions": [{
"version": "0.1.0", "apiVersion": 2, "minApiVersion": 2, "minAppVersion": "0.0.1",
"platforms": ["macos-arm64", "macos-x86_64"],
"url": "https://github.com/dongnguyenvie/bashcut-plugins/releases/download/silence-markers-v0.1.0/bashcut.silence-markers-0.1.0.zip",
"sha256": "…", "signature": null, "size": 3352, "downloadBytes": 0, "releasedAt": "2026-10-03"
}]
}]
}Display text (name, summary, and in manifests title, help, confirm) is a language map such as
{"en": "Silence Markers", "vi": "Đánh dấu im lặng"}; a plain string means English, and a map with several
languages must include en. Adding a language is adding a key — no new fields. package.py rejects the old
titleVi/nameVi style.
author ({"name", "url"}, url optional) is who wrote the plugin; package.py copies it from the manifest's
author. publisher is who signs and ships it (bashcut for everything in this repo), so a contributed plugin keeps
publisher: bashcut and names its contributor as author. BashCut shows By in Plugins; older versions ignore
the field.
Each archive holds one folder named after the plugin id. The registry keeps the newest 3 versions of each plugin.
signature is ed25519:BASE64 over the 32 raw bytes of the archive's SHA-256. BashCut checks it before
downloading and shows Signed by BashCut; a signature that matches no key is refused, an unsigned archive gets a
warning. The BashCut public key is compiled into the app (PluginSignature.firstPartyKeys) and mirrored in
publishers.bashcut.keys here for scripts/verify-registry.swift; the app ignores registry keys for bashcut.
- The private key is the
BASHCUT_SIGNING_KEYActions secret (base64 raw 32 bytes). An offline copy is in the maintainer's login keychain as "BashCut plugin signing key (ed25519)". release.ymlrefuses to publish without it, uploads<archive>.signext to the zip and verifies the registry.scripts/sign-registry.pysigns versions published before signing existed (it re-downloads and checks each archive first);scripts/sign.swiftis the signer both use.- Rotating: ship the new public key in a BashCut release first, then switch the secret; keep the old key in the app for one release cycle.
scripts/yank.py <id> <version> "<reason>" marks a version "yanked" in its versions.json and regenerates
registry.json; commit both and push. BashCut stops offering it,
tells users who have it why, and Updates offers the newest good version. --undo restores it. Never re-publish a
yanked version number.
scripts/new-plugin.py creates a working plugin to start from: the manifest, an entrypoint that already speaks the
protocol, smoke tests and a README.
scripts/new-plugin.py my-voice --template capability --capability voice.synthesize --name "My Voice"
scripts/new-plugin.py clip-tools --template action --lang shell
scripts/new-plugin.py my-agent --template chat-agent --lang node --private --out ~/code| Template | What you get |
|---|---|
capability |
A provider for voice.synthesize, captions.transcribe, audio.beats, audio.loudness or audio.sync (--capability), returning a valid placeholder result |
action |
A contributes.actions command that proposes a timeline edit (a marker at the playhead) |
hook |
contributes.hooks on export.finished (writes pluginData) and media.imported |
options |
Options of every type, including a secret, read by an action |
chat-agent |
An agent.chat session plugin: a tab in the agent dock that streams events and calls BashCut commands |
--lang picks the language: swift (default; build.sh compiles a universal binary, nothing to install for users),
shell (sh and macOS built-ins; not for chat-agent), node (plain ES modules; the install recipe downloads Node.js
when the Mac has none) or python (standard library; fine for private plugins, but the registry refuses a python3
dependency, see below). Your code goes in one handlers file; the protocol file next to it handles requests, errors,
progress, events and host calls.
The plugin goes to plugins/<slug> with a listing.json and an empty versions.json. --category (one of
CATEGORIES in scripts/plugin_manifest.py, which BashCut groups plugins by) goes in both plugin.json and
listing.json; scripts/build-registry.py --check refuses any other category. --private, or --out outside plugins/, makes a standalone plugin for people who keep it
to themselves; link it with scripts/dev-link.sh <folder>. The script checks the manifest against BashCut's rules
before writing, refuses an existing folder or a plugin id already in use, and prints the next steps: build, run the
tests, dev-link, Trust. scripts/tests/test_new_plugin.py generates every template in every language and runs its
tests, so the templates keep up with the API.
A plugin can teach agents how to use it (plugin API 7): --skill adds skills/<slug>/SKILL.md and
"contributes": {"skills": [{"path": "skills/<slug>"}]}. BashCut gives the skill to Claude, Codex and its other
agents while the plugin is trusted and turned on, as <plugin-id>:<name>. Write it like the agent kit's skills:
- Front matter
nameequal to the folder name, and adescription: what it is for, "Use when …", thenTriggers:with the words users really say (Vietnamese and English). - When to use the plugin, and when another tool is better.
- The exact
bashcutcommands in order (plugins run <action> --params '{…}', the capability's command such ascaptions generate,plugins optionfor the options that matter), and what to check afterwards. - Limits: what needs the user (Trust, Install Dependencies…) and what to say when it fails.
Keep it under 64 KB (a folder under 2 MB) and text only. package.py refuses a skill BashCut would leave out.
plugins/whisper-captions/skills/whisper-captions is an example.
A plugin can have its own panel in BashCut's left rail (plugin API 8): contributes.container (an SF Symbol icon and
a title) and contributes.views. A view is not UI code: BashCut asks view.render, the plugin answers with JSON
components (text, lists, buttons, inputs, images, audio…), BashCut draws them natively and sends what the user does
back as view.event. The panel also lists the plugin's actions as Tools, its skills, the plugins it requires and the
capabilities it uses.
Start from samples/views-example: every component, events and state, and how to reuse other
plugins from a view (generate speech with voice.speak through whichever voice plugin the user has, call another
plugin's capability with plugins.invoke). scripts/dev-link.sh samples/views-example to try it.
Installing a plugin is one click in BashCut; users never open Terminal, install Homebrew or fix a Python. So:
- A dependency is either a tool every Mac has (
package.pykeeps the list:sh,curl,afconvert,ditto,osascript…) or has an install recipe that downloads it intoBASHCUT_PLUGIN_DATA.python3,git,swiftand Homebrew tools are refused: on a fresh Mac they are missing or ask to install the Command Line Tools. - Plugins written in TypeScript ship one esbuild bundle and need only Node.js, which their recipe downloads from
nodejs.org when the Mac has none (see AI Editor:
build.shrunsnpm ciwith the lockfile;node_modulesis never shipped). Their tests usenode:testand run withnpm testwhen the folder has apackage.json. - Small plugins are compiled Swift (see Silence Markers:
build.shmakes a universal binary with AVFoundation, no runtime needed). Plugins that need Python bring their own withuv, like VieNeu. - Probes must exit 0 when the tool works (
afconvert -hexits 2, for example).
Run plugins/<slug>/build.sh first when the plugin has one. scripts/dev-link.sh <slug> symlinks a plugin into ~/Library/Application Support/BashCut/Plugins/. Open
Plugins in BashCut and choose Trust once; changes to plugin.json or the entrypoint ask for Trust again,
other files can change while you iterate. scripts/dev-link.sh <slug> --remove unlinks it. Plugins with heavy
models have a fake mode for tests and CI (for VieNeu, VIENEU_FAKE=1).
Any plugin folder links too: scripts/dev-link.sh ~/code/my-plugin. Samples link the same way: scripts/dev-link.sh samples/terminal-agent adds a sample agent CLI to the agent dock
(agent.terminal, plugin API 5). Its README explains how to turn it into a real CLI plugin such as Gemini CLI.
Versions stay 0.0.x while plugins are in beta.
- Change
plugins/<slug>, bumpversioninplugin.json, merge tomain. - Tag and push:
git tag silence-markers-v0.2.0 && git push origin silence-markers-v0.2.0. - The
Release pluginworkflow tests the plugin, zips it, creates the GitHub Release with the archive and its.sha256and.sig, re-downloads and checks it, then commits the signed version toversions.jsonand the regeneratedregistry.json.
A version is never re-published: bump it instead.
In BashCut, open Plugins › Browse, choose Install, review the source, checksum and dependency plan, and
approve. Updates appear under Updates; Installed › Remove uninstalls. Agents can run
bashcut plugins search and bashcut plugins install <id>, but only the user approves an install.
By hand: download the zip from Releases, unzip it into ~/Library/Application Support/BashCut/Plugins/, open
Plugins and choose Trust.
MIT. See LICENSE.