pi-bro 0.13.2 → 0.15.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 (5) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +172 -20
  3. package/bro.ts +1302 -65
  4. package/package.json +8 -5
  5. package/prompt.ts +29 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,25 @@
2
2
 
3
3
  All notable changes to pi-bro are documented here.
4
4
 
5
+ ## [0.15.0] - 2026-09-22
6
+
7
+ ### Changed
8
+
9
+ - In the `/bro btw` modal, `/copy` and `/copy-all` now copy the latest answer or the full thread to the **system clipboard** instead of the main editor. New `/insert` and `/insert-all` commands (with `/insert!`/`/insert-all!` force variants to replace an existing main-editor draft) insert into the main editor without submitting. Removed the legacy `/send` aliases and the spaced `/copy all`/`/insert all` spellings.
10
+
11
+ ## [0.14.0] - 2026-09-20
12
+
13
+ ### Added
14
+
15
+ - `bro_advisor` — a tool the **executor agent** (not the human) can voluntarily call mid-task for a second opinion from a fresh, standalone Agy process with real, unsandboxed tool access in the workspace (`--dangerously-skip-permissions`). Bro automatically captures a harness-neutral snapshot of the executor's system instructions, active tools, and conversation so far (including tool calls and results) and sends it, plus an optional executor-supplied question, to the advisor. The advisor is instructed to investigate before advising and to leave edits to the executor, but that boundary is behavioral, not enforced. Invocation failures retry twice (5s, then 10s) with the identical snapshot before surfacing Agy's own diagnostic.
16
+ - `/bro advisor-steer` — an editor for one persistent, session-scoped steering brief the advisor always reads (e.g. "quick prototype; keep A and B careful, everything else minimal"). The brief is stored as session-only extension data, is never added to Pi's conversation or sent to the main model, persists across resume/reload, and is inherited by forks.
17
+ - `/bro advisor` — a quick notice of whether `bro_advisor` is currently exposed and active, pointing at `/bro config`, `/bro advisor-steer`, and `/bro doctor`. `/bro doctor` carries the full diagnostic, including the Agy 1.1.15+ compatibility check.
18
+ - `/bro config` — a production settings screen replacing the earlier configuration-only spike: a shared default model/effort (still owned by `/bro model`/`/bro effort`) plus optional per-capability overrides for `explain`, `show`, `btw`, and `advisor`. An override always pins both model and effort together and is only ever cleared by an explicit "Default" selection. Saves are serialized and coalesced, with in-place revert and an inline error notice on failure.
19
+
20
+ ### Changed
21
+
22
+ - Requires Earendil Pi `>=0.84.2 <1` and `agy >=1.1.15` (up from `>=0.78.1 <1` and `>=1.1.11`).
23
+
5
24
  ## [0.13.2] - 2026-09-19
6
25
 
7
26
  ### Changed
package/README.md CHANGED
@@ -11,7 +11,7 @@ with your selected model.
11
11
 
12
12
  ## Quick start
13
13
 
14
- You need Earendil Pi `>=0.78.1 <1`, Node.js `>=22.19.0`, and `agy >=1.1.11`
14
+ You need Earendil Pi `>=0.84.2 <1`, Node.js `>=22.19.0`, and `agy >=1.1.15`
15
15
  installed and available on your `PATH`. Run `agy` once in your terminal to sign
16
16
  in, then install Bro:
17
17
 
@@ -63,10 +63,13 @@ text directly captures a new source the same way.
63
63
  | `/bro show [n-turns] [query]` | Draw recent session turns (default last 1) as shapes instead of prose, from user and assistant conversation text only — tool calls, tool results, reasoning, and images are omitted. An optional query steers what the shapes focus on, with or without a leading turn count. |
64
64
  | `/bro doctor` | Check Bro's settings, Agy installation, account, model, effort, and mode. |
65
65
  | `/bro usage [--provider agy]` | Show current Agy resource limits. |
66
- | `/bro model [id]` | View or choose the Agy model. |
67
- | `/bro effort [low\|medium\|high]` | View or choose the supported reasoning effort. |
66
+ | `/bro model [id]` | View or choose the shared default Agy model. |
67
+ | `/bro effort [low\|medium\|high]` | View or choose the shared default reasoning effort. |
68
68
  | `/bro mode [brief\|balanced\|faithful]` | View or choose the explanation mode. |
