cocoaskills 0.2.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 (67) hide show
  1. cocoaskills-0.2.0/.github/workflows/ci.yml +90 -0
  2. cocoaskills-0.2.0/.github/workflows/distribution-smoke.yml +399 -0
  3. cocoaskills-0.2.0/.github/workflows/release.yml +118 -0
  4. cocoaskills-0.2.0/.gitignore +38 -0
  5. cocoaskills-0.2.0/CHANGELOG.md +81 -0
  6. cocoaskills-0.2.0/LICENSE +201 -0
  7. cocoaskills-0.2.0/PKG-INFO +202 -0
  8. cocoaskills-0.2.0/README.md +168 -0
  9. cocoaskills-0.2.0/docs/CNAME +1 -0
  10. cocoaskills-0.2.0/docs/index.html +53 -0
  11. cocoaskills-0.2.0/docs/install.sh +64 -0
  12. cocoaskills-0.2.0/docs/mvp-design.md +1008 -0
  13. cocoaskills-0.2.0/pyproject.toml +70 -0
  14. cocoaskills-0.2.0/setup.cfg +4 -0
  15. cocoaskills-0.2.0/src/cocoaskills.egg-info/PKG-INFO +202 -0
  16. cocoaskills-0.2.0/src/cocoaskills.egg-info/SOURCES.txt +65 -0
  17. cocoaskills-0.2.0/src/cocoaskills.egg-info/dependency_links.txt +1 -0
  18. cocoaskills-0.2.0/src/cocoaskills.egg-info/entry_points.txt +2 -0
  19. cocoaskills-0.2.0/src/cocoaskills.egg-info/requires.txt +5 -0
  20. cocoaskills-0.2.0/src/cocoaskills.egg-info/top_level.txt +1 -0
  21. cocoaskills-0.2.0/src/csk/__init__.py +18 -0
  22. cocoaskills-0.2.0/src/csk/__main__.py +6 -0
  23. cocoaskills-0.2.0/src/csk/_version.py +24 -0
  24. cocoaskills-0.2.0/src/csk/adapters.py +120 -0
  25. cocoaskills-0.2.0/src/csk/cli.py +452 -0
  26. cocoaskills-0.2.0/src/csk/config.py +197 -0
  27. cocoaskills-0.2.0/src/csk/env_files.py +26 -0
  28. cocoaskills-0.2.0/src/csk/gc.py +35 -0
  29. cocoaskills-0.2.0/src/csk/git_ops.py +111 -0
  30. cocoaskills-0.2.0/src/csk/gitignore_gate.py +49 -0
  31. cocoaskills-0.2.0/src/csk/hashing.py +26 -0
  32. cocoaskills-0.2.0/src/csk/installer.py +314 -0
  33. cocoaskills-0.2.0/src/csk/locale.py +105 -0
  34. cocoaskills-0.2.0/src/csk/locking.py +64 -0
  35. cocoaskills-0.2.0/src/csk/manifest.py +140 -0
  36. cocoaskills-0.2.0/src/csk/project_resolver.py +108 -0
  37. cocoaskills-0.2.0/src/csk/shell_init.py +77 -0
  38. cocoaskills-0.2.0/src/csk/shims.py +77 -0
  39. cocoaskills-0.2.0/src/csk/skillspec.py +126 -0
  40. cocoaskills-0.2.0/src/csk/snapshot.py +32 -0
  41. cocoaskills-0.2.0/src/csk/status.py +84 -0
  42. cocoaskills-0.2.0/src/csk/whitelist.py +98 -0
  43. cocoaskills-0.2.0/tests/conftest.py +114 -0
  44. cocoaskills-0.2.0/tests/fixtures/skill_legacy_runtime/.gitkeep +1 -0
  45. cocoaskills-0.2.0/tests/fixtures/skill_minimal/.gitkeep +1 -0
  46. cocoaskills-0.2.0/tests/fixtures/skill_no_remote/.gitkeep +1 -0
  47. cocoaskills-0.2.0/tests/fixtures/skill_with_locales/.gitkeep +1 -0
  48. cocoaskills-0.2.0/tests/fixtures/skill_with_scripts/.gitkeep +1 -0
  49. cocoaskills-0.2.0/tests/fixtures/skill_with_submodule/.gitkeep +1 -0
  50. cocoaskills-0.2.0/tests/test_adapters.py +62 -0
  51. cocoaskills-0.2.0/tests/test_cli.py +357 -0
  52. cocoaskills-0.2.0/tests/test_config.py +64 -0
  53. cocoaskills-0.2.0/tests/test_e2e.py +120 -0
  54. cocoaskills-0.2.0/tests/test_env_files.py +11 -0
  55. cocoaskills-0.2.0/tests/test_gc.py +55 -0
  56. cocoaskills-0.2.0/tests/test_gitignore_gate.py +21 -0
  57. cocoaskills-0.2.0/tests/test_hashing.py +16 -0
  58. cocoaskills-0.2.0/tests/test_install.py +307 -0
  59. cocoaskills-0.2.0/tests/test_locale.py +51 -0
  60. cocoaskills-0.2.0/tests/test_locking.py +14 -0
  61. cocoaskills-0.2.0/tests/test_manifest.py +44 -0
  62. cocoaskills-0.2.0/tests/test_project_resolver.py +59 -0
  63. cocoaskills-0.2.0/tests/test_refs.py +23 -0
  64. cocoaskills-0.2.0/tests/test_shims.py +40 -0
  65. cocoaskills-0.2.0/tests/test_skillspec.py +50 -0
  66. cocoaskills-0.2.0/tests/test_status.py +39 -0
  67. cocoaskills-0.2.0/tests/test_whitelist.py +39 -0
