@gavin-hu/my-pi-agent 0.21.0 → 0.22.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 (254) hide show
  1. package/README.md +46 -31
  2. package/README.zh-CN.md +40 -26
  3. package/assets/screens/board.png +0 -0
  4. package/assets/screens/chrome.png +0 -0
  5. package/assets/screens/file-browse.png +0 -0
  6. package/assets/screens/plan-review.png +0 -0
  7. package/assets/screens/plan.png +0 -0
  8. package/docs/extensions/README.md +22 -24
  9. package/docs/extensions/board.md +361 -147
  10. package/docs/extensions/bytefly.md +152 -111
  11. package/docs/extensions/check.md +15 -17
  12. package/docs/extensions/context.md +35 -15
  13. package/docs/extensions/file-browse.md +30 -31
  14. package/docs/extensions/file-open.md +9 -11
  15. package/docs/extensions/file-read.md +12 -14
  16. package/docs/extensions/goal.md +15 -17
  17. package/docs/extensions/job.md +54 -42
  18. package/docs/extensions/memory.md +17 -19
  19. package/docs/extensions/plan.md +166 -126
  20. package/docs/extensions/question.md +16 -18
  21. package/docs/extensions/rewind.md +17 -19
  22. package/docs/extensions/serve.md +34 -18
  23. package/docs/extensions/session-bus.md +38 -16
  24. package/docs/extensions/subagent.md +23 -25
  25. package/docs/extensions/team.md +72 -33
  26. package/docs/extensions/todo.md +18 -18
  27. package/docs/extensions/watch.md +31 -31
  28. package/docs/extensions/web-access.md +2 -4
  29. package/docs/extensions/wechat.md +34 -35
  30. package/docs/extensions/worktree.md +56 -50
  31. package/extensions/board/commands.ts +56 -15
  32. package/extensions/board/format.ts +43 -24
  33. package/extensions/board/index.ts +5 -4
  34. package/extensions/board/input.ts +4 -9
  35. package/extensions/board/join.ts +4 -40
  36. package/extensions/board/lazy-page.ts +40 -0
  37. package/extensions/board/page.ts +13 -11
  38. package/extensions/board/result.ts +30 -16
  39. package/extensions/board/rows.ts +62 -25
  40. package/extensions/board/schema.ts +42 -10
  41. package/extensions/board/snapshot.ts +2 -1
  42. package/extensions/board/tone.ts +22 -0
  43. package/extensions/board/tools.ts +131 -44
  44. package/extensions/board/types.ts +36 -32
  45. package/extensions/board/view/card.ts +229 -0
  46. package/extensions/board/view/client.ts +33 -15
  47. package/extensions/board/view/dialog.ts +169 -6
  48. package/extensions/board/view/empty.ts +47 -0
  49. package/extensions/board/view/keys.ts +13 -2
  50. package/extensions/board/view/live.ts +89 -21
  51. package/extensions/board/view/names.ts +13 -4
  52. package/extensions/board/view/page.ts +23 -9
  53. package/extensions/board/view/render.ts +208 -271
  54. package/extensions/board/view/styles.ts +123 -31
  55. package/extensions/board/view/team.ts +297 -76
  56. package/extensions/bytefly/block/fallback.ts +1 -1
  57. package/extensions/bytefly/doctor/runtime.ts +5 -2
  58. package/extensions/bytefly/footer/collect/label.ts +60 -0
  59. package/extensions/bytefly/footer/collect/snapshot.ts +8 -1
  60. package/extensions/bytefly/footer/config.ts +4 -2
  61. package/extensions/bytefly/footer/register.ts +33 -4
  62. package/extensions/bytefly/footer/render/lines.ts +7 -4
  63. package/extensions/bytefly/footer/render/segments.ts +37 -10
  64. package/extensions/bytefly/footer/render/status.ts +0 -11
  65. package/extensions/bytefly/footer/types.ts +7 -0
  66. package/extensions/bytefly/header/config.ts +1 -1
  67. package/extensions/bytefly/index.ts +32 -6
  68. package/extensions/bytefly/mode/editor.ts +115 -0
  69. package/extensions/bytefly/panel/commands.ts +210 -120
  70. package/extensions/bytefly/panel/format.ts +4 -4
  71. package/extensions/bytefly/panel/settings.ts +73 -6
  72. package/extensions/bytefly/panel/tui/keys.ts +2 -6
  73. package/extensions/bytefly/update/cache.ts +1 -1
  74. package/extensions/bytefly/update/register.ts +4 -4
  75. package/extensions/bytefly/working/config.ts +5 -7
  76. package/extensions/bytefly/working/format.ts +10 -13
  77. package/extensions/bytefly/working/register.ts +22 -27
  78. package/extensions/check/commands.ts +3 -2
  79. package/extensions/check/{tui.ts → view/check-screens.ts} +14 -19
  80. package/extensions/context/commands.ts +2 -2
  81. package/extensions/context/{tui.ts → view/context-report.ts} +51 -19
  82. package/extensions/file-browse/index.ts +67 -36
  83. package/extensions/file-read/render.ts +4 -4
  84. package/extensions/goal/plan-route.ts +2 -2
  85. package/extensions/job/child.ts +527 -0
  86. package/extensions/job/clock.ts +142 -0
  87. package/extensions/job/commands.ts +4 -2
  88. package/extensions/job/config.ts +8 -1
  89. package/extensions/job/format.ts +22 -1
  90. package/extensions/job/log-writer.ts +13 -2
  91. package/extensions/job/registry.ts +63 -7
  92. package/extensions/job/render.ts +34 -0
  93. package/extensions/job/runtime.ts +296 -655
  94. package/extensions/job/session.ts +132 -19
  95. package/extensions/job/store.ts +7 -7
  96. package/extensions/job/tools.ts +3 -3
  97. package/extensions/job/types.ts +47 -0
  98. package/extensions/job/{tui.ts → view/jobs-screen.ts} +19 -73
  99. package/extensions/job/waiters.ts +44 -1
  100. package/extensions/memory/commands.ts +4 -2
  101. package/extensions/memory/{tui.ts → view/memory-picker.ts} +12 -12
  102. package/extensions/plan/commands.ts +12 -9
  103. package/extensions/plan/format.ts +8 -2
  104. package/extensions/plan/git-bash.ts +115 -16
  105. package/extensions/plan/index.ts +53 -52
  106. package/extensions/plan/plan-index.ts +4 -2
  107. package/extensions/plan/plans.ts +5 -54
  108. package/extensions/plan/policy.ts +8 -10
  109. package/extensions/plan/review-lazy.ts +85 -0
  110. package/extensions/plan/review.ts +2 -2
  111. package/extensions/plan/runtime.ts +49 -39
  112. package/extensions/plan/sections.ts +1 -1
  113. package/extensions/plan/steps.ts +3 -3
  114. package/extensions/plan/todos.ts +1 -1
  115. package/extensions/plan/tools/cancel.ts +27 -62
  116. package/extensions/plan/tools/enter.ts +55 -81
  117. package/extensions/plan/tools/exit.ts +221 -238
  118. package/extensions/plan/tools/refusal.ts +18 -0
  119. package/extensions/plan/tools/steps.ts +75 -92
  120. package/extensions/plan/tools/write.ts +77 -120
  121. package/extensions/plan/tools.ts +188 -30
  122. package/extensions/plan/tui.ts +5 -5
  123. package/extensions/plan/types.ts +48 -5
  124. package/extensions/plan/web-client.ts +3 -0
  125. package/extensions/plan/web-lazy.ts +53 -0
  126. package/extensions/plan/web-markdown.ts +9 -67
  127. package/extensions/plan/web-payload.ts +3 -3
  128. package/extensions/plan/web-router.ts +18 -9
  129. package/extensions/plan/web.ts +1 -12
  130. package/extensions/question/tools.ts +6 -2
  131. package/extensions/question/{tui.ts → view/question-screen.ts} +11 -11
  132. package/extensions/rewind/commands/commands.ts +6 -3
  133. package/extensions/rewind/state/runtime.ts +1 -1
  134. package/extensions/rewind/{view/status.ts → status.ts} +4 -4
  135. package/extensions/rewind/view/review-tui.ts +4 -4
  136. package/extensions/serve/config.ts +10 -8
  137. package/extensions/serve/index.ts +17 -3
  138. package/extensions/serve/registry.ts +26 -17
  139. package/extensions/session-bus/commands.ts +11 -6
  140. package/extensions/session-bus/join-request.ts +2 -1
  141. package/extensions/session-bus/mail-watch.ts +26 -17
  142. package/extensions/session-bus/protocol.ts +15 -3
  143. package/extensions/session-bus/resolve.ts +5 -7
  144. package/extensions/session-bus/runtime.ts +40 -12
  145. package/extensions/session-bus/store.ts +101 -19
  146. package/extensions/session-bus/types.ts +19 -0
  147. package/extensions/session-bus/{tui.ts → view/session-list.ts} +16 -16
  148. package/extensions/subagent/agents.ts +1 -1
  149. package/extensions/subagent/commands.ts +4 -2
  150. package/extensions/subagent/format.ts +15 -15
  151. package/extensions/subagent/index.ts +1 -1
  152. package/extensions/subagent/render.ts +3 -3
  153. package/extensions/subagent/{background-tui.ts → view/agents-board.ts} +11 -11
  154. package/extensions/team/commands.ts +24 -7
  155. package/extensions/team/dispatch.ts +18 -4
  156. package/extensions/team/format.ts +17 -2
  157. package/extensions/team/index.ts +9 -8
  158. package/extensions/team/schema.ts +6 -5
  159. package/extensions/team/tools.ts +107 -45
  160. package/extensions/team/types.ts +3 -2
  161. package/extensions/todo/commands.ts +4 -2
  162. package/extensions/todo/format.ts +11 -0
  163. package/extensions/todo/render.ts +57 -0
  164. package/extensions/todo/runtime.ts +1 -1
  165. package/extensions/todo/tools.ts +1 -1
  166. package/extensions/todo/view/todo-screen.ts +122 -0
  167. package/extensions/todo/widget.ts +47 -0
  168. package/extensions/watch/commands.ts +4 -2
  169. package/extensions/watch/format.ts +36 -7
  170. package/extensions/watch/render.ts +57 -0
  171. package/extensions/watch/runtime.ts +27 -22
  172. package/extensions/watch/tools.ts +4 -4
  173. package/extensions/watch/{tui.ts → view/watch-screen.ts} +18 -80
  174. package/extensions/web-access/fetch/tool.ts +12 -4
  175. package/extensions/wechat/commands/commands.ts +18 -14
  176. package/extensions/wechat/index.ts +72 -37
  177. package/extensions/wechat/state/credentials.ts +1 -1
  178. package/extensions/wechat/state/state.ts +1 -1
  179. package/extensions/wechat/text/status.ts +112 -0
  180. package/extensions/wechat/types.ts +2 -2
  181. package/extensions/wechat/view/status-screen.ts +4 -103
  182. package/extensions/worktree/commands.ts +11 -9
  183. package/extensions/worktree/enter.ts +2 -2
  184. package/extensions/worktree/exit.ts +2 -2
  185. package/extensions/worktree/index.ts +5 -5
  186. package/extensions/worktree/screen.ts +3 -3
  187. package/extensions/worktree/status.ts +2 -2
  188. package/extensions/worktree/tools.ts +113 -116
  189. package/lib/README.md +27 -13
  190. package/lib/board/actions.ts +111 -0
  191. package/lib/board/artifact.ts +149 -0
  192. package/lib/board/card.ts +163 -52
  193. package/lib/board/config.ts +58 -0
  194. package/lib/board/contract.ts +113 -0
  195. package/lib/board/join.ts +38 -0
  196. package/lib/board/lanes.ts +182 -0
  197. package/lib/board/lead.ts +130 -0
  198. package/lib/board/limits.ts +78 -0
  199. package/lib/board/links.ts +31 -10
  200. package/lib/board/page-pointer.ts +1 -1
  201. package/lib/board/read.ts +83 -0
  202. package/lib/board/reason.ts +102 -0
  203. package/lib/board/round-text.ts +68 -6
  204. package/lib/board/round.ts +31 -15
  205. package/lib/board/rules.ts +151 -307
  206. package/lib/board/seat.ts +35 -0
  207. package/lib/board/session.ts +136 -54
  208. package/lib/board/skipped.ts +22 -0
  209. package/lib/board/steps.ts +487 -0
  210. package/lib/board/store.ts +87 -30
  211. package/lib/board/tone.ts +92 -0
  212. package/lib/board/view.ts +70 -58
  213. package/{extensions/watch → lib}/child.ts +40 -21
  214. package/lib/env.ts +65 -7
  215. package/lib/format.ts +13 -0
  216. package/lib/http/index.ts +14 -12
  217. package/lib/http/port.ts +15 -10
  218. package/lib/lazy.ts +45 -0
  219. package/lib/plan-entry.ts +39 -10
  220. package/lib/plans/paths.ts +82 -0
  221. package/lib/policy.ts +9 -6
  222. package/lib/report.ts +3 -2
  223. package/lib/requirements.ts +8 -8
  224. package/lib/session-label.ts +111 -5
  225. package/lib/session-roster.ts +43 -1
  226. package/lib/session-tool.ts +5 -4
  227. package/lib/stray-fields.ts +7 -2
  228. package/lib/tool-names.ts +5 -10
  229. package/lib/tui/README.md +23 -8
  230. package/lib/tui/chrome.ts +16 -15
  231. package/lib/tui/cursor.ts +18 -3
  232. package/lib/tui/glyphs.ts +9 -7
  233. package/lib/tui/tokens.ts +33 -2
  234. package/lib/tui/tool.ts +3 -3
  235. package/lib/web/README.md +21 -0
  236. package/lib/web/components.ts +5 -1
  237. package/lib/web/index.ts +1 -0
  238. package/lib/web/markdown.ts +83 -0
  239. package/lib/web/markup.ts +2 -1
  240. package/lib/web/runtime.ts +9 -10
  241. package/package.json +6 -1
  242. package/skills/init/SKILL.md +5 -5
  243. package/skills/next/SKILL.md +3 -5
  244. package/extensions/board/seat.ts +0 -28
  245. package/extensions/todo/tui.ts +0 -212
  246. package/lib/tui/index.ts +0 -34
  247. package/skills/project-docs/SKILL.md +0 -133
  248. package/skills/project-docs/references/audit.md +0 -131
  249. package/skills/project-docs/references/changelog.md +0 -105
  250. package/skills/project-docs/references/discovery.md +0 -127
  251. package/skills/project-docs/references/readme.md +0 -80
  252. package/skills/project-docs/references/release.md +0 -84
  253. package/skills/project-docs/references/roadmap.md +0 -93
  254. package/skills/project-docs/scripts/state.ts +0 -316
