[emacs] More hacking, snapshot to sync
This commit is contained in:
parent
cdc09a3c8c
commit
1fd66682e5
3 changed files with 505 additions and 275 deletions
|
|
@ -57,8 +57,28 @@
|
||||||
'(org-log-done t)
|
'(org-log-done t)
|
||||||
'(org-odd-levels-only t)
|
'(org-odd-levels-only t)
|
||||||
'(org-todo-keywords '((sequence "TODO" "|" "DONE" "ABANDONED" "DEFERRED")))
|
'(org-todo-keywords '((sequence "TODO" "|" "DONE" "ABANDONED" "DEFERRED")))
|
||||||
|
'(package-check-signature nil)
|
||||||
'(package-selected-packages
|
'(package-selected-packages
|
||||||
'(gptel fish-mode editorconfig jsonnet-mode scala-ts-mode adaptive-wrap add-node-modules-path ag auto-complete auto-complete-nxml bazel blacken cider clang-format clojure-mode color-theme-monokai color-theme-sanityinc-solarized color-theme-sanityinc-tomorrow company company-jedi company-lsp compat cquery dash-functional deadgrep dockerfile-mode doom-themes earthfile-mode eglot eglot-java elm-mode esup exec-path-from-shell filladapt flycheck flycheck-elm flycheck-rust flymake flyspell fsharp-mode geiser gnu-elpa-keyring-update go-autocomplete go-mode graphviz-dot-mode hack-mode haxe-mode howm ink-mode js2-mode js2-refactor json-mode lsp-hack lsp-pyright lsp-ui lua-mode magit markdown-mode merlin mocha modus-themes monky monokai-theme multi-term mustache-mode nyan-mode paredit popup prettier-js projectile protobuf-mode python-mode rjsx-mode ruby-mode rust-mode sql-indent swift-mode switch-window terraform-mode thrift tide tree-sitter tss tuareg typescript-mode use-package vterm web-mode wgrep xref-js2 xterm-color yaml-mode zig-mode))
|
'(adaptive-wrap add-node-modules-path ag auto-complete auto-complete-nxml
|
||||||
|
bazel blacken cider clang-format clipetty clojure-mode
|
||||||
|
color-theme-monokai color-theme-sanityinc-solarized
|
||||||
|
color-theme-sanityinc-tomorrow company company-jedi
|
||||||
|
company-lsp compat cquery dash-functional deadgrep
|
||||||
|
dockerfile-mode doom-themes earthfile-mode editorconfig
|
||||||
|
eglot eglot-java elm-mode esup exec-path-from-shell
|
||||||
|
filladapt fish-mode flycheck flycheck-elm flycheck-rust
|
||||||
|
flymake flyspell fsharp-mode geiser
|
||||||
|
gnu-elpa-keyring-update go-autocomplete go-mode gptel
|
||||||
|
graphviz-dot-mode hack-mode haxe-mode howm ink-mode
|
||||||
|
js2-mode js2-refactor json-mode jsonnet-mode lsp-hack
|
||||||
|
lsp-pyright lsp-ui lua-mode magit markdown-mode merlin
|
||||||
|
mocha modus-themes monky monokai-theme multi-term
|
||||||
|
mustache-mode nyan-mode paredit popup prettier-js
|
||||||
|
projectile protobuf-mode python-mode request rjsx-mode
|
||||||
|
ruby-mode rust-mode scala-mode scala-ts-mode sql-indent
|
||||||
|
swift-mode switch-window terraform-mode thrift tide
|
||||||
|
tree-sitter tss tuareg typescript-mode use-package vterm
|
||||||
|
web-mode wgrep xref-js2 xterm-color yaml-mode zig-mode))
|
||||||
'(reb-re-syntax 'string)
|
'(reb-re-syntax 'string)
|
||||||
'(rmail-mail-new-frame t)
|
'(rmail-mail-new-frame t)
|
||||||
'(safe-local-variable-values
|
'(safe-local-variable-values
|
||||||
|
|
@ -73,8 +93,8 @@
|
||||||
(whitespace-mode 0)
|
(whitespace-mode 0)
|
||||||
(whitespace-mode 1))
|
(whitespace-mode 1))
|
||||||
(whitespace-line-column . 80)
|
(whitespace-line-column . 80)
|
||||||
(whitespace-style face trailing lines-tail)
|
(whitespace-style face trailing lines-tail) (require-final-newline . t)))
|
||||||
(require-final-newline . t)))
|
'(scala-indent:use-javadoc-style t)
|
||||||
'(scroll-conservatively 1)
|
'(scroll-conservatively 1)
|
||||||
'(scroll-step 1)
|
'(scroll-step 1)
|
||||||
'(sd-user-email "johndoty@microsoft.com")
|
'(sd-user-email "johndoty@microsoft.com")
|
||||||
|
|
@ -86,6 +106,7 @@
|
||||||
'(tags-revert-without-query t)
|
'(tags-revert-without-query t)
|
||||||
'(tramp-completion-reread-directory-timeout nil)
|
'(tramp-completion-reread-directory-timeout nil)
|
||||||
'(tramp-default-method "sshx")
|
'(tramp-default-method "sshx")
|
||||||
|
'(tramp-use-connection-share nil)
|
||||||
'(tramp-use-ssh-controlmaster-options nil)
|
'(tramp-use-ssh-controlmaster-options nil)
|
||||||
'(transient-mark-mode t)
|
'(transient-mark-mode t)
|
||||||
'(truncate-lines t)
|
'(truncate-lines t)
|
||||||
|
|
|
||||||
|
|
@ -1401,44 +1401,24 @@ Do this when you edit your project view."
|
||||||
;; =================================================================
|
;; =================================================================
|
||||||
;; AI Shit
|
;; AI Shit
|
||||||
;; =================================================================
|
;; =================================================================
|
||||||
(use-package request :ensure)
|
(defun claude-get-api-key ()
|
||||||
|
"Get Claude API key from auth-source."
|
||||||
|
(let ((auth-info (nth 0 (auth-source-search :host "anthropic.com"
|
||||||
|
:user "claude-api"
|
||||||
|
:require '(:secret)
|
||||||
|
:create t))))
|
||||||
|
(if auth-info
|
||||||
|
(let ((secret (plist-get auth-info :secret)))
|
||||||
|
(if (functionp secret)
|
||||||
|
(funcall secret)
|
||||||
|
secret))
|
||||||
|
(error "Claude API key not found in auth-source"))))
|
||||||
|
|
||||||
(use-package claude
|
(use-package gptel :ensure
|
||||||
:load-path "~/site-lisp/"
|
|
||||||
:custom
|
|
||||||
(claude-model "claude-3-7-sonnet-20250219") ;; Current model as of March 2025
|
|
||||||
(claude-max-tokens 4000)
|
|
||||||
(claude-auto-display-results t)
|
|
||||||
:config
|
:config
|
||||||
;; If you want to add any custom tools, add them here
|
(setq
|
||||||
;; (claude-register-tool
|
gptel-model 'claude-3-7-sonnet-20250219 ; "claude-3-opus-20240229" also available
|
||||||
;; '(:name "my_custom_tool"
|
gptel-backend (gptel-make-anthropic "Claude"
|
||||||
;; :description "A custom tool that does something specific"
|
:stream t :key #'claude-get-api-key))
|
||||||
;; :parameters ((properties
|
)
|
||||||
;; (param1 (title . "Parameter 1")
|
|
||||||
;; (type . "string")
|
|
||||||
;; (description . "Description of parameter 1"))))))
|
|
||||||
|
|
||||||
;; (claude-register-tool-handler
|
|
||||||
;; "my_custom_tool"
|
|
||||||
;; (lambda (parameters)
|
|
||||||
;; ;; Your implementation here
|
|
||||||
;; (format "Tool executed with param: %s" (cdr (assoc 'param1 parameters)))))
|
|
||||||
|
|
||||||
;; Enable the minor mode for keybindings
|
|
||||||
(claude-mode 1)
|
|
||||||
:bind (:map claude-mode-map
|
|
||||||
;; You can customize the keybindings if you prefer different ones
|
|
||||||
("C-c C-a a" . claude-prompt-and-send) ;; Add a custom binding
|
|
||||||
;; Default bindings included by claude-mode:
|
|
||||||
;; C-c C-a s - claude-send-region
|
|
||||||
;; C-c C-a b - claude-send-buffer
|
|
||||||
;; C-c C-a r - claude-code-review
|
|
||||||
;; C-c C-a e - claude-explain-code
|
|
||||||
;; C-c C-a c - claude-complete-code
|
|
||||||
;; C-c C-a t - claude-send-with-tools
|
|
||||||
;; C-c C-a l - claude-list-requested-tools
|
|
||||||
;; C-c C-a p - claude-prompt-and-send
|
|
||||||
))
|
|
||||||
|
|
||||||
;;; init.el ends here
|
;;; init.el ends here
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,9 @@
|
||||||
;; This package provides integration with the Claude AI assistant from
|
;; This package provides integration with the Claude AI assistant from
|
||||||
;; Anthropic. It includes basic functionality like sending text to Claude, as
|
;; Anthropic. It includes basic functionality like sending text to Claude, as
|
||||||
;; well as code-specific features and tool use capabilities.
|
;; well as code-specific features and tool use capabilities.
|
||||||
|
;;
|
||||||
|
;; It now features a conversational interface with support for context buffers
|
||||||
|
;; and optional tool use.
|
||||||
|
|
||||||
;;; Code:
|
;;; Code:
|
||||||
|
|
||||||
|
|
@ -37,29 +40,77 @@
|
||||||
:type 'boolean
|
:type 'boolean
|
||||||
:group 'claude)
|
:group 'claude)
|
||||||
|
|
||||||
|
(defcustom claude-conversation-buffer-name "*Claude Conversation*"
|
||||||
|
"Name of the buffer for Claude conversations."
|
||||||
|
:type 'string
|
||||||
|
:group 'claude)
|
||||||
|
|
||||||
|
(defcustom claude-enable-markdown-fontification t
|
||||||
|
"If non-nil, enable markdown fontification in the conversation buffer."
|
||||||
|
:type 'boolean
|
||||||
|
:group 'claude)
|
||||||
|
|
||||||
|
(defcustom claude-user-message-face 'font-lock-keyword-face
|
||||||
|
"Face for user messages in the conversation buffer."
|
||||||
|
:type 'face
|
||||||
|
:group 'claude)
|
||||||
|
|
||||||
|
(defcustom claude-assistant-message-face 'font-lock-comment-face
|
||||||
|
"Face for Claude's messages in the conversation buffer."
|
||||||
|
:type 'face
|
||||||
|
:group 'claude)
|
||||||
|
|
||||||
|
(defcustom claude-timestamp-face 'font-lock-doc-face
|
||||||
|
"Face for timestamps in the conversation buffer."
|
||||||
|
:type 'face
|
||||||
|
:group 'claude)
|
||||||
|
|
||||||
|
(defcustom claude-separator-string "────────────────────────────────────────────────────────────"
|
||||||
|
"Separator string used between messages in the conversation buffer."
|
||||||
|
:type 'string
|
||||||
|
:group 'claude)
|
||||||
|
|
||||||
;;; Core functionality:
|
;;; Core functionality:
|
||||||
|
|
||||||
(defvar claude-buffer-name "*Claude*"
|
(defvar claude-conversation-history nil
|
||||||
"Name of the buffer for Claude interactions.")
|
"History of conversation with Claude as a list of alists with keys 'role' and 'content'.")
|
||||||
|
|
||||||
(defvar claude-last-request nil
|
(defvar claude-context-buffers nil
|
||||||
"Store the last request data sent to Claude.")
|
"List of buffers to provide as context for the conversation.")
|
||||||
|
|
||||||
(defvar claude-response-mode-map
|
(defvar claude-tools-enabled nil
|
||||||
|
"If non-nil, enable tool use in the conversation.")
|
||||||
|
|
||||||
|
(defvar claude-conversation-mode-map
|
||||||
(let ((map (make-sparse-keymap)))
|
(let ((map (make-sparse-keymap)))
|
||||||
(define-key map "q" 'quit-window)
|
(define-key map (kbd "RET") 'claude-send-input)
|
||||||
(define-key map "r" 'claude-refresh-last-request) ;; We'll define this function later
|
(define-key map (kbd "C-c C-c") 'claude-send-input)
|
||||||
|
(define-key map (kbd "C-c C-k") 'claude-cancel-input)
|
||||||
|
;; Change these keybindings to avoid conflict with the global prefix
|
||||||
|
(define-key map (kbd "C-c a") 'claude-add-context-buffer) ;; was C-c C-a
|
||||||
|
(define-key map (kbd "C-c r") 'claude-remove-context-buffer) ;; was C-c C-r
|
||||||
|
(define-key map (kbd "C-c l") 'claude-list-context-buffers) ;; was C-c C-l
|
||||||
|
(define-key map (kbd "C-c t") 'claude-toggle-tool-use) ;; was C-c C-t
|
||||||
|
(define-key map (kbd "C-c n") 'claude-new-conversation) ;; was C-c C-n
|
||||||
map)
|
map)
|
||||||
"Keymap for Claude response buffers.")
|
"Keymap for Claude conversation buffers.")
|
||||||
|
|
||||||
(define-derived-mode claude-response-mode markdown-mode "Claude"
|
(define-derived-mode claude-conversation-mode text-mode "Claude Conversation"
|
||||||
"Major mode for viewing Claude AI responses."
|
"Major mode for conversing with Claude AI assistant."
|
||||||
(use-local-map claude-response-mode-map)
|
|
||||||
;; Enable visual line mode for word wrapping
|
;; Enable visual line mode for word wrapping
|
||||||
(visual-line-mode 1)
|
(visual-line-mode 1)
|
||||||
;; Make buffer read-only by default
|
(setq-local indent-tabs-mode nil)
|
||||||
(setq buffer-read-only t))
|
|
||||||
|
|
||||||
|
(use-local-map claude-conversation-mode-map)
|
||||||
|
|
||||||
|
(when claude-enable-markdown-fontification
|
||||||
|
(when (fboundp 'markdown-mode)
|
||||||
|
;; We only want the font-lock features of markdown mode
|
||||||
|
(setq-local font-lock-defaults (cadr (assoc 'font-lock-defaults
|
||||||
|
(symbol-plist 'markdown-mode))))))
|
||||||
|
;; Make the bottom of the buffer input-only
|
||||||
|
(setq-local claude-input-marker (point-max-marker))
|
||||||
|
(set-marker-insertion-type claude-input-marker nil)) ;; ?
|
||||||
|
|
||||||
(defun claude-get-api-key ()
|
(defun claude-get-api-key ()
|
||||||
"Get Claude API key from auth-source."
|
"Get Claude API key from auth-source."
|
||||||
|
|
@ -74,24 +125,34 @@
|
||||||
secret))
|
secret))
|
||||||
(error "Claude API key not found in auth-source"))))
|
(error "Claude API key not found in auth-source"))))
|
||||||
|
|
||||||
(defun claude-ensure-buffer ()
|
(defun claude-ensure-conversation-buffer ()
|
||||||
"Ensure the Claude buffer exists with proper formatting and keybindings."
|
"Ensure the Claude conversation buffer exists and return it."
|
||||||
(let ((buffer (get-buffer-create claude-buffer-name)))
|
(let ((buffer (get-buffer-create claude-conversation-buffer-name)))
|
||||||
(with-current-buffer buffer
|
(with-current-buffer buffer
|
||||||
(unless (eq major-mode 'claude-response-mode)
|
(unless (eq major-mode 'claude-conversation-mode)
|
||||||
;; Use our custom mode that has the q key binding
|
(claude-conversation-mode)
|
||||||
(if (fboundp 'markdown-mode) ;; Check if markdown-mode is available
|
(claude-insert-conversation-header)))
|
||||||
(claude-response-mode) ;; Use our derived mode if markdown is available
|
|
||||||
;; Fallback if markdown-mode isn't available
|
|
||||||
(special-mode) ;; special-mode also has the q key binding
|
|
||||||
(visual-line-mode 1))
|
|
||||||
|
|
||||||
;; Set word-wrap and margins regardless of mode
|
|
||||||
(setq word-wrap t)
|
|
||||||
(setq left-margin-width 2
|
|
||||||
right-margin-width 2)))
|
|
||||||
buffer))
|
buffer))
|
||||||
|
|
||||||
|
(defun claude-insert-conversation-header ()
|
||||||
|
"Insert the header for a new conversation."
|
||||||
|
(let ((inhibit-read-only t))
|
||||||
|
(erase-buffer)
|
||||||
|
(insert (propertize "Claude Conversation" 'face 'bold) "\n\n")
|
||||||
|
(insert "Type your message below and press RET or C-c C-c to send.\n")
|
||||||
|
(insert "Commands:\n")
|
||||||
|
(insert " C-c a: Add a context buffer\n") ;; was C-c C-a
|
||||||
|
(insert " C-c r: Remove a context buffer\n") ;; was C-c C-r
|
||||||
|
(insert " C-c l: List context buffers\n") ;; was C-c C-l
|
||||||
|
(insert " C-c t: Toggle tool use\n") ;; was C-c C-t
|
||||||
|
(insert " C-c n: Start a new conversation\n") ;; was C-c C-n
|
||||||
|
(insert "\n")
|
||||||
|
(insert (propertize claude-separator-string 'face 'shadow))
|
||||||
|
(insert "\n\n")
|
||||||
|
;; Set the input marker at the end of the buffer
|
||||||
|
(setq-local claude-input-marker (point-marker))
|
||||||
|
(set-marker-insertion-type claude-input-marker nil)))
|
||||||
|
|
||||||
(defun claude-send-request (data &optional callback error-callback)
|
(defun claude-send-request (data &optional callback error-callback)
|
||||||
"Send request with DATA to Claude API.
|
"Send request with DATA to Claude API.
|
||||||
If CALLBACK is provided, call it with the response data.
|
If CALLBACK is provided, call it with the response data.
|
||||||
|
|
@ -115,158 +176,237 @@ If ERROR-CALLBACK is provided, call it with any error."
|
||||||
(funcall error-callback error-thrown)
|
(funcall error-callback error-thrown)
|
||||||
(message "Claude API error: %s" error-thrown)))))))
|
(message "Claude API error: %s" error-thrown)))))))
|
||||||
|
|
||||||
(defun claude-display-response (data)
|
(defun claude-append-to-conversation (role content)
|
||||||
"Display the response DATA from Claude with nice formatting."
|
"Append a message with ROLE and CONTENT to the conversation history."
|
||||||
(let ((content (cdr (assoc 'content data)))
|
(add-to-list 'claude-conversation-history
|
||||||
(buffer (claude-ensure-buffer)))
|
`((role . ,role)
|
||||||
|
(content . ,content))
|
||||||
|
t))
|
||||||
|
|
||||||
|
(defun claude-display-user-message (message)
|
||||||
|
"Display the user MESSAGE in the conversation buffer."
|
||||||
|
(let ((buffer (claude-ensure-conversation-buffer))
|
||||||
|
(timestamp (format-time-string "%H:%M:%S")))
|
||||||
(with-current-buffer buffer
|
(with-current-buffer buffer
|
||||||
(let ((inhibit-read-only t))
|
(let ((inhibit-read-only t))
|
||||||
;; Clear the buffer
|
(save-excursion
|
||||||
(erase-buffer)
|
(goto-char claude-input-marker)
|
||||||
|
(insert "\n")
|
||||||
|
(insert (propertize (format "[You - %s]" timestamp)
|
||||||
|
'face claude-timestamp-face))
|
||||||
|
(insert "\n\n")
|
||||||
|
(insert (propertize message 'face claude-user-message-face))
|
||||||
|
(insert "\n\n")
|
||||||
|
(insert (propertize claude-separator-string 'face 'shadow))
|
||||||
|
(insert "\n\n")))
|
||||||
|
(setq-local claude-input-marker (point-max-marker)))))
|
||||||
|
|
||||||
;; Add a timestamp header
|
(defun claude-display-assistant-message (content)
|
||||||
(insert (propertize
|
"Display the assistant's CONTENT in the conversation buffer."
|
||||||
(format "Claude response at %s\n\n"
|
(let ((buffer (claude-ensure-conversation-buffer))
|
||||||
(format-time-string "%H:%M:%S"))
|
(timestamp (format-time-string "%H:%M:%S")))
|
||||||
'face 'font-lock-comment-face))
|
(with-current-buffer buffer
|
||||||
|
(let ((inhibit-read-only t))
|
||||||
|
(save-excursion
|
||||||
|
(goto-char claude-input-marker)
|
||||||
|
(insert "\n")
|
||||||
|
(insert (propertize (format "[Claude - %s]" timestamp)
|
||||||
|
'face claude-timestamp-face))
|
||||||
|
(insert "\n\n")
|
||||||
|
(insert (propertize content 'face claude-assistant-message-face))
|
||||||
|
(insert "\n\n")
|
||||||
|
(insert (propertize claude-separator-string 'face 'shadow))
|
||||||
|
(insert "\n\n")))
|
||||||
|
(setq-local claude-input-marker (point-max-marker))
|
||||||
|
(goto-char (point-max)))))
|
||||||
|
|
||||||
;; Process content
|
(defun claude-display-loading-indicator ()
|
||||||
(if (and content (arrayp content) (> (length content) 0))
|
"Display a loading indicator in the conversation buffer."
|
||||||
(dotimes (i (length content))
|
(let ((buffer (claude-ensure-conversation-buffer))
|
||||||
(let* ((item (aref content i))
|
(timestamp (format-time-string "%H:%M:%S")))
|
||||||
(type (cdr (assoc 'type item))))
|
(with-current-buffer buffer
|
||||||
(when (string= type "text")
|
(let ((inhibit-read-only t))
|
||||||
(let ((text (cdr (assoc 'text item))))
|
(save-excursion
|
||||||
(insert text "\n\n")))))
|
(goto-char claude-input-marker)
|
||||||
|
(insert "\n")
|
||||||
|
(insert (propertize (format "[Claude - %s]" timestamp)
|
||||||
|
'face claude-timestamp-face))
|
||||||
|
(insert "\n\n")
|
||||||
|
(insert (propertize "Thinking..." 'face 'italic))
|
||||||
|
;; Don't insert separator yet
|
||||||
|
(insert "\n\n")
|
||||||
|
(setq-local claude-loading-indicator-point (point-marker))
|
||||||
|
(set-marker-insertion-type claude-loading-indicator-point t))))))
|
||||||
|
|
||||||
;; Handle unexpected response format
|
(defun claude-remove-loading-indicator ()
|
||||||
(insert (propertize "Unexpected response format from Claude API.\n"
|
"Remove the loading indicator from the conversation buffer."
|
||||||
'face 'font-lock-warning-face))
|
(when (and (boundp 'claude-loading-indicator-point)
|
||||||
(insert "Response data: " (prin1-to-string data)))
|
claude-loading-indicator-point)
|
||||||
|
(let ((buffer (claude-ensure-conversation-buffer)))
|
||||||
;; Add helpful instructions at the bottom
|
|
||||||
(goto-char (point-max))
|
|
||||||
(insert (propertize "\n──────────────────────────────────────\n"
|
|
||||||
'face 'font-lock-comment-face))
|
|
||||||
(insert (propertize "Press q to close this window\n"
|
|
||||||
'face 'font-lock-comment-face))
|
|
||||||
|
|
||||||
;; Return to the start of the buffer
|
|
||||||
(goto-char (point-min))))
|
|
||||||
|
|
||||||
;; Only display the buffer if requested and not already visible
|
|
||||||
(when claude-auto-display-results
|
|
||||||
(unless (get-buffer-window buffer)
|
|
||||||
(display-buffer-other-window buffer)))))
|
|
||||||
|
|
||||||
(defun claude-send-message (prompt &optional system-prompt tools)
|
|
||||||
"Send PROMPT to Claude and display the response.
|
|
||||||
If SYSTEM-PROMPT is provided, include it in the request.
|
|
||||||
If TOOLS is provided, enable tool use."
|
|
||||||
(let ((data `((model . ,claude-model)
|
|
||||||
(max_tokens . ,claude-max-tokens)
|
|
||||||
(messages . [((role . "user")
|
|
||||||
(content . ,prompt))]))))
|
|
||||||
|
|
||||||
;; Add system prompt if provided
|
|
||||||
(when system-prompt
|
|
||||||
(setq data (append data `((system . ,system-prompt)))))
|
|
||||||
|
|
||||||
;; Add tools if provided
|
|
||||||
(when tools
|
|
||||||
(setq data (append data `((tools . ,tools)
|
|
||||||
(tool_choice . "auto")))))
|
|
||||||
|
|
||||||
;; Display the buffer with a loading message
|
|
||||||
(let ((buffer (claude-ensure-buffer)))
|
|
||||||
(with-current-buffer buffer
|
(with-current-buffer buffer
|
||||||
(let ((inhibit-read-only t))
|
(let ((inhibit-read-only t))
|
||||||
(erase-buffer)
|
(save-excursion
|
||||||
(insert "Sending request to Claude...\n\n")))
|
(goto-char claude-loading-indicator-point)
|
||||||
(when claude-auto-display-results
|
(forward-line -3) ;; Go to the start of the loading message
|
||||||
(display-buffer buffer)))
|
(delete-region (point) claude-loading-indicator-point)))))))
|
||||||
|
|
||||||
;; Send request
|
(defun claude-process-response (data)
|
||||||
|
"Process and display the response DATA from Claude."
|
||||||
|
(let ((content (cdr (assoc 'content data))))
|
||||||
|
(claude-remove-loading-indicator)
|
||||||
|
|
||||||
|
(if (and content (arrayp content) (> (length content) 0))
|
||||||
|
(let ((response-text ""))
|
||||||
|
;; Collect text content from all response parts
|
||||||
|
(dotimes (i (length content))
|
||||||
|
(let* ((item (aref content i))
|
||||||
|
(type (cdr (assoc 'type item))))
|
||||||
|
(when (string= type "text")
|
||||||
|
(let ((text (cdr (assoc 'text item))))
|
||||||
|
(setq response-text (concat response-text text))))))
|
||||||
|
|
||||||
|
;; Add to conversation history
|
||||||
|
(claude-append-to-conversation "assistant" response-text)
|
||||||
|
|
||||||
|
;; Display the response
|
||||||
|
(claude-display-assistant-message response-text))
|
||||||
|
|
||||||
|
;; Handle unexpected response format
|
||||||
|
(let ((error-message "Received an unexpected response format from Claude API."))
|
||||||
|
(claude-display-assistant-message
|
||||||
|
(concat error-message "\n\nResponse data: " (prin1-to-string data))))))
|
||||||
|
|
||||||
|
;; Move to input area
|
||||||
|
(with-current-buffer (claude-ensure-conversation-buffer)
|
||||||
|
(goto-char (point-max))))
|
||||||
|
|
||||||
|
(defun claude-get-context-content ()
|
||||||
|
"Get the content from all context buffers."
|
||||||
|
(let ((context-content ""))
|
||||||
|
(dolist (buffer claude-context-buffers)
|
||||||
|
(when (buffer-live-p buffer)
|
||||||
|
(with-current-buffer buffer
|
||||||
|
(setq context-content
|
||||||
|
(concat context-content
|
||||||
|
(format "\n\nContent from buffer '%s':\n\n%s"
|
||||||
|
(buffer-name buffer)
|
||||||
|
(buffer-substring-no-properties (point-min) (point-max))))))))
|
||||||
|
context-content))
|
||||||
|
|
||||||
|
(defun claude-send-message (prompt)
|
||||||
|
"Send PROMPT to Claude and display the response in the conversation buffer."
|
||||||
|
;; Add user message to history
|
||||||
|
(claude-append-to-conversation "user" prompt)
|
||||||
|
|
||||||
|
;; Display the user's message
|
||||||
|
(claude-display-user-message prompt)
|
||||||
|
|
||||||
|
;; Show loading indicator
|
||||||
|
(claude-display-loading-indicator)
|
||||||
|
|
||||||
|
;; Prepare messages for the API
|
||||||
|
(let* ((context (claude-get-context-content))
|
||||||
|
(final-prompt (if (string-empty-p context)
|
||||||
|
prompt
|
||||||
|
(concat prompt "\n\n" context)))
|
||||||
|
(messages-array (vconcat
|
||||||
|
(apply #'vector
|
||||||
|
(mapcar (lambda (msg)
|
||||||
|
`((role . ,(cdr (assoc 'role msg)))
|
||||||
|
(content . ,(cdr (assoc 'content msg)))))
|
||||||
|
claude-conversation-history))))
|
||||||
|
(data `((model . ,claude-model)
|
||||||
|
(max_tokens . ,claude-max-tokens)
|
||||||
|
(messages . ,messages-array))))
|
||||||
|
|
||||||
|
;; Add tools if enabled
|
||||||
|
(when claude-tools-enabled
|
||||||
|
(setq data (append data `((tools . ,(mapcar (lambda (tool)
|
||||||
|
`((name . ,(plist-get tool :name))
|
||||||
|
(description . ,(plist-get tool :description))
|
||||||
|
(parameters . ,(plist-get tool :parameters))))
|
||||||
|
claude-tools))
|
||||||
|
(tool_choice . "auto")))))
|
||||||
|
|
||||||
|
;; Send the request
|
||||||
(claude-send-request
|
(claude-send-request
|
||||||
data
|
data
|
||||||
(lambda (data)
|
(lambda (data)
|
||||||
(if tools
|
(if (and claude-tools-enabled
|
||||||
(claude-handle-tool-response data (claude-ensure-buffer))
|
(or (assoc 'tool_use (aref (cdr (assoc 'content (aref (cdr (assoc 'messages data)) 0))) 0))
|
||||||
(claude-display-response data)))
|
(assoc 'tool_result (aref (cdr (assoc 'content (aref (cdr (assoc 'messages data)) 0))) 0))))
|
||||||
|
(claude-handle-tool-response data (claude-ensure-conversation-buffer))
|
||||||
|
(claude-process-response data)))
|
||||||
(lambda (error-thrown)
|
(lambda (error-thrown)
|
||||||
(let ((buffer (claude-ensure-buffer)))
|
(claude-remove-loading-indicator)
|
||||||
(with-current-buffer buffer
|
(claude-display-assistant-message (format "Error: %s" error-thrown))))))
|
||||||
(let ((inhibit-read-only t))
|
|
||||||
(erase-buffer)
|
|
||||||
(insert (format "Error: %s" error-thrown)))))))))
|
|
||||||
|
|
||||||
;;; Interactive commands:
|
(defun claude-send-input ()
|
||||||
|
"Send the current input to Claude."
|
||||||
(defun claude-send-region (start end)
|
|
||||||
"Send the region between START and END to Claude and display the response."
|
|
||||||
(interactive "r")
|
|
||||||
(let ((prompt (buffer-substring-no-properties start end)))
|
|
||||||
(claude-send-message prompt)))
|
|
||||||
|
|
||||||
(defun claude-send-buffer ()
|
|
||||||
"Send the entire buffer to Claude."
|
|
||||||
(interactive)
|
(interactive)
|
||||||
(claude-send-region (point-min) (point-max)))
|
(let ((buffer (claude-ensure-conversation-buffer)))
|
||||||
|
(with-current-buffer buffer
|
||||||
|
(let ((input (buffer-substring-no-properties claude-input-marker (point-max))))
|
||||||
|
(when (not (string-empty-p (string-trim input)))
|
||||||
|
;; Delete the input text
|
||||||
|
(let ((inhibit-read-only t))
|
||||||
|
(delete-region claude-input-marker (point-max)))
|
||||||
|
;; Send the message
|
||||||
|
(claude-send-message (string-trim input)))))))
|
||||||
|
|
||||||
(defun claude-code-review ()
|
(defun claude-cancel-input ()
|
||||||
"Ask Claude to review the code in the current buffer."
|
"Cancel the current input."
|
||||||
(interactive)
|
(interactive)
|
||||||
(let ((code (buffer-substring-no-properties (point-min) (point-max))))
|
(let ((buffer (claude-ensure-conversation-buffer)))
|
||||||
(claude-send-message
|
(with-current-buffer buffer
|
||||||
(concat "Please review the following code and suggest improvements:\n\n```\n"
|
(let ((inhibit-read-only t))
|
||||||
code "\n```"))))
|
(delete-region claude-input-marker (point-max))))))
|
||||||
|
|
||||||
(defun claude-explain-code ()
|
(defun claude-new-conversation ()
|
||||||
"Ask Claude to explain the selected code."
|
"Start a new conversation with Claude."
|
||||||
(interactive)
|
(interactive)
|
||||||
(if (use-region-p)
|
(setq claude-conversation-history nil)
|
||||||
(let ((code (buffer-substring-no-properties (region-beginning) (region-end))))
|
(setq claude-context-buffers nil)
|
||||||
(claude-send-message
|
(let ((buffer (claude-ensure-conversation-buffer)))
|
||||||
(concat "Please explain what this code does in detail:\n\n```\n"
|
(with-current-buffer buffer
|
||||||
code "\n```")))
|
(claude-insert-conversation-header)))
|
||||||
(message "No region selected")))
|
(message "Started a new conversation with Claude"))
|
||||||
|
|
||||||
(defun claude-complete-code ()
|
(defun claude-add-context-buffer (buffer)
|
||||||
"Ask Claude to complete the code at point."
|
"Add BUFFER to the list of context buffers."
|
||||||
|
(interactive (list (read-buffer "Add buffer as context: " (other-buffer (current-buffer) t))))
|
||||||
|
(let ((buf (get-buffer buffer)))
|
||||||
|
(when buf
|
||||||
|
(unless (memq buf claude-context-buffers)
|
||||||
|
(add-to-list 'claude-context-buffers buf)
|
||||||
|
(message "Added %s to context buffers" (buffer-name buf))))))
|
||||||
|
|
||||||
|
(defun claude-remove-context-buffer (buffer)
|
||||||
|
"Remove BUFFER from the list of context buffers."
|
||||||
|
(interactive
|
||||||
|
(list
|
||||||
|
(when claude-context-buffers
|
||||||
|
(let ((buffer-names (mapcar #'buffer-name claude-context-buffers)))
|
||||||
|
(completing-read "Remove buffer from context: " buffer-names nil t)))))
|
||||||
|
(when buffer
|
||||||
|
(let ((buf (get-buffer buffer)))
|
||||||
|
(when buf
|
||||||
|
(setq claude-context-buffers (delq buf claude-context-buffers))
|
||||||
|
(message "Removed %s from context buffers" (buffer-name buf))))))
|
||||||
|
|
||||||
|
(defun claude-list-context-buffers ()
|
||||||
|
"List all context buffers in the minibuffer."
|
||||||
(interactive)
|
(interactive)
|
||||||
(let* ((buffer-text (buffer-substring-no-properties (point-min) (point-max)))
|
(if claude-context-buffers
|
||||||
(cursor-pos (point))
|
(let ((buffer-list (mapconcat #'buffer-name claude-context-buffers ", ")))
|
||||||
(text-before (buffer-substring-no-properties (point-min) cursor-pos))
|
(message "Context buffers: %s" buffer-list))
|
||||||
(text-after (buffer-substring-no-properties cursor-pos (point-max))))
|
(message "No context buffers set")))
|
||||||
(claude-send-message
|
|
||||||
(concat "I'm writing code and need you to continue it from where the cursor is marked with [CURSOR]. Only provide the code that should replace [CURSOR], nothing else.\n\n```\n"
|
|
||||||
text-before "[CURSOR]" text-after "\n```"))))
|
|
||||||
|
|
||||||
(defun claude-prompt-and-send ()
|
(defun claude-toggle-tool-use ()
|
||||||
"Prompt for input and send to Claude."
|
"Toggle the use of tools in the conversation."
|
||||||
(interactive)
|
(interactive)
|
||||||
(let ((prompt (read-string "Ask Claude: ")))
|
(setq claude-tools-enabled (not claude-tools-enabled))
|
||||||
(claude-send-message prompt)))
|
(message "Claude tool use %s" (if claude-tools-enabled "enabled" "disabled")))
|
||||||
|
|
||||||
(defun claude-refresh-last-request ()
|
|
||||||
"Refresh the last Claude request by sending it again."
|
|
||||||
(interactive)
|
|
||||||
(if claude-last-request
|
|
||||||
(let ((prompt (plist-get claude-last-request :prompt))
|
|
||||||
(system-prompt (plist-get claude-last-request :system-prompt))
|
|
||||||
(tools (plist-get claude-last-request :tools)))
|
|
||||||
|
|
||||||
;; Display refreshing message
|
|
||||||
(let ((buffer (claude-ensure-buffer)))
|
|
||||||
(with-current-buffer buffer
|
|
||||||
(let ((inhibit-read-only t))
|
|
||||||
(erase-buffer)
|
|
||||||
(insert "Refreshing last request to Claude...\n\n"))))
|
|
||||||
|
|
||||||
;; Re-send the same request
|
|
||||||
(claude-send-message prompt system-prompt tools)
|
|
||||||
(message "Refreshing Claude request..."))
|
|
||||||
(message "No previous Claude request to refresh")))
|
|
||||||
|
|
||||||
;;; Tool use functionality:
|
;;; Tool use functionality:
|
||||||
|
|
||||||
|
|
@ -300,95 +440,113 @@ The handler will be called with the parameters passed by Claude."
|
||||||
(funcall handler parameters)
|
(funcall handler parameters)
|
||||||
(format "Error: No handler registered for tool %s" tool-name))))
|
(format "Error: No handler registered for tool %s" tool-name))))
|
||||||
|
|
||||||
(defun claude-send-with-tools (prompt)
|
|
||||||
"Send PROMPT to Claude with tools enabled and handle the response."
|
|
||||||
(interactive "sPrompt: ")
|
|
||||||
(let ((tools-json (mapcar (lambda (tool)
|
|
||||||
`((name . ,(plist-get tool :name))
|
|
||||||
(description . ,(plist-get tool :description))
|
|
||||||
(parameters . ,(plist-get tool :parameters))))
|
|
||||||
claude-tools)))
|
|
||||||
(claude-send-message prompt nil tools-json)))
|
|
||||||
|
|
||||||
(defun claude-handle-tool-response (data buffer)
|
(defun claude-handle-tool-response (data buffer)
|
||||||
"Handle response DATA from Claude that may contain tool calls.
|
"Handle response DATA from Claude that may contain tool calls.
|
||||||
Display results in BUFFER."
|
Display results in BUFFER."
|
||||||
(let ((message (aref (cdr (assoc 'messages data)) 0)))
|
(let ((message (aref (cdr (assoc 'messages data)) 0)))
|
||||||
|
(claude-remove-loading-indicator)
|
||||||
(with-current-buffer buffer
|
(with-current-buffer buffer
|
||||||
(let ((inhibit-read-only t))
|
(let ((content-items (cdr (assoc 'content message)))
|
||||||
(erase-buffer)
|
(tool-usage-text "")
|
||||||
(let ((content-items (cdr (assoc 'content message))))
|
(response-text ""))
|
||||||
(dolist (item content-items)
|
|
||||||
(let ((type (cdr (assoc 'type item))))
|
|
||||||
(cond
|
|
||||||
((string= type "text")
|
|
||||||
(insert (cdr (assoc 'text item)) "\n\n"))
|
|
||||||
((string= type "tool_use")
|
|
||||||
(let* ((tool-use (cdr (assoc 'tool_use item)))
|
|
||||||
(tool-name (cdr (assoc 'name tool-use)))
|
|
||||||
(parameters (cdr (assoc 'parameters tool-use)))
|
|
||||||
(handler (gethash tool-name claude-tool-handlers)))
|
|
||||||
|
|
||||||
;; Check if we have this tool
|
;; Process all content items
|
||||||
(if handler
|
(dotimes (i (length content-items))
|
||||||
(let ((tool-result (claude-execute-tool tool-name parameters)))
|
(let* ((item (aref content-items i))
|
||||||
(insert (format "Tool call: %s\n" tool-name))
|
(type (cdr (assoc 'type item))))
|
||||||
(insert (format "Parameters: %s\n" (json-encode parameters)))
|
(cond
|
||||||
(insert (format "Result: %s\n\n" tool-result))
|
((string= type "text")
|
||||||
|
(setq response-text
|
||||||
|
(concat response-text (cdr (assoc 'text item)) "\n\n")))
|
||||||
|
|
||||||
;; Send the tool result back to Claude
|
((string= type "tool_use")
|
||||||
(claude-send-tool-result data tool-name tool-result))
|
(let* ((tool-use (cdr (assoc 'tool_use item)))
|
||||||
|
(tool-name (cdr (assoc 'name tool-use)))
|
||||||
|
(parameters (cdr (assoc 'parameters tool-use)))
|
||||||
|
(handler (gethash tool-name claude-tool-handlers)))
|
||||||
|
|
||||||
;; Tool not found - record the request and notify
|
;; Check if we have this tool
|
||||||
(let ((description (format "Tool requested by Claude for task: %s"
|
(if handler
|
||||||
(or (cdr (assoc 'description tool-use))
|
(let ((tool-result (claude-execute-tool tool-name parameters)))
|
||||||
"No description provided")))
|
(setq tool-usage-text
|
||||||
(param-structure (or (cdr (assoc 'parameter_structure tool-use))
|
(concat tool-usage-text
|
||||||
parameters)))
|
(format "Tool call: %s\n" tool-name)
|
||||||
|
(format "Parameters: %s\n" (json-encode parameters))
|
||||||
|
(format "Result: %s\n\n" tool-result)))
|
||||||
|
|
||||||
;; Record the tool request
|
;; Send the tool result back to Claude
|
||||||
(claude-request-tool tool-name description param-structure)
|
(claude-send-tool-result data tool-name tool-result))
|
||||||
|
|
||||||
;; Notify Claude about missing tool
|
;; Tool not found - record the request and notify
|
||||||
(insert (format "Tool requested: %s\n" tool-name))
|
(let ((description (format "Tool requested by Claude for task: %s"
|
||||||
(insert (format "This tool is not currently available.\n"))
|
(or (cdr (assoc 'description tool-use))
|
||||||
(insert "The request has been recorded. You can implement it with M-x claude-list-requested-tools.\n\n")
|
"No description provided")))
|
||||||
|
(param-structure (or (cdr (assoc 'parameter_structure tool-use))
|
||||||
|
parameters)))
|
||||||
|
|
||||||
;; Send error message back to Claude
|
;; Record the tool request
|
||||||
(let ((error-msg (format "The requested tool '%s' is not currently available. Would you like me to suggest an alternative approach?" tool-name)))
|
(claude-request-tool tool-name description param-structure)
|
||||||
(claude-send-tool-result data tool-name error-msg))))))))))))))
|
|
||||||
|
;; Add to response text
|
||||||
|
(setq tool-usage-text
|
||||||
|
(concat tool-usage-text
|
||||||
|
(format "Tool requested: %s\n" tool-name)
|
||||||
|
"This tool is not currently available.\n"
|
||||||
|
"The request has been recorded. You can implement it with M-x claude-list-requested-tools.\n\n"))
|
||||||
|
|
||||||
|
;; Send error message back to Claude
|
||||||
|
(let ((error-msg (format "The requested tool '%s' is not currently available. Would you like me to suggest an alternative approach?" tool-name)))
|
||||||
|
(claude-send-tool-result data tool-name error-msg))))))))))
|
||||||
|
|
||||||
|
;; Display the combined message
|
||||||
|
(let ((full-response (concat response-text
|
||||||
|
(if (not (string-empty-p tool-usage-text))
|
||||||
|
(concat "\n## Tool Usage\n\n" tool-usage-text)
|
||||||
|
""))))
|
||||||
|
(claude-append-to-conversation "assistant" full-response)
|
||||||
|
(claude-display-assistant-message full-response)))))
|
||||||
|
|
||||||
(defun claude-send-tool-result (data tool-name tool-result)
|
(defun claude-send-tool-result (data tool-name tool-result)
|
||||||
"Send TOOL-RESULT for TOOL-NAME back to Claude based on the original DATA."
|
"Send TOOL-RESULT for TOOL-NAME back to Claude based on the original DATA."
|
||||||
(let ((message-id (cdr (assoc 'id (aref (cdr (assoc 'messages data)) 0))))
|
(let ((message-id (cdr (assoc 'id (aref (cdr (assoc 'messages data)) 0))))
|
||||||
(tool-call-id (cdr (assoc 'id (cdr (assoc 'tool_use (aref (cdr (assoc 'content (aref (cdr (assoc 'messages data)) 0))) 0))))))
|
(tool-call-id nil)
|
||||||
(api-key (claude-get-api-key))
|
(api-key (claude-get-api-key))
|
||||||
(buffer (claude-ensure-buffer)))
|
(buffer (claude-ensure-conversation-buffer)))
|
||||||
|
|
||||||
(request
|
;; Find the tool call ID
|
||||||
"https://api.anthropic.com/v1/messages"
|
(let* ((content-items (cdr (assoc 'content (aref (cdr (assoc 'messages data)) 0)))))
|
||||||
:type "POST"
|
(dotimes (i (length content-items))
|
||||||
:headers `(("Content-Type" . "application/json")
|
(let* ((item (aref content-items i))
|
||||||
("x-api-key" . ,api-key)
|
(type (cdr (assoc 'type item))))
|
||||||
("anthropic-version" . "2023-06-01"))
|
(when (string= type "tool_use")
|
||||||
:data (json-encode
|
(setq tool-call-id (cdr (assoc 'id (cdr (assoc 'tool_use item)))))))))
|
||||||
`((model . ,claude-model)
|
|
||||||
(max_tokens . ,claude-max-tokens)
|
(when tool-call-id
|
||||||
(messages . ,(vconcat (cdr (assoc 'messages data))
|
;; Show loading indicator
|
||||||
`[((role . "assistant")
|
(claude-display-loading-indicator)
|
||||||
(content . [((type . "tool_result")
|
|
||||||
(tool_result . ((tool_call_id . ,tool-call-id)
|
(request
|
||||||
(content . ,tool-result))))]))]))))
|
"https://api.anthropic.com/v1/messages"
|
||||||
:parser 'json-read
|
:type "POST"
|
||||||
:success (cl-function
|
:headers `(("Content-Type" . "application/json")
|
||||||
(lambda (&key data &allow-other-keys)
|
("x-api-key" . ,api-key)
|
||||||
(claude-handle-tool-response data buffer)))
|
("anthropic-version" . "2023-06-01"))
|
||||||
:error (cl-function
|
:data (json-encode
|
||||||
(lambda (&key error-thrown &allow-other-keys)
|
`((model . ,claude-model)
|
||||||
(with-current-buffer buffer
|
(max_tokens . ,claude-max-tokens)
|
||||||
(let ((inhibit-read-only t))
|
(messages . ,(vconcat (cdr (assoc 'messages data))
|
||||||
(erase-buffer)
|
`[((role . "assistant")
|
||||||
(insert (format "Error: %s" error-thrown)))))))))
|
(content . [((type . "tool_result")
|
||||||
|
(tool_result . ((tool_call_id . ,tool-call-id)
|
||||||
|
(content . ,tool-result))))]))]))))
|
||||||
|
:parser 'json-read
|
||||||
|
:success (cl-function
|
||||||
|
(lambda (&key data &allow-other-keys)
|
||||||
|
(claude-handle-tool-response data buffer)))
|
||||||
|
:error (cl-function
|
||||||
|
(lambda (&key error-thrown &allow-other-keys)
|
||||||
|
(claude-remove-loading-indicator)
|
||||||
|
(claude-display-assistant-message (format "Error: %s" error-thrown))))))))
|
||||||
|
|
||||||
(defun claude-request-tool (tool-name description parameters)
|
(defun claude-request-tool (tool-name description parameters)
|
||||||
"Record a request from Claude for a tool that isn't available."
|
"Record a request from Claude for a tool that isn't available."
|
||||||
|
|
@ -635,21 +793,92 @@ Display results in BUFFER."
|
||||||
(claude-clear-tools)
|
(claude-clear-tools)
|
||||||
(claude-register-filesystem-tools)
|
(claude-register-filesystem-tools)
|
||||||
(claude-register-emacs-tools)
|
(claude-register-emacs-tools)
|
||||||
|
;; Uncomment the line below if you want shell tools enabled by default
|
||||||
;; (claude-register-shell-tools)
|
;; (claude-register-shell-tools)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
;;; Interactive commands:
|
||||||
|
|
||||||
|
(defun claude-start-conversation ()
|
||||||
|
"Start or switch to a conversation with Claude."
|
||||||
|
(interactive)
|
||||||
|
(let ((buffer (claude-ensure-conversation-buffer)))
|
||||||
|
(switch-to-buffer buffer)))
|
||||||
|
|
||||||
|
(defun claude-send-region (start end)
|
||||||
|
"Send the region between START and END to Claude in the conversation buffer."
|
||||||
|
(interactive "r")
|
||||||
|
(let ((prompt (buffer-substring-no-properties start end)))
|
||||||
|
(claude-start-conversation)
|
||||||
|
(with-current-buffer (claude-ensure-conversation-buffer)
|
||||||
|
(goto-char claude-input-marker)
|
||||||
|
(insert prompt)
|
||||||
|
(claude-send-input))))
|
||||||
|
|
||||||
|
(defun claude-send-buffer ()
|
||||||
|
"Send the entire buffer to Claude in the conversation buffer."
|
||||||
|
(interactive)
|
||||||
|
(claude-send-region (point-min) (point-max)))
|
||||||
|
|
||||||
|
(defun claude-code-review ()
|
||||||
|
"Ask Claude to review the code in the current buffer."
|
||||||
|
(interactive)
|
||||||
|
(let ((code (buffer-substring-no-properties (point-min) (point-max)))
|
||||||
|
(buffer-name (buffer-name))
|
||||||
|
(buffer (current-buffer)))
|
||||||
|
;; Add the current buffer as context
|
||||||
|
(unless (memq buffer claude-context-buffers)
|
||||||
|
(add-to-list 'claude-context-buffers buffer))
|
||||||
|
;; Start the conversation
|
||||||
|
(claude-start-conversation)
|
||||||
|
(with-current-buffer (claude-ensure-conversation-buffer)
|
||||||
|
(goto-char claude-input-marker)
|
||||||
|
(insert (format "Please review the code in %s and suggest improvements." buffer-name))
|
||||||
|
(claude-send-input))))
|
||||||
|
|
||||||
|
(defun claude-explain-code ()
|
||||||
|
"Ask Claude to explain the selected code or current buffer."
|
||||||
|
(interactive)
|
||||||
|
(let ((buffer (current-buffer)))
|
||||||
|
;; Add the current buffer as context
|
||||||
|
(unless (memq buffer claude-context-buffers)
|
||||||
|
(add-to-list 'claude-context-buffers buffer))
|
||||||
|
;; Start the conversation
|
||||||
|
(claude-start-conversation)
|
||||||
|
(with-current-buffer (claude-ensure-conversation-buffer)
|
||||||
|
(goto-char claude-input-marker)
|
||||||
|
(if (use-region-p)
|
||||||
|
(insert "Please explain the selected code in detail.")
|
||||||
|
(insert (format "Please explain the code in %s in detail." (buffer-name buffer))))
|
||||||
|
(claude-send-input))))
|
||||||
|
|
||||||
|
(defun claude-complete-code ()
|
||||||
|
"Ask Claude to complete the code at point."
|
||||||
|
(interactive)
|
||||||
|
(let* ((buffer (current-buffer))
|
||||||
|
(cursor-pos (point))
|
||||||
|
(buffer-name (buffer-name)))
|
||||||
|
;; Add the current buffer as context
|
||||||
|
(unless (memq buffer claude-context-buffers)
|
||||||
|
(add-to-list 'claude-context-buffers buffer))
|
||||||
|
;; Start the conversation
|
||||||
|
(claude-start-conversation)
|
||||||
|
(with-current-buffer (claude-ensure-conversation-buffer)
|
||||||
|
(goto-char claude-input-marker)
|
||||||
|
(insert (format "Complete the code at the cursor position in %s. The cursor is at position %d."
|
||||||
|
buffer-name cursor-pos))
|
||||||
|
(claude-send-input))))
|
||||||
|
|
||||||
;;; Keybindings:
|
;;; Keybindings:
|
||||||
|
|
||||||
(defvar claude-mode-map
|
(defvar claude-mode-map
|
||||||
(let ((map (make-sparse-keymap)))
|
(let ((map (make-sparse-keymap)))
|
||||||
|
(define-key map (kbd "C-c C-a c") 'claude-start-conversation)
|
||||||
(define-key map (kbd "C-c C-a s") 'claude-send-region)
|
(define-key map (kbd "C-c C-a s") 'claude-send-region)
|
||||||
(define-key map (kbd "C-c C-a b") 'claude-send-buffer)
|
(define-key map (kbd "C-c C-a b") 'claude-send-buffer)
|
||||||
(define-key map (kbd "C-c C-a r") 'claude-code-review)
|
(define-key map (kbd "C-c C-a r") 'claude-code-review)
|
||||||
(define-key map (kbd "C-c C-a e") 'claude-explain-code)
|
(define-key map (kbd "C-c C-a e") 'claude-explain-code)
|
||||||
(define-key map (kbd "C-c C-a c") 'claude-complete-code)
|
(define-key map (kbd "C-c C-a o") 'claude-complete-code)
|
||||||
(define-key map (kbd "C-c C-a t") 'claude-send-with-tools)
|
|
||||||
(define-key map (kbd "C-c C-a l") 'claude-list-requested-tools)
|
|
||||||
(define-key map (kbd "C-c C-a p") 'claude-prompt-and-send)
|
|
||||||
map)
|
map)
|
||||||
"Keymap for Claude mode.")
|
"Keymap for Claude mode.")
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue