Class: AgentHarness::Skill

Inherits:
Object
  • Object
show all
Defined in:
lib/agent_harness/skill.rb

Overview

Canonical provider-agnostic skill definition loaded from SKILL.md.

Constant Summary collapse

PROVIDER_FAMILY_ALIASES =
{
  anthropic: :anthropic,
  google: :google,
  openai: :openai,
  openai_compatible: :openai
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name:, description:, instructions:, trigger: nil, tools: [], mcp_servers: [], providers: {}, source_path: nil) ⇒ Skill

Returns a new instance of Skill.



18
19
20
21
22
23
24
25
26
27
28
# File 'lib/agent_harness/skill.rb', line 18

def initialize(name:, description:, instructions:, trigger: nil, tools: [], mcp_servers: [],
  providers: {}, source_path: nil)
  @name = normalize_name(name)
  @description = validate_string!(:description, description)
  @instructions = validate_string!(:instructions, instructions)
  @trigger = trigger.nil? ? nil : validate_string!(:trigger, trigger)
  @tools = normalize_array(:tools, tools)
  @mcp_servers = normalize_mcp_servers(mcp_servers)
  @provider_overrides = normalize_provider_overrides(providers)
  @source_path = source_path && File.expand_path(source_path)
end

Instance Attribute Details

#descriptionObject (readonly)

Returns the value of attribute description.



15
16
17
# File 'lib/agent_harness/skill.rb', line 15

def description
  @description
end

#instructionsObject (readonly)

Returns the value of attribute instructions.



15
16
17
# File 'lib/agent_harness/skill.rb', line 15

def instructions
  @instructions
end

#mcp_serversObject (readonly)

Returns the value of attribute mcp_servers.



15
16
17
# File 'lib/agent_harness/skill.rb', line 15

def mcp_servers
  @mcp_servers
end

#nameObject (readonly)

Returns the value of attribute name.



15
16
17
# File 'lib/agent_harness/skill.rb', line 15

def name
  @name
end

#provider_overridesObject (readonly)

Returns the value of attribute provider_overrides.



16
17
18
# File 'lib/agent_harness/skill.rb', line 16

def provider_overrides
  @provider_overrides
end

#source_pathObject (readonly)

Returns the value of attribute source_path.



16
17
18
# File 'lib/agent_harness/skill.rb', line 16

def source_path
  @source_path
end

#toolsObject (readonly)

Returns the value of attribute tools.



15
16
17
# File 'lib/agent_harness/skill.rb', line 15

def tools
  @tools
end

#triggerObject (readonly)

Returns the value of attribute trigger.



15
16
17
# File 'lib/agent_harness/skill.rb', line 15

def trigger
  @trigger
end

Class Method Details

.from_hash(hash = nil, source_path: nil, **kwargs) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/agent_harness/skill.rb', line 30

def self.from_hash(hash = nil, source_path: nil, **kwargs)
  hash = kwargs if hash.nil? && !kwargs.empty?

  unless hash.is_a?(Hash)
    raise ConfigurationError, "Skill definition must be a Hash, got #{hash.class}"
  end

  attrs = hash.each_with_object({}) do |(key, value), memo|
    memo[key.to_sym] = value
  end

  %i[name description instructions].each do |field|
    value = attrs[field]
    next if value.is_a?(String) && !value.strip.empty?
    next if value.is_a?(Symbol)

    raise ConfigurationError, "#{field} is required"
  end

  new(**attrs.merge(source_path: source_path))
end

.load_file(path) ⇒ Object



52
53
54
55
56
57
58
59
60
61
# File 'lib/agent_harness/skill.rb', line 52

def self.load_file(path)
  expanded_path = File.expand_path(path)
  content = File.read(expanded_path)
  frontmatter, body = parse_markdown(content)
  from_hash(frontmatter.merge(instructions: body), source_path: expanded_path)
rescue Errno::ENOENT
  raise ConfigurationError, "Skill file not found: #{path}"
rescue Psych::Exception => e
  raise ConfigurationError, "Invalid YAML frontmatter in skill #{path}: #{e.message}"
end

Instance Method Details

#provider_override_for(provider) ⇒ Object



63
64
65
66
67
68
69
70
71
72
# File 'lib/agent_harness/skill.rb', line 63

def provider_override_for(provider)
  merged = provider_override_keys_for(provider).reduce(nil) do |runtime, key|
    override = @provider_overrides[key]
    next runtime unless override

    runtime ? runtime.merge(override) : ProviderRuntime.wrap(override)
  end

  merged ? merged.to_h : {}
end

#to_hObject



74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/agent_harness/skill.rb', line 74

def to_h
  {
    name: @name,
    description: @description,
    instructions: @instructions,
    trigger: @trigger,
    tools: deep_dup(@tools),
    mcp_servers: deep_dup(@mcp_servers),
    providers: deep_dup(@provider_overrides),
    source_path: @source_path
  }.compact
end