概要
commandindex status コマンドを拡張し、チーム運用に必要な統計情報を表示する。
背景・動機
チームでCommandIndexを運用する際、「どのファイルがインデックスされているか」「どの言語がカバーされているか」「インデックスの鮮度は十分か」といった情報が必要になる。
提案する解決策
拡張された status 出力
commandindex status --detail
Index Status:
Path: .commandindex/
Version: 5
Last indexed: 2026-03-22 14:30:00
Last commit at index: abc1234
Coverage:
Total files: 1500 (walkdir scan, respecting .cmindexignore)
Indexed files: 1420
Skipped files: 80 (filtered by .cmindexignore / unsupported extensions)
By type:
Markdown: 800 files (56%)
TypeScript: 450 files (32%)
Python: 170 files (12%)
Embedding coverage:
Files with embeddings: 1200 / 1420 (85%)
Model: nomic-embed-text (or "(not configured)" if config.toml absent)
Staleness:
Commits since last index: 12
Files changed since last index: 23
Recommendation: Run `commandindex update`
(Git info unavailable: git not found) ← git未インストール時
Storage:
tantivy/: 45 MB
symbols.db: 12 MB
embeddings.db: 8 MB
Other: 0 MB
Total: 65 MB
CLIオプション
# 基本情報(既存互換)
commandindex status
# 詳細情報
commandindex status --detail
# JSON出力(CI/スクリプト向け)— 既存の --format json を拡張情報に対応
commandindex status --format json
# カバレッジのみ(--detail との併用不可、排他オプション)
commandindex status --coverage
オプション排他ルール:
--detail と --coverage は排他。同時指定時はエラーメッセージを表示して終了。
--format json は --detail / --coverage いずれとも併用可能。
- オプションなしの
commandindex status は既存の出力と完全互換を維持。
実装方針
IndexState の拡張(MF-1: 後方互換性の確保)
IndexState 構造体に last_commit_hash: Option<String> フィールドを追加する。
#[serde(default)] を付与し、既存の state.json との後方互換を保つ。フィールドが存在しない古い state.json をデシリアライズしても None となりパニックしない。
schema_version のバンプは不要(Option + serde(default) で既存データを壊さないため)。この判断根拠をコード内コメントにも明記する。
- マイグレーション戦略: 次回
commandindex index / commandindex update 実行時に自動的にフィールドが書き込まれる。明示的なマイグレーションコマンドは不要。
status コマンドの run() シグネチャ変更(MF-2: StatusOptions 構造体の導入)
- 現在の
run() 関数のシグネチャを直接変更するのではなく、StatusOptions 構造体を導入してオプションを集約する。
pub struct StatusOptions {
pub detail: bool,
pub coverage: bool,
pub format: OutputFormat,
}
StatusOptions には Default トレイトを実装し、StatusOptions::default() で既存互換の動作(detail: false, coverage: false, format: OutputFormat::Human)となるようにする。
- 既存テストでは
StatusOptions::default() を使用することで、新オプション追加による既存テストの破壊を防ぐ。
- 将来のオプション追加時も
StatusOptions にフィールドを追加 + Default 更新のみで済む拡張性を確保。
--detail / --coverage の排他制御(MF-3: clap conflicts_with + テスト)
- clap の
conflicts_with 属性で --detail と --coverage を排他にする。
#[arg(long, conflicts_with = "coverage")]
detail: bool,
#[arg(long, conflicts_with = "detail")]
coverage: bool,
--coverage 単体指定時は Coverage セクションのみ出力。
--detail 単体指定時は全セクション(Index Status / Coverage / Staleness / Storage)を出力。
tests/cli_args.rs に排他テストを追加:
--detail --coverage 同時指定でエラー終了することを検証。
--detail 単体、--coverage 単体がそれぞれ正常動作することを検証。
StatusInfo の新フィールド後方互換(SF-1: serde skip_serializing_if)
StatusInfo 構造体の新規追加フィールド(coverage, staleness, storage, embedding_coverage)には全て #[serde(skip_serializing_if = "Option::is_none")] を付与する。
- これにより、
--format json の出力が既存スクリプトを壊さない(新フィールドが None の場合はキーごと省略される)。
- 既存の
--format json パーサーが未知のキーを無視する前提だが、念のためデフォルト出力では不要フィールドを含めない。
EmbeddingStore の拡張(SF-2: get_embedding_file_count ヘルパー)
EmbeddingStore に count_distinct_files() -> Result<usize> メソッドを追加する。
SELECT COUNT(DISTINCT file_path) FROM embeddings で取得。
get_embedding_file_count() ヘルパー関数を追加し、status コマンドから直接呼び出す。
- DB ファイルが存在しない場合: エラーではなく
0 を返却。
- DB 接続エラー / クエリエラー:
Result::Err を返し、呼び出し側で (unavailable) と表示。
status --detail の Embedding coverage 表示に使用する。
ファイル走査(SF-3: パフォーマンスとフィルタリング)
- Total files:
status 実行時に walkdir でプロジェクトルートを再走査し、対象ファイル数をカウントする。
- フィルタリング:
.cmindexignore のルールを適用してスキップ。
- デフォルト除外ディレクトリ:
.git/, node_modules/, target/ は .cmindexignore の有無にかかわらず常に除外。
.gitignore にも対応(既存の ignore 処理ロジックを再利用)。
- Indexed files: tantivy インデックスに登録されているドキュメント数(
manifest.json から取得、または tantivy reader で num_docs() を使用)。
- Skipped files:
Total files - Indexed files で算出。理由として .cmindexignore フィルタおよび未対応拡張子を表示。
Git 情報の取得(SF-4: best-effort 方式)
std::process::Command で git コマンドを呼び出す方式を採用する(git2 crate は不使用)。
git rev-parse HEAD、git log --oneline <last_commit>..HEAD などで差分情報を取得。
- Graceful degradation(best-effort):
git コマンドが見つからない場合: Staleness セクション全体を (Git info unavailable: git not found) と表示。None を返却。
git は存在するがリポジトリでない場合: (Git info unavailable: not a git repository) と表示。None を返却。
git コマンドがタイムアウト / 予期せぬエラー: None / デフォルト値を使用し、エラーにはしない。
- いずれの場合も
status コマンド自体は正常終了する(exit code 0)。
Storage 内訳の構造化(SF-5: StorageBreakdown 構造体)
StorageBreakdown 構造体を導入:
pub struct StorageBreakdown {
pub tantivy_bytes: u64,
pub symbols_db_bytes: u64,
pub embeddings_db_bytes: u64,
pub other_bytes: u64,
pub total_bytes: u64,
}
- 各項目は
fs::metadata またはディレクトリ走査で実サイズを取得。
- indexer モジュールの既存パスヘルパー(
index_dir_path(), state_file_path() 等)を活用してパスのハードコーディングを避ける。
- 表示時は
humanize_bytes() ヘルパーで人間が読みやすい単位(KB/MB/GB)に変換。
config.toml 不在時の表示
- Embedding coverage の Model 表示について、
config.toml が存在しない / embedding モデル設定がない場合は (not configured) と表示する。
影響を受けるファイル・テスト
変更対象ファイル
| ファイル |
変更内容 |
src/cli/status.rs |
StatusOptions 構造体導入、run() シグネチャ変更、全表示ロジック追加 |
src/indexer/state.rs |
IndexState に last_commit_hash 追加(#[serde(default)]) |
src/indexer/mod.rs |
パスヘルパー公開(StorageBreakdown で利用) |
src/embedding/store.rs |
count_distinct_files() / get_embedding_file_count() 追加 |
src/output/mod.rs |
StatusInfo に新フィールド追加(#[serde(skip_serializing_if)]) |
src/main.rs |
--detail / --coverage clap 定義、conflicts_with 設定 |
影響を受けるテストファイル
| テストファイル |
変更内容 |
tests/cli_args.rs |
--detail / --coverage 排他テスト追加、StatusOptions::default() 互換テスト |
tests/status_test.rs(新規) |
status --detail の統合テスト、JSON 出力の構造検証 |
src/indexer/state.rs(ユニットテスト) |
last_commit_hash の serde 後方互換テスト(古い JSON → 新しい struct) |
src/embedding/store.rs(ユニットテスト) |
count_distinct_files() の正常系 / DB 不在時テスト |
新規追加される型
| 型名 |
定義場所 |
用途 |
StatusOptions |
src/cli/status.rs |
status コマンドのオプション集約 |
StorageBreakdown |
src/cli/status.rs または src/indexer/ |
ストレージ内訳の構造化 |
受け入れ基準
機能要件
データモデル
設計要件
テスト要件
品質要件
将来の拡張候補
--watch モード(リアルタイム status 更新)
status 結果のキャッシュ(大規模リポジトリでの walkdir 高速化)
- Health check スコア表示(カバレッジ + staleness を総合評価)
--output-file オプション(結果をファイルに保存)
依存 Issue
概要
commandindex statusコマンドを拡張し、チーム運用に必要な統計情報を表示する。背景・動機
チームでCommandIndexを運用する際、「どのファイルがインデックスされているか」「どの言語がカバーされているか」「インデックスの鮮度は十分か」といった情報が必要になる。
提案する解決策
拡張された status 出力
CLIオプション
オプション排他ルール:
--detailと--coverageは排他。同時指定時はエラーメッセージを表示して終了。--format jsonは--detail/--coverageいずれとも併用可能。commandindex statusは既存の出力と完全互換を維持。実装方針
IndexState の拡張(MF-1: 後方互換性の確保)
IndexState構造体にlast_commit_hash: Option<String>フィールドを追加する。#[serde(default)]を付与し、既存のstate.jsonとの後方互換を保つ。フィールドが存在しない古いstate.jsonをデシリアライズしてもNoneとなりパニックしない。schema_versionのバンプは不要(Option+serde(default)で既存データを壊さないため)。この判断根拠をコード内コメントにも明記する。commandindex index/commandindex update実行時に自動的にフィールドが書き込まれる。明示的なマイグレーションコマンドは不要。status コマンドの run() シグネチャ変更(MF-2: StatusOptions 構造体の導入)
run()関数のシグネチャを直接変更するのではなく、StatusOptions構造体を導入してオプションを集約する。StatusOptionsにはDefaultトレイトを実装し、StatusOptions::default()で既存互換の動作(detail: false, coverage: false, format: OutputFormat::Human)となるようにする。StatusOptions::default()を使用することで、新オプション追加による既存テストの破壊を防ぐ。StatusOptionsにフィールドを追加 +Default更新のみで済む拡張性を確保。--detail / --coverage の排他制御(MF-3: clap conflicts_with + テスト)
conflicts_with属性で--detailと--coverageを排他にする。--coverage単体指定時は Coverage セクションのみ出力。--detail単体指定時は全セクション(Index Status / Coverage / Staleness / Storage)を出力。tests/cli_args.rsに排他テストを追加:--detail --coverage同時指定でエラー終了することを検証。--detail単体、--coverage単体がそれぞれ正常動作することを検証。StatusInfo の新フィールド後方互換(SF-1: serde skip_serializing_if)
StatusInfo構造体の新規追加フィールド(coverage,staleness,storage,embedding_coverage)には全て#[serde(skip_serializing_if = "Option::is_none")]を付与する。--format jsonの出力が既存スクリプトを壊さない(新フィールドがNoneの場合はキーごと省略される)。--format jsonパーサーが未知のキーを無視する前提だが、念のためデフォルト出力では不要フィールドを含めない。EmbeddingStore の拡張(SF-2: get_embedding_file_count ヘルパー)
EmbeddingStoreにcount_distinct_files() -> Result<usize>メソッドを追加する。SELECT COUNT(DISTINCT file_path) FROM embeddingsで取得。get_embedding_file_count()ヘルパー関数を追加し、status コマンドから直接呼び出す。0を返却。Result::Errを返し、呼び出し側で(unavailable)と表示。status --detailの Embedding coverage 表示に使用する。ファイル走査(SF-3: パフォーマンスとフィルタリング)
status実行時にwalkdirでプロジェクトルートを再走査し、対象ファイル数をカウントする。.cmindexignoreのルールを適用してスキップ。.git/,node_modules/,target/は.cmindexignoreの有無にかかわらず常に除外。.gitignoreにも対応(既存の ignore 処理ロジックを再利用)。manifest.jsonから取得、または tantivy reader でnum_docs()を使用)。Total files - Indexed filesで算出。理由として.cmindexignoreフィルタおよび未対応拡張子を表示。Git 情報の取得(SF-4: best-effort 方式)
std::process::Commandでgitコマンドを呼び出す方式を採用する(git2crate は不使用)。git rev-parse HEAD、git log --oneline <last_commit>..HEADなどで差分情報を取得。gitコマンドが見つからない場合: Staleness セクション全体を(Git info unavailable: git not found)と表示。Noneを返却。gitは存在するがリポジトリでない場合:(Git info unavailable: not a git repository)と表示。Noneを返却。gitコマンドがタイムアウト / 予期せぬエラー:None/ デフォルト値を使用し、エラーにはしない。statusコマンド自体は正常終了する(exit code 0)。Storage 内訳の構造化(SF-5: StorageBreakdown 構造体)
StorageBreakdown構造体を導入:fs::metadataまたはディレクトリ走査で実サイズを取得。index_dir_path(),state_file_path()等)を活用してパスのハードコーディングを避ける。humanize_bytes()ヘルパーで人間が読みやすい単位(KB/MB/GB)に変換。config.toml 不在時の表示
config.tomlが存在しない / embedding モデル設定がない場合は(not configured)と表示する。影響を受けるファイル・テスト
変更対象ファイル
src/cli/status.rsStatusOptions構造体導入、run()シグネチャ変更、全表示ロジック追加src/indexer/state.rsIndexStateにlast_commit_hash追加(#[serde(default)])src/indexer/mod.rsStorageBreakdownで利用)src/embedding/store.rscount_distinct_files()/get_embedding_file_count()追加src/output/mod.rsStatusInfoに新フィールド追加(#[serde(skip_serializing_if)])src/main.rs--detail/--coverageclap 定義、conflicts_with設定影響を受けるテストファイル
tests/cli_args.rs--detail/--coverage排他テスト追加、StatusOptions::default()互換テストtests/status_test.rs(新規)status --detailの統合テスト、JSON 出力の構造検証src/indexer/state.rs(ユニットテスト)last_commit_hashの serde 後方互換テスト(古い JSON → 新しい struct)src/embedding/store.rs(ユニットテスト)count_distinct_files()の正常系 / DB 不在時テスト新規追加される型
StatusOptionssrc/cli/status.rsStorageBreakdownsrc/cli/status.rsまたはsrc/indexer/受け入れ基準
機能要件
status --detailでファイルカバレッジ(Total / Indexed / Skipped)が表示される.git/,node_modules/,target/はデフォルト除外)EmbeddingStore::count_distinct_files()を使って表示されるembeddings.db不在時は Embedding count が0として扱われる(エラーにならない)(not configured)と表示されるgitコマンドで取得・表示されるgit未インストール / リポジトリ外 / コマンドエラー時は Staleness セクションが best-effort で(Git info unavailable)と表示され、exit code 0 で終了する--format jsonの出力に拡張情報(coverage / staleness / storage / embedding_coverage)が含まれる--format jsonの新フィールドは#[serde(skip_serializing_if = "Option::is_none")]でデフォルト出力の後方互換を維持StorageBreakdown構造体を使いtantivy//symbols.db/embeddings.db/Otherの内訳で表示される--detailと--coverageは clapconflicts_withで排他オプションとして動作する(同時指定時エラー)statusは既存出力と完全互換データモデル
IndexStateにlast_commit_hash: Option<String>が追加されている#[serde(default)]により既存state.jsonとの後方互換が維持されるschema_versionバンプ不要であることが確認され、コード内コメントで理由が明記されている設計要件
StatusOptions構造体が導入され、Defaultトレイト実装により既存テストがStatusOptions::default()で互換維持されるStorageBreakdown構造体が導入され、ストレージ情報が構造化されているget_embedding_file_count()ヘルパーが存在し、DB 不在時に0を返すテスト要件
tests/cli_args.rsに--detail --coverage排他テストが存在するIndexStateの serde 後方互換テスト(古い JSON → 新しい struct)が存在するcount_distinct_files()の正常系 / DB 不在時ユニットテストが存在するStatusOptions::default()を使った既存テスト互換が維持されている品質要件
将来の拡張候補
--watchモード(リアルタイム status 更新)status結果のキャッシュ(大規模リポジトリでの walkdir 高速化)--output-fileオプション(結果をファイルに保存)依存 Issue