<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
<title>Loong</title>
<link>https://cloudtides.cn/</link>
<description>AI学习者，生活追随者</description>
<language>zh-CN</language>
<atom:link href="https://cloudtides.cn/rss.xml" rel="self" type="application/rss+xml"/>
<item><title>提问的智慧：怎样提出容易得到答案的技术问题</title><link>https://cloudtides.cn/notes/%E6%8F%90%E9%97%AE%E7%9A%84%E6%99%BA%E6%85%A7-%E6%8A%80%E6%9C%AF%E6%8F%90%E9%97%AE%E6%8C%87%E5%8D%97</link><guid isPermaLink="true">https://cloudtides.cn/notes/%E6%8F%90%E9%97%AE%E7%9A%84%E6%99%BA%E6%85%A7-%E6%8A%80%E6%9C%AF%E6%8F%90%E9%97%AE%E6%8C%87%E5%8D%97</guid><pubDate>Thu, 24 Sep 2026 16:00:00 GMT</pubDate><description>把目标、现象、证据和已做的尝试整理好，让别人能迅速判断问题卡在哪里。基于 How To Ask Questions The Smart Way 的归纳与改写，附提问模板。</description><content:encoded>&lt;blockquote&gt;
&lt;p&gt;本文是对 Eric S. Raymond 与 Rick Moen 的 &lt;a href=&quot;http://www.catb.org/~esr/faqs/smart-questions.html&quot;&gt;How To Ask Questions The Smart Way&lt;/a&gt; 的归纳与改写，参考了&lt;a href=&quot;https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md&quot;&gt;《提问的智慧》简体中文版&lt;/a&gt;。示例和模板为本文新增，并非原文翻译。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;一个好问题，不是把困难扔给别人，而是把&lt;strong&gt;目标、现象、证据和已做的尝试&lt;/strong&gt;整理好，让别人能迅速判断：问题是什么、卡在哪里、怎样帮你最有效。&lt;/p&gt;
&lt;h2&gt;从遇到问题到得到答案&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-mermaid&quot;&gt;flowchart TD
    A[遇到问题] --&amp;gt; B[写清真正目标与实际现象]
    B --&amp;gt; C[搜索文档、FAQ、已有讨论与报错]
    C --&amp;gt; D[动手验证，尽量缩小复现范围]
    D --&amp;gt; E{已经解决？}
    E -- 是 --&amp;gt; F[记录解法，结束]
    E -- 否 --&amp;gt; G[选对项目、社区或同事]
    G --&amp;gt; H[写出可复现、可回答的问题]
    H --&amp;gt; I[根据回复补充信息并验证]
    I --&amp;gt; J[公开补充最终原因与解法]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这套流程的目的不是规定提问前必须独自耗尽所有办法，而是避免让回答者重新做一遍你已经做过的排查。说明自己查过什么、试过什么、发现了什么，往往比写一句“求助”更有用。&lt;/p&gt;
&lt;h2&gt;提问前：先弄清自己要解决什么&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;先说目标，再说卡住的做法。&lt;/strong&gt; 例如，真正的目标是“正确解析 CSV 文件”，却只问“怎样删除字符串里的逗号”，回答者可能会帮你实现一个会破坏带引号字段的做法。交代目标，别人才有机会指出更合适的路径。&lt;/p&gt;
&lt;p&gt;接着做三件事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;搜索完整报错、官方文档、FAQ、已有 issue 或讨论；必要时读相关源码。&lt;/li&gt;
&lt;li&gt;自己运行一次可控的实验，找出触发问题的最少步骤、输入或代码。&lt;/li&gt;
&lt;li&gt;找合适的地方问：项目自身的问题去项目渠道，语言或框架问题去对应社区，内部系统问题找掌握上下文的人。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;提问时：让对方一次看懂&lt;/h2&gt;
&lt;p&gt;一个技术问题通常需要以下信息。按重要程度写，能省略的就省略：&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;信息&lt;/th&gt;&lt;th&gt;应该写什么&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;标题&lt;/td&gt;&lt;td&gt;技术或功能、具体症状、触发条件；避免“救命”“出错了”&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;目标&lt;/td&gt;&lt;td&gt;你最终想实现什么，而不只是当前尝试了哪一步&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;预期与实际&lt;/td&gt;&lt;td&gt;原本希望发生什么，实际发生了什么&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;复现步骤&lt;/td&gt;&lt;td&gt;从什么环境和输入开始，依次做了什么&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;证据&lt;/td&gt;&lt;td&gt;关键报错、相关日志、最小复现代码；只贴与问题有关的部分&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;已做尝试&lt;/td&gt;&lt;td&gt;查过什么、试过什么、每次得到什么结果&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;具体请求&lt;/td&gt;&lt;td&gt;希望别人帮你判断原因、指出排查方向，还是检查某段代码&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;描述&lt;strong&gt;观察到的症状&lt;/strong&gt;，并把“我怀疑是缓存导致的”标成猜测。不要把未经验证的原因写成事实。代码问题尽量提供能重现异常的短例子；数百行代码加一句“不能运行”，通常让人无从下手。&lt;/p&gt;
&lt;h3&gt;一个对照示例&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;难以回答：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Python 排序报错，怎么修？急！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;更容易回答：&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;标题：&lt;/strong&gt; Python 3.12 中按时间戳排序时，含 &lt;code&gt;None&lt;/code&gt; 的记录触发 &lt;code&gt;TypeError&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;我想按 &lt;code&gt;created_at&lt;/code&gt; 时间戳升序排列记录，并把缺少时间戳的记录放在最后。数据里有 &lt;code&gt;None&lt;/code&gt;；运行下面的最小例子会报错：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;records = [{&amp;quot;created_at&amp;quot;: 3}, {&amp;quot;created_at&amp;quot;: None}, {&amp;quot;created_at&amp;quot;: 1}]
sorted(records, key=lambda item: item[&amp;quot;created_at&amp;quot;])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;实际报错是 &lt;code&gt;TypeError: &amp;#x27;&amp;lt;&amp;#x27; not supported between instances of &amp;#x27;NoneType&amp;#x27; and &amp;#x27;int&amp;#x27;&lt;/code&gt;。我确认去掉 &lt;code&gt;None&lt;/code&gt; 后可以排序，但业务上需要保留这些记录。怎样写排序键，才能让有时间戳的记录正常排序、&lt;code&gt;None&lt;/code&gt; 稳定地排在最后？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;第二种问法给出了目标、触发条件、最小复现、实际结果和清楚的求助点。回答者可以直接针对问题作答，也能指出提问者未考虑到的边界。&lt;/p&gt;
&lt;h2&gt;得到回复后：把问题收尾&lt;/h2&gt;
&lt;p&gt;如果回答需要更多信息，就补充可验证的结果；如果建议有效，回到原讨论说明&lt;strong&gt;最终原因、采用的解法和适用条件&lt;/strong&gt;。这样既尊重帮助你的人，也让后来遇到同样问题的人能搜到完整答案。如果暂时没人回复，先检查问题是否发对地方、是否缺少复现信息，再补充关键证据。&lt;/p&gt;
&lt;h2&gt;可复制的提问模板&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;### 标题
&amp;lt;技术/功能&amp;gt; 在 &amp;lt;条件&amp;gt; 下出现 &amp;lt;具体症状&amp;gt;

### 我想实现
&amp;lt;最终目标，以及必要背景&amp;gt;

### 环境
&amp;lt;相关版本、系统、配置；只列会影响问题的内容&amp;gt;

### 复现步骤
1. &amp;lt;步骤一&amp;gt;
2. &amp;lt;步骤二&amp;gt;

### 预期与实际
- 预期：&amp;lt;应该发生什么&amp;gt;
- 实际：&amp;lt;发生了什么；附关键报错或最小复现&amp;gt;

### 我已尝试
- &amp;lt;尝试及结果&amp;gt;

