@gavin-hu/my-pi-agent 0.8.0 → 0.9.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 (98) hide show
  1. package/README.md +9 -8
  2. package/README.zh-CN.md +8 -8
  3. package/assets/file-browse.png +0 -0
  4. package/assets/plan-review.png +0 -0
  5. package/extensions/bytefly/README.md +49 -36
  6. package/extensions/bytefly/doctor/format.ts +36 -10
  7. package/extensions/bytefly/doctor/tools.ts +6 -5
  8. package/extensions/bytefly/footer/config.ts +1 -1
  9. package/extensions/bytefly/footer/render/format.ts +5 -18
  10. package/extensions/bytefly/footer/render/layout.ts +2 -1
  11. package/extensions/bytefly/footer/render/segments.ts +51 -78
  12. package/extensions/bytefly/header/component.ts +33 -16
  13. package/extensions/bytefly/header/config.ts +28 -18
  14. package/extensions/bytefly/index.ts +1 -1
  15. package/extensions/bytefly/panel/commands.ts +69 -35
  16. package/extensions/bytefly/panel/format.ts +8 -8
  17. package/extensions/bytefly/panel/tui.ts +45 -70
  18. package/extensions/bytefly/separator/config.ts +16 -8
  19. package/extensions/bytefly/separator/format.ts +13 -40
  20. package/extensions/bytefly/separator/types.ts +0 -10
  21. package/extensions/document/README.md +1 -1
  22. package/extensions/document/render.ts +11 -10
  23. package/extensions/document/tool.ts +1 -1
  24. package/extensions/file-browse/README.md +62 -14
  25. package/extensions/file-browse/client.ts +103 -2
  26. package/extensions/file-browse/config.ts +12 -0
  27. package/extensions/file-browse/diff.ts +187 -0
  28. package/extensions/file-browse/git.ts +176 -1
  29. package/extensions/file-browse/html.ts +412 -23
  30. package/extensions/file-browse/index.ts +15 -4
  31. package/extensions/file-browse/markdown.ts +98 -5
  32. package/extensions/file-browse/router.ts +94 -2
  33. package/extensions/file-browse/server.ts +4 -1
  34. package/extensions/goal/format.ts +11 -10
  35. package/extensions/goal/tools.ts +6 -5
  36. package/extensions/goal/tui.ts +1 -1
  37. package/extensions/job/README.md +2 -2
  38. package/extensions/job/format.ts +36 -13
  39. package/extensions/job/index.ts +1 -1
  40. package/extensions/job/tools.ts +11 -10
  41. package/extensions/job/tui.ts +141 -220
  42. package/extensions/job/ui.ts +24 -49
  43. package/extensions/memory/README.md +1 -1
  44. package/extensions/memory/tools.ts +4 -3
  45. package/extensions/memory/tui.ts +32 -36
  46. package/extensions/plan/README.md +64 -34
  47. package/extensions/plan/blocks.ts +44 -2
  48. package/extensions/plan/feedback.ts +20 -5
  49. package/extensions/plan/index.ts +1 -1
  50. package/extensions/plan/runtime.ts +3 -2
  51. package/extensions/plan/tools.ts +23 -22
  52. package/extensions/plan/tui.ts +21 -27
  53. package/extensions/plan/web-client.ts +335 -30
  54. package/extensions/plan/web-page.ts +110 -30
  55. package/extensions/plan/web-router.ts +6 -4
  56. package/extensions/plan/web.ts +41 -10
  57. package/extensions/question/README.md +1 -1
  58. package/extensions/question/tools.ts +12 -11
  59. package/extensions/question/tui.ts +132 -76
  60. package/extensions/rewind/README.md +1 -1
  61. package/extensions/rewind/format.ts +29 -1
  62. package/extensions/rewind/rewind-tui.ts +66 -62
  63. package/extensions/rewind/status.ts +3 -2
  64. package/extensions/subagent/render.ts +62 -47
  65. package/extensions/todo/README.md +2 -2
  66. package/extensions/todo/format.ts +31 -10
  67. package/extensions/todo/tools.ts +5 -4
  68. package/extensions/todo/tui.ts +45 -26
  69. package/extensions/watch/README.md +2 -2
  70. package/extensions/watch/format.ts +33 -13
  71. package/extensions/watch/index.ts +1 -1
  72. package/extensions/watch/tools.ts +12 -7
  73. package/extensions/watch/tui.ts +135 -194
  74. package/extensions/watch/ui.ts +24 -46
  75. package/extensions/web-access/fetch/tool.ts +12 -11
  76. package/extensions/web-access/search/tool.ts +16 -13
  77. package/extensions/wechat/README.md +2 -2
  78. package/extensions/wechat/index.ts +4 -3
  79. package/extensions/wechat/login-screen.ts +8 -5
  80. package/extensions/wechat/status-screen.ts +41 -22
  81. package/extensions/wechat/tools.ts +6 -5
  82. package/extensions/worktree/enter.ts +1 -1
  83. package/extensions/worktree/index.ts +1 -1
  84. package/extensions/worktree/runtime.ts +1 -1
  85. package/lib/README.md +10 -7
  86. package/lib/requirements.ts +2 -2
  87. package/lib/{tui.ts → tui/chrome.ts} +9 -30
  88. package/lib/tui/columns.ts +96 -0
  89. package/lib/{list-cursor.ts → tui/cursor.ts} +56 -3
  90. package/lib/tui/gauge.ts +78 -0
  91. package/lib/tui/index.ts +25 -0
  92. package/lib/tui/line-pane.ts +173 -0
  93. package/lib/tui/rows.ts +109 -0
  94. package/lib/tui/rules.ts +88 -0
  95. package/lib/tui/status-controller.ts +107 -0
  96. package/lib/tui/tokens.ts +58 -0
  97. package/package.json +2 -2
  98. /package/lib/{ui.ts → tui/glyphs.ts} +0 -0
package/README.md CHANGED
@@ -11,7 +11,7 @@ context across sessions. `my-pi-agent` is a single [Pi](https://pi.dev) package
11
11
  that adds sixteen extensions and two themes — install the pack, turn off what
12
12
  you do not want, and keep using Pi unchanged.
13
13
 
14
- <img src="./assets/bytefly-chrome.png" alt="A terminal window running the Pi TUI with the bytefly header, hint row, context and loaded extensions, a turn separator, and the status bar" width="100%">
14
+ ![A terminal window running the Pi TUI with the bytefly header, hint row, context and loaded extensions, a turn separator, and the status bar](assets/bytefly-chrome.png)
15
15
 
16
16
  ## Contents
17
17
 
@@ -59,10 +59,11 @@ pi -e . # try it for one run
59
59
 
60
60
  Then, from inside Pi:
61
61
 
62
- - `/extensions` (an alias of `/bytefly`) lists every loaded extension with its
63
- runtime health and enables or disables one.
64
- - `/doctor` opens the same screen, narrowed to one extension when named, and
65
- checks that each enabled extension has what it needs at runtime.
62
+ - `/bytefly [extension] [refresh]` opens the extension screen, lists every loaded
63
+ extension with its runtime health, and enables or disables one.
64
+ - `/bytefly doctor [extension] [refresh]` prints the doctor report —
65
+ `run_doctor`'s text — and checks that each enabled extension has what it needs
66
+ at runtime.
66
67
  - `/settings` → **Theme** selects a theme.
67
68
  - `/plan <task>` starts a task in read-only planning mode.
68
69
 
@@ -105,7 +106,7 @@ extension's own `README.md` has the full tool and command surface.
105
106
 
106
107
  | Extension | Tools and commands | What it does |
107
108
  |---|---|---|
108
- | [`bytefly`](./extensions/bytefly/) | `/bytefly`, `/extensions`, `/doctor`, `run_doctor` | A themed header and width-adaptive footer, a rule between completed turns, the `/bytefly` extension screen (aliases `/extensions` and `/doctor`) that lists every extension with its health and enables or disables one, and an opt-in `notify` capability that raises OS notifications when a run or a background job finishes or the agent asks for input. DeepSeek is priced out of the box, and `bytefly.json` is the shared config document every extension reads. |
109
+ | [`bytefly`](./extensions/bytefly/) | `/bytefly` (with the `doctor` subcommand), `run_doctor` | A themed header and width-adaptive footer, a rule between completed turns, the `/bytefly` extension screen that lists every extension with its health and enables or disables one (`/bytefly doctor` prints the doctor report), and an opt-in `notify` capability that raises OS notifications when a run or a background job finishes or the agent asks for input. DeepSeek is priced out of the box, and `bytefly.json` is the shared config document every extension reads. |
109
110
  | [`bytefly-dark`](./themes/bytefly-dark.json) / [`bytefly-light`](./themes/bytefly-light.json) | `/settings` → **Theme** | A GitHub-inspired dark palette and its light companion, with optional automatic light/dark switching. |
110
111
 
111
112
  ## Configuration
@@ -146,9 +147,9 @@ stops the factories from registering; to drop the whole package, use Pi's
146
147
  - **OS** — Windows, macOS, and Linux; the full check suite runs on all three in
147
148
  CI.
148
149
  - **git** on `PATH` for the `worktree` and `rewind` extensions; everything else
149
- degrades or disables cleanly, and `/doctor` tells you what is missing.
150
+ degrades or disables cleanly, and `/bytefly doctor` tells you what is missing.
150
151
  - Optional runtime pieces (for example a browser for HTML exports) are reported
151
- per extension by `/doctor`.
152
+ per extension by `/bytefly doctor`.
152
153
 
153
154
  ## Contributing
154
155
 
package/README.zh-CN.md CHANGED
@@ -10,7 +10,7 @@
10
10
  `my-pi-agent` 是一个 [Pi](https://pi.dev) 包,新增十六个扩展和两个主题 —— 安装
11
11
  整个包,关掉你不需要的部分,其余照常使用 Pi。
12
12
 
13
- <img src="./assets/bytefly-chrome.png" alt="一个运行 Pi TUI 的终端窗口,包含 bytefly 头部、提示行、上下文与已加载扩展、回合分隔线以及状态栏" width="100%">
13
+ ![一个运行 Pi TUI 的终端窗口,包含 bytefly 头部、提示行、上下文与已加载扩展、回合分隔线以及状态栏](assets/bytefly-chrome.png)
14
14
 
15
15
  ## 目录
16
16
 
@@ -55,10 +55,10 @@ pi -e . # 试运行一次
55
55
 
56
56
  然后在 Pi 中:
57
57
 
58
- - `/extensions`(`/bytefly` 的别名)列出每个已加载扩展及其运行时健康状况,并启用
59
- 或禁用一个扩展。
60
- - `/doctor` 打开同一屏幕,指定扩展名时只显示该扩展,并检查每个已启用扩展在运行时
61
- 是否具备所需条件。
58
+ - `/bytefly [extension] [refresh]` 打开扩展屏幕,列出每个已加载扩展及其运行时健康
59
+ 状况,并启用或禁用一个扩展。
60
+ - `/bytefly doctor [extension] [refresh]` 打印 doctor 报告(`run_doctor` 的文本),
61
+ 检查每个已启用扩展在运行时是否具备所需条件。
62
62
  - `/settings` → **Theme** 选择主题。
63
63
  - `/plan <任务>` 以只读规划模式开始一个任务。
64
64
 
@@ -101,7 +101,7 @@ pi -e . # 试运行一次
101
101
 
102
102
  | 扩展 | 工具与命令 | 说明 |
103
103
  |---|---|---|
104
- | [`bytefly`](./extensions/bytefly/) | `/bytefly`, `/extensions`, `/doctor`, `run_doctor` | 主题化头部与宽度自适应页脚、已完成回合之间的分隔线、`/bytefly` 扩展屏幕(别名 `/extensions` 和 `/doctor`)——列出每个扩展及其健康状况并启用或禁用一个扩展;以及可选的 `notify` 能力:当一次运行或后台任务结束、或智能体请求输入时发出系统通知。DeepSeek 开箱即定价,`bytefly.json` 是每个扩展都会读取的共享配置文档。 |
104
+ | [`bytefly`](./extensions/bytefly/) | `/bytefly`(`doctor` 子命令)、`run_doctor` | 主题化头部与宽度自适应页脚、已完成回合之间的分隔线、`/bytefly` 扩展屏幕——列出每个扩展及其健康状况并启用或禁用一个扩展(`/bytefly doctor` 打印 doctor 报告);以及可选的 `notify` 能力:当一次运行或后台任务结束、或智能体请求输入时发出系统通知。DeepSeek 开箱即定价,`bytefly.json` 是每个扩展都会读取的共享配置文档。 |
105
105
  | [`bytefly-dark`](./themes/bytefly-dark.json) / [`bytefly-light`](./themes/bytefly-light.json) | `/settings` → **Theme** | GitHub 风格的深色调色板及其浅色搭档,并支持可选的自动浅色/深色切换。 |
106
106
 
107
107
  ## 配置
@@ -139,8 +139,8 @@ Pi 的 `--no-extensions`,或从你的设置中删除该包。
139
139
  - **Pi** —— 需要一个可用的安装;`my-pi-agent` 是一个包,不是分叉。
140
140
  - **操作系统** —— Windows、macOS 和 Linux;完整检查套件在三者的 CI 上都会运行。
141
141
  - **git** 需在 `PATH` 上,供 `worktree` 和 `rewind` 扩展使用;其余部分会优雅降级或
142
- 禁用,`/doctor` 会告诉你缺什么。
143
- - 可选的运行时组件(例如用于 HTML 导出的浏览器)由 `/doctor` 按扩展报告。
142
+ 禁用,`/bytefly doctor` 会告诉你缺什么。
143
+ - 可选的运行时组件(例如用于 HTML 导出的浏览器)由 `/bytefly doctor` 按扩展报告。
144
144
 
145
145
  ## 参与贡献
146
146
 
Binary file
Binary file
@@ -23,17 +23,17 @@ The extension owns five surfaces: the startup header, the footer, the rule
23
23
  between completed turns, the extension screen, and notifications.
24
24
 
25
25
  **Header.** A nameplate: a two-tone half-block mark from the active theme's
26
- `accent` / `warning` tokens, the agent identity (`Pi` and its version), the
27
- chrome identity (`enhanced by bytefly` and the package version), a `─` rule, and
28
- a grouped help block. The help keys resolve live from Pi's keybindings, so a
29
- rebound key shows the user's chord; the identity and help text are `muted`/`dim`
30
- so the mark keeps the eye. It is installed only in interactive (`tui`) sessions,
31
- and on terminals that cannot render half blocks the mark falls back to a
32
- two-tone `Pi` wordmark.
26
+ `accent` / `warn` roles, the agent identity (`Pi`, bold, and its version), the
27
+ chrome identity (`enhanced by bytefly` and the package version), a signature
28
+ rule whose `accent` lead sits under the mark, and a grouped help block. The help
29
+ keys resolve live from Pi's keybindings, so a rebound key shows the user's
30
+ chord; the identity and help text are `meta` / `faint` so the mark keeps the
31
+ eye. It is installed only in interactive (`tui`) sessions, and on terminals that
32
+ cannot render half blocks the mark falls back to a two-tone `Pi` wordmark.
33
33
 
34
34
  ```
35
35
  █▀█ Pi v1.2.0
36
- █▀ █ enhanced by bytefly v0.7.0
36
+ █▀ █ enhanced by bytefly v0.8.1
37
37
  ─────────────────────────────────────────────────
38
38
  escape to interrupt · ctrl+c to clear · ctrl+d to exit (empty)
39
39
  ctrl+o to expand tools · ctrl+t to expand thinking · ctrl+l to select model
@@ -133,19 +133,18 @@ the platform notifier (`osascript`, `notify-send`); see
133
133
  | Command | What it does |
134
134
  |---|---|
135
135
  | `/bytefly [extension] [refresh]` | Open the extension screen. In `tui` it mounts a docked list; every other mode prints the grouped listing, or one extension's full findings when named. |
136
- | `/extensions` | Alias for `/bytefly`: the same screen and the same handler. |
137
- | `/doctor [extension] [refresh]` | Alias for `/bytefly`: the same screen and the same handler. |
136
+ | `/bytefly doctor [extension] [refresh]` | Print the doctor report. Outside `tui` it prints `run_doctor`'s text for every extension, or one named extension's findings; in `tui` it mounts the same docked screen. |
138
137
 
139
- Tab completes extension names and `refresh` for all three commands.
138
+ Tab completes `doctor`, extension names, and `refresh`.
140
139
 
141
140
  ## Behaviour by mode
142
141
 
143
142
  The header, footer, and turn rule are installed only in interactive (`tui`)
144
143
  sessions and are always on; every mode loads the extension, and non-TUI modes
145
144
  change nothing. The screen is available in every mode: in `tui`, `/bytefly` and
146
- its `/extensions` and `/doctor` aliases mount the docked screen, while RPC,
147
- JSON, and print modes print the grouped listing with a per-row health suffix and
148
- the headline, or that extension's full findings when one is named. The
145
+ `/bytefly doctor` mount the docked screen, while RPC, JSON, and print modes
146
+ print the grouped listing with a per-row health suffix and the headline, or that
147
+ extension's full findings when one is named. The
149
148
  proactive session-start warning is skipped when there is no UI. Notify runs in
150
149
  every mode and picks its method from the environment; the OSC methods need a
151
150
  TTY, so a print/JSON/RPC run gets an OS-level notification or none.
@@ -394,10 +393,13 @@ it:
394
393
  extension Pi resolves for the session, so the list matches what actually loads.
395
394
  It merges the former extensions picker and doctor report into one row per
396
395
  extension: the picker's enable checkbox and label, the doctor's worst-severity
397
- glyph and summary. `/extensions` and `/doctor [extension] [refresh]` are
398
- registered against the same handler, so all three names open the same screen;
399
- `[extension]` narrows it to one extension and `refresh` re-probes the doctor
400
- before it opens. Tab completes extension names and `refresh`.
396
+ glyph and summary. The screen is drawn from the design system: a label-first
397
+ `bytefly ────` title rule, one row grammar (`❯ ◼ ✓ name … summary`), and the
398
+ shared `◼` / `◻` checkbox. `/bytefly doctor [extension] [refresh]` selects the doctor
399
+ report, the same text the `run_doctor` tool returns; in `tui` it opens the same
400
+ screen. `[extension]` narrows either surface to one extension and `refresh`
401
+ re-probes the doctor before it opens. Tab completes `doctor`, extension names,
402
+ and `refresh`.
401
403
 
402
404
  The screen reuses Pi's own settings and package resolution (`SettingsManager`
403
405
  plus `DefaultPackageManager` with `onMissing: "skip"`), so opening it never
@@ -408,7 +410,7 @@ installs or fetches a package.
408
410
  - Omits `bytefly`'s own entrypoint from the picker groups, so the tool that
409
411
  manages the list is never a selectable row; its doctor report still appears in
410
412
  the trailing `Not resolved` group.
411
- - Shows whether each one is enabled (`[x]`) or disabled (`[ ]`).
413
+ - Shows whether each one is enabled (`◼`) or disabled (`◻`).
412
414
  - Shows the worst severity for a reported extension (`✓` ok, `!` degraded, `✕`
413
415
  missing, `∅` disabled) and its summary; an extension with no doctor report
414
416
  leaves the glyph column and the summary blank rather than claiming `ok`.
@@ -524,6 +526,9 @@ control-free line before it reaches an escape sequence or a shell argument.
524
526
 
525
527
  ## Limitations
526
528
 
529
+ - **`doctor` is a reserved first argument.** `/bytefly doctor` always selects the
530
+ report, so an extension literally named `doctor` cannot be narrowed to by
531
+ name.
527
532
  - **The picker manages only the `extensions` resource type.** Skills, prompts,
528
533
  and themes are handled by Pi's own `pi config`.
529
534
  - **Built-in extensions are not listed** (`builtin:mcp`, `builtin:codemode`, …):
@@ -602,7 +607,7 @@ rather than showing a wrong value.
602
607
  |---|---|
603
608
  | Header | `ctx.ui.setHeader` with a custom component, replacing Pi's built-in header; installed only when `ctx.mode === "tui"`. The mark and text are drawn from the active theme's tokens, the help keys resolve live through `keyText`, and the mark falls back to a wordmark on Apple Terminal (`isAppleTerminalSession`), mirroring Pi's built-in. |
604
609
  | Footer | `ctx.ui.setFooter` with a custom component, replacing Pi's built-in footer; installed only when `ctx.mode === "tui"`. |
605
- | Screen command | `pi.registerCommand("bytefly"`, `"extensions"`, and `"doctor"`) share one handler: `tui` mounts a dock screen via `ctx.ui.custom()` with the rails suppressed, other modes print through `ctx.ui.notify()`. Tab completes extension names and `refresh`. |
610
+ | Screen command | `pi.registerCommand("bytefly")` takes `[doctor] [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, or `run_doctor`'s report for `doctor`. Tab completes `doctor`, extension names, and `refresh`. |
606
611
  | Turn separator | `registerEntryRenderer("bytefly-separator")` draws each rule; `agent_settled` appends `pi.appendEntry("bytefly-separator", { turn })` in `tui` mode only. |
607
612
  | 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. |
608
613
  | Picker discovery | `SettingsManager.create(...)` plus `DefaultPackageManager.resolve({ onMissing: "skip" })`, both public exports; both views drop `bytefly`'s own entrypoint. |
@@ -617,12 +622,18 @@ rather than showing a wrong value.
617
622
 
618
623
  ## Design notes
619
624
 
625
+ - **One kit, one look.** Every bytefly surface is drawn from the package's
626
+ `lib/tui/` design system: a role is resolved by the active theme, a rule comes
627
+ from `rules.ts`, a list row from `rows.ts`, a meter from `gauge.ts`, and a
628
+ width from `columns.ts`. Nothing in bytefly names a literal color, and a
629
+ theme swap re-skins the whole chrome.
620
630
  - **The header is a nameplate, not a dashboard.** Pi's built-in brand colors are
621
631
  fixed, so the mark ignores the active theme; bytefly's header draws a two-tone
622
- mark from `accent`/`warning` and pairs it with the agent identity, the chrome
623
- identity, and a grouped help block. It adds no session or model data — that is
624
- the footer's job — and it stays a fixed four-plus rows so the startup
625
- transcript never reflows.
632
+ mark from the `accent`/`warn` roles, a signature rule whose accent lead sits
633
+ under the mark, and pairs them with the agent identity, the chrome identity,
634
+ and a grouped help block. It adds no session or model data — that is the
635
+ footer's job — and it stays a fixed four-plus rows so the startup transcript
636
+ never reflows.
626
637
  - **Two lines, four zones.** Line 1 is identity (`pwd` left, branch + worktree
627
638
  right); line 2 is resources (meters left, model + level right). This keeps
628
639
  "where am I / what state" separate from "how much am I burning".
@@ -722,7 +733,7 @@ rather than showing a wrong value.
722
733
  reads the same command whether a tool failed or the report named it.
723
734
  - **The doctor is quiet by default at startup.** Only `missing` findings warn,
724
735
  only for enabled extensions, only with a UI, and only once per session;
725
- `degraded` findings — including a malformed config — wait in `/doctor`, because
736
+ `degraded` findings — including a malformed config — wait in `/bytefly doctor`, because
726
737
  a session that nags about an optional package is worse than one that never
727
738
  mentions it.
728
739
  - **Probe the real thing.** `subagent`'s requirement resolves through the same
@@ -737,13 +748,15 @@ rather than showing a wrong value.
737
748
  The surfaces are folders: `header/`, `footer/` (the status bar and its price
738
749
  calculation), `separator/`, `panel/` (the unified screen and its merged model),
739
750
  `picker/` (discovery and the settings store), `doctor/` (the `run_doctor` tool
740
- and its probe runtime), and `notify/`.
751
+ and its probe runtime), and `notify/`. The chrome and the screen are drawn from
752
+ the package's [`lib/tui/`](../../lib/tui/) design system (tokens, rules, rows,
753
+ columns, gauge), so the look follows the active theme.
741
754
 
742
755
  | File | Responsibility |
743
756
  |---|---|
744
757
  | `index.ts` | Composition root: the disable guard and the six `register*` calls. |
745
- | `header/config.ts` | Header tuning (`HEADER_CONFIG`): mark and identity tokens, padding, tagline, and the grouped help entries. |
746
- | `header/component.ts` | `ByteflyHeader`: the mark, identity rows, rule, and grouped help, plus its factory. |
758
+ | `header/config.ts` | Header tuning (`HEADER_CONFIG`): mark and identity roles, padding, tagline, and the grouped help entries. |
759
+ | `header/component.ts` | `ByteflyHeader`: the mark, identity rows, signature rule, and grouped help, plus its factory. |
747
760
  | `header/register.ts` | Installs/restores the header and owns its session lifecycle. |
748
761
  | `footer/register.ts` | Installs/restores the footer and owns its session lifecycle. |
749
762
  | `footer/config.ts` | Bar tuning (`CONFIG`): thresholds, gauge, separators, icons, status keys, labels. |
@@ -755,22 +768,22 @@ and its probe runtime), and `notify/`.
755
768
  | `footer/price/rates.ts` | Model-key matching: exact and wildcard, most specific first. |
756
769
  | `footer/price/schedule.ts` | Peak-window parsing and evaluation in a configured zone. |
757
770
  | `footer/price/price.ts` | Records → totals: rates, peak multiplier, recorded-cost fallback. |
758
- | `footer/render/format.ts` | Generic formatters (tokens, cost in the configured currency, cwd, gauge, colors). |
771
+ | `footer/render/format.ts` | Generic formatters (tokens, cost in the configured currency, cwd, the context role, truncation). |
759
772
  | `footer/render/status.ts` | Status-chip transforms: `compactStatus` and `dropStatusDetail`. |
760
- | `footer/render/segments.ts` | The segment builders: forms, weights, separators, colors. |
773
+ | `footer/render/segments.ts` | The segment builders: forms, weights, separators, roles. |
761
774
  | `footer/render/lines.ts` | Snapshot → two `LineSpec`s; status routing. |
762
775
  | `footer/render/layout.ts` | Width reduction and right-zone folding. |
763
776
  | `footer/render/footer.ts` | `StatusBar` component and footer factory. |
764
- | `separator/config.ts` | Turn-rule tuning: `customType`, glyph, label prefix, padding, and colors. |
777
+ | `separator/config.ts` | Turn-rule tuning: `customType`, glyph, label prefix, padding, and roles. |
765
778
  | `separator/register.ts` | Registers the entry renderer and appends a separator on `agent_settled`. |
766
779
  | `separator/component.ts` | `SeparatorComponent`: the stateless entry renderer. |
767
- | `separator/format.ts` | Pure `separatorLabel` / `separatorParts` / `separatorLine`. |
768
- | `separator/types.ts` | `TurnSeparatorData`, `SeparatorParts`. |
780
+ | `separator/format.ts` | Pure `separatorLine`: the centered turn rule, drawn by the kit's `centeredRule`. |
781
+ | `separator/types.ts` | `TurnSeparatorData`. |
769
782
  | `panel/types.ts` | `PanelRow` and `PanelModel`, the merged row model. |
770
783
  | `panel/join.ts` | The item/report union with the unambiguous-name health rule and unique row keys, groups, and ordering. |
771
784
  | `panel/format.ts` | Non-TUI text: the headline and the grouped listing with a per-row health suffix. |
772
- | `panel/tui.ts` | `PanelComponent`, the unified dock screen. |
773
- | `panel/commands.ts` | `/bytefly`, `/extensions`, and `/doctor`: one runner for the screen and its text modes. |
785
+ | `panel/tui.ts` | `PanelComponent`, the unified dock screen, drawn from the `lib/tui/` kit. |
786
+ | `panel/commands.ts` | `/bytefly`: one command for the screen, the `doctor` report, and their text modes. |
774
787
  | `picker/types.ts` | Picker host seams (`SettingsStore`, `ExtensionResolver`, `Discovery`) and row types. |
775
788
  | `picker/resources.ts` | Pure grouping, labels, display names, and pattern helpers. |
776
789
  | `picker/toggle.ts` | Pure next-settings transforms for global and project toggles. |
@@ -781,7 +794,7 @@ and its probe runtime), and `notify/`.
781
794
  | `doctor/tools.ts` | The `run_doctor` tool and its renderers. |
782
795
  | `doctor/schema.ts` | Tool parameters and the structured output schema. |
783
796
  | `doctor/config.ts` | `DEFAULT_CONFIG`, `normalizeConfig`, `loadConfig` for the `doctor` section of `bytefly.json`. |
784
- | `doctor/format.ts` | Pure report text, summary, glyphs, detail lines, and the proactive warning sentence. |
797
+ | `doctor/format.ts` | Pure report text, summary, severity roles and glyphs, detail lines, and the proactive warning sentence. |
785
798
  | `notify/types.ts` | `NotifyConfig`, `NotifyMethod`, and the method list. |
786
799
  | `notify/config.ts` | The `notify` section of `bytefly.json`: defaults, validation, loader. |
787
800
  | `notify/dispatch.ts` | Method selection and delivery: OSC 777/99, Windows toast, `osascript`, `notify-send`, over injected seams. |
@@ -12,7 +12,8 @@ import type { Theme } from "@earendil-works/pi-coding-agent";
12
12
  import { wrapTextWithAnsi } from "@earendil-works/pi-tui";
13
13
  import { sanitizeLine } from "../../../lib/format.ts";
14
14
  import type { ExtensionReport, Finding, FindingSeverity } from "../../../lib/requirements.ts";
15
- import { GLYPHS } from "../../../lib/ui.ts";
15
+ import { GLYPHS } from "../../../lib/tui/glyphs.ts";
16
+ import { paint, type TuiRole } from "../../../lib/tui/tokens.ts";
16
17
 
17
18
  /** Counts the report headline uses. */
18
19
  export interface ReportCounts {
@@ -102,15 +103,40 @@ export function formatWarning(findings: readonly Finding[]): string {
102
103
  byExtension.set(finding.extension, list);
103
104
  }
104
105
  const parts = [...byExtension].map(([extension, checks]) => `${sanitizeLine(extension)} (${checks.join(", ")})`);
105
- return `doctor: missing ${parts.join("; ")} — run /doctor`;
106
+ return `doctor: missing ${parts.join("; ")} — run /bytefly doctor`;
106
107
  }
107
108
 
108
- /** The severity glyph, from the shared vocabulary. */
109
+ /** The design role a severity is drawn in. */
110
+ export function severityRole(severity: FindingSeverity): TuiRole {
111
+ switch (severity) {
112
+ case "missing":
113
+ return "bad";
114
+ case "degraded":
115
+ return "warn";
116
+ case "info":
117
+ return "faint";
118
+ default:
119
+ return "ok";
120
+ }
121
+ }
122
+
123
+ /** The bare severity glyph, from the shared vocabulary. */
124
+ export function severityGlyphChar(severity: FindingSeverity): string {
125
+ switch (severity) {
126
+ case "missing":
127
+ return GLYPHS.cross;
128
+ case "degraded":
129
+ return GLYPHS.warn;
130
+ case "info":
131
+ return GLYPHS.disabled;
132
+ default:
133
+ return GLYPHS.check;
134
+ }
135
+ }
136
+
137
+ /** The severity glyph, themed by role. */
109
138
  export function severityGlyph(severity: FindingSeverity, theme: Theme): string {
110
- if (severity === "missing") return theme.fg("error", GLYPHS.cross);
111
- if (severity === "degraded") return theme.fg("warning", GLYPHS.warn);
112
- if (severity === "info") return theme.fg("dim", GLYPHS.disabled);
113
- return theme.fg("success", GLYPHS.check);
139
+ return paint(theme, severityRole(severity), severityGlyphChar(severity));
114
140
  }
115
141
 
116
142
  /** The severity that represents an extension: worst finding wins. */
@@ -147,7 +173,7 @@ export function detailLines(
147
173
  const budget = Math.max(1, rows);
148
174
  const shown = includeInfo ? report.findings : report.findings.filter((finding) => finding.severity !== "info");
149
175
  if (shown.length === 0) {
150
- return [` ${theme.fg("dim", report.enabled ? "no problems" : "disabled")}`];
176
+ return [` ${paint(theme, "faint", report.enabled ? "no problems" : "disabled")}`];
151
177
  }
152
178
 
153
179
  const lines: string[] = [];
@@ -160,10 +186,10 @@ export function detailLines(
160
186
  }
161
187
  if (finding.hint) {
162
188
  for (const line of wrapTextWithAnsi(`→ ${sanitizeLine(finding.hint)}`, Math.max(1, width - 4))) {
163
- lines.push(` ${theme.fg("dim", line)}`);
189
+ lines.push(` ${paint(theme, "faint", line)}`);
164
190
  }
165
191
  }
166
192
  }
167
193
  if (lines.length <= budget) return lines;
168
- return [...lines.slice(0, budget - 1), ` ${theme.fg("dim", "…")}`];
194
+ return [...lines.slice(0, budget - 1), ` ${paint(theme, "faint", "…")}`];
169
195
  }
@@ -9,7 +9,8 @@
9
9
 
10
10
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
11
11
  import { missingFindings } from "../../../lib/requirements.ts";
12
- import { reuseText, toolDetails } from "../../../lib/tui.ts";
12
+ import { reuseText, toolDetails } from "../../../lib/tui/chrome.ts";
13
+ import { paint, type TuiRole } from "../../../lib/tui/tokens.ts";
13
14
  import { formatExtension, formatReport, isProblemSeverity } from "./format.ts";
14
15
  import type { DoctorRuntime } from "./runtime.ts";
15
16
  import { DoctorOutput, DoctorParams, TOOL_NAME, type DoctorArgs, type DoctorStructured } from "./schema.ts";
@@ -93,20 +94,20 @@ export function registerTools(pi: ExtensionAPI, runtime: DoctorRuntime): void {
93
94
  const input = args as DoctorArgs;
94
95
  const scope = `${input.extension ? ` ${input.extension}` : ""}${input.refresh ? " (refresh)" : ""}`;
95
96
  const text = reuseText(context?.lastComponent);
96
- text.setText(`${theme.fg("toolTitle", "doctor")}${theme.fg("dim", scope)}`);
97
+ text.setText(`${theme.fg("toolTitle", "doctor")}${paint(theme, "faint", scope)}`);
97
98
  return text;
98
99
  },
99
100
 
100
101
  renderResult(result, _options, theme, context) {
101
102
  const structured = toolDetails<DoctorStructured>(result.details);
102
- const tone = (structured?.missing ?? 0) > 0 ? "error" : (structured?.degraded ?? 0) > 0 ? "warning" : "success";
103
+ const tone: TuiRole = (structured?.missing ?? 0) > 0 ? "bad" : (structured?.degraded ?? 0) > 0 ? "warn" : "ok";
103
104
  const text = reuseText(context?.lastComponent);
104
105
  if (!structured) {
105
106
  const first = result.content[0];
106
- text.setText(first?.type === "text" ? first.text : theme.fg("muted", "doctor: no result"));
107
+ text.setText(first?.type === "text" ? first.text : paint(theme, "meta", "doctor: no result"));
107
108
  return text;
108
109
  }
109
- text.setText(theme.fg(tone, summaryLines(structured).join("\n")));
110
+ text.setText(paint(theme, tone, summaryLines(structured).join("\n")));
110
111
  return text;
111
112
  },
112
113
  });
@@ -7,7 +7,7 @@
7
7
  * configuration is `bytefly.json`, read by `../price/config.ts`.
8
8
  */
9
9
 
10
- import { GLYPHS, SEPARATORS, STATUS_KEYS } from "../../../lib/ui.ts";
10
+ import { GLYPHS, SEPARATORS, STATUS_KEYS } from "../../../lib/tui/glyphs.ts";
11
11
 
