repospend 0.0.4 → 0.0.5
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 +10 -0
- package/dist/cli.js +174 -32
- package/docs/PUBLISHING.md +4 -1
- package/package.json +1 -1
- package/web-dist/assets/index-D6eYRahs.css +1 -0
- package/web-dist/assets/{index-B8lKmhuv.js → index-DcroX3EF.js} +60 -60
- package/web-dist/index.html +2 -2
- package/web-dist/assets/index-CUTybR_N.css +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to RepoSpend will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## 0.0.5
|
|
6
|
+
|
|
7
|
+
- Fix Codex token under-count on long sessions by summing per-turn `last_token_usage` instead of taking the max of `total_token_usage`, which Codex resets on each context compaction.
|
|
8
|
+
- Track Codex context compactions per session (auto vs. manual `/compact`) and surface them in the session detail panel.
|
|
9
|
+
- Filter Codex startup-context blobs (AGENTS.md/INSTRUCTIONS/environment_context) out of the prompt timeline and prompt counts.
|
|
10
|
+
- Dedupe Claude Code token usage across resumed `.jsonl` files using `requestId` + message `id`, so replayed assistant turns are not double-billed.
|
|
11
|
+
- Label Codex sub-agent sessions as "Codex subagent" instead of folding them into "Codex app".
|
|
12
|
+
- Show the running RepoSpend version in the dashboard footer.
|
|
13
|
+
- Publish a GitHub release with the matching `CHANGELOG.md` section whenever a `v*.*.*` tag is pushed.
|
|
14
|
+
|
|
5
15
|
## 0.0.4
|
|
6
16
|
|
|
7
17
|
- Automatically retry Codex SQLite access after attempting to rebuild `better-sqlite3` when an `npx` or local install has a Node native module ABI mismatch.
|
package/dist/cli.js
CHANGED
|
@@ -212,6 +212,8 @@ var defaultPricing = {
|
|
|
212
212
|
"gpt-5.4-mini": { inputPerMillion: 0.75, cachedInputPerMillion: 0.075, outputPerMillion: 4.5, reasoningOutputPerMillion: 4.5 },
|
|
213
213
|
"gpt-5.4-nano": { inputPerMillion: 0.2, cachedInputPerMillion: 0.02, outputPerMillion: 1.25, reasoningOutputPerMillion: 1.25 },
|
|
214
214
|
"gpt-5.4-pro": { inputPerMillion: 30, outputPerMillion: 180, reasoningOutputPerMillion: 180 },
|
|
215
|
+
"gpt-5.3-codex": { inputPerMillion: 1.75, cachedInputPerMillion: 0.175, outputPerMillion: 14, reasoningOutputPerMillion: 14 },
|
|
216
|
+
"gpt-5.3-codex-spark": { inputPerMillion: 1.75, cachedInputPerMillion: 0.175, outputPerMillion: 14, reasoningOutputPerMillion: 14, note: "Research preview pricing is not final in the OpenAI Codex rate card; this API-equivalent rate matches the practical ccusage/LiteLLM-style estimate." },
|
|
215
217
|
"gpt-5.2": { inputPerMillion: 1.75, cachedInputPerMillion: 0.175, outputPerMillion: 14, reasoningOutputPerMillion: 14 },
|
|
216
218
|
"gpt-5.2-chat-latest": { inputPerMillion: 1.75, cachedInputPerMillion: 0.175, outputPerMillion: 14, reasoningOutputPerMillion: 14 },
|
|
217
219
|
"gpt-5.2-codex": { inputPerMillion: 1.75, cachedInputPerMillion: 0.175, outputPerMillion: 14, reasoningOutputPerMillion: 14 },
|
|
@@ -946,8 +948,9 @@ import Database from "better-sqlite3";
|
|
|
946
948
|
// packages/core/src/sources/agent-friction.ts
|
|
947
949
|
function analyzeAgentFriction(records, totalTokens) {
|
|
948
950
|
const analysis = emptyAgentFrictionAnalysis();
|
|
951
|
+
const messages = [];
|
|
949
952
|
for (const record of records) {
|
|
950
|
-
applyActionMetadata(analysis, record);
|
|
953
|
+
applyActionMetadata(analysis, record, messages);
|
|
951
954
|
}
|
|
952
955
|
finalizeCommandIssues(analysis, totalTokens);
|
|
953
956
|
return analysis;
|
|
@@ -973,14 +976,14 @@ function emptyAgentFrictionAnalysis() {
|
|
|
973
976
|
assistantMessageCount: 0
|
|
974
977
|
};
|
|
975
978
|
}
|
|
976
|
-
function applyActionMetadata(analysis, record) {
|
|
979
|
+
function applyActionMetadata(analysis, record, messages) {
|
|
977
980
|
if (!record || typeof record !== "object") return;
|
|
978
981
|
const object = record;
|
|
979
982
|
const payload = firstObject(object.payload) ?? object;
|
|
980
983
|
const type = stringValue(object.type) ?? stringValue(payload.type);
|
|
981
984
|
const role = stringValue(payload.role);
|
|
982
|
-
|
|
983
|
-
if (
|
|
985
|
+
const messageRole = payload.type === "user_message" || role === "user" ? "user" : payload.type === "agent_message" || role === "assistant" ? "assistant" : void 0;
|
|
986
|
+
if (messageRole) applyMessageCount(analysis, messages, messageRole, payload, object);
|
|
984
987
|
const text = JSON.stringify(record).toLowerCase();
|
|
985
988
|
const isTool = type?.includes("tool") || type?.includes("function_call") || text.includes('"tool_call"') || text.includes('"function_call"');
|
|
986
989
|
if (isTool) analysis.toolCallCount += 1;
|
|
@@ -1140,6 +1143,64 @@ function firstObject(...values) {
|
|
|
1140
1143
|
function stringValue(value) {
|
|
1141
1144
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1142
1145
|
}
|
|
1146
|
+
function applyMessageCount(analysis, messages, role, payload, object) {
|
|
1147
|
+
const message = extractMessageText(payload) ?? extractMessageText(object);
|
|
1148
|
+
if (!message) return;
|
|
1149
|
+
const item = {
|
|
1150
|
+
role,
|
|
1151
|
+
text: compactText(message),
|
|
1152
|
+
timestamp: dateValue(payload.timestamp) ?? dateValue(object.timestamp) ?? dateValue(payload.created_at) ?? dateValue(object.created_at)
|
|
1153
|
+
};
|
|
1154
|
+
if (!item.text || isCodexStartupContext(item.text) || hasNearbyDuplicateMessage(messages, item)) return;
|
|
1155
|
+
messages.push(item);
|
|
1156
|
+
if (role === "user") analysis.userPromptCount += 1;
|
|
1157
|
+
if (role === "assistant") analysis.assistantMessageCount += 1;
|
|
1158
|
+
}
|
|
1159
|
+
function extractMessageText(record) {
|
|
1160
|
+
for (const key of ["text", "message", "content", "input"]) {
|
|
1161
|
+
const value = record[key];
|
|
1162
|
+
if (typeof value === "string" && value.trim()) return value;
|
|
1163
|
+
if (Array.isArray(value)) {
|
|
1164
|
+
const parts = value.map((item) => {
|
|
1165
|
+
if (typeof item === "string") return item;
|
|
1166
|
+
if (item && typeof item === "object") {
|
|
1167
|
+
const object = item;
|
|
1168
|
+
return stringValue(object.text) ?? stringValue(object.content);
|
|
1169
|
+
}
|
|
1170
|
+
return void 0;
|
|
1171
|
+
}).filter((item) => Boolean(item?.trim()));
|
|
1172
|
+
if (parts.length) return parts.join("\n");
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
return void 0;
|
|
1176
|
+
}
|
|
1177
|
+
function compactText(text) {
|
|
1178
|
+
return text.replace(/\s+/g, " ").trim();
|
|
1179
|
+
}
|
|
1180
|
+
function isCodexStartupContext(text) {
|
|
1181
|
+
return text.includes("<environment_context>") && (text.includes("# AGENTS.md instructions") || text.includes("<INSTRUCTIONS>"));
|
|
1182
|
+
}
|
|
1183
|
+
function hasNearbyDuplicateMessage(items, item) {
|
|
1184
|
+
return items.some((existing) => {
|
|
1185
|
+
if (existing.role !== item.role || existing.text !== item.text) return false;
|
|
1186
|
+
if (!existing.timestamp || !item.timestamp) return true;
|
|
1187
|
+
const existingTime = new Date(existing.timestamp).getTime();
|
|
1188
|
+
const itemTime = new Date(item.timestamp).getTime();
|
|
1189
|
+
if (!Number.isFinite(existingTime) || !Number.isFinite(itemTime)) return false;
|
|
1190
|
+
return Math.abs(existingTime - itemTime) <= 5e3;
|
|
1191
|
+
});
|
|
1192
|
+
}
|
|
1193
|
+
function dateValue(value) {
|
|
1194
|
+
if (typeof value === "number") {
|
|
1195
|
+
if (!Number.isFinite(value)) return void 0;
|
|
1196
|
+
return new Date(value > 1e10 ? value : value * 1e3).toISOString();
|
|
1197
|
+
}
|
|
1198
|
+
if (typeof value === "string") {
|
|
1199
|
+
const date = new Date(value);
|
|
1200
|
+
return Number.isNaN(date.getTime()) ? void 0 : date.toISOString();
|
|
1201
|
+
}
|
|
1202
|
+
return void 0;
|
|
1203
|
+
}
|
|
1143
1204
|
|
|
1144
1205
|
// packages/core/src/sources/codex-token.ts
|
|
1145
1206
|
function aggregateTokens(records) {
|
|
@@ -1210,8 +1271,8 @@ function finalizeTokenAccumulator(accumulator) {
|
|
|
1210
1271
|
{ inputTokens: 0, cachedInputTokens: 0, outputTokens: 0, reasoningTokens: 0, totalTokens: 0 }
|
|
1211
1272
|
);
|
|
1212
1273
|
applyFinalUsage(accumulator, total);
|
|
1213
|
-
accumulator.tokenAggregationMethod = "
|
|
1214
|
-
accumulator.tokenConfidence = "
|
|
1274
|
+
accumulator.tokenAggregationMethod = "delta_sum";
|
|
1275
|
+
accumulator.tokenConfidence = "high";
|
|
1215
1276
|
return;
|
|
1216
1277
|
}
|
|
1217
1278
|
accumulator.tokenAggregationMethod = "unknown";
|
|
@@ -1222,6 +1283,10 @@ function extractRecordTokens(record) {
|
|
|
1222
1283
|
const object = record;
|
|
1223
1284
|
const payload = firstObject2(object.payload);
|
|
1224
1285
|
const payloadInfo = firstObject2(payload?.info);
|
|
1286
|
+
const codexLastUsage = firstObject2(payloadInfo?.last_token_usage);
|
|
1287
|
+
if (codexLastUsage) {
|
|
1288
|
+
return tokenUsageFromObject(codexLastUsage, false);
|
|
1289
|
+
}
|
|
1225
1290
|
const codexTotalUsage = firstObject2(payloadInfo?.total_token_usage);
|
|
1226
1291
|
if (codexTotalUsage) {
|
|
1227
1292
|
return tokenUsageFromObject(codexTotalUsage, true);
|
|
@@ -1438,8 +1503,8 @@ function threadToUsage(row, index, sessionFiles, options) {
|
|
|
1438
1503
|
gitRemoteUrl: repo.gitRemoteUrl,
|
|
1439
1504
|
gitBranch: repo.gitBranch,
|
|
1440
1505
|
title: stringValue2(row.title),
|
|
1441
|
-
startedAt:
|
|
1442
|
-
endedAt:
|
|
1506
|
+
startedAt: dateValue2(row.created_at),
|
|
1507
|
+
endedAt: dateValue2(row.updated_at),
|
|
1443
1508
|
model: stringValue2(row.model),
|
|
1444
1509
|
provider: stringValue2(row.model_provider),
|
|
1445
1510
|
inputTokens,
|
|
@@ -1456,7 +1521,7 @@ function threadToUsage(row, index, sessionFiles, options) {
|
|
|
1456
1521
|
rawTokenTotal: dbTokens || sessionBreakdown.totalTokens || void 0,
|
|
1457
1522
|
warnings,
|
|
1458
1523
|
codexHome: options.codexHome ?? path4.join(os2.homedir(), ".codex"),
|
|
1459
|
-
durationMs: durationMs(
|
|
1524
|
+
durationMs: durationMs(dateValue2(row.created_at), dateValue2(row.updated_at)),
|
|
1460
1525
|
rawEventCount: sessionBreakdown.rawEventCount,
|
|
1461
1526
|
parseStatus: sessionBreakdown.parseStatus,
|
|
1462
1527
|
parseErrors: sessionBreakdown.parseErrors,
|
|
@@ -1480,7 +1545,8 @@ function threadToUsage(row, index, sessionFiles, options) {
|
|
|
1480
1545
|
fileReadCount: sessionBreakdown.fileReadCount,
|
|
1481
1546
|
fileEditCount: sessionBreakdown.fileEditCount,
|
|
1482
1547
|
promptTimeline: sessionBreakdown.promptTimeline,
|
|
1483
|
-
sessionOutcome: inferOutcome(sessionBreakdown)
|
|
1548
|
+
sessionOutcome: inferOutcome(sessionBreakdown),
|
|
1549
|
+
compaction: sessionBreakdown.compaction
|
|
1484
1550
|
};
|
|
1485
1551
|
const cost = hasPricedBreakdown ? calculateCostUsd(usage, options.pricing) : void 0;
|
|
1486
1552
|
const costWarnings = hasPricedBreakdown ? warnings : [...warnings, "missing_token_breakdown"];
|
|
@@ -1550,7 +1616,8 @@ function readStandaloneSessions(sessionFiles, options) {
|
|
|
1550
1616
|
fileReadCount: breakdown.fileReadCount,
|
|
1551
1617
|
fileEditCount: breakdown.fileEditCount,
|
|
1552
1618
|
promptTimeline: breakdown.promptTimeline,
|
|
1553
|
-
sessionOutcome: inferOutcome(breakdown)
|
|
1619
|
+
sessionOutcome: inferOutcome(breakdown),
|
|
1620
|
+
compaction: breakdown.compaction
|
|
1554
1621
|
};
|
|
1555
1622
|
});
|
|
1556
1623
|
}
|
|
@@ -1588,11 +1655,15 @@ function readSessionBreakdown(filePath) {
|
|
|
1588
1655
|
breakdown.parseStatus = parse.errors.length ? records.length ? "partial" : "failed" : "ok";
|
|
1589
1656
|
let eventMessageCount = 0;
|
|
1590
1657
|
let responseMessageCount = 0;
|
|
1658
|
+
let lastUserMessageText;
|
|
1591
1659
|
for (const record of records) {
|
|
1592
1660
|
applySessionMetadata(breakdown, record);
|
|
1593
1661
|
applyPromptTimeline(breakdown, record);
|
|
1594
1662
|
if (isCodexEventMessage(record)) eventMessageCount += 1;
|
|
1595
1663
|
if (isResponseMessage(record)) responseMessageCount += 1;
|
|
1664
|
+
const userText = extractUserMessageText(record);
|
|
1665
|
+
if (userText !== void 0) lastUserMessageText = userText;
|
|
1666
|
+
applyCompactionEvent(breakdown, record, lastUserMessageText);
|
|
1596
1667
|
}
|
|
1597
1668
|
breakdown.messageCount = eventMessageCount || responseMessageCount;
|
|
1598
1669
|
const tokenAggregation = aggregateTokens(records);
|
|
@@ -1701,7 +1772,8 @@ function emptyBreakdown() {
|
|
|
1701
1772
|
commandIssueSamples: [],
|
|
1702
1773
|
fileReadCount: 0,
|
|
1703
1774
|
fileEditCount: 0,
|
|
1704
|
-
promptTimeline: []
|
|
1775
|
+
promptTimeline: [],
|
|
1776
|
+
compaction: { count: 0, autoCount: 0, manualCount: 0, events: [] }
|
|
1705
1777
|
};
|
|
1706
1778
|
}
|
|
1707
1779
|
function fallbackCwdFromPath(filePath) {
|
|
@@ -1729,6 +1801,35 @@ function isResponseMessage(record) {
|
|
|
1729
1801
|
const payload = firstObject3(object.payload);
|
|
1730
1802
|
return payload?.type === "message" && (payload.role === "user" || payload.role === "assistant");
|
|
1731
1803
|
}
|
|
1804
|
+
function extractUserMessageText(record) {
|
|
1805
|
+
if (!record || typeof record !== "object") return void 0;
|
|
1806
|
+
const object = record;
|
|
1807
|
+
const payload = firstObject3(object.payload) ?? object;
|
|
1808
|
+
const payloadType = stringValue2(payload.type);
|
|
1809
|
+
const roleValue = stringValue2(payload.role);
|
|
1810
|
+
const isUser = payloadType === "user_message" || object.type === "response_item" && payloadType === "message" && roleValue === "user";
|
|
1811
|
+
if (!isUser) return void 0;
|
|
1812
|
+
return extractMessageText2(payload) ?? extractMessageText2(object);
|
|
1813
|
+
}
|
|
1814
|
+
function applyCompactionEvent(breakdown, record, lastUserMessageText) {
|
|
1815
|
+
if (!record || typeof record !== "object") return;
|
|
1816
|
+
const object = record;
|
|
1817
|
+
if (object.type !== "event_msg") return;
|
|
1818
|
+
const payload = firstObject3(object.payload);
|
|
1819
|
+
if (payload?.type !== "context_compacted") return;
|
|
1820
|
+
const trigger = isManualCompactCommand(lastUserMessageText) ? "manual" : "auto";
|
|
1821
|
+
breakdown.compaction.count += 1;
|
|
1822
|
+
if (trigger === "manual") breakdown.compaction.manualCount += 1;
|
|
1823
|
+
else breakdown.compaction.autoCount += 1;
|
|
1824
|
+
breakdown.compaction.events.push({
|
|
1825
|
+
timestamp: dateValue2(object.timestamp) ?? dateValue2(payload.timestamp),
|
|
1826
|
+
trigger
|
|
1827
|
+
});
|
|
1828
|
+
}
|
|
1829
|
+
function isManualCompactCommand(text) {
|
|
1830
|
+
if (!text) return false;
|
|
1831
|
+
return /^\s*\/compact\b/.test(text);
|
|
1832
|
+
}
|
|
1732
1833
|
function applyPromptTimeline(breakdown, record) {
|
|
1733
1834
|
if (breakdown.promptTimeline.length >= 80 || !record || typeof record !== "object") return;
|
|
1734
1835
|
const object = record;
|
|
@@ -1737,15 +1838,19 @@ function applyPromptTimeline(breakdown, record) {
|
|
|
1737
1838
|
const roleValue = stringValue2(payload.role);
|
|
1738
1839
|
const role = payloadType === "user_message" || roleValue === "user" ? "user" : payloadType === "agent_message" || roleValue === "assistant" ? "assistant" : void 0;
|
|
1739
1840
|
if (!role) return;
|
|
1740
|
-
const text =
|
|
1841
|
+
const text = extractMessageText2(payload) ?? extractMessageText2(object);
|
|
1741
1842
|
if (!text) return;
|
|
1742
|
-
|
|
1843
|
+
const timelineText = compactTimelineText(text);
|
|
1844
|
+
if (!timelineText || isCodexStartupContext2(timelineText)) return;
|
|
1845
|
+
const item = {
|
|
1743
1846
|
role,
|
|
1744
|
-
text: normalizeTimelineText(
|
|
1745
|
-
timestamp:
|
|
1746
|
-
}
|
|
1847
|
+
text: normalizeTimelineText(timelineText),
|
|
1848
|
+
timestamp: dateValue2(payload.timestamp) ?? dateValue2(object.timestamp) ?? dateValue2(payload.created_at) ?? dateValue2(object.created_at)
|
|
1849
|
+
};
|
|
1850
|
+
if (hasNearbyDuplicatePromptTimelineItem(breakdown.promptTimeline, item)) return;
|
|
1851
|
+
breakdown.promptTimeline.push(item);
|
|
1747
1852
|
}
|
|
1748
|
-
function
|
|
1853
|
+
function extractMessageText2(record) {
|
|
1749
1854
|
for (const key of ["text", "message", "content", "input"]) {
|
|
1750
1855
|
const value = record[key];
|
|
1751
1856
|
if (typeof value === "string" && value.trim()) return value;
|
|
@@ -1763,8 +1868,24 @@ function extractMessageText(record) {
|
|
|
1763
1868
|
}
|
|
1764
1869
|
return void 0;
|
|
1765
1870
|
}
|
|
1871
|
+
function compactTimelineText(text) {
|
|
1872
|
+
return text.replace(/\s+/g, " ").trim();
|
|
1873
|
+
}
|
|
1766
1874
|
function normalizeTimelineText(text) {
|
|
1767
|
-
return text
|
|
1875
|
+
return compactTimelineText(text).slice(0, 1e3);
|
|
1876
|
+
}
|
|
1877
|
+
function isCodexStartupContext2(text) {
|
|
1878
|
+
return text.includes("<environment_context>") && (text.includes("# AGENTS.md instructions") || text.includes("<INSTRUCTIONS>"));
|
|
1879
|
+
}
|
|
1880
|
+
function hasNearbyDuplicatePromptTimelineItem(items, item) {
|
|
1881
|
+
return items.some((existing) => {
|
|
1882
|
+
if (existing.role !== item.role || existing.text !== item.text) return false;
|
|
1883
|
+
if (!existing.timestamp || !item.timestamp) return true;
|
|
1884
|
+
const existingTime = new Date(existing.timestamp).getTime();
|
|
1885
|
+
const itemTime = new Date(item.timestamp).getTime();
|
|
1886
|
+
if (!Number.isFinite(existingTime) || !Number.isFinite(itemTime)) return false;
|
|
1887
|
+
return Math.abs(existingTime - itemTime) <= 5e3;
|
|
1888
|
+
});
|
|
1768
1889
|
}
|
|
1769
1890
|
function detectSurface(source, threadSource) {
|
|
1770
1891
|
const raw = `${source ?? ""} ${threadSource ?? ""}`.toLowerCase();
|
|
@@ -1793,15 +1914,15 @@ function stringValue2(value) {
|
|
|
1793
1914
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1794
1915
|
}
|
|
1795
1916
|
function codexSourceAppLabel(source, threadSource) {
|
|
1796
|
-
if (threadSource === "subagent") return "Codex
|
|
1917
|
+
if (threadSource === "subagent") return "Codex subagent";
|
|
1797
1918
|
if (!source) return "Codex";
|
|
1798
1919
|
if (source === "vscode" || source === "codex_vscode" || source === "codex-vscode") return "VS Code";
|
|
1799
1920
|
if (source === "cli" || source === "terminal" || source === "codex-tui") return "Terminal";
|
|
1800
1921
|
if (source === "codex_app" || source === "codex-app") return "Codex app";
|
|
1801
|
-
if (source.trim().startsWith("{")) return threadSource === "subagent" ? "Codex
|
|
1922
|
+
if (source.trim().startsWith("{")) return threadSource === "subagent" ? "Codex subagent" : "Codex";
|
|
1802
1923
|
return source.replace(/^codex[_-]/, "").split(/[_-]/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
1803
1924
|
}
|
|
1804
|
-
function
|
|
1925
|
+
function dateValue2(value) {
|
|
1805
1926
|
if (typeof value === "number") {
|
|
1806
1927
|
return new Date(value > 1e10 ? value : value * 1e3).toISOString();
|
|
1807
1928
|
}
|
|
@@ -1831,7 +1952,9 @@ function scanClaude(options) {
|
|
|
1831
1952
|
const warnings = [];
|
|
1832
1953
|
const discovery = discoverClaudeSessionFiles(claudeHome, warnings, Boolean(options.claudeHome));
|
|
1833
1954
|
const historyExists = fs5.existsSync(historyPath);
|
|
1834
|
-
const
|
|
1955
|
+
const seenUsageKeys = /* @__PURE__ */ new Set();
|
|
1956
|
+
const orderedFiles = discovery.files.map((file) => ({ file, mtimeMs: safeMtimeMs(file.filePath) })).sort((a, b) => a.mtimeMs - b.mtimeMs);
|
|
1957
|
+
const sessions = orderedFiles.map(({ file }, index) => claudeFileToUsage(file, index, options, seenUsageKeys));
|
|
1835
1958
|
const historyEntryCount = countHistoryEntries(historyPath);
|
|
1836
1959
|
const parseFailureCount = sessions.filter((session) => session.parseStatus === "failed" || (session.parseErrors?.length ?? 0) > 0).length;
|
|
1837
1960
|
const unreadableFileCount = sessions.filter((session) => session.parseErrors?.some((error) => error.startsWith("unable_to_read_session_file:"))).length;
|
|
@@ -1937,8 +2060,8 @@ function walk2(root, maxDepth, onFile, depth = 0) {
|
|
|
1937
2060
|
}
|
|
1938
2061
|
}
|
|
1939
2062
|
}
|
|
1940
|
-
function claudeFileToUsage(file, index, options) {
|
|
1941
|
-
const breakdown = readClaudeBreakdown(file.filePath);
|
|
2063
|
+
function claudeFileToUsage(file, index, options, seenUsageKeys) {
|
|
2064
|
+
const breakdown = readClaudeBreakdown(file.filePath, seenUsageKeys);
|
|
1942
2065
|
const id = breakdown.id ?? (path5.basename(file.filePath).replace(/\.jsonl$/i, "") || `claude-session-${index}`);
|
|
1943
2066
|
const fallbackCwd = inferProjectPath(file.projectDir, file.projectsPath);
|
|
1944
2067
|
const cwd = breakdown.cwd ?? fallbackCwd;
|
|
@@ -2009,7 +2132,7 @@ function claudeFileToUsage(file, index, options) {
|
|
|
2009
2132
|
warnings: cost === void 0 ? [...usage.warnings, "unknown_pricing"] : usage.warnings
|
|
2010
2133
|
};
|
|
2011
2134
|
}
|
|
2012
|
-
function readClaudeBreakdown(filePath) {
|
|
2135
|
+
function readClaudeBreakdown(filePath, seenUsageKeys) {
|
|
2013
2136
|
const breakdown = emptyBreakdown2();
|
|
2014
2137
|
try {
|
|
2015
2138
|
const text = fs5.readFileSync(filePath, "utf8");
|
|
@@ -2018,7 +2141,7 @@ function readClaudeBreakdown(filePath) {
|
|
|
2018
2141
|
breakdown.parseErrors = parse.errors;
|
|
2019
2142
|
breakdown.parseStatus = parse.errors.length ? parse.records.length ? "partial" : "failed" : "ok";
|
|
2020
2143
|
for (const record of parse.records) {
|
|
2021
|
-
applyClaudeRecord(breakdown, record);
|
|
2144
|
+
applyClaudeRecord(breakdown, record, seenUsageKeys);
|
|
2022
2145
|
}
|
|
2023
2146
|
breakdown.totalTokens = breakdown.inputTokens + breakdown.outputTokens;
|
|
2024
2147
|
} catch (error) {
|
|
@@ -2029,8 +2152,8 @@ function readClaudeBreakdown(filePath) {
|
|
|
2029
2152
|
}
|
|
2030
2153
|
return breakdown;
|
|
2031
2154
|
}
|
|
2032
|
-
function applyClaudeRecord(breakdown, record) {
|
|
2033
|
-
const timestamp3 =
|
|
2155
|
+
function applyClaudeRecord(breakdown, record, seenUsageKeys) {
|
|
2156
|
+
const timestamp3 = dateValue3(record.timestamp);
|
|
2034
2157
|
if (timestamp3) {
|
|
2035
2158
|
breakdown.startedAt = minDate(breakdown.startedAt, timestamp3);
|
|
2036
2159
|
breakdown.endedAt = maxDate(breakdown.endedAt, timestamp3);
|
|
@@ -2052,11 +2175,22 @@ function applyClaudeRecord(breakdown, record) {
|
|
|
2052
2175
|
const usageTokens = usage ? usageTotal(usage) : 0;
|
|
2053
2176
|
const model = normalizeClaudeModel(message.model);
|
|
2054
2177
|
if (model && (usageTokens > 0 || !breakdown.model)) breakdown.model = model;
|
|
2055
|
-
if (usage && usageTokens > 0
|
|
2178
|
+
if (usage && usageTokens > 0 && !alreadySeenUsage(record, message, seenUsageKeys)) {
|
|
2179
|
+
applyUsage(breakdown, usage);
|
|
2180
|
+
}
|
|
2056
2181
|
applyContentSignals(breakdown, message.content);
|
|
2057
2182
|
}
|
|
2058
2183
|
if (looksFailedToolResult(record.toolUseResult)) breakdown.failedToolCallCount += 1;
|
|
2059
2184
|
}
|
|
2185
|
+
function alreadySeenUsage(record, message, seenUsageKeys) {
|
|
2186
|
+
const messageId = stringValue3(message.id);
|
|
2187
|
+
const requestId = stringValue3(record.requestId);
|
|
2188
|
+
if (!messageId || !requestId) return false;
|
|
2189
|
+
const key = `${requestId}:${messageId}`;
|
|
2190
|
+
if (seenUsageKeys.has(key)) return true;
|
|
2191
|
+
seenUsageKeys.add(key);
|
|
2192
|
+
return false;
|
|
2193
|
+
}
|
|
2060
2194
|
function applyUsage(breakdown, usage) {
|
|
2061
2195
|
const input = numberValue(usage.input_tokens);
|
|
2062
2196
|
const cacheRead = numberValue(usage.cache_read_input_tokens);
|
|
@@ -2240,7 +2374,7 @@ function normalizeClaudeModel(value) {
|
|
|
2240
2374
|
if (!model || model === "<synthetic>") return void 0;
|
|
2241
2375
|
return model;
|
|
2242
2376
|
}
|
|
2243
|
-
function
|
|
2377
|
+
function dateValue3(value) {
|
|
2244
2378
|
if (typeof value !== "string") return void 0;
|
|
2245
2379
|
const date = new Date(value);
|
|
2246
2380
|
return Number.isNaN(date.getTime()) ? void 0 : date.toISOString();
|
|
@@ -2269,6 +2403,13 @@ function looksFailedToolResult(value) {
|
|
|
2269
2403
|
function errorMessage2(error) {
|
|
2270
2404
|
return error instanceof Error ? error.message : String(error);
|
|
2271
2405
|
}
|
|
2406
|
+
function safeMtimeMs(filePath) {
|
|
2407
|
+
try {
|
|
2408
|
+
return fs5.statSync(filePath).mtimeMs;
|
|
2409
|
+
} catch {
|
|
2410
|
+
return 0;
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2272
2413
|
|
|
2273
2414
|
// packages/core/src/sources/index.ts
|
|
2274
2415
|
function scanUsageSources(options) {
|
|
@@ -2387,7 +2528,7 @@ var baseDate = "2026-05-18";
|
|
|
2387
2528
|
var demoPricing = {
|
|
2388
2529
|
...defaultPricing,
|
|
2389
2530
|
"gpt-5.3-codex": { inputPerMillion: 1.75, cachedInputPerMillion: 0.175, outputPerMillion: 14, reasoningOutputPerMillion: 14 },
|
|
2390
|
-
"gpt-5.3-codex-spark": { inputPerMillion:
|
|
2531
|
+
"gpt-5.3-codex-spark": { inputPerMillion: 1.75, cachedInputPerMillion: 0.175, outputPerMillion: 14, reasoningOutputPerMillion: 14 },
|
|
2391
2532
|
"claude-haiku-4-5-20251001": { inputPerMillion: 1, cacheCreationInputPerMillion: 1.25, cachedInputPerMillion: 0.1, outputPerMillion: 5 }
|
|
2392
2533
|
};
|
|
2393
2534
|
function demoModeEnabled() {
|
|
@@ -3191,6 +3332,7 @@ function createServer(options = {}) {
|
|
|
3191
3332
|
const rtkGain = demoMode ? readDemoRtkGain() : await readRtkGain();
|
|
3192
3333
|
return {
|
|
3193
3334
|
...data,
|
|
3335
|
+
appVersion: readPackageVersion(),
|
|
3194
3336
|
summary: { ...data.summary, skippedZeroTokenSessions: data.skippedZeroTokenSessions },
|
|
3195
3337
|
pricing: {
|
|
3196
3338
|
info: pricingInfo,
|
package/docs/PUBLISHING.md
CHANGED
|
@@ -44,7 +44,10 @@ git tag v0.0.3
|
|
|
44
44
|
git push origin main v0.0.3
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
The publish workflow runs when a `v*.*.*` tag is pushed.
|
|
47
|
+
The publish workflow runs when a `v*.*.*` tag is pushed. After npm publishes
|
|
48
|
+
successfully, the workflow also creates a GitHub release for the tag using the
|
|
49
|
+
matching section from `CHANGELOG.md`. For example, tag `v0.0.4` uses the
|
|
50
|
+
`## 0.0.4` changelog section.
|
|
48
51
|
|
|
49
52
|
## npm Trusted Publishing
|
|
50
53
|
|
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.visible{visibility:visible}.mb-2{margin-bottom:.5rem}.ml-1{margin-left:.25rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.flex{display:flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.min-w-0{min-width:0px}.max-w-72{max-width:18rem}.max-w-80{max-width:20rem}.max-w-96{max-width:24rem}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.resize{resize:both}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-between{justify-content:space-between}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.border{border-width:1px}.border-line{--tw-border-opacity: 1;border-color:rgb(217 221 229 / var(--tw-border-opacity, 1))}.border-rose\/30{border-color:#be123c4d}.bg-rose\/5{background-color:#be123c0d}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.text-amber{--tw-text-opacity: 1;color:rgb(180 83 9 / var(--tw-text-opacity, 1))}.text-plum{--tw-text-opacity: 1;color:rgb(124 58 237 / var(--tw-text-opacity, 1))}.text-rose{--tw-text-opacity: 1;color:rgb(190 18 60 / var(--tw-text-opacity, 1))}.text-slate-100{--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity, 1))}.text-slate-400{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity, 1))}.text-slate-500{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity, 1))}.text-slate-600{--tw-text-opacity: 1;color:rgb(71 85 105 / var(--tw-text-opacity, 1))}.text-slate-700{--tw-text-opacity: 1;color:rgb(51 65 85 / var(--tw-text-opacity, 1))}.text-teal{--tw-text-opacity: 1;color:rgb(15 118 110 / var(--tw-text-opacity, 1))}.\!filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)!important}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}*{box-sizing:border-box}body{margin:0;background:radial-gradient(circle at 18% 0%,rgba(109,93,252,.14),transparent 26rem),radial-gradient(circle at 88% 8%,rgba(45,212,191,.1),transparent 24rem),#060b12;color:#e5e7eb;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}button,select,input{font:inherit}.panel{border:1px solid rgba(148,163,184,.18);background:linear-gradient(180deg,#0f172ae6,#0a121ddb);border-radius:10px;box-shadow:0 18px 50px #00000047,inset 0 1px #ffffff08}.panel-heading{display:flex;min-height:56px;align-items:center;justify-content:space-between;gap:12px;border-bottom:1px solid rgba(148,163,184,.16);padding:12px 16px}.panel-heading h2{font-size:15px;font-weight:650;color:#f8fafc}.panel-actions{display:flex;align-items:center;justify-content:flex-end;gap:10px;flex-wrap:wrap}.app-shell{display:grid;min-height:100vh;grid-template-columns:260px minmax(0,1fr)}.app-shell.sidebar-collapsed{grid-template-columns:72px minmax(0,1fr)}.sidebar{position:sticky;top:0;display:flex;height:100vh;flex-direction:column;gap:22px;border-right:1px solid rgba(148,163,184,.18);background:#030712d1;padding:22px 16px;transition:width .16s ease,padding .16s ease;overflow:hidden}.sidebar-collapsed .sidebar{align-items:center;padding:18px 10px}.brand-lockup{display:flex;align-items:center;gap:12px;padding:4px;min-width:0;width:100%}.brand-mark{display:grid;height:38px;width:38px;place-items:center;border-radius:10px;background:linear-gradient(135deg,#6d5dfc,#2dd4bf);color:#fff}.brand-lockup h1{color:#fff;font-size:19px;font-weight:760;letter-spacing:0}.brand-lockup p{color:#94a3b8;font-size:12px}.brand-text{min-width:0}.sidebar-toggle{display:grid;height:30px;width:30px;flex:0 0 auto;place-items:center;border:1px solid rgba(148,163,184,.18);border-radius:8px;background:#0f172ad1;color:#cbd5e1;cursor:pointer;margin-left:auto}.sidebar-toggle:hover{background:#1e293beb;color:#fff}.sidebar-collapsed .brand-lockup{justify-content:center;flex-direction:column;gap:8px}.sidebar-collapsed .brand-text,.sidebar-collapsed .nav-item span,.sidebar-collapsed .sidebar-label,.sidebar-collapsed .sidebar-status div,.sidebar-collapsed .sidebar-section .button span{display:none}.sidebar-collapsed .sidebar-toggle{margin-left:0}.sidebar-nav{display:grid;gap:6px}.nav-item{display:flex;min-height:40px;align-items:center;gap:10px;border:1px solid transparent;border-radius:8px;background:transparent;color:#cbd5e1;cursor:pointer;padding:0 11px;text-align:left}.sidebar-collapsed .nav-item,.sidebar-collapsed .sidebar-section .button{width:42px;justify-content:center;padding:0}.nav-item:hover{background:#94a3b814;color:#fff}.nav-item.active{border-color:#a78bfa73;background:linear-gradient(135deg,#6d5dfcf2,#7c3aedd9);color:#fff}.sidebar-section{display:grid;gap:8px;border-top:1px solid rgba(148,163,184,.14);padding-top:16px;width:100%}.sidebar-label{color:#64748b;font-size:11px;font-weight:700;text-transform:uppercase}.sidebar-status{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:8px;margin-top:auto;border-top:1px solid rgba(148,163,184,.14);color:#e2e8f0;font-size:13px;padding-top:16px}.sidebar-collapsed .sidebar-status{grid-template-columns:1fr;justify-items:center;width:100%}.sidebar-collapsed .sidebar-status .icon-button{margin-top:8px}.sidebar-status span:not(.status-dot){color:#64748b;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-size:11px}.status-dot{display:inline-block;height:9px;width:9px;border-radius:999px;background:#22c55e;box-shadow:0 0 16px #22c55ebf}.icon-button{display:grid;height:34px;width:34px;place-items:center;border:1px solid rgba(148,163,184,.18);border-radius:8px;background:#0f172ae6;color:#cbd5e1;cursor:pointer}.content-shell{min-width:0;width:100%;max-width:1600px;padding:24px}.page-header{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:16px}.page-header h2{color:#fff;font-size:22px;font-weight:760}.page-header p{color:#94a3b8;font-size:13px}.page-header-badge{display:inline-flex;align-items:center;gap:8px;border:1px solid rgba(148,163,184,.16);border-radius:8px;background:#0f172ab8;color:#cbd5e1;font-size:12px;padding:8px 10px}.dashboard-grid{display:grid;grid-template-columns:minmax(0,1fr) 340px;gap:16px}.overview-chart-stack{display:grid;gap:16px}.overview-chart-pair{display:grid;gap:16px;grid-template-columns:repeat(2,minmax(0,1fr))}.primary-kpi-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.kpi-card{overflow:hidden;padding:14px}.kpi-card-strong{border-color:#6d5dfc5c;background:linear-gradient(180deg,#141f36f5,#0b1422e6)}.kpi-label{display:inline-flex;align-items:center;gap:6px;color:#cbd5e1;font-size:12px}.kpi-icon{display:grid;height:28px;width:28px;place-items:center;border:1px solid rgba(109,93,252,.25);border-radius:8px;background:#6d5dfc24;color:#a78bfa}.kpi-value{margin-top:8px;color:#fff;font-size:24px;font-weight:760;line-height:1.1;overflow-wrap:anywhere}.kpi-detail{margin-top:6px;color:#94a3b8;font-size:12px}.kpi-link{max-width:100%;overflow:hidden;color:inherit;cursor:pointer;font:inherit;text-align:left;text-overflow:ellipsis;white-space:nowrap}.kpi-link:hover,.kpi-link:focus-visible{color:#5eead4;outline:none}.secondary-metric-grid,.data-health-grid,.waste-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.source-breakdown-panel{overflow:hidden}.source-breakdown-grid{display:grid;gap:10px;padding:12px}.source-breakdown-row{display:grid;grid-template-columns:160px repeat(4,minmax(0,1fr));align-items:center;gap:10px;border:1px solid rgba(148,163,184,.12);border-radius:8px;background:#0206173d;padding:10px}.source-note{border-top:1px solid rgba(148,163,184,.12);color:#94a3b8;font-size:12px;padding:10px 14px 12px}.breakdown-heading{display:grid;grid-template-columns:1fr auto 1fr;align-items:center}.breakdown-heading-text{justify-self:start}.breakdown-tabs{display:inline-flex;gap:4px;border:1px solid rgba(148,163,184,.16);border-radius:8px;background:#02061747;justify-self:center;padding:4px}.breakdown-tabs button{border:0;border-radius:6px;background:transparent;color:#94a3b8;cursor:pointer;font-size:12px;font-weight:700;padding:6px 10px}.breakdown-tabs button:hover{color:#f8fafc}.breakdown-tabs button.active{background:#2dd4bf1f;color:#5eead4}.data-health-card{overflow:hidden;padding:14px}.data-health-main{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;padding:2px 2px 14px}.data-health-main h2{color:#f8fafc;font-size:15px;font-weight:650}.data-health-main p{color:#94a3b8;font-size:13px}.token-accuracy-card{padding:14px}.token-accuracy-main{padding:2px 2px 14px}.token-accuracy-main h2{color:#f8fafc;font-size:15px;font-weight:650}.token-accuracy-main p{color:#94a3b8;font-size:13px}.token-accuracy-grid,.parser-health-grid{display:grid;gap:12px;grid-template-columns:repeat(5,minmax(0,1fr))}.parser-health-grid{grid-template-columns:repeat(4,minmax(0,1fr))}.panel-inline-heading{display:flex;align-items:center;gap:8px}.panel-inline-heading h2{color:#f8fafc;font-size:15px;font-weight:650}.empty-panel{padding:18px}.loading-state{border-color:#2dd4bf57;margin-top:16px;padding:18px}.loading-state-compact{position:sticky;top:12px;z-index:15;margin-top:12px;padding:12px 14px;box-shadow:0 14px 38px #0000005c,0 0 0 1px #2dd4bf1f}.loading-state-inner{display:flex;align-items:flex-start;gap:12px}.loading-state-icon{height:20px;width:20px;color:#2dd4bf;margin-top:2px}.loading-state h2{color:#f8fafc;font-size:15px;font-weight:760}.loading-state p{color:#94a3b8;font-size:13px;margin-top:3px}.key-insights-grid{display:grid;gap:10px;grid-template-columns:repeat(3,minmax(0,1fr))}.key-insights-list{display:grid;gap:10px}.key-insight{border:1px solid rgba(148,163,184,.14);border-radius:8px;background:#0206176b;color:#dbeafe;font-size:13px;line-height:1.45;padding:12px}.key-insight-topline{display:flex;align-items:center;justify-content:space-between;gap:10px;color:#94a3b8;font-size:11px;font-weight:750;text-transform:uppercase}.key-insight-topline strong{color:#f8fafc;font-size:12px;text-transform:none}.key-insight p{margin-top:7px}.key-insight-good{border-color:#2dd4bf42}.key-insight-attention{border-color:#f59e0b4d}.waste-card{border:1px solid rgba(245,158,11,.24);border-radius:8px;background:#f59e0b14;padding:12px}.waste-good{border-color:#2dd4bf42;background:#2dd4bf12}.button{display:inline-flex;min-height:36px;align-items:center;gap:8px;border:1px solid rgba(148,163,184,.18);border-radius:8px;background:#0f172ad1;color:#dbeafe;padding:7px 10px;text-decoration:none}.button:hover{background:#1e293be6}.danger-button{border-color:#ef44446b;background:#7f1d1d47;color:#fecaca}.danger-button:hover{background:#7f1d1d6b;color:#fff1f2}.active-button{border-color:#6d5dfc8c;background:#6d5dfc2e;color:#c4b5fd;font-weight:650}.red-zone{display:flex;align-items:center;justify-content:space-between;gap:16px;border-color:#ef444452}.red-zone h2{color:#fecaca;font-size:15px;font-weight:760}.red-zone p{color:#cbd5e1;font-size:13px;line-height:1.55;margin-top:4px}.red-zone code{color:#fca5a5}.text-button{border:0;background:transparent;color:#2dd4bf;cursor:pointer;font-size:12px;font-weight:700;padding:0;text-decoration:none}.row-link-button{border:0;background:transparent;color:inherit;cursor:pointer;font:inherit;font-weight:700;padding:0;text-align:left}.row-link-button:hover,.row-link-button:focus-visible{color:#5eead4;text-decoration:underline;text-decoration-style:dotted;text-underline-offset:4px}.field{display:flex;min-width:0;flex-direction:column;gap:6px}.filter-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(240px,1fr))}.field>span{display:flex;align-items:center;gap:6px;font-size:12px;font-weight:650;color:#94a3b8}.field select,.field input{min-width:0;height:38px;border:1px solid rgba(148,163,184,.2);border-radius:8px;background:#020617ad;color:#f8fafc;padding:0 10px}.display-control-row,.settings-control-grid{display:grid;gap:12px;grid-template-columns:repeat(2,minmax(0,1fr))}.limit-select{display:grid;gap:6px}.limit-select span{color:#94a3b8;font-size:12px;font-weight:700;text-transform:uppercase}.limit-select select{min-height:38px;border:1px solid rgba(148,163,184,.22);border-radius:8px;background:#0206176b;color:#f8fafc;padding:0 10px}.app-picker-field{min-width:0}.app-picker{display:flex;min-height:38px;min-width:0;flex-wrap:wrap;gap:6px;align-content:flex-start;border:1px solid rgba(148,163,184,.2);border-radius:8px;background:#02061775;overflow:visible;padding:5px}.filter-search-field{display:flex;min-height:32px;align-items:center;gap:7px;border:1px solid rgba(148,163,184,.18);border-radius:8px;background:#02061761;color:#94a3b8;margin-bottom:6px;padding:0 8px}.filter-search-field input{min-width:0;width:100%;border:0;background:transparent;color:#f8fafc;font-size:12px;outline:none}.filter-search-field input::-moz-placeholder{color:#64748b}.filter-search-field input::placeholder{color:#64748b}.filter-empty{align-self:center;color:#94a3b8;font-size:12px;padding:4px 8px}.app-pill{display:inline-flex;max-width:100%;min-height:28px;align-items:center;gap:6px;border:1px solid transparent;border-radius:7px;background:transparent;color:#cbd5e1;cursor:pointer;font-size:12px;font-weight:650;padding:4px 8px}.app-pill:hover{background:#94a3b81a;color:#f8fafc}.app-pill.active{border-color:#2dd4bf57;background:#2dd4bf1f;color:#5eead4}.app-pill span{color:inherit;font-size:inherit;font-weight:inherit;line-height:1.2;overflow-wrap:anywhere;white-space:normal}.date-range-picker{min-width:240px}.date-range-select-row{display:grid}.date-range-shortcuts{display:flex;flex-wrap:wrap;gap:6px}.date-range-shortcuts button{min-height:28px;border:1px solid rgba(148,163,184,.16);border-radius:999px;background:#0f172a8a;color:#94a3b8;cursor:pointer;font-size:12px;font-weight:700;padding:4px 10px}.date-range-shortcuts button:hover,.date-range-shortcuts button.active{border-color:#2dd4bf57;background:#2dd4bf1a;color:#5eead4}.app-label{display:inline-flex;align-items:center;gap:7px;color:#e2e8f0}.source-badge{display:inline-flex;align-items:center;gap:7px;width:-moz-max-content;width:max-content;max-width:100%;border:1px solid rgba(148,163,184,.16);border-radius:999px;background:#0f172ab8;color:#e2e8f0;font-size:12px;font-weight:700;padding:5px 8px}.source-badge-codex{border-color:#5eead43d;color:#99f6e4}.source-badge-claude{border-color:#f59e0b47;color:#fcd34d}.source-app-cell{display:flex;min-width:0;flex-direction:column;gap:5px}.brand-icon{height:16px;width:16px;flex:0 0 auto}.vscode-icon{color:#1f9cf0}.codex-icon{color:#5eead4}.codex-app-icon{filter:drop-shadow(0 1px 2px rgba(99,102,241,.45))}.claude-icon{color:#e8643a}.claude-desktop-icon{color:#f59e0b}.active-filters{display:flex;flex-wrap:wrap;align-items:center;gap:7px;border-top:1px solid rgba(148,163,184,.12);margin-top:12px;padding-top:12px}.active-filter-label{color:#94a3b8;font-size:11px;font-weight:750;text-transform:uppercase}.active-filter-chip{display:inline-flex;min-height:28px;align-items:center;gap:6px;border:1px solid rgba(45,212,191,.28);border-radius:999px;background:#2dd4bf14;color:#dbeafe;cursor:pointer;font-size:12px;font-weight:650;padding:4px 9px}.active-filter-chip.muted{border-color:#94a3b829;background:#0f172a8a;color:#94a3b8;cursor:default}.active-filter-empty{color:#64748b;font-size:12px}.active-filter-reset{margin-left:auto}.repo-compact-filters{padding:10px}.repo-compact-filter-row{display:grid;align-items:start;gap:10px;grid-template-columns:repeat(4,minmax(150px,1fr)) auto}.repo-more-filters{position:relative}.repo-more-filters summary{display:inline-flex;min-height:38px;align-items:center;gap:8px;border:1px solid rgba(148,163,184,.18);border-radius:8px;background:#0f172ab8;color:#cbd5e1;cursor:pointer;font-size:12px;font-weight:750;padding:0 12px;list-style:none}.repo-more-filters summary::-webkit-details-marker{display:none}.repo-more-filter-body{position:absolute;right:0;top:calc(100% + 8px);z-index:20;width:min(640px,calc(100vw - 32px));display:grid;gap:12px;border:1px solid rgba(148,163,184,.24);border-radius:10px;background:#111827;box-shadow:0 18px 45px #00000061;padding:12px}.repo-custom-dates{display:grid;gap:10px;grid-template-columns:repeat(2,minmax(0,1fr))}.repo-filter-date-note{color:#94a3b8;font-size:11px;margin-top:8px}.input{min-width:0;height:38px;border:1px solid rgba(148,163,184,.2);border-radius:8px;background:#020617ad;color:#f8fafc;padding:0 10px}.pricing-table{min-width:860px}.session-controls{display:grid;gap:10px;border-bottom:1px solid rgba(148,163,184,.12);padding:12px 16px}.search-field{display:flex;min-height:38px;align-items:center;gap:8px;border:1px solid rgba(148,163,184,.18);border-radius:8px;background:#02061785;color:#94a3b8;padding:0 10px}.search-field input{min-width:0;width:100%;border:0;background:transparent;color:#f8fafc;outline:none}.quick-filter-row{display:flex;flex-wrap:wrap;gap:7px}.quick-filter{min-height:28px;border:1px solid rgba(148,163,184,.18);border-radius:999px;background:#0f172a9e;color:#cbd5e1;cursor:pointer;font-size:12px;font-weight:650;padding:4px 9px}.quick-filter:hover:not(:disabled),.quick-filter.active{border-color:#2dd4bf57;background:#2dd4bf1f;color:#5eead4}.quick-filter:disabled{cursor:not-allowed;opacity:.38}.price-input{width:124px;height:38px;border:1px solid rgba(148,163,184,.2);border-radius:7px;background:#020617ad;color:#f8fafc;padding:0 8px}.table-wrap{width:100%;overflow-x:auto}.settings-page{display:grid;gap:16px}.settings-status-banner{display:flex;align-items:center;justify-content:space-between;gap:16px;border:1px solid rgba(45,212,191,.18);border-radius:10px;background:linear-gradient(180deg,#0d222dc7,#09121dc7);padding:14px 16px}.settings-status-title{color:#f8fafc;font-size:14px;font-weight:760}.settings-status-banner p{color:#94a3b8;font-size:13px;margin-top:2px}.settings-tabs{display:flex;flex-wrap:wrap;gap:8px}.settings-tab{min-height:36px;border:1px solid rgba(148,163,184,.14);border-radius:999px;background:#0f172a85;color:#94a3b8;cursor:pointer;font-size:13px;font-weight:700;padding:7px 13px}.settings-tab:hover,.settings-tab.active{border-color:#2dd4bf57;background:#2dd4bf1a;color:#dbeafe}.settings-panel{overflow:hidden}.settings-tab-panel{display:grid;gap:18px;padding:20px}.settings-section-header{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}.settings-section-header h2{color:#f8fafc;font-size:18px;font-weight:760}.settings-section-header p,.settings-supporting-copy{max-width:860px;color:#94a3b8;font-size:13px;line-height:1.55;margin-top:5px}.settings-primary-actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px}.pricing-toolbar{display:grid;gap:10px}.pricing-filter-group{display:inline-flex;width:-moz-fit-content;width:fit-content;flex-wrap:wrap;gap:7px;border:1px solid rgba(148,163,184,.12);border-radius:999px;background:#0206173d;padding:5px}.pricing-search{max-width:520px}.pricing-add-row{display:grid;gap:10px;grid-template-columns:minmax(220px,520px) auto;justify-content:start}.pricing-meta-row,.source-card-grid,.privacy-grid,.settings-detail-grid{display:grid;gap:12px}.pricing-meta-row{grid-template-columns:repeat(4,minmax(0,1fr))}.settings-table-wrap{border:1px solid rgba(148,163,184,.12);border-radius:10px;background:#02061742}.spacious-pricing-table th,.spacious-pricing-table td{padding:14px 16px}.pricing-model-cell{display:grid;gap:4px;min-width:260px}.pricing-model-meta{display:flex;flex-wrap:wrap;align-items:center;gap:8px;color:#94a3b8;font-size:12px}.settings-empty-note{color:#94a3b8;font-size:13px}.settings-supporting-copy{display:flex;flex-wrap:wrap;align-items:center;gap:12px;margin-top:0}.settings-supporting-copy code{color:#cbd5e1}.source-card-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.settings-source-card{display:grid;gap:16px;border:1px solid rgba(148,163,184,.13);border-radius:10px;background:#02061747;padding:16px}.settings-source-card-header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.settings-source-card h3,.settings-subsection h3,.settings-info-panel h2{color:#f8fafc;font-size:15px;font-weight:720}.settings-source-card-header p{color:#94a3b8;font-size:13px;margin-top:3px}.settings-source-metrics,.settings-token-grid{display:grid;gap:10px;grid-template-columns:repeat(3,minmax(0,1fr))}.settings-details{border-top:1px solid rgba(148,163,184,.12);padding-top:10px}.settings-details summary{cursor:pointer;color:#2dd4bf;font-size:12px;font-weight:750}.settings-path-list{display:flex;flex-wrap:wrap;gap:7px;margin-top:10px}.settings-detail-grid{grid-template-columns:repeat(3,minmax(0,1fr));margin-top:10px}.privacy-grid{grid-template-columns:repeat(3,minmax(0,1fr))}.settings-info-panel{border-radius:10px;background:#02061742;padding:16px}.settings-info-panel p{color:#94a3b8;font-size:13px;line-height:1.55;margin-top:7px}.advanced-settings-grid{display:grid;gap:16px}.settings-subsection{display:grid;gap:12px;border-radius:10px;background:#0206173d;padding:16px}.settings-danger-zone{margin-top:2px;background:#7f1d1d1f}.pagination-controls{display:flex;align-items:center;justify-content:space-between;gap:12px;border-top:1px solid rgba(148,163,184,.14);color:#94a3b8;font-size:12px;padding:10px 12px}.pagination-buttons{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.pagination-size{display:inline-flex;align-items:center;gap:6px;color:#94a3b8;font-size:12px;font-weight:700}.pagination-size select{min-height:30px;border:1px solid rgba(148,163,184,.22);border-radius:8px;background:#02061799;color:#f8fafc;padding:0 8px}.pagination-buttons .button{min-height:30px;padding:5px 9px}.pagination-buttons .button:disabled{cursor:not-allowed;opacity:.45}table{width:100%;min-width:1500px;border-collapse:collapse;font-size:13px}.sessions-table th:first-child,.sessions-table td:first-child{position:sticky;left:0;z-index:2;background:#0b1220}.sessions-table th:first-child{z-index:3}.column-menu-wrap{position:relative}.column-menu{position:absolute;right:0;top:calc(100% + 8px);z-index:30;width:280px;max-height:min(560px,72vh);overflow:auto;border:1px solid rgba(148,163,184,.24);border-radius:12px;background:#111827;box-shadow:0 18px 45px #00000061;padding:10px}.column-menu-header{display:flex;align-items:center;justify-content:space-between;gap:10px;border-bottom:1px solid rgba(148,163,184,.14);color:#f8fafc;font-size:13px;font-weight:750;padding:2px 2px 8px}.column-menu-group{padding-top:10px}.column-menu-label{color:#94a3b8;font-size:10px;font-weight:800;letter-spacing:.08em;text-transform:uppercase;margin-bottom:5px}.column-menu-option{display:flex;min-height:30px;align-items:center;gap:8px;border-radius:7px;color:#cbd5e1;cursor:pointer;font-size:12px;padding:4px 6px}.column-menu-option:hover{background:#94a3b81a;color:#f8fafc}.column-menu-option input{accent-color:#2dd4bf}thead th{position:sticky;top:0;z-index:1}.expensive-table{min-width:1500px}.compact-table{min-width:980px}th,td{border-bottom:1px solid rgba(148,163,184,.12);padding:12px;text-align:left;white-space:nowrap}th{background:#0f172aad;color:#94a3b8;font-size:12px;font-weight:700}td{color:#e2e8f0}.sort-header{display:inline-flex;align-items:center;gap:5px;border:0;background:transparent;color:inherit;cursor:pointer;padding:0;font-weight:700}.sort-header.active{color:#2dd4bf}.inline-code{border:1px solid rgba(148,163,184,.18);background:#0206178c;color:#cbd5e1;padding:3px 6px;font-size:12px}.price-chip{border:1px solid rgba(148,163,184,.18);background:#0206178c;color:#cbd5e1;padding:4px 7px;font-size:11px}.value-with-detail{cursor:help;text-decoration:underline;text-decoration-color:#2dd4bf61;text-decoration-style:dotted;text-underline-offset:3px;overflow-wrap:anywhere}.mini-stat-label{display:inline-flex;align-items:center;gap:6px;color:#94a3b8;font-size:12px;text-transform:uppercase}.mini-stat-label+div{overflow-wrap:anywhere}.metric-help-button{display:inline-grid;height:18px;width:18px;place-items:center;border:1px solid rgba(45,212,191,.28);border-radius:999px;background:#2dd4bf14;color:#5eead4;line-height:1;vertical-align:middle}.metric-help-button:hover,.metric-help-button:focus-visible{border-color:#2dd4bf94;background:#2dd4bf29;outline:none}.metric-popover-trigger-wrap{display:inline-flex;align-items:center}.metric-popover{position:fixed;z-index:100;width:min(420px,calc(100vw - 24px));max-width:420px;overflow:visible;border:1px solid rgba(148,163,184,.24);border-radius:10px;background:#111827;box-shadow:0 18px 44px #0000006b,0 0 0 1px #0f172a73;color:#e5e7eb;padding:12px;white-space:normal}.metric-popover-header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.metric-popover-title{color:#f8fafc;font-size:12px;font-weight:760}.metric-popover-value{margin-top:4px;color:#fff;font-size:20px;font-weight:780;line-height:1.1;word-break:break-word}.metric-popover-body,.metric-popover-note{margin-top:10px;color:#a8b3c7;font-size:12px;font-weight:500;line-height:1.45;white-space:normal;overflow-wrap:anywhere}.metric-popover-note{border-left:2px solid rgba(245,158,11,.54);color:#f8d08c;font-weight:650;padding-left:10px}.metric-popover-section,.metric-popover-footer{margin-top:12px;border-top:1px solid rgba(148,163,184,.14);padding-top:12px}.metric-popover-row{display:flex;align-items:center;justify-content:space-between;gap:12px;color:#94a3b8;font-size:12px;white-space:normal}.metric-popover-row+.metric-popover-row{margin-top:8px}.metric-popover-row strong{color:#e5e7eb;font-weight:720;text-align:right}.metric-popover-close{display:grid;height:24px;width:24px;flex:0 0 auto;place-items:center;border:1px solid rgba(148,163,184,.18);border-radius:8px;color:#cbd5e1}.metric-popover-close:hover,.metric-popover-close:focus-visible{border-color:#94a3b86b;outline:none}.cost-value{font-variant-numeric:tabular-nums}.summary-action{cursor:pointer;transition:border-color .12s ease,box-shadow .12s ease,transform .12s ease}.summary-action:hover{border-color:#6d5dfc8c;box-shadow:0 12px 36px #00000057;transform:translateY(-1px)}.meter{height:10px;overflow:hidden;border:1px solid rgba(148,163,184,.18);border-radius:999px;background:#0f172ae6}.meter div{height:100%;background:linear-gradient(90deg,#6d5dfc,#2dd4bf)}.rtk-impact-bars{display:grid;gap:13px;margin-top:16px}.rtk-impact-stats{display:grid;gap:10px;grid-template-columns:repeat(3,minmax(0,1fr));margin-top:14px}.rtk-impact-row{display:grid;gap:7px}.rtk-impact-label,.rtk-impact-footer{display:flex;align-items:center;justify-content:space-between;gap:12px;color:#94a3b8;font-size:12px}.rtk-impact-label strong,.rtk-impact-footer strong{color:#f8fafc;font-weight:700}.rtk-impact-track{height:12px;overflow:hidden;border:1px solid rgba(148,163,184,.18);border-radius:999px;background:#0206179e}.rtk-impact-track div{height:100%;border-radius:inherit}.rtk-impact-before div{background:#6d5dfc}.rtk-impact-after div{background:#38bdf8}.rtk-impact-saved div{background:#2dd4bf}.rtk-impact-footer{margin-top:14px;border-top:1px solid rgba(148,163,184,.12);padding-top:12px}.plain-summary-list,.action-list{display:grid;gap:9px;color:#cbd5e1;font-size:14px}.plain-summary-list li,.action-list li{border:1px solid rgba(148,163,184,.12);border-radius:8px;background:#02061752;padding:9px 10px}.signal-list{display:grid;gap:8px;color:#cbd5e1;font-size:13px}.signal-list li{border:1px solid rgba(148,163,184,.12);border-radius:8px;background:#02061752;padding:8px 9px}.section-title-row,.issue-row-badges,.timeline-meta{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.section-title-row{justify-content:space-between}.issue-row{display:grid;gap:12px;grid-template-columns:minmax(0,1fr) auto;border:1px solid rgba(148,163,184,.12);border-radius:8px;background:#02061752;padding:10px}.timeline-list{display:grid;gap:10px}.timeline-list li{border:1px solid rgba(148,163,184,.12);border-radius:8px;background:#02061752;padding:10px}.timeline-meta{color:#94a3b8;font-size:11px;margin-bottom:6px}.timeline-list p{color:#dbeafe;font-size:13px;line-height:1.55}.timeline-list p:not(.expanded){display:-webkit-box;overflow:hidden;-webkit-box-orient:vertical;-webkit-line-clamp:3}.session-detail-panel{display:grid}.session-detail-heading{display:grid;gap:14px;grid-template-columns:minmax(0,1fr) auto;align-items:start;border-bottom:1px solid rgba(148,163,184,.16);padding:18px}.session-detail-heading h2{overflow:hidden;color:#f8fafc;font-size:19px;font-weight:760;line-height:1.25;text-overflow:ellipsis;white-space:nowrap}.session-detail-heading p{margin-top:8px;color:#cbd5e1;font-size:14px}.session-detail-eyebrow{color:#94a3b8;font-size:11px;font-weight:760;letter-spacing:0;text-transform:uppercase}.session-summary-meta{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-top:9px;color:#94a3b8;font-size:12px;font-weight:650}.session-summary-meta>span:not(.status-badge){display:inline-flex;align-items:center;min-height:24px}.session-summary-meta>span:not(.status-badge):before{content:"";display:inline-block;width:3px;height:3px;border-radius:999px;background:#94a3b89e;margin-right:8px}.session-key-metrics{display:grid;gap:10px;grid-template-columns:repeat(6,minmax(0,1fr));border-bottom:1px solid rgba(148,163,184,.12);padding:14px 18px}.session-detail-tabs{display:flex;flex-wrap:wrap;gap:8px;border-bottom:1px solid rgba(148,163,184,.12);padding:12px 18px}.session-detail-tabs button,.session-filter-buttons button{min-height:34px;border:1px solid rgba(148,163,184,.14);border-radius:999px;background:#0f172a85;color:#94a3b8;cursor:pointer;font-size:13px;font-weight:700;padding:7px 12px}.session-detail-tabs button:hover,.session-detail-tabs button.active,.session-filter-buttons button:hover,.session-filter-buttons button.active{border-color:#2dd4bf57;background:#2dd4bf1a;color:#dbeafe}.session-detail-body{padding:18px}.session-tab-grid{display:grid;gap:14px;grid-template-columns:repeat(2,minmax(0,1fr))}.session-tab-stack{display:grid;gap:14px}.session-card-wide{grid-column:1 / -1}.session-mini-grid{display:grid;gap:10px;grid-template-columns:repeat(2,minmax(0,1fr))}.session-timeline-toolbar{display:grid;gap:10px;grid-template-columns:minmax(0,1fr) auto;align-items:center}.session-filter-buttons{display:flex;flex-wrap:wrap;gap:7px;justify-content:flex-end}.session-message-list{margin:0;padding:0;list-style:none}.metadata-list{display:grid;gap:8px}.metadata-row{display:grid;gap:10px;grid-template-columns:140px minmax(0,1fr);align-items:start;border-bottom:1px solid rgba(148,163,184,.1);padding:8px 0}.metadata-row:last-child{border-bottom:0}.metadata-row span{color:#94a3b8;font-size:12px;font-weight:700}.metadata-row strong{overflow-wrap:anywhere;color:#e2e8f0;font-size:13px;font-weight:650}.detail-muted{color:#cbd5e1;font-size:13px;line-height:1.55}.rtk-health-banner{padding:14px}.rtk-health-banner h2{color:#f8fafc;font-size:15px;font-weight:760}.rtk-health-banner p{color:#cbd5e1;font-size:13px}.rtk-health-good{border-color:#2dd4bf47;background:linear-gradient(180deg,#141f36f5,#0d26298a);color:#5eead4}.rtk-health-critical{border-color:#ef444466;background:linear-gradient(180deg,#30141dbd,#0f172aeb);color:#fca5a5}.developer-details-actions{display:flex;flex-wrap:wrap;align-items:center;gap:8px;border-top:1px solid rgba(148,163,184,.12);padding:12px 14px 0}.recent-command{border:1px solid rgba(148,163,184,.14);background:#02061773;color:#cbd5e1;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-size:12px;padding:7px 8px}.recent-rtk-list{display:grid;gap:7px}.recent-rtk-item{display:grid;grid-template-columns:90px minmax(0,1fr) auto;gap:10px;align-items:center;border:1px solid rgba(148,163,184,.14);border-radius:8px;background:#02061773;padding:9px 10px}.recent-rtk-time,.recent-rtk-savings{color:#94a3b8;font-size:12px;white-space:nowrap}.recent-rtk-command{overflow:hidden;color:#e2e8f0;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-size:12px;text-overflow:ellipsis;white-space:nowrap}.rtk-command-table{display:grid;gap:6px}.rtk-command-row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:12px;border:1px solid rgba(148,163,184,.14);border-radius:8px;background:#02061773;padding:8px 10px}.session-list{display:grid}.session-row{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:10px;align-items:center;border-bottom:1px solid rgba(148,163,184,.1);padding:11px 14px}.session-row-button{width:100%;border:0;background:transparent;color:inherit;cursor:pointer;font:inherit;text-align:left}.session-row-button:hover,.session-row-button:focus-visible{background:#6d5dfc1f;outline:2px solid rgba(45,212,191,.7);outline-offset:-2px}.session-row-rich{align-items:start}.session-row:last-child{border-bottom:0}.session-icon{display:grid;height:34px;width:34px;place-items:center;border-radius:9px;background:#6d5dfc29;color:#a78bfa}.repo-rank-list{display:grid;gap:12px;padding:14px}.repo-rank-row{display:grid;gap:6px}.repo-rank-topline{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:10px;color:#e2e8f0;font-size:13px}.repo-bar{height:6px;overflow:hidden;border-radius:999px;background:#94a3b81a}.repo-bar div{height:100%;border-radius:inherit}.status-badge{display:inline-flex;align-items:center;border:1px solid rgba(148,163,184,.18);border-radius:999px;background:#0f172ab8;color:#cbd5e1;font-size:11px;font-weight:650;line-height:1;padding:5px 9px;text-transform:capitalize;white-space:nowrap}.status-files-edited,.status-completed,.status-high,.status-medium{border-color:#2dd4bf57;color:#5eead4}.status-high-token,.status-no-code-change,.status-unknown-repo,.status-unknown-surface,.status-low{border-color:#f59e0b57;color:#fbbf24}.status-failed-commands,.status-command-failures-detected,.status-command-issue,.status-command-issues-detected{border-color:#ef444457;color:#fca5a5}.repo-detail-summary,.health-summary-row{display:grid;gap:12px;grid-template-columns:repeat(4,minmax(0,1fr))}.repo-detail-page,.repo-tab-body{display:grid;gap:16px}.repo-hero{display:grid;gap:18px;grid-template-columns:minmax(0,1.2fr) minmax(360px,.8fr);padding:18px}.repo-hero-main{min-width:0}.repo-hero-eyebrow{color:#94a3b8;font-size:12px;font-weight:750;text-transform:uppercase}.repo-hero h2{color:#f8fafc;font-size:30px;font-weight:820;line-height:1.12;margin-top:6px;overflow-wrap:anywhere}.repo-hero p{color:#cbd5e1;font-size:14px;line-height:1.5;margin-top:8px}.repo-hero-warning{display:inline-flex;align-items:center;gap:8px;color:#fbbf24!important;font-weight:650}.repo-cost-note{color:#94a3b8!important;font-size:12px!important}.repo-hero-metrics{display:grid;gap:10px;grid-template-columns:repeat(2,minmax(0,1fr))}.repo-detail-tabs{display:flex;flex-wrap:wrap;gap:8px}.repo-detail-tabs button{min-height:36px;border:1px solid rgba(148,163,184,.14);border-radius:999px;background:#0f172a85;color:#94a3b8;cursor:pointer;font-size:13px;font-weight:700;padding:7px 13px}.repo-detail-tabs button:hover,.repo-detail-tabs button.active{border-color:#2dd4bf57;background:#2dd4bf1a;color:#dbeafe}.repo-overview-grid{display:grid;gap:16px;grid-template-columns:minmax(0,1fr) minmax(360px,.9fr)}.repo-diagnosis-card{border-left:3px solid #2dd4bf}.repo-diagnosis-text{color:#f8fafc;font-size:18px;font-weight:650;line-height:1.45;margin-top:12px}.repo-diagnosis-points{display:grid;gap:8px;margin-top:14px}.repo-diagnosis-points span,.compact-warning,.success-state{border:1px solid rgba(148,163,184,.13);border-radius:8px;background:#0f172a80;color:#cbd5e1;font-size:13px;line-height:1.45;padding:9px 10px}.success-state{border-color:#2dd4bf47;color:#5eead4}.compact-warning{border-color:#f59e0b47;color:#fbbf24}.compact-warning details{margin-top:8px}.compact-warning summary{cursor:pointer;color:#f8fafc;font-weight:750}.repo-warning-card{border-color:#f59e0b57;background:linear-gradient(180deg,#2d1c0aad,#0a121ddb)}.repo-concentration-stats{display:grid;gap:10px;grid-template-columns:repeat(2,minmax(0,1fr));margin-top:12px}.repo-concentration-list,.repo-review-list,.repo-warning-list,.repo-model-list{display:grid;gap:8px}.repo-concentration-list{margin-top:12px}.repo-concentration-row{display:grid;gap:7px;border:1px solid rgba(148,163,184,.12);border-radius:8px;background:#02061757;color:#e2e8f0;cursor:pointer;padding:9px 10px;text-align:left}.repo-concentration-row span,.repo-review-main strong{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.repo-concentration-row:hover,.buttonless-row:hover{background:#6d5dfc1f}.repo-review-list{padding:12px}.repo-review-row{display:grid;gap:12px;grid-template-columns:minmax(0,1fr) auto;align-items:center;border:1px solid rgba(148,163,184,.12);border-radius:8px;background:#02061757;padding:10px 12px}.repo-review-main{display:grid;gap:4px;min-width:0}.repo-review-main span,.repo-model-row span{color:#94a3b8;font-size:12px}.repo-review-metrics{display:flex;align-items:center;justify-content:flex-end;flex-wrap:wrap;gap:8px;color:#cbd5e1;font-size:12px}.repo-warning-list{padding:12px}.repo-warning-row{display:flex;align-items:flex-start;gap:8px;border:1px solid rgba(245,158,11,.18);border-radius:8px;background:#f59e0b14;color:#fbbf24;font-size:13px;line-height:1.45;padding:10px}.repo-model-row{display:grid;gap:4px;border:1px solid rgba(148,163,184,.12);border-radius:8px;background:#02061757;color:#e2e8f0;padding:10px}.buttonless-row{width:100%;cursor:pointer;text-align:left}.cost-outlier-row td:first-child{box-shadow:inset 3px 0 #f59e0b}.cost-outlier-row td{background:#f59e0b0b}.detail-card{border:1px solid rgba(148,163,184,.14);border-radius:8px;background:#0206176b;padding:14px}.detail-card h3{color:#f8fafc;font-size:13px;font-weight:700}.advanced-summary{cursor:pointer;color:#f8fafc;font-weight:650;padding:14px 16px}.badge-row{display:flex;flex-wrap:wrap;gap:5px;margin-top:6px}.source-card{display:grid;gap:8px;border:1px solid rgba(148,163,184,.12);border-radius:8px;background:#02061752;padding:10px}.command-health-note{padding:16px}.command-health-note h2{color:#f8fafc;font-size:15px;font-weight:700}.command-health-note p{margin-top:6px;color:#cbd5e1;font-size:13px;line-height:1.5}.command-health-table{min-width:1080px}.status-completed,.status-ok,.status-vs-code{border-color:#2dd4bf57;color:#5eead4}.status-partial,.status-setup-debugging,.status-research-only{border-color:#f59e0b57;color:#fbbf24}.status-failed,.status-unknown{border-color:#ef444457;color:#fca5a5}.raw-output{max-height:360px;overflow:auto;background:#0f172a;color:#e2e8f0;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-size:12px;line-height:1.55;margin:0;padding:14px 16px;white-space:pre-wrap}.insights-grid{display:grid;gap:16px;grid-template-columns:repeat(2,minmax(0,1fr))}.insight-card{border:1px solid rgba(148,163,184,.14);border-radius:8px;background:#0206176b;padding:12px}.insight-good{border-left:3px solid #2dd4bf}.insight-attention{border-left:3px solid #f59e0b}.insight-metric{flex:0 0 auto;border:1px solid rgba(148,163,184,.18);background:#0f172ac7;color:#cbd5e1;padding:3px 6px;font-size:12px;font-weight:650}.severity-pill{display:inline-flex;align-items:center;border:1px solid rgba(148,163,184,.18);border-radius:999px;color:#cbd5e1;font-size:10px;font-weight:760;line-height:1;padding:4px 7px;text-transform:uppercase}.severity-info{border-color:#2dd4bf4d;color:#5eead4}.severity-warning{border-color:#f59e0b57;color:#fbbf24}.severity-critical{border-color:#ef444457;color:#fca5a5}@media(max-width:900px){.insights-grid{grid-template-columns:1fr}}tbody tr{cursor:default}tbody tr.clickable-row{cursor:pointer}tbody tr.clickable-row:focus-visible{background:#6d5dfc29;outline:2px solid rgba(45,212,191,.7);outline-offset:-2px}tbody tr:hover,tbody tr.selected{background:#6d5dfc1f}.warning-pill{display:inline-flex;align-items:center;gap:5px;border:1px solid rgba(245,158,11,.32);background:#f59e0b1a;color:#fbbf24;padding:5px 8px;font-size:12px}.warning-text{overflow-wrap:anywhere;word-break:break-word}.text-slate-500{color:#94a3b8!important}.text-slate-600,.text-slate-700{color:#cbd5e1!important}.text-teal{color:#2dd4bf!important}.text-plum{color:#a78bfa!important}.text-amber{color:#f59e0b!important}.bg-white{background:#0f172ab8!important}.border-line{border-color:#94a3b829!important}@media(max-width:1200px){.app-shell{grid-template-columns:1fr}.sidebar{position:static;height:auto}.sidebar-nav{grid-template-columns:repeat(4,minmax(0,1fr))}.sidebar-section,.sidebar-status{display:none}.dashboard-grid,.overview-chart-pair{grid-template-columns:1fr}.filter-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.repo-compact-filter-row,.repo-hero,.repo-overview-grid{grid-template-columns:1fr}.repo-more-filter-body{left:0;right:auto}.primary-kpi-grid,.secondary-metric-grid,.session-key-metrics,.source-breakdown-row,.data-health-grid,.waste-grid,.token-accuracy-grid,.parser-health-grid,.key-insights-grid,.pricing-meta-row,.source-card-grid,.privacy-grid,.settings-detail-grid,.settings-source-metrics,.settings-token-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.session-tab-grid{grid-template-columns:1fr}}@media(max-width:700px){.content-shell{padding:16px}.page-header{align-items:flex-start;flex-direction:column}.sidebar-nav{grid-template-columns:repeat(2,minmax(0,1fr))}.filter-grid,.repo-compact-filter-row,.repo-hero-metrics,.repo-detail-summary,.repo-concentration-stats,.repo-custom-dates,.repo-review-row{grid-template-columns:1fr}.repo-review-metrics{justify-content:flex-start}.settings-status-banner,.settings-section-header,.session-detail-heading,.session-timeline-toolbar,.red-zone{align-items:stretch;grid-template-columns:1fr;flex-direction:column}.settings-primary-actions{justify-content:flex-start}.pricing-add-row,.primary-kpi-grid,.secondary-metric-grid,.session-key-metrics,.session-mini-grid,.source-breakdown-row,.data-health-grid,.waste-grid,.token-accuracy-grid,.parser-health-grid,.key-insights-grid,.pricing-meta-row,.source-card-grid,.privacy-grid,.settings-detail-grid,.settings-source-metrics,.settings-token-grid,.settings-control-grid{grid-template-columns:1fr}.session-detail-heading h2{white-space:normal}.metadata-row,.recent-rtk-item{grid-template-columns:1fr}.recent-rtk-time,.recent-rtk-savings{white-space:normal}}@media(min-width:768px){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media(min-width:1280px){.xl\:col-span-2{grid-column:span 2 / span 2}.xl\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.xl\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.xl\:grid-cols-\[1\.1fr_0\.9fr\]{grid-template-columns:1.1fr .9fr}}
|