Skip to content

commandindexdev before-change <file> コマンドの実装 #142

@Kewton

Description

@Kewton

概要

ファイルを変更する前に知るべき設計制約・過去のレビュー指摘を集約して返すコマンド。git log走査 → Issue特定 → ナレッジグラフ文書取得 → セマンティックランキングの多段フロー。

前提

  • ナレッジグラフ(knowledge_nodes / knowledge_edges)が実装済みであること
  • BGE-M3によるセマンティック検索が動作すること(多言語embeddingモデル対応 (BGE-M3) #134 完了済み)
  • ソースファイルとIssueの関連付け: 現在のナレッジグラフにはソースファイル→Issueのリンクが存在しないため、git logのコミットメッセージからIssue番号を抽出してファイルとIssueを紐づける仕組みを実装する必要がある

使用例

commandindexdev before-change src/config/z-index.ts

期待される出力:

src/config/z-index.ts を変更する前に:

設計制約:
  Issue #299: z-index体系を統一。新しい値はMAXIMIZED_EDITOR(55)の後に追加
    → dev-reports/design/issue-299-ipad-layout-fix-design-policy.md

  Issue #104: iPad CSSフォールバックでz-index:55を前提とした実装あり
    → dev-reports/design/issue-104-ipad-fullscreen-bugfix-design-policy.md

過去のレビュー指摘:
  Issue #299 stage4: clickjacking対策としてz-indexの層構造を維持すること
    → dev-reports/review/2026-02-18-issue299-security-review-stage4.md

関連する他のIssue:
  Issue #112: sidebar transform(同じファイルを変更)

アーキテクチャ

設計方針

  • git log走査ロジックは src/cli/before_change.rs に新規関数として実装(既存のknowledge.rsのパスパターンベース抽出とは責務を明確に分離)
  • SymbolStoreの既存メソッドは変更せず、Issue番号起点の文書取得用に新メソッドを追加
  • embedding未生成でも動作するよう、セマンティック検索はオプショナル
  • インデックスパス解決は resolve_index_pathsymbol_db_path() / embeddings_db_path() の既存パターンに統一

モジュール構成(変更対象)

src/cli/before_change.rs    # before-changeコマンド本体 + git log走査ロジック(新規)
src/cli/mod.rs               # before_changeモジュール宣言追加
src/main.rs                  # Commands enumにBeforeChange追加、分岐追加
src/indexer/symbol_store.rs  # find_knowledge_by_issue() メソッド追加
src/output/*.rs              # BeforeChangeResult用フォーマッタ追加
src/cli/help_llm.rs          # before-changeコマンド情報追加
tests/cli_args.rs            # help-llmコマンド数テスト更新、トップレベルhelp期待値更新
tests/e2e_before_change.rs   # E2Eテスト(新規)

実装

全体フロー

file → git log → issue番号群 → knowledge docs → セマンティックランキング

  1. ソースファイル → Issue 紐づけ(git log走査): git log --max-count=N -- <file> のコミットメッセージからIssue番号(#NNN(#NNN)fixes #NNNrefs #NNN パターン)を抽出。merge commitも対象とする。パフォーマンスのため --max-commits オプションで走査コミット数を制限(デフォルト: 200)。
  2. Issue起点の文書取得(ナレッジグラフ1ホップ): 特定されたIssue番号を起点に、SymbolStore::find_knowledge_by_issue() でhas_design/has_review/has_workplanエッジを辿り、関連ドキュメントを取得
  3. セマンティック検索(ランキング・オプショナル): ステップ2で見つかったドキュメントの中から、対象ファイルのembedding(EmbeddingStore::find_by_path() で取得)とのコサイン類似度でランキング。複数section embeddingの集約方針: 対象ファイル・文書それぞれ複数のsection embeddingを持つため、各ペアの最大コサイン類似度(max pooling)をスコアとする。embedding未生成の場合はスキップし、ナレッジグラフの関係性のみで結果を返す。
  4. 別インデックス参照(オプション): --index-path 指定時、resolve_index_pathsymbol_db_path() / embeddings_db_path() で別worktreeのインデックスを参照
fn before_change(file_path: &str, index_path: Option<&Path>, max_commits: usize) -> Result<Vec<Finding>> {
    // ステップ1: git logからIssue番号を抽出
    let issue_numbers = extract_issues_from_git_log(file_path, max_commits)?;

    // ステップ2: ナレッジグラフでIssue → ドキュメント取得(1ホップ)
    let resolved_path = resolve_index_path(index_path)?;
    let symbol_store = SymbolStore::open(&symbol_db_path(&resolved_path))?;
    let docs = symbol_store.find_knowledge_by_issue(&issue_numbers)?;

    // ステップ3: セマンティック検索でランキング(オプショナル)
    let emb_path = embeddings_db_path(&resolved_path);
    let findings = match EmbeddingStore::open(&emb_path) {
        Ok(embedding_store) => {
            let file_embs = embedding_store.find_by_path(file_path)?;
            rank_by_max_similarity(&file_embs, &docs, &embedding_store)?
        },
        Err(_) => {
            eprintln!("Warning: embedding store not found, skipping semantic ranking");
            docs.into_iter().map(|d| Finding { doc: d, similarity: None }).collect()
        },
    };

    Ok(findings)
}

出力フォーマット

  • --format human, --format json, --format llm, --format path
  • --format llm はエージェントのコンテキストとして直接利用可能
  • before-change固有の出力構造体 BeforeChangeResult を定義し、各フォーマッタに format_before_change_* 関数を追加

オプション

  • --index-path <PATH>: 別のworktree(develop等)のインデックスを参照。サブコマンドレベルで指定し、resolve_index_path で解決。
  • --limit <N>: 結果件数制限(デフォルト: 10)。全体の上位N件をスコア順で返す。
  • --max-commits <N>: git log走査の最大コミット数(デフォルト: 200)

エラーハンドリング

  • インデックス未作成: エラーメッセージで commandindexdev index 実行を案内
  • embedding未生成(DB不存在): Warning を表示しつつナレッジグラフのみで動作
  • 対象ファイルのembedding欠如: Info メッセージを表示しつつランキングなしで結果を返す
  • 関連文書のembedding欠如: その文書を未ランクとして末尾に表示
  • 対象ファイルがインデックスにない: 警告を表示しつつgit log走査のみで結果を返す
  • 関連Issueが見つからない: 「関連する設計制約は見つかりませんでした」メッセージ
  • 不正なファイルパス(空文字、先頭-等): エラーメッセージで拒否
  • gitリポジトリ外での実行: 明確なエラーメッセージを返す

受け入れ基準

  • before-change <file> で関連するナレッジドキュメント(設計書・レビュー結果)が返ること
  • git logからIssue番号を正しく抽出し、ソースファイルとIssueを紐づけられること(#NNN(#NNN)fixes #NNNrefs #NNN パターン対応)
  • ナレッジグラフのIssue起点文書取得(Issue → 設計ポリシー・レビュー)が動作すること
  • セマンティック検索による関連度ランキングが動作すること(embedding存在時、max pooling方式)
  • embedding未生成時でもナレッジグラフのみで動作すること
  • --format human で人間が読みやすい形式で出力されること
  • --format json でJSON形式で出力されること
  • --format llm でLLM向け形式で出力されること
  • --format path でパスのみ出力されること
  • 関連Issueが存在しないファイルに対して適切なメッセージが返ること
  • --index-path で別worktreeのインデックスを参照できること(resolve_index_path経由)
  • --limit で全体上位N件に結果が制限されること
  • --max-commits でgit log走査コミット数が制限されること
  • 不正なファイルパスを拒否すること
  • gitリポジトリ外では明確なエラーを返すこと
  • help-llm の出力にbefore-changeコマンド情報が含まれること
  • tests/cli_args.rs のトップレベルhelp期待値が更新されていること
  • tests/cli_args.rs のhelp-llmコマンド一覧・件数テストが更新されていること
  • before-change --help がサブコマンドのヘルプを正しく表示すること
  • E2Eテスト: embeddingあり/なし、indexあり/なし、不正パス、非gitリポジトリのケース
  • cargo test で全テストがパスすること
  • cargo clippy で警告0件

影響範囲

  • 既存の検索・indexingロジック: 直接影響なし(新規追加のみ)
  • CLI公開面(--help / help-llm): トップレベルhelpにbefore-changeが追加される。help-llmのコマンド一覧・件数が変更される。これに依存する既存テストの更新が必要。
  • 出力モジュール(output/*.rs): BeforeChangeResultとformat関数の追加が必要。既存のformat_*関数には影響しない。
  • データモデル: knowledge_nodes/edgesテーブルの変更不要(読み取りのみ)
  • 依存crate: 追加不要(既存のregex, rusqlite等で実装可能)
  • パフォーマンス: git log走査(プロセス起動+履歴走査)とsection embedding総当たり比較(対象ファイルsection数×文書section数×文書件数)が新たなホットパス。履歴が深いファイルやsection数の多い文書群ではレイテンシ増加の可能性あり。--max-commits上限と--limit上限で制御。
  • テスト: tests/cli_args.rsのトップレベルhelp・help-llm期待値更新、before-change --helpテスト追加、git repoセットアップを伴うE2Eテスト新規追加。

関連

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