Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .github/assets/client-hermes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
| <img width="48px" src=".github/assets/client-opencode.png" alt="OpenCode" /> | [OpenCode](https://github.com/sst/opencode) | `~/.local/share/opencode/opencode.db` (1.2+) or/and `~/.local/share/opencode/storage/message/` (legacy/unmigrated) | ✅ Yes |
| <img width="48px" src=".github/assets/client-claude.jpg" alt="Claude" /> | [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | `~/.claude/projects/` | ✅ Yes |
| <img width="48px" src=".github/assets/client-openclaw.jpg" alt="OpenClaw" /> | [OpenClaw](https://openclaw.ai/) | `~/.openclaw/agents/` (+ legacy: `.clawdbot`, `.moltbot`, `.moldbot`) | ✅ Yes |
| <img width="48px" src=".github/assets/client-hermes.png" alt="Hermes Agent" /> | [Hermes Agent](https://github.com/NousResearch/hermes-agent) | `$HERMES_HOME/state.db` (fallback: `~/.hermes/state.db`) | ✅ Yes |
| <img width="48px" src=".github/assets/client-openai.jpg" alt="Codex" /> | [Codex CLI](https://github.com/openai/codex) | `~/.codex/sessions/` | ✅ Yes |
| <img width="48px" src=".github/assets/client-gemini.png" alt="Gemini" /> | [Gemini CLI](https://github.com/google-gemini/gemini-cli) | `~/.gemini/tmp/*/chats/*.json` | ✅ Yes |
| <img width="48px" src=".github/assets/client-cursor.jpg" alt="Cursor" /> | [Cursor IDE](https://cursor.com/) | API sync via `~/.config/tokscale/cursor-cache/` | ✅ Yes |
Expand Down Expand Up @@ -134,7 +135,7 @@ In the age of AI-assisted development, **tokens are the new energy**. They power
- GitHub-style contribution graph with 9 color themes
- Real-time filtering and sorting
- Zero flicker rendering
- **Multi-platform support** - Track usage across OpenCode, Claude Code, Codex CLI, Cursor IDE, Gemini CLI, Amp, Droid, OpenClaw, Pi, Kimi CLI, Qwen CLI, Roo Code, Kilo, Mux, Kilo CLI, Crush, and Synthetic
- **Multi-platform support** - Track usage across OpenCode, Claude Code, Codex CLI, Cursor IDE, Gemini CLI, Amp, Droid, OpenClaw, Hermes Agent, Pi, Kimi CLI, Qwen CLI, Roo Code, Kilo, Mux, Kilo CLI, Crush, and Synthetic
- **Real-time pricing** - Fetches current pricing from LiteLLM with 1-hour disk cache; automatic OpenRouter fallback and Cursor model pricing for newly released models
- **Detailed breakdowns** - Input, output, cache read/write, and reasoning token tracking
- **Native Rust core** - All parsing and aggregation done in Rust for 10x faster processing
Expand Down Expand Up @@ -307,6 +308,9 @@ tokscale --droid
# Show only OpenClaw usage
tokscale --openclaw

# Show only Hermes Agent usage
tokscale --hermes

# Show only Pi usage
tokscale --pi

Expand Down Expand Up @@ -583,7 +587,7 @@ The frontend provides a GitHub-style contribution graph visualization:
- **Interactive tooltips**: Hover for detailed daily breakdowns
- **Day breakdown panel**: Click to see per-source and per-model details
- **Year filtering**: Navigate between years
- **Source filtering**: Filter by platform (OpenCode, Claude, Codex, Cursor, Gemini, Amp, Droid, OpenClaw, Pi, Kimi, Qwen, Roo Code, Kilo, Mux, Kilo CLI, Crush, Synthetic)
- **Source filtering**: Filter by platform (OpenCode, Claude, Codex, Cursor, Gemini, Amp, Droid, OpenClaw, Hermes Agent, Pi, Kimi, Qwen, Roo Code, Kilo, Mux, Kilo CLI, Crush, Synthetic)
- **Stats panel**: Total cost, tokens, active days, streaks
- **FOUC prevention**: Theme applied before React hydrates (no flash)

Expand Down Expand Up @@ -875,6 +879,7 @@ AI coding tools store their session data in cross-platform locations. Most tools
| OpenCode | `~/.local/share/opencode/` | `%USERPROFILE%\.local\share\opencode\` | Uses [`xdg-basedir`](https://github.com/sindresorhus/xdg-basedir) for cross-platform consistency ([source](https://github.com/sst/opencode/blob/main/packages/opencode/src/global/index.ts)) |
| Claude Code | `~/.claude/` | `%USERPROFILE%\.claude\` | Same path on all platforms |
| OpenClaw | `~/.openclaw/` (+ legacy: `.clawdbot`, `.moltbot`, `.moldbot`) | `%USERPROFILE%\.openclaw\` (+ legacy paths) | Same path on all platforms |
| Hermes Agent | `~/.hermes/` | `%USERPROFILE%\.hermes\` | Configurable via `HERMES_HOME` env var ([source](https://github.com/NousResearch/hermes-agent/blob/main/website/docs/developer-guide/session-storage.md)) |
| Codex CLI | `~/.codex/` | `%USERPROFILE%\.codex\` | Configurable via `CODEX_HOME` env var ([source](https://github.com/openai/codex)) |
| Gemini CLI | `~/.gemini/` | `%USERPROFILE%\.gemini\` | Same path on all platforms |
| Amp | `~/.local/share/amp/` | `%USERPROFILE%\.local\share\amp\` | Uses `xdg-basedir` like OpenCode |
Expand Down Expand Up @@ -1051,6 +1056,12 @@ Session JSONL format with model_change events and assistant messages:
{"type":"message","message":{"role":"assistant","usage":{"input":1660,"output":55,"cacheRead":108928,"cost":{"total":0.02}},"timestamp":1769753935279}}
```

### Hermes Agent

Location: `$HERMES_HOME/state.db` (fallback: `~/.hermes/state.db`)

Hermes stores session-level usage in a SQLite `sessions` table. Tokscale imports rows where `model` is present and token or cost totals are non-zero, uses `started_at` as the timestamp, preserves `message_count`, and prefers `actual_cost_usd` over `estimated_cost_usd`.

### Pi

Location: `~/.pi/agent/sessions/<encoded-cwd>/*.jsonl`
Expand Down
22 changes: 22 additions & 0 deletions crates/tokscale-cli/src/commands/wrapped.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1357,6 +1357,7 @@ fn client_display_name(client: &str) -> Option<&'static str> {
"amp" => Some("Amp"),
"droid" => Some("Droid"),
"openclaw" => Some("OpenClaw"),
"hermes" => Some("Hermes Agent"),
"pi" => Some("Pi"),
"kimi" => Some("Kimi CLI"),
"qwen" => Some("Qwen CLI"),
Expand All @@ -1380,6 +1381,7 @@ fn client_logo_url(client_name: &str) -> Option<&'static str> {
"Amp" => Some("https://tokscale.ai/assets/logos/amp.png"),
"Droid" => Some("https://tokscale.ai/assets/logos/droid.png"),
"OpenClaw" => Some("https://tokscale.ai/assets/logos/openclaw.png"),
"Hermes Agent" => Some("https://tokscale.ai/assets/logos/hermes.png"),
"Pi" => Some("https://tokscale.ai/assets/logos/pi.png"),
"Kimi CLI" => Some("https://tokscale.ai/assets/logos/kimi.png"),
"Qwen CLI" => Some("https://tokscale.ai/assets/logos/qwen.png"),
Expand Down Expand Up @@ -1732,6 +1734,7 @@ fn default_clients() -> Vec<String> {
"amp".to_string(),
"droid".to_string(),
"openclaw".to_string(),
"hermes".to_string(),
"pi".to_string(),
]
}
Expand Down Expand Up @@ -2186,6 +2189,11 @@ mod tests {
assert_eq!(client_display_name("openclaw"), Some("OpenClaw"));
}

#[test]
fn test_client_display_name_hermes() {
assert_eq!(client_display_name("hermes"), Some("Hermes Agent"));
}

#[test]
fn test_client_display_name_pi() {
assert_eq!(client_display_name("pi"), Some("Pi"));
Expand All @@ -2208,6 +2216,12 @@ mod tests {
assert_eq!(client_display_name("Claude"), None); // case-sensitive
}

#[test]
fn test_default_clients_includes_hermes() {
let clients = default_clients();
assert!(clients.iter().any(|client| client == "hermes"));
}

// ========== client_logo_url tests ==========

#[test]
Expand Down Expand Up @@ -2274,6 +2288,14 @@ mod tests {
);
}

#[test]
fn test_client_logo_url_hermes() {
assert_eq!(
client_logo_url("Hermes Agent"),
Some("https://tokscale.ai/assets/logos/hermes.png")
);
}

#[test]
fn test_client_logo_url_pi() {
assert_eq!(
Expand Down
Loading