@narumitw/pi-btw 0.54.2 → 0.55.1

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 CHANGED
@@ -1,27 +1,19 @@
1
- # 💬 pi-btw — Side Questions for the Pi Coding Agent
1
+ # 💬 pi-btw — Ask Side Questions Without Derailing the Main Task
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/@narumitw/pi-btw)](https://www.npmjs.com/package/@narumitw/pi-btw) [![Pi extension](https://img.shields.io/badge/Pi-extension-blue)](https://pi.dev) [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](./LICENSE)
4
4
 
5
- `@narumitw/pi-btw` is a native [Pi coding agent](https://pi.dev) extension that adds `/btw`, a side-question command for quick clarifications that should not interrupt or pollute the main agent conversation.
5
+ Ask temporary questions in a separate side thread while the main Pi conversation and coding task stay focused.
6
6
 
7
- Use it when you want to ask a temporary question, inspect context, or get a short explanation while keeping the primary coding task focused.
7
+ Bring back only the answer or context you explicitly choose.
8
8
 
9
9
  ## ✨ Features
10
10
 
11
- - Adds a `/btw` menu for starting or resuming an in-memory side thread, choosing context from the main session tree, or changing pi-btw settings.
12
- - Starts a fresh side thread from any persisted main-session branch without switching the main branch.
13
- - Keeps `/btw <question>` as a direct fast path that always starts a fresh side thread.
14
- - Answers side questions in a dedicated, scrollable full-screen UI.
15
- - Keeps mouse-drag copying stable while the main agent continues running in the background.
16
- - Supports follow-up questions in the same ephemeral side thread.
17
- - Resumes any non-empty side thread retained by the current Pi session, listed by its first question.
18
- - Queues Pi-style `Steering` questions while an answer is running and processes them one at a time.
19
- - Optionally brings the latest answer, a question-to-end suffix, an exact line range, or the entire side thread into the main editor.
20
- - Uses the current session branch as context.
21
- - Uses Pi's current model or an independent model selected in `pi-btw.json`.
22
- - Uses a pi-btw thinking level that can either start from the main thread or use a fixed remembered value.
23
- - Does not append the side question or answer to the main conversation.
24
- - Works as an independently installable npm Pi extension package.
11
+ - Starts a fresh side thread immediately with `/btw <question>` or opens the manager with `/btw`.
12
+ - Uses any persisted main-session branch as context without switching that branch.
13
+ - Supports scrollable answers, follow-up questions, queued steering, and resumable in-memory threads.
14
+ - Keeps side questions and answers out of the main conversation by default.
15
+ - Brings back only the latest answer, a selected range, a question suffix, or the complete side thread when requested.
16
+ - Uses Pi's current model and thinking level or independent saved choices.
25
17
 
26
18
  ## 📦 Install
27
19
 
@@ -35,17 +27,21 @@ Try without installing permanently:
35
27
  pi -e npm:@narumitw/pi-btw
36
28
  ```
37
29
 
38
- Try this package locally from the repository root:
30
+ Build and try this package locally from the repository root:
39
31
 
40
32
  ```bash
33
+ npm --workspace @narumitw/pi-btw run build
41
34
  pi -e ./packages/pi-btw
42
35
  ```
43
36
 
44
- The package declares `dist/index.ts`, so an unbuilt local checkout must run `npm --workspace @narumitw/pi-btw run build` before Pi loads the package directory.
37
+ The package declares `dist/index.ts`, so an unbuilt local checkout must run the build before Pi loads the package directory.
45
38
 
46
- `just try btw` runs that build automatically.
39
+ ## 🚀 Quick start
47
40
 
48
- ## 🚀 Usage
41
+ Run `/btw <question>` to start a side thread immediately, or run `/btw` to choose context and settings first.
42
+ Side-thread questions and answers remain separate from the main conversation unless you explicitly bring selected context back.
43
+
44
+ ## 💬 Commands
49
45
 
50
46
  Open the pi-btw menu or provide the first question immediately:
51
47
 
@@ -77,75 +73,70 @@ Opening and closing a thread without a new result does not reorder it.
77
73
  `/btw <question>` bypasses this menu and always starts a fresh side thread.
78
74
  Its answer opens above the side-thread editor.
79
75
  The side thread uses a dedicated full-screen terminal view.
80
- The main agent continues running in the background, but its screen rendering stays suspended until
81
- `/btw` closes, so new main-thread output cannot move a mouse selection inside the side thread.
82
- Drag the primary mouse button across side-thread text to select and copy it through Pi's terminal
83
- clipboard support. Returning from `/btw` redraws the main view with everything produced while it
84
- was hidden. A compact `btw · side thread` header stays fixed above the content so the ephemeral
85
- workspace remains recognizable while scrolling. Messages use Pi's normal
86
- user and assistant presentation without numbered turns or role labels. Type each question and press
87
- `Enter`; no follow-up shortcut is required.
88
- Previous side questions and answers remain available to the model and visible for that
89
- invocation. The side-thread header shows its current thinking level. Press Pi's configured
90
- `app.thinking.cycle` shortcut (`Shift+Tab` by default) in the composer to cycle the levels
91
- supported by the side-thread model; every later question uses the displayed level until it is
92
- changed again. When a fixed thinking level is selected, each shortcut change is also written to
93
- `pi-btw.json` for the next invocation by default. Turn **Remember thinking level changes** off in
94
- Settings to keep fixed-level changes local to the current side thread. When **Same as main thread** is
95
- selected, shortcut changes are always local to the current side thread. Neither path changes the main
96
- session's thinking level.
97
- While a response is running, the transcript and composer remain visible above an `Answering…`
98
- status.
99
- Type another question and press `Enter` to queue it as `Steering`; queued questions are shown in
100
- submission order and answered one at a time after the active response completes.
76
+ The main agent continues running in the background, but its screen rendering stays suspended until `/btw` closes, so new main-thread output cannot move a mouse selection inside the side thread.
77
+ Drag the primary mouse button across side-thread text to request a copy through Pi's host clipboard helper.
78
+ The view reports `Copied!` when Pi accepts the copy and `Copy failed` when Pi reports failure; actual clipboard availability still depends on operating-system and terminal support.
79
+ Returning from `/btw` redraws the main view with everything produced while it was hidden.
80
+ A compact `btw · side thread` header stays fixed above the content so the ephemeral workspace remains recognizable while scrolling.
81
+ Messages use Pi's normal user and assistant presentation without numbered turns or role labels.
82
+ Type each question and press `Enter`; no follow-up shortcut is required.
83
+ Press `Ctrl+Shift+F` to search completed or answering transcript content.
84
+ Press `Enter` or `Ctrl+G` for the next match, `Shift+Enter` or `Ctrl+Shift+G` for the previous match, and `Escape` to close search.
85
+ Search uses Pi's active theme, keeps the composer available after closing the search overlay, and does not include fixed header or footer chrome.
86
+ Previous side questions and answers remain available to the model and visible for that invocation.
87
+ The side-thread header shows its current thinking level.
88
+ Press Pi's configured `app.thinking.cycle` shortcut (`Shift+Tab` by default) in the composer to cycle the levels supported by the side-thread model; every later question uses the displayed level until it is changed again.
89
+ When a fixed thinking level is selected, each shortcut change is also written to `pi-btw.json` for the next invocation by default.
90
+ Turn **Remember thinking level changes** off in Settings to keep fixed-level changes local to the current side thread.
91
+ When **Same as main thread** is selected, shortcut changes are always local to the current side thread.
92
+ Neither path changes the main session's thinking level.
93
+ While a response is running, the transcript and composer remain visible above an `Answering…` status.
94
+ Type another question and press `Enter` to queue it as `Steering`; queued questions are shown in submission order and answered one at a time after the active response completes.
101
95
  A queued question uses the side thread's thinking level when its turn begins.
102
96
  A failed active response is shown in the transcript and does not discard later steering questions.
103
97
  Use the mouse wheel or trackpad to scroll transcript history like Pi's main thread.
104
- Keyboard `PgUp`/`PgDn` history navigation remains available.
105
- It appears in the footer only when the transcript can scroll.
98
+ Keyboard `PgUp`/`PgDn` history navigation remains available and appears in the footer only when the transcript can scroll.
106
99
  Press `Ctrl+C` to cancel the active response and discard the ephemeral side-thread draft and steering queue.
107
100
  Completed questions, answers, and visible errors remain available through Resume until the current extension instance ends.
108
101
  Steering remains entirely inside pi-btw and never appends to the main conversation or editor.
109
102
 
110
- After at least one successful answer, press `Ctrl+R` to bring selected context to the main
111
- editor. The scope menu shows the size of the latest question and answer and the entire side
112
- thread before you choose. Bring the latest question and answer, everything from a chosen
113
- question onward, an exact text range, or the entire side thread. Question-suffix, exact-range,
114
- and entire-thread choices preview the exact editable context block before the side thread closes;
115
- `Escape` returns and `Ctrl+C` closes without bringing anything to main.
103
+ After at least one successful answer, press `Ctrl+R` to bring selected context to the main editor.
104
+ The scope menu shows the size of the latest question and answer and the entire side thread before you choose.
105
+ Bring the latest question and answer, everything from a chosen question onward, an exact text range, or the entire side thread.
106
+ Question-suffix, exact-range, and entire-thread choices preview the exact editable context block before the side thread closes.
107
+ Pressing `Escape` returns, while `Ctrl+C` closes without bringing anything to main.
116
108
 
117
109
  The text-range selector supports both fast line selection and editor-style character selection.
118
- It reports whether anything is selected plus the selected line, message, and approximate token
119
- counts. Press `Space` to select the current raw source line, then use `Up`/`Down` to extend by
120
- whole lines; press `Space` again to clear it. Alternatively, use the arrow keys to move the cursor
121
- and `Shift`+arrow keys to extend a character-level selection. Starting a Shift selection replaces
122
- any active line selection. Selected lines include a visible `●` marker in addition to highlighting.
123
- Pi's configured keys control vertical navigation, bringing, and going back (`Up`/`Down`, `Enter`,
124
- and `Escape` by default), and the selector displays the active keys. Selection follows raw source
125
- text rather than terminal-wrapped visual rows.
126
-
127
- Bringing context to main closes the side thread and loads a deterministic, editable context block
128
- into Pi's main editor. It never sends the draft automatically. If the main editor already has a
129
- draft, append is the recommended default. Replace is labeled as destructive and requires a second
130
- confirmation; Cancel returns to the side thread without changing either draft. Concurrent editor
131
- updates made while these menus are open are preserved. A success message reports whether context
132
- was loaded, appended, or replaced and its approximate size.
133
- Without an explicit bring-to-main action, closing `/btw` never adds the side thread to the main
134
- conversation. Non-empty threads remain only in memory for Resume within the current Pi session.
135
- `/new`, Pi `/resume`, `/reload`, extension replacement, and process restart discard every retained
136
- thread. Unsent drafts, steering queues, interrupted answers, and model credentials are never retained.
137
-
138
- ## ⚙️ Model and thinking level
139
-
140
- By default, `/btw` uses the current session model. To use an independent model for side
141
- questions, create:
110
+ It reports whether anything is selected plus the selected line, message, and approximate token counts.
111
+ Press `Space` to select the current raw source line, then use `Up`/`Down` to extend by whole lines; press `Space` again to clear it.
112
+ Alternatively, use the arrow keys to move the cursor and `Shift`+arrow keys to extend a character-level selection.
113
+ Starting a Shift selection replaces any active line selection.
114
+ Selected lines include a visible `●` marker in addition to highlighting.
115
+ Pi's configured keys control vertical navigation, bringing, and going back (`Up`/`Down`, `Enter`, and `Escape` by default), and the selector displays the active keys.
116
+ Selection follows raw source text rather than terminal-wrapped visual rows.
117
+
118
+ Bringing context to main closes the side thread and loads a deterministic, editable context block into Pi's main editor.
119
+ It never sends the draft automatically.
120
+ If the main editor already has a draft, append is the recommended default.
121
+ Replace is labeled as destructive and requires a second confirmation; Cancel returns to the side thread without changing either draft.
122
+ Concurrent editor updates made while these menus are open are preserved.
123
+ A success message reports whether context was loaded, appended, or replaced and its approximate size.
124
+ Without an explicit bring-to-main action, closing `/btw` never adds the side thread to the main conversation.
125
+ Non-empty threads remain only in memory for Resume within the current Pi session.
126
+ `/new`, Pi `/resume`, `/reload`, extension replacement, and process restart discard every retained thread.
127
+ Unsent drafts, steering queues, interrupted answers, and model credentials are never retained.
128
+
129
+ ## ⚙️ Settings
130
+
131
+ By default, `/btw` uses the current session model.
132
+ To use an independent model for side questions, create:
142
133
 
143
134
  ```text
144
135
  $PI_CODING_AGENT_DIR/pi-btw.json
145
136
  ```
146
137
 
147
- The normal location is `~/.pi/agent/pi-btw.json`. `PI_CODING_AGENT_DIR` is an existing Pi
148
- setting; pi-btw does not add any environment variables.
138
+ The normal location is `~/.pi/agent/pi-btw.json`.
139
+ `PI_CODING_AGENT_DIR` is an existing Pi setting; pi-btw does not add any environment variables.
149
140
 
150
141
  ```json
151
142
  {
@@ -155,42 +146,41 @@ setting; pi-btw does not add any environment variables.
155
146
  }
156
147
  ```
157
148
 
158
- The `model` value uses `provider/model-id` format. Only the first `/` is the separator, so
159
- model IDs may contain additional slashes, such as `openrouter/anthropic/claude-sonnet`.
160
- The configured model must exist in Pi's model registry and have usable credentials. If it
161
- cannot be found or authenticated, pi-btw warns and falls back to the current session model.
162
- If neither model is available, `/btw` reports an error and stops. This selection affects only
163
- `/btw`; it does not change the main session model.
164
-
165
- Pi calls its reasoning setting the **thinking level**. In Settings, choose **Same as main thread**
166
- to start each new side thread from the main thread's current thinking level. This is stored by
167
- omitting `thinkingLevel` from `pi-btw.json`.
168
-
169
- Set `thinkingLevel` only when you want a fixed pi-btw starting level. Accepted fixed values are
170
- `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. The initial value and shortcut cycle
171
- are clamped to the selected side model's capabilities using Pi's model rules. Resumed side threads
172
- keep their own local thinking level instead of re-syncing with the main thread. Pi-btw does not read,
173
- write, or change the main session's `defaultThinkingLevel`.
174
-
175
- `rememberThinkingLevelChanges` controls only persistence for fixed thinking levels and defaults to
176
- `true` when omitted. A side-thread shortcut always changes that side thread immediately. When a fixed
177
- thinking level is selected and remembering is on, the concrete level is written for the next
178
- invocation; when off, `pi-btw.json` stays unchanged. When **Same as main thread** is selected,
179
- shortcut changes stay local even when remembering is on. If a shortcut write fails, the local change
180
- remains active and pi-btw warns that it was not remembered. A failed Settings-screen save instead
181
- restores the previous displayed value.
182
-
183
- A missing settings file is a side-effect-free read: pi-btw creates it only after a Settings change
184
- or a remembered shortcut change. Saves are ordered within the Pi process and published atomically
185
- with a same-directory temporary file and rename. They preserve `model` and unknown fields; malformed
186
- or invalid files block saves and remain unchanged. Settings must be valid UTF-8 and no larger than
187
- 64 KiB, so unexpectedly large or invalidly encoded files are rejected without being rewritten.
188
- Separate Pi processes and external editors are outside this in-process ordering boundary. The file
189
- is read for each `/btw` invocation, so edits apply without `/reload`.
149
+ The `model` value uses `provider/model-id` format.
150
+ Only the first `/` is the separator, so model IDs may contain additional slashes, such as `openrouter/anthropic/claude-sonnet`.
151
+ The configured model must exist in Pi's model registry and have usable credentials.
152
+ If it cannot be found or authenticated, pi-btw warns and falls back to the current session model.
153
+ If neither model is available, `/btw` reports an error and stops.
154
+ This selection affects only `/btw`; it does not change the main session model.
155
+
156
+ Pi calls its reasoning setting the **thinking level**.
157
+ In Settings, choose **Same as main thread** to start each new side thread from the main thread's current thinking level.
158
+ This is stored by omitting `thinkingLevel` from `pi-btw.json`.
159
+
160
+ Set `thinkingLevel` only when you want a fixed pi-btw starting level.
161
+ Accepted fixed values are `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`.
162
+ The initial value and shortcut cycle are clamped to the selected side model's capabilities using Pi's model rules.
163
+ Resumed side threads keep their own local thinking level instead of re-syncing with the main thread.
164
+ Pi-btw does not read, write, or change the main session's `defaultThinkingLevel`.
165
+
166
+ `rememberThinkingLevelChanges` controls only persistence for fixed thinking levels and defaults to `true` when omitted.
167
+ A side-thread shortcut always changes that side thread immediately.
168
+ When a fixed thinking level is selected and remembering is on, the concrete level is written for the next invocation; when off, `pi-btw.json` stays unchanged.
169
+ When **Same as main thread** is selected, shortcut changes stay local even when remembering is on.
170
+ If a shortcut write fails, the local change remains active and pi-btw warns that it was not remembered.
171
+ A failed Settings-screen save instead restores the previous displayed value.
172
+
173
+ A missing settings file is a side-effect-free read: pi-btw creates it only after a Settings change or a remembered shortcut change.
174
+ Saves are ordered within the Pi process and published atomically with a same-directory temporary file and rename.
175
+ They preserve `model` and unknown fields; malformed or invalid files block saves and remain unchanged.
176
+ Settings must be valid UTF-8 and no larger than 64 KiB, so unexpectedly large or invalidly encoded files are rejected without being rewritten.
177
+ Separate Pi processes and external editors are outside this in-process ordering boundary.
178
+ The file is read for each `/btw` invocation, so edits apply without `/reload`.
190
179
 
191
180
  ## 🧠 Why use pi-btw?
192
181
 
193
- Normal assistant messages become part of the main Pi conversation and can distract the coding agent from the task. `pi-btw` creates a lightweight side channel for context-aware questions, making it useful for pair programming, debugging, code review, and repository exploration.
182
+ Normal assistant messages become part of the main Pi conversation and can distract the coding agent from the task.
183
+ `pi-btw` creates a lightweight side channel for context-aware questions, making it useful for pair programming, debugging, code review, and repository exploration.
194
184
 
195
185
  ## 🗂️ Package layout
196
186
 
@@ -233,4 +223,5 @@ Pi extension, Pi coding agent, AI coding agent, side question command, agent cha
233
223
 
234
224
  ## 📄 License
235
225
 
236
- MIT. See [`LICENSE`](./LICENSE).
226
+ MIT.
227
+ See [`LICENSE`](./LICENSE).
package/dist/index.ts CHANGED
@@ -477,6 +477,9 @@ function escapeTerminalControls(text) {
477
477
 
478
478
  // src/fullscreen-ui.ts
479
479
  import { spawn } from "node:child_process";
480
+ import {
481
+ copyToClipboard as copyToHostClipboard
482
+ } from "@earendil-works/pi-coding-agent";
480
483
  import {
481
484
  TuiAltScreen,
482
485
  truncateToWidth as truncateToWidth2
@@ -498,7 +501,12 @@ var FullscreenUiDisposedError = class extends Error {
498
501
  }
499
502
  };
500
503
  async function runBtwFullscreen(ctx, run, dependencies = {}) {
501
- const createTui = dependencies.createTui ?? ((parent) => createBtwFullscreenTui(parent, dependencies.openUrl ?? openUrlInBrowser));
504
+ const createTui = dependencies.createTui ?? ((parent, theme) => createBtwFullscreenTui(
505
+ parent,
506
+ theme,
507
+ dependencies.openUrl ?? openUrlInBrowser,
508
+ dependencies.copyToClipboard ?? copyToHostClipboard
509
+ ));
502
510
  let liveEditorText = ctx.ui.getEditorText();
503
511
  let restoreEditor = false;
504
512
  const outcome = await ctx.ui.custom(
@@ -528,10 +536,21 @@ async function runBtwFullscreen(ctx, run, dependencies = {}) {
528
536
  if (outcome.kind === "failed") throw outcome.error;
529
537
  return outcome.value;
530
538
  }
531
- function createBtwFullscreenTui(parent, openUrl) {
539
+ function createBtwFullscreenTui(parent, theme, openUrl, copyToClipboard2) {
540
+ const styleSearchMatch = (text) => theme.bg("searchMatchBg", theme.fg("searchMatchText", text));
532
541
  return new TuiAltScreen(parent.terminal, parent.getShowHardwareCursor(), void 0, {
533
542
  mouse: true,
534
- openUrl
543
+ searchMatchStyle: (text) => theme.underline(styleSearchMatch(text)),
544
+ searchCurrentMatchStyle: (text) => theme.bold(theme.inverse(styleSearchMatch(text))),
545
+ openUrl,
546
+ copySelection: async (text) => {
547
+ try {
548
+ await copyToClipboard2(text);
549
+ return true;
550
+ } catch {
551
+ return false;
552
+ }
553
+ }
535
554
  });
536
555
  }
537
556
  function openUrlInBrowser(target) {
@@ -583,7 +602,7 @@ var BtwFullscreenHost = class {
583
602
  this.parent.stop({ preserveScreen: true });
584
603
  parentStopped = true;
585
604
  if (this.disposed) throw new FullscreenUiDisposedError();
586
- this.fullscreen = this.createTui(this.parent);
605
+ this.fullscreen = this.createTui(this.parent, this.theme);
587
606
  fullscreenCreated = true;
588
607
  this.fullscreen.start();
589
608
  outcome = { kind: "completed", value: await this.run(this.createContext()) };