Emacs interface to opencode
  • Emacs Lisp 99.8%
  • Shell 0.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
sczi 213a6cea0a fix warning, and minor performance improvement
don't add to buffer-undo-list when replaying past session messages

it gave the warning:
combine-change-calls: buffer-undo-list has been truncated

**Cause:** Your Doom config has advice on `comint-output-filter` (`~/.config/emacs/lisp/doom-emacs.el:1477`) that sets `buffer-undo-list` to nil after comint output. The replay runs inside `combine-change-calls`, which needs the undo list it saved at the start to still be there at the end. Doom's reset wiped it, so it printed the warning.
2026-11-13 20:26:59 -06:00
screenshots update session_with_diff screenshot 2026-04-19 22:43:31 -04:00
.dir-locals.el font lock as markdown-ts-mode 2026-09-29 19:16:55 -04:00
.gitattributes add README 2026-01-14 16:01:57 -05:00
.gitignore initial opencode-mode 2025-12-25 18:05:06 -05:00
AGENTS.md reorganize code 2026-11-06 16:18:18 -05:00
Bugs.org document known external bugs 2026-11-12 11:50:59 -05:00
LICENSE start 2025-12-25 17:27:27 -05:00
opencode-api.el add 5h and session usage information for claude 2026-11-12 13:50:52 -05:00
opencode-common.el %r for reporting subscription reset time 2026-11-13 09:10:05 -06:00
opencode-convert.el format json tool output 2026-11-07 19:40:32 -05:00
opencode-diff-parser-test.el port tests to v2, add event tests, update readme 2026-11-05 15:44:05 -05:00
opencode-diff-parser.el parse diffs and return line ranges modified 2026-05-27 20:23:55 -04:00
opencode-edit-hooks.el finished editing hooks uses session diff 2026-11-10 22:26:38 -05:00
opencode-ert.el fix opencode-open-project outside of git repo 2026-11-13 19:49:07 -06:00
opencode-flycheck.el fix opencode-files-finished-editing-functions 2026-11-06 17:47:08 -05:00
opencode-format-tool-calls.el Skip directory local variables when diffing edits 2026-11-06 20:27:23 -05:00
opencode-forms.el support non-question forms 2026-11-10 18:33:02 -05:00
opencode-permission.el reorganize code 2026-11-06 16:18:18 -05:00
opencode-session-control.el fix opencode-open-project outside of git repo 2026-11-13 19:49:07 -06:00
opencode-session-input.el cycle reasoning levels 2026-11-12 21:39:48 -06:00
opencode-session-output.el fix warning, and minor performance improvement 2026-11-13 20:26:59 -06:00
opencode-sessions-test.el fix warning, and minor performance improvement 2026-11-13 20:26:59 -06:00
opencode-sessions.el default opencode-overflow-format 2026-11-13 18:12:10 -06:00
opencode.el fix opencode-open-project outside of git repo 2026-11-13 19:49:07 -06:00
README.org opencode-fix-ert autofix ERT test failures 2026-11-13 18:32:19 -06:00
run-ert-tests.sh ERT test runner script 2026-09-29 20:05:01 -04:00

Emacs OpenCode UI

