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.

ArchetypeDeclared inRuns asTypical language
Event/data[[events]], [[actions]], [[startup]]Short-lived process per triggerRust, bash+jq, bun/TS
TUI pane[[panes]]Long-lived full-screen app in a paneRust (ratatui)
Thin launcher[[actions]]Shim that execs a real appAny (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_json with preserve_order when you rewrite user config files so keys do not shuffle.
  • rusqlite (bundled) when the data lives in agent databases, like OpenCode’s opencode.db.
  • ureq for 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: popup is a session-modal singleton, not a pane. It has no pane id, emits no pane lifecycle events, receives all input including Escape, and returns ui_busy if 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

LanguageStrengthsCosts
RustSingle static binary, [[build]] is one command, type-safe JSON/SQLSlowest iteration; compile step on every install
bash + jqZero build, instant edits, great for glueParsing HERDR_PLUGIN_CONTEXT_JSON is painful; easy to be non-idempotent
bun/TSFast iteration, good JSON handling, single script fileRequires 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

PluginArchetypeSteal this
herdr-agent-usageevent/dataPer-provider data plumbing, startup/actions layout, credential scoping
herdr-pane-topic-syncevent/data (bun)State gating, ownership inference, *.renamed avoidance
herdr-automatic-renameevent/data (bash)Version-compat doctrine, idempotent shell reconcile
herdr-file-viewerTUI paneratatui structure, perf budgets, AI-collaboration docs
colliethin launcherFrozen command strings, ADR-driven docs, out-of-band service

All five have local mirrors under _repos/; the teardown notes in notes/ collect the details.