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