Skip to content

機能仕様書: C# LSP統合機能 #95

@akiojin

Description

@akiojin

Spec

機能仕様書: C# LSP統合機能

機能ID: SPEC-e757a01f
作成日: 2025-10-17
更新日: 2026-02-23
ステータス: 進行中
入力: ユーザー説明: "C# LSP統合機能"

統合SPEC:

  • SPEC-aa705b2b: コードインデックスビルドのバックグラウンド実行(実装完了)
  • SPEC-eb99c755: Prebuilt better-sqlite3 配布(未実装)

実行フロー (main)

1. 入力からユーザー説明を解析
   → 完了: "C# LSP統合機能"
2. 説明から主要概念を抽出
   → アクター: Unity開発者、LLMユーザー
   → アクション: シンボル検索、参照検索、構造化編集、軽量スニペット編集、リネーム、インデックス管理
   → データ: C#シンボル、ソースコード、編集操作
   → 制約: Roslyn LSP、Unity非依存、構造化操作のみ
3. 不明確な側面: なし(既存実装のSpec化)
4. ユーザーシナリオ&テストセクションを記入
   → 7つの主要ユーザーストーリー定義済み
5. 機能要件を生成
   → 34個の機能要件をテスト可能な形式で定義
6. 主要エンティティを識別
   → シンボル、編集操作、インデックス
7. レビューチェックリストを実行
   → 技術詳細は参考実装セクションに分離
   → 要件はテスト可能
8. 戻り値: SUCCESS (計画準備完了)

⚡ クイックガイドライン

  • ✅ ユーザーが「何を」必要とし「なぜ」必要なのかに焦点を当てる
  • ❌ 「どのように」実装するかを避ける (技術スタック、API、コード構造なし)
  • 👥 ビジネス関係者向けに記述 (開発者向けではない)

機能概要

自己完結型のC# Language Server (LSP) を使用した、シンボル検索・参照検索・構造化編集・軽量スニペット編集・リネーム・インデックス管理機能。Unity通信に依存せず、ローカルでC#コードを安全に操作する。

ビジネス価値

  • 安全なコード編集: 構造化操作により、行ベースの危険な編集を排除
  • 効率的な検索: シンボルインデックスにより、大規模コードベースでも高速検索
  • Unity非依存: LSPはスタンドアロンで動作し、Unity Editor停止中でもコード編集可能
  • 柔軟な編集粒度: 大規模な構造化編集と1-2行の軽量編集を使い分けることで、LLMエージェントの効率を最大化

ユーザーシナリオ&テスト

主要ユーザーストーリー

US-1: シンボル検索

AS Unity開発者
I WANT クラス名/メソッド名/フィールド名でシンボルを検索したい
SO THAT 大規模プロジェクトでも目的のコードを素早く特定できる

受け入れ基準:

  • 名前でシンボルを検索できる(部分一致/完全一致)
  • シンボル種別(class/method/field/property)で絞り込める
  • 検索スコープ(Assets/Packages/All)を指定できる
  • 検索結果にファイルパス、行番号、コンテナ情報が含まれる

US-2: 参照検索

AS Unity開発者
I WANT シンボルの使用箇所をすべて検索したい
SO THAT リファクタリング影響範囲を把握できる

受け入れ基準:

  • シンボル名を指定して参照箇所を検索できる
  • 参照箇所にファイルパス、行番号、コード断片が含まれる
  • スニペットコンテキスト行数を指定できる
  • ページング対応でLLM最適化されている

US-3: 構造化編集(挿入/置換)

AS Unity開発者
I WANT メソッド本体やクラスメンバーを安全に編集したい
SO THAT 手動の行番号指定による編集ミスを防げる

受け入れ基準:

  • シンボルを指定してメソッド本体を置換できる
  • クラス内にメソッドを挿入できる(before/after指定)
  • 編集前にプレビューできる(オプション)
  • エラー時に詳細な診断情報が返る

US-4: リネーム(リファクタリング)

AS Unity開発者
I WANT クラス/メソッド/フィールド名を一括リネームしたい
SO THAT 手動での置換漏れを防げる

受け入れ基準:

  • シンボルを指定してプロジェクト全体でリネームできる
  • リネーム前にプレビューできる
  • 依存ファイルすべてに変更が適用される

US-5: シンボル定義取得

AS LLMユーザー
I WANT ファイル内のすべてのシンボル定義を取得したい
SO THAT コード構造を理解できる

受け入れ基準:

  • ファイルパスを指定してシンボル一覧を取得できる
  • シンボルの種別、名前、位置、コンテナが含まれる
  • ネストされたシンボル(内部クラス等)も取得できる

US-6: コードインデックス管理

AS Unity開発者
I WANT コードインデックスを構築・更新したい
SO THAT シンボル検索が高速に動作する

受け入れ基準:

  • プロジェクト全体のインデックスを構築できる
  • インデックス状態(ファイル数、カバレッジ)を確認できる
  • 増分更新をサポートする

US-7: 軽量スニペット編集

AS LLMエージェント
I WANT 1-2行規模のコード断片を安全に編集したい
SO THAT メソッド全体を置換せずに局所的な変更を高速に適用できる

受け入れ基準:

  • 同一ファイル内で最大10箇所の編集を1回の呼び出しで処理できる
  • アンカー文字列(前後のコードスニペット)を指定して編集対象を特定できる
  • 編集内容は80文字以内に制限され、超過する場合は拒否される
  • 括弧・波括弧の整合性チェックが自動実行され、エラー時はロールバックされる
  • 各編集の適用結果(applied/skipped/error)と理由が返される
  • ガード削除(nullチェック等)、条件式の最小変更、ログ挿入が主な用途

主要サブシナリオ:

US-7.1: ガード削除

  • 例外的なnullチェック(if (foo == null) { return; })を安全に削除
  • 削除対象が見つからない場合は適用せず、警告を返す
  • 編集後の空行や余分なセミコロンを自動整理

US-7.2: 条件式の最小変更

  • 比較演算子や呼び出し名など部分一致で最大5箇所まで置換
  • 周辺コンテキスト(前後2行)を指定して誤適用を防ぐ

US-7.3: ログ挿入

  • return文など特定キーワードをアンカーに直前へ1行挿入
  • 既存ログとの重複を検出し、重複時は挿入しない

US-8: バックグラウンドインデックスビルド(実装完了)

AS Unity開発者
I WANT コードインデックスのビルドをバックグラウンドで実行したい
SO THAT ビルド完了を待たずに他の作業を継続できる

受け入れ基準:

  • インデックスビルド開始後、1秒以内にジョブIDとともにレスポンスが返る
  • 実行中のビルドの進捗状況(処理済み/全体、処理速度)を確認できる
  • 既にビルドが実行中の場合、新たなビルドは開始されず既存のジョブIDが返される
  • ビルド完了時に結果情報(更新ファイル数、削除ファイル数、総シンボル数)を取得できる
  • 自動ビルド(IndexWatcher)と手動ビルドが同時に実行されることがない

主要サブシナリオ:

US-8.1: 非ブロッキングなビルド開始

  • 大規模プロジェクト(10000+ファイル)でもビルド開始が1秒以内に完了
  • ビルド開始後も他のMCPツールは正常に動作

US-8.2: 進捗確認

  • get_index_statusでリアルタイムの進捗情報を取得
  • 処理速度(files/sec)から完了時間を予測可能

US-8.3: 重複実行防止

  • 手動ビルド実行中に自動ビルドがトリガーされてもスキップ
  • 手動ビルド完了後に自動ビルドが再開

US-10: Worker Threadsによる非ブロッキングビルド(未実装)

AS Unity開発者
I WANT バックグラウンドインデックスビルド中も他のMCPツールが即座に応答してほしい
SO THAT ビルド中でもシンボル検索やpingが遅延なく動作する

受け入れ基準:

  • バックグラウンドビルド実行中もpingが1秒以内に応答する
  • バックグラウンドビルド実行中もget_index_statusが即座に進捗を返す
  • バックグラウンドビルド実行中もsearchfind_symbolが正常に動作する
  • ビルド処理自体の速度低下は10%以内に抑えられる
  • Worker Thread内でエラーが発生してもMCPサーバーは継続動作する

主要サブシナリオ:

US-10.1: 非ブロッキング応答

  • better-sqlite3の同期DB操作がメインスレッドをブロックしない
  • LSP documentSymbol呼び出しがメインスレッドをブロックしない

US-10.2: Worker Thread管理

  • Worker Threadは必要時にのみ起動し、アイドル時はリソースを解放
  • Worker Threadがクラッシュした場合、適切にリカバリーする

US-10.3: 進捗通知

  • ビルド進捗はメインスレッドに非同期で通知される
  • JobManager経由の進捗追跡は維持される

US-9: 初回起動の高速化(未実装)

AS Unity CLI Bridge新規ユーザー
I WANT 初回起動時にビルド待ち時間なくサーバーを使用したい
SO THAT MCPクライアントのタイムアウトを回避できる

受け入れ基準:

  • npx @akiojin/unity-cli@latest --help が30秒以内に完了する
  • better-sqlite3のネイティブドライバが初回から利用可能
  • 対応外プラットフォームではWASMフォールバックで動作する
  • 環境変数でネイティブビルド強制/スキップを制御できる

主要サブシナリオ:

US-9.1: Prebuiltバイナリの自動展開

  • linux/darwin/win32 × x64/arm64のprebuiltバイナリをnpm packageに同梱
  • postinstallで適切なバイナリを自動選択・展開

US-9.2: WASMフォールバック

  • 対応外プラットフォームでは即座にsql.jsにフォールバック
  • get_index_statusが成功することを確認

US-9.3: 環境変数による制御

  • UNITY_CLI_SKIP_NATIVE_BUILD=1 でネイティブビルドをスキップ
  • UNITY_CLI_FORCE_NATIVE=1 でソースビルドを強制

受け入れシナリオ

  1. 前提 プロジェクトにC#ファイルが存在、実行 クラス名検索、結果 該当クラスの定義場所が返る
  2. 前提 メソッドが複数箇所で使用、実行 参照検索、結果 すべての使用箇所リストが返る
  3. 前提 メソッドが存在、実行 メソッド本体置換、結果 新しい本体に置き換わる
  4. 前提 クラス名が複数ファイルで使用、実行 リネーム、結果 すべてのファイルで名前が変更される
  5. 前提 C#ファイルが存在、実行 シンボル定義取得、結果 ファイル内の全シンボルリストが返る
  6. 前提 プロジェクトが存在、実行 インデックス構築、結果 インデックスが作成され、検索が高速化される
  7. 前提 メソッド内にnullチェックガードが存在、実行 アンカー指定でガード削除、結果 ガードが削除され括弧整合性が保たれる
  8. 前提 条件式に誤った演算子が存在、実行 周辺コンテキスト指定で置換、結果 正しい演算子に置き換わる
  9. 前提 メソッド内に複数のreturn文が存在、実行 ログ挿入をreturn前に適用、結果 すべてのreturn直前にログが挿入される
  10. 前提 大規模プロジェクト(10000+ファイル)、実行 build_index、結果 1秒以内にジョブIDが返り、バックグラウンドでビルド継続
  11. 前提 インデックスビルドが実行中、実行 get_index_status、結果 進捗情報(処理済み/全体、処理速度)が返る
  12. 前提 インデックスビルドが実行中、実行 再度build_index、結果 エラーと既存のジョブIDが返る
  13. 前提 npmキャッシュをクリアした状態、実行 npx @akiojin/unity-cli@latest --help結果 30秒以内に完了
  14. 前提 対応外プラットフォーム、実行 get_index_status、結果 WASMフォールバックで成功
  15. 前提 indexWatcherがバックグラウンドビルドを実行中、実行 ping、結果 1秒以内に応答が返る
  16. 前提 indexWatcherがバックグラウンドビルドを実行中、実行 get_index_status、結果 即座に進捗情報が返る
  17. 前提 indexWatcherがバックグラウンドビルドを実行中、実行 search、結果 検索結果が正常に返る
  18. 前提 Worker Thread内でエラーが発生、実行 他のMCPツール呼び出し、結果 MCPサーバーは継続動作し応答が返る

エッジケース

  • 存在しないシンボルを検索した場合、空の結果が返るか?
  • 構造化編集でシンボルが見つからない場合、エラーメッセージは明確か?
  • リネーム時に構文エラーが発生した場合、変更はロールバックされるか?
  • 大規模プロジェクト(10000+ファイル)でインデックス構築時間は許容範囲か?
  • インデックス構築中に検索を実行した場合、どのように処理されるか?
  • 軽量スニペット編集でアンカーが一意に決定できない場合、候補一覧が返されるか?
  • 編集対象がコメントアウトされている場合、無視して警告が返されるか?
  • 編集後にコードフォーマッタが破綻する恐れがある場合、適用が拒否されるか?
  • 80文字制限を超える編集指示が与えられた場合、拒否理由と代替案が提示されるか?
  • ファイルがロックされている場合、失敗理由が明示されるか?

要件

シンボル検索機能要件

  • FR-001: システムは名前でシンボルを検索できる必要がある(部分一致/完全一致)
  • FR-002: システムはシンボル種別(class/method/field/property)で絞り込める必要がある
  • FR-003: システムは検索スコープ(Assets/Packages/Embedded/All)を指定できる必要がある
  • FR-004: システムは検索結果にファイルパス、行番号、コンテナ情報を含める必要がある

参照検索機能要件

  • FR-005: システムはシンボル名を指定して参照箇所を検索できる必要がある
  • FR-006: システムは参照箇所にファイルパス、行番号、コード断片を含める必要がある
  • FR-007: システムはスニペットコンテキスト行数を指定できる必要がある
  • FR-008: システムは参照検索結果をページングできる必要がある

構造化編集機能要件

  • FR-009: システムはシンボルを指定してメソッド本体を置換できる必要がある
  • FR-010: システムはクラス内にメソッドを挿入できる必要がある(before/after指定)
  • FR-011: システムは編集前にプレビューを提供できる必要がある(オプション)
  • FR-012: システムは編集エラー時に詳細な診断情報を返す必要がある
  • FR-013: システムは行ベース編集を禁止し、構造化操作のみ許可する必要がある

リネーム機能要件

  • FR-014: システムはシンボルを指定してプロジェクト全体でリネームできる必要がある
  • FR-015: システムはリネーム前にプレビューを提供できる必要がある
  • FR-016: システムは依存ファイルすべてに変更を適用する必要がある
  • FR-017: システムはリネーム失敗時に変更をロールバックする必要がある

シンボル定義取得機能要件

  • FR-018: システムはファイルパスを指定してシンボル一覧を取得できる必要がある
  • FR-019: システムはシンボルの種別、名前、位置、コンテナを含める必要がある
  • FR-020: システムはネストされたシンボル(内部クラス等)も取得できる必要がある

インデックス管理機能要件

  • FR-021: システムはプロジェクト全体のインデックスを構築できる必要がある
  • FR-022: システムはインデックス状態(ファイル数、カバレッジ)を確認できる必要がある
  • FR-023: システムは増分更新をサポートする必要がある
  • FR-024: システムはインデックスをSQLiteに永続化する必要がある

DBインデックス必須要件

  • FR-051: シンボル検索(find_symbol)はDBインデックスが構築されている場合のみ結果を返し、未構築の場合はLSPフォールバックせずエラーを返す必要がある
  • FR-052: 参照検索(find_refs)はDBインデックスが構築されている場合のみ実行され、未構築の場合はエラーを返す必要がある
  • FR-053: インデックスが構築中の場合、システムはindex_buildingエラーとビルド進捗情報(処理済み/全体、パーセンテージ)を返す必要がある
  • FR-054: インデックスが未構築で構築ジョブも実行されていない場合、システムはindex_not_readyエラーとbuild_indexの使用を促すヒントを返す必要がある
  • FR-055: MCPサーバー起動時にDBインデックスが存在しない場合、システムは自動的にバックグラウンドでインデックス構築を開始する必要がある
  • FR-062: 参照検索(find_refs)はページングのために startAfter を受け付け、結果が切り詰められた場合は cursor を返す必要がある

軽量スニペット編集機能要件

  • FR-025: システムは1回の呼び出しで最大10箇所の局所編集を処理できる必要がある
  • FR-026: システムは各編集指示に操作タイプ(削除/置換/挿入)、アンカー情報、編集内容を必須として受け取る必要がある
  • FR-027: システムはアンカー検証を行い、対象が一意に決定できない場合は候補一覧を返す必要がある
  • FR-028: システムはアンカー一致後の差分が80文字以内であることを検証し、超過する場合は拒否する必要がある
  • FR-029: システムは編集結果適用前に括弧・波括弧の整合性をチェックし、不整合時は全体をロールバックする必要がある
  • FR-030: システムは各編集について status(applied/skipped/error)と reason、編集前後のスニペットを返す必要がある
  • FR-031: システムはプレビューモード時に副作用を発生させず、候補結果とフォーマット変更を提示する必要がある
  • FR-032: システムは Assets/ または Packages/ 外のファイルを拒否する必要がある
  • FR-033: システムは編集後に空になったブロック(空のif文等)を検出し、警告を返す必要がある
  • FR-034: システムは編集結果のハッシュを返して edit_structured との併用時の重複適用を防ぐ必要がある
  • FR-035: システムは C# LSP バイナリを ~/.unity/tools/lsp/<rid>/(または UNITY_CLI_TOOLS_ROOT で指定されたディレクトリ)に配置し、プロジェクトローカルの ./.unity/tools に依存しない構成を保証する必要がある

バックグラウンドビルド機能要件(実装完了)

  • FR-036: システムはインデックスビルドをバックグラウンドで実行し、即座に制御を開発者に返す必要がある
  • FR-037: システムはビルド開始時に一意のジョブIDを生成して返す必要がある
  • FR-038: 開発者はいつでもビルドの進捗状況を確認できる必要がある
  • FR-039: システムは既に実行中のビルドがある場合、新たなビルドを開始せず、既存のジョブIDを返す必要がある
  • FR-040: システムは実行中のビルドの進捗情報(処理済みファイル数、全体のファイル数、処理速度)を提供する必要がある
  • FR-041: システムはビルド完了時に結果情報(更新されたファイル数、削除されたファイル数、総シンボル数)を保持する必要がある
  • FR-042: システムはビルド中のエラーを記録し、開発者に通知する必要がある
  • FR-043: システムは自動ビルド(IndexWatcher)と手動ビルドの競合を防ぐ必要がある
  • FR-044: システムは既存のインデックス状態確認機能との下位互換性を維持する必要がある
  • FR-045: システムは進捗ログを一定パーセンテージごと(デフォルト: 10%)に出力する必要がある

Worker Threads非ブロッキング機能要件(未実装)

  • FR-056: システムはインデックスビルド処理をWorker Threadで実行し、メインスレッドをブロックしない必要がある
  • FR-057: システムはバックグラウンドビルド中もpingが1秒以内に応答する必要がある
  • FR-058: システムはWorker Thread内でのエラーをメインスレッドに伝播し、MCPサーバーの継続動作を保証する必要がある
  • FR-059: システムはビルド進捗をWorker Threadからメインスレッドに非同期で通知する必要がある
  • FR-060: システムは既存のbuild_indexツールインターフェースを維持し、後方互換性を保つ必要がある
  • FR-061: システムはindexWatcherからの呼び出しもWorker Thread経由で実行する必要がある

Prebuilt配布機能要件(未実装)

  • FR-046: システムはnpm パッケージに linux/darwin/win32 × x64/arm64 × Node18/20/22 向け better-sqlite3 バイナリを同梱する必要がある
  • FR-047: システムはインストール時に同梱バイナリを優先して展開し、30秒以内に完了する必要がある
  • FR-048: システムは未対応プラットフォームでは即座に WASM フォールバックに切り替わる必要がある
  • FR-049: システムは環境変数でネイティブビルド強制 (UNITY_CLI_FORCE_NATIVE=1) またはスキップ (UNITY_CLI_SKIP_NATIVE_BUILD=1) を制御できる必要がある
  • FR-050: システムはREADME(英/日)に初回体験と環境変数の説明を記載する必要がある

非機能要件

性能要件

  • NFR-001: シンボル検索は1000ファイルで1秒以内に完了する必要がある
  • NFR-002: インデックス構築は10000ファイルで2分以内に完了する必要がある

信頼性要件

  • NFR-003: 構造化編集は構文エラーを事前検出する必要がある
  • NFR-004: リネームは原子的操作である必要がある(全成功または全失敗)

LLM最適化要件

  • NFR-005: 検索結果はページング対応である必要がある(maxBytes、pageSize)
  • NFR-006: 参照検索はスニペットコンテキストを制限できる必要がある
  • NFR-007: エラーメッセージは要約される必要がある(≤30件、≤200文字)

軽量スニペット編集要件

  • NFR-008: 最大10箇所編集でレスポンス時間は2秒以内である必要がある(LSP常駐時)
  • NFR-009: エラーメッセージは200文字以内、日本語で返却される必要がある
  • NFR-010: 成功応答のテキストフィールドは1000文字以内に抑制される必要がある
  • NFR-011: 競合発生時は編集箇所を自動スキップし、他の編集は継続する必要がある(ベストエフォート)
  • NFR-012: LSP未起動時は自動起動を試み、失敗時に詳細なトラブルシュート情報を返す必要がある

LSP分離要件

  • NFR-023: LSP操作は用途別に独立プロセスで実行し、長時間処理が他のLSP操作をブロックしないこと
  • NFR-024: C# LSPは同一プロセス内でリクエストを並列実行でき、同一ファイルの読み書きは排他制御されること
  • NFR-025: 実行中のLSPが旧バージョンで、ローカルに新バージョンが存在する場合は再起動して最新バージョンに切り替えること
  • NFR-026: 複数MCPサーバー起動時はクライアントが対象プロジェクトを指定でき、サーバー側で一致検証を行うこと

LSP性能履歴要件

  • NFR-027: scripts/lsp-perf-check.sh は毎回、固定ケース一式について速度(mean/p95)を計測する必要がある
  • NFR-028: scripts/lsp-perf-check.sh は毎回、レスポンスサイズ(bytes/chars/tokens)を計測し、tokenizer o200k_base で token 数を算出する必要がある
  • NFR-029: scripts/lsp-perf-check.sh は毎回、specs/perf/lsp-history.jsonl に履歴を1レコード追記する必要がある

バックグラウンドビルド要件(実装完了)

  • NFR-013: ビルド開始後1秒以内にジョブIDとともにレスポンスが返る必要がある
  • NFR-014: 進捗更新は500ms間隔で行われる必要がある
  • NFR-015: 完了ジョブは5分後に自動削除される必要がある(メモリリーク防止)

Worker Threads要件(未実装)

  • NFR-019: バックグラウンドビルド中もpingが1秒以内に応答する必要がある
  • NFR-020: Worker Thread化によるビルド処理の速度低下は10%以内に抑える必要がある
  • NFR-021: Worker Threadによるメモリ使用量の増加は50MB以内に抑える必要がある
  • NFR-022: Node.js 18以上をサポートする必要がある

Prebuilt配布要件(未実装)

  • NFR-016: npx @akiojin/unity-cli@latest --help が30秒以内に完了する必要がある
  • NFR-017: postinstallスクリプトは5秒以内に完了する必要がある
  • NFR-018: npm パッケージサイズは50MB未満である必要がある

主要エンティティ

  • シンボル: クラス/メソッド/フィールド/プロパティ等のコード要素。名前、種別、位置、コンテナを持つ
  • 編集操作: コード変更の単位。挿入/置換/削除、対象シンボル、新しいテキストを含む
  • スニペット編集操作: 軽量な局所編集の単位。操作タイプ(削除/置換/挿入)、アンカー情報(前後のコードスニペット)、編集内容(80文字以内)、適用結果(status/reason)を含む
  • インデックス: シンボル情報のSQLiteデータベース。高速検索のために永続化される
  • ビルドジョブ: バックグラウンド実行中または完了したインデックスビルド。ジョブID、状態、進捗情報、結果を持つ
  • Prebuiltバイナリ: プラットフォーム別のbetter-sqlite3ネイティブバイナリ。OS、CPU、Nodeバージョンの組み合わせで管理

スコープ外

以下の機能は本仕様のスコープ外とし、将来のバージョンで対応:

  • コード補完機能
  • ホバー情報表示
  • 定義ジャンプ(IDEナビゲーション)
  • コードフォーマット機能
  • リアルタイム診断(エラーチェック)
  • 大規模リファクタリング(複数ファイル横断での意味解析)
  • テンプレート生成

技術制約

  • 自己完結型Roslyn LSPを使用(Unity通信不要)
  • インデックスは.unity/cache/code-indexに保存
  • 構造化操作とアンカーベース軽量編集をサポート(純粋な行ベース編集禁止)
  • Unityコンパイル/ドメインリロードの影響を受けない
  • 軽量スニペット編集は80文字制限、最大10箇所/リクエスト

参考実装

本仕様は既存実装を文書化したものです。参考実装:

  • unity-cli/src/handlers/script/ScriptSymbolFindToolHandler.js: シンボル検索
  • unity-cli/src/handlers/script/ScriptRefsFindToolHandler.js: 参照検索
  • unity-cli/src/handlers/script/ScriptEditStructuredToolHandler.js: 構造化編集
  • unity-cli/src/handlers/script/ScriptEditSnippetToolHandler.js: 軽量スニペット編集(計画中)
  • unity-cli/src/handlers/script/ScriptRefactorRenameToolHandler.js: リネーム
  • unity-cli/src/handlers/script/ScriptSymbolsGetToolHandler.js: シンボル定義取得
  • unity-cli/src/handlers/script/CodeIndexBuildToolHandler.js: インデックス構築
  • unity-cli/src/handlers/script/CodeIndexStatusToolHandler.js: インデックス状態

レビュー&受け入れチェックリスト

コンテンツ品質

  • 実装詳細なし (言語、フレームワーク、API)
  • ユーザー価値とビジネスニーズに焦点
  • 非技術関係者向けに記述
  • すべての必須セクション完成

要件完全性

  • [要明確化]マーカーが残っていない
  • 要件はテスト可能で曖昧さがない
  • 成功基準は測定可能
  • スコープが明確に境界付けられている
  • 依存関係と前提条件が識別されている

実行ステータス

  • ユーザー説明を解析済み
  • 主要概念を抽出済み
  • 曖昧さをマーク済み(なし)
  • ユーザーシナリオを定義済み(US-1〜US-9)
  • 要件を生成済み(FR-001〜FR-050、NFR-001〜NFR-018)
  • エンティティを識別済み
  • レビューチェックリスト合格

ステータス: 進行中

実装状況:

  • US-1〜US-7: C# LSP統合機能(完了)
  • US-8: バックグラウンドインデックスビルド(完了)
  • US-9: 初回起動の高速化(未実装)
  • US-10: Worker Threadsによる非ブロッキングビルド(未実装)

Plan

実装計画: C# LSP統合機能(コードインデックス)

機能ID: SPEC-e757a01f | 更新日: 2025-11-26 | ステータス: 完了

統合SPEC:

  • SPEC-aa705b2b: コードインデックスビルドのバックグラウンド実行(実装完了)
  • SPEC-eb99c755: Prebuilt better-sqlite3 配布(実装完了)

概要

自己完結型のC# Language Server (LSP) を使用した、シンボル検索・参照検索・構造化編集・軽量スニペット編集・リネーム・インデックス管理・バックグラウンドビルド・初回起動高速化機能。

実装状況

実装完了(全9ユーザーストーリー)

  • ✅ US-1: シンボル検索
  • ✅ US-2: 参照検索
  • ✅ US-3: 構造化編集(メソッド本体置換、メンバー挿入)
  • ✅ US-4: リネーム
  • ✅ US-5: シンボル定義取得
  • ✅ US-6: コードインデックス管理
  • ✅ US-7: 軽量スニペット編集
  • ✅ US-8: バックグラウンドインデックスビルド
  • ✅ US-9: 初回起動の高速化(Prebuilt better-sqlite3 配布)
    • TDDテスト: 11ユニット + 5統合テスト
    • CIワークフロー: prebuild.yml
    • ドキュメント: README.mdにトラブルシューティング追加

軽量スニペット編集の技術設計

背景

既存の edit_structured はメソッド全体の置換やクラス単位の追加に最適化されており、1-2行規模の局所編集には過剰です。過去に行ベース編集を許容した際には括弧整合が破綻し、最終的に edit_structured のみを許可する方針へ転換した経緯があります。本設計では、その制約を維持しつつ「小さな断片編集を安全に適用する」ための新ツール edit_snippet を設計します。

アーキテクチャ

1. 指示バンドル設計

1回のリクエストで最大10件の編集を受け取るJSONスキーマ:

{
  "filePath": "Assets/Scripts/Example.cs",
  "edits": [
    {
      "operation": "delete|replace|insert",
      "anchor": {
        "before": "前後2-3行のコンテキスト",
        "target": "編集対象の文字列",
        "after": "前後2-3行のコンテキスト"
      },
      "newText": "置換/挿入の場合の新テキスト(80文字以内)"
    }
  ],
  "preview": false
}

2. アンカー解決エンジン

  • LSPの mcp/findText または既存 search 結果を活用してアンカー候補を特定
  • 候補が複数ある場合はエラーで返し、曖昧さを許容しない
  • 実際の編集適用前に該当範囲を再抽出し、diff長を80文字以内に収める

3. 括弧・構文検証パイプライン

  • 編集後に対象ファイルを一時バッファへ適用
  • LSPへシンタックスチェックを要求(textDocument/diagnostics
  • diagnosticsに括弧不整合・構文エラーが含まれる場合は全編集をロールバック
  • 必要であれば mcp/formatDocument をpreflightで呼び出し、フォーマット破綻も検出

4. 適用パイプライン

  • Previewモード: アンカー結果と推定diff、構文検証結果を返し書き込みは行わない
  • Applyモード: 全編集をまとめて WorkspaceEdit としてLSPへ送信し、ハッシュ値(before/after)をレスポンスに含めて二重適用防止を支援

5. フォールバック設計

  • アンカーが解決できない場合や差分が80文字超の場合は早期に失敗
  • edit_structured へのフォールバック案内を含む
  • エージェント指示を更新し、用途の棲み分けを明確化

技術コンテキスト

  • 言語/ランタイム: Node.js 20.x (既存MCPサーバ)、ECMAScriptモジュール
  • 主要依存: LspRpcClient(内製C# LSPラッパー)、Roslynベースの構文解析(LSPサーバ側)
  • 対象ファイル: Assets/ および Packages/ 以下のC#スクリプト
  • テスト: unity-cli/tests/unit/handlers/script/*.test.js(mocha + chai)
  • 制約: LSPから取得できる構文情報とテキスト編集APIで括弧整合を検証、Node側のみで完結

リサーチ項目(Phase 0)

  • Roslyn LSP側で任意テキスト編集後に即時構文診断を得る最適なリクエストを特定
  • LspRpcClientに複数テキストEditを適用するAPIの存在確認と拡張方法検討
  • 既存 search のレスポンス構造確認、アンカー解像度への再利用可能性検証
  • フォーマッタ(dotnet-format等)呼び出しの必要性評価

バックグラウンドインデックスビルドの技術設計(実装完了)

アーキテクチャ

1. JobManagerクラス

メモリ内Mapでジョブを管理するシングルトン:

  • create(jobId, jobFn): ジョブ作成&バックグラウンド実行
  • get(jobId): ジョブ状態取得
  • cleanup(jobId, retentionMs): 自動削除(5分後)

2. バックグラウンド実行パターン

  • Promise非同期実行(Worker Threads不要)
  • オブジェクト参照による進捗共有(EventEmitter不要)
  • 1ビルドジョブのみ許可(リソース競合防止)

3. IndexWatcher統合

  • JobManagerで実行中ジョブをチェック
  • 手動ビルド実行中は自動ビルドをスキップ
  • ジョブID命名規則: build-xxx(手動)、watcher-xxx(自動)

実装ファイル

  • unity-cli/src/core/jobManager.js: JobManagerクラス
  • unity-cli/src/handlers/script/CodeIndexBuildToolHandler.js: バックグラウンド化
  • unity-cli/src/handlers/script/CodeIndexStatusToolHandler.js: buildJob拡張
  • unity-cli/src/core/indexWatcher.js: JobManager統合

Prebuilt better-sqlite3 配布の技術設計(未実装)

アーキテクチャ

1. Prebuiltバイナリ同梱

  • npm パッケージに主要プラットフォーム向けバイナリを同梱
  • 対象: linux/darwin/win32 × x64/arm64 × Node18/20/22

2. postinstallスクリプト

  • ensure-better-sqlite3.mjs: バイナリ検知とフォールバック
  • 同梱バイナリを優先展開(ソースビルドなし)
  • 未対応プラットフォームはWASMフォールバック

3. 環境変数制御

  • UNITY_CLI_SKIP_NATIVE_BUILD=1: ネイティブビルドスキップ
  • UNITY_CLI_FORCE_NATIVE=1: ソースビルド強制

実装予定ファイル

  • unity-cli/prebuilt/better-sqlite3/: プラットフォーム別バイナリ
  • unity-cli/scripts/ensure-better-sqlite3.mjs: postinstallスクリプト
  • .github/workflows/prebuild.yml: CI用ビルドワークフロー

参考実装

実装詳細については spec.md の「参考実装」セクションを参照してください。


本ドキュメントは2025-10-24に作成され、2025-11-26にSPEC統合に伴い更新されました

Tasks

タスク: C# LSP統合機能(コードインデックス)

機能ID: SPEC-e757a01f | ステータス: 完了

統合SPEC:

  • SPEC-aa705b2b: コードインデックスビルドのバックグラウンド実行(実装完了)
  • SPEC-eb99c755: Prebuilt better-sqlite3 配布(実装完了)

実装状況サマリー

ユーザーストーリー ステータス テストカバレッジ
US-1: シンボル検索 ✅ 完了 ユニット + E2E
US-2: 参照検索 ✅ 完了 ユニット + E2E
US-3: 構造化編集 ✅ 完了 ユニット
US-4: リネーム ✅ 完了 ユニット
US-5: シンボル定義取得 ✅ 完了 ユニット
US-6: コードインデックス管理 ✅ 完了 ユニット
US-7: 軽量スニペット編集 ✅ 完了 ユニット (9/9)
US-8: バックグラウンドビルド ✅ 完了 ユニット + 統合 (20/20)
US-9: 初回起動高速化 ✅ 完了 ユニット (11) + 統合 (5)
US-10: LSPプロセス分離 ✅ 完了 ユニット
US-11: LSPバージョン切替 ✅ 完了 ユニット
US-12: サーバー識別 ✅ 完了 ユニット

US-1〜US-6: 基本機能(完了済み)

これらの機能は初期実装で完了しています。

テストファイル

  • tests/unit/handlers/script/ScriptSymbolFindToolHandler.test.js
  • tests/unit/handlers/script/ScriptSymbolsGetToolHandler.test.js
  • tests/unit/handlers/script/ScriptRefsFindToolHandler.test.js
  • tests/unit/handlers/script/ScriptEditStructuredToolHandler.test.js
  • tests/unit/handlers/script/ScriptRefactorRenameToolHandler.test.js
  • tests/unit/handlers/script/CodeIndexStatusToolHandler.test.js
  • tests/unit/handlers/script/CodeIndexUpdateToolHandler.test.js

US-2: 参照検索ページング(完了)

Phase 3: テスト ✅

  • T-201: find_refs の cursor/startAfter ページング動作(ユニット)

テストファイル: tests/unit/handlers/script/ScriptRefsFindToolHandler.test.js


US-7: 軽量スニペット編集(完了済み)

Phase 0: リサーチ ✅

  • R-001: Roslyn LSP側で構文診断を得るリクエストを特定
    • 結果: LspRpcClient.validateText() (mcp/validateTextEdits) が利用可能
  • R-002: 複数テキストEditを適用するAPI確認
    • 結果: 専用API不要、テキストレベルの順次適用で十分
  • R-003: searchのアンカー解決への再利用可能性
    • 結果: search不要、indexOfで十分
  • R-004: フォーマッタ呼び出しの必要性評価
    • 結果: 構文診断のみで十分、フォーマッタ不要

Phase 1-2: 設計・実装 ✅ スキップ(実装済み)

Phase 0リサーチにてScriptEditSnippetToolHandler.jsが既に実装済みと判明。

Phase 3: テスト ✅

  • T-001: 複数ガード削除のプレビュー生成
  • T-002: 80文字制限の拒否
  • T-003: 括弧不整合時のロールバック
  • T-004: アンカー一意性検証(複数マッチ時エラー)
  • T-005: replace操作
  • T-006: insert操作(position=after/before)
  • T-007: バッチ編集(3件の順次適用)
  • T-008: Applyモード(ファイル書き込み)

テストファイル: tests/unit/handlers/script/ScriptEditSnippetToolHandler.test.js
テスト結果: 9/9 成功

Phase 4: ドキュメント ✅

  • DOC-001: CLAUDE.md更新(edit_snippet使用ガイドライン)
  • DOC-002: 使用例追加
  • DOC-003: トラブルシューティング追加

US-8: バックグラウンドインデックスビルド(完了済み)

統合元: SPEC-aa705b2b

Phase 0: リサーチ ✅

  • R-005: Node.jsバックグラウンドジョブパターン
    • 決定: オブジェクト参照による進捗共有
  • R-006: メモリ内ジョブ管理 vs 永続化
    • 決定: メモリ内Map(永続化なし)
  • R-007: IndexWatcherとの統合
    • 決定: JobManagerでの一元管理 + runningフラグ保持
  • R-008: 既存ハンドラの拡張パターン
    • 決定: 下位互換性完全維持

Phase 3.1: セットアップ ✅

  • T001: ESLint/Prettier設定確認
  • T002: テスト環境確認
  • T003: jobManager.js配置場所確認 (src/core/jobManager.js)

Phase 3.2: テストファースト (TDD) ✅

Contract Tests:

  • T004: tests/unit/core/jobManager.test.js - JobManager contract tests (13テスト)
  • T005: tests/unit/handlers/script/CodeIndexBuildToolHandler.test.js - バックグラウンド実行契約
  • T006: tests/unit/handlers/script/CodeIndexStatusToolHandler.test.js - buildJob拡張契約

Integration Tests:

  • T007: tests/integration/code-index-background.test.js - US-1: 非ブロッキングビルド
  • T008: 同上 - US-2: 進捗状況の可視化
  • T009: 同上 - US-3: 重複実行の防止

Phase 3.3: コア実装 ✅

  • T010: src/core/jobManager.js - JobManagerクラス実装 (164行)
  • T011: src/handlers/script/CodeIndexBuildToolHandler.js - バックグラウンド化
  • T012: src/handlers/script/CodeIndexStatusToolHandler.js - buildJob拡張
  • T013: src/core/indexWatcher.js - JobManager統合

Phase 3.4: 統合 ✅

  • T014: IndexWatcher統合E2Eテスト (4シナリオ)
  • T015: エラーハンドリング改善

Phase 3.5: 仕上げ ✅

  • T016: Unit testsカバレッジ確認 (80%以上)
  • T017: パフォーマンステスト
  • T018: ドキュメント更新
  • T019: コードクリーンアップ
  • T020: 最終検証

全体進捗: 20/20タスク完了 ✅
実装完了日: 2025-10-29

テスト結果サマリー

  • ✅ JobManager: 13/13 contract tests合格
  • ✅ Integration tests: US-1, US-2, US-3すべてカバー
  • ✅ IndexWatcher E2E: 4つの統合シナリオテスト
  • ✅ パフォーマンス: build_index < 1秒、get_index_status < 100ms

実装ファイル

  • src/core/jobManager.js (新規作成)
  • src/handlers/script/CodeIndexBuildToolHandler.js (変更)
  • src/handlers/script/CodeIndexStatusToolHandler.js (変更)
  • src/core/indexWatcher.js (変更)

US-9: 初回起動高速化(完了)

統合元: SPEC-eb99c755

Phase 0: リサーチ ✅

  • R-009: Prebuildツールチェーン
    • 決定: npm package内にprebuiltを同梱、postinstallで展開

Phase 3.1: セットアップ ✅

  • T101 [P] prebuilt/better-sqlite3/ディレクトリ構造作成
  • T102 [P] prebuildifyツール評価・選定
  • T103 [P] GitHub Actions用ビルドマトリクス設計

Phase 3.2: テストファースト (TDD)

Contract Tests:

  • T104 [P] tests/unit/scripts/ensure-better-sqlite3.test.js - postinstallスクリプト契約

    • 契約: プラットフォーム検出 (linux/darwin/win32 × x64/arm64)
    • 契約: Node ABIバージョン検出 (18.x=115, 20.x=120, 22.x=131)
    • 契約: 同梱バイナリ優先展開
    • 契約: WASMフォールバック
    • 結果: 6/6テスト成功
  • T105 [P] tests/integration/prebuilt-sqlite.test.js - 初回起動時間テスト

    • シナリオ: クリーンインストール → 30秒以内に起動完了
    • シナリオ: 未対応プラットフォーム → WASMフォールバック成功
    • 結果: 1/1テスト成功

Phase 3.3: コア実装 ✅

  • T106 unity-cli/scripts/ensure-better-sqlite3.mjs - postinstallスクリプト実装

    • 実装: プラットフォーム・アーキテクチャ検出
    • 実装: prebuilt/better-sqlite3/からバイナリコピー
    • 実装: 環境変数制御 (UNITY_CLI_SKIP_NATIVE_BUILD, UNITY_CLI_FORCE_NATIVE)
    • 実装: WASMフォールバック
  • T107 [P] .github/workflows/prebuild.yml - CI用ビルドワークフロー

    • 実装: ビルドマトリクス (linux/darwin/win32 × x64/arm64 × Node18/20/22)
    • 実装: クロスコンパイル対応(Linux arm64)
    • 実装: prebuilt/better-sqlite3/へのアーティファクト保存・マニフェスト生成
  • T108 unity-cli/package.json - postinstall設定

    • 変更: "postinstall": "node scripts/ensure-better-sqlite3.mjs"
    • 変更: prebuilt/をnpm publishに含める
    • 現状: postinstallnode scripts/ensure-better-sqlite3.mjs

Phase 3.4: 統合

  • T109 CI統合テスト

    • テスト: 各プラットフォームでのインストール検証
    • テスト: postinstall < 5秒
    • 結果: 未実施
  • T110 npm publish検証

    • テスト: パッケージサイズ < 50MB
    • テスト: prebuilt/better-sqlite3/が正しく含まれている
    • 結果: ローカル npm pack --workspace unity-cli で一覧/サイズ確認(package size 167.4 kB)。prebuilt/better-sqlite3 の実バイナリはCIで要検証。

Phase 3.5: 仕上げ

  • T111 [P] ドキュメント更新

    • 更新: README.mdにインストール手順追加
    • 更新: トラブルシューティング(WASMフォールバック)
  • T112 最終検証

    • 実行: クリーンインストールテスト
    • 確認: 30秒タイムアウト内に完了
    • 確認: 全対応プラットフォームで動作
    • 結果: ローカルで npm cache clean --force 後に npx --yes @akiojin/unity-cli@latest --help を実行。計測 1.69 秒で完了。全プラットフォームの検証はCIで要実施。

依存関係

US-9タスクの順序制約

Setup (T101-T103)
    ↓
Tests (T104-T105) ← すべて並列実行可能
    ↓
Core (T106-T108)
  T106 (postinstall) ← T104の合格が条件
  T107 (CI workflow) ← 並列実行可能
  T108 (package.json) ← T106完了が条件
    ↓
Integration (T109-T110) ← T106-T108完了が条件
    ↓
Polish (T111-T112) ← T109-T110完了が条件

TDDカバレッジ分析

テストファイル一覧

カテゴリ ファイル テスト数
Core tests/unit/core/jobManager.test.js 15
Core tests/unit/core/codeIndex.test.js 3
Core tests/unit/core/codeIndexDb.test.js 2
Core tests/unit/core/indexWatcher.test.js 6
Handler tests/unit/handlers/script/*.test.js 14ファイル
Integration tests/integration/code-index-background.test.js 15
Scripts tests/unit/scripts/ensure-better-sqlite3.test.js 11
Integration tests/integration/prebuilt-sqlite.test.js 5

カバレッジ状況

  • US-1〜US-7: ユニットテストでカバー
  • US-8: ユニット + 統合テストで完全カバー
  • US-9: ユニット(11) + 統合(5)テストでカバー(CI統合テストはprebuild.yml実行待ち)

US-10: LSPプロセス分離(完了済み)

Phase 3: テスト ✅

  • T-301: 目的別のLSPインスタンスが分離されること
  • T-302: symbols/rename/edit_structured/remove_symbol/update_index が専用LSPを使用すること
  • T-303: C# LSP並列実行のロック契約(file/write/limiter)を検証すること

テストファイル:

  • tests/unit/lsp/LspIsolationRouting.test.js
  • lsp/ConcurrencyTests.cs

US-11: LSPバージョン切替(完了済み)

Phase 3: テスト ✅

  • T-401: 実行中LSPのバージョンと希望バージョンが異なる場合は再起動すること

テストファイル:

  • tests/unit/lsp/LspProcessManager.test.js

US-12: サーバー識別(完了済み)

Phase 3: テスト ✅

  • T-501: get_server_info で pid/projectRoot/workspaceRoot を取得できること
  • T-502: projectRoot 不一致時は tools/call がエラーを返すこと(requireClientRoot 有効時)

テストファイル:

  • tests/unit/handlers/system/SystemGetServerInfoToolHandler.test.js
  • tests/unit/core/startServer.test.js

参考

  • spec.md: 機能要件とユーザーストーリー
  • plan.md: 技術設計とアーキテクチャ
  • research.md: リサーチ結果
  • data-model.md: エンティティ定義
  • contracts/: API契約定義

本ドキュメントは2025-10-24に作成され、2025-11-26にSPEC統合に伴い更新されました

TDD

TODO

Research

Phase 0: リサーチ結果

機能ID: SPEC-e757a01f (US-7: 軽量スニペット編集)
実施日: 2025-10-24
ステータス: ✅ 完了

概要

軽量スニペット編集ツール(edit_snippet)の実装に先立ち、技術的な実現可能性と既存インフラの利用可能性を調査しました。

調査結果

R-001: Roslyn LSP側で構文診断を得る最適なリクエスト

結論: ✅ mcp/validateTextEdits メソッドを使用

詳細:

  • 実装場所: LspRpcClient.validateText() (src/lsp/LspRpcClient.js:102-114)
  • メソッド: mcp/validateTextEdits
  • パラメータ:
    • relative: ファイルパス(Assets/またはPackages/からの相対パス)
    • newText: 編集後のテキスト全体
  • レスポンス: 診断結果の配列
    {
      severity: number,  // 1=Error, 2=Warning, 3=Info, 4=Hint
      message: string,
      id: string,
      line: number,
      column: number
    }
  • エラー検出: severity === 1 でエラーを判定

実装済み: すでにLspRpcClientに実装されており、そのまま利用可能。


R-002: LspRpcClientに複数テキストEditを適用するAPI

結論: ✅ 専用APIは不要(現在の実装で十分)

詳細:

  • ScriptEditSnippetToolHandler は複数編集をテキストレベルで順次適用(ループ処理)
  • 各編集は単純な文字列操作(indexOf + スライス+結合)
  • 最終的なテキストを一度だけ validateText で検証
  • この方式で以下を実現:
    • 最大10箇所の編集を1回のリクエストで処理
    • 中間状態の検証は不要(最終結果のみ検証)
    • シンプルで効率的

既存メソッド:

  • LspRpcClient.request(method, params): 汎用リクエストメソッド
  • LspRpcClient.validateText(relative, newText): テキスト検証専用

結論: 拡張不要。現在のアプローチで要件を満たす。


R-003: search のレスポンス構造とアンカー解決への再利用

結論: ✅ search は不要(単純な indexOf で十分)

詳細:

  • search の用途: 大規模ファイル横断検索

    • glob/regex パターンマッチング
    • ページング対応
    • セマンティックフィルタ(namespace, container, identifier)
    • 3つの出力モード(metadata/snippets/full)
  • 軽量スニペット編集のアンカー解決:

    • 単一ファイル内の正確な文字列一致
    • text.indexOf(target) で実現(ScriptEditSnippetToolHandler:171)
    • 一意性チェック: 複数マッチ時はエラー(179-181行目)

理由:

  • アンカーは正確な文字列一致が必須
  • ファイル横断検索は不要
  • indexOf で O(n) の高速検索
  • search はオーバーエンジニアリング

結論: 既存実装の indexOf 方式が最適。


R-004: フォーマッタ(dotnet-format等)呼び出しの必要性

結論: ✅ フォーマッタ呼び出しは不要

詳細:

  • 構文診断で十分な理由:

    • validateText は括弧・波括弧の整合性を検出
    • 構文エラー(severity === 1)があれば即座に失敗
    • 軽量編集(1-2行、80文字以内)なのでフォーマット破綻リスクは最小限
  • Unity側での自動フォーマット:

    • Unity Editor は C# ファイル保存時に自動フォーマット可能
    • IDE(Rider、VS Code)もフォーマット機能あり
    • 開発者が手動でフォーマット可能
  • フォーマッタ不要の根拠:

    • 小規模編集(最大80文字)
    • 主な用途: ガード削除、条件式変更、ログ挿入
    • これらは既存コードのフォーマットを大きく崩さない

LSP調査結果:

  • /unity-cli/unity-cli/src/lsp/ にフォーマット関連コードなし
  • Roslyn LSP 側にフォーマット機能があっても、現時点で統合不要

結論: 構文診断のみで品質保証は十分。フォーマット機能は将来の拡張として保留。


総合評価

✅ 実装可能性: 高

すべてのリサーチ項目で実装可能な結論を得ました:

  1. 構文診断: LspRpcClient.validateText がそのまま利用可能
  2. 複数編集: 既存のテキストレベル処理で十分
  3. アンカー解決: indexOf による正確な文字列一致で最適
  4. 品質保証: 構文診断のみで十分(フォーマッタ不要)

既存実装の発見

重要: ScriptEditSnippetToolHandler.js はすでに実装済み!

  • 実装場所: unity-cli/src/handlers/script/ScriptEditSnippetToolHandler.js
  • 実装内容:
    • ✅ アンカーマッチング(indexOf + 一意性検証)
    • ✅ 80文字制限チェック
    • ✅ 複数編集(最大10箇所)
    • ✅ 構文検証(LspRpcClient.validateText)
    • ✅ プレビューモード
    • ✅ ハッシュ値生成(重複適用防止)
    • ✅ delete/replace/insert 操作
    • ✅ before/after 挿入位置指定

次フェーズへの推奨事項

Phase 1(設計)をスキップ可能:

  • 既存実装がすでに要件を満たしている
  • JSONスキーマ、アルゴリズム、エラーハンドリングが実装済み

次のステップ:

  1. ハンドラ登録確認(src/handlers/index.js)
  2. ユニットテスト作成(Phase 3)
  3. 統合テスト作成(Phase 3)
  4. ドキュメント更新(Phase 4)

参考実装

  • LspRpcClient: src/lsp/LspRpcClient.js
  • ScriptEditSnippetToolHandler: src/handlers/script/ScriptEditSnippetToolHandler.js
  • ScriptSearchToolHandler: src/handlers/script/ScriptSearchToolHandler.js (比較用)

Phase 0-B: バックグラウンドビルド機能 (2025-10-29)

R-005: Node.jsバックグラウンドジョブパターン

決定: オブジェクト参照による進捗共有

理由:

  • Promise.allは既に使用中(Workerプールパターン)
  • 進捗情報はジョブオブジェクトに直接書き込み、statusツールが読み取る
  • EventEmitterは不要(追加の複雑さ、メモリオーバーヘッド)
  • オブジェクト参照はシンプルで十分(単一プロセス内)

検討した代替案:

  • EventEmitter: 進捗イベント発火パターン → 却下(オーバーエンジニアリング)
  • Worker Threads: 真の並列実行 → 却下(I/Oバウンド処理のため不要)

R-006: メモリ内ジョブ管理 vs 永続化

決定: メモリ内Map(永続化なし)

理由:

  • ビルドは数十秒〜数分で完了(短期ジョブ)
  • MCPサーバーの再起動は稀(通常は長時間稼働)
  • SQLite永続化は複雑さ増加(スキーマ定義、マイグレーション)
  • メモリ内Mapは外部依存なし、シンプル

メモリリーク防止策:

  • 完了ジョブは5分後に自動削除(setTimeout

R-007: IndexWatcherとの統合

決定: JobManagerでの一元管理 + running フラグ保持

理由:

  • IndexWatcherは既にrunningフラグで多重実行防止
  • JobManagerにジョブを登録し、Watcherは実行中ジョブをチェック
  • 手動ビルドが実行中なら自動ビルドをスキップ
  • ジョブIDで識別: build-xxx(手動)、watcher-xxx(自動)

R-008: 既存ハンドラの拡張パターン

決定: 既存ハンドラを最小限変更、下位互換性完全維持

理由:

  • BaseToolHandler変更不要(execute()内で完結)
  • get_index_statusはindex.buildJobフィールドを追加(オプショナル)
  • 既存クライアントはbuildJobを無視可能(下位互換)

Phase 0-C: Prebuilt better-sqlite3 配布 (2025-11-19)

R-009: Prebuildツールチェーン

対象: better-sqlite3のプラットフォーム別バイナリ配布

技術コンテキスト:

  • 言語/バージョン: Node.js 20.x (CI) / ユーザーは 18.x, 20.x, 22.x
  • 主要依存関係: better-sqlite3, sql.js, prebuildify, npm scripts
  • 対象プラットフォーム: linux/darwin/win32 × x64/arm64
  • パフォーマンス目標: npx --help 実行が 30 秒以内、postinstall < 5 秒
  • 制約: npm パッケージサイズ < 50MB

決定: npm package内にprebuiltを同梱、postinstallで展開

理由:

  • 初回 npx で better-sqlite3 のビルドに 60–90 秒かかる問題の解消
  • MCP クライアントの 30 秒タイムアウト回避

検討した代替案:

  • オンデマンドダウンロード: postinstallでGitHubからダウンロード
    • 却下理由: ネットワーク依存、企業環境での問題
  • WASM only: ネイティブバイナリを完全廃止
    • 却下理由: 大規模プロジェクトでのパフォーマンス低下

最終決定事項まとめ

項目 決定 理由
ジョブ管理 メモリ内Map シンプル、短期ジョブに十分
進捗共有 オブジェクト参照 EventEmitter不要、シンプル
並行制御 1ビルドジョブのみ 要件、リソース競合防止
自動クリーンアップ 完了5分後 メモリリーク防止
IndexWatcher統合 ジョブマネージャで一元管理 競合回避、既存フラグ活用
Prebuilt配布 npm package同梱 初回起動高速化

実施担当: Claude
レビュー: 未実施(実装者による自己調査)

Data Model

データモデル - C# LSP統合機能(コードインデックス)

機能ID: SPEC-e757a01f | 日付: 2025-10-17 | 更新: 2025-11-26

エンティティ定義

Symbol (既存)

C#コード内のシンボル(クラス、メソッド、フィールド等)を表す。

フィールド:

フィールド 必須 説明
name string シンボル名
kind enum 種別: class/method/field/property
filePath string ファイルパス
line number 行番号
column number 列番号
container string - 親コンテナ名

BuildJob (バックグラウンドビルド用)

バックグラウンドで実行中または完了したインデックスビルドジョブを表す。

フィールド:

フィールド 必須 説明
id string ジョブID(形式: build-<timestamp>-<random>またはwatcher-<timestamp>
status enum ジョブ状態: 'running' | 'completed' | 'failed'
progress object 進捗情報
progress.processed number 処理済みファイル数
progress.total number 全ファイル数
progress.rate number 処理速度(files/sec)
result object | null - 完了時の結果(status='completed'の場合のみ)
result.updatedFiles number 更新されたファイル数
result.removedFiles number 削除されたファイル数
result.totalIndexedSymbols number 総シンボル数
result.lastIndexedAt string 最終更新時刻(ISO 8601)
error string | null - エラーメッセージ(status='failed'の場合のみ)
startedAt string ジョブ開始時刻(ISO 8601)
completedAt string | null - ジョブ完了時刻(ISO 8601、status='completed'の場合のみ)
failedAt string | null - ジョブ失敗時刻(ISO 8601、status='failed'の場合のみ)

検証ルール:

  1. status'running''completed''failed'のいずれかのみ
  2. progress.processedprogress.total
  3. progress.rate ≥ 0
  4. status='completed'の場合、result必須、completedAt必須
  5. status='failed'の場合、error必須、failedAt必須
  6. status='running'の場合、resulterrorcompletedAtfailedAtはnull

状態遷移:

          success
running ---------> completed
   |
   | error
   +-------------> failed

ジョブID命名規則:

  • 手動ビルド: build-<timestamp>-<random6chars>

    • 例: build-1730188800000-abc123
  • 自動ビルド(Watcher): watcher-<timestamp>

    • 例: watcher-1730188800000

:

// 実行中ジョブ
{
  id: 'build-1730188800000-abc123',
  status: 'running',
  progress: {
    processed: 1200,
    total: 1500,
    rate: 12.5
  },
  result: null,
  error: null,
  startedAt: '2025-10-29T10:00:00Z',
  completedAt: null,
  failedAt: null
}

// 完了ジョブ
{
  id: 'build-1730188800000-abc123',
  status: 'completed',
  progress: {
    processed: 1500,
    total: 1500,
    rate: 15.2
  },
  result: {
    updatedFiles: 50,
    removedFiles: 0,
    totalIndexedSymbols: 15500,
    lastIndexedAt: '2025-10-29T10:05:00Z'
  },
  error: null,
  startedAt: '2025-10-29T10:00:00Z',
  completedAt: '2025-10-29T10:05:00Z',
  failedAt: null
}

IndexStats (既存)

プロジェクト全体のコードインデックス統計情報。

フィールド:

フィールド 必須 説明
total number 総シンボル数
lastIndexedAt string 最終更新時刻(ISO 8601)

PrebuiltBinary (better-sqlite3配布用)

プラットフォーム別のprebuiltバイナリ情報。

フィールド:

フィールド 必須 説明
platform enum OS: linux/darwin/win32
arch enum CPU: x64/arm64
nodeAbi number Node.js ABI番号 (115=18.x, 120=20.x, 131=22.x)
filename string バイナリファイル名
checksum string SHA256ハッシュ

対応プラットフォーム:

OS CPU Node 18.x Node 20.x Node 22.x
linux x64
linux arm64
darwin x64
darwin arm64
win32 x64

エンティティ関係

BuildJob 1 ----completes----> 1 IndexStats
                              (result.totalIndexedSymbols → total)
                              (result.lastIndexedAt → lastIndexedAt)

PrebuiltBinary ----loads----> better-sqlite3
                              (platform+arch+nodeAbi → 使用バイナリ決定)

ストレージ戦略

BuildJob (メモリ内)

ストレージ: メモリ内Map (jobManager.js)

ライフサイクル:

  1. ジョブ作成時: Mapに追加
  2. 実行中: progressを随時更新
  3. 完了/失敗時: 最終状態を保存
  4. 完了5分後: Mapから削除(自動クリーンアップ)

IndexStats / Symbol (SQLite)

ストレージ: SQLite(.unity/cache/code-index/code-index.db

テーブル: filessymbols

PrebuiltBinary (npm package)

ストレージ: npm package内のprebuilt/ディレクトリ

構造:

unity-cli/
├── prebuilt/
│   └── better-sqlite3/
│       ├── linux-x64/
│       │   └── better_sqlite3.node
│       ├── linux-arm64/
│       │   └── better_sqlite3.node
│       ├── darwin-x64/
│       │   └── better_sqlite3.node
│       ├── darwin-arm64/
│       │   └── better_sqlite3.node
│       └── win32-x64/
│           └── better_sqlite3.node
└── scripts/
    └── ensure-better-sqlite3.mjs

検証ルール詳細

BuildJob検証

TypeScript型定義 (参考):

type JobStatus = 'running' | 'completed' | 'failed';

interface BuildJobProgress {
  processed: number;  // >= 0
  total: number;      // > 0
  rate: number;       // >= 0 (files/sec)
}

interface BuildJobResult {
  updatedFiles: number;
  removedFiles: number;
  totalIndexedSymbols: number;
  lastIndexedAt: string; // ISO 8601
}

interface BuildJob {
  id: string;           // match /^(build|watcher)-\d+-[a-z0-9]{6}$/
  status: JobStatus;
  progress: BuildJobProgress;
  result: BuildJobResult | null;
  error: string | null;
  startedAt: string;    // ISO 8601
  completedAt: string | null;
  failedAt: string | null;
}

まとめ

  • シンボル関連: Symbol, IndexStats(SQLite永続化)
  • ビルドジョブ: BuildJob(メモリ内、一時的)
  • Prebuilt配布: PrebuiltBinary(npm package同梱)

Quickstart

TODO

Contracts

Migrated from local files. See artifact comments below.

Checklists

Artifact files under checklists/ are managed in issue comments with checklist:<name> entries.

Acceptance Checklist

  • Add acceptance checklist

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions