feat(best_practice): add code quality scanning and commit message validation
- Implemented a code quality scanner that checks for common clean-code violations in Rust source files, including missing documentation, usage of `.unwrap()` in production code, and commented-out code. - Introduced a commit message validator that follows the Conventional Commits specification, ensuring proper formatting and providing suggestions for invalid messages. - Created a unified BestPracticeEngine to encapsulate the functionalities of skills, architecture audits, code quality checks, and commit message validation. - Added tests for both the code quality scanner and commit message validator to ensure reliability and correctness.
This commit is contained in:
1 parent
55677dd671
commit
8ecc588a3e
20 files changed
+3449
No files matched your search
@@ -0,0 +1,188 @@
|
||||
//! # Built-in Kana Engineering Best Practices Engine
|
||||
//!
|
||||
//! This module provides compile-time embedded skills, architecture audit,
|
||||
//! code-quality scanning, and commit-message validation as **built-in**
|
||||
//! features of the zesdex binary — not external configuration files.
|
||||
//!
|
||||
//! ## Modules
|
||||
//!
|
||||
//! | Module | Purpose |
|
||||
//! |--------|---------|
|
||||
//! | [`skills`] | Compile-time embedded skill markdown files via `include_dir!` |
|
||||
//! | [`arch_audit`] | Clean-architecture layering violation scanner |
|
||||
//! | [`code_quality`] | Clean-code rule checker (unwrap, missing docs, etc.) |
|
||||
//! | [`commit`] | Conventional Commits message validator |
|
||||
//!
|
||||
//! ## Usage
|
||||
//!
|
||||
//! The [`BestPracticeEngine`] struct provides a unified API that tools call.
|
||||
//! New tool implementations in `crate::tools::best_practice` delegate to this
|
||||
//! engine.
|
||||
|
||||
pub mod arch_audit;
|
||||
pub mod code_quality;
|
||||
pub mod commit;
|
||||
pub mod skills;
|
||||
|
||||
use anyhow::Result;
|
||||
use std::path::Path;
|
||||
|
||||
/// Unified engine for all best-practice operations.
|
||||
///
|
||||
/// Wraps the individual audit, quality, and commit modules behind a single
|
||||
/// struct that tools and subagents can call without knowing the module layout.
|
||||
#[derive(Default)]
|
||||
pub struct BestPracticeEngine {
|
||||
/// Compile-time embedded skills, lazy-loaded.
|
||||
embedded_skills: std::sync::OnceLock<skills::EmbeddedSkills>,
|
||||
}
|
||||
|
||||
impl BestPracticeEngine {
|
||||
/// Create a new engine (skills index is built lazily on first access).
|
||||
pub fn new() -> Self {
|
||||
BestPracticeEngine {
|
||||
embedded_skills: std::sync::OnceLock::new(),
|
||||
}
|
||||
}
|
||||
|
||||
// ── Skills ─────────────────────────────────────────────────────────
|
||||
|
||||
/// Access the embedded skills index.
|
||||
pub fn skills(&self) -> &skills::EmbeddedSkills {
|
||||
self.embedded_skills
|
||||
.get_or_init(skills::EmbeddedSkills::load)
|
||||
}
|
||||
|
||||
/// List all available embedded-skills names.
|
||||
pub fn list_skills(&self) -> Vec<&'static str> {
|
||||
self.skills().list()
|
||||
}
|
||||
|
||||
/// Get the full content of a skill by name.
|
||||
pub fn get_skill(&self, name: &str) -> Option<&'static str> {
|
||||
self.skills().get(name)
|
||||
}
|
||||
|
||||
/// Get skill summaries (name + description).
|
||||
pub fn skill_summaries(&self) -> Vec<(&'static str, &'static str)> {
|
||||
self.skills().summaries()
|
||||
}
|
||||
|
||||
// ── Architecture Audit ─────────────────────────────────────────────
|
||||
|
||||
/// Run a full architecture layering audit on a workspace directory.
|
||||
pub fn audit_layering(&self, workspace_root: &Path) -> Result<arch_audit::AuditReport> {
|
||||
arch_audit::audit_layering(workspace_root)
|
||||
}
|
||||
|
||||
// ── Code Quality ───────────────────────────────────────────────────
|
||||
|
||||
/// Run a full code-quality scan on a workspace directory.
|
||||
pub fn scan_quality(&self, workspace_root: &Path) -> Result<code_quality::CodeQualityReport> {
|
||||
code_quality::scan_quality(workspace_root)
|
||||
}
|
||||
|
||||
// ── Commit Convention ──────────────────────────────────────────────
|
||||
|
||||
/// Validate a commit message against Conventional Commits.
|
||||
pub fn validate_commit(&self, message: &str) -> Result<(), Vec<String>> {
|
||||
commit::validate_commit_message(message)
|
||||
}
|
||||
|
||||
/// Parse a commit message into its structured fields.
|
||||
pub fn parse_commit(&self, message: &str) -> Option<commit::CommitInfo> {
|
||||
commit::parse_commit_message(message)
|
||||
}
|
||||
|
||||
/// Get a commit template suggestion.
|
||||
pub fn suggest_commit_template(&self, type_: &str, scope: Option<&str>) -> String {
|
||||
commit::suggest_template(type_, scope)
|
||||
}
|
||||
|
||||
// ── Combined Audit ─────────────────────────────────────────────────
|
||||
|
||||
/// Run all audits (layering + code quality) and return combined results.
|
||||
pub fn audit_all(&self, workspace_root: &Path) -> Result<CombinedAuditReport> {
|
||||
let layering = self.audit_layering(workspace_root)?;
|
||||
let quality = self.scan_quality(workspace_root)?;
|
||||
|
||||
Ok(CombinedAuditReport {
|
||||
layering,
|
||||
quality,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Combined report from all audits.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CombinedAuditReport {
|
||||
pub layering: arch_audit::AuditReport,
|
||||
pub quality: code_quality::CodeQualityReport,
|
||||
}
|
||||
|
||||
impl CombinedAuditReport {
|
||||
/// Format the full report as a human-readable string.
|
||||
pub fn format(&self) -> String {
|
||||
let mut out = String::new();
|
||||
|
||||
// ── Layering ──
|
||||
out.push_str(&format!(
|
||||
"=== Architecture Layering Audit ===\n\
|
||||
Files scanned: {}\n",
|
||||
self.layering.files_scanned
|
||||
));
|
||||
if self.layering.violations.is_empty() {
|
||||
out.push_str(" ✅ No layering violations found.\n");
|
||||
} else {
|
||||
for v in &self.layering.violations {
|
||||
out.push_str(&format!(
|
||||
" [{}] {}:{} — {}\n",
|
||||
v.severity, v.file, v.line, v.message
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// ── Code Quality ──
|
||||
out.push_str(&format!(
|
||||
"\n=== Code Quality Scan ===\n\
|
||||
Files scanned: {}\n",
|
||||
self.quality.files_scanned
|
||||
));
|
||||
if self.quality.findings.is_empty() {
|
||||
out.push_str(" ✅ No code-quality issues found.\n");
|
||||
} else {
|
||||
let by_rule = self.quality.count_by_rule();
|
||||
out.push_str(" By rule:\n");
|
||||
for (rule, count) in &by_rule {
|
||||
out.push_str(&format!(" {rule}: {count}\n"));
|
||||
}
|
||||
out.push_str(" Top findings:\n");
|
||||
for f in self.quality.findings.iter().take(20) {
|
||||
out.push_str(&format!(
|
||||
" [{}] {}:{} — {}: {}\n",
|
||||
f.severity, f.file, f.line, f.rule, f.message
|
||||
));
|
||||
}
|
||||
if self.quality.findings.len() > 20 {
|
||||
out.push_str(&format!(
|
||||
" ... and {} more findings.\n",
|
||||
self.quality.findings.len() - 20
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn engine_lists_skills() {
|
||||
let engine = BestPracticeEngine::new();
|
||||
let names = engine.list_skills();
|
||||
assert!(names.contains(&"clean-code"));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user