Class: SwarmSDK::Tools::Glob

Inherits:
Base
  • Object
show all
Includes:
PathResolver
Defined in:
lib/swarm_sdk/tools/glob.rb

Overview

Glob tool for fast file and directory pattern matching

Finds files and directories matching glob patterns, sorted by modification time. Works efficiently with any codebase size.

Instance Attribute Summary

Attributes included from PathResolver

#agent_name, #directory

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

removable, removable?, #removable?

Constructor Details

#initialize(directory:) ⇒ Glob

Returns a new instance of Glob.



19
20
21
22
# File 'lib/swarm_sdk/tools/glob.rb', line 19

def initialize(directory:)
  super()
  @directory = File.expand_path(directory)
end

Class Method Details

.creation_requirementsObject



14
15
16
# File 'lib/swarm_sdk/tools/glob.rb', line 14

def creation_requirements
  [:directory]
end

Instance Method Details

#execute(pattern:, path: nil) ⇒ Object

NOTE: Result limit now accessed via SwarmSDK.config.glob_result_limit



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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/swarm_sdk/tools/glob.rb', line 55

def execute(pattern:, path: nil)
  # Validate inputs
  return validation_error("pattern is required") if pattern.nil? || pattern.to_s.strip.empty?

  # Validate path if provided
  if path && !path.to_s.strip.empty?
    # Check for literal "undefined" or "null" strings
    if path.to_s.strip.downcase == "undefined" || path.to_s.strip.downcase == "null"
      return validation_error("Invalid path value. Omit the path parameter entirely to use the current working directory.")
    end

    unless File.exist?(path)
      return validation_error("Path does not exist: #{path}")
    end

    unless File.directory?(path)
      return validation_error("Path is not a directory: #{path}")
    end

    # CRITICAL: Resolve relative paths against agent directory
    search_path = resolve_path(path)
  else
    # CRITICAL: Use agent's directory as default (NOT Dir.pwd)
    search_path = @directory
  end

  # Execute glob from specified path
  begin
    # Build full pattern by joining search path with pattern
    # If pattern is already absolute, File.join will use it as-is
    full_pattern = if pattern.start_with?("/")
      # Pattern is absolute, use it directly
      pattern
    else
      # Pattern is relative, join with search path
      File.join(search_path, pattern)
    end

    matches = Dir.glob(full_pattern, File::FNM_DOTMATCH)

    # Remove . and .. entries (handle both with and without trailing slashes)
    matches.reject! do |f|
      basename = File.basename(f.chomp("/"))
      basename == "." || basename == ".."
    end

    # Handle no matches
    if matches.empty?
      return "No matches found for pattern: #{pattern}"
    end

    # Sort by modification time (most recent first)
    matches.sort_by! { |f| -File.mtime(f).to_i }

    # Limit results
    max_results = SwarmSDK.config.glob_result_limit
    if matches.count > max_results
      matches = matches.take(max_results)
      truncated = true
    else
      truncated = false
    end

    # Format output
    output = matches.join("\n")

    # Add system reminder if truncated
    if truncated
      output += "\n        <system-reminder>\n        Results limited to first \#{max_results} matches (sorted by most recently modified).\n        Consider using a more specific pattern to narrow your search.\n        </system-reminder>\n      REMINDER\n    end\n\n    # Add usage reminder\n    output += \"\\n\\n\" + build_usage_reminder(matches.count, pattern)\n\n    output\n  rescue Errno::EACCES => e\n    error(\"Permission denied: \#{e.message}\")\n  rescue StandardError => e\n    error(\"Failed to execute glob: \#{e.class.name} - \#{e.message}\")\n  end\nrescue StandardError => e\n  error(\"Unexpected error during glob: \#{e.class.name} - \#{e.message}\")\nend\n"