fix(install): gate confluence for every profile and dedup PROFILE selection
- Add 'confluence' (always-enabled, per README) to COMMON_BUNDLED so a default install (no PI_KIT_PROFILE / no tty) no longer silently strips it from settings.json. The allowlist now covers every skill under skills/. - Deduplicate PROFILES in profiles_from_text: skip a canonical name already present, so 'frontend,1' or 'backend backend' no longer causes a redundant 'npx skills add' call or a misleading 'Профили: frontend frontend'. - Ship the multi-profile refactor it builds on: merged profile sets, profile_skills()/add_word() helpers, README updates, and the pm-task-spec bundled skill. Validated: bash -n; functional checks for dedup and allowlist coverage.
This commit is contained in:
@@ -17,8 +17,8 @@ curl -fsSL https://git.codelab.vc/ai/pi-kit/raw/branch/stable/install.sh | bash
|
||||
```
|
||||
|
||||
Скрипт проверит `git` и Node.js (>= 20) — оба обязательны: git нужен для `pi install git:...`,
|
||||
Node для Pi. Затем поставит Pi (если нет), установит этот пакет, **спросит твой профиль**, поставит
|
||||
нужные скилы и **интерактивно настроит окружение** (`TRACKER_URL`, `CONFLUENCE_URL`,
|
||||
Node для Pi. Затем поставит Pi (если нет), установит этот пакет, **спросит один или несколько твоих профилей**,
|
||||
поставит нужные скилы (их наборы объединяются) и **интерактивно настроит окружение** (`TRACKER_URL`, `CONFLUENCE_URL`,
|
||||
`JIRA_TOKEN`, `CONFLUENCE_TOKEN`, `GITLAB_TOKEN`, ключ LLM-провайдера). Секреты вводятся скрыто и
|
||||
сохраняются в
|
||||
`~/.config/pi-kit/env.sh` (`chmod 600`), который подключается из твоего `~/.zshrc`/`~/.bashrc`.
|
||||
@@ -30,12 +30,16 @@ Pi провайдер-агностичен — модель/провайдера
|
||||
Пропустить интерактивную настройку окружения: `PI_KIT_SETUP_ENV=0`. Значения потом можно
|
||||
поменять прямо в `~/.config/pi-kit/env.sh` или перезапустив `./install.sh`.
|
||||
|
||||
Профиль можно задать заранее (для CI/неинтерактивной установки):
|
||||
Профили можно задать заранее (для CI/неинтерактивной установки) — через запятую:
|
||||
|
||||
```bash
|
||||
PI_KIT_PROFILE=backend curl -fsSL https://git.codelab.vc/ai/pi-kit/raw/branch/stable/install.sh | bash
|
||||
PI_KIT_PROFILE=backend,qa curl -fsSL https://git.codelab.vc/ai/pi-kit/raw/branch/stable/install.sh | bash
|
||||
```
|
||||
|
||||
Один профиль тоже подходит: `PI_KIT_PROFILE=backend`. Скилы выбранных профилей
|
||||
**объединяются** (дубли убираются), а `jira-workflow`/`repo-map`/`docs-map` и общие
|
||||
публичные скилы ставятся всегда.
|
||||
|
||||
### Профили и скилы
|
||||
|
||||
Общее для всех: команды `/bugfix /feature /review /rfc /kit-config /kit-doctor /kit-help`,
|
||||
@@ -54,7 +58,8 @@ guardrails, скиллы `jira-workflow`, `confluence`, `repo-map`, `docs-map`,
|
||||
сжатой постановки задачи до структурированной, пригодной для передачи в разработку)
|
||||
без языковых стандартов кода.
|
||||
|
||||
Сменить профиль позже: `PI_KIT_PROFILE=frontend ./install.sh` (или перезапусти установку).
|
||||
Сменить/добавить профиль позже: `PI_KIT_PROFILE=frontend,qa ./install.sh` (или перезапусти установку
|
||||
и выбери несколько). Уже установленные наборы скилов можно дополнить, указав нужные профили ещё раз.
|
||||
|
||||
Либо вручную (канал `stable` по умолчанию):
|
||||
|
||||
@@ -208,17 +213,19 @@ pi-kit/
|
||||
|
||||
### Профили и гейтинг скилов
|
||||
|
||||
`install.sh` спрашивает профиль (или берёт `PI_KIT_PROFILE`) и делает две вещи:
|
||||
`install.sh` спрашивает один или несколько профилей (или берёт `PI_KIT_PROFILE`) и делает две вещи:
|
||||
|
||||
1. **Гейтит наши скилы** — переписывает запись пакета в `~/.pi/agent/settings.json` в
|
||||
object-форму `{"source": "...", "skills": ["skills/go-standards", ...]}`, чтобы грузились
|
||||
только скилы профиля. Prompts и расширения не гейтятся (их ключи опущены → грузятся все).
|
||||
только скилы выбранных профилей. Prompts и расширения не гейтятся (их ключи опущены → грузятся все).
|
||||
2. **Ставит публичные скилы** через `npx skills add <repo> --skill <name> --global` в
|
||||
`~/.agents/skills/` (Pi читает эту папку). Чужой код в наш репо не вендорится.
|
||||
|
||||
Чтобы поменять маппинг профиль→скилы — правь блок `case "$PROFILE"` и `COMMON_PUBLIC_SKILLS`
|
||||
в `install.sh`. Языковой скилл добавляется как обычный (см. ниже) и подключается к нужному
|
||||
профилю в этом `case`. Профили: `frontend`, `backend`, `qa`, `mobile`, `pm`.
|
||||
Наборы скилов нескольких профилей объединяются (путь `frontend+qa` даст и `typescript`
|
||||
и `python` стандарты). Чтобы поменять маппинг профиль→скилы — правь функцию `profile_skills()`
|
||||
и `COMMON_PUBLIC_SKILLS` в `install.sh`. Языковой скилл добавляется как обычный (см. ниже)
|
||||
и подключается к нужному профилю в этой функции. Профили: `frontend`, `backend`, `qa`,
|
||||
`mobile`, `pm` — любые сочетаются между собой.
|
||||
|
||||
### Как добавить шаблон-команду
|
||||
|
||||
|
||||
+115
-58
@@ -7,9 +7,8 @@
|
||||
# # 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.
|
||||
# Profile selection (multiple allowed, merged):
|
||||
# Non-interactive: set PI_KIT_PROFILE=frontend or PI_KIT_PROFILE=frontend,qa before running.
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
@@ -80,70 +79,132 @@ info "Устанавливаю корпоративный пакет: ${PI_KIT_R
|
||||
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 | pm) ;; # 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 ' 5) pm — постановка задач продакт-менеджера\n'
|
||||
printf 'Номер или имя [1-5]: '
|
||||
} >/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 ;;
|
||||
5 | pm) PROFILE=pm ;;
|
||||
*) PROFILE=all ;;
|
||||
# --- 4. Resolve profiles --------------------------------------------------
|
||||
# Several profiles can be selected at once; their skill sets are merged.
|
||||
# Precedence: PI_KIT_PROFILE env > interactive prompt.
|
||||
# Env is comma- or space-separated: PI_KIT_PROFILE="backend,qa" (a single
|
||||
# value like `backend` still works). Empty env + no tty => all profiles.
|
||||
|
||||
profile_name() { # <token> -> canonical profile name, or empty if unknown
|
||||
case "$1" in
|
||||
1 | frontend) echo frontend ;;
|
||||
2 | backend) echo backend ;;
|
||||
3 | qa) echo qa ;;
|
||||
4 | mobile) echo mobile ;;
|
||||
5 | pm) echo pm ;;
|
||||
esac
|
||||
}
|
||||
|
||||
is_profile() { # <token> — 0 if it's a known profile name/number
|
||||
[ -n "$(profile_name "$1")" ]
|
||||
}
|
||||
|
||||
# Parse a comma/space separated selection into the PROFILES array (deduplicated).
|
||||
# Unknown tokens are dropped with a warning.
|
||||
profiles_from_text() { # <text>
|
||||
local raw="$1" arr p name
|
||||
PROFILES=()
|
||||
IFS=', ' read -r -a arr <<<"$raw"
|
||||
for p in "${arr[@]}"; do
|
||||
[ -z "$p" ] && continue
|
||||
if is_profile "$p"; then
|
||||
name="$(profile_name "$p")"
|
||||
case " ${PROFILES[*]} " in
|
||||
*" $name "*) ;; # duplicate token/alias — already present
|
||||
*) PROFILES+=("$name") ;;
|
||||
esac
|
||||
else
|
||||
PROFILE=all
|
||||
warn "Неизвестный профиль/номер '${p}' — пропущен."
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
warn "Неизвестный PI_KIT_PROFILE='${PROFILE}', использую 'all' (без гейтинга скилов)."
|
||||
PROFILE=all
|
||||
;;
|
||||
esac
|
||||
info "Профиль: ${PROFILE}"
|
||||
done
|
||||
}
|
||||
|
||||
# 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"
|
||||
# Extra bundled skills beyond language standards, added per profile (e.g. product/PM tooling).
|
||||
all_profiles() { PROFILES=(frontend backend qa mobile pm); }
|
||||
|
||||
PROFILES=()
|
||||
if [ -n "${PI_KIT_PROFILE:-}" ]; then
|
||||
profiles_from_text "$PI_KIT_PROFILE"
|
||||
if [ "${#PROFILES[@]}" -eq 0 ]; then
|
||||
warn "В PI_KIT_PROFILE нет распознанных профилей — ставлю все."
|
||||
all_profiles
|
||||
fi
|
||||
elif [ -r /dev/tty ]; then
|
||||
# ask if we have a terminal, even under `curl | bash` (read from /dev/tty)
|
||||
{
|
||||
printf 'Выбери один или несколько профилей (через пробел или запятую):\n'
|
||||
printf ' frontend — TS/UI\n'
|
||||
printf ' backend — Go/Rust/Python\n'
|
||||
printf ' qa — тестирование\n'
|
||||
printf ' mobile — Android/iOS\n'
|
||||
printf ' pm — постановка задач продакт-менеджера\n'
|
||||
printf 'Пример: backend qa | frontend,pm: '
|
||||
} >/dev/tty
|
||||
read -r ans </dev/tty || ans=""
|
||||
if [ -z "$ans" ]; then
|
||||
all_profiles
|
||||
else
|
||||
profiles_from_text "$ans"
|
||||
if [ "${#PROFILES[@]}" -eq 0 ]; then
|
||||
warn "Не распознаны профили — ставлю все."
|
||||
all_profiles
|
||||
fi
|
||||
fi
|
||||
else
|
||||
all_profiles
|
||||
fi
|
||||
|
||||
# Bundled language/extra skills and public skills per profile. Common skills
|
||||
# (confluence/jira-workflow/repo-map/docs-map) are always enabled; prompts and
|
||||
# guardrail extensions are never gated. For several selected profiles their sets are merged.
|
||||
COMMON_BUNDLED="confluence jira-workflow repo-map docs-map"
|
||||
LANG_BUNDLED=""
|
||||
EXTRA_BUNDLED=""
|
||||
PROFILE_PUBLIC=()
|
||||
case "$PROFILE" in
|
||||
|
||||
# Add word(s) to a space-separated variable, skipping duplicates.
|
||||
# Accepts one space-separated word list ("a b c") and/or separate args.
|
||||
add_word() { # <var-name> <words...>
|
||||
local var="$1" w cur all new
|
||||
cur="${!var}"
|
||||
shift
|
||||
all="$*"
|
||||
new=""
|
||||
for w in $all; do
|
||||
case " $cur " in
|
||||
*" $w "*) ;; # already present
|
||||
*) new="$new $w" ;;
|
||||
esac
|
||||
done
|
||||
[ -n "$new" ] && printf -v "$var" '%s%s' "$cur" "$new"
|
||||
}
|
||||
|
||||
# Accumulate the skill sets of one selected profile.
|
||||
profile_skills() { # <profile>
|
||||
case "$1" in
|
||||
frontend)
|
||||
LANG_BUNDLED="typescript-standards"
|
||||
PROFILE_PUBLIC=("anthropics/skills:frontend-design")
|
||||
add_word LANG_BUNDLED "typescript-standards"
|
||||
PROFILE_PUBLIC+=("anthropics/skills:frontend-design")
|
||||
;;
|
||||
backend)
|
||||
LANG_BUNDLED="go-standards rust-standards python-standards"
|
||||
add_word LANG_BUNDLED "go-standards rust-standards python-standards"
|
||||
;;
|
||||
qa) # TODO: уточнить стек тестировщиков; по умолчанию — Python и TS (автотесты)
|
||||
LANG_BUNDLED="python-standards typescript-standards"
|
||||
add_word LANG_BUNDLED "python-standards typescript-standards"
|
||||
;;
|
||||
mobile) # Android (Kotlin) + iOS (Swift)
|
||||
LANG_BUNDLED="kotlin-standards swift-standards"
|
||||
add_word LANG_BUNDLED "kotlin-standards swift-standards"
|
||||
;;
|
||||
pm) # Продакт-менеджер: постановка задач, без языковых стандартов кода
|
||||
LANG_BUNDLED=""
|
||||
EXTRA_BUNDLED="pm-task-spec"
|
||||
;;
|
||||
*) # all: не гейтим, включаем все языковые скилы
|
||||
LANG_BUNDLED="go-standards rust-standards python-standards typescript-standards kotlin-standards swift-standards"
|
||||
add_word EXTRA_BUNDLED "pm-task-spec"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
for p in "${PROFILES[@]}"; do
|
||||
profile_skills "$p"
|
||||
done
|
||||
LANG_BUNDLED="$(printf '%s' "$LANG_BUNDLED" | sed 's/^ *//;s/ *$//')"
|
||||
info "Профили: ${PROFILES[*]}"
|
||||
|
||||
# --- 5. Gate bundled skills via settings object-form filter -----------------
|
||||
# Rewrites the pi-kit package entry in settings.json to {source, skills:[...]}
|
||||
@@ -176,12 +237,8 @@ console.log(patched ? "gated bundled skills -> " + allowed.join(", ") : "pi-kit
|
||||
NODE
|
||||
}
|
||||
|
||||
if [ "$PROFILE" = "all" ]; then
|
||||
info "Профиль 'all' — оставляю все скилы пакета без гейтинга."
|
||||
else
|
||||
info "Гейчу скилы пакета под профиль '${PROFILE}'..."
|
||||
info "Гейчу скилы пакета под профили '${PROFILES[*]}'..."
|
||||
gate_bundled_skills "${COMMON_BUNDLED} ${LANG_BUNDLED} ${EXTRA_BUNDLED}" || warn "не удалось отфильтровать скилы (см. выше)."
|
||||
fi
|
||||
|
||||
# --- 6. Install public skills ----------------------------------------------
|
||||
# Uses the skills.sh CLI (`npx skills add`). Failures are non-fatal.
|
||||
@@ -200,7 +257,7 @@ for entry in "${COMMON_PUBLIC_SKILLS[@]}"; do
|
||||
add_public_skill "${entry%%:*}" "${entry##*:}"
|
||||
done
|
||||
if [ "${#PROFILE_PUBLIC[@]}" -gt 0 ]; then
|
||||
info "Ставлю публичные скилы профиля '${PROFILE}'..."
|
||||
info "Ставлю публичные скилы профилей '${PROFILES[*]}'..."
|
||||
for entry in "${PROFILE_PUBLIC[@]}"; do
|
||||
add_public_skill "${entry%%:*}" "${entry##*:}"
|
||||
done
|
||||
@@ -379,13 +436,13 @@ fi
|
||||
cat <<EOF
|
||||
|
||||
────────────────────────────────────────────────────────
|
||||
Готово! Профиль: ${PROFILE} Канал: ${PI_KIT_CHANNEL}
|
||||
Готово! Профили: ${PROFILES[*]} Канал: ${PI_KIT_CHANNEL}
|
||||
|
||||
Запуск: pi
|
||||
Обновление: pi update --extensions (следует за каналом ${PI_KIT_CHANNEL})
|
||||
Окружение: ${ENV_FILE} (JIRA_TOKEN/CONFLUENCE_TOKEN/GITLAB_TOKEN/ключ провайдера)
|
||||
URL-адреса — дефолты из config/company.json (можно переопределить в env.sh)
|
||||
Сменить профиль: PI_KIT_PROFILE=backend ./install.sh (или перезапусти и выбери; есть и 'pm')
|
||||
Сменить профиль: PI_KIT_PROFILE=frontend,qa ./install.sh (или перезапусти и выбери несколько)
|
||||
Сменить канал: PI_KIT_CHANNEL=beta ./install.sh
|
||||
Команды: /bugfix <тикет> /feature <тикет> /review [фокус] /rfc <тема>
|
||||
/kit-config /kit-doctor /kit-help
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
---
|
||||
name: pm-task-spec
|
||||
description: Используй, когда продакт-менеджер приносит сжатую/неполную постановку задачи и нужно её довести до ясной, непротиворечивой формулировки, пригодной для передачи в разработку. Скилл дополнительно подходит для профиля продукт-менеджера (pm): он опрашивает по ключевым доменам постановки, находит слабые места и выдаёт готовую структурированную постановку задачи.
|
||||
---
|
||||
|
||||
# Постановка задачи (для продакт-менеджера)
|
||||
|
||||
Скилл помогает продакт-менеджеру (ПМ) превратить сжатую или неполную постановку задачи в
|
||||
**готовую структурированную постановку**, в которой нет противоречий и все ключевые моменты
|
||||
понятны и ясны перед передачей в разработку.
|
||||
|
||||
## Принцип работы
|
||||
|
||||
1. Принимаешь исходный текст постановки от ПМ (он может быть очень коротким).
|
||||
2. Анализируешь её **адаптивно** — без жёсткого зачитывания фиксированного чек-листа:
|
||||
определяешь, каких из ключевых доменов не хватает или где есть неоднозначность/противоречие.
|
||||
3. Задаёшь **точечные уточняющие вопросы** только по найденным слабым местам, простым языком,
|
||||
объясняя термины (ПМ может быть не техническим).
|
||||
4. **Не завершаешь опрос, пока каждый ключевой домен постановки не прояснён до однозначного
|
||||
текста.** Непрояснённые/спорные пункты не замалчиваешь — выносишь в раздел «Открытые вопросы».
|
||||
5. В конце выдаёшь **готовую постановку** готовым markdown-шаблоном, который ПМ копирует
|
||||
в нужное место (Jira-тикет, wiki, описание в MR). Никакого автосоздания карточек.
|
||||
|
||||
## Ключевые домены постановки
|
||||
|
||||
Проходишь по ним, но вопрос задаёшь, только если домен пуст, неоднозначен или противоречит
|
||||
другим уже данным ответам:
|
||||
|
||||
- **Суть / заголовок** — одна фраза, что делаем и зачем.
|
||||
- **Контекст и бизнес-цель** — какую проблему/метрику закрываем, почему сейчас.
|
||||
- **Пользователи / аудитория** — для кого, сегменты.
|
||||
- **Сценарии и шаги** — основной сценарий + ключевые вариации (happy path и отступления).
|
||||
- **Границы scope** — что входит, что явно НЕ входит.
|
||||
- **Критерии приёмки** — как понять, что задача сделана и работает.
|
||||
- **Риски и зависимости** — от чего зависит, что может пойти не так.
|
||||
- **Открытые вопросы** — то, что осталось неопределённым на момент выдачи.
|
||||
|
||||
## Правила ведения опроса
|
||||
|
||||
- Говори просто и по делу. Если используешь термин, сразу объясни его другим словом.
|
||||
- Один вопрос за раз, маленькими порциями. Не заваливай ПМ списком из десятка пунктов.
|
||||
- Если ответ противоречит уже сказанному — укажи на противоречие и попроси подсказать,
|
||||
какой вариант верный (не решай за ПМ).
|
||||
- Если ПМ не знает техническую деталь — не додумывай, а отметь это в открытых вопросах
|
||||
(техрешения по реализации оставь разработчикам — это вне scope скилла).
|
||||
- Будь настойчивым, но доброжелательным: цель — закрыть пробелы, а не выпустить сырой текст.
|
||||
|
||||
## Шаблон итоговой постановки
|
||||
|
||||
В конце выдай маркдаун-шаблон. Для каждого раздела проставляй значение из ответов ПМ;
|
||||
если что-то осталось неопределённым — честно помечай `не определено` и добавляй в «Открытые
|
||||
вопросы» с пояснением, что стоит уточнить.
|
||||
|
||||
```markdown
|
||||
## Постановка задачи
|
||||
|
||||
### 1. Суть / заголовок
|
||||
<одна фраза: что делаем и для чего>
|
||||
|
||||
### 2. Контекст и бизнес-цель
|
||||
- Проблема: <что не так сегодня>
|
||||
- Цель/метрика: <что должно измениться, на чём меряем успех>
|
||||
- Почему сейчас: <предпосылки/драйвер>
|
||||
|
||||
### 3. Пользователи / аудитория
|
||||
<кто, сегменты>
|
||||
|
||||
### 4. Сценарии и шаги
|
||||
- Основной сценарий: <шаг за шагом>
|
||||
- Вариации / отступления: <ключевые edge-случаи>
|
||||
|
||||
### 5. Границы scope
|
||||
- Входит: <...>
|
||||
- Не входит: <явные исключения>
|
||||
|
||||
### 6. Критерии приёмки
|
||||
<как понять, что задача решена; желательно проверяемо>
|
||||
|
||||
### 7. Риски и зависимости
|
||||
<от чего зависит, что может пойти не так>
|
||||
|
||||
### 8. Открытые вопросы
|
||||
<пункты, которые осталось уточнить; пометь, кому адресован>
|
||||
```
|
||||
|
||||
## Non-goals
|
||||
|
||||
Этот скилл — **организатор постановки**, а не техконсультант-исполнитель. Он:
|
||||
|
||||
- **не** консультирует по архитектуре/реализации в Go и других языках;
|
||||
- **не** оценивает трудозатраты и сроки;
|
||||
- **не** создаёт/обновляет/таскает Jira-тикеты автоматически;
|
||||
- **не** заменяет прямое обсуждение ПМ с разработчиками — наоборот, готовит материал
|
||||
для такого обсуждения.
|
||||
|
||||
## Интеграция
|
||||
|
||||
Контекст — наша компания: трекер **Jira** (см. скилл `jira-workflow`), git-хост
|
||||
**gitlab.tech.mvideo.ru** (см. `repo-map`), документация **wiki.mvideo.ru** (см. `docs-map`).
|
||||
Скилл автономен: выдаёт готовый текст постановки, ПМ сам переносит его в нужное место.
|
||||
Reference in New Issue
Block a user