{"slug": "coding-agents-moving-from-bash-mimics-to-ast-manipulators", "title": "Coding Agents: Moving From \"Bash Mimics\" to \"AST Manipulators\"", "summary": "A developer replaced the standard text-based editing approach in coding agents with direct Elixir execution, enabling AST-aware refactoring. By giving the model full Elixir execution to edit files, the system eliminates brittle search/replace blocks and reduces token costs. The approach allows zero-context re-reads, localized logic, native multitasking, and dynamic introspection within a single API call.", "body_md": "In the last post, we killed the \"Tool Abstraction\" layer. By replacing 50 brittle JSON-RPC wrappers with a single eeva process (Elixir on the BEAM).\n\nHere is how we moved our agent from text-based hacking to actual AST-aware refactoring.\n\nStandard agents \"edit\" files by outputting search blocks:\n\n```\nPlaintext\n<<<< SEARCH\ndef hello_world, do: \"hi\"\n==== REPLACE\ndef hello_world, do: \"hello elixir\"\n>>>>\n```\n\nThis is fundamentally broken. It relies on the model perfectly hallucinating the exact state of the file, including indentation, hidden newlines, and context. If the file has changed by one space since the last read, the entire operation fails. It is brittle, state-blind, and expensive. Coding agents are trying to work this out by introducing not so easy algorithms for multi-edits. Each time model changes a file, for the next edit it needs to read the file again. If this would be standard flow, it would cost you a huge amount of tokens to make it right. Coding agents try to fix it with a few ninja tricks by keeping last edits in memory and shifting next edits. But model doesnt know about this. Eventually, the simple operation of editing files becomes a guessing game both for model and for the harness.\n\nWe didn't solve this by introducing custom \"edit primitives\" or specialized editing tools. Writing unique tool APIs just forces you back into prompt engineering hell.\n\nInstead, we give the model full Elixir execution to edit files directly. Because the code is the tool, the model can execute a complete chain of multi-edits across multiple files in a single, atomic operation using standard language features.\n\nThe model doesn't output raw diff blocks; it pipes the files through pure functional transformations.\n\n```\nelixir\nconfig_path = \"config/config.exs\"\n\nFile.read!(config_path)\n|> String.replace(\":old_port, 4000\", \":new_port, 8080\")\n|> then(&File.write!(config_path, &1))\n\n\"lib/workspace/\"\n|> File.ls!()\n|> Enum.filter(&String.ends_with?(&1, \"_worker.ex\"))\n|> Enum.each(fn file ->\n  path = \"lib/workspace/#{file}\"\n  File.read!(path)\n  |> String.replace(\"get_port(:old_port)\", \"get_port(:new_port)\")\n  |> then(&File.write!(path, &1))\nend)\n```\n\nBy using the raw programming language as the editing engine, we unlock a few massive architectural advantages:\n\nZero Context Re-Reads\n\nThe model does not need to execute an edit, wait for the harness to update, read the file again, and format a second diff. The state lives inside the Elixir evaluation stream. It can pipe the output of one file read directly into the modification of another, completing complex refactors in a single API call.\n\nLocalized Logic, Less Hallucinations\n\nThe model doesn't need to guess line numbers or spaces. It writes native Elixir filters, regex matches, or map functions to locate exactly what it wants to change. The search logic is executed live by the BEAM runtime, completely removing the brittle dependency on matching precise whitespace layouts.\n\nNative Multitasking\n\nBecause eeva executes inside a supervisor tree, this entire multi-edit code runs inside an isolated, transient BEAM process. If the model makes a logic error mid-stream, the process crashes safely, and the compiler drops the exact structural failure back into the context.\n\nDynamic Introspection\n\nIn a standard text-based setup, the model edits a file, hopes for the best, and then has to call a separate tool to run tests or verify syntax. If the edit broke a dependency three folders over, the agent is blind to it until the entire run crashes.\n\nWith pure Elixir execution, the model can introspect its edits inline before finishing the operation. It can pipe its file mutations directly into the live compiler to verify the changes don't break the system:\n\n```\nelixir\n# Edit the file, then verify the module still compiles in the same pass\nFile.read!(\"lib/math.ex\")\n|> String.replace(\"def add(a, b), do: a + b\", \"def add(a, b), do: b + a\")\n|> then(&File.write!(\"lib/math.ex\", &1))\n\n# Live validation check before the token stream concludes\ncase Code.compile_file(\"lib/math.ex\") do\n  {:ok, _modules} -> \"Success\"\n  {:error, errors} -> \"Failed compilation inline: #{inspect(errors)}\"\nend\n```\n\nTry it out: [https://github.com/beamcore/agent](https://github.com/beamcore/agent)", "url": "https://wpnews.pro/news/coding-agents-moving-from-bash-mimics-to-ast-manipulators", "canonical_source": "https://dev.to/6e2baa41a8b2856/coding-agents-moving-from-bash-mimics-to-ast-manipulators-1fc6", "published_at": "2026-06-21 11:27:11+00:00", "updated_at": "2026-06-21 11:36:49.532324+00:00", "lang": "en", "topics": ["large-language-models", "developer-tools", "ai-agents"], "entities": ["Elixir", "BEAM", "eeva"], "alternates": {"html": "https://wpnews.pro/news/coding-agents-moving-from-bash-mimics-to-ast-manipulators", "markdown": "https://wpnews.pro/news/coding-agents-moving-from-bash-mimics-to-ast-manipulators.md", "text": "https://wpnews.pro/news/coding-agents-moving-from-bash-mimics-to-ast-manipulators.txt", "jsonld": "https://wpnews.pro/news/coding-agents-moving-from-bash-mimics-to-ast-manipulators.jsonld"}}