Files
pi-kit/install.sh
Aleksey Shakhmatov e98e4ca9ae feat(skills): add kotlin-standards + swift-standards for mobile profile
- kotlin-standards: gradle build/test, ktlint/detekt, Android lint; company specifics TODO
- swift-standards: SwiftPM/xcodebuild, swiftformat/swiftlint, XCTest; company specifics TODO
- wire mobile profile -> kotlin+swift; 'all' now includes both; README matrix updated
- verified all six *-standards skills load via pi RPC
2026-07-16 12:31:12 +03:00

236 lines
10 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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 </dev/tty || ans=""
case "$ans" in
1 | frontend) PROFILE=frontend ;;
2 | backend) PROFILE=backend ;;
3 | qa) PROFILE=qa ;;
4 | mobile) PROFILE=mobile ;;
*) PROFILE=all ;;
esac
else
PROFILE=all
fi
;;
*)
warn "Неизвестный PI_KIT_PROFILE='${PROFILE}', использую 'all' (без гейтинга скилов)."
PROFILE=all
;;
esac
info "Профиль: ${PROFILE}"
# Bundled language skills per profile. Common skills (jira-workflow/repo-map/
# docs-map) are always enabled. Prompts and guardrail extensions are never gated.
COMMON_BUNDLED="jira-workflow repo-map docs-map"
PROFILE_PUBLIC=()
case "$PROFILE" in
frontend)
LANG_BUNDLED="typescript-standards"
PROFILE_PUBLIC=("anthropics/skills:frontend-design")
;;
backend)
LANG_BUNDLED="go-standards rust-standards python-standards"
;;
qa) # TODO: уточнить стек тестировщиков; по умолчанию — Python и TS (автотесты)
LANG_BUNDLED="python-standards typescript-standards"
;;
mobile) # Android (Kotlin) + iOS (Swift)
LANG_BUNDLED="kotlin-standards swift-standards"
;;
*) # all: не гейтим, включаем все языковые скилы
LANG_BUNDLED="go-standards rust-standards python-standards typescript-standards kotlin-standards swift-standards"
;;
esac
# --- 5. Gate bundled skills via settings object-form filter -----------------
# Rewrites the pi-kit package entry in settings.json to {source, skills:[...]}
# so only the profile-relevant skills load. Extensions/prompts stay untouched
# (their keys are omitted => 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() { # <owner/repo> <skill-name>
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 <<EOF
────────────────────────────────────────────────────────
Готово! Профиль: ${PROFILE}
Запуск: pi
Обновление: pi update --extensions
Сменить профиль: PI_KIT_PROFILE=backend ./install.sh (или перезапусти и выбери)
Команды: /bugfix <тикет> /feature <тикет>
/review [фокус] /rfc <тема> /kit-config
Проверка: /kit-config — покажет действующие корпоративные значения
Внутренний курс по Pi: ${INTERNAL_COURSE_URL}
────────────────────────────────────────────────────────
EOF