#!/usr/bin/env bash # ╔═══════════════════════════════════════════════════════════════════════════╗ # ║ ReasonKit Think MCP — Universal Installer ║ # ║ https://get.reasonkit.sh/think ║ # ║ ║ # ║ Usage: ║ # ║ curl -fsSL https://get.reasonkit.sh/think | bash ║ # ║ wget -qO- https://get.reasonkit.sh/think | bash ║ # ╚═══════════════════════════════════════════════════════════════════════════╝ set -euo pipefail IFS=$'\n\t' # ─── Colors ────────────────────────────────────────────────────────────────── if [ -t 1 ] || [ -c /dev/tty ]; then BOLD='\033[1m'; DIM='\033[2m'; RESET='\033[0m' CYAN='\033[0;36m'; BCYAN='\033[1;36m'; PURPLE='\033[0;35m' GREEN='\033[0;32m'; BGREEN='\033[1;32m'; YELLOW='\033[1;33m' RED='\033[0;31m'; WHITE='\033[1;37m' else BOLD=''; DIM=''; RESET=''; CYAN=''; BCYAN=''; PURPLE='' GREEN=''; BGREEN=''; YELLOW=''; RED=''; WHITE='' fi # ─── Constants ──────────────────────────────────────────────────────────────── BINARY_NAME="reasonkit-think-mcp" CRATE_NAME="reasonkit-think-mcp" GITHUB_REPO="reasonkit/ReasonKit-think" INSTALL_DIR="${HOME}/.local/bin" # Pinned release. Never resolve "latest": an unversioned crates.io install # returns whatever was published last, which can be older than the docs. # Override with REASONKIT_THINK_VERSION=. RK_VERSION="${REASONKIT_THINK_VERSION:-0.2.0}" # Public source commit whose Cargo.toml carries RK_SOURCE_VERSION. Used only # when that exact version has no release binary and is not on crates.io yet. RK_SOURCE_VERSION="0.2.0" RK_SOURCE_REV="f50607080a9823f69bf4d8c8e0d400507ceaa849" # Release asset base URL (cargo-dist layout). Overridable for testing. RK_RELEASE_BASE="${REASONKIT_THINK_RELEASE_BASE:-https://github.com/${GITHUB_REPO}/releases/download/v${RK_VERSION}}" # ─── Example Prompts (shown during installation) ────────────────────────────── PROMPTS=( '"Use reasonkit-think for a quick CoT analysis — step by step, identify the bug in this function and state your assumptions."' '"Explore multiple options for refactoring this auth system using reasonkit-think Tree-of-Thoughts. Score each path by security and complexity."' '"Use reasonkit-think to merge paths from our 3 competing API designs into a single hybrid solution that eliminates each one'\''s weaknesses."' '"Fact-check the assumption that this DB migration is safe — run reasonkit-think verification with evidence quality gates."' '"Apply a debugging pattern via reasonkit-think to trace this intermittent timeout. Start with CoT, then branch into ToT if the root cause is unclear."' '"Converge branches — we have 4 architecture proposals. Use reasonkit-think GoT to distill a final, prioritized action plan."' '"Record the assumption that our cache TTL is safe, then run a go/no-go decision gate via reasonkit-think before deploying."' '"Use reasonkit-think to outline first the entire system design (skeleton-of-thought), then elaborate each branch in parallel."' '"Replay the trace of our last architecture decision — reasonkit-think, export memory snapshot for post-mortem."' '"Run a quality gate checkpoint via reasonkit-think — verify all 3 critical claims have Tier-1 evidence before we proceed."' '"Use reasonkit-think deep think mode: production DB at 100% CPU, 3 theories, CoT→ToT→GoT→audit trail."' '"Apply the scientific method reasoning pattern via reasonkit-think to validate our performance hypothesis systematically."' ) # ─── Helpers ───────────────────────────────────────────────────────────────── banner() { echo "" echo -e "${BCYAN}${BOLD} ⬡ ReasonKit Think MCP${RESET}" echo -e "${DIM} Advanced Reasoning for AI Coding Agents${RESET}" echo -e "${DIM} CoT + ToT + GoT + Verification + Governance${RESET}" echo "" } step() { echo -e "\n${BCYAN}${BOLD}▶ $1${RESET}"; } info() { echo -e " ${CYAN}→${RESET} $1"; } ok() { echo -e " ${BGREEN}✓${RESET} $1"; } warn() { echo -e " ${YELLOW}⚠${RESET} $1"; } fail() { echo -e " ${RED}✗${RESET} $1" >&2; } die() { fail "$1"; exit 1; } show_prompt() { local idx=$(( $1 % ${#PROMPTS[@]} )) local p="${PROMPTS[$idx]}" echo "" echo -e " ${DIM}💡 While you wait — try this with your agent:${RESET}" echo -e " ${PURPLE} $p${RESET}" echo "" } # TTY-safe read: works even when stdin is a pipe (curl | bash) tty_read() { if [ -c /dev/tty ]; then read -r "$@" /dev/null && ! command -v wget &>/dev/null; then die "curl or wget is required. Install one and re-run." fi ok "Network tools available" HAVE_CARGO=false if command -v cargo &>/dev/null; then HAVE_CARGO=true ok "Rust/Cargo found: $(cargo --version 2>/dev/null)" else warn "Rust not found: this needs a pre-built v${RK_VERSION} release binary for your platform" warn "If none is published yet, install Rust 1.95+ first: https://rustup.rs" fi if ! command -v python3 &>/dev/null; then warn "python3 not found — JSON config patching will use fallback method" fi } # ─── Binary Installation ────────────────────────────────────────────────────── # Order: 1) checksum-verified release binary, 2) cargo build of the pinned # version (crates.io if published there, else the pinned public source commit). fetch() { if command -v curl &>/dev/null; then curl -fsSL "$1" -o "$2" 2>/dev/null else wget -q "$1" -O "$2" 2>/dev/null fi } sha256_of() { if command -v sha256sum &>/dev/null; then sha256sum "$1" | cut -d' ' -f1 elif command -v shasum &>/dev/null; then shasum -a 256 "$1" | cut -d' ' -f1 else return 1 fi } # cargo-dist target triple for this machine; empty when no release target exists. release_target() { case "$(detect_os)/$(detect_arch)" in linux/x86_64) echo "x86_64-unknown-linux-gnu" ;; linux/aarch64) echo "aarch64-unknown-linux-gnu" ;; macos/x86_64) echo "x86_64-apple-darwin" ;; macos/aarch64) echo "aarch64-apple-darwin" ;; *) echo "" ;; esac } # True when crates.io lists RK_VERSION as a non-yanked release. crate_version_published() { local index index="$(mktemp "${TMPDIR:-/tmp}/rk-index-XXXXXX")" if fetch "https://index.crates.io/re/as/${CRATE_NAME}" "${index}" \ && grep -F "\"vers\":\"${RK_VERSION}\"" "${index}" | grep -qF '"yanked":false'; then rm -f "${index}" return 0 fi rm -f "${index}" return 1 } install_release_binary() { local target asset work expected actual extracted target="$(release_target)" if [ -z "${target}" ]; then info "No release binary target for $(detect_os)/$(detect_arch)" return 1 fi if ! command -v tar &>/dev/null; then warn "tar not found: skipping the release binary" return 1 fi asset="${BINARY_NAME}-${target}.tar.xz" work="$(mktemp -d "${TMPDIR:-/tmp}/rk-release-XXXXXX")" info "Looking for the v${RK_VERSION} release binary (${target})..." if ! fetch "${RK_RELEASE_BASE}/${asset}" "${work}/${asset}"; then info "No v${RK_VERSION} release binary is published for ${target}" rm -rf "${work}" return 1 fi if ! fetch "${RK_RELEASE_BASE}/${asset}.sha256" "${work}/${asset}.sha256"; then warn "Release checksum is missing: refusing an unverified binary" rm -rf "${work}" return 1 fi expected="$(cut -d' ' -f1 < "${work}/${asset}.sha256")" actual="$(sha256_of "${work}/${asset}" || true)" if [ -z "${actual}" ] || [ "${expected}" != "${actual}" ]; then warn "Checksum mismatch (or no sha256 tool): refusing the download" rm -rf "${work}" return 1 fi ok "Checksum verified (sha256)" if ! tar -xJf "${work}/${asset}" -C "${work}" 2>/dev/null; then warn "Could not unpack ${asset} (tar needs xz support)" rm -rf "${work}" return 1 fi extracted="${work}/${BINARY_NAME}-${target}/${BINARY_NAME}" if [ ! -f "${extracted}" ]; then warn "Release archive layout not recognised" rm -rf "${work}" return 1 fi BINARY_PATH="${INSTALL_DIR}/${BINARY_NAME}" cp -f "${extracted}" "${BINARY_PATH}" chmod 0755 "${BINARY_PATH}" rm -rf "${work}" if ! _verify_binary; then warn "The release binary does not run on this system (Linux builds need glibc 2.34+)" rm -f "${BINARY_PATH}" return 1 fi ok "Installed release binary → ${BINARY_PATH}" return 0 } install_with_cargo() { local -a source_args if crate_version_published; then source_args=(--version "${RK_VERSION}" "${CRATE_NAME}") info "Building ${CRATE_NAME} ${RK_VERSION} from crates.io (a few minutes) ☕" elif [ "${RK_VERSION}" = "${RK_SOURCE_VERSION}" ]; then source_args=(--git "https://github.com/${GITHUB_REPO}" --rev "${RK_SOURCE_REV}" "${CRATE_NAME}") info "${RK_VERSION} is not on crates.io yet: building it from the public source, commit ${RK_SOURCE_REV:0:7} (a few minutes) ☕" else fail "${CRATE_NAME} ${RK_VERSION} is not on crates.io and has no pinned source commit" return 1 fi MANUAL_CARGO_CMD="cargo install --locked ${source_args[*]}" echo "" show_prompt 0 # Write output to temp log so $! is cargo's direct PID (not a pipe's PID) local CARGO_LOG CARGO_BUILD_TMP CARGO_LOG="$(mktemp /tmp/rk-install-XXXXXX.log)" # Force a writable build target dir — the user's system may have # build.target-dir pointing at a read-only mount (e.g. /mnt/storage/…) CARGO_BUILD_TMP="${HOME}/.cargo/rk-build-tmp" mkdir -p "${CARGO_BUILD_TMP}" 2>/dev/null || CARGO_BUILD_TMP="$(mktemp -d /tmp/rk-cargo-XXXXXX)" CARGO_TARGET_DIR="${CARGO_BUILD_TMP}" \ cargo install --locked "${source_args[@]}" --root "${HOME}/.cargo" \ --quiet > "${CARGO_LOG}" 2>&1 & local BUILD_PID=$! local TICK=0 while kill -0 "${BUILD_PID}" 2>/dev/null; do sleep 6 TICK=$(( TICK + 1 )) printf "." if kill -0 "${BUILD_PID}" 2>/dev/null; then show_prompt "${TICK}" fi done echo "" if wait "${BUILD_PID}" 2>/dev/null; then rm -f "${CARGO_LOG}" rm -rf "${CARGO_BUILD_TMP}" 2>/dev/null || true BINARY_PATH="${HOME}/.cargo/bin/${BINARY_NAME}" ok "Installed via cargo → ${BINARY_PATH}" _verify_binary || warn "Installed binary did not report a version" return 0 fi warn "cargo install failed:" tail -3 "${CARGO_LOG}" 2>/dev/null | while IFS= read -r l; do info " ${l}"; done rm -f "${CARGO_LOG}" rm -rf "${CARGO_BUILD_TMP}" 2>/dev/null || true return 1 } install_binary() { step "Installing ${BINARY_NAME} ${RK_VERSION}" mkdir -p "${INSTALL_DIR}" MANUAL_CARGO_CMD="" # ── Strategy 1: checksum-verified release binary (no Rust needed) ───────── if install_release_binary; then return 0 fi # ── Strategy 2: build the pinned version with cargo ─────────────────────── if [ "${HAVE_CARGO}" = "true" ] && install_with_cargo; then return 0 fi # ── Nothing worked ───────────────────────────────────────────────────────── echo "" fail "Installation failed: no usable v${RK_VERSION} release binary for $(detect_os)/$(detect_arch), and no successful Rust build." echo "" if [ "${HAVE_CARGO}" = "true" ]; then info "Run the build manually to see the full log:" info " ${MANUAL_CARGO_CMD:-cargo install --locked --git https://github.com/${GITHUB_REPO} ${CRATE_NAME}}" else info "Install Rust 1.95+ first, then re-run this installer:" info " curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh" fi info "Releases: https://github.com/${GITHUB_REPO}/releases" exit 1 } _verify_binary() { local reported reported="$("${BINARY_PATH}" --version 2>/dev/null)" || return 1 ok "Binary verified: ${reported}" case "${reported}" in *" ${RK_VERSION}") ;; *) warn "Expected version ${RK_VERSION}, the binary reports: ${reported}" ;; esac return 0 } # ─── PATH Setup ─────────────────────────────────────────────────────────────── ensure_path() { local BIN_DIR BIN_DIR="$(dirname "${BINARY_PATH}")" if [[ ":${PATH}:" != *":${BIN_DIR}:"* ]]; then step "Adding ${BIN_DIR} to PATH" # POSIX/bash/zsh shells — export PATH=... local configs=() [ -f "${HOME}/.bashrc" ] && configs+=("${HOME}/.bashrc") [ -f "${HOME}/.zshrc" ] && configs+=("${HOME}/.zshrc") [ -f "${HOME}/.profile" ] && configs+=("${HOME}/.profile") [ -f "${HOME}/.bash_profile" ] && configs+=("${HOME}/.bash_profile") [ -f "${HOME}/.kshrc" ] && configs+=("${HOME}/.kshrc") for cfg in "${configs[@]}"; do if ! grep -qF "${BIN_DIR}" "${cfg}" 2>/dev/null; then printf '\n# ReasonKit Think MCP\nexport PATH="%s:$PATH"\n' "${BIN_DIR}" >> "${cfg}" ok "Added to ${cfg}" fi done # Fish shell — fish_add_path (idempotent, Fish 3.2+) local fish_cfg="${HOME}/.config/fish/config.fish" if [ -d "${HOME}/.config/fish" ]; then touch "${fish_cfg}" if ! grep -qF "${BIN_DIR}" "${fish_cfg}" 2>/dev/null; then printf '\n# ReasonKit Think MCP\nfish_add_path "%s"\n' "${BIN_DIR}" >> "${fish_cfg}" ok "Added to ${fish_cfg}" fi fi # Elvish shell — set paths = [dir $@paths] local elvish_rc="${HOME}/.config/elvish/rc.elv" if [ -d "${HOME}/.config/elvish" ]; then touch "${elvish_rc}" if ! grep -qF "${BIN_DIR}" "${elvish_rc}" 2>/dev/null; then printf '\n# ReasonKit Think MCP\nset paths = [%s $@paths]\n' "${BIN_DIR}" >> "${elvish_rc}" ok "Added to ${elvish_rc}" fi fi # Nushell — $env.PATH local nu_env="${HOME}/.config/nushell/env.nu" if [ -d "${HOME}/.config/nushell" ]; then touch "${nu_env}" if ! grep -qF "${BIN_DIR}" "${nu_env}" 2>/dev/null; then printf '\n# ReasonKit Think MCP\n$env.PATH = ($env.PATH | prepend "%s")\n' "${BIN_DIR}" >> "${nu_env}" ok "Added to ${nu_env}" fi fi export PATH="${BIN_DIR}:${PATH}" ok "PATH updated for current session" fi } # ─── Client Detection ───────────────────────────────────────────────────────── CLIENT_KEYS=(claude gemini codex opencode cursor copilot vscode qwen zed windsurf) client_label() { case "$1" in claude) echo "Claude Code (claude)" ;; gemini) echo "Gemini CLI (gemini)" ;; codex) echo "Codex CLI (codex)" ;; opencode) echo "OpenCode.ai (opencode)" ;; cursor) echo "Cursor & Cursor Agent (cursor)" ;; copilot) echo "GitHub Copilot CLI (gh copilot)" ;; vscode) echo "VS Code / Insiders (code)" ;; qwen) echo "Qwen Code (qwen)" ;; zed) echo "Zed Editor (zed)" ;; windsurf) echo "Windsurf (windsurf)" ;; esac } client_detected() { case "$1" in claude) command -v claude &>/dev/null || [ -f "${HOME}/.claude.json" ] || [ -d "${HOME}/.claude" ] ;; gemini) command -v gemini &>/dev/null || [ -f "${HOME}/.gemini/settings.json" ] || [ -d "${HOME}/.gemini" ] ;; codex) command -v codex &>/dev/null || [ -d "${HOME}/.codex" ] ;; opencode) command -v opencode &>/dev/null || [ -d "${HOME}/.opencode" ] || [ -f "${HOME}/opencode.json" ] ;; cursor) command -v cursor &>/dev/null || [ -d "${HOME}/.cursor" ] ;; copilot) { command -v gh &>/dev/null && gh extension list 2>/dev/null | grep -q copilot; } || \ [ -d "${HOME}/.copilot" ] ;; vscode) command -v code &>/dev/null || command -v code-insiders &>/dev/null || \ [ -d "${HOME}/.config/Code" ] || \ [ -d "${HOME}/Library/Application Support/Code" ] ;; qwen) command -v qwen &>/dev/null || [ -d "${HOME}/.qwen" ] ;; zed) command -v zed &>/dev/null || [ -d "${HOME}/.config/zed" ] ;; windsurf) command -v windsurf &>/dev/null || [ -d "${HOME}/.codeium/windsurf" ] ;; *) return 1 ;; esac } # ─── Interactive Client Selection ───────────────────────────────────────────── SELECTED_CLIENTS="" # space-separated list of selected client keys is_selected() { [[ " ${SELECTED_CLIENTS} " == *" $1 "* ]]; } select_client() { is_selected "$1" || SELECTED_CLIENTS="${SELECTED_CLIENTS} $1"; } deselect_client() { SELECTED_CLIENTS="${SELECTED_CLIENTS/ $1/}" SELECTED_CLIENTS="${SELECTED_CLIENTS% }" SELECTED_CLIENTS="${SELECTED_CLIENTS# }" } build_default_selection() { step "Detecting AI clients" for key in "${CLIENT_KEYS[@]}"; do if client_detected "${key}"; then select_client "${key}" ok "Found: $(client_label "${key}")" fi done if [ -z "${SELECTED_CLIENTS}" ]; then warn "No AI clients auto-detected — you can still select manually." fi return 0 } show_menu() { local i=1 for key in "${CLIENT_KEYS[@]}"; do local label label="$(client_label "${key}")" local det="" client_detected "${key}" && det=" ${DIM}(detected)${RESET}" if is_selected "${key}"; then echo -e " ${BGREEN}[$i] ✓ ${label}${det}${RESET}" else echo -e " ${DIM}[$i] ${label}${det}${RESET}" fi i=$(( i + 1 )) done } select_clients_interactive() { build_default_selection if ! [ -c /dev/tty ]; then warn "No TTY available — skipping interactive client selection." info "Run the script interactively to choose which clients to configure:" info " bash <(curl -fsSL https://get.reasonkit.sh/think)" return fi step "Select clients to configure" echo -e " ${DIM}Detected clients are pre-selected.${RESET}" echo -e " ${DIM}Enter numbers to toggle. Press Enter with no input to confirm.${RESET}" echo "" local all_keys=() for key in "${CLIENT_KEYS[@]}"; do all_keys+=("${key}") done local total=${#all_keys[@]} while true; do show_menu echo "" printf " Toggle (e.g. '1 3'), 'a' all, 'n' none, or Enter to confirm: " local input="" tty_read input [ -z "${input}" ] && break if [ "${input}" = "a" ]; then SELECTED_CLIENTS="" for key in "${CLIENT_KEYS[@]}"; do select_client "${key}"; done echo -e " ${BGREEN}✓ All selected${RESET}" echo "" continue fi if [ "${input}" = "n" ]; then SELECTED_CLIENTS="" echo -e " ${YELLOW}⚠ All deselected${RESET}" echo "" continue fi for num in ${input}; do if echo "${num}" | grep -qE '^[0-9]+$' && [ "${num}" -ge 1 ] && [ "${num}" -le "${total}" ]; then local idx=$(( num - 1 )) local key="${all_keys[$idx]}" if is_selected "${key}"; then deselect_client "${key}" else select_client "${key}" fi fi done echo "" done echo "" step "Confirmed selection" local count=0 for key in "${CLIENT_KEYS[@]}"; do if is_selected "${key}"; then ok "Will configure: $(client_label "${key}")" count=$(( count + 1 )) fi done if [ "${count}" -eq 0 ]; then warn "No clients selected — configure manually later." fi return 0 } # ─── JSON / TOML Helpers ────────────────────────────────────────────────────── # Uses python3 for JSON manipulation; falls back to creation if file is absent py_json_set() { # $1: file path $2: outer key (e.g. "mcpServers") # $3: entry name (e.g. "reasonkit-think") $4: JSON value string python3 - "$1" "$2" "$3" "$4" <<'PY' import json, sys, os fp, outer, name, val = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4] data = {} if os.path.exists(fp): try: with open(fp) as f: text = f.read().strip() if text: data = json.loads(text) except Exception: pass data.setdefault(outer, {})[name] = json.loads(val) os.makedirs(os.path.dirname(os.path.abspath(fp)), exist_ok=True) with open(fp, 'w') as f: json.dump(data, f, indent=2) f.write('\n') PY } py_json_set_schema() { # Like py_json_set but preserves/adds "$schema" at root level python3 - "$1" "$2" "$3" "$4" "$5" <<'PY' import json, sys, os fp, schema_url, outer, name, val = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5] data = {} if os.path.exists(fp): try: with open(fp) as f: text = f.read().strip() if text: data = json.loads(text) except Exception: pass if schema_url and '$schema' not in data: data['$schema'] = schema_url data.setdefault(outer, {})[name] = json.loads(val) os.makedirs(os.path.dirname(os.path.abspath(fp)), exist_ok=True) with open(fp, 'w') as f: json.dump(data, f, indent=2) f.write('\n') PY } py_json_set_zed() { # Zed uses context_servers.name.command = {path, args, env} python3 - "$1" "$2" <<'PY' import json, sys, os fp, bp = sys.argv[1], sys.argv[2] data = {} if os.path.exists(fp): try: with open(fp) as f: text = f.read().strip() if text: data = json.loads(text) except Exception: pass data.setdefault('context_servers', {})['reasonkit-think'] = { "command": {"path": bp, "args": [], "env": {"TMPDIR": "/tmp"}}, "settings": {} } os.makedirs(os.path.dirname(os.path.abspath(fp)), exist_ok=True) with open(fp, 'w') as f: json.dump(data, f, indent=2) f.write('\n') PY } toml_add_mcp() { local file="$1" local bp="$2" mkdir -p "$(dirname "${file}")" if [ -f "${file}" ] && grep -q '\[mcp_servers.reasonkit-think\]' "${file}" 2>/dev/null; then info "reasonkit-think already in ${file}" return fi { echo "" echo "[mcp_servers.reasonkit-think]" echo "command = \"${bp}\"" echo "args = []" echo "" echo "[mcp_servers.reasonkit-think.env]" echo "TMPDIR = \"/tmp\"" } >> "${file}" } # ─── Client Configuration ───────────────────────────────────────────────────── configure_clients() { [ -z "${SELECTED_CLIENTS}" ] && return step "Configuring AI clients" local BP="${BINARY_PATH}" local HAVE_PY=false command -v python3 &>/dev/null && HAVE_PY=true # Standard stdio MCP entry (used by most clients) local STD_ENTRY STD_ENTRY=$(printf '{"type":"stdio","command":"%s","args":[],"env":{"TMPDIR":"/tmp"}}' "${BP}") # ── Claude Code ──────────────────────────────────────────────────────────── if is_selected "claude"; then local f="${HOME}/.claude.json" if [ "${HAVE_PY}" = "true" ]; then py_json_set "${f}" "mcpServers" "reasonkit-think" "${STD_ENTRY}" ok "Claude Code → ${f}" else warn "python3 needed for Claude Code config; patch manually:" info " ${f}: mcpServers.reasonkit-think.command = \"${BP}\"" fi fi # ── Gemini CLI ───────────────────────────────────────────────────────────── if is_selected "gemini"; then local f="${HOME}/.gemini/settings.json" local GEMINI_ENTRY GEMINI_ENTRY=$(printf '{"command":"%s","args":[],"env":{"TMPDIR":"/tmp"},"trust":true}' "${BP}") if [ "${HAVE_PY}" = "true" ]; then py_json_set "${f}" "mcpServers" "reasonkit-think" "${GEMINI_ENTRY}" ok "Gemini CLI → ${f}" else warn "python3 needed for Gemini CLI config" fi fi # ── Codex CLI ───────────────────────────────────────────────────────────── if is_selected "codex"; then local f="${HOME}/.codex/config.toml" mkdir -p "${HOME}/.codex" toml_add_mcp "${f}" "${BP}" ok "Codex CLI → ${f}" fi # ── OpenCode ────────────────────────────────────────────────────────────── if is_selected "opencode"; then local f="${HOME}/.opencode/opencode.json" local OC_ENTRY OC_ENTRY=$(printf '{"type":"local","command":["%s"],"enabled":true,"environment":{"TMPDIR":"/tmp"}}' "${BP}") if [ "${HAVE_PY}" = "true" ]; then py_json_set_schema "${f}" "https://opencode.ai/config.json" "mcp" "reasonkit-think" "${OC_ENTRY}" ok "OpenCode → ${f}" else warn "python3 needed for OpenCode config" fi fi # ── Cursor ──────────────────────────────────────────────────────────────── if is_selected "cursor"; then local f="${HOME}/.cursor/mcp.json" if [ "${HAVE_PY}" = "true" ]; then py_json_set "${f}" "mcpServers" "reasonkit-think" "${STD_ENTRY}" ok "Cursor → ${f}" else warn "python3 needed for Cursor config" fi fi # ── Copilot CLI ─────────────────────────────────────────────────────────── if is_selected "copilot"; then local f="${HOME}/.copilot/mcp-config.json" local CP_ENTRY CP_ENTRY=$(printf '{"type":"local","command":"%s","args":[],"env":{"TMPDIR":"/tmp"},"tools":["*"]}' "${BP}") if [ "${HAVE_PY}" = "true" ]; then py_json_set "${f}" "mcpServers" "reasonkit-think" "${CP_ENTRY}" ok "Copilot CLI → ${f}" else warn "python3 needed for Copilot CLI config" fi fi # ── VS Code ─────────────────────────────────────────────────────────────── if is_selected "vscode"; then local OS OS=$(detect_os) local f case "${OS}" in macos) f="${HOME}/Library/Application Support/Code/User/mcp.json" ;; *) f="${HOME}/.config/Code/User/mcp.json" ;; esac local VSC_ENTRY VSC_ENTRY=$(printf '{"type":"stdio","command":"%s","args":[],"env":{"TMPDIR":"/tmp"}}' "${BP}") if [ "${HAVE_PY}" = "true" ]; then py_json_set "${f}" "servers" "reasonkit-think" "${VSC_ENTRY}" ok "VS Code → ${f}" else warn "python3 needed for VS Code config" fi fi # ── Qwen Code ───────────────────────────────────────────────────────────── if is_selected "qwen"; then local f="${HOME}/.qwen/mcp.json" if [ "${HAVE_PY}" = "true" ]; then py_json_set "${f}" "mcpServers" "reasonkit-think" "${STD_ENTRY}" ok "Qwen Code → ${f}" else warn "python3 needed for Qwen Code config" fi fi # ── Zed ─────────────────────────────────────────────────────────────────── if is_selected "zed"; then local f="${HOME}/.config/zed/settings.json" if [ "${HAVE_PY}" = "true" ]; then py_json_set_zed "${f}" "${BP}" ok "Zed Editor → ${f}" else warn "python3 needed for Zed config" fi fi # ── Windsurf ────────────────────────────────────────────────────────────── if is_selected "windsurf"; then local f="${HOME}/.codeium/windsurf/mcp_config.json" if [ "${HAVE_PY}" = "true" ]; then py_json_set "${f}" "mcpServers" "reasonkit-think" "${STD_ENTRY}" ok "Windsurf → ${f}" else warn "python3 needed for Windsurf config" fi fi } # ─── Success Summary ────────────────────────────────────────────────────────── print_success() { echo "" echo -e "${BGREEN}${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" echo -e "${BGREEN}${BOLD} ✓ ReasonKit Think MCP installed successfully!${RESET}" echo -e "${BGREEN}${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" echo "" echo -e " ${BOLD}Binary:${RESET} ${BINARY_PATH}" echo "" echo -e " ${BCYAN}${BOLD}Next steps:${RESET}" echo -e " ${CYAN}1.${RESET} Restart your AI client(s)" echo -e " ${CYAN}2.${RESET} Try this showcase prompt:" echo "" echo -e " ${PURPLE}┌────────────────────────────────────────────────────────────────┐${RESET}" echo -e " ${PURPLE}│ \"Use the reasonkit-think reasoning system to think through │${RESET}" echo -e " ${PURPLE}│ this deeply. Our production DB is at 100% CPU, 3 theories. │${RESET}" echo -e " ${PURPLE}│ CoT for first-pass assumptions → ToT to explore all 3 │${RESET}" echo -e " ${PURPLE}│ theories → verify critical claims → GoT to distill a single │${RESET}" echo -e " ${PURPLE}│ prioritized action plan with an audit trail.\" │${RESET}" echo -e " ${PURPLE}└────────────────────────────────────────────────────────────────┘${RESET}" echo "" echo -e " ${BOLD}${CYAN}Natural triggers (no tool names needed):${RESET}" printf " %-28s→ %s\n" '"step by step"' "CoT linear analysis" printf " %-28s→ %s\n" '"explore options"' "ToT multi-path branching" printf " %-28s→ %s\n" '"merge paths"' "GoT synthesis" printf " %-28s→ %s\n" '"fact-check"' "Verification engine" printf " %-28s→ %s\n" '"go/no-go"' "Governance gate" printf " %-28s→ %s\n" '"converge branches"' "Quality checkpoint" echo "" echo -e " ${DIM}GitHub: https://github.com/reasonkit/reasonkit-think${RESET}" echo -e " ${DIM}Web: https://reasonkit.sh${RESET}" echo "" } # ─── Main ───────────────────────────────────────────────────────────────────── main() { banner check_prereqs install_binary ensure_path select_clients_interactive configure_clients print_success } main "$@"