@@ -10,35 +10,34 @@ default, and from `bytefly.json` otherwise.
10
10
 
11
11
  ## Capabilities
12
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
13
  ### Chrome
16
14
 
17
- | Capability | How | What you get | Status |
18
- |---|---|---|---|
19
- | Startup header | `quietStartup` | One two-tone nameplate with the agent and chrome versions and the live help keys, in place of Pi's startup header and resource listing. | Shipped |
20
- | Two-line footer | every `tui` session | State on one line — location, modes, and alerts — and meters on the other — context, spend, tokens, cache, and model — each shedding parts rather than clipping. | Shipped |
21
- | Extension status chips | `footerData.getExtensionStatuses()` | Worktree, browse, plan, and alert statuses appear in the footer, using each extension's own colour. A failed job is the one chip no width drops. | Shipped |
22
- | Priced session total | the `price` section of `bytefly.json` | The session's spend in yuan or dollars, with DeepSeek priced out of the box. | Shipped |
23
- | Delegated-run cost roll-up | the `↳` footer segment | A finished subagent's cost appears beside the session total, without merging the two ledgers. | Shipped |
24
- | User-message nameplate | every `tui` session | Each user message opens with a two-row `You` label and rule. | Shipped |
25
- | Turn receipt | the `bytefly-separator` entry | One rule per completed turn, recording its number, duration, tool count, and outcome. | Shipped |
26
- | Shared notices | the `my-pi-agent/notice` type | Any extension's notice renders at the transcript margin with a severity-coloured rail. | Shipped |
27
- | Compaction canvas | the shipped `themes/` palettes | Pi's `/compact` block keeps its content and loses its fill, so it sits on the canvas. | Shipped |
28
- | Tool-block frame | `registerToolRenderer` | Every tool block leads with its own call line and hangs its result from the transcript rail. | Shipped |
29
- | Working line | `ctx.ui.setWorkingMessage` | The working row counts seconds, then names the running tool and how many tools the run has started. | Shipped |
15
+ | Capability | How | What you get |
16
+ |---|---|---|
17
+ | Startup header | `quietStartup` | One two-tone nameplate with the agent and chrome versions and the live help keys, in place of Pi's startup header and resource listing. |
18
+ | Two-line footer | every `tui` session | State on one line — location, modes, and alerts — and meters on the other — context, spend, tokens, cache, and model — each shedding parts rather than clipping. |
19
+ | Extension status chips | `footerData.getExtensionStatuses()` | Worktree, browse, plan, and alert statuses appear in the footer, using each extension's own colour. A failed job is the one chip no width drops. |
20
+ | Priced session total | the `price` section of `bytefly.json` | The session's spend in yuan or dollars, with DeepSeek priced out of the box. |
21
+ | Delegated-run cost roll-up | the `↳` footer segment | A finished subagent's cost appears beside the session total, without merging the two ledgers. |
22
+ | User-message nameplate | every `tui` session | Each user message opens with a two-row `You` label and rule. |
23
+ | Turn receipt | the `bytefly-separator` entry | One rule per completed turn, recording its number, duration, tool count, and outcome. |
24
+ | Shared notices | the `my-pi-agent/notice` type | Any extension's notice renders at the transcript margin with a severity-coloured rail. |
25
+ | Compaction canvas | the shipped `themes/` palettes | Pi's `/compact` block keeps its content and loses its fill, so it sits on the canvas. |
26
+ | Tool-block frame | `registerToolRenderer` | Every tool block leads with its own call line and hangs its result from the transcript rail. |
27
+ | Working line | `ctx.ui.setWorkingMessage` | The composer's top border carries it beside Pi's spinner: the elapsed count, then how many tools the run has started. |
28
+ | Plan-mode chord | `Shift+Tab` at the main editor | Flips `auto` and plan through `plan`'s request channel; the footer mode segment and the composer border follow. Shift+Tab is taken from Pi's thinking cycle only while `plan` is loaded, and restored on shutdown. |
30
29
 
