Skip to content

Instantly share code, notes, and snippets.

@ericboehs
Created June 5, 2025 02:03
Show Gist options
  • Save ericboehs/e9b3d3a6ef1bc252e4cecc837d12e9d6 to your computer and use it in GitHub Desktop.
Save ericboehs/e9b3d3a6ef1bc252e4cecc837d12e9d6 to your computer and use it in GitHub Desktop.
Lightweight Devin CLI in Bash with fzf session selection
#!/usr/bin/env bash
#
# Lightweight Devin CLI in Bash
#
# Usage:
# devin new "<prompt>"
# devin message <session_id> "<text>"
# devin list [--all]
#
set -e
# Check for DEVIN_API_KEY
if [ -z "$DEVIN_API_KEY" ]; then
echo "⚠️ Please set DEVIN_API_KEY in your environment." >&2
exit 1
fi
# Base URL and headers
BASE_URL="https://api.devin.ai/v1"
AUTH_HEADER="Authorization: Bearer $DEVIN_API_KEY"
CONTENT_HEADER="Content-Type: application/json"
cmd="$1"; shift || true
case "$cmd" in
new)
# devin new "<prompt>"
prompt="$*"
if [ -z "$prompt" ]; then
echo "Usage: devin new \"<prompt>\"" >&2
exit 1
fi
# Safely build JSON payload via jq
payload=$(jq -n --arg p "$prompt" '{prompt: $p}')
# POST /sessions
resp=$(curl -s -H "$AUTH_HEADER" -H "$CONTENT_HEADER" -d "$payload" "$BASE_URL/sessions")
# Extract and print the URL
url=$(echo "$resp" | jq -r .url)
echo "$url"
;;
message)
# devin message [<session_id>] "<text>"
# Check if first argument looks like a session ID (starts with "devin-")
if [[ "$1" =~ ^devin- ]]; then
sid="$1"
shift
message_text="$*"
else
sid=""
message_text="$*"
fi
if [ -z "$message_text" ]; then
echo "Usage: devin message [<session_id>] \"<text>\"" >&2
exit 1
fi
# Auto-select session if not provided
if [ -z "$sid" ]; then
# Get running sessions first
resp=$(curl -s -H "$AUTH_HEADER" "$BASE_URL/sessions")
running_sessions=$(echo "$resp" | jq -r '.sessions[] | select(.status == "running") | "\(.session_id)\t\(.status)\t\(.title)"')
if [ -z "$running_sessions" ]; then
# No running sessions, get all sessions
all_sessions=$(echo "$resp" | jq -r '.sessions[] | "\(.session_id)\t\(.status)\t\(.title)"')
if [ -z "$all_sessions" ]; then
echo "❌ No sessions found" >&2
exit 1
fi
session_count=$(echo "$all_sessions" | wc -l)
if [ "$session_count" -eq 1 ]; then
sid=$(echo "$all_sessions" | cut -f1)
else
if ! command -v fzf >/dev/null 2>&1; then
echo "❌ Multiple sessions found. Please install fzf or provide a session_id explicitly:" >&2
echo " brew install fzf # or your package manager" >&2
echo " devin message <session_id> \"<text>\"" >&2
exit 1
fi
echo "📋 No running sessions. Select from all sessions:"
selected=$(echo "$all_sessions" | fzf --with-nth=2,3 --delimiter='\t' | cut -f1)
if [ -z "$selected" ]; then
echo "❌ No session selected" >&2
exit 1
fi
sid="$selected"
fi
else
session_count=$(echo "$running_sessions" | wc -l)
if [ "$session_count" -eq 1 ]; then
sid=$(echo "$running_sessions" | cut -f1)
else
if ! command -v fzf >/dev/null 2>&1; then
echo "❌ Multiple running sessions found. Please install fzf or provide a session_id explicitly:" >&2
echo " brew install fzf # or your package manager" >&2
echo " devin message <session_id> \"<text>\"" >&2
exit 1
fi
echo "🏃 Select running session:"
selected=$(echo "$running_sessions" | fzf --with-nth=2,3 --delimiter='\t' | cut -f1)
if [ -z "$selected" ]; then
echo "❌ No session selected" >&2
exit 1
fi
sid="$selected"
fi
fi
fi
# Safely build JSON payload via jq
payload=$(jq -n --arg m "$message_text" '{message: $m}')
# POST /session/{id}/message
http_code=$(curl -s -o /dev/null -w "%{http_code}" \
-X POST \
-H "$AUTH_HEADER" -H "$CONTENT_HEADER" \
-d "$payload" \
"$BASE_URL/session/$sid/message")
if [[ "$http_code" =~ ^2 ]]; then
echo "✅ message sent"
else
echo "❌ HTTP $http_code" >&2
exit 1
fi
;;
list)
# devin list [--all]
show_all=false
if [ "$1" == "--all" ]; then
show_all=true
fi
# GET /sessions
resp=$(curl -s -H "$AUTH_HEADER" "$BASE_URL/sessions")
# Check if response is valid JSON
if ! echo "$resp" | jq . >/dev/null 2>&1; then
echo "❌ API Error: $resp" >&2
exit 1
fi
if [ "$show_all" = true ]; then
# Print every session
echo "$resp" | jq -r '.sessions[] | "\(.session_id)\t\(.status)\t\(.title)"'
else
# Only show running sessions
echo "$resp" | jq -r '.sessions[] | select(.status == "running") | "\(.session_id)\t\(.status)\t\(.title)"'
fi
;;
*)
echo "Usage: devin {new \"<prompt>\" | message [<session_id>] \"<text>\" | list [--all]}" >&2
exit 1
;;
esac
@ericboehs
Copy link
Author

Lightweight Devin CLI

A Bash script for interacting with the Devin API with enhanced session management and fzf integration.

Features

  • Smart session selection: Auto-selects single sessions, uses fzf for multiple sessions
  • Prioritizes running sessions: Shows only active sessions by default
  • Session ID detection: Automatically detects if first argument is a session ID (starts with devin-)
  • Error handling: Graceful handling of API errors and missing dependencies
  • Clean output: Only shows what matters most

Usage

# Create a new session
devin new "implement user authentication"

# Send message (auto-selects if 1 session, uses fzf if multiple)
devin message "add unit tests"

# Send message to specific session
devin message devin-abc123 "fix the bug in login.js"

# List running sessions only
devin list

# List all sessions (including suspended/exit)
devin list --all

Setup

  1. Set your API key: export DEVIN_API_KEY=your_key_here
  2. Install fzf for session selection: brew install fzf
  3. Make executable: chmod +x devin

Dependencies

  • curl - for API requests
  • jq - for JSON parsing
  • fzf - for interactive session selection (optional, fallback to explicit session IDs)

If fzf is not installed and multiple sessions exist, the script will provide helpful instructions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment