pi-observability 1.0.0 → 1.3.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/.oxfmtrc.json ADDED
@@ -0,0 +1,3 @@
1
+ {
2
+ "ignorePatterns": []
3
+ }
package/.oxlintrc.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "$schema": "./node_modules/oxlint/configuration_schema.json",
3
+ "plugins": [
4
+ "typescript",
5
+ "unicorn",
6
+ "oxc"
7
+ ],
8
+ "categories": {
9
+ "correctness": "error"
10
+ },
11
+ "rules": {},
12
+ "env": {
13
+ "builtin": true
14
+ }
15
+ }
@@ -0,0 +1,222 @@
1
+ {
2
+ "lsp": {
3
+ "oxlint": {
4
+ "initialization_options": {
5
+ "settings": {
6
+ "configPath": "./.oxlintrc.json",
7
+ "run": "onType",
8
+ "disableNestedConfig": false,
9
+ "fixKind": "safe_fix",
10
+ "typeAware": true,
11
+ "unusedDisableDirectives": "deny"
12
+ }
13
+ }
14
+ },
15
+ "oxfmt": {
16
+ "initialization_options": {
17
+ "settings": {
18
+ "configPath": "./vite.config.ts",
19
+ "run": "onSave"
20
+ }
21
+ }
22
+ }
23
+ },
24
+ "languages": {
25
+ "CSS": {
26
+ "format_on_save": "on",
27
+ "prettier": {
28
+ "allowed": false
29
+ },
30
+ "formatter": [
31
+ {
32
+ "language_server": {
33
+ "name": "oxfmt"
34
+ }
35
+ }
36
+ ]
37
+ },
38
+ "HTML": {
39
+ "format_on_save": "on",
40
+ "prettier": {
41
+ "allowed": false
42
+ },
43
+ "formatter": [
44
+ {
45
+ "language_server": {
46
+ "name": "oxfmt"
47
+ }
48
+ }
49
+ ]
50
+ },
51
+ "JavaScript": {
52
+ "format_on_save": "on",
53
+ "prettier": {
54
+ "allowed": false
55
+ },
56
+ "formatter": [
57
+ {
58
+ "language_server": {
59
+ "name": "oxfmt"
60
+ }
61
+ }
62
+ ],
63
+ "code_action": "source.fixAll.oxc"
64
+ },
65
+ "JSX": {
66
+ "format_on_save": "on",
67
+ "prettier": {
68
+ "allowed": false
69
+ },
70
+ "formatter": [
71
+ {
72
+ "language_server": {
73
+ "name": "oxfmt"
74
+ }
75
+ }
76
+ ]
77
+ },
78
+ "JSON": {
79
+ "format_on_save": "on",
80
+ "prettier": {
81
+ "allowed": false
82
+ },
83
+ "formatter": [
84
+ {
85
+ "language_server": {
86
+ "name": "oxfmt"
87
+ }
88
+ }
89
+ ]
90
+ },
91
+ "JSON5": {
92
+ "format_on_save": "on",
93
+ "prettier": {
94
+ "allowed": false
95
+ },
96
+ "formatter": [
97
+ {
98
+ "language_server": {
99
+ "name": "oxfmt"
100
+ }
101
+ }
102
+ ]
103
+ },
104
+ "JSONC": {
105
+ "format_on_save": "on",
106
+ "prettier": {
107
+ "allowed": false
108
+ },
109
+ "formatter": [
110
+ {
111
+ "language_server": {
112
+ "name": "oxfmt"
113
+ }
114
+ }
115
+ ]
116
+ },
117
+ "Less": {
118
+ "format_on_save": "on",
119
+ "prettier": {
120
+ "allowed": false
121
+ },
122
+ "formatter": [
123
+ {
124
+ "language_server": {
125
+ "name": "oxfmt"
126
+ }
127
+ }
128
+ ]
129
+ },
130
+ "Markdown": {
131
+ "format_on_save": "on",
132
+ "prettier": {
133
+ "allowed": false
134
+ },
135
+ "formatter": [
136
+ {
137
+ "language_server": {
138
+ "name": "oxfmt"
139
+ }
140
+ }
141
+ ]
142
+ },
143
+ "MDX": {
144
+ "format_on_save": "on",
145
+ "prettier": {
146
+ "allowed": false
147
+ },
148
+ "formatter": [
149
+ {
150
+ "language_server": {
151
+ "name": "oxfmt"
152
+ }
153
+ }
154
+ ]
155
+ },
156
+ "SCSS": {
157
+ "format_on_save": "on",
158
+ "prettier": {
159
+ "allowed": false
160
+ },
161
+ "formatter": [
162
+ {
163
+ "language_server": {
164
+ "name": "oxfmt"
165
+ }
166
+ }
167
+ ]
168
+ },
169
+ "TypeScript": {
170
+ "format_on_save": "on",
171
+ "prettier": {
172
+ "allowed": false
173
+ },
174
+ "formatter": [
175
+ {
176
+ "language_server": {
177
+ "name": "oxfmt"
178
+ }
179
+ }
180
+ ]
181
+ },
182
+ "TSX": {
183
+ "format_on_save": "on",
184
+ "prettier": {
185
+ "allowed": false
186
+ },
187
+ "formatter": [
188
+ {
189
+ "language_server": {
190
+ "name": "oxfmt"
191
+ }
192
+ }
193
+ ]
194
+ },
195
+ "Vue.js": {
196
+ "format_on_save": "on",
197
+ "prettier": {
198
+ "allowed": false
199
+ },
200
+ "formatter": [
201
+ {
202
+ "language_server": {
203
+ "name": "oxfmt"
204
+ }
205
+ }
206
+ ]
207
+ },
208
+ "YAML": {
209
+ "format_on_save": "on",
210
+ "prettier": {
211
+ "allowed": false
212
+ },
213
+ "formatter": [
214
+ {
215
+ "language_server": {
216
+ "name": "oxfmt"
217
+ }
218
+ }
219
+ ]
220
+ }
221
+ }
222
+ }
package/DEVELOPMENT.md ADDED
@@ -0,0 +1,243 @@
1
+ # Development Workflow
2
+
3
+ This guide covers how to develop, test, and publish the `pi-observability` extension.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Project Structure](#project-structure)
8
+ - [Local Development](#local-development)
9
+ - [Testing Changes](#testing-changes)
10
+ - [Publishing](#publishing)
11
+ - [Versioning](#versioning)
12
+ - [Troubleshooting](#troubleshooting)
13
+
14
+ ---
15
+
16
+ ## Project Structure
17
+
18
+ ```
19
+ pi-observability/
20
+ ├── extensions/
21
+ │ └── observability.ts # Main extension entry point
22
+ ├── package.json # Package manifest + pi config
23
+ ├── tsconfig.json # TypeScript config
24
+ ├── README.md # User-facing docs
25
+ ├── DEVELOPMENT.md # This file
26
+ └── LICENSE
27
+ ```
28
+
29
+ The `pi` key in `package.json` declares what pi loads:
30
+
31
+ ```json
32
+ {
33
+ "pi": {
34
+ "extensions": ["./extensions/observability.ts"]
35
+ }
36
+ }
37
+ ```
38
+
39
+ ---
40
+
41
+ ## Local Development
42
+
43
+ ### 1. Clone and install
44
+
45
+ ```bash
46
+ git clone https://github.com/imran-vz/pi-observability.git
47
+ cd pi-observability
48
+ npm install
49
+ ```
50
+
51
+ ### 2. Link for live testing
52
+
53
+ The fastest way to iterate is to **symlink** the extension into pi's auto-discovery directory. This lets you edit the source file and hot-reload with `/reload`.
54
+
55
+ ```bash
56
+ npm run dev:link
57
+ ```
58
+
59
+ This creates a symlink:
60
+ ```
61
+ ~/.pi/agent/extensions/observability.ts → ./extensions/observability.ts
62
+ ```
63
+
64
+ > **Important:** If you previously had a copy (not symlink) at `~/.pi/agent/extensions/observability.ts`, this script removes it first.
65
+
66
+ ### 3. Start pi and test
67
+
68
+ ```bash
69
+ pi
70
+ ```
71
+
72
+ Make edits to `extensions/observability.ts`, then in pi run:
73
+
74
+ ```
75
+ /reload
76
+ ```
77
+
78
+ The extension reloads instantly. No need to restart pi.
79
+
80
+ ### 4. Unlink when done
81
+
82
+ ```bash
83
+ npm run dev:unlink
84
+ ```
85
+
86
+ This removes the symlink. To continue using the published version, reinstall:
87
+
88
+ ```bash
89
+ pi install npm:pi-observability
90
+ ```
91
+
92
+ ### Alternative: `-e` flag (quick tests)
93
+
94
+ For one-off testing without linking:
95
+
96
+ ```bash
97
+ pi -e ./extensions/observability.ts
98
+ ```
99
+
100
+ This loads the extension for that session only. Good for testing on a clean slate.
101
+
102
+ ### Alternative: Local path in settings
103
+
104
+ Add to `~/.pi/agent/settings.json`:
105
+
106
+ ```json
107
+ {
108
+ "extensions": ["/absolute/path/to/pi-observability/extensions/observability.ts"]
109
+ }
110
+ ```
111
+
112
+ ---
113
+
114
+ ## Testing Changes
115
+
116
+ Before publishing, verify:
117
+
118
+ 1. **Type check:**
119
+ ```bash
120
+ npm run typecheck
121
+ ```
122
+
123
+ 2. **Load in pi:**
124
+ ```bash
125
+ npm run dev:link
126
+ pi
127
+ ```
128
+
129
+ 3. **Test all commands:**
130
+ - `/obs` — Dashboard prints correctly
131
+ - `/obs-toggle` — Footer toggles on/off
132
+ - Footer updates during streaming
133
+ - History persists across sessions
134
+
135
+ 4. **Test edge cases:**
136
+ - Non-git directories (diff stats should show 0)
137
+ - Very long paths (truncation works)
138
+ - Context window exceeded (usage display)
139
+ - Multiple models in one session
140
+
141
+ ---
142
+
143
+ ## Publishing
144
+
145
+ ### Prerequisites
146
+
147
+ - Logged into npm: `npm login`
148
+ - Write access to the GitHub repo
149
+ - Clean working tree: `git status`
150
+
151
+ ### Release workflow
152
+
153
+ **Patch release** (bug fixes):
154
+ ```bash
155
+ npm run version:patch # bumps 1.0.0 → 1.0.1, tags, pushes
156
+ npm run publish:pkg # publishes to npm
157
+ ```
158
+
159
+ **Minor release** (new features):
160
+ ```bash
161
+ npm run version:minor # bumps 1.0.0 → 1.1.0
162
+ npm run publish:pkg
163
+ ```
164
+
165
+ **Major release** (breaking changes):
166
+ ```bash
167
+ npm run version:major # bumps 1.0.0 → 2.0.0
168
+ npm run publish:pkg
169
+ ```
170
+
171
+ ### What `npm version` does
172
+
173
+ 1. Updates `version` in `package.json`
174
+ 2. Creates a git commit: `1.0.1`
175
+ 3. Creates a git tag: `v1.0.1`
176
+ 4. Pushes commit + tag to origin
177
+
178
+ ### After publishing
179
+
180
+ Users update with:
181
+ ```bash
182
+ pi update
183
+ ```
184
+
185
+ Or reinstall to get the latest:
186
+ ```bash
187
+ pi remove npm:pi-observability
188
+ pi install npm:pi-observability
189
+ ```
190
+
191
+ ---
192
+
193
+ ## Versioning
194
+
195
+ We follow [SemVer](https://semver.org/):
196
+
197
+ | Version change | When to use |
198
+ |----------------|-------------|
199
+ | **Patch** `1.0.0 → 1.0.1` | Bug fixes, typo corrections, performance improvements |
200
+ | **Minor** `1.0.0 → 1.1.0` | New commands, new footer features, new metrics |
201
+ | **Major** `1.0.0 → 2.0.0` | Breaking changes (command renames, removed features) |
202
+
203
+ ---
204
+
205
+ ## Troubleshooting
206
+
207
+ ### Extension not loading after `/reload`
208
+
209
+ ```bash
210
+ # Check the symlink points to the right place
211
+ ls -la ~/.pi/agent/extensions/observability.ts
212
+
213
+ # If it's a copy instead of a symlink, remove and re-link
214
+ rm ~/.pi/agent/extensions/observability.ts
215
+ npm run dev:link
216
+ ```
217
+
218
+ ### Type errors from pi packages
219
+
220
+ Pi bundles its core packages at runtime. The `devDependencies` are only for IDE support. If TypeScript complains about missing modules during `npm run typecheck`, make sure you've run:
221
+
222
+ ```bash
223
+ npm install
224
+ ```
225
+
226
+ ### Published version not updating for users
227
+
228
+ npm has a TTL on package metadata. Users may need:
229
+ ```bash
230
+ pi remove npm:pi-observability
231
+ pi install npm:pi-observability
232
+ ```
233
+
234
+ Or wait a few minutes and run `pi update`.
235
+
236
+ ### Conflicts with local copy
237
+
238
+ If you have both the npm-installed version and a local symlink, pi may load both. Unlink during published-version testing:
239
+
240
+ ```bash
241
+ npm run dev:unlink
242
+ pi
243
+ ```
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # 🔭 pi-observability
2
2
 
3
- A [pi](https://github.com/mariozechner/pi) extension that replaces the default footer with a live observability bar and provides a full dashboard command.
3
+ A [pi](https://github.com/mariozechner/pi) extension that replaces the default footer with a live observability bar, provides a full dashboard command, and prints a TPS summary after each agent run.
4
4
 
5
5
  ## Features
6
6
 
@@ -8,19 +8,69 @@ A [pi](https://github.com/mariozechner/pi) extension that replaces the default f
8
8
  - Session input/output tokens & estimated cost
9
9
  - Live TPS (tokens per second) during streaming
10
10
  - Session runtime
11
- - Current model & git branch
11
+ - Current model, thinking level, fast mode & git branch
12
12
  - Git diff stats (added/removed lines)
13
13
  - Context usage (current / max)
14
+ - **Thinking level colors match pi's input field** — off/low/medium/high use the same theme colors as the editor border
15
+ - **Rainbow mode** — `xhigh` and `max` thinking levels render the model indicator in cycling rainbow colors
14
16
 
15
- - **`/obs` command** — Print a full observability dashboard with per-turn breakdowns and last 10 session history
17
+ - **`/obs` command** — Full-screen TUI dashboard with per-turn breakdowns and last 10 session history. Renders through pi's native TUI (no console spam), with theme-aware borders and dynamic terminal width.
18
+
19
+ - **End-of-run TPS notification** — Prints the legacy TPS summary after each agent run: output TPS, input/output tokens, cache read/write tokens, total tokens, and elapsed time.
16
20
 
17
21
  - **`/obs-toggle` command** — Toggle the live footer on/off
18
22
 
19
23
  ## Preview
20
24
 
25
+ ### Screed recording
26
+
27
+ > GitHub does not render inline MP4 players in `README.md`, so here's a short animated preview. Click it to open the full recording.
28
+
29
+ [![Animated preview of pi-observability](./demo-preview.gif)](./output.mp4)
30
+
31
+ [Open the full screed recording (MP4)](./output.mp4)
32
+
33
+ ### Footer
34
+
35
+ Compact single-line layout that falls back to two lines when the terminal is narrow:
36
+
37
+ ```
38
+ gpt-5.5:high ▸ ⏱ 12:34 ▸ 📁 my-app ▸  main +42 -7 ▸ ctx 4.2k/200k ▸ ↑1.2k ↓3.4k ▸ ⚡45.2 ▸ $0.0042
39
+ ```
40
+
41
+ With `xhigh` or `max` thinking, the model name renders in rainbow:
42
+
21
43
  ```
22
- ~/projects/my-app (main) +42 -7
23
- ⏱ 12:34 ctx 4.2k/200k ↑1.2k ↓3.4k $0.0042 ⚡ 45.2 tok/s claude-sonnet-4
44
+ gpt-5.5:xhigh ▸ ⏱ 12:34 ▸ 📁 my-app ↑1.2k ↓3.4k ▸ ⚡45.2 ▸ $0.0042
45
+ ```
46
+
47
+ ### Git diff in the status bar
48
+
49
+ ![Git diff in the footer status bar](./diff.png)
50
+
51
+ ### Dashboard (`/obs`)
52
+
53
+ ```
54
+ ┌──────────────────────────────────────────┐
55
+ │ Agent Observability Dashboard │
56
+ ├──────────────────────────────────────────┤
57
+ │ Runtime: 12:34 Dir: ~/projects/my-app │
58
+ │ Branch: main Model: claude-sonnet-4 │
59
+ ├──────────────────────────────────────────┤
60
+ │ Tokens: ↑1.2k ↓3.4k │
61
+ │ Cost: $0.004200 │
62
+ └──────────────────────────────────────────┘
63
+
64
+ TURNS (2)
65
+ # Input Output Time TPS Cost Model
66
+ ─────────────────────────────────────────────────
67
+ 1 ↑450 ↓1200 0:45 26.7 $0.00 claude-sonnet-4
68
+ 2 ↑320 ↓900 0:32 28.1 $0.00 claude-sonnet-4
69
+
70
+ LAST 10 SESSIONS
71
+ When Duration Turns Input Output Cost
72
+ ───────────────────────────────────────────────────────────
73
+ Apr 18, 04:19 PM 9:05 10 ↑110k ↓9.9k $0.00
24
74
  ```
25
75
 
26
76
  ## Install
@@ -39,34 +89,25 @@ pi install git:github.com/imran-vz/pi-observability
39
89
 
40
90
  ### Manual
41
91
 
42
- Copy `extensions/observability.ts` to `~/.pi/agent/extensions/observability.ts` (or `.pi/extensions/observability.ts` for project-local).
92
+ Copy the entire `extensions/` directory to `~/.pi/agent/extensions/` (or `.pi/extensions/` for project-local):
93
+
94
+ ```bash
95
+ cp -r extensions/* ~/.pi/agent/extensions/
96
+ ```
97
+
98
+ > **Note:** This extension is split into multiple files (`observability.ts` + `lib/`). Copying only the main file will break imports.
43
99
 
44
100
  ## Commands
45
101
 
46
102
  | Command | Description |
47
103
  |---------|-------------|
48
- | `/obs` | Print full observability dashboard + last 10 sessions history |
104
+ | `/obs` | Open full observability dashboard in TUI overlay |
49
105
  | `/obs-toggle` | Toggle the observability footer on/off |
106
+ | `/obs-toggle-path` | Toggle between folder name and full path in footer |
50
107
 
51
- ## Dashboard Output
108
+ ## Migration from TPS
52
109
 
53
- ```
54
- ╔══════════════════════════════════════════════════════════════╗
55
- ║ 🕵️ Agent Observability Dashboard ║
56
- ╠══════════════════════════════════════════════════════════════╣
57
- ║ Runtime: 12:34 ║
58
- ║ Dir: ~/projects/my-app ║
59
- ║ Branch: main ║
60
- ║ Model: claude-sonnet-4 ║
61
- ╠══════════════════════════════════════════════════════════════╣
62
- ║ Tokens: ↑1.2k ↓3.4k ║
63
- ║ Cost: $0.004200 ║
64
- ╠══════════════════════════════════════════════════════════════╣
65
- ║ Turns: ║
66
- ║ #1 ↑450 ↓1200 0:45 26.7/s $0.0015 claude-sonne ║
67
- ║ #2 ↑320 ↓900 0:32 28.1/s $0.0012 claude-sonne ║
68
- ╚══════════════════════════════════════════════════════════════╝
69
- ```
110
+ The standalone TPS extension is no longer required. pi-observability now includes its end-of-run TPS notification, so remove `~/.pi/agent/extensions/tps.ts` if it is installed to avoid duplicate notifications.
70
111
 
71
112
  ## Requirements
72
113
 
Binary file
package/diff.png ADDED
Binary file
@@ -0,0 +1,67 @@
1
+ import { homedir } from "node:os";
2
+ import type { ThemeColor } from "@mariozechner/pi-coding-agent";
3
+
4
+ export function fmtDuration(ms: number): string {
5
+ if (!Number.isFinite(ms) || ms < 0) ms = 0;
6
+ const s = Math.floor(ms / 1000);
7
+ const h = Math.floor(s / 3600);
8
+ const m = Math.floor((s % 3600) / 60);
9
+ const sec = s % 60;
10
+ if (h > 0) return `${h}:${m.toString().padStart(2, "0")}:${sec.toString().padStart(2, "0")}`;
11
+ return `${m}:${sec.toString().padStart(2, "0")}`;
12
+ }
13
+
14
+ export function fmtTokens(n: number): string {
15
+ if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(2)}M`;
16
+ if (n >= 1_000) return `${(n / 1_000).toFixed(1)}k`;
17
+ return `${n}`;
18
+ }
19
+
20
+ export function shortenPath(p: string): string {
21
+ const home = homedir();
22
+ if (home && p.startsWith(home)) return p.replace(home, "~");
23
+ return p;
24
+ }
25
+
26
+ export function thinkingColor(level: string): ThemeColor {
27
+ switch (level) {
28
+ case "off":
29
+ return "thinkingOff";
30
+ case "minimal":
31
+ return "thinkingMinimal";
32
+ case "low":
33
+ return "thinkingLow";
34
+ case "medium":
35
+ return "thinkingMedium";
36
+ case "high":
37
+ return "thinkingHigh";
38
+ case "xhigh":
39
+ return "thinkingXhigh";
40
+ default:
41
+ return "thinkingOff";
42
+ }
43
+ }
44
+
45
+ export function contextUsageColor(pct: number, expert: number, warning: number): ThemeColor {
46
+ if (pct <= expert) return "success";
47
+ if (pct <= warning) return "warning";
48
+ return "error";
49
+ }
50
+
51
+ export function rainbowText(text: string): string {
52
+ const colors = [
53
+ "\x1b[38;2;255;0;0m", // red
54
+ "\x1b[38;2;255;127;0m", // orange
55
+ "\x1b[38;2;255;255;0m", // yellow
56
+ "\x1b[38;2;0;255;0m", // green
57
+ "\x1b[38;2;0;255;255m", // cyan
58
+ "\x1b[38;2;0;0;255m", // blue
59
+ "\x1b[38;2;255;0;255m", // magenta
60
+ ];
61
+ let result = "";
62
+ for (let i = 0; i < text.length; i++) {
63
+ result += colors[i % colors.length] + text[i];
64
+ }
65
+ result += "\x1b[0m";
66
+ return result;
67
+ }