lib/llm_toolkit.ex

defmodule LlmToolkit do
  @moduledoc """
  Base code tools for agentic LLM execution.

  LlmToolkit provides the fundamental tools every agentic consumer needs to
  interact with a filesystem, shell, and the web — read, write, edit, bash,
  grep, glob, tree, and http_get. Each tool implements `LlmToolkit.ToolResolver`.

  ## Usage

      # Use the default tool resolver (cwd = ".")
      defmodule MyAgent do
        @resolver LlmToolkit.CodeTools

        def run(task) do
          MyAgent.Loop.run(task, @resolver, [])
        end
      end

      # Use with a specific working directory
      resolver = {LlmToolkit.CodeTools, "/path/to/project"}
  """

  alias LlmToolkit.CodeTools
  alias LlmToolkit.Tool.Call

  @doc """
  Creates a ToolResolver function bound to a working directory.

  Returns a tuple `{LlmToolkit.CodeTools, cwd}` suitable for use
  as a resolver in any agent loop that accepts `ToolResolver` modules.

  ## Examples

      iex> LlmToolkit.resolver("/tmp/project")
      {LlmToolkit.CodeTools, "/tmp/project"}
  """
  @spec resolver(String.t()) :: {module(), String.t()}
  def resolver(cwd) when is_binary(cwd) do
    {CodeTools, cwd}
  end

  @doc """
  Creates a resolver closure for use in contexts that expect
  a simple function, not a tuple.

  ## Examples

      resolver = LlmToolkit.resolver_fn("/tmp/project")
      {:ok, content} = resolver.(%Call{name: "read_file", arguments: %{"path" => "README.md"}})
  """
  @spec resolver_fn(String.t()) :: (Call.t() -> {:ok, String.t()} | {:error, String.t()})
  def resolver_fn(cwd) when is_binary(cwd) do
    fn %Call{} = call ->
      CodeTools.resolve(call, cwd)
    end
  end
end