#![cfg_attr(coverage_nightly, coverage(off))]
use anyhow::{bail, Result};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum PmatSubAgent {
ComplexityAnalyst,
MutationTester,
SATDDetector,
DeadCodeEliminator,
DocumentationEnforcer,
RustQualityExpert,
PythonQualityExpert,
TypeScriptQualityExpert,
WasmDeepInspector,
RefactoringAdvisor,
TestCoverageAnalyst,
QualityGateOrchestrator,
}
impl PmatSubAgent {
pub fn name(&self) -> &'static str {
match self {
Self::ComplexityAnalyst => "complexity-analyst",
Self::MutationTester => "mutation-tester",
Self::SATDDetector => "satd-detector",
Self::DeadCodeEliminator => "dead-code-eliminator",
Self::DocumentationEnforcer => "documentation-enforcer",
Self::RustQualityExpert => "rust-quality-expert",
Self::PythonQualityExpert => "python-quality-expert",
Self::TypeScriptQualityExpert => "typescript-quality-expert",
Self::WasmDeepInspector => "wasm-deep-inspector",
Self::RefactoringAdvisor => "refactoring-advisor",
Self::TestCoverageAnalyst => "test-coverage-analyst",
Self::QualityGateOrchestrator => "quality-gate-orchestrator",
}
}
pub fn description(&self) -> &'static str {
match self {
Self::ComplexityAnalyst => {
"Expert in cyclomatic and cognitive complexity analysis, suggests refactorings"
}
Self::MutationTester => {
"Mutation testing specialist with ML prediction and test improvement suggestions"
}
Self::SATDDetector => {
"Technical debt identifier tracking TODO, FIXME, and HACK comments"
}
Self::DeadCodeEliminator => {
"Unused code removal specialist identifying safe-to-delete code"
}
Self::DocumentationEnforcer => {
"Generic description detector enforcing documentation quality standards"
}
Self::RustQualityExpert => {
"Rust-specific quality expert covering ownership, lifetimes, and idiomatic patterns"
}
Self::PythonQualityExpert => {
"Python quality expert for type hints, PEP compliance, and best practices"
}
Self::TypeScriptQualityExpert => {
"TypeScript/JavaScript quality expert for type safety and modern patterns"
}
Self::WasmDeepInspector => {
"WebAssembly bytecode analyst for compiler debugging and optimization"
}
Self::RefactoringAdvisor => {
"AI-powered refactoring advisor using historical pattern learning"
}
Self::TestCoverageAnalyst => {
"Test coverage gap identifier suggesting missing test cases"
}
Self::QualityGateOrchestrator => {
"Coordinates multiple quality checks and synthesizes results"
}
}
}
pub fn is_mvp(&self) -> bool {
matches!(
self,
Self::ComplexityAnalyst
| Self::MutationTester
| Self::SATDDetector
| Self::DeadCodeEliminator
| Self::DocumentationEnforcer
)
}
pub fn primary_tools(&self) -> Vec<&'static str> {
match self {
Self::ComplexityAnalyst => vec!["analyze_complexity", "analyze_cognitive_complexity"],
Self::MutationTester => {
vec!["mutation_test", "mutation_predict", "equivalent_detector"]
}
Self::SATDDetector => vec!["analyze_satd", "analyze_context"],
Self::DeadCodeEliminator => vec!["analyze_dead_code", "analyze_imports"],
Self::DocumentationEnforcer => vec!["check_generic_docs", "analyze_context"],
Self::RustQualityExpert => vec!["analyze_complexity", "analyze_borrow_checker"],
Self::PythonQualityExpert => vec!["analyze_complexity", "analyze_context"],
Self::TypeScriptQualityExpert => vec!["analyze_complexity", "analyze_context"],
Self::WasmDeepInspector => vec!["deep_wasm_analyze", "wasm_disassemble"],
Self::RefactoringAdvisor => vec!["suggest_refactorings", "query_patterns"],
Self::TestCoverageAnalyst => vec!["analyze_coverage", "suggest_tests"],
Self::QualityGateOrchestrator => vec!["run_quality_gates", "aggregate_metrics"],
}
}
pub fn all_mvp() -> Vec<Self> {
vec![
Self::ComplexityAnalyst,
Self::MutationTester,
Self::SATDDetector,
Self::DeadCodeEliminator,
Self::DocumentationEnforcer,
]
}
pub fn all() -> Vec<Self> {
vec![
Self::ComplexityAnalyst,
Self::MutationTester,
Self::SATDDetector,
Self::DeadCodeEliminator,
Self::DocumentationEnforcer,
Self::RustQualityExpert,
Self::PythonQualityExpert,
Self::TypeScriptQualityExpert,
Self::WasmDeepInspector,
Self::RefactoringAdvisor,
Self::TestCoverageAnalyst,
Self::QualityGateOrchestrator,
]
}
}
impl std::str::FromStr for PmatSubAgent {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"complexity-analyst" => Ok(Self::ComplexityAnalyst),
"mutation-tester" => Ok(Self::MutationTester),
"satd-detector" => Ok(Self::SATDDetector),
"dead-code-eliminator" => Ok(Self::DeadCodeEliminator),
"documentation-enforcer" => Ok(Self::DocumentationEnforcer),
"rust-quality-expert" => Ok(Self::RustQualityExpert),
"python-quality-expert" => Ok(Self::PythonQualityExpert),
"typescript-quality-expert" => Ok(Self::TypeScriptQualityExpert),
"wasm-deep-inspector" => Ok(Self::WasmDeepInspector),
"refactoring-advisor" => Ok(Self::RefactoringAdvisor),
"test-coverage-analyst" => Ok(Self::TestCoverageAnalyst),
"quality-gate-orchestrator" => Ok(Self::QualityGateOrchestrator),
_ => bail!("Unknown sub-agent: {}", s),
}
}
}
impl std::fmt::Display for PmatSubAgent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name())
}
}
pub struct SubAgentGenerator {
_template_dir: PathBuf,
}
impl SubAgentGenerator {
pub fn new() -> Self {
Self {
_template_dir: PathBuf::from("server/src/scaffold/agent/subagent_templates"),
}
}
pub fn with_template_dir(template_dir: PathBuf) -> Self {
Self {
_template_dir: template_dir,
}
}
pub fn generate_subagent(&self, agent: PmatSubAgent) -> Result<String> {
if !agent.is_mvp() {
bail!(
"Sub-agent {} is not yet implemented (future phase)",
agent.name()
);
}
match agent {
PmatSubAgent::ComplexityAnalyst => self.generate_complexity_analyst(),
PmatSubAgent::MutationTester => self.generate_mutation_tester(),
PmatSubAgent::SATDDetector => self.generate_satd_detector(),
PmatSubAgent::DeadCodeEliminator => self.generate_dead_code_eliminator(),
PmatSubAgent::DocumentationEnforcer => self.generate_documentation_enforcer(),
_ => bail!("Sub-agent {} not implemented in MVP", agent.name()),
}
}
pub fn export_for_claude_code(
&self,
agent: PmatSubAgent,
output_dir: &Path,
) -> Result<PathBuf> {
let content = self.generate_subagent(agent)?;
let filename = format!("{}.md", agent.name());
let output_path = output_dir.join(filename);
std::fs::create_dir_all(output_dir)?;
std::fs::write(&output_path, content)?;
Ok(output_path)
}
pub fn export_all_mvp(&self, output_dir: &Path) -> Result<Vec<PathBuf>> {
let mut paths = Vec::new();
for agent in PmatSubAgent::all_mvp() {
let path = self.export_for_claude_code(agent, output_dir)?;
paths.push(path);
}
Ok(paths)
}
pub fn get_tool_mapping() -> HashMap<PmatSubAgent, Vec<&'static str>> {
let mut mapping = HashMap::new();
for agent in PmatSubAgent::all() {
mapping.insert(agent, agent.primary_tools());
}
mapping
}
fn generate_complexity_analyst(&self) -> Result<String> {
Ok(include_str!("subagent_templates/complexity_analyst.md.tmpl").to_string())
}
fn generate_mutation_tester(&self) -> Result<String> {
Ok(include_str!("subagent_templates/mutation_tester.md.tmpl").to_string())
}
fn generate_satd_detector(&self) -> Result<String> {
Ok(include_str!("subagent_templates/satd_detector.md.tmpl").to_string())
}
fn generate_dead_code_eliminator(&self) -> Result<String> {
Ok(include_str!("subagent_templates/dead_code_eliminator.md.tmpl").to_string())
}
fn generate_documentation_enforcer(&self) -> Result<String> {
Ok(include_str!("subagent_templates/documentation_enforcer.md.tmpl").to_string())
}
}
impl Default for SubAgentGenerator {
fn default() -> Self {
Self::new()
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_subagent_name() {
assert_eq!(PmatSubAgent::ComplexityAnalyst.name(), "complexity-analyst");
assert_eq!(PmatSubAgent::MutationTester.name(), "mutation-tester");
}
#[test]
fn test_subagent_from_str() {
let agent: PmatSubAgent = "complexity-analyst".parse().unwrap();
assert_eq!(agent, PmatSubAgent::ComplexityAnalyst);
let agent: PmatSubAgent = "mutation-tester".parse().unwrap();
assert_eq!(agent, PmatSubAgent::MutationTester);
}
#[test]
fn test_subagent_from_str_invalid() {
let result: Result<PmatSubAgent> = "invalid-agent".parse();
assert!(result.is_err());
}
#[test]
fn test_all_mvp_agents() {
let agents = PmatSubAgent::all_mvp();
assert_eq!(agents.len(), 5);
assert!(agents.iter().all(|a| a.is_mvp()));
}
#[test]
fn test_primary_tools() {
let tools = PmatSubAgent::ComplexityAnalyst.primary_tools();
assert!(tools.contains(&"analyze_complexity"));
let tools = PmatSubAgent::MutationTester.primary_tools();
assert!(tools.contains(&"mutation_test"));
assert!(tools.contains(&"mutation_predict"));
}
#[test]
fn test_tool_mapping() {
let mapping = SubAgentGenerator::get_tool_mapping();
assert!(!mapping.is_empty());
let complexity_tools = mapping.get(&PmatSubAgent::ComplexityAnalyst).unwrap();
assert!(complexity_tools.contains(&"analyze_complexity"));
}
#[test]
fn test_generator_creation() {
let gen = SubAgentGenerator::new();
assert!(gen
._template_dir
.to_string_lossy()
.contains("subagent_templates"));
}
#[test]
fn test_non_mvp_agent_generates_error() {
let gen = SubAgentGenerator::new();
let result = gen.generate_subagent(PmatSubAgent::RustQualityExpert);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("not yet implemented"));
}
}