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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '048ac6f6a76a7fd48994d208799b967754b25ecc3959acd56d44f5ab707f9f8d'
4
- data.tar.gz: 268827da6d73359d9451b159b1abe68dea7881419df6b473cceeb2d8c74bc073
3
+ metadata.gz: ebbccd1655baa8f141a020e2a32fdad03f8c6ddc7cd8929cb6b35cee8e4fc488
4
+ data.tar.gz: b1d175a5fc1ec6c7603318a42fdb34d44f25680da95fa21a2b3f042621644641
5
5
  SHA512:
6
- metadata.gz: febe501ab666f7e1cdf06e043ab44305ea065ffe561508b6fdcb9a959a90eabad8d2cb9fcc90384536eaf0871f73f2e52c3c9e93b8d883fbe8823274f547f0f3
7
- data.tar.gz: 73226c3f1eb42ba6073dbfdcccb9f334896e078ca04ab1814260feb4218d432a83cf92817a32bb6c1abe847cf7fcd0098259aa2bab5886ce933d2c258d4ae8f1
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-skills"
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 (v1.12+)
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 (151 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 llm_chat_for].freeze
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
- private
92
-
93
- def apply_configuration(chat_object, **kwargs)
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
- input_values = kwargs[:input_values] || {}
96
- runtime = runtime_context(chat: chat_object, inputs: input_values)
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
- def apply_skills(llm_chat, runtime)
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
- llm_chat.with_skills(*normalized_sources, only: config[:only])
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
- with_tool(skill_tool)
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
- param :command, type: "string",
31
- desc: "The skill name (e.g., 'pdf' or 'write-poem')"
32
- param :arguments, type: "string", required: false,
33
- desc: "Arguments passed after the command (e.g., '/write-poem about robots' passes 'about robots')"
34
- param :resource, type: "string", required: false,
35
- desc: "Optional resource path to load (e.g., 'scripts/helper.rb', 'references/guide.md')"
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: params_schema
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RubyLLM
4
4
  module Skills
5
- VERSION = "0.3.0"
5
+ VERSION = "0.4.0.pre1"
6
6
  end
7
7
  end
@@ -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.3.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-02-17 00:00:00.000000000 Z
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: '1.12'
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: '1.12'
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: '0'
85
+ version: 1.3.1
77
86
  requirements: []
78
- rubygems_version: 3.6.2
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: []