feat: add first-party skills, ponytail lock, and full lint suite
Some checks failed
🧪 Test / 🧪 Lock + lint (push) Has been cancelled
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:
18
.github/workflows/test.yml
vendored
18
.github/workflows/test.yml
vendored
@@ -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
|
||||||
|
|||||||
11
.github/workflows/update.yml
vendored
11
.github/workflows/update.yml
vendored
@@ -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
19
.gitignore
vendored
@@ -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
13
.markdownlint-cli2.jsonc
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"default": true,
|
||||||
|
"MD013": false,
|
||||||
|
"MD033": false,
|
||||||
|
"MD041": false
|
||||||
|
},
|
||||||
|
"ignores": [
|
||||||
|
"node_modules",
|
||||||
|
".agents",
|
||||||
|
".claude"
|
||||||
|
]
|
||||||
|
}
|
||||||
22
LICENSE
22
LICENSE
@@ -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.
|
||||||
|
|||||||
4
Makefile
4
Makefile
@@ -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:
|
||||||
|
|||||||
73
README.md
73
README.md
@@ -1,20 +1,73 @@
|
|||||||
# skills 🧠
|
# skills 🧠
|
||||||
|
|
||||||
Reproducible agent skill collection, pinned in `skills-lock.json`.
|
[](https://github.com/sponsors/kevinveenbirkenbach) [](https://www.patreon.com/c/kevinveenbirkenbach) [](https://buymeacoffee.com/kevinveenbirkenbach) [](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.)
|
||||||
|
|||||||
@@ -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}"
|
||||||
|
|||||||
@@ -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
32
skills/medusa/SKILL.md
Normal 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.
|
||||||
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user