#!/bin/sh # CandleKeep Try — zero-friction onboarding # Usage: curl -sL getcandlekeep.com/try | sh set -e # ── Helpers ────────────────────────────────────────────────────────── CANDLEKEEP_API="https://www.getcandlekeep.com" SESSION_ID=$(date +%s)-$$-$(od -An -N4 -tx4 /dev/urandom | tr -d ' ') send_event() { _step="$1" _usecases="${2:-}" _meta="${3:-}" _body="{\"sessionId\":\"$SESSION_ID\",\"step\":\"$_step\"" if [ -n "$_usecases" ]; then _body="${_body},\"useCases\":[$_usecases]" fi if [ -n "$_meta" ]; then _body="${_body},\"metadata\":{$_meta}" fi _body="${_body}}" curl -s -X POST "$CANDLEKEEP_API/api/public/try/events" \ -H "Content-Type: application/json" \ -d "$_body" < /dev/null > /dev/null 2>&1 & } bold() { printf "\033[1m%s\033[0m" "$1"; } green() { printf "\033[32m%s\033[0m" "$1"; } yellow() { printf "\033[33m%s\033[0m" "$1"; } red() { printf "\033[31m%s\033[0m" "$1"; } dim() { printf "\033[2m%s\033[0m" "$1"; } spin() { _msg="$1"; shift "$@" < /dev/null > /dev/null 2>&1 & _pid=$! _chars='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏' while kill -0 "$_pid" 2>/dev/null; do for _c in $(echo "$_chars" | grep -o .); do printf "\r %s %s" "$_c" "$_msg" sleep 0.1 done done wait "$_pid" _exit=$? printf "\r \r" return $_exit } # ── Banner ─────────────────────────────────────────────────────────── printf "\n" printf " %s\n" "$(bold ' CandleKeep')" printf " %s\n" "$(dim ' Turn any AI agent into a domain expert')" printf "\n" send_event "script_started" "" "\"os\":\"$(uname -s)\",\"arch\":\"$(uname -m)\"" # ── Pre-install detection ──────────────────────────────────────────── CK_PRE_INSTALLED=false if command -v ck > /dev/null 2>&1; then CK_PRE_INSTALLED=true fi CLAUDE_INSTALLED=false if command -v claude > /dev/null 2>&1; then CLAUDE_INSTALLED=true fi # ── Check / Install ck CLI ─────────────────────────────────────────── if command -v ck > /dev/null 2>&1; then printf " %s Checking for ck CLI updates...\n" "$(dim '~')" if command -v brew > /dev/null 2>&1 && brew list CandleKeepAgents/candlekeep/candlekeep-cli < /dev/null > /dev/null 2>&1; then spin "Updating ck CLI..." brew upgrade CandleKeepAgents/candlekeep/candlekeep-cli || true fi printf " %s ck CLI ready (%s)\n" "$(green '+')" "$(ck --version 2>/dev/null || echo 'unknown')" else printf " %s ck CLI not found. Installing via Homebrew...\n" "$(yellow '!')" if ! command -v brew > /dev/null 2>&1; then printf " %s Homebrew is required. Install it from https://brew.sh\n" "$(red 'x')" exit 1 fi spin "Tapping CandleKeep..." brew tap CandleKeepAgents/candlekeep spin "Installing ck CLI..." brew install CandleKeepAgents/candlekeep/candlekeep-cli printf " %s ck CLI installed\n" "$(green '+')" fi send_event "cli_installed" "" "\"ckPreInstalled\":$CK_PRE_INSTALLED,\"claudeInstalled\":$CLAUDE_INSTALLED" # ── Check Claude Code ──────────────────────────────────────────────── if ! command -v claude > /dev/null 2>&1; then printf " %s Claude Code not found. Install it: %s\n" "$(yellow '!')" "$(dim 'npm install -g @anthropic-ai/claude-code')" printf " %s You can still explore content, but the full experience needs Claude Code.\n" "$(dim ' ')" fi # ── Install CandleKeep Plugin ────────────────────────────────────── if command -v claude > /dev/null 2>&1; then printf " %s Installing CandleKeep plugin...\n" "$(dim '~')" spin "Adding CandleKeep marketplace..." claude plugin marketplace add CandleKeepAgents/candlekeep-marketplace || true spin "Installing CandleKeep plugin..." claude plugin install candlekeep-cloud@candlekeep || true printf " %s CandleKeep plugin ready\n" "$(green '+')" fi # ── Detect Use Cases ───────────────────────────────────────────────── printf "\n Scanning your project...\n" DETECTED="" # React / Frontend if [ -f "package.json" ]; then if grep -q '"react"' package.json 2>/dev/null || \ ls ./*.tsx ./*.jsx src/**/*.tsx src/**/*.jsx 2>/dev/null | head -1 > /dev/null 2>&1 || \ [ -f "next.config.js" ] || [ -f "next.config.mjs" ] || [ -f "next.config.ts" ]; then DETECTED="${DETECTED} react" fi fi # PostgreSQL if [ -f "prisma/schema.prisma" ] || \ ls ./*.sql 2>/dev/null | head -1 > /dev/null 2>&1 || \ [ -d "supabase" ] || \ ([ -f "package.json" ] && grep -qE '"pg"|"postgres"' package.json 2>/dev/null); then DETECTED="${DETECTED} postgres" fi # Claude Code Skills if [ -d ".claude" ] || [ -f "CLAUDE.md" ] || [ -d "skills" ] || \ [ -d ".claude-plugin" ] || [ -f "plugin.json" ]; then DETECTED="${DETECTED} claude-skills" fi # Agents if [ -d "agents" ] || [ -d ".claude/agents" ]; then DETECTED="${DETECTED} agents" fi # Security Audit (webapp projects) if [ -f "package.json" ] || [ -f "Dockerfile" ] || [ -f "docker-compose.yml" ] || \ [ -f ".env" ] || [ -f ".env.example" ] || \ ls ./*.ts ./*.js src/**/*.ts src/**/*.js 2>/dev/null | head -1 > /dev/null 2>&1; then DETECTED="${DETECTED} security" fi DETECTED=$(echo "$DETECTED" | xargs) # Build JSON array from DETECTED DETECTED_JSON="" for _d in $DETECTED; do if [ -n "$DETECTED_JSON" ]; then DETECTED_JSON="${DETECTED_JSON},\"$_d\"" else DETECTED_JSON="\"$_d\"" fi done send_event "detection_completed" "" "\"detected\":[$DETECTED_JSON]" # ── Pre-selected (from web configurator) ──────────────────────────── PRESELECTED="" # Use case titles title_react="React & Frontend" title_postgres="PostgreSQL" title_claude_skills="Claude Code Skills" title_agents="Building Agents" title_security="Security Audit" desc_react="Review your code against Vercel's frontend guidelines" desc_postgres="Review your DB against Supabase's best practices" desc_claude_skills="Build powerful skills for Claude Code" desc_agents="Architect reliable agent systems" desc_security="Audit your webapp for vulnerabilities" # All available use cases ALL_CASES="react postgres claude-skills agents security" if [ -n "$PRESELECTED" ]; then # ── Pre-selected from web configurator — skip interactive menu ──── SELECTED="$PRESELECTED" SELECTED_CSV=$(echo "$SELECTED" | tr ' ' ',') SELECTED_JSON="" for _s in $SELECTED; do if [ -n "$SELECTED_JSON" ]; then SELECTED_JSON="${SELECTED_JSON},\"$_s\"" else SELECTED_JSON="\"$_s\"" fi done printf " %s Pre-selected audits:\n" "$(green '+')" for uc in $SELECTED; do eval "uc_title=\$title_$(echo "$uc" | tr '-' '_')" printf " %s %s\n" "$(bold '-')" "$uc_title" done printf "\n" else # ── Interactive Menu ──────────────────────────────────────────────── MENU_ITEMS="" IDX=0 for uc in $ALL_CASES; do IDX=$((IDX + 1)) IS_DETECTED="" for d in $DETECTED; do if [ "$d" = "$uc" ]; then IS_DETECTED=" $(green '[detected]')" break fi done eval "uc_title=\$title_$(echo "$uc" | tr '-' '_')" eval "uc_desc=\$desc_$(echo "$uc" | tr '-' '_')" printf " %s %s%s\n" "$(bold "$IDX")" "$uc_title" "$IS_DETECTED" printf " %s\n" "$(dim "$uc_desc")" MENU_ITEMS="${MENU_ITEMS} $uc" done printf "\n Enter numbers separated by spaces (e.g., 1 3): " read -r CHOICES < /dev/tty # ── Parse Selection ────────────────────────────────────────────────── SELECTED="" SELECTED_JSON="" for choice in $CHOICES; do i=0 for uc in $MENU_ITEMS; do i=$((i + 1)) if [ "$i" = "$choice" ]; then SELECTED="${SELECTED} $uc" if [ -n "$SELECTED_JSON" ]; then SELECTED_JSON="${SELECTED_JSON},\"$uc\"" else SELECTED_JSON="\"$uc\"" fi fi done done SELECTED=$(echo "$SELECTED" | xargs) SELECTED_CSV=$(echo "$SELECTED" | tr ' ' ',') if [ -z "$SELECTED" ]; then printf " %s No valid selection. Exiting.\n" "$(red 'x')" exit 1 fi fi printf "\n Selected: %s\n\n" "$(bold "$SELECTED")" send_event "usecase_selected" "$SELECTED_JSON" "\"detected\":[$DETECTED_JSON],\"selected\":[$SELECTED_JSON]" # ── Build Claude Prompt ────────────────────────────────────────────── PROMPT="You have access to CandleKeep, an AI knowledge library. " PROMPT="${PROMPT}For each use case below, run the corresponding ck try command to fetch expert content, " PROMPT="${PROMPT}then apply that knowledge to review and improve the code in this repository. " for uc in $SELECTED; do eval "uc_title=\$title_$(echo "$uc" | tr '-' '_')" PROMPT="${PROMPT} ## $uc_title Run: ck try $uc Then apply the retrieved knowledge to this codebase. " done PROMPT="${PROMPT} After reviewing, present your complete report with all findings and recommended next steps. AFTER you have presented your full report with every finding listed, as the very last step, open the user's browser so they can save this knowledge to their library. Run this shell command: open \"https://www.getcandlekeep.com/try/claim?uc=${SELECTED_CSV}&sid=${SESSION_ID}\" Tell the user: 'I\'ve opened CandleKeep in your browser — sign up to save this knowledge to your personal library. After that, your AI agent will always have access to it.'" # ── Spawn Claude Code ──────────────────────────────────────────────── if command -v claude > /dev/null 2>&1; then send_event "claude_spawned" "$SELECTED_JSON" printf " Starting Claude Code...\n\n" claude "$PROMPT" send_event "claude_completed" "$SELECTED_JSON" else printf " %s Claude Code is not installed. Here's what Claude would do:\n\n" "$(yellow '!')" printf " For each selected use case, Claude Code would:\n" for uc in $SELECTED; do eval "uc_title=\$title_$(echo "$uc" | tr '-' '_')" printf " 1. Run: ck try %s\n" "$uc" printf " 2. Apply %s knowledge to your codebase\n\n" "$uc_title" done fi