-
Notifications
You must be signed in to change notification settings - Fork 0
[BUG] --with-snippet が空文字列を返す(related/impact) #123
Description
再現手順
commandindexdev index --path .
commandindexdev search --related src/components/worktree/TerminalDisplay.tsx --format json --with-snippet --limit 3期待される動作
各結果の snippet フィールドにファイル本文の先頭部分(デフォルト500文字/10行)が含まれる。
また、file_path フィールドにはインポートパスではなく実ファイルの相対パスが含まれる。
実際の動作
{"path":"@/components/worktree/TerminalSearchBar","relations":["import_dependency"],"score":1.3,"snippet":""}
{"path":"react","relations":["import_dependency"],"score":0.9,"snippet":""}
{"path":"@/hooks/useTerminalSearch","relations":["import_dependency"],"score":0.9,"snippet":""}全結果で snippet が空文字列 ""。--format llm でも同様にスニペットが含まれない。
さらに、path フィールドがインポートパス(@/components/..., react)のまま返され、実ファイルパス(src/components/...)に変換されていない。
根本原因
問題1: related検索結果の file_path がインポートパスのまま
score_import_deps() (related.rs:181) が imp.target_module.clone() をscores HashMapのキーとして使用。
target_module はTypeScriptパーサから直接取得したインポートパス(例: @/components/worktree/TerminalSearchBar, react)であり、
tantivyインデックスに保存された実ファイルパス(例: src/components/worktree/TerminalSearchBar.tsx)と一致しない。
問題2: 逆方向ルックアップの不一致
find_imports_by_target(target) (related.rs:194) は正規化された実ファイルパスでSQLiteを検索するが、
dependencies.target_module にはインポートパス形式で格納されているため、逆方向の依存関係(「このファイルをimportしているファイル」)も常に0件になる。
問題3: snippet空はパス不一致の副作用
fetch_snippet() が search_by_exact_path() でtantivy TermQuery完全一致検索を行うが、
インポートパス形式のキーでは実ファイルパスにマッチせず、空文字列が返される。
影響範囲
related サブコマンド
commandindex search --related <file> --with-snippetでsnippetが空file_pathがインポートパスのまま返されるenrich_related_with_snippets()(snippet_helper.rs:66) が影響
impact サブコマンド
commandindex impact <file> --with-snippetでもsnippetが空enrich_impact_with_snippets()(snippet_helper.rs:47) が影響aggregate_impact()(impact.rs) がfind_relatedの結果をそのまま使用するため、impacted_files にインポートパスが出力される
context サブコマンド
context.rsのenrich_entry内find_imports_by_source(path)も影響- ImportDependency 関連のエンリッチメント(symbols フィールド)が不正確になる可能性
影響を受けないコマンド
- keyword search (run) — score_import_deps を経由しない
- symbol search (run_symbol_search) — score_import_deps を経由しない
- semantic search (run_semantic_search) — score_import_deps を経由しない
- 出力フォーマッタ (json/llm/human/path) — file_pathを受け取って表示するだけ
LLMコンテキスト提供
- CommandIndexの差別化機能(コード内容付きの関連ファイル出力)が機能しない
- LLMエージェントへのコンテキスト提供において、パス一覧のみになり
file.readの代替にならない
受け入れ基準
--with-snippet指定時、相対パスインポート(./xxx,../xxx)の結果で snippet が非空であること- エイリアスインポート(
@/xxx)の結果でも snippet が非空であること(パスエイリアス解決により) - 外部パッケージ(
react等、tantivyインデックスに存在しないもの)は snippet 空文字列を許容する --format jsonと--format llmの両方で snippet が正しく出力されること- related検索結果の
file_pathが実ファイルの相対パスで返されること - impact検索結果のimpacted_filesも実ファイルパスで返されること
- 既存テスト(
cargo test --all)が全てパスすること cargo clippy --all-targets -- -D warningsが警告0件であること- import依存関係のE2Eテスト(--with-snippet付き)が追加されていること
解決方針
案A(推奨): score_import_deps 内で、インポートパスからtantivyインデックスのファイルパスをマッチングで解決する
- 最も局所的かつ安全
- パフォーマンス: resolve結果のキャッシュまたはメモリ上マッチングで軽微
- M1修正でsnippet問題(M3)も連鎖的に解決
案B(将来対応): インデックス時にインポートパスを実ファイルパスに解決してSQLiteに保存
- スキーマ変更が必要(dependencies テーブルに resolved_file_path カラム追加)
- 既存インデックスとの互換性問題
- 根本的解決だが初期リリースでは案Aを優先
環境
- commandindex 0.1.0
- 対象リポジトリ: 388ソースファイル(TypeScript/Next.js)
深刻度
高