2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ccusage と Claude Code のログを紐づけて、「どんなプロンプトがトークン消費が重いか」を調べてもらう

2
Last updated at Posted at 2026-08-31

Claude Code などのコーディングエージェントをどのぐらい使ったかを調べるのに ccusage が便利です。

期間単位、セッション (一連の AI との対話のことです) 単位とかで利用トークンや (API 料金換算での) 金額とかをこんな感じ (↓) で出してくれるのですが、
ただ ccusage 単体だと、Session ID までしか分からず、トークン消費が多いセッションがどんなセッションだったかというのを実態としてつかみにくいです。

ということでこの記事では、ccusage の出力と Claude Code のログを紐づけて調査する方法について説明します。

$ npx ccusage@latest session

╭─────────────────────────────────────────────╮
│                                             │
│  Coding (Agent) CLI Usage Report - Session  │
│  Detected: Claude, Codex, Goose, OpenCode   │
│                                             │
╰─────────────────────────────────────────────╯

┌───────────────────────┬────────────┬───────────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┐
│ Session               │ Agent      │ Models        │    Input │   Output │    Cache │    Cache │    Total │     Cost │
│                       │            │               │          │          │   Create │     Read │   Tokens │    (USD) │
├───────────────────────┼────────────┼───────────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤
│ a1b2c3d4-1111-4aaa-9… │ Claude     │ - opus-5      │      363 │  206,603 │  563,141 │ 29,384,… │ 30,154,… │   $21.93 │
│                       │            │ - sonnet-5    │          │          │          │          │          │          │
├───────────────────────┼────────────┼───────────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤
│ b2c3d4e5-2222-4bbb-8… │ Claude     │ - sonnet-5    │       72 │   27,888 │  103,334 │ 3,630,7… │ 3,762,0… │    $1.42 │
├───────────────────────┼────────────┼───────────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤
│ c3d4e5f6-3333-4ccc-7… │ Claude     │ - haiku-4-5   │      498 │  126,208 │  495,903 │ 17,988,… │ 18,610,… │    $5.64 │
│                       │            │ - sonnet-5    │          │          │          │          │          │          │
├───────────────────────┼────────────┼───────────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤
│ d4e5f6a7-4444-4ddd-6… │ Claude     │ - sonnet-5    │      206 │  165,611 │  343,292 │ 26,369,… │ 26,878,… │    $8.30 │
├───────────────────────┼────────────┼───────────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤
│ e5f6a7b8-5555-4eee-5… │ Claude     │ - fable-5     │  150,123 │   38,823 │  295,498 │ 7,251,4… │ 7,735,8… │    $4.46 │
│                       │            │ - sonnet-5    │          │          │          │          │          │          │
└───────────────────────┴────────────┴───────────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┘

Session ID に対応する Claude Code のログの探し方

Claude Code のセッションの情報は、セッション毎に以下のパスで JSONL ファイル (https://jsonlines.org/) が作られています。

~/.claude/projects/<cwdをエスケープした名前>/<session-id>.jsonl

ちなみに subagent のログは以下のパスにあります。

~/.claude/projects/<cwdをエスケープした名前>/<session-id>/subagents/agent-<Agent ID?>.jsonl

ファイルの中身としてはこんな感じです。メッセージやメタデータ的情報まで、様々な情報がこのファイルに保存されています。

{"type": "mode", ...}
{"type": "permission-mode", ...}
{"type": "file-history-snapshot", ...}
{"type": "user", "isSidechain": false, "origin": {"kind": "human"}, "timestamp": "...", "cwd": "...", "gitBranch": "..."}

メッセージに当たる情報はこんな感じで入ってます。

{
  "type": "user",
  "timestamp": "2026-08-27T05:54:49.096Z",
  "cwd": "/Users/you/worktrees/main-app/feature-xyz",
  "gitBranch": "feature-xyz",
  "sessionId": "05b32bf0-6c38-4695-9a94-0ec4cfce7206",
  "message": { "role": "user", "content": "..." }
}

JSONL は jq コマンド (https://jqlang.org/) で解析できるので例えば以下のコマンドで最初のメッセージを取り出すことが出来ます。

jq -nc 'first(inputs | select(.type == "user"))' CLAUDE_LOG_PATH

メッセージのデータとしては、ユーザーが実際に入力したプロンプトやディレクトリ、git branch の情報が含まれているため、 Session ID を元に、最初のユーザーメッセージのデータを取ってきて、AI にそれらを解析してもらえば、どういうセッションがトークン消費が激しいかなどを分析してもらうことが出来ます。
(npx ccusage@latest session --json などで json 形式で出力できるので、ccusage のログ自体も機械的な分析は十分可能です。)

AI に「どういうセッションがトークンを消費しているか」調査してもらう

ということで、いざ実践なのですが、流石に 2026 年に手でスクリプト色々書きながら分析するのは大変なので AI にやってもらうことにしましょう。

自分も AI にスクリプト作ってもらってやっているので、この記事の残りはそれを貼ります。
例えば、以下のプロンプトを投げれば、AI が分析してくれます。

https://qiita.com/tomoasleep/items/e9e66d8ca90fdb5c9af4 を参考に、
直近1週間のトークン使用量をプロジェクト、プロンプトの種類ごとに集計し、レポートにしてほしい

例えばこんな感じでまとめてくれます。自分の場合だと Claude Code Routine を多用しているので、「どの Routine が一番トークンを消費しているか」みたいなのをまとめてもらっています。

Routine 経由の情報とかもデータとして入っているので、Routine (scheduled-task) 毎にこんな感じで分類してくれます。
(※ 実際の出力にいくつかフェイクを入れているので細かい整合性が取れてないですが、大体こんな感じで出せます)

プロジェクト別(全体に対する割合)

┌───────┬───────────┬──────────────┬────────────────────────────────┐
│ 割合   │ コスト($)  │ セッション数   │ プロジェクト                     │
├───────┼───────────┼──────────────┼────────────────────────────────┤
│ 28.0% │ 3857.98   │ 1974         │ (unknown / 他エージェント経由)    │
│ 18.1% │ 2492.02   │ 1840         │ notes-and-automation           │
│ 16.4% │ 2258.20   │ 1666         │ internal-wiki-sync             │
│ 11.3% │ 1551.97   │ 1319         │ agent-experiments              │
│ 10.9% │ 1494.27   │ 1174         │ main-app/feature-a             │
│ 5.2%  │ 714.63    │ 766          │ main-app/feature-b             │
│ 4.8%  │ 663.49    │ 380          │ main-app/feature-c             │
│ 4.5%  │ 624.45    │ 259          │ cli-tool-fork/feature-d        │
│ 0.6%  │ 77.11     │ 158          │ main-app/feature-e             │
│ 0.2%  │ 21.35     │ 153          │ main-app/feature-f             │
└───────┴───────────┴──────────────┴────────────────────────────────┘

notes-and-automation 内訳(プロジェクト内シェア)

┌───────┬───────────┬──────┬───────────────────────────────────┐
│ 割合   │ コスト($)  │ 回数 │ 内容                               │
├───────┼───────────┼──────┼───────────────────────────────────┤
│ 19.5% │ 239.56    │ 49   │ scheduled-task: inbox-triage      │
│ 17.4% │ 212.96    │ 22   │ scheduled-task: dependency-review │
│ 14.3% │ 175.37    │ 18   │ scheduled-task: doc-sync          │
│ 14.0% │ 171.25    │ 14   │ scheduled-task: incident-watch    │
│ 13.2% │ 161.28    │ 34   │ scheduled-task: weekly-retro      │
│ 10.8% │ 132.26    │ 5    │ scheduled-task: release-notes     │
│ 4.5%  │ 54.70     │ 5    │ 見積もり資料の下書き                 │
│ 3.7%  │ 45.33     │ 18   │ scheduled-task: daily-digest      │
│ 2.0%  │ 24.76     │ 3    │ 議事録の整理                        │
│ 0.7%  │ 8.70      │ 5    │ 障害対応の一時調査                   │
└───────┴───────────┴──────┴───────────────────────────────────┘

scheduled-task の積み重ねがこのプロジェクトのコストの多くを占めていて、手動で頼んだ単発の調査タスクよりも効いています。金額ベースでも $ は API 換算なので、サブスク上での重みという意味ではこの % の並びをそのまま「どこに配分されているか」として見るのが妥当です。

ということで、以下自分が使っている AI 向けの説明資料です。めっちゃ長いので折りたたんでおきます。この記事を AI に食わせると似たような分析をやってくれます。
(説明資料は AI に書いてもらいました)


↓ ここから (ほぼ) AI 生成 ↓

分析する方法 (※ AI 生成)

分析する方法 (※ AI 生成)

0. セッションごとのコストの取得

ccusageccusage session --json はセッションごとのコストを教えてくれます。

$ npx -y ccusage@latest session --json
...
{
  "period": "9bfcc42e-2d28-4369-9002-db251eb3bdaf",
  "totalCost": 45.45,
  "totalTokens": ...,
  "metadata": { "lastActivity": "2026-08-20T03:02:26.743Z" }
}

⚠️ 注意: ccusage の $ 表示は従量課金レートで計算した金額です。Pro/Max/Team のようなサブスクリプションプランで使っている場合、実際にその額を払っているわけではありません。サブスク利用度合いを見たいときは、絶対額そのものより「全体に対する割合(%)」で見るほうが実態に近いです。この記事のスクリプトの出力にも割合を入れています。

ccusage session --json の各要素の period フィールドが session id です。コマンド名が session なのに period という名前なのはやや紛らわしいですが、値を見ると Claude Code のセッションなら UUID 形式の文字列です。ccusage はトークン使用量ログからコストを計算しているだけなので、そのセッションで何をしていたかという情報は持っていません。

一方 Claude Code は CLI から使うと、セッションのやり取りをまるごとローカルの JSON Lines ファイルとして保存しています。

~/.claude/projects/<cwdをエスケープした名前>/<session-id>.jsonl

このファイルを読めば、「そのセッションが何だったか」を後から復元できます。

1. セッション索引を作る

build_session_index.py
#!/usr/bin/env python3
"""
Build a static index of Claude Code sessions: session_id, start_time, cwd, gitBranch, first_prompt.
Scans ~/.claude/projects/*/<session-id>.jsonl (top-level session files only, not subagents/).
Only reads until the first qualifying "user" entry is found, then moves on (fast).
"""
import json
import glob
import os
import sys

PROJECTS_DIR = os.path.expanduser("~/.claude/projects")

def first_prompt_text(message_content):
    if isinstance(message_content, str):
        return message_content
    if isinstance(message_content, list):
        for block in message_content:
            if isinstance(block, dict) and block.get("type") == "text":
                return block.get("text", "")
    return ""

def index_file(path):
    session_id = os.path.basename(path)[: -len(".jsonl")]
    try:
        with open(path, "r", errors="replace") as f:
            for line in f:
                line = line.strip()
                if not line:
                    continue
                try:
                    d = json.loads(line)
                except json.JSONDecodeError:
                    continue
                if d.get("type") != "user":
                    continue
                # skip sidechain (subagent-internal) turns and tool-result-only turns
                if d.get("isSidechain") is True:
                    continue
                origin = d.get("origin") or {}
                if isinstance(origin, dict) and origin.get("kind") not in (None, "human"):
                    continue
                msg = d.get("message", {})
                content = msg.get("content")
                text = first_prompt_text(content)
                if not text.strip():
                    continue
                return {
                    "session_id": session_id,
                    "timestamp": d.get("timestamp"),
                    "cwd": d.get("cwd"),
                    "gitBranch": d.get("gitBranch"),
                    "prompt": text.strip().replace("\n", " ")[:200],
                }
    except OSError:
        return None
    return None

def main():
    entries = []
    for path in glob.glob(os.path.join(PROJECTS_DIR, "*", "*.jsonl")):
        entry = index_file(path)
        if entry:
            entries.append(entry)
    entries.sort(key=lambda e: e["timestamp"] or "", reverse=True)
    out_path = sys.argv[1] if len(sys.argv) > 1 else None
    if out_path:
        with open(out_path, "w") as f:
            for e in entries:
                f.write(json.dumps(e, ensure_ascii=False) + "\n")
        print(f"wrote {len(entries)} entries to {out_path}")
    else:
        for e in entries:
            print(json.dumps(e, ensure_ascii=False))

if __name__ == "__main__":
    main()
$ python3 build_session_index.py session_index.jsonl
wrote 1010 entries to session_index.jsonl

これだけで、session id からタイトル的な情報を逆引きできる索引ファイルができます。

$ grep <session-id> session_index.jsonl
{"session_id": "05b32bf0-...", "timestamp": "2026-08-27T05:54:49.096Z", "cwd": "...", "gitBranch": "...", "prompt": "..."}

2. ccusage の出力と突き合わせる

session_cost_report.py
#!/usr/bin/env python3
"""
Join `ccusage session --json` output with a static index of Claude Code
session jsonl files (session_id -> timestamp, cwd, gitBranch, first prompt)
to see which sessions/projects are costing the most.

Usage:
  npx -y ccusage@latest session --json > ccusage_session.json
  python3 session_cost_report.py ccusage_session.json [--by-project] [--top N]
"""
import json
import glob
import os
import sys
import argparse

PROJECTS_DIR = os.path.expanduser("~/.claude/projects")


def first_prompt_text(message_content):
    if isinstance(message_content, str):
        return message_content
    if isinstance(message_content, list):
        for block in message_content:
            if isinstance(block, dict) and block.get("type") == "text":
                return block.get("text", "")
    return ""


def index_file(path):
    session_id = os.path.basename(path)[: -len(".jsonl")]
    try:
        with open(path, "r", errors="replace") as f:
            for line in f:
                line = line.strip()
                if not line:
                    continue
                try:
                    d = json.loads(line)
                except json.JSONDecodeError:
                    continue
                if d.get("type") != "user":
                    continue
                if d.get("isSidechain") is True:
                    continue
                origin = d.get("origin") or {}
                if isinstance(origin, dict) and origin.get("kind") not in (None, "human"):
                    continue
                msg = d.get("message", {})
                text = first_prompt_text(msg.get("content"))
                if not text.strip():
                    continue
                return {
                    "session_id": session_id,
                    "timestamp": d.get("timestamp"),
                    "cwd": d.get("cwd"),
                    "gitBranch": d.get("gitBranch"),
                    "prompt": text.strip().replace("\n", " ")[:150],
                }
    except OSError:
        return None
    return None


def build_index():
    idx = {}
    for path in glob.glob(os.path.join(PROJECTS_DIR, "*", "*.jsonl")):
        e = index_file(path)
        if e:
            idx[e["session_id"]] = e
    return idx


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("ccusage_json", help="path to `ccusage session --json` output")
    ap.add_argument("--by-project", action="store_true", help="aggregate cost by cwd instead of listing sessions")
    ap.add_argument("--top", type=int, default=30)
    args = ap.parse_args()

    with open(args.ccusage_json) as f:
        ccu = json.load(f)["session"]

    idx = build_index()

    rows = []
    for s in ccu:
        sid = s.get("period")
        meta = idx.get(sid)
        rows.append({
            "session_id": sid,
            "totalCost": s.get("totalCost", 0.0),
            "totalTokens": s.get("totalTokens", 0),
            "lastActivity": (s.get("metadata") or {}).get("lastActivity"),
            "modelsUsed": s.get("modelsUsed"),
            "cwd": meta["cwd"] if meta else None,
            "gitBranch": meta["gitBranch"] if meta else None,
            "prompt": meta["prompt"] if meta else None,
        })

    grand_total = sum(r["totalCost"] for r in rows) or 1.0

    if args.by_project:
        agg = {}
        for r in rows:
            key = r["cwd"] or "(unknown / non-CLI session)"
            a = agg.setdefault(key, {"cost": 0.0, "tokens": 0, "count": 0})
            a["cost"] += r["totalCost"]
            a["tokens"] += r["totalTokens"]
            a["count"] += 1
        for key, a in sorted(agg.items(), key=lambda kv: kv[1]["cost"], reverse=True)[: args.top]:
            pct = a["cost"] / grand_total * 100
            print(f"${a['cost']:>10.2f}  {pct:>5.1f}%  {a['count']:>4} sessions  {a['tokens']:>12,} tok  {key}")
        print(f"\ntotal: ${grand_total:.2f} across {len(rows)} sessions")
    else:
        rows.sort(key=lambda r: r["totalCost"], reverse=True)
        for r in rows[: args.top]:
            branch = f"[{r['gitBranch']}]" if r["gitBranch"] else ""
            pct = r["totalCost"] / grand_total * 100
            print(f"${r['totalCost']:>8.2f}  {pct:>5.1f}%  {r['lastActivity'] or '':<24}  {r['session_id']}")
            if r["cwd"]:
                print(f"           {r['cwd']} {branch}")
            if r["prompt"]:
                print(f"           > {r['prompt']}")
            print()


if __name__ == "__main__":
    main()

実行はこれだけです(出力の 2 列目が全体に対する割合です。サブスクで使っている場合はここを見てください)。

npx -y ccusage@latest session --json > ccusage_session.json

# コスト降順でセッション一覧(上位20件)
python3 session_cost_report.py ccusage_session.json --top 20

# cwd(プロジェクト)ごとに集計
python3 session_cost_report.py ccusage_session.json --by-project

ccusage のコスト計算の仕組み

totalCost がどこから来ているかも押さえておくと、数値の読み方を誤りません。ccusage のドキュメントによると、session --json の 1 セッションぶんの要素には inputTokens / outputTokens / cacheCreationTokens / cacheReadTokens / totalTokens / totalCost / modelsUsed / modelBreakdowns(モデルごとの内訳)が入ります。

コストの算出方法は --mode オプションで切り替えられ、3 種類あります。

  • auto(デフォルト):Claude Code のログに事前計算済みのコストが付いていればそれを使い、無ければトークン数から計算します。
  • calculate:事前計算済みの値があっても使わず、常にトークン数とモデル単価から計算し直します。
  • display:事前計算済みのコストだけを表示し、無ければ計算しません。

--mode auto のままで大きくずれることは少ないですが、モデル単価表が古い環境や、Claude Code のバージョン差でログの持ち方が変わった場合は --mode calculate で計算し直した値と突き合わせておくと安心です。

ccusage 側の便利オプション

上記スクリプトに渡す ccusage_session.jsonccusage session --json の出力そのままなので、ccusage 側のフィルタオプションと組み合わせられます(詳細: ccusage Session Reports ガイド)。

  • --since YYYYMMDD / --until YYYYMMDD: 期間で絞り込みます。「直近 1 週間だけ見たい」なら

    npx -y ccusage@latest session --json --since 20260820 --until 20260828 > ccusage_session_week.json
    python3 session_cost_report.py ccusage_session_week.json --by-project
    
  • --id <session-id>: 1 セッションだけに絞り込みます。出力形式が変わり、そのセッション内の各ターンのトークン内訳(entries 配列)が見られます。上のレポートで高コストな session id を見つけたら、次にこれで「具体的に何にトークンを使ったか」を確認する、という流れがおすすめです。

    npx -y ccusage@latest session --id 05b32bf0-6c38-4695-9a94-0ec4cfce7206 --json
    
  • --breakdown: モデル別のコスト内訳を表示します。

  • --mode auto|calculate|display: 前節のとおりコストの算出方法を切り替えます。

プロジェクト内の内訳を見たい場合

--by-project で「どのプロジェクトにコストがかかっているか」までは分かりますが、そのプロジェクトの中で「具体的にどんなプロンプトが上位を占めているか」まで見たい場合は、対象の cwd を絞り込んで、プロンプトの先頭パターン(scheduled task 名や冒頭の文言)でグルーピングすると見えやすくなります。

breakdown_project.py
#!/usr/bin/env python3
import json
import glob
import os
import re
import sys
from collections import defaultdict

PROJECTS_DIR = os.path.expanduser("~/.claude/projects")


def first_prompt_text(message_content):
    if isinstance(message_content, str):
        return message_content
    if isinstance(message_content, list):
        for block in message_content:
            if isinstance(block, dict) and block.get("type") == "text":
                return block.get("text", "")
    return ""


def index_file(path):
    session_id = os.path.basename(path)[: -len(".jsonl")]
    try:
        with open(path, "r", errors="replace") as f:
            for line in f:
                line = line.strip()
                if not line:
                    continue
                try:
                    d = json.loads(line)
                except json.JSONDecodeError:
                    continue
                if d.get("type") != "user":
                    continue
                if d.get("isSidechain") is True:
                    continue
                origin = d.get("origin") or {}
                if isinstance(origin, dict) and origin.get("kind") not in (None, "human"):
                    continue
                msg = d.get("message", {})
                text = first_prompt_text(msg.get("content"))
                if not text.strip():
                    continue
                return {
                    "session_id": session_id,
                    "timestamp": d.get("timestamp"),
                    "cwd": d.get("cwd"),
                    "gitBranch": d.get("gitBranch"),
                    "prompt": text.strip(),
                }
    except OSError:
        return None
    return None


def build_index():
    idx = {}
    for path in glob.glob(os.path.join(PROJECTS_DIR, "*", "*.jsonl")):
        e = index_file(path)
        if e:
            idx[e["session_id"]] = e
    return idx


def classify(prompt):
    m = re.search(r'scheduled-task name="([^"]+)"', prompt)
    if m:
        return f"scheduled-task: {m.group(1)}"
    m = re.search(r"^/([a-zA-Z0-9_-]+)", prompt)
    if m:
        return f"slash-command: /{m.group(1)}"
    first_line = prompt.split("\n", 1)[0].strip()
    return first_line[:60]


def main():
    ccusage_path, target_cwd = sys.argv[1], sys.argv[2]
    with open(ccusage_path) as f:
        ccu = json.load(f)["session"]
    idx = build_index()

    rows = []
    for s in ccu:
        sid = s.get("period")
        meta = idx.get(sid)
        if not meta or meta["cwd"] != target_cwd:
            continue
        rows.append({
            "session_id": sid,
            "totalCost": s.get("totalCost", 0.0),
            "totalTokens": s.get("totalTokens", 0),
            "lastActivity": (s.get("metadata") or {}).get("lastActivity"),
            "gitBranch": meta["gitBranch"],
            "prompt": meta["prompt"],
        })

    rows.sort(key=lambda r: r["totalCost"], reverse=True)

    print(f"=== top sessions in {target_cwd} ===")
    for r in rows[:20]:
        print(f"${r['totalCost']:>8.2f}  {r['lastActivity'] or '':<24}  {r['session_id']}  [{r['gitBranch']}]")
        print(f"           > {r['prompt'][:180].replace(chr(10), ' ')}")
        print()

    project_total = sum(r["totalCost"] for r in rows) or 1.0
    print(f"=== aggregated by prompt pattern (n={len(rows)} sessions, total ${project_total:.2f}) ===")
    agg = defaultdict(lambda: {"cost": 0.0, "count": 0})
    for r in rows:
        key = classify(r["prompt"])
        agg[key]["cost"] += r["totalCost"]
        agg[key]["count"] += 1
    for key, a in sorted(agg.items(), key=lambda kv: kv[1]["cost"], reverse=True)[:25]:
        pct = a["cost"] / project_total * 100
        print(f"${a['cost']:>9.2f}  {pct:>5.1f}%  {a['count']:>4} sessions  {key}")


if __name__ == "__main__":
    main()
python3 breakdown_project.py ccusage_session.json /Users/you/notes-and-automation

classify()<scheduled-task name="..."> を正規表現で拾っているので、自分の scheduled-tasks 構成に合わせてそのまま使えます。実際に手元の環境で 1 つのプロジェクトの内訳を見てみたところ、こんな結果になりました(金額とタスク名は説明用に丸め、置き換えたものです。比率と「何が効いているか」の傾向は実際のデータに基づいています)。

=== aggregated by prompt pattern (n=392 sessions, total $1784.30) ===
$   249.40  14.0%    29 sessions  scheduled-task: daily-planning
$   197.81  11.1%    49 sessions  scheduled-task: task-advice
$   161.77   9.1%    27 sessions  scheduled-task: task-triage
$   154.97   8.7%    24 sessions  scheduled-task: meeting-prep
$   111.76   6.3%    25 sessions  scheduled-task: wiki-improvement
$   106.43   6.0%    15 sessions  scheduled-task: standup-ingest
$    80.32   4.5%    35 sessions  scheduled-task: kanban-triage
...

scheduled task の合計だけで全体の 6 割強を占めていて、最上位のタスクが最も高コストでした(1 回あたりの単価も高めです)。「手動で頼んだ重い調査タスク」より「毎日回っているルーティンの積み重ね」のほうが効いている、という気づきが得られます。プロジェクトごとに何にコストがかかっているか気になったら、まずこの粒度で見るのがおすすめです。

注意点

  • --by-project の集計には (unknown / non-CLI session) という行がまとまった量で出てきます。最初は「使わなくなって削除した worktree のログが読めていないのでは」と疑いましたが、これは外れでした。worktree を git worktree remove で消しても ~/.claude/projects/ のログ自体はホームディレクトリ配下にあるので消えず、突合はできます。実際に手元で確認すると、原因は ccusage が Claude Code 専用のツールではなく、opencode や Codex CLI、goose といった他の AI コーディングエージェントのログも横断集計しているためでした。session --json の各要素には agent フィールドがあり、"claude" 以外の値を持つセッションは当然 ~/.claude/projects/ には存在しません。手元の環境では unknown の 9 割以上がこの他エージェント由来で、agent ごとに突合できた割合が全く違いました。少数ですが agent: "claude" の中にも periodwf_... という接頭辞を持つセッションがあり、これは Workflow 機能のサブエージェント実行に紐づくもので ~/.claude/projects/ のトップレベル jsonl としては記録されません。自分の環境で unknown が多いときは、まず s.get("agent") で内訳を見ると原因の切り分けが早くなります。
  • ~/.claude/projects/ 配下の jsonl にはプロンプトの本文がそのまま入っています。索引ファイル(session_index.jsonlccusage_session.json)を社外に出したり、無関係な人と共有したりしないよう注意してください。
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?