@mariozechner/pi-coding-agent 0.42.1 → 0.42.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.
Files changed (36) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +3 -1
  3. package/dist/core/extensions/types.d.ts +8 -2
  4. package/dist/core/extensions/types.d.ts.map +1 -1
  5. package/dist/core/extensions/types.js.map +1 -1
  6. package/dist/core/footer-data-provider.d.ts +25 -0
  7. package/dist/core/footer-data-provider.d.ts.map +1 -0
  8. package/dist/core/footer-data-provider.js +115 -0
  9. package/dist/core/footer-data-provider.js.map +1 -0
  10. package/dist/core/keybindings.d.ts +1 -1
  11. package/dist/core/keybindings.d.ts.map +1 -1
  12. package/dist/core/keybindings.js +2 -0
  13. package/dist/core/keybindings.js.map +1 -1
  14. package/dist/index.d.ts +1 -0
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  18. package/dist/modes/interactive/components/assistant-message.js +7 -2
  19. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  20. package/dist/modes/interactive/components/footer.d.ts +10 -25
  21. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  22. package/dist/modes/interactive/components/footer.js +27 -145
  23. package/dist/modes/interactive/components/footer.js.map +1 -1
  24. package/dist/modes/interactive/components/model-selector.d.ts +1 -1
  25. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  26. package/dist/modes/interactive/components/model-selector.js +10 -2
  27. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  28. package/dist/modes/interactive/interactive-mode.d.ts +6 -0
  29. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  30. package/dist/modes/interactive/interactive-mode.js +110 -20
  31. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  32. package/docs/tui.md +10 -9
  33. package/examples/extensions/custom-footer.ts +33 -55
  34. package/examples/extensions/with-deps/package-lock.json +2 -2
  35. package/examples/extensions/with-deps/package.json +1 -1
  36. package/package.json +4 -4
package/docs/tui.md CHANGED
@@ -624,23 +624,24 @@ ctx.ui.setWidget("my-widget", undefined);
624
624
 
625
625
  ### Pattern 6: Custom Footer
626
626
 
627
- Replace the entire footer with custom content.
627
+ Replace the footer. `footerData` exposes data not otherwise accessible to extensions.
628
628
 
629
629
  ```typescript
630
- ctx.ui.setFooter((_tui, theme) => ({
630
+ ctx.ui.setFooter((tui, theme, footerData) => ({
631
+ invalidate() {},
631
632
  render(width: number): string[] {
632
- const left = theme.fg("dim", "custom footer");
633
- const right = theme.fg("accent", "status");
634
- const padding = " ".repeat(Math.max(1, width - visibleWidth(left) - visibleWidth(right)));
635
- return [truncateToWidth(left + padding + right, width)];
633
+ // footerData.getGitBranch(): string | null
634
+ // footerData.getExtensionStatuses(): ReadonlyMap<string, string>
635
+ return [`${ctx.model?.id} (${footerData.getGitBranch() || "no git"})`];
636
636
  },
637
- invalidate() {},
637
+ dispose: footerData.onBranchChange(() => tui.requestRender()), // reactive
638
638
  }));
639
639
 
640
- // Restore default
641
- ctx.ui.setFooter(undefined);
640
+ ctx.ui.setFooter(undefined); // restore default
642
641
  ```
643
642
 
643
+ Token stats available via `ctx.sessionManager.getBranch()` and `ctx.model`.
644
+
644
645
  **Examples:** [custom-footer.ts](../examples/extensions/custom-footer.ts)
645
646
 
646
647
  ### Pattern 7: Custom Editor (vim mode, etc.)
@@ -1,8 +1,11 @@
1
1
  /**
2
- * Custom Footer Extension
2
+ * Custom Footer Extension - demonstrates ctx.ui.setFooter()
3
3
  *
4
- * Demonstrates ctx.ui.setFooter() for replacing the built-in footer
5
- * with a custom component showing session context usage.
4
+ * footerData exposes data not otherwise accessible:
5
+ * - getGitBranch(): current git branch
6
+ * - getExtensionStatuses(): texts from ctx.ui.setStatus()
7
+ *
8
+ * Token stats come from ctx.sessionManager/ctx.model (already accessible).
6
9
  */
7
10
 
8
11
  import type { AssistantMessage } from "@mariozechner/pi-ai";
