ccgauge 1.0.2 → 1.0.3

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.
Files changed (64) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/app-build-manifest.json +26 -26
  3. package/.next/standalone/.next/app-path-routes-manifest.json +7 -7
  4. package/.next/standalone/.next/build-manifest.json +2 -2
  5. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  6. package/.next/standalone/.next/server/app/api/blocks/route.js +1 -1
  7. package/.next/standalone/.next/server/app/api/blocks/route.js.nft.json +1 -1
  8. package/.next/standalone/.next/server/app/api/blocks/route_client-reference-manifest.js +1 -1
  9. package/.next/standalone/.next/server/app/api/export/usage/route.js +1 -1
  10. package/.next/standalone/.next/server/app/api/export/usage/route.js.nft.json +1 -1
  11. package/.next/standalone/.next/server/app/api/export/usage/route_client-reference-manifest.js +1 -1
  12. package/.next/standalone/.next/server/app/api/pricing/route_client-reference-manifest.js +1 -1
  13. package/.next/standalone/.next/server/app/api/projects/route.js +1 -1
  14. package/.next/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  15. package/.next/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  16. package/.next/standalone/.next/server/app/api/scan/route.js +1 -1
  17. package/.next/standalone/.next/server/app/api/scan/route.js.nft.json +1 -1
  18. package/.next/standalone/.next/server/app/api/scan/route_client-reference-manifest.js +1 -1
  19. package/.next/standalone/.next/server/app/api/sessions/route.js +1 -1
  20. package/.next/standalone/.next/server/app/api/sessions/route.js.nft.json +1 -1
  21. package/.next/standalone/.next/server/app/api/sessions/route_client-reference-manifest.js +1 -1
  22. package/.next/standalone/.next/server/app/api/usage/route.js +1 -1
  23. package/.next/standalone/.next/server/app/api/usage/route.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/api/usage/route_client-reference-manifest.js +1 -1
  25. package/.next/standalone/.next/server/app/models/page.js +2 -2
  26. package/.next/standalone/.next/server/app/models/page.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/models/page_client-reference-manifest.js +1 -1
  28. package/.next/standalone/.next/server/app/page.js +2 -2
  29. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  31. package/.next/standalone/.next/server/app/projects/[id]/page.js +2 -2
  32. package/.next/standalone/.next/server/app/projects/[id]/page.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/projects/[id]/page_client-reference-manifest.js +1 -1
  34. package/.next/standalone/.next/server/app/projects/page.js +1 -1
  35. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  37. package/.next/standalone/.next/server/app/sessions/[id]/page.js +2 -2
  38. package/.next/standalone/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
  40. package/.next/standalone/.next/server/app/sessions/page.js +1 -1
  41. package/.next/standalone/.next/server/app/sessions/page.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  43. package/.next/standalone/.next/server/app/settings/page.js +1 -1
  44. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  45. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  46. package/.next/standalone/.next/server/app/usage/page.js +2 -2
  47. package/.next/standalone/.next/server/app/usage/page.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/usage/page_client-reference-manifest.js +1 -1
  49. package/.next/standalone/.next/server/app-paths-manifest.json +7 -7
  50. package/.next/standalone/.next/server/chunks/971.js +1 -1
  51. package/.next/standalone/.next/server/chunks/98.js +1 -0
  52. package/.next/standalone/.next/server/functions-config-manifest.json +2 -2
  53. package/.next/standalone/.next/server/pages/500.html +1 -1
  54. package/.next/standalone/package.json +1 -1
  55. package/CHANGELOG.md +148 -0
  56. package/README.md +19 -6
  57. package/README.zh-CN.md +19 -6
  58. package/bin/cli.mjs +37 -17
  59. package/dist/mcp/server.mjs +40 -23699
  60. package/dist/report/index.mjs +38 -18
  61. package/package.json +1 -1
  62. package/.next/standalone/.next/server/chunks/155.js +0 -1
  63. /package/.next/standalone/.next/static/{4YjiQrRI-CsVEPC1UOUEJ → alqi5oQtTQUdpxp2x0yAt}/_buildManifest.js +0 -0
  64. /package/.next/standalone/.next/static/{4YjiQrRI-CsVEPC1UOUEJ → alqi5oQtTQUdpxp2x0yAt}/_ssgManifest.js +0 -0
