driving-claude-code-sessions
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDriving Claude Code Sessions
管控Claude Code会话
Overview
概述
You can launch other Claude Code sessions as "workers" in tmux, send them prompts, monitor their progress through lifecycle events, read their output, and hand them off to a human operator. This gives you the ability to delegate work, run tasks in parallel, or set up supervised workflows.
Workers are full interactive Claude Code sessions launched with so they never block on interactive permission prompts. A plugin (claude-session-driver) injects hooks that emit lifecycle events to a JSONL file, which you poll to track worker state. A PreToolUse hook gives the controller a window to inspect and approve or deny every tool call before it executes. The scripts handle all the plumbing: tmux management, session IDs, event files, and cleanup.
--dangerously-skip-permissions你可以在tmux中启动其他Claude Code会话作为“worker”,向其发送提示词,通过生命周期事件监控进度,读取输出内容,还可以将其交接给人工操作员。这让你能够委派工作、并行运行任务或搭建受监督的工作流。
Worker是通过参数启动的完整交互式Claude Code会话,因此它们不会因交互式权限提示而停滞。一个名为claude-session-driver的插件会注入钩子,将生命周期事件输出到JSONL文件,你可以轮询该文件来跟踪worker状态。PreToolUse钩子让管控者能够在工具执行前检查并批准或拒绝每一次工具调用。相关脚本会处理所有底层工作:tmux管理、会话ID、事件文件以及清理工作。
--dangerously-skip-permissionsPrerequisites
前提条件
These must be available on the system:
- tmux - for running worker sessions in detached terminals
- jq - for parsing JSON output from scripts and event files
- claude CLI - the Claude Code binary
系统中必须安装以下工具:
- tmux - 用于在后台终端运行worker会话
- jq - 用于解析脚本和事件文件的JSON输出
- claude CLI - Claude Code的二进制执行文件
Setup
配置步骤
All scripts live at relative to this skill's base directory. Set a convenience variable:
../../scripts/bash
SCRIPTS="<this-skill's-base-directory>/plugin/scripts"所有脚本位于此技能基础目录的路径下。可以设置一个便捷变量:
../../scripts/bash
SCRIPTS="<this-skill's-base-directory>/plugin/scripts"Workflow
工作流
1. Launch a Worker
1. 启动Worker
bash
RESULT=$("$SCRIPTS/launch-worker.sh" my-worker /path/to/project)
SESSION_ID=$(echo "$RESULT" | jq -r '.session_id')
TMUX_NAME=$(echo "$RESULT" | jq -r '.tmux_name')The script:
- Creates a detached tmux session named
my-worker - Starts with the session-driver plugin loaded and
claude--dangerously-skip-permissions - Waits for the event (up to 30s)
session_start - Returns JSON with ,
session_id, andtmux_nameevents_file
Permission bypass is automatic. The worker's PreToolUse hook provides controller-based gating (see Tool Approval below), so the built-in interactive permission dialog is redundant.
Pass extra CLI arguments after the working directory:
claudebash
undefinedbash
RESULT=$("$SCRIPTS/launch-worker.sh" my-worker /path/to/project)
SESSION_ID=$(echo "$RESULT" | jq -r '.session_id')
TMUX_NAME=$(echo "$RESULT" | jq -r '.tmux_name')该脚本会:
- 创建名为的后台tmux会话
my-worker - 加载session-driver插件并通过参数启动
--dangerously-skip-permissionsclaude - 等待事件(最多等待30秒)
session_start - 返回包含、
session_id和tmux_name的JSON数据events_file
权限绕过是自动的。Worker的PreToolUse钩子提供了基于管控者的权限控制(见下文的工具批准),因此内置的交互式权限对话框是多余的。
可以在工作目录后添加额外的 CLI参数:
claudebash
undefinedUse a specific model
使用指定模型
RESULT=$("$SCRIPTS/launch-worker.sh" my-worker /path/to/project --model sonnet)
undefinedRESULT=$("$SCRIPTS/launch-worker.sh" my-worker /path/to/project --model sonnet)
undefined2. Converse (Preferred)
2. 对话交互(推荐方式)
For most interactions, use — it sends the prompt, waits for the worker to finish, and returns the assistant's response in one call:
converse.shbash
RESPONSE=$("$SCRIPTS/converse.sh" my-worker "$SESSION_ID" "Refactor the auth module to use JWT tokens" 300)
echo "$RESPONSE"It handles tracking automatically, so multi-turn conversations just work:
--after-linebash
R1=$("$SCRIPTS/converse.sh" my-worker "$SESSION_ID" "Write tests for the auth module" 300)
R2=$("$SCRIPTS/converse.sh" my-worker "$SESSION_ID" "Now add edge case tests for expired tokens" 300)对于大多数交互场景,使用——它会发送提示词,等待worker完成任务,并一次性返回助手的响应:
converse.shbash
RESPONSE=$("$SCRIPTS/converse.sh" my-worker "$SESSION_ID" "重构认证模块以使用JWT令牌" 300)
echo "$RESPONSE"它会自动处理跟踪,因此多轮对话可以直接生效:
--after-linebash
R1=$("$SCRIPTS/converse.sh" my-worker "$SESSION_ID" "为认证模块编写测试用例" 300)
R2=$("$SCRIPTS/converse.sh" my-worker "$SESSION_ID" "现在添加针对过期令牌的边缘情况测试" 300)3. Send a Prompt (Low-Level)
3. 发送提示词(底层方式)
For finer control, use the individual scripts. sends text without waiting:
send-prompt.shbash
"$SCRIPTS/send-prompt.sh" my-worker "Refactor the auth module to use JWT tokens"如需更精细的控制,可以使用独立脚本。会发送文本但不等待结果:
send-prompt.shbash
"$SCRIPTS/send-prompt.sh" my-worker "重构认证模块以使用JWT令牌"4. Wait for the Worker to Finish
4. 等待Worker完成任务
bash
"$SCRIPTS/wait-for-event.sh" "$SESSION_ID" stop 300This blocks until the worker emits a event (meaning it finished processing and is waiting for input) or the timeout (in seconds) expires. Exit code 0 means the event arrived; exit code 1 means timeout.
stopThe matching event JSON line is printed to stdout:
json
{"ts":"2025-01-15T10:30:00Z","event":"stop"}bash
"$SCRIPTS/wait-for-event.sh" "$SESSION_ID" stop 300该命令会阻塞直到worker发出事件(表示已完成处理并等待输入)或超时(以秒为单位)。退出码0表示事件已触发;退出码1表示超时。
stop匹配的事件JSON行会输出到标准输出:
json
{"ts":"2025-01-15T10:30:00Z","event":"stop"}5. Read Worker Events
5. 读取Worker事件
bash
undefinedbash
undefinedAll events
读取所有事件
"$SCRIPTS/read-events.sh" "$SESSION_ID"
"$SCRIPTS/read-events.sh" "$SESSION_ID"
Last 3 events
读取最后3个事件
"$SCRIPTS/read-events.sh" "$SESSION_ID" --last 3
"$SCRIPTS/read-events.sh" "$SESSION_ID" --last 3
Only stop events
仅读取stop事件
"$SCRIPTS/read-events.sh" "$SESSION_ID" --type stop
"$SCRIPTS/read-events.sh" "$SESSION_ID" --type stop
Follow in real-time (blocks -- run in background Bash job for monitoring)
实时跟踪(会阻塞——在后台Bash作业中运行以进行监控)
"$SCRIPTS/read-events.sh" "$SESSION_ID" --follow &
MONITOR_PID=$!
"$SCRIPTS/read-events.sh" "$SESSION_ID" --follow &
MONITOR_PID=$!
... do other work ... then stop monitoring:
... 执行其他工作 ... 然后停止监控:
kill $MONITOR_PID 2>/dev/null
Event types emitted by the plugin:
| Event | Meaning | Extra fields |
|-------|---------|-------------|
| `session_start` | Worker session initialized | `cwd` |
| `user_prompt_submit` | A prompt was submitted to the worker | |
| `pre_tool_use` | Worker is about to call a tool | `tool`, `tool_input` |
| `stop` | Worker finished processing, waiting for input | |
| `session_end` | Worker session terminated | |kill $MONITOR_PID 2>/dev/null
插件输出的事件类型:
| 事件 | 含义 | 额外字段 |
|-------|---------|-------------|
| `session_start` | Worker会话已初始化 | `cwd` |
| `user_prompt_submit` | 已向worker提交提示词 | |
| `pre_tool_use` | Worker即将调用工具 | `tool`, `tool_input` |
| `stop` | Worker完成处理,等待输入 | |
| `session_end` | Worker会话已终止 | |6. Read Worker Conversation Log
6. 读取Worker对话日志
The worker's full conversation (prompts and responses) is stored in Claude's JSONL session log. The path uses an encoded form of the working directory where becomes with a leading :
/--- encodes to
/Users/jesse/myproject-Users-jesse-myproject
The log is at:
~/.claude/projects/<encoded-path>/<session-id>.jsonlTo read the last assistant response:
bash
ENCODED_PATH=$(echo "/path/to/project" | sed 's|/|-|g')
LOG_FILE=~/.claude/projects/${ENCODED_PATH}/${SESSION_ID}.jsonl
grep '"type":"assistant"' "$LOG_FILE" | tail -1 | jq -r '.message.content[] | select(.type=="text") | .text'Worker的完整对话记录(提示词和响应)存储在Claude的JSONL会话日志中。日志路径使用工作目录的编码形式,其中会被替换为并添加前缀:
/--- 会编码为
/Users/jesse/myproject-Users-jesse-myproject
日志路径为:
~/.claude/projects/<encoded-path>/<session-id>.jsonl读取最后一条助手响应:
bash
ENCODED_PATH=$(echo "/path/to/project" | sed 's|/|-|g')
LOG_FILE=~/.claude/projects/${ENCODED_PATH}/${SESSION_ID}.jsonl
grep '"type":"assistant"' "$LOG_FILE" | tail -1 | jq -r '.message.content[] | select(.type=="text") | .text'7. Stop a Worker
7. 停止Worker
bash
"$SCRIPTS/stop-worker.sh" my-worker "$SESSION_ID"The script:
- Sends to the tmux session
/exit - Waits up to 10s for a event
session_end - Kills the tmux session if still running
- Cleans up event and metadata files in
/tmp/claude-workers/
bash
"$SCRIPTS/stop-worker.sh" my-worker "$SESSION_ID"该脚本会:
- 向tmux会话发送命令
/exit - 最多等待10秒以获取事件
session_end - 如果会话仍在运行则终止tmux会话
- 清理下的事件和元数据文件
/tmp/claude-workers/
8. Hand Off to a Human
8. 交接给人工操作员
If you want a human to take over an active worker session:
The worker is running in tmux session 'my-worker'. You can:
- Watch live: tmux attach -t my-worker
- Take over: just start typing in the attached session
- Return to me: detach with Ctrl-B dLeave the worker running. Do not stop it when handing off.
如果你想让人工接管活跃的worker会话:
Worker正在tmux会话'my-worker'中运行。你可以:
- 实时查看:tmux attach -t my-worker
- 接管会话:在附加的会话中直接输入内容
- 返回给我:按下Ctrl-B d分离会话交接时请保持worker运行状态,不要停止它。
Script Reference
脚本参考
| Script | Usage | Description |
|---|---|---|
| | Send prompt, wait, return response |
| | Start a worker session |
| | Send a prompt to a worker |
| | Block until event or timeout |
| | Read event stream |
| | Gracefully stop and clean up |
| | Respond to a pending tool approval |
| | Format last turn as markdown |
All scripts exit 0 on success, non-zero on failure. Error messages go to stderr.
| 脚本 | 使用方式 | 描述 |
|---|---|---|
| | 发送提示词,等待结果,返回响应 |
| | 启动worker会话 |
| | 向worker发送提示词 |
| | 阻塞直到事件触发或超时 |
| | 读取事件流 |
| | 优雅停止并清理会话 |
| | 响应待处理的工具批准请求 |
| | 将最后一轮对话格式化为markdown |
所有脚本成功时返回0,失败时返回非0值。错误信息输出到标准错误流。
Common Patterns
常见模式
Single Worker: Delegate and Wait
单Worker:委派并等待
bash
RESULT=$("$SCRIPTS/launch-worker.sh" task-worker ~/myproject)
SESSION_ID=$(echo "$RESULT" | jq -r '.session_id')
"$SCRIPTS/send-prompt.sh" task-worker "Run the test suite and fix any failures"
"$SCRIPTS/wait-for-event.sh" "$SESSION_ID" stop 600bash
RESULT=$("$SCRIPTS/launch-worker.sh" task-worker ~/myproject)
SESSION_ID=$(echo "$RESULT" | jq -r '.session_id')
"$SCRIPTS/send-prompt.sh" task-worker "运行测试套件并修复所有失败用例"
"$SCRIPTS/wait-for-event.sh" "$SESSION_ID" stop 600Read what happened, then clean up
查看执行结果,然后清理
"$SCRIPTS/read-events.sh" "$SESSION_ID"
"$SCRIPTS/stop-worker.sh" task-worker "$SESSION_ID"
undefined"$SCRIPTS/read-events.sh" "$SESSION_ID"
"$SCRIPTS/stop-worker.sh" task-worker "$SESSION_ID"
undefinedFan-Out: Multiple Workers in Parallel
扇出模式:多Worker并行
bash
undefinedbash
undefinedLaunch workers for different tasks
为不同任务启动worker
R1=$("$SCRIPTS/launch-worker.sh" worker-api ~/myproject)
S1=$(echo "$R1" | jq -r '.session_id')
R2=$("$SCRIPTS/launch-worker.sh" worker-ui ~/myproject)
S2=$(echo "$R2" | jq -r '.session_id')
R1=$("$SCRIPTS/launch-worker.sh" worker-api ~/myproject)
S1=$(echo "$R1" | jq -r '.session_id')
R2=$("$SCRIPTS/launch-worker.sh" worker-ui ~/myproject)
S2=$(echo "$R2" | jq -r '.session_id')
Send each their task
为每个worker分配任务
"$SCRIPTS/send-prompt.sh" worker-api "Add pagination to the /users endpoint"
"$SCRIPTS/send-prompt.sh" worker-ui "Add a loading spinner to the user list page"
"$SCRIPTS/send-prompt.sh" worker-api "为/users端点添加分页功能"
"$SCRIPTS/send-prompt.sh" worker-ui "为用户列表页面添加加载动画"
Wait for both (sequentially -- first one to finish unblocks its wait)
等待所有worker完成(按顺序——第一个完成的会解除阻塞)
"$SCRIPTS/wait-for-event.sh" "$S1" stop 600
"$SCRIPTS/wait-for-event.sh" "$S2" stop 600
"$SCRIPTS/wait-for-event.sh" "$S1" stop 600
"$SCRIPTS/wait-for-event.sh" "$S2" stop 600
Clean up
清理资源
"$SCRIPTS/stop-worker.sh" worker-api "$S1"
"$SCRIPTS/stop-worker.sh" worker-ui "$S2"
undefined"$SCRIPTS/stop-worker.sh" worker-api "$S1"
"$SCRIPTS/stop-worker.sh" worker-ui "$S2"
undefinedPipeline: Chained Workers
流水线模式:链式Worker
Pass one worker's output to the next:
bash
undefined将一个worker的输出传递给下一个:
bash
undefinedWorker 1: Generate an API spec
Worker 1:生成API规格
R1=$("$SCRIPTS/launch-worker.sh" worker-spec ~/myproject)
S1=$(echo "$R1" | jq -r '.session_id')
"$SCRIPTS/send-prompt.sh" worker-spec "Generate an OpenAPI spec for the users endpoint and save it to /tmp/api-spec.yaml"
"$SCRIPTS/wait-for-event.sh" "$S1" stop 300
R1=$("$SCRIPTS/launch-worker.sh" worker-spec ~/myproject)
S1=$(echo "$R1" | jq -r '.session_id')
"$SCRIPTS/send-prompt.sh" worker-spec "为用户端点生成OpenAPI规格并保存到/tmp/api-spec.yaml"
"$SCRIPTS/wait-for-event.sh" "$S1" stop 300
Worker 2: Implement from the spec that Worker 1 produced
Worker 2:基于Worker 1生成的规格实现功能
R2=$("$SCRIPTS/launch-worker.sh" worker-impl ~/myproject)
S2=$(echo "$R2" | jq -r '.session_id')
"$SCRIPTS/send-prompt.sh" worker-impl "Implement the API endpoint defined in /tmp/api-spec.yaml"
"$SCRIPTS/wait-for-event.sh" "$S2" stop 600
R2=$("$SCRIPTS/launch-worker.sh" worker-impl ~/myproject)
S2=$(echo "$R2" | jq -r '.session_id')
"$SCRIPTS/send-prompt.sh" worker-impl "实现/tmp/api-spec.yaml中定义的API端点"
"$SCRIPTS/wait-for-event.sh" "$S2" stop 600
Clean up both
清理两个worker
"$SCRIPTS/stop-worker.sh" worker-spec "$S1"
"$SCRIPTS/stop-worker.sh" worker-impl "$S2"
The key: workers communicate through files on disk. The controller orchestrates the sequence."$SCRIPTS/stop-worker.sh" worker-spec "$S1"
"$SCRIPTS/stop-worker.sh" worker-impl "$S2"
核心要点:worker通过磁盘上的文件进行通信,管控者负责编排执行顺序。Supervised: Multi-Turn Conversation
受监督模式:多轮对话
converse.sh--after-linebash
RESULT=$("$SCRIPTS/launch-worker.sh" supervised ~/myproject)
SESSION_ID=$(echo "$RESULT" | jq -r '.session_id')
R1=$("$SCRIPTS/converse.sh" supervised "$SESSION_ID" "Write tests for the auth module" 300)
R2=$("$SCRIPTS/converse.sh" supervised "$SESSION_ID" "Now add edge case tests for expired tokens" 300)
"$SCRIPTS/stop-worker.sh" supervised "$SESSION_ID"converse.sh--after-linebash
RESULT=$("$SCRIPTS/launch-worker.sh" supervised ~/myproject)
SESSION_ID=$(echo "$RESULT" | jq -r '.session_id')
R1=$("$SCRIPTS/converse.sh" supervised "$SESSION_ID" "为认证模块编写测试用例" 300)
R2=$("$SCRIPTS/converse.sh" supervised "$SESSION_ID" "现在添加针对过期令牌的边缘情况测试" 300)
"$SCRIPTS/stop-worker.sh" supervised "$SESSION_ID"Reviewing Worker Output
审查Worker输出
converse.shread-turn.shbash
undefinedconverse.shread-turn.shbash
undefinedAfter a converse.sh call, review what the worker actually did
在converse.sh调用后,审查worker的实际执行过程
"$SCRIPTS/read-turn.sh" "$SESSION_ID"
"$SCRIPTS/read-turn.sh" "$SESSION_ID"
Show complete tool results (default truncates to 5 lines)
显示完整的工具执行结果(默认截断为5行)
"$SCRIPTS/read-turn.sh" "$SESSION_ID" --full
Output is formatted as markdown: thinking in blockquotes, tool calls as code blocks, results in fenced blocks, and text responses inline."$SCRIPTS/read-turn.sh" "$SESSION_ID" --full
输出内容会格式化为markdown:思考过程用块引用,工具调用用代码块,结果用围栏块,文本响应直接显示。Edge Cases
边缘情况处理
Worker Crashes or tmux Dies
Worker崩溃或tmux异常终止
If the tmux session disappears, will fail with "tmux session does not exist." Check before sending:
send-prompt.shbash
if ! tmux has-session -t my-worker 2>/dev/null; then
echo "Worker is gone -- need to relaunch"
fiThe event file at will still contain events emitted before the crash.
/tmp/claude-workers/<session-id>.events.jsonl如果tmux会话消失,会返回错误“tmux session does not exist”。发送前请检查:
send-prompt.shbash
if ! tmux has-session -t my-worker 2>/dev/null; then
echo "Worker已失效——需要重新启动"
fi崩溃前输出的事件仍会保存在文件中。
/tmp/claude-workers/<session-id>.events.jsonlTimeout While Waiting
等待超时
wait-for-event.sh- Extend the wait: Call again with a new timeout
wait-for-event.sh - Give up: Stop the worker with
stop-worker.sh - Hand off: Tell the human to take over
wait-for-event.sh- 延长等待时间:再次调用并设置新的超时时间
wait-for-event.sh - 放弃任务:使用停止worker
stop-worker.sh - 交接人工:通知人工操作员接管会话
Long Prompts
超长提示词
send-prompt.shtmux send-keys -lbash
echo "Your detailed instructions here..." > /tmp/worker-instructions.txt
"$SCRIPTS/send-prompt.sh" my-worker "Read /tmp/worker-instructions.txt and follow those instructions"send-prompt.shtmux send-keys -lbash
echo "你的详细指令内容..." > /tmp/worker-instructions.txt
"$SCRIPTS/send-prompt.sh" my-worker "读取/tmp/worker-instructions.txt并按照其中的指令执行"Tool Approval
工具调用批准
Every tool call the worker makes emits a event and waits up to 30 seconds for a controller decision before auto-approving. This gives you a window to inspect and approve or deny each tool call.
pre_tool_useTo monitor and auto-approve (default): Do nothing. If no decision is written within the timeout, the tool call proceeds.
To actively review tool calls:
bash
undefinedWorker的每一次工具调用都会触发事件,并等待最多30秒以获取管控者的决策,超时后会自动批准。这让你有机会检查并批准或拒绝每一次工具调用。
pre_tool_use监控并自动批准(默认):无需任何操作。如果超时前未做出决策,工具调用会自动执行。
主动审查工具调用:
bash
undefinedWatch for pending tool approvals
监控待处理的工具批准请求
PENDING_FILE="/tmp/claude-workers/${SESSION_ID}.tool-pending"
PENDING_FILE="/tmp/claude-workers/${SESSION_ID}.tool-pending"
Check if a tool call is waiting for approval
检查是否有工具调用等待批准
if [ -f "$PENDING_FILE" ]; then
cat "$PENDING_FILE" # Shows tool_name and tool_input
Approve it
"$SCRIPTS/approve-tool.sh" "$SESSION_ID" allow
Or deny it
"$SCRIPTS/approve-tool.sh" "$SESSION_ID" deny
fi
The timeout is configurable via the `CLAUDE_SESSION_DRIVER_APPROVAL_TIMEOUT` environment variable (default: 30 seconds). To change it, pass the env var when launching:
```bash
RESULT=$(CLAUDE_SESSION_DRIVER_APPROVAL_TIMEOUT=60 "$SCRIPTS/launch-worker.sh" my-worker ~/project)if [ -f "$PENDING_FILE" ]; then
cat "$PENDING_FILE" # 显示工具名称和工具输入参数
批准调用
"$SCRIPTS/approve-tool.sh" "$SESSION_ID" allow
或拒绝调用
"$SCRIPTS/approve-tool.sh" "$SESSION_ID" deny
fi
超时时间可通过环境变量`CLAUDE_SESSION_DRIVER_APPROVAL_TIMEOUT`配置(默认:30秒)。启动时传递该变量即可修改:
```bash
RESULT=$(CLAUDE_SESSION_DRIVER_APPROVAL_TIMEOUT=60 "$SCRIPTS/launch-worker.sh" my-worker ~/project)Important Notes
重要注意事项
- One controller per worker. Do not have multiple controllers sending prompts to the same tmux session.
- Clean up on failure. If something goes wrong, always try to stop the worker and remove temp files. handles this, but if the script itself fails, clean up manually:
stop-worker.shbashtmux kill-session -t my-worker 2>/dev/null rm -f /tmp/claude-workers/<session-id>.events.jsonl rm -f /tmp/claude-workers/<session-id>.meta - Event files are append-only JSONL. Each line is a self-contained JSON object with and
tsfields.event - Workers are full Claude Code sessions. They have their own tools, context, and conversation history. They do not share state with the controller except through files on disk and the event stream.
- The tmux session name must be unique. will fail if a session with that name already exists.
launch-worker.sh
- 一个worker对应一个管控者:不要让多个管控者向同一个tmux会话发送提示词。
- 失败时清理资源:如果出现异常,请始终尝试停止worker并删除临时文件。会处理这些操作,但如果脚本本身执行失败,请手动清理:
stop-worker.shbashtmux kill-session -t my-worker 2>/dev/null rm -f /tmp/claude-workers/<session-id>.events.jsonl rm -f /tmp/claude-workers/<session-id>.meta - 事件文件为追加式JSONL:每一行都是包含和
ts字段的独立JSON对象。event - Worker是完整的Claude Code会话:它们拥有独立的工具、上下文和对话历史。除了通过磁盘文件和事件流外,不会与管控者共享状态。
- tmux会话名称必须唯一:如果同名会话已存在,会执行失败。
launch-worker.sh