このサイトはWebfile便で作成しました利用はこちら

GEMINI CLIとの会話を自動でZETTELKASTENに記録する仕組みを作った

URL: Claude Code版の記事 (Zenn)

概要:フロー状態のまま「知」を蓄積する

開発者の思考プロセスは、今やターミナル上の AI との対話の中にこそ最も色濃く反映される。しかし、ターミナルは「フロー(流れ)」の場であり、知識はコマンドとともに流れて消えていく。

一方で Obsidian などのノートアプリは「ストック(蓄積)」の場である。

このシステムの本質は、「フローの場」であるターミナルから一歩も出ることなく、自動的に「ストックの場」へ知識を永続化させるパイプラインの構築にある。

[!IMPORTANT] 「コンテキストスイッチの排除」が最大の価値 コードを書いている最中にブラウザやノートアプリに切り替えて記録する行為は、深い集中(ゾーン)を断ち切る。 本システムは、「対話すること=記録すること」という等式を成立させ、無意識下でのナレッジベース構築を実現する。

graph LR User["User"] Terminal_A["Terminal A
Project A"] Terminal_B["Terminal B
Project B"] Log_A["Log A (JSON)"] Log_B["Log B (JSON)"] Archiver["Archiver
(Background)"] Obsidian["Obsidian
Daily Note"] User -- Chat --> Terminal_A User -- Chat --> Terminal_B Terminal_A -- Update --> Log_A Terminal_B -- Update --> Log_B Log_A -. Watch .-> Archiver Log_B -. Watch .-> Archiver Archiver == Sync ==> Obsidian style User fill:#fff,stroke:#333,stroke-width:2px style Archiver fill:#fbbc05,stroke:#fff,color:#fff style Obsidian fill:#7c4dff,stroke:#fff,color:#fff

アーキテクチャ:Gemini CLI 版の独自性

元記事の Claude Code はログを単純なテキストとして追記する形式だったが、Gemini CLI はより現代的かつ複雑な JSON 構造を持つ。さらに、マルチセッション(複数タブでの同時作業)に対応するため、独自の監視アーキテクチャを採用した。

1. データの源泉:分散するスナップショット

Gemini CLI は会話のたびに、プロジェクト固有のディレクトリに JSON ログを「上書き保存」する。しかも、タブ(プロジェクト)を変えるとログファイル自体が変わる。

[!WARNING] 「単一監視」の限界 単純に「最新のファイル」だけを見ていると、タブ A で作業中にタブ B で会話した瞬間、監視対象が切り替わり、状態管理が破綻する。 Gemini 版では、「直近60分以内に更新されたすべてのセッションファイル」を並行して監視することで、この問題を解決している。

2. 抽出エンジン:IDベースの差分管理

ログが追記型ではないため、「どこまで読んだか」を行数で管理することはできない。代わりに、メッセージ固有の UUIDを利用する。

このロジックにより、「どのタブで」「どの順番で」会話しても、重複なく、漏れなく、Obsidian に集約される。

3. 永続化先 (The Destination)

Obsidian のデイリーノート(yyyy/mm/dd/📓...gemini-log.md)へ集約される。複数の文脈が一本のタイムラインに統合されることで、その日の思考の流れが「時間軸」で可視化される。


実装の詳細:Gemini Archiver (Multi-Session Edition)

flowchart TD N_Start((デーモン起動)) --> N_Loop{監視ループ
5秒毎} N_Loop --> N_Find[アクティブなセッション検索
60分以内の更新] N_Find --> N_Check{ファイル有?} N_Check -- No --> N_Sleep[5秒待機] N_Check -- Yes --> N_Iterate[ファイル反復処理] N_Iterate --> N_GetID[セッションUUID抽出] N_GetID --> N_ReadState[Stateファイル読込
前回のID取得] N_ReadState --> N_JQ[jqでメッセージ抽出
前回ID以降の差分] N_JQ --> N_HasNew{新規ある?} N_HasNew -- No --> N_NextFile[次のファイルへ] N_HasNew -- Yes --> N_Append[Obsidianノートへ
追記保存] N_Append --> N_UpdateState[Stateファイル更新
最新IDを保存] N_UpdateState --> N_NextFile N_NextFile --> N_Iterate N_Iterate -- 完了 --> N_Sleep N_Sleep --> N_Loop style N_Start fill:#2ecc71,stroke:#27ae60,color:#fff style N_Loop fill:#3498db,stroke:#2980b9,color:#fff style N_Append fill:#e74c3c,stroke:#c0392b,color:#fff

監視スクリプト (gemini_archiver.sh)

クリックして表示
#!/bin/bash
# gemini_archiver.sh
# Monitors Gemini CLI chat logs and archives them to Obsidian daily notes.
# Multi-session support: Tracks each session independently.

GEMINI_TMP_BASE="$HOME/.gemini/tmp"
OBSIDIAN_ROOT="$HOME/dotfiles/zettelkasten"
STATE_DIR="$HOME/.gemini/archiver_states"

# Ensure jq is available
if ! command -v jq > /dev/null; then
    echo "Error: jq is required but not installed." >&2
    exit 1
fi

get_date_vars() {
    YYYY=$(date +%Y)
    MM=$(date +%m)
    DD=$(date +%d)
    DATE_STR="${YYYY}${MM}${DD}"
}

process_session() {
    local session_file="$1"
    
    # Extract Session UUID from JSON content
    local session_id=$(jq -r '.sessionId // empty' "$session_file" 2>/dev/null)
    
    if [ -z "$session_id" ]; then
        return
    fi
    
    local state_file="$STATE_DIR/${session_id}.state"
    local last_id=""
    
    if [ -f "$state_file" ]; then
        last_id=$(cat "$state_file")
    fi
    
    # Check if session file has messages
    local has_messages=$(jq '.messages | length' "$session_file" 2>/dev/null)
    if [ -z "$has_messages" ] || [ "$has_messages" -eq 0 ]; then
        return
    fi

    # Construct jq filter
    local jq_filter
    if [ -z "$last_id" ]; then
        # New session tracking: Get ALL messages
        jq_filter='.messages[] | select(.type == "user" or .type == "gemini")'
    else
        # Existing session: Get messages AFTER last_id
        # Safety: If last_id is missing (null index), output empty to avoid duplication
        jq_filter='.messages as $m | ($m | to_entries | map(select(.value.id == "'"$last_id"'")) | .[0].key) as $idx | if $idx == null then empty else $m[$idx+1:][] end | select(.type == "user" or .type == "gemini")'
    fi

    local tmp_output=$(mktemp)
    
    # Run jq
    jq -r "$jq_filter | \"**\" + (.type | ascii_upcase) + \"**: \" + .content + \"\n\"" "$session_file" > "$tmp_output"
    
    if [ -s "$tmp_output" ]; then
        get_date_vars
        local target_dir="$OBSIDIAN_ROOT/dagnetz/$YYYY/$MM/$DD"
        local target_file="$target_dir/📓${DATE_STR}_gemini-log.md"
        
        mkdir -p "$target_dir"
        if [ ! -f "$target_file" ]; then
            echo "# $YYYY-$MM-$DD Gemini Log" > "$target_file"
        fi
        
        # Append newline and content
        echo "" >> "$target_file"
        cat "$tmp_output" >> "$target_file"
        
        # Update state file with the NEW last ID
        local new_last_id=$(jq -r '.messages[] | select(.type == "user" or .type == "gemini") | .id' "$session_file" | tail -1)
        if [ -n "$new_last_id" ]; then
            echo "$new_last_id" > "$state_file"
        fi
    fi
    
    rm "$tmp_output"
}

# Main Loop
while true; do
    # Find ALL session files modified in the last 60 minutes
    find "$GEMINI_TMP_BASE" -type f -path "*/chats/session-*.json" -mmin -60 2>/dev/null | while read -r session_file; do
        process_session "$session_file"
    done
    
    sleep 5
done
[!TIP] process_session 関数の妙 各セッションファイルから sessionId を読み取り、それに対応する .state ファイルを動的に参照する。 これにより、1つのスクリプトプロセスで、N個の同時進行セッションを完璧に捌くことができる。

運用:常駐化による脳のリソース解放

システムが意識されるようでは不完全である。ターミナルを開いた瞬間に、空気のように裏で動き出す必要がある。

シェル起動時の自動フック

.bashrc.zshrc に以下を仕込むことで、特別なデーモン設定(systemd等)すら不要になる。

# 既に動いていなければ、裏でそっと起動する
if ! pgrep -f "gemini_archiver.sh" > /dev/null; then
    ~/dotfiles/shellscripts/gemini_archiver.sh > /dev/null 2>&1 &
fi
[!CAUTION] ゾンビプロセスの防止 ガード節 (pgrep) により、タブを100個開いても監視プロセスは常に「1つ」に保たれる。リソース消費は最小限である。

結論:AIとの対話は「資産」である

このシステムを導入することで、以下のパラダイムシフトが発生する。

  1. 検索可能性: 過去のデバッグログやコード生成の経緯が、Obsidian の強力な検索機能(全文検索、タグ付け)の対象となる。
  2. 文脈の再利用: 「あの時どうやって解決したっけ?」が、自分の記憶ではなく、自分のノートから即座に引き出せるようになる。
  3. マルチタスクの統合: 複数のプロジェクトを行き来しても、全ての知見が「今日のログ」という一箇所に自動集約される。
[!NOTE] Obsidianのグラフビューへの影響 毎日自動生成されるログは、知識のグラフにおいて「時系列のバックボーン」を形成する。 他のノートからこのログへリンクを貼る([[📓20260112_gemini-log]])ことで、その日の思考の文脈をいつでも参照できるようになる。