Exy

Minimal BEAM-native coding-agent substrate.

Exy keeps the model-facing tool surface small:

Everything else is normal Elixir callable from elixir_eval:

First principles

  1. Few tools outside, many BEAM powers inside.
  2. Prefer elixir_eval over shelling out to mix run.
  3. Prefer elixir_ast over grep for Elixir syntax.
  4. Use LSP for diagnostics/navigation, runtime eval for OTP state.
  5. Subagents are OTP processes, not prompt magic.
  6. Self-improvement evolves skills/helpers first, runtime core only with validation.

Examples

Exy.Eval.run("Exy.OTP.runtime_info()")

Exy.AST.run(%{
  action: :search,
  path: "lib/",
  pattern: "def handle_call(_, _, _) do _ end"
})

Exy.Subagents.run_many([
  %{role: :static, goal: "Count modules", run: fn _ -> length(Path.wildcard("lib/**/*.ex")) end},
  %{role: :runtime, goal: "Runtime info", run: fn _ -> Exy.OTP.runtime_info() end}
])

# ChatGPT/Codex OAuth
Exy.Auth.Codex.login()
Exy.Auth.Codex.ensure_fresh()

# Direct LLM call through ReqLLM
Exy.LLM.ask("Summarize Exy's architecture", model: "openai:gpt-4o-mini")

# Elixir API: Jido-backed agent
{:ok, pid} = Exy.start_link(model: "openai:gpt-4o-mini")
Exy.ask(pid, "Use elixir_eval to inspect runtime info")

# CLI / Mix task
#   mix exy
#   mix exy -p "Inspect runtime info"
#   mix exy --eval "Exy.OTP.runtime_info()"
#   mix exy --compact --keep-recent 20
#   mix exy --login codex

# Expert LSP gateway
Exy.LSP.run(%{action: :diagnostics, file: "lib/exy.ex"})

# Ghostty terminal primitive
{:ok, pane} = Exy.TUI.Terminal.start(cmd: "/bin/sh")
Exy.TUI.Terminal.write(pane, "echo hello\\n")
Exy.TUI.Terminal.pump_once(pane)
Exy.TUI.Terminal.snapshot(pane)