Class: Pikuri::Skill::Catalog

Inherits:
Object
  • Object
show all
Defined in:
lib/pikuri/skill/catalog.rb

Overview

Discovery + validation of skills — Markdown files with YAML frontmatter that the agent loads on demand via SkillTool.

A skill is a directory containing a SKILL.md whose frontmatter declares name and description. Sibling files (scripts, references, templates) are sidecar content the LLM pulls in via the regular read tool against File.dirname(location). Pikuri implements the Agent Skills standard leniently — only a missing description is fatal.

Two concrete subclasses ship: Empty (the EMPTY singleton) and Bundled (the on-disk scanner). Hosts that read skills from something other than the filesystem (virtual FS, hardcoded registry) subclass directly.

Direct Known Subclasses

Bundled, Empty

Defined Under Namespace

Classes: Bundled, Empty, Skill

Constant Summary collapse

MAX_NAME_LENGTH =

Frontmatter name cap per the Agent Skills standard.

64
MAX_DESCRIPTION_LENGTH =

Frontmatter description cap per the Agent Skills standard.

1024

Instance Method Summary collapse

Instance Method Details

#empty?Boolean

Returns true when #list is empty. Extension keys its auto-wiring off this — empty catalog ⇒ no surface change.

Returns:

  • (Boolean)

    true when #list is empty. Extension keys its auto-wiring off this — empty catalog ⇒ no surface change.



51
52
53
# File 'lib/pikuri/skill/catalog.rb', line 51

def empty?
  list.empty?
end

#format_for_promptString

System-prompt block advertising every loaded skill to the LLM, in the Agent Skills standard’s XML shape. Returns “” for an empty catalog so callers can unconditionally concatenate.

Returns:

  • (String)


70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/pikuri/skill/catalog.rb', line 70

def format_for_prompt
  return '' if empty?

  lines = [
    '',
    '',
    'The following skills provide specialized instructions for specific tasks.',
    "Use the `skill` tool with the skill's name to load its full instructions.",
    '',
    '<available_skills>'
  ]
  list.each do |skill|
    lines << '  <skill>'
    lines << "    <name>#{escape_xml(skill.name)}</name>"
    lines << "    <description>#{escape_xml(skill.description)}</description>"
    lines << "    <location>#{escape_xml(skill.location)}</location>"
    lines << '  </skill>'
  end
  lines << '</available_skills>'
  lines.join("\n")
end

#get(name) ⇒ Skill?

Parameters:

  • name (String)

Returns:

Raises:

  • (NotImplementedError)

    in the abstract base



45
46
47
# File 'lib/pikuri/skill/catalog.rb', line 45

def get(name)
  raise NotImplementedError, "#{self.class}#get must be implemented"
end

#listArray<Skill>

Returns skills in discovery order (which equals precedence order).

Returns:

  • (Array<Skill>)

    skills in discovery order (which equals precedence order).

Raises:

  • (NotImplementedError)

    in the abstract base



38
39
40
# File 'lib/pikuri/skill/catalog.rb', line 38

def list
  raise NotImplementedError, "#{self.class}#list must be implemented"
end

#rootsArray<String>

Absolute paths of the skill directories this catalog covers. Hosts wire these into Pikuri::Workspace::Filesystem‘s readable: list so the LLM can read sidecar files.

Returns:

  • (Array<String>)

Raises:

  • (NotImplementedError)

    in the abstract base



61
62
63
# File 'lib/pikuri/skill/catalog.rb', line 61

def roots
  raise NotImplementedError, "#{self.class}#roots must be implemented"
end