claude-inspect 0.5.0 → 0.5.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.de.md +1 -0
- package/README.md +1 -1
- package/dist/app/server/history/aggregator.js +46 -21
- package/dist/app/server/inspector.js +7 -3
- package/dist/web/assets/index-CpYuiE4Y.js +16 -0
- package/dist/web/assets/index-Jv_qFlIw.css +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/index-Cdr60wEj.css +0 -1
- package/dist/web/assets/index-kf9FqAN8.js +0 -16
package/README.de.md
CHANGED
|
@@ -65,6 +65,7 @@ Aus den Quellen: `git clone …`, `npm install`, `npm run build`, `npm start`
|
|
|
65
65
|
MCP-Server …) als gestapelter Verlauf je Programmname, dazu die Prozesse mit der meisten CPU-Zeit
|
|
66
66
|
- *Cost*: geschätzte Kosten (API-Listenpreis) der Session samt aller Subagenten und per
|
|
67
67
|
`claude` gestarteten Sitzungen, je Agent und je Modell mit Tokens (Input, Output, Cache)
|
|
68
|
+
und als Zeitleiste mit den teuersten Momenten
|
|
68
69
|
- Beim Hochscrollen blendet sich eine kompakte Kopfzeile mit Sprung nach oben/unten ein
|
|
69
70
|
- **CPU**: alle 3 s aus der Prozesstabelle gemessen (100 % = ein Kern), Verlaufslinie auf der
|
|
70
71
|
Dashboard-Karte. Pro Session eine CSV-Datei im App-Datenverzeichnis (macOS
|
package/README.md
CHANGED
|
@@ -50,7 +50,7 @@ becomes hard to tell *who is doing what*. claude-inspect answers:
|
|
|
50
50
|
| **Agent tree & call graph** | Main agent → subagents (nested) → `claude` sessions started via Bash, including fork/resume relations. Finished agents are greyed out. |
|
|
51
51
|
| **Timeline** | One lane per agent, tool calls as bars, spawn links between lanes, idle gaps > 10 min collapsed, zoom. Click a bar to jump to the call. |
|
|
52
52
|
| **Spawn linking** | Links sessions started by `claude -p …` to the calling tool use via the process tree (exact), `--session-id`, prompt text, `--agent`, time window and working directory – with a confidence label. |
|
|
53
|
-
| **Cost per agent tree** | Cost tab per session: API list-price estimate for the main agent plus all its subagents and spawned `claude` sessions, per agent and per model with input, output and cache tokens. |
|
|
53
|
+
| **Cost per agent tree** | Cost tab per session: API list-price estimate for the main agent plus all its subagents and spawned `claude` sessions, per agent and per model with input, output and cache tokens, plus a cost timeline with the most expensive moments. |
|
|
54
54
|
| **Statistics** | API list-price estimate, tokens per model/day/project, cache share, activity heatmap, tool statistics with error rate and p95 duration. |
|
|
55
55
|
| **CPU usage** | CPU of each Claude Code process plus all its subprocesses (shells, builds, test runners, MCP servers …), measured every 3 s from the process table. Sparkline on the dashboard card, a *CPU* tab per session with a stacked chart by process name and the processes that used the most CPU time. Recorded as one CSV per session in the app data directory (`~/Library/Application Support/claude-inspect/cpu` on macOS, `~/.local/share/claude-inspect/cpu` on Linux, `%LOCALAPPDATA%\claude-inspect\cpu` on Windows), so it stays available after the session ended; files older than 30 days are deleted. Only measured while claude-inspect runs; processes that live less than 3 s are missed. `--no-cpu-log` turns recording off. |
|
|
56
56
|
| **Tool explorer & files** | Every tool call across all sessions, filterable; which files agents read, edited or wrote. |
|
|
@@ -42,27 +42,7 @@ export class HistoryIndex {
|
|
|
42
42
|
seen.add(p);
|
|
43
43
|
continue;
|
|
44
44
|
}
|
|
45
|
-
|
|
46
|
-
let names = [];
|
|
47
|
-
try {
|
|
48
|
-
names = await readdir(dir);
|
|
49
|
-
}
|
|
50
|
-
catch {
|
|
51
|
-
/* keine Subagenten */
|
|
52
|
-
}
|
|
53
|
-
for (const n of names) {
|
|
54
|
-
const m = /^agent-(.+)\.jsonl$/.exec(n);
|
|
55
|
-
if (!m)
|
|
56
|
-
continue;
|
|
57
|
-
const path = join(dir, n);
|
|
58
|
-
seen.add(path);
|
|
59
|
-
try {
|
|
60
|
-
await this.scan(path, (await stat(path)).size, f.sessionId, m[1], project);
|
|
61
|
-
}
|
|
62
|
-
catch {
|
|
63
|
-
/* gelöscht */
|
|
64
|
-
}
|
|
65
|
-
}
|
|
45
|
+
await this.scanSubagents(f, project, seen);
|
|
66
46
|
}
|
|
67
47
|
// Von Claude Code aufgeräumte Dateien vergessen
|
|
68
48
|
for (const p of [...this.files.keys()])
|
|
@@ -71,7 +51,52 @@ export class HistoryIndex {
|
|
|
71
51
|
this.totalFiles = this.files.size;
|
|
72
52
|
this.ready = true;
|
|
73
53
|
}
|
|
54
|
+
async scanSubagents(f, project, seen) {
|
|
55
|
+
const dir = subagentDir(f.path);
|
|
56
|
+
let names = [];
|
|
57
|
+
try {
|
|
58
|
+
names = await readdir(dir);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
/* keine Subagenten */
|
|
62
|
+
}
|
|
63
|
+
for (const n of names) {
|
|
64
|
+
const m = /^agent-(.+)\.jsonl$/.exec(n);
|
|
65
|
+
if (!m)
|
|
66
|
+
continue;
|
|
67
|
+
const path = join(dir, n);
|
|
68
|
+
seen?.add(path);
|
|
69
|
+
try {
|
|
70
|
+
await this.scan(path, (await stat(path)).size, f.sessionId, m[1], project);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
/* gelöscht */
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Eine Session samt Subagenten sofort auf Stand bringen, ohne auf den ersten
|
|
79
|
+
* vollständigen Scan zu warten (für die Kostenansicht direkt nach dem Start).
|
|
80
|
+
*/
|
|
81
|
+
async ensureSession(f, project) {
|
|
82
|
+
try {
|
|
83
|
+
await this.scan(f.path, (await stat(f.path)).size, f.sessionId, undefined, project);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return; // gelöscht
|
|
87
|
+
}
|
|
88
|
+
await this.scanSubagents(f, project);
|
|
89
|
+
}
|
|
90
|
+
/** Läuft parallel zum Hintergrund-Scan: pro Datei nur ein Lesevorgang gleichzeitig. */
|
|
91
|
+
scanning = new Map();
|
|
74
92
|
async scan(path, size, sessionId, agentId, project) {
|
|
93
|
+
for (let running = this.scanning.get(path); running; running = this.scanning.get(path))
|
|
94
|
+
await running;
|
|
95
|
+
const p = this.scanUnlocked(path, size, sessionId, agentId, project).finally(() => this.scanning.delete(path));
|
|
96
|
+
this.scanning.set(path, p);
|
|
97
|
+
return p;
|
|
98
|
+
}
|
|
99
|
+
async scanUnlocked(path, size, sessionId, agentId, project) {
|
|
75
100
|
let agg = this.files.get(path);
|
|
76
101
|
if (agg && agg.size === size)
|
|
77
102
|
return;
|
|
@@ -509,7 +509,12 @@ export class Inspector extends EventEmitter {
|
|
|
509
509
|
const flow = await this.flow(rootSessionId);
|
|
510
510
|
if (!flow)
|
|
511
511
|
return undefined;
|
|
512
|
-
|
|
512
|
+
// nur die Dateien dieses Baums einlesen, statt auf den ersten vollständigen History-Scan zu warten
|
|
513
|
+
for (const sid of new Set(flow.lanes.map((l) => l.sessionId))) {
|
|
514
|
+
const f = this.index.get(sid);
|
|
515
|
+
if (f)
|
|
516
|
+
await this.history.ensureSession(f, await this.projectOf(f));
|
|
517
|
+
}
|
|
513
518
|
const seen = new Set();
|
|
514
519
|
const total = emptyTotals();
|
|
515
520
|
const byModel = new Map();
|
|
@@ -556,7 +561,6 @@ export class Inspector extends EventEmitter {
|
|
|
556
561
|
const rootFile = this.index.get(rootSessionId);
|
|
557
562
|
return {
|
|
558
563
|
rootSessionId,
|
|
559
|
-
ready: this.historyReady,
|
|
560
564
|
rows,
|
|
561
565
|
total,
|
|
562
566
|
costUSD,
|
|
@@ -609,7 +613,7 @@ function costTimeline(points) {
|
|
|
609
613
|
b.byModel[p.model] = (b.byModel[p.model] ?? 0) + p.cost;
|
|
610
614
|
let l = b.lanes.get(p.lane.id);
|
|
611
615
|
if (!l)
|
|
612
|
-
b.lanes.set(p.lane.id, (l = { label: p.lane.label, costUSD: 0 }));
|
|
616
|
+
b.lanes.set(p.lane.id, (l = { label: p.lane.label, sublabel: p.lane.sublabel, costUSD: 0 }));
|
|
613
617
|
l.costUSD += p.cost;
|
|
614
618
|
}
|
|
615
619
|
return {
|