@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
@@ -5,11 +5,19 @@ handler, or reveals it in the file manager. `/open` does it interactively and
5
5
  the `open_path` tool does it from a script, both resolving the path against the
6
6
  effective working directory.
7
7
 
8
- ## Tool rendering
8
+ ## Capabilities
9
9
 
10
- How the `open_path` call and its result look in the transcript — the call line as a headline and the body indented under it:
10
+ Status records what a reader can rely on: **Shipped** is in a tagged release, **Unreleased** is complete in the current tree but not yet tagged, and **Partial** has one released part and one unreleased part. Rows are ordered by user value, highest first.
11
11
 
12
- <img src="../../assets/tools/open_path.png" alt="The open_path tool's call and result, drawn by the generic fallback" width="100%">
12
+ | Capability | How | What you get | Status |
13
+ |---|---|---|---|
14
+ | Native open | `/open`, `open_path` | The file or directory opens in the application the OS associates with its type. | Shipped |
15
+ | Reveal in the file manager | `/open -r`, `open_path` with `reveal: true` | The target is selected in the file manager instead of opened. | Shipped |
16
+ | Working-directory resolution | `/open`, `open_path` | A relative path resolves against the effective working directory, so under [`worktree`](./worktree.md) `/open .` opens the isolated worktree. | Shipped |
17
+ | Codemode-only exposure | `exposure: "codemode"` | The model cannot call `open_path` directly unless a codemode script asks for it; the table does not promise otherwise. | Shipped |
18
+ | Pasted-path cleanup | `/open`, `open_path` | The editor's `@` mention prefix, surrounding quotes, and control characters are stripped before the path is used. | Shipped |
19
+ | Failure reporting | `/open`, `open_path` | A missing path, a launcher that cannot start, and a non-zero exit after a successful start on macOS/Linux are reported. | Shipped |
20
+ | Session-outliving launch | `/open`, `open_path` | The handler runs detached, so the application outlives the Pi session. | Shipped |
13
21
 
14
22
  ## Quickstart
15
23
 
@@ -25,21 +33,6 @@ pi install ./ # install the package for a user
25
33
  /open -r src reveal src in the file manager
26
34
  ```
27
35
 
28
- ## What it does
29
-
30
- - Opens a file or directory with the OS-native application: `open` on macOS,
31
- `explorer` on Windows, `xdg-open` on Linux.
32
- - Reveals it in the file manager instead with `-r` (or `reveal: true`): `open -R`
33
- on macOS, `explorer /select,` on Windows, and the containing directory on
34
- Linux, which has no "select" verb.
35
- - Unwraps the editor's `@` file-mention prefix and surrounding quotes, so
36
- `/open @src/app.ts` and `/open @"my file.txt"` both work.
37
- - Reports a launcher that cannot be started, and (on macOS and Linux) a launcher
38
- that exits non-zero because no handler accepted the target.
39
- - Resolves relative paths against the effective working directory, so under the
40
- `worktree` extension `/open .` opens the isolated worktree.
41
- - Launches the handler detached, so it outlives the Pi session.
42
-
43
36
  ## Commands and flags
44
37
 
45
38
  ```bash
@@ -55,6 +48,8 @@ needs an interactive terminal UI.
55
48
 
56
49
  ## Tool
57
50
 
51
+ <img src="../../assets/tools/open_path.png" alt="The open_path tool's call and result, drawn by the generic fallback" width="100%">
52
+
58
53
  `open_path` — `exposure: "codemode"`, inactive by default, not read-only,
59
54
  open-world (`openWorldHint: true` because it starts an OS process). It is listed
60
55
  by the `codemode` tool for a script to call as `tools.open_path(...)`, and an
@@ -106,6 +101,19 @@ JSON, and print modes; a codemode script can call it in any of them. Only the
106
101
  non-zero after a successful open, so only a failure to start is reported
107
102
  there. Nothing is retried.
108
103
 
104
+ ## Native handlers
105
+
106
+ Both surfaces run the same command for a platform; the mode alone chooses
107
+ between open and reveal.
108
+
109
+ | Platform | Open | Reveal |
110
+ |---|---|---|
111
+ | macOS | `open <path>` | `open -R <path>` |
112
+ | Windows | `explorer <path>` | `explorer /select,<path>` |
113
+ | Linux | `xdg-open <path>` | `xdg-open <dir>` |
114
+
115
+ Reveal has no native meaning on Linux, so there the containing directory opens.
116
+
109
117
  ## Limitations
110
118
 
111
119
  - Reveal has no native meaning on Linux, so it opens the containing directory.
@@ -127,7 +135,7 @@ JSON, and print modes; a codemode script can call it in any of them. Only the
127
135
  | Command | `/open [path]` and `/open -r [path]`; reports through `ctx.ui.notify`. |
128
136
  | State | None persisted, and none needed: every call is independent. |
129
137
  | Lifecycle | No `session_start` / `session_shutdown` hooks; the factory holds no long-lived resources. |
130
- | Host seams | `resolveEffectiveCwd` and `isExtensionEnabled` (`lib/env.ts`), `stripControlChars` (`lib/format.ts`), `spawn` / `open` / `xdg-open` / `explorer` (via the injected launcher in `open.ts`). |
138
+ | Host seams | `resolveEffectiveCwd` and `isExtensionEnabled` ([`lib/env.ts`](../../lib/env.ts)), `stripControlChars` ([`lib/format.ts`](../../lib/format.ts)), `spawn` / `open` / `xdg-open` / `explorer` (via the injected launcher in [`open.ts`](../../extensions/file-open/open.ts)). |
131
139
  | Config | None. |
132
140
 
133
141
  ## Design notes
@@ -149,7 +157,7 @@ of the model's active tool set unless a script explicitly reaches for it.
149
157
 
150
158
  | Module | Responsibility |
151
159
  |---|---|
152
- | `index.ts` | The extension factory: registers `open_path` and the `/open` command, resolves the target, and routes a launch failure to `ctx.ui.notify`. |
153
- | `open.ts` | `normalizeTarget` and `buildCommand` (pure), `resolveTarget` (existence check), and `openTarget` with the injectable launcher, which reports a start failure and a meaningful non-zero exit. |
154
- | `schema.ts` | `TOOL_NAME`, the TypeBox `parameters` / `outputSchema`, and the `OpenPathArgs` / `OpenPathResult` types. |
155
- | `extension.test.ts` | The capability suite: normalization, resolution, per-platform commands, the launcher seam, registration, disabling, the tool call, and the command. |
160
+ | [`index.ts`](../../extensions/file-open/index.ts) | The extension factory: registers `open_path` and the `/open` command, resolves the target, and routes a launch failure to `ctx.ui.notify`. |
161
+ | [`open.ts`](../../extensions/file-open/open.ts) | `normalizeTarget` and `buildCommand` (pure), `resolveTarget` (existence check), and `openTarget` with the injectable launcher, which reports a start failure and a meaningful non-zero exit. |
162
+ | [`schema.ts`](../../extensions/file-open/schema.ts) | `TOOL_NAME`, the TypeBox `parameters` / `outputSchema`, and the `OpenPathArgs` / `OpenPathResult` types. |
163
+ | [`extension.test.ts`](../../extensions/file-open/extension.test.ts) | The capability suite: normalization, resolution, per-platform commands, the launcher seam, registration, disabling, the tool call, and the command. |
@@ -8,12 +8,29 @@
8
8
  the effective working directory with the same symlink-aware guard the built-in
9
9
  path tools use.
10
10
 
11
+ ## Capabilities
12
+
13
+ Status records what a reader can rely on: **Shipped** is in a tagged release, **Unreleased** is complete in the current tree but not yet tagged, and **Partial** has one released part and one unreleased part. Rows are ordered by user value, highest first.
14
+
15
+ | Capability | How | What you get | Status |
16
+ |---|---|---|---|
17
+ | Document extraction | `read_doc` | Plain text from a document or workbook whose bytes the built-in `read` cannot decode. | Shipped |
18
+ | Paged reading | `read_doc` (`startIndex`, `maxChars`) | A long file arrives in slices, and a truncated result names the offset to ask for next. | Shipped |
19
+ | Workbook text | `read_doc` on `.xlsx` | One `[Sheet]` heading per worksheet followed by tab-separated rows, so the shape of the file survives. | Shipped |
20
+ | Confined reads | `read_doc` | A path outside the effective working directory is refused, symlinks resolved, before any parser runs. | Shipped |
21
+ | Progress and cancellation | `startToolProgress`, the abort signal | A slow extraction shows `Reading... <elapsed>` and can be cancelled instead of looking dead. | Shipped |
22
+ | Bounded extraction | `file-read.maxFileBytes`, `file-read.extractTimeoutMs` | An over-size file or a parser past its deadline fails the read instead of parking the turn. | Shipped |
23
+ | Per-format switches | `file-read.formats` | Any of the six formats can be turned off, and a disabled read names the key that did it. | Shipped |
24
+ | Unsupported-format hint | `read_doc` on another extension | The refusal names the supported formats and the legacy `.xls` conversion hint instead of a generic read error. | Shipped |
25
+ | Lazy parsers | `read_doc` | Only the format you read loads a library, so registration costs nothing at startup. | Shipped |
26
+ | Code-point slicing | `read_doc` (`startIndex`, `maxChars`) | CJK and emoji stay well-formed across a page boundary. | Shipped |
27
+
11
28
  ## Quickstart
