---
name: workbuddy-context-handoff
description: "给 WorkBuddy（桌面端 CodeBuddy Code 运行时）配置基于 hooks 的上下文 handoff 门禁 —— 上下文占用达阈值时自动强制产出 handoff 文档，新会话自动注入继续干活。也用于排查 WorkBuddy hooks / 上下文压缩阈值 / transcript 用量相关问题。触发场景：上下文到 N% 自动生成 handoff、交接文档、新会话续接、hooks 怎么配、为什么自动压缩了、上下文用量怎么算、context handoff、auto compact。注意：本技能的平台路径与 hook 事件名是 WorkBuddy 专有，不适用于其它 agent 运行时。"
agent_created: true
---

# WorkBuddy 上下文 handoff 门禁

## Purpose

在 WorkBuddy 桌面端搭一条"上下文快满 -> 强制产出 handoff 文档 -> 新会话自动续接"的流水线。

- **触发端**：Stop hook，按 transcript 里的 token 用量判断是否到阈值，到了就用
  `{"continue": false, "reason": "..."}` 强制模型先写 handoff。
- **注入端**：SessionStart hook，把本项目最新未被消费的 handoff 注入新会话上下文。
- 附带用途：解释/排查 WorkBuddy 的自动压缩阈值、hooks 事件与 transcript 用量计算。

## When to Use

- 用户要"上下文到 N% 自动生成 handoff / 交接文档 / 新会话续接"。
- 用户问 WorkBuddy hooks 怎么配、为什么自动压缩了、上下文用量怎么算。
- 需要定位 WorkBuddy 桌面端的运行时行为（阈值、config option、hook 事件）。
- 触发词：上下文交接、handoff、上下文压缩、自动压缩、hooks 配置、新会话续接、
  context handoff、auto compact。

## When NOT to Use

- 用户只是想知道"我现在用了多少上下文" -> 直接按 transcript 算一次回答，不必配 hook。
- 目标运行时不是 WorkBuddy 桌面端（路径、事件名、环境变量都不同）-> 不适用，先确认运行时。
- 用户要的是一份手工交接文档 -> 直接写，不需要这套门禁。

## Workflow

1. **确认位置与备份**：定位 settings（用户级 `~/.workbuddy/settings.json` 或项目级
   `<proj>/.workbuddy/settings.json`），先备份，再读现有 `hooks` 键。
2. **写三个脚本**到 `~/.workbuddy/hooks/`：`handoff-trigger.mjs`（Stop）、
   `handoff-resume.mjs`（SessionStart）、`run-hook.sh`。各文件职责与防死循环要求见
   `references/hook-implementation.md`。
3. **注册 hooks**（合并进 settings 的 `hooks` 键，不要整体覆盖）：

```json
"hooks": {
  "Stop": [
    { "hooks": [ { "type": "command", "command": "<abs>/run-hook.sh <abs>/handoff-trigger.mjs", "timeout": 15 } ] }
  ],
  "SessionStart": [
    { "matcher": "startup|clear",
      "hooks": [ { "type": "command", "command": "<abs>/run-hook.sh <abs>/handoff-resume.mjs", "timeout": 15 } ] }
  ]
}
```

4. **校验 JSON**：用 `JSON.parse` 解析整个 settings 文件，确认其余键未丢。
5. **实测触发**：手动复现单个 hook —— `echo '<payload json>' | /bin/bash -lc '<command>'`，
   再跑一轮真实会话确认 handoff 产出与注入都发生。
6. **清理测试产物**：删掉 `hooks/state/` 与 `handoffs/` 下的测试文件。

## Decision Rules

- **触发点只能选 Stop**：它是唯一能强制模型继续的事件；**注入点只能选 SessionStart**：它的 stdout
  或 `hookSpecificOutput.additionalContext` 是唯一能进新会话上下文的位置。**PreCompact 不能当
  handoff 触发器**（stdout 只进压缩指令，exit 2 只给用户看消息）。
- **阈值调整优先级**：先看是不是被内置 50% pre-message 压缩截断 —— 开桌面端 **Max Mode**，
  或设 `CODEBUDDY_PRE_MESSAGE_COMPACT_PCT=80`；确认能到目标比例后，再调
  `handoff.config.json` 里的 handoff 阈值。
