#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
#![cfg_attr(coverage_nightly, coverage(off))]
use clap::{Parser, Subcommand};
use pmat::agents::analyzer_actor::AnalyzerActor;
use pmat::agents::registry::AgentRegistry;
use pmat::agents::transformer_actor::TransformerActor;
use pmat::agents::validator_actor::ValidatorActor;
use pmat::mcp_integration::server::{McpServer, ServerConfig};
use pmat::workflow::dsl::DslCompiler;
use pmat::workflow::{DefaultWorkflowExecutor, WorkflowContext, WorkflowExecutor};
use std::sync::Arc;
use std::time::Duration;
use tokio::fs;
#[derive(Parser)]
#[command(name = "pmat-agent")]
#[command(about = "PMAT Agent System - Enterprise-grade agent orchestration", long_about = None)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Serve {
#[arg(short, long, default_value = "127.0.0.1:3000")]
bind: String,
#[arg(short = 'u', long)]
socket: Option<String>,
#[arg(long)]
stdio: bool,
#[arg(long, default_value_t = 100)]
max_connections: usize,
},
Execute {
#[arg(short, long)]
file: String,
#[arg(short, long)]
params: Option<String>,
#[arg(short, long)]
timeout: Option<u64>,
},
Validate {
#[arg(short, long)]
file: String,
},
Analyze {
#[arg(short, long)]
path: String,
#[arg(short, long)]
language: String,
#[arg(short, long, default_value = "text")]
output: String,
},
QualityGate {
#[arg(short, long)]
path: String,
#[arg(short, long)]
language: String,
#[arg(long, default_value_t = 10)]
max_complexity: u32,
#[arg(long, default_value_t = 0)]
max_satd: usize,
#[arg(long)]
fail_on_violation: bool,
},
Info,
}
#[actix_rt::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
let cli = Cli::parse();
let registry = Arc::new(AgentRegistry::new());
initialize_agents(®istry).await?;
match cli.command {
Commands::Serve {
bind,
socket,
stdio,
max_connections,
} => serve_mcp(registry, bind, socket, stdio, max_connections).await?,
Commands::Execute {
file,
params,
timeout,
} => execute_workflow(registry, file, params, timeout).await?,
Commands::Validate { file } => validate_workflow(file).await?,
Commands::Analyze {
path,
language,
output,
} => analyze_code(registry, path, language, output).await?,
Commands::QualityGate {
path,
language,
max_complexity,
max_satd,
fail_on_violation,
} => {
run_quality_gate(
registry,
path,
language,
max_complexity,
max_satd,
fail_on_violation,
)
.await?
}
Commands::Info => show_info().await?,
}
Ok(())
}
async fn initialize_agents(
registry: &Arc<AgentRegistry>,
) -> Result<(), Box<dyn std::error::Error>> {
use pmat::agents::orchestrator_actor::OrchestratorActor;
registry
.register("analyzer", Arc::new(AnalyzerActor::default()))
.await;
registry
.register("transformer", Arc::new(TransformerActor::default()))
.await;
registry
.register("validator", Arc::new(ValidatorActor::default()))
.await;
registry
.register("orchestrator", Arc::new(OrchestratorActor::new()))
.await;
println!(
"✓ Initialized {} agents",
registry.list_agents().await.len()
);
Ok(())
}
async fn serve_mcp(
registry: Arc<AgentRegistry>,
bind: String,
socket: Option<String>,
stdio: bool,
max_connections: usize,
) -> Result<(), Box<dyn std::error::Error>> {
let config = ServerConfig {
name: "PMAT Agent Server".to_string(),
bind_address: bind.clone(),
unix_socket: socket.clone(),
max_connections,
..Default::default()
};
let server = McpServer::new(registry, config)?;
server.register_defaults().await?;
println!("🚀 PMAT Agent Server v{}", env!("CARGO_PKG_VERSION"));
println!(" Protocol: MCP {}", pmat::mcp_integration::MCP_VERSION);
if stdio {
println!("📝 Using stdio transport");
server.run_stdio().await?;
} else if let Some(socket_path) = socket {
println!("🔌 Listening on Unix socket: {}", socket_path);
server.run_unix().await?;
} else {
println!("🌐 Listening on TCP: {}", bind);
server.run_tcp().await?;
}
Ok(())
}
async fn execute_workflow(
registry: Arc<AgentRegistry>,
file: String,
params: Option<String>,
timeout: Option<u64>,
) -> Result<(), Box<dyn std::error::Error>> {
println!("📋 Loading workflow: {}", file);
let content = fs::read_to_string(&file).await?;
let mut workflow = DslCompiler::compile(&content)?;
if let Some(timeout_secs) = timeout {
workflow.timeout = Some(Duration::from_secs(timeout_secs));
}
println!("▶️ Executing workflow: {}", workflow.name);
println!(" Steps: {}", workflow.steps.len());
let context = WorkflowContext::new(workflow.id, registry.clone());
if let Some(params_json) = params {
let params: serde_json::Value = serde_json::from_str(¶ms_json)?;
for (key, value) in params.as_object().unwrap_or(&serde_json::Map::new()) {
context.set_variable(key.clone(), value.clone());
}
}
let executor = DefaultWorkflowExecutor::new(registry);
let start = std::time::Instant::now();
match executor.execute(&workflow, &context).await {
Ok(result) => {
let elapsed = start.elapsed();
println!("✅ Workflow completed in {:.2}s", elapsed.as_secs_f64());
println!(" Result: {}", serde_json::to_string_pretty(&result)?);
}
Err(e) => {
println!("❌ Workflow failed: {}", e);
std::process::exit(1);
}
}
Ok(())
}
async fn validate_workflow(file: String) -> Result<(), Box<dyn std::error::Error>> {
println!("🔍 Validating workflow: {}", file);
let content = fs::read_to_string(&file).await?;
match DslCompiler::compile(&content) {
Ok(workflow) => {
println!("✅ Valid workflow: {}", workflow.name);
println!(" Version: {}", workflow.version);
println!(" Steps: {}", workflow.steps.len());
for (i, step) in workflow.steps.iter().enumerate() {
println!(" {}. {} ({})", i + 1, step.name, step.id);
}
}
Err(e) => {
println!("❌ Invalid workflow: {}", e);
std::process::exit(1);
}
}
Ok(())
}
async fn analyze_code(
_registry: Arc<AgentRegistry>,
path: String,
language: String,
output: String,
) -> Result<(), Box<dyn std::error::Error>> {
use pmat::quality::complexity::ComplexityAnalyzer;
use pmat::quality::entropy::EntropyCalculator;
use pmat::quality::satd_item::SatdDetectorWithItems;
println!("🔬 Analyzing: {}", path);
let code = fs::read_to_string(&path).await?;
let analyzer = ComplexityAnalyzer::default();
let complexity = analyzer.analyze_string(&code).unwrap_or_default();
let detector = SatdDetectorWithItems::new();
let satd_items = detector.detect(&code);
let calculator = EntropyCalculator::new();
let entropy = calculator.calculate(&code);
match output.as_str() {
"json" => {
let result = serde_json::json!({
"file": path,
"language": language,
"complexity": {
"cyclomatic": complexity.cyclomatic,
"cognitive": complexity.cognitive,
},
"satd": satd_items,
"entropy": entropy,
});
println!("{}", serde_json::to_string_pretty(&result)?);
}
"html" => {
println!("HTML output not yet implemented");
}
_ => {
println!("📊 Analysis Results:");
println!(" Cyclomatic Complexity: {}", complexity.cyclomatic);
println!(" Cognitive Complexity: {}", complexity.cognitive);
println!(" Shannon Entropy: {:.2}", entropy);
println!(" SATD Items: {}", satd_items.len());
if !satd_items.is_empty() {
println!("\n⚠️ Self-Admitted Technical Debt:");
for item in satd_items {
println!(
" - {} (line {}): {}",
item.satd_type, item.line, item.comment
);
}
}
}
}
Ok(())
}
async fn run_quality_gate(
_registry: Arc<AgentRegistry>,
path: String,
language: String,
max_complexity: u32,
max_satd: usize,
fail_on_violation: bool,
) -> Result<(), Box<dyn std::error::Error>> {
use pmat::quality::complexity::ComplexityAnalyzer;
use pmat::quality::gate_runner::{QualityGateRunner, QualityThresholds};
use pmat::quality::satd_item::SatdDetectorWithItems;
println!("🚦 Running quality gates on: {}", path);
let code = fs::read_to_string(&path).await?;
let gate = QualityGateRunner::new(
vec![
Box::new(ComplexityAnalyzer::default()),
Box::new(SatdDetectorWithItems::new()),
],
QualityThresholds {
max_complexity,
max_satd_items: max_satd,
min_test_coverage: 0.0, max_duplication: 1.0, },
);
let result = gate.check(&code, &language).await;
if result.passed {
println!("✅ Quality gates PASSED");
} else {
println!("❌ Quality gates FAILED");
for violation in &result.violations {
println!(" ⚠️ {}", violation);
}
if fail_on_violation {
std::process::exit(1);
}
}
println!("\n📊 Metrics:");
for (key, value) in &result.metrics {
println!(" {}: {}", key, value);
}
Ok(())
}
async fn show_info() -> Result<(), Box<dyn std::error::Error>> {
println!("PMAT Agent System v{}", env!("CARGO_PKG_VERSION"));
println!("═══════════════════════════════════════");
println!("MCP Protocol: {}", pmat::mcp_integration::MCP_VERSION);
println!(
"Build: {} {}",
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_VERSION")
);
println!();
println!("Features:");
println!(" ✓ Actix Actor System");
println!(" ✓ Zero-copy Message Passing");
println!(" ✓ Event Sourcing with Snapshots");
println!(" ✓ Raft Consensus");
println!(" ✓ Resource Control (CPU/Memory/GPU/Network/IO)");
println!(" ✓ MCP Protocol Integration");
println!(" ✓ Workflow Orchestration");
println!(" ✓ Quality Gates (Complexity/SATD/Entropy)");
println!();
println!("Repository: https://github.com/paiml/pmat-agent-toolkit");
Ok(())
}