Improve org-ql tools' argument descriptions

This commit is contained in:
Phil Bajsicki 2025-04-16 17:00:18 +02:00
parent de14a8d066
commit 6240a40ff0
2 changed files with 110 additions and 180 deletions

View file

@ -102,21 +102,6 @@ config.el:
(require 'gptel-org-tools) (require 'gptel-org-tools)
(mapcar (lambda (tool) (cl-pushnew tool gptel-tools)) gptel-org-tools) (mapcar (lambda (tool) (cl-pushnew tool gptel-tools)) gptel-org-tools)
#+end_src #+end_src
** Variables
If you're like me, you may have structured headings based on timestamps in your org-mode notes.
To that end, there are a few variables that may help you from blowing out the context, specifically for the ~org-extract-headings~ tool.
#+begin_src elisp
(defvar gptel-org-tools-skip-heading-extraction '())
#+end_src
Use ~setq~ in your configuration, with a list of buffer names whose headings you *never* want to extract. E.g.
#+begin_src elisp :tangle no
(setq gptel-org-tools-skip-heading-extraction '("journal.org" ".org"))
#+end_src
* Code * Code
** Preamble ** Preamble
:PROPERTIES: :PROPERTIES:
@ -165,10 +150,34 @@ Collects into =gptel-org-tools= list, distinct from =gptel-tools=
#+begin_src elisp #+begin_src elisp
(defvar gptel-org-tools '()) (defvar gptel-org-tools '())
#+end_src #+end_src
** Variables
If you're like me, you may have structured headings based on timestamps in your org-mode notes.
To that end, there are a few variables that may help you from blowing out the context, specifically for the ~org-extract-headings~ tool.
#+begin_src elisp
(defvar gptel-org-tools-skip-heading-extraction '())
#+end_src
Use ~setq~ in your configuration, with a list of buffer names whose headings you *never* want to extract. E.g.
#+begin_src elisp :tangle no
(setq gptel-org-tools-skip-heading-extraction '("journal.org" ".org"))
#+end_src
** Helper Functions ** Helper Functions
These abstract away some of the tool definitions. These abstract away some of the tool definitions.
Both of these clear the org-ql-cache to work around issues where a file may be updated between tool calls. *** Retrieve heading (line])
#+begin_src elisp
(defun gptel-org-tools--heading ()
(concat
(buffer-substring-no-properties
(line-beginning-position)
(line-end-position))
'"\n---\n"))
#+end_src
*** Retrieve heading and body (without subheadings) *** Retrieve heading and body (without subheadings)
#+begin_src elisp #+begin_src elisp
(defun gptel-org-tools--heading-body () (defun gptel-org-tools--heading-body ()
@ -190,15 +199,19 @@ Both of these clear the org-ql-cache to work around issues where a file may be u
(org-end-of-subtree)) (org-end-of-subtree))
"---\n")) "---\n"))
#+end_src #+end_src
** Note on org-ql ** Note on org-ql (caching)
Given that there isn't (yet?) a built-in way of disabling caching, every (org-ql-select) call is wrapped like so. There isn't (yet?) a good way to disable caching. This means that repeated queries will return the same output, until org-ql-cache is cleared.
See [[https://github.com/alphapapa/org-ql/issues/437][this issue]] for details. See [[https://github.com/alphapapa/org-ql/issues/437][this issue]] for details.
#+begin_src elisp :tangle no The problem is that trying the solution given in the GitHub issue throws errors, which make LLMs freak out.
(let ((org-ql-cache (make-hash-table)))
(org-ql-select ...)) So for now, you can manually re-set the cache like so:
#+begin_src elisp :tangle no :results none
(setq org-ql-cache (make-hash-table))
#+end_src #+end_src
** The tools ** The tools
*** Emacs *** Emacs
These tools are primarily concerned with Emacs, Emacs Lisp, and files-or-buffers. These tools are primarily concerned with Emacs, Emacs Lisp, and files-or-buffers.
@ -245,7 +258,8 @@ Seems to be one of the most reliable tools in the basket... mostly because
:description "Access the list of buffers open in Emacs, including file names and full paths." :description "Access the list of buffers open in Emacs, including file names and full paths."
:args (list '(:name "arg" :args (list '(:name "arg"
:type string :type string
:description "Does nothing.")) :description "Does nothing."
:optional t))
:category "emacs")) :category "emacs"))
#+end_src #+end_src
@ -277,11 +291,14 @@ Be sure to customize the function to point to your org directory, if you wish. I
#+end_src #+end_src
**** find-buffer-visiting **** find-buffer-visiting
Disabled for now, as it's causing some issues.
#+begin_src elisp #+begin_src elisp
(add-to-list 'gptel-org-tools (add-to-list 'gptel-org-tools
(gptel-make-tool (gptel-make-tool
:function (lambda (filename) :function (lambda (filename)
(bufferp (find-buffer-visiting (expand-file-name filename)))) (concat
(bufferp (find-buffer-visiting (expand-file-name filename)))
"\n---\nTool execution complete. Proceed with next step."))
:name "find-buffer-visiting" :name "find-buffer-visiting"
:description "Check if the file is open in a buffer. Usage (find-buffer-visiting filename)" :description "Check if the file is open in a buffer. Usage (find-buffer-visiting filename)"
:args (list '(:name "filename" :args (list '(:name "filename"
@ -289,6 +306,7 @@ Be sure to customize the function to point to your org directory, if you wish. I
:description "The filename to compare to open buffers.")) :description "The filename to compare to open buffers."))
:category "org-mode")) :category "org-mode"))
#+end_src #+end_src
**** open-file-inactive **** open-file-inactive
Continuation from above. Open a file into a buffer for processing, once it's found by dired-list. Continuation from above. Open a file into a buffer for processing, once it's found by dired-list.
#+begin_src elisp #+begin_src elisp
@ -297,7 +315,9 @@ Continuation from above. Open a file into a buffer for processing, once it's fou
:function (lambda (file) :function (lambda (file)
(with-current-buffer (get-buffer-create file) (with-current-buffer (get-buffer-create file)
(insert-file-contents file) (insert-file-contents file)
(current-buffer))) (concat
(current-buffer)
"\n---\nTool execution complete. Proceed with next step.")))
:name "open-file-inactive" :name "open-file-inactive"
:description "Open the file in a background buffer. This doesn't interfere with the user." :description "Open the file in a background buffer. This doesn't interfere with the user."
:args (list '(:name "file" :args (list '(:name "file"
@ -314,7 +334,9 @@ This reads file contents,
:function (lambda (filename) :function (lambda (filename)
(with-temp-buffer (with-temp-buffer
(insert-file-contents (expand-file-name filename)) (insert-file-contents (expand-file-name filename))
(buffer-string))) (concat
(buffer-string)
"\n---\nTool execution complete. Proceed with next step.")))
:name "read-file-contents" :name "read-file-contents"
:description "Read and return the contents of a specified file." :description "Read and return the contents of a specified file."
:args (list '(:name "filename" :args (list '(:name "filename"
@ -422,9 +444,7 @@ Therefore, headings. A reasonable amount of information, and still keeping the s
(user-error "Buffer %s has too many headings, use org-extract-tags or org-ql-select-rifle." buffer) (user-error "Buffer %s has too many headings, use org-extract-tags or org-ql-select-rifle." buffer)
(with-current-buffer buffer (with-current-buffer buffer
(org-map-entries (org-map-entries
#'(buffer-substring-no-properties #'gptel-org-tools--heading
(line-beginning-position)
(line-end-position))
t t
'file)))) 'file))))
@ -460,7 +480,7 @@ Currently *not* tangled, as I'm testing breaking out each type of query into its
(if (stringp query) (if (stringp query)
(read query) (read query)
query) query)
:action #'gptel-org-tools--heading-body :action #'gptel-org-tools--heading-body))
(add-to-list 'gptel-org-tools (add-to-list 'gptel-org-tools
(gptel-make-tool (gptel-make-tool
@ -546,7 +566,6 @@ The following tools are still very much WIP, and I think they're self-explanator
Retrieve the headings where the heading matches query.. Retrieve the headings where the heading matches query..
#+begin_src elisp #+begin_src elisp
(defun gptel-org-tools--org-ql-select-headings (buf query) (defun gptel-org-tools--org-ql-select-headings (buf query)
(let ((org-ql-cache (make-hash-table)))
(org-ql-select (org-ql-select
(get-buffer buf) (get-buffer buf)
`(heading ,query) `(heading ,query)
@ -554,20 +573,20 @@ Retrieve the headings where the heading matches query..
(concat (concat
(buffer-substring-no-properties (buffer-substring-no-properties
(line-beginning-position) (line-beginning-position)
(line-end-position))))))) (line-end-position))))))
(add-to-list 'gptel-org-tools (add-to-list 'gptel-org-tools
(gptel-make-tool (gptel-make-tool
:function #'gptel-org-tools--org-ql-select-headings :function #'gptel-org-tools--org-ql-select-headings
:name "org-ql-select-headings" :name "org-ql-select-headings"
:description "Retreive matching headings from buffer using org-ql-select. Matches only against heading. Using filename fails." :description "Retreive matching headings from buffer. Matches only a single string. Using filename fails."
:args (list '(:name "buffer" :args (list '(:name "buffer"
:type string :type string
:description "The name of the buffer. See the NAME column in ~emacs-list-buffers~.") :description "The name of the buffer. See the NAME column in ~emacs-list-buffers~.")
'(:name "query" '(:name "query"
:type string :type string
:description "The string to match entry headings against.")) :description "The keyword to match entry headings against."))
:category "org-ql")) :category "org-ql"))
#+end_src #+end_src
@ -575,15 +594,10 @@ Retrieve the headings where the heading matches query..
Retrieve all the headings where either heading or content matches query. Retrieve all the headings where either heading or content matches query.
#+begin_src elisp #+begin_src elisp
(defun gptel-org-tools--org-ql-select-headings-rifle (buf query) (defun gptel-org-tools--org-ql-select-headings-rifle (buf query)
(let ((org-ql-cache (make-hash-table)))
(org-ql-select (org-ql-select
(get-buffer buf) (get-buffer buf)
`(rifle ,query) `(rifle ,query)
:action #'(lambda () :action #'gptel-org-tools--heading))
(concat
(buffer-substring-no-properties
(line-beginning-position)
(line-end-position)))))))
(add-to-list 'gptel-org-tools (add-to-list 'gptel-org-tools
@ -596,7 +610,7 @@ Retrieve all the headings where either heading or content matches query.
:description "The name of the buffer. See the NAME column in ~emacs-list-buffers~.") :description "The name of the buffer. See the NAME column in ~emacs-list-buffers~.")
'(:name "query" '(:name "query"
:type string :type string
:description "The string to match entry headings against.")) :description "The keyword to match entry headings against."))
:category "org-ql")) :category "org-ql"))
#+end_src #+end_src
@ -604,17 +618,10 @@ Retrieve all the headings where either heading or content matches query.
This pulls all the headings (and their contents) when they match tags (without inheritance.) This pulls all the headings (and their contents) when they match tags (without inheritance.)
#+begin_src elisp #+begin_src elisp
(defun gptel-org-tools--org-ql-select-tags-local (buf query) (defun gptel-org-tools--org-ql-select-tags-local (buf query)
(let ((org-ql-cache (make-hash-table)))
(org-ql-select (org-ql-select
(get-buffer buf) (get-buffer buf)
`(tags-local ,query) `(tags-local ,query)
:action #'(lambda () :action #'gptel-org-tools-heading-body))
(concat
(buffer-substring-no-properties
(line-beginning-position)
(progn
(outline-next-heading)
(line-beginning-position))))))))
(add-to-list 'gptel-org-tools (add-to-list 'gptel-org-tools
@ -635,18 +642,10 @@ This pulls all the headings (and their contents) when they match tags (without i
This pulls all the headings (and their contents) when they match tags (with inheritance; if a parent entry has the tag, descendant entries do, too.) This pulls all the headings (and their contents) when they match tags (with inheritance; if a parent entry has the tag, descendant entries do, too.)
#+begin_src elisp #+begin_src elisp
(defun gptel-org-tools--org-ql-select-tags (buf query) (defun gptel-org-tools--org-ql-select-tags (buf query)
(let ((org-ql-cache (make-hash-table)))
(org-ql-select (org-ql-select
(get-buffer buf) (get-buffer buf)
`(tags ,query) `(tags ,query)
:action #'(lambda () :action #'gptel-org-tools--heading-body))
(concat
(buffer-substring-no-properties
(line-beginning-position)
(progn
(outline-next-heading)
(line-beginning-position))))))))
(add-to-list 'gptel-org-tools (add-to-list 'gptel-org-tools
(gptel-make-tool (gptel-make-tool
@ -658,27 +657,20 @@ This pulls all the headings (and their contents) when they match tags (with inhe
:description "The name of the buffer. See the NAME column in `emacs-list-buffers`. Using filename fails.") :description "The name of the buffer. See the NAME column in `emacs-list-buffers`. Using filename fails.")
'(:name "query" '(:name "query"
:type string :type string
:description "The string to match entry headings against.")) :description "The keyword to match entry headings against."))
:category "org-ql")) :category "org-ql"))
#+end_src #+end_src
***** org-ql-select-rifle ***** org-ql-select-rifle
And, the "grab everything that matches" tool. And, the "grab everything that matches" tool.
#+begin_src elisp #+begin_src elisp
(defun hash-gptl-org-tools--org-ql-select-rifle () (defun gptel-org-tools--org-ql-select-rifle (buf query)
(let ((org-ql-cache (make-hash-table)) (let ((buffer (get-buffer buf)))
(buffer (get-buffer buf)))
(if buffer (if buffer
(org-ql-select (org-ql-select
buffer buffer
`(rifle ,query) `(rifle ,query)
:action #'(lambda () :action #'gptel-org-tools--heading-subtree)
(concat
(buffer-substring-no-properties
(line-beginning-position)
(progn
(outline-next-heading)
(line-beginning-position))))))
(message "Buffer '%s' does not exist." buf)))) (message "Buffer '%s' does not exist." buf))))
(add-to-list 'gptel-org-tools (add-to-list 'gptel-org-tools
@ -691,25 +683,18 @@ And, the "grab everything that matches" tool.
:description "The name of the buffer. See the NAME column in `emacs-list-buffers`. Using filename fails.") :description "The name of the buffer. See the NAME column in `emacs-list-buffers`. Using filename fails.")
'(:name "query" '(:name "query"
:type string :type string
:description "The string to match entry headings and content against.")) :description "A single keyword to search for."))
:category "org-ql")) :category "org-ql"))
#+end_src #+end_src
***** org-ql-select-agenda-tags-local ***** org-ql-select-agenda-tags-local
This pulls all the headings (and their contents) when they match tags (without inheritance.) This pulls all the headings (and their contents) when they match tags (without inheritance.)
#+begin_src elisp #+begin_src elisp
(defun gptel-org-tools--org-ql-select-agenda-tags (query) (defun gptel-org-tools--org-ql-select-agenda-tags-local (query)
(let ((org-ql-cache (make-hash-table)))
(org-ql-select (org-ql-select
(org-agenda-files) (org-agenda-files)
`(tags-local ,query) `(tags-local ,query)
:action #'(lambda () :action #'gptel-org-tools--heading-body))
(concat
(buffer-substring-no-properties
(line-beginning-position)
(progn
(outline-next-heading)
(line-beginning-position))))))))
(add-to-list 'gptel-org-tools (add-to-list 'gptel-org-tools
@ -719,7 +704,7 @@ This pulls all the headings (and their contents) when they match tags (without i
:description "Run simple word query against all files in (org-agenda-files). WITHOUT tag inheritance, only directly tagged headings." :description "Run simple word query against all files in (org-agenda-files). WITHOUT tag inheritance, only directly tagged headings."
:args (list '(:name "query" :args (list '(:name "query"
:type string :type string
:description "The string to match entry tags against.")) :description "A single word to scan for."))
:category "org-ql")) :category "org-ql"))
#+end_src #+end_src
@ -727,18 +712,10 @@ This pulls all the headings (and their contents) when they match tags (without i
This pulls all the headings (and their contents) when they match tags (with inheritance; if a parent entry has the tag, descendant entries do, too.) This pulls all the headings (and their contents) when they match tags (with inheritance; if a parent entry has the tag, descendant entries do, too.)
#+begin_src elisp #+begin_src elisp
(defun gptel-org-tools--org-ql-select-agenda-tags (query) (defun gptel-org-tools--org-ql-select-agenda-tags (query)
(let ((org-ql-cache (make-hash-table)))
(org-ql-select (org-ql-select
(org-agenda-files) (org-agenda-files)
`(tags ,query) `(tags ,query)
:action #'(lambda () :action #'gptel-org-tools--heading-subtree))
(concat
(buffer-substring-no-properties
(line-beginning-position)
(progn
(outline-next-heading)
(line-beginning-position))))))))
(add-to-list 'gptel-org-tools (add-to-list 'gptel-org-tools
(gptel-make-tool (gptel-make-tool
@ -747,7 +724,7 @@ This pulls all the headings (and their contents) when they match tags (with inhe
:description "Run simple word query against all files in (org-agenda-files). WITH tag inheritance." :description "Run simple word query against all files in (org-agenda-files). WITH tag inheritance."
:args (list '(:name "query" :args (list '(:name "query"
:type string :type string
:description "The string to match entry tags against.")) :description "A single word to scan for."))
:category "org-ql")) :category "org-ql"))
#+end_src #+end_src
@ -757,24 +734,17 @@ And for the entire =(org-agenda-files)=.
Note that I define my agenda in this way: Note that I define my agenda in this way:
#+begin_src elisp :tangle no #+begin_src elisp :tangle no
(setq org-agenda-files (directory-files-recursively "~/enc/org/" ".org$"))1 (setq org-agenda-files (directory-files-recursively "~/enc/org/" ".org$"))
#+end_src #+end_src
This means that /every org-mode file I have/ is part of this search. This means that /every org-mode file I have/ is part of this search.
#+begin_src elisp #+begin_src elisp
(defun gptel-org-tools--org-ql-select-agenda-rifle (query) (defun gptel-org-tools--org-ql-select-agenda-rifle (query)
(let ((org-ql-cache (make-hash-table)))
(org-ql-select (org-ql-select
(org-agenda-files) (org-agenda-files)
`(rifle ,query) `(rifle ,query)
:action #'(lambda () :action #'gptel-org-tools--heading-subtree))
(concat
(buffer-substring-no-properties
(line-beginning-position)
(progn
(outline-next-heading)
(line-beginning-position))))))))
(add-to-list 'gptel-org-tools (add-to-list 'gptel-org-tools
(gptel-make-tool (gptel-make-tool
@ -783,7 +753,7 @@ This means that /every org-mode file I have/ is part of this search.
:description "Run simple word query against all files in (org-agenda-files)" :description "Run simple word query against all files in (org-agenda-files)"
:args (list '(:name "query" :args (list '(:name "query"
:type string :type string
:description "The string to match entry headings and content against.")) :description "The keyword to match entry headings and content against."))
:category "org-ql")) :category "org-ql"))
#+end_src #+end_src

View file

@ -1,5 +1,3 @@
(defvar gptel-org-tools-skip-heading-extraction '())
;;; gptel-org-tools.el --- LLM Tools for org-mode interaction. -*- lexical-binding: t; -*- ;;; gptel-org-tools.el --- LLM Tools for org-mode interaction. -*- lexical-binding: t; -*-
;; Copyright (C) 2025 Phil Bajsicki ;; Copyright (C) 2025 Phil Bajsicki
@ -38,6 +36,15 @@
(defvar gptel-org-tools '()) (defvar gptel-org-tools '())
(defvar gptel-org-tools-skip-heading-extraction '())
(defun gptel-org-tools--heading ()
(concat
(buffer-substring-no-properties
(line-beginning-position)
(line-end-position))
'"\n---\n"))
(defun gptel-org-tools--heading-body () (defun gptel-org-tools--heading-body ()
(concat (concat
(buffer-substring-no-properties (buffer-substring-no-properties
@ -64,7 +71,8 @@
:description "Access the list of buffers open in Emacs, including file names and full paths." :description "Access the list of buffers open in Emacs, including file names and full paths."
:args (list '(:name "arg" :args (list '(:name "arg"
:type string :type string
:description "Does nothing.")) :description "Does nothing."
:optional t))
:category "emacs")) :category "emacs"))
(add-to-list 'gptel-org-tools (add-to-list 'gptel-org-tools
@ -86,7 +94,9 @@
(add-to-list 'gptel-org-tools (add-to-list 'gptel-org-tools
(gptel-make-tool (gptel-make-tool
:function (lambda (filename) :function (lambda (filename)
(bufferp (find-buffer-visiting (expand-file-name filename)))) (concat
(bufferp (find-buffer-visiting (expand-file-name filename)))
"\n---\nTool execution complete. Proceed with next step."))
:name "find-buffer-visiting" :name "find-buffer-visiting"
:description "Check if the file is open in a buffer. Usage (find-buffer-visiting filename)" :description "Check if the file is open in a buffer. Usage (find-buffer-visiting filename)"
:args (list '(:name "filename" :args (list '(:name "filename"
@ -99,7 +109,9 @@
:function (lambda (file) :function (lambda (file)
(with-current-buffer (get-buffer-create file) (with-current-buffer (get-buffer-create file)
(insert-file-contents file) (insert-file-contents file)
(current-buffer))) (concat
(current-buffer)
"\n---\nTool execution complete. Proceed with next step.")))
:name "open-file-inactive" :name "open-file-inactive"
:description "Open the file in a background buffer. This doesn't interfere with the user." :description "Open the file in a background buffer. This doesn't interfere with the user."
:args (list '(:name "file" :args (list '(:name "file"
@ -163,9 +175,7 @@
(user-error "Buffer %s has too many headings, use org-extract-tags or org-ql-select-rifle." buffer) (user-error "Buffer %s has too many headings, use org-extract-tags or org-ql-select-rifle." buffer)
(with-current-buffer buffer (with-current-buffer buffer
(org-map-entries (org-map-entries
#'(buffer-substring-no-properties #'gptel-org-tools--heading
(line-beginning-position)
(line-end-position))
t t
'file)))) 'file))))
@ -215,7 +225,6 @@
:category "org")) :category "org"))
(defun gptel-org-tools--org-ql-select-headings (buf query) (defun gptel-org-tools--org-ql-select-headings (buf query)
(let ((org-ql-cache (make-hash-table)))
(org-ql-select (org-ql-select
(get-buffer buf) (get-buffer buf)
`(heading ,query) `(heading ,query)
@ -223,32 +232,27 @@
(concat (concat
(buffer-substring-no-properties (buffer-substring-no-properties
(line-beginning-position) (line-beginning-position)
(line-end-position))))))) (line-end-position))))))
(add-to-list 'gptel-org-tools (add-to-list 'gptel-org-tools
(gptel-make-tool (gptel-make-tool
:function #'gptel-org-tools--org-ql-select-headings :function #'gptel-org-tools--org-ql-select-headings
:name "org-ql-select-headings" :name "org-ql-select-headings"
:description "Retreive matching headings from buffer using org-ql-select. Matches only against heading. Using filename fails." :description "Retreive matching headings from buffer. Matches only a single string. Using filename fails."
:args (list '(:name "buffer" :args (list '(:name "buffer"
:type string :type string
:description "The name of the buffer. See the NAME column in ~emacs-list-buffers~.") :description "The name of the buffer. See the NAME column in ~emacs-list-buffers~.")
'(:name "query" '(:name "query"
:type string :type string
:description "The string to match entry headings against.")) :description "The keyword to match entry headings against."))
:category "org-ql")) :category "org-ql"))
(defun gptel-org-tools--org-ql-select-headings-rifle (buf query) (defun gptel-org-tools--org-ql-select-headings-rifle (buf query)
(let ((org-ql-cache (make-hash-table)))
(org-ql-select (org-ql-select
(get-buffer buf) (get-buffer buf)
`(rifle ,query) `(rifle ,query)
:action #'(lambda () :action #'gptel-org-tools--heading))
(concat
(buffer-substring-no-properties
(line-beginning-position)
(line-end-position)))))))
(add-to-list 'gptel-org-tools (add-to-list 'gptel-org-tools
@ -261,21 +265,14 @@
:description "The name of the buffer. See the NAME column in ~emacs-list-buffers~.") :description "The name of the buffer. See the NAME column in ~emacs-list-buffers~.")
'(:name "query" '(:name "query"
:type string :type string
:description "The string to match entry headings against.")) :description "The keyword to match entry headings against."))
:category "org-ql")) :category "org-ql"))
(defun gptel-org-tools--org-ql-select-tags-local (buf query) (defun gptel-org-tools--org-ql-select-tags-local (buf query)
(let ((org-ql-cache (make-hash-table)))
(org-ql-select (org-ql-select
(get-buffer buf) (get-buffer buf)
`(tags-local ,query) `(tags-local ,query)
:action #'(lambda () :action #'gptel-org-tools-heading-body))
(concat
(buffer-substring-no-properties
(line-beginning-position)
(progn
(outline-next-heading)
(line-beginning-position))))))))
(add-to-list 'gptel-org-tools (add-to-list 'gptel-org-tools
@ -292,18 +289,10 @@
:category "org-ql")) :category "org-ql"))
(defun gptel-org-tools--org-ql-select-tags (buf query) (defun gptel-org-tools--org-ql-select-tags (buf query)
(let ((org-ql-cache (make-hash-table)))
(org-ql-select (org-ql-select
(get-buffer buf) (get-buffer buf)
`(tags ,query) `(tags ,query)
:action #'(lambda () :action #'gptel-org-tools--heading-body))
(concat
(buffer-substring-no-properties
(line-beginning-position)
(progn
(outline-next-heading)
(line-beginning-position))))))))
(add-to-list 'gptel-org-tools (add-to-list 'gptel-org-tools
(gptel-make-tool (gptel-make-tool
@ -315,23 +304,16 @@
:description "The name of the buffer. See the NAME column in `emacs-list-buffers`. Using filename fails.") :description "The name of the buffer. See the NAME column in `emacs-list-buffers`. Using filename fails.")
'(:name "query" '(:name "query"
:type string :type string
:description "The string to match entry headings against.")) :description "The keyword to match entry headings against."))
:category "org-ql")) :category "org-ql"))
(defun hash-gptl-org-tools--org-ql-select-rifle () (defun gptel-org-tools--org-ql-select-rifle (buf query)
(let ((org-ql-cache (make-hash-table)) (let ((buffer (get-buffer buf)))
(buffer (get-buffer buf)))
(if buffer (if buffer
(org-ql-select (org-ql-select
buffer buffer
`(rifle ,query) `(rifle ,query)
:action #'(lambda () :action #'gptel-org-tools--heading-subtree)
(concat
(buffer-substring-no-properties
(line-beginning-position)
(progn
(outline-next-heading)
(line-beginning-position))))))
(message "Buffer '%s' does not exist." buf)))) (message "Buffer '%s' does not exist." buf))))
(add-to-list 'gptel-org-tools (add-to-list 'gptel-org-tools
@ -344,21 +326,14 @@
:description "The name of the buffer. See the NAME column in `emacs-list-buffers`. Using filename fails.") :description "The name of the buffer. See the NAME column in `emacs-list-buffers`. Using filename fails.")
'(:name "query" '(:name "query"
:type string :type string
:description "The string to match entry headings and content against.")) :description "A single keyword to search for."))
:category "org-ql")) :category "org-ql"))
(defun gptel-org-tools--org-ql-select-agenda-tags (query) (defun gptel-org-tools--org-ql-select-agenda-tags-local (query)
(let ((org-ql-cache (make-hash-table)))
(org-ql-select (org-ql-select
(org-agenda-files) (org-agenda-files)
`(tags-local ,query) `(tags-local ,query)
:action #'(lambda () :action #'gptel-org-tools--heading-body))
(concat
(buffer-substring-no-properties
(line-beginning-position)
(progn
(outline-next-heading)
(line-beginning-position))))))))
(add-to-list 'gptel-org-tools (add-to-list 'gptel-org-tools
@ -368,22 +343,14 @@
:description "Run simple word query against all files in (org-agenda-files). WITHOUT tag inheritance, only directly tagged headings." :description "Run simple word query against all files in (org-agenda-files). WITHOUT tag inheritance, only directly tagged headings."
:args (list '(:name "query" :args (list '(:name "query"
:type string :type string
:description "The string to match entry tags against.")) :description "A single word to scan for."))
:category "org-ql")) :category "org-ql"))
(defun gptel-org-tools--org-ql-select-agenda-tags (query) (defun gptel-org-tools--org-ql-select-agenda-tags (query)
(let ((org-ql-cache (make-hash-table)))
(org-ql-select (org-ql-select
(org-agenda-files) (org-agenda-files)
`(tags ,query) `(tags ,query)
:action #'(lambda () :action #'gptel-org-tools--heading-subtree))
(concat
(buffer-substring-no-properties
(line-beginning-position)
(progn
(outline-next-heading)
(line-beginning-position))))))))
(add-to-list 'gptel-org-tools (add-to-list 'gptel-org-tools
(gptel-make-tool (gptel-make-tool
@ -392,21 +359,14 @@
:description "Run simple word query against all files in (org-agenda-files). WITH tag inheritance." :description "Run simple word query against all files in (org-agenda-files). WITH tag inheritance."
:args (list '(:name "query" :args (list '(:name "query"
:type string :type string
:description "The string to match entry tags against.")) :description "A single word to scan for."))
:category "org-ql")) :category "org-ql"))
(defun gptel-org-tools--org-ql-select-agenda-rifle (query) (defun gptel-org-tools--org-ql-select-agenda-rifle (query)
(let ((org-ql-cache (make-hash-table)))
(org-ql-select (org-ql-select
(org-agenda-files) (org-agenda-files)
`(rifle ,query) `(rifle ,query)
:action #'(lambda () :action #'gptel-org-tools--heading-subtree))
(concat
(buffer-substring-no-properties
(line-beginning-position)
(progn
(outline-next-heading)
(line-beginning-position))))))))
(add-to-list 'gptel-org-tools (add-to-list 'gptel-org-tools
(gptel-make-tool (gptel-make-tool
@ -415,7 +375,7 @@
:description "Run simple word query against all files in (org-agenda-files)" :description "Run simple word query against all files in (org-agenda-files)"
:args (list '(:name "query" :args (list '(:name "query"
:type string :type string
:description "The string to match entry headings and content against.")) :description "The keyword to match entry headings and content against."))
:category "org-ql")) :category "org-ql"))
(provide 'gptel-org-tools) (provide 'gptel-org-tools)