projectkeeper 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/LICENSE +21 -0
  3. package/README.md +283 -2
  4. package/README.zh-CN.md +285 -0
  5. package/THIRD_PARTY_NOTICES.md +53 -0
  6. package/bin/pk.mjs +6 -0
  7. package/bin/postinstall.mjs +5 -0
  8. package/bin/strip-types.mjs +28 -0
  9. package/package.json +68 -4
  10. package/patches/README.md +111 -0
  11. package/patches/pi-ai-0.85.0.ts +122 -0
  12. package/patches/pi-ai-0.87.1.ts +122 -0
  13. package/scripts/apply-pi-streaming-patch.ts +86 -0
  14. package/skills/clerk/coverage/SKILL.md +35 -0
  15. package/skills/clerk/cross-check/SKILL.md +118 -0
  16. package/skills/clerk/deepen-dispatch/SKILL.md +69 -0
  17. package/skills/clerk/lane-follow-up/SKILL.md +94 -0
  18. package/skills/clerk/lane-plan/SKILL.md +107 -0
  19. package/skills/clerk/lane-slot/SKILL.md +113 -0
  20. package/skills/clerk/lane-topic/SKILL.md +116 -0
  21. package/skills/clerk/orientation/SKILL.md +160 -0
  22. package/skills/clerk/reconcile/SKILL.md +77 -0
  23. package/skills/clerk/skeleton-dispatch/SKILL.md +43 -0
  24. package/skills/clerk/spot-check/SKILL.md +52 -0
  25. package/skills/clerk/synthesis/SKILL.md +65 -0
  26. package/skills/table-follow-up/SKILL.md +42 -0
  27. package/src/cli.ts +319 -0
  28. package/src/codemap/README.md +13 -0
  29. package/src/codemap/engines.ts +102 -0
  30. package/src/codemap/facts.ts +504 -0
  31. package/src/context/agent-entry.ts +232 -0
  32. package/src/context/ask.ts +97 -0
  33. package/src/context/assemble.ts +1058 -0
  34. package/src/context/code-source.ts +103 -0
  35. package/src/context/k-briefs.ts +114 -0
  36. package/src/context/lineage.ts +100 -0
  37. package/src/context/object-brief.ts +300 -0
  38. package/src/context/owner-words.ts +191 -0
  39. package/src/context/pack-fixture.ts +342 -0
  40. package/src/context/project-rules.ts +160 -0
  41. package/src/intake/intake.ts +392 -0
  42. package/src/intake/material-rules.ts +166 -0
  43. package/src/intake/removal.ts +316 -0
  44. package/src/intake/skipped.ts +72 -0
  45. package/src/keeper/adjustment.ts +565 -0
  46. package/src/keeper/bounds/boundary.ts +509 -0
  47. package/src/keeper/bounds/command.ts +1665 -0
  48. package/src/keeper/bounds/paths.ts +164 -0
  49. package/src/keeper/bounds/reads.ts +718 -0
  50. package/src/keeper/bounds/shell-write-guard.ts +521 -0
  51. package/src/keeper/capabilities.ts +136 -0
  52. package/src/keeper/clerk-steps.ts +151 -0
  53. package/src/keeper/clerk-tools.ts +1424 -0
  54. package/src/keeper/conversation.ts +253 -0
  55. package/src/keeper/evidence.ts +662 -0
  56. package/src/keeper/fake-provider.ts +132 -0
  57. package/src/keeper/held.ts +15 -0
  58. package/src/keeper/history-tools.ts +163 -0
  59. package/src/keeper/jobs/answering.ts +53 -0
  60. package/src/keeper/key-check.ts +105 -0
  61. package/src/keeper/ledger-tools.ts +295 -0
  62. package/src/keeper/link-check.ts +185 -0
  63. package/src/keeper/merge.ts +260 -0
  64. package/src/keeper/note-origin.ts +56 -0
  65. package/src/keeper/numbers-check.ts +102 -0
  66. package/src/keeper/open-in-pi.ts +57 -0
  67. package/src/keeper/organize/absence.ts +266 -0
  68. package/src/keeper/organize/carried-on.ts +214 -0
  69. package/src/keeper/organize/clerk-coverage.ts +293 -0
  70. package/src/keeper/organize/clerk-prompts.ts +132 -0
  71. package/src/keeper/organize/clerk.ts +1395 -0
  72. package/src/keeper/organize/coverage-gate.ts +19 -0
  73. package/src/keeper/organize/coverage-tools.ts +765 -0
  74. package/src/keeper/organize/entries.ts +153 -0
  75. package/src/keeper/organize/entry-gate.ts +289 -0
  76. package/src/keeper/organize/fill-tools.ts +718 -0
  77. package/src/keeper/organize/follow-up.ts +280 -0
  78. package/src/keeper/organize/generation-check.ts +504 -0
  79. package/src/keeper/organize/graph.ts +230 -0
  80. package/src/keeper/organize/lane-tools.ts +289 -0
  81. package/src/keeper/organize/looked-tools.ts +54 -0
  82. package/src/keeper/organize/materials.ts +243 -0
  83. package/src/keeper/organize/owner-lines.ts +493 -0
  84. package/src/keeper/organize/place-tools.ts +216 -0
  85. package/src/keeper/organize/placement-inference.ts +1045 -0
  86. package/src/keeper/organize/placement-time.ts +320 -0
  87. package/src/keeper/organize/placing.ts +821 -0
  88. package/src/keeper/organize/positioning.ts +33 -0
  89. package/src/keeper/organize/readiness.ts +47 -0
  90. package/src/keeper/organize/reading.ts +401 -0
  91. package/src/keeper/organize/round-blocks.ts +65 -0
  92. package/src/keeper/organize/round-news.ts +164 -0
  93. package/src/keeper/organize/round-open.ts +586 -0
  94. package/src/keeper/organize/schedule-state.ts +39 -0
  95. package/src/keeper/organize/schedule.ts +137 -0
  96. package/src/keeper/organize/service.ts +409 -0
  97. package/src/keeper/organize/skills.ts +116 -0
  98. package/src/keeper/organize/slots.ts +85 -0
  99. package/src/keeper/organize/stage-gate.ts +175 -0
  100. package/src/keeper/organize/stage-tools.ts +471 -0
  101. package/src/keeper/organize/standing-notes.ts +141 -0
  102. package/src/keeper/organize/table-follow-up.ts +330 -0
  103. package/src/keeper/organize/takeover-clerk.ts +832 -0
  104. package/src/keeper/organize/takeover-state.ts +103 -0
  105. package/src/keeper/organize/takeover.ts +216 -0
  106. package/src/keeper/organize/update-pending.ts +535 -0
  107. package/src/keeper/organize/workbench-placement.ts +92 -0
  108. package/src/keeper/own-keys.ts +114 -0
  109. package/src/keeper/owner-text.ts +165 -0
  110. package/src/keeper/project-folder.ts +334 -0
  111. package/src/keeper/prompts.ts +101 -0
  112. package/src/keeper/references.ts +177 -0
  113. package/src/keeper/relook/compose.ts +120 -0
  114. package/src/keeper/relook/prompts.ts +36 -0
  115. package/src/keeper/roles.ts +259 -0
  116. package/src/keeper/round-tools.ts +227 -0
  117. package/src/keeper/route.ts +148 -0
  118. package/src/keeper/rules.ts +97 -0
  119. package/src/keeper/runtime.ts +1858 -0
  120. package/src/keeper/scope-tools.ts +194 -0
  121. package/src/keeper/sendback-text.ts +83 -0
  122. package/src/keeper/step-timing.ts +207 -0
  123. package/src/keeper/testing/clerk-script.ts +212 -0
  124. package/src/keeper/testing/streaming-arguments.ts +57 -0
  125. package/src/keeper/tools.ts +2340 -0
  126. package/src/keeper/utterance-tools.ts +64 -0
  127. package/src/keeper/verbatim.ts +397 -0
  128. package/src/ledger/adapters.ts +186 -0
  129. package/src/ledger/arrangements.ts +257 -0
  130. package/src/ledger/blame.ts +58 -0
  131. package/src/ledger/code-engine-child.ts +69 -0
  132. package/src/ledger/code-engine.ts +548 -0
  133. package/src/ledger/code.ts +607 -0
  134. package/src/ledger/docs.ts +294 -0
  135. package/src/ledger/git-read.ts +321 -0
  136. package/src/ledger/index.ts +1850 -0
  137. package/src/ledger/item-range.ts +16 -0
  138. package/src/ledger/lines.ts +328 -0
  139. package/src/ledger/numbering.ts +269 -0
  140. package/src/ledger/ranges.ts +74 -0
  141. package/src/ledger/rebuild-worker.ts +15 -0
  142. package/src/ledger/rebuild.ts +387 -0
  143. package/src/ledger/repo-scan.ts +181 -0
  144. package/src/ledger/schema.ts +450 -0
  145. package/src/ledger/sessions.ts +134 -0
  146. package/src/ledger/text-scan.ts +466 -0
  147. package/src/ledger/time.ts +185 -0
  148. package/src/ledger/ts-symbols.ts +180 -0
  149. package/src/ledger/views.ts +377 -0
  150. package/src/mcp/server.ts +127 -0
  151. package/src/mcp/tools.ts +187 -0
  152. package/src/model/ids.ts +23 -0
  153. package/src/model/k-types.ts +912 -0
  154. package/src/model/time.ts +85 -0
  155. package/src/model/types.ts +1382 -0
  156. package/src/model/views-k.ts +707 -0
  157. package/src/model/vocab.ts +269 -0
  158. package/src/process/analysis.ts +79 -0
  159. package/src/process/breakpoint-candidates.ts +383 -0
  160. package/src/process/breakpoints.ts +746 -0
  161. package/src/process/claim-check.ts +295 -0
  162. package/src/process/delivery-links.ts +52 -0
  163. package/src/process/facts.ts +487 -0
  164. package/src/process/generations.ts +138 -0
  165. package/src/process/index.ts +243 -0
  166. package/src/process/integration.ts +128 -0
  167. package/src/process/placement.ts +107 -0
  168. package/src/process/plan.ts +276 -0
  169. package/src/process/sendbacks.ts +244 -0
  170. package/src/process/text.ts +185 -0
  171. package/src/process/units.ts +543 -0
  172. package/src/process/unnumbered.ts +431 -0
  173. package/src/process/work.ts +884 -0
  174. package/src/scope/candidates.ts +86 -0
  175. package/src/scope/decisions.ts +229 -0
  176. package/src/scope/discover.ts +741 -0
  177. package/src/scope/ignore.ts +126 -0
  178. package/src/scope/ignored-place.ts +93 -0
  179. package/src/scope/skip.ts +161 -0
  180. package/src/scope/toolchain.ts +180 -0
  181. package/src/scope/worktree.ts +187 -0
  182. package/src/server/api.ts +549 -0
  183. package/src/server/app.ts +595 -0
  184. package/src/server/drafts-api.ts +170 -0
  185. package/src/server/folders-api.ts +243 -0
  186. package/src/server/graph-view.ts +639 -0
  187. package/src/server/http.ts +224 -0
  188. package/src/server/k-agent-api.ts +56 -0
  189. package/src/server/k-rounds-d99.ts +91 -0
  190. package/src/server/k-views.ts +405 -0
  191. package/src/server/keeper-page.ts +446 -0
  192. package/src/server/keys-api.ts +239 -0
  193. package/src/server/project-folder-api.ts +104 -0
  194. package/src/server/vendor.ts +14 -0
  195. package/src/server/workbench-content.ts +561 -0
  196. package/src/sources/anchor.ts +129 -0
  197. package/src/sources/files.ts +267 -0
  198. package/src/sources/gitobs.ts +41 -0
  199. package/src/sources/history.ts +231 -0
  200. package/src/sources/scan-worker.ts +10 -0
  201. package/src/sources/sessions/locate.ts +292 -0
  202. package/src/sources/sessions/read.ts +346 -0
  203. package/src/sources/sessions/scope.ts +42 -0
  204. package/src/sources/sessions/utterances.ts +223 -0
  205. package/src/sources/watch.ts +215 -0
  206. package/src/store/json-file.ts +50 -0
  207. package/src/store/paths.ts +39 -0
  208. package/src/store/project-store.ts +295 -0
  209. package/src/store/versions.ts +172 -0
  210. package/src/store/workspace.ts +216 -0
  211. package/src/table/check.ts +333 -0
  212. package/src/table/follow-up-tools.ts +30 -0
  213. package/src/table/follow-up.ts +344 -0
  214. package/src/table/import.ts +610 -0
  215. package/src/table/project-home.ts +61 -0
  216. package/src/table/sessions.ts +168 -0
  217. package/src/table/takeover.ts +483 -0
  218. package/src/util/git-check.ts +29 -0
  219. package/src/util/git.ts +411 -0
  220. package/src/util/own-ts.ts +15 -0
  221. package/src/util/paths.ts +225 -0
  222. package/template/FILLING-IN.md +148 -0
  223. package/template/examples/papertrail.json +747 -0
  224. package/template/projectkeeper.schema.json +1001 -0
  225. package/ui/app.js +1135 -0
  226. package/ui/failures.js +82 -0
  227. package/ui/flyout.js +120 -0
  228. package/ui/folder-chooser.js +264 -0
  229. package/ui/glossary.js +156 -0
  230. package/ui/graph-fit.js +157 -0
  231. package/ui/graph-material.js +61 -0
  232. package/ui/graph-palette.js +109 -0
  233. package/ui/graph-tools.js +62 -0
  234. package/ui/graph.js +1712 -0
  235. package/ui/index.html +31 -0
  236. package/ui/k/code-view.js +230 -0
  237. package/ui/k/draft-view.js +93 -0
  238. package/ui/k/k-views.css +224 -0
  239. package/ui/k/round-tree.js +450 -0
  240. package/ui/k/scope-k.js +159 -0
  241. package/ui/k-data.js +118 -0
  242. package/ui/k-fold.js +106 -0
  243. package/ui/k-process.css +302 -0
  244. package/ui/k-process.js +934 -0
  245. package/ui/keeper-activity.js +175 -0
  246. package/ui/keeper-view.js +801 -0
  247. package/ui/keeper.css +101 -0
  248. package/ui/key-order.js +62 -0
  249. package/ui/list-fold.js +67 -0
  250. package/ui/markdown.d.ts +21 -0
  251. package/ui/markdown.js +472 -0
  252. package/ui/placement.d.ts +92 -0
  253. package/ui/placement.js +445 -0
  254. package/ui/popover-place.d.ts +16 -0
  255. package/ui/popover-place.js +89 -0
  256. package/ui/popover.js +198 -0
  257. package/ui/project-folder.js +37 -0
  258. package/ui/shell-state.js +40 -0
  259. package/ui/strip-rows.js +171 -0
  260. package/ui/styles.css +492 -0
  261. package/ui/theme-pick.js +36 -0
  262. package/ui/theme.js +74 -0
  263. package/ui/themes/LICENSE.md +13 -0
  264. package/ui/themes/README.md +44 -0
  265. package/ui/themes/TOKENS.md +291 -0
  266. package/ui/themes/a1/board.css +1557 -0
  267. package/ui/themes/a1/pk.css +392 -0
  268. package/ui/themes/b1/board.css +1163 -0
  269. package/ui/themes/b1/pk.css +218 -0
  270. package/ui/themes/d2/board.css +518 -0
  271. package/ui/themes/d2/pk.css +243 -0
  272. package/ui/themes/guard.css +73 -0
  273. package/ui/themes/i1/board.css +776 -0
  274. package/ui/themes/i1/pk.css +223 -0
  275. package/ui/themes/manifest.json +13 -0
  276. package/ui/views.js +2796 -0
