Repository navigation
fix: timezone bucketing and cache-write cost accuracy - #62
Merged
Carl723000 merged 5 commits intoJul 5, 2026
Merged
Carl723000 merged 5 commits into
Carl723000 merged 5 commits into
Conversation
* bucket Today/month/daily/hourly totals by the configured IANA zone * keep the loader zone in lockstep with display; recompute on change * split cache writes into 1h (2x) vs 5m (1.25x) when the log has the ttl * add tests for zone bucketing and the cache-write split
* add the day-boundary clause to the timezone help in all six locales * update the timezone description in README and the locale editions
…changes The timezone-bucketing half overlaps with the shared zone-aware day-key rework landing in V2.2, so this reduces the PR to just the cache-write TTL pricing (pricing.ts + types.ts + pricing.test.ts) to merge cleanly. zeyutang's original commits are preserved; the timezone work is welcome as a follow-up PR.
Collaborator
|
Merged the cache-write TTL pricing — thank you, this is a solid, well-tested addition. 🙏 I trimmed the PR to just that part before merging: the timezone-bucketing half overlapped with a shared zone-aware day-key rework that V2.2 already ships, so it would have collided. Your original commits are preserved in the merge. If you'd like to keep optimizing the timezone side, a fresh PR on top of V2.2 (once it's out) would be very welcome — happy to point you at the |
Carl723000
added a commit
that referenced
this pull request
Jul 6, 2026
…eatmap & more (#68) * chore: V2.2 changelog scaffold, v2.0.3 + v2.1.0 dates, org metadata - CHANGELOG: add [2.2.0] — Unreleased; date [2.1.0] — 2026-06-26; add the missing [2.0.3] maintenance section (macOS Keychain, node:test, issue templates, CI hardening, release workflow). - package.json: repository / bugs / homepage now point at the org (ClaudeCodeUsage/ClaudeCodeUsage), not jack21. Co-Authored-By: Claude <noreply@anthropic.com> * feat(settings): tokenDecimalPlaces — decimals for compact token display New setting (default 1, 0–2) controlling only compact token rendering (1.2M / 345.6K), separate from cost decimalPlaces; full integer counts are unaffected. Wired through I18n.formatTokensCompact and the formatNumber compact path, the settings catalog + SETTINGS_I18N (5 locales), ExtensionConfig and config apply. 7 new node:test cases (0/1/2 places, K/M/B, integer pass-through, clamp). Co-Authored-By: Claude <noreply@anthropic.com> * feat(quota): clean status-bar quota text; opt-in reset + 5h-only Status bar stays clean — default '5h 6% · wk 1%' (space, middot), not the dense '5h:6%:4.8h | wk:1%:1.6d'. Two opt-in settings (both default off): - showResetInStatusBar → '5h 6% ↻4.8h | wk 1% ↻1.6d' (compact countdown, bar sep) - quotaFiveHourOnly → show only the 5h window Full reset times always remain in the tooltip. Extracted a pure, dependency-free src/quotaFormat.ts (formatQuotaStatusText / compactReset / worstShownUtilisation) so it's unit-tested without vscode; statusBar.updateQuota now uses it. Catalog + SETTINGS_I18N (5 locales) + config wiring. 8 new node:test cases. Co-Authored-By: Claude <noreply@anthropic.com> * feat(settings): dashboardAutoRefresh (positive) replacing pauseDashboardRefresh Double-negative 'Pause dashboard auto-refresh' becomes the positive 'Dashboard auto-refresh' (key dashboardAutoRefresh, default true). One-shot SettingsStore.migrateDashboardAutoRefresh inverts any old pauseDashboardRefresh value (globalState or settings.json) — true→false, false/undefined→true — behind its own migration flag so it runs even post-v1-migration. All consumers (extension refresh gating, webview body auto-off class + toggle handler) and SETTINGS_I18N (5 locales) updated. Compile clean; 26/26 node:test green. Co-Authored-By: Claude <noreply@anthropic.com> * feat(v2.2): share-card + heatmap pure-logic cores (tested) Tested, dependency-free foundations for the two big V2.2 features; the webview rendering + PNG export + heatmap cells will build on these in follow-up PRs. - src/shareCard.ts (Usage Share Card / 用量卡片): totalTokens, cacheSharePct, modelFamily (no version exposed), selectShareBadge (deterministic, local rules: Context Marathoner / Cache Saver / Token Sprinter / Workflow Pilot / Steady Builder), buildShareCardData (privacy redaction — project name OFF by default, no room in the data shape for paths/ids/prompts), DEFAULT_SECTIONS, shareCardFilename. - src/heatmap.ts: daysInMonth, intensityBucket (0..4), buildMonthHeatmap (zero- fills every day, tokens/cost/sessions metric switch). - 11 new node:test cases (Phase 5.8 + 6.4). 37/37 green. Co-Authored-By: Claude <noreply@anthropic.com> * docs: V2.2 roadmap + V2.3 achievements spec - docs/V2.2.md: what shipped in 2.2 (status-legended), the Usage Share Card design + enforced privacy model, the token heatmap design, the settings changes, session/quota status, the contributor/PR credit policy, and the planning items (advice 'new session/clear' nudge, Optimizer opt-in file reading, conversation export, subscription sharing, Codex adapter, GitHub profile heatmap). - docs/V2.3-achievements.md: SPEC ONLY (no V2.2 code) — positioning, privacy, the full achievement catalogue (token/session/prompt/workflow/model/plugin/ hidden), levels, anti-gaming, community roadmap, the icon-prompt template, milestones, and the GitHub-profile sharing plan. Co-Authored-By: Claude <noreply@anthropic.com> * docs(changelog): fill [2.2.0] with shipped items Co-Authored-By: Claude <noreply@anthropic.com> * docs: consolidate V2.2 specs into one local master Fold the public roadmap (docs/V2.2.md) and the two local working specs (V2.2-FEATURES-SPEC.md, V2.2-AUTOMATION-SPEC.md) into a single gitignored master, docs/V2.2-SPEC.md (+ a zh-CN mirror for review), so the whole V2.2 plan can be handed to one execution pass. Drop the CHANGELOG pointer to the now-removed public roadmap; the Added/Changed entries already record what actually shipped in 2.2.0. * fix(stats): drop API-error retry re-logs from the Messages count When a request errors, Claude Code retries and re-logs the same user prompt, so an identical prompt can appear several times seconds apart around a system:api_error record — inflating "Messages". Dedupe identical prompts that recur within a short window (retry re-logs); genuine re-sends minutes/hours later still count. Logic isolated in a pure, unit-tested module. Verified on real sessions: removes only true retry re-logs, keeps repeats. Co-Authored-By: Claude <noreply@anthropic.com> * feat(dashboard): cache-hit-rate column in the breakdown tables Adds a cache hit rate (cacheRead / input-side tokens) column to the All-time monthly table, the This-month daily table, and both expanded drill-downs (month→days, day→hours), so cache efficiency is readable per row. Reuses the existing cacheHitRate/formatPercent helpers server-side and a small cacheHitPct helper client-side; detail-row colspans bumped 8→9. Co-Authored-By: Claude <noreply@anthropic.com> * fix(dashboard): timezone-correct date bucketing + table scroll behavior Bucketing: the month/day breakdowns mixed a UTC day key with a local month boundary, so a record just after local midnight on the 1st was filed under the previous month's last day (visible as a stray "30th" inside This-Month). Day and month keys now both derive from the configured timezone (empty = system) via a pure, unit-tested dateKeys module. Table scroll (2b): number cells no longer wrap — compact k/M numbers fit the panel with no horizontal scrollbar; full integer numbers overflow so only the table scrolls, while the daily bar chart keeps its own independent scroll. Co-Authored-By: Claude <noreply@anthropic.com> * feat(dashboard): click a month's Token composition to drill into daily The All-time Token-composition bars are now clickable (each is a month): a click expands that month's detail — which now includes a per-day composition chart next to the daily chart and table — so the input/output/cache split is readable day by day. Reuses the existing month-detail round-trip (toggleMonthlyDetail → getDailyData), with a small client-side composition renderer mirroring the server one. Co-Authored-By: Claude <noreply@anthropic.com> * fix(i18n): finish wiring pt-BR (settings dropdown + READMEs + CLAUDE.md) pt-BR shipped in 2.1.1 (#48) with the strings, package.json enum and SupportedLanguage type, but was missing from the dashboard language dropdown (settings.ts enumValues) and every README's UI-language list — so users couldn't actually pick it. Added it in all those places and noted it in CLAUDE.md (which also flags that de-DE strings exist but are exposed nowhere). Co-Authored-By: Claude <noreply@anthropic.com> * feat(heatmap): GitHub-style contribution-graph SVG core (Claude orange) Pure, unit-tested heatmapSvg module: lays a calendar year of daily usage (Jan 1 → today, no future padding, GitHub current-year style) into a contribution grid and renders a self-contained SVG — Claude-orange intensity ramp, title row (no month-label overlap), month + weekday labels, Less/More legend, a bottom-left source watermark, per-day tooltips — for a GitHub profile README. Not wired to a command yet; a real-data preview was generated. Co-Authored-By: Claude <noreply@anthropic.com> * feat(heatmap): GitHub-style contribution-graph SVG core (Claude orange) Pure, unit-tested heatmapSvg module for a GitHub profile README: trailing ~year ending today (GitHub's default view, no future padding), Claude-orange intensity ramp, a top-left summary ("5.3B tokens in Claude Code · 2026"), GitHub-style per-day tooltips ("1.2M tokens on June 18th"), month + weekday labels, Less/More legend, and a bottom-left source watermark. Not wired to a command yet. Co-Authored-By: Claude <noreply@anthropic.com> * feat(heatmap): Export Token Heatmap command (GitHub-style SVG) Adds "Claude Code Usage: Export Token Heatmap (GitHub style SVG)" — builds a per-day usage map (timezone-aware, via getDailyUsageMap) and writes the contribution-graph SVG to a chosen file, offering to copy the Markdown embed snippet for a GitHub profile README. Command-palette accessible. Co-Authored-By: Claude <noreply@anthropic.com> * feat(heatmap): larger fonts (title 15 / months 12 / labels 11), 12px cells * feat(dashboard): opt-in token heatmap on the All tab (showHeatmap, default off) Adds a showHeatmap setting (default off). When on, the All tab shows a GitHub-style yearly token heatmap at the top, built from allRecords via getDailyUsageMap + renderHeatmapSvg. Inline SVG, so per-day hover tooltips work in the dashboard (unlike GitHub's image-sanitised READMEs). Mainly a shareable re-view of existing data, hence opt-in. Co-Authored-By: Claude <noreply@anthropic.com> * fix(stats): exclude the compaction "session continued" summary from Messages Verifying the Messages count on real logs surfaced ~5 counted "prompts" that were actually Claude Code's auto-injected "This session is being continued from a previous conversation…" compaction summary (a user-role message the user never typed). isSyntheticUserText now drops it, alongside interrupted-request and slash-command echoes. (Tool-result user messages were already excluded.) Co-Authored-By: Claude <noreply@anthropic.com> * fix: timezone is a validated dropdown, not free text (#51) The reporter suggested a dropdown of valid zones — the right fix, since it makes an invalid value impossible to enter. The Timezone setting is now an enum populated from Intl.supportedValuesOf('timeZone') ('' = system default). Kept a validation guard in I18n.setTimezone as belt-and-suspenders for old synced configs that already hold a bad hand-typed value (which otherwise made Intl throw and crashed the dashboard). Also the explicit claude-sonnet-5 pricing entry. Unit-tested. Co-Authored-By: Claude <noreply@anthropic.com> * feat: one-time "what's new" prompt after an upgrade (feature discovery) Shows a single dismissible notification the first time a new major.minor runs, pointing at the dashboard — so users find new features, including the opt-in default-off ones they'd otherwise never notice. Keyed by major.minor in globalState (patch releases don't re-nag); skipped on fresh installs. Co-Authored-By: Claude <noreply@anthropic.com> * feat(content): opt-in top-10 costliest conversations (showEfficiency) New showEfficiency setting (default off — not everyone wants cost metrics). When on, the Content tab gains a "Top 10 costliest conversations" panel: rows ranked by cost, each expandable (native <details>, no JS) to its tokens, cache-hit rate, top model and project. Same toggle will gate the cost-per-message + cache-savings chips on Today/projects (next). Co-Authored-By: Claude <noreply@anthropic.com> * feat(content): efficiency chips — cost per message + realised cache savings Under the usage summary (Today / Month / All), gated by showEfficiency: an exact "cost per message" chip (totalCost ÷ messages) and a "cache savings" chip (cacheRead tokens × the per-model input−cacheRead price gap — what caching actually saved vs. full input price). Off by default. Co-Authored-By: Claude <noreply@anthropic.com> * feat(share-card): render usage share card as self-contained SVG + export command Batch 6 (V2.2 share card) — make the tested shareCard.ts core visible/exportable. - shareCardSvg.ts: renderShareCardSvg(ShareCardData) -> SVG. No DOM, no html-to-image dep, no CSP concerns. Landscape 1200x630 social-card layout: header, big token hero (falls back to cost/sessions), up to four stat tiles, a per-day rhythm strip, badge pill, and watermark. Only draws fields present in ShareCardData, so privacy stays enforced by the type's shape. - ClaudeDataLoader.buildShareInput(records, range): aggregate inputs for today / last 7 days / this month, timezone-bucketed to match the dashboard. Returns only aggregate numbers (no records, no identifiers). - extension.ts: "Export Usage Share Card (SVG)" command — pick range, save, and open for review. - 8 renderer tests (well-formed output, hero fallback, omitted sections, tiles/badge/rhythm, XSS-escaping, watermark toggle). 75/75 pass. Co-Authored-By: Claude <noreply@anthropic.com> * fix(efficiency): scope efficiency chips to the Today view Per spec the cost/message + cache-savings chips belong on Today (and per-project), not on the Month / All-time summaries. renderUsageData now takes an { efficiency } opt and only Today passes it, so the chips no longer leak onto the other two scopes. Still opt-in via showEfficiency. Co-Authored-By: Claude <noreply@anthropic.com> * feat(efficiency): add opt-in "Cost/msg" column to the projects table Completes the "Today + projects" efficiency surfacing. Projects is a table, so per-project cost-per-message reads better as a column than a summary chip. Gated by showEfficiency (off by default); shows "—" when a project has no counted messages. Non-sortable column — sortTable() reads data-sort-* row attributes, so inserting it doesn't disturb the other columns' sorting. Co-Authored-By: Claude <noreply@anthropic.com> * feat(v2.2): efficiency tokens/msg chip, curated tz dropdown, quota Xd Yh, session-delete opt-in, whats-new preview Batch of the smaller items from Carl's review: - Efficiency chips: add "Tokens / message"; show on Today AND Month AND All-time again (removed the Today-only scoping). Projects keeps its column. - Timezone: trimmed the ~400-zone dropdown to a curated common set, each labelled with its live (DST-aware) UTC offset, e.g. "(UTC+08:00) Shanghai". Settings enum renderer now keeps a stored-but-uncurated value selectable. - Quota tooltip: weekly reset countdown now "4d 12h" instead of "4.5d" (statusBar.formatCountdown); the compact status-bar form keeps "4.5d". - Session delete: gated behind new opt-in "enableSessionDelete" (off by default) — it touches local history files, so it shouldn't be on for everyone. Resume tooltip now explains what resume does (EN/zh-CN/zh-TW). - What's-new: extracted showWhatsNew(); added "Preview What's New" command so it's testable (a fresh F5 install otherwise just sets the baseline silently). Compile clean, 75/75 tests pass. Co-Authored-By: Claude <noreply@anthropic.com> * feat(content): rework "costliest" panel to top-10 single MESSAGES The old panel just re-ranked sessions (a dupe of the Sessions tab). Carl wants the Content tab's version to fit that tab's theme (AI advice / optimizer): rank individual assistant turns and let each expand to explain itself. - getCostliestMessages(records, limit): walks each session in timestamp order, attaches the preceding user prompt, and keeps the top-N billed turns by cost (bounded insertion). Each carries the full model id, the authoritative skill/plugin attribution, the 4 token types, cost, prompt, project. - Store a truncated _promptText (300 chars) on the synthetic user-prompt records so the panel can show what triggered an expensive turn. - Webview: renderCostliestMessages() — summary row = rank + prompt + cost; expands to the prompt (blockquote), model, skill, token breakdown, project. Precomputed in the extension and passed in (not re-walked per re-render). Verified on real data (8855 records): top-10 with prompts + full model ids (fable-5 / opus-4-8 / opus-4-7) attach correctly. Co-Authored-By: Claude <noreply@anthropic.com> * feat(share-card): discoverable in-dashboard preview + richer card (composition, QR, full model, peak) Addresses Carl's share-card review: - Discoverable: an always-visible "Share card" panel on the All tab with an inline, responsive preview + "Export as SVG…" button (the command alone was unfindable). Export now matches the preview (composition on). - More metrics: a token-composition stacked bar (the four billed token types) with a legend — off in DEFAULT_SECTIONS but on in the dashboard/export. - Bar scale: the daily-rhythm strip now has a baseline, a peak gridline and a "peak <N>" label so the bars carry a scale. - Full model name: prettyModelName() → "Opus 4.8" (not just "Opus"); the card shows the specific model. - Traffic (引流): a real QR to the upstream repo bottom-right, wrapped in a link + "★ Star us on GitHub". QR is a static embedded asset (generated once with `qrcode`, then uninstalled — zero runtime deps). See shareCardQr.ts. - Canvas grew to 1200×760 to fit the new blocks; layout verified overlap-free. +6 tests (QR link, full model, composition bar, peak label, prettyModelName). 80/80 pass, compile clean. Co-Authored-By: Claude <noreply@anthropic.com> * fix(content): show hidden thinking honestly + full scrollable prompt in top-10 Two fixes from Carl's review: #6 Thinking-share = 0 bug. Models that omit their raw reasoning (Fable 5; Opus 4.8 by default) log `"thinking":""` with the CoT encrypted in `signature`, so the text-length estimate read 0% even though thinking was on and billed. Now: detect a thinking block with empty text + a signature and flag the session (ThinkingShare.hiddenThinking). The Sessions "thinking share" column shows "hidden" (tooltip explains) instead of a misleading 0%; a partly-hidden session shows "N%*". i18n across all 7 languages. #1 Top-10 costliest messages: raise the stored prompt cap 300 → 4000 chars and render the prompt in a scrollable box (max-height 220px) so the whole triggering prompt is visible for cost analysis; giant pastes still capped + marked "(truncated)". Verified on real data: 33 measurable / 2 hidden / 14 genuine-zero sessions. Compile clean, 80/80 tests pass. Co-Authored-By: Claude <noreply@anthropic.com> * feat(settings): timezone dropdown covers all IANA zones (Common + All optgroups) #3: the curated list was nicer but not complete. Now enumValues holds every IANA zone (418) so any is valid, and the dropdown groups them with <optgroup>: "Common" (25 curated, offset-labelled) at the top for quick picking, then "All zones" (the remaining 395, alphabetical, also offset-labelled). Added an optional enumGroups to SettingDef; the webview enum renderer draws optgroups when present and still injects a previously-stored out-of-list value. Co-Authored-By: Claude <noreply@anthropic.com> * feat(share-card): configurable, default-off share panel (range/scope/metrics, on-demand) Overhaul from Carl's review (Q2): - Drop the QR (deleted shareCardQr.ts) — watermark now carries the repo URL. - Default OFF: new `enableShareCard` setting; the All-tab panel shows a discoverable pointer to Settings until enabled. - Config IN the plugin: range (this month / last 7d / last 12 months / today / last 12 specific calendar months) + scope (overall / a project / a session) + per-metric checkboxes. buildShareInput now takes range+scope; supports `month:YYYY-MM` and `year`, resolves a project/session filter, and returns dailyDates + a human range label ("June 2026"). - Generate-on-demand: nothing renders until "Generate preview" (no per-keystroke re-render). Export uses the same config (webview builds + saves). - More selectable sections incl. a new `messages` (message count); default ON = est. cost + top model + cache-hit (+ token hero, rhythm, badge). Sessions, messages, composition, project name are opt-in. - Token composition legend now shows the absolute amount as well as the %. - Rhythm strip labels the first & last date so the x-axis is meaningful. - Buttons reuse the plugin's .btn-secondary style. Canvas back to 1200×680. Verified on real data (month/year/specific-month, project & session scope). Compile clean, 83/83 tests pass. Co-Authored-By: Claude <noreply@anthropic.com> * feat(heatmap): publish token heatmap to GitHub via built-in auth (Q4) One-click "Publish Token Heatmap to GitHub" — uses VS Code's GitHub authentication (getSession 'github' ['repo'], no PAT handling), then creates or updates the heatmap SVG in a repo via the Contents API. Defaults to the user's profile repo (owner/name = username) so it renders on their GitHub home page; remembers the last repo/path. Buttons added to the heatmap panel (Export / Publish to GitHub) + a command-palette entry. Minimal https helper (the codebase avoids fetch). Supersedes the spec's earlier "no GitHub in V2.2" note — Carl approved it for V2.2. Co-Authored-By: Claude <noreply@anthropic.com> * feat(ci): mention-driven @claude agent (Track 2 A1) — inert until owner enables First piece of the V2.2 AI automation. A maintainer can summon Claude with "@claude ..." in an issue / comment / PR review; it reads the repo (CLAUDE.md, ARCHITECTURE.md, CONTRIBUTING.md) and can reply or open a PR. SAFE BY DEFAULT — the job's `if:` requires BOTH a repo variable AUTOMATION_ENABLED='true' (kill switch) AND the ANTHROPIC_API_KEY secret, and only fires for OWNER/MEMBER/COLLABORATOR authors. With neither set it never runs and spends no budget. `anthropics/claude-code-action@v1`, --max-turns 12, timeout 15m, concurrency-guarded. A2 (triage draft-queue) and A3 (PR first-pass review) — the auto-posting pieces — are the next, separately-reviewed step; they need the owner to add the ANTHROPIC_API_KEY secret first. See docs/V2.2-SPEC.md Track 2. Co-Authored-By: Claude <noreply@anthropic.com> * feat(settings): timezone dropdown = full UTC-offset coverage (not every city) Carl clarified Q3: it's the UTC *offsets* that must be complete, not every location. Replaced the 418-city "All zones" group with a "UTC offset" group covering every offset (-12:00 … +14:00, incl. the real :30/:45 ones). Whole hours use fixed-offset Etc/GMT zones (no DST); fractional offsets use a representative zone with a region hint. "Common" named zones stay at the top. Co-Authored-By: Claude <noreply@anthropic.com> * feat(content): quantify cache-miss in top-10 costliest messages (Q7) Seeing the prompt alone didn't explain the cost. Each costly message now carries its cost split by token type (cache write / read / output / uncached input), and the panel shows the main cost driver (e.g. "cache writes (miss) — 96% of cost"), the cache-hit rate, and the full split. On real data the top turns are ~0-2% cache-hit and cache-write-dominated — i.e. the context was re-sent uncached (a cache miss), not that the answer was long. Now distinguishable at a glance. Co-Authored-By: Claude <noreply@anthropic.com> * feat(share-card): portrait + size presets, avatar, hourly today, tidy panel (Q1/Q2/Q3/Q5/Q6 review) Share-card round 2 from Carl's review: - Layout rewritten to flow vertically and distribute spare height as even gaps, so the same content reads well at every size. Size presets: Landscape 1200×680, Square 1080×1080, Portrait 1080×1350, Story 1080×1920 (portrait for phones). Watermark pinned to the bottom with a clear gap — fixes the date/name overlap. - Rhythm bars are centred + width-capped, so short ranges don't look sparse; "today" now uses HOURLY buckets (24 bars, 00:00→23:00) instead of one bar. - Avatar: opt-in "GitHub avatar" fetches the signed-in user's avatar (read:user, on demand, cached) and embeds it (circular, top-right, replacing the badge). Renderer supports opts.size + opts.avatarDataUri. - Config panel beautified to the plugin's card language (bordered card, field grid, chip checkboxes, checkerboard preview frame) + a Size select; the range dropdown is grouped (Rolling / Specific month) with a "(last month)" marker. - Heatmap→GitHub publish now shows a consent modal first (it's an auth+write action, unlike a local export). +4 tests (size presets, avatar embed). 85/85 pass, compile clean. Co-Authored-By: Claude <noreply@anthropic.com> * feat(settings): group opt-in toggles under "Optional features" (Q3) Reorganized the settings for user habit: the four opt-in dashboard toggles (token heatmap, efficiency insights, share card, delete-session action) move out of the General grab-bag into a dedicated "Optional features" group, so General holds just locale/format/behaviour. New SettingGroup 'features' + i18n label across all 7 languages. (Share-card range dropdown grouping / "last month" shipped in af3509e.) Co-Authored-By: Claude <noreply@anthropic.com> * feat(ci): let the @claude agent use a third-party Anthropic-compatible endpoint (Q8) A Claude subscription can't drive CI (Actions needs a key). For maintainers without an official Anthropic key, the mention agent now honours a repo variable ANTHROPIC_BASE_URL (env on the action step) so it can point at any Anthropic-format-compatible provider, with that provider's key in the ANTHROPIC_API_KEY secret. Unset = official API. Still inert until enabled. Clarified that repo guidance is read from CLAUDE.md/ARCHITECTURE.md (no full-repo re-read each run). Co-Authored-By: Claude <noreply@anthropic.com> * fix(quota): don't fabricate a 5h reset countdown while idle after a reset (#3) The 5h / weekly windows are usage-anchored: the next window (and its reset) only starts when you send your next message. When a window's resets_at passed while idle, liveWindows() rolled it forward ("reset = old + 5h") and showed a countdown that was wrong until the next real fetch — exactly the "reset already ticking before I sent anything, then jumps when I do" bug. Now a recently-expired window shows 0% with NO countdown (resets_at cleared → "—"); the real reset lands on next use. (Long-expired still drops; unparseable still kept.) Co-Authored-By: Claude <noreply@anthropic.com> * feat(share-card+content): round-4 review — landscape-only, last-30 default, full numbers, >4 fix, avatar/refresh fixes, top-10 privacy toggle, neutral tz labels Share card: - Size selector removed — landscape 1200×680 only for now (other sizes need per-size tuning; deferred to a patch). Aesthetic redesign is coming separately. - Default range is now the last 30 days (added `last30`), not "this month". - New "Full numbers" toggle shows the exact token count in the hero (font auto-shrinks so it fits). - Fixed the >4-metrics bug: tiles allow up to 6 and wrap to a second row. - Rhythm first/last date labels now sit UNDER their bars (centred), not pinned to the axis ends — correct for short, centred ranges. - Avatar fix: httpsGet now follows redirects (GitHub avatars 302 to a CDN — not following them was why the avatar came back blank); + xlink:href for export. - Refresh no longer wipes the card: the last generated SVG + the config (range/ scope/metrics/toggles) are remembered and rehydrated on re-render. (Same class of issue Carl hit with the optimizer output.) Content: the "top 10 costliest messages" panel is now its OWN opt-in (`showCostliestMessages`, off) — separate from the efficiency chips — because it displays your prompt text (privacy). Timezone: labels use canonical IANA identifiers (official/apolitical) — dropped the editorialised country groupings; the UTC-offset group shows only the offset. 85/85 tests pass, compile clean. Co-Authored-By: Claude <noreply@anthropic.com> * feat(content+webview): top-10 input-interval metric, keep expanded rows on refresh, fix avatar CSP #1 Top-10: each costly turn now shows "Time since last turn" (gap to the previous billable turn in the same session; "New conversation" for the first), and both Cache-hit rate and the gap are bold. A gap past the ~5-min cache TTL is flagged. On real data the priciest turns pair 0-2% cache-hit with multi-hour gaps — i.e. the cache went cold while idle, then had to be rewritten. Now obvious at a glance. #2 Auto-refresh no longer collapses expanded <details data-persist> rows — open state is saved (vscode.setState) and restored across re-renders, so a data refresh doesn't interrupt reading. Switching tabs resets to default (as asked). #7 Avatar root cause: the webview CSP had no img-src, so it inherited default-src 'none' and blocked ALL images incl. data: URIs — the avatar <image> never rendered even once fetched. Added `img-src data:;` (data URIs only, still no external hosts). Combined with the earlier redirect-follow fix, the avatar should now show. 85/85 tests pass, compile clean. Co-Authored-By: Claude <noreply@anthropic.com> * feat(ci): AI auto first-pass on every issue + PR; rename mention handle to @ccu-bot (Q4) Carl authorized auto-replies and added the API key (repo secret, third-party 1M-context, ANTHROPIC_BASE_URL-capable). Two new workflows: - issue-first-pass.yml (issues: opened): posts ONE first-pass comment. - pr-first-pass.yml (pull_request_target: opened): posts ONE first-pass review; base-repo checkout only, reads the diff via API — never runs PR code. Both: labelled "🤖 Automated first-pass reply (via Claude Code)", end with a model-generated / a-maintainer-follows-up disclaimer, read ARCHITECTURE.md/ CLAUDE.md/CONTRIBUTING.md and — if those are insufficient — the repo files (never guess), reply in the author's language. Skip bot authors; concurrency + timeout + max-turns. Inert until AUTOMATION_ENABLED='true'; third-party endpoint via ANTHROPIC_BASE_URL. Renamed the mention trigger @claude → @ccu-bot (the model may be third-party, so "@claude" was misleading). A2 draft-queue stays deferred until this runs stably. Co-Authored-By: Claude <noreply@anthropic.com> * feat(share-card): avatar + badge + GitHub name together (personalized corner) Carl: avatar and badge shouldn't be mutually exclusive, and a name adds personality. The top-right corner now shows the avatar circle with a two-line name / badge block to its left — any subset renders. New opt-in "GitHub name" checkbox; getGithubIdentity() fetches avatar + display name in one call (cached), so the name and avatar share the single read:user sign-in. Config (avatar/name) is persisted across refresh like the rest. Co-Authored-By: Claude <noreply@anthropic.com> * feat(content): detect model-switch cache flush + estimate the cache TTL (Q1) Model switch: top-10 messages now carry the previous turn's model; when the hit rate is low the panel names the likely cause — "model switch (Opus→Sonnet) flushed the cache" vs "idle Xh — cache went cold". Distinguishes the two cache-miss reasons Carl noticed (a 17m/1h25m gap with a low hit rate is usually a switch, not idle expiry). Cache-TTL estimate (Carl approved for V2.2): estimateCacheTtl() buckets same-model consecutive turns by idle gap and finds where the hit rate drops. On real data the hit rate holds ~100% to ~60 min then falls off — i.e. the warm window is ~1 hour here, NOT the assumed 5 min. Surfaced as a "Cache warmth" insight on the Content tab (opt-in via showEfficiency; aggregate only, no prompts): "cache stays warm ~60 min; batch complex work within that window." 86/86 tests pass, compile clean. Co-Authored-By: Claude <noreply@anthropic.com> * feat: @ccu-bot contributor note, data-contribution scaffold; graceful arch-doc fallback - CONTRIBUTING.md: section explaining @ccu-bot (labelled + disclaimed, comment-only, ask-not-guess, off unless the maintainer enables it). (Q9) - src/dataContribution.ts: opt-in data-contribution SCAFFOLD for future cross-user features (cache-TTL by tier/task, etc.). OFF by default, no endpoint, no-op transport; Observation type has no identifying field; tier redacted unless shared. +4 tests. (Q2) - Automation: ARCHITECTURE.md is gitignored (a private local context pack), so CI can't read it — the first-pass prompts now say "ARCHITECTURE.md if present" and fall back to CLAUDE.md + reading src/. Whether to publish a scrubbed technical ARCHITECTURE.md is Carl's call (see docs/ARCHITECTURE-private.md). (Q6) 90/90 tests pass, compile clean. Co-Authored-By: Claude <noreply@anthropic.com> * feat(share-card): implement GPT "Code Pulse · Aurora Console" redesign with dual themes Executes docs/V2.2-ShareCard-GPT-design.md. One renderer, theme tokens: - Two themes: **Claude Cream** (default — warm/editorial) and **Aurora Dark** (technical/social). SHARE_CARD_THEMES token set drives bg, panels, text, accents, composition/rhythm colors, badge. Theme option auto|claudeCream| auroraDark; "auto" follows the VS Code theme (resolveShareCardTheme). - New composition: aurora gradient bg + soft blobs, brand block ("Claude Code Usage" / "AI coding usage snapshot" / range), glass stat tiles with an accent rail, token-mix bar (semantic colors), daily-pulse strip (peak highlighted), brand-signature watermark. No longer looks like a dashboard screenshot. - Badge redesigned as a sticker card with on-brand copy + a one-line explanation (BADGE_COPY: Cache Alchemist "缓存命中高,token 没白烧" etc.), sharing the corner with the avatar + GitHub name. - Defaults follow the design: hero + 4 tiles (cost/cache/model/sessions) + token mix + daily pulse + badge; messages/project/workflow/peak opt-in. - Theme selector added to the config panel (default Claude Cream), persisted. Palette export command + webview both theme-aware (isDark for "auto"). - Landscape 1200×680 only (square/portrait deferred, per the brief). Privacy/self-contained constraints unchanged. Tests updated for the new look + theme parsing/default/auto/colors/hidden-fields. 89/89 pass. Co-Authored-By: Claude <noreply@anthropic.com> * docs: publish the technical ARCHITECTURE.md (Q6 — Carl chose to publish) Un-ignored ARCHITECTURE.md + ARCHITECTURE-zh-CN.md so they're public: they're already technical-only (module map, data flow, exact token/cost computation, log-format facts, invariants, refresh, release), so nothing needed scrubbing. Now the @ccu-bot first-pass automation and contributors get a real technical map instead of re-reading src/. Maintainer product-direction stays private in docs/ARCHITECTURE-private.md; the public file must remain technical-only. Co-Authored-By: Claude <noreply@anthropic.com> * feat(share-card): add Claude Classic (orange) theme + adaptive layout fixes - New "Claude Classic (orange)" theme — the pre-redesign single-hue orange (monochrome orange ramp for the token mix, no violet/mint), for Carl to compare against the busier Claude Cream. Aurora Dark unchanged. - Layout: content moved up (contentTop 134); the hero now sits at a fixed top-left position and the full-width sections flow BELOW whichever of the hero / top-right corner reaches lower — so enabling the GitHub avatar no longer pushes the whole card down and off the canvas (the reported overflow). Verified: with avatar, max content y=592, watermark at 652, all within 680. - Token mix gets internal padding (h 64→84) so it no longer crowds the tiles above / pulse below; inter-section gaps clamped to [14,38] (not hollow, not cramped). Adapts to whichever sections are enabled. 89/89 tests pass, compile clean. Theme selector now offers Cream / Classic / Aurora Dark / Auto. Co-Authored-By: Claude <noreply@anthropic.com> * docs(architecture): bring the public technical map current (V2.2) - Fixed stale facts: 6→7 languages (added pt-BR); the "~5-min cache TTL" note corrected to the MEASURED ~60 min (estimateCacheTtl infers it from the user's own turns). - Added a "V2.2 additions" table: shareCard/shareCardSvg (dual themes), heatmap/heatmapSvg + GitHub publish, dateKeys, promptDedup, quotaFormat, dataContribution; new dataLoader methods (getCostliestMessages, estimateCacheTtl, buildShareInput); efficiency/cache insights; thinking-hidden; UTC-offset timezone; and the @ccu-bot first-pass CI (inert unless enabled). Keeps the file technical-only; product direction stays in the private companion. The @ccu-bot automation now reads an up-to-date map. Co-Authored-By: Claude <noreply@anthropic.com> * feat(share-card): default to Claude Classic (orange) theme Carl's pick — Claude Classic is now the default (dropdown first, panel/export/ handler defaults, and resolve's light fallback); Cream + Aurora Dark + Auto stay available. Tests updated. Co-Authored-By: Claude <noreply@anthropic.com> * chore(v2.2): settings i18n for new toggles (7 langs), resume tooltip de/ja/ko/pt, what's-new + CHANGELOG Pre-release polish so V2.2 is release-ready: - SETTINGS_I18N: added the 5 new opt-in toggles (showHeatmap, showEfficiency, showCostliestMessages, enableShareCard, enableSessionDelete) in de-DE / zh-TW / zh-CN / ja / ko / pt-BR, and refreshed the timezone help (now UTC-offset). - Resume-session tooltip: the fuller "reopens in Claude Code / claude --resume" explanation now in de/ja/ko/pt too (en/zh already done). - What's-new toast copy updated for the real V2.2 (share card, GitHub heatmap publish, efficiency + cache insights). - CHANGELOG [2.2.0]: added share card (themes), heatmap publish, top-10 costliest MESSAGES (cost split / cache-miss / interval / model-switch), cache warmth, efficiency chips, session-delete opt-in, UTC-offset timezone, and the thinking-hidden / quota-format / refresh-persist / avatar-CSP fixes. 89/89 tests pass, compile clean. Co-Authored-By: Claude <noreply@anthropic.com> * feat(share-card): localize the card to the UI language (no en/zh mixing) Carl: the card was English-only, and the badge line was Chinese even on an English card. Now the whole card (subtitle, hero unit, tile labels, token-mix legend, pulse, watermark, and the badge title + line) renders in the UI language. Fully localized for en / zh-CN / zh-TW (the maintainer's languages); any other UI language falls back to English — so a card never mixes languages. Card strings + per-language BADGE_COPY live in shareCardSvg.ts (self-contained + tested); the webview/export pass I18n.getLocale(). Badge line width widened so English lines aren't clipped. 90/90 tests pass. de/ja/ko/pt card localization = a follow-up (they fall back to en for now, consistently). Co-Authored-By: Claude <noreply@anthropic.com> * fix(settings): heatmap toggle references the actual "All" tab name per language Carl: the token-heatmap toggle said "全部标签" (unclear), not the real tab name. Now the parenthetical matches each language's All-time tab label — zh-CN "("所有"选项卡)", zh-TW "(「所有」分頁)", ja "(「すべて」タブ)", de/pt use their tab labels; en "(All-time tab)". Co-Authored-By: Claude <noreply@anthropic.com> * feat(share-card): Chinese cards use 万/亿 units + hero keeps one decimal Carl: on a zh-CN card the token unit should read in 万 / 亿 (万 / 億 for zh-TW), not M/K — and the hero total should always show one decimal (e.g. "5.1亿", "5.0B"). compact() is now language-aware (万/亿 for Chinese) with a keepDecimal flag the hero uses; tiles, composition amounts and the pulse peak follow the card language too. Co-Authored-By: Claude <noreply@anthropic.com> * feat(analysis): cache-churn cost data layer (缓存损耗账单) — first V2.2 analysis estimateCacheChurnCost(): the $ spent RE-writing cache that a warm cache would have served cheaply, split by the two AVOIDABLE causes — a model switch (per- model cache flush) and an idle gap past the TTL (~60 min). Only counts a rewrite of a cache that existed on the previous same-session turn (read≈0, write>0), so a genuine big-new-context isn't blamed. Waste ≈ cacheCreation × (writeRate − readRate) per model, via getModelRatesPerMillion. IMPORTANT framing (per Carl): a switch can still be net-worth-it if the other model is cheaper per token — this is the churn cost, not "never switch". On real data (30d): ~$240, mostly from idle gaps (71×) not switches (5×) — so momentum within the warm window matters more than avoiding switches here. Data layer + 2 tests now; the styled Content-tab section + model-fit (C5) land next (with frontend-design). 93/93 pass. Co-Authored-By: Claude <noreply@anthropic.com> * fix(ci): add id-token: write to first-pass workflows (sync #58 to V2.2 branch) * feat(content): "Experimental insights" section — cache-churn bill (first card) The V2.2 analysis workstream, styled to the plugin's card language (theme vars, consistent with the costly-messages / cache-warmth panels) with a small "estimate" tag as the honest signature. - New `showInsights` setting (Optional features, default OFF) — these are our own heuristics, not standardized metrics, so opt-in + labelled. - Content tab gains an "Experimental insights" section. First card: the **cache-churn bill** — headline $ (last 30 days) spent re-writing cache a warm cache would have served cheaply, split into idle-gap vs model-switch pills, with the honest note that a switch can still be worth it (this is only the churn cost). Built on estimateCacheChurnCost; aggregate only, no prompts. C5 model right-sizing (next card) + the other insights come next. showInsights settings i18n (7 langs) folded into the pre-release i18n pass. 93/93 tests pass. Co-Authored-By: Claude <noreply@anthropic.com> * polish(content): insight card — separate the caveat onto its own line + crisper copy The cache-churn card read as a wall of text. Split it: a "💡" actionable takeaway line (recoverable — keep momentum in the warm window), then the "a switch can still be worth it" caveat as a smaller, top-bordered note on its own line. Pills clarified ($ · cause · count). Reads as an insight now, not a paragraph. Co-Authored-By: Claude <noreply@anthropic.com> * chore(ci): sync controlled-API first-pass bot to V2.2 branch (matches #64) * feat(content): cache-hit-rate-by-model insight + weekly throttle for cache analyses Second insight card (Carl's top pick — cache is the key cost lever): cache hit rate per model, with the model family's nominal provider and a volume bar. cacheStatsByModel() aggregates read ÷ input-side per model over 30 days. Real data shows opus-4-8 97% / deepseek-v4-pro 98% / fable 96% etc. — so you can see which models actually serve from cache. Tip: favour low-hit models for short, self-contained tasks. (True cross-PROVIDER comparison needs pooled cross-user data — reserved via the dataContribution scaffold.) Also: the cache analyses (TTL / churn / by-model) now recompute at most WEEKLY (analysisCache), not every render — Carl's call; they move slowly. 93/93 tests pass. Co-Authored-By: Claude <noreply@anthropic.com> * feat(content): cache card shows per-model warm window (TTL), not just hit rate Carl's call: the durable, cross-user-comparable signal is how long each model's cache actually stays warm — and it drifts over time — so make TTL the primary metric. cacheStatsByModel() now also buckets same-model idle gaps per model and infers a warm window; hit rate + volume ride along as secondary. Real data is telling: DeepSeek-v4-pro stays warm ~240 min, Opus 4.8 ~60 min, Fable 5 only ~2 min (bursty, rarely resumed after a gap). That per-model spread is exactly what you can't see from a single global number. Also fixes the bar/number ambiguity Carl flagged: the bar AND the bold number are now the same metric (TTL); "—" shown when a model has too few gaps to estimate. Hit% + token volume moved to a muted sub-line. 93/93 tests pass. Co-Authored-By: Claude <noreply@anthropic.com> * feat(bot): two-tier first-pass with controlled repo-file escalation + skimmable template When the cheap model (CCU_BOT_MODEL, default deepseek-v4-flash) says it can't answer from the docs alone, escalate to a stronger model (CCU_BOT_MODEL_PRO, default deepseek-v4-pro) with thinking on, and let it NAME a few repo source files. The SCRIPT reads them — repo-relative text files only, no "..", size- capped, must resolve inside the repo root — so there's still no agent tool and no way for a malicious issue to make it read or run anything arbitrary. If it still can't answer, its reply asks for the specific missing info/logs and suggests the issue template. Also gives every reply a skim-friendly shape: TL;DR/结论 first, then analysis, then suggested next steps. Thinking param is best-effort (retries without it if the endpoint rejects it), so it stays portable across third-party endpoints. Co-Authored-By: Claude <noreply@anthropic.com> * feat(sessions): read-only conversation viewer (opt-in) — re-read a session without spending context Carl's use case: after /clear or /compact he wants to RE-READ his prompts and the model's answers to jog his memory, but resume (`claude --resume`) reloads the whole session INTO context and costs tokens. This viewer is the zero-context path — it just reads the session .jsonl and shows it. - conversationLog.ts: pure, tested parser (.jsonl text -> ordered display turns). Keeps prompts + assistant text front and centre; carries thinking / tool_use / tool_result too. Skips meta + sidechain lines, dedupes by uuid, truncates per turn, supports "last N turns". 7 unit tests. - conversationViewerHtml.ts: frontend design per Carl's ask — user PROMPTS are the star (accent rail + tint + a top "jump to your prompts" nav), model TEXT is a readable card, thinking / tool traffic is present but quiet (collapsed <details>, muted). No scripts (webview enableScripts:false); VS Code theme vars only; a read-only badge makes the zero-context promise explicit. - webview.ts: an eye view action on Sessions rows (gated by the new setting) -> viewConversation message -> openConversationViewer() reads the file and opens a panel. settings.ts: showConversationViewer (features, default OFF). - i18n: viewConversation button tooltip in all 7 languages. (Viewer panel chrome is English-first like the other experimental surfaces; folded into the pre-release i18n pass.) 100/100 tests pass. Co-Authored-By: Claude <noreply@anthropic.com> * feat(viewer): chat-mode conversation viewer — rendered Markdown, filled answers, default-hidden tools Round of viewer feedback from Carl: - Model answers now render Markdown (new dependency-free miniMarkdown.ts, 8 tests) instead of showing raw source — much better to read inside the panel. - Assistant answers get their OWN filled card (was plain), so they stand apart from both prompts (accent) and the muted tool traffic. - Default is a clean CHAT view: thinking and tool activity are HIDDEN behind two pure-CSS toggles (checkbox hack, no scripts) — on open you see only prompts + answers. - Opens on the last ~10 rounds; earlier rounds collapse into a <details>. - Prompt nav items are now expandable to the full prompt text, each with a "jump ↓" link. Co-Authored-By: Claude <noreply@anthropic.com> * feat(sessions): add "Active" column — idle-capped hands-on time The Duration column is the first→last wall-clock span, which is misleading for long-lived sessions (one real session spans 957h but only ~12.5h was hands-on). The new Active column sums the gaps between turns with each idle gap capped at 5 min (activeDurationBySession), so it reflects time actually spent. Sortable, with a tooltip explaining the estimate; labelled in all 7 languages. Co-Authored-By: Claude <noreply@anthropic.com> * fix(ci): don't auto-review maintainer PRs; treat an empty model reply as a no-op Two first-pass bot fixes (also going to main via PR): - The PR first-pass now SKIPS pull requests authored by OWNER / MEMBER / COLLABORATOR. A maintainer's own finished PR doesn't need an auto first-pass (that review is for external contributors) — and #66 showed it firing on our own PR and failing on the big self-referential diff. - An empty model reply is now a soft no-op (exit 0) instead of a hard failure, so a benign blank response doesn't leave a red ✗ check. Co-Authored-By: Claude <noreply@anthropic.com> * feat(viewer): interim-answer toggle, Markdown tables, visible jump + latest marker More viewer feedback from Carl: - Assistant answers that aren't the round's FINAL output are marked "interim" and can be toggled off (default ON — intermediate results have value); the final answer of each round always stays. - Markdown tables now render (added GFM table support to miniMarkdown + 2 tests). - Prompt-nav jump link no longer clipped on long prompts — it sits outside the truncating text (flex), always visible; the caret still expands full text. - The most recent prompt gets a "latest" badge in the nav for quick jumping. Co-Authored-By: Claude <noreply@anthropic.com> * feat(sessions,insights): active-time tuning, merge session mutating actions, C5 right-sizing One round of Carl's feedback on sessions + insights: - Active column: idle-gap cap raised 5m → 90m (reading/reviewing a diff is real work the log doesn't capture; the old cap badly under-counted). Column moved BEFORE Duration; header + cells carry the estimate tooltip. Help text updated in all 7 languages. - Resume + Delete now share ONE opt-in (enableSessionActions, default off): both ACT on the user's Claude Code (reopen / trash a log), at odds with the extension being read-only, so they hide together. The read-only conversation viewer flips to default ON (showConversationViewer) — it only reads local logs. - C5 "Premium models on lightweight turns": the compute premium spent running a top-tier model (output ≥ $20/M) on small-output turns (≤800 out — the translation / quick-edit signature), vs Haiku-4.5 rates. Surfaces a per-switch churn cost so it says "batch simple tasks on the cheap model", not "flip every turn". Real data: ~$19.74 over 2339 turns, mostly Opus. Weekly-cached. 110/110 tests pass. Co-Authored-By: Claude <noreply@anthropic.com> * refactor(insights,viewer): defer C5 card; cap prompt-nav to the last 10 - C5 "premium models on lightweight turns" is no longer rendered. Output size is a weak proxy for task difficulty, and the advice ("use a cheaper model for simple tasks") isn't something users lack — in practice they stay on the top model and tack simple work onto long tasks. A worthwhile version needs a real difficulty judgement (sampling prompt text via the opt-in AI-advice API, which must be clearly labelled). modelRightsizing() stays as dormant scaffolding. - Viewer prompt-nav is capped to the last 10 prompts (matching the recent rounds shown) so the list is a consistent length and every jump lands in expanded content; a note flags any earlier prompts folded into "Show earlier". 110/110 tests pass. Co-Authored-By: Claude <noreply@anthropic.com> * fix(viewer): cap by rounds, not raw turns — long sessions now show 10+ prompts Bug (Carl): a long session with 79 prompts showed only the latest ~3. Cause: the viewer capped at the last 500 RAW turns, but one prompt is followed by dozens of tool turns, so 500 turns held only ~4 prompts. Fix: parseConversation now caps by ROUNDS (a round = a prompt + its replies) via maxRounds, with a raw-turn ceiling as a safety net. The viewer keeps the last 15 rounds (10 shown + a small "earlier" buffer). Parser also reports totalRounds; the header now reads "N prompts total · … · showing last 15 of N rounds". Verified on the real 79-prompt / 6798-turn session: nav shows the last 10 prompts, latest badge on the newest, ~0.6 MB html. 111/111 tests pass. Co-Authored-By: Claude <noreply@anthropic.com> * feat(insights): "Big one-shot turns" card — nudge toward more checkpoints Next insights-roadmap card (session-health), implementing the checkpoint habit Carl wanted surfaced: sessionHealth() measures how much of your output came from a few very large single responses (≥4k output tokens — a big one-shot generation with no human checkpoint mid-way). Real data: ~39% of output over 321 jumbo turns (largest ≈50k). The card frames it as a gentle nudge (smaller checkpoints = cheaper/faster review, catch a wrong direction early), not a verdict — a big single turn is sometimes exactly right. Weekly-cached, opt-in (showInsights). 111/111 tests pass. Co-Authored-By: Claude <noreply@anthropic.com> * feat: viewer refreshes one reusable panel; new "Your active hours" insight Two items from Carl's feedback: - Conversation viewer now (re)uses a SINGLE panel: each "view" click re-reads the session .jsonl fresh and refreshes that panel (revealing it), instead of piling up a new tab per click. An ongoing session shows its latest turns. - New insights card "Your active hours": activeHours() buckets non-cache work (input+output tokens) by hour-of-day in the user's timezone, with a 24-hour sparkline and the busiest contiguous 4-hour window + its share. Real data: peak 15:00–19:00, 35% of work. Tip ties it to protecting the peak window and keeping momentum in one warm session there. Weekly-cached, opt-in. 111/111 tests pass. Co-Authored-By: Claude <noreply@anthropic.com> * feat(insights): "Skill ROI" card — cost vs output per skill/plugin Next roadmap card. skillRoi() uses the authoritative _skill/_plugin attribution Claude Code ≥2.1 stamps on usage lines: per skill, the cost spent while it was active and the output it returned, with outPerUsd (output tokens per $) as the ROI angle. Real data separates lean skills (lit-review ~15k tok/$, paper-plotting ~13.5k) from context-heavy ones (claude-api ~1.9k, code-review ~2.1k) — so you can see which skills burn context for little output. Bar/number = cost; note flags it's a proxy (cost attributed to the active skill, output isn't "value"). Weekly-cached, opt-in. (Budget-pacing card skipped for V2.2 per Carl.) 111/111 tests pass. Co-Authored-By: Claude <noreply@anthropic.com> * fix(insights): Skill ROI shows output-tokens-per-$ as the bar AND number Carl: the right-hand figure was a bare dollar amount, unclear as "ROI" and not matching the bar. Now the bar and the number are the SAME metric — output tokens returned per $ (with a "/$" unit) — sorted by it so the bars rank; cost + turns move to the muted sub-line. * feat(settings): live-refresh delay dropdown (0/1/2/5/10/20/30s) + settings i18n sync - Replaces the fileWatching on/off toggle with a discrete "Live refresh delay" choice: Off / 1 / 2 / 5 / 10 / 20 / 30s (fileWatchSeconds, default 2s ≈ old 1.5s). The fs watcher uses it as the debounce; Off stops watching. Help makes clear this only re-reads LOCAL log files — no API call; the /usage quota fetch is throttled separately, so a short delay doesn't hammer the official API. - Settings i18n sync (Carl's release check): localized the setting label/help in all 6 non-English locales for fileWatchSeconds, plus showInsights and showConversationViewer (previously English-only), and renamed the enableSessionDelete labels to enableSessionActions to match the merged setting. 111/111 tests pass. Co-Authored-By: Claude <noreply@anthropic.com> * fix(viewer,charts): cache-write bars were invisible; viewer "show earlier" was confusing - Cache-write ("input cache miss") bars rendered transparent in every usage bar chart. Cause: .cache-creation-bar's gradient referenced var(--vscode-charts-pink), which VS Code doesn't define — an invalid color makes the whole gradient (and thus the background) invalid, so only the axis + value labels showed. Added a pink fallback so the gradient is valid. - Conversation viewer: dropped the "Show earlier conversation (N rounds)" collapse. It was confusing — the top prompt-nav didn't reflect it, so you couldn't tell whether the earlier prompts were shown, and toggling it only changed the body. Now every loaded round renders flat and the nav lists every loaded prompt (all jump targets exist). Default load is the last 10 rounds ("showing last 10 of N"). 111/111 tests pass. Co-Authored-By: Claude <noreply@anthropic.com> * docs(release): V2.2 CHANGELOG + What's New; drop unused @types/glob - CHANGELOG [2.2.0]: added the conversation viewer, Experimental insights suite (cache-churn / warmth-by-model / big one-shot / active hours / skill ROI), Sessions "Active" column, live-refresh delay control, Chinese share-card units, the enableSessionActions merge + timezone-aware bucketing (Changed), the cache-write invisible-bars fix (Fixed), and the @types/glob removal (Removed). - Refreshed the in-extension "What's New" 2.2 blurb to match. - Removed the unused @types/glob devDependency (folds in #63, thanks @zeyutang). Co-Authored-By: Claude <noreply@anthropic.com> * docs(architecture): add v2.2 modules + Experimental-insights methods ARCHITECTURE.md module table now lists sessionResume.ts, conversationLog.ts, conversationViewerHtml.ts and miniMarkdown.ts, and the data-layer notes cover the new insight methods (estimateCacheChurnCost, cacheStatsByModel, sessionHealth, activeHours, skillRoi, activeDurationBySession, modelRightsizing). Co-Authored-By: Claude <noreply@anthropic.com> * fix: timezone bucketing and cache-write cost accuracy (#62) * fix: timezone bucketing and cache-write cost accuracy * bucket Today/month/daily/hourly totals by the configured IANA zone * keep the loader zone in lockstep with display; recompute on change * split cache writes into 1h (2x) vs 5m (1.25x) when the log has the ttl * add tests for zone bucketing and the cache-write split * i18n: note timezone sets the daily cost boundary * add the day-boundary clause to the timezone help in all six locales * update the timezone description in README and the locale editions * fix: changelog minor touch-up * Maintainer edit: keep the cache-write TTL pricing; drop the timezone changes The timezone-bucketing half overlaps with the shared zone-aware day-key rework landing in V2.2, so this reduces the PR to just the cache-write TTL pricing (pricing.ts + types.ts + pricing.test.ts) to merge cleanly. zeyutang's original commits are preserved; the timezone work is welcome as a follow-up PR. * Remove stray sample-card SVGs accidentally swept into the maintainer edit --------- Co-authored-by: Carl <74130028+Carl723000@users.noreply.github.com> * docs(changelog): credit #62 cache-write TTL pricing (@zeyutang) * chore(release): bullet the 2.2 "What's New"; let #63 own the @types/glob removal - What's New for 2.2 is now newline bullets (• per feature) instead of one dense paragraph, so the toast is scannable. Copy unchanged (Carl approved it). - Restore @types/glob locally: #62-style, the removal now traces solely to zeyutang's merged #63 rather than a duplicate local edit. It's a devDependency (never packaged), so the vsix is unaffected; the merge to main drops it. --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Zeyu Tang <tang@zeyu.one>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The
timezonesetting previously affected only date formatting, so the cost aggregations bucketed "Today" by machine-local midnight while the daily/monthly charts keyed off UTC. The two disagreed with each other and neither matched the Anthropic console.This PR makes every Today / month / daily / hourly total derive its day boundary from the configured IANA zone (empty = system zone), kept in lockstep with the display and recomputed when the zone changes. It also prices cache-writes by TTL: a 1-hour write bills at 2× base input (vs the 5-minute 1.25×) when the log carries the
ephemeral_1h/5msplit, so the cost estimate tracks Anthropic's cache rules; logs without the split are unchanged.Before
After
Linked issues
None (standalone).
Type of change
fix(patch)feature(minor)breaking(major)docs/ci/chore(patch)How was this tested?
npm run compileclean;npm test30/30 pass, including new unit tests:cache-write splits 1h vs 5m and falls back without the split
(
src/test/pricing.test.ts); day/hour bucketing respects the configured zonewith DST-aware fixtures, and an invalid zone falls back without throwing
(
src/test/dataLoader.timezone.test.ts).vs $23.15 under the system (Pacific) zone, confirming the setting now moves the
day boundary; the all-time cache-write cost shifts by the expected ~1% (the
observed 1h-cache share). Changes are in pure aggregation/pricing logic, so
coverage is via unit tests + real-data recompute rather than the EDH.
Checklist
npm run compileis cleanI18nwith all six languages filledCHANGELOG.mdupdated (user-visible changes)README.mdupdated if behaviour/settings changedtimezonedefault""= system, unchanged for anyone who never set itIntlis built-in