summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVineet Kumar <git@vineetk.net>2026-05-24 15:42:02 -0400
committerVineet Kumar <git@vineetk.net>2026-05-24 15:42:02 -0400
commitecf082bd0a47da1436e4f8b482f6b92f10f0b924 (patch)
tree0592743fa2be7567af1baf496100fe456cffd311
parent137bec6d0706cfc341f99b12da52bb6932508580 (diff)
add confirm predicate to bash tool
-rw-r--r--llm-tools.el52
1 files changed, 31 insertions, 21 deletions
diff --git a/llm-tools.el b/llm-tools.el
index bbae522..41f6c56 100644
--- a/llm-tools.el
+++ b/llm-tools.el
@@ -59,12 +59,7 @@ final file."))
59 59
60;; TODO edit_file (hashline + patch apply) 60;; TODO edit_file (hashline + patch apply)
61 61
62;; TODO bash_readonly (shell-command-to-string, whitelisted commands)
63;; TODO compile command (calls build system, doesn't need confirmation, whitelisted commands)
64
65;; bash (make-process, taken from gptel-agent) 62;; bash (make-process, taken from gptel-agent)
66;; TODO refuse destructive operations like rm and dd
67;; TODO refuse operations that have dedicated tool definitions like grep and compile
68(defun llm-tools--execute-bash (callback command) 63(defun llm-tools--execute-bash (callback command)
69 "Execute COMMAND asynchronously in bash and call CALLBACK with output. 64 "Execute COMMAND asynchronously in bash and call CALLBACK with output.
70 65
@@ -90,6 +85,24 @@ COMMAND is the bash command string to execute."
90 exit-code output))))))))) 85 exit-code output)))))))))
91 proc)) 86 proc))
92 87
88(defun llm-tools--bash-confirm-p (command)
89 (let ((destructive
90 '("\\brm\\b"
91 "\\bdd\\b"
92 "\\bshred\\b"
93 "\\btruncate\\b"
94 "\\bmkfs\\b"
95 "\\bsudo\\b"
96 "\\bdoas\\b"
97 "[^>]>[^>]" ; any singular > that isn't >>
98 "\\$("
99 "`"
100 "|[ \t]*\\bsh\\b"
101 "|[ \t]*\\bbash\\b")))
102 (cl-some (lambda (regexp)
103 (string-match-p regexp command))
104 destructive)))
105
93(gptel-make-tool 106(gptel-make-tool
94 :name "bash" 107 :name "bash"
95 :function #'llm-tools--execute-bash 108 :function #'llm-tools--execute-bash
@@ -99,10 +112,8 @@ This tool provides access to a Bash shell with GNU coreutils (or
99equivalents) available. Use this to inspect system state, run builds, 112equivalents) available. Use this to inspect system state, run builds,
100tests or other development or system administration tasks. 113tests or other development or system administration tasks.
101 114
102TODO replace with saying do not use bash for operations that have equivalent tool definitions 115Use the dedicated tools for file operations, searching, and code
103Do NOT use this for file operations, finding, reading or editing files. 116navigation instead of shell commands.
104Use the provided file tools instead: `read_file`, `write_file`,
105`edit_file`, `find_file`, `grep`.
106 117
107- Quote file paths with spaces using double quotes. 118- Quote file paths with spaces using double quotes.
108- Chain dependent commands with && (or ; if failures are OK) 119- Chain dependent commands with && (or ; if failures are OK)
@@ -121,18 +132,18 @@ returned as a string. Long outputs should be filtered/limited using pipes."
121 :description "The Bash command to execute. \ 132 :description "The Bash command to execute. \
122Can include pipes and standard shell operators. 133Can include pipes and standard shell operators.
123Example: 'ls -la | head -20' or 'grep -i error app.log | tail -50'")) 134Example: 'ls -la | head -20' or 'grep -i error app.log | tail -50'"))
124 :confirm t 135 :confirm #'llm-tools--bash-confirm-p
125 :include t 136 :include t
126 :async t) 137 :async t)
127 138
128;; find_files (directory-files-recursively) 139;; find_files (directory-files-recursively)
129(gptel-make-tool 140(gptel-make-tool
130 :name "find_files" 141 :name "find_files"
131 :function (lambda (dir &optional pattern) 142 :function (lambda (dir &optional regexp)
132 (let ((git-dir (concat (file-name-as-directory dir) ".git"))) 143 (let ((git-dir (concat (file-name-as-directory dir) ".git")))
133 (delq nil (mapcar (lambda (f) 144 (delq nil (mapcar (lambda (f)
134 (unless (string-prefix-p git-dir f) f)) 145 (unless (string-prefix-p git-dir f) f))
135 (directory-files-recursively dir (or pattern "")))))) 146 (directory-files-recursively dir (or regexp ""))))))
136 :description "\ 147 :description "\
137Recursively find all files in a given directory, excluding files within 148Recursively find all files in a given directory, excluding files within
138.git directories. Returns a list of absolute file paths. Optionally 149.git directories. Returns a list of absolute file paths. Optionally
@@ -142,9 +153,9 @@ or editing them."
142 :args '((:name "directory" 153 :args '((:name "directory"
143 :type string 154 :type string
144 :description "Path to the directory to be read, relative to the working directory.") 155 :description "Path to the directory to be read, relative to the working directory.")
145 (:name "pattern" 156 (:name "regexp"
146 :type string 157 :type string
147 :description "Optional file name pattern to filter results, e.g. '*.el' or '*.txt'." 158 :description "Optional regular expression pattern to search for, uses extended regex syntax."
148 :optional t)) 159 :optional t))
149 :async nil 160 :async nil
150 :confirm nil 161 :confirm nil
@@ -153,25 +164,24 @@ or editing them."
153;; grep (just grep (or git grep) :D) 164;; grep (just grep (or git grep) :D)
154(gptel-make-tool 165(gptel-make-tool
155 :name "grep" 166 :name "grep"
156 :function (lambda (regex &optional path) 167 :function (lambda (regexp &optional path)
157 (with-temp-buffer 168 (with-temp-buffer
158 (if (magit-gitdir) 169 (if (magit-gitdir)
159 (call-process "git" nil t nil 170 (call-process "git" nil t nil
160 "--no-pager"
161 "grep" 171 "grep"
162 "-rnEH" 172 "-rnEH"
163 regex 173 regexp
164 (or path ".")) 174 (or path "."))
165 (call-process "grep" nil t nil 175 (call-process "grep" nil t nil
166 "-rnEH" 176 "-rnEH"
167 regex 177 regexp
168 (or path "."))) 178 (or path ".")))
169 (buffer-string))) 179 (buffer-string)))
170 :description "\ 180 :description "\
171Recursively search for lines matching a regex pattern in files under a 181Recursively search for lines matching a regexp pattern in files under a
172given path. Uses `git grep` when inside a Git repository, otherwise falls 182given path. Uses `git grep` when inside a Git repository, otherwise falls
173back to GNU grep. Both are invoked with flags -rnEH (recursive, line 183back to GNU grep. Both are invoked with flags -rnEH (recursive, line
174numbers, extended regex, always show filename). When using `git grep`, 184numbers, extended regexp, always show filename). When using `git grep`,
175only tracked files are searched. Returns raw output with one match per 185only tracked files are searched. Returns raw output with one match per
176line in the format FILE:LINE:CONTENT. Use this tool to find code 186line in the format FILE:LINE:CONTENT. Use this tool to find code
177patterns, locate function definitions, search for strings, or identify 187patterns, locate function definitions, search for strings, or identify
@@ -179,7 +189,7 @@ files containing specific text before reading them. For simple file name
179discovery, use `find_files` instead. For reading file contents, use 189discovery, use `find_files` instead. For reading file contents, use
180`read_file`. The path argument is relative to the working directory; 190`read_file`. The path argument is relative to the working directory;
181omit it to search from the current directory." 191omit it to search from the current directory."
182 :args '((:name "regex" 192 :args '((:name "regexp"
183 :type string 193 :type string
184 :description "\ 194 :description "\
185Regular expression pattern to search for. Uses GNU grep extended syntax.") 195Regular expression pattern to search for. Uses GNU grep extended syntax.")