package/CHANGELOG.md CHANGED
@@ -5,6 +5,153 @@ All notable changes to **ccgauge** are documented here.
5
5
  The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
6
6
  this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.0.3] — 2026-05-15
9
+
10
+ A focused **MCP server** correctness + performance + ergonomics pass,
11
+ plus a docs catch-up for the marketing site and the bundled READMEs.
12
+ Dashboard runtime is unchanged from 1.0.2. The MCP server adds a 9th
13
+ tool (`cost_estimator`), gets ~4× faster on weekly summaries, halves
14
+ its bundle size, and ships a `ccgauge mcp --check` self-test.
15
+
16
+ ### Highlights
17
+
18
+ - **9 MCP tools** — added `cost_estimator(source, model, input_tokens,
19
+ output_tokens, …)` for pure pricing what-ifs (no record lookup).
20
+ Pairs with `usage_summary` for "if I run X more on Opus 4.7, what
21
+ does it add to my month-to-date?".
22
+ - **`weekly_summary` is ~4× faster.** Rewrote the per-day trend from
23
+ 8 full record-set passes (1 totals + 7 daily totals × 2 internal
24
+ source filters) down to a single `timeBuckets('day')` call + a
25
+ 7-slot skeleton merge. Same JSON shape out, including zero-fill
26
+ for empty days.
27
+ - **MCP bundle 810 KB → 379 KB.** esbuild `minify: true` +
28
+ `external: ['fsevents']` shaves 53% off the published tarball's
29
+ MCP slice with no runtime behaviour change.
30
+ - **`ccgauge mcp --check`** — verifies the bundle, boots the indexer,
31
+ and prints one line per provider (files / records / scanned dirs).
32
+ Lets users debug "Claude Desktop doesn't see ccgauge tools"
33
+ without spinning up an MCP client first.
34
+
35
+ ### Added
36
+
37
+ - `cost_estimator` MCP tool — uses the provider's built-in
38
+ per-1M-token pricing table; does NOT consult usage history.
39
+ Useful for budgeting and cap planning.
40
+ - `ccgauge mcp --check` self-test command.
41
+ - README cross-references for both new entry points (`mcp --check`,
42
+ `cost_estimator`) in en + zh.
43
+ - `__SERVER_VERSION__` injected by esbuild from `package.json#version`
44
+ so the server's MCP handshake always agrees with the npm release.
45
+
46
+ ### Changed
47
+
48
+ - **`ccgauge mcp` runs in-process.** The CLI subcommand used to
49
+ spawn a second Node process for the bundled server (`spawn(node,
50
+ [bundle])`); it now `await import()`s the bundle and calls
51
+ `runStdioServer()` directly. Saves ~80–150 ms per invocation and
52
+ removes a brittle signal-forwarding shim.
53
+ - **MCP responses are compact JSON.** Drop the `JSON.stringify(...,
54
+ null, 2)` pretty-print on every tool response — LLMs don't read
55
+ indentation but pay for it. ~30–50% smaller responses for the
56
+ heavier tools (`weekly_summary`, `usage_by_session`).
57
+ `CCGAUGE_MCP_PRETTY=1` re-enables.
58
+ - **Default limits on `usage_by_model` (20) and `usage_by_project`
59
+ (20).** The cap existed as a max; the default is now the same
60
+ value. A user asking "what did I work on this year" with hundreds
61
+ of projects no longer gets a 200-entry payload.
62
+ - **`recent_activity` defaults to a 30-day window** (`days` arg to
63
+ widen). Previously aggregated every session across the user's
64
+ full CLI lifetime just to return the top 10 by `end_time` — cost
65
+ grew linearly with history.
66
+ - **Day-aligned named ranges.** `7d` / `30d` / `90d` now map to
67
+ `[start-of(N-1 days ago), end-of-today]` like `today` /
68
+ `yesterday` / `this_week` already did. Previously `7d` was a
69
+ rolling 7×24h window, so summing seven `daily_summary` calls
70
+ didn't equal one `usage_summary({range:"7d"})` — they do now.
71
+ - **README Highlights refreshed** (en + zh). New top-level subsections
72
+ for `ccgauge report` (CLI) and `ccgauge mcp` (MCP server) so the
73
+ two no-server entry points are visible in the first scroll.
74
+ Cross-provider section updated for 1.0.2's tri-state switcher +
75
+ real provider logos + worktree-aware Projects collapsing.
76
+ Drill-down section mentions the Tokens / Conversations trend toggle.
77
+
78
+ ### Fixed
79
+
80
+ - **`parseDateLike` no longer accepts calendar-overflow dates with
81
+ a time suffix.** `2026-02-31T00:00:00` used to silently roll
82
+ forward to March 3 (JS `new Date()` normalises invalid dates);
83
+ now both the bare-date branch and the time-suffix branch reject
84
+ it. Affects every MCP date arg, the dashboard's `from`/`to` URL
85
+ params, and `ccgauge report --since`/`--until`.
86
+ - **`SERVER_VERSION` no longer hardcoded** to `0.4.0`. Now sourced
87
+ from `package.json#version` at bundle time, so the MCP server's
88
+ `initialize` response reports the same version as the npm release
89
+ it shipped in.
90
+ - **README `range: "14d"` example was bogus** — the schema enum
91
+ doesn't accept `14d`, so following the README's project-breakdown
92
+ prompt produced an error. Replaced with the explicit-`from`/`to`
93
+ form. Same fix in `README.zh-CN.md`.
94
+
95
+ ### Performance
96
+
97
+ | Metric | Before | After |
98
+ | --- | --- | --- |
99
+ | `dist/mcp/server.mjs` size | 810 KB | **379 KB** (-53%) |
100
+ | `weekly_summary` record-set passes | 8 (+ 2 internal source filters each) | **2** |
101
+ | `recent_activity` aggregation scope | every session ever | **last 30 days** |
102
+ | MCP response JSON byte count (typical) | indented | **~half** |
103
+ | Aggregator `withinRange` Date#toISOString() calls | per record × N filters | **once per call** |
104
+
105
+ ### Security / privacy
106
+
107
+ - **MCP tool errors are scrubbed of `$HOME` paths** before they
108
+ reach the SDK's error envelope. Belt-and-suspenders today (the
109
+ only user-visible throws are clean date-arg validators), but
110
+ defends against future error paths that might wrap an indexer
111
+ error. Extracted the existing `sanitizeForUser` from the indexer
112
+ module into a shared `lib/sanitize` so both layers use one
113
+ definition.
114
+
115
+ ### Internal
116
+
117
+ - `lib/aggregator/index.ts` — entry-point functions now hoist
118
+ `from/to → ISO string` and `models/projects → Set` once per
119
+ call instead of recomputing them per record. Measurable on the
120
+ `weekly_summary` hot path; invisible for the dashboard's
121
+ single-pass aggregators.
122
+ - New `lib/mcp/text-result.ts` — single source of truth for the
123
+ MCP response wrapper. `lib/mcp/safe-handler.ts` — HOF that wraps
124
+ tool callbacks with the path-scrub guard.
125
+ - `parseDayArg` moved from `tools/activity.ts` to `mcp/context.ts`
126
+ next to `parseDateRange` so the two share a single implementation
127
+ of the "today / yesterday / monday / YYYY-MM-DD" parser.
128
+ - 30 s polling fallback in the indexer is now **off by default for
129
+ the MCP instance** and **on by default for the dashboard**.
130
+ Override with `CCGAUGE_POLL_FALLBACK={0,1}`. The MCP server runs
131
+ in a background daemon spawned by an LLM client and shouldn't
132
+ keep the host warm just to catch the rare `fs.watch(recursive)`
133
+ miss.
134
+ - `AGENTS.md` documents the `site/` sub-project, the v4 sidechain
135
+ merge invariant, a release checklist, and the
136
+ `raw.githubusercontent.com` hero-image fragility. Two new
137
+ "intentionally NOT supported" items: folding `site/` into a pnpm
138
+ workspace, and letting `site/` leak into the npm tarball.
139
+
140
+ ### Docs (doesn't ship to npm)
141
+
142
+ - Marketing site (`site/`) catches up to 1.0.2: features page
143
+ covers All view + conversation-count toggle + worktree merging;
144
+ homepage hero, "Multi-source analytics" card, and ScreenshotFrame
145
+ now use three distinct screenshots; hardcoded `v1.0.0` eyebrows
146
+ removed.
147
+ - `site/public/images/README.md` rewritten as a single-source-of-
148
+ truth inventory (file → used-by → source), with the prompt
149
+ catalogue trimmed to filenames that actually exist. Legacy
150
+ placeholder SVGs moved to an explicit "kept but unused"
151
+ subsection.
152
+ - `site/public/robots.txt` no longer points at a non-existent
153
+ `sitemap-index.xml`.
154
+
8
155
  ## [1.0.2] — 2026-05-15
