diff --git a/Example/.gitignore b/Example/.gitignore index ec9c289..e5a3e07 100644 --- a/Example/.gitignore +++ b/Example/.gitignore @@ -1,13 +1,14 @@ # AI Rules - Generated Files **/.agents/commands/*-ai-rules.md -**/.agents/skills/ai-rules-generated-*/ +**/.agents/skills/ai-rules-generated-* **/.claude/commands/ai-rules/ -**/.claude/skills/ai-rules-generated-*/ -**/.codex/skills/ai-rules-generated-*/ +**/.claude/skills/ai-rules-generated-* +**/.codex/skills/ai-rules-generated-* **/.cursor/commands/ai-rules/ **/.cursor/mcp.json **/.cursor/rules/ +**/.firebender/skills/ai-rules-generated-* **/.gemini/settings.json **/.mcp.json **/.roo/mcp.json diff --git a/Example/ai-rules/skills/stars-we-prefer/SKILL.md b/Example/ai-rules/skills/stars-we-prefer/SKILL.md new file mode 100644 index 0000000..25c05a9 --- /dev/null +++ b/Example/ai-rules/skills/stars-we-prefer/SKILL.md @@ -0,0 +1,19 @@ +--- +name: stars-we-prefer +description: Our preferred stars for celestial navigation and astronomical reference +--- + +# Stars We Prefer + +When referencing stars in this project, these are our 10 preferred choices: + +1. **Polaris** - The North Star, essential for navigation and finding true north +2. **Sirius** - The brightest star in the night sky, located in Canis Major +3. **Betelgeuse** - A red supergiant in Orion, easily recognizable by its reddish hue +4. **Vega** - One of the brightest stars visible from Earth, part of the Summer Triangle +5. **Rigel** - A blue supergiant in Orion, contrasting beautifully with Betelgeuse +6. **Arcturus** - The brightest star in the northern celestial hemisphere +7. **Capella** - A bright star in Auriga, actually a system of four stars +8. **Aldebaran** - The red eye of Taurus, a giant star with an orange-red glow +9. **Antares** - The heart of Scorpius, a red supergiant rivaling Mars in color +10. **Deneb** - Part of the Summer Triangle, marking the tail of Cygnus the Swan diff --git a/src/agents/claude.rs b/src/agents/claude.rs index d1d55ee..89b8890 100644 --- a/src/agents/claude.rs +++ b/src/agents/claude.rs @@ -120,7 +120,7 @@ impl AgentRuleGenerator for ClaudeGenerator { fn gitignore_patterns(&self) -> Vec { let mut patterns = vec![self.output_filename.clone()]; if self.skills_mode { - patterns.push(format!("{}/{}*/", CLAUDE_SKILLS_DIR, GENERATED_FILE_PREFIX)); + patterns.push(format!("{}/{}*", CLAUDE_SKILLS_DIR, GENERATED_FILE_PREFIX)); } patterns } @@ -188,7 +188,7 @@ mod tests { assert_eq!(patterns.len(), 2); assert!(patterns.contains(&"CLAUDE.md".to_string())); - assert!(patterns.contains(&".claude/skills/ai-rules-generated-*/".to_string())); + assert!(patterns.contains(&".claude/skills/ai-rules-generated-*".to_string())); } #[test] diff --git a/src/agents/external_skills_generator.rs b/src/agents/external_skills_generator.rs index 1292c03..7b270d6 100644 --- a/src/agents/external_skills_generator.rs +++ b/src/agents/external_skills_generator.rs @@ -147,7 +147,7 @@ mod tests { fn test_external_skills_generator_gitignore_patterns() { let generator = ExternalSkillsGenerator::new(".claude/skills"); let patterns = generator.skills_gitignore_patterns(); - assert_eq!(patterns, vec![".claude/skills/ai-rules-generated-*/"]); + assert_eq!(patterns, vec![".claude/skills/ai-rules-generated-*"]); } #[test] @@ -156,21 +156,21 @@ mod tests { let claude_gen = ExternalSkillsGenerator::new(".claude/skills"); assert_eq!( claude_gen.skills_gitignore_patterns(), - vec![".claude/skills/ai-rules-generated-*/"] + vec![".claude/skills/ai-rules-generated-*"] ); // Test Codex target let codex_gen = ExternalSkillsGenerator::new(".codex/skills"); assert_eq!( codex_gen.skills_gitignore_patterns(), - vec![".codex/skills/ai-rules-generated-*/"] + vec![".codex/skills/ai-rules-generated-*"] ); // Test AMP target let amp_gen = ExternalSkillsGenerator::new(".agents/skills"); assert_eq!( amp_gen.skills_gitignore_patterns(), - vec![".agents/skills/ai-rules-generated-*/"] + vec![".agents/skills/ai-rules-generated-*"] ); } } diff --git a/src/agents/firebender.rs b/src/agents/firebender.rs index d8ddf96..5449e23 100644 --- a/src/agents/firebender.rs +++ b/src/agents/firebender.rs @@ -1,9 +1,12 @@ //! Firebender agent implementation for generating firebender.json configuration files. +use crate::agents::external_skills_generator::ExternalSkillsGenerator; use crate::agents::rule_generator::AgentRuleGenerator; +use crate::agents::skills_generator::SkillsGeneratorTrait; use crate::constants::{ AGENTS_MD_FILENAME, AI_RULE_SOURCE_DIR, FIREBENDER_JSON, FIREBENDER_OVERLAY_JSON, - FIREBENDER_USE_CURSOR_RULES_FIELD, MCP_SERVERS_FIELD, OPTIONAL_RULES_FILENAME, + FIREBENDER_SKILLS_DIR, FIREBENDER_USE_CURSOR_RULES_FIELD, MCP_SERVERS_FIELD, + OPTIONAL_RULES_FILENAME, }; use crate::models::SourceFile; use crate::operations::body_generator::generated_body_file_reference_path; @@ -114,6 +117,12 @@ impl AgentRuleGenerator for FirebenderGenerator { Ok(vec![firebender_path]) } + + fn skills_generator(&self) -> Option> { + Some(Box::new(ExternalSkillsGenerator::new( + FIREBENDER_SKILLS_DIR, + ))) + } } /// Generates `firebender.json`, merging the optional overlay if present. @@ -1001,4 +1010,25 @@ Create a git commit with proper formatting."#; "ai-rules/commands/commit.md" ); } + + #[test] + fn test_firebender_has_skills_generator() { + let generator = FirebenderGenerator; + assert!(generator.skills_generator().is_some()); + } + + #[test] + fn test_firebender_skills_target_dir() { + let generator = FirebenderGenerator; + let skills_gen = generator.skills_generator().unwrap(); + assert_eq!(skills_gen.skills_target_dir(), ".firebender/skills"); + } + + #[test] + fn test_firebender_skills_gitignore_patterns() { + let generator = FirebenderGenerator; + let skills_gen = generator.skills_generator().unwrap(); + let patterns = skills_gen.skills_gitignore_patterns(); + assert_eq!(patterns, vec![".firebender/skills/ai-rules-generated-*"]); + } } diff --git a/src/constants.rs b/src/constants.rs index 43022df..c1d8726 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -13,6 +13,7 @@ pub const CLAUDE_SKILLS_DIR: &str = ".claude/skills"; pub const CODEX_SKILLS_DIR: &str = ".codex/skills"; #[allow(dead_code)] pub const AMP_SKILLS_DIR: &str = ".agents/skills"; +pub const FIREBENDER_SKILLS_DIR: &str = ".firebender/skills"; pub const SKILL_FILENAME: &str = "SKILL.md"; pub const SKILLS_DIR: &str = "skills"; diff --git a/src/operations/skills_reader.rs b/src/operations/skills_reader.rs index cc585ef..db10bd4 100644 --- a/src/operations/skills_reader.rs +++ b/src/operations/skills_reader.rs @@ -215,7 +215,7 @@ pub fn check_skill_symlinks_in_sync(current_dir: &Path, target_dir: &str) -> Res /// Returns gitignore patterns for generated skill symlinks #[allow(dead_code)] pub fn get_skill_gitignore_patterns(target_dir: &str) -> Vec { - vec![format!("{}/{}*/", target_dir, GENERATED_FILE_PREFIX)] + vec![format!("{}/{}*", target_dir, GENERATED_FILE_PREFIX)] } #[cfg(test)] @@ -427,7 +427,7 @@ mod tests { fn test_get_skill_gitignore_patterns() { let patterns = get_skill_gitignore_patterns(".claude/skills"); assert_eq!(patterns.len(), 1); - assert_eq!(patterns[0], ".claude/skills/ai-rules-generated-*/"); + assert_eq!(patterns[0], ".claude/skills/ai-rules-generated-*"); } #[test]