12
29
 
13
30
  ```bash
14
- pi --extension ./extensions/file-read # this extension only
15
- pi -e . # the package from this checkout
16
- pi install ./ # install the package for a user
31
+ pi --extension ./extensions/file-read # load just this extension
32
+ pi -e . # load the whole @gavin-hu/my-pi-agent package
33
+ pi install ./ # install the package
17
34
  ```
18
35
 
19
36
  The extraction libraries are ordinary dependencies, so a normal install brings
@@ -23,31 +40,15 @@ tool directly:
23
40
  ```jsonc
24
41
  { "path": "docs/spec.pdf" }
25
42
  { "path": "docs/spec.pdf", "startIndex": 40000, "maxChars": 40000 }
26
- { "path": "report.xlsx" }
27
43
  ```
28
44
 
29
- ## What it does
30
-
31
- - Extracts plain text from `.pdf`, `.docx`, `.doc`, `.odt`, `.rtf`, and `.xlsx`
32
- files for the model to read.
33
- - Pages long files: when output is truncated, call again with the `startIndex`
34
- named in the note.
35
- - Slices by code point, so CJK and emoji stay well-formed.
36
- - Reads only inside the effective working directory, resolving symlinks so a
37
- link cannot escape it.
38
- - Loads a format's parser only when that format is actually read, so registration
39
- costs nothing at startup.
40
-
41
45
  ## Tool
42
46
 
43
- `read_doc` — direct exposure, active by default, read-only, closed-world
44
- (no network).
45
-
46
- How the call and its result look in the transcript — the call line as a headline
47
- and the body indented under it:
48
-
49
47
  <img src="../../assets/tools/read_doc.png" alt="The read_doc tool's result: the call line and an indented body with the file path, format, size, and the extracted text preview" width="100%">
50
48
 
49
+ `read_doc` — direct exposure, active by default, read-only, closed-world (no
50
+ network). It takes one file and returns that file's text.
51
+
51
52
  | Parameter | Type | Meaning |
52
53
  |---|---|---|
53
54
  | `path` | string, required, 1–4096 chars | Path to a `.pdf`, `.docx`, `.doc`, `.odt`, `.rtf`, or `.xlsx`, relative to the working directory (or absolute inside it). |
@@ -74,14 +75,8 @@ the text says what the file was.
74
75
 
75
76
  Errors are thrown for: a missing or non-file path, a path outside the root, an
76
77
  unsupported extension (with a targeted hint for legacy `.xls`), a disabled
77
- format, an over-size file, and a parser that rejects the file. The extension
78
- installs nothing.
79
-
80
- ## Behaviour by mode
81
-
82
- The tool behaves identically in `tui`, RPC, JSON, and print modes; it has no
83
- widgets or status output. `renderCall` / `renderResult` affect only the
84
- interactive transcript (and HTML exports).
78
+ format, an over-size file, and a parser that rejects the file. No external
79
+ executable is installed.
85
80
 
86
81
  The call line carries the path (`~`-shortened, OSC-8 linked when the terminal
87
82
  supports it) and any paging options. The result line drops the path — it is
@@ -117,11 +112,18 @@ applied, so the theme's own ANSI codes stay intact. A host-rejected call (a
117
112
  permission block, a validation failure) renders its own message through the same
118
113
  sanitized error path.
119
114
 
115
+ ## Behaviour by mode
116
+
117
+ The tool behaves identically in `tui`, RPC, JSON, and print modes; it has no
118
+ widgets or status output. `renderCall` / `renderResult` affect only the
119
+ interactive transcript (and HTML exports).
120
+
120
121
  ## Configuration
121
122
 
