pi-bro 0.22.0 → 0.22.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,1024 +1,153 @@
1
1
  # pi-bro
2
2
 
3
- Turn a dense AI reply, pasted text, document or webpage into a plain-language explanation. Ask side questions with `/bro btw`, or understand and assess a PR with `/bro guided-review`. These windows do not add their discussions to your main agent's context.
3
+ [![npm](https://img.shields.io/npm/v/pi-bro)](https://www.npmjs.com/package/pi-bro)
4
+ [![tests](https://img.shields.io/github/actions/workflow/status/tranhoangnguyen03/pi-bro/publish.yml?branch=main&label=tests)](https://github.com/tranhoangnguyen03/pi-bro/actions/workflows/publish.yml)
5
+ [![node](https://img.shields.io/node/v/pi-bro)](package.json)
6
+ [![license](https://img.shields.io/npm/l/pi-bro)](LICENSE)
4
7
 
5
- `pi-bro` is an extension for [Earendil Pi](https://github.com/earendil-works/pi).
6
- Experimental [PiG 0.3.0 compatibility](docs/pig-compatibility.md) uses the same npm package and requires Node.js. See the compatibility notes for verified coverage and the PiG RPC tool-exclusion limitation.
7
- It opens explanations in a separate modal and runs them through a CLI backend
8
- you already have installed and signed in to. Explain, show, BTW and advisor use five backend adapters. Guided Review generation/questions require Claude or Muse file-only inspection (live verification varies by feature):
8
+ A sidekick for you and your Pi agent: plain-language explanations, side
9
+ questions, diagrams, PR review and second opinions, without cluttering the
10
+ agent's context.
9
11
 
10
- - [Google Antigravity CLI](https://antigravity.google/docs/cli-install) (`agy`) — the default for new settings
11
- - [Claude Code](https://docs.anthropic.com/en/docs/claude-code) (`claude`)
12
- - Grok Build (`grok`)
13
- - [Codex CLI](https://github.com/openai/codex) (`codex`)
14
- - Meta Muse Code (`muse`)
15
-
16
- You only need the backend(s) you select; Agy is the default, not a requirement.
17
-
18
- ## Quick start
19
-
20
- You need Earendil Pi `>=0.84.2 <1`, Node.js `>=22.19.0`, and at least one
21
- backend CLI on your `PATH`, signed in once from your terminal (`agy >=1.1.15`
22
- for the default). Then install Bro:
23
-
24
- ```sh
25
- pi install npm:pi-bro
26
- ```
27
-
28
- Restart Pi or run `/reload`. If you use Claude Code, Grok, Codex, or Muse instead of Agy,
29
- choose it in `/bro config` (for everything, or per feature). Then try:
30
-
31
- ```text
32
- /bro
33
- /bro text Paste text here
34
- /bro file docs/report.pdf
35
- /bro url https://example.com/article
36
- /bro btw "what file defines this route?"
37
- ```
38
-
39
- Run `/bro doctor` after installation or whenever Bro is not working.
40
-
41
- To install from GitHub instead, use
42
- `pi install git:github.com/tranhoangnguyen03/pi-bro`. To try Bro without
43
- installing it, use `pi -e npm:pi-bro`.
44
-
45
- ## What Bro can explain
46
-
47
- | Source | Command | What Bro does |
48
- | --- | --- | --- |
49
- | Latest assistant reply | `/bro` | Explains the latest completed reply without adding the result to the conversation. |
50
- | Pasted text | `/bro text <text>` | Explains text supplied directly in the command. |
51
- | Local document | `/bro file <path>` | Extracts text from a workspace-local Markdown, text, PDF, or DOCX file. |
52
- | Public webpage | `/bro url <url>` | Fetches one public HTML page and extracts its main readable content. |
53
- | Recent session turns | `/bro show` | Draws the last turns' conversation text as shapes instead of prose (tool calls, tool results, reasoning, and images are omitted). |
54
- | Any of the above, auto-detected | `/bro <input>` | Routes a lone URL to the webpage reader, an existing workspace file with a supported extension to the document reader, and anything else to pasted text. |
55
-
56
- Pressing **R** simplifies the captured source again in the mode shown, and
57
- **M** re-simplifies it in the next mode. These commands capture a
58
- new source: `/bro text`, `/bro file`, `/bro url`, and `/bro show`. Giving `/bro` a URL, path, or
59
- text directly captures a new source the same way.
60
-
61
- ## Commands
62
-
63
- | Command | Description |
64
- | --- | --- |
65
- | `/bro`, `/bro simplify`, or `/bro text` | Explain the latest completed assistant response. |
66
- | `/bro text <text>` | Explain pasted text. |
67
- | `/bro <input>` | Explain it directly: a lone URL runs the webpage reader, an existing workspace file with a supported extension runs the document reader, and anything else is pasted text. A quoted path with spaces is routed too when the file exists. |
68
- | `/bro file <path>` | Explain a workspace-local `.md`, `.markdown`, `.txt`, `.pdf`, or `.docx` file. |
69
- | `/bro url <url>` | Explain one public, text-based webpage. |
70
- | `/bro open` | Reopen the latest explanation without calling the simplifier again. |
71
- | `/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. |
72
- | `/bro doctor` | Check Bro's settings, preferences, and each selected backend, with the effective backend/model/effort per feature. |
73
- | `/bro mode [brief\|balanced\|faithful]` | View or choose the explanation mode. |
74
- | `/bro preferences` | View or edit what Bro knows about you and how you like answers; see [Preferences](#preferences). |
75
- | `/bro config` | Open settings for the shared backend/model/effort, explain mode, show turns, and per-capability (explain/show/btw/advisor/review) overrides. |
76
- | `/bro guided-review <PR number or URL>` | Automatically explain and assess a captured PR in local Pi, with source evidence and optional private questions. Opening the same PR restores its saved review. |
77
- | `/bro guided-review` | List saved reviews; `resume` is an alias. |
78
- | `/bro btw [question]` | Open a side conversation in a modal, seeded with recent main-session context. Starts conversation-only; type `/mode` inside to toggle full permission (read and edit the workspace) without losing the thread. |
79
- | `/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`. |
80
- | `/bro advisor-steer` | View, edit, save, or clear the one persistent steering brief the advisor always sees. |
81
- | `/bro help` | Open the built-in quick reference. |
82
-
83
- Giving `/bro` the input directly works the same way:
84
-
85
- ```text
86
- /bro https://example.com/article
87
- /bro docs/report.pdf
88
- /bro any other text is explained as pasted text
89
- ```
90
-
91
- ## Explanation modes
92
-
93
- Bro treats the source as data, rejects embedded instructions, preserves its
94
- language (unless your [preferences](#preferences) name another), and avoids
95
- adding facts, advice, or conclusions in every mode. Choose
96
- a persistent mode with `/bro mode`, or press **M** in an explanation to try the
97
- next mode without saving it:
98
-
99
- - **`brief`**: Uses the original audience-led ELI-simpleton prompt with no fixed
100
- word target.
101
- - **`balanced`**: The default. Keeps important details, conditions, warnings,
102
- context, code, and formatting while trimming fluff and repetition.
103
- - **`faithful`**: Simplifies the language while preserving every claim,
104
- qualification, warning, number, command, code block, and formatting choice.
105
-
106
- ### Modal controls
107
-
108
- - **Mouse wheel / trackpad**: Scroll in regular or fullscreen mode
109
- - **↑ / ↓**: Scroll in any mode
110
- - **C**: Copy the complete explanation
111
- - **R**: Simplify the captured source again in the mode shown, or run the
112
- current Doctor check again
113
- - **M**: Re-simplify the captured source in the next mode (brief → balanced →
114
- faithful → brief). It applies to this explanation only and does not change
115
- the mode saved by `/bro mode`; pressing it again while Bro is working skips
116
- ahead
117
- - **O**: Open the HTML diagram when a show reply contains one
118
- - **Esc**: Close the modal, or cancel while Bro is working
119
-
120
- The modal header shows the model and reasoning effort the explanation or
121
- drawing used (`default` when the model's own effort applies), followed by the
122
- explanation mode, and `prefs` when your [preferences](#preferences) shaped the
123
- result; `/bro open` keeps the original labels, mode, and tag.
124
-
125
- Bro temporarily captures mouse input while its modal is open. Native mouse
126
- selection may be unavailable or visually extend outside the modal depending on
127
- your terminal mode; press **C** to copy the complete explanation reliably.
128
-
129
-
130
- ## Guided Review
131
-
132
- **Open a PR → read and investigate → leave when satisfied.** Bro automatically
133
- prepares an explanation and quality assessment with captured code, ranked
134
- findings and suggested fixes/checks. Asking private questions is optional.
135
- Nothing is automatically applied or posted to GitHub.
136
-
137
- ```text
138
- /bro guided-review https://github.com/OWNER/REPO/pull/123
139
- ```
140
-
141
- Local Pi interactive mode, Git, authenticated `gh`, and Claude or Muse configured for review
142
- are required. A new review makes a model call using that backend's account.
143
- Opening the same PR restores its saved review without another model call.
144
- Bare `/bro guided-review` lists saved reviews (`resume` is an alias).
145
-
146
- Contents holds Overview, topics, Findings, Coverage, Changed files and Your
147
- questions. Enter opens an item or focuses contextual actions; Enter on a
148
- highlighted action activates it. Tab changes focus. Esc goes back, then closes
149
- from Contents. Your work is saved automatically; **Close** leaves normally,
150
- and **Stop and close** cancels active work before saving and leaving.
151
-
152
- Optional conveniences:
153
- - **Copy finding** copies its text to the system clipboard; nothing is submitted.
154
- - **Regenerate guide** makes a confirmed model call about the **same captured
155
- revision**. Success replaces the guide while retaining original private
156
- discussion context; failure keeps it. It does not fetch later commits.
157
-
158
- Reopening also keeps the captured revision, even after the author pushes.
159
- Findings are model suggestions, not approval; citations show real locations,
160
- not proof of the conclusion. Review uses Claude or Muse file-only inspection: shell, writes and web tools are disabled. Agy/Grok/Codex review requests fail clearly; choose a review override in `/bro config`. Saved reviews remain readable with any selection. Repository text can still mislead the model; tool restrictions are not proof of its conclusions.
161
- Use `pi --tui-mode fullscreen` for pane-local wheel scrolling.
162
-
163
- See the [Guided Review guide](docs/guided-review.md) for controls, calls and
164
- costs, source freshness, access/privacy, local storage, recovery and current
165
- scope. Notes, examination tracking, branch/update review and feedback delivery
166
- are not implemented.
167
-
168
- ## Bro btw (side conversation)
169
-
170
- `/bro btw` opens a separate multi-turn conversation in a modal, so you can ask
171
- a quick side question while the main agent keeps working. It runs through the
172
- selected backend and never adds anything to Pi's conversation unless you
173
- explicitly insert it into the editor. Bro writes for a tired reader: the point
174
- first, in plain words, at a length that fits the question, in the language you
175
- asked in.
176
-
177
- - `/bro btw <question>` asks immediately; `/bro btw` opens an empty thread.
178
- Reopening keeps the thread and its mode.
179
- - **Two modes, toggled with `/mode`**: a new thread starts
180
- **conversation-only**; type `/mode` in the modal to switch to **full
181
- permission** (read and edit the workspace, run commands) and again to switch
182
- back. The conversation is kept across switches. How conversation-only is
183
- enforced depends on the backend — see
184
- [Backends: access and retention](#backends-access-and-retention).
185
- - Every new or cleared thread is seeded with up to the last 8 turns of
186
- main-session user/assistant conversation text (40,000 characters max, with a
187
- truncation notice). Later turns continue the backend's native session; in
188
- full permission mode the side agent can also read the repo itself.
189
- - **In the modal**: type a question and press Enter (empty Enter re-asks the
190
- last question). Composer actions trigger only on these exact commands:
191
- - `/mode`: toggles conversation-only / full permission, keeping the thread
192
- - `/copy`: copies the latest answer to the system clipboard
193
- - `/copy-all`: copies the full thread to the system clipboard
194
- - `/insert`: inserts the latest answer into the main editor without submitting
195
- - `/insert-all`: inserts the full thread into the main editor without submitting
196
- - `/retry`: re-asks the last question (empty Enter does the same)
197
- - `/clear`: resets the thread
198
-
199
- `/insert` and `/insert-all` never replace an existing main-editor draft:
200
- edit or clear it first. Any other text, including other slash-prefixed input
201
- such as `/send`, is sent as a question. Esc closes the modal (or cancels a
202
- running turn). The header shows the model and reasoning effort the latest
203
- turn used (`default` when the model's own effort applies) and the current
204
- mode.
205
- - A turn is capped at 2 minutes in conversation-only mode and 10 minutes in
206
- full permission mode.
207
- - Bro's thread lives in memory only — it clears when you switch Pi sessions,
208
- reload extensions, or quit Pi. Changing the BTW backend starts a fresh
209
- thread; native session IDs never cross backends.
210
-
211
- ## Bro advisor
212
-
213
- `bro_advisor` is a tool the **executor agent** — not you — can voluntarily
214
- call mid-task for a second opinion. Unlike `/bro btw`, which is a side
215
- conversation for you, `bro_advisor` is a tool for the model you're working
216
- with; it shows up as a normal tool call/result in the transcript, not a
217
- modal. It is registered like any other tool when the extension loads and has
218
- no on/off switch of its own — whether the executor can actually call it
219
- depends entirely on this host's own tool restrictions.
220
-
221
- `/bro advisor` is a quick notice of whether `bro_advisor` is available right
222
- now, pointing at `/bro config`, `/bro advisor-steer`, and `/bro doctor`.
223
- `/bro doctor` has the full diagnostic: whether this host exposes and
224
- activates `bro_advisor`, its resolved backend/model/effort, steering
225
- presence, and backend compatibility (for Agy, a minimum CLI version with an
226
- `agy update` action when it is too old).
227
-
228
- - **Automatic context, no prep needed**: the executor never assembles a
229
- summary. Bro captures a harness-neutral snapshot — the executor's system
230
- instructions, its active tools, and the conversation so far including tool
231
- calls and results — and sends it, along with an optional `question` the
232
- executor may pass, to a **fresh, standalone process of the selected
233
- backend** for every consultation. Nothing is resumed or reused across calls,
234
- including retries.
235
- - **Instructed to investigate, not implement**: the advisor process has real
236
- tool access in the workspace with permissions auto-approved — there is no
237
- enforced read-only isolation. It is instructed to verify claims itself and
238
- return advice, leaving edits to the executor, but that boundary is a
239
- behavioral prompt instruction rather than an enforced sandbox constraint, so
240
- treat its findings as advice to verify, not a guaranteed hands-off review.
241
- - **Steering**: The advisor reads standing priorities from `~/.pi/agent/bro-advisor.md`
242
- and session-specific priorities from `/bro advisor-steer`.
243
- `/bro advisor-steer` opens an editor for one session steering brief — e.g. "quick
244
- prototype; keep A and B careful, everything else minimal". Session steering takes
245
- precedence over durable standing defaults where they conflict.
246
- **Ctrl+S** saves and keeps the editor open; **Enter** or **Shift+Enter** inserts
247
- a newline; **Ctrl+K** clears both the saved session brief and draft while staying open;
248
- **Ctrl+C** copies the entire current draft, including unsaved edits; and **Esc**
249
- closes without saving unsaved edits. Neither steering source is **ever added to Pi's
250
- conversation or sent to the main model** — the advisor is the only thing that reads them.
251
- - **Persistence**: standing defaults persist across all sessions in `~/.pi/agent/bro-advisor.md`.
252
- The session steering brief persists with the Pi session as custom extension data in the
253
- session file (`bro-advisor-steering`) and is restored on resume or reload. Forking a
254
- session inherits session steering; edits made after the fork are independent of the
255
- original branch. Clearing session steering leaves durable steering intact.
256
- - **Retries**: on an invocation failure (not a completed answer — "I need
257
- more evidence" is a normal result, not a failure), Bro retries with the
258
- identical snapshot, steering, and question: once after 5 seconds, once more
259
- after 10 seconds, then returns the backend's own diagnostic — including a
260
- context-length error, verbatim — as the failure. Host-imposed deadlines and
261
- cancellation are terminal: neither starts another attempt or a backoff wait.
262
- The advisor CLI is asked to stop at 10 minutes; Bro's host deadline is 610
263
- seconds plus bounded process cleanup. A CLI-reported error before that host
264
- deadline remains an invocation failure and can be retried.
265
- Cancelling the tool call skips any pending retry wait.
266
- - **Progress and provenance**: while an attempt is running, Bro shows the last
267
- thing the advisor actually reported — a tool name or a user-facing response
268
- line, never hidden reasoning — and how long ago it arrived, e.g. `last
269
- reported: Read src/app.ts (2s ago)`, or "awaiting first activity" before
270
- anything arrives. Expanding a running consultation shows up to the last 4
271
- reported activity lines; each retry starts the trail over. The returned
272
- answer starts with backend, model, effort, attempt count, duration,
273
- workspace, steering presence, snapshot size, and known omission/compaction
274
- notes; the advisor's complete answer follows unchanged.
275
- - **Model/effort**: resolved the same way as explain/show/btw, through
276
- `/bro config` (shared default or per-capability override).
277
-
278
- ## Bro show
279
-
280
- Where the explanation modes rewrite dense prose in simpler words, `/bro show`
281
- changes the form: it draws what you and the assistant said in the last few
282
- session turns as a shape instead of paragraphs. Capture keeps only user and
283
- assistant conversation text, including every intermediate assistant message in
284
- a turn — tool calls, tool results, reasoning, and images never leave the
285
- session. It runs the same backend-specific model call and shows the result
286
- in the same modal, never touching your conversation. `/bro show` uses its own
287
- draw prompt; the explanation modes do not affect it. Your
288
- [preferences](#preferences) can change only its wording and language, never
289
- which shapes it draws or its rules.
290
-
291
- Because the draw model only ever sees conversation text, its shapes reflect
292
- what was *reported* in the conversation — what the assistant said it did or
293
- found — not an independent check against the actual code or system state.
294
-
295
- Shapes are terminal-first: pseudocode, call trees, file trees, component
296
- trees, diffs, and types and signatures. Bro picks the shape from what
297
- happened in the session; there is no flag to request a specific one. When the source has no code structure
298
- to draw, Bro falls back to a plain outline instead of forcing a diagram. A
299
- reply that ends in one self-contained HTML block — layout, a state comparison,
300
- anything where position itself carries meaning — is written to a file and
301
- opened with **O**.
302
-
303
- Pressing **R** redraws the same captured turns; running `/bro show` again
304
- captures the latest turns afresh. `/bro show <n-turns>` overrides the default turn
305
- count for a single run.
306
-
307
- Add a query to steer what the shapes focus on, either after a turn count or on
308
- its own: `/bro show what changed in the auth flow`, or `/bro show 3 what
309
- changed in the auth flow`. The query is used as a lens on the captured turns,
310
- not as additional evidence, and its casing is preserved as typed. Pressing
311
- **R** retries with the same turn count and query.
312
-
313
- Any leading whitespace-delimited word that looks like a number is treated as the
314
- requested turn count: for example, `/bro show 3 what changed` captures 3 turns
315
- and steers on "what changed", while `/bro show 404 handler` parses "404" as the
316
- turn count and "handler" as the steering query. To steer on a phrase that starts
317
- with digits while choosing a turn count, specify the turn count explicitly
318
- first: `/bro show 1 404 handler` captures 1 turn and steers on "404 handler".
319
- If the first word is not a number, the whole input is treated as the steering
320
- query using the saved `showTurns` default.
321
-
322
- ### A slow session-create, traced
323
-
324
- The agent followed a two-second delay from the handler down to the worker.
325
- `/bro show` drew the chain:
326
-
327
- ```text
328
- handleCreateSession(req)
329
- ├── validateRequest(req)
330
- ├── SessionStore.insert(req.body)
331
- └── publish('session.created')
332
- └── AgentWorker.run(sessionId)
333
- ├── loadContext(sessionId)
334
- ├── callModel(context)
335
- └── persistResult(sessionId, result)
336
- ```
337
-
338
- ### The shape of code before it exists
339
-
340
- A design discussion agreed on the data model ahead of implementation. `/bro
341
- show` kept just the shape:
342
-
343
- ```typescript
344
- interface Item { id: ItemId; parentId: ItemId | null }
345
- interface Cursor { position: ItemId; direction: 'up' | 'down' }
346
- function resolveTarget(items: Item[], cursor: Cursor): ItemId | null
347
- ```
348
-
349
- ### A layout that collapses at narrow widths
350
-
351
- Text can't show a before/after layout change at a glance, so Bro ends with one
352
- self-contained HTML block and **O** opens it (the terminal first shows
353
- `[HTML diagram saved — press O to open]`):
12
+ `pi-bro` is an extension for [Pi](https://github.com/earendil-works/pi), a
13
+ terminal coding agent. Bro opens its answers in a separate window and runs
14
+ them through an AI coding CLI you already have and are signed in to, so your
15
+ main agent's conversation stays clean. Before you install, see
16
+ [what leaves your machine](#what-leaves-your-machine).
354
17
 
355
18
  <p align="center">
356
- <a href="https://raw.githubusercontent.com/tranhoangnguyen03/pi-bro/main/docs/images/bro-show-layout.png">
357
- <img alt="A before/after dashboard grid" src="https://raw.githubusercontent.com/tranhoangnguyen03/pi-bro/main/docs/images/bro-show-layout.png" width="720">
19
+ <a href="https://raw.githubusercontent.com/tranhoangnguyen03/pi-bro/main/docs/images/bro-response-after.png">
20
+ <img alt="A dense agent reply explained in plain language in the Bro window" src="https://raw.githubusercontent.com/tranhoangnguyen03/pi-bro/main/docs/images/bro-response-after.png" width="820">
358
21
  </a>
359
22
  </p>
360
23
 
361
- <details>
362
- <summary><strong>More shapes</strong></summary>
363
-
364
- **Diff** — what changed in the save handler:
365
-
366
- ```diff
367
- on(save)
368
- - write content
369
- + if content is unchanged
370
- + return cached result
371
- + write new content
372
- + invalidate cache
373
- ```
374
-
375
- **File layout** — where everything lives, for a refactor:
376
-
377
- ```text
378
- src/
379
- ├── commands/ # user intents
380
- │ ├── registry.ts
381
- │ └── show-me.ts
382
- ├── sessions/ # state and lifecycle
383
- │ ├── events.ts
384
- │ ├── store.ts
385
- │ └── worker.ts
386
- ├── transport/ # API
387
- │ ├── client.ts
388
- │ └── stream.ts
389
- └── config.ts # root config
390
- ```
391
-
392
- **Component tree** — what the session page renders:
393
-
394
- ```text
395
- SessionPage (apps/example/src/routes/session.tsx)
396
- ├── [hook] useSessionEvents
397
- ├── SessionToolbar (packages/ui)
398
- │ └── RunSkillButton
399
- └── SessionTimeline (packages/ui)
400
- └── SkillResultCard
401
- ```
402
-
403
- **Pseudocode** — how scroll capture and restore work:
404
-
405
- ```text
406
- capture(blocks, targetRect, scrollRect) -> Snapshot:
407
- target = focusedBlock ?? firstBlockIntersectingViewportTop(blocks)
408
- anchor = wholeBlockAnchor(blocks, target)
409
- offset = targetRect.top - scrollRect.top
410
- return { anchor, offset, scrollTop, revision + 1 }
411
-
412
- restore(snapshot, blocks) -> number:
413
- placement = resolveAnchor(blocks, snapshot.anchor)
414
- if placement: return scrollRect.top + targetRect.top - snapshot.offset
415
- return snapshot.scrollTop
416
- ```
417
-
418
- </details>
419
-
420
- Prose in, outline out: a purely conversational session with no code to draw
421
- degrades to a plain outline of the discussion — never a forced diagram.
422
-
423
- ## Bro in action
424
-
425
- ### Assistant response
426
-
427
- **Before `/bro`: the original agent response**
428
-
429
- [![A dense assistant response before Bro](https://raw.githubusercontent.com/tranhoangnguyen03/pi-bro/main/docs/images/bro-response-before.png)](https://raw.githubusercontent.com/tranhoangnguyen03/pi-bro/main/docs/images/bro-response-before.png)
430
-
431
- **After `/bro`: the plain-language explanation**
432
-
433
- [![The assistant response explained in the Bro modal](https://raw.githubusercontent.com/tranhoangnguyen03/pi-bro/main/docs/images/bro-response-after.png)](https://raw.githubusercontent.com/tranhoangnguyen03/pi-bro/main/docs/images/bro-response-after.png)
434
-
435
- ### Local document
436
-
437
- **Before: a complex PDF**
438
-
439
- [![A complex PDF before Bro](https://raw.githubusercontent.com/tranhoangnguyen03/pi-bro/main/docs/images/bro-file-before.png)](https://raw.githubusercontent.com/tranhoangnguyen03/pi-bro/main/docs/images/bro-file-before.png)
440
-
441
- **After `/bro file <path>`**
442
-
443
- [![The PDF explained in the Bro modal](https://raw.githubusercontent.com/tranhoangnguyen03/pi-bro/main/docs/images/bro-file-after.png)](https://raw.githubusercontent.com/tranhoangnguyen03/pi-bro/main/docs/images/bro-file-after.png)
444
-
445
- Bro optimizes for understanding, not simply for fewer words. The written
446
- examples below are synthetic and were run through Bro's default prompt. Click
447
- a screenshot to see it at full size.
448
-
449
- <details>
450
- <summary><strong>Short:</strong> TypeScript says a value is <code>never</code></summary>
451
-
452
- ### Before
453
-
454
- > The diagnostic is not evidence that `result` is intrinsically uninhabitable; it is an artifact of TypeScript's intraprocedural control-flow graph. Because `result` is initialized to `null` and only assigned inside the `forEach` callback, the outer scope does not model that callback as definitely executing. After `if (!result) return`, the remaining branch therefore collapses to `never`, producing the property-access error. Replace the callback-driven mutation with a control-flow-visible loop: `for (const item of items) { if (matches(item)) { result = item; break; } }`. Alternatively, derive the value expressionally with `const result = items.find(matches);` and guard `if (!result) return;` before dereferencing it. Avoid an `as Result` assertion; it suppresses the symptom while preserving the unsound state transition that caused the narrowing anomaly.
455
-
456
- ### After `/bro`
457
-
458
- #### What happened
24
+ ## Install
459
25
 
460
- TypeScript showed an error saying your variable `result` cannot be used or
461
- accessed.
462
-
463
- This happened because `result` was set to `null` outside a `forEach` loop and
464
- then modified inside the loop's helper function (callback). TypeScript tracks
465
- code line by line. It cannot guarantee that code inside a `forEach` callback
466
- will actually run.
467
-
468
- #### What it means
469
-
470
- Because TypeScript assumes the loop's callback might not run:
471
-
472
- 1. It believes `result` might still be `null`.
473
- 2. When you check `if (!result) return`, TypeScript thinks `result` can never
474
- hold a valid value after that check.
475
- 3. It sets the type of `result` to `never` (a special type meaning "impossible
476
- value") and blocks you from reading its properties.
477
-
478
- The error is a limitation in how TypeScript analyzes loops with callbacks.
479
-
480
- #### What you should do next
481
-
482
- Choose one of these two fixes:
483
-
484
- **Option 1: Use a standard `for...of` loop**
485
-
486
- ```ts
487
- for (const item of items) {
488
- if (matches(item)) {
489
- result = item;
490
- break;
491
- }
492
- }
493
- ```
494
-
495
- **Option 2: Use `Array.prototype.find`**
496
-
497
- ```ts
498
- const result = items.find(matches);
499
- if (!result) return;
500
- ```
501
-
502
- Avoid using a type assertion like `as Result`. It only hides the warning
503
- without fixing the underlying tracking issue.
504
-
505
- </details>
506
-
507
- <details>
508
- <summary><strong>Medium:</strong> Login succeeds, but the user is still logged out</summary>
509
-
510
- ### Before
511
-
512
- > What you're observing is almost certainly not an authentication failure in the credential-validation layer; it is a post-authentication session persistence failure caused by an environment-dependent mismatch between Express's transport-security inference and the TLS termination topology in production.
513
- >
514
- > Locally, Express sees the request as plain HTTP and your development cookie configuration probably permits a non-secure session cookie. In production, however, the browser connects over HTTPS to Nginx, a load balancer, or an ingress controller, which terminates TLS and forwards the request to Node over HTTP. Unless Express trusts that intermediary, `req.secure` remains `false`, even when the proxy sends `X-Forwarded-Proto: https`. With `cookie.secure: true`, `express-session` can consequently decline to emit the cookie because it believes the originating request was insecure. The login route may still return `200`, making the credentials appear accepted, but the next request has no session identifier and is treated as anonymous.
515
- >
516
- > Configure proxy trust before registering session middleware, and make the cookie policy reflect whether the frontend and API are same-site or genuinely cross-site:
517
- >
518
- > ```js
519
- > app.set("trust proxy", 1);
520
- >
521
- > app.use(session({
522
- > secret: process.env.SESSION_SECRET,
523
- > resave: false,
524
- > saveUninitialized: false,
525
- > proxy: true,
526
- > cookie: {
527
- > httpOnly: true,
528
- > secure: true,
529
- > sameSite: "lax",
530
- > maxAge: 1000 * 60 * 60 * 24
531
- > }
532
- > }));
533
- > ```
534
- >
535
- > Use `sameSite: "none"` instead of `"lax"` only when the browser must send the cookie in a cross-site context, such as a frontend and API on unrelated registrable domains. Browsers require `Secure` when `SameSite=None`, so that combination must remain HTTPS-only. Separate subdomains such as `app.example.com` and `api.example.com` are usually same-site, though they are still cross-origin.
536
- >
537
- > Also verify that the proxy explicitly forwards `X-Forwarded-Proto`, that the browser receives a `Set-Cookie` response header, and that frontend requests use `credentials: "include"` when crossing origins. If CORS is involved, the server must return the exact allowed origin plus `Access-Control-Allow-Credentials: true`; a wildcard origin is incompatible with credentialed requests.
538
- >
539
- > Avoid setting a cookie `domain` unless sharing across subdomains is intentional. A host-only cookie is safer and eliminates another production-only failure mode.
540
-
541
- ### After `/bro`
542
-
543
- #### What happened
544
-
545
- Your username and password check succeeded, and the login route returned `200
546
- OK`. However, the browser did not receive or return a usable session cookie in
547
- production.
548
-
549
- #### What it means
550
-
551
- In production, secure web traffic (HTTPS) hits an intermediary, such as a load
552
- balancer or Nginx, before reaching your Node/Express app as plain HTTP.
553
-
554
- Because your session cookie requires HTTPS (`secure: true`), Express may withhold
555
- it when it does not trust the proxy forwarding the request. The exact trust
556
- setting must match your deployment's proxy topology.
557
-
558
- #### What you should do next
559
-
560
- 1. Configure Express to trust only your actual proxy topology. Put this before
561
- the session setup. For example, use `1` only when there is exactly one trusted
562
- proxy hop:
563
-
564
- ```js
565
- app.set("trust proxy", 1);
566
-
567
- app.use(session({
568
- secret: process.env.SESSION_SECRET,
569
- resave: false,
570
- saveUninitialized: false,
571
- cookie: {
572
- httpOnly: true,
573
- secure: true,
574
- sameSite: "lax",
575
- maxAge: 1000 * 60 * 60 * 24
576
- }
577
- }));
578
- ```
579
-
580
- 2. Check your cookie settings:
581
- - Keep `sameSite: "lax"` if your frontend and backend share the same main
582
- domain, such as `app.example.com` and `api.example.com`.
583
- - Use `sameSite: "none"` only when the cookie must be sent cross-site.
584
- Browsers require `secure: true` with this setting.
585
- - Avoid defining a custom cookie `domain` unless you deliberately need to
586
- share cookies across subdomains.
587
-
588
- 3. Verify the proxy, browser, and CORS settings:
589
- - Ensure the proxy forwards the `X-Forwarded-Proto` header.
590
- - Check that the response contains a `Set-Cookie` header.
591
- - Include `credentials: "include"` in cross-origin frontend requests.
592
- - Return the exact allowed origin with
593
- `Access-Control-Allow-Credentials: true`. Do not use a wildcard (`*`) for
594
- credentialed requests.
595
-
596
- </details>
597
-
598
- <details>
599
- <summary><strong>Long:</strong> A CI build fails only when its cache is restored</summary>
600
-
601
- ### Before
602
-
603
- > The failure is not a nondeterministic pnpm installation problem, despite the misleading first error being emitted from the test runner immediately after dependency hydration. It is a state-contamination problem at the intersection of the repository cache, the package export map, and an incorrectly scoped cache key. The decisive observation is that the same commit succeeds in a clean checkout and in CI when cache restoration is disabled, but fails when the pre-build workspace cache is restored. That isolates the variable to files materialized outside Git rather than source, Node, or runner image drift.
604
- >
605
- > The affected package, `@acme/config`, was recently migrated from a dual CommonJS/ESM build to an ESM-oriented TypeScript output. Its root `package.json` declares `"type": "module"` and maps the `import` condition to `./dist/index.js`, while the legacy `require` condition still maps to `./dist/index.cjs`. The current compiler emits `index.js` but does not remove the previous build directory first. An older cached `dist` directory therefore contributes two files that no longer belong to the current build graph: `dist/index.cjs` and `dist/package.json`, the latter declaring `"type": "commonjs"`. The new compiler overlays `dist/index.js` but leaves both obsolete files intact. Because nested package boundaries override the root package type, Node interprets the newly emitted ESM `index.js` as CommonJS in that restored workspace and reports `Unexpected token 'export'`. Test processes entering through `require()` instead resolve the obsolete `index.cjs`, which references a removed chunk and can instead produce `MODULE_NOT_FOUND`. These apparently different errors are two projections of the same dirty-output condition.
606
- >
607
- > The cache configuration makes that contaminated state persistent. The workflow computes the key with `hashFiles('packages/**/pnpm-lock.yaml')`, but this workspace has only the root-level `pnpm-lock.yaml`. GitHub Actions consequently evaluates the hash expression to an empty value, yielding a key equivalent to `Linux-node20-workspace-`. A broad restore key then permits an archive produced before the module-format migration to satisfy the lookup. That archive combines the pnpm content-addressable store, Turborepo metadata, and every package's `dist` directory. Those data classes do not share valid invalidation semantics: pnpm store entries are immutable by content, Turborepo artifacts are task-hash addressed, and arbitrary build directories are mutable snapshots whose correctness depends on complete deletion or exact provenance. Treating them as one cache effectively elevates obsolete untracked files into undeclared build inputs.
608
- >
609
- > The evidence is visible by comparing the restored and clean workspaces before compilation. In the failing job, `packages/config/dist/package.json` exists with `"type":"commonjs"` and `packages/config/dist/index.cjs` has a timestamp and checksum predating the current commit. Neither file appears after `git clean -ffdx` followed by installation and build. The Actions cache log also shows the truncated key without a lockfile digest, while `git diff` remains empty because the problematic files are ignored. Finally, deleting only `packages/config/dist` before rerunning the unchanged test command converts the failure to a pass, which excludes dependency resolution and network variability as primary causes.
610
- >
611
- > The minimal durable remediation has three parts. First, stop caching package output directories. Cache the pnpm store separately, using a key that includes the actual root lockfile:
612
- >
613
- > ```yaml
614
- > key: ${{ runner.os }}-node20-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
615
- > restore-keys: |
616
- > ${{ runner.os }}-node20-pnpm-
617
- > ```
618
- >
619
- > A broad fallback is acceptable for the pnpm store because pnpm validates content-addressed entries; it is not acceptable for mutable `dist` snapshots. If remote or local Turborepo caching is retained, cache only `.turbo` and allow Turbo's task hash to govern artifact reuse rather than wrapping raw workspace outputs in a generic archive.
620
- >
621
- > Second, make every package build replace, rather than merge into, its output directory. `@acme/config` should delete `dist` before invoking `tsc`, `tsup`, or the existing bundler. The CI workflow should also run the repository's recursive clean target before the first build as a defense against future cache or runner reuse. This is not a substitute for fixing cache scope, but it restores the invariant that generated output is a function of the checked-out sources and declared toolchain.
622
- >
623
- > Third, make the export contract agree with emitted files. If `@acme/config` is intentionally ESM-only, remove the `require` condition and the obsolete CommonJS metadata. If CommonJS consumers remain supported, restore an explicit CJS build that deterministically emits `dist/index.cjs`; do not let a historical artifact impersonate that support. In either case, package publication should reject export targets that do not exist.
624
- >
625
- > Verification should exercise both cache states and both module entry paths. Run the full pipeline once with no cache, save only the corrected caches, and rerun the identical commit expecting a cache hit and the same result. Inspect the built package to confirm the nested CommonJS `package.json` and legacy chunks are absent unless intentionally generated. Add a lightweight smoke check using dynamic `import('@acme/config')`; if CommonJS remains supported, add a second check through `createRequire`. Finally, change the root lockfile and confirm the logged cache key changes. That matrix validates clean-build reproducibility, restored-cache reproducibility, export-map coherence, and dependency-cache invalidation independently rather than relying on a single green run.
626
-
627
- ### After `/bro`
628
-
629
- #### What happened
630
-
631
- Your build failed because old files were left behind by a broken cache (a saved
632
- snapshot of previous build files).
633
-
634
- The package `@acme/config` recently switched its module format to modern
635
- JavaScript (ESM). However, the build process did not delete the old output
636
- folder (`dist`) before creating new files.
637
-
638
- Because the GitHub Actions setting searches the wrong path
639
- (`hashFiles('packages/**/pnpm-lock.yaml')`), GitHub could not find the root
640
- `pnpm-lock.yaml`. It restored an outdated cache containing `dist/index.cjs` and
641
- a `dist/package.json` configured for older JavaScript (CommonJS). The new build
642
- wrote some files but left the old ones behind, confusing Node and causing errors
643
- such as `Unexpected token 'export'` and `MODULE_NOT_FOUND`.
644
-
645
- A clean checkout without the cache succeeds. This proves the problem is old
646
- cached files, not your source code or dependencies.
647
-
648
- #### What it means
649
-
650
- - **Cache contamination:** The CI workflow restores mutable `dist` folders
651
- alongside package-manager files, mixing obsolete files into new builds.
652
- - **Mismatched module settings:** The package says it supports both modern
653
- (`import`) and legacy (`require`) formats, but it only builds the modern one
654
- while relying on leftover cached files for the legacy one.
655
-
656
- #### What you should do next
657
-
658
- 1. **Fix the GitHub Actions cache:** Stop caching `dist` folders. Cache only the
659
- pnpm store, using the root lockfile:
660
-
661
- ```yaml
662
- key: ${{ runner.os }}-node20-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
663
- restore-keys: |
664
- ${{ runner.os }}-node20-pnpm-
665
- ```
666
-
667
- If you use Turborepo, cache only `.turbo`.
668
-
669
- 2. **Clean output folders before building:** Make `@acme/config` delete `dist`
670
- before running `tsc`, `tsup`, or the bundler. Run the repository's recursive
671
- clean command before CI builds.
672
-
673
- 3. **Update the package exports:**
674
- - If `@acme/config` is ESM-only, remove the `require` entry and old CommonJS
675
- metadata.
676
- - If CommonJS is still needed, add a real build step for `dist/index.cjs`.
677
-
678
- 4. **Verify the fix:**
679
- - Delete the generated `packages/config/dist` folder, then run a clean build.
680
- Do not use `git clean -ffdx` in a working copy with files you need.
681
- - Run the workflow without a cache, save the new cache, and rerun the same
682
- commit to verify that a cache hit also passes.
683
- - Test `import('@acme/config')`, and test `createRequire` if CommonJS is
684
- supported.
685
-
686
- </details>
687
-
688
- ## Explain pasted text
689
-
690
- Paste text directly after the command:
691
-
692
- ```text
693
- /bro text OAuth refresh tokens are rotated after every successful use.
694
- ```
695
-
696
- Bro explains the pasted text instead of the latest assistant reply. With no text
697
- after `/bro text`, it falls back to the latest completed reply. Press **R**
698
- to simplify the same captured text again.
699
-
700
- ## Explain a document
701
-
702
- Use a path relative to Pi's current workspace, or an absolute path inside it:
703
-
704
- ```text
705
- /bro file "docs/incident review.pdf"
26
+ ```sh
27
+ pi install npm:pi-bro
706
28
  ```
707
29
 
708
- Paths may contain spaces; matching single or double quotes are also accepted.
709
- Bro extracts text locally, then sends that text through the same explanation
710
- flow used by `/bro`. Pressing **R** retries the extracted snapshot; running a
711
- new `/bro file <path>` command reads the file again.
712
-
713
- Files are limited to 10 MiB and 100,000 extracted characters. Scanned PDFs are
714
- not supported because Bro does not perform OCR.
715
-
716
- ## Explain a webpage
717
-
718
- Pass one public HTTP or HTTPS page:
30
+ Then restart Pi or run `/reload`, and check your setup:
719
31
 
720
32
  ```text
721
- /bro url https://example.com/complicated-article
33
+ /bro doctor
722
34
  ```
723
35
 
724
- Bro fetches the page, extracts its main readable text locally, and sends only
725
- that text through the existing explanation flow. The completed modal shows the
726
- final website and page title. Pressing **R** retries the captured page without
727
- fetching again; running a new `/bro url <url>` command fetches a fresh copy.
728
-
729
- The first version is intentionally limited to one public, text-based page. It
730
- does not use browser cookies, sign in, run page JavaScript, bypass paywalls or
731
- bot protection, load complete discussion threads, follow pagination, or
732
- understand images and video. Pages that depend on those features may fail.
733
-
734
- If Bro cannot read a page, copy its content into a `.txt` or `.md` file, or save
735
- it as a PDF, then use `/bro file <path>`.
736
-
737
-
738
- ## Check your setup
739
-
740
- Run `/bro doctor` when Bro is newly installed or something is not working. It
741
- checks Bro's settings and preferences, then probes only the backends some feature
742
- actually selects, and reports the effective backend/model/effort for each
743
- feature. Failed checks explain what to fix.
744
-
745
- - **Agy**: installed version, model catalog, and account access.
746
- - **Claude Code**: installed version and configured authentication.
747
- - **Grok**: installed version only; authentication and connectivity are not
748
- verified.
749
- - **Codex**: installed version and configured authentication.
750
- - **Muse**: installed version only; authentication and connectivity are not
751
- verified.
36
+ To try Bro without installing it, run `pi -e npm:pi-bro`. To install from
37
+ GitHub, use `pi install git:github.com/tranhoangnguyen03/pi-bro`.
752
38
 
753
- Doctor never sends source text or runs a model completion. A successful check
754
- confirms the setup but cannot guarantee that a later provider request will
755
- succeed.
756
-
757
- ## Settings
758
-
759
- Use `/bro config` to review or change the shared default backend/model/effort
760
- and any per-capability (explain/show/btw/advisor/review) overrides, the explanation
761
- mode, and the default show turn count. Changes save immediately. Esc inside a
762
- picker cancels that pick; Esc on the settings screen closes it, keeping
763
- whatever was already saved. A failed save (for example, a read-only file) is
764
- shown inline. `/bro mode` changes the mode directly (**M** in an explanation
765
- tries another mode without saving it), and `/bro help` shows the active settings
766
- and file path.
767
-
768
- Settings live in this user-editable file, created when the extension loads
769
- (under `$PI_CODING_AGENT_DIR` instead when that is set):
770
-
771
- ```text
772
- ~/.pi/agent/bro-settings.json
773
- ```
39
+ ## Try it
774
40
 
775
- ```json
776
- {
777
- "version": 2,
778
- "default": { "backend": "agy", "model": "gemini-3.7-flash", "effort": "low" },
779
- "mode": "balanced",
780
- "showTurns": 1,
781
- "overrides": {
782
- "explain": { "backend": "claude", "model": "sonnet", "effort": "medium" },
783
- "advisor": { "backend": "grok", "model": "grok-4.7", "effort": "high" }
784
- }
785
- }
786
- ```
787
-
788
- Bro reads the file again before each request, so manual edits apply next time.
789
-
790
- - Each selection is one atomic backend/model/effort choice. An override pins
791
- its own complete selection and ignores the shared default, even when it
792
- happens to match it; select **Default** in `/bro config` to inherit again.
793
- Omitted overrides inherit `default`.
794
- - `effort` must be one the backend offers (`default` omits it and lets the
795
- model decide). Agy selections are normalized against Agy's installed model
796
- catalog. Claude, Grok, Codex, and Muse accept seeded or custom model IDs; a model/effort
797
- combination the account does not support fails with the backend's own error
798
- instead of silently falling back.
799
- - `mode` is `brief`, `balanced` (the default), or `faithful`. `showTurns` is
800
- the default number of turns `/bro show` draws (default 1); `/bro show
801
- <n-turns>` overrides it for one run.
802
- - `PI_BRO_MODEL` picks the initial Agy model only when Bro creates a missing
803
- settings file, for example `PI_BRO_MODEL=gemini-3.7-flash-low pi`.
804
- - Older flat settings files (root `model`/`effort`) still load and mean Agy.
805
-
806
- ## Backends: access and retention
807
-
808
- Bro uses each CLI's own account and billing route, never Pi provider
809
- credentials, and does not copy credentials or rewrite backend configuration.
810
- Each backend and your model provider may retain sessions, logs, and request
811
- data under their own settings and policies.
812
-
813
- | | Explain / show | BTW conversation-only | BTW full permission | Advisor | Guided Review |
814
- | --- | --- | --- | --- | --- | --- |
815
- | **Agy** | Temporary directory, Agy sandbox | Temporary directory, Agy sandbox | Workspace, permissions bypassed | Fresh workspace process, permissions bypassed | Unavailable; choose Claude/Muse |
816
- | **Claude Code** | Scratch directory, tools/MCP/skills disabled, no session persistence | Workspace, tools disabled | Workspace, permissions bypassed | Fresh workspace process, permissions bypassed | Restricted Read/Grep/Glob only; confined checkout, no persistence |
817
- | **Grok** | Temporary directory, **prompt instruction only** | Workspace, **prompt instruction only** | Workspace, permissions bypassed | Fresh workspace process, permissions bypassed | Unavailable; choose Claude/Muse |
818
- | **Codex** | Scratch directory, read-only sandbox, ephemeral session | Workspace, read-only sandbox | Workspace, approvals and sandbox bypassed | Fresh workspace process, approvals and sandbox bypassed | Unavailable; command execution is not allowed for review |
819
- | **Muse** | Scratch directory, approval/write/shell disabled, no session log | Workspace, approval/write/shell disabled | Workspace, permissions bypassed (`--yolo`) | Fresh workspace process, permissions bypassed (`--yolo`) | Captured checkout; writes/shell/web disabled, no session log |
820
-
821
- - Grok always runs with its sandbox off and permissions bypassed; its tools,
822
- hooks, skills, plugins, and MCP may remain available. "Answer only from the
823
- supplied context" is a request, not an enforced restriction.
824
- - Codex's read-only sandbox is not a tool-free mode: workspace reads and
825
- sandboxed commands remain available. Bro does not disable configured MCP
826
- servers or web search; their access is governed by Codex configuration.
827
- - Muse's restricted mode disables write and shell tools and approval prompts,
828
- not all tools: workspace reads and web tools remain available without approval.
829
- It is not a tool-free or network-isolated mode.
830
- - Claude's restrictions are tool/configuration settings (with safe mode), not
831
- an OS sandbox; built-in and managed Claude behavior can remain. Running a
832
- permission-bypassing mode as root may be rejected by Claude.
833
- - BTW continues natively: Agy by conversation ID, Claude and Grok by
834
- `--resume`, Codex by `resume`, and Muse by `--session-id` of the same session,
835
- including across `/mode` switches. An Agy conversation stays bound to the
836
- workspace it started in, so after a `/mode` switch — or whenever a thread
837
- has turns but no native session ID — Bro starts a fresh native session
838
- seeded with the main-session context and every earlier turn.
839
- - Cancellation, deadlines, and invalid output streams stop the backend's
840
- process group on POSIX, escalating after a five-second grace period. On
841
- Windows only the direct child is targeted. Detached shell work or external
842
- services a backend started are not contained. Bro removes its private
843
- temporary prompt files.
844
-
845
- ### Configuration precedence
846
-
847
- When resolving backend, model, and reasoning effort:
848
- 1. **Per-capability override**: `overrides.<capability>` (`explain`, `show`, `btw`, `advisor`, or `review`) pins that capability's complete selection.
849
- 2. **Shared default**: otherwise the capability inherits `default` (root `model`/`effort` in older flat files).
850
- 3. **Agy catalog normalization**: for Agy selections, Bro maps suffixed variant IDs and handles fixed-effort models.
851
- 4. **Initial file creation only**: `PI_BRO_MODEL` has no effect once the settings file exists.
852
-
853
- When resolving turn count for `/bro show`:
854
- 1. **Command argument**: an explicit count like `/bro show 3` or `/bro show 1 query` overrides for that run.
855
- 2. **Saved setting**: `showTurns` (defaults to 1).
856
-
857
- When shaping an answer, each part has one owner:
858
-
859
- | Part | Decided by |
41
+ | Type this in Pi | What happens |
860
42
  | --- | --- |
861
- | Bro's source rules: quoted source treated as data, no added facts, Show's traceability rules, BTW access mode | Built in; nothing overrides them |
862
- | How much of the source an explanation keeps | The mode: **M** for one explanation, otherwise the saved `mode` |
863
- | What a Show drawing focuses on | The steering query |
864
- | Who the answer is for: wording, tone, depth, and answer language | Your [preferences](#preferences), otherwise Bro's built-in defaults |
865
-
866
- ## Preferences
43
+ | `/bro` | Explains the agent's last reply |
44
+ | `/bro file docs/report.pdf` | Explains a document (use your own path) |
45
+ | `/bro url https://example.com/article` | Explains a webpage (use your own link) |
46
+ | `/bro btw "what file defines this route?"` | Asks a side question |
47
+ | `/bro show` | Draws the last turn as a diagram |
867
48
 
868
- Tell Bro about yourself and how you like answers. Bro adds what you write to
869
- every explain, `/bro show`, `/bro btw`, and Guided Review prompt as a labelled section,
870
- alongside its own instructions. It never replaces them: modes, **M**, and the
871
- source rules keep working. The advisor never receives your preferences; use
872
- `/bro advisor-steer` for it.
49
+ ## What it does
873
50
 
874
- Run `/bro preferences` to edit them: **Ctrl+S** saves, **Ctrl+K** deletes the
875
- file, **Ctrl+C** copies, and **Esc** closes without saving. The first time,
876
- the editor opens with this starter text, which is not saved until you press
877
- **Ctrl+S**:
878
-
879
- ```md
880
- ## About me
881
- I'm an overworked white-collar worker, and so are my colleagues. By the end of a
882
- hard day our brains are fried and we can only handle simple language, no matter
883
- how sharp we are at our best.
884
-
885
- ## How I like answers
886
- - Explain it like I'm a simpleton: plain, everyday words.
887
- - Go easy on analogies. No forced ones.
888
- ```
889
-
890
- You can also edit the file directly (under `$PI_CODING_AGENT_DIR` instead when
891
- that is set):
892
-
893
- ```text
894
- ~/.pi/agent/bro-preferences.md
895
- ```
896
-
897
- - **Free-form**: no placeholder or structure is required. Bro re-reads the
898
- file on every request, so edits apply next time. A missing or empty file
899
- means no preferences, and Bro's prompts are then exactly the built-in ones.
900
- - **What preferences can change**: wording, tone, technical depth, length in
901
- BTW, and the answer language. If you name a language, code, commands, paths,
902
- names, and numbers still stay exactly as written. In `/bro show`,
903
- preferences only change wording and language. Guided Review also receives preferences for explanation/answer wording; they do not override its evidence or review rules. A per-run choice (the mode,
904
- **M**, a Show steering query, or what a BTW/review question asks for) wins over a
905
- standing preference.
906
- - **Limit**: 4,000 characters, because the text goes with every request. A
907
- longer file stops explain, Show, BTW, and Guided Review with an error until you trim it;
908
- Bro never cuts it silently. `/bro doctor` reports the problem, and the
909
- editor still opens the file so you can fix it.
910
- - **BTW threads**: when your preferences change, the next side question starts
911
- a fresh backend session that is caught up on the thread, so old preferences
912
- don't linger.
51
+ | Command | What you get |
52
+ | --- | --- |
53
+ | `/bro`, `/bro text`, `/bro file`, `/bro url` | **[Explain](docs/user-guide.md#what-bro-can-explain).** A plain-language version of the agent's last reply, pasted text, a PDF/DOCX/Markdown file, or a public webpage. Three modes: `brief`, `balanced`, `faithful`. |
54
+ | `/bro show` | **[Show](docs/user-guide.md#bro-show).** The last few turns drawn as a shape (call tree, file tree, diff, pseudocode, or an HTML diagram) instead of prose. |
55
+ | `/bro btw` | **[Side questions](docs/user-guide.md#bro-btw-side-conversation).** A separate conversation seeded with recent context, so you can ask without interrupting the agent. ⚠️ [Full-permission mode](#what-leaves-your-machine) can edit files. |
56
+ | `/bro guided-review <PR>` | **[Guided Review](docs/guided-review.md).** An explanation and quality assessment of a GitHub PR, with ranked findings, source evidence and private questions. Needs the Claude or Muse backend. |
57
+ | `bro_advisor` tool | **[Second opinion](docs/user-guide.md#bro-advisor).** A tool your agent can call mid-task; a fresh backend process investigates and returns advice to the agent. ⚠️ [Runs with permissions bypassed](#what-leaves-your-machine). |
58
+ | `/bro config`, `/bro preferences` | **[Settings](docs/user-guide.md#settings) and [preferences](docs/user-guide.md#preferences).** Pick the backend, model and effort per feature, and tell Bro how you like answers. |
913
59
 
914
- `bro-prompt.md`, the full prompt template from earlier versions, is no longer
915
- read. Move what you want to keep into `/bro preferences`, and choose
916
- `/bro mode brief` for the original ELI-simpleton instruction.
60
+ Every command, control and setting is in the **[user guide](docs/user-guide.md)**.
61
+ Guided Review has its own [guide](docs/guided-review.md).
917
62
 
63
+ ## Requirements
918
64
 
919
- ## Privacy and safety
65
+ - Pi `>=0.84.2 <1` and Node.js `>=22.19.0`.
66
+ - macOS or Linux. Windows is untested; there, cancelling stops only the
67
+ backend's direct process, not anything it started.
68
+ - At least one backend CLI on your `PATH`, signed in once from your terminal.
69
+ You only need the one you use:
920
70
 
921
- - **External requests**: Bro sends the latest completed assistant response,
922
- pasted text, extracted document text, extracted webpage text, or recent
923
- session conversation text (tool calls, tool results, reasoning, and images
924
- omitted) to the selected backend and its configured model provider.
925
- - **Side conversation requests**: `/bro btw` sends your side questions and the
926
- seeded main-session conversation text (plus earlier turns when a native
927
- session is reseeded) to the selected backend. In full permission mode the
928
- side agent can additionally read and edit the workspace.
929
- - **Guided Review requests**: PR identity/description, captured diff, relevant private discussion and preferences go to the selected backend, which can inspect captured files using Claude/Muse read-only tools. Main-session context is not attached. Review shell/write/web tools are disabled; Claude file tools are confined with `--restricted`, Muse uses workspace-scoped inspection. Prompt injection can still corrupt conclusions or reveal captured content to the provider; this is not a general confidentiality guarantee. See [Guided Review access and storage](docs/guided-review.md#access-privacy-and-storage).
930
- - **Preferences**: `bro-preferences.md` is sent with every explain, Show,
931
- BTW, and Guided Review request to the selected backend. It is never sent to the advisor or
932
- to Pi's main model.
933
- - **Advisor requests**: `bro_advisor` sends the executor agent's system
934
- instructions, active tool list (excluding `bro_advisor`), ordered
935
- conversation history including tool calls and tool results (unlike Show,
936
- which omits them), your steering brief, and the executor's optional question
937
- to the selected backend. Reasoning and image bodies are omitted with explicit
938
- markers (`[reasoning omitted]`, `[image omitted]`). The advisor process runs
939
- in your workspace with auto-approved permissions; its instruction to only
940
- advise is behavioral, not an enforced boundary.
941
- - **Access enforcement** differs by backend; see
942
- [Backends: access and retention](#backends-access-and-retention). Prompt
943
- instructions are not access enforcement.
944
- - **Context isolation**: Bro does not add explanations, BTW answers, or the
945
- steering brief to Pi's conversation history or main-agent context. BTW text
946
- reaches the main editor only through `/insert` or `/insert-all`, and advisor
947
- results appear as normal tool results in the executor's transcript.
948
- - **Saved reviews**: Guided Review records, questions, drafts and captured Git source persist under the host's `getAgentDir()/bro-reviews/`. No automatic cleanup or sync is provided; see [storage and removal](docs/guided-review.md#access-privacy-and-storage).
949
- - **Memory**: the latest explanation (for `/bro open`) and the BTW thread live
950
- only in process memory and clear when you switch Pi sessions, reload
951
- extensions, or quit Pi. Backend-native sessions can persist independently.
952
- Standing advisor priorities are stored in `~/.pi/agent/bro-advisor.md`, and
953
- the session steering brief is stored as session-scoped extension data
954
- (`bro-advisor-steering`) in the session file.
955
- - **File safety**: `/bro file` reads only regular files whose resolved path is
956
- inside Pi's current workspace, including after resolving symlinks. Bro's
957
- extractor does not modify them.
958
- - **Web requests**: `/bro url` connects directly to the target website. The site
959
- sees your IP address and Bro's user agent. Bro sends no browser cookies,
960
- authorization, or referrer information, and it refuses local, private, and
961
- reserved network destinations, including redirects. Avoid private or signed
962
- URLs whose query string contains secrets.
963
- - **Web extraction**: Bro parses downloaded HTML locally without executing page
964
- scripts or loading page subresources. It sends the extracted readable text,
965
- including links preserved in that text, to the selected backend; it does not
966
- separately send the requested URL or raw page HTML.
967
- - **Show diagrams**: When a show reply ends in one self-contained HTML block,
968
- Bro writes it to `/tmp/pi-bro-<uid>/bro-show-<hash>.html` with a restrictive
969
- Content-Security-Policy, and opens it in your browser only when you press
970
- **O**. **C** copies the full reply, including the HTML.
971
- - **Clipboard**: **C**, `/copy`, `/copy-all`, and Guided Review's Copy finding copy text to your system
972
- clipboard, where your operating system or clipboard manager may retain it.
71
+ | Backend | CLI | Notes |
72
+ | --- | --- | --- |
73
+ | [Google Antigravity](https://antigravity.google/docs/cli-install) | `agy` | Default for new settings. Needs `>=1.1.15`. Not for Guided Review. |
74
+ | [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | `claude` | Works for every feature, including Guided Review. |
75
+ | [Grok Build](https://docs.x.ai/build/overview) | `grok` | Not for Guided Review. |
76
+ | [Codex CLI](https://github.com/openai/codex) | `codex` | Not for Guided Review. |
77
+ | [Muse Code](https://dev.meta.ai/docs/muse-code) | `muse` | Works for every feature, including Guided Review. |
973
78
 
974
- ## Troubleshooting and current limits
79
+ - Guided Review also needs Git and an authenticated [`gh`](https://cli.github.com/).
80
+ Choose Claude or Muse for it in `/bro config`.
81
+ - [PiG](docs/pig-compatibility.md) support is experimental.
975
82
 
976
- If an explanation fails, run `/bro doctor` first. If a webpage cannot be
977
- extracted, copy its content into a supported text file or save it as a PDF and
978
- use `/bro file`. If a PDF contains only scanned images, run OCR with another
979
- tool before giving it to Bro.
83
+ ## What leaves your machine
980
84
 
981
- - An unsupported model, effort, or missing backend fails with that backend's
982
- diagnostic; Bro never falls back to another backend.
983
- - Document input supports `.md`, `.markdown`, `.txt`, `.pdf`, and `.docx` only;
984
- it does not perform OCR.
985
- - Webpage input supports one public HTML page, up to 5 MiB downloaded and
986
- 100,000 extracted characters. JavaScript-only, authenticated, paywalled,
987
- blocked, paginated, and media-first pages are not supported.
988
- - Direct webpage fetching does not currently use `HTTP_PROXY`, `HTTPS_PROXY`,
989
- or other proxy environment variables.
990
- - `/bro btw` threads are memory-only and do not survive reloads or restarts.
991
- - The Agy advisor requires Agy CLI `>=1.1.15`.
992
- - Show captures only the conversation text of what already happened in the
993
- current session — the last few turns' user and assistant messages, with
994
- tool calls, tool results, reasoning, and images always omitted; its shapes
995
- reflect what was reported in the conversation, not independent verification.
996
- - HTML diagrams open in your default browser; pressing **O** on a remote or
997
- headless session with no display reports the failure instead of opening
998
- anything.
999
- - Explain keeps only the latest result in memory and does not export directly to files. Guided Review separately persists review records and captured source.
1000
- - Guided Review is local-Pi interactive only, keeps its captured revision on reopen, and does not publish, review a branch or fetch later PR changes. See [review recovery and limits](docs/guided-review.md#if-something-goes-wrong).
1001
- - Bro temporarily captures mouse input while its modal is open so mouse-wheel
1002
- and trackpad scrolling work in regular and fullscreen modes. Native mouse
1003
- selection may be unavailable or visually extend outside the Bro window;
1004
- press **C** to copy the full explanation instead.
85
+ Each feature sends data to the backend you chose and its model provider,
86
+ which may keep it under their own policies. Paths below are under
87
+ `~/.pi/agent/` (or `$PI_CODING_AGENT_DIR`).
1005
88
 
1006
- ## Development
89
+ | Feature | What is sent | What Bro keeps on disk |
90
+ | --- | --- | --- |
91
+ | Explain | The agent's last reply, your pasted text, or text extracted from your file or webpage, plus your preferences | Nothing; the last result stays in memory |
92
+ | Show | User and assistant text from recent turns (no tool calls, tool results, reasoning or images), plus your preferences | HTML diagrams in `pi-bro-<uid>/` in your system temp folder |
93
+ | Side questions | Your questions and up to the last 8 turns of conversation text, plus your preferences | Nothing; the thread stays in memory |
94
+ | Guided Review | The PR description and diff, the files the backend reads, your questions, plus your preferences | Reviews and captured source in `bro-reviews/`, with no automatic cleanup |
95
+ | Advisor | The agent's system instructions, tool list and whole conversation, including tool calls and results (reasoning and images omitted), plus your steering | Steering in `bro-advisor.md` and the session file |
96
+
97
+ `/bro url` also connects straight to the website, which sees your IP address.
98
+ Bro runs each CLI as you, with your environment. It never reads or copies Pi's
99
+ provider credentials, but the CLI signs in its own way, which can include API
100
+ keys set in your environment. Nothing Bro produces enters Pi's conversation
101
+ unless you insert it, except advisor answers, which return to the agent as
102
+ tool results.
103
+
104
+ > [!WARNING]
105
+ > These run the backend in your workspace with its permission checks bypassed:
106
+ > - **Advisor:** it can read, edit and run commands. "Advise, don't edit" is
107
+ > only an instruction.
108
+ > - **BTW full-permission mode** (turned on with `/mode` inside BTW): it is
109
+ > meant to edit files and run commands.
110
+ > - **Grok, wherever you use it:** its sandbox is always off. "Answer only from
111
+ > the supplied context" is only an instruction.
112
+
113
+ Details per backend:
114
+ [Backends: access and retention](docs/user-guide.md#backends-access-and-retention)
115
+ and [Privacy and safety](docs/user-guide.md#privacy-and-safety).
116
+
117
+ ## Limits
118
+
119
+ - Documents: `.md`, `.markdown`, `.txt`, `.pdf`, `.docx` only, no OCR, up to 10 MiB.
120
+ → Run OCR elsewhere first.
121
+ - Webpages: one public HTML page; no logins, paywalls, page JavaScript or
122
+ proxy settings. → Save the page as PDF or text and use `/bro file`.
123
+ - No fallback: if the chosen backend, model or effort fails, Bro shows that
124
+ backend's error instead of switching. → Run `/bro doctor`.
125
+ - BTW threads and the last explanation live in memory only and clear on
126
+ reload or restart.
127
+ - Guided Review runs in local interactive Pi only and does not post to
128
+ GitHub.
129
+
130
+ Full list: [Troubleshooting and current limits](docs/user-guide.md#troubleshooting-and-current-limits).
131
+
132
+ ## Stability
133
+
134
+ Bro is `0.x`: a minor version can change commands or settings. Breaking
135
+ changes are listed under **Breaking** in the [changelog](CHANGELOG.md) and the
136
+ [release notes](https://github.com/tranhoangnguyen03/pi-bro/releases). Each
137
+ release is published to npm from CI with
138
+ [provenance](https://docs.npmjs.com/generating-provenance-statements).
139
+
140
+ ## Contributing
1007
141
 
1008
142
  ```sh
1009
- npm install
1010
- npm test
143
+ npm ci
144
+ npm test # offline: uses fake backend CLIs, never calls a model
1011
145
  pi --tui-mode fullscreen -e ./bro.ts
1012
146
  ```
1013
147
 
1014
- `npm test` uses fake `agy`, `claude`, `grok`, `codex`, and `muse` executables and never calls an
1015
- external model. See [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for the code
1016
- map and invariants, [docs/TESTING.md](docs/TESTING.md) for the manual
1017
- end-to-end checklist, and [docs/README.md](docs/README.md) for the docs index.
1018
-
1019
- The prompt benchmark is manual and makes live Agy calls. Read
1020
- [`benchmark/README.md`](benchmark/README.md) before running it; it is never part
1021
- of `npm test`.
148
+ See [CONTRIBUTING.md](CONTRIBUTING.md), the [code map](docs/DEVELOPMENT.md)
149
+ and the [docs index](docs/README.md). Report security problems privately as
150
+ described in [SECURITY.md](SECURITY.md).
1022
151
 
1023
152
  ## License
1024
153