Skip to content

xenodium/agent-shell

Repository files navigation

Emacs Agent Shell

👉 Support this work via GitHub Sponsors by @xenodium (check out my blog)

agent-shell.png

This project needs your funding

As you pay for those useful LLM tokens, consider sponsoring development and maintenance of this project.

agent-shell is in its infancy. It’s got rough edges and lots of features to implement still. With your help, I can make this effort more sustainable.

Thank you!

Alvaro

agent-shell

A native Emacs shell to interact with LLM agents powered by ACP (Agent Client Protocol).

With agent-shell, you can chat with the likes of Gemini CLI, Claude Code, or any other ACP-driven agent.

Note: This package is in the very early stages and is likely incomplete or may have some rough edges.

Related projects

Setup

External dependencies

Claude Code

For Anthropic’s Claude Code, follow Zed’s claude-code-acp instructions, typically something like:

npm install -g @zed-industries/claude-code-acp

Note: The -g flag is required to install the binary globally so it’s available in your PATH. After installation, verify it’s available by running which claude-code-acp in your terminal.

Codex

For OpenAI’s Codex, install zed/codex-acp and ensure the `codex-acp` executable is in PATH.

Gemini CLI

For Google’s Gemini CLI, be sure to get a recent release supporting the --experimental-acp flag.

Goose

For Goose CLI, install goose and ensure the `goose` executable is in PATH.

Qwen Code

For Qwen Code, install with:

npm install -g @qwen-code/qwen-code@latest

See https://github.com/QwenLM/qwen-code for details.

Installation

agent-shell is powered by built-in comint-shell, via shell-maker, available on MELPA.

Both agent-shell and its dependency acp.el are now available on MELPA.

You can install via:

(use-package agent-shell
  :ensure t)

This will automatically install the required dependencies (acp.el and shell-maker).

Doom Emacs

If you are using Doom Emacs and would like to use the package! macro:

(package! shell-maker)
(package! acp)
(package! agent-shell)

Run doom sync and restart.

Include require before configuration:

