Language Server Protocol(LSP,语言服务器协议)

Hermes 运行完整的语言服务器——pyright、gopls、rust-analyzer、typescript-language-server、clangd 以及约 20 个更多——作为后台子进程,并将其语义诊断信息输入到 write_filepatch 使用的写后 lint 检查中。当代理编辑文件时,它看到的是该编辑引入的确切错误——不仅仅是语法错误,还有类型错误、未定义名称、缺失导入以及语言服务器检测到的项目范围语义问题

这是顶级编码代理使用的相同架构。Hermes 自包含地提供了它:无需编辑器主机、无需安装插件、无需管理单独的守护进程。

LSP 何时运行

LSP 以 git 工作区检测为门控。当代理的工作目录(或正在编辑的文件)在 git 仓库内时,LSP 会针对该工作区运行。当两者都不在 git 仓库中时,LSP 保持休眠——这对 cwd 是用户主目录且没有项目可诊断的消息网关很有用。

检查是分层的:首先是进程内语法检查(微秒级),然后当语法干净时是 LSP 诊断。有故障或缺失的语言服务器永远不会破坏写入——每个 LSP 失败路径都会静默回退到仅语法结果。

具体来说,在每次成功的 write_filepatch 上:

  1. Hermes 捕获文件的当前诊断基线。
  2. 执行写入。
  3. 重新查询语言服务器,过滤掉基线中已存在的诊断,仅呈现新的诊断。

代理看到类似这样的输出:

{
  "bytes_written": 42,
  "dirs_created": false,
  "lint": {"status": "ok", "output": ""},
  "lsp_diagnostics": "此编辑引入的 LSP 诊断:\n<diagnostics file=\"/path/to/foo.py\">\nERROR [42:5] Cannot find name 'foo' [reportUndefinedVariable] (Pyright)\nERROR [50:1] Argument of type \"str\" is not assignable to \"int\" [reportArgumentType] (Pyright)\n</diagnostics>"
}

lint 字段携带语法检查结果(微秒级进程内解析,通过 ast.parsejson.loads 等);lsp_diagnostics 字段携带来自真实语言服务器的语义诊断。两个通道,独立信号——代理看到一个语法干净但存在语义问题的文件时,会显示 lint: ok 加一个填充的 lsp_diagnostics

支持的语言

语言服务器自动安装
Pythonpyright-langservernpm
TypeScript / JavaScript / JSX / TSXtypescript-language-servernpm
Vue@vue/language-servernpm
Sveltesvelte-language-servernpm
Astro@astrojs/language-servernpm
Gogoplsgo install
Rustrust-analyzer手动(rustup)
C / C++clangd手动(LLVM)
Bash / Zshbash-language-servernpm
YAMLyaml-language-servernpm
Lualua-language-server手动(GitHub releases)
PHPintelephensenpm
OCamlocaml-lsp手动(opam)
Dockerfiledockerfile-language-server-nodejsnpm
Terraformterraform-ls手动
Dartdart language-server手动(dart sdk)
Haskellhaskell-language-server手动(ghcup)
Juliajulia + LanguageServer.jl手动
Clojureclojure-lsp手动
Nixnixd手动
Zigzls手动
Gleamgleam lsp手动(gleam install)
Elixirelixir-ls手动
Prismaprisma language-server手动
Kotlinkotlin-language-server手动
Javajdtls手动

对于”手动”条目,请通过对该语言有意义的工具链管理器(rustup、ghcup、opam、brew 等)安装服务器。Hermes 会在 PATH 或 <HERMES_HOME>/lsp/bin/ 中自动检测二进制文件。

少数服务器与 npm 不会自动拉取的同行依赖一起安装。当前的情况是 typescript-language-server,它需要从同一 node_modules 树可导入的 typescript SDK——当您运行 hermes lsp install typescript 或首次使用时自动安装触发时,Hermes 会一同安装这两个包。

CLI

hermes lsp status          # 服务状态 + 每服务器安装状态
hermes lsp list            # 注册表,可选 --installed-only
hermes lsp install <id>    # 主动安装一个服务器
hermes lsp install-all     # 尝试每个有已知配方服务器的安装
hermes lsp restart         # 拆除运行中的客户端
hermes lsp which <id>      # 打印解析的二进制路径

hermes lsp status 是最好的起点——它显示今天哪些语言会获得语义诊断,哪些需要安装二进制文件。

配置

默认值适用于典型设置;如果二进制文件在 PATH 上则无需设置任何内容。