69
+ | `/bro config` | Open an interactive settings screen for the shared default model/effort, explain mode, show turns, and per-capability (explain/show/btw/advisor) model and effort overrides. |
69
70
  | `/bro btw [--fresh] [--full] [question]` | Open a side conversation in a modal. Sandboxed (read-only) by default; `--full` lets it read and edit the workspace, `--fresh` skips main-session context. |
71
+ | `/bro advisor` | Quick notice of whether the executor's `bro_advisor` tool is available right now, pointing at `/bro config`, `/bro advisor-steer`, and `/bro doctor`. |
72
+ | `/bro advisor-steer` | View, edit, save, or clear the one persistent steering brief the advisor always sees. |
70
73
  | `/bro help` | Open the built-in quick reference. |
71
74
 
72
75
  Giving `/bro` the input directly works the same way:
@@ -108,25 +111,105 @@ your terminal mode; press **C** to copy the complete explanation reliably.
108
111
  `/bro btw` opens a separate multi-turn conversation in a modal, so you can ask
109
112
  a quick side question while the main agent keeps working. It runs through Agy,
110
113
  the same backend as the rest of Bro, and never adds anything to Pi's
111
- conversation unless you explicitly copy it into the editor.
114
+ conversation unless you explicitly insert it into the editor.
112
115
 
113
116
  - **Sandboxed by default**: the side conversation is read-only (no project
114
117
  access). Add `--full` to let it read and edit the workspace.
115
118
  - `/bro btw <question>` asks immediately; `/bro btw` opens an empty thread.
116
119
  - `--fresh` starts a thread without seeding the main session's recent
117
- conversation text.
120
+ conversation text. Reopening without an access flag preserves the existing
121
+ thread's access mode, including `--full`. Use `--sandbox` to return to sandbox
122
+ mode; changing access mode starts a new thread. `--fresh` alone does not reset
123
+ the access mode.
118
124
  - The first turn is seeded with up to the last 8 turns of user/assistant
119
125
  conversation text (40,000 characters max, with a truncation notice); the
120
126
  side agent can also read the repo itself when running in `--full` mode.
121
127
  - **In the modal**: type a question and press Enter (empty Enter re-asks the
122
- last question). `/copy` copies the latest answer into the main editor
123
- without submitting (use `/copy!` to replace an existing draft); `/copy-all`
124
- copies the full thread; `/retry` re-asks
125
- the last question; `/clear` resets the thread; Esc closes. A visible
126
- `full · edits repo` badge shows whenever `--full` mode is active.
128
+ last question). Composer actions trigger only on these exact commands:
129
+ - `/copy`: copies the latest answer to the system clipboard
130
+ - `/copy-all`: copies the full thread to the system clipboard
131
+ - `/insert`: inserts the latest answer into the main editor without submitting (use `/insert!` to replace an existing editor draft)
132
+ - `/insert-all`: inserts the full thread into the main editor without submitting (use `/insert-all!` to replace an existing editor draft)
133
+ - `/retry`: re-asks the last question (empty Enter does the same)
134
+ - `/clear`: resets the thread
135
+ Any other text or slash-prefixed input (such as `/send` or `/copy!`) is not a composer command and is submitted directly as a question to the side conversation. Esc closes the modal. A visible `full · edits repo` badge shows whenever `--full` mode is active.
127
136
  - The thread lives in memory only — it clears when you switch Pi sessions,
128
137
  reload extensions, or quit Pi.
129
138
 