31
30
  ### Control center
32
31
 
33
- | Capability | How | What you get | Status |
34
- |---|---|---|---|
35
- | One frame, three sections | `/bytefly`, `/bytefly skills`, `/bytefly settings` | Extensions, skills, and settings in one dock: the worst problem in the header, each section's count on the tabs, the write scope at the right, and one body height for all three, so changing section never resizes the screen. | Shipped |
36
- | Resource rows and the block | `Space`, `Enter`, `?` | A name-first row — `worktree`, not `worktree/index.ts` — with one checkbox, one status glyph and one verdict word, and the focused row's findings, path, source, state, and health beside the list at 96 columns and under it below. | Shipped |
37
- | Grouped settings | `/bytefly settings` | Twelve preferences under `Startup`, `Notifications`, `Session`, and `Price`, each value in one aligned column and editable in place; `J` shows the stored JSON section. | Shipped |
38
- | Theme selection | the `Theme` row in `/bytefly settings` | `system` and the package's `bytefly-dark` and `bytefly-light` palettes are one selection; a reload applies it. | Shipped |
39
- | Filters and scope | `/`, `!`, `s`, `x` | Narrow the rows by text or to what needs attention — the chips on the tab row say which — and choose where a toggle lands, the personal or project `settings.json`, with `x` clearing a project override. | Shipped |
40
- | Reload after changes | `R` | `unsaved changes` joins the header while a write is pending, and `R` reloads to apply it instead of leaving the session to be restarted by hand. | Shipped |
41
- | Health report | `run_doctor`, `/bytefly doctor` | Prerequisites, parse errors, and aged rate tables as text and typed data. | Shipped |
32
+ | Capability | How | What you get |
33
+ |---|---|---|
34
+ | One frame, three sections | `/bytefly`, `/bytefly skills`, `/bytefly settings` | Extensions, skills, and settings in one dock: the worst problem in the header, each section's count on the tabs, the write scope at the right, and one body height for all three, so changing section never resizes the screen. |
35
+ | Resource rows and the block | `Space`, `Enter`, `?` | A name-first row — `worktree`, not `worktree/index.ts` — with one checkbox, one status glyph and one verdict word, and the focused row's findings, path, source, state, and health beside the list at 96 columns and under it below. |
36
+ | Grouped settings | `/bytefly settings` | Fifteen preferences under `Startup`, `Notifications`, `Session`, `Board`, and `Price`, each value in one aligned column and editable in place; `J` shows the stored JSON section. |
37
+ | Theme selection | the `Theme` row in `/bytefly settings` | `system` and the package's `bytefly-dark` and `bytefly-light` palettes are one selection; a reload applies it. |
38
+ | Filters and scope | `/`, `!`, `s`, `x` | Narrow the rows by text or to what needs attention — the chips on the tab row say which — and choose where a toggle lands, the personal or project `settings.json`, with `x` clearing a project override. |
39
+ | Reload after changes | `R` | `unsaved changes` joins the header while a write is pending, and `R` reloads to apply it instead of leaving the session to be restarted by hand. |
40
+ | Health report | `run_doctor`, `/bytefly doctor` | Prerequisites, parse errors, and aged rate tables as text and typed data. |
42
41
 
43
42
  ### Upkeep
44
43
 
@@ -229,7 +228,9 @@ A nameplate: a two-tone half-block mark from the active theme's `accent` /
229
228
  identity (`enhanced by bytefly` and the package version), a signature rule whose
230
229
  `accent` lead sits under the mark, and a grouped help block. The help keys
231
230
  resolve live from Pi's keybindings, so a rebound key shows the user's chord; the
232
- identity and help text are `meta` / `faint` so the mark keeps the eye. It opens
231
+ one raw hint, `shift+tab to toggle plan`, is bytefly's own editor chord rather
232
+ than a bindable Pi action. The identity and help text are `meta` / `faint` so the
233
+ mark keeps the eye. It opens
233
234
  with a blank row, so the nameplate clears the top edge. It is installed only in
234
235
  interactive (`tui`) sessions when Pi's `quietStartup` is `true` — the Settings
235
236
  row **Startup screen** — and `false`, `"header"`, and unset leave Pi's
@@ -261,14 +262,17 @@ identity, and grouped help are tuned in
261
262
 
262
263
  Two lines, each with a left and right zone:
263
264
 
264
- - **Line 1 — identity:** `pwd` and the session name on the left; on the right the
265
- alert, then the mode chips (`≡ plan`), then `⎇ branch · ⑂ worktree`, then the
266
- session's page port (`◉ 4780`) while anything is served on it. One server serves
267
- every page of a session, so it is one number from one status key.
268
- - **Line 2 — resources:** the context gauge and usage meters on the left, the
269
- child-run roll-up (`↳ 3 · ¥0.12`, present only once a subagent has finished),
270
- and the model and thinking level on the right. The gauge is anchored at column
271
- 0, so `%` is always in the same place.
265
+ - **Line 1 — identity:** `pwd` and the name this session is on the team as on the left —
266
+ the bus's roster label when it has one, and Pi's own session name when it has none, so a
267
+ session off the bus reads exactly as it did; on the right the
268
+ alert, then the remaining status chips (a job count, a rewind timeline, the
269
+ `✆` bridge), then `⎇ branch · ⑂ worktree`, then the session's page port
270
+ (`◉ 4780`) while anything is served on it. One server serves every page of a
271
+ session, so it is one number from one status key.
272
+ - **Line 2 — resources:** the mode (`▶ auto` or `≡ plan`), the context gauge and
273
+ usage meters on the left, the child-run roll-up (`↳ 3 · ¥0.12`, present only
274
+ once a subagent has finished), and the model and thinking level on the right.
275
+ The gauge follows the mode, so `%` stays put once the mode stops changing.
272
276
  The roll-up is its own number, never folded into the session's cost: a subagent
273
277
  runs in its own process, and the footer prices those runs with the same rate
274
278
  table and currency as the session so a currency change moves both. It covers
@@ -276,8 +280,8 @@ Two lines, each with a left and right zone:
276
280
  `/resume` (the events are not session entries).
277
281
 
278
282
  ```
279
- ~/repo/my-pi-agent ≡ plan · ⎇ main · ⑂ smoke
280
- ▰▰▰▰▰▰▱▱▱▱ 62%/200k · ¥0.31 · ↳ 2 · ¥0.06 · ↑42k ↓8.0k · R96k CH 87% opus-4.5 · high
283
+ ~/repo/my-pi-agent ⎇ main · ⑂ smoke
284
+ ≡ plan · ▰▰▰▰▰▰▱▱▱▱ 62%/200k · ¥0.31 · ↳ 2 · ¥0.06 · ↑42k ↓8.0k · R96k CH 87% opus-4.5 · high
281
285
  ```
