@gavin-hu/my-pi-agent 0.18.0 → 0.20.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 (324) hide show
  1. package/README.md +66 -52
  2. package/README.zh-CN.md +56 -46
  3. package/docs/extensions/README.md +34 -0
  4. package/docs/extensions/board.md +507 -0
  5. package/{extensions/bytefly/README.md → docs/extensions/bytefly.md} +774 -574
  6. package/{extensions/check/README.md → docs/extensions/check.md} +83 -68
  7. package/{extensions/context/README.md → docs/extensions/context.md} +40 -23
  8. package/docs/extensions/file-browse.md +561 -0
  9. package/{extensions/file-open/README.md → docs/extensions/file-open.md} +31 -23
  10. package/{extensions/file-read/README.md → docs/extensions/file-read.md} +47 -55
  11. package/{extensions/goal/README.md → docs/extensions/goal.md} +115 -131
  12. package/{extensions/job/README.md → docs/extensions/job.md} +62 -52
  13. package/{extensions/memory/README.md → docs/extensions/memory.md} +71 -60
  14. package/{extensions/plan/README.md → docs/extensions/plan.md} +440 -322
  15. package/{extensions/question/README.md → docs/extensions/question.md} +54 -31
  16. package/{extensions/rewind/README.md → docs/extensions/rewind.md} +115 -122
  17. package/docs/extensions/session-bus.md +298 -0
  18. package/{extensions/subagent/README.md → docs/extensions/subagent.md} +165 -118
  19. package/docs/extensions/team.md +254 -0
  20. package/{extensions/todo/README.md → docs/extensions/todo.md} +85 -88
  21. package/{extensions/watch/README.md → docs/extensions/watch.md} +54 -36
  22. package/{extensions/web-access/README.md → docs/extensions/web-access.md} +93 -50
  23. package/{extensions/wechat/README.md → docs/extensions/wechat.md} +260 -163
  24. package/{extensions/worktree/README.md → docs/extensions/worktree.md} +137 -79
  25. package/extensions/board/commands.ts +105 -0
  26. package/extensions/board/config.ts +36 -0
  27. package/extensions/board/format.ts +170 -0
  28. package/extensions/board/index.ts +40 -0
  29. package/extensions/board/input.ts +54 -0
  30. package/extensions/board/join.ts +42 -0
  31. package/extensions/board/page.ts +388 -0
  32. package/extensions/board/result.ts +108 -0
  33. package/extensions/board/rows.ts +48 -0
  34. package/extensions/board/schema.ts +90 -0
  35. package/extensions/board/seat.ts +28 -0
  36. package/extensions/board/snapshot.ts +42 -0
  37. package/extensions/board/tools.ts +444 -0
  38. package/extensions/board/transport/router.ts +91 -0
  39. package/extensions/board/transport/server.ts +138 -0
  40. package/extensions/board/types.ts +121 -0
  41. package/extensions/board/view/client.ts +51 -0
  42. package/extensions/board/view/dialog.ts +43 -0
  43. package/extensions/board/view/keys.ts +125 -0
  44. package/extensions/board/view/live.ts +231 -0
  45. package/extensions/board/view/names.ts +91 -0
  46. package/extensions/board/view/page.ts +118 -0
  47. package/extensions/board/view/render.ts +519 -0
  48. package/extensions/board/view/styles.ts +228 -0
  49. package/extensions/board/view/team.ts +327 -0
  50. package/extensions/bytefly/footer/budget.ts +6 -5
  51. package/extensions/bytefly/footer/collect/entries.ts +12 -4
  52. package/extensions/bytefly/footer/collect/snapshot.ts +8 -17
  53. package/extensions/bytefly/footer/config.ts +19 -8
  54. package/extensions/bytefly/footer/price/price.ts +30 -8
  55. package/extensions/bytefly/footer/register.ts +5 -5
  56. package/extensions/bytefly/footer/render/format.ts +18 -0
  57. package/extensions/bytefly/footer/render/lines.ts +23 -7
  58. package/extensions/bytefly/footer/render/segments.ts +117 -31
  59. package/extensions/bytefly/footer/types.ts +29 -5
  60. package/extensions/bytefly/footer/unpriced.ts +42 -17
  61. package/extensions/bytefly/panel/commands.ts +67 -7
  62. package/extensions/bytefly/panel/filter.ts +68 -0
  63. package/extensions/bytefly/panel/format.ts +16 -15
  64. package/extensions/bytefly/panel/join.ts +56 -13
  65. package/extensions/bytefly/panel/prices.ts +43 -0
  66. package/extensions/bytefly/panel/settings-runtime.ts +0 -6
  67. package/extensions/bytefly/panel/settings.ts +75 -43
  68. package/extensions/bytefly/panel/tui/entries.ts +26 -8
  69. package/extensions/bytefly/panel/tui/frame.ts +130 -93
  70. package/extensions/bytefly/panel/tui/keys.ts +352 -0
  71. package/extensions/bytefly/panel/tui/list-view.ts +25 -46
  72. package/extensions/bytefly/panel/tui/panes.ts +76 -0
  73. package/extensions/bytefly/panel/tui/section-view.ts +126 -0
  74. package/extensions/bytefly/panel/tui/settings-section.ts +54 -125
  75. package/extensions/bytefly/panel/tui/settings-view.ts +30 -131
  76. package/extensions/bytefly/panel/tui/side-pane.ts +224 -0
  77. package/extensions/bytefly/panel/tui.ts +306 -259
  78. package/extensions/bytefly/picker/discovery.ts +0 -2
  79. package/extensions/bytefly/picker/types.ts +0 -2
  80. package/extensions/bytefly/update/check.ts +39 -0
  81. package/extensions/bytefly/update/format.ts +15 -0
  82. package/extensions/bytefly/update/register.ts +73 -34
  83. package/extensions/bytefly/update/state.ts +6 -3
  84. package/extensions/check/commands.ts +2 -1
  85. package/extensions/context/tui.ts +1 -1
  86. package/extensions/file-browse/{config.ts → config/config.ts} +1 -1
  87. package/extensions/file-browse/files/classify.ts +136 -0
  88. package/extensions/file-browse/files/files.ts +12 -0
  89. package/extensions/file-browse/files/index.ts +90 -0
  90. package/extensions/file-browse/files/list.ts +261 -0
  91. package/extensions/file-browse/{paths.ts → files/paths.ts} +1 -1
  92. package/extensions/file-browse/files/tree.ts +101 -0
  93. package/extensions/file-browse/{git.ts → git/git.ts} +10 -3
  94. package/extensions/file-browse/index.ts +4 -4
  95. package/extensions/file-browse/{live.ts → live/live.ts} +20 -69
  96. package/extensions/file-browse/{router.ts → transport/router.ts} +21 -17
  97. package/extensions/file-browse/transport/server.ts +110 -0
  98. package/extensions/file-browse/{chrome.ts → view/chrome.ts} +4 -4
  99. package/extensions/file-browse/{client.ts → view/client.ts} +1 -1
  100. package/extensions/file-browse/{diff-html.ts → view/diff-html.ts} +3 -3
  101. package/extensions/file-browse/view/emit.ts +51 -0
  102. package/extensions/file-browse/{html.ts → view/html.ts} +19 -18
  103. package/extensions/file-browse/{icons.ts → view/icons.ts} +1 -1
  104. package/extensions/file-browse/{markdown.ts → view/markdown.ts} +18 -20
  105. package/extensions/file-browse/{raw-html.ts → view/raw-html.ts} +19 -14
  106. package/extensions/file-browse/{styles.ts → view/styles.ts} +29 -5
  107. package/extensions/file-browse/{tree.ts → view/tree.ts} +1 -1
  108. package/extensions/file-browse/{urls.ts → view/urls.ts} +1 -1
  109. package/extensions/file-read/schema.ts +2 -1
  110. package/extensions/job/runtime.ts +4 -4
  111. package/extensions/memory/store.ts +12 -5
  112. package/extensions/plan/blocks.ts +23 -2
  113. package/extensions/plan/commands.ts +21 -16
  114. package/extensions/plan/feedback.ts +185 -52
  115. package/extensions/plan/format.ts +10 -3
  116. package/extensions/plan/index.ts +23 -5
  117. package/extensions/plan/plan-files.ts +88 -0
  118. package/extensions/plan/plan-index.ts +43 -9
  119. package/extensions/plan/plan-text.ts +33 -23
  120. package/extensions/plan/plans.ts +27 -34
  121. package/extensions/plan/policy.ts +30 -17
  122. package/extensions/plan/review.ts +3 -27
  123. package/extensions/plan/runtime.ts +52 -11
  124. package/extensions/plan/sections.ts +112 -0
  125. package/extensions/plan/steps.ts +3 -22
  126. package/extensions/plan/todos.ts +51 -3
  127. package/extensions/plan/tools/exit.ts +20 -95
  128. package/extensions/plan/tools/todo-seed.ts +4 -2
  129. package/extensions/plan/tools/write.ts +12 -1
  130. package/extensions/plan/tui.ts +5 -34
  131. package/extensions/plan/types.ts +15 -8
  132. package/extensions/plan/web-blocks.ts +89 -0
  133. package/extensions/plan/web-chrome.ts +3 -5
  134. package/extensions/plan/web-client.ts +179 -330
  135. package/extensions/plan/web-collab.ts +3 -3
  136. package/extensions/plan/web-comments.ts +18 -8
  137. package/extensions/plan/web-composer.ts +80 -0
  138. package/extensions/plan/web-draft.ts +40 -0
  139. package/extensions/plan/web-keys.ts +134 -0
  140. package/extensions/plan/web-outline.ts +192 -0
  141. package/extensions/plan/web-page.ts +4 -8
  142. package/extensions/plan/web-plan.ts +150 -0
  143. package/extensions/plan/web-router.ts +3 -58
  144. package/extensions/plan/web-settle.ts +194 -0
  145. package/extensions/plan/web-share-session.ts +24 -30
  146. package/extensions/plan/web-streams.ts +14 -14
  147. package/extensions/plan/web-tokens.ts +2 -2
  148. package/extensions/plan/web.ts +66 -120
  149. package/extensions/rewind/{commands.ts → commands/commands.ts} +5 -5
  150. package/extensions/rewind/{review.ts → commands/review.ts} +15 -14
  151. package/extensions/rewind/{rewind.ts → commands/rewind.ts} +6 -6
  152. package/extensions/rewind/{config.ts → config/config.ts} +1 -1
  153. package/extensions/rewind/index.ts +3 -3
  154. package/extensions/rewind/{policy.ts → state/policy.ts} +4 -4
  155. package/extensions/rewind/{runtime.ts → state/runtime.ts} +11 -11
  156. package/extensions/rewind/{snapshot-run.ts → state/snapshot-run.ts} +1 -1
  157. package/extensions/rewind/{timeline.ts → state/timeline.ts} +2 -2
  158. package/extensions/rewind/{format.ts → text/format.ts} +3 -3
  159. package/extensions/rewind/{git.ts → transport/git.ts} +2 -2
  160. package/extensions/rewind/{restore.ts → transport/restore.ts} +2 -2
  161. package/extensions/rewind/{snapshot.ts → transport/snapshot.ts} +1 -1
  162. package/extensions/rewind/{store.ts → transport/store.ts} +1 -1
  163. package/extensions/rewind/{checklist.ts → view/checklist.ts} +6 -6
  164. package/extensions/rewind/{hunk-picker.ts → view/hunk-picker.ts} +8 -8
  165. package/extensions/rewind/{path-picker.ts → view/path-picker.ts} +7 -7
  166. package/extensions/rewind/{review-tui.ts → view/review-tui.ts} +11 -11
  167. package/extensions/rewind/{rewind-tui.ts → view/rewind-tui.ts} +17 -11
  168. package/extensions/rewind/{status.ts → view/status.ts} +4 -4
  169. package/extensions/session-bus/commands.ts +17 -69
  170. package/extensions/session-bus/format.ts +22 -45
  171. package/extensions/session-bus/index.ts +13 -7
  172. package/extensions/session-bus/join-request.ts +40 -0
  173. package/extensions/session-bus/mail-watch.ts +134 -0
  174. package/extensions/session-bus/notice.ts +32 -4
  175. package/{lib/claims → extensions/session-bus}/overlap.ts +11 -18
  176. package/extensions/session-bus/protocol.ts +19 -49
  177. package/extensions/session-bus/resolve.ts +67 -0
  178. package/extensions/session-bus/result.ts +16 -51
  179. package/extensions/session-bus/rows.ts +11 -5
  180. package/extensions/session-bus/runtime.ts +220 -267
  181. package/extensions/session-bus/store.ts +55 -7
  182. package/extensions/session-bus/tools.ts +31 -66
  183. package/extensions/session-bus/tui.ts +5 -5
  184. package/extensions/session-bus/types.ts +23 -14
  185. package/extensions/subagent/agents.ts +9 -8
  186. package/extensions/subagent/commands.ts +2 -1
  187. package/extensions/subagent/invocation.ts +4 -2
  188. package/extensions/team/commands.ts +145 -0
  189. package/extensions/team/dispatch.ts +133 -0
  190. package/extensions/team/format.ts +119 -0
  191. package/extensions/team/index.ts +34 -0
  192. package/extensions/team/schema.ts +29 -0
  193. package/extensions/team/tools.ts +267 -0
  194. package/extensions/team/types.ts +65 -0
  195. package/extensions/todo/index.ts +4 -1
  196. package/extensions/todo/nudge.ts +8 -2
  197. package/extensions/web-access/fetch/page.ts +4 -1
  198. package/extensions/web-access/fetch/structured.ts +447 -0
  199. package/extensions/web-access/fetch/tool.ts +2 -1
  200. package/extensions/web-access/search/tool.ts +2 -1
  201. package/extensions/wechat/bridge.ts +106 -251
  202. package/extensions/wechat/{commands.ts → commands/commands.ts} +26 -18
  203. package/extensions/wechat/{doctor.ts → commands/doctor.ts} +5 -5
  204. package/extensions/wechat/{history.ts → commands/history.ts} +1 -1
  205. package/extensions/wechat/{login.ts → commands/login.ts} +3 -3
  206. package/extensions/wechat/{slash.ts → commands/slash.ts} +38 -2
  207. package/extensions/wechat/dialog/channels.ts +136 -0
  208. package/extensions/wechat/{interaction.ts → dialog/interaction.ts} +37 -37
  209. package/extensions/wechat/{prompt.ts → dialog/prompt.ts} +2 -2
  210. package/extensions/wechat/{remote-ui.ts → dialog/remote-ui.ts} +1 -1
  211. package/extensions/wechat/index.ts +10 -10
  212. package/extensions/wechat/intake.ts +245 -0
  213. package/extensions/wechat/{admission.ts → peer/admission.ts} +2 -2
  214. package/extensions/wechat/{peer-session.ts → peer/peer-session.ts} +2 -2
  215. package/extensions/wechat/{peer-worker.ts → peer/peer-worker.ts} +100 -22
  216. package/extensions/wechat/{presence.ts → peer/presence.ts} +8 -7
  217. package/extensions/wechat/{reply-pipeline.ts → peer/reply-pipeline.ts} +1 -1
  218. package/extensions/wechat/{credentials.ts → state/credentials.ts} +2 -2
  219. package/extensions/wechat/{state.ts → state/state.ts} +14 -2
  220. package/extensions/wechat/{format.ts → text/format.ts} +3 -3
  221. package/extensions/wechat/{notices.ts → text/notices.ts} +8 -0
  222. package/extensions/wechat/tools.ts +29 -12
  223. package/extensions/wechat/{client.ts → transport/client.ts} +26 -2
  224. package/extensions/wechat/{deliver.ts → transport/deliver.ts} +72 -28
  225. package/extensions/wechat/{inbound.ts → transport/inbound.ts} +5 -5
  226. package/extensions/wechat/{media.ts → transport/media.ts} +11 -5
  227. package/extensions/wechat/transport/outbound.ts +142 -0
  228. package/extensions/wechat/{poll.ts → transport/poll.ts} +3 -3
  229. package/extensions/wechat/types.ts +10 -1
  230. package/extensions/wechat/{login-screen.ts → view/login-screen.ts} +4 -4
  231. package/extensions/wechat/{status-screen.ts → view/status-screen.ts} +31 -26
  232. package/extensions/worktree/commands.ts +24 -17
  233. package/extensions/worktree/exit.ts +12 -6
  234. package/extensions/worktree/git.ts +22 -14
  235. package/extensions/worktree/guard.ts +18 -290
  236. package/extensions/worktree/handoff.ts +10 -2
  237. package/extensions/worktree/index.ts +2 -1
  238. package/extensions/worktree/lifecycle.ts +2 -0
  239. package/extensions/worktree/registry.ts +20 -11
  240. package/extensions/worktree/root-tools.ts +13 -3
  241. package/extensions/worktree/runtime.ts +8 -2
  242. package/extensions/worktree/screen.ts +15 -8
  243. package/extensions/worktree/shell.ts +303 -0
  244. package/extensions/worktree/state.ts +1 -1
  245. package/extensions/worktree/status.ts +42 -9
  246. package/extensions/worktree/tools.ts +19 -13
  247. package/lib/README.md +11 -8
  248. package/lib/board/card.ts +479 -0
  249. package/lib/board/links.ts +141 -0
  250. package/lib/board/page-pointer.ts +310 -0
  251. package/lib/board/round-text.ts +81 -0
  252. package/lib/board/round.ts +94 -0
  253. package/lib/board/rules.ts +437 -0
  254. package/lib/board/session.ts +105 -0
  255. package/lib/board/store.ts +497 -0
  256. package/lib/board/view.ts +234 -0
  257. package/lib/errors.ts +12 -4
  258. package/lib/format.ts +35 -0
  259. package/lib/http/guard.ts +112 -0
  260. package/lib/http/index.ts +16 -5
  261. package/lib/http/serve.ts +237 -0
  262. package/lib/http/server.ts +12 -10
  263. package/lib/http/sink.ts +135 -0
  264. package/lib/http/token.ts +38 -0
  265. package/lib/plan-entry.ts +11 -0
  266. package/lib/policy.ts +25 -2
  267. package/lib/requirements.ts +6 -7
  268. package/lib/session-label.ts +64 -0
  269. package/lib/session-roster.ts +201 -0
  270. package/lib/session-tool.ts +184 -0
  271. package/lib/spawn.ts +37 -3
  272. package/lib/stray-fields.ts +45 -0
  273. package/lib/tool-names.ts +38 -2
  274. package/lib/tui/glyphs.ts +3 -2
  275. package/lib/web/README.md +57 -4
  276. package/lib/web/client-runtime.ts +123 -0
  277. package/lib/web/components.ts +5 -0
  278. package/lib/web/index.ts +4 -2
  279. package/lib/web/markup.ts +88 -1
  280. package/lib/web/names.ts +42 -0
  281. package/lib/web/runtime.ts +7 -1
  282. package/lib/web/tokens.ts +25 -2
  283. package/package.json +26 -14
  284. package/skills/next/SKILL.md +4 -3
  285. package/skills/project-docs/SKILL.md +133 -0
  286. package/skills/project-docs/references/audit.md +131 -0
  287. package/skills/project-docs/references/changelog.md +105 -0
  288. package/skills/project-docs/references/discovery.md +127 -0
  289. package/skills/project-docs/references/readme.md +80 -0
  290. package/skills/project-docs/references/release.md +84 -0
  291. package/skills/project-docs/references/roadmap.md +93 -0
  292. package/skills/project-docs/scripts/state.ts +316 -0
  293. package/skills/refactor/SKILL.md +100 -125
  294. package/skills/refactor/references/handoff.md +35 -0
  295. package/skills/refactor/references/signals.md +54 -0
  296. package/skills/refactor/scripts/inventory.ts +587 -0
  297. package/extensions/bytefly/panel/tui/extensions-view.ts +0 -40
  298. package/extensions/bytefly/panel/tui/skills-view.ts +0 -50
  299. package/extensions/file-browse/README.md +0 -506
  300. package/extensions/file-browse/files.ts +0 -564
  301. package/extensions/file-browse/server.ts +0 -141
  302. package/extensions/job/process.ts +0 -35
  303. package/extensions/plan/split.ts +0 -87
  304. package/extensions/session-bus/README.md +0 -247
  305. package/extensions/session-bus/team.ts +0 -102
  306. package/extensions/wechat/outbound.ts +0 -100
  307. package/lib/plans/sections.ts +0 -172
  308. package/lib/plans/setting.ts +0 -52
  309. package/lib/plans/split.ts +0 -215
  310. package/lib/plans/store.ts +0 -19
  311. package/lib/plans/team.ts +0 -151
  312. package/skills/changelog/SKILL.md +0 -148
  313. package/skills/parallel-dev/SKILL.md +0 -150
  314. /package/extensions/file-browse/{rev.ts → live/rev.ts} +0 -0
  315. /package/extensions/file-browse/{diff.ts → text/diff.ts} +0 -0
  316. /package/extensions/file-browse/{fuzzy.ts → text/fuzzy.ts} +0 -0
  317. /package/extensions/file-browse/{live-client.ts → view/live-client.ts} +0 -0
  318. /package/extensions/file-browse/{palette.ts → view/palette.ts} +0 -0
  319. /package/extensions/rewind/{patch.ts → text/patch.ts} +0 -0
  320. /package/extensions/rewind/{paths.ts → text/paths.ts} +0 -0
  321. /package/extensions/rewind/{queue.ts → transport/queue.ts} +0 -0
  322. /package/extensions/rewind/{temp-index.ts → transport/temp-index.ts} +0 -0
  323. /package/extensions/wechat/{lock.ts → state/lock.ts} +0 -0
  324. /package/extensions/wechat/{channel.ts → text/channel.ts} +0 -0
