- Emacs Lisp 100%
|
sczi
66dde4bfa2
fix scrolling
if point is at end of buffer, it should scroll with output sometimes point would get moved back to the start of some block - `delete-region` over the streamed block could collapse point back to that block’s start. can now remove the goto point max when it shows the prompt, as we correctly scroll if point was already at the end, and if point wasn't it's because the user was looking at something else and doesn't want to jump to the prompt |
||
|---|---|---|
| screenshots | update session_with_diff screenshot | |
| .dir-locals.el | minor edits | |
| .gitattributes | add README | |
| .gitignore | initial opencode-mode | |
| AGENTS.md | replace emacsclient with emacs_eval_elisp tool | |
| LICENSE | start | |
| opencode-api.el | opencode-compact-session | |
| opencode-common.el | buttonize file references with line ranges | |
| opencode-convert.el | opencode-copy-conversation | |
| opencode-diff-parser-test.el | parse diffs and return line ranges modified | |
| opencode-diff-parser.el | parse diffs and return line ranges modified | |
| opencode-flycheck.el | opencode-report-diagnostic for finished edit hooks | |
| opencode-format-tool-calls.el | allow custom tool formatters | |
| opencode-permission.el | flycheck integration | |
| opencode-question.el | flycheck integration | |
| opencode-sessions.el | fix scrolling | |
| opencode.el | refactor: opencode--with-session-buffer | |
| README.org | opencode-copy-conversation | |
Emacs OpenCode UI
An emacs UI to opencode - an open source AI coding agent. This provides two sets of advantages over the official TUI:
-
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, markdown-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,
comint-kill-output-to-kill-ring, 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. -
Emacs integration
-
runs
opencode-file-edited-functionsafter edit tool calls. The default hooks will:- fix parentheses with parinfer if
parinfer-rust-modeis installed after edits to emacs lisp and common lisp files - reload file after edits to emacs lisp files
- fix parentheses with parinfer if
-
calls
opencode-files-finished-editing-functionswith a list of all files edited in the last assistant turn. The default hook will fix indentation withindent-regionand run flycheck and report all errors and warnings in the edited files to the LLM if you have flycheck setup.You can set project specific hooks in
opencode-project-files-finished-editing-functions. We includeopencode-uv-pytestandopencode-emacs-ertwhich will run all tests from pytest and ERT and report errors to the LLM. They are defined usingopencode-run-command-diagnosticwhich 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 tosafe-local-variable-directoriesand then create a.dir-locals.elin 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 - integration with magit is possible,
opencode-new-worktreewill 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-expandin 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 depends on magit, plz, plz-media-type, plz-event-source, and markdown-mode.
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 (as of Emacs 30+)
(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.
Session Management
/shihpin/opencode.el/media/branch/main/screenshots/session_management.png
Here you see a table view with information on all sessions for a given project.
Shortcuts to act on the session at point:
| Key | Description |
|---|---|
x |
kill session |
R |
rename session |
s |
share session, copies a URL to the clipboard that will give a read-only view of the session |
u |
unshare session |
RET or o |
open the session |
Buffer-wide shortcuts:
| Key | Description |
|---|---|
n |
new session |
M |
toggle MCP - fuzzy completes on all available MCPs |
U |
unshare all shared sessions for this project |
v |
toggle verbose - whether to show subagent sessions |
Chat Sessions
/shihpin/opencode.el/media/branch/main/screenshots/session.png /shihpin/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.
| Key | Description |
|---|---|
C-c C-y |
yank code block at point |
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 |
select a buffer to add to context |
M-x yank-media |
add an image from clipboard to context |
C-c s |
share session - copy a URL to the clipboard with a read-only view of the session |
C-c u |
unshare session |
C-c U |
unshare all sessions |
C-c m |
select model |
C-c v |
select model variant |
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 D |
delete a message (user prompt and assistant response) from context |
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. |
| 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. (fn &key TITLE CALLBACK) |
| 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. |
| opencode-add-buffer-dwim | Add current buffer or prompt for one to add. |
Question Tool
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.
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.
Configuration
opencode-commandandopencode-serve-commandcan be used to configure how to run a headless opencode serveropencode-hostandopencode-portare the default host and port to use withM-x opencode-connectopencode-server-usernameandopencode-server-passwordconfigure how to authenticate to the opencode server- the
opencode-facesgroup configures faces used when displaying sessions opencode-toast-functionis used to show toast notificationsopencode-file-edited-functionsis a hook run after edit tool calls=opencode-files-finished-editing-functions=is a hook run after the assistant response finishes, with all files that it editedopencode-project-files-finished-editing-functionsis 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 LLMopencode-session-deleted-functionsis a hook run when a session is deletedopencode-tool-formatterscan be used to control how tools are displayed