Module: Brute::Skill
- Defined in:
- lib/brute/skill.rb
Overview
Discovers and loads SKILL.md files from standard directories.
A skill is a markdown file with YAML frontmatter:
---
name: debugging
description: Systematic debugging workflow for isolating and fixing bugs
---
When debugging, follow these steps...
Skills are scanned from (in order):
1. .brute/skills/**/SKILL.md (project-local)
2. ~/.config/brute/skills/**/SKILL.md (global)
The directory name containing SKILL.md becomes the skill name if frontmatter doesn’t specify one.
Defined Under Namespace
Classes: Info
Constant Summary collapse
- FILENAME =
"SKILL.md"
Class Method Summary collapse
-
.all(cwd: Dir.pwd) ⇒ Object
Scan all skill directories and return an array of Info structs.
-
.fmt(skills) ⇒ Object
Format skills as XML for the system prompt.
-
.get(name, cwd: Dir.pwd) ⇒ Object
Get a single skill by name.
-
.load(path) ⇒ Object
Parse a SKILL.md file into an Info struct.
Class Method Details
.all(cwd: Dir.pwd) ⇒ Object
Scan all skill directories and return an array of Info structs.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/brute/skill.rb', line 30 def self.all(cwd: Dir.pwd) skills = {} scan_dirs(cwd).each do |dir| Dir.glob(File.join(dir, "**", FILENAME)).sort.each do |path| info = load(path) next unless info # First found wins (project-local overrides global) skills[info.name] ||= info end end skills.values.sort_by(&:name) end |
.fmt(skills) ⇒ Object
Format skills as XML for the system prompt.
51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/brute/skill.rb', line 51 def self.fmt(skills) return nil if skills.empty? lines = ["<available_skills>"] skills.each do |skill| lines << " <skill>" lines << " <name>#{skill.name}</name>" lines << " <description>#{skill.description}</description>" lines << " </skill>" end lines << "</available_skills>" lines.join("\n") end |
.get(name, cwd: Dir.pwd) ⇒ Object
Get a single skill by name.
46 47 48 |
# File 'lib/brute/skill.rb', line 46 def self.get(name, cwd: Dir.pwd) all(cwd: cwd).detect { |s| s.name == name } end |
.load(path) ⇒ Object
Parse a SKILL.md file into an Info struct. Returns nil if the file is invalid or missing required fields.
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/brute/skill.rb', line 67 def self.load(path) raw = File.read(path) frontmatter, content = parse_frontmatter(raw) return nil unless frontmatter name = frontmatter["name"] || File.basename(File.dirname(path)) description = frontmatter["description"] return nil unless description && !description.strip.empty? Info.new( name: name.to_s.strip, description: description.to_s.strip, location: path, content: content.to_s.strip, ) rescue => e warn "Failed to load skill #{path}: #{e.}" nil end |