@erlin-ai/pi-footer-wrap 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.2.0] - 2026-10-02
9
+
10
+ ### Added
11
+
12
+ - Virtual models now show their routed physical model in the footer, e.g.
13
+ `auto → group/auto-glm-5-3-flash`, mirroring pi 0.99.x default footer
14
+ behavior. Read from session entries; same-model turns show no arrow.
15
+
16
+ ### Fixed
17
+
18
+ - `layoutStats` called `String(wrapTextWithAnsi(...))`, but pi-tui returns
19
+ `string[]`; `String()` re-joined the wrapped pieces into one line, so an
20
+ over-wide stats line rendered unsplit and crashed pi's TUI with
21
+ `rendered line exceeds terminal width`.
22
+
8
23
  ## [0.1.0] - 2026-09-20
9
24
 
10
25
  ### Added
package/lib/footer.ts CHANGED
@@ -155,10 +155,13 @@ export function layoutStats(
155
155
  if (lw + minPad + rw <= width) {
156
156
  return [left + " ".repeat(width - lw - rw) + right];
157
157
  }
158
- const wrap = (s: string): string[] =>
159
- visibleWidth(s) <= width
160
- ? [s]
161
- : String(wrapTextWithAnsi(s, width)).split("\n");
158
+ // wrapTextWithAnsi returns string[] (pi-tui utils.d.ts) — never String() it:
159
+ // Array→String joins with "," and can yield one over-wide line (crash).
160
+ const wrap = (s: string): string[] => {
161
+ if (visibleWidth(s) <= width) return [s];
162
+ const wrapped = wrapTextWithAnsi(s, width);
163
+ return Array.isArray(wrapped) ? wrapped : String(wrapped).split("\n");
164
+ };
162
165
  if (lw <= width || rw <= width) {
163
166
  const leftLines = lw <= width ? [left] : wrap(left);
164
167
  const rightLines =
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@erlin-ai/pi-footer-wrap",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Adaptive-wrapping footer for the pi coding agent: takes over the default footer and wraps extension statuses across lines instead of truncating them. No data is dropped.",
5
5
  "type": "module",
6
6
  "files": [
package/src/index.ts CHANGED
@@ -25,6 +25,20 @@ function wrapToLines(text: string, width: number): string[] {
25
25
  return Array.isArray(wrapped) ? wrapped : String(wrapped).split("\n");
26
26
  }
27
27
 
28
+ /** Physical model id of the latest assistant response (what a virtual model routed to). */
29
+ function latestRoutedModel(ctx: any): string | undefined {
30
+ try {
31
+ const entries = ctx.sessionManager?.getEntries?.() ?? [];
32
+ for (let i = entries.length - 1; i >= 0; i--) {
33
+ const msg = entries[i]?.message;
34
+ if (msg?.role === "assistant" && msg.model) return String(msg.model);
35
+ }
36
+ } catch {
37
+ // best-effort
38
+ }
39
+ return undefined;
40
+ }
41
+
28
42
  export default function (pi: any) {
29
43
  let enabled = true;
30
44
 
@@ -69,6 +83,14 @@ export default function (pi: any) {
69
83
  right =
70
84
  level === "off" ? `${right} • thinking off` : `${right} • ${level}`;
71
85
  }
86
+ // A virtual model routes each request; show where the latest response went
87
+ // (mirrors pi 0.99.x default footer, footer.js:207-211). ExtensionContext
88
+ // doesn't expose session.routedModel, so read the latest assistant message's
89
+ // physical model from session entries.
90
+ const routed = latestRoutedModel(ctx);
91
+ if (routed && routed !== model?.id) {
92
+ right += ` → ${routed}`;
93
+ }
72
94
  if ((footerData?.getAvailableProviderCount?.() ?? 1) > 1 && model) {
73
95
  right = `(${model.provider}) ${right}`;
74
96
  }