pi-focus-mode 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/LICENSE +19 -0
  2. package/README.md +203 -0
  3. package/focus-mode.js +2097 -0
  4. package/package.json +38 -0
package/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ezoushen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE.
package/README.md ADDED
@@ -0,0 +1,203 @@
1
+ # pi-focus-mode
2
+
3
+ Show timing, token, cache, cost, and tool-use statistics for each Pi exchange.
4
+ Each run of thinking and tool calls between pieces of assistant text starts folded
5
+ as one process line, even when it spans assistant messages or turns. The line shows
6
+ thinking and tool counts as `◈ n` and `⚙ m`, plus total wall time, or the current
7
+ block and its live time while streaming. In the running exchange, a tool call that
8
+ has streamed but not yet started shows as `⚙ <tool> queued`. A call whose exchange
9
+ settled, or was restored, without a result shows `no result` in its title and no
10
+ live activity. At the next fold level, each block has a one-line title in stream order, with dim `├` and `└` guides under its process line and `│` continuing through rows with later siblings.
11
+ While an exchange streams, assistant text stays visible. After it settles, an
12
+ exchange with a process and a trailing answer folds its processes and earlier
13
+ assistant text into one dim progress line at the first progress item. Opening that
14
+ line shows the process lines and earlier text beneath it, grouped by dim tree guides;
15
+ the trailing answer stays visible without a guide. Its `1 note` or `N notes` segment
16
+ counts interim text items and is omitted when there are none. An exchange without a
17
+ process or a trailing answer keeps its existing rows.
18
+ Tool titles show the argument, status, duration, and result line count. Thinking
19
+ titles show the trace's last complete sentence, or `Thinking` until one completes.
20
+ With `summaryModel` configured, a short model headline takes its place when ready;
21
+ the leading `≈` marks it as a summary. A failed or timed-out request returns to
22
+ the sentence fallback, on screen and in the saved entry, even after an earlier
23
+ model headline; it gives one warning per session.
24
+ The headline is shortened first to make room for duration and stats.
25
+ Titles also show elapsed time, thinking tokens and rate while streaming; a `~` marks
26
+ token estimates when the provider has not reported reasoning usage. Settled titles
27
+ show duration and word count. Opening a block keeps its title and shows Pi's native
28
+ output below it, two columns beneath its tree guide; a thinking block shows Pi's full trace
29
+ there, regardless of Pi's hide-thinking setting. Process lines, settled progress lines,
30
+ and block titles use the active Pi theme's dim color and italic style, except the line
31
+ selected by the transcript cursor, which uses the theme's accent color and stays
32
+ italic. Picker text and exchange card rows use the same dim italic style; the picker
33
+ border stays upright. Visible assistant text and opened native content keep Pi's own
34
+ styling.
35
+
36
+ Press `ctrl+alt+f` to toggle the latest process between its line and block titles.
37
+ Press `ctrl+alt+e` to toggle the latest exchange's progress line after it settles;
38
+ while it streams, the shortcut opens or folds all its processes. Press
39
+ `ctrl+alt+s` to open the picker; use Up/Down and Enter to toggle an exchange,
40
+ process, or block, and Escape to close it. The exchange row toggles settled progress
41
+ or the streaming processes. The picker marks open items; opening a block also opens
42
+ its process, while folding that block leaves the process open. These controls change
43
+ only the display. In fullscreen mode, left click a process line, progress line, or
44
+ block title to toggle it. Option+left-click a progress or process line toggles that
45
+ row and its direct children; on a block title it behaves like a plain click. A terminal
46
+ that swallows Option+click, for example to move the cursor, sends a plain click.
47
+ Hovering one of these rows brightens its text and puts it
48
+ on the theme's selection background, from the title after its tree guide to the right edge;
49
+ opened native output is not highlighted. The highlight follows the pointer across fold rows,
50
+ assistant text and the exchange card; it can stay on a row after the pointer
51
+ moves straight to Pi's own rows (a user message, the editor), until the pointer
52
+ next crosses the transcript. Terminal multiplexers that Pi runs in button-motion
53
+ mode (tmux, screen, zellij) send no hover events, so there is no highlight there.
54
+
55
+ An experimental transcript cursor is available with `cursorMode: true`. Press
56
+ `ctrl+alt+g` to enter it, Up/Down to move through progress and process lines and the
57
+ block titles of open processes, Enter to toggle the highlighted item, and Escape to
58
+ return to the editor. The selected title also appears in the status line, since
59
+ regular terminal scrollback can leave the highlighted line above the viewport.
60
+ Fullscreen Pi exposes scrolling by line, but its extension UI does not expose the
61
+ selected transcript row's position; the cursor therefore cannot scroll to it.
62
+
63
+ An **exchange** is one uninterrupted work span from a submitted prompt until Pi has
64
+ nothing left to do automatically. A **turn** is one model response plus the tools it
65
+ invokes, so an exchange can contain several turns. The status line shows the current
66
+ or most recent exchange. Exchange and session cards are summary-only and show one
67
+ line when their content fits; at narrower widths Pi wraps the text without
68
+ truncating its numbers. The exchange line contains its duration, finish time when
69
+ the record has `endedAt`, and model, separated by `·`; its metrics follow the model
70
+ in parentheses after one space, separated by `·`. It does not include an exchange
71
+ number. The finish time uses the runtime default locale and local time zone in
72
+ 24-hour `HH:MM:SS` style, with the date when that local date is not today. Records
73
+ saved by version 0.1.0 without `endedAt` omit the finish time. The session line keeps
74
+ its turn and exchange counts and follows the same model and metrics layout. Metrics
75
+ show input and output tokens, cache reads as `cache R` with `/ W written` when cache writes are
76
+ nonzero (or `cache W written` when there are writes but no reads), waiting time only
77
+ when nonzero, and total cost at the end even when it is `$0`. Cards omit turns,
78
+ prompts, tools, thinking, and total-token details. Block titles carry individual
79
+ timing and count details.
80
+
81
+ Tool time is the union of tool spans, not their sum, so parallel calls are not counted
82
+ twice. Model time is estimated as turn wall time minus tool time. Output throughput is
83
+ calculated per turn, while cumulative cost and token fields use Pi's reported usage.
84
+ Run `/exstats` to append a cumulative session card. Exchange entries retain block
85
+ durations, status, counts, and headlines outside model context. A model headline
86
+ that arrives after its exchange settles is saved in a separate, unrendered
87
+ `exchange-stats-headline` entry holding only the block id, headline and source, and
88
+ restore applies the latest one. On resume, reload,
89
+ or a branch switch, the extension rebuilds fold state from the active session
90
+ branch without sending historical thinking to the headline model. Older entries
91
+ without block statistics still show their card; their thinking titles use `—` for
92
+ unknown duration.
93
+
94
+ ## External contract
95
+
96
+ Pi must emit its documented session, agent, turn, tool, and UI-prompt lifecycle events.
97
+ Display wrappers are installed at `session_start` and released at `session_shutdown`;
98
+ loading the extension without a session leaves Pi's component prototypes unchanged.
99
+ Overlapping extension instances share the wrappers until the last session releases them.
100
+ A tool row renders with the session that received its tool call id. An assistant
101
+ message renders with the session that received a message with its timestamp; when
102
+ several sessions did, with the one whose received thinking or tool calls match the
103
+ message. Sessions whose messages share a timestamp and identical content, or a
104
+ component built before any session received its message, use the latest session.
105
+ Tool folding uses Pi's `ToolExecutionComponent.render` interface from version 0.87.1.
106
+ Thinking folding uses `AssistantMessageComponent.updateContent` and Pi's
107
+ `message_update` events. Its clock starts at the first thinking delta and stops
108
+ at `thinking_end`, the first following non-thinking event, or message end,
109
+ whichever comes first. Streamed snapshots of one assistant message must retain
110
+ the same Pi message timestamp for the local clock to follow them.
111
+ Assistant messages should include usage and cost fields when the provider supports
112
+ them. Model headlines use Pi's configured provider; no separate endpoint is needed.
113
+
114
+ ## If the contract is unmet
115
+
116
+ Missing usage fields are reported as zero for exchange totals; the extension does
117
+ not invent exchange token or cost data. Thinking estimates are explicitly marked
118
+ with `~`.
119
+ Missing lifecycle events produce an incomplete or absent span. When UI status is
120
+ unavailable, status updates are skipped. If custom-entry persistence is unavailable,
121
+ the live status can still update and the agent turn continues. If Pi no longer
122
+ provides the tool render interface, a single warning is shown and tool rows use
123
+ Pi's native renderer; exchange stats continue.
124
+ If Pi no longer provides the assistant content interface, a single warning is
125
+ shown and thinking uses Pi's native rendering.
126
+
127
+ The timings are measured locally, then saved in exchange entries. They do not
128
+ claim provider-side queue time, exclusive model compute time, or billing beyond
129
+ the usage object Pi received.
130
+
131
+ ## Compatibility
132
+
133
+ Uninstall `pi-exchange-stats` before installing `pi-focus-mode`; both packages
134
+ must not be loaded together. The published `pi-exchange-stats@0.1.0` had no
135
+ settings or environment variables, so no settings migration is needed. Existing
136
+ sessions remain readable through their `exchange-stats` and
137
+ `exchange-stats-headline` entries. The `/exstats` command and `exchange` status
138
+ key stay unchanged.
139
+
140
+ ## Settings
141
+
142
+ The optional `focus-mode.json` file in Pi's agent directory configures
143
+ `processKey`, `exchangeKey`, and `pickerKey`. Their defaults are `ctrl+alt+f`,
144
+ `ctrl+alt+e`, and `ctrl+alt+s`. The experimental `cursorKey` defaults to
145
+ `ctrl+alt+g`. Each key value must be a modified Pi key such as
146
+ `ctrl+alt+x` or `alt+enter`; an invalid value uses its default and produces one warning.
147
+ The matching environment variables are `PI_FOCUS_MODE_PROCESS_KEY`,
148
+ `PI_FOCUS_MODE_EXCHANGE_KEY`, `PI_FOCUS_MODE_PICKER_KEY`, and
149
+ `PI_FOCUS_MODE_CURSOR_KEY`. Set `cursorMode` to the boolean `true` in the same
150
+ file, or set `PI_FOCUS_MODE_CURSOR_MODE=true`, to register the cursor shortcut.
151
+ It is off by default. A project's `.pi/focus-mode.json` is not read for the keys
152
+ or `cursorMode`, even in a trusted project.
153
+
154
+ Set `summaryModel` to a `provider/modelId` registered in Pi's model registry.
155
+ It is off by default. Put it in `<agentDir>/focus-mode.json`, or in a trusted
156
+ project's `.pi/focus-mode.json`. For example:
157
+
158
+ ```json
159
+ { "summaryModel": "my-provider/my-headline-model" }
160
+ ```
161
+
162
+ `PI_FOCUS_MODE_SUMMARY_MODEL` overrides the file setting. While thinking
163
+ streams, the extension sends at most the last 1,500 trace characters after about
164
+ 400 new tokens or six seconds, and once more when the block ends. Requests use
165
+ reasoning off and a 32 token output cap. The model call never delays the turn or
166
+ the trace; with the setting absent or an unknown model, titles use the trace
167
+ sentence.
168
+
169
+ The summary model must honor Pi's reasoning `off` level. Set
170
+ `thinkingLevelMap.off` in that model's Pi `models.json` entry to the provider's
171
+ own value for disabling thinking. For a provider that uses `none`, for example:
172
+
173
+ ```json
174
+ { "thinkingLevelMap": { "off": "none" } }
175
+ ```
176
+
177
+ Without it, a model may spend the 32 token cap reasoning and return no headline.
178
+
179
+ For a development install, do not load this package from a path whose
180
+ `node_modules` contains its own `@earendil-works/pi-*` packages. That path makes
181
+ the display patch land on those private component classes rather than the classes
182
+ used by the running Pi. Stage the built package files outside such a tree, or
183
+ `npm pack` this package and install the tarball with
184
+ `pi install npm:pi-focus-mode@file:/absolute/path/to/pi-focus-mode-0.1.0.tgz`.
185
+
186
+ ## Install and verify
187
+
188
+ ```sh
189
+ pi install npm:pi-focus-mode
190
+ ```
191
+
192
+ Submit a prompt that makes at least one tool call and produces thinking. While it
193
+ streams, check that process lines update and assistant text remains visible. After Pi
194
+ settles, check that one progress line covers the work before the final answer; open it
195
+ to see process lines and interim text grouped beneath it with tree guides, while the
196
+ final answer stays unmarked. Check that exchanges without a process or a trailing
197
+ answer stay unfolded. Check that the exchange card uses one line when it fits and
198
+ wraps without truncating numbers at narrower widths, with metrics in parentheses,
199
+ cost last and no expanded detail. Open a process to see individual block timings
200
+ and counts. Run `/exstats` and confirm the session card keeps its turn and exchange
201
+ counts, with metrics in parentheses and following the same metrics rules. To check
202
+ parallel-tool accounting, run two overlapping tools and confirm their union is not
203
+ larger than the exchange wall time.