Skip to content

Commit b31c107

Browse files
Merge branch 'karthink:master' into gh-change-token-management
2 parents dbbf854 + df7accc commit b31c107

File tree

7 files changed

+171
-53
lines changed

7 files changed

+171
-53
lines changed

NEWS

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
# -*- mode: org; -*-
22

3+
* 0.9.9.3-pre
4+
5+
** New features and UI changes
6+
7+
- ~gptel-mcp-connect~ can now start MCP servers synchronously. This
8+
is useful for scripting purposes, when MCP tools need to be
9+
available before performing other actions. One common use is
10+
starting MCP servers when applying a gptel preset.
11+
12+
- "gitignored" files are omitted by default when adding files to gptel's
13+
context. This setting can be controlled via the user option
14+
~gptel-context-restrict-to-project-files~.
15+
316
* 0.9.9 2025-08-02
417

518
** Breaking changes

README.org

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1380,6 +1380,8 @@ You can examine the active context from the menu:
13801380
And then browse through or remove context from the context buffer:
13811381
#+html: <img src="https://github.com/karthink/gptel/assets/8607532/79a5ffe8-3d63-4bf7-9bf6-0457ab61bf2a" align="center" alt="Image showing gptel's context buffer.">
13821382

1383+
By default, files in a version control system that are not project files ("gitignored" files) will not be added to the context. To be able to add these files, set =gptel-context-restrict-to-project-files= to =nil=. Note that remote files are always included, regardless of the value of =gptel-context-restrict-to-project-files=.
1384+
13831385
*** Handle "reasoning" content
13841386

13851387
Some LLMs include in their response a "thinking" or "reasoning" block. This text improves the quality of the LLM’s final output, but may not be interesting to you by itself. You can decide how you would like this "reasoning" content to be handled by gptel by setting the user option =gptel-include-reasoning=. You can include it in the LLM response (the default), omit it entirely, include it in the buffer but ignore it on subsequent conversation turns, or redirect it to another buffer. As with most options, you can specify this behvaior from gptel's transient menu globally, buffer-locally or for the next request only.

gptel-context.el

Lines changed: 70 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@
2727

2828
;;; Code:
2929

