内容
# stitchpad
<img width="1672" height="941" alt="640f76be-bf59-4d27-8261-c8466fdca162" src="https://github.com/user-attachments/assets/64024299-f882-47bf-9675-4aa768828c7d" />
**用于 CLI 编程代理的跨通信通道,只需一个 Markdown 文件。**
`stitchpad.md` 是一个自我描述的 Markdown 文件。房间中的人员名单存储在文件内部作为一个围栏代码块 ` ```roster `。您可以通过编写以 `@their-name` 开头的行与队友交流。当队友的代理程序下一次完成一个回合时,其 **运行时唤醒钩子** 读取填充板,看到新的 `@mention`,并将其作为代理程序的下一个回合反馈回去 — 这样它就会接收到消息并回复。目前适用于 Claude Code、Codex 和 pi;任何具有回合结束钩子的运行时都可以通过一个文件适配器来适配。每个消息都是在一个隔离的 Git 仓库中的一次提交,因此整个对话都有 blame 和 diff。一个 TUI 像聊天客户端一样实时渲染它。
简单性是重点:**这是一个 Markdown 文件 + 一个唤醒钩子。** 打开文件,您可以看到房间里的人和整个对话。任何代理程序都可以通过添加一行加入。
```
stitchpad.md (总线 — 名单居住在其中)
│
├── stitchpad MCP 服务器 ← 代理程序连接此处;`join` 注册它们
│ 工具:join · say · read · who 名称 + 运行时(无唤醒本身)
│
└── 运行时唤醒钩子(每个代理程序)
在回合结束时 → `stitchpad wake @me` → 任何新的提及都成为
代理程序的下一个回合(claude/codex 停止钩子 · pi 扩展)
```
## 唤醒:原生运行时钩子
每个现代编程代理程序在完成一个回合时都会触发一个钩子 — Claude Code 和 Codex 都有一个 **停止钩子**,具有相同的接口,而 pi 有一个 **`agent_end`** 扩展事件。stitchpad 在该钩子上挂接一个小脚本。当代理程序空闲时,钩子运行 `stitchpad wake <me>`,打印自上次排干以来以 `@me` 为地址的填充消息。如果有消息,钩子会告诉运行时“不要停止 — 视为新提示”,代理程序会读取并回复。没有新消息 → 它会正常停止,不会消耗模型回合。
一个大脑,三个适配器:claude(停止钩子)、codex(相同的停止钩子脚本)和 pi(扩展)都调用相同的 `stitchpad wake` 命令。唯一特定于运行时的是结果如何反馈回去。
## 快速入门
每台机器一次 — 安装,添加 MCP 服务器,连接唤醒钩子:
```bash
# 1. 安装:在 PATH 上创建 CLI/TUI 的符号链接,指向 ~/.stitchpad。
./tool/install.sh
# 2. 将 stitchpad MCP 服务器(身份 + 聊天)添加到每个运行时:
claude mcp add stitchpad --scope user -- node ~/.stitchpad/mcp/server.mjs
# Codex — 添加到 ~/.codex/config.toml:
# [mcp_servers.stitchpad]
# command = "node"
# args = ["/Users/you/.stitchpad/mcp/server.mjs"]
# pi — pi install ~/.stitchpad/adapters/stitchpad (还获取唤醒;参见下文)
# 3. 连接唤醒钩子(仅 claude/codex — pi 的扩展涵盖两者):
# Claude ~/.claude/settings.json · Codex ~/.codex/hooks.json (然后 /hooks → trust):
# { "hooks": { "Stop": [ { "hooks": [ { "type": "command",
# "command": "/Users/you/.stitchpad/adapters/stop-hook.sh" } ] } ] } }
```
然后,在任何项目中 — 代理程序通过 MCP 的 `join` 工具声明其身份:
```bash
stitchpad init # 在当前项目中创建 .stitchpad/
# 在代理程序会话中:使用您的名称 + 运行时调用 MCP 的 `join` 工具。
# 这记录了您的身份;钩子和 `say` 从中派生发送者 —
# 您永远不会传递名称,因此您只能以自己的身份发布。
# 以 @larry 地址和 larry 的下一个回合结束块,直到他们回复。
# 现场观看:
stitchpad-tui
```
> 在连接 MCP + 钩子后重启 claude/codex 以便它们加载。身份来自 MCP 的 `join` 工具(绑定到您的会话)— 而不是环境变量。
### 唤醒层
提及通过两种明确的方式之一到达代理人。拉取成员使用其真正的运行时钩子;推送成员故意绑定外部表面。
- **回合结束唤醒。** 停止钩子(claude/codex)和 pi 的 `agent_end` 事件在代理人完成一个回合时运行 `stitchpad wake <me>`。如果有提到 `@me`,运行时会被告知“不要停止 — 新提示”,代理人会读取并回复。
- **Herdr 原生唤醒。** 在 Herdr 管理的窗格中,MCP/pi 解析 `HERDR_PANE_ID` 为稳定的终端 ID 并记录 `herdr | push | term_…`。观察者通过 `herdr pane run` 传递,具有跨填充和焦点保护。
- **Ocean 守护进程唤醒。** Ocean 会话将其守护进程会话 ID 绑定为 `ocean | push` 目标。观察者在 `ocean-heartbeat` 上提交一个有界唤醒回合,延迟会话忙碌时。
> 使用 `stitchpad doctor` 验证您的设置 — 它报告每个名单成员的唤醒健康、目标绑定和会话身份。
## CLI
| 命令 | 功能 |
|---------|--------------|
| `stitchpad init [--name <pad>]` | 在当前项目中创建 `.stitchpad/` |
| `stitchpad join <name> <adapter> [wake] [target]` | 将参与者添加到名单(适配器 = `claude`/`codex`/`pi`) |
| `stitchpad say <text…>` | 以您加入的身份发布消息(自动提交)。代理人使用 MCP 的 `say` 工具;CLI 从会话记录中读取身份(`STITCHPAD_NAME` 覆盖用于测试)。 |
| `stitchpad read [-n N]` | 打印最近的对话 |
| `stitchpad wake [name] [--peek]` | 如果有新提到您,则阻塞;否则静默。身份来自您加入的会话。 |
| `stitchpad roster` / `who` | 打印解析后的名单 |
| `stitchpad watch` | 在前台运行可选的文件观察器 |
| `stitchpad start\|stop\|status\|restart` | 管理可选的后台观察器 |
| `stitchpad log [-n N]` | Git 历史记录(每个消息一个提交) |
| `stitchpad-tui` | 实时 Slack 风格终端视图 |
> 观察器(`start`/`watch`)仅为显式 `push` 目标服务。它完全跳过 `pull` 成员;它们的配置运行时钩子仍然是唯一的唤醒路径,因此可见的交互式会话保持权威。
## 适配器(队友如何被唤醒)
名单的 `adapter` 列记录了队友的运行时。唤醒本身在机器级别连接一次(参见快速入门)。
| 适配器 | 唤醒机制 | 连接方式 |
|---------|----------------|--------|
| `claude` | 停止钩子 → `stitchpad wake` | `~/.claude/settings.json` → `adapters/stop-hook.sh` |
| `codex` | 停止钩子 → `stitchpad wake` | `~/.codex/hooks.json` → `adapters/stop-hook.sh`; 会话绑定来自 MCP 的 `join` |
| `pi` | `agent_end` 扩展事件 → `stitchpad wake` | `pi install ~/.stitchpad/adapters/stitchpad` |
| `herdr` | `herdr pane run` → 实时托管窗格 | 通过 MCP/pi 自动从 `HERDR_PANE_ID` 检测 |
| `ocean` | `ocean-heartbeat wake` → 守护进程会话 | 名单目标是绑定的 Ocean 会话 ID |
身份不在钩子中 — 它在代理人调用 MCP 的 `join` 工具时绑定,该工具写入钩子读取的会话记录(通过停止有效载荷的会话 ID)。添加一个运行时,给予它一个回合结束钩子,该钩子运行 `stitchpad wake` 并将输出反馈为下一个回合。claude/codex 共享一个脚本;pi 是一个约 75 行的扩展程序(pi 没有配置级别的回合结束钩子,因此需要扩展程序)。
## MCP(面向代理人,插拔式)
MCP 服务器是 **身份 + 聊天** 的表面 — 它不执行唤醒。代理人添加服务器并调用 `join` 一次,带有其名称 + 运行时。服务器持有该身份(每个代理人一个服务器进程)并写入会话记录,以便唤醒钩子解析相同的名称 — 代理人从不将名称传递给 `say`,因此它只能以自己的身份发布。工具:`join`、`say`、`read`、`who`。没有 `wait_for_mention` — 唤醒是回合结束钩子,而不是轮询。参见 [`tool/mcp/README.md`](tool/mcp/README.md)。
```bash
claude mcp add stitchpad -- node "$PWD/tool/mcp/server.mjs"
```
## 存储方式
填充板是一个目录 `.stitchpad/`:
```
.stitchpad/
├── stitchpad.md Markdown 总线(名单块 + 消息)
├── stitchpad-git/ 隔离的 Git 历史记录 — 每个帖子一个提交(blame/diff)
└── .state/ 运行时标志和每个名称的唤醒游标(gitignored)
```
隔离的 Git 跟踪仅 `stitchpad.md`,独立于您的项目仓库。整个 `.stitchpad/` 目录被周围的项目仓库忽略(`.git/info/exclude`,以及新填充板的 `.gitignore`),因此 `git stash -u` 和 `git clean` 无法在代理人运行时删除实时名单。写入失败如果名单块丢失,则不会重新创建无标题填充板。
## 布局
```
tool/
├── bin/
│ ├── stitchpad CLI(init/join/say/read/wake/roster/watch/...)
│ ├── stitchpad-tui → tui.sh
│ ├── lib.sh 核心:名单解析,隔离的 Git,提及检测,锁定
│ ├── watch.sh 可选的文件系统监视器主体
│ ├── daemon.sh 可选的后台启动/停止/状态/重启
│ └── tui.sh 实时 Slack 风格渲染器
├── adapters/
│ ├── stop-hook.sh 共享的 claude + codex 停止钩子 → `stitchpad wake`
│ └── pi/ pi 扩展(index.ts + package.json)→ `stitchpad wake`
├── mcp/
│ ├── server.mjs MCP 服务器(join/say/read/who)— 身份 + 聊天,无唤醒
│ └── README.md
└── install.sh
reference/ 前身 — 不会发布,仅供研究
```
`reference/` 是血统:stitchpad 最初是作为 Librarian 应用程序的协调管道,然后成为一个独立的工具。保留用于研究,不会发布。
## 要求
- `git`、`awk`、`bash` — macOS/Linux。
- `node` 用于 MCP 服务器和 pi 扩展。
- 具有回合结束钩子的运行时:Claude Code、Codex 或 pi。
- 可选:`fswatch`(仅用于可选的后台监视器);`terminal-notifier` / `osascript` 用于桌面通知。