Packages
fnord
0.8.21
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/file/manage.ex
defmodule AI.Tools.File.Manage do
@behaviour AI.Tools
@impl AI.Tools
def async?, do: true
@impl AI.Tools
def is_available?, do: true
@impl AI.Tools
def ui_note_on_request(%{"operation" => "create", "path" => path}), do: {"Creating file", path}
def ui_note_on_request(%{"operation" => "delete", "path" => path}), do: {"Deleting file", path}
def ui_note_on_request(%{"operation" => "move", "path" => path, "destination_path" => dest}) do
{"Moving file", "#{path} -> #{dest}"}
end
def ui_note_on_request(_), do: nil
@impl AI.Tools
def ui_note_on_result(%{"operation" => "create", "path" => path}, {:ok, _}) do
{"File created", path}
end
def ui_note_on_result(%{"operation" => "delete", "path" => path}, {:ok, _}) do
{"File deleted", path}
end
def ui_note_on_result(
%{"operation" => "move", "path" => path, "destination_path" => dest},
{:ok, _}
) do
{"File moved", "#{path} -> #{dest}"}
end
def ui_note_on_result(_args, {:error, reason}), do: {"File operation error", to_string(reason)}
def ui_note_on_result(_args, result), do: {"File operation result", inspect(result)}
@impl AI.Tools
def read_args(%{"operation" => "create"} = args) do
with {:ok, path} <- AI.Tools.get_arg(args, "path") do
{:ok,
%{
"operation" => "create",
"path" => path,
"is_directory" => Map.get(args, "is_directory", false)
}}
end
end
def read_args(%{"operation" => "delete"} = args) do
with {:ok, path} <- AI.Tools.get_arg(args, "path") do
{:ok,
%{
"operation" => "delete",
"path" => path,
"is_directory" => Map.get(args, "is_directory", false)
}}
end
end
def read_args(%{"operation" => "move"} = args) do
with {:ok, path} <- AI.Tools.get_arg(args, "path"),
{:ok, dest_path} <- AI.Tools.get_arg(args, "destination_path") do
{:ok,
%{
"operation" => "move",
"path" => path,
"destination_path" => dest_path
}}
end
end
def read_args(%{"operation" => _}) do
{:error, :invalid_argument, "operation"}
end
def read_args(_) do
{:error, :missing_argument, "operation"}
end
@impl AI.Tools
def spec do
%{
type: "function",
function: %{
name: "file_manage_tool",
description: """
Create, delete, or move files within the project source root. This does
NOT allow you to edit files directly. It only allows you to perform
basic file operations like creating, deleting, or moving/renaming files
and directories.
""",
parameters: %{
type: "object",
required: ["operation", "path"],
properties: %{
operation: %{
type: "string",
enum: ["create", "delete", "move"],
description: "The operation to perform: create, delete, or move."
},
path: %{
type: "string",
description:
"Path (relative to project root) of the file to operate on (or *source path* for move)."
},
destination_path: %{
type: "string",
description: "Required for move: the new path (relative to project root)."
},
is_directory: %{
type: "boolean",
description:
"If true, treat the path as a directory (for create or delete operations). Required to delete a directory."
}
}
}
}
}
end
@impl AI.Tools
def call(%{"operation" => op, "path" => path} = args) do
with {:ok, project} <- Store.get_project(),
abs_src <- Store.Project.expand_path(path, project),
is_directory? <- Map.get(args, "is_directory", false),
:ok <- validate_path(project, path) do
case op do
"create" -> create_path(path, abs_src, is_directory?)
"delete" -> delete_path(path, abs_src, is_directory?)
"move" -> move_path(project, path, abs_src, args["destination_path"])
_ -> {:error, :invalid_argument, "operation"}
end
end
end
defp validate_path(project, path) do
if Util.path_within_root?(path, project.source_root) do
:ok
else
{:error, "Path escapes project root!"}
end
end
defp create_path(path, abs_path, true) do
if File.exists?(abs_path) do
{:error, "Path already exists: #{path}"}
else
abs_path
|> Path.dirname()
|> File.mkdir_p()
|> case do
:ok -> {:ok, "Created directory: #{path}"}
{:error, reason} -> {:error, "Failed to create directory #{path}: #{inspect(reason)}"}
end
end
end
defp create_path(path, abs_path, false) do
if File.exists?(abs_path) do
{:error, "Path already exists: #{path}"}
else
abs_path |> Path.dirname() |> File.mkdir_p!()
case File.write(abs_path, "") do
:ok -> {:ok, "Created file: #{path}"}
{:error, reason} -> {:error, "Failed to create file #{path}: #{inspect(reason)}"}
end
end
end
defp delete_path(path, abs_path, true) do
if File.exists?(abs_path) do
abs_path
|> File.rm_rf()
|> case do
{:ok, _} ->
{:ok, "Deleted directory: #{path}"}
{:error, _posix, reason} ->
{:error, "Failed to delete directory #{path}: #{inspect(reason)}"}
end
else
{:error, "File does not exist: #{path}"}
end
end
defp delete_path(path, abs_path, false) do
if File.exists?(abs_path) do
abs_path
|> File.rm()
|> case do
:ok -> {:ok, "Deleted file: #{path}"}
{:error, reason} -> {:error, "Failed to delete file #{path}: #{inspect(reason)}"}
end
else
{:error, "File does not exist: #{path}"}
end
end
defp move_path(project, path, abs_path, dest_path) do
dest =
dest_path
|> to_string()
|> Store.Project.expand_path(project)
cond do
not Util.path_within_root?(dest, project.source_root) ->
{:error, "Destination path escapes project root."}
not File.exists?(abs_path) ->
{:error, "Source path does not exist: #{path}"}
File.exists?(dest) ->
{:error, "Destination path already exists: #{dest_path}"}
true ->
File.mkdir_p!(Path.dirname(dest))
case File.rename(abs_path, dest) do
:ok ->
{:ok, "Moved #{path} -> #{dest_path}"}
{:error, reason} ->
{:error, "Failed to move (#{path} to #{dest_path}): #{inspect(reason)}"}
end
end
end
end