Class: Rubino::Tools::GrepTool
- Defined in:
- lib/rubino/tools/grep_tool.rb
Overview
Tool for searching file contents using regex patterns. Backed by ripgrep (rg) if available, falls back to Ruby grep.
Instance Attribute Summary
Attributes inherited from Base
#cancel_token, #read_tracker, #stream_chunk, #stream_kind
Instance Method Summary collapse
- #call(arguments) ⇒ Object
- #description ⇒ Object
- #input_schema ⇒ Object
- #name ⇒ Object
- #risk_level ⇒ Object
Methods inherited from Base
#cancellation_requested?, #config_key, #emit_chunk, #risky?, #to_tool_definition, workspace_root, workspace_roots
Instance Method Details
#call(arguments) ⇒ Object
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 |
# File 'lib/rubino/tools/grep_tool.rb', line 59 def call(arguments) pattern = arguments["pattern"] || arguments[:pattern] path = arguments["path"] || arguments[:path] || "." include_pattern = arguments["include"] || arguments[:include] max_results = arguments["max_results"] || arguments[:max_results] || 50 # -A/-B/-C semantics, mirroring ripgrep: `context` (-C) overrides # both halves; otherwise each side defaults to 0. Clamp at 50 lines # per side so a runaway model can't ask for 10_000 lines of context # per match and overrun the output budget. ctx = arguments["context"] || arguments[:context] before = (ctx || arguments["before"] || arguments[:before] || 0).to_i.clamp(0, 50) after = (ctx || arguments["after"] || arguments[:after] || 0).to_i.clamp(0, 50) = (path) # Search is BROAD (#406): grep resolves any NON-secret path like # Hermes/Claude/Codex. A grep whose `path` is a SECRET file directly # (#446) is gated UPSTREAM by Security::ApprovalPolicy#decide (→ :ask), # exactly like read — so it is NOT refused here; an approved grep of a # secret file proceeds, a denied/headless one never reaches #call. # # F2: a DIRECTORY grep with `include: "*.env"` is NOT a secret target — # the gate above can't see it — but rg's --glob OVERRIDES the default # hidden-exclusion and would LEAK the matched .env lines. We therefore # post-filter the RESULTS (see #filter_secret_hits): any result line that # points at a secret file is stripped, so secrets never escape via an # include-glob regardless of approval. return "Error: Path not found: #{path}" unless File.exist?() if ripgrep_available? search_with_ripgrep(pattern, , include_pattern, max_results, before, after) else search_with_ruby(pattern, , include_pattern, max_results, before, after) end end |
#description ⇒ Object
12 13 14 15 16 |
# File 'lib/rubino/tools/grep_tool.rb', line 12 def description "Search file contents using regular expressions. " \ "Returns matching file paths and line numbers. " \ "Supports include patterns to filter by file type." end |
#input_schema ⇒ Object
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 48 49 50 51 52 53 |
# File 'lib/rubino/tools/grep_tool.rb', line 18 def input_schema { type: "object", properties: { pattern: { type: "string", description: "The regex pattern to search for" }, path: { type: "string", description: "Directory to search in (defaults to current directory)" }, include: { type: "string", description: "File pattern to include (e.g., '*.rb', '*.{ts,tsx}')" }, max_results: { type: "integer", description: "Maximum number of results to return (default: 50)" }, before: { type: "integer", description: "Lines of leading context to include before each match (-B). Default 0." }, after: { type: "integer", description: "Lines of trailing context to include after each match (-A). Default 0." }, context: { type: "integer", description: "Symmetric context (-C): sets both before and after. Wins over before/after when given." } }, required: %w[pattern] } end |
#name ⇒ Object
8 9 10 |
# File 'lib/rubino/tools/grep_tool.rb', line 8 def name "grep" end |
#risk_level ⇒ Object
55 56 57 |
# File 'lib/rubino/tools/grep_tool.rb', line 55 def risk_level :low end |