Wren Linux

wren-linux / bin / claude

#!/bin/bash
# claude [--account NAME] [ARGS...]: written by Wren Linux extras/claude-vm.sh (and, before they
# were retired, extras/claude-code.sh and extras/claude-regular.sh: a computer that still has one
# is offered it here)
# Starts the Claude Code you have set up. One way and one account: that one. More:
# it asks which, and Enter takes the most walled-off: the VM (claude-vm), then
# its own user (claude-sandbox), then as you (claude-regular). With no terminal to
# ask on, it takes that one without asking. It holds no rights of its own: each
# of those commands does its own checks.
#   --account NAME   only that account's ways
# The VM takes no options of Claude's, so with options it isn't offered.
set -euo pipefail

BIN=$HOME/.local/bin
die() { printf 'claude: %s\n' "$*" >&2; exit 1; }
[ "$(id -u)" -ne 0 ] || die "run it as your normal user, not root."

account=
case ${1:-} in
    --account) [ $# -ge 2 ] || die "--account needs a name: claude --account NAME"; account=$2; shift 2 ;;
    --account=*) account=${1#--account=}; shift ;;
esac
if [ -n "$account" ]; then
    [[ $account =~ ^[a-z][a-z0-9]{0,14}$ ]] || die "an account name is small letters and digits, a letter first."
fi

# From a launcher (Super+d, then claude): a window to ask and run in. Only with
# no terminal at all: claude --help | grep, from a terminal, stays in it. "No
# terminal" is no controlling one (/dev/tty), not what stdin is: sway, started
# on tty1, hands that console to what it starts, out of sight
if ! { : </dev/tty; } 2>/dev/null && [ -n "${WAYLAND_DISPLAY:-}" ] && [ -z "${CLAUDE_CHOOSER_WINDOW:-}" ] && command -v foot >/dev/null; then
    exec foot --title=Claude env CLAUDE_CHOOSER_WINDOW=1 "$0" ${account:+--account "$account"} "$@"
fi

# The accounts of a way: the users root made (their names are checked)
users_like() { # PREFIX
    local name _
    while IFS=: read -r name _; do
        [[ $name =~ ^$1-([a-z][a-z0-9]{0,14})$ ]] && echo "${BASH_REMATCH[1]}"
    done <"${WREN_PASSWD:-/etc/passwd}" | sort
}

kinds=() accounts=() labels=()
offer() { # KIND ACCOUNT LABEL
    [ -z "$account" ] || [ "$account" = "$2" ] || return 0
    kinds+=("$1") accounts+=("$2") labels+=("$3${2:+, account $2}")
}
quiet=false
case ${1:-} in -v | --version | -h | --help) quiet=true ;; esac
if [ -x "$BIN/claude-vm" ] && [ $# -eq 0 ]; then
    offer vm "" "in its own virtual machine (the most walled off)"
    while read -r a; do offer vm "$a" "in its own virtual machine"; done < <(users_like claude-vm)
fi
if [ -x "$BIN/claude-sandbox" ]; then
    offer sandbox "" "as its own user, in ~/projects"
    while read -r a; do offer sandbox "$a" "as its own user, in ~/projects-$a"; done < <(users_like claude-agent)
fi
if [ -x "$BIN/claude-regular" ]; then
    offer regular "" "as you, with your files (its own rules keep it from your keys)"
    for d in "$HOME"/.claude-*/; do
        a=${d%/}; a=${a##*/.claude-}
        [[ $a =~ ^[a-z][a-z0-9]{0,14}$ ]] && [ -f "${d}settings.json" ] && offer regular "$a" "as you, with your files"
    done
fi
if [ ${#kinds[@]} -eq 0 ]; then
    if [ -n "$account" ]; then die "no account $account is set up. Run: wrenctl install claude-vm --account $account"; fi
    if [ $# -gt 0 ] && [ -x "$BIN/claude-vm" ]; then
        die "the VM takes no options of Claude's: claude-vm starts it, and you give them inside."
    fi
    die "Claude Code isn't set up. Run: wrenctl install claude-vm"
fi

pick=0
if [ ${#kinds[@]} -gt 1 ] && [ -t 0 ] && [ -t 1 ] && ! $quiet; then
    echo "Which Claude Code?"
    for i in "${!kinds[@]}"; do printf '  %d) %s\n' "$((i + 1))" "${labels[i]}"; done
    while :; do
        printf 'Enter for 1: '
        read -r answer || answer=
        [ -n "$answer" ] || break
        if [[ $answer =~ ^[0-9]{1,3}$ ]] && [ "$answer" -ge 1 ] && [ "$answer" -le ${#kinds[@]} ]; then
            pick=$((answer - 1))
            break
        fi
    done
fi

kind=${kinds[pick]} a=${accounts[pick]}
case $kind in
    vm) exec "$BIN/claude-vm" ${a:+--account "$a"} ;;
    sandbox)
        # It works only in its folder: start there unless you are in it already
        projects=$HOME/projects${a:+-$a}
        case $(pwd -P) in
            "$projects" | "$projects"/*) ;;
            *) if ! $quiet; then cd "$projects" 2>/dev/null || true; fi ;;
        esac
        exec "$BIN/claude-sandbox" ${a:+--account "$a"} "$@" ;;
    regular) exec "$BIN/claude-regular" ${a:+--account "$a"} "$@" ;;
esac