
Claude Code のセッションログから日報と予実用の稼働記録を分けて作るスキルを作ってみた
こんにちは。クラウド事業統括本部コンサルティング1部の桑野です。
皆さんは日報や予実管理を毎日欠かさず登録することができているでしょうか?
私はできていませんでした。
日報や予実(予定・実績)は基本的に毎日つけるものだとは考えているのですが、どうしてもその時間を捻出するのが難しくて悩んでしました。。
私は普段、打ち合わせの管理に Google カレンダー、他メンバーとのやり取りに Slack、コード管理に Git を使っています。加えて、業務のほとんどを Claude Code の上で行っています。これらに残っている記録をもとに、普段の日報作成や稼働実績の集計を楽にできないか考えてみました。
今回は、日報と予実を Claude Code のスキルとして自動生成する仕組みを作ったので、その内容を共有します。
この記事で紹介するもの
Google カレンダー・Slack・Git log・Claude Code のセッションログから一次情報を集め、その日の日報と稼働実績を出力し、稼働実績は工数管理システム向けのCSVに変換できるようにしています。
この記事の中心は、日報と予実を1つの成果物にまとめず、あえて分けて作っているという点です。この2つは似ているようで役割も目的も違うため、分けたほうが運用しやすいというのが今回の主題になります。
あわせて、複数のツールに散らばった記録を1つの日報にまとめるときの集め方・確認の進め方や、AI に記録を書かせるときに推測で埋めさせないための書き方についても触れます。
後半では番外編として、ログの残し方、データ形式に JSONL・設定ファイルに JSONC を選んだ理由、設定をどこに置くかといった、実装するうえで検討した部分もまとめています。
なお、これは筆者の環境に合わせて作ったものなので、そのまま全ての方に当てはまるわけではありません。ただ、複数のソースを並列に見にいく設計にしているため、普段からカレンダーや Slack を活用されている方であれば、Claude Code を使っていなくても考え方は流用いただけるかと思います。
前提
作成物は以下のリポジトリにあります。本記事と併せてご覧ください。
以下の環境で動かしています。
- OS:macOS Tahoe バージョン 26.5.2
- チップ:Apple M4
- Claude Code
- 任意: Google カレンダー・Slack の MCP 連携(日報の材料として使用)
- 任意: Python 3 /
jq(Claude Code のセッションログを保存するフックが使用)
情報源はいずれも任意です。全て揃っていれば日報の情報量が増えますが、揃っていなくても取得できたソースの範囲で日報は作られます。
作る前に整理したこと
いきなり作り始める前に、日報と予実に何を求めているのかを整理しました。
日報と予実は役割・目的が違う点
まず整理したのが、日報と予実の役割の違いです。ここは人によって捉え方が違う部分だと思いますが、私は次のように考えています。
予実(工数実績)は、案件ごとの稼働時間を工数管理システムに登録するためのものです。必要なのはたいていの場合「どの作業コードに何時間」という数字で、粒度も一般的には15分刻み程度で足りるかと思います(このあたりは利用するシステムによって異なります)。ただし、毎日つけないと後から思い出すのが難しい種類の情報でもあります。
一方で日報は、振り返りや目標設定のために後から見返すものだと捉えています。何をやったかだけでなく、何を決めたか、なぜそう決めたか、どの選択肢を捨てたかといった経緯が残っていると資料としての価値が出ると考えています。
この2つを1つのフォーマットに押し込めようとすると、記録したい粒度が噛み合わない気がしています。数字を合わせるために文章の記録が雑になるか、文章に時間をかけて数字が後回しになるか、どちらかに偏ってしまいそうです。
そこで、予実につける稼働は毎日チェックする、日報は事実に基づいて記録を残す、という役割分担にしてみました。同じ材料から2つの成果物を出す形です。
1日の活動記録が複数のツールに分散している点
もう1つ整理したのが、日報と稼働実績の材料をどこから取るかという点です。
私の場合、打ち合わせの予定はカレンダーに、他メンバーとのやり取りは Slack に、実作業の痕跡はコミット履歴や作業ログに残っています。用途ごとにツールを使い分けているので当然ではあるのですが、日報や稼働実績を作るという観点で見ると、材料が複数箇所に散らばっている状態です。
それぞれ分かる範囲が違うため、どれか1つを選ぶのではなく、並列の情報源として組み合わせて使うのが良さそうだと考えました。
| 情報源 | 何が分かるか |
|---|---|
| Google カレンダー | 打ち合わせの時刻・参加者 |
| Slack | 他メンバーとのやり取り、作業時刻の裏付け |
| Git log | コミットという確定した成果物 |
| Claude Code のセッションログ | 実作業の中身(何を読み、何を書き、何を実行したか) |
どれか1つだけでは1日を再構成できないので、取れるものは全て取ることにしました。逆に言えば、どれかが欠けてもその範囲の情報が薄くなるだけで、残りのソースから日報と稼働実績は作れるはずです。
私の場合は業務のほとんどが Claude Code 上で完結するため、実作業の記録としてはセッションログの比重が大きくなります。ここは使っているツールによって変わる部分で、たとえばエディタでの作業が中心であればコミット履歴の比重が上がるかと思います。
全体の流れ
ここまで整理した内容をもとに組み立てた全体像が以下です。
このうち、図の中央にある2つの工程は毎日・毎月同じ手順を繰り返すことになります。決まった情報源を決まった順序で集めて2種類のアウトプットを出す部分と、それをCSVに変換する部分です。
ここは定型化できそうだったので、それぞれを Claude Code のスキルにしました。スキルにしておけば /スキル名 と打つだけで同じ手順が再現されます。
| スキル | 対応する工程 |
|---|---|
/daily-reflection |
一次情報を集め、日報と稼働実績を出力する |
/yojitsu-csv |
稼働実績を工数管理システム向けのCSVに変換する |
定型化するなら、規則性に基づいた処理ロジックを書くという選択肢もあります。ただ日報に関しては、AIに任せてしまうのが向いていると考えました。
そもそも日報に書く内容は、人間が書こうがAIが書こうが大きくは変わらないはずです。その日にやったことを、決まったフォーマットに沿って並べ直す作業だからです。判断が要るとすれば「この作業はどのプロジェクトに紐づくか」「この時間帯は何をしていたか」といった部分ですが、これは条件分岐で書き切れるものではなく、材料を突き合わせて解釈する必要があります。
ただし、解釈を任せるといっても書いてよいのは材料から言えることまでです。日報は後から見返す記録なので、材料に無いことを推測で補われると資料として使えなくなります。解釈はさせるが憶測は書かせない、という線引きが必要になります。
推論なので日によって表現がブレる可能性はありますが、これはむしろ利点だと捉えています。毎日まったく同じ文面が並ぶよりも、その日の作業内容に応じた書き方になる方が、後から読み返したときに思い出しやすいためです。
一方で、ブレてほしくない部分もあります。稼働実績の数字や、日報のセクション構成がそれにあたります。そこで、フォーマットと守るべきルールだけを定義しておき、中身の解釈はAIに任せる、という切り分けにしました。後述する事実ベースの原則や、稼働実績の整合性チェックが該当します。
なお一次情報のうち、カレンダーと Slack は MCP 経由で、Git log は git log コマンドで、スキルの実行時にその場で取得します。セッションログだけは後から取得できないため、あらかじめ保存しておく仕組みが必要になります。これについては番外編で触れます。
扱うデータは4種類あり、それぞれ形式と宛先が違います。
| データ | 形式 | 保存先 | 生成するもの |
|---|---|---|---|
| セッションログ | JSONL | ~/.claude/daily-logs/YYYY-MM-DD/HH-MM-SS.jsonl |
SessionEnd フック |
| 日報 | Markdown | {output_dirs.report}/YYYY/MM/YYYY-MM-DD.md |
/daily-reflection |
| 稼働実績 | JSON | {output_dirs.timesheet}/YYYY/MM/YYYY-MM-DD.json |
/daily-reflection |
| 工数管理CSV | CSV | {export.output_path}/YYYY-MM/YYYY-MM.csv |
/yojitsu-csv |
スキルを作るうえで工夫した点
ここからが本題です。スキルの中身を作るにあたって、特に考えた点を紹介します。
日報と稼働実績を別ファイルとして出力する
1つ目は出力の分け方です。冒頭で整理した「役割・目的が違う」を、そのまま出力の形に反映しています。
/daily-reflection を実行すると、2つのファイルが保存されます。
| ファイル | 中身 | 宛先 |
|---|---|---|
| 日報(Markdown) | 事実の記録。保存後は基本触らない | 人間(後から見返す) |
| 稼働実績(JSON) | 工数入力用の申告値 | 機械(/yojitsu-csv と工数管理システム) |
日報(Markdown)
日報は frontmatter + セクション構成です。frontmatter を付けているのは、後から機械的に集計・検索するためです。
以下は生成される日報のサンプルです。
---
date: 2026-08-12
day: 水曜日
office: remote # remote | office | hybrid | unknown
projects: [ACMEポータル刷新, 社内ツール整備]
meetings_count: 2
---
## タイムライン
| 時間 | プロジェクト | 内容 |
|------|------------|------|
| 09:30-10:00 | 社内ツール整備 | タスク整理 |
| 10:00-11:00 | 🔀 並行 | 週次定例 / 設計メモ作成 |
## 持ち越しTODO(前日分)
- API 設計レビューの依頼(着手したが未完了)
## プロジェクト: ACMEポータル刷新
> 株式会社ACME_会員ポータル刷新(2026年4-9月)
### 認証方式の見直し
- やったこと: OIDC への移行案を2パターン作成
- 決めたこと: パターンAを本線に、Bは比較用に残す
- 捨てた選択肢: _(言及なし)_
- 背景: _(言及なし)_
### 明日やること
- パターンBのレビュー依頼(根拠: セッション中のTODO)
## 打ち合わせ
- 10:00-11:00 週次定例(参加者: _参加者要確認_)
## 総評
- 明日の予定: 10:00-11:00 定例
- 改善点: ...
## 自由記述
> ここに感想・意気込み・雑感を自分で書く
構成上のポイントがいくつかあります。
_(言及なし)_ はバグではなく仕様です。ソースに無いことを推測で埋めない設計にしているため、材料が無い欄は明示的に「無い」と書きます。これについては後述します。
同時間帯に打ち合わせと作業が重なった場合は 🔀 並行 として1行で両方書きます。どちらかを消すと事実と合わなくなるためです。
「自由記述」は後から自分で書き足すための欄です。感想や意気込みは事実ベースの記録とは性質が違うので、AIが埋めるのではなく人間が書く場所として分けています。日報を保存する前に「自由記述欄に何か書きたいことはあるか」とAIが聞いてくれるので、そこで伝えれば反映されますし、無ければ空欄のまま残ります。
稼働実績(JSON)
一方で稼働実績は JSON で作ります。読み手が /yojitsu-csv と工数管理システムなので、プログラムで読みやすい形式にしています。
{
"date": "2026-08-12",
"day": "水曜日",
"total_minutes": 450,
"entries": [
{
"case_number": "0000009901",
"customer": "株式会社ACME",
"code": "P990000009901001",
"work_name": "株式会社ACME_会員ポータル刷新(2026年4-9月)",
"project": "ACMEポータル刷新",
"minutes": 285,
"breakdown": [
{ "activity": "認証方式の比較検討・設計メモ作成", "minutes": 135 },
{ "activity": "既存コード調査・移行方針の整理", "minutes": 150 }
]
}
]
}
時間は全て分単位の整数にしています("1h30m" ではなく 90)。計算と検証を機械的に行えるようにするためです。
保存前には以下の制約を満たしているかチェックします。
total_minutes= 勤務終了 − 勤務開始 − 1h(固定昼休憩) − 追加休憩entries[].minutesの合計 =total_minutesentries[].breakdown[].minutesの合計 = その entry のminutesentries[].minutesとbreakdown[].minutesは15の倍数
ここが日報と分けた方がいいと判断した部分です。
日報のタイムラインと稼働実績は、同じ1日を扱っていても求められるものが違います。タイムラインは事実の記録なので5分単位で細かく書きますが、合計が何時間になるかは問いません。一方の稼働実績は申告値なので、15分刻みに揃えたうえで合計が勤務時間と一致している必要があります。
つまり同じ数字を扱いながら、片方は事実に忠実であること、もう片方は辻褄が合っていることを優先します。これを1つのファイルに同居させると、どの数字にどちらのルールが適用されるのかが分かりにくくなります。ファイルごと分けてしまえば、稼働実績側にだけ整合性チェックをかければよくなります。
もう1つ、稼働実績の方には人間の判断が入る余地を残しておきたい、という理由もあります。
AIを業務に取り入れるようになってから、複数のセッションを並行して動かす場面が増えました。片方の応答を待っている間にもう片方を進める、といった具合です。この場合、タイムライン上は同じ時間帯に複数の作業が事実として並びます。ただ稼働実績に落とすときは、どちらにどれだけ時間を割いたのかを決める必要があります。手が止まっていた時間や思考の比重までは記録に残らないので、ここは自分で判断して配分を決めます。
タイムラインを事実のまま残しておけば、配分を直したあとでも元の記録は変わりません。稼働実績だけを直しても、根拠となる事実の記録は手つかずで残るという状態にしたかった、というのも分けた理由の1つです。
ここまでが出力を分けた理由ですが、実際に運用するとなると、その稼働実績をどう確認するかという話も出てきます。
稼働実績は後日まとめて作ろうとすると、その日何をしていたかを思い出す作業から始めることになります。それを避けるために毎日つけるようにしているのですが、そうなると1日の終わりの確認をいかに軽くするかが問題になります。
JSONは機械には読みやすい一方、人間が目視で確認するには向いていません。そこで、保存したJSONをそのまま見せるのではなく、テーブルに整形し、分数も 4h45m のような表記に直して画面に提示するようにしました。
もう1つは提示するタイミングです。保存前に「これでいいですか?」と聞いて待つのではなく、先に保存してから提示します。
稼働実績を保存しました(outputs/timesheets/2026/08/2026-08-12.json)
| 案件番号 | 顧客名 | 作業コード | 作業名称 | 稼働時間 | 備考 |
|---------|------|----------|---------|---------|------|
| 0000009901 | 株式会社ACME | P990000009901001 | ACME_会員ポータル刷新 | 4h45m | 設計2h15m、調査2h30m |
| 0000009902 | 自社 | P990000009902000 | 社内共通:ツール整備 | 2h45m | タスク整理30m、定例1h、CI設定1h15m |
| | | | 合計 | 7h30m | |
修正したい箇所があれば教えてください。
稼働実績は「どの作業にどれだけ時間を使ったか」という単純なデータなので、間違っていても後から直すのは簡単です。それであれば、先に出力させてしまって、違う部分だけを指摘する方が早く済みます。
保存前に確認を挟む形にすると、合っている場合でも「問題ない」と答えるまで保存されません。毎日繰り返すことを考えると、この一手間が煩わしく感じます。合っていれば何も言わずに終われる方が、日々の運用としては軽くなると考えて設計しました。
複数の情報源から材料を集める
2つ目は材料の集め方です。散らばった記録をどういう順序で集め、どう確認するかという話になります。
チャンク単位で確認していく
当初は「全情報を一括収集してから日報を組み立てる」形にしていたのですが、2つ問題がありました。
1つはコンテキストの問題です。1日分のセッションログ・カレンダー・Slack を全て読み込むとコンテキストが膨らみ、圧縮が走って読み直しが発生します。
もう1つは確認の重さです。組み上がった日報を丸ごと提示されても、どこが合っていてどこが違うのかを判断するのは負荷が高くなります。
そこで、1〜2時間単位のチャンクごとに「情報収集 → 確認」を繰り返す形にしました。
【09:00-11:00】
- 09:10-09:50 A案件: 検証環境コスト一覧作成
- 09:50-11:00 A案件: チケット差分更新 + 定例アジェンダ作成
- その他: B案件Slack(10:30)
→ これで合っていますか?
この単位であれば「この1時間はこうでしたよね?」に答えるだけで済みます。チャンクごとに処理が完結するのでコンテキスト圧縮も起きにくく、チャンク間のギャップがそのまま空白時間として検出できるという副次効果もありました。
確認の粒度を調整する
確認の粒度でもう1つ調整したのが、細かい空白時間は確認しないという点です。
タスク間の5〜20分程度の空白は、直後のタスクに含めます。裏で準備や思考や移動をしている可能性があり、都度「この10分は何をしていましたか?」と確認が入るとテンポが悪くなるためです。確認するのは30分以上の空白だけにしています。
毎日使うものなので、精度と確認の手数のバランスが実用性に影響します。ここは実際に使いながら調整した部分です。
日跨ぎセッションへの対応
材料を集めるうえで、セッションログには保存のされ方に固有の癖があります。日付をまたぐ作業を扱う場合は把握しておく必要があるので、ここで触れておきます。
セッションログは SessionEnd フック実行時、つまりセッション開始日のディレクトリに保存されます。そのため、前日の夜に開始して日付をまたいだセッションは、当日のディレクトリには存在しません。
対策として、各エントリのタイムスタンプに日付を含めておき、前日・前々日のログも対象日のタイムスタンプを含むか検索するようにしています。
# 前日・前々日のセッションログのうち、対象日のtsを含むものを検索
grep -l "<対象日>" ~/.claude/daily-logs/<前日>/*.jsonl ~/.claude/daily-logs/<前々日>/*.jsonl 2>/dev/null
もう1つ、ファイル名のタイムスタンプはセッションの保存時刻であって、作業開始時刻ではありません。1つのセッションに数時間分の作業が含まれることもあるため、ファイル名だけを見て時系列を組むとずれます。実際の作業時系列は、ログの中身にある各エントリのタイムスタンプから再構成しています。
推測で埋めさせない
3つ目は事実の担保です。前述の「解釈はさせるが憶測は書かせない」を、どうやってスキルに落とし込んだかという話になります。
実際に作ってみると、材料が足りない部分をもっともらしい文章で埋められてしまうことがありました。「〜と思われる」「別案も検討したが採用せず」といった、読むと何か書いてあるようで実は中身が空の記述です。
日報は後から見返すためのものなので、事実でない記述が混ざると資料としての価値が下がります。そこで、SKILL.md の最重要原則として以下を明記しました。
- でっち上げ禁止: 「捨てた選択肢」「背景」「決めたこと」をソース無しに推測で補完しない
- 婉曲表現禁止: 「想定」「たぶん」「〜と思われる」などの推測語を本文に書かない
- 断定禁止: ソースが無いのに「〜は無かった」「〜のみ」と断定しない
そのうえで、材料が無い項目は _(言及なし)_ と明記します。書かないか、明示するかの二択で、穴埋めはしないというルールです。
SKILL.md には OK/NG の対比表も置いています。
| OK | NG |
|---|---|
docs/vpc-cidr-expansion.md を作成(Writeツール呼び出しで確認可) |
ドキュメント基盤を整えた(抽象化しすぎ) |
Slack投稿で 5月中旬目処に進捗報告 と記載(原文引用) |
5月中旬を目指す方針で合意したと思われる(推測) |
| 明日の予定: カレンダーに登録なし | 明日は 09:30-10:00 想定: ◯◯定例(想定) |
| 捨てた選択肢: (言及なし) | 捨てた選択肢: 別案も検討したが採用せず(中身空っぽの埋め草) |
抽象的なルールだけでは解釈が揺れるため、具体例で境界を示すようにしました。「抽象化しすぎ」がなぜNGなのかは、対比する OK 例があって初めて伝わります。
もう1つ入れているのが日付境界のバリデーションです。セッションログ内の「昨日」「先週」といった時間表現は言及であって今日の作業ではないため、ツール呼び出しに対応しない記述は日報に書かない、というルールにしています。
なお、この設計では材料が無い欄がそのまま _(言及なし)_ として残ります。これは「その日は背景まで言語化していなかった」という事実がそのまま出ている状態です。セッション中に「なぜそうするか」を会話に残しておくと日報に反映されるため、日報の情報量を増やしたい場合は普段の作業中に判断理由を言語化しておく、という運用になります。
CSVの列構成を設定で定義する
4つ目は工数管理システムへの登録部分です。/yojitsu-csv で稼働実績JSONをCSVに変換します。
日付,プロジェクト番号,作業オーダ番号,作業名称,実績時間,備考
2026-08-12,09901,P990000009901001,株式会社ACME_会員ポータル刷新(2026年4-9月),2.25,認証方式の比較検討・設計メモ作成(2.25)
2026-08-12,09901,P990000009901001,株式会社ACME_会員ポータル刷新(2026年4-9月),2.50,既存コード調査・移行方針の整理(2.50)
やっていることはJSONをCSVに詰め替えるだけです。ただ、この列構成は工数管理システム側の仕様で決まるものなので、私の環境に合わせてハードコードしても他の方には使えません。そこで、列の並びやヘッダ名は設定ファイルで定義できるようにしました。
"columns": [
{ "name": "日付", "source": "date" },
{ "name": "実績時間", "source": "minutes", "format": "decimal_hours2" },
{ "name": "備考", "source": "breakdown", "format": "activity" }
]
配列の並び順がそのままCSVの列順になり、name がヘッダ行に出ます。source には稼働実績JSONのフィールド名を指定します。
値の加工が必要な場合に備えて、transform と format も用意しました。ただしここで指定できるのは、あらかじめ決めた語彙だけです。
| 指定 | 動作 | 例 |
|---|---|---|
transform: "last5" |
下5桁を取る | 0000009901 → 09901 |
format: "decimal_hours2" |
分を小数時間に変換 | 90 → 1.50 |
format: "activity" |
内訳を 活動名(時間) の形式で連結 |
- |
任意の式を書けるようにしていないのは、CSVが工数管理システムへ登録するデータだからです。テンプレート言語や自由記述を許すと解釈が揺れる余地が生まれます。語彙が足りなければキーワードを追加して拡張する方針にしました。
ところで、ここまでの処理はJSONを読んで列を並べ替えるだけなので、スクリプトを書けば済む話ではあります。それでもスキルにしたのは、変換の前後に人間の判断が入る余地を残したかったためです。
たとえば月次のファイルに追記していく運用なので、同じ日付のデータが既に含まれていれば重複になります。この場合は確認を挟んでから処理します。また、生成前には各行の合計がその日の total_minutes と一致するか、案件番号や作業コードが設定ファイルに存在するかを検証しています。
- 各日の全CSV行の実績時間合計 = その日の稼働実績JSONの total_minutes
- 各行の case_number・code が config.jsonc の work_codes に存在すること
- 日付形式が YYYY-MM-DD
こうした検証で引っかかったときに、エラーで止まるのではなく「ここが合っていませんが、どうしますか」と聞いてもらえる方が、実際の運用では扱いやすいと感じています。
あわせて、対象日の指定も柔軟にできます。/yojitsu-csv 8/7 のような日付指定に加えて、8/1-8/10 や「今月」といった期間指定も解釈されます。この程度の融通は、スクリプトで引数をパースするより自然言語で渡せた方が楽です。
番外編: 実装するうえで工夫した点
ここからは、これらを実際に動くものにするまでに検討した技術的な部分をまとめます。ログをどう残すか、データ形式に何を選ぶか、設定をどこに置くかといった話です。
日報や稼働実績に限らず、何かを定型化して自動生成する仕組みを作る際には共通して出てくる検討事項かと思います。
SessionEnd フックでログを残す
日報の一次情報になるセッションログは、SessionEnd フックで機械的に保存しています。
フックの中身はシンプルで、標準入力から受け取った JSON から transcript_path などを取り出し、Python スクリプトに渡すだけです。
INPUT=$(cat)
TRANSCRIPT_PATH=$(echo "$INPUT" | jq -r '.transcript_path // empty')
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // empty')
CWD=$(echo "$INPUT" | jq -r '.cwd // empty')
なお SessionEnd フックには注意点があり、全体で 1.5 秒の実行予算を共有します(timeout 指定で最大60秒まで拡張可能)。このフックは timeout: 30 を指定していますが、他に重い SessionEnd フックがある環境ではログ保存が間に合わない可能性があります。ログが残らない場合にはまずこの部分を疑うとよさそうです。
日報に使う情報だけを抜き出す
Claude Code の会話履歴(transcript)には、画面に表示される内容だけでなく、内部的にやり取りされているデータも全て含まれています。これをそのまま保存すると1セッションで数MBに達することもあり、日報を作る際に読み込むには量が多すぎます。
そこで、日報の材料になる部分だけを抜き出して保存しています。残しているのは以下です。
- ユーザーとAIの発言(最大1000文字まで。それ以降は切り詰める)
- ツールを呼び出した記録(ツール名と、読んだファイルや実行したコマンド)
逆に、以下は保存していません。
| 除外するもの | 理由 |
|---|---|
| ツールの実行結果 | ファイルの中身やコマンドの出力そのもの。量が多く、日報には不要 |
| システムが自動で挿入するテキスト | <ide_opened_file> や <system-reminder> など、ユーザーが書いたものではない |
| スキル起動時に読み込まれる SKILL.md の全文 | スキルの手順書そのもの。毎回同じ内容が入る |
つまり「何をしたか」は残りますが「その結果どうなったか」は残りません。日報に書きたいのは行動の履歴であって、コマンドの出力内容ではないためです。
なお、記録に値する発言が1件も無かったセッションは、ファイル自体を作りません。短い確認だけで終えたセッションのログを残す必要があまりないと判断したためです。
ログ形式に JSONL を採用する
セッションログは JSONL(1行1エントリのJSON)にしています。
{"type": "session", "id": "bbf3f5ca-...", "cwd": "/Users/you/work/acme-portal", "started": "2026-08-13T12:58:39+09:00"}
{"ts": "2026-08-13T12:58:39+09:00", "role": "user", "text": "ダッシュボードにデータが表示されません"}
{"ts": "2026-08-13T12:58:42+09:00", "role": "assistant", "tool": "Read", "target": "/Users/you/work/acme-portal/dashboard.tf"}
{"ts": "2026-08-13T13:02:10+09:00", "role": "assistant", "tool": "Bash", "target": "terraform plan"}
1行目がセッションのメタ情報、2行目以降が時系列のエントリという構造です。
JSONL にしている理由は主に2つあります。
1つは追記できることです。1つの巨大な JSON 配列にすると、追記のたびに全体をパースし直す必要があります。
もう1つは部分読みできることです。Claude Code に読ませる際、jq で必要な行だけ絞り込めます。
# ツール呼び出しだけ機械的に絞り込む
jq -c 'select(.tool)' ~/.claude/daily-logs/<対象日>/<ファイル名>.jsonl
# メタ情報だけ取る
head -1 ~/.claude/daily-logs/<対象日>/<ファイル名>.jsonl | jq .
1日分のログを全部読み込むとコンテキストを圧迫するので、必要な部分だけを読み込めるかどうかで、扱えるログの量が変わります。なお Claude Code 自身の transcript も JSONL なので、そのまま踏襲した形です。
余談ですが、JSONL は生成AIにデータを渡す際の形式としてもよく登場します。たとえば Amazon Bedrock のバッチ推論では、入力データを JSONL で用意する必要があります。
{ "recordId" : "string", "modelInput" : {JSON body} }
1件1行という構造がレコード単位の処理と相性がよいためだと思われます。モデルに読ませることを前提としたデータであれば、JSONL を選んでおくと扱いやすい場面が多そうです。
設定ファイルに JSONC を採用する
一方、設定ファイルは JSONC(コメント付きJSON)にしています。
// git log --author に渡す値。メールアドレスを推奨。
// author 名(表示名)はリポジトリごとに異なることがあるが、
// メールアドレスは共通のことが多いため。
"git": {
"author": "your.name@example.com"
}
理由は、設定項目の意図をその場に書けるからです。
この設定ファイルは、案件コードや出力先を自分で書き換えるものです。「なぜこの値なのか」「どういう制約があるのか」がその場に書いてあれば、編集するときに別の資料を開かずに済みます。説明を別途ドキュメントに書く方法もありますが、設定を変えたときに片方だけ古くなりがちです。
コメントを持てない素の JSON でも、__comment のようなキーを設けて説明を書く方法はあります。ただこの場合、説明文が設定値と同じ見た目で並ぶことになり、どれを書き換えればよいのかが分かりづらくなります。
JSONC であれば // で始まる行がコメントだと一目で分かり、それ以外は全て設定値だと判断できます。編集する場所が明確になるので、こちらを選びました。
なお JSONC は標準の JSON パーサでは読めないため、SKILL.md 側に以下の注意書きを入れています。
⚠️
config.jsoncは JSONC(コメント付きJSON)。//で始まる行はコメントなので、値として解釈しないこと。
フックとスキルをグローバルに置く
日報は案件をまたいで書くものなので、どのディレクトリで作業していてもフックが発火し、スキルを呼び出せる必要があります。プロジェクト側の .claude/ に置くと、そのプロジェクトを開いている時しか効きません。そのため、フックの登録もスキルの配置もグローバル領域(~/.claude/)に対して行っています。
とはいえ、グローバル領域に実体を置くと管理が難しくなります。そこで実体はリポジトリ側に持たせ、グローバルにはシンボリックリンクと設定の1エントリだけを置く形にしました。
daily-reflection/
├── .claude/
│ └── skills/
│ ├── daily-reflection/ ← 実体
│ │ ├── SKILL.md 手順(いつ何をするか)
│ │ └── format.md 出力仕様(何をどう書くか)
│ └── yojitsu-csv/
│ └── SKILL.md
この形にしたのは、私自身がグローバル領域をあまり見に行かないためです。~/.claude/ に直接スキルを置くと、作ったこと自体を忘れて放置しがちでした。リポジトリとして切り出しておけば、役割が明確な単位で管理でき、後から見直したり手を入れたりしやすくなります。シンボリックリンク経由なので、リポジトリ側を編集すれば保存した時点で反映されます。
リンクの作成とフックの登録は、セットアップスクリプト(scripts/setup.sh)にまとめました。
ln -s "$skill_src" "$link_path"
スクリプトがやっているのは、前提コマンド(jq / python3)の確認、スキルへのシンボリックリンクの作成、設定ファイルの雛形コピーと検証、そして ~/.claude/settings.json の SessionEnd への登録です。
既存の設定を壊さないよう、SessionEnd にはエントリを1つ追加するだけにしています。同じイベントに複数のフックを登録でき、それぞれ並列に実行されるためです。既にある他のフックや、他イベント(PreToolUse など)の設定には触れません。書き換える前に ~/.claude/backups/ へバックアップを取り、何度実行しても同じ結果になるようにしています。
なおスキルは、リポジトリ内から呼ばれる場合とグローバル経由で呼ばれる場合があるため、SKILL.md 側ではどちらの経路でも動くようにしています。
for d in ./.claude/skills/daily-reflection ~/.claude/skills/daily-reflection; do
[ -f "$d/SKILL.md" ] && SKILL_DIR="$d" && break
done
SKILL_DIR_REAL="$(cd "$SKILL_DIR" && pwd -P)"
REPO_ROOT="$(cd "$SKILL_DIR_REAL/../../.." && pwd -P)"
pwd -P でシンボリックリンクを解決してからリポジトリルートを求めているのがポイントです。
SKILL.md を手順と出力仕様に分ける
daily-reflection スキルは、SKILL.md(手順)と format.md(出力仕様)の2ファイルに分けています。
SKILL.md— いつ何をするか(情報収集の順序、確認の粒度)format.md— 何をどう書くか(セクション構成、JSONスキーマ)
分けている理由は、変更の頻度と理由が違うためです。「日報のセクションを1つ増やしたい」ときに触るのは出力仕様だけで、手順は変わりません。逆に「確認の粒度を変えたい」ときは手順だけを触ります。
1ファイルに全部書くと、どちらの変更でも全体を読み直すことになります。変更理由ごとにファイルを分けるという考え方は、SKILL.md でも有効なようです。
出力先パスの解決ルールを明示的にエラーにする
日報と稼働実績の出力先は、設定ファイルで自由に変えられるようにしています。リポジトリ内に置いてもよいですし、Obsidian の Vault のように普段使っている場所を指定することもできます。
そうすると、パスをどう書いてもらうかが問題になります。書き方によって基準が変わるため、解釈を1つに定めておかないと出力先がずれます。そこで、受け付ける書き方を以下の3種類に限定しました。
| 書き方 | 基準 |
|---|---|
{REPO_ROOT}/... |
このリポジトリのルート |
~/... |
ホームディレクトリ |
/... |
絶対パス |
./outputs/... のような裸の相対パスはエラーにします。
./ は一般に「実行時の cwd 基準」を意味しますが、このスキルはグローバル登録されて任意のディレクトリから実行されるため、cwd 基準で解決すると出力先が散らばります。かといって「リポジトリルート基準」という独自解釈を持たせると、読み手が誤解します。
そこで黙って解決せずエラーにしました。設定ミスを「出力先の事故」ではなく「実行時のエラー」として気付けるようにするためです。
なお実装上、コマンド置換への代入は関数が return 1 してもそれ自体は失敗扱いにならないため、|| exit 1 を明示的に付けています。
REPORT_DIR="$(resolve_output_dir "<config.output_dirs.report>")" || exit 1
これが無いと、空の出力先のまま処理が続いてしまいます。
最後に
普段使っているツールに残っている記録から、日報と予実用の稼働記録を作るスキルをご紹介しました。
構成上のポイントは、日報と予実を1つの成果物にまとめず、同じ材料から性質の違う2つの出力を作るようにしたことです。予実は毎日つけないと後から思い出せないため機械可読な JSON で残し、日報は後から見返すためのものなので事実ベースの記録として残す、という役割分担にしています。
冒頭でも触れたとおり、情報源の構成や工数管理システムの仕様は私の環境や業務の仕方に合わせたものです。そのまま使えるケースは限られると思いますが、複数のソースを並列に見にいく設計にしているため、普段からカレンダーや Slack を活用されている方であれば流用いただける部分はあるかと思います。役割の違う出力は分ける、推測で埋めさせない、といった考え方の部分も他の用途に応用が効くのではないかと考えています。
番外編でご紹介したログの残し方や JSONL / JSONC の使い分けについても、何かを定型化する仕組みを作る際の参考になれば嬉しいです。
最後までご覧いただきありがとうございました。








