pi-tropos 0.8.0 → 0.9.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
@@ -21,11 +21,15 @@ 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).
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).
27
29
  - **`/zen` mode** — reading mode for brainstorming: collapses all tool output and shrinks
28
30
  the footer to `$cost` + the ctx gauge. `/zen` again restores your previous state.
31
+ - **`/peek <path>`** — transient code viewer: syntax-highlighted, line-numbered overlay.
32
+ Scroll with ↑/↓/jk/PgUp/PgDn/g/G, Esc or q closes. Leaves no trace in the chat.
29
33
  - **Barista mascot** — the working spinner becomes a steaming cup of kaapi; every other
30
34
  brew cycle it's a boba 🧋. Hidden thinking blocks read "Steeping…".
31
35
  - **pi-smritikosh** — semantic codebase search, bundled as a dependency. One install
package/index.ts CHANGED
@@ -3,12 +3,14 @@ import smritikosh from "pi-smritikosh/index.js";
3
3
  import barista from "./src/barista.js";
4
4
  import dumbzone from "./src/dumbzone.js";
5
5
  import footer from "./src/footer.js";
6
+ import peek from "./src/peek.js";
6
7
  import sessions from "./src/sessions.js";
7
8
 
8
9
  export default function (pi: ExtensionAPI) {
9
10
  barista(pi);
10
11
  dumbzone(pi);
11
12
  footer(pi);
13
+ peek(pi);
12
14
  sessions(pi);
13
15
  smritikosh(pi);
14
16
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-tropos",
3
- "version": "0.8.0",
3
+ "version": "0.9.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
@@ -52,11 +52,11 @@ function totals(ctx: ExtensionContext): Totals {
52
52
 
53
53
  function telemetryLine(t: Totals) {
54
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)}`);
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
58
  parts.push(`$${t.cost.toFixed(2)}`);
59
- return parts.join(" ");
59
+ return parts.join(" ");
60
60
  }
61
61
 
62
62
  let zen = false;
package/src/peek.ts ADDED
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Peek — transient code viewer overlay. Leaves no trace in the chat.
3
+ *
4
+ * /peek src/footer.ts
5
+ *
6
+ * Opens a centered overlay with the file syntax-highlighted (kaapi
7
+ * theme colors) and line-numbered. Scroll with ↑/↓, PgUp/PgDn, g/G;
8
+ * Esc or q closes. Nothing is appended to the transcript.
9
+ */
10
+
11
+ import { readFileSync, statSync } from "node:fs";
12
+ import { isAbsolute, join } from "node:path";
13
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
14
+ import { getLanguageFromPath, highlightCode } from "@earendil-works/pi-coding-agent";
15
+ import { ScrollView, truncateToWidth } from "@earendil-works/pi-tui";
16
+
17
+ const MAX_BYTES = 200_000;
18
+
19
+ export default function peek(pi: ExtensionAPI) {
20
+ pi.registerCommand("peek", {
21
+ description: "Peek at a file in a transient overlay — /peek <path>",
22
+ handler: async (args, ctx) => {
23
+ if (ctx.mode !== "tui") {
24
+ ctx.ui.notify("/peek needs interactive mode", "error");
25
+ return;
26
+ }
27
+ const target = args.trim();
28
+ if (!target) {
29
+ ctx.ui.notify("Usage: /peek <path>", "info");
30
+ return;
31
+ }
32
+ const path = isAbsolute(target) ? target : join(ctx.cwd, target);
33
+
34
+ let content: string;
35
+ try {
36
+ const stat = statSync(path);
37
+ if (!stat.isFile()) throw new Error("not a file");
38
+ if (stat.size > MAX_BYTES) throw new Error("file too large");
39
+ content = readFileSync(path, "utf-8");
40
+ } catch (err) {
41
+ ctx.ui.notify(`Cannot peek: ${err instanceof Error ? err.message : err}`, "error");
42
+ return;
43
+ }
44
+
45
+ const lang = getLanguageFromPath(path);
46
+ const highlighted = highlightCode(content, lang);
47
+
48
+ await ctx.ui.custom(
49
+ (tui, theme, _kb, done) => {
50
+ const gutter = String(highlighted.length).length;
51
+ const numbered = highlighted.map(
52
+ (line, i) => theme.fg("dim", `${String(i + 1).padStart(gutter)} │ `) + line,
53
+ );
54
+ const inner = {
55
+ invalidate() {},
56
+ render: (width: number) => numbered.map((l) => truncateToWidth(l, width - 2)),
57
+ };
58
+ const scroll = new ScrollView(inner, { follow: "none", overscroll: "contain" });
59
+
60
+ return {
61
+ invalidate() {},
62
+ render: (width: number) => scroll.render(width),
63
+ handleInput(data: string) {
64
+ if (data === "\x1b" || data === "q") return done(undefined);
65
+ if (data === "\x1b[A" || data === "k") scroll.scrollBy(-1);
66
+ else if (data === "\x1b[B" || data === "j") scroll.scrollBy(1);
67
+ else if (data === "\x1b[5~") scroll.scrollBy(-10);
68
+ else if (data === "\x1b[6~") scroll.scrollBy(10);
69
+ else if (data === "g") scroll.scrollToStart();
70
+ else if (data === "G") scroll.scrollToEnd();
71
+ tui.requestRender();
72
+ },
73
+ };
74
+ },
75
+ {
76
+ overlay: true,
77
+ overlayOptions: { width: "85%", maxHeight: "80%", anchor: "center" },
78
+ },
79
+ );
80
+ },
81
+ });
82
+ }