An emacs UI to opencode - an open source AI coding agent. This provides two sets of advantages over the official TUI:

  1. A better TUI and GUI

    Emacs is a mature TUI and GUI framework, that while janky in its own way, is far less janky than the TUIs the new agentic coding tools have written from scratch. This package builds on a solid foundation of comint, vtable, diff-mode, hideshow, markdown-ts-mode, emacs' completion system, and more, to offer a (IMO) nicer UI. Also if you're an emacs user, the UI is more consistent: goto next or previous prompt, history search, and everything else works the same as in any other repl or shell based on comint mode, completion and filtering works the same as everywhere else in emacs, and everything is just a text buffer where all your usual editing and other commands work as expected.

  2. Emacs integration

    • runs opencode-file-edited-functions after edit tool calls. The default hook will reload file after edits to emacs lisp files. You can also add opencode-fix-parens to fix parentheses with parinfer if parinfer-rust-mode is installed after edits to lisp files.
    • calls opencode-files-finished-editing-functions with a list of all files changed in the last assistant turn, including by shell commands, according to the server's snapshot diff, so it requires a git repository and gitignored files aren't included. Add opencode-indent-files here to automatically fix indentation with indent-region, and opencode-flycheck-edited-files to run flycheck and report all errors and warnings in the edited files to the LLM.

      ⚠️ WARNING: opencode-fix-parens, opencode-indent-files, and opencode-flycheck-edited-files are all disabled by default, but if you use them they call revert-buffer, discarding changes in emacs in favor of llm edits. I use git worktrees or just stash, if I am editing at the same time as an llm.

      You can set project specific hooks in opencode-project-files-finished-editing-functions. We include opencode-uv-pytest and opencode-emacs-ert which will run all tests from pytest and ERT and report errors to the LLM. They are defined using opencode-run-command-diagnostic which you can use to run any shell command and report errors to the LLM. To set up a python project to run pytest after LLM edits add your project directory to safe-local-variable-directories and then create a .dir-locals.el in the project directory with:

      ((nil . ((opencode-project-files-finished-editing-functions . (opencode-uv-pytest)))))
    • add any emacs buffer to chat context with opencode-add-buffer-dwim
    • add files from dired to context with opencode-add-file-dwim
    • add selected region to context with opencode-add-region
    • add function at point to context with opencode-add-defun
    • start a new session to fix all flycheck errors in the current buffer with opencode-fix-flycheck
    • start a new session to diagnose and fix failed tests from the ERT results buffer with opencode-fix-ert, bound to o in ert-results-mode. By default it will diagnose all failed tests, but with a prefix argument will just diagnose the test at point.
    • integration with magit is possible, opencode-new-worktree will create a new git branch and worktree for the current project, and start an opencode session in it
    • `file` and file:line references in LLM output are turned into clickable buttons
    • use dabbrev-expand in the chat window to complete long variable or function names from your code buffers
    • configure how your custom MCP and tools are displayed with opencode-tool-formatters
    • I use with eval-elisp-mcp when developing in elisp.
    • For python development integrated with opencode.el, with features like AI autodebugging exceptions, check out Swanky Python

Demo

A brief demo showing starting opencode, viewing a past session, sharing it, toggling an MCP, selecting the agent, model, and variant, performing some edits, receiving a notification when the response is finished if the session buffer isn't focused, reverting edits, and forking the session.

Installation

When this package is a bit more stable I'll package it on MELPA, for now install from git. This package requires Emacs 31.1 with the markdown and markdown-inline Tree-sitter grammars (install with M-x markdown-ts-mode-install-parsers), plus magit, plz, plz-media-type, and plz-event-source.

Manual

Clone this repository and add it to your load path.

