From 770c1f3d19843b2a21a9aaa17d69d85b229e05b6 Mon Sep 17 00:00:00 2001 From: Knowit Date: Sat, 18 Apr 2026 09:34:17 +0800 Subject: [PATCH] Add token-stats slash command --- README.md | 26 ++++++++++++++ commands/token-stats.md | 76 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 README.md create mode 100644 commands/token-stats.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..8eac9fd --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +# claudeplus + +Claude Code 自定义命令集合。 + +## 安装 + +将 `commands/` 目录下的文件复制到 `~/.claude/commands/`: + +```bash +mkdir -p ~/.claude/commands +cp commands/* ~/.claude/commands/ +``` + +重启 Claude Code 后即可使用。 + +## 命令列表 + +### /token-stats [username|all] + +统计本机 Claude token 用量,按日期分组展示。 + +``` +/token-stats # 当前用户 +/token-stats charles # 指定用户 +/token-stats all # 所有用户(含 root) +``` diff --git a/commands/token-stats.md b/commands/token-stats.md new file mode 100644 index 0000000..3e04757 --- /dev/null +++ b/commands/token-stats.md @@ -0,0 +1,76 @@ +--- +description: Show Claude token usage statistics per user. Usage: /token-stats [username|all] +argument-hint: [username|all] +allowed-tools: [Bash] +--- + +Show Claude token usage statistics from local session files. + +## Arguments + +The user invoked this command with: $ARGUMENTS + +## Instructions + +1. Parse the argument: + - No argument → use the current system user (`whoami`) as the target + - A specific username → use that username + - `all` → show stats for all users (including root) + +2. To resolve a user's home directory, use this logic: + - `root` → `/root` + - anything else → `/home/{user}` + +3. For a **single user**, run this Bash command (substitute `TARGET_USER` and `TARGET_HOME`): + +```bash +python3 << 'PYEOF' +import glob, json +from collections import defaultdict + +user = "TARGET_USER" +user_home = "TARGET_HOME" +stats = defaultdict(lambda: {'input': 0, 'output': 0, 'cache_write': 0, 'cache_read': 0, 'sessions': set()}) + +for f in glob.glob(f'{user_home}/.claude/projects/**/*.jsonl', recursive=True): + session_id = f.split('/')[-1].replace('.jsonl', '') + for line in open(f, errors='ignore'): + try: + d = json.loads(line) + u = d.get('message', {}).get('usage', {}) + if not u: + continue + ts = d.get('timestamp', '') + date = ts[:10] if ts else 'unknown' + stats[date]['input'] += u.get('input_tokens', 0) + stats[date]['output'] += u.get('output_tokens', 0) + stats[date]['cache_write'] += u.get('cache_creation_input_tokens', 0) + stats[date]['cache_read'] += u.get('cache_read_input_tokens', 0) + stats[date]['sessions'].add(session_id) + except: + pass + +if not stats: + print(f"No token data found for user: {user}") +else: + print(f"\n=== Token Usage: {user} ===\n") + print(f"{'Date':<12} {'Input':>12} {'Output':>12} {'Cache Write':>12} {'Cache Read':>12} {'Sessions':>9}") + print('-' * 72) + total_in = total_out = total_cw = total_cr = 0 + for date in sorted(stats.keys()): + s = stats[date] + total_in += s['input'] + total_out += s['output'] + total_cw += s['cache_write'] + total_cr += s['cache_read'] + print(f"{date:<12} {s['input']:>12,} {s['output']:>12,} {s['cache_write']:>12,} {s['cache_read']:>12,} {len(s['sessions']):>9}") + print('-' * 72) + print(f"{'Total':<12} {total_in:>12,} {total_out:>12,} {total_cw:>12,} {total_cr:>12,}") + print(f"\nActual tokens (input+output): {total_in + total_out:,}") + print(f"Cache reads (free on Max plan): {total_cr:,}") +PYEOF +``` + +4. For **all users**, collect home dirs from `/home/*/` plus `/root`, run the same logic for each, then print a grand total. + +5. Present output in a code block.