Skip to content

A multi-llm Emacs shell (ChatGPT, Claude, Gemini) + editing integrations

License

Notifications You must be signed in to change notification settings

xenodium/chatgpt-shell

Repository files navigation

👉 Support this work via GitHub Sponsors

https://stable.melpa.org/packages/chatgpt-shell-badge.svg https://melpa.org/packages/chatgpt-shell-badge.svg

chatgpt-shell

A multi-llm Emacs comint shell, by me.

Related packages

  • ob-chatgpt-shell: Evaluate chatgpt-shell blocks as Emacs org babel blocks.
  • ob-dall-e-shell: Evaluate DALL-E shell blocks as Emacs org babel blocks.
  • dall-e-shell: An Emacs shell for OpenAI’s DALL-E.
  • shell-maker: Create Emacs shells backed by either local or cloud services.

News

chatgpt-shell goes multi model 🎉

It’s been quite a bit of work, and I still have plenty of pending work. Please sponsor the project to make development + support sustainable.

ProviderModelSupported
OpenAIChatGPTYes
GoogleGeminiNew 💫
AnthropicClaudeNew 💫
OllamaLlamaNew 💫

My favourite model is missing.

File a feature requestsponsor the work

A familiar shell

chatgpt-shell is a comint shell. Bring your favourite Emacs shell flows along.

Swap models

One shell to query all. Swap LLM provider and continue with your familiar flow.

A shell hybrid

chatgpt-shell includes a compose buffer experience. This is my favourite and most frequently used mechanism to interact with LLMs.

For example, select a region and invoke M-x chatgpt-shell-prompt-compose (C-c C-e is my preferred binding), and an editable buffer automatically copies the region and enables crafting a more thorough query. When ready, submit with the familiar C-c C-c binding. The buffer automatically becomes read-only and enables single-character bindings.

Navigation: n/p (or TAB/shift-TAB)

Navigate through source blocks (including previous submissions in history). Source blocks are automatically selected.

Reply: r

Reply with with follow-up requests using the r binding.

Give me more: m

Want to ask for more of the same data? Press m to request more of it. This is handy to follow up on any kind of list (suggestion, candidates, results, etc).

Quick quick: q

I’m a big fan of quickly disposing of Emacs buffers with the q binding. chatgpt-shell compose buffers are no exception.

Request entire snippets: e

LLM being lazy and returning partial code? Press e to request entire snippet.

Confirm inline mods (via diffs)

Request inline modifications, with explicit confirmation before accepting.

Execute snippets (a la org babel)

Both the shell and the compose buffers enable users to execute source blocks via C-c C-c, leveraging org babel.

Vision experiments

I’ve been experimenting with image queries (currently ChatGPT only, please sponsor to help bring support for others).

Below is a handy integration to extract Japanese vocabulary. There’s also a generic image descriptor available via M-x chatgpt-shell-describe-image that works on any Emacs image (via dired, image buffer, point on image, or selecting a desktop region).

Support this effort

If you’re finding chatgpt-shell useful, help make the project sustainable and consider ✨sponsoring✨.

chatgpt-shell is in development. Please report issues or send pull requests for improvements.

Like this package? Tell me about it 💙

Finding it useful? Like the package? I’d love to hear from you. Get in touch (Mastodon / Twitter / Bluesky / Reddit / Email).