@@ -6,39 +6,38 @@
6
6
  * for one plan, opens the default browser at a tokenized URL, and resolves
7
7
  * `done` when the user saves feedback, cancels, closes the tab (detected by the
8
8
  * event stream dropping), or aborts the turn. The route logic lives in the pure
9
- * `web-router.ts`; this module is the only part that owns a socket and timers,
10
- * and it takes injected seams so tests can run one on `127.0.0.1:0`.
9
+ * `web-router.ts`; this module owns the routes and the timers, `lib/http` owns
10
+ * the socket, and it takes injected seams so tests can run one on `127.0.0.1:0`.
11
11
  */
12
12
 
13
- import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
14
13
  import { networkInterfaces } from "node:os";
15
14
  import { basename } from "node:path";
16
15
  import { openInBrowser } from "../../lib/browser.ts";
17
16
  import {
18
- bindLoopback,
19
- createCloser,
20
17
  jsonHeaders,
21
- openEventStream,
18
+ type LoopbackRequest,
19
+ type LoopbackReply,
20
+ type LoopbackServer,
22
21
  parseRequestUrl,
23
- respond,
22
+ serveLoopback,
24
23
  type ServeResponse,
25
24
  } from "../../lib/http/index.ts";
26
25
  import { fenceLanguage, highlightLines } from "../../lib/highlight.ts";
