agent_rules_tool/
format.rs1use clap::ValueEnum;
4use serde_json::Value;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
8pub enum RuleFormatArg {
9 Agents,
11 Cursor,
13 Windsurf,
15 Copilot,
17 Cline,
19 Claude,
21 Jetbrains,
23 Amazonq,
25 Auto,
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub enum RuleFormat {
32 Agents,
34 Cursor,
36 Windsurf,
38 Copilot,
40 Cline,
42 Claude,
44 Jetbrains,
46 AmazonQ,
48 Auto,
50}
51
52impl From<RuleFormatArg> for RuleFormat {
53 fn from(arg: RuleFormatArg) -> Self {
54 match arg {
55 RuleFormatArg::Agents => RuleFormat::Agents,
56 RuleFormatArg::Cursor => RuleFormat::Cursor,
57 RuleFormatArg::Windsurf => RuleFormat::Windsurf,
58 RuleFormatArg::Copilot => RuleFormat::Copilot,
59 RuleFormatArg::Cline => RuleFormat::Cline,
60 RuleFormatArg::Claude => RuleFormat::Claude,
61 RuleFormatArg::Jetbrains => RuleFormat::Jetbrains,
62 RuleFormatArg::Amazonq => RuleFormat::AmazonQ,
63 RuleFormatArg::Auto => RuleFormat::Auto,
64 }
65 }
66}
67
68impl RuleFormat {
69 pub fn default_output_dir(self) -> &'static str {
71 match self {
72 RuleFormat::Agents => ".agents/rules",
73 RuleFormat::Cursor => ".cursor/rules",
74 RuleFormat::Windsurf => ".windsurf/rules",
75 RuleFormat::Copilot => ".github/instructions",
76 RuleFormat::Cline => ".clinerules",
77 RuleFormat::Claude => ".claude/rules",
78 RuleFormat::Jetbrains => ".aiassistant/rules",
79 RuleFormat::AmazonQ => ".amazonq/rules",
80 RuleFormat::Auto => ".agents/rules",
81 }
82 }
83
84 pub fn detect_from_frontmatter(frontmatter: &Value) -> RuleFormat {
86 if frontmatter.is_null() {
87 return RuleFormat::Agents;
88 }
89 let obj = match frontmatter.as_object() {
90 Some(o) if !o.is_empty() => o,
91 _ => return RuleFormat::Agents,
92 };
93
94 if obj.contains_key("applyTo") {
95 return RuleFormat::Copilot;
96 }
97
98 if obj.contains_key("globs") {
99 if obj.contains_key("alwaysApply") {
100 return RuleFormat::Cursor;
101 }
102 if let Some(trigger) = obj.get("trigger").and_then(|v| v.as_str())
103 && matches!(trigger, "glob" | "always_on" | "manual" | "model_decision")
104 {
105 return RuleFormat::Windsurf;
106 }
107 return RuleFormat::Cursor;
108 }
109
110 if obj.contains_key("paths") {
111 if let Some(trigger) = obj.get("trigger").and_then(|v| v.as_str())
112 && matches!(trigger, "always" | "auto" | "manual")
113 {
114 return RuleFormat::Agents;
115 }
116 return RuleFormat::Claude;
117 }
118
119 if obj.contains_key("trigger") {
120 return RuleFormat::Agents;
121 }
122
123 RuleFormat::Agents
124 }
125
126 pub fn resolve(self, frontmatter: &Value) -> RuleFormat {
128 if self == RuleFormat::Auto {
129 Self::detect_from_frontmatter(frontmatter)
130 } else {
131 self
132 }
133 }
134}