claude;或者让 Moshpit 现开一个
git worktree,在里面起 Claude Code。开始之前你得有什么。一台你本来就能 SSH 上去的服务器、一台 iOS 18 以上的 iPhone、以及服务器上已经装好的 Claude Code。Moshpit 不负责装 Claude Code,也不和 Anthropic 通信——它驱动的是一个真实的 shell,Claude Code 只是跑在里面的一个程序。
herdr 不是必需的。它的价值在于把「agent 现在什么状态」从猜变成读,所以这份 指南用它。走 tmux 那条路一样能成,见 用 tmux 和 在锁屏上回应 agent。
第一步
先把主机连上
四个字段加一次指纹核对。如果你在电脑上本来就能 ssh 上这台机器,
这一步一分钟就完了。
新建连接
首页右上角点 +。表单是分组的:
- CONNECTION——
Name、Host、Port(默认 22)、Username。Name 和 Host 填了,Save 才会亮。 - AUTHENTICATION——
Password或SSH Key。密钥可以是你在 Settings → SSH Keys 里生成的,也可以直接把 PEM 贴进表单。 - ADVANCED——
Multiplexer选 herdr。下面那个自定义路径除非 你的二进制放在很偏的地方,否则留空:填了路径就等于你替它担保,能力探测会被整个跳过。
多路复用器是按连接选的,不做全局默认。你手上的机器本来装的东西就不一样, 一个全局默认只会换来「怎么就这台连不上」。
指纹核一次
第一次连的时候握手会停下来,Moshpit 把对方给出的主机密钥摆给你看:
New Host
First connection to your-host:22.
Key fingerprint:
SHA256:…
Verify it matches the server (e.g. `ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub`).
[ Cancel ] [ Trust ]
Moshpit 没法替你验这串指纹,它能做的是把该敲的命令一并写给你。 如果以后某台主机的密钥变了,这个对话框会换成 ⚠️ Host Key Changed,两串指纹都印出来,默认按钮是 Disconnect, Trust New Key 被标成破坏性操作。
这里有条限制,现在就该知道。App 里没有任何一个地方能列出或删掉已信任的主机密钥。 删掉一条连接,它的指纹还留着;信任是按设备存的,而且排除在备份之外——换台手机,每台主机都会 重新问你一遍。主机正当换了密钥之后,唯一的 App 内路径就是在那个 ⚠️ 对话框里接受变更。
还有:直接填在连接表单里的密码和 PEM,保存时没有加 Face ID 门禁;只有在 Add Key 里建、并且开了 Require Face ID 的密钥才会每次读取都验人。 带密码短语加密的私钥则完全用不了。
第二步
把 herdr 装到主机上
herdr 是专门为 CLI coding agent 写的 Rust 单二进制。它把
agent_status 做成了每个 pane 上的协议字段——这就是为什么 Moshpit
不用你在主机上装任何东西,也知道 Claude Code 正等着你。
装它
# macOS / Linuxbrew brew install herdr # 其它情况 curl -fsSL https://herdr.dev/install.sh | sh
这里给不出 apt-get 那一行:herdr 不在任何 Linux 发行版仓库里,
它自己的 packaging 只覆盖 Windows。Moshpit 也拒绝硬凑一条
sudo apt-get install -y herdr——一条以「找不到软件包」收场的命令,
用户只会当成是 App 的 bug。
安装脚本把二进制放进 ~/.local/bin,并且刻意不动你任何 rc 文件。
Moshpit 在探测和拉起 herdr 时都会把这个目录加进 PATH,所以主机那边你什么都不用改。
没装的话,App 会直说
选了 herdr 却连到一台没装它的主机上,你得到的是一条可关闭的横幅, 而不是一次失败:
- 横幅写 “herdr not found on this host — plain shell session.”,右边一个 Install herdr。
- 点开 Install Assist,命令已经填好,上面那句是 “Moshpit never installs anything silently. Run the command below in your shell — sudo and its output stay fully visible.”
- Run in terminal 把命令粘进当前这个 shell 执行,sudo 的交互和输出全程可见; 旁边还有 Copy command 和 Re-check。
- 会话照样能用,只是退成裸 shell,而且绝不会被悄悄换成 tmux—— tmux 和 herdr 是两个独立服务器、装的是毫不相干的会话,替你 attach 另一个, 等于把别人的活儿端到你面前说这是你的。
这套集成是对着哪个版本写的。设计基准是 herdr main / v0.8.0,protocol 19;
真机验证跑的是 0.7.3,protocol 16——当时 brew install herdr 装到的就是它。
snapshot 解码器按「字段缺了就降级这一处细节,绝不让整份读取失败」写。
0.7.3 上看得见的代价:它不上报 pane 的命令,所以终端面包屑的第三段会回落成
pane N,而不是告诉你里面跑的是什么。这一段仍然保留,因为它是进
Select Pane sheet 的唯一入口。
这份指南请走 SSH,不要走 Mosh。Mosh 传的是渲染后的屏幕差分,会毁掉按行分帧,
所以它扛不动 herdr 的 frame 协议(tmux -CC 走不了 mosh 是同一个原因)。
走 Mosh 时 herdr 跑的是它自己的全屏 TUI,Moshpit 只是个渲染器——在手机上,
herdr 的侧栏大概要吃掉三分之一屏宽。控制面的那些 sheet 仍然可用,走的是另一条 SSH 旁路。
第三步
起 Claude Code
两条路。在你已有的 pane 里跑,或者让 Moshpit 开一个隔离的 git worktree 在里面跑。后一条才是在手机上真正值得用的那条。
在已有的 pane 里跑
没有新东西要学
- 在 herdr 的 pane 里敲 claude,这一步就完了
- herdr 靠观察自己 pane 的屏幕内容认出它
- 不用配置,也不用多记什么
New Agent Task
一个 worktree、一个 workspace、一个 agent
- 一张表单,入口在首页 AGENTS 那一栏
- 在主机上现开一个 git worktree,并在里面起 Claude Code
- 你自己那份工作区一动不动
- 只有 herdr 有——tmux 给不了这个
两条路上 herdr 都是靠屏幕内容推断出这是个 agent。想要它被上报
而不是被推断,在主机上跑一次 herdr integration install claude,
之后 Claude Code 会自己汇报状态。这更权威,但不是必需的——这一页里的一切,
不装它照样成立。
这张表单,一栏一栏说
TASK 组,脚注:“Creates a git worktree on the host, then starts the agent inside it. Your working tree is untouched.”
- Repo——一个菜单。Moshpit 同时从两处找候选:每个已开 pane 的 cwd,用
git rev-parse --show-toplevel反查 git 根;以及按修改时间扫一遍$HOME。找的过程中菜单写 “Looking for repositories…”;一个都没找到就写 “None found — no panes in repos, and nothing under ~”。最后一项永远是 Other…,点开露出一个Repository path输入框。 - Branch——在手机上先校验,不合法就根本不发请求。空名字、空格、
开头的
-或/、结尾的/、..、 结尾的.lock,以及~ ^ : ? * [ \这些字符,都会被顶回来, 并给一句人话,比如 “No spaces in a branch name”。 - Agent——从
herdr server agent-manifests --json读。默认优先claude,然后codex,再回落到第一个。(按字母序排的时候, 真机上默认值填进去的是agy,所以现在不按字母序了。)
FIRST MESSAGE 组,脚注:“Optional. Sent to the agent once it's running — leave blank to type it yourself.” 点 Start 之后按钮会变成 Starting…——大仓库 checkout 要几十秒,一个从不变化的按钮读起来就像坏了。
点 Start 之后实际跑的是什么
三条命令,按顺序走 SSH。没有任何一步是藏着的;你填的每个标签在靠近 shell 之前都会被单引号包好。
herdr worktree create --cwd '~/code/payments-api' --branch 'fix-webhook-retry' \
--label 'fix-webhook-retry' --focus --json
→ {"type":"worktree_created",
"workspace":{"workspace_id":"w4"},
"root_pane":{"pane_id":"w4:p1",
"cwd":"~/.herdr/worktrees/payments-api/fix-webhook-retry"}}
herdr pane run 'w4:p1' 'claude' <- 把命令敲进去并回车
herdr agent send 'w4:p1' '<第一条消息>' <- 2 秒后,只在你填了的时候
一个新的 git worktree、一个新的 workspace、一个 cwd 已经在里面的 pane,
并且焦点已经过去了。第二步刻意用 pane run 而不是 agent start:
实测 agent start --workspace 不继承 worktree 的目录,新 pane 会落在
发起命令的那个目录里。pane run 敲进去的就是你自己会敲的那一行,而且你看得见。
你会先看到什么
真机验证那一轮:workspace 起来了,cwd 是
~/.herdr/worktrees/…/fix-scroll-jump,git worktree list
确认分支是真建了,Claude Code 真的起来了,停在它的信任目录提示上。herdr 把这个 pane
标成 agent: claude / status: blocked,首页的 Agents 区随即显示
AGENTS 1 · NEEDS YOU。那第一个提示,就是第四步里你要回的那个。
用完之后怎么收
首页长按那条 workspace → Remove Worktree。这个菜单项只在真的是
linked worktree 的 workspace 上出现。第一条命令绝不带 --force:
- Remove the worktree for “…”?——“Deletes the branch checkout under
~/.herdr/worktrees.
payments-apiitself is untouched.” - 只有当 herdr 因为工作区是脏的而拒绝时,才会弹第二个对话框:
“…” has uncommitted changes——“Those changes exist nowhere else. Removing
the worktree throws them away.”按钮是 Delete anyway / Keep it。
只有这一次明确点击才会加上
--force。这是 herdr 自己的安全网, 不是我们发明的——顺着它比替你做决定好。
这一步的边界,摊开讲。
worktree 落在 ~/.herdr/worktrees/<repo>/<branch>,不在仓库旁边,
这一版也没有自定义路径的选项——那不是个值得在手机上打字的东西。大仓库 checkout 要几十秒。
对着一个脏仓库跑 git worktree add 预期没问题(它本来就是独立 checkout),
但还没验证过。
agent 的启动参数不替你选。manifest 里 claude 的默认命令是什么,
跑的就是什么。像 --dangerously-skip-permissions 这种,得你自己在 pane 里敲。
herdr 只看得见自己 pane 里的 agent。你在 Terminal.app 里起的那个
claude 不会出现——herdr 是 runtime,不是进程扫描器。这条实测踩过:
五个 herdr pane 全在空转、另一个终端里 claude 跑得正欢,而 Agents 区诚实地写着
“Nothing running — start a task to isolate one”,用户则确信有东西在跑。
没在设备上验到的一处:长按出来的那个上下文菜单本身。自动化驱动不了 SwiftUI 的长按, 所以「菜单 → 对话框 → 对话框」这段只有编译和单测保证,没有设备录像。
第四步
人在哪,都能回它的权限询问
Claude Code 停下来问你能不能跑某条命令。琥珀色在这个站上只有一个意思—— 「有 agent 在等人」——它出现的地方就是这里:一条能落到锁屏上的通知。
锁屏上的那个问题 标题是 agent,正文是它在问什么、在哪儿问—— Claude needs your permission to use Bash — m1-pro · pit。 同一台主机上几个 agent 一起等的时候,只有一张卡:claude +2。
点一下 直接落在提问的那个 pane 里。你在一个真实的终端里读完提示, 用你在工位上会敲的那几个键回答它。
锁屏上刻意没有 Allow 按钮 这个 App 曾经有过的那排按钮,发的是盲打的按键。批准一个你没读过的请求, 恰恰违背了「你随时能读到它」这个 App 的全部价值——所以它们被拿掉了。
App 没在跑,它是怎么到你手机上的
被挂起的 iPhone 做不到的那部分,你的主机来做。给 pane 盖状态戳的那套 hook,
会把 attention 和 done——从来不含 working——
交给一个小小的发送器 ~/.moshpit/moshpit-push.sh:它用一把只存在于你手机上
的钥匙把提醒封好,把密文交给 Moshpit 的推送中转,再由 Apple 送达。两边都读不到一个字:
agent 名字、命令、问题本身,都是在你手机上的通知扩展里、在锁屏上才解开的。
第一次给主机打开通知时自动完成配对;唯一留下的一次确认,是首次安装 hook 那一下。
适用范围,说在明处。发送器搭在 agent hook 上,而 hook 盖章的对象是
tmux 的 pane——所以「App 关着也能收到推送」这条路,需要你的 agent 跑在装了
hook 的 tmux 里。纯 herdr 的 pane 上,herdr 自带的 agent_status
会在 Moshpit 活着的时候驱动灵动岛和通知;App 一关,推送也就停了。
如果「关着 App 也被叫醒」正是你要的,现阶段请把 Claude Code 跑在 tmux 的 pane 里。
让它保持安静的四条规则
什么都通知,等于什么都没通知——所以每一次打扰都得挣来:
| 规则 | 意味着什么 |
|---|---|
| 先站住 30 秒 | 在宽限窗口内、你在工位上顺手答掉的问题,永远不会惊动任何手机。 |
| 每台主机一张卡 | 所有在等的 agent 共用一张汇总卡——“claude +2”。只有 没人在等 → 有人在等的那个瞬间会响、可以穿透专注模式;之后都只是安静地更新。 |
| 跑满 3 分钟的完成才响 | 一个跑了三分钟以上的回合结束时值得一声提示音;二十秒的回答只进列表, 不点亮屏幕。 |
| 停着的 agent 保持沉默 | 你自己留在提示符上的 agent 发来的「空闲提醒」不会点亮任何东西—— 放了一天的陈旧「需要你」也会自愈成已完成。 |
细节处也诚实:在 App 里看过那个提示就算已读;重连或重启不会为一个 已经告诉过你的问题再响一次。
控制它的四个开关
Settings → NOTIFICATIONS,四个默认都是开的。
- Notifications——“Alert when an agent needs you”
- Live Activity——“Show agent session status in the Dynamic Island”
- Alert sound——“Play a sound when the agent needs you”
- Show detail on lock screen——“Display what the agent is running/asking —
off keeps it private”。关掉之后,工作中 / 需要你 / 已完成照样显示,只是不再露出
Bash: npm install那一行。
Notifications 和 Live Activity 两个都关掉,就什么都不看了。 开关下面的 Set up this host 一行,列出当前连接的主机上都有什么:哪些 agent 装了 hook、推送发送器在不在、这台手机握着哪些配对——安装本身在连接时自动完成。
一处得说在前面的现状:锁屏通知的正文是 hook 从 agent 那里原样抓来的 ——Claude needs your permission to use Bash 这类句子通常是英文; “claude +2” 这种计数则与语言无关。App 内的界面是中文的,锁屏这一层跟着 agent 走。
解锁时 · 需要你 一个 agent 卡住就是琥珀色感叹号,多个就是数量。胶囊里只放得下一个—— 展开后的 Switch 按钮用来轮到别的。
解锁时 · 工作中 Claude Code 在跑的时候是青色加一个走着的计时器。在想的 agent 和卡住的 agent 长得不一样。
诚实的那一段
你把 App 切到后台之后会怎样
在你打算依赖上面那一切之前,这一段才是该读的。现在补上这个缺口的是 密封推送——它补不上的部分,也一并写在这里。
iOS 会挂起连接,实时的那几层就停了
iOS 一挂起 App,那个 2 秒的轮询和 SSH 的拉取也跟着停了。能跨过这个缺口的,
是第四步讲的推送:主机上的 hook 自己注意到 attention 和 done,
自己把密封的提醒发出来——不需要活着的会话,手机锁着、App 关着都行。
跨不过去的是 Live Activity。它渲染的是 App 最后看到的状态,App 被挂起后 就没有东西再喂它了。真机实测:App 切到后台之后,把一个 pane 从 blocked 翻到 working, 灵动岛纹丝不动;只有回到前台、再过一个轮询周期,它才翻过来。
所以灵动岛选择说「暂停了」,而不是骗你
Moshpit 2 working · 1 needs you ● claude NEEDS YOU · 2m mac-studio · ~ · Tab 1 Bash: npm install …… App 被挂起两分钟 …… Moshpit paused — open Moshpit to refresh
Live Activity 带着一个 120 秒的过期线——两个轮询周期没跟上—— 到点就渲染成诚实的「暂停」,而不是一个冻住的「working」。桌面小组件用的是 180 秒, 过期后把所有状态点都压成灰的。只要第四步的发送器在位,推送通知不受这些影响,照样送达。
就算在前台,外面的改动也会滞后
- herdr 的控制面轮询在有变化时是 2 秒一次,连着读到三次一模一样就退到 8 秒。任何变化、或者你手动刷新一下,都会立刻把它拉回 2 秒。
- 你在 App 里做的任何操作都是立即刷新的——每次改动后面都跟一次重读。 但在别处发生的事(你的电脑、另一个客户端、agent 自己变了状态)最多要 8 秒才出现。
- 这也是为什么已持续时间只印到分钟——
now、2m、1h 12m,从不印秒。背后的数据本身就有最多 8 秒的粗糙度, 印出秒来等于宣称一个它没有的精度。 - herdr 的 socket API 其实是有事件订阅的,但它的 CLI 没有暴露 subscribe 子命令, 而手机也打不开你主机上的 Unix socket。所以今天能用的只有轮询。
两个 Moshpit 连同一个 pane 会打架
herdr 的直连是按 pane 独占的。Moshpit 必须带
--takeover 去 attach,因为每次重连都会和自己那条还没死透的旧通道撞上。
于是两台手机连同一个 pane,就会每两秒左右互相抢一次。这是独占式直连的固有行为,
在客户端消不掉。
Moshpit 能做的是:30 秒内被非预期地踢掉 3 次,就暂停 30 秒并给一条看得见的提示 ——“Another client is using this pane — retrying shortly”—— 等画面重新出帧就自动消失。你电脑上跑 herdr 自己的 TUI 不占 attach 名额, 所以「电脑 TUI + 手机」理论上是安全的,但这个组合没有被验证过。
零碎问题
这份指南通常会引出的几个问题
非得用 herdr 吗?
不用。走 tmux 的话,去 Settings → NOTIFICATIONS → Install agent hooks
装一次 hook:一条命令,把 Claude Code 的 UserPromptSubmit、
PreToolUse、Notification、Stop 注册上,
让它们把状态盖到 tmux 的 pane 上。它会先备份
~/.claude/settings.json,永远 exit 0 所以挡不住 agent,
重复跑会自己去重。不装 hook 的话,Moshpit 退回去读 pane 输出和终端响铃——
那是猜,文档里也是当猜来写的。首页的 Agents 区块则是 herdr 独有的。
Moshpit 关掉之后还能叫醒我吗?
能——推送这条路就是为这个存在的。主机上的 hook 用一把只有你手机持有的钥匙把提醒封好, 经 Moshpit 的中转发出;手机上的通知扩展在锁屏上解开它——App 关着、手机锁着都行。 第四步的适用范围同样成立:发送器搭在 agent hook 上、hook 盖章的是 tmux 的 pane, 在乎这条路就把 agent 跑在 tmux 里。四条安静规则——30 秒宽限、每台主机一张卡、 3 分钟以上的完成才响、停着的 agent 沉默——见 在锁屏上回应 agent。
那 Mosh 呢?
想让一个 shell 扛过 Wi-Fi 切 5G,Mosh 是对的选择,但它扛不动 herdr 的 frame 协议,
也扛不动 tmux -CC。走 Mosh 时 herdr 在终端里跑自己的 TUI,侧栏要占掉手机
大约三分之一的宽度;控制面的 sheet 仍然通过另一条 SSH 旁路工作。这份指南请用 SSH。
细节见 Mosh 与漫游。
Claude Code 明明在跑,Agents 区却是空的。
那基本可以肯定它不在 herdr 的 pane 里。herdr 靠观察自己的 pane 认 agent,
所以在 Terminal.app 或另一条 SSH 里起的 claude 对它是不可见的。
用 New Agent Task 起,或者手动在 herdr 的 pane 里跑。herdr 0.7.3 上还有第二层原因:
它不上报 agent 名字,所以空闲状态的 agent 根本长不出那一行。
Moshpit 会读我的代码、我的 prompt,或者别的什么吗?
没有 Moshpit 账号、没有埋点,终端流量只去你自己添加的机器。Moshpit 运营的只有 推送中转这一件东西,而它承载的是自己解不开的密文——提醒在你的主机上、 用只有你手机持有的钥匙封好。唯一会离开 pane 的,是 hook 抓到的那一行短标题 ——agent 正在跑什么或问什么,截到 80 个字符——它全程密封,最终渲染在你自己的锁屏上。
接下来看哪?
连接与密钥讲完整的表单和主机密钥行为, 用 herdr 讲术语和键位, 在锁屏上回应 agent 把通知和 Live Activity 那条链讲透。连不上或者行为奇怪,看 排查。
接下来看 在任何地方连回自己的机器 · 回到指南列表