面向 agent 协作开发 的 Emacs 配置 (Emacs 31,macOS)。
- 在后台运行
claude/codex/dsh(DeepSeek Harness) CLI,走结构化协议;对话、工具调用、diff、 审批全部由 Emacs 自己渲染,和代码并排,不嵌入 agent 自带的 TUI。 - agent 能反过来操作 Emacs:打开文件并高亮、读取诊断和编辑器上下文、执行 Elisp, 以及用 Emacs 原生渲染 带表格、图表、流程图、按钮的页面。
- 远程项目 (
/rpc:路径) 里,agent CLI 跑在远程主机上,界面仍在本地 Emacs。 - 支持语言:Rust、Go、Emacs Lisp、Nix、Agda。
用 Homebrew 安装:
| 用途 | 软件 |
|---|---|
| 编辑器 | Emacs 31.1+ (日常用 emacs-plus 31) |
agent → Emacs 的桥 bin/emx | node |
| 基础工具 | git 、 python3 、 ripgrep (C-c r) |
| agent CLI | claude 、 codex ,可选 dsh ;各自完成登录 |
| vterm 首次编译 | cmake |
| 本地 Rust / Go 开发 | rust-analyzer (rustup)、 gopls |
字体: init-ui.el 优先用中英文严格等宽对齐的字体,按顺序取第一个已安装的
Sarasa Term SC 、 Sarasa Mono SC 、 Maple Mono NF CN 。都没有时退回
JetBrains Mono 等英文字体加 LXGW WenKai Mono / PingFang SC,中文会对不齐。
git clone https://github.com/LittleJianCH/emacs.d.git ~/.config/emacsEmacs 启动时会自动加载 ~/.config/emacs 中的配置。
- 插件从 GNU / NonGNU ELPA 和 MELPA 下载;tramp-rpc 通过
:vc从 GitHub 拉取。需要能访问这些站点。 - tree-sitter 语法首次打开对应语言时会询问是否安装,装到
tree-sitter/。 - Emacs server 自动启动,agent 通过它回调 Emacs。
远程文件走 tramp-rpc:用 /rpc:HOST:/path 代替 /ssh:HOST:/path 。
在 ~/.ssh/config 中配好主机即可,例如:
Host dev
HostName <ip>
User <user>
IdentityFile ~/.ssh/id_ed25519_dev
IdentitiesOnly yes
tramp-rpc 的服务端在首次连接时从 GitHub Releases 下载并部署到远程主机。远程主机上需要
agent CLI (用 dsh 时还要 python3 和 dsh 自己的登录凭据);其余文件 (emx 中转、dsh 插件、
上传的图片) 由 Emacs 按需放到远程的 ~/.cache/emacs-agent/ 。远程进程会使用远程登录 shell 的
PATH ,装在 home 下的工具链 (如 gvm 的 Go、 ~/.cargo/bin ) 可以直接用。
- telega (Telegram,
C-c t):Homebrew 的 TDLib 太旧,需要从源码编译到~/.local/opt/tdlib, 步骤见lisp/app/init-telega.el开头的注释。 - sis (前缀键时自动切到英文输入法):需要
macism(brew install laishulu/homebrew/macism),目前默认关闭。
early-init.el 首帧之前的设置
init.el 包管理、模块加载顺序、启用的语言 my/languages 与后端 my/agents
lisp/core/ 基础层
init-base.el 默认行为、var/ 路径、macOS 环境、evil 开关
init-reload.el M-x my/reload-config:不重启地应用修改过的文件
init-ui.el 字体、布局、状态栏、侧边栏
init-theme.el 跟随 macOS 外观的亮 / 暗主题
init-evil.el Vim 键位(默认启用)
init-completion.el vertico / orderless / consult / corfu / cape
init-dev.el project、magit、eglot、tree-sitter
init-env.el 项目环境:direnv / nix-shell,异步且按 buffer 隔离
init-remote.el 远程编辑(tramp-rpc)
lisp/lang/ 语言模块:lang-{elisp,rust,go,nix,agda}.el
lisp/agent/ agent 公共设施
backend/ 各 agent 后端,通过 my/agents 启用
agent-claude.el Claude 后端:claude -p stream-json
agent-codex.el Codex 后端:codex app-server JSON-RPC
agent-dsh.el DeepSeek Harness 后端(插件在 backend/dsh/)
agent-session.el 会话界面与管理(对话记录 + 底部输入区)
agent-remote.el 远程项目:agent 跑在远程主机,emx 经 ssh 转发回本地
agent-session-list.el 会话侧边栏
agent-ui.el agent → Emacs 的方法注册与 RPC
agent-canvas.el 原生页面渲染(render / patch)
agent-review.el 分支的原子审查
agent-collab.el 会话之间互相协作(@handle)
init-agent.el 装配:加载后端、按键、server
lisp/app/ 独立应用:init-telega.el
bin/emx agent 调用 Emacs 的桥:命令行 + MCP server
skills/ 交给 Emacs 启动的 agent 的 skill
test/ ERT 测试
var/ 运行时状态(不进 git)
以下是 Emacs 原生键位;默认启用的 evil 键位见下文。
| 按键 | 功能 |
|---|---|
C-c a a | 显示 / 隐藏右侧的 agent 列 |
C-c a n | 新建会话,用最近用过的 agent (C-u 先选 agent) |
C-c a s | 会话列表:进行中的会话 + 本项目的历史会话 (RET 打开 / 恢复) |
C-c a i | 跳到当前项目会话的输入区 |
C-c a k | 结束会话 |
C-c a f | 把当前文件引用插入会话输入区 (有选区时带行号范围) |
C-c a r | 把选区代码块插入会话输入区 |
C-c a v | 回到 agent 最近展示的页面 |
C-x p p 后按 a | 切换项目并新建 agent 会话 |
C-x g | magit |
C-c r / C-c f / C-s / C-x b | ripgrep / 最近文件 / 搜索当前文件 / 切换 buffer |
M-g n / M-g p / M-g f | 下一个 / 上一个 / 列出所有诊断 |
C-c e / C-c l a / C-c l r / C-c l f | 启动 eglot / 代码操作 / 重命名 / 格式化 |
C-c i | 打开 init.el |
Rust (rust-ts-mode): C-c C-c 运行 cargo 子命令, C-c C-t 运行光标所在的测试,
C-c C-r cargo run, C-c C-k 重新运行上一次命令;保存时用 rustfmt 格式化。
Evil 下在 SPC l : c cargo 子命令, t 光标处测试, x cargo run, k 重新运行。
Go (go-ts-mode): C-c C-c 运行 go 子命令, C-c C-r go run, C-c C-k 重新运行;
保存时通过 gopls 整理 import 并格式化。Evil 下在 SPC l : c go 子命令, x go run,
k 重新运行, t 光标处测试, T 测当前文件, p 测当前包, d 插入文档注释。
Elisp: C-c C-b eval-buffer, C-c C-e 求值并美化输出, C-c C-m 展开宏,
C-c C-z IELM; C-h f/v/k/x 使用 helpful。Evil 下在 SPC l : b 、 e 、 m 、 z 同上,
d 用 helpful 查看光标处符号;~*scratch*~ 中有 SPC l e 。
Nix (nix-mode):安装 nil 后自动连接 Eglot,提供补全和诊断;另装 nixfmt 后保存时自动格式化。
项目设置可放在 eglot-workspace-configuration 的 :nil 中,服务器命令由 my/nil-command 配置。
没有这些工具时仍可使用语法高亮和缩进。
Agda:安装 Agda 并让 Emacs 的 PATH 能找到 ~agda~;首次打开 .agda / .lagda 及其
literate 文件时加载该版本自带的官方 ~agda2-mode~,兼容旧版的 ~agda-mode locate~。
.lagda.md 自动启用 ~poly-markdown-mode~:正文使用 Markdown 排版,光标进入
Agda 代码块后使用官方 Agda 模式。代码块继承项目环境,保留语义高亮和定义跳转。
以下 Agda 快捷键在代码块内使用。
C-c C-l 加载 / 检查, C-c C-, 查看目标和上下文, C-c C-c 分情况,
C-c C-SPC 填入目标;在 Agda 输入法中用 \lambda / \to 输入 Unicode。
官方安装说明:Agda;nil 的项目设置见 配置文档。
项目环境通过 envrc.el 按 buffer 隔离,异步执行 direnv,可直接使用
nix-shell 的 ~shellHook~。先安装 ~direnv~(~brew install direnv~),
再在项目中创建 =.envrc=:
use nix运行 direnv allow 授权这个项目,或在 Emacs 中用 ~M-x envrc-allow~。
首次加载时 Agda buffer 会等待环境,准备好后自动启用模式;Nix 的 Eglot
也会在环境就绪后连接。~C-c E~ 是环境操作前缀,~M-x envrc-reload~
重新加载,~M-x envrc-show-log~ 查看日志。修改 .envrc 后需要重新授权。
配置还会自动识别标准 Nix profile 路径,方便从 Dock 启动 Emacs。
布局参考 telega:上方是对话,下方一条分隔线 (显示工作中 / 等待确认等状态),
其下是辅助行 (待确认请求的选项按钮、排队中的消息) 和提示符 Claude ❯ ,之后就是输入区。
模型、推理强度、权限模式和用量显示在状态栏,可用鼠标点击切换。
| 按键 | 功能 |
|---|---|
C-c C-c | 发送 (C-u 时作为 steer 插入当前回合) |
RET / C-j | 换行;在对话中的按钮上 RET 为点击 |
M-p / M-n | 历史输入 |
TAB | 补全:行首 / 命令、 @ 文件 |
C-c C-k | 有输入时清空,否则中断 agent |
C-c C-y / C-c C-n | 允许 / 拒绝待确认的请求 |
C-c C-o | 会话菜单 |
C-c C-b / C-c C-f | 上一条 / 下一条消息 |
C-c C-a | 插入图片 |
在对话中打字会自动跳到输入区。 /model /effort /mode /agent /plan /default /new
/resume 在 Emacs 中执行,其余发给 agent。
agent 页面中: TAB 折叠区块 / 下一个按钮, RET 点击, c 回复 agent, g 重绘, q 关闭。
默认启用。 M-x my/toggle-evil 在当前 Emacs 中开关, C-u M-x my/toggle-evil 同时保存为默认值。
insert state 使用 Emacs 的编辑键,上面的 Emacs 键位在 evil 下照常可用。
leader 为 SPC : SPC f 找文件, SPC b 切换 buffer, SPC r 最近文件, SPC s 搜索当前文件,
SPC / 项目内搜索, SPC p 项目命令, SPC g g magit, SPC g b 开关逐行 blame, SPC a agent 命令 (同 C-c a),
SPC w 窗口, SPC u 前缀参数 (C-u 用于向上翻页), SPC i 打开 init.el。
SPC l 是语言命令:用 Eglot 的语言下 SPC l a 代码操作、 SPC l r 重命名、 SPC l f 格式化
(同 C-c l),各语言自己的命令也在 SPC l 下。
会话起始为 normal: i / a / o 在对话中跳到输入区末尾进入 insert,insert 下 C-c C-c 发送,
ESC 回到 normal 浏览; ]] / [[ 在消息间跳转。本地 leader , : ,o 菜单, ,m 模型,
,e 推理强度, ,M 权限模式, ,p 计划模式, ,A 切换 agent, ,y / ,n 允许 / 拒绝,
,k 中断, ,s 会话列表, ,N 新会话, ,r 恢复会话。
Agda 的命令在 SPC l 下(normal / motion / visual 状态):
| 按键 | 功能 |
|---|---|
SPC l l | 加载 / 类型检查 |
SPC l c | 查看目标和上下文 |
SPC l t | 查看目标、上下文和推断类型 |
SPC l s | 分情况 |
SPC l g | 填入目标 |
SPC l r | 精化目标 |
SPC l n | 下一个目标 |
SPC l p | 上一个目标 |
SPC l R | 重启 Agda |
SPC l q | 中断计算 |
项目环境操作:~SPC E r~ 重新加载,~SPC E a~ 授权,~SPC E d~ 撤销授权,
SPC E l 查看日志。原有 C-c 按键在 Evil 中照常使用。
Emacs 启动的 agent 进程里, bin/ 已加入 PATH , EMACS_SOCKET_NAME 指向当前 Emacs,
所以 agent 可以直接运行 emx :
emx help # 列出所有方法
emx help render # 查看页面 block 格式
emx context # 用户正在看的文件、光标、选区
emx open src/main.rs 42 # 打开文件跳到第 42 行并高亮
emx render page.json # 渲染原生页面,JSON:{"title": ..., "blocks": [...]}
emx eval '(buffer-list)' # 执行任意 Elisp
emx reload # 热加载修改过的配置文件同时这些方法会作为 MCP 工具 (emacs) 自动注入给 Emacs 启动的 agent 进程
(Claude 用 --mcp-config ,Codex 用 -c mcp_servers.emacs.* ,dsh 用它自己的 MCP 客户端),
不写入任何全局配置,在 Emacs 之外启动的 claude / codex 不受影响。
render 的 block 类型:标题、富文本 (粗体、行内代码、可点击的文件链接)、
列表 (可嵌套、可勾选)、代码块 (按语言高亮,或直接引用文件片段)、表格、键值对、
提示框、图表 (柱状、横向柱状、折线)、流程图 (自动布局)、进度条、可折叠区块、
图片、分隔线、由 agent 写的 Elisp 绘制的区块,以及 按钮 :用户点击后,按钮上的 prompt
会作为消息发回 agent 会话。完整格式见 emx help render 。
修改后用 emx reload (或 M-x my/reload-config) 热加载,不必重启;测试、编译检查和注意事项见
AGENTS.md。
- 语言 :新建
lisp/lang/lang-NAME.el(末尾(provide 'lang-NAME)), 再把NAME加入init.el中的my/languages。 - agent :参考
agent-claude.el实现agent-session-backend-*泛型函数,把协议消息翻译成agent-session-text/agent-session-tool/agent-session-ask等界面调用,再在lisp/agent/backend/agent-NAME.el中用(agent-session-define-agent NAME :title "...")注册,末尾(provide 'agent-NAME)~,再把 ~NAME加入init.el中的my/agents。 - agent 可调用的方法 :用
agent-ui-defmethod定义,MCP 工具列表会自动包含它。 - 页面 block 类型 :写一个接收 block plist 的渲染函数,加入
agent-canvas-renderers。
界面外观 (init-theme.el 的亮 / 暗主题, init-ui.el 的留白布局、状态栏和侧边栏) 模仿自
Nicolas P. Rougier 与 N Λ N O 开发者的 N Λ N O Emacs (GPL-3.0-or-later),
设计思路见 Rougier 的文章 On the design of text editors。语义化的少量 face、
亮色 (Material Design) 与暗色 (Nord) 两套配色、宽留白和带状态标签的状态栏都来自 nano;
代码是为这份配置和它的 agent 界面重新实现的,没有直接复制。
agent 会话的交互部分参考了 T3 Code 和 Claude Desktop:由 Emacs 自己渲染整段对话 (而不是嵌入 agent 的 TUI)、按项目列出会话的侧边栏,以及远程项目里让 agent CLI 跑在远程主机上的方式 (类似 Claude Desktop 的 SSH 模式)。
分支原子审查 (agent-review.el) 的设计与交互流程借鉴自 HerrCai0907 的 cui:
让 agent 将分支的提交变更按独立意图拆解为原子审查项、严格保证每行改动被完整覆盖,并通过
侧边栏导航与行内标注向 agent 提问和获取答复。
感谢这些项目!