Pi 插件解剖|git-checkpoint.ts:只用 53 行,让 fork 恢复代码状态

一句话定位

每个 agent 回合前自动 git stash create 拍代码快照,/fork 时按历史点恢复代码的工作流增强插件。

作用(为什么存在)

它解决的是「会话历史」和「代码状态」错位的问题。 你在 pi 里 fork 一个会话到某个历史点继续对话,但工作区的代码还是最新状态------会话回到了过去,代码却没跟上。

git-checkpoint 用 53 行解决这个错位:fork 到哪个历史点,代码就恢复到哪个点的快照。

注意一个容易混淆的点:它不是 「LLM 改坏了代码,撤销一下」的 undo 工具------触发者是 /fork 操作,语义是「新分支的代码起点对齐」,不是「回退开关」。

关键信息

项 内容
源码位置 examples/extensions/git-checkpoint.ts(53 行)
核心 API on("tool_result") / on("turn_start") / on("session_before_fork") / on("agent_end") + pi.exec + ctx.sessionManager.getLeafEntry() + ctx.ui.select
插件类型 事件钩子型(生命周期)

触发流程 / 数据流

csharp 复制代码
tool_result(工具结果回传)
  → getLeafEntry() → currentEntryId = 本轮树尖 entry      ← 记落点
turn_start(下一轮开始前)
  → git stash create → 快照 ref → checkpoints.set(entryId, ref)  ← 拍快照
session_before_fork(用户 /fork 时)
  → checkpoints.get(event.entryId) → 命中则确认恢复        ← 按坐标取快照
agent_end(整轮 run 结束)
  → checkpoints.clear()                                    ← 清理

核心设计:用 pi 会话树的 entryId 当 key,把「树上的位置」和「git 里的快照」绑定 。tool_result 记落点,turn_start 用上一个落点建快照,session_before_fork 按 fork 起点查快照。

架构 / 流程

flowchart TD subgraph S1[&#34;每轮交互&#34;] TR[&#34;tool_result 事件<br/>记录 leaf.id&#34;] --> ID[&#34;存 currentEntryId&#34;] TS[&#34;turn_start 事件<br/>git stash create&#34;] --> HAS{&#34;有 ref?&#34;} HAS -- 是 --> STORE[&#34;checkpoints.set(currentEntryId, ref)&#34;] HAS -- 否 --> SKIP[&#34;跳过本轮&#34;] end subgraph S2[&#34;fork 时&#34;] BF[&#34;session_before_fork 事件&#34;] --> GET{&#34;checkpoints.get(entryId)?&#34;} GET -- 无 ref --> RET[&#34;返回,不恢复&#34;] GET -- 有 ref --> UI{&#34;有 UI?&#34;} UI -- 否 --> RET2[&#34;不自动恢复&#34;] UI -- 是 --> CH[&#34;ui.select 询问恢复?&#34;] CH -- &#34;Yes&#34; --> APPLY[&#34;git stash apply ref<br/>notify 提示&#34;] CH -- &#34;No&#34; --> KEEP[&#34;保留当前代码&#34;] end subgraph S3[&#34;收尾&#34;] AE[&#34;agent_end 事件&#34;] --> CLEAR[&#34;checkpoints.clear&#34;] end

关键代码解读

typescript 复制代码
pi.on("turn_start", async () => {
  // ① 在 LLM 改代码之前,git stash create 拍快照
  //    create 只创建对象,不写 ref、不改工作区(区别于 git stash 入栈)
  const { stdout } = await pi.exec("git", ["stash", "create"]);
  const ref = stdout.trim();
  if (ref && currentEntryId) {          // ② 有脏文件 且 有落点,才存
    checkpoints.set(currentEntryId, ref);
  }
});

pi.on("session_before_fork", async (event, ctx) => {
  const ref = checkpoints.get(event.entryId);  // ③ 按 fork 起点坐标查快照
  if (!ref) return;                             // ④ 无快照 → 静默跳过
  if (!ctx.hasUI) return;                       // ⑤ 无 UI → 保守不恢复
  const choice = await ctx.ui.select("Restore code state?", [
    "Yes, restore code to that point",
    "No, keep current code",
  ]);
  if (choice?.startsWith("Yes")) {
    await pi.exec("git", ["stash", "apply", ref]);  // ⑥ 参数数组,防注入
    ctx.ui.notify("Code restored to checkpoint", "info");
  }
});

亮点 / 踩坑

亮点 1:事件是「时机」,不是只能「拦截」。 session_before_fork 返回 undefined(不拦 fork),只利用「fork 前」这一刻挂自己的逻辑。对比上期的 permission-gate:两者都是可拦截的 before 型事件 ------tool_call 返回 {block:true} 拦截,session_before_fork 返回 undefined 挂副逻辑。事件不同,但「拦截型事件既能拦也能挂逻辑」的用法模式相同。

亮点 2:git stash create 不污染 git history。 它创建的是 dangling 对象(无 ref 指向),不会进 HEAD 历史、不会被 push。代价是对象躺在 .git 里等 gc 回收(默认 2 周 prune)------轻量且安全。

踩坑提示:检查点只覆盖「未提交(dirty)」变更。 已 commit 的代码不受管理;工作区干净时 stash create 返回空串,直接跳过。

边界(Limitations)

边界 表现
进程级 checkpoints Map 是纯内存,重启进程即失效
机器级 dangling 对象不推送,换机器 clone 后 fork 不恢复代码
覆盖范围 只对 dirty 变更生效
首次 turn 第一个 turn_start 时 currentEntryId 为 undefined,第一轮无检查点
粒度 currentEntryId 只在 tool_result 更新,纯对话轮不产生检查点

场景(Scenarios)

能恢复:同一进程内 fork 到「工具调用后的历史点」→ 命中 Map → 确认后恢复。

静默跳过 (不报错、不恢复):fork 到第一轮之前、干净工作区、无 UI(CI/headless)、agent_end 之后、重启/换机器。

可借鉴的模式

  1. 状态生命周期配对 :turn_start 建 → agent_end 清。内存状态必须有清理时机(类比 React useEffect setup/cleanup)。
  2. 事件即时机 :拦截型事件返回 undefined 也能挂副逻辑。
  3. 命令参数化防注入 :pi.exec("git", ["stash", "apply", ref]) 用数组而非字符串拼接。
  4. 快照-恢复模式:改前存快照 → 关联坐标 → 确认恢复 → 清理。可复用到任何「Agent 改工作区」的插件(自动备份/工作区保护)。
  5. 无 UI 保守降级 :!ctx.hasUI 时不擅自动作。

一句话总结

53 行把「会话树的坐标」和「git 快照」绑定起来,让 fork 具备代码恢复能力------快照-恢复模式的最小范本。

相关推荐
子兮曰5 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰5 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万5 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝5 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋5 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
1点东西5 天前
做了近两年的Agent开发,其实真正要学的就是这五件事
llm·agent·ai编程
晨米酱5 天前
AGENTS.md:Agent 的上下文策略层
面试·架构·agent
invicinble5 天前
记录一个学习技术栈的想法和思路
agent
卡布鲁5 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
染指11105 天前
122.Agent-LangChain核心组件-中间件-动态提示词(dynamic_promapt)
人工智能·langchain·agent·agents