#!/bin/sh # Puts hamloy, Hamloy's program for this computer, in ~/.local/bin, and that # folder on the PATH of new terminals: # curl -fsSL https://hamloy.com/install | sh # Then, in a project's folder, it goes straight on to hamloy connect: one # sign-in in the browser, and Claude Code and Codex set up for the folder. # /install?project=NAME connects to that project; words after sh -s -- are # another hamloy command to run instead. # The server hands this out with its own address and the command filled in. set -eu base="https://hamloy.com" dir="$HOME/.local/bin" [ $# -gt 0 ] || set -- connect fail() { printf '✗ %s\n' "$1" >&2 [ -z "${2:-}" ] || printf ' fix: %s\n' "$2" >&2 exit 1 } case "$(uname -s)" in Darwin) os=darwin ;; Linux) os=linux ;; MINGW* | MSYS* | CYGWIN*) fail "this is Windows" "in PowerShell: irm $base/install.ps1 | iex" ;; *) fail "hamloy isn't made for $(uname -s) yet" ;; esac case "$(uname -m)" in x86_64 | amd64) arch=amd64 ;; arm64 | aarch64) arch=arm64 ;; *) fail "hamloy isn't made for $(uname -m) processors yet" ;; esac # A shell under Rosetta still gets the program made for the Apple chip. if [ "$os" = darwin ] && [ "$arch" = amd64 ] && [ "$(sysctl -n sysctl.proc_translated 2>/dev/null || true)" = 1 ]; then arch=arm64 fi fetch() { if command -v curl >/dev/null 2>&1; then curl -fsSL "$1" -o "$2" elif command -v wget >/dev/null 2>&1; then wget -q "$1" -O "$2" else fail "neither curl nor wget is on this computer" "install curl, then run this again" fi } sum() { if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1"; else shasum -a 256 "$1"; fi | cut -d ' ' -f 1 } # The system's temporary folder can be closed to a sandboxed assistant; # the home folder, which this installs into anyway, is the other place. tmp=$(mktemp -d 2>/dev/null || mktemp -d "$HOME/.hamloy-install.XXXXXX") || fail "couldn't make a folder to download into" "check that $HOME can be written to, then run this again" trap 'rm -rf "$tmp"' EXIT name="hamloy-$os-$arch" echo " downloading hamloy for $os/$arch from $base" fetch "$base/downloads/$name" "$tmp/hamloy" || fail "couldn't download $base/downloads/$name" "check the connection, then run this again" fetch "$base/downloads/SHA256SUMS" "$tmp/SHA256SUMS" || fail "couldn't download $base/downloads/SHA256SUMS" "check the connection, then run this again" want=$(awk -v name="$name" '$2 == name || $2 == "*" name { print $1 }' "$tmp/SHA256SUMS") if [ -z "$want" ] || [ "$want" != "$(sum "$tmp/hamloy")" ]; then fail "the download doesn't match its checksum, so nothing was installed" "run this again" fi chmod 755 "$tmp/hamloy" mkdir -p "$dir" mv -f "$tmp/hamloy" "$dir/hamloy" echo "✓ installed $("$dir/hamloy" version) in ~/.local/bin/hamloy" # New terminals find hamloy by its name: ~/.local/bin goes on the PATH in # the shell's profile, once. Claude Code and Codex don't need it; they run # the program by its full path. # This terminal keeps the PATH it had, so it is told the full path. program=hamloy case ":$PATH:" in *":$dir:"*) ;; *) program="~/.local/bin/hamloy" line='export PATH="$HOME/.local/bin:$PATH"' case "${SHELL##*/}" in zsh) profile="${ZDOTDIR:-$HOME}/.zshrc" ;; bash) if [ "$os" = darwin ]; then profile="$HOME/.bash_profile"; else profile="$HOME/.bashrc"; fi ;; fish) profile="$HOME/.config/fish/conf.d/hamloy.fish" line='fish_add_path -g $HOME/.local/bin' ;; *) profile="$HOME/.profile" ;; esac shown=$(printf '%s' "$profile" | sed "s|^$HOME|~|") if grep -qs '\.local/bin' "$profile"; then echo " ~/.local/bin is on the PATH in $shown; terminals opened from now on find hamloy" elif mkdir -p "$(dirname "$profile")" && printf '\n# hamloy\n%s\n' "$line" >> "$profile"; then echo "✓ put ~/.local/bin on the PATH of new terminals, in $shown (the two lines marked # hamloy; delete them to undo)" else echo " couldn't add ~/.local/bin to $shown; add this line to your shell's profile: $line" fi ;; esac # On to connect, unless this is the home folder rather than a project's. # Without a terminal (an assistant running this) connect still signs in in # the browser, and says what it couldn't ask. if [ "$(pwd -P)" != "$(cd "$HOME" && pwd -P)" ]; then echo if (: /dev/null; then exec "$dir/hamloy" "$@"