cnife-skill-manager 0.1.0__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.
Files changed (38) hide show
  1. cnife_skill_manager-0.1.0/.agents/skills/e2e-happy-path/SKILL.md +68 -0
  2. cnife_skill_manager-0.1.0/.agents/skills/e2e-happy-path/scripts/e2e-setup-sync.sh +57 -0
  3. cnife_skill_manager-0.1.0/.github/workflows/ci.yml +36 -0
  4. cnife_skill_manager-0.1.0/.github/workflows/publish.yml +83 -0
  5. cnife_skill_manager-0.1.0/.gitignore +18 -0
  6. cnife_skill_manager-0.1.0/.pre-commit-config.yaml +21 -0
  7. cnife_skill_manager-0.1.0/AGENTS.md +13 -0
  8. cnife_skill_manager-0.1.0/CONTEXT.md +45 -0
  9. cnife_skill_manager-0.1.0/LICENSE +21 -0
  10. cnife_skill_manager-0.1.0/PKG-INFO +107 -0
  11. cnife_skill_manager-0.1.0/README.md +82 -0
  12. cnife_skill_manager-0.1.0/docs/adr/0001-symlink-not-copy.md +3 -0
  13. cnife_skill_manager-0.1.0/docs/adr/0002-sources-derived-from-declarations.md +3 -0
  14. cnife_skill_manager-0.1.0/docs/adr/0003-explicit-skill-name.md +3 -0
  15. cnife_skill_manager-0.1.0/docs/adr/0004-sync-add-only.md +3 -0
  16. cnife_skill_manager-0.1.0/docs/adr/0005-version-from-git-tags.md +3 -0
  17. cnife_skill_manager-0.1.0/docs/adr/0006-pypi-trusted-publishing.md +3 -0
  18. cnife_skill_manager-0.1.0/docs/adr/0007-pypi-name-cnife-skill-manager.md +3 -0
  19. cnife_skill_manager-0.1.0/docs/agents/domain.md +37 -0
  20. cnife_skill_manager-0.1.0/docs/agents/issue-tracker.md +39 -0
  21. cnife_skill_manager-0.1.0/docs/agents/triage-labels.md +13 -0
  22. cnife_skill_manager-0.1.0/docs/e2e-fixture/README.md +33 -0
  23. cnife_skill_manager-0.1.0/docs/e2e-fixture/SKILL.md +8 -0
  24. cnife_skill_manager-0.1.0/pyproject.toml +75 -0
  25. cnife_skill_manager-0.1.0/src/skill_manager/__init__.py +6 -0
  26. cnife_skill_manager-0.1.0/src/skill_manager/__main__.py +6 -0
  27. cnife_skill_manager-0.1.0/src/skill_manager/_version.py +24 -0
  28. cnife_skill_manager-0.1.0/src/skill_manager/cli.py +392 -0
  29. cnife_skill_manager-0.1.0/src/skill_manager/config.py +190 -0
  30. cnife_skill_manager-0.1.0/src/skill_manager/links.py +65 -0
  31. cnife_skill_manager-0.1.0/src/skill_manager/paths.py +52 -0
  32. cnife_skill_manager-0.1.0/src/skill_manager/sources.py +75 -0
  33. cnife_skill_manager-0.1.0/tests/conftest.py +59 -0
  34. cnife_skill_manager-0.1.0/tests/test_cli.py +482 -0
  35. cnife_skill_manager-0.1.0/tests/test_config.py +199 -0
  36. cnife_skill_manager-0.1.0/tests/test_links.py +105 -0
  37. cnife_skill_manager-0.1.0/tests/test_sources.py +76 -0
  38. cnife_skill_manager-0.1.0/uv.lock +347 -0