282
286
 
283
287
  (The examples show the default `¥` symbol. A model that neither `rates` nor the
@@ -285,11 +289,11 @@ built-in table covers contributes nothing in `CNY` mode, so the bar shows no
285
289
  figure rather than a guessed one; `USD` mode adds Pi's recorded dollars for that
286
290
  record instead — see [Cost calculation](#cost-calculation).)
287
291
 
288
- With no plan mode the mode slot is simply absent:
292
+ With no plan mode the mode segment reads its other state:
289
293
 
290
294
  ```
291
295
  ~/repo/my-pi-agent ⎇ main · ⑂ smoke
292
- ▰▰▰▰▰▰▱▱▱▱ 62%/200k · ¥0.31 · ↑42k ↓8.0k · R96k CH 87% opus-4.5 · high
296
+ ▶ auto · ▰▰▰▰▰▰▱▱▱▱ 62%/200k · ¥0.31 · ↑42k ↓8.0k · R96k CH 87% opus-4.5 · high
293
297
  ```
294
298
 
295
299
  While a page is served, the session's port joins the run beside the worktree:
@@ -304,13 +308,13 @@ clipping. The branch keeps its name and is dropped when the name no longer
304
308
  fits, rather than collapsing to a bare glyph:
305
309
 
306
310
  ```
307
- ~/repo/my-pi-agent ≡ plan · ⎇ main · ⑂
308
- ▰▰▰▰▰▰▱▱▱▱ 62%/200k · ¥0.31 opus-4.5 · high
311
+ ~/repo/my-pi-agent ⎇ main · ⑂
312
+ ≡ plan · ▰▰▰▰▰▰▱▱▱▱ 62%/200k · ¥0.31 opus-4.5 · high
309
313
  ```
310
314
 
311
315
  ```
312
316
  ~/my-pi-agent ⎇ main
313
- ▰▰▰▰▰▰▱▱▱▱ 62% opus-4.5
317
+ ▶ auto · ▰▰▰▰▰▰▱▱▱▱ 62% opus-4.5
314
318
  ```
315
319
 
316
320
  The port drops the digits before the page they name: it compacts to `◉` once
@@ -319,14 +323,14 @@ says a page is up.
319
323
 
320
324
  ```
321
325
  ~/repo/my-pi-agent ⎇ main · ◉
322
- ▰▰▰▰▰▰▱▱▱▱ 62% · ¥0.31 opus-4.5 · high
326
+ ≡ plan · ▰▰▰▰▰▰▱▱▱▱ 62% · ¥0.31 opus-4.5 · high
323
327
  ```
324
328
 
325
329
  With a named session, the name follows the path and truncates before it drops:
326
330
 
327
331
  ```
328
332
  ~/repo/project · [feature-refac…] ⎇ main
329
- ▰▰▰▰▰▰▱▱▱▱ 62%/200k · ¥0.31 opus-4.5 · high
333
+ ▶ auto · ▰▰▰▰▰▰▱▱▱▱ 62%/200k · ¥0.31 opus-4.5 · high
330
334
  ```
331
335
 
332
336
  Zero-value meters are omitted, so a fresh session shows only `?/window` instead
@@ -345,6 +349,7 @@ Each segment reads a theme role and carries its own drop order:
345
349
  | branch | 1 right | muted (warning when detached) | last |
346
350
  | worktree | 1 right | success | first |
347
351
  | port `◉ 4780` | 1 right | success glyph + accent port | first |
352
+ | mode `≡ plan` / `▶ auto` | 2 left | warning while planning, accent otherwise | never |
348
353
  | context gauge + `%` | 2 left | muted ≤70 · warning 71–90 · error >90 | never |
349
354
  | `/window` | 2 left | dim | before cost |
350
355
  | cost | 2 left | muted | after tokens |
@@ -362,13 +367,26 @@ opens. It is a two-tone `<glyph> <port>` with a green glyph and an accent port,
362
367
  and it compacts from `◉ 4780` to `◉` before it drops, so a bar too narrow for
363
368
  the number still says a page is up.
364
369
 
365
- The mode slot holds everything else an extension publishes: a plan chip, a
366
- job count, a rewind timeline, the `✆` bridge, and an alert's front position. A
367
- plan chip's trailing plan file name is dropped, so the slot reads `≡ plan` even
368
- though the built-in footer keeps the name, and an alert (a failed job) sorts to
369
- the front of it. It keeps each extension's own colour when it compacts, and it is
370
- droppable: a tight bar drops the slot rather than truncating the model, which
371
- sits at the end of the line.
370
+ The mode segment leads the resource line: `≡ plan` in the warning role while
371
+ planning, `▶ auto` in accent otherwise. The bar draws that word itself and reads
372
+ only the presence of the `plan-mode` status key, so neither the plan file name nor
373
+ a chip truncated elsewhere can reach it. `auto` is not a
374
+ behaviour of its own: it names Pi's default mode, every tool as it ships and writes
375
+ included, so the segment states which mode the session is in and `≡ plan` is the one
376
+ override that withholds — a mode added later is a third value of this one chip, never a
377
+ second indicator saying what the session may do. `Shift+Tab` at
378
+ the main editor asks [plan](./plan.md) to flip it; the chip and the composer
379
+ border are read from the branch, so `/plan` and the model's own `plan` action `exit`
380
+ move them too. Bytefly unsets Pi's `app.thinking.cycle` chord while it holds the
381
+ key, so `/hotkeys` and the header stop claiming Shift+Tab, and restores it on
382
+ shutdown. A session without `plan` still reads `▶ auto` and keeps the thinking
383
+ cycle.
384
+
385
+ The status slot beside the branch holds everything else an extension publishes: a
386
+ job count, a rewind timeline, the `✆` bridge, and an alert's front position. An
387
+ alert (a failed job) sorts to the front of it. It keeps each extension's own
388
+ colour when it compacts, and it is droppable: a tight bar drops the slot rather
389
+ than truncating the model, which sits at the end of the line.
372
390
 
373
391
  ## Cost calculation
374
392
 
@@ -619,44 +637,50 @@ edges its own `Box` pads itself with, and hands it a theme whose `bg` is inert s
619
637
  it never paints a band the rest of the transcript does not have.
620
638
 
621
639
  The call line is one colour grammar, shared with Pi's built-ins: the tool name is
622
- `toolTitle` and bold and is the line's only accent; a primary subject (a query, a
623
- URL, a path, a status) reads in the body role (`label`), counts and summaries are
624
- `muted`, trailing options are `dim`, and the result body is `toolOutput`. The
625
- shipped themes point `toolTitle` at the accent and `label` at the body colour, so
626
- the name is the call line's one accent and the subject follows as body text; a
627
- theme that maps `toolTitle` elsewhere is respected rather than overridden.
640
+ in the `toolTitle` role, bold, and is the line's only accent; a primary subject (a
641
+ query, a URL, a path, a status) reads in the body role (`label`), counts and
642
+ summaries are `meta`, trailing options are `faint`, and the result body is
643
+ `toolOutput`. The shipped themes point `toolTitle` at the accent and `label` at
644
+ the body colour, so the name is the call line's one accent and the subject follows
645
+ as body text; a theme that maps `toolTitle` elsewhere is respected rather than
646
+ overridden.
628
647
 
629
648
  ## The working line
630
649
 
631
- Pi's working row shows a static `Working`, so a long run reads the same as a
632
- short one. While the row is up, bytefly advances its message once a second and
633
- hands it back to Pi when the row comes down. Before the first tool starts it is
634
- a count:
650
+ Pi's working indicator shows a static `Working`, so a long run reads the same as
651
+ a short one. It is drawn on the composer's top border while a run is up, and
652
+ bytefly advances its message once a second and hands it back to Pi when the run
653
+ settles. Before the first tool starts it is a count:
635
654
 
636
655
  ```
637
- Working… 12s
656
+ ── ◐ working · 12s ───────────────────
638
657
  ```
639
658
 
640
- Once a top-level tool starts, the row becomes a live run line: the tool, how
641
- many top-level tools this run has started, and the elapsed time.
659
+ Once a top-level tool starts, the border adds how many top-level tools this run
660
+ has started:
642
661
 
643
662
  ```
644
- edit · 3 tools · 12s
663
+ ── ◐ working · 3 tools · 12s ─────────
645
664
  ```
646
665
 
