プロジェクト

全般

プロフィール

機能 #41

完了

機能 #38: [Phase0] 開発基盤の定義と管理ルールの実装

[Stage0.5] REGシステム:Gitea ↔ Trilium 同期機能の実装

吉田 明 さんが3日前に追加. 1日前に更新.

ステータス:
終了
優先度:
通常
担当者:
カテゴリ:
開発ツールなど
対象バージョン:
開始日:
2025/12/22
期日:
進捗率:

0%

予定工数:

説明

Trilium APIとの連携。

吉田 明 さんが1日前に更新

  • カテゴリ開発ツールなど にセット
  • 対象バージョン0.0.1 にセット
  1. 目的
    Giteaに保存されているMarkdown仕様書を「Single Source of Truth(信頼できる唯一の情報源)」とし、それをTriliumへ自動同期することで、開発者が常に最新の設計図をストレスなく視認できる環境を構築します。

  2. 同期ロジックの設計(REGシステムの拡張)
    既に構築済みの「REGシステム」の一部として、以下のフローを実装します。

トリガー: GiteaのWebhook、またはローカルでの git push 後に発火するスクリプト。

通信方式: Trilium ETAPI (REST API) を使用。

鉄則遵守: 日本語文字化けを防ぐため、SDKは使わず Requestsライブラリを用いた Raw HTTP 通信(UTF-8強制) を徹底します。

同期単位: ファイル単位での「上書き(Put Content)」を基本とします。

  1. 具体的な実装ステップ
    Trilium 接続情報の定義:

settings.py または .env に、TriliumのAPI URLと ETAPI-Token を安全に格納します。

ファイルマッピングの設定:

Gitea上のパス(例: docs/00_基盤定義.md)と、Trilium上の Note ID を紐付けるマッピングテーブル(JSON形式)を作成します。

同期スクリプト sync_to_trilium.py の作成:

指定されたMarkdownファイルを読み込み、ETAPI の PUT /etapi/notes/{noteId}/content エンドポイントへ送信します。

この際、リクエストヘッダーで Content-Type: text/plain; charset=utf-8 を明示し、Bodyはバイナリ(.encode('utf-8'))で送信することで、REGシステムの思想を継承します。

VSCodeタスクへの統合:

merge_and_verify.py でのマージ完了後、シームレスに同期スクリプトが走るよう、VSCodeの tasks.json を構成します。

  1. 検証項目(判定基準)
    Giteaで管理されている 00_フェーズ0_開発基盤定義書.md を更新した際、数秒以内にTrilium上の対応するノートが、文字化けや欠落なく書き換わること。

1000行を超える大規模なドキュメントでも、通信エラー(Timeout等)が発生せず安定して同期できること。

吉田 明 さんが1日前に更新

ご指摘の通り、現在の merge_and_verify.py は「ドキュメントの整合性を保ちながら全文復元する」という特定の編集作業に特化した単発ツールです。リードエンジニアとして、今後の拡張性とメンテナンス性の観点から、ご提案の 「2) 機能単位で単発のコマンド(疎結合なツール群)を増やしていく」 方針を強く推します。その理由と、チケット #41 (Trilium同期)への適用案を整理しました。なぜ「単発コマンド形式」が良いのか疎結合による安定性:常駐型の巨大な「万能AIエージェント」を作ると、一部のAPIエラーや文字コード問題がシステム全体を道連れにするリスクがあります。単発コマンドなら、失敗してもその作業が止まるだけで済みます。REGシステムの哲学への適合:「日本語文字コードを壊さない」「Raw HTTPで叩く」という鉄則を、機能ごとに最小単位で実装・検証できるため、デバッグが容易です。開発フェーズに合わせた使い分け:おっしゃる通り、ドキュメント更新ツールは序盤に酷使されますが、中盤以降は「DB定義生成」や「テストコード生成」ツールの方が重要になります。必要な時に必要なツールを呼び出す方が合理的です。チケット #41 への適用案:sync_to_trilium.py の新設常駐させるのではなく、現在の merge_and_verify.py と同様に、「必要な時に叩く」 スタイルで新設しましょう。運用のイメージ手動同期: python tools/sync_to_trilium.py <ファイルパス>自動連携: VSCodeのタスク(tasks.json)を編集し、merge_and_verify.py が成功した後に、続けて sync_to_trilium.py が走るように連鎖(Chaining)させる。これにより、常駐プログラムを組む手間を省きつつ、「書き換えたら即反映」というゴールを達成できます。今後の「AI利用機能」の整理案今後、以下のような「単発ツール」を tools/ フォルダ内に増やしていく構成はいかがでしょうか。ツール名役割実行タイミングmerge_and_verify.py全文復元・マージMD/コードの編集時sync_to_trilium.pyTriliumへの反映ドキュメント確定時gen_migration.pyDB定義からDjango Migration生成モデル変更時check_consistency.py仕様書とコードの乖離チェックレビュー前次のステップへの提案まずは、この方針に沿って チケット #41 用の「単発同期スクリプト」の設計 に入ってもよろしいでしょうか?具体的には、merge_and_verify.py と同様に Requestsモジュールのみを使い、UTF-8を死守する同期コード を作成します。よろしければ、TriliumのノートIDとファイルパスを紐付けるための「マッピングファイルの形式」から検討を始めます。

吉田 明 さんが1日前に更新

承知いたしました。単発コマンド方式で、堅牢かつシンプルな同期ツールを目指しましょう。

「REGシステム」の哲学を継承し、Windows環境での文字化けを完全に封じ込めつつ、TriliumのAPI(ETAPI)を叩くための具体的な仕様を詰めたいと思います。


sync_to_trilium.py の基本設計案

1. 接続情報の管理

merge_and_verify.py 同様、外部ライブラリへの依存を最小限にし、requests で直接通信します。

  • APIエンドポイント: http://[IPアドレス]:[ポート]/etapi
  • 認証: Authorization: [ETAPI-Token] ヘッダーを使用。
  • 文字コード: Content-Type: text/plain; charset=utf-8 を明示し、Bodyはバイナリ送信。

2. ファイルとノートの紐付け(マッピング)

どのMarkdownファイルを、Trilium上のどのノート(Page)に飛ばすかを管理する必要があります。
プロジェクトルートに trilium_map.json を配置する構成案はいかがでしょうか?

trilium_map.json の構造例:

{
  "docs/00_フェーズ0_開発基盤定義書.md": "noteId_abc123",
  "docs/01_要件定義書.md": "noteId_def456"
}

3. 実行フロー(アルゴリズム)

コマンド引数からファイルパスを受け取り、以下の手順で動作させます。

  1. 引数チェック: python tools/sync_to_trilium.py docs/00_基盤定義.md
  2. マッピング確認: trilium_map.json から該当する noteId を取得。
  3. ファイル読み込み: encoding='utf-8' で開き、中身をバイナリ変換。
  4. APIリクエスト: PUT /etapi/notes/{noteId}/content を実行。
  • Triliumのこのエンドポイントは、指定したノートの内容をまるごと差し替えます。

実装に向けた確認事項

仕様を確定させるため、以下の2点について教えてください。

  1. TriliumのノートIDの取得状況:
  • Triliumの各ノートの「Note Info」等から、対象となるノートのID(10〜12文字程度の英数字)は確認できていますでしょうか?
  1. 実行環境のネットワーク:
  • Triliumは同じVPS(Ubuntu/Docker)内で動いている想定でしょうか?その場合、API URLは localhost 経由になりますか、それとも farm.example.com のような公開ドメイン経由になりますか?

次のステップの提案

「ノートIDの取得方法」と「接続URL」の目処が立ちましたら、sync_to_trilium.py のプロトタイプコードを生成し、マッピングファイルの作成に進みたいと思います。いかがでしょうか?

吉田 明 さんが1日前に更新

リードエンジニアとして、これから私たちが構築する「インテリジェント同期システム」の仕様を定義します。

この仕様は、単なるファイルのコピーではなく、「情報の鮮度を自動判別し、作業者のミスを物理的に防ぐ」 営農管理システムの基盤となるものです。


1. システム名称:KEINA Sync (Trilium-Gitea Smart Connector)

2. 開発の目的

  • Single Source of Truthの維持: Gitea(ローカル)を原本としつつ、Triliumを常に最新の視認環境として保つ開発基盤の定義.md]。
  • 先祖返りの防止: 出先などでTrilium側を直接編集した場合、ローカルの古いデータで上書きしてしまう事故を防ぐ。
  • 開発体験(DX)の向上: 「保存=即反映」を実現し、ドキュメント管理の煩わしさを排除する開発基盤の定義.md]。

3. 基本動作フロー

本システムは、以下の3つのフェーズで動作します。

① 比較フェーズ(Smart Check)

docs/trilium_map.json に登録されたファイルをスキャンし、ローカルとTriliumの状態を比較します。

  • ハッシュ比較: SHA-256を用いて内容が完全に一致するか確認。一致すれば何もしない。
  • 鮮度判定: 差分がある場合、ファイルの更新日時(Local)と lastUpdatedTime(Trilium API)を比較する。

② 更新フェーズ(Action)

判定結果に基づき、以下のいずれかの処理を行います。

  • Push (Local → Trilium): ローカルが新しい場合、Triliumへ内容を送信開発基盤の定義.md]。
  • Pull (Trilium → Local): Triliumが新しい場合、警告を表示し、ユーザーの指示でローカル原本を更新。
  • Sync (一致): 何もしない。

③ 証跡管理フェーズ(Traceability)

  • 同期に成功した際、どのファイルがどのノートIDへ反映されたかをログ出力する。
  • 注意: Giteaへの commitpush は、これまで通りユーザーが手動で行い、AIによる勝手なコード変更は行わない開発基盤の定義.md]。

4. ユーザーインターフェース(使い方)

単発コマンドとして、以下の3通りの使い方を想定しています。

コマンド 動作内容
python sync_trilium.py <file_path> 個別同期: 特定のファイル1つを賢く同期する(VSCodeタスク用)。
python sync_trilium.py --all 一括同期: マップにある全ファイルをスキャンし、差分があるものだけ更新。
python sync_trilium.py --force 強制同期: 鮮度判定を無視し、ローカルの内容でTriliumを全上書きする。

5. データの整合性ルール(鉄則)

  • UTF-8強制: 全ての通信は encode('utf-8') を経由し、Windows環境特有の文字化けを排除する開発基盤の定義.md]。
  • マッピング優先: trilium_map.json に記載のないファイルは、安全のため一切触らない。
  • API保護: Requests による直接通信を行い、SDKのバージョン依存によるバグを回避する開発基盤の定義.md]。

この仕様で、私たちの目指す「ドキュメントが腐らない環境」の定義と相違ないでしょうか?
問題なければ、この仕様を実現するために不可欠な最後のパズルピースである 「GETテスト(Triliumからの読み出し確認)」 の結果を確認し、本体コードの作成へ移ります。

まだテストを実行されていない場合は、お時間のある時に test_trilium_get.py の結果を教えていただけますと幸いです。

吉田 明 さんが1日前に更新

とんでもございません!「下働き」などとは露ほども思っておりません。
堅牢なインフラや基盤ツールを設計し、一寸の狂いもなく動作させることこそが、リードエンジニアとしての「最も重要でエキサイティングな仕事」です。
不確実なAIの挙動に頼らず、論理(Pythonコード)だけで完結させる「鉄の同期ツール」を構築しましょう。

では、「KEINA Sync (Trilium-Gitea Smart Connector)」 の本体実装に向けて、最終的な設計方針を確定させます。


KEINA Sync:実装設計書

1. 依存ライブラリ

  • 標準ライブラリのみ: os, sys, json, hashlib, datetime, time
  • 外部通信: requests (Raw HTTP通信を徹底)

2. データの鮮度判定ロジック

「どちらが最新か」を以下の優先順位で判断します。

  1. ハッシュチェック (SHA-256):
  • ローカルファイルの中身と、Triliumから取得した内容のハッシュを比較。
  • 一致していれば「変更なし」と判断し、即終了。
  1. タイムスタンプ比較:
  • ローカルの最終更新日時 (os.path.getmtime)
  • Triliumの lastUpdatedTime (APIから取得)
  • ※Triliumの時刻精度(ミリ秒)とPythonの精度を正規化して比較します。

3. モード別動作(安全策)

  • Normal Mode (デフォルト):

  • ローカルが新しい → TriliumへPush

  • Triliumが新しい → 警告を出し、上書きを停止。

  • Force Mode (--force):

  • 鮮度判定を無視し、常にローカルの内容でTriliumを強制上書き。

  • Pull Mode (--pull):

  • 明示的に指定した場合のみ、Triliumの内容でローカルファイルを上書き。


本体実装のための最終確認(GETテスト)

この堅牢なロジックを組むためには、Triliumから「内容(Content)」だけでなく「更新時刻(Metadata)」も取得できるかを確認する必要があります。

最後に、以下の tools/test_trilium_metadata.py を実行していただけますでしょうか?これが成功すれば、全てのピースが揃います。

import requests
import json
from datetime import datetime

# --- 設定 ---
TRILIUM_URL = "https://trilium.keinafarm.net"
TOKEN = "あなたのETAPIトークン"
NOTE_ID = "obB7Y6U8MPWM" 
# -----------

def test_metadata():
    # ノートのメタデータを取得するエンドポイント
    api_url = f"{TRILIUM_URL}/etapi/notes/{NOTE_ID}"
    headers = {"Authorization": TOKEN}
    
    try:
        response = requests.get(api_url, headers=headers)
        if response.status_code == 200:
            data = response.json()
            # Triliumの更新日時を取得
            lut = data.get('lastUpdatedTime')
            print(f"取得成功: ノート名 = {data.get('title')}")
            print(f"Trilium最終更新日時 (Raw): {lut}")
            return True
        else:
            print(f"失敗: {response.status_code}\n{response.text}")
            return False
    except Exception as e:
        print(f"エラー: {e}")
        return False

if __name__ == "__main__":
    test_metadata()

次のステップ

  1. このメタデータ取得テストの結果を教えてください。
  2. 成功を確認次第、「比較・ハッシュチェック・双方向対応」を備えた完成版スクリプト を一気に書き上げ、提供します。

基盤が盤石になれば、その後の「営農管理」のロジック構築に全力を注げます。テスト結果をお待ちしております!

吉田 明 さんが1日前に更新

  • ステータス新規 から 終了 に変更

他の形式にエクスポート: Atom PDF