Packages
fnord
0.8.0
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/codex.ex
defmodule AI.Tools.Codex do
@min_version "0.3.0"
@model AI.Model.smart()
@prompt """
IMPORTANT: You must follow these constraints exactly.
1. NEVER modify any code outside the specified location or region.
2. NEVER add new functions, modules, files, or dependencies unless explicitly instructed.
3. NEVER move existing code.
4. NEVER delete or rewrite any surrounding code, even for "cleanup".
5. NEVER include trailing or unrelated code fragments (e.g. duplicated definitions, helper functions, or stray tokens).
6. NEVER insert code *outside* the defined structural scope (e.g., beyond the bounds of a function, class, or module).
7. DO NOT attempt to "help" by reformatting unrelated parts of the file.
8. DO NOT assume or infer types, imports, or external definitions unless they are present in the surrounding context.
9. If placement is ambiguous, return an error or request clarification instead of guessing.
10. All inserted code MUST be valid, self-contained, and syntactically correct.
11. All inserted code MUST preserve indentation and formatting consistent with the surrounding code.
12. If an error occurs that prevents you from completing the task (such as a tool call that cannot be invoked without approval), respond with a *clear* error message, informing the user of exact nature of the issue.
Violating any of these constraints is considered a failure.
"""
@behaviour AI.Tools
@impl AI.Tools
def is_available?() do
cond do
!codex_installed?() ->
Once.warn("""
Codex CLI not installed.
Editing tools are disabled.
""")
false
!has_min_version?() ->
Once.warn("""
Codex CLI version is too old.
Minimum required version is #{@min_version}.
Editing tools are disabled.
""")
false
true ->
true
end
end
defp codex_installed? do
"codex"
|> System.find_executable()
|> is_nil()
|> Kernel.not()
end
# outputs: `codex-cli 0.3.0`
defp has_min_version? do
case System.cmd("codex", ["--version"]) do
{output, 0} ->
output
|> String.trim()
|> String.split(" ")
|> case do
[_, version] -> Version.compare(version, @min_version) != :lt
_ -> false
end
_ ->
false
end
end
@impl AI.Tools
def ui_note_on_request(%{"steps" => steps}) do
steps =
steps
|> Enum.map(fn %{"file" => file, "change" => change} ->
"- #{file}: #{change}"
end)
|> Enum.join("\n")
{"Implementing change plan", steps}
end
@impl AI.Tools
def ui_note_on_result(%{"steps" => steps}, result) do
steps =
steps
|> Enum.map(fn %{"file" => file, "change" => change} ->
"- #{file}: #{change}"
end)
|> Enum.join("\n")
{"Changes implemented",
"""
# Steps
#{steps}
# Result
#{result}
"""}
end
@impl AI.Tools
def read_args(args), do: {:ok, args}
@impl AI.Tools
def spec() do
%{
type: "function",
function: %{
name: "codex",
description: """
Assign a coding task to an AI agent (OpenAI's codex tool).
The agent can write code in multiple languages, run tests, as well as edit and create files.
YOU **MUST** MANUALLY DOUBLE-CHECK THE AGENT'S WORK YOURSELF BY READING IT.
Treat this tool like a junior dev that requires careful guidance and review.
YOU are the guard rails.
""",
parameters: %{
type: "object",
required: ["steps"],
properties: %{
steps: %{
type: "array",
description: """
A list of steps to be executed by the AI agent.
Each step should be a small, self-contained change to a single file.
The AI will execute these steps in order, so ensure they are logically sequenced.
Each step should be a single, small change in a single file.
Use this to break down complex tasks into manageable parts.
""",
items: %{
type: "object",
required: ["file", "change"],
properties: %{
file: %{
type: "string",
description: """
The path to the file to be modified.
This should be relative to the project root.
Ensure the file exists in the project.
"""
},
change: %{
type: "string",
description: """
A concise description of the small, discrete change to be made in the specified file.
NEVER use open-ended terms like "refactor" or "improve" without specifying EXACTLY what you want to change.
This should be a clear, concise instruction for the AI agent.
"""
}
}
}
}
}
}
}
}
end
@impl AI.Tools
def call(%{"steps" => steps}) do
with {:ok, msg} <- perform_steps(steps) do
step_list =
steps
|> Enum.map(fn %{"file" => file, "change" => change} ->
"- #{file}: #{change}"
end)
|> Enum.join("\n")
{:ok,
"""
Codex completed execution of the requested steps:
#{step_list}
-----
#{msg}
-----
**IMPORTANT**
Read the updated file(s) carefully!
Codex tends to take the easiest route, without considering the whole.
Half the time, it doesn't even DO the work you asked it to.
There are VERY LIKELY to be problems with its implementation.
YOU are responsible for identifying them and correcting them.
"""}
end
end
defp perform_steps([]) do
{:ok, "All steps completed successfully."}
end
defp perform_steps([%{"file" => file, "change" => change} | remaining]) do
with {:ok, output} <- perform_step(file, change) do
UI.info("Step completed", """
File: #{file}
Change: #{change}
-----
#{output}
""")
perform_steps(remaining)
else
{:error, output} ->
UI.error("Step failed", """
File: #{file}
Change: #{change}
-----
#{output}
""")
{:error, output}
end
end
defp perform_step(file, change) do
with {:ok, project} <- Store.get_project() do
prompt = """
#{@prompt}
-----
File: #{file}
Instructions:
#{change}
"""
System.cmd("codex", [
"exec",
"--cd",
project.source_root,
"--skip-git-repo-check",
"--full-auto",
"--sandbox",
"workspace-write",
"--config",
"disable_response_storage = true",
"--model",
@model.model,
prompt
])
|> case do
{output, 0} -> {:ok, output}
{error_output, _} -> {:error, error_output}
end
end
end
end