Class: RubyLLM::Skills::SkillTool

Inherits:
Tool
  • Object
show all
Defined in:
lib/ruby_llm/skills/skill_tool.rb

Overview

A RubyLLM Tool that enables progressive skill loading.

This tool is the key integration point for LLM skill discovery. It embeds skill metadata (name + description) in its description, allowing the LLM to discover available skills. When invoked, it returns the full skill content.

Progressive Disclosure Pattern:

  1. Skill metadata is always visible in the tool description (~100 tokens/skill)
  2. Full skill content is loaded on-demand when the tool is called (~5k tokens)
  3. Resources (scripts, references) can be loaded separately as needed

Examples:

Basic usage

loader = RubyLLM::Skills.from_directory("app/skills")
skill_tool = RubyLLM::Skills::SkillTool.new(loader)

chat.with_tools(skill_tool)
chat.ask("Help me generate a PDF report")
# LLM sees available skills, calls skill_tool with name="pdf-report"
# Tool returns full SKILL.md content for LLM to follow

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(loader) ⇒ SkillTool

Initialize with a skill loader.

Parameters:

  • loader (Loader) —

    any loader (FilesystemLoader, ZipLoader, etc.)



42
43
44
# File 'lib/ruby_llm/skills/skill_tool.rb', line 42

def initialize(loader)
  @loader = loader
end

Instance Attribute Details

#loader ⇒ Object (readonly)

Returns the value of attribute loader.



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

def loader
  @loader
end

Instance Method Details

#description ⇒ String

Dynamic description including available skills.

Returns:

  • (String) —

    tool description with embedded skill metadata



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/ruby_llm/skills/skill_tool.rb', line 56

def description
  base_description = self.class.description
  skills_xml = build_skills_xml
  "    \#{base_description}\n\n    When to use this tool:\n    - When the user's message starts with \"/\" followed by a skill name (e.g., \"/write-poem about robots\"), invoke this tool with that command and pass any text after the command name as arguments\n    - When the user's request matches one of the available skills below\n\n    Call with command (and optional arguments) to get the full skill instructions.\n    Call with command and resource to load a specific file (script, reference, or asset).\n\n    \#{skills_xml}\n  DESC\nend\n".strip

#execute(command:, arguments: nil, resource: nil) ⇒ String

Execute the tool to load a skill's content or a specific resource.

Parameters:

  • command (String) —

    name of skill to load

  • arguments (String, nil) (defaults to: nil) —

    optional arguments passed with the command

  • resource (String, nil) (defaults to: nil) —

    optional resource path within the skill

Returns:

  • (String) —

    skill content, resource content, or error message



79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/ruby_llm/skills/skill_tool.rb', line 79

def execute(command:, arguments: nil, resource: nil)
  skill = @loader.find(command)

  unless skill
    available = @loader.list.map(&:name).join(", ")
    return "Skill '#{command}' not found. Available skills: #{available}"
  end

  if resource
    load_resource(skill, resource)
  else
    build_skill_response(skill, arguments: arguments)
  end
end

#name ⇒ String

Tool name for RubyLLM.

Returns:

  • (String) —

    "skill"



49
50
51
# File 'lib/ruby_llm/skills/skill_tool.rb', line 49

def name
  "skill"
end

#to_tool_definition ⇒ Hash

Convert to RubyLLM Tool-compatible format.

Returns:

  • (Hash) —

    tool definition for RubyLLM



97
98
99
100
101
102
103
# File 'lib/ruby_llm/skills/skill_tool.rb', line 97

def to_tool_definition
  {
    name: name,
    description: description,
    parameters: params_schema
  }
end