12
12
  export const CONFIG = {
13
13
  /** Context percentage thresholds. */
@@ -6,9 +6,10 @@
6
6
  */
7
7
 
8
8
  import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
9
- import type { ThemeColor } from "@earendil-works/pi-coding-agent";
10
9
  import { stripTerminalSequences, truncateToWidth } from "@earendil-works/pi-tui";
11
10
  import { shortenPath } from "../../../../lib/format.ts";
11
+ import { gaugeRole } from "../../../../lib/tui/gauge.ts";
12
+ import type { TuiRole } from "../../../../lib/tui/tokens.ts";
12
13
  import { CONFIG } from "../config.ts";
13
14
  import { DEFAULT_CURRENCY } from "../price/config.ts";
14
15
  import type { Currency } from "../price/types.ts";
@@ -65,23 +66,9 @@ export function formatCwd(cwd: string, home: string | undefined, level: 0 | 1 |
65
66
  return parts.length <= 2 ? shortened : `…/${parts.slice(-2).join("/")}`;
66
67
  }
67
68
 
68
- /** Filled/empty block counts for a percentage across `blocks` cells. */
69
- export function computeGauge(percent: number | null, blocks: number): { filled: number; empty: number } {
70
- if (blocks <= 0) return { filled: 0, empty: 0 };
71
- if (percent === null) return { filled: 0, empty: blocks };
72
- const clamped = Math.max(0, Math.min(100, percent));
73
- // Floor so the bar is only full at 100%; force one block for any non-zero
74
- // usage so a small percentage is still visible.
75
- const filled = Math.max(clamped > 0 ? 1 : 0, Math.floor((clamped / 100) * blocks));
76
- return { filled, empty: blocks - filled };
77
- }
78
-
79
- /** Gauge color for a context percentage. Neutral until it crosses a threshold. */
80
- export function contextColor(percent: number | null): ThemeColor {
81
- if (percent === null) return "muted";
82
- if (percent > CONFIG.thresholds.danger) return "error";
83
- if (percent > CONFIG.thresholds.warn) return "warning";
84
- return "muted";
69
+ /** The design role a context percentage is drawn in. Neutral until it crosses a threshold. */
70
+ export function contextRole(percent: number | null): TuiRole {
71
+ return gaugeRole(percent, CONFIG.thresholds);
85
72
  }
86
73
 
87
74
  const THINKING_TOKENS: Record<ThinkingLevel, ThinkingColor> = {
@@ -10,6 +10,7 @@
10
10
 
11
11
  import type { Theme } from "@earendil-works/pi-coding-agent";
12
12
  import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
13
+ import { paint } from "../../../../lib/tui/tokens.ts";
13
14
  import { CONFIG } from "../config.ts";
14
15
  import type { LineSpec, Segment } from "../types.ts";
15
16
 
@@ -135,5 +136,5 @@ export function renderLine(spec: LineSpec, width: number, theme: Theme): string
135
136
  if (!collapseSeparator(states)) break;
136
137
  }
137
138
 
138
- return truncateToWidth(assemble(states, target), target, theme.fg("dim", "…"));
139
+ return truncateToWidth(assemble(states, target), target, paint(theme, "faint", "…"));
139
140
  }