summon-cc
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinesesummon-cc — conduct Claude Code sessions from any agent
summon-cc — 从任意 agent 管理 Claude Code 会话
Deprecated. summon-cc is archived and unmaintained. Its successor is conclave (https://github.com/anfreire/conclave), which spawns agents on Claude Code, Codex or OpenCode through one CLI, with no tmux pane to drive. Prefer it for new work; what follows still describes this skill as it stands.
You are the orchestrator. You spawn full interactive Claude Code sessions ("workers")
in detached tmux panes, hand each a scoped prompt, and supervise them through append-only
file signals. You orchestrate; workers do the work. Three laws make this cheap and calm:
- Never parse the terminal. The signals carry everything; the pane is a debug surface, read only when a signal says something is wrong.
- Never idle-poll. One waiter per worker blocks silently until something happens.
- Act on signals, verify by signals. Every move — spawn, prompt, answer — leaves a signal proving it landed. The scripts verify their own steps; you verify yours.
Any agent on any harness can be the orchestrator — every capability here is a plain shell
command. Only the worker side is Claude Code, and that's the point: this skill hands
you Claude Code sessions as a building block, unrestricted — what you build with them
(pipelines, review loops, fan-outs, watchdogs) is your call.
已弃用。 summon-cc 已归档且不再维护。它的后继项目是 conclave(https://github.com/anfreire/conclave),可通过一个 CLI 在 Claude Code、Codex 或 OpenCode 上启动 agent,无需驱动 tmux 窗格。新任务建议优先使用它; 以下内容仍按此 skill 当前的状态进行描述。
你是编排者。你会在分离的 tmux 窗格中启动完整的交互式 Claude Code 会话(“worker”),
向每个 worker 分配范围明确的 prompt,并通过仅追加的文件信号来监管它们。
你负责编排;worker 负责执行。让这一切保持低成本且平稳的三条法则是:
- 绝不解析终端。 信号承载全部信息;只有在信号表明出问题时,才把窗格当作调试界面读取。
- 绝不空闲轮询。 每个 worker 配一个 waiter,静默阻塞,直到有事件发生。
- 根据信号行动,根据信号验证。 每一步操作——启动、提示、回答——都会留下 信号,证明操作已生效。脚本会验证自身步骤;你也验证自己的步骤。
任意 harness 上的任意 agent 都可以充当编排者——这里的所有能力都只是普通 shell
命令。只有 worker 一侧是 Claude Code,而这正是重点:此 skill 将 Claude Code
会话作为一个不受限制的构建模块交给你——你用它们构建什么(流水线、审查循环、任务分发、
watchdog)由你决定。
Requirements
环境要求
- Linux or macOS; ,
bash,tmux, and ajqCLI withclaudeand--settings(any current one —--effortto confirm).claude --help - Nothing to install or wire. Spawn injects the six lifecycle hooks per worker via
on the launch line: no settings file of the user's is ever touched, and sessions you didn't summon never load these hooks at all.
--settings - The two scripts sit next to this SKILL.md in ; invoke them by absolute path (your working directory is usually elsewhere).
scripts/below means this skill's directory.<skill-dir>
- Linux 或 macOS;需要 、
bash、tmux,以及支持jq和--settings的--effortCLI(任意当前版本均可——运行claude确认)。claude --help - 无需安装或配置任何内容。 Spawn 会在启动命令中通过 为每个 worker 注入六个生命周期 hooks:绝不会触碰用户的任何 settings 文件,你也未 summon 的会话 绝不会加载这些 hooks。
--settings - 两个脚本位于此 SKILL.md 旁边的 中;必须通过绝对路径调用(你的工作目录 通常位于其他位置)。下文的
scripts/表示此 skill 所在目录。<skill-dir>
Quickstart — one worker, start to finish
快速入门——一个 worker,从启动到完成
brief="$(mktemp)" # a fresh file per worker
printf '%s\n' 'Fix the failing test in src/foo. END YOUR TURN with a one-paragraph report.' > "$brief"
bash <skill-dir>/scripts/spawn.sh fixer "$brief"brief="$(mktemp)" # a fresh file per worker
printf '%s\n' 'Fix the failing test in src/foo. END YOUR TURN with a one-paragraph report.' > "$brief"
bash <skill-dir>/scripts/spawn.sh fixer "$brief"→ SUMMONED id=fixer-6a53bd1e00002f40 state=/home/you/.local/state/summon/workers/fixer-6a53bd1e00002f40
→ SUMMONED id=fixer-6a53bd1e00002f40 state=/home/you/.local/state/summon/workers/fixer-6a53bd1e00002f40
bash <skill-dir>/scripts/wait.sh fixer-6a53bd1e00002f40 # silent until → EVENT=STOP + the report
tmux kill-session -t '=fixer-6a53bd1e00002f40' # done with it ('=' = exact match)
`SUMMONED` prints your two handles: the **id** (wait, send-keys, kill) and the **state
dir** (every signal file) — `<id>` and `<state>` below. Everything composes these two.bash <skill-dir>/scripts/wait.sh fixer-6a53bd1e00002f40 # silent until → EVENT=STOP + the report
tmux kill-session -t '=fixer-6a53bd1e00002f40' # done with it ('=' = exact match)
`SUMMONED` 会打印两个句柄:**id**(用于 wait、send-keys、kill)和**状态目录**
(包含所有信号文件)。下文所有操作都围绕这两项展开。Spawn
Spawn
Write the worker's prompt to a file (multi-line pastes as one block). It must not
begin with , , or — those are TUI input prefixes (slash command / bash mode /
memory), so the text would run as a command instead of submitting; spawn rejects such
files loudly. Then:
/!#bash <skill-dir>/scripts/spawn.sh <name> <prompt-file> [effort] [cwd] [permissions]- — handed to
effortverbatim: claude owns the vocabulary (claude --effortlists it);claude --help(the default) keeps the session's own setting. Fire-and-forget tuning: an unknown value doesn't abort — claude silently runs at its default, with no pane warning; the STOP line shows the effective effort (default) as claude reports it — the one place to confirm a value took.effort=… - — the worker's project dir (default: yours).
cwd - —
permissions(default →bypass: a detached pane can't answer permission prompts, so workers self-gate instead — see Worker prompts) ·--dangerously-skip-permissions(no flag: claude's own default in that cwd) · anything else is handed toinheritverbatim (--permission-modelists the modes). A bad mode fails boot with claude's own error.claude --help - — optional env var of extra flags appended verbatim to the launch line:
SUMMON_CLAUDE_ARGSfor cheap scouts,--model haikuto revive a dead worker's conversation in a fresh pane (its session_id is in--resume <session_id>).<state>/events.jsonl
Spawn is self-verifying end to end (boot, paste, submit — all against hook signals, with
its own retries). If it fails it says why, loudly, with the pane tail and the remedy;
the two first-run consent dialogs are the usual cause (Gotchas).
将 worker 的 prompt 写入一个文件(多行内容应作为一个整体粘贴)。文件内容不能
以 、 或 开头——这些是 TUI 输入前缀(斜杠命令 / bash 模式 /
memory),因此文本会被当作命令执行,而不是提交;spawn 会明确拒绝此类文件。然后运行:
/!#bash <skill-dir>/scripts/spawn.sh <name> <prompt-file> [effort] [cwd] [permissions]- ——原样传给
effort:具体词汇由 claude 决定 (claude --effort会列出);claude --help(默认值)会保留会话自身的设置。这是一种即发即弃式 调优:未知值不会中止运行——claude 会在默认级别静默运行,窗格中不会显示警告; STOP 行会按照 claude 的报告显示实际生效的 effort(default),这是确认参数是否 生效的唯一位置。effort=… - ——worker 的项目目录(默认使用你的当前目录)。
cwd - ——
permissions(默认 →bypass:分离的窗格 无法回答权限提示,因此 worker 应自行设限——参见“Worker prompt”);--dangerously-skip-permissions(不传 flag:使用 claude 在该 cwd 下的默认设置);其他值会原样传给inherit(可用模式由--permission-mode列出)。无效模式会导致启动失败, 并显示 claude 自身的错误。claude --help - ——可选环境变量,用于将额外 flag 原样追加到启动命令: 例如使用
SUMMON_CLAUDE_ARGS启用低成本 scout,或使用--model haiku在新窗格中恢复已死亡 worker 的对话(其 session_id 位于--resume <session_id>中)。<state>/events.jsonl
Spawn 会端到端地自我验证(启动、粘贴、提交——全部依据 hook 信号完成,并带有自己的
重试机制)。如果失败,它会明确说明原因,同时给出窗格尾部内容和修复方法;最常见的原因是
首次运行时的同意对话框(见“常见陷阱”)。
The loop
循环
spawn → wait → act on the event → wait again → … → downbash <skill-dir>/scripts/wait.sh <id> [max_secs] # default 1800Run it in the background if your harness supports that (it's silent until it exits);
otherwise run it blocking. It costs you nothing while the worker runs, and exits with one
self-contained block:
- — the worker ended its turn; its final message follows, captured by the Stop hook (the transcript is never parsed). Usually the report you asked for — but a worker sometimes ends a turn just to tell you something: read it, then poke or re-wait. A Stop line that can't be read is still
EVENT=STOP— the block says the message is unreadable and points at the transcript, so a Stop is never hidden behind an IDLE.EVENT=STOP - — paused on AskUserQuestion; the numbered options follow. Answer (next section), then wait again.
EVENT=QUESTION - — cap hit, nothing happened; heartbeat age + recent tool activity follow. Long silence is normal (thinking, subagents, long subprocesses) — re-arm the same command. Intervene only on real evidence of a stall.
EVENT=IDLE - — the worker is gone: pane killed, or claude exited leaving a bare shell behind (the block says which, with post-mortem pointers).
EVENT=DEAD
Re-arming is literally the same command again — the cursor (kept in the worker dir)
resumes from the last event reported.
spawn → wait → 根据事件行动 → 再次 wait → … → 结束bash <skill-dir>/scripts/wait.sh <id> [max_secs] # default 1800如果你的 harness 支持后台执行,请在后台运行它(它会静默等待直至退出);否则可以使用
阻塞方式。worker 运行期间它不会产生任何开销,退出时会返回一个自包含的区块:
- ——worker 已结束本轮;随后是由 Stop hook 捕获的最终消息 (绝不解析 transcript)。这通常就是你要求的报告——但 worker 有时结束一轮只是为了 向你传递信息:阅读消息,然后 poke 它或重新 wait。 即使无法读取 Stop 行,仍会返回
EVENT=STOP——区块会说明消息不可读并指向 transcript, 因此 Stop 绝不会隐藏在 IDLE 后面。EVENT=STOP - ——worker 暂停并等待 AskUserQuestion;随后会列出编号选项。 回答问题(见下一节),然后再次 wait。
EVENT=QUESTION - ——已达到等待上限,期间没有发生任何事件;随后会给出 heartbeat 时长 和近期工具活动。长时间静默很正常(思考、subagents、长时间运行的 subprocess); 重新执行同一条命令即可。仅在有确凿证据表明任务停滞时才介入。
EVENT=IDLE - ——worker 已消失:窗格被杀死,或 claude 退出后只留下一个空 shell (区块会说明具体情况,并提供事后排查线索)。
EVENT=DEAD
重新等待只需再次执行完全相同的命令——保存在 worker 目录中的 cursor 会从上次报告的
事件处继续。
Answer a question
回答问题
Options are numbered in order; the pane appends its own entries ("Type
something", "Chat about this") after the real options, so your numbers are the real ones.
question.json- Single question — a digit selects and submits:
tmux send-keys -t <id> 2 - Several questions — digits auto-advance; after the last, Enter submits the review step:
tmux send-keys -t <id> 1; sleep 1; tmux send-keys -t <id> 3; sleep 1; tmux send-keys -t <id> Enter - — digits toggle, Enter submits.
[multi-select] - Free text exists (the "Type something" option) but is fiddly — prefer a listed option.
- Verify, don't assume. The landed answer appends a line (with the chosen labels) to
QuestionAnswered, and the next wait moves past the question. A single-question digit commits instantly — there is no review screen — so when the choice matters, read the QuestionAnswered line. Multi-question flows end on an Enter-gated review screen: before submitting a destructive choice, peek it:<state>/events.jsonltmux capture-pane -t <id> -p | tail -30
选项按照 中的顺序编号;窗格会在真实选项之后追加自己的条目
(“Type something”“Chat about this”),因此前面的数字才是真实选项的编号。
question.json- 单个问题——输入数字即可选择并提交:
tmux send-keys -t <id> 2 - 多个问题——数字会自动前进;完成最后一个后,按 Enter 提交审查步骤:
tmux send-keys -t <id> 1; sleep 1; tmux send-keys -t <id> 3; sleep 1; tmux send-keys -t <id> Enter - ——数字用于切换选择,按 Enter 提交。
[multi-select] - 存在自由文本选项(“Type something”),但操作较麻烦——优先选择列出的选项。
- 验证,不要假设。 已提交的答案会向 追加一条 QuestionAnswered 行(其中包含所选标签),下一次 wait 会越过该问题。 单问题模式输入数字后会立即提交——没有审查界面——因此,当选项很重要时, 请读取 QuestionAnswered 行。多问题流程会进入一个需按 Enter 确认的审查界面: 提交破坏性选择前,先查看界面:
<state>/events.jsonltmux capture-pane -t <id> -p | tail -30
Poke a running worker
Poke 正在运行的 worker
Only when no question is pending ( absent). While a question
modal is up, typed text is captured by the modal and a trailing Enter selects whatever is
highlighted — it answers the question by accident instead of queueing your message.
Answer the question first, or interrupt to cancel it.
<state>/question.json- Queue a follow-up (read at the worker's next turn boundary; = literal, so a one-word message like
-lisn't sent as the tmux key of that name):Uptmux send-keys -t <id> -l 'your message' && tmux send-keys -t <id> Enter - Long or quote-heavy follow-up — paste it the way spawn does (per-worker buffer name,
so parallel pokes can't cross):
tmux load-buffer -b poke-<id> <file> && tmux paste-buffer -t <id> -b poke-<id> -p -d && sleep 1 && tmux send-keys -t <id> Enter - Never start a poke with ,
/, or!— TUI input prefixes, typed or pasted alike:#runs a slash command (/kills the worker),/exitexecutes the rest as a shell command in the worker's cwd,!opens the memory picker; none submits a prompt (no UserPromptSubmit lands). Need a literal one? Lead with something else ("Note: /foo …").# - Interrupt generation to redirect: , then poke. Escape is the TUI's own interrupt key and a no-op on an idle worker — but send it once: a rapid double Escape opens the Rewind menu, a modal the signals can't see that can restore code and conversation. If a poke seems swallowed (no UserPromptSubmit line lands), resend Enter first — a lagged paste leaves your text sitting in the input box, and one more Enter just submits it (re-pasting first would stack a second copy). Still nothing? A modal is up: one Escape dismisses it, then re-poke. Never C-c — a quick double C-c exits claude entirely, and a misjudged stall is exactly when you'd hit an idle one. An interrupted turn emits no Stop — the next completed turn (your poke's) does. Interrupting cancels a pending question; the orphaned question.json is archived automatically the moment your next message submits.
tmux send-keys -t <id> Escape - A fresh spawn is a blank context — to continue a worker, poke its pane; don't respawn.
仅在没有待处理问题时操作( 不存在)。当问题模态框打开时,
输入的文本会被模态框捕获,末尾的 Enter 会选择当前高亮项——这会意外地回答问题,
而不是将消息加入队列。请先回答问题,或者中断并取消它。
<state>/question.json- 排入一条后续消息(会在 worker 下一轮开始时读取;表示按字面文本发送, 因此像
-l这样的单词不会被当作同名 tmux 按键发送):Uptmux send-keys -t <id> -l 'your message' && tmux send-keys -t <id> Enter - 较长或包含大量引号的后续消息——按 spawn 的方式粘贴(使用每个 worker 独立的
buffer 名称,因此并行 poke 之间不会串扰):
tmux load-buffer -b poke-<id> <file> && tmux paste-buffer -t <id> -b poke-<id> -p -d && sleep 1 && tmux send-keys -t <id> Enter - 绝不要让 poke 以 、
/或!开头——无论手动输入还是粘贴,这些 TUI 输入 前缀都会生效:#会运行斜杠命令(/会终止 worker),/exit会在 worker 的 cwd 中将其余内容作为 shell 命令执行,!会打开 memory picker;它们都不会提交 prompt (不会产生 UserPromptSubmit)。如果必须使用字面量字符,请以其他内容开头,例如 “Note: /foo …”。# - 中断生成以改变方向:,然后再 poke。Escape 是 TUI 自身的 interrupt 按键,对空闲 worker 无效——但只能发送一次:快速连按两次 Escape 会打开 Rewind 菜单,这是一个信号无法看到的模态界面,并且可能恢复代码和对话。 如果 poke 似乎被吞掉(没有产生 UserPromptSubmit 行),请先再次发送 Enter——粘贴延迟会让 文本留在输入框中,再按一次 Enter 只会提交该文本(如果先重新粘贴,反而会叠加第二份副本)。 仍然没有反应?说明模态界面已打开:按一次 Escape 将其关闭,然后再 poke。 绝不要按 C-c——快速连按两次会直接退出 claude,而误判任务停滞时恰恰最容易这样做。 被中断的一轮不会发出 Stop——下一轮完成的任务(也就是你的 poke)才会发出。 中断会取消待处理问题;孤立无主的 question.json 会在下一条消息提交时自动归档。
tmux send-keys -t <id> Escape - 新 spawn 的上下文是空白的——若要继续现有 worker,请 poke 它的窗格,不要重新 spawn。
Results, status, debugging
结果、状态与调试
- Final message — printed by wait on STOP; re-read any time:
grep '"event":"Stop"' <state>/events.jsonl | tail -1 | jq -r .last_assistant_message - Answers given —
grep '"event":"QuestionAnswered"' <state>/events.jsonl | tail -1 | jq -c .answers - All workers —
— cross-check liveness with
find "${SUMMON_STATE:-$HOME/.local/state/summon}/workers" -name meta.json -exec jq -r '"\(.id) \(.name) \(.cwd)"' {} + 2>/dev/null, pending questions withtmux ls.find "${SUMMON_STATE:-$HOME/.local/state/summon}/workers" -name question.json 2>/dev/null - Instant probe — returns at once: any unconsumed event, else an IDLE snapshot (heartbeat age + recent activity). Same single-shot, cursor-safe semantics as any wait — probing costs nothing.
bash <skill-dir>/scripts/wait.sh <id> 0 - Peek (debug only — every pane capture costs orchestrator context; the signals above
are the normal path):
tmux capture-pane -t <id> -p | tail -40
- 最终消息——wait 在 STOP 时打印;可随时重新读取:
grep '"event":"Stop"' <state>/events.jsonl | tail -1 | jq -r .last_assistant_message - 已提交的答案——读取方式如下:
grep '"event":"QuestionAnswered"' <state>/events.jsonl | tail -1 | jq -c .answers - 所有 worker——
— 使用
find "${SUMMON_STATE:-$HOME/.local/state/summon}/workers" -name meta.json -exec jq -r '"\(.id) \(.name) \(.cwd)"' {} + 2>/dev/null交叉检查存活状态,使用以下命令查找待处理问题:tmux ls。find "${SUMMON_STATE:-$HOME/.local/state/summon}/workers" -name question.json 2>/dev/null - 即时探测——会立即返回:任何尚未消费的 事件,或一份 IDLE 快照(heartbeat 时长与近期活动)。它与普通 wait 具有相同的单次执行、 cursor 安全语义——探测不产生任何额外开销。
bash <skill-dir>/scripts/wait.sh <id> 0 - 查看窗格(仅用于调试——每次捕获窗格都会消耗编排者上下文;正常使用上述信号即可):
tmux capture-pane -t <id> -p | tail -40
Teardown
清理
When you've consumed a worker's result: — keep the :
it pins the exact name, where a bare falls back to prefix matching once the exact
session is gone (Gotchas). The Claude transcript persists on disk regardless. Remove
the state dir when you're done with its evidence: . Ids are never
reused — a stale dir can't collide.
tmux kill-session -t '=<id>'=-trm -rf <state>消费完 worker 的结果后:——务必保留 :
它会锁定精确会话名,而裸用 在精确会话消失后会退化为前缀匹配(见“常见陷阱”)。
无论是否执行此操作,Claude transcript 都会保留在磁盘上。处理完相关证据后,删除状态目录:
。Id 绝不会被复用——陈旧目录不会发生冲突。
tmux kill-session -t '=<id>'=-trm -rf <state>Worker prompts — what makes orchestration work
Worker prompt——让编排真正奏效的关键
- Self-contained and scoped. The worker shares none of your context: state the goal, the exact scope, what's out of scope, and "read X and Y yourself". One job per worker.
- Tell it how to finish: "END YOUR TURN with a report of A/B/C." Stop is your completion signal, so finishing the turn — with the facts you need in the final message — is part of the worker's job. The final message is all you should ever need.
- Self-gate risky actions (essential under ): instruct the worker to pause and ask via AskUserQuestion before anything irreversible, destructive, or out-of-scope — that routes the decision to you through the QUESTION event, with numbered options you answer by digit.
bypass - Independent review catches more: for pipelines, have one worker do the step and a fresh worker review it; repeat until a reviewer that changed nothing approves.
- 自包含且范围明确。 worker 不共享你的任何上下文:说明目标、确切范围、 范围外的事项,并要求它“自行读取 X 和 Y”。每个 worker 只负责一项工作。
- 告诉它如何完成。 例如:“END YOUR TURN with a report of A/B/C.” Stop 是你的 完成信号,因此结束本轮——并在最终消息中给出所需事实——本身就是 worker 工作的一部分。 最终消息应该就是你所需的全部信息。
- 让 worker 自行设限高风险操作(在 下至关重要):指示 worker 在执行任何 不可逆、破坏性或超出范围的操作之前暂停,并通过 AskUserQuestion 提问——这会通过 QUESTION 事件将决策路由给你,并提供可由数字回答的编号选项。
bypass - 独立审查能发现更多问题:对于流水线,让一个 worker 执行步骤,再由一个全新的 worker 审查;重复此过程,直到一位没有做出任何修改的审查者批准为止。
Decision routing
决策路由
When a worker asks: answer it yourself if the task's constraints already determine the
answer, or the choice is recoverable — keep the work moving. Escalate to the human only
what is genuinely new, material, or irreversible. When unsure whether something is
reversible, treat it as escalate-worthy. Agree this boundary with the human up front.
当 worker 提问时:如果任务约束已经确定了答案,或者该选择可以恢复,就由你自行回答,
让工作继续推进。只有真正新颖、重大或不可逆的事项才升级给人类。如果无法确定某个选择
是否可逆,就将其视为需要升级的事项。请事先与人类约定好这条边界。
One-by-one vs batch
逐个执行与批处理
- One-by-one: spawn → wait to completion → act → down → next. Each result gates the next step; trivial bookkeeping. Default for dependent steps.
- Batch: spawn N workers (distinct names, a brief file per worker; keep each
printed id and state), arm one waiter per worker, handle whichever exits first,
re-arm just that one. Every signal
file is per-worker — concurrent workers never cross wires. No background execution on
your harness? Round-robin with short caps — — waits are single-shot and cursor-safe, so taking turns loses nothing. Cap concurrency to what the machine and your attention can handle — a fleet booting at once can push claude's start past spawn's 45s boot window.
wait.sh w1 60; wait.sh w2 60; …
- 逐个执行:spawn → 等待完成 → 采取行动 → 结束 → 下一个。每一步结果都会决定 下一步是否执行;这适用于简单 bookkeeping。存在依赖关系时默认采用此方式。
- 批处理:spawn N 个 worker(使用不同名称和各自的 brief 文件;保留每个 worker 打印出的
id 与状态),为每个 worker 启动一个 waiter,先处理最先退出的那个,然后只重新等待
该 worker。每个信号文件都独立属于对应 worker——并发 worker 之间绝不会串扰。
如果你的 harness 不支持后台执行?使用短时限进行轮询——
——wait 是单次执行且 cursor 安全的,因此轮流等待 不会丢失任何信息。将并发数量限制在机器和你的注意力所能承受的范围内——同时启动大量 worker 可能会使 claude 的启动时间超过 spawn 的 45 秒启动窗口。
wait.sh w1 60; wait.sh w2 60; …
The state dir — reference
状态目录——参考
One per worker: (spawn prints it).
${SUMMON_STATE:-~/.local/state/summon}/workers/<id>/| file | meaning |
|---|---|
| what was spawned: name, cwd, prompt file, effort, permissions, extra args |
| what |
| lifecycle: SessionStart, UserPromptSubmit, AskUserQuestion, QuestionAnswered, Stop (carries the final message, permission mode, effort), SessionEnd — one line per event, whole or absent |
| the pending AskUserQuestion; presence == pending (answered/stale copies are archived beside it) |
| epoch of last tool call / one attributed line per tool call |
| exit sentinel: the pane's shell drops it the moment claude exits, for any reason (spawn fails fast on it during startup; wait reads it as DEAD when claude dies without a SessionEnd — hard kill, OOM) |
| waiter bookkeeping — automatic, don't touch |
每个 worker 拥有一个目录:
(由 spawn 打印)。
${SUMMON_STATE:-~/.local/state/summon}/workers/<id>/| 文件 | 含义 |
|---|---|
| 启动内容:名称、cwd、prompt 文件、effort、permissions 和额外参数 |
| 通过 |
| 生命周期事件:SessionStart、UserPromptSubmit、AskUserQuestion、QuestionAnswered、Stop(携带最终消息、permission mode 和 effort)、SessionEnd——每个事件占一行,可能完整存在或完全缺失 |
| 待处理的 AskUserQuestion;文件存在即表示有待处理问题(已回答或陈旧的副本会归档在同目录旁) |
| 最近一次工具调用的 epoch 时间 / 每次工具调用对应一行带归属信息的记录 |
| 退出哨兵:无论 claude 因何原因退出,窗格中的 shell 都会立即将其删除(spawn 在启动期间快速失败时也会触发;claude 未产生 SessionEnd 就死亡时——硬杀、OOM——wait 会将其读取为 DEAD) |
| waiter 的内部记录——自动维护,不要修改 |
Gotchas
常见陷阱
- Stop is completion; SessionEnd is bookkeeping — SessionEnd may or may not fire
for any given death: a clean writes one,
/exitsometimes does too, a hard kill never. Only its presence means anything — never wait on it, never read anything into its absence. wait.sh encodes all of this: claude gone with the pane's shell still alive reads DEAD — via SessionEnd when it fired, via thetmux kill-sessionsentinel when it didn't (SIGKILL, OOM) — never eternal IDLE.claude_exited - First-run dialogs stall boot (spawn fails loud at the SessionStart step): a machine
that has never accepted , or a cwd Claude doesn't trust yet, shows an interactive dialog first. Remedy: run
--dangerously-skip-permissionsonce in that dir interactively, accept, respawn. Don't script past these — they're consent gates.claude - Non-bypass workers can stall on a permission dialog — permission and plan-approval prompts are not AskUserQuestion: no hook fires, no signal appears, the heartbeat just goes stale. On a non-bypass worker, IDLE + old heartbeat + no pending question → peek the pane; answer the dialog with send-keys, or respawn under bypass.
- Vocabularies live in claude, not here. Effort levels and permission modes are
passed through verbatim and never validated — is the source of truth.
claude --help - No ghost text in a worker's input box. The TUI's suggested next prompt — dim text
after each turn, which a pane capture prints as if typed — is switched off in every
worker's injected settings (), so text sitting in a worker's input box is real: a lagged paste or a queued poke.
promptSuggestionEnabled: false - tmux prefix-matches once no exact name survives — a dead worker's id could then resolve to a similarly named session. Defense in depth: ids are fixed-width, spawn rejects names that embed an id-shaped run (so one id can never nest another — don't pass an old worker's id as a new worker's name), and the teardown one-liner pins with
-t. Pane-target commands take the same pinning as=if you want it for send-keys/paste/capture.-t '=<id>:' - Hooks gate on and are injected per worker — no session you didn't summon loads or writes anything. Every event line twice? A second copy of the hooks is registered in a settings file claude loads for that cwd — find it with
$SUMMON_IDand remove it; the signals tolerate the duplicates meanwhile.grep -l on_session_start.sh <cwd>/.claude/settings*.json ~/.claude/settings.json - Workers get the tmux server's environment, not yours — the server keeps the
environment of whoever started it, so a variable exported before spawn never reaches
the pane. Per-worker knobs go on the launch line () or into the injected settings, which is how spawn passes its own.
SUMMON_CLAUDE_ARGS - Don't extend the scripts with GNU-isms (,
date -d,sed -i,flock) — epoch-seconds arithmetic and plain POSIX tools are what keep macOS supported.timeout
- Stop 表示完成;SessionEnd 仅用于记录——对于任意一种死亡方式,SessionEnd 可能触发,
也可能不触发:正常执行 会写入一条,
/exit有时也会写入, 硬杀则永远不会。只有它是否出现才有意义——绝不要等待它,也不要因它缺失而推断 任何情况。wait.sh 对此进行了完整编码:当 claude 已消失但窗格中的 shell 仍存活时, 会读取为 DEAD——若 SessionEnd 已触发则使用它,否则使用tmux kill-session哨兵 (SIGKILL、OOM)——绝不会永远停留在 IDLE。claude_exited - 首次运行对话框会阻塞启动(spawn 会在 SessionStart 步骤明确报错):如果机器尚未接受过
,或者 Claude 尚未信任某个 cwd,就会先显示交互式对话框。 修复方法:在该目录中交互式运行一次
--dangerously-skip-permissions,接受相关设置,然后重新 spawn。 不要用脚本绕过这些对话框——它们是同意机制。claude - 非 bypass worker 可能会卡在权限对话框上——权限和计划审批提示不是 AskUserQuestion:不会触发 hook,也不会产生信号,只会显示 heartbeat 逐渐过期。 对于非 bypass worker,如果出现 IDLE + heartbeat 过旧 + 没有待处理问题,请查看窗格: 使用 send-keys 回答对话框,或在 bypass 模式下重新 spawn。
- 词汇表由 claude 定义,而不是这里。 Effort 级别和 permission mode 会被原样透传,
此处绝不校验——是唯一真源。
claude --help - worker 输入框中不会出现幽灵文本。 TUI 在每轮结束后显示的下一条建议 prompt
(窗格捕获时看起来像已经输入的暗色文本)会通过每个 worker 注入的 settings 被关闭
(),因此输入框中的内容都是真实内容:可能是粘贴延迟, 也可能是排队的 poke。
promptSuggestionEnabled: false - 当精确名称消失后,tmux 会进行前缀匹配——已死亡 worker 的 id 可能会解析到 名称相似的其他会话。纵深防御措施:id 采用固定宽度,spawn 会拒绝名称中嵌入 id 形状 字符串的请求(因此一个 id 绝不可能嵌套另一个 id——不要把旧 worker 的 id 作为新 worker 的 name),并且清理命令会使用
-t锁定精确目标。如果希望 send-keys、 paste 或 capture 也使用这种锁定方式,可将 pane 目标写为=。-t '=<id>:' - Hooks 根据 进行门控,并按 worker 注入——任何未由你 summon 的会话 都不会加载或写入任何内容。每个事件行出现两次?说明第二份 hooks 被注册在 claude 为 该 cwd 加载的 settings 文件中——使用以下命令找到并移除它:
$SUMMON_ID在此期间,信号机制可以容忍重复项。grep -l on_session_start.sh <cwd>/.claude/settings*.json ~/.claude/settings.json - worker 会获得 tmux server 的环境,而不是你的环境——server 会保留最初启动它的
进程的 environment,因此在 spawn 前导出的变量绝不会到达窗格中。每个 worker 的调整项
应放在启动命令上()或注入的 settings 中,spawn 正是通过这种方式 传递自身的配置。
SUMMON_CLAUDE_ARGS - 不要用 GNU 专属语法扩展脚本(、
date -d、sed -i、flock)—— epoch 秒算术和普通 POSIX 工具才是保持 macOS 兼容的关键。timeout