#![cfg_attr(coverage_nightly, coverage(off))]
use super::{AgentsMdDocument, Command, Path, PathBuf, QualityRules, Section};
use anyhow::{Context, Result};
use std::collections::HashMap;
use std::fmt::Write;
pub struct AgentsMdGenerator {
templates: HashMap<ProjectType, Template>,
config: GeneratorConfig,
}
#[derive(Debug, Clone)]
pub struct GeneratorConfig {
pub include_quality: bool,
pub include_security: bool,
pub include_pr_guidelines: bool,
pub max_commands: usize,
pub include_examples: bool,
}
impl Default for GeneratorConfig {
fn default() -> Self {
Self {
include_quality: true,
include_security: true,
include_pr_guidelines: true,
max_commands: 20,
include_examples: true,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ProjectType {
Rust,
JavaScript,
Python,
Go,
Java,
Generic,
}
#[derive(Debug, Clone)]
pub struct Template {
pub sections: Vec<TemplateSection>,
pub default_commands: Vec<Command>,
}
#[derive(Debug, Clone)]
pub struct TemplateSection {
pub title: String,
pub content: String,
pub variables: Vec<String>,
}
pub struct PmatAnalysis {
pub project_name: String,
pub description: String,
pub project_type: ProjectType,
pub commands: Vec<Command>,
pub quality_metrics: QualityMetrics,
pub dependencies: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct QualityMetrics {
pub avg_complexity: f64,
pub test_coverage: f64,
pub satd_count: usize,
pub grade: String,
}
pub struct ProjectInfo {
pub root: PathBuf,
pub name: String,
pub version: String,
pub description: String,
pub readme: Option<String>,
}
pub struct Updates {
pub new_commands: Vec<Command>,
pub quality_rules: Option<QualityRules>,
pub new_sections: Vec<Section>,
}
impl Default for AgentsMdGenerator {
fn default() -> Self {
Self::new()
}
}
impl AgentsMdGenerator {
#[must_use]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn new() -> Self {
let mut generator = Self {
templates: HashMap::new(),
config: GeneratorConfig::default(),
};
generator.load_default_templates();
generator
}
#[must_use]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn with_config(config: GeneratorConfig) -> Self {
let mut generator = Self {
templates: HashMap::new(),
config,
};
generator.load_default_templates();
generator
}
fn load_default_templates(&mut self) {
self.templates.insert(ProjectType::Rust, Template {
sections: vec![
TemplateSection {
title: "Project Overview".to_string(),
content: "{project_name}\n{description}".to_string(),
variables: vec!["project_name".to_string(), "description".to_string()],
},
TemplateSection {
title: "Development Setup".to_string(),
content: "```bash\n# Install dependencies\ncargo build --all\n\n# Run tests\ncargo test --all\n```".to_string(),
variables: vec![],
},
],
default_commands: vec![
Command {
name: "Build".to_string(),
command: "cargo build --all".to_string(),
working_dir: None,
env: vec![],
timeout: Some(60),
safe: true,
},
Command {
name: "Test".to_string(),
command: "cargo test --all".to_string(),
working_dir: None,
env: vec![],
timeout: Some(120),
safe: true,
},
],
});
self.templates.insert(
ProjectType::Generic,
Template {
sections: vec![TemplateSection {
title: "Project Overview".to_string(),
content: "{project_name}\n{description}".to_string(),
variables: vec!["project_name".to_string(), "description".to_string()],
}],
default_commands: vec![],
},
);
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn generate_from_analysis(&self, analysis: &PmatAnalysis) -> Result<String> {
let _template = self
.templates
.get(&analysis.project_type)
.or_else(|| self.templates.get(&ProjectType::Generic))
.context("No template found")?;
let mut output = String::new();
writeln!(output, "# AGENTS.md")?;
writeln!(output)?;
writeln!(output, "## Project Overview")?;
writeln!(
output,
"{}\n{}",
analysis.project_name, analysis.description
)?;
writeln!(output)?;
self.generate_dev_setup(&mut output, analysis)?;
self.generate_testing(&mut output, analysis)?;
if self.config.include_quality {
self.generate_code_style(&mut output, analysis)?;
}
if self.config.include_pr_guidelines {
self.generate_pr_guidelines(&mut output)?;
}
if self.config.include_security {
self.generate_security(&mut output)?;
}
Ok(output)
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn generate_from_project(&self, project: &ProjectInfo) -> Result<String> {
let analysis = PmatAnalysis {
project_name: project.name.clone(),
description: project.description.clone(),
project_type: self.detect_project_type(&project.root)?,
commands: self.detect_commands(&project.root)?,
quality_metrics: QualityMetrics {
avg_complexity: 10.0,
test_coverage: 80.0,
satd_count: 0,
grade: "A".to_string(),
},
dependencies: vec![],
};
self.generate_from_analysis(&analysis)
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn update_existing(&self, current: &str, updates: Updates) -> Result<String> {
let parser = super::parser::AgentsMdParser::new();
let mut doc = parser.parse(current)?;
for cmd in updates.new_commands {
if !doc.commands.iter().any(|c| c.name == cmd.name) {
doc.commands.push(cmd);
}
}
if let Some(rules) = updates.quality_rules {
doc.quality_rules = Some(rules);
}
for section in updates.new_sections {
if !doc.sections.iter().any(|s| s.title == section.title) {
doc.sections.push(section);
}
}
self.format_document(&doc)
}
fn format_document(&self, doc: &AgentsMdDocument) -> Result<String> {
let mut output = String::new();
writeln!(output, "# AGENTS.md")?;
writeln!(output)?;
for section in &doc.sections {
self.format_section(&mut output, section, 2)?;
}
Ok(output)
}
#[allow(clippy::only_used_in_recursion)]
fn format_section(&self, output: &mut String, section: &Section, level: usize) -> Result<()> {
let heading = "#".repeat(level);
writeln!(output, "{} {}", heading, section.title)?;
writeln!(output, "{}", section.content)?;
writeln!(output)?;
for subsection in §ion.subsections {
self.format_section(output, subsection, level + 1)?;
}
Ok(())
}
fn generate_dev_setup(&self, output: &mut String, analysis: &PmatAnalysis) -> Result<()> {
writeln!(output, "## Development Setup")?;
writeln!(output, "```bash")?;
let commands = &analysis.commands[..analysis.commands.len().min(5)];
for cmd in commands {
writeln!(output, "# {}", cmd.name)?;
writeln!(output, "{}", cmd.command)?;
writeln!(output)?;
}
writeln!(output, "```")?;
writeln!(output)?;
Ok(())
}
fn generate_testing(&self, output: &mut String, analysis: &PmatAnalysis) -> Result<()> {
writeln!(output, "## Testing Instructions")?;
writeln!(output, "- Run tests before committing")?;
writeln!(
output,
"- Ensure {}%+ coverage maintained",
analysis.quality_metrics.test_coverage as u32
)?;
writeln!(output, "- All functions must have complexity ≤{}", 10)?;
writeln!(output)?;
Ok(())
}
fn generate_code_style(&self, output: &mut String, analysis: &PmatAnalysis) -> Result<()> {
writeln!(output, "## Code Style")?;
writeln!(output, "- Follow project coding standards")?;
writeln!(
output,
"- Current quality grade: {}",
analysis.quality_metrics.grade
)?;
writeln!(output, "- Zero SATD tolerance")?;
writeln!(output)?;
Ok(())
}
fn generate_pr_guidelines(&self, output: &mut String) -> Result<()> {
writeln!(output, "## PR Guidelines")?;
writeln!(output, "- Squash commits with conventional format")?;
writeln!(output, "- Must pass all quality gates")?;
writeln!(output, "- Include tests for new features")?;
writeln!(output)?;
Ok(())
}
fn generate_security(&self, output: &mut String) -> Result<()> {
writeln!(output, "## Security Considerations")?;
writeln!(output, "- No secrets in code")?;
writeln!(output, "- Validate all external input")?;
writeln!(output, "- Use secure defaults")?;
writeln!(output)?;
Ok(())
}
fn detect_project_type(&self, path: &Path) -> Result<ProjectType> {
if path.join("Cargo.toml").exists() {
Ok(ProjectType::Rust)
} else if path.join("package.json").exists() {
Ok(ProjectType::JavaScript)
} else if path.join("setup.py").exists() || path.join("pyproject.toml").exists() {
Ok(ProjectType::Python)
} else if path.join("go.mod").exists() {
Ok(ProjectType::Go)
} else if path.join("pom.xml").exists() || path.join("build.gradle").exists() {
Ok(ProjectType::Java)
} else {
Ok(ProjectType::Generic)
}
}
fn detect_commands(&self, path: &Path) -> Result<Vec<Command>> {
let mut commands = Vec::new();
if path.join("Makefile").exists() {
commands.push(Command {
name: "Build".to_string(),
command: "make build".to_string(),
working_dir: None,
env: vec![],
timeout: Some(60),
safe: true,
});
commands.push(Command {
name: "Test".to_string(),
command: "make test".to_string(),
working_dir: None,
env: vec![],
timeout: Some(120),
safe: true,
});
}
if path.join("package.json").exists() {
commands.push(Command {
name: "Install".to_string(),
command: "npm install".to_string(),
working_dir: None,
env: vec![],
timeout: Some(300),
safe: true,
});
}
Ok(commands)
}
}
include!("generator_tests.rs");