Module: RobotLab::ScriptTool

Defined in:
lib/robot_lab/script_tool.rb

Overview

Factory module for wrapping AgentSkills scripts as RobotLab::Tool instances.

Given a path to an executable shell script, produces a Tool that shells out to the script and returns its combined stdout+stderr output. Non-executable scripts return nil with a logged warning.

Class Method Summary collapse

Class Method Details

.derive_name(path) ⇒ String

Returns snake_case tool name derived from filename.

Parameters:

  • path (Pathname)

Returns:

  • (String)

    snake_case tool name derived from filename



51
52
53
54
55
56
# File 'lib/robot_lab/script_tool.rb', line 51

def self.derive_name(path)
  path.basename.to_s
      .sub(/\.[^.]+$/, '')
      .gsub(/[^a-zA-Z0-9]+/, '_')
      .gsub(/^_+|_+$/, '')
end

.extract_description(path) ⇒ String

Extract tool description from the first non-shebang comment line.

Parameters:

  • path (Pathname)

Returns:

  • (String)


62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/robot_lab/script_tool.rb', line 62

def self.extract_description(path)
  File.foreach(path) do |line|
    stripped = line.strip
    next unless stripped.start_with?('#')
    next if stripped.start_with?('#!') # skip shebang

    desc = stripped.sub(/^#+\s*/, '').strip
    return desc unless desc.empty?
  end
  derive_name(path)
rescue StandardError
  derive_name(path)
end

.from_path(script_path) ⇒ RobotLab::Tool?

Wrap a script file as a RobotLab::Tool.

Parameters:

  • script_path (String, Pathname)

    path to the script file

Returns:



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/robot_lab/script_tool.rb', line 17

def self.from_path(script_path)
  path = Pathname.new(script_path)

  unless path.executable?
    RobotLab.config.logger.warn(
      "ScriptTool: #{path.basename} is not executable, skipping"
    )
    return nil
  end

  tool_name   = derive_name(path)
  description = extract_description(path)
  script      = path.to_s

  Tool.create(
    name: tool_name,
    description: description,
    parameters: {
      type: 'object',
      properties: {
        args: { type: 'string', description: 'Optional command-line arguments' }
      },
      required: []
    }
  ) do |tool_args|
    cli_args = tool_args[:args].to_s.strip
    cmd      = cli_args.empty? ? ['bash', script] : ['bash', script, *Shellwords.split(cli_args)]
    output, status = Open3.capture2e(*cmd)
    status.success? ? output : "Error (exit #{status.exitstatus}):\n#{output}"
  end
end