s-skillkit 0.4.0__tar.gz → 0.4.1__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.4.0 → s_skillkit-0.4.1}/CHANGELOG.md +22 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/PKG-INFO +1 -1
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/pyproject.toml +1 -1
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/targets/__init__.py +7 -0
- s_skillkit-0.4.1/tests/test_targets_public_names.py +83 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/.gitignore +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/.gitlab-ci.yml +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/AGENTS.md +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/LICENSE +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/README.md +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/docs/adr/0000-template.md +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/docs/adr/0001-kanon-navyka-zhivet-v-skillkit.md +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/docs/adr/README.md +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/__init__.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/binary_installer.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/bootstrap.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/canon.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/collections.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/config.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/deps_installer.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/errors.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/filter.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/hook_register.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/installer/__init__.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/installer/differ.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/installer/fetchers.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/installer/fsops.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/installer/meta.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/installer/models.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/installer/ownership.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/linker.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/manifest.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/mcp_register.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/path_store.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/paths.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/pkg_manager.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/plugin_register.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/proc.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/project.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/targets/antigravity.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/targets/base.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/targets/claude_code.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/targets/codex.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/targets/detect.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/targets/opencode.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/telemetry.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/tooling.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/skillkit/uv_bootstrap.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/conftest.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_agent_skill_layouts_2026_07_21.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_artifact_ownership_2272.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_binary_installer.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_bootstrap.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_canon.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_claude_frontmatter.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_collections.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_config.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_deps_binary.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_deps_installer.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_destructive_ownership_gate_2026_07_21.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_errors.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_filter.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_git_install.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_hooks.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_installer.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_legacy_paths_no_orphans_2026_07_21.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_manifest.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_mcp_register.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_no_raw_subprocess.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_packaging_lock.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_path_store.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_paths.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_pkg_manager.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_pkgsrc_not_in_agent_zone.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_plugin_register.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_proc.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_project.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_remove_ownership_guard_2026_07_21.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_stub_guard.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_targets.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_telemetry_adapter.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_tooling.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_transactional_install.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_traversal_guard.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_typed_fields.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_uv_bootstrap.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_w268_subdir_skill.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/tests/test_w272_empty_manifest_guard.py +0 -0
- {s_skillkit-0.4.0 → s_skillkit-0.4.1}/uv.lock +0 -0
|
@@ -6,6 +6,28 @@
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.4.1] — 2026-09-01
|
|
10
|
+
|
|
11
|
+
### Fixed — `IAgentTarget` снова экспортируется из `skillkit.targets` (#2542)
|
|
12
|
+
|
|
13
|
+
Выпуск 0.4.0 уронил `skillery login` у пользователя с чистой установкой:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
from skillkit.targets import (
|
|
17
|
+
ImportError: cannot import name 'IAgentTarget' from 'skillkit.targets'
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Протокол переименовали `IAgentTarget` → `AgentTarget` (lib#667) и положили
|
|
21
|
+
совместимое имя в `skillkit/targets/base.py`. Этого оказалось мало: чужой код
|
|
22
|
+
зовёт имя **из пакета**, а не из модуля `base`. Теперь алиас реэкспортируется
|
|
23
|
+
пакетом и объявлен в его `__all__`.
|
|
24
|
+
|
|
25
|
+
Почему не поймали заранее: у разработчика в окружении стоял кит 0.3.7 (там ещё
|
|
26
|
+
старое имя и нет нового), а `s-skillkit` тянется с верхней границей открытой —
|
|
27
|
+
пользователь получил 0.4.0 и упал. Сторож `tests/test_targets_public_names.py`
|
|
28
|
+
теперь проверяет ровно тот импорт, на котором падал клиент.
|
|
29
|
+
|
|
30
|
+
|
|
9
31
|
### Added — канон навыка переехал в кит: `skillkit.canon` (#162)
|
|
10
32
|
|
|
11
33
|
- **Один источник правды вместо трёх.** Форма навыка (раскладка `src/<пакет>/` +
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: s-skillkit
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.1
|
|
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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "s-skillkit"
|
|
3
|
-
version = "0.4.
|
|
3
|
+
version = "0.4.1"
|
|
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" }
|
|
@@ -8,6 +8,12 @@ from skillkit.targets.base import (
|
|
|
8
8
|
BaseAgentTarget,
|
|
9
9
|
HookConfigDescriptor,
|
|
10
10
|
AgentTarget,
|
|
11
|
+
#: Совместимое имя (lib#667, skl#2542). Протокол переименован из
|
|
12
|
+
#: `IAgentTarget`, но чужой код продолжает звать старое имя — и зовёт его
|
|
13
|
+
#: ИЗ ПАКЕТА, а не из `base`. Алиас в `base` без этой строки никого не спас:
|
|
14
|
+
#: 01.09.2026 выпуск 0.4.0 уронил `skillery login` у живого пользователя
|
|
15
|
+
#: с `cannot import name 'IAgentTarget' from 'skillkit.targets'`.
|
|
16
|
+
IAgentTarget,
|
|
11
17
|
McpConfigDescriptor,
|
|
12
18
|
)
|
|
13
19
|
from skillkit.targets.claude_code import ClaudeCodeTarget
|
|
@@ -53,6 +59,7 @@ __all__ = [
|
|
|
53
59
|
"CodexTarget",
|
|
54
60
|
"HookConfigDescriptor",
|
|
55
61
|
"AgentTarget",
|
|
62
|
+
"IAgentTarget",
|
|
56
63
|
"McpConfigDescriptor",
|
|
57
64
|
"OpenCodeTarget",
|
|
58
65
|
"agent_skill_dir",
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""Пакет `skillkit.targets` отдаёт оба имени протокола (skl#2542).
|
|
2
|
+
|
|
3
|
+
Прецедент 01.09.2026: протокол переименовали `IAgentTarget` → `AgentTarget`
|
|
4
|
+
(lib#667) и положили алиас в `skillkit/targets/base.py`. Этого оказалось мало —
|
|
5
|
+
чужой код зовёт имя ИЗ ПАКЕТА:
|
|
6
|
+
|
|
7
|
+
from skillkit.targets import IAgentTarget
|
|
8
|
+
|
|
9
|
+
Выпуск 0.4.0 уехал на PyPI и уронил `skillery login` у живого пользователя:
|
|
10
|
+
`ImportError: cannot import name 'IAgentTarget' from 'skillkit.targets'`.
|
|
11
|
+
|
|
12
|
+
Урок, ради которого написан файл: алиас обязан стоять там, ГДЕ ЕГО ИЩУТ. Тесты
|
|
13
|
+
кита импортировали из `base` — и потому молчали, пока клиент падал.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_оба_имени_есть_в_пакете():
|
|
20
|
+
"""Ровно тот импорт, на котором упал пользователь."""
|
|
21
|
+
from skillkit.targets import AgentTarget, IAgentTarget # noqa: F401
|
|
22
|
+
|
|
23
|
+
assert IAgentTarget is AgentTarget, "алиас обязан быть ТЕМ ЖЕ объектом"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_оба_имени_есть_в_модуле_base():
|
|
27
|
+
"""Старый путь импорта тоже жив: им пользуется код, писавшийся до пакета."""
|
|
28
|
+
from skillkit.targets.base import AgentTarget, IAgentTarget
|
|
29
|
+
|
|
30
|
+
assert IAgentTarget is AgentTarget
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_имена_объявлены_в_all():
|
|
34
|
+
"""`__all__` — обещание пакета. Имя, которого там нет, легко потерять при
|
|
35
|
+
следующей уборке экспорта: ровно так оно и потерялось."""
|
|
36
|
+
import skillkit.targets as пакет
|
|
37
|
+
|
|
38
|
+
assert "AgentTarget" in пакет.__all__
|
|
39
|
+
assert "IAgentTarget" in пакет.__all__
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_алиас_виден_снаружи_как_атрибут():
|
|
43
|
+
"""`from ... import` и `getattr` — разные пути; проверяем оба, потому что
|
|
44
|
+
потребители пользуются и тем, и другим."""
|
|
45
|
+
import skillkit.targets as пакет
|
|
46
|
+
|
|
47
|
+
assert getattr(пакет, "IAgentTarget", None) is пакет.AgentTarget
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_алиас_остаётся_протоколом_а_не_заглушкой():
|
|
51
|
+
"""Имя должно оставаться РАБОЧИМ протоколом, а не просто существовать.
|
|
52
|
+
|
|
53
|
+
Иначе импорт легко «починить» строкой-пустышкой и перенести беду на шаг
|
|
54
|
+
дальше от места правки — туда, где именем аннотируют или наследуются.
|
|
55
|
+
|
|
56
|
+
`isinstance` здесь НЕ проверяется намеренно: протокол объявлен без
|
|
57
|
+
`@runtime_checkable`, он для аннотаций, а не для проверок во время работы.
|
|
58
|
+
Первая версия этого теста звала isinstance и упала — ошибка была в ожидании,
|
|
59
|
+
а не в ките.
|
|
60
|
+
"""
|
|
61
|
+
from typing import get_protocol_members, is_protocol
|
|
62
|
+
|
|
63
|
+
from skillkit.targets import IAgentTarget
|
|
64
|
+
|
|
65
|
+
assert is_protocol(IAgentTarget), "имя перестало быть протоколом"
|
|
66
|
+
члены = get_protocol_members(IAgentTarget)
|
|
67
|
+
assert "base_dir" in члены, f"протокол потерял ключевой метод, осталось: {sorted(члены)}"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_настоящая_цель_подходит_под_протокол():
|
|
71
|
+
"""Проверка формы БЕЗ isinstance: у живой цели есть всё, что обещает контракт.
|
|
72
|
+
|
|
73
|
+
Так ловится расхождение протокола и реализации, ради которого протокол и
|
|
74
|
+
заводился, — но без требования делать его runtime_checkable.
|
|
75
|
+
"""
|
|
76
|
+
from typing import get_protocol_members
|
|
77
|
+
|
|
78
|
+
from skillkit.targets import IAgentTarget, get_target
|
|
79
|
+
|
|
80
|
+
цель = get_target("claude_code")
|
|
81
|
+
нет = [и for и in get_protocol_members(IAgentTarget) if not hasattr(цель, и)]
|
|
82
|
+
|
|
83
|
+
assert not нет, f"цель claude_code не выполняет контракт: не хватает {нет}"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|