Packages
fnord
0.7.17
0.9.40
0.9.39
0.9.38
0.9.37
0.9.36
0.9.35
0.9.34
0.9.33
0.9.32
0.9.31
0.9.30
0.9.29
0.9.28
0.9.27
0.9.26
0.9.25
0.9.24
0.9.23
0.9.22
0.9.21
0.9.20
0.9.19
0.9.18
0.9.17
0.9.16
0.9.15
0.9.14
0.9.13
0.9.12
0.9.11
0.9.10
0.9.9
0.9.8
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.99
0.8.98
0.8.97
0.8.96
0.8.95
0.8.94
0.8.93
0.8.92
0.8.91
0.8.90
0.8.89
0.8.88
0.8.87
0.8.86
0.8.85
0.8.84
0.8.83
0.8.82
0.8.81
0.8.80
0.8.79
0.8.78
0.8.77
0.8.76
0.8.75
0.8.74
0.8.73
0.8.72
0.8.71
0.8.70
0.8.69
0.8.68
0.8.67
0.8.66
0.8.65
0.8.64
0.8.63
0.8.62
0.8.61
0.8.60
0.8.59
0.8.58
0.8.57
0.8.56
0.8.55
0.8.54
0.8.53
0.8.52
0.8.51
0.8.50
0.8.49
0.8.48
0.8.47
0.8.46
0.8.45
0.8.44
0.8.43
0.8.42
0.8.41
0.8.40
0.8.39
0.8.38
0.8.37
0.8.36
0.8.35
0.8.34
0.8.33
0.8.32
0.8.31
0.8.30
0.8.29
0.8.27
0.8.26
0.8.25
0.8.24
0.8.23
0.8.22
0.8.21
0.8.20
0.8.19
0.8.18
0.8.17
0.8.16
0.8.15
0.8.14
0.8.13
0.8.12
0.8.11
0.8.1
0.8.0
0.7.24
0.7.23
0.7.22
0.7.21
0.7.20
0.7.19
0.7.18
0.7.17
0.7.16
0.7.15
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.3
0.7.2
0.7.1
0.7.0
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.1
0.6.0
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.44
0.4.43
0.4.42
0.4.41
0.4.40
0.4.39
0.4.38
0.4.37
0.4.36
0.4.35
0.4.34
0.4.33
0.4.32
0.4.30
0.4.29
0.4.28
0.4.27
0.4.26
0.4.25
0.4.24
0.4.23
0.4.22
0.4.21
0.4.20
0.4.19
0.4.18
0.4.17
0.4.16
0.4.15
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.0
0.2.0
0.1.0
AI code archaeology
Current section
Files
Jump to
Current section
Files
lib/ai/tools/ripgrep.ex
defmodule AI.Tools.Ripgrep do
@doc """
This tool requires that ripgrep (rg) is installed and available in the PATH.
"""
def is_available?() do
System.find_executable("rg") |> is_nil() |> Kernel.not()
end
@behaviour AI.Tools
@impl AI.Tools
def ui_note_on_request(args) do
{"Searching project files", Jason.encode!(args)}
end
@impl AI.Tools
def ui_note_on_result(args, result) do
{"Searched project files",
"""
Searched with: #{Jason.encode!(args)}
Result:
#{result}
"""}
end
@impl AI.Tools
def read_args(args), do: {:ok, args}
@impl AI.Tools
def spec() do
%{
type: "function",
function: %{
name: "ripgrep_search",
description:
"Searches the project directory using ripgrep. This tool is ideal when you need to find an exact match.",
parameters: %{
type: "object",
required: ["pattern"],
properties: %{
pattern: %{
type: "string",
description:
"The pattern to search for (regex by default, or literal with fixed_strings)."
},
fixed_strings: %{
type: "boolean",
description:
"Treat the pattern as a literal string instead of a regular expression.",
default: false
},
ignore_case: %{
type: "boolean",
description: "Perform case-insensitive matching.",
default: false
},
invert_match: %{
type: "boolean",
description: "Select non-matching lines.",
default: false
},
glob: %{
type: "array",
items: %{type: "string"},
description:
"Include or exclude files using glob patterns (e.g., '*.rs', '!Cargo.lock')."
},
hidden: %{
type: "boolean",
description: "Search hidden files and directories.",
default: false
},
no_ignore: %{
type: "boolean",
description: "Don't respect .gitignore and other ignore files.",
default: false
}
}
}
}
}
end
@impl AI.Tools
def call(args) do
root = Store.get_project() |> Map.get(:source_root)
args = atomize_keys(args)
cmd = build_ripgrep_cmd(args)
try do
System.cmd("rg", cmd ++ [root], stderr_to_stdout: true)
|> case do
{output, 0} -> {:ok, output}
{_output, 1} -> {:ok, "No files were matched"}
{output, code} -> {:error, "ripgrep failed with exit code #{code}:\n#{output}"}
end
rescue
e in ErlangError ->
case e.original do
:enoent ->
{
:error,
"""
`rg` (ripgrep) is not installed or not in PATH.
In your final response, instruct the user to install it to enhance your search capabilities.
"""
}
other ->
{:error, "ripgrep execution error: #{inspect(other)}"}
end
end
end
defp atomize_keys(map) when is_map(map) do
Map.new(map, fn {k, v} -> {String.to_atom(k), v} end)
end
defp build_ripgrep_cmd(%{pattern: pattern} = args) do
[]
|> append_flag(args[:fixed_strings], "--fixed-strings")
|> append_flag(args[:ignore_case], "--ignore-case")
|> append_flag(args[:invert_match], "--invert-match")
|> append_flag(args[:hidden], "--hidden")
|> append_flag(args[:no_ignore], "--no-ignore")
|> append_list("--glob", args[:glob])
|> Kernel.++([pattern])
end
defp append_flag(cmd, true, flag), do: cmd ++ [flag]
defp append_flag(cmd, _, _), do: cmd
defp append_list(cmd, _flag, nil), do: cmd
defp append_list(cmd, flag, list) when is_binary(list) do
cmd ++ [flag, list]
end
defp append_list(cmd, flag, list) when is_list(list) do
Enum.reduce(list, cmd, fn item, acc -> acc ++ [flag, item] end)
end
end