Skip to content

Instantly share code, notes, and snippets.

@jaeyson
Forked from thmsmlr/README.md
Created February 7, 2025 10:37
Show Gist options
  • Save jaeyson/2a38f655e295e8b79135bed010434c37 to your computer and use it in GitHub Desktop.
Save jaeyson/2a38f655e295e8b79135bed010434c37 to your computer and use it in GitHub Desktop.
cmd+k for any terminal

These three scripts can provide you a cursor style CMD+K Like experience in any terminal.

How it works

  1. Set logged-shell to be your default shell for your terminal emulator. shell = /path/to/logged-shell in ~/.config/kitty/kitty.conf for kitty users.
  2. This will stream both the inputs and outputs of your terminal session into a file defined at $SHELL_LOG_FILE
  3. The ai-bash-command will take a user prompt, add the shell as context, and call OpenAI (with the ai command) to get the bash command that satisfies the prompt.
  4. It will type the results back into your terminal using wtype for wayland users.

Customizing

If you are not using wayland, you'll have to customize the type back part of ai-bash-command. Similar commands like xdotool exist for X11 users. I'm sure there's something that also exists for macOS.

#!/bin/bash
# Default values
MODEL=""
CONTEXT_LINES=20
# Help documentation
show_help() {
cat << EOF
Usage: $(basename "$0") [OPTIONS] PROMPT
Generate and type a bash command based on the given prompt using AI.
Options:
-h Show this help message
-m MODEL Specify the AI model to use
-c LINES Number of lines of shell history context to include (default: 20)
Arguments:
PROMPT The natural language description of the command you want
Example:
$(basename "$0") "list all pdf files recursively"
$(basename "$0") -m gpt-4 "show disk usage in human readable format"
$(basename "$0") -c 50 "find large files"
EOF
}
# Parse options
while getopts "hm:c:" opt; do
case $opt in
h)
show_help
exit 0
;;
m)
MODEL="$OPTARG"
;;
c)
if ! [[ "$OPTARG" =~ ^[0-9]+$ ]]; then
echo "Error: Context lines must be a positive number" >&2
exit 1
fi
CONTEXT_LINES="$OPTARG"
;;
\?)
echo "Invalid option: -$OPTARG" >&2
show_help
exit 1
;;
esac
done
# Shift past the options
shift $((OPTIND-1))
# Check if a prompt was provided
if [ $# -eq 0 ]; then
echo "Error: No prompt provided" >&2
show_help
exit 1
fi
SCHEMA='{
"type": "object",
"properties": {
"bash_command": {
"type": "string",
"description": "A bash command to execute to solve the prompt"
}
},
"required": ["bash_command"]
}'
PROMPT="$*"
# Check if context was explicitly requested but no log file is available
if [ "$OPTIND" -gt 1 ] && [ -n "$CONTEXT_LINES" ] && { [ -z "$SHELL_LOG_FILE" ] || [ ! -f "$SHELL_LOG_FILE" ]; }; then
echo "Warning: Context lines requested but no shell log file is available" >&2
fi
# Add shell log context if available
if [ -n "$SHELL_LOG_FILE" ] && [ -f "$SHELL_LOG_FILE" ]; then
SHELL_CONTEXT=$(tail -n "$CONTEXT_LINES" "$SHELL_LOG_FILE")
PROMPT="Given this recent shell history as context:
$SHELL_CONTEXT
Generate a command for this request: $PROMPT"
fi
AI_CMD="ai"
if [ -n "$MODEL" ]; then
AI_CMD="ai -m $MODEL"
fi
CMD=$($AI_CMD -j "$SCHEMA" "$PROMPT" | jq -r '.bash_command')
wtype "$CMD" &
#!/usr/bin/env bash
#
# ai - A simple CLI tool to call the OpenAI Chat API.
#
# Requirements: curl and jq must be installed.
#
# Usage: ./ai [options] prompt...
#
# Options:
# -m MODEL OpenAI model to use (default: gpt-3.5-turbo).
# -j SCHEMA JSONSchema for structured output (optional).
# -k API_KEY Your OpenAI API key (or set the environment variable OPENAI_API_KEY).
# -h Display this help message and exit.
#
# Default parameter values
MODEL="gpt-4o-mini"
JSON_SCHEMA=""
usage() {
cat <<EOF
Usage: ${0##*/} [options] prompt...
Options:
-m MODEL OpenAI model to use (default: ${MODEL}).
-j SCHEMA JSONSchema for structured output (optional).
-k API_KEY Your OpenAI API key (or use the environment variable OPENAI_API_KEY).
-h Display this help message and exit.
Example:
${0##*/} Tell me a joke about programming
${0##*/} -m gpt-4 What is the meaning of life?
${0##*/} -j '{"type":"object","properties":{"name":{"type":"string"},"age":{"type":"number"}}}' Tell me about John who is 25
EOF
}
# Process command-line options
while getopts "m:j:k:h" opt; do
case "$opt" in
m) MODEL="$OPTARG" ;;
j) JSON_SCHEMA="$OPTARG" ;;
k) API_KEY="$OPTARG" ;;
h)
usage
exit 0
;;
*)
usage
exit 1
;;
esac
done
# Remove processed options
shift $((OPTIND-1))
# Check if there are any arguments left for the prompt
if [ $# -eq 0 ]; then
echo "Error: Prompt is required." >&2
usage
exit 1
fi
# Concatenate all remaining arguments as the prompt
PROMPT="$*"
# Determine API key from argument or environment variable
if [ -z "$API_KEY" ]; then
if [ -z "$OPENAI_API_KEY" ]; then
echo "Error: API key not provided. Use -k or set the OPENAI_API_KEY environment variable." >&2
usage
exit 1
else
API_KEY="$OPENAI_API_KEY"
fi
fi
# Build the JSON payload using jq (avoids manual escaping)
if [ -n "$JSON_SCHEMA" ]; then
PAYLOAD=$(jq -n \
--arg model "$MODEL" \
--arg prompt "$PROMPT" \
--argjson schema "$JSON_SCHEMA" \
'{
model: $model,
messages: [{role: "user", content: $prompt}],
response_format: {
type: "json_schema",
json_schema: {
name: "response",
schema: $schema
}
}
}')
else
PAYLOAD=$(jq -n \
--arg model "$MODEL" \
--arg prompt "$PROMPT" \
'{
model: $model,
messages: [{role: "user", content: $prompt}]
}')
fi
# Call the OpenAI API using curl
response=$(curl -s https://api.openai.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${API_KEY}" \
-d "$PAYLOAD")
# Check if curl encountered an error
if [ $? -ne 0 ]; then
echo "Error: Failed to contact OpenAI API." >&2
exit 1
fi
# Check if the response contains an error
if echo "$response" | jq -e 'has("error")' > /dev/null; then
error_message=$(echo "$response" | jq -r '.error.message')
echo "Error from OpenAI API: $error_message" >&2
exit 1
fi
echo "$response" | jq -r '.choices[0].message.content'
#!/bin/bash
LOG_FILE="/tmp/logged-shell-$(uuidgen).log"
export SHELL_LOG_FILE="$LOG_FILE"
if [ -n "$SHELL_LOG_FILE" ]; then
echo "Live shell log can be found at $SHELL_LOG_FILE" 1>&2
fi
script -f "$LOG_FILE"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment