@gavin-hu/my-pi-agent 0.15.0 → 0.16.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 (106) hide show
  1. package/README.md +12 -11
  2. package/README.zh-CN.md +9 -7
  3. package/extensions/bytefly/README.md +98 -116
  4. package/extensions/bytefly/notice/component.ts +6 -4
  5. package/extensions/bytefly/notice/config.ts +16 -2
  6. package/extensions/bytefly/panel/format.ts +3 -8
  7. package/extensions/bytefly/panel/join.ts +1 -6
  8. package/extensions/bytefly/panel/settings.ts +74 -377
  9. package/extensions/bytefly/panel/tui/entries.ts +16 -14
  10. package/extensions/bytefly/panel/tui/extensions-view.ts +3 -0
  11. package/extensions/bytefly/panel/tui/frame.ts +2 -2
  12. package/extensions/bytefly/panel/tui/settings-view.ts +8 -12
  13. package/extensions/bytefly/panel/tui.ts +4 -6
  14. package/extensions/bytefly/picker/resources.ts +7 -3
  15. package/extensions/bytefly/working/register.ts +2 -8
  16. package/extensions/check/tools.ts +2 -11
  17. package/extensions/context/README.md +162 -0
  18. package/extensions/context/collect.ts +53 -0
  19. package/extensions/context/commands.ts +100 -0
  20. package/extensions/context/format.ts +82 -0
  21. package/extensions/context/index.ts +16 -0
  22. package/extensions/context/report.ts +319 -0
  23. package/extensions/context/tui.ts +268 -0
  24. package/extensions/context/types.ts +122 -0
  25. package/extensions/file-browse/README.md +83 -8
  26. package/extensions/file-browse/client.ts +3 -1
  27. package/extensions/file-browse/config.ts +23 -0
  28. package/extensions/file-browse/files.ts +183 -48
  29. package/extensions/file-browse/fuzzy.ts +65 -11
  30. package/extensions/file-browse/html.ts +78 -133
  31. package/extensions/file-browse/index.ts +17 -2
  32. package/extensions/file-browse/live-client.ts +158 -0
  33. package/extensions/file-browse/live.ts +256 -0
  34. package/extensions/file-browse/markdown.ts +3 -10
  35. package/extensions/file-browse/raw-html.ts +3 -10
  36. package/extensions/file-browse/router.ts +33 -8
  37. package/extensions/file-browse/server.ts +78 -13
  38. package/extensions/file-browse/styles.ts +124 -0
  39. package/extensions/file-open/README.md +1 -2
  40. package/extensions/{document → file-read}/README.md +72 -56
  41. package/extensions/{document → file-read}/config.ts +12 -9
  42. package/extensions/{document → file-read}/extract/doc.ts +2 -2
  43. package/extensions/{document → file-read}/extract/docx.ts +3 -3
  44. package/extensions/{document → file-read}/extract/file.ts +9 -9
  45. package/extensions/{document → file-read}/extract/odt.ts +3 -3
  46. package/extensions/{document → file-read}/extract/pdf.ts +3 -3
  47. package/extensions/{document → file-read}/extract/rtf.ts +3 -3
  48. package/extensions/{document → file-read}/extract/xlsx.ts +4 -4
  49. package/extensions/{document → file-read}/formats.ts +11 -11
  50. package/extensions/file-read/index.ts +17 -0
  51. package/extensions/{document → file-read}/paging.ts +15 -13
  52. package/extensions/{document → file-read}/render.ts +17 -13
  53. package/extensions/{document → file-read}/schema.ts +7 -7
  54. package/extensions/{document → file-read}/tool.ts +36 -28
  55. package/extensions/goal/tools.ts +10 -14
  56. package/extensions/job/commands.ts +32 -46
  57. package/extensions/job/runtime.ts +6 -8
  58. package/extensions/job/tools.ts +7 -26
  59. package/extensions/memory/commands.ts +30 -44
  60. package/extensions/memory/tools.ts +6 -14
  61. package/extensions/plan/tools/enter.ts +6 -7
  62. package/extensions/plan/tools/exit.ts +6 -7
  63. package/extensions/plan/tools/steps.ts +6 -7
  64. package/extensions/plan/tools/write.ts +8 -9
  65. package/extensions/plan/web-router.ts +2 -12
  66. package/extensions/plan/web.ts +30 -15
  67. package/extensions/question/tools.ts +5 -4
  68. package/extensions/rewind/checklist.ts +10 -3
  69. package/extensions/rewind/git.ts +27 -14
  70. package/extensions/rewind/hunk-picker.ts +1 -1
  71. package/extensions/rewind/path-picker.ts +1 -1
  72. package/extensions/rewind/review-tui.ts +3 -3
  73. package/extensions/rewind/snapshot.ts +13 -4
  74. package/extensions/subagent/background-tui.ts +1 -1
  75. package/extensions/subagent/background.ts +1 -7
  76. package/extensions/todo/tools.ts +10 -14
  77. package/extensions/watch/README.md +5 -5
  78. package/extensions/watch/commands.ts +33 -47
  79. package/extensions/watch/runtime.ts +5 -7
  80. package/extensions/watch/tools.ts +7 -18
  81. package/extensions/web-access/README.md +3 -3
  82. package/extensions/web-access/search/search.ts +9 -18
  83. package/extensions/wechat/timer.ts +9 -30
  84. package/extensions/wechat/tools.ts +4 -7
  85. package/extensions/worktree/status.ts +14 -9
  86. package/lib/README.md +12 -8
  87. package/lib/escape.ts +15 -2
  88. package/lib/git/read.ts +20 -0
  89. package/lib/http/headers.ts +20 -0
  90. package/lib/http/server.ts +22 -4
  91. package/lib/pdf.ts +2 -2
  92. package/lib/pool.ts +62 -0
  93. package/lib/requirements.ts +2 -2
  94. package/lib/screen-session.ts +85 -0
  95. package/lib/timer.ts +61 -0
  96. package/lib/tui/README.md +3 -3
  97. package/lib/tui/columns.ts +33 -8
  98. package/lib/tui/dock-screen.ts +12 -4
  99. package/lib/tui/rows.ts +15 -6
  100. package/lib/tui/rules.ts +4 -8
  101. package/lib/tui/tool.ts +55 -2
  102. package/package.json +8 -4
  103. package/skills/changelog/SKILL.md +136 -0
  104. package/extensions/document/index.ts +0 -17
  105. /package/extensions/{document → file-read}/deadline.ts +0 -0
  106. /package/extensions/{document → file-read}/extract/vendor.d.ts +0 -0
package/README.md CHANGED
@@ -43,8 +43,8 @@ want the agent to *think first and work safely*:
43
43
  - **Remember across sessions.** Durable notes live in plain markdown that you
44
44
  can read, edit, and version.
45
45
  - **Reach the web and your files.** Search, fetch, browse, open, and read
46
- documents from inside the session, and answer a WeChat chat through a bridge to
47
- the live session.
46
+ documents and spreadsheets from inside the session, and answer a WeChat chat
47
+ through a bridge to the live session.
48
48
 
49
49
  Everything is optional and independently toggleable. Nothing is forked, and no
50
50
  installed Pi file is modified: Pi discovers the resources through the `pi`
@@ -73,10 +73,10 @@ Then, from inside Pi:
73
73
  - `/bytefly [extension] [refresh]` opens the control center on Extensions, lists
74
74
  every loaded extension with its runtime health, enables or disables one, and
75
75
  clears a project override with `x`.
76
- - `/bytefly settings` opens the control center's Settings section: ten groups
77
- covering startup, notifications, diagnostics, pricing, transcript, terminal,
78
- worktree, snapshots, jobs, and memory, plus a **Theme** row, with
79
- `Space`/`Enter` to change a value and `Tab` for Global/Project scope.
76
+ - `/bytefly settings` opens the control center's Settings section: one flat list
77
+ of the fourteen preferences a session changes, each with its value beside the
78
+ label, `Space`/`Enter` to change one and `Tab` for Global/Project scope. Every
79
+ other key is a `bytefly.json` edit, listed in the configuration reference.
80
80
  - `/bytefly doctor [extension] [refresh]` prints the doctor report —
81
81
  `run_doctor`'s text — and checks that each enabled extension has what it needs
82
82
  at runtime.
@@ -94,9 +94,8 @@ Nothing about the session is sent, and `PI_OFFLINE` skips the check;
94
94
 
95
95
  ## What you get
96
96
 
97
- Seventeen extensions and two packaged skills, grouped by what you are trying to
98
- do, plus two themes. Each extension's own `README.md` has the full tool and
99
- command surface.
97
+ 18 extensions, 3 packaged skills, and 2 themes, grouped by what you are trying
98
+ to do. Each extension's own `README.md` has the full tool and command surface.
100
99
 
101
100
  ### Plan and delegate
102
101
 
@@ -110,6 +109,7 @@ command surface.
110
109
  | [`question`](./extensions/question/) | `ask_user_question` | Ask you one or more structured questions (labelled options plus free-form "Other") and wait. |
111
110
  | [`job`](./extensions/job/) | `job`, `/jobs` | Run long-lived shell commands in the background; inspect logs, wait, or kill them. |
112
111
  | [`watch`](./extensions/watch/) | `watch`, `/watch` | Tail a file or command output, match lines against patterns, and alert you or hand matching lines to the agent. |
112
+ | [`context`](./extensions/context/) | `/context` | Show where the context window goes — system prompt, tool definitions, conversation by role, injected notes — and compact from the report with the before and after numbers. |
113
113
 
114
114
  ### Keep your work safe
115
115
 
@@ -124,8 +124,8 @@ command surface.
124
124
  | Extension | Tools and commands | What it does |
125
125
  |---|---|---|
126
126
  | [`web-access`](./extensions/web-access/) | `web_search`, `web_fetch` | Search the web (keyless DuckDuckGo by default, optional SearXNG or Brave) and fetch a page as readable text, with paging, `find`, per-hop SSRF checks, and optional PDF support. |
127
- | [`file-browse`](./extensions/file-browse/) | `/browse` | Start a loopback, read-only file browser rooted at the working directory, with Markdown preview and a fuzzy quick-open palette. |
128
- | [`document`](./extensions/document/) | `read_doc` | Extract text from a local PDF, DOCX, DOC, ODT, RTF, or XLSX in process, paged. |
127
+ | [`file-browse`](./extensions/file-browse/) | `/browse` | Start a loopback, read-only file browser rooted at the working directory, with Markdown preview, a fuzzy quick-open palette, and live status for a page left open. |
128
+ | [`file-read`](./extensions/file-read/) | `read_doc` | Extract text from a local PDF, DOCX, DOC, ODT, RTF, or XLSX in process, paged. |
129
129
  | [`file-open`](./extensions/file-open/) | `/open`, `open_path` | Open or reveal a file or directory in the OS-native application. |
130
130
  | [`wechat`](./extensions/wechat/) | `/wechat`, `send_wechat` | Bridge a WeChat chat to the live session over the Weixin iLink bot API; inbound text, images, and files become turns. |
131
131
 
@@ -145,6 +145,7 @@ advertised to the model, and its instructions load only when the task matches
145
145
  |---|---|
146
146
  | [`init`](./skills/init/SKILL.md) | Creates or updates a project's `AGENTS.md` through a built-in interactive interview and plan review, from repository evidence. |
147
147
  | [`refactor`](./skills/refactor/SKILL.md) | Audits a project or path for refactoring opportunities: duplication, oversized modules, coupling, drift, and high-churn code with weak tests. Returns a ranked shortlist with evidence and verification commands; discovery is read-only. |
148
+ | [`changelog`](./skills/changelog/SKILL.md) | Drafts the candidate bullet before implementation, records one user-visible bullet in the same commit, audits commits since the last tag for missing or weak entries, and curates a release section from the project's own rules. |
148
149
 
149
150
  ### Themes
150
151
 
package/README.zh-CN.md CHANGED
@@ -37,7 +37,7 @@ Pi 提供一个精简而锐利的内核。这个包是在其之上的一层,
37
37
  - **绝不搞乱检出区。** 隔离只需一条命令:任务切到自己分支的 `git worktree` 中,
38
38
  每次提示还会生成快照,因此一次糟糕的回合只需一个 `/rewind` 就能回退。
39
39
  - **跨会话记忆。** 持久笔记以纯 Markdown 保存,可读、可改、可纳入版本控制。
40
- - **连通网络与文件。** 在会话内搜索、抓取、浏览、打开和读取文档,还可通过桥接
40
+ - **连通网络与文件。** 在会话内搜索、抓取、浏览、打开和读取文档与表格,还可通过桥接
41
41
  在微信里直接回答当前会话。
42
42
 
43
43
  一切都是可选的,且可独立开关。没有任何东西被分叉,也没有修改已安装的 Pi 文件:
@@ -64,9 +64,9 @@ Windows、macOS 和 Linux 均可运行,完整检查套件在每个平台上都
64
64
 
65
65
  - `/bytefly [extension] [refresh]` 打开控制中心的 Extensions 区,列出每个已加载扩展
66
66
  及其运行时健康状况,启用或禁用一个扩展,并用 `x` 清除项目级覆盖。
67
- - `/bytefly settings` 打开控制中心的 Settings 区:十个分组覆盖启动、通知、
68
- 诊断、计价、转录、终端、worktree、快照、任务与监听器、记忆,并提供 **Theme**
69
- 行;用 `Space`/`Enter` 修改,用 `Tab` 切换 Global/Project 作用域。
67
+ - `/bytefly settings` 打开控制中心的 Settings 区:一次会话会改动的十四项设置排成
68
+ 一列,值紧跟在名称之后;用 `Space`/`Enter` 修改,用 `Tab` 切换 Global/Project
69
+ 作用域。其余键都是 `bytefly.json` 编辑,见配置参考。
70
70
  - `/bytefly doctor [extension] [refresh]` 打印 doctor 报告(`run_doctor` 的文本),
71
71
  检查每个已启用扩展在运行时是否具备所需条件。
72
72
  - `/bytefly update` 立即重跑包更新检查,报告可更新的包、本包版本与 release URL,
@@ -81,7 +81,7 @@ Windows、macOS 和 Linux 均可运行,完整检查套件在每个平台上都
81
81
 
82
82
  ## 包含内容
83
83
 
84
- 十七个扩展和两个随包技能,按你想达成的目标分组,外加两个主题。每个扩展自己的 `README.md`
84
+ 18 个扩展、3 个随包技能,以及 2 个主题,按你想达成的目标分组。每个扩展自己的 `README.md`
85
85
  提供完整的工具与命令说明。
86
86
 
87
87
  ### 规划与委派
@@ -96,6 +96,7 @@ Windows、macOS 和 Linux 均可运行,完整检查套件在每个平台上都
96
96
  | [`question`](./extensions/question/) | `ask_user_question` | 向你提出一个或多个结构化问题(带标签的选项外加自由填写的“其他”)并等待回答。 |
97
97
  | [`job`](./extensions/job/) | `job`, `/jobs` | 在后台运行长时 shell 命令;查看日志、等待或结束它们。 |
98
98
  | [`watch`](./extensions/watch/) | `watch`, `/watch` | 跟踪文件或命令输出,按模式匹配行,并提醒你或把匹配的行交给智能体。 |
99
+ | [`context`](./extensions/context/) | `/context` | 显示上下文窗口的去向 —— 系统提示、工具定义、按角色拆分的对话、注入的笔记 —— 并从报告中触发压缩,给出压缩前后的数字。 |
99
100
 
100
101
  ### 保护你的工作
101
102
 
@@ -110,8 +111,8 @@ Windows、macOS 和 Linux 均可运行,完整检查套件在每个平台上都
110
111
  | 扩展 | 工具与命令 | 说明 |
111
112
  |---|---|---|
112
113
  | [`web-access`](./extensions/web-access/) | `web_search`, `web_fetch` | 搜索网络(默认无密钥的 DuckDuckGo,可选 SearXNG 或 Brave),并把页面抓取为可读文本,支持分页、`find`、逐跳 SSRF 检查以及可选的 PDF 支持。 |
113
- | [`file-browse`](./extensions/file-browse/) | `/browse` | 启动一个以工作目录为根的本地回环、只读文件浏览器,带 Markdown 预览和模糊快速打开面板。 |
114
- | [`document`](./extensions/document/) | `read_doc` | 在进程内从本地 PDF、DOCX、DOC、ODT、RTF 或 XLSX 提取文本,支持分页。 |
114
+ | [`file-browse`](./extensions/file-browse/) | `/browse` | 启动一个以工作目录为根的本地回环、只读文件浏览器,带 Markdown 预览、模糊快速打开面板,以及为常开页面提供的实时状态。 |
115
+ | [`file-read`](./extensions/file-read/) | `read_doc` | 在进程内从本地 PDF、DOCX、DOC、ODT、RTF 或 XLSX 提取文本,支持分页。 |
115
116
  | [`file-open`](./extensions/file-open/) | `/open`, `open_path` | 用操作系统原生应用打开或定位文件/目录。 |
116
117
  | [`wechat`](./extensions/wechat/) | `/wechat`, `send_wechat` | 通过微信 iLink 机器人 API 把微信聊天桥接到实时会话;入站文本、图片和文件会变成回合。 |
117
118
 
@@ -129,6 +130,7 @@ Windows、macOS 和 Linux 均可运行,完整检查套件在每个平台上都
129
130
  |---|---|
130
131
  | [`init`](./skills/init/SKILL.md) | 基于仓库证据,通过内置的交互式访谈和计划审阅创建或更新项目的 `AGENTS.md`。 |
131
132
  | [`refactor`](./skills/refactor/SKILL.md) | 审计项目或路径中的重构机会:重复代码、过大的模块、耦合、结构漂移,以及测试薄弱且改动频繁的代码。返回带证据和验证命令的排序清单;发现阶段只读。 |
133
+ | [`changelog`](./skills/changelog/SKILL.md) | 实现前先起草候选条目,实施后在同一次提交中记录一条用户可见的变更,审计上一个 tag 以来缺失或薄弱的条目,并按项目自己的规则整理发布段落。 |
132
134
 
133
135
  ### 主题
134
136
 
@@ -217,7 +217,10 @@ job · 2 background jobs finished
217
217
  ```
218
218
 
219
219
  The rail is painted by the notice's worst severity, so a failure reads red at the
220
- edge like an errored tool result.
220
+ edge like an errored tool result, and the headline's title takes the notice's own
221
+ severity — a warning reads yellow where its sentence is, while an `info` or `ok`
222
+ notice stays body text. A row's label stays body text too: its severity is the
223
+ glyph it hangs on, so one failing row never tints the rest of the list.
221
224
 
222
225
  `job` and `watch` post one when a background job finishes or a watched line
223
226
  matches, so the report the model reads is visible too. `doctor` posts one when a
@@ -336,7 +339,7 @@ failed read is silent and not cached, so the next session tries again; see
336
339
  | Command | What it does |
337
340
  |---|---|
338
341
  | `/bytefly [extension] [refresh]` | Open the control center on Extensions. In `tui` it mounts a docked list; every other mode prints the grouped listing, or one extension's full findings when named. |
339
- | `/bytefly settings` | Open the control center on Settings. Outside `tui` it prints the grouped settings listing with each row's value. |
342
+ | `/bytefly settings` | Open the control center on Settings. Outside `tui` it prints one `Label: Value` line per setting. |
340
343
  | `/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. |
341
344
  | `/bytefly update` | Re-run the package update check now, ignoring the cache window, and report what it found — the updateable packages, this package's version and release URL when known, `PI_OFFLINE`, or an unreadable list. It resolves no packages and probes no extension. |
342
345
 
@@ -355,7 +358,7 @@ print the grouped listing with a per-row health suffix and the headline, or that
355
358
  extension's full findings when one is named. In `tui` the screen opens on the
356
359
  session's cached report and probes on the first Extensions view; the text modes
357
360
  probe before they print. `/bytefly settings` prints the
358
- grouped settings listing with each row's value outside `tui`; it does not
361
+ flat settings listing with each row's value outside `tui`; it does not
359
362
  resolve packages or probe the doctor. The
360
363
  proactive session-start check posts a transcript notice through the shared
361
364
  `my-pi-agent/notice` renderer rather than a transient notification, and is
@@ -499,7 +502,7 @@ it with its own rates for the models they match:
499
502
  For example, DeepSeek flash at `¥2` in and `¥8` out per million, with the
500
503
  published peak windows and `offPeakMultiplier: 0.5`, prices a 1M-in + 1M-out
501
504
  turn at `¥10.00` at peak and `¥5.00` off-peak. That example is worked end to end
502
- in [`footer/price/price.test.ts`](./footer/price/price.test.ts), which also prices the built-in
505
+ in [`footer/footer.test.ts`](./footer/footer.test.ts), which also prices the built-in
503
506
  table with no config file at all.
504
507
 
505
508
  ## Content and colors
@@ -642,92 +645,59 @@ installs or fetches a package.
642
645
  without leaving the session.
643
646
 
644
647
  **Settings.** The second section uses the same frame and the same `Tab` scope
645
- switch, with its own cursor. It lists twenty-nine preferences in ten groups:
648
+ switch, with its own cursor. It is the shortlist a session changes: fourteen
649
+ preferences on one flat line each, the value beside its label, and no group
650
+ headings. The whole list fits a 24-row terminal, and the frame stops growing
651
+ there however tall the terminal is.
646
652
 
647
653
  ```
648
654
  bytefly ──────────────────────────────────────────────────────────────
649
- Extensions · Settings Global · 29 settings
650
-
651
- Startup (1)
652
- ❯ ◻ Bytefly startup TUI Off
653
- Notifications (7)
654
- ◻ Desktop notifications Off
655
- ◼ Turn finished On
656
- ◼ Needs input On
657
- ◼ Job finished On
658
- ◼ Watcher matched On
659
- Delivery method Auto
660
- Notification title Pi
661
- Diagnostics (2)
662
- ◼ Proactive health check On
663
- Probe timeout 2000
664
- Pricing (3)
665
- Currency CNY (¥)
666
- Cost decimals 2
667
- Session budget 0
668
- Transcript (3)
669
- Rail glyph ╎
670
- Achieved goal Hide
671
- Output padding 1
672
- Terminal (2)
673
- Theme system
674
- ◻ Hardware cursor Off
675
- Worktree (4)
676
- ◼ Fetch remote On
677
- Base ref Fresh
678
- Branch prefix worktree-
679
- Prune after (days) 7
680
- Snapshots (3)
681
- ◼ Automatic snapshots On
682
- Snapshots kept 20
683
- ◼ Safety snapshot On
684
- Jobs and watchers (3)
685
- ◻ Wake when a job finishes Off
686
- ◻ Keep jobs after exit Off
687
- ◻ Wake on a match Off
688
- Memory (1)
689
- ◼ Inject notes On
655
+ Extensions · Settings Global · 14 settings
656
+
657
+ ❯ ◻ Bytefly startup TUI Off
658
+ ◼ Theme system
659
+ ◻ Desktop notifications Off
660
+ ◼ Turn finished On
661
+ ◼ Needs input On
662
+ ◼ Job finished On
663
+ ◼ Watcher matched On
664
+ Delivery method Auto
665
+ ◻ Wake when a job finishes Off
666
+ ◻ Wake on a match Off
667
+ ◼ Automatic snapshots On
668
+ ◼ Proactive health check On
669
+ Currency CNY (¥)
670
+ Session budget 0
671
+
690
672
  Bytefly startup TUI — Pi setting
691
673
  Use bytefly's startup header in place of Pi's startup TUI.
692
674
  Default · ~/.pi/agent/settings.json
693
675
 
694
- Esc close · ←/→ · Space change · Tab scope · u reset · J JSON · [ ] group
676
+ Esc close · ←/→ · Space change · Tab scope · u reset · J JSON · ↑/↓ select
695
677
  ```
696
678
 
697
- | Group | Row | Source | Values |
698
- |---|---|---|---|
699
- | Startup | Bytefly startup TUI | Pi `settings.json` (`quietStartup`) | Off, On |
700
- | Notifications | Desktop notifications | `notify.enabled` | Off, On |
701
- | Notifications | Turn finished | `notify.turnFinished` | Off, On |
702
- | Notifications | Needs input | `notify.needsInput` | Off, On |
703
- | Notifications | Job finished | `notify.jobFinished` | Off, On |
704
- | Notifications | Watcher matched | `notify.matchFound` | Off, On |
705
- | Notifications | Delivery method | `notify.method` | Auto, OSC 777, OSC 99, Windows toast, macOS, Linux |
706
- | Notifications | Notification title | `notify.title` | Text, up to 40 characters |
707
- | Diagnostics | Proactive health check | `doctor.proactive` | Off, On |
708
- | Diagnostics | Probe timeout | `doctor.timeoutMs` | Number, 100–30000 ms |
709
- | Pricing | Currency | `price.currency.code` | CNY, USD |
710
- | Pricing | Cost decimals | `price.currency.decimals` | 0, 1, 2, 3, 4 |
711
- | Pricing | Session budget | `price.budget` | Number, `0` = off |
712
- | Transcript | Rail glyph | `chrome.rail` | Text, exactly one column |
713
- | Transcript | Achieved goal | `goal.achieved` | Show, Hide |
714
- | Transcript | Output padding | Pi `settings.json` | 0, 1 |
715
- | Terminal | Theme | Pi `settings.json` (`theme`) | `system`, `bytefly-dark`, `bytefly-light` |
716
- | Terminal | Hardware cursor | Pi `settings.json` | Off, On |
717
- | Worktree | Base ref | `worktree.baseRef` | Fresh, Head |
718
- | Worktree | Branch prefix | `worktree.branchPrefix` | Text, trimmed, required |
719
- | Worktree | Fetch remote | `worktree.fetchRemote` | Off, On |
720
- | Worktree | Prune after (days) | `worktree.pruneAfterDays` | Number, 0–3650 |
721
- | Snapshots | Automatic snapshots | `rewind.autoSnapshots` | Off, On |
722
- | Snapshots | Snapshots kept | `rewind.max` | Number, 0–1000 |
723
- | Snapshots | Safety snapshot | `rewind.safetySnapshot` | Off, On |
724
- | Jobs and watchers | Wake when a job finishes | `job.wakeOnFinish` | Off, On |
725
- | Jobs and watchers | Keep jobs after exit | `job.detachedByDefault` | Off, On |
726
- | Jobs and watchers | Wake on a match | `watch.wakeOnMatch` | Off, On |
727
- | Memory | Inject notes | `memory.inject` | Off, On |
728
-
729
- Choosing **Currency** also writes that currency's default precision. Both rows
730
- stay editable afterwards, so the footer follows the explicit values.
679
+ <img src="../../assets/bytefly-settings.png" alt="The /bytefly Settings section: one flat list of fourteen preferences with each value beside its label, the `Global · 14 settings` counts, and a three-row detail pane naming the focused row's source, description, and file" width="100%">
680
+
681
+ | Row | Source | Values |
682
+ |---|---|---|
683
+ | Bytefly startup TUI | Pi `settings.json` (`quietStartup`) | Off, On |
684
+ | Theme | Pi `settings.json` (`theme`) | `system`, `bytefly-dark`, `bytefly-light` |
685
+ | Desktop notifications | `notify.enabled` | Off, On |
686
+ | Turn finished | `notify.turnFinished` | Off, On |
687
+ | Needs input | `notify.needsInput` | Off, On |
688
+ | Job finished | `notify.jobFinished` | Off, On |
689
+ | Watcher matched | `notify.matchFound` | Off, On |
690
+ | Delivery method | `notify.method` | Auto, OSC 777, OSC 99, Windows toast, macOS, Linux |
691
+ | Wake when a job finishes | `job.wakeOnFinish` | Off, On |
692
+ | Wake on a match | `watch.wakeOnMatch` | Off, On |
693
+ | Automatic snapshots | `rewind.autoSnapshots` | Off, On |
694
+ | Proactive health check | `doctor.proactive` | Off, On |
695
+ | Currency | `price.currency.code` | CNY, USD |
696
+ | Session budget | `price.budget` | Number, `0` = off |
697
+
698
+ Choosing **Currency** also writes that currency's default precision
699
+ (`price.currency.decimals`). Both stay editable in `bytefly.json` afterwards, so
700
+ the footer follows the explicit values.
731
701
 
732
702
  The **Theme** row lists `system` first, then the palettes this package ships,
733
703
  `bytefly-dark` and `bytefly-light`; it cycles no further, so a bytefly screen
@@ -739,24 +709,34 @@ and is replaced by the next choice here, so manage those in Pi's `/settings` →
739
709
  **Theme**.
740
710
 
741
711
  `Space` (or `Enter`) changes the focused row: a boolean toggles, an enum cycles
