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.
- cnife_skill_manager-0.1.0/.agents/skills/e2e-happy-path/SKILL.md +68 -0
- cnife_skill_manager-0.1.0/.agents/skills/e2e-happy-path/scripts/e2e-setup-sync.sh +57 -0
- cnife_skill_manager-0.1.0/.github/workflows/ci.yml +36 -0
- cnife_skill_manager-0.1.0/.github/workflows/publish.yml +83 -0
- cnife_skill_manager-0.1.0/.gitignore +18 -0
- cnife_skill_manager-0.1.0/.pre-commit-config.yaml +21 -0
- cnife_skill_manager-0.1.0/AGENTS.md +13 -0
- cnife_skill_manager-0.1.0/CONTEXT.md +45 -0
- cnife_skill_manager-0.1.0/LICENSE +21 -0
- cnife_skill_manager-0.1.0/PKG-INFO +107 -0
- cnife_skill_manager-0.1.0/README.md +82 -0
- cnife_skill_manager-0.1.0/docs/adr/0001-symlink-not-copy.md +3 -0
- cnife_skill_manager-0.1.0/docs/adr/0002-sources-derived-from-declarations.md +3 -0
- cnife_skill_manager-0.1.0/docs/adr/0003-explicit-skill-name.md +3 -0
- cnife_skill_manager-0.1.0/docs/adr/0004-sync-add-only.md +3 -0
- cnife_skill_manager-0.1.0/docs/adr/0005-version-from-git-tags.md +3 -0
- cnife_skill_manager-0.1.0/docs/adr/0006-pypi-trusted-publishing.md +3 -0
- cnife_skill_manager-0.1.0/docs/adr/0007-pypi-name-cnife-skill-manager.md +3 -0
- cnife_skill_manager-0.1.0/docs/agents/domain.md +37 -0
- cnife_skill_manager-0.1.0/docs/agents/issue-tracker.md +39 -0
- cnife_skill_manager-0.1.0/docs/agents/triage-labels.md +13 -0
- cnife_skill_manager-0.1.0/docs/e2e-fixture/README.md +33 -0
- cnife_skill_manager-0.1.0/docs/e2e-fixture/SKILL.md +8 -0
- cnife_skill_manager-0.1.0/pyproject.toml +75 -0
- cnife_skill_manager-0.1.0/src/skill_manager/__init__.py +6 -0
- cnife_skill_manager-0.1.0/src/skill_manager/__main__.py +6 -0
- cnife_skill_manager-0.1.0/src/skill_manager/_version.py +24 -0
- cnife_skill_manager-0.1.0/src/skill_manager/cli.py +392 -0
- cnife_skill_manager-0.1.0/src/skill_manager/config.py +190 -0
- cnife_skill_manager-0.1.0/src/skill_manager/links.py +65 -0
- cnife_skill_manager-0.1.0/src/skill_manager/paths.py +52 -0
- cnife_skill_manager-0.1.0/src/skill_manager/sources.py +75 -0
- cnife_skill_manager-0.1.0/tests/conftest.py +59 -0
- cnife_skill_manager-0.1.0/tests/test_cli.py +482 -0
- cnife_skill_manager-0.1.0/tests/test_config.py +199 -0
- cnife_skill_manager-0.1.0/tests/test_links.py +105 -0
- cnife_skill_manager-0.1.0/tests/test_sources.py +76 -0
- 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
|
+
# 经 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…_
|