Module: Ask::Skills

Defined in:
lib/ask/skills.rb,
lib/ask/skills/skill.rb,
lib/ask/skills/version.rb,
lib/ask/skills/registry.rb,
lib/ask/skills/formatter.rb,
lib/ask/skills/validator.rb,
lib/ask/skills/sources/base.rb,
lib/ask/skills/sources/gems.rb,
lib/ask/skills/sources/filesystem.rb

Defined Under Namespace

Modules: Source Classes: Error, Formatter, Registry, Skill, Validator

Constant Summary collapse

VERSION =
"0.4.1"

Class Method Summary collapse

Class Method Details

.builtin_skills_dirObject

Built-in skills that ship with the gem.



37
38
39
# File 'lib/ask/skills.rb', line 37

def builtin_skills_dir
  File.join(__dir__, "skills")
end

.default_sourcesObject

Default sources when no custom sources or agent directory given. Legacy .agents/skills/ is kept for backward compatibility.



32
33
34
# File 'lib/ask/skills.rb', line 32

def default_sources
  build_source_list
end

.discover(sources: nil, agent_dir: nil) ⇒ Registry

Discover skills from all configured sources.

Parameters:

  • agent_dir (String, nil) (defaults to: nil)

    optional agent directory to discover per-agent skills from (e.g. "agents/health_check")

  • sources (Array<Source::Base>, nil) (defaults to: nil)

    custom source list

Returns:



25
26
27
28
# File 'lib/ask/skills.rb', line 25

def discover(sources: nil, agent_dir: nil)
  all_sources = sources || build_source_list(agent_dir: agent_dir)
  Registry.new(all_sources)
end

.extract_body(content) ⇒ Object

Extract the markdown body from a file with frontmatter.



95
96
97
98
99
100
101
# File 'lib/ask/skills.rb', line 95

def extract_body(content)
  return content unless content.start_with?("---\n")
  end_idx = content.index("\n---\n", 4)
  return content unless end_idx
  body = content[(end_idx + 5)..] || ""
  body.sub(/\A\n/, "").strip
end

.load_file(path) ⇒ Object

Load a skill from an arbitrary markdown file path.



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/ask/skills.rb', line 42

def load_file(path)
  path = File.expand_path(path)
  content = File.read(path)
  frontmatter = parse_frontmatter(content)
  body = extract_body(content)

  name = frontmatter["name"] || File.basename(path, ".md")
  description = frontmatter["description"] || "Ad-hoc skill loaded from #{File.basename(path)}"

  Skill.new(
    name: name,
    description: description,
    instructions: body.empty? ? content : body,
    source: path
  )
end

.parse_frontmatter(content) ⇒ Object

Simple frontmatter parsing for skill files.



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
# File 'lib/ask/skills.rb', line 60

def parse_frontmatter(content)
  return {} unless content.start_with?("---\n")
  end_idx = content.index("\n---\n", 4)
  return {} unless end_idx

  yaml_str = content[4...end_idx]
  yaml = {}
  current_key = nil
  current_value = nil

  yaml_str.split("\n").each do |line|
    if (m = line.match(/\A(\w[\w_]*):\s*(.*)\z/))
      if current_key
        yaml[current_key] = (current_value.strip)
      end
      current_key = m[1]
      current_value = m[2].strip
    elsif current_key && line.match?(/\A\s+/)
      current_value << " #{line.strip}"
    end
  end

  if current_key
    yaml[current_key] = (current_value.strip)
  end

  yaml
end

.process_metadata_value(value) ⇒ Object



89
90
91
92
# File 'lib/ask/skills.rb', line 89

def (value)
  return value unless value
  value.gsub(/\A"|"\z/, "").gsub(/\A'|'\z/, "")
end