diff --git a/CLAUDE.md b/CLAUDE.md index fa4595c1..cdf27a4a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -7,7 +7,7 @@ Guidance for Claude Code working in this repository. Every line here is loaded i Token Tracker is a local-first AI token usage tracker. - **CLI** (`src/`, CommonJS, Node ≥20) — entry `bin/tracker.js` → `src/cli.js`. `serve` runs a local HTTP server on `:7680`, `sync` parses logs into `~/.tokentracker/queue.jsonl`. -- **Dashboard** (`dashboard/`, React 18 + Vite 7 + TS strict + Tailwind) — built to `dashboard/dist/`, served by the CLI locally and by Vercel at `www.tokentracker.cc`. +- **Dashboard** (`dashboard/`, React 18 + Vite 7 + TS strict + Tailwind) — built to `dashboard/dist/`, served by the CLI on localhost. Local-only: there is no hosted deployment. - **macOS app** (`TokenTrackerBar/`, Swift 5.9, XcodeGen) — menu bar + WidgetKit. `EmbeddedServer/` bundles the CLI runtime + built dashboard so the `.app` is self-contained. - **Windows app** (`TokenTrackerWin/`, .NET 8 WinForms + WPF + WebView2) — system-tray counterpart of the macOS app. Launches the bundled CLI `serve` on a dynamic loopback port (avoids the DoSvc-held `:7680`), hosts the dashboard in WebView2, registers the `tokentracker://` deep-link for OAuth. Built `EmbeddedServer/` (Node + CLI + dashboard) is bundled by `scripts/bundle-node.ps1` so the `.exe` is self-contained. Dashboard adaptations are gated behind `isNativeWindowsApp()` (`dashboard/src/lib/native-bridge.js`) so macOS/web paths are untouched. @@ -40,7 +40,7 @@ node bin/tracker.js serve --no-sync # local dashboard server on :7680 | Add a local API endpoint | `src/lib/local-api.js` — search `/functions/tokentracker-` | | Wire a provider into sync | `src/commands/sync.js` (call site + totals aggregation) + `src/commands/status.js` (status reporting) | | Add pricing for a model | `src/lib/pricing/curated-overrides.json` | -| Add a dashboard page | `dashboard/src/pages/` (lazy-loaded via `React.lazy()` in `App.jsx` — **except `NativeAuthCallbackPage`, which must stay eager-imported**, see Lessons learned) | +| Add a dashboard page | `dashboard/src/pages/` (lazy-loaded via `React.lazy()` in `App.jsx`) | | Add UI components | `dashboard/src/ui/dashboard/components/` | | Add a provider icon | `dashboard/src/ui/dashboard/components/ProviderIcon.jsx` (`PROVIDER_ICON_MAP` keyed by `source.toUpperCase()`) | | Add user-facing text | `dashboard/src/content/copy.csv` — never hardcode | @@ -163,23 +163,8 @@ openspec validate --strict ### Dashboard layout -- **`AppLayout`-wrapped pages use `flex flex-col flex-1`** as the outer wrapper, not `min-h-screen` + own sticky header/footer. Reference: `LimitsPage.jsx` / `LeaderboardPage.jsx` / `SettingsPage.jsx`. `LeaderboardProfilePage.jsx` is intentionally excluded via `isLeaderboardIndexPath` in `App.jsx`. +- **`AppLayout`-wrapped pages use `flex flex-col flex-1`** as the outer wrapper, not `min-h-screen` + own sticky header/footer. Reference: `LimitsPage.jsx` / `SettingsPage.jsx`. - **Motion height animations clip box-shadow focus rings.** Use `focus:ring-inset` on inputs inside `AnimatePresence` height-collapsing containers (see `SettingsPage.jsx` Account section). -- **`NativeAuthCallbackPage` must stay eager-imported in `App.jsx`** (do NOT convert it to `React.lazy()` even when adding other lazy pages). Its module captures the OAuth `insforge_code` query param synchronously at module-load time, BEFORE the InsForge SDK's `detectAuthCallback()` runs `cleanUrlParams("insforge_code")` to strip it. Lazy-loading delays the module until the route mounts, by which point the SDK has already wiped the URL — the captured code is `null` and the page falls through to the "Sign-in incomplete" failure state. Regression history: PR splitting the 1.9MB main bundle broke OAuth callback for every user until reverted for this one page. - -### Native ↔ web bridge - -``` -JS → window.webkit.messageHandlers.nativeBridge.postMessage({ type, key?, value?, name? }) -Swift → NativeBridge.shared.handle(message:) via WKScriptMessageHandler -Swift → JS: webView.evaluateJavaScript("window.dispatchEvent(new CustomEvent('native:settings', { detail }))") -React → useNativeSettings() subscribes -``` - -After `SMAppService.mainApp.register/unregister` from the bridge (not via `LaunchAtLoginManager.toggle`), call `launchAtLoginManager.refresh()` so the popover menu reflects `@Published isEnabled`. - -### Parser correctness - - **Parser dedup**: use `claudeMessageDedupKey()`. Bare `if (msgId && reqId)` fails open on DeepSeek/Kimi/Mimo/MiniMax/Claude sub-agents (no `reqId`) and over-counts 1.6–3.7×. - **Don't trust `input_tokens` semantics blindly** when adding a new provider. Codex/every-code's `input` includes cached tokens — naive copy inflates cost 6–7×. Verify with raw usage + provider billing dashboard before shipping. - **`contextTokensUsed`-style fields are usually snapshots, not cumulative.** PR #74 (Grok) shipped on that bad assumption. diff --git a/PRODUCT.md b/PRODUCT.md index cd488506..12f3d094 100644 --- a/PRODUCT.md +++ b/PRODUCT.md @@ -4,11 +4,11 @@ register: product ## Product purpose -Local-first AI token-usage tracker. Parses logs from AI coding CLIs (Claude Code, Codex, Cursor, Gemini, Copilot, Kimi, and more) into a local dashboard so developers can see how many tokens they burn, the estimated cost, and how it trends. Privacy-first: token counts only, never prompts or conversation bodies. Ships as a CLI (`serve` on :7680), a web dashboard (www.tokentracker.cc), and a self-contained macOS menu-bar app. +Local-first AI token-usage tracker. Parses logs from AI coding CLIs (Claude Code, Codex, Cursor, Gemini, Copilot, Kimi, and more) into a local dashboard so developers can see how many tokens they burn, the estimated cost, and how it trends. Privacy-first: token counts only, never prompts or conversation bodies. Ships as a CLI (`serve` on :7680) with a dashboard it serves locally, plus a self-contained macOS menu-bar app. Nothing is uploaded: there is no account, no sync, and no server component. ## Users -Developers and AI-power-users who run multiple agent CLIs daily and want a single, trustworthy view of consumption and cost. They are fluent in tools like Linear, Raycast, Vercel, and GitHub. They check usage both at a desk (deep review) and on a phone (quick glance: "how much did I burn today / where am I on the leaderboard"). They distrust inflated numbers, so accuracy and legible key metrics matter more than decoration. +Developers and AI-power-users who run multiple agent CLIs daily and want a single, trustworthy view of consumption and cost. They are fluent in tools like Linear, Raycast, Vercel, and GitHub. They check usage at a desk for deep review and at a glance for "how much did I burn today". They distrust inflated numbers, so accuracy and legible key metrics matter more than decoration. ## Tone & principles diff --git a/README.md b/README.md index cd5e1bb5..342a7e93 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ tokentracker doctor # health check - 🔌 **22 tools out of the box.** Claude Code, Codex, Cursor, Gemini, Copilot, Antigravity, OpenCode, Kiro, Zed, Goose, and more — auto-detected. - 💰 **Accurate cost.** 2,200+ models priced from [LiteLLM](https://github.com/BerriAI/litellm) (refreshed daily) with a bundled offline snapshot, so USD totals are right even without a network. Cross-provider records are de-duplicated so your numbers match each provider's own billing. - 📈 **Rate-limit tracking.** Live quota windows with reset countdowns for Claude, Codex, Cursor, Gemini, Kimi, Z.AI, Kiro, Copilot, and Antigravity. -- 🧩 **Optional extras.** A worldwide leaderboard (opt-in) and a Skills tab that syncs 250+ public skills across your tools. +- 🧩 **Skills tab.** Syncs 250+ public skills across your tools. --- @@ -105,7 +105,7 @@ AI CLI tools → hooks / passive readers → local SQLite → dashboard 3. Counts are aggregated into 30-minute UTC buckets in a local SQLite snapshot. 4. The dashboard reads that snapshot and renders it in your browser's timezone. -The leaderboard is the only feature that ever leaves your machine, and only when you opt in. +Nothing leaves your machine. There is no account, no upload, and no server to sign in to. --- @@ -114,7 +114,7 @@ The leaderboard is the only feature that ever leaves your machine, and only when | Protection | What it means | |---|---| | **No content** | Only token counts and timestamps. Never prompts, responses, or files. | -| **Local by default** | All data stays on your machine. The leaderboard is fully opt-in. | +| **Local only** | All data stays on your machine. There is no upload path at all. | | **Auditable** | Open source — read [`src/lib/rollout.js`](src/lib/rollout.js); it's just numbers and timestamps. | | **No telemetry** | No analytics, no crash reporting, no phone-home. | diff --git a/dashboard/index.html b/dashboard/index.html index cc300bcb..4bcb183b 100644 --- a/dashboard/index.html +++ b/dashboard/index.html @@ -8,14 +8,10 @@ - - - - @@ -146,12 +142,6 @@ "name": "Home", "item": "https://www.tokentracker.cc/" }, - { - "@type": "ListItem", - "position": 2, - "name": "Leaderboard", - "item": "https://www.tokentracker.cc/leaderboard" - }, { "@type": "ListItem", "position": 3, @@ -227,7 +217,7 @@ "name": "Does Token Tracker work offline?", "acceptedAnswer": { "@type": "Answer", - "text": "Yes. Token Tracker is local-first. All parsing, aggregation, and dashboard rendering happens on your machine. Cloud sync is optional and used only for the public leaderboard." + "text": "Yes. Token Tracker is local-only. All parsing, aggregation, and dashboard rendering happens on your machine, and nothing is uploaded anywhere." } } ] @@ -260,7 +250,7 @@

What you get after installing

Token Tracker runs entirely on your machine. After npx tokentracker-cli, a local dashboard at http://localhost:7680 exposes the following views — your data stays on - your device unless you opt into cloud sync. + your device. There is no upload path.

-

Privacy by design: token counts only, never prompts or conversation content. Local-first; cloud sync is optional and powers only the public leaderboard.

+

Privacy by design: token counts only, never prompts or conversation content. Local-only — nothing is uploaded.

Supported AI coding agent CLIs