Class: Norn::Skill
- Inherits:
-
Object
- Object
- Norn::Skill
- Defined in:
- lib/norn/skill.rb
Instance Attribute Summary collapse
-
#argument_hint ⇒ Object
readonly
Returns the value of attribute argument_hint.
-
#description ⇒ Object
readonly
Returns the value of attribute description.
-
#instructions ⇒ Object
readonly
Returns the value of attribute instructions.
-
#invocable ⇒ Object
readonly
Returns the value of attribute invocable.
-
#location ⇒ Object
readonly
Returns the value of attribute location.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#triggers ⇒ Object
readonly
Returns the value of attribute triggers.
Class Method Summary collapse
Instance Method Summary collapse
-
#base_directory ⇒ Object
Return the parent directory of the skill file.
-
#initialize(name:, description:, triggers:, invocable:, argument_hint:, instructions:, location:) ⇒ Skill
constructor
A new instance of Skill.
-
#matches?(text) ⇒ Boolean
Checks if a given text string matches any of the skill's triggers.
-
#resources ⇒ Object
Scan the base directory for supporting resources (all files excluding SKILL.md itself).
Constructor Details
#initialize(name:, description:, triggers:, invocable:, argument_hint:, instructions:, location:) ⇒ Skill
Returns a new instance of Skill.
7 8 9 10 11 12 13 14 15 |
# File 'lib/norn/skill.rb', line 7 def initialize(name:, description:, triggers:, invocable:, argument_hint:, instructions:, location:) @name = name.to_s.strip @description = description.to_s.strip @triggers = Array(triggers).map { |t| t.to_s.strip.downcase } @invocable = !!invocable @argument_hint = argument_hint ? argument_hint.to_s.strip : nil @instructions = instructions.to_s.strip @location = location ? File.(location) : nil end |
Instance Attribute Details
#argument_hint ⇒ Object (readonly)
Returns the value of attribute argument_hint.
5 6 7 |
# File 'lib/norn/skill.rb', line 5 def argument_hint @argument_hint end |
#description ⇒ Object (readonly)
Returns the value of attribute description.
5 6 7 |
# File 'lib/norn/skill.rb', line 5 def description @description end |
#instructions ⇒ Object (readonly)
Returns the value of attribute instructions.
5 6 7 |
# File 'lib/norn/skill.rb', line 5 def instructions @instructions end |
#invocable ⇒ Object (readonly)
Returns the value of attribute invocable.
5 6 7 |
# File 'lib/norn/skill.rb', line 5 def invocable @invocable end |
#location ⇒ Object (readonly)
Returns the value of attribute location.
5 6 7 |
# File 'lib/norn/skill.rb', line 5 def location @location end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
5 6 7 |
# File 'lib/norn/skill.rb', line 5 def name @name end |
#triggers ⇒ Object (readonly)
Returns the value of attribute triggers.
5 6 7 |
# File 'lib/norn/skill.rb', line 5 def triggers @triggers end |
Class Method Details
.parse_content(content, filepath = nil) ⇒ Object
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/norn/skill.rb', line 51 def parse_content(content, filepath = nil) # Match YAML frontmatter between --- and --- match = content.match(/\A---\s*\n(.*?)\n---\s*\n(.*)/m) unless match return nil end frontmatter_raw = match[1] body = match[2].strip frontmatter = nil begin frontmatter = YAML.safe_load(frontmatter_raw, permitted_classes: [Symbol, Date, Time]) rescue => e # Retry with sanitized YAML begin sanitized = sanitize_malformed_yaml(frontmatter_raw) frontmatter = YAML.safe_load(sanitized, permitted_classes: [Symbol, Date, Time]) rescue => e2 warn "Norn Skill Parser Error: Failed to parse frontmatter YAML at #{filepath || 'string'}: #{e2.}" return nil end end return nil unless frontmatter.is_a?(Hash) # Normalize keys (symbolize, downcase, substitute hyphens) normalized_fm = {} frontmatter.each do |k, v| norm_key = k.to_s.downcase.gsub("-", "_").to_sym normalized_fm[norm_key] = v end # Validation name = normalized_fm[:name]&.to_s&.strip if (name.nil? || name.empty?) && filepath name = File.basename(File.dirname(filepath)) end if name.nil? || name.empty? warn "Norn Skill Parser Warning: Skill is missing a 'name' field." return nil end description = normalized_fm[:description]&.to_s&.strip if description.nil? || description.empty? warn "Norn Skill Parser Warning: Skill '#{name}' is missing a 'description' field." return nil end # Check directory name matches if filepath parent_dir_name = File.basename(File.dirname(filepath)) if name != parent_dir_name warn "Norn Skill Parser Warning: Skill name '#{name}' does not match its parent directory name '#{parent_dir_name}'." end end new( name: name, description: description, triggers: normalized_fm[:triggers] || [], invocable: normalized_fm[:invocable], argument_hint: normalized_fm[:argument_hint] || normalized_fm[:argument_hint_hint], # handle various spellings instructions: body, location: filepath ) end |
.parse_file(filepath) ⇒ Object
45 46 47 48 49 |
# File 'lib/norn/skill.rb', line 45 def parse_file(filepath) return nil unless File.exist?(filepath) content = File.read(filepath) parse_content(content, filepath) end |
Instance Method Details
#base_directory ⇒ Object
Return the parent directory of the skill file
18 19 20 21 |
# File 'lib/norn/skill.rb', line 18 def base_directory return nil unless @location File.dirname(@location) end |
#matches?(text) ⇒ Boolean
Checks if a given text string matches any of the skill's triggers
37 38 39 40 41 42 |
# File 'lib/norn/skill.rb', line 37 def matches?(text) normalized_text = text.to_s.downcase @triggers.any? do |trigger| normalized_text.include?(trigger) end end |
#resources ⇒ Object
Scan the base directory for supporting resources (all files excluding SKILL.md itself)
24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/norn/skill.rb', line 24 def resources dir = base_directory return [] unless dir && Dir.exist?(dir) # Find all files recursively under the skill directory Dir.glob(File.join(dir, "**", "*"), File::FNM_DOTMATCH) .select { |f| File.file?(f) } .map { |f| File.(f) } .reject { |f| File.basename(f) == "SKILL.md" } .map { |f| Pathname.new(f).relative_path_from(Pathname.new(dir)).to_s } end |