mirror of
https://github.com/shareAI-lab/learn-claude-code.git
synced 2026-04-30 23:39:34 +00:00
add worktree & up task、teammate etc
This commit is contained in:
parent
c6a27ef1d7
commit
aea8844bac
54 changed files with 2404 additions and 210 deletions
|
|
@ -1,6 +1,6 @@
|
|||
# s01: Agent Loop (智能体循环)
|
||||
|
||||
> AI 编程智能体的全部秘密就是一个 while 循环 -- 把工具执行结果反馈给模型, 直到模型决定停止。
|
||||
> AI 编程智能体的核心是一个 while 循环 -- 把工具执行结果反馈给模型, 直到模型决定停止。
|
||||
|
||||
## 问题
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ response = client.messages.create(
|
|||
messages.append({"role": "assistant", "content": response.content})
|
||||
```
|
||||
|
||||
4. 检查 stop_reason。如果模型没有调用工具, 循环结束。这是唯一的退出条件。
|
||||
4. 检查 stop_reason。如果模型没有调用工具, 循环结束。在本节最小实现里, 这是唯一的循环退出条件。
|
||||
|
||||
```python
|
||||
if response.stop_reason != "tool_use":
|
||||
|
|
@ -115,7 +115,7 @@ def agent_loop(messages: list):
|
|||
|
||||
## 设计原理
|
||||
|
||||
这个循环是所有基于 LLM 的智能体的通用基础。生产实现会增加错误处理、token 计数、流式输出和重试逻辑, 但基本结构不变。简洁性就是重点: 一个退出条件 (`stop_reason != "tool_use"`) 控制整个流程。本课程中的所有其他内容 -- 工具、规划、压缩、团队 -- 都是在这个循环之上叠加, 而不修改它。理解这个循环就是理解所有智能体。
|
||||
这个循环是所有基于 LLM 的智能体基础。生产实现还会增加错误处理、token 计数、流式输出、重试、权限策略与生命周期编排, 但核心交互模式仍从这里开始。本节强调简洁性: 在本节最小实现里, 一个退出条件 (`stop_reason != "tool_use"`) 就能支撑我们先学会主流程。本课程中的其他内容都在这个循环上叠加。理解这个循环是建立基础心智模型, 不是完整的生产架构。
|
||||
|
||||
## 试一试
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
只有 `bash` 时, 智能体所有操作都通过 shell: 读文件、写文件、编辑文件。这能用但很脆弱。`cat` 的输出会被不可预测地截断。`sed` 替换遇到特殊字符就会失败。模型浪费大量 token 构造 shell 管道, 而一个直接的函数调用会简单得多。
|
||||
|
||||
更重要的是, bash 是一个安全攻击面。每次 bash 调用都能做 shell 能做的一切。有了专用工具如 `read_file` 和 `write_file`, 你可以在工具层面强制路径沙箱化, 阻止危险模式, 而不是寄希望于模型自觉回避。
|
||||
更重要的是, bash 存在安全风险。每次 bash 调用都能做 shell 能做的一切。有了专用工具如 `read_file` 和 `write_file`, 你可以在工具层面强制路径沙箱化, 阻止危险模式, 而不是寄希望于模型自觉回避。
|
||||
|
||||
关键洞察: 添加工具不需要修改循环。s01 的循环保持不变。你只需在工具数组中添加条目, 编写处理函数, 然后通过 dispatch map 把它们关联起来。
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
解决方案是结构化状态: 一个模型显式写入的 TodoManager。模型创建计划, 工作时将项目标记为 in_progress, 完成后标记为 completed。nag reminder 机制在模型连续 3 轮以上不更新待办时注入提醒。
|
||||
|
||||
教学简化说明: 这里 nag 阈值设为 3 轮是为了教学可见性。生产环境的智能体通常使用约 10 轮的阈值以避免过度提醒。
|
||||
注: nag 阈值 3 轮是为教学可见性设的低值, 生产环境通常更高。从 s07 起, 课程转向 Task 看板处理持久化多步工作; TodoWrite 仍可用于轻量清单。
|
||||
|
||||
## 解决方案
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
这对探索性任务尤其糟糕。"这个项目用了什么测试框架?" 可能需要读取 5 个文件, 但父智能体的历史中并不需要这 5 个文件的全部内容 -- 它只需要答案: "pytest, 使用 conftest.py 配置。"
|
||||
|
||||
解决方案是进程隔离: 以 `messages=[]` 启动一个子智能体。子智能体进行探索、读取文件、运行命令。完成后, 只有最终的文本响应返回给父智能体。子智能体的全部消息历史被丢弃。
|
||||
在本课程里, 一个实用解法是 fresh `messages[]` 隔离: 以 `messages=[]` 启动一个子智能体。子智能体进行探索、读取文件、运行命令。完成后, 只有最终的文本响应返回给父智能体。子智能体的全部消息历史被丢弃。
|
||||
|
||||
## 解决方案
|
||||
|
||||
|
|
@ -124,11 +124,10 @@ def run_subagent(prompt: str) -> str:
|
|||
| 上下文 | 单一共享 | 父 + 子隔离 |
|
||||
| Subagent | 无 | `run_subagent()` 函数 |
|
||||
| 返回值 | 不适用 | 仅摘要文本 |
|
||||
| Todo 系统 | TodoManager | 已移除 (非本节重点) |
|
||||
|
||||
## 设计原理
|
||||
|
||||
进程隔离免费提供了上下文隔离。全新的 `messages[]` 意味着子智能体不会被父级的对话历史干扰。代价是通信开销 -- 结果必须压缩回父级, 丢失细节。这与操作系统进程隔离的权衡相同: 用序列化成本换取安全性和整洁性。限制子智能体深度 (不允许递归生成) 防止无限资源消耗, 最大迭代次数确保失控的子进程能终止。
|
||||
在本节中, fresh `messages[]` 隔离是一个近似实现上下文隔离的实用办法。全新的 `messages[]` 意味着子智能体从不携带父级历史开始。代价是通信开销 -- 结果必须压缩回父级, 丢失细节。这是消息历史隔离策略, 不是操作系统进程隔离本身。限制子智能体深度 (不允许递归生成) 防止无限资源消耗, 最大迭代次数确保失控的子任务能终止。
|
||||
|
||||
## 试一试
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
## 问题
|
||||
|
||||
你希望智能体针对不同领域遵循特定的工作流: git 约定、测试模式、代码审查清单。简单粗暴的做法是把所有内容都塞进系统提示。但系统提示的有效注意力是有限的 -- 文本太多, 模型就会开始忽略其中一部分。
|
||||
智能体需要针对不同领域遵循特定的工作流: git 约定、测试模式、代码审查清单。简单粗暴的做法是把所有内容都塞进系统提示。但系统提示的有效注意力是有限的 -- 文本太多, 模型就会开始忽略其中一部分。
|
||||
|
||||
如果你有 10 个技能, 每个 2000 token, 那就是 20,000 token 的系统提示。模型关注开头和结尾, 但会略过中间部分。更糟糕的是, 这些技能中大部分与当前任务无关。文件编辑任务不需要 git 工作流说明。
|
||||
|
||||
|
|
@ -132,7 +132,6 @@ class SkillLoader:
|
|||
| 系统提示 | 静态字符串 | + 技能描述列表 |
|
||||
| 知识库 | 无 | .skills/*.md 文件 |
|
||||
| 注入方式 | 无 | 两层 (系统提示 + result) |
|
||||
| Subagent | `run_subagent()` | 已移除 (非本节重点) |
|
||||
|
||||
## 设计原理
|
||||
|
||||
|
|
|
|||
|
|
@ -149,7 +149,6 @@ def agent_loop(messages):
|
|||
| Auto-compact | 无 | token 阈值触发 |
|
||||
| Manual compact | 无 | `compact` 工具 |
|
||||
| Transcripts | 无 | 保存到 .transcripts/ |
|
||||
| Skills | load_skill | 已移除 (非本节重点) |
|
||||
|
||||
## 设计原理
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,19 @@
|
|||
|
||||
更根本地说, 内存中的状态对其他智能体不可见。当我们最终构建团队 (s09+) 时, 队友需要一个共享的任务看板。内存中的数据结构是进程局部的。
|
||||
|
||||
解决方案是将任务作为 JSON 文件持久化在 `.tasks/` 目录中。每个任务是一个单独的文件, 包含 ID、主题、状态和依赖图。完成任务 1 会自动解除任务 2 的阻塞 (如果任务 2 有 `blockedBy: [1]`)。文件系统成为唯一的真实来源。
|
||||
解决方案是将任务作为 JSON 文件持久化在 `.tasks/` 目录中。每个任务是一个单独的文件, 包含 ID、主题、状态和依赖图。完成任务 1 会自动解除任务 2 的阻塞 (如果任务 2 有 `blockedBy: [1]`)。在本教学实现里, 文件系统是任务状态的真实来源。
|
||||
|
||||
## Task vs Todo: 何时用哪个
|
||||
|
||||
从 s07 起, Task 是默认主线。Todo 仍可用于短期线性清单。
|
||||
|
||||
## 快速判定矩阵
|
||||
|
||||
| 场景 | 优先选择 | 原因 |
|
||||
|---|---|---|
|
||||
| 短时、单会话、线性清单 | Todo | 心智负担最低,记录最快 |
|
||||
| 跨会话、存在依赖、多人协作 | Task | 状态可持久、依赖可表达、协作可见 |
|
||||
| 一时拿不准 | Task | 后续降级更容易,半途迁移成本更低 |
|
||||
|
||||
## 解决方案
|
||||
|
||||
|
|
@ -132,17 +144,20 @@ class TaskManager:
|
|||
|
||||
## 相对 s06 的变更
|
||||
|
||||
| 组件 | 之前 (s06) | 之后 (s07) |
|
||||
|----------------|------------------|----------------------------------|
|
||||
| Tools | 5 | 8 (+task_create/update/list/get) |
|
||||
| 状态存储 | 仅内存 | .tasks/ 中的 JSON 文件 |
|
||||
| 依赖关系 | 无 | blockedBy + blocks 图 |
|
||||
| 压缩机制 | 三层 | 已移除 (非本节重点) |
|
||||
| 持久化 | 压缩后丢失 | 压缩后存活 |
|
||||
| 组件 | 之前 (s06) | 之后 (s07) |
|
||||
|---|---|---|
|
||||
| Tools | 5 | 8 (`task_create/update/list/get`) |
|
||||
| 状态存储 | 仅内存 | `.tasks/` 中的 JSON 文件 |
|
||||
| 依赖关系 | 无 | `blockedBy + blocks` 图 |
|
||||
| 持久化 | 压缩后丢失 | 压缩后存活 |
|
||||
|
||||
## 设计原理
|
||||
|
||||
基于文件的状态能在上下文压缩中存活。当智能体的对话被压缩时, 内存中的状态会丢失, 但写入磁盘的任务会持久保存。依赖图确保即使在上下文丢失后也能按正确顺序执行。这是临时对话与持久工作之间的桥梁 -- 智能体可以忘记对话细节, 但始终有任务看板来提醒它还需要做什么。文件系统作为唯一真实来源也为未来的多智能体共享提供了基础, 因为任何进程都可以读取相同的 JSON 文件。
|
||||
基于文件的状态能在上下文压缩中存活。当智能体的对话被压缩时, 内存中的状态会丢失, 但写入磁盘的任务会持久保存。依赖图确保即使在上下文丢失后也能按正确顺序执行。这是临时对话与持久工作之间的桥梁 -- 智能体可以忘记对话细节, 但始终有任务看板来提醒它还需要做什么。在本教学实现里, 文件系统作为任务状态真实来源也为未来的多智能体共享提供了基础, 因为任何进程都可以读取相同的 JSON 文件。
|
||||
|
||||
但“持久化”成立有前提:每次写入前都要重新读取任务文件,确认 `status/blockedBy` 与预期一致,再原子写回。否则并发写入很容易互相覆盖状态。
|
||||
|
||||
从课程设计上看, 这也是为什么 s07 之后我们默认采用 Task 而不是 Todo: 它更接近真实工程中的长期执行与协作需求。
|
||||
|
||||
## 试一试
|
||||
|
||||
|
|
|
|||
|
|
@ -157,7 +157,6 @@ class BackgroundManager:
|
|||
| 执行方式 | 仅阻塞 | 阻塞 + 后台线程 |
|
||||
| 通知机制 | 无 | 每轮排空的队列 |
|
||||
| 并发 | 无 | 守护线程 |
|
||||
| 任务系统 | 基于文件的 CRUD | 已移除 (非本节重点) |
|
||||
|
||||
## 设计原理
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# s09: Agent Teams (智能体团队)
|
||||
|
||||
> 持久化的队友通过 JSONL 收件箱将孤立的智能体转变为可通信的团队 -- spawn、message、broadcast 和 drain。
|
||||
> 持久化的队友通过 JSONL 收件箱提供了一种教学协议, 将孤立的智能体转变为可通信的团队 -- spawn、message、broadcast 和 drain。
|
||||
|
||||
## 问题
|
||||
|
||||
|
|
@ -194,7 +194,7 @@ class MessageBus:
|
|||
|
||||
## 设计原理
|
||||
|
||||
基于文件的邮箱 (追加式 JSONL) 提供了并发安全的智能体间通信。追加操作在大多数文件系统上是原子的, 避免了锁竞争。"读取时排空" 模式 (读取全部, 截断) 提供批量传递。这比共享内存或基于 socket 的 IPC 更简单、更健壮。代价是延迟 -- 消息只在下一次轮询时才被看到 -- 但对于每轮需要数秒推理时间的 LLM 驱动智能体来说, 轮询延迟相比推理时间可以忽略不计。
|
||||
基于文件的邮箱 (追加式 JSONL) 在教学代码中具有可观察、易理解的优势。"读取时排空" 模式 (读取全部, 截断) 用很少的机制就能实现批量传递。代价是延迟 -- 消息只在下一次轮询时才被看到 -- 但对于每轮需要数秒推理时间的 LLM 驱动智能体来说, 本课程中该延迟是可接受的。
|
||||
|
||||
## 试一试
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
但自治智能体面临一个微妙问题: 上下文压缩后, 智能体可能忘记自己是谁。如果消息被摘要化, 原始系统提示中的身份 ("你是 alice, 角色: coder") 就会淡化。身份重注入通过在压缩后的上下文开头插入身份块来解决这个问题。
|
||||
|
||||
教学简化说明: 这里的 token 估算比较粗糙 (字符数 / 4)。生产系统使用专业的 tokenizer 库。s03 中的 nag 阈值 3 轮是为教学可见性设的低值; 生产环境的智能体通常使用约 10 轮的阈值。
|
||||
注: token 估算使用字符数/4 (粗略)。nag 阈值 3 轮是为教学可见性设的低值。
|
||||
|
||||
## 解决方案
|
||||
|
||||
|
|
|
|||
193
docs/zh/s12-worktree-task-isolation.md
Normal file
193
docs/zh/s12-worktree-task-isolation.md
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
# s12: Worktree + 任务隔离
|
||||
|
||||
> 目录隔离, 任务 ID 协调 -- 用"任务板 (控制面) + worktree (执行面)"把并行改动从互相污染变成可追踪、可恢复、可收尾。
|
||||
|
||||
## 问题
|
||||
|
||||
s11 时, agent 已经能认领任务并协同推进。但所有任务共享同一个工作目录。两个 agent 同时改同一棵文件树时, 未提交的变更互相干扰, 任务状态和实际改动对不上, 收尾时也无法判断该保留还是清理哪些文件。
|
||||
|
||||
考虑一个具体场景: agent A 在做 auth 重构, agent B 在做登录页。两者都修改了 `config.py`。A 的半成品改动被 B 的 `git status` 看到, B 以为是自己的遗留, 尝试提交 -- 结果两个任务都坏了。
|
||||
|
||||
根因是"做什么"和"在哪里做"没有分开。任务板管目标, 但执行上下文是共享的。解决方案: 给每个任务分配独立的 git worktree 目录, 用任务 ID 把两边关联起来。
|
||||
|
||||
## 解决方案
|
||||
|
||||
```
|
||||
控制面 (.tasks/) 执行面 (.worktrees/)
|
||||
+------------------+ +------------------------+
|
||||
| task_1.json | | auth-refactor/ |
|
||||
| status: in_progress <----> branch: wt/auth-refactor
|
||||
| worktree: "auth-refactor" | task_id: 1 |
|
||||
+------------------+ +------------------------+
|
||||
| task_2.json | | ui-login/ |
|
||||
| status: pending <----> branch: wt/ui-login
|
||||
| worktree: "ui-login" | task_id: 2 |
|
||||
+------------------+ +------------------------+
|
||||
|
|
||||
index.json (worktree registry)
|
||||
events.jsonl (lifecycle log)
|
||||
```
|
||||
|
||||
三层状态:
|
||||
1. 控制面 (What): `.tasks/task_*.json` -- 任务目标、责任归属、完成状态
|
||||
2. 执行面 (Where): `.worktrees/index.json` -- 隔离目录路径、分支、存活状态
|
||||
3. 运行态 (Now): 单轮内存上下文 -- 当前任务、当前 worktree、工具结果
|
||||
|
||||
状态机:
|
||||
```text
|
||||
Task: pending -> in_progress -> completed
|
||||
Worktree: absent -> active -> removed | kept
|
||||
```
|
||||
|
||||
## 工作原理
|
||||
|
||||
1. 创建任务, 把目标写入任务板。
|
||||
|
||||
```python
|
||||
TASKS.create("Implement auth refactor")
|
||||
# -> .tasks/task_1.json status=pending worktree=""
|
||||
```
|
||||
|
||||
2. 创建 worktree 并绑定任务。传入 `task_id` 时自动把任务推进到 `in_progress`。
|
||||
|
||||
```python
|
||||
WORKTREES.create("auth-refactor", task_id=1)
|
||||
# -> git worktree add -b wt/auth-refactor .worktrees/auth-refactor HEAD
|
||||
# -> index.json 追加 entry, task_1.json 绑定 worktree="auth-refactor"
|
||||
```
|
||||
|
||||
3. 在隔离目录中执行命令。`cwd` 指向 worktree 路径, 主目录不受影响。
|
||||
|
||||
```python
|
||||
WORKTREES.run("auth-refactor", "git status --short")
|
||||
# -> subprocess.run(command, cwd=".worktrees/auth-refactor", ...)
|
||||
```
|
||||
|
||||
4. 观测和回写。`worktree_status` 查看 git 状态, `task_update` 维护进度。
|
||||
|
||||
```python
|
||||
WORKTREES.status("auth-refactor") # git status inside worktree
|
||||
TASKS.update(1, owner="agent-A") # update task metadata
|
||||
```
|
||||
|
||||
5. 收尾: 选择 keep 或 remove。`remove` 配合 `complete_task=true` 会同时完成任务并解绑 worktree。
|
||||
|
||||
```python
|
||||
WORKTREES.remove("auth-refactor", complete_task=True)
|
||||
# -> git worktree remove
|
||||
# -> task_1.json status=completed, worktree=""
|
||||
# -> index.json status=removed
|
||||
# -> events.jsonl 写入 task.completed + worktree.remove.after
|
||||
```
|
||||
|
||||
6. 进程中断后, 从 `.tasks/` + `.worktrees/index.json` 重建现场。会话记忆是易失的, 磁盘状态是持久的。
|
||||
|
||||
## 核心代码
|
||||
|
||||
事件流 -- append-only 生命周期日志 (来自 `agents/s12_worktree_task_isolation.py`):
|
||||
|
||||
```python
|
||||
class EventBus:
|
||||
def emit(self, event, task=None, worktree=None, error=None):
|
||||
payload = {
|
||||
"event": event,
|
||||
"ts": time.time(),
|
||||
"task": task or {},
|
||||
"worktree": worktree or {},
|
||||
}
|
||||
if error:
|
||||
payload["error"] = error
|
||||
with self.path.open("a", encoding="utf-8") as f:
|
||||
f.write(json.dumps(payload) + "\n")
|
||||
```
|
||||
|
||||
事件流写入 `.worktrees/events.jsonl`, 每个关键操作发出三段式事件:
|
||||
- `worktree.create.before / after / failed`
|
||||
- `worktree.remove.before / after / failed`
|
||||
- `task.completed` (当 `complete_task=true` 成功时)
|
||||
|
||||
事件负载形状:
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "worktree.remove.after",
|
||||
"task": {"id": 7, "status": "completed"},
|
||||
"worktree": {"name": "auth-refactor", "path": "...", "status": "removed"},
|
||||
"ts": 1730000000
|
||||
}
|
||||
```
|
||||
|
||||
任务绑定 -- Task 侧持有 worktree 名称:
|
||||
|
||||
```python
|
||||
def bind_worktree(self, task_id: int, worktree: str, owner: str = "") -> str:
|
||||
task = self._load(task_id)
|
||||
task["worktree"] = worktree
|
||||
if task["status"] == "pending":
|
||||
task["status"] = "in_progress"
|
||||
self._save(task)
|
||||
```
|
||||
|
||||
隔离执行 -- cwd 路由到 worktree 目录:
|
||||
|
||||
```python
|
||||
r = subprocess.run(
|
||||
command,
|
||||
shell=True,
|
||||
cwd=path,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=300,
|
||||
)
|
||||
```
|
||||
|
||||
收尾联动 -- remove 同时完成任务:
|
||||
|
||||
```python
|
||||
def remove(self, name, force=False, complete_task=False):
|
||||
self._run_git(["worktree", "remove", wt["path"]])
|
||||
if complete_task and wt.get("task_id") is not None:
|
||||
self.tasks.update(wt["task_id"], status="completed")
|
||||
self.tasks.unbind_worktree(wt["task_id"])
|
||||
self.events.emit("task.completed", ...)
|
||||
```
|
||||
|
||||
生命周期工具注册:
|
||||
|
||||
```python
|
||||
"worktree_keep": lambda **kw: WORKTREES.keep(kw["name"]),
|
||||
"worktree_events": lambda **kw: EVENTS.list_recent(kw.get("limit", 20)),
|
||||
```
|
||||
|
||||
## 相对 s11 的变更
|
||||
|
||||
| 组件 | 之前 (s11) | 之后 (s12) |
|
||||
|----------------|----------------------------|-----------------------------------------|
|
||||
| 协调状态 | 任务板 (owner/status) | 任务板 + `worktree` 显式绑定 |
|
||||
| 执行上下文 | 共享目录 | 每个任务可分配独立 worktree 目录 |
|
||||
| 可恢复性 | 依赖任务状态 | 任务状态 + worktree 索引双重恢复 |
|
||||
| 收尾语义 | 任务完成 | 任务完成 + worktree 显式 keep/remove |
|
||||
| 生命周期可见性 | 隐式日志 | `.worktrees/events.jsonl` 显式事件流 |
|
||||
|
||||
## 设计原理
|
||||
|
||||
控制面/执行面分离是这一章的核心模式。Task 管"做什么", worktree 管"在哪做", 两者通过 task ID 关联但不强耦合。这意味着一个任务可以先不绑定 worktree (纯规划阶段), 也可以在多个 worktree 之间迁移。
|
||||
|
||||
显式状态机让每次迁移都可审计、可恢复。进程崩溃后, 从 `.tasks/` 和 `.worktrees/index.json` 两个文件就能重建全部现场, 不依赖会话内存。
|
||||
|
||||
事件流是旁路可观测层, 不替代主状态机写入。审计、通知、配额控制等副作用放在事件消费者中处理, 核心流程保持最小。`keep/remove` 作为显式收尾动作存在, 而不是隐式清理 -- agent 必须做出决策, 这个决策本身被记录。
|
||||
|
||||
## 试一试
|
||||
|
||||
```sh
|
||||
cd learn-claude-code
|
||||
python agents/s12_worktree_task_isolation.py
|
||||
```
|
||||
|
||||
可以尝试的提示:
|
||||
|
||||
1. `Create tasks for backend auth and frontend login page, then list tasks.`
|
||||
2. `Create worktree "auth-refactor" for task 1, create worktree "ui-login", then bind task 2 to "ui-login".`
|
||||
3. `Run "git status --short" in worktree "auth-refactor".`
|
||||
4. `Keep worktree "ui-login", then list worktrees and inspect worktree events.`
|
||||
5. `Remove worktree "auth-refactor" with complete_task=true, then list tasks/worktrees/events.`
|
||||
Loading…
Add table
Add a link
Reference in a new issue