742
- forward, and a text row opens an inline editor on its value — the notification
743
- title, the probe timeout, the branch prefix, and the rail glyph. `u` deletes the
744
- focused row's key so its default applies again (`Already at its default.` when
745
- nothing is stored in that scope; an inherited Project value says so instead),
746
- `J` swaps the detail pane for the focused row's whole section as the active
747
- scope stores it — the only view of the keys this screen leaves to JSON — and
748
- `[` / `]` jump to the previous or next group. The detail pane names
749
- the source, describes the preference, and shows
750
- where the value comes from — `Global · ~/.pi/agent/bytefly.json`, `Project ·
712
+ forward, and the one text row — **Session budget** — opens an inline editor on its
713
+ amount. `u` deletes the focused row's key so its default applies again (`Already
714
+ at its default.` when nothing is stored in that scope; an inherited Project value
715
+ says so instead), and `J` swaps the detail pane for the focused row's whole
716
+ section as the active scope stores it — the only view of the keys this screen
717
+ leaves to JSON. The detail pane names the source, describes the preference, and
718
+ shows where the value comes from — `Global · ~/.pi/agent/bytefly.json`, `Project ·
751
719
  .pi/bytefly.json`, `Inherited from Global · ~/.pi/agent/bytefly.json`, or
752
720
  `Default · <the active scope's file>`; an app row names the personal
753
721
  `settings.json`. A successful change marks the screen `Saved · reload to apply`
754
722
  — even when the terminal is too short for the detail body — and closing offers
755
723
  the same reload confirm as an extension toggle.
756
724
 
757
- Six of the ten `bytefly.json` sections belong to other extensions. The screen
758
- cannot import their normalizers, so it reports the leaf as the file states it and
759
- declares the loader's default; `test/settings-coverage.test.ts` holds the
725
+ The list is capped at fourteen rows so the screen keeps fitting a 24-row
726
+ terminal: adding a setting means removing one (`panel/panel.test.ts` holds the
727
+ ceiling). Everything else stays in `bytefly.json` — the tuning numbers, the
728
+ cosmetic labels, and the Pi preferences Pi's own `/settings` owns — and
729
+ `test/settings-coverage.test.ts` keeps the split honest: every loader key is
730
+ either a row or a declared JSON-only edit with the reason it left. Four
731
+ `bytefly.json` sections therefore have no row at all (`chrome`, `goal`,
732
+ `worktree`, `memory`), as do `notify.title`, `doctor.timeoutMs`,
733
+ `price.currency.decimals`, `rewind.max`, `rewind.safetySnapshot`, and
734
+ `job.detachedByDefault` inside sections that do. Each is documented in
735
+ [`docs/configuration.md`](../../docs/configuration.md).
736
+
737
+ Three of the six sections belong to other extensions (`rewind`, `job`, and
738
+ `watch`). The screen cannot import their normalizers, so it reports the leaf as
739
+ the file states it and declares the loader's default; the same test holds the
760
740
  catalogue to every loader's defaults and bounds in both directions.
761
741
 
762
742
  **Bytefly startup TUI** is the one row whose label differs from the Pi key it
@@ -767,31 +747,28 @@ with `true` or `false`.
767
747
 
768
748
  App rows are **Global only**: Pi's setters for them write the personal
769
749
  `settings.json`, and only resource paths have public project setters. Project
770
- scope therefore lists the twenty-five `bytefly.json` rows and a
750
+ scope therefore lists the twelve `bytefly.json` rows and a
771
751
  `App settings are Global only.` note rather than promising a write that cannot
772
752
  happen. A `bytefly.json` row writes one leaf in the selected scope (a depth-1
773
- key, or a nested one such as `price.currency.decimals`) through a per-key
774
- writer, so every sibling key, section, `$schema`, and `version` is preserved. A
775
- malformed document is refused with a notice instead of overwritten, and `u`
776
- removes that one leaf through the same writer's `unsetConfigValueAt`.
753
+ key, or a nested one such as `price.currency.code`) through a per-key writer, so
754
+ every sibling key, section, `$schema`, and `version` is preserved. A malformed
755
+ document is refused with a notice instead of overwritten, and `u` removes that
756
+ one leaf through the same writer's `unsetConfigValueAt`.
777
757
 
778
- The non-`tui` form prints the headline, the groups, and `Label: Value` rows:
758
+ The non-`tui` form prints the headline and one `Label: Value` row per line, in
759
+ screen order:
779
760
 
780
761
  ```
781
- bytefly: Settings · Global · 29 settings
782
-
783
- Startup (1)
762
+ bytefly: Settings · Global · 14 settings
784
763
  Bytefly startup TUI: Off
785
-
786
- Notifications (7)
764
+ Theme: system
787
765
  Desktop notifications: Off
788
766
  Turn finished: On
789
767
  Needs input: On
790
768
  Job finished: On
791
769
  Watcher matched: On
792
770
  Delivery method: Auto
793
- Notification title: Pi
794
- ...
771
+ ...
795
772
  ```
796
773
 
797
774
  The doctor side probes every declared extension and reports each check as `ok`,
@@ -1004,12 +981,17 @@ command.
1004
981
  - **Project overrides of app settings are not shown.** App rows read the
1005
982
  personal file; a `.pi/settings.json` override still wins at runtime, and
1006
983
  `pi config` is the place to manage it.
1007
- - **Arrays and tables stay JSON.** A row is one boolean, enum, string, or number.
1008
- `doctor.skip` (an array), the `rates` table, and `price.peak` (a nested object)
1009
- are still `bytefly.json` edits; every scalar preference has a row.
1010
- - **A custom rail glyph must be one column.** The editor refuses anything else,
984
+ - **The list is a shortlist, not the whole document.** A row is one boolean,
985
+ enum, string, or number; the tuning numbers, the labels, and the Pi
986
+ preferences `/settings` owns stay in their files, and the catalogue is capped
987
+ at fourteen rows so the screen fits a 24-row terminal. `doctor.skip` (an
988
+ array), the `rates` table, and `price.peak` (a nested object) could never be a
989
+ row; the rest are declared JSON-only with their reason in
990
+ `test/settings-coverage.test.ts`.
991
+ - **The rail glyph is typed in `bytefly.json`.** It must be exactly one column,
1011
992
  because a two-column glyph would push every railed body one cell right; the
1012
- former four-glyph menu is now the description's example list.
993
+ loader drops anything else, and the former four-glyph menu is the
994
+ configuration reference's example list.
1013
995
  - **`doctor`, `settings`, and `update` are reserved first arguments.**
1014
996
  `/bytefly doctor` always selects the report, so an extension literally named
1015
997
  `doctor` cannot be narrowed to by name; `settings` and `update` are reserved
@@ -1134,9 +1116,9 @@ rather than showing a wrong value.
1134
1116
  | Header | `ctx.ui.setHeader` with a custom component, replacing Pi's built-in header; installed only when `ctx.mode === "tui"` and `pi.getSettings().quietStartup === true` (the **Bytefly startup TUI** row). `false`, `"header"`, and unset call `setHeader(undefined)`, leaving Pi's built-in header in place. The nameplate insets by Pi's `outputPad` (read through `pi.getSettings()`), so it sits on the transcript margin, and the help hangs one body indent inside it. 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. The factory reads `tui.terminal.rows` on every render, so the height ladder in `header/config.ts` sheds help groups and then the leading blank row as the terminal shortens (unknown height keeps the full header). The chrome version gains `↑<latest>` from the `UpdateSource`, and `registerHeader` returns a `refresh()` handle that re-installs the last context's header when that value arrives. |
1135
1117
  | Footer | `ctx.ui.setFooter` with a custom component, replacing Pi's built-in footer; installed only when `ctx.mode === "tui"`. |
1136
1118
  | User message | `pi.registerMarkdownTransformer` puts a two-row `You` nameplate (the label over a full-width rule) above each user message; one padded level-2 heading carries the accent through `mdHeading` for both rows, and the shipped themes set `userMessageBg` to the terminal default, so the block has no fill. Pi renders the result as Markdown, so the surface adds no component or color of its own. |
1137
- | Notice | `registerMessageRenderer(NOTICE_TYPE)` draws every shared transcript notice at the margin from its `details`; the headline stays at the margin and its rows hang from the transcript rail, painted by the notice's worst severity, whose glyph comes from the shared `RailSource`. `job` and `watch` post one when their work settles, and `doctor` posts one at `session_start` for a missing hard requirement. The type and shape come from `lib/notice.ts`. A message without `details` falls back to Pi's default custom-message box. |
1119
+ | Notice | `registerMessageRenderer(NOTICE_TYPE)` draws every shared transcript notice at the margin from its `details`; the headline stays at the margin with its title painted by the notice's own severity, and its rows hang from the transcript rail, painted by the notice's worst severity, whose glyph comes from the shared `RailSource`. `job` and `watch` post one when their work settles, and `doctor` posts one at `session_start` for a missing hard requirement. The type and shape come from `lib/notice.ts`. A message without `details` falls back to Pi's default custom-message box. |
1138
1120
  | 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. |
1139
- | 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 grouped 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. |
1121
+ | 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. |
1140
1122
  | 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 }`. |
1141
1123
  | 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. |
1142
1124
  | 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. |
@@ -1344,7 +1326,7 @@ rather than showing a wrong value.
1344
1326
  a third layer.** An extension cannot import another extension's modules, so
1345
1327
  the only legal home for a shared prerequisite table is `lib/requirements.ts`;
1346
1328
  the report itself is a pure function of the probe results.
1347
- - **The install hint has one home.** `document` and `web-access` format their
1329
+ - **The install hint has one home.** `file-read` and `web-access` format their
1348
1330
  tool errors from the same `REQUIREMENTS` entries `doctor` prints, so a user
1349
1331
  reads the same command whether a tool failed or the report named it.
1350
1332
  - **Settings are a curated table, not a schema walker.** The rows cover every
@@ -1456,8 +1438,8 @@ columns, gauge), so the look follows the active theme.
1456
1438
  | `working/register.ts` | Starts the tick on `turn_start`, tracks top-level tools on `tool_execution_start`, and cancels on `agent_end`/`session_shutdown`. |
1457
1439
  | `panel/types.ts` | `PanelSection` (`extensions` | `settings`), `PanelRow`, and `PanelModel`, the merged row model. |
1458
1440
  | `panel/join.ts` | The item/report union with the unambiguous-name health rule and unique row keys, groups, and ordering. |
1459
- | `panel/format.ts` | Non-TUI text: the extension headline and grouped listing with a per-row health suffix, plus the section segments, the Settings counts/headline, and the grouped Settings listing. |
1460
- | `panel/settings.ts` | The curated Settings catalogue: descriptors, snapshot views and origins, value cycling, stored-value mapping, validation, and grouping, plus the per-section readers that resolve a row's leaf over the global and project documents. |
1441
+ | `panel/format.ts` | Non-TUI text: the extension headline and grouped listing with a per-row health suffix, plus the section segments, the Settings counts/headline, and the flat Settings listing. |
1442
+ | `panel/settings.ts` | The curated Settings catalogue: the fourteen descriptors in screen order, snapshot views and origins, value cycling, stored-value mapping, validation, plus the per-section readers that resolve a row's leaf over the global and project documents. |
1461
1443
  | `panel/settings-runtime.ts` | The Settings runtime: snapshot cache, scope guards, app setters, per-leaf `bytefly.json` writes, dirty state, and change listeners. |
1462
1444
  | `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. |
1463
1445
  | `panel/tui.ts` | `PanelComponent`, the two-section dock screen (Extensions list and Settings list) and its section composition; the editing key map is `tui/editing.ts` and the shared frame is `tui/frame.ts`. |
@@ -7,7 +7,9 @@
7
7
  * — so it goes through `sanitizeLine` before it reaches the terminal, and every
8
8
  * line is clipped to the width it is given. The rail is painted by the notice's
9
9
  * worst severity, so a failure reads red at the edge like an errored tool
10
- * result; the `source · title` headline stays at the margin.
10
+ * result, and the headline's title is painted by the notice's own severity, so a
11
+ * warning reads yellow where its sentence is; the source name stays at the
12
+ * margin and a row's label stays body text behind its glyph.
11
13
  */
12
14
 
13
15
  import type { Theme } from "@earendil-works/pi-coding-agent";
@@ -65,16 +67,16 @@ export function noticeLines(
65
67
  glyph = DEFAULT_RAIL_GLYPH,
66
68
  ): string[] {
67
69
  const w = Math.max(1, width);
70
+ const fallback = knownSeverity(details.severity, "info");
68
71
  const sep = paint(theme, NOTICE_CONFIG.detailRole, SEPARATORS.item);
69
72
  const source = paint(theme, NOTICE_CONFIG.sourceRole, sanitizeLine(details.source));
70
- const title = paint(theme, NOTICE_CONFIG.titleRole, sanitizeLine(details.title));
73
+ const title = paint(theme, NOTICE_CONFIG.titleRoles[fallback], sanitizeLine(details.title));
71
74
  const items = details.items ?? [];
72
75
  const shown = items.slice(0, NOTICE_CONFIG.maxItems);
73
- const fallback = knownSeverity(details.severity, "info");
74
76
  const gap = " ".repeat(GLYPH_GAP);
75
77
 
76
78
  const rows = shown.map((item) => {
77
- const label = paint(theme, NOTICE_CONFIG.titleRole, sanitizeLine(item.label));
79
+ const label = paint(theme, NOTICE_CONFIG.rowRole, sanitizeLine(item.label));
78
80
  const detail = item.detail ? `${sep}${paint(theme, NOTICE_CONFIG.detailRole, sanitizeLine(item.detail))}` : "";
79
81
  return `${severityGlyph(theme, severityOf(item, fallback))}${gap}${label}${detail}`;
80
82
  });
@@ -6,6 +6,10 @@
6
6
  * glyph. The glyphs come from the shared vocabulary, the roles from the shared
7
7
  * rail map, and the rail glyph from the `chrome` config, so a notice reads like
8
8
  * the tool results around it.
9
+ *
10
+ * Severity reaches two surfaces: the rail (the block's worst severity) and the
11
+ * headline's title (the notice's own). A row carries no colour of its own, so a
12
+ * notice reporting one success and one failure stays readable as a list.
9
13
  */
10
14
 
11
15
  import type { NoticeSeverity } from "../../../lib/notice.ts";
@@ -16,8 +20,18 @@ import { RAIL_ROLES } from "../chrome.ts";
16
20
  export const NOTICE_CONFIG = {
17
21
  /** Role for the source name at the head of the block. */
18
22
  sourceRole: "accent" as TuiRole,
19
- /** Role for the headline's title text and each row's label. */
20
- titleRole: "label" as TuiRole,
23
+ /**
24
+ * Role for the headline's title, keyed by the notice's own severity: an alert
25
+ * reads in colour where the sentence is, a routine notice stays body text.
26
+ */
27
+ titleRoles: {
28
+ info: "label",
29
+ ok: "label",
30
+ warn: "warn",
31
+ bad: "bad",
32
+ } as Record<NoticeSeverity, TuiRole>,
33
+ /** Role for each row's label; a row states its severity with its glyph. */
34
+ rowRole: "label" as TuiRole,
21
35
  /** Role for the `·` separators and each row's trailing detail. */
22
36
  detailRole: "faint" as TuiRole,
23
37
  /** Width-1 glyph per severity, from the shared vocabulary. */
@@ -18,7 +18,7 @@ import { sanitizeLine } from "../../../lib/format.ts";
18
18
  import { GLYPHS } from "../../../lib/tui/glyphs.ts";
19
19
  import type { TuiRole } from "../../../lib/tui/tokens.ts";
20
20
  import { reportSeverity, reportSummary, severityGlyphChar, severityRole } from "../doctor/format.ts";
21
- import { groupViews, type SettingView } from "./settings.ts";
21
+ import type { SettingView } from "./settings.ts";
22
22
  import type { PanelModel, PanelRow, PanelSection } from "./types.ts";
23
23
 
24
24
  /** The settings scope the screen shows. */
@@ -125,15 +125,10 @@ export function formatSettingsHeadline(count: number): string {
125
125
  return `bytefly: Settings · Global · ${count} settings`;
126
126
  }
127
127
 
128
- /** The grouped Settings listing for non-TUI modes. */
128
+ /** The Settings listing for non-TUI modes, one row per line, in screen order. */
129
129
  export function formatSettingsListing(views: readonly SettingView[]): string {
130
130
  const lines = [formatSettingsHeadline(views.length)];
131
- for (const group of groupViews(views)) {
132
- lines.push("", `${group.label} (${group.views.length})`);
133
- for (const view of group.views) {
134
- lines.push(` ${sanitizeLine(view.label)}: ${sanitizeLine(view.valueLabel)}`);
135
- }
136
- }
131
+ for (const view of views) lines.push(` ${sanitizeLine(view.label)}: ${sanitizeLine(view.valueLabel)}`);
137
132
  return lines.join("\n");
138
133
  }
139
134
 
@@ -20,7 +20,7 @@
20
20
 
21
21
  import { basename, dirname } from "node:path";
22
22
  import type { ExtensionReport } from "../../../lib/requirements.ts";
23
- import { buildEntries } from "../picker/resources.ts";
23
+ import { buildEntries, resourceKey } from "../picker/resources.ts";
24
24
  import type { ResourceItem } from "../picker/types.ts";
25
25
  import type { PanelGroup, PanelModel, PanelRow } from "./types.ts";
26
26
 
@@ -29,11 +29,6 @@ export function extensionName(path: string): string {
29
29
  return basename(dirname(path));
30
30
  }
31
31
 
32
- /** Unique identity of a resolved resource, independent of the directory name. */
33
- export function resourceKey(item: ResourceItem): string {
34
- return `${item.metadata.source}:${item.path}`;
35
- }
36
-
37
32
  /** Row key for a report that has no resolved extension item. */
38
33
  function reportKey(name: string): string {
39
34
  return `report:${name}`;