From 7fba16a02ef63a08e7fdf74bdde04fe607f15a91 Mon Sep 17 00:00:00 2001 From: Phil Bajsicki <phil@bajsicki.com> Date: Thu, 17 Apr 2025 02:34:58 +0200 Subject: [PATCH] Add regexp tool --- README.org | 72 ++++++++++++++++---------- gptel-org-tools.el | 126 +++++++++++++++++++++++++-------------------- 2 files changed, 113 insertions(+), 85 deletions(-) diff --git a/README.org b/README.org index e3a8660..cf268ef 100644 --- a/README.org +++ b/README.org @@ -517,7 +517,7 @@ But, any customizations to tweak this is left to the user, as everyone has their #+begin_src elisp -(defun gptel-org-tools--org-ql-select-dates (buf date) +(defun gptel-org-tools--org-ql-select-by-date (buf date) (org-ql-select (get-buffer buf) `(heading ,date) @@ -526,18 +526,18 @@ But, any customizations to tweak this is left to the user, as everyone has their (add-to-list 'gptel-org-tools (gptel-make-tool - :function #'gptel-org-tools--org-ql-select-dates - :name "org-ql-select-dates" - :description "Extract org subtree by date in YYYY or YYYY-MM format. Prefer using this first when request specifies for time periods." + :function #'gptel-org-tools--org-ql-select-by-date + :name "org-ql-select-by-date" + :description "Returns all timestamped headings matching query. Query may be: YYYY, YYYY-MM, YYYY-MM-DD. Prefer using this first when request specifies any time periods. Example: get all headings matching March 2025: \"2025-03\"" :args (list '(:name "buffer" :type string :description "Buffer name.") '(:name "date" :type string - :description "Date in YYYY or YYYY-MM format. Can add multiple like so: \"YYYY-MM\" \"YYYY-MM\"")) + :description "Date in YYYY or YYYY-MM format.")) :category "org")) #+end_src -***** org-agenda-fortnight +***** org-agenda-seek This is still work in progress, the idea is to have the LLM check my calendar and see what my plans are. I have not had time to really dig into this yet. It works, in principle, but I haven't been able to find a use for it yet. The real challenge is in building a context where the tools integrate with each-other in a way that makes sense. For now, this exists. @@ -550,8 +550,8 @@ It works, in principle, but I haven't been able to find a use for it yet. The re (let ((content (buffer-string))) (kill-buffer (current-buffer)) content))) - :name "org-agenda-fortnight" - :description "Get X days of user's org-agenda." + :name "org-agenda-seek" + :description "Get user's agenda/ tasking from now to X days in the past or future" :args (list '(:name "days" :type integer :description "Days. Positive = future. Negative = past. Default: 14")) @@ -569,11 +569,7 @@ Retrieve the headings where the heading matches query.. (org-ql-select (get-buffer buf) `(heading ,query) - :action #'(lambda () - (concat - (buffer-substring-no-properties - (line-beginning-position) - (line-end-position)))))) + :action #''gptel-org-tools--heading)) (add-to-list 'gptel-org-tools @@ -670,7 +666,7 @@ And, the "grab everything that matches" tool. (org-ql-select buffer `(rifle ,query) - :action #'gptel-org-tools--heading-subtree) + :action #'gptel-org-tools--heading-body) (message "Buffer '%s' does not exist." buf)))) (add-to-list 'gptel-org-tools @@ -687,10 +683,10 @@ And, the "grab everything that matches" tool. :category "org-ql")) #+end_src -***** org-ql-select-agenda-tags-local +***** org-ql-select-all-tags-local This pulls all the headings (and their contents) when they match tags (without inheritance.) #+begin_src elisp -(defun gptel-org-tools--org-ql-select-agenda-tags-local (query) +(defun gptel-org-tools--org-ql-select-all-tags-local (query) (org-ql-select (org-agenda-files) `(tags-local ,query) @@ -699,8 +695,8 @@ This pulls all the headings (and their contents) when they match tags (without i (add-to-list 'gptel-org-tools (gptel-make-tool - :function #'gptel-org-tools--org-ql-select-agenda-tags-local - :name "org-ql-select-agenda-tags-local" + :function #'gptel-org-tools--org-ql-select-all-tags-local + :name "org-ql-select-all-tags-local" :description "Run single word query against all files in (org-agenda-files). WITHOUT tag inheritance, only directly tagged headings." :args (list '(:name "query" :type string @@ -708,19 +704,19 @@ This pulls all the headings (and their contents) when they match tags (without i :category "org-ql")) #+end_src -***** org-ql-select-agenda-tags +***** org-ql-select-all-tags 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 -(defun gptel-org-tools--org-ql-select-agenda-tags (query) +(defun gptel-org-tools--org-ql-select-all-tags (query) (org-ql-select (org-agenda-files) `(tags ,query) - :action #'gptel-org-tools--heading-subtree)) + :action #'gptel-org-tools--heading-body)) (add-to-list 'gptel-org-tools (gptel-make-tool - :function #'gptel-org-tools--org-ql-select-agenda-tags - :name "org-ql-select-agenda-tags" + :function #'gptel-org-tools--org-ql-select-all-tags + :name "org-ql-select-all-tags" :description "Run single word query against all files in (org-agenda-files). WITH tag inheritance." :args (list '(:name "query" :type string @@ -728,7 +724,7 @@ This pulls all the headings (and their contents) when they match tags (with inhe :category "org-ql")) #+end_src -***** org-ql-select-agenda-rifle +***** org-ql-select-all-rifle And for the entire =(org-agenda-files)=. Note that I define my agenda in this way: @@ -740,16 +736,16 @@ Note that I define my agenda in this way: This means that /every org-mode file I have/ is part of this search. #+begin_src elisp -(defun gptel-org-tools--org-ql-select-agenda-rifle (query) +(defun gptel-org-tools--org-ql-select-all-rifle (query) (org-ql-select (org-agenda-files) `(rifle ,query) - :action #'gptel-org-tools--heading-subtree)) + :action #'gptel-org-tools--heading-body)) (add-to-list 'gptel-org-tools (gptel-make-tool - :function #'gptel-org-tools--org-ql-select-agenda-rifle - :name "org-ql-select-agenda-rifle" + :function #'gptel-org-tools--org-ql-select-all-rifle + :name "org-ql-select-all-rifle" :description "Run single word query against ALL org-mode files (notes)." :args (list '(:name "query" :type string @@ -758,6 +754,26 @@ This means that /every org-mode file I have/ is part of this search. #+end_src +***** org-ql-select-all-regexp +#+begin_src elisp +(defun gptel-org-tools--org-ql-select-all-rifle (query) + (org-ql-select + (org-agenda-files) + `(regexp ,query) + :action #'gptel-org-tools--heading-body)) + +(add-to-list 'gptel-org-tools + (gptel-make-tool + :function #'gptel-org-tools--org-ql-select-all-rifle + :name "org-ql-select-all-rifle" + :description "Run regexp on ALL files at once." + :args (list '(:name "query" + :type string + :description "Regexp, Emacs Lisp format.")) + :category "org-ql")) +#+end_src + + ** End #+begin_src elisp (provide 'gptel-org-tools) diff --git a/gptel-org-tools.el b/gptel-org-tools.el index fbf21cb..25c62ca 100644 --- a/gptel-org-tools.el +++ b/gptel-org-tools.el @@ -46,27 +46,27 @@ '"\n---\n")) (defun gptel-org-tools--heading-body () - (concat - (buffer-substring-no-properties - (line-beginning-position) - (progn - (outline-next-heading) - (line-beginning-position))) - "---\n")) + (concat + (buffer-substring-no-properties + (line-beginning-position) + (progn + (outline-next-heading) + (line-beginning-position))) + "---\n")) (defun gptel-org-tools--heading-subtree () - (concat - (buffer-substring-no-properties - (line-beginning-position) - (org-end-of-subtree)) - "---\n")) + (concat + (buffer-substring-no-properties + (line-beginning-position) + (org-end-of-subtree)) + "---\n")) (add-to-list 'gptel-org-tools (gptel-make-tool :function (lambda (arg) - (list-buffers-noselect) - (with-current-buffer "*Buffer List*" - (buffer-string))) + (list-buffers-noselect) + (with-current-buffer "*Buffer List*" + (buffer-string))) :name "list-buffers" :description "Access the list of buffers open in Emacs, including file names and full paths." :args (list '(:name "arg" @@ -107,11 +107,11 @@ (add-to-list 'gptel-org-tools (gptel-make-tool :function (lambda (file) - (with-current-buffer (get-buffer-create file) - (insert-file-contents file) - (concat - (current-buffer) - "\n---\nTool execution complete. Proceed with next step."))) + (with-current-buffer (get-buffer-create file) + (insert-file-contents file) + (concat + (current-buffer) + "\n---\nTool execution complete. Proceed with next step."))) :name "open-file-inactive" :description "Open the file in a background buffer. This doesn't interfere with the user." :args (list '(:name "file" @@ -173,11 +173,11 @@ (defun gptel-org-tools--org-extract-headings (buffer) (if (member buffer gptel-org-tools-skip-heading-extraction) (user-error "Buffer %s has too many headings, use org-extract-tags or org-ql-select-rifle." buffer) - (with-current-buffer buffer - (org-map-entries - #'gptel-org-tools--heading - t - 'file)))) + (with-current-buffer buffer + (org-map-entries + #'gptel-org-tools--heading + t + 'file)))) (add-to-list 'gptel-org-tools (gptel-make-tool @@ -189,7 +189,7 @@ :description "The Org buffer to extract headings from.")) :category "org-mode")) -(defun gptel-org-tools--org-ql-select-dates (buf date) +(defun gptel-org-tools--org-ql-select-by-date (buf date) (org-ql-select (get-buffer buf) `(heading ,date) @@ -198,15 +198,15 @@ (add-to-list 'gptel-org-tools (gptel-make-tool - :function #'gptel-org-tools--org-ql-select-dates - :name "org-ql-select-dates" - :description "Extract org subtree by date in YYYY or YYYY-MM format. Prefer using this first when request specifies for time periods." + :function #'gptel-org-tools--org-ql-select-by-date + :name "org-ql-select-by-date" + :description "Returns all timestamped headings matching query. Query may be: YYYY, YYYY-MM, YYYY-MM-DD. Prefer using this first when request specifies any time periods. Example: get all headings matching March 2025: \"2025-03\"" :args (list '(:name "buffer" :type string :description "Buffer name.") '(:name "date" :type string - :description "Date in YYYY or YYYY-MM format. Can add multiple like so: \"YYYY-MM\" \"YYYY-MM\"")) + :description "Date in YYYY or YYYY-MM format.")) :category "org")) (add-to-list 'gptel-org-tools @@ -217,22 +217,18 @@ (let ((content (buffer-string))) (kill-buffer (current-buffer)) content))) - :name "org-agenda-fortnight" - :description "Get X days of user's org-agenda." + :name "org-agenda-seek" + :description "Get user's agenda/ tasking from now to X days in the past or future" :args (list '(:name "days" :type integer :description "Days. Positive = future. Negative = past. Default: 14")) :category "org")) (defun gptel-org-tools--org-ql-select-headings (buf query) - (org-ql-select + (org-ql-select (get-buffer buf) `(heading ,query) - :action #'(lambda () - (concat - (buffer-substring-no-properties - (line-beginning-position) - (line-end-position)))))) + :action #''gptel-org-tools--heading)) (add-to-list 'gptel-org-tools @@ -249,10 +245,10 @@ :category "org-ql")) (defun gptel-org-tools--org-ql-select-headings-rifle (buf query) - (org-ql-select - (get-buffer buf) - `(rifle ,query) - :action #'gptel-org-tools--heading)) + (org-ql-select + (get-buffer buf) + `(rifle ,query) + :action #'gptel-org-tools--heading)) (add-to-list 'gptel-org-tools @@ -269,7 +265,7 @@ :category "org-ql")) (defun gptel-org-tools--org-ql-select-tags-local (buf query) - (org-ql-select + (org-ql-select (get-buffer buf) `(tags-local ,query) :action #'gptel-org-tools-heading-body)) @@ -289,7 +285,7 @@ :category "org-ql")) (defun gptel-org-tools--org-ql-select-tags (buf query) - (org-ql-select + (org-ql-select (get-buffer buf) `(tags ,query) :action #'gptel-org-tools--heading-body)) @@ -308,12 +304,12 @@ :category "org-ql")) (defun gptel-org-tools--org-ql-select-rifle (buf query) - (let ((buffer (get-buffer buf))) + (let ((buffer (get-buffer buf))) (if buffer (org-ql-select buffer `(rifle ,query) - :action #'gptel-org-tools--heading-subtree) + :action #'gptel-org-tools--heading-body) (message "Buffer '%s' does not exist." buf)))) (add-to-list 'gptel-org-tools @@ -329,7 +325,7 @@ :description "A single keyword to search for.")) :category "org-ql")) -(defun gptel-org-tools--org-ql-select-agenda-tags-local (query) +(defun gptel-org-tools--org-ql-select-all-tags-local (query) (org-ql-select (org-agenda-files) `(tags-local ,query) @@ -338,45 +334,61 @@ (add-to-list 'gptel-org-tools (gptel-make-tool - :function #'gptel-org-tools--org-ql-select-agenda-tags-local - :name "org-ql-select-agenda-tags-local" + :function #'gptel-org-tools--org-ql-select-all-tags-local + :name "org-ql-select-all-tags-local" :description "Run single word query against all files in (org-agenda-files). WITHOUT tag inheritance, only directly tagged headings." :args (list '(:name "query" :type string :description "A single word to scan for.")) :category "org-ql")) -(defun gptel-org-tools--org-ql-select-agenda-tags (query) +(defun gptel-org-tools--org-ql-select-all-tags (query) (org-ql-select (org-agenda-files) `(tags ,query) - :action #'gptel-org-tools--heading-subtree)) + :action #'gptel-org-tools--heading-body)) (add-to-list 'gptel-org-tools (gptel-make-tool - :function #'gptel-org-tools--org-ql-select-agenda-tags - :name "org-ql-select-agenda-tags" + :function #'gptel-org-tools--org-ql-select-all-tags + :name "org-ql-select-all-tags" :description "Run single word query against all files in (org-agenda-files). WITH tag inheritance." :args (list '(:name "query" :type string :description "A single word to scan for.")) :category "org-ql")) -(defun gptel-org-tools--org-ql-select-agenda-rifle (query) +(defun gptel-org-tools--org-ql-select-all-rifle (query) (org-ql-select (org-agenda-files) `(rifle ,query) - :action #'gptel-org-tools--heading-subtree)) + :action #'gptel-org-tools--heading-body)) (add-to-list 'gptel-org-tools (gptel-make-tool - :function #'gptel-org-tools--org-ql-select-agenda-rifle - :name "org-ql-select-agenda-rifle" + :function #'gptel-org-tools--org-ql-select-all-rifle + :name "org-ql-select-all-rifle" :description "Run single word query against ALL org-mode files (notes)." :args (list '(:name "query" :type string :description "The keyword to match entry headings and content against.")) :category "org-ql")) +(defun gptel-org-tools--org-ql-select-all-rifle (query) + (org-ql-select + (org-agenda-files) + `(regexp ,query) + :action #'gptel-org-tools--heading-body)) + +(add-to-list 'gptel-org-tools + (gptel-make-tool + :function #'gptel-org-tools--org-ql-select-all-rifle + :name "org-ql-select-all-rifle" + :description "Run regexp on ALL files at once." + :args (list '(:name "query" + :type string + :description "Regexp, Emacs Lisp format.")) + :category "org-ql")) + (provide 'gptel-org-tools) ;;; gptel-org-tools.el ends here