;;; ai.el -*- lexical-binding: t; -*- ;;; ============================================================================ ;;; gptel Configuration ;;; ============================================================================ ;; Core gptel setup (use-package! gptel :defer t :config ;; Default backend: OpenRouter (setq gptel-model 'google/gemini-3.flash-preview gptel-backend (gptel-make-openai "OpenRouter" :host "openrouter.ai" :endpoint "/api/v1/chat/completions" :stream t :key "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" :models '(google/gemini-2.5-flash x-ai/grok-4.1-fast x-ai/grok-code-fast-1 google/gemini-3-pro-preview anthropic/claude-sonnet-4.5))) ;; Use org-mode by default for better note-taking (setq gptel-default-mode 'org-mode) ;; Move cursor to end of response automatically (add-hook 'gptel-post-response-functions 'gptel-end-of-response) ;; Keybindings - using SPC A for AI (uppercase to avoid conflicts) (map! :leader (:prefix ("A" . "AI/gptel") :desc "gptel chat" "g" #'gptel :desc "gptel send" "s" #'gptel-send :desc "gptel menu" "m" #'gptel-menu :desc "gptel rewrite" "r" #'gptel-rewrite :desc "Add context" "c" #'gptel-add :desc "Add file to context" "f" #'gptel-add-file)) ;; Additional keybindings in gptel buffer (map! :map gptel-mode-map "C-c RET" #'gptel-send "C-c C-k" #'gptel-abort "C-c C-c" #'gptel-menu)) ;; gptel presets for different tasks (after! gptel ;; Coding assistant preset (gptel-make-preset 'coder :description "Expert coding assistant" :backend "OpenRouter" :model 'anthropic/claude-sonnet-4.5 :system "You are an expert coding assistant. Provide high-quality code solutions, refactorings, and explanations. Be concise but thorough. Include error handling and follow best practices.") ;; Code explainer preset (gptel-make-preset 'explain :description "Explain code to beginners" :system "Explain this code to a novice programmer. Use simple language and break down complex concepts.") ;; Refactoring preset (gptel-make-preset 'refactor :description "Code refactoring expert" :model 'anthropic/claude-sonnet-4.5 :system "You are a code refactoring expert. Improve code quality, readability, and performance while maintaining functionality. Explain your changes.") ;; Quick answers preset (gptel-make-preset 'quick :description "Quick, concise answers" :model 'google/gemini-2.5-flash :system "Provide quick, concise answers. Be direct and to the point.") ;; Documentation writer preset (gptel-make-preset 'docs :description "Documentation writer" :system "Write clear, comprehensive documentation. Include examples and explain edge cases.")) ;;; ============================================================================ ;;; gptel-quick - Quick lookups and explanations ;;; ============================================================================ (use-package! gptel-quick :after gptel :defer t :config ;; Include context from gptel-add if available (setq gptel-quick-use-context t) (map! :leader (:prefix "A" :desc "Quick lookup" "q" #'gptel-quick))) ;;; ============================================================================ ;;; gptel-extensions - Extra utility functions ;;; ============================================================================ (use-package! gptel-extensions :after gptel :defer t :config (map! :leader (:prefix "A" :desc "Load buffer into session" "l" #'gptel-extensions-load-buffer :desc "Send whole buffer" "b" #'gptel-extensions-send-whole-buffer :desc "Refactor region" "R" #'gptel-extensions-refactor))) ;;; ============================================================================ ;;; gptel-autocomplete - Inline code completion ;;; ============================================================================ (use-package! gptel-autocomplete :after gptel :defer t :commands (gptel-complete gptel-accept-completion) :config ;; Configure context size (setq gptel-autocomplete-before-context-lines 100 gptel-autocomplete-after-context-lines 20 gptel-autocomplete-temperature 0.1) ;; Keybindings for autocomplete (map! :leader (:prefix "A" :desc "Complete at point" "C" #'gptel-complete :desc "Accept completion" "a" #'gptel-accept-completion)) ;; Also add convenient keybindings in prog-mode (map! :map prog-mode-map "M-TAB" #'gptel-complete "C-c TAB" #'gptel-complete "C-c C-a" #'gptel-accept-completion)) ;;; ============================================================================ ;;; Optional: Add Ollama for local models (commented out by default) ;;; ============================================================================ ;; Uncomment to add Ollama support for local models ;; (after! gptel ;; (gptel-make-ollama "Ollama" ;; :host "localhost:11434" ;; :stream t ;; :models '(qwen2.5-coder:latest ;; mistral:latest ;; llama3.2:latest))) ;; To make Ollama default, uncomment: ;; (setq gptel-model 'qwen2.5-coder:latest ;; gptel-backend (gptel-make-ollama "Ollama" ;; :host "localhost:11434" ;; :stream t ;; :models '(qwen2.5-coder:latest ;; mistral:latest))) ;;; ============================================================================ ;;; Helper functions ;;; ============================================================================ (defun my/gptel-quick-explain-region () "Quickly explain selected code region." (interactive) (when (use-region-p) (let ((code (buffer-substring-no-properties (region-beginning) (region-end)))) (gptel-request code :system "Explain this code concisely in 2-3 sentences." :callback (lambda (response info) (message "Explanation: %s" response)))))) (defun my/gptel-proofread-region () "Proofread and improve selected text." (interactive) (when (use-region-p) (gptel-rewrite-region (region-beginning) (region-end) "Fix spelling, grammar, and improve clarity. Keep the same tone and style."))) ;; Add keybindings for helper functions (map! :leader (:prefix "A" :desc "Quick explain" "e" #'my/gptel-quick-explain-region :desc "Proofread text" "p" #'my/gptel-proofread-region)) ;;; Claude Code Configuration (use-package! claude-code :defer t :config ;; Unset CLAUDECODE to allow running Claude Code inside Emacs sub-processes (setenv "CLAUDECODE" nil) (setq claude-code-program "ccr" claude-code-program-switches '("code" "--dangerously-skip-permissions")) ;; Use Eat backend (setq claude-code-terminal-backend 'eat ;; Emacs-style editing in Claude buffer: RET inserts newline, S-RET sends claude-code-newline-keybinding-style 'shift-return-to-send) ;; Enable global mode (claude-code-mode 1) ;; Emacs-style prefix key (works without Doom/Evil leader keys) (global-set-key (kbd "C-c c") claude-code-command-map) ;; Set Doom leader keybindings (map! :leader (:prefix ("C" . "claude-code") :desc "Start Claude" "c" #'claude-code :desc "Start in directory" "d" #'claude-code-start-in-directory :desc "Continue conversation" "C" #'claude-code-continue :desc "Resume session" "R" #'claude-code-resume :desc "New instance" "i" #'claude-code-new-instance :desc "Kill Claude" "k" #'claude-code-kill :desc "Kill all instances" "K" #'claude-code-kill-all :desc "Send command" "s" #'claude-code-send-command :desc "Send command with context" "x" #'claude-code-send-command-with-context :desc "Send region/buffer" "r" #'claude-code-send-region :desc "Send buffer file" "o" #'claude-code-send-buffer-file :desc "Fix error at point" "e" #'claude-code-fix-error-at-point :desc "Toggle window" "t" #'claude-code-toggle :desc "Switch to buffer" "b" #'claude-code-switch-to-buffer :desc "Select buffer" "B" #'claude-code-select-buffer :desc "Toggle read-only" "z" #'claude-code-toggle-read-only-mode :desc "Cycle mode" "M" #'claude-code-cycle-mode :desc "Transient menu" "m" #'claude-code-transient :desc "Slash commands" "/" #'claude-code-slash-commands :desc "Fork conversation" "f" #'claude-code-fork :desc "Send return" "y" #'claude-code-send-return :desc "Send escape" "n" #'claude-code-send-escape :desc "Send 1" "1" #'claude-code-send-1 :desc "Send 2" "2" #'claude-code-send-2 :desc "Send 3" "3" #'claude-code-send-3)) ;; Optional: Configure window display (side window on right) (add-to-list 'display-buffer-alist '("^\\*claude" (display-buffer-in-side-window) (side . right) (window-width . 90))) ;; Optional: Enable auto-revert for files modified by Claude (global-auto-revert-mode 1) ;; Optional: Increase vterm scrollback for long conversations (add-hook 'claude-code-start-hook (lambda () (when (eq claude-code-terminal-backend 'vterm) (setq-local vterm-max-scrollback 100000))))) ;;; Gemini CLI Configuration (use-package! gemini-cli :defer t :config ;; Set the Gemini executable (setq gemini-cli-program "gemini" gemini-cli-program-switches nil) ;; Use Eat backend (setq gemini-cli-terminal-backend 'eat ;; Emacs-style editing in Gemini buffer: RET inserts newline, S-RET sends gemini-cli-newline-keybinding-style 'shift-return-to-send) ;; Enable global mode (gemini-cli-mode 1) ;; Emacs-style prefix key (global-set-key (kbd "C-c g") gemini-cli-command-map) ;; Set Doom leader keybindings under SPC G (map! :leader (:prefix ("G" . "gemini-cli") :desc "Start Gemini" "c" #'gemini-cli :desc "Start in directory" "d" #'gemini-cli-start-in-directory :desc "Continue conversation" "C" #'gemini-cli-continue :desc "Resume session" "R" #'gemini-cli-resume :desc "New instance" "i" #'gemini-cli-new-instance :desc "Kill Gemini" "k" #'gemini-cli-kill :desc "Kill all instances" "K" #'gemini-cli-kill-all :desc "Send command" "s" #'gemini-cli-send-command :desc "Send command with context" "x" #'gemini-cli-send-command-with-context :desc "Send region/buffer" "r" #'gemini-cli-send-region :desc "Send buffer file" "o" #'gemini-cli-send-buffer-file :desc "Fix error at point" "e" #'gemini-cli-fix-error-at-point :desc "Toggle window" "t" #'gemini-cli-toggle :desc "Switch to buffer" "b" #'gemini-cli-switch-to-buffer :desc "Select buffer" "B" #'gemini-cli-select-buffer :desc "Toggle read-only" "z" #'gemini-cli-toggle-read-only-mode :desc "Cycle mode" "M" #'gemini-cli-cycle-mode :desc "Transient menu" "m" #'gemini-cli-transient :desc "Slash commands" "/" #'gemini-cli-slash-commands :desc "Fork conversation" "f" #'gemini-cli-fork :desc "Send return" "y" #'gemini-cli-send-return :desc "Send escape" "n" #'gemini-cli-send-escape :desc "Send 1" "1" #'gemini-cli-send-1 :desc "Send 2" "2" #'gemini-cli-send-2 :desc "Send 3" "3" #'gemini-cli-send-3)) ;; Optional: Configure window display (side window on right) (add-to-list 'display-buffer-alist '("^\\*gemini" (display-buffer-in-side-window) (side . right) (window-width . 90))) ;; Optional: Enable auto-revert for files modified by Gemini ;; (global-auto-revert-mode 1) ;; Optional: Increase vterm scrollback for long conversations (add-hook 'gemini-cli-start-hook (lambda () (when (eq gemini-cli-terminal-backend 'vterm) (setq-local vterm-max-scrollback 100000))))) ;; (use-package! claudemacs ;; :defer t ;; :commands (claudemacs claudemacs-transient-menu) ;; :init ;; (setq claudemacs-program "ccr" ;; claudemacs-program-switches '("code" "--dangerously-skip-permissions") ;; claudemacs-default-tool 'claude ;; claudemacs-prefer-projectile-root t) ;; :config ;; (map! :map prog-mode-map ;; "C-c C-e" #'claudemacs-transient-menu) ;; (map! :map emacs-lisp-mode-map ;; "C-c C-e" #'claudemacs-transient-menu) ;; (map! :map text-mode-map ;; "C-c C-e" #'claudemacs-transient-menu) ;; (after! python ;; (map! :map python-base-mode-map ;; "C-c C-e" #'claudemacs-transient-menu)) ;; (after! eat ;; (setq eat-term-scrollback-size 400000) ;; (add-hook 'eat-mode-hook ;; (lambda () ;; (setq-local show-trailing-whitespace nil)))) ;; (map! :leader ;; (:prefix ("C" . "claude-code") ;; :desc "Claudemacs menu" "E" #'claudemacs-transient-menu)))