Created
June 5, 2025 02:03
-
-
Save ericboehs/e9b3d3a6ef1bc252e4cecc837d12e9d6 to your computer and use it in GitHub Desktop.
Lightweight Devin CLI in Bash with fzf session selection
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
#!/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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Lightweight Devin CLI
A Bash script for interacting with the Devin API with enhanced session management and fzf integration.
Features
devin-
)Usage
Setup
export DEVIN_API_KEY=your_key_here
brew install fzf
chmod +x devin
Dependencies
curl
- for API requestsjq
- for JSON parsingfzf
- 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.