ruby_llm-skills 0.3.0 → 0.4.0.pre1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -0
- data/README.md +6 -3
- data/lib/ruby_llm/skills/agent_extensions.rb +19 -18
- data/lib/ruby_llm/skills/chat_extensions.rb +4 -9
- data/lib/ruby_llm/skills/skill_tool.rb +14 -14
- data/lib/ruby_llm/skills/source_detection.rb +25 -0
- data/lib/ruby_llm/skills/version.rb +1 -1
- data/lib/ruby_llm/skills.rb +0 -45
- metadata +19 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ebbccd1655baa8f141a020e2a32fdad03f8c6ddc7cd8929cb6b35cee8e4fc488
|
|
4
|
+
data.tar.gz: b1d175a5fc1ec6c7603318a42fdb34d44f25680da95fa21a2b3f042621644641
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1b39b6966015406944f68897ed6170bce39cbd6f36ad6d363e650a1dbfc160a99a532e728b1e599d3b5243a4323db6c24220e79c20f796efa3c714a45126140d
|
|
7
|
+
data.tar.gz: eb71f38a6faa4c75faf675f9c6ce7ac7ae835db82a1a58c1d1c5c2b5ec5511426fc7c5e770ec91e1ad1019e8868c81a49ef65ae254e8cf4256e66f0348c87d41
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.4.0.pre1] - 2026-09-10
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- **Breaking:** requires RubyLLM 2.0 (`ruby_llm >= 2.0.0.rc2, < 3`); 1.x users should stay on 0.3.x
|
|
13
|
+
- `SkillTool` declares parameters with the 2.0 `parameter`/`description:` DSL, exposes `parameters_schema`, and sets its name through `tool_name`
|
|
14
|
+
- `Chat#with_skills` registers the skill tool via `Chat#with_tools` (2.0 removed `Chat#with_tool`)
|
|
15
|
+
- `AgentExtensions` hooks the 2.0 `Agent.apply_configuration(chat, input_values:, persist_instructions:)` signature and applies skills to the chat or `acts_as_chat` record it receives
|
|
16
|
+
- Integration tests pin `openai_protocol = :chat_completions` and match VCR cassettes on method and URI so the recorded 1.x interactions replay under 2.0
|
|
17
|
+
|
|
18
|
+
### Removed
|
|
19
|
+
|
|
20
|
+
- Global `Module#delegate` fallback and its compatibility test (2.0's `Agent` uses `Forwardable`)
|
|
21
|
+
- Dependency on the removed `Agent.llm_chat_for` hook
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- StandardRB `Layout/EmptyLinesAroundModuleBody` offense in `chat_extensions.rb` that failed CI
|
|
26
|
+
|
|
8
27
|
## [0.3.0] - 2026-02-17
|
|
9
28
|
|
|
10
29
|
### Added
|
data/README.md
CHANGED
|
@@ -9,9 +9,12 @@ Agent Skills for [RubyLLM](https://github.com/crmne/ruby_llm). Teach your AI how
|
|
|
9
9
|
## Installation
|
|
10
10
|
|
|
11
11
|
```ruby
|
|
12
|
-
gem "ruby_llm
|
|
12
|
+
gem "ruby_llm", "2.0.0.rc2"
|
|
13
|
+
gem "ruby_llm-skills", "0.4.0.pre1"
|
|
13
14
|
```
|
|
14
15
|
|
|
16
|
+
Requires RubyLLM 2.0 (`>= 2.0.0.rc2`). If you are still on RubyLLM 1.x, stay on `ruby_llm-skills ~> 0.3.0` and follow the [RubyLLM 2.0 upgrade guide](https://rubyllm.com/next/upgrading/) before upgrading both gems together.
|
|
17
|
+
|
|
15
18
|
## Quick Start
|
|
16
19
|
|
|
17
20
|
```ruby
|
|
@@ -31,7 +34,7 @@ chat.with_skills("app/skills", "app/commands") # multiple paths
|
|
|
31
34
|
chat.with_skills("app/skills", user.skills) # with database records
|
|
32
35
|
```
|
|
33
36
|
|
|
34
|
-
### With RubyLLM::Agent
|
|
37
|
+
### With RubyLLM::Agent
|
|
35
38
|
|
|
36
39
|
```ruby
|
|
37
40
|
class SupportAgent < RubyLLM::Agent
|
|
@@ -127,7 +130,7 @@ bin/setup
|
|
|
127
130
|
### Running Tests
|
|
128
131
|
|
|
129
132
|
```bash
|
|
130
|
-
bundle exec rake test # Unit tests (
|
|
133
|
+
bundle exec rake test # Unit tests (177 tests)
|
|
131
134
|
bundle exec rake test_rails # Rails integration tests (25+ tests)
|
|
132
135
|
bundle exec rake test_all # Both
|
|
133
136
|
bundle exec rake # Tests + linting
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "source_detection"
|
|
4
|
+
|
|
3
5
|
module RubyLLM
|
|
4
6
|
module Skills
|
|
5
7
|
# Extensions for RubyLLM::Agent to enable declarative skill configuration.
|
|
@@ -16,9 +18,11 @@ module RubyLLM
|
|
|
16
18
|
# end
|
|
17
19
|
#
|
|
18
20
|
module AgentExtensions
|
|
19
|
-
REQUIRED_AGENT_SINGLETON_METHODS = %i[apply_configuration runtime_context
|
|
21
|
+
REQUIRED_AGENT_SINGLETON_METHODS = %i[apply_configuration runtime_context].freeze
|
|
20
22
|
|
|
21
23
|
module ClassMethods
|
|
24
|
+
include SourceDetection
|
|
25
|
+
|
|
22
26
|
def self.extended(base)
|
|
23
27
|
base.instance_variable_set(:@skill_sources, nil)
|
|
24
28
|
base.instance_variable_set(:@skill_only, nil)
|
|
@@ -60,19 +64,11 @@ module RubyLLM
|
|
|
60
64
|
return [] if source.nil?
|
|
61
65
|
return [source] if source.is_a?(String)
|
|
62
66
|
return [source] if loader_source?(source)
|
|
63
|
-
return [source] if database_collection_source?(source)
|
|
67
|
+
return source.empty? ? [] : [source] if database_collection_source?(source)
|
|
64
68
|
return source.flat_map { |item| flatten_skill_sources(item) } if source.is_a?(Array)
|
|
65
69
|
|
|
66
70
|
[source]
|
|
67
71
|
end
|
|
68
|
-
|
|
69
|
-
def loader_source?(source)
|
|
70
|
-
source.respond_to?(:list) && source.respond_to?(:find)
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def database_collection_source?(source)
|
|
74
|
-
source.respond_to?(:to_a) && source.first&.respond_to?(:name) && source.first.respond_to?(:content)
|
|
75
|
-
end
|
|
76
72
|
end
|
|
77
73
|
|
|
78
74
|
module InstanceMethods
|
|
@@ -88,16 +84,21 @@ module RubyLLM
|
|
|
88
84
|
end
|
|
89
85
|
|
|
90
86
|
module ConfigurationPatch
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
87
|
+
# RubyLLM 2.0 passes either a RubyLLM::Chat or an acts_as_chat record.
|
|
88
|
+
# Both respond to #with_skills (ChatExtensions / ActiveRecordExtensions),
|
|
89
|
+
# so skills apply directly to whatever the agent configured.
|
|
90
|
+
#
|
|
91
|
+
# apply_configuration is a :nodoc: hook; **options forwards any keyword a
|
|
92
|
+
# later 2.x release adds so the patch fails at super, not before it.
|
|
93
|
+
def apply_configuration(chat, input_values: {}, **options)
|
|
94
94
|
super
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
apply_skills(llm_chat_for(chat_object), runtime)
|
|
95
|
+
runtime = runtime_context(chat: chat, inputs: input_values)
|
|
96
|
+
apply_skills(chat, runtime)
|
|
98
97
|
end
|
|
99
98
|
|
|
100
|
-
|
|
99
|
+
private
|
|
100
|
+
|
|
101
|
+
def apply_skills(chat, runtime)
|
|
101
102
|
config = skills
|
|
102
103
|
sources = config[:sources]
|
|
103
104
|
return if sources.nil?
|
|
@@ -112,7 +113,7 @@ module RubyLLM
|
|
|
112
113
|
return if normalized_sources.empty?
|
|
113
114
|
|
|
114
115
|
validate_skill_sources!(normalized_sources)
|
|
115
|
-
|
|
116
|
+
chat.with_skills(*normalized_sources, only: config[:only])
|
|
116
117
|
end
|
|
117
118
|
|
|
118
119
|
def validate_skill_sources!(sources)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "source_detection"
|
|
3
4
|
require_relative "skill_tool"
|
|
4
5
|
|
|
5
6
|
module RubyLLM
|
|
@@ -19,6 +20,8 @@ module RubyLLM
|
|
|
19
20
|
# chat.with_skills(only: [:pdf_report])
|
|
20
21
|
#
|
|
21
22
|
module ChatExtensions
|
|
23
|
+
include SourceDetection
|
|
24
|
+
|
|
22
25
|
# Add skills to this chat.
|
|
23
26
|
#
|
|
24
27
|
# @param sources [Array] skill sources - auto-detects type (directory, zip, collection)
|
|
@@ -32,7 +35,7 @@ module RubyLLM
|
|
|
32
35
|
loader = FilteredLoader.new(loader, only) if only
|
|
33
36
|
|
|
34
37
|
skill_tool = RubyLLM::Skills::SkillTool.new(loader)
|
|
35
|
-
|
|
38
|
+
with_tools(skill_tool)
|
|
36
39
|
end
|
|
37
40
|
|
|
38
41
|
private
|
|
@@ -50,14 +53,6 @@ module RubyLLM
|
|
|
50
53
|
"Invalid skill source: #{source.class}. Expected String path, Loader, or record collection."
|
|
51
54
|
end
|
|
52
55
|
end
|
|
53
|
-
|
|
54
|
-
def loader_source?(source)
|
|
55
|
-
source.respond_to?(:list) && source.respond_to?(:find)
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def database_collection_source?(source)
|
|
59
|
-
source.respond_to?(:to_a) && source.first&.respond_to?(:name) && source.first.respond_to?(:content)
|
|
60
|
-
end
|
|
61
56
|
end
|
|
62
57
|
|
|
63
58
|
# Simple wrapper that filters skills by name.
|
|
@@ -27,15 +27,22 @@ module RubyLLM
|
|
|
27
27
|
#
|
|
28
28
|
class SkillTool < RubyLLM::Tool
|
|
29
29
|
description "Execute a skill within the main conversation."
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
parameter :command, type: "string",
|
|
31
|
+
description: "The skill name (e.g., 'pdf' or 'write-poem')"
|
|
32
|
+
parameter :arguments, type: "string", required: false,
|
|
33
|
+
description: "Arguments passed after the command (e.g., '/write-poem about robots' passes 'about robots')"
|
|
34
|
+
parameter :resource, type: "string", required: false,
|
|
35
|
+
description: "Optional resource path to load (e.g., 'scripts/helper.rb', 'references/guide.md')"
|
|
36
36
|
|
|
37
37
|
attr_reader :loader
|
|
38
38
|
|
|
39
|
+
# Tool name for RubyLLM.
|
|
40
|
+
#
|
|
41
|
+
# @return [String] "skill"
|
|
42
|
+
def self.tool_name
|
|
43
|
+
"skill"
|
|
44
|
+
end
|
|
45
|
+
|
|
39
46
|
# Initialize with a skill loader.
|
|
40
47
|
#
|
|
41
48
|
# @param loader [Loader] any loader (FilesystemLoader, ZipLoader, etc.)
|
|
@@ -43,13 +50,6 @@ module RubyLLM
|
|
|
43
50
|
@loader = loader
|
|
44
51
|
end
|
|
45
52
|
|
|
46
|
-
# Tool name for RubyLLM.
|
|
47
|
-
#
|
|
48
|
-
# @return [String] "skill"
|
|
49
|
-
def name
|
|
50
|
-
"skill"
|
|
51
|
-
end
|
|
52
|
-
|
|
53
53
|
# Dynamic description including available skills.
|
|
54
54
|
#
|
|
55
55
|
# @return [String] tool description with embedded skill metadata
|
|
@@ -98,7 +98,7 @@ module RubyLLM
|
|
|
98
98
|
{
|
|
99
99
|
name: name,
|
|
100
100
|
description: description,
|
|
101
|
-
parameters:
|
|
101
|
+
parameters: parameters_schema
|
|
102
102
|
}
|
|
103
103
|
end
|
|
104
104
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module Skills
|
|
5
|
+
# Shared duck-typing predicates for classifying skill sources.
|
|
6
|
+
# Used by both AgentExtensions and ChatExtensions.
|
|
7
|
+
module SourceDetection
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def loader_source?(source)
|
|
11
|
+
source.respond_to?(:list) && source.respond_to?(:find)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def database_collection_source?(source)
|
|
15
|
+
return false unless source.respond_to?(:to_a)
|
|
16
|
+
|
|
17
|
+
# ActiveRecord relations/CollectionProxy: recognizable even when empty
|
|
18
|
+
return true if source.respond_to?(:klass) && source.respond_to?(:where_values_hash)
|
|
19
|
+
|
|
20
|
+
first = source.first
|
|
21
|
+
first&.respond_to?(:name) && first.respond_to?(:content)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
data/lib/ruby_llm/skills.rb
CHANGED
|
@@ -1,50 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# RubyLLM 1.12.0 Agent uses `delegate` but doesn't require ActiveSupport.
|
|
4
|
-
# Provide a minimal fallback for plain Ruby environments.
|
|
5
|
-
unless Module.method_defined?(:delegate)
|
|
6
|
-
class Module
|
|
7
|
-
def delegate(*methods, to:, prefix: nil, allow_nil: false, private: false, **_options)
|
|
8
|
-
target_method = to.to_sym
|
|
9
|
-
|
|
10
|
-
methods.each do |method_name|
|
|
11
|
-
delegated_method = delegated_method_name(method_name, target_method, prefix)
|
|
12
|
-
|
|
13
|
-
define_method(delegated_method) do |*args, **kwargs, &block|
|
|
14
|
-
target = public_send(target_method)
|
|
15
|
-
if target.nil?
|
|
16
|
-
return nil if allow_nil
|
|
17
|
-
|
|
18
|
-
raise NoMethodError,
|
|
19
|
-
"#{self.class}##{delegated_method} delegated to ##{target_method}, but ##{target_method} is nil"
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
if kwargs.empty?
|
|
23
|
-
target.public_send(method_name, *args, &block)
|
|
24
|
-
else
|
|
25
|
-
target.public_send(method_name, *args, **kwargs, &block)
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
private delegated_method if binding.local_variable_get(:private)
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
private
|
|
34
|
-
|
|
35
|
-
def delegated_method_name(method_name, target_method, prefix)
|
|
36
|
-
case prefix
|
|
37
|
-
when true
|
|
38
|
-
:"#{target_method}_#{method_name}"
|
|
39
|
-
when String, Symbol
|
|
40
|
-
:"#{prefix}_#{method_name}"
|
|
41
|
-
else
|
|
42
|
-
method_name
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
3
|
require "ruby_llm"
|
|
49
4
|
|
|
50
5
|
require_relative "skills/version"
|
metadata
CHANGED
|
@@ -1,28 +1,35 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby_llm-skills
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0.pre1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kieran Klaassen
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: exe
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
11
|
+
date: 2026-09-10 00:00:00.000000000 Z
|
|
11
12
|
dependencies:
|
|
12
13
|
- !ruby/object:Gem::Dependency
|
|
13
14
|
name: ruby_llm
|
|
14
15
|
requirement: !ruby/object:Gem::Requirement
|
|
15
16
|
requirements:
|
|
16
|
-
- - "
|
|
17
|
+
- - ">="
|
|
17
18
|
- !ruby/object:Gem::Version
|
|
18
|
-
version:
|
|
19
|
+
version: 2.0.0.rc2
|
|
20
|
+
- - "<"
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: '3'
|
|
19
23
|
type: :runtime
|
|
20
24
|
prerelease: false
|
|
21
25
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
26
|
requirements:
|
|
23
|
-
- - "
|
|
27
|
+
- - ">="
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: 2.0.0.rc2
|
|
30
|
+
- - "<"
|
|
24
31
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: '
|
|
32
|
+
version: '3'
|
|
26
33
|
description: Load, validate, and integrate Agent Skills with RubyLLM. Supports the
|
|
27
34
|
open Agent Skills specification for progressive skill discovery and loading from
|
|
28
35
|
filesystem, zip archives, and databases.
|
|
@@ -50,6 +57,7 @@ files:
|
|
|
50
57
|
- lib/ruby_llm/skills/railtie.rb
|
|
51
58
|
- lib/ruby_llm/skills/skill.rb
|
|
52
59
|
- lib/ruby_llm/skills/skill_tool.rb
|
|
60
|
+
- lib/ruby_llm/skills/source_detection.rb
|
|
53
61
|
- lib/ruby_llm/skills/tasks/skills.rake
|
|
54
62
|
- lib/ruby_llm/skills/validator.rb
|
|
55
63
|
- lib/ruby_llm/skills/version.rb
|
|
@@ -61,6 +69,7 @@ metadata:
|
|
|
61
69
|
source_code_uri: https://github.com/kieranklaassen/ruby_llm-skills
|
|
62
70
|
changelog_uri: https://github.com/kieranklaassen/ruby_llm-skills/blob/main/CHANGELOG.md
|
|
63
71
|
rubygems_mfa_required: 'true'
|
|
72
|
+
post_install_message:
|
|
64
73
|
rdoc_options: []
|
|
65
74
|
require_paths:
|
|
66
75
|
- lib
|
|
@@ -71,11 +80,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
71
80
|
version: 3.2.0
|
|
72
81
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
73
82
|
requirements:
|
|
74
|
-
- - "
|
|
83
|
+
- - ">"
|
|
75
84
|
- !ruby/object:Gem::Version
|
|
76
|
-
version:
|
|
85
|
+
version: 1.3.1
|
|
77
86
|
requirements: []
|
|
78
|
-
rubygems_version: 3.
|
|
87
|
+
rubygems_version: 3.4.10
|
|
88
|
+
signing_key:
|
|
79
89
|
specification_version: 4
|
|
80
90
|
summary: Agent Skills extension for RubyLLM
|
|
81
91
|
test_files: []
|