pi-context-map 0.7.0 → 0.7.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/CHANGELOG.md +6 -0
- package/README.md +19 -14
- package/extensions/analyzer.ts +8 -29
- package/extensions/generator.ts +13 -12
- package/extensions/index.ts +20 -15
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.7.1] - 2026-06-15
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
- **Fixed insights dropdown**: Changed from direct `addEventListener` to event delegation (`document.addEventListener('click', ...)` with `e.target.closest('.insight-header')`). The dropdown now works after SSE body replacement.
|
|
6
|
+
- **Fixed auto-open browser**: Added `cmd /c start` with `explorer` fallback for Windows. Browser now opens reliably on first `/context-map` invocation.
|
|
7
|
+
- **Removed unused variable**: Cleaned up `reportPath` destructuring warning in command handler.
|
|
8
|
+
|
|
3
9
|
## [0.7.0] - 2026-06-15
|
|
4
10
|
### Bug Fixes
|
|
5
11
|
- **Fixed token accuracy**: Now uses Pi's actual token count from `ctx.getContextUsage()` instead of heuristic estimation. The usage percentage now matches Pi's terminal display.
|
package/README.md
CHANGED
|
@@ -8,11 +8,15 @@ A visual context window mapping extension for [Pi](https://pi.dev/) that transfo
|
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
11
|
-
- **Visual Context Budget**: Real-time breakdown of tokens used by System, History, Files, and
|
|
12
|
-
- **
|
|
11
|
+
- **Visual Context Budget**: Real-time breakdown of tokens used by System, Tools, History, Files, and Summaries.
|
|
12
|
+
- **Accurate Token Count**: Uses Pi's actual token count from `ctx.getContextUsage()`, not heuristic estimation — matches the terminal display.
|
|
13
|
+
- **Working Set Analysis**: Categorizes files as `Active`, `Stale`, or `Legacy` based on position in the conversation.
|
|
13
14
|
- **Token Weighting**: Identifies "token hogs" by calculating the approximate size of each file in the window.
|
|
14
|
-
- **Operation Tracking**: Marks files with their last operation (Read
|
|
15
|
-
- **
|
|
15
|
+
- **Operation Tracking**: Marks files with their last operation (Read, Write, Edit).
|
|
16
|
+
- **Compaction Detection**: Tracks compaction summaries and branch summaries as a separate slice.
|
|
17
|
+
- **Auto-Open Browser**: Report automatically opens in your default browser on first invocation.
|
|
18
|
+
- **Dark Mode**: Toggle between light and dark themes. Preference persists across sessions via localStorage.
|
|
19
|
+
- **Live Server**: SSE-powered localhost server with auto-refresh after each assistant message.
|
|
16
20
|
|
|
17
21
|
## Installation
|
|
18
22
|
|
|
@@ -33,20 +37,21 @@ The extension will analyze the session and create an interactive HTML report at:
|
|
|
33
37
|
|
|
34
38
|
## Context Statuses
|
|
35
39
|
|
|
36
|
-
|
|
40
|
+
Files are categorized by their position in the conversation (more reliable than turn-based calculation):
|
|
37
41
|
|
|
38
|
-
| Status |
|
|
39
|
-
|
|
40
|
-
| **Active** |
|
|
41
|
-
| **Stale** |
|
|
42
|
-
| **Legacy** |
|
|
42
|
+
| Status | Position in Messages | Action |
|
|
43
|
+
|--------|---------------------|--------|
|
|
44
|
+
| **Active** | Last 30% of messages | Keep in context |
|
|
45
|
+
| **Stale** | Middle 40% of messages | Monitor for removal |
|
|
46
|
+
| **Legacy** | First 30% of messages | Prime candidate for compaction |
|
|
43
47
|
|
|
44
48
|
## How It Works
|
|
45
49
|
|
|
46
|
-
1. **Scanning**: The analyzer iterates through
|
|
47
|
-
2. **Weighting**: It
|
|
48
|
-
3. **
|
|
49
|
-
4. **
|
|
50
|
+
1. **Scanning**: The analyzer iterates through session messages, detecting `toolCall` blocks, `toolResult` messages, `compactionSummary` entries, and image attachments.
|
|
51
|
+
2. **Weighting**: It calculates token counts for each message type using a code-aware heuristic (multipliers for code blocks, strings, etc.).
|
|
52
|
+
3. **Accuracy**: When available, Pi's actual token count from `ctx.getContextUsage()` overrides the heuristic for the usage percentage.
|
|
53
|
+
4. **Categorization**: Files are classified by their position in the message array (last 30% = active, middle 40% = stale, first 30% = legacy).
|
|
54
|
+
5. **Visualization**: Generates a self-contained HTML dashboard with stacked composition bar, file cards with search/filter, dark mode toggle, and interactive insights.
|
|
50
55
|
|
|
51
56
|
## Live Localhost Server
|
|
52
57
|
|
package/extensions/analyzer.ts
CHANGED
|
@@ -148,10 +148,7 @@ export class ContextAnalyzer {
|
|
|
148
148
|
turn,
|
|
149
149
|
timestamp: msg.timestamp || Date.now(),
|
|
150
150
|
},
|
|
151
|
-
status: this.calculateStatus(
|
|
152
|
-
index,
|
|
153
|
-
totalMessages,
|
|
154
|
-
),
|
|
151
|
+
status: this.calculateStatus(index, totalMessages),
|
|
155
152
|
});
|
|
156
153
|
}
|
|
157
154
|
}
|
|
@@ -178,10 +175,7 @@ export class ContextAnalyzer {
|
|
|
178
175
|
turn,
|
|
179
176
|
timestamp: msg.timestamp || Date.now(),
|
|
180
177
|
},
|
|
181
|
-
status: this.calculateStatus(
|
|
182
|
-
index,
|
|
183
|
-
totalMessages,
|
|
184
|
-
),
|
|
178
|
+
status: this.calculateStatus(index, totalMessages),
|
|
185
179
|
});
|
|
186
180
|
}
|
|
187
181
|
}
|
|
@@ -200,10 +194,7 @@ export class ContextAnalyzer {
|
|
|
200
194
|
turn,
|
|
201
195
|
timestamp: msg.timestamp || Date.now(),
|
|
202
196
|
},
|
|
203
|
-
status: this.calculateStatus(
|
|
204
|
-
index,
|
|
205
|
-
totalMessages,
|
|
206
|
-
),
|
|
197
|
+
status: this.calculateStatus(index, totalMessages),
|
|
207
198
|
});
|
|
208
199
|
}
|
|
209
200
|
}
|
|
@@ -224,15 +215,9 @@ export class ContextAnalyzer {
|
|
|
224
215
|
const p = this.extractPath(block.name, block.arguments);
|
|
225
216
|
if (p) {
|
|
226
217
|
const opType = this.getOpType(block.name);
|
|
227
|
-
const result = this.findToolResult(
|
|
228
|
-
messages,
|
|
229
|
-
index,
|
|
230
|
-
block.id,
|
|
231
|
-
);
|
|
218
|
+
const result = this.findToolResult(messages, index, block.id);
|
|
232
219
|
const content = result?.content || "";
|
|
233
|
-
const w = TokenCounter.count(
|
|
234
|
-
String(JSON.stringify(content)),
|
|
235
|
-
);
|
|
220
|
+
const w = TokenCounter.count(String(JSON.stringify(content)));
|
|
236
221
|
fileTokens += w;
|
|
237
222
|
fileRegistry.set(p, {
|
|
238
223
|
path: p,
|
|
@@ -242,10 +227,7 @@ export class ContextAnalyzer {
|
|
|
242
227
|
turn,
|
|
243
228
|
timestamp: msg.timestamp || Date.now(),
|
|
244
229
|
},
|
|
245
|
-
status: this.calculateStatus(
|
|
246
|
-
index,
|
|
247
|
-
totalMessages,
|
|
248
|
-
),
|
|
230
|
+
status: this.calculateStatus(index, totalMessages),
|
|
249
231
|
});
|
|
250
232
|
}
|
|
251
233
|
}
|
|
@@ -263,8 +245,7 @@ export class ContextAnalyzer {
|
|
|
263
245
|
|
|
264
246
|
const mk = (tokens: number): ContextSlice => ({
|
|
265
247
|
tokens: Math.ceil(tokens),
|
|
266
|
-
percent:
|
|
267
|
-
totalTokens > 0 ? Math.round((tokens / totalTokens) * 100) : 0,
|
|
248
|
+
percent: totalTokens > 0 ? Math.round((tokens / totalTokens) * 100) : 0,
|
|
268
249
|
});
|
|
269
250
|
|
|
270
251
|
const files_detail = Array.from(fileRegistry.values())
|
|
@@ -306,9 +287,7 @@ export class ContextAnalyzer {
|
|
|
306
287
|
if (Array.isArray(content)) {
|
|
307
288
|
for (const block of content) {
|
|
308
289
|
if (block.type === "text" && typeof block.text === "string") {
|
|
309
|
-
const match = block.text.match(
|
|
310
|
-
/(?:\/|[A-Z]:\\)[\w./\\-]+\.\w+/,
|
|
311
|
-
);
|
|
290
|
+
const match = block.text.match(/(?:\/|[A-Z]:\\)[\w./\\-]+\.\w+/);
|
|
312
291
|
if (match) return match[0];
|
|
313
292
|
}
|
|
314
293
|
}
|
package/extensions/generator.ts
CHANGED
|
@@ -18,9 +18,10 @@ export class ReportGenerator {
|
|
|
18
18
|
actualTokens?: number | null,
|
|
19
19
|
): string {
|
|
20
20
|
// Use Pi's actual token count when available
|
|
21
|
-
const total =
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
const total =
|
|
22
|
+
actualTokens != null && actualTokens > 0
|
|
23
|
+
? actualTokens
|
|
24
|
+
: composition.total.tokens;
|
|
24
25
|
const usagePercent =
|
|
25
26
|
total > 0 ? Math.round((total / contextWindow) * 100) : 0;
|
|
26
27
|
|
|
@@ -647,15 +648,15 @@ h2:first-of-type { margin-top: 48px; }
|
|
|
647
648
|
applyTheme(cur === 'dark' ? 'light' : 'dark');
|
|
648
649
|
});
|
|
649
650
|
|
|
650
|
-
// Insight toggles
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
}
|
|
651
|
+
// Insight toggles (event delegation — survives SSE body replacement)
|
|
652
|
+
document.addEventListener('click', function(e) {
|
|
653
|
+
var btn = e.target.closest('.insight-header');
|
|
654
|
+
if (!btn) return;
|
|
655
|
+
var card = btn.closest('.insight-card');
|
|
656
|
+
if (!card) return;
|
|
657
|
+
var was = card.classList.toggle('collapsed');
|
|
658
|
+
btn.setAttribute('aria-expanded', was ? 'false' : 'true');
|
|
659
|
+
});
|
|
659
660
|
|
|
660
661
|
// Live SSE
|
|
661
662
|
try {
|
package/extensions/index.ts
CHANGED
|
@@ -27,21 +27,29 @@ function makeReportPath(sessionName?: string): string {
|
|
|
27
27
|
const now = new Date();
|
|
28
28
|
const date = now.toISOString().split("T")[0];
|
|
29
29
|
const time = now.toTimeString().split(" ")[0].replace(/:/g, "-");
|
|
30
|
-
const safe = (sessionName || "session")
|
|
31
|
-
.replace(/[^\w.-]/g, "_")
|
|
32
|
-
.slice(0, 40);
|
|
30
|
+
const safe = (sessionName || "session").replace(/[^\w.-]/g, "_").slice(0, 40);
|
|
33
31
|
const filename = `${date}_${time}_${safe}.html`;
|
|
34
32
|
return path.join(dir, filename);
|
|
35
33
|
}
|
|
36
34
|
|
|
37
35
|
function openBrowser(url: string): void {
|
|
38
36
|
const platform = process.platform;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
37
|
+
try {
|
|
38
|
+
if (platform === "win32") {
|
|
39
|
+
// Use cmd /c start with separate title arg to handle URLs with special chars
|
|
40
|
+
exec(`cmd /c start "" "${url}"`, (err) => {
|
|
41
|
+
if (err) {
|
|
42
|
+
// Fallback: try explorer directly
|
|
43
|
+
exec(`explorer "${url}"`);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
} else if (platform === "darwin") {
|
|
47
|
+
exec(`open "${url}"`);
|
|
48
|
+
} else {
|
|
49
|
+
exec(`xdg-open "${url}"`);
|
|
50
|
+
}
|
|
51
|
+
} catch {
|
|
52
|
+
// Silent — browser open is best-effort
|
|
45
53
|
}
|
|
46
54
|
}
|
|
47
55
|
|
|
@@ -194,7 +202,7 @@ export default async function piContextMap(pi: ExtensionAPI): Promise<void> {
|
|
|
194
202
|
|
|
195
203
|
ctx.ui.notify("Analyzing session context...", "info");
|
|
196
204
|
try {
|
|
197
|
-
const { composition, insights
|
|
205
|
+
const { composition, insights } = await runAnalysis();
|
|
198
206
|
const criticalCount = insights.filter(
|
|
199
207
|
(i) => i.severity === "critical",
|
|
200
208
|
).length;
|
|
@@ -253,9 +261,7 @@ export default async function piContextMap(pi: ExtensionAPI): Promise<void> {
|
|
|
253
261
|
actualPercent != null
|
|
254
262
|
? actualPercent
|
|
255
263
|
: composition.total.tokens > 0
|
|
256
|
-
? Math.round(
|
|
257
|
-
(composition.total.tokens / contextWindow) * 100,
|
|
258
|
-
)
|
|
264
|
+
? Math.round((composition.total.tokens / contextWindow) * 100)
|
|
259
265
|
: 0;
|
|
260
266
|
const summary =
|
|
261
267
|
`Context: ${composition.total.tokens.toLocaleString()} tokens (${usagePercent.toFixed(1)}% of ${(contextWindow / 1000).toFixed(0)}k). ` +
|
|
@@ -270,8 +276,7 @@ export default async function piContextMap(pi: ExtensionAPI): Promise<void> {
|
|
|
270
276
|
summary,
|
|
271
277
|
"",
|
|
272
278
|
...insights.map(
|
|
273
|
-
(i) =>
|
|
274
|
-
`[${i.severity.toUpperCase()}] ${i.title}: ${i.message}`,
|
|
279
|
+
(i) => `[${i.severity.toUpperCase()}] ${i.title}: ${i.message}`,
|
|
275
280
|
),
|
|
276
281
|
`Report: ${reportPath}`,
|
|
277
282
|
serverUrl ? `Live: ${serverUrl}` : "",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-context-map",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "Professional context profiler for Pi that visualizes the session context window, token distribution, and integrates with Nexus packages for actionable insights.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|