Class: OllamaAgent::DiffPathValidator

Inherits:
Object
  • Object
show all
Defined in:
lib/ollama_agent/diff_path_validator.rb

Overview

Validates unified diffs: hunk headers and path alignment with edit_file (applyability is patch –dry-run). rubocop:disable Metrics/ClassLength – normalization helpers live alongside validation

Constant Summary collapse

CONTEXT_DIFF_HUNK =

Legacy context-diff hunks (‘— N,M —-`) are not unified diffs; models sometimes emit them by mistake.

/^\s*---\s+\d+\s*,\s*\d+\s*----\s*$/m

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(diff, root, target_path) ⇒ DiffPathValidator

Returns a new instance of DiffPathValidator.



56
57
58
59
60
# File 'lib/ollama_agent/diff_path_validator.rb', line 56

def initialize(diff, root, target_path)
  @diff = self.class.normalize_diff(diff)
  @root = File.expand_path(root)
  @target_path = target_path
end

Class Method Details

.call(diff, root, target_path) ⇒ Object



12
13
14
# File 'lib/ollama_agent/diff_path_validator.rb', line 12

def self.call(diff, root, target_path)
  new(diff, root, target_path).validate
end

.ensure_trailing_newline(diff) ⇒ Object



44
45
46
47
48
# File 'lib/ollama_agent/diff_path_validator.rb', line 44

def self.ensure_trailing_newline(diff)
  return diff if diff.empty? || diff.end_with?("\n")

  "#{diff}\n"
end

.expand_escaped_newlines_when_no_real_newlines(diff) ⇒ Object



30
31
32
33
34
# File 'lib/ollama_agent/diff_path_validator.rb', line 30

def self.expand_escaped_newlines_when_no_real_newlines(diff)
  return diff unless diff.include?("\\n") && !diff.include?("\n")

  diff.gsub("\\\\n", "\n").gsub("\\n", "\n")
end

.normalize_diff(diff) ⇒ Object

Normalizes newlines and escaped “\n” sequences models sometimes send in tool args.



17
18
19
20
21
22
23
24
# File 'lib/ollama_agent/diff_path_validator.rb', line 17

def self.normalize_diff(diff)
  d = normalize_newlines(diff.to_s)
  d = expand_escaped_newlines_when_no_real_newlines(d)
  d = strip_cursor_patch_markers(d)
  d = strip_trailing_commas_on_headers(d)
  d = split_glued_hunk_headers(d)
  ensure_trailing_newline(d)
end

.normalize_newlines(diff) ⇒ Object



26
27
28
# File 'lib/ollama_agent/diff_path_validator.rb', line 26

def self.normalize_newlines(diff)
  diff.gsub("\r\n", "\n").gsub("\r", "\n")
end

.split_glued_hunk_headers(diff) ⇒ Object



40
41
42
# File 'lib/ollama_agent/diff_path_validator.rb', line 40

def self.split_glued_hunk_headers(diff)
  diff.gsub(/(\S)\s(@@ -\d[^\n]*)/, "\\1\n\\2")
end

.strip_cursor_patch_markers(diff) ⇒ Object



50
51
52
53
54
# File 'lib/ollama_agent/diff_path_validator.rb', line 50

def self.strip_cursor_patch_markers(diff)
  # Multiline `/m` so `^` matches each line; strips Cursor-style trailers that are not valid patch input.
  diff.gsub(/^\s*\*\*\*\s*(?:Begin|End)\s+Patch\s*$/im, "")
      .gsub(/^\s*(?:Begin|End)\s+Patch\s*$/im, "")
end

.strip_trailing_commas_on_headers(diff) ⇒ Object



36
37
38
# File 'lib/ollama_agent/diff_path_validator.rb', line 36

def self.strip_trailing_commas_on_headers(diff)
  diff.gsub(/^((?:---|\+\+\+)[^\n]+),\s*$/m, "\\1")
end

Instance Method Details

#validateString?

Returns error message, or nil if the diff is acceptable.

Returns:

  • (String, nil)

    error message, or nil if the diff is acceptable



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/ollama_agent/diff_path_validator.rb', line 63

def validate
  return "Diff is empty." if @diff.strip.empty?
  return "edit_file path is missing or empty." if @target_path.nil? || @target_path.to_s.strip.empty?

  err = context_diff_hunk_error
  return err if err

  err = header_order_error
  return err if err

  err = structure_error
  return err if err

  path_alignment_error
end