@@ -0,0 +1,68 @@
1
+ ---
2
+ name: e2e-happy-path
3
+ description: End-to-end happy-path test for skill-manager — declares the public fixture, syncs, and asserts the correct path. Run before PR.
4
+ disable-model-invocation: true
5
+ ---
6
+
7
+ # E2E happy-path test for skill-manager
8
+
9
+ Verify skill-manager's **correct path** end to end: a declared skill is cloned from a real public GitHub repo, linked into `.agents/skills/`, and its `SKILL.md` stays readable. The test object is skill-manager itself.
10
+
11
+ The split is fixed: a bash **script** runs the deterministic flow (isolate, declare, sync) and emits a **manifest**; you, the agent, do the orchestration, assertions, and human-readable verdict.
12
+
13
+ ## Preconditions
14
+
15
+ - Run from the **skill-manager repo root** (cwd = repo root). The script path below is relative to it.
16
+ - `skill-manager` is installed and on PATH.
17
+ - GitHub is reachable for anonymous HTTPS clone (the fixture repo is public).
18
+
19
+ ## Steps
20
+
21
+ ### 1. Run setup + sync
22
+
23
+ Execute the script and capture the full stdout and exit code. It builds an isolated tree, redirects XDG, writes the fixture declaration, and runs `skill-manager sync`. It prints `skill-manager sync` output followed by exactly one `E2E_MANIFEST {...}` line on exit.
24
+
25
+ ```bash
26
+ .agents/skills/e2e-happy-path/scripts/e2e-setup-sync.sh
27
+ ```
28
+
29
+ **Completion criterion**: you hold the script's exit code and full stdout, and the `E2E_MANIFEST {...}` line is present in it.
30
+
31
+ ### 2. Route on exit code
32
+
33
+ - **Non-zero exit**: an **infra failure** — setup or sync broke. Grep the `E2E_MANIFEST` line for `root` (preserve that tmpdir for triage), read stderr, and go straight to the verdict.
34
+ - **Zero exit**: sync succeeded; continue to assertion.
35
+
36
+ **Completion criterion**: you have routed to exactly one path — infra failure (verdict) or sync success (assertion).
37
+
38
+ ### 3. Parse the manifest
39
+
40
+ From the `E2E_MANIFEST {...}` line, parse the JSON and take four paths: `root`, `project_dir`, `xdg_config_home`, `xdg_cache_home`.
41
+
42
+ ```bash
43
+ grep '^E2E_MANIFEST ' <<<"$STDOUT" | sed 's/^E2E_MANIFEST //'
44
+ ```
45
+
46
+ **Completion criterion**: all four paths are non-empty.
47
+
48
+ ### 4. Run the thorough gate
49
+
50
+ Assert all five points below. Each records pass/fail with evidence. **Run every point to the end even if an earlier one fails** - the gate is exhaustive so nothing is under-reported; one fail makes the whole run fail, but the rest still execute.
51
+
52
+ Let `REPO=CNife/skill-manager-e2e-fixture` and `CACHE=$xdg_cache_home/skill-manager/repos/$REPO`.
53
+
54
+ 1. **Symlink valid**: `$project_dir/.agents/skills/e2e-fixture` is a symlink whose resolved target equals `$CACHE`.
55
+ 2. **SKILL.md readable**: reading `SKILL.md` through that symlink, its first line is `---`.
56
+ 3. **Ledger HEAD consistent**: in `$xdg_config_home/skill-manager/config.json`, `sources["CNife/skill-manager-e2e-fixture"].commit` equals `git -C "$CACHE" rev-parse HEAD` (compare the two live values).
57
+ 4. **Clone happened**: `$CACHE/.git` exists.
58
+ 5. **sync output**: the script stdout excluding the `E2E_MANIFEST` line contains both `ensured CNife/skill-manager-e2e-fixture` and `created e2e-fixture`.
59
+
60
+ **Completion criterion**: every one of the five points has been asserted and carries a pass/fail plus evidence.
61
+
62
+ ### 5. Verdict
63
+
64
+ - **All five pass**: verdict is **PASS**; you may delete `$root`.
65
+ - **Any fail**: verdict is **FAIL**; list each failing point with its evidence, and **preserve `$root`** for triage, stating the path in the verdict.
66
+ - **Infra failure** (step 2): verdict is **FAIL (infra)**; summarize stderr and state `$root`, preserving it.
67
+
68
+ **Completion criterion**: a single unambiguous PASS or FAIL verdict has been emitted, and on any FAIL the `$root` path is stated and the tmpdir preserved.
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env bash
2
+ # E2E happy-path setup + sync for skill-manager.
3
+ #
4
+ # Deterministic flow ONLY: build an isolated tree, redirect XDG, write the
5
+ # fixture declaration, and run `skill-manager sync`. NO assertions, NO pass/fail
6
+ # judgement — that is the driving agent's job.
7
+ #
8
+ # Contract with the driving agent (skill-manager issue #8):
9
+ # - stdout: `skill-manager sync` output flows straight through, followed by
10
+ # exactly one `E2E_MANIFEST {...}` line emitted on EXIT (success or failure).
11
+ # - stderr: sync failure diagnostics (`Error: ...`).
12
+ # - exit code: the process exit code (0 = setup+sync ok, non-zero = infra fail).
13
+ #
14
+ # The manifest carries the isolated paths the agent needs to assert against, plus
15
+ # `root` (the tmpdir) so a failed run can preserve the whole tree for triage.
16
+ set -euo pipefail
17
+
18
+ E2E_ROOT=""
19
+ PROJECT_DIR=""
20
+ XDG_CONFIG_HOME_VAL=""
21
+ XDG_CACHE_HOME_VAL=""
22
+ _E2E_EMITTED=0
23
+
24
+ emit_manifest() {
25
+ if [ "$_E2E_EMITTED" -eq 1 ]; then return; fi
26
+ _E2E_EMITTED=1
27
+ printf 'E2E_MANIFEST {"root":"%s","project_dir":"%s","xdg_config_home":"%s","xdg_cache_home":"%s"}\n' \
28
+ "$E2E_ROOT" "$PROJECT_DIR" "$XDG_CONFIG_HOME_VAL" "$XDG_CACHE_HOME_VAL"
29
+ }
30
+ trap emit_manifest EXIT
31
+
32
+ # 1. Build the isolated tree: <root>/{project,config,cache}.
33
+ E2E_ROOT="$(mktemp -d -t e2e-happy-path.XXXXXX)"
34
+ PROJECT_DIR="$E2E_ROOT/project"
35
+ XDG_CONFIG_HOME_VAL="$E2E_ROOT/config"
36
+ XDG_CACHE_HOME_VAL="$E2E_ROOT/cache"
37
+ mkdir -p "$PROJECT_DIR" "$XDG_CONFIG_HOME_VAL" "$XDG_CACHE_HOME_VAL"
38
+
39
+ # 2. Declare the fixture skill. Full project-config shape is required by
40
+ # config.load_project_config ({"skills":[...]}), not the bare SkillRef shown
41
+ # in the fixture README.
42
+ cat > "$PROJECT_DIR/.skill-manager.json" <<'EOF'
43
+ {"skills":[{"name":"e2e-fixture","repo":"CNife/skill-manager-e2e-fixture","path":"."}]}
44
+ EOF
45
+
46
+ # 3. Redirect XDG so sync writes the ledger + clone cache under <root>, never
47
+ # the real ~/.config/skill-manager or ~/.cache/skill-manager.
48
+ export XDG_CONFIG_HOME="$XDG_CONFIG_HOME_VAL"
49
+ export XDG_CACHE_HOME="$XDG_CACHE_HOME_VAL"
50
+
51
+ # 4. Run sync with cwd = project dir: sync resolves .skill-manager.json and
52
+ # .agents/skills/ relative to cwd. stdout passes through for the agent;
53
+ # a sync failure exits non-zero (set -e) and the trap still emits the manifest.
54
+ cd "$PROJECT_DIR"
55
+ skill-manager sync
56
+
57
+ # trap EXIT emits the manifest on the success path too.
@@ -0,0 +1,36 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ python-version: ["3.11", "3.12", "3.13"]
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ with:
19
+ fetch-depth: 0
20
+
21
+ - name: Install uv
22
+ uses: astral-sh/setup-uv@v5
23
+ with:
24
+ python-version: ${{ matrix.python-version }}
25
+
26
+ - name: Sync dependencies
27
+ run: uv sync --locked --group dev
28
+
29
+ - name: Ruff check
30
+ run: uv run ruff check .
31
+
32
+ - name: Ruff format
33
+ run: uv run ruff format --check .
34
+
35
+ - name: Pytest
36
+ run: uv run pytest
@@ -0,0 +1,83 @@
1
+ name: Publish
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ python-version: ["3.11", "3.12", "3.13"]
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ with:
18
+ fetch-depth: 0
19
+
20
+ - name: Install uv
21
+ uses: astral-sh/setup-uv@v5
22
+ with:
23
+ python-version: ${{ matrix.python-version }}
24
+
25
+ - name: Sync dependencies
26
+ run: uv sync --locked --group dev
27
+
28
+ - name: Ruff check
29
+ run: uv run ruff check .
30
+
31
+ - name: Ruff format
32
+ run: uv run ruff format --check .
33
+
34
+ - name: Pytest
35
+ run: uv run pytest
36
+
37
+ publish:
38
+ needs: test
39
+ runs-on: ubuntu-latest
40
+ environment: pypi
41
+ permissions:
42
+ id-token: write
43
+ contents: write
44
+ steps:
45
+ - uses: actions/checkout@v4
46
+ with:
47
+ fetch-depth: 0
48
+
49
+ - name: Ensure tag commit is on main
50
+ run: |
51
+ git fetch origin main
52
+ TAG_COMMIT="$(git rev-parse "${GITHUB_REF_NAME}^{commit}")"
53
+ if ! git merge-base --is-ancestor "$TAG_COMMIT" origin/main; then
54
+ echo "Tag ${GITHUB_REF_NAME} (${TAG_COMMIT}) is not an ancestor of origin/main; refusing to publish."
55
+ exit 1
56
+ fi
57
+
58
+ - name: Install uv
59
+ uses: astral-sh/setup-uv@v5
60
+ with:
61
+ python-version: "3.13"
62
+
63
+ - name: Build
64
+ run: uv build
65
+
66
+ - name: Publish to PyPI
67
+ uses: pypa/gh-action-pypi-publish@release/v1
68
+
69
+ - name: Create GitHub Release
70
+ env:
71
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
72
+ run: |
73
+ TAG="${GITHUB_REF_NAME}"
74
+ if gh release view "$TAG" >/dev/null 2>&1; then
75
+ echo "Release $TAG already exists; skipping creation."
76
+ exit 0
77
+ fi
78
+ NOTES=$(git tag -l --format='%(contents)' "$TAG")
79
+ if [ -n "$(printf '%s' "$NOTES" | tr -d '[:space:]')" ]; then
80
+ gh release create "$TAG" --title "$TAG" --notes "$NOTES"
81
+ else
82
+ gh release create "$TAG" --title "$TAG" --generate-notes
83
+ fi
@@ -0,0 +1,18 @@
1
+ .pi/
2
+ .agents/skills/*
3
+ !.agents/skills/e2e-happy-path/
4
+
5
+ # Python / uv
6
+ __pycache__/
7
+ *.py[cod]
8
+ .venv/
9
+ dist/
10
+ build/
11
+ *.egg-info/
12
+ .pytest_cache/
13
+ .ruff_cache/
14
+ .coverage
15
+ htmlcov/
16
+
17
+ # hatch-vcs generated
18
+ src/skill_manager/_version.py
@@ -0,0 +1,21 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v6.0.0
4
+ hooks:
5
+ - id: trailing-whitespace
6
+ - id: end-of-file-fixer
7
+ - id: mixed-line-ending
8
+ args: ["--fix=lf"]
9
+ - id: check-json
10
+ - id: check-toml
11
+ - id: check-yaml
12
+ - repo: https://github.com/astral-sh/uv-pre-commit
13
+ rev: "0.9.22"
14
+ hooks:
15
+ - id: uv-lock
16
+ - repo: https://github.com/astral-sh/ruff-pre-commit
17
+ rev: v0.14.10
18
+ hooks:
19
+ - id: ruff-format
20
+ - id: ruff-check
21
+ args: ["--fix"]
@@ -0,0 +1,13 @@
1
+ ## Agent skills
2
+
3
+ ### Issue tracker
4
+
5
+ Issues live as GitHub issues in this repo, via the `gh` CLI. See `docs/agents/issue-tracker.md`.
6
+
7
+ ### Triage labels
8
+
9
+ Five default triage labels: `needs-triage`, `needs-info`, `ready-for-agent`, `ready-for-human`, `wontfix`. See `docs/agents/triage-labels.md`.
10
+
11
+ ### Domain docs
12
+
13
+ Single-context: one `CONTEXT.md` + `docs/adr/` at the repo root. See `docs/agents/domain.md`.
@@ -0,0 +1,45 @@
1
+ # Skill Manager
2
+
3
+ 按项目声明式启用 coding agent 技能:Skill 只在 Source 中保留一份,项目通过符号链接引用,全局技能保持精简。
4
+
5
+ ## Language
6
+
7
+ **Skill / 技能**:
8
+ 含 `SKILL.md` 的目录,可被遵守 `./.agents/skills/`(及可选 `~/.agents/skills/`)约定的 coding agent 启用。
9
+ _Avoid_: package, installable, 包
10
+
11
+ **Source / 源仓库**:
12
+ 以 `owner/repo` 标识的 GitHub 仓库,克隆进缓存后可提供一个或多个 Skill。
13
+ _Avoid_: registry, remote, 远端, 注册表
14
+
15
+ **Skill declaration / 技能声明**:
16
+ 项目配置中的一条启用记录,由 `name`、`repo`、`path` 组成。
17
+ _Avoid_: skill ref, entry, 安装项
18
+
19
+ **Project config / 项目配置**:
20
+ `./.skill-manager.json`,声明本项目启用哪些 Skill。
21
+ _Avoid_: manifest, lockfile, 清单
22
+
23
+ **Global config / 全局配置**:
24
+ XDG 配置文件(默认 `~/.config/skill-manager/config.json`),记录各 Source 及其 HEAD commit。
25
+ _Avoid_: ledger, database, 台账
26
+
27
+ **Source cache / 源缓存**:
28
+ XDG 缓存目录(默认 `~/.cache/skill-manager/repos/`),存放已克隆的 Source。
29
+ _Avoid_: library, global library, 全局技能库
30
+
31
+ **Link / 链接**:
32
+ `./.agents/skills/<name>` 下的符号链接,指向源缓存内某个 Skill 目录。
33
+ _Avoid_: install, copy, 安装
34
+
35
+ **sync / 同步**:
36
+ 幂等操作:确保 Source 已克隆、记录 HEAD、为已声明 Skill 创建 Link。
37
+ _Avoid_: restore, apply, 恢复, 应用
38
+
39
+ **enable / 启用**:
40
+ 交互式向项目配置追加一条 Skill declaration,并执行 sync。
41
+ _Avoid_: add, install, 添加, 安装
42
+
43
+ **disable / 禁用**:
44
+ 交互式从项目配置移除一条 Skill declaration,并清理对应 Link。
45
+ _Avoid_: remove, uninstall, 删除, 卸载
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CNife
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,107 @@
1
+ Metadata-Version: 2.4
2
+ Name: cnife-skill-manager
3
+ Version: 0.1.0
4
+ Summary: Project-scoped declarative skill manager for agent skills
5
+ Project-URL: Homepage, https://github.com/CNife/skill-manager
6
+ Project-URL: Repository, https://github.com/CNife/skill-manager
7
+ Project-URL: Issues, https://github.com/CNife/skill-manager/issues
8
+ Author-email: CNife <CNife@vip.qq.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: agents,cli,skills,xdg
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Requires-Python: >=3.11
23
+ Requires-Dist: typer>=0.12
24
+ Description-Content-Type: text/markdown
25
+
26
+ # Skill Manager
27
+
28
+ Project-scoped declarative skill manager for coding agents that load skills from `./.agents/skills/` (and optionally `~/.agents/skills/`).
29
+
30
+ **One source, many projects, slim globals.** Each skill lives once in a cached Source. Projects get symlinks (Links), not copies. Keep agent-global skills minimal; declare per project what you actually need.
31
+
32
+ Compatible with any agent that discovers skill directories containing `SKILL.md` under those paths ([pi](https://github.com/earendil-works/pi-coding-agent) is one example).
33
+
34
+ Today skill-manager only writes project Links under `./.agents/skills/`. Managing `~/.agents/skills/` is on the [roadmap](#roadmap).
35
+
36
+ ## Features
37
+
38
+ - **Declare and sync project skills** — write `.skill-manager.json`, run `skill-manager sync`
39
+ - **Inspect source and link status** — `skill-manager list` (`linked` / `external` / `broken` / `unlinked`)
40
+ - **Enable / disable interactively** — `skill-manager enable` / `disable`
41
+ - **Manage source cache** — `skill-manager source list|add|remove|update`
42
+
43
+ ## Install
44
+
45
+ ```bash
46
+ uv tool install cnife-skill-manager
47
+ # or: pipx install cnife-skill-manager
48
+
49
+ # from source (dev):
50
+ uv tool install .
51
+ # or run in-place:
52
+ uv run skill-manager
53
+ ```
54
+
55
+ ## Configure
56
+
57
+ Create `./.skill-manager.json` in your project:
58
+
59
+ ```json
60
+ {
61
+ "skills": [
62
+ {"name": "read", "repo": "tw93/Waza", "path": "skills/read"},
63
+ {"name": "kami", "repo": "tw93/Kami", "path": "."}
64
+ ]
65
+ }
66
+ ```
67
+
68
+ - `name`: symlink name under `./.agents/skills/` (single path component; duplicates error)
69
+ - `repo`: GitHub `owner/repo` (both parts must be safe slug components)
70
+ - `path`: skill directory inside the repo (`.` for repo-root skills; must contain `SKILL.md`; path traversal is rejected)
71
+
72
+ ## Usage
73
+
74
+ ```bash
75
+ skill-manager sync # clone/fetch sources, link declared skills
76
+ skill-manager list # show sources and skill status
77
+ skill-manager enable # interactively add a skill from the cache, then sync
78
+ skill-manager disable # interactively remove a skill and its link
79
+ ```
80
+
81
+ ### Source repository management
82
+
83
+ ```bash
84
+ skill-manager source list # list cached repos and HEAD status
85
+ skill-manager source add <owner/repo> # add and clone a new source
86
+ skill-manager source remove <repo> # remove source (cache + config)
87
+ skill-manager source update [repo] # update one or all sources
88
+ ```
89
+
90
+ `sync` is idempotent and never overwrites an existing non-tool symlink (it skips with a notice). Sources are derived from the declared skills' `repo` fields.
91
+
92
+ ## Paths (XDG)
93
+
94
+ | What | Path |
95
+ |------|------|
96
+ | Global config | `$XDG_CONFIG_HOME/skill-manager/config.json` (default `~/.config/skill-manager/config.json`) |
97
+ | Source cache | `$XDG_CACHE_HOME/skill-manager/repos/` (default `~/.cache/skill-manager/repos/`) |
98
+ | Project skills | `./.agents/skills/` |
99
+
100
+ ## Roadmap
101
+
102
+ - [ ] **Global skills** — manage Links under `~/.agents/skills/` (same model as project Links)
103
+ - [ ] **Non-interactive enable/disable** — CLI flags for scripts and CI
104
+ - [ ] **Broader sources** — arbitrary Git URLs; local git repos or folders as Sources
105
+ - [ ] **Source inspect** — list skill paths in a Source before enabling
106
+ - [ ] **Hidden-dir scan policy** — skip skills under hidden directories by default; `--all` to include them
107
+ - [ ] **Pinned versions** — pin a Source to a commit or tag in the project declaration
@@ -0,0 +1,82 @@
1
+ # Skill Manager
2
+
3
+ Project-scoped declarative skill manager for coding agents that load skills from `./.agents/skills/` (and optionally `~/.agents/skills/`).
4
+
5
+ **One source, many projects, slim globals.** Each skill lives once in a cached Source. Projects get symlinks (Links), not copies. Keep agent-global skills minimal; declare per project what you actually need.
6
+
7
+ Compatible with any agent that discovers skill directories containing `SKILL.md` under those paths ([pi](https://github.com/earendil-works/pi-coding-agent) is one example).
8
+
9
+ Today skill-manager only writes project Links under `./.agents/skills/`. Managing `~/.agents/skills/` is on the [roadmap](#roadmap).
10
+
11
+ ## Features
12
+
13
+ - **Declare and sync project skills** — write `.skill-manager.json`, run `skill-manager sync`
14
+ - **Inspect source and link status** — `skill-manager list` (`linked` / `external` / `broken` / `unlinked`)
15
+ - **Enable / disable interactively** — `skill-manager enable` / `disable`
16
+ - **Manage source cache** — `skill-manager source list|add|remove|update`
17
+
18
+ ## Install
19
+
20
+ ```bash
21
+ uv tool install cnife-skill-manager
22
+ # or: pipx install cnife-skill-manager
23
+
24
+ # from source (dev):
25
+ uv tool install .
26
+ # or run in-place:
27
+ uv run skill-manager
28
+ ```
29
+
30
+ ## Configure
31
+
32
+ Create `./.skill-manager.json` in your project:
33
+
34
+ ```json
35
+ {
36
+ "skills": [
37
+ {"name": "read", "repo": "tw93/Waza", "path": "skills/read"},
38
+ {"name": "kami", "repo": "tw93/Kami", "path": "."}
39
+ ]
40
+ }
41
+ ```
42
+
43
+ - `name`: symlink name under `./.agents/skills/` (single path component; duplicates error)
44
+ - `repo`: GitHub `owner/repo` (both parts must be safe slug components)
45
+ - `path`: skill directory inside the repo (`.` for repo-root skills; must contain `SKILL.md`; path traversal is rejected)
46
+
47
+ ## Usage
48
+
49
+ ```bash
50
+ skill-manager sync # clone/fetch sources, link declared skills
51
+ skill-manager list # show sources and skill status
52
+ skill-manager enable # interactively add a skill from the cache, then sync
53
+ skill-manager disable # interactively remove a skill and its link
54
+ ```
55
+
56
+ ### Source repository management
57
+
58
+ ```bash
59
+ skill-manager source list # list cached repos and HEAD status
60
+ skill-manager source add <owner/repo> # add and clone a new source
61
+ skill-manager source remove <repo> # remove source (cache + config)
62
+ skill-manager source update [repo] # update one or all sources
63
+ ```
64
+
65
+ `sync` is idempotent and never overwrites an existing non-tool symlink (it skips with a notice). Sources are derived from the declared skills' `repo` fields.
66
+
67
+ ## Paths (XDG)
68
+
69
+ | What | Path |
70
+ |------|------|
71
+ | Global config | `$XDG_CONFIG_HOME/skill-manager/config.json` (default `~/.config/skill-manager/config.json`) |
72
+ | Source cache | `$XDG_CACHE_HOME/skill-manager/repos/` (default `~/.cache/skill-manager/repos/`) |
73
+ | Project skills | `./.agents/skills/` |
74
+
75
+ ## Roadmap
76
+
77
+ - [ ] **Global skills** — manage Links under `~/.agents/skills/` (same model as project Links)
78
+ - [ ] **Non-interactive enable/disable** — CLI flags for scripts and CI
79
+ - [ ] **Broader sources** — arbitrary Git URLs; local git repos or folders as Sources
80
+ - [ ] **Source inspect** — list skill paths in a Source before enabling
81
+ - [ ] **Hidden-dir scan policy** — skip skills under hidden directories by default; `--all` to include them
82
+ - [ ] **Pinned versions** — pin a Source to a commit or tag in the project declaration
@@ -0,0 +1,3 @@
1
+ # 用符号链接暴露 Skill,不拷贝
2
+
3
+ pi 从项目的 `./.agents/skills/` 发现技能。skill-manager 在该目录创建指向源缓存内 Skill 目录的符号链接,而不是把文件拷贝进项目。这样 Skill 内容始终以 Source 的 HEAD 为准,项目侧无分叉副本;换用拷贝会在每次更新后留下过期副本,且与「全局做小、按项目启用」的意图相悖。
@@ -0,0 +1,3 @@
1
+ # Source 从技能声明派生
2
+
3
+ 项目配置只声明 Skill(`name` + `repo` + `path`),不维护显式的 sources 列表。`sync` 从声明的 `repo` 字段去重派生需要的 Source。保持 DRY,避免「仓库已声明但无技能」与「技能已声明但仓库未注册」两套状态。独立的 `source add/remove/update` 管理全局缓存层,与项目声明正交:预克隆可以,但项目侧仍以声明为准派生。
@@ -0,0 +1,3 @@
1
+ # Skill 用显式 name 字段命名链接
2
+
3
+ 曾考虑从 `path` 末段(或仓库名)派生链接名,但对仓库根目录技能(`path: "."`)会歧义,且同名技能跨仓库时无法消解。因此每条技能声明带显式 `name`,作为 `./.agents/skills/<name>` 的链接名;`name` 必须是单个路径分量,配置内重名直接报错。身份仍是 `name + repo + path`,`name` 只负责磁盘句柄,不替代 `repo + path` 的定位。
@@ -0,0 +1,3 @@
1
+ # sync 只增不删
2
+
3
+ `sync` 确保声明的 Source 与 Link 存在,但不删除未再声明的链接,也不覆盖已存在的外部符号链接或非链接条目(跳过并提示)。多数同步工具会双向对账;这里选择保守安全:避免误删用户手建链接,也不在未标记「本工具创建」的前提下做回收。移除技能靠 `disable` 或用户手动清理。
@@ -0,0 +1,3 @@
1
+ # 版本以 git tag + hatch-vcs 为唯一来源
2
+
3
+ 包版本不手写在 `pyproject.toml` 或 `__init__.py`。发布时打 `v*` tag(如 `v0.1.0`),由 hatch-vcs 解析为 PEP 440 版本并写入 `_version.py`。这样 tag、PyPI 版本与运行时 `__version__` / `--version` 同一来源,避免多处漂移;相对静态写版本或 attr 读源码,更适合「打 tag 即发布」的 CI 流程。
@@ -0,0 +1,3 @@
1
+ # 经 GitHub OIDC Trusted Publishing 发 PyPI
2
+
3
+ 发布不使用长期 PyPI API token,而在 push `v*` tag 时由 `.github/workflows/publish.yml` 通过 OIDC 向 PyPI 换短时凭证上传。publisher 绑定 PyPI 项目 `cnife-skill-manager`、仓库 `CNife/skill-manager`、workflow `publish.yml`、Environment `pypi`。相对本地 `uv publish` + token,可审计、无密钥落盘,且与 tag 驱动版本一致;首次需在 PyPI 控制台登记 pending publisher。
@@ -0,0 +1,3 @@
1
+ # PyPI 分发名用 cnife-skill-manager,与 CLI 名分离
2
+
3
+ 理想包名 `skill-manager` 因与已有 `skill-mgr` / `agent-skill-manager` 等过于相似,被 PyPI 拒绝登记。分发名改为 `cnife-skill-manager`;CLI 入口与 import 仍为 `skill-manager` / `skill_manager`,避免用户命令与代码路径被品牌前缀绑架。安装:`uv tool install cnife-skill-manager`,使用:`skill-manager`。
@@ -0,0 +1,37 @@
1
+ # Domain Docs
2
+
3
+ How the engineering skills should consume this repo's domain documentation when exploring the codebase.
4
+
5
+ ## Before exploring, read these
6
+
7
+ - **`CONTEXT.md`** at the repo root
8
+ - **`docs/adr/`** — ADRs that touch the area you're about to work in
9
+
10
+ If any of these are missing, **proceed silently**. `/domain-modeling` (via `/grill-with-docs` and `/improve-codebase-architecture`) creates them lazily when terms or decisions resolve.
11
+
12
+ ## File structure
13
+
14
+ Single-context:
15
+
16
+ ```
17
+ /
18
+ ├── CONTEXT.md
19
+ ├── docs/adr/
20
+ │ ├── 0001-symlink-not-copy.md
21
+ │ ├── 0002-sources-derived-from-declarations.md
22
+ │ ├── 0003-explicit-skill-name.md
23
+ │ └── 0004-sync-add-only.md
24
+ └── src/
25
+ ```
26
+
27
+ ## Use the glossary's vocabulary
28
+
29
+ When your output names a domain concept (issue title, refactor proposal, hypothesis, test name), use the term as defined in `CONTEXT.md`. Synonyms under `_Avoid_` are out of bounds.
30
+
31
+ If the concept isn't in the glossary yet — either inventing language the project doesn't use (reconsider), or a real gap (note it for `/domain-modeling`).
32
+
33
+ ## Flag ADR conflicts
34
+
35
+ If your output contradicts an existing ADR, surface it rather than silently overriding:
36
+
37
+ > _Contradicts ADR-0004 (sync add-only) — but worth reopening because…_