@henryqw/pi-footer 0.5.4 → 0.5.6
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 +23 -14
- package/extensions/footer.ts +20 -4
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# `@henryqw/pi-footer`
|
|
2
2
|
|
|
3
|
-
Henry's opinionated Pi footer style
|
|
3
|
+
Henry's opinionated Pi footer style shows checkout identity, usage, and extension status.
|
|
4
4
|
|
|
5
5
|
## Why
|
|
6
6
|
|
|
7
|
-
- **Created for**:
|
|
8
|
-
- **Advantage**:
|
|
7
|
+
- **Created for**: Show concise checkout identity, usage, and extension status in the Pi footer.
|
|
8
|
+
- **Advantage**: Keep repository, PR, usage, model, and extension state visible without separate status commands.
|
|
9
9
|
|
|
10
10
|
## Install
|
|
11
11
|
|
|
@@ -17,14 +17,13 @@ pi install npm:@henryqw/pi-footer
|
|
|
17
17
|
|
|
18
18
|
| Package | Why |
|
|
19
19
|
| --- | --- |
|
|
20
|
-
| `@henryqw/pi-multi-codex` | Adds active Codex subscription quota and reset status. |
|
|
21
|
-
| `@henryqw/pi-open-in` | Adds `/open` and `/set-open-in` commands for editor configuration. |
|
|
20
|
+
| `@henryqw/pi-multi-codex` | Improves. Adds active Codex subscription quota and reset status. |
|
|
21
|
+
| `@henryqw/pi-open-in` | Improves. Adds `/open` and `/set-open-in` commands for editor configuration. |
|
|
22
|
+
| `@henryqw/pi-pr` | Improves. Adds current-branch pull-request status. |
|
|
22
23
|
|
|
23
24
|
## Use
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
| --- | --- | --- |
|
|
27
|
-
| Footer | UI | Show checkout, usage, model, thinking, and extension statuses. |
|
|
26
|
+
The footer shows checkout, usage, model, thinking, and extension statuses.
|
|
28
27
|
|
|
29
28
|
```text
|
|
30
29
|
pi-packages · clear-field-f8d2 · PR #123 · approved Codex #1 · 50% · 7d 1d 1h 22m
|
|
@@ -32,19 +31,27 @@ pi-packages · clear-field-f8d2 · PR #123 · approved Codex #1 · 50% · 7d
|
|
|
32
31
|
● 🐴 ponytail: ⚡ FULL ◷ 12m 34s
|
|
33
32
|
```
|
|
34
33
|
|
|
35
|
-
|
|
34
|
+
- The first line shows the repository, branch, and `pi-pr` pull request status after the clickable checkout link. Linked-worktree branches drop the generated `worktree/` prefix.
|
|
35
|
+
- The second line shows cumulative input tokens, output tokens, latest cache-hit rate, and tokens per second for the most recent assistant response. It also shows estimated cost and context usage. Totals include reported tool usage and finished `pi-subagent` background workflows. The active model and thinking level are right-aligned.
|
|
36
|
+
- The third line shows other extension statuses on the left and cumulative agent-work time on the right, beneath the active model.
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
Unavailable values render as `—` without a misleading percent sign.
|
|
38
39
|
|
|
39
|
-
`off` uses the same dim grey as the model name. Active levels use an ANSI-256 gradient: green `minimal`, yellow-green `low`, lime `medium`, yellow `high`, orange `xhigh`, and red `max`.
|
|
40
|
+
`off` uses the same dim grey as the model name. Active levels use an ANSI-256 gradient: green `minimal`, yellow-green `low`, lime `medium`, yellow `high`, orange `xhigh`, and red `max`.
|
|
41
|
+
|
|
42
|
+
`ultra` renders as a rainbow when the runtime supplies it. Pi 0.84.4 does not yet accept `ultra`, so that footer path remains unreachable until Pi adds it.
|
|
40
43
|
|
|
41
44
|
Non-empty statuses from `@henryqw` extensions, currently Codex quota, occupy the right side of the first line.
|
|
42
45
|
|
|
43
|
-
|
|
46
|
+
Agent-work time counts each run from `agent_start` through the final idle `agent_settled`. It includes automatic retries and auto-compaction inside that run. It excludes blocking user-prompt waits and idle waits between runs.
|
|
47
|
+
|
|
48
|
+
Standalone `/compact` is excluded because it runs outside the agent-run lifecycle and emits no `agent_start`. The cumulative total is persisted in the session through a `pi-footer:agent-work` custom entry after each finalized run. It is restored on session resume.
|
|
49
|
+
|
|
50
|
+
Statuses from all other extensions, including Ponytail and `pi-rewind`, share the left side. They are sorted by key with producer text, spacing, colors, links, and glyphs preserved.
|
|
44
51
|
|
|
45
52
|
## Clickable checkout
|
|
46
53
|
|
|
47
|
-
When `pi-open-in.json` command is exactly `code` and Pi reports hyperlink support, the accent-colored checkout name is an OSC 8 `vscode://` link to the current path. Other configured commands and terminals with hyperlinks disabled render plain text.
|
|
54
|
+
When the `pi-open-in.json` command is exactly `code` and Pi reports hyperlink support, the accent-colored checkout name is an OSC 8 `vscode://` link to the current path. Other configured commands and terminals with hyperlinks disabled render plain text.
|
|
48
55
|
|
|
49
56
|
Use Pi fullscreen TUI so Pi handles the custom URI:
|
|
50
57
|
|
|
@@ -54,4 +61,6 @@ Use Pi fullscreen TUI so Pi handles the custom URI:
|
|
|
54
61
|
}
|
|
55
62
|
```
|
|
56
63
|
|
|
57
|
-
Set this through `/settings`, or launch with `--tui-mode fullscreen`. Then use normal primary click. Regular TUI delegates OSC 8 activation to the terminal
|
|
64
|
+
Set this through `/settings`, or launch with `--tui-mode fullscreen`. Then use normal primary click. Regular TUI delegates OSC 8 activation to the terminal.
|
|
65
|
+
|
|
66
|
+
Ghostty uses `Cmd+click` but may not open custom URI schemes.
|
package/extensions/footer.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { basename, dirname } from "node:path";
|
|
2
|
-
import type { Usage } from "@earendil-works/pi-ai";
|
|
3
2
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
4
3
|
import { getCapabilities, hyperlink, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
5
4
|
import { configuredOpenUri } from "@henryqw/pi-open-in/open-uri";
|
|
@@ -14,11 +13,26 @@ const THINKING_COLORS = {
|
|
|
14
13
|
} as const;
|
|
15
14
|
const HENRY_STATUS_KEY = "pi-multi-codex";
|
|
16
15
|
const AGENT_TIME_ENTRY = "pi-footer:agent-work";
|
|
16
|
+
const SUBAGENT_BACKGROUND_RESULT = "subagent-background-result";
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
type CountedUsage = { input: number; output: number; cost: { total: number } };
|
|
19
|
+
|
|
20
|
+
function isNonNegativeNumber(value: unknown): value is number {
|
|
19
21
|
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
20
22
|
}
|
|
21
23
|
|
|
24
|
+
function subagentBackgroundUsage(details: unknown): CountedUsage | undefined {
|
|
25
|
+
if (!details || typeof details !== "object" || Array.isArray(details)) return;
|
|
26
|
+
const usage = (details as Record<string, unknown>).usage;
|
|
27
|
+
if (!usage || typeof usage !== "object" || Array.isArray(usage)) return;
|
|
28
|
+
const record = usage as Record<string, unknown>;
|
|
29
|
+
const cost = record.cost;
|
|
30
|
+
if (!cost || typeof cost !== "object" || Array.isArray(cost)) return;
|
|
31
|
+
const total = (cost as Record<string, unknown>).total;
|
|
32
|
+
if (!isNonNegativeNumber(record.input) || !isNonNegativeNumber(record.output) || !isNonNegativeNumber(total)) return;
|
|
33
|
+
return { input: record.input, output: record.output, cost: { total } };
|
|
34
|
+
}
|
|
35
|
+
|
|
22
36
|
function formatTokens(count: number): string {
|
|
23
37
|
if (count < 1_000) return `${count}`;
|
|
24
38
|
if (count < 1_000_000) return `${(count / 1_000).toFixed(1)}k`;
|
|
@@ -129,7 +143,7 @@ export default function footerExtension(pi: ExtensionAPI): void {
|
|
|
129
143
|
// Latest valid entry wins; stored data is untrusted.
|
|
130
144
|
activeMilliseconds = 0;
|
|
131
145
|
for (const entry of ctx.sessionManager.getEntries()) {
|
|
132
|
-
if (entry.type === "custom" && entry.customType === AGENT_TIME_ENTRY &&
|
|
146
|
+
if (entry.type === "custom" && entry.customType === AGENT_TIME_ENTRY && isNonNegativeNumber(entry.data)) {
|
|
133
147
|
activeMilliseconds = entry.data;
|
|
134
148
|
}
|
|
135
149
|
}
|
|
@@ -169,7 +183,7 @@ export default function footerExtension(pi: ExtensionAPI): void {
|
|
|
169
183
|
output = 0;
|
|
170
184
|
cost = 0;
|
|
171
185
|
cacheRate = undefined;
|
|
172
|
-
const add = (usage:
|
|
186
|
+
const add = (usage: CountedUsage | undefined) => {
|
|
173
187
|
if (!usage) return;
|
|
174
188
|
input += usage.input;
|
|
175
189
|
output += usage.output;
|
|
@@ -184,6 +198,8 @@ export default function footerExtension(pi: ExtensionAPI): void {
|
|
|
184
198
|
add(usage);
|
|
185
199
|
} else if (entry.type === "message" && entry.message.role === "toolResult") {
|
|
186
200
|
add(entry.message.usage);
|
|
201
|
+
} else if (entry.type === "custom_message" && entry.customType === SUBAGENT_BACKGROUND_RESULT) {
|
|
202
|
+
add(subagentBackgroundUsage(entry.details));
|
|
187
203
|
} else if (entry.type === "branch_summary" || entry.type === "compaction") {
|
|
188
204
|
add(entry.usage);
|
|
189
205
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@henryqw/pi-footer",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
4
4
|
"description": "Show concise repository, branch, and usage details in the Pi footer.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
},
|
|
15
15
|
"license": "MIT",
|
|
16
16
|
"files": [
|
|
17
|
+
"LICENSE",
|
|
17
18
|
"extensions",
|
|
18
|
-
"README.md"
|
|
19
|
-
"LICENSE"
|
|
19
|
+
"README.md"
|
|
20
20
|
],
|
|
21
21
|
"scripts": {
|
|
22
22
|
"test": "node --test test/*.test.ts",
|