| name | interest-profile |
| description | 会話履歴からユーザーの興味プロファイルを生成・更新するスキル。「/interest-profile sync」「プロファイル更新」「興味プロファイル」「/interest-profile show」「興味を見せて」等でトリガーする。 |
興味プロファイル生成スキル
Claude Code および Codex (ChatGPT app) の会話履歴を分析し、ユーザーの興味プロファイルを INTERESTS.md(プロジェクトルート)に蓄積・更新する。
- Claude Code ログ:
~/.claude/projects/<エンコード済みパス>/*.jsonl
- Codex ログ:
~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl + ~/.codex/archived_sessions/*.jsonl
- バンドルID
com.openai.codex(旧 Codex.app と統合された ChatGPT.app)
~/.codex が存在しない場合はスキップ(エラーにならない)
このスキルの責務は 「会話履歴から興味を抽出して蓄積する」ことだけ に絞られている。
普段どおり Claude や Codex と会話を重ねるほど、その履歴からプロファイルが自動的に育っていく。記事を読んで質問する、調べ物をする、意見を述べる——そうした日常の会話がそのままシグナルになる。
このスキルには2つのモードがあります:
- syncモード: 「sync」「更新」「プロファイル更新」等で実行 → Section 1〜5を実行
- showモード: 「show」「見せて」「表示」等で実行 → Section 6を実行
データ保存先: data/interests/
重要: 自律実行ルール
cron や /loop からの自動実行時は以下を厳守する:
- ユーザーへの確認・質問は一切行わない
- 全ての処理を即座に自律的に実行する
- エラー発生時はログに記録して処理を継続する
syncモード
Section 1: 会話ログからユーザーメッセージを差分抽出
Pythonスクリプトで会話ログの差分抽出を行う。
python3 .claude/skills/interest-profile/scripts/extract_interest.py \
--state-file "data/interests/last-sync.json" \
--state-out "data/interests/last-sync.json.pending" \
--max-messages 500
注意:
--logs-dir は省略可。省略時はカレントディレクトリから対応するログディレクトリ(~/.claude/projects/<エンコード済みパス>)を自動推定する。別プロジェクトのログを解析したい場合のみ --logs-dir で明示指定する。
- Codex ログは
~/.codex が存在する場合に自動的に同時抽出される。無効化したい場合は --no-codex を付ける。Codex のベースディレクトリを変えたい場合は --codex-logs-dir <path> で指定する。
- state ファイルには
sessions(Claude)と codex_sessions(Codex)が別々に管理される。既存の state ファイルに codex_sessions がなければ空から開始する(全 Codex ログが初回一括抽出される)。
- この段階では本ファイル
last-sync.json は更新しない。--state-out で「進めるべき状態」を pending ファイル(last-sync.json.pending)に書き出すだけにとどめ、全処理が正常完了した後に Section 5 で原子的に昇格(commit)する。これによりトランザクション境界が保たれ、途中で失敗してもしおりは進まない。
スクリプトの出力(JSON配列)を受け取る。抽出メッセージが0件の場合は、新規シグナルなしとしてSection 5の状態更新のみ行い終了してよい。
Section 2: 既存の蓄積データの読み込み(直近分のみ)
data/interests/interest-log.jsonl から直近90日分のシグナルだけを読み込む。全行は読まない(ログは無限に増えるため、全部読むとコンテキストが際限なく膨らむ。時間重み上、30日超の古いシグナルはほぼ効かないので直近分で十分)。
スクリプトに直近分だけを出力させ、その出力を読む:
python3 .claude/skills/interest-profile/scripts/extract_interest.py \
--recent-log "data/interests/interest-log.jsonl" \
--recent-days 90
このコマンドは ts が直近90日以内の行だけを stdout に出す(ファイルが無ければ何も出さない)。これが過去の蓄積データとなり、今回の新規シグナルと合算してプロファイルを再構成する。生ログ自体は削除せず、全履歴がファイルに残り続ける(読み込む範囲だけを絞っている)。
Section 3: 興味シグナルの分類
Section 1で抽出した新規ユーザーメッセージを分類する。
3a. ノイズ判定
以下はノイズとして除外する(interest-log.jsonlに記録しない):
- 純粋な操作指示: 「最後に確認して」「チェックして」「コミットして」
- ファイル操作: 「mkdir」「移動して」「削除して」
- 設定変更: 「permissions」「bypass」「設定を変えて」
- スキル起動のみ:
/email check 等(追加のコメントなし)
- 挨拶・短い返答: 「はい」「OK」「ありがとう」
3b. シグナル分類
ノイズでないメッセージを以下のカテゴリに分類する。intensity は「その1メッセージが示す興味の強さ」を表す:
| category | 判定基準 | intensity |
|---|
question | 「〜とは?」「教えて」「どういうこと?」等の質問 | 1-2 |
deep-dive | 同一セッション内で同トピックに3回以上質問 | 3 |
creation-intent | 「作りたい」「試したい」「書きたい」「やってみたい」等、能動的に手を動かす意図 | 3 |
topic-exploration | トピックについての意見表明、比較検討 | 1-2 |
opinion | 明確な好み・評価の表明 | 2 |
3c. トピックとキーワードの付与
各シグナルに以下を付与する:
- topic: 簡潔なトピック名(日本語、20文字以内)
- keywords: マッチング用キーワード配列(英語小文字、3-8個)
- raw_excerpt: 元メッセージの先頭200文字
各シグナルのJSON形式(interest-log.jsonl に1行1JSONで記録する):
{"ts": "<ISO8601>", "session_id": "<セッションID>", "source": "<source>", "category": "<カテゴリ>", "intensity": <1-3>, "topic": "<トピック名>", "keywords": ["..."], "raw_excerpt": "<先頭200文字>"}
source の値:
"conversation" — Claude Code(既存エントリ、後方互換)
"codex" — Codex / ChatGPT app からの会話
Section 4: INTERESTS.md の生成
Section 2で読み込んだ直近90日分のシグナル + 今回の新規シグナルから INTERESTS.md を生成する。
4a. 興味スコアの算出
各トピックの「関心の強さ」を次の式で算出し、これを「今の関心」の並び順の根拠とする:
トピックのスコア = Σ (intensity × 時間重み) ※そのトピックに属する全シグナルを合計
時間重み:
- 直近14日 : 1.0
- 15〜30日 : 0.5
- 30日超 : 0.25
この合計式により、次の2つが自然に反映される:
- 直近の関心ほど重い: 時間重みで最近のシグナルが優先される。
- 質問・言及が多いほど強い: 同じトピックに繰り返し触れるたびシグナルが増え、スコアが伸びる。さらに同一セッションで3回以上掘り下げたものは
deep-dive(intensity 3)に昇格し、一気に重くなる。
スコア上位を「今の関心」、時間幅をまたいで継続的にスコアが付いているものを「継続的な関心」として扱う。
4b. 文章スタイルのガイドライン
INTERESTS.md は自然言語の文章で書く。テーブル形式は使わない。
以下の原則に従う:
- 人物像が伝わる文章にする: この人がどういう興味を持ち、何を目指しているかが、初めて読む人にも伝わるように。
- 具体的に書く: 「設計に関心がある」ではなく「Anthropicのハーネス設計記事を精読し、木構造探索の挙動まで掘り下げて質問していた」のように、行動の証拠を添える。
- コンパクトに: 全体で80行以内。
- 意思決定や深掘りに役立つ情報を優先: 単なる操作への興味(権限設定、cron設定等)は省略してよい。
4c. INTERESTS.md テンプレート
---
last_updated: "{YYYY-MM-DD}"
signals_total: {総シグナル数}
---
# 興味プロファイル
## この人について
{ユーザーの基本的な人物像を2-3文で。関心の幅、姿勢(表面的ではなく本質を掘り下げるタイプ等)。}
## 今の関心(直近14日)
{4aのスコア上位から順に、自然な文章で記述する。各トピックについて:
- 何にどれくらい深く関心を持っているか
- どのような行動(質問、繰り返しの深掘り、比較検討、やりたいことの表明等)からそれがわかるか
- 関連する具体的なキーワードや固有名詞
段落ごとに1つの大きなテーマを扱い、太字で導入する。
重要度の低いものは簡潔に触れる程度でよい。}
## 継続的な関心
{14日より前から継続してスコアが付いている、この人の根っこにあるテーマを2-4個。
一過性の関心と区別して、長く向き合っているものを記述する。}
## 新規探索のヒント
{ユーザーの興味パターンから推測される「まだ直接触れていないが関連性の高い領域」を3-5個。
それぞれ1-2文で、なぜこの人に刺さりそうかの理由を添える。}
INTERESTS.md は INTERESTS.md(プロジェクトルート)に保存する。
Section 5: 状態の更新
全処理が正常完了した後、以下をこの順序で実行する。順序を守ること(逆順だと部分コミットになり、しおりだけ進んで新規シグナルが未記録になる):
- 新規シグナルを
data/interests/interest-log.jsonl に追記(1行1JSON)
- pending ファイル(
last-sync.json.pending)を本ファイルへ昇格(commit)する:
python3 .claude/skills/interest-profile/scripts/extract_interest.py \
--state-file "data/interests/last-sync.json" \
--commit "data/interests/last-sync.json.pending"
- このコマンドは pending を読み、
os.replace で last-sync.json を原子的に差し替えた後、pending ファイルを削除する。
- commit 成功後は
.pending ファイルは存在しない(昇格済み)。次回 sync で .pending が残っていればそれは前回の中断状態の残骸であり、Section 1 の --state-out が無条件で上書きするため放置してよい。
- 重複についての注意: この順序は「しおりだけ進んで新規シグナルが失われる」取りこぼしを防ぐ代わりに、手順1と2の間で中断すると次回 sync が同じメッセージを再抽出し、同じシグナルが interest-log.jsonl に二重記録されうる(取りこぼしより重複を許容する設計)。重複はスコアに二重計上されるため、追記前に
ts+raw_excerpt が既存行と一致するシグナルはスキップすること。
- 完了報告:
興味プロファイルを更新しました。
- 新規メッセージ: {N}件分析
- 新規シグナル: {M}件検出
- 保存先: INTERESTS.md
showモード
Section 6: プロファイル表示
INTERESTS.md(プロジェクトルート)を読み込んで表示する。
ファイルが存在しない場合は「まだプロファイルが生成されていません。/interest-profile sync を実行してください」と案内する。