#!/bin/sh # lucerna-crew 0.5.1 — the crew board in your terminal and in Claude Code. # # Read this before you run it. In order, it: # 1. checks for Node.js 20.3 or newer, and curl; # 2. downloads the 27 files listed near the bottom from # https://lucernanoetica.com/crew/files/ into a temporary folder, and checks each one's # sha256 against that list. A mismatch means a download arrived broken, # and nothing is installed; # 3. moves them to ~/.local/share/lucerna-crew/0.5.1 (XDG_DATA_HOME is honoured); # 4. if Claude Code is installed, adds that folder as the "lucerna" plugin # marketplace and installs the lucerna-crew plugin; # 5. runs "lucerna-crew install": a two-line lucerna-crew in ~/.local/bin, the # crew line after your own Claude Code status line (your settings are backed # up first), and the plugin switched on for your user. It joins no board and # says nothing in any session until you run /lucerna-crew:join in it. # It never asks for a password, never reads a key, and changes nothing else. # Signing in is a separate step you run yourself: lucerna-crew login. set -eu VERSION='0.5.1' BASE="${LUCERNA_CREW_BASE:-https://lucernanoetica.com}/crew/files" say() { printf '%s\n' "$*"; } die() { printf 'lucerna-crew: %s\n' "$*" >&2; exit 1; } command -v curl >/dev/null 2>&1 || die "needs curl" command -v node >/dev/null 2>&1 || die "needs Node.js 20.3 or newer (https://nodejs.org) — Claude Code's own installer does not bring it" node -e 'const [a,b]=process.versions.node.split(".").map(Number);process.exit(a>20||(a===20&&b>=3)?0:1)' \ || die "Node.js $(node -v) is too old — lucerna-crew needs 20.3 or newer" if command -v sha256sum >/dev/null 2>&1; then sha() { sha256sum "$1" | cut -d' ' -f1; } elif command -v shasum >/dev/null 2>&1; then sha() { shasum -a 256 "$1" | cut -d' ' -f1; } else die "needs sha256sum or shasum to check the downloads"; fi DATA="${XDG_DATA_HOME:-$HOME/.local/share}/lucerna-crew" DIR="$DATA/$VERSION" mkdir -p "$DATA" TMP="$(mktemp -d "$DATA/.incoming.XXXXXX")" trap 'rm -rf "$TMP"' EXIT INT TERM fetch() { mkdir -p "$TMP/$(dirname "$1")" curl -fsSL --proto '=https,http' "$BASE/$1" -o "$TMP/$1" || die "could not download $1 — nothing was installed" [ "$(sha "$TMP/$1")" = "$2" ] || die "$1 arrived broken (its sha256 does not match) — nothing was installed" } say "lucerna-crew $VERSION: downloading 27 files…" fetch '.claude-plugin/marketplace.json' '7626f5758d1d229ca0b48c1787422e14bb634ff8a2a30cbf7a12e1dc2046cd6d' fetch 'plugins/lucerna-crew/.claude-plugin/plugin.json' '0f4723dadb47582f3b0d9007768c8308ef62acd0fd393689f876061cd857473a' fetch 'plugins/lucerna-crew/bin/accounts.js' '15dc928a4e416193d00b591ed879d9159417aa2e3b246047719db9e425563b9b' fetch 'plugins/lucerna-crew/bin/accountsfile.js' '92eb59d17a7af68e3251793a6df11978116c3aaf5f852d6319f2b49a4332c7a1' fetch 'plugins/lucerna-crew/bin/api.js' '074b4354edd71b901e885af0b871b8eaa3a57248e4a0e75e7e5775189745ec41' fetch 'plugins/lucerna-crew/bin/brake.js' '6c29b6bda4b5bce3a983fb32831aa1217cb51f47a9151bb4dcc065671f29b33b' fetch 'plugins/lucerna-crew/bin/cli.js' '0b72f65936c79cea4c0ba79ab0d0fcaefb953cb13d9e514d725696f22896cae2' fetch 'plugins/lucerna-crew/bin/feed.js' 'bc7f77c5da6a69d8e13156407b700a79fb3d0d38fff96b03d5c4097ba7477f11' fetch 'plugins/lucerna-crew/bin/git.js' '1119163eaea5baa0abe913f11bdb71a0877edd1c28d6c8ba02e8a1b070e3c753' fetch 'plugins/lucerna-crew/bin/herdr.js' '6683b6730e83feb7f01f88045b35c0d99e75e9e02c5ad2752511e230f86fcd99' fetch 'plugins/lucerna-crew/bin/here.js' '5b7bf02e4aa896d6ec1eac54e55b120642dd7297cb26e0039a220512ea1f4de3' fetch 'plugins/lucerna-crew/bin/identity.js' 'f0d1389c78355efd728949844cb025b5f45589f1aa2afa5923653beb816a8328' fetch 'plugins/lucerna-crew/bin/lobby.js' '0f03e11ef99b62d4a9091725990d901e70a75302e65231f666f14373050f5af2' fetch 'plugins/lucerna-crew/bin/login.js' 'c36c0772f4696f20273961038bae82816a6775a7e03de78c70f0035b44b8ca4c' fetch 'plugins/lucerna-crew/bin/notify.js' 'f209a1fff40cea2a5a913dc15503e8f4eb459e994babc7881d5c9d38639cc8ae' fetch 'plugins/lucerna-crew/bin/package.json' '89c47db7851f89caec72da4f215ebc21cf1e294aeef3b45d2583562d846ab035' fetch 'plugins/lucerna-crew/bin/pane.js' 'a257b61b1dcf5cacfa925cadb9be26967824b5012bd7f10e95337e7642816931' fetch 'plugins/lucerna-crew/bin/peek.js' 'c84f08622d29ff35d450c3f1a29e7f0f1f2c03ad00e4c110b3c990ec23c21ff2' fetch 'plugins/lucerna-crew/bin/run.js' '59eacd2f1972f23d55df9c37c62fcb301b1a05cc9aeafc54b1f64ae2a02a9f86' fetch 'plugins/lucerna-crew/bin/state.js' '9bed9e90d25e188939c528306b062ef493dfdaa20a18636a27ecf81f9ba452e5' fetch 'plugins/lucerna-crew/hooks/hooks.json' '9960ccec86c26ea1f5fca6f1b0bf90d40a7d1c00190263c5ba992adc335d0c30' fetch 'plugins/lucerna-crew/monitors/monitors.json' '80754ce99d4a98d959993b31e1778c5bbb12bb37af0755baef9993e2b8e6edab' fetch 'plugins/lucerna-crew/skills/account/SKILL.md' 'cd22098daffc65145919b6be1c039cff54c29b391ef8c898f6e460a9f68cdeff' fetch 'plugins/lucerna-crew/skills/crew/SKILL.md' 'b2447d7209e0ce25d927aac73345ac61daa666884a567ac711d91390e532aa91' fetch 'plugins/lucerna-crew/skills/join/SKILL.md' '4c0bb256c2a690831ae23ca48be7fe0351b9f2674cd1cc52cc828bac740bc1c8' fetch 'plugins/lucerna-crew/skills/leave/SKILL.md' '75f89fb476c646158ddd4993804e41289959bb3daca5cb55e9849aa1b55917be' fetch 'plugins/lucerna-crew/skills/switch/SKILL.md' 'e124683b9b4c4e5489cc0c7ce24edc541133c0c999f04d3c7b12613a6d172f35' rm -rf "$DIR" mv "$TMP" "$DIR" trap - EXIT INT TERM ln -sfn "$DIR" "$DATA/current" CLI="$DATA/current/plugins/lucerna-crew/bin/cli.js" if command -v claude >/dev/null 2>&1; then # an upgrade: add and install both answer 0 when the marketplace or plugin is # already there (read live: "already on disk", "already installed"), so an # update behind `||` never ran. Each step runs on its own; update refreshes # the version Claude Code records, whatever scope it was installed at. claude plugin marketplace add "$DATA/current" >/dev/null 2>&1 || true claude plugin marketplace update lucerna >/dev/null 2>&1 || true claude plugin install lucerna-crew@lucerna >/dev/null 2>&1 || true claude plugin update lucerna-crew@lucerna >/dev/null 2>&1 || true say "The lucerna-crew plugin is in Claude Code." else say "Claude Code is not on this PATH, so the plugin was not added (the pane and the CLI work without it)." fi node "$CLI" install case ":$PATH:" in *":$HOME/.local/bin:"*) ;; *) say "Add ~/.local/bin to your PATH to call lucerna-crew by name.";; esac say "" say "Next:" say " lucerna-crew login # a browser sign-in, once — no account needed beforehand" say " cd your/repo && lucerna-crew link # optional: lets sessions here join under the repo's name" say " /lucerna-crew:join # in a Claude Code session: pick the squad and your name" say " lucerna-crew pane # the live board (inside herdr: lucerna-crew pane --split)"