Custom herdr plugins, built and linked from one repo.
just # build every plugin and link it with herdr
just test # run all tests
just dev-one command-palette # build + link one plugin
just ci # fmt + clippy + tests, same as the PR gatePull requests into main must pass CI (.github/workflows/ci.yml: fmt,
clippy with warnings denied, and tests on Linux and macOS) before merging.
herdr launches plugin commands fresh each time, so after just the next
invocation uses the new build. Relinking is safe; it refreshes the manifest.
crates/herdr-client— typed client for herdr's socket API, shared by plugins.plugins/<name>/herdr-plugin.toml— one directory per plugin.scripts/plugins.py— runs each manifest's[[build]]commands and links it.
- Create
plugins/<name>/herdr-plugin.toml(see the herdr plugin docs). - For a Rust plugin, add
plugins/<name>tomembersin the rootCargo.tomland usecargo build --release --target-dir targetas its build command. - Run
just.
Editor-style fuzzy palette: workspaces, tabs, agents, built-in herdr commands, other plugins' actions, and your own commands. Frequently and recently used items rank first.
~/.config/herdr/config.toml:
[[keys.command]]
key = "cmd+k"
type = "plugin_action"
command = "jacob.command-palette.open"
description = "command palette"Ghostty binds cmd+k itself, so free it in ~/.config/ghostty/config:
keybind = cmd+k=unbind
↑/↓ or ctrl-p/ctrl-n move · enter runs · esc closes (or leaves a
prompt) · ctrl-u clears · fzf syntax works: ^start, end$, 'exact, !not.
~/.config/herdr/plugins/config/jacob.command-palette/commands.toml
(herdr plugin config-dir jacob.command-palette prints the directory):
[[commands]]
title = "Deploy staging"
run = "just deploy staging" # via sh -c
keywords = ["ship"] # optional extra match words
cwd = "~/project" # optional; default is the focused pane's cwd
[[commands]]
title = "Open repo in browser"
argv = ["gh", "browse"] # alternative to `run`; exactly one is requiredCommands run detached, so use them to launch things rather than for interactive programs.
~/.local/state/herdr/plugins/jacob.command-palette/palette.loghas source failures and action errors.herdr plugin log list --plugin jacob.command-paletteshows launch failures.
Popup git status for the focused pane's repository: branch, upstream ahead/behind, stash count, and staged/unstaged/untracked/conflicted files. Stage, diff, commit, switch or create branches, and stash without leaving the pane you're in.
[[keys.command]]
key = "cmd+g"
type = "plugin_action"
command = "jacob.git-glance.open"
description = "git glance"| View | Keys |
|---|---|
| Status | j/k or ↑/↓ move · space stage/unstage · a stage all · u unstage all · enter/d diff · c commit · b branches · z stashes · S stash changes · r refresh · q/esc close |
| Diff | j/k scroll · space/b page · g/G top/bottom · esc back |
| Branches | type to filter · enter switches, or creates the typed name when nothing matches · esc back |
| Stashes | enter/p pop · a apply · x drop (asks first) · esc back |
S stashes untracked files too. Commits use git commit -m, so hooks run but
no editor opens. A failed commit keeps your message in the prompt.
- The repo comes from the focused pane's cwd, then the workspace cwd.
- Read-only git calls run with
GIT_OPTIONAL_LOCKS=0, so opening the glance never takes the index lock from an agent or editor working in the same repo. - Git errors show in the footer. The full text goes to
~/.local/state/herdr/plugins/jacob.git-glance/glance.log.
Popup picker for the package.json scripts of the focused pane's project.
Pick one and it runs in a new tab (or split) as <pm> run <script>, so output
stays visible and ctrl-c stops it.
- Walks up from the focused pane's cwd to the repo root; the nearest
package.jsonwith scripts wins, so a pane deep in an nx lib still finds the root's helper scripts. - Package manager:
packageManagerfield, else lockfile (pnpm, yarn, bun, npm), else npm. - Scripts keep their
package.jsonorder; typing fuzzy-filters names and commands (serve apifinds"start:api": "nx serve api").
[[keys.command]]
key = "cmd+r"
type = "plugin_action"
command = "jacob.project-runner.open"
description = "run project script"↑/↓ or ctrl-p/ctrl-n move · enter runs in a new tab · ctrl-v split
right · ctrl-x split down · esc closes · ctrl-u clears.