@henryqw/pi-footer 2.0.1 → 2.1.1

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
@@ -17,20 +17,21 @@ pi install npm:@henryqw/pi-footer
17
17
  | [`@henryqw/pi-multi-codex`](https://pi.henry.wang/extensions/pi-multi-codex) | Improves | Adds active Codex subscription quota and reset status. |
18
18
  | [`@henryqw/pi-open-in`](https://pi.henry.wang/extensions/pi-open-in) | Improves | Adds `/open` and `/set-open-in` commands for editor configuration. |
19
19
  | [`@henryqw/pi-pr`](https://pi.henry.wang/extensions/pi-pr) | Improves | Adds current-branch pull-request status. |
20
+ | [`@henryqw/pi-codegraph`](https://pi.henry.wang/extensions/pi-codegraph) | Improves | Shows CodeGraph index and direct tool activity when loaded. |
20
21
 
21
22
  ## Use
22
23
 
23
- The footer shows checkout, usage, model, thinking, and extension statuses.
24
+ After installing, start a Pi TUI session to see checkout, usage, model, thinking, and extension statuses.
24
25
 
25
26
  ```text
26
27
  pi-harness · clear-field-f8d2 [+2 ~3 ?1 ↑2] · PR #123 · approved Codex #1 · 50% · 7d 1d 1h 22m
27
28
  ↑ 12.4k · ↓ 2.1k · ↺ 84.3% · ⚡ 87.4 t/s · $ 0.127 · ◔ 36.8% gpt-5.6-luna • high
28
- ● 🐴 ponytail: ⚡ FULL ◷ 12m 34s
29
+ ✓ CG · ● 🐴 ponytail: ⚡ FULL ◷ 12m 34s
29
30
  ```
30
31
 
31
32
  - The first line shows the repository, branch, Git state, and `pi-pr` pull request status. Linked-worktree branches drop the generated `worktree/` prefix.
32
33
  - 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.
33
- - The third line shows other extension statuses on the left and cumulative agent-work time on the right, beneath the active model.
34
+ - The third line shows a compact CodeGraph badge first when loaded: `✓ CG` indexed, `● CG` in use, `◐ CG` checking or indexing, `○ CG` missing, `! CG` setup problem, or `? CG` unknown state. Its icon uses the active theme's state color; `CG` remains plain so the badge is readable without color. When pi-codegraph is not loaded, no badge appears. A middle dot separates statuses from different extensions; cumulative agent-work time stays on the right, beneath the active model.
34
35
 
35
36
  Git badges appear only when action is needed:
36
37
 
@@ -57,7 +58,7 @@ Unavailable values render as `—` without a misleading percent sign.
57
58
 
58
59
  Non-empty statuses from `@henryqw` extensions, currently Codex quota, occupy the right side of the first line.
59
60
 
60
- 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.
61
+ Statuses from 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. Scripted `mcpScript` calls are not visible as individual tool calls, so they do not trigger the `● CG` badge.
61
62
 
62
63
  ## Limits and recovery
63
64
 
@@ -1,6 +1,6 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import { basename, dirname, join } from "node:path";
3
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
3
+ import type { ExtensionAPI, Theme } from "@earendil-works/pi-coding-agent";
4
4
  import { getCapabilities, hyperlink, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
5
5
  import { configuredOpenUri } from "@henryqw/pi-open-in/open-uri";
6
6
 
@@ -167,6 +167,26 @@ function rainbow(text: string): string {
167
167
  return [...text].map((character, index) => color(character, colors[index % colors.length]!)).join("");
168
168
  }
169
169
 
170
+ function isCodegraphCall(toolName: string, args: unknown): boolean {
171
+ if (toolName === "codegraph_explore") return true;
172
+ if (toolName !== "mcp" || !args || typeof args !== "object") return false;
173
+ const { tool, server } = args as { tool?: unknown; server?: unknown };
174
+ return server === "henryqw_pi-codegraph__codegraph" && typeof tool === "string" && tool.startsWith("codegraph_");
175
+ }
176
+
177
+ function codegraphBadge(status: string, inUse: boolean, theme: Theme): string {
178
+ if (inUse) return `${theme.fg("accent", "●")} CG`;
179
+ switch (status) {
180
+ case "pi-codegraph: indexed": return `${theme.fg("success", "✓")} CG`;
181
+ case "pi-codegraph: missing": return `${theme.fg("dim", "○")} CG`;
182
+ case "pi-codegraph: checking index…":
183
+ case "pi-codegraph: indexing…": return `${theme.fg("warning", "◐")} CG`;
184
+ case "pi-codegraph: prerequisites missing":
185
+ case "pi-codegraph: setup failed": return `${theme.fg("error", "!")} CG`;
186
+ default: return `${theme.fg("warning", "?")} CG`;
187
+ }
188
+ }
189
+
170
190
  export default function footerExtension(pi: ExtensionAPI): void {
171
191
  let activeMilliseconds = 0;
172
192
  let activeStartedAt: number | undefined;
@@ -174,6 +194,15 @@ export default function footerExtension(pi: ExtensionAPI): void {
174
194
  let runtimeTimer: ReturnType<typeof setInterval> | undefined;
175
195
  let requestRuntimeRender: (() => void) | undefined;
176
196
  let refreshGitStatus: (() => Promise<void>) | undefined;
197
+ const activeCodegraphCalls = new Set<string>();
198
+ pi.on("tool_execution_start", (event) => {
199
+ if (!isCodegraphCall(event.toolName, event.args)) return;
200
+ activeCodegraphCalls.add(event.toolCallId);
201
+ requestRuntimeRender?.();
202
+ });
203
+ pi.on("tool_execution_end", (event) => {
204
+ if (activeCodegraphCalls.delete(event.toolCallId)) requestRuntimeRender?.();
205
+ });
177
206
  const stopRuntimeTimer = () => {
178
207
  if (runtimeTimer === undefined) return;
179
208
  clearInterval(runtimeTimer);
@@ -230,6 +259,7 @@ export default function footerExtension(pi: ExtensionAPI): void {
230
259
  activeStartedAt = undefined;
231
260
  promptPaused = false;
232
261
  refreshGitStatus = undefined;
262
+ activeCodegraphCalls.clear();
233
263
  });
234
264
 
235
265
  pi.on("session_start", async (_event, ctx) => {
@@ -238,6 +268,7 @@ export default function footerExtension(pi: ExtensionAPI): void {
238
268
  promptPaused = false;
239
269
  requestRuntimeRender = undefined;
240
270
  refreshGitStatus = undefined;
271
+ activeCodegraphCalls.clear();
241
272
  // Latest valid entry wins; stored data is untrusted.
242
273
  activeMilliseconds = 0;
243
274
  for (const entry of ctx.sessionManager.getEntries()) {
@@ -346,8 +377,10 @@ export default function footerExtension(pi: ExtensionAPI): void {
346
377
  const openUri = configuredOpenUri(ctx.cwd);
347
378
  const extensionStatuses = data.getExtensionStatuses();
348
379
  const prStatus = sanitizeStatus(extensionStatuses.get("pi-pr") ?? "");
380
+ const codegraphStatus = extensionStatuses.get("pi-codegraph");
381
+ const codegraph = codegraphStatus === undefined ? "" : codegraphBadge(codegraphStatus, activeCodegraphCalls.size > 0, theme);
349
382
  const statuses = [...extensionStatuses]
350
- .filter(([key]) => key !== "pi-pr")
383
+ .filter(([key]) => key !== "pi-pr" && key !== "pi-codegraph")
351
384
  .sort(([a], [b]) => a.localeCompare(b))
352
385
  .map(([key, text]) => [key, sanitizeStatus(text)] as const)
353
386
  .filter(([, text]) => Boolean(text));
@@ -385,7 +418,7 @@ export default function footerExtension(pi: ExtensionAPI): void {
385
418
  const lines = [
386
419
  firstLine,
387
420
  align(usage, model, width, ellipsis),
388
- alignRightReserved(externalStatuses.join(" "), runtime, width, ellipsis),
421
+ alignRightReserved([codegraph, ...externalStatuses].filter(Boolean).join(" · "), runtime, width, ellipsis),
389
422
  ];
390
423
  return lines.map((line) => truncateToWidth(line, width, ellipsis));
391
424
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@henryqw/pi-footer",
3
- "version": "2.0.1",
3
+ "version": "2.1.1",
4
4
  "description": "Show concise repository, branch, and usage details in the Pi footer.",
5
5
  "keywords": [
6
6
  "pi-package",