diff --git a/README.org b/README.org index 2a4196d..b0269e7 100644 --- a/README.org +++ b/README.org @@ -216,6 +216,41 @@ Start an agent shell session: - =M-x agent-shell-google-start-gemini= - Start a Gemini agent session - =M-x agent-shell-goose-start-agent= - Start a Goose agent session +*** Sidebar + +=agent-shell-sidebar= provides a persistent side panel interface. Each project gets its own independent sidebar that maintains separate state across visibility toggles. + +Commands: + +- =M-x agent-shell-sidebar-toggle= - Toggle sidebar visibility for current project +- =M-x agent-shell-sidebar-toggle-focus= - Toggle focus between sidebar and last buffer +- =M-x agent-shell-sidebar-change-provider= - Switch to a different agent provider +- =M-x agent-shell-sidebar-reset= - Reset sidebar for current project + +Configuration options: + +#+begin_src emacs-lisp +;; Sidebar width (default: "25%") +;; Integer for absolute columns or string with % for percentage of frame +(setq agent-shell-sidebar-width "25%") + +;; Minimum width (default: 80) +(setq agent-shell-sidebar-minimum-width 80) + +;; Maximum width (default: "50%") +(setq agent-shell-sidebar-maximum-width "50%") + +;; Position: 'left or 'right (default: 'right) +(setq agent-shell-sidebar-position 'right) + +;; Default provider (default: nil - prompt user) +;; Values: 'anthropic-claude-code, 'google-gemini, 'openai-codex, 'goose-agent +(setq agent-shell-sidebar-default-provider 'anthropic-claude-code) + +;; Lock sidebar position and size (default: t) +(setq agent-shell-sidebar-locked t) +#+end_src + ** Running agents in Devcontainers / Docker containers (Experimental) =agent-shell= provides rudimentary support for running agents in containers. diff --git a/agent-shell-sidebar.el b/agent-shell-sidebar.el new file mode 100644 index 0000000..944add6 --- /dev/null +++ b/agent-shell-sidebar.el @@ -0,0 +1,551 @@ +;;; agent-shell-sidebar.el --- Sidebar interface for agent-shell -*- lexical-binding: t; -*- + +;; Copyright (C) 2025 Calum MacRae, Alvaro Ramirez + +;; Author: Calum MacRae https://github.com/cmacrae +;; URL: https://github.com/xenodium/agent-shell + +;; This package is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 3, or (at your option) +;; any later version. + +;; This package is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Commentary: +;; +;; Provides sidebar functionality for agent-shell, allowing the agent +;; interface to be displayed as a persistent side panel similar to treemacs. +;; +;; Each project gets its own independent sidebar that persists across +;; visibility toggles and maintains separate state for buffer, provider, +;; and last focused window. +;; +;; Usage: +;; M-x agent-shell-sidebar-toggle - Toggle sidebar visibility +;; M-x agent-shell-sidebar-toggle-focus - Toggle focus between sidebar and last buffer +;; M-x agent-shell-sidebar-change-provider - Switch to a different agent provider +;; M-x agent-shell-sidebar-reset - Reset sidebar for current project +;; +;; Customization: +;; `agent-shell-sidebar-width' - Width of the sidebar (default: 80) +;; `agent-shell-sidebar-position' - Position: 'left or 'right (default: 'right) +;; `agent-shell-sidebar-default-provider' - Default provider to use +;; `agent-shell-sidebar-locked' - Lock sidebar position and size (default: t) + +;;; Code: + +(eval-when-compile + (require 'cl-lib)) +(require 'agent-shell) +(require 'agent-shell-anthropic) +(require 'agent-shell-google) +(require 'agent-shell-openai) +(require 'agent-shell-goose) + +(declare-function agent-shell--start "agent-shell") +(declare-function agent-shell-cwd "agent-shell") +(declare-function agent-shell-anthropic--claude-code-welcome-message "agent-shell-anthropic") +(declare-function agent-shell-anthropic-make-claude-client "agent-shell-anthropic") +(declare-function agent-shell-google--gemini-welcome-message "agent-shell-google") +(declare-function agent-shell-google-make-gemini-client "agent-shell-google") +(declare-function agent-shell-openai--codex-welcome-message "agent-shell-openai") +(declare-function agent-shell-openai-key "agent-shell-openai") +(declare-function agent-shell-goose--welcome-message "agent-shell-goose") +(declare-function agent-shell-goose-make-client "agent-shell-goose") +(declare-function project-root "project") +(declare-function project-roots "project") +(declare-function project-current "project") +(declare-function projectile-project-root "projectile") + +(defvar agent-shell-google-authentication) +(defvar agent-shell-anthropic-authentication) +(defvar agent-shell-openai-authentication) +(defvar agent-shell-openai-codex-command) +(defvar agent-shell-goose-authentication) +(defvar agent-shell-goose-command) + +(defgroup agent-shell-sidebar nil + "Sidebar interface for agent-shell." + :group 'agent-shell + :prefix "agent-shell-sidebar-") + +(defcustom agent-shell-sidebar-width "25%" + "Width of the agent-shell sidebar window. + +Can be specified as: + * An integer (e.g., 80) for absolute width in columns + * A string with % suffix (e.g., \"25%\") for percentage of frame width + +The final width will be constrained by `agent-shell-sidebar-minimum-width' +and `agent-shell-sidebar-maximum-width'." + :type '(choice (integer :tag "Absolute width in columns") + (string :tag "Percentage of frame width (e.g., \"25%\")")) + :group 'agent-shell-sidebar) + +(defcustom agent-shell-sidebar-minimum-width 80 + "Minimum width of the agent-shell sidebar window. + +Can be specified as: + * An integer (e.g., 80) for absolute width in columns + * A string with % suffix (e.g., \"10%\") for percentage of frame width + +This constraint is applied after calculating the configured width." + :type '(choice (integer :tag "Absolute width in columns") + (string :tag "Percentage of frame width (e.g., \"10%\")")) + :group 'agent-shell-sidebar) + +(defcustom agent-shell-sidebar-maximum-width "50%" + "Maximum width of the agent-shell sidebar window. + +Can be specified as: + * An integer (e.g., 120) for absolute width in columns + * A string with % suffix (e.g., \"50%\") for percentage of frame width + +This constraint is applied after calculating the configured width. +When the minimum width is greater than the maximum width, the minimum +takes precedence." + :type '(choice (integer :tag "Absolute width in columns") + (string :tag "Percentage of frame width (e.g., \"50%\")")) + :group 'agent-shell-sidebar) + +(defcustom agent-shell-sidebar-position 'right + "Position of the agent-shell sidebar. + +Valid values are: + * `left' - Display sidebar on the left side + * `right' - Display sidebar on the right side" + :type '(choice (const :tag "Left" left) + (const :tag "Right" right)) + :group 'agent-shell-sidebar) + +(defcustom agent-shell-sidebar-default-provider nil + "Default provider to use for sidebar sessions. + +When set, the sidebar will automatically use this provider without prompting. +When nil, the user will be prompted to select a provider. + +Valid values are: + * `anthropic-claude-code' - Use Anthropic's Claude Code + * `google-gemini' - Use Google's Gemini + * `openai-codex' - Use OpenAI's Codex + * `goose-agent' - Use Goose Agent + * nil - Prompt user to select provider (default)" + :type '(choice (const :tag "Anthropic Claude Code" anthropic-claude-code) + (const :tag "Google Gemini" google-gemini) + (const :tag "OpenAI Codex" openai-codex) + (const :tag "Goose Agent" goose-agent) + (const :tag "Prompt for provider" nil)) + :group 'agent-shell-sidebar) + +(defcustom agent-shell-sidebar-locked t + "When non-nil, lock the sidebar to its fixed position. + +A locked sidebar: + * Cannot be resized manually + * Is invisible to `other-window' commands (C-x o) + +When nil, the sidebar can be resized manually and will be visible to +`other-window' commands." + :type 'boolean + :group 'agent-shell-sidebar) + +(defvar agent-shell-sidebar--project-state (make-hash-table :test 'equal) + "Hash table storing sidebar state per project. +Keys are project root paths, values are alists with: + (:buffer . BUFFER) - sidebar buffer for this project + (:provider . SYMBOL) - provider (anthropic, google, openai, goose) + (:last-buffer . BUFFER) - last non-sidebar buffer with focus + (:width . INTEGER) - current width of the sidebar window") + +(defvar-local agent-shell-sidebar--is-sidebar nil + "Non-nil if this buffer is an agent-shell sidebar buffer.") + +(cl-defun agent-shell-sidebar--make-state (&key buffer provider last-buffer width) + "Construct sidebar state with BUFFER, PROVIDER, LAST-BUFFER, and WIDTH." + (list (cons :buffer buffer) + (cons :provider provider) + (cons :last-buffer last-buffer) + (cons :width width))) + +(defun agent-shell-sidebar--get-project-root () + "Get the current project root directory. +Returns nil if not in a project, otherwise returns the project root. +Checks projectile first, then project.el, then default-directory." + (or (when (fboundp 'projectile-project-root) + (ignore-errors (projectile-project-root))) + (when (fboundp 'project-root) + (when-let* ((proj (project-current))) + (if (fboundp 'project-root) + (project-root proj) + (car (project-roots proj))))) + default-directory)) + +(defun agent-shell-sidebar--project-state (project-root) + "Get the sidebar state alist for PROJECT-ROOT. +Creates empty state if none exists." + (or (gethash project-root agent-shell-sidebar--project-state) + (let ((state (agent-shell-sidebar--make-state))) + (puthash project-root state agent-shell-sidebar--project-state) + state))) + +(defun agent-shell-sidebar--provider-base-name (provider) + "Return the base name for PROVIDER to pass to agent-shell--start. +Final buffer name includes ' Agent @ '." + (pcase provider + ('anthropic "Claude Code") + ('google "Gemini") + ('openai "Codex") + ('goose "Goose") + (_ "Agent"))) + +(defun agent-shell-sidebar--parse-width-value (value frame-width) + "Parse width VALUE into absolute columns. +VALUE can be an integer (absolute columns) or a string ending in '%' +(percentage of FRAME-WIDTH). Returns an integer representing columns." + (cond + ((integerp value) + value) + ((and (stringp value) (string-suffix-p "%" value)) + (let* ((percentage-str (substring value 0 -1)) + (percentage (string-to-number percentage-str))) + (if (and (numberp percentage) (> percentage 0)) + (round (* frame-width (/ percentage 100.0))) + (error "Invalid percentage value: %s" value)))) + (t + (error "Width value must be an integer or percentage string: %s" value)))) + +(defun agent-shell-sidebar--calculate-width () + "Calculate the sidebar width in columns. +Parses `agent-shell-sidebar-width', `agent-shell-sidebar-minimum-width', +and `agent-shell-sidebar-maximum-width', applying constraints. +Returns an integer representing the final width in columns." + (let* ((frame-width (frame-width)) + (configured-width (agent-shell-sidebar--parse-width-value + agent-shell-sidebar-width frame-width)) + (min-width (agent-shell-sidebar--parse-width-value + agent-shell-sidebar-minimum-width frame-width)) + (max-width (agent-shell-sidebar--parse-width-value + agent-shell-sidebar-maximum-width frame-width))) + ;; If min > max, prefer min (user's responsibility to set sensible values) + (cond + ((> min-width max-width) + min-width) + (t + (max min-width (min configured-width max-width)))))) + +(cl-defun agent-shell-sidebar--get-buffer (&key (project-root (agent-shell-sidebar--get-project-root))) + "Get the agent-shell sidebar buffer for PROJECT-ROOT." + (when-let* ((state (agent-shell-sidebar--project-state project-root)) + (buffer (map-elt state :buffer))) + (when (buffer-live-p buffer) + buffer))) + +(cl-defun agent-shell-sidebar--get-window (&key (project-root (agent-shell-sidebar--get-project-root))) + "Get the window displaying the sidebar for PROJECT-ROOT." + (when-let* ((buffer (agent-shell-sidebar--get-buffer :project-root project-root))) + (get-buffer-window buffer))) + +(cl-defun agent-shell-sidebar--visible-p (&key (project-root (agent-shell-sidebar--get-project-root))) + "Return t if sidebar for PROJECT-ROOT is currently visible." + (not (null (agent-shell-sidebar--get-window :project-root project-root)))) + +(defun agent-shell-sidebar--buffer-p (buffer) + "Return t if BUFFER is a sidebar buffer for any project." + (when buffer + (buffer-local-value 'agent-shell-sidebar--is-sidebar buffer))) + +(cl-defun agent-shell-sidebar--setup-window (&key buffer project-root) + "Setup window parameters for sidebar BUFFER in PROJECT-ROOT." + (when-let* ((window (get-buffer-window buffer))) + (with-selected-window window + (set-window-parameter window 'no-delete-other-windows t) + (set-window-parameter window 'window-side agent-shell-sidebar-position) + (set-window-parameter window 'window-slot 0) + (set-window-dedicated-p window t) + + (with-current-buffer buffer + (setq-local window-size-fixed (when agent-shell-sidebar-locked 'width))) + + (unless (one-window-p) + (let* ((state (agent-shell-sidebar--project-state project-root)) + (target-width (if agent-shell-sidebar-locked + ;; When locked, always recalculate from config (responsive) + (agent-shell-sidebar--calculate-width) + ;; When unlocked, use saved width or calculate from config + (or (map-elt state :width) + (agent-shell-sidebar--calculate-width)))) + (window-size-fixed nil)) + (when (> (window-width) target-width) + (shrink-window-horizontally (- (window-width) target-width))) + (when (< (window-width) target-width) + (enlarge-window-horizontally (- target-width (window-width))))))))) + +(cl-defun agent-shell-sidebar--display-buffer (&key buffer project-root) + "Display BUFFER as a sidebar for PROJECT-ROOT." + (let ((window (display-buffer + buffer + `(display-buffer-in-side-window + . ((side . ,agent-shell-sidebar-position) + (slot . 0) + (window-width . ,(agent-shell-sidebar--calculate-width)) + (dedicated . t) + (window-parameters . ((no-delete-other-windows . t)))))))) + (agent-shell-sidebar--setup-window :buffer buffer :project-root project-root) + window)) + +(cl-defun agent-shell-sidebar--make-session (&key provider base-name) + "Create agent session for PROVIDER with BASE-NAME." + (pcase provider + ('anthropic + (agent-shell--start + :no-focus t + :new-session t + :mode-line-name "Claude Code" + :buffer-name base-name + :shell-prompt "Claude Code> " + :shell-prompt-regexp "Claude Code> " + :icon-name "anthropic.png" + :welcome-function #'agent-shell-anthropic--claude-code-welcome-message + :client-maker #'agent-shell-anthropic-make-claude-client)) + ('google + (agent-shell--start + :no-focus t + :new-session t + :mode-line-name "Gemini" + :buffer-name base-name + :shell-prompt "Gemini> " + :shell-prompt-regexp "Gemini> " + :icon-name "gemini.png" + :welcome-function #'agent-shell-google--gemini-welcome-message + :needs-authentication t + :authenticate-request-maker (lambda () + (cond ((map-elt agent-shell-google-authentication :api-key) + (acp-make-authenticate-request :method-id "gemini-api-key")) + ((map-elt agent-shell-google-authentication :vertex-ai) + (acp-make-authenticate-request :method-id "vertex-ai")) + (t + (acp-make-authenticate-request :method-id "oauth-personal")))) + :client-maker #'agent-shell-google-make-gemini-client)) + ('openai + (let ((api-key (agent-shell-openai-key))) + (unless api-key + (user-error "Please set your `agent-shell-openai-authentication'")) + (agent-shell--start + :no-focus t + :new-session t + :mode-line-name "Codex" + :buffer-name base-name + :shell-prompt "Codex> " + :shell-prompt-regexp "Codex> " + :icon-name "openai.png" + :welcome-function #'agent-shell-openai--codex-welcome-message + :client-maker (lambda () + (acp-make-client + :command (car agent-shell-openai-codex-command) + :command-params (cdr agent-shell-openai-codex-command) + :environment-variables (list (format "OPENAI_API_KEY=%s" api-key))))))) + ('goose + (agent-shell--start + :no-focus t + :new-session t + :mode-line-name "Goose" + :buffer-name base-name + :shell-prompt "Goose> " + :shell-prompt-regexp "Goose> " + :icon-name "goose.png" + :welcome-function #'agent-shell-goose--welcome-message + :client-maker #'agent-shell-goose-make-client)) + (_ (error "Unknown provider: %s" provider)))) + +(defun agent-shell-sidebar--clean-up () + "Clean up sidebar resources when buffer is killed." + (when agent-shell-sidebar--is-sidebar + (let ((project-root (agent-shell-sidebar--get-project-root))) + (when-let* ((state (gethash project-root agent-shell-sidebar--project-state)) + (buffer (map-elt state :buffer))) + (when (eq buffer (current-buffer)) + (map-put! (agent-shell-sidebar--project-state project-root) :buffer nil)))))) + +(cl-defun agent-shell-sidebar--start-session (&key provider project-root) + "Start new agent-shell sidebar session for PROVIDER in PROJECT-ROOT. +PROVIDER should be: `anthropic', `google', `openai', or `goose'." + (let* ((base-name (agent-shell-sidebar--provider-base-name provider)) + (state (agent-shell-sidebar--project-state project-root)) + (existing-buffer (map-elt state :buffer))) + + (when (and existing-buffer (buffer-live-p existing-buffer)) + (kill-buffer existing-buffer)) + + (let ((shell-buffer (agent-shell-sidebar--make-session + :provider provider + :base-name base-name))) + (with-current-buffer shell-buffer + (setq-local agent-shell-sidebar--is-sidebar t) + (add-hook 'kill-buffer-hook #'agent-shell-sidebar--clean-up nil t)) + + (map-put! state :buffer shell-buffer) + (map-put! state :provider provider) + + shell-buffer))) + +(defun agent-shell-sidebar--map-provider-symbol (provider-symbol) + "Map user-facing PROVIDER-SYMBOL to internal provider symbol. +Returns the internal symbol (anthropic, google, openai, goose) or nil." + (pcase provider-symbol + ('anthropic-claude-code 'anthropic) + ('google-gemini 'google) + ('openai-codex 'openai) + ('goose-agent 'goose) + (_ nil))) + +(defun agent-shell-sidebar--select-provider () + "Select which agent provider to use. +If `agent-shell-sidebar-default-provider' is set, use that without prompting. +Otherwise, interactively prompt the user to select a provider." + (if agent-shell-sidebar-default-provider + (or (agent-shell-sidebar--map-provider-symbol agent-shell-sidebar-default-provider) + (error "Invalid agent-shell-sidebar-default-provider: %s" agent-shell-sidebar-default-provider)) + (let ((providers '(("Claude (Anthropic)" . anthropic) + ("Gemini (Google)" . google) + ("Codex (OpenAI)" . openai) + ("Goose" . goose)))) + (cdr (assoc (completing-read "Select agent provider: " providers nil t) + providers))))) + +(cl-defun agent-shell-sidebar--save-last-buffer (&key project-root) + "Save current buffer as last-buffer for PROJECT-ROOT if not a sidebar." + (unless (agent-shell-sidebar--buffer-p (current-buffer)) + (map-put! (agent-shell-sidebar--project-state project-root) :last-buffer (current-buffer)))) + +(cl-defun agent-shell-sidebar--restore-last-buffer (&key project-root) + "Restore focus to last buffer for PROJECT-ROOT or find window." + (let* ((state (agent-shell-sidebar--project-state project-root)) + (last-buffer (map-elt state :last-buffer))) + (if (and last-buffer (buffer-live-p last-buffer)) + (if-let* ((last-window (get-buffer-window last-buffer))) + (select-window last-window) + ;; Last buffer exists but not visible - try to find another window + (let ((mru-window (get-mru-window (selected-frame) nil :not-selected))) + (if mru-window + (select-window mru-window) + ;; No other window exists - display last-buffer + (switch-to-buffer last-buffer)))) + ;; No last buffer - try to find most recently used window + (let ((mru-window (get-mru-window (selected-frame) nil :not-selected))) + (when mru-window + (select-window mru-window)))))) + +(cl-defun agent-shell-sidebar--hide-sidebar (&key project-root window) + "Hide sidebar WINDOW for PROJECT-ROOT and save width if unlocked." + ;; Only save width when unlocked (user may have manually resized) + (unless agent-shell-sidebar-locked + (map-put! (agent-shell-sidebar--project-state project-root) :width (window-width window))) + (agent-shell-sidebar--restore-last-buffer :project-root project-root) + (delete-window window)) + +(cl-defun agent-shell-sidebar--show-existing-sidebar (&key project-root buffer) + "Show existing sidebar BUFFER for PROJECT-ROOT." + (agent-shell-sidebar--save-last-buffer :project-root project-root) + (let ((window (agent-shell-sidebar--display-buffer :buffer buffer :project-root project-root))) + (select-window window))) + +(cl-defun agent-shell-sidebar--create-and-show-sidebar (&key project-root) + "Create new sidebar for PROJECT-ROOT and show it." + (agent-shell-sidebar--save-last-buffer :project-root project-root) + (let* ((state (agent-shell-sidebar--project-state project-root)) + (provider (or (map-elt state :provider) + (agent-shell-sidebar--select-provider))) + (shell-buffer (agent-shell-sidebar--start-session :provider provider :project-root project-root)) + (window (agent-shell-sidebar--display-buffer :buffer shell-buffer :project-root project-root))) + (select-window window))) + +;;;###autoload +(defun agent-shell-sidebar-toggle () + "Toggle sidebar visibility for the current project. +If sidebar doesn't exist, create new session and focus it. +If it exists but not visible, show it and focus it. +If visible, hide it and return focus to last focused buffer. + +Each project gets its own independent sidebar." + (interactive) + (let ((project-root (agent-shell-sidebar--get-project-root))) + (cond + ((agent-shell-sidebar--get-window :project-root project-root) + (agent-shell-sidebar--hide-sidebar + :project-root project-root + :window (agent-shell-sidebar--get-window :project-root project-root))) + ((agent-shell-sidebar--get-buffer :project-root project-root) + (agent-shell-sidebar--show-existing-sidebar + :project-root project-root + :buffer (agent-shell-sidebar--get-buffer :project-root project-root))) + (t + (agent-shell-sidebar--create-and-show-sidebar :project-root project-root))))) + +;;;###autoload +(defun agent-shell-sidebar-toggle-focus () + "Toggle focus between sidebar and last buffer for current project. +If not in sidebar, switch to it (creating if necessary). +If in sidebar, switch back to the last non-sidebar buffer. +When repeatedly called, switches focus back and forth. + +Each project maintains its own sidebar and last-buffer state." + (interactive) + (let* ((project-root (agent-shell-sidebar--get-project-root)) + (sidebar-buffer (agent-shell-sidebar--get-buffer :project-root project-root)) + (in-sidebar (and sidebar-buffer (eq (current-buffer) sidebar-buffer)))) + (if in-sidebar + (agent-shell-sidebar--restore-last-buffer :project-root project-root) + (progn + (agent-shell-sidebar--save-last-buffer :project-root project-root) + (cond + ((agent-shell-sidebar--get-window :project-root project-root) + (select-window (agent-shell-sidebar--get-window :project-root project-root))) + (sidebar-buffer + (agent-shell-sidebar--show-existing-sidebar + :project-root project-root + :buffer sidebar-buffer)) + (t + (agent-shell-sidebar--create-and-show-sidebar :project-root project-root))))))) + +;;;###autoload +(defun agent-shell-sidebar-change-provider () + "Change the agent provider for the current project's sidebar. +This will kill the current sidebar session and start a new one with +the selected provider." + (interactive) + (let ((project-root (agent-shell-sidebar--get-project-root)) + (provider (agent-shell-sidebar--select-provider))) + (when-let* ((buffer (agent-shell-sidebar--get-buffer :project-root project-root))) + (kill-buffer buffer)) + (let* ((shell-buffer (agent-shell-sidebar--start-session :provider provider :project-root project-root)) + (window (agent-shell-sidebar--display-buffer :buffer shell-buffer :project-root project-root))) + (select-window window)))) + +;;;###autoload +(defun agent-shell-sidebar-reset () + "Reset the sidebar for the current project by killing the current session. +The next toggle will create a fresh session." + (interactive) + (let ((project-root (agent-shell-sidebar--get-project-root))) + (when-let* ((buffer (agent-shell-sidebar--get-buffer :project-root project-root))) + (when-let* ((window (get-buffer-window buffer))) + (delete-window window)) + (kill-buffer buffer)) + (remhash project-root agent-shell-sidebar--project-state) + (message "Sidebar reset for project: %s" + (file-name-nondirectory (directory-file-name project-root))))) + +(with-eval-after-load 'golden-ratio + (when (boundp 'golden-ratio-exclude-modes) + (add-to-list 'golden-ratio-exclude-modes 'agent-shell-mode))) + +(provide 'agent-shell-sidebar) + +;;; agent-shell-sidebar.el ends here diff --git a/agent-shell.el b/agent-shell.el index cfcf352..bf0f69c 100644 --- a/agent-shell.el +++ b/agent-shell.el @@ -1480,6 +1480,9 @@ FORMAT-ARGS are passed to `format' with ERROR-FORMAT." " ") error-message) format-args))) +(with-eval-after-load 'agent-shell + (require 'agent-shell-sidebar)) + (provide 'agent-shell) ;;; agent-shell.el ends here