Terminal is Ghostty. Editing is mostly done through AI CLIs (claude,
codex, cursor-agent) rather than a GUI editor, and those run inside
herdr, which keeps their panes alive and shows what every one of them is
doing. Everything here follows from those facts.
git clone https://github.com/ryutamaki/dotfiles.git ~/dotfiles
cd ~/dotfiles
bash ./bin/setup.shThat installs Homebrew and everything in Brewfile, links the config files,
installs Node / Ruby / Terraform through mise, installs the AI CLIs that
Homebrew does not carry, and wires the agents and herdr to each other — the
skills the agents use, and the hooks herdr reads their state from. It is safe
to re-run: correct symlinks are left alone, and anything real that is in the
way is moved to ~/dotfiles_old/<timestamp>/.
Package versions are not upgraded by setup.sh. Upgrading is deliberate:
brew bundle upgrade --file=BrewfileIf that upgrades herdr, restart its server before trusting the herdr CLI
again — the old server keeps running and a newer client refuses to talk to it,
so every command fails with protocol_mismatch. herdr status shows
restart_needed: yes. Run this from a plain Ghostty tab, not from inside
herdr, because stopping exits every pane process:
HERDR_SOCKET_PATH="$HOME/.config/herdr/herdr.sock" herdr server stop
herdrPanes come back with their agents resumed, thanks to the integrations. Scrollback does not.
These cannot be automated:
-
gh auth login -
claude— and sign in -
codex— and sign in -
cursor-agent login -
SSH keys into
~/.ssh, public key added to GitHub -
Fill in
~/.gitconfig.local(personal) and~/.gitconfig.work(work) ifsetup.shcreated them from the template, and uncomment oneincludeIfin~/.gitconfig.localper work directory — nothing else points at~/.gitconfig.work -
Open Ghostty once, then allow it under System Settings › Privacy & Security › Accessibility so cmd+` can summon the quick terminal from any app
-
Open Raycast once and give it cmd+space as its hotkey. Spotlight owns that shortcut on a fresh machine, and Raycast offers to take it off Spotlight when you press it — accept. Nothing here can do this step: Raycast's preferences are its own file, rewritten by the app and carrying account state, so the repo tracks only the
Brewfileline. Declining is not a middle ground: the system shortcut wins, so Raycast's hotkey never fires at all -
Point the AI CLIs at
bin/statusline.py. Their config files are not tracked (see below), so this is per machine.setup.shprints the command with this machine's path already filled in:```jsonc // ~/.claude/settings.json and ~/.cursor/cli-config.json. // Spell the path out: neither tool expands ~ in an argument. "statusLine": { "type": "command", "command": "/usr/bin/python3 /Users/YOU/dotfiles/bin/statusline.py", "padding": 1 } ``` ```toml # ~/.codex/config.toml -- codex has no command hook, so it gets the # nearest built-in items instead. `/statusline` edits the same list. # No five-hour-limit: that window does not exist on this plan, and the # item is simply omitted when its data is unavailable. [tui] status_line = [ "model-with-reasoning", "context-used", "weekly-limit", "current-dir", "git-branch", "pull-request-number", ] status_line_use_colors = false ``` -
Paste
agents/global.mdinto cursor-agent's User Rules, in the Cursor app under Settings › Rules.setup.shlinks that file to~/.claude/CLAUDE.mdand~/.codex/AGENTS.md, but cursor-agent has no equivalent on disk — its global layer arrives in the server response, so it is account-side and cannot be symlinked. Re-paste it when that file changes -
Set cursor-agent's default model to Grok 4.6 Extra High (not Fast).
claudepins Opus from the repo; this pin is machine-local, in~/.cursor/cli-config.json, and is what a Grok session started by hand inherits -
Pin codex's model and reasoning effort in
~/.codex/config.toml. Same category as the pin above, and machine-local for the same reason: that file also carries a trust level per project directory.bin/delegate.shpasses codex no--model, so this is what thegptandimageroles inherit as well as a session started by hand:```toml # ~/.codex/config.toml -- top level, above the [projects] tables. model = "gpt-6-astra" model_reasoning_effort = "medium" ``` `medium` rather than `high` because effort is a smaller lever than it looks. Measured over this machine's 98 `high` turns: reasoning was **0.08%** of the tokens a turn spent and 2.4% of its cost at API rates, while input — re-sent once per tool call, eight calls to the median turn — was 99.5% of the tokens with 95% of that cached. Dropping a step buys about 1% and nothing more, so the rung is chosen on how much thinking the work wants, which is where codex's own docs put it: start at medium, go to high when depth is actually missing. What does move capacity is context, not effort -
Start
codexonce and press t at its hook review prompt. codex holds every newly installed hook until a human trusts it, so herdr's agent-state integration reports nothing until then.claudeandcursor-agentneed no equivalent step -
Create the space the plan budgets are drawn in, and drag it to the top of the sidebar:
```sh herdr workspace create --label usage --cwd "$HOME" --no-focus ``` `bin/statusline.py` pushes how much of claude's, codex's and cursor's plans has been spent to a space with exactly that label, and pushes nothing when there is none. herdr owns the sidebar's order, so the position is a drag rather than a setting -
Restart the shell
Run herdr in a project, start claude / codex / cursor-agent in a pane,
and split for more. Panes keep running when the window closes; herdr reattaches
to them. The sidebar shows every agent across every project and whether it is
working, blocked or done. Mouse works everywhere; ctrl+t
then ? lists the keys.
The prefix is ctrl+t rather than herdr's own ctrl+b, because ctrl+b is emacs' backward-char and gets pressed far more often than any pane command. | and - split, as they did under tmux. That takes ctrl+t away from fzf inside a pane, so its file widget moved to ctrl+o.
Those agents can also drive herdr back. setup.sh installs the herdr skill
into all three, so an agent that is asked to can split a pane, run a build
beside itself without taking focus, read what came out, and wait for another
agent to finish — through the same herdr CLI, which answers in JSON. The
skill will not spawn panes on its own.
There is one exception, and it is the reason agents/global.md exists: a dev
server or watcher goes in its own pane without being asked, so its log is
visible to a human rather than held in the agent's background.
herdr agent list # what every agent is doing, as JSON
herdr status # client and serverOpus 5 or Grok 4.6 Extra High gets started by hand — claude or
cursor-agent, each with the model already pinned. Grok is the chair when
the claude plan is empty, or when the work wants Grok in it. Either
manages, and hands work that is not already in the chair to the rest of
the table. Nothing asks which model at launch, because that question
wants the shape of the work before any of it has been done.
delegate takes a role, not a model. It splits a herdr pane, starts the
right CLI on the right model, and submits the task.
delegate bulk audit "read every .tf file and list what 1.5.7 pins us to"
delegate --collect audit # wait for it to settle, print the tail
delegate --status # live delegates, and what each plan has left
delegate --answer audit y # leftover prompt, not the normal path
delegate --close audit # or --close-all
delegate --list # the table, with what each plan has left| Role | Goes to | For |
|---|---|---|
bulk |
Grok 4.6 High (cursor-agent) | Default — inventories, first passes, a lot of files |
web |
Grok 4.6 High | Research that means reading many web pages |
deep |
Grok 4.6 Extra High | When a first pass was not enough |
peer |
Grok 4.6 High | A parallel subtask the caller could have done itself |
hard |
Fable 5 (claude) | Scarce — genuinely hard design and argument |
gpt |
GPT-6 Astra (codex) | Scarce — a different vendor, not a generic second pass |
image |
Image 2, through codex | Images |
Seven roles, four destinations. Four land on Grok because that is the plentiful
plan — and they stay four names rather than collapsing into one, because
dlg:web:pricing and dlg:peer:tests say different things in a sidebar holding
four panes. Fast is deliberately off every Grok row: Cursor prices it at exactly
double per token, and the paid plans meter tokens rather than requests. That is a
real trade rather than a free win — Cursor's own docs say Fast is the default
speed tier on Pro and higher, so this steps down from that default to make the
pool last twice as long.
It delegates without being asked when one of five countable things is true — a
survey past ~10 files, research past ~3 web pages, an answer it does not hold
with confidence, a design about to be settled, or an image in the deliverable.
Counts rather than judgement, because reading a file yourself always feels
faster than opening a pane and waiting for one, so a softer rule loses every
time. peer is the one role with no trigger: "worth not waiting for" is a
judgement call, and it would undo a list that argues only counts survive.
Every delegate is a herdr agent, so the sidebar shows what it is doing.
Approvals are answered at start so the task can finish; blocked is the
leftover case, and --answer is the keypress. The pane's label is the only registry there is,
so herdr pane list is the ledger — and every closing verb is scoped to the tab
you are in, so --close-all reaches neither a pane you opened yourself nor
another session's delegates.
The three plans are metered separately, and bin/statusline.py already draws
all three in the sidebar. delegate reads the same numbers: above 85% it says
which plan is emptiest and which roles route there — and sends anyway, because
quietly rerouting would put the task on a model nobody chose.
The /loop-goal skill is for the case where the finish line cannot be written
at the start, and the real requirement only shows up a few iterations in.
It asks for a direction, a first move, and a budget — and lets the goal itself
be 不明. Each iteration appends three lines to .loop/<slug>.md: what was
done, what that taught you about the goal, and how the goal was rewritten (or
unchanged). Three unchanged in a row means it has converged, and that is
when a real exit check can finally be written. Five rewrites in a row means the
direction is wrong, not that discovery is working.
The budget is the one thing it will not let you leave blank. Not knowing where a loop ends is fine; not knowing that it ends is not.
| Path | |
|---|---|
config/ghostty/config |
The only place colors are defined. Change the theme here and everything else follows |
config/herdr/config.toml |
The multiplexer the agents run in. Carries Catppuccin — the one deliberate exception to the line above, argued in the file |
agents/global.md |
What every agent reads in every project. Symlinked to ~/.claude/CLAUDE.md and ~/.codex/AGENTS.md |
config/starship.toml |
The shell prompt. A whitelist: any module it does not name is off, and every one it names states an ANSI color |
config/mise/config.toml |
Global Node / Ruby / Terraform versions |
config/git/ignore |
Global gitignore. Symlinked to ~/.config/git/ignore, which git reads by default |
.zsh/path.zsh |
The only place PATH is defined. Sourced from both .zshenv and .zprofile |
.zsh/plugins.zsh |
zsh plugins, all installed by brew bundle |
claude/skills/ |
The agent skills written here rather than installed. Symlinked into both ~/.claude/skills and ~/.agents/skills — claude reads the first, codex and cursor-agent the second |
claude/skill-lock.json |
Record of the installed skills setup.sh restores from upstream |
bin/statusline.py |
The status line claude and cursor-agent both draw. codex gets the nearest built-in items |
bin/delegate.sh |
The only place a delegate's model string is written. Symlinked to ~/.local/bin/delegate. Each CLI still pins its own default in its own config |
Brewfile |
Everything installed on a fresh machine |
CLAUDE.md |
The rules an AI should not break when editing this repo |
Machine-local files are never committed: ~/.zshenv.local,
~/.vimrc.local, ~/.gitconfig.local, ~/.gitconfig.work.
- tmux / screen —
herdris the multiplexer, and it knows which agent is working, blocked or done. Ghostty still owns the window - A vim plugin manager — vim is for commit messages and quick edits
- A zsh plugin manager —
brew bundlealready is one - Flutter — its SDK is a git clone at
~/Development/flutter, which is how Flutter expects to be managed - The AI CLIs' config files —
~/.claude/settings.json,~/.cursor/cli-config.jsonand~/.codex/config.tomleach get rewritten by their own tool and each holds credentials or per-directory trust levels. Onlybin/statusline.pyis tracked; wiring it in is a manual step above.claude/settings.base.jsonis the one exception, and it is a merge rather than a link: it carries the handful of settings that are decisions rather than machine state, andsetup.shmerges them into~/.claude/settings.jsonwithout touching any other key - The herdr agent-state hooks —
~/.claude/settings.json,~/.codex/hooks.jsonand~/.cursor/hooks.json. herdr writes and owns those scripts;setup.shcallsherdr integration installrather than tracking a copy that would go stale on the next herdr release