139 lines
4.4 KiB
Bash
139 lines
4.4 KiB
Bash
|
|
#!/bin/bash
|
||
|
|
# ==============================================================================
|
||
|
|
# ai-tabs - Ultra Orchestrator
|
||
|
|
# Version: v1.0.0
|
||
|
|
# License: MIT
|
||
|
|
# Author: Fu-Jie
|
||
|
|
# Description: Batch-launches and orchestrates multiple AI CLI tools as Tabs.
|
||
|
|
# ==============================================================================
|
||
|
|
|
||
|
|
# 1. Single-Instance Lock
|
||
|
|
LOCK_FILE="/tmp/ai_terminal_launch.lock"
|
||
|
|
# If lock is less than 10 seconds old, another instance is running. Exit.
|
||
|
|
if [ -f "$LOCK_FILE" ]; then
|
||
|
|
LOCK_TIME=$(stat -f %m "$LOCK_FILE")
|
||
|
|
NOW=$(date +%s)
|
||
|
|
if (( NOW - LOCK_TIME < 10 )); then
|
||
|
|
echo "⚠️ Another launch in progress. Skipping to prevent duplicates."
|
||
|
|
exit 0
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
touch "$LOCK_FILE"
|
||
|
|
trap 'rm -f "$LOCK_FILE"' EXIT
|
||
|
|
|
||
|
|
# 2. Configuration & Constants
|
||
|
|
INIT_DELAY=4.5
|
||
|
|
PASTE_DELAY=0.3
|
||
|
|
CMD_CREATION_DELAY=0.3
|
||
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
|
|
PARENT_DIR="$(dirname "$SCRIPT_DIR")"
|
||
|
|
|
||
|
|
# Search for .env
|
||
|
|
if [ -f "${SCRIPT_DIR}/.env" ]; then
|
||
|
|
ENV_FILE="${SCRIPT_DIR}/.env"
|
||
|
|
elif [ -f "${PARENT_DIR}/.env" ]; then
|
||
|
|
ENV_FILE="${PARENT_DIR}/.env"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Supported Tools
|
||
|
|
SUPPORTED_TOOLS=(
|
||
|
|
"claude:--continue"
|
||
|
|
"opencode:--continue"
|
||
|
|
"gemini:--resume latest"
|
||
|
|
"copilot:--continue"
|
||
|
|
"iflow:--continue"
|
||
|
|
"kilo:--continue"
|
||
|
|
)
|
||
|
|
|
||
|
|
FOUND_TOOLS_NAMES=()
|
||
|
|
FOUND_CMDS=()
|
||
|
|
|
||
|
|
# 3. Part A: Load Manual Configuration
|
||
|
|
if [ -f "$ENV_FILE" ]; then
|
||
|
|
set -a; source "$ENV_FILE"; set +a
|
||
|
|
for var in $(compgen -v | grep '^TOOL_[0-9]' | sort -V); do
|
||
|
|
TPATH="${!var}"
|
||
|
|
if [ -x "$TPATH" ]; then
|
||
|
|
NAME=$(basename "$TPATH")
|
||
|
|
FLAG="--continue"
|
||
|
|
for item in "${SUPPORTED_TOOLS[@]}"; do
|
||
|
|
[[ "${item%%:*}" == "$NAME" ]] && FLAG="${item#*:}" && break
|
||
|
|
done
|
||
|
|
FOUND_TOOLS_NAMES+=("$NAME")
|
||
|
|
FOUND_CMDS+=("'$TPATH' $FLAG || '$TPATH' || exec \$SHELL")
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
fi
|
||
|
|
|
||
|
|
# 4. Part B: Automatic Tool Discovery
|
||
|
|
for item in "${SUPPORTED_TOOLS[@]}"; do
|
||
|
|
NAME="${item%%:*}"
|
||
|
|
FLAG="${item#*:}"
|
||
|
|
ALREADY_CONFIGURED=false
|
||
|
|
for configured in "${FOUND_TOOLS_NAMES[@]}"; do
|
||
|
|
[[ "$configured" == "$NAME" ]] && ALREADY_CONFIGURED=true && break
|
||
|
|
done
|
||
|
|
[[ "$ALREADY_CONFIGURED" == true ]] && continue
|
||
|
|
TPATH=$(which "$NAME" 2>/dev/null)
|
||
|
|
if [ -z "$TPATH" ]; then
|
||
|
|
SEARCH_PATHS=(
|
||
|
|
"/opt/homebrew/bin/$NAME"
|
||
|
|
"/usr/local/bin/$NAME"
|
||
|
|
"$HOME/.local/bin/$NAME"
|
||
|
|
"$HOME/bin/$NAME"
|
||
|
|
"$HOME/.$NAME/bin/$NAME"
|
||
|
|
"$HOME/.nvm/versions/node/*/bin/$NAME"
|
||
|
|
"$HOME/.npm-global/bin/$NAME"
|
||
|
|
"$HOME/.cargo/bin/$NAME"
|
||
|
|
)
|
||
|
|
for p in "${SEARCH_PATHS[@]}"; do
|
||
|
|
for found_p in $p; do [[ -x "$found_p" ]] && TPATH="$found_p" && break 2; done
|
||
|
|
done
|
||
|
|
fi
|
||
|
|
if [ -n "$TPATH" ]; then
|
||
|
|
FOUND_TOOLS_NAMES+=("$NAME")
|
||
|
|
FOUND_CMDS+=("'$TPATH' $FLAG || '$TPATH' || exec \$SHELL")
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
|
||
|
|
NUM_FOUND=${#FOUND_CMDS[@]}
|
||
|
|
[[ "$NUM_FOUND" -eq 0 ]] && exit 1
|
||
|
|
|
||
|
|
# 5. Core Orchestration (Reset + Launch)
|
||
|
|
# Using Command Palette automation to avoid the need for manual shortcut binding.
|
||
|
|
AS_SCRIPT="tell application \"System Events\"\n"
|
||
|
|
|
||
|
|
# Phase A: Creation (Using Command Palette to ensure it opens in Editor Area)
|
||
|
|
for ((i=1; i<=NUM_FOUND; i++)); do
|
||
|
|
AS_SCRIPT+=" keystroke \"p\" using {command down, shift down}\n"
|
||
|
|
AS_SCRIPT+=" delay 0.1\n"
|
||
|
|
# Ensure we are searching for the command. Using clipboard for speed and universal language support.
|
||
|
|
AS_SCRIPT+=" set the clipboard to \"Terminal: Create New Terminal in Editor Area\"\n"
|
||
|
|
AS_SCRIPT+=" keystroke \"v\" using {command down}\n"
|
||
|
|
AS_SCRIPT+=" delay 0.1\n"
|
||
|
|
AS_SCRIPT+=" keystroke return\n"
|
||
|
|
AS_SCRIPT+=" delay $CMD_CREATION_DELAY\n"
|
||
|
|
done
|
||
|
|
|
||
|
|
# Phase B: Warmup
|
||
|
|
AS_SCRIPT+=" delay $INIT_DELAY\n"
|
||
|
|
|
||
|
|
# Phase C: Command Injection (Reverse)
|
||
|
|
for ((i=NUM_FOUND-1; i>=0; i--)); do
|
||
|
|
FULL_CMD="${FOUND_CMDS[$i]}"
|
||
|
|
CLEAN_CMD=$(echo "$FULL_CMD" | sed 's/"/\\"/g')
|
||
|
|
AS_SCRIPT+=" set the clipboard to \"$CLEAN_CMD\"\n"
|
||
|
|
AS_SCRIPT+=" delay 0.1\n"
|
||
|
|
AS_SCRIPT+=" keystroke \"v\" using {command down}\n"
|
||
|
|
AS_SCRIPT+=" delay $PASTE_DELAY\n"
|
||
|
|
AS_SCRIPT+=" keystroke return\n"
|
||
|
|
if [ $i -gt 0 ]; then
|
||
|
|
AS_SCRIPT+=" delay 0.5\n"
|
||
|
|
AS_SCRIPT+=" keystroke \"[\" using {command down, shift down}\n"
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
AS_SCRIPT+="end tell"
|
||
|
|
|
||
|
|
# Execute
|
||
|
|
echo -e "$AS_SCRIPT" | osascript
|
||
|
|
echo "✨ Ai tabs initialized successfully ($NUM_FOUND tools found)."
|