概要
issue --format llm と before-change --format llm はファイルパスのリストのみを返す。「過去の判断を取り出す」というプロダクトコアを実現するには、各文書の**判断理由の要約(スニペット)**をインライン表示する必要がある。
現状
# issue --format llm: パスのみ
commandindexdev issue 299 --format llm
# → - dev-reports/design/issue-299-ipad-layout-fix-design-policy.md
# (中身は不明。読みに行く必要がある)
# before-change --format json: snippet が NONE
commandindexdev before-change src/config/z-index.ts --format json
# → findings[].snippet: null(全件)
期待される結果
# Issue #299 関連ドキュメント
## 設計
- dev-reports/design/issue-299-ipad-layout-fix-design-policy.md
> z-index指定方式をinline style方式で統一。Z_INDEX定数を直接参照し、Tailwindのz-[60]を廃止。DRY違反を解消。
## レビュー
- dev-reports/review/2026-02-18-issue299-consistency-review-stage2.md
> Must Fix 1件: Toast.tsxがZ_INDEX.TOASTではなくz-50ハードコード。設計書の前提と不整合。
# before-change --format llm: 判断理由がインライン
commandindexdev before-change src/config/z-index.ts --format llm --with-snippet
# → - issue-299-ipad-layout-fix-design-policy.md (#299, has_design)
# > inline style方式で統一。Z_INDEX定数を直接参照。
対象バリュー
- 判断再利用: パスのリストでは判断を再利用できない。判断理由が直接読めて初めて「次の意思決定に接続」できる
- 文脈先回り: AIエージェントがbefore-changeの結果だけで設計制約を把握できる。各ファイルをfile.readする多段ステップが不要になる
実装方針
Phase 1: 基本スニペット付与(本Issue スコープ)
既存の snippet_helper::fetch_snippet() を活用し、tantivy インデックスの body フィールドから先頭N文字を取得してスニペットとする。
snippet 未取得時の契約
snippet: Option<String> に統一:
- 取得成功時:
Some("非空のスニペット文字列")
- tantivy 未存在 / 未インデックス / 空本文 / 検索失敗:
None
Some("")(空文字列)は使用しない
- JSON出力:
None → フィールド省略(--with-snippet 未指定時)または null(--with-snippet 指定時)
- 既存 impact/related の JSON ポリシーに合わせ、
--with-snippet 指定時のみ snippet フィールドを出力
既存の fetch_snippet() は空文字列を返すが、呼び出し側で空文字列を None に変換する。
CLIオプション
issue と before-change に --with-snippet フラグを追加(デフォルトオフ)
- 他コマンド(impact, search)との一貫性を維持
- main.rs の Cli 定義に追加
--snippet-lines / --snippet-chars も既存コマンドと同じパターンで追加
- デフォルト:
lines=3, chars=200(既存の lines=2, chars=120 とは異なる設定)
- 設定ファイル連携も既存パターンに従う
型変更
BeforeChangeFinding (output/mod.rs) に snippet: Option<String> フィールドを追加
- 影響箇所: before_change.rs 内コンストラクタ5箇所、テスト10箇所以上(全て
snippet: None で初期化)
IssueDocumentEntry (cli/issue.rs) に snippet: Option<String> フィールドを追加
- 影響箇所: symbol_store.rs find_documents_by_issue() 1箇所、issue.rs テスト6箇所(全て
snippet: None で初期化)
tantivy IndexReader の追加
- issue コマンド:
commandindex_dir は既に受け取り済み。IndexReaderWrapper を開き、enrich_issue_documents_with_snippets() を呼び出す。
- before-change コマンド:
run_before_change() に IndexReaderWrapper を追加。main.rs のコール箇所も更新。
- tantivy 未存在時のフォールバック: IndexReaderWrapper のオープンに失敗した場合は snippet: None でフォールバック(エラーにしない)。
snippet_helper パターンの一貫性
既存の enrich_impact_with_snippets() / enrich_related_with_snippets() パターンに従い、以下を追加:
enrich_issue_documents_with_snippets()
enrich_before_change_with_snippets()
出力フォーマット
before-change の表示順序(human/llm):
- 1行目:
doc_path [similarity] (#issue, relation)
- 2行目:
doc_title(存在する場合)
- 3行目:
> snippet...(--with-snippet 指定時かつ snippet が Some の場合)
issue の JSON スキーマ:
--with-snippet 未指定時: 現行の string[] を維持(後方互換性)
--with-snippet 指定時: オブジェクト配列に拡張
// --with-snippet 未指定時(現行互換)
{
"issue_number": "299",
"documents": {
"設計": ["path/to/design.md"],
"レビュー": ["path/to/review.md"]
}
}
// --with-snippet 指定時
{
"issue_number": "299",
"documents": {
"設計": [{"file_path": "path/to/design.md", "snippet": "z-index指定方式を..."}],
"レビュー": [{"file_path": "path/to/review.md", "snippet": null}]
}
}
before-change の JSON:
--with-snippet 指定時のみ snippet フィールドを追加(既存ポリシー準拠: None時はフィールド省略ではなく null で出力)
--format path: スニペット非出力(変更なし)
help-llm 更新
src/cli/help_llm.rs の issue/before-change コマンド説明に --with-snippet / --snippet-lines / --snippet-chars 追加
- snippet 付き出力例を追加(例:
issue 140 --with-snippet --format llm, before-change src/auth.rs --with-snippet --format llm)
Phase 2: セクション優先抽出(将来の拡張、本Issue スコープ外)
has_design: 設計ポリシーの「採用方針」または「結論」セクションを優先抽出
has_review: summary-reportの「Executive Summary」セクションを優先抽出
has_workplan: Phase一覧の要約を抽出
現在の fetch_snippet() は先頭N文字を返すのみ。セクション優先抽出にはheadingフィールドでのフィルタリングが必要であり、別Issueとして対応する。
影響範囲
変更が必要なファイル
| ファイル |
変更内容 |
src/output/mod.rs |
BeforeChangeFinding に snippet フィールド追加 |
src/indexer/knowledge.rs or src/cli/issue.rs |
IssueDocumentEntry に snippet フィールド追加 |
src/indexer/symbol_store.rs |
find_documents_by_issue() で snippet: None 設定 |
src/cli/before_change.rs |
コンストラクタ5箇所 + テスト10箇所に snippet: None 追加、enrich 呼び出し |
src/cli/issue.rs |
enrich 呼び出し追加、フォーマッタ4関数更新、テスト6箇所更新 |
src/cli/snippet_helper.rs |
enrich 関数2つ追加 |
src/output/human.rs |
format_before_change_human にスニペット表示追加 |
src/output/llm.rs |
format_before_change_llm にスニペット表示追加 |
src/output/json.rs |
format_before_change_json にスニペットフィールド追加 |
src/main.rs |
--with-snippet / --snippet-lines / --snippet-chars 追加、引数更新 |
src/cli/help_llm.rs |
コマンド説明・出力例更新 |
tests/cli_args.rs |
新オプションのhelp表示・境界値・受理/拒否テスト追加 |
tests/e2e_issue.rs |
JSON スキーマ対応(--with-snippet 有無の両パターン) |
tests/e2e_before_change.rs |
snippet フィールド検証追加 |
tests/output_format.rs |
フォーマッタ単体テスト(snippet=None/Some × human/llm/json/path) |
テスト方針
- CLIテスト (tests/cli_args.rs): 新オプションのhelp表示、境界値(--snippet-lines 0, --snippet-chars 0)、受理/拒否テスト
- フォーマッタ単体テスト (tests/output_format.rs): issue/before-change の human/llm/json/path × snippet=None/Some
- E2Eテスト: 各コマンド2系統
- tantivy あり: snippet 取得成功ケース(tantivy index を含むフィクスチャ構築)
- tantivy なし: snippet なし(None)フォールバックケース
- issue E2E: --with-snippet 有無の両パターンでJSON スキーマ検証
- before-change E2E: --with-snippet 指定時の snippet フィールド存在検証
依存関係の影響
- 新規外部依存なし
- 実行時依存の拡大: issue / before-change に tantivy IndexReaderWrapper 依存が追加される
- 障害モード: symbols.db はあるが tantivy インデックスが壊れている/ない → 非fatal フォールバック(snippet: None)
- 既存の impact/search コマンドは同じ依存を既に持っており、パターンは確立済み
パフォーマンス影響
- before-change: tantivy IndexReader オープン1回 + fetch_snippet() 最大20回(issue limit × 2 docs)— 軽微
- issue: tantivy IndexReader オープン1回 + fetch_snippet() 最大100回(find_documents_by_issue の LIMIT 100)— 件数比例だが TermQuery(完全一致)で O(1) に近く影響は軽微
- 必要なら同一パス重複の除去や limit の見直しも検討可能
影響なし
- issue/before-change 以外のコマンド(search, impact, why, suggest 等)
受け入れ基準
テスト環境
- commandindex 0.1.0
- CommandMateリポジトリ(2910ファイル、124690セクション)
概要
issue --format llmとbefore-change --format llmはファイルパスのリストのみを返す。「過去の判断を取り出す」というプロダクトコアを実現するには、各文書の**判断理由の要約(スニペット)**をインライン表示する必要がある。現状
期待される結果
対象バリュー
実装方針
Phase 1: 基本スニペット付与(本Issue スコープ)
既存の
snippet_helper::fetch_snippet()を活用し、tantivy インデックスの body フィールドから先頭N文字を取得してスニペットとする。snippet 未取得時の契約
snippet: Option<String>に統一:Some("非空のスニペット文字列")NoneSome("")(空文字列)は使用しないNone→ フィールド省略(--with-snippet未指定時)またはnull(--with-snippet指定時)--with-snippet指定時のみ snippet フィールドを出力CLIオプション
issueとbefore-changeに--with-snippetフラグを追加(デフォルトオフ)--snippet-lines/--snippet-charsも既存コマンドと同じパターンで追加lines=3, chars=200(既存のlines=2, chars=120とは異なる設定)型変更
BeforeChangeFinding(output/mod.rs) にsnippet: Option<String>フィールドを追加snippet: Noneで初期化)IssueDocumentEntry(cli/issue.rs) にsnippet: Option<String>フィールドを追加snippet: Noneで初期化)tantivy IndexReader の追加
commandindex_dirは既に受け取り済み。IndexReaderWrapper を開き、enrich_issue_documents_with_snippets()を呼び出す。run_before_change()に IndexReaderWrapper を追加。main.rs のコール箇所も更新。snippet_helper パターンの一貫性
既存の
enrich_impact_with_snippets()/enrich_related_with_snippets()パターンに従い、以下を追加:enrich_issue_documents_with_snippets()enrich_before_change_with_snippets()出力フォーマット
before-change の表示順序(human/llm):
doc_path [similarity] (#issue, relation)doc_title(存在する場合)> snippet...(--with-snippet指定時かつ snippet が Some の場合)issue の JSON スキーマ:
--with-snippet未指定時: 現行のstring[]を維持(後方互換性)--with-snippet指定時: オブジェクト配列に拡張before-change の JSON:
--with-snippet指定時のみsnippetフィールドを追加(既存ポリシー準拠: None時はフィールド省略ではなくnullで出力)--format path: スニペット非出力(変更なし)
help-llm 更新
src/cli/help_llm.rsの issue/before-change コマンド説明に--with-snippet/--snippet-lines/--snippet-chars追加issue 140 --with-snippet --format llm,before-change src/auth.rs --with-snippet --format llm)Phase 2: セクション優先抽出(将来の拡張、本Issue スコープ外)
has_design: 設計ポリシーの「採用方針」または「結論」セクションを優先抽出has_review: summary-reportの「Executive Summary」セクションを優先抽出has_workplan: Phase一覧の要約を抽出影響範囲
変更が必要なファイル
src/output/mod.rssrc/indexer/knowledge.rsorsrc/cli/issue.rssrc/indexer/symbol_store.rssrc/cli/before_change.rssrc/cli/issue.rssrc/cli/snippet_helper.rssrc/output/human.rssrc/output/llm.rssrc/output/json.rssrc/main.rssrc/cli/help_llm.rstests/cli_args.rstests/e2e_issue.rstests/e2e_before_change.rstests/output_format.rsテスト方針
依存関係の影響
パフォーマンス影響
影響なし
受け入れ基準
issue --with-snippet --format humanで各ドキュメントにスニペットが表示されるissue --with-snippet --format llmで各ドキュメントにスニペットが表示される(> snippet...形式)issue --with-snippet --format jsonで各ドキュメントにsnippetフィールドが含まれる(オブジェクト配列)issue --format json(--with-snippet なし)は現行の string[] を維持(後方互換性)before-change --with-snippet --format humanで各 finding にスニペットが表示されるbefore-change --with-snippet --format llmで各 finding にスニペットがインライン表示されるbefore-change --with-snippet --format jsonで各 finding にsnippetフィールドが含まれる--format pathではスニペットを出力しない--with-snippet未指定時はスニペットを出力しない(後方互換性維持)None(JSON:null)を返す(Some("")は使用しない)--snippet-lines/--snippet-charsで文字数制御可能(デフォルト: lines=3, chars=200)cargo clippy --all-targets -- -D warnings警告0件cargo test --all全テストパステスト環境