Bump version. Bunch of updates. Semi-stable?
This commit is contained in:
parent
18c52fa9d7
commit
d6332967c8
2 changed files with 319 additions and 221 deletions
289
README.org
289
README.org
|
@ -38,8 +38,7 @@ These are primarily for my own use. Please don't expect quality.
|
|||
This forge is for my personal use, and as such: [[https://bajsicki.com/contact/][contact]], just in case there's questions, issues, whatnot.
|
||||
|
||||
* My system
|
||||
I'm running LLMs exclusively locally, on the smaller side.
|
||||
With Qwen 14B, I'm getting reasonably good results with the following set-up:
|
||||
I'm running LLMs exclusively locally, on the smaller side. I'm getting reasonably good results with the following set-up:
|
||||
- Hardware: RX 7900XTX
|
||||
- Software:
|
||||
- [[https://github.com/ggml-org/llama.cpp][llama.cpp]] compiled with ROCm 6.4.
|
||||
|
@ -117,7 +116,7 @@ Stuff, headers, etc.
|
|||
;; Keywords: extensions, comm, tools, matching, convenience,
|
||||
;;
|
||||
;; Author: Phil Bajsicki <phil@bajsicki.com>
|
||||
;; Version: 0.0.1
|
||||
;; Version: 0.0.2
|
||||
;; Package-Requires: ((emacs "30.1") (gptel 0.9.8) (org-ql 0.9))
|
||||
;; URL: https://github.com/phil/gptel-org-tools
|
||||
;; SPDX-License-Identifier: GPL-3.0
|
||||
|
@ -181,23 +180,23 @@ These abstract away some of the tool definitions.
|
|||
*** Retrieve heading and body (without subheadings)
|
||||
#+begin_src elisp
|
||||
(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"))
|
||||
#+end_src
|
||||
|
||||
*** Retrieve heading and subheadings (until next same-level heading)
|
||||
#+begin_src elisp
|
||||
(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"))
|
||||
#+end_src
|
||||
** Note on org-ql (caching)
|
||||
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.
|
||||
|
@ -215,7 +214,6 @@ So for now, you can manually re-set the cache like so:
|
|||
** The tools
|
||||
*** Emacs
|
||||
These tools are primarily concerned with Emacs, Emacs Lisp, and files-or-buffers.
|
||||
|
||||
**** eval
|
||||
Dangerous, but occasionally useful for pure chaos and amusement...
|
||||
I would like to say. But in actuality, especially with the 'smarter' models, they can surprise with the varied approaches they have to problem-solving.
|
||||
|
@ -225,15 +223,17 @@ E.g. can't find the right ~org-ql-select~ query? Let's use an eldritch abominati
|
|||
Highly not recommended, but sometimes an LLM can pull a rabbit out of pure entropy.
|
||||
|
||||
#+begin_src elisp :tangle no
|
||||
(defun gptel-org-tools--eval (elisp)
|
||||
(unless (stringp elisp) (error "elisp code must be a string"))
|
||||
(with-temp-buffer
|
||||
(insert (eval (read elisp)))
|
||||
(buffer-string)))
|
||||
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function ((lambda () (interactive) ) (elisp)
|
||||
(unless (stringp elisp) (error "elisp code must be a string"))
|
||||
(with-temp-buffer
|
||||
(insert (eval (read elisp)))
|
||||
(buffer-string)))
|
||||
:name "eval"
|
||||
:description "Execute arbitrary Emacs Lisp code"
|
||||
:function #'gptel-org-tools--eval
|
||||
:name "gptel-org-tools--eval"
|
||||
:description "Execute Emacs Lisp code"
|
||||
:args (list '(:name "eval"
|
||||
:type string
|
||||
:description "The Emacs Lisp code to evaluate."))
|
||||
|
@ -248,13 +248,15 @@ The rationale behind using ~list-buffers~ is the same as with dired. They both d
|
|||
|
||||
Seems to be one of the most reliable tools in the basket... mostly because
|
||||
#+begin_src elisp
|
||||
(defun gptel-org-tool--list-buffers ()
|
||||
(list-buffers-noselect)
|
||||
(with-temp-buffer "*Buffer List*"
|
||||
(buffer-string)))
|
||||
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function (lambda (arg)
|
||||
(list-buffers-noselect)
|
||||
(with-current-buffer "*Buffer List*"
|
||||
(buffer-string)))
|
||||
:name "list-buffers"
|
||||
:function #'gptel-org-tool--list-buffers
|
||||
:name "gptel-org-tool--list-buffers"
|
||||
:description "Access the list of buffers open in Emacs, including file names and full paths."
|
||||
:args (list '(:name "arg"
|
||||
:type string
|
||||
|
@ -273,15 +275,17 @@ Be sure to customize the function to point to your org directory, if you wish. I
|
|||
#+end_comment
|
||||
|
||||
#+begin_src elisp
|
||||
(defun gptel-org-tools--dir (dir)
|
||||
(with-temp-buffer
|
||||
(dired (or dir "~"))
|
||||
(let ((content (buffer-string)))
|
||||
(kill-buffer (current-buffer))
|
||||
content)))
|
||||
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function (lambda (dir)
|
||||
(with-temp-buffer
|
||||
(dired (or dir "~"))
|
||||
(let ((content (buffer-string)))
|
||||
(kill-buffer (current-buffer))
|
||||
content)))
|
||||
:name "dired"
|
||||
:function #'gptel-org-tools--dir
|
||||
:name "gptel-org-tools--dir"
|
||||
:description "List directory contents"
|
||||
:args (list '(:name "dir"
|
||||
:type string
|
||||
|
@ -292,14 +296,17 @@ Be sure to customize the function to point to your org directory, if you wish. I
|
|||
|
||||
**** find-buffer-visiting
|
||||
Disabled for now, as it's causing some issues.
|
||||
#+begin_src elisp
|
||||
#+begin_src elisp :tangle no :results none
|
||||
(defun gptel-org-tools--find-buffer-visiting (filename)
|
||||
(concat
|
||||
(bufferp
|
||||
(find-buffer-visiting
|
||||
(expand-file-name filename)))))
|
||||
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function (lambda (filename)
|
||||
(concat
|
||||
(bufferp (find-buffer-visiting (expand-file-name filename)))
|
||||
"\n---\nTool execution complete. Proceed with next step."))
|
||||
:name "find-buffer-visiting"
|
||||
:function #'gptel-org-tools--find-buffer-visiting
|
||||
:name "gptel-org-tools--find-buffer-visiting"
|
||||
:description "Check if the file is open in a buffer. Usage (find-buffer-visiting filename)"
|
||||
:args (list '(:name "filename"
|
||||
:type string
|
||||
|
@ -310,15 +317,15 @@ Disabled for now, as it's causing some issues.
|
|||
**** open-file-inactive
|
||||
Continuation from above. Open a file into a buffer for processing, once it's found by dired-list.
|
||||
#+begin_src elisp
|
||||
(defun gptel-org-tools--open-file-inactive (file)
|
||||
(with-current-buffer (get-buffer-create file)
|
||||
(insert-file-contents file)
|
||||
(concat
|
||||
(current-buffer))))
|
||||
(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.")))
|
||||
:name "open-file-inactive"
|
||||
:function #'gptel-org-tools--open-file-inactive
|
||||
:name "gptel-org-tools--open-file-inactive"
|
||||
:description "Open the file in a background buffer. This doesn't interfere with the user."
|
||||
:args (list '(:name "file"
|
||||
:type string
|
||||
|
@ -329,15 +336,17 @@ Continuation from above. Open a file into a buffer for processing, once it's fou
|
|||
**** read-file-contents
|
||||
This reads file contents,
|
||||
#+begin_src elisp :tangle no
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function (lambda (filename)
|
||||
(with-temp-buffer
|
||||
(defun gptel-org-tools--read-file-contents (file)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents (expand-file-name filename))
|
||||
(concat
|
||||
(buffer-string)
|
||||
"\n---\nTool execution complete. Proceed with next step.")))
|
||||
:name "read-file-contents"
|
||||
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function #'gptel-org-tools--read-file-contents
|
||||
:name "gptel-org-tools--read-file-contents"
|
||||
:description "Read and return the contents of a specified file."
|
||||
:args (list '(:name "filename"
|
||||
:type string
|
||||
|
@ -347,14 +356,16 @@ This reads file contents,
|
|||
#+end_src
|
||||
**** describe-variable
|
||||
#+begin_src elisp
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function (lambda (var)
|
||||
(let ((symbol (intern var)))
|
||||
(defun gptel-org-tools--describe-variable (var)
|
||||
(let ((symbol (intern var)))
|
||||
(if (boundp symbol)
|
||||
(prin1-to-string (symbol-value symbol))
|
||||
(format "Variable %s is not bound." var))))
|
||||
:name "describe-variable"
|
||||
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function #'gptel-org-tools--describe-variable
|
||||
:name "gptel-org-tools--describe-variable"
|
||||
:description "See variable contents"
|
||||
:args (list '(:name "var"
|
||||
:type string
|
||||
|
@ -364,14 +375,16 @@ This reads file contents,
|
|||
|
||||
**** describe-function
|
||||
#+begin_src elisp
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function (lambda (fun)
|
||||
(let ((symbol (intern fun)))
|
||||
(defun gptel-org-tools--describe-function (fun)
|
||||
(let ((symbol (intern fun)))
|
||||
(if (fboundp symbol)
|
||||
(prin1-to-string (documentation symbol 'function))
|
||||
(format "Function %s is not defined." fun))))
|
||||
:name "describe-function"
|
||||
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function #'gptel-org-tools--describe-function
|
||||
:name "gptel-org-tools--describe-function"
|
||||
:description "See function description"
|
||||
:args (list '(:name "fun"
|
||||
:type string
|
||||
|
@ -425,7 +438,7 @@ This is not, by any means, sufficient, but I do tag people and specific events f
|
|||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function #'gptel-org-tools--org-extract-tags
|
||||
:name "org-extract-tags"
|
||||
:name "gptel-org-tools--org-extract-tags"
|
||||
:description "Extract all unique tags from an org-mode buffer"
|
||||
:args (list '(:name "buffer"
|
||||
:type string
|
||||
|
@ -442,16 +455,16 @@ Therefore, headings. A reasonable amount of information, and still keeping the s
|
|||
(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
|
||||
:function #'gptel-org-tools--org-extract-headings
|
||||
:name "org-extract-headings"
|
||||
:name "gptel-org-tools--org-extract-headings"
|
||||
:description "Extract all headings from an org-mode buffer"
|
||||
:args (list '(:name "buffer"
|
||||
:type string
|
||||
|
@ -485,7 +498,7 @@ Currently *not* tangled, as I'm testing breaking out each type of query into its
|
|||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function #'gptel-org-tools--org-ql-select
|
||||
:name "org-ql-select"
|
||||
:name "gptel-org-tools--org-ql-select"
|
||||
:description "Run org-ql-select against buffer with query. Using filename fails."
|
||||
:args (list '(:name "buffer"
|
||||
:type string
|
||||
|
@ -516,7 +529,41 @@ Notice it pulls to the end of the subtree. So for months, that's at least 28 ent
|
|||
But, any customizations to tweak this is left to the user, as everyone has their own conventions.
|
||||
|
||||
|
||||
Testing new version:
|
||||
#+begin_src elisp
|
||||
(defun gptel-org-tools--org-ql-select-by-date (buf date)
|
||||
"Returns all timestamped headings matching the specified date or date range.
|
||||
The date can be in the format YYYY, YYYY-MM, or YYYY-MM-DD.
|
||||
|
||||
BUFFER is the name of the buffer to search.
|
||||
DATE is the date or date range to match."
|
||||
(let* ((buffer (get-buffer buf))
|
||||
(mode (buffer-local-value 'major-mode buffer)))
|
||||
(if buffer
|
||||
(if (eq mode 'org-mode)
|
||||
(org-ql-select buffer
|
||||
`(heading ,date)
|
||||
:action #'gptel-org-tools--heading-subtree)
|
||||
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
||||
(message "Buffer '%s' does not exist." buf))))
|
||||
|
||||
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function #'gptel-org-tools--org-ql-select-by-date
|
||||
:name "gptel-org-tools--org-ql-select-by-date"
|
||||
:description "Returns all timestamped headings matching query. Query may be: YYYY, YYYY-MM, YYYY-MM-DD."
|
||||
:args (list '(:name "buffer"
|
||||
:type string
|
||||
:description "Buffer name.")
|
||||
'(:name "date"
|
||||
:type string
|
||||
:description "Date in YYYY or YYYY-MM format."))
|
||||
:category "org"))
|
||||
#+end_src
|
||||
|
||||
Original (works but not ideal).
|
||||
#+begin_src elisp :tangle no
|
||||
(defun gptel-org-tools--org-ql-select-by-date (buf date)
|
||||
(org-ql-select
|
||||
(get-buffer buf)
|
||||
|
@ -527,7 +574,7 @@ 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-by-date
|
||||
:name "org-ql-select-by-date"
|
||||
:name "gptel-org-tools--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
|
||||
|
@ -542,16 +589,18 @@ This is still work in progress, the idea is to have the LLM check my calendar an
|
|||
|
||||
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.
|
||||
#+begin_src elisp
|
||||
(defun gptel-org-tools--org-agenda-seek (days)
|
||||
(with-temp-buffer
|
||||
(org-agenda-list (or days 14))
|
||||
(let ((content (buffer-string)))
|
||||
(kill-buffer (current-buffer))
|
||||
content)))
|
||||
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function (lambda (days)
|
||||
(with-temp-buffer
|
||||
(org-agenda-list (or days 14))
|
||||
(let ((content (buffer-string)))
|
||||
(kill-buffer (current-buffer))
|
||||
content)))
|
||||
:name "org-agenda-seek"
|
||||
:description "Get user's agenda/ tasking from now to X days in the past or future"
|
||||
:function #'gptel-org-tools--org-agenda-seek
|
||||
:name "gptel-org-tools--org-agenda-seek"
|
||||
:description "Get user's agenda (tasking) spanning X days from now. This can be used to get all agenda tasks that are due or scheduled in the next X days, or in the past X days, depending on whether the days argument is positive or negative. Example: get all agenda tasks due in the next 7 days: \"7\""
|
||||
:args (list '(:name "days"
|
||||
:type integer
|
||||
:description "Days. Positive = future. Negative = past. Default: 14"))
|
||||
|
@ -566,7 +615,7 @@ The following tools are still very much WIP, and I think they're self-explanator
|
|||
Retrieve the headings where the heading matches query..
|
||||
#+begin_src elisp
|
||||
(defun gptel-org-tools--org-ql-select-headings (buf query)
|
||||
(org-ql-select
|
||||
(org-ql-select
|
||||
(get-buffer buf)
|
||||
`(heading ,query)
|
||||
:action #''gptel-org-tools--heading))
|
||||
|
@ -575,14 +624,14 @@ Retrieve the headings where the heading matches query..
|
|||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function #'gptel-org-tools--org-ql-select-headings
|
||||
:name "org-ql-select-headings"
|
||||
:name "gptel-org-tools--org-ql-select-headings"
|
||||
:description "Retreive matching headings from buffer. Matches only a single string. Using filename fails."
|
||||
:args (list '(:name "buffer"
|
||||
:type string
|
||||
:description "The name of the buffer. See the NAME column in ~emacs-list-buffers~.")
|
||||
'(:name "query"
|
||||
:type string
|
||||
:description "The keyword to match entry headings against."))
|
||||
:description "The string to match entry headings against."))
|
||||
:category "org-ql"))
|
||||
#+end_src
|
||||
|
||||
|
@ -590,23 +639,23 @@ Retrieve the headings where the heading matches query..
|
|||
Retrieve all the headings where either heading or content matches query.
|
||||
#+begin_src elisp
|
||||
(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
|
||||
(gptel-make-tool
|
||||
:function #'gptel-org-tools--org-ql-select-headings-rifle
|
||||
:name "org-ql-select-headings-rifle"
|
||||
:name "gptel-org-tools--org-ql-select-headings-rifle"
|
||||
:description "Retreive headings from buffer using org-ql-select. Matches against both heading and content. Using filename fails."
|
||||
:args (list '(:name "buffer"
|
||||
:type string
|
||||
:description "The name of the buffer. See the NAME column in ~emacs-list-buffers~.")
|
||||
'(:name "query"
|
||||
:type string
|
||||
:description "The keyword to match entry headings against."))
|
||||
:description "The string to match entry headings against."))
|
||||
:category "org-ql"))
|
||||
#+end_src
|
||||
|
||||
|
@ -614,16 +663,16 @@ 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.)
|
||||
#+begin_src elisp
|
||||
(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))
|
||||
:action #'gptel-org-tools--heading-body))
|
||||
|
||||
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function #'gptel-org-tools--org-ql-select-tags-local
|
||||
:name "org-ql-select-tags-local"
|
||||
:name "gptel-org-tools--org-ql-select-tags-local"
|
||||
:description "Run org-ql-select-tags-local against buffer with query. No tag inheritance."
|
||||
:args (list '(:name "buffer"
|
||||
:type string
|
||||
|
@ -634,11 +683,35 @@ This pulls all the headings (and their contents) when they match tags (without i
|
|||
:category "org-ql"))
|
||||
#+end_src
|
||||
|
||||
***** org-ql-select-tags-local-count
|
||||
This pulls all the local tags (without inheritance) from buffer, and returns the number of these tagged headings.
|
||||
#+begin_src elisp
|
||||
(defun gptel-org-tools--org-ql-select-tags-local (buf query)
|
||||
(length (org-ql-select
|
||||
(get-buffer buf)
|
||||
`(tags-local ,query)
|
||||
:action #'gptel-org-tools--heading-body)))
|
||||
|
||||
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function #'gptel-org-tools--org-ql-select-tags-local
|
||||
:name "gptel-org-tools--org-ql-select-tags-local"
|
||||
:description "Get count of matching tags from buffer. No tag inheritance."
|
||||
:args (list '(:name "buffer"
|
||||
:type string
|
||||
:description "The name of the buffer. See the NAME column in `emacs-list-buffers`. Using filename fails.")
|
||||
'(:name "query"
|
||||
:type string
|
||||
:description "The string match entry tags against."))
|
||||
:category "org-ql"))
|
||||
#+end_src
|
||||
|
||||
***** org-ql-select-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-tags (buf query)
|
||||
(org-ql-select
|
||||
(org-ql-select
|
||||
(get-buffer buf)
|
||||
`(tags ,query)
|
||||
:action #'gptel-org-tools--heading-body))
|
||||
|
@ -646,14 +719,14 @@ This pulls all the headings (and their contents) when they match tags (with inhe
|
|||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function #'gptel-org-tools--org-ql-select-tags
|
||||
:name "org-ql-select-tags"
|
||||
:name "gptel-org-tools--org-ql-select-tags"
|
||||
:description "Run org-ql-select-tags against buffer with query. Supports tag inheritance."
|
||||
:args (list '(:name "buffer"
|
||||
:type string
|
||||
:description "The name of the buffer. See the NAME column in `emacs-list-buffers`. Using filename fails.")
|
||||
'(:name "query"
|
||||
:type string
|
||||
:description "The keyword to match entry headings against."))
|
||||
:description "The string to match entry headings against."))
|
||||
:category "org-ql"))
|
||||
#+end_src
|
||||
|
||||
|
@ -661,7 +734,7 @@ This pulls all the headings (and their contents) when they match tags (with inhe
|
|||
And, the "grab everything that matches" tool.
|
||||
#+begin_src elisp
|
||||
(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
|
||||
|
@ -672,14 +745,14 @@ And, the "grab everything that matches" tool.
|
|||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function #'gptel-org-tools--org-ql-select-rifle
|
||||
:name "org-ql-select-rifle"
|
||||
:name "gptel-org-tools--org-ql-select-rifle"
|
||||
:description "Run org-ql-select-rifle against buffer with query."
|
||||
:args (list '(:name "buffer"
|
||||
:type string
|
||||
:description "The name of the buffer. See the NAME column in `emacs-list-buffers`. Using filename fails.")
|
||||
'(:name "query"
|
||||
:type string
|
||||
:description "A single keyword to search for."))
|
||||
:description "A single string to search for."))
|
||||
:category "org-ql"))
|
||||
#+end_src
|
||||
|
||||
|
@ -696,8 +769,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-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."
|
||||
:name "gptel-org-tools--org-ql-select-all-tags-local"
|
||||
:description "Run single string 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."))
|
||||
|
@ -716,11 +789,11 @@ This pulls all the headings (and their contents) when they match tags (with inhe
|
|||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
: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."
|
||||
:name "gptel-org-tools--org-ql-select-all-tags"
|
||||
:description "Run single string query against all files in (org-agenda-files). WITH tag inheritance."
|
||||
:args (list '(:name "query"
|
||||
:type string
|
||||
:description "A single word to scan for."))
|
||||
:description "A simple (single) string to scan for."))
|
||||
:category "org-ql"))
|
||||
#+end_src
|
||||
|
||||
|
@ -745,11 +818,11 @@ This means that /every org-mode file I have/ is part of this search.
|
|||
(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 single word query against ALL org-mode files (notes)."
|
||||
:name "gptel-org-tools--org-ql-select-all-rifle"
|
||||
:description "Run simple (single) string against ALL org-mode files (notes)."
|
||||
:args (list '(:name "query"
|
||||
:type string
|
||||
:description "The keyword to match entry headings and content against."))
|
||||
:description "The string to match entry headings and content against."))
|
||||
:category "org-ql"))
|
||||
#+end_src
|
||||
|
||||
|
@ -762,13 +835,11 @@ This means that /every org-mode file I have/ is part of this search.
|
|||
(org-agenda-files)
|
||||
`(regexp ,bound-query)
|
||||
:action #'gptel-org-tools--heading-body)))
|
||||
#+end_src
|
||||
|
||||
#+begin_src elisp
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function #'gptel-org-tools--org-ql-select-all-regexp
|
||||
:name "org-ql-select-all-regexp"
|
||||
:name "gptel-org-tools--org-ql-select-all-regexp"
|
||||
:description "Run regexp on ALL files at once."
|
||||
:args (list '(:name "query"
|
||||
:type string
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
;; Keywords: extensions, comm, tools, matching, convenience,
|
||||
;;
|
||||
;; Author: Phil Bajsicki <phil@bajsicki.com>
|
||||
;; Version: 0.0.1
|
||||
;; Version: 0.0.2
|
||||
;; Package-Requires: ((emacs "30.1") (gptel 0.9.8) (org-ql 0.9))
|
||||
;; URL: https://github.com/phil/gptel-org-tools
|
||||
;; SPDX-License-Identifier: GPL-3.0
|
||||
|
@ -46,28 +46,30 @@
|
|||
'"\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"))
|
||||
|
||||
(defun gptel-org-tool--list-buffers ()
|
||||
(list-buffers-noselect)
|
||||
(with-temp-buffer "*Buffer List*"
|
||||
(buffer-string)))
|
||||
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function (lambda (arg)
|
||||
(list-buffers-noselect)
|
||||
(with-current-buffer "*Buffer List*"
|
||||
(buffer-string)))
|
||||
:name "list-buffers"
|
||||
:function #'gptel-org-tool--list-buffers
|
||||
:name "gptel-org-tool--list-buffers"
|
||||
:description "Access the list of buffers open in Emacs, including file names and full paths."
|
||||
:args (list '(:name "arg"
|
||||
:type string
|
||||
|
@ -75,15 +77,17 @@
|
|||
:optional t))
|
||||
:category "emacs"))
|
||||
|
||||
(defun gptel-org-tools--dir (dir)
|
||||
(with-temp-buffer
|
||||
(dired (or dir "~"))
|
||||
(let ((content (buffer-string)))
|
||||
(kill-buffer (current-buffer))
|
||||
content)))
|
||||
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function (lambda (dir)
|
||||
(with-temp-buffer
|
||||
(dired (or dir "~"))
|
||||
(let ((content (buffer-string)))
|
||||
(kill-buffer (current-buffer))
|
||||
content)))
|
||||
:name "dired"
|
||||
:function #'gptel-org-tools--dir
|
||||
:name "gptel-org-tools--dir"
|
||||
:description "List directory contents"
|
||||
:args (list '(:name "dir"
|
||||
:type string
|
||||
|
@ -91,56 +95,47 @@
|
|||
:optional t))
|
||||
:category "filesystem"))
|
||||
|
||||
(defun gptel-org-tools--open-file-inactive (file)
|
||||
(with-current-buffer (get-buffer-create file)
|
||||
(insert-file-contents file)
|
||||
(concat
|
||||
(current-buffer))))
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function (lambda (filename)
|
||||
(concat
|
||||
(bufferp (find-buffer-visiting (expand-file-name filename)))
|
||||
"\n---\nTool execution complete. Proceed with next step."))
|
||||
:name "find-buffer-visiting"
|
||||
:description "Check if the file is open in a buffer. Usage (find-buffer-visiting filename)"
|
||||
:args (list '(:name "filename"
|
||||
:type string
|
||||
:description "The filename to compare to open buffers."))
|
||||
:category "org-mode"))
|
||||
|
||||
(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.")))
|
||||
:name "open-file-inactive"
|
||||
:function #'gptel-org-tools--open-file-inactive
|
||||
:name "gptel-org-tools--open-file-inactive"
|
||||
:description "Open the file in a background buffer. This doesn't interfere with the user."
|
||||
:args (list '(:name "file"
|
||||
:type string
|
||||
:description "Path to file.."))
|
||||
:category "filesystem"))
|
||||
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function (lambda (var)
|
||||
(let ((symbol (intern var)))
|
||||
(defun gptel-org-tools--describe-variable (var)
|
||||
(let ((symbol (intern var)))
|
||||
(if (boundp symbol)
|
||||
(prin1-to-string (symbol-value symbol))
|
||||
(format "Variable %s is not bound." var))))
|
||||
:name "describe-variable"
|
||||
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function #'gptel-org-tools--describe-variable
|
||||
:name "gptel-org-tools--describe-variable"
|
||||
:description "See variable contents"
|
||||
:args (list '(:name "var"
|
||||
:type string
|
||||
:description "Variable name"))
|
||||
:category "emacs"))
|
||||
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function (lambda (fun)
|
||||
(let ((symbol (intern fun)))
|
||||
(defun gptel-org-tools--describe-function (fun)
|
||||
(let ((symbol (intern fun)))
|
||||
(if (fboundp symbol)
|
||||
(prin1-to-string (documentation symbol 'function))
|
||||
(format "Function %s is not defined." fun))))
|
||||
:name "describe-function"
|
||||
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function #'gptel-org-tools--describe-function
|
||||
:name "gptel-org-tools--describe-function"
|
||||
:description "See function description"
|
||||
:args (list '(:name "fun"
|
||||
:type string
|
||||
|
@ -163,7 +158,7 @@
|
|||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function #'gptel-org-tools--org-extract-tags
|
||||
:name "org-extract-tags"
|
||||
:name "gptel-org-tools--org-extract-tags"
|
||||
:description "Extract all unique tags from an org-mode buffer"
|
||||
:args (list '(:name "buffer"
|
||||
:type string
|
||||
|
@ -173,16 +168,16 @@
|
|||
(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
|
||||
:function #'gptel-org-tools--org-extract-headings
|
||||
:name "org-extract-headings"
|
||||
:name "gptel-org-tools--org-extract-headings"
|
||||
:description "Extract all headings from an org-mode buffer"
|
||||
:args (list '(:name "buffer"
|
||||
:type string
|
||||
|
@ -190,42 +185,54 @@
|
|||
:category "org-mode"))
|
||||
|
||||
(defun gptel-org-tools--org-ql-select-by-date (buf date)
|
||||
(org-ql-select
|
||||
(get-buffer buf)
|
||||
`(heading ,date)
|
||||
:action #'gptel-org-tools--heading-subtree))
|
||||
"Returns all timestamped headings matching the specified date or date range.
|
||||
The date can be in the format YYYY, YYYY-MM, or YYYY-MM-DD.
|
||||
|
||||
BUFFER is the name of the buffer to search.
|
||||
DATE is the date or date range to match."
|
||||
(let* ((buffer (get-buffer buf))
|
||||
(mode (buffer-local-value 'major-mode buffer)))
|
||||
(if buffer
|
||||
(if (eq mode 'org-mode)
|
||||
(org-ql-select buffer
|
||||
`(heading ,date)
|
||||
:action #'gptel-org-tools--heading-subtree)
|
||||
(message "Buffer '%s' isn't an org-mode buffer." buf))
|
||||
(message "Buffer '%s' does not exist." buf))))
|
||||
|
||||
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
: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."))
|
||||
:category "org"))
|
||||
(gptel-make-tool
|
||||
:function #'gptel-org-tools--org-ql-select-by-date
|
||||
:name "gptel-org-tools--org-ql-select-by-date"
|
||||
:description "Returns all timestamped headings matching query. Query may be: YYYY, YYYY-MM, YYYY-MM-DD."
|
||||
:args (list '(:name "buffer"
|
||||
:type string
|
||||
:description "Buffer name.")
|
||||
'(:name "date"
|
||||
:type string
|
||||
:description "Date in YYYY or YYYY-MM format."))
|
||||
:category "org"))
|
||||
|
||||
(defun gptel-org-tools--org-agenda-seek (days)
|
||||
(with-temp-buffer
|
||||
(org-agenda-list (or days 14))
|
||||
(let ((content (buffer-string)))
|
||||
(kill-buffer (current-buffer))
|
||||
content)))
|
||||
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function (lambda (days)
|
||||
(with-temp-buffer
|
||||
(org-agenda-list (or days 14))
|
||||
(let ((content (buffer-string)))
|
||||
(kill-buffer (current-buffer))
|
||||
content)))
|
||||
:name "org-agenda-seek"
|
||||
:description "Get user's agenda/ tasking from now to X days in the past or future"
|
||||
:function #'gptel-org-tools--org-agenda-seek
|
||||
:name "gptel-org-tools--org-agenda-seek"
|
||||
:description "Get user's agenda (tasking) spanning X days from now. This can be used to get all agenda tasks that are due or scheduled in the next X days, or in the past X days, depending on whether the days argument is positive or negative. Example: get all agenda tasks due in the next 7 days: \"7\""
|
||||
: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 #''gptel-org-tools--heading))
|
||||
|
@ -234,47 +241,47 @@
|
|||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function #'gptel-org-tools--org-ql-select-headings
|
||||
:name "org-ql-select-headings"
|
||||
:name "gptel-org-tools--org-ql-select-headings"
|
||||
:description "Retreive matching headings from buffer. Matches only a single string. Using filename fails."
|
||||
:args (list '(:name "buffer"
|
||||
:type string
|
||||
:description "The name of the buffer. See the NAME column in ~emacs-list-buffers~.")
|
||||
'(:name "query"
|
||||
:type string
|
||||
:description "The keyword to match entry headings against."))
|
||||
:description "The string to match entry headings against."))
|
||||
: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
|
||||
(gptel-make-tool
|
||||
:function #'gptel-org-tools--org-ql-select-headings-rifle
|
||||
:name "org-ql-select-headings-rifle"
|
||||
:name "gptel-org-tools--org-ql-select-headings-rifle"
|
||||
:description "Retreive headings from buffer using org-ql-select. Matches against both heading and content. Using filename fails."
|
||||
:args (list '(:name "buffer"
|
||||
:type string
|
||||
:description "The name of the buffer. See the NAME column in ~emacs-list-buffers~.")
|
||||
'(:name "query"
|
||||
:type string
|
||||
:description "The keyword to match entry headings against."))
|
||||
:description "The string to match entry headings against."))
|
||||
: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))
|
||||
:action #'gptel-org-tools--heading-body))
|
||||
|
||||
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function #'gptel-org-tools--org-ql-select-tags-local
|
||||
:name "org-ql-select-tags-local"
|
||||
:name "gptel-org-tools--org-ql-select-tags-local"
|
||||
:description "Run org-ql-select-tags-local against buffer with query. No tag inheritance."
|
||||
:args (list '(:name "buffer"
|
||||
:type string
|
||||
|
@ -284,8 +291,28 @@
|
|||
:description "The string match entry tags against."))
|
||||
:category "org-ql"))
|
||||
|
||||
(defun gptel-org-tools--org-ql-select-tags-local (buf query)
|
||||
(length (org-ql-select
|
||||
(get-buffer buf)
|
||||
`(tags-local ,query)
|
||||
:action #'gptel-org-tools--heading-body)))
|
||||
|
||||
|
||||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function #'gptel-org-tools--org-ql-select-tags-local
|
||||
:name "gptel-org-tools--org-ql-select-tags-local"
|
||||
:description "Get count of matching tags from buffer. No tag inheritance."
|
||||
:args (list '(:name "buffer"
|
||||
:type string
|
||||
:description "The name of the buffer. See the NAME column in `emacs-list-buffers`. Using filename fails.")
|
||||
'(:name "query"
|
||||
:type string
|
||||
:description "The string match entry tags against."))
|
||||
: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))
|
||||
|
@ -293,18 +320,18 @@
|
|||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function #'gptel-org-tools--org-ql-select-tags
|
||||
:name "org-ql-select-tags"
|
||||
:name "gptel-org-tools--org-ql-select-tags"
|
||||
:description "Run org-ql-select-tags against buffer with query. Supports tag inheritance."
|
||||
:args (list '(:name "buffer"
|
||||
:type string
|
||||
:description "The name of the buffer. See the NAME column in `emacs-list-buffers`. Using filename fails.")
|
||||
'(:name "query"
|
||||
:type string
|
||||
:description "The keyword to match entry headings against."))
|
||||
:description "The string to match entry headings against."))
|
||||
: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
|
||||
|
@ -315,14 +342,14 @@
|
|||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function #'gptel-org-tools--org-ql-select-rifle
|
||||
:name "org-ql-select-rifle"
|
||||
:name "gptel-org-tools--org-ql-select-rifle"
|
||||
:description "Run org-ql-select-rifle against buffer with query."
|
||||
:args (list '(:name "buffer"
|
||||
:type string
|
||||
:description "The name of the buffer. See the NAME column in `emacs-list-buffers`. Using filename fails.")
|
||||
'(:name "query"
|
||||
:type string
|
||||
:description "A single keyword to search for."))
|
||||
:description "A single string to search for."))
|
||||
:category "org-ql"))
|
||||
|
||||
(defun gptel-org-tools--org-ql-select-all-tags-local (query)
|
||||
|
@ -335,8 +362,8 @@
|
|||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
: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."
|
||||
:name "gptel-org-tools--org-ql-select-all-tags-local"
|
||||
:description "Run single string 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."))
|
||||
|
@ -351,11 +378,11 @@
|
|||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
: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."
|
||||
:name "gptel-org-tools--org-ql-select-all-tags"
|
||||
:description "Run single string query against all files in (org-agenda-files). WITH tag inheritance."
|
||||
:args (list '(:name "query"
|
||||
:type string
|
||||
:description "A single word to scan for."))
|
||||
:description "A simple (single) string to scan for."))
|
||||
:category "org-ql"))
|
||||
|
||||
(defun gptel-org-tools--org-ql-select-all-rifle (query)
|
||||
|
@ -367,11 +394,11 @@
|
|||
(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 single word query against ALL org-mode files (notes)."
|
||||
:name "gptel-org-tools--org-ql-select-all-rifle"
|
||||
:description "Run simple (single) string against ALL org-mode files (notes)."
|
||||
:args (list '(:name "query"
|
||||
:type string
|
||||
:description "The keyword to match entry headings and content against."))
|
||||
:description "The string to match entry headings and content against."))
|
||||
:category "org-ql"))
|
||||
|
||||
(defun gptel-org-tools--org-ql-select-all-regexp (query)
|
||||
|
@ -384,7 +411,7 @@
|
|||
(add-to-list 'gptel-org-tools
|
||||
(gptel-make-tool
|
||||
:function #'gptel-org-tools--org-ql-select-all-regexp
|
||||
:name "org-ql-select-all-regexp"
|
||||
:name "gptel-org-tools--org-ql-select-all-regexp"
|
||||
:description "Run regexp on ALL files at once."
|
||||
:args (list '(:name "query"
|
||||
:type string
|
||||
|
|
Loading…
Add table
Reference in a new issue