Plugin archetypes
Every quality plugin in the ecosystem fits one of three shapes. Picking the archetype first decides your dependency stack, your manifest, and most of your failure modes.
| Archetype | Declared in | Runs as | Typical language |
|---|---|---|---|
| Event/data | [[events]], [[actions]], [[startup]] | Short-lived process per trigger | Rust, bash+jq, bun/TS |
| TUI pane | [[panes]] | Long-lived full-screen app in a pane | Rust (ratatui) |
| Thin launcher | [[actions]] | Shim that execs a real app | Any (shim + real app) |
Event/data plugins
The workhorse archetype: react to herdr events, call back into the CLI, own a state file. No UI of your own; you change herdr’s state (renames, metadata) or push data to external services.
The proven Rust stack, from herdr-agent-usage:
[dependencies]
anyhow = "1.0"
clap = { version = "4.5", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
thiserror = "2.0"
ureq = { version = "2.12", features = ["json"] } # HTTP to external services
rusqlite = { version = "0.32", features = ["bundled"] } # read local agent DBs
toml = "0.8"
toml_edit = "0.22"
directories = "5.0"
- One binary, clap subcommands per manifest entry (
run --event NAME,sync,configure). serde_jsonwithpreserve_orderwhen you rewrite user config files so keys do not shuffle.rusqlite(bundled) when the data lives in agent databases, like OpenCode’sopencode.db.ureqfor blocking HTTP; failures logged, never propagated to herdr.
The same shape works without Rust. herdr-pane-topic-sync is one 460-line bun script; herdr-automatic-rename is bash+jq with a Makefile and CHANGELOG. The discipline matters more than the language: one idempotent reconcile entry per event, state-file gating, atomic writes.
TUI pane plugins
[[panes]] entrypoints open real terminal panes running full-screen apps. Placements: overlay (default), popup, split, tab, zoomed. After opening, split/tab/zoomed panes are ordinary panes: movable, resizable, and plugin ownership follows moves.
The proven Rust stack, from herdr-file-viewer:
[dependencies]
ratatui = "0.30"
crossterm = "0.29"
ansi-to-tui = "8" # render agent output with ANSI styling intact
ignore = "0.4" # fast, gitignore-aware file traversal
Gotcha:
popupis a session-modal singleton, not a pane. It has no pane id, emits no pane lifecycle events, receives all input including Escape, and returnsui_busyif Settings, Copy mode, or another modal is open. Design popups as transient, self-closing views.
Keep render loops tight: the app-grade plugins treat frame cost as a budget (file-viewer ships perf-budget test features for exactly this).
Thin launchers
A manifest whose actions launch a real application that lives outside herdr. collie is the exemplar: every action runs bash scripts/collie-ctl.sh <verb>, the shim resolves bun, lazily builds bin/collie, and execs it; a systemd —user bridge service outlives herdr restarts.
The hard-won lesson: manifest commands are a public API. Herdr versions before 0.8.0 invoke the action set cached at install time, so collie freezes its command strings byte-for-byte (ADR 0006). If you ever change how an action is invoked, old installs break silently. Freeze the entry command; iterate inside the app it launches.
Choosing a language
| Language | Strengths | Costs |
|---|---|---|
| Rust | Single static binary, [[build]] is one command, type-safe JSON/SQL | Slowest iteration; compile step on every install |
| bash + jq | Zero build, instant edits, great for glue | Parsing HERDR_PLUGIN_CONTEXT_JSON is painful; easy to be non-idempotent |
| bun/TS | Fast iteration, good JSON handling, single script file | Requires bun on the user’s machine; document it in the manifest preview |
For the lab suite the answer is Rust (see the suite conventions in distribution and versioning); use bash only for throwaway glue.
Read the exemplars
| Plugin | Archetype | Steal this |
|---|---|---|
| herdr-agent-usage | event/data | Per-provider data plumbing, startup/actions layout, credential scoping |
| herdr-pane-topic-sync | event/data (bun) | State gating, ownership inference, *.renamed avoidance |
| herdr-automatic-rename | event/data (bash) | Version-compat doctrine, idempotent shell reconcile |
| herdr-file-viewer | TUI pane | ratatui structure, perf budgets, AI-collaboration docs |
| collie | thin launcher | Frozen command strings, ADR-driven docs, out-of-band service |
All five have local mirrors under _repos/; the teardown notes in notes/ collect the details.