Skip to content

Commit 09681d5

Browse files
committed
Change category strings to symbols
* llm-tool-collection.el (llm-tool-collection-get-category): Update docstring and example code. Use 'eq' instead of 'string=' when comparing categories. (read-file, list-directory, create-file, create-directory) (view-buffer): Change :category values from strings to symbols.
1 parent 5c1423f commit 09681d5

File tree

1 file changed

+284
-8
lines changed

1 file changed

+284
-8
lines changed

llm-tool-collection.el

Lines changed: 284 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,14 @@ called with the tool's spec as their argument."
116116
;;;###autoload
117117
(defun llm-tool-collection-get-category (category)
118118
"Return a list of all tool definitions in the collection part of CATEGORY.
119+
CATEGORY should be a symbol like \\='filesystem or \\='buffers.
119120
120121
Mapping over this list with `gptel-make-tool', `llm-make-tool', or
121122
similar will add all tools to the respective client:
122123
123124
(mapcar (apply-partially #\\='apply #\\='gptel-make-tool)
124-
(llm-tool-collection-get-category \"filesystem\"))"
125-
(seq-filter (lambda (tool) (string= (plist-get tool :category) category))
125+
(llm-tool-collection-get-category \\='filesystem))"
126+
(seq-filter (lambda (tool) (eq (plist-get tool :category) category))
126127
(llm-tool-collection-get-all)))
127128

128129
;;;###autoload
@@ -147,7 +148,7 @@ similar will add all tools to the respective client:
147148
(llm-tool-collection-deftool read-file
148149
(:description
149150
"Read the contents of a file and return its content as a string."
150-
:category "filesystem"
151+
:category filesystem
151152
:confirm t
152153
:include t)
153154
((path
@@ -159,7 +160,7 @@ similar will add all tools to the respective client:
159160

160161
(llm-tool-collection-deftool list-directory
161162
(:description "List the contents of a specified directory."
162-
:category "filesystem"
163+
:category filesystem
163164
:confirm t
164165
:include t)
165166
((path
@@ -168,14 +169,16 @@ similar will add all tools to the respective client:
168169
(let ((expanded-path (expand-file-name path)))
169170
(if (file-directory-p expanded-path)
170171
(string-join `(,(format "Contents of %s:" path)
171-
,@(directory-files expanded-path))
172+
,@(seq-filter (lambda (file)
173+
(not (member file '("." ".."))))
174+
(directory-files expanded-path)))
172175
"\n")
173176
(error "%s is not a directory" expanded-path))))
174177

175178
(llm-tool-collection-deftool create-file
176179
(:description
177180
"Create a new file with the specified content if it does not already exist."
178-
:category "filesystem"
181+
:category filesystem
179182
:confirm t)
180183
((path
181184
:type string
@@ -191,7 +194,7 @@ similar will add all tools to the respective client:
191194
(llm-tool-collection-deftool create-directory
192195
(:description
193196
"Create a new directory at the specified path if it does not already exist."
194-
:category "filesystem"
197+
:category filesystem
195198
:confirm t)
196199
((path
197200
:type string
@@ -206,7 +209,7 @@ similar will add all tools to the respective client:
206209
(:description "View contents of BUFFER-NAME with optional OFFSET and LIMIT.
207210
OFFSET specifies the starting line (0-based).
208211
LIMIT specifies the maximum number of lines to return."
209-
:category "buffers")
212+
:category buffers)
210213
((buffer-name :type string :description "Name of the buffer to view.")
211214
&optional
212215
(offset
@@ -221,6 +224,279 @@ LIMIT specifies the maximum number of lines to return."
221224
(selected-lines (seq-subseq lines start end)))
222225
(string-join selected-lines "\n"))))
223226

227+
(llm-tool-collection-deftool bash
228+
(:description "Executes bash commands"
229+
:category system
230+
:confirm t)
231+
((command
232+
:type string :description "Command to execute")
233+
&optional
234+
(timeout
235+
:type number :description "Optional timeout in milliseconds (max 600000)"))
236+
;; TODO: use the timeout
237+
(let ((result (shell-command-to-string command)))
238+
(if (string-empty-p result)
239+
"Command executed successfully (no output)"
240+
result)))
241+
242+
(llm-tool-collection-deftool glob
243+
(:description "File pattern matching"
244+
:category filesystem
245+
:include t)
246+
((pattern
247+
:type string
248+
:description "Glob pattern to match files")
249+
&optional
250+
(path
251+
:type string
252+
:description "Directory to search in"))
253+
(let* ((default-directory (or path default-directory))
254+
(files (file-expand-wildcards pattern)))
255+
(string-join files "\n")))
256+
257+
(llm-tool-collection-deftool grep
258+
(:description "Content search using regex"
259+
:category search
260+
:include t)
261+
((pattern
262+
:type string
263+
:description "Regex pattern to search in file contents")
264+
&optional
265+
(include
266+
:type string
267+
:description "File pattern to include in search")
268+
(path
269+
:type string
270+
:description "Directory to search in"))
271+
(let* ((default-directory (or path default-directory))
272+
(include-arg (if include
273+
(format "--include=\"%s\"" include)
274+
""))
275+
(command (format "grep -r -n -E %s %s ."
276+
(shell-quote-argument pattern)
277+
include-arg))
278+
(result (shell-command-to-string command)))
279+
(if (string-empty-p result)
280+
"No matches found"
281+
result)))
282+
283+
(llm-tool-collection-deftool ls
284+
(:description "Lists files and directories"
285+
:category filesystem
286+
:include t)
287+
((path
288+
:type string
289+
:description "Absolute path to directory to list")
290+
&optional
291+
(ignore
292+
:type array :items (:type string)
293+
:description "Array of Elisp regexp patterns (e.g., \\.pdf$) to ignore"))
294+
(let ((files (directory-files path t nil t)))
295+
(when ignore
296+
(let ((ignore-patterns (if (stringp ignore)
297+
(list ignore)
298+
ignore)))
299+
(seq-do (lambda (pattern)
300+
(setq files (seq-filter (lambda (f)
301+
(not (string-match-p pattern f)))
302+
files)))
303+
ignore-patterns)))
304+
(string-join (mapcar #'file-name-nondirectory files) "\n")))
305+
306+
(llm-tool-collection-deftool view-file
307+
(:description "Reads files"
308+
:category filesystem
309+
:include t)
310+
((file-path
311+
:type string
312+
:description "Absolute path to the file to read")
313+
&optional
314+
(offset
315+
:type number
316+
:description "Line number to start reading from")
317+
(limit
318+
:type number
319+
:description "Number of lines to read"))
320+
(with-temp-buffer
321+
(insert-file-contents file-path)
322+
(let* ((lines (split-string (buffer-string) "\n"))
323+
(start (or offset 0))
324+
(end (if limit (min (+ start limit) (length lines)) (length lines)))
325+
(selected-lines (seq-subseq lines start end)))
326+
(string-join selected-lines "\n"))))
327+
328+
(llm-tool-collection-deftool edit-file
329+
(:description "Edits files"
330+
:category filesystem
331+
:confirm t)
332+
((file-path
333+
:type string
334+
:description "Absolute path to the file to modify")
335+
(old-string
336+
:type string
337+
:description "Text to replace (must match exactly)")
338+
(new-string
339+
:type string
340+
:description "Text to replace old_string with"))
341+
(with-temp-buffer
342+
(insert-file-contents file-path)
343+
(let ((case-fold-search nil))
344+
(if (string= old-string "")
345+
(progn
346+
(erase-buffer)
347+
(insert new-string)
348+
(write-file file-path)
349+
(format "Created new file: %s" file-path))
350+
(goto-char (point-min))
351+
(let ((count 0))
352+
(while (search-forward old-string nil t)
353+
(setq count (1+ count)))
354+
(if (= count 0)
355+
(format "Error: Could not find text to replace in %s" file-path)
356+
(if (> count 1)
357+
(format "Error: Found %d matches for the text to replace in %s" count file-path)
358+
(goto-char (point-min))
359+
(search-forward old-string)
360+
(replace-match new-string t t)
361+
(write-file file-path)
362+
(format "Successfully edited %s" file-path))))))))
363+
364+
(llm-tool-collection-deftool replace-file
365+
(:description "Completely overwrites files"
366+
:category filesystem
367+
:confirm t)
368+
((file-path
369+
:type string
370+
:description "Absolute path to file to write")
371+
(content
372+
:type string
373+
:description "Content to write to the file"))
374+
(with-temp-buffer
375+
(insert content)
376+
(write-file file-path)
377+
(format "File replaced: %s" file-path)))
378+
379+
;; Buffer-related tools
380+
381+
(llm-tool-collection-deftool buffer-search
382+
(:description "Searches within Emacs buffers for text patterns"
383+
:category buffers
384+
:include t)
385+
((pattern
386+
:type string
387+
:description "Regex pattern to search for in buffer contents.\nRegex syntax is that of Emacs -- parentheses are NOT escaped! Search for \"'(defun\", not \"\\\\(defun\".")
388+
&optional
389+
(buffer-name
390+
:type string
391+
:description "Buffer name to search in (searches all user buffers if not specified)"))
392+
(condition-case err
393+
(let ((results '()))
394+
(dolist (buf (if buffer-name
395+
(if-let ((specified-buf (get-buffer buffer-name)))
396+
(list specified-buf)
397+
(error "Buffer '%s' does not exist" buffer-name))
398+
(seq-filter (lambda (buf)
399+
(let ((buf-name (buffer-name buf)))
400+
(and (not (string-prefix-p " " buf-name))
401+
(not (string-prefix-p "*" buf-name)))))
402+
(buffer-list))))
403+
(with-current-buffer buf
404+
(save-excursion
405+
(goto-char (point-min))
406+
(let ((line-num 1))
407+
(while (re-search-forward pattern nil t)
408+
(let* ((line-beg (line-beginning-position))
409+
(line-end (line-end-position))
410+
(line-text (buffer-substring-no-properties line-beg line-end))
411+
(col-num (- (point) line-beg)))
412+
(push (format "%s:%d:%d: %s"
413+
(buffer-name buf)
414+
line-num
415+
col-num
416+
line-text)
417+
results))
418+
(forward-line 1)
419+
(setq line-num (1+ line-num)))))))
420+
(if results
421+
(string-join (nreverse results) "\n")
422+
"No matches found"))
423+
(invalid-regexp
424+
(format "Invalid regexp pattern: %s" (error-message-string err)))
425+
(error
426+
(format "Error during search: %s" (error-message-string err)))))
427+
428+
(llm-tool-collection-deftool list-buffers
429+
(:description "Lists active buffers that a user might care about"
430+
:category buffers
431+
:include t)
432+
()
433+
(let* ((all-buffers (buffer-list))
434+
(user-buffers (seq-filter
435+
(lambda (buf)
436+
(let ((buf-name (buffer-name buf)))
437+
(and (not (string-prefix-p " " buf-name))
438+
(not (string-prefix-p "*" buf-name))
439+
(not (eq buf (current-buffer))))))
440+
all-buffers))
441+
(sorted-buffers (sort user-buffers
442+
(lambda (a b)
443+
(string< (buffer-name a)
444+
(buffer-name b))))))
445+
(mapconcat (lambda (buf)
446+
(let ((buf-name (buffer-name buf))
447+
(file-name (or (buffer-file-name buf) "")))
448+
(format "%s%s"
449+
buf-name
450+
(if (string-empty-p file-name)
451+
""
452+
(format " (%s)" file-name)))))
453+
sorted-buffers
454+
"\n")))
455+
456+
(llm-tool-collection-deftool edit-buffer
457+
(:description "Edits Emacs buffers"
458+
:category buffers
459+
:confirm t)
460+
((buffer-name
461+
:type string
462+
:description "Name of the buffer to modify")
463+
(old-string
464+
:type string
465+
:description "Text to replace (must match exactly)")
466+
(new-string
467+
:type string
468+
:description "Text to replace old_string with"))
469+
(with-current-buffer buffer-name
470+
(let ((case-fold-search nil))
471+
(save-excursion
472+
(goto-char (point-min))
473+
(let ((count 0))
474+
(while (search-forward old-string nil t)
475+
(setq count (1+ count)))
476+
(if (= count 0)
477+
(format "Error: Could not find text to replace in buffer %s" buffer-name)
478+
(if (> count 1)
479+
(format "Error: Found %d matches for the text to replace in buffer %s" count buffer-name)
480+
(goto-char (point-min))
481+
(search-forward old-string)
482+
(replace-match new-string t t)
483+
(format "Successfully edited buffer %s" buffer-name))))))))
484+
485+
(llm-tool-collection-deftool replace-buffer
486+
(:description "Completely overwrites buffer contents"
487+
:category buffers
488+
:confirm t)
489+
((buffer-name
490+
:type string
491+
:description "Name of the buffer to overwrite")
492+
(content
493+
:type string
494+
:description "Content to write to the buffer"))
495+
(with-current-buffer buffer-name
496+
(erase-buffer)
497+
(insert content)
498+
(format "Buffer replaced: %s" buffer-name)))
499+
224500
(provide 'llm-tool-collection)
225501

226502
;;; llm-tool-collection.el ends here

0 commit comments

Comments
 (0)