Skip to content

[Feature] stdin パイプ入力対応 #89

@Kewton

Description

@Kewton

概要

stdin からファイルパスリストを受け取り、変更影響範囲分析や関連ファイル検索の入力として使用する。

背景・動機

  • シナリオ10(リグレッション影響分析): git diff --name-only | commandindex impact のパイプラインを実現
  • Unix哲学に沿った CLI 設計(他コマンドとの組み合わせ)

提案する解決策

# git diff の出力を直接パイプ
git diff main...develop --name-only | commandindex impact --format json

# ファイルリストをパイプ
cat file_list.txt | commandindex search --related-stdin --format json

# grep 結果をパイプ
grep -rl "getDbInstance" src/ | commandindex impact --format json

# 引数でもファイル指定可能(stdinと排他)
commandindex impact src/main.rs src/cli/search.rs --format json

実装方針

  • impact サブコマンドは stdin またはコマンドライン引数からファイルリストを受け取る
    • 引数が指定されている場合: 引数のファイルリストを使用
    • 引数がない場合: stdin から読み取る(TTY の場合はエラー)
    • 引数あり + stdin パイプの場合: 引数を優先(stdin は無視)
  • search --related-stdin で stdin からファイルリストを読み取る(1行1ファイルパス)
    • --related-stdin を採用する理由: 既存の --related <FILE> は値を1つ取る Option で、clap の conflicts_with_all による排他モデルに載っている。--related-stdin は値を取らない bool フラグとして追加し、--related と相互排他にすることで、既存の排他モデルを最小限の変更で拡張できる。main.rs の match 分岐では related_stdinrelated の前に if let で先にチェックする。
  • stdin が TTY の場合はエラー(パイプ専用)

impact サブコマンドの機能定義

impact サブコマンドは、入力されたファイルパスリストに対して 関連ファイルの集約分析 を行う。

注意: 既存の RelatedSearchEngine::find_related は双方向で関連ファイルを集計する(Markdown link も import dependency も順方向・逆方向の両方を含む)。したがって impact は厳密な「逆依存のみの分析」ではなく、「複数入力ファイルの関連ファイルを集約し、影響度スコアで降順ソートする」機能となる。将来的に逆依存のみのフィルタリングが必要になった場合は、--reverse-only オプション等で拡張する。

context との差分:

  • context: 複数ファイルの関連コンテキスト生成(ファイル内容のマージ出力、LLM向け)
  • impact: 複数ファイルの関連ファイル集約分析(影響を受けるファイルの一覧出力、CI/CD向け)

具体的な処理:

  1. stdin または引数からファイルパスリストを取得
  2. 各ファイルに対して RelatedSearchEngine::find_related を呼び出し
  3. 結果を集約・重複排除し、影響度スコアで降順ソート
  4. 指定フォーマット(human/json/path)で出力

出力スキーマ(JSON)

{
  "input_files": ["src/main.rs", "src/cli/search.rs"],
  "impacted_files": [
    {
      "file_path": "tests/cli_args.rs",
      "score": 1.5,
      "relation_types": ["import_dependency", "path_similarity"],
      "impacted_by": ["src/main.rs"]
    }
  ],
  "total_input_files": 2,
  "total_impacted_files": 5
}

: relation_types は既存の snake_case 命名規約に合わせる(import_dependency, path_similarity, markdown_link, tag_match, directory_proximity

search --related-stdin の出力仕様

複数ファイル入力時の集約ルール:

  • 結果は union(全入力ファイルの関連結果を統合)
  • 重複ファイルは 最大スコアを採用
  • relation_types は全入力からのユニーク集合
  • 入力ファイル自体は出力から除外
  • 出力形式は既存の --related と同じ(human/json/path の3形式、JSONL形式)
    • impacted_by は含まない(既存の related 出力互換を維持)

stdin 入力仕様

  • 1行1ファイルパス
  • 空行はスキップ
  • 前後の空白はトリム
  • 重複パスはユニーク化(正規化後のパスで比較。例: a/b.rs./a/b.rs は同一扱い)
  • 相対パスのみ(絶対パスはエラー)
  • .. を含むパスは禁止
  • バックスラッシュを含むパスは禁止(context.rs と同じ制約)
  • 存在しないファイルは warning 出力してスキップ
  • 入力ファイル数の上限: 500件

エッジケースの挙動

ケース 終了コード stderr stdout
stdin が TTY(パイプなし) 1 Error: stdin is a terminal. Pipe input required. なし
stdin が空(0行) 1 Error: no input received from stdin なし
有効パスが1件も残らない 1 Error: no valid file paths found in input なし
関連結果が0件 0 No impacted files found フォーマットに応じた空結果

実装上の影響範囲

  1. CLI層:
    • src/main.rs: Commands enum に Impact バリアント追加、match 式にハンドラ追加
    • src/cli/mod.rs: pub mod impact;pub mod stdin; 追加
    • src/cli/impact.rs: 新規作成(impact サブコマンドロジック)
    • src/cli/stdin.rs: 新規作成(stdin 読み取り・バリデーション共通ユーティリティ)
    • src/main.rs Search: --related-stdin オプション追加、conflicts_with 更新
  2. エラー型:
    • src/cli/search.rs SearchError: stdin 関連バリアント追加(StdinNotPiped, StdinReadError, StdinEmptyInput)
  3. 出力フォーマッタ:
    • src/output/mod.rs: ImpactResult 型追加
    • src/output/human.rs, json.rs, path.rs: format_impact_* 関数追加
    • : relation_types は既存の snake_case 規約に合わせる
  4. テスト:
    • tests/cli_args.rs: help テストに impact 追加、排他テスト追加
    • tests/e2e_impact.rs: 新規(assert_cmd .write_stdin() 使用)
    • tests/e2e_related_search.rs: --related-stdin テスト追加
  5. 依存追加不要: TTY検出は std::io::IsTerminal(Rust 1.70+)を使用

既存ロジックの再利用

  • src/search/related.rs: RelatedSearchEngine::find_related を複数ファイルに対して呼び出し
  • src/cli/context.rs: ファイルパスバリデーションロジック再利用

受け入れ基準

  • impact サブコマンドが stdin からファイルリストを読み取れる
  • impact サブコマンドがコマンドライン引数からもファイルリストを受け取れる
  • impact サブコマンドが --format human/json/path をサポートする
  • search --related-stdin が stdin からファイルリストを読み取れる
  • search --related-stdin の複数入力時の集約が union + 最大スコア採用
  • --related-stdin--related が相互排他
  • 1行1ファイルパスの形式
  • stdin が TTY の場合にエラーメッセージ
  • 空stdin / 有効パス0件 / 関連結果0件のエッジケースが正しく処理される
  • 空行スキップ・空白トリム・重複排除・不正パス warning スキップ
  • パスバリデーション: 相対パスのみ、.. 禁止、バックスラッシュ正規化
  • cargo test / clippy / fmt 全パス
  • E2Eテスト: assert_cmd の .write_stdin() を使ったパイプ入力テスト
  • CLI排他テスト: --related と --related-stdin の排他確認

実装規模

関連シナリオ

  • scenario.md シナリオ10

関連サブコマンド

  • context: 複数ファイルの関連コンテキスト生成(順方向、ファイル内容マージ出力)
  • search --related: 単一ファイルの関連ファイル検索

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions