06 - CLI 與自動化基礎

05-Agent偵測與狀態機制 | 00-Index | 下一篇 → 07-Agent-Skill讓AI自己操控Herdr


心智模型:三種基本元件

元件職責
Layoutworkspace / tab / pane 拓撲)建立與組織「終端機的位置」
Pane控制一個原始終端:跑指令、送輸入、讀輸出、等輸出
Agent用名字或 pane 控制一個被認出來的 coding agent,含 lifecycle 狀態

選錯元件是自動化最常見的錯誤。 判斷準則:

  • 一般 shell、測試、server、CI watcher → pane 指令
  • 需要 Herdr 理解「這是哪個 agent」「它現在什麼狀態」 → agent 指令

鐵則一:ID 從 JSON 讀,不要猜

建立類指令都會印 JSON。永遠從回應裡抓 ID。

created=$(herdr workspace create --cwd ~/Taiwan_IM_board --label board --no-focus)
ws=$(printf '%s\n' "$created" | jq -r '.result.workspace.workspace_id')
pane=$(printf '%s\n' "$created" | jq -r '.result.root_pane.pane_id')
 
split=$(herdr pane split "$pane" --direction right --no-focus)
worker=$(printf '%s\n' "$split" | jq -r '.result.pane.pane_id')

回應路徑速查:

指令主要回傳路徑
workspace create.result.workspace / .result.tab / .result.root_pane
tab create.result.tab / .result.root_pane
pane split.result.pane
pane move.result.move_result.pane.pane_id(舊值在 .previous_pane_id
agent start / agent prompt / agent wait.result.agent
pane wait-output.result.pane_id / .result.matched_line / .result.read
pane read / agent read(socket API).result.read.text

鐵則二:明確指定目標

herdr pane split --current --direction right --cwd "$PWD" --no-focus   # ✅
herdr pane split --direction right                                      # ❌ 用 UI 目前 focus 的 pane

省略目標時 Herdr 會用 UI 目前 focus 的 pane——那可能是使用者剛切過去的、或另一個 client 的。多 agent 環境下這一定會出事。


建立版面

# workspace(順便有 tab + root pane)
herdr workspace create --cwd ~/project --label api --no-focus
 
# tab
herdr tab create --workspace w1 --label validate --no-focus
 
# 分割 pane
herdr pane split --current --direction right --cwd "$PWD" --no-focus
herdr pane split w1:p2 --direction down --ratio 0.4 --no-focus

分割方向的幾何規則:寬的往右切、窄或高的往下切,避免連續同方向切出無法使用的細長條。先看形狀:

herdr pane layout --pane "$HERDR_PANE_ID"

在 pane 裡跑普通指令

herdr pane run w1:p3 "python3 tools/validate.py"
herdr pane send-text w1:p3 "some literal text"    # 不送 Enter
herdr pane send-keys w1:p3 ctrl+c                 # 送按鍵
  • pane run 會把指令文字和 Enter 一起原子送出,而且尊重 pane 目前的 bracketed-paste 模式。跑指令一律用它。
  • send-text + send-keys enter 是低階做法,兩次操作分開,不保證原子性。

按鍵語法:普通字元 a、特殊鍵 enter tab esc backspace left right up down、和弦 ctrl+h alt+x shift+tab、功能鍵 f1、有名字的標點 minus plus backtickescapeesc 的別名;舊式 C-c 也接受。


讀輸出

herdr pane read w1:p3 --source recent-unwrapped --lines 120
herdr agent read writer-a --source recent-unwrapped --lines 200
herdr pane read w1:p3 --source visible --ansi

四種 source:

Source意思適合
visible目前渲染的畫面UI 回饋迴圈
recent近期輸出,含終端換行折行一般
recent-unwrapped近期輸出,折行接回去log、transcript 首選
detectionagent 偵測用的底部純文字快照除錯偵測

行為細節:

  • 預設輸出 UTF-8 純文字、剝掉 ANSI escape。要保留顏色用 --format ansi--ansidetection 永遠是純文字)。
  • recent 系列:--lines N 取「最後 N 個渲染終端列」;不給預設 80 列
  • visibledetection:不給 --lines 回傳整份快照;給了則保留最後 N 行。

