Skip to content

Latest commit

 

History

History
121 lines (102 loc) · 38.7 KB

File metadata and controls

121 lines (102 loc) · 38.7 KB

Changelog

Unreleased

  • Bugfix: TAB on an expandable block label no longer blocks the <tab> -> TAB key translation, extending the fix in #281 to the label's keymap text property, which outranks the mode map. It bound the raw <tab> event and shadowed layered keymaps binding only TAB; it now binds TAB, which also makes the label's own binding reachable in terminal frames, where <tab> is never sent.

  • Bugfix: chat block and table faces now follow theme switches (#301). The refresh was hooked buffer-locally on enable-theme-functions, so it never ran (theme switches run the hook from the user's current buffer, not the chat buffer); it is now a global hook, with enable-theme/disable-theme advice on Emacs < 29 where that hook doesn't exist. Expandable-block line-prefix indentation also baked the resolved background color into the prefix strings; it now stores the face symbol, so already-rendered indentation updates on redisplay too.

  • Show the summaries of tool calls pending approval in the inline prompt overlay status instead of the generic "Waiting for tool call approval" progress text (which overwrote the approval status), so it's clear what is about to run. With multiple pending tools, resolving one now keeps showing the remaining summaries, and approving from another client clears the stale approve/reject hints.

  • Bugfix: a region ending at the beginning of a line (whole-lines selection) no longer includes that extra line in the lines range sent by the eca-chat-add-context-* commands, and ranges/cursor positions computed in narrowed buffers now use absolute file line numbers.

  • Add eca-chat-inline-prompt (transient menu I): ask ECA from any buffer, streaming the answer into a markdown-rendered scrollable overlay at point, backed by the server chat/inlinePrompt method. Picks the backing chat on first use (forking an existing one server-side or starting fresh), sticky per buffer, C-u re-asks; region/file attached as context, r replies, a/d answer tool calls, m opens a menu to change inline model/variant/agent, open the backing chat or toggle overlays visibility (eca-chat-inline-toggle-overlays). Requires eca server with chat/inlinePrompt support.

  • Killing a chat buffer no longer asks about deleting the chat from the server: it just closes the buffer, leaving the chat resumable until the server retention cleanup removes it. Delete explicitly with eca-chat-delete, the workspaces dashboard d, or the new server /delete-chat command.

  • Add eca-chat-compose (transient menu i): compose a prompt in a dedicated markdown buffer instead of the inline chat prompt field, convenient for long or multi-line prompts. C-c C-c sends the buffer content to the chat it was opened from (or the session's last used chat), C-c C-k discards it. @context/#filepath mentions complete against the ECA server, and yanking a clipboard image inserts an @file mention pointing at a saved screenshot, mirroring the chat buffer.

  • Bugfix: Enter now works with transient/overriding keymaps that bind only "RET" (e.g. Embark's action maps) inside chat buffers. eca-chat-mode-map bound the raw <return> event, which blocked the <return> -> RET key translation in GUI frames, so those keymaps' RET binding was never reached and the keypress silently fell through to eca-chat--key-pressed-return instead; only RET is bound now.

  • Restore the rolled-back user message into the prompt field. Rollback chat to before this message (when messages are included) used to discard the clicked message's text together with the rest of the turn; the text now lands in the prompt unsent, ready to edit and resend, and any draft already typed in the prompt is kept below it, separated by a blank line.

  • Fall back to the buffer's xref backend (eglot, etags with a loaded tags table, any custom backend) for editor/getDefinition/editor/getReferences when lsp-mode cannot serve the file, instead of answering no-server. includeDeclaration: false is honored by filtering definition locations out of the references, since LSP-backed xref backends always include the declaration. (editor-code-assistant/eca#351)

  • Support editor/getDefinition and editor/getReferences server requests via lsp-mode, letting the LLM navigate to a symbol definition/references using the editor's LSP. When no server is attached yet for a file of a known lsp project, lsp is started in background and starting is answered so the eca server retries. starting is also answered while an attached workspace is still initializing, since lsp-mode would reject the request with a misleading "does not support method" capability error before the initialize handshake completes. Synchronous lsp-mode errors are answered as error responses, and any server-request handler error now answers an error response instead of escaping into the process filter ("error in process filter" in Messages) and leaving the request pending until the server times it out (30s). Requires eca server with editor navigation support (editor-code-assistant/eca#351).

  • Bugfix: a GitHub outage no longer prevents eca from starting. The release check now has a timeout and retries (eca-server-fetch-timeout, eca-server-fetch-retries) instead of hanging Emacs, failed checks back off for 60s instead of re-blocking every start, and error payloads (e.g. rate limit) are no longer cached as a valid releases list. When an update download fails but a server is already installed, eca warns and starts the installed binary instead of not starting at all (also for errors in the async url-retrieve download, which were previously uncaught).

  • Support adding a region of a non-file buffer (magit, vterm, compilation, the chat itself) as context with the eca-chat-add-context-* commands: the selection becomes a text context with a lines range, its content sliced from the live buffer at prompt time. The commands now signal a user-error instead of silently doing nothing when no context applies. #294

  • Bugfix: eca-chat-remove-workspace-root failed with Workspace folder not found for a dir shown in the mode-line. Workspace dirs are matched with string=, but C-u M-x eca stored them as read-directory-name returned them (~/proj/) while add and remove expanded their argument, and expand-file-name keeps trailing slashes so /x and /x/ never matched either. Dirs are now expanded and stripped of the trailing slash wherever they are stored or compared, which also stops M-x eca opening a second session for a root already held under a different spelling.

  • Bugfix: shell command breakdown no longer stays yellow when the tool call was auto-approved (e.g. trust mode) or already ran; commands now show green once approved, keeping yellow only while approval is pending.

  • Turn eca-workspaces into a live dashboard: rows refresh automatically as chats change state, each chat showing status (⏳ running, 🚧 pending approval, ❓ waiting answer), elapsed time, cost and model, with workspaces sorted alphabetically. Actions on the entry at point, all listed in a transient menu (?): new chat with optional initial prompt (+), delete chat or stop workspace with confirmation (d/DEL), rename (r), fork (f), compact (C), select model/variant (m/v), accept/reject pending tool calls (a/A/x), stop prompt (s) and resume a closed chat (R).

  • Bugfix: quitting the delete-chat prompt (C-g) now really cancels the kill instead of leaving the chat alive but orphaned, without a window, tab or registry entry. Quitting it during eca-chat-reset leaves the session untouched as well, instead of adding a replacement chat next to the one that survived. The answered prompt no longer lingers in the echo area.

0.10.0

  • Bugfix: restore the status colors of the mcps: counts in the chat header-line, lost in #288 which overrode the per-status faces with eca-chat-option-value-face; that face is now merged with lower priority so both apply.

  • Bugfix: cursor context no longer shows as @cursor( :) when no position was tracked yet. Tracking now picks the most recent file buffer inside a workspace (instead of bailing when the newest file buffer is external), the chip shows @cursor(no file) until a position exists, and at prompt send the position is computed as a last resort, dropping the cursor context instead of sending it with empty path/position.

  • Bugfix: stop Emacs stuttering while ECA runs and a buffer of another project is visited. The cursor-tracking idle timer resolved the session via eca-session from whatever buffer was current; for buffers outside any session that resolution is never cached, so every idle tick probed project.el and shelled out git rev-parse --git-common-dir (once for the buffer root plus once per session workspace folder). The timer now matches the last visited file buffer against the sessions' workspace folders in memory, and eca--git-common-dir caches its results (cleared on session create/delete) and runs git via process-file without an intermediate shell. #275

  • Bugfix: server auto-update was permanently disabled when the version file (eca-server-version-file-path) was missing (e.g. after an interrupted update): the nil version made the up-to-date check always pass, and eca-server-check-updates misreported it as not installed. A missing version file now triggers a reinstall on start, the updater only writes the version file on success instead of deleting it upfront, startup uses the installed binary when the release check fails (offline), and eca-uninstall-server removes the version file too.

  • Bugfix: prevent (invalid-function #<symbol json-parse-buffer ...>) on Emacs 31 when fetching releases or parsing server messages. eca-api--json-read-buffer expanded to a shared literal form that the byte-compiler can mutate in place (macroexp--posify-form-1), poisoning later runtime expansions; the macro now conses a fresh form on each expansion. #284

  • Bugfix: don't crash with wrong-type-argument number-or-marker-p nil on chat/statusChanged idle when the chat buffer lost its prompt-block overlays. eca-chat--content-insertion-point and the table align/beautify end bound now fall back to point-max instead of (1- nil), and eca-chat--refresh-progress skips when the progress overlay is missing. #283

  • Bugfix: stop the chat view jumping up and down while the LLM streams small chunks (e.g. preparing tool calls). Chat buffers now set scroll-conservatively/scroll-margin buffer-locally so redisplay scrolls line by line instead of recentering (the jumping depended on the user's global scroll settings). Also reduce streaming redisplay churn: the progress spinner keeps a fixed width, the progress area only re-renders when its text changes, and unchanged tool-call labels skip their delete/re-insert. #268

  • Support adding non-file buffers (compile, comint, etc.) as chat context: @ completion now offers eligible buffer names, sent as text contexts with fresh content captured at prompt time, and the eca-chat-add-context-* commands work from non-file buffers. Customize candidates with eca-chat-context-buffer-predicate and size with eca-chat-context-buffer-max-chars. Requires eca server with text context support. #278

  • Add current-window chat display: set eca-chat-window-side to nil to open a new chat in the selected window without creating a split. This takes precedence over eca-chat-use-side-window and does not replace another chat visible in a different window on the selected frame.

  • Bugfix: auto-scroll the chat so the prompt stays visible after sending a long message. Rendering the sent user message pushed the prompt below the window end, which also made the guarded auto-scroll a no-op (its window-end check runs after the insertion, so it always failed for long messages); the send path now forces the scroll, skipped when prepending older history pages. #279

  • Make the eca-workspaces tree buffer more user-friendly. It now uses a dedicated eca-workspaces-mode (derived from special-mode), so q closes the window and g refreshes. Folding/unfolding a workspace no longer requires hitting the small arrow icon: pressing TAB/RET on its line toggles it, and pressing RET anywhere on a chat line switches to that chat. n/p move to the next/previous workspace entry. When eca-buttons-allow-mouse is enabled, clicking the workspace text folds/unfolds it and clicking a chat switches to it.

  • Add eca-mcp-toggle-server, available as M in the ECA transient menu and C-c C-S-m in chat buffers, to filter configured MCP servers and start, stop, or connect the selected server from aligned, color-coded operational-state candidates.

  • Bugfix: TAB now accepts the selected candidate in completion popups (corfu) in GUI Emacs. eca-chat-mode-map bound the raw <tab> event, which blocked the <tab> -> TAB key translation and shadowed the completion UI's own TAB binding, restarting the completion session instead; only TAB is bound now, so keymap precedence decides while a popup is open. #281

  • Bugfix: a forward deletion of the first prompt character now works instead of dinging. eca-chat--key-pressed-deletion guarded the prompt boundary by rejecting any deletion at the prompt-field start regardless of direction, so C-d and, under evil, the ~ (evil-invert-char), r, x and s operators left the first char in place (~ prepended the inverted char instead of replacing it). At the prompt-field start the guard now blocks only backward-deleting commands (delete-backward-char, backward-kill-word, evil-delete-backward-char, ...); deletions above the prompt (context/progress area) and everywhere else are unchanged.

  • Show the shell/git command breakdown in tool call approvals: raw command plus per-command lines colored by remember state (green saved, yellow not yet), and Accept and remember lists the command keys it would newly save (e.g. git status, rg), hiding when nothing new would be remembered.

  • Support TRAMP / remote hosts: the eca server starts on the remote host, the binary is resolved from the remote PATH, and paths are translated both ways using mappings derived from TRAMP workspace folders (explicit eca-local-to-remote-prefix-map entries win). Auto-install is local only: install eca on the remote or point eca-custom-command at it. #270

  • Improve chat @/# completion: selecting a directory now drills into it (keeps completing its contents) instead of finalizing; candidates are workspace-relative paths with directories ending in /; candidates no longer vanish mid-token depending on the user's completion-styles; TAB in the prompt triggers completion for @/#// tokens; typing a space after a raw @path turns it into a context chip and raw @path tokens still count as contexts when the prompt is sent.

  • Respect eca-chat-window-side (and width/height) even when eca-chat-use-side-window is nil, displaying the chat on that side via display-buffer-in-direction. To avoid overlapping/duplicate windows, placement now reuses the window already showing the chat, otherwise replaces another visible chat window in place (new-tab behavior), and only opens a new window when there is nothing to reuse.

  • Bugfix: eca-chat-add-context-to-user-prompt and eca-chat-add-filepath-to-user-prompt no longer error with window-live-p, nil when the chat buffer is not visible; the chat window is now displayed first. With a prefix arg (C-u) they add the context without selecting the chat window, leaving point where it was. #266

  • Add eca-chat-hide-markdown-markup, an opt-in display control for users seeing fenced code blocks jump in chat buffers. The default preserves the existing hidden-markup rendering; set it to nil to keep markdown fences/backticks visible, which avoids markdown-mode's fenced-code boundary invisibility during typing and streaming.

  • Bugfix: don't move the cursor while the chat is streaming. eca-chat-content-received rendered without a save-excursion, so content streamed above the prompt (e.g. while "thinking") dragged point up to the middle of the buffer; point is now preserved.

  • Bugfix: keep the :usage and :trust mode-line segments visible after adding the context-usage bar. The right-alignment reserved space from (length right), which counts the bar's pixel-width display spaces (and wide glyphs) as ~1 char each, so the segments overflowed off the right edge. It now measures the real rendered width via string-pixel-width and aligns flush to the right edge in pixels (Emacs 29+ only; on Emacs 28 right segments follow left without alignment).

  • Bugfix: closing a chat (kill-buffer, C-c C-k, or the tab close button) now switches the chat window to a sibling chat (the previous tab, or the only one left) instead of falling back to an unrelated buffer like the settings buffer, and drops the dead chat from the session registry. C-c C-k (eca-chat-reset) only starts a fresh chat when the closed chat was the last one.

  • Add eca-chat-copy-at-point, bound to C-c C-w. It copies the fenced code block at point, the assistant response at point, or the latest response as a fallback.

  • Bugfix: guard chat/askQuestion against a non-sequence :options (e.g. a malformed string from a misbehaving server). append was splitting such a string into character integers that rendered as a list of random numbers; non-sequence options are now treated as empty.

  • Add a context-usage bar in the chat mode-line, left of the token usage, showing how full the model context window is, colored by category (system prompt, rules, skills, AGENTS.md, tool definitions, tool calls, conversation, free space), each a distinct color. In graphical frames it renders pixel-width thin segments for high granularity (small percentages stay visible); terminals fall back to block characters. Same footprint either way (eca-chat-context-bar-width). Colors come from the server (canonical color/freeColor) so they are consistent; hover the bar for a legend that maps each category to its server emoji swatch (emoji/freeEmoji, matching the /context command output), or click to run the new /context command. Configurable via eca-chat-context-bar-width and the :context-bar module in eca-chat-mode-line-format. Needs an eca server that sends contextBreakdown in usage content.

  • Auto-dismiss a pending ask_user question when another client (e.g. an SSE/web client in remote mode, see eca 0.139.0) answers it first. The server resolves the ask_user tool out from under us and sends a toolCalled/toolCallRejected for that tool-call id but no longer expects our answer (it cancels our request); we now correlate that id with eca-chat--pending-question and clear the stale answer-mode prompt state instead of staying stuck waiting for input.

  • Paginate long chats instead of replaying the full history on open. With eca-chat-history-page-size non-nil (default 50), eca-chat-resume opens a chat with only the newest page and shows a clickable "Load older messages" control at the top to page through earlier history on demand via the new chat/history request (also bound to C-c C-S-o). Older pages are prepended above the existing content, reusing the streaming renderer (subagent nesting included) and kept read-only like the rest of the history. Set eca-chat-history-page-size to nil to keep the previous full-replay behavior.

  • Bugfix: resuming a previous session after a server restart no longer reopens it as a *Closed session* (mode-line) / This chat is closed buffer that rejects new prompts. eca-chat-exit (run on restart) marks the most-recent chat buffer eca-chat--closed but leaves it in the session registry; eca-chat-opened was treating that live-but-closed buffer as "already known" and replaying the resumed history into it. It now treats a registered buffer marked eca-chat--closed as stale, killing the leftover closed buffer and creating a fresh, writable one in its place.

  • Color the Doom Emacs workspace tabline by ECA session status: orange when a chat waits for approval/question, dim yellow while running. Enabled automatically on Doom (:ui workspaces module), disable with eca-doom-workspace-tabs.

  • Bugfix: keep the Doom workspace tab colors in sync with the real status. The tabline was only recomputed on workspace switch / +workspace/display, so a shown tabline went stale: a tab could stay white after its prompt started, or keep the running color after it finished. Status transitions (chat start/finish, approval/question shown or answered) now refresh the tabline while it is the message on screen, and a chat that is only stopping no longer counts as running.

  • Add eca-chat-go-to-next-attention and eca-chat-go-to-next-attention-in-project commands to jump to the next chat waiting on the user (pending tool call approval or unanswered question), cycling across all sessions or only the current one.

  • Make the chat history/output, the --- separator and the task area read-only so only the progress, @-context and prompt input lines stay editable, preventing accidental edits to previous messages and assistant output. The read-only text property is applied up to the progress area (with stickiness tuned so typing still works) and kept in sync as content streams in and as blocks are expanded. Toggle off via the new eca-chat-read-only-history (default t).

  • Add eca-table-open to view a markdown table at point in a dedicated *eca-table* buffer with truncate-lines, so wide tables can be scrolled horizontally with the header staying aligned and the whole table searched/copied. Press o with point on any table to open it, or click [o] open on a wide table's action bar. The o binding lives in a char-property keymap scoped to the table, so it takes precedence over o only while point is on a table (works under evil) and leaves o untouched elsewhere. The in-chat table rendering is otherwise unchanged.

  • 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: pressing RET on a URL in the chat now opens it even when the URL is wrapped in markdown emphasis like **https://...** or _https://..._. The face at point is a list in that case, so the old eq check missed it; the trailing **/_ markers are also stripped so the right URL opens. Proper [text](url) links still go through markdown-follow-thing-at-point.

  • Bugfix: switching chat tabs via tab-line-switch-to-next-tab/tab-line-switch-to-prev-tab or clicking a tab now switches the chat in place instead of opening it in a new window.

  • 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.

  • Bugfix: clear the chat progress text and spinner when chat/contentReceived progress/finished arrives while eca-chat--chat-loading is nil (e.g. after the 10s stopping safety-timer reset the flag, or for server-driven progress not started via eca-chat--send-prompt). The wildcard pcase arm added in 19aa392 to ignore stale duplicate finished events was dropping the visible UI cleanup too; it now still clears progress text, spinner and elapsed timers idempotently while skipping the trailing newline / fontify / queued-prompt dispatch so duplicate finished events remain harmless.

  • Bugfix: eca-chat-resume no longer silently no-ops when the picker is shown from a fresh welcome buffer. Filter out chats the server can't open (id is nil, which happens for legacy DB rows that pre-date the per-chat :id field) so they aren't offered, and surface a user-error when chat/open reports :found? false or no buffer was registered for the chat-id, instead of falling through the when-let*.

  • Add eca-chat-resume command to pick and resume a past chat via completing-read annotated with model, message count and relative update time. A Resume a previous session link in the welcome area triggers it, and e selects it in the transient menu. The originating empty welcome buffer is killed once the resumed chat is in place, so no stray tab is left behind.

  • Bugfix: avoid (wrong-number-of-arguments color-rgb-to-hsl 0) crash on chat startup in a no-window/TTY Emacs. face-background returns the literal sentinel "unspecified-bg" (not nil) on non-graphic frames, which was fed into color-lighten-name / color-darken-name from eca-chat--update-expandable-block-faces and eca-table-update-faces. New eca-safe-face-background helper in eca-util.el normalizes the "unspecified-bg" / "unspecified-fg" sentinels to nil so the when-let* guards short-circuit and the faces remain at their default appearance on TTY. #244

  • Add inline [+ Add MCP server] form and per-row remove button (with two-step inline confirmation) on the MCPs settings tab. Handles the new tool/serverRemoved notification so the UI updates live without a server restart.

  • eca-doctor now emits a ### Self-check section probing whether the loaded code carries the chat-RET expand fix: eca-chat-mode-map RET still points at eca-chat--key-pressed-return, eca-chat--key-pressed-return references the expandable clause before the markdown-link clause, the nearest expandable block label has RET bound in its text-property keymap, and the loaded eca-chat-expandable file's path (with a stale-.elc warning when the on-disk .el is newer). Each failing probe pushes a targeted hint pointing at commit dac33d8 and the appropriate remediation (pull master, restart Emacs, M-x byte-recompile-directory, or start a new chat so blocks get the new label keymap).

  • Bugfix: restore RET expanding/collapsing > blocks in the chat. The recent markdown-link-on-RET clause was intercepting the toggle when the label text was fontified as a link; the expandable check now runs before the link follow, and the label's text-property keymap also binds RET so it works in evil normal state without evil-collection wiring.

  • Add eca-doctor command that prints a markdown-formatted diagnostic report to *eca-doctor* for bug reports: a ## Versions section (same as eca-version) plus a ## Chat section sourced from the active session's auto-detected chat buffer. #242

  • Bugfix: stop the merged worktree fallback in eca-session from auto-adding an unrelated ancestor (typically $HOME) as a second workspace folder. The fallback previously declared two paths to be the "same repo" purely on git rev-parse --git-common-dir equality, which is also true for any subdirectory of a git-tracked HOME (yadm/chezmoi/git init ~); a buffer whose default-directory is ~ (e.g. the doom scratch buffer) would then trigger workspace/didChangeWorkspaceFolders and append ~ to the session created with C-u M-x eca on ~/.config/eca, showing both roots in the chat modeline. The match in eca--session-for-worktree now additionally requires that ROOT and the candidate folder are not in an ancestor/descendant relationship (real worktrees are always sibling directories, never nested), via the new eca--paths-nested-p helper. True sibling worktrees continue to merge as before. #238

  • Generate chatId client-side at chat-buffer creation. #231

  • Scope model/agent/variant selection to the active chat: eca-chat-select-model, eca-chat-select-variant and eca-chat--set-agent now write the local change to the chat buffer the user is interacting with (current buffer when invoked from inside a chat, falling back to the session's last chat) and include :chatId in the resulting chat/selectedModelChanged / chat/selectedAgentChanged notification. eca-chat-config-updated honors the new chatId field on config/updated: when present, per-chat fields (selectModel, selectAgent, selectVariant, selectTrust) apply only to that chat's buffer; when absent, the legacy session-wide path applies to every chat (still used for the initial config/updated after initialize). Fixes a long-standing leak where changing the model in one chat would silently overwrite another chat's display. #231

  • Add eca-process-wrapper-function, an optional function called with the resolved server command and the list of workspace folder paths just before make-process is invoked. It must return a (possibly transformed) command list, letting users run the eca server inside any command-prefix-style sandbox (firejail, bubblewrap, jai, docker, ...) directly from elisp — without a separate launcher script and with the workspace roots dynamically injected into the sandbox's directory whitelist. New "Sandboxing" section in the README documents the recommended pattern with jai and firejail examples.

  • Expand the eca-send-process-id docstring to explicitly cover sandbox tools that hide or remap the host PID (firejail, bubblewrap, jai, containers): in those setups the server's parent-process watchdog would otherwise see an invalid PID and shut down right after startup, so the variable must be set to nil. Cross-references eca-process-wrapper-function.

  • Bugfix: avoid void-function flymake-diagnostic-code errors in eca-editor--flymake-diagnostics (which surfaced as a 30s Timeout waiting for editor diagnostics response on editor/getDiagnostics whenever any flymake diagnostic was processed) by fboundp-guarding the flymake-diagnostic-code accessor; :code is simply omitted when the accessor isn't shipped with the running Emacs's flymake. The existing declare-function is preserved so the byte-compiler stays quiet on Emacsen that do expose it. #228

  • Hide the [-] (remove workspace) mode-line button when only a single workspace folder is active in the session, since removing the last workspace is disallowed anyway; the [+] (add workspace) button is still always shown. The button reappears automatically as soon as a second workspace is added.

  • Stop the CPU peg when watching a long uncollapsed write-tool argument body stream in (#234). The dominant cost was gfm-mode's markdown matchers (markdown-match-italic / markdown-match-bold / markdown-match-code) re-fontifying the whole growing body on every chunk, because toolCallPrepare updates go through eca-chat--update-expandable-content's delete-region + reinsert branch (which marks the entire body as needing fontification and lets jit-lock then run the matchers over it on the next redisplay). Introduce a buffer-local custom font-lock-fontify-region-function (eca-chat--fontify-region) that walks the requested region in eca-no-fontify text-property runs and only delegates the un-tagged sub-ranges to font-lock-default-fontify-region; tagged sub-ranges are skipped entirely, so jit-lock has nothing to do on them. The toolCallPrepare arm now tags the streaming body with eca-no-fontify at the source string, which travels naturally through (propertize content 'line-prefix ...) inside eca-chat--update-expandable-content and through the toggle-open path's eca-chat--insert, so every code path that lands the body in the buffer carries the tag. Cleanup is automatic: when toolCalled arrives it reinserts un-tagged final content via the same delete-region + reinsert path, after-change-functions clear fontified, and jit-lock then refontifies normally on the next redisplay (no manual font-lock-flush needed). Measured via a new tool-prep-stream-{skip,full} benchmark in benchmarks/eca-chat-bench.el (synchronous fontify per chunk to simulate jit-lock under --batch): at body 1000/5000/20000 chars, per-chunk cost drops from ~4.5ms / ~62ms / ~908ms to ~173µs / ~144µs / ~225µs respectively (26× / 432× / 4030×). The post-fix path is FLAT in body size (only the existing buffer churn remains), and the 20000-char run drops from 265 GCs to 0.

  • Make long chats fast again. Replaced linear (overlays-in (point-min) (point-max)) scans in singleton-overlay accessors and the expandable-block id lookup with O(1) buffer-local caches (a per-overlay slot for the chat singletons; a per-buffer hash table for expandable blocks). Region-scoped the per-turn font-lock-ensure and end-of-stream eca-chat--align-tables / eca-chat--beautify-tables to start at eca-chat--last-user-message-pos instead of point-min, so cost is bounded by the current turn rather than the full chat history. Measured on a synthetic 500-turn fixture (benchmarks/eca-chat-bench.el): per-streamed-chunk insert dropped from ~426µs to ~6µs (71×), expandable-id lookup from ~324µs to ~0.4µs (790×), and the end-of-stream finalize block from ~2.6s to ~9ms (274×).

  • Add benchmarks/eca-chat-bench.el, a chat-rendering benchmark harness runnable via M-x eca-chat-bench-run or eask emacs --batch -l benchmarks/eca-chat-bench.el -f eca-chat-bench-run. Builds synthetic chat buffers of varying size and times the streaming hot paths (insertion-point lookup, streamed chunk insert, expandable lookup/update, end-of-stream finalize); prints a markdown table with per-call µs / wall-ms / GC counts so future changes can be validated quantitatively.

  • Bugfix: avoid corrupted redisplay in emacs -nw by using single-cell BMP glyphs ( / ) for the chat mode-line trust indicator on terminal frames; emoji defaults are preserved on graphic frames. Configurable via eca-chat-trust-on-symbol-tty / eca-chat-trust-off-symbol-tty. #229

  • Expire the cached GitHub releases list used for eca server update detection. Previously eca-process--releases-cache was populated once per Emacs session and never invalidated, so long-running Emacs instances never noticed new eca server releases (and eca-install-server, despite its docstring, was bound by the same stale cache). New eca-server-releases-cache-ttl defcustom (default 3600 seconds; nil disables expiry; 0 always refetches) refreshes the cache on access once expired. New autoloaded interactive command eca-server-check-updates clears the cache and reports whether the installed server is up to date, has an available upgrade, or whether the GitHub check failed. eca-install-server now also clears the cache before fetching the latest version, matching its "Force download the latest eca server." docstring. #230

  • Render assistant-emitted ChatImageContent from chat/contentReceived inline in the chat buffer (e.g. images returned by the OpenAI image_generation tool). Decodes the base64 payload and displays it via an overlay carrying create-image data (overlay rather than display text-property so markdown-mode/gfm-mode font-lock cannot strip it on font-lock-ensure), falling back to a [Image: <mediaType>, <N> bytes] placeholder on TTY frames or when the image type is unavailable in the current Emacs build. Sizing is configurable via eca-chat-image-max-width (default fit-window — scales to the chat window body width, hard-capped by eca-chat-image-window-fit-cap, default 800px) and eca-chat-image-max-height (default nil); a fixed integer or nil (unconstrained) are also accepted for eca-chat-image-max-width. Per-chat zoom: eca-chat-image-zoom-in (C-c C-z + / C-c C-z =), eca-chat-image-zoom-out (C-c C-z -), and eca-chat-image-zoom-reset (C-c C-z 0) mutate the buffer-local eca-chat-image-scale (multiplier on the resolved width cap, default 1.0) by eca-chat-image-scale-step (default 1.2) and re-rasterize already-rendered images via image-flush so existing images snap to the new size without re-fetching. Save inline images to disk via eca-chat-save-image-at-point (C-c C-z s), also reachable via RET or mouse-2 directly on the image overlay (with a tooltip describing media type / byte count), with the destination customizable via eca-chat-save-image-directory (default workspace-root, resolving to <root>/.eca/images/; also accepts a literal path string or nil for default-directory) and eca-chat-save-image-filename-format (default eca-image-<YYYYMMDD-HHMMSS>.<ext>); saved bytes are pulled directly from the existing overlay via image-property :data, so display zoom does not affect the file and no server round-trip is needed.

  • Syntax-highlight the inline eca-complete ghost-text overlay using the buffer's major-mode, with optional foreground dimming for a "ghost" look similar to Cursor / Copilot. Configurable via eca-completion-syntax-highlight (default t) and eca-completion-overlay-dim-ratio (default 0.5, nil disables dimming). eca-completion-overlay-face now composes with APPEND priority on top of font-lock faces instead of overriding them. #227

  • Sync the mode-line trust indicator on chat resume by honoring the new selectTrust field on config/updated: the shield/flame icon now matches the server's persisted trust state for the resumed chat instead of always defaulting to off (eca #426).

  • Defer fontification during chat streaming: replace the per-chunk font-lock-ensure with a debounced idle-timer scheduler plus a single guaranteed font-lock-ensure at end-of-stream, configurable via eca-chat-fontify-debounce-interval (default 0.15s, nil disables intermediate fontify). Cuts font-lock CPU by ~99% on fast-streaming models and ~84% on slow-streaming ones.

  • Improve mode-line trust indicator: 🔥 (fire) when ON, 🛡 (shield) when OFF. Customizable via eca-chat-trust-on-symbol / eca-chat-trust-off-symbol.

  • Add eca-chat-remove-workspace-root command and [-] mode-line button to remove a workspace folder from a running session. Both 'add' and 'remove' is in the transient menu (W a / W r).

  • Add eca-chat-mode-line-format for customizable chat mode line layout. #184

  • Fix top-level (require 'tab-line) causing side effects when eca-chat-tab-line is nil. #195

  • Add support for chat/opened server notification, enabling the /fork command to open forked chats as new tabs.

  • Fix eca--uri-to-path returning wrong path on Windows (leading slash before drive letter). #200

  • Add eca-settings-tab-line option to disable tab-line-mode in settings buffers. #201

  • Replace helpful-heading face dependency with eca-settings-heading defface. #201

  • Fix settings tab re-registration reordering tabs. #201

  • Add support for chat/askQuestion server request, enabling tools to ask users questions with selectable options in the chat buffer. #338

0.6.0

  • Add eca-chat-save-to-file command. #95
  • Fix chat not being closed. #89
  • Fix: check existing eca sessions when opening chat. #88
  • Add rewrite feature.
  • Fix issue with view_ediff displaying empty buffers in ediff. #86
  • Fix user message contexts and file paths not being sent. #112

Started tracking since 03/11/25