# config.yaml
lsp:
  # 主开关。禁用会跳过整个子系统——不会生成服务器,
  # 不会运行后台事件循环。
  enabled: true
 
  # 每次写入后等待诊断的时间。
  wait_mode: document      # "document" 或 "full"
  wait_timeout: 5.0
 
  # 如何处理缺失的服务器二进制文件。
  #   auto    — 通过 npm/pip/go install 安装到 <HERMES_HOME>/lsp/bin
  #   manual  — 仅使用已在 PATH 上的二进制文件
  install_strategy: auto
 
  # 每服务器覆盖(全部可选)。
  servers:
    pyright:
      disabled: false
      command: ["/abs/path/to/pyright-langserver", "--stdio"]
      env: { PYRIGHT_LOG_LEVEL: "info" }
      initialization_options:
        python:
          analysis:
            typeCheckingMode: "strict"
    typescript:
      disabled: true       # 即使扩展名匹配也跳过 TS

每服务器键

  • disabled: true —— 即使其扩展名匹配文件也完全跳过此服务器
  • command: [bin, ...args] —— 固定自定义二进制路径。绕过自动安装
  • env: {KEY: value} —— 传递给生成进程的额外环境变量
  • initialization_options: {...} —— 合并到 LSP initializationOptions 负载中,在 initialize 握手中发送。服务器特定;请查阅语言服务器的文档

安装位置

install_strategy: auto 时,Hermes 将二进制文件安装到 <HERMES_HOME>/lsp/bin/。NPM 包放入 <HERMES_HOME>/lsp/node_modules/,二进制符号链接向上移一级。Go 二进制文件来自 go installGOBIN 指向暂存目录。

任何内容都不会安装到 /usr/local/~/.local/ 或任何其他共享位置——暂存目录完全由 Hermes 拥有,并在您重置配置时被移除。

性能特征

LSP 服务器在首次使用时惰性生成。在从未见过 .py 流量的项目中编辑 Python 文件会生成 pyright;大多数服务器的生成需要 1-3 秒(rust-analyzer 在冷项目上可能需要 10 秒以上)。同一工作区中的后续编辑会重用正在运行的服务器。

当没有发出诊断时,LSP 层为干净写入增加几毫秒。当发出诊断时,等待预算为 wait_timeout 秒——通常服务器在几十毫秒内响应(pyright/tsserver),rust-analyzer 在索引中则需要几秒。

服务器在 Hermes 进程的整个生命周期内保持存活。没有空闲超时回收器——在每次写入时重启服务器索引的成本远高于持有守护进程。

禁用

config.yaml 中设置 lsp.enabled: false 以禁用整个子系统。写后检查回退到进程内语法检查(Python 的 ast.parse、JSON 的 json.loads 等),这些与早期版本保持不变。

要禁用单一语言而不禁用整个层:

lsp:
  servers:
    rust-analyzer:
      disabled: true

故障排除

hermes lsp status 将服务器显示为”missing”

二进制文件不在 PATH 上,也不在 <HERMES_HOME>/lsp/bin/ 中。运行 hermes lsp install <server_id> 以尝试自动安装,或通过该语言的正常工具链手动安装二进制文件。

hermes lsp status 中的 Backend warnings 部分

某些服务器作为薄包装器,实际诊断委托给外部 CLI——它们干净地生成并接受请求,但辅助二进制文件缺失时从不发出错误。最常见的情况是 bash-language-server,它将诊断委托给 shellcheck。当 hermes lsp status 显示 Backend warnings 部分时,通过操作系统包管理器安装命名工具:

apt install shellcheck      # Debian / Ubuntu
brew install shellcheck     # macOS
scoop install shellcheck    # Windows

同样的警告在服务器生成时记录一次在 ~/.hermes/logs/agent.log 中。

服务器启动但从不返回诊断

检查 ~/.hermes/logs/agent.log[agent.lsp.client] 条目——语言服务器的 stderr 和协议错误都记录在那里。某些服务器(特别是 rust-analyzer)需要完成项目范围索引后才能发出每文件诊断;服务器启动后的第一次编辑可能在没有诊断的情况下完成,后续编辑会拾取它们。

服务器崩溃

崩溃的服务器被添加到损坏集合中,在该会话的剩余时间内不会重试。运行 hermes lsp restart 以清除该集合;下次编辑会重新生成。

在任何 git 仓库外编辑文件

根据设计,LSP 仅在 git 仓库内运行。如果项目尚未初始化,运行 git init 以启用 LSP 诊断。否则应用进程内仅语法回退。