pi-bar 0.3.1 → 0.3.2

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
@@ -3,21 +3,22 @@
3
3
  [![npm version](https://img.shields.io/npm/v/pi-bar.svg)](https://www.npmjs.com/package/pi-bar)
4
4
  [![npm downloads](https://img.shields.io/npm/dm/pi-bar.svg)](https://www.npmjs.com/package/pi-bar)
5
5
 
6
- **Never accidentally run Opus on a typo again.** pi-bar puts the active model, thinking level, and context usage in pi's footer so you catch wrong-model / wrong-reasoning / context-pressure problems before they cost you.
6
+ **Never accidentally run Opus on a typo again.** pi-bar puts the active model, thinking level, context usage, and extension statuses in pi's footer so you catch wrong-model / wrong-reasoning / context-pressure problems before they cost you.
7
7
 
8
8
  ```text
9
- ◈ claude-opus-4.7 ❯ ✦ think:med ❯ ◷ 2.6% / 1.0M
9
+ ◈ claude-opus-4.7 ❯ ✦ think:med ❯ ◷ 2.6% / 1.0M ❯ ✧ plan:active
10
10
  ```
11
11
 
12
- ![pi-bar screenshot, three rows showing green, yellow, and red context usage](https://cdn.jsdelivr.net/npm/pi-bar@0.3.0/assets/screenshot.png)
12
+ ![pi-bar screenshot, three rows showing green, yellow, and red context usage](https://cdn.jsdelivr.net/npm/pi-bar@0.3.2/assets/screenshot.png)
13
13
 
14
14
  ## Why use it?
15
15
 
16
16
  - **See the active model at a glance** — catch accidental model switches before an expensive or sensitive task starts.
17
17
  - **Track thinking level in place** — immediately notice when you are using the wrong reasoning setting.
18
18
  - **Watch context pressure early** — context usage turns green, yellow, then red as you approach the limit.
19
+ - **Keep extension statuses visible** — statuses set with `ctx.ui.setStatus()` still appear in the custom footer.
19
20
 
20
- pi-bar is intentionally tiny: one small extension, no broad behavior changes, and no commands to learn. It replaces pi's built-in footer with a compact model / thinking / context status line that refreshes when the model changes, thinking level changes, and after assistant turns.
21
+ pi-bar is intentionally tiny: one small extension, no broad behavior changes, and no commands to learn. It replaces pi's built-in footer with a compact model / thinking / context / extension-status line that refreshes when model, thinking level, context usage, or extension statuses change.
21
22
 
22
23
  ## Install
23
24
 
@@ -38,11 +39,11 @@ pi-bar works out of the box, but you can tune it with environment variables befo
38
39
  ### Show or hide segments
39
40
 
40
41
  ```bash
41
- PI_BAR_SHOW=model,thinking,context pi
42
+ PI_BAR_SHOW=model,thinking,context,extensions pi
42
43
  PI_BAR_SHOW=model,context pi
43
44
  ```
44
45
 
45
- Allowed segments are `model`, `thinking`, and `context`.
46
+ Allowed segments are `model`, `thinking`, `context`, and `extensions`. The `extensions` segment is hidden when no extension has set a status.
46
47
 
47
48
  ### Change context thresholds
48
49
 
@@ -62,10 +63,10 @@ PI_BAR_ICONS=nerdfont pi
62
63
 
63
64
  Supported presets:
64
65
 
65
- - `unicode` — default, e.g. `◈ gpt-5.5 ❯ ✦ think:medium ❯ ◷ 42.7% / 272k`
66
- - `plain` — maximum compatibility, e.g. `model:gpt-5.5 | think:medium | ctx:42.7% / 272k`
67
- - `emoji` — colorful emoji markers, e.g. `🤖 gpt-5.5 › ✨ think:medium › 🧭 42.7% / 272k`
68
- - `nerdfont` — icon-font glyphs for terminals configured with a Nerd Font, e.g. `󰊩 gpt-5.5 󰮱 󰉑 think:medium 󰮱 󰍛 42.7% / 272k`
66
+ - `unicode` — default, e.g. `◈ gpt-5.5 ❯ ✦ think:medium ❯ ◷ 42.7% / 272k ❯ ✧ plan:active`
67
+ - `plain` — maximum compatibility, e.g. `model:gpt-5.5 | think:medium | ctx:42.7% / 272k | ext:plan:active`
68
+ - `emoji` — colorful emoji markers, e.g. `🤖 gpt-5.5 › ✨ think:medium › 🧭 42.7% / 272k › 🧩 plan:active`
69
+ - `nerdfont` — icon-font glyphs for terminals configured with a Nerd Font, e.g. `󰚩 gpt-5.5 󰧑 think:medium 󰍛 42.7% / 272k  󰐱 plan:active`
69
70
 
70
71
  ## Compatibility
71
72
 
@@ -1,17 +1,17 @@
1
1
  /**
2
2
  * pi-bar — footer/statusline extension.
3
3
  *
4
- * Replaces the built-in footer with three left-aligned segments:
5
- * ◈ <model name> ❯ ✦ think:<level> ❯ ◷ <context% / window>
4
+ * Replaces the built-in footer with left-aligned segments:
5
+ * ◈ <model name> ❯ ✦ think:<level> ❯ ◷ <context% / window> ❯ ✧ <extension statuses>
6
6
  *
7
7
  * Examples:
8
8
  * ◈ claude-opus-4.7 ❯ ✦ think:med ❯ ◷ 2.6% / 1.0M
9
9
  *
10
- * Re-renders on model change, thinking-level change, and after each
11
- * assistant turn so context usage stays current.
10
+ * Re-renders on model change, thinking-level change, status updates,
11
+ * and after each assistant turn so context usage stays current.
12
12
  *
13
13
  * Configuration env vars:
14
- * PI_BAR_SHOW=model,thinking,context
14
+ * PI_BAR_SHOW=model,thinking,context,extensions
15
15
  * PI_BAR_THRESHOLDS=70,90
16
16
  * PI_BAR_ICONS=unicode|plain|emoji|nerdfont
17
17
  */
@@ -19,21 +19,56 @@
19
19
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
20
20
  import { truncateToWidth } from "@earendil-works/pi-tui";
21
21
 
22
- type SegmentName = "model" | "thinking" | "context";
22
+ type SegmentName = "model" | "thinking" | "context" | "extensions";
23
23
  type IconPreset = "unicode" | "plain" | "emoji" | "nerdfont";
24
24
 
25
- const DEFAULT_SEGMENTS: SegmentName[] = ["model", "thinking", "context"];
25
+ const DEFAULT_SEGMENTS: SegmentName[] = [
26
+ "model",
27
+ "thinking",
28
+ "context",
29
+ "extensions",
30
+ ];
26
31
  const DEFAULT_WARNING_THRESHOLD = 70;
27
32
  const DEFAULT_ERROR_THRESHOLD = 90;
28
33
 
29
34
  const ICON_PRESETS: Record<
30
35
  IconPreset,
31
- { model: string; thinking: string; context: string; separator: string }
36
+ {
37
+ model: string;
38
+ thinking: string;
39
+ context: string;
40
+ extensions: string;
41
+ separator: string;
42
+ }
32
43
  > = {
33
- unicode: { model: "◈", thinking: "✦", context: "◷", separator: "❯" },
34
- plain: { model: "model", thinking: "think", context: "ctx", separator: "|" },
35
- emoji: { model: "🤖", thinking: "✨", context: "🧭", separator: "›" },
36
- nerdfont: { model: "󰚩", thinking: "󰧑", context: "󰍛", separator: "" },
44
+ unicode: {
45
+ model: "◈",
46
+ thinking: "✦",
47
+ context: "◷",
48
+ extensions: "✧",
49
+ separator: "❯",
50
+ },
51
+ plain: {
52
+ model: "model",
53
+ thinking: "think",
54
+ context: "ctx",
55
+ extensions: "ext",
56
+ separator: "|",
57
+ },
58
+ emoji: {
59
+ model: "🤖",
60
+ thinking: "✨",
61
+ context: "🧭",
62
+ extensions: "🧩",
63
+ separator: "›",
64
+ },
65
+ nerdfont: {
66
+ model: "󰚩",
67
+ thinking: "󰧑",
68
+ context: "󰍛",
69
+ extensions: "󰐱",
70
+ separator: "",
71
+ },
37
72
  };
38
73
 
39
74
  function formatTokens(n: number): string {
@@ -95,7 +130,7 @@ function parseSegments(): SegmentName[] {
95
130
  .split(",")
96
131
  .map((segment) => segment.trim().toLowerCase())
97
132
  .filter((segment): segment is SegmentName =>
98
- ["model", "thinking", "context"].includes(segment),
133
+ ["model", "thinking", "context", "extensions"].includes(segment),
99
134
  );
100
135
 
101
136
  return requested.length > 0 ? requested : DEFAULT_SEGMENTS;
@@ -140,6 +175,14 @@ function prefixed(icon: string, text: string, preset: IconPreset): string {
140
175
  return `${icon} ${text}`;
141
176
  }
142
177
 
178
+ function formatExtensionStatuses(statuses: ReadonlyMap<string, string>): string | null {
179
+ const parts = Array.from(statuses.entries())
180
+ .filter(([, text]) => text.trim().length > 0)
181
+ .map(([key, text]) => `${key}:${text}`);
182
+
183
+ return parts.length > 0 ? parts.join(" ") : null;
184
+ }
185
+
143
186
  export default function (pi: ExtensionAPI) {
144
187
  let requestRender: (() => void) | undefined;
145
188
  const refresh = () => requestRender?.();
@@ -157,7 +200,7 @@ export default function (pi: ExtensionAPI) {
157
200
  const icons = ICON_PRESETS[iconPreset];
158
201
  const { warningThreshold, errorThreshold } = parseThresholds();
159
202
 
160
- ctx.ui.setFooter((tui, theme) => {
203
+ ctx.ui.setFooter((tui, theme, footerData) => {
161
204
  requestRender = () => tui.requestRender();
162
205
 
163
206
  return {
@@ -168,6 +211,9 @@ export default function (pi: ExtensionAPI) {
168
211
  render(width: number): string[] {
169
212
  const modelName = formatModelName(ctx.model?.id);
170
213
  const thinkingLevel = String(pi.getThinkingLevel());
214
+ const extensionStatuses = formatExtensionStatuses(
215
+ footerData?.getExtensionStatuses?.() ?? new Map(),
216
+ );
171
217
  const usage = ctx.getContextUsage();
172
218
  const contextSegmentColor = contextColor(
173
219
  usage?.percent,
@@ -179,7 +225,7 @@ export default function (pi: ExtensionAPI) {
179
225
  ? `${usage.percent !== null ? `${usage.percent.toFixed(1)}%` : "—%"} / ${formatTokens(usage.contextWindow)}`
180
226
  : "—";
181
227
 
182
- const segmentRenderers: Record<SegmentName, string> = {
228
+ const segmentRenderers: Record<SegmentName, string | null> = {
183
229
  model: theme.fg(
184
230
  "accent",
185
231
  prefixed(icons.model, modelName, iconPreset),
@@ -192,11 +238,18 @@ export default function (pi: ExtensionAPI) {
192
238
  contextSegmentColor,
193
239
  prefixed(icons.context, contextText, iconPreset),
194
240
  ),
241
+ extensions: extensionStatuses
242
+ ? theme.fg(
243
+ "muted",
244
+ prefixed(icons.extensions, extensionStatuses, iconPreset),
245
+ )
246
+ : null,
195
247
  };
196
248
 
197
249
  const separator = ` ${theme.fg("dim", icons.separator)} `;
198
250
  const line = visibleSegments
199
251
  .map((segment) => segmentRenderers[segment])
252
+ .filter((segment): segment is string => segment !== null)
200
253
  .join(separator);
201
254
 
202
255
  return [truncateToWidth(line, width)];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-bar",
3
- "version": "0.3.1",
4
- "description": "Never accidentally run Opus on a typo. Pi footer/statusline showing active model, thinking level, and context usage.",
3
+ "version": "0.3.2",
4
+ "description": "Never accidentally run Opus on a typo. Pi footer/statusline showing active model, thinking level, context usage, and extension statuses.",
5
5
  "type": "module",
6
6
  "keywords": [
7
7
  "pi-package",
@@ -46,6 +46,6 @@
46
46
  "extensions": [
47
47
  "./extensions/status-footer.ts"
48
48
  ],
49
- "image": "https://cdn.jsdelivr.net/npm/pi-bar@0.3.0/assets/screenshot.png"
49
+ "image": "https://cdn.jsdelivr.net/npm/pi-bar@0.3.2/assets/screenshot.png"
50
50
  }
51
51
  }