stats

📁 saturate/claude 📅 4 days ago
4
总安装量
3
周安装量
#48624
全站排名
安装命令
npx skills add https://github.com/saturate/claude --skill stats

Agent 安装分布

codex 3
opencode 2
gemini-cli 2
antigravity 2
claude-code 2
github-copilot 2

Skill 文档

You are querying Claude Code observability logs stored as daily-rotated JSONL files in ~/.claude/logs/.

Log Files

File pattern Contents
tool-usage-YYYY-MM-DD.jsonl Pre/post tool use events + tool failures
sessions-YYYY-MM-DD.jsonl Session start/end with token totals and cost
turns-YYYY-MM-DD.jsonl Per-turn token usage and cost
prompts-YYYY-MM-DD.jsonl User prompts with text and length
subagents-YYYY-MM-DD.jsonl Subagent start/stop with duration and tokens
compactions-YYYY-MM-DD.jsonl Context compaction events
notifications-YYYY-MM-DD.jsonl Notification events
permissions-YYYY-MM-DD.jsonl Permission request events

Date Ranges

Parse what the user asks for and build the appropriate file list:

  • Today: *-$(date +%Y-%m-%d).jsonl
  • This week: iterate from Monday to today
  • This month: iterate from 1st to today
  • Specific date: *-YYYY-MM-DD.jsonl
  • All time: *.jsonl in the logs directory

Use this pattern to cat multiple days:

cat ~/.claude/logs/sessions-2026-02-{01..23}.jsonl 2>/dev/null

Or for cross-month ranges, generate the dates with a loop.

Common Queries

Cost summary

Query turns-*.jsonl for real-time cost data (available during active sessions, not just after session_end):

cat ~/.claude/logs/turns-$(date +%Y-%m-%d).jsonl 2>/dev/null \
  | jq -s '{
    turns: length,
    total_cost_usd: (map(.estimated_cost_usd // 0) | add),
    total_input_tokens: (map(.usage.input_tokens // 0) | add),
    total_output_tokens: (map(.usage.output_tokens // 0) | add),
    total_cache_read: (map(.usage.cache_read_input_tokens // 0) | add),
    total_cache_create: (map(.usage.cache_creation_input_tokens // 0) | add),
    total_tools: (map(.tool_count // 0) | add),
    total_failures: (map(.tool_failures // 0) | add),
    sessions: (map(.session_id) | unique | length)
  }'

Cost by project

Uses turns data so active sessions are included:

cat ~/.claude/logs/turns-$(date +%Y-%m-%d).jsonl 2>/dev/null \
  | jq -s 'group_by(.project) | map({
    project: .[0].project,
    sessions: (map(.session_id) | unique | length),
    turns: length,
    cost_usd: (map(.estimated_cost_usd // 0) | add)
  }) | sort_by(-.cost_usd)'

Most-used tools (today)

cat ~/.claude/logs/tool-usage-$(date +%Y-%m-%d).jsonl 2>/dev/null \
  | jq -s 'map(select(.event == "post")) | group_by(.tool_name) | map({
    tool: .[0].tool_name,
    count: length,
    avg_duration_ms: (map(.duration_ms // 0) | if length > 0 then (add / length | floor) else 0 end)
  }) | sort_by(-.count)'

Tool failures

cat ~/.claude/logs/tool-usage-$(date +%Y-%m-%d).jsonl 2>/dev/null \
  | jq -s 'map(select(.event == "tool_failure")) | {
    total_failures: length,
    by_tool: (group_by(.tool_name) | map({tool: .[0].tool_name, count: length}) | sort_by(-.count)),
    interrupts: (map(select(.is_interrupt == true)) | length)
  }'

Session list

cat ~/.claude/logs/sessions-$(date +%Y-%m-%d).jsonl 2>/dev/null \
  | jq -s 'map(select(.event == "session_end")) | map({
    session_id: .session_id[:8],
    project: .project,
    model: .model,
    duration_min: ((.duration_s // 0) / 60 | floor),
    turns: .turn_count,
    tools: .tool_count,
    cost_usd: .estimated_cost_usd,
    cache_hit: ((.cache_hit_rate // 0) * 100 | floor | tostring + "%")
  })'

Current session

If the user asks about “this session” or “current session”, get the session_id from the environment or from the most recent session_start entry, then filter all log files by that session_id.

# Get current/latest session ID
SID=$(cat ~/.claude/logs/sessions-$(date +%Y-%m-%d).jsonl 2>/dev/null \
  | jq -r 'select(.event == "session_start") | .session_id' | tail -1)

# Get turns for this session
cat ~/.claude/logs/turns-$(date +%Y-%m-%d).jsonl 2>/dev/null \
  | jq -s --arg sid "$SID" '[.[] | select(.session_id == $sid)] | {
    turns: length,
    total_cost_usd: (map(.estimated_cost_usd // 0) | add),
    total_tools: (map(.tool_count // 0) | add),
    total_failures: (map(.tool_failures // 0) | add)
  }'

Subagent usage

cat ~/.claude/logs/subagents-$(date +%Y-%m-%d).jsonl 2>/dev/null \
  | jq -s 'map(select(.event == "subagent_stop")) | {
    total: length,
    by_type: (group_by(.agent_type) | map({type: .[0].agent_type, count: length, avg_duration_ms: (map(.duration_ms // 0) | add / length | floor)})),
    total_cost_usd: (map(.estimated_cost_usd // 0) | add)
  }'

Compaction pressure

cat ~/.claude/logs/compactions-$(date +%Y-%m-%d).jsonl 2>/dev/null \
  | jq -s '{
    total_compactions: length,
    by_session: (group_by(.session_id) | map({session: .[0].session_id[:8], count: length}) | sort_by(-.count))
  }'

Output Format

Present results as a clean markdown table or summary. Keep it concise. Round costs to 4 decimal places USD. Round percentages to whole numbers. Use human-readable durations (e.g., “12 min” not “720s”).

If no data exists for the requested period, say so clearly rather than showing empty results.

If the user doesn’t specify a time range, default to today.

Arguments

Users may invoke this as:

  • /stats — show today’s cost summary
  • /stats week — this week’s summary
  • /stats tools — most-used tools today
  • /stats sessions — list today’s sessions
  • /stats cost march — cost for March
  • Or ask naturally: “how much have I spent this week?”

Parse the intent from their message and run the appropriate queries.