use serde::Serialize;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum TargetId {
AgentsMd,
ClaudeMd,
CursorRules,
GeminiMd,
CopilotInstructions,
Clinerules,
WindsurfRules,
CopilotInstructionsScoped,
JunieGuidelines,
}
impl TargetId {
pub const ALL: &'static [TargetId] = &[
TargetId::AgentsMd,
TargetId::ClaudeMd,
TargetId::CursorRules,
TargetId::GeminiMd,
TargetId::CopilotInstructions,
TargetId::Clinerules,
TargetId::WindsurfRules,
TargetId::CopilotInstructionsScoped,
TargetId::JunieGuidelines,
];
#[must_use]
pub fn id(self) -> &'static str {
match self {
TargetId::AgentsMd => "agents-md",
TargetId::ClaudeMd => "claude-md",
TargetId::CursorRules => "cursor-rules",
TargetId::GeminiMd => "gemini-md",
TargetId::CopilotInstructions => "copilot-instructions",
TargetId::Clinerules => "clinerules",
TargetId::WindsurfRules => "windsurf-rules",
TargetId::CopilotInstructionsScoped => "copilot-instructions-scoped",
TargetId::JunieGuidelines => "junie-guidelines",
}
}
#[must_use]
pub fn from_id(id: &str) -> Option<Self> {
match id {
"agents-md" => Some(TargetId::AgentsMd),
"claude-md" => Some(TargetId::ClaudeMd),
"cursor-rules" => Some(TargetId::CursorRules),
"gemini-md" => Some(TargetId::GeminiMd),
"copilot-instructions" => Some(TargetId::CopilotInstructions),
"clinerules" => Some(TargetId::Clinerules),
"windsurf-rules" => Some(TargetId::WindsurfRules),
"copilot-instructions-scoped" => Some(TargetId::CopilotInstructionsScoped),
"junie-guidelines" => Some(TargetId::JunieGuidelines),
_ => None,
}
}
#[must_use]
pub fn label(self) -> &'static str {
match self {
TargetId::AgentsMd => "AGENTS.md",
TargetId::ClaudeMd => "CLAUDE.md",
TargetId::CursorRules => ".cursor/rules/",
TargetId::GeminiMd => "GEMINI.md",
TargetId::CopilotInstructions => ".github/copilot-instructions.md",
TargetId::Clinerules => ".clinerules/",
TargetId::WindsurfRules => ".windsurf/rules/",
TargetId::CopilotInstructionsScoped => ".github/instructions/",
TargetId::JunieGuidelines => ".junie/guidelines.md",
}
}
#[must_use]
pub fn primary_path(self) -> &'static str {
match self {
TargetId::AgentsMd => "AGENTS.md",
TargetId::ClaudeMd => "CLAUDE.md",
TargetId::CursorRules => ".cursor/rules/default.mdc",
TargetId::GeminiMd => "GEMINI.md",
TargetId::CopilotInstructions => ".github/copilot-instructions.md",
TargetId::Clinerules => ".clinerules/default.md",
TargetId::WindsurfRules => ".windsurf/rules/default.md",
TargetId::CopilotInstructionsScoped => ".github/instructions/default.md",
TargetId::JunieGuidelines => ".junie/guidelines.md",
}
}
#[must_use]
pub fn generated_glob(self) -> &'static str {
match self {
TargetId::AgentsMd => "AGENTS.md",
TargetId::ClaudeMd => "CLAUDE.md",
TargetId::CursorRules => ".cursor/rules/**",
TargetId::GeminiMd => "GEMINI.md",
TargetId::CopilotInstructions => ".github/copilot-instructions.md",
TargetId::Clinerules => ".clinerules/**",
TargetId::WindsurfRules => ".windsurf/rules/**",
TargetId::CopilotInstructionsScoped => ".github/instructions/**",
TargetId::JunieGuidelines => ".junie/guidelines.md",
}
}
#[must_use]
pub fn tier(self) -> Tier {
match self {
TargetId::AgentsMd
| TargetId::ClaudeMd
| TargetId::CursorRules
| TargetId::GeminiMd
| TargetId::CopilotInstructions => Tier::Stable,
TargetId::Clinerules
| TargetId::WindsurfRules
| TargetId::CopilotInstructionsScoped
| TargetId::JunieGuidelines => Tier::Experimental,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum Tier {
Stable,
Experimental,
}
impl Tier {
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
Tier::Stable => "stable",
Tier::Experimental => "experimental",
}
}
}
#[allow(clippy::struct_excessive_bools)]
#[derive(Debug, Clone, Serialize)]
pub struct OutputTargets {
pub agents_md: bool,
pub claude_md: bool,
pub cursor_rules: bool,
pub gemini_md: bool,
pub copilot_instructions: bool,
pub clinerules: bool,
pub windsurf_rules: bool,
pub copilot_instructions_scoped: bool,
pub junie_guidelines: bool,
}
impl Default for OutputTargets {
fn default() -> Self {
Self {
agents_md: true,
claude_md: false,
cursor_rules: false,
gemini_md: false,
copilot_instructions: false,
clinerules: false,
windsurf_rules: false,
copilot_instructions_scoped: false,
junie_guidelines: false,
}
}
}
impl OutputTargets {
#[must_use]
pub fn is_empty(&self) -> bool {
!self.agents_md
&& !self.claude_md
&& !self.cursor_rules
&& !self.gemini_md
&& !self.copilot_instructions
&& !self.clinerules
&& !self.windsurf_rules
&& !self.copilot_instructions_scoped
&& !self.junie_guidelines
}
#[must_use]
pub fn enabled(&self) -> Vec<TargetId> {
let mut out = Vec::new();
if self.agents_md {
out.push(TargetId::AgentsMd);
}
if self.claude_md {
out.push(TargetId::ClaudeMd);
}
if self.cursor_rules {
out.push(TargetId::CursorRules);
}
if self.gemini_md {
out.push(TargetId::GeminiMd);
}
if self.copilot_instructions {
out.push(TargetId::CopilotInstructions);
}
if self.clinerules {
out.push(TargetId::Clinerules);
}
if self.windsurf_rules {
out.push(TargetId::WindsurfRules);
}
if self.copilot_instructions_scoped {
out.push(TargetId::CopilotInstructionsScoped);
}
if self.junie_guidelines {
out.push(TargetId::JunieGuidelines);
}
out
}
#[must_use]
pub fn from_targets(targets: &[TargetId]) -> Self {
let mut out = Self {
agents_md: false,
claude_md: false,
cursor_rules: false,
gemini_md: false,
copilot_instructions: false,
clinerules: false,
windsurf_rules: false,
copilot_instructions_scoped: false,
junie_guidelines: false,
};
for t in targets {
match t {
TargetId::AgentsMd => out.agents_md = true,
TargetId::ClaudeMd => out.claude_md = true,
TargetId::CursorRules => out.cursor_rules = true,
TargetId::GeminiMd => out.gemini_md = true,
TargetId::CopilotInstructions => out.copilot_instructions = true,
TargetId::Clinerules => out.clinerules = true,
TargetId::WindsurfRules => out.windsurf_rules = true,
TargetId::CopilotInstructionsScoped => out.copilot_instructions_scoped = true,
TargetId::JunieGuidelines => out.junie_guidelines = true,
}
}
out
}
}