Class: Pikuri::Tool::Skill

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

Overview

The skill tool: instantiating Tool::Skill.new(catalog:) produces a tool whose execute closure looks the requested name up in the bound SkillCatalog and returns the skill’s body wrapped in a <skill> block with the absolute base directory, so the LLM can resolve relative sidecar paths against it via the regular read tool.

The catalog of available skills is not duplicated into this tool’s description. It lives in the system prompt — see Pikuri::Tool::SkillCatalog#format_for_prompt — because skills describe what the agent is, not just what one of its tools returns. Putting the catalog in the system prompt keeps it next to the agent’s persona and matches what PI and Claude Code do (opencode is the outlier here). The tool description below is therefore static — it explains the load mechanism, not the inventory.

This tool is not registered manually. Agent#initialize auto- registers it whenever skill_catalog is non-empty, and skips it otherwise; sub-agents inherit it through the parent’s tool snapshot. The bound catalog rides along in the execute closure, so any sub-agent invoking skill resolves names against the same catalog the parent saw.

Constant Summary collapse

DESCRIPTION =

Description shown to the LLM. Follows the opencode-shape (summary + Usage: bullets) prescribed by the project’s tool-description convention. The list of which skills exist is not here — see the class header.

Returns:

  • (String)
<<~DESC
  Load a specialized skill that provides domain-specific instructions and resources for a particular task.

  Usage:
  - The catalog of available skills is listed in your system prompt under `<available_skills>`.
  - Invoke this tool with a skill's `name` to inject its full instructions into the conversation.
  - The loaded skill may reference helper scripts and files in its base directory — use the `read` tool to load those when the skill's instructions tell you to.
  - On `Error: skill '...' not found`, do NOT retry with a guessed name. Pick a name that actually appears in `<available_skills>`, or report to the user that no matching skill is installed.
DESC

Constants inherited from Pikuri::Tool

CALCULATOR, FETCH, WEB_SCRAPE, WEB_SEARCH

Instance Attribute Summary

Attributes inherited from Pikuri::Tool

#description, #execute, #name, #parameters

Instance Method Summary collapse

Methods inherited from Pikuri::Tool

#run, #to_ruby_llm_tool

Constructor Details

#initialize(catalog:) ⇒ Skill

Parameters:

  • catalog (SkillCatalog)

    the catalog to resolve names against. Captured by closure so the tool retains access to the same instance even when copied into a sub-agent’s tool snapshot.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/pikuri/tool/skill.rb', line 49

def initialize(catalog:)
  super(
    name: 'skill',
    description: DESCRIPTION,
    parameters: Parameters.build { |p|
      p.required_string :name,
                        'Name of the skill to load, e.g. "pdf-extraction". ' \
                        'Must match a name listed under `<available_skills>` ' \
                        'in the system prompt.'
    },
    execute: lambda { |name:|
      loaded = catalog.get(name)
      if loaded.nil?
        available = catalog.list.map(&:name).sort
        list = available.empty? ? 'none' : available.join(', ')
        next "Error: skill '#{name}' not found. Available skills: #{list}."
      end

      base_dir = File.dirname(loaded.location)
      <<~OUT
        <skill name="#{loaded.name}" location="#{loaded.location}">
        Sidecar paths in this skill (e.g. scripts/, references/) are relative to #{base_dir}.

        #{loaded.body}
        </skill>
      OUT
    }
  )
end