12 Devenv Integration

Jotain ships a native integration with devenv, the Nix-based developer environment tool. The library lives in lisp/devenv.el (standalone and reusable — no Jotain dependencies) and is wired in by init-devenv.el. Everything is driven through the devenv CLI and degrades to a clean error when the binary is not on PATH. devenv 2.1+ is recommended; every invocation sets AI_AGENT=1 so devenv’s TUI stays out of the machine-readable output.

12.1 Entry point: C-c v

C-c v (or M-x devenv) opens a transient covering the whole surface:

KeyCommandWhat it does
tdevenv-task-runPick a task (annotated from devenv tasks list --json) and run it; prefix arg selects the dependency mode (before/single/after/all)
sdevenv-script-runPick a script defined in devenv.nix (annotated with its description) and run it
Tdevenv-testdevenv test (enterTest, with processes started/stopped around it)
bdevenv-builddevenv build, optionally for specific attributes
u / ddevenv-up / devenv-downStart/stop the project’s processes; up streams into a live buffer (or detaches, see devenv-up-detached); prefix arg picks a subset
pdevenv-processesProcess dashboard (below)
ldevenv-processes-logsTail one process’s logs
i / / / :devenv-info / devenv-search / devenv-evalIntrospection: environment summary, package/option search, evaluate any config attribute to pretty-printed JSON
rdevenv-reloadRefresh the environment (below)
edevenv-env-global-modeToggle the native environment loader (below)
mdevenv-modeline-modeToggle the mode-line status segment (below)
a / xdevenv-allow / devenv-revokeTrust / untrust the project for auto-activation (below)
Mdevenv-mcp-setupRegister the project’s MCP server with mcp.el
U / Gdevenv-update / devenv-gcUpdate devenv.lock inputs / garbage-collect old generations
$devenv-show-logEvery CLI invocation is recorded in *devenv log*

The -c (--clean) and -r (--refresh-eval-cache) infixes apply to devenv-test and devenv-build.

Long-running commands (test, build, tasks, update, gc) run in compilation buffers with Nix trace matching, so M-g M-n jumps to the file.nix:LINE:COL locations in evaluation errors.

12.2 Process dashboard

devenv-processes opens a tabulated view of the devenv process manager. Keys: s start, k stop, r restart, l/RET logs, u up, d down, g refresh. Data comes from devenv processes list (JSON when available, plain table on older devenv), fetched asynchronously so the UI never blocks.

12.3 Environment loading and reload

Environment loading is done by the library’s native loader (devenv-env-global-mode, enabled by init-devenv; direnv/envrc is disabled in init-prog). Every buffer inside a trusted devenv project carries the project environment buffer-locally, so eglot servers, apheleia formatters, M-x compile, and the ghostel terminal all see devenv-provided tools with no extra configuration.

The goal is parity with a terminal devenv shell, which takes more than reading devenv print-dev-env:

While a fetch is in flight, eglot-ensure is deferred and replayed once the environment lands, so language servers never start with the wrong PATH. If the fetch fails, the deferred buffers are released without connecting (starting a server with the login environment would be the wrong toolchain) and the message says how many are without an LSP; M-x eglot or C-c v r retries.

After changing devenv.nix, run devenv-reload (C-c v r). It invalidates the library’s caches, re-fetches the environment, and — because eglot snapshots the environment when a server starts — offers to eglot-reconnect every server serving the project so language servers pick up new PATH entries. Where direnv is in charge instead, envrc + use devenv is an equally good substrate: set devenv-env-defer-to-direnv back to t and the native loader stays out of any project with a .envrc, delegating reload to envrc-reload-all.

12.4 Auto-activation trust (devenv allow / revoke)

devenv 2.1+ gates shell auto-activation behind a per-project trust database (~/.local/share/devenv/allowed), managed with devenv allow and devenv revoke. The integration honours the same contract: the native loader (devenv-env-global-mode) consults the internal devenv hook-should-activate check and never evaluates an untrusted devenv.nix automatically — exactly like the zsh/fish/nu shell hooks. The same gate decides whether the environment may be obtained by sourcing (which runs the project’s shellHook); an untrusted or unverifiable project falls back to the JSON loader. devenv-allow (C-c v a) trusts the project and immediately activates the loader in its buffers; devenv-revoke (C-c v x) untrusts it and drops the native environment. On devenv versions without the trust model (< 2.1) the loader behaves as before. Note that the direnv/envrc path has its own consent gate (envrc-allow → direnv allow), independent of devenv’s database.

12.5 Mode-line status

devenv-modeline-mode (global, enabled by init-devenv at startup) adds a status segment to the mode line for buffers inside a devenv project:

Outside devenv projects the segment is empty. mouse-1 on the segment opens the devenv transient. The redisplay path is subprocess-free: trust is probed once per project in the background from find-file-hook and cached.

12.6 LSP for devenv.nix

devenv 2.0+ ships devenv lsp, a bundled nixd preconfigured with the project’s devenv module options (completion, hover docs for devenv.nix options). init-devenv routes devenv.nix and devenv.local.nix buffers to it while nil keeps serving every other Nix file — the routing entry delegates non-devenv buffers to whatever contact was registered before it.

12.7 MCP server for the AI stack

devenv 2.0+ ships devenv mcp, a stdio MCP server exposing nixpkgs package search, devenv option search, and process control (list/status/logs/start/stop/restart). devenv-mcp-setup (C-c v M) registers it with mcpel under a per-project name (devenv-<project>); start it from M-x mcp-hub and run M-x gptel-mcp-connect to hand its tools to gptel. devenv-mcp-remove unregisters it.

12.8 Customization

The devenv customization group covers the executable name (devenv-executable), the quiet-mode environment (devenv-extra-env), global CLI flags (devenv-global-arguments), cache TTL and command timeout, detached up (devenv-up-detached), and the dashboard’s log line count (devenv-processes-log-lines).

The native loader adds: how the environment is obtained (devenv-env-loader), whether direnv keeps ownership (devenv-env-defer-to-direnv), extra variables to drop (devenv-env-ignored-variables, filtered on top of the built-in devenv-env-never-applied-variables), which variables extend rather than replace the global value (devenv-env-layered-variables), and the shell bookkeeping ignored when diffing a sourced environment (devenv-env-shell-ignored-variables).