Metadata-Version: 2.4
Name: s-skillkit
Version: 0.2.2
Summary: Локальное ядро управления навыками: install из git/локальной папки, junction/symlink-линковка в scope агента, .skillignore-фильтр, build manifest, project-манифест. 0 завязок на сеть/auth — stdlib + tomli-w + pathspec + platformdirs.
Author: Dmitry
License: MIT
License-File: LICENSE
Requires-Python: >=3.11
Requires-Dist: pathspec>=0.12.0
Requires-Dist: platformdirs>=4.0
Requires-Dist: tomli-w>=1.0
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Description-Content-Type: text/markdown

# s-skillkit

Локальное ядро управления навыками (skills) — **SIBLING** `librarykit`
(не зависит от него, от httpx, auth или сети). Только локальная ФС-механика.

## Что внутри

- **`SkillStore`** (`skillkit.installer`) — материализация навыка в центральный
  стор и линковка (junction на Windows / symlink на POSIX) в scope агента:
  - `install_from_git` / `install_from_path` / `install` / `materialize`
  - `update` (инкрементальный sha-diff), `link_existing`, `migrate_scope`
  - `remove` (keep-local / purge)
  - P0 **stub-would-clobber guard** (stub не затирает живой контент).
- **`targets`** — `IAgentTarget` + `detect_agent` / `get_target` (Claude Code,
  Codex, Antigravity).
- **`manifest`** — `build_manifest` для publish + ридеры frontmatter /
  `_skill_meta.toml`.
- **`filter`** — `.skillignore` / `files`-allowlist фильтр (pathspec).
- **`project`** — проектный манифест `.skills-hub/skills.toml`.
- **`Paths`** — инъекция каталогов (`store_dir` / `config_dir` / `bin_dir`).
  `Paths.default()` — нативная раскладка через platformdirs.

## Инъекция вместо завязки на конфиг

Кит НЕ читает env/config. Каталоги передаются явным `Paths`; git-учётка —
инъектируемым `credential_resolver` (callable `url -> url`). Потребитель (CLI)
читает env-токен и собирает резолвер:

```python
from skillkit import SkillStore, Paths, get_target

paths = Paths.default()  # или из ClientConfig

def resolver(url: str) -> str:
    token = os.environ.get("SKILLS_HUB_GIT_TOKEN")
    if token and url.startswith("https://") and "@" not in url:
        return url.replace("https://", f"https://oauth2:{token}@", 1)
    return url

store = SkillStore(get_target(None), paths.store_dir, credential_resolver=resolver)
```

## Канон «супер-навыка» (навык + CLI + онбординг)

**Супер-навык** — навык, который несёт собственный CLI-инструмент. Канон нужен,
чтобы ОДИН и тот же навык одинаково ставился тремя путями: локальным
install-скриптом, `skillery install` из хаба и `skillery install --path/--from-git`.

### Структура репозитория

```
<repo>/
  pyproject.toml            # пакет CLI-инструмента (публикуется на PyPI)
  src/<tool>/               # исходники CLI
  install/install.sh|.ps1   # локальный установщик (см. ниже)
  skills/<name>/            # САМ НАВЫК — только это материализуется агенту
    SKILL.md                # инструкция для ИИ-агента
    _skill_meta.toml        # ДЕКЛАРАЦИЯ навыка (источник истины)
    references/  agents/
```

Навык может лежать и в корне репо (`SKILL.md` рядом с `_skill_meta.toml`) — тогда
подпапка не нужна. Если навык в подпапке, хаб хранит её в `Skill.skill_path`, и
манифест версии читается ИМЕННО оттуда (иначе tooling-поля теряются).

### `_skill_meta.toml` — полная декларация

```toml
description = "Atlas - local-first PM портфеля проектов и задач."
version = "0.3.0"
kind = "tooling"                 # prompt | comprehensive | tooling
tags = ["pm", "cli"]

# ВАЖНО (TOML): top-level массивы объявляются ДО заголовков [[...]] —
# иначе tomllib отнесёт ключ ВНУТРЬ таблицы, а не на верхний уровень.
runtime_dependencies = [
    { kind = "pip", spec = "atlas-pm==0.3.0" },   # чем ставится CLI
]

[[cli]]                          # какие команды навык приносит
command_name = "atlas"
entrypoint = "atlas.cli:app"

[onboarding]                     # что делать ПОСЛЕ установки
summary = "Локальный PM портфеля: проекты, задачи, эпики."
next_steps = [
  "atlas setup          # правила в CLAUDE.md/AGENTS.md + SessionStart-хук",
  "atlas task triage    # что в работе / застряло / забыто",
]
docs = "https://github.com/<owner>/<repo>#readme"
```

### Как ставится: две дороги, один источник истины

**Локальный install-скрипт** (`install/install.sh|.ps1`) — для человека «с нуля»:
ставит `uv` (если нет) → `uv tool install <пакет>` → запускает post-setup
инструмента (напр. `atlas setup`). Скрипты держим **ASCII-only**: их тянут через
`irm | iex` / `curl | sh`, и не-ASCII может побиться.

**`skillery install <skill>`** — та же логика, но декларативно:
1. материализует папку навыка агенту (global или project scope);
2. читает `_skill_meta.toml` **из установленного навыка** и ставит
   `runtime_dependencies` + регистрирует `cli[]` (`apply_tooling_artifacts`);
3. печатает `[onboarding]` — «что делать дальше».

Источник истины для tooling — **декларация в самом навыке**, а не то, что доехало
в манифесте бандла хаба: у навыка в подпапке бандл может прийти без `cli`/
`runtime_dependencies`, поэтому инсталлятор до-читывает `_skill_meta.toml`.

### Детект: не навреди стороннему навыку

Установка CLI запускается **только по явной декларации** (`runtime_dependencies` /
`cli` в `_skill_meta.toml`). Эвристики вида «рядом лежит `pyproject.toml` — значит
надо поставить пакет» ЗАПРЕЩЕНЫ: сторонний навык часто живёт в чужом репо, и такая
догадка поставила бы левый пакет. Навык без `_skill_meta.toml` (просто `SKILL.md`)
материализуется как есть — ничего не выполняется и не ставится.

### Онбординг обязателен для супер-навыка

Навык, приносящий CLI, ОБЯЗАН объявить `[onboarding].next_steps`. Инсталлятор
печатает их сразу после установки, чтобы **ИИ-агент довёл настройку сам**, а не
оставлял пользователя с установленным, но ненастроенным инструментом. Контракт
вывода: в text-режиме — человекочитаемый список, в `--json` — структурные поля
(`next_steps`) в stderr, чтобы не засорять stdout с основным payload'ом.

## Зависимости

`tomli-w`, `pathspec`, `platformdirs`. requires-python `>=3.11`. MIT.
