pi-bar 0.3.0 → 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 +16 -12
- package/extensions/status-footer.ts +68 -15
- package/package.json +19 -3
package/README.md
CHANGED
|
@@ -3,23 +3,22 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/pi-bar)
|
|
4
4
|
[](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,
|
|
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
|
-

|
|
15
13
|
|
|
16
14
|
## Why use it?
|
|
17
15
|
|
|
18
16
|
- **See the active model at a glance** — catch accidental model switches before an expensive or sensitive task starts.
|
|
19
17
|
- **Track thinking level in place** — immediately notice when you are using the wrong reasoning setting.
|
|
20
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.
|
|
21
20
|
|
|
22
|
-
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
|
|
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.
|
|
23
22
|
|
|
24
23
|
## Install
|
|
25
24
|
|
|
@@ -40,11 +39,11 @@ pi-bar works out of the box, but you can tune it with environment variables befo
|
|
|
40
39
|
### Show or hide segments
|
|
41
40
|
|
|
42
41
|
```bash
|
|
43
|
-
PI_BAR_SHOW=model,thinking,context pi
|
|
42
|
+
PI_BAR_SHOW=model,thinking,context,extensions pi
|
|
44
43
|
PI_BAR_SHOW=model,context pi
|
|
45
44
|
```
|
|
46
45
|
|
|
47
|
-
Allowed segments are `model`, `thinking`, and `
|
|
46
|
+
Allowed segments are `model`, `thinking`, `context`, and `extensions`. The `extensions` segment is hidden when no extension has set a status.
|
|
48
47
|
|
|
49
48
|
### Change context thresholds
|
|
50
49
|
|
|
@@ -64,10 +63,10 @@ PI_BAR_ICONS=nerdfont pi
|
|
|
64
63
|
|
|
65
64
|
Supported presets:
|
|
66
65
|
|
|
67
|
-
- `unicode` — default, e.g. `◈ gpt-5.5 ❯ ✦ think:medium ❯ ◷ 42.7% / 272k`
|
|
68
|
-
- `plain` — maximum compatibility, e.g. `model:gpt-5.5 | think:medium | ctx:42.7% / 272k`
|
|
69
|
-
- `emoji` — colorful emoji markers, e.g. `🤖 gpt-5.5 › ✨ think:medium › 🧭 42.7% / 272k`
|
|
70
|
-
- `nerdfont` — icon-font glyphs for terminals configured with a Nerd Font, e.g.
|
|
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`
|
|
71
70
|
|
|
72
71
|
## Compatibility
|
|
73
72
|
|
|
@@ -77,6 +76,11 @@ The default `unicode` preset uses common Unicode symbols that render well in mos
|
|
|
77
76
|
PI_BAR_ICONS=plain pi
|
|
78
77
|
```
|
|
79
78
|
|
|
79
|
+
## Pairs well with
|
|
80
|
+
|
|
81
|
+
- **[pi-chrome](https://www.npmjs.com/package/pi-chrome)** — give your Pi agent your real, signed-in Chrome. Use pi-bar's red-context threshold as the signal to wrap up long browser scrapes before context overflows.
|
|
82
|
+
- **[pi-qq](https://www.npmjs.com/package/pi-qq)** — ask side questions about what the agent just did without polluting the transcript.
|
|
83
|
+
|
|
80
84
|
## Security note
|
|
81
85
|
|
|
82
86
|
Pi extensions run with your local user permissions. Review any pi package source before installing it.
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* pi-bar — footer/statusline extension.
|
|
3
3
|
*
|
|
4
|
-
* Replaces the built-in footer with
|
|
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,
|
|
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[] = [
|
|
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
|
-
{
|
|
36
|
+
{
|
|
37
|
+
model: string;
|
|
38
|
+
thinking: string;
|
|
39
|
+
context: string;
|
|
40
|
+
extensions: string;
|
|
41
|
+
separator: string;
|
|
42
|
+
}
|
|
32
43
|
> = {
|
|
33
|
-
unicode: {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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.
|
|
4
|
-
"description": "Never accidentally run Opus on a typo. Pi footer/statusline showing active model, thinking level, and
|
|
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",
|
|
@@ -16,6 +16,14 @@
|
|
|
16
16
|
"context-window"
|
|
17
17
|
],
|
|
18
18
|
"license": "MIT",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+ssh://git@github.com/tianrendong/pi-bar.git"
|
|
22
|
+
},
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/tianrendong/pi-bar/issues"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://github.com/tianrendong/pi-bar#readme",
|
|
19
27
|
"files": [
|
|
20
28
|
"assets",
|
|
21
29
|
"extensions",
|
|
@@ -26,10 +34,18 @@
|
|
|
26
34
|
"@earendil-works/pi-coding-agent": "*",
|
|
27
35
|
"@earendil-works/pi-tui": "*"
|
|
28
36
|
},
|
|
37
|
+
"peerDependenciesMeta": {
|
|
38
|
+
"@earendil-works/pi-coding-agent": {
|
|
39
|
+
"optional": true
|
|
40
|
+
},
|
|
41
|
+
"@earendil-works/pi-tui": {
|
|
42
|
+
"optional": true
|
|
43
|
+
}
|
|
44
|
+
},
|
|
29
45
|
"pi": {
|
|
30
46
|
"extensions": [
|
|
31
47
|
"./extensions/status-footer.ts"
|
|
32
48
|
],
|
|
33
|
-
"image": "https://cdn.jsdelivr.net/npm/pi-bar@0.3.
|
|
49
|
+
"image": "https://cdn.jsdelivr.net/npm/pi-bar@0.3.2/assets/screenshot.png"
|
|
34
50
|
}
|
|
35
51
|
}
|