@@ -10,76 +13,51 @@ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
10
13
  import { truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
11
14
 
12
15
  export default function (pi: ExtensionAPI) {
13
- let isCustomFooter = false;
16
+ let enabled = false;
14
17
 
15
- // Toggle custom footer with /footer command
16
18
  pi.registerCommand("footer", {
17
- description: "Toggle custom footer showing context usage",
19
+ description: "Toggle custom footer",
18
20
  handler: async (_args, ctx) => {
19
- isCustomFooter = !isCustomFooter;
21
+ enabled = !enabled;
22
+
23
+ if (enabled) {
24
+ ctx.ui.setFooter((tui, theme, footerData) => {
25
+ const unsub = footerData.onBranchChange(() => tui.requestRender());
20
26
 
21
- if (isCustomFooter) {
22
- ctx.ui.setFooter((_tui, theme) => {
23
27
  return {
28
+ dispose: unsub,
29
+ invalidate() {},
24
30
  render(width: number): string[] {
25
- // Calculate usage from branch entries
26
- let totalInput = 0;
27
- let totalOutput = 0;
28
- let totalCost = 0;
29
- let lastAssistant: AssistantMessage | undefined;
30
-
31
- for (const entry of ctx.sessionManager.getBranch()) {
32
- if (entry.type === "message" && entry.message.role === "assistant") {
33
- const msg = entry.message as AssistantMessage;
34
- totalInput += msg.usage.input;
35
- totalOutput += msg.usage.output;
36
- totalCost += msg.usage.cost.total;
37
- lastAssistant = msg;
31
+ // Compute tokens from ctx (already accessible to extensions)
32
+ let input = 0,
33
+ output = 0,
34
+ cost = 0;
35
+ for (const e of ctx.sessionManager.getBranch()) {
36
+ if (e.type === "message" && e.message.role === "assistant") {
37
+ const m = e.message as AssistantMessage;
38
+ input += m.usage.input;
39
+ output += m.usage.output;
40
+ cost += m.usage.cost.total;
38
41
  }
39
42
  }
40
43
 
41
- // Context percentage from last assistant message
42
- const contextTokens = lastAssistant
43
- ? lastAssistant.usage.input +
44
- lastAssistant.usage.output +
45
- lastAssistant.usage.cacheRead +
46
- lastAssistant.usage.cacheWrite
47
- : 0;
48
- const contextWindow = ctx.model?.contextWindow || 0;
49
- const contextPercent = contextWindow > 0 ? (contextTokens / contextWindow) * 100 : 0;
50
-
51
- // Format tokens
44
+ // Get git branch (not otherwise accessible)
45
+ const branch = footerData.getGitBranch();
52
46
  const fmt = (n: number) => (n < 1000 ? `${n}` : `${(n / 1000).toFixed(1)}k`);
53
47
 
54
- // Build footer line
55
- const left = [
56
- theme.fg("dim", `↑${fmt(totalInput)}`),
57
- theme.fg("dim", `↓${fmt(totalOutput)}`),
58
- theme.fg("dim", `$${totalCost.toFixed(3)}`),
59
- ].join(" ");
48
+ const left = theme.fg("dim", `↑${fmt(input)} ↓${fmt(output)} $${cost.toFixed(3)}`);
49
+ const branchStr = branch ? ` (${branch})` : "";
50
+ const right = theme.fg("dim", `${ctx.model?.id || "no-model"}${branchStr}`);
60
51
 
61
- // Color context percentage based on usage
62
- let contextStr = `${contextPercent.toFixed(1)}%`;
63
- if (contextPercent > 90) {
64
- contextStr = theme.fg("error", contextStr);
65
- } else if (contextPercent > 70) {
66
- contextStr = theme.fg("warning", contextStr);
67
- } else {
68
- contextStr = theme.fg("success", contextStr);
69
- }
70
-
71
- const right = `${contextStr} ${theme.fg("dim", ctx.model?.id || "no model")}`;
72
- const padding = " ".repeat(Math.max(1, width - visibleWidth(left) - visibleWidth(right)));
73
-
74
- return [truncateToWidth(left + padding + right, width)];
52
+ const pad = " ".repeat(Math.max(1, width - visibleWidth(left) - visibleWidth(right)));
53
+ return [truncateToWidth(left + pad + right, width)];
75
54
  },
76
- invalidate() {},
77
55
  };
78
56
  });
79
57
  ctx.ui.notify("Custom footer enabled", "info");
80
58
  } else {
81
59
  ctx.ui.setFooter(undefined);
82
- ctx.ui.notify("Built-in footer restored", "info");
60
+ ctx.ui.notify("Default footer restored", "info");
83
61
  }
84
62
  },
85
63
  });
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-with-deps",
3
- "version": "1.6.1",
3
+ "version": "1.6.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-with-deps",
9
- "version": "1.6.1",
9
+ "version": "1.6.2",
10
10
  "dependencies": {
11
11
  "ms": "^2.1.3"
12
12
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-with-deps",
3
3
  "private": true,
4
- "version": "1.6.1",
4
+ "version": "1.6.2",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mariozechner/pi-coding-agent",
3
- "version": "0.42.1",
3
+ "version": "0.42.2",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "piConfig": {
@@ -39,9 +39,9 @@
39
39
  },
40
40
  "dependencies": {
41
41
  "@mariozechner/clipboard": "^0.3.0",
42
- "@mariozechner/pi-agent-core": "^0.42.1",
43
- "@mariozechner/pi-ai": "^0.42.1",
44
- "@mariozechner/pi-tui": "^0.42.1",
42
+ "@mariozechner/pi-agent-core": "^0.42.2",
43
+ "@mariozechner/pi-ai": "^0.42.2",
44
+ "@mariozechner/pi-tui": "^0.42.2",
45
45
  "chalk": "^5.5.0",
46
46
  "cli-highlight": "^2.1.11",
47
47
  "diff": "^8.0.2",