Shell Integration
Table of Contents
1. Introduction
Comprehensive terminal and shell integration for Emacs, providing multiple options for command-line interaction:
- Terminal Emulators: eat and vterm for full terminal emulation
- Shell Mode: Traditional comint-based shell with readline support
- Eshell: Pure Elisp shell with deep Emacs integration
- Custom Utilities: Enhanced async shell commands with project awareness
2. Terminal Emulators
Full-featured terminal emulators that support complex TUI applications like vim, htop, ncurses programs, etc.
2.1. Eat (Emulate A Terminal)
A fast, feature-complete terminal emulator written in pure Elisp. Works great on all platforms and integrates seamlessly with eshell.
Features:
- Pure Elisp implementation (no external dependencies)
- Full 256-color support
- Mouse support
- Integration with eshell for visual commands
- Fast and lightweight
- Kill buffer on exit for cleaner workflow
Usage:
C-c `: Open eat terminalC-c t e: Alternative binding- Works automatically in eshell for visual commands
;;; shell-conf.el --- Shell and Terminal Configuration -*- lexical-binding: t; -*- ;;; Commentary: ;;; Comprehensive configuration for terminal emulators, shell modes, ;;; and Emacs shell (eshell) with custom utilities. ;;; Code: ;; Terminal Emulators (use-package eat :ensure t :custom ;; Use eat-256color for best compatibility with interactive programs (eat-term-name "eat-256color") ;; Enable yanking to terminal (eat-enable-yank-to-terminal t) ;; Clean up buffers when terminal exits (eat-kill-buffer-on-exit t) :bind (("C-c `" . eat) ; Open eat terminal ("C-c t e" . eat)) ; Alternative binding :config ;; CRITICAL: Compile terminfo database for eat ;; This is required for proper terminal emulation (unless (file-exists-p (expand-file-name "~/.terminfo/e/eat-256color")) (eat-compile-terminfo)) ;; Enable eat in eshell for better terminal emulation (add-hook 'eshell-load-hook #'eat-eshell-mode) (add-hook 'eshell-load-hook #'eat-eshell-visual-command-mode) ;; Performance optimizations (add-hook 'eat-mode-hook (lambda () ;; Disable line numbers in terminal (setq-local display-line-numbers nil) ;; Disable word wrap (setq-local truncate-lines t))))
2.2. Vterm (Terminal Emulator)
High-performance terminal emulator using libvterm. Requires compilation but provides the best terminal emulation.
Features:
- Native libvterm integration (C library)
- Excellent performance
- Full terminal emulation (better than eat for some edge cases)
- Proper 256-color and true-color support
- Good for running complex TUI applications
Note: Requires cmake and libtool for compilation. Only enabled if cmake is available.
Usage:
C-c t v: Open vtermC-c t n: Open vterm in other window
;; Vterm - only if cmake is available for compilation (when (executable-find "cmake") (use-package vterm :ensure t :custom ;; Scrollback buffer size (larger = more history) (vterm-max-scrollback 10000) ;; Buffer naming (vterm-buffer-name-string "vterm %s") ;; Clean up on exit (vterm-kill-buffer-on-exit t) ;; Redisplay optimization (vterm-timer-delay nil) ;; Always try to compile the module (vterm-always-compile-module t) ;; Set proper TERM environment (vterm-environment '("TERM=xterm-256color")) (vterm-term-environment-variable "xterm-256color") ;; Shell to use (defaults to $SHELL or /bin/bash) (vterm-shell (or (getenv "SHELL") "/bin/bash")) :bind (("C-c t v" . vterm) ("C-c t n" . vterm-other-window)) :config ;; Don't query when killing vterm buffer (add-hook 'vterm-mode-hook (lambda () (setq-local confirm-kill-processes nil) (setq-local display-line-numbers nil)))))
3. Shell Mode
Traditional comint-based shell mode that runs your system shell (bash, zsh, etc.) in an Emacs buffer.
Features:
- Uses your default system shell
- Full readline support
- History navigation with
M-p/M-n - Command completion with
TAB - Read-only prompt to prevent accidental edits
- Echo process output for better display
When to use:
- Quick shell commands that don't need full terminal emulation
- When you want Emacs-style editing of command lines
- Simple tasks that benefit from Emacs integration
Usage:
M-x shell: Open shell bufferC-x p s: Open shell in project root (via project.el)
;;; shell-conf.el --- Shell and Terminal Configuration -*- lexical-binding: t; -*- ;;; Commentary: ;;; Code: ;; Shell Mode (use-package shell :ensure nil :custom ;; Make prompt read-only to prevent accidental edits (comint-prompt-read-only t) ;; Process should echo output (prevents duplication) (comint-process-echoes t) ;; Scrollback buffer size (comint-buffer-maximum-size 10000) ;; Always scroll to bottom on input (comint-scroll-to-bottom-on-input t) ;; Move point to end before sending input (comint-move-point-for-output t) :config ;; Enable ANSI colors in shell output (add-hook 'shell-mode-hook #'ansi-color-for-comint-mode-on) ;; Make shell buffers more distinctive (add-hook 'shell-mode-hook (lambda () (setq-local scroll-margin 0))))
4. Eshell
Emacs Shell (eshell) is a pure Elisp command shell that provides deep integration with Emacs. Unlike shell-mode, eshell doesn't run a separate shell process but implements shell features directly in Elisp.
4.1. Core Configuration
Features:
- Pure Elisp implementation (no subprocess)
- Cross-platform consistency
- Mix shell commands with Elisp functions
- Powerful scripting with Elisp
- Visual command support for TUI apps
- Smart history with deduplication
When to use:
- Cross-platform scripts (same behavior on Windows/Linux/macOS)
- Mix shell and Elisp commands seamlessly
- When you want deep Emacs integration
- Scripting that needs Elisp functions
Usage:
M-x eshell: Open eshell bufferC-x p e: Open eshell in project root (via project.el)
;; Eshell (use-package eshell :ensure nil :defer t :custom ;; History settings (eshell-history-size 10000) (eshell-buffer-maximum-lines 10000) (eshell-hist-ignoredups t) (eshell-save-history-on-exit t) ;; Scrolling behavior (eshell-scroll-to-bottom-on-input 'all) (eshell-scroll-to-bottom-on-output t) ;; Command behavior (eshell-error-if-no-glob t) (eshell-prefer-lisp-functions t) (eshell-destroy-buffer-when-process-dies t) ;; Visual commands (use terminal emulation for these) (eshell-visual-commands '("vi" "vim" "nvim" "screen" "tmux" "top" "htop" "btm" "less" "more" "lynx" "ncftp" "mutt" "pine" "tin" "trn" "elm" "irssi" "nmtui" "nmtui-connect" "nethack" "alsamixer" "w3m" "ncmpcpp" "newsbeuter" "fzf" "ssh" "tail")) ;; Subcommands that should use visual mode (eshell-visual-subcommands '(("git" "log" "diff" "show") ("docker" "logs") ("kubectl" "logs"))) ;; Options for visual commands (eshell-visual-options '(("git" "--paginate" "--help"))) :config ;; Load common Eshell modules (require 'eshell) (require 'em-smart) (require 'em-tramp) ;; Smart display behavior (add-hook 'eshell-mode-hook (lambda () ;; Plan text mode for better performance (setq-local global-hl-line-mode nil) ;; Disable line numbers (setq-local display-line-numbers nil))))
4.2. Eshell Prompt
Beautiful and functional prompt with git integration and directory shortening.
Features:
- Lambda-style prompt
- Git branch and status integration
- Syntax highlighting
- Clean and minimal design
;; Eshell Prompt Extras (use-package eshell-prompt-extras :ensure t :after eshell :custom ;; Enable prompt highlighting (eshell-highlight-prompt t) ;; Use lambda theme for clean look (eshell-prompt-function 'epe-theme-lambda) :config ;; Custom prompt regexp for eshell-prompt-extras (setq eshell-prompt-regexp "^[^#$\n]* [#$] "))
4.3. Eshell Aliases
Convenient aliases for common commands to improve productivity.
;; Eshell Aliases (with-eval-after-load 'eshell (defun fu/eshell-setup-aliases () "Set up common eshell aliases." ;; Navigation (eshell/alias ".." "cd ..") (eshell/alias "..." "cd ../..") (eshell/alias "...." "cd ../../..") ;; List files (eshell/alias "l" "ls -lh $*") (eshell/alias "ll" "ls -lh $*") (eshell/alias "la" "ls -alh $*") (eshell/alias "lt" "ls -lht $*") ; by time ;; Git shortcuts (eshell/alias "gs" "git status") (eshell/alias "gd" "git diff $*") (eshell/alias "gl" "git log --oneline --graph --decorate $*") (eshell/alias "gp" "git pull") (eshell/alias "gP" "git push") (eshell/alias "ga" "git add $*") (eshell/alias "gc" "git commit $*") (eshell/alias "gco" "git checkout $*") (eshell/alias "gb" "git branch $*") ;; Emacs shortcuts (eshell/alias "e" "find-file $1") (eshell/alias "ee" "find-file-other-window $1") (eshell/alias "d" "dired $1") ;; Clear screen (eshell/alias "clear" "clear-scrollback")) ;; Set up aliases on first eshell invocation (add-hook 'eshell-first-time-mode-hook #'fu/eshell-setup-aliases))
4.4. Eshell Banner
Customize the eshell banner to be more informative and welcoming.
;; Custom Eshell Banner (with-eval-after-load 'eshell (setq eshell-banner-message (format "%s Welcome to Eshell - the Emacs Shell Type 'help' for common commands, or any Elisp expression to evaluate Current directory: %s " (propertize "╭──────────────────────────────────────────╮\n│ │\n│ Eshell - Emacs Shell Environment │\n│ │\n╰──────────────────────────────────────────╯" 'face '(:foreground "cyan")) (abbreviate-file-name default-directory))))
5. Custom Shell Utilities
Enhanced utilities for running shell commands with better integration.
5.1. Async Shell Command
Improved async shell command that runs commands in project root and provides better output formatting.
Features:
- Automatically runs in project root if in a project
- Timestamped output buffers
- Process completion notifications
- Exit code reporting
- Separate error buffers
Usage:
M-x fu/async-shell-command: Run async command with enhanced output
;; Custom Shell Utilities (defun fu/async-shell-command (command) "Run `async-shell-command' with COMMAND in project root. Provides better output formatting and runs in project context." (interactive (list (read-string "Shell command: "))) (let* ((output-buffer-time (format-time-string "%Y-%m-%dT%H:%M:%S")) (output-buffer-name (format "*Shell Command [%s]: %s*" output-buffer-time command)) (error-buffer-name (format "*Shell Error [%s]: %s*" output-buffer-time command)) (current-project (project-current)) (default-directory (if current-project (project-root current-project) default-directory))) ;; Run the command (async-shell-command command output-buffer-name error-buffer-name) ;; Add process sentinel for completion notification (when-let* ((proc (get-buffer-process output-buffer-name))) (set-process-sentinel proc (lambda (process event) (when (buffer-live-p (process-buffer process)) (with-current-buffer (process-buffer process) (let ((inhibit-read-only t)) (goto-char (point-max)) (insert "\n" (make-string 60 ?-) "\n") (insert (format "Process %s %s" (process-name process) (string-trim event))) (insert (format " at %s\n" (format-time-string "%Y-%m-%d %H:%M:%S"))) (when (process-exit-status process) (insert (format "Exit code: %s\n" (process-exit-status process)))) (insert (make-string 60 ?-) "\n"))))))))) ;; Bind to convenient key (global-set-key (kbd "C-c !") #'fu/async-shell-command)
5.2. Quick Shell Command in Project
Run a quick shell command in the current project's root directory.
(defun fu/shell-command-in-project (command) "Run COMMAND synchronously in project root and display output." (interactive "sShell command in project: ") (let* ((project (project-current)) (default-directory (if project (project-root project) default-directory))) (shell-command command))) (global-set-key (kbd "C-c |") #'fu/shell-command-in-project)
5.3. Open External Terminal
Open an external terminal application in the current directory or project root.
(defun fu/open-external-terminal () "Open external terminal in current directory or project root." (interactive) (let ((dir (if-let* ((project (project-current))) (project-root project) default-directory))) (cond ;; macOS ((eq system-type 'darwin) (call-process "open" nil 0 nil "-a" "Terminal" dir)) ;; Linux with GNOME ((and (eq system-type 'gnu/linux) (executable-find "gnome-terminal")) (call-process "gnome-terminal" nil 0 nil (concat "--working-directory=" dir))) ;; Linux with other terminal ((executable-find "xterm") (start-process "xterm" nil "xterm" "-e" "cd" dir)) (t (message "No external terminal configured for your system"))))) (global-set-key (kbd "C-c t x") #'fu/open-external-terminal)
5.4. DWIM Shell Command
A "Do What I Mean" shell command wrapper that intelligently operates on files in Dired or the current buffer. It provides powerful template placeholders for working with files and integrates seamlessly with Emacs workflows.
Features:
- Smart file selection from Dired marked files or current buffer
- Template placeholders for flexible command construction
- Async execution with progress tracking
- Works with single files, multiple files, or clipboard content
- Automatic output buffer management
Template Placeholders:
<<f>>: Current file or marked files (iterates for each file)<<fne>>: File name without extension<<e>>: File extension only<<td>>: Temporary directory<<cb>>: Clipboard content<<*>>: All marked files at once (not iterating)
Usage:
M-!: Run DWIM shell command (remapped fromshell-command)- In Dired:
!runs command on marked files - Example:
convert <<f>> <<fne>>.pngconverts images to PNG
(use-package dwim-shell-command :ensure t :bind (([remap shell-command] . dwim-shell-command) :map dired-mode-map ([remap dired-do-async-shell-command] . dwim-shell-command) ([remap dired-do-shell-command] . dwim-shell-command) ([remap dired-smart-shell-command] . dwim-shell-command)) :config (setq dwim-shell-command-git-clone-dirs '("~/Projects")) ;; Load additional command templates (require 'dwim-shell-commands nil t))
6. Agent Shell
A native Emacs interface to interact with LLM agents powered by ACP (Agent Client Protocol). Agent Shell enables chatting directly with AI coding assistants from within Emacs, supporting multiple providers while maintaining session state.
6.1. Agent Shell Core
The core agent-shell package provides the foundation for AI agent interaction in Emacs.
Features:
- Native Emacs buffer for AI agent interaction
- Built on
shell-makerandacp.elfor robust communication - Multiple provider support with unified interface
- MCP (Model Context Protocol) server integration
- Container and devcontainer execution support
- Environment variable configuration (inherit, .env files)
- Source block syntax highlighting
- File completion and read/write capabilities
- Screenshot capture support
Supported Agents:
- Anthropic Claude Code:
npm install -g @anthropic-ai/claude-code(native ACP support with--acpflag) - Google Gemini CLI:
npm install -g @google/gemini-cli(usegemini --acpflag) - OpenAI Codex:
npm install -g @openai/codex - Goose CLI: Install from block/goose repository
- Cursor Agent: Install cursor-acp adapter to bridge Cursor CLI to ACP
Usage:
M-x agent-shell: Start agent shell (interactive agent selection)C-u M-x agent-shell: Force new sessionM-x agent-shell-anthropic-start-claude-code: Start Claude directlyM-x agent-shell-google-start-gemini: Start Gemini directlyC-c C-c: Interrupt current operationTAB/S-TAB: Navigate interactive elements
;; Agent Shell - LLM agents via ACP in Emacs (use-package agent-shell :ensure t :custom ;; Display welcome message on start (agent-shell-show-welcome-message nil) ;; Enable source block highlighting (agent-shell-highlight-blocks t) ;; Enable file completion (agent-shell-file-completion-enabled t) ;; Enable file read/write capabilities (agent-shell-text-file-capabilities t) ;; Show icons in agent selection (agent-shell-show-config-icons t) (agent-shell-header-style 'graphical) :config ;; Set preferred/default agent (Claude Code) (setq agent-shell-preferred-agent-config (agent-shell-anthropic-make-claude-code-config)) ;; Authentication for Anthropic (uses interactive login) (setq agent-shell-anthropic-authentication (agent-shell-anthropic-make-authentication :login t)) ;; Optional: Configure MCP servers ;; (setq agent-shell-mcp-servers ;; '(((name . "filesystem") ;; (type . "stdio") ;; (command . "npx") ;; (args . ("-y" "@modelcontextprotocol/server-filesystem" "/path"))))) ;; Optional: Environment variables (inherit from Emacs) ;; (setq agent-shell-anthropic-claude-environment ;; (agent-shell-make-environment-variables :inherit-env t)) ;; Optional: Devcontainer support ;; (setq agent-shell-container-command-runner ;; '("devcontainer" "exec" "--workspace-folder" ".")) )
6.2. Agent Shell Manager
A buffer management tool for agent-shell that provides a tabulated list view of all open agent-shell buffers with real-time status monitoring and management capabilities.
Repository: jethrokuan/agent-shell-manager
Features:
- Tabulated display showing folder paths, status (ready/working/waiting/initializing/killed), session status, and mode
- Auto-refresh capability (updates every 2 seconds)
- Process controls for killing, restarting, and creating agent-shell instances
- Session management with mode cycling and interrupt functions
- Debugging utilities including traffic log viewing and ACP logging toggles
- Visual organization with color-coded statuses and killed processes displayed separately
Usage:
M-x agent-shell-manager-toggle: Toggle the manager interfaceRET: Switch to bufferg: Refresh the listk: Kill processc/r/d: Create / restart / delete buffersm/M: Set or cycle session modet/l: View logs or toggle logging
;; Agent Shell Manager - buffer management for agent-shell (use-package agent-shell-manager :ensure t :vc (:url "https://github.com/jethrokuan/agent-shell-manager" :rev :newest) :after agent-shell :custom ;; Window positioning: 'left, 'right, 'top, 'bottom, or nil ;; When nil, defers to display-buffer-alist (agent-shell-manager-side 'right) :bind ;; Convenient binding to open manager ("C-c t m" . agent-shell-manager-toggle))
6.3. Agent Shell Sidebar
A persistent sidebar interface for agent-shell, providing a treemacs-like side panel for interacting with LLM agents. The sidebar is project-aware and maintains state across visibility toggles.
Repository: cmacrae/agent-shell-sidebar
Requirements:
- Emacs 29.1 or later
- agent-shell 0.5.1 or newer
Features:
- Project-aware with individual sidebar state per project
- Persistent sidebar that maintains state across visibility toggles
- Configurable width, position (left/right), and locking behavior
- Locked sidebars adapt automatically to frame size changes
Usage:
M-x agent-shell-sidebar-toggle: Toggle sidebar visibility for current projectM-x agent-shell-sidebar-switch-to-buffer: Switch focus between sidebar and other buffersM-x agent-shell-sidebar-set-config: Change LLM provider on demandM-x agent-shell-sidebar-reset: Reset sidebar state
;; Agent Shell Sidebar - persistent sidebar for agent-shell (use-package agent-shell-sidebar :ensure t :vc (:url "https://github.com/cmacrae/agent-shell-sidebar" :rev :newest) :after agent-shell :custom ;; Panel width (integer or percentage string) (agent-shell-sidebar-width "25%") ;; Minimum width threshold (agent-shell-sidebar-minimum-width 80) ;; Maximum width limit (agent-shell-sidebar-maximum-width "50%") ;; Placement: 'left or 'right (agent-shell-sidebar-position 'right) ;; Fixed vs. resizable mode (agent-shell-sidebar-locked t) ;; Optional: Set default LLM provider ;; (agent-shell-sidebar-default-config ;; (agent-shell-anthropic-make-claude-code-config)) :bind ;; Toggle sidebar ("C-c t s" . agent-shell-sidebar-toggle))
7. End of File
(provide 'shell-conf) ;;; shell-conf.el ends here