Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
## What

<!-- Brief description of the change -->

## Skill Entry

<!-- If adding/updating a skill, paste your JSON entry here -->

```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))
1 change: 0 additions & 1 deletion .github/workflows/validate-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
76 changes: 60 additions & 16 deletions scripts/audit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,34 @@ 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" =~ ^(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"
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 '/' '-')
Expand All @@ -64,28 +88,48 @@ 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

echo "$audit_output"
# 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

# 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}')
audit_json=$(skillshare audit "$audit_target" --threshold high --json 2>/dev/null) || true

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."
# 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
elif [[ "$risk_label" == "HIGH" || "$risk_label" == "CRITICAL" ]]; then
rm -rf "$clone_dir"
log_endgroup
continue
fi

# 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)"

# 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"
Expand Down
8 changes: 8 additions & 0 deletions scripts/validate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 10 additions & 10 deletions skillshare-hub.json
Original file line number Diff line number Diff line change
@@ -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.",
Expand All @@ -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"
]
}
Comment on lines +13 to 22

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For consistency with other entries in this file, I suggest defining the source and skill properties for the new skillshare skill using the established owner/repo pattern. The current use of a full URL for source and the omission of the skill property introduces a new format, which can make parsing and maintenance of this file more complex. Adhering to a single pattern improves maintainability.

    {
      "name": "skillshare",
      "description": "Built-in AI skill for skillshare – helps install, search, and manage skills from the hub",
      "source": "runkids/skillshare",
      "skill": "skillshare",
      "tags": [
        "skillshare",
        "agent",
        "skill"
      ]
    }

]
}