Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions Example/.gitignore
Original file line number Diff line number Diff line change
@@ -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
Expand Down
19 changes: 19 additions & 0 deletions Example/ai-rules/skills/stars-we-prefer/SKILL.md
Original file line number Diff line number Diff line change
@@ -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
4 changes: 2 additions & 2 deletions src/agents/claude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ impl AgentRuleGenerator for ClaudeGenerator {
fn gitignore_patterns(&self) -> Vec<String> {
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
}
Expand Down Expand Up @@ -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]
Expand Down
8 changes: 4 additions & 4 deletions src/agents/external_skills_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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-*"]
);
}
}
32 changes: 31 additions & 1 deletion src/agents/firebender.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -114,6 +117,12 @@ impl AgentRuleGenerator for FirebenderGenerator {

Ok(vec![firebender_path])
}

fn skills_generator(&self) -> Option<Box<dyn SkillsGeneratorTrait>> {
Some(Box::new(ExternalSkillsGenerator::new(
FIREBENDER_SKILLS_DIR,
)))
}
}

/// Generates `firebender.json`, merging the optional overlay if present.
Expand Down Expand Up @@ -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-*"]);
}
}
1 change: 1 addition & 0 deletions src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down
4 changes: 2 additions & 2 deletions src/operations/skills_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> {
vec![format!("{}/{}*/", target_dir, GENERATED_FILE_PREFIX)]
vec![format!("{}/{}*", target_dir, GENERATED_FILE_PREFIX)]
}

#[cfg(test)]
Expand Down Expand Up @@ -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]
Expand Down