From 638c29b1be81bb2e306abd10a2f58b7eb007cfa8 Mon Sep 17 00:00:00 2001 From: kj Date: Thu, 27 Aug 2026 11:26:27 -0300 Subject: [PATCH] refactor(configs): remove gptel-agent configuration and tools --- configs/init-ai.el | 425 --------------------------------------------- 1 file changed, 425 deletions(-) diff --git a/configs/init-ai.el b/configs/init-ai.el index 0fc742e..3162a47 100644 --- a/configs/init-ai.el +++ b/configs/init-ai.el @@ -179,431 +179,6 @@ content do not break gptel-magit." :map gptel-autocomplete-completion-map ("C-" . gptel-accept-completion))) -(use-package gptel-agent - :ensure t - :defer nil - :init - (defvar kj-gptel-agents-dir - (expand-file-name "configs/gptel-agents" user-emacs-directory) - "Directory with the agents and prompts ported from opencode for gptel-agent.") - - (defcustom kj-gptel-opencode-small-model nil - "Model for session titles/summaries (must exist in the active backend). -nil uses the current session's model." - :type '(choice (const :tag "Current session model" nil) string) - :group 'gptel) - - (defcustom kj-gptel-plans-dir - (expand-file-name "gptel-plans" user-emacs-directory) - "Directory where gptel-agent plans are stored, outside the project. -Plans are named ----.md, where is a -truncated md5 of the project root, so plans never pollute the project -directory and can be mapped back to their project." - :type 'directory - :group 'gptel) - (defun kj-gptel-load-agents-md () - "Add AGENTS.md rules to the current gptel buffer context. -Loads AGENTS.md from the project root and from `user-emacs-directory', -if they exist." - (interactive) - (require 'gptel-context nil t) - (let ((root (or (when-let* ((proj (project-current))) (project-root proj)) - (vc-root-dir) - default-directory)) - (seen '())) - (dolist (f (list (expand-file-name "AGENTS.md" root) - (expand-file-name "AGENTS.md" user-emacs-directory))) - (when (and (file-readable-p f) (not (member f seen))) - (push f seen) - (condition-case nil - (gptel-context-add-file f) - (error (message "kj-gptel: could not add AGENTS.md %s" f))))))) - - (defun kj-gptel--agent-buffer-setup () - "Load AGENTS.md rules when a gptel-agent session buffer is created. -Fires for new sessions (buffer *gptel-agent:*) and for sessions -restored from a saved file (gptel--preset of the agent)." - (when (and (bound-and-true-p gptel-mode) - (or (string-prefix-p "*gptel-agent" (buffer-name)) - (and (boundp 'gptel--preset) - (memq gptel--preset '(gptel-agent gptel-plan))))) - (kj-gptel-load-agents-md))) - (defun kj-gptel--header-button-action () - "Return the action function of the `[Agent]' / `[Plan]' header button, or nil. -Invoking it is exactly what clicking the button does. The button lives in -gptel-agent's header, rendered as (:eval (funcall DISPLAY))." - (let* ((elt (and (listp header-line-format) (car header-line-format))) - (expr (and (listp elt) (eq (car elt) :eval) (cadr elt))) - (fn (and (listp expr) (eq (car expr) 'funcall) (cadr expr))) - (str (and (functionp fn) (funcall fn)))) - (or (and (stringp str) - (catch 'found - (dotimes (i (length str)) - (let ((action (get-text-property i 'action str))) - (when (functionp action) - (throw 'found action)))))) - ;; Fallback: render vía `format-mode-line' - (let ((str2 (ignore-errors (format-mode-line header-line-format)))) - (and (stringp str2) - (catch 'found - (dotimes (i (length str2)) - (let ((action (get-text-property i 'action str2))) - (when (functionp action) - (throw 'found action)))))))))) - - (defun kj-gptel-toggle-plan-agent (&optional _data) - "Toggle between the default agent (build) and plan mode. -Equivalent to clicking the header button: it invokes the same action -that gptel-agent uses for that button. DATA is ignored (compatibility -with `buttonize')." - (interactive) - (unless (bound-and-true-p gptel-mode) - (user-error "You are not in a gptel session")) - (let ((action (kj-gptel--header-button-action))) - (if action - (funcall action nil) ; exactamente lo que hace el clic - ;; Sin header disponible: aplicamos directamente el toggle - (gptel--apply-preset - (if (eq gptel--preset 'gptel-plan) 'gptel-agent 'gptel-plan) - (lambda (sym val) (set (make-local-variable sym) val))))) - (message "Mode %s activated" (if (eq gptel--preset 'gptel-plan) "plan" "build"))) - - (defun kj-gptel--eglot-start () - "Start eglot immediately for the current buffer if it is not managed. -Unlike `eglot-ensure', this connects right away instead of waiting for a -`post-command-hook', so gptel tools can use LSP on their first call." - (require 'eglot) - (unless eglot--managed-mode - (condition-case err - (apply #'eglot--connect (eglot--guess-contact)) - (error (message "kj-gptel: error starting eglot: %s" - (error-message-string err)))))) - - (defun kj-gptel--lsp-buffer (&optional path) - "Return a buffer with a live eglot server for PATH. -When PATH is nil, return any buffer visiting a file with a live server, -preferring one in the current project." - (if path - (let ((buf (or (find-buffer-visiting path) - (and (file-exists-p path) (find-file-noselect path))))) - (when buf - (with-current-buffer buf - (unless (eglot-current-server) - (kj-gptel--eglot-start)) - buf))) - (let* ((root (and-let* ((proj (project-current))) (project-root proj))) - (in-project - (cl-find-if - (lambda (b) - (with-current-buffer b - (and (eglot-current-server) - (buffer-file-name b) - root - (file-in-directory-p (buffer-file-name b) root)))) - (buffer-list))) - (any - (cl-find-if - (lambda (b) - (with-current-buffer b - (and (eglot-current-server) (buffer-file-name b)))) - (buffer-list)))) - (or in-project any)))) - - (defun kj-gptel-lsp-diagnostics (path) - "Return LSP diagnostics (errors/warnings) for the file at PATH. -Uses eglot and flymake; starts the language server if needed." - (require 'flymake nil t) - (let ((buf (kj-gptel--lsp-buffer path))) - (if (not buf) - (format "Could not open file %s" path) - (with-current-buffer buf - (if (not (eglot-current-server)) - (format "No LSP server for %s (mode %s)." - path major-mode) - (let ((deadline (+ (float-time) 5))) - (while (and (< (float-time) deadline) - (null (flymake-diagnostics (point-min) (point-max)))) - (sit-for 0.2))) - (when (null (flymake-diagnostics (point-min) (point-max))) - ;; Forzar la recolección de diagnósticos del servidor - (ignore-errors (flymake-start)) - (let ((deadline (+ (float-time) 5))) - (while (and (< (float-time) deadline) - (null (flymake-diagnostics (point-min) (point-max)))) - (sit-for 0.2)))) - (let ((diags (flymake-diagnostics (point-min) (point-max)))) - (if (null diags) - (format "No diagnostics for %s." path) - (mapconcat - (lambda (d) - (let ((beg (flymake-diagnostic-beg d)) - (type (flymake-diagnostic-type d))) - (save-excursion - (goto-char beg) - (format "%s:%d:%d [%s] %s" - path - (line-number-at-pos) - (1+ (current-column)) - (or type "unknown") - (flymake-diagnostic-text d))))) - diags "\n")))))))) - - (defun kj-gptel-lsp-symbols (query &optional path) - "Search workspace symbols matching QUERY via LSP (eglot)." - (require 'eglot) - (let ((buf (kj-gptel--lsp-buffer path))) - (if (not buf) - "There is no buffer with a connected LSP server in the project." - (condition-case err - (with-current-buffer buf - (let ((res (eglot--workspace-symbols query))) - (if (null res) - (format "No symbols matching \"%s\"." query) - (mapconcat #'substring-no-properties res "\n")))) - (error (format "Error in the LSP query: %S" err)))))) - - (defun kj-gptel--project-root () - "Return the current project root, or `default-directory'." - (or (when-let* ((proj (project-current))) (project-root proj)) - (vc-root-dir) - default-directory)) - - (defun kj-gptel--read-prompt (name) - "Read the opencode prompt file NAME (e.g. \"title.txt\") from the agents dir." - (string-trim - (with-temp-buffer - (insert-file-contents - (expand-file-name (format "prompts/%s" name) kj-gptel-agents-dir)) - (buffer-string)))) - - (defun kj-gptel--patch-apply (diff root level) - "Apply DIFF in ROOT with `patch -pLEVEL'. Return (EXIT . OUTPUT)." - (let* ((tmp (make-temp-file "gptel-patch-" nil ".diff")) - (default-directory root)) - (with-temp-file tmp (insert diff)) - (let ((out (shell-command-to-string - (format "patch --batch --forward --fuzz=3 -p%d < %s 2>&1; echo PATCH_EXIT=$?" - level tmp)))) - (delete-file tmp) - (if (string-match "PATCH_EXIT=\\([0-9]+\\)" out) - (cons (string-to-number (match-string 1 out)) - (replace-regexp-in-string "PATCH_EXIT=[0-9]+" "" out)) - (cons -1 out))))) - - (defun kj-gptel-apply-patch (diff) - "Apply the unified DIFF to the project and return the result. -Tries `patch -p1' first and falls back to `-p0'." - (let* ((root (kj-gptel--project-root)) - (r1 (kj-gptel--patch-apply diff root 1))) - (if (zerop (car r1)) - (format "Patch applied successfully.\n%s" (cdr r1)) - (let ((r0 (kj-gptel--patch-apply diff root 0))) - (if (zerop (car r0)) - (format "Patch applied successfully (with -p0).\n%s" (cdr r0)) - (format "Could not apply the patch (exit %s).\n%s" - (car r1) (cdr r1))))))) - - (defun kj-gptel-plan-write (filename content) - "Write CONTENT to a plan file for the current project and return status. -Plans are stored in `kj-gptel-plans-dir' (outside the project), named -----.md so that each project keeps its own -plans without adding extra files to the project directory." - (let* ((root (kj-gptel--project-root)) - (hash (substring (md5 root) 0 12)) - (project (replace-regexp-in-string - "[^A-Za-z0-9._-]" "-" - (file-name-nondirectory (directory-file-name root)))) - (name (file-name-nondirectory filename)) - (file (expand-file-name - (format "%s--%s--%s" hash project name) - kj-gptel-plans-dir))) - (make-directory kj-gptel-plans-dir t) - (with-temp-file file (insert content)) - (format "Plan saved to %s" file))) - - (defun kj-gptel-opencode-summary () - "Generate a PR-style summary of the current gptel session (prompt summary.txt)." - (interactive) - (unless (bound-and-true-p gptel-mode) - (user-error "You are not in a gptel session")) - (let* ((buf (current-buffer)) - (transcript (buffer-substring-no-properties (point-min) (point-max))) - (out (get-buffer-create "*opencode-summary*"))) - (with-current-buffer out - (let ((inhibit-read-only t)) (erase-buffer)) - (gptel-mode 1) - (setq-local gptel-backend (with-current-buffer buf gptel-backend) - gptel-model (or kj-gptel-opencode-small-model - (with-current-buffer buf gptel-model)))) - (display-buffer out) - (gptel-request transcript - :buffer out - :stream t - :system (kj-gptel--read-prompt "summary.txt") - :callback (lambda (_info &rest _) - (message "Summary generated in *opencode-summary*"))))) - - (defun kj-gptel-opencode-title () - "Generate a title for the current gptel session (prompt title.txt)." - (interactive) - (unless (bound-and-true-p gptel-mode) - (user-error "You are not in a gptel session")) - (let* ((buf (current-buffer)) - (transcript (buffer-substring-no-properties (point-min) (point-max))) - (tmp (generate-new-buffer " *gptel-title*"))) - (with-current-buffer tmp - (gptel-mode 1) - (setq-local gptel-backend (with-current-buffer buf gptel-backend) - gptel-model (or kj-gptel-opencode-small-model - (with-current-buffer buf gptel-model)))) - (gptel-request transcript - :buffer tmp - :stream nil - :system (kj-gptel--read-prompt "title.txt") - :callback (lambda (_info &rest _) - (let ((title (string-trim - (with-current-buffer tmp (buffer-string))))) - (kill-buffer tmp) - (with-current-buffer buf - (rename-buffer (format "*gptel-agent:%s*" title) t)) - (message "Title: %s" title)))))) - :custom - (kj-gptel-opencode-small-model nil) - :bind - (("C-c g" . gptel-agent) - :map gptel-mode-map - ("C-c C-a" . kj-gptel-toggle-plan-agent)) - :hook (gptel-mode . kj-gptel--agent-buffer-setup) - :config - (add-to-list 'gptel-agent-dirs kj-gptel-agents-dir t) - (setq gptel-agent-compact-prompt - (string-trim - (with-temp-buffer - (insert-file-contents - (expand-file-name "prompts/compaction.txt" kj-gptel-agents-dir)) - (buffer-string)))) - ;; En modo agente, que no pidan confirmación las tools de uso frecuente - ;; (comandos y escritura de archivos); Eval y Agent siguen confirmando. - (dolist (name '("Bash" "Edit" "Write" "Insert" "Mkdir")) - (let ((tool (gptel-get-tool name))) - (when tool - (setf (gptel-tool-confirm tool) nil)))) - - ;; Sobrescribir la tool "Eval" para que sea segura: con `inhibit-interaction', - ;; las llamadas interactivas (read-*, y-or-n-p, call-interactively, ...) - ;; lanzan error en vez de quedarse esperando input y colgar al agente. - (gptel-make-tool - :name "Eval" - :function - (lambda (expression) - (let ((standard-output (generate-new-buffer " *gptel-agent-eval-elisp*")) - (result nil) (output nil)) - (unwind-protect - (condition-case err - (progn - (let ((inhibit-interaction t)) - (setq result (eval (read expression) t))) - (when (> (buffer-size standard-output) 0) - (setq output (with-current-buffer standard-output (buffer-string)))) - (concat - (format "Result:\n%S" result) - (and output (format "\n\nSTDOUT:\n%s" output)))) - ((error user-error) - (concat - (if (eq (car err) 'inhibited-interaction) - "Error: the expression tried to prompt the user for input interactively, which is blocked in the Eval tool." - (format "Error: eval failed with error %S: %S" (car err) (cdr err))) - (and output (format "\n\nSTDOUT:\n%s" output))))) - (kill-buffer standard-output)))) - :description "Evaluate Elisp EXPRESSION and return result and any printed output. - -EXPRESSION can be anything to evaluate. It can be a function call, a -variable, a quasi-quoted expression. The only requirement is that only -the first sexp will be read and evaluated, so if you need to evaluate -multiple expressions, make one call per expression. Do not combine -expressions using progn etc. Just go expression by expression and try -to make standalone single expressions. - -Interactive operations are blocked: if the expression tries to prompt -for input (read-*, y-or-n-p, call-interactively, etc.) it will error -instead of waiting. - -The return value is formatted to a string using %S, so a string will be -returned as an escaped embedded string and literal forms will be -compatible with `read' where possible. Some forms have no printed -representation that can be read and will be represented with -# instead. - -Output from `print', `prin1', and `princ' is captured and returned as STDOUT. -Use `print' for diagnostic output, not `message' (which goes to *Messages* -and is not captured)." - :args '((:name "expression" - :type string - :description "A single elisp sexp to evaluate.")) - :category "gptel-agent" - :confirm t - :include t) - - (gptel-agent-update) - - ;; Herramientas del agente (equivalente a las tools de opencode) - (gptel-make-tool - :name "lsp_diagnostics" - :function #'kj-gptel-lsp-diagnostics - :description "Get the LSP diagnostics (errors and warnings) of a file via the language server (eglot). - -Use it to know if a file has errors after editing it or to check the state of the code. It takes the file path and returns one line per diagnostic in the format path:line:column [type] message. If the LSP server is not connected for that file, it tries to start it." - :args '((:name "path" - :type string - :description "Path of the file to get diagnostics from.")) - :category "gptel-agent" - :include t) - - (gptel-make-tool - :name "lsp_symbols" - :function #'kj-gptel-lsp-symbols - :description "Search workspace symbols (functions, classes, variables, etc.) via LSP (eglot). - -Useful for locating definitions by name or seeing which symbols exist. It takes a query (pattern) and returns the list of matching symbols in the format \"container name Type\"." - :args '((:name "query" - :type string - :description "Pattern to search in the workspace symbols.") - (:name "path" - :type string - :description "Path of a project file whose LSP server to use (optional)." - :optional t)) - :category "gptel-agent" - :include t) - - (gptel-make-tool - :name "apply_patch" - :function #'kj-gptel-apply-patch - :description "Apply a unified diff to the project files. - -Takes a unified diff (git format or --- a/ / +++ b/). It is applied with the patch tool (tries -p1 and then -p0). Use it for large edits or edits that touch multiple files, instead of multiple Edit calls. Modifies project files." - :args '((:name "diff" - :type string - :description "The unified diff to apply, with file headers (--- a/... and +++ b/...).")) - :category "gptel-agent" - :confirm t - :include t) - - (gptel-make-tool - :name "plan_write" - :function #'kj-gptel-plan-write - :description "Save the current plan as a Markdown file in the Emacs directory, outside the project. - -Use it when you have drafted a plan, to persist it to disk and be able to resume it later. Plans are stored in `kj-gptel-plans-dir' (inside the Emacs directory) named ----.md, where is a truncated md5 of the project root, so each project keeps its own plans without adding files to the project directory. Does not modify any project file." - :args '((:name "filename" - :type string - :description "Name of the plan file (e.g. refactor-auth.md).") - (:name "content" - :type string - :description "Markdown content of the plan.")) - :category "gptel-agent" - :confirm t - :include t)) - (use-package macher :ensure (:host github :repo "kmontag/macher") :custom