9
156
 
10
157
  This release brings the dashboard the long-requested **All view** (one
@@ -531,6 +678,7 @@ of HTML to the browser.
531
678
  - Initial public release as `ccgauge`: local Next.js dashboard for
532
679
  Claude Code token usage, cost, and 5-hour block tracking.
533
680
 
681
+ [1.0.3]: https://github.com/chengzuopeng/ccgauge/compare/v1.0.2...v1.0.3
534
682
  [1.0.2]: https://github.com/chengzuopeng/ccgauge/compare/v1.0.1...v1.0.2
535
683
  [1.0.1]: https://github.com/chengzuopeng/ccgauge/compare/v1.0.0...v1.0.1
536
684
  [1.0.0]: https://github.com/chengzuopeng/ccgauge/compare/v0.4.0...v1.0.0
package/README.md CHANGED
@@ -38,8 +38,9 @@ Everything runs locally as a Next.js app. Your conversation transcripts never le
38
38
  ## Highlights
39
39
 
40
40
  ### Cross-provider analytics
41
- - One dashboard for both **Claude Code** and **OpenAI Codex CLI**
42
- - Toggle data source from the nav bar; URL persists via `?source=`, last choice cached in cookie
41
+ - One dashboard for both **Claude Code** and **OpenAI Codex CLI**, plus an **All view** that merges the two
42
+ - Toggle data source from the nav bar (Claude · Codex · All), each button rendered with the real provider logo; URL persists via `?source=`, last choice cached in cookie
43
+ - **Worktree-aware Projects** — all worktrees of the same repo collapse into a single project row
43
44
  - Built-in **provider adapter layer** (`lib/providers/`) — adding a third CLI (Gemini CLI, Cursor, Aider, …) is one new file plus a single registry line
44
45
 
45
46
  ### At-a-glance KPIs
@@ -51,7 +52,7 @@ Everything runs locally as a Next.js app. Your conversation transcripts never le
51
52
  - **Sessions** — per-conversation list with model / tokens / cost / duration, plus a message-level timeline
52
53
  - **Projects** — per-`cwd` aggregation cards with sparkline and spend share
53
54
  - **Models** — side-by-side comparison: cost share, tokens share, cache hit, USD pricing
54
- - **Usage** — turn-grouped table with expandable tool-call breakdown, CSV export
55
+ - **Usage** — turn-grouped table with expandable tool-call breakdown, CSV export. **Tokens / Conversations** toggle on the trend chart so you can count rows the way the usage table counts them
55
56
 
56
57
  ### Cost transparency
57
58
  - **Cache savings** is its own KPI — quantifies how much Anthropic prompt caching saved you vs. paying full input price
@@ -63,6 +64,17 @@ Everything runs locally as a Next.js app. Your conversation transcripts never le
63
64
  - **English / 中文** (cookie + localStorage)
64
65
  - Filters: time range (today / 7d / 30d / 90d / all), granularity (hour / day / week / month), model and project multi-select
65
66
 
67
+ ### CLI report (no server)
68
+ - `ccgauge report` prints a colored, aligned terminal usage report in ~0.2 s from the same JSONL the dashboard reads
69
+ - `--range / --source / --by / --since / --until / --model / --project` filters
70
+ - `--json` for machine-readable output; `--no-color` auto-applied when piped — drops cleanly into shell scripts and CI
71
+
72
+ ### MCP server (for LLMs)
73
+ - `ccgauge mcp` runs a stdio JSON-RPC server so **Claude Desktop / Cursor / Cline** can query your local usage directly
74
+ - Nine MCP tools: `usage_summary`, `usage_by_time`, `usage_by_model`, `usage_by_project`, `usage_by_session`, `daily_summary`, `weekly_summary`, `recent_activity`, `cost_estimator`
75
+ - Reasoning-token breakdown surfaced for the models that emit one
76
+ - Separate named cache (`index-mcp-v2.json`) so MCP runs don't contend with the dashboard
77
+
66
78
  ### Privacy by design
67
79
  - 100 % local: read-only access to existing JSONL files, zero outbound network calls
68
80
  - Open source, MIT-licensed
@@ -217,6 +229,7 @@ English with real numbers from your machine.
217
229
  | `daily_summary` | "What did I do today / yesterday / Monday / on YYYY-MM-DD?" Sessions grouped by project + models + top tool calls. |
218
230
  | `weekly_summary` | 7-day roll-up: per-day cost trend, top sessions, top projects, models. `week_offset=-1` for last week. |
219
231
  | `recent_activity` | The N most recently active sessions across providers. |
232
+ | `cost_estimator` | Compute the USD cost of a hypothetical request (`{ source, model, input_tokens, output_tokens, cache_* }`). Uses built-in per-1M-token pricing; does NOT consult usage history. |
220
233
 
221
234
  | Resource URI | Content |
222
235
  | --- | --- |
@@ -334,8 +347,8 @@ debug "why did it answer X".
334
347
  → `daily_summary({ date: "yesterday" })`
335
348
  - *"Generate a Monday stand-up bullet list of what I shipped last week."*
336
349
  → `weekly_summary({ week_offset: -1 })`
337
- - *"Which 3 projects have I touched most in the last 2 weeks?"*
338
- → `usage_by_project({ range: "14d", limit: 3 })` (LLM may also pull `weekly_summary`)
350
+ - *"Which 3 projects have I touched most in the last two weeks?"*
351
+ → `usage_by_project({ from: "2026-05-01", to: "2026-05-15", limit: 3 })` — pass explicit `from`/`to` for any window not covered by the named ranges (`7d` / `30d` / `90d` / `this_week` / `last_week` / …).
339
352
  - *"What was my last coding session about?"*
340
353
  → `recent_activity({ limit: 1 })`
341
354
 
@@ -351,7 +364,7 @@ debug "why did it answer X".
351
364
  - *"At my current burn rate, how much will I spend this month?"*
352
365
  → `usage_summary({ range: "this_month" })` + `usage_by_time({ range: "this_month", granularity: "day" })` — LLM extrapolates.
353
366
  - *"If I run another 200K input + 50K output on Opus 4.7 today, what does that add to my month-to-date cost?"*
354
- → `usage_summary({ range: "this_month" })` + LLM does the arithmetic from the published per-1M-token rates.
367
+ → `cost_estimator({ source: "claude", model: "claude-opus-4-7", input_tokens: 200000, output_tokens: 50000 })` + `usage_summary({ range: "this_month" })` the estimator returns the dollar cost for the hypothetical request without touching your usage history.
355
368
 
356
369
  #### Cross-source comparisons
357
370
 
package/README.zh-CN.md CHANGED
@@ -38,8 +38,9 @@ npx ccgauge
38
38
  ## 亮点
39
39
 
40
40
  ### 多 CLI 数据源
41
- - 一份看板覆盖 **Claude Code** 与 **OpenAI Codex CLI**
42
- - 顶部一键切换,URL 用 `?source=` 持久化,cookie 记忆上次选择
41
+ - 一份看板覆盖 **Claude Code** 与 **OpenAI Codex CLI**,并提供 **All 视图**把两者合并查看
42
+ - 顶部三档切换(Claude · Codex · All),每个按钮都带真品牌 logo;URL 用 `?source=` 持久化,cookie 记忆上次选择
43
+ - **Worktree 感知的 Projects 合并** —— 同一个 repo 的所有 worktree 自动并到同一个项目行
43
44
  - 内置 **Provider 适配层**(`lib/providers/`)—— 增加第三个 CLI(Gemini CLI / Cursor / Aider …)只需一个新文件加注册表一行
44
45
 
45
46
  ### KPI 一眼看完
@@ -51,7 +52,7 @@ npx ccgauge
51
52
  - **会话页** —— 每场对话单独成行(模型 / token / 花费 / 时长),点进去看消息级时间线
52
53
  - **项目页** —— 按 `cwd` 聚合成卡片网格,含趋势条与花费占比
53
54
  - **模型页** —— 各模型并排对比:成本占比、token 占比、缓存命中、官方单价
54
- - **用量页** —— 按对话轮次分组的明细表,可展开看每次工具调用,支持 CSV 导出
55
+ - **用量页** —— 按对话轮次分组的明细表,可展开看每次工具调用,支持 CSV 导出。趋势图支持 **Token / 对话数** 切换,让条形图行数和用量表 1:1 对齐
55
56
 
56
57
  ### 成本透明
57
58
  - **缓存节省** 单独成卡 —— 量化 Anthropic prompt caching 实际帮你节省了多少美元
@@ -63,6 +64,17 @@ npx ccgauge
63
64
  - **English / 中文** 双语,cookie + localStorage 双向同步
64
65
  - 完整筛选:时间区间(今天 / 7 天 / 30 天 / 90 天 / 全部)、粒度(小时 / 天 / 周 / 月)、模型 / 项目 multi-select
65
66
 
67
+ ### 命令行报告(无 server)
68
+ - `ccgauge report` 读取同一份 JSONL,在 ~0.2 秒内打出彩色对齐的终端报告
69
+ - `--range / --source / --by / --since / --until / --model / --project` 滤波参数
70
+ - `--json` 输出给脚本;`--no-color` 走管道时自动开启 —— 可以直接塞进 shell 和 CI
71
+
72
+ ### MCP 服务(给 LLM 用)
73
+ - `ccgauge mcp` 起一个 stdio JSON-RPC 服务,让 **Claude Desktop / Cursor / Cline** 等 MCP 客户端直接查你本地的 ccgauge 历史
74
+ - 9 个 MCP tool:`usage_summary`、`usage_by_time`、`usage_by_model`、`usage_by_project`、`usage_by_session`、`daily_summary`、`weekly_summary`、`recent_activity`、`cost_estimator`
75
+ - 支持模型有 reasoning token 时单独折算
76
+ - 独立命名缓存(`index-mcp-v2.json`),MCP 进程不会和仪表盘抢同一份磁盘索引
77
+
66
78
  ### 隐私优先
67
79
  - 100 % 本地:只读访问已有 JSONL 文件,零外网调用
68
80
  - 开源,MIT 协议
@@ -213,7 +225,8 @@ LLM 会自动选合适的 tool、本地调用、用大白话给你带真实数
213
225
  | `usage_by_session` | 会话列表,含标题(首条用户消息)/ 模型 / 时长 / 花费。可按 recent / cost / tokens / duration 排序。 |
214
226
  | `daily_summary` | "今天 / 昨天 / 周一 / YYYY-MM-DD 我都干了啥?" 按项目分组的会话 + 模型 + top 工具调用。 |
215
227
  | `weekly_summary` | 7 天 roll-up:每日花费趋势 + top 会话 + top 项目 + 模型分布。`week_offset=-1` 看上周。 |
216
- | `recent_activity` | 最近 N 条活跃会话(不限日期)。 |
228
+ | `recent_activity` | 最近 N 条活跃会话(默认最近 30 天,可显式给 `from`/`to`)。 |
229
+ | `cost_estimator` | 计算"假设我用 X 模型发 N 个 token 要花多少钱"。直接读内置 per-1M-token 单价表,不查历史。常用于额度规划 / what-if。 |
217
230
 
218
231
  | Resource URI | 内容 |
219
232
  | --- | --- |
@@ -329,7 +342,7 @@ LLM 大概率会调的工具——方便你"为什么会这样回答"反查。
329
342
  - *"给我一份周一 standup 用的 bullet list,列我上周完成的事。"*
330
343
  → `weekly_summary({ week_offset: -1 })`
331
344
  - *"过去两周我接触最多的 3 个项目是什么?"*
332
- → `usage_by_project({ range: "14d", limit: 3 })`(LLM 也可能补一次 `weekly_summary`)
345
+ → `usage_by_project({ from: "2026-05-01", to: "2026-05-15", limit: 3 })`——非 `7d` / `30d` / `90d` / `this_week` / `last_week` 等命名窗口时,传显式 `from` / `to`。
333
346
  - *"我最近一次的编码会话是关于什么的?"*
334
347
  → `recent_activity({ limit: 1 })`
335
348
 
@@ -345,7 +358,7 @@ LLM 大概率会调的工具——方便你"为什么会这样回答"反查。
345
358
  - *"按当前消耗速度,本月预计花多少?"*
346
359
  → `usage_summary({ range: "this_month" })` + `usage_by_time({ range: "this_month", granularity: "day" })`——LLM 自己外推。
347
360
  - *"如果我今天再在 Opus 4.7 上跑 200K input + 50K output,本月累计要多少?"*
348
- → `usage_summary({ range: "this_month" })` + LLM 按公开单价做算术。
361
+ → `cost_estimator({ source: "claude", model: "claude-opus-4-7", input_tokens: 200000, output_tokens: 50000 })` + `usage_summary({ range: "this_month" })`——estimator 直接按内置单价表返回这笔假设请求的美元成本,不查历史。
349
362
 
350
363
  #### 跨数据源对比
351
364
 
package/bin/cli.mjs CHANGED
@@ -142,8 +142,9 @@ program
142
142
  program
143
143
  .command('mcp')
144
144
  .description('start the MCP server (stdio) so LLMs can query usage data')
145
- .action(async () => {
146
- await startMcp();
145
+ .option('--check', 'verify the bundle + indexer; print one line per provider and exit')
146
+ .action(async (opts) => {
147
+ await startMcp(opts);
147
148
  });
148
149
 
149
150
  function addReportOptions(cmd) {
@@ -438,7 +439,7 @@ or run the full build with
438
439
  }
439
440
  }
440
441
 
441
- async function startMcp() {
442
+ async function startMcp(opts = {}) {
442
443
  const bundle = join(packageRoot, 'dist', 'mcp', 'server.mjs');
443
444
  if (!existsSync(bundle)) {
444
445
  console.error(`
@@ -455,20 +456,39 @@ or run the full build with
455
456
  `);
456
457
  process.exit(1);
457
458
  }
458
- // Hand control to the bundled MCP server. It owns the stdio JSON-RPC
459
- // session for the lifetime of the parent (the LLM client) process.
460
- const child = spawn(process.execPath, [bundle], {
461
- stdio: 'inherit',
462
- env: process.env,
463
- });
464
- const forward = (sig) => () => {
465
- if (!child.killed) child.kill(sig);
466
- };
467
- process.on('SIGINT', forward('SIGINT'));
468
- process.on('SIGTERM', forward('SIGTERM'));
469
- child.on('exit', (code, sig) => {
470
- process.exit(typeof code === 'number' ? code : sig ? 128 : 0);
471
- });
459
+
460
+ // --check: don't actually run the JSON-RPC server load the bundle,
461
+ // boot the indexer, print one line per provider, and exit. Lets users
462
+ // verify their install without wiring up an MCP client.
463
+ if (opts.check) {
464
+ const mod = await import(pathToFileURL(bundle).href);
465
+ if (typeof mod.printCheck !== 'function') {
466
+ console.error('[ccgauge-mcp] this bundle was built without --check support');
467
+ process.exit(1);
468
+ }
469
+ const code = await mod.printCheck();
470
+ process.exit(typeof code === 'number' ? code : 0);
471
+ }
472
+
473
+ // Run the bundled MCP server **in this process** — the bundle exposes a
474
+ // top-level `runStdioServer()` so we just import + invoke it. Spawning a
475
+ // second Node process here is wasted memory/latency (LLM clients already
476
+ // spawn `ccgauge mcp` per conversation), and forwarding signals across
477
+ // processes is brittle (e.g. SIGHUP isn't covered by the old shim).
478
+ try {
479
+ const mod = await import(pathToFileURL(bundle).href);
480
+ if (typeof mod.runStdioServer !== 'function') {
481
+ console.error('[ccgauge-mcp] bundle missing runStdioServer export');
482
+ process.exit(1);
483
+ }
484
+ await mod.runStdioServer();
485
+ // runStdioServer keeps the loop alive via the stdio transport; if it
486
+ // ever returns it means the transport closed cleanly.
487
+ process.exit(0);
488
+ } catch (err) {
489
+ console.error('[ccgauge-mcp] failed to start:', err?.message ?? err);
490
+ process.exit(1);
491
+ }
472
492
  }
473
493
 
474
494
  async function resolvePort(opts) {