Module: RubyLLM::Toolbox::Tools::ToolchainHelpers

Included in:
Bundle, Lint, PythonTests, RunTests
Defined in:
lib/ruby_llm/toolbox/tools/toolchain_helpers.rb

Overview

Shared behavior for the Ruby toolchain tools (run_tests, lint, bundle).

Unlike BashTool, these run in fs_root and inherit the full host environment. That’s deliberate: tools like bundler, rbenv/rvm, and the test/lint binaries rely on the Ruby environment (PATH shims, GEM_HOME, BUNDLE_*) to resolve correctly, and these are trusted dev commands the user opted into via enable_exec_tools — not arbitrary user input.

Defined Under Namespace

Classes: CommandMissing

Instance Method Summary collapse

Instance Method Details

#gemfile?Boolean

Returns:

  • (Boolean)


20
21
22
# File 'lib/ruby_llm/toolbox/tools/toolchain_helpers.rb', line 20

def gemfile?
  File.file?(File.join(config.fs_root, "Gemfile"))
end

#jail_relative(path) ⇒ Object

Resolve a path param to a project-relative path, rejecting escapes.



52
53
54
55
56
57
58
# File 'lib/ruby_llm/toolbox/tools/toolchain_helpers.rb', line 52

def jail_relative(path)
  return nil if path.nil? || path.to_s.empty?

  jail = Safety::PathJail.new(config.fs_root)
  real = jail.resolve(path)
  Pathname.new(real).relative_path_from(Pathname.new(jail.root)).to_s
end

#run_in_project(argv, use_bundle: false) ⇒ Object

Runs argv in fs_root. With use_bundle, prefixes ‘bundle exec` when a Gemfile is present. Returns [out, err, status]; raises CommandMissing if the executable can’t be found.



27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/ruby_llm/toolbox/tools/toolchain_helpers.rb', line 27

def run_in_project(argv, use_bundle: false)
  command = use_bundle && gemfile? ? ["bundle", "exec", *argv] : argv
  ProcessRunner.capture(
    command,
    env: {},                 # inherit the full host environment
    chdir: config.fs_root,
    timeout: config.command_timeout,
    unsetenv_others: false
  )
rescue Errno::ENOENT
  raise CommandMissing, command.first
end

#toolchain_output(out, err, status, pass_label:, fail_label:) ⇒ Object

Maps a finished toolchain run onto a return value. Non-zero exit is NOT treated as a tool error here — failing tests / lint offenses are results the agent needs to see, with a headline prepended.



43
44
45
46
47
48
49
# File 'lib/ruby_llm/toolbox/tools/toolchain_helpers.rb', line 43

def toolchain_output(out, err, status, pass_label:, fail_label:)
  return error("timed out after #{config.command_timeout}s", code: :timeout) if status == :timeout

  combined = [out, err].map(&:to_s).reject(&:empty?).join("\n")
  headline = status.exitstatus.zero? ? pass_label : fail_label
  truncate("#{headline}\n\n#{combined}".strip)
end