Skip to content

Repository files navigation

Shepherd app icon

Shepherd

A native macOS review inbox for the pull request flood.

Latest release CI License: MIT Platform: macOS 27+ Swift 6 Telemetry: anonymous, opt-in

Shepherd's inbox: pull requests from Claude Code, GitHub Copilot and people across every repository, each row with its CI state, review state and diff size

Why

Coding agents open pull requests faster than any human can keep up with β€” across all of your repositories at once, and in a study of 33,596 agent-authored pull requests, 61% carried no recorded human review at all (the numbers). Review tools were built for a handful of pull requests a week; Shepherd is built for the flood. It herds every pull request from every repository into one place: a fast, local-first, keyboard-driven inbox where you triage, review and merge without opening a browser tab. It is open source, and docs/PRIVACY.md lists everything it sends and where.

What it does

Review
πŸ” Real diffs, in the app
Side-by-side and inline Monaco diffs β€” the VS Code engine β€” with syntax highlighting, viewed-state tracking, and files ordered by what deserves attention first.
πŸ’¬ Full GitHub review parity
Inline comments, multi-comment pending reviews, approve / request changes / comment, thread replies and resolves, checks, and merge / squash / rebase.
⌨️ Focus session
β‡§βŒ˜βŽ walks you through every pull request waiting on you, one at a time, over a queue frozen at start. Twenty agent PRs, twenty keystrokes.
πŸ“ Saved replies & templates
Reusable snippets in every comment field, plus a per-repo review checklist that prefills a new, empty review β€” and never touches one you started.
🧭 Open in your editor
Link a repository to its local checkout and jump from a file, a finding or a CI failure straight to the line β€” in VS Code, IntelliJ IDEA, Cursor, the system default or a command of your own.
πŸ“€ Nothing out of sight
A merge on its way, a queued approval or a change GitHub refused shows on the row and in the review β€” Merging…, Merge queued, Not sent β€” with Retry right there. Merged appears only once GitHub confirms it.
Triage
βœ… Bulk triage
Tick the green ones, then approve or merge them behind one confirmation that lists what it will skip β€” red CI, conflicts, drafts, yours β€” and why.
🧾 Claims beside the evidence
What the description says it did β€” tests added, nothing breaking, fixes #142 β€” next to what the diff and CI show. Look closer lets the on-device model point at the lines behind a claim; Shepherd finds every excerpt in the diff itself.
🏷️ Where it came from
Claude Code, Copilot, Codex, Devin, Cursor or a colleague β€” detected on every row and usable as a lens next to repository and review state. The rail keeps its places whichever view you pick, with your watched repositories always on top.
πŸ“₯ Watch a repository
Every open pull request in a repository you watch reaches the inbox, even the ones nobody asked you to review.
β˜€οΈ Morning digest
An opt-in daily summary built from the local database alone: new requests, green PRs one keystroke from done, your red CI, reviews still parked.
πŸ“Š Menu-bar quick inbox
The number of pull requests waiting on your review, and the top ones one click away β€” off the same local data, so it costs no extra API call.
πŸ“’ The fleet
Every agent Shepherd has seen, and what became of its pull requests: merged, closed, reverted, rounds of changes and how often its first push was green β€” across every repository, then one repository at a time underneath, plus at most three sentences the counts below them support. Counts, never a score: no rank, no ordinal, no sortable rate, no traffic-light colour, and no page for a person.
πŸ”Ž Semantic ⌘K search
Type what a pull request was about β€” β€œflaky login test” finds β€œRetry the auth suite” β€” over titles, labels, branches, descriptions and the diffs you have opened. On-device embeddings, stored in your own SQLite, never sent to an AI endpoint; owner/repo#128 still wins outright.
Automate
πŸ› οΈ Delegate to a local agent
Hand a PR or a single finding back to Claude Code in an isolated worktree with a budget cap and a turn cap you can lift. Optionally started for you when CI turns red.
🚦 Auto-merge rules
Opt in, and an agent PR that is green, approved and mergeable gets its merge queued for you β€” narrowable by repo and label, never approving anything, every decision in a local audit log.
πŸ’» Your own clones
Pick a folder and Shepherd reads the repository from its origin, links the checkout and watches every pull request in it β€” one step. Then Start an agent… hands a task you type to Claude Code on a fresh agent/… branch off the default branch, with the same caps β€” as many tasks side by side in one repository as you like, each in its own worktree. Shepherd itself pushes nothing.
πŸ”— Webhooks, deep links, CLI
Signed outbound events into n8n, shepherd:// links, and a shepherd binary that drives the app from a terminal, Raycast or Shortcuts.
πŸ—£οΈ Shortcuts & Siri
App Intents with typed parameters: open a pull request, show a filtered inbox, sync, start a review session, or just ask how many need you. Notifications name their pull request, so Siri can open or summarise the one it is about. No write actions β€” nothing can approve or merge from a phrase.
πŸ”¦ Spotlight
Your inbox in ⌘Space: title, owner/repo#123 Β· author Β· CI state, labels and agent as keywords. Titles and metadata only β€” never a description or a diff β€” and one toggle removes them all.
Intelligence
✨ Drafts, not submissions
Draft a review summary or an inline comment from the diff in front of you. It lands as editable text; nothing is ever submitted for you.
🧠 On-device first
Heuristics always, Apple Foundation Models where available, your own key optional β€” Claude through Apple's own model interface, any OpenAI-compatible endpoint, Konduit (EU) or Ollama.
🌐 Translate in place
A description or comment in a language you don't read gets an on-device translation below the original β€” never instead of it, never through a cloud endpoint.
✍️ Writing Tools everywhere
Apple's proofread, rewrite and tone tools in every field you write review text in β€” summary, inline comment, thread reply, saved reply.
πŸ–ΌοΈ Screenshots, read on this Mac
Switch it on, and a click lets the on-device model describe up to two screenshots from a pull request's description β€” what changed visually, next to the text summary. Off by default; the images come from GitHub's own upload host and never reach a cloud model.
Sync & privacy
πŸ” Sync you host
Every setting and every secret in one AES-256-GCM object in an S3 bucket you own. A new Mac plus the passphrase is a set-up Mac. No account, no server.
πŸ—„οΈ Local-first by construction
SQLite is the source of truth, writes go through a persisted outbox, secrets live in the Keychain, and anonymous usage counts are off until you say yes.
πŸ‡©πŸ‡ͺ Auf Deutsch
Set your Mac to German and the whole app is German β€” the diff viewer and GitHub's error messages included; no language setting, it follows the system. GitHub's own review vocabulary stays English inside the German sentences (pull request, review, approve, request changes, merge, draft, CI), so what you read matches what the next window says.

