Packages

fnord

0.9.8

AI code archaeology

Current section

Files

Jump to
fnord lib ai tools coder.ex
Raw

lib/ai/tools/coder.ex

defmodule AI.Tools.Coder do
@max_steps 4
# ----------------------------------------------------------------------------
# Behaviour Implementation
# ----------------------------------------------------------------------------
@behaviour AI.Tools
@impl AI.Tools
def async? do
Settings.get_edit_mode() && Settings.get_auto_approve()
end
@impl AI.Tools
def is_available? do
Settings.get_edit_mode()
end
@impl AI.Tools
def read_args(args), do: {:ok, args}
@impl AI.Tools
def ui_note_on_request(%{"requirements" => requirements}) do
{"Planning implementation", requirements}
end
@impl AI.Tools
def ui_note_on_result(_args, result) do
{"Changes implemented", result}
end
@impl AI.Tools
def tool_call_failure_message(_args, reason), do: reason
@impl AI.Tools
def spec do
%{
type: "function",
function: %{
name: "coder_tool",
description: """
AI-powered, multi-phase code change orchestration.
This tool is for large, complex code changes that span many files.
Use this tool for "epics", not "stories".
Use for:
- Architectural refactoring
- Changes spanning multiple files or modules
- Any ambiguous or partially-specified requirement
- Tasks demanding robust planning and post-edit validation
NOT for quick, precise, atomic line edits; use `file_edit_tool` for those!
""",
parameters: %{
type: "object",
required: ["requirements"],
additionalProperties: false,
properties: %{
requirements: %{
type: "string",
description: """
Detailed requirements for the code change:
- Purpose of the change
- Concrete functionality to be implemented
- Clear acceptance criteria
- Never use the term "refactor"; the LLM *will* misinterpret it as a license to rewrite everything
"""
}
}
}
}
}
end
@impl AI.Tools
def call(args) do
with {:ok, requirements} <- AI.Tools.get_arg(args, "requirements"),
{:ok, state} <- code_stuff(requirements) do
{:ok, summarize_outcome(state)}
end
end
# ----------------------------------------------------------------------------
# Internal State
# ----------------------------------------------------------------------------
defstruct [
:requirements,
:steps,
:task_list_id,
:changes
]
@type t :: %__MODULE__{
# Requirements for the overall change
requirements: binary,
# Number of *implementation* steps taken
steps: non_neg_integer,
# ID of the task list managing this change
task_list_id: binary,
# Summary of changes made, one per implementation step
changes: [binary]
}
defp code_stuff(requirements) do
%__MODULE__{
requirements: requirements,
steps: 0,
changes: []
}
|> plan()
end
defp plan(state) do
AI.Agent.Code.TaskPlanner
|> AI.Agent.new()
|> AI.Agent.get_response(%{
request: state.requirements
})
|> case do
{:ok, task_list_id} ->
%{state | task_list_id: task_list_id}
|> implement()
other ->
other
end
end
defp implement(state) do
AI.Agent.Code.TaskImplementor
|> AI.Agent.new()
|> AI.Agent.get_response(%{
task_list_id: state.task_list_id,
requirements: state.requirements
})
|> case do
{:ok, changes} ->
%{state | steps: state.steps + 1, changes: [changes | state.changes]}
|> validate()
other ->
other
end
end
defp validate(%{steps: steps} = state) when steps >= @max_steps do
{:ok, state}
end
defp validate(state) do
AI.Agent.Code.TaskValidator
|> AI.Agent.new()
|> AI.Agent.get_response(%{
task_list_id: state.task_list_id,
requirements: state.requirements,
change_summary: state.changes |> Enum.join("\n")
})
|> case do
{:ok, :validated} ->
{:ok, state}
{:error, :issues_identified} ->
[latest | prior] = state.changes
latest = """
#{latest}
NOTE: VALIDATION IDENTIFIED PROBLEMS WITH THIS ITERATION.
PLEASE ADDRESS THE ISSUES BEFORE PROCEEDING.
"""
%{state | changes: [latest | prior]}
|> implement()
other ->
other
end
end
defp summarize_changes(%{changes: changes}) do
changes
|> Enum.reverse()
|> Enum.with_index(1)
|> Enum.map(fn {change, idx} -> "## Step #{idx}\n#{change}" end)
|> Enum.join("\n\n")
end
defp summarize_outcome(%{steps: steps} = state) when steps >= @max_steps do
"""
# Result
#{Services.Task.as_string(state.task_list_id, true)}
# Change Summary
Changes were applied in #{state.steps} implementation steps.
#{summarize_changes(state)}
# Outcome
The change was not completed because it exceeded the maximum number of
implementation steps (#{@max_steps}).
This is likely due to overly broad or ambiguous requirements or a
logical inconsistency in the requested change.
The changes made here were not reverted! You must review the current
state of the code to understand what has been done so far.
Once you have refined the requirements, you can re-run the tool to
continue the work.
"""
end
defp summarize_outcome(state) do
"""
# Result
#{Services.Task.as_string(state.task_list_id, true)}
# Change Summary
Changes were applied in #{state.steps} implementation steps.
#{summarize_changes(state)}
# Outcome
All changes have been successfully implemented and validated.
"""
end
end