refactor(cli): migrate all command modules from console::style to Theme
Replace all console::style() calls in command modules with the centralized Theme API and render:: utility functions. This ensures consistent color behavior across the entire CLI, proper NO_COLOR/--color never support via the LoreRenderer singleton, and eliminates duplicated formatting code. Changes per module: - count.rs: Theme for table headers, render::format_number replacing local duplicate. Removed local format_number implementation. - doctor.rs: Theme::success/warning/error for check status symbols and messages. Unicode escapes for check/warning/cross symbols. - drift.rs: Theme::bold/error/success for drift detection headers and status messages. - embed.rs: Compact output format — headline with count, zero-suppressed detail lines, 'nothing to embed' short-circuit for no-op runs. - generate_docs.rs: Same compact pattern — headline + detail + hint for next step. No-op short-circuit when regenerated==0. - ingest.rs: Theme for project summaries, sync status, dry-run preview. All console::style -> Theme replacements. - list.rs: Replace comfy-table with render::LoreTable for issue/MR listing. Remove local colored_cell, colored_cell_hex, format_relative_time, truncate_with_ellipsis, and format_labels (all moved to render.rs). - list_tests.rs: Update test assertions to use render:: functions. - search.rs: Add render_snippet() for FTS5 <mark> tag highlighting via Theme::bold().underline(). Compact result layout with type badges. - show.rs: Theme for entity detail views, delegate format_date and wrap_text to render module. - stats.rs: Section-based layout using render::section_divider. Compact middle-dot format for document counts. Color-coded embedding coverage percentage (green >=95%, yellow >=50%, red <50%). - sync.rs: Compact sync summary — headline with counts and elapsed time, zero-suppressed detail lines, visually prominent error-only section. - sync_status.rs: Theme for run history headers, removed local format_number duplicate. - timeline.rs: Theme for headers/footers, render:: for date/truncate, standard format! padding replacing console::pad_str. - who.rs: Theme for all expert/workload/active/overlap/review output modes, render:: for relative time and truncation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use console::style;
|
||||
use crate::cli::render::{self, Theme};
|
||||
use rusqlite::Connection;
|
||||
use serde::Serialize;
|
||||
|
||||
@@ -322,124 +322,183 @@ fn table_exists(conn: &Connection, table: &str) -> bool {
|
||||
> 0
|
||||
}
|
||||
|
||||
fn section(title: &str) {
|
||||
println!("{}", render::section_divider(title));
|
||||
}
|
||||
|
||||
pub fn print_stats(result: &StatsResult) {
|
||||
println!("{}", style("Documents").cyan().bold());
|
||||
println!(" Total: {}", result.documents.total);
|
||||
println!(" Issues: {}", result.documents.issues);
|
||||
println!(" Merge Requests: {}", result.documents.merge_requests);
|
||||
println!(" Discussions: {}", result.documents.discussions);
|
||||
section("Documents");
|
||||
let mut parts = vec![format!("{} total", result.documents.total)];
|
||||
if result.documents.issues > 0 {
|
||||
parts.push(format!("{} issues", result.documents.issues));
|
||||
}
|
||||
if result.documents.merge_requests > 0 {
|
||||
parts.push(format!("{} MRs", result.documents.merge_requests));
|
||||
}
|
||||
if result.documents.discussions > 0 {
|
||||
parts.push(format!("{} discussions", result.documents.discussions));
|
||||
}
|
||||
println!(" {}", parts.join(" \u{b7} "));
|
||||
if result.documents.truncated > 0 {
|
||||
println!(
|
||||
" Truncated: {}",
|
||||
style(result.documents.truncated).yellow()
|
||||
" {}",
|
||||
Theme::warning().render(&format!("{} truncated", result.documents.truncated))
|
||||
);
|
||||
}
|
||||
println!();
|
||||
|
||||
println!("{}", style("Search Index").cyan().bold());
|
||||
println!(" FTS indexed: {}", result.fts.indexed);
|
||||
section("Search Index");
|
||||
println!(" {} FTS indexed", result.fts.indexed);
|
||||
let coverage_color = if result.embeddings.coverage_pct >= 95.0 {
|
||||
Theme::success().render(&format!("{:.0}%", result.embeddings.coverage_pct))
|
||||
} else if result.embeddings.coverage_pct >= 50.0 {
|
||||
Theme::warning().render(&format!("{:.0}%", result.embeddings.coverage_pct))
|
||||
} else {
|
||||
Theme::error().render(&format!("{:.0}%", result.embeddings.coverage_pct))
|
||||
};
|
||||
println!(
|
||||
" Embedding coverage: {:.1}% ({}/{})",
|
||||
result.embeddings.coverage_pct,
|
||||
result.embeddings.embedded_documents,
|
||||
result.documents.total
|
||||
" {} embedding coverage ({}/{})",
|
||||
coverage_color, result.embeddings.embedded_documents, result.documents.total,
|
||||
);
|
||||
if result.embeddings.total_chunks > 0 {
|
||||
println!(" Total chunks: {}", result.embeddings.total_chunks);
|
||||
}
|
||||
println!();
|
||||
|
||||
println!("{}", style("Queues").cyan().bold());
|
||||
println!(
|
||||
" Dirty sources: {} pending, {} failed",
|
||||
result.queues.dirty_sources, result.queues.dirty_sources_failed
|
||||
);
|
||||
println!(
|
||||
" Discussion fetch: {} pending, {} failed",
|
||||
result.queues.pending_discussion_fetches, result.queues.pending_discussion_fetches_failed
|
||||
);
|
||||
if result.queues.pending_dependent_fetches > 0
|
||||
|| result.queues.pending_dependent_fetches_failed > 0
|
||||
|| result.queues.pending_dependent_fetches_stuck > 0
|
||||
{
|
||||
println!(
|
||||
" Dependent fetch: {} pending, {} failed, {} stuck",
|
||||
result.queues.pending_dependent_fetches,
|
||||
result.queues.pending_dependent_fetches_failed,
|
||||
result.queues.pending_dependent_fetches_stuck
|
||||
" {}",
|
||||
Theme::dim().render(&format!("{} chunks", result.embeddings.total_chunks))
|
||||
);
|
||||
}
|
||||
|
||||
// Queues: only show if there's anything to report
|
||||
let has_queue_activity = result.queues.dirty_sources > 0
|
||||
|| result.queues.dirty_sources_failed > 0
|
||||
|| result.queues.pending_discussion_fetches > 0
|
||||
|| result.queues.pending_discussion_fetches_failed > 0
|
||||
|| result.queues.pending_dependent_fetches > 0
|
||||
|| result.queues.pending_dependent_fetches_failed > 0;
|
||||
|
||||
if has_queue_activity {
|
||||
section("Queues");
|
||||
if result.queues.dirty_sources > 0 || result.queues.dirty_sources_failed > 0 {
|
||||
let mut q = Vec::new();
|
||||
if result.queues.dirty_sources > 0 {
|
||||
q.push(format!("{} pending", result.queues.dirty_sources));
|
||||
}
|
||||
if result.queues.dirty_sources_failed > 0 {
|
||||
q.push(
|
||||
Theme::error()
|
||||
.render(&format!("{} failed", result.queues.dirty_sources_failed)),
|
||||
);
|
||||
}
|
||||
println!(" dirty sources: {}", q.join(", "));
|
||||
}
|
||||
if result.queues.pending_discussion_fetches > 0
|
||||
|| result.queues.pending_discussion_fetches_failed > 0
|
||||
{
|
||||
let mut q = Vec::new();
|
||||
if result.queues.pending_discussion_fetches > 0 {
|
||||
q.push(format!(
|
||||
"{} pending",
|
||||
result.queues.pending_discussion_fetches
|
||||
));
|
||||
}
|
||||
if result.queues.pending_discussion_fetches_failed > 0 {
|
||||
q.push(Theme::error().render(&format!(
|
||||
"{} failed",
|
||||
result.queues.pending_discussion_fetches_failed
|
||||
)));
|
||||
}
|
||||
println!(" discussion fetch: {}", q.join(", "));
|
||||
}
|
||||
if result.queues.pending_dependent_fetches > 0
|
||||
|| result.queues.pending_dependent_fetches_failed > 0
|
||||
{
|
||||
let mut q = Vec::new();
|
||||
if result.queues.pending_dependent_fetches > 0 {
|
||||
q.push(format!(
|
||||
"{} pending",
|
||||
result.queues.pending_dependent_fetches
|
||||
));
|
||||
}
|
||||
if result.queues.pending_dependent_fetches_failed > 0 {
|
||||
q.push(Theme::error().render(&format!(
|
||||
"{} failed",
|
||||
result.queues.pending_dependent_fetches_failed
|
||||
)));
|
||||
}
|
||||
if result.queues.pending_dependent_fetches_stuck > 0 {
|
||||
q.push(Theme::warning().render(&format!(
|
||||
"{} stuck",
|
||||
result.queues.pending_dependent_fetches_stuck
|
||||
)));
|
||||
}
|
||||
println!(" dependent fetch: {}", q.join(", "));
|
||||
}
|
||||
} else {
|
||||
section("Queues");
|
||||
println!(" {}", Theme::success().render("all clear"));
|
||||
}
|
||||
|
||||
if let Some(ref integrity) = result.integrity {
|
||||
println!();
|
||||
let status = if integrity.ok {
|
||||
style("OK").green().bold()
|
||||
section("Integrity");
|
||||
if integrity.ok {
|
||||
println!(
|
||||
" {} all checks passed",
|
||||
Theme::success().render("\u{2713}")
|
||||
);
|
||||
} else {
|
||||
style("ISSUES FOUND").red().bold()
|
||||
};
|
||||
println!("{} Integrity: {}", style("Check").cyan().bold(), status);
|
||||
|
||||
if integrity.fts_doc_mismatch {
|
||||
println!(" {} FTS/document count mismatch", style("!").red());
|
||||
}
|
||||
if integrity.orphan_embeddings > 0 {
|
||||
println!(
|
||||
" {} {} orphan embeddings",
|
||||
style("!").red(),
|
||||
integrity.orphan_embeddings
|
||||
);
|
||||
}
|
||||
if integrity.stale_metadata > 0 {
|
||||
println!(
|
||||
" {} {} stale embedding metadata",
|
||||
style("!").red(),
|
||||
integrity.stale_metadata
|
||||
);
|
||||
}
|
||||
let orphan_events = integrity.orphan_state_events
|
||||
+ integrity.orphan_label_events
|
||||
+ integrity.orphan_milestone_events;
|
||||
if orphan_events > 0 {
|
||||
println!(
|
||||
" {} {} orphan resource events (state: {}, label: {}, milestone: {})",
|
||||
style("!").red(),
|
||||
orphan_events,
|
||||
integrity.orphan_state_events,
|
||||
integrity.orphan_label_events,
|
||||
integrity.orphan_milestone_events
|
||||
);
|
||||
}
|
||||
if integrity.queue_stuck_locks > 0 {
|
||||
println!(
|
||||
" {} {} stuck queue locks",
|
||||
style("!").yellow(),
|
||||
integrity.queue_stuck_locks
|
||||
);
|
||||
}
|
||||
if integrity.queue_max_attempts > 3 {
|
||||
println!(
|
||||
" {} max queue retry attempts: {}",
|
||||
style("!").yellow(),
|
||||
integrity.queue_max_attempts
|
||||
);
|
||||
if integrity.fts_doc_mismatch {
|
||||
println!(
|
||||
" {} FTS/document count mismatch",
|
||||
Theme::error().render("\u{2717}")
|
||||
);
|
||||
}
|
||||
if integrity.orphan_embeddings > 0 {
|
||||
println!(
|
||||
" {} {} orphan embeddings",
|
||||
Theme::error().render("\u{2717}"),
|
||||
integrity.orphan_embeddings
|
||||
);
|
||||
}
|
||||
if integrity.stale_metadata > 0 {
|
||||
println!(
|
||||
" {} {} stale embedding metadata",
|
||||
Theme::error().render("\u{2717}"),
|
||||
integrity.stale_metadata
|
||||
);
|
||||
}
|
||||
let orphan_events = integrity.orphan_state_events
|
||||
+ integrity.orphan_label_events
|
||||
+ integrity.orphan_milestone_events;
|
||||
if orphan_events > 0 {
|
||||
println!(
|
||||
" {} {} orphan resource events",
|
||||
Theme::error().render("\u{2717}"),
|
||||
orphan_events
|
||||
);
|
||||
}
|
||||
if integrity.queue_stuck_locks > 0 {
|
||||
println!(
|
||||
" {} {} stuck queue locks",
|
||||
Theme::warning().render("!"),
|
||||
integrity.queue_stuck_locks
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref repair) = integrity.repair {
|
||||
println!();
|
||||
if repair.dry_run {
|
||||
println!(
|
||||
"{} {}",
|
||||
style("Repair").cyan().bold(),
|
||||
style("(dry run - no changes made)").yellow()
|
||||
" {} {}",
|
||||
Theme::bold().render("Repair"),
|
||||
Theme::warning().render("(dry run)")
|
||||
);
|
||||
} else {
|
||||
println!("{}", style("Repair").cyan().bold());
|
||||
println!(" {}", Theme::bold().render("Repair"));
|
||||
}
|
||||
|
||||
let action = if repair.dry_run {
|
||||
style("would fix").yellow()
|
||||
Theme::warning().render("would fix")
|
||||
} else {
|
||||
style("fixed").green()
|
||||
Theme::success().render("fixed")
|
||||
};
|
||||
|
||||
if repair.fts_rebuilt {
|
||||
@@ -453,15 +512,17 @@ pub fn print_stats(result: &StatsResult) {
|
||||
}
|
||||
if repair.stale_cleared > 0 {
|
||||
println!(
|
||||
" {} {} stale metadata entries cleared",
|
||||
" {} {} stale metadata cleared",
|
||||
action, repair.stale_cleared
|
||||
);
|
||||
}
|
||||
if !repair.fts_rebuilt && repair.orphans_deleted == 0 && repair.stale_cleared == 0 {
|
||||
println!(" No issues to repair.");
|
||||
println!(" {}", Theme::dim().render("nothing to repair"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!();
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
|
||||
Reference in New Issue
Block a user