The long form β€” every feature, with the decisions behind it β€” is in docs/FEATURES.md.

What it looks like

Shepherd's inbox: three pull requests grouped under a Humans heading, each row with its CI state, labels, risk lane and diff size; a left rail counts Needs my review, My pull requests, Involved, Watched and Approved by me; the right pane shows the selected pull request's checks, the files worth reading first, and an on-device summary

The inbox. One row per pull request across every repository, with its CI state, review state and diff size on the row itself β€” grouped by repository, review state or author (person, bot, or named coding agent), whichever lens you reach for. The right pane is the pull request without leaving the list: its CI, the files worth opening first, and a summary written by the on-device model.

A side-by-side diff of an Objective-C file: three collapsed bars reading 18 hidden lines, 10 hidden lines and 39 hidden lines stand in for the unchanged parts, changed lines are highlighted down to the individual word, and the file list on the left orders the two changed files by what deserves attention first

The review screen. A real Monaco diff β€” the VS Code engine β€” side by side or inline, with the unchanged stretches folded away and changes highlighted down to the word. Comment, approve, request changes and merge without opening a browser tab.

The Watched rail showing four open pull requests from sparkle-project/Sparkle, one of them grouped under a Claude Code heading because an agent opened it

Watched repositories. The inbox is built from @me searches, which is right until a repository matters to you without anyone naming you on it. Add it in Settings and its open pull requests arrive too β€” under Watched until you are involved in one.

How it stays yours

  • Local SQLite is the source of truth. GitHub is a sync target, not a backend (ADR 0006).
  • Writes go through an outbox. Approve offline; it lands when the network does, with retries and a staleness check.
  • Secrets live in the Keychain β€” never in UserDefaults, never in the database.
  • Anonymous telemetry, off in one click. Thirteen allow-listed events, every property an enum or a bucket, and never a repository, a branch or a line of code β€” docs/PRIVACY.md says exactly what is sent and ADR 0036 says why. The complete list of hosts Shepherd may contact is in CONTRIBUTING.md; adding one requires a new ADR.
  • Sync is end-to-end encrypted and self-hosted. Your bucket, your passphrase, ciphertext on the wire (ADR 0014).
  • AI runs only when you ask. Off by default, on-device where possible, and the unattended morning digest may never call an endpoint at all. ⌘K search is the other side of the same rule: it runs on every keystroke, so it is on-device only and has no code path to a provider (ADR 0019).
  • Crash reports stay on disk. Opt-in MetricKit JSON in Application Support, no uploader in the code path (ADR 0017).

Keyboard

Keys Action Keys Action
j k Move down / up the list r a Approve
⏎ Open the selected pull request r x Request changes
x Tick a row for bulk triage r c Comment
g a g r g s Group by agent / repo / review state m Merge…
⌘K Command palette & pull-request search r f Β· β‡§βŒ˜βŽ Start a focus review session
⌘R Sync now d n esc In a session: done & next · next · end
⌘⏎ Submit the pending review

Two-keystroke sequences forget an unfinished prefix after 1.5 s, so a stray r never swallows the next key.

Automation & integrations

shepherd open schnaq/review#128        # …/review/128 and a github.com PR URL work too
shepherd inbox needs-my-review         # mine Β· involved Β· approved-by-me Β· watched
shepherd inbox --filter agent:claude-code   # humans Β· bots Β· agent:<id> Β· repo:<owner>/<name>
shepherd fleet                         # every agent; add an id for one agent's page
shepherd sync                          # sweep every repository now
shepherd settings automation           # jump to a Settings tab

Every command is a URL the app parses, so anything that can open one β€” Raycast, Shortcuts, a bookmark, open(1), an n8n Execute Command node β€” can drive Shepherd (ADR 0013):

URL Effect
shepherd://pr/<owner>/<repo>/<number> Open that pull request's review screen
shepherd://inbox Β· shepherd://inbox?filter=<token> Inbox, optionally filtered
shepherd://fleet Β· shepherd://fleet/<agent-id> The fleet, optionally on one agent's page
shepherd://sync Run one sweep now
shepherd://settings Β· shepherd://settings/<tab> Open Settings, optionally on a tab

Outbound webhooks (Settings β†’ Automation) POST a versioned JSON event to the one URL you type β€” review.submitted, pr.merged, delegation.finished, inbox.new_review_request β€” after the action really reached GitHub, plus pr.auto_merge_queued the moment a rule decides something unattended. With a signing secret each request carries X-Shepherd-Signature: sha256=<hex HMAC of the raw body>, deliberately the same shape as GitHub's X-Hub-Signature-256, so an n8n Crypto node you already have works unchanged. Schema, guarantees and a three-minute n8n recipe: docs/WEBHOOKS.md.

AI endpoints are yours to pick: Anthropic, or any OpenAI-compatible base URL with one-click presets for Konduit (EU) and a local Ollama, model discovery and a connection test.

Install

brew install --cask schnaq/tap/shepherd

Or download the DMG from the latest release. Every build is notarized by Apple and keeps itself current through Sparkle 2; the cask sets auto_updates, so Homebrew leaves the installed copy to it. To build from source instead:

brew install xcodegen
git clone https://github.com/schnaq/shepherd.git && cd shepherd
cd web/diff-viewer && npm ci && npm run build && cd ../..   # bundle the Monaco diff viewer
xcodegen generate
open Shepherd.xcodeproj

Needs macOS 27 (Golden Gate) or later on Apple Silicon, Xcode 27+ and Node 22+. Sign in with GitHub via device flow, or paste a fine-grained personal access token. A source build is unsigned and has its updater switched off, which Settings β†’ Account states in one line. The ShepherdKit package is platform-independent β€” cd Packages/ShepherdKit && swift test needs no Xcode. The shepherd CLI is its own scheme:

xcodebuild -project Shepherd.xcodeproj -scheme ShepherdCLI -configuration Release \
  -derivedDataPath .build/cli build
cp .build/cli/Build/Products/Release/shepherd /usr/local/bin/

Status

Under active development, and in daily use by the people who build it. Releases ship signed and notarized, update themselves through Sparkle and install through Homebrew β€” see the latest release. It is young software, so expect rough edges and tell us about them. What is done, next and deliberately out of scope: docs/ROADMAP.md.

Architecture

The app target owns all UI and every Apple-only framework; everything else lives in ShepherdKit, an SPM package that imports no AppKit, SwiftUI or WebKit and is tested headlessly on Linux in CI. The shepherd CLI links only the domain module, so it has no client, no database and no Keychain access β€” it can reach the app solely through shepherd://.

flowchart LR
  CLI["shepherd CLI"] -->|"shepherd://"| App
  App["Shepherd.app<br/>SwiftUI Β· Monaco in WKWebView"] --> Sync["ShepherdSync"]
  App --> DB["ShepherdPersistence<br/>SQLite Β· outbox"]
  Sync --> GH["GitHubKit<br/>GraphQL + REST"]
  Sync --> DB
  GH --> Core["ShepherdCore<br/>models Β· heuristics Β· agent detection"]
  DB --> Core
  GH --> GitHub[("github.com")]
  App -.->|"a bucket you own"| S3[("S3-compatible storage")]
  App -.->|"only when you ask"| AI[("AI endpoint you chose")]
Loading

Details in docs/ARCHITECTURE.md; every significant decision has an ADR in docs/adr, grounded in the research reports in docs/research.

Contributing

CONTRIBUTING.md has the setup, the module rules and the hard privacy lines (allow-listed anonymous telemetry, Keychain-only secrets, local-first), and docs/PRIVACY.md is the plain-language version for people who are not reading the source. Third-party licences that ship inside the app are in NOTICES.md.

License

MIT β€” πŸ‘

About

A native macOS review inbox for the age of AI coding agents.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages