From ef4762f59dc59f97761a6011341f014bde4dc56e Mon Sep 17 00:00:00 2001 From: Carl <74130028+Carl723000@users.noreply.github.com> Date: Sun, 12 Jul 2026 20:49:16 +0800 Subject: [PATCH 1/3] docs: date CHANGELOG, add What's new in 2.2, codify pre-release checklist - CHANGELOG: date [2.2.0] 2026-07-07 and [2.1.1] 2026-06-30 (both shipped while still marked "Unreleased") - README.md + README-zh-CN.md: add a "What's new in 2.2" section - CLAUDE.md: add a Pre-release checklist under Build & Release - PR template: correct "six" -> "seven" languages (pt-BR) Co-authored-by: Claude --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- CHANGELOG.md | 4 ++-- CLAUDE.md | 25 +++++++++++++++++++ README-zh-CN.md | 14 +++++++++++ README.md | 41 ++++++++++++++++++++++++++++++++ 5 files changed, 83 insertions(+), 3 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 5c00372f..4334f058 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -29,7 +29,7 @@ Keep the section headings; fill what applies, delete what doesn't. ## Checklist - [ ] `npm run compile` is clean -- [ ] User-facing strings go through `I18n` with **all six languages** filled +- [ ] User-facing strings go through `I18n` with **all seven languages** (en / de-DE / zh-TW / zh-CN / ja / ko / pt-BR) filled - [ ] `CHANGELOG.md` updated (user-visible changes) - [ ] `README.md` updated if behaviour/settings changed - [ ] New settings default to existing behaviour (opt-in) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f0ceacf..fbcf92eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this fork compared to upstream [`ClaudeCodeUsage/ClaudeCodeUsage`](https://github.com/ClaudeCodeUsage/ClaudeCodeUsage) (last upstream release: 1.0.8). Format follows [Keep a Changelog](https://keepachangelog.com). -## [2.2.0] — Unreleased +## [2.2.0] — 2026-07-07 ### Added - **`tokenDecimalPlaces`** (default 1, 0–2) — decimals for the *compact* token @@ -156,7 +156,7 @@ upstream release: 1.0.8). Format follows [Keep a Changelog](https://keepachangel has no `-4-` segment — fell through to the 200K legacy default. The dashboard and status-bar context bar now correctly show a 1M window for Sonnet 5. -## [2.1.1] — Unreleased +## [2.1.1] — 2026-06-30 ### Added - **Monthly cost in the status bar** — the `statusBarMetric` setting gains a diff --git a/CLAUDE.md b/CLAUDE.md index 6ffd4510..5f8fc3c0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -68,6 +68,31 @@ npx @vscode/vsce package # build a .vsix - Versioning follows semver and is driven by PR labels: `fix`/none → patch (`2.0.x`); `feature` → minor (`2.x.0`); `breaking` → major. +### Pre-release checklist + +Before a maintainer clicks **Publish** on the draft Release, run through this. +The steps that keep getting skipped are the CHANGELOG date and the "What's new" +section — both 2.1.1 and 2.2.0 shipped still marked `— Unreleased`. + +1. **CHANGELOG date** — change the top `## [x.y.z] — Unreleased` to + `## [x.y.z] — YYYY-MM-DD`, then `grep -n Unreleased CHANGELOG.md` to confirm + none remain. +2. **CHANGELOG completeness** — every PR since the last tag is under + Added / Changed / Fixed with `(#NN, @author)`. +3. **"What's new"** — add a `## What's new in ` section to `README.md` + and `README-zh-CN.md` (the two editions that carry it); for a patch, fold + notable items into the current minor's list. +4. **Localisation parity** — new user-facing strings exist in **all seven** + languages (en / de-DE / zh-TW / zh-CN / ja / ko / pt-BR); a new UI language is + wired through `SupportedLanguage` (types.ts), the `package.json` enum and + `settings.ts` `enumValues`, and added to the README language table. +5. **Settings / commands** — any new setting or command appears in `README.md`'s + settings table and `package.json` `contributes`. +6. **Build & test** — `npm run compile` and `npm test` are clean; package a + `.vsix` and smoke-test the changed behaviour locally. +7. **After Publish** — pull the new tag + commits so local `main` isn't left + behind upstream. + ## Conventions - **Commit hygiene:** one meaningful commit per logical change; avoid a stream diff --git a/README-zh-CN.md b/README-zh-CN.md index 7c0ca1ea..ba891620 100644 --- a/README-zh-CN.md +++ b/README-zh-CN.md @@ -60,6 +60,20 @@ AI 建议生成的是一份 **Markdown 文档**,用文字展示比截图更直 --- +## 2.2 新功能 + +- **用量分享卡**(可选,`enableShareCard`):一张可配置的单页 SVG 用量卡——自选时间范围 × 范围(总体 / 工程 / 会话)× 展示哪些指标,以及主题(**Claude 经典橙** / **奶油** / **极光暗色** / **自动**),可选带上 GitHub 头像与名称。自包含、可复现;提示词、路径、ID 一律不出本机。中文语言下使用 万/亿 单位。 +- **只读会话查看器**(会话标签,**默认开启**):每行的「查看」按钮以只读方式重开某个历史会话的提示词与 Markdown 渲染的回答,帮你回忆内容,而**不必**像「恢复」那样把它重新塞回模型上下文。思考与工具调用收进折叠区,默认展示最近若干轮。 +- **Token 热力图**(可选,`showHeatmap`):All 标签顶部的 GitHub 风格年度 token 热力图,并可**导出 / 发布到你的 GitHub 主页**(自包含 SVG,一键复制 Markdown 嵌入代码)。 +- **实验性洞察**(可选,`showInsights`,Content 标签):基于本地日志的启发式估算(均标注为估算)——**缓存损耗账单**(模型切换 / 空闲导致重写缓存的花费)、**各模型缓存保温时长**、**大单轮**、**你的活跃时段**、以及**技能 ROI**(每美元产出的输出 token)。 +- **最贵的 10 条消息**(可选,`showCostliestMessages`):按成本排序单轮对话,区分**缓存未命中**与长回答,附缓存命中率与距上一轮的间隔。 +- **会话「活跃」列**:每个会话的估算实际操作时长(空闲间隔按 1.5 小时封顶),比原始的首尾时间跨度更有意义。 +- **缓存命中率列**:All-time(按月)与本月(按日)表格及其下钻均新增该列,逐行可见缓存效率。 +- **实时刷新延迟**(`fileWatchSeconds`:关 / 1 / 2 / 5 / 10 / 20 / 30 秒)取代原来的开 / 关切换;只重读**本地**日志。 +- **时区感知分桶**:Today / 日 / 月 / 小时 的统计都以你配置的 IANA 时区为准(下拉含完整 UTC 偏移),彼此之间以及与 Anthropic 控制台保持一致。 +- **升级后「新功能」提示**:每次首次运行新的 major.minor 版本时给一次可关闭的提示,让可选功能不被埋没。 +- **修复**:Sonnet 5 正确上报 1M 上下文窗口(#50);日志带 TTL 拆分时,1 小时缓存写入按 2× 基础输入计价(#62);多窗口下后台窗口在获得焦点时刷新、不再显示过期数据(#55);时区设置改为校验过的下拉框,非法值不再拖垮仪表板(#51);德语(de-DE)与巴西葡语(pt-BR)在各处均可选。 + ## 2.1 新功能 - **会话:恢复 / 拷贝 / 删除**:每行可拷贝会话 ID、**恢复**(本工程会话优先在官方 Claude Code 扩展的新 tab 打开;其他工程的会话在其所在目录开终端执行 `claude --resume `,因为官方 in-tab 恢复用的是当前工作区目录、找不到跨工程会话)、或**删除**(移至回收站,需确认)。并新增「当前工程 / 全部」筛选,默认当前工程。 diff --git a/README.md b/README.md index c299a921..2c757339 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,47 @@ consent prompt. --- +## What's new in 2.2 + +- **Usage share card** (opt-in, `enableShareCard`) — a themed, configurable + one-page SVG of your usage: pick a range × scope (overall / project / session) + × which metrics to show, and a theme (**Claude Classic** / **Cream** / + **Aurora Dark** / **Auto**), with an optional GitHub avatar + name. + Self-contained and deterministic; no prompts, paths or ids ever leave your + machine. Chinese locales use 万/亿 units. +- **Read-only conversation viewer** (Sessions tab, **on by default**) — a "view" + button re-opens a past session's prompts and Markdown-rendered answers so you + can jog your memory **without** loading it back into the model's context + (unlike resume). Thinking and tool traffic sit behind toggles; opens on the + last rounds. +- **Token heatmap** (opt-in, `showHeatmap`) — a GitHub-style yearly token heatmap + on the All tab, plus **Export / Publish to your GitHub profile** as a + self-contained SVG with a one-click Markdown embed. +- **Experimental insights** (opt-in, `showInsights`, Content tab) — labelled + estimates from your local logs: a **cache-churn bill** ($ spent re-writing + cache after model switches / idle gaps), **cache warmth by model**, **big + one-shot turns**, **your active hours**, and **skill ROI** (output tokens + returned per $). +- **Top-10 costliest messages** (opt-in, `showCostliestMessages`) — ranks single + turns by cost, splitting a **cache miss** from a long answer, with the + cache-hit rate and the time since the last turn. +- **Sessions "Active" column** — estimated hands-on time per session (idle gaps + capped at 1.5 h), far more meaningful than the raw first-to-last span. +- **Cache-hit-rate column** in the All-time (monthly) and This-month (daily) + tables and their drill-downs, so cache efficiency is visible per row. +- **Live-refresh delay** (`fileWatchSeconds`: Off / 1 / 2 / 5 / 10 / 20 / 30 s) + replaces the on/off live-watch toggle; it only re-reads your **local** logs. +- **Timezone-aware bucketing** — Today / day / month / hour totals all key off + your configured IANA zone (full UTC-offset dropdown), so they agree with each + other and the Anthropic console. +- **"What's new" prompt after upgrades** — a single dismissible nudge the first + time you run a new major.minor version, so opt-in features stay discoverable. +- **Fixes** — Sonnet 5 reports a 1M context window (#50); 1-hour cache writes are + priced at 2× base input when the log carries the split (#62); background + windows refresh on focus instead of going stale (#55); the Timezone setting is + a validated dropdown so a bad value can't crash the dashboard (#51); German + (de-DE) and Brazilian Portuguese (pt-BR) are selectable everywhere. + ## What's new in 2.1 - **Sessions: resume / copy / delete** — each row can copy the session id, From 2ca6fcd383347bb196c78227324559a59d27d30b Mon Sep 17 00:00:00 2001 From: Carl <74130028+Carl723000@users.noreply.github.com> Date: Sun, 12 Jul 2026 21:03:39 +0800 Subject: [PATCH 2/3] feat(sessions): default to all sessions with time / project / model filters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Sessions tab showed only the 50 most-recent sessions (a hard limit in getSessionBreakdown), defaulted to the current project, and had a single project toggle — so it read as 'this month, this project only'. Now: - dataLoader: raise the session cap 50 -> 1000 (DOM-safety bound, effectively all of a normal user's sessions). - webview: a filter bar above the table with time range (All / Today / 7d / 30d), the existing project toggle (now defaulting to All), and a model dropdown built from each session's models; per-row data-models attribute. - client JS: one combined filter (project x time x model) that owns row display, each dimension persisted to localStorage and restored after the post-delete reload; drops the old CSS show-current mechanism. - i18n: sessionRangeToday/7d/30d + sessionModelAll across all seven locales. Verified: compile + 114 tests pass; filter predicate simulated across 7 combinations. Co-authored-by: Claude --- src/dataLoader.ts | 6 +- src/i18n.ts | 32 +++++++++++ src/webview.ts | 136 ++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 148 insertions(+), 26 deletions(-) diff --git a/src/dataLoader.ts b/src/dataLoader.ts index b3f982a0..433ed210 100644 --- a/src/dataLoader.ts +++ b/src/dataLoader.ts @@ -2119,9 +2119,11 @@ export class ClaudeDataLoader { * Group records by their source session (.jsonl file) and aggregate usage per session. * Returns sessions with billable usage, sorted by most recent activity first. * @param records All loaded usage records - * @param limit Maximum number of sessions to return (default 50) + * @param limit Maximum number of sessions to return. Default 1000 — a DOM-safety + * cap, not a "recent" window: the Sessions tab shows all of a normal user's + * sessions and narrows them with client-side time / project / model filters. */ - static getSessionBreakdown(records: ClaudeUsageRecord[], limit: number = 50): SessionUsage[] { + static getSessionBreakdown(records: ClaudeUsageRecord[], limit: number = 1000): SessionUsage[] { const recordsBySession: Record = {}; for (const record of records) { diff --git a/src/i18n.ts b/src/i18n.ts index 23600346..e7f2e12f 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -144,6 +144,10 @@ export interface Translations { resumeInvalid: string; sessionFilterCurrent: string; sessionFilterAll: string; + sessionRangeToday: string; + sessionRange7d: string; + sessionRange30d: string; + sessionModelAll: string; deleteSession: string; deleteSessionConfirm: string; deleteSessionDetail: string; @@ -349,6 +353,10 @@ const translations: Record = { resumeInvalid: 'Invalid session id — cannot resume.', sessionFilterCurrent: 'Current project', sessionFilterAll: 'All', + sessionRangeToday: 'Today', + sessionRange7d: '7 days', + sessionRange30d: '30 days', + sessionModelAll: 'All models', deleteSession: 'Delete session', deleteSessionConfirm: 'Delete session "{name}"?', deleteSessionDetail: 'Its conversation log moves to the trash (recoverable). The extension is otherwise read-only.', @@ -570,6 +578,10 @@ const translations: Record = { resumeInvalid: 'Ungültige Sitzungs-ID — Fortsetzen nicht möglich.', sessionFilterCurrent: 'Aktuelles Projekt', sessionFilterAll: 'Alle', + sessionRangeToday: 'Heute', + sessionRange7d: '7 Tage', + sessionRange30d: '30 Tage', + sessionModelAll: 'Alle Modelle', deleteSession: 'Sitzung löschen', deleteSessionConfirm: 'Sitzung "{name}" löschen?', deleteSessionDetail: 'Das Gesprächsprotokoll wandert in den Papierkorb (wiederherstellbar). Die Erweiterung ist ansonsten schreibgeschützt.', @@ -790,6 +802,10 @@ const translations: Record = { resumeInvalid: '無效的會話 ID,無法恢復。', sessionFilterCurrent: '目前專案', sessionFilterAll: '全部', + sessionRangeToday: '今天', + sessionRange7d: '7 天', + sessionRange30d: '30 天', + sessionModelAll: '全部模型', deleteSession: '刪除會話', deleteSessionConfirm: '刪除會話「{name}」?', deleteSessionDetail: '對話記錄會移至垃圾桶(可復原)。此擴充功能其餘部分為唯讀。', @@ -1007,6 +1023,10 @@ const translations: Record = { resumeInvalid: '无效的会话 ID,无法恢复。', sessionFilterCurrent: '当前工程', sessionFilterAll: '全部', + sessionRangeToday: '今天', + sessionRange7d: '7 天', + sessionRange30d: '30 天', + sessionModelAll: '全部模型', deleteSession: '删除会话', deleteSessionConfirm: '删除会话「{name}」?', deleteSessionDetail: '对话日志将移至回收站(可恢复)。本扩展其余部分为只读。', @@ -1228,6 +1248,10 @@ const translations: Record = { resumeInvalid: '無効なセッションIDのため再開できません。', sessionFilterCurrent: '現在のプロジェクト', sessionFilterAll: 'すべて', + sessionRangeToday: '今日', + sessionRange7d: '7日間', + sessionRange30d: '30日間', + sessionModelAll: 'すべてのモデル', deleteSession: 'セッションを削除', deleteSessionConfirm: 'セッション「{name}」を削除しますか?', deleteSessionDetail: '会話ログはゴミ箱に移動します(復元可能)。この拡張機能は他の部分では読み取り専用です。', @@ -1450,6 +1474,10 @@ const translations: Record = { resumeInvalid: '잘못된 세션 ID입니다 — 재개할 수 없습니다.', sessionFilterCurrent: '현재 프로젝트', sessionFilterAll: '전체', + sessionRangeToday: '오늘', + sessionRange7d: '7일', + sessionRange30d: '30일', + sessionModelAll: '전체 모델', deleteSession: '세션 삭제', deleteSessionConfirm: '세션 "{name}"을(를) 삭제할까요?', deleteSessionDetail: '대화 로그가 휴지통으로 이동합니다(복구 가능). 확장 프로그램은 그 외에는 읽기 전용입니다.', @@ -1672,6 +1700,10 @@ const translations: Record = { resumeInvalid: 'ID de sessão inválido — não é possível retomar.', sessionFilterCurrent: 'Projeto atual', sessionFilterAll: 'Todos', + sessionRangeToday: 'Hoje', + sessionRange7d: '7 dias', + sessionRange30d: '30 dias', + sessionModelAll: 'Todos os modelos', deleteSession: 'Excluir sessão', deleteSessionConfirm: 'Excluir a sessão "{name}"?', deleteSessionDetail: 'O log da conversa vai para a lixeira (recuperável). A extensão é somente leitura no restante.', diff --git a/src/webview.ts b/src/webview.ts index d0531a5b..5f0aec7b 100644 --- a/src/webview.ts +++ b/src/webview.ts @@ -1709,6 +1709,8 @@ export class UsageWebviewProvider { // first→last span. Computed once for the whole table. const activeMap = ClaudeDataLoader.activeDurationBySession(this.allRecords); let rows = ''; + // Distinct short model names across all sessions → the model filter dropdown. + const modelSet = new Set(); this.sessionBreakdown.forEach((s) => { const d = s.data; const foreign = workspacePath @@ -1745,9 +1747,13 @@ export class UsageWebviewProvider { : thinkingHidden ? t.thinkingHiddenShort : '-'; + // Distinct short model names for this session → the model filter matches on these. + const rowModels = [...new Set(Object.keys(d.modelBreakdown || {}).map((m) => this.shortModelName(m)))]; + rowModels.forEach((m) => modelSet.add(m)); rows += ' '' + label + ''; - // Filter the list to the current project (default) or all. + // Filter bar above the table: time range · project · model. All three narrow the + // list client-side; the list now defaults to ALL sessions and ALL projects. const total = this.sessionBreakdown.length; const foreignCount = total - currentCount; const showToggle = !!workspacePath && currentCount > 0 && foreignCount > 0; - const filterBar = showToggle - ? '
' + - '' + - '' + - '
' - : ''; + const modelOptions = [...modelSet] + .sort() + .map((m) => '') + .join(''); + const filterBar = + '
' + + '
' + + '' + + '' + + '' + + '' + + '
' + + (showToggle + ? '
' + + '' + + '' + + '
' + : '') + + (modelOptions + ? '' + : '') + + '
'; return ( '
' + '

' + t.sessionBreakdown + '

' + '

' + t.sortHint + '

' + filterBar + - '
' + + '
' + '
' + '' + '' + @@ -3794,11 +3818,17 @@ export class UsageWebviewProvider { border-color: var(--vscode-errorForeground, #f44336); } - /* Sessions "current project / all" filter. */ + /* Sessions filter bar: time range · project · model. */ + .sess-filters { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 10px; + margin: 0 0 10px; + } .sess-filter { display: flex; gap: 6px; - margin: 0 0 10px; } .sess-filter-btn { background: none; @@ -3814,8 +3844,13 @@ export class UsageWebviewProvider { color: var(--vscode-button-foreground); border-color: var(--vscode-button-background); } - .session-list.show-current .session-foreign { - display: none; + .sess-model-select { + background: var(--vscode-dropdown-background); + color: var(--vscode-dropdown-foreground); + border: 1px solid var(--vscode-dropdown-border, var(--vscode-panel-border)); + border-radius: 4px; + padding: 2px 6px; + font-size: 12px; } /* iOS-style auto-refresh toggle. The label/switch sit next to the other @@ -5392,28 +5427,81 @@ function viewConversation(btn) { }); } -function applySessionFilter(mode) { +function ccuSessState() { + var pb = document.querySelector('.sess-filter[data-group="project"] .sess-filter-btn.active'); + var rb = document.querySelector('.sess-filter[data-group="range"] .sess-filter-btn.active'); + var ms = document.querySelector('.sess-model-select'); + return { + project: pb ? pb.getAttribute('data-mode') : 'all', + range: rb ? rb.getAttribute('data-range') : 'all', + model: ms ? ms.value : 'all' + }; +} +function applySessionFilters() { var list = document.getElementById('sessionList'); if (!list) { return; } - list.classList.toggle('show-current', mode === 'current'); - document.querySelectorAll('.sess-filter-btn').forEach(function(b) { - b.classList.toggle('active', b.getAttribute('data-mode') === mode); + var st = ccuSessState(); + var now = Date.now(); + var threshold = st.range === 'today' ? new Date().setHours(0, 0, 0, 0) + : st.range === '7' ? now - 7 * 86400000 + : st.range === '30' ? now - 30 * 86400000 + : 0; + list.querySelectorAll('tr.sort-row').forEach(function(tr) { + var okProject = st.project === 'all' || !tr.classList.contains('session-foreign'); + var okTime = st.range === 'all' || Number(tr.getAttribute('data-sort-time')) >= threshold; + var okModel = st.model === 'all' || (tr.getAttribute('data-models') || '').split('|').indexOf(st.model) !== -1; + tr.style.display = (okProject && okTime && okModel) ? '' : 'none'; }); } +// One handler per filter group; all persist and re-apply the combined filter. +function ccuSetActive(btn) { + var group = btn.parentNode; + if (!group) { return; } + group.querySelectorAll('.sess-filter-btn').forEach(function(b) { b.classList.remove('active'); }); + btn.classList.add('active'); +} function sessionFilter(btn) { if (!btn) { return; } - var mode = btn.getAttribute('data-mode'); - applySessionFilter(mode); - // Persist so the full-document reload (e.g. after a delete) keeps the filter. - try { localStorage.setItem('ccu.sessionFilter', mode); } catch (e) {} + ccuSetActive(btn); + try { localStorage.setItem('ccu.sessionFilter', btn.getAttribute('data-mode')); } catch (e) {} + applySessionFilters(); +} +function sessionRange(btn) { + if (!btn) { return; } + ccuSetActive(btn); + try { localStorage.setItem('ccu.sessionRange', btn.getAttribute('data-range')); } catch (e) {} + applySessionFilters(); +} +function sessionModel(sel) { + if (!sel) { return; } + try { localStorage.setItem('ccu.sessionModel', sel.value); } catch (e) {} + applySessionFilters(); } function restoreSessionFilter() { + // Restore each dimension from localStorage (survives the post-delete reload), + // then apply once. Defaults (nothing stored) = All range, All projects, All models. try { var mode = localStorage.getItem('ccu.sessionFilter'); - if (mode && document.querySelector('.sess-filter-btn[data-mode="' + mode + '"]')) { - applySessionFilter(mode); + if (mode) { + var pb = document.querySelector('.sess-filter[data-group="project"] .sess-filter-btn[data-mode="' + mode + '"]'); + if (pb) { ccuSetActive(pb); } + } + var range = localStorage.getItem('ccu.sessionRange'); + if (range) { + var rb = document.querySelector('.sess-filter[data-group="range"] .sess-filter-btn[data-range="' + range + '"]'); + if (rb) { ccuSetActive(rb); } + } + var model = localStorage.getItem('ccu.sessionModel'); + if (model) { + var ms = document.querySelector('.sess-model-select'); + if (ms) { + for (var i = 0; i < ms.options.length; i++) { + if (ms.options[i].value === model) { ms.value = model; break; } + } + } } } catch (e) {} + applySessionFilters(); } function copyPath(btn, e) { From 3b231f9665a6cb84e123780b3e0542822b94677f Mon Sep 17 00:00:00 2001 From: Carl <74130028+Carl723000@users.noreply.github.com> Date: Mon, 13 Jul 2026 00:12:38 +0800 Subject: [PATCH 3/3] docs: add the 2.2.1 CHANGELOG section and correct the version-resolver note Co-authored-by: Claude --- CHANGELOG.md | 20 ++++++++++++++++++++ CLAUDE.md | 7 +++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbcf92eb..f14af838 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,26 @@ All notable changes to this fork compared to upstream [`ClaudeCodeUsage/ClaudeCodeUsage`](https://github.com/ClaudeCodeUsage/ClaudeCodeUsage) (last upstream release: 1.0.8). Format follows [Keep a Changelog](https://keepachangelog.com). +## [2.2.1] — 2026-07-13 + +### Added +- **Sessions: all sessions + filters** — the Sessions tab defaults to *all* + sessions (it was capped at the 50 most-recent) and no longer defaults to the + current project; a filter bar adds a **time range** (All / Today / 7d / 30d), the + **project** toggle, and a **model** dropdown, each persisted across reloads. (#73) + +### Changed +- **`@ccu-bot` first-pass replies default to English** — bilingual (English + 中文) + only when the issue/PR author themselves wrote in Chinese, never Chinese-only; + the section headings default to English. (#72) + +### Fixed +- **Monthly Usage table month labels** no longer read one month behind in + negative-UTC timezones — the monthly label was built from `new Date('YYYY-MM-01')` + (UTC midnight) then formatted in the user's timezone, so a behind-UTC zone showed + the previous month; it now parses the key textually and pins it to noon UTC. The + daily label had the same latent off-by-one. (#72, #54) + ## [2.2.0] — 2026-07-07 ### Added diff --git a/CLAUDE.md b/CLAUDE.md index 5f8fc3c0..bcb2aac5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -65,8 +65,11 @@ npx @vscode/vsce package # build a .vsix compiles, packages, publishes to the VS Code Marketplace + Open VSX, and attaches the `.vsix`. So to release: just **Publish the draft Release** — no manual `version` bump, no `git tag`. -- Versioning follows semver and is driven by PR labels: `fix`/none → patch - (`2.0.x`); `feature` → minor (`2.x.0`); `breaking` → major. +- Versioning follows semver, resolved by `release-drafter.yml` from the merged + PRs' labels: **everything defaults to patch** (`fix` / `feature` / `enhancement` + / `docs` / `ci` / `chore` → patch); only an explicit **`minor`** label bumps the + minor (`2.x.0`); `breaking` / `major` bump the major. So a feature ships in a + patch release *unless a maintainer deliberately labels the PR `minor`*. ### Pre-release checklist