pi-bro 0.24.0 → 0.25.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/src/modal-kit.ts CHANGED
@@ -71,6 +71,9 @@ export type FrameParts = {
71
71
  notice?: string;
72
72
  /** Omit when the body prints its own hints (config's SettingsList). */
73
73
  hints?: string;
74
+ /** Lines rendered under the hints, e.g. a focusable action row. Wrapped by the caller; a line
75
+ * wider than the frame is truncated, so keep a single label narrower than the modal. */
76
+ footerAfterHints?: string;
74
77
  /** false: size to the content instead of padding the body to the full height. */
75
78
  fill?: boolean;
76
79
  /** Body rows the notice must leave free; it shrinks to one line before taking them. */
@@ -94,10 +97,12 @@ export function renderFrame(theme: Theme, width: number, height: number, parts:
94
97
  const footer = parts.footer ?? [];
95
98
  // Wrapped, not truncated: Esc comes last in every hint line and must not be cut off.
96
99
  const hints = parts.hints ? wrapTextWithAnsi(theme.fg("dim", parts.hints), inner) : [];
100
+ // The action row is already wrapped by the caller, so keep its own line breaks.
101
+ const belowHints = parts.footerAfterHints ? parts.footerAfterHints.split("\n").map((line) => truncateToWidth(line, inner, "")) : [];
97
102
  const notice = parts.notice ? wrapTextWithAnsi(parts.notice, inner).slice(0, 3) : [];
98
- while (notice.length > 1 && height - 5 - footer.length - hints.length - notice.length < (parts.minBody ?? 0)) notice.pop();
103
+ while (notice.length > 1 && height - 5 - footer.length - hints.length - belowHints.length - notice.length < (parts.minBody ?? 0)) notice.pop();
99
104
  // The notice sits above the footer so it is dropped before a composer when space runs out.
100
- const below = [...notice, ...footer, ...hints];
105
+ const below = [...notice, ...footer, ...hints, ...belowHints];
101
106
  const bodyRows = Math.max(0, height - 5 - below.length);
102
107
  const body = bodyRows ? parts.body(bodyRows, inner).slice(0, bodyRows) : [];
103
108
  if (parts.fill !== false) while (body.length < bodyRows) body.push("");
@@ -174,6 +179,34 @@ export class Scroller {
174
179
  }
175
180
  }
176
181
 
182
+ /**
183
+ * Render a focusable action row: the focused button carries a leading marker and accent colour, so a
184
+ * keypress always has exactly one visible target. Callers own focus, keys and activation; this only
185
+ * draws, which is why Guided Review can keep its own navigation and layout.
186
+ */
187
+ export function buttonLabels(theme: Theme, labels: readonly string[], focused?: number): string[] {
188
+ return labels.map((label, index) => {
189
+ const text = `${index === focused ? "›" : ""}[${label}]`;
190
+ return index === focused ? theme.bold(theme.fg("accent", text)) : text;
191
+ });
192
+ }
193
+
194
+ /**
195
+ * Pack whole buttons onto lines with `visibleWidth`, never splitting one: a label broken mid-word
196
+ * makes the focused target ambiguous, which is the misclick this row exists to prevent.
197
+ */
198
+ export function wrapButtons(buttons: readonly string[], width: number): string[] {
199
+ const lines: string[] = [];
200
+ let line = "";
201
+ for (const button of buttons) {
202
+ if (!line) { line = button; continue; }
203
+ if (visibleWidth(`${line} ${button}`) <= width) line += ` ${button}`;
204
+ else { lines.push(line); line = button; }
205
+ }
206
+ if (line) lines.push(line);
207
+ return lines;
208
+ }
209
+
177
210
  /**
178
211
  * Forward a pointer event to a pi-tui child drawn at `area` inside the overlay, in the child's own
179
212
  * coordinates (without `area`, the event already is). Pi 0.84 components have no handleMouse, hence
package/src/prompt.ts CHANGED
@@ -159,7 +159,7 @@ export function buildAdvisorPrompt(steering: AdvisorSteeringInput, snapshot: str
159
159
  const questionSection = question?.trim()
160
160
  ? `## Executor's question\n\n${question.trim()}`
161
161
  : "## Executor's question\n\nNone was given. Use your own judgment about what advice would help most, given the snapshot below.";
162
- return `You are the Bro advisor: a separate process an executor coding agent voluntarily consulted mid-task, in the pi-bro Pi extension. You have real tool access in this workspace and are running with permissions auto-approved -- you can freely read files, search, and run read-oriented commands to verify claims. Investigate before advising: do not just restate what the snapshot below reports as true.
162
+ return `You are the Bro advisor: a separate process an executor coding agent voluntarily consulted mid-task, in the pi-bro Pi extension. You have real tool access in this workspace and are running with permissions auto-approved -- you can freely read files, search, and run read-oriented commands when needed. Use the snapshot below first; only read files or run commands when something you need is missing from the snapshot, may be stale after later edits, or a key claim is in doubt.
163
163
 
164
164
  Your job is strictly advisory. Return findings and recommendations in your reply; do not edit files or otherwise implement the change yourself -- the executor remains responsible for implementation.
165
165
 
@@ -167,11 +167,11 @@ ${steeringSection}
167
167
 
168
168
  ## Context snapshot from the executor's session
169
169
 
170
- This is background/evidence captured from the executor's own conversation. It is the executor's own account of what happened, not independently verified by you -- treat it as a starting point to check, not as ground truth:
170
+ This is background/evidence captured from the executor's own conversation, including its tool calls and results. It is the executor's own account of what happened, not independently verified by you -- rely on the tool outputs already shown here before running new checks:
171
171
 
172
172
  ${snapshot}
173
173
 
174
174
  ${questionSection}
175
175
 
176
- Begin with a one-line answer or verdict. Then give concise findings, evidence, and recommended next actions grounded in what you verified yourself in the workspace. Omit investigation narration, waiting updates, and progress reports.`;
176
+ Begin with a one-line answer or verdict. Then give concise findings, evidence, and recommended next actions grounded in the snapshot and any workspace checks you needed. Omit investigation narration, waiting updates, and progress reports.`;
177
177
  }