Package skills as standalone downloadable zips via CI - #21
Conversation
Publish each Skyflow skill as a portable .zip via GitHub Releases, in addition to the existing plugin/marketplace install. Skill directories remain the single source of truth; zips are build artifacts and are not committed, so the plugin install is unaffected. - validate-skills.py / validate-skills.yml: PR check that every SKILL.md has valid frontmatter (name matches dir, lowercase-hyphen, <=64 chars; description present, <=1024 chars). - package-skills.sh / package-skills.yml: on a v* tag or manual dispatch, zip each skill (extracts to <skill>/SKILL.md), emit SHA256SUMS.txt, and attach to the matching GitHub Release. - README: Standalone Skill Downloads section with install steps. - CONTRIBUTING: packaging/release process and local build commands. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XxeXNVRY2tk864Dm65HM9R
|
Hey @claude (in GitHub Actions) could you do a code review? |
jstjoe
left a comment
There was a problem hiding this comment.
Code review
Reviewed the diff (scripts + workflows + docs). Overall it's solid and does what it says: skills stay the single source of truth, zips are build-only artifacts published to Releases, and nothing in the plugin/marketplace tree changes so existing /plugin install users are unaffected. Both scripts were run locally against all 6 skills (validate passes; archives extract to <skill>/SKILL.md with CONTRIBUTING.md/.DS_Store excluded).
A few notes, none blocking:
1. Frontmatter parser only handles single-line description (low severity, latent).
.github/scripts/validate-skills.py parses key: value line-by-line. All 6 current skills use single-line name/description, so it's correct today — but a future skill using a YAML block scalar (description: > / | with the text on following indented lines) would parse description as just > and pass validation with an effectively empty description. If we expect multi-line descriptions later, switch to yaml.safe_load on the fenced block (PyYAML is present on ubuntu-latest). Fine to defer until a skill actually needs it.
2. workflow_dispatch with the default tag will create a new git tag.
In package-skills.yml, a manual run falls back to v<plugin.json version>. If that tag doesn't exist yet, gh release create creates it at the checked-out commit (default branch). That's reasonable behavior, just worth being aware of — running dispatch without bumping plugin.json first could tag a version that's already released. The gh release view guard handles the "already exists" case by clobbering assets, so it won't error.
3. Backtick escaping in --notes is correct.
Flagging because it looks risky: the \.zip`sequences inside the double-quoted--notes` are properly escaped, so bash treats them as literal backticks (no command substitution). Verified.
Optional enhancement: an all-in-one skyflow-skills-all.zip bundle for users who want every skill at once — deliberately left out per the packaging decision (per-skill only), easy to add later.
Recommendation: 👍 mergeable as-is. Item 1 is the only thing I'd consider addressing, and only if multi-line descriptions are on the roadmap.
Generated by Claude Code
Parse the SKILL.md frontmatter block with yaml.safe_load instead of a line-by-line key:value scan, so multi-line values (folded/literal block scalars, quoted strings spanning lines) are handled correctly. The old parser would reduce a `description: >` block scalar to just ">" and pass validation with an effectively empty description. - validate-skills.py: extract the --- fenced block and yaml.safe_load it; type-check name/description are non-empty strings. - validate-skills.yml / package-skills.yml: pip install pyyaml before running the validator (setup-python provides a clean interpreter). Addresses review feedback on PR #21. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XxeXNVRY2tk864Dm65HM9R
Summary
Publishes each Skyflow skill as an individual, portable
.zipvia GitHub Releases — in addition to the existing plugin/marketplace install. This makes skills easy to download directly from GitHub and drop into another project or any Agent Skills–compatible harness.The skill directories under
skyflow-skills-plugin/skills/remain the single source of truth. The zips are pure build artifacts: they are not committed (dist/is gitignored) and nothing in the plugin/marketplace tree changes, so vanilla Claude Code plugin users are completely unaffected.Why zip (not
.skill)There is no official
.skillcontainer in the Agent Skills spec — a skill is a directory withSKILL.mdplus resources, and the portable unit understood by Claude Code, claude.ai, and the Agent SDK is a zip of that directory. A custom extension only adds friction, so these ship as plain.zipfiles that unzip to<skill-name>/SKILL.md.What's included
.github/scripts/validate-skills.py— validates everySKILL.mdfrontmatter:namematches its directory, is lowercase-hyphen and ≤64 chars;descriptionpresent and ≤1024 chars..github/scripts/package-skills.sh— zips each skill (extracting to<skill>/SKILL.md, excluding repo-only cruft likeCONTRIBUTING.md/.DS_Store) and emitsSHA256SUMS.txt..github/workflows/validate-skills.yml— PR check: validates skills on any PR that touches them..github/workflows/package-skills.yml— on a pushedv*tag or manual dispatch: validates, packages, and attaches the zips + checksums to the matching GitHub Release. Defaults the tag tov<plugin.json version>for manual runs.curl/unzipinstall steps and thereleases/latest/download/...stable URLs.Testing
Both scripts were run locally against the current 6 skills:
validate-skills.py→ all 6 skills valid.package-skills.sh→ producedcreate-vault.zip,call-rest-apis.zip, etc. +SHA256SUMS.txt; verified each archive extracts to<skill>/SKILL.mdand that excluded files (e.g.call-rest-apis/CONTRIBUTING.md) are absent.Release flow
Bump
versioninskyflow-skills-plugin/.claude-plugin/plugin.json, thengit tag vX.Y.Z && git push origin vX.Y.Z— CI builds and publishes the release. (Or run the Package skills workflow manually from the Actions tab.)🤖 Generated with Claude Code
https://claude.ai/code/session_01XxeXNVRY2tk864Dm65HM9R
Generated by Claude Code