### 想请教
&amp;lt;一个明确、可回答的问题&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;记住一句话：&lt;/strong&gt; 好问题让回答者看见你要去哪里、现在站在哪里，以及已经走过哪些路。&lt;/p&gt;
&lt;h2&gt;来源&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md&quot;&gt;《提问的智慧》简体中文版（GitHub）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.catb.org/~esr/faqs/smart-questions.html&quot;&gt;How To Ask Questions The Smart Way（英文原文）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>提问</category><category>沟通</category><category>效率</category></item>
<item><title>从 cmux 切到 herdr：一份可照做的上手指南</title><link>https://cloudtides.cn/blog/herdr-getting-started</link><guid isPermaLink="true">https://cloudtides.cn/blog/herdr-getting-started</guid><pubDate>Thu, 06 Aug 2026 16:00:00 GMT</pubDate><description>习惯了 cmux 管理多个 Coding Agent，换到 herdr 要补哪些心智模型、快捷键、配置和脚本化编排。30 分钟跑起一套多 Agent 工作流。</description><content:encoded>&lt;blockquote&gt;
&lt;p&gt;面向已经习惯 cmux 管理多个 Coding Agent 的人。目标:&lt;strong&gt;30 分钟内&lt;/strong&gt;用 herdr 跑起一个三端项目(RN App / Web 前端 / 运营后台)的多 Agent 工作流。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;h2&gt;0. 先建立心智模型:cmux ↔ herdr 概念对照&lt;/h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;你在 cmux 里的东西&lt;/th&gt;&lt;th&gt;herdr 里对应什么&lt;/th&gt;&lt;th&gt;关键差别&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;原生 macOS 窗口(libghostty 渲染)&lt;/td&gt;&lt;td&gt;&lt;strong&gt;你自己的终端&lt;/strong&gt;里的 TUI&lt;/td&gt;&lt;td&gt;herdr 不自带渲染器,跑在 iTerm/Ghostty/Warp/Windows Terminal 里都行&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;分屏 pane&lt;/td&gt;&lt;td&gt;pane(同名)&lt;/td&gt;&lt;td&gt;一样支持鼠标拖拽边框、右键菜单&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;标签页&lt;/td&gt;&lt;td&gt;tab&lt;/td&gt;&lt;td&gt;一样&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;—&lt;/td&gt;&lt;td&gt;&lt;strong&gt;workspace&lt;/strong&gt;(多出来的一层)&lt;/td&gt;&lt;td&gt;比 tab 更上一层。&lt;strong&gt;建议一个 workspace = 一个仓库/一个端&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;会话保存在 app 里&lt;/td&gt;&lt;td&gt;&lt;strong&gt;后台常驻 server&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;关掉终端 / SSH 断线,Agent 继续跑&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;只能在这台 Mac 上&lt;/td&gt;&lt;td&gt;SSH 进服务器后 &lt;code&gt;herdr&lt;/code&gt; 直接接管&lt;/td&gt;&lt;td&gt;这是切过去最大的实际收益&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Agent 状态靠自己看&lt;/td&gt;&lt;td&gt;侧边栏统一显示 &lt;code&gt;working / blocked / done / idle&lt;/code&gt;&lt;/td&gt;&lt;td&gt;herdr 的核心卖点&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;你去驱动 Agent&lt;/td&gt;&lt;td&gt;Agent 也能反过来开 pane、互相下 prompt(socket API)&lt;/td&gt;&lt;td&gt;可脚本化编排&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;一句话:&lt;/strong&gt; cmux 是&amp;quot;给 Agent 定制的 GUI 终端&amp;quot;,herdr 是&amp;quot;知道 Agent 在干嘛的 tmux&amp;quot;。切过去要接受的心理落差是:没有原生 GUI 那层壳;换来的是 SSH 常驻 + 可脚本编排。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;顺带一提:两者不冲突。herdr 可以直接跑在 cmux 的一个 pane 里,你不必一次性砍掉 cmux。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;h2&gt;1. 安装(2 分钟)&lt;/h2&gt;
&lt;p&gt;macOS / Linux 任选其一:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 官方安装脚本(推荐,自带 herdr update --handoff 能力)
curl -fsSL https://herdr.dev/install.sh | sh

# 或 Homebrew
brew install herdr

