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 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
- if (payload.type === "user_message" || role === "user") analysis.userPromptCount += 1;
983
- if (payload.type === "agent_message" || role === "assistant") analysis.assistantMessageCount += 1;
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 = "direct_usage";
1214
- accumulator.tokenConfidence = "medium";
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: dateValue(row.created_at),
1442
- endedAt: dateValue(row.updated_at),
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(dateValue(row.created_at), dateValue(row.updated_at)),
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 = extractMessageText(payload) ?? extractMessageText(object);
1841
+ const text = extractMessageText2(payload) ?? extractMessageText2(object);
1741
1842
  if (!text) return;
1742
- breakdown.promptTimeline.push({
1843
+ const timelineText = compactTimelineText(text);
1844
+ if (!timelineText || isCodexStartupContext2(timelineText)) return;
1845
+ const item = {
1743
1846
  role,
1744
- text: normalizeTimelineText(text),
1745
- timestamp: dateValue(payload.timestamp) ?? dateValue(object.timestamp) ?? dateValue(payload.created_at) ?? dateValue(object.created_at)
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 extractMessageText(record) {
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.replace(/\s+/g, " ").trim().slice(0, 1e3);
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 app";
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 app" : "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 dateValue(value) {
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 sessions = discovery.files.map((file, index) => claudeFileToUsage(file, index, options));
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 = dateValue2(record.timestamp);
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) applyUsage(breakdown, usage);
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 dateValue2(value) {
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: 0.75, cachedInputPerMillion: 0.075, outputPerMillion: 4.5, reasoningOutputPerMillion: 4.5 },
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,
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repospend",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "Local-first dashboard for tracking AI coding token usage by repo, source, session, model, and day.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Mehmet Mustafa Demir",
@@ -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}}