Install

  • Load (require 'chatgpt-shell)

MELPA

If using use-package, you can install with :ensure t.

(use-package chatgpt-shell
  :ensure t
  :custom
  ((chatgpt-shell-openai-key
    (lambda ()
      (auth-source-pass-get 'secret "openai-key")))))

Set OpenAI key

You’ll first need to get a key from OpenAI.

As function

;; if you are using the "pass" password manager
(setq chatgpt-shell-openai-key
      (lambda ()
        ;; (auth-source-pass-get 'secret "openai-key") ; alternative using pass support in auth-sources
        (nth 0 (process-lines "pass" "show" "openai-key"))))

;; or if using auth-sources, e.g., so the file ~/.authinfo has this line:
;;  machine api.openai.com password OPENAI_KEY
(setq chatgpt-shell-openai-key
      (auth-source-pick-first-password :host "api.openai.com"))

;; or same as previous but lazy loaded (prevents unexpected passphrase prompt)
(setq chatgpt-shell-openai-key
      (lambda ()
        (auth-source-pick-first-password :host "api.openai.com")))

Manually

M-x set-variable chatgpt-shell-openai-key

As variable

(setq chatgpt-shell-openai-key "my key")

As an ENV variable

(setq chatgpt-shell-openai-key (getenv "OPENAI_API_KEY"))

ChatGPT through proxy service

If you use ChatGPT through proxy service “https://api.chatgpt.domain.com”, set options like the following:

(use-package chatgpt-shell
  :ensure t
  :custom
  ((chatgpt-shell-api-url-base "https://api.chatgpt.domain.com")
   (chatgpt-shell-openai-key
    (lambda ()
      ;; Here the openai-key should be the proxy service key.
      (auth-source-pass-get 'secret "openai-key")))))

If your proxy service API path is not OpenAI ChatGPT default path like ”/v1/chat/completions”, then you can customize option chatgpt-shell-api-url-path.

Using ChatGPT through HTTP(S) proxy

Behind the scenes chatgpt-shell uses curl to send requests to the openai server. If you use ChatGPT through a HTTP proxy (for example you are in a corporate network and a HTTP proxy shields the corporate network from the internet), you need to tell curl to use the proxy via the curl option -x https://your_proxy. One way to do this is to set the proxy url via the customizable variable chatgpt-shell-additional-curl-options. If you set this variable via the Emacs Customize interface you should insert two separate items -x and https://your_proxy. See the curl manpage for more details and further options.

Using ChatGPT through Azure OpenAI Service

Endpoint: https://{your-resource-name}.openai.azure.com/openai/deployments/{deployment-id}/chat/completions?api-version={api-version}

Configure the following variables:

(setq chatgpt-shell-api-url-base "https://{your-resource-name}.openai.azure.com")
(setq chatgpt-shell-api-url-path "/openai/deployments/{deployment-id}/chat/completions?api-version={api-version}")
(setq chatgpt-shell-auth-header (lambda () (format "api-key: %s" (chatgpt-shell-openai-key))))

Launch

Launch with M-x chatgpt-shell.

Note: M-x chatgpt-shell keeps a single shell around, refocusing if needed. To launch multiple shells, use C-u M-x chatgpt-shell.

Clear buffer

Type clear as a prompt.

ChatGPT> clear

Alternatively, use either M-x chatgpt-shell-clear-buffer or M-x comint-clear-buffer.

Saving and restoring

Save with M-x chatgpt-shell-save-session-transcript and restore with M-x chatgpt-shell-restore-session-from-transcript.

Some related values stored in shell-maker like shell-maker-transcript-default-path and shell-maker-forget-file-after-clear.

Streaming

chatgpt-shell can either wait until the entire response is received before displaying, or it can progressively display as chunks arrive (streaming).

Streaming is enabled by default. (setq chatgpt-shell-streaming nil) to disable it.

chatgpt-shell customizations

Custom variableDescription
chatgpt-shell-google-api-url-baseGoogle API’s base URL.
chatgpt-shell-prompt-header-write-git-commitPrompt header of ‘git-commit‘.
chatgpt-shell-highlight-blocksWhether or not to highlight source blocks.
chatgpt-shell-display-functionFunction to display the shell. Set to ‘display-buffer’ or custom function.
chatgpt-shell-prompt-header-generate-unit-testPrompt header of ‘generate-unit-test‘.
chatgpt-shell-prompt-header-refactor-codePrompt header of ‘refactor-code‘.
chatgpt-shell-prompt-header-proofread-regionPrompt header used by ‘chatgpt-shell-proofread-region‘.
chatgpt-shell-welcome-functionFunction returning welcome message or nil for no message.
chatgpt-shell-prompt-query-response-styleDetermines the prompt style when invoking from other buffers.
chatgpt-shell-model-versionThe active ChatGPT OpenAI model index.
chatgpt-shell-loggingLogging disabled by default (slows things down).
chatgpt-shell-api-url-baseOpenAI API’s base URL.
chatgpt-shell-google-keyGoogle API key as a string or a function that loads and returns it.
chatgpt-shell-ollama-api-url-baseOllama API’s base URL.
chatgpt-shell-babel-headersAdditional headers to make babel blocks work.
chatgpt-shell–pretty-smerge-mode-hookHook run after entering or leaving ‘chatgpt-shell–pretty-smerge-mode’.
chatgpt-shell-source-block-actionsBlock actions for known languages.
chatgpt-shell-default-promptsList of default prompts to choose from.
chatgpt-shell-anthropic-keyAnthropic API key as a string or a function that loads and returns it.
chatgpt-shell-prompt-header-eshell-summarize-last-command-outputPrompt header of ‘eshell-summarize-last-command-output‘.
chatgpt-shell-system-promptThe system prompt ‘chatgpt-shell-system-prompts’ index.
chatgpt-shell-transmitted-context-lengthControls the amount of context provided to chatGPT.
chatgpt-shell-root-pathRoot path location to store internal shell files.
chatgpt-shell-prompt-header-whats-wrong-with-last-commandPrompt header of ‘whats-wrong-with-last-command‘.
chatgpt-shell-read-string-functionFunction to read strings from user.
chatgpt-shell-after-command-functionsAbnormal hook (i.e. with parameters) invoked after each command.
chatgpt-shell-system-promptsList of system prompts to choose from.
chatgpt-shell-openai-keyOpenAI key as a string or a function that loads and returns it.
chatgpt-shell-prompt-header-describe-codePrompt header of ‘describe-code‘.
chatgpt-shell-insert-dividersWhether or not to display a divider between requests and responses.
chatgpt-shell-modelsThe list of supported models to swap from.
chatgpt-shell-language-mappingMaps external language names to Emacs names.
chatgpt-shell-prompt-compose-view-mode-hookHook run after entering or leaving ‘chatgpt-shell-prompt-compose-view-mode’.
chatgpt-shell-streamingWhether or not to stream ChatGPT responses (show chunks as they arrive).
chatgpt-shell-anthropic-api-url-baseAnthropic API’s base URL.
chatgpt-shell-model-temperatureWhat sampling temperature to use, between 0 and 2, or nil.
chatgpt-shell-request-timeoutHow long to wait for a request to time out in seconds.

There are more. Browse via M-x set-variable

chatgpt-shell-display-function (with custom function)

If you’d prefer your own custom display function,

(setq chatgpt-shell-display-function #'my/chatgpt-shell-frame)

(defun my/chatgpt-shell-frame (bname)
  (let ((cur-f (selected-frame))
        (f (my/find-or-make-frame "chatgpt")))
    (select-frame-by-name "chatgpt")
    (pop-to-buffer-same-window bname)
    (set-frame-position f (/ (display-pixel-width) 2) 0)
    (set-frame-height f (frame-height cur-f))
    (set-frame-width f  (frame-width cur-f) 1)))

(defun my/find-or-make-frame (fname)
  (condition-case
      nil
      (select-frame-by-name fname)
    (error (make-frame `((name . ,fname))))))

Thanks to tuhdo for the custom display function.

chatgpt-shell commands

BindingCommandDescription
chatgpt-shell-japanese-lookupLook Japanese term up.
chatgpt-shell-next-source-blockMove point to previous source block.
chatgpt-shell-prompt-compose-request-entire-snippetIf the response code is incomplete, request the entire snippet.
chatgpt-shell-prompt-compose-request-moreRequest more data. This is useful if you already requested examples.
chatgpt-shell-execute-babel-block-action-at-pointExecute block as org babel.
C-c C-schatgpt-shell-swap-system-promptSwap system prompt from `chatgpt-shell-system-prompts’.
chatgpt-shell-system-prompts-menuChatGPT
chatgpt-shell-prompt-compose-swap-model-versionSwap the compose buffer’s model version.
chatgpt-shell-describe-codeDescribe code from region using ChatGPT.
C-<up> or M-pchatgpt-shell-previous-inputCycle backwards through input history, saving input.
C-c C-vchatgpt-shell-swap-modelSwap model version from `chatgpt-shell-models’.
C-x C-schatgpt-shell-save-session-transcriptSave shell transcript to file.
chatgpt-shell-proofread-regionProofread text from region using ChatGPT.
chatgpt-shell-prompt-compose-quit-and-close-frameQuit compose and close frame if it’s the last window.
chatgpt-shell-prompt-compose-other-bufferJump to the shell buffer (compose’s other buffer).
chatgpt-shell-prompt-compose-next-blockJump to and select next code block.
chatgpt-shellStart a ChatGPT shell interactive command.
RETchatgpt-shell-submitSubmit current input.
chatgpt-shell-prompt-compose-swap-system-promptSwap the compose buffer’s system prompt.
chatgpt-shell-describe-imageRequest OpenAI to describe image.
chatgpt-shell-prompt-compose-search-historySearch prompt history, select, and insert to current compose buffer.
chatgpt-shell-prompt-compose-previous-historyInsert previous prompt from history into compose buffer.
chatgpt-shell-delete-interaction-at-pointDelete interaction (request and response) at point.
chatgpt-shell-refresh-renderingRefresh markdown rendering by re-applying to entire buffer.
chatgpt-shell-explain-codeDescribe code from region using ChatGPT.
chatgpt-shell-execute-block-action-at-pointExecute block at point.
chatgpt-shell-load-awesome-promptsLoad `chatgpt-shell-system-prompts’ from awesome-chatgpt-prompts.
chatgpt-shell-write-git-commitWrite commit from region using ChatGPT.
chatgpt-shell-prompt-compose-previous-blockJump to and select previous code block.
chatgpt-shell-restore-session-from-transcriptRestore session from transcript.
chatgpt-shell-prompt-compose-next-interactionShow next interaction (request / response).
C-c C-pchatgpt-shell-previous-itemGo to previous item.
chatgpt-shell-fix-error-at-pointFixes flymake error at point.
chatgpt-shell-prompt-appending-kill-ringMake a ChatGPT request from the minibuffer appending kill ring.
C-<down> or M-nchatgpt-shell-next-inputCycle forwards through input history.
chatgpt-shell-prompt-compose-view-modeLike `view-mode`, but extended for ChatGPT Compose.
chatgpt-shell-clear-bufferClear the current shell buffer.
C-c C-nchatgpt-shell-next-itemGo to next item.
chatgpt-shell-prompt-compose-send-bufferSend compose buffer content to shell for processing.
C-c C-echatgpt-shell-prompt-composeCompose and send prompt from a dedicated buffer.
chatgpt-shell-rename-bufferRename current shell buffer.
chatgpt-shell-remove-block-overlaysRemove block overlays. Handy for renaming blocks.
chatgpt-shell-send-regionSend region to ChatGPT.
chatgpt-shell-send-and-review-regionSend region to ChatGPT, review before submitting.
C-M-hchatgpt-shell-mark-at-point-dwimMark source block if at point. Mark all output otherwise.
chatgpt-shell–pretty-smerge-modeMinor mode to display overlays for conflict markers.
chatgpt-shell-mark-blockMark current block in compose buffer.
chatgpt-shell-prompt-compose-replyReply as a follow-up and compose another query.
chatgpt-shell-set-as-primary-shellSet as primary shell when there are multiple sessions.
chatgpt-shell-rename-block-at-pointRename block at point (perhaps a different language).
chatgpt-shell-quick-insertRequest from minibuffer and insert response into current buffer.
chatgpt-shell-reload-default-modelsReload all available models.
S-<return>chatgpt-shell-newlineInsert a newline, and move to left margin of the new line.
chatgpt-shell-generate-unit-testGenerate unit-test for the code from region using ChatGPT.
chatgpt-shell-prompt-compose-next-historyInsert next prompt from history into compose buffer.
C-c C-cchatgpt-shell-ctrl-c-ctrl-cIf point in source block, execute it. Otherwise interrupt.
chatgpt-shell-eshell-summarize-last-command-outputAsk ChatGPT to summarize the last command output.
M-rchatgpt-shell-search-historySearch previous input history.
chatgpt-shell-modeMajor mode for ChatGPT shell.
chatgpt-shell-prompt-compose-modeMajor mode for composing ChatGPT prompts from a dedicated buffer.
chatgpt-shell-previous-source-blockMove point to previous source block.
chatgpt-shell-promptMake a ChatGPT request from the minibuffer.
chatgpt-shell-japanese-ocr-lookupSelect a region of the screen to OCR and look up in Japanese.
chatgpt-shell-refactor-codeRefactor code from region using ChatGPT.
chatgpt-shell-japanese-audio-lookupTranscribe audio at current file (buffer or `dired’) and look up in Japanese.
chatgpt-shell-eshell-whats-wrong-with-last-commandAsk ChatGPT what’s wrong with the last eshell command.
chatgpt-shell-prompt-compose-cancelCancel and close compose buffer.
chatgpt-shell-prompt-compose-retryRetry sending request to shell.
chatgpt-shell-versionShow `chatgpt-shell’ mode version.
chatgpt-shell-prompt-compose-previous-interactionShow previous interaction (request / response).
chatgpt-shell-interruptInterrupt `chatgpt-shell’ from any buffer.
chatgpt-shell-view-at-pointView prompt and output at point in a separate buffer.

Browse all available via M-x.

Feature requests

  • Please go through this README to see if the feature is already supported.
  • Need custom behaviour? Check out existing issues/feature requests. You may find solutions in discussions.

Pull requests

Pull requests are super welcome. Please reach out before getting started to make sure we’re not duplicating effort. Also search existing discussions.

Reporting bugs

Setup isn’t working?

Please share the entire snippet you’ve used to set chatgpt-shell up (but redact your key). Share any errors you encountered. Read on for sharing additional details.

Found runtime/elisp errors?

Please enable M-x toggle-debug-on-error, reproduce the error, and share the stack trace.

Found unexpected behaviour?

Please enable logging (setq chatgpt-shell-logging t) and share the content of the *chatgpt-log* buffer in the bug report.

Babel issues?

Please also share the entire org snippet.

Support my work

👉 Find my work useful? Support this work via GitHub Sponsors or buy my iOS apps.

My other utilities, packages, apps, writing…

Contributors

Made with contrib.rocks.