139
+ ## Bro advisor
140
+
141
+ `bro_advisor` is a tool the **executor agent** — not you — can voluntarily
142
+ call mid-task for a second opinion. Unlike `/bro btw`, which is a side
143
+ conversation for you, `bro_advisor` is a tool for the model you're working
144
+ with; it shows up as a normal tool call/result in the transcript, not a
145
+ modal. It is registered like any other tool when the extension loads and has
146
+ no on/off switch of its own — whether the executor can actually call it
147
+ depends entirely on this host's own tool restrictions.
148
+
149
+ `/bro advisor` is a quick notice of whether `bro_advisor` is available right
150
+ now, pointing at `/bro config`, `/bro advisor-steer`, and `/bro doctor`.
151
+ `/bro doctor` has the full diagnostic: whether this host exposes and
152
+ activates `bro_advisor`, its resolved model/effort, steering presence, and
153
+ the Agy compatibility floor — it also checks the installed Agy version and
154
+ gives an `agy update` action when it is too old.
155
+
156
+ - **Automatic context, no prep needed**: the executor never assembles a
157
+ summary. Bro captures a harness-neutral snapshot — the executor's system
158
+ instructions, its active tools, and the conversation so far including tool
159
+ calls and results — and sends it, along with an optional `question` the
160
+ executor may pass, to a **fresh, standalone Agy process** for every
161
+ consultation. Nothing is resumed or reused across calls, including retries.
162
+ - **Instructed to investigate, not implement**: the advisor process has real
163
+ tool access in the workspace with permissions auto-approved
164
+ (`--dangerously-skip-permissions`) — there is no enforced read-only
165
+ isolation. It is instructed to verify claims itself and return advice,
166
+ leaving edits to the executor, but that boundary is a behavioral prompt
167
+ instruction rather than an enforced sandbox constraint, so treat its
168
+ findings as advice to verify, not a guaranteed hands-off review.
169
+ - **Steering**: `/bro advisor-steer` opens an editor for one persistent
170
+ steering brief — e.g. "quick prototype; keep A and B careful, everything
171
+ else minimal" — that the advisor always reads. **Ctrl+S** saves and keeps
172
+ the editor open; **Enter** or **Shift+Enter** inserts a newline; **Ctrl+K**
173
+ clears both the saved brief and draft while staying open; **Ctrl+C** copies
174
+ the entire current draft, including unsaved edits; and **Esc** closes without
175
+ saving unsaved edits. Actions and clipboard errors are reported inline. The
176
+ brief is stored as session-only extension data and is **never added to Pi's
177
+ conversation or sent to the main model** — the advisor is the only thing
178
+ that reads it.
179
+ - **Persistence**: the steering brief persists with the Pi session (not
180
+ globally, not per project) as custom extension data in the session file and
181
+ is restored on resume or reload. Forking a session inherits it; edits made
182
+ after the fork are independent of the original branch. The advisor tool has
183
+ no separate activation state persisted or toggled.
184
+ - **Retries**: on an invocation failure (not a completed answer — "I need
185
+ more evidence" is a normal result, not a failure), Bro retries with the
186
+ identical snapshot, steering, and question: once after 5 seconds, once more
187
+ after 10 seconds, then returns Agy's own diagnostic — including a
188
+ context-length error, verbatim — as the failure. Cancelling the tool call
189
+ aborts immediately and skips any pending retry wait.
190
+ - **Progress and provenance**: while an attempt is running, Bro parses the
191
+ advisor's own stream for the last thing it actually reported — either a
192
+ tool name or a user-facing response line (hidden reasoning is never
193
+ surfaced) — and shows it with how long ago it arrived, e.g. `last reported:
194
+ Read src/app.ts (2s ago)`. Before Agy reports anything, this reads
195
+ explicitly as "awaiting first activity from Agy" rather than guessing at
196
+ what it might be doing. This is what was actually reported, not a live
197
+ claim about Agy's current tool, and silence is never described as
198
+ "stalled". Expanding a running consultation shows up to the last 4 reported
199
+ activity lines. Each retry starts this trail over empty — a failed
200
+ attempt's activity never carries into the next one. Elapsed running time
201
+ still ticks once a second regardless of activity; a retry countdown with
202
+ the last failure is shown the same way as before. The returned answer
203
+ starts with model, effort, actual attempt count, duration, workspace,
204
+ steering presence, snapshot size, no-Bro-truncation status, and known
205
+ omission/compaction notes; the advisor's complete answer follows unchanged.
206
+ - **Model/effort**: resolved the same way as explain/show/btw, through
207
+ `/bro model`/`/bro effort` (shared default) or `/bro config` (per-capability
208
+ override).
209
+
210
+ See [docs/plans/2026-09-19-bro-advisor-design.md](docs/plans/2026-09-19-bro-advisor-design.md)
211
+ for the full design.
212
+
130
213
  ## Bro show
131
214
 
132
215
  Where the explanation modes rewrite dense prose in simpler words, `/bro show`
@@ -160,11 +243,14 @@ changed in the auth flow`. The query is used as a lens on the captured turns,
160
243
  not as additional evidence, and its casing is preserved as typed. Pressing
161
244
  **R** retries with the same turn count and query.
162
245
 
163
- Only the first word is ever read as the turn count — a query that starts with
164
- digits is not ambiguous. `/bro show 1 404 handler` captures 1 turn and steers
165
- on "404 handler"; `/bro show 404 handler` (no leading count) steers on the
166
- whole phrase "404 handler" using the default turn count, since "404" alone
167
- would be a count but "404 handler" is not.
246
+ Any leading whitespace-delimited word that looks like a number is treated as the
247
+ requested turn count: for example, `/bro show 3 what changed` captures 3 turns
248
+ and steers on "what changed", while `/bro show 404 handler` parses "404" as the
249
+ turn count and "handler" as the steering query. To steer on a phrase that starts
250
+ with digits while choosing a turn count, specify the turn count explicitly
251
+ first: `/bro show 1 404 handler` captures 1 turn and steers on "404 handler".
252
+ If the first word is not a number, the whole input is treated as the steering
253
+ query using the saved `showTurns` default.
168
254
 
169
255
  ### A slow session-create, traced
170
256
 
@@ -610,15 +696,41 @@ Bro creates this user-editable settings file when the extension loads:
610
696
  }
611
697
  ```
612
698
 
613
- Use `/bro model`, `/bro effort`, and `/bro mode` to update it from Pi, or edit
614
- it directly. Bro reads the file again before each explanation, so manual changes
699
+ `model` and `effort` are the **shared default**: explain, show, and btw all use
700
+ them unless a capability has its own override. An optional `overrides` object
701
+ adds per-capability overrides, each a full `{ "model": ..., "effort": ... }`
702
+ pair:
703
+
704
+ ```json
705
+ {
706
+ "model": "gemini-3.7-flash",
707
+ "effort": "low",
708
+ "mode": "balanced",
709
+ "showTurns": 1,
710
+ "overrides": {
711
+ "show": { "model": "gemini-3.7-pro", "effort": "high" }
712
+ }
713
+ }
714
+ ```
715
+
716
+ Use `/bro model`, `/bro effort`, and `/bro mode` to update the shared default
717
+ and mode from Pi, `/bro config` to review or change the shared default and any
718
+ per-capability (explain/show/btw/advisor) overrides interactively, or edit the file
719
+ directly. Bro reads the file again before each explanation, so manual changes
615
720
  apply to the next `/bro`. Use a model ID shown by `/bro model`; `effort` must be
616
721
  one of the levels shown by `/bro effort`. Models without adjustable effort use
617
722
  `default`. `mode` must be `brief`, `balanced`, or `faithful`; existing settings
618
723
  without it use `balanced`. `showTurns` is the default number of turns `/bro
619
- show` draws (default 1); `/bro show <n-turns>` overrides it for a single run. There
620
- is no `/bro showTurns` command — edit the file directly. The choices remain active across Pi restarts until
621
- you change them. `/bro help` shows the active settings and exact file path.
724
+ show` draws (default 1); `/bro show <n-turns>` overrides it for a single run. Settings
725
+ written before per-capability overrides existed load unchanged, with no overrides. The choices remain
726
+ active across Pi restarts until you change them. `/bro help` shows the active
727
+ settings, any overrides, and the exact file path.
728
+
729
+ `/bro config`'s changes save immediately as you make them. Pressing Esc inside
730
+ a model or effort picker cancels that pick without changing anything; pressing
731
+ Esc on the settings screen itself just closes it, keeping whatever was already
732
+ saved. If a save fails (for example, a read-only settings file), the screen
733
+ shows the error inline instead of losing the change silently.
622
734
 
623
735
  If `PI_CODING_AGENT_DIR` is set, the file lives there instead. `PI_BRO_MODEL`
624
736
  chooses the initial model only when Bro creates a missing settings file:
@@ -627,6 +739,23 @@ chooses the initial model only when Bro creates a missing settings file:
627
739
  PI_BRO_MODEL=gemini-3.7-flash-low pi
628
740
  ```
629
741
 
742
+ ### Configuration precedence
743
+
744
+ When resolving model and reasoning effort:
745
+ 1. **Per-capability override**: If configured under `overrides.<capability>` (`explain`, `show`, `btw`, or `advisor`) in `bro-settings.json`, that capability pins its own `{ "model": ..., "effort": ... }` pair and ignores the shared default.
746
+ 2. **Shared default**: If no override is set for that capability, it inherits the root `model` and `effort` in `bro-settings.json`.
747
+ 3. **Catalog normalization**: Bro normalizes the resolved `{ model, effort }` against Agy's installed model catalog (mapping suffixed variant IDs and handling fixed-effort models).
748
+ 4. **Initial file creation only**: `PI_BRO_MODEL` selects the initial default model only when Bro creates a missing `bro-settings.json` file. It has no effect once the file exists.
749
+
750
+ When resolving turn count for `/bro show`:
751
+ 1. **Command argument**: An explicit count like `/bro show 3` or `/bro show 1 query` overrides for that single execution.
752
+ 2. **Saved setting**: `showTurns` in `bro-settings.json` (defaults to 1; configurable interactively via `/bro config` or direct file edit).
753
+
754
+ When resolving explanation prompt (`explain` capability only):
755
+ 1. **Custom prompt**: `~/.pi/agent/bro-prompt.md` (or `$PI_CODING_AGENT_DIR/bro-prompt.md`), if present and valid (`{{response}}` exactly once), completely overrides all built-in modes.
756
+ 2. **Saved mode**: `mode` in `bro-settings.json` (`brief`, `balanced`, or `faithful`; defaults to `balanced`).
757
+ 3. Note: `bro-prompt.md` applies only to `/bro`, `/bro text`, `/bro file`, and `/bro url`; it does not affect `/bro show`, `/bro btw`, or `bro_advisor`.
758
+
630
759
  ## Custom prompt
631
760
 
632
761
  Bro uses a built-in prompt by default. To use your own, create:
@@ -705,6 +834,24 @@ run `/bro doctor` for the exact problem.
705
834
  according to their own settings and privacy policies.
706
835
  - **Clipboard**: Pressing **C** copies the text to your system clipboard, where
707
836
  your operating system or clipboard manager may retain it.
837
+ - **Advisor requests**: `bro_advisor` sends the executor agent's system
838
+ instructions, active tool list (excluding `bro_advisor`), ordered
839
+ conversation history including tool calls and tool results (unlike Show, which
840
+ omits them), human steering brief, and the executor's optional question to
841
+ Agy and your configured model provider. Reasoning and image bodies are
842
+ omitted with explicit markers (`[reasoning omitted]`, `[image omitted]`).
843
+ - **Advisor tool execution & safety boundary**: The advisor process runs
844
+ directly in your workspace (`cwd`) with auto-approved permissions
845
+ (`--dangerously-skip-permissions`). It has real tool access (file reading,
846
+ search, command execution). The directive to only advise and leave edits to
847
+ the executor is a **behavioral prompt instruction**, not an enforced sandbox
848
+ or security boundary. Treat its findings as advice to verify before applying.
849
+ - **Advisor steering persistence**: The steering brief is saved as
850
+ session-scoped custom extension data (`bro-advisor-steering`) in the session
851
+ file. It persists across session resume and reload, and is inherited on
852
+ session fork (post-fork edits on branches remain independent). It is never
853
+ sent to the main model or added to Pi's conversation. The advisor tool has
854
+ no separate activation state.
708
855
 
709
856
  ## Troubleshooting and current limits
710
857
 
@@ -724,6 +871,11 @@ tool before giving it to Bro.
724
871
  - `/bro btw` threads are memory-only and do not survive reloads or restarts.
725
872
  The side conversation needs Agy's `--conversation` resume support; sandbox
726
873
  mode caps a turn at 2 minutes and full mode at 10 minutes.
874
+ - `bro_advisor` requires Agy CLI `>=1.1.15` (for `--input-format stream-json`).
875
+ Consultations run directly in the workspace with auto-approved permissions
876
+ without enforced file-modification isolation; an attempt is capped at 10
877
+ minutes (`--print-timeout 10m`) and retries up to 2 times on invocation
878
+ failure (5-second, then 10-second backoff).
727
879
  - Show captures only the conversation text of what already happened in the
728
880
  current session — the last few turns' user and assistant messages, with
729
881
  tool calls, tool results, reasoning, and images always omitted; it cannot