Skip to content
Kward Search API index

Class: Kward::Skills::Registry

Inherits:
Object
  • Object
show all
Defined in:
lib/kward/skills/registry.rb

Overview

Parsed skill metadata and instruction path.

Defined Under Namespace

Classes: SkillSource

Instance Method Summary collapse

Constructor Details

#initialize(config_dir:, workspace_root:, project_skills_trusted:, skill_class:, max_file_bytes:, markdown_parser:, inside_directory:) ⇒ Registry

Returns a new instance of Registry.



11
12
13
14
15
16
17
18
19
# File 'lib/kward/skills/registry.rb', line 11

def initialize(config_dir:, workspace_root:, project_skills_trusted:, skill_class:, max_file_bytes:, markdown_parser:, inside_directory:)
  @config_dir = config_dir
  @workspace_root = workspace_root
  @project_skills_trusted = project_skills_trusted
  @skill_class = skill_class
  @max_file_bytes = max_file_bytes
  @markdown_parser = markdown_parser
  @inside_directory = inside_directory
end

Instance Method Details

#read_skill_file(name, relative_path = nil) ⇒ Object



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/kward/skills/registry.rb', line 42

def read_skill_file(name, relative_path = nil)
  skill = skills.find { |candidate| candidate.name == name.to_s }
  return "Error: unknown skill: #{name}" unless skill

  path = relative_path.to_s.empty? ? "SKILL.md" : relative_path.to_s
  return "Error: skill path must be relative" if Pathname.new(path).absolute?

  base = File.realpath(skill.folder)
  target = File.expand_path(path, base)
  real_target = File.realpath(target)
  unless @inside_directory.call(real_target, base)
    return "Error: skill path outside skill folder: #{path}"
  end
  return "Error: skill path is not a file: #{path}" unless File.file?(real_target)

  size = File.size(real_target)
  return "Error: skill file too large: #{path} (#{size} bytes)" if size > @max_file_bytes

  content = File.read(real_target)
  relative_path.to_s.empty? ? skill_content(skill, content) : content
rescue Errno::ENOENT
  "Error: skill file not found: #{path}"
rescue StandardError => e
  "Error: could not read skill file #{path}: #{e.message}"
end

#skillsObject



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/kward/skills/registry.rb', line 21

def skills
  seen = {}
  skill_sources.flat_map do |source|
    scan_source(source).filter_map do |path|
      skill = parse_skill(path)
      next unless skill

      if seen[skill.name]
        warn "Warning: skipping duplicate Kward skill #{skill.name.inspect}: #{path}"
        next
      end

      seen[skill.name] = true
      skill
    end
  end
rescue StandardError => e
  warn "Warning: skipping Kward skills: #{e.message}"
  []
end