Skip to content

Instantly share code, notes, and snippets.

@juanluisrto
Last active July 4, 2025 13:10
Show Gist options
  • Save juanluisrto/15b224f6a08c8c6fc28b73c0922efea9 to your computer and use it in GitHub Desktop.
Save juanluisrto/15b224f6a08c8c6fc28b73c0922efea9 to your computer and use it in GitHub Desktop.
a chat command that sends your visible terminal state to GPT and preloads the reply as your next command.
#!/usr/bin/env zsh
# chat – ask OpenAI using the last visible terminal lines (curl + jq)
chat() {
local lines=30 # how many terminal lines to capture
local model="gpt-4.1-nano" # default model
local verbose=false
local -a query_parts=()
# -------------------- parse options --------------------
while (( $# )); do
case "$1" in
-l|--lines)
[[ -n $2 && $2 != -* ]] || { echo "Option $1 needs a value" >&2; return 1; }
lines=$2; shift 2 ;;
-m|--model)
[[ -n $2 && $2 != -* ]] || { echo "Option $1 needs a value" >&2; return 1; }
model=$2; shift 2 ;;
-v|--verbose)
verbose=true; shift ;;
-h|--help)
cat <<'HELP'
chat — ask OpenAI for the next shell command
-l, --lines N number of terminal lines to send (default 30)
-m, --model M model name (default gpt-4.1-nano)
-v, --verbose print curl request, raw JSON response, and parsed command
-h, --help show this help
-- stop option parsing; everything after is query text
Examples:
chat git commit -m "initial"
chat --model gpt-4o-mini -v how to write a Makefile
chat how to use ls -l -- -R
HELP
return 0 ;;
--) # explicit end of options
shift; query_parts+=("$@"); break ;;
-*)
echo "Unknown option: $1" >&2; return 1 ;;
*)
query_parts+=("$1"); shift ;;
esac
done
(( ${#query_parts} )) || { echo "No query given" >&2; return 1; }
local query="${(j: :)query_parts}"
# -------------------------------------------------------
[[ -z $OPENAI_API_KEY ]] && { echo "OPENAI_API_KEY not set" >&2; return 1; }
# --------------- capture terminal excerpt --------------
if [[ -z $TMUX ]]; then
echo "Not inside tmux; can't capture pane output." >&2
return 1
fi
local pane
pane=$(tmux capture-pane -p -S -$((lines+1)) | sed '$d' | tail -n $lines)
# ------------------ build prompt -----------------------
local prompt="You are a CLI assistant. Given the following terminal \
session excerpt, reply with ONLY the next shell command—no explanations, \
no markdown.
Terminal excerpt:
$pane
Request: $query"
local payload
payload=$(jq -n --arg model "$model" --arg p "$prompt" \
'{model:$model, input:$p, temperature:0.3, background:false}')
# ------------------ headers ----------------------------
local -a headers=(
-H "Content-Type: application/json"
-H "Authorization: Bearer $OPENAI_API_KEY"
)
[[ -n $OPENAI_PROJECT_ID ]] && headers+=(-H "OpenAI-Project: $OPENAI_PROJECT_ID")
[[ -n $OPENAI_ORG_ID ]] && headers+=(-H "OpenAI-Organization: $OPENAI_ORG_ID")
# ------------------ request ----------------------------
local -a curl_cmd=(curl -s https://api.openai.com/v1/responses "${headers[@]}" -d "$payload")
local response
response=$("${curl_cmd[@]}")
# handle API-side errors early
if jq -e '.error' >/dev/null 2>&1 <<<"$response"; then
jq -r '.error.message' <<<"$response" >&2
return 2
fi
# ------------------ extract command --------------------
local cmd
cmd=$(jq -r '
if .output then
.output[].content[].text?
elif .choices then
.choices[].message.content?
else empty end' <<<"$response" |
sed -e 's/^```[[:alpha:]]*//' -e 's/```$//' \
-e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
[[ -z $cmd ]] && { echo "No command received." >&2; return 2; }
# ------------------ verbose block ----------------------
if [[ $verbose == true ]]; then
echo "CURL REQUEST:"
echo "curl -s https://api.openai.com/v1/responses \\"
for h in "${headers[@]}"; do
echo " $h \\"
done
echo " -d '$payload'"
echo
echo "RAW RESPONSE:"
echo "$response"
echo
echo "PARSED COMMAND:"
echo "$cmd"
echo
fi
# ------------- preload into command line ---------------
if [[ -o interactive && $+functions[zle] -eq 1 ]]; then
BUFFER="$cmd"
CURSOR=${#BUFFER}
zle -R # redraw prompt
else
# fall back: queue it into history & next prompt
print -z -- "$cmd"
fi
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment