Class: AgentC::Tools::EditFile

Inherits:
RubyLLM::Tool
  • Object
show all
Defined in:
lib/agent_c/tools/edit_file.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(workspace_dir: nil) ⇒ EditFile

Returns a new instance of EditFile.

Raises:

  • (ArgumentError)


41
42
43
44
# File 'lib/agent_c/tools/edit_file.rb', line 41

def initialize(workspace_dir: nil, **)
  raise ArgumentError, "workspace_dir is required" unless workspace_dir
  @workspace_dir = workspace_dir
end

Instance Attribute Details

#workspace_dirObject (readonly)

Returns the value of attribute workspace_dir.



40
41
42
# File 'lib/agent_c/tools/edit_file.rb', line 40

def workspace_dir
  @workspace_dir
end

Instance Method Details

#execute(path:, mode:, content:, start_line: nil, end_line: nil, **params) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/agent_c/tools/edit_file.rb', line 46

def execute(path:, mode:, content:, start_line: nil, end_line: nil, **params)
  if params.any?
    return "The following params were passed but are not allowed: #{params.keys.join(",")}"
  end

  unless Paths.allowed?(workspace_dir, path)
    return "Path: #{path} not acceptable. Must be a child of directory: #{workspace_dir}."
  end

  workspace_path = Paths.relative_to_dir(workspace_dir, path)

  case mode
  when "delete"
    FileUtils.rm_f(path)
  when "overwrite"
    FileUtils.mkdir_p(File.dirname(workspace_path))
    File.write(workspace_path, content)

  when "replace_lines"
    return "start_line and end_line required for replace_lines mode" unless start_line && end_line
    return "start_line must be <= end_line" if start_line > end_line

    lines = File.exist?(workspace_path) ? File.readlines(workspace_path, chomp: true) : []

    # Convert to 0-indexed
    start_idx = start_line - 1
    end_idx = end_line - 1

    # Replace the range with new content lines
    new_lines = content.split("\n")
    lines[start_idx..end_idx] = new_lines

    File.write(workspace_path, lines.join("\n") + "\n")

  when "insert_at_line"
    return "start_line required for insert_at_line mode" unless start_line

    lines = File.exist?(workspace_path) ? File.readlines(workspace_path, chomp: true) : []

    # Insert before the specified line (1-indexed)
    insert_idx = start_line - 1
    insert_idx = [0, [insert_idx, lines.length].min].max  # Clamp to valid range

    new_lines = content.split("\n")
    lines.insert(insert_idx, *new_lines)

    File.write(workspace_path, lines.join("\n") + "\n")

  when "append"
    FileUtils.mkdir_p(File.dirname(workspace_path))
    File.open(workspace_path, "a") do |f|
      f.write(content)
      f.write("\n") unless content.end_with?("\n")
    end
  end

  # Run syntax check for Ruby files
  if workspace_path.end_with?(".rb")
    syntax_check = `ruby -c #{workspace_path} 2>&1`
    unless $?.success?
      return "File successfully edited, but syntax errors were found:\n#{syntax_check}"
    end
  end

  true
end