← Back to home@vritser

dsh-emacs

An Emacs client for DeepSeek Harness

Stars
31
Language
Emacs Lisp
Created
Aug 23, 2026
Updated
Oct 7, 2026

Introduction

dsh-emacs — an Emacs client for DeepSeek Harness

dsh-emacs brings DeepSeek Harness (dsh) into Emacs: streaming replies, tool calls, thinking blocks, slash commands, file/session references, and model selection. It uses Emacs built-ins (Emacs 27.1+) with no third-party dependencies.

Chat buffer with streaming replies and tool calls

0.5.x targets the dsh 0.1.7 wire protocol (servers 0.1.5 or newer: the queue surfaces read the inbox session projection, which 0.1.5, 0.1.6 and 0.1.7 all publish).

Quick start

You need Emacs 27.1+ and a provider/model configured in dsh to send messages. dsh-emacs can install and start the dsh server for you; provider credentials and model configuration remain in dsh. For an existing local or remote server, set its address as described in Server setup before connecting.

  1. Clone the repository:

    git clone https://github.com/vritser/dsh-emacs.git ~/dsh-emacs
    
  2. Add this to your Emacs configuration and evaluate it, or restart Emacs:

    (add-to-list 'load-path (expand-file-name "~/dsh-emacs"))
    (require 'dsh-emacs)
    
  3. Run M-x dsh-emacs to open the session list. If no local server is running, dsh-emacs starts one, offering to install the CLI if it is missing. On a fresh dsh setup, use M-x dsh-emacs-open-web to configure your provider and model before sending a message.

  4. Press c in the session list to create a session. Use C-c C-m in the chat buffer to choose a model if needed.

  5. Type after the ❯ prompt and press C-c C-c to send your first message.

For an optional use-package setup, see Example configuration.

Using it

Chat buffers show the DeepSeek Harness whale in the mode line when SVG is supported: blue on dark themes, black on light themes, with DSH as the terminal fallback. Colors follow theme changes. The icon keeps the standard Emacs major-mode menu. See Mode-line status.

Session list

M-x dsh-emacs opens your sessions, grouped by workspace. Press c to create a session or RET to open one. TAB folds a workspace header or expands a session's subagents beneath it. Expansion arrows appear only when children are confirmed; unknown or empty catalogs have none. Child rows show their mode and activity; use TAB for nested children and RET to open a child conversation. Expansion and cursor position survive refreshes. See Session and workspace controls for list management and navigation.

Session list grouped by workspace

Subagent conversations

M-x dsh-emacs-list-subagents (or mouse-1 on the mode-line child count) opens minibuffer completion for this conversation's direct children. RET opens the selected chat; C-u before the command opens it in another window. C-g cancels and M-, returns to the originating chat position through xref. Inside a child, run the command again to select its children. Your normal completion frontend and keys apply; no separate browser buffer is created.

The count uses a thin brain-and-circuit SVG, with the brain above three downward branches, in the existing muted status-text color. Without SVG support it falls back to Nerd Font source_branch, then Sub. Only the total appears (for example Sub3); hover for the running count. Candidates show mode, activity and available duration/token metrics. M-x dsh-emacs-subagent-refresh refreshes the current conversation's catalog and parent availability.

Continuable children accept text with C-c C-c and stop with C-c C-b; C-u C-c C-c steers. Input closes while the parent is unavailable or the host connection is lost. One-shot children are read-only and cannot be interrupted through this surface. C-c C-o pages older child history. Queue editing, model selection, attachments, slash commands and late question answers are unavailable inside child chats. M-x dsh-emacs-subagent-stop chooses a running continuable child to stop after confirmation; M-x dsh-emacs-subagent-describe chooses a child for detailed inspection. Token totals include cache usage and are session projections, not billing estimates or the chat mode line's message accumulator.

See subagent integration for protocol and test details.

Inside a chat buffer

KeyWhat it does
C-c C-cSend input or interrupt; see below
C-c C-bInterrupt the running turn
C-c C-qManage the pending queue
C-c C-jManage background jobs (view output / stop)
C-c C-pAnswer a timed question whose window expired (dsh 0.2.0)
C-c C-gOpen the goal-action prefix
C-c C-mSwitch model / reasoning effort
C-c C-aAttach an image file and send it now
C-c C-vPaste the clipboard image into the next message
C-c C-dDiscard staged images
s-vPaste a clipboard image, else yank text
C-c C-s / C-c M-sSwitch session in this workspace / across all
C-c C-rRefresh
C-c C-oLoad older messages above the current transcript
C-c C-wCopy (region → code block → message at point → last reply)
C-c C-fToggle mode-line stats
C-c C-!Stop the tracked local shell process
M-p / M-nPrevious / next input
C-/ / C-_ / C-x uUndo input editing; redo with C-g C-/, or undo-redo on Emacs 28+ (the transcript is never undone)
TABComplete a slash command or skill

Sending during a running turn: by default, C-c C-c queues a non-empty message for the next turn. C-u C-c C-c steers the running turn instead; C-c C-c with empty input interrupts it. Configure this with dsh-emacs-busy-enter-behavior. The C-c C-q queue menu acts on the highlighted item; x deletes all pending items.

Type @ to choose file, directory or session references: @src/ drills into a directory and @session-title mentions another session. See @ references.

Type /, then press TAB to complete a slash command. Automatic popups depend on your completion front-end and its settings: corfu/company can provide them with auto completion enabled; stock completion, vertico and icomplete require TAB. The same list carries the session's skills (host instruction bundles), with user-only ones marked. The token completes wherever the host accepts a gesture — at the start of the message or after a space — so please /rev + TAB becomes please /review ; a name no command or skill matches is left to path/word completion. See Slash commands and Skills.

TAB completes a local file path once the token carries a separator: docs/rp, ./src/, ~/… and /abs/… complete through the stock file-name completer against the chat buffer's working directory — the session workspace — with the same directory drill-down as find-file, the path suffix after the cursor preserved, and the active completion styles (abbreviated directories work with partial-completion). A /name at the start of the input is reserved for slash commands even when their catalog is empty; in the middle of a message a /name only goes to command completion when a command or skill actually matches it, so see /usr still completes as a path. Completion reads the machine Emacs runs on, like ! shell lines — with a dsh server on another host, use an @ reference instead. A path with spaces is not handled in plain text (the token ends at the space); quote it as an @ reference.

TAB also completes an ordinary word from what is already in the buffer: the draft above point and, within dsh-emacs-word-completion-limit characters, the transcript above it — so a term from an earlier message or tool result completes instead of being retyped. Words are runs of letters, digits, _ and -, so identifier-like terms such as dsh-emacs-mode complete whole. Candidates appear nearest-first, and completion inside a word includes the existing suffix. Slash commands and @ references keep their own completion even when their catalogs are empty.

The composer shows the current goal and the next pending message above ❯. Hover over the preview for its full text, or use C-c C-q to manage pending messages. Goal shortcuts and inline controls are described in Goal actions.

The mode line shows Retry 1/3 while waiting to retry, Retrying 1/3 once the request starts, and Compacting during context compaction. Hover, click, or run M-x dsh-emacs-describe-status for details. See Execution feedback.

Plan mode shows Plan while active, or Plan → on / Plan → off while a switch is pending. Use /plan to enter and /plan off to leave; the badge persists across turns. See Plan mode. Submitted plans open as readable Markdown in a separate buffer, with Approve and execute (C-c C-c) and Request changes (C-c C-k). Requesting changes returns to the chat input for feedback. q only closes the document window; reopen a pending review with M-x dsh-emacs-plan-review, or open a previous plan from its titled transcript card. See Plan review.

Expanded file-edit cards show unchanged lines once as context, with red/green rows and totals for the changes. See Tool cards for the display rules, including the limit for very large replacements.

Answering questions

Agent ask prompts are answered in one minibuffer read. The question text is the prompt, the options are the completion candidates (each one carries its description as an annotation), and the question detail shows in the echo area.

  • Single choice: pick a candidate, RET accepts it. Empty input skips the question.
  • Multiple choice: type the options comma-separated — 2,3 or alpha,beta — and RET submits them (completing-read-multiple, Emacs' standard comma-separated input path). An unambiguous prefix works too (alph), and an ambiguous one is left as your answer text rather than guessed.
  • Anything that names no option is taken as your answer text, like at any Emacs completion prompt — there is no separate "type an answer" step, and a partly-matched answer is never silently trimmed.
  • C-c C-s skips the question (empty input does the same); C-g abandons the whole group.

Nothing is toggled in place and the reader never reopens: one read per question, so the menu cannot flicker or reorder.

(setq dsh-emacs-question-help-display 'echo-area) ; default; nil hides the detail

Try M-x dsh-emacs-question-preview locally, without contacting a server. See Question prompts for details.

Approval reasons

Approval prompts use the host's localized reason when available (dsh 0.2.0), following the Emacs message locale. To select Chinese explicitly:

(setq dsh-emacs-approval-language "zh") ; nil follows the Emacs locale

Missing translations fall back to English, then the original reason. See Approval prompts for the lookup order.

Workspaces

Workspaces group sessions by project/directory.

New sessions use the current workspace when created from a workspace header, its empty New Session row, or an existing chat. Without that context, a local server can use the Emacs project of the current buffer's directory, creating its workspace on first use. This detection is controlled by dsh-emacs-new-session-auto-project and does not run for remote servers. Otherwise the new session uses the current buffer's directory.

Local shell commands

Enter !git status and press C-c C-c to run a command locally in the session's workspace directory. Output appears in the transcript, including while a model turn is running. C-c C-! stops the tracked shell process.

Shell output is not sent to the model or saved in server history; refreshing the transcript removes it. With attachments, a leading ! is caption text sent to the model. See Shell commands for multiline scripts, shell selection and process handling.

Skills

dsh skills are host-side instruction bundles (SKILL.md plus resources) for a session's working directory and preset. dsh-emacs lists them in the / completion (user-only ones marked) and in M-x dsh-emacs-command, the same menu that runs slash commands: picking a skill inserts /name at the cursor, or with C-u, opens the picked skill's SKILL.md. The host expands a /name gesture found in your prompt, so a skill is invoked like ordinary text (/review check the parser). See Skills.

Images

C-c C-v pastes the system clipboard's image into the next message: it joins a staged-attachments row above the input, you type a caption (or leave the input empty to use the image name), and C-c C-c sends text and image together. C-c C-d discards the staged images; the trailing ✕ on the row does the same. s-v does the same as C-c C-v whenever the clipboard holds an image, and falls back to ordinary text yank otherwise; C-y always stays a plain text yank, so a clipboard carrying both an image and text can still paste the text. Where the pasteboard exposes TIFF rather than PNG (macOS), the image is converted with the system sips tool before upload, provided PNG is in dsh-emacs-attach-media-types. A rejected send restores its caption and images only while both the input and staged images are empty. Emacs 29+ users can also run M-x yank-media. C-c C-a remains the one-shot form: it picks an image file and sends it immediately. Only the media types in dsh-emacs-attach-media-types are accepted.

Models & presets

Configure providers, models and agent presets in dsh, through M-x dsh-emacs-open-web or dsh's own configuration files. Use C-c C-m to select a session's model and reasoning effort.

dsh-emacs-default-preset selects the preset for new sessions; nil uses the host default. dsh-emacs-default-model is a display fallback for the mode line and does not select the model used by a session. See Model picker for details.

Server setup

By default dsh-emacs manages a local server. To use one you run yourself, set dsh-emacs-base-url to its address. Remote addresses, including HTTPS and URLs with user:pass@ Basic auth, never trigger a local server start. Set dsh-emacs-server-auto-start to nil to disable automatic startup locally.

For a server dsh-emacs starts, launch-token authentication is automatic. For a server you started yourself, provide the launch token from the URL it prints (dsh web: …/?token=…). You can set dsh-emacs-server-auth-token to the token, or paste the whole URL into dsh-emacs-base-url. If a reverse proxy also requires Basic authentication, include its separate credentials as http://user:pass@host:port; the dsh launch token is still required. RPC authentication failures report HTTP 401 instead of asking for a username and password, and clear the rejected cookie before the next attempt.

When prompted for an external server's token, a successful answer is saved for reuse. After a server restart, the previous token may be stale and need replacing. See Server options.

Streaming and appearance

Replies and thinking appear as they arrive. Large Markdown regions finish styling while Emacs is idle; see Markdown responsiveness for tuning options.

Use M-x customize-face or custom-set-faces to change the appearance. UI styling lists the active faces and explains rendering; the streaming performance audit records measurements and remaining limits.

Documentation

Contributing

Development workflow and commit conventions are in AGENTS.md. Keep pull requests focused on one topic; for non-trivial work, open an issue first to align the scope. Run scripts/verify.sh before pushing. It checks syntax, checker self-tests, byte compilation, the full unit suite, silent loading, diff whitespace, and generated-file cleanup.

Real-server tests are separate. See E2E testing for the batch smoke suite and timed-question tests in a running Emacs.

Acknowledgments

The UI mirrors dsh web, including its tool icons, session list and context meter. Markdown rendering and folding build on agent-shell; mode-line stats and compact token formatting follow pi-mono.

License

GPL-3.0-or-later — GNU General Public License v3 or later.