(require 'acp)
(require 'agent-shell)
;; rest of config...

Configuration

Configure authentication for the agent providers you want to use.

Environment variables

Pass environment variables to the spawned agent process by customizing the `agent-shell-*-environment` variable with `agent-shell-make-environment-variables`. The helper accepts key/value pairs and exports them when the agent starts.

(setq agent-shell-anthropic-claude-environment
      (agent-shell-make-environment-variables
       "ANTHROPIC_API_KEY" (auth-source-pass-get "secret" "anthropic-api-key")
       "HTTPS_PROXY" "http://proxy.example.com:8080"))

Inheriting environment variables

By default, the agent process starts with a minimal environment. To inherit environment variables from the parent Emacs process, use the `:inherit-env t` parameter in `agent-shell-make-environment-variables`:

(setenv "ANTHROPIC_API_KEY" (auth-source-pass-get "secret" "anthropic-api-key"))

(setq agent-shell-anthropic-claude-environment
      (agent-shell-make-environment-variables :inherit-env t))

This ensures that environment variables like `PATH`, `HOME`, and others from your Emacs session are available to the agent process, while still allowing you to override or add specific variables.

Loading environment variables from files

You can load environment variables from .env files using the `:load-env` parameter. This supports both single and multiple files:

;; Load from a single .env file
(setq agent-shell-anthropic-claude-environment
      (agent-shell-make-environment-variables
       :load-env "~/.env"
       "CUSTOM_VAR" "custom_value"))

;; Load from multiple .env files
(setq agent-shell-anthropic-claude-environment
      (agent-shell-make-environment-variables
       :load-env '("~/.env" ".env.local")
       :inherit-env t))

The .env files should contain variables in the format `KEY=value`, with one variable per line. Comments (lines starting with `#`) and empty lines are ignored.

Anthropic Claude

For login-based authentication (default):

(setq agent-shell-anthropic-authentication
      (agent-shell-anthropic-make-authentication :login t))

For API key authentication:

;; With string
(setq agent-shell-anthropic-authentication
      (agent-shell-anthropic-make-authentication :api-key "your-anthropic-api-key-here"))

;; With function
(setq agent-shell-anthropic-authentication
      (agent-shell-anthropic-make-authentication
       :api-key (lambda () (auth-source-pass-get "secret" "anthropic-api-key"))))

For alternative Anthropic-compatible API endpoints, configure via environment variables:

(setq agent-shell-anthropic-claude-environment
      (agent-shell-make-environment-variables
       "ANTHROPIC_BASE_URL" "https://api.moonshot.cn/anthropic"
       "ANTHROPIC_MODEL" "kimi-k2-turbo-preview"
       "ANTHROPIC_SMALL_FAST_MODEL" "kimi-k2-turbo-preview"))

Google Gemini

For login-based authentication (default):

(setq agent-shell-google-authentication
      (agent-shell-google-make-authentication :login t))

For API key authentication:

;; With string
(setq agent-shell-google-authentication
      (agent-shell-google-make-authentication :api-key "your-google-api-key-here"))

;; With function
(setq agent-shell-google-authentication
      (agent-shell-google-make-authentication
       :api-key (lambda () (auth-source-pass-get "secret" "google-api-key"))))

For Vertex AI authentication:

(setq agent-shell-google-authentication
      (agent-shell-google-make-authentication :vertex-ai t))

OpenAI Codex

For login-based authentication (default):

(setq agent-shell-openai-authentication
      (agent-shell-openai-make-authentication :login t))

For API key authentication:

;; With string
(setq agent-shell-openai-authentication
      (agent-shell-openai-make-authentication :api-key "your-openai-api-key-here"))

;; With function
(setq agent-shell-openai-authentication
      (agent-shell-openai-make-authentication
       :api-key (lambda () (auth-source-pass-get "secret" "openai-api-key"))))

Goose

For OpenAI API key authentication:

;; With string
(setq agent-shell-goose-authentication
      (agent-shell-make-goose-authentication :openai-api-key "your-openai-api-key-here"))

;; With function
(setq agent-shell-goose-authentication
      (agent-shell-make-goose-authentication
       :openai-api-key (lambda () (auth-source-pass-get "secret" "openai-api-key"))))

Qwen Code

For OAuth login-based authentication:

(setq agent-shell-qwen-authentication
      (agent-shell-qwen-make-authentication :login t))

Customizing Available Agents

By default, agent-shell includes configurations for all supported agents (Claude Code, Gemini CLI, Codex, Goose, and Qwen Code). You can customize which agents are available through the agent-shell-agent-configs variable.

Usage

Quick Start

M-x agent-shell - Start or reuse any of the known agents.

You can select and start any of the known agent shells (see agent-shell-agent-configs) via the agent-shell interactive command and enables reusing existing shells when available. With a prefix argument (C-u M-x agent-shell), it forces starting a new shell session, thus instantiating multiple agent shells.

Specific Agent Commands

Start a specific agent shell session directly:

  • M-x agent-shell-anthropic-start-claude-code - Start a Claude Code agent session
  • M-x agent-shell-openai-start-codex - Start a Codex agent session
  • M-x agent-shell-google-start-gemini - Start a Gemini agent session
  • M-x agent-shell-goose-start-agent - Start a Goose agent session
  • M-x agent-shell-qwen-start - Start a Qwen Code agent session

Running agents in Devcontainers / Docker containers (Experimental)

agent-shell provides rudimentary support for running agents and shell commands in containers.

Use agent-shell-container-command-runner to prefix the command that starts the agent, or a shell command that should be run so it is executed inside the container; for example:

(setq agent-shell-container-command-runner '("devcontainer" "exec" "--workspace-folder" "."))

Note that any :environment-variables you may have passed to acp-make-client will not apply to the agent process running inside the container. It’s expected to inject environment variables by means of your devcontainer configuration / Dockerfile.

Next, set an agent-shell-path-resolver-function that resolves container paths in the local working directory, and vice versa. Agent shell provides the agent-shell--resolve-devcontainer-path function for use with devcontainers specifically: it reads the workspaceFolder specified in .devcontainer/devcontainer.json, or uses the default value of /workspaces/<repository-name> otherwise.

(setq agent-shell-path-resolver-function #'agent-shell--resolve-devcontainer-path)

Note that this allows the agent to access files on your local file-system. While care has been taken to restrict access to files in the local working directory, it’s probably possible for a malicious agent to circumvent this restriction.

Optional: to prevent the agent running inside the container to access your local file-system altogether and to have it read/modify files inside the container directly, in addition to setting the resolver function, disable the “read/write text file” client capabilities:

(setq agent-shell-text-file-capabilities nil)

All of the above settings can be applied on a per-project basis using directory-local variables.

Keybindings

  • C-c C-c - Interrupt current agent operation
  • TAB and Shift-TAB - Navigate interactive elements

Customizations

Custom variableDescription
agent-shell-agent-configsThe list of known agent configurations.
agent-shell-anthropic-authenticationConfiguration for Anthropic authentication.
agent-shell-anthropic-claude-commandCommand and parameters for the Anthropic Claude client.
agent-shell-anthropic-claude-environmentEnvironment variables for the Anthropic Claude client.
agent-shell-completion-mode-hookHook run after entering or leaving ‘agent-shell-completion-mode’.
agent-shell-container-command-runnerCommand prefix for executing commands in a container.
agent-shell-cursor-commandCommand and parameters for the Cursor agent client.
agent-shell-cursor-environmentEnvironment variables for the Cursor agent client.
agent-shell-display-actionDisplay action for agent shell buffers.
agent-shell-embed-file-size-limitMaximum file size in bytes for embedding with ContentBlock::Resource.
agent-shell-file-completion-enabledNon-nil automatically enables file completion when starting shells.
agent-shell-google-authenticationConfiguration for Google authentication.
agent-shell-google-gemini-commandCommand and parameters for the Gemini client.
agent-shell-google-gemini-environmentEnvironment variables for the Google Gemini client.
agent-shell-goose-authenticationConfiguration for Goose authentication.
agent-shell-goose-commandCommand and parameters for the Goose client.
agent-shell-goose-environmentEnvironment variables for the Goose client.
agent-shell-header-styleStyle for agent shell buffer headers.
agent-shell-openai-authenticationConfiguration for OpenAI authentication.
agent-shell-openai-codex-commandCommand and parameters for the OpenAI Codex client.
agent-shell-openai-codex-environmentEnvironment variables for the OpenAI Codex client.
agent-shell-opencode-authenticationConfiguration for OpenCode authentication.
agent-shell-opencode-commandCommand and parameters for the OpenCode client.
agent-shell-opencode-environmentEnvironment variables for the OpenCode client.
agent-shell-path-resolver-functionFunction for resolving remote paths on the local file-system, and vice versa.
agent-shell-permission-iconIcon displayed when shell commands require permission to execute.
agent-shell-qwen-authenticationConfiguration for Qwen Code authentication.
agent-shell-qwen-commandCommand and parameters for the Qwen Code client.
agent-shell-qwen-environmentEnvironment variables for the Qwen Code client.
agent-shell-screenshot-commandThe program to use for capturing screenshots.
agent-shell-show-config-iconsWhether to show icons in agent config selection.
agent-shell-show-welcome-messageNon-nil to show welcome message.
agent-shell-text-file-capabilitiesWhether agents are initialized with read/write text file capabilities.
agent-shell-thought-process-iconIcon displayed during the AI’s thought process.
agent-shell-ui-mode-hookHook run after entering or leaving ‘agent-shell-ui-mode’.

Commands

BindingCommandDescription
agent-shellStart or reuse an existing agent shell.
agent-shell–display-bufferToggle agent SHELL-BUFFER display.
agent-shell-add-regionAdd region to last accessed shell buffer in project.
agent-shell-anthropic-start-claude-codeStart an interactive Claude Code agent shell.
agent-shell-clear-bufferClear the current shell buffer.
agent-shell-completion-modeToggle agent shell completion with @ or / prefix.
agent-shell-cursor-start-agentStart an interactive Cursor agent shell.
agent-shell-cycle-session-modeCycle through available session modes for the current `agent-shell’ session.
agent-shell-delete-interaction-at-pointDelete interaction (request and response) at point.
agent-shell-fakes-load-sessionLoad and replay a traffic session from file.
agent-shell-google-start-geminiStart an interactive Gemini CLI agent shell.
agent-shell-goose-start-agentStart an interactive Goose agent shell.
agent-shell-help-menuTransient menu for `agent-shell’ commands.
agent-shell-insert-fileInsert a file into `agent-shell’.
agent-shell-insert-shell-command-outputExecute a shell command and insert output as a code block.
C-c C-cagent-shell-interruptInterrupt in-progress request and reject all pending permissions.
agent-shell-jump-to-latest-permission-button-rowJump to the latest permission button row.
agent-shell-modeMajor mode for agent shell.
agent-shell-new-shellStart a new agent shell.
S-<return>agent-shell-newlineInsert a newline, and move to left margin of the new line.
C-<down> or M-nagent-shell-next-inputCycle forwards through input history.
<tab> or TABagent-shell-next-itemGo to next item.
agent-shell-next-permission-buttonJump to the next button.
agent-shell-openai-start-codexStart an interactive Codex agent shell.
agent-shell-opencode-start-agentStart an interactive OpenCode agent shell.
C-<up> or M-pagent-shell-previous-inputCycle backwards through input history, saving input.
S-TAB or <backtab>agent-shell-previous-itemGo to previous item.
agent-shell-previous-permission-buttonJump to the previous button.
agent-shell-qwen-startStart an interactive Qwen Code CLI agent shell.
agent-shell-rename-bufferRename current shell buffer.
agent-shell-reset-logsReset all log buffers.
agent-shell-restore-session-from-transcriptRestore session from file transcript (or HISTORY).
agent-shell-run-all-testsRun all agent-shell tests in batch mode.
C-x C-sagent-shell-save-session-transcriptSave shell transcript to file.
M-ragent-shell-search-historySearch previous input history.
agent-shell-send-current-fileInsert a file into `agent-shell’.
agent-shell-send-fileInsert a file into `agent-shell’.
agent-shell-send-other-filePrompt to send a file into `agent-shell’.
agent-shell-send-regionSend region to last accessed shell buffer in project.
agent-shell-send-screenshotCapture a screenshot and insert it into `agent-shell’.
agent-shell-set-session-modeSet session mode (if any available).
RETagent-shell-submitSubmit current input.
agent-shell-toggleToggle agent shell display.
agent-shell-toggle-loggingToggle logging.
agent-shell-ui-backward-blockJump to the previous block.
agent-shell-ui-forward-blockJump to the next block.
agent-shell-ui-modeMinor mode for SUI block navigation.
agent-shell-ui-toggle-dialog-block-at-pointToggle visibility of dialog block body at point.
agent-shell-versionShow `agent-shell’ mode version.
agent-shell-view-acp-logsView agent shell ACP logs buffer.
agent-shell-view-trafficView agent shell traffic buffer.

Contributing

Before Contributing

Before implementing new features, please file a feature request first to discuss the proposal. This helps ensure alignment with the project’s direction and prevents unnecessary work.

As the maintainer, I must be mindful of all features I accept since I inherit the code to maintain it. Some features may be better suited as separate packages (like agent-shell-sidebar).

I’ll gladly promote your package wherever possible.

Style (or personal preference TBH)

There are lots of ways to accomplish things in elisp. While the following are merely personal preferences, as maintainer, it really simplifies things for me to try to limit the number of ways to accomplish things.

Maps (use alists)

This project relies on alists for much of its functionality. Sure, we can also use plists, hashtables, etc.

Unless we have a strong argument to use something else, please stick with alists (and : keywords).

'((:species . "Cat")
  (:name . "Whiskers")
  (:age . 4)
  (:color . "Gray")
  (:favorite-toy . "Feather Wand"))

seq.el

Accessing and working with lists? Please prefer seq.el, unless we have a strong argument to use an alternative.

(setq animals
      (list
       '((:species . "Cat")
         (:name . "Whiskers")
         (:age . 4)
         (:color . "Gray"))
       '((:species . "Dog")
         (:name . "Buddy")
         (:age . 6)
         (:color . "Brown"))))

(seq-first animals)

map.el

Accessing and working with alists? Please prefer map.el unless we have a strong argument to use an alternative.

(setq animal (seq-first animals))
(map-elt animal :species)

cl-lib (limited to cl-defun)

While I’m a fan of cl-defun, please limit cl usage to cl-defun if possible. Nothing against cl-lib. I’m just limiting the surface and number of idioms I need to keep in my head to maintain the codebase. Often, seq.el and map.el can do the job just fine.

cl-defun, on the other hand, please do! I’m a fan of named parameters (yay for self-documenting), so use &key if possible.

(cl-defun describe (&key animal)
  "Describe an ANIMAL, which is an alist of properties like :species, :name, :age, :color."
  (message "This is a %d-year-old %s %s named %s."
           (map-elt animal :age 0)
           (map-elt animal :color "Unknown Color")
           (map-elt animal :species "Unknown Species")
           (map-elt animal :name "Unnamed")))

(describe :animal '((:species . "Cat")
                    (:name . "Whiskers")
                    (:age . 4)
                    (:color . "Gray")))

Code/feature consistency

Please try to look for a similar feature in the code base and replicate an existing pattern usage if possible.

Code Checks

Before submitting a PR, please run:

  • M-x checkdoc - Ensures documentation consistency
  • M-x byte-compile-file - Identifies compilation warnings

Tests

I’m aware, we’re a bit light on tests, but we started adding some tests. If adding a new feature, please try to add tests.

Tests live under the tests directory:

ls tests/*tests.el

Running tests

Opening any file under the tests directory will load the agent-shell-run-all-tests command.

Run tests with M-x agent-shell-run-all-tests.

Contributors

Made with contrib.rocks.

About

A native Emacs buffer to interact with LLM agents powered by ACP

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Packages

No packages published