branch: elpa/aidermacs
commit 1a47d246d0a99bfafd3ef7df31c8e47890e976c1
Merge: ba8508d7d8 9db897d312
Author: Matthew Zeng <matthew...@posteo.net>
Commit: GitHub <nore...@github.com>

    Merge pull request #45 from MatthewZMD/ediff
    
    Use Ediff to Show Better Code Changes
---
 README.md                   |  52 ++++---
 aidermacs-backend-comint.el |  43 +++++-
 aidermacs-backend-vterm.el  |  76 +++++++---
 aidermacs-backends.el       | 135 ++++++++++++++---
 aidermacs-models.el         |  17 +--
 aidermacs.el                | 356 ++++++++++++++++++++++++++++++++++----------
 6 files changed, 522 insertions(+), 157 deletions(-)

diff --git a/README.md b/README.md
index 83a0f0ee1a..b9a970cb26 100644
--- a/README.md
+++ b/README.md
@@ -4,10 +4,9 @@
 
 # Aidermacs: AI Pair Programming in Emacs
 
-Miss using [Cursor](https://cursor.sh) but prefer living in Emacs? Aidermacs 
brings Cursor-like AI-powered development to your Emacs workflow by integrating 
[Aider](https://github.com/paul-gauthier/aider), one of the most powerful 
open-source AI pair programming tools available. As a community-driven project, 
Aidermacs prioritizes the needs and preferences of Emacs users. It provides the 
same powerful features you'd find in Cursor:
-
-Key features:
+Missing [Cursor](https://cursor.sh) but prefer living in Emacs? Aidermacs 
brings Cursor-like AI-powered development to your Emacs workflow by integrating 
[Aider](https://github.com/paul-gauthier/aider), one of the most powerful 
open-source AI pair programming tools. As a community-driven project, Aidermacs 
prioritizes Emacs users' needs and preferences while providing the same 
powerful features found in Cursor!
 
+- Built-in **Ediff** integration for reviewing AI-generated changes
 - Top performance on the SWE Bench, solving real GitHub issues in major open 
source projects
 - Support for multi-file edits in complex codebases
 - Real-time file synchronization for true pair programming
@@ -22,14 +21,14 @@ Key features:
 
 ### Community-Driven Development
 
-Aidermacs thrives on community involvement. We believe that the best software 
is built collaboratively, with input from users and contributors. We encourage 
you to:
+Aidermacs thrives on community involvement. We believe collaborative 
development with user and contributor input creates the best software. We 
encourage you to:
 
 - Contribute Code: Submit pull requests with bug fixes, new features, or 
improvements to existing functionality.
 - Report Issues: Let us know about any bugs, unexpected behavior, or feature 
requests through GitHub Issues.
 - Share Ideas: Participate in discussions and propose new ideas for making 
Aidermacs even better.
 - Improve Documentation: Help us make the documentation clearer, more 
comprehensive, and easier to use.
 
-Your contributions are essential to making Aidermacs the best AI pair 
programming tool in Emacs!
+Your contributions are essential for making Aidermacs the best AI pair 
programming tool in Emacs!
 
 <a href = "https://github.com/MatthewZMD/aidermacs/graphs/contributors";>
   <img src = "https://contrib.rocks/image?repo=MatthewZMD/aidermacs"/>
@@ -68,13 +67,13 @@ You can customize the default AI model used by Aidermacs by 
setting the `aiderma
 (setq aidermacs-default-model "sonnet")
 ```
 
-This allows you to easily switch between different AI models without modifying 
the `aidermacs-extra-args` variable.
+This enables easy switching between different AI models without modifying the 
`aidermacs-extra-args` variable.
 
 *Note: This configuration will be overwritten by the existence of an 
`.aider.conf.yml` file (see 
[details](#Overwrite-Configuration-with-Configuration-File)).*
 
 ### Dynamic Model Selection
 
-Aidermacs provides intelligent model selection for the solo (non-Architect) 
mode that automatically detects and integrates with multiple AI providers:
+Aidermacs offers intelligent model selection for solo (non-Architect) mode, 
automatically detecting and integrating with multiple AI providers:
 
 - Automatically fetches available models from supported providers (OpenAI, 
Anthropic, DeepSeek, Google Gemini, OpenRouter)
 - Caches model lists for quick access
@@ -97,7 +96,7 @@ The system will automatically filter models to only show ones 
that are:
 
 ### Architect Mode - Separating Code Reasoning and Editing Models
 
-Aidermacs supports an experimental mode that leverages two models for each 
coding task: an Architect model for reasoning and an Editor model for 
generating code edits. This approach has **achieved state-of-the-art (SOTA) 
results on aider's code editing benchmark**, as detailed in [this blog 
post](https://aider.chat/2024/09/26/architect.html).
+Aidermacs features an experimental mode using two specialized models for each 
coding task: an Architect model for reasoning and an Editor model for code 
generation. This approach has **achieved state-of-the-art (SOTA) results on 
aider's code editing benchmark**, as detailed in [this blog 
post](https://aider.chat/2024/09/26/architect.html).
 
 To enable this mode, set `aidermacs-use-architect-mode` to `t`. You must also 
configure the `aidermacs-architect-model` variable to specify the model to use 
for the Architect role.
 
@@ -117,7 +116,7 @@ When Architect mode is enabled, the 
`aidermacs-default-model` setting is ignored
 
 Choose your preferred terminal backend by setting `aidermacs-backend`:
 
-`vterm` provides better terminal compatibility, while `comint` is a simple, 
built-in option that's still fully compatible with aidermacs.
+`vterm` offers better terminal compatibility, while `comint` provides a 
simple, built-in option that remains fully compatible with Aidermacs.
 
 ```emacs-lisp
 ;; Use vterm backend (default is comint)
@@ -142,20 +141,20 @@ You can customize keybindings for multiline input, this 
key allows you to enter
 
 ### Re-Enable Auto-Commits
 
-Aider by default automatically commits changes made by the AI. We find this 
behavior *very* intrusive, so we disabled it for you. You can re-enable 
auto-commits by setting `aidermacs-auto-commits` to `t`:
+Aider automatically commits AI-generated changes by default. We consider this 
behavior *very* intrusive, so we've disabled it. You can re-enable auto-commits 
by setting `aidermacs-auto-commits` to `t`:
 
 ```emacs-lisp
 ;; Enable auto-commits
 (setq aidermacs-auto-commits t)
 ```
 
-With auto-commits disabled, you'll need to manually commit changes using your 
preferred Git workflow.
+With auto-commits disabled, you must manually commit changes using your 
preferred Git workflow.
 
 *Note: This configuration will be overwritten by the existence of an 
`.aider.conf.yml` file (see 
[details](#Overwrite-Configuration-with-Configuration-File)).*
 
 ### Customizing Aider Options with `aidermacs-extra-args`
 
-If the above configurations aren't enough already, the `aidermacs-extra-args` 
variable allows you to pass any command-line options supported by Aider.
+If these configurations aren't sufficient, the `aidermacs-extra-args` variable 
enables passing any Aider-supported command-line options.
 
 See the [Aider configuration 
documentation](https://aider.chat/docs/config/options.html) for a full list of 
available options.
 
@@ -328,29 +327,36 @@ While `aider.el` strictly mirrors Aider's CLI behavior, 
`Aidermacs` is built aro
 
 With `Aidermacs`, you get:
 
-1. Intelligent Model Selection
+1. Built-in Ediff Integration for AI-Generated Changes
+   - Seamless Code Review: Automatically shows diffs for all AI-modified files 
using Emacs' powerful `ediff` interface
+   - Familiar Interface: Uses Emacs' native `ediff` workflow for reviewing 
changes
+   - Interactive Workflow: Accept or reject changes with standard `ediff` 
commands
+   - Syntax Highlighting: Maintains proper syntax highlighting during 
comparisons
+   - Safe Change Management: Preserves original file states for easy 
comparison and rollback
+
+2. Intelligent Model Selection
    - Automatic discovery of available models from multiple providers
    - Real-time model compatibility checking
    - Seamless integration with your configured API keys
    - Caching for quick access to frequently used models
    - Support for both popular pre-configured models and dynamically discovered 
ones
 
-2. Flexible Terminal Backend Support
+3. Flexible Terminal Backend Support
    - `Aidermacs` supports multiple terminal backends (comint and vterm) for 
better compatibility and performance
    - Easy configuration to choose your preferred terminal emulation
    - Extensible architecture for adding new backends
 
-3. Smarter Syntax Highlighting
+4. Smarter Syntax Highlighting
    - AI-generated code appears with proper syntax highlighting in major 
languages.
    - Ensures clarity and readability without additional configuration.
 
-4. Better Support for Multiline Input
+5. Better Support for Multiline Input
    - `aider` is primarily designed as a command-line program, where multiline 
input is restricted by terminal limitations.
    - Terminal-based tools require special syntax or manual formatting to 
handle multiline input, which can be cumbersome and unintuitive.
    - `Aidermacs` eliminates these restrictions by handling multiline prompts 
natively within Emacs, allowing you to compose complex AI requests just like 
any other text input.
    - Whether you're pasting blocks of code or refining AI-generated responses, 
multiline interactions in `Aidermacs` feel natural and seamless.
 
-5. Enhanced File Management from Emacs
+6. Enhanced File Management from Emacs
    - List files currently in chat with `M-x aidermacs-list-added-files`
    - Drop specific files from chat with `M-x aidermacs-drop-file`
    - View output history with `M-x aidermacs-show-output-history`
@@ -359,17 +365,17 @@ With `Aidermacs`, you get:
    - Create a temporary file for adding code snippets or notes to the Aider 
session with `M-x aidermacs-create-session-scratchpad`
    - and more
 
-6. Greater Configurability
+7. Greater Configurability
    - `Aidermacs` offers more customization options to tailor the experience to 
your preferences.
 
-7. Streamlined Transient Menu Selection
+8. Streamlined Transient Menu Selection
    - The transient menus have been completely redesigned to encompass 
functionality and ergonomics, prioritizing user experience.
 
-8. Flexible Ways to Add Content
+9. Flexible Ways to Add Content
    - `Aidermacs` provides multiple ways to add content to the Aider session, 
including adding files, creating temporary scratchpad files, and more.
 
-9. Community-Driven Development
-   - `Aidermacs` is actively developed and maintained by the community, 
incorporating user feedback and contributions.
-   - We prioritize features and improvements that directly benefit Emacs 
users, ensuring a tool that evolves with your needs.
+10. Community-Driven Development
+    - `Aidermacs` is actively developed and maintained by the community, 
incorporating user feedback and contributions.
+    - We prioritize features and improvements that directly benefit Emacs 
users, ensuring a tool that evolves with your needs.
 
 ... and more to come 🚀
diff --git a/aidermacs-backend-comint.el b/aidermacs-backend-comint.el
index 2965fa612e..e548a3a32c 100644
--- a/aidermacs-backend-comint.el
+++ b/aidermacs-backend-comint.el
@@ -24,6 +24,11 @@
 
 (require 'comint)
 
+;; Forward declarations
+(declare-function aidermacs--prepare-for-code-edit "aidermacs")
+(declare-function aidermacs--cleanup-all-temp-files "aidermacs")
+(declare-function aidermacs--show-ediff-for-edited-files "aidermacs")
+(declare-function aidermacs--detect-edited-files "aidermacs")
 (declare-function aidermacs--process-message-if-multi-line "aidermacs" (str))
 
 (defcustom aidermacs-language-name-map '(("elisp" . "emacs-lisp")
@@ -65,7 +70,6 @@ This allows for multi-line input without sending the command."
   "Face for search/replace block content."
   :group 'aidermacs)
 
-
 (defvar aidermacs-font-lock-keywords
   '(("^\x2500+\n?" 0 '(face aidermacs-command-separator) t)
     ("^\x2500+" 0 '(face nil display (space :width 2)))
@@ -97,6 +101,24 @@ This allows for multi-line input without sending the 
command."
 This variable holds the actual marker text (e.g., <<<<<<< SEARCH, =======, 
etc.)
 that was matched at the start of the current syntax block.")
 
+(defvar-local aidermacs--comint-output-temp ""
+  "Temporary output variable storing the raw output string.")
+
+(defun aidermacs--comint-output-filter (output)
+  "Accumulate OUTPUT string until a prompt is detected, then store it."
+  (when (and (aidermacs--is-aidermacs-buffer-p) (not (string-empty-p output)))
+    (setq aidermacs--comint-output-temp
+          (concat aidermacs--comint-output-temp (substring-no-properties 
output)))
+    ;; Check if the output contains a prompt
+    (when (string-match-p "\n[^[:space:]]*>[[:space:]]$" 
aidermacs--comint-output-temp)
+      (aidermacs--store-output aidermacs--comint-output-temp)
+      ;; Check if any files were edited and show ediff if needed
+      (let ((edited-files (aidermacs--detect-edited-files)))
+        (if edited-files
+            (aidermacs--show-ediff-for-edited-files edited-files)
+          (aidermacs--cleanup-all-temp-files)))
+      (setq aidermacs--comint-output-temp ""))))
+
 (defun aidermacs-reset-font-lock-state ()
   "Reset font lock state to default for processing a new source block."
   (setq aidermacs--syntax-block-delimiter nil
@@ -239,7 +261,7 @@ _OUTPUT is the text to be processed."
        (cdr (cl-assoc-if (lambda (re) (string-match re file)) 
auto-mode-alist))))
    'fundamental-mode))
 
-(defun aidermacs-kill-buffer ()
+(defun aidermacs--comint-cleanup-hook ()
   "Clean up the fontify buffer."
   (when (bufferp aidermacs--syntax-work-buffer)
     (kill-buffer aidermacs--syntax-work-buffer)))
@@ -248,6 +270,12 @@ _OUTPUT is the text to be processed."
   "Reset font-lock state before executing a command.
 PROC is the process to send to.  STRING is the command to send."
   (aidermacs-reset-font-lock-state)
+  ;; Store the command for tracking in the correct buffer
+  (with-current-buffer (process-buffer proc)
+    (unless (member string '("" "y" "n" "d" "yes" "no"))
+      (setq aidermacs--last-command string)
+      ;; Always prepare for potential edits
+      (aidermacs--prepare-for-code-edit)))
   (comint-simple-send proc (aidermacs--process-message-if-multi-line string)))
 
 (defun aidermacs-run-comint (program args buffer-name)
@@ -264,8 +292,10 @@ BUFFER-NAME is the name for the aidermacs buffer."
         (setq-local comint-input-sender 'aidermacs-input-sender)
         (setq aidermacs--syntax-work-buffer
               (get-buffer-create (concat " *aidermacs-syntax" buffer-name)))
-        (add-hook 'kill-buffer-hook #'aidermacs-kill-buffer nil t)
+        (add-hook 'kill-buffer-hook #'aidermacs--comint-cleanup-hook nil t)
         (add-hook 'comint-output-filter-functions #'aidermacs-fontify-blocks 
100 t)
+        (add-hook 'comint-output-filter-functions 
#'aidermacs--comint-output-filter)
+        (advice-add 'comint-interrupt-subjob :around 
#'aidermacs--cleanup-temp-files-on-interrupt-comint)
         (let ((local-map (make-sparse-keymap)))
           (set-keymap-parent local-map comint-mode-map)
           (define-key local-map (kbd aidermacs-comint-multiline-newline-key) 
#'comint-accumulate)
@@ -306,6 +336,13 @@ The output is collected and passed to the current 
callback."
       (aidermacs--store-output (with-current-buffer output-buffer
                                  (buffer-string))))))
 
+(defun aidermacs--cleanup-temp-files-on-interrupt-comint (orig-fun &rest args)
+  "Run `aidermacs--cleanup-all-temp-files' after interrupting a comint subjob.
+ORIG-FUN is the original function being advised.  ARGS are its arguments."
+  (apply orig-fun args)
+  (when (aidermacs--is-aidermacs-buffer-p)
+    (aidermacs--cleanup-all-temp-files)))
+
 (provide 'aidermacs-backend-comint)
 
 ;;; aidermacs-backend-comint.el ends here
diff --git a/aidermacs-backend-vterm.el b/aidermacs-backend-vterm.el
index 70bfb832b6..38f4345fe7 100644
--- a/aidermacs-backend-vterm.el
+++ b/aidermacs-backend-vterm.el
@@ -38,6 +38,12 @@
 (declare-function vterm-send-return "vterm")
 (declare-function vterm-insert "vterm")
 
+(declare-function aidermacs--prepare-for-code-edit "aidermacs")
+(declare-function aidermacs--cleanup-all-temp-files "aidermacs")
+(declare-function aidermacs--show-ediff-for-edited-files "aidermacs")
+(declare-function aidermacs--detect-edited-files "aidermacs")
+(declare-function aidermacs--store-output "aidermacs")
+(declare-function aidermacs--is-aidermacs-buffer-p "aidermacs")
 
 (defvar-local aidermacs--vterm-active-timer nil
   "Store the active timer for vterm output processing.")
@@ -55,19 +61,11 @@
   :type 'string
   :group 'aidermacs)
 
-(defun aidermacs--is-aidermacs-vterm-buffer-p (&optional buffer)
-  "Check if BUFFER is an aidermacs vterm buffer.
-If BUFFER is nil, check the current buffer.
-Returns non-nil if the buffer name matches the aidermacs buffer pattern."
-  (let ((buf (or buffer (current-buffer))))
-    (and (derived-mode-p 'vterm-mode)
-         (string-match-p "^\\*aidermacs:" (buffer-name buf)))))
-
-(defun aidermacs--vterm-check-finish-sequence-repeated (proc orig-filter 
start-point expected)
+(defun aidermacs--vterm-check-finish-sequence-repeated (proc orig-filter 
start-point)
   "Check for the finish sequence in PROC's buffer.
 PROC is the process to check.  ORIG-FILTER is the original process filter.
-START-POINT is the starting position for output capture.  EXPECTED is the
-pattern to match.  If the finish sequence is detected, store the output via
+START-POINT is the starting position for output capture.
+If the finish sequence is detected, store the output via
 `aidermacs--store-output`, restore ORIG-FILTER, and return t."
   (when (buffer-live-p (process-buffer proc))
     (with-current-buffer (process-buffer proc)
@@ -82,7 +80,9 @@ pattern to match.  If the finish sequence is detected, store 
the output via
                              (error (point-max))))
              ;; Only check if we have a new prompt or haven't checked this 
position yet
              (last-check (or aidermacs--vterm-last-check-point start-point))
-             (should-check (> prompt-point last-check)))
+             (should-check (> prompt-point last-check))
+             ;; Simplified pattern that just looks for a shell prompt
+             (expected "^[^[:space:]]*>[[:space:]]"))
 
         ;; Update the last check point
         (setq aidermacs--vterm-last-check-point prompt-point)
@@ -100,8 +100,13 @@ pattern to match.  If the finish sequence is detected, 
store the output via
 
             ;; If we found a shell prompt
             (when (string-match-p expected prompt-line)
-              (let ((output (buffer-substring-no-properties start-point 
seq-start)))
-                (aidermacs--store-output (string-trim output)))
+              (let ((output (buffer-substring-no-properties start-point 
seq-start))
+                    (edited-files (aidermacs--detect-edited-files)))
+                (aidermacs--store-output (string-trim output))
+                ;; Check if any files were edited and show ediff if needed
+                (if edited-files
+                    (aidermacs--show-ediff-for-edited-files edited-files)
+                  (aidermacs--cleanup-all-temp-files)))
               (set-process-filter proc orig-filter))))))))
 
 (defun aidermacs--vterm-output-advice (orig-fun &rest args)
@@ -109,15 +114,18 @@ pattern to match.  If the finish sequence is detected, 
store the output via
 ORIG-FUN is the original function being advised.  ARGS are its arguments.
 This sets a temporary process filter that checks for the finish sequence
 after each output chunk, reducing the need for timers."
-  (if (aidermacs--is-aidermacs-vterm-buffer-p)
+  (if (aidermacs--is-aidermacs-buffer-p)
       (let* ((start-point (condition-case nil
                               (vterm--get-prompt-point)
                             (error (point-min))))
              (proc (get-buffer-process (current-buffer)))
-             ;; Simplified pattern that just looks for a shell prompt
-             (expected "^[^[:space:]]*>[[:space:]]")
              (orig-filter (process-filter proc)))
 
+        ;; Store the command for tracking in the correct buffer
+        (with-current-buffer (process-buffer proc)
+          (when (and args (car args) (stringp (car args)))
+            (setq-local aidermacs--last-command (car args))))
+
         ;; Initialize tracking variables
         (setq-local aidermacs--vterm-last-check-point nil)
 
@@ -137,7 +145,7 @@ after each output chunk, reducing the need for timers."
                     aidermacs-vterm-check-interval
                     (lambda ()
                       (when (aidermacs--vterm-check-finish-sequence-repeated
-                             proc orig-filter start-point expected)
+                             proc orig-filter start-point)
                         (when (timerp aidermacs--vterm-active-timer)
                           (cancel-timer aidermacs--vterm-active-timer)
                           (setq aidermacs--vterm-active-timer nil))
@@ -163,6 +171,8 @@ BUFFER-NAME is the name for the vterm buffer."
                     aidermacs--vterm-active-timer nil
                     aidermacs--vterm-last-check-point nil)
         (advice-add 'vterm-send-return :around 
#'aidermacs--vterm-output-advice)
+        (advice-add 'vterm-send-return :before 
#'aidermacs--vterm-capture-keyboard-input)
+        (advice-add 'vterm--self-insert :after 
#'aidermacs--cleanup-temp-files-on-interrupt-vterm)
         ;; Set up multi-line key binding
         (let ((map (make-sparse-keymap)))
           (set-keymap-parent map (current-local-map))
@@ -188,12 +198,40 @@ BUFFER is the target buffer to send to.  COMMAND is the 
text to send."
   (interactive)
   (vterm-insert "\n"))
 
+(defun aidermacs--vterm-capture-keyboard-input (orig-fun &rest args)
+  "Capture keyboard input in vterm.
+ORIG-FUN is the original function being advised. ARGS are its arguments."
+  (when (and (aidermacs--is-aidermacs-buffer-p)
+             (eq this-command 'vterm-send-return))
+    ;; Get the current line content which should be the command
+    ;; TODO: current line may not be enough
+    (save-excursion
+      (let* ((prompt-point (condition-case nil
+                               (vterm--get-prompt-point)
+                             (error (point-min))))
+             (command (buffer-substring-no-properties
+                       prompt-point
+                       (line-end-position))))
+        (when (not (string-empty-p command))
+          (setq-local aidermacs--last-command command)
+          ;; Always prepare for potential edits
+          (aidermacs--prepare-for-code-edit)))))
+  (apply orig-fun args))
+
 (defun aidermacs--vterm-cleanup ()
   "Clean up vterm resources when buffer is killed."
   (when aidermacs--vterm-active-timer
     (cancel-timer aidermacs--vterm-active-timer)
     (setq-local aidermacs--vterm-active-timer nil))
-  (setq-local aidermacs--vterm-last-check-point nil))
+  (setq-local aidermacs--vterm-last-check-point nil)
+  (advice-remove 'vterm-send-return #'aidermacs--vterm-capture-keyboard-input))
+
+(defun aidermacs--cleanup-temp-files-on-interrupt-vterm (&rest args)
+  "Run `aidermacs--cleanup-all-temp-files' after interrupting a vterm subjob.
+ARGS are the arguments."
+  (when (and (aidermacs--is-aidermacs-buffer-p)
+             (equal (this-command-keys) "\C-c\C-c"))
+    (aidermacs--cleanup-all-temp-files)))
 
 (provide 'aidermacs-backend-vterm)
 
diff --git a/aidermacs-backends.el b/aidermacs-backends.el
index 0e7fc3bf87..fed3f98883 100644
--- a/aidermacs-backends.el
+++ b/aidermacs-backends.el
@@ -26,10 +26,10 @@
 (when (commandp 'vterm)
   (require 'aidermacs-backend-vterm))
 
-(declare-function aidermacs-run-vterm "aidermacs-backend-vterm"
-                  (program args buffer-name))
-(declare-function aidermacs--send-command-vterm "aidermacs-backend-vterm"
-                  (buffer command))
+(declare-function aidermacs-run-vterm "aidermacs-backend-vterm" (program args 
buffer-name))
+(declare-function aidermacs--send-command-vterm "aidermacs-backend-vterm" 
(buffer command))
+(declare-function aidermacs-project-root "aidermacs" ())
+(declare-function aidermacs--get-files-in-session "aidermacs" (callback))
 
 (defgroup aidermacs-backends nil
   "Backend customization for aidermacs."
@@ -37,7 +37,7 @@
 
 (defcustom aidermacs-backend 'comint
   "Backend to use for the aidermacs process.
-Options are `'comint' (the default) or `'vterm'.  When set to `'vterm',
+Options are `comint' (the default) or `vterm'.  When set to `vterm',
 aidermacs launches a fully functional vterm buffer instead
 of using a comint process."
   :type '(choice (const :tag "Comint" comint)
@@ -54,14 +54,14 @@ of using a comint process."
   :type 'integer
   :group 'aidermacs-output)
 
-(defvar aidermacs--output-history nil
+(defvar-local aidermacs--output-history nil
   "List to store aidermacs output history.
 Each entry is a cons cell (timestamp . output-text).")
 
-(defvar aidermacs--last-command nil
+(defvar-local aidermacs--last-command nil
   "Store the last command sent to aidermacs.")
 
-(defvar aidermacs--current-output ""
+(defvar-local aidermacs--current-output ""
   "Accumulator for current output being captured as a string.")
 
 (defun aidermacs-get-output-history (&optional limit)
@@ -78,12 +78,92 @@ Returns a list of (timestamp . output-text) pairs, most 
recent first."
   (interactive)
   (setq aidermacs--output-history nil))
 
-(defvar aidermacs--current-callback nil
+(defvar-local aidermacs--current-callback nil
   "Store the callback function for the current command.")
 
-(defvar aidermacs--in-callback nil
+(defvar-local aidermacs--in-callback nil
   "Flag to prevent recursive callbacks.")
 
+(defvar-local aidermacs--tracked-files nil
+  "List of files that have been mentioned in the aidermacs output.
+This is used to avoid having to run /ls repeatedly.")
+
+(defun aidermacs--verify-tracked-files ()
+  "Verify files in `aidermacs--tracked-files` exist.
+Remove any files that don't exist."
+  (let ((project-root (aidermacs-project-root))
+        (valid-files nil))
+    (dolist (file aidermacs--tracked-files)
+      (let* ((is-readonly (string-match-p " (read-only)$" file))
+             (actual-file (if is-readonly
+                              (substring file 0 (- (length file) 12))
+                            file))
+             (full-path (expand-file-name actual-file project-root)))
+        (when (file-exists-p full-path)
+          (push file valid-files))))
+    (setq aidermacs--tracked-files valid-files)))
+
+(defun aidermacs--parse-output-for-files (output)
+  "Parse OUTPUT for files and add them to `aidermacs--tracked-files'."
+  (when output
+    (let ((lines (split-string output "\n"))
+          (last-line ""))
+      (dolist (line lines)
+        (cond
+         ;; Applied edit to <filename>
+         ((string-match "Applied edit to \\(\\./\\)?\\(.+\\)" line)
+          (when-let ((file (match-string 2 line)))
+            (add-to-list 'aidermacs--tracked-files file)))
+
+         ;; Added <filename> to the chat.
+         ((string-match "Added \\(\\./\\)?\\(.+\\) to the chat" line)
+          (when-let ((file (match-string 2 line)))
+            (add-to-list 'aidermacs--tracked-files file)))
+
+         ;; Removed <filename> from the chat (with or without ./ prefix)
+         ((string-match "Removed \\(\\./\\)?\\(.+\\) from the chat" line)
+          (when-let ((file (match-string 2 line)))
+            (setq aidermacs--tracked-files (delete file 
aidermacs--tracked-files))))
+
+         ;; Added <filename> to read-only files.
+         ((string-match "Added \\(\\./\\)?\\(.+\\) to read-only files" line)
+          (when-let ((file (match-string 2 line)))
+            (add-to-list 'aidermacs--tracked-files (concat file " 
(read-only)"))))
+
+         ;; Moved <file> from editable to read-only files in the chat
+         ((string-match "Moved \\(\\./\\)?\\(.+\\) from editable to read-only 
files in the chat" line)
+          (when-let ((file (match-string 2 line)))
+            (let ((editable-file (replace-regexp-in-string " (read-only)$" "" 
file)))
+              (setq aidermacs--tracked-files (delete editable-file 
aidermacs--tracked-files))
+              (add-to-list 'aidermacs--tracked-files (concat file " 
(read-only)")))))
+
+         ;; Moved <file> from read-only to editable files in the chat
+         ((string-match "Moved \\(\\./\\)?\\(.+\\) from read-only to editable 
files in the chat" line)
+          (when-let ((file (match-string 2 line)))
+            (let ((read-only-file (concat file " (read-only)")))
+              (setq aidermacs--tracked-files (delete read-only-file 
aidermacs--tracked-files))
+              (add-to-list 'aidermacs--tracked-files file))))
+
+         ;; <file>\nAdd file to the chat?
+         ((string-match "Add file to the chat?" line)
+          (add-to-list 'aidermacs--tracked-files last-line))
+
+         ;; <file> is already in the chat as an editable file
+         ((string-match "\\(\\./\\)?\\(.+\\) is already in the chat as an 
editable file" line)
+          (when-let ((file (match-string 2 line)))
+            (add-to-list 'aidermacs--tracked-files file))))
+        (setq last-line line))
+
+      ;; Verify all tracked files exist
+      (aidermacs--verify-tracked-files))))
+
+(defun aidermacs-reset-tracked-files ()
+  "Reset the list of tracked files and force a refresh."
+  (interactive)
+  (setq aidermacs--tracked-files nil)
+  (aidermacs--get-files-in-session (lambda (files)
+                                     (message "Refreshed file list: %s" 
files))))
+
 (defun aidermacs--store-output (output)
   "Store output string in the history with timestamp.
 OUTPUT is the string to store.
@@ -93,10 +173,12 @@ If there's a callback function, call it with the output."
   (when (> (length aidermacs--output-history) aidermacs-output-limit)
     (setq aidermacs--output-history
           (seq-take aidermacs--output-history aidermacs-output-limit)))
+  ;; Parse output for file mentions
+  (aidermacs--parse-output-for-files output)
   (unless aidermacs--in-callback
     (when aidermacs--current-callback
       (let ((aidermacs--in-callback t))
-        (funcall aidermacs--current-callback output)
+        (funcall aidermacs--current-callback)
         (setq aidermacs--current-callback nil)))))
 
 ;; Backend dispatcher functions
@@ -111,25 +193,30 @@ BUFFER-NAME is the name for the aidermacs buffer."
    (t
     (aidermacs-run-comint program args buffer-name))))
 
-(defun aidermacs--send-command-backend (buffer command)
-  "Send command to buffer using the appropriate backend.
-BUFFER is the target buffer.  COMMAND is the text to send."
-  (setq aidermacs--last-command command
-        aidermacs--current-output nil)
-  (if (eq aidermacs-backend 'vterm)
-      (aidermacs--send-command-vterm buffer command)
-    (aidermacs--send-command-comint buffer command)))
-
-(defun aidermacs--send-command-redirect-backend (buffer command &optional 
callback)
+(defun aidermacs--is-aidermacs-buffer-p (&optional buffer)
+  "Check if BUFFER is any type of aidermacs buffer.
+If BUFFER is nil, check the current buffer.
+Returns non-nil if the buffer name matches the aidermacs buffer pattern
+and is using either comint or vterm mode."
+  (let ((buf (or buffer (current-buffer))))
+    (with-current-buffer buf
+      (and (string-match-p "^\\*aidermacs:" (buffer-name buf))
+           (or (derived-mode-p 'comint-mode)
+               (and (fboundp 'vterm-mode)
+                    (derived-mode-p 'vterm-mode)))))))
+
+(defun aidermacs--send-command-backend (buffer command &optional redirect 
callback)
   "Send command to buffer using the appropriate backend.
 BUFFER is the target buffer.  COMMAND is the text to send.
-CALLBACK if provided will be called with the command output when available."
+If REDIRECT is non-nil it redirects the output (hidden) for comint backend.
+If CALLBACK is non-nil it will be called after the command finishes."
   (setq aidermacs--last-command command
-        aidermacs--current-output nil
         aidermacs--current-callback callback)
   (if (eq aidermacs-backend 'vterm)
       (aidermacs--send-command-vterm buffer command)
-    (aidermacs--send-command-redirect-comint buffer command)))
+    (if redirect
+        (aidermacs--send-command-redirect-comint buffer command)
+      (aidermacs--send-command-comint buffer command))))
 
 (provide 'aidermacs-backends)
 
diff --git a/aidermacs-models.el b/aidermacs-models.el
index 0078a57a43..0f981d4cba 100644
--- a/aidermacs-models.el
+++ b/aidermacs-models.el
@@ -26,8 +26,7 @@
 (require 'json)
 (require 'url)
 
-(declare-function aidermacs--send-command "aidermacs" (command &optional 
switch-to-buffer))
-(declare-function aidermacs--send-command-redirect "aidermacs" (command 
callback))
+(declare-function aidermacs--send-command "aidermacs" (command &optional 
no-switch-to-buffer use-existing redirect callback))
 (declare-function aidermacs-buffer-name "aidermacs" ())
 (declare-function aidermacs-exit "aidermacs" ())
 
@@ -126,20 +125,20 @@ This is a private function used internally."
   (condition-case nil
       (let ((model (completing-read "Select AI model: " 
aidermacs--cached-models nil t)))
         (when model
-          (aidermacs--send-command (format "/model %s" model) t)))
+          (aidermacs--send-command (format "/model %s" model))))
     (quit (message "Model selection cancelled"))))
 
 (defun aidermacs--get-available-models ()
   "Get list of models supported by aider using the /models command.
 This fetches models from various API providers and caches them."
-  (aidermacs--send-command-redirect
-   "/models /"
-   (lambda (output)
+  (aidermacs--send-command
+   "/models /" nil nil t
+   (lambda ()
      (let* ((supported-models
              (seq-filter
               (lambda (line)
                 (string-prefix-p "- " line))
-              (split-string output "\n" t)))
+              (split-string aidermacs--current-output "\n" t)))
             (models nil))
        (setq supported-models
              (mapcar (lambda (line)
@@ -176,8 +175,8 @@ This is useful when available models have changed."
   (interactive)
   (when (and aidermacs--cached-models
              (equal aidermacs--cached-models aidermacs-popular-models)
-             (fboundp 'aidermacs-buffer-name)
-             (get-buffer (aidermacs-buffer-name)))
+             (fboundp 'aidermacs-get-buffer-name)
+             (get-buffer (aidermacs-get-buffer-name)))
     (setq aidermacs--cached-models nil))
 
   (if aidermacs--cached-models
diff --git a/aidermacs.el b/aidermacs.el
index e5d39dee78..55dbb6991a 100644
--- a/aidermacs.el
+++ b/aidermacs.el
@@ -28,6 +28,7 @@
 
 (require 'comint)
 (require 'dired)
+(require 'ediff)
 (require 'project)
 (require 'transient)
 (require 'vc-git)
@@ -38,6 +39,10 @@
 (require 'aidermacs-backends)
 (require 'aidermacs-models)
 
+(defvar-local aidermacs--current-mode nil
+  "Buffer-local variable to track the current aidermacs mode.
+Possible values: `code', `ask', `architect', `help'.")
+
 (declare-function magit-show-commit "magit-diff" (rev &optional noselect 
module))
 
 (defgroup aidermacs nil
@@ -94,9 +99,16 @@ This is the file name without path."
   :type 'string
   :group 'aidermacs)
 
-(defvar aidermacs-read-string-history nil
+(defvar-local aidermacs-read-string-history nil
   "History list for aidermacs read string inputs.")
 
+(defvar-local aidermacs--pre-edit-files nil
+  "Alist of (filename . temp-filename) pairs storing file state before Aider 
edits.")
+
+(defvar-local aidermacs--pre-edit-buffers nil
+  "Alist of (filename . temp-buffer) pairs for active ediff sessions.")
+
+
 ;;;###autoload
 (defun aidermacs-plain-read-string (prompt &optional initial-input)
   "Read a string from the user with PROMPT and optional INITIAL-INPUT.
@@ -274,6 +286,9 @@ This function sets up the appropriate arguments and 
launches the process."
     (if (get-buffer buffer-name)
         (aidermacs-switch-to-buffer buffer-name)
       (aidermacs-run-backend aidermacs-program final-args buffer-name)
+      (with-current-buffer (get-buffer buffer-name)
+        ;; Set initial mode based on startup configuration
+        (setq-local aidermacs--current-mode (if aidermacs-use-architect-mode 
'architect 'code)))
       (aidermacs-switch-to-buffer buffer-name))))
 
 ;;;###autoload
@@ -285,31 +300,186 @@ This is useful for working in monorepos where you want 
to limit aider's scope."
         (default-directory (file-truename default-directory)))
     (aidermacs-run)))
 
-(defun aidermacs--send-command (command &optional switch-to-buffer 
use-existing)
+(defun aidermacs--capture-file-state (filename)
+  "Store the current state of FILENAME in a temporary file."
+  (when (and filename (file-exists-p filename))
+    (let ((temp-file (make-temp-file
+                      (concat "aidermacs-"
+                              (file-name-nondirectory filename) "-"))))
+      (condition-case err
+          (progn
+            (copy-file filename temp-file t)
+            (cons filename temp-file))
+        (error
+         (message "Error capturing file state for %s: %s"
+                  filename (error-message-string err))
+         (when (file-exists-p temp-file)
+           (delete-file temp-file))
+         nil)))))
+
+(defun aidermacs--cleanup-all-temp-files ()
+  "Clean up all temporary files created for ediff sessions.
+This is called when all ediff sessions are complete."
+  (interactive)
+  (with-current-buffer (get-buffer (aidermacs-get-buffer-name))
+    (dolist (file-pair aidermacs--pre-edit-files)
+      (let ((temp-file (cdr file-pair)))
+        (when (and temp-file (stringp temp-file) (file-exists-p temp-file))
+          (message "Deleting %s" temp-file)
+          (delete-file temp-file))))
+    ;; Clear the list after cleanup
+    (setq aidermacs--pre-edit-files nil)
+    (setq aidermacs--pre-edit-buffers nil)))
+
+(defun aidermacs--prepare-for-code-edit ()
+  "Prepare for Aider code edits by capturing current file states."
+  (let ((files aidermacs--tracked-files))
+    (when files
+      (setq aidermacs--pre-edit-files
+            (mapcar (lambda (file)
+                      (let* ((clean-file (replace-regexp-in-string " 
(read-only)$" "" file))
+                             (full-path (expand-file-name clean-file 
(aidermacs-project-root))))
+                        ;; Check if a pre-edit file already exists for this 
file
+                        (unless (assoc full-path aidermacs--pre-edit-files)
+                          (aidermacs--capture-file-state full-path))))
+                    files))
+      ;; Remove nil entries from the list (where capture failed or was skipped)
+      (setq aidermacs--pre-edit-files (delq nil aidermacs--pre-edit-files))
+      (message "Updated Temp Files: %s" aidermacs--pre-edit-files))))
+
+(defun aidermacs--ediff-quit-handler ()
+  "Handle ediff session cleanup and process next files in queue.
+This function is called when an ediff session is quit and performs two tasks:
+1. Cleans up resources (buffers and temp files) for the current ediff session
+2. Processes the next file in the ediff queue if any remain"
+  ;; Clean up any pre-edit buffers
+  (dolist (buf (buffer-list))
+    (when (string-match "\\*aidermacs-pre-edit:\\(.*\\)\\*" (buffer-name buf))
+      (when (buffer-live-p buf)
+        (kill-buffer buf))))
+  (aidermacs--process-next-ediff-file))
+
+(defun aidermacs--setup-ediff-cleanup-hooks ()
+  "Set up hooks to ensure proper cleanup of temporary buffers after ediff."
+  (add-hook 'ediff-quit-hook #'aidermacs--ediff-quit-handler))
+
+(defun aidermacs--detect-edited-files ()
+  "Parse current output to find files edited by Aider."
+  (let ((edited-files nil)
+        (output aidermacs--current-output))
+    (with-temp-buffer
+      (insert output)
+      (goto-char (point-min))
+      (while (re-search-forward "Applied edit to \\(.+\\)" nil t)
+        (push (match-string 1) edited-files)))
+    (nreverse edited-files)))
+
+(defun aidermacs--create-pre-edit-buffer (filename temp-file)
+  "Create a buffer for FILENAME using content from TEMP-FILE for ediff."
+  (condition-case err
+      (let ((buffer (generate-new-buffer (format "*aidermacs-pre-edit:%s*"
+                                                (file-name-nondirectory 
filename)))))
+        (with-current-buffer buffer
+          (condition-case err2
+              (progn
+                (insert-file-contents temp-file)
+                (set-buffer-modified-p nil)
+                ;; Use same major mode as the original file would have
+                (let ((buffer-file-name filename))
+                  (set-auto-mode))
+                ;; Ensure syntax highlighting is applied
+                (font-lock-ensure)
+                ;; Make sure buffer is read-only
+                (setq buffer-read-only t))
+            (error
+             (message "Error setting up pre-edit buffer: %s" 
(error-message-string err2))
+             (kill-buffer buffer)
+             nil)))
+        buffer)
+    (error
+     (message "Failed to create pre-edit buffer: %s" (error-message-string 
err))
+     nil)))
+
+(defvar-local aidermacs--ediff-queue nil
+  "Buffer-local queue of files waiting to be processed by ediff.")
+
+(defun aidermacs--process-next-ediff-file ()
+  "Process the next file in the ediff queue for the current buffer."
+  (with-current-buffer (get-buffer (aidermacs-get-buffer-name))
+    (if aidermacs--ediff-queue
+        (let ((file (pop aidermacs--ediff-queue)))
+          (aidermacs--show-ediff-for-file file))
+      (aidermacs--cleanup-all-temp-files))))
+
+(defun aidermacs--show-ediff-for-file (file)
+  "Show ediff for FILE."
+  (let* ((full-path (expand-file-name file (aidermacs-project-root)))
+         (pre-edit-pair (assoc full-path aidermacs--pre-edit-files))
+         (temp-file (and pre-edit-pair (cdr pre-edit-pair))))
+    (if (and temp-file
+             (stringp temp-file)
+             (file-exists-p temp-file))
+        (progn
+          ;; Create buffer from temp file only when needed
+          (let* ((pre-edit-buffer (aidermacs--create-pre-edit-buffer full-path 
temp-file))
+                 (current-buffer (or (get-file-buffer full-path)
+                                     (find-file-noselect full-path))))
+            (with-current-buffer current-buffer
+              (revert-buffer t t t))
+            ;; Store buffer for cleanup
+            (unless (boundp 'aidermacs--pre-edit-buffers)
+              (setq-local aidermacs--pre-edit-buffers nil))
+            (push (cons full-path pre-edit-buffer) aidermacs--pre-edit-buffers)
+            ;; Debug info
+            (message "Comparing %s with %s" temp-file full-path)
+            ;; Give Emacs a moment to finish buffer setup
+            ;; Start ediff session
+            (ediff-buffers pre-edit-buffer current-buffer)))
+      ;; If no pre-edit temp file found, continue with next file
+      (message "No pre-edit file found for %s in %s out of %s, skipping" file 
pre-edit-pair aidermacs--pre-edit-files)
+      (aidermacs--process-next-ediff-file))))
+
+(defun aidermacs--show-ediff-for-edited-files (edited-files)
+  "Show ediff for each file in EDITED-FILES."
+  (when edited-files
+    ;; Display a message about which files were changed
+    (message "Modified %d file(s): %s"
+             (length edited-files)
+             (mapconcat #'identity edited-files ", "))
+
+    ;; Set up the queue in the current buffer
+    (setq-local aidermacs--ediff-queue edited-files)
+
+    ;; Process the first file
+    (aidermacs--process-next-ediff-file)))
+
+;; Function removed as its functionality is now integrated directly in the 
output filters
+
+
+(defun aidermacs--send-command (command &optional no-switch-to-buffer 
use-existing redirect callback)
   "Send command to the corresponding aidermacs process.
 COMMAND is the text to send.
-If SWITCH-TO-BUFFER is non-nil, switch to the aidermacs buffer.
-If USE-EXISTING is non-nil, use an existing buffer instead of creating new."
+If NO-SWITCH-TO-BUFFER is non-nil, don't switch to the aidermacs buffer.
+If USE-EXISTING is non-nil, use an existing buffer instead of creating new.
+If REDIRECT is non-nil it redirects the output (hidden) for comint backend.
+If CALLBACK is non-nil it will be called after the command finishes."
   (let* ((buffer-name (aidermacs-get-buffer-name use-existing))
          (buffer (or (get-buffer buffer-name)
                      (progn (aidermacs-run)
                             (get-buffer buffer-name))))
          (processed-command (aidermacs--process-message-if-multi-line 
command)))
-    (aidermacs--send-command-backend buffer processed-command)
-    (when (and switch-to-buffer (not (string= (buffer-name) buffer-name)))
-      (aidermacs-switch-to-buffer buffer-name))))
 
-(defun aidermacs--send-command-redirect (command callback &optional 
use-existing)
-  "Send command to the corresponding aidermacs process in the background.
-COMMAND is the text to send.
-CALLBACK will be called with the command output when available.
-If USE-EXISTING is non-nil, use an existing buffer instead of creating new."
-  (let* ((buffer-name (aidermacs-get-buffer-name use-existing))
-         (buffer (or (get-buffer buffer-name)
-                     (progn (aidermacs-run)
-                            (get-buffer buffer-name))))
-         (processed-command (aidermacs--process-message-if-multi-line 
command)))
-    (aidermacs--send-command-redirect-backend buffer processed-command 
callback)))
+    ;; Reset current output before sending new command
+    (with-current-buffer buffer
+      (setq-local aidermacs--current-output nil)
+      (setq-local aidermacs--last-command processed-command)
+      ;; Always prepare for potential edits
+      (aidermacs--cleanup-all-temp-files)
+      (aidermacs--prepare-for-code-edit))
+
+    (aidermacs--send-command-backend buffer processed-command redirect 
callback)
+    (when (and (not no-switch-to-buffer) (not (string= (buffer-name) 
buffer-name)))
+      (aidermacs-switch-to-buffer buffer-name))))
 
 ;;;###autoload
 (defun aidermacs-switch-to-buffer (&optional buffer-name)
@@ -333,19 +503,21 @@ If the current buffer is already the aidermacs buffer, do 
nothing."
 (defun aidermacs-clear-chat-history ()
   "Send the command \"/clear\" to the aidermacs buffer."
   (interactive)
+  (setq aidermacs--tracked-files nil)
   (aidermacs--send-command "/clear"))
 
 ;;;###autoload
 (defun aidermacs-reset ()
   "Send the command \"/reset\" to the aidermacs buffer."
   (interactive)
+  (setq aidermacs--tracked-files nil)
   (aidermacs--send-command "/reset"))
 
 ;;;###autoload
 (defun aidermacs-exit ()
   "Send the command \"/exit\" to the aidermacs buffer."
   (interactive)
-  (aidermacs--send-command "/exit"))
+  (aidermacs--send-command "/exit" t))
 
 
 (defun aidermacs--process-message-if-multi-line (str)
@@ -388,14 +560,14 @@ The full command will be \"COMMAND-PREFIX <current buffer 
file full path>\"."
   (interactive)
   (let ((command (aidermacs-read-string "Enter general aider command: ")))
     ;; Use the shared helper function to send the command
-    (aidermacs--send-command command t)))
+    (aidermacs--send-command command)))
 
 ;;;###autoload
 (defun aidermacs-direct-change ()
   "Prompt the user for an input and send it to aidemracs prefixed with \"/code 
\"."
   (interactive)
   (when-let ((command (aidermacs--form-prompt "/code" nil t "empty to change 
to code mode")))
-    (aidermacs--send-command command t)))
+    (aidermacs--send-command command)))
 
 (defun aidermacs--parse-ls-output (output)
   "Parse the /ls command output to extract files in chat.
@@ -435,32 +607,37 @@ Returns a deduplicated list of such file names."
             (forward-line 1)))
 
         ;; Remove duplicates and return
-        (delete-dups (nreverse files))))))
+        (setq aidermacs--tracked-files (delete-dups (nreverse files)))
+        aidermacs--tracked-files))))
+
+(defun aidermacs--get-files-in-session (callback)
+  "Get list of files in current session and call CALLBACK with the result."
+  (aidermacs--send-command
+   "/ls" nil nil t
+   (lambda ()
+     (let ((files (aidermacs--parse-ls-output aidermacs--current-output)))
+       (funcall callback files)))))
 
 ;;;###autoload
 (defun aidermacs-list-added-files ()
   "List all files currently added to the chat session.
 Sends the \"/ls\" command and returns the list of files via callback."
   (interactive)
-  (aidermacs--send-command-redirect
-   "/ls"
-   (lambda (output)
-     (let ((files (aidermacs--parse-ls-output output)))
-       (message "%s" (prin1-to-string files))
-       files))))
+  (aidermacs--get-files-in-session
+   (lambda (files)
+     (message "%s" (prin1-to-string files))
+     files)))
 
 ;;;###autoload
 (defun aidermacs-drop-file ()
   "Drop a file from the chat session by selecting from currently added files."
   (interactive)
-  (aidermacs--send-command-redirect
-   "/ls"
-   (lambda (output)
-     (if-let* ((files (aidermacs--parse-ls-output output))
-               (file (completing-read "Select file to drop: " files nil t))
+  (aidermacs--get-files-in-session
+   (lambda (files)
+     (if-let* ((file (completing-read "Select file to drop: " files nil t))
                (clean-file (replace-regexp-in-string " (read-only)$" "" file)))
-         (aidermacs--send-command (format "/drop ./%s" clean-file)))
-     (message "No files available to drop"))))
+         (aidermacs--send-command (format "/drop ./%s" clean-file))
+       (message "No files available to drop")))))
 
 
 ;;;###autoload
@@ -512,41 +689,41 @@ If called from the aidermacs buffer, use general question 
instead."
       (call-interactively #'aidermacs-general-question)
     (when-let ((command (aidermacs--form-prompt "/ask" "Question")))
       (aidermacs-add-current-file)
-      (aidermacs--send-command command t))))
+      (aidermacs--send-command command))))
 
 ;;;###autoload
 (defun aidermacs-general-question ()
   "Prompt the user for a general question without code context."
   (interactive)
   (when-let ((command (aidermacs--form-prompt "/ask" nil t "empty to change to 
ask mode")))
-    (aidermacs--send-command command t)))
+    (aidermacs--send-command command)))
 
 ;;;###autoload
 (defun aidermacs-help ()
   "Prompt the user for an input prefixed with \"/help \"."
   (interactive)
   (when-let ((command (aidermacs--form-prompt "/help" nil t "empty for general 
/help")))
-    (aidermacs--send-command command t)))
+    (aidermacs--send-command command)))
 
 ;;;###autoload
 (defun aidermacs-general-architect ()
   "Prompt the user for an input prefixed with \"/architect \"."
   (interactive)
   (when-let ((command (aidermacs--form-prompt "/architect" nil t "empty to 
change to architect mode")))
-    (aidermacs--send-command command t)))
+    (aidermacs--send-command command)))
 
 ;;;###autoload
 (defun aidermacs-debug-exception ()
   "Prompt the user for an input and send it to aidemracs prefixed with 
\"/debug \"."
   (interactive)
   (when-let ((command (aidermacs--form-prompt "/ask" "Debug exception")))
-    (aidermacs--send-command command t)))
+    (aidermacs--send-command command)))
 
 ;;;###autoload
 (defun aidermacs-accept-change ()
   "Send the command \"go ahead\" to the aidemracs."
   (interactive)
-  (aidermacs--send-command "/code go ahead" t))
+  (aidermacs--send-command "/code go ahead"))
 
 
 ;;;###autoload
@@ -594,7 +771,7 @@ If point is in a function, inspect that function."
       (call-interactively #'aidermacs-general-architect)
     (when-let ((command (aidermacs--form-prompt "/architect" "Architect")))
       (aidermacs-add-current-file)
-      (aidermacs--send-command command t))))
+      (aidermacs--send-command command))))
 
 ;;;###autoload
 (defun aidermacs-question-this-symbol ()
@@ -609,14 +786,14 @@ If point is in a function, inspect that function."
     (if symbol
         (progn
           (aidermacs-add-current-file)
-          (aidermacs--send-command prompt t))
+          (aidermacs--send-command prompt))
       (error "No symbol under point!"))))
 
 (defun aidermacs-send-command-with-prefix (prefix command)
   "Send COMMAND to the aidermacs buffer with PREFIX.
 PREFIX is the text to prepend.  COMMAND is the text to send."
   (aidermacs-add-current-file)
-  (aidermacs--send-command (concat prefix command) t))
+  (aidermacs--send-command (concat prefix command)))
 
 (defun aidermacs--add-files-helper (files read-only &optional message)
   "Helper function to add files with read-only flag.
@@ -626,8 +803,9 @@ as read-only.  Optional MESSAGE can override the default 
success message."
          (files (delq nil files)))
     (if files
         (progn
-          (aidermacs--send-command (format "%s %s" cmd
-                                           (mapconcat #'identity files " ")) t)
+          (aidermacs--send-command
+           (format "%s %s" cmd
+                   (mapconcat #'identity files " ")))
           (message (or message
                        (format "Added %d files as %s"
                                (length files)
@@ -741,7 +919,7 @@ Otherwise:
                                 function-name))
                        (command (aidermacs--form-prompt "/architect" 
initial-input)))
                   (aidermacs-add-current-file)
-                  (aidermacs--send-command command t))
+                  (aidermacs--send-command command))
               (message "Current function '%s' does not appear to be a test 
function." function-name))
           (message "Please place cursor inside a test function to 
implement.")))
        ;; Non-test file case
@@ -755,7 +933,7 @@ Otherwise:
                           (file-name-nondirectory buffer-file-name) 
common-instructions)))
                (command (aidermacs--form-prompt "/architect" initial-input)))
           (aidermacs-add-current-file)
-          (aidermacs--send-command command t)))))))
+          (aidermacs--send-command command)))))))
 
 ;;;###autoload
 (defun aidermacs-fix-failing-test-under-cursor ()
@@ -767,13 +945,14 @@ This function assumes the cursor is on or inside a test 
function."
                                     test-function-name))
              (command (aidermacs--form-prompt "/architect" initial-input)))
         (aidermacs-add-current-file)
-        (aidermacs--send-command command t))
+        (aidermacs--send-command command))
     (message "No test function found at cursor position.")))
 
 (defun aidermacs-create-session-scratchpad ()
   "Create a new temporary file for adding content to the aider session.
-The file will be created in the system's temp directory with a timestamped 
name.
-Use this to add functions, code snippets, or other content to the session."
+The file will be created in the system's temp directory
+with a timestamped name.  Use this to add functions, code
+snippets, or other content to the session."
   (interactive)
   (let* ((temp-dir (file-name-as-directory (temporary-file-directory)))
          (filename (expand-file-name
@@ -785,14 +964,15 @@ Use this to add functions, code snippets, or other 
content to the session."
       (insert ";; Add your code snippets, functions, or other content here\n")
       (insert ";; Just edit and save - changes will be available to aider\n\n")
       (write-file filename))
-    (aidermacs--send-command (format "/read %s" filename) t)
+    (aidermacs--send-command (format "/read %s" filename))
     (find-file-other-window filename)
     (message "Created and added scratchpad to session: %s" filename)))
 
 ;;;###autoload
 (defun aidermacs-add-file-to-session ()
   "Interactively add a file to an existing aidermacs session using /read.
-This allows you to add the file's content to a specific session."
+This allows you to add the file's content to a
+specific session."
   (interactive)
   (let* ((initial (when buffer-file-name
                     (file-name-nondirectory buffer-file-name)))
@@ -801,12 +981,12 @@ This allows you to add the file's content to a specific 
session."
                                 nil nil t initial))))
     (if (not (file-exists-p file))
         (message "File does not exist: %s" file)
-      (aidermacs--send-command (format "/read %s" file) t t))))
+      (aidermacs--send-command (format "/read %s" file) nil t))))
 
 (defun aidermacs--is-comment-line (line)
   "Check if LINE is a comment line based on current buffer's comment syntax.
-Returns non-nil if LINE starts with one or more comment characters,
-ignoring leading whitespace."
+Returns non-nil if LINE starts with one or more
+comment characters, ignoring leading whitespace."
   (when comment-start
     (let ((comment-str (string-trim-right comment-start)))
       (string-match-p (concat "^[ \t]*"
@@ -833,7 +1013,7 @@ Otherwise implement TODOs for the entire current file."
                                      (format " on this comment: `%s`." 
current-line))
                                    " Keep existing code structure"))))
         (aidermacs-add-current-file)
-        (aidermacs--send-command command t)))))
+        (aidermacs--send-command command)))))
 
 ;;;###autoload
 (defun aidermacs-send-line-or-region ()
@@ -842,44 +1022,50 @@ If region is active, send the selected region.
 Otherwise, send the line under cursor."
   (interactive)
   (let ((text (if (use-region-p)
-                  (buffer-substring-no-properties (region-beginning) 
(region-end))
+                  (buffer-substring-no-properties
+                   (region-beginning) (region-end))
                 (string-trim (thing-at-point 'line t)))))
     (when text
-      (aidermacs--send-command text t))))
+      (aidermacs--send-command text))))
 
 ;;;###autoload
 (defun aidermacs-send-region-by-line ()
   "Send the text of the current selected region, split into lines."
   (interactive)
   (if (use-region-p)
-      (let* ((text (buffer-substring-no-properties (region-beginning) 
(region-end)))
+      (let* ((text (buffer-substring-no-properties
+                    (region-beginning) (region-end)))
              (lines (split-string text "\n" t)))
         (mapc (lambda (line)
                 (let ((trimmed (string-trim line)))
                   (when (not (string-empty-p trimmed))
-                    (aidermacs--send-command trimmed t))))
+                    (aidermacs--send-command trimmed))))
               lines))
     (message "No region selected.")))
 
 ;;;###autoload
 (defun aidermacs-send-block-or-region ()
   "Send the current active region text or current paragraph content.
-When sending paragraph content, preserve cursor position."
+When sending paragraph content, preserve cursor
+position."
   (interactive)
   (let ((text (if (use-region-p)
-                  (buffer-substring-no-properties (region-beginning) 
(region-end))
+                  (buffer-substring-no-properties
+                   (region-beginning) (region-end))
                 (save-excursion
                   (mark-paragraph)
                   (prog1
-                      (buffer-substring-no-properties (region-beginning) 
(region-end))
+                      (buffer-substring-no-properties
+                       (region-beginning) (region-end))
                     (deactivate-mark))))))
     (when text
-      (aidermacs--send-command text t))))
+      (aidermacs--send-command text))))
 
 ;;;###autoload
 (defun aidermacs-open-prompt-file ()
   "Open aidermacs prompt file under git repo root.
-If file doesn't exist, create it with command binding help and sample prompt."
+If file doesn't exist, create it with command binding help and
+sample prompt."
   (interactive)
   (let* ((git-root (vc-git-root default-directory))
          (prompt-file (when git-root
@@ -934,9 +1120,8 @@ These are exact filename matches (including the dot 
prefix)."
 (defun aidermacs--maybe-enable-minor-mode ()
   "Determines whether to enable `aidermacs-minor-mode'."
   (when (and buffer-file-name
-             (when buffer-file-name
-               (let ((base-name (file-name-nondirectory buffer-file-name)))
-                 (member base-name aidermacs-auto-mode-files))))
+             (member (file-name-nondirectory buffer-file-name)
+                     aidermacs-auto-mode-files))
     (aidermacs-minor-mode 1)))
 
 ;;;###autoload
@@ -957,36 +1142,49 @@ prompt files and other Aider-related files:
 ;;;###autoload
 (defun aidermacs-switch-to-code-mode ()
   "Switch aider to code mode.
-In code mode, aider will make changes to your code to satisfy your requests."
+In code mode, aider will make changes to your code to satisfy
+your requests."
   (interactive)
-  (aidermacs--send-command "/chat-mode code" t)
+  (aidermacs--send-command "/chat-mode code")
+  (with-current-buffer (get-buffer (aidermacs-get-buffer-name))
+    (setq-local aidermacs--current-mode 'code))
   (message "Switched to code mode <default> - aider will make changes to your 
code"))
 
 ;;;###autoload
 (defun aidermacs-switch-to-ask-mode ()
   "Switch aider to ask mode.
-In ask mode, aider will answer questions about your code, but never edit it."
+In ask mode, aider will answer questions about your code, but
+never edit it."
   (interactive)
-  (aidermacs--send-command "/chat-mode ask" t)
+  (aidermacs--send-command "/chat-mode ask")
+  (with-current-buffer (get-buffer (aidermacs-get-buffer-name))
+    (setq-local aidermacs--current-mode 'ask))
   (message "Switched to ask mode - you can chat freely, aider will not edit 
your code"))
 
 ;;;###autoload
 (defun aidermacs-switch-to-architect-mode ()
   "Switch aider to architect mode.
-In architect mode, aider will first propose a solution, then ask if you want
-it to turn that proposal into edits to your files."
+In architect mode, aider will first propose a solution, then ask
+if you want it to turn that proposal into edits to your files."
   (interactive)
-  (aidermacs--send-command "/chat-mode architect" t)
+  (aidermacs--send-command "/chat-mode architect")
+  (with-current-buffer (get-buffer (aidermacs-get-buffer-name))
+    (setq-local aidermacs--current-mode 'architect))
   (message "Switched to architect mode - aider will propose solutions before 
making changes"))
 
 ;;;###autoload
 (defun aidermacs-switch-to-help-mode ()
   "Switch aider to help mode.
-In help mode, aider will answer questions about using aider, configuring,
-troubleshooting, etc."
+In help mode, aider will answer questions about using aider,
+configuring, troubleshooting, etc."
   (interactive)
-  (aidermacs--send-command "/chat-mode help" t)
+  (aidermacs--send-command "/chat-mode help")
+  (with-current-buffer (get-buffer (aidermacs-get-buffer-name))
+    (setq-local aidermacs--current-mode 'help))
   (message "Switched to help mode - aider will answer questions about using 
aider"))
 
+;; Initialize the cleanup mechanisms
+(aidermacs--setup-ediff-cleanup-hooks)
+
 (provide 'aidermacs)
 ;;; aidermacs.el ends here

Reply via email to