(add-to-list 'load-path "/path/to/opencode.el")
(require 'opencode)

With straight.el

(use-package opencode
  :straight (opencode :type git :host codeberg :repo "sczi/opencode.el"))

Emacs use-package

(use-package opencode 
  :vc (:url "https://codeberg.org/sczi/opencode.el.git" :rev :newest))

In Doom Emacs

add to packages.el and run doom sync

(package! opencode
  :recipe (:host codeberg :repo "sczi/opencode.el"))

Connecting

Automatic

M-x opencode will try to connect to an opencode server at opencode-host:opencode-port or will start a headless opencode server if one isn't already running. Then it opens a Session Management buffer for the current project.

Manual

M-x opencode-connect will prompt for the host and port of an opencode server to connect to.

Project Management

In opencode, sessions are grouped by project, where each git repository is a different project. Different git worktrees for the same repository will be grouped in the same project. Sessions started in directories outside of any git repository will go in the global / project. opencode-select-project will fuzzy complete on all projects and open the Session Management buffer for the project you select. When creating a new git repository or adding a git remote, you need to run opencode-reload-locations for the OpenCode server to pick up the changed project.

Session Management

/sczi/opencode.el/media/branch/main/screenshots/session_management.png

Here you see a table view with information on all sessions for a given project. You can configure what columns are shown with opencode-session-control-table-columns.

With a claude subscription plan, the cost column will show an estimate in dollars at API pricing, and show the estimated percent of the 5 hour limit a session has used.

Below the table is the actual usage of your 5 hour and weekly limits, as last reported by Claude Code. Until a claude-agent session makes a request, it is read from the most recent one, so can under report if you use claude outside of opencode.

PRs are welcome to add this information for providers of other subscription plans you may have.

Shortcuts to act on the session at point:

Key Description
x kill session
R rename session
RET or o open the session

Buffer-wide shortcuts:

Key Description
n new session
M toggle MCP - fuzzy completes on all available MCPs
v toggle verbose - whether to show subagent sessions, as a tree under their parent

Chat Sessions

/sczi/opencode.el/media/branch/main/screenshots/session.png /sczi/opencode.el/media/branch/main/screenshots/session_with_diff.png

This is what session buffers look like. In the modeline at the bottom right we see the current agent, current model, model variant if one is selected (like low/medium/high thinking level), the percent of context used up so far, and an indicator for if the session is idle or busy. User prompts are shown with a blue margin highlight, information about tool calls in green, and reasoning in brown. These colors can be configured through the opencode-*-margin-highlight faces.

Reasoning, tool output, and the diffs of edits are folded with hs-minor-mode, leaving their first line visible. Click the ellipsis or use hs-toggle-hiding (C-c @ C-c) on the first line to expand them, and hs-show-all (C-c @ C-M-s) to expand everything.

Key Description
C-c C-y yank code block at point
C-c y yank-media, adds an image from clipboard to context
C-c Y Copy the full conversation as a Markdown transcript, including reasoning and tool outputs.
Useful to gather context in OpenCode, and then iterate on a plan in webchat if webchat has separate usage limits.
C-c C-c cancel the current request
C-c C-p respond to a permission request
C-c x delete this session
TAB cycle through available primary agents
C-c r rename this session
C-c n start a new session in the same project
C-c l list and select from all sessions in the same project
C-c c fuzzy select a child session to open - children are all subagent sessions spawned by this session
C-c C compact session
C-c t scroll to the top of the text part of the last assistant response
C-c p open parent session - when in a subagent session, open the session that spawned it
C-c f select a file to add to context, if called with a prefix argument then select a directory
C-c b /btw — ask a side question about the conversation without adding to it, the answer opens in a new buffer
C-c B select a buffer to add to context
C-c m select model
M-TAB cycle to the next reasoning level
M-S-TAB cycle to the previous reasoning level
C-c M select an MCP to toggle
C-c F fork session from message at point - use this when a model responds badly to avoid polluting context with the bad response
C-c R revert all edits done by this session after the message at point
/ when at the start of a user prompt, select a slash command to run
@ reference a sub-agent

Notifications

If a session buffer and frame is not active, emacs will show a notification when that session's response has finished, so that you can go work on other stuff and be notified when the agent has finished.

Commands

Command Description
opencode Open opencode sessions control buffer for the current project directory. Connects to an existing server if one is running, otherwise starts a new one if ‘opencode-auto-start-server’ is non-nil.
opencode-connect Connect to opencode server, prompting for HOST and PORT. Call ON-CONNECT once connected.
opencode-disconnect Disconnect from opencode server, optionally log EVENT.
opencode-select-project Completing read to prompt which project to select.
opencode-select-open-session Select among open session buffers.
opencode-new-worktree Create a new git branch, and worktree prompting for a name. Then open an opencode session in it.
opencode-new-session Create a new session. With a prefix argument it will ask for TITLE. Without it will use a default title and then automatically generate one. If CALLBACK is given, it will be called with the session after it is created. If INITIAL-PROMPT is given, it is sent after CALLBACK is called. (fn &key TITLE CALLBACK INITIAL-PROMPT)
opencode-select-idle Select a session that hasn’t been visited since it went idle.
opencode-visit-last-idle Open the most recent session to notify as idle.
opencode-add-file-dwim If in Dired, add all marked files, or file at point if none marked. Otherwise add the current buffer’s file. Otherwise prompt for file in current project.
opencode-add-region Add the active region to context. If the buffer visits a file, the region is sent with its file name and line numbers.
opencode-add-defun Add the defun at point to context, as a region. If the buffer visits a file, the defun is sent with its file name and line numbers.
opencode-add-buffer-dwim Add current buffer or prompt for one to add.
opencode-fix-flycheck Start a new opencode session to fix the current buffer’s Flycheck errors.
opencode-reload-locations Make the server resolve the project of each directory again. Run this after a directory’s project changes, such as on ‘git init’, its first commit or adding a remote.
opencode-unrevert-all Unrevert all reverted messages in the current session.
opencode-fix-ert Start a new opencode session to diagnose and fix ERT test failures. Uses all unexpected results in the current ERT results buffer. With a prefix argument AT-POINT, only use the test at point.

Question Tool

/sczi/opencode.el/media/branch/main/screenshots/single_choice_question.png
Single choice question
/sczi/opencode.el/media/branch/main/screenshots/multiple_choice_question.png
Multiple choice question

OpenCode includes a question tool that models can use to ask clarifying questions of the user. If the session buffer is active when the questions are raised, it will immediately prompt each question with options for responses. The options the model provides are suggestions, feel free to type your own answer. If the session buffer or frame is not active at the time, then emacs will notify that a question is awaiting response. You can switch to the session buffer that most recently raised a notification with opencode-visit-last-idle, and as soon as you visit the buffer it will prompt the questions. For single choice questions, selecting an option will immediately submit that response. For multiple choice questions, select the options you want and then press C-c C-c to submit.

Other requests for input from the server, like web search consent and MCP server elicitations, are prompted the same way. Text and numbers are read in the minibuffer. Elicitations aren't tied to a session so they're prompted right away.

Security

Opencode's permission system, especially for bash, is not nearly as fine-grained as claude's. However, I don't think this really matters. Any coding agent with the ability to write and execute code can execute potentially malicious code by design and should be untrusted. One approach I'd recommend is to develop inside virtual machines or at least unprivileged containers managed by incus. If you use btrfs or ZFS for your incus storage pool, then incus copy is almost instant. So you can have a VM with emacs and opencode and your project, but without secrets or sensitive data, and spawn a copy to do agent-assisted development in. When you finish, push the changes to a git repo in a shared folder, and delete the VM. If you only need one copy at a time, you can just snapshot and restore rather than creating and deleting copies. Then from your somewhat more secure and non AI-infected development environment, review and then pull the changes from the shared folder.

opencode.el sets a random password for the opencode server we start, to guard against a history of vulnerabilities in OpenCode's server.

Performance

Make sure you are using plz-event-source version >0.1.5-pre with a major performance fix, and set both comint-scroll-show-maximum-output and comint-move-point-for-output to nil.

Bugs

Known bugs in other packages that affect users of opencode.el are documented in Bugs.org. For bugs in opencode.el itself, see the issue tracker.

Configuration

  • opencode-command and opencode-serve-command can be used to configure how to run a headless opencode server
  • opencode-host and opencode-port are the default host and port to use with M-x opencode-connect
  • opencode-server-password configures how to authenticate to the opencode server
  • the opencode-faces group configures faces used when displaying sessions
  • opencode-toast-function is used to show toast notifications
  • opencode-file-edited-functions is a hook run after edit tool calls
  • opencode-files-finished-editing-functions is a hook run after the assistant response finishes, with all files changed during it
  • opencode-project-files-finished-editing-functions is a buffer local hook run after the assistant response finishes, that you can use for example to run your test suite and report errors back the to the LLM
  • opencode-session-deleted-functions is a hook run when a session is deleted
  • opencode-tool-formatters can be used to control how tools are displayed
  • opencode-shown-outputs lists what to show unfolded: reasoning, tool names like shell or edit, or shell-non-zero to show shell tool output only when the command failed. Shell commands you run yourself are always shown. Shown outputs can still be folded the same way
  • opencode-open-session-same-window controls whether to open sessions in a new window or in the current window
  • opencode-session-control-table-columns configures what columns are shown in the session management buffer
  • opencode-mode-line-process, opencode-header-line-format, opencode-overflow-format and opencode-mode-line-format configure the session information shown in the mode line and header line