pi-better-btw-plus 1.0.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 ADDED
@@ -0,0 +1,23 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nico Bailon
4
+ Copyright (c) 2026 yceachan
5
+ Copyright (c) 2026 hu3rror
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,251 @@
1
+ <p>
2
+ <img src="https://raw.githubusercontent.com/hu3rror/pi-better-btw-plus/main/banner.png" alt="pi-better-btw-plus" width="1100">
3
+ </p>
4
+
5
+ # pi-better-btw-plus
6
+
7
+ **English | [简体中文](README.zh-CN.md)**
8
+
9
+ > [!note]
10
+ >
11
+ > This package is a **maintained fork** of [`@yceachan/pi-better-btw`](https://www.npmjs.com/package/@yceachan/pi-better-btw) — itself a fork of [nicobailon/pi-side-chat](https://github.com/nicobailon/pi-side-chat) — maintained by **hu3rror** at [hu3rror/pi-better-btw-plus](https://github.com/hu3rror/pi-better-btw-plus). Attribution: original author **Nico Bailon** → extended by **yceachan** → this fork.
12
+
13
+ ## TL;DR
14
+
15
+ **Fork the current conversation into a side chat (`btw`) while the main agent keeps working.**
16
+
17
+ [![npm version](https://img.shields.io/npm/v/pi-better-btw-plus?style=for-the-badge)](https://www.npmjs.com/package/pi-better-btw-plus)
18
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=for-the-badge)](LICENSE)
19
+
20
+ ```bash
21
+ pi install npm:pi-better-btw-plus
22
+ # in pi tui
23
+ > /btw || or Alt+W
24
+ ```
25
+
26
+ You're in the middle of a longer task and want to ask something small without derailing the main thread — check an API detail, sanity-check an approach, search something, or peek at what the main agent is doing. Open the btw TUI overlay, ask, close it. The main thread never gets interrupted.
27
+
28
+ <img src="https://raw.githubusercontent.com/hu3rror/pi-better-btw-plus/main/docs/overlay.png" alt="pi-better-btw-plus overlay" />
29
+
30
+ ## What's New in This Fork
31
+
32
+ Everything in [`@yceachan/pi-better-btw`](https://www.npmjs.com/package/@yceachan/pi-better-btw) is here, plus:
33
+
34
+ - **Right-click copy & paste** — drag-select chat text and right-click to copy (Windows Terminal muscle memory); right-click inside the input editor pastes the system clipboard through the editor's own normalization and `[paste #N …]` markers for large pastes. No more hotkey-only copying.
35
+ - **Fork model switching (`Alt+M`)** — pick any authenticated model for the side chat without rebuilding the fork; fork-local only (ADR 0002), thinking level auto-clamped to the new model's capabilities.
36
+ - **Turn-level auto-retry** — shares the main session's `settings.retry` budget: transient provider errors back off and retry with a live countdown; `Esc` cancels.
37
+ - **Feature kill switches** — the layered config's `features` block turns any of the above off (`rightClickCopyPaste` / `modelSwitch` / `retry`), plus `readOnlyExtensionAllowlistExclude` to drop bundled allowlist defaults.
38
+
39
+ See [Feat](#feat) for the full feature set.
40
+
41
+ ## Feat
42
+
43
+ > [!note]
44
+ >
45
+ > **That's Why It's Called Better-Btw**
46
+ >
47
+ > The author tried [nicobailon/pi-side-chat](https://github.com/nicobailon/pi-side-chat) and [dbachelder/pi-btw](https://github.com/dbachelder/pi-btw) — both are simple forks from the main lane: when the agent is on turn, both tend to try to advance the main line. See [feat request: btw aside-session self-cognition — the side chat must not continue the main session's work · Issue #5 · nicobailon/pi-side-chat](https://github.com/nicobailon/pi-side-chat/issues/5).
48
+ >
49
+ > Hence the following features were carefully developed:
50
+
51
+ - `Aside-Agent self-Cognition`: injects the main-lane context so you can ask a btw question about the project's main line. Careful context engineering strengthens the side agent's cognition and keeps the full main-lane context's tool-call traces from polluting it or competing with the main lane to advance the project. The shared main-lane prefix is preserved for good cache hits.
52
+
53
+ - `Prompt pack`: every injected prompt is documented, with three-level overrides — `bundle` / `$PI_HOME` / `$CWD`.
54
+
55
+ - `TUI scroll, select, copy`: subscribes to mouse/hotkey events in the TUI overlay for scrolling, text selection, and `Ctrl+C` copy.
56
+
57
+ - `Readonly/Edit Mode`: read-only by default to answer btw questions; if you want the agent to make small edits along the way, `Ctrl+T` switches to edit mode.
58
+
59
+ - ToolAllowList: bundle + config.json custom
60
+
61
+ | Mode | Tools |
62
+ | ---- | ---- |
63
+ | Read-only | `read`, `grep`, `find`, `ls`; `peek_main`; `config.json.readOnlyExtensionAllowlist` |
64
+ | Edit | `read`, `bash`, `edit`, `write` |
65
+
66
+ ## Usage
67
+
68
+ Open the side chat with `/btw` (alias `/side`) or `Alt+W` (which also toggles background/display). Ask a question and press `Enter`.
69
+
70
+ Press `Esc` to close it. Reopen with `/btw` or `Alt+W` to continue where you left off.
71
+
72
+ | Shortcut | Action |
73
+ | -------- | ------ |
74
+ | `Alt+W` | Open (when closed) / background (when visible) / restore (when hidden) |
75
+ | `Ctrl+T` | Toggle read-only / edit mode |
76
+ | `Alt+R` | Re-fork from the latest main context |
77
+ | `Alt+N` | Start an empty conversation |
78
+ | `Alt+E` | Export the transcript to `$CWD/.agents/eval/pi-better-btw-<timestamp>.md` |
79
+ | `Alt+M` | Open the fork model picker (scoped + authenticated models; `↑/↓` select, `Enter` confirm, `Esc` cancel) |
80
+
81
+ In Read-only Mode (default), the read-only lane is **enforced**: attempting an out-of-lane tool call is hard-blocked with a prompt injection; a second violation escalates the wording and aborts the turn (a `🚧 lane blocked` status line). Executed-but-failed read-only calls are re-grounded by an `afterToolCall` note. Edit mode (`Ctrl+T`) is unaffected.
82
+
83
+ **Peek at the main agent** — the `peek_main` tool reads recent activity from the main session.
84
+
85
+ ```text
86
+ What is the main agent doing right now?
87
+ What changed since I opened this side chat?
88
+ ```
89
+
90
+ **Non-capturing overlay + backgrounding** — the overlay opens at the top of the screen so the main editor stays visible underneath. It stays focused while open; `Alt+W` backgrounds it (hidden, the agent keeps streaming) to hand the keyboard back, and `Alt+W` restores it.
91
+
92
+ **Taller chat area** — the message area is ~2.5x taller than upstream, so long answers and tool output stay readable; it adapts to small terminals (never overflows, always leaves the main editor visible).
93
+
94
+ **Scroll the history** — `PgUp`/`PgDn` scroll by a page, `Shift+↑`/`Shift+↓` by a few lines, and the mouse wheel scrolls when the pointer is over the chat. When scrolled away from the latest message, a `[↑N]` indicator appears in the header and the hint bar switches to `↑N · PgDn/Wheel ↓`. While streaming, the viewport follows the bottom until you scroll away, then freezes content-anchored (new lines grow the scroll offset instead of sliding the visible content); it resumes following once you're back at the bottom or a new message arrives.
95
+
96
+ **Mouse select + right-click copy** — drag to select chat text (inverse-video highlight); double-click selects the whole rendered line. Copy the retained selection with `Ctrl+C` / `Ctrl+Shift+C` **or right-click on the chat area** (Windows Terminal muscle memory): the right-click fires on release, needs an active selection, and keeps the highlight so repeated right-clicks re-copy. Copies go through the native clipboard cascade (`wl-copy`/`xclip`, OSC 52 fallback); dragging never touches the clipboard, so mouse interaction stays off the event loop. Mouse reporting follows overlay *visibility* — backgrounding the chat releases the terminal's native selection.
97
+
98
+ **Right-click paste in the input box** — right-click inside the editor pastes the system clipboard at the cursor through the editor's built-in paste entry: line endings/tabs are normalized (`\r`→`\n`, `\t`→4 spaces), large pastes (>10 lines or >1000 chars) collapse to a `[paste #N +X lines]` / `[paste #N X chars]` marker that expands back to full text on submit, and the paste is a single undo step. When the clipboard can't be read (platform channel + OSC 52 fallback both unavailable) or holds no text, a one-line hint appears and the editor is left untouched.
99
+
100
+ **Transcript export** — `Alt+E` dumps the btw history (forked context, framing block, conversation, in-flight stream) to `$CWD/.agents/eval/pi-better-btw-<timestamp>.md` as a markdown diagnostic artifact, useful for debugging feature work.
101
+ **Fork model switching** — `Alt+M` opens a model picker inside the overlay (`↑/↓` move, `Enter` confirm, `Esc` cancel). It lists the session's scoped models first (`--models` / `enabledModels`), falling back to the available catalogue, and only shows models with configured auth. Confirming swaps the fork agent's runtime model — the next turn uses it without rebuilding the fork — and clamps the thinking level to the new model's capabilities (no-reasoning models go to `off`). The choice is fork-local (ADR 0002): the main session's model is never touched. It survives backgrounding (`Alt+W`) and resets on `Alt+R`/`Alt+N`/`Esc` close. The header shows the current fork model; opening is rejected while streaming.
102
+ **Auto-retry (turn-level)** — reads pi's `settings.retry` budget (`enabled` / `maxRetries` / `baseDelayMs`, same defaults as the main session). Transient provider errors (overloaded / rate limit / 5xx) auto-retry with exponential backoff — the status area shows `Retrying (n/m) in Xs…` with a live countdown — and the failed assistant message is stripped before the retry so it never re-enters the next request. Context overflow and aborts never retry. `Esc` during the backoff cancels the wait and surfaces the last error as the final result; budget exhaustion does the same. With `enabled: false` (e.g. local-model debugging) errors surface immediately with zero overhead.
103
+
104
+
105
+ ## Shortcuts
106
+
107
+ | Key | Action |
108
+ | ---- | ---- |
109
+ | `Alt+W` | Open (when closed) / background (when visible) / restore (when hidden) |
110
+ | `Enter` | Send message |
111
+ | `Esc` | Interrupt streaming / cancel the retry backoff, or close when idle |
112
+ | `Alt+R` | Re-fork from latest main context |
113
+ | `Alt+N` | Start empty conversation |
114
+ | `Alt+E` | Export the btw chat history to `$CWD/.agents/eval/pi-better-btw-<timestamp>.md` |
115
+ | `Alt+M` | Open the fork model picker (`↑/↓` select, `Enter` confirm, `Esc` cancel) |
116
+ | `Ctrl+T` | Toggle read-only / edit mode |
117
+ | `PgUp` / `PgDn` | Scroll history by a page |
118
+ | `Shift+↑` / `Shift+↓` | Scroll by a few lines |
119
+ | Mouse wheel | Scroll when the pointer is over the chat |
120
+ | Mouse drag | Select text in the chat area (inverse-video highlight); no copy on release |
121
+ | Double-click | Select the whole rendered line |
122
+ | `Ctrl+C` / `Ctrl+Shift+C` | Copy the active mouse selection (the selection is kept until you click elsewhere, so repeated presses re-copy) |
123
+ | Mouse right-click (chat area) | Copy the retained mouse selection (fires on release, keeps the highlight) |
124
+ | Mouse right-click (input editor) | Paste the system clipboard at the cursor (editor normalization + `[paste #N …]` markers for large pastes) |
125
+ ## Command Reference
126
+
127
+ ### `/btw`
128
+
129
+ Opens the side chat overlay. Alias for `/side`.
130
+
131
+ ### `/side`
132
+
133
+ Opens the side chat overlay (upstream name kept as a compatibility alias).
134
+
135
+ ### `peek_main`
136
+
137
+ Available to the side agent only.
138
+
139
+ | Param | Type | Description |
140
+ |-------|------|-------------|
141
+ | `lines` | integer | Max items to inspect (default: 20, max: 50) |
142
+ | `since_fork` | boolean | Only show activity after the side chat was opened |
143
+
144
+ ## Configuration
145
+
146
+ pi-better-btw reads `config.json` from three locations, layered in increasing precedence — a layer only overrides the keys it actually defines:
147
+
148
+ | Layer | Location |
149
+ |---|---|
150
+ | Bundle (defaults) | `config.json` next to the extension — git-tracked, ships with the published package |
151
+ | User | `~/.pi/agent/pi-better-btw/config.json` |
152
+ | Project | `<project>/.pi/pi-better-btw/config.json` |
153
+
154
+ Keys:
155
+
156
+ - `readOnlyExtensionAllowlist` — extension tool names allowed in the read-only lane (the lane always includes the builtin read tools `read`/`grep`/`find`/`ls` and `peek_main`). Lists are **unioned** across layers in bundle → user → project order (deduped, first occurrence wins): a user/project layer adds tools, it never drops the defaults shipped below it.
157
+ - `readOnlyExtensionAllowlistExclude` — tool names removed from the final list, e.g. to drop a bundled default.
158
+ - `promptPack` — prompt-pack manifest (see below); merges per key with the higher layer winning. Relative paths resolve against the layer's own directory, so a user-level manifest can live next to the user config; absolute paths work too.
159
+ - `features` — per-feature kill switches, each defaulting to `true`; a layer only overrides the keys it defines (higher layer wins per key). Set a switch to `false` to disable a behavior without touching the others:
160
+
161
+ | Switch | Behavior when `false` |
162
+ |--------|------------------------|
163
+ | `rightClickCopyPaste` | right-click copy (chat) / paste (editor) is inert — the hotkeys still work |
164
+ | `modelSwitch` | `Alt+M` does nothing |
165
+ | `retry` | fork turns run a single attempt with zero backoff, even if pi's `settings.retry` is enabled |
166
+
167
+ Example (user or project layer):
168
+
169
+ ```json
170
+ {
171
+ "readOnlyExtensionAllowlist": ["pi-vision-helper", "lens_diagnostics"],
172
+ "features": { "retry": false }
173
+ }
174
+ ```
175
+
176
+ ### Prompt pack manifest
177
+
178
+ `promptPack` maps each injected prompt to a markdown file (relative to the layer's directory, or absolute). All keys are optional — an absent or unreadable key falls back to the bundled `prompts/` default (with a UI warning):
179
+
180
+ | Key | Bundled default | Injected |
181
+ |-----|-----------------|----------|
182
+ | `promptPack.framing` | `prompts/btw-framing.md` | after the forked context (never rendered as a chat bubble) — frames the cite as reference-only |
183
+ | `promptPack.focusAnchor` | `prompts/btw-focus-anchor.md` | every turn — "answer only the latest btw message" |
184
+ | `promptPack.laneReminders.base` | `prompts/lane-reminder-base.md` | first read-only-lane violation (`{{tool}}` / `{{count}}`) |
185
+ | `promptPack.laneReminders.escalated` | `prompts/lane-reminder-escalated.md` | second violation, before the turn abort |
186
+ | `promptPack.laneReminders.failedNote` | `prompts/lane-failed-note.md` | after an executed-but-failed read-only call |
187
+ | `promptPack.laneReminders.preamble` | `prompts/lane-preamble.md` | read-only lane preamble |
188
+
189
+ The bundled `config.json` ships the official pi tool set in the read-only allowlist (`web_search`, `source_check`, `fetch_content`, `get_search_content`); third-party tools (pi-lens, context7, vision, …) are added through the user layer.
190
+
191
+ ## How It Works
192
+
193
+ The extension clones the current session context, creates a separate agent instance with all extension-registered tools, and renders it in a TUI overlay. Closing saves the conversation in memory so reopening restores it. Backgrounding (`Alt+W`) hides the overlay via the TUI's overlay handle while the agent keeps running.
194
+
195
+ The btw context keeps the main lane's system prompt in the system slot and injects the fork snapshot verbatim, so the btw request head is a token prefix of the main request (gateway prefix-cache hits). `forkSurgery` (`srcs/fork-surgery.ts`) makes the trailing tool exchange gateway-legal on the snapshot; the prompt pack supplies all injected prompt text; lane enforcement wraps `beforeToolCall`/`afterToolCall` (`srcs/side-chat-overlay.ts`) in the read-only lane only.
196
+
197
+ Main-agent tool execution events are tracked to maintain a set of written file paths (`srcs/file-activity-tracker.ts`); write-capable tools are wrapped to warn before touching those paths (`srcs/tool-wrapper.ts`).
198
+
199
+ While the side chat is open, xterm mouse reporting (SGR, button + motion tracking) is enabled and overlay events are routed to the chat: the wheel scrolls it, a left-button drag selects text, and a right-click copies the selection / pastes into the editor as described above. All mouse sequences are consumed so they never leak into the editor, and reporting follows overlay visibility.
200
+ `peek_main` reads the current session branch on demand and returns a compact summary.
201
+
202
+ ## Development
203
+
204
+ Structure:
205
+
206
+ ```text
207
+ .
208
+ ├── srcs/ # TypeScript implementation (pi loads TS directly, no build step)
209
+ │ ├── index.ts # extension entry: commands, shortcut, overlay lifecycle
210
+ │ ├── config.ts # layered config resolution (bundle / user / project)
211
+ │ ├── prompt-pack.ts # prompt-pack manifest loader + template substitution
212
+ │ ├── fork-surgery.ts # shared-prefix fork snapshot surgery (gateway-legal tails)
213
+ │ ├── side-chat-overlay.ts # TUI overlay, agent lifecycle, lane enforcement, mouse routing
214
+ │ ├── side-chat-messages.ts # message rendering, wrapping, selection, scrolling
215
+ │ ├── side-chat-mouse.ts # minimal SGR mouse parsing
216
+ │ ├── clipboard-read.ts # platform clipboard read (win32 / darwin / linux + OSC 52 fallback)
217
+ │ ├── retry.ts # turn-level retry: classifyRetryable + runWithRetry
218
+ │ ├── shortcuts.ts # hotkey bindings (Alt+W / Ctrl+T)
219
+ │ ├── model-switch.ts # Alt+M fork model picker: list building + thinking clamp
220
+ │ ├── side-chat-export.ts # Alt+E transcript export
221
+ │ ├── tool-wrapper.ts # write-path overlap warnings
222
+ │ └── file-activity-tracker.ts
223
+ ├── prompts/ # bundled prompt-pack defaults (framing, focus anchor, lane reminders)
224
+ ├── test/ # bun test suites (config resolution, mouse select)
225
+ ├── config.json # bundled defaults (promptPack manifest + read-only allowlist)
226
+ ├── banner.png
227
+ └── README.md
228
+ ```
229
+
230
+ Commands:
231
+
232
+ ```bash
233
+ bun install # install dependencies
234
+ bun run typecheck # tsc --noEmit -p tsconfig.json
235
+ bun test # bun test test/ (run serially, see bunfig.toml)
236
+ ```
237
+
238
+ The published package ships `srcs/`, `prompts/`, `config.json` and the docs; tests stay out of the tarball. To load the extension in pi during development, point pi's extension loader at `./srcs/index.ts` and `/reload` after edits (no build step — pi loads TypeScript directly).
239
+
240
+ ## Limitations
241
+
242
+ - One side chat at a time
243
+ - Won't open on top of another visible overlay
244
+ - Does not merge messages back into the main thread
245
+ - Bash overlap detection is heuristic — catches common write patterns, not all
246
+ - `peek_main` is on-demand, not live
247
+ - Mouse interaction (scroll and select) only works in the regular (non-fullscreen) TUI mode — the fullscreen alt-screen handler owns all mouse sequences
248
+
249
+ ## License
250
+
251
+ MIT — see [LICENSE](LICENSE). The license retains all three copyright lines: the original upstream author (Nico Bailon), the intermediate fork (yceachan), and this fork (hu3rror).
@@ -0,0 +1,252 @@
1
+ <p>
2
+ <img src="https://raw.githubusercontent.com/hu3rror/pi-better-btw-plus/main/banner.png" alt="pi-better-btw-plus" width="1100">
3
+ </p>
4
+
5
+ # pi-better-btw-plus
6
+
7
+ **[English](README.md) | 简体中文**
8
+
9
+ > [!note]
10
+ >
11
+ > 本包是对 [`@yceachan/pi-better-btw`](https://www.npmjs.com/package/@yceachan/pi-better-btw) 的**维护型 fork**(其本身又是 [nicobailon/pi-side-chat](https://github.com/nicobailon/pi-side-chat) 的 fork),由 **hu3rror** 维护于 [hu3rror/pi-better-btw-plus](https://github.com/hu3rror/pi-better-btw-plus)。署名链:原作者 **Nico Bailon** → **yceachan** 扩展 → 本 fork。
12
+
13
+ ## TL;DR
14
+
15
+ **把当前会话 fork 到一个旁路会话(btw)中,主线 agent 继续干活。**
16
+
17
+ [![npm version](https://img.shields.io/npm/v/pi-better-btw-plus?style=for-the-badge)](https://www.npmjs.com/package/pi-better-btw-plus)
18
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=for-the-badge)](LICENSE)
19
+
20
+ ```bash
21
+ pi install npm:pi-better-btw-plus
22
+ # in pi tui
23
+ > /btw || or Alt+W
24
+ ```
25
+
26
+ 处理长任务中途想顺便问点小事,又不想打断主线——查个 API 细节、验证一个思路、搜点东西,或瞄一眼主线 agent 在干什么。打开 btw TUI 浮层,提问,关闭,主线不受打扰。
27
+
28
+ <img src="https://raw.githubusercontent.com/hu3rror/pi-better-btw-plus/main/docs/overlay.png" alt="pi-better-btw-plus overlay" style="zoom:33%;" />
29
+
30
+ ## 本 fork 新增
31
+
32
+ [`@yceachan/pi-better-btw`](https://www.npmjs.com/package/@yceachan/pi-better-btw) 的全部功能都在,另新增:
33
+
34
+ - **右键复制与粘贴** —— 聊天区拖选后右键复制(延续 Windows Terminal 肌肉记忆);输入框右键从系统剪贴板粘贴,走编辑器内置归一化与大段 `[paste #N …]` 折叠标记。不再是 hotkey-only。
35
+ - **Fork 模型切换(`Alt+M`)** —— 侧聊内选择任意已认证模型,无需重建 fork;仅作用于 fork 本身(ADR 0002),thinking level 按新模型能力自动钳制。
36
+ - **Turn 级自动重试** —— 与主会话共用 `settings.retry` 预算:瞬时 provider 错误指数退避重试,带实时倒计时;`Esc` 取消。
37
+ - **功能开关** —— 分层配置的 `features` 块可关闭以上任意功能(`rightClickCopyPaste` / `modelSwitch` / `retry`),另有 `readOnlyExtensionAllowlistExclude` 移除内置 allowlist 默认项。
38
+
39
+ 完整功能见 [Feat](#feat)。
40
+
41
+ ## Feat
42
+
43
+ > [!note]
44
+ >
45
+ > **Thats Why Called Better-Btw**
46
+ >
47
+ > Author 尝试过[nicobailon/pi-side-chat](https://github.com/nicobailon/pi-side-chat) 与[dbachelder/pi-btw](https://github.com/dbachelder/pi-btw),均是简单从main 主线fork,如果 agent on turn ,均会出现尝试推进主线的情况,see[[feat request\] btw aside-session self-cognition — the side chat must not continue the main session's work · Issue #5 · nicobailon/pi-side-chat](https://github.com/nicobailon/pi-side-chat/issues/5)。
48
+ >
49
+ > 于是精心开发了如下feat
50
+
51
+ - `Aside-Agent self-Cognition` :注入主线上下文,便于对工程主线 ask a btw question;同时做出精心的上下文工程优化,强化辅助Agent认知,**避免全量主线上下文的tool call trace干扰认知,与主线竞争推进工程**。同时保留主线共享前缀,实现较好的缓存命中。
52
+
53
+ - `Prompt pack`: 所有提示此注入均文档化+ `bundle`/`$PI_HOME` / `$CWD`三级覆盖。
54
+
55
+ - `TUI scroll,select,copy` :在TUI-overlay 自订阅鼠标/hotkey事件,实现滚屏 ,text选中 , Ctrl +C 复制功能
56
+
57
+ - `Readonly/Edit Mode` : 默认只读来回应btw question,如果你希望Agent顺手做些小修改,Ctrl + t To Edit Mode.
58
+
59
+ - ToolAllowList : bundle + config.json custom
60
+
61
+ | 模式 | 工具 |
62
+ | ---- | ------------------------------------------------------------ |
63
+ | 只读 | `read`、`grep`、`find`、`ls` ;`peek_main` ;`config.json.readOnlyExtensionAllowlist` |
64
+ | 编辑 | `read`、`bash`、`edit`、`write` |
65
+
66
+ - `Auto-retry(turn 级重试)` :读取 pi 的 `settings.retry` 预算(`enabled` / `maxRetries` / `baseDelayMs`,默认值与主会话一致)。瞬时 provider 错误(overloaded / rate limit / 5xx)按指数退避自动重试——状态区显示 `Retrying (n/m) in Xs…` 实时倒计时——重试前剥离失败的 assistant 消息,避免错误重复进入下一次请求。上下文溢出与 abort 永不重试。退避等待中按 `Esc` 立即取消并展示最后一次错误作为最终结果;预算耗尽同理。`enabled: false`(如本地模型调试)时错误零开销直接展示。
67
+
68
+ ## Usage
69
+
70
+ 用 `/btw`(别名 `/side`)或 `Alt+W`(同时负责后台/显示切换)打开旁路会话。提问后按 `Enter`。
71
+
72
+ 按 `Esc` 关闭。用 `/btw` 或 `Alt+W` 重新打开,会话继续保留。
73
+
74
+ | 快捷键 | 作用 |
75
+ | ------ | ---- |
76
+ | `Alt+W` | 打开(关闭时)/ 后台化(显示时)/ 恢复(隐藏时) |
77
+ | `Ctrl+T` | 切换只读 / 编辑模式 |
78
+ | `Alt+R` | 从最新主线上下文重新 fork |
79
+ | `Alt+N` | 开始空白对话 |
80
+ | `Alt+E` | 导出对话记录到 `$CWD/.agents/eval/pi-better-btw-<timestamp>.md` |
81
+ | `Alt+M` | 打开 fork 模型选择器(scoped + 已认证模型;`↑/↓` 选择,`Enter` 确认,`Esc` 取消) |
82
+
83
+ 在Readonly Mode(default),只读车道是**强制的**:越权调用工具会被硬阻断并注入prompt;第二次违规会升级措辞并中止该轮,提示(`🚧 lane blocked` 状态行)。已执行但失败的只读调用会被 `afterToolCall` 备注再次归位。编辑模式(`Ctrl+T`)不受影响。
84
+
85
+ **窥视主线 agent** —— `peek_main` 工具读取主线会话的近期活动。
86
+
87
+ ```text
88
+ What is the main agent doing right now?
89
+ What changed since I opened this side chat?
90
+ ```
91
+
92
+ **非抢占浮层 + 后台化** —— 浮层在屏幕顶部打开,主编辑器保持可见。浮层打开期间始终聚焦;`Alt+W` 将其后台化(隐藏,agent 继续流式输出)交还键盘,再按 `Alt+W` 恢复显示。
93
+
94
+ **更高的聊天区域** —— 消息区比上游高约 2.5 倍,长回答和工具输出更易读;在小终端上自适应(不溢出,始终保留主编辑器可见)。
95
+
96
+ **滚动历史** —— `PgUp`/`PgDn` 整页滚动,`Shift+↑`/`Shift+↓` 按行滚动,鼠标指针悬停于聊天区域时滚轮滚动。离开最新消息时,标题栏出现 `[↑N]` 指示器,提示栏切换为 `↑N · PgDn/Wheel ↓`。流式期间视口跟随底部;一旦你向上滚动就冻结内容锚定(新行增长滚动偏移而不是滑动可见内容),回到底部或新消息后恢复跟随。
97
+
98
+ **鼠标选择 + 右键复制** —— 拖拽选择聊天文本(反色高亮);双击选择整行。用 `Ctrl+C` / `Ctrl+Shift+C` **或在聊天区右键**复制保留的选择(延续 Windows Terminal 肌肉记忆:右键在松开时触发、需要有活跃选区、且保持高亮可重复右键再复制)。复制走原生剪贴板级联(`wl-copy`/`xclip`,OSC 52 兜底);拖拽不碰剪贴板,鼠标交互不阻塞事件循环。鼠标上报跟随浮层*可见性*——后台化时释放终端原生选择。
99
+
100
+ **输入框右键粘贴** —— 在编辑器内右键把系统剪贴板文本粘贴到光标处,走编辑器内置粘贴入口:换行/制表符归一化(`\r`→`\n`、`\t`→4 空格),大段粘贴(>10 行或 >1000 字符)折叠为 `[paste #N +X lines]` / `[paste #N X chars]` 标记、提交时展开为完整文本,且粘贴是单个撤销步。剪贴板读取失败(平台通道与 OSC 52 兜底均不可用)或为空时显示一行提示,编辑器保持原样。
101
+
102
+ **对话导出** —— `Alt+E` 把 btw 历史(fork 上下文、framing 块、对话、流式中内容)导出为 `$CWD/.agents/eval/pi-better-btw-<timestamp>.md` 的 markdown 诊断产物,便于调试功能开发。
103
+
104
+ **Fork 模型切换** —— `Alt+M` 在浮层内打开模型选择器(`↑/↓` 移动,`Enter` 确认,`Esc` 取消)。列表优先展示会话作用域模型(`--models` / `enabledModels`),为空时回退可用模型目录,且只显示已配置认证的模型。确认后直接替换 fork agent 的运行时模型——下一 turn 生效,无需重建 fork——并按新模型能力钳制 thinking level(无 reasoning 的模型钳制为 `off`)。选择为 fork 局部状态(ADR 0002):主会话模型不受影响。后台化(`Alt+W`)保留选择;`Alt+R` / `Alt+N` / `Esc` 关闭后随实例重置。头部显示当前 fork 模型;流式期间拒绝打开。
105
+
106
+ ## 快捷键
107
+
108
+ | 按键 | 作用 |
109
+ | ---- | ---- |
110
+ | `Alt+W` | 打开(关闭时)/ 后台化(显示时)/ 恢复(隐藏时) |
111
+ | `Enter` | 发送消息 |
112
+ | `Esc` | 中断流式输出;空闲时关闭 |
113
+ | `Alt+R` | 从最新主线上下文重新 fork |
114
+ | `Alt+N` | 开始空白对话 |
115
+ | `Alt+E` | 导出 btw 对话历史到 `$CWD/.agents/eval/pi-better-btw-<timestamp>.md` |
116
+ | `Alt+M` | 打开 fork 模型选择器(`↑/↓` 选择,`Enter` 确认,`Esc` 取消) |
117
+ | `Ctrl+T` | 切换只读 / 编辑模式 |
118
+ | `PgUp` / `PgDn` | 整页滚动历史 |
119
+ | `Shift+↑` / `Shift+↓` | 按行滚动 |
120
+ | 鼠标滚轮 | 指针位于聊天区域时滚动 |
121
+ | 鼠标拖拽 | 选择聊天文本(反色高亮);松开不自动复制 |
122
+ | 双击 | 选择整行 |
123
+ | `Ctrl+C` / `Ctrl+Shift+C` | 复制当前鼠标选择(选择保留到下次点击,可重复复制) |
124
+ | 鼠标右键(聊天区) | 复制保留的鼠标选择(松开时触发,保持高亮) |
125
+ | 鼠标右键(输入框) | 把系统剪贴板粘贴到光标处(编辑器归一化 + 大段 `[paste #N …]` 标记) |
126
+ ## 命令参考
127
+
128
+ ### `/btw`
129
+
130
+ 打开旁路会话浮层。`/side` 的别名。
131
+
132
+ ### `/side`
133
+
134
+ 打开旁路会话浮层(保留上游命令名作为兼容别名)。
135
+
136
+ ### `peek_main`
137
+
138
+ 仅旁路 agent 可用。
139
+
140
+ | 参数 | 类型 | 说明 |
141
+ | ---- | ---- | ---- |
142
+ | `lines` | integer | 最多检查条数(默认 20,最大 50) |
143
+ | `since_fork` | boolean | 仅显示旁路会话打开之后的活动 |
144
+
145
+ ## 配置
146
+
147
+ pi-better-btw 从三个位置按优先级递增读取 `config.json` —— 每层只覆盖它实际定义的键:
148
+
149
+ | 层 | 位置 |
150
+ | -- | -- |
151
+ | Bundle(默认) | 扩展目录下的 `config.json` —— 随 git 跟踪,随发布包分发 |
152
+ | 用户 | `~/.pi/agent/pi-better-btw/config.json` |
153
+ | 项目 | `<project>/.pi/pi-better-btw/config.json` |
154
+
155
+ 键:
156
+
157
+ - `readOnlyExtensionAllowlist` —— 只读车道允许的扩展工具名(车道始终包含内置只读工具 `read`/`grep`/`find`/`ls` 和 `peek_main`)。各层按 bundle → user → project 顺序**取并集**(去重,先到先得):高层只增不减。
158
+ - `readOnlyExtensionAllowlistExclude` —— 从最终列表中移除的工具名,例如用于去掉某个内置默认。
159
+ - `promptPack` —— 提示包清单(见下);按键合并,高层优先。相对路径按所在层目录解析,用户级 manifest 可放在用户配置旁边;绝对路径亦可。
160
+ - `features` —— 按功能开关,每项默认 `true`;层只覆盖它定义的键(按键高层优先)。设为 `false` 即可禁用某个行为而不影响其它:
161
+
162
+ | 开关 | 为 `false` 时的行为 |
163
+ | ---- | ---- |
164
+ | `rightClickCopyPaste` | 右键复制(聊天区)/ 粘贴(输入框)失效——快捷键不受影响 |
165
+ | `modelSwitch` | `Alt+M` 无反应 |
166
+ | `retry` | fork 每轮只跑一次尝试、零退避,即使 pi 的 `settings.retry` 开启 |
167
+
168
+ 示例(用户或项目层):
169
+
170
+ ```json
171
+ {
172
+ "readOnlyExtensionAllowlist": ["pi-vision-helper", "lens_diagnostics"],
173
+ "features": { "retry": false }
174
+ }
175
+ ```
176
+
177
+ ### 提示包清单
178
+
179
+ `promptPack` 把每条注入提示映射到一个 markdown 文件(相对于本层目录,或绝对路径)。所有键均可选——缺失或不可读的键回退到随包的 `prompts/` 默认值(并给出 UI 警告):
180
+
181
+ | 键 | 内置默认 | 注入时机 |
182
+ | -- | -------- | ---- |
183
+ | `promptPack.framing` | `prompts/btw-framing.md` | fork 上下文之后(不作为聊天气泡渲染)——把引用框定为"仅供引用" |
184
+ | `promptPack.focusAnchor` | `prompts/btw-focus-anchor.md` | 每轮——"只回答 btw 最新消息" |
185
+ | `promptPack.laneReminders.base` | `prompts/lane-reminder-base.md` | 第一次只读违规(`{{tool}}` / `{{count}}`) |
186
+ | `promptPack.laneReminders.escalated` | `prompts/lane-reminder-escalated.md` | 第二次违规,中止本轮之前 |
187
+ | `promptPack.laneReminders.failedNote` | `prompts/lane-failed-note.md` | 已执行但失败的只读调用之后 |
188
+ | `promptPack.laneReminders.preamble` | `prompts/lane-preamble.md` | 只读车道开场白 |
189
+
190
+ 随包的 `config.json` 只读白名单默认只含官方 pi 工具集合(`web_search`、`source_check`、`fetch_content`、`get_search_content`);第三方工具(pi-lens、context7、vision 等)通过用户层追加。
191
+
192
+ ## 工作原理
193
+
194
+ 扩展克隆当前会话上下文,创建带全部扩展工具的独立 agent 实例,并在 TUI 浮层中渲染。关闭时在内存中保存对话,重开恢复。后台化(`Alt+W`)通过 TUI 的 overlay handle 隐藏浮层,agent 继续运行。
195
+
196
+ btw 上下文保留主线的 system prompt 于 system 槽位,并逐字注入 fork 快照,使 btw 请求头成为主线请求的 token 前缀(网关前缀缓存命中)。`forkSurgery`(`srcs/fork-surgery.ts`)让快照的尾部工具交换对网关合法;提示包供给全部注入文本;车道强制在只读模式下包装 `beforeToolCall`/`afterToolCall`(`srcs/side-chat-overlay.ts`)。
197
+
198
+ 主线 agent 的工具执行事件被跟踪以维护已写文件路径集合(`srcs/file-activity-tracker.ts`);写类工具被包装以在触碰这些路径前警告(`srcs/tool-wrapper.ts`)。
199
+
200
+ 旁路会话打开期间启用 xterm 鼠标上报(SGR,按键 + 移动跟踪),浮层事件路由到聊天区:滚轮滚动,左键拖拽选择,右键复制选区 / 在输入框粘贴(见上)。所有鼠标序列都被吞掉,绝不泄漏到编辑器;上报跟随浮层可见性。
201
+ `peek_main` 按需读取当前会话分支并返回紧凑摘要。
202
+
203
+ ## 开发
204
+
205
+ 结构:
206
+
207
+ ```text
208
+ .
209
+ ├── srcs/ # TypeScript 实现(pi 直接加载 TS,无构建步骤)
210
+ │ ├── index.ts # 扩展入口:命令、快捷键、浮层生命周期
211
+ │ ├── config.ts # 分层配置解析(bundle / user / project)
212
+ │ ├── prompt-pack.ts # 提示包清单加载 + 模板替换
213
+ │ ├── fork-surgery.ts # 共享前缀 fork 快照手术(网关合法尾部)
214
+ │ ├── side-chat-overlay.ts # TUI 浮层、agent 生命周期、车道强制、鼠标路由
215
+ │ ├── side-chat-messages.ts # 消息渲染、换行、选择、滚动
216
+ │ ├── side-chat-mouse.ts # 最小 SGR 鼠标解析
217
+ │ ├── clipboard-read.ts # 平台剪贴板读取(win32 / darwin / linux + OSC 52 兜底)
218
+ │ ├── retry.ts # turn 级重试引擎:classifyRetryable + runWithRetry
219
+ │ ├── model-switch.ts # Alt+M fork 模型选择器:列表构建 + thinking 钳制
220
+ │ ├── shortcuts.ts # 快捷键绑定(Alt+W / Ctrl+T)
221
+ │ ├── side-chat-export.ts # Alt+E 对话导出
222
+ │ ├── tool-wrapper.ts # 写路径重叠警告
223
+ │ └── file-activity-tracker.ts
224
+ ├── prompts/ # 随包提示包默认值(framing、焦点锚、车道提醒)
225
+ ├── test/ # bun test 测试套件(配置解析、鼠标选择)
226
+ ├── config.json # 内置默认(promptPack 清单 + 只读白名单)
227
+ ├── banner.png
228
+ └── README.md
229
+ ```
230
+
231
+ 命令:
232
+
233
+ ```bash
234
+ bun install # 安装依赖
235
+ bun run typecheck # tsc --noEmit -p tsconfig.json
236
+ bun test # bun test test/(串行运行,见 bunfig.toml)
237
+ ```
238
+
239
+ 发布包包含 `srcs/`、`prompts/`、`config.json` 与文档;测试不进入 tarball。开发时把 pi 的扩展加载器指向 `./srcs/index.ts`,改完代码 `/reload` 即可(无构建步骤——pi 直接加载 TypeScript)。
240
+
241
+ ## 限制
242
+
243
+ - 同一时间只能有一个旁路会话
244
+ - 无法在另一个可见浮层之上打开
245
+ - 不会把消息合并回主线会话
246
+ - bash 重叠检测是启发式的——覆盖常见写模式,非全部
247
+ - `peek_main` 是按需读取,非实时
248
+ - 鼠标交互(滚动与选择)仅在常规(非全屏)TUI 模式下可用——全屏 alt-screen 处理器拥有全部鼠标序列
249
+
250
+ ## License
251
+
252
+ MIT —— 见 [LICENSE](LICENSE)。许可证同时保留三行版权:上游原作者(Nico Bailon)、中间 fork(yceachan)与本 fork(hu3rror)。
package/banner.png ADDED
Binary file
package/config.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "promptPack": {
3
+ "framing": "prompts/btw-framing.md",
4
+ "focusAnchor": "prompts/btw-focus-anchor.md",
5
+ "laneReminders": {
6
+ "base": "prompts/lane-reminder-base.md",
7
+ "escalated": "prompts/lane-reminder-escalated.md",
8
+ "failedNote": "prompts/lane-failed-note.md",
9
+ "preamble": "prompts/lane-preamble.md"
10
+ }
11
+ },
12
+ "readOnlyExtensionAllowlist": [
13
+ "web_search",
14
+ "source_check",
15
+ "fetch_content",
16
+ "get_search_content"
17
+ ],
18
+ "features": {
19
+ "rightClickCopyPaste": true,
20
+ "modelSwitch": true,
21
+ "retry": true
22
+ }
23
+ }
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "pi-better-btw-plus",
3
+ "version": "1.0.0",
4
+ "description": "pi extension: /btw side-chat overlay — maintained fork of @yceachan/pi-better-btw (+ right-click copy/paste, fork model switch, turn-level retry)",
5
+ "type": "module",
6
+ "keywords": [
7
+ "pi-package",
8
+ "pi-extension",
9
+ "pi",
10
+ "side-chat",
11
+ "btw",
12
+ "overlay",
13
+ "tui",
14
+ "coding-agent"
15
+ ],
16
+ "author": "hu3rror",
17
+ "contributors": [
18
+ "Nico Bailon",
19
+ "yceachan <yceachan@foxmail.com>"
20
+ ],
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/hu3rror/pi-better-btw-plus.git"
25
+ },
26
+ "homepage": "https://github.com/hu3rror/pi-better-btw-plus#readme",
27
+ "files": [
28
+ "srcs",
29
+ "prompts",
30
+ "config.json",
31
+ "README.md",
32
+ "README.zh-CN.md",
33
+ "banner.png"
34
+ ],
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "pi": {
39
+ "extensions": [
40
+ "./srcs/index.ts"
41
+ ]
42
+ },
43
+ "scripts": {
44
+ "typecheck": "tsc --noEmit -p tsconfig.json",
45
+ "test": "bun test test/"
46
+ },
47
+ "peerDependencies": {
48
+ "@earendil-works/pi-coding-agent": "*",
49
+ "@earendil-works/pi-agent-core": "*",
50
+ "@earendil-works/pi-ai": "*",
51
+ "@earendil-works/pi-tui": "*",
52
+ "@sinclair/typebox": "*"
53
+ },
54
+ "devDependencies": {
55
+ "@earendil-works/pi-agent-core": "^0.84.2",
56
+ "@earendil-works/pi-ai": "^0.84.2",
57
+ "@earendil-works/pi-coding-agent": "^0.84.2",
58
+ "@earendil-works/pi-tui": "^0.84.2",
59
+ "@types/node": "^24",
60
+ "bun-types": "^1.3.14",
61
+ "typebox": "1.1.38",
62
+ "typescript": "^6.0.3"
63
+ },
64
+ "trustedDependencies": [
65
+ "@google/genai",
66
+ "koffi",
67
+ "protobufjs"
68
+ ]
69
+ }
@@ -0,0 +1 @@
1
+ Focus: answer only the latest user message in this btw conversation (the one directly above your reply). The cited main context is reference only — its tool calls and answers were performed by the main agent, not you, and none of its user messages are your questions. Never continue the main lane's pending work, and call tools only when the question actually needs a lookup.
@@ -0,0 +1,8 @@
1
+ You are the btw side chat — a quick-question lane running parallel to the main agent. The main agent is working independently and cannot see this chat.
2
+
3
+ The main session's context in this conversation is reference only — it is not your work. Do not resume the main agent's pending commands, tool calls, edits, or unfinished answers, and do not redo its work with your own tools.
4
+
5
+ Your task is the latest user message. Answer it directly and concisely. If the user asks what the main lane is doing, check with `peek_main` or suggest waiting.
6
+
7
+ Working directory: {{cwd}}
8
+ Model: {{model}}
@@ -0,0 +1 @@
1
+ [btw side chat] That call failed. You are in the read-only lane — retry with read/grep/find/ls, or report back. Do not continue the main lane's work.
@@ -0,0 +1 @@
1
+ You are in the read-only lane: only read/grep/find/ls, allowlisted tools and peek_main are available here. You cannot write files, edit, or execute commands — that is the main lane's job. If a write is genuinely needed, tell the user to switch to edit mode (Ctrl+T) or ask the main agent.