Run Pi coding agent inside Emacs using
vterm (emacs-libvterm) — the full TUI, including ANSI cursor positioning,
interactive menus, and mouse support, rendered correctly.
Born from frustration with comint-based Pi integrations that break the TUI. VTERM is a real terminal emulator (backed by libvterm C library), so Pi looks and behaves exactly like it does in a native terminal.
- Emacs 28.1+
- vterm — install from MELPA
piinstalled and on yourexec-path
Add the directory to your load-path:
(add-to-list 'load-path "~/path/to/pi.el/")
(require 'pi)Or put it in one of your package-directory-list dirs and package-install-file.
(use-package pi
:straight (:type git :host github :repo "localredhead/pi.el"
:files ("*.el")))(use-package pi
:load-path "~/path/to/pi.el" ; or omit if installed via MELPA
:config
;; Define your own keybindings — pi.el ships with none by default
(global-set-key (kbd "C-c p p") #'pi)
(global-set-key (kbd "C-c p t") #'pi-toggle)
(global-set-key (kbd "C-c p s") #'pi-select))| Command | Description |
|---|---|
M-x pi |
Launch Pi for current project (reuses existing session) |
C-u M-x pi |
Force a fresh Pi session |
M-x pi-other-window |
Launch Pi in another window |
M-x pi-cwd |
Launch Pi using current directory (no project lookup) |
M-x pi-restart |
Kill current session and start fresh |
M-x pi-select |
Switch to Pi session, create if none exists |
Pi launches from the project root, automatically detected via:
- Emacs
project.el(built-in, Emacs 28+) - Projectile
projectile-project-root(if available)
Falls back to default-directory if neither finds a project.
Default opens in the same window. Control it with:
;; Always in a bottom side-window
(setq pi-display-function #'display-buffer)
(pi-setup-display-rules)
;; Or any display function
(setq pi-display-function #'switch-to-buffer)After vterm starts, the package waits and then sends the pi command.
;; Don't auto-launch — just open a shell in the project dir
(setq pi-auto-launch-command nil)
;; Increase wait time for slow shells
(setq pi-startup-wait 2.0)
;; Use a different shell
(setq pi-shell "/usr/bin/env zsh")Pi buffers get pi-mode automatically,
which gives them a mode line indicator ( Pi)
and runs pi-mode-hook after enabling.
No keybindings are defined by default — define your own in your config:
M-x customize-group RET pi RET
| Variable | Default | Description |
|---|---|---|
pi-binary |
"pi" |
Command to launch Pi |
pi-shell |
shell-file-name |
Shell for vterm |
pi-buffer-name |
"*pi:%s*" |
Buffer name format |
pi-auto-launch-command |
t |
Auto-send pi after shell starts |
pi-startup-wait |
1.0 |
Seconds delay before sending command |
pi-use-project-root |
t |
CD to project root first |
pi-display-function |
pop-to-buffer-same-window |
How to display the buffer |
pi-pre-launch-hook |
(pi--maybe-cd-project) |
Hook before launching Pi |
pi-mode-line |
" Pi" |
Mode line indicator |
Pi uses ncurses-style TUI rendering: ANSI cursor movement, full-screen
repaints, color attributes, and mouse-aware menus. comint-mode can't handle
this — it's line-oriented and strips escape codes. vterm uses libvterm (the
same library used by GNOME Terminal, xfce4-terminal, etc.), so Pi renders
correctly.
One Emacs package already provides Pi integration on MELPA:
- dnouri/pi-coding-agent — A native Emacs chat interface with Markdown rendering, tree-sitter syntax highlighting, transient menus, chat history with fork/search.
pi.el fills a different niche: a lightweight vterm wrapper that shows Pi's native TUI exactly as it appears in a real terminal. It runs on Emacs 28.1+.
The primary motivation for pi.el is to use Pi's slash commands
(/help, /ask, /edit, /undo, /model, etc.) and package system
exactly as Pi's developers intended. Pi's slash commands present rich,
ncurses-based menus for file selection, pattern matching, diff reviews,
and multi-agent handoffs — all of which render correctly in vterm.
A re-rendered UI may display chat history beautifully, but it loses the
interactive TUI experience built into Pi itself.
| pi.el | dnouri/pi-coding-agent | |
|---|---|---|
| Interface | vterm — Pi's native TUI unchanged | Native Emacs buffers (chat + input) |
| Rendering | Raw ANSI/ncurses — what Pi ships | Markdown + tree-sitter syntax highlighting |
| Requirements | Emacs 28.1+, vterm | Emacs 29+, tree-sitter, C compiler |
| Slash commands | Full — menus, diff reviews, pickers intact | Pi is consumed and re-rendered |
| Project awareness | Auto-cds to project root via project.el |
Works from current directory |
| Session model | Hides/restores vterm — state survives | Opens/closes Emacs buffers per session |
| Extra deps | vterm only | transient, md-ts-mode, markdown-table-wrap |
Choose pi.el if you want Pi's slash commands and menus working as designed, or you're on Emacs 28.x. Choose dnouri if you want a richer Emacs-native UI and have Emacs 29+.
Apache 2.0