README.md

# Exy

Minimal BEAM-native coding-agent substrate.

Exy keeps the model-facing tool surface small:

- `read`
- `edit` / `write`
- `bash` / terminal
- `elixir_eval` → `Exy.Eval.run/2`
- `elixir_ast` → `Exy.AST.run/1`
- `elixir_lsp` → `Exy.LSP.run/1`

Everything else is normal Elixir callable from `elixir_eval`:

- `Exy.OTP` — process, ETS, supervision, runtime introspection
- `Exy.Profile` — `:cprof`, `:eprof`, `:fprof`, process-growth helpers
- `Exy.Subagents` — supervised subagent process runner
- `Exy.Skill` — procedural-memory skill files
- `Exy.Trajectory` — structured event capture for self-improvement
- `Exy.SelfPatch` — development hot-compile helpers
- `Exy.LLM` — direct ReqLLM calls
- `Exy.Agent` / `Exy.Agent.Coding` — Jido.AI ReAct agent over Exy's three Elixir tools
- `Exy.Auth.Codex` — ChatGPT/Codex OAuth login compatible with pi's flow
- `Exy.TUI.Terminal` — minimal Ghostty-backed terminal pane primitive

## 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

```elixir
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)
```