Created
October 28, 2025 00:16
-
-
Save michaelneale/c66d21d254d4a2186510d78bffaae004 to your computer and use it in GitHub Desktop.
agent.sh
This file contains hidden or 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
| #!/bin/bash | |
| # Simple bash-based agent that can call shell commands via OpenAI API | |
| # Requires: curl, jq, OPENAI_API_KEY environment variable | |
| set -e | |
| API_URL="https://api.openai.com/v1/chat/completions" | |
| MODEL="gpt-5" | |
| CONVERSATION_FILE="/tmp/agent_conversation_$$.json" | |
| # Initialize conversation with system message and tools | |
| cat > "$CONVERSATION_FILE" <<'EOF' | |
| { | |
| "model": "gpt-5", | |
| "messages": [ | |
| { | |
| "role": "system", | |
| "content": "You are a helpful assistant that can execute shell commands. When you need to run a command, use the execute_shell tool. Always explain what you're doing before executing commands." | |
| } | |
| ], | |
| "tools": [ | |
| { | |
| "type": "function", | |
| "function": { | |
| "name": "execute_shell", | |
| "description": "Execute a shell command and return its output", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "command": { | |
| "type": "string", | |
| "description": "The shell command to execute" | |
| } | |
| }, | |
| "required": ["command"] | |
| } | |
| } | |
| } | |
| ] | |
| } | |
| EOF | |
| # Function to execute shell command | |
| execute_shell() { | |
| local cmd="$1" | |
| echo "π§ Executing: $cmd" >&2 | |
| output=$(eval "$cmd" 2>&1) || true | |
| echo "$output" | |
| } | |
| # Function to add message to conversation | |
| add_message() { | |
| local role="$1" | |
| local content="$2" | |
| local temp_file=$(mktemp) | |
| jq --arg role "$role" --arg content "$content" \ | |
| '.messages += [{role: $role, content: $content}]' \ | |
| "$CONVERSATION_FILE" > "$temp_file" | |
| mv "$temp_file" "$CONVERSATION_FILE" | |
| } | |
| # Function to add tool call response | |
| add_tool_response() { | |
| local tool_call_id="$1" | |
| local output="$2" | |
| local temp_file=$(mktemp) | |
| jq --arg tool_call_id "$tool_call_id" --arg output "$output" \ | |
| '.messages += [{role: "tool", tool_call_id: $tool_call_id, content: $output}]' \ | |
| "$CONVERSATION_FILE" > "$temp_file" | |
| mv "$temp_file" "$CONVERSATION_FILE" | |
| } | |
| # Function to add assistant message with tool calls | |
| add_assistant_with_tools() { | |
| local response="$1" | |
| local temp_file=$(mktemp) | |
| jq --argjson response "$response" \ | |
| '.messages += [$response]' \ | |
| "$CONVERSATION_FILE" > "$temp_file" | |
| mv "$temp_file" "$CONVERSATION_FILE" | |
| } | |
| # Function to call OpenAI API | |
| call_api() { | |
| local payload=$(jq -c '.' "$CONVERSATION_FILE") | |
| response=$(curl -s "$API_URL" \ | |
| -H "Content-Type: application/json" \ | |
| -H "Authorization: Bearer $OPENAI_API_KEY" \ | |
| -d "$payload") | |
| echo "$response" | |
| } | |
| # Main interaction loop | |
| echo "π€ Simple Bash Agent (type 'exit' or 'quit' to end)" | |
| echo "================================================" | |
| while true; do | |
| # Get user input | |
| echo "" | |
| echo -n "You: " | |
| read -r user_input | |
| # Check for exit | |
| if [[ "$user_input" == "exit" ]] || [[ "$user_input" == "quit" ]]; then | |
| echo "π Goodbye!" | |
| rm -f "$CONVERSATION_FILE" | |
| exit 0 | |
| fi | |
| # Skip empty input | |
| if [[ -z "$user_input" ]]; then | |
| continue | |
| fi | |
| # Add user message | |
| add_message "user" "$user_input" | |
| # Agent loop - keep calling API until we get a final response | |
| while true; do | |
| # Call API | |
| api_response=$(call_api) | |
| # Check for API errors | |
| if echo "$api_response" | jq -e '.error' > /dev/null 2>&1; then | |
| error_msg=$(echo "$api_response" | jq -r '.error.message') | |
| echo "β API Error: $error_msg" >&2 | |
| break | |
| fi | |
| # Extract the assistant's message | |
| assistant_msg=$(echo "$api_response" | jq '.choices[0].message') | |
| finish_reason=$(echo "$api_response" | jq -r '.choices[0].finish_reason') | |
| # Check if there are tool calls | |
| tool_calls=$(echo "$assistant_msg" | jq '.tool_calls // []') | |
| tool_calls_count=$(echo "$tool_calls" | jq 'length') | |
| if [[ "$tool_calls_count" -gt 0 ]]; then | |
| # Add assistant message with tool calls | |
| add_assistant_with_tools "$assistant_msg" | |
| # Execute each tool call | |
| for i in $(seq 0 $((tool_calls_count - 1))); do | |
| tool_call=$(echo "$tool_calls" | jq ".[$i]") | |
| tool_call_id=$(echo "$tool_call" | jq -r '.id') | |
| function_name=$(echo "$tool_call" | jq -r '.function.name') | |
| arguments=$(echo "$tool_call" | jq -r '.function.arguments') | |
| if [[ "$function_name" == "execute_shell" ]]; then | |
| command=$(echo "$arguments" | jq -r '.command') | |
| output=$(execute_shell "$command") | |
| add_tool_response "$tool_call_id" "$output" | |
| fi | |
| done | |
| # Continue loop to get next response | |
| continue | |
| else | |
| # No tool calls, this is the final response | |
| content=$(echo "$assistant_msg" | jq -r '.content // ""') | |
| if [[ -n "$content" ]]; then | |
| echo "" | |
| echo "Agent: $content" | |
| # Add assistant message to conversation | |
| add_message "assistant" "$content" | |
| fi | |
| break | |
| fi | |
| done | |
| done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment