273 lines
10 KiB
EmacsLisp
273 lines
10 KiB
EmacsLisp
;;; 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)))))
|
|
|
|
;; (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)))
|