From ecf082bd0a47da1436e4f8b482f6b92f10f0b924 Mon Sep 17 00:00:00 2001 From: Vineet Kumar Date: Sun, 24 May 2026 15:42:02 -0400 Subject: add confirm predicate to bash tool --- llm-tools.el | 52 +++++++++++++++++++++++++++++++--------------------- 1 file 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.")) ;; TODO edit_file (hashline + patch apply) -;; TODO bash_readonly (shell-command-to-string, whitelisted commands) -;; TODO compile command (calls build system, doesn't need confirmation, whitelisted commands) - ;; bash (make-process, taken from gptel-agent) -;; TODO refuse destructive operations like rm and dd -;; TODO refuse operations that have dedicated tool definitions like grep and compile (defun llm-tools--execute-bash (callback command) "Execute COMMAND asynchronously in bash and call CALLBACK with output. @@ -90,6 +85,24 @@ COMMAND is the bash command string to execute." exit-code output))))))))) proc)) +(defun llm-tools--bash-confirm-p (command) + (let ((destructive + '("\\brm\\b" + "\\bdd\\b" + "\\bshred\\b" + "\\btruncate\\b" + "\\bmkfs\\b" + "\\bsudo\\b" + "\\bdoas\\b" + "[^>]>[^>]" ; any singular > that isn't >> + "\\$(" + "`" + "|[ \t]*\\bsh\\b" + "|[ \t]*\\bbash\\b"))) + (cl-some (lambda (regexp) + (string-match-p regexp command)) + destructive))) + (gptel-make-tool :name "bash" :function #'llm-tools--execute-bash @@ -99,10 +112,8 @@ This tool provides access to a Bash shell with GNU coreutils (or equivalents) available. Use this to inspect system state, run builds, tests or other development or system administration tasks. -TODO replace with saying do not use bash for operations that have equivalent tool definitions -Do NOT use this for file operations, finding, reading or editing files. -Use the provided file tools instead: `read_file`, `write_file`, -`edit_file`, `find_file`, `grep`. +Use the dedicated tools for file operations, searching, and code +navigation instead of shell commands. - Quote file paths with spaces using double quotes. - 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." :description "The Bash command to execute. \ Can include pipes and standard shell operators. Example: 'ls -la | head -20' or 'grep -i error app.log | tail -50'")) - :confirm t + :confirm #'llm-tools--bash-confirm-p :include t :async t) ;; find_files (directory-files-recursively) (gptel-make-tool :name "find_files" - :function (lambda (dir &optional pattern) + :function (lambda (dir &optional regexp) (let ((git-dir (concat (file-name-as-directory dir) ".git"))) (delq nil (mapcar (lambda (f) (unless (string-prefix-p git-dir f) f)) - (directory-files-recursively dir (or pattern "")))))) + (directory-files-recursively dir (or regexp "")))))) :description "\ Recursively find all files in a given directory, excluding files within .git directories. Returns a list of absolute file paths. Optionally @@ -142,9 +153,9 @@ or editing them." :args '((:name "directory" :type string :description "Path to the directory to be read, relative to the working directory.") - (:name "pattern" + (:name "regexp" :type string - :description "Optional file name pattern to filter results, e.g. '*.el' or '*.txt'." + :description "Optional regular expression pattern to search for, uses extended regex syntax." :optional t)) :async nil :confirm nil @@ -153,25 +164,24 @@ or editing them." ;; grep (just grep (or git grep) :D) (gptel-make-tool :name "grep" - :function (lambda (regex &optional path) + :function (lambda (regexp &optional path) (with-temp-buffer (if (magit-gitdir) (call-process "git" nil t nil - "--no-pager" "grep" "-rnEH" - regex + regexp (or path ".")) (call-process "grep" nil t nil "-rnEH" - regex + regexp (or path "."))) (buffer-string))) :description "\ -Recursively search for lines matching a regex pattern in files under a +Recursively search for lines matching a regexp pattern in files under a given path. Uses `git grep` when inside a Git repository, otherwise falls back to GNU grep. Both are invoked with flags -rnEH (recursive, line -numbers, extended regex, always show filename). When using `git grep`, +numbers, extended regexp, always show filename). When using `git grep`, only tracked files are searched. Returns raw output with one match per line in the format FILE:LINE:CONTENT. Use this tool to find code patterns, locate function definitions, search for strings, or identify @@ -179,7 +189,7 @@ files containing specific text before reading them. For simple file name discovery, use `find_files` instead. For reading file contents, use `read_file`. The path argument is relative to the working directory; omit it to search from the current directory." - :args '((:name "regex" + :args '((:name "regexp" :type string :description "\ Regular expression pattern to search for. Uses GNU grep extended syntax.") -- cgit v1.2.3