Lambda Symbolics

Emacs and sly

Autolith's image can be your Common Lisp development image. emacs/autolith.el in the source tree sets up one frame with three windows: your code, a sly REPL connected to Autolith's own image, and an agent-shell conversation with the same Autolith. What you define in the REPL lives beside the agent, which can inspect it and advise.

The conversation occupies the right third of the frame. The sly REPL occupies the bottom quarter of the left pane, with your code above it.

Install sly and agent-shell, load the file, and run M-x autolith-ide from the buffer you are editing:

Emacs Lisp
(load "/path/to/autolith/emacs/autolith.el")

Optional Sly contribs need their Lisp dependencies in Autolith's image. For sly-stepper, install agnostic-lizard in a persistent ASDF source registry visible to the Autolith process before Slynk starts.

autolith-ide starts the Emacs server if needed and launches autolith acp through agent-shell with three environment variables:

  • AUTOLITH_SLYNK_DIRECTORY is your sly's own slynk/ directory, so the server speaks exactly your client's protocol. Autolith loads that slynk.asd by path and compiles it into the ASDF cache on first use.
  • AUTOLITH_SLYNK_PORT_FILE receives the loopback port once Slynk listens, or error: REASON when it cannot start. autolith-ide waits for it, up to autolith-slynk-timeout seconds, and connects sly.
  • AUTOLITH_EMACS_SERVER is this Emacs's server socket and enables the emacs: resources and emacs.* tools.

M-x autolith-agent-shell starts only the conversation, with Emacs access and without Slynk. The same variables, or the :slynk-directory, :slynk-port-file and :emacs-server-socket settings, work outside Emacs. In the terminal, (slynk) starts Slynk and shows the port for M-x sly-connect, and (slynk "stop") closes it.

The agent reaches your definitions with lisp.describe, lisp.source and lisp.apropos using target self, or with self.eval. While Slynk serves, a context note tells it so. Your Emacs appears to the agent only while the configured server socket exists:

  • emacs:current, read with resource.read, lists authorized visible windows with point, the active region and open file buffers, each by its emacs:buffer/NAME URI. Outside-root files and non-file buffers require full-access approval; denied buffers are omitted from this snapshot.
  • emacs:buffer/NAME is a live buffer's text, unsaved edits included, with the buffer name percent-encoded. resource.edit applies line operations to the buffer itself: the change stays unsaved and undoable, and is refused as stale when you edited the buffer since the agent read it. A buffer visiting a file outside the workspace and source roots asks the same permission as that path.
  • emacs.visit shows a file position in a file window of the selected frame.
  • emacs.eval evaluates Emacs Lisp. It asks command authorization for emacs.eval -- FORM and runs only with full access.

Slynk listens on 127.0.0.1 only. Like ordinary sly it has no authentication beyond the optional ~/.sly-secret, so anyone who can reach loopback on this machine can evaluate in the image. Your code shares the agent's heap: a fatal error or (sb-ext:exit) ends the session, which recovers like any crash. Slynk's debugger and output hooks stay off for Autolith's own threads, so your REPL sees only your evaluations. A checkpoint stops Slynk, closing sly connections, and serves the same port again; reconnect with M-x autolith-ide-reconnect. A checkpoint waits for in-flight sly evaluations and fails if they exceed the shutdown timeout.