Skip to content

Instantly share code, notes, and snippets.

@pmarreck
Last active January 17, 2025 04:09
Show Gist options
  • Save pmarreck/9ce17f7996347dd532f3e20a2a383c0d to your computer and use it in GitHub Desktop.
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
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