Development

April 30, 2026 ยท View on GitHub

Getting Started

First follow the installation instructions to install the required prerequisites.

To build Expert, run:

just release

You may point your editor's LSP configuration to the start_expert executable in the generated release:

<your-repo>/apps/expert/_build/prod/rel/plain/bin/start_expert --stdio

Parser Configuration

Expert uses Spitfire as the default parser. To use the built-in Elixir parser instead, set the EXPERT_PARSER env variable to elixir when building the release:

EXPERT_PARSER=elixir just release

Logging

When expert starts up, it creates a .expert directory in the root directory of a project. Inside that directory are two log files, expert.log and project.log. The expert.log log file contains logging and OTP messages from the language server, while the project.log file contains logging and OTP messages from the project's node. While developing expert, it's helpful to open up a terminal and tail both of these log files so you can see any errors and messages that expert emits. To do that, run the following in a terminal while in the project's root directory:

tail -f .expert/*.log

Note: These log files roll over when they reach 1 megabyte, so after a time, it will be necessary to re-run the above command.

Remote Shell

Expert supports a remote shell, which will connect a remote IEx session to a currently-running language server process.

connectionDetails command

The connectionDetails LSP command (via workspace/executeCommand) returns the connection info needed to attach a remote shell:

{
  "nodeName": "expert-manager-core-41110@127.0.0.1",
  "port": 59345,
  "cookie": "expert",
  "epmdModule": "Elixir.XPForge.EPMD",
  "epmdEbinPath": "/path/to/forge/ebin",
  "debugScriptPath": "/path/to/remote_shell.sh",
  "command": "'/path/to/remote_shell.sh' 'expert-manager-core-41110@127.0.0.1' '59345' 'Elixir.XPForge.EPMD' '/path/to/forge/ebin' 'expert'"
}

Editor extensions can use debugScriptPath to spawn a terminal running the remote shell with the connection details as arguments:

<debugScriptPath> <nodeName> <port> <epmdModule> <epmdEbinPath> [cookie]

This will connect you to a remote IEx session inside the language server, where all evaluation happens on the manager node. You can investigate processes, make changes to the running code, or run :observer.

Editor integrations can use these details to launch a remote shell and some of them may already have a builtin integration for it.

If your editor does not include one, you can manually configure it to launch the shell on demand, for example you can add this to your neovim+lsp-config configuration to open a new terminal pane with the remote shell, assuming you have configured the Expert LSP client with the name expert:

vim.api.nvim_create_autocmd("LspAttach", {
  group = vim.api.nvim_create_augroup("kickstart-lsp-attach", { clear = true }),

  callback = function(event)
    local client = vim.lsp.get_client_by_id(event.data.client_id)

    if client.name == "expert" then
      map("<leader>ed", function()
        client:request('workspace/executeCommand', {
          command = 'connectionDetails',
          arguments = {},
        }, function(err, result)
          if err then
            vim.notify('connectionDetails failed: ' .. vim.inspect(err), vim.log.levels.ERROR)
            return
          end

          vim.cmd('belowright split | terminal')
          vim.api.nvim_chan_send(vim.b.terminal_job_id, result.command .. '\n')
        end)
      end, "Expert remote shell")
    end
  end,
})