Class: Pikuri::Tool::SkillCatalog
- Inherits:
-
Object
- Object
- Pikuri::Tool::SkillCatalog
- Defined in:
- lib/pikuri/tool/skill_catalog.rb
Overview
Discovery + validation of skills — Markdown files with YAML frontmatter that the agent loads on demand via Skill.
A skill is a directory containing a SKILL.md file. The frontmatter declares the skill’s name and description; everything in the directory (helper scripts, reference notes, templates) is treated as sidecar content the LLM can pull in via the regular read tool after loading the skill. Pikuri implements the Agent Skills standard leniently — invalid frontmatter is warned about but the skill still loads, except a missing description which is fatal (no description means the LLM has no signal for when to invoke it).
Catalog vs. tool
SkillCatalog owns the discovery layer: where skills live, how the frontmatter is parsed, what wins on a name collision. The Skill tool is the loading layer: it receives a catalog at construction and looks names up against it when the LLM invokes skill. The two are paired by Agent — see Agent#initialize for the auto-registration rule (non-empty catalog ⇒ Tool::Skill is appended to the tool list and the catalog’s prompt block is appended to the system prompt).
The seam
SkillCatalog is an abstract base with two bundled implementations: Empty (the EMPTY singleton, used by pikuri-chat and as the default for any caller that doesn’t have a filesystem story) and Bundled (the on-disk scanner). The seam lets future hosts (e.g. an in-editor pikuri reading skills from a virtual filesystem, or a synthetic test catalog) swap in without touching Skill or Agent.
Defined Under Namespace
Classes: Bundled, Empty, Skill
Constant Summary collapse
- MAX_NAME_LENGTH =
Frontmatter
namecap per the Agent Skills standard. 64- MAX_DESCRIPTION_LENGTH =
Frontmatter
descriptioncap per the Agent Skills standard. 1024- SKILL_DIR_NAMES =
The three skill directories pikuri scans, in precedence order.
.pikurifirst (the user’s own pikuri-specific skills),.claudesecond (so existing Claude Code skills travel along for free),.agentslast (the cross-harness convention PI also honors). Same names are used for both global roots (under ~/) and project roots (relative to CWD, walked up). ['.pikuri/skills', '.claude/skills', '.agents/skills'].freeze
Instance Method Summary collapse
-
#empty? ⇒ Boolean
True when #list is empty.
-
#format_for_prompt ⇒ String
System-prompt block advertising every loaded skill to the LLM.
-
#get(name) ⇒ Skill?
Look up a skill by name.
-
#list ⇒ Array<Skill>
Skills available to the LLM, in stable insertion order (which equals the order they were discovered, which equals precedence).
Instance Method Details
#empty? ⇒ Boolean
93 94 95 |
# File 'lib/pikuri/tool/skill_catalog.rb', line 93 def empty? list.empty? end |
#format_for_prompt ⇒ String
System-prompt block advertising every loaded skill to the LLM. Follows the Agent Skills standard’s XML shape (PI uses the same one) so a skill folder authored against any compliant harness renders consistently here.
Returns “” for an empty catalog so callers can unconditionally concatenate without an if. The leading nn separates the block from whatever the caller’s static system prompt ends with.
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/pikuri/tool/skill_catalog.rb', line 107 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?
Look up a skill by name.
86 87 88 |
# File 'lib/pikuri/tool/skill_catalog.rb', line 86 def get(name) raise NotImplementedError, "#{self.class}#get must be implemented" end |
#list ⇒ Array<Skill>
Skills available to the LLM, in stable insertion order (which equals the order they were discovered, which equals precedence).
77 78 79 |
# File 'lib/pikuri/tool/skill_catalog.rb', line 77 def list raise NotImplementedError, "#{self.class}#list must be implemented" end |