30-
;;; -*- lexical-binding: t -*-
3130
(require 'gptel)
3231
(require 'cl-lib)
32+
(require 'project)
3333

3434
(declare-function gptel-menu "gptel-transient")
3535
(declare-function dired-get-marked-files "dired")
@@ -91,6 +91,23 @@ context chunk. This is accessible as, for example:
9191
:group 'gptel
9292
:type 'function)
9393

94+
(defcustom gptel-context-restrict-to-project-files t
95+
"Restrict files eligible to be added to the context to project files.
96+
97+
When set to t, files in a VCS that are not project files (such as files
98+
listed in `.gitignore' in a Git repository) will not be added to the
99+
context."
100+
:group 'gptel
101+
:type 'boolean)
102+
103+
(defvar gptel-context--project-files nil
104+
"Cached alist of project files per project.")
105+
106+
(defvar gptel-context--reset-cache nil
107+
"Whether a project files cache-buster has been scheduled.")
108+
109+
;;; Commands
110+
94111
(defun gptel-context-add-current-kill (&optional arg)
95112
"Add current-kill to gptel, accumulating if arg is non-nil"
96113
(interactive "P")
@@ -149,8 +166,9 @@ context chunk. This is accessible as, for example:
149166
(mapc action-fn files))))
150167
;; If in an image buffer
151168
((and (derived-mode-p 'image-mode)
152-
(gptel--model-capable-p 'media)
153-
(buffer-file-name))
169+
(gptel--model-capable-p 'media)
170+
(buffer-file-name)
171+
(not (gptel-context--skip-p (buffer-file-name))))
154172
(funcall (if (and arg (< (prefix-numeric-value arg) 0))
155173
#'gptel-context-remove
156174
#'gptel-context-add-file)
@@ -212,35 +230,66 @@ Return PATH if added, nil if ignored."
212230
(defun gptel-context--add-directory (path action)
213231
"Process all files in directory at PATH according to ACTION.
214232
ACTION should be either `add' or `remove'."
215-
(let ((files (directory-files-recursively path "." t)))
216-
(mapc (lambda (file)
217-
(unless (file-directory-p file)
218-
(pcase-exhaustive action
219-
('add
220-
(if (gptel--file-binary-p file)
221-
(gptel-context--add-binary-file file)
222-
(gptel-context--add-text-file file)))
223-
('remove
224-
(setf (alist-get file gptel-context--alist nil 'remove #'equal) nil)))))
225-
files)
226-
(when (eq action 'remove)
227-
(message "Directory \"%s\" removed from context." path))))
233+
(dolist (file (directory-files-recursively path "."))
234+
(pcase-exhaustive action
235+
('add (gptel-context-add-file file))
236+
('remove
237+
(setf (alist-get file gptel-context--alist nil 'remove #'equal) nil)))))
228238

229239
(defun gptel-context-add-file (path)
230240
"Add the file at PATH to the gptel context.
231241
232-
If PATH is a directory, recursively add all files in it.
233-
PATH should be readable as text."
242+
If PATH is a directory, recursively add all files in it. PATH should be
243+
readable as text."
234244
(interactive "fChoose file to add to context: ")
245+
(unless gptel-context--reset-cache
246+
(setq gptel-context--reset-cache t)
247+
(run-at-time
248+
0 nil
249+
(lambda () (setq gptel-context--reset-cache nil
250+
gptel-context--project-files nil))))
235251
(cond ((file-directory-p path)
236-
(gptel-context--add-directory path 'add))
252+
(gptel-context--add-directory path 'add))
253+
((gptel-context--skip-p path)
254+
;; Don't message about .git, as this creates thousands of messages
255+
(unless (string-match-p "\\.git/" path)
256+
(gptel-context--message-skipped path)))
237257
((gptel--file-binary-p path)
238258
(gptel-context--add-binary-file path))
239-
((gptel-context--add-text-file path))))
259+
(t (gptel-context--add-text-file path))))
240260

241261
;;;###autoload (autoload 'gptel-add-file "gptel-context" "Add files to gptel's context." t)
242262
(defalias 'gptel-add-file #'gptel-context-add-file)
243263

264+
;;; project-related functions
265+
(defun gptel-context--get-project-files (dir)
266+
"Return a list of files in the project DIR, or nil if no project is found."
267+
(when-let* ((project (project-current nil dir)))
268+
(with-memoization (alist-get dir gptel-context--project-files
269+
nil nil #'equal)
270+
(project-files project))))
271+
272+
(defun gptel-context--skip-p (file)
273+
"Return non-nil if FILE should not be added to the context."
274+
(when (and gptel-context-restrict-to-project-files
275+
(not (file-remote-p file)))
276+
(and-let* ((project (project-current nil file)))
277+
(not (member (expand-file-name file)
278+
(gptel-context--get-project-files (project-root project)))))))
279+
280+
(defun gptel-context--message-skipped (file)
281+
"Message that FILE is skipped because it is not a project file."
282+
(let* ((type (if (file-directory-p file) "directory" "file"))
283+
(reminder (format "To include it, unset `%S'."
284+
'gptel-context-restrict-to-project-files)))
285+
(if-let* ((root (cl-some (lambda (dir) (and (file-in-directory-p file dir) dir))
286+
(map-keys gptel-context--project-files)))
287+
(rel-file (file-relative-name file root)))
288+
(message "Skipping %s \"%s\" in project \"%s\". %s"
289+
type rel-file root reminder)
290+
(message "Skipping %s \"%s\". %s" type file reminder))))
291+
292+
;;; Remove context
244293
(defun gptel-context-remove (&optional context)
245294
"Remove the CONTEXT overlay from the contexts list.
246295
@@ -287,6 +336,7 @@ afterwards."
287336
finally do (setq gptel-context--alist nil)))
288337
(when verbose (message "Removed all gptel context sources."))))
289338

339+
;;; Context wrap
290340
(defun gptel-context--make-overlay (start end &optional advance)
291341
"Highlight the region from START to END.
292342

gptel-gh.el

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,13 @@
159159
:context-window 1000
160160
:input-cost 0.10
161161
:output-cost 0.40
162-
:cutoff-date "2024-08")))
162+
:cutoff-date "2024-08")
163+
(grok-code-fast-1
164+
:description "Fast reasoning model for agentic coding"
165+
:capabilities '(tool-use json reasoning)
166+
:context-window 128
167+
:input-cost 0.2
168+
:output-cost 1.5)))
163169

164170
(cl-defstruct (gptel--gh (:include gptel-openai)
165171
(:copier nil)

gptel-org.el

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -424,11 +424,13 @@ parameters.
424424
ARGS are the original function call arguments."
425425
(if (derived-mode-p 'org-mode)
426426
(pcase-let ((`(,gptel--system-message ,gptel-backend ,gptel-model
427-
,gptel-temperature ,gptel-max-tokens)
427+
,gptel-temperature ,gptel-max-tokens
428+
,gptel--num-messages-to-send ,gptel-tools)
428429
(seq-mapn (lambda (a b) (or a b))
429430
(gptel-org--entry-properties)
430431
(list gptel--system-message gptel-backend gptel-model
431-
gptel-temperature gptel-max-tokens))))
432+
gptel-temperature gptel-max-tokens
433+
gptel--num-messages-to-send gptel-tools))))
432434
(apply send-fun args))
433435
(apply send-fun args)))
434436

@@ -445,12 +447,12 @@ ARGS are the original function call arguments."
445447
(defun gptel-org--entry-properties (&optional pt)
446448
"Find gptel configuration properties stored at PT."
447449
(pcase-let
448-
((`(,system ,backend ,model ,temperature ,tokens ,num)
450+
((`(,system ,backend ,model ,temperature ,tokens ,num ,tools)
449451
(mapcar
450452
(lambda (prop) (org-entry-get (or pt (point)) prop 'selective))
451453
'("GPTEL_SYSTEM" "GPTEL_BACKEND" "GPTEL_MODEL"
452454
"GPTEL_TEMPERATURE" "GPTEL_MAX_TOKENS"
453-
"GPTEL_NUM_MESSAGES_TO_SEND"))))
455+
"GPTEL_NUM_MESSAGES_TO_SEND" "GPTEL_TOOLS"))))
454456
(when system
455457
(setq system (string-replace "\\n" "\n" system)))
456458
(when backend
@@ -461,7 +463,16 @@ ARGS are the original function call arguments."
461463
(setq temperature (gptel--to-number temperature)))
462464
(when tokens (setq tokens (gptel--to-number tokens)))
463465
(when num (setq num (gptel--to-number num)))
464-
(list system backend model temperature tokens num)))
466+
(when tools
467+
(setq tools (cl-loop
468+
for tname in (split-string tools)
469+
for tool = (with-demoted-errors "gptel: %S"
470+
(gptel-get-tool tname))
471+
if tool collect tool else do
472+
(display-warning
473+
'(gptel org tools)
474+
(format "Tool %s not found, ignoring" tname)))))
475+
(list system backend model temperature tokens num tools)))
465476

466477
(defun gptel-org--restore-state ()
467478
"Restore gptel state for Org buffers when turning on `gptel-mode'."
@@ -472,7 +483,7 @@ ARGS are the original function call arguments."
472483
(progn
473484
(when-let* ((bounds (org-entry-get (point-min) "GPTEL_BOUNDS")))
474485
(gptel--restore-props (read bounds)))
475-
(pcase-let ((`(,system ,backend ,model ,temperature ,tokens ,num)
486+
(pcase-let ((`(,system ,backend ,model ,temperature ,tokens ,num ,tools)
476487
(gptel-org--entry-properties (point-min))))
477488
(when system (setq-local gptel--system-message system))
478489
(if backend (setq-local gptel-backend backend)
@@ -486,7 +497,8 @@ ARGS are the original function call arguments."
486497
(when model (setq-local gptel-model model))
487498
(when temperature (setq-local gptel-temperature temperature))
488499
(when tokens (setq-local gptel-max-tokens tokens))
489-
(when num (setq-local gptel--num-messages-to-send num))))
500+
(when num (setq-local gptel--num-messages-to-send num))
501+
(when tools (setq-local gptel-tools tools))))
490502
(:success (message "gptel chat restored."))
491503
(error (message "Could not restore gptel state, sorry! Error: %s" status)))
492504
(set-buffer-modified-p modified))))
@@ -503,20 +515,27 @@ non-nil (default), display a message afterwards."
503515
(interactive (list (point) t))
504516
(org-entry-put pt "GPTEL_MODEL" (gptel--model-name gptel-model))
505517
(org-entry-put pt "GPTEL_BACKEND" (gptel-backend-name gptel-backend))
506-
(unless (equal (default-value 'gptel-temperature) gptel-temperature)
507-
(org-entry-put pt "GPTEL_TEMPERATURE"
508-
(number-to-string gptel-temperature)))
509-
(when (natnump gptel--num-messages-to-send)
510-
(org-entry-put pt "GPTEL_NUM_MESSAGES_TO_SEND"
511-
(number-to-string gptel--num-messages-to-send)))
512-
(org-entry-put pt "GPTEL_SYSTEM"
518+
(org-entry-put pt "GPTEL_SYSTEM" ;TODO: Handle nil case correctly
513519
(and-let* ((msg (car-safe
514520
(gptel--parse-directive
515521
gptel--system-message))))
516522
(string-replace "\n" "\\n" msg)))
517-
(when gptel-max-tokens
518-
(org-entry-put
519-
pt "GPTEL_MAX_TOKENS" (number-to-string gptel-max-tokens)))
523+
(if gptel-tools
524+
(org-entry-put
525+
pt "GPTEL_TOOLS" (mapconcat #'gptel-tool-name gptel-tools " "))
526+
(org-entry-delete pt "GPTEL_TOOLS"))
527+
(if (equal (default-value 'gptel-temperature) gptel-temperature)
528+
(org-entry-delete pt "GPTEL_TEMPERATURE")
529+
(org-entry-put pt "GPTEL_TEMPERATURE"
530+
(number-to-string gptel-temperature)))
531+
(if (natnump gptel--num-messages-to-send)
532+
(org-entry-put pt "GPTEL_NUM_MESSAGES_TO_SEND"
533+
(number-to-string gptel--num-messages-to-send))
534+
(org-entry-delete pt "GPTEL_NUM_MESSAGES_TO_SEND"))
535+
(if gptel-max-tokens
536+
(org-entry-put
537+
pt "GPTEL_MAX_TOKENS" (number-to-string gptel-max-tokens))
538+
(org-entry-delete pt "GPTEL_MAX_TOKENS"))
520539
(when msg
521540
(message "Added gptel configuration to current headline.")))
522541

gptel.el

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,13 @@ This opens up advanced options in `gptel-menu'.")
802802
(defvar gptel--num-messages-to-send nil)
803803
(put 'gptel--num-messages-to-send 'safe-local-variable #'always)
804804

805+
(defvar-local gptel--tool-names nil
806+
"Store to persist tool names to file across Emacs sessions.
807+
808+
Note: Changing this variable does not affect gptel\\='s behavior
809+
in any way.")
810+
(put 'gptel--backend-name 'safe-local-variable #'always)
811+
805812
(defcustom gptel-log-level nil
806813
"Logging level for gptel.
807814
@@ -1429,7 +1436,17 @@ applied before being re-persisted in the new structure."
14291436
"Could not activate gptel backend \"%s\"! "
14301437
"Switch backends with \\[universal-argument] \\[gptel-send]"
14311438
" before using gptel."))
1432-
gptel--backend-name))))))
1439+
gptel--backend-name)))
1440+
(when gptel--tool-names
1441+
(if-let* ((tools (cl-loop
1442+
for tname in gptel--tool-names
1443+
for tool = (with-demoted-errors "gptel: %S"
1444+
(gptel-get-tool tname))
1445+
if tool collect tool else do
1446+
(display-warning
1447+
'(gptel org tools)
1448+
(format "Tool %s not found, ignoring" tname)))))
1449+
(setq-local gptel-tools tools))))))
14331450

14341451
(defun gptel--save-state ()
14351452
"Write the gptel state to the buffer.
@@ -1448,18 +1465,25 @@ file."
14481465
(add-file-local-variable 'gptel-model gptel-model)
14491466
(add-file-local-variable 'gptel--backend-name
14501467
(gptel-backend-name gptel-backend))
1451-
(unless (equal (default-value 'gptel-temperature) gptel-temperature)
1452-
(add-file-local-variable 'gptel-temperature gptel-temperature))
14531468
(unless (equal (default-value 'gptel--system-message)
14541469
gptel--system-message)
14551470
(add-file-local-variable
1456-
'gptel--system-message
1471+
'gptel--system-message ;TODO: Handle nil case correctly
14571472
(car-safe (gptel--parse-directive gptel--system-message))))
1458-
(when gptel-max-tokens
1459-
(add-file-local-variable 'gptel-max-tokens gptel-max-tokens))
1460-
(when (natnump gptel--num-messages-to-send)
1461-
(add-file-local-variable 'gptel--num-messages-to-send
1462-
gptel--num-messages-to-send))
1473+
(if gptel-tools
1474+
(add-file-local-variable
1475+
'gptel--tool-names (mapcar #'gptel-tool-name gptel-tools))
1476+
(delete-file-local-variable 'gptel--tool-names))
1477+
(if (equal (default-value 'gptel-temperature) gptel-temperature)
1478+
(delete-file-local-variable 'gptel-temperature)
1479+
(add-file-local-variable 'gptel-temperature gptel-temperature))
1480+
(if gptel-max-tokens
1481+
(add-file-local-variable 'gptel-max-tokens gptel-max-tokens)
1482+
(delete-file-local-variable 'gptel-max-tokens))
1483+
(if (natnump gptel--num-messages-to-send)
1484+
(add-file-local-variable 'gptel--num-messages-to-send
1485+
gptel--num-messages-to-send)
1486+
(delete-file-local-variable 'gptel--num-messages-to-send))
14631487
(add-file-local-variable 'gptel--bounds (gptel--get-buffer-bounds)))))))
14641488

14651489

@@ -1940,9 +1964,11 @@ The following keys are optional
19401964
CATEGORY: A string indicating a category for the tool. This is
19411965
used only for grouping in gptel's UI. Defaults to \"misc\".
19421966
1943-
CONFIRM: Whether the tool call should wait for the user to run
1944-
it. If true, the user will be prompted with the proposed tool
1945-
call, which can be examined, accepted, deferred or canceled.
1967+
CONFIRM: Whether the tool call should wait for the user to run it. If
1968+
true, the user will be prompted with the proposed tool call, which can
1969+
be examined, accepted, deferred or canceled. It can also be a function
1970+
that receives the same arguments as FUNCTION and returns true if the
1971+
user should be prompted.
19461972
19471973
INCLUDE: Whether the tool results should be included as part of
19481974
the LLM output. This is useful for logging and as context for
@@ -2420,8 +2446,10 @@ Run post-response hooks."
24202446
(plist-get args key)))
24212447
(gptel-tool-args tool-spec)))
24222448
;; Check if tool requires confirmation
2423-
(if (and gptel-confirm-tool-calls (or (eq gptel-confirm-tool-calls t)
2424-
(gptel-tool-confirm tool-spec)))
2449+
(if (and gptel-confirm-tool-calls
2450+
(or (eq gptel-confirm-tool-calls t) ;always confirm, or
2451+
(and-let* ((confirm (gptel-tool-confirm tool-spec)))
2452+
(or (not (functionp confirm)) (apply confirm arg-values)))))
24252453
(push (list tool-spec arg-values process-tool-result)
24262454
pending-calls)
24272455
;; If not, run the tool

test

0 commit comments

Comments
 (0)