122
- The `file-read` section of the shared `bytefly.json`, loaded from
123
- `~/.pi/agent/bytefly.json` then `<cwd>/.pi/bytefly.json` (project wins), each
124
- merged over the defaults and clamped:
123
+ The keys live in the `file-read` section of the shared `bytefly.json`; resolution
124
+ rules are in
125
+ [`../configuration.md`](../configuration.md#file-read--document-and-spreadsheet-extraction).
126
+ Each value is merged over the defaults and clamped:
125
127
 
126
128
  | Key | Default | Clamp | Meaning |
127
129
  |---|---|---|---|
@@ -163,16 +165,17 @@ reads only `file-read`, and `bun run config:migrate` renames an existing
163
165
 
164
166
  ## Extraction
165
167
 
166
- Each extractor builds a plain-text document from the format's bytes:
168
+ Each extractor builds a plain-text document from the format's bytes. Every
169
+ format is enabled by default; `file-read.formats` turns one off.
167
170
 
168
- | Format | Library | Output normalization |
169
- |---|---|---|
170
- | PDF | `unpdf` (`extractText`, `mergePages: true`) | Every page in reading order, pages separated by a blank line. No OCR: a scanned or image-only PDF yields little or no text. |
171
- | DOCX | `mammoth` (`extractRawText`) | The body story, paragraphs separated by blank lines. Headers, footers, footnotes, and comments are not included. |
172
- | DOC | `word-extractor` (OLE body) | The classic Word body story. |
173
- | ODT | `fflate` + `fast-xml-parser` | `content.xml` is unzipped and walked: paragraphs and headings are lines, table cells are tab-separated, and `text:s` / `text:tab` / `text:line-break` become their whitespace. |
174
- | RTF | `rtf-parser` | Paragraphs are flattened to their span text; formatting is dropped. |
175
- | XLSX | `exceljs` | One `[Sheet]` heading per non-empty worksheet, then tab-separated rows. Dates and numbers use the workbook's displayed text; formulas show their computed value. Cell formatting is dropped. |
171
+ | Format | Extensions | Library | Output normalization |
172
+ |---|---|---|---|
173
+ | PDF | `.pdf` | `unpdf` (`extractText`, `mergePages: true`) | Every page in reading order, pages separated by a blank line. No OCR: a scanned or image-only PDF yields little or no text. |
174
+ | DOCX | `.docx` | `mammoth` (`extractRawText`) | The body story, paragraphs separated by blank lines. Headers, footers, footnotes, and comments are not included. |
175
+ | DOC | `.doc` | `word-extractor` (OLE body) | The classic Word body story. |
176
+ | ODT | `.odt` | `fflate` + `fast-xml-parser` | `content.xml` is unzipped and walked: paragraphs and headings are lines, table cells are tab-separated, and `text:s` / `text:tab` / `text:line-break` become their whitespace. |
177
+ | RTF | `.rtf` | `rtf-parser` | Paragraphs are flattened to their span text; formatting is dropped. |
178
+ | XLSX | `.xlsx` | `exceljs` | One `[Sheet]` heading per non-empty worksheet, then tab-separated rows. Dates and numbers use the workbook's displayed text; formulas show their computed value. Cell formatting is dropped. |
176
179
 
177
180
  The tool is pageable across the whole extracted text, so a word-processing
178
181
  document or a workbook is read in the same `startIndex` / `maxChars` way as a PDF.
@@ -181,17 +184,6 @@ Each call is bounded by `extractTimeoutMs` (30 s by default). A parser that
181
184
  outlasts the deadline is abandoned — the read fails with the limit named, and a
182
185
  late result is discarded — so a pathological file cannot park the turn.
183
186
 
184
- ## Supported formats
185
-
186
- | Format | Extensions | Extractor | Enabled by default |
187
- |---|---|---|---|
188
- | PDF | `.pdf` | `unpdf` | yes |
189
- | DOCX | `.docx` | `mammoth` | yes |
190
- | DOC | `.doc` | `word-extractor` | yes |
191
- | ODT | `.odt` | `fflate` + `fast-xml-parser` | yes |
192
- | RTF | `.rtf` | `rtf-parser` | yes |
193
- | XLSX | `.xlsx` | `exceljs` | yes |
194
-
195
187
  ## Adding a format
196
188
 
197
189
  The format table in `formats.ts` is the single source of truth; the handler,
@@ -208,7 +200,7 @@ config defaults, and error listing all derive from it.
208
200
  3. Add a `ReadFormat` entry to `FORMATS` and widen the `id` union.
209
201
  4. Add the new extractor to `extract/extract.test.ts` with a real minimal
210
202
  fixture, and extend `tool.test.ts` for format selection and config.
211
- 5. Add a row to the tables above.
203
+ 5. Add a row to the format table above.
212
204
 
213
205
  `tool.ts`, `paging.ts`, and the config normalizer need no changes.
214
206
 
@@ -318,4 +310,4 @@ builds a genuine PDF, DOCX, and ODT; XLSX is written with `exceljs`; legacy `.do
318
310
  reads the committed `files/legacy.doc`), so the libraries are exercised, not
319
311
  stubbed. `tool.test.ts` injects fake extractors for behavioural cases and reads a
320
312
  real PDF and a real `.doc` end to end; `test/helpers/fixtures/file-read.ts`
321
- provides the scratch root and an escape symlink.
313
+ provides the scratch root and an escape symlink.
@@ -1,7 +1,27 @@
1
1
  # goal — a persistent session objective for Pi
2
2
 
3
3
  A single high-level objective for the session, kept visible and restated before
4
- every turn. The goal is the *what*; the [`todo`](../todo/) list is the *how*.
4
+ every turn. The goal is the *what*; the [`todo`](./todo.md) list is the *how*.
5
+
6
+ ## Capabilities
7
+
8
+ Status records what a reader can rely on: **Shipped** is in a tagged release, **Unreleased** is complete in the current tree but not yet tagged, and **Partial** has one released part and one unreleased part. Rows are ordered by user value, highest first.
9
+
10
+ | Capability | How | What you get | Status |
11
+ |---|---|---|---|
12
+ | Goal recording | `goal`, `/goal set`, `/goal raw`, `/goal clear` | The objective is replaced whole, cleared by an empty value, and recorded verbatim when you ask for it. | Shipped |
13
+ | Phase tracking | `goal`, `/goal done` | The goal is `planning`, `active`, or `achieved`, and only the workflow can open a draft. | Shipped |
14
+ | Plan-first question | `goal`, `/goal <prompt>`, `/goal raw` | One dialog decides whether a new goal enters plan mode before it is pursued. | Shipped |
15
+ | Plan-mode allowlist | `goal` in plan mode | The model can record or update the objective while plan mode keeps the rest of the session read-only. | Shipped |
16
+ | Plan-phase settlement | `planning` goal, `exit_plan_mode` | Leaving plan mode without approval makes the draft `active`; approving the plan writes its `## Goal` into it. | Shipped |
17
+ | Understanding loop | `/goal <prompt>`, `/goal set`, `/goal edit` | A typed goal is restated, questioned when it is unclear, and confirmed in the editor before it is recorded. | Shipped |
18
+ | Goal rail | the `Goal · active · …` widget, the `achieved` key | The objective stays one line above the editor while it is pursued. | Shipped |
19
+ | Per-turn restatement | the `[SESSION GOAL]` reminder | The model reads the pursued objective before each turn, without it entering the transcript. | Shipped |
20
+ | Open-list check | `goal`, `/goal done` | Completing the goal names the todo items the list still has open. | Shipped |
21
+ | Branch-following state | tool-result `details`, the `goal` session entry | The goal survives `/resume` and `/tree`, and abandoned branches never leak in. | Shipped |
22
+ | Command help | `/goal help`, `/goal -h`, `/goal --help` | The verb list and the usage of each form, without changing the goal. | Shipped |
23
+
24
+ ## Quickstart
5
25
 
6
26
  ```bash
7
27
  pi --extension ./extensions/goal # load just this extension
@@ -9,86 +29,29 @@ pi -e . # load the whole @gavin-hu/my-pi-agent packa
9
29
  pi install ./ # install the package
10
30
  ```
11
31
 
12
- ## Tool rendering
32
+ ```text
33
+ /goal # show the goal, or the usage when none is set
34
+ /goal Refactor the parser to support streaming input
35
+ ```
13
36
 
14
- How the `goal` call and its result look in the transcript — the call line as a headline and the body indented under it:
37
+ The model records the same objective with `goal`; the model-facing tool never
38
+ runs the understanding loop.
15
39
 
16
- <img src="../../assets/tools/goal.png" alt="The goal tool's result: the call line and an indented body with the Goal header and the objective body" width="100%">
40
+ ## Tool
17
41
 
18
- ## What it does
19
-
20
- - Registers one model-callable tool, `goal`, that records the session
21
- objective. Send the complete objective every call; an empty objective clears
22
- it.
23
- - Tracks the goal's **phase**, not a flag: `planning` while a plan is being
24
- designed, `active` once the objective is final, `achieved` when it is done.
25
- A model-issued call may only send `active` or `achieved`, so one call can never
26
- claim a plan that does not exist.
27
- - **Asks once whether to plan first** when a new goal is recorded: accepting
28
- enters plan mode and stores the objective as `planning`, declining stores it
29
- `active`. The `plan` extension owns the mode; the question and its consent
30
- sentence come from `lib/plan-entry.ts`, the same ones `enter_plan_mode` shows,
31
- so the user answers once. The question is skipped for an update to a goal that
32
- is already pursued, without a dialog UI, or when `plan` is not loaded.
33
- - **Settles a draft whose plan never arrived.** If plan mode is left without an
34
- approved plan — the toggle, the shortcut, a resumed branch — the `planning`
35
- goal becomes `active` with its own wording on the next observation, so the
36
- phase can never get stuck. Approval is different: `plan` sets the goal from the
37
- plan's own `## Goal` section (see the [`plan`](../plan/) docs).
38
- - Achieves a goal over an open list knowingly: completing it while the branch's
39
- todo list still has open items names the count in the result (`1 todo item is
40
- still open…`), so the model closes the *how* instead of declaring the *what*
41
- done over an unfinished list; `/goal done` appends the same sentence to its
42
- notice. The count is read from the branch through `lib/todo-state.ts`; the
43
- structured result is unchanged.
44
- - Shows a persistent one-line rail above the editor whenever a goal exists,
45
- label-first to match the [`todo`](../todo/) widget, pinned at the top of the
46
- `Goal / Todos` stack when both are loaded:
47
-
48
- ```
49
- Goal · active · Refactor the parser to support streaming input and ship it …
50
- ```
51
-
52
- Once achieved the goal is hidden from the widget by default, matching a
53
- completed todo list; the transcript keeps the record. Set `achieved: "show"`
54
- in the `goal` section of `bytefly.json` to keep drawing the dimmed line
55
- instead.
56
-
57
- - Restates a **pursued** goal to the model before each turn (an invisible
58
- `[SESSION GOAL]` message) — `planning` and `active` alike, so a plan is designed
59
- against the objective — and stops once it is achieved.
60
- - Stores tool-set goals in tool-result `details` and command-set goals as a
61
- `goal` session entry, so either way the goal follows the active session branch
62
- and survives `/resume` and `/tree` — a `planning` draft included, since it is
63
- written the same way. Abandoned branches never leak into the current goal.
64
- - Understands the goal before recording it: `/goal <prompt>` asks the session
65
- model to restate the goal as it understands it and, when it cannot yet, to ask
66
- up to three questions — which the command asks directly through host dialogs
67
- (`select` / `input`). The answers are carried into the editor under the
68
- restatement, below a `--- notes (not saved) ---` line, so accepting the
69
- restatement as-is records the goal alone; folding an answer into the sentence
70
- above the line keeps it. `/goal set
71
- <prompt>` reuses the loop but records without starting a turn, `/goal edit`
72
- changes the current goal in place, and `/goal raw <prompt>` records the typed
73
- words verbatim. The loop is best-effort and needs a dialog UI plus a model; the
74
- `goal` tool stays the silent setter.
42
+ <img src="../../assets/tools/goal.png" alt="The goal tool's result: the call line and an indented body with the Goal header and the objective body" width="100%">
75
43
 
76
- ## Tool
44
+ `goal` — `exposure: "direct"` (the default), `executionMode: "sequential"`,
45
+ read-write, closed-world, idempotent. It is a whole-goal replacement: the model
46
+ sends the complete objective and the previous goal is discarded.
77
47
 
78
- `goal` is a whole-goal replacement tool. It declares an `outputSchema` and
79
- returns matching `structuredContent` (`{ goal, action, error? }`, with
80
- `goal: null` after a clear), so codemode/scripts can read the goal as data.
48
+ | Parameter | Type | Meaning |
49
+ |---|---|---|
50
+ | `objective` | string, required | The complete objective, one line. Pass `""` to clear. |
51
+ | `status` | `active` \| `achieved` | `achieved` completes the goal; `active` records a final objective; omit it to let the workflow ask whether to plan first. `planning` is the workflow's to set; a call cannot. |
81
52
 
82
- | Field | Value |
83
- |---|---|
84
- | `name` | `goal` |
85
- | `objective` | The complete objective, one line. Pass `""` to clear. |
86
- | `status` | `achieved` completes the goal; `active` records a final objective; omit it to let the workflow ask whether to plan first. `planning` is the workflow's to set; a call cannot. |
87
- | `exposure` | `direct` (default). |
88
- | `executionMode` | `sequential` — calls share the in-memory goal. |
89
- | `annotations` | `idempotentHint: true` (a snapshot write), not read-only. |
90
- | `outputSchema` | `GoalResult` — `{ goal, action, error? }`, also returned as `structuredContent`. |
91
- | `promptGuidelines` | Carry the when-to-use rule and the mark-achieved-with-a-blank-objective rule; the replacement/clear semantics live in the description. |
53
+ Result (`details` / `structuredContent`): `{ goal, action, error? }`, with
54
+ `goal: null` after a clear.
92
55
 
93
56
  Validation (a violation returns an error result and leaves the goal unchanged):
94
57
  a known `status`, an objective of at most 2000 characters, and a non-blank
@@ -109,19 +72,22 @@ whether the todos were written by the model, by `/todos`, or by an approved
109
72
  plan. It is text only: `details`, `structuredContent`, and the transcript header
110
73
  are unaffected.
111
74
 
112
- `goal` is allowlisted in `plan-mode` (alongside `todo`), so the model can record
113
- or update the objective during read-only exploration without leaving plan mode.
114
- Plan mode also reads the goal: it restates a pursued one in its injected context,
115
- it promotes a `planning` goal when a plan is approved, and it offers to record the
116
- plan's `## Goal` when the session has none (`lib/goal-state.ts` is the reader). An
117
- `active` or `achieved` goal — a final objective — is never rewritten by an
118
- approval.
75
+ The transcript result is the status header alone, directly under the call line
76
+ that names the objective:
77
+
78
+ ```
79
+ goal → set: Refactor the parser to support streaming input
80
+ Goal · active
81
+ ```
82
+
83
+ The renderer reuses the slot's component (`context.lastComponent`) rather than
84
+ rebuilding it each render.
119
85
 
120
- ### `/goal` command
86
+ ## Commands
121
87
 
122
88
  The command has no tool result, so it persists each change as a `goal` custom
123
89
  entry; `reconstructGoal` replays both entry kinds in branch order through
124
- `lib/goal-state.ts`, so its readers agree.
90
+ `lib/goal-state.ts`, so its readers agree. `/goal` completes each verb.
125
91
 
126
92
  | Command | What it does |
127
93
  |---|---|
@@ -134,50 +100,28 @@ entry; `reconstructGoal` replays both entry kinds in branch order through
134
100
  | `/goal clear` | Clear the goal. |
135
101
  | `/goal help` (or `-h` / `--help`) | Show the command usage. |
136
102
 
137
- `/goal <prompt>` runs the loop only when a dialog UI and a session model are
138
- present (TUI or RPC). In print and JSON mode the typed text is set and run
139
- unchanged. A plain-text clarifier reply is treated as a restatement, so a model
140
- that ignores the JSON instruction still works; a failed, timed-out, or unusable
141
- comprehension notices the user and keeps the typed text; and a dismissed
142
- question or editor cancels the command, changing nothing. The questions are asked
143
- one at a time with `ctx.ui.select` (for a question with options) and
144
- `ctx.ui.input` (for a free-form one); the command never calls
145
- `ask_user_question`. A bare `/goal set` and `/goal edit` need a dialog UI —
146
- without one they show the usage instead. `/goal raw` never needs one for the
147
- words, but the plan question does need a dialog: both run variants ask it only
148
- where a dialog UI exists, and record `active` otherwise. The record-only paths
149
- never ask; a goal recorded while plan mode is already on is still a draft
150
- (`planning`), because that is what it is.
103
+ The questions and the confirm step are host dialogs, forwarded by RPC, so the
104
+ loop needs one; in print and JSON mode the typed text is set and run unchanged.
105
+ A dismissed question or editor cancels the command, changing nothing. The
106
+ [`plan`](./plan.md) extension owns plan mode, so the plan question is skipped
107
+ when it is not loaded, and a bare `/goal set` shows the usage instead of an
108
+ editor when there is no dialog UI.
151
109
 
152
110
  ## Behaviour by mode
153
111
 
154
112
  The tool works in every mode. The persistent widget requires interactive
155
113
  (`tui`) mode; the reminder and the `/goal` command work everywhere. The
156
- `/goal <prompt>` runs the loop: its model call uses a cancellable `tui` spinner
157
- in interactive mode and runs directly in RPC; the questions and the confirm step
158
- are host dialogs, forwarded by RPC. In print and JSON mode the loop is skipped
159
- and the typed text is sent. The plan question is a host dialog too, so it is
160
- asked only in TUI and RPC, and skipped when the `plan` extension is not loaded.
161
- The widget is always a single line; the transcript result is the status header
162
- alone, directly under the call line that names the objective:
163
-
164
- ```
165
- goal → set: Refactor the parser to support streaming input
166
- Goal · active
167
- ```
168
-
169
- The renderer reuses the slot's component (`context.lastComponent`) rather than
170
- rebuilding it each render. A `planning` goal shows the same rail with a warning
171
- status word and is restated; achieved goals are hidden from the widget by
172
- default (restore the dimmed line with `achieved: "show"`), and they are no
173
- longer restated to the model. While a dock screen is open (`/todos`, `/jobs`,
174
- `/rewind`, or the `question` questionnaire), the rail is hidden and
114
+ `/goal <prompt>` comprehension call uses a cancellable `tui` spinner in
115
+ interactive mode and runs directly in RPC. When a goal exists, the widget is
116
+ always a single line above the editor; while a dock screen is open (`/todos`,
117
+ `/jobs`, `/rewind`, or the `question` questionnaire), the rail is hidden and
175
118
  returns on close.
176
119
 
177
120
  ## Configuration
178
121
 
179
122
  The widget presentation is configurable from the `goal` section of the shared
180
- `bytefly.json` (global document, then project document, project wins):
123
+ `bytefly.json`; resolution rules are in
124
+ [`../configuration.md`](../configuration.md#goal--the-goal-widget).
181
125
 
182
126
  | Key | Default | Meaning |
183
127
  |---|---|---|
@@ -186,6 +130,53 @@ The widget presentation is configurable from the `goal` section of the shared
186
130
  Malformed documents and invalid values are ignored, and goal behavior never
187
131
  depends on config.
188
132
 
133
+ ## Plan entry
134
+
135
+ A new goal's phase is decided once, with one dialog. Accepting enters plan mode
136
+ and stores the objective as `planning`, so a plan can be designed against it;
137
+ declining stores it `active`, the final objective. The question is skipped for
138
+ an update to a goal that is already pursued, without a dialog UI, and when
139
+ `plan` is not loaded. The question and its consent sentence come from
140
+ `lib/plan-entry.ts`, the same ones `enter_plan_mode` shows, so the user answers
141
+ once.
142
+
143
+ A draft whose plan never arrived is settled rather than stranded. If plan mode is
144
+ left without an approved plan — the toggle, the shortcut, a resumed branch — the
145
+ `planning` goal becomes `active` with its own wording on the next observation, so
146
+ the phase can never get stuck. Approval is different: `plan` sets the goal from
147
+ the plan's own `## Goal` section (see the [`plan`](./plan.md) docs), and only a
148
+ `planning` goal is eligible, so a final objective is the user's.
149
+
150
+ `goal` is allowlisted in `plan-mode` (alongside `todo`), so the model can record
151
+ or update the objective during read-only exploration without leaving plan mode.
152
+ Plan mode also reads the goal: it restates a pursued one in its injected context,
153
+ it promotes a `planning` goal when a plan is approved, and it offers to record the
154
+ plan's `## Goal` when the session has none (`lib/goal-state.ts` is the reader). An
155
+ `active` or `achieved` goal — a final objective — is never rewritten by an
156
+ approval.
157
+
158
+ ## The understanding loop
159
+
160
+ `/goal <prompt>` asks the session model to restate the goal as it understands it
161
+ and, when it cannot yet, to ask up to three questions, which the command asks
162
+ directly through host dialogs (`select` / `input`). The answers are carried into
163
+ the editor under the restatement, below a `--- notes (not saved) ---` line, so
164
+ accepting the restatement as-is records the goal alone; folding an answer into
165
+ the sentence above the line keeps it. `/goal set <prompt>` reuses the loop but
166
+ records without starting a turn, and `/goal raw <prompt>` records the typed words
167
+ verbatim.
168
+
169
+ The loop is best-effort and needs a dialog UI plus a model. A plain-text clarifier
170
+ reply is treated as a restatement, so a model that ignores the JSON instruction
171
+ still works; a failed, timed-out, or unusable comprehension notices the user and
172
+ keeps the typed text. The questions are asked one at a time with `ctx.ui.select`
173
+ (for a question with options) and `ctx.ui.input` (for a free-form one); the
174
+ command never calls `ask_user_question`. `/goal raw` never needs a dialog for the
175
+ words, but the plan question does: both run variants ask it only where a dialog
176
+ UI exists, and record `active` otherwise. The record-only paths never ask, and a
177
+ goal recorded while plan mode is already on is still a draft (`planning`), because
178
+ that is what it is. The `goal` tool stays the silent setter.
179
+
189
180
  ## Non-goals
190
181
 
191
182
  - No multiple or nested goals — one objective per session.
@@ -246,19 +237,12 @@ depends on config.
246
237
  record and stops the reminder, which a single empty-clear cannot express.
247
238
  - **The command runs the understanding loop.** `/goal <prompt>` is the
248
239
  user-driven path: one comprehension call (`clarify-prompt.ts` + `clarify.ts`)
249
- returns `{ understood, goal, questions }`. An understood goal opens a prefilled
250
- `ctx.ui.editor`, then `pi.sendUserMessage` sets and runs it. A not-understood
251
- goal has its questions asked through host dialogs, and its answers ride into
252
- the editor as `Also:` notes below a sentinel line that is cut away on save:
253
- no second model call rewrites the goal, so the user is the one who folds an
254
- answer into the sentence, above the line, when they want it kept.
255
- `/goal set`
256
- reuses the loop but skips the send; a bare `/goal set` or `/goal edit` opens
257
- the editor prefilled with the current goal; and `/goal raw` skips the loop
258
- entirely. The loop is best-effort (a failure keeps the typed text) and a
259
- dismissed dialog cancels the command, so nothing is half-applied. No
260
- conversation history is sent: the model must not invent scope the user did not
261
- state. The `goal` tool remains the model's setter, with no comprehension loop.
240
+ returns `{ understood, goal, questions }`. No second model call rewrites the
241
+ goal, so the user is the one who folds an answer into the sentence, above the
242
+ sentinel line, when they want it kept; and no conversation history is sent, so
243
+ the model cannot invent scope the user did not state. The loop is best-effort
244
+ and a dismissed dialog cancels the command, so nothing is half-applied. The
245
+ `goal` tool remains the model's setter, with no comprehension loop.
262
246
  - **State in `details` and a custom entry.** A `goal` tool call stores the goal
263
247
  in its result's `details`; the `/goal` command appends a `goal` custom entry
264
248
  (`pi.appendEntry`). `reconstructGoal` reads the branch through