Skip to content

Instantly share code, notes, and snippets.

@weehongkoh
Last active May 3, 2026 14:18
Show Gist options
  • Select an option

  • Save weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee to your computer and use it in GitHub Desktop.

Select an option

Save weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee to your computer and use it in GitHub Desktop.
Terminal Setup

Terminal Command

Theses are the configuration files used by .zshrc.

Installation

Install Package

bash -c "$(curl -fsSL https://gist.githubusercontent.com/weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee/raw/install_package.sh)"

Install Flutter and Android SDK

bash -c "$(curl -fsSL https://gist.githubusercontent.com/weehongkoh/72bdb76beacacf2ca3dd39a72395b9ee/raw/flutter_android_installer.sh)"
# =============================================================================
# OS-SPECIFIC ALIASES
# =============================================================================
if [[ "$OSTYPE" == "linux-gnu"* ]] && command -v apt-get >/dev/null 2>&1; then
# Linux Only System update & cleanup
alias uu='sudo apt-get update && \
sudo apt-get upgrade -y && \
sudo apt-get full-upgrade -y && \
sudo apt-get autoremove -y && \
sudo apt-get autoclean -y && \
sudo apt-get clean'
elif [[ "$OSTYPE" == "darwin"* ]]; then
# macOS Only
alias flushdns='sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder'
fi
# =============================================================================
# UNIVERSAL GIT ALIASES (Fixed: Removed broken "$@" from aliases)
# =============================================================================
alias gph='git push'
alias gco='git checkout'
alias gbh='git branch'
alias gmt='git commit'
alias gpl='git pull'
alias grb='git rebase'
alias grt='git reset'
alias gst='git status'
alias grmrf='git checkout -- . && git clean -fd'
#!/bin/bash
# Exit immediately if a command exits with a non-zero status
set -e
echo "=== 1. Updating System and Installing Prerequisites ==="
sudo apt-get update
sudo apt-get install -y curl git unzip xz-utils zip libglu1-mesa
echo "=== 2. Installing Flutter SDK ==="
# Defaulting to the root of the Home directory
FLUTTER_ROOT="$HOME/flutter"
if [ ! -d "$FLUTTER_ROOT" ]; then
echo "Downloading Flutter SDK (stable channel) to $FLUTTER_ROOT..."
git clone https://github.com/flutter/flutter.git -b stable "$FLUTTER_ROOT"
else
echo "Flutter directory already exists at $FLUTTER_ROOT. Skipping clone."
fi
# Temporarily add Flutter to the current path for script execution
export PATH="$FLUTTER_ROOT/bin:$PATH"
echo "Pre-caching Flutter binaries..."
flutter precache
echo "=== 3. Installing Android SDK Command Line Tools ==="
ANDROID_HOME="$HOME/Android/Sdk"
CMDLINE_TOOLS_URL="https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip"
CMDLINE_ZIP="/tmp/cmdline-tools.zip"
mkdir -p "$ANDROID_HOME/cmdline-tools"
echo "Downloading Android Command Line Tools..."
curl -L -o "$CMDLINE_ZIP" "$CMDLINE_TOOLS_URL"
echo "Extracting tools..."
unzip -q "$CMDLINE_ZIP" -d "$ANDROID_HOME/cmdline-tools"
rm "$CMDLINE_ZIP"
# Restructure so sdkmanager works correctly (requires 'latest' directory)
if [ -d "$ANDROID_HOME/cmdline-tools/cmdline-tools" ]; then
rm -rf "$ANDROID_HOME/cmdline-tools/latest" # Clean up old versions if they exist
mv "$ANDROID_HOME/cmdline-tools/cmdline-tools" "$ANDROID_HOME/cmdline-tools/latest"
fi
# Temporarily export paths for sdkmanager
export ANDROID_HOME="$ANDROID_HOME"
export PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$PATH"
echo "=== 4. Configuring Android SDK Packages ==="
echo "Accepting Android licenses..."
yes | sdkmanager --licenses > /dev/null 2>&1
echo "Installing essential Android packages..."
sdkmanager "platform-tools" "platforms;android-34" "build-tools;34.0.0"
echo "=== 5. Linking Flutter and Android SDK ==="
flutter config --android-sdk "$ANDROID_HOME"
echo "Accepting Flutter Android licenses..."
yes | flutter doctor --android-licenses
echo "=== 6. Updating ~/.pathrc ==="
PATHRC_FILE="$HOME/.pathrc"
# Check if we've already added the Flutter/Android block
if ! grep -q "=== FLUTTER & ANDROID PATHS ===" "$PATHRC_FILE"; then
echo "Appending paths to $PATHRC_FILE..."
cat << 'EOF' >> "$PATHRC_FILE"
# === FLUTTER & ANDROID PATHS ===
# This block was added by the installation script
# Flutter
FLUTTER_BIN="$HOME/flutter/bin"
if [ -d "$FLUTTER_BIN" ]; then
case ":$NEW_PATH:" in
*":$FLUTTER_BIN:"*) ;;
*) NEW_PATH="$NEW_PATH:$FLUTTER_BIN" ;;
esac
fi
# Android SDK
export ANDROID_HOME="$HOME/Android/Sdk"
# Android Tools (Command line & Platform tools)
for DIR in "$ANDROID_HOME/cmdline-tools/latest/bin" "$ANDROID_HOME/platform-tools"; do
if [ -d "$DIR" ]; then
case ":$NEW_PATH:" in
*":$DIR:"*) ;;
*) NEW_PATH="$NEW_PATH:$DIR" ;;
esac
fi
done
# === Export final PATH ===
export PATH="$NEW_PATH"
EOF
echo "Successfully updated $PATHRC_FILE!"
else
echo "Paths already detected in $PATHRC_FILE. Skipping update."
fi
echo "=== Installation Complete! ==="
echo "Run 'source ~/.pathrc' to refresh your current terminal."
# =============================================================================
# CUSTOM FUNCTIONS
# =============================================================================
# -----------------------------------------------------------------------------
# Function: clip
# -----------------------------------------------------------------------------
clip() {
local cmd
local args=()
if command -v pbcopy >/dev/null 2>&1; then
cmd="pbcopy" # macOS
elif grep -qi "microsoft" /proc/version 2>/dev/null && command -v clip.exe >/dev/null 2>&1; then
cmd="clip.exe" # WSL
elif [ "$XDG_SESSION_TYPE" = "wayland" ] && command -v wl-copy >/dev/null 2>&1; then
cmd="wl-copy" # Linux Wayland
elif command -v xclip >/dev/null 2>&1; then
cmd="xclip" # Linux X11 (Fallback 1)
args=("-selection" "clipboard")
elif command -v xsel >/dev/null 2>&1; then
cmd="xsel" # Linux X11 (Fallback 2)
args=("--clipboard" "--input")
else
printf "Error: No supported clipboard utility found.\n" >&2
return 1
fi
if [ $# -gt 0 ]; then
if [ -f "$1" ]; then
"$cmd" "${args[@]}" < "$1"
echo "Copied contents of '$1' to clipboard."
else
printf "Error: File '%s' not found.\n" "$1" >&2
return 1
fi
else
"$cmd" "${args[@]}"
fi
}
# -----------------------------------------------------------------------------
# Function: open (Cross-platform file/directory opener)
# -----------------------------------------------------------------------------
open_file() {
local target="${1:-.}"
if [[ "$OSTYPE" == "darwin"* ]]; then
# Use native macOS open
command open "$target"
elif grep -qi "microsoft" /proc/version 2>/dev/null; then
# Use WSL Explorer
if command -v wslpath >/dev/null 2>&1 && command -v explorer.exe >/dev/null 2>&1; then
explorer.exe "$(wslpath -w "$target")"
else
printf "Error: 'wslpath' or 'explorer.exe' not found. Windows interop might be disabled.\n" >&2
return 1
fi
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
# Use Linux xdg-open
if command -v xdg-open >/dev/null 2>&1; then
xdg-open "$target"
else
printf "Error: 'xdg-open' not found.\n" >&2
return 1
fi
fi
}
alias open='open_file'
# -----------------------------------------------------------------------------
# Function: clear_history
# -----------------------------------------------------------------------------
clear_history() {
history -p
}
# -----------------------------------------------------------------------------
# Function: claude
# -----------------------------------------------------------------------------
claude() {
if ! command -v claude >/dev/null 2>&1; then
printf "Error: 'claude' CLI is not installed or not in your PATH.\n" >&2
return 1
fi
if [[ "$1" == "--yolo" ]]; then
shift
command claude --dangerously-skip-permissions "$@"
return $?
fi
command claude "$@"
}
# -----------------------------------------------------------------------------
# Function: create
# -----------------------------------------------------------------------------
create() {
if ! command -v install >/dev/null 2>&1; then
printf "Error: 'install' coreutil is not available on this system.\n" >&2
return 1
fi
if [ $# -eq 0 ]; then
printf "Usage: create <file> [file ...]\n" >&2
return 2
fi
for p in "$@"; do
if [ "${p#\~}" != "$p" ]; then
p="${HOME}${p#\~}"
fi
if [ -z "$p" ] || [ "$p" = "/" ]; then
printf "create: refusing to operate on '%s'\n" "$p" >&2
return 1
fi
if [ -e "$p" ]; then
printf "create: '%s' already exists. Skipping.\n" "$p" >&2
continue
fi
if ! install -D /dev/null "$p"; then
printf "create: failed to create '%s'\n" "$p" >&2
return 1
fi
done
return 0
}
# -----------------------------------------------------------------------------
# Function: check_port
# -----------------------------------------------------------------------------
check_port() {
local PORT=$1
if [ -z "$PORT" ]; then
printf "Usage: check_port <port_number>\n" >&2
return 1
fi
if [[ "$OSTYPE" == "darwin"* ]]; then
if lsof -i :"$PORT" >/dev/null 2>&1; then
echo "Port $PORT is IN USE"
lsof -i :"$PORT"
else
echo "Port $PORT is FREE"
fi
else
if ! command -v ss >/dev/null 2>&1; then
printf "Error: 'ss' command not found. This function requires Linux iproute2 utilities.\n" >&2
return 1
fi
if sudo ss -tulnp | grep -q ":$PORT "; then
echo "Port $PORT is IN USE"
sudo ss -tulnp | grep ":$PORT "
else
echo "Port $PORT is FREE"
fi
fi
}
# -----------------------------------------------------------------------------
# Function: append_path
# -----------------------------------------------------------------------------
append_path() {
local dir="$1"
if [ -d "$dir" ]; then
case ":$PATH:" in
*":$dir:"*) ;; # Already in PATH
*) PATH="$PATH:$dir" ;;
esac
elif [ "${SUPPRESS_WARNINGS:-0}" -ne 1 ]; then
echo "Warning: $dir does not exist." >&2
fi
}
# -----------------------------------------------------------------------------
# Function: add_path_to_config
# -----------------------------------------------------------------------------
add_path_to_config() {
local new_path="$1"
local config_file="$HOME/.bashrc"
if [[ "$SHELL" == *"zsh"* ]]; then
config_file="$HOME/.zshrc"
fi
if [ -z "$new_path" ]; then
printf "Usage: addpath <directory>\n" >&2
return 1
fi
new_path="${new_path/#\~/$HOME}"
if [ ! -d "$new_path" ]; then
printf "Warning: '%s' does not exist.\n" "$new_path" >&2
read -p "Do you still want to add it to your config? (y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
return 1
fi
fi
if grep -Fq "append_path \"$new_path\"" "$config_file" || grep -Fq "append_path '$new_path'" "$config_file"; then
printf "Notice: '%s' is already in your %s.\n" "$new_path" "$config_file"
return 0
fi
if grep -q "^export PATH" "$config_file"; then
awk -v new_cmd="append_path \"$new_path\"" '/^export PATH/{print new_cmd}1' "$config_file" > "${config_file}.tmp" && mv "${config_file}.tmp" "$config_file"
else
echo "append_path \"$new_path\"" >> "$config_file"
echo "export PATH" >> "$config_file"
fi
printf "✅ Success: Added '%s' to %s\n" "$new_path" "$config_file"
if type append_path >/dev/null 2>&1; then
append_path "$new_path"
export PATH
printf "🚀 Path is now active in your current session!\n"
fi
}
#!/usr/bin/env bash
set -u
# =============================
# COLORS & LOGGING
# =============================
GREEN="\033[0;32m"
RED="\033[0;31m"
YELLOW="\033[1;33m"
BLUE="\033[0;34m"
NC="\033[0m"
log() { echo -e "${GREEN}▶ $*${NC}"; }
warn() { echo -e "${YELLOW}⚠ $*${NC}"; }
err() { echo -e "${RED}✖ $*${NC}"; }
info() { echo -e "${BLUE}ℹ $*${NC}"; }
# =============================
# HELPERS
# =============================
require_cmd() {
command -v "$1" >/dev/null 2>&1 || {
return 1
}
}
require_node() {
require_cmd node || {
warn "Node.js not found. Install NVM (Option 2) first, then restart your shell."
return 1
}
}
cleanup_python_tmp() {
if ls /tmp/Python-* >/dev/null 2>&1; then
warn "Cleaning leftover Python build files..."
sudo rm -rf /tmp/Python-* || true
log "Python build cleanup completed."
fi
}
# =============================
# OS DETECTION
# =============================
detect_os() {
if [[ "$OSTYPE" == "darwin"* ]]; then
echo "macOS"
elif grep -qi microsoft /proc/version 2>/dev/null; then
echo "WSL"
elif [[ -f /etc/os-release ]]; then
. /etc/os-release
[[ "$ID" == "ubuntu" ]] && echo "Ubuntu" || echo "Linux"
else
echo "Unknown"
fi
}
OS=$(detect_os)
log "Detected OS: $OS"
echo
# =============================
# CORE INSTALLERS (No Dependencies)
# =============================
install_build_tools() {
log "Installing GCC & Make (Build Essentials)..."
if [[ "$OS" == "Ubuntu" || "$OS" == "WSL" ]]; then
sudo apt-get update && sudo apt-get install -y build-essential
elif [[ "$OS" == "macOS" ]]; then
xcode-select --install || warn "Xcode tools might already be installed"
else
warn "Manual installation required for this OS."
fi
}
install_nvm() {
log "Installing NVM..."
curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
local SOURCERC="$HOME/.sourcerc"
if [[ -f "$SOURCERC" ]] && ! grep -q "NVM_DIR" "$SOURCERC"; then
log "Injecting NVM configuration into .sourcerc..."
cat << 'EOF' >> "$SOURCERC"
# ==========================================
# NVM (Node Version Manager)
# ==========================================
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
EOF
fi
info "NOTE: After installing NVM, restart your terminal and run: 'nvm install --lts'"
}
install_python() {
log "Installing Python 3..."
bash -c "$(curl -fsSL https://gist.githubusercontent.com/weehongkoh/e56d8161fa47e3ac8416ad8340ee3f82/raw/python_installer.sh)"
cleanup_python_tmp
}
install_go() {
log "Installing Go..."
bash -c "$(curl -fsSL https://gist.githubusercontent.com/weehongkoh/e56d8161fa47e3ac8416ad8340ee3f82/raw/go_installer.sh)"
local PATHRC_FILE="$HOME/.pathrc"
if [[ -f "$PATHRC_FILE" ]] && ! grep -q "GO_BIN" "$PATHRC_FILE"; then
log "Injecting Go bin path into .pathrc..."
awk -v block='
# === GO PATH ===
GO_BIN="$HOME/go/bin"
if [ -d "$GO_BIN" ]; then
case ":$NEW_PATH:" in
*":$GO_BIN:"*) ;;
*) NEW_PATH="$NEW_PATH:$GO_BIN" ;;
esac
else
if [ "$SUPPRESS_WARNINGS" -ne 1 ]; then
echo "Warning: $GO_BIN does not exist." >&2
fi
fi
' '/# === Export final PATH ===/ {print block} 1' "$PATHRC_FILE" > "${PATHRC_FILE}.tmp" && mv "${PATHRC_FILE}.tmp" "$PATHRC_FILE"
fi
}
install_sdkman() {
log "Installing SDKMAN..."
export SDKMAN_DIR="$HOME/.sdkman"
curl -fsSL https://get.sdkman.io | bash
# Check if we are on macOS and running a Bash version older than 4
if [[ "$OS" == "macOS" && "${BASH_VERSINFO[0]}" -lt 4 ]]; then
info "macOS with Bash < 4 detected. Routing through Zsh (nonomatch) to bypass strict globbing..."
zsh -o nonomatch <(curl -fsSL "https://get.sdkman.io")
else
curl -fsSL https://get.sdkman.io | bash
fi
local SOURCERC="$HOME/.sourcerc"
if [[ -f "$SOURCERC" ]] && ! grep -q "sdkman-init.sh" "$SOURCERC"; then
log "Injecting SDKMAN configuration into .sourcerc..."
cat << 'EOF' >> "$SOURCERC"
# ==========================================
# SDKMAN (Java/Kotlin Version Manager)
# ==========================================
export SDKMAN_DIR="$HOME/.sdkman"
[[ -s "$SDKMAN_DIR/bin/sdkman-init.sh" ]] && source "$SDKMAN_DIR/bin/sdkman-init.sh"
EOF
fi
}
install_dotnet() {
log "Installing .NET..."
curl -fsSL https://dot.net/v1/dotnet-install.sh | bash
local PATHRC_FILE="$HOME/.pathrc"
if [[ -f "$PATHRC_FILE" ]] && ! grep -q "DOTNET_TOOLS" "$PATHRC_FILE"; then
log "Injecting .NET Tools path into .pathrc..."
awk -v block='
# === .NET TOOLS PATH ===
DOTNET_TOOLS="$HOME/.dotnet/tools"
if [ -d "$DOTNET_TOOLS" ]; then
case ":$NEW_PATH:" in
*":$DOTNET_TOOLS:"*) ;;
*) NEW_PATH="$NEW_PATH:$DOTNET_TOOLS" ;;
esac
else
if [ "$SUPPRESS_WARNINGS" -ne 1 ]; then
echo "Warning: $DOTNET_TOOLS does not exist." >&2
fi
fi
' '/# === Export final PATH ===/ {print block} 1' "$PATHRC_FILE" > "${PATHRC_FILE}.tmp" && mv "${PATHRC_FILE}.tmp" "$PATHRC_FILE"
fi
info "NOTE: .NET is installed to ~/.dotnet by default."
}
# =============================
# STANDALONE CLI TOOLS
# =============================
install_docker() {
log "Checking Docker installation..."
if ! command -v docker >/dev/null; then
if [[ "$OS" == "macOS" ]]; then
if command -v brew >/dev/null; then
log "Detected Homebrew. Installing Docker Desktop..."
brew install --cask docker
info "Docker Desktop installed. Launch it from Applications to finish setup."
return 0
else
err "Homebrew not found. Install Docker Desktop manually."
return 1
fi
else
log "Installing Docker Engine..."
curl -fsSL https://get.docker.com | sh
fi
else
log "Docker is already installed."
fi
if [[ "$OS" != "macOS" ]]; then
log "Verifying Docker permissions..."
if ! groups "$USER" | grep -q docker; then
log "Adding $USER to 'docker' group..."
sudo usermod -aG docker "$USER"
fi
if [[ -S /var/run/docker.sock ]]; then
log "Applying immediate permission fix to Docker socket..."
sudo chmod 666 /var/run/docker.sock
fi
fi
}
install_aws() {
log "Installing AWS CLI..."
if command -v aws >/dev/null; then warn "AWS CLI already installed"; return; fi
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip -q awscliv2.zip && sudo ./aws/install && rm -rf aws awscliv2.zip
}
install_ngrok() {
log "Installing Ngrok..."
if command -v ngrok >/dev/null; then warn "Ngrok is already installed"; return; fi
if [[ "$OS" == "macOS" ]]; then
if command -v brew >/dev/null; then
brew install ngrok/ngrok/ngrok
else
warn "Homebrew not found."
fi
else
log "Installing Ngrok (Linux/WSL)..."
curl -sSL https://ngrok-agent.s3.amazonaws.com/ngrok.asc \
| sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null \
&& echo "deb https://ngrok-agent.s3.amazonaws.com bookworm main" \
| sudo tee /etc/apt/sources.list.d/ngrok.list \
&& sudo apt update \
&& sudo apt install -y ngrok
fi
info "Run 'ngrok config add-authtoken <YOUR_TOKEN>' to authenticate."
}
install_gh() {
log "Installing GitHub CLI (gh)..."
if command -v gh >/dev/null; then warn "GitHub CLI already installed"; return; fi
if [[ "$OS" == "macOS" ]]; then
if command -v brew >/dev/null; then
brew install gh
else
warn "Homebrew not found."
fi
else
log "Setting up GitHub apt repository..."
type -p curl >/dev/null || (sudo apt update && sudo apt install curl -y)
sudo mkdir -p -m 755 /etc/apt/keyrings
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \
&& sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
&& sudo apt update \
&& sudo apt install gh -y
fi
info "GitHub CLI installed. Run 'gh auth login' to authenticate."
}
install_glab() {
log "Installing GitLab CLI (glab)..."
if command -v glab >/dev/null; then warn "GitLab CLI already installed"; return; fi
if [[ "$OS" == "macOS" ]]; then
if command -v brew >/dev/null; then brew install glab; fi
else
curl -sL https://gitlab.com/gitlab-org/cli/-/raw/main/scripts/install.sh | sudo bash
fi
info "GitLab CLI installed. Run 'glab auth login' to authenticate."
}
install_infisical() {
log "Installing Infisical CLI..."
if command -v infisical >/dev/null; then warn "Infisical CLI is already installed"; return; fi
if [[ "$OS" == "macOS" ]]; then
if command -v brew >/dev/null; then
brew install infisical/get-cli/infisical
else
warn "Homebrew not found. Install Infisical manually."
fi
elif [[ "$OS" == "Ubuntu" || "$OS" == "WSL" ]]; then
curl -1sLf 'https://artifacts-cli.infisical.com/setup.deb.sh' | sudo -E bash
sudo apt-get update && sudo apt-get install -y infisical
elif command -v yum >/dev/null; then
curl -1sLf 'https://artifacts-cli.infisical.com/setup.rpm.sh' | sudo -E bash
sudo yum install -y infisical
elif command -v apk >/dev/null; then
apk add --no-cache bash sudo wget
wget -qO- 'https://artifacts-cli.infisical.com/setup.apk.sh' | sudo sh
apk update && sudo apk add infisical
else
warn "Unsupported OS for automatic Infisical installation. Please install manually."
fi
info "Infisical CLI installed. Run 'infisical login' to authenticate."
}
install_claude() { log "Installing Claude Code (Native)..."; curl -fsSL https://claude.ai/install.sh | bash; }
install_opencode() {
log "Installing OpenCode (opencode.ai)..."
curl -fsSL https://opencode.ai/install | bash
local PATHRC_FILE="$HOME/.pathrc"
if [[ -f "$PATHRC_FILE" ]] && ! grep -q "OPENCODE_PATH" "$PATHRC_FILE"; then
log "Injecting OpenCode configuration into .pathrc..."
awk -v block='
# === OPENCODE PATH ===
OPENCODE_PATH="$HOME/.opencode/bin"
if [ -d "$OPENCODE_PATH" ]; then
case ":$NEW_PATH:" in
*":$OPENCODE_PATH:"*) ;; # Already in PATH
*) NEW_PATH="$NEW_PATH:$OPENCODE_PATH" ;;
esac
else
if [ "$SUPPRESS_WARNINGS" -ne 1 ]; then
echo "Warning: $OPENCODE_PATH does not exist." >&2
fi
fi
' '/# === Export final PATH ===/ {print block} 1' "$PATHRC_FILE" > "${PATHRC_FILE}.tmp" && mv "${PATHRC_FILE}.tmp" "$PATHRC_FILE"
fi
info "OpenCode installed!"
info "Run 'source ~/.zshrc' to apply path changes, then run 'opencode' to start the agent."
}
# =============================
# DEPENDENT TOOLS (Require Node or GH)
# =============================
install_appwrite() { require_node || return 0; log "Installing Appwrite CLI..."; npm install -g appwrite-cli; }
install_vercel() { require_node || return 0; log "Installing Vercel CLI..."; npm install -g vercel; }
install_trigger() { require_node || return 0; log "Installing Trigger.dev CLI..."; npm install -g @trigger.dev/cli; }
install_gemini() { require_node || return 0; log "Installing Gemini CLI..."; npm install -g @google/gemini-cli; }
install_copilot_cli() {
log "Installing GitHub Copilot CLI (gh extension)..."
# 1. Ensure 'gh' is installed
if ! command -v gh >/dev/null; then
warn "'gh' (GitHub CLI) is not found but is required for Copilot."
install_gh
fi
# 2. Check if user is logged in (optional check, but good for UX)
if ! gh auth status >/dev/null 2>&1; then
warn "You are not logged into GitHub CLI."
info "Please run 'gh auth login' in a separate terminal, then retry this installation."
read -p "Press Enter if you have logged in, or Ctrl+C to exit..."
fi
# 3. Install Extension
log "Installing gh-copilot extension..."
gh extension install github/gh-copilot --force
info "Copilot CLI installed! Usage:"
info " • gh copilot suggest \"how to undo last commit\""
info " • gh copilot explain \"ls -lah\""
}
# =============================
# MENU
# =============================
while true; do
echo "==========================================="
echo " Developer Environment Installer"
echo "==========================================="
echo " --- Core & Runtimes (No Dependencies) ---"
echo " 1) GCC & Make (Build Essentials)"
echo " 2) NVM (Node Version Manager)"
echo " 3) Python 3"
echo " 4) Go"
echo " 5) SDKMAN (Java/Kotlin Version Manager)"
echo " 6) .NET"
echo " --- Standalone Tools (No Dependencies) ---"
echo " 7) Docker (Install or Fix Permissions)"
echo " 8) AWS CLI"
echo " 9) Ngrok"
echo " 10) GitHub CLI (gh)"
echo " 11) GitLab CLI (glab)"
echo " 12) Infisical CLI"
echo " 13) Claude Code CLI (Native)"
echo " 14) OpenCode (opencode.ai)"
echo " --- Dependent Tools (Require Node or GH) ---"
echo " 15) Appwrite CLI (Requires Node)"
echo " 16) Vercel CLI (Requires Node)"
echo " 17) Trigger.dev CLI (Requires Node)"
echo " 18) Google Gemini CLI (Requires Node 20+)"
echo " 19) GitHub Copilot CLI (Requires gh)"
echo "==========================================="
echo " 20) Quit"
echo "==========================================="
echo "👉 You can enter multiple values (e.g., 1 2 10 19)"
read -p "Select: " input
echo
input="${input//,/ }"
for choice in $input; do
case "$choice" in
1) install_build_tools || warn "Build tools failed" ;;
2) install_nvm || warn "NVM install failed" ;;
3) install_python || warn "Python install failed" ;;
4) install_go || warn "Go install failed" ;;
5) install_sdkman || warn "SDKMAN install failed" ;;
6) install_dotnet || warn ".NET install failed" ;;
7) install_docker || warn "Docker install failed" ;;
8) install_aws || warn "AWS install failed" ;;
9) install_ngrok || warn "Ngrok install failed" ;;
10) install_gh || warn "GitHub CLI install failed" ;;
11) install_glab || warn "GitLab CLI install failed" ;;
12) install_infisical || warn "Infisical CLI install failed" ;;
13) install_claude || warn "Claude Code install failed" ;;
14) install_opencode || warn "OpenCode install failed" ;;
15) install_appwrite || warn "Appwrite install failed" ;;
16) install_vercel || warn "Vercel install failed" ;;
17) install_trigger || warn "Trigger.dev install failed" ;;
18) install_gemini || warn "Gemini CLI install failed" ;;
19) install_copilot_cli || warn "Copilot CLI install failed" ;;
20) log "Goodbye!"; exit 0 ;;
*) warn "Skipping invalid option: $choice" ;;
esac
echo
done
read -p "Install more tools? (y/n): " again
[[ "$again" =~ ^[Yy]$ ]] || break
done
log "All requested installations completed!"
# =============================================================================
# PATH MANAGEMENT ENVIRONMENT
# =============================================================================
OS_TYPE="$(uname -s)"
export SUPPRESS_WARNINGS=${SUPPRESS_WARNINGS:-1}
# === HOMEBREW PATH Detection ===
if [ "$OS_TYPE" = "Darwin" ]; then
if [ -d "/opt/homebrew/bin" ]; then
HOMEBREW_PATH="/opt/homebrew/bin"
else
HOMEBREW_PATH="/usr/local/bin"
fi
else
HOMEBREW_PATH="/home/linuxbrew/.linuxbrew/bin"
fi
# === INITIALIZE DIRECTORIES ===
# (Note: 'append_path' works here because we loaded functions first in .zshrc)
append_path "$HOME/.local/bin"
export DOTNET_ROOT="$HOME/.dotnet"
append_path "$DOTNET_ROOT"
append_path "$DOTNET_ROOT/tools"
append_path "$HOMEBREW_PATH"
append_path "$HOME/go/bin"
append_path "/opt/sonar-scanner/bin"
append_path "$HOME/.opencode/bin"
# Export the final updated PATH
export PATH
# =============================================================================
# FILE: ~/.sourcerc
# Description: Initializes third-party package managers and Oh My Zsh.
# =============================================================================
# Source the custom environment setup file if it exists and is not empty
if [[ -s "$HOME/.local/bin/env" ]]; then
source "$HOME/.local/bin/env"
fi
# Source the SDKMAN initialization script if it exists and is not empty
if [[ -s "$HOME/.sdkman/bin/sdkman-init.sh" ]]; then
source "$HOME/.sdkman/bin/sdkman-init.sh"
fi
# Source the NVM initialization script if it exists and is not empty
if [[ -s "$HOME/.nvm/nvm.sh" ]]; then
source "$HOME/.nvm/nvm.sh"
fi
# ==========================================
# CLAUDE CODE / AI GATEWAY
# ==========================================
# Source Oh My Zsh if it exists and is not empty
# NOTE: ZSH_THEME and plugins must be defined before this runs!
if [[ -s "$HOME/.oh-my-zsh/oh-my-zsh.sh" ]]; then
source "$HOME/.oh-my-zsh/oh-my-zsh.sh"
else
echo "Warning: Oh My Zsh not found or is empty."
fi
" Enable line numbers
set number
" Enable relative line numbers
set relativenumber
" Enable syntax highlighting
syntax on
" Set colorscheme
colorscheme slate
" Enable file type detection and plugins
filetype plugin indent on
" Set the tab width to 4 spaces
set tabstop=4
set shiftwidth=4
set expandtab
" Enable auto-indentation
set autoindent
set smartindent
" Highlight current line
set cursorline
" Show matching parentheses
set showmatch
" Enable line wrapping
set wrap
" Enable mouse support
set mouse=a
" Enable clipboard access
set clipboard=unnamedplus
" Disable swap file
set noswapfile
" Enable incremental search
set incsearch
" Ignore case in search
set ignorecase
" Override ignorecase if search contains capital letters
set smartcase
" Display line and column number of the cursor position
set ruler
" Set the status line at the bottom
set laststatus=2
" Show command in bottom bar
set showcmd
" Set command height
set cmdheight=2
" Set history lines
set history=1000
" Disable backup file
set nobackup
" Enable persistent undo
set undofile
" Set maximum number of undo levels
set undolevels=1000
" Set undo directory
if has("persistent_undo")
silent !mkdir ~/.vim/undodir > /dev/null 2>&1
set undodir=~/.vim/undodir
endif
" Set search highlighting
set hlsearch
" Enable visual bell
set visualbell
" Set default file encoding
set encoding=utf-8
" Set the leader key to space
let mapleader = " "
" Map <Leader>w to save the file
nnoremap <Leader>w :w<CR>
" Map <Leader>q to quit
nnoremap <Leader>q :q<CR>
" Map <Leader>x to save and quit
nnoremap <Leader>x :wq<CR>
" Enable folding
set foldmethod=syntax
set foldlevelstart=99
" Enable line wrapping at 80 characters
set textwidth=80
set colorcolumn=80
" Add some basic key mappings
" Map jj to escape insert mode
inoremap jj <Esc>
" Map <Leader>n to toggle line numbers
nnoremap <Leader>n :set number!<CR>
" Map <Leader>r to toggle relative line numbers
nnoremap <Leader>r :set relativenumber!<CR>
" Configure plugins (if you use a plugin manager like vim-plug)
" Example with vim-plug:
" call plug#begin('~/.vim/plugged')
" Plug 'tpope/vim-sensible'
" Plug 'preservim/nerdtree'
" Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
" Plug 'airblade/vim-gitgutter'
" call plug#end()
" NERDTree key mappings
" nnoremap <C-n> :NERDTreeToggle<CR>
" Enable automatic hard wrapping at textwidth (80 chars)
set formatoptions+=t " Auto-wrap text using textwidth
set formatoptions+=c " Auto-wrap comments using textwidth
set formatoptions+=r " Continue comments when pressing Enter
set formatoptions+=o " Continue comments when using 'o' or 'O'
set formatoptions+=q " Allow formatting of comments with 'gq'
set formatoptions+=n " Recognize numbered lists
set formatoptions+=l " Don't break lines that were already long
" Enable syntax highlighting
syntax on
" Increase memory limit for complex syntax parsing (Prevents E363)
set maxmempattern=20000
# =============================================================================
# 1. ZSH THEME & PLUGINS (Must be defined before Oh My Zsh loads)
# =============================================================================
export ZSH="$HOME/.oh-my-zsh"
ZSH_THEME="spaceship"
SPACESHIP_PROMPT_ORDER=( user dir git exec_time line_sep jobs exit_code char )
SPACESHIP_USER_SHOW=always
SPACESHIP_PROMPT_SEPARATE_LINE=true
SPACESHIP_PROMPT_ADD_NEWLINE=true
SPACESHIP_CHAR_SYMBOL="❯"
SPACESHIP_CHAR_SUFFIX=" "
plugins=(git zsh-syntax-highlighting zsh-autosuggestions)
# =============================================================================
# 2. THIRD-PARTY INITIALIZATION (SDKMAN, NVM, Oh My Zsh)
# =============================================================================
[[ -s "$HOME/.sourcerc" ]] && source "$HOME/.sourcerc"
# =============================================================================
# 3. CUSTOM OVERRIDES (Functions must load before Path Management)
# =============================================================================
[[ -s "$HOME/.func" ]] && source "$HOME/.func"
# =============================================================================
# 4. PATH MANAGEMENT (Relies on append_path from .func)
# =============================================================================
[[ -s "$HOME/.pathrc" ]] && source "$HOME/.pathrc"
# =============================================================================
# 5. ALIASES (Loaded dead last so YOUR code always wins over Oh My Zsh)
# =============================================================================
[[ -s "$HOME/.alias" ]] && source "$HOME/.alias"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment