@gavin-hu/my-pi-agent 0.0.0-stage → 0.7.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.
- package/LICENSE +21 -0
- package/README.md +160 -2
- package/assets/bytefly-chrome.png +0 -0
- package/assets/bytefly.schema.json +769 -0
- package/assets/file-browse.png +0 -0
- package/assets/plan-review.png +0 -0
- package/extensions/bytefly/README.md +820 -0
- package/extensions/bytefly/doctor/commands.ts +68 -0
- package/extensions/bytefly/doctor/config.ts +47 -0
- package/extensions/bytefly/doctor/format.ts +188 -0
- package/extensions/bytefly/doctor/register.ts +54 -0
- package/extensions/bytefly/doctor/runtime.ts +87 -0
- package/extensions/bytefly/doctor/schema.ts +51 -0
- package/extensions/bytefly/doctor/tools.ts +113 -0
- package/extensions/bytefly/doctor/tui.ts +140 -0
- package/extensions/bytefly/footer/collect/entries.ts +137 -0
- package/extensions/bytefly/footer/collect/snapshot.ts +64 -0
- package/extensions/bytefly/footer/config.ts +35 -0
- package/extensions/bytefly/footer/price/config.ts +152 -0
- package/extensions/bytefly/footer/price/price.ts +59 -0
- package/extensions/bytefly/footer/price/rates.ts +54 -0
- package/extensions/bytefly/footer/price/schedule.ts +112 -0
- package/extensions/bytefly/footer/price/types.ts +53 -0
- package/extensions/bytefly/footer/register.ts +116 -0
- package/extensions/bytefly/footer/render/footer.ts +51 -0
- package/extensions/bytefly/footer/render/format.ts +106 -0
- package/extensions/bytefly/footer/render/layout.ts +139 -0
- package/extensions/bytefly/footer/render/lines.ts +50 -0
- package/extensions/bytefly/footer/render/segments.ts +281 -0
- package/extensions/bytefly/footer/render/status.ts +42 -0
- package/extensions/bytefly/footer/types.ts +104 -0
- package/extensions/bytefly/header/component.ts +112 -0
- package/extensions/bytefly/header/config.ts +77 -0
- package/extensions/bytefly/header/register.ts +53 -0
- package/extensions/bytefly/index.ts +48 -0
- package/extensions/bytefly/notify/config.ts +60 -0
- package/extensions/bytefly/notify/dispatch.ts +138 -0
- package/extensions/bytefly/notify/format.ts +39 -0
- package/extensions/bytefly/notify/register.ts +85 -0
- package/extensions/bytefly/notify/types.ts +28 -0
- package/extensions/bytefly/picker/commands.ts +73 -0
- package/extensions/bytefly/picker/discovery.ts +128 -0
- package/extensions/bytefly/picker/resources.ts +226 -0
- package/extensions/bytefly/picker/runtime.ts +108 -0
- package/extensions/bytefly/picker/toggle.ts +145 -0
- package/extensions/bytefly/picker/tui.ts +260 -0
- package/extensions/bytefly/picker/types.ts +55 -0
- package/extensions/bytefly/separator/component.ts +26 -0
- package/extensions/bytefly/separator/config.ts +24 -0
- package/extensions/bytefly/separator/format.ts +50 -0
- package/extensions/bytefly/separator/register.ts +42 -0
- package/extensions/bytefly/separator/types.ts +22 -0
- package/extensions/document/README.md +281 -0
- package/extensions/document/config.ts +58 -0
- package/extensions/document/extract/doc.ts +19 -0
- package/extensions/document/extract/docx.ts +18 -0
- package/extensions/document/extract/file.ts +45 -0
- package/extensions/document/extract/odt.ts +101 -0
- package/extensions/document/extract/pdf.ts +25 -0
- package/extensions/document/extract/rtf.ts +45 -0
- package/extensions/document/extract/vendor.d.ts +39 -0
- package/extensions/document/extract/xlsx.ts +88 -0
- package/extensions/document/formats.ts +59 -0
- package/extensions/document/index.ts +17 -0
- package/extensions/document/paging.ts +110 -0
- package/extensions/document/render.ts +105 -0
- package/extensions/document/schema.ts +50 -0
- package/extensions/document/tool.ts +152 -0
- package/extensions/file-browse/README.md +269 -0
- package/extensions/file-browse/client.ts +550 -0
- package/extensions/file-browse/config.ts +82 -0
- package/extensions/file-browse/files.ts +433 -0
- package/extensions/file-browse/fuzzy.ts +75 -0
- package/extensions/file-browse/git.ts +74 -0
- package/extensions/file-browse/html.ts +346 -0
- package/extensions/file-browse/icons.ts +119 -0
- package/extensions/file-browse/index.ts +149 -0
- package/extensions/file-browse/markdown.ts +592 -0
- package/extensions/file-browse/paths.ts +101 -0
- package/extensions/file-browse/router.ts +289 -0
- package/extensions/file-browse/server.ts +104 -0
- package/extensions/file-open/README.md +135 -0
- package/extensions/file-open/index.ts +93 -0
- package/extensions/file-open/open.ts +102 -0
- package/extensions/file-open/schema.ts +31 -0
- package/extensions/goal/README.md +241 -0
- package/extensions/goal/clarify-parse.ts +203 -0
- package/extensions/goal/clarify-prompt.ts +80 -0
- package/extensions/goal/clarify.ts +234 -0
- package/extensions/goal/commands.ts +220 -0
- package/extensions/goal/config.ts +35 -0
- package/extensions/goal/format.ts +111 -0
- package/extensions/goal/index.ts +77 -0
- package/extensions/goal/runtime.ts +90 -0
- package/extensions/goal/schema.ts +105 -0
- package/extensions/goal/state.ts +69 -0
- package/extensions/goal/tools.ts +136 -0
- package/extensions/goal/tui.ts +97 -0
- package/extensions/goal/types.ts +42 -0
- package/extensions/job/README.md +309 -0
- package/extensions/job/commands.ts +74 -0
- package/extensions/job/config.ts +68 -0
- package/extensions/job/format.ts +256 -0
- package/extensions/job/index.ts +140 -0
- package/extensions/job/logs.ts +56 -0
- package/extensions/job/paths.ts +31 -0
- package/extensions/job/process.ts +106 -0
- package/extensions/job/registry.ts +265 -0
- package/extensions/job/reservations.ts +82 -0
- package/extensions/job/runtime.ts +846 -0
- package/extensions/job/schema.ts +148 -0
- package/extensions/job/session.ts +116 -0
- package/extensions/job/status.ts +69 -0
- package/extensions/job/store.ts +103 -0
- package/extensions/job/tools.ts +295 -0
- package/extensions/job/tui.ts +486 -0
- package/extensions/job/types.ts +92 -0
- package/extensions/job/ui.ts +99 -0
- package/extensions/job/waiters.ts +46 -0
- package/extensions/memory/README.md +221 -0
- package/extensions/memory/commands.ts +190 -0
- package/extensions/memory/config.ts +35 -0
- package/extensions/memory/errors.ts +9 -0
- package/extensions/memory/format.ts +141 -0
- package/extensions/memory/index.ts +59 -0
- package/extensions/memory/runtime.ts +199 -0
- package/extensions/memory/schema.ts +86 -0
- package/extensions/memory/store.ts +231 -0
- package/extensions/memory/tools.ts +165 -0
- package/extensions/memory/tui.ts +430 -0
- package/extensions/memory/types.ts +38 -0
- package/extensions/plan/README.md +417 -0
- package/extensions/plan/blocks.ts +197 -0
- package/extensions/plan/commands.ts +134 -0
- package/extensions/plan/feedback.ts +157 -0
- package/extensions/plan/git-bash.ts +320 -0
- package/extensions/plan/index.ts +179 -0
- package/extensions/plan/path-guard.ts +101 -0
- package/extensions/plan/plans.ts +267 -0
- package/extensions/plan/policy.ts +73 -0
- package/extensions/plan/runtime.ts +195 -0
- package/extensions/plan/steps.ts +228 -0
- package/extensions/plan/tools.ts +605 -0
- package/extensions/plan/tui.ts +391 -0
- package/extensions/plan/types.ts +67 -0
- package/extensions/plan/web-client.ts +433 -0
- package/extensions/plan/web-page.ts +169 -0
- package/extensions/plan/web-router.ts +155 -0
- package/extensions/plan/web.ts +267 -0
- package/extensions/question/README.md +189 -0
- package/extensions/question/answers.ts +119 -0
- package/extensions/question/dialogs.ts +109 -0
- package/extensions/question/index.ts +28 -0
- package/extensions/question/schema.ts +119 -0
- package/extensions/question/tools.ts +122 -0
- package/extensions/question/tui-state.ts +240 -0
- package/extensions/question/tui.ts +324 -0
- package/extensions/question/types.ts +61 -0
- package/extensions/rewind/README.md +202 -0
- package/extensions/rewind/commands.ts +74 -0
- package/extensions/rewind/config.ts +71 -0
- package/extensions/rewind/format.ts +110 -0
- package/extensions/rewind/git.ts +177 -0
- package/extensions/rewind/index.ts +60 -0
- package/extensions/rewind/policy.ts +42 -0
- package/extensions/rewind/queue.ts +27 -0
- package/extensions/rewind/restore.ts +96 -0
- package/extensions/rewind/rewind-tui.ts +135 -0
- package/extensions/rewind/rewind.ts +146 -0
- package/extensions/rewind/runtime.ts +174 -0
- package/extensions/rewind/snapshot-run.ts +103 -0
- package/extensions/rewind/snapshot.ts +80 -0
- package/extensions/rewind/status.ts +67 -0
- package/extensions/rewind/store.ts +142 -0
- package/extensions/rewind/temp-index.ts +87 -0
- package/extensions/rewind/timeline.ts +119 -0
- package/extensions/rewind/types.ts +58 -0
- package/extensions/subagent/README.md +310 -0
- package/extensions/subagent/agents.ts +454 -0
- package/extensions/subagent/format.ts +126 -0
- package/extensions/subagent/index.ts +181 -0
- package/extensions/subagent/invocation.ts +57 -0
- package/extensions/subagent/orchestrate.ts +182 -0
- package/extensions/subagent/render.ts +491 -0
- package/extensions/subagent/run.ts +212 -0
- package/extensions/subagent/schema.ts +102 -0
- package/extensions/subagent/stream.ts +144 -0
- package/extensions/subagent/types.ts +79 -0
- package/extensions/todo/README.md +230 -0
- package/extensions/todo/commands.ts +41 -0
- package/extensions/todo/config.ts +29 -0
- package/extensions/todo/format.ts +142 -0
- package/extensions/todo/index.ts +100 -0
- package/extensions/todo/nudge.ts +64 -0
- package/extensions/todo/runtime.ts +149 -0
- package/extensions/todo/schema.ts +125 -0
- package/extensions/todo/state.ts +65 -0
- package/extensions/todo/tools.ts +136 -0
- package/extensions/todo/tui.ts +170 -0
- package/extensions/todo/types.ts +33 -0
- package/extensions/watch/README.md +214 -0
- package/extensions/watch/commands.ts +144 -0
- package/extensions/watch/config.ts +59 -0
- package/extensions/watch/format.ts +212 -0
- package/extensions/watch/index.ts +129 -0
- package/extensions/watch/process.ts +63 -0
- package/extensions/watch/runtime.ts +525 -0
- package/extensions/watch/schema.ts +177 -0
- package/extensions/watch/tools.ts +278 -0
- package/extensions/watch/tui.ts +427 -0
- package/extensions/watch/types.ts +83 -0
- package/extensions/watch/ui.ts +97 -0
- package/extensions/web-access/README.md +347 -0
- package/extensions/web-access/config.ts +36 -0
- package/extensions/web-access/fetch/cache.ts +78 -0
- package/extensions/web-access/fetch/config.ts +81 -0
- package/extensions/web-access/fetch/extract.ts +124 -0
- package/extensions/web-access/fetch/find.ts +158 -0
- package/extensions/web-access/fetch/format.ts +117 -0
- package/extensions/web-access/fetch/page.ts +169 -0
- package/extensions/web-access/fetch/pdf.ts +45 -0
- package/extensions/web-access/fetch/schema.ts +192 -0
- package/extensions/web-access/fetch/ssrf.ts +191 -0
- package/extensions/web-access/fetch/tool.ts +238 -0
- package/extensions/web-access/fetch/types.ts +26 -0
- package/extensions/web-access/html.ts +41 -0
- package/extensions/web-access/http.ts +133 -0
- package/extensions/web-access/index.ts +28 -0
- package/extensions/web-access/search/config.ts +115 -0
- package/extensions/web-access/search/format.ts +65 -0
- package/extensions/web-access/search/json.ts +75 -0
- package/extensions/web-access/search/provider.ts +38 -0
- package/extensions/web-access/search/providers/brave.ts +76 -0
- package/extensions/web-access/search/providers/duckduckgo.ts +109 -0
- package/extensions/web-access/search/providers/searxng.ts +98 -0
- package/extensions/web-access/search/registry.ts +49 -0
- package/extensions/web-access/search/schema.ts +65 -0
- package/extensions/web-access/search/search.ts +129 -0
- package/extensions/web-access/search/tool.ts +112 -0
- package/extensions/web-access/search/types.ts +32 -0
- package/extensions/web-access/text.ts +26 -0
- package/extensions/web-access/transcript.ts +20 -0
- package/extensions/wechat/README.md +273 -0
- package/extensions/wechat/bridge.ts +577 -0
- package/extensions/wechat/client.ts +411 -0
- package/extensions/wechat/commands.ts +220 -0
- package/extensions/wechat/config.ts +88 -0
- package/extensions/wechat/credentials.ts +89 -0
- package/extensions/wechat/format.ts +76 -0
- package/extensions/wechat/index.ts +175 -0
- package/extensions/wechat/interaction.ts +130 -0
- package/extensions/wechat/lock.ts +86 -0
- package/extensions/wechat/login-screen.ts +91 -0
- package/extensions/wechat/login.ts +130 -0
- package/extensions/wechat/media.ts +103 -0
- package/extensions/wechat/outbound.ts +102 -0
- package/extensions/wechat/prompt.ts +104 -0
- package/extensions/wechat/qrcode.ts +46 -0
- package/extensions/wechat/remote-ui.ts +97 -0
- package/extensions/wechat/slash.ts +114 -0
- package/extensions/wechat/state.ts +71 -0
- package/extensions/wechat/status-screen.ts +176 -0
- package/extensions/wechat/tools.ts +109 -0
- package/extensions/wechat/types.ts +170 -0
- package/extensions/worktree/README.md +278 -0
- package/extensions/worktree/commands.ts +110 -0
- package/extensions/worktree/config.ts +107 -0
- package/extensions/worktree/enter.ts +273 -0
- package/extensions/worktree/errors.ts +18 -0
- package/extensions/worktree/exit.ts +122 -0
- package/extensions/worktree/git.ts +429 -0
- package/extensions/worktree/guard.ts +566 -0
- package/extensions/worktree/include.ts +147 -0
- package/extensions/worktree/index.ts +221 -0
- package/extensions/worktree/lifecycle.ts +14 -0
- package/extensions/worktree/prune.ts +99 -0
- package/extensions/worktree/registry.ts +221 -0
- package/extensions/worktree/root-tools.ts +162 -0
- package/extensions/worktree/runtime.ts +135 -0
- package/extensions/worktree/state.ts +65 -0
- package/extensions/worktree/status.ts +115 -0
- package/extensions/worktree/tools.ts +147 -0
- package/lib/README.md +49 -0
- package/lib/browser.ts +40 -0
- package/lib/config.ts +186 -0
- package/lib/env.ts +41 -0
- package/lib/escape.ts +27 -0
- package/lib/events.ts +40 -0
- package/lib/format.ts +76 -0
- package/lib/git/index.ts +7 -0
- package/lib/git/read.ts +76 -0
- package/lib/git/runner.ts +80 -0
- package/lib/highlight.ts +1000 -0
- package/lib/interaction.ts +132 -0
- package/lib/list-cursor.ts +222 -0
- package/lib/path.ts +49 -0
- package/lib/pi-invocation.ts +71 -0
- package/lib/policy.ts +67 -0
- package/lib/rails.ts +91 -0
- package/lib/requirements.ts +431 -0
- package/lib/shell.ts +33 -0
- package/lib/tool-names.ts +32 -0
- package/lib/tui.ts +184 -0
- package/lib/ui.ts +105 -0
- package/lib/web/base.ts +18 -0
- package/lib/web/components.ts +122 -0
- package/lib/web/index.ts +12 -0
- package/lib/web/layout.ts +30 -0
- package/lib/web/markup.ts +99 -0
- package/lib/web/runtime.ts +68 -0
- package/lib/web/tokens.ts +128 -0
- package/package.json +134 -4
- package/themes/bytefly-dark.json +91 -0
- package/themes/bytefly-light.json +91 -0
|
@@ -0,0 +1,820 @@
|
|
|
1
|
+
# bytefly — the TUI header, footer, turn rule, extensions picker, doctor, and notifications
|
|
2
|
+
|
|
3
|
+
`bytefly` owns the terminal chrome, configuration, and diagnostics surfaces
|
|
4
|
+
this package adds: one themed startup header that recolors Pi's brand mark and
|
|
5
|
+
version, one compact, colorful, width-adaptive two-line footer, a labeled rule
|
|
6
|
+
that marks each completed turn in the transcript, the `/extensions` picker that
|
|
7
|
+
lists and toggles every resolved Pi extension, the `/doctor` surface that
|
|
8
|
+
reports whether each extension has what it needs, and an opt-in notify
|
|
9
|
+
capability that raises an OS notification when a run finishes, a job finishes,
|
|
10
|
+
or the agent asks for input. DeepSeek is priced from the provider's published
|
|
11
|
+
yuan rates and peak tariff by default, and any other model can be priced from
|
|
12
|
+
your own rates when Pi's catalog does not match what a provider actually
|
|
13
|
+
charges.
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
pi --extension ./extensions/bytefly # load just this extension
|
|
17
|
+
pi -e . # load the whole @gavin-hu/my-pi-agent package
|
|
18
|
+
pi install ./ # install the package
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## What it does
|
|
22
|
+
|
|
23
|
+
The extension owns five surfaces: the startup header, the footer, the rule
|
|
24
|
+
between completed turns, the `/extensions` picker, and the `/doctor` report.
|
|
25
|
+
|
|
26
|
+
**Header.** A nameplate: a two-tone half-block mark from the active theme's
|
|
27
|
+
`accent` / `warning` tokens, the agent identity (`Pi` and its version), the
|
|
28
|
+
chrome identity (`enhanced by bytefly` and the package version), a `─` rule, and
|
|
29
|
+
a grouped help block. The help keys resolve live from Pi's keybindings, so a
|
|
30
|
+
rebound key shows the user's chord; the identity and help text are `muted`/`dim`
|
|
31
|
+
so the mark keeps the eye. It is installed only in interactive (`tui`) sessions,
|
|
32
|
+
and on terminals that cannot render half blocks the mark falls back to a
|
|
33
|
+
two-tone `Pi` wordmark.
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
█▀█ Pi v1.2.0
|
|
37
|
+
█▀ █ enhanced by bytefly v0.5.1
|
|
38
|
+
─────────────────────────────────────────────────
|
|
39
|
+
escape to interrupt · ctrl+c to clear · ctrl+d to exit
|
|
40
|
+
ctrl+o more · ctrl+t to think · ctrl+l models · ctrl+g editor
|
|
41
|
+
/ commands · ! bash · /bytefly chrome · /doctor checks
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
As the terminal narrows, the identity sheds the chrome version, then the chrome
|
|
45
|
+
line, then the agent name, and the help rows drop whole keys, so nothing wraps or
|
|
46
|
+
clips mid-word. The mark, tokens, identity, and grouped help are tuned in
|
|
47
|
+
[`header/config.ts`](./header/config.ts).
|
|
48
|
+
|
|
49
|
+
**Footer.** Two lines, each with a left and right zone:
|
|
50
|
+
|
|
51
|
+
- **Line 1 — identity:** `pwd` and the session name on the left; git state
|
|
52
|
+
(`⎇ branch · ⑂ worktree`) and the `◉` browse chip on the right.
|
|
53
|
+
- **Line 2 — resources:** the context gauge and usage meters on the left, the
|
|
54
|
+
mode/alert slot (`⋮ plan`) trailing after a `│` when present; the model and
|
|
55
|
+
thinking level on the right. The gauge is anchored at column 0, so `%` is
|
|
56
|
+
always in the same place.
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
~/repo/my-pi-agent ⎇ main · ⑂ smoke
|
|
60
|
+
▰▰▰▰▰▰▱▱▱▱ 62%/200k · ¥0.31 · ↑42k ↓8.0k · R96k CH 87% │ ⋮ plan opus-4.5 · high
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
(The examples show the default `¥` symbol. For a model the built-in table does
|
|
64
|
+
not cover, the number stays Pi's recorded USD amount — see the currency note
|
|
65
|
+
under [Configuration](#configuration).)
|
|
66
|
+
|
|
67
|
+
With no plan mode there is no mode slot, so the `│` is omitted:
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
~/repo/my-pi-agent ⎇ main · ⑂ smoke
|
|
71
|
+
▰▰▰▰▰▰▱▱▱▱ 62%/200k · ¥0.31 · ↑42k ↓8.0k · R96k CH 87% opus-4.5 · high
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
As the terminal narrows, segments shrink and drop by priority rather than
|
|
75
|
+
clipping. The branch keeps its name and is dropped when the name no longer
|
|
76
|
+
fits, rather than collapsing to a bare glyph:
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
~/repo/project ⎇ main · ⑂ smoke
|
|
80
|
+
▰▰▰▰▰▰▱▱▱▱ 62% · ¥0.31 │ ⋮ plan opus-4.5 · high
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
project ⎇ main
|
|
85
|
+
62% ⋮ opus-4.5
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
With a named session, the name follows the path and truncates before it drops:
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
~/repo/project · feature-refac… ⎇ main
|
|
92
|
+
▰▰▰▰▰▰▱▱▱▱ 62% │ ⋮ plan opus-4.5
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Zero-value meters are omitted, so a fresh session shows only `?/window` instead
|
|
96
|
+
of a row of `¥0.00 · ↑0 ↓0 · R0 W0`. Unknown context usage shows a muted `?`
|
|
97
|
+
with no gauge (known `0%` still draws the empty gauge), so unknown and zero stay
|
|
98
|
+
distinct. Cost keeps the configured precision — the segment drops rather than
|
|
99
|
+
rounding to a misleading `¥0.3` — and a positive amount below one minor unit
|
|
100
|
+
shows as `¥<0.01` rather than `¥0.00`.
|
|
101
|
+
|
|
102
|
+
DeepSeek needs no config file at all: the bar prices it from the published yuan
|
|
103
|
+
rates and peak tariff built into [`footer/price/config.ts`](./footer/price/config.ts). A
|
|
104
|
+
`bytefly.json` adds models of your own, or replaces a built-in entry:
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
~/repo/project ⎇ main
|
|
108
|
+
▰▰▰▰▰▰▱▱▱▱ 62%/200k · ¥2.50 · ↑42k ↓8.0k · R96k CH 87% opus-4.5 · high
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Turn separator.** After each completed turn the extension appends an inert
|
|
112
|
+
entry and draws a centered `╌╌╌ turn N ╌╌╌` rule across the transcript; see
|
|
113
|
+
[Turn separator](#turn-separator). No config file is needed and there is no
|
|
114
|
+
command.
|
|
115
|
+
|
|
116
|
+
**Extensions picker.** `/extensions` opens a keyboard-driven dock list of every
|
|
117
|
+
Pi extension the session resolves — across personal settings and, when trusted,
|
|
118
|
+
the project — and writes a `+pattern` / `-pattern` toggle into the matching
|
|
119
|
+
`settings.json`; see [Extensions picker](#extensions-picker). There is no
|
|
120
|
+
config file, and changes apply on reload.
|
|
121
|
+
|
|
122
|
+
**Doctor.** `/doctor` reports whether each extension has what it needs at
|
|
123
|
+
runtime — a binary on `PATH`, an optional package, a login/state file, or a
|
|
124
|
+
config file that fails to parse — and `run_doctor` returns the same report as
|
|
125
|
+
text and typed data. Only a missing *hard* requirement warns at session start;
|
|
126
|
+
everything else waits in the report. See [Doctor](#doctor).
|
|
127
|
+
|
|
128
|
+
**Notify.** Off by default, one `notify` section in `bytefly.json` turns on OS
|
|
129
|
+
notifications for unattended runs: when a full agent run settles, when a
|
|
130
|
+
background job finishes, and when the agent opens a prompt asking for input. The
|
|
131
|
+
method follows the terminal (OSC 777, OSC 99, a Windows toast) or falls back to
|
|
132
|
+
the platform notifier (`osascript`, `notify-send`); see
|
|
133
|
+
[Notifications](#notifications).
|
|
134
|
+
|
|
135
|
+
## Commands
|
|
136
|
+
|
|
137
|
+
| Command | What it does |
|
|
138
|
+
|---|---|
|
|
139
|
+
| `/bytefly` | Toggle the bytefly chrome — header and footer — on or off. |
|
|
140
|
+
| `/bytefly header` | Toggle the header alone. |
|
|
141
|
+
| `/bytefly footer` | Toggle the footer alone. |
|
|
142
|
+
| `/bytefly status` | Report whether the header and footer are on. |
|
|
143
|
+
| `/extensions` | Open the extensions picker in the TUI, or print the grouped list elsewhere. |
|
|
144
|
+
| `/doctor [extension] [refresh]` | Probe the extensions and show the report; in the TUI this opens a docked list screen. |
|
|
145
|
+
|
|
146
|
+
The `status` subcommand completes with Tab.
|
|
147
|
+
|
|
148
|
+
## Behaviour by mode
|
|
149
|
+
|
|
150
|
+
The header, footer, and turn rule are installed only in interactive (`tui`)
|
|
151
|
+
sessions; `/bytefly` is a no-op elsewhere. The picker is available in every
|
|
152
|
+
mode: in `tui` it opens the dock screen, while in RPC, JSON, and print modes
|
|
153
|
+
`/extensions` prints the same grouped list and does not toggle. `/doctor` probes
|
|
154
|
+
in every mode: in `tui` it opens its list screen, while RPC, JSON, and print
|
|
155
|
+
modes print the same report, and the proactive session-start warning is skipped
|
|
156
|
+
when there is no UI. Notify runs in every mode and picks its method from the
|
|
157
|
+
environment; the OSC methods need a TTY, so a print/JSON/RPC run gets an
|
|
158
|
+
OS-level notification or none.
|
|
159
|
+
|
|
160
|
+
## Configuration
|
|
161
|
+
|
|
162
|
+
The header is tuned in [`header/config.ts`](./header/config.ts) — the mark and
|
|
163
|
+
identity theme tokens, the padding and tagline, and the grouped help entries
|
|
164
|
+
(each naming a Pi keybinding resolved live or a raw command prefix). The footer's
|
|
165
|
+
content and layout are tuned in [`footer/config.ts`](./footer/config.ts) —
|
|
166
|
+
thresholds, gauge
|
|
167
|
+
glyphs and widths, separators, icons, and status keys. The turn separator has no
|
|
168
|
+
JSON either: its glyph, label prefix, padding, and colors are tuned in
|
|
169
|
+
[`separator/config.ts`](./separator/config.ts).
|
|
170
|
+
|
|
171
|
+
Everything JSON-configurable lives in `bytefly.json`, one shared document with a
|
|
172
|
+
top-level section per extension (see [Configuration](../../docs/configuration.md)).
|
|
173
|
+
It is read from the global document, then `<cwd>/.pi/bytefly.json`. The old
|
|
174
|
+
per-extension files
|
|
175
|
+
(`document.json`, `jobs.json`, …) are no longer read; run
|
|
176
|
+
`bun run config:migrate` to move them into the shared document.
|
|
177
|
+
|
|
178
|
+
The price
|
|
179
|
+
calculation is configured by the `price` section of the shared `bytefly.json`
|
|
180
|
+
(see [Configuration](../../docs/configuration.md)), read from
|
|
181
|
+
`~/.pi/agent/bytefly.json`, then `<cwd>/.pi/bytefly.json` (project wins).
|
|
182
|
+
|
|
183
|
+
Both documents are optional, and neither is needed for DeepSeek. The built-in
|
|
184
|
+
table is the base layer for every session: DeepSeek is priced in yuan at the
|
|
185
|
+
published peak and off-peak rates, and every other model keeps Pi's recorded
|
|
186
|
+
cost, whose number comes from Pi's catalog. A document can change the currency,
|
|
187
|
+
replace a built-in rate, or add rates of your own. A malformed document is
|
|
188
|
+
ignored, a wrong-typed value falls back to its default, and an unknown timezone
|
|
189
|
+
drops the schedule rather than the session.
|
|
190
|
+
|
|
191
|
+
```json
|
|
192
|
+
{
|
|
193
|
+
"price": {
|
|
194
|
+
"currency": { "symbol": "¥", "decimals": 2 },
|
|
195
|
+
"fallbackToRecordedCost": true,
|
|
196
|
+
"rates": {
|
|
197
|
+
"deepseek/deepseek-flash": { "input": 2, "output": 8, "cacheRead": 0.04, "cacheWrite": 0, "offPeakMultiplier": 0.5 },
|
|
198
|
+
"deepseek/deepseek-v4-pro": { "input": 9, "output": 27, "cacheRead": 0.3, "cacheWrite": 0, "offPeakMultiplier": 0.5 }
|
|
199
|
+
},
|
|
200
|
+
"peak": {
|
|
201
|
+
"timezone": "Asia/Shanghai",
|
|
202
|
+
"windows": [
|
|
203
|
+
{ "days": [1, 2, 3, 4, 5], "from": "09:00", "to": "12:00" },
|
|
204
|
+
{ "days": [1, 2, 3, 4, 5], "from": "14:00", "to": "18:00" }
|
|
205
|
+
]
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
That file restates the built-in defaults, so on its own it changes nothing; it
|
|
212
|
+
is shown to document every key at once.
|
|
213
|
+
|
|
214
|
+
| Key | Meaning |
|
|
215
|
+
|---|---|
|
|
216
|
+
| `currency.symbol` | Printed before the number; at most four characters. Default `¥`. |
|
|
217
|
+
| `currency.decimals` | Digits after the point, 0–4. Default `2`. |
|
|
218
|
+
| `fallbackToRecordedCost` | Use Pi's recorded cost for a record no rate matches. Default `true`. |
|
|
219
|
+
| `rates` | Per-million-token prices in `currency`, keyed by `provider/model`; the built-in DeepSeek table is the base, and a key stated here replaces its entry. |
|
|
220
|
+
| `rates.<key>.input` · `output` · `cacheRead` · `cacheWrite` | Rate per token class; a class left out is free. |
|
|
221
|
+
| `rates.<key>.offPeakMultiplier` | Multiplier outside every peak window, 0–1. Default `1`. |
|
|
222
|
+
| `peak.timezone` | The IANA zone the windows are stated in. |
|
|
223
|
+
| `peak.windows` | `days` (1 = Monday … 7 = Sunday; omitted means every day), `from` inclusive, `to` exclusive, `HH:MM` local; a `to` before `from` wraps midnight. An empty list means every moment is peak. |
|
|
224
|
+
|
|
225
|
+
Rates merge key by key across the two documents, so a project document can
|
|
226
|
+
override one model without restating the rest; a key present in both is replaced,
|
|
227
|
+
not merged field by field. No windows means every moment is peak, so a rate with
|
|
228
|
+
`offPeakMultiplier: 0.5` and no schedule stays flat. The `price` section is read
|
|
229
|
+
when the footer is installed, so a new session — or `/bytefly` toggled off and on
|
|
230
|
+
— picks up an edit.
|
|
231
|
+
|
|
232
|
+
**Mixing currencies is the sharp edge of the built-in default.** A record priced
|
|
233
|
+
from `rates` or the built-in table is in `currency.symbol` units — yuan for
|
|
234
|
+
DeepSeek — while a record that fell back carries Pi's USD number under that same
|
|
235
|
+
symbol. A session mixing DeepSeek with another provider therefore sums two
|
|
236
|
+
currencies into one number. Either cover every model you use (a `"*"` catch-all
|
|
237
|
+
helps) and set `fallbackToRecordedCost: false`, or set the symbol to `"$"` and
|
|
238
|
+
treat the rates purely as an off-peak correction.
|
|
239
|
+
|
|
240
|
+
### The `notify` section
|
|
241
|
+
|
|
242
|
+
The notify capability reads the same shared `bytefly.json`, under its own
|
|
243
|
+
top-level `notify` key.
|
|
244
|
+
It is **off by default**; `enabled: true` is the whole setup needed for an
|
|
245
|
+
unattended run, and the three trigger flags then narrow what is announced. The
|
|
246
|
+
price section and the notify section are parsed independently, so a wrong-typed
|
|
247
|
+
value in one never reaches the other.
|
|
248
|
+
|
|
249
|
+
```json
|
|
250
|
+
{
|
|
251
|
+
"notify": {
|
|
252
|
+
"enabled": true,
|
|
253
|
+
"turnFinished": true,
|
|
254
|
+
"needsInput": true,
|
|
255
|
+
"jobFinished": true,
|
|
256
|
+
"method": "auto",
|
|
257
|
+
"title": "Pi"
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
| Key | Default | Meaning |
|
|
263
|
+
|---|---|---|
|
|
264
|
+
| `enabled` | `false` | Master switch. A notification on every turn is intrusive, so it is opt-in. |
|
|
265
|
+
| `turnFinished` | `true` | Notify when a full agent run settles and Pi waits for the next message. An aborted run is never announced. |
|
|
266
|
+
| `needsInput` | `true` | Notify when the agent opens a prompt mid-run (`select`, `confirm`, `input`, `editor`, `custom`). A prompt the user typed while idle does not fire. |
|
|
267
|
+
| `jobFinished` | `true` | Notify when a background `job` reaches a terminal state. |
|
|
268
|
+
| `method` | `auto` | `auto`, `osascript`, `notify-send`, `toast`, `osc777`, or `osc99`. An explicit method that the environment cannot deliver is a no-op. |
|
|
269
|
+
| `title` | `Pi` | Notification title, clamped to 40 characters. |
|
|
270
|
+
|
|
271
|
+
A `notify` section is read at `session_start`, so a new session or `/reload`
|
|
272
|
+
picks up an edit.
|
|
273
|
+
|
|
274
|
+
### The `doctor` section
|
|
275
|
+
|
|
276
|
+
The doctor surface reads the same shared `bytefly.json`, under its own top-level
|
|
277
|
+
`doctor` key:
|
|
278
|
+
|
|
279
|
+
```json
|
|
280
|
+
{
|
|
281
|
+
"doctor": {
|
|
282
|
+
"proactive": true,
|
|
283
|
+
"timeoutMs": 2000,
|
|
284
|
+
"skip": []
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
| Key | Default | Meaning |
|
|
290
|
+
|---|---|---|
|
|
291
|
+
| `proactive` | `true` | Probe the extensions with hard requirements at session start and warn once when something is missing. |
|
|
292
|
+
| `timeoutMs` | `2000` | Per-probe timeout for one spawned command, clamped to 100–30000. |
|
|
293
|
+
| `skip` | `[]` | Extension names to leave out of every report. |
|
|
294
|
+
|
|
295
|
+
A malformed `bytefly.json` is ignored like every other config file — and named
|
|
296
|
+
by the report `doctor` produces. The section is read at `session_start`, so a
|
|
297
|
+
new session or `/reload` picks up an edit.
|
|
298
|
+
|
|
299
|
+
## Security
|
|
300
|
+
|
|
301
|
+
- Project settings are only read or written when the project is trusted
|
|
302
|
+
(`ctx.isProjectTrusted()`); an untrusted session offers Global scope only.
|
|
303
|
+
- Discovery never installs or fetches a package: missing sources are skipped.
|
|
304
|
+
- The picker registers no model-facing tool, so the model cannot change resource
|
|
305
|
+
configuration.
|
|
306
|
+
|
|
307
|
+
## Cost calculation
|
|
308
|
+
|
|
309
|
+
Pi prices every model call from its bundled catalog in USD, and the provider API
|
|
310
|
+
never returns a price. Built-in and configured rates replace that for the models
|
|
311
|
+
they match:
|
|
312
|
+
|
|
313
|
+
- **Which built-in rates.** [`footer/price/config.ts`](./footer/price/config.ts) ships DeepSeek's published
|
|
314
|
+
yuan prices, with the peak schedule that doubles the price during peak hours
|
|
315
|
+
([DeepSeek's pricing page](https://api-docs.deepseek.com/quick_start/pricing)).
|
|
316
|
+
They are ordinary rate entries, so a `bytefly.json` key replaces one of
|
|
317
|
+
them, and the schedule is the default `peak` every rate is judged against.
|
|
318
|
+
- **Which rate.** The most specific key wins, with the model segment outranking
|
|
319
|
+
the provider one: `provider/model`, then `*/model`, then `provider/*`, then
|
|
320
|
+
`*`. A segment ending in a star is a prefix match (`*/gpt-*`); equally
|
|
321
|
+
specific keys are resolved in file order.
|
|
322
|
+
- **Which formula.** `tokens × rate ÷ 1,000,000`, summed over the four token
|
|
323
|
+
classes. Cache tokens are never billed again as fresh input.
|
|
324
|
+
- **Which tariff.** Each record is dated by its session entry, so a window is
|
|
325
|
+
applied at the local time the tokens were actually spent: an off-peak turn
|
|
326
|
+
stays off-peak when peak starts. A record with no usable timestamp is priced
|
|
327
|
+
at peak, because it cannot be dated.
|
|
328
|
+
- **Which source.** A record with no matching rate — built-in or configured —
|
|
329
|
+
keeps Pi's recorded cost, or contributes nothing when
|
|
330
|
+
`fallbackToRecordedCost` is `false`.
|
|
331
|
+
|
|
332
|
+
For example, DeepSeek flash at `¥2` in and `¥8` out per million, with the
|
|
333
|
+
published peak windows and `offPeakMultiplier: 0.5`, prices a 1M-in + 1M-out
|
|
334
|
+
turn at `¥10.00` at peak and `¥5.00` off-peak. That example is worked end to end
|
|
335
|
+
in [`footer/price/price.test.ts`](./footer/price/price.test.ts), which also prices the built-in
|
|
336
|
+
table with no config file at all.
|
|
337
|
+
|
|
338
|
+
## Content and colors
|
|
339
|
+
|
|
340
|
+
| Segment | Line / zone | Color | Drops |
|
|
341
|
+
|---|---|---|---|
|
|
342
|
+
| pwd | 1 left | dim | last |
|
|
343
|
+
| session | 1 left | muted, bracketed | after worktree |
|
|
344
|
+
| browse | 1 right | success glyph + accent port | first |
|
|
345
|
+
| branch | 1 right | muted (warning when detached) | last |
|
|
346
|
+
| worktree | 1 right | success | first |
|
|
347
|
+
| context gauge + `%` | 2 left | muted ≤70 · warning 71–90 · error >90 | never |
|
|
348
|
+
| `/window` | 2 left | dim | before cost |
|
|
349
|
+
| cost | 2 left | muted | after tokens |
|
|
350
|
+
| tokens `↑in ↓out` | 2 left | dim | after cache |
|
|
351
|
+
| cache `R… CH …%` | 2 left | dim | first |
|
|
352
|
+
| statuses | 2 left | as emitted by each extension | before the model clips |
|
|
353
|
+
| model | 2 right | accent | never |
|
|
354
|
+
| thinking level | 2 right | thinking token | before cost |
|
|
355
|
+
|
|
356
|
+
The worktree status is read from the `worktree` status key, so the
|
|
357
|
+
[worktree](../worktree/) extension and the bar agree. The browse chip is read
|
|
358
|
+
from the `browse` status key published by [file-browse](../file-browse/) while
|
|
359
|
+
its server runs, and shows the two-tone `◉ <port>` (green glyph, accent port).
|
|
360
|
+
The trailing slot shows plan-style alerts when present and is omitted otherwise;
|
|
361
|
+
a plan chip's trailing plan file name is dropped, so the slot reads `⋮ plan`
|
|
362
|
+
even though the built-in footer keeps the name. An alert (a failed job) sorts to
|
|
363
|
+
the front of the slot. The slot trails the meters after a `│`, so the context
|
|
364
|
+
gauge always starts the line. It keeps each extension's own colour when it
|
|
365
|
+
compacts, and it is droppable: a tight bar drops the slot rather than truncating
|
|
366
|
+
the model, which sits at the end of the line.
|
|
367
|
+
|
|
368
|
+
## Turn separator
|
|
369
|
+
|
|
370
|
+
`agent_settled` fires once after an agent run fully settles — exactly one user
|
|
371
|
+
turn — which is why it is used instead of `turn_end` (that fires once per
|
|
372
|
+
assistant message, so tool-calling rounds would each get a line). On each
|
|
373
|
+
settled run the extension appends an inert custom entry and a renderer draws
|
|
374
|
+
it:
|
|
375
|
+
|
|
376
|
+
```
|
|
377
|
+
╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ turn 3 ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
- The turn number is `count(separators on the current branch) + 1`, so a
|
|
381
|
+
resumed or forked session continues the parent's numbering and abandoned
|
|
382
|
+
branches do not leak counts.
|
|
383
|
+
- The entry is excluded from LLM context, so it costs no tokens, and it
|
|
384
|
+
survives resume and fork. It is drawn under the `bytefly-separator` custom
|
|
385
|
+
entry type.
|
|
386
|
+
- The line is centered and width-adaptive: the label is filled out with the
|
|
387
|
+
glyph, and when it cannot fit the line becomes a plain dashed rule rather
|
|
388
|
+
than overflowing.
|
|
389
|
+
- The rule is installed only in interactive (`tui`) sessions; RPC, JSON, and
|
|
390
|
+
print sessions append nothing.
|
|
391
|
+
- Separators recorded by the retired `turn-separator` extension are not
|
|
392
|
+
rendered, and `PI_DISABLED_EXTENSIONS=turn-separator` no longer affects the
|
|
393
|
+
rule; disable the whole extension with `bytefly` instead.
|
|
394
|
+
- There is no runtime command; the glyph, label prefix, padding, and colors are
|
|
395
|
+
tuned in [`separator/config.ts`](./separator/config.ts).
|
|
396
|
+
|
|
397
|
+
## Extensions picker
|
|
398
|
+
|
|
399
|
+
`/extensions` is a keyboard-driven dock list of every extension Pi resolves for
|
|
400
|
+
the session, so the list matches what actually loads. It reuses Pi's own settings
|
|
401
|
+
and package resolution (`SettingsManager` plus `DefaultPackageManager` with
|
|
402
|
+
`onMissing: "skip"`), so opening the screen never installs or fetches a package.
|
|
403
|
+
|
|
404
|
+
- Lists every resolved extension, grouped by source (each package, user
|
|
405
|
+
settings, project settings), sorted packages-first and then by display name.
|
|
406
|
+
- Omits `bytefly` itself, so the tool that manages the list is never a row.
|
|
407
|
+
Turn the chrome off with `/bytefly`; use `pi config` to disable bytefly
|
|
408
|
+
entirely.
|
|
409
|
+
- Shows whether each one is enabled (`[x]`) or disabled (`[ ]`).
|
|
410
|
+
- Toggles the focused extension with `Space` / `Enter`, writing `+pattern` /
|
|
411
|
+
`-pattern` into `extensions` (and package filter objects) in the active
|
|
412
|
+
scope.
|
|
413
|
+
- Switches the write scope with `Tab`: **Global** (personal
|
|
414
|
+
`settings.json`) or **Project** (`.pi/settings.json`, only when the project
|
|
415
|
+
is trusted).
|
|
416
|
+
- Offers a reload after pending changes, so the new selection takes effect
|
|
417
|
+
without leaving the session.
|
|
418
|
+
|
|
419
|
+
### Keys
|
|
420
|
+
|
|
421
|
+
| Key | Effect |
|
|
422
|
+
|---|---|
|
|
423
|
+
| `Space` / `Enter` | Toggle the focused extension. |
|
|
424
|
+
| `Tab` | Switch between Global and Project scope. |
|
|
425
|
+
| `↑`/`↓`, `j`/`k` | Move the selection. |
|
|
426
|
+
| `PgUp`/`PgDn` | Move a page. |
|
|
427
|
+
| `g` / `G`, `Home` / `End` | Jump to the first or last extension. |
|
|
428
|
+
| `Esc` / `Ctrl+C` | Close the screen. |
|
|
429
|
+
|
|
430
|
+
Mouse wheel scrolling works; every action also has a keyboard path.
|
|
431
|
+
|
|
432
|
+
## Doctor
|
|
433
|
+
|
|
434
|
+
`/doctor` probes every declared extension and reports each check as `ok`,
|
|
435
|
+
`missing`, `degraded`, or `info`; `run_doctor` returns the same report as text
|
|
436
|
+
plus a typed `structuredContent` report. Only `missing` and `degraded` are
|
|
437
|
+
problems: `missing` means an enabled extension cannot do its job, `degraded`
|
|
438
|
+
means reduced capability — an optional package that is absent, a config file
|
|
439
|
+
that fails to parse, a login that has not happened. `doctor` never changes
|
|
440
|
+
anything and never installs anything.
|
|
441
|
+
|
|
442
|
+
`/doctor <extension>` narrows the report to one extension — including the
|
|
443
|
+
checks that passed — and `/doctor refresh` ignores the findings cached earlier
|
|
444
|
+
in the session. In the TUI the report is a docked list screen: one row per
|
|
445
|
+
extension with a severity glyph, the focused extension's findings underneath.
|
|
446
|
+
`Enter` switches between problems only and every check, `r` re-probes, `Esc`
|
|
447
|
+
closes; while a re-probe is in flight the hint shows `refreshing…` and a second
|
|
448
|
+
`r` is ignored, so probes cannot stack. Every other mode prints the same report
|
|
449
|
+
as text.
|
|
450
|
+
|
|
451
|
+
### Keys
|
|
452
|
+
|
|
453
|
+
| Key | Effect |
|
|
454
|
+
|---|---|
|
|
455
|
+
| `↑`/`↓`, `j`/`k` | Move the selection. |
|
|
456
|
+
| `PgUp`/`PgDn` | Move a page. |
|
|
457
|
+
| `g` / `G`, `Home` / `End` | Jump to the first or last extension. |
|
|
458
|
+
| `Enter` | Toggle between problems only and every check. |
|
|
459
|
+
| `r` | Re-probe. |
|
|
460
|
+
| `Esc` | Close the screen. |
|
|
461
|
+
|
|
462
|
+
### What is checked
|
|
463
|
+
|
|
464
|
+
| Extension | Hard requirement | Optional / degraded |
|
|
465
|
+
|---|---|---|
|
|
466
|
+
| worktree | `git` | `.worktreeinclude` symlinks need symlink support |
|
|
467
|
+
| rewind | `git` | — |
|
|
468
|
+
| document | — | — |
|
|
469
|
+
| subagent | a runnable nested `pi` | project-local agents need a trusted project |
|
|
470
|
+
| plan | — | `git` for project-local `.pi/plans`; an interactive UI |
|
|
471
|
+
| memory | — | `git` for the project-scope root |
|
|
472
|
+
| file-browse | — | `git` for the branch chip; the browser opener is best-effort |
|
|
473
|
+
| web-access | — | `unpdf` |
|
|
474
|
+
| wechat | — | `wechat/credentials.json` (not logged in) |
|
|
475
|
+
| job | — | Windows has no `EXIT` trap; `ps` is POSIX-only |
|
|
476
|
+
| todo, goal | — | their config files |
|
|
477
|
+
| bytefly | — | TUI only for the header, footer, and turn rule; the picker's project scope needs a trusted project; `bytefly.json` |
|
|
478
|
+
| question | — | needs an interactive UI |
|
|
479
|
+
|
|
480
|
+
Every extension that reads a config file also gets the malformed-JSON check:
|
|
481
|
+
the file and the parse error are named instead of being silently ignored. The
|
|
482
|
+
install guidance a tool error shows and the hint this report prints come from
|
|
483
|
+
the same entry in `lib/requirements.ts`, so the two cannot drift.
|
|
484
|
+
|
|
485
|
+
## Notifications
|
|
486
|
+
|
|
487
|
+
The notify capability exists for unattended runs: start a long turn, leave the
|
|
488
|
+
terminal, and get a desktop notification when Pi needs you — or when it is done.
|
|
489
|
+
It fires on three events, each independently switchable in the `notify` section
|
|
490
|
+
of `bytefly.json` (see [Configuration](#configuration)):
|
|
491
|
+
|
|
492
|
+
- **A run settled** (`agent_settled`). Pi finished the turn and waits for the
|
|
493
|
+
next message. An aborted run is skipped: interrupting with Escape means the
|
|
494
|
+
user is already at the keyboard.
|
|
495
|
+
- **The agent needs input.** Pi opened a prompt while a run was active — an
|
|
496
|
+
`ask_user_question`, a plan review, a subagent approval. A prompt opened while
|
|
497
|
+
idle is a command the user just typed, so it does not fire; the run boundary
|
|
498
|
+
is what separates "the agent is asking" from "the user is asking".
|
|
499
|
+
- **A background job finished.** The [job](../job/) extension announces each
|
|
500
|
+
terminal transition on the shared `pi.events` bus
|
|
501
|
+
(`my-pi-agent/job-finished`); bytefly listens and never imports it.
|
|
502
|
+
|
|
503
|
+
### Delivery method
|
|
504
|
+
|
|
505
|
+
`method: "auto"` (the default) chooses from the environment, terminal first so
|
|
506
|
+
it works over SSH:
|
|
507
|
+
|
|
508
|
+
| Detected | Method |
|
|
509
|
+
|---|---|
|
|
510
|
+
| `WT_SESSION` (Windows Terminal / WSL) | `toast` — a PowerShell Windows toast |
|
|
511
|
+
| `KITTY_WINDOW_ID` | `osc99` |
|
|
512
|
+
| `TERM_PROGRAM` is `WezTerm` / `iTerm.app`, or `GHOSTTY_RESOURCES_DIR` is set | `osc777` |
|
|
513
|
+
| a plain `win32` host | `toast` |
|
|
514
|
+
| a TTY on any other host | `osc777` |
|
|
515
|
+
| headless macOS | `osascript` |
|
|
516
|
+
| headless Linux | `notify-send` |
|
|
517
|
+
|
|
518
|
+
Set `method` to any of `osc777`, `osc99`, `toast`, `osascript`, or `notify-send`
|
|
519
|
+
to force one. OSC 777 reaches Ghostty, iTerm2, WezTerm, and rxvt-unicode;
|
|
520
|
+
OSC 99 reaches Kitty. Off a TTY the OSC methods are a no-op — writing an escape
|
|
521
|
+
sequence into captured output would corrupt it — so a headless run needs an
|
|
522
|
+
OS-level method.
|
|
523
|
+
|
|
524
|
+
### Text
|
|
525
|
+
|
|
526
|
+
A notification body names the session (the session name, else the working
|
|
527
|
+
directory's folder) so concurrent runs can be told apart, and a finished job
|
|
528
|
+
adds its id, status, exit code, and label. Every piece that can carry model- or
|
|
529
|
+
user-authored text — a prompt title, a job label — is sanitized to one
|
|
530
|
+
control-free line before it reaches an escape sequence or a shell argument.
|
|
531
|
+
|
|
532
|
+
## Limitations
|
|
533
|
+
|
|
534
|
+
- **The picker manages only the `extensions` resource type.** Skills, prompts,
|
|
535
|
+
and themes are handled by Pi's own `pi config`.
|
|
536
|
+
- **Built-in extensions are not listed** (`builtin:mcp`, `builtin:codemode`, …):
|
|
537
|
+
their authoritative names are not exposed to extensions. Use `pi config`.
|
|
538
|
+
- **Packages are not installed, removed, or updated here**; use `pi install`,
|
|
539
|
+
`pi remove`, and `pi update`.
|
|
540
|
+
- **Project toggles are two-state** (`enable`/`disable`). Resetting an override
|
|
541
|
+
back to "inherit" is done in `pi config`.
|
|
542
|
+
- **One built-in provider.** DeepSeek is the only provider with a built-in
|
|
543
|
+
table; another provider's currency and tariff have to be stated in
|
|
544
|
+
`bytefly.json`.
|
|
545
|
+
- **Holidays.** A tariff that excludes public holidays — DeepSeek's peak window
|
|
546
|
+
does — cannot be expressed. A weekend can, by leaving `days` 6 and 7 out.
|
|
547
|
+
- **One schedule.** A single `peak` section applies to every rate. Only a rate
|
|
548
|
+
with an `offPeakMultiplier` below 1 is affected by it, so two providers with
|
|
549
|
+
different time-of-day tariffs cannot both be described.
|
|
550
|
+
- **No tiers, no special cache writes.** Per-request input tiers and
|
|
551
|
+
Anthropic's 2× one-hour cache write are not modelled; Pi's recorded cost
|
|
552
|
+
covers those models when no rate is configured.
|
|
553
|
+
- **No FX conversion.** A fallback record is USD and is never converted into
|
|
554
|
+
the configured currency.
|
|
555
|
+
- **The displayed number is the total, rounded once** — not the sum of rounded
|
|
556
|
+
records.
|
|
557
|
+
- **`doctor` can only check what is declared.** A new extension with no
|
|
558
|
+
`REQUIREMENTS` entry is invisible to it — `test/structure.test.ts` fails on
|
|
559
|
+
exactly that, which is why the table cannot rot quietly.
|
|
560
|
+
- **The disabled check reads `PI_DISABLED_EXTENSIONS`** through `lib/env.ts`; it
|
|
561
|
+
does not read Pi's own `settings.json` disable list, so an extension disabled
|
|
562
|
+
only there may still be probed.
|
|
563
|
+
- **A probe proves a binary runs, not that it works:** a `git` that crashes on a
|
|
564
|
+
specific repository is still `ok`.
|
|
565
|
+
- **Config scope is checked for `<agent-dir>/bytefly.json` and
|
|
566
|
+
`<cwd>/.pi/bytefly.json` only**; a `job.registryDir` override is not
|
|
567
|
+
followed.
|
|
568
|
+
- **Notifications are not focus-aware.** They fire whether or not the terminal
|
|
569
|
+
has focus, and once per trigger rather than once per unseen event. There is
|
|
570
|
+
no focus API to consult.
|
|
571
|
+
- **A background job cannot ask for input.** The `needsInput` trigger is a
|
|
572
|
+
prompt raised during a run, not a process waiting on stdin: jobs are
|
|
573
|
+
fire-and-observe, so that state does not exist.
|
|
574
|
+
- **An explicit `method` is honored blindly.** Point `method` at a notifier the
|
|
575
|
+
host lacks (or an OSC method off a TTY) and the notification is silently
|
|
576
|
+
dropped; only `auto` inspects the environment.
|
|
577
|
+
|
|
578
|
+
## Non-goals
|
|
579
|
+
|
|
580
|
+
- No per-segment CLI: the bar's look lives in `footer/config.ts`, the price in
|
|
581
|
+
`bytefly.json`.
|
|
582
|
+
- No per-model cost breakdown, no `/bytefly price`, and no on-screen marker
|
|
583
|
+
for which tariff or which source produced a number.
|
|
584
|
+
- No todo-count segment; the [todo](../todo/) widget already shows progress.
|
|
585
|
+
- No shelling out for git ahead/behind; the footer data provider exposes only
|
|
586
|
+
the branch.
|
|
587
|
+
- No session or model data in the header: it shows Pi's brand mark and version
|
|
588
|
+
only. The footer owns the session, model, and working-directory data.
|
|
589
|
+
- No auto-fix: `doctor` reports the install command; it never runs it.
|
|
590
|
+
- No aggregation of runtime failures: a tool that errored earlier in the session
|
|
591
|
+
is not a prerequisite problem, and `doctor` does not pretend to know about it.
|
|
592
|
+
- No status chip for the doctor report: it is on demand plus one warning, not a
|
|
593
|
+
permanent footer segment.
|
|
594
|
+
- No notification for individual tool calls, message turns, or subagent steps:
|
|
595
|
+
the three triggers — a settled run, a prompt mid-run, a finished job — are the
|
|
596
|
+
whole surface.
|
|
597
|
+
- No sound, action buttons, notification history, or de-duplication; a
|
|
598
|
+
notification is a fire-and-forget message, not an inbox.
|
|
599
|
+
|
|
600
|
+
The built-in footer's `(auto)` auto-compact indicator and `(sub)`
|
|
601
|
+
subscription-cost marker are not reachable from `ExtensionContext` (there is no
|
|
602
|
+
`autoCompactEnabled` or provider subscription check), so the bar omits them
|
|
603
|
+
rather than showing a wrong value.
|
|
604
|
+
|
|
605
|
+
## Pi integration
|
|
606
|
+
|
|
607
|
+
| Integration point | Details |
|
|
608
|
+
|---|---|
|
|
609
|
+
| 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. |
|
|
610
|
+
| Footer | `ctx.ui.setFooter` with a custom component, replacing Pi's built-in footer; installed only when `ctx.mode === "tui"` and the footer is on. |
|
|
611
|
+
| Command | `/bytefly [header|footer|status]` toggles the header and footer together (no argument) or one surface, and reports their state (tui only; a no-op elsewhere), with Tab completion. |
|
|
612
|
+
| Turn separator | `registerEntryRenderer("bytefly-separator")` draws each rule; `agent_settled` appends `pi.appendEntry("bytefly-separator", { turn })` in `tui` mode only. |
|
|
613
|
+
| 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. |
|
|
614
|
+
| Picker command | `pi.registerCommand("extensions")`; `tui` opens a dock screen via `ctx.ui.custom()` with the rails suppressed, other modes print through `ctx.ui.notify()`. |
|
|
615
|
+
| Picker discovery | `SettingsManager.create(...)` plus `DefaultPackageManager.resolve({ onMissing: "skip" })`, both public exports; both views drop `bytefly`'s own entrypoint. |
|
|
616
|
+
| Picker writes | `SettingsManager` setters (`setExtensionPaths`, `setPackages`, `setProjectExtensionPaths`, `setProjectPackages`) then `flush()`; `ctx.reload()` after the screen closes when changes are pending. |
|
|
617
|
+
| Doctor tool | `pi.registerTool("run_doctor")`, read-only (`readOnlyHint: true`), `exposure: "direct"`, active by default, with a typed `outputSchema` mirroring `structuredContent`. Its `readOnlyHint` is what the shared default-deny policy in `lib/policy.ts` reads to allow it in plan mode. |
|
|
618
|
+
| Doctor command | `pi.registerCommand("doctor")`; `tui` opens a dock screen via `ctx.ui.custom()` with the rails suppressed, other modes print through `ctx.ui.notify()`. |
|
|
619
|
+
| Doctor probes | `run_doctor` and `/doctor` share the session cache; `/doctor 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. |
|
|
620
|
+
| 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. |
|
|
621
|
+
| Config | `bytefly.json` from the agent directory and `<cwd>/.pi`; the `price` section is read when the footer is installed (a new session, or `/bytefly` toggled back on), and the `notify` and `doctor` sections at `session_start`. Nothing watches the document. |
|
|
622
|
+
| Data sources | `footerData.getGitBranch()`, `footerData.getAvailableProviderCount()`, `ctx.getContextUsage()`, `ctx.sessionManager.getEntries()`, `ctx.sessionManager.getSessionName()`, `ctx.model`; subscribes to `footerData.onBranchChange` to repaint. |
|
|
623
|
+
| 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 picker derives its screen from live runtime state and persists only through `settings.json`. 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. |
|
|
624
|
+
| Lifecycle | `session_start` installs the header and footer, points the doctor runtime at the session cwd, probes the extensions with hard requirements when `bytefly.json`'s `doctor.proactive` is on, and loads the notify config and subscribes to the job bus; `session_tree` reinstalls the header and footer; `session_shutdown` restores Pi's built-in header and footer, disposes the branch subscription, drops doctor's cached findings, and drops the notify bus subscription. |
|
|
625
|
+
|
|
626
|
+
## Design notes
|
|
627
|
+
|
|
628
|
+
- **The header is a nameplate, not a dashboard.** Pi's built-in brand colors are
|
|
629
|
+
fixed, so the mark ignores the active theme; bytefly's header draws a two-tone
|
|
630
|
+
mark from `accent`/`warning` and pairs it with the agent identity, the chrome
|
|
631
|
+
identity, and a grouped help block. It adds no session or model data — that is
|
|
632
|
+
the footer's job — and it stays a fixed four-plus rows so the startup
|
|
633
|
+
transcript never reflows.
|
|
634
|
+
- **Two lines, four zones.** Line 1 is identity (`pwd` left, branch + worktree
|
|
635
|
+
right); line 2 is resources (meters left, model + level right). This keeps
|
|
636
|
+
"where am I / what state" separate from "how much am I burning".
|
|
637
|
+
- **The gauge is anchored at column 0.** A leading mode slot would shift the
|
|
638
|
+
context percentage left and right as plan mode came and went; trailing it
|
|
639
|
+
after a `│` keeps the percentage in a fixed place while the mode stays
|
|
640
|
+
visible as the meters drop.
|
|
641
|
+
- **Statuses are routed, not repeated.** The built-in footer renders every
|
|
642
|
+
`setStatus` value on its own line; the bar splits them by meaning (`worktree`
|
|
643
|
+
and `browse` are location, plan/alert is mode). The [wechat](../wechat/)
|
|
644
|
+
extension publishes `✆ wechat` while its bridge runs, joining the mode/alert
|
|
645
|
+
slot; this is why the bar must render `getExtensionStatuses()` at all.
|
|
646
|
+
- **Browse joins the git state.** The `◉ <port>` chip is routed beside `⎇` branch
|
|
647
|
+
and `⑂` worktree rather than after `pwd`, so the left zone stays the
|
|
648
|
+
filesystem identity (path + session name) and every "where am I / what is
|
|
649
|
+
running" fact sits on line 1 right.
|
|
650
|
+
- **Segments degrade; they do not clip.** Each `Segment` carries progressively
|
|
651
|
+
shorter `forms` and a `weight`; the layout advances the lowest-priority form
|
|
652
|
+
or drops the lowest-priority segment, and only truncates once everything is
|
|
653
|
+
minimal. The gauge steps `10 → 5 → 3 → 0` blocks, tokens drop before cost,
|
|
654
|
+
and a branch that no longer fits its name drops rather than collapsing to a
|
|
655
|
+
glyph. The gauge floors its fill and shows at least one block for any
|
|
656
|
+
non-zero usage, so it reads full only at 100% and a small percentage never
|
|
657
|
+
disappears; unknown usage draws no gauge at all. Only at extreme widths does
|
|
658
|
+
`truncateToWidth` clip the remaining text.
|
|
659
|
+
- **Separators shrink to a single space.** Padded separators (` · `, ` │ `)
|
|
660
|
+
collapse to one space before `truncateToWidth` applies, so adjacent segments
|
|
661
|
+
never collide and the ellipsis never replaces a `│` with nothing after it.
|
|
662
|
+
- **Statuses compact with their colour.** A themed status's compact icon form
|
|
663
|
+
reattaches its opening and closing SGR sequences around the visible text, so a
|
|
664
|
+
red `✗1` stays red instead of losing its colour exactly when the bar tightens.
|
|
665
|
+
A two-token `icon count` badge (for `rewind`: `↺ 2`) compacts to `↺2`, keeping
|
|
666
|
+
the count. The slot sorts an alert (a failed job) ahead of the modes and is
|
|
667
|
+
droppable, so it goes before the model is clipped.
|
|
668
|
+
- **Neutral by default; color means something.** Only the context gauge carries
|
|
669
|
+
status color, and it stays `muted` below 70%; cost is `muted`, the session and
|
|
670
|
+
branch are `muted`, and tokens/cache/window are `dim`, so yellow and red are
|
|
671
|
+
reserved for the gauge crossing a threshold rather than always on.
|
|
672
|
+
- **Data is read fresh, except the usage scan.** The component resolves the
|
|
673
|
+
theme through a getter and reads a fresh snapshot each render. The expensive
|
|
674
|
+
part — summing token/cost totals across the session — is memoized by
|
|
675
|
+
`sessionId:leafId` (every append moves the leaf), avoiding
|
|
676
|
+
`getEntryCount()`, which is not exposed on the read-only session manager.
|
|
677
|
+
- **TTY-only, fail-safe.** The bytefly is installed only in `tui` mode;
|
|
678
|
+
non-interactive runs load the extension and change nothing.
|
|
679
|
+
- **Notify splits when, what, and how.** The trigger policy lives in
|
|
680
|
+
`notify/register.ts`, the wording in `notify/format.ts`, and the delivery in
|
|
681
|
+
`notify/dispatch.ts`, so each is testable without a terminal or a child
|
|
682
|
+
process and the sanitize-at-the-boundary rule has one home. The `job`
|
|
683
|
+
extension publishes completions on `pi.events` rather than being imported,
|
|
684
|
+
because extensions load with isolated caches and may not import each other;
|
|
685
|
+
the channel and its payload live in `lib/events.ts`.
|
|
686
|
+
- **The price is a stage, not a branch in the renderer.** Entries are reduced to
|
|
687
|
+
usage records (`collect/`), records are priced into totals (`price/`), and the
|
|
688
|
+
snapshot is drawn (`render/`). Pricing is a pure function of records and rates
|
|
689
|
+
— no session, terminal, or theme to stub — which is what lets the published
|
|
690
|
+
DeepSeek example be a unit test. The recorded-cost fallback keeps the stage
|
|
691
|
+
opt-in per model: a record with no matching rate is exactly Pi's own number,
|
|
692
|
+
in Pi's catalog units. The built-in DeepSeek table is only the default value
|
|
693
|
+
of `rates`, so nothing in the pricer is provider-specific.
|
|
694
|
+
- **DeepSeek is built in, not bundled as a catalog.** Its published price is in
|
|
695
|
+
yuan at two tariffs, while Pi's catalog holds one USD number per model, so an
|
|
696
|
+
unconfigured DeepSeek session showed a bill that was neither the right
|
|
697
|
+
currency nor the right amount outside peak. The table sits beside `CONFIG` as
|
|
698
|
+
the default `rates` and `peak`, which keeps config in charge and leaves the
|
|
699
|
+
pricer a pure function of records and rates.
|
|
700
|
+
- **A record is dated, not the frame.** The peak schedule is evaluated against
|
|
701
|
+
each entry's own timestamp, so a total is stable as the clock crosses a
|
|
702
|
+
boundary — and memoizing the scan by `sessionId:leafId` stays valid.
|
|
703
|
+
- **The picker reuses Pi's resolution; only the toggle is ported.** Reading
|
|
704
|
+
`SettingsManager` and `DefaultPackageManager` (public exports) makes the list
|
|
705
|
+
match what Pi loads; the `+`/`-` pattern arithmetic is a trimmed port of Pi's
|
|
706
|
+
config selector, kept pure and separately tested so a future drift is easy to
|
|
707
|
+
diff.
|
|
708
|
+
- **Two picker views, one write scope.** Personal (`global`) and effective
|
|
709
|
+
(`project`) resources are resolved separately, mirroring `pi config`; the
|
|
710
|
+
screen shows the view for the active scope and writes through the matching
|
|
711
|
+
store. A project package override is written with `autoload: false`, so it
|
|
712
|
+
filters the personal entry instead of replacing it.
|
|
713
|
+
- **Doctor declarations live in `lib/`, probes in the extension, presentation in
|
|
714
|
+
a third layer.** An extension cannot import another extension's modules, so
|
|
715
|
+
the only legal home for a shared prerequisite table is `lib/requirements.ts`;
|
|
716
|
+
the report itself is a pure function of the probe results.
|
|
717
|
+
- **The install hint has one home.** `document` and `web-access` format their
|
|
718
|
+
tool errors from the same `REQUIREMENTS` entries `doctor` prints, so a user
|
|
719
|
+
reads the same command whether a tool failed or the report named it.
|
|
720
|
+
- **The doctor is quiet by default at startup.** Only `missing` findings warn,
|
|
721
|
+
only for enabled extensions, only with a UI, and only once per session;
|
|
722
|
+
`degraded` findings — including a malformed config — wait in `/doctor`, because
|
|
723
|
+
a session that nags about an optional package is worse than one that never
|
|
724
|
+
mentions it.
|
|
725
|
+
- **Probe the real thing.** `subagent`'s requirement resolves through the same
|
|
726
|
+
`getPiInvocation` the extension uses rather than re-guessing whether a `pi`
|
|
727
|
+
binary exists.
|
|
728
|
+
- **The prerequisite table is data.** No I/O, no host access, no module state:
|
|
729
|
+
every probe dependency arrives as a seam, so `probeExtension` is testable with
|
|
730
|
+
no process and `lib/` stays value-only.
|
|
731
|
+
|
|
732
|
+
## Files
|
|
733
|
+
|
|
734
|
+
The six surfaces are folders: `header/`, `footer/` (the status bar and its
|
|
735
|
+
price calculation), `separator/`, `picker/`, `doctor/`, and `notify/`.
|
|
736
|
+
|
|
737
|
+
| File | Responsibility |
|
|
738
|
+
|---|---|
|
|
739
|
+
| `index.ts` | Composition root: the disable guard and the six `register*` calls. |
|
|
740
|
+
| `header/config.ts` | Header tuning (`HEADER_CONFIG`): mark and identity tokens, padding, tagline, and the grouped help entries. |
|
|
741
|
+
| `header/component.ts` | `ByteflyHeader`: the mark, identity rows, rule, and grouped help, plus its factory. |
|
|
742
|
+
| `header/register.ts` | Installs/restores the header, exposes its enabled state, and owns its session lifecycle. |
|
|
743
|
+
| `footer/register.ts` | Installs/restores the footer, registers `/bytefly [header|footer|status]`, and owns its session lifecycle. |
|
|
744
|
+
| `footer/config.ts` | Bar tuning (`CONFIG`): thresholds, gauge, separators, icons, status keys, labels. |
|
|
745
|
+
| `footer/types.ts` | `StatusSnapshot`, `Segment`, `LineSpec`, `UsageRecord`, and footer/TUI shapes. |
|
|
746
|
+
| `footer/collect/entries.ts` | Session entries → usage records, each resolved to the model in effect. |
|
|
747
|
+
| `footer/collect/snapshot.ts` | Snapshot assembly; memoized pricing scan per branch leaf. |
|
|
748
|
+
| `footer/price/types.ts` | `Currency`, `ModelRates`, `PeakWindow`/`PeakSchedule`, `ByteflyConfig`. |
|
|
749
|
+
| `footer/price/config.ts` | The `price` section of `bytefly.json`: schema, defaults, built-in DeepSeek rates/tariff, normalizer, loader. |
|
|
750
|
+
| `footer/price/rates.ts` | Model-key matching: exact and wildcard, most specific first. |
|
|
751
|
+
| `footer/price/schedule.ts` | Peak-window parsing and evaluation in a configured zone. |
|
|
752
|
+
| `footer/price/price.ts` | Records → totals: rates, peak multiplier, recorded-cost fallback. |
|
|
753
|
+
| `footer/render/format.ts` | Generic formatters (tokens, cost in the configured currency, cwd, gauge, colors). |
|
|
754
|
+
| `footer/render/status.ts` | Status-chip transforms: `compactStatus` and `dropStatusDetail`. |
|
|
755
|
+
| `footer/render/segments.ts` | The segment builders: forms, weights, separators, colors. |
|
|
756
|
+
| `footer/render/lines.ts` | Snapshot → two `LineSpec`s; status routing. |
|
|
757
|
+
| `footer/render/layout.ts` | Width reduction and right-zone folding. |
|
|
758
|
+
| `footer/render/footer.ts` | `StatusBar` component and footer factory. |
|
|
759
|
+
| `separator/config.ts` | Turn-rule tuning: `customType`, glyph, label prefix, padding, and colors. |
|
|
760
|
+
| `separator/register.ts` | Registers the entry renderer and appends a separator on `agent_settled`. |
|
|
761
|
+
| `separator/component.ts` | `SeparatorComponent`: the stateless entry renderer. |
|
|
762
|
+
| `separator/format.ts` | Pure `separatorLabel` / `separatorParts` / `separatorLine`. |
|
|
763
|
+
| `separator/types.ts` | `TurnSeparatorData`, `SeparatorParts`. |
|
|
764
|
+
| `picker/types.ts` | Picker host seams (`SettingsStore`, `ExtensionResolver`, `Discovery`) and row types. |
|
|
765
|
+
| `picker/resources.ts` | Pure grouping, labels, display names, pattern helpers, text listing. |
|
|
766
|
+
| `picker/toggle.ts` | Pure next-settings transforms for global and project toggles. |
|
|
767
|
+
| `picker/discovery.ts` | Host wiring: builds the global and effective views from Pi's managers. |
|
|
768
|
+
| `picker/runtime.ts` | Picker scope, live view copies, dirty tracking, and persistence. |
|
|
769
|
+
| `picker/tui.ts` | `ExtensionsListComponent`, the dock screen. |
|
|
770
|
+
| `picker/commands.ts` | `/extensions`: TUI path and text fallback. |
|
|
771
|
+
| `doctor/register.ts` | Registers `run_doctor`, `/doctor`, and the proactive session-start check; returns the runtime. |
|
|
772
|
+
| `doctor/runtime.ts` | Host seams, the session cache, and the two probe modes. |
|
|
773
|
+
| `doctor/tools.ts` | The `run_doctor` tool and its renderers. |
|
|
774
|
+
| `doctor/commands.ts` | The `/doctor` command and its TUI wiring. |
|
|
775
|
+
| `doctor/schema.ts` | Tool parameters and the structured output schema. |
|
|
776
|
+
| `doctor/config.ts` | `DEFAULT_CONFIG`, `normalizeConfig`, `loadConfig` for the `doctor` section of `bytefly.json`. |
|
|
777
|
+
| `doctor/format.ts` | Pure report text, rows, glyphs, and the proactive warning sentence. |
|
|
778
|
+
| `doctor/tui.ts` | `DoctorScreen`, the docked list screen with its detail pane. |
|
|
779
|
+
| `notify/types.ts` | `NotifyConfig`, `NotifyMethod`, and the method list. |
|
|
780
|
+
| `notify/config.ts` | The `notify` section of `bytefly.json`: defaults, validation, loader. |
|
|
781
|
+
| `notify/dispatch.ts` | Method selection and delivery: OSC 777/99, Windows toast, `osascript`, `notify-send`, over injected seams. |
|
|
782
|
+
| `notify/format.ts` | Pure notification text: session label and the turn / input / job bodies. |
|
|
783
|
+
| `notify/register.ts` | Registers the three triggers, loads the config at `session_start`, and owns the job-bus subscription. |
|
|
784
|
+
|
|
785
|
+
## Testing
|
|
786
|
+
|
|
787
|
+
`extensions/bytefly/` covers the install/restore/toggle lifecycle
|
|
788
|
+
(`extension`), the header's rendering, width shedding, and live theme
|
|
789
|
+
(`header/component`),
|
|
790
|
+
the formatters (`footer/render/format`), the status-chip transforms
|
|
791
|
+
(`footer/render/status`), entry
|
|
792
|
+
reduction and model inheritance (`footer/collect/entries`), snapshot assembly
|
|
793
|
+
and its memoization (`footer/collect/snapshot`), rate matching
|
|
794
|
+
(`footer/price/rates`), window parsing
|
|
795
|
+
and peak evaluation in two zones (`footer/price/schedule`), the pricing formula,
|
|
796
|
+
the off-peak multiplier, the built-in DeepSeek table with no config file, and the
|
|
797
|
+
fallback (`footer/price/price`), the config schema, its clamping, the built-in
|
|
798
|
+
defaults and their replacement, and the two file layers
|
|
799
|
+
(`footer/price/config`), zone/priority
|
|
800
|
+
assignment
|
|
801
|
+
(`footer/render/lines`), the reduction, separator collapse, and right-zone
|
|
802
|
+
folding across a width sweep from 6 to 120 plus wide characters
|
|
803
|
+
(`footer/render/layout`), exact
|
|
804
|
+
golden output at fixed widths (`footer/render/render`), and the install/skip/
|
|
805
|
+
restore/toggle lifecycle plus a config-driven price (`extension`). The turn
|
|
806
|
+
separator
|
|
807
|
+
is covered by its pure formatting (`separator/format`) and its
|
|
808
|
+
register/append/render/disable lifecycle (`separator/register`). The picker is
|
|
809
|
+
covered by the pure helpers (`picker/resources`), the toggle transforms
|
|
810
|
+
(`picker/toggle`), discovery with an injected factory (`picker/discovery`), the
|
|
811
|
+
runtime state machine (`picker/runtime`), the screen's rendering and key
|
|
812
|
+
handling (`picker/tui`), and the command through the bytefly factory
|
|
813
|
+
(`picker/commands`). The doctor surface is covered by its pure report
|
|
814
|
+
formatting (`doctor/format`), the screen's rendering and key handling
|
|
815
|
+
(`doctor/tui`), its config loader (`doctor/config`), and registration, the
|
|
816
|
+
proactive warning, the session cache, and `run_doctor` through the factory
|
|
817
|
+
(`doctor/register`). The notify capability is covered by its section loader
|
|
818
|
+
(`notify/config`), the pure text builders (`notify/format`), method selection and
|
|
819
|
+
delivery over recording seams (`notify/dispatch`), and its three triggers,
|
|
820
|
+
reload, and shutdown through `registerNotify` (`notify/register`).
|