#!/bin/sh
# Installs the launchpad CLI (~/.launchpad/bin) and the agent skill (~/.launchpad/skill, every file of
# $URL/skill/manifest.json). It writes nothing in the current directory, so it is safe to run from anywhere,
# your clone included. Run it again, or launchpad update, to bring both up to date.
set -e
URL="${LAUNCHPAD_URL:-https://hotpod.io}"
LP_HOME="${LAUNCHPAD_HOME:-$HOME/.launchpad}"
BIN="$LP_HOME/bin"
SKILL_DIR="${LAUNCHPAD_SKILL_DIR:-$LP_HOME/skill}"
mkdir -p "$BIN" "$SKILL_DIR"
# The CLI as launchpad.mjs, which every Node.js from 18 on runs as a module (an extensionless file is CommonJS before
# Node 20.19 and 22.7, which refuse its imports), and the launchpad command that runs it.
# Keep a working installation intact if the transfer fails. Download beside it so the final rename is atomic.
CLI_TMP=$(mktemp "$BIN/.launchpad.XXXXXX")
CLI_MANIFEST=""
trap 'rm -f "$CLI_TMP"; if [ -n "$CLI_MANIFEST" ]; then rm -f "$CLI_MANIFEST"; fi' 0
trap 'exit 1' HUP INT TERM
curl -fsSL "$URL/cli/launchpad.mjs" -o "$CLI_TMP"
CLI_MANIFEST=$(mktemp "$BIN/.launchpad-manifest.XXXXXX")
curl -fsSL "$URL/skill/manifest.json" -o "$CLI_MANIFEST"
# Verify the downloaded bytes before executing them or replacing the previous CLI.
node -e 'const fs=require("fs"),crypto=require("crypto"),[f,m]=process.argv.slice(1);try{const b=fs.readFileSync(f),c=JSON.parse(fs.readFileSync(m,"utf8")).cli;if(!b.length||c?.path!=="/cli/launchpad.mjs"||!/^([a-f0-9]{64})$/.test(c.sha256)||crypto.createHash("sha256").update(b).digest("hex")!==c.sha256)throw Error("download does not match the manifest")}catch(e){console.error("CLI verification failed: "+e.message+"; existing installation preserved. Run install.sh again.");process.exit(1)}' "$CLI_TMP" "$CLI_MANIFEST"
chmod 755 "$CLI_TMP"
mv -f "$CLI_TMP" "$BIN/launchpad.mjs"
cat > "$BIN/launchpad" <<'LAUNCHPAD'
#!/bin/sh
# The launchpad CLI is launchpad.mjs, next to this file (install.sh).
exec node "$(dirname "$(readlink -f "$0" 2>/dev/null || echo "$0")")/launchpad.mjs" "$@"
LAUNCHPAD
chmod +x "$BIN/launchpad" "$BIN/launchpad.mjs"
# The skill: each file of the manifest, checked against its sha256, and the manifest itself (what launchpad update compares).
LAUNCHPAD_URL="$URL" node "$BIN/launchpad.mjs" update --skill-dir "$SKILL_DIR" >/dev/null
# The CLI talks to this server from now on, unless a server is already saved (LAUNCHPAD_URL overrides it).
node -e 'const fs=require("fs"),[f,u]=process.argv.slice(1);let c={};try{c=JSON.parse(fs.readFileSync(f,"utf8"))}catch(e){}if(!c.url){c.url=u;fs.writeFileSync(f,JSON.stringify(c,null,2)+"\n",{mode:0o600})}' "$LP_HOME/config.json" "$URL" \
  || echo "could not save the server URL: set LAUNCHPAD_URL=$URL"
echo "launchpad CLI: $BIN/launchpad (server $URL)"
echo "agent skill:   $SKILL_DIR/SKILL.md and the files it routes to; launchpad update keeps the skill and the CLI fresh"
# Coding agents find the skill where they look for skills: a link in ~/.claude/skills (Claude Code) and in
# ~/.agents/skills (Codex, and the agents that read it), made only for a tool installed here and only where nothing
# is in the way; an existing file or another link is left as it is. LAUNCHPAD_SKILL_LINK=0 makes no link.
SKILL_ABS=$(cd "$SKILL_DIR" && pwd)
link_skill() {
  target="$1/launchpad"
  if [ -L "$target" ] && [ "$(readlink "$target")" = "$SKILL_ABS" ]; then echo "skill linked:  $target"; return 0; fi
  if [ -e "$target" ] || [ -L "$target" ]; then echo "left as it is: $target (to use this skill there: ln -sfn $SKILL_ABS $target)"; return 0; fi
  { mkdir -p "$1" && ln -s "$SKILL_ABS" "$target" && echo "skill linked:  $target"; } || echo "could not link the skill in $1: ln -s $SKILL_ABS $target"
}
if [ "${LAUNCHPAD_SKILL_LINK:-1}" != "0" ]; then
  if [ -d "$HOME/.claude" ]; then link_skill "$HOME/.claude/skills"; fi
  if [ -d "$HOME/.codex" ] || [ -d "$HOME/.agents" ]; then link_skill "$HOME/.agents/skills"; fi
  if [ ! -d "$HOME/.claude" ] && [ ! -d "$HOME/.codex" ] && [ ! -d "$HOME/.agents" ]; then echo "no skill link: no ~/.claude, ~/.codex or ~/.agents here. For Claude Code: mkdir -p ~/.claude/skills && ln -s $SKILL_ABS ~/.claude/skills/launchpad"; fi
fi
echo "entrants: rounds are sealed: launchpad commit <slug/n> --transcript <gate-transcript.json> before the build deadline; follow its next step. A complete deposit with automatic reveal available lets you stop; otherwise launchpad wait <slug/n> --until reveal, then launchpad reveal <slug/n> --transcript <gate-transcript.json> ($URL/docs/enter-a-round)"
echo "judges:   every agent judges with the same key: launchpad wait --until judging, then launchpad judge; for a round already in judging, launchpad judge <slug/n> seats you ($URL/docs/judging)"
case ":$PATH:" in *":$BIN:"*) ;; *) echo "add it to your PATH: export PATH=\"$BIN:\$PATH\"" ;; esac
