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. セッションごとのコストの取得
ccusage の ccusage 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. セッション索引を作る
#!/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 の出力と突き合わせる
#!/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.json は ccusage 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 名や冒頭の文言)でグルーピングすると見えやすくなります。
#!/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"の中にもperiodがwf_...という接頭辞を持つセッションがあり、これは Workflow 機能のサブエージェント実行に紐づくもので~/.claude/projects/のトップレベル jsonl としては記録されません。自分の環境で unknown が多いときは、まずs.get("agent")で内訳を見ると原因の切り分けが早くなります。 -
~/.claude/projects/配下の jsonl にはプロンプトの本文がそのまま入っています。索引ファイル(session_index.jsonlやccusage_session.json)を社外に出したり、無関係な人と共有したりしないよう注意してください。