@@ -0,0 +1,90 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ concurrency:
10
+ group: ci-${{ github.ref }}
11
+ cancel-in-progress: true
12
+
13
+ jobs:
14
+ test:
15
+ name: Tests / Python ${{ matrix.python-version }} on ${{ matrix.os }}
16
+ runs-on: ${{ matrix.os }}
17
+ strategy:
18
+ fail-fast: false
19
+ matrix:
20
+ os: [ubuntu-latest, macos-latest, windows-latest]
21
+ python-version: ["3.11", "3.12", "3.13", "3.14"]
22
+ steps:
23
+ - name: Checkout
24
+ uses: actions/checkout@v4
25
+ with:
26
+ fetch-depth: 0
27
+
28
+ - name: Set up Python ${{ matrix.python-version }}
29
+ uses: actions/setup-python@v5
30
+ with:
31
+ python-version: ${{ matrix.python-version }}
32
+ allow-prereleases: true
33
+
34
+ - name: Configure git (POSIX)
35
+ if: runner.os != 'Windows'
36
+ run: |
37
+ git config --global user.email "ci@example.com"
38
+ git config --global user.name "CI"
39
+ git config --global init.defaultBranch main
40
+
41
+ - name: Configure git (Windows)
42
+ if: runner.os == 'Windows'
43
+ shell: pwsh
44
+ run: |
45
+ git config --global user.email "ci@example.com"
46
+ git config --global user.name "CI"
47
+ git config --global init.defaultBranch main
48
+ git config --global core.autocrlf false
49
+ git config --global core.symlinks false
50
+
51
+ - name: Install package
52
+ run: |
53
+ python -m pip install --upgrade pip
54
+ python -m pip install -e ".[dev]"
55
+
56
+ - name: Run tests
57
+ run: python -m pytest -v
58
+
59
+ build:
60
+ name: Build artifacts
61
+ runs-on: ubuntu-latest
62
+ needs: test
63
+ steps:
64
+ - name: Checkout
65
+ uses: actions/checkout@v4
66
+ with:
67
+ fetch-depth: 0
68
+
69
+ - name: Set up Python
70
+ uses: actions/setup-python@v5
71
+ with:
72
+ python-version: "3.12"
73
+
74
+ - name: Install build tools
75
+ run: |
76
+ python -m pip install --upgrade pip
77
+ python -m pip install build twine
78
+
79
+ - name: Build sdist and wheel
80
+ run: python -m build
81
+
82
+ - name: Verify metadata
83
+ run: twine check dist/*
84
+
85
+ - name: Upload artifacts
86
+ uses: actions/upload-artifact@v4
87
+ with:
88
+ name: dist
89
+ path: dist/
90
+ if-no-files-found: error
@@ -0,0 +1,399 @@
1
+ name: Distribution Smoke
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_run:
7
+ workflows: ["Release"]
8
+ types: [completed]
9
+ workflow_dispatch:
10
+ inputs:
11
+ version:
12
+ description: "Version or tag to smoke, for example 0.1.1 or v0.1.1"
13
+ required: true
14
+ type: string
15
+ include_homebrew:
16
+ description: "Also test the Homebrew tap. Enable after the tap formula is bumped."
17
+ required: false
18
+ default: false
19
+ type: boolean
20
+
21
+ permissions:
22
+ contents: read
23
+
24
+ jobs:
25
+ resolve-version:
26
+ name: Resolve version
27
+ runs-on: ubuntu-latest
28
+ if: ${{ github.event_name != 'workflow_run' || (github.event.workflow_run.conclusion == 'success' && !contains(github.event.workflow_run.head_branch, '-')) }}
29
+ outputs:
30
+ version: ${{ steps.version.outputs.version }}
31
+ tag: ${{ steps.version.outputs.tag }}
32
+ steps:
33
+ - name: Normalize version
34
+ id: version
35
+ env:
36
+ RAW_VERSION: ${{ github.event_name == 'workflow_dispatch' && inputs.version || github.event_name == 'workflow_run' && github.event.workflow_run.head_branch || github.event.release.tag_name }}
37
+ run: |
38
+ python - <<'PY' >> "$GITHUB_OUTPUT"
39
+ import os
40
+
41
+ raw = os.environ["RAW_VERSION"].strip()
42
+ tag = raw if raw.startswith("v") else f"v{raw}"
43
+ version = raw[1:] if raw.startswith("v") else raw
44
+ version = version.replace("-rc", "rc").replace("-alpha", "a").replace("-beta", "b")
45
+ print(f"tag={tag}")
46
+ print(f"version={version}")
47
+ PY
48
+
49
+ smoke:
50
+ name: ${{ matrix.channel }} / ${{ matrix.os }}
51
+ needs: resolve-version
52
+ runs-on: ${{ matrix.os }}
53
+ strategy:
54
+ fail-fast: false
55
+ matrix:
56
+ include:
57
+ - os: ubuntu-latest
58
+ channel: pipx
59
+ - os: macos-latest
60
+ channel: pipx
61
+ - os: windows-latest
62
+ channel: pipx
63
+ - os: ubuntu-latest
64
+ channel: uv-tool
65
+ - os: macos-latest
66
+ channel: uv-tool
67
+ - os: windows-latest
68
+ channel: uv-tool
69
+ - os: ubuntu-latest
70
+ channel: mise
71
+ - os: macos-latest
72
+ channel: mise
73
+ - os: ubuntu-latest
74
+ channel: install-sh
75
+ - os: macos-latest
76
+ channel: install-sh
77
+ defaults:
78
+ run:
79
+ shell: bash
80
+ env:
81
+ CSK_VERSION: ${{ needs.resolve-version.outputs.version }}
82
+ steps:
83
+ - name: Set up Python
84
+ uses: actions/setup-python@v5
85
+ with:
86
+ python-version: "3.12"
87
+
88
+ - name: Configure git
89
+ run: |
90
+ git config --global user.email "ci@example.com"
91
+ git config --global user.name "CI"
92
+ git config --global init.defaultBranch main
93
+ git config --global core.autocrlf false
94
+ git config --global core.symlinks false
95
+
96
+ - name: Install with pipx
97
+ if: matrix.channel == 'pipx'
98
+ run: |
99
+ python -m pip install --upgrade pip pipx
100
+ for attempt in 1 2 3 4 5; do
101
+ if python -m pipx install "cocoaskills==$CSK_VERSION"; then
102
+ break
103
+ fi
104
+ python -m pipx uninstall cocoaskills >/dev/null 2>&1 || true
105
+ if [ "$attempt" -eq 5 ]; then
106
+ exit 1
107
+ fi
108
+ sleep $((attempt * 15))
109
+ done
110
+ python - <<'PY' >> "$GITHUB_PATH"
111
+ import subprocess
112
+ import sys
113
+
114
+ print(subprocess.check_output(
115
+ [sys.executable, "-m", "pipx", "environment", "--value", "PIPX_BIN_DIR"],
116
+ text=True,
117
+ ).strip())
118
+ PY
119
+
120
+ - name: Install with uv tool
121
+ if: matrix.channel == 'uv-tool'
122
+ run: |
123
+ python -m pip install --upgrade pip uv
124
+ for attempt in 1 2 3 4 5; do
125
+ if python -m uv tool install "cocoaskills==$CSK_VERSION"; then
126
+ break
127
+ fi
128
+ python -m uv tool uninstall cocoaskills >/dev/null 2>&1 || true
129
+ if [ "$attempt" -eq 5 ]; then
130
+ exit 1
131
+ fi
132
+ sleep $((attempt * 15))
133
+ done
134
+ python -m uv tool dir --bin >> "$GITHUB_PATH"
135
+
136
+ - name: Install with mise
137
+ if: matrix.channel == 'mise'
138
+ run: |
139
+ curl -fsSL https://mise.run | sh
140
+ echo "$HOME/.local/bin" >> "$GITHUB_PATH"
141
+ for attempt in 1 2 3 4 5; do
142
+ if "$HOME/.local/bin/mise" install -y "pipx:cocoaskills@$CSK_VERSION"; then
143
+ break
144
+ fi
145
+ if [ "$attempt" -eq 5 ]; then
146
+ exit 1
147
+ fi
148
+ sleep $((attempt * 15))
149
+ done
150
+ mkdir -p "$RUNNER_TEMP/csk-mise-wrapper"
151
+ cat > "$RUNNER_TEMP/csk-mise-wrapper/csk" <<EOF
152
+ #!/usr/bin/env bash
153
+ exec "$HOME/.local/bin/mise" exec -y "pipx:cocoaskills@$CSK_VERSION" -- csk "\$@"
154
+ EOF
155
+ chmod +x "$RUNNER_TEMP/csk-mise-wrapper/csk"
156
+ echo "$RUNNER_TEMP/csk-mise-wrapper" >> "$GITHUB_PATH"
157
+
158
+ - name: Install with install.sh
159
+ if: matrix.channel == 'install-sh'
160
+ run: |
161
+ python -m pip install --upgrade pip uv
162
+ curl -fsSL https://cocoaskills.org/install.sh | sh
163
+ python -m uv tool dir --bin >> "$GITHUB_PATH"
164
+
165
+ - name: Verify installed CLI version
166
+ run: |
167
+ actual="$(csk --version)"
168
+ printf '%s\n' "$actual"
169
+ case "$actual" in
170
+ *" $CSK_VERSION") ;;
171
+ *) echo "Expected csk $CSK_VERSION, got: $actual" >&2; exit 1 ;;
172
+ esac
173
+
174
+ - name: Run minimal published-package E2E
175
+ run: |
176
+ root="$(mktemp -d)"
177
+ skills="$root/skills"
178
+ project="$root/project"
179
+ csk_home="$root/csk-home"
180
+ mkdir -p "$skills/skill-a" "$project" "$csk_home"
181
+
182
+ (
183
+ cd "$skills/skill-a"
184
+ git init
185
+ git branch -M main
186
+ printf '%s\n' '---' 'name: skill-a' '---' '' '# Skill A' > SKILL.md
187
+ git add SKILL.md
188
+ git commit -m "skill"
189
+ git tag v1
190
+ )
191
+
192
+ (
193
+ cd "$project"
194
+ git init
195
+ git branch -M main
196
+ printf '.agents/\n.codex/skills/\n' > .gitignore
197
+ cat > Skillfile.json <<'JSON'
198
+ {
199
+ "schema_version": 1,
200
+ "agents": ["codex_cli"],
201
+ "skills": [
202
+ { "name": "skill-a", "tag": "v1" }
203
+ ]
204
+ }
205
+ JSON
206
+ git add .gitignore Skillfile.json
207
+ git commit -m "project"
208
+ )
209
+
210
+ config_file="$csk_home/config.json"
211
+ config_skills="$skills"
212
+ config_project="$project"
213
+ if command -v cygpath >/dev/null 2>&1; then
214
+ config_file="$(cygpath -w "$config_file")"
215
+ config_skills="$(cygpath -w "$config_skills")"
216
+ config_project="$(cygpath -w "$config_project")"
217
+ fi
218
+
219
+ CONFIG_FILE="$config_file" CONFIG_SKILLS="$config_skills" CONFIG_PROJECT="$config_project" python - <<'PY'
220
+ import json
221
+ import os
222
+ from pathlib import Path
223
+
224
+ config_file = Path(os.environ["CONFIG_FILE"])
225
+ config_file.parent.mkdir(parents=True, exist_ok=True)
226
+ config_file.write_text(
227
+ json.dumps(
228
+ {
229
+ "schema_version": 1,
230
+ "skills_root": os.environ["CONFIG_SKILLS"],
231
+ "default_agents": ["codex_cli"],
232
+ "adapter_mode": "auto",
233
+ "projects": {
234
+ "app": {
235
+ "path": os.environ["CONFIG_PROJECT"],
236
+ "agents": ["codex_cli"],
237
+ }
238
+ },
239
+ },
240
+ indent=2,
241
+ ),
242
+ encoding="utf-8",
243
+ )
244
+ PY
245
+
246
+ export CSK_CONFIG="$config_file"
247
+ csk install app
248
+ test -f "$project/.agents/skills/skill-a/SKILL.md"
249
+ test -f "$project/.agents/skills/skill-a/.csk-install.json"
250
+ csk status app | tee "$root/status.txt"
251
+ grep -q "up-to-date" "$root/status.txt"
252
+
253
+ marker_file="$project/.agents/skills/skill-a/.csk-install.json"
254
+ if command -v cygpath >/dev/null 2>&1; then
255
+ marker_file="$(cygpath -w "$marker_file")"
256
+ fi
257
+
258
+ MARKER_FILE="$marker_file" python - <<PY > "$RUNNER_TEMP/content-sha256-${{ matrix.os }}-${{ matrix.channel }}.txt"
259
+ import json
260
+ import os
261
+ from pathlib import Path
262
+
263
+ marker = Path(os.environ["MARKER_FILE"])
264
+ print(json.loads(marker.read_text())["content_sha256"])
265
+ PY
266
+
267
+ - name: Upload content hash
268
+ uses: actions/upload-artifact@v4
269
+ with:
270
+ name: content-hash-${{ matrix.os }}-${{ matrix.channel }}
271
+ path: ${{ runner.temp }}/content-sha256-${{ matrix.os }}-${{ matrix.channel }}.txt
272
+ if-no-files-found: error
273
+
274
+ compare-hashes:
275
+ name: Compare content hashes
276
+ needs: smoke
277
+ if: ${{ always() && !cancelled() }}
278
+ runs-on: ubuntu-latest
279
+ steps:
280
+ - name: Download content hashes
281
+ uses: actions/download-artifact@v4
282
+ with:
283
+ pattern: content-hash-*
284
+ path: hashes
285
+ merge-multiple: true
286
+
287
+ - name: Assert cross-channel hash stability
288
+ run: |
289
+ python - <<'PY'
290
+ from pathlib import Path
291
+
292
+ values = {}
293
+ for path in sorted(Path("hashes").glob("*.txt")):
294
+ values[path.name] = path.read_text(encoding="utf-8").strip()
295
+
296
+ if len(values) < 2:
297
+ raise SystemExit(f"Expected at least two content hash artifacts, got {len(values)}")
298
+
299
+ unique = set(values.values())
300
+ for name, value in values.items():
301
+ print(f"{name}: {value}")
302
+
303
+ if len(unique) != 1:
304
+ raise SystemExit("content_sha256 differs across smoke jobs")
305
+ PY
306
+
307
+ homebrew-smoke:
308
+ name: homebrew / macos-latest
309
+ needs: resolve-version
310
+ runs-on: macos-latest
311
+ if: ${{ github.event_name == 'workflow_dispatch' && inputs.include_homebrew }}
312
+ defaults:
313
+ run:
314
+ shell: bash
315
+ env:
316
+ CSK_VERSION: ${{ needs.resolve-version.outputs.version }}
317
+ steps:
318
+ - name: Set up Python
319
+ uses: actions/setup-python@v5
320
+ with:
321
+ python-version: "3.12"
322
+
323
+ - name: Configure git
324
+ run: |
325
+ git config --global user.email "ci@example.com"
326
+ git config --global user.name "CI"
327
+ git config --global init.defaultBranch main
328
+ git config --global core.autocrlf false
329
+ git config --global core.symlinks false
330
+
331
+ - name: Install with Homebrew
332
+ run: |
333
+ brew tap ivanopcode/csk
334
+ brew install cocoaskills
335
+
336
+ - name: Verify installed CLI version
337
+ run: |
338
+ actual="$(csk --version)"
339
+ printf '%s\n' "$actual"
340
+ case "$actual" in
341
+ *" $CSK_VERSION") ;;
342
+ *) echo "Expected csk $CSK_VERSION, got: $actual" >&2; exit 1 ;;
343
+ esac
344
+
345
+ - name: Run minimal Homebrew E2E
346
+ run: |
347
+ root="$(mktemp -d)"
348
+ skills="$root/skills"
349
+ project="$root/project"
350
+ csk_home="$root/csk-home"
351
+ mkdir -p "$skills/skill-a" "$project" "$csk_home"
352
+
353
+ (
354
+ cd "$skills/skill-a"
355
+ git init
356
+ git branch -M main
357
+ printf '%s\n' '---' 'name: skill-a' '---' '' '# Skill A' > SKILL.md
358
+ git add SKILL.md
359
+ git commit -m "skill"
360
+ git tag v1
361
+ )
362
+
363
+ (
364
+ cd "$project"
365
+ git init
366
+ git branch -M main
367
+ printf '.agents/\n.codex/skills/\n' > .gitignore
368
+ cat > Skillfile.json <<'JSON'
369
+ {
370
+ "schema_version": 1,
371
+ "agents": ["codex_cli"],
372
+ "skills": [
373
+ { "name": "skill-a", "tag": "v1" }
374
+ ]
375
+ }
376
+ JSON
377
+ git add .gitignore Skillfile.json
378
+ git commit -m "project"
379
+ )
380
+
381
+ cat > "$csk_home/config.json" <<JSON
382
+ {
383
+ "schema_version": 1,
384
+ "skills_root": "$skills",
385
+ "default_agents": ["codex_cli"],
386
+ "adapter_mode": "auto",
387
+ "projects": {
388
+ "app": {
389
+ "path": "$project",
390
+ "agents": ["codex_cli"]
391
+ }
392
+ }
393
+ }
394
+ JSON
395
+
396
+ export CSK_CONFIG="$csk_home/config.json"
397
+ csk install app
398
+ test -f "$project/.agents/skills/skill-a/SKILL.md"
399
+ csk status app | grep -q "up-to-date"
@@ -0,0 +1,118 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ build:
13
+ name: Build distributions
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - name: Checkout
17
+ uses: actions/checkout@v4
18
+ with:
19
+ fetch-depth: 0
20
+
21
+ - name: Set up Python
22
+ uses: actions/setup-python@v5
23
+ with:
24
+ python-version: "3.12"
25
+
26
+ - name: Install build tools
27
+ run: python -m pip install --upgrade build twine
28
+
29
+ - name: Build wheel and sdist
30
+ run: python -m build
31
+
32
+ - name: Check metadata
33
+ run: twine check dist/*
34
+
35
+ - name: Generate checksums
36
+ run: |
37
+ python - <<'PY'
38
+ from pathlib import Path
39
+ import hashlib
40
+
41
+ lines = []
42
+ for path in sorted(Path("dist").iterdir()):
43
+ if path.name == "SHA256SUMS":
44
+ continue
45
+ digest = hashlib.sha256(path.read_bytes()).hexdigest()
46
+ lines.append(f"{digest} {path.name}")
47
+ Path("dist/SHA256SUMS").write_text("\n".join(lines) + "\n", encoding="utf-8")
48
+ PY
49
+
50
+ - name: Upload Python distributions
51
+ uses: actions/upload-artifact@v4
52
+ with:
53
+ name: python-dist
54
+ path: |
55
+ dist/*.tar.gz
56
+ dist/*.whl
57
+
58
+ - name: Upload checksums
59
+ uses: actions/upload-artifact@v4
60
+ with:
61
+ name: checksums
62
+ path: dist/SHA256SUMS
63
+
64
+ publish-testpypi:
65
+ name: Publish to TestPyPI
66
+ needs: build
67
+ runs-on: ubuntu-latest
68
+ if: startsWith(github.ref_name, 'v') && (contains(github.ref_name, '-rc') || contains(github.ref_name, '-alpha') || contains(github.ref_name, '-beta'))
69
+ environment: testpypi
70
+ permissions:
71
+ contents: read
72
+ id-token: write
73
+ steps:
74
+ - name: Download distributions
75
+ uses: actions/download-artifact@v4
76
+ with:
77
+ name: python-dist
78
+ path: dist
79
+
80
+ - name: Publish to TestPyPI
81
+ uses: pypa/gh-action-pypi-publish@release/v1
82
+ with:
83
+ repository-url: https://test.pypi.org/legacy/
84
+ packages-dir: dist/
85
+
86
+ publish-pypi:
87
+ name: Publish to PyPI and GitHub Releases
88
+ needs: build
89
+ runs-on: ubuntu-latest
90
+ if: startsWith(github.ref_name, 'v') && !contains(github.ref_name, '-')
91
+ environment: pypi
92
+ permissions:
93
+ contents: write
94
+ id-token: write
95
+ steps:
96
+ - name: Download distributions
97
+ uses: actions/download-artifact@v4
98
+ with:
99
+ name: python-dist
100
+ path: dist
101
+
102
+ - name: Publish to PyPI
103
+ uses: pypa/gh-action-pypi-publish@release/v1
104
+ with:
105
+ packages-dir: dist/
106
+
107
+ - name: Download checksums
108
+ uses: actions/download-artifact@v4
109
+ with:
110
+ name: checksums
111
+ path: dist
112
+
113
+ - name: Create GitHub Release
114
+ uses: softprops/action-gh-release@v2
115
+ with:
116
+ files: dist/*
117
+ fail_on_unmatched_files: true
118
+ generate_release_notes: true
@@ -0,0 +1,38 @@
1
+ # macOS
2
+ .DS_Store
3
+
4
+ # Python
5
+ __pycache__/
6
+ *.py[cod]
7
+ *$py.class
8
+ .Python
9
+ .venv/
10
+ venv/
11
+ env/
12
+ ENV/
13
+ .pytest_cache/
14
+ .mypy_cache/
15
+ .ruff_cache/
16
+ .coverage
17
+ htmlcov/
18
+ build/
19
+ dist/
20
+ *.egg-info/
21
+
22
+ # Editor and local files
23
+ .idea/
24
+ .vscode/
25
+ *.swp
26
+ *.swo
27
+ *.log
28
+
29
+ # setuptools-scm
30
+ src/csk/_version.py
31
+
32
+ # CocoaSkill generated runtime/install artifacts
33
+ .agents/
34
+ .claude/skills/
35
+ .codex/skills/
36
+ .gemini/skills/
37
+ .cursor/rules/
38
+