@gavin-hu/my-pi-agent 0.7.0 → 0.8.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/README.md +10 -10
- package/README.zh-CN.md +153 -0
- package/extensions/bytefly/README.md +129 -122
- package/extensions/bytefly/doctor/format.ts +3 -22
- package/extensions/bytefly/doctor/register.ts +10 -8
- package/extensions/bytefly/footer/register.ts +17 -91
- package/extensions/bytefly/header/component.ts +8 -4
- package/extensions/bytefly/header/config.ts +11 -6
- package/extensions/bytefly/header/register.ts +7 -27
- package/extensions/bytefly/index.ts +15 -14
- package/extensions/bytefly/panel/commands.ts +147 -0
- package/extensions/bytefly/panel/format.ts +135 -0
- package/extensions/bytefly/panel/join.ts +103 -0
- package/extensions/bytefly/panel/tui.ts +416 -0
- package/extensions/bytefly/panel/types.ts +44 -0
- package/extensions/bytefly/picker/resources.ts +0 -11
- package/extensions/file-browse/README.md +12 -5
- package/extensions/file-browse/markdown.ts +227 -45
- package/extensions/plan/README.md +16 -12
- package/extensions/plan/plans.ts +16 -17
- package/extensions/plan/runtime.ts +1 -1
- package/extensions/plan/steps.ts +31 -19
- package/extensions/plan/tools.ts +62 -32
- package/extensions/plan/types.ts +6 -6
- package/lib/README.md +1 -1
- package/lib/requirements.ts +3 -3
- package/lib/tui.ts +7 -8
- package/lib/web/components.ts +11 -0
- package/package.json +1 -1
- package/extensions/bytefly/doctor/commands.ts +0 -68
- package/extensions/bytefly/doctor/tui.ts +0 -140
- package/extensions/bytefly/picker/commands.ts +0 -73
- package/extensions/bytefly/picker/tui.ts +0 -260
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
# bytefly — the TUI header, footer, turn rule,
|
|
1
|
+
# bytefly — the TUI header, footer, turn rule, extension screen, and notifications
|
|
2
2
|
|
|
3
3
|
`bytefly` owns the terminal chrome, configuration, and diagnostics surfaces
|
|
4
|
-
this package adds: one themed startup header
|
|
5
|
-
|
|
6
|
-
that marks each completed turn in the transcript,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
charges.
|
|
4
|
+
this package adds: one themed startup header whose two-tone nameplate carries
|
|
5
|
+
the agent and chrome identities, one compact, colorful, width-adaptive two-line
|
|
6
|
+
footer, a labeled rule that marks each completed turn in the transcript, one
|
|
7
|
+
keyboard-driven screen that lists every resolved Pi extension with its enable
|
|
8
|
+
checkbox and its health, and an opt-in notify capability that raises an OS
|
|
9
|
+
notification when a run finishes, a job finishes, or the agent asks for input.
|
|
10
|
+
DeepSeek is priced from the provider's published yuan rates and peak tariff by
|
|
11
|
+
default, and any other model can be priced from your own rates when Pi's catalog
|
|
12
|
+
does not match what a provider actually charges.
|
|
14
13
|
|
|
15
14
|
```
|
|
16
15
|
pi --extension ./extensions/bytefly # load just this extension
|
|
@@ -21,7 +20,7 @@ pi install ./ # install the package
|
|
|
21
20
|
## What it does
|
|
22
21
|
|
|
23
22
|
The extension owns five surfaces: the startup header, the footer, the rule
|
|
24
|
-
between completed turns, the
|
|
23
|
+
between completed turns, the extension screen, and notifications.
|
|
25
24
|
|
|
26
25
|
**Header.** A nameplate: a two-tone half-block mark from the active theme's
|
|
27
26
|
`accent` / `warning` tokens, the agent identity (`Pi` and its version), the
|
|
@@ -34,11 +33,11 @@ two-tone `Pi` wordmark.
|
|
|
34
33
|
|
|
35
34
|
```
|
|
36
35
|
█▀█ Pi v1.2.0
|
|
37
|
-
█▀ █ enhanced by bytefly v0.
|
|
36
|
+
█▀ █ enhanced by bytefly v0.7.0
|
|
38
37
|
─────────────────────────────────────────────────
|
|
39
|
-
escape to interrupt · ctrl+c to clear · ctrl+d to exit
|
|
40
|
-
ctrl+o
|
|
41
|
-
/ commands · ! bash ·
|
|
38
|
+
escape to interrupt · ctrl+c to clear · ctrl+d to exit (empty)
|
|
39
|
+
ctrl+o to expand tools · ctrl+t to expand thinking · ctrl+l to select model
|
|
40
|
+
/ commands · ! bash · !! bash (no context) · /bytefly extensions
|
|
42
41
|
```
|
|
43
42
|
|
|
44
43
|
As the terminal narrows, the identity sheds the chrome version, then the chrome
|
|
@@ -113,17 +112,14 @@ entry and draws a centered `╌╌╌ turn N ╌╌╌` rule across the transcri
|
|
|
113
112
|
[Turn separator](#turn-separator). No config file is needed and there is no
|
|
114
113
|
command.
|
|
115
114
|
|
|
116
|
-
**
|
|
117
|
-
Pi extension the session resolves — across personal settings
|
|
118
|
-
the project —
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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).
|
|
115
|
+
**Extension screen.** `/bytefly [extension] [refresh]` opens a keyboard-driven
|
|
116
|
+
dock list of every Pi extension the session resolves — across personal settings
|
|
117
|
+
and, when trusted, the project — one row per extension with its enable checkbox
|
|
118
|
+
and its worst-severity health, and writes a `+pattern` / `-pattern` toggle into
|
|
119
|
+
the matching `settings.json`. The focused extension's findings sit underneath,
|
|
120
|
+
and `run_doctor` returns the same report as text and typed data. Only a missing
|
|
121
|
+
*hard* requirement warns at session start; everything else waits in the report.
|
|
122
|
+
See [The bytefly screen](#the-bytefly-screen).
|
|
127
123
|
|
|
128
124
|
**Notify.** Off by default, one `notify` section in `bytefly.json` turns on OS
|
|
129
125
|
notifications for unattended runs: when a full agent run settles, when a
|
|
@@ -136,26 +132,23 @@ the platform notifier (`osascript`, `notify-send`); see
|
|
|
136
132
|
|
|
137
133
|
| Command | What it does |
|
|
138
134
|
|---|---|
|
|
139
|
-
| `/bytefly` |
|
|
140
|
-
| `/
|
|
141
|
-
| `/
|
|
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. |
|
|
135
|
+
| `/bytefly [extension] [refresh]` | Open the extension screen. In `tui` it mounts a docked list; every other mode prints the grouped listing, or one extension's full findings when named. |
|
|
136
|
+
| `/extensions` | Alias for `/bytefly`: the same screen and the same handler. |
|
|
137
|
+
| `/doctor [extension] [refresh]` | Alias for `/bytefly`: the same screen and the same handler. |
|
|
145
138
|
|
|
146
|
-
|
|
139
|
+
Tab completes extension names and `refresh` for all three commands.
|
|
147
140
|
|
|
148
141
|
## Behaviour by mode
|
|
149
142
|
|
|
150
143
|
The header, footer, and turn rule are installed only in interactive (`tui`)
|
|
151
|
-
sessions
|
|
152
|
-
|
|
153
|
-
`/extensions`
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
when there is no UI. Notify runs in
|
|
157
|
-
environment; the OSC methods need a
|
|
158
|
-
OS-level notification or none.
|
|
144
|
+
sessions and are always on; every mode loads the extension, and non-TUI modes
|
|
145
|
+
change nothing. The screen is available in every mode: in `tui`, `/bytefly` and
|
|
146
|
+
its `/extensions` and `/doctor` aliases mount the docked screen, while RPC,
|
|
147
|
+
JSON, and print modes print the grouped listing with a per-row health suffix and
|
|
148
|
+
the headline, or that extension's full findings when one is named. The
|
|
149
|
+
proactive session-start warning is skipped when there is no UI. Notify runs in
|
|
150
|
+
every mode and picks its method from the environment; the OSC methods need a
|
|
151
|
+
TTY, so a print/JSON/RPC run gets an OS-level notification or none.
|
|
159
152
|
|
|
160
153
|
## Configuration
|
|
161
154
|
|
|
@@ -226,8 +219,9 @@ Rates merge key by key across the two documents, so a project document can
|
|
|
226
219
|
override one model without restating the rest; a key present in both is replaced,
|
|
227
220
|
not merged field by field. No windows means every moment is peak, so a rate with
|
|
228
221
|
`offPeakMultiplier: 0.5` and no schedule stays flat. The `price` section is read
|
|
229
|
-
|
|
230
|
-
|
|
222
|
+
once per install — at `session_start`, and again on a `session_tree` change — so
|
|
223
|
+
a new session or tree change picks up an edit; there is no toggle, and nothing
|
|
224
|
+
watches the file.
|
|
231
225
|
|
|
232
226
|
**Mixing currencies is the sharp edge of the built-in default.** A record priced
|
|
233
227
|
from `rates` or the built-in table is in `currency.symbol` units — yuan for
|
|
@@ -301,8 +295,8 @@ new session or `/reload` picks up an edit.
|
|
|
301
295
|
- Project settings are only read or written when the project is trusted
|
|
302
296
|
(`ctx.isProjectTrusted()`); an untrusted session offers Global scope only.
|
|
303
297
|
- Discovery never installs or fetches a package: missing sources are skipped.
|
|
304
|
-
- The
|
|
305
|
-
|
|
298
|
+
- The extension screen registers no model-facing tool; toggling writes only
|
|
299
|
+
through Pi's `settings.json`, and `run_doctor` is read-only.
|
|
306
300
|
|
|
307
301
|
## Cost calculation
|
|
308
302
|
|
|
@@ -394,70 +388,69 @@ it:
|
|
|
394
388
|
- There is no runtime command; the glyph, label prefix, padding, and colors are
|
|
395
389
|
tuned in [`separator/config.ts`](./separator/config.ts).
|
|
396
390
|
|
|
397
|
-
##
|
|
391
|
+
## The bytefly screen
|
|
392
|
+
|
|
393
|
+
`/bytefly [extension] [refresh]` is a keyboard-driven dock list of every
|
|
394
|
+
extension Pi resolves for the session, so the list matches what actually loads.
|
|
395
|
+
It merges the former extensions picker and doctor report into one row per
|
|
396
|
+
extension: the picker's enable checkbox and label, the doctor's worst-severity
|
|
397
|
+
glyph and summary. `/extensions` and `/doctor [extension] [refresh]` are
|
|
398
|
+
registered against the same handler, so all three names open the same screen;
|
|
399
|
+
`[extension]` narrows it to one extension and `refresh` re-probes the doctor
|
|
400
|
+
before it opens. Tab completes extension names and `refresh`.
|
|
398
401
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
`onMissing: "skip"`), so opening the screen never installs or fetches a package.
|
|
402
|
+
The screen reuses Pi's own settings and package resolution (`SettingsManager`
|
|
403
|
+
plus `DefaultPackageManager` with `onMissing: "skip"`), so opening it never
|
|
404
|
+
installs or fetches a package.
|
|
403
405
|
|
|
404
406
|
- Lists every resolved extension, grouped by source (each package, user
|
|
405
407
|
settings, project settings), sorted packages-first and then by display name.
|
|
406
|
-
- Omits `bytefly`
|
|
407
|
-
|
|
408
|
-
|
|
408
|
+
- Omits `bytefly`'s own entrypoint from the picker groups, so the tool that
|
|
409
|
+
manages the list is never a selectable row; its doctor report still appears in
|
|
410
|
+
the trailing `Not resolved` group.
|
|
409
411
|
- Shows whether each one is enabled (`[x]`) or disabled (`[ ]`).
|
|
410
|
-
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
-
|
|
414
|
-
`
|
|
415
|
-
|
|
412
|
+
- Shows the worst severity for a reported extension (`✓` ok, `!` degraded, `✕`
|
|
413
|
+
missing, `∅` disabled) and its summary; an extension with no doctor report
|
|
414
|
+
leaves the glyph column and the summary blank rather than claiming `ok`.
|
|
415
|
+
- Toggles the focused extension with `Space`, writing `+pattern` / `-pattern`
|
|
416
|
+
into `extensions` (and package filter objects) in the active scope.
|
|
417
|
+
- Switches the write scope with `Tab`: **Global** (personal `settings.json`) or
|
|
418
|
+
**Project** (`.pi/settings.json`, only when the project is trusted; an
|
|
419
|
+
untrusted project refuses with a notice).
|
|
420
|
+
- Expands or collapses the focused extension's findings with `Enter`, and
|
|
421
|
+
re-probes with `r`. While a re-probe is in flight the hint shows `refreshing…`
|
|
422
|
+
and a second `r` is ignored, so probes cannot stack.
|
|
416
423
|
- Offers a reload after pending changes, so the new selection takes effect
|
|
417
424
|
without leaving the session.
|
|
418
425
|
|
|
419
|
-
|
|
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`,
|
|
426
|
+
The doctor side probes every declared extension and reports each check as `ok`,
|
|
435
427
|
`missing`, `degraded`, or `info`; `run_doctor` returns the same report as text
|
|
436
428
|
plus a typed `structuredContent` report. Only `missing` and `degraded` are
|
|
437
429
|
problems: `missing` means an enabled extension cannot do its job, `degraded`
|
|
438
430
|
means reduced capability — an optional package that is absent, a config file
|
|
439
|
-
that fails to parse, a login that has not happened.
|
|
431
|
+
that fails to parse, a login that has not happened. Doctor never changes
|
|
440
432
|
anything and never installs anything.
|
|
441
433
|
|
|
442
|
-
`/
|
|
443
|
-
checks that passed
|
|
444
|
-
in the session.
|
|
445
|
-
|
|
446
|
-
|
|
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.
|
|
434
|
+
`/bytefly <extension>` narrows the same screen to that extension, including the
|
|
435
|
+
checks that passed, and `/bytefly refresh` ignores the findings cached earlier
|
|
436
|
+
in the session. Every other mode prints the grouped listing with a health suffix
|
|
437
|
+
on each row and the headline; a named extension prints its full findings
|
|
438
|
+
instead.
|
|
450
439
|
|
|
451
440
|
### Keys
|
|
452
441
|
|
|
453
442
|
| Key | Effect |
|
|
454
443
|
|---|---|
|
|
444
|
+
| `Space` | Toggle the focused extension. |
|
|
445
|
+
| `Enter` | Expand or collapse the focused extension's findings. |
|
|
446
|
+
| `Tab` | Switch between Global and Project scope. |
|
|
447
|
+
| `r` | Re-probe the doctor. |
|
|
455
448
|
| `↑`/`↓`, `j`/`k` | Move the selection. |
|
|
456
449
|
| `PgUp`/`PgDn` | Move a page. |
|
|
457
450
|
| `g` / `G`, `Home` / `End` | Jump to the first or last extension. |
|
|
458
|
-
| `
|
|
459
|
-
|
|
460
|
-
|
|
451
|
+
| `Esc` / `Ctrl+C` | Close the screen. |
|
|
452
|
+
|
|
453
|
+
Mouse wheel scrolling works; every action also has a keyboard path.
|
|
461
454
|
|
|
462
455
|
### What is checked
|
|
463
456
|
|
|
@@ -474,7 +467,7 @@ as text.
|
|
|
474
467
|
| wechat | — | `wechat/credentials.json` (not logged in) |
|
|
475
468
|
| job | — | Windows has no `EXIT` trap; `ps` is POSIX-only |
|
|
476
469
|
| todo, goal | — | their config files |
|
|
477
|
-
| bytefly | — | TUI only for the header, footer, and turn rule; the
|
|
470
|
+
| bytefly | — | TUI only for the header, footer, and turn rule; the screen's project scope needs a trusted project; `bytefly.json` |
|
|
478
471
|
| question | — | needs an interactive UI |
|
|
479
472
|
|
|
480
473
|
Every extension that reads a config file also gets the malformed-JSON check:
|
|
@@ -579,13 +572,14 @@ control-free line before it reaches an escape sequence or a shell argument.
|
|
|
579
572
|
|
|
580
573
|
- No per-segment CLI: the bar's look lives in `footer/config.ts`, the price in
|
|
581
574
|
`bytefly.json`.
|
|
582
|
-
- No per-model cost breakdown
|
|
583
|
-
|
|
575
|
+
- No per-model cost breakdown and no on-screen marker for which tariff or
|
|
576
|
+
which source produced a number.
|
|
584
577
|
- No todo-count segment; the [todo](../todo/) widget already shows progress.
|
|
585
578
|
- No shelling out for git ahead/behind; the footer data provider exposes only
|
|
586
579
|
the branch.
|
|
587
|
-
- No session or model data in the header: it shows
|
|
588
|
-
only. The footer owns the session, model, and working-directory
|
|
580
|
+
- No session or model data in the header: it shows the nameplate and the grouped
|
|
581
|
+
help block only. The footer owns the session, model, and working-directory
|
|
582
|
+
data.
|
|
589
583
|
- No auto-fix: `doctor` reports the install command; it never runs it.
|
|
590
584
|
- No aggregation of runtime failures: a tool that errored earlier in the session
|
|
591
585
|
is not a prerequisite problem, and `doctor` does not pretend to know about it.
|
|
@@ -607,20 +601,18 @@ rather than showing a wrong value.
|
|
|
607
601
|
| Integration point | Details |
|
|
608
602
|
|---|---|
|
|
609
603
|
| 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"
|
|
611
|
-
|
|
|
604
|
+
| Footer | `ctx.ui.setFooter` with a custom component, replacing Pi's built-in footer; installed only when `ctx.mode === "tui"`. |
|
|
605
|
+
| Screen command | `pi.registerCommand("bytefly"`, `"extensions"`, and `"doctor"`) share one handler: `tui` mounts a dock screen via `ctx.ui.custom()` with the rails suppressed, other modes print through `ctx.ui.notify()`. Tab completes extension names and `refresh`. |
|
|
612
606
|
| Turn separator | `registerEntryRenderer("bytefly-separator")` draws each rule; `agent_settled` appends `pi.appendEntry("bytefly-separator", { turn })` in `tui` mode only. |
|
|
613
607
|
| 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
608
|
| Picker discovery | `SettingsManager.create(...)` plus `DefaultPackageManager.resolve({ onMissing: "skip" })`, both public exports; both views drop `bytefly`'s own entrypoint. |
|
|
616
609
|
| Picker writes | `SettingsManager` setters (`setExtensionPaths`, `setPackages`, `setProjectExtensionPaths`, `setProjectPackages`) then `flush()`; `ctx.reload()` after the screen closes when changes are pending. |
|
|
617
610
|
| 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
|
|
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. |
|
|
611
|
+
| Doctor probes | `run_doctor` and the screen share the session cache created by `registerDoctor`; `/bytefly refresh` and `refresh: true` re-probe. Probes spawn `<binary> --version`, lazily import optional packages, and read config files through `lib/requirements.ts`; nothing is ever written. |
|
|
620
612
|
| 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
|
|
613
|
+
| Config | `bytefly.json` from the agent directory and `<cwd>/.pi`; the `price` section is read once per footer install (a new session or a session-tree change), and the `notify` and `doctor` sections at `session_start`. Nothing watches the document. |
|
|
622
614
|
| 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
|
|
615
|
+
| State storage | None besides the inert separator entries. The footer reads session entries and the price config (a memoized scan) and writes no tool-result `details`; it appends `bytefly-separator` entries excluded from LLM context. The screen derives its view from the picker runtime and the current reports 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
616
|
| 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
617
|
|
|
626
618
|
## Design notes
|
|
@@ -700,7 +692,7 @@ rather than showing a wrong value.
|
|
|
700
692
|
- **A record is dated, not the frame.** The peak schedule is evaluated against
|
|
701
693
|
each entry's own timestamp, so a total is stable as the clock crosses a
|
|
702
694
|
boundary — and memoizing the scan by `sessionId:leafId` stays valid.
|
|
703
|
-
- **The
|
|
695
|
+
- **The screen reuses Pi's resolution; only the toggle is ported.** Reading
|
|
704
696
|
`SettingsManager` and `DefaultPackageManager` (public exports) makes the list
|
|
705
697
|
match what Pi loads; the `+`/`-` pattern arithmetic is a trimmed port of Pi's
|
|
706
698
|
config selector, kept pure and separately tested so a future drift is easy to
|
|
@@ -710,6 +702,17 @@ rather than showing a wrong value.
|
|
|
710
702
|
screen shows the view for the active scope and writes through the matching
|
|
711
703
|
store. A project package override is written with `autoload: false`, so it
|
|
712
704
|
filters the personal entry instead of replacing it.
|
|
705
|
+
- **The merge keys on a directory name only when it is unambiguous.** A
|
|
706
|
+
subfolder resource displays as `web-access/index.ts` while its doctor report
|
|
707
|
+
is named `web-access`, so the join starts from `basename(dirname(path))`. A
|
|
708
|
+
name shared by more than one resource — a user `plan` beside the package's
|
|
709
|
+
`plan`, or the `extensions` directory every flat file shares — claims no
|
|
710
|
+
report, so no row is lent another resource's health; the unclaimed report
|
|
711
|
+
appends in the trailing `Not resolved` group with its findings intact. Rows are keyed
|
|
712
|
+
by the unique resource identity (`source:path`, or `report:<name>` for a
|
|
713
|
+
report with no item) so the cursor survives a rebuild even when two rows
|
|
714
|
+
share a directory name, and a row without a report shows no glyph and no
|
|
715
|
+
summary rather than a false `ok`.
|
|
713
716
|
- **Doctor declarations live in `lib/`, probes in the extension, presentation in
|
|
714
717
|
a third layer.** An extension cannot import another extension's modules, so
|
|
715
718
|
the only legal home for a shared prerequisite table is `lib/requirements.ts`;
|
|
@@ -731,16 +734,18 @@ rather than showing a wrong value.
|
|
|
731
734
|
|
|
732
735
|
## Files
|
|
733
736
|
|
|
734
|
-
The
|
|
735
|
-
|
|
737
|
+
The surfaces are folders: `header/`, `footer/` (the status bar and its price
|
|
738
|
+
calculation), `separator/`, `panel/` (the unified screen and its merged model),
|
|
739
|
+
`picker/` (discovery and the settings store), `doctor/` (the `run_doctor` tool
|
|
740
|
+
and its probe runtime), and `notify/`.
|
|
736
741
|
|
|
737
742
|
| File | Responsibility |
|
|
738
743
|
|---|---|
|
|
739
744
|
| `index.ts` | Composition root: the disable guard and the six `register*` calls. |
|
|
740
745
|
| `header/config.ts` | Header tuning (`HEADER_CONFIG`): mark and identity tokens, padding, tagline, and the grouped help entries. |
|
|
741
746
|
| `header/component.ts` | `ByteflyHeader`: the mark, identity rows, rule, and grouped help, plus its factory. |
|
|
742
|
-
| `header/register.ts` | Installs/restores the header
|
|
743
|
-
| `footer/register.ts` | Installs/restores the footer
|
|
747
|
+
| `header/register.ts` | Installs/restores the header and owns its session lifecycle. |
|
|
748
|
+
| `footer/register.ts` | Installs/restores the footer and owns its session lifecycle. |
|
|
744
749
|
| `footer/config.ts` | Bar tuning (`CONFIG`): thresholds, gauge, separators, icons, status keys, labels. |
|
|
745
750
|
| `footer/types.ts` | `StatusSnapshot`, `Segment`, `LineSpec`, `UsageRecord`, and footer/TUI shapes. |
|
|
746
751
|
| `footer/collect/entries.ts` | Session entries → usage records, each resolved to the model in effect. |
|
|
@@ -761,21 +766,22 @@ price calculation), `separator/`, `picker/`, `doctor/`, and `notify/`.
|
|
|
761
766
|
| `separator/component.ts` | `SeparatorComponent`: the stateless entry renderer. |
|
|
762
767
|
| `separator/format.ts` | Pure `separatorLabel` / `separatorParts` / `separatorLine`. |
|
|
763
768
|
| `separator/types.ts` | `TurnSeparatorData`, `SeparatorParts`. |
|
|
769
|
+
| `panel/types.ts` | `PanelRow` and `PanelModel`, the merged row model. |
|
|
770
|
+
| `panel/join.ts` | The item/report union with the unambiguous-name health rule and unique row keys, groups, and ordering. |
|
|
771
|
+
| `panel/format.ts` | Non-TUI text: the headline and the grouped listing with a per-row health suffix. |
|
|
772
|
+
| `panel/tui.ts` | `PanelComponent`, the unified dock screen. |
|
|
773
|
+
| `panel/commands.ts` | `/bytefly`, `/extensions`, and `/doctor`: one runner for the screen and its text modes. |
|
|
764
774
|
| `picker/types.ts` | Picker host seams (`SettingsStore`, `ExtensionResolver`, `Discovery`) and row types. |
|
|
765
|
-
| `picker/resources.ts` | Pure grouping, labels, display names, pattern helpers
|
|
775
|
+
| `picker/resources.ts` | Pure grouping, labels, display names, and pattern helpers. |
|
|
766
776
|
| `picker/toggle.ts` | Pure next-settings transforms for global and project toggles. |
|
|
767
777
|
| `picker/discovery.ts` | Host wiring: builds the global and effective views from Pi's managers. |
|
|
768
778
|
| `picker/runtime.ts` | Picker scope, live view copies, dirty tracking, and persistence. |
|
|
769
|
-
| `
|
|
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. |
|
|
779
|
+
| `doctor/register.ts` | Registers `run_doctor` and the proactive session-start check; returns the runtime. |
|
|
772
780
|
| `doctor/runtime.ts` | Host seams, the session cache, and the two probe modes. |
|
|
773
781
|
| `doctor/tools.ts` | The `run_doctor` tool and its renderers. |
|
|
774
|
-
| `doctor/commands.ts` | The `/doctor` command and its TUI wiring. |
|
|
775
782
|
| `doctor/schema.ts` | Tool parameters and the structured output schema. |
|
|
776
783
|
| `doctor/config.ts` | `DEFAULT_CONFIG`, `normalizeConfig`, `loadConfig` for the `doctor` section of `bytefly.json`. |
|
|
777
|
-
| `doctor/format.ts` | Pure report text,
|
|
778
|
-
| `doctor/tui.ts` | `DoctorScreen`, the docked list screen with its detail pane. |
|
|
784
|
+
| `doctor/format.ts` | Pure report text, summary, glyphs, detail lines, and the proactive warning sentence. |
|
|
779
785
|
| `notify/types.ts` | `NotifyConfig`, `NotifyMethod`, and the method list. |
|
|
780
786
|
| `notify/config.ts` | The `notify` section of `bytefly.json`: defaults, validation, loader. |
|
|
781
787
|
| `notify/dispatch.ts` | Method selection and delivery: OSC 777/99, Windows toast, `osascript`, `notify-send`, over injected seams. |
|
|
@@ -805,16 +811,17 @@ golden output at fixed widths (`footer/render/render`), and the install/skip/
|
|
|
805
811
|
restore/toggle lifecycle plus a config-driven price (`extension`). The turn
|
|
806
812
|
separator
|
|
807
813
|
is covered by its pure formatting (`separator/format`) and its
|
|
808
|
-
register/append/render/disable lifecycle (`separator/register`). The picker
|
|
809
|
-
covered by the pure helpers (`picker/resources`), the toggle
|
|
810
|
-
(`picker/toggle`), discovery with an injected factory
|
|
811
|
-
runtime state machine (`picker/runtime`)
|
|
812
|
-
|
|
813
|
-
(`
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
814
|
+
register/append/render/disable lifecycle (`separator/register`). The picker
|
|
815
|
+
helpers are covered by the pure helpers (`picker/resources`), the toggle
|
|
816
|
+
transforms (`picker/toggle`), discovery with an injected factory
|
|
817
|
+
(`picker/discovery`), and the runtime state machine (`picker/runtime`). The
|
|
818
|
+
unified screen is covered by its pure model merge (`panel/join`), its text
|
|
819
|
+
formatter (`panel/format`), the screen's rendering and key handling
|
|
820
|
+
(`panel/tui`), and the command through the bytefly factory (`panel/commands`).
|
|
821
|
+
The doctor surface is covered by its pure report
|
|
822
|
+
formatting (`doctor/format`), its config loader (`doctor/config`), and
|
|
823
|
+
registration, the proactive warning, the session cache, and `run_doctor`
|
|
824
|
+
through the factory (`doctor/register`). The notify capability is covered by its section loader
|
|
818
825
|
(`notify/config`), the pure text builders (`notify/format`), method selection and
|
|
819
826
|
delivery over recording seams (`notify/dispatch`), and its three triggers,
|
|
820
827
|
reload, and shutdown through `registerNotify` (`notify/register`).
|
|
@@ -2,16 +2,15 @@
|
|
|
2
2
|
* Pure formatting for doctor's reports.
|
|
3
3
|
*
|
|
4
4
|
* Two shapes share these helpers: the plain multi-line text every non-TUI mode
|
|
5
|
-
* shows, and the
|
|
5
|
+
* shows, and the per-extension summary the unified panel renders. Nothing here
|
|
6
6
|
* touches the filesystem or the host, and every string that came from a probe (a
|
|
7
7
|
* parse error, a captured path) goes through {@link sanitizeLine} — probe text can carry
|
|
8
8
|
* control characters, a parse error quotes the file's own bytes.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
12
|
-
import {
|
|
12
|
+
import { wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
13
13
|
import { sanitizeLine } from "../../../lib/format.ts";
|
|
14
|
-
import { selectionMarker } from "../../../lib/list-cursor.ts";
|
|
15
14
|
import type { ExtensionReport, Finding, FindingSeverity } from "../../../lib/requirements.ts";
|
|
16
15
|
import { GLYPHS } from "../../../lib/ui.ts";
|
|
17
16
|
|
|
@@ -123,7 +122,7 @@ export function reportSeverity(report: ExtensionReport): FindingSeverity {
|
|
|
123
122
|
}
|
|
124
123
|
|
|
125
124
|
/** A short right-hand status for one extension row. */
|
|
126
|
-
function reportSummary(report: ExtensionReport): string {
|
|
125
|
+
export function reportSummary(report: ExtensionReport): string {
|
|
127
126
|
if (!report.enabled) return "disabled";
|
|
128
127
|
const missing = report.findings.filter((finding) => finding.severity === "missing").length;
|
|
129
128
|
const degraded = report.findings.filter((finding) => finding.severity === "degraded").length;
|
|
@@ -132,24 +131,6 @@ function reportSummary(report: ExtensionReport): string {
|
|
|
132
131
|
return "ok";
|
|
133
132
|
}
|
|
134
133
|
|
|
135
|
-
/** One extension row: marker, severity glyph, name, and a summary. */
|
|
136
|
-
export function extensionRow(report: ExtensionReport, theme: Theme, width: number, selected = false): string {
|
|
137
|
-
const severity = reportSeverity(report);
|
|
138
|
-
const glyph = severityGlyph(severity, theme);
|
|
139
|
-
const head = `${selectionMarker(theme, selected)}${glyph} `;
|
|
140
|
-
const summary = reportSummary(report);
|
|
141
|
-
const available = Math.max(1, width - visibleWidth(head) - visibleWidth(summary) - 1);
|
|
142
|
-
const name = truncateToWidth(sanitizeLine(report.extension), available, "…");
|
|
143
|
-
const pad = " ".repeat(Math.max(0, available - visibleWidth(name)));
|
|
144
|
-
const tail =
|
|
145
|
-
severity === "missing"
|
|
146
|
-
? theme.fg("error", summary)
|
|
147
|
-
: severity === "degraded"
|
|
148
|
-
? theme.fg("warning", summary)
|
|
149
|
-
: theme.fg("dim", summary);
|
|
150
|
-
return truncateToWidth(`${head}${name}${pad} ${tail}`, width);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
134
|
/**
|
|
154
135
|
* The focused extension's findings, wrapped to `width` and capped at `rows`
|
|
155
136
|
* lines. Problems come first: `includeInfo` is the screen's expand toggle, and
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* bytefly's doctor surface: the `run_doctor` tool
|
|
3
|
-
*
|
|
2
|
+
* bytefly's doctor surface: the `run_doctor` tool and the proactive
|
|
3
|
+
* session-start check.
|
|
4
4
|
*
|
|
5
5
|
* An extension that is enabled but inert is the failure this exists to prevent:
|
|
6
6
|
* a missing extractor CLI, an optional package that was never installed, a
|
|
7
7
|
* config file with a typo, or a runtime the extension needs but cannot reach.
|
|
8
8
|
* `doctor` knows the answer because each extension declares its prerequisites in
|
|
9
|
-
* `lib/requirements.ts`, and the same probes feed
|
|
9
|
+
* `lib/requirements.ts`, and the same probes feed the unified panel and
|
|
10
|
+
* `run_doctor`.
|
|
10
11
|
*
|
|
11
12
|
* This module registers itself from bytefly's factory; it has no env guard of
|
|
12
|
-
* its own because `PI_DISABLED_EXTENSIONS=bytefly` is what disables it.
|
|
13
|
+
* its own because `PI_DISABLED_EXTENSIONS=bytefly` is what disables it. It
|
|
14
|
+
* creates the session-scoped runtime and returns it so the composition root can
|
|
15
|
+
* hand the same cache to the panel.
|
|
13
16
|
*/
|
|
14
17
|
|
|
15
18
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
16
19
|
import { missingFindings } from "../../../lib/requirements.ts";
|
|
17
|
-
import { registerCommands } from "./commands.ts";
|
|
18
20
|
import { loadConfig } from "./config.ts";
|
|
19
21
|
import { formatWarning } from "./format.ts";
|
|
20
22
|
import { createDoctorRuntime, type DoctorRuntime } from "./runtime.ts";
|
|
@@ -28,14 +30,14 @@ export interface DoctorDeps {
|
|
|
28
30
|
/**
|
|
29
31
|
* Register the doctor surface and return its runtime.
|
|
30
32
|
*
|
|
31
|
-
* Registers the read-only tool
|
|
32
|
-
*
|
|
33
|
+
* Registers the read-only tool, points the runtime at each session, and drops
|
|
34
|
+
* the cached findings on shutdown. `/bytefly` and its aliases are registered by
|
|
35
|
+
* the panel, which shares this runtime.
|
|
33
36
|
*/
|
|
34
37
|
export function registerDoctor(pi: ExtensionAPI, deps: DoctorDeps = {}): DoctorRuntime {
|
|
35
38
|
const runtime = deps.runtime ?? createDoctorRuntime(pi);
|
|
36
39
|
|
|
37
40
|
registerTools(pi, runtime);
|
|
38
|
-
registerCommands(pi, runtime);
|
|
39
41
|
|
|
40
42
|
pi.on("session_start", async (_event, ctx) => {
|
|
41
43
|
const config = loadConfig(ctx.cwd);
|