666
+ The spinner and the colour are Pi's: the indicator owns the `◐` frame and takes
667
+ the composer border's role, so it is `accent` in auto and `warning` while
668
+ planning. The line deliberately does not name the tool — the transcript already
669
+ draws each call as its own block, and one word reads faster than a name that
670
+ changes under you.
671
+
647
672
  A nested tool call (one carrying `parentToolCallId`, such as a codemode script's
648
- inner call) neither counts nor takes over the line, so the number matches the
649
- tool blocks in the transcript. The line keeps the last tool name while the
650
- model streams after it, and a tool event repaints the row immediately instead
651
- of waiting for the next tick. The line covers the whole run, tool calls
652
- included. It is tied to the row's visibility: it starts on `turn_start`, stops
673
+ inner call) does not count, so the number matches the tool blocks in the
674
+ transcript. A tool event repaints the border immediately instead of waiting for
675
+ the next tick. The line covers the whole run, tool calls
676
+ included. It is tied to the indicator's visibility: it starts on `turn_start`, stops
653
677
  on `agent_end`, and is reset on `session_shutdown`; turns inside one run do not
654
678
  restart it, and a retry starts a new run. The words and the interval are tuned
655
679
  in [`working/config.ts`](../../extensions/bytefly/working/config.ts).
656
680
 
657
681
  ## The control center
658
682
 
659
- <img src="../../assets/screens/bytefly.png" alt="The /bytefly control center at 120 columns: a `Bytefly` rule with `1 missing` at its right edge, the `Extensions 9 · Skills 0 · Settings 12` tabs with `writes Global` at the right, one name-first row per extension with its checkbox and verdict, and the focused extension's block beside the list — its findings and their fix, Path, Source, State, and Health" width="100%">
683
+ <img src="../../assets/screens/bytefly-extensions.png" alt="The /bytefly control center at 120 columns: a `Bytefly` rule with `1 missing` at its right edge, the `Extensions 9 · Skills 3 · Settings 15` tabs with `writes Global` at the right, one name-first row per extension with its checkbox and verdict, and the focused extension's block beside the list — its findings and their fix, Path, Source, State, and Health" width="100%">
660
684
 
661
685
  `/bytefly [extension] [refresh]` opens the control center on **Extensions**: a
662
686
  keyboard-driven dock of every resource Pi resolves for the session, so the list
@@ -780,9 +804,11 @@ finds the rows that need it.
780
804
  - Shows whether each one is enabled (`◼`) or disabled (`◻`) in settings.
781
805
  - Gives each row one verdict from the doctor's report: the worst severity's
782
806
  glyph (`!` degraded, `✕` missing) and its summary. A row that is `◻` reads
783
- `off` instead, because it will not load; a row the session's
784
- `PI_DISABLED_EXTENSIONS` excludes reads `env disabled`, so the two axes cannot
785
- contradict each other. A row with nothing to report leaves the glyph column
807
+ `off` instead, because it will not load; a row the session's environment
808
+ excludes — disabled through `PI_DISABLED_EXTENSIONS`, or an opt-in extension
809
+ that `PI_ENABLED_EXTENSIONS` does not name — reads `env disabled`, so the two
810
+ axes cannot contradict each other. The finding beside it names the switch that
811
+ would change the answer. A row with nothing to report leaves the glyph column
786
812
  blank and says `ok` in words when a report exists.
787
813
  - Toggles the focused extension with `Space`, writing `+pattern` / `-pattern`
788
814
  into `extensions` (and package filter objects) in the write scope. A row in
@@ -826,20 +852,24 @@ line until `Enter` expands it. There is nothing to probe, so `r` is not offered
826
852
  here; the metadata is read once when the section first appears, so an edited
827
853
  `SKILL.md` shows on the next open.
828
854
 
855
+ <img src="../../assets/screens/bytefly-skills.png" alt="The /bytefly Skills section at 120 columns: the `Extensions 9 · Skills 3 · Settings 15` tabs with `writes Global` at the right, the three skills this package ships under an `npm:pkg (user) (3)` heading with the focused `init` row marked `hidden`, and its block beside the list — Path, Source, State, the `/skill:init` call, `Model hidden from the model`, and the description the file declares under About" width="100%">
856
+
829
857
  Themes and prompts have no section of their own, by decision rather than by
830
858
  omission: a theme is one active selection — a scalar, global setting — and a
831
859
  prompt template is a file Pi discovers from a directory, so neither is a scoped
832
860
  enable list this screen's checkbox rows can write. Themes stay with `pi config`;
833
- `ROADMAP.md` carries the reason under *Deliberately left thin*.
861
+ [`architecture.md`](../architecture.md#documented-limits-with-no-planned-card) carries
862
+ the reason.
834
863
 
835
864
  **Settings.** The third section is the same frame over the curated preferences,
836
865
  grouped by what they do rather than by which file holds them: `Startup (2)`,
837
- `Notifications (5)`, `Session (3)`, and `Price (2)`. It is the shortlist a
838
- session changes: twelve preferences, each with its value in one aligned column
839
- instead of beside its label, so a column of values can be read down. Sixteen
840
- entries — twelve rows and their four headings — fit a 24-row terminal when the
841
- block sits beside the list; on a narrower one the list scrolls and the block
842
- keeps its three rows. The frame never grows past the tallest section, however
866
+ `Notifications (5)`, `Session (3)`, `Board (3)`, and `Price (2)`. It is the
867
+ shortlist a session changes: fifteen preferences, each with its value in one
868
+ aligned column instead of beside its label, so a column of values can be read
869
+ down. Twenty entries — fifteen rows and their five headings — want a 28-row
870
+ terminal to be seen whole when the block sits beside the list; on a shorter or
871
+ narrower one the list scrolls, says `showing 1–13 of 15`, and the block keeps its
872
+ three rows. The frame never grows past the tallest section, however
843
873
  tall the terminal is. In Project scope the two app rows drop out and the note
844
874
  `App settings are Global only.` stands above the list.
845
875
 
@@ -850,7 +880,7 @@ description, and `Stored in <origin> · <file>` — `Global`, `Project`,
850
880
  otherwise). `J` on a `bytefly.json` row appends that section as the active scope
851
881
  stores it, under a `── notify ──` rule.
852
882
 
853
- <img src="../../assets/screens/bytefly-settings.png" alt="The /bytefly Settings section at 120 columns: the `Startup (2)`, `Notifications (5)`, `Session (3)`, and `Price (2)` groups with each value in one aligned column, the `Extensions 9 · Skills 0 · Settings 12` tabs with `writes Global` at the right, and the focused row's block beside the list — its value, description, and the file the value is stored in" width="100%">
883
+ <img src="../../assets/screens/bytefly-settings.png" alt="The /bytefly Settings section at 120 columns: the `Startup (2)`, `Notifications (5)`, `Session (3)`, `Board (3)` and `showing 1–11 of 15` rows with each value in one aligned column, the `Extensions 9 · Skills 3 · Settings 15` tabs with `writes Global` at the right, and the focused row's block beside the list — its value, description, and the file the value is stored in" width="100%">
854
884
 
855
885
  | Group | Row | Source | Values |
856
886
  |---|---|---|---|
@@ -890,9 +920,10 @@ only view of the keys this screen leaves to JSON. A successful change marks the
890
920
  block `Saved · reload to apply` and puts `unsaved changes` in the header, and
891
921
  closing offers the same reload confirm as an extension or skill toggle.
892
922
 
893
- The list is capped at twelve rows so the screen keeps fitting a 24-row
923
+ The list is capped at fifteen rows so the screen keeps fitting a 28-row
894
924
  terminal: adding a setting means removing one (`panel/settings.test.ts` holds the
895
- ceiling). Everything else stays in `bytefly.json` — the tuning numbers, the
925
+ ceiling, and a 24-row terminal scrolls the list rather than the frame). Everything
926
+ else stays in `bytefly.json` — the tuning numbers, the
896
927
  cosmetic labels, and the Pi preferences Pi's own `/settings` owns — and
897
928
  `test/settings-coverage.test.ts` keeps the split honest: every loader key is
898
929
  either a row or a declared JSON-only edit with the reason it left. Most
@@ -915,7 +946,7 @@ Off, because it keeps Pi's header; the next toggle replaces it with `true` or
915
946
 
916
947
  App rows are **Global only**: Pi's setters for them write the personal
917
948
  `settings.json`, and only resource paths have public project setters. Project
