feat: add first-party skills, ponytail lock, and full lint suite
Some checks failed
🧪 Test / 🧪 Lock + lint (push) Has been cancelled

- add ponytail skill set to skills-lock.json (14 locked skills total)
- add first-party medusa skill under skills/, layered on install
- make test now runs ruff, shellcheck, and markdownlint-cli2
- CI installs the lint tools before make test in both workflows
- MIT license, funding buttons, thanks and donation sections in README
- .markdownlint-cli2.jsonc config; ignore lint caches in .gitignore
This commit is contained in:
2026-07-10 12:54:47 +02:00
parent 16c8b7f4af
commit 077643098b
11 changed files with 237 additions and 16 deletions

View File

@@ -6,11 +6,27 @@ on:
jobs: jobs:
test: test:
name: 🧪 Lock + scripts name: 🧪 Lock + lint
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: ⬇️ Checkout - name: ⬇️ Checkout
uses: actions/checkout@v7 uses: actions/checkout@v7
- name: 🐍 Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: 🟢 Set up Node.js
uses: actions/setup-node@v6
with:
node-version: lts/*
- name: 📦 Install lint tools
run: |
pip install ruff
sudo apt-get update && sudo apt-get install -y shellcheck
npm install -g markdownlint-cli2
- name: 🧪 Run tests - name: 🧪 Run tests
run: make test run: make test

View File

@@ -17,11 +17,22 @@ jobs:
- name: ⬇️ Checkout - name: ⬇️ Checkout
uses: actions/checkout@v7 uses: actions/checkout@v7
- name: 🐍 Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: 🔧 Set up Node.js - name: 🔧 Set up Node.js
uses: actions/setup-node@v6 uses: actions/setup-node@v6
with: with:
node-version: lts/* node-version: lts/*
- name: 📦 Install lint tools
run: |
pip install ruff
sudo apt-get update && sudo apt-get install -y shellcheck
npm install -g markdownlint-cli2
- name: 🔄 Update skills - name: 🔄 Update skills
run: make update run: make update

19
.gitignore vendored
View File

@@ -1,8 +1,21 @@
# Python
.venv/ .venv/
dist/
build/
__pycache__/ __pycache__/
*.pyc *.pyc
dist/
build/
.ruff_cache/
.pytest_cache/
.mypy_cache/
# Node
node_modules/
npm-debug.log*
# Rendered skill install targets
.agents/ .agents/
.claude/ .claude/
node_modules/
# Editor / OS
.DS_Store
*.swp

13
.markdownlint-cli2.jsonc Normal file
View File

@@ -0,0 +1,13 @@
{
"config": {
"default": true,
"MD013": false,
"MD033": false,
"MD041": false
},
"ignores": [
"node_modules",
".agents",
".claude"
]
}

22
LICENSE
View File

@@ -1 +1,21 @@
All rights reserved by Kevin Veen-Birkenbach MIT License
Copyright (c) 2026 Kevin Veen-Birkenbach
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -9,10 +9,12 @@ project:
@test -n "$(TARGET)" || { echo "usage: make project TARGET=/path/to/repo"; exit 2; } @test -n "$(TARGET)" || { echo "usage: make project TARGET=/path/to/repo"; exit 2; }
@TARGET="$(TARGET)" bash scripts/install.sh @TARGET="$(TARGET)" bash scripts/install.sh
# Validate skills-lock.json and shellcheck the scripts. # Validate the lock, lint Python (ruff), scripts (shellcheck), and markdown.
test: test:
@python3 -m unittest discover -s tests -v @python3 -m unittest discover -s tests -v
@if command -v ruff >/dev/null; then ruff check .; else echo "ruff not installed - skipped"; fi
@if command -v shellcheck >/dev/null; then shellcheck scripts/*.sh; else echo "shellcheck not installed - skipped"; fi @if command -v shellcheck >/dev/null; then shellcheck scripts/*.sh; else echo "shellcheck not installed - skipped"; fi
@if command -v markdownlint-cli2 >/dev/null; then markdownlint-cli2 "**/*.md"; else echo "markdownlint-cli2 not installed - skipped"; fi
# Update all skills to their latest versions and refresh skills-lock.json. # Update all skills to their latest versions and refresh skills-lock.json.
update: update:

View File

@@ -1,20 +1,73 @@
# skills 🧠 # skills 🧠
Reproducible agent skill collection, pinned in `skills-lock.json`. [![GitHub Sponsors](https://img.shields.io/badge/Sponsor-GitHub%20Sponsors-blue?logo=github)](https://github.com/sponsors/kevinveenbirkenbach) [![Patreon](https://img.shields.io/badge/Support-Patreon-orange?logo=patreon)](https://www.patreon.com/c/kevinveenbirkenbach) [![Buy Me a Coffee](https://img.shields.io/badge/Buy%20me%20a%20Coffee-Funding-yellow?logo=buymeacoffee)](https://buymeacoffee.com/kevinveenbirkenbach) [![PayPal](https://img.shields.io/badge/Donate-PayPal-blue?logo=paypal)](https://s.veen.world/paypaldonate)
Works for Claude Code, Codex, Gemini CLI, Cursor, Copilot, Windsurf, Cline, and more.
Homepage: [github.com/kevinveenbirkenbach/skills](https://github.com/kevinveenbirkenbach/skills) The agent skill collection of Kevin Veen-Birkenbach.
Every skill is pinned in `skills-lock.json` for reproducible installs.
First-party skills live under `skills/` and are layered on top of the locked ones at install time.
Compatible with Claude Code, Codex, Gemini CLI, Cursor, Copilot, Windsurf, Cline, and other agents.
## Usage 🚀 ## Requirements 📦
- `make install` installs the skills globally into your home (`~/.agents/skills` and `~/.claude/skills`). - Node.js (provides `npx skills`).
- `make project TARGET=/path/to/repo` copies the skills into a project (`<repo>/.agents/skills` and `<repo>/.claude/skills`). - `jq` (only required for `make update`).
- `make update` refreshes every skill to its latest version and rewrites `skills-lock.json`.
- `make test` validates `skills-lock.json` and shellchecks the scripts; CI runs it on every push and after every update.
Node.js is REQUIRED (`npx skills`); `make update` additionally needs `jq`. ## Installation 🚀
Restart your agent after installing so it loads the new skills.
Install the skills globally into your home (`~/.agents/skills` and `~/.claude/skills`):
```bash
make install
```
Copy the skills into a specific project (`<repo>/.agents/skills` and `<repo>/.claude/skills`):
```bash
make project TARGET=/path/to/repo
```
Restart your agent afterwards so it loads the new skills.
## Maintenance 🔧
Refresh every skill to its latest version and rewrite `skills-lock.json`:
```bash
make update
```
Validate `skills-lock.json` and shellcheck the scripts (also run by CI on every push and after every update):
```bash
make test
```
## Related Tools 🧰
This collection is part of a wider toolset for managing IT and infrastructure:
- [alias](https://github.com/kevinveenbirkenbach/alias) — shell aliases for common commands.
- [package-manager](https://github.com/kevinveenbirkenbach/package-manager) — `pkgmgr`, a single command to install and manage these repositories.
- [infinito-nexus](https://github.com/kevinveenbirkenbach/infinito-nexus) — automation platform for self-hosted infrastructure.
## License 📜
MIT License, Copyright (c) 2026 Kevin Veen-Birkenbach.
## Author 👤 ## Author 👤
Kevin Veen-Birkenbach [kevin@veen.world](mailto:kevin@veen.world) Kevin Veen-Birkenbach [kevin@veen.world](mailto:kevin@veen.world)
## Thanks 🙏
Thanks to the [#ai:infinito.nexus](https://matrix.to/#/%23ai:infinito.nexus) community, and especially to [Paulinhx](https://github.com/Paulinhx) for the inspiration.
## Donation ❤️
If these tools help you, consider supporting their development:
- [GitHub Sponsors](https://github.com/sponsors/kevinveenbirkenbach)
- [Patreon](https://www.patreon.com/c/kevinveenbirkenbach)
- [Buy Me a Coffee](https://buymeacoffee.com/kevinveenbirkenbach)
- [PayPal](https://s.veen.world/paypaldonate)
- Bitcoin: [bc1qle7j8m9kvny2h2q3jzsnc64s4f5zme5re23pzc2lc2d7hhasc4dqachflp](bitcoin:bc1qle7j8m9kvny2h2q3jzsnc64s4f5zme5re23pzc2lc2d7hhasc4dqachflp?message=%E2%9D%A4%EF%B8%8F%20Help%20Build%20the%20Future%20-%20Donation%20to%20Kevin%20Veen-Birkenbach.)

View File

@@ -32,6 +32,11 @@ if [[ ! -d "${src}" ]]; then
exit 1 exit 1
fi fi
if [[ -d "${REPO_ROOT}/skills" ]]; then
log "skills: adding first-party skills from skills/..."
cp -a "${REPO_ROOT}/skills/." "${src}/"
fi
for dst in "${TARGET}/.agents/skills" "${TARGET}/.claude/skills"; do for dst in "${TARGET}/.agents/skills" "${TARGET}/.claude/skills"; do
log "skills: copying -> ${dst}" log "skills: copying -> ${dst}"
rm -rf "${dst}" rm -rf "${dst}"

View File

@@ -48,6 +48,42 @@
"sourceType": "github", "sourceType": "github",
"skillPath": "skills/compress/SKILL.md", "skillPath": "skills/compress/SKILL.md",
"computedHash": "c91bdf427a68b686ee9d6f63766c72d164b16915434736edfedf13e24dae0e8c" "computedHash": "c91bdf427a68b686ee9d6f63766c72d164b16915434736edfedf13e24dae0e8c"
},
"ponytail": {
"source": "DietrichGebert/ponytail",
"sourceType": "github",
"skillPath": "skills/ponytail/SKILL.md",
"computedHash": "210fa0831f649ce9f8a305f7b7c8cf2a0dd09ac4dd53067109941b96e7811d4e"
},
"ponytail-audit": {
"source": "DietrichGebert/ponytail",
"sourceType": "github",
"skillPath": "skills/ponytail-audit/SKILL.md",
"computedHash": "8b8d72e6ff70b65b6c9518d470fcf29dc696eea93d28f3efa10fdcc60cbd696d"
},
"ponytail-debt": {
"source": "DietrichGebert/ponytail",
"sourceType": "github",
"skillPath": "skills/ponytail-debt/SKILL.md",
"computedHash": "0099b82174e3a4261828b4ec04d2c068b1b294fdab14f76701c2acd1f7c99539"
},
"ponytail-gain": {
"source": "DietrichGebert/ponytail",
"sourceType": "github",
"skillPath": "skills/ponytail-gain/SKILL.md",
"computedHash": "c45ecba7906960eea904218b623abf097ebff84ecbacca6d68da1d0687435f75"
},
"ponytail-help": {
"source": "DietrichGebert/ponytail",
"sourceType": "github",
"skillPath": "skills/ponytail-help/SKILL.md",
"computedHash": "10e8382b1033c9a0fec0073de6d3de3846b4f5dca7d78b6df2d476a1f4cd43c6"
},
"ponytail-review": {
"source": "DietrichGebert/ponytail",
"sourceType": "github",
"skillPath": "skills/ponytail-review/SKILL.md",
"computedHash": "4646594fcd75455533f7ff1425f4f1c5ba0fa65f88241bf262b1c4fc275f049a"
} }
} }
} }

32
skills/medusa/SKILL.md Normal file
View File

@@ -0,0 +1,32 @@
---
name: medusa
description: >
Run the MEDUSA AI security scanner (medusa-security on PyPI) against a
repository, working tree, or secret stores. Use when the user asks for a
security scan, supply-chain vetting of a repo before cloning, leaked-secret
detection in AI chat or shell history, or invokes /medusa. Defensive scanning
only.
---
MEDUSA is a defensive AI-first security scanner (40,000+ patterns: CVEs, prompt injection, MCP tool poisoning, leaked secrets).
## Setup
Check availability with `medusa --version`.
If missing, install it with `pipx install medusa-security` (fallback: `pip install --user medusa-security`).
## Commands
| Task | Command |
| --- | --- |
| Scan the current project | `medusa scan .` |
| Vet a remote repo BEFORE cloning (supply chain, poisoned `.claude/` hooks, permissions, skills) | `medusa scan --git <url-or-org/repo>` |
| Find leaked API keys in AI chat + shell history | `medusa secrets scan` |
| Scan one exported file | `medusa secrets scan --path <file>` |
## Rules
- Run scans read-only; never pass `--reveal` and never run `medusa secrets purge` unless the user explicitly asks for it.
- Summarize findings by severity with file/line references; do not paste secret values into the conversation, even redacted ones.
- Treat findings as signals for review, not verdicts; confirm true positives in the code before proposing fixes.
- Vet third-party agent repos (`medusa scan --git`) before recommending the user clone or install them.

View File

@@ -50,6 +50,26 @@ class TestSkillsLock(unittest.TestCase):
f"skills must be sorted ascending; expected {sorted(names)}", f"skills must be sorted ascending; expected {sorted(names)}",
) )
def test_first_party_skills_have_valid_frontmatter(self):
for skill_md in sorted((REPO_ROOT / "skills").glob("*/SKILL.md")):
with self.subTest(skill=skill_md.parent.name):
text = skill_md.read_text(encoding="utf-8")
self.assertTrue(text.startswith("---\n"), "missing frontmatter")
frontmatter = text.split("---", 2)[1]
self.assertIn("name:", frontmatter, "frontmatter missing name")
self.assertIn(
"description:", frontmatter, "frontmatter missing description"
)
def test_first_party_skills_do_not_shadow_locked_skills(self):
locked = set(self.lock["skills"].keys())
for skill_dir in sorted((REPO_ROOT / "skills").glob("*/")):
self.assertNotIn(
skill_dir.name.rstrip("/"),
locked,
f"first-party skill '{skill_dir.name}' collides with a locked skill",
)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()