Skip to content

docs: discover LLM models at runtime #61

docs: discover LLM models at runtime

docs: discover LLM models at runtime #61

name: Validate Skills
on:
pull_request:
paths:
- "higgsfield-*/**"
- ".claude-plugin/**"
- ".codex-plugin/**"
- ".cursor-plugin/**"
- "VERSION"
- ".github/workflows/validate-skills.yml"
push:
branches: [main]
paths:
- "higgsfield-*/**"
- ".claude-plugin/**"
- ".codex-plugin/**"
- ".cursor-plugin/**"
- "VERSION"
- ".github/workflows/validate-skills.yml"
jobs:
validate:
name: Frontmatter, version sync, and references
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install PyYAML
run: pip install pyyaml --quiet
- name: Validate frontmatter and name
run: |
set -euo pipefail
fail=0
for skill_dir in higgsfield-*/; do
skill_name="${skill_dir%/}"
skill_md="${skill_dir}SKILL.md"
if [ ! -f "$skill_md" ]; then
echo "::error::$skill_md missing"
fail=1
continue
fi
python3 - "$skill_md" "$skill_name" <<'PY' || fail=1
import sys, yaml, re, pathlib
path = pathlib.Path(sys.argv[1])
expected_name = sys.argv[2]
text = path.read_text()
m = re.match(r"^---\n(.*?)\n---\n", text, re.DOTALL)
if not m:
print(f"::error file={path}::no YAML frontmatter")
sys.exit(1)
fm = yaml.safe_load(m.group(1))
errors = []
if fm.get("name") != expected_name:
errors.append(f"name '{fm.get('name')}' != directory '{expected_name}'")
if not fm.get("version"):
errors.append("version missing")
if not fm.get("description"):
errors.append("description missing")
desc = fm.get("description", "")
if len(desc) > 1024:
errors.append(f"description exceeds 1024 characters ({len(desc)})")
if "Use when" not in desc:
errors.append("description missing 'Use when' trigger phrases")
if "NOT for" not in desc:
errors.append("description missing 'NOT for' boundary")
for err in errors:
print(f"::error file={path}::{err}")
if errors:
sys.exit(1)
print(f"✓ {path} — frontmatter valid (name={fm['name']}, version={fm['version']})")
PY
done
[ "$fail" -eq 0 ]
- name: Validate version sync across all manifests
run: |
set -euo pipefail
ROOT_VERSION="$(cat VERSION | tr -d '[:space:]')"
echo "Root VERSION: $ROOT_VERSION"
fail=0
for skill_dir in higgsfield-*/; do
v=$(awk '/^---/{c++; next} c==1 && /^version:/{gsub(/^version:[[:space:]]*/,""); gsub(/[[:space:]]*#.*$/,""); gsub(/[[:space:]]/,""); print; exit}' "${skill_dir}SKILL.md")
if [ "$v" != "$ROOT_VERSION" ]; then
echo "::error file=${skill_dir}SKILL.md::version $v != VERSION $ROOT_VERSION"
fail=1
fi
done
for f in .claude-plugin/plugin.json .codex-plugin/plugin.json .cursor-plugin/plugin.json; do
v=$(python3 -c "import json,sys; print(json.load(open('$f'))['version'])")
if [ "$v" != "$ROOT_VERSION" ]; then
echo "::error file=$f::version $v != VERSION $ROOT_VERSION"
fail=1
fi
done
# marketplace.json — version is nested under plugins[0].version
v=$(python3 -c "import json; print(json.load(open('.claude-plugin/marketplace.json'))['plugins'][0]['version'])")
if [ "$v" != "$ROOT_VERSION" ]; then
echo "::error file=.claude-plugin/marketplace.json::plugins[0].version $v != VERSION $ROOT_VERSION"
fail=1
fi
[ "$fail" -eq 0 ] && echo "✓ all version locations match $ROOT_VERSION"
- name: Validate marketplace.json lists every skill folder
run: |
set -euo pipefail
fail=0
listed=$(python3 -c "import json; print(' '.join(s['path'] for s in json.load(open('.claude-plugin/marketplace.json'))['plugins'][0]['skills']))")
for skill_dir in higgsfield-*/; do
skill_name="${skill_dir%/}"
if ! echo " $listed " | grep -q " $skill_name "; then
echo "::error::skill folder '$skill_name' is not listed in .claude-plugin/marketplace.json"
fail=1
fi
done
[ "$fail" -eq 0 ] && echo "✓ marketplace.json includes every skill folder"
- name: Validate references resolve and no orphans
run: |
set -euo pipefail
fail=0
for skill_dir in higgsfield-*/; do
skill_md="${skill_dir}SKILL.md"
if [ ! -d "${skill_dir}references" ]; then
continue
fi
# every reference link in SKILL.md must exist
for ref in $(grep -oE 'references/[a-zA-Z0-9_./-]+\.md' "$skill_md" | sort -u); do
if [ ! -f "${skill_dir}${ref}" ]; then
echo "::error file=$skill_md::references $ref but ${skill_dir}${ref} missing"
fail=1
fi
done
# every references/*.md file must be linked from SKILL.md
for f in "${skill_dir}references"/*.md; do
base=$(basename "$f")
if ! grep -q "$base" "$skill_md"; then
echo "::error file=$f::orphaned reference (not linked from $skill_md)"
fail=1
fi
done
done
[ "$fail" -eq 0 ] && echo "✓ all references resolve, no orphans"
- name: Validate no parent-dir references in SKILL.md or references/
run: |
set -euo pipefail
fail=0
for skill_dir in higgsfield-*/; do
# Match ../ at start of line or after non-dot, non-slash to skip URLs like "..." in https://...
if grep -nE '(^|[^./])\.\./' "${skill_dir}SKILL.md" 2>/dev/null; then
echo "::error file=${skill_dir}SKILL.md::contains parent-dir (../) reference; skill must be self-contained"
fail=1
fi
if [ -d "${skill_dir}references" ]; then
if grep -rnE '(^|[^./])\.\./\.\./' "${skill_dir}references/" 2>/dev/null; then
echo "::error::${skill_dir}references/ contains ../../ paths that will break inside an installed bundle"
fail=1
fi
fi
done
[ "$fail" -eq 0 ] && echo "✓ no parent-dir references; skills are self-contained"