- **contextWindow 兜底顺序**：`handoff.config.json.contextWindow` 留 null 时依次取
  `settings.autoCompactWindow` -> `CODEBUDDY_AUTO_COMPACT_WINDOW` -> `200000`。
- **需要确认事件名 / matcher 语义 / hook 入参 / 阈值常量** -> 读 `references/runtime-facts.md`。
- **要写或改 hook 脚本本身** -> 读 `references/hook-implementation.md`。
- **hook 不触发 / 到不了阈值 / 搜不到字符串** -> 见 `## Troubleshooting`。

## Constraints

- 改 `~/.workbuddy/settings.json` 前先备份；改完必须 `JSON.parse` 校验，并确认其余键未丢。
- **所有 hook 脚本的异常分支都必须 `exit 0`**，否则会卡住对话。
- 每 session 只触发一次：Stop 脚本要检查 `stop_hook_active===true` 直接 return，并用
  `state/<sid>.fired` 去重。
- handoff 目录按 `sha1(cwd).slice(0,12)` 分项目，不要依赖 app 的 cwd-slug（不同版本大小写规则不一致）。
- 脚本内不写 Unicode 制表/装饰字符（会乱码）；中文可正常写。
- 测完清理 `hooks/state/` 与 `handoffs/` 下的测试产物。
- **不要用 PreCompact 做 handoff 触发**；不要把 hook 的 stdout 当成能进用户可见消息的通道。

## Priority

1. **不卡住对话**（hook 任何异常一律 `exit 0`）> "一定要触发 handoff"。
2. **先解除原生 50% 压缩**，再调 handoff 阈值 —— 否则阈值永远达不到。
3. **防死循环**（`stop_hook_active` 检查 + 每 session 一次）> 尽量多触发。

## Stop Conditions

- handoff 到不了阈值 -> 不要反复下调阈值；先确认是否被 50% 原生压缩截断、或用户没开 Max Mode。
- `grep app.asar` 搜不到目标字符串 -> 不要反复 grep；改用
  `npx @electron/asar extract <asar> <dir>` 后再 grep。
- 改完 settings 后 `JSON.parse` 失败 -> 立即还原备份，不要继续追加改动。

## Verification

交付前逐项确认：

- [ ] `~/.workbuddy/settings.json` 已备份，且 `JSON.parse` 通过、其余键未丢。
- [ ] 三个脚本就位且可执行；每个异常分支都 `exit 0`。
- [ ] Stop 注册带 `stop_hook_active` 与 `state/<sid>.fired` 双重防死循环。
- [ ] SessionStart 注册的 matcher 为 `startup|clear`，且 `source==="compact"` 时跳过。
- [ ] 实测确认：达标时产出 handoff，新会话读到且只读一次。
- [ ] `hooks/state/` 与 `handoffs/` 下的测试产物已清理。

## Output

- **Outcome**：一条可用的 handoff 门禁 —— 上下文达阈值时自动产出 handoff 文档，
  新会话（startup / clear）自动注入该文档继续干活。
- **Done when**：`## Verification` 全部勾选，且用户能在真实会话里看到 handoff 产出与注入。
- **Evidence**：settings 中新增的 `hooks` 段；`handoffs/` 下带 `<file>.consumed` 标记的文件；
  `state/<sid>.fired` 标记；一轮实测会话的前后对比。

## References

按需加载，不要预先全读：

- `references/runtime-facts.md` — 需要确认 hook 事件名、matcher 语义、hook 入参、transcript 路径
  或内置压缩阈值常量时读。
- `references/hook-implementation.md` — 要写或修改 `handoff-trigger.mjs` / `handoff-resume.mjs` /
  `run-hook.sh` 时读。

## Troubleshooting

- **hook 不触发**：确认 `~/.workbuddy/settings.json` JSON 合法、命令用绝对路径、脚本可执行。
  手动复现：`echo '<payload json>' | /bin/bash -lc '<command>'`。
- **到不了阈值**：几乎一定是被内置 50% pre-message 压缩截断 —— 开 Max Mode，或设
  `CODEBUDDY_PRE_MESSAGE_COMPACT_PCT=80`，或降低 handoff 阈值。
- **grep app.asar 搜不到字符串**：284MB 大文件直接 grep 会漏。用
  `npx @electron/asar extract <asar> <dir>`（3-4s，约 800MB）解包后再 grep。
