pi-tropos 0.9.0 → 0.10.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 +7 -3
- package/package.json +1 -1
- package/src/footer.ts +35 -10
- package/src/peek.ts +2 -1
package/README.md
CHANGED
|
@@ -21,9 +21,13 @@ 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;
|
|
25
|
-
the dumbzone gauge right. Zero-valued counters hide; no full paths, no
|
|
26
|
-
no session titles. Auto-named sessions are sanitized (no markdown
|
|
24
|
+
- **Clean footer** — one line: `project branch` left; `in · out · cached · stored · $cost`
|
|
25
|
+
plus the dumbzone `ctx` gauge right. Zero-valued counters hide; no full paths, no
|
|
26
|
+
cache-hit %, no session titles. Auto-named sessions are sanitized (no markdown junk).
|
|
27
|
+
`ctx` is the live context snapshot; the others are cumulative session billing counters
|
|
28
|
+
(`cached`/`stored` = provider prompt-cache reads/writes, re-read every turn). The
|
|
29
|
+
counter with the highest share of session spend is highlighted in the accent color —
|
|
30
|
+
typically `cached`, which is why keeping ctx small (the dumb-zone rule) also saves money.
|
|
27
31
|
- **`/zen` mode** — reading mode for brainstorming: collapses all tool output and shrinks
|
|
28
32
|
the footer to `$cost` + the ctx gauge. `/zen` again restores your previous state.
|
|
29
33
|
- **`/peek <path>`** — transient code viewer: syntax-highlighted, line-numbered overlay.
|
package/package.json
CHANGED
package/src/footer.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* (Zen lives here because the footer owns the chrome it minimizes.)
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
13
|
+
import type { ExtensionAPI, ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
|
|
14
14
|
import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
15
15
|
|
|
16
16
|
interface Usage {
|
|
@@ -18,7 +18,7 @@ interface Usage {
|
|
|
18
18
|
output: number;
|
|
19
19
|
cacheRead: number;
|
|
20
20
|
cacheWrite: number;
|
|
21
|
-
cost: { total: number };
|
|
21
|
+
cost: { total: number; input: number; output: number; cacheRead: number; cacheWrite: number };
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
interface Totals {
|
|
@@ -27,6 +27,8 @@ interface Totals {
|
|
|
27
27
|
cacheRead: number;
|
|
28
28
|
cacheWrite: number;
|
|
29
29
|
cost: number;
|
|
30
|
+
/** Per-component spend, so the dominant cost driver can be highlighted. */
|
|
31
|
+
costParts: { input: number; output: number; cacheRead: number; cacheWrite: number };
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
function fmt(n: number) {
|
|
@@ -36,7 +38,14 @@ function fmt(n: number) {
|
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
function totals(ctx: ExtensionContext): Totals {
|
|
39
|
-
const t: Totals = {
|
|
41
|
+
const t: Totals = {
|
|
42
|
+
input: 0,
|
|
43
|
+
output: 0,
|
|
44
|
+
cacheRead: 0,
|
|
45
|
+
cacheWrite: 0,
|
|
46
|
+
cost: 0,
|
|
47
|
+
costParts: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
48
|
+
};
|
|
40
49
|
for (const e of ctx.sessionManager.getBranch()) {
|
|
41
50
|
if (e.type !== "message" || e.message.role !== "assistant") continue;
|
|
42
51
|
const u = (e.message as { usage?: Usage }).usage;
|
|
@@ -46,17 +55,33 @@ function totals(ctx: ExtensionContext): Totals {
|
|
|
46
55
|
t.cacheRead += u.cacheRead ?? 0;
|
|
47
56
|
t.cacheWrite += u.cacheWrite ?? 0;
|
|
48
57
|
t.cost += u.cost?.total ?? 0;
|
|
58
|
+
t.costParts.input += u.cost?.input ?? 0;
|
|
59
|
+
t.costParts.output += u.cost?.output ?? 0;
|
|
60
|
+
t.costParts.cacheRead += u.cost?.cacheRead ?? 0;
|
|
61
|
+
t.costParts.cacheWrite += u.cost?.cacheWrite ?? 0;
|
|
49
62
|
}
|
|
50
63
|
return t;
|
|
51
64
|
}
|
|
52
65
|
|
|
53
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Build styled telemetry segments. The component with the highest share
|
|
68
|
+
* of session spend is highlighted in the accent color — that's where
|
|
69
|
+
* your money is going (usually cache reads: the whole conversation is
|
|
70
|
+
* re-read from cache every turn, so small ctx = smaller bill).
|
|
71
|
+
*/
|
|
72
|
+
function telemetryLine(t: Totals, theme: Theme) {
|
|
54
73
|
if (t.input === 0) return "";
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
74
|
+
const segments: { text: string; cost: number }[] = [
|
|
75
|
+
{ text: `in ${fmt(t.input)}`, cost: t.costParts.input },
|
|
76
|
+
{ text: `out ${fmt(t.output)}`, cost: t.costParts.output },
|
|
77
|
+
];
|
|
78
|
+
if (t.cacheRead > 0) segments.push({ text: `cached ${fmt(t.cacheRead)}`, cost: t.costParts.cacheRead });
|
|
79
|
+
if (t.cacheWrite > 0) segments.push({ text: `stored ${fmt(t.cacheWrite)}`, cost: t.costParts.cacheWrite });
|
|
80
|
+
|
|
81
|
+
const top = segments.reduce((a, b) => (b.cost > a.cost ? b : a));
|
|
82
|
+
const styled = segments.map((s) => theme.fg(s === top && s.cost > 0 ? "accent" : "dim", s.text));
|
|
83
|
+
styled.push(theme.fg("dim", `$${t.cost.toFixed(2)}`));
|
|
84
|
+
return styled.join(theme.fg("dim", " "));
|
|
60
85
|
}
|
|
61
86
|
|
|
62
87
|
let zen = false;
|
|
@@ -102,7 +127,7 @@ export default function footer(pi: ExtensionAPI) {
|
|
|
102
127
|
const dir = ctx.cwd.split("/").pop() ?? ctx.cwd;
|
|
103
128
|
const branch = footerData.getGitBranch();
|
|
104
129
|
const left = theme.fg("dim", branch ? `${dir} ${branch}` : dir);
|
|
105
|
-
const metrics =
|
|
130
|
+
const metrics = telemetryLine(t, theme);
|
|
106
131
|
const right = [metrics, statuses].filter(Boolean).join(" ");
|
|
107
132
|
|
|
108
133
|
const pad = " ".repeat(Math.max(1, width - visibleWidth(left) - visibleWidth(right)));
|
package/src/peek.ts
CHANGED
|
@@ -24,7 +24,8 @@ export default function peek(pi: ExtensionAPI) {
|
|
|
24
24
|
ctx.ui.notify("/peek needs interactive mode", "error");
|
|
25
25
|
return;
|
|
26
26
|
}
|
|
27
|
-
|
|
27
|
+
// Strip surrounding quotes — users habitually paste 'paths' or "paths".
|
|
28
|
+
const target = args.trim().replace(/^['"]|['"]$/g, "");
|
|
28
29
|
if (!target) {
|
|
29
30
|
ctx.ui.notify("Usage: /peek <path>", "info");
|
|
30
31
|
return;
|