27
26
  import { type ArmTimer, defaultInterval, defaultTimer, type RepeatTimer } from "../../lib/timer.ts";
28
- import { codeBody, fenceInfo, planBlocks } from "./blocks.ts";
27
+ import { codeBody, fenceInfo } from "./blocks.ts";
29
28
  import {
30
29
  claimAuthor,
31
30
  MAX_ANNOTATIONS,
32
31
  MAX_COMMENT_LENGTH,
33
32
  MAX_QUOTE_LENGTH,
34
33
  normalizeAuthor,
35
- parseFeedback,
34
+ readFeedback,
36
35
  stripFeedback,
37
36
  type PlanAnnotation,
38
37
  } from "./feedback.ts";
39
38
  import { planTitle, type PlanStore, type StoredPlan } from "./plans.ts";
40
39
  import { changedSections, planDigest, type SectionDigest } from "./plan-text.ts";
41
- import { extractPlanSections, extractPlanSteps } from "./steps.ts";
40
+ import { extractPlanSteps } from "./steps.ts";
42
41
  import type { ReviewCaller } from "./types.ts";
43
42
  import { handleReviewRequest, type ReviewContext, type SaveResult } from "./web-router.ts";
44
43
  import type { ReviewPageData } from "./web-page.ts";
@@ -71,8 +70,6 @@ export interface PlanReviewOptions {
71
70
  signal?: AbortSignal;
72
71
  /** Whether the browser may approve the plan (only the `exit_plan_mode` flow). */
73
72
  canApprove?: boolean;
74
- /** A line the page shows the owner, such as why approval belongs in the terminal. */
75
- note?: string;
76
73
  /** The section digests the previous review showed, so this page can mark changed sections. */
77
74
  baseline?: SectionDigest[];
78
75
  /**
@@ -143,40 +140,6 @@ const DROP_GRACE_MS = 1500;
143
140
  */
144
141
  const PLAN_POLL_MS = 1000;
145
142
 
146
- /** A request body, or the fact that it was too large to read. */
147
- type BodyResult = { ok: true; body: string } | { ok: false };
148
-
149
- /**
150
- * Read a request body, stopping at {@link MAX_BODY_BYTES}. An oversize body is
151
- * reported rather than destroyed here, so the handler can still answer 413 over
152
- * the open socket instead of the client seeing a dropped connection.
153
- */
154
- function readBody(request: IncomingMessage, limit: number): Promise<BodyResult> {
155
- return new Promise((resolve) => {
156
- const chunks: Buffer[] = [];
157
- let size = 0;
158
- let done = false;
159
- const settle = (result: BodyResult): void => {
160
- if (done) return;
161
- done = true;
162
- resolve(result);
163
- };
164
- request.on("data", (chunk: Buffer) => {
165
- if (done) return;
166
- size += chunk.length;
167
- if (size > limit) {
168
- chunks.length = 0;
169
- request.pause();
170
- settle({ ok: false });
171
- return;
172
- }
173
- chunks.push(chunk);
174
- });
175
- request.on("end", () => settle({ ok: true, body: Buffer.concat(chunks).toString("utf-8") }));
176
- request.on("error", () => settle({ ok: false }));
177
- });
178
- }
179
-
180
143
  export function createPlanWebReview(store: PlanStore, deps: PlanWebReviewDeps = {}): PlanWebReview {
181
144
  const sessions = new Set<PlanReviewSession>();
182
145
  // What each running review can do about sharing; the session itself stays the
@@ -193,7 +156,9 @@ export function createPlanWebReview(store: PlanStore, deps: PlanWebReviewDeps =
193
156
  ): Promise<PlanReviewSession | undefined> => {
194
157
  const { signal } = options;
195
158
  const canApprove = options.canApprove === true;
196
- const maxBody = deps.maxBodyBytes ?? MAX_BODY_BYTES;
159
+ // One cap for both listeners: a reviewer's page posts the same bodies as the
160
+ // owner's, so the shared one cannot be the stricter or the looser of the two.
161
+ const bodyLimit = deps.maxBodyBytes ?? MAX_BODY_BYTES;
197
162
  // One token per holder: the owner's is minted here and never shared, and a
198
163
  // reviewer token is minted by `share()`. The table owns the collision rule and
199
164
  // the name each link has bound.
@@ -279,13 +244,16 @@ export function createPlanWebReview(store: PlanStore, deps: PlanWebReviewDeps =
279
244
  const page = async (caller: ReviewCaller): Promise<ReviewPageData> => {
280
245
  const { role } = caller;
281
246
  const stored = await refreshPageText();
282
- const current = pageText;
283
- const blocks = planBlocks(current);
284
- const sections = extractPlanSections(current);
285
- // Per-occurrence change flags, aligned with the sections list: the same shape
286
- // the terminal review renders, so duplicate headings cannot both light up.
287
- const changed = changedSections(options.baseline, current);
288
- const outline = sections.flatMap((section, index) =>
247
+ // The body, its blocks, and the comments out of one read of the file: an
248
+ // anchor names a block from this same parse, so the island cannot mark a
249
+ // comment onto a line the page never rendered.
250
+ const read = readFeedback(stored.content);
251
+ const current = read.body;
252
+ const blocks = read.blocks;
253
+ // Per-section change marks, each carrying its own section: the same shape the
254
+ // terminal review renders, so a heading and its mark cannot be paired wrongly.
255
+ const marks = changedSections(options.baseline, current);
256
+ const outline = marks.flatMap(({ section, changed }) =>
289
257
  section.title === ""
290
258
  ? []
291
259
  : [
@@ -294,7 +262,7 @@ export function createPlanWebReview(store: PlanStore, deps: PlanWebReviewDeps =
294
262
  line: section.line,
295
263
  level: section.level,
296
264
  text: section.title,
297
- ...(changed[index] ? { changed: true } : {}),
265
+ ...(changed ? { changed: true } : {}),
298
266
  },
299
267
  ],
300
268
  );
@@ -312,9 +280,9 @@ export function createPlanWebReview(store: PlanStore, deps: PlanWebReviewDeps =
312
280
  return {
313
281
  // The document's own H1 reads better than the file-name slug, and the
314
282
  // slug is still the fallback for a plan that starts with something else.
315
- title: sections.find((section) => section.level === 1)?.title || planTitle(basename(plan.path)),
283
+ title: marks.find((mark) => mark.section.level === 1)?.section.title || planTitle(basename(plan.path)),
316
284
  blocks,
317
- annotations: parseFeedback(stored.content),
285
+ annotations: read.annotations,
318
286
  outline,
319
287
  steps: {
320
288
  done: stepList.filter((step) => step.status === "completed").length,
@@ -328,7 +296,6 @@ export function createPlanWebReview(store: PlanStore, deps: PlanWebReviewDeps =
328
296
  rev: planDigest(current),
329
297
  ...(caller.name === undefined ? {} : { author: caller.name }),
330
298
  canApprove: role === "owner" && canApprove,
331
- ...(role === "owner" && options.note !== undefined ? { note: options.note } : {}),
332
299
  share: role === "owner" ? shareSession.state() : undefined,
333
300
  };
334
301
  };
@@ -376,6 +343,7 @@ export function createPlanWebReview(store: PlanStore, deps: PlanWebReviewDeps =
376
343
  // frame hooks, so sharing stays one concern instead of five fields here.
377
344
  const shareSession = createShareSession({
378
345
  handleRequest,
346
+ maxBodyBytes: bodyLimit,
379
347
  ownerPort: () => currentPort,
380
348
  networkInterfaces: deps.networkInterfaces ?? networkInterfaces,
381
349
  mintReviewer: () => tokens.mint("reviewer"),
@@ -410,78 +378,56 @@ export function createPlanWebReview(store: PlanStore, deps: PlanWebReviewDeps =
410
378
  // Live event streams, used only to notice a closed owner tab: a reviewer's
411
379
  // tab is not the review's lifetime. The explicit Cancel button and the abort
412
380
  // signal are the deterministic paths.
413
- const server = createServer(handleRequest);
414
381
  /** One request, on either listener: the loopback page or a shared one. */
415
- function handleRequest(request: IncomingMessage, response: ServerResponse): void {
416
- void (async () => {
417
- const method = request.method ?? "GET";
418
- const read =
419
- method.toUpperCase() === "POST" ? await readBody(request, maxBody) : { ok: true as const, body: "" };
420
- if (!read.ok) {
421
- response.writeHead(413, { ...jsonHeaders(), Connection: "close" });
422
- response.end(JSON.stringify({ error: "The request body is too large." }));
423
- return;
424
- }
425
- const body = read.body;
426
- let result: ServeResponse;
427
- try {
428
- result = await handleReviewRequest(context, method, request.url ?? "/", request.headers, body);
429
- } catch {
430
- result = {
431
- status: 500,
432
- headers: jsonHeaders(),
433
- body: JSON.stringify({ error: "Internal error" }),
434
- };
435
- }
436
- if (result.stream) {
437
- openEventStream(response, result);
438
- // A page that walks away mid-frame must not take the session down.
439
- response.on("error", () => {});
440
- const token = parseRequestUrl(request.url ?? "/")?.search.get("token") ?? "";
441
- const streamCaller = context.caller(token);
442
- if (streamCaller !== undefined) streams.add(response, { role: streamCaller.role, token });
443
- request.on("close", () => streams.remove(response));
444
- return;
445
- }
446
- // A save or cancel only resolves the session after its response is on the
447
- // wire; otherwise `close()` would force-close the socket mid-reply. The
448
- // `close` path covers a client that walks away before the flush callback
449
- // runs, so a settle cannot be lost with the socket.
450
- const settlePending = (): void => {
451
- const terminal = pendingResult;
452
- if (!terminal) return;
453
- pendingResult = undefined;
454
- finish(terminal);
382
+ async function handleRequest(request: LoopbackRequest): Promise<LoopbackReply> {
383
+ let result: ServeResponse;
384
+ try {
385
+ result = await handleReviewRequest(context, request.method, request.url, request.headers, request.body);
386
+ } catch {
387
+ result = {
388
+ status: 500,
389
+ headers: jsonHeaders(),
390
+ body: JSON.stringify({ error: "Internal error" }),
455
391
  };
456
- respond(request, response, result, settlePending);
457
- response.on("close", settlePending);
458
- })();
392
+ }
393
+ // A save or cancel only resolves the session after its response is on the
394
+ // wire; otherwise `close()` would force-close the socket mid-reply. The same
395
+ // hook covers a client that walks away before the flush, so a settle cannot be
396
+ // lost with the socket.
397
+ const settlePending = (): void => {
398
+ const terminal = pendingResult;
399
+ if (!terminal) return;
400
+ pendingResult = undefined;
401
+ finish(terminal);
402
+ };
403
+ if (!result.stream) return { result, afterResponse: settlePending };
404
+ const token = parseRequestUrl(request.url)?.search.get("token") ?? "";
405
+ const streamCaller = context.caller(token);
406
+ return {
407
+ result,
408
+ openSink: (sink) => {
409
+ if (streamCaller !== undefined) streams.add(sink, { role: streamCaller.role, token });
410
+ return () => streams.remove(sink);
411
+ },
412
+ };
459
413
  }
460
414
 
461
- // The accept-level `error` listener lives in `bindHost`, so a transient
462
- // failure cannot take the host down; the review still ends through its own
463
- // paths: cancel, abort, drop, or shutdown.
464
415
  const preferred = deps.port ?? ports.get(plan.path) ?? 0;
465
- let port: number | undefined;
416
+ let server: LoopbackServer;
466
417
  try {
467
- port = await bindLoopback(server, preferred);
418
+ server = await serveLoopback({
419
+ ports: preferred === 0 ? [0] : [preferred, 0],
420
+ maxBodyBytes: bodyLimit,
421
+ handler: handleRequest,
422
+ });
468
423
  } catch {
469
- // The remembered port may have been taken between turns; fall back once.
470
- if (preferred !== 0) {
471
- try {
472
- port = await bindLoopback(server, 0);
473
- } catch {
474
- port = undefined;
475
- }
476
- }
477
- }
478
- if (port === undefined) {
479
- server.close();
424
+ // The remembered port may have been taken between turns; the fallback above
425
+ // covers that, so reaching here means neither port answered.
480
426
  return undefined;
481
427
  }
428
+ const port = server.port;
482
429
  ports.set(plan.path, port);
483
430
  currentPort = port;
484
- const closeServer = createCloser(server);
485
431
 
486
432
  const url = `http://127.0.0.1:${port}/?token=${token}`;
487
433
  let closed = false;
@@ -498,7 +444,7 @@ export function createPlanWebReview(store: PlanStore, deps: PlanWebReviewDeps =
498
444
  stopTicker();
499
445
  streams.clear();
500
446
  await shareSession.close();
501
- await closeServer();
447
+ await server.close();
502
448
  };
503
449
 
504
450
  const session: PlanReviewSession = { url, port, done, close };
@@ -8,13 +8,13 @@
8
8
  */
9
9
 
10
10
  import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
11
- import { withRailsSuppressed } from "../../lib/rails.ts";
12
- import { formatRewindListText, formatRewindRow } from "./format.ts";
11
+ import { withRailsSuppressed } from "../../../lib/rails.ts";
12
+ import { formatRewindListText, formatRewindRow } from "../text/format.ts";
13
13
  import { openReview } from "./review.ts";
14
14
  import { rewindTo } from "./rewind.ts";
15
- import type { RewindRuntime } from "./runtime.ts";
16
- import { RewindListComponent } from "./rewind-tui.ts";
17
- import { buildRewindPoints, type RewindPoint } from "./timeline.ts";
15
+ import type { RewindRuntime } from "../state/runtime.ts";
16
+ import { RewindListComponent } from "../view/rewind-tui.ts";
17
+ import { buildRewindPoints, type RewindPoint } from "../state/timeline.ts";
18
18
 
19
19
  /** Pick a point from the timeline, or undefined when the user cancels. */
20
20
  async function choosePoint(ctx: ExtensionCommandContext, points: RewindPoint[]): Promise<RewindPoint | undefined> {
@@ -10,10 +10,11 @@
10
10
  */
11
11
 
12
12
  import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
13
- import { messageOf } from "../../lib/errors.ts";
14
- import { sanitizeLine } from "../../lib/format.ts";
15
- import { isPlanModeEnabled } from "../../lib/plan-state.ts";
16
- import { withRailsSuppressed } from "../../lib/rails.ts";
13
+ import { messageOf } from "../../../lib/errors.ts";
14
+ import { sanitizeLine } from "../../../lib/format.ts";
15
+ import { planModeRefusal } from "../../../lib/plan-entry.ts";
16
+ import { isPlanModeEnabled } from "../../../lib/plan-state.ts";
17
+ import { withRailsSuppressed } from "../../../lib/rails.ts";
17
18
  import {
18
19
  boundDiff,
19
20
  formatHunkRevertBody,
@@ -23,20 +24,20 @@ import {
23
24
  formatRevertSelectedBody,
24
25
  formatReviewListText,
25
26
  formatReviewRevertText,
26
- } from "./format.ts";
27
- import { HunkPickerComponent } from "./hunk-picker.ts";
28
- import { buildHunkPatch, parseFilePatch } from "./patch.ts";
29
- import { entryPaths, toReviewEntries, type ReviewEntry } from "./paths.ts";
27
+ } from "../text/format.ts";
28
+ import { HunkPickerComponent } from "../view/hunk-picker.ts";
29
+ import { buildHunkPatch, parseFilePatch } from "../text/patch.ts";
30
+ import { entryPaths, toReviewEntries, type ReviewEntry } from "../text/paths.ts";
30
31
  import {
31
32
  ReviewDiffComponent,
32
33
  ReviewListComponent,
33
34
  type ReviewDiffAction,
34
35
  type ReviewListResult,
35
- } from "./review-tui.ts";
36
- import type { RestorePlan } from "./restore.ts";
37
- import type { RewindRuntime } from "./runtime.ts";
38
- import { buildRewindPoints, type RewindPoint } from "./timeline.ts";
39
- import type { Snapshot, RestoreSummary } from "./types.ts";
36
+ } from "../view/review-tui.ts";
37
+ import type { RestorePlan } from "../transport/restore.ts";
38
+ import type { RewindRuntime } from "../state/runtime.ts";
39
+ import { buildRewindPoints, type RewindPoint } from "../state/timeline.ts";
40
+ import type { Snapshot, RestoreSummary } from "../types.ts";
40
41
 
41
42
  /** One reviewable view of the working tree. */
42
43
  interface ReviewView {
@@ -75,7 +76,7 @@ async function loadReview(runtime: RewindRuntime, ctx: ExtensionCommandContext):
75
76
  return {
76
77
  ok: false,
77
78
  kind: "warning",
78
- message: "Plan mode is read-only. Approve the plan before reviewing and reverting its changes.",
79
+ message: planModeRefusal("reviewing and reverting its changes"),
79
80
  };
80
81
  }
81
82
 
@@ -9,13 +9,13 @@
9
9
  */
10
10
 
11
11
  import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
12
- import { withRailsSuppressed } from "../../lib/rails.ts";
12
+ import { withRailsSuppressed } from "../../../lib/rails.ts";
13
13
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
14
- import { formatChangeSummary, formatRestoreText, previewDiffText } from "./format.ts";
15
- import { PathPickerComponent } from "./path-picker.ts";
16
- import type { ChangedPath } from "./paths.ts";
17
- import type { RewindRuntime } from "./runtime.ts";
18
- import { rewindScopes, type RewindPoint, type RewindScope } from "./timeline.ts";
14
+ import { formatChangeSummary, formatRestoreText, previewDiffText } from "../text/format.ts";
15
+ import { PathPickerComponent } from "../view/path-picker.ts";
16
+ import type { ChangedPath } from "../text/paths.ts";
17
+ import type { RewindRuntime } from "../state/runtime.ts";
18
+ import { rewindScopes, type RewindPoint, type RewindScope } from "../state/timeline.ts";
19
19
 
20
20
  /** Human labels for the scope picker, in menu order. */
21
21
  const SCOPE_LABELS: Record<RewindScope, string> = {
@@ -6,7 +6,7 @@
6
6
  * malformed documents are ignored, and every value is clamped to a safe range.
7
7
  */
8
8
 
9
- import { clampInteger, cleanString, loadConfigSection, stringList } from "../../lib/config.ts";
9
+ import { clampInteger, cleanString, loadConfigSection, stringList } from "../../../lib/config.ts";
10
10
 
11
11
  export interface RewindConfig {
12
12
  /** Take one automatic snapshot per user prompt. Default: true. */
@@ -16,9 +16,9 @@
16
16
 
17
17
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
18
18
  import { isExtensionEnabled } from "../../lib/env.ts";
19
- import { registerCommands } from "./commands.ts";
20
- import { createRuntime } from "./runtime.ts";
21
- import { createSnapshotRun } from "./snapshot-run.ts";
19
+ import { registerCommands } from "./commands/commands.ts";
20
+ import { createRuntime } from "./state/runtime.ts";
21
+ import { createSnapshotRun } from "./state/snapshot-run.ts";
22
22
 
23
23
  export function registerRewind(pi: ExtensionAPI): void {
24
24
  const runtime = createRuntime(pi);
@@ -9,12 +9,12 @@
9
9
  */
10
10
 
11
11
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
12
- import { createReadOnlyPolicy } from "../../lib/policy.ts";
13
- import { ASK_USER_QUESTION_TOOL, GOAL_TOOL, TODO_TOOL } from "../../lib/tool-names.ts";
14
- import type { RewindConfig } from "./config.ts";
12
+ import { createReadOnlyPolicy, WORKSPACE_READERS } from "../../../lib/policy.ts";
13
+ import { ASK_USER_QUESTION_TOOL, GOAL_TOOL, TODO_TOOL } from "../../../lib/tool-names.ts";
14
+ import type { RewindConfig } from "../config/config.ts";
15
15
 
16
16
  /** Tools that only read files or track session state, so they never snapshot. */
17
- const READER_TOOLS = ["read", "grep", "find", "ls", TODO_TOOL, GOAL_TOOL];
17
+ const READER_TOOLS = [...WORKSPACE_READERS, TODO_TOOL, GOAL_TOOL];
18
18
 
19
19
  /** The interactive tool: it prompts the user and never touches the working tree. */
20
20
  const SESSION_TOOLS = [ASK_USER_QUESTION_TOOL];
@@ -9,8 +9,8 @@
9
9
  */
10
10
 
11
11
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
12
- import { resolveEffectiveCwd } from "../../lib/env.ts";
13
- import { loadConfig, type RewindConfig } from "./config.ts";
12
+ import { resolveEffectiveCwd } from "../../../lib/env.ts";
13
+ import { loadConfig, type RewindConfig } from "../config/config.ts";
14
14
  import {
15
15
  applyPatchReverse,
16
16
  createRewindRunner,
@@ -19,16 +19,16 @@ import {
19
19
  repoRoot,
20
20
  revParse,
21
21
  type RunGit,
22
- } from "./git.ts";
23
- import { createOperationQueue } from "./queue.ts";
24
- import { applyRestore, planRestore, type PlanResult, type RestoreInput } from "./restore.ts";
25
- import type { PathStat } from "./paths.ts";
26
- import { createSnapshot } from "./snapshot.ts";
27
- import { createStatusChip } from "./status.ts";
28
- import { belongsToRoot, listSnapshots, pruneSnapshots } from "./store.ts";
29
- import { createTempIndexStore } from "./temp-index.ts";
22
+ } from "../transport/git.ts";
23
+ import { createOperationQueue } from "../transport/queue.ts";
24
+ import { applyRestore, planRestore, type PlanResult, type RestoreInput } from "../transport/restore.ts";
25
+ import type { PathStat } from "../text/paths.ts";
26
+ import { createSnapshot } from "../transport/snapshot.ts";
27
+ import { createStatusChip } from "../view/status.ts";
28
+ import { belongsToRoot, listSnapshots, pruneSnapshots } from "../transport/store.ts";
29
+ import { createTempIndexStore } from "../transport/temp-index.ts";
30
30
  import { lastUserEntryId } from "./timeline.ts";
31
- import type { Snapshot, SnapshotReason, RestoreSummary } from "./types.ts";
31
+ import type { Snapshot, SnapshotReason, RestoreSummary } from "../types.ts";
32
32
 
33
33
  export interface SnapshotOptions {
34
34
  reason: SnapshotReason;
@@ -10,7 +10,7 @@
10
10
  */
11
11
 
12
12
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
13
- import { summarizePrompt } from "./format.ts";
13
+ import { summarizePrompt } from "../text/format.ts";
14
14
  import { createSnapshotPolicy, type SnapshotPolicy } from "./policy.ts";
15
15
  import type { RewindRuntime } from "./runtime.ts";
16
16
 
@@ -9,8 +9,8 @@
9
9
  */
10
10
 
11
11
  import type { SessionEntry, SessionMessageEntry } from "@earendil-works/pi-coding-agent";
12
- import { summarizePrompt } from "./format.ts";
13
- import type { Snapshot } from "./types.ts";
12
+ import { summarizePrompt } from "../text/format.ts";
13
+ import type { Snapshot } from "../types.ts";
14
14
 
15
15
  /** What a rewind can restore for one point. */
16
16
  export type RewindScope = "both" | "conversation" | "code";
@@ -7,10 +7,10 @@
7
7
  */
8
8
 
9
9
  import { sliceByColumn, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
10
- import { sanitizeLine, stripControlChars } from "../../lib/format.ts";
11
- import { GLYPHS, SEPARATORS } from "../../lib/tui/glyphs.ts";
10
+ import { sanitizeLine, stripControlChars } from "../../../lib/format.ts";
11
+ import { GLYPHS, SEPARATORS } from "../../../lib/tui/glyphs.ts";
12
12
  import { SELECTION_MARKS, type ReviewEntry } from "./paths.ts";
13
- import type { RestoreSummary } from "./types.ts";
13
+ import type { RestoreSummary } from "../types.ts";
14
14
 
15
15
  /** Longest prompt summary shown before truncation. */
16
16
  const LABEL_WIDTH = 48;
@@ -23,8 +23,8 @@ import {
23
23
  revParseMany,
24
24
  runGitOrThrow as run,
25
25
  type RunGit,
26
- } from "../../lib/git/index.ts";
27
- import { parseNameStatus, parseNumStat, type ChangedPath, type PathStat } from "./paths.ts";
26
+ } from "../../../lib/git/index.ts";
27
+ import { parseNameStatus, parseNumStat, type ChangedPath, type PathStat } from "../text/paths.ts";
28
28
 
29
29
  // Re-export exactly the shared helpers this extension consumes, so rewind code
30
30
  // has one git import surface instead of reaching into `lib/git` directly.
@@ -26,8 +26,8 @@ import {
26
26
  treeFromWorkingTree,
27
27
  type RunGit,
28
28
  } from "./git.ts";
29
- import { entryPaths, summarize, type ChangedPath } from "./paths.ts";
30
- import type { Snapshot, RestoreSummary } from "./types.ts";
29
+ import { entryPaths, summarize, type ChangedPath } from "../text/paths.ts";
30
+ import type { Snapshot, RestoreSummary } from "../types.ts";
31
31
 
32
32
  export interface RestoreDeps {
33
33
  runGit: RunGit;
@@ -18,7 +18,7 @@ import {
18
18
  type RunGit,
19
19
  } from "./git.ts";
20
20
  import { encodeMessage, refFor, META_VERSION, type SnapshotMeta } from "./store.ts";
21
- import type { Snapshot, SnapshotReason } from "./types.ts";
21
+ import type { Snapshot, SnapshotReason } from "../types.ts";
22
22
 
23
23
  export interface SnapshotDeps {
24
24
  runGit: RunGit;
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import { commitMessages, deleteRef, listRefs, type RunGit } from "./git.ts";
10
- import type { Snapshot, SnapshotReason } from "./types.ts";
10
+ import type { Snapshot, SnapshotReason } from "../types.ts";
11
11
 
12
12
  /** Prefix of the metadata line in a snapshot commit body. */
13
13
  export const META_MARKER = "pi-rewind: ";
@@ -14,12 +14,12 @@
14
14
 
15
15
  import type { Theme } from "@earendil-works/pi-coding-agent";
16
16
  import { Key, matchesKey, type Component, type TuiMouseEvent, type TuiMouseEventResult } from "@earendil-works/pi-tui";
17
- import { detailBlockRows, dockListFrame, type ViewportRowsSource } from "../../lib/tui/chrome.ts";
18
- import { clip } from "../../lib/tui/columns.ts";
19
- import { fitRows, formatRange, isCloseKey, ListCursor, navigateList, scrollList } from "../../lib/tui/cursor.ts";
20
- import { emptyState } from "../../lib/tui/rows.ts";
21
- import { titleRule } from "../../lib/tui/rules.ts";
22
- import { paint } from "../../lib/tui/tokens.ts";
17
+ import { detailBlockRows, dockListFrame, type ViewportRowsSource } from "../../../lib/tui/chrome.ts";
18
+ import { clip } from "../../../lib/tui/columns.ts";
19
+ import { fitRows, formatRange, isCloseKey, ListCursor, navigateList, scrollList } from "../../../lib/tui/cursor.ts";
20
+ import { emptyState } from "../../../lib/tui/rows.ts";
21
+ import { titleRule } from "../../../lib/tui/rules.ts";
22
+ import { paint } from "../../../lib/tui/tokens.ts";
23
23
 
24
24
  /** Rows the checklist shows when the terminal height is unknown. */
25
25
  const SCREEN_DEFAULT_ITEMS = 12;
@@ -11,15 +11,15 @@
11
11
  */
12
12
 
13
13
  import type { Theme } from "@earendil-works/pi-coding-agent";
14
- import { sanitizeLine } from "../../lib/format.ts";
15
- import { screenHint, type ViewportRowsSource } from "../../lib/tui/chrome.ts";
16
- import { clip } from "../../lib/tui/columns.ts";
17
- import { SEPARATORS } from "../../lib/tui/glyphs.ts";
18
- import { checkbox, listRow } from "../../lib/tui/rows.ts";
19
- import { paint } from "../../lib/tui/tokens.ts";
14
+ import { sanitizeLine } from "../../../lib/format.ts";
15
+ import { screenHint, type ViewportRowsSource } from "../../../lib/tui/chrome.ts";
16
+ import { clip } from "../../../lib/tui/columns.ts";
17
+ import { SEPARATORS } from "../../../lib/tui/glyphs.ts";
18
+ import { checkbox, listRow } from "../../../lib/tui/rows.ts";
19
+ import { paint } from "../../../lib/tui/tokens.ts";
20
20
  import { ChecklistScreen } from "./checklist.ts";
21
- import { formatReviewEntryCounts } from "./format.ts";
22
- import type { Hunk } from "./patch.ts";
21
+ import { formatReviewEntryCounts } from "../text/format.ts";
22
+ import type { Hunk } from "../text/patch.ts";
23
23
 
24
24
  export interface HunkPickerOptions {
25
25
  /** The file the hunks belong to. */
@@ -13,14 +13,14 @@
13
13
  */
14
14
 
15
15
  import type { Theme } from "@earendil-works/pi-coding-agent";
16
- import { sanitizeLine } from "../../lib/format.ts";
17
- import { screenHint, type ViewportRowsSource } from "../../lib/tui/chrome.ts";
18
- import { clip } from "../../lib/tui/columns.ts";
19
- import { SEPARATORS } from "../../lib/tui/glyphs.ts";
20
- import { checkbox, listRow } from "../../lib/tui/rows.ts";
21
- import { paint } from "../../lib/tui/tokens.ts";
16
+ import { sanitizeLine } from "../../../lib/format.ts";
17
+ import { screenHint, type ViewportRowsSource } from "../../../lib/tui/chrome.ts";
18
+ import { clip } from "../../../lib/tui/columns.ts";
19
+ import { SEPARATORS } from "../../../lib/tui/glyphs.ts";
20
+ import { checkbox, listRow } from "../../../lib/tui/rows.ts";
21
+ import { paint } from "../../../lib/tui/tokens.ts";
22
22
  import { ChecklistScreen } from "./checklist.ts";
23
- import { baseName, expandGroup, groupPaths, SELECTION_MARKS, type ChangedPath, type PathGroup } from "./paths.ts";
23
+ import { baseName, expandGroup, groupPaths, SELECTION_MARKS, type ChangedPath, type PathGroup } from "../text/paths.ts";
24
24
 
25
25
  export interface PathPickerOptions {
26
26
  /** The changed paths the snapshot differs on. */