918
- scope therefore lists the ten `bytefly.json` rows and a
949
+ scope therefore lists the thirteen `bytefly.json` rows and a
919
950
  `App settings are Global only.` note rather than promising a write that cannot
920
951
  happen. A `bytefly.json` row writes one leaf in the selected scope (a depth-1
921
952
  key, or a nested one such as `price.currency.code`) through a per-key writer, so
@@ -927,7 +958,7 @@ The non-`tui` form prints the headline and one `Label: Value` row per line, in
927
958
  screen order:
928
959
 
929
960
  ```
930
- bytefly: Settings · Global · 12 settings
961
+ bytefly: Settings · Global · 15 settings
931
962
  Startup screen: Off
932
963
  Theme: bytefly-dark
933
964
  Desktop notifications: On
@@ -1143,7 +1174,7 @@ install, and `PI_OFFLINE` all follow its package manager. The only extra read is
1143
1174
  this package, used just to name the version arrow and release URL. A failed
1144
1175
  version read drops those two pieces but keeps the package list and command.
1145
1176
 
1146
- The answer is cached in `<agentDir>/bytefly/update.json` as the package list,
1177
+ The answer is cached in `<agent-dir>/bytefly/update.json` as the package list,
1147
1178
  this package's optional version, the time it was read, and a key over the cwd and
1148
1179
  the effective package sources. A session that starts inside the 24-hour window
1149
1180
  reuses that snapshot — painting the marker and posting the notice from it —
@@ -1197,7 +1228,7 @@ why the line says so, and each source the manager reports is shown while it runs
1197
1228
  - **The list is a shortlist, not the whole document.** A row is one boolean,
1198
1229
  enum, string, or number; the tuning numbers, the labels, and the Pi
1199
1230
  preferences `/settings` owns stay in their files, and the catalogue is capped
1200
- at twelve rows so the screen fits a 24-row terminal. `doctor.skip` (an
1231
+ at fifteen rows so the screen fits a 28-row terminal. `doctor.skip` (an
1201
1232
  array), the `rates` table, and `price.peak` (a nested object) could never be a
1202
1233
  row; the rest are declared JSON-only with their reason in
1203
1234
  `test/settings-coverage.test.ts`.
@@ -1272,12 +1303,15 @@ why the line says so, and each source the manager reports is shown while it runs
1272
1303
  - **The displayed number is the total, rounded once** — not the sum of rounded
1273
1304
  records.
1274
1305
  - **`doctor` can only check what is declared.** A new extension with no
1275
- `REQUIREMENTS` entry is invisible to it — `test/structure.test.ts` fails on
1306
+ `REQUIREMENTS` entry is invisible to it — `test/prerequisites.test.ts` fails on
1276
1307
  exactly that, which is why the table cannot rot quietly.
1277
- - **The disabled check reads `PI_DISABLED_EXTENSIONS`** through `lib/env.ts`; it
1278
- does not read Pi's own `settings.json` disable list, so an extension disabled
1279
- only there may still be probed. The row labels that state `env disabled`,
1280
- separately from the picker's `off`.
1308
+ - **The disabled check reads the environment** through `lib/env.ts`: the disable
1309
+ list, and the opt-in list that a default-off extension needs. It does not read
1310
+ Pi's own `settings.json` disable list, so an extension disabled only there may
1311
+ still be probed. The row labels that state `env disabled`, separately from the
1312
+ picker's `off` — the picker's `Space` writes settings patterns and cannot set
1313
+ an environment variable, so an opt-in extension is switched on in the shell,
1314
+ not on that screen.
1281
1315
  - **A probe proves a binary runs, not that it works:** a `git` that crashes on a
1282
1316
  specific repository is still `ok`.
1283
1317
  - **Config scope is checked for `<agent-dir>/bytefly.json` and
@@ -1296,11 +1330,14 @@ why the line says so, and each source the manager reports is shown while it runs
1296
1330
  host lacks (or an OSC method off a TTY) and the notification is dropped; only
1297
1331
  `auto` inspects the environment. `doctor` now reports the mismatch as a
1298
1332
  `degraded` finding, which says what to change but does not fix it for you.
1299
- - **The working line is one global row.** `setWorkingMessage` drives the
1300
- single working row, so bytefly and any other extension setting it race — last
1301
- writer wins — and the message is reset to Pi's default at `agent_end`. If the
1302
- row is hidden (`ctx.ui.setWorkingVisible(false)`) the line has no surface,
1303
- and a reload mid-run does not resume the count until the next turn.
1333
+ - **The working line is one global message.** `setWorkingMessage` drives the
1334
+ single working indicator, so bytefly and any other extension setting it race — last
1335
+ writer wins — and the message is reset to Pi's default at `agent_end`. It is drawn
1336
+ on the composer's top border because the mode editor opts into Pi's embedded
1337
+ indicator; an editor that does not (or a host without `embedWorkingStatus`) puts the
1338
+ same line in a row above the composer instead. If the indicator is hidden
1339
+ (`ctx.ui.setWorkingVisible(false)`) the line has no surface, and a reload mid-run
1340
+ does not resume the count until the next turn.
1304
1341
 
1305
1342
  ## Non-goals
1306
1343
 
@@ -1325,7 +1362,7 @@ why the line says so, and each source the manager reports is shown while it runs
1325
1362
  - No sound, action buttons, notification history, or de-duplication; a
1326
1363
  notification is a fire-and-forget message, not an inbox.
1327
1364
  - No frozen per-run working duration: the line is live and is handed back to
1328
- Pi when the working row comes down, because the extension API exposes no hook
1365
+ Pi when the working indicator comes down, because the extension API exposes no hook
1329
1366
  to keep a finished run's total.
1330
1367
 
1331
1368
  The built-in footer's `(auto)` auto-compact indicator and `(sub)`
@@ -1344,7 +1381,7 @@ rather than showing a wrong value.
1344
1381
  | Tool blocks | `registerToolRenderer` returns, for every tool, `renderShell: "self"`; `renderCall` leaves the tool's own first line as the block's headline (a shell prompt shown as the tool name, its command repainted in the body colour, and a primary subject demoted to the body colour) and indents the rest of the call two columns under it, `renderResult` indents its body the same and leads it with the status-coloured rail, and each wraps the tool's own renderer (or a generic fallback). `edit` and `write` carry their result on the call body, so that body is railed too. |
1345
1382
  | Screen command | `pi.registerCommand("bytefly")` takes `[doctor] [settings] [extension] [refresh]`: `tui` mounts a dock screen via `ctx.ui.custom()` with the rails suppressed; other modes print through `ctx.ui.notify()` — the grouped listing, `run_doctor`'s report for `doctor`, or the flat settings listing with values for `settings`. A named extension is checked against the discovered set before any probe. Tab completes the subcommands, extension names after `doctor`, and `refresh`; `/bytefly settings` rejects a name. |
1346
1383
  | Turn separator | `registerEntryRenderer("bytefly-separator")` draws each stored receipt; `agent_start` opens it, top-level `tool_execution_start`/`tool_execution_end` count tools and failures, and `agent_settled` appends `pi.appendEntry("bytefly-separator", { turn, durationMs, tools, failures, aborted })` in `tui` mode only. The receipt fields are omitted when no `agent_start` was seen, so an older or partially observed entry stays `{ turn }`. |
1347
- | Working line | `turn_start` starts a 1s tick that drives `ctx.ui.setWorkingMessage` in `tui` mode; before a tool runs the message is `Working… Ns`, and each top-level `tool_execution_start` repaints it as `tool · N tools · Ns` (nested calls carrying `parentToolCallId` are skipped). `agent_end` restores Pi's default and cancels the tick (also on `session_shutdown`). No config file and no persisted state; the count lives in the register's closure. |
1384
+ | Working line | `turn_start` starts a 1s tick that drives `ctx.ui.setWorkingMessage` in `tui` mode; before a tool runs the message is `working · Ns`, and each top-level `tool_execution_start` repaints it as `working · N tools · Ns` (nested calls carrying `parentToolCallId` are skipped). It renders on the composer's top border, which the mode editor enables with `embedWorkingStatus`. `agent_end` restores Pi's default and cancels the tick (also on `session_shutdown`). No config file and no persisted state; the count lives in the register's closure. |
1348
1385
  | Notifications | `agent_settled` (skipped when aborted), `ui_prompt_start` only while `agent_start`…`agent_settled` marks a run active, and `my-pi-agent/job-finished` on `pi.events`; the `notify` section of `bytefly.json` gates each. Delivery writes an OSC 777/99 sequence to stdout or spawns `osascript`, `notify-send`, or `powershell.exe`. The bus subscription is created in `session_start` and released in `session_shutdown`, and the `job` extension never imports bytefly. |
1349
1386
  | Picker discovery | `SettingsManager.create(...)` plus `DefaultPackageManager.resolve({ onMissing: "skip" })`, both public exports; both views drop `bytefly`'s own entrypoint. |
1350
1387
  | Picker writes | `SettingsManager` setters (`setExtensionPaths`, `setPackages`, `setProjectExtensionPaths`, `setProjectPackages`) then `flush()`; `ctx.reload()` after the screen closes when changes are pending. |
@@ -1353,10 +1390,10 @@ rather than showing a wrong value.
1353
1390
  | Doctor probes | `run_doctor` and the screen share the session cache created by `registerDoctor`; the screen mounts on that cache and probes on the first Extensions view (the command's `refresh` forces it), while the text modes probe before printing, and `/bytefly refresh` and `refresh: true` re-probe. Probes spawn `<binary> --version`, lazily import optional packages, and read config files through `lib/requirements.ts`; nothing is ever written. |
1354
1391
  | Status routing | Reads `footerData.getExtensionStatuses()` (a custom footer replaces the built-in one entirely); `worktree` routes to line 1, plan/alert statuses to line 2's trailing slot. |
1355
1392
  | Release notice | `session_start` in `tui` mode patches `InteractiveMode.prototype.showNewVersionNotification` through the shared `patchPrototypeMethod`, so the release Pi resolved becomes `pi.sendMessage(piReleaseNotice(...))` under the shared notice type and `paintLatest` records its version for the agent line's marker (`onUpdate` re-installs the header). The callback posts once per load and only while the session is live; a throw calls the captured original, so Pi's own box is the fallback. `session_shutdown` restores the method and clears the marker. |
1356
- | Update check | `session_start` in `tui` mode installs a suppression on `DefaultPackageManager.prototype.checkForAvailableUpdates`, then reads `<agentDir>/bytefly/update.json`. A fresh entry whose key matches the cwd and effective package sources is recorded without a package-manager call; otherwise bytefly fires `fetchAvailableUpdates()` without awaiting, resolves this package's version through `fetchLatestVersion()` when Pi reports it, writes the snapshot, and records it. `onUpdate` re-installs the header, and `pi.sendMessage(updateNotice(...))` posts the one notice with its version arrow and release URL. `PI_OFFLINE` paints the cached marker only. `/bytefly update` calls the returned `UpdateHandle.recheck()`, which bypasses the window and returns an `UpdateOutcome`; `/bytefly upgrade` calls `UpdateHandle.apply()`, which runs `applyUpdates()` — Pi's `DefaultPackageManager.update()`, with its progress passed to `ctx.ui.notify` — and then re-reads the snapshot so the marker cannot outlive the version it named. A generation counter drops a background reply that lands after `session_shutdown`, a per-load flag keeps it to one announcement, and shutdown restores Pi's method. |
1393
+ | Update check | `session_start` in `tui` mode installs a suppression on `DefaultPackageManager.prototype.checkForAvailableUpdates`, then reads `<agent-dir>/bytefly/update.json`. A fresh entry whose key matches the cwd and effective package sources is recorded without a package-manager call; otherwise bytefly fires `fetchAvailableUpdates()` without awaiting, resolves this package's version through `fetchLatestVersion()` when Pi reports it, writes the snapshot, and records it. `onUpdate` re-installs the header, and `pi.sendMessage(updateNotice(...))` posts the one notice with its version arrow and release URL. `PI_OFFLINE` paints the cached marker only. `/bytefly update` calls the returned `UpdateHandle.recheck()`, which bypasses the window and returns an `UpdateOutcome`; `/bytefly upgrade` calls `UpdateHandle.apply()`, which runs `applyUpdates()` — Pi's `DefaultPackageManager.update()`, with its progress passed to `ctx.ui.notify` — and then re-reads the snapshot so the marker cannot outlive the version it named. A generation counter drops a background reply that lands after `session_shutdown`, a per-load flag keeps it to one announcement, and shutdown restores Pi's method. |
1357
1394
  | Config | `bytefly.json` from the agent directory and `<cwd>/.pi`; the `price` section is read once per footer install (a new session or a session-tree change), the `notify` and `doctor` sections at `session_start`, and the `chrome` section's rail glyph at `session_start`/`session_tree`. Nothing watches the document. The update check has no section: it is on whenever `PI_OFFLINE` is off, and its 24 h window is not configurable. |
1358
1395
  | Data sources | `footerData.getGitBranch()`, `footerData.getAvailableProviderCount()`, `ctx.getContextUsage()`, `ctx.sessionManager.getEntries()`, `ctx.sessionManager.getSessionName()`, `ctx.model`; subscribes to `footerData.onBranchChange` to repaint. |
1359
- | State storage | None besides the inert separator entries. The footer reads session entries and the price config (a memoized scan) and writes no tool-result `details`; it appends `bytefly-separator` entries excluded from LLM context. The screen derives its view from the picker runtime, the current reports, and the skill index, and persists extension and skill toggles through `settings.json`. The Settings section derives from a snapshot of the personal `settings.json` and both `bytefly.json` documents, and persists through the same `SettingsManager` setters or the per-leaf `bytefly.json` writer. Doctor's findings are session-scoped in memory only — cached for the session, never written to the session or a file. Notify holds only the config and the run-active flag in its closure. The update check holds this package's version in the per-load `UpdateSource` and one project-scoped snapshot in `<agentDir>/bytefly/update.json`. |
1396
+ | State storage | None besides the inert separator entries. The footer reads session entries and the price config (a memoized scan) and writes no tool-result `details`; it appends `bytefly-separator` entries excluded from LLM context. The screen derives its view from the picker runtime, the current reports, and the skill index, and persists extension and skill toggles through `settings.json`. The Settings section derives from a snapshot of the personal `settings.json` and both `bytefly.json` documents, and persists through the same `SettingsManager` setters or the per-leaf `bytefly.json` writer. Doctor's findings are session-scoped in memory only — cached for the session, never written to the session or a file. Notify holds only the config and the run-active flag in its closure. The update check holds this package's version in the per-load `UpdateSource` and one project-scoped snapshot in `<agent-dir>/bytefly/update.json`. |
1360
1397
  | Lifecycle | `session_start` installs the header when Pi's `quietStartup` is `true` and the footer, loads the chrome rail glyph, points the doctor runtime at the session cwd, probes the extensions with hard requirements when `bytefly.json`'s `doctor.proactive` is on and `quietStartup` is `true`, loads the notify config and subscribes to the job bus, and starts the package-update check; `session_tree` reinstalls the header and footer and reloads the rail; `session_shutdown` restores Pi's built-in header, footer, package-update method, and release-notice method, disposes the branch subscription, drops doctor's cached findings, drops the notify bus subscription, and clears the recorded versions. |
1361
1398
 
1362
1399
  ## Design notes
@@ -1462,17 +1499,19 @@ rather than showing a wrong value.
1462
1499
  the session's; it prices the `my-pi-agent/subagent-finished` events with the
1463
1500
  same pricer and renders them as `↳`, so "this session" keeps meaning the
1464
1501
  dispatching process and the delegated work stays visible beside it.
1465
- - **The gauge is anchored at column 0, and nothing can displace it.** A leading
1466
- mode slot used to push the context percentage left and right as plan mode came
1467
- and went, which is why the mode chip trailed the meters after a `│`; moving the
1468
- mode slot to the state line leaves the gauge as the resource line's first
1469
- segment, so the percentage cannot move at all.
1502
+ - **The mode leads the resource line, and the gauge follows it.** A tight bar used
1503
+ to push the context percentage around as plan mode came and went; putting the
1504
+ mode first gives the gauge a fixed offset after a segment of fixed width, so the
1505
+ percentage no longer moves as statuses are published. The mode is never
1506
+ dropped: a reader knows which mode the next request lands in before reading how
1507
+ much room is left.
1470
1508
  - **Statuses are routed, not repeated.** The built-in footer renders every
1471
1509
  `setStatus` value on its own line, so the bar sorts them by meaning instead:
1472
1510
  `worktree` is location, so the `◉ <port>` chip sits behind `⎇`
1473
1511
  branch and `⑂` worktree, and it is the session's one page port, so it keeps a
1474
- weight and a floor of its own; a plan is a mode, so it leads the state line
1475
- beside them; a failed job is an alert, so it is its own segment ahead of both. The
1512
+ weight and a floor of its own; a plan is a mode, so its chip leads the resource
1513
+ line and bytefly fills in `▶ auto` when it is absent; a failed job is an alert,
1514
+ so it is its own segment ahead of the state chips. The
1476
1515
  [wechat](./wechat.md) extension publishes `✆ wechat` while its bridge runs,
1477
1516
  which is why the bar has to render `getExtensionStatuses()` at all.
1478
1517
  - **A state chip leads its zone, so the zone does not move under it.** The right
@@ -1635,7 +1674,7 @@ rather than showing a wrong value.
1635
1674
  The surfaces are folders: `header/`, `footer/` (the status bar and its price
1636
1675
  calculation), `message/` (the user-message marker), `notice/` (the shared notice),
1637
1676
  `block/` (the tool-block frame), `separator/`, `working/` (the live
1638
- run line on Pi's working row), `panel/` (the
1677
+ run line on the composer's top border), `panel/` (the
1639
1678
  unified screen and its merged model),
1640
1679
  `picker/` (discovery and the settings store), `doctor/` (the `run_doctor` tool
1641
1680
  and its probe runtime), `notify/`, and `update/` (Pi's package list, Pi's own
@@ -1653,12 +1692,13 @@ columns, gauge), so the look follows the active theme.
1653
1692
  | `header/identity.ts` | `IdentityInput` and `identityCells`: the agent and chrome cells, each with its `↑` marker, in the ladder that sheds the chrome marker, the chrome version, the chrome line, and finally the agent's marker with the row. |
1654
1693
  | `header/register.ts` | Installs/restores the header and owns its session lifecycle. |
1655
1694
  | `footer/register.ts` | Installs/restores the footer and owns its session lifecycle. |
1656
- | `footer/config.ts` | Bar tuning (`CONFIG`): thresholds, gauge, separators, icons, alert and plan status keys, labels. |
1695
+ | `footer/config.ts` | Bar tuning (`CONFIG`): thresholds, gauge, separators, icons, alert and mode status keys, labels. |
1657
1696
  | `footer/types.ts` | `StatusSnapshot`, `Segment`, `LineSpec`, `UsageRecord`, and footer/TUI shapes. |
1658
1697
  | `footer/budget.ts` | The session spend guardrail: whether the configured ceiling has been crossed and the one notice that says so — an alert, never a limit. |
1659
1698
  | `footer/delegation.ts` | The child-run ledger: the pricing inputs of the last delegated runs, versioned so the footer re-prices when a subagent finishes. |
1660
1699
  | `footer/collect/entries.ts` | Session entries → usage records, each resolved to the model in effect. |
1661
1700
  | `footer/collect/snapshot.ts` | Snapshot assembly; memoized pricing scan, context estimate, and session name per branch leaf. |
1701
+ | `footer/collect/label.ts` | The roster label the bar names this session by: one read of the bus's own record, then every label the bus publishes. |
1662
1702
  | `footer/price/types.ts` | `Currency`, `ModelRates`, `PeakWindow`/`PeakSchedule`, `ByteflyConfig`. |
1663
1703
  | `footer/price/config.ts` | The `price` section of `bytefly.json`: schema, defaults, built-in DeepSeek rates/tariff, currency defaults, normalizer, loader. |
1664
1704
  | `footer/price/rates.ts` | Model-key matching: `createRateLookup` splits every key once, splits a key at its first slash (so an OpenRouter model id may contain one), matches exact and wildcard most-specific-first, and remembers the last record's model. |
@@ -1675,6 +1715,7 @@ columns, gauge), so the look follows the active theme.
1675
1715
  | `message/config.ts` | Nameplate tuning (`MESSAGE_CONFIG`): the label and the rule. |
1676
1716
  | `message/format.ts` | Pure `nameplate` and `styleUserMessage`: the two-row nameplate (label over a full-width rule) and its placement above the message. |
1677
1717
  | `message/register.ts` | Registers the Markdown transformer (the nameplate). |
1718
+ | `mode/editor.ts` | The main editor with `Shift+Tab` bound to plan mode: `isShiftTab` consumption, the mode-aware border (`success`/`warning`) applied in `render`, the `app.thinking.cycle` unbind/restore, and `attachModeKey(pi, ctx)` with its idempotent cleanup. Loads on the first TUI session through a dynamic import. |
1678
1719
  | `notice/config.ts` | Notice tuning (`NOTICE_CONFIG`): source/title/detail roles, the severity glyphs and roles, the shared rail roles, and the item cap. |
1679
1720
  | `notice/component.ts` | Pure `noticeLines` and `NoticeComponent`: the headline at the margin, the rail-led severity rows, sanitized and width-clipped. |
1680
1721
  | `notice/register.ts` | Registers the message renderer for the shared notice type and hands it the rail source. |
@@ -1694,7 +1735,7 @@ columns, gauge), so the look follows the active theme.
1694
1735
  | `panel/join.ts` | The item/report union with the unambiguous-name naming rule — a row is `worktree`, and a directory claims a report only when one resource uses that name — plus unique row keys, groups, ordering, and the skills model that labels rows from `SKILL.md`. |
1695
1736
  | `panel/filter.ts` | The two filters as predicates and chips: a text query over a row's name, path, and source, the attention switch, and what the tab row says is on. |
1696
1737
  | `panel/format.ts` | Non-TUI text: the resource headline and grouped listing with a per-row verdict, the header's attention segments, the one `rowBadge` the screen and the listing share, the section segments and their order, the Settings counts/headline, and the flat Settings listing. |
1697
- | `panel/settings.ts` | The curated Settings catalogue: the twelve descriptors in screen order, their four groups, the value labels (`Off`, `¥5.00`) and display origins, value cycling, stored-value mapping, validation, plus the per-section readers that resolve a row's leaf over the global and project documents. |
1738
+ | `panel/settings.ts` | The curated Settings catalogue: the fifteen descriptors in screen order, their five groups, the value labels (`Off`, `¥5.00`) and display origins, value cycling, stored-value mapping, validation, plus the per-section readers that resolve a row's leaf over the global and project documents. |
1698
1739
  | `panel/settings-runtime.ts` | The Settings runtime: snapshot cache, scope guards, app setters, per-leaf `bytefly.json` writes, dirty state, and change listeners. |
1699
1740
  | `panel/themes.ts` | Which themes the Theme row offers: the host's resolved list narrowed to the palettes under this package's `themes/`, identified by directory rather than name. |
1700
1741
  | `panel/prices.ts` | The `/bytefly prices` report: the unpriced models by key, the records behind each, and the one line saying where the keys go. It quotes no price — a rate is the user's to state. |
@@ -1734,7 +1775,7 @@ columns, gauge), so the look follows the active theme.
1734
1775
  | `update/check.ts` | Pi's package list through `DefaultPackageManager`, plus the registry `GET` that names this package's version for the marker, arrow, and release URL. |
1735
1776
  | `update/version.ts` | Pure semver parsing and `isNewerVersion`, including prerelease precedence. |
1736
1777
  | `update/state.ts` | `UpdateSource` and `createUpdateState` (one known version per subject per load), `paintLatest` (record and repaint the header only on a change), plus `UpdateOutcome`, the four answers an explicit re-check can give. |
1737
- | `update/cache.ts` | The project-scoped snapshot: `<agentDir>/bytefly/update.json`, the cwd/package key, the 24 h window in `isFresh`, a validated read that degrades to a miss, and the atomic write. |
1778
+ | `update/cache.ts` | The project-scoped snapshot: `<agent-dir>/bytefly/update.json`, the cwd/package key, the 24 h window in `isFresh`, a validated read that degrades to a miss, and the atomic write. |
1738
1779
  | `update/format.ts` | `updateMarker`, `updateNotice`, `releaseUrl`, `updateOutcomeText`, and `piReleaseNotice`, plus the shared command/link rows: the header's `↑0.12.0`, the multi-package notice, the line `/bytefly update` prints, and the notice that replaces Pi's `Update Available` box. |
1739
1780
  | `update/register.ts` | The package-update lifecycle: suppression, the cache-first session-start read, the async package-manager call, this package's version lookup, the `PI_OFFLINE` and `tui` gates, the once-per-load announcement, the shutdown generation guard, and the `recheck()` handle `/bytefly update` calls. |
1740
1781
  | `update/release.ts` | Pi's own release lifecycle: install the notification patch in `tui` mode, record the release's version for the agent-line marker, post the notice once per load while the session is live, and restore the method on shutdown. |