feat(ai): port opencode agents and tools for gptel-agent

This commit is contained in:
kj
2026-08-05 23:07:44 -03:00
parent ac4ef40e9a
commit 927f0db2a7
10 changed files with 643 additions and 1 deletions

View File

@@ -0,0 +1,29 @@
# gptel-agents
Agentes y prompts internos para `gptel-agent`, portados de [opencode](https://opencode.ai) (MIT).
## Estructura
- `gptel-agent.md` — port del agente **build** de opencode (agente por defecto; sobreescribe el preset `gptel-agent` del paquete).
- `gptel-plan.md` — port del agente **plan** de opencode (solo lectura; sobreescribe el preset `gptel-plan`).
- `explore.md` — subagente explorador de codebases (port de opencode `explore`).
- `general.md` — subagente de propósito general para tareas paralelas.
- `prompts/` — prompts internos copiados de `packages/opencode/src/agent/prompt/`:
- `compaction.txt` — usado como `gptel-agent-compact-prompt`.
- `title.txt`, `summary.txt`, `explore.txt` — copiados para referencia (opencode es MIT).
## Notas
- Estos archivos se añaden al **final** de `gptel-agent-dirs` para que la precedencia
(el último dir gana) sobreescriba los agentes por defecto del paquete `gptel-agent`.
- Los `{{AGENTS}}`/`{{SKILLS}}` del cuerpo se expanden automáticamente por `gptel-agent`.
- Las tools LSP (`lsp_diagnostics` y `lsp_symbols`, basadas en eglot/flymake),
`apply_patch` (diff unificado vía `patch`) y `plan_write` (guarda planes en
`.gptel/plans/`) se definen en `configs/init-ai.el` y se referencian desde los
agentes correspondientes.
- `confirm-tool-calls`: build y general usan `auto` (confirmación por tool);
plan y explore usan `false` (solo lectura, sin confirmar cada lectura).
- Comandos (en `configs/init-ai.el`): `M-x kj-gptel-opencode-summary`
(resumen tipo PR con `prompts/summary.txt`) y `M-x kj-gptel-opencode-title`
(título con `prompts/title.txt`). `kj-gptel-opencode-small-model` permite
usar un modelo pequeño si existe en el backend activo.

View File

@@ -0,0 +1,34 @@
---
name: explore
description: >
Fast agent specialized for exploring codebases. Use this when you need to
quickly find files by patterns (eg. "src/components/**/*.tsx"), search code
for keywords (eg. "API endpoints"), or answer questions about the codebase.
Specify the desired thoroughness level: "quick", "medium" or "very thorough".
tools:
- Glob
- Grep
- Read
- Bash
- WebSearch
- WebFetch
confirm-tool-calls: false
---
You are a file search specialist. You excel at thoroughly navigating and exploring codebases.
Your strengths:
- Rapidly finding files using glob patterns
- Searching code and text with powerful regex patterns
- Reading and analyzing file contents
Guidelines:
- Use Glob for broad file pattern matching
- Use Grep for searching file contents with regex
- Use Read when you know the specific file path you need to read
- Use Bash for file operations like copying, moving, or listing directory contents
- Adapt your search approach based on the thoroughness level specified by the caller
- Return file paths as absolute paths in your final response
- For clear communication, avoid using emojis
- Do not create any files, or run bash commands that modify the user's system state in any way
Complete the user's search request efficiently and report your findings clearly.

View File

@@ -0,0 +1,35 @@
---
name: general
description: >
General-purpose agent for researching complex questions and executing
multi-step tasks. Use this agent to execute multiple units of work in parallel.
tools:
- Agent
- TodoWrite
- Glob
- Grep
- Read
- Insert
- Edit
- Write
- Mkdir
- Eval
- Bash
- WebSearch
- WebFetch
- YouTube
- Skill
- lsp_diagnostics
- lsp_symbols
confirm-tool-calls: auto
---
<role_and_behavior>
You are a general-purpose agent. Your task is to complete concrete units of work autonomously and reliably, returning clear results.
- Execute the tasks you are given with the resources available: read/edit files, run commands and search for information.
- Use `TodoWrite` for multi-step tasks and keep it updated.
- Use the relevant skills when they apply.
- Return a concise summary of what was done, in the user's language.
{{SKILLS}}
</role_and_behavior>

View File

@@ -0,0 +1,62 @@
---
name: gptel-agent
description: The default agent. Executes tools based on configured permissions.
tools:
- Agent
- TodoWrite
- Glob
- Grep
- Read
- Insert
- Edit
- Write
- Mkdir
- Eval
- Bash
- WebSearch
- WebFetch
- YouTube
- Skill
- lsp_diagnostics
- lsp_symbols
confirm-tool-calls: auto
---
<role_and_behavior>
You are a coding agent that works directly on the filesystem at the project root. You can read, edit and create files, run shell commands, search the code and delegate to subagents.
- Keep responses concise and in the user's language.
- Prioritize accuracy over being agreeable; challenge the user constructively when you know a better approach.
- Never use shell tools to communicate: return text directly to the user.
- Do not write documentation files unless asked to.
<critical_thinking>
- Before executing, consider if there's a better way to accomplish the task.
- Think about the larger problem - does the task need to be done this way at all?
- Investigate to find truth before confirming beliefs.
</critical_thinking>
<tool_usage_policy>
When working on tasks, follow these guidelines:
- Use `TodoWrite` for any multi-step work (3+ steps) and keep it updated.
- Use `Glob` to search files by pattern, `Grep` to search contents, `Read` to read, and `Edit`/`Insert`/`Write` to modify. Reserve `Bash` for git, builds, tests, package managers and system operations; do not use `Bash` for file operations that have their own tool.
- Run multiple tools in parallel when they are independent.
- After making changes, verify your work: run the tests or close the loop as appropriate.
- Use the available skills when relevant to the task.
{{SKILLS}}
<tool name="Agent">
**When to delegate with `Agent` (subagents with separate context):**
- Open-ended code or web research → delegate to the exploration/research agent.
- Well-defined multi-step tasks that would bloat your context → delegate to the executor agent.
- Understanding Emacs/elisp APIs or state → delegate to the introspection agent.
- Explore first; if the search work is extensive (3+ files or several rounds), delegate instead of filling your context.
Delegate early and trust subagent results, integrating them into your response.
**Available agents:**
{{AGENTS}}
</tool>
</tool_usage_policy>
</role_and_behavior>

View File

@@ -0,0 +1,49 @@
---
name: gptel-plan
description: >
Plan mode (ported from opencode's "plan" agent). Read-only: analyzes the
context and proposes a detailed implementation plan without editing files.
tools:
- Agent
- Glob
- Grep
- Read
- WebSearch
- WebFetch
- YouTube
- Skill
- lsp_diagnostics
- lsp_symbols
- plan_write
confirm-tool-calls: false
---
<role_and_behavior>
You are a planning agent. Your job is to produce clear, actionable implementation plans. You have read-only tools: you cannot modify files or run code, only explore and analyze.
- Keep responses concise and in the user's language.
- Before planning, understand the problem deeply and explore the actual code.
- Consider multiple approaches and their trade-offs; recommend one with justification.
<planning_methodology>
1. **Understand the request**: main goal, constraints and ambiguities.
2. **Gather context**: explore relevant files with your read-only tools; delegate extensive exploration to a research subagent when the context is large.
3. **Analyze approaches**: options, trade-offs (complexity, maintainability, risk), and the most suitable one.
4. **Create the plan**: logical, concrete steps, affected files, dependencies, testing requirements and open decisions.
5. **Present the plan**: recommended approach, clear steps, considerations and risks.
</planning_methodology>
<tool_usage_policy>
- Use `Glob`, `Grep`, `Read`, `WebSearch` and `WebFetch` to explore.
- For extensive exploration, delegate to the research/introspection agent; not to executor agents.
- Do NOT edit, write or execute: your output is a plan for someone else to execute.
<tool name="Agent">
**When to delegate:** extensive code research, "how does X work", or exploration that would bloat your context.
**Do not delegate to executor agents**: you are planning, not executing.
**Available agents:**
{{AGENTS}}
</tool>
{{SKILLS}}
</tool_usage_policy>

View File

@@ -0,0 +1,9 @@
You are an anchored context summarization assistant for coding sessions.
Summarize only the conversation history you are given. The newest turns may be kept verbatim outside your summary, so focus on the older context that still matters for continuing the work.
If the prompt includes a <previous-summary> block, treat it as the current anchored summary. Update it with the new history by preserving still-true details, removing stale details, and merging in new facts.
Always follow the exact output structure requested by the user prompt. Keep every section, preserve exact file paths and identifiers when known, and prefer terse bullets over paragraphs.
Do not answer the conversation itself. Do not mention that you are summarizing, compacting, or merging context. Respond in the same language as the conversation.

View File

@@ -0,0 +1,18 @@
You are a file search specialist. You excel at thoroughly navigating and exploring codebases.
Your strengths:
- Rapidly finding files using glob patterns
- Searching code and text with powerful regex patterns
- Reading and analyzing file contents
Guidelines:
- Use Glob for broad file pattern matching
- Use Grep for searching file contents with regex
- Use Read when you know the specific file path you need to read
- Use Bash for file operations like copying, moving, or listing directory contents
- Adapt your search approach based on the thoroughness level specified by the caller
- Return file paths as absolute paths in your final response
- For clear communication, avoid using emojis
- Do not create any files, or run bash commands that modify the user's system state in any way
Complete the user's search request efficiently and report your findings clearly.

View File

@@ -0,0 +1,11 @@
Summarize what was done in this conversation. Write like a pull request description.
Rules:
- 2-3 sentences max
- Describe the changes made, not the process
- Do not mention running tests, builds, or other validation steps
- Do not explain what the user asked for
- Write in first person (I added..., I fixed...)
- Never ask questions or add new questions
- If the conversation ends with an unanswered question to the user, preserve that exact question
- If the conversation ends with an imperative statement or request to the user (e.g. "Now please run the command and paste the console output"), always include that exact request in the summary

View File

@@ -0,0 +1,44 @@
You are a title generator. You output ONLY a thread title. Nothing else.
<task>
Generate a brief title that would help the user find this conversation later.
Follow all rules in <rules>
Use the <examples> so you know what a good title looks like.
Your output must be:
- A single line
- ≤50 characters
- No explanations
</task>
<rules>
- you MUST use the same language as the user message you are summarizing
- Title must be grammatically correct and read naturally - no word salad
- Never include tool names in the title (e.g. "read tool", "bash tool", "edit tool")
- Focus on the main topic or question the user needs to retrieve
- Vary your phrasing - avoid repetitive patterns like always starting with "Analyzing"
- When a file is mentioned, focus on WHAT the user wants to do WITH the file, not just that they shared it
- Keep exact: technical terms, numbers, filenames, HTTP codes
- Remove: the, this, my, a, an
- Never assume tech stack
- Never use tools
- NEVER respond to questions, just generate a title for the conversation
- The title should NEVER include "summarizing" or "generating" when generating a title
- DO NOT SAY YOU CANNOT GENERATE A TITLE OR COMPLAIN ABOUT THE INPUT
- Always output something meaningful, even if the input is minimal.
- If the user message is short or conversational (e.g. "hello", "lol", "what's up", "hey"):
→ create a title that reflects the user's tone or intent (such as Greeting, Quick check-in, Light chat, Intro message, etc.)
</rules>
<examples>
"debug 500 errors in production" → Debugging production 500 errors
"refactor user service" → Refactoring user service
"why is app.js failing" → app.js failure investigation
"implement rate limiting" → Rate limiting implementation
"how do I connect postgres to my API" → Postgres API connection
"best practices for React hooks" → React hooks best practices
"@src/auth.ts can you add refresh token support" → Auth refresh token support
"@utils/parser.ts this is broken" → Parser bug fix
"look at @config.json" → Config review
"@App.tsx add dark mode toggle" → Dark mode toggle in App
</examples>

View File

@@ -124,6 +124,10 @@
(setq gptel-backend (cdr (assoc (car sel) gptel--known-backends)) (setq gptel-backend (cdr (assoc (car sel) gptel--known-backends))
gptel-model (cdr sel)) gptel-model (cdr sel))
(message "gptel set to %s:%s" (car sel) (cdr sel))))) (message "gptel set to %s:%s" (car sel) (cdr sel)))))
;; Al cargar gptel, cargar gptel-agent para que sus presets estén
;; disponibles (p. ej. al reabrir una sesión guardada de agente).
(require 'gptel-agent)
) )
;; (use-package copilot ;; (use-package copilot
@@ -146,7 +150,354 @@
(use-package gptel-agent (use-package gptel-agent
:ensure t :ensure t
:config (gptel-agent-update)) :defer nil
:init
(defvar kj-gptel-agents-dir
(expand-file-name "configs/gptel-agents" user-emacs-directory)
"Directorio con los agentes y prompts portados de opencode para gptel-agent.")
(defcustom kj-gptel-opencode-small-model nil
"Modelo para títulos/resúmenes de sesión (debe existir en el backend activo).
nil usa el modelo actual de la sesión."
:type '(choice (const :tag "Modelo actual de la sesión" nil) string)
: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: no se pudo añadir AGENTS.md %s" f)))))))
(defun kj-gptel--agent-buffer-setup ()
"Load AGENTS.md rules when a gptel-agent session buffer is created.
Dispara para sesiones nuevas (buffer *gptel-agent:*) y para sesiones
restauradas desde un archivo guardado (gptel--preset de agente)."
(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)
"Alternar entre el agente por defecto (build) y el modo plan.
Equivale a hacer clic en el botón del header: invoca la misma acción
que gptel-agent usa para ese botón. DATA se ignora (compatibilidad
con `buttonize')."
(interactive)
(unless (bound-and-true-p gptel-mode)
(user-error "No estás en una sesión gptel"))
(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 "Modo %s activado" (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 iniciando 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 "No se pudo abrir el archivo %s" path)
(with-current-buffer buf
(if (not (eglot-current-server))
(format "Sin servidor LSP para %s (modo %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 "Sin diagnósticos para %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)
"No hay un buffer con servidor LSP conectado en el proyecto."
(condition-case err
(with-current-buffer buf
(let ((res (eglot--workspace-symbols query)))
(if (null res)
(format "Sin símbolos que coincidan con \"%s\"." query)
(mapconcat #'substring-no-properties res "\n"))))
(error (format "Error en la consulta LSP: %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 (p. ej. \"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 "Parche aplicado correctamente.\n%s" (cdr r1))
(let ((r0 (kj-gptel--patch-apply diff root 0)))
(if (zerop (car r0))
(format "Parche aplicado correctamente (con -p0).\n%s" (cdr r0))
(format "No se pudo aplicar el parche (exit %s).\n%s"
(car r1) (cdr r1)))))))
(defun kj-gptel-plan-write (filename content)
"Write CONTENT to `.gptel/plans/FILENAME' in the project. Return status."
(let* ((root (kj-gptel--project-root))
(dir (expand-file-name ".gptel/plans" root))
(file (expand-file-name (file-name-nondirectory filename) dir)))
(make-directory dir t)
(with-temp-file file (insert content))
(format "Plan guardado en %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 "El buffer actual no es una sesión gptel"))
(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 "Resumen generado en *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 "El buffer actual no es una sesión gptel"))
(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 "Título: %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))))
(gptel-agent-update)
;; Herramientas del agente (equivalente a las tools de opencode)
(gptel-make-tool
:name "lsp_diagnostics"
:function #'kj-gptel-lsp-diagnostics
:description "Obtener los diagnósticos LSP (errores y advertencias) de un archivo, vía el servidor de lenguaje (eglot).
Úsalo para saber si un archivo tiene errores tras editarlo o para comprobar el estado del código. Recibe la ruta del archivo y devuelve una línea por diagnóstico con formato ruta:línea:columna [tipo] mensaje. Si el servidor LSP no está conectado para ese archivo, intenta iniciarlo."
:args '((:name "path"
:type string
:description "Ruta del archivo del que obtener los diagnósticos."))
:category "gptel-agent"
:include t)
(gptel-make-tool
:name "lsp_symbols"
:function #'kj-gptel-lsp-symbols
:description "Buscar símbolos (funciones, clases, variables, etc.) en el workspace mediante LSP (eglot).
Útil para localizar definiciones por nombre o ver qué símbolos existen. Recibe una consulta (patrón) y devuelve la lista de símbolos coincidentes en formato \"contenedor nombre Tipo\"."
:args '((:name "query"
:type string
:description "Patrón a buscar en los símbolos del workspace.")
(:name "path"
:type string
:description "Ruta de un archivo del proyecto cuyo servidor LSP usar (opcional)."
:optional t))
:category "gptel-agent"
:include t)
(gptel-make-tool
:name "apply_patch"
:function #'kj-gptel-apply-patch
:description "Aplicar un diff unificado a los archivos del proyecto.
Recibe un diff unificado (formato git o --- a/ / +++ b/). Se aplica con la herramienta patch (intenta -p1 y luego -p0). Úsalo para ediciones grandes o que tocan varios archivos, en lugar de múltiples llamadas a Edit. Modifica archivos del proyecto."
:args '((:name "diff"
:type string
:description "El diff unificado a aplicar, con cabeceras de archivo (--- a/... y +++ b/...)."))
:category "gptel-agent"
:confirm t
:include t)
(gptel-make-tool
:name "plan_write"
:function #'kj-gptel-plan-write
:description "Guardar el plan actual como archivo Markdown en .gptel/plans/ del proyecto.
Úsalo cuando hayas elaborado un plan, para persistirlo en disco y poder retomarlo después. Guarda el contenido en .gptel/plans/<nombre>.md. No modifica ningún otro archivo."
:args '((:name "filename"
:type string
:description "Nombre del archivo del plan (p. ej. refactor-auth.md).")
(:name "content"
:type string
:description "Contenido Markdown del plan."))
:category "gptel-agent"
:confirm t
:include t))
(use-package macher (use-package macher
:ensure (:host github :repo "kmontag/macher") :ensure (:host github :repo "kmontag/macher")