@henryqw/pi-footer 0.5.5 → 0.6.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/README.md CHANGED
@@ -32,7 +32,7 @@ pi-packages · clear-field-f8d2 · PR #123 · approved Codex #1 · 50% · 7d
32
32
  ```
33
33
 
34
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. The active model and thinking level are right-aligned.
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
36
  - The third line shows other extension statuses on the left and cumulative agent-work time on the right, beneath the active model.
37
37
 
38
38
  Unavailable values render as `—` without a misleading percent sign.
@@ -51,7 +51,7 @@ Statuses from all other extensions, including Ponytail and `pi-rewind`, share th
51
51
 
52
52
  ## Clickable checkout
53
53
 
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.
54
+ When `~/.pi/agent/config/pi-open-in/config.json` sets `command` to exactly `code` and Pi reports hyperlink support, the accent-colored checkout name is an OSC 8 `vscode://` link to the current path. A missing file silently uses `code` and gets the same link. Other configured commands and terminals with hyperlinks disabled render plain text.
55
55
 
56
56
  Use Pi fullscreen TUI so Pi handles the custom URI:
57
57
 
@@ -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
- function isValidMilliseconds(value: unknown): value is number {
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 && isValidMilliseconds(entry.data)) {
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: Usage | undefined) => {
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.5",
3
+ "version": "0.6.0",
4
4
  "description": "Show concise repository, branch, and usage details in the Pi footer.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -45,6 +45,6 @@
45
45
  ]
46
46
  },
47
47
  "dependencies": {
48
- "@henryqw/pi-open-in": "^0.2.0"
48
+ "@henryqw/pi-open-in": "^1.0.0"
49
49
  }
50
50
  }