feat: list Agent Skills a repo publishes - #8
Conversation
|
Warning Review limit reached
Next review available in: 58 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Central YAML (base), Organization UI (inherited) Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (7)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
usage.sh now detects `skills/<name>/SKILL.md` and lists what it finds, the layout from https://agentskills.io/specification. Detecting the convention that already exists means a repo shipping skills for Claude Code, Cursor or Copilot gets a page here for free, with no second place to publish to. This is the part of jdx/mise#9479 that does not need a packaging channel. The objection to bridging tool-bundled skills was that 86.5% of mise's registry is binary tarballs with nowhere to put markdown, and that auto-injecting third-party prose into an agent's context is a supply-chain surface. Serving them from a repo sidesteps both: nothing ships in the tarball, and the agent pulls a skill for a CLI it asked about rather than having prose pushed at it for every installed tool. So the page serves skills, and does not rank, recommend or merge them. Provenance stays attached — the name, the license, and a link to the file in the repo it came from — and the section says the vendor published it, not usage.sh. skills.ts reads the four scalar fields the spec defines and skips the rest. It is deliberately not a YAML parser: the fields are all scalars, so it handles plain, quoted, folded and literal values and drops anything it cannot read, which costs a missing subtitle where a general parser would cost far more bundle than four fields justify. A skill without a description — which the spec requires — is skipped rather than shown half-parsed. Verified against real published files, not just fixtures: anthropics/skills brand-guidelines, crazyguitar/pysheeet, and tanweai/pua, which between them cover a bare description, a quoted one containing em-dashes and CJK, and a license field. Reading skills is capped per repo, since the cost is one fetch each on a cold hit for any repo anyone visits. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Rebased onto
This comment was generated by an AI coding assistant. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit b2a974e. Configure here.
Greptile SummaryThis PR adds repository-published Agent Skill discovery and presentation.
Confidence Score: 3/5This PR is not yet safe to merge because valid skills can be omitted and invalid or incorrectly parsed skill metadata can still be published. The previously reported defects remain in current HEAD: discovery truncates directories before validating skills, missing names are synthesized, and the scalar parser diverges from valid YAML behavior before its output reaches HTML and JSON consumers. Files Needing Attention: src/forges/github.ts, src/skills.ts, src/skills.test.ts Important Files Changed
Reviews (3): Last reviewed commit: "fix: tell the two empty skill states apa..." | Re-trigger Greptile |
|
|
||
| const dirs = entries | ||
| .filter((e) => e.type === "dir") | ||
| .map((e) => e.name) | ||
| .sort() |
There was a problem hiding this comment.
Cap excludes valid skill directories
When more than 25 directories exist under skills/, this truncates the sorted directory list before checking for SKILL.md, so unrelated or invalid directories can consume the limit and cause valid published skills to be omitted or the page to report that no skills exist.
| const { fields, body } = parseFrontmatter(source); | ||
| const name = fields.name ?? dir; | ||
| const description = fields.description; | ||
| if (!description) return null; | ||
|
|
| const match = /^([A-Za-z][\w-]*):[ \t]*(.*)$/.exec(line); | ||
| if (!match) continue; | ||
|
|
||
| const key = match[1]; | ||
| let value = match[2].trim(); | ||
|
|
||
| if (value === "|" || value === ">" || /^[|>][-+]?$/.test(value)) { | ||
| // Block scalar: take the indented lines that follow. | ||
| const folded = value.startsWith(">"); | ||
| const block: string[] = []; | ||
| while (i + 1 < lines.length && /^(\s+|$)/.test(lines[i + 1])) { | ||
| block.push(lines[++i].replace(/^\s{1,4}/, "")); | ||
| } | ||
| value = folded | ||
| ? block.join(" ").replace(/\s+/g, " ").trim() | ||
| : block.join("\n").trimEnd(); | ||
| } else if ( | ||
| (value.startsWith('"') && value.endsWith('"') && value.length > 1) || | ||
| (value.startsWith("'") && value.endsWith("'") && value.length > 1) | ||
| ) { | ||
| value = value.slice(1, -1); | ||
| if (line.includes('"')) value = value.replace(/\\"/g, '"'); |
There was a problem hiding this comment.
Valid YAML scalars are misparsed
When valid frontmatter uses YAML features such as an inline comment, double-quoted escapes, or block content indented by more than four spaces, this partial parser retains comment text, leaves escapes literal, or preserves unintended indentation, causing incorrect descriptions and metadata in both the rendered page and JSON output.
`skills()` returned null both when there was no `skills/` directory and when there was one that yielded nothing readable, so the page claimed a repo had no skills while the API had just listed the directory — loose files instead of subdirectories, a missing SKILL.md, or frontmatter that would not parse all landed on the wrong message. Null now means the directory is absent and an empty array means it is there but unreadable, and the page says so, pointing at what a SKILL.md needs. Separately, a quoted or block `description` holding only whitespace passed the required-field check, because the value was tested for truthiness before being trimmed. `description: " "` listed a skill with no description at all. Values are trimmed before the check now, covered both ways. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Both correct; fixed. Skills empty state misleading — the important one, because it made the page assert something false. Whitespace-only description — also right, and it defeated the check I had written specifically to avoid listing half-parsed skills. The value was tested for truthiness before being trimmed, so 27/27 tests, typecheck and build clean. This comment was generated by an AI coding assistant. |

Stacked on #7 — review that first; this diff is only the skills work.
usage.sh now detects
skills/<name>/SKILL.mdand lists what it finds, following the agentskills layout. Detecting a convention that already exists means a repo shipping skills for Claude Code, Cursor or Copilot gets a page here for free, with no second place to publish to.Why this is the version of jdx/mise#9479 that works
The objection to mise bridging tool-bundled skills was concrete: 86.5% of mise's registry is binary tarballs with nowhere to put markdown, and auto-injecting third-party prose into an agent's context is a supply-chain surface — 37% of skills in public registries carry a security flaw.
Serving them from the repo sidesteps both. Nothing ships in the tarball, so the packaging problem disappears and it works for every backend. And the trust posture inverts: an agent pulls a skill for a CLI it asked about, instead of prose being pushed at it for every installed tool.
So the page serves skills and does not rank, recommend or merge them. Provenance stays attached — name, license, and a link to the file in the repo — and the section says the vendor published it, not usage.sh.
On not using a YAML parser
skills.tsreads the four scalar fields the spec defines and skips the rest. That is a deliberate departure from the reasoning in #7, where I argued for a real KDL parser because a subtly wrong command tree is worse than none.The difference is blast radius. A mis-parsed spec produces a plausible-but-wrong command tree; a mis-parsed frontmatter costs a missing subtitle. The fields are all scalars, so this handles plain, quoted, folded (
>) and literal (|) values and drops anything it cannot read. A general YAML parser is ~100KB for four fields.A skill with no
description— which the spec requires — is skipped rather than shown half-parsed. It is someone else's file, not ours to guess at.Verified against real files, not just fixtures
anthropics/skillsbrand-guidelineslicenseas free textcrazyguitar/pysheeettanweai/pualicense: MITAll three parse correctly. 11 unit tests cover the forms the spec allows and the ones it does not: quoted, folded, literal, CRLF, nested
metadata:blocks not leaking into fields, missing description, absent and unterminated frontmatter.Cost
One directory listing plus one fetch per skill, capped at 25. That is paid on a cold hit for any repo anyone visits, and nothing stops a repo having a hundred directories under
skills/. Both cached at the edge for 10 minutes like the rest.npm test26/26 ·npm run typecheck0 errors ·npm run buildclean.Next
MCP over both halves —
get_cli,get_command,get_skill— now that there is a command tree and a skill list to expose.This PR was generated by an AI coding assistant.
Note
Medium Risk
Adds up to 26 GitHub/raw fetches per cold repo page (listing + capped skill files) and surfaces third-party markdown with attribution only; no auth or data-store changes.
Overview
Adds automatic discovery and listing of Agent Skills at
skills/<name>/SKILL.mdon GitHub repo pages, alongside existing Usage command specs.A new
skills.tsmodule parsesSKILL.mdfrontmatter with a small scalar-only reader (no full YAML dependency): requireddescription, optionallicense/compatibility/allowed-tools, and skips invalid or half-parsed files.Forge.skills()is added to the forge interface; the GitHub adapter listsskills/via the Contents API, fetches eachSKILL.mdfrom raw.githubusercontent.com, caps at 25 skills, and distinguishesnull(noskills/dir) from[](dir exists but nothing parseable).repoDataloads skills in parallel with other sections;?format=jsonincludes them too. The repo Astro page gets a Skills section with cards (name, description, link to source on GitHub) and copy that skills are published by the repo, not usage.sh.11 unit tests cover frontmatter edge cases; global.css adds card layout for the section.
Reviewed by Cursor Bugbot for commit 8eb0451. Bugbot is set up for automated code reviews on this repo. Configure here.