Skip to content

Installer source / review before running

Read every line.
Before it runs.

The exact shell script served at /install.sh. Review the source, inspect the checksums, and know what you run.
01 / Open to inspection

A clear installation path.

Displayed source
Exact script bundled into this build and served at /install.sh.
Published release
WaitDojo v0.2.6 for macOS, Linux, and WSL2.
Archive integrity
SHA-256 detects a corrupt or mismatched downloaded archive.
Trust boundary
Detached Ed25519 signatures and signed provenance cover every archive. A fresh install still needs an out-of-band trusted public key for independent publisher authentication.
02 / Before your first rep

Public installer, invited access.

The installer source and release downloads are public. Running the installer does not grant beta access or create an entitlement.

Invited access starts when a private invitation is claimed with GitHub and the site records that account entitlement. Beta-gated builds check the same GitHub account at launch.

The published v0.2.6 beta-gated builds require a claimed invitation and check access at launch. An uninvited account cannot start installed drills. Progress backup is optional.

Join the invited-beta waitlist
03 / The complete script

Published installer source.

The source below is inert text. Nothing runs here. Use the explicitly labeled raw file only if you want to download it.

Download raw .sh
waitdojo-install.shbash
#!/bin/sh
set -eu
# Installer output and checksum/archive tools must behave the same on GNU and
# BSD userlands, even when the calling terminal exports a locale unavailable on
# the target host (for example C.UTF-8 on macOS).
LC_ALL=C
LANG=C
export LC_ALL LANG
PRODUCT="waitdojo"
BASE_URL="${WAITDOJO_BASE_URL:-https://waitdojo.dev/releases}"
VERSION="${WAITDOJO_VERSION:-latest}"
DEFAULT_INSTALL_DIR="$HOME/.local/bin"
INSTALL_DIR="${WAITDOJO_INSTALL_DIR:-$DEFAULT_INSTALL_DIR}"
DRY_RUN="${WAITDOJO_DRY_RUN:-0}"
NO_MODIFY_PATH="${WAITDOJO_NO_MODIFY_PATH:-0}"
AGENT="${WAITDOJO_AGENT:-}"
PATH_PROFILE_CONFIGURED=0
SETUP_SUPPORTED=0
usage() {
cat <<'EOF'
Usage: install.sh [--dry-run] [--no-modify-path] [--agent codex|claude|none]
Environment:
WAITDOJO_INSTALL_DIR install directory, defaults to ~/.local/bin
WAITDOJO_VERSION release version, defaults to latest
WAITDOJO_BASE_URL release base URL, defaults to the live WaitDojo site
WAITDOJO_DRY_RUN set to 1 to print actions without changing files
WAITDOJO_NO_MODIFY_PATH
set to 1 to leave shell profiles unchanged
WAITDOJO_AGENT optionally configure codex or claude hooks after install
EOF
}
while [ "$#" -gt 0 ]; do
case "$1" in
--dry-run)
DRY_RUN=1
;;
--no-modify-path)
NO_MODIFY_PATH=1
;;
--agent)
shift
if [ "$#" -eq 0 ]; then
echo "--agent requires codex, claude, or none" >&2
exit 2
fi
AGENT="$1"
;;
-h|--help)
usage
exit 0
;;
*)
echo "unknown argument: $1" >&2
usage >&2
exit 2
;;
esac
shift
done
case "$AGENT" in
""|none) AGENT="" ;;
codex|claude) ;;
*)
echo "WAITDOJO_AGENT/--agent must be codex, claude, or none" >&2
exit 2
;;
esac
case "$NO_MODIFY_PATH" in
0|1) ;;
*)
echo "WAITDOJO_NO_MODIFY_PATH must be 0 or 1" >&2
exit 2
;;
esac
case "$INSTALL_DIR" in
/*) ;;
*) INSTALL_DIR="$PWD/$INSTALL_DIR" ;;
esac
detect_os() {
os="${WAITDOJO_TEST_UNAME_S:-$(uname -s 2>/dev/null || true)}"
case "$os" in
Darwin) echo "macos" ;;
Linux) echo "linux" ;;
MINGW*|MSYS*|CYGWIN*)
echo "native Windows is not supported; install and run WaitDojo inside WSL2" >&2
exit 1
;;
*)
echo "unsupported OS: $os" >&2
exit 1
;;
esac
}
detect_arch() {
arch="${WAITDOJO_TEST_UNAME_M:-$(uname -m 2>/dev/null || true)}"
case "$arch" in
x86_64|amd64) echo "x64" ;;
arm64|aarch64) echo "arm64" ;;
*)
echo "unsupported architecture: $arch" >&2
exit 1
;;
esac
}
need_cmd() {
if ! command -v "$1" >/dev/null 2>&1; then
echo "missing required command: $1" >&2
exit 1
fi
}
is_wsl2() {
if [ "${WAITDOJO_TEST_WSL:-}" = "1" ]; then
return 0
fi
if [ -n "${WSL_INTEROP:-}" ] || [ -n "${WSL_DISTRO_NAME:-}" ]; then
return 0
fi
if [ -r /proc/sys/kernel/osrelease ] && grep -qi "microsoft\\|wsl" /proc/sys/kernel/osrelease; then
return 0
fi
return 1
}
append_posix_path_block() {
profile="$1"
marker="# >>> waitdojo PATH >>>"
if grep -F "$marker" "$profile" >/dev/null 2>&1; then
echo "PATH already configured in $profile"
PATH_PROFILE_CONFIGURED=1
return 0
fi
if {
if [ -s "$profile" ]; then
printf '\n'
fi
printf '%s\n' \
"$marker" \
'# Added by the WaitDojo installer. Remove this block to opt out.' \
'case "$PATH" in' \
' "$HOME/.local/bin"|"$HOME/.local/bin":*) ;;' \
' *) export PATH="$HOME/.local/bin:$PATH" ;;' \
'esac' \
'# <<< waitdojo PATH <<<'
} >>"$profile"
then
echo "configured PATH in $profile"
PATH_PROFILE_CONFIGURED=1
else
echo "warning: could not update PATH in $profile" >&2
fi
}
append_fish_path_block() {
profile="$1"
marker="# >>> waitdojo PATH >>>"
profile_dir="${profile%/*}"
if ! mkdir -p "$profile_dir"; then
echo "warning: could not create $profile_dir for PATH setup" >&2
return 0
fi
if grep -F "$marker" "$profile" >/dev/null 2>&1; then
echo "PATH already configured in $profile"
PATH_PROFILE_CONFIGURED=1
return 0
fi
if {
if [ -s "$profile" ]; then
printf '\n'
fi
printf '%s\n' \
"$marker" \
'# Added by the WaitDojo installer. Remove this block to opt out.' \
'if not contains -- "$HOME/.local/bin" "$PATH[1]"' \
' set -gx PATH "$HOME/.local/bin" $PATH' \
'end' \
'# <<< waitdojo PATH <<<'
} >>"$profile"
then
echo "configured PATH in $profile"
PATH_PROFILE_CONFIGURED=1
else
echo "warning: could not update PATH in $profile" >&2
fi
}
configure_shell_path() {
if [ "$NO_MODIFY_PATH" = "1" ]; then
echo "PATH setup skipped (--no-modify-path)"
return 0
fi
if [ "$INSTALL_DIR" != "$DEFAULT_INSTALL_DIR" ]; then
echo "PATH setup skipped for custom install directory $INSTALL_DIR"
return 0
fi
shell_path="${SHELL:-}"
shell_name="${shell_path##*/}"
case "$shell_name" in
bash)
append_posix_path_block "$HOME/.bashrc"
if [ -f "$HOME/.bash_profile" ]; then
append_posix_path_block "$HOME/.bash_profile"
elif [ -f "$HOME/.bash_login" ]; then
append_posix_path_block "$HOME/.bash_login"
else
append_posix_path_block "$HOME/.profile"
fi
;;
zsh)
append_posix_path_block "${ZDOTDIR:-$HOME}/.zshrc"
;;
fish)
append_fish_path_block "${XDG_CONFIG_HOME:-$HOME/.config}/fish/conf.d/waitdojo.fish"
;;
""|sh|dash|ash|ksh)
append_posix_path_block "$HOME/.profile"
;;
*)
echo "warning: shell $shell_name is not supported for automatic PATH setup" >&2
;;
esac
}
current_waitdojo_path() {
command -v "$PRODUCT" 2>/dev/null || true
}
warn_if_shadowed() {
installed="$INSTALL_DIR/$PRODUCT"
resolved="$(current_waitdojo_path)"
if [ -z "$resolved" ] || [ "$resolved" = "$installed" ]; then
return 0
fi
echo "warning: another waitdojo currently wins on PATH: $resolved" >&2
if [ -x "$resolved" ]; then
shadow_version="$("$resolved" --version 2>/dev/null || true)"
if [ -n "$shadow_version" ]; then
echo " existing: $shadow_version" >&2
fi
fi
echo " installed: $installed" >&2
echo " prepend the installed directory now or open a new terminal after PATH setup" >&2
}
print_next_steps() {
if [ "$RUNTIME" = "wsl2" ]; then
echo " workspace: keep source checkouts under WSL, for example ~/waitdojo, not /mnt/c/..."
fi
resolved="$(current_waitdojo_path)"
if [ "$resolved" != "$INSTALL_DIR/$PRODUCT" ]; then
if [ "$INSTALL_DIR" = "$DEFAULT_INSTALL_DIR" ]; then
shell_install_dir='$HOME/.local/bin'
else
shell_install_dir="$INSTALL_DIR"
fi
echo " current shell: export PATH=\"${shell_install_dir}:\$PATH\""
echo " hash -r 2>/dev/null || true"
if [ "$PATH_PROFILE_CONFIGURED" = "1" ]; then
echo " future shells: PATH is configured; open a new terminal"
elif [ "$NO_MODIFY_PATH" = "1" ]; then
echo " future shells: add the install directory to PATH yourself"
elif [ "$DRY_RUN" = "1" ] && [ "$INSTALL_DIR" = "$DEFAULT_INSTALL_DIR" ]; then
echo " future shells: the installer will configure PATH automatically"
else
echo " future shells: add export PATH=\"${shell_install_dir}:\$PATH\" to your shell profile"
fi
fi
if [ -n "$AGENT" ]; then
if [ "$DRY_RUN" = "1" ]; then
echo " setup: would configure $AGENT using the downloaded version's setup or init command"
elif [ "$SETUP_SUPPORTED" = "1" ]; then
echo " setup: $PRODUCT setup --agent $AGENT completed"
else
echo " setup: passive $AGENT hooks configured via $PRODUCT init $AGENT"
fi
echo " review: open $AGENT and run /hooks"
echo " launch: $INSTALL_DIR/$PRODUCT"
else
echo " start: $PRODUCT"
echo " detects your configured agent and opens the project sidecar"
if [ "$SETUP_SUPPORTED" = "1" ]; then
echo " setup: $PRODUCT setup --agent codex # or: --agent claude"
else
echo " setup: $PRODUCT init codex # or: $PRODUCT init claude"
fi
echo " launch: $PRODUCT codex # or: $PRODUCT claude"
fi
echo " local: $PRODUCT pane"
echo " toggle: Ctrl+G or click [↑ open WaitDojo] / [↓ collapse]"
echo " advanced: tmux prefix, then C-g, then an action key"
echo " check: $PRODUCT doctor --product --agent ${AGENT:-codex}"
echo " tools:"
for cmd in cargo tmux codex claude; do
if command -v "$cmd" >/dev/null 2>&1; then
echo " $cmd: ok"
else
echo " $cmd: missing"
fi
done
}
OS="$(detect_os)"
ARCH="$(detect_arch)"
RUNTIME="$OS"
if [ "$OS" = "linux" ] && is_wsl2; then
RUNTIME="wsl2"
fi
ASSET="${PRODUCT}-${OS}-${ARCH}.tar.gz"
URL="${BASE_URL}/${VERSION}/${ASSET}"
case "$VERSION" in
""|*[!0-9A-Za-z.+-]*)
echo "invalid requested WaitDojo version: $VERSION" >&2
exit 2
;;
esac
echo "WaitDojo installer"
echo " runtime: ${RUNTIME}"
echo " platform: ${OS}-${ARCH}"
echo " asset: ${ASSET}"
echo " install: ${INSTALL_DIR}/${PRODUCT}"
echo " source: ${URL}"
if [ "$DRY_RUN" = "1" ]; then
echo "dry run: no files changed"
echo "next steps:"
print_next_steps
exit 0
fi
need_cmd curl
need_cmd tar
need_cmd install
need_cmd mktemp
need_cmd awk
need_cmd grep
if command -v sha256sum >/dev/null 2>&1; then
CHECKSUM_COMMAND="sha256sum"
elif command -v shasum >/dev/null 2>&1; then
CHECKSUM_COMMAND="shasum"
else
echo "missing required command: sha256sum or shasum" >&2
exit 1
fi
mkdir -p "$INSTALL_DIR"
tmp_dir="$(mktemp -d)"
trap 'rm -rf "$tmp_dir"' EXIT INT TERM
read_latest_version() {
version_url="${BASE_URL}/latest/VERSION"
curl -fsSL "$version_url" -o "$tmp_dir/VERSION"
published_version="$(
awk '
NF {
lines += 1
value = $0
sub(/\r$/, "", value)
}
END {
if (lines != 1 || value == "" || value ~ /[[:space:]]/) exit 1
print value
}
' "$tmp_dir/VERSION"
)" || {
echo "invalid published VERSION from $version_url" >&2
exit 1
}
case "$published_version" in
""|*[!0-9A-Za-z.+-]*)
echo "invalid published WaitDojo version: $published_version" >&2
exit 1
;;
esac
}
if [ "$VERSION" = "latest" ]; then
read_latest_version
expected_version="$published_version"
curl -fsSL "$URL" -o "$tmp_dir/$ASSET"
else
expected_version="$VERSION"
# Prefer the immutable version directory. Older sites published only latest;
# use that location only after proving it names exactly the requested version.
# The archive checksum and executable version are verified again below, so a
# concurrent latest promotion cannot silently install a different release.
if ! curl -fsSL "$URL" -o "$tmp_dir/$ASSET"; then
read_latest_version
if [ "$published_version" != "$expected_version" ]; then
echo "requested WaitDojo $expected_version is unavailable; latest is $published_version; no binary was installed" >&2
exit 1
fi
URL="${BASE_URL}/latest/${ASSET}"
echo " source: $URL (verified latest is exactly $expected_version)"
curl -fsSL "$URL" -o "$tmp_dir/$ASSET"
fi
fi
case "$expected_version" in
""|*[!0-9A-Za-z.+-]*)
echo "invalid requested or published WaitDojo version: $expected_version" >&2
exit 1
;;
esac
checksum_url="${URL}.sha256"
curl -fsSL "$checksum_url" -o "$tmp_dir/$ASSET.sha256"
expected_checksum="$(
awk -v asset="$ASSET" '
NF {
lines += 1
name = $2
sub(/^\*/, "", name)
sub(/\r$/, "", name)
hash = tolower($1)
sub(/\r$/, "", hash)
if (lines != 1 || NF != 2 || name != asset || length(hash) != 64 || hash !~ /^[0-9a-f]+$/) {
invalid = 1
}
expected = hash
}
END {
if (lines != 1 || invalid) exit 1
print expected
}
' "$tmp_dir/$ASSET.sha256"
)" || {
echo "invalid checksum file from $checksum_url" >&2
exit 1
}
if [ "$CHECKSUM_COMMAND" = "sha256sum" ]; then
actual_checksum="$(sha256sum "$tmp_dir/$ASSET")"
else
actual_checksum="$(shasum -a 256 "$tmp_dir/$ASSET")"
fi
actual_checksum="${actual_checksum%% *}"
if [ "$actual_checksum" != "$expected_checksum" ]; then
echo "checksum verification failed for $ASSET" >&2
exit 1
fi
echo "$ASSET: checksum verified"
tar -xzf "$tmp_dir/$ASSET" -C "$tmp_dir"
if [ ! -f "$tmp_dir/$PRODUCT" ] || [ ! -x "$tmp_dir/$PRODUCT" ]; then
echo "archive did not contain $PRODUCT binary" >&2
exit 1
fi
version_output="$("$tmp_dir/$PRODUCT" --version)"
printf '%s\n' "$version_output" >"$tmp_dir/version.txt"
if [ "$version_output" != "$PRODUCT $expected_version" ]; then
echo "downloaded WaitDojo version mismatch: expected $PRODUCT $expected_version, got $version_output" >&2
exit 1
fi
"$tmp_dir/$PRODUCT" --help >"$tmp_dir/help.txt"
if grep -Eq '^[[:space:]]*setup([[:space:]]|$)' "$tmp_dir/help.txt"; then
SETUP_SUPPORTED=1
fi
if ! grep -Eq '(^|[[:space:]])codex([[:space:]]|$)' "$tmp_dir/help.txt" || \
! grep -Eq '(^|[[:space:]])claude([[:space:]]|$)' "$tmp_dir/help.txt"; then
echo "downloaded WaitDojo build is missing Codex or Claude Code support" >&2
exit 1
fi
validation_home="$tmp_dir/validation-home"
mkdir -p "$validation_home"
HOME="$validation_home" \
CODEX_HOME="$validation_home/.codex" \
WAITDOJO_DATA_DIR="$validation_home/.codex/waitdojo" \
"$tmp_dir/$PRODUCT" init codex --dry-run --allow-dev-path >/dev/null
HOME="$validation_home" \
CLAUDE_CONFIG_DIR="$validation_home/.claude" \
WAITDOJO_DATA_DIR="$validation_home/.codex/waitdojo" \
"$tmp_dir/$PRODUCT" init claude --dry-run --allow-dev-path >/dev/null
echo "$PRODUCT: Codex and Claude Code support verified"
install -m 0755 "$tmp_dir/$PRODUCT" "$INSTALL_DIR/$PRODUCT"
"$INSTALL_DIR/$PRODUCT" --version >/dev/null
# Bake the install-time campaign bucket (WAITDOJO_CAMPAIGN_BUCKET, if set)
# into the installation identity now; without this, installs that skip
# --agent lose attribution because the env var is gone by first launch.
"$INSTALL_DIR/$PRODUCT" install-stamp >/dev/null 2>&1 || true
echo "installed $PRODUCT to $INSTALL_DIR/$PRODUCT"
configure_shell_path
warn_if_shadowed
if [ -n "$AGENT" ]; then
if [ "$SETUP_SUPPORTED" = "1" ]; then
echo "configuring $AGENT via waitdojo setup"
if "$INSTALL_DIR/$PRODUCT" setup --agent "$AGENT"; then
:
else
setup_status=$?
echo "WaitDojo was installed, but $AGENT setup did not complete (exit $setup_status)." >&2
echo "Resolve the reported checks, then rerun: \"$INSTALL_DIR/$PRODUCT\" setup --agent $AGENT" >&2
echo "Launch WaitDojo to finish sign-in and access checks: \"$INSTALL_DIR/$PRODUCT\" pane" >&2
exit "$setup_status"
fi
else
echo "configuring passive $AGENT hooks via waitdojo init $AGENT"
if "$INSTALL_DIR/$PRODUCT" init "$AGENT"; then
:
else
setup_status=$?
echo "WaitDojo was installed, but $AGENT hook setup failed (exit $setup_status)." >&2
echo "Resolve the error, then rerun: \"$INSTALL_DIR/$PRODUCT\" init $AGENT" >&2
echo "Launch WaitDojo to finish sign-in and access checks: \"$INSTALL_DIR/$PRODUCT\" pane" >&2
exit "$setup_status"
fi
fi
fi
echo "next steps:"
print_next_steps
The public proof manifest documents the historical v0.2.4 deterministic UI capture; it is not itself a release signature or publisher-authenticity proof.