From 96cc2d43c0e5f12a15d1793dc4193809ba04e76b Mon Sep 17 00:00:00 2001 From: Willie Date: Fri, 13 Feb 2026 00:44:00 +0800 Subject: [PATCH 1/3] Replace ascii-box-check with skillshare skill in hub registry Remove ascii-box-check skill and add the built-in skillshare skill for installing, searching, and managing skills from the hub. --- skillshare-hub.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/skillshare-hub.json b/skillshare-hub.json index 08da980..4c87b42 100644 --- a/skillshare-hub.json +++ b/skillshare-hub.json @@ -1,16 +1,6 @@ { "schemaVersion": 1, "skills": [ - { - "name": "ascii-box-check", - "description": "Verify and fix ASCII box-drawing diagram alignment in markdown files", - "source": "runkids/my-skills", - "skill": "ascii-box-check", - "tags": [ - "docs", - "workflow" - ] - }, { "name": "skill-creator", "description": "Guide for creating effective skills. This skill should be used when users want to create a new skill (or update an existing skill) that extends Claude's capabilities with specialized knowledge, workflows, or tool integrations.", @@ -19,6 +9,16 @@ "tags": [ "skill" ] + }, + { + "name": "skillshare", + "description": "Built-in AI skill for skillshare – helps install, search, and manage skills from the hub", + "source": "https://github.com/runkids/skillshare/tree/main/skills/skillshare", + "tags": [ + "skillshare", + "agent", + "skill" + ] } ] } From fc0cb59c1caa3baea23df173e54d4ed04e754763 Mon Sep 17 00:00:00 2001 From: Willie Date: Fri, 13 Feb 2026 01:12:51 +0800 Subject: [PATCH 2/3] fix(audit): parse GitHub browser URLs and subpaths correctly The audit script now handles all source formats: - GitHub browser URL: https://github.com/owner/repo/tree/branch/path - GitHub domain shorthand: github.com/owner/repo/path - GitHub shorthand: owner/repo/path - Plain HTTP URLs (non-GitHub) For sources with subpaths, only the subdirectory is audited instead of the entire cloned repo. --- scripts/audit.sh | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/scripts/audit.sh b/scripts/audit.sh index 8b86835..cac8565 100755 --- a/scripts/audit.sh +++ b/scripts/audit.sh @@ -44,10 +44,27 @@ details=() while IFS= read -r source; do [ -z "$source" ] && continue - if [[ "$source" == http* ]]; then + # Parse source into clone_url + optional subpath + subpath="" + if [[ "$source" =~ ^https?://github\.com/([^/]+/[^/]+)/tree/[^/]+/(.+)$ ]]; then + # GitHub browser URL: https://github.com/owner/repo/tree/branch/path + clone_url="https://github.com/${BASH_REMATCH[1]}.git" + subpath="${BASH_REMATCH[2]}" + elif [[ "$source" =~ ^https?://github\.com/([^/]+/[^/]+)/?$ ]]; then + # GitHub repo URL (no subpath): https://github.com/owner/repo + clone_url="https://github.com/${BASH_REMATCH[1]}.git" + elif [[ "$source" =~ ^github\.com/([^/]+/[^/]+)(/(.+))?$ ]]; then + # github.com shorthand: github.com/owner/repo[/path] + clone_url="https://github.com/${BASH_REMATCH[1]}.git" + subpath="${BASH_REMATCH[3]}" + elif [[ "$source" == http* ]]; then + # Other HTTP URLs — use as-is clone_url="$source" else - clone_url="https://github.com/${source}.git" + # GitHub shorthand: owner/repo[/path] + owner_repo=$(echo "$source" | cut -d'/' -f1-2) + subpath=$(echo "$source" | cut -d'/' -f3-) + clone_url="https://github.com/${owner_repo}.git" fi safe_name=$(echo "$source" | tr '/' '-') @@ -64,7 +81,13 @@ while IFS= read -r source; do continue fi - audit_output=$(skillshare audit "$clone_dir" --threshold high 2>&1 | sed 's/\x1b\[[0-9;]*m//g') || true + # Audit subpath if specified, otherwise audit entire clone + audit_target="$clone_dir" + if [ -n "$subpath" ] && [ -d "$clone_dir/$subpath" ]; then + audit_target="$clone_dir/$subpath" + fi + + audit_output=$(skillshare audit "$audit_target" --threshold high 2>&1 | sed 's/\x1b\[[0-9;]*m//g') || true echo "$audit_output" From b18c28ed122485060447da721f05156156a67709 Mon Sep 17 00:00:00 2001 From: Willie Date: Fri, 13 Feb 2026 02:05:33 +0800 Subject: [PATCH 3/3] improve CI: JSON audit parsing, Gitea support, duplicate source check - Remove skillshare init workaround (path mode no longer needs config) - Switch audit.sh from sed/awk text parsing to --json + jq for reliable risk extraction - Add Gitea/Forgejo URL parsing (src/branch/ pattern) for non-GitHub skill sources - Add duplicate source validation in validate.sh --- .github/PULL_REQUEST_TEMPLATE.md | 20 +++++++++++++ .github/workflows/validate-pr.yml | 1 - scripts/audit.sh | 49 ++++++++++++++++++++++--------- scripts/validate.sh | 8 +++++ 4 files changed, 63 insertions(+), 15 deletions(-) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..7627062 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,20 @@ +## What + + + +## Skill Entry + + + +```json + +``` + +## Checklist + +- [ ] `skillshare-hub.json` is valid JSON +- [ ] `name` is lowercase with hyphens only +- [ ] `description` is a clear one-liner +- [ ] `source` repo is publicly accessible and contains a valid `SKILL.md` +- [ ] No duplicate skill name in the hub +- [ ] Tags are 1-3 lowercase words (see [CONTRIBUTING.md](../CONTRIBUTING.md#tags)) diff --git a/.github/workflows/validate-pr.yml b/.github/workflows/validate-pr.yml index 022a7fc..596efe0 100644 --- a/.github/workflows/validate-pr.yml +++ b/.github/workflows/validate-pr.yml @@ -31,7 +31,6 @@ jobs: run: | curl -fsSL https://raw.githubusercontent.com/runkids/skillshare/main/install.sh | sh skillshare version - skillshare init -g --no-copy --no-targets --no-git --no-skill - name: Audit new skills id: audit diff --git a/scripts/audit.sh b/scripts/audit.sh index cac8565..f3f53cc 100755 --- a/scripts/audit.sh +++ b/scripts/audit.sh @@ -57,6 +57,13 @@ while IFS= read -r source; do # github.com shorthand: github.com/owner/repo[/path] clone_url="https://github.com/${BASH_REMATCH[1]}.git" subpath="${BASH_REMATCH[3]}" + elif [[ "$source" =~ ^(https?://[^/]+)/([^/]+/[^/]+)/src/branch/[^/]+/(.+)$ ]]; then + # Gitea/Forgejo browser URL: https://host/owner/repo/src/branch/main/path + clone_url="${BASH_REMATCH[1]}/${BASH_REMATCH[2]}.git" + subpath="${BASH_REMATCH[3]}" + elif [[ "$source" =~ ^(https?://[^/]+)/([^/]+/[^/]+)/src/branch/ ]]; then + # Gitea/Forgejo repo URL (branch root, no subpath) + clone_url="${BASH_REMATCH[1]}/${BASH_REMATCH[2]}.git" elif [[ "$source" == http* ]]; then # Other HTTP URLs — use as-is clone_url="$source" @@ -87,28 +94,42 @@ while IFS= read -r source; do audit_target="$clone_dir/$subpath" fi - audit_output=$(skillshare audit "$audit_target" --threshold high 2>&1 | sed 's/\x1b\[[0-9;]*m//g') || true + audit_json=$(skillshare audit "$audit_target" --threshold high --json 2>/dev/null) || true - echo "$audit_output" + # Check if output is valid audit JSON + if ! echo "$audit_json" | jq -e '.summary' >/dev/null 2>&1; then + # Not valid JSON — command errored (e.g. path not found) + audit_err=$(skillshare audit "$audit_target" --threshold high 2>&1) || true + echo "$audit_err" + results+=("| \`${source}\` | :x: Error | - |") + details+=("DETAIL_SEP### \`${source}\`"$'\n'"\`\`\`"$'\n'"${audit_err}"$'\n'"\`\`\`") + failed=1 + rm -rf "$clone_dir" + log_endgroup + continue + fi - # Extract risk score and label - risk=$(echo "$audit_output" | sed -n 's/.*Risk: \([A-Z]* ([0-9]*\/[0-9]*)\).*/\1/p' | tail -1) - [ -z "$risk" ] && risk="N/A" - risk_label=$(echo "$risk" | awk '{print $1}') + # Extract risk info with jq + risk_score=$(echo "$audit_json" | jq -r '.summary.riskScore') + risk_label=$(echo "$audit_json" | jq -r '.summary.riskLabel' | tr '[:lower:]' '[:upper:]') + risk="${risk_label} (${risk_score}/100)" - if echo "$audit_output" | grep -q "config not found"; then - results+=("| \`${source}\` | :x: No config | - |") - details+=("DETAIL_SEP### \`${source}\`"$'\n'"No skillshare config found. Run \`skillshare init\` in the source repo.") - log_error "No skillshare config found for ${source}. Run 'skillshare init' in the source repo." - failed=1 - elif [[ "$risk_label" == "HIGH" || "$risk_label" == "CRITICAL" ]]; then + # Format findings for display + findings_text=$(echo "$audit_json" | jq -r ' + [.results[] | (.findings // [])[] | " \(.severity): \(.message) (\(.file):\(.line))\n \"\(.snippet)\""] + | join("\n\n")') + audit_display="Risk: ${risk}" + [ -n "$findings_text" ] && audit_display="${audit_display}"$'\n\n'"${findings_text}" + echo "$audit_display" + + if [[ "$risk_label" == "HIGH" || "$risk_label" == "CRITICAL" ]]; then results+=("| \`${source}\` | :x: Risk ${risk_label} | ${risk} |") - details+=("DETAIL_SEP### \`${source}\`"$'\n'"\`\`\`"$'\n'"${audit_output}"$'\n'"\`\`\`") + details+=("DETAIL_SEP### \`${source}\`"$'\n'"\`\`\`"$'\n'"${audit_display}"$'\n'"\`\`\`") log_error "Risk ${risk_label} for ${source}" failed=1 else results+=("| \`${source}\` | :white_check_mark: Passed | ${risk} |") - details+=("DETAIL_SEP### \`${source}\`"$'\n'"\`\`\`"$'\n'"${audit_output}"$'\n'"\`\`\`") + details+=("DETAIL_SEP### \`${source}\`"$'\n'"\`\`\`"$'\n'"${audit_display}"$'\n'"\`\`\`") fi rm -rf "$clone_dir" diff --git a/scripts/validate.sh b/scripts/validate.sh index f4c10f9..035f098 100755 --- a/scripts/validate.sh +++ b/scripts/validate.sh @@ -31,6 +31,14 @@ if [ -n "$dupes" ]; then fi echo "OK: no duplicate names" +# Check no duplicate sources +dupe_sources=$(jq -r '[.skills[].source] | group_by(.) | map(select(length > 1)) | flatten | unique | .[]' "$HUB_FILE") +if [ -n "$dupe_sources" ]; then + echo "ERROR: Duplicate skill sources: $dupe_sources" + exit 1 +fi +echo "OK: no duplicate sources" + # Check name format (lowercase, hyphens only) bad_names=$(jq -r '.skills[].name | select(test("^[a-z0-9][a-z0-9-]*$") | not)' "$HUB_FILE") if [ -n "$bad_names" ]; then