@@ -0,0 +1,285 @@
1
+ # ProjectKeeper
2
+
3
+ **AI 写代码越来越快,但你还知道它做出来的是不是你想要的吗?**
4
+
5
+ ProjectKeeper 把你说过的要求、改过的决定和 AI 实际完成的工作放在一张图上,帮你发现偏差,让下一个 agent 接得上,项目做到一半也能接手。接手由你自己的 agent 来做:照项目本来的样子读一遍,填一张表;之后一个常驻的 agent 每天跟着项目更新这张图。
6
+
7
+ - **以你说过的话为准。** 你的原话一字不改地留着,带着是在哪段会话里说的。文档和你的话对不上了、你要的东西半路没了、多出了你没要过的工作,ProjectKeeper 都会告诉你。
8
+ - **做到一半的项目,随时能交接。** 把它指向项目文件夹:文档、git 历史、代码和 agent 会话整理成一张图;一条命令,就把某项工作的上下文交给下一个 agent。
9
+
10
+ [English](README.md) · 简体中文
11
+
12
+ [![许可证:MIT](https://img.shields.io/badge/licence-MIT-blue)](LICENSE)
13
+ ![Node.js 24 及以上](https://img.shields.io/badge/node-%E2%89%A5%2024-brightgreen)
14
+ [![CI](https://github.com/feiyanglyy-stack/ProjectKeeper/actions/workflows/ci.yml/badge.svg)](https://github.com/feiyanglyy-stack/ProjectKeeper/actions/workflows/ci.yml)
15
+ ![本地运行:不用账号,不传遥测](https://img.shields.io/badge/local--first-no%20account%2C%20no%20telemetry-success)
16
+
17
+ ![一个真实项目的故事地图:最上面是 owner 的原话和产品,往下是目标和产品区域,两代旧计划收成两条,再往下是现行计划里的每一项工作](docs/images/hero-story-map.png)
18
+
19
+ *一个真实项目,一屏看完:owner 要的是什么、目标和产品区域、两代旧计划、现行计划里的工作。每个对象点开都能看到出处。*
20
+
21
+ ## 为什么要有它
22
+
23
+ 让 AI 连续开发一个软件,很容易走到这一步:对话越来越多,计划不断修改,agent 换了好几轮。每个 agent 都说完成了任务,却没人能完整说清:最初要的是什么、后来改了什么、最后到底做成了什么样。答案其实都在——在文档里、提交说明里、没人会再翻的会话记录里。
24
+
25
+ ProjectKeeper 就像一个工程档案员。它不替 agent 写代码,而是把每一次要求、每一次改动和实际完成的工作理清楚。哪里对不上,它会指出来;换了 agent,它能把来龙去脉交给下一个。
26
+
27
+ 项目做到一半才请它来也不晚:它把以往的会话、文档和提交都翻一遍,告诉你每一版要求是什么、后来改成了什么、现在做成的是哪一版。这些都在你自己的机器上完成。
28
+
29
+ ## 你能得到什么
30
+
31
+ - **整个项目在一张图上。** 你说过想要什么、目标、产品区域、计划,每一项工作各在其位。
32
+ - **每项工作到底经过了什么。** 派出、交付、审查、修复、合并,每一步都带着它依据的提交、报告或判定;Keeper 自己推断的标 `Inferred`。
33
+ - **一个决定是怎么来的。** 你的原话,一字不改,带着是在哪段会话里说的;还有把这个决定落下去的文档和提交。
34
+ - **旧计划后来怎么样了。** 上一代计划里的每一项各有去向:接着做了、挪走了、推后了,还是放弃了。
35
+ - **哪些事要你拿主意。** 需要你决定的 note 会给出几个选项;审查没过、又没人接的事(送回)可以直接复制给 agent。
36
+ - **给下一个 agent 的上下文。** 一条命令,就把某项工作的上下文交给它:这项工作为了什么、怎么走到今天、你这个项目的规矩、代码在哪。
37
+
38
+ 而且它会一直跟着:接管完之后,按你定的时间每天整理一轮,或者你按一下 `Follow up`。
39
+
40
+ ## 跑完第一遍
41
+
42
+ 第一遍读文档、git 历史、代码结构和你的 agent 会话,填出三个视图。
43
+
44
+ ![第一遍之后的 Graph 视图](docs/images/first-pass-01-story-map.png)
45
+
46
+ *Graph:最上面是你的原话和产品,横着是目标和区域,旧计划收成横条,现行计划的工作落在各自的格子里。*
47
+
48
+ ![第一遍之后的 List 视图,展开了一个区域](docs/images/first-pass-02b-list-module-open.png)
49
+
50
+ *List:展开一个区域。左边是计划了什么,右边是实际发生了什么——进行中、在哪个提交里合并了、交付了但没有检查记录。*
51
+
52
+ ![第一遍之后的 Code 视图](docs/images/first-pass-03-code-territories.png)
53
+
54
+ *Code:哪块代码属于哪个产品区域、是哪些工作做出来的、有多少测试、现行计划还用不用它。*
55
+
56
+ **实测:** 这一遍用 DeepSeek 跑了大约 50 分钟,花了大约 11 元人民币(约 1.5 美元)。被整理的项目就是 ProjectKeeper 自己的工作仓库:文档加代码大约 1,000 个文件(约 470 份 Markdown 文档、400 个源码文件,约 21.7 万行),大约 750 次提交,22 段 agent 会话。
57
+
58
+ ## 加深之后
59
+
60
+ 加深是带着问题去挖历史,并且自己查一遍自己的结论。同一个项目上,加深又用了 71 分钟。
61
+
62
+ ![一项工作,连同它的步骤和证据](docs/images/deepened-02-work-item-steps-and-evidence.png)
63
+
64
+ *一项工作,下面挂着它的步骤——检查、派出、交付、合并;旁边是它改过的代码,以及它经过的提交和审查判定。*
65
+
66
+ ![一个区域的需求、设计和决定,以及其中一个决定的来历](docs/images/deepened-01-intent-chain-and-where-a-decision-came-from.png)
67
+
68
+ *一个决定是怎么来的:一个区域的需求、设计和决定列在一起;点开其中一条,能看到它是哪天定的、哪些提交把它落了下去。*
69
+
70
+ ![上一代计划,每一项划掉,旁边是接着做它的现行工作](docs/images/deepened-03-earlier-generation-what-its-items-became.png)
71
+
72
+ *旧计划后来怎么样了:上一代的每一项都划掉了,旁边写着现在是哪一项接着在做。*
73
+
74
+ ![一条要 owner 拿主意的 note,带选项](docs/images/deepened-05-note-for-the-owner-with-options.png)
75
+
76
+ *要你拿主意的 note:问的是什么、Keeper 怎么看、为什么要紧、三个选项各自会带来什么,以及带出处的事实。*
77
+
78
+ ![每一轮、每个模型的用量](docs/images/deepened-06b-keeper-usage-by-model.png)
79
+
80
+ *每一轮用了多少、哪个模型承担了哪些步骤。图上的美元数是按标价估的,这一轮实际扣的钱比它少(见[跑一轮要花多少](#跑一轮要花多少))。*
81
+
82
+ ![从地图到一项工作,到它的步骤,再到它改的代码](docs/images/map-to-work-item-to-steps-to-code.gif)
83
+
84
+ *从地图,到一项工作,到它经过了什么,再到它改了哪些代码。*
85
+
86
+ ## 不用 key 先看看
87
+
88
+ ```sh
89
+ npm run demo
90
+ ```
91
+
92
+ 它会生成一个虚构的项目——Papertrail,一个人用的稍后读清单——已经整理好,在 `http://127.0.0.1:4880/` 打开。不需要 key,不调模型,不联网;demo 里 Keeper 的回答是预先写好的。它写的东西都在系统临时目录下的一个文件夹里。
93
+
94
+ ## 四套界面
95
+
96
+ 工作台带四套主题,在顶栏的 `Theme` 里切换。下面是 demo 项目在每套主题下的样子。
97
+
98
+ <table>
99
+ <tr>
100
+ <td width="50%"><img src="docs/images/theme-a1-walnut-workshop.png" alt="demo 项目的 Graph,Walnut Workshop 主题"><br><b>Walnut Workshop</b>(默认)</td>
101
+ <td width="50%"><img src="docs/images/theme-b1-peacock-and-brass.png" alt="demo 项目的 Graph,Peacock &amp; Brass 主题"><br><b>Peacock &amp; Brass</b></td>
102
+ </tr>
103
+ <tr>
104
+ <td width="50%"><img src="docs/images/theme-d2-warm-clay.png" alt="demo 项目的 Graph,Warm Clay 主题"><br><b>Warm Clay</b></td>
105
+ <td width="50%"><img src="docs/images/theme-i1-task-console.png" alt="demo 项目的 Graph,Task Console 主题"><br><b>Task Console</b></td>
106
+ </tr>
107
+ </table>
108
+
109
+ 同一个列表里的 `Factory` 是不带主题的内置外观。
110
+
111
+ ## 快速开始
112
+
113
+ 需要 Node.js 24 及以上,以及 git;系统是 Windows 或 macOS。在 Mac 上,git 随 Xcode 命令行工具安装(`xcode-select --install`);Homebrew 的 `node` 比这里测试过的 24 要新:见 [Mac 上需要什么](docs/getting-started.md#on-a-mac)。
114
+
115
+ ```sh
116
+ git clone https://github.com/feiyanglyy-stack/ProjectKeeper.git && cd ProjectKeeper
117
+ npm ci
118
+ npm start
119
+ ```
120
+
121
+ 也可以不克隆,直接从 npm 装:`npm install -g projectkeeper`,然后 `projectkeeper serve`;本页写 `npx pk` 的地方,换成 `projectkeeper`。
122
+
123
+ 然后接手你的项目,有两种方式。
124
+
125
+ **用你自己的 agent(推荐)。** Claude Code、Codex 或你在用的其他 agent 读项目,填那张表:
126
+
127
+ ```sh
128
+ npx pk takeover --project <你的项目文件夹> --prepare
129
+ ```
130
+
131
+ 它会在 ProjectKeeper 自己的目录里(不会写进你的项目)摆好三个文件夹,各附一份说明:
132
+ - 一个给只读你在以往会话里说过的话的 agent;
133
+ - 一个给只读项目副本的 agent;
134
+ - 一个给把两边合起来的 main agent。
135
+
136
+ 用你的 agent 把这三步跑完,再照命令打印出的那条 `pk import` 把表导进来。用 Codex 的话,把 `--prepare` 换成 `--run codex`,三步会自己跑完,表核对无误就自动导进来。然后打开 `http://127.0.0.1:4870/` 就能看到。agent 怎么填这张表:见[模板](template/FILLING-IN.md)。
137
+
138
+ **用 Keeper 和你的 key。** 打开 `http://127.0.0.1:4870/`,然后点四下:
139
+
140
+ 1. **Add project**——起个名字,填上项目所在的文件夹(直接输入路径,或点 `Browse…` 从列表里选)。这时什么都还没开始跑。
141
+ 2. **加一把 key**——在随后打开的 Keeper 页上,`Model provider` 里:选服务商,粘贴 key,`Save`。
142
+ 3. **选一个深度**——`Full`、`Focused` 或 `First picture only`。
143
+ 4. **Start。**
144
+
145
+ 先出第一份可用的全貌;选了加深的话,接着做加深。细节见 [Getting started](docs/getting-started.md)。
146
+
147
+ 不管用哪种方式接手,日常更新都要一把 key:项目变了,Keeper 用它把图跟着更新。
148
+
149
+ ## 模型与 key
150
+
151
+ key 你自己带。底下的模型库([pi](https://github.com/earendil-works/pi))列了四十来家服务商,我们实际跑过的是 **DeepSeek**(`deepseek-v4-pro`、`deepseek-flash`)和**智谱 GLM**(`glm-5.3`、`glm-5.3-flash`)。你保存的 key 只留在本机 ProjectKeeper 自己的文件夹里,保存之后界面上不会再显示。一轮里的每一步可以各用各的模型;一把 key 额度用完了,备用的 key 会接上:见 [Models and keys](docs/models-and-keys.md)。
152
+
153
+ **订阅登录:** Claude 的 Pro、Max 订阅跑不了 Keeper:Anthropic 只许在 Claude Code 和 claude.ai 里用这种登录,请改用 Claude Console 的 API key。ChatGPT(Codex)、Kimi Coding、GitHub Copilot 的订阅能经 pi 的登录接上,都还没实测:见 [Subscriptions](docs/models-and-keys.md#subscriptions)。
154
+
155
+ ## 跑一轮要花多少
156
+
157
+ **用你自己的 agent 接手**,花的是那个 agent 自己的订阅额度。在 ProjectKeeper 自己的工作仓库上,Codex 跑完三步约 27 分钟:
158
+ - 读原话和读项目两路同时跑,约 18 分钟;
159
+ - main agent 再跑 8 分钟。
160
+
161
+ 填出来的表有:你要的事 75 件,决定 33 条加边界 7 条,工作 36 项,你和项目对不上的地方 18 处。
162
+
163
+ **每天的跟进**用你的 key;没有新东西的日子不调用模型。
164
+
165
+ **由 Keeper 接手**(第一遍加一次完整加深),同一个项目一共实测了四次:
166
+
167
+ | 模型搭配 | 用时 | 花费 |
168
+ |---|---|---|
169
+ | DeepSeek:全用 flash,最后的抽查用 pro | 126 分钟 | 实扣 29 元(约 4 美元) |
170
+ | DeepSeek:主 agent 和加深的子 agent 用 pro,其余用 flash | 122 分钟 | 实扣 57 元(约 8 美元) |
171
+ | 智谱 GLM-5.3 做主 agent,其余用 GLM-5.3-flash | 198 分钟 | 按标价 19 美元 |
172
+ | 全用智谱 GLM-5.3 | 200 分钟 | 按标价 67 美元 |
173
+
174
+ 这是同一个项目上的四次运行,只能粗略横向比一比,不是评测。几次运行之间程序改过(两次 GLM 是较早的版本);全 flash 那一次的主 agent,还在它整理的项目里读到了上一次运行的问题清单。
175
+
176
+ - **只跑第一遍**,用 DeepSeek:大约 50 分钟;pro 做主 agent 约 11 元,全用 flash 约 10 元。
177
+ - **GLM 如果用的是 Coding Plan 订阅**,实际花费远低于标价。按维护者自己的说法:GLM-5.3 跑第一遍,大约用掉团队初级成员 5 小时额度的一半;加深大约用掉一个 5 小时额度;每天的 follow up 基本看不出额度变化;全用 flash 的话,整个跑完大约是一个 5 小时额度的三分之一。
178
+ - **Usage 页上的金额是估算。** 它拿记录下来的 token 数去乘一张标价表;用 pro 的那次 DeepSeek 运行,页面显示 14.48 美元,账户实际扣了 57 元。
179
+
180
+ 每一次跑对了什么、漏了什么:见 [Cost and quality](docs/cost-and-quality.md)。
181
+
182
+ ## 它不做什么,以及隐私
183
+
184
+ - **它不改你项目里的文件。** 它只写自己的文件夹(`~/.projectkeeper`)。在你的项目里,它只写一个属于它自己的文件夹,而且要你先授权。
185
+ - **只监听 `127.0.0.1`。** 不用账号,自己也不传任何遥测。
186
+ - **会离开你机器的东西**,是发给你所选模型服务商的内容:提示词里带着你项目的文字——文档、代码、提交说明,以及你在 agent 会话里说过的话。
187
+ - **它不写代码,不跑你的测试,也不管工单。** 它只读,然后把读到的告诉你,每句话都带出处。
188
+
189
+ 更多见 [Privacy](docs/privacy.md)。
190
+
191
+ ## 给你的 agent:`pk`
192
+
193
+ 工作台开着的时候,agent 可以用一条命令读到同一份全貌。下面是 demo 项目上两段真实的输出,`…` 处有省略:
194
+
195
+ ```text
196
+ $ pk context --work T-18
197
+ # Context for Incoming agent · Work: T-18 Search index incremental rebuild 增量重建 · Kind: Implement
198
+ …
199
+ ## Owner's words
200
+ What the owner said that this work traces up to, word for word:
201
+ …
202
+ - `ow_search_fast` · the owner, 2026-09-16 [2]:
203
+ “搜索要在一秒内出结果,不然我不会用”
204
+ …
205
+ ## Open problems
206
+ - The receipt claims a one-minute background refresh; the code does not have it.
207
+ - Is the one-minute refresh claim measured anywhere?
208
+ …
209
+ ```
210
+
211
+ ```text
212
+ $ pk get sb_search_refresh
213
+ # Send-back · sb_search_refresh
214
+ Where: Search index incremental rebuild 增量重建 (thread_search_index)
215
+ What: Batch 2 says the index refreshes every minute; the review could not confirm it from the code, and batch 2 was signed off anyway.
216
+ Send back to: Work · Reopen T-18: make the index refresh in the background, or correct the receipt.
217
+ Status: Suggested · Open
218
+ …
219
+ ```
220
+
221
+ 用 MCP 接工具的客户端(Claude Desktop、Cursor 等)可以用 `pk mcp`:它把同样这几条命令做成一个本地 MCP 服务,配置见 [As an MCP server](docs/pk.md#as-an-mcp-server)。
222
+
223
+ 全部用法,以及怎样对着 demo 跑这些命令:见 [The `pk` command](docs/pk.md)。
224
+
225
+ ## 它是怎么工作的
226
+
227
+ ```mermaid
228
+ flowchart LR
229
+ subgraph P["你的项目,照原样读"]
230
+ D["文档与计划"]
231
+ G["Git 历史"]
232
+ C["代码"]
233
+ S["Agent 会话"]
234
+ end
235
+ subgraph T["接手:由你自己的 agent 来做"]
236
+ O["你的原话,从会话里读"]
237
+ R["项目,从副本里读"]
238
+ M["main agent:合并两边,对不上的地方写 note"]
239
+ O --> M
240
+ R --> M
241
+ end
242
+ subgraph K["ProjectKeeper,在你的机器上"]
243
+ L["账本:程序记下有什么、各在什么时候"]
244
+ C2["pk check、pk import:拿项目核对这张表"]
245
+ U["日常:Keeper 在这张表上接着更新"]
246
+ end
247
+ P --> L
248
+ P --> T
249
+ M --> C2
250
+ C2 --> U
251
+ K --> W["浏览器里的工作台"]
252
+ K --> A["pk,给你的 agent"]
253
+ ```
254
+
255
+ 先由程序把「有什么、各在什么时候」记成账本:每次提交、每个文档版本、每个编号、每段会话。这一步不用模型。接着由你自己的 agent 用它自己的方式读项目,填好一张表。程序拿项目核对这张表:每个编号、每处出处、每句原话都要逐字对上,然后画出来。之后项目每有变化,Keeper 就在同一张表上接着更新,照样过同一道核对。程序数得清、查得了的,都交给程序;模型的注意力留给判断。
256
+
257
+ Keeper 也能用你的 key,自己分轮次接手一个项目。详见 [How the Keeper works](docs/how-the-keeper-works.md)。
258
+
259
+ ## 和同类工具比
260
+
261
+ 画代码结构图的工具(比如 Understand-Anything)告诉你代码之间怎么连;ProjectKeeper 画的是意图和工作——想要什么、计划了什么、做了什么——代码挂在它服务的产品区域下面。会话记忆类工具(比如 claude-mem)从装上那天起记录 agent 做的事;ProjectKeeper 接手的是已经存在的一切,连同历史。规格和任务类工具(比如 OpenSpec、Backlog.md)要你和 agent 按它们的格式来写;ProjectKeeper 不要你多写任何东西。它读项目里本来就有的材料,整理出来给人翻,也给 agent 查。
262
+
263
+ ## 现状与局限
264
+
265
+ - **v1.0。** 它会出错,同一个项目每次跑出来也不完全一样;每句话都连着出处,你可以自己核对。四次实测以及各自漏了什么,见 [Cost and quality](docs/cost-and-quality.md)。
266
+ - **Windows 和 macOS。** ProjectKeeper 是在 Windows 上做出来的,也每天在 Windows 上用。它也能在 macOS 上运行:测试套件在 GitHub 的 macOS 运行器上通过,但还没有人在 Mac 上日常用过,难免有粗糙的地方——欢迎告诉我们它在你的 Mac 上表现如何。Linux 暂不支持。
267
+ - **Mac 上的文件名。** 只差大小写的两个名字,或者只差 Unicode 写法的两个名字(带重音的字母、韩文、日文假名的合成与分解两种形式),ProjectKeeper 都当作同一个名字,和 Mac 常用的卷一样。如果卷被格式化成区分大小写的,这样的两个文件会被当成一个。
268
+ - **路径很长的项目。** 项目里再深的文件都能读。但在 Windows 上,项目自身的路径有上限,git 和 Windows 的长路径设置都解除不了:超过 246 个字符,git 打不开这个仓库(文件照读,历史读不到,`Project scope` 里会写明);超过 251 个字符,Keeper 无法在这个目录里工作。请把这样的项目挪到短一些的路径下。
269
+ - **会话只读 Claude Code 和 Codex 的。** 其他 agent 的会话暂时不读。
270
+ - **界面是英文的,内容跟着你项目的语言走。** 截图来自一个中文项目。
271
+ - **名字。** ProjectKeeper 开发期间的名字是 ContextKeeper;截图里它整理的正是它自己的项目,那个项目用的还是这个名字。
272
+
273
+ ## 文档
274
+
275
+ 文档目前只有英文:[Getting started](docs/getting-started.md) · [Concepts](docs/concepts.md) · [Models and keys](docs/models-and-keys.md) · [How the Keeper works](docs/how-the-keeper-works.md) · [Cost and quality](docs/cost-and-quality.md) · [The `pk` command](docs/pk.md) · [Privacy](docs/privacy.md) · [FAQ](docs/faq.md) · [Architecture](docs/architecture.md)
276
+
277
+ ## 参与
278
+
279
+ 欢迎提 issue 和 pull request:见 [CONTRIBUTING.md](CONTRIBUTING.md)。版本变化记在 [CHANGELOG.md](CHANGELOG.md)。
280
+
281
+ ## 许可证
282
+
283
+ MIT,见 [LICENSE](LICENSE)。`ui/themes/` 下的主题不在这份许可证之内:见 [ui/themes/LICENSE.md](ui/themes/LICENSE.md)。第三方材料:[THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)。
284
+
285
+ 使用 [Claude Code](https://claude.com/claude-code) 构建。
@@ -0,0 +1,53 @@
1
+ # Third-party notices
2
+
3
+ ProjectKeeper's own code is under the MIT licence (see [LICENSE](LICENSE)). This file lists the third-party material
4
+ that is redistributed inside this repository. Packages that npm installs (`package.json`, `package-lock.json`) are not
5
+ redistributed here; each carries its own licence in `node_modules`.
6
+
7
+ ## pi-ai (`@earendil-works/pi-ai`), versions 0.87.1 and 0.85.0 — MIT
8
+
9
+ Where: `patches/pi-ai-0.87.1.ts` and `patches/pi-ai-0.85.0.ts`.
10
+
11
+ These two files describe the changes ProjectKeeper makes to the installed copy of pi-ai (`patches/README.md` says what
12
+ the changes do). To find the places to change, each file quotes short passages of pi-ai's distributed JavaScript
13
+ (`dist/utils/json-parse.js`, `dist/utils/validation.js` and six files under `dist/api/`) next to the text that replaces
14
+ them. Those quoted passages are pi-ai's, used under its licence:
15
+
16
+ - Package: `@earendil-works/pi-ai`, from <https://github.com/earendil-works/pi> (`packages/ai`)
17
+ - Licence: MIT
18
+
19
+ ```
20
+ MIT License
21
+
22
+ Copyright (c) 2025 Mario Zechner
23
+
24
+ Permission is hereby granted, free of charge, to any person obtaining a copy
25
+ of this software and associated documentation files (the "Software"), to deal
26
+ in the Software without restriction, including without limitation the rights
27
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28
+ copies of the Software, and to permit persons to whom the Software is
29
+ furnished to do so, subject to the following conditions:
30
+
31
+ The above copyright notice and this permission notice shall be included in all
32
+ copies or substantial portions of the Software.
33
+
34
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
37
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
40
+ SOFTWARE.
41
+ ```
42
+
43
+ ## The themes under `ui/themes/` — not open source
44
+
45
+ The four themes (`a1`, `b1`, `d2`, `i1`) were made for another product, WorkflowKeeper, by the same author, and are
46
+ shipped with ProjectKeeper as they are. They are not covered by the MIT licence: see
47
+ [ui/themes/LICENSE.md](ui/themes/LICENSE.md).
48
+
49
+ ## Colour-vision simulation matrices
50
+
51
+ `scripts/palette-check.py` (a development check, not part of the running product) uses the protanopia and deuteranopia
52
+ simulation matrices published in Machado, Oliveira and Fernandes, "A Physiologically-based Model for Simulation of Color
53
+ Vision Deficiency", IEEE Transactions on Visualization and Computer Graphics, 2009.
package/bin/pk.mjs ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ // pk, also installed as projectkeeper — the ProjectKeeper command (src/cli.ts says what it does). This file is plain
3
+ // JavaScript so that it runs wherever npm puts it: from a clone, after `npm link`, through `npm exec` / `npx`, or installed as a package — where Node will
4
+ // not strip types under node_modules by itself, and bin/strip-types.mjs does it for ProjectKeeper's own sources.
5
+ if (import.meta.url.includes('/node_modules/')) await import('./strip-types.mjs');
6
+ await import('../src/cli.ts');
@@ -0,0 +1,5 @@
1
+ // After `npm ci` / `npm install`: apply ProjectKeeper's patch to the installed pi-ai (patches/README.md). Plain
2
+ // JavaScript for the same reason as bin/pk.mjs: installed as a package, the script it runs sits under node_modules.
3
+ if (import.meta.url.includes('/node_modules/')) await import('./strip-types.mjs');
4
+ const { applyPiStreamingPatch, label } = await import('../scripts/apply-pi-streaming-patch.ts');
5
+ console.log(`${label}:`, await applyPiStreamingPatch());
@@ -0,0 +1,28 @@
1
+ // Node runs ProjectKeeper's TypeScript sources as they are, by stripping the types — except for files under a
2
+ // node_modules directory, which it refuses. So when ProjectKeeper is installed as a package (not run from a clone),
3
+ // this module does the same stripping for ProjectKeeper's own .ts files, and for nothing else. It is loaded by
4
+ // bin/pk.mjs and bin/postinstall.mjs, and handed on to the worker thread and the child process ProjectKeeper starts
5
+ // (src/util/own-ts.ts). In a clone it is never loaded.
6
+ import * as nodeModule from 'node:module';
7
+ import { readFileSync } from 'node:fs';
8
+ import { fileURLToPath } from 'node:url';
9
+
10
+ const root = new URL('../', import.meta.url).href;
11
+ const own = (url) => url.startsWith(root) && !url.slice(root.length).includes('node_modules/') && url.endsWith('.ts');
12
+
13
+ if (typeof nodeModule.registerHooks !== 'function' || typeof nodeModule.stripTypeScriptTypes !== 'function') {
14
+ throw new Error(`ProjectKeeper needs Node.js 24 or later to run from an installed package (this is ${process.version}).`);
15
+ }
16
+ // Node warns once that stripTypeScriptTypes is experimental; from a clone the same stripping runs without a word, so
17
+ // an installed command says nothing about it either. Every other warning goes through.
18
+ const emitWarning = process.emitWarning;
19
+ process.emitWarning = function (warning, ...rest) {
20
+ if (String(warning?.message ?? warning).includes('stripTypeScriptTypes')) return;
21
+ return emitWarning.call(this, warning, ...rest);
22
+ };
23
+ nodeModule.registerHooks({
24
+ load(url, context, nextLoad) {
25
+ if (!own(url)) return nextLoad(url, context);
26
+ return { format: 'module', shortCircuit: true, source: nodeModule.stripTypeScriptTypes(readFileSync(fileURLToPath(url), 'utf8')) };
27
+ },
28
+ });
package/package.json CHANGED
@@ -1,6 +1,70 @@
1
1
  {
2
2
  "name": "projectkeeper",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0",
4
+ "description": "A resident agent that reads a software project as it is — documents, git history, code and agent sessions — and keeps a browsable picture of what was planned, decided and done. Local-first; bring your own model key.",
5
+ "keywords": [
6
+ "ai-agents",
7
+ "coding-agents",
8
+ "project-context",
9
+ "context-engineering",
10
+ "project-management",
11
+ "developer-tools",
12
+ "claude-code",
13
+ "codex",
14
+ "local-first",
15
+ "project-graph",
16
+ "decision-records"
17
+ ],
18
+ "homepage": "https://github.com/feiyanglyy-stack/ProjectKeeper#readme",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/feiyanglyy-stack/ProjectKeeper.git"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/feiyanglyy-stack/ProjectKeeper/issues"
25
+ },
26
+ "license": "MIT",
27
+ "type": "module",
28
+ "engines": {
29
+ "node": ">=24"
30
+ },
31
+ "bin": {
32
+ "projectkeeper": "bin/pk.mjs",
33
+ "pk": "bin/pk.mjs"
34
+ },
35
+ "files": [
36
+ "bin/",
37
+ "src/",
38
+ "!src/**/*.test.ts",
39
+ "!src/**/*.test-helpers.ts",
40
+ "ui/",
41
+ "skills/",
42
+ "template/",
43
+ "patches/",
44
+ "scripts/apply-pi-streaming-patch.ts",
45
+ "THIRD_PARTY_NOTICES.md",
46
+ "CHANGELOG.md"
47
+ ],
48
+ "mcpName": "io.github.feiyanglyy-stack/projectkeeper",
49
+ "scripts": {
50
+ "postinstall": "node bin/postinstall.mjs",
51
+ "start": "node src/cli.ts serve",
52
+ "demo": "node scripts/demo.ts",
53
+ "test": "node --test \"src/**/*.test.ts\"",
54
+ "typecheck": "tsc --noEmit -p tsconfig.json"
55
+ },
56
+ "dependencies": {
57
+ "@colbymchenry/codegraph": "1.6.0",
58
+ "@earendil-works/pi-agent-core": "0.87.1",
59
+ "@earendil-works/pi-ai": "0.87.1",
60
+ "@earendil-works/pi-coding-agent": "0.87.1",
61
+ "cytoscape": "3.34.3",
62
+ "cytoscape-dagre": "4.0.1",
63
+ "dagre": "0.8.5",
64
+ "typebox": "1.3.7",
65
+ "typescript": "5.9.3"
66
+ },
67
+ "devDependencies": {
68
+ "@types/node": "24.13.5"
69
+ }
70
+ }
@@ -0,0 +1,111 @@
1
+ # pi-ai patches: streaming argument repair and complete arguments only
2
+
3
+ `npm ci` / `npm install` runs `scripts/apply-pi-streaming-patch.ts` (through `bin/postinstall.mjs`). The patch targets
4
+ pi-ai **0.87.1** in every lockfile installation location (currently two copies); where ProjectKeeper is installed as a
5
+ package and has no lockfile of its own, in the copy pi-coding-agent loads and the copy ProjectKeeper itself would load.
6
+ It checks package versions, whole-file SHA-256 hashes and unique replacement contexts
7
+ before writing any file. Re-running is safe. A changed package or unexpected source
8
+ fails installation with an explicit error; upgrading pi requires reviewing this patch.
9
+ An installation with `--ignore-scripts` must run the postinstall command explicitly.
10
+
11
+ The patch has two parts, added one after the other. In the patched files their comments
12
+ are marked `ProjectKeeper AO` (delta parsing) and `ProjectKeeper AX` (complete arguments
13
+ only); the letters are only the names of the markers.
14
+
15
+ Each file's replacements apply in order, and a later generation only appends its own.
16
+ An installation that an earlier generation patched (for example delta parsing only) is
17
+ therefore still recognised: the script undoes whatever replacements it finds, accepts the result
18
+ only if it has the upstream hash, and applies the whole current list. So after pulling
19
+ a newer patch, `node scripts/apply-pi-streaming-patch.ts` (or `npm ci`) brings an
20
+ existing `node_modules` up to date. A file changed any other way is refused; `npm ci`
21
+ restores the upstream files.
22
+
23
+ `pi-ai-0.85.0.ts` is retained as the exact patch manifest for a 0.85.0 rollback.
24
+ The installer and baseline benchmark use only `pi-ai-0.87.1.ts`; the older
25
+ manifest is not applied to a 0.87.1 installation.
26
+
27
+ ## Delta parsing (marker `AO`)
28
+
29
+ Only tool argument **delta** parsing changes: each tool-call object has a weakly held
30
+ last parsed length. After its first snapshot, parse again at >= 1,024 characters and
31
+ at least double that length thereafter. Other deltas retain the last valid snapshot
32
+ and still emit the complete raw delta. Total prefix length processed is O(N), instead
33
+ of O(N²/k). State is per object, so streams and interleaved tool calls cannot mix.
34
+ No scratch state is added to tool arguments or session files.
35
+
36
+ ## Complete arguments only (marker `AX`): a tool call runs only on arguments that arrived as complete JSON
37
+
38
+ Upstream, every native provider parses the complete buffer at tool end with
39
+ `parseStreamingJson`, which falls back to `partial-json` when the buffer does not
40
+ parse. That fallback completes a cut-off buffer into a shorter value that can pass the
41
+ tool's validation and run: a check run saw 103 characters of markdown saved as 15, with
42
+ the job `Done`; with the built-in `write` it would write a truncated file.
43
+
44
+ This part replaces those final parses with `finalizeStreamingToolArguments` (in
45
+ `dist/utils/json-parse.js`). It parses with upstream's `parseJsonWithRepair`, so raw
46
+ control characters and invalid escapes are repaired exactly as before, and an empty
47
+ buffer is still `{}`. When that fails — the text ends inside a string or before its
48
+ brackets close, or anything else keeps it from being one JSON value — the call is
49
+ marked with `Symbol.for("projectkeeper.pi-ai.incompleteToolArguments")`. The
50
+ `partial-json` value is still stored as the arguments, so the Keeper view and the
51
+ replayed conversation show what arrived; a symbol key is never serialized into session
52
+ files or provider requests.
53
+
54
+ `validateToolArguments` (in `dist/utils/validation.js`) refuses a marked call before
55
+ it validates anything. pi-agent-core validates every tool call through it, whatever
56
+ the tool — the built-ins, ProjectKeeper's `pk_*` tools, an extension's tools — and
57
+ before any `tool_call` hook, so nothing runs. The refusal is the tool result the model
58
+ gets, and it leads with what the model must do:
59
+
60
+ > Tool call "write" was not executed: its arguments did not arrive as complete JSON,
61
+ > so they may have been cut off. Nothing was run; send the call again with the complete
62
+ > arguments. (200 characters arrived; Unterminated string in JSON at position 200)
63
+
64
+ The Keeper records the refused call as a step, with this text as its summary.
65
+
66
+ Covered final parses: OpenAI Completions, Anthropic Messages, Bedrock Converse, Mistral,
67
+ and the Responses shared implementation (Azure/Codex), including its second parse at
68
+ `output_item.done`, which decides whether the call stays marked. Pi Messages receives
69
+ arguments its upstream already finalized; Google adapters receive structured arguments.
70
+ Neither has a final parse of its own to change. A reply cut off at the output limit
71
+ (`length`) is refused by pi-agent-core itself, for all its tool calls.
72
+
73
+ ## Shared notes
74
+
75
+ The assistant-message frame utility's checkpoint/replay/catch-up logic is separate
76
+ from provider argument streaming and is unchanged. This patch does not promise a
77
+ fixed latency bound for arbitrarily large single JSON values or transport bursts.
78
+ Existing source maps still describe upstream code; line numbers after insertions shift.
79
+
80
+ ProjectKeeper's runtime forwards `text_delta` only; it reads tool arguments at
81
+ `tool_execution_start`, after finalization. It does not consume `toolcall_delta`
82
+ argument snapshots. Public pi interfaces and session formats are unchanged.
83
+
84
+ Local-only reproduction (no model keys or sessions):
85
+
86
+ ```sh
87
+ node scripts/benchmark-tool-stream.ts --baseline
88
+ node scripts/benchmark-tool-stream.ts
89
+ node --test src/keeper/streaming-arguments.test.ts src/keeper/pi-streaming-patch.test.ts src/keeper/long-output-guards.test.ts src/qc/long-output.test.ts
90
+ ```
91
+
92
+ The benchmark enters through coding-agent's `ModelRuntime`, the same provider-composition
93
+ path as the Keeper service, with an in-memory empty credential store and a synthetic key.
94
+ That unbundled SDK resolves its nested pi-ai installation; postinstall patches that copy
95
+ and the root copy discovered in the lockfile. coding-agent's prebuilt CLI bundle is a
96
+ different executable path and is not loaded by the ProjectKeeper service or changed here
97
+ (so native pi, as opened by `Open in pi`, keeps upstream behaviour).
98
+ The baseline command restores original pi-ai modules **in memory in its own process**;
99
+ it does not change installed dependencies. The benchmark uses 20-character SSE deltas
100
+ and a 10 ms heartbeat; reported latency is excess over that interval.
101
+ The 200 KB baseline can take about ten seconds; the ordinary regression test uses
102
+ 100 KB and takes a fraction of a second when patched. It allows 1,500 ms heartbeat
103
+ delay to leave ample margin for slower machines and the parallel full test suite.
104
+
105
+ Investigated npm tarballs: 0.85.0, 0.85.1, 0.86.0, 0.86.1, 0.87.0, 0.87.1. All have the
106
+ same `dist/utils/json-parse.js` SHA-256:
107
+ `824fa2bf05b37d65b105ac8c07172d19f22f56a4ac66fbffba943933e4fb6d62`.
108
+ Their provider delta sites still reparse accumulated arguments.
109
+ The [0.86.0 changelog](https://github.com/earendil-works/pi/blob/v0.87.0/packages/ai/CHANGELOG.md)
110
+ fix for quadratic `EventStream` draining addresses a different path. No upstream
111
+ issue or pull request was submitted for this patch.