Skip to main content

aster/rules/
applier.rs

1//! 规则应用器
2//!
3//! 应用自定义规则到内容
4
5use regex::Regex;
6
7use super::types::{CustomRule, ProjectRules, RuleAction, RuleApplyResult};
8
9/// 应用自定义规则到内容
10pub fn apply_rules(content: &str, rules: &[CustomRule]) -> RuleApplyResult {
11    let mut result = content.to_string();
12    let mut warnings = Vec::new();
13    let mut blocked = false;
14
15    for rule in rules {
16        let pattern = match &rule.pattern {
17            Some(p) => p,
18            None => continue,
19        };
20
21        let regex = match Regex::new(pattern) {
22            Ok(r) => r,
23            Err(_) => continue, // 无效正则,跳过
24        };
25
26        if regex.is_match(content) {
27            match rule.action {
28                RuleAction::Deny => {
29                    blocked = true;
30                    warnings.push(format!(
31                        "Blocked by rule \"{}\": {}",
32                        rule.name,
33                        rule.message.as_deref().unwrap_or("No message")
34                    ));
35                }
36                RuleAction::Warn => {
37                    warnings.push(format!(
38                        "Warning from rule \"{}\": {}",
39                        rule.name,
40                        rule.message.as_deref().unwrap_or("No message")
41                    ));
42                }
43                RuleAction::Transform => {
44                    if let Some(ref transform) = rule.transform {
45                        result = regex.replace_all(&result, transform.as_str()).to_string();
46                    }
47                }
48                RuleAction::Allow => {
49                    // 无需操作
50                }
51            }
52        }
53    }
54
55    RuleApplyResult {
56        result,
57        warnings,
58        blocked,
59    }
60}
61
62/// 从规则生成系统提示词附加内容
63pub fn generate_system_prompt_addition(rules: &ProjectRules) -> String {
64    let mut parts = Vec::new();
65
66    if let Some(ref instructions) = rules.instructions {
67        parts.push("## Project Instructions\n".to_string());
68        parts.push(instructions.clone());
69        parts.push(String::new());
70    }
71
72    if let Some(ref memory) = rules.memory {
73        if !memory.is_empty() {
74            parts.push("## Project Context\n".to_string());
75            for (key, value) in memory {
76                parts.push(format!("- **{}**: {}", key, value));
77            }
78            parts.push(String::new());
79        }
80    }
81
82    if let Some(ref custom_rules) = rules.custom_rules {
83        if !custom_rules.is_empty() {
84            parts.push("## Custom Rules\n".to_string());
85            for rule in custom_rules {
86                parts.push(format!(
87                    "- **{}** ({:?}): {}",
88                    rule.name,
89                    rule.action,
90                    rule.message.as_deref().unwrap_or("No description")
91                ));
92            }
93            parts.push(String::new());
94        }
95    }
96
97    parts.join("\n")
98}
99
100/// 创建默认 AGENTS.md 模板
101pub fn create_agents_md_template() -> String {
102    r#"# Project Instructions
103
104Add your project-specific instructions here. The agent will follow these when working on your codebase.
105
106## Guidelines
107
108- Describe your coding style preferences
109- List important conventions
110- Mention key architecture decisions
111
112## Memory
113
114- **Project Type**: (e.g., Web App, CLI Tool, Library)
115- **Language**: (e.g., TypeScript, Python, Rust)
116- **Framework**: (e.g., React, Express, Actix)
117
118## Allowed Tools
119
120- Read
121- Write
122- Edit
123- Bash
124- Glob
125- Grep
126
127## Rules
128
129- **No Console Logs**: Avoid adding console.log statements in production code
130- **Test Coverage**: All new features should include tests
131"#
132    .to_string()
133}
134
135/// 在目录中初始化 AGENTS.md
136pub fn init_agents_md(dir: Option<&std::path::Path>) -> Result<std::path::PathBuf, String> {
137    let target_dir = dir
138        .map(|p| p.to_path_buf())
139        .unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
140
141    let file_path = target_dir.join("AGENTS.md");
142
143    if file_path.exists() {
144        return Err("AGENTS.md already exists".to_string());
145    }
146
147    let template = create_agents_md_template();
148    std::fs::write(&file_path, template).map_err(|e| format!("Failed to write file: {}", e))?;
149
150    Ok(file_path)
151}