const REQUIRED_PATTERNS: &[&str] = &["pmat query", "NEVER use grep", "--faults"];
const FORBIDDEN_PATTERNS: &[&str] = &[
"grep -r",
"grep -rn",
"find . -name",
"find . -type f",
"rg \"",
"rg '",
];
fn check_index_age(index_path: &Path) -> (Option<f64>, bool) {
let manifest_path = index_path.join("manifest.json");
let check_path = if manifest_path.exists() {
&manifest_path
} else {
index_path
};
let metadata = match fs::metadata(check_path) {
Ok(m) => m,
Err(_) => return (None, false),
};
let modified = match metadata.modified() {
Ok(m) => m,
Err(_) => return (None, false),
};
let age = std::time::SystemTime::now()
.duration_since(modified)
.unwrap_or_default();
let hours = age.as_secs_f64() / 3600.0;
(Some(hours), hours > 24.0)
}
fn is_negative_example(line: &str) -> bool {
let lower = line.to_lowercase();
lower.contains("bad")
|| lower.contains("don't")
|| lower.contains("never")
|| lower.contains("avoid")
}
fn find_forbidden_patterns(content: &str) -> Vec<ForbiddenPatternMatch> {
let mut forbidden = Vec::new();
for (line_num, line) in content.lines().enumerate() {
for &pattern in FORBIDDEN_PATTERNS {
if line.contains(pattern) && !is_negative_example(line) {
forbidden.push(ForbiddenPatternMatch {
pattern: pattern.to_string(),
line: line_num + 1,
context: line.chars().take(80).collect(),
});
}
}
}
forbidden
}
fn check_claude_md_patterns(
project_path: &Path,
) -> (bool, Vec<String>, Vec<ForbiddenPatternMatch>) {
let claude_md_path = project_path.join("CLAUDE.md");
let content = match fs::read_to_string(&claude_md_path) {
Ok(c) => c,
Err(_) => {
return (
false,
REQUIRED_PATTERNS.iter().map(|s| s.to_string()).collect(),
vec![],
);
}
};
let configured = content.contains("pmat_query_code") || content.contains("pmat query");
let missing: Vec<String> = REQUIRED_PATTERNS
.iter()
.filter(|&p| !content.to_lowercase().contains(&p.to_lowercase()))
.map(|s| s.to_string())
.collect();
let forbidden = find_forbidden_patterns(&content);
(configured, missing, forbidden)
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub fn detect_cb130_agent_context_adoption(project_path: &Path) -> AgentContextReport {
let index_path = project_path.join(".pmat/context.idx");
let db_path = project_path.join(".pmat/context.db");
let index_exists = index_path.exists() || db_path.exists();
let age_check_path = if db_path.exists() {
&db_path
} else {
&index_path
};
let (index_age_hours, index_stale) = if index_exists {
check_index_age(age_check_path)
} else {
(None, false)
};
let function_count = if index_exists {
match crate::services::agent_context::AgentContextIndex::load(&index_path) {
Ok(idx) => idx.manifest().function_count,
Err(_) => 0,
}
} else {
0
};
let (claude_md_configured, missing_required_patterns, forbidden_patterns_found) =
check_claude_md_patterns(project_path);
AgentContextReport {
index_exists,
index_age_hours,
index_stale,
function_count,
claude_md_configured,
missing_required_patterns,
forbidden_patterns_found,
}
}