# 或 mise
mise use -g herdr
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Windows(beta):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;powershell -ExecutionPolicy Bypass -c &amp;quot;irm https://herdr.dev/install.ps1 | iex&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;验证:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;herdr --version
herdr --default-config    # 打印全部默认配置,先扫一眼有哪些可调项
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ 用 brew/nix 装的话,后面 &lt;code&gt;herdr update --handoff&lt;/code&gt;(热迁移进程,不中断 Agent)用不了,只能走包管理器升级 + 重启 server。如果你在意&amp;quot;升级不打断 Agent&amp;quot;,装官方脚本版。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;h2&gt;2. 第一次启动(3 分钟)&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cd ~/code/my-app     # 先进你的项目目录
herdr
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;发生了什么:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;它&lt;strong&gt;启动或接管&lt;/strong&gt;一个后台默认 session,你不用管 socket、不用 &lt;code&gt;new-session -s xxx&lt;/code&gt; 这套 tmux 仪式&lt;/li&gt;
&lt;li&gt;没有 workspace 时会自动建一个&lt;/li&gt;
&lt;li&gt;界面是&lt;strong&gt;鼠标优先&lt;/strong&gt;的:pane / tab / workspace 都能点、边框能拖、右键有菜单 → &lt;strong&gt;第一天你可以完全不背快捷键&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;然后在 pane 里直接跑 Agent:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;claude
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;或 &lt;code&gt;codex&lt;/code&gt;、&lt;code&gt;opencode&lt;/code&gt;、&lt;code&gt;pi&lt;/code&gt;、&lt;code&gt;droid&lt;/code&gt;、&lt;code&gt;cursor-agent&lt;/code&gt;、&lt;code&gt;amp&lt;/code&gt; 等。&lt;strong&gt;herdr 自动识别&lt;/strong&gt;,侧边栏立刻出现这个 Agent 的状态灯。&lt;/p&gt;
&lt;h3&gt;试一下&amp;quot;离开再回来&amp;quot;(这是 herdr 的灵魂)&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;给 claude 派个活儿,让它跑起来&lt;/li&gt;
&lt;li&gt;按 &lt;code&gt;ctrl+b&lt;/code&gt; 然后 &lt;code&gt;q&lt;/code&gt; detach —— 或者直接&lt;strong&gt;把终端窗口关了&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;重新开终端,敲 &lt;code&gt;herdr&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Agent 还在跑,输出都在&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;要彻底停掉后台 server:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;herdr server stop
&lt;/code&gt;&lt;/pre&gt;
&lt;hr/&gt;
&lt;h2&gt;3. 只背 6 个快捷键就够(prefix 默认 &lt;code&gt;ctrl+b&lt;/code&gt;,和 tmux 一致)&lt;/h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;动作&lt;/th&gt;&lt;th&gt;键&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;新建 tab&lt;/td&gt;&lt;td&gt;&lt;code&gt;prefix + c&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;向右分屏&lt;/td&gt;&lt;td&gt;&lt;code&gt;prefix + v&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;向下分屏&lt;/td&gt;&lt;td&gt;&lt;code&gt;prefix + -&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;pane 之间移动&lt;/td&gt;&lt;td&gt;&lt;code&gt;prefix + h / j / k / l&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;workspace 切换面板&lt;/td&gt;&lt;td&gt;&lt;code&gt;prefix + w&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;detach 离开&lt;/td&gt;&lt;td&gt;&lt;code&gt;prefix + q&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;第二梯队(用顺手了再加):&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;动作&lt;/th&gt;&lt;th&gt;键&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;pane 全屏放大 / 还原&lt;/td&gt;&lt;td&gt;&lt;code&gt;prefix + z&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;关掉 pane&lt;/td&gt;&lt;td&gt;&lt;code&gt;prefix + x&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;调整 pane 大小(模式)&lt;/td&gt;&lt;td&gt;&lt;code&gt;prefix + r&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;复制模式(翻历史、搜索)&lt;/td&gt;&lt;td&gt;&lt;code&gt;prefix + [&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;跳转选择器(模糊搜所有 pane/workspace)&lt;/td&gt;&lt;td&gt;&lt;code&gt;prefix + g&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;开/关侧边栏&lt;/td&gt;&lt;td&gt;&lt;code&gt;prefix + b&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;新建 workspace&lt;/td&gt;&lt;td&gt;&lt;code&gt;prefix + shift + n&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;切到第 N 个 tab&lt;/td&gt;&lt;td&gt;&lt;code&gt;prefix + 1..9&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;复制模式里:&lt;code&gt;h/j/k/l&lt;/code&gt; &lt;code&gt;w/b/e&lt;/code&gt; 移动,&lt;code&gt;/&lt;/code&gt; 搜索,&lt;code&gt;v&lt;/code&gt; 或空格选中,&lt;code&gt;y&lt;/code&gt; 或回车复制,&lt;code&gt;q&lt;/code&gt;/&lt;code&gt;Esc&lt;/code&gt; 退出。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;prefix + g&lt;/code&gt; 是最被低估的一个键。Agent 一多,靠眼睛找 pane 就崩了,直接模糊搜。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;h2&gt;4. 用你的真实项目搭一套布局(三端项目)&lt;/h2&gt;
&lt;p&gt;思路:&lt;strong&gt;一个 workspace = 一个端&lt;/strong&gt;,每个 workspace 里 = 1 个 Agent pane + 1 个跑命令的 pane。&lt;/p&gt;
&lt;p&gt;手动搭一遍(建议第一次手动做,建立肌肉记忆):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;prefix + shift + n&lt;/code&gt; 新建 workspace,&lt;code&gt;prefix + shift + w&lt;/code&gt; 重命名为 &lt;code&gt;app&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;在 pane 里 &lt;code&gt;cd ~/code/my-app &amp;amp;&amp;amp; claude&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prefix + v&lt;/code&gt; 向右分屏,在新 pane 里跑 &lt;code&gt;npx expo start&lt;/code&gt; 或你的 RN 启动命令&lt;/li&gt;
&lt;li&gt;重复 1–3,建 &lt;code&gt;web&lt;/code&gt;(前端)和 &lt;code&gt;ops&lt;/code&gt;(运营后台)两个 workspace&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prefix + b&lt;/code&gt; 打开侧边栏 —— 现在三个端的 Agent 状态一屏可见&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;给 Agent 起名字&lt;/strong&gt;,这样脚本和 &lt;code&gt;prefix+g&lt;/code&gt; 里才好认:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;herdr agent rename w1:p1 app-dev
herdr agent rename w2:p1 web-dev
herdr agent rename w3:p1 ops-dev

# 之后可以直接按名字跳过去
herdr agent attach web-dev

# 清掉自定义名字
herdr agent rename web-dev --clear
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;没有现成项目?用这个示例项目练手&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;mkdir -p ~/herdr-demo &amp;amp;&amp;amp; cd ~/herdr-demo
git init &amp;amp;&amp;amp; npm init -y
mkdir -p src &amp;amp;&amp;amp; printf &amp;#x27;export const add=(a,b)=&amp;gt;a+b;\n&amp;#x27; &amp;gt; src/math.js
herdr
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后在 herdr 里:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;workspace &lt;code&gt;demo-a&lt;/code&gt;:pane1 跑 &lt;code&gt;claude&lt;/code&gt;,让它&amp;quot;给 src/math.js 写单测&amp;quot;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prefix + v&lt;/code&gt; 分屏,pane2 跑 &lt;code&gt;npm test -- --watch&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prefix + shift + n&lt;/code&gt; 建 workspace &lt;code&gt;demo-b&lt;/code&gt;,跑 &lt;code&gt;codex&lt;/code&gt;,让它&amp;quot;给这个项目加 ESLint 配置&amp;quot;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prefix + b&lt;/code&gt; 看侧边栏:两个 Agent 谁在 &lt;code&gt;working&lt;/code&gt;、谁 &lt;code&gt;blocked&lt;/code&gt;(等你确认)一目了然&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这个 demo 的唯一目的:&lt;strong&gt;体感一下&amp;quot;blocked 状态灯&amp;quot;帮你省了多少次来回切窗口&lt;/strong&gt;。&lt;/p&gt;
&lt;hr/&gt;
&lt;h2&gt;5. 装 hook 集成 —— 让状态灯变准(强烈建议做)&lt;/h2&gt;
&lt;p&gt;herdr 判断 Agent 状态有两条路:&lt;strong&gt;生命周期 hook&lt;/strong&gt;(准)和&lt;strong&gt;读屏幕快照 + TOML 规则匹配&lt;/strong&gt;(兜底)。装上 hook 之后状态判断才是权威的。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;herdr integration install claude
herdr integration status        # 看哪些装了、哪些还在靠读屏
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果哪个 Agent 状态一直不对,用这个排查:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;herdr agent explain &amp;lt;target&amp;gt;                                   # 解释它为什么判成这个状态
herdr agent explain --file screen.txt --agent codex --json     # 拿离屏文本调试规则
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;你之前在 cmux 上折腾过 hook 追踪 session ID 那套 —— herdr 这里是同一个思路,但状态语义(&lt;code&gt;working/blocked/done/idle&lt;/code&gt;)是内置的,不用你自己定义。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;h2&gt;6. 配置文件:一份够用的起步版&lt;/h2&gt;
&lt;p&gt;路径:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;macOS / Linux → &lt;code&gt;~/.config/herdr/config.toml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Windows → &lt;code&gt;%APPDATA%\herdr\config.toml&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-toml&quot;&gt;onboarding = false

[terminal]
new_cwd = &amp;quot;follow&amp;quot;          # 新 pane 继承当前目录,多仓库场景很关键

[keys]
prefix = &amp;quot;ctrl+b&amp;quot;           # 想避开 tmux 冲突可改成 &amp;quot;ctrl+a&amp;quot; 或 &amp;quot;ctrl+space&amp;quot;

# 自定义弹窗命令:prefix+alt+g 开 lazygit
[[keys.command]]
key = &amp;quot;prefix+alt+g&amp;quot;
type = &amp;quot;popup&amp;quot;
command = &amp;quot;lazygit&amp;quot;
description = &amp;quot;run lazygit&amp;quot;
width = &amp;quot;80%&amp;quot;
height = &amp;quot;80%&amp;quot;

[theme]
name = &amp;quot;catppuccin&amp;quot;
auto_switch = true
light_name = &amp;quot;catppuccin-latte&amp;quot;
dark_name = &amp;quot;catppuccin&amp;quot;

[ui]
tab_bar_position = &amp;quot;top&amp;quot;

# 侧边栏每个 Agent 显示两行:状态+位置 / 名字
[ui.sidebar.agents]
row_gap = 0
rows = [
  [&amp;quot;state_icon&amp;quot;, &amp;quot;workspace&amp;quot;, &amp;quot;tab&amp;quot;],
  [&amp;quot;agent&amp;quot;],
]

# 侧边栏 workspace 区显示 git 分支和状态
[ui.sidebar.spaces]
row_gap = 0
rows = [
  [&amp;quot;state_icon&amp;quot;, &amp;quot;workspace&amp;quot;],
  [&amp;quot;branch&amp;quot;, &amp;quot;git_status&amp;quot;],
]

[ui.toast]
delivery = &amp;quot;herdr&amp;quot;
delay_seconds = 1

[ui.toast.herdr]
position = &amp;quot;bottom-right&amp;quot;

[session]
resume_agents_on_restore = true   # server 重启后尝试恢复 Agent 会话
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;想看全量可选项:&lt;code&gt;herdr --default-config&lt;/code&gt;。&lt;/p&gt;
&lt;hr/&gt;
&lt;h2&gt;7. 会话生命周期:哪些能恢复、哪些不能(别踩坑)&lt;/h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;场景&lt;/th&gt;&lt;th&gt;结果&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;detach&lt;/strong&gt;(&lt;code&gt;prefix+q&lt;/code&gt; / 关终端)&lt;/td&gt;&lt;td&gt;server 不停,进程原样继续。&lt;code&gt;herdr&lt;/code&gt; 重连即可&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;server 停了再起&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;快照恢复&lt;/strong&gt;:workspace / tab / pane / 目录布局回来了,但&lt;strong&gt;进程没了&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Agent 会话本身&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;若 Agent 支持原生 session restore(且开了 &lt;code&gt;resume_agents_on_restore&lt;/code&gt;),对话可以续上&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;pane 里的历史输出&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;默认&lt;strong&gt;不保留&lt;/strong&gt;,需显式开实验特性&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;升级 herdr&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;herdr update --handoff&lt;/code&gt; 尝试把活进程热迁移到新 server(仅官方安装脚本版可用)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;开启 pane 输出回放:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-toml&quot;&gt;[experimental]
pane_history = true
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ 安全提醒:pane 输出里可能含 token、密钥、prompt 原文。开了 &lt;code&gt;pane_history&lt;/code&gt; 就意味着这些会落盘到 session 文件。如果产品处理用户隐私数据,建议要么别开,要么把 herdr 的 session 目录纳入你之前做的 redactor / PII lint 那套检查。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;h2&gt;8. 进阶:脚本化编排(cmux 给不了你的东西)&lt;/h2&gt;
&lt;p&gt;这是切过去真正的杠杆。herdr 的 CLI 就是它的编排 API,创建类命令&lt;strong&gt;返回 JSON&lt;/strong&gt;,用 &lt;code&gt;jq&lt;/code&gt; 取 ID,不要自己猜 ID。&lt;/p&gt;
&lt;p&gt;核心命令速查:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 布局
herdr workspace create --cwd &amp;lt;path&amp;gt; --label &amp;lt;name&amp;gt; [--no-focus]
herdr tab create &amp;lt;workspace&amp;gt;
herdr pane split &amp;lt;pane_id&amp;gt; --direction right|down [--no-focus]

# Agent 控制
herdr agent start &amp;lt;name&amp;gt; --kind &amp;lt;type&amp;gt; --pane &amp;lt;pane_id&amp;gt; -- &amp;lt;args&amp;gt;
herdr agent prompt &amp;lt;agent&amp;gt; &amp;quot;&amp;lt;text&amp;gt;&amp;quot; --wait --timeout &amp;lt;ms&amp;gt;
herdr agent wait &amp;lt;agent&amp;gt; --until &amp;lt;idle|done|blocked|working&amp;gt; --timeout &amp;lt;ms&amp;gt;
herdr agent send-keys &amp;lt;agent&amp;gt; &amp;lt;key|chord&amp;gt;
herdr agent read &amp;lt;agent&amp;gt; --source recent|visible --lines &amp;lt;N&amp;gt;
herdr agent get &amp;lt;pane_id&amp;gt;

# 裸 pane 控制(跑测试/构建)
herdr pane run &amp;lt;pane_id&amp;gt; &amp;quot;&amp;lt;command&amp;gt;&amp;quot;
herdr pane send-text &amp;lt;pane_id&amp;gt; &amp;quot;&amp;lt;text&amp;gt;&amp;quot;
herdr pane wait-output &amp;lt;pane_id&amp;gt; --regex &amp;quot;&amp;lt;pattern&amp;gt;&amp;quot; --timeout &amp;lt;ms&amp;gt;
herdr pane read &amp;lt;pane_id&amp;gt; --lines &amp;lt;N&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;可直接改用的脚本:一键起三端&lt;/h3&gt;
&lt;p&gt;存成 &lt;code&gt;~/bin/stack-up.sh&lt;/code&gt;,&lt;code&gt;chmod +x&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;#!/usr/bin/env bash
set -euo pipefail

boot () {
  local label=&amp;quot;$1&amp;quot; dir=&amp;quot;$2&amp;quot; agent=&amp;quot;$3&amp;quot; cmd=&amp;quot;$4&amp;quot;

  ws=$(herdr workspace create --cwd &amp;quot;$dir&amp;quot; --label &amp;quot;$label&amp;quot; --no-focus | jq -r &amp;#x27;.result.id&amp;#x27;)
  pane=$(herdr pane split &amp;quot;$ws:p1&amp;quot; --direction right --no-focus | jq -r &amp;#x27;.result.id&amp;#x27;)

  herdr agent start &amp;quot;$label&amp;quot; --kind claude --pane &amp;quot;$ws:p1&amp;quot;
  herdr pane run &amp;quot;$pane&amp;quot; &amp;quot;$cmd&amp;quot;

  echo &amp;quot;✅ $label 就绪 (workspace=$ws, side-pane=$pane)&amp;quot;
}

boot app ~/code/my-app   claude &amp;quot;npx expo start&amp;quot;
boot web ~/code/my-web   claude &amp;quot;pnpm dev&amp;quot;
boot ops ~/code/my-ops   claude &amp;quot;pnpm dev&amp;quot;

herdr
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;先把三行 &lt;code&gt;boot&lt;/code&gt; 里的路径和启动命令换成你实际的。&lt;code&gt;--kind&lt;/code&gt; 按你要用的 Agent 改(&lt;code&gt;claude&lt;/code&gt; / &lt;code&gt;codex&lt;/code&gt; / …)。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;更有意思的用法:把你的双 Agent bugfix 流水线搬过来&lt;/h3&gt;
&lt;p&gt;你之前设计的&amp;quot;实现 Agent + 审查 Agent + 人工审批闸门&amp;quot;那套,在 herdr 里可以这样落地:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;#!/usr/bin/env bash
set -euo pipefail
ISSUE=&amp;quot;$1&amp;quot;

# 1. 实现 Agent 干活,阻塞等它完成
herdr agent prompt impl &amp;quot;按 .rca/${ISSUE}/facts.lock.json 修复该问题,只改必要文件&amp;quot; \
  --wait --timeout 1800000

# 2. 等它真的进 done 态
herdr agent wait impl --until done --timeout 1800000

# 3. 把结果交给审查 Agent
herdr agent prompt reviewer &amp;quot;审查 impl 针对 ${ISSUE} 的改动,对照 facts.lock.json 逐条核验&amp;quot; \
  --wait --timeout 900000

# 4. 审查 Agent 一旦 blocked(需要人拍板),侧边栏会亮起来提醒你
herdr agent wait reviewer --until blocked --timeout 900000
echo &amp;quot;⚠️ ${ISSUE} 需要人工审批,去 reviewer pane 看&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;关键点:&lt;code&gt;--wait&lt;/code&gt; / &lt;code&gt;wait --until&lt;/code&gt; 让你能写&lt;strong&gt;真正的串行编排&lt;/strong&gt;,而不是靠人肉盯屏幕。这正好接上你 &lt;code&gt;.rca/&amp;lt;issue-id&amp;gt;/&lt;/code&gt; 文件契约那套设计 —— 状态机由 herdr 管,事实由 git 里的文件管。&lt;/p&gt;
&lt;hr/&gt;
&lt;h2&gt;9. 从 cmux 迁移的实际注意事项&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;prefix 冲突&lt;/strong&gt;:你如果本来就在用 tmux,&lt;code&gt;ctrl+b&lt;/code&gt; 会打架。要么改 herdr 的 prefix,要么别嵌套(herdr 本身就是多路复用器,不需要再套 tmux)。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;别指望 GUI 特性跟过来&lt;/strong&gt;:cmux 的浏览器自动化、系统通知这类原生能力,herdr 没有。真需要的话,让 herdr 跑在 cmux 的一个 pane 里,两边都要。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第一天用鼠标就行&lt;/strong&gt;:herdr 是鼠标原生的,不必先背快捷键再开工。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;状态灯不准 ≠ 坏了&lt;/strong&gt;:先 &lt;code&gt;herdr integration install &amp;lt;agent&amp;gt;&lt;/code&gt;,再 &lt;code&gt;herdr agent explain&lt;/code&gt; 排查。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;进程恢复的边界&lt;/strong&gt;:server 重启后布局回来但进程不回来 —— 长任务前先确认别手滑 &lt;code&gt;herdr server stop&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;许可证&lt;/strong&gt;:herdr 是 Apache-2.0(对比页面上有 AGPL 的说法,以仓库 LICENSE 为准),商用前建议自己确认一遍当前版本。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr/&gt;
&lt;h2&gt;10. 第一天验收清单&lt;/h2&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt; &lt;code&gt;herdr --version&lt;/code&gt; 有输出&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt; &lt;code&gt;herdr&lt;/code&gt; 能起来,pane 里能跑 &lt;code&gt;claude&lt;/code&gt;,侧边栏出现状态灯&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt; 关掉终端 → 重开 → &lt;code&gt;herdr&lt;/code&gt; → Agent 还在跑&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt; &lt;code&gt;herdr integration install claude&lt;/code&gt; + &lt;code&gt;herdr integration status&lt;/code&gt; 显示已装&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt; 写好 &lt;code&gt;~/.config/herdr/config.toml&lt;/code&gt;,主题/侧边栏是你想要的样子&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt; 三个 workspace(app / web / ops)建起来并重命名&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt; &lt;code&gt;prefix + g&lt;/code&gt; 能模糊跳到任意 pane&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt; &lt;code&gt;stack-up.sh&lt;/code&gt; 能一键复现整套布局&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt; 至少跑通一次 &lt;code&gt;herdr agent prompt ... --wait&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h2&gt;附:官方文档入口&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;快速开始 — &lt;a href=&quot;https://herdr.dev/docs/quick-start/&quot;&gt;https://herdr.dev/docs/quick-start/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;安装 — &lt;a href=&quot;https://herdr.dev/docs/install/&quot;&gt;https://herdr.dev/docs/install/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Agent 支持与检测 — &lt;a href=&quot;https://herdr.dev/docs/agents/&quot;&gt;https://herdr.dev/docs/agents/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;会话状态 / 恢复 — &lt;a href=&quot;https://herdr.dev/docs/session-state/&quot;&gt;https://herdr.dev/docs/session-state/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;配置 — &lt;a href=&quot;https://herdr.dev/docs/configuration/&quot;&gt;https://herdr.dev/docs/configuration/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;键位 — &lt;a href=&quot;https://herdr.dev/docs/keyboard/&quot;&gt;https://herdr.dev/docs/keyboard/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Agent 自动化(CLI / socket API)— &lt;a href=&quot;https://herdr.dev/docs/agent-automation/&quot;&gt;https://herdr.dev/docs/agent-automation/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;仓库 — &lt;a href=&quot;https://github.com/herdrdev/herdr&quot;&gt;https://github.com/herdrdev/herdr&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;小技巧:herdr 官方提供了一份&amp;quot;agent guide&amp;quot;,你可以直接把 &lt;a href=&quot;https://herdr.dev/docs/&quot;&gt;https://herdr.dev/docs/&lt;/a&gt; 丢给 Claude Code,让它读完之后&lt;strong&gt;在你机器上边配边解释&lt;/strong&gt;,比看文档快。&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded><category>AI</category><category>Agent</category><category>herdr</category><category>工具</category></item>
<item><title>herdr 实操：Flutter 双模拟器 + 双 Agent 工作流</title><link>https://cloudtides.cn/blog/herdr-flutter-dual-agent</link><guid isPermaLink="true">https://cloudtides.cn/blog/herdr-flutter-dual-agent</guid><pubDate>Thu, 06 Aug 2026 16:00:00 GMT</pubDate><description>用 herdr 同时跑 iOS、Android 两个模拟器，Claude Code 负责实现、Codex 负责审查，Agent 改完代码自动热重载并跑 analyze。</description><content:encoded>&lt;blockquote&gt;
&lt;p&gt;项目:一个单仓 Flutter App(下文路径用 &lt;code&gt;~/code/my-flutter-app&lt;/code&gt; 代替)
目标:iOS 模拟器 + Android 模拟器双开,Claude Code 写实现 / Codex 做审查
从 cmux 切过来,预计 40 分钟跑通全套&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;h2&gt;1. 开工前自检(2 分钟)&lt;/h2&gt;
&lt;p&gt;先跑这几条,确认下面的脚本里哪些需要改:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cd ~/code/my-flutter-app

pwd &amp;amp;&amp;amp; ls -1                      # 确认有 lib/ test/ pubspec.yaml ios/ android/
grep -n &amp;quot;^name:&amp;quot; pubspec.yaml     # 拿到包名
flutter --version
flutter devices                   # ★ 记下 iOS/Android 的 device id,后面脚本要用
git branch --show-current
ls -d */ | head -20               # 看有没有 packages/ 之类的子目录
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;flutter devices&lt;/code&gt; 输出大概长这样,&lt;strong&gt;把两个 id 抄下来&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iPhone 15 Pro (mobile) • 3A2B1C4D-...-E5F6 • ios      • com.apple.CoreSimulator...
sdk gphone64 arm64 (mobile) • emulator-5554 • android-arm64 • Android 14 (API 34)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;后面脚本里我用占位 &lt;code&gt;&amp;lt;IOS_ID&amp;gt;&lt;/code&gt; / &lt;code&gt;&amp;lt;ANDROID_ID&amp;gt;&lt;/code&gt;,你替换成实际值。也可以偷懒用 &lt;code&gt;-d ios&lt;/code&gt; / &lt;code&gt;-d android&lt;/code&gt;,但同时开两台同类设备时会歧义。&lt;/p&gt;
&lt;hr/&gt;
&lt;h2&gt;2. 安装 herdr(2 分钟)&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl -fsSL https://herdr.dev/install.sh | sh
herdr --version
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;用官方脚本装,不要用 brew —— 后面 &lt;code&gt;herdr update --handoff&lt;/code&gt;(升级时热迁移进程、不打断正在跑的 Agent 和 &lt;code&gt;flutter run&lt;/code&gt;)只有脚本版支持。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;h2&gt;3. 装 Agent 集成(1 分钟,别跳过)&lt;/h2&gt;
&lt;p&gt;herdr 判断 Agent 状态有两条路:&lt;strong&gt;生命周期 hook&lt;/strong&gt;(准)和&lt;strong&gt;读屏幕快照 + 规则匹配&lt;/strong&gt;(兜底)。你要挂两个 Agent,状态灯准不准直接决定这套东西有没有用。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;herdr integration install claude
herdr integration install codex
herdr integration status          # 两行都应该显示已安装
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;后面如果发现某个 Agent 状态一直不对:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;herdr agent explain impl          # 解释它为什么被判成当前状态
&lt;/code&gt;&lt;/pre&gt;
&lt;hr/&gt;
&lt;h2&gt;4. 配置文件(3 分钟)&lt;/h2&gt;
&lt;p&gt;写到 &lt;code&gt;~/.config/herdr/config.toml&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-toml&quot;&gt;onboarding = false

[terminal]
new_cwd = &amp;quot;follow&amp;quot;

[keys]
prefix = &amp;quot;ctrl+b&amp;quot;

# prefix+alt+g 弹 lazygit —— Flutter 项目改动碎,这个很顺手
[[keys.command]]
key = &amp;quot;prefix+alt+g&amp;quot;
type = &amp;quot;popup&amp;quot;
command = &amp;quot;lazygit&amp;quot;
description = &amp;quot;run lazygit&amp;quot;
width = &amp;quot;80%&amp;quot;
height = &amp;quot;80%&amp;quot;

[theme]
name = &amp;quot;catppuccin&amp;quot;
auto_switch = true
light_name = &amp;quot;catppuccin-latte&amp;quot;
dark_name = &amp;quot;catppuccin&amp;quot;

[ui]
tab_bar_position = &amp;quot;top&amp;quot;

# 侧边栏:两个 Agent 各占两行,一眼看出谁在 working / 谁 blocked
[ui.sidebar.agents]
row_gap = 0
rows = [
  [&amp;quot;state_icon&amp;quot;, &amp;quot;workspace&amp;quot;, &amp;quot;tab&amp;quot;],
  [&amp;quot;agent&amp;quot;],
]

[ui.sidebar.spaces]
row_gap = 0
rows = [
  [&amp;quot;state_icon&amp;quot;, &amp;quot;workspace&amp;quot;],
  [&amp;quot;branch&amp;quot;, &amp;quot;git_status&amp;quot;],
]

[ui.toast]
delivery = &amp;quot;herdr&amp;quot;
delay_seconds = 1

[ui.toast.herdr]
position = &amp;quot;bottom-right&amp;quot;

[session]
resume_agents_on_restore = true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;⚠️ 特别注意:不要开 &lt;code&gt;[experimental] pane_history = true&lt;/code&gt;。&lt;/strong&gt;
如果 App 处理用户隐私数据,pane 输出里会出现真实的用户数据、API token、prompt 原文,开了这个就意味着这些内容落盘到 herdr 的 session 文件。要开的话,先把 herdr 的 session 目录纳入你之前那套 redactor / PII lint 检查再说。&lt;/p&gt;
&lt;hr/&gt;
&lt;h2&gt;5. 手动搭一遍布局(第一次务必手动做)&lt;/h2&gt;
&lt;p&gt;设计:&lt;strong&gt;两个 workspace,职责分离&lt;/strong&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;workspace: cr-impl            workspace: cr-review
┌──────────┬──────────┐       ┌──────────┬──────────┐
│ claude   │ flutter  │       │ codex    │ flutter  │
│ (impl)   │ run ios  │       │ (review) │ analyze  │
│          ├──────────┤       │          ├──────────┤
│          │ flutter  │       │          │ flutter  │
│          │ run andr │       │          │ test     │
└──────────┴──────────┘       └──────────┴──────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;操作步骤:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;终端里 &lt;code&gt;cd ~/code/my-flutter-app &amp;amp;&amp;amp; herdr&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prefix + shift + w&lt;/code&gt; 把当前 workspace 改名 &lt;code&gt;cr-impl&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;左边 pane 跑 &lt;code&gt;claude&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prefix + v&lt;/code&gt; 向右分屏 → &lt;code&gt;flutter run -d &amp;lt;IOS_ID&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prefix + -&lt;/code&gt; 在右侧再向下分屏 → &lt;code&gt;flutter run -d &amp;lt;ANDROID_ID&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prefix + shift + n&lt;/code&gt; 新建 workspace,&lt;code&gt;prefix + shift + w&lt;/code&gt; 改名 &lt;code&gt;cr-review&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;左 pane 跑 &lt;code&gt;codex&lt;/code&gt;;&lt;code&gt;prefix + v&lt;/code&gt; 分屏跑 &lt;code&gt;flutter analyze --watch&lt;/code&gt;(或 &lt;code&gt;flutter test&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prefix + b&lt;/code&gt; 打开侧边栏 —— 两个 Agent 的状态灯出来了&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;给 Agent 起名,后面脚本和 &lt;code&gt;prefix+g&lt;/code&gt; 全靠它:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;herdr agent rename w1:p1 impl
herdr agent rename w2:p1 review

herdr agent attach impl      # 之后直接按名字跳过去
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;验证&amp;quot;离开再回来&amp;quot;&lt;/h3&gt;
&lt;p&gt;给 claude 派个活儿,然后 &lt;code&gt;prefix + q&lt;/code&gt; detach,&lt;strong&gt;把终端窗口关掉&lt;/strong&gt;,重开终端敲 &lt;code&gt;herdr&lt;/code&gt; —— Agent 还在跑,两个 &lt;code&gt;flutter run&lt;/code&gt; 也还活着。这是切过来最实在的收益:再也不会因为误关终端把双模拟器热重载会话搞没。&lt;/p&gt;
&lt;hr/&gt;
&lt;h2&gt;6. 只背这 7 个键&lt;/h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;动作&lt;/th&gt;&lt;th&gt;键&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;pane 间移动&lt;/td&gt;&lt;td&gt;&lt;code&gt;prefix + h/j/k/l&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;向右 / 向下分屏&lt;/td&gt;&lt;td&gt;&lt;code&gt;prefix + v&lt;/code&gt; / &lt;code&gt;prefix + -&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;pane 全屏放大(看 flutter 日志时救命)&lt;/td&gt;&lt;td&gt;&lt;code&gt;prefix + z&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;模糊跳转到任意 pane / workspace&lt;/td&gt;&lt;td&gt;&lt;code&gt;prefix + g&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;开关侧边栏&lt;/td&gt;&lt;td&gt;&lt;code&gt;prefix + b&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;复制模式(翻 stack trace)&lt;/td&gt;&lt;td&gt;&lt;code&gt;prefix + [&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;detach&lt;/td&gt;&lt;td&gt;&lt;code&gt;prefix + q&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;复制模式里:&lt;code&gt;/&lt;/code&gt; 搜索、&lt;code&gt;v&lt;/code&gt; 选中、&lt;code&gt;y&lt;/code&gt; 复制、&lt;code&gt;q&lt;/code&gt; 退出。Flutter 的异常栈很长,&lt;code&gt;prefix+[&lt;/code&gt; 然后 &lt;code&gt;/Exception&lt;/code&gt; 直接定位。&lt;/p&gt;
&lt;hr/&gt;
&lt;h2&gt;7. 一键起项目:&lt;code&gt;cr-up.sh&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;存成 &lt;code&gt;~/bin/cr-up.sh&lt;/code&gt; 并 &lt;code&gt;chmod +x ~/bin/cr-up.sh&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;#!/usr/bin/env bash
set -euo pipefail

REPO=~/code/my-flutter-app
IOS_ID=&amp;quot;&amp;lt;IOS_ID&amp;gt;&amp;quot;          # ← 换成 flutter devices 里的实际值
ANDROID_ID=&amp;quot;&amp;lt;ANDROID_ID&amp;gt;&amp;quot;  # ←

# ---------- workspace 1: 实现 ----------
ws1=$(herdr workspace create --cwd &amp;quot;$REPO&amp;quot; --label cr-impl --no-focus | jq -r &amp;#x27;.result.id&amp;#x27;)
p_ios=$(herdr pane split &amp;quot;$ws1:p1&amp;quot; --direction right --no-focus | jq -r &amp;#x27;.result.id&amp;#x27;)
p_and=$(herdr pane split &amp;quot;$p_ios&amp;quot;  --direction down  --no-focus | jq -r &amp;#x27;.result.id&amp;#x27;)

herdr agent start impl --kind claude --pane &amp;quot;$ws1:p1&amp;quot;
herdr pane run &amp;quot;$p_ios&amp;quot; &amp;quot;cd $REPO &amp;amp;&amp;amp; flutter run -d $IOS_ID&amp;quot;
herdr pane run &amp;quot;$p_and&amp;quot; &amp;quot;cd $REPO &amp;amp;&amp;amp; flutter run -d $ANDROID_ID&amp;quot;

# ---------- workspace 2: 审查 ----------
ws2=$(herdr workspace create --cwd &amp;quot;$REPO&amp;quot; --label cr-review --no-focus | jq -r &amp;#x27;.result.id&amp;#x27;)
p_test=$(herdr pane split &amp;quot;$ws2:p1&amp;quot; --direction right --no-focus | jq -r &amp;#x27;.result.id&amp;#x27;)

herdr agent start review --kind codex --pane &amp;quot;$ws2:p1&amp;quot;
herdr pane run &amp;quot;$p_test&amp;quot; &amp;quot;cd $REPO &amp;amp;&amp;amp; flutter analyze&amp;quot;

# 把 pane id 存下来,给后面的热重载脚本用
cat &amp;gt; ~/.cr-panes &amp;lt;&amp;lt;EOF
CR_IOS_PANE=$p_ios
CR_AND_PANE=$p_and
CR_TEST_PANE=$p_test
EOF

echo &amp;quot;✅ cr-impl=$ws1  cr-review=$ws2&amp;quot;
echo &amp;quot;   ios=$p_ios  android=$p_and  test=$p_test&amp;quot;
herdr
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;跑一次 &lt;code&gt;cr-up.sh&lt;/code&gt;,整套环境起来。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;jq&lt;/code&gt; 没装的话:&lt;code&gt;brew install jq&lt;/code&gt;。创建类命令返回 JSON,&lt;strong&gt;不要自己猜 pane id&lt;/strong&gt;,一定从 &lt;code&gt;.result.id&lt;/code&gt; 取。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;h2&gt;8. Flutter 专属杀手锏:让 Agent 改完代码自动热重载&lt;/h2&gt;
&lt;p&gt;这是 cmux 给不了你的东西。&lt;code&gt;flutter run&lt;/code&gt; 的热重载是靠**在那个终端里按 &lt;code&gt;r&lt;/code&gt;**触发的 —— herdr 能远程把这个键送过去。&lt;/p&gt;
&lt;p&gt;存成 &lt;code&gt;~/bin/cr-reload.sh&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;#!/usr/bin/env bash
set -euo pipefail
source ~/.cr-panes

herdr pane send-keys &amp;quot;$CR_IOS_PANE&amp;quot; r    # hot reload iOS
herdr pane send-keys &amp;quot;$CR_AND_PANE&amp;quot; r    # hot reload Android
echo &amp;quot;🔥 双端已热重载&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;R&lt;/code&gt;(大写)是 hot restart,状态全清:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;herdr pane send-keys &amp;quot;$CR_IOS_PANE&amp;quot; R
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;组合起来:Agent 改完 → 自动重载 → 自动 analyze&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;~/bin/cr-task.sh&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;#!/usr/bin/env bash
set -euo pipefail
source ~/.cr-panes
TASK=&amp;quot;$1&amp;quot;

# 1. 派活给 claude,阻塞等它做完(30 分钟上限)
herdr agent prompt impl &amp;quot;$TASK&amp;quot; --wait --timeout 1800000
herdr agent wait impl --until done --timeout 1800000

# 2. 双端热重载
herdr pane send-keys &amp;quot;$CR_IOS_PANE&amp;quot; r
herdr pane send-keys &amp;quot;$CR_AND_PANE&amp;quot; r

# 3. 跑静态检查,等它出结果
herdr pane run &amp;quot;$CR_TEST_PANE&amp;quot; &amp;quot;flutter analyze &amp;amp;&amp;amp; flutter test&amp;quot;
herdr pane wait-output &amp;quot;$CR_TEST_PANE&amp;quot; --regex &amp;quot;All tests passed|Some tests failed|error •&amp;quot; --timeout 600000

# 4. 把检查结果读出来
herdr pane read &amp;quot;$CR_TEST_PANE&amp;quot; --lines 60
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;用法:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cr-task.sh &amp;quot;把 lib/pages/record_detail.dart 里的时间格式统一成 yyyy-MM-dd HH:mm,补上对应 widget test&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;这就是从 cmux 切过来最大的收益&lt;/strong&gt;:以前你得人肉盯着 claude 什么时候写完、切过去按 r、再切到另一个 pane 跑 analyze;现在一条命令串起来。&lt;/p&gt;
&lt;hr/&gt;
&lt;h2&gt;9. 双 Agent 流程:claude 实现 + codex 审查&lt;/h2&gt;
&lt;p&gt;对应你之前设计的那套「实现 Agent + 审查 Agent + 人工审批闸门」。&lt;code&gt;~/bin/cr-review.sh&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;#!/usr/bin/env bash
set -euo pipefail
ISSUE=&amp;quot;$1&amp;quot;

# 1. claude 实现
herdr agent prompt impl \
  &amp;quot;修复 ${ISSUE}。只改必要文件,改完把涉及的文件列表输出出来&amp;quot; \
  --wait --timeout 1800000
herdr agent wait impl --until done --timeout 1800000

# 2. 把改动交给 codex 审查
herdr agent prompt review \
  &amp;quot;审查工作区当前未提交的改动(git diff),针对 ${ISSUE}。重点看:空安全、await 遗漏、setState 在 dispose 后调用、用户隐私数据是否被打进日志。有问题直接指出文件和行号&amp;quot; \
  --wait --timeout 900000

# 3. 审查 Agent 一旦 blocked = 需要你拍板,侧边栏会亮
herdr agent wait review --until blocked --timeout 900000 || true

echo &amp;quot;⚠️  ${ISSUE} 审查完成/需人工确认 → prefix+g 跳到 review&amp;quot;
herdr agent read review --source recent --lines 80
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;配合你 &lt;code&gt;.rca/&amp;lt;issue-id&amp;gt;/&lt;/code&gt; 那套文件契约:&lt;strong&gt;状态机交给 herdr 管,事实交给 git 里的文件管&lt;/strong&gt;,两边不重叠。&lt;/p&gt;
&lt;hr/&gt;
&lt;h2&gt;10. 日常三个高频动作&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;① 改一行 UI 想看双端效果&lt;/strong&gt;
&lt;code&gt;prefix+g&lt;/code&gt; → 跳到 claude pane → 说需求 → 等状态灯变 &lt;code&gt;done&lt;/code&gt; → &lt;code&gt;cr-reload.sh&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;② 模拟器卡死要重启&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;source ~/.cr-panes
herdr pane send-keys &amp;quot;$CR_IOS_PANE&amp;quot; q          # 退出 flutter run
herdr pane run &amp;quot;$CR_IOS_PANE&amp;quot; &amp;quot;flutter run -d &amp;lt;IOS_ID&amp;gt;&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;③ 想看某个 Agent 最近说了啥,但不想切过去&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;herdr agent read impl --source recent --lines 40
&lt;/code&gt;&lt;/pre&gt;
&lt;hr/&gt;
&lt;h2&gt;11. 从 cmux 迁过来的实际注意事项&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;prefix 冲突&lt;/strong&gt;:如果你还在用 tmux,&lt;code&gt;ctrl+b&lt;/code&gt; 会打架。herdr 本身就是多路复用器,别嵌套;非要共存就把 herdr 的 prefix 改成 &lt;code&gt;ctrl+space&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;cmux 的原生能力不会跟过来&lt;/strong&gt;:浏览器自动化、系统通知这些没有。真需要就让 herdr 跑在 cmux 的一个 pane 里,不必一次性砍掉 cmux。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;server 重启 ≠ 进程恢复&lt;/strong&gt;:&lt;code&gt;herdr server stop&lt;/code&gt; 之后重启,workspace/tab/pane/目录布局会回来,但 &lt;code&gt;flutter run&lt;/code&gt; 和 Agent 进程&lt;strong&gt;不会&lt;/strong&gt;自动复活 —— 重跑 &lt;code&gt;cr-up.sh&lt;/code&gt; 就行。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第一天用鼠标&lt;/strong&gt;:herdr 是鼠标原生的,pane 能点能拖能右键,不必先背快捷键。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;iOS 构建慢&lt;/strong&gt;:第一次 &lt;code&gt;flutter run -d ios&lt;/code&gt; 可能几分钟,&lt;code&gt;pane wait-output&lt;/code&gt; 的 timeout 别设太短。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr/&gt;
&lt;h2&gt;12. 验收清单&lt;/h2&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt; &lt;code&gt;flutter devices&lt;/code&gt; 拿到两个 device id 并填进 &lt;code&gt;cr-up.sh&lt;/code&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt; &lt;code&gt;herdr integration status&lt;/code&gt; 显示 claude 和 codex 都已安装&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt; &lt;code&gt;~/.config/herdr/config.toml&lt;/code&gt; 写好,&lt;strong&gt;确认没开 &lt;code&gt;pane_history&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt; &lt;code&gt;cr-up.sh&lt;/code&gt; 一键起来:2 个 workspace、双模拟器在跑、两个 Agent 有状态灯&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt; 关掉终端 → &lt;code&gt;herdr&lt;/code&gt; → 一切还在&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt; &lt;code&gt;cr-reload.sh&lt;/code&gt; 能触发双端热重载&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt; &lt;code&gt;cr-task.sh &amp;quot;随便一个小改动&amp;quot;&lt;/code&gt; 能跑完整条链路&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt; &lt;code&gt;prefix + g&lt;/code&gt; 能在 4 个 pane 之间瞬移&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h2&gt;附:命令速查&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 布局
herdr workspace create --cwd &amp;lt;path&amp;gt; --label &amp;lt;name&amp;gt; [--no-focus]
herdr pane split &amp;lt;pane_id&amp;gt; --direction right|down [--no-focus]

# Agent
herdr agent start &amp;lt;name&amp;gt; --kind claude|codex --pane &amp;lt;pane_id&amp;gt;
herdr agent rename &amp;lt;pane_id&amp;gt; &amp;lt;name&amp;gt;
herdr agent attach &amp;lt;name&amp;gt;
herdr agent prompt &amp;lt;name&amp;gt; &amp;quot;&amp;lt;text&amp;gt;&amp;quot; --wait --timeout &amp;lt;ms&amp;gt;
herdr agent wait &amp;lt;name&amp;gt; --until idle|done|blocked|working --timeout &amp;lt;ms&amp;gt;
herdr agent read &amp;lt;name&amp;gt; --source recent|visible --lines &amp;lt;N&amp;gt;
herdr agent explain &amp;lt;name&amp;gt;

# Pane(Flutter 交互全靠这几条)
herdr pane run &amp;lt;pane_id&amp;gt; &amp;quot;&amp;lt;command&amp;gt;&amp;quot;
herdr pane send-keys &amp;lt;pane_id&amp;gt; r        # hot reload
herdr pane send-keys &amp;lt;pane_id&amp;gt; R        # hot restart
herdr pane wait-output &amp;lt;pane_id&amp;gt; --regex &amp;quot;&amp;lt;pattern&amp;gt;&amp;quot; --timeout &amp;lt;ms&amp;gt;
herdr pane read &amp;lt;pane_id&amp;gt; --lines &amp;lt;N&amp;gt;

# 集成 / 会话
herdr integration install &amp;lt;agent&amp;gt;
herdr integration status
herdr server stop
herdr update --handoff
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;官方文档:&lt;a href=&quot;https://herdr.dev/docs/&quot;&gt;https://herdr.dev/docs/&lt;/a&gt; · 仓库:&lt;a href=&quot;https://github.com/herdrdev/herdr&quot;&gt;https://github.com/herdrdev/herdr&lt;/a&gt;&lt;/p&gt;</content:encoded><category>AI</category><category>Agent</category><category>herdr</category><category>Flutter</category></item>
<item><title>用 pilot-shell 跑通第一个 /spec：Claude Code 规格驱动开发实操</title><link>https://cloudtides.cn/blog/pilot-shell-spec-workflow</link><guid isPermaLink="true">https://cloudtides.cn/blog/pilot-shell-spec-workflow</guid><pubDate>Mon, 22 Jun 2026 16:00:00 GMT</pubDate><description>从安装、项目规则初始化到 /spec 的 Plan、TDD 实现、浏览器 E2E 验证全流程，以一个 React Native 学习 App 的打卡功能为例。</description><content:encoded>&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 安装
curl -fsSL https://raw.githubusercontent.com/maxritter/pilot-shell/main/install.sh | bash
# 激活
pilot activate &amp;lt;你的-license-key&amp;gt;
# 使用
pilot update
pilot
codex/claude 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;下面这份从零到第一个 &lt;code&gt;/spec&lt;/code&gt; 跑通的完整流程,我用你正在做的那个 &lt;strong&gt;React Native 英语学习 app&lt;/strong&gt; 当真实示例,这样每一步都能对得上你的实际场景。&lt;/p&gt;
&lt;hr/&gt;
&lt;h2&gt;阶段一:前置准备(只做一次)&lt;/h2&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt;  &lt;strong&gt;装 Claude Code(native installer)&lt;/strong&gt; — 如果你之前用 &lt;code&gt;npm install -g&lt;/code&gt; 或 &lt;code&gt;brew&lt;/code&gt; 装过,先卸载干净,否则 pilot 会冲突。pilot 安装器检测不到 Claude Code 时会尝试帮你装,但建议自己先装好。&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt;  &lt;strong&gt;确认 Claude 订阅档位&lt;/strong&gt; — 单人开发选 Max 5x(中度)或 Max 20x(重度)。你深度用 Claude API + 要跑 TDD 全流程,实测会很吃 token,建议直接 20x。&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt;  &lt;strong&gt;终端(可选但推荐)&lt;/strong&gt; — cmux 的竖向标签页适合开多个 session 并行跑 spec;iTerm2 / Ghostty 也行。&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt;  &lt;strong&gt;Claude Chrome 扩展(推荐)&lt;/strong&gt; — 这个对你特别有用。你的 app 有动画 flashcard UI,&lt;code&gt;/spec&lt;/code&gt; 在 verify 阶段会用浏览器自动化逐条跑 E2E 场景,装了扩展才能真正验证 UI。&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt;  &lt;strong&gt;(可选)Codex 对抗式 review&lt;/strong&gt; — 需要 ChatGPT Plus($20/月)。它在 &lt;code&gt;/spec&lt;/code&gt; 规划和验证阶段给一个独立第二意见。你之前折腾过 Claude Code ↔ Codex 通信,这个你应该会喜欢,但默认是关的,可以先跳过。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h2&gt;阶段二:安装(只做一次,全局生效)&lt;/h2&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt;  运行安装脚本:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl -fsSL https://raw.githubusercontent.com/maxritter/pilot-shell/main/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt;  等 7 步安装器跑完。它会做:检查/装依赖(Node、Python 3.12+、uv、git、jq)→ 写入 &lt;code&gt;~/.claude/&lt;/code&gt; → 装开发工具(Semble 代码搜索、CodeGraph、RTK token 压缩、language servers、Chrome DevTools MCP、playwright-cli)→ 配置 bash/zsh/fish 的 &lt;code&gt;pilot&lt;/code&gt; 别名 → 装 VS Code 扩展。&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt;  装完后,重开一个终端窗口(让 &lt;code&gt;pilot&lt;/code&gt; 别名生效)。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;全局装一次就够,所有项目共用 &lt;code&gt;~/.pilot/&lt;/code&gt; 和 &lt;code&gt;~/.claude/&lt;/code&gt;,不用每个项目重装。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;h2&gt;阶段三:首次配置(每个项目一次)&lt;/h2&gt;
&lt;p&gt;以你的英语学习 app 为例:&lt;/p&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt;  进项目目录并启动:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cd ~/projects/english-learning-app
pilot                              # 首次会让你登录 Claude
pilot activate &amp;lt;你的-license-key&amp;gt;   # 从 polar.sh/max-ritter/portal 拿 key
&lt;/code&gt;&lt;/pre&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt;  &lt;strong&gt;跑 &lt;code&gt;/setup-rules&lt;/code&gt;&lt;/strong&gt; — 这一步很关键,别跳过:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; /setup-rules
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;它会用语义搜索 + 结构分析探索你的代码库,自动识别出你用的是 Expo、Zustand、SM-2 spaced repetition 这些技术栈和约定,生成模块化的项目规则(&lt;code&gt;{slug}-project.md&lt;/code&gt;)。它还会检测你现有的 &lt;code&gt;CLAUDE.md&lt;/code&gt; / &lt;code&gt;AGENTS.md&lt;/code&gt; 并迁移进去。&lt;strong&gt;之后每次 session 都自带这些上下文&lt;/strong&gt;,Claude 就不会忘记你的架构了。&lt;/p&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;/&gt;  &lt;strong&gt;(可选)启用 Codex review:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; /codex:setup
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;认证后去 Console 的 Settings → Reviewers 里打开。&lt;/p&gt;
&lt;hr/&gt;
&lt;h2&gt;阶段四:完整示例——用 &lt;code&gt;/spec&lt;/code&gt; 加一个功能&lt;/h2&gt;
&lt;p&gt;假设你要给学习 app 加一个&amp;quot;&lt;strong&gt;连续打卡 streak + 每日复习提醒&lt;/strong&gt;&amp;quot;功能。完整走一遍:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 先想清楚再写代码(可选,适合需求模糊时)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;如果只是模糊想法,先用 &lt;code&gt;/prd&lt;/code&gt; 头脑风暴:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; /prd &amp;quot;加一个学习连续打卡和每日复习提醒,提升用户留存&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;它会抛 3-5 个方向跟你对线,收敛成一份 PRD(存到 &lt;code&gt;docs/prd/&lt;/code&gt;),最后问你要不要直接交给 &lt;code&gt;/spec&lt;/code&gt;。需求已经清楚的话,跳过这步。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 切到 Opus 做规划&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; /model opus
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ 注意:有个 &lt;code&gt;spec-mode-guard&lt;/code&gt; 钩子会&lt;strong&gt;硬性拦截在非 Opus 模型上跑 &lt;code&gt;/spec&lt;/code&gt;&lt;/strong&gt;——规划必须用 Opus。这是故意的,规划质量直接决定后面所有产出。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;3. 启动 spec 工作流&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; /spec &amp;quot;添加学习连续打卡(streak):每日完成复习后 streak +1,断签清零,首页展示当前/历史最高 streak,并在每天固定时间推送复习提醒&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来它会自动走这个循环:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Discuss → Plan → Approve → Implement(TDD) → Verify → Done
                                    ↑              ↓
                                    └──── Loop ────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Plan 阶段:&lt;/strong&gt; 用语义搜索摸清你的 Zustand store 和 SM-2 逻辑 → 问你澄清问题(比如&amp;quot;streak 按本地时区还是 UTC 算?&amp;quot;&amp;quot;提醒时间用户可配置吗?&amp;quot;)→ 写出带 scope/任务/完成定义的详细 spec → 因为有 UI,会写&lt;strong&gt;可被浏览器执行的 E2E 测试场景&lt;/strong&gt; → 一个 spec-review 子 agent 检查完整性 → &lt;strong&gt;停下来等你批准&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;批准前你可以可视化批注:&lt;/strong&gt; 打开 Console(浏览器访问 &lt;code&gt;localhost:41777&lt;/code&gt;),在 Specifications 标签页默认是 Annotate 模式,选中任何一段文字或点 &lt;strong&gt;+&lt;/strong&gt; 就能写批注(&amp;quot;streak 断签判断要考虑跨夜的边界情况&amp;quot;)。agent 会在下一个检查点读你的批注、改 plan、再请你批准。批准对了再往下走,比在 PR 阶段才发现问题省事得多。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;4. 切到 Sonnet 实现&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;批准后切模型省钱:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; /model sonnet[1m]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Implement 阶段:&lt;/strong&gt; 自动建一个隔离的 git worktree → 每个任务严格 TDD(RED 写失败测试 → GREEN 实现 → REFACTOR)→ 质量钩子在每次编辑后自动跑 lint / format / 类型检查 → 每个任务后跑全套测试。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;5. Verify(自动)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;跑全套测试 + 实际执行程序 → 统一 review 子 agent 检查(合规 + 质量 + 目标达成)→ UI 功能逐条用浏览器自动化跑 E2E 场景(你那个 flashcard 动画就在这里被真实验证)→ 自动修复发现的问题 → 通过后 squash merge 回 main。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6. 最终 code review(可选但建议)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在 Console 的 Changes 标签页开 Review 模式,在 diff 行上点 &lt;strong&gt;+&lt;/strong&gt; 加行级批注,agent 读完逐条解决后才标记为 verified。等于在代码离开你机器前先过一遍 PR review。&lt;/p&gt;
&lt;hr/&gt;
&lt;h2&gt;日常使用速查&lt;/h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;场景&lt;/th&gt;&lt;th&gt;命令&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;小改动 / 探索&lt;/td&gt;&lt;td&gt;直接聊(Quick 模式,默认。TDD 和质量钩子照常生效)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;新功能 / 重构 / 架构改动&lt;/td&gt;&lt;td&gt;&lt;code&gt;/spec &amp;quot;...&amp;quot;&lt;/code&gt;(先 &lt;code&gt;/model opus&lt;/code&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;修单点 bug&lt;/td&gt;&lt;td&gt;&lt;code&gt;/fix &amp;quot;复习进度在保存重载之间丢失&amp;quot;&lt;/code&gt;(单文件根因 bug,自动 TDD)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;模糊想法变需求&lt;/td&gt;&lt;td&gt;&lt;code&gt;/prd &amp;quot;...&amp;quot;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;切模型&lt;/td&gt;&lt;td&gt;&lt;code&gt;/model opus&lt;/code&gt; 规划 → &lt;code&gt;/model sonnet[1m]&lt;/code&gt; 实现&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;看进度 / 改设置&lt;/td&gt;&lt;td&gt;浏览器开 &lt;code&gt;localhost:41777&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;code&gt;/fix&lt;/code&gt; 有个聪明的 bail-out:如果调查发现 bug 是多组件/架构性的,它会干净地停下来让你改用 &lt;code&gt;/spec&lt;/code&gt;,不会硬修。&lt;/p&gt;
&lt;hr/&gt;
&lt;h2&gt;维护(每隔几周)&lt;/h2&gt;
&lt;p&gt;跑久了 session 日志和缓存会拖慢速度,定期 reset 一次:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 1. Claude Code 内登出
/logout
# 2. 备份配置
mv ~/.claude.json ~/.claude.json.bak
mv ~/.claude ~/.claude.bak
mv ~/.pilot ~/.pilot.bak
# 3. 重装
curl -fsSL https://raw.githubusercontent.com/maxritter/pilot-shell/main/install.sh | bash
# 4. 重新登录激活
pilot
pilot activate &amp;lt;你的-license-key&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;跑顺了再删掉 &lt;code&gt;.bak&lt;/code&gt; 副本。&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;一个实操建议:&lt;strong&gt;第一次别上来就跑大功能。&lt;/strong&gt; 先在你的 app 上挑一个小而完整的功能(比如就那个 streak 计数)走完一遍 &lt;code&gt;/spec&lt;/code&gt; 全流程,把&amp;quot;它怎么问澄清问题、E2E 怎么验证、Console 批注怎么用&amp;quot;这套手感跑熟,再上复杂的。&lt;/p&gt;</content:encoded><category>AI</category><category>Claude Code</category><category>工作流</category></item>
</channel>
</rss>
