Project context, docked next to your terminal.
A Herdr plugin that puts your files and LLM conversation history in a narrow
pane beside the active terminal or coding agent.
About · Highlights · Install · Sidebar summary · Controls · Configuration
Note
herdr-context is an independent plugin built entirely on Herdr's public plugin surface, and is discoverable through the community marketplace. External history discovery covers exactly the provider versions listed below; anything else degrades gracefully instead of closing the dock.
herdr-context docks two views to the right of your work: a Files browser with Git/Jujutsu status markers, and a History browser for LLM conversations tied to the current project. Both render in one Ratatui process, and the project context is captured from the originating terminal before the dock ever takes focus.
┌────────────────┬──────────────────────────────────┬────────────────────────┐
│ Native sidebar │ Terminal / agent │ herdr-context │
│ Herdr │ │ Project / view icons │
└────────────────┴──────────────────────────────────┴────────────────────────┘
The dock is a narrow Herdr pane rather than an extension of the native sidebar. That keeps the plugin on Herdr's public plugin surface, compatible with upstream Herdr, and independently installable. As an opt-in extra, the plugin can also publish a compact VCS summary into the native sidebar's existing Spaces rows — see Sidebar summary.
Docks survive Herdr server restarts: toggling a dock records where it was
open, and after herdr server stop and a fresh start a startup hook re-opens
those docks — without stealing focus and at their saved width. Herdr restores
the old dock slot as a plain shell; the startup hook closes that shell once
the dock is ready, provided another terminal remains in the tab. Opt out with
[dock] restore_on_startup = false in the plugin config
(herdr plugin config-dir herdr-context).
The goal is simple: know where the project stands — files, changes, conversations — without leaving the tab.
- Two views, one process — switch between Files and History without restarting the dock; selection, scroll position, and collapsed groups survive tab switches.
- Compact dock — a shared bold uppercase project title above padded, icon-only view buttons, a two-column tree indent and one state chevron per row; the full-width neutral selection band keeps icon and status colors.
- VCS-aware tree — status markers use green, yellow or red; directories inherit the strongest status of their descendants, and deleted files remain visible even though they left disk.
- Modified-files list — every changed path in one flat list beneath the
tree, with green
+N/ red-Nworkspace diff totals on the divider. - History across tools — Claude Code, Codex CLI, Pi, OMP, and OpenCode sessions associated with the project, merged with live Herdr sessions.
- Resume in one keystroke —
Enteron a session opens it in a new focused tab of the current workspace. - File references —
Enteron a file row inserts its project-relative@pathinto the originating pane and hands focus back to it. - Path filter —
/searches project-relative paths across collapsed directories through a bounded background index. - Responsive by construction — disk reads, Git/Jujutsu commands, and transcript parsing never block the rendering thread.
- Read-only and local — the plugin never modifies project files or VCS history, sends nothing over the network, and caches metadata only.
- browse the directory the dock was opened from;
- expand and collapse directories with keyboard or mouse;
- show the lexical opening directory's basename as a shared bold uppercase title above both views, rather than substituting the repository root;
- honor
.gitignoreand configurable hidden-file visibility; - search paths across collapsed directories; the field appears only while editing or a filter is active, opened by the loupe or configured shortcut. The index follows the same visibility and ignore rules as the tree;
- support Git and Jujutsu (
jj) workspaces behind one normalized status model — added, modified, deleted, renamed, copied, untracked, conflicted; - split into a top tree and a bottom flat list of every file carrying a
status, separated by a rule showing
+N/-Ntracked line totals (file count when the diff is unavailable); the rule lights up while the flat pane holds focus, and the flat pane hides while the path filter is active; - keep file names neutral, preserve typed file icons and their configured colors, and place VCS markers at the right edge. Deterministic truecolor status colors prevent terminal palette remapping from turning red green;
- show the focused Tree or Changes selection's relative path and metadata in a compact footer, followed by configured shortcuts when they fit;
- never intentionally modify project files or VCS history.
Jujutsu status requires jj 0.37 or newer and is integration-tested against
jj 0.44.0. The default Fresh mode snapshots only when Files activates or
you press r — it never polls in the background. The opt-in Passive mode
adds --ignore-working-copy and always marks the shown status as potentially
stale. The plugin never issues commit, bookmark, or rewrite commands.
- list conversations associated with the current project or worktree;
- prefer histories stored inside the project, then discover external stores elsewhere on the filesystem through canonical project metadata;
- merge filesystem history with live Herdr sessions as enrichment, never as the sole source;
- group providers alphabetically with most-recent sessions first; collapsing a provider group loads and exposes no transcript content;
- show one compact title row per session, with a short UTC date at the right when space permits; only the selected session's tool, state, resume capability, update time and provenance appear in the two-line footer. Selecting a provider instead shows its group details;
- isolate failures per source, so one unreadable store cannot hide the others.
| Source | Kind |
|---|---|
Claude Code 2.1.232 |
external store |
Codex CLI 0.147.0 |
external store |
Pi 0.84.1 |
external store |
OMP 17.3.2 |
external store |
OpenCode 1.18.18 |
external store |
| Project-local records | .herdr/conversations/, .jsonl, .json |
| Live Herdr sessions | runtime enrichment |
Project-local records are read shallowly — direct children of
.herdr/conversations/ plus two fixed filenames, never a recursive scan.
Each record needs session_id, the canonical project-root cwd, an RFC 3339
timestamp, a role, and a string message. Message bodies are validated
and never returned to the UI or diagnostics.
External stores are only hints until verified: encoded directory layouts and
filenames count for nothing unless native session IDs, timestamps, and
canonical cwd evidence agree. Live sessions match by tool plus native ID,
then transcript path plus fingerprint, then verified path identity — titles,
timestamps, and prefixes are never identities. Unmatched live rows appear
transiently and survive until the filesystem transcript appears; Herdr
failures are warnings while filesystem history stays visible.
Discovery runs in bounded recent-first pages on the low-priority worker. Large JSONL sessions advance through complete-record cursors instead of being loaded whole, and sessions disappear only after a refresh proves they were deleted or replaced. Incomplete inventories keep prior metadata, and source-scoped warnings stay visible beside healthy adapters.
herdr plugin install Anthodev/herdr-contextHerdr clones the repository, runs the manifest build
(cargo build --release --locked), stores a managed checkout, and registers
the plugin. This path needs git and a Rust toolchain (the crate pins
1.97.1). There is no separate update command in plugin v1 — reinstall to
refresh.
- Herdr
0.8.0or newer and a POSIX shell. gitplus a Rust toolchain (the crate pins 1.97.1) for the source install; the build runscargo build --release --lockedon your machine.- Git is optional at runtime; Jujutsu status needs
jj0.37or newer, and missing VCS tools degrade the affected status view without closing the dock.
There is no separate update command in plugin v1 — run
herdr plugin install Anthodev/herdr-context again to refresh the managed
checkout, and herdr plugin uninstall herdr-context to remove it. Reinstall
and uninstall preserve Herdr-managed config, state, and conversation
histories.
Optional, off by default, and verified against Herdr 0.9.3. When enabled, a
small background publisher reports each Space's VCS state to the token
$herdr_context_vcs, which you place yourself in the native sidebar's
existing Spaces rows — the plugin never rewrites your layout:
[ui.sidebar.spaces]
rows = [
["state_icon", "workspace"],
["branch"],
["$herdr_context_vcs"],
][sidebar]
enabled = false # startup hook creates the publisher only when true
interval_ms = 5000 # 1000..300000; independent of the dock's VCS cadences
[sidebar.roots."<absolute session socket>"]
"<workspace_id>" = "/absolute/path/to/project"Get the socket from the socket: line of herdr status server and the
Space IDs from herdr workspace list, passing the same --session to both
if you run a named session. Roots resolve in order: an explicit mapping for
that socket and workspace ID, then Herdr's native
WorkspaceInfo.worktree.checkout_path, then worktree list — which uses the
unique worktrees[] entry whose open_workspace_id matches the Space, or
source.source_checkout_path only when source.source_workspace_id matches
that same Space; a foreign source association yields no root, and a malformed
or ambiguous association is a protocol error. Absent mappings use these
native paths normally; a nonexistent or invalid explicit mapping value is an
error that never falls back to another project, another checkout, or an
active pane's cwd, and no repo_root or pane cwd inference exists. A
Jujutsu workspace that is not colocated has no native checkout root and needs
an explicit mapping. Without a resolvable root or a selected VCS, the token
is absent.
Token values, refreshed by real collection only. The value is the same
added/deleted line diffstat as the dock's modified-files list totals —
Git counts tracked staged + unstaged changes against HEAD (untracked files
are not included), and Jujutsu diffs @ against its parent the same way
Files does:
| State | Value |
|---|---|
| Working-copy status is dirty | +123/-45 |
| Working-copy status is clean | No changes |
| Collection or root/protocol error | vcs error |
| No project or VCS available | token cleared |
Clean versus dirty is decided from status, not from the line counts, so a
working copy with only renames, binary changes, or untracked files (Git) can
report +0/-0 rather than No changes. The backend, its mode, and
changed-file counts are not shown — the token value itself is the whole summary
(dirty values are prefixed with the Nerd Font Git logo `` U+E702, which
requires a Nerd Font terminal; the Jujutsu passive
mode still applies under the hood: it can hold
`No changes` until the next working-copy snapshot).
Each value carries a TTL of 3 × interval_ms + 7000 ms (22000 by default);
an expired value disappears from the sidebar. Collections run with all docks
closed, and the summary follows its Space's own project root — switching to a
tab in another project does not change it. The [vcs] backend and
jujutsu_mode settings are shared with the dock; only the cadence differs.
To activate on an already-running server (the startup hook fires on Herdr start only):
herdr plugin action invoke herdr-context.sidebar-startHonest limits: a single owner per session holds a canonical lock keyed on the
socket, and a detected socket replacement or generation change makes the old
writer exit without touching the successor's data — but the underlying stat +
CLI probe is not atomic with the CLI's connect, so a replacement between the
two is still possible; values are temporary and TTL-bounded, so the sidebar
cannot show stale data for long. Disabling the config or the plugin stops
scheduling and clears the token within a bounded cleanup budget; otherwise
TTL expiry covers it. A crashed worker is not supervised or restarted
automatically — run the sidebar-start action again to recover. Herdr's own
version handoff behavior is not exercised by this plugin's verification.
| Input | Action |
|---|---|
Tab / Shift+Tab, 1 / 2 |
Switch views without restarting the dock |
Arrows or h j k l |
Navigate the focused pane (tree, modified-files list, or conversation rows) |
Home / End |
Select the first or last visible row |
Enter / Space |
Expand or collapse directories and provider groups; insert @path from a file row; resume a resumable session in a new focused tab |
w |
Move focus between the Files tree and its modified-files list; activating a missing file reports in the notice line instead of inserting a reference |
/ or Files loupe |
Open the path filter — Enter keeps a nonempty filter, Ctrl+U clears the query while editing, Esc clears it and restores Tree/Changes |
| Mouse | Click anywhere in a view or search button, including its padding, or select and focus a list row; right-click toggles and wheel scrolls inside lists. Titles, gaps, notices and footers are not list targets |
q, Esc with no filter, Ctrl+C |
Close the dock and restore the terminal |
The table shows default keys. Custom bindings also update the footer hints; only complete hints that fit after the selection details are shown. History shows the two view-switch hints, while Files can also show search and focus.
View and search controls occupy one row, with two cells of horizontal padding on each side when space permits. Only the active view has a neutral background; inactive views and the search control stay background-free. One blank, noninteractive row separates the controls from the lists; short docks omit it. The active view and an open or active search filter use bold accent icons.
The plugin reads config.toml from HERDR_PLUGIN_CONFIG_DIR on a worker,
after the first frame. An absent file silently uses safe defaults; malformed
files and invalid fields fall back field-by-field and produce a sanitized
warning in the dock.
[dock]
initial_width = 40 # 24..60
restore_on_startup = true # re-open docks after a Herdr server restart
[ui]
display_mode = "ascii" # ascii, unicode, or nerd
colored_icons = true # nerd mode only: type-colored file icons
[files]
show_hidden = false
show_ignored = false # `i` toggles ignored files in-session
search_ignored = false # search ignored paths without showing them normally
exclusions = ["target", "generated/cache"] # project-relative paths
[conversations]
enabled_sources = [
"claude-code",
"codex-cli",
"omp",
"opencode",
"pi",
"project-local-generic-jsonl",
]
project_roots = [".agents/history"] # additional shallow project-local roots
page_size = 128 # 1..512 records per source and pass
cache_entries = 4096 # 16..4096 metadata rows
[conversations.external_roots]
claude-code = ["/home/me/.claude/projects"]
pi = ["/home/me/.pi/agent/sessions"]
[vcs]
backend = "auto" # auto, git, or jj
jujutsu_mode = "fresh" # fresh or passive
git_cadence = "manual" # manual or adaptive
git_min_interval_ms = 2000 # adaptive only; 250..300000
git_max_interval_ms = 30000
passive_jujutsu_interval_ms = 0 # 0 disables; otherwise 1000..300000
[sidebar]
enabled = false # opt-in VCS summary for the native sidebar
interval_ms = 5000 # 1000..300000
[sidebar.roots."/path/to/session/socket"]
"<workspace_id>" = "/absolute/project/root" # explicit roots (JJ-only Spaces)
[keybindings]
refresh = ["r"]
search = ["/"]
toggle_files_focus = ["w"]
toggle_ignored_files = ["i"]
quit = ["q", "esc", "ctrl+c"]Extra project roots are project-relative; extra external roots are absolute. Every configured root remains subject to its adapter's version, layout, and metadata bounds, and unreadable roots are isolated so healthy sources stay visible.
Rows share one anatomy in every mode — status marker, two-column indent, state glyph, optional icon, name — with no tree guide connectors:
| Mode | Look | Requires |
|---|---|---|
ascii |
+ / - directories, f / l files; * / - / ? History states |
Nothing — the compact default |
unicode |
▸ / ▾ chevrons, Unicode file and session bullets |
A Unicode-capable terminal |
nerd |
Chevrons plus Nerd Font folder and typed-file icons, colored by type (colored_icons = false for monochrome) |
A Nerd Font |
Selection is a full-width neutral band in both views; markers and names keep their colors on top of it.
Changing modes reuses cached state — no additional filesystem reads or traversal.
Files and VCS refresh immediately when Files activates or refresh fires.
Adaptive Git polling starts at the minimum interval, doubles while status is
unchanged up to the maximum, resets on change, and suspends outside the Files
view. Fresh Jujutsu never polls; passive Jujutsu polls only when its interval
is non-zero and always renders status as potentially stale.
- Config:
herdr plugin config-dir herdr-context, normally below${XDG_CONFIG_HOME:-$HOME/.config}/herdr/plugins/config/herdr-context/. - State and metadata-only conversation cache: normally below
${XDG_STATE_HOME:-$HOME/.local/state}/herdr/plugins/herdr-context/. - Managed source checkout: shown by
herdr plugin listfor theherdr-contextentry.
Nothing is packaged or shipped: the install path is a source checkout built on your machine. Conversation content never leaves the machine; the disposable cache stores display, provenance, resume, and watermark metadata only, with private permissions and atomic generation replacement. Platforms that cannot enforce owner-only cache permissions fail closed instead of persisting external metadata.
- The manifest declares Linux and macOS only; Windows is unsupported, and the source build targets whatever the pinned Rust toolchain builds.
- External history discovery is restricted to the fixture-validated provider versions above; undocumented, encrypted, or remote-only histories need a dedicated adapter and cannot be inferred safely.
- History can resume sessions but never edits, deletes, summarizes, or uploads them.
- Public Herdr integration — manifest, injected context, CLI, and socket API only.
- One Rust/Ratatui binary — no cross-pane IPC for the core experience.
- Filesystem-first history — project-local and external conversations are indexed independently of Herdr's session lifecycle.
- Responsive by construction — nothing slow ever runs on the rendering thread.
- Bounded work — lazy expansion, coalesced jobs, limited caches, incremental indexing, suspended refreshes for inactive views.
- VCS-neutral core — Git and Jujutsu feed one normalized model without leaking backend specifics into the UI.
- Read-only and resilient — a missing VCS, malformed transcript, or absent tool must never close the dock.
- Local privacy — conversation content stays off the network; caches hold only what discovery and display require.
Requirements: Rust 1.97.1 (pinned in Cargo.toml) and a POSIX shell; git
and jj 0.37+ are optional at runtime.
git clone https://github.com/Anthodev/herdr-context.git
cd herdr-context
cargo build --release --locked
herdr plugin link "$PWD"CI gates every change on rustfmt, Clippy with warnings denied, and the full test suite.
Version 0.20.0 is the current release line. Tag v0.20.0, Cargo
metadata, both manifests, and the minimum Herdr version are validated
together; pushing a v* tag runs formatting, Clippy, the full test suite,
and the contract checks, then publishes curated release notes taken from
the versioned CHANGELOG.md section. There are no packaged assets to
install.
Every performance budget has a retained independent review in
release/performance-review.toml; a failed budget blocks a tagged release
unless its record names the accepting authority, rationale, scope, and
follow-up issue. Ratatui measurements exclude terminal-driver and
multiplexer transport latency — see docs/performance.md
for budgets, baselines, and residual risks.
- herdr-beads — the docked-pane integration pattern
- herdr-file-viewer — the Git-aware tree and repository trust boundaries
- herdr-agent-inbox — active-session enrichment and native transcript formats
- Herdr plugin docs and socket API
- Jujutsu CLI reference and templates
Issues and pull requests are welcome. Bug reports should include the Herdr version, OS, terminal, and VCS backend, with steps to reproduce — and no conversation content or other personal data.
MIT.