How Shane works with AI agents
A walkthrough of the setup: how the agents are organised, how work reaches them, how they talk to Shane, and the small tools that hold it together. Written for a friend who asked. Machine names, addresses and work details are left out on purpose.
1. The short version
Shane runs a lot of coding agents at once (Claude Code mostly, plus Codex, Pi and OpenCode). Each one has a single job: one repo, one machine, or one role. They all live in the Herdr app, a desktop app for running agents, with panels Shane wrote himself. Any agent can find another by a stable address, send it a message and read its reply. He checks in from his desk or from a phone app.
Around that sit a handful of small tools he wrote. Review Desk makes sure a review request, a review comment or a failed check lands with the agent that owns that code. sidediff is where he reads what agents changed. Whatever an agent produces shows up on the screen he is actually looking at. Every agent gets credentials that last an hour instead of forever. And all of his tools come from one Homebrew tap that keeps every machine up to date on its own.
2. One agent per job
The rule is simple: every piece of work has exactly one agent that owns it. An agent with one repo in its head has a small, clean context, knows that repo's conventions, and can be handed a message like "CI is red on your branch" without any explanation of which branch or why.
- Project agents. One per repo: the phone app, the dictation app, the secrets helper, the MCP router, and so on. Each one stays alive for as long as the project is active, so its context carries over from day to day.
- A manager agent per dev machine. One for the Linux desktop and one for the Mac mini. They own the machine itself: packages, OS settings, dotfiles, background services. Project agents never change machine settings; they ask the manager.
- A homelab agent. Owns the home servers and their deploys. Homelab changes are pushed to the Git server and deployed by CI, never by hand, and this agent is the one that watches those runs.
- A chief of staff. The orchestrator. It keeps a roster of every agent on every machine (what it is doing, which branch, whether it is waiting on Shane), answers the questions it can, folds duplicate agents into one, merges finished branches with linear history, and batches the questions that really need Shane into one short message.
All four agent programs follow one shared rulebook; see One rulebook for every agent.
3. reach: every agent has an address
Agents find each other through reach, which gives every agent a stable address made of its machine, its space and its name (say desktop/homelab/homelab). reach send delivers a message to exactly that agent, waking it if it was asleep and waiting if it is busy. An address that matches nothing, or more than one agent, is refused rather than guessed, so a message never lands with the wrong agent.
reach ls every agent, its state and where it runs
reach send desktop/homelab/homelab "..." deliver a message (wakes it if asleep)
reach read desktop/web/web --lines 40 the bottom of its screen
reach pin desktop/web/main desktop/web/web give an agent the address it keeps
reach keeps no registry of its own: each time it is asked, it asks the hosts the agents run in, so it always knows what is alive, asleep or ended right now. Review Desk and nudge both deliver through it.
4. The Herdr app: where everything lives
Every agent and terminal runs in the Herdr app. It has two halves: herdr-core, a background server that runs the terminals and keeps them alive when the window closes or updates, and the window itself, which arranges them into spaces, tabs and panes. Agents use the same server from the command line: hc ls lists every terminal and agent with what it is doing, hc send hands one a message, hc read reads its screen. The app recognises each agent and knows whether it is working, idle, done or blocked on a question.
Shane's mods
The app's panels are small JavaScript mods, and Shane wrote his own on top of the stock ones:
- Threads: a sidebar listing every project's Claude threads, with whatever needs him at the top. It also gives each agent its stable address, and puts idle threads to sleep and wakes them again.
- Notes: a background Claude keeps running notes for each project (decided, gotchas, next) from the thread transcripts.
- Highlights: a summary of the focused thread: what it changed, the files it touched, the commit it made.
- Show and Markup: every picture the thread's Claude produced. He opens one, circles, draws arrows and adds numbered notes on it, then sends it straight back to Claude.
- Options: when Claude offers choices, they open as a grid of pictures and his pick goes back to the thread.
- Secret prompt: when an agent needs a token, a masked dialog writes it to a private file; the agent only ever gets the path.
Alongside those he uses stock panels such as Peek (a look at a sub-agent over its parent's tab), Files, Changes and worktrees, plus a third-party browser mod that draws a real Chrome page into a pane next to the agent.
sth-mux is in progress: Shane's own build of the Herdr app on the Moonframe toolkit. It reads the same herdr-core state and runs his Threads mod natively, so the two can be used side by side while it matures. The older terminal version of Herdr still exists, but the app is where the agents live now.
The phone app
The phone app is how Shane keeps up when he is away from the desk. A small connector on each machine reports its agents to a hub, and the phone shows them grouped by what they need ("Needs you" first), each conversation with tool runs folded away, and lets him queue a message for after an agent's current turn. Prompts are saved the moment they are sent and delivered exactly once, so a patchy connection neither loses nor repeats one.
5. Review Desk: work reaches the right agent
An agent that pushes a branch shouldn't have to sit polling CI, and Shane shouldn't have to copy review comments from one window into another. Review Desk is the piece that routes all of it, and it carries a lot of the day.
Agents do the first rounds of code review with each other, and Shane gets a queue of what is ready for a person:
- When someone asks Shane for a review, the desk hands it to the agent that owns that part of the code, in whatever terminal it runs in.
- That agent reviews and posts its findings through the desk. The author's side hands them to the agent on the branch, which fixes them and replies. They go back and forth, at most three rounds per thread.
- When everything is settled, the pull request lands in Needs you, grouped as "needs your architectural eye", "worth a real look" or "probably safe, skim it", with a summary of what the agents settled.
Needs you also lists everything else waiting on him, each with the reason: a review no agent covers, an author who changed code under his comment without replying, his own pull requests that a person commented on, that are ready to merge or whose CI failed with no agent on it, and @mentions in his repositories. A failed check on a branch an agent is working on goes straight to that agent, which reads the log, fixes the cause and pushes. A menu bar item shows how many things need him.
Agents never speak for Shane
This is a hard rule. Any comment an agent writes on his accounts carries a visible label saying it was written by an AI, and whether Shane directed it or not. An agent never replies to a person on its own: it drafts the text, shows Shane, and posts only once he says so. Review Desk is the one standing exception, and even there every comment goes through the desk with the label "AI agent review, not yet read by Shane". Agents there never approve, request changes, merge or resolve threads. Text from a pull request or a mention is treated as data, never as instructions to an agent.
6. sidediff: reviewing what agents write
sidediff is the tool Shane reads code in, all day. It is a GitHub-style diff viewer in the browser (file tree, split or unified view, "viewed" checkboxes with a progress bar) with a third column of notes aligned to the lines they describe. It watches the working tree, so the page updates as the agent edits, commits or switches branches.
What makes it work with agents is that the agent is in the review with him:
- Notes are plain files. While Shane reads, the agent drops a note on line 42 saying why that line changed, and it appears immediately.
- The agent can drive the page. It scrolls to a file, highlights a range, and opens an explainer panel beside the diff, which turns a review into a guided tour of the change, one stop at a time.
- Comments go both ways. When Shane leaves a comment in the page, the agent is listening and answers it, or fixes the code and the diff updates.
This is a real screenshot of a review of one small change in one of his repos (the command that pins an agent to an address in reach), at the ultra-wide size he works at. Click it to open it full size.
Between Review Desk deciding what needs him and sidediff showing him exactly what changed and why, Shane spends his attention on judgement rather than on finding things.
7. Handoff and Screen Organizer
Handoff is a menu bar panel for the files agents produce: reports, HTML pages like this one, charts, screenshots. An agent never prints a file path at Shane. It hands the file over, and it shows up in the panel with a preview, is put on the clipboard (paste into a chat and you get the file, paste into a terminal and you get the path), and opens. Each file is delivered once, when it is finished, on whichever machine Shane is using (see sth-seat below).
Screen Organizer is a small menu bar app for the screenshots and screen recordings that end up in prompts and bug reports. It compresses new screenshots in place, compresses and moves screen recordings to their own folder, and can sort both into a folder per day, so what he drags into an agent is small and easy to find.
8. tether: a durable agent platform
tether is Shane's own platform for agents that run without him at a terminal. It runs on the homelab, and each piece of work is a durable workflow: a run survives restarts, waits out a model's usage limit and carries on, and can take days. Its steps are fixed commands, calls to the Git server and to chat, and agents working inside a sandboxed box.
- Talk to it from anywhere. It listens in Slack and Discord, so Shane can ask it from his phone to build a feature, look into something, or research a question, and it answers in the thread.
- It does real work on code. It watches the Git server's pull requests and keeps working on its own changes as comments come in. A nightly run reviews the architecture of chosen repos, fixes what is small and safe, and opens a pull request. It reviews every new commit on main against his written engineering rules, and triages issues, fixing the trivial ones itself.
- As much or as little access as he wants. Every box's traffic goes through a proxy, the ward, that enforces that run's policy: which hosts it may reach and what it may change. Credentials are added by the ward on the way out, so the box never holds a secret, and a test suite tries every escape a hostile agent would and fails if one works.
- He can see what it did. A console shows each run step by step, and a morning summary in chat lists what ran overnight, what failed and what is still going.
Everything it writes on his repos is labelled as an AI's, and its changes arrive as pull requests for him to approve.
9. sth-seat: every machine feels like the one he's at
Most agents run on the headless Linux desktop, but Shane sits at the Mac mini. sth-seat is why that never feels like two computers. Things just show up where he is:
- A report appears in his tray. An agent on the desktop finishes a report and hands it over. A moment later it is in the Handoff tray on the Mac, with a preview: one click opens it, a drag puts it in Slack, a paste puts it in a prompt.
- A file is revealed in Finder. An agent on the other computer says "it's this file", and Finder on his Mac opens with the file selected, copied over for him.
- A dev server opens as if it were local. A dev server running on the desktop at
localhost:5173opens in the Mac's browser at a localhost address, through a tunnel. Nothing is configured, no ports are opened, and the server never leaves localhost on the machine it runs on. - Notifications find him. "The build finished" or "I need a decision" pops up on whichever machine he is typing on, because the seat follows the keyboard.
- Secrets go the right way. When an agent needs a token, the prompt appears on his screen, and what he types lands in a private file on the agent's machine. The agent gets the path, never the secret, and the secret never passes through the chat.
How it works
A small hub on the homelab knows when each machine last saw keyboard or mouse input; the machine with the most recent input is the seat. A daemon on every machine keeps a connection to the hub and does what it is sent: open a link in the default browser, put a file into Handoff, show a notification, show a prompt. Agents call the hub with the sth-seat command or as tools on the MCP router, and never need to know which machine Shane is at.
Machines can also act on each other directly: sth-seat run and cp run a command or copy a file on another machine in place of SSH, and send leaves a message in another machine's inbox. Every one of these is signed with the sending machine's own key, and the receiving machine checks it against its own policy file, which says exactly what it accepts from whom. Every command is logged where it ran, and nothing in a message is ever run. On the Mac the daemon is a signed app, so macOS remembers its permissions across upgrades.
10. One tap, updated every hour
Every tool on this page that Shane wrote ships from one Homebrew tap, seethruhead/tap: sth-seat, Review Desk, sidediff, Handoff, nudge, reach, sth-dictate, sth-mcp, Screen Organizer and the rest. A dev build is only for testing a change; once it is released, the machine goes back to the brew version.
sth-update keeps every machine current. Every hour it refreshes only that tap, upgrades only the tap's tools that are out of date, one at a time, and restarts the services that run an upgraded tool so they pick up the new binary. It notifies him only when something fails. It never restarts the programs that host terminals and agents; those are upgraded and take effect on their next restart. A release reaches every machine within the hour, with nothing to paste.
nudge is a plain scheduler: it sends an agent a fixed, pre-written message on a schedule ("check CI on main every 30 minutes"), waiting until the agent is idle. That is all it does.
11. CI/CD he owns
Every repo lives on a self-hosted Gitea, and every pipeline runs on Gitea Actions on Shane's own hardware. Agents push; CI checks, builds, releases and deploys; nobody deploys by hand.
One runner, on the desktop
There is exactly one Actions runner, on the 96 GB desktop, taking four jobs at a time. Its labels decide where a job runs: app checks and image builds run on the host; homelab and deploy jobs run in containers on the desktop's Docker. Jobs run as a separate ci user that can't read Shane's code checkouts or keys.
Pipelines in Dagger, tasks in moon
Pipelines are written in Dagger, so the same pipeline runs on an agent's machine and in CI; "it passed locally" means the same thing in both places. Inside a repo, moon runs the tasks (typecheck, lint, tests, builds) and only the ones a change affects, with a remote cache on the homelab so a task that already passed for the same inputs isn't run again. Agents only ever run a repo's own scripts, never a tool binary directly, so they use the same flags and config CI does.
CI that works with the internet down
On 13 November outside DNS failed, and the deploy that would have fixed it couldn't run, because CI downloaded its tools from the internet on every run. Since then CI pulls everything through a cache stack on the homelab, which keeps serving what it holds when the upstream, or DNS itself, is down:
- Container images: pull-through mirrors for Docker Hub and ghcr.io, which serve the last tag they saw when the upstream can't answer.
- npm: Verdaccio, which serves its own copy when npmjs fails.
- Everything else: an nginx file cache for apt and apk packages, Go modules, crates and rustup, GitHub release downloads, Playwright's browsers and Chrome, keeping files for a year after their last use.
- Git: Gitea mirrors of the Actions and git repos that pipelines use.
- Dagger itself: a pinned Dagger CLI on the runner and a runtime image with certificates baked in, so the engine needs nothing from outside.
Every cache name resolves from the homelab's own DNS. It was proven with a test DNS server that answers only local names: with the rest of the internet unreachable, the pipelines still check, build and deploy.
Deploys only through CI
A merge to main builds the service's image and publishes it to Gitea's registry. CI then commits that image's exact digest to the homelab repo (a "pin" commit), and the homelab repo's own pipeline validates the stacks and deploys the change. That repo is the single record of what runs where; agents never change a running service by hand.
Releases to the tap
Shane's tools are released by tagging a version; the release's formula or cask in seethruhead/tap is updated to point at it, and sth-update installs it on every machine within the hour.
The hosted CI he tried: boat.dev
Before the desktop runner took everything, CI ran on boat.dev, a hosted service of sandbox boxes that can be stopped, resumed and forked. Shane wrote a controller that listened to Gitea's job events, woke a box (or forked a warm one) for each queued job and parked idle boxes again. It worked, but it was a constant fight:
- A box was parked in the middle of a job after the service answered a park request with a server error, killing a test suite four seconds in, so the controller had to double-check with Gitea before every park.
- Some boxes ran tests at a small fraction of their normal speed.
- Spending had to be held inside the plan by a budget governor that read the service's limits every ten minutes.
- Start times needed warm boxes kept around and juggled, and a probe had to watch for jobs that went silent.
Moving every job onto the desktop runner made CI faster, cheaper and predictable. boat.dev is now only a fallback, used for app checks when the desktop is booted into Windows or off.
12. One rulebook for every agent
Shane uses four agent programs (Claude Code, Codex, Pi and OpenCode), and all of them follow the same rules:
- One instruction file, assembled on each machine from a shared base plus a layer for each kind of machine (home, macOS, Linux), and linked into every program's own config location. It holds only invariants: each procedure lives in exactly one skill or rule, and everything else points at it.
- One skills folder that every program reads: how to publish to npm, how to review a pull request, how to drive sidediff, how to hand a file over, how to use the router, and so on.
- Rules for specific work (Effect code, Mermaid diagrams, HTML documents, CI metrics), each with a "when" line, indexed automatically so an agent can find the one that applies before it starts.
- All of it is tracked as dotfiles with yadm, so a rule written on one machine reaches every machine on the next pull.
A few of the invariants, to give the flavour: never add AI attribution to a commit; never post a comment that reads as if Shane wrote it; one heavy command at a time; wrap anything that can hang in a short timeout; deliver finished files once, through Handoff; plain English, no em dashes.
13. How code is held to a standard
- The Engineering Codex. Shane's standards, written down once so people and agents apply them the same way: each topic says why it matters, then gives numbered rules. An AI reviewer applies it to every change, strictly, which frees human review for what only a person can judge: architecture. tether reviews every new commit on main against it and records anything that breaks a "must".
- House lint rules, enforced by an oxlint plugin in every repo: no loops, no mutation, no comments, guard clauses instead of nested ifs, no casts. Code reads as code; types and names carry the context.
- Agents review themselves first. Before any push, an agent runs the review skills on its own diff (a mechanical pass, then a judgement pass against every rule, then a readability pass) and fixes what they find. A reviewer should never be the first to spot a rule breach.
- Coverage is measured against 100%, but a shortfall warns rather than fails, naming the files to improve; a failing test still fails.
- Hooks in his repos: pre-commit runs typecheck, lint and format on what changed; commit-msg rejects AI attribution; pre-push runs the affected tests.
- One default stack. New tools are Bun and Effect v4, shipped as single binaries; daemons that talk to the OS are Rust, or Swift on macOS.
14. Worktrees, tickets and memory
- worktrunk is the only way a worktree gets made. Its hooks install dependencies, link the shared Postgres data and check out Effect's docs, so an agent can start work on a new branch in its own folder in one command, without disturbing anyone else's checkout.
- tk is the ticket CLI. Agents track work as tickets in the repo instead of in a harness's own to-do list, so the plan survives the session.
- aihist reads past sessions from every agent program in one place: index, search for the words, open the session. An agent uses it to find how something was decided last week, or to read its own earlier work after its context was compacted.
15. Observability-driven development
The homelab runs a full Grafana stack (Prometheus for metrics, Loki for logs, Tempo for traces, Alloy to collect), and Shane's services send OpenTelemetry to it. The point is that agents debug from evidence:
- graf is an agent-first Grafana CLI: JSON by default, no prompts, one credential for metrics, logs, traces, dashboards and alerts. An agent asks "what did this request actually do" and reads the trace instead of guessing.
- CI is observable too. Every job, task, test failure and flaky test is an event in Loki, with a dashboard for agents. The rule is to check whether CI was already slow or failing before blaming your own change.
- sth-vitals sends each machine's CPU, memory, GPU, disk and sensor readings, so a slow machine is looked at with real numbers; Gatus checks every service and alerts after repeated failures.
16. Credentials: nothing long-lived in an agent's hands
Agents never hold long-lived secrets. The secrets live in OpenBao, an open-source fork of HashiCorp Vault running on the homelab, and agents get short-lived credentials from it through a small helper, sth-bao.
- Each machine has its own identity. One login pair per machine, readable only by that machine's user. sth-bao uses it to get a token that lasts an hour, caches it, and makes the request, so no agent ever handles a token by hand.
- Database logins last an hour.
sth-bao dbcreates a Postgres user that expires after an hour and is then dropped. - SSH certificates last an hour.
sth-bao sshsigns the agent's own key for one hour. That key is in no server's list of allowed keys, so it is useless without a fresh certificate. - Secrets are laid out by who may read them:
operator/(admin tokens, the two dev machines only),shared/(every agent machine),machine/<name>/(that machine only). Shane's own admin area is his alone, behind single sign-on. - Secrets never go on a command line or into a chat. They reach a tool through standard input or a file descriptor, never as a command argument where other processes and logs could see them.
- Secrets from Shane go into a file. When an agent needs one he has,
sth-seat ask-secretshows a hidden prompt on his screen and writes the answer to a file only the agent's user can read. If it should outlive the task, the agent stores it in OpenBao and deletes the file.
Machine tokens are only accepted from the home network or the private VPN, and every request is written to an audit log.
17. The homelab and how machines trust each other
The MCP router and sth-mcp
The homelab runs several MCP servers that agents use as tools: the wiki, sth-ingest (web search, page reading, video transcripts and the saved library), sth-seat, health data from his phone, habits, a budget, a wish list and a hotel search. Rather than configure each one in every agent, they all sit behind one router, which holds a couple of hundred tools. Each machine has its own token for it.
The sth-mcp CLI sits in front of the router. Each command is one fresh request with no session held open, so restarting the router or any server behind it never means restarting an agent. An agent searches for a tool by a few words, reads that one tool's help and calls it, which keeps hundreds of tool descriptions out of its context.
The wiki
A self-hosted wiki holds the state of the homelab, the plans and handoff notes. Agents read it before answering questions about the setup and write down what they learn, so the next agent starts from what the last one found out.
18. Voice and reading: Whisper, sth-dictate, sth-ingest
A lot of Shane's prompts are spoken, not typed. whisper.cpp runs on the desktop's NVIDIA card with the large-v3-turbo model, as a service that starts at boot and offers the standard OpenAI transcription API on the home network. Transcription is fast enough that dictation feels immediate.
sth-dictate
sth-dictate is a small dictation app, on the Mac as a menu bar app and on Linux too: tap a hotkey to start, tap again to stop, and the text is pasted at the cursor. Double tap and it streams, sending each sentence as you pause. The part that matters for agents: it can be pinned to Herdr, so dictation goes straight into the focused agent through the Herdr app itself, followed by Return, while Shane keeps working in another app. Talking to an agent becomes as quick as talking to a person across the room.
sth-ingest and the Chrome extension
sth-ingest gives agents what a person can read or watch, as tools on the MCP router: web search, pages read in a real browser on the homelab, and videos. A video with no captions is transcribed by the same Whisper server. A Chrome extension adds a "Send to Ingest" button on YouTube videos and a popup that sends any page's text, as the tab shows it, to a saved library. An agent can then "watch" a talk by reading its timestamped transcript and pulling frames at the moments that matter, or read a page Shane saved for it.
19. The machines
- The desktop is an Omarchy (Arch Linux) box with 96 GB of RAM, a 16-core CPU and the NVIDIA card. It is used almost entirely headless, as the development machine: most agents and their dev servers live there, and it runs the Whisper server.
- The Mac mini is the machine Shane usually sits at, and it runs agents too: everything macOS-only (Swift menu bar apps, iOS builds) and the always-on menu bar tools. The Herdr app there shows the agents on both machines.
- The homelab runs the hubs, the router, the wiki, secrets and CI.
sth-vitals is a small daemon on each machine that sends CPU, memory, GPU, disk and sensor readings to the homelab's Grafana stack, so a slow machine can be looked at with real numbers. The working rule for agents is just as simple: heavy work (test suites, builds, installs) runs one command at a time, full browser suites belong in CI, and anything that might hang runs under a short timeout so it never sits there for ten minutes.
20. The principles underneath
- One owner for each piece of work, with a stable address, so a message always has an obvious recipient.
- Agents talk through named channels: reach messages, Review Desk routing, pull request threads. Nothing depends on a human copying text between windows.
- Shane approves anything said in his name, and everything an agent writes publicly is labelled as such.
- Credentials are short-lived and per machine. Nothing long-lived sits in a file an agent can read.
- Output comes to Shane, on the screen he is using, once, when it is finished.
- Tools update themselves. One tap, checked every hour, so every machine runs the same versions.