1use serde::{Deserialize, Serialize};
7use std::fmt;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11#[serde(rename_all = "kebab-case")]
12pub enum Tool {
13 Cursor,
14 ClaudeCode,
15 Copilot,
16 Continue,
17 Windsurf,
18 Cline,
19 Aider,
20 Generic,
21}
22
23impl Tool {
24 pub fn all() -> &'static [Tool] {
26 &[
27 Tool::Cursor,
28 Tool::ClaudeCode,
29 Tool::Copilot,
30 Tool::Continue,
31 Tool::Windsurf,
32 Tool::Cline,
33 Tool::Aider,
34 Tool::Generic,
35 ]
36 }
37
38 pub fn output_path(&self) -> &'static str {
40 match self {
41 Tool::Cursor => ".cursorrules",
42 Tool::ClaudeCode => "CLAUDE.md",
43 Tool::Copilot => ".github/copilot-instructions.md",
44 Tool::Continue => ".continue/config.json",
45 Tool::Windsurf => ".windsurfrules",
46 Tool::Cline => ".clinerules",
47 Tool::Aider => ".aider.conf.yml",
48 Tool::Generic => "AGENTS.md",
49 }
50 }
51
52 pub fn name(&self) -> &'static str {
54 match self {
55 Tool::Cursor => "Cursor",
56 Tool::ClaudeCode => "Claude Code",
57 Tool::Copilot => "GitHub Copilot",
58 Tool::Continue => "Continue.dev",
59 Tool::Windsurf => "Windsurf",
60 Tool::Cline => "Cline",
61 Tool::Aider => "Aider",
62 Tool::Generic => "Generic (AGENTS.md)",
63 }
64 }
65
66 pub fn supports_mcp(&self) -> bool {
68 matches!(self, Tool::ClaudeCode | Tool::Continue | Tool::Cline)
69 }
70
71 pub fn format(&self) -> OutputFormat {
73 match self {
74 Tool::Continue => OutputFormat::Json,
75 Tool::Aider => OutputFormat::Yaml,
76 _ => OutputFormat::Markdown,
77 }
78 }
79
80 pub fn from_name(name: &str) -> Option<Tool> {
82 match name.to_lowercase().as_str() {
83 "cursor" => Some(Tool::Cursor),
84 "claude-code" | "claudecode" | "claude" => Some(Tool::ClaudeCode),
85 "copilot" | "github-copilot" => Some(Tool::Copilot),
86 "continue" | "continue-dev" => Some(Tool::Continue),
87 "windsurf" => Some(Tool::Windsurf),
88 "cline" => Some(Tool::Cline),
89 "aider" => Some(Tool::Aider),
90 "generic" | "agents" => Some(Tool::Generic),
91 _ => None,
92 }
93 }
94}
95
96impl fmt::Display for Tool {
97 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98 write!(f, "{}", self.name())
99 }
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq)]
104pub enum OutputFormat {
105 Markdown,
106 Json,
107 Yaml,
108}
109
110#[derive(Debug, Clone, Copy, PartialEq, Eq)]
112pub enum MergeStrategy {
113 Replace,
115 Section,
117 Append,
119 Merge,
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn test_tool_output_paths() {
129 assert_eq!(Tool::Cursor.output_path(), ".cursorrules");
130 assert_eq!(Tool::ClaudeCode.output_path(), "CLAUDE.md");
131 assert_eq!(
132 Tool::Copilot.output_path(),
133 ".github/copilot-instructions.md"
134 );
135 assert_eq!(Tool::Generic.output_path(), "AGENTS.md");
136 }
137
138 #[test]
139 fn test_tool_from_name() {
140 assert_eq!(Tool::from_name("cursor"), Some(Tool::Cursor));
141 assert_eq!(Tool::from_name("Claude-Code"), Some(Tool::ClaudeCode));
142 assert_eq!(Tool::from_name("unknown"), None);
143 }
144
145 #[test]
146 fn test_tool_mcp_support() {
147 assert!(Tool::ClaudeCode.supports_mcp());
148 assert!(Tool::Continue.supports_mcp());
149 assert!(!Tool::Cursor.supports_mcp());
150 }
151}