#!/bin/sh # cc-board installer # curl -fsSL https://ccboard.dev/install.sh | sh # # What it does, in order: # 1. checks this Mac: macOS on Apple Silicon, Xcode Command Line Tools, git, Node 22+ where the app looks for it, tmux # 2. fetches one pinned commit of https://github.com/Lfrangt/cc-board into ~/.cc-board/src # 3. runs that commit's ./build.sh, which compiles a small Swift shell and installs ~/Applications/总控台.app # (the app's code also goes to ~/Library/Application Support/cc-board/app) # 4. opens the app # It needs no sudo and writes nowhere else. Uninstall: delete those three folders. # # Override with environment variables: CCBOARD_SRC (where the source goes), CCBOARD_REF (another commit or tag), # CCBOARD_NO_OPEN=1 (don't open the app at the end). set -eu REPO="https://github.com/Lfrangt/cc-board.git" REF="${CCBOARD_REF:-c9fe28974957f8eb0daba1ed779b7aeda4f817da}" SRC="${CCBOARD_SRC:-$HOME/.cc-board/src}" APP="$HOME/Applications/总控台.app" bold() { printf '\033[1m%s\033[0m\n' "$*"; } fail() { printf '\n\033[31mcc-board: %s\033[0m\n' "$*" >&2; exit 1; } warn() { printf '\033[33m! %s\033[0m\n' "$*"; } # Everything runs inside main(), called on the last line: when this script arrives through a pipe, sh has read # all of it before anything runs, and no command below can swallow the rest of the script from stdin. main() { bold "Installing cc-board" # ── 1. this Mac ── [ "$(uname -s)" = "Darwin" ] || fail "cc-board is a macOS app." [ "$(uname -m)" = "arm64" ] || fail "cc-board needs an Apple Silicon Mac for now." major=$(sw_vers -productVersion | cut -d. -f1) [ "$major" -ge 13 ] || fail "cc-board needs macOS 13 or later (this Mac has $(sw_vers -productVersion))." xcode-select -p >/dev/null 2>&1 && [ -x /usr/bin/swiftc ] || fail "Install the Xcode Command Line Tools first: xcode-select --install" command -v git >/dev/null 2>&1 || fail "git is missing. It comes with the Xcode Command Line Tools: xcode-select --install" # The app starts its server with the first of these three that exists, so Node has to be in one of them. NODE="" for n in "$HOME/.local/node/bin/node" /opt/homebrew/bin/node /usr/local/bin/node; do if [ -x "$n" ]; then NODE="$n"; break; fi done [ -n "$NODE" ] || fail "cc-board needs Node 22 or later in /opt/homebrew/bin, /usr/local/bin or ~/.local/node/bin. With Homebrew: brew install node" nmajor=$("$NODE" -p 'process.versions.node.split(".")[0]' 2>/dev/null || echo 0) [ "$nmajor" -ge 22 ] || fail "$NODE is Node $("$NODE" --version 2>/dev/null). cc-board needs Node 22 or later: brew upgrade node" echo " Node $("$NODE" --version) at $NODE" if ! command -v tmux >/dev/null 2>&1 && [ ! -x /opt/homebrew/bin/tmux ] && [ ! -x /usr/local/bin/tmux ]; then warn "tmux is not installed. cc-board will show your sessions, but starting new ones from it needs tmux: brew install tmux" fi command -v claude >/dev/null 2>&1 || [ -d "$HOME/.claude" ] || warn "Claude Code doesn't seem to be installed yet. cc-board shows Claude Code sessions: https://claude.com/claude-code" # ── 2. source ── mkdir -p "$SRC" if [ ! -d "$SRC/.git" ]; then git -C "$SRC" init -q git -C "$SRC" remote add origin "$REPO" fi echo " Fetching cc-board ($REF)" git -C "$SRC" fetch -q --depth 1 origin "$REF" || fail "Could not download cc-board from GitHub." git -C "$SRC" checkout -q --force FETCH_HEAD # ── 3. build and install ── echo " Building (a few seconds to a minute)" ( cd "$SRC" && NODE="$NODE" /bin/zsh ./build.sh ) || fail "The build failed. The messages above say why." [ -d "$APP" ] || fail "The build finished but $APP is not there." # ── 4. open ── bold "cc-board is installed: $APP" echo " The first time it starts, macOS asks whether it may control Ghostty. That's only for replying into" echo " sessions you started in Ghostty tabs; say no if you don't use Ghostty." if [ "${CCBOARD_NO_OPEN:-}" != "1" ]; then open "$APP"; fi } main "$@"