pi-tropos 0.6.0 → 0.8.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 +22 -0
- package/index.ts +2 -0
- package/package.json +1 -1
- package/src/footer.ts +114 -0
- package/src/sessions.ts +6 -1
package/README.md
CHANGED
|
@@ -21,6 +21,11 @@ Personal pi customization bundle. One package, installable anywhere.
|
|
|
21
21
|
- **`kaapi` theme** — filter-coffee dark: deep espresso base (`#15100D`), latte-foam text
|
|
22
22
|
(`#E8DCCB`), caramel accent (`#D9984A`), matcha success, terracotta error. Warm and
|
|
23
23
|
caffeinated. Select via `/settings` or `"theme": "kaapi"`.
|
|
24
|
+
- **Clean footer** — one line: `project branch` left; `↑in ↓out Rcache Wcache $cost` plus
|
|
25
|
+
the dumbzone gauge right. Zero-valued counters hide; no full paths, no cache-hit %,
|
|
26
|
+
no session titles. Auto-named sessions are sanitized (no markdown table junk).
|
|
27
|
+
- **`/zen` mode** — reading mode for brainstorming: collapses all tool output and shrinks
|
|
28
|
+
the footer to `$cost` + the ctx gauge. `/zen` again restores your previous state.
|
|
24
29
|
- **Barista mascot** — the working spinner becomes a steaming cup of kaapi; every other
|
|
25
30
|
brew cycle it's a boba 🧋. Hidden thinking blocks read "Steeping…".
|
|
26
31
|
- **pi-smritikosh** — semantic codebase search, bundled as a dependency. One install
|
|
@@ -42,3 +47,20 @@ npm run typecheck
|
|
|
42
47
|
```
|
|
43
48
|
|
|
44
49
|
No build step — pi loads the TypeScript directly.
|
|
50
|
+
|
|
51
|
+
## Font (recommended)
|
|
52
|
+
|
|
53
|
+
pi inherits the terminal's font — it can't set one. In VS Code (where pi usually runs):
|
|
54
|
+
|
|
55
|
+
```jsonc
|
|
56
|
+
// settings.json
|
|
57
|
+
"terminal.integrated.fontFamily": "JetBrains Mono",
|
|
58
|
+
"terminal.integrated.fontSize": 14,
|
|
59
|
+
"terminal.integrated.lineHeight": 1.35,
|
|
60
|
+
"terminal.integrated.fontLigatures.enabled": true
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Good code-reading fonts: **JetBrains Mono** (free, tall x-height, superb at small sizes —
|
|
64
|
+
`brew install --cask font-jetbrains-mono`), **SF Mono** (preinstalled on macOS),
|
|
65
|
+
**Monaspace Neon** (GitHub's family — `brew install --cask font-monaspace`), **Berkeley
|
|
66
|
+
Mono** (paid, beautiful). Size 13–14 + lineHeight 1.3–1.4 is the biggest readability win.
|
package/index.ts
CHANGED
|
@@ -2,11 +2,13 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
2
2
|
import smritikosh from "pi-smritikosh/index.js";
|
|
3
3
|
import barista from "./src/barista.js";
|
|
4
4
|
import dumbzone from "./src/dumbzone.js";
|
|
5
|
+
import footer from "./src/footer.js";
|
|
5
6
|
import sessions from "./src/sessions.js";
|
|
6
7
|
|
|
7
8
|
export default function (pi: ExtensionAPI) {
|
|
8
9
|
barista(pi);
|
|
9
10
|
dumbzone(pi);
|
|
11
|
+
footer(pi);
|
|
10
12
|
sessions(pi);
|
|
11
13
|
smritikosh(pi);
|
|
12
14
|
}
|
package/package.json
CHANGED
package/src/footer.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Footer — clean one-line status bar + /zen mode.
|
|
3
|
+
*
|
|
4
|
+
* Normal: `project branch` left; `↑in ↓out Rcache Wcache $cost` + extension
|
|
5
|
+
* statuses (e.g. the dumbzone ctx gauge) right. Zero-valued counters hide.
|
|
6
|
+
*
|
|
7
|
+
* /zen: collapses all tool output and shrinks the footer to just
|
|
8
|
+
* `$cost` + the ctx gauge. Reading mode for brainstorming — the chat
|
|
9
|
+
* becomes the content. /zen again restores your previous state.
|
|
10
|
+
* (Zen lives here because the footer owns the chrome it minimizes.)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
14
|
+
import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
15
|
+
|
|
16
|
+
interface Usage {
|
|
17
|
+
input: number;
|
|
18
|
+
output: number;
|
|
19
|
+
cacheRead: number;
|
|
20
|
+
cacheWrite: number;
|
|
21
|
+
cost: { total: number };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface Totals {
|
|
25
|
+
input: number;
|
|
26
|
+
output: number;
|
|
27
|
+
cacheRead: number;
|
|
28
|
+
cacheWrite: number;
|
|
29
|
+
cost: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function fmt(n: number) {
|
|
33
|
+
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
|
|
34
|
+
if (n >= 1_000) return `${Math.round(n / 1_000)}k`;
|
|
35
|
+
return `${n}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function totals(ctx: ExtensionContext): Totals {
|
|
39
|
+
const t: Totals = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 };
|
|
40
|
+
for (const e of ctx.sessionManager.getBranch()) {
|
|
41
|
+
if (e.type !== "message" || e.message.role !== "assistant") continue;
|
|
42
|
+
const u = (e.message as { usage?: Usage }).usage;
|
|
43
|
+
if (!u) continue;
|
|
44
|
+
t.input += u.input;
|
|
45
|
+
t.output += u.output;
|
|
46
|
+
t.cacheRead += u.cacheRead ?? 0;
|
|
47
|
+
t.cacheWrite += u.cacheWrite ?? 0;
|
|
48
|
+
t.cost += u.cost?.total ?? 0;
|
|
49
|
+
}
|
|
50
|
+
return t;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function telemetryLine(t: Totals) {
|
|
54
|
+
if (t.input === 0) return "";
|
|
55
|
+
const parts = [`↑${fmt(t.input)}`, `↓${fmt(t.output)}`];
|
|
56
|
+
if (t.cacheRead > 0) parts.push(`R${fmt(t.cacheRead)}`);
|
|
57
|
+
if (t.cacheWrite > 0) parts.push(`W${fmt(t.cacheWrite)}`);
|
|
58
|
+
parts.push(`$${t.cost.toFixed(2)}`);
|
|
59
|
+
return parts.join(" ");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let zen = false;
|
|
63
|
+
let wasExpanded = true;
|
|
64
|
+
|
|
65
|
+
export default function footer(pi: ExtensionAPI) {
|
|
66
|
+
pi.registerCommand("zen", {
|
|
67
|
+
description: "Toggle zen mode — collapse tool noise, minimal footer",
|
|
68
|
+
handler: async (_args, ctx) => {
|
|
69
|
+
if (ctx.mode !== "tui") {
|
|
70
|
+
ctx.ui.notify("/zen needs interactive mode", "error");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
zen = !zen;
|
|
74
|
+
if (zen) {
|
|
75
|
+
wasExpanded = ctx.ui.getToolsExpanded();
|
|
76
|
+
ctx.ui.setToolsExpanded(false);
|
|
77
|
+
ctx.ui.notify("Zen ☁️ — Ctrl+T hides thinking · /zen to exit", "info");
|
|
78
|
+
} else {
|
|
79
|
+
ctx.ui.setToolsExpanded(wasExpanded);
|
|
80
|
+
ctx.ui.notify("Zen off — full telemetry restored", "info");
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
pi.on("session_start", (_event, ctx) => {
|
|
86
|
+
zen = false;
|
|
87
|
+
if (!ctx.hasUI) return;
|
|
88
|
+
ctx.ui.setFooter((tui, theme, footerData) => {
|
|
89
|
+
const unsub = footerData.onBranchChange(() => tui.requestRender());
|
|
90
|
+
return {
|
|
91
|
+
dispose: unsub,
|
|
92
|
+
invalidate() {},
|
|
93
|
+
render(width: number): string[] {
|
|
94
|
+
const t = totals(ctx);
|
|
95
|
+
const statuses = [...footerData.getExtensionStatuses().values()].join(" ");
|
|
96
|
+
|
|
97
|
+
if (zen) {
|
|
98
|
+
const right = [t.cost > 0 ? `$${t.cost.toFixed(2)}` : "", statuses].filter(Boolean).join(" ");
|
|
99
|
+
return [truncateToWidth(theme.fg("dim", right), width)];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const dir = ctx.cwd.split("/").pop() ?? ctx.cwd;
|
|
103
|
+
const branch = footerData.getGitBranch();
|
|
104
|
+
const left = theme.fg("dim", branch ? `${dir} ${branch}` : dir);
|
|
105
|
+
const metrics = theme.fg("dim", telemetryLine(t));
|
|
106
|
+
const right = [metrics, statuses].filter(Boolean).join(" ");
|
|
107
|
+
|
|
108
|
+
const pad = " ".repeat(Math.max(1, width - visibleWidth(left) - visibleWidth(right)));
|
|
109
|
+
return [truncateToWidth(left + pad + right, width)];
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
}
|
package/src/sessions.ts
CHANGED
|
@@ -16,7 +16,12 @@ import type { ExtensionAPI, ExtensionContext, SessionInfo } from "@earendil-work
|
|
|
16
16
|
const TITLE_MAX = 60;
|
|
17
17
|
|
|
18
18
|
function oneLine(text: string, max: number) {
|
|
19
|
-
|
|
19
|
+
// Strip markdown noise (tables, emphasis, headings) so pasted SPEC
|
|
20
|
+
// rows and the like don't become unreadable titles.
|
|
21
|
+
const line = text
|
|
22
|
+
.replace(/[|*#`>\[\]]+/g, " ")
|
|
23
|
+
.replace(/\s+/g, " ")
|
|
24
|
+
.trim();
|
|
20
25
|
return line.length > max ? `${line.slice(0, max - 1)}…` : line;
|
|
21
26
|
}
|
|
22
27
|
|