From 30b7ce68cadc852fd2df0aba85aeb3fda6fde03b Mon Sep 17 00:00:00 2001 From: Ryan Schmukler Date: Tue, 2 Jun 2026 13:50:08 -0400 Subject: [PATCH] feat(chat): add eca-chat-delete command Delete the active chat from the server in one step, without the kill-buffer prompt. It resolves the current project's session and acts on its last visited chat, so it works from any buffer in the project, not only the chat buffer. When the session has other chats, any window showing the deleted chat switches to another chat first so the side window is not left empty. Bound to C-c C-S-k and added as a Delete entry in the transient menu. --- CHANGELOG.md | 1 + README.md | 1 + eca-chat.el | 37 +++++++++++++++++++++++++++++++++++++ eca-util.el | 1 + 4 files changed, 40 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f9575f..599aef8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- Add `eca-chat-delete` command to delete the active chat from the server without prompting. Works from any buffer in the project (acts on the session's last visited chat), switches the chat window to another chat first when one exists, and is bound to `C-c C-S-k` plus a `Delete` entry in the transient menu. - Bugfix: trigger `@`/`#` chat completion even when a char like `(` immediately precedes it. - Bugfix: don't crash with `(wrong-type-argument stringp nil)` when `chat/askQuestion` sends options as plain strings or option objects without a `:label`. Options are normalized via `eca-chat--normalize-question-option` (string or plist) and the label always falls back to a non-nil string. - Bugfix: keep the `stop` button available while a question is pending. A pending question keeps the turn active server-side even when the chat reports idle, so the loading/stop transient segment and `eca-chat--stop-prompt` now treat a pending question like the loading state, and the transient area is refreshed when a question is shown/answered/cancelled. diff --git a/README.md b/README.md index efdaf1e..60bbf35 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,7 @@ Chat - `eca-chat-rename`: Rename current chat - `eca-chat-clear`: Clear chat messages both on server and local. - `eca-chat-reset`: Deletes current chat and start a new one +- `eca-chat-delete`: Delete the active chat from the server without prompting (works from any buffer in the project) - `eca-chat-select-model`: Change chat model - `eca-chat-select-agent`: Change chat agent - `eca-chat-cycle-agent`: Change chat agent to next available diff --git a/eca-chat.el b/eca-chat.el index f5b9365..5f409fb 100644 --- a/eca-chat.el +++ b/eca-chat.el @@ -681,6 +681,7 @@ and resume link are not left behind under the replayed messages.") (define-key map (kbd "") #'eca-chat--key-pressed-tab) (define-key map (kbd "TAB") #'eca-chat--key-pressed-tab) (define-key map (kbd "C-c C-k") #'eca-chat-reset) + (define-key map (kbd "C-c C-S-k") #'eca-chat-delete) (define-key map (kbd "C-c C-l") #'eca-chat-clear) (define-key map (kbd "C-c C-t") #'eca-chat-toggle-trust) (define-key map (kbd "C-c C-S-t") #'eca-chat-talk) @@ -4209,6 +4210,42 @@ the empty buffer that was used to trigger the resume." :method "chat/update" :params (list :chatId eca-chat--id :title new-name))))) +;;;###autoload +(defun eca-chat-delete () + "Delete the active chat of the current session from the server. +Operates on the session's last visited chat buffer, so it can be +called from any buffer in the project, not only from the chat +buffer itself. Unlike killing the chat buffer, this never +prompts; the chat is always removed server-side. When the session +has other chats, any window showing the deleted chat switches to +another chat first." + (interactive) + (let ((session (eca-session))) + (eca-assert-session-running session) + (let* ((buffer (eca-chat--get-last-buffer session)) + (chat-id (and (buffer-live-p buffer) + (buffer-local-value 'eca-chat--id buffer)))) + (unless (and (buffer-live-p buffer) chat-id) + (user-error "No active chat to delete")) + (when-let ((other (-first (lambda (buf) + (and (buffer-live-p buf) (not (eq buf buffer)))) + (eca-vals (eca--session-chats session))))) + (setf (eca--session-last-chat-buffer session) other) + (dolist (win (get-buffer-window-list buffer nil t)) + (let ((dedicated (window-dedicated-p win))) + (set-window-dedicated-p win nil) + (set-window-buffer win other) + (set-window-dedicated-p win dedicated)))) + ;; Mark closed so the kill-buffer hook neither prompts nor sends a + ;; second chat/delete when BUFFER is killed below. + (with-current-buffer buffer + (setq-local eca-chat--closed t)) + (eca-api-request-sync session + :method "chat/delete" + :params (list :chatId chat-id)) + (when (buffer-live-p buffer) + (kill-buffer buffer))))) + ;;;###autoload (defun eca-chat-new () "Start a new ECA chat for same session." diff --git a/eca-util.el b/eca-util.el index e60deb5..a609f8d 100644 --- a/eca-util.el +++ b/eca-util.el @@ -432,6 +432,7 @@ Inheirits BASE-MAP." ("f" "Select" eca-chat-select) ("c" "Clear" eca-chat-clear) ("r" "Reset" eca-chat-reset) + ("k" "Delete" eca-chat-delete) ("R" "Rename" eca-chat-rename) ("t" "Talk" eca-chat-talk) ("p" "Repeat prompt" eca-chat-repeat-prompt)