⚠️ Alternate screen 的坑(Claude Code / OpenCode)

全螢幕型 agent(Claude Code、OpenCode)把 transcript 畫在終端的 alternate screen,不是 Herdr 的 host scrollback。

Herdr 有做了一件聰明事:對一個閒置、被認出來、而且停在 transcript 底部的 agent,recent / recent-unwrapped--lines 超過可見畫面時,會自動用 agent 的滑鼠捲動介面去收集重疊的頁面,讀完再把 viewport 放回底部。

但這條路徑有條件:

  • agent 正在 working / blocked / unknown 時,明確指定 --lines N 的 alternate-screen 歷史讀取會回 agent_not_idle。→ 等它 idle 再讀,或改用 --source visible
  • 使用者手動捲動過、有 direct attach、或那個程式不回報滑鼠滾輪時,Herdr 不會動 viewport。
  • visibledetection、ANSI 讀取、輸出等待與訂閱一律是被動的,不會移動 viewport。

官方給的 fallback,也是實務上最好用的一招

叫 agent 把完整回應寫成 Markdown 存到暫存目錄,只回傳檔案路徑,然後你直接讀那個檔案。

⚠️ 但這是 fallback,不要一開始就在 prompt 裡要求寫檔(會污染任務)。不過在詳解 / 筆記量產這種本來就要寫檔的任務裡,寫檔是任務本體,所以天然沒有這個問題——見 09-實戰-刷題網站Taiwan-IM-board


等待

等普通指令的輸出

herdr pane wait-output w1:p3 --match "OK" --timeout 120000
herdr pane wait-output w1:p3 --regex "passed|failed" --timeout 120000
  • 它會立刻搜尋目前的快照,所以已經存在的輸出也會命中
  • 預設 source 名稱是 recent,比對時當成「最新 80 個渲染列的 unwrapped 近期輸出」;--lines 可改。
  • --match單行內的字面子字串--regex 是 Rust 正規表示式,一次比對一行
  • 它完全不解讀 agent lifecycle。

等 agent 狀態

herdr agent wait writer-a --timeout 900000
herdr agent wait writer-a --until blocked --timeout 120000
herdr agent wait writer-a --until idle --until done --timeout 900000
  • 不給 --until 時,預設接受 idle / done / blocked
  • agent wait觀察目前狀態:如果現在就符合,立刻回傳
  • 要接受 unknown 必須明確寫 --until unknown

送 prompt 並等它

herdr agent prompt writer-a "把 §Diagnosis 依 2026 guideline 改寫" --wait --timeout 900000

規則(這段一定要看懂,不然會誤判):

  • agent prompt文字 + 編碼過的 Enter 原子送出,尊重終端目前的 bracketed-paste 模式。agent 正在工作時也可以送。
  • --wait立刻送出,然後:
    • 如果 agent 從非 working 狀態開始,Herdr 要求五秒內觀察到 lifecycle 變化。沒有的話回 agent_prompt_stalled,而不是無限等下去。(caller --timeout ≤ 5 秒時走一般的 timeout 錯誤。)
    • 觀察到活動之後,才等你要的 settled 狀態。
  • ⚠️ 它追的是 lifecycle 狀態,不是「這一輪對話」。如果 agent 本來就在工作,目前那一輪結束就可能滿足這個 wait
  • --until 只有在同時有 --wait 時才允許。

🔑 實務結論agent prompt --wait 回來 不等於「我要的任務做完了」。真正的驗收要靠:檢查它應該產生的檔案是否存在且正確,這比讀畫面可靠一百倍。

Timeout 與錯誤

  • pane wait-outputagent waitagent prompt --wait 不給 --timeout 就會無限等
  • Timeout 或 server 錯誤 → JSON 印到 stderrexit code 1
  • CLI 語法錯誤 → exit code 2

