環境
- Ubuntu Linux 24.04LTS
- Claude Code
何か
Claude Code のセッション終了のたびに、当該セッションログ(~/.claude/projects/<project>/<session-id>.jsonl)を、
プレーンテキストに変換して書き出します。
もともとは Evernote のノートに流し込みたくて、
クリップボード経由や、 msmtp等とEvernote のメール転送機能(xxxx@m.evernote.com 宛にメールを送るとノートになる機能)を使う想定でしたが、
メール本文に直接書いても添付にしても改行が消えてしまい実用にならなかったため、
代わりに「テキストファイルとして書き出し、Evernote 側で手動で取り込む」という運用にしています。
できること
- Claude Code の SessonEnd (/logout) ごとに当該セッションIDをファイル名に含んだファイルを /tmp に書き出す。
- JSONなのでプレーンテキストに変換してから書き出す。
- 同じセッションの場合は黙って上書き。
機能詳細
-
claude-log-to-txt.py— 変換本体。標準入力から Claude Code のセッションログ(JSONL)を読み、1行目にタイトル・空行を挟んで書き起こし本文を続けた、整形済みプレーンテキストを標準出力に書きます。副作用の無い純粋なフィルタです。 -
claude-log-to-txt.sh— 上記スクリプトをラップし、標準出力に結果を流しつつ、同じ内容を/tmp/claude-log-<session-id>.txtにも保存します。同名ファイルが既にあれば黙って上書きします。 -
SessionEndhook との連携 — Claude Code のSessionEndhook から自動起動できる設定例(claude-log-settings.json)を同梱しています。これを使うと、セッション終了のたびに自動でテキストファイルが/tmp配下に保存されます。
コード
claude-log-to-txt.py
# -*- coding: utf-8 -*-
"""claude session log to text
- claude session log (jsonl) to plane text.
- claude session log from stdin.
- session title is 1 line, blank line, folllow body.
- to stdout
"""
import json, sys
from datetime import datetime, timezone
title = None
body = []
for line in sys.stdin:
d = json.loads(line)
role = d.get('type')
if role == 'ai-title' and title is None:
title = d.get('aiTitle')
continue
if role in ('user', 'assistant'):
msg = d.get('message', {})
content = msg.get('content')
ts = d.get('timestamp')
if ts:
dt = datetime.fromisoformat(ts.replace('Z', '+00:00')).astimezone()
ts_str = dt.strftime('%Y-%m-%d %H:%M:%S')
else:
ts_str = '?'
if isinstance(content, str):
body.append(f'[{ts_str}] [{role}] {content}\n')
elif isinstance(content, list):
for c in content:
ctype = c.get('type')
if ctype == 'text':
body.append(f'[{ts_str}] [{role}] {c["text"]}\n')
elif ctype == 'tool_use':
name = c.get('name')
tool_input = json.dumps(c.get('input', {}), ensure_ascii=False, indent=2)
body.append(f'[{ts_str}] [{role}] tool_use: {name}\n{tool_input}\n')
elif ctype == 'tool_result':
result_content = c.get('content')
if isinstance(result_content, list):
result_text = '\n'.join(
rc.get('text', '') for rc in result_content
if isinstance(rc, dict) and rc.get('type') == 'text'
)
else:
result_text = result_content or ''
body.append(f'[{ts_str}] [{role}] tool_result:\n{result_text}\n')
# 1行目にタイトル、空行を1行挟んでから続きを書く
print(title or '')
print()
for entry in body:
print(entry)
claude-log-to-txt.sh
#!/bin/bash
# claude-log-to-txt.py をラップし、stdout に整形済みテキストを流しつつ
# 同じ内容をテンポラリファイルにも保存する。
# stdout をそのまま msmtp にパイプできるよう、保存先パスは stderr に出す。
# 第1引数にセッションIDを渡すと /tmp/claude-log-<session-id>.txt に保存する
# (同名ファイルがあれば黙って上書き)。省略時はタイムスタンプ付きファイル名にする。
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
session_id="${1:-}"
if [ -n "$session_id" ]; then
outpath="/tmp/claude-log-${session_id}.txt"
else
outpath="/tmp/claude-log-$(date +%Y%m%d-%H%M%S).txt"
fi
python3 "$SCRIPT_DIR/claude-log-to-txt.py" | tee "$outpath"
echo "saved: $outpath" >&2
claude-log-settings.
- 実際には
~/.claude/settings.jsonに配置される。 - また、起動するスクリプトは下記では
~/binに配置される
{
"hooks": {
"SessionEnd": [
{
"hooks": [
{
"type": "command",
"command": "jq -r '.session_id, .transcript_path' | { read -r sid; read -r f; \"$HOME/bin/claude-log-to-txt.sh\" \"$sid\" < \"$f\"; } 2>/dev/null || true"
}
]
}
]
},
"theme": "dark"
}
設定方法
SessionEnd hook 経由で自動変換するようにする。
a. claude-log-to-txt.sh と claude-log-to-txt.py を PATH の通った場所に配置します。この例では ~/bin/ にコピーする運用にしています。
```bash
cp claude-log-to-txt.sh claude-log-to-txt.py ~/bin/
```
b. リポジトリ内の claude-log-settings.json の hooks.SessionEnd の内容を、実際の ~/.claude/settings.json にマージします。例えば以下です。
```json:settings.json
{
"hooks": {
"SessionEnd": [
{
"hooks": [
{
"type": "command",
"command": "jq -r '.session_id, .transcript_path' | { read -r sid; read -r f; \"$HOME/bin/claude-log-to-txt.sh\" \"$sid\" < \"$f\"; } 2>/dev/null || true"
}
]
}
]
}
}
```
使い方
-
hook を設定していれば、Claude Code のセッションが終了するたびに自動で
/tmp/claude-log-<session-id>.txtが生成されます。同じセッションを resume, continue した場合は同じセッションIDのファイルに黙って上書きします。 -
直接実行(標準出力のみ):
python3 claude-log-to-txt.py < ~/.claude/projects/<project-dir>/<session-id>.jsonl -
ラッパー(teeで標準出力 +
/tmpへの保存):./claude-log-to-txt.sh <session-id> < ~/.claude/projects/<project-dir>/<session-id>.jsonlセッション ID を省略するとタイムスタンプ付きのファイル名(
/tmp/claude-log-YYYYMMDD-HHMMSS.txt)になります。
注意
Claude Code のセッションログ形式は保証されてないので変更があると使えなくなります。