Class: KairosMcp::Skillset
- Inherits:
-
Object
- Object
- KairosMcp::Skillset
- Defined in:
- lib/kairos_mcp/skillset.rb
Overview
Represents a single SkillSet plugin installed in .kairos/skillsets/#name/
A SkillSet is an independent package containing tools, libraries, knowledge, and configuration. The layer declaration determines governance policy (blockchain recording level, approval requirements, RAG indexing).
Constant Summary collapse
- REQUIRED_FIELDS =
%w[name version].freeze
- VALID_LAYERS =
%i[L0 L1 L2].freeze
- EXECUTABLE_EXTENSIONS =
%w[.rb .py .sh .js .ts .pl .lua .exe .so .dylib .dll .class .jar .wasm].freeze
Instance Attribute Summary collapse
-
#metadata ⇒ Object
readonly
Returns the value of attribute metadata.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#path ⇒ Object
readonly
Returns the value of attribute path.
Instance Method Summary collapse
-
#all_file_hashes ⇒ Object
Hash of each file for full blockchain recording (L0).
- #author ⇒ Object
- #config_files ⇒ Object
-
#content_hash ⇒ Object
Compute content hash of all files in the SkillSet.
- #default_layer ⇒ Object
- #depends_on ⇒ Object
-
#depends_on_with_versions ⇒ Object
Detailed dependency info with version constraints Each element: { name: String, version: String|nil }.
- #description ⇒ Object
-
#exchangeable? ⇒ Boolean
Only knowledge-only SkillSets are safe to exchange over the network.
-
#file_list ⇒ Object
Sorted list of relative file paths within the SkillSet.
- #has_knowledge? ⇒ Boolean
- #has_plugin? ⇒ Boolean
- #index_knowledge? ⇒ Boolean
-
#initialize(path) ⇒ Skillset
constructor
A new instance of Skillset.
- #knowledge_dir_names ⇒ Object
-
#knowledge_dirs ⇒ Object
Full path to knowledge directories.
-
#knowledge_only? ⇒ Boolean
True if the SkillSet contains no executable code (tools/ or lib/) Checks for executable extensions (.rb, .py, .sh, etc.) and shebang lines.
-
#layer ⇒ Object
Layer as symbol (:L0, :L1, :L2), with override support.
- #layer=(sym) ⇒ Object
-
#load! ⇒ Object
Load the SkillSet code (require lib/ and tools/).
- #loaded? ⇒ Boolean
-
#place_extensions ⇒ Object
Place extension declarations for PlaceRouter integration.
-
#plugin_config ⇒ Object
Plugin projection support: Claude Code plugin artifacts in plugin/ directory.
- #provides ⇒ Object
- #to_h ⇒ Object
- #tool_class_names ⇒ Object
- #valid? ⇒ Boolean
- #version ⇒ Object
-
#version_compatible? ⇒ Boolean
Check if the SkillSet is compatible with the current core version.
Constructor Details
#initialize(path) ⇒ Skillset
Returns a new instance of Skillset.
19 20 21 22 23 24 |
# File 'lib/kairos_mcp/skillset.rb', line 19 def initialize(path) @path = File.(path) @metadata = @name = @metadata['name'] @loaded = false end |
Instance Attribute Details
#metadata ⇒ Object (readonly)
Returns the value of attribute metadata.
17 18 19 |
# File 'lib/kairos_mcp/skillset.rb', line 17 def @metadata end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
17 18 19 |
# File 'lib/kairos_mcp/skillset.rb', line 17 def name @name end |
#path ⇒ Object (readonly)
Returns the value of attribute path.
17 18 19 |
# File 'lib/kairos_mcp/skillset.rb', line 17 def path @path end |
Instance Method Details
#all_file_hashes ⇒ Object
Hash of each file for full blockchain recording (L0)
148 149 150 151 152 153 154 155 |
# File 'lib/kairos_mcp/skillset.rb', line 148 def all_file_hashes hashes = {} Dir[File.join(@path, '**', '*')].select { |f| File.file?(f) }.sort.each do |file| relative = file.sub("#{@path}/", '') hashes[relative] = Digest::SHA256.hexdigest(File.read(file)) end hashes end |
#author ⇒ Object
51 52 53 |
# File 'lib/kairos_mcp/skillset.rb', line 51 def @metadata['author'] || '' end |
#config_files ⇒ Object
73 74 75 |
# File 'lib/kairos_mcp/skillset.rb', line 73 def config_files @metadata['config_files'] || [] end |
#content_hash ⇒ Object
Compute content hash of all files in the SkillSet
143 144 145 |
# File 'lib/kairos_mcp/skillset.rb', line 143 def content_hash Digest::SHA256.hexdigest(all_file_hashes.to_json) end |
#default_layer ⇒ Object
38 39 40 41 |
# File 'lib/kairos_mcp/skillset.rb', line 38 def default_layer raw = @metadata['layer'] || 'L1' raw.to_sym end |
#depends_on ⇒ Object
55 56 57 |
# File 'lib/kairos_mcp/skillset.rb', line 55 def depends_on parsed_depends_on.map { |d| d[:name] } end |
#depends_on_with_versions ⇒ Object
Detailed dependency info with version constraints Each element: { name: String, version: String|nil }
61 62 63 |
# File 'lib/kairos_mcp/skillset.rb', line 61 def depends_on_with_versions parsed_depends_on end |
#description ⇒ Object
47 48 49 |
# File 'lib/kairos_mcp/skillset.rb', line 47 def description @metadata['description'] || '' end |
#exchangeable? ⇒ Boolean
Only knowledge-only SkillSets are safe to exchange over the network
174 175 176 |
# File 'lib/kairos_mcp/skillset.rb', line 174 def exchangeable? knowledge_only? && valid? end |
#file_list ⇒ Object
Sorted list of relative file paths within the SkillSet
179 180 181 182 183 184 |
# File 'lib/kairos_mcp/skillset.rb', line 179 def file_list Dir[File.join(@path, '**', '*')] .select { |f| File.file?(f) } .map { |f| f.sub("#{@path}/", '') } .sort end |
#has_knowledge? ⇒ Boolean
127 128 129 |
# File 'lib/kairos_mcp/skillset.rb', line 127 def has_knowledge? !knowledge_dirs.empty? end |
#has_plugin? ⇒ Boolean
136 137 138 139 140 |
# File 'lib/kairos_mcp/skillset.rb', line 136 def has_plugin? return false unless plugin_config plugin_dir = File.join(@path, 'plugin') Dir.exist?(plugin_dir) end |
#index_knowledge? ⇒ Boolean
87 88 89 90 91 92 |
# File 'lib/kairos_mcp/skillset.rb', line 87 def index_knowledge? return @metadata['index_knowledge'] == true if @metadata.key?('index_knowledge') # Default: L0 and L1 are indexed, L2 is not %i[L0 L1].include?(layer) end |
#knowledge_dir_names ⇒ Object
77 78 79 |
# File 'lib/kairos_mcp/skillset.rb', line 77 def knowledge_dir_names @metadata['knowledge_dirs'] || [] end |
#knowledge_dirs ⇒ Object
Full path to knowledge directories
123 124 125 |
# File 'lib/kairos_mcp/skillset.rb', line 123 def knowledge_dirs knowledge_dir_names.map { |d| File.join(@path, d) }.select { |d| File.directory?(d) } end |
#knowledge_only? ⇒ Boolean
True if the SkillSet contains no executable code (tools/ or lib/) Checks for executable extensions (.rb, .py, .sh, etc.) and shebang lines
159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/kairos_mcp/skillset.rb', line 159 def knowledge_only? tools_dir = File.join(@path, 'tools') lib_dir = File.join(@path, 'lib') no_tools = !File.directory?(tools_dir) || Dir[File.join(tools_dir, '**', '*')].none? { |f| File.file?(f) && (executable_extension?(f) || has_shebang?(f)) } no_lib = !File.directory?(lib_dir) || Dir[File.join(lib_dir, '**', '*')].none? { |f| File.file?(f) && (executable_extension?(f) || has_shebang?(f)) } no_tools && no_lib end |
#layer ⇒ Object
Layer as symbol (:L0, :L1, :L2), with override support
27 28 29 |
# File 'lib/kairos_mcp/skillset.rb', line 27 def layer @layer_override || default_layer end |
#layer=(sym) ⇒ Object
31 32 33 34 35 36 |
# File 'lib/kairos_mcp/skillset.rb', line 31 def layer=(sym) sym = sym.to_sym raise ArgumentError, "Invalid layer: #{sym}. Valid: #{VALID_LAYERS}" unless VALID_LAYERS.include?(sym) @layer_override = sym end |
#load! ⇒ Object
Load the SkillSet code (require lib/ and tools/)
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/kairos_mcp/skillset.rb', line 95 def load! return if @loaded return false unless version_compatible? lib_dir = File.join(@path, 'lib') $LOAD_PATH.unshift(lib_dir) if File.directory?(lib_dir) && !$LOAD_PATH.include?(lib_dir) # Require lib entry point if it exists entry_point = Dir[File.join(lib_dir, '*.rb')].first require entry_point if entry_point # Ensure BaseTool is available before loading SkillSet tools require_relative 'tools/base_tool' # Require all tool files tools_dir = File.join(@path, 'tools') if File.directory?(tools_dir) Dir[File.join(tools_dir, '*.rb')].sort.each { |f| require f } end @loaded = true end |
#loaded? ⇒ Boolean
118 119 120 |
# File 'lib/kairos_mcp/skillset.rb', line 118 def loaded? @loaded end |
#place_extensions ⇒ Object
Place extension declarations for PlaceRouter integration. Returns Array of Hashes, each with ‘class’, ‘require’, ‘route_actions’.
83 84 85 |
# File 'lib/kairos_mcp/skillset.rb', line 83 def place_extensions @metadata['place_extensions'] || [] end |
#plugin_config ⇒ Object
Plugin projection support: Claude Code plugin artifacts in plugin/ directory
132 133 134 |
# File 'lib/kairos_mcp/skillset.rb', line 132 def plugin_config @metadata['plugin'] end |
#provides ⇒ Object
65 66 67 |
# File 'lib/kairos_mcp/skillset.rb', line 65 def provides @metadata['provides'] || [] end |
#to_h ⇒ Object
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/kairos_mcp/skillset.rb', line 186 def to_h { name: @name, version: version, description: description, author: , layer: layer, depends_on: depends_on, provides: provides, tool_classes: tool_class_names, knowledge_only: knowledge_only?, exchangeable: exchangeable?, path: @path, loaded: @loaded } end |
#tool_class_names ⇒ Object
69 70 71 |
# File 'lib/kairos_mcp/skillset.rb', line 69 def tool_class_names @metadata['tool_classes'] || [] end |
#valid? ⇒ Boolean
203 204 205 206 207 |
# File 'lib/kairos_mcp/skillset.rb', line 203 def valid? REQUIRED_FIELDS.all? { |f| @metadata[f] && !@metadata[f].to_s.strip.empty? } rescue StandardError false end |
#version ⇒ Object
43 44 45 |
# File 'lib/kairos_mcp/skillset.rb', line 43 def version @metadata['version'] end |
#version_compatible? ⇒ Boolean
Check if the SkillSet is compatible with the current core version
210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/kairos_mcp/skillset.rb', line 210 def version_compatible? min_version = @metadata['min_core_version'] return true unless min_version current = KairosMcp::VERSION if Gem::Version.new(current) < Gem::Version.new(min_version) warn "[SkillSet] '#{@name}' requires core >= #{min_version}, current: #{current} — skipping" return false end true rescue ArgumentError warn "[SkillSet] '#{@name}' has invalid min_core_version: #{min_version} — ignoring" true end |