啟動 agent

# 1. 先要有一個停在 shell prompt 的 pane
split=$(herdr pane split --current --direction right --cwd "$PWD" --no-focus)
p=$(printf '%s\n' "$split" | jq -r '.result.pane.pane_id')
 
# 2. 才能啟動 agent
herdr agent start writer-a --kind codex --pane "$p"
 
# 3. 需要傳給 agent 本身的參數放在 -- 之後
herdr agent start reviewer --kind codex --pane "$p" -- --profile worker

⚠️ 不要在編排腳本裡寫死模型名。 官方文件的範例寫的是 -m gpt-5.4,但該模型已於 2026-08-31 從 ChatGPT 登入的 Codex 退場。 讓 ~/.codex/config.toml 的 profile 決定模型(見 Codex CLI 05),模型換代時腳本才不會跟著壞。

規則:

  • 「可用的 shell pane」= 停在互動 shell prompt:shell 自己擁有前景,沒有前景指令、編輯器或 agent 在跑。
  • agent start 只在 Herdr 偵測到預期的 agent 在同一個終端、而且準備好接受互動輸入之後才回傳
  • 預設啟動 timeout 30000 ms;明確給的值必須 > 3000 且 ≤ 300000
  • --kind 選的是 Herdr 認定的標準執行檔;-- 之後的參數原封不動傳給它。
  • 手動啟動的 agent 也會被自動偵測,用 pane ID 定址;想要穩定的名字就 agent rename

送按鍵給 agent 的互動 UI

herdr agent send-keys writer-a esc
herdr agent send-keys writer-a ctrl+c
herdr agent send-keys writer-a up enter

Herdr 會先驗證所有按鍵,才寫出任何 byte

pane 輸入 vs agent 輸入的差別

  • pane 輸入:不管目前誰佔用,直接對那個終端下手。
  • agent 輸入:先解析出存活的 agent;如果那個 agent 已經不再控制該 pane,操作會被拒絕

👉 編排時用 agent 輸入(比較安全);只有在你刻意要原始終端控制時才用 pane 輸入。


一段完整的最小編排範例

#!/usr/bin/env bash
set -euo pipefail
 
# 1. 開一個背景 pane
split=$(herdr pane split --current --direction right --cwd "$PWD" --no-focus)
p=$(printf '%s\n' "$split" | jq -r '.result.pane.pane_id')
 
# 2. 啟動 codex
herdr agent start reviewer --kind codex --pane "$p"
 
# 3. 丟任務並等它
herdr agent prompt reviewer \
  "審查目前的 git diff,只回報可執行的問題。" \
  --wait --timeout 600000
 
# 4. 讀結果
herdr agent read reviewer --source recent-unwrapped --lines 200

等一個 agent 來問你問題、看一下、然後回它:

herdr agent wait reviewer --until blocked --timeout 120000
herdr agent read reviewer --source recent-unwrapped --lines 80
herdr agent send-keys reviewer esc

跑一個普通 process 並等它的輸出(不當成 agent):

herdr pane run w1:p3 "npm run validate"
herdr pane wait-output w1:p3 --regex "通過|FAIL|Error" --timeout 300000
herdr pane read w1:p3 --source recent-unwrapped --lines 60

其他常用指令

# 狀態
herdr status
herdr status server
herdr status client
 
# session
herdr session list --json
 
# git worktree(見第 09、10 章的隔離工作流)
herdr worktree list --cwd .
herdr worktree create --cwd . --branch feat/詳解-114 --no-focus
herdr worktree remove --workspace w2 --force
 
# 通知(可以在腳本結束時叫自己)
herdr notification show "詳解量產完成" --body "114 心臟 24 題已產出" --sound done
 
# API schema(要寫工具時)
herdr api schema --json --output herdr-api.schema.json

🔗 相關筆記


最後更新:2026-08-04