#!/usr/bin/env bash # # pi-kit bootstrap — one-shot setup for new engineers. Idempotent: safe to re-run. # # Usage: # curl -fsSL https://git.codelab.vc/ai/pi-kit/-/raw/main/install.sh | bash # # or, from a checkout: # ./install.sh # # Profile selection: # Interactive by default (prompts via /dev/tty, so it works under curl | bash). # Non-interactive: set PI_KIT_PROFILE=frontend|backend|qa|mobile before running. # set -euo pipefail # --------------------------------------------------------------------------- # The ONLY place the repository address appears. No version ref on purpose, so # `pi update --extensions` always pulls the latest main. # --------------------------------------------------------------------------- PI_KIT_REPO="git:git.codelab.vc/ai/pi-kit" MIN_NODE_MAJOR=20 INTERNAL_COURSE_URL="TODO: ссылка на внутренний курс по Pi" SETTINGS="$HOME/.pi/agent/settings.json" # Public skills installed for EVERYONE (repo:skill), via `npx skills add`. COMMON_PUBLIC_SKILLS=( "mattpocock/skills:grill-me" "mattpocock/skills:grill-with-docs" "mattpocock/skills:code-review" "mattpocock/skills:diagnosing-bugs" # диагностика багов (в mattpocock/skills скилл называется так) ) info() { printf '\033[1;34m==>\033[0m %s\n' "$*"; } warn() { printf '\033[1;33m[!]\033[0m %s\n' "$*"; } err() { printf '\033[1;31m[x]\033[0m %s\n' "$*" >&2; } # --- 1. Node.js ------------------------------------------------------------- if ! command -v node >/dev/null 2>&1; then err "Node.js не найден. Установи Node.js >= ${MIN_NODE_MAJOR} (https://nodejs.org) и запусти скрипт снова." exit 1 fi NODE_MAJOR="$(node -p 'process.versions.node.split(".")[0]')" if [ "$NODE_MAJOR" -lt "$MIN_NODE_MAJOR" ]; then err "Нужен Node.js >= ${MIN_NODE_MAJOR}, а установлен $(node -v). Обнови Node.js и запусти скрипт снова." exit 1 fi info "Node.js $(node -v) — ок." # --- 2. Pi ------------------------------------------------------------------ if command -v pi >/dev/null 2>&1; then info "Pi уже установлен ($(pi --version 2>/dev/null || echo 'версия неизвестна'))." else info "Ставлю Pi..." curl -fsSL https://pi.dev/install.sh | sh # Make pi available in the current shell if the installer added a new PATH dir. if ! command -v pi >/dev/null 2>&1; then export PATH="$HOME/.local/bin:$PATH" fi if ! command -v pi >/dev/null 2>&1; then err "Pi установлен, но не найден в PATH. Открой новый терминал (или поправь PATH) и запусти скрипт снова." exit 1 fi fi # --- 3. Install the corporate package -------------------------------------- info "Устанавливаю корпоративный пакет: ${PI_KIT_REPO}" pi install "$PI_KIT_REPO" info "Пакет установлен. Обновляться потом: pi update --extensions" # --- 4. Resolve profile ----------------------------------------------------- # Precedence: PI_KIT_PROFILE env > interactive prompt > "all" (no gating). PROFILE="${PI_KIT_PROFILE:-}" case "$PROFILE" in frontend | backend | qa | mobile) ;; # valid, keep "") # ask if we have a terminal, even under `curl | bash` (read from /dev/tty) if [ -r /dev/tty ]; then { printf 'Выбери профиль:\n' printf ' 1) frontend — TS/UI\n' printf ' 2) backend — Go/Rust/Python\n' printf ' 3) qa — тестирование\n' printf ' 4) mobile — Android/iOS\n' printf 'Номер или имя [1-4]: ' } >/dev/tty read -r ans all load). gate_bundled_skills() { local allowed="$1" if [ ! -f "$SETTINGS" ]; then warn "settings.json не найден ($SETTINGS) — пропускаю гейтинг скилов." return 0 fi SETTINGS_FILE="$SETTINGS" ALLOWED="$allowed" SRC_MATCH="pi-kit" node <<'NODE' const fs = require("fs"); const file = process.env.SETTINGS_FILE; const allowed = process.env.ALLOWED.trim().split(/\s+/).filter(Boolean).map((s) => "skills/" + s); const s = JSON.parse(fs.readFileSync(file, "utf8")); if (!Array.isArray(s.packages)) process.exit(0); let patched = false; s.packages = s.packages.map((p) => { const src = typeof p === "string" ? p : (p && p.source); if (typeof src === "string" && src.includes(process.env.SRC_MATCH)) { patched = true; const base = (p && typeof p === "object") ? p : {}; return Object.assign({}, base, { source: src, skills: allowed }); } return p; }); fs.writeFileSync(file, JSON.stringify(s, null, 2) + "\n"); console.log(patched ? "gated bundled skills -> " + allowed.join(", ") : "pi-kit entry not found; skipped gating"); NODE } if [ "$PROFILE" = "all" ]; then info "Профиль 'all' — оставляю все скилы пакета без гейтинга." else info "Гейчу скилы пакета под профиль '${PROFILE}'..." gate_bundled_skills "${COMMON_BUNDLED} ${LANG_BUNDLED}" || warn "не удалось отфильтровать скилы (см. выше)." fi # --- 6. Install public skills ---------------------------------------------- # Uses the skills.sh CLI (`npx skills add`). Failures are non-fatal. add_public_skill() { # local repo="$1" name="$2" info "Публичный скилл: ${name} (${repo})" # --global installs into ~/.agents/skills (the universal dir Pi reads), # not the current project. --yes skips the interactive scope prompt. if ! npx --yes skills add "https://github.com/${repo}" --skill "${name}" --global --yes >/dev/null 2>&1; then warn "не удалось поставить '${name}'. Вручную: npx skills add https://github.com/${repo} --skill ${name} -g -y" fi } info "Ставлю общие публичные скилы..." for entry in "${COMMON_PUBLIC_SKILLS[@]}"; do add_public_skill "${entry%%:*}" "${entry##*:}" done if [ "${#PROFILE_PUBLIC[@]}" -gt 0 ]; then info "Ставлю публичные скилы профиля '${PROFILE}'..." for entry in "${PROFILE_PUBLIC[@]}"; do add_public_skill "${entry%%:*}" "${entry##*:}" done fi # --- 7. Provider API keys --------------------------------------------------- # Pi is provider-agnostic: engineers use different LLM providers (Anthropic, # OpenAI, OpenRouter, Google, ...). The package does NOT pin a provider/model. # We only check that *some* provider key is present and hint if none is. PROVIDER_KEYS=(ANTHROPIC_API_KEY OPENAI_API_KEY OPENROUTER_API_KEY GEMINI_API_KEY GOOGLE_API_KEY GROQ_API_KEY) found_key="" for k in "${PROVIDER_KEYS[@]}"; do if [ -n "${!k:-}" ]; then found_key="$k" break fi done if [ -n "$found_key" ]; then info "Найден ключ LLM-провайдера: ${found_key} — ок." else warn "Не найден ни один ключ LLM-провайдера." cat <<'EOF' Pi работает с разными провайдерами — выбери свой и задай ключ, например: export ANTHROPIC_API_KEY="..." # Anthropic export OPENAI_API_KEY="..." # OpenAI export OPENROUTER_API_KEY="..." # OpenRouter Провайдер и модель выбираются флагами --provider/--model или в настройках Pi (см. `pi --list-models`) — пакет их не навязывает. Для скилла работы с трекером также понадобятся: export TRACKER_URL="..." # адрес Jira (или возьмётся из корп. контекста) export JIRA_TOKEN="..." # Jira personal access token (Server/DC) EOF fi # --- 8. Cheat sheet --------------------------------------------------------- cat < /feature <тикет> /review [фокус] /rfc <тема> /kit-config Проверка: /kit-config — покажет действующие корпоративные значения Внутренний курс по Pi: ${INTERNAL_COURSE_URL} ──────────────────────────────────────────────────────── EOF