lumira 1.4.0 → 1.5.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/README.md +23 -0
- package/dist/commands/stats.d.ts +71 -0
- package/dist/commands/stats.js +371 -0
- package/dist/commands/stats.js.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/dist/normalize.d.ts +3 -0
- package/dist/normalize.js +37 -0
- package/dist/normalize.js.map +1 -1
- package/dist/parsers/transcript-stats.d.ts +41 -0
- package/dist/parsers/transcript-stats.js +141 -0
- package/dist/parsers/transcript-stats.js.map +1 -0
- package/dist/render/colors.d.ts +1 -1
- package/dist/render/colors.js +1 -1
- package/dist/render/line2.js +3 -3
- package/dist/render/line2.js.map +1 -1
- package/dist/render/minimal.js +3 -1
- package/dist/render/minimal.js.map +1 -1
- package/dist/render/powerline-line2.js +4 -3
- package/dist/render/powerline-line2.js.map +1 -1
- package/dist/render/shared.d.ts +7 -0
- package/dist/render/shared.js +8 -2
- package/dist/render/shared.js.map +1 -1
- package/dist/tui/preview.js +22 -2
- package/dist/tui/preview.js.map +1 -1
- package/dist/types.d.ts +32 -5
- package/dist/types.js +25 -2
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,6 +34,7 @@ Interactive wizard — preset, theme, icons — previewed live before write.
|
|
|
34
34
|
- [Install](#install)
|
|
35
35
|
- [Display modes](#display)
|
|
36
36
|
- [Themes](#themes)
|
|
37
|
+
- [Stats CLI](#stats-cli)
|
|
37
38
|
- [Powerline](#powerline)
|
|
38
39
|
- [Configuration](#configuration)
|
|
39
40
|
- [Architecture](#architecture)
|
|
@@ -190,6 +191,28 @@ lumira themes preview --all --powerline # the powerline grid (great for scr
|
|
|
190
191
|
|
|
191
192
|
Adding a theme is a single new file plus a one-line registration. Every PR runs the **WCAG AA contrast guard** — if any powerline cell drops below 4.5:1 against the foreground, CI rejects it. See [CONTRIBUTING.md → Adding a theme](CONTRIBUTING.md#adding-a-theme) for the walkthrough.
|
|
192
193
|
|
|
194
|
+
## Stats CLI
|
|
195
|
+
|
|
196
|
+
`lumira stats` reads a Claude Code or Qwen Code transcript `.jsonl` and prints a one-shot analytics summary — session duration, total cost, token totals, cache hit rate, tool call frequency, and burn rate (`$/h`). Useful for post-session review, scripting, and CI dashboards.
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
# Just works — auto-discovers the newest transcript for the current cwd.
|
|
200
|
+
lumira stats
|
|
201
|
+
# Session: 2h 15m — $4.23 — 156k tokens — 87% cache
|
|
202
|
+
# Tools: Bash×45 Read×32 Write×18 Edit×12 Agent×8
|
|
203
|
+
# Burn: $1.88/h
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**Auto-discovery:** with no flags, `lumira stats` derives the Claude Code project slug from `cwd` (`/home/me/proj` → `-home-me-proj`) and reads the newest `.jsonl` under `~/.claude/projects/<slug>/`. If the current directory has no matching project dir, it falls back to the globally most-recently-modified transcript under `~/.claude/projects/` and prints a notice to stderr ("reading most recent session from …") so JSON pipelines on stdout stay clean.
|
|
207
|
+
|
|
208
|
+
**Flags:**
|
|
209
|
+
|
|
210
|
+
- `--session-id <path-or-uuid>` — override auto-discovery. A path (anything containing `/` or ending in `.jsonl`) is used as-is. A bare uuid is resolved first under `~/.claude/projects/<cwd-slug>/<uuid>.jsonl`, then by scanning every project dir for that filename.
|
|
211
|
+
- `--no-color` — strip ANSI escapes (also honored when the `NO_COLOR` env var is set, per [no-color.org](https://no-color.org)).
|
|
212
|
+
- `--json` — emit the raw `SessionStats` object as pretty-printed JSON for `jq` / CI composability.
|
|
213
|
+
|
|
214
|
+
**Qwen Code sessions** are parsed the same way, but cost and burn-rate lines are suppressed when the transcript lacks usage blocks (`hasCostData: false` in the JSON output) — no misleading `$0.00`.
|
|
215
|
+
|
|
193
216
|
## Powerline
|
|
194
217
|
|
|
195
218
|
`style: "powerline"` (or `--powerline`) renders the statusline with colored segment backgrounds and glyph separators inspired by powerline-go / oh-my-posh. Available separator presets via `powerline.style` (or `--powerline-style=<name>`):
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { type SessionStats } from '../parsers/transcript-stats.js';
|
|
2
|
+
export interface StatsArgs {
|
|
3
|
+
sessionId?: string;
|
|
4
|
+
noColor: boolean;
|
|
5
|
+
json: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Result of invoking the stats subcommand. Mirrors `ThemesCommandResult` so
|
|
9
|
+
* the dispatcher in `index.ts` can treat both subcommands uniformly.
|
|
10
|
+
*/
|
|
11
|
+
export interface StatsCommandResult {
|
|
12
|
+
stdout: string;
|
|
13
|
+
stderr: string;
|
|
14
|
+
exitCode: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Optional overrides for `runStatsCommand`. `cwd` and `homeDir` are injected
|
|
18
|
+
* here (rather than read from `process`) so tests can build isolated fake
|
|
19
|
+
* `~/.claude/projects/` trees in tmpdir without touching the real one.
|
|
20
|
+
*/
|
|
21
|
+
export interface StatsCommandOpts {
|
|
22
|
+
cwd?: string;
|
|
23
|
+
homeDir?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Parse argv for `lumira stats [...flags]`. argv is the full process.argv;
|
|
27
|
+
* the 'stats' command starts at argv[2], flags from argv[3].
|
|
28
|
+
*
|
|
29
|
+
* Per https://no-color.org, the NO_COLOR env var (any non-empty value)
|
|
30
|
+
* disables color. An empty string is *not* a trigger — the variable being
|
|
31
|
+
* unset and the variable being empty must behave identically, otherwise
|
|
32
|
+
* shell environments that pre-export NO_COLOR='' would silently disable
|
|
33
|
+
* color everywhere.
|
|
34
|
+
*
|
|
35
|
+
* Unknown flags are ignored to stay forward-compatible: a future minor that
|
|
36
|
+
* adds a new flag shouldn't crash older binaries piped against new shells.
|
|
37
|
+
*/
|
|
38
|
+
export declare function parseStatsArgs(argv: string[]): StatsArgs;
|
|
39
|
+
/**
|
|
40
|
+
* Render a SessionStats either as human-readable text or as pretty JSON.
|
|
41
|
+
*
|
|
42
|
+
* In `json` mode we emit `JSON.stringify(stats, null, 2)` — the 2-space
|
|
43
|
+
* indent makes the output diff-friendly without being absurdly verbose, and
|
|
44
|
+
* matches the convention used elsewhere in the tree for CLI JSON output.
|
|
45
|
+
*
|
|
46
|
+
* In human mode we always render the `Session:` and `Tools:` lines so the
|
|
47
|
+
* user gets a consistent skeleton regardless of which renderer produced the
|
|
48
|
+
* transcript. Cost and burn-rate lines are gated on `hasCostData` so Qwen
|
|
49
|
+
* (and other no-usage backends) never surface a misleading "$0.00".
|
|
50
|
+
*/
|
|
51
|
+
export declare function formatStatsOutput(stats: SessionStats, opts: {
|
|
52
|
+
noColor: boolean;
|
|
53
|
+
json: boolean;
|
|
54
|
+
}): string;
|
|
55
|
+
/**
|
|
56
|
+
* Execute `lumira stats [...]`. Resolution order for the transcript:
|
|
57
|
+
* 1. `--session-id <path>` (contains `/` or ends in `.jsonl`) — used as-is.
|
|
58
|
+
* 2. `--session-id <uuid>` — looked up under cwd-slug, then globally.
|
|
59
|
+
* 3. No flag — auto-discover newest in cwd-slug dir, then globally.
|
|
60
|
+
*
|
|
61
|
+
* The parser's own allow-list check (LUMIRA_ALLOWED_ROOTS) remains the
|
|
62
|
+
* security boundary — anything we hand it must pass `isUnderAllowedRoot`.
|
|
63
|
+
* Discovered paths live under `~/.claude/projects/` (covered by the home
|
|
64
|
+
* root) or under tmpdir in tests (also covered).
|
|
65
|
+
*
|
|
66
|
+
* `cols` is accepted for parity with `runThemesCommand` but the human
|
|
67
|
+
* renderer here doesn't wrap or align to terminal width — keep it in the
|
|
68
|
+
* signature so a future widened renderer (sparklines, multi-column tool
|
|
69
|
+
* frequency) can opt in without breaking the dispatcher contract.
|
|
70
|
+
*/
|
|
71
|
+
export declare function runStatsCommand(argv: string[], _cols?: number, opts?: StatsCommandOpts): Promise<StatsCommandResult>;
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `lumira stats` subcommand (issue #114) — print an aggregate summary of a
|
|
3
|
+
* Claude Code transcript session. Consumes `aggregateStats` from the parser
|
|
4
|
+
* layer, formats either human-readable or JSON output, and follows the same
|
|
5
|
+
* `{ stdout, stderr, exitCode }` contract as `runThemesCommand` so the
|
|
6
|
+
* top-level CLI dispatcher can wire it identically.
|
|
7
|
+
*
|
|
8
|
+
* Color handling: we deliberately do NOT auto-detect TTY here. Tests assert
|
|
9
|
+
* that `--no-color` strips ANSI escapes, and the default rendering already
|
|
10
|
+
* uses no escape codes (plain text only) — keeping the formatter color-free
|
|
11
|
+
* in the default path means a TTY-less test environment never sees stray
|
|
12
|
+
* sequences, and a future enhancement can layer color in via `createColors`
|
|
13
|
+
* without changing the test contract.
|
|
14
|
+
*
|
|
15
|
+
* Session auto-discovery (issue #114 follow-up): when `--session-id` is
|
|
16
|
+
* omitted, derive the Claude Code project slug from `cwd` (`/foo/bar` →
|
|
17
|
+
* `-foo-bar`) and read the newest `.jsonl` in `<homeDir>/.claude/projects/
|
|
18
|
+
* <slug>/`. If that dir is missing or empty, fall back to the globally
|
|
19
|
+
* newest transcript across all project subdirs and emit a stderr notice so
|
|
20
|
+
* users know a non-cwd session was picked. A bare uuid passed via
|
|
21
|
+
* `--session-id` is treated the same way: prefer cwd-slug, fall back to
|
|
22
|
+
* global search by filename.
|
|
23
|
+
*/
|
|
24
|
+
import { promises as fs } from 'node:fs';
|
|
25
|
+
import { homedir } from 'node:os';
|
|
26
|
+
import { join, relative, isAbsolute } from 'node:path';
|
|
27
|
+
import { aggregateStats } from '../parsers/transcript-stats.js';
|
|
28
|
+
import { formatTokens, formatDuration, formatCost, formatBurnRate } from '../utils/format.js';
|
|
29
|
+
import { stripAnsi } from '../render/colors.js';
|
|
30
|
+
/**
|
|
31
|
+
* Parse argv for `lumira stats [...flags]`. argv is the full process.argv;
|
|
32
|
+
* the 'stats' command starts at argv[2], flags from argv[3].
|
|
33
|
+
*
|
|
34
|
+
* Per https://no-color.org, the NO_COLOR env var (any non-empty value)
|
|
35
|
+
* disables color. An empty string is *not* a trigger — the variable being
|
|
36
|
+
* unset and the variable being empty must behave identically, otherwise
|
|
37
|
+
* shell environments that pre-export NO_COLOR='' would silently disable
|
|
38
|
+
* color everywhere.
|
|
39
|
+
*
|
|
40
|
+
* Unknown flags are ignored to stay forward-compatible: a future minor that
|
|
41
|
+
* adds a new flag shouldn't crash older binaries piped against new shells.
|
|
42
|
+
*/
|
|
43
|
+
export function parseStatsArgs(argv) {
|
|
44
|
+
let sessionId;
|
|
45
|
+
let noColor = false;
|
|
46
|
+
let json = false;
|
|
47
|
+
for (let i = 3; i < argv.length; i++) {
|
|
48
|
+
const arg = argv[i];
|
|
49
|
+
if (arg === '--session-id' && i + 1 < argv.length) {
|
|
50
|
+
const next = argv[i + 1];
|
|
51
|
+
// Guard: if the "value" begins with `-`, the user almost certainly
|
|
52
|
+
// forgot to pass a value (e.g. `--session-id --json`). Consuming it
|
|
53
|
+
// would silently swallow the next flag — so skip without advancing
|
|
54
|
+
// and let the loop reprocess `--json` on its own.
|
|
55
|
+
if (next.startsWith('-'))
|
|
56
|
+
continue;
|
|
57
|
+
sessionId = next;
|
|
58
|
+
i += 1;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (arg === '--no-color') {
|
|
62
|
+
noColor = true;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (arg === '--json') {
|
|
66
|
+
json = true;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
// Unknown flag — ignored.
|
|
70
|
+
}
|
|
71
|
+
const envNoColor = process.env['NO_COLOR'];
|
|
72
|
+
if (typeof envNoColor === 'string' && envNoColor.length > 0)
|
|
73
|
+
noColor = true;
|
|
74
|
+
return { sessionId, noColor, json };
|
|
75
|
+
}
|
|
76
|
+
/** True when the session has no temporal extent and no activity at all. */
|
|
77
|
+
function isEmptySession(stats) {
|
|
78
|
+
const noTokens = stats.inputTokens === 0 && stats.outputTokens === 0
|
|
79
|
+
&& stats.cacheReadTokens === 0 && stats.cacheCreationTokens === 0;
|
|
80
|
+
const noTools = Object.keys(stats.toolFrequency).length === 0;
|
|
81
|
+
const noTime = stats.sessionStart === null || stats.durationMs === 0;
|
|
82
|
+
return noTokens && noTools && noTime;
|
|
83
|
+
}
|
|
84
|
+
/** Cache hit % = cache_read / (cache_read + input). Returns null when undefined. */
|
|
85
|
+
function cacheHitPercent(stats) {
|
|
86
|
+
const denom = stats.cacheReadTokens + stats.inputTokens;
|
|
87
|
+
if (denom <= 0)
|
|
88
|
+
return null;
|
|
89
|
+
return Math.round((stats.cacheReadTokens / denom) * 100);
|
|
90
|
+
}
|
|
91
|
+
function totalTokens(stats) {
|
|
92
|
+
return stats.inputTokens + stats.outputTokens
|
|
93
|
+
+ stats.cacheReadTokens + stats.cacheCreationTokens;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Render a SessionStats either as human-readable text or as pretty JSON.
|
|
97
|
+
*
|
|
98
|
+
* In `json` mode we emit `JSON.stringify(stats, null, 2)` — the 2-space
|
|
99
|
+
* indent makes the output diff-friendly without being absurdly verbose, and
|
|
100
|
+
* matches the convention used elsewhere in the tree for CLI JSON output.
|
|
101
|
+
*
|
|
102
|
+
* In human mode we always render the `Session:` and `Tools:` lines so the
|
|
103
|
+
* user gets a consistent skeleton regardless of which renderer produced the
|
|
104
|
+
* transcript. Cost and burn-rate lines are gated on `hasCostData` so Qwen
|
|
105
|
+
* (and other no-usage backends) never surface a misleading "$0.00".
|
|
106
|
+
*/
|
|
107
|
+
export function formatStatsOutput(stats, opts) {
|
|
108
|
+
if (opts.json)
|
|
109
|
+
return JSON.stringify(stats, null, 2);
|
|
110
|
+
if (isEmptySession(stats)) {
|
|
111
|
+
const text = 'Session: empty session — no activity recorded.';
|
|
112
|
+
return opts.noColor ? stripAnsi(text) : text;
|
|
113
|
+
}
|
|
114
|
+
const lines = [];
|
|
115
|
+
// Session: <duration> [— <cost>, <tokens> tokens, <cache>% cache]
|
|
116
|
+
const segments = [`Session: ${formatDuration(stats.durationMs) || '0s'}`];
|
|
117
|
+
if (stats.hasCostData) {
|
|
118
|
+
const cost = formatCost(stats.costUsd);
|
|
119
|
+
if (cost)
|
|
120
|
+
segments.push(cost);
|
|
121
|
+
const tt = totalTokens(stats);
|
|
122
|
+
if (tt > 0)
|
|
123
|
+
segments.push(`${formatTokens(tt)} tokens`);
|
|
124
|
+
const hit = cacheHitPercent(stats);
|
|
125
|
+
if (hit !== null)
|
|
126
|
+
segments.push(`${hit}% cache`);
|
|
127
|
+
}
|
|
128
|
+
lines.push(segments.join(' — '));
|
|
129
|
+
// Tools: ToolName×N ToolName×N ...
|
|
130
|
+
const toolEntries = Object.entries(stats.toolFrequency)
|
|
131
|
+
.sort(([, a], [, b]) => b - a)
|
|
132
|
+
.map(([name, count]) => `${name}×${count}`);
|
|
133
|
+
lines.push(`Tools: ${toolEntries.length > 0 ? toolEntries.join(' ') : '(none)'}`);
|
|
134
|
+
// Burn: $X.XX/h — only meaningful when we have cost AND a long-enough session.
|
|
135
|
+
if (stats.hasCostData) {
|
|
136
|
+
const burn = formatBurnRate(stats.costUsd, stats.durationMs);
|
|
137
|
+
if (burn)
|
|
138
|
+
lines.push(`Burn: ${burn}`);
|
|
139
|
+
}
|
|
140
|
+
const out = lines.join('\n');
|
|
141
|
+
return opts.noColor ? stripAnsi(out) : out;
|
|
142
|
+
}
|
|
143
|
+
function err(message, exitCode = 1) {
|
|
144
|
+
return { stdout: '', stderr: message.endsWith('\n') ? message : `${message}\n`, exitCode };
|
|
145
|
+
}
|
|
146
|
+
function ok(stdout, stderr = '') {
|
|
147
|
+
return { stdout, stderr, exitCode: 0 };
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Convert a cwd into the Claude Code project slug. Claude Code names project
|
|
151
|
+
* directories by replacing every `/` in the absolute cwd with `-`, including
|
|
152
|
+
* the leading slash — `/home/me/proj` becomes `-home-me-proj`.
|
|
153
|
+
*
|
|
154
|
+
* Trailing slashes are stripped first so `/foo/bar/` and `/foo/bar` produce
|
|
155
|
+
* the same slug `-foo-bar`. Without this normalization a stray trailing slash
|
|
156
|
+
* (e.g. from a shell prompt's PWD with a trailing /) would yield `-foo-bar-`
|
|
157
|
+
* and silently miss the real project dir.
|
|
158
|
+
*/
|
|
159
|
+
function cwdToSlug(cwd) {
|
|
160
|
+
return cwd.replace(/\/+$/, '').replace(/\//g, '-');
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Return the newest `.jsonl` file in `dir` by mtime, or `null` if the dir
|
|
164
|
+
* doesn't exist, can't be read, or contains no `.jsonl` files. Symlinks and
|
|
165
|
+
* non-regular files are tolerated — `fs.stat` follows symlinks, and a stat
|
|
166
|
+
* failure on a single entry just drops it from the candidate set.
|
|
167
|
+
*/
|
|
168
|
+
async function newestJsonl(dir) {
|
|
169
|
+
let entries;
|
|
170
|
+
try {
|
|
171
|
+
entries = await fs.readdir(dir);
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
let bestPath = null;
|
|
177
|
+
let bestMtime = -Infinity;
|
|
178
|
+
for (const name of entries) {
|
|
179
|
+
if (!name.endsWith('.jsonl'))
|
|
180
|
+
continue;
|
|
181
|
+
const full = join(dir, name);
|
|
182
|
+
try {
|
|
183
|
+
const st = await fs.stat(full);
|
|
184
|
+
if (!st.isFile())
|
|
185
|
+
continue;
|
|
186
|
+
if (st.mtimeMs > bestMtime) {
|
|
187
|
+
bestMtime = st.mtimeMs;
|
|
188
|
+
bestPath = full;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
// Unstable entry (deleted between readdir and stat, permission denied,
|
|
193
|
+
// broken symlink). Skip it — discovery should never crash on one bad file.
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return bestPath;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Walk every project subdir under `projectsRoot` and return the globally
|
|
200
|
+
* newest `.jsonl` by mtime. Used as the fallback when the cwd-slug dir
|
|
201
|
+
* doesn't exist (e.g. user runs `lumira stats` from a directory that isn't
|
|
202
|
+
* a known Claude Code project).
|
|
203
|
+
*/
|
|
204
|
+
async function globalNewestJsonl(projectsRoot) {
|
|
205
|
+
let dirs;
|
|
206
|
+
try {
|
|
207
|
+
dirs = await fs.readdir(projectsRoot);
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
let bestPath = null;
|
|
213
|
+
let bestMtime = -Infinity;
|
|
214
|
+
for (const subdir of dirs) {
|
|
215
|
+
const candidate = await newestJsonl(join(projectsRoot, subdir));
|
|
216
|
+
if (!candidate)
|
|
217
|
+
continue;
|
|
218
|
+
try {
|
|
219
|
+
const st = await fs.stat(candidate);
|
|
220
|
+
if (st.mtimeMs > bestMtime) {
|
|
221
|
+
bestMtime = st.mtimeMs;
|
|
222
|
+
bestPath = candidate;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
// Race: file vanished between `newestJsonl` and this stat. Skip.
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return bestPath;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Locate a transcript when the user didn't pass an explicit path. Prefers
|
|
233
|
+
* the cwd-slug project dir; falls back to the globally newest transcript.
|
|
234
|
+
*/
|
|
235
|
+
async function discoverTranscript(cwd, homeDir) {
|
|
236
|
+
const projectsRoot = join(homeDir, '.claude', 'projects');
|
|
237
|
+
const cwdSlugDir = join(projectsRoot, cwdToSlug(cwd));
|
|
238
|
+
const local = await newestJsonl(cwdSlugDir);
|
|
239
|
+
if (local)
|
|
240
|
+
return local;
|
|
241
|
+
return await globalNewestJsonl(projectsRoot);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Resolve a bare session uuid to a JSONL path. Prefer the cwd-slug dir so
|
|
245
|
+
* a uuid that exists in multiple projects (rare but possible if the user
|
|
246
|
+
* imports an old session) maps to the locally-scoped one. Falls back to the
|
|
247
|
+
* first match found by scanning every project subdir.
|
|
248
|
+
*/
|
|
249
|
+
async function resolveSessionId(uuid, cwd, homeDir) {
|
|
250
|
+
const projectsRoot = join(homeDir, '.claude', 'projects');
|
|
251
|
+
// Accept either bare `<uuid>` or `<uuid>.jsonl` — strip the extension if
|
|
252
|
+
// present so we don't produce `<uuid>.jsonl.jsonl`.
|
|
253
|
+
const bare = uuid.endsWith('.jsonl') ? uuid.slice(0, -'.jsonl'.length) : uuid;
|
|
254
|
+
const filename = `${bare}.jsonl`;
|
|
255
|
+
// 1. Try cwd-slug dir.
|
|
256
|
+
const localPath = join(projectsRoot, cwdToSlug(cwd), filename);
|
|
257
|
+
try {
|
|
258
|
+
const st = await fs.stat(localPath);
|
|
259
|
+
if (st.isFile())
|
|
260
|
+
return localPath;
|
|
261
|
+
}
|
|
262
|
+
catch { /* miss — fall through to global scan */ }
|
|
263
|
+
// 2. Walk all project subdirs.
|
|
264
|
+
let dirs;
|
|
265
|
+
try {
|
|
266
|
+
dirs = await fs.readdir(projectsRoot);
|
|
267
|
+
}
|
|
268
|
+
catch {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
for (const sub of dirs) {
|
|
272
|
+
const cand = join(projectsRoot, sub, filename);
|
|
273
|
+
try {
|
|
274
|
+
const st = await fs.stat(cand);
|
|
275
|
+
if (st.isFile())
|
|
276
|
+
return cand;
|
|
277
|
+
}
|
|
278
|
+
catch { /* not in this project — keep walking */ }
|
|
279
|
+
}
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Cheap detector for "looks like a path, not a uuid". The presence of a `/`
|
|
284
|
+
* is the only reliable signal — a bare filename (even one ending in `.jsonl`,
|
|
285
|
+
* such as `<uuid>.jsonl`) should still go through uuid resolution so the
|
|
286
|
+
* cwd-slug lookup applies. Treating `.jsonl` as path-ish would route
|
|
287
|
+
* `--session-id <uuid>.jsonl` to the "use as-is" branch, producing a
|
|
288
|
+
* `Transcript file not found` error instead of resolving via the project dir.
|
|
289
|
+
*/
|
|
290
|
+
function looksLikePath(value) {
|
|
291
|
+
return value.includes('/');
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Execute `lumira stats [...]`. Resolution order for the transcript:
|
|
295
|
+
* 1. `--session-id <path>` (contains `/` or ends in `.jsonl`) — used as-is.
|
|
296
|
+
* 2. `--session-id <uuid>` — looked up under cwd-slug, then globally.
|
|
297
|
+
* 3. No flag — auto-discover newest in cwd-slug dir, then globally.
|
|
298
|
+
*
|
|
299
|
+
* The parser's own allow-list check (LUMIRA_ALLOWED_ROOTS) remains the
|
|
300
|
+
* security boundary — anything we hand it must pass `isUnderAllowedRoot`.
|
|
301
|
+
* Discovered paths live under `~/.claude/projects/` (covered by the home
|
|
302
|
+
* root) or under tmpdir in tests (also covered).
|
|
303
|
+
*
|
|
304
|
+
* `cols` is accepted for parity with `runThemesCommand` but the human
|
|
305
|
+
* renderer here doesn't wrap or align to terminal width — keep it in the
|
|
306
|
+
* signature so a future widened renderer (sparklines, multi-column tool
|
|
307
|
+
* frequency) can opt in without breaking the dispatcher contract.
|
|
308
|
+
*/
|
|
309
|
+
export async function runStatsCommand(argv,
|
|
310
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
311
|
+
_cols, opts = {}) {
|
|
312
|
+
const args = parseStatsArgs(argv);
|
|
313
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
314
|
+
const homeDir = opts.homeDir ?? homedir();
|
|
315
|
+
const projectsRoot = join(homeDir, '.claude', 'projects');
|
|
316
|
+
let transcriptPath;
|
|
317
|
+
let usedDiscovery = false;
|
|
318
|
+
if (args.sessionId) {
|
|
319
|
+
if (looksLikePath(args.sessionId)) {
|
|
320
|
+
// Explicit path — hand straight to the parser.
|
|
321
|
+
transcriptPath = args.sessionId;
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
// Bare uuid — resolve via cwd-slug then global scan.
|
|
325
|
+
const resolved = await resolveSessionId(args.sessionId, cwd, homeDir);
|
|
326
|
+
if (!resolved) {
|
|
327
|
+
return err(`lumira stats: session id "${args.sessionId}" not found under ${projectsRoot}\n`);
|
|
328
|
+
}
|
|
329
|
+
transcriptPath = resolved;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
const discovered = await discoverTranscript(cwd, homeDir);
|
|
334
|
+
if (!discovered) {
|
|
335
|
+
return err(`lumira stats: no transcripts found under ${projectsRoot}\n\n`
|
|
336
|
+
+ 'Pass --session-id <path-or-uuid> explicitly, or run `lumira stats` from a directory '
|
|
337
|
+
+ 'where Claude Code has recorded a session.\n');
|
|
338
|
+
}
|
|
339
|
+
transcriptPath = discovered;
|
|
340
|
+
usedDiscovery = true;
|
|
341
|
+
}
|
|
342
|
+
let stats;
|
|
343
|
+
try {
|
|
344
|
+
stats = await aggregateStats(transcriptPath);
|
|
345
|
+
}
|
|
346
|
+
catch (e) {
|
|
347
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
348
|
+
return err(`lumira stats: ${message}`);
|
|
349
|
+
}
|
|
350
|
+
// Fallback notice — emitted to stderr only when discovery picked a
|
|
351
|
+
// transcript outside the cwd-slug project dir. Keeping it on stderr means
|
|
352
|
+
// `lumira stats --json | jq` stays parseable even when the fallback fired.
|
|
353
|
+
//
|
|
354
|
+
// Use `path.relative` instead of `startsWith` because slug names can be
|
|
355
|
+
// prefix-collisions of each other: `cwd=/foo` (slug `-foo`) vs a transcript
|
|
356
|
+
// under `<projects>/-foo-bar/x.jsonl` — `startsWith('-foo')` matches the
|
|
357
|
+
// sibling project and would silently SUPPRESS the cross-project notice.
|
|
358
|
+
// `relative(cwdSlugDir, transcriptPath)` returns `..` or an absolute path
|
|
359
|
+
// when the target is outside the dir, which we reject correctly.
|
|
360
|
+
let stderr = '';
|
|
361
|
+
if (usedDiscovery) {
|
|
362
|
+
const cwdSlugDir = join(projectsRoot, cwdToSlug(cwd));
|
|
363
|
+
const rel = relative(cwdSlugDir, transcriptPath);
|
|
364
|
+
const inCwdSlug = rel !== '' && !rel.startsWith('..') && !isAbsolute(rel);
|
|
365
|
+
if (!inCwdSlug) {
|
|
366
|
+
stderr = `lumira stats: no transcripts for cwd; reading most recent session from ${transcriptPath}\n`;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return ok(formatStatsOutput(stats, { noColor: args.noColor, json: args.json }), stderr);
|
|
370
|
+
}
|
|
371
|
+
//# sourceMappingURL=stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stats.js","sourceRoot":"","sources":["../../src/commands/stats.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,7 @@ import { render } from './render/index.js';
|
|
|
16
16
|
import { resolveIcons } from './render/icons.js';
|
|
17
17
|
import { install, uninstall } from './installer.js';
|
|
18
18
|
import { runThemesCommand } from './commands/themes.js';
|
|
19
|
+
import { runStatsCommand } from './commands/stats.js';
|
|
19
20
|
import { EMPTY_TRANSCRIPT } from './types.js';
|
|
20
21
|
import { normalize } from './normalize.js';
|
|
21
22
|
const defaultDeps = {
|
|
@@ -82,6 +83,19 @@ if (isDirectRun()) {
|
|
|
82
83
|
if (r.exitCode !== 0)
|
|
83
84
|
process.exit(r.exitCode);
|
|
84
85
|
}
|
|
86
|
+
else if (cmd === 'stats') {
|
|
87
|
+
runStatsCommand(process.argv, process.stdout.columns).then(r => {
|
|
88
|
+
if (r.stdout)
|
|
89
|
+
process.stdout.write(r.stdout);
|
|
90
|
+
if (r.stderr)
|
|
91
|
+
process.stderr.write(r.stderr);
|
|
92
|
+
if (r.exitCode !== 0)
|
|
93
|
+
process.exit(r.exitCode);
|
|
94
|
+
}).catch(e => {
|
|
95
|
+
process.stderr.write(`Stats error: ${e.message}\n`);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
85
99
|
else {
|
|
86
100
|
main().then(o => process.stdout.write(o)).catch(e => { if (!(e instanceof StdinParseError))
|
|
87
101
|
process.stderr.write(`Statusline error: ${e.message}\n`); });
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,IAAI,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC5E,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/normalize.d.ts
CHANGED
|
@@ -23,6 +23,9 @@ export interface NormalizedInput {
|
|
|
23
23
|
context: {
|
|
24
24
|
usedPercentage: number;
|
|
25
25
|
windowSize?: number;
|
|
26
|
+
realUsedPercentage?: number;
|
|
27
|
+
/** True when context fill is in the 5pp window before the platform's auto-compact threshold. */
|
|
28
|
+
nearAutoCompact: boolean;
|
|
26
29
|
};
|
|
27
30
|
/** Cost in USD (Claude only) */
|
|
28
31
|
cost?: number;
|
package/dist/normalize.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// Single internal format that all renderers can consume.
|
|
4
4
|
// Platform-specific quirks are handled once here.
|
|
5
5
|
// Renderers check field presence, not platform identity.
|
|
6
|
+
import { AUTO_COMPACT_THRESHOLD, AUTO_COMPACT_WARNING_GAP } from './types.js';
|
|
6
7
|
export function isQwenInput(input) {
|
|
7
8
|
const raw = input;
|
|
8
9
|
if (!raw.metrics || typeof raw.metrics !== 'object' || !('models' in raw.metrics))
|
|
@@ -27,6 +28,21 @@ export function sanitizeTermString(s) {
|
|
|
27
28
|
}
|
|
28
29
|
/** Allowed values for the reasoning effort level field (CC ≥ 2.1.x). */
|
|
29
30
|
const VALID_EFFORT_LEVELS = new Set(['low', 'medium', 'high', 'xhigh', 'max']);
|
|
31
|
+
/**
|
|
32
|
+
* Sum all four token categories from `context_window.current_usage` to compute
|
|
33
|
+
* a real context usage total (input + output + cache_read + cache_creation).
|
|
34
|
+
* Returns undefined when `cu` is absent or not an object shape.
|
|
35
|
+
*/
|
|
36
|
+
function getRealUsageTotal(cu) {
|
|
37
|
+
if (typeof cu !== 'object' || !cu)
|
|
38
|
+
return undefined;
|
|
39
|
+
const obj = cu;
|
|
40
|
+
const total = (obj.input_tokens ?? 0)
|
|
41
|
+
+ (obj.output_tokens ?? 0)
|
|
42
|
+
+ (obj.cache_read_input_tokens ?? 0)
|
|
43
|
+
+ (obj.cache_creation_input_tokens ?? 0);
|
|
44
|
+
return total;
|
|
45
|
+
}
|
|
30
46
|
/**
|
|
31
47
|
* Extract cache fields from `context_window.current_usage` (modern ≥ 2.1.x payloads).
|
|
32
48
|
* Returns `cached` (cache_read_input_tokens) and `denominator` (sum of all per-turn input
|
|
@@ -84,6 +100,25 @@ export function normalize(input) {
|
|
|
84
100
|
if (claude) {
|
|
85
101
|
({ denominator: cacheTurnDenominator } = getCacheFields(claude.context_window?.current_usage));
|
|
86
102
|
}
|
|
103
|
+
// Real context usage percentage (Claude only): includes output tokens in the numerator,
|
|
104
|
+
// unlike the hook-provided `used_percentage` which excludes them. This gives an accurate
|
|
105
|
+
// picture of actual context consumption, especially near auto-compact thresholds.
|
|
106
|
+
let realUsedPercentage;
|
|
107
|
+
if (claude) {
|
|
108
|
+
const total = getRealUsageTotal(claude.context_window?.current_usage);
|
|
109
|
+
const windowSize = claude.context_window?.context_window_size;
|
|
110
|
+
if (total !== undefined && windowSize) {
|
|
111
|
+
realUsedPercentage = Math.min(100, Math.round((total / windowSize) * 100 * 10) / 10);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Auto-compact proximity warning: fires when context fill is in the
|
|
115
|
+
// [threshold-gap, threshold) window. Uses realUsedPercentage when available
|
|
116
|
+
// (more accurate; includes output+cache), falls back to usedPercentage for
|
|
117
|
+
// legacy payloads. Gated by platform (different thresholds Claude vs Qwen).
|
|
118
|
+
const effectivePct = realUsedPercentage ?? contextWindow.used_percentage ?? 0;
|
|
119
|
+
const platformAutoCompactThreshold = AUTO_COMPACT_THRESHOLD[platform];
|
|
120
|
+
const nearAutoCompact = effectivePct >= (platformAutoCompactThreshold - AUTO_COMPACT_WARNING_GAP)
|
|
121
|
+
&& effectivePct < platformAutoCompactThreshold;
|
|
87
122
|
// Performance (Qwen only)
|
|
88
123
|
let performance;
|
|
89
124
|
if (qwen && first?.api) {
|
|
@@ -138,6 +173,8 @@ export function normalize(input) {
|
|
|
138
173
|
windowSize: qwen
|
|
139
174
|
? qwen.context_window.context_window_size
|
|
140
175
|
: claude?.context_window?.context_window_size,
|
|
176
|
+
realUsedPercentage,
|
|
177
|
+
nearAutoCompact,
|
|
141
178
|
},
|
|
142
179
|
cost: claude ? claude.cost?.total_cost_usd : undefined,
|
|
143
180
|
durationMs: claude ? claude.cost?.total_duration_ms : undefined,
|
package/dist/normalize.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalize.js","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,EAAE;AACF,yDAAyD;AACzD,kDAAkD;AAClD,yDAAyD;
|
|
1
|
+
{"version":3,"file":"normalize.js","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"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"}
|