Files
herdr/AGENTS.md
T

3.6 KiB

herdr

Terminal workspace manager for AI coding agents. Rust + ratatui.

Principles

  • State is separated from runtime. AppState is pure data, testable without PTYs or async. PaneState is separate from PaneRuntime. Workspace logic doesn't need real terminals.
  • Render is pure. compute_view() handles geometry and mutations. render() takes &AppState and only draws. Never mutate state during render.
  • No god objects. If a module is doing too many things, split it. app/ is already split into state, actions, and input. Keep it that way.
  • Platform code is isolated. OS-specific behavior lives in src/platform/. Core modules don't have #[cfg(target_os)].
  • Detection is decoupled. The detector reads a screen snapshot, never touches the parser or viewport state.
  • UI patterns should be reused. Herdr is a mouse-first TUI. New dialogs, onboarding, settings, and post-update flows should follow the existing UI/UX language and interaction patterns instead of inventing one-off screens. Prefer reusing existing modal/screen structure, affordances, and close actions so the app feels consistent.

Testing

just check              # formatting + unit tests
just test               # unit tests
just test-all           # full local test suite

Default flow: run just check before committing.

Unit tests live next to the code (#[cfg(test)] mod tests). If you add behavior to AppState or Workspace, it should be testable with AppState::test_new() and Workspace::test_new() — no PTYs.

Conventions

  • Conventional commits, lowercase, no emojis.
  • Rust: no unwrap() in production code. tracing for logging. #[allow] only with a comment explaining why.
  • Don't bypass checks. If tests fail, fix them before committing.
  • Don't add dependencies without a reason. Check if the existing deps cover it first.

Releases

Before cutting a release, draft the upcoming notes under ## Unreleased in CHANGELOG.md. The release script promotes that section into the versioned entry.

Default release flow:

just check
just release 0.x.y
# wait for the GitHub release and all four binary assets to exist
just update-latest-json 0.x.y
just verify-release-state 0.x.y

just release 0.x.y prepares the changelog entry, bumps Cargo.toml, runs tests, commits, tags, and pushes. GitHub Actions builds the binaries after the tag is pushed.

just update-latest-json 0.x.y is the post-release website step. It uses gh release view against ogulcancelik/herdr, verifies the published release exists, rejects draft or prerelease releases, requires a non-empty release body, requires these four assets to exist, and only then rewrites website/latest.json:

  • herdr-linux-x86_64
  • herdr-linux-aarch64
  • herdr-macos-x86_64
  • herdr-macos-aarch64

It also refuses to run if website/latest.json is already at the same or newer version. It leaves the file unstaged and prints the next commands to review, commit, and push.

just verify-release-state 0.x.y is the final read-only verification step. It checks that the published GitHub release, the local website/latest.json, the live https://herdr.dev/latest.json, and the release asset URLs all match the same version and manifest contents.

website/latest.json is the shipped updater source of truth. Keep its schema aligned with src/update.rs:

{
  "version": "0.x.y",
  "notes": "### ...",
  "assets": {
    "linux-x86_64": "...",
    "linux-aarch64": "...",
    "macos-x86_64": "...",
    "macos-aarch64": "..."
  }
}

The app update check and the in-app What's New flow both depend on that exact manifest shape.