Last active
January 17, 2025 04:09
-
-
Save pmarreck/9ce17f7996347dd532f3e20a2a383c0d to your computer and use it in GitHub Desktop.
"please"- a bash function to ask an LLM to compose and run a bash command, with an approval/denial step
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
needs() { | |
command -v "$1" > /dev/null || { >&2 printf "%s is required- not installed or in PATH; %s\n" "$1" "${@:2}"; return 1; } | |
} | |
_generate_curl_api_request_for_please() { | |
needs jq; | |
local request args timeout model curl; | |
local curl=${CURL:-curl}; | |
local model=${OPENAI_MODEL:-gpt-4o}; | |
local timeout=${OPENAI_TIMEOUT:-30}; | |
local args="$@"; | |
args=$(printf "%b" "$args" | sed "s/'/'\\\\''/g"); | |
read -r -d '' request <<EOF | |
$curl https://api.openai.com/v1/chat/completions -H "Authorization: Bearer $OPENAI_API_KEY" -H "Content-Type: application/json" --silent --max-time $timeout -d '{"model": "$model", "messages": [{"role": "user", "content": "$args"}], "temperature": 0.7}' | |
EOF | |
printf "%b" "$request" | |
} | |
clip() { | |
if command -v pbcopy > /dev/null; then | |
[ -t 0 ] && pbpaste || pbcopy; | |
elif command -v xclip > /dev/null; then | |
[ -t 0 ] && xclip -o -selection clipboard || xclip -selection clipboard; | |
else | |
echo "clip function error: Neither pbcopy/pbpaste nor xclip are available." >&2; | |
return 1; | |
fi | |
} | |
please() { | |
needs curl | |
needs jq | |
needs gum from https://github.com/charmbracelet/gum | |
local request response response_parsed response_parsed_cleaned args | |
local plat=$(platform) | |
request=$(_generate_curl_api_request_for_please "What is the $plat bash command to $@? Only return the command to run itself, do not describe anything. Only use commands and executables that are common on most $plat systems. Do not quote the response and do not use markdown.") | |
# printf "request: %s\n" "$request" >&2 | |
response=$(eval "gum spin --show-output -s line --title \"Figuring out how to do this...\" -- $request") | |
# printf "response: %s\n" "$response" >&2 | |
response_parsed=$(printf "%s" "$response" | jq --raw-output '.choices[0].message.content') | |
# printf "response_parsed: %s\n" "$response_parsed" >&2 | |
if [[ "$response_parsed" == "null" || "$?" != "0" ]]; then | |
printf "Error:\n" >&2 | |
printf "%b\n" "$response" >&2 | |
printf "%b\n" "$response_parsed" | |
else | |
response_parsed_cleaned=$(printf "%s" "$response_parsed" | sed -e 's/^[\\n]\+//' -e 's/^[\n]\+//') | |
printf "\e[0;33m%s\n\e[m" "$response_parsed_cleaned" >&2 | |
local choice=$(gum choose --limit 1 "Run it" "Copy to clipboard" "GTFO"); | |
case "$choice" in | |
"Run it") | |
printf "%s" "$response_parsed_cleaned" | bash; | |
;; | |
"Copy to clipboard") | |
printf "Copying command to clipboard.\n"; | |
printf "%s" "$response_parsed_cleaned" | clip; | |
;; | |
*) | |
printf "%s" "Aborted."; | |
return 1; | |
;; | |
esac; | |
fi | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment