pi-tropos 0.9.1 → 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 CHANGED
@@ -25,7 +25,9 @@ Personal pi customization bundle. One package, installable anywhere.
25
25
  plus the dumbzone `ctx` gauge right. Zero-valued counters hide; no full paths, no
26
26
  cache-hit %, no session titles. Auto-named sessions are sanitized (no markdown junk).
27
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).
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.
29
31
  - **`/zen` mode** — reading mode for brainstorming: collapses all tool output and shrinks
30
32
  the footer to `$cost` + the ctx gauge. `/zen` again restores your previous state.
31
33
  - **`/peek <path>`** — transient code viewer: syntax-highlighted, line-numbered overlay.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-tropos",
3
- "version": "0.9.1",
3
+ "version": "0.10.1",
4
4
  "description": "Personal pi customization bundle — context-health indicator, session handoff, session browser",
5
5
  "type": "module",
6
6
  "keywords": [
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 = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 };
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
- function telemetryLine(t: Totals) {
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 parts = [`in ${fmt(t.input)}`, `out ${fmt(t.output)}`];
56
- if (t.cacheRead > 0) parts.push(`cached ${fmt(t.cacheRead)}`);
57
- if (t.cacheWrite > 0) parts.push(`stored ${fmt(t.cacheWrite)}`);
58
- parts.push(`$${t.cost.toFixed(2)}`);
59
- return parts.join(" ");
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 = theme.fg("dim", telemetryLine(t));
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
- const target = args.trim();
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;