For some months now I have been working on my own take on the “terminal based coding agent”. It started out as a learning project, but has since progressed to the point where I use it every day for my professional work as well.
Unlike most other such recent programs like Claude Code, CodexCLI, etc. the program does not provide a Curses style user interface. Instead my inspiration is the interactive read eval print loop offered by programming languages such as Scheme and Common Lisp. More prosaically, it’s also similar to an interactive UNIX shell plus Lisp-style extension command. The entire “UI” (such as it is) is just text.
In general I find that the REPL metaphor, which is inherently language based, is more interesting and powerful than the “text user interface” (TUI) m…
For some months now I have been working on my own take on the “terminal based coding agent”. It started out as a learning project, but has since progressed to the point where I use it every day for my professional work as well.
Unlike most other such recent programs like Claude Code, CodexCLI, etc. the program does not provide a Curses style user interface. Instead my inspiration is the interactive read eval print loop offered by programming languages such as Scheme and Common Lisp. More prosaically, it’s also similar to an interactive UNIX shell plus Lisp-style extension command. The entire “UI” (such as it is) is just text.
In general I find that the REPL metaphor, which is inherently language based, is more interesting and powerful than the “text user interface” (TUI) model which is much more limited. The latter brings to mind the green screen terminal programs which were historically used for data entry / form processing, and which guide the user through a more specific process or workflow that the software designer has prepared for them in advance. Although it can be very efficient for specific use cases, it removes flexibility and power from the end user in exchange for that.
Although it must be said in fairness that many of the modern terminal based coding agents also provide REPLs in the broader sense, I still don’t like the paradigm very much due to the intertwining of “user interface drawing boxes” and “interactive shell language” which in my view they conflate, to the detriment of both UI design and language design. For example, imagine that in order to use POSIX shell, you had to adopt some specific curses TUI with it? This conflation of concerns is a real design error IMO.
Another issue I have with the terminal based coding agents is that, having checked out the codebases and poked around a bit , many of them have a problem with Way Too Much Code ™ for my taste. When last I looked at Codex CLI, it had approximately 60,000 lines of code. I honestly don’t know what all that code is doing. No doubt it is serving some need that OpenAI has for the program in terms of enterprise integrations or features or whatever. But I am not really excited about running 60k lines of code (given the concomitant bugs to lines of code ratio that has been shown to be fairly constant over the years) in order to talk to some APIs and do a bit of processing on the output.
In general, when dealing with problems like this, I would much rather write my own smaller program that does just what I need and has UX that is to my taste. In this case in particular, it is a requirement that the UX be “text only” since I primarily live in and interact with the computer through Emacs (aside from the mandatory web browser). My experience has been that it is generally very easy to integrate things that speak plain text into Emacs in useful ways. I already have a “user interface” for any such program via an Emacs buffer that I control, so I have little interest in giving up that surface in exchange for some curses TUI someone else designed for their “average user”.
Further, years of experience interacting with various Lisp environments which has given me strong opinions about the overall UX I’d like, the types of “meta commands” that should be available, and how they should fit together in an integrated way. For example, I should be able to select any arbitrary text in any arbitrary buffer in Emacs and send it to the REPL subprocess for evaluation (whatever “evaluation” may mean in the context of that process)
I also want to be able to make a distinction in my work with the assistant between turn-based chat interactions vs explicitly asking for an agent loop to perform some task for me. There are times when I want more control over the interaction, and times when I want the assistant to “just do it for me”. As far as I can tell, tools like Codex and Claude Code assume that I always want the latter type of interaction.
From the above you can see that some of my primary requirements for SYNERGY were/are:
- plain text only for input/outout
not too much code, small and hackable for my needs
solid integration with Emacs (or Vim; I haven’t written that code but it should be easy for a Vimmer to write)
provides UX reminiscent of Lisp REPLs, with useful set of “meta commands”
makes a distinction between turn-based request/response interactions, and explicitly invoking agent loops
I’m writing about this here to share design ideas that may be interesting to someone in the context of REPL design. Nothing here is an original idea. These are all transparent rehashes of design ideas that have already been in use for a long time. Nonetheless I hope it’s useful to share them. I may write more about this in the future.
NB. There is a GitHub repo that I update sporadically in a “source code drop” fashion. It’s pretty much always out of date vs what I actually run. It’s not designed for anyone else to actually use, and I’m not really interested in contributions. What I am interested in is sharing some of the design ideas.
What follows is the current documentation taken from the SYNERGY source code as of 2026-01-24. It’s incomplete and also doesn’t explain everything as well as I’d like, but it’s a start.
From https://jem.fandom.com/wiki/Synergy
Synergy is a highly technologically advanced artificial intelligence. She can understand what others say and still have her own opinions.
Overview
Synergy is a REPL for talking to AI assistants. It is designed for both interactive and non-interactive use from the command line.
There are two “modes” for interacting with Synergy: interactive mode, which provides a REPL; and non-interactive mode, which is when you’re talking to Synergy over a pipe.
Interactive mode (REPL)
If you go to the Terminal and start C<synergy>, a prompt appears. You can type questions at the prompt, and they will be sent to the AI assistant. The assistant’s responses will be printed out. For example:
$ synergy
USER > i am writing perl documentation in the POD format (a README), how do i represent a quotation / blockquote?
(1741177286.90149) thinking …
In Perl’s POD (Plain Old Documentation) format, you can represent a quotation or blockquote using the =begin and =end blocks with the text format. Here’s an example: …
Synergy also accepts special commands starting with a comma. These commands are not sent to the AI assistant. Instead, they control the state of the REPL. For example, you can add a text file into the context of the current conversation, or print out the estimated number of tokens used so far.
When you add a file to the context, it is stored in a stack. There are several stack manipulation commands for pushing, popping, rotating, etc.
Non-interactive mode (input via pipe)
The simplest way to talk to Synergy is via one-shot commands over a pipe
$ echo “Please write a haiku in the style of Li Po about what it’s like to be an artificial intelligence” | synergy Here’s a haiku in the style of Li Po, capturing the essence of an AI’s experience:
Moonlight of data Flowing through circuits of thought No heart, yet I dream
…
Command processor commands
The complete list of special commands is documented in the interactive help output.
$ synergy USER > ,help This is Synergy. You are interacting with the command processor. A command is either a prose form for the Assistant to evaluate or one of the following: ,help ,? Print this help message ,history Print out the conversation history so far ,reset [N] Discard conversation history (A B C – ) – With no args: discard entire history – With arg N: reset history to position N ,collect Replace current conversation history with a summary ,s Print contents of file context stack ,push file.txt Push file.txt onto file context stack (B C – A B C) ,swap Swap top two elements of file context stack (A B – B A) ,rot Rotate file context stack entries (A B C – B C A) ,drop [N…] Remove elements of file context stack: – With no args: pop top stack element (A B C – B C) – With arg N: remove element at index N (A B .. N – A B ..) ,peek N View contents of Nth element of file context stack ,dump file.xml Save session history (including current file context stack, if any) to disk ,load file.xml Load session history (including file context stack, if any) from disk ,pwd Print current working directory ,cd /some/dir Change working directory to /some/dir ,exec cmd Execute shell command (grep sed wc ls file find cat make head) Output is saved to conversation history ,apply_patch file.txt ‘<<<<<<< ORIGINAL
foo
bar
>>>>>> UPDATED’ Apply patch in diff-fenced edit format to file.txt ,comment Write a comment that is printed back to the REPL’s output. This is useful for notes to self by humans or AI agents. ,model [name] Print (or switch) the current model – With no args: Print the current model and the list of options – With name (e.g.,
gpt-5): Switch to model with that name ,tokens Print the approximate token count in the current conversation history ,encoded Base64 encode contents of API calls to the Assistant (default ON) ,exit Quit the SYNERGY command processor
Setup
The synergy program is written in Perl, and uses a number of libraries that need to be installed. For now, see the source code for details.
You will need to provide an API key:
$ export LANGERTHA_ANTHROPIC_API_KEY=”ABC123…”
Emacs integration
There is a file inf-synergy.el in this directory that provides some niceties for interacting with Synergy from Emacs, including:
REPL interaction
- M-x run-synergy: Start or switch to a Synergy chat buffer.
C-x C-a y: Quick shortcut to create or switch to Synergy buffer.
Once in the REPL, the following features are available:
- C-c C-n: Move forward through commands.
C-c C-p: Move backward through commands.
S-return: Insert newline without sending input.
RET: Send input to Synergy.
Note that the Synergy REPL will highlight any single-line inputs to the REPL that are longer than 999 characters in bright red starting at the 1000th character.
This is necessary because the maximum length of string a comint-based REPL will accept without hanging with ‘^G’ (control-G) characters is 1005 based on my testing. (This applies to both older and more recent Emacs versions, and seems to be controlled by the process interaction layer at the C code level and not user-configurable.)
File context management
- M-x synergy-push: Pushes files to Synergy’s file context stack. Pushes current region if a region is selected. If no region is selected, pushes the entire buffer. In Dired mode, pushes marked files or just the file at point if nothing is marked.
Code block handling
- C-x M-w: Copy code block from Markdown-formatted text.
Credits
Credit for the design goes to:
Scheme48: The command processor “look and feel”, as well as the special command syntax and help output, are directly taken from Scheme48.
Forth: The file context stack manipulation commands are inspired by the Forth programming language.
I’m the author of ‘Jelec: the White Bear, or, Beware an Encounter with a Raven and his Friends.’ For my day job, I work as a technical writer at a software company. View all posts by logicgrimoire