s-skillkit 0.5.0__tar.gz → 0.6.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/.gitignore +5 -0
- s_skillkit-0.6.2/.gitlab-ci.yml +138 -0
- s_skillkit-0.5.0/.gitlab-ci.yml → s_skillkit-0.6.2/.gitlab-ci.yml.bak +27 -0
- s_skillkit-0.6.2/.importlinter +70 -0
- s_skillkit-0.5.0/.importlinter → s_skillkit-0.6.2/.importlinter.bak +3 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/CHANGELOG.md +53 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/PKG-INFO +5 -3
- s_skillkit-0.6.2/docs/agent-projections.md +76 -0
- s_skillkit-0.6.2/docs/safe-updates.md +41 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/pyproject.toml +20 -3
- s_skillkit-0.6.2/scripts/cycles_gate.py +397 -0
- s_skillkit-0.6.2/scripts/cycles_gate.py.bak +397 -0
- s_skillkit-0.6.2/scripts/lock_gate.py +228 -0
- s_skillkit-0.6.2/scripts/pins_gate.py +268 -0
- s_skillkit-0.6.2/scripts/pins_gate.py.bak +269 -0
- s_skillkit-0.6.2/scripts/release_guard.py +275 -0
- s_skillkit-0.6.2/scripts/source_guard.py +104 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/__init__.py +4 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/installer/__init__.py +189 -20
- s_skillkit-0.6.2/skillkit/installer/differ.py +168 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/installer/fetchers.py +6 -1
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/installer/models.py +7 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/installer/ownership.py +35 -0
- s_skillkit-0.6.2/skillkit/installer/projections.py +338 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/mcp_register.py +4 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/targets/__init__.py +2 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/targets/antigravity.py +0 -22
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/targets/base.py +0 -55
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/targets/claude_code.py +15 -29
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/targets/codex.py +14 -2
- s_skillkit-0.6.2/skillkit/targets/deepseek.py +18 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/targets/detect.py +3 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/conftest.py +7 -0
- s_skillkit-0.6.2/tests/test_agent_projections.py +424 -0
- s_skillkit-0.6.2/tests/test_deepseek_target.py +19 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_destructive_ownership_gate_2026_07_21.py +43 -3
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_git_install.py +2 -1
- s_skillkit-0.6.2/tests/test_git_payload_bytes.py +49 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_installer.py +7 -3
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_legacy_paths_no_orphans_2026_07_21.py +37 -7
- s_skillkit-0.6.2/tests/test_mcp_disabled_preference.py +40 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_remove_ownership_guard_2026_07_21.py +10 -2
- s_skillkit-0.6.2/tests/test_safe_update_gate.py +304 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_targets.py +2 -1
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_w272_empty_manifest_guard.py +2 -1
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/uv.lock +16 -3
- s_skillkit-0.5.0/skillkit/installer/differ.py +0 -44
- s_skillkit-0.5.0/skillkit/plugin_register.py +0 -260
- s_skillkit-0.5.0/tests/test_plugin_register.py +0 -163
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/.skillgateignore +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/AGENTS.md +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/LICENSE +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/README.md +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/docs/adr/0000-template.md +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/docs/adr/0001-kanon-navyka-zhivet-v-skillkit.md +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/docs/adr/README.md +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/scripts/check_dist_leaks.py +0 -0
- /s_skillkit-0.5.0/scripts/release_guard.py → /s_skillkit-0.6.2/scripts/release_guard.py.bak +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/binary_installer.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/bootstrap.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/canon.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/collections.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/config.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/deps_installer.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/errors.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/filter.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/hook_register.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/installer/fsops.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/installer/meta.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/installer_cli/__init__.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/installer_cli/_branding.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/installer_cli/_detached.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/installer_cli/config.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/installer_cli/direct.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/installer_cli/doctor.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/installer_cli/install_scripts.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/installer_cli/publish.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/installer_cli/self_update.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/installer_cli/tokens.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/linker.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/manifest.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/path_store.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/paths.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/pkg_manager.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/proc.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/project.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/targets/opencode.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/telemetry.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/tooling.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/skillkit/uv_bootstrap.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_agent_skill_layouts_2026_07_21.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_artifact_ownership_2272.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_binary_installer.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_bootstrap.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_canon.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_claude_frontmatter.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_collections.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_config.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_deps_binary.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_deps_installer.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_errors.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_filter.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_hooks.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_installer_cli_direct.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_installer_cli_doctor.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_installer_cli_publish.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_installer_cli_smoke.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_installer_cli_tokens.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_manifest.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_mcp_register.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_no_raw_subprocess.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_packaging_lock.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_path_store.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_paths.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_pkg_manager.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_pkgsrc_not_in_agent_zone.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_proc.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_project.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_stub_guard.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_targets_public_names.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_telemetry_adapter.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_tooling.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_transactional_install.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_traversal_guard.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_typed_fields.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_uv_bootstrap.py +0 -0
- {s_skillkit-0.5.0 → s_skillkit-0.6.2}/tests/test_w268_subdir_skill.py +0 -0
|
@@ -48,3 +48,8 @@ NUL
|
|
|
48
48
|
|
|
49
49
|
# git worktrees (агентские)
|
|
50
50
|
.worktrees/
|
|
51
|
+
|
|
52
|
+
# Отметка локального прогона гейта 3 — свидетельство «на ЭТОЙ машине
|
|
53
|
+
# потребители были зелёные». В git ей не место: там она означала бы, что
|
|
54
|
+
# прогон был у всех, а он локальный по построению (раннер не видит соседей).
|
|
55
|
+
.kits-gate3.json
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# СГЕНЕРИРОВАНО из devcontour/tools/kits/template/gitlab-ci.yml — не правь здесь.
|
|
2
|
+
# Правка идёт в шаблон, затем `python tools/kits/apply_template.py <кит>`.
|
|
3
|
+
# Сверка: `python tools/kits/apply_template.py <кит> --check` (код 1 при расхождении).
|
|
4
|
+
#
|
|
5
|
+
# Автопубликация на PyPI по семвер-тегу vX.Y.Z через Trusted Publishing (OIDC).
|
|
6
|
+
# Три стадии, и это не украшение: версии на PyPI неизменяемы, и шесть китов
|
|
7
|
+
# уезжали туда без единого теста (browserkit 0.0.18, netkit ≤0.0.12 — оба в бою).
|
|
8
|
+
# gates — тесты, линтер, слои, doctor: на каждый push и на тег
|
|
9
|
+
# release-guard — тег == версия во всех местах (pyproject/uv.lock/CHANGELOG)
|
|
10
|
+
# publish-pypi — только после двух первых; версия, уже лежащая на PyPI, не
|
|
11
|
+
# публикуется повторно (тег «догоняет» выпуск, а не роняет CI);
|
|
12
|
+
# после сборки и до обмена токена — check_dist_leaks.py
|
|
13
|
+
# (личные данные разработчика в собранном дистрибутиве)
|
|
14
|
+
# Токены НИГДЕ не хранятся: GitLab выдаёт короткоживущий OIDC-токен (PYPI_ID_TOKEN),
|
|
15
|
+
# он одноразово обменивается на PyPI API-токен. Издатель настраивается на pypi.org:
|
|
16
|
+
# Manage -> Publishing -> GitLab (namespace=S-kits, project=skillkit,
|
|
17
|
+
# top-level pipeline file=.gitlab-ci.yml, environment — пусто).
|
|
18
|
+
# Дистрибутив на PyPI: s-skillkit.
|
|
19
|
+
stages: [test, guard, publish]
|
|
20
|
+
|
|
21
|
+
.uv:
|
|
22
|
+
image: ghcr.io/astral-sh/uv:python3.12-bookworm
|
|
23
|
+
|
|
24
|
+
gates:
|
|
25
|
+
extends: .uv
|
|
26
|
+
stage: test
|
|
27
|
+
rules:
|
|
28
|
+
- if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/
|
|
29
|
+
- if: $CI_PIPELINE_SOURCE == "push"
|
|
30
|
+
script:
|
|
31
|
+
# dev-зависимости у китов объявлены двумя способами — extra `dev` и группа
|
|
32
|
+
# `[dependency-groups] dev`; --all-extras покрывает первое, группу добавляем
|
|
33
|
+
# только если она объявлена, иначе uv падает «Group dev is not defined».
|
|
34
|
+
# `--locked`: замок обязан соответствовать манифесту, а не тихо
|
|
35
|
+
# пересобираться под него. Без флага правка пина без `uv lock` доезжает до
|
|
36
|
+
# прода незамеченной — CI зелёный, а у потребителя приезжает старая версия.
|
|
37
|
+
- uv sync --locked --all-extras $(python3 -c "import tomllib;print('--group dev' if 'dev' in tomllib.load(open('pyproject.toml','rb')).get('dependency-groups',{}) else '')")
|
|
38
|
+
- uv run pytest tests/ -q
|
|
39
|
+
# Линтер по ВСЕМУ пакету, а не по списку файлов: сужение до «файлов волны 0»
|
|
40
|
+
# держалось девять китов и ни разу не было расширено — сужение приживается,
|
|
41
|
+
# расширение нет.
|
|
42
|
+
- uv run ruff check .
|
|
43
|
+
# Границы слоёв: контракт в .importlinter (ставится тем же шаблоном).
|
|
44
|
+
# — доп. пакеты через --with (пусто у большинства
|
|
45
|
+
# китов; adapterkit — пример кита, которому нужен сосед на прогоне
|
|
46
|
+
# lint-imports, см. apply_template.py --extra-with).
|
|
47
|
+
- uv run --with import-linter lint-imports
|
|
48
|
+
# Гейт 1, часть «согласованность локальных venv» (kitsctl doctor). Скрипт
|
|
49
|
+
# раздаётся шаблоном не всем: в раннере видно ОДИН репозиторий, соседних
|
|
50
|
+
# venv'ов там нет, — поэтому шаг условный и молча пропускается, когда
|
|
51
|
+
# инструмента у кита нет. Настоящее место doctor — локальный прогон перед
|
|
52
|
+
# тегом; см. docs/kits/SAFETY-NET.md, «Известные ограничения».
|
|
53
|
+
- 'if [ -f scripts/kitsctl.py ]; then python3 scripts/kitsctl.py doctor; else echo "kitsctl doctor — инструмента нет в репозитории, пропуск (см. SAFETY-NET.md)"; fi'
|
|
54
|
+
# Гейт 2: у каждой зависимости на кит есть верхняя граница. Значение границы
|
|
55
|
+
# здесь не вычисляется — в раннере соседних репозиториев нет, версий взять
|
|
56
|
+
# неоткуда; проверяется НАЛИЧИЕ. Без этого шага правило остаётся устной
|
|
57
|
+
# дисциплиной, которую забывают: портфель дожил до 248 незакрытых пинов
|
|
58
|
+
# ровно потому, что напоминать о нём было некому (#2800).
|
|
59
|
+
- 'if [ -f scripts/pins_gate.py ]; then python3 scripts/pins_gate.py --self-check --root .; else echo "гейт пинов — инструмента нет в репозитории, пропуск"; fi'
|
|
60
|
+
# Гейт колец: объявленное в `[tool.kit] lazy_deps` и правда грузится лениво.
|
|
61
|
+
# Колец здесь не увидеть (соседей в раннере нет), но видно главное: подъём
|
|
62
|
+
# такого импорта на верхний уровень замыкает кольцо ПРЯМО СЕЙЧАС, а
|
|
63
|
+
# выглядит безобидной уборкой («чего он тут в теле функции») — #2833.
|
|
64
|
+
- 'if [ -f scripts/cycles_gate.py ]; then python3 scripts/cycles_gate.py --self-check --root .; else echo "гейт колец — инструмента нет в репозитории, пропуск"; fi'
|
|
65
|
+
# Гейт замка: объявленная версия кита ДОЕЗЖАЕТ до окружения. `--locked`
|
|
66
|
+
# выше ловит другое — что замок не пересобран под манифест; а если у
|
|
67
|
+
# зависимости переопределён источник (`[tool.uv.sources]` = git/path), то
|
|
68
|
+
# спецификатор версии в разрешении не участвует вовсе, замок считается
|
|
69
|
+
# актуальным, и отставание не видит НИКТО. Так agentskit и issuekit
|
|
70
|
+
# держали clikit 0.1.6 при объявленном >=0.2.1 (#2921).
|
|
71
|
+
- 'if [ -f scripts/lock_gate.py ]; then python3 scripts/lock_gate.py --self-check --root .; else echo "гейт замка — инструмента нет в репозитории, пропуск"; fi'
|
|
72
|
+
|
|
73
|
+
release-guard:
|
|
74
|
+
extends: .uv
|
|
75
|
+
stage: guard
|
|
76
|
+
needs: [gates]
|
|
77
|
+
rules:
|
|
78
|
+
- if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/
|
|
79
|
+
script:
|
|
80
|
+
- python3 scripts/release_guard.py "$CI_COMMIT_TAG"
|
|
81
|
+
|
|
82
|
+
publish-pypi:
|
|
83
|
+
extends: .uv
|
|
84
|
+
stage: publish
|
|
85
|
+
needs: [release-guard]
|
|
86
|
+
rules:
|
|
87
|
+
- if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/
|
|
88
|
+
id_tokens:
|
|
89
|
+
PYPI_ID_TOKEN:
|
|
90
|
+
aud: pypi
|
|
91
|
+
script:
|
|
92
|
+
# КУДА уезжает выпуск, решает признак В САМОМ КИТЕ ([tool.kit] visibility),
|
|
93
|
+
# а не память человека и не отдельный список где-то в стороне. Приватный кит
|
|
94
|
+
# (готовый сервис, реализация чужой площадки) уходит в приватный индекс
|
|
95
|
+
# GitLab по ВСТРОЕННОМУ токену задания — новых секретов заводить не нужно.
|
|
96
|
+
# Умолчание — private: забыть пометить приватный кит дороже, чем забыть
|
|
97
|
+
# пометить открытый, и ошибаться надо в безопасную сторону.
|
|
98
|
+
- |
|
|
99
|
+
VIS=$(python3 -c "import tomllib;print((tomllib.load(open('pyproject.toml','rb')).get('tool') or {}).get('kit',{}).get('visibility','private'))")
|
|
100
|
+
echo "видимость кита: $VIS"
|
|
101
|
+
if [ "$VIS" != "public" ]; then
|
|
102
|
+
uv build
|
|
103
|
+
python3 scripts/check_dist_leaks.py dist/
|
|
104
|
+
uv publish --publish-url "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi" --username gitlab-ci-token --password "$CI_JOB_TOKEN"
|
|
105
|
+
echo "выпуск ушёл в приватный индекс проекта, на публичный PyPI НЕ уезжал"
|
|
106
|
+
exit 0
|
|
107
|
+
fi
|
|
108
|
+
- |
|
|
109
|
+
DIST=$(python3 -c "import tomllib;print(tomllib.load(open('pyproject.toml','rb'))['project']['name'])")
|
|
110
|
+
VER="${CI_COMMIT_TAG#v}"
|
|
111
|
+
if python3 - "$DIST" "$VER" <<'PY'
|
|
112
|
+
import json, sys, urllib.request
|
|
113
|
+
dist, ver = sys.argv[1], sys.argv[2]
|
|
114
|
+
try:
|
|
115
|
+
data = json.load(urllib.request.urlopen(f"https://pypi.org/pypi/{dist}/json"))
|
|
116
|
+
except Exception:
|
|
117
|
+
sys.exit(1)
|
|
118
|
+
sys.exit(0 if ver in data.get("releases", {}) else 1)
|
|
119
|
+
PY
|
|
120
|
+
then echo "версия $VER уже на PyPI — публикацию пропускаем"; exit 0; fi
|
|
121
|
+
- uv build
|
|
122
|
+
# Последний рубеж перед необратимым шагом (версии на PyPI неизменяемы,
|
|
123
|
+
# yank — не удаление). Прецедент — leasekit: реальный email и домашний
|
|
124
|
+
# путь разработчика уехали в публичный индекс из тестовых фикстур. Ловит
|
|
125
|
+
# по классам, не по строкам; см. docs/kits/SAFETY-NET.md.
|
|
126
|
+
- python3 scripts/check_dist_leaks.py dist/
|
|
127
|
+
- |
|
|
128
|
+
export UV_PUBLISH_TOKEN=$(python3 - <<'PY'
|
|
129
|
+
import json, os, urllib.request
|
|
130
|
+
req = urllib.request.Request(
|
|
131
|
+
"https://pypi.org/_/oidc/mint-token",
|
|
132
|
+
data=json.dumps({"token": os.environ["PYPI_ID_TOKEN"]}).encode(),
|
|
133
|
+
headers={"Content-Type": "application/json"},
|
|
134
|
+
)
|
|
135
|
+
print(json.load(urllib.request.urlopen(req))["token"])
|
|
136
|
+
PY
|
|
137
|
+
)
|
|
138
|
+
- uv publish
|
|
@@ -48,6 +48,17 @@ gates:
|
|
|
48
48
|
# инструмента у кита нет. Настоящее место doctor — локальный прогон перед
|
|
49
49
|
# тегом; см. docs/kits/SAFETY-NET.md, «Известные ограничения».
|
|
50
50
|
- 'if [ -f scripts/kitsctl.py ]; then python3 scripts/kitsctl.py doctor; else echo "kitsctl doctor — инструмента нет в репозитории, пропуск (см. SAFETY-NET.md)"; fi'
|
|
51
|
+
# Гейт 2: у каждой зависимости на кит есть верхняя граница. Значение границы
|
|
52
|
+
# здесь не вычисляется — в раннере соседних репозиториев нет, версий взять
|
|
53
|
+
# неоткуда; проверяется НАЛИЧИЕ. Без этого шага правило остаётся устной
|
|
54
|
+
# дисциплиной, которую забывают: портфель дожил до 248 незакрытых пинов
|
|
55
|
+
# ровно потому, что напоминать о нём было некому (#2800).
|
|
56
|
+
- 'if [ -f scripts/pins_gate.py ]; then python3 scripts/pins_gate.py --self-check --root .; else echo "гейт пинов — инструмента нет в репозитории, пропуск"; fi'
|
|
57
|
+
# Гейт колец: объявленное в `[tool.kit] lazy_deps` и правда грузится лениво.
|
|
58
|
+
# Колец здесь не увидеть (соседей в раннере нет), но видно главное: подъём
|
|
59
|
+
# такого импорта на верхний уровень замыкает кольцо ПРЯМО СЕЙЧАС, а
|
|
60
|
+
# выглядит безобидной уборкой («чего он тут в теле функции») — #2833.
|
|
61
|
+
- 'if [ -f scripts/cycles_gate.py ]; then python3 scripts/cycles_gate.py --self-check --root .; else echo "гейт колец — инструмента нет в репозитории, пропуск"; fi'
|
|
51
62
|
|
|
52
63
|
release-guard:
|
|
53
64
|
extends: .uv
|
|
@@ -68,6 +79,22 @@ publish-pypi:
|
|
|
68
79
|
PYPI_ID_TOKEN:
|
|
69
80
|
aud: pypi
|
|
70
81
|
script:
|
|
82
|
+
# КУДА уезжает выпуск, решает признак В САМОМ КИТЕ ([tool.kit] visibility),
|
|
83
|
+
# а не память человека и не отдельный список где-то в стороне. Приватный кит
|
|
84
|
+
# (готовый сервис, реализация чужой площадки) уходит в приватный индекс
|
|
85
|
+
# GitLab по ВСТРОЕННОМУ токену задания — новых секретов заводить не нужно.
|
|
86
|
+
# Умолчание — private: забыть пометить приватный кит дороже, чем забыть
|
|
87
|
+
# пометить открытый, и ошибаться надо в безопасную сторону.
|
|
88
|
+
- |
|
|
89
|
+
VIS=$(python3 -c "import tomllib;print((tomllib.load(open('pyproject.toml','rb')).get('tool') or {}).get('kit',{}).get('visibility','private'))")
|
|
90
|
+
echo "видимость кита: $VIS"
|
|
91
|
+
if [ "$VIS" != "public" ]; then
|
|
92
|
+
uv build
|
|
93
|
+
python3 scripts/check_dist_leaks.py dist/
|
|
94
|
+
uv publish --publish-url "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi" --username gitlab-ci-token --password "$CI_JOB_TOKEN"
|
|
95
|
+
echo "выпуск ушёл в приватный индекс проекта, на публичный PyPI НЕ уезжал"
|
|
96
|
+
exit 0
|
|
97
|
+
fi
|
|
71
98
|
- |
|
|
72
99
|
DIST=$(python3 -c "import tomllib;print(tomllib.load(open('pyproject.toml','rb'))['project']['name'])")
|
|
73
100
|
VER="${CI_COMMIT_TAG#v}"
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# СГЕНЕРИРОВАНО из devcontour/tools/kits/template/importlinter.ini — не правь
|
|
2
|
+
# здесь. Правка идёт в шаблон, затем `python tools/kits/apply_template.py <кит>`.
|
|
3
|
+
#
|
|
4
|
+
# Заготовка контракта слоёв. Запуск: `uv run --with import-linter lint-imports`.
|
|
5
|
+
#
|
|
6
|
+
# Контракт ниже — минимум, который годится ЛЮБОМУ киту и не требует ручной
|
|
7
|
+
# настройки: «skillkit не импортирует те киты экосистемы, которых нет в его
|
|
8
|
+
# объявленных зависимостях». Скрытая зависимость — это кит, который работает у
|
|
9
|
+
# автора (сосед стоит в venv) и разваливается у потребителя на первом импорте;
|
|
10
|
+
# именно так ломались pypi-publish и gemini-chat.
|
|
11
|
+
#
|
|
12
|
+
# Список forbidden_modules подставлен установщиком из реестра китов на момент
|
|
13
|
+
# установки: все известные пакеты китов МИНУС собственный МИНУС объявленные в
|
|
14
|
+
# pyproject зависимости. Появилась новая честная зависимость — объяви её в
|
|
15
|
+
# pyproject и переустанови шаблон, а не правь этот файл.
|
|
16
|
+
#
|
|
17
|
+
# Собственные контракты кита (layers/eager_forbidden/declared_dependencies,
|
|
18
|
+
# как у adapterkit) дописываются НИЖЕ отдельными секциями прямо в этом файле.
|
|
19
|
+
# Установщик сверяет файл целиком, поэтому `--check` после такой правки
|
|
20
|
+
# закономерно покажет расхождение с каноном — это ожидаемо и осознанно
|
|
21
|
+
# игнорируется в отчёте ревью, см. docs/kits/SAFETY-NET.md, «Что делать при
|
|
22
|
+
# расхождении» (п.2).
|
|
23
|
+
|
|
24
|
+
[importlinter]
|
|
25
|
+
# include_external_packages нужен: forbidden_modules перечисляет пакеты,
|
|
26
|
+
# внешние относительно root_packages.
|
|
27
|
+
include_external_packages = True
|
|
28
|
+
root_packages =
|
|
29
|
+
skillkit
|
|
30
|
+
exclude_type_checking_imports = True
|
|
31
|
+
|
|
32
|
+
[importlinter:contract:no-undeclared-kits]
|
|
33
|
+
name = skillkit не импортирует киты, которых нет в его зависимостях
|
|
34
|
+
type = forbidden
|
|
35
|
+
source_modules =
|
|
36
|
+
skillkit
|
|
37
|
+
forbidden_modules =
|
|
38
|
+
accountpoolkit
|
|
39
|
+
adapterkit
|
|
40
|
+
agentskit
|
|
41
|
+
aichatkit
|
|
42
|
+
authkit_client
|
|
43
|
+
authkit_contracts
|
|
44
|
+
authkit_server
|
|
45
|
+
b24kit
|
|
46
|
+
browserkit
|
|
47
|
+
chatkit
|
|
48
|
+
clientkit
|
|
49
|
+
corekit
|
|
50
|
+
emojikit
|
|
51
|
+
gitkit
|
|
52
|
+
installerkit
|
|
53
|
+
iokit
|
|
54
|
+
issuekit
|
|
55
|
+
leasekit
|
|
56
|
+
maxkit
|
|
57
|
+
modelkit
|
|
58
|
+
netkit
|
|
59
|
+
orchestrationkit
|
|
60
|
+
ormkit
|
|
61
|
+
persistkit
|
|
62
|
+
pluginkit
|
|
63
|
+
replicationkit
|
|
64
|
+
servicekit
|
|
65
|
+
sessionkit
|
|
66
|
+
socialkit
|
|
67
|
+
storagekit
|
|
68
|
+
totp_client
|
|
69
|
+
vaultkit
|
|
70
|
+
worker_service
|
|
@@ -4,6 +4,59 @@
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/ru/1.1.0/); версионирование —
|
|
5
5
|
[SemVer](https://semver.org/lang/ru/).
|
|
6
6
|
|
|
7
|
+
## [0.6.2] - 2026-10-14
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Verify published payload bytes before hub updates and again before committing staged content; preserve local edits and refuse unknown baselines even with force.
|
|
12
|
+
- Preserve disabled managed MCP entries when updating their commands.
|
|
13
|
+
- Skip filesystem writes for verified current copy projections.
|
|
14
|
+
- Fetch Git releases without inherited newline conversion so Windows and Linux receive the same published bytes.
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- Native DeepSeek Harness skill installation roots with DSH_HOME support.
|
|
19
|
+
- Managed copy projections for agents running under Windows redirection restrictions, with conflict protection and explicit CODEX_HOME support.
|
|
20
|
+
|
|
21
|
+
## [0.6.1] — 2026-09-09
|
|
22
|
+
|
|
23
|
+
### Исправлено
|
|
24
|
+
|
|
25
|
+
- **`ClaudeCodeTarget.mcp_config()` больше не игнорирует подменённый `root`.**
|
|
26
|
+
Global-путь считался от `Path.home()`, поэтому прогон тестов кита писал
|
|
27
|
+
«чужой» образец `["важные", "данные", "пользователя"]` в НАСТОЯЩИЙ
|
|
28
|
+
`~/.claude.json` разработчика. Claude Code читал массив, превращал его в
|
|
29
|
+
ключи `"0"/"1"/"2"` и дописывал своё — список доверенных проектов пропадал, и
|
|
30
|
+
диалог «Trust this workspace?» возвращался на уже доверенных папках. На живой
|
|
31
|
+
машине это случилось дважды за час. Боевое поведение не меняется: по
|
|
32
|
+
умолчанию `root` = `~/.claude`, его родитель — тот же дом.
|
|
33
|
+
- **Сторож песочницы** (`test_podmenennyi_root_uvodit_vse_puti_v_pesochnitsu`):
|
|
34
|
+
при подменённом `root` ни один путь цели — ни `base_dir`, ни `slug_dir`, ни
|
|
35
|
+
дескрипторы MCP и хуков — не ведёт наружу. Проверяются ВСЕ цели разом:
|
|
36
|
+
соседний `hook_config` правило соблюдал и даже объяснял его в докстринге, так
|
|
37
|
+
что подвела именно помодульная бдительность.
|
|
38
|
+
|
|
39
|
+
## [0.6.0] — 2026-09-09
|
|
40
|
+
|
|
41
|
+
### Удалено (ломающее)
|
|
42
|
+
|
|
43
|
+
- **`skillkit.plugin_register`** — движок «плагин как единица поставки»
|
|
44
|
+
(манифест `plugin.json` + запись в реестр агента). Ноль потребителей во всём
|
|
45
|
+
портфеле, ноль публичной поверхности: нет ни в README, ни в `__all__`, ни в
|
|
46
|
+
`__init__`, ни в entry-points — причём групп entry-points у кита нет вовсе.
|
|
47
|
+
Написан 30.08.2026 одним коммитом и больше не трогался. Нишу занял живой
|
|
48
|
+
`pluginkit` с четырьмя потребителями (gateway, aichatkit, adapterkit, atlas):
|
|
49
|
+
формально он про python-пакеты, а не про `plugin.json` в каталоге агента, —
|
|
50
|
+
но второй незанятый движок в той же нише дороже, чем отсутствие обоих.
|
|
51
|
+
- **`PluginDescriptor` и методы `plugin_config`** в `targets/base.py`,
|
|
52
|
+
`targets/claude_code.py`, `targets/antigravity.py` — хвост, который держался
|
|
53
|
+
только на удалённом модуле. Убран тем же комплектом: иначе в ките осталась бы
|
|
54
|
+
раскладка плагинов, которую некому прочитать.
|
|
55
|
+
|
|
56
|
+
Ломающее для тех, кто аннотировал `PluginDescriptor` или переопределял
|
|
57
|
+
`plugin_config` в своей цели. Таких в портфеле нет (проверено грепом), но
|
|
58
|
+
поверхность целей публична, поэтому минор, а не патч (Atlas #2835).
|
|
59
|
+
|
|
7
60
|
## [0.5.0] — 2026-09-08
|
|
8
61
|
|
|
9
62
|
### Добавлено
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: s-skillkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.2
|
|
4
4
|
Summary: Локальное ядро управления навыками: install из git/локальной папки, junction/symlink-линковка в scope агента, .skillignore-фильтр, build manifest, project-манифест. 0 завязок на сеть/auth — stdlib + tomli-w + pathspec + platformdirs.
|
|
5
5
|
Author: Dmitry
|
|
6
6
|
License: MIT
|
|
@@ -8,11 +8,13 @@ License-File: LICENSE
|
|
|
8
8
|
Requires-Python: >=3.11
|
|
9
9
|
Requires-Dist: pathspec>=0.12.0
|
|
10
10
|
Requires-Dist: platformdirs>=4.0
|
|
11
|
-
Requires-Dist: s-telemetrykit
|
|
11
|
+
Requires-Dist: s-telemetrykit<0.4,>=0.2.0
|
|
12
12
|
Requires-Dist: tomli-w>=1.0
|
|
13
|
+
Requires-Dist: typer>=0.15
|
|
13
14
|
Provides-Extra: installer-cli
|
|
14
|
-
Requires-Dist: s-clikit
|
|
15
|
+
Requires-Dist: s-clikit<0.3,>=0.1.6; extra == 'installer-cli'
|
|
15
16
|
Requires-Dist: s-librarykit<0.9; extra == 'installer-cli'
|
|
17
|
+
Requires-Dist: s-skillgate<0.2,>=0.1.0; extra == 'installer-cli'
|
|
16
18
|
Description-Content-Type: text/markdown
|
|
17
19
|
|
|
18
20
|
# s-skillkit
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Readable skill projections
|
|
2
|
+
|
|
3
|
+
`SkillStore(target, store_dir, projection="auto")` accepts `auto`, `link`, and
|
|
4
|
+
`copy`. An explicit `InstallRequest.projection` overrides the installer default;
|
|
5
|
+
`None` inherits it. The legacy `install`, `install_from_path`,
|
|
6
|
+
`install_from_snapshot`, and `link_existing` keyword APIs accept the same override.
|
|
7
|
+
`materialize` still writes content to the store only.
|
|
8
|
+
|
|
9
|
+
`auto` preserves the existing link-first behavior for new installations. A skill
|
|
10
|
+
already installed as a managed copy remains a copy on later automatic updates.
|
|
11
|
+
`link` reports link creation failures without silently falling back to a copy.
|
|
12
|
+
`copy` creates a regular skill directory readable by processes that cannot follow
|
|
13
|
+
Windows junctions, including SSH descendants enforcing RedirectionGuard. It does
|
|
14
|
+
not change any Windows security policy.
|
|
15
|
+
|
|
16
|
+
Each copy has independent metadata under `_skill_meta.json`:
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"projection": {
|
|
21
|
+
"kind": "copy",
|
|
22
|
+
"store_dir": "absolute/source/store/skill",
|
|
23
|
+
"source_revision": "source-commit",
|
|
24
|
+
"preserved_paths": ["_local/", "browser_profiles/"],
|
|
25
|
+
"baseline": {
|
|
26
|
+
"files": [{"path": "SKILL.md", "sha256": "sha256-of-original-payload"}],
|
|
27
|
+
"sha256": "sha256-of-canonical-files-index"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The baseline comes from a successful fresh materialization or a verified previous
|
|
34
|
+
release manifest. Existing files without a verifiable baseline are never adopted
|
|
35
|
+
as original content. Copy and source payload checks run before source updates;
|
|
36
|
+
the scope is checked again before the transactional swap. Local edits, missing
|
|
37
|
+
files, untracked payload, malformed baselines, runtime links, and unmanaged scope
|
|
38
|
+
entries cause an explicit refusal. `force` does not bypass these checks.
|
|
39
|
+
|
|
40
|
+
Copy payload excludes `.git`, `.pkgsrc`, `.venv`, bytecode caches, `.env`, and
|
|
41
|
+
declared preserved state. CLI dependencies remain in their independently managed
|
|
42
|
+
runtime. Existing ordinary preserved state in the scope is kept locally; it is
|
|
43
|
+
not copied from another source installation or included in a payload fingerprint.
|
|
44
|
+
Previously declared preserved paths remain protected when a later release omits
|
|
45
|
+
them. Runtime links are rejected instead of following their targets.
|
|
46
|
+
|
|
47
|
+
The new copy is staged beside the provider directory and activated through
|
|
48
|
+
`PathGuard.swap_in_scope`. Only the exact owned scope entry is renamed or removed;
|
|
49
|
+
link targets are retained. A failed activation restores the previous entry. If a
|
|
50
|
+
foreign entry appears during rollback, both the foreign entry and the original
|
|
51
|
+
backup are retained and the error identifies the recovery blocker. Store
|
|
52
|
+
materialization and scope activation are separate transactions: a failed scope
|
|
53
|
+
activation can leave the verified new source version in the store while the old
|
|
54
|
+
copy remains readable. Retrying updates the scope from that verified source.
|
|
55
|
+
|
|
56
|
+
Direct `link_existing` uses the same checks. `migrate_scope` retains readable
|
|
57
|
+
managed copy projections and reports them in `skipped_projection`. Legacy copy
|
|
58
|
+
migration requires ownership metadata and a prior release manifest whose raw
|
|
59
|
+
hashes match every payload file. Unknown bytes or namesake metadata are left
|
|
60
|
+
unchanged. Recovery requires a verified pinned previous hub manifest/bundle or an
|
|
61
|
+
explicit manual review and export; `force` cannot establish a baseline.
|
|
62
|
+
|
|
63
|
+
## Codex runtime home
|
|
64
|
+
|
|
65
|
+
With an explicit absolute `CODEX_HOME`, the Codex target places global skills at
|
|
66
|
+
`$CODEX_HOME/skills` and global MCP settings at `$CODEX_HOME/config.toml`. It does
|
|
67
|
+
not claim unrelated sibling legacy directories. An injected `root=` continues to
|
|
68
|
+
override the environment for isolated callers and tests, and `config_root=` can
|
|
69
|
+
explicitly override the configuration path.
|
|
70
|
+
|
|
71
|
+
When `CODEX_HOME` is unset, the existing global `.agents/skills`, `.codex` config,
|
|
72
|
+
and managed legacy lookup remain unchanged. Project skills still use
|
|
73
|
+
`<project>/.agents/skills`, and project MCP configuration still uses
|
|
74
|
+
`<project>/.codex/config.toml`. Paths must be absolute in the current operating
|
|
75
|
+
system's syntax; a Windows drive path is not silently treated as a Linux relative
|
|
76
|
+
directory.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Safe updates of hub-managed skills
|
|
2
|
+
|
|
3
|
+
Existing hub payload is checked against the SHA-256 inventory of its installed
|
|
4
|
+
published version before materialization and again before the staged version
|
|
5
|
+
replaces the store. The same check protects a managed copy in the agent scope.
|
|
6
|
+
An unchanged symlink or Windows junction to the store is a projection and is
|
|
7
|
+
checked through the store instead.
|
|
8
|
+
|
|
9
|
+
Modified or deleted files, file type changes, payload links, and untracked
|
|
10
|
+
payload that could be overwritten or filtered out stop the complete update.
|
|
11
|
+
An update also stops when its old inventory is missing, malformed, or cannot
|
|
12
|
+
be read. A new manifest cannot hide edited payload by declaring it preserved.
|
|
13
|
+
The `force` option does not bypass these checks.
|
|
14
|
+
|
|
15
|
+
For `SkillStore.install(...)`, the result has `skipped=True`, one of
|
|
16
|
+
`skip_reason="local-modifications"` or `"unverifiable-baseline"`, and safe
|
|
17
|
+
relative `conflict_paths`. A path of `.` denotes an unavailable baseline or
|
|
18
|
+
conflict at the installation root. No version, metadata, scope link, or payload
|
|
19
|
+
is replaced. Consumers must skip tooling and must report the reason to the
|
|
20
|
+
user. `SkillStore.materialize(...)` uses the same guard and returns a dictionary
|
|
21
|
+
with `skipped`, `skip_reason`, and `conflict_paths` on refusal; consumers should
|
|
22
|
+
check this before reading the normal success fields.
|
|
23
|
+
|
|
24
|
+
The comparison is byte-exact. LF/CRLF conversion is a modification and is not
|
|
25
|
+
silently normalized. Existing Git-based Windows installs with line-ending
|
|
26
|
+
conversion need a separately verified recovery process. A trusted old baseline
|
|
27
|
+
must come from the immutable installed version or its verified snapshot, never
|
|
28
|
+
from hashing the currently edited installation. New local edits should be
|
|
29
|
+
committed and released from an author's source repository rather than made in
|
|
30
|
+
the managed store.
|
|
31
|
+
|
|
32
|
+
Declared runtime state and installer/runtime artifacts (`.git`, `.pkgsrc`,
|
|
33
|
+
`.venv`, `__pycache__`, and `_skill_meta.json`) are outside this payload hash
|
|
34
|
+
check. Their existing lifecycle is unchanged. A malformed or missing baseline
|
|
35
|
+
is not repaired automatically. Generated stub-over-stub updates keep their
|
|
36
|
+
existing behavior because they have no published payload inventory.
|
|
37
|
+
|
|
38
|
+
This guard checks state again immediately before swapping staged content. It
|
|
39
|
+
does not provide a filesystem transaction against an uncoordinated writer
|
|
40
|
+
changing files during the final check/swap or against other concurrent
|
|
41
|
+
installers; callers still need to serialize installation work.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "s-skillkit"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.6.2"
|
|
4
4
|
description = "Локальное ядро управления навыками: install из git/локальной папки, junction/symlink-линковка в scope агента, .skillignore-фильтр, build manifest, project-манифест. 0 завязок на сеть/auth — stdlib + tomli-w + pathspec + platformdirs."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "MIT" }
|
|
@@ -25,10 +25,15 @@ authors = [{ name = "Dmitry" }]
|
|
|
25
25
|
# ответственность ИМЕННО этого кита. Зависимость лёгкая: telemetrykit
|
|
26
26
|
# stdlib-only и своих зависимостей не имеет.
|
|
27
27
|
dependencies = [
|
|
28
|
+
# Объявлено ПРЯМО (гейт зависимостей, #2834): кит импортирует пакет сам,
|
|
29
|
+
# а приезжал он транзитивно. Прямой импорт на чужой зависимости живёт до
|
|
30
|
+
# дня, когда сосед сменит свой набор, — и падает ModuleNotFoundError в
|
|
31
|
+
# бою при зелёной сборке.
|
|
32
|
+
"typer>=0.15",
|
|
28
33
|
"tomli-w>=1.0",
|
|
29
34
|
"pathspec>=0.12.0",
|
|
30
35
|
"platformdirs>=4.0",
|
|
31
|
-
"s-telemetrykit>=0.2.0",
|
|
36
|
+
"s-telemetrykit>=0.2.0,<0.4",
|
|
32
37
|
]
|
|
33
38
|
|
|
34
39
|
#: installer_cli (волна 5, #2710) — бывший псевдокит installerkit, консолидирован
|
|
@@ -40,7 +45,11 @@ dependencies = [
|
|
|
40
45
|
#: `pip install s-skillkit[installer_cli]`. Модуль и не импортируется eagerly
|
|
41
46
|
#: из ``skillkit/__init__.py`` (тот же приём, что у ``skillkit.telemetry``).
|
|
42
47
|
[project.optional-dependencies]
|
|
43
|
-
|
|
48
|
+
# s-skillgate объявлен здесь же: `installer_cli/publish.py` зовёт
|
|
49
|
+
# `from skillgate import run_gate` — секрет-скан перед публикацией.
|
|
50
|
+
# Импорт ленивый, но без пакета публикация падает ровно в момент,
|
|
51
|
+
# когда её и запускают (гейт #2834).
|
|
52
|
+
installer_cli = ["s-clikit>=0.1.6,<0.3", "s-librarykit<0.9", "s-skillgate>=0.1.0,<0.2"]
|
|
44
53
|
|
|
45
54
|
#: Инструменты прогона объявлены ГРУППОЙ, а не extra, намеренно (skl#2545).
|
|
46
55
|
#: extra ставится только по явному `--extra dev`; забыли флаг — pytest в
|
|
@@ -94,3 +103,11 @@ ignore = ["RUF001", "RUF002", "RUF003", "RUF022", "RUF059"]
|
|
|
94
103
|
asyncio_mode = "auto"
|
|
95
104
|
testpaths = ["tests"]
|
|
96
105
|
pythonpath = ["."]
|
|
106
|
+
|
|
107
|
+
[tool.kit]
|
|
108
|
+
# ОТКРЫТЫЙ. Это вспомогательная механика: из неё собирают, но сама по себе она
|
|
109
|
+
# ничьей площадки и ничьего продукта не раскрывает.
|
|
110
|
+
#
|
|
111
|
+
# Признак читает шаг публикации: открытый кит уезжает на PyPI, приватный — в
|
|
112
|
+
# приватный индекс GitLab. Граница держится машиной, а не памятью.
|
|
113
|
+
visibility = "public"
|