Skip to content

About

Official plugin registry for BashCut: signed plugins such as Whisper Captions, VieNeu TTS (Vietnamese voiceover), Silence Markers and the Director chat agent, served from registry.json and GitHub Releases.

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Latest commit

 

History

318 Commits

Folders and files

Repository files navigation

BashCut plugins

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
Image Generation AI illustrations with your own Gemini or OpenAI key (or an OpenAI-compatible local server): every scene of a video in one batch, one locked style and the same character from reference pictures; image.generate provider
Translate Captions Vietsub with your own Gemini or OpenAI key: translates captions keeping their timing, covers subtitles burned into the footage (band or zoom), and adds a Vietsub TikTok batch preset; without a key the agent translates
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
Vlog Recipes for vlogs, one agent skill per topic (travel, food, daily, product review, product ad, talking head, podcast clips, tutorial) as sourced sample ranges the agent turns into an edit plan and review profile, plus ideate, plan, hook-script, publish and scene-prompt (AI video prompts, based on cinematic-video-prompt-engineer, MIT); a library pack of vlog text styles, stickers and transitions

Layout

registry.json                 catalog BashCut reads — generated, never edited by hand
publishers.json               schemaVersion and publishers (source of registry.json)
bundles.json                  plugin bundles such as Recommended (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,
                              samples/review-check-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)

Why registry.json is generated

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.

Registry format

{
  "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.

Bundles

bundles.json lists plugins installed together after one approval: Plugins › Browse › Recommended in BashCut, bashcut plugins install --bundle starter, and the agent kit's /bc:setup. The approval lists every plugin with a checkbox (default is whether it starts checked), its download size and capabilities; BashCut then downloads, verifies, sets up and trusts each one in turn, like a single install.

{"bundles": [{
  "id": "starter", "name": {"en": "Recommended", "vi": "Gói đề xuất"}, "summary": {"en": "…", "vi": "…"},
  "plugins": [{"id": "bashcut.whisper-captions", "default": true}, {"id": "bashcut.antigravity", "default": false}]
}]}

Edit it by hand and run scripts/build-registry.py, which copies it into registry.json. It refuses a bundle that names a plugin the registry does not have or one from an unverified publisher, since one approval covers them all. Plugins that do not run on a Mac (platform, BashCut version) are shown greyed out and skipped. BashCut versions without bundles ignore the field.

Signatures

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_KEY Actions secret (base64 raw 32 bytes). An offline copy is in the maintainer's login keychain as "BashCut plugin signing key (ed25519)".
  • release.yml refuses to publish without it, uploads <archive>.sig next to the zip and verifies the registry.
  • scripts/sign-registry.py signs versions published before signing existed (it re-downloads and checks each archive first); scripts/sign.swift is 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.

Withdrawing a version

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.

Writing a plugin

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
scripts/new-plugin.py my-panel --template views --lang node
scripts/new-plugin.py my-skills --template skills
scripts/new-plugin.py party-pack --template library
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
views A rail panel (contributes.container) with a panel view, a dock tab, a sheet and a window (a split with a nav and a grid), an action that opens the sheet, and the request loop with call, wait_job / waitJob and render helpers (see Panels and views); plugin API 11, so listing.json asks for BashCut 0.0.25
skills A skill pack without code: skills/<slug>/SKILL.md and no entrypoint (host feature skillsOnly, API 7)
library A library pack without code: packs/<slug>/pack.json with a sound (a generated WAV), a sticker, a text style, a look and a transition, no entrypoint

--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 or views), 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). skills and library have no code and take no --lang: BashCut never runs them, Trust still pins every file, and an app older than the skillsOnly feature lists them as invalid. 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.

Agent skills

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 name equal to the folder name, and a description: what it is for, "Use when …", then Triggers: with the words users really say (Vietnamese and English).
  • When to use the plugin, and when another tool is better.
  • The exact bashcut commands in order (plugins run <action> --params '{…}', the capability's command such as captions generate, plugins option for 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.

Panels and views

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.

scripts/new-plugin.py my-panel --template views --lang swift|node|python makes one to start from: a panel view (state, a host call while the view shows progress, opening the sheet and the window), a dock tab listing the project's media (rows drag onto the timeline with file; a row button measures loudness through another plugin with plugins.invoke and wait_job), a sheet form that closes itself, and a window ("location": "window", API 11) whose whole body is a three-column split: a nav of media kinds, a grid of the media whose tiles drag onto the timeline, and the selected tile's details with a button that places it at the playhead (media.place). Smoke tests answer the host calls. A window view needs minApiVersion 11 (older apps refuse the manifest); for a plugin that must run on an older BashCut, drop the browse view and lower minApiVersion to 8.

samples/views-example shows 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, remember the choice in the plugin's own option with plugins.option, call another plugin's capability with plugins.invoke), plus marks on the timeline (plugins.set-overlays) and redraws outside a request (viewUpdate lines). scripts/dev-link.sh samples/views-example to try it.

Newer parts are host features that the session hello lists in features: views.colorPicker and views.fontPicker (colour well, font menu), views.drag, options.set, viewUpdate, overlays, inspector.when, voice.maxTakes. Read them from the hello (HOST_FEATURES / hostFeatures in the templates' protocol file) and fall back, for example to a textField, when one is missing. List a feature in plugin.json features only when the plugin cannot work without it: an app that lacks it then refuses the whole plugin. plugins/hyperframes (its Inspector view) does this. scripts/plugin_manifest.py view_problems(answer) checks a view answer the way BashCut does.

A plugin can also add its own checks to BashCut's review (plugin API 9): a review.check provider gets the project and returns issues (severity, frames, an optional fix) that review run lists next to the built-in ones, marked with the plugin. Start from samples/review-check-example: brand fonts and a call to action at the end, in one Python file.

Rules for users who are not developers

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.py keeps the list: sh, curl, afconvert, ditto, osascript…) or has an install recipe that downloads it into BASHCUT_PLUGIN_DATA. python3, git, swift and 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.sh runs npm ci with the lockfile; node_modules is never shipped). Their tests use node:test and run with npm test when the folder has a package.json.
  • Small plugins are compiled Swift (see Silence Markers: build.sh makes a universal binary with AVFoundation, no runtime needed). Plugins that need Python bring their own with uv, like VieNeu.
  • Probes must exit 0 when the tool works (afconvert -h exits 2, for example).

Testing locally

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.

Publishing

  1. Change plugins/<slug>, bump version in plugin.json, merge to main.
  2. Tag and push: git tag silence-markers-v0.2.0 && git push origin silence-markers-v0.2.0.
  3. The Release plugin workflow tests the plugin, zips it, creates the GitHub Release with the archive and its .sha256 and .sig, re-downloads and checks it, then commits the signed version to versions.json and the regenerated registry.json.

A version is never re-published: bump it instead.

Installing

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.

License

MIT. See LICENSE.

About

Official plugin registry for BashCut: signed plugins such as Whisper Captions, VieNeu TTS (Vietnamese voiceover), Silence Markers and the Director chat agent, served from registry.json and GitHub Releases.

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages