Class: Pikuri::Tool::SkillCatalog::Bundled

Inherits:
Pikuri::Tool::SkillCatalog show all
Defined in:
lib/pikuri/tool/skill_catalog.rb

Overview

On-disk catalog: eager-scans the given roots at construction and freezes itself. Use Bundled.discover to build one against pikuri’s conventional locations; pass roots: directly in tests so collision and ordering behavior can be exercised without staging dirs under ~/.

Precedence

roots is processed left to right. The first skill seen for a given name wins; later occurrences are dropped with a warning logged through Pikuri.logger_for(‘Skills’). Callers that want “project beats global” pass project roots first.

Validation

The Agent Skills standard is enforced leniently. A missing description is the only hard failure (the skill is skipped); name/dir mismatch, oversized name/description, invalid name characters, malformed YAML — all log warnings and the skill still loads with whatever could be salvaged. Skills without a YAML frontmatter block at all are skipped silently (the file is presumably not meant as a skill).

Constant Summary

Constants inherited from Pikuri::Tool::SkillCatalog

MAX_DESCRIPTION_LENGTH, MAX_NAME_LENGTH, SKILL_DIR_NAMES

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Pikuri::Tool::SkillCatalog

#empty?, #format_for_prompt

Constructor Details

#initialize(roots:) ⇒ Bundled

Parameters:

  • roots (Array<String, Pathname>)

    skill directories in precedence order — first occurrence of a given skill name wins.



238
239
240
241
242
243
244
245
# File 'lib/pikuri/tool/skill_catalog.rb', line 238

def initialize(roots:)
  super()
  @skills = {}
  roots.each { |root| scan_root(root.to_s) }
  @skills.freeze
  @list = @skills.values.freeze
  freeze
end

Class Method Details

.discover(cwd: Dir.pwd) ⇒ Bundled

Build a catalog from pikuri’s conventional discovery roots: project skills (walked from cwd up to the git root or filesystem root) take precedence over global skills (under the user’s home directory). Within each tier, the order in SKILL_DIR_NAMES applies (.pikuri.claude.agents).

Non-existent directories are skipped silently; the loaded set is whatever existed at construction time.

Parameters:

  • cwd (String, Pathname) (defaults to: Dir.pwd)

    working directory whose ancestor chain is searched for project skill roots.

Returns:



192
193
194
# File 'lib/pikuri/tool/skill_catalog.rb', line 192

def self.discover(cwd: Dir.pwd)
  new(roots: discover_project_roots(cwd: cwd) + discover_global_roots)
end

.discover_global_rootsArray<String>

Global roots under the user’s home directory.

Returns:

  • (Array<String>)

    absolute paths to existing dirs



228
229
230
231
232
# File 'lib/pikuri/tool/skill_catalog.rb', line 228

def self.discover_global_roots
  home = Dir.home
  SKILL_DIR_NAMES.map { |sub| File.join(home, sub) }
                 .select { |dir| File.directory?(dir) }
end

.discover_project_roots(cwd:) ⇒ Array<String>

Project roots walked from cwd upwards, stopping at the nearest ancestor containing .git (inclusive) or at the filesystem root. The deepest directory’s roots appear first, so closer-to-CWD skills win on name collisions.

Parameters:

  • cwd (String, Pathname)

Returns:

  • (Array<String>)

    absolute paths to existing dirs



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/pikuri/tool/skill_catalog.rb', line 203

def self.discover_project_roots(cwd:)
  results = []
  current = File.expand_path(cwd.to_s)
  loop do
    SKILL_DIR_NAMES.each do |sub|
      candidate = File.join(current, sub)
      results << candidate if File.directory?(candidate)
    end

    # Stop *after* processing the git root — git roots are
    # natural project boundaries; ancestors above are someone
    # else's project.
    break if File.directory?(File.join(current, '.git'))

    parent = File.dirname(current)
    break if parent == current # filesystem root

    current = parent
  end
  results
end

Instance Method Details

#get(name) ⇒ Skill?

Parameters:

  • name (String)

Returns:



254
255
256
# File 'lib/pikuri/tool/skill_catalog.rb', line 254

def get(name)
  @skills[name]
end

#listArray<Skill>

Returns:



248
249
250
# File 'lib/pikuri/tool/skill_catalog.rb', line 248

def list
  @list
end