Repository navigation
v0.0.6: discover skills in any repository layout, adopt outside skills, select removals by repository, one-document --json - #1
Merged
Conversation
A project command without SKILL.mod usually means the user is standing in the wrong directory — most often $HOME, whose installation directories are the global scope's — so the project advice now offers --global before suggesting init, and the global scope gets its own advice spelling skillmod --global init/get instead of reusing the project wording. Pins updated per the wording contract; adoption share behavior is unchanged.
A bare path like openai/skills used to become https://openai/skills, which only resolves when the first segment happens to be a host. Complete a two-segment path without a dotted first segment with github.com instead, because that is where skill repositories overwhelmingly live, so skillmod get openai/skills//gh-fix-ci now works. A first segment that carries a dot stays a host, a port or Windows drive letter is left alone, and filesystem paths are untouched. The manifest still records the short form, so the expansion is invisible in SKILL.mod.
Discovery used to look at the repository root and below skills/ only, so a repository that publishes its skills directly in the root, or under an agent directory such as .agents/skills or .claude/skills, was reported as having no SKILL.md at all. Scan the conventional collection roots plus every hidden <name>/skills directory and the root-level directories as well, in a fixed order, with duplicates collapsed. An explicit subdirectory still wins over a name, and the name shorthand now resolves anywhere a skill was found, so a repository only has to look conventional to people who never type a path.
remove only accepted skill names and aliases, so clearing everything that came from one repository meant naming each skill. A positional argument that contains a slash — which a skill name can never do — is now a repository selector: it matches the entries declared from that repository and, when more than one is declared, offers them interactively exactly like get offers a repository's skills. A single-entry repository needs no choice, a run with no channel to ask through lists the candidates instead, and a repository nothing comes from is reported by name. The --agent unlink path resolves the same selectors to names before delegating to share.
--json used to print the human summary on stderr alongside the document on stdout, so a terminal showed the same run twice in two formats, and a command that failed early — a refused selection, a missing manifest, a bad address — printed no document at all, which made the flag useless for automation. In JSON mode every human-readable channel is now closed: no summary, no progress, and one document on stdout whatever happens, with an error field naming the failure and the exit code unchanged. entries always marshals as an array so a consumer never special-cases null. A cobra usage error still exits 1 with plain text on stderr, because no command ran to produce a report.
list refreshed every declared repository with ls-remote on each run, which is what made list --global slow with many skills. Cache each snapshot with the time it was taken and reuse one younger than fifteen minutes, so a repeated list is local-only; update stays the command that deliberately contacts the remote, and an absent, unreadable, or aged-out snapshot is refreshed exactly as before. The fetch time is rewritten on every ls-remote, even when the tags are unchanged, because it is what tells a reader how old its cache is.
Skills that appear in the installation directory after a manual copy, an npx install, or a hand edit were invisible to sync: no declaration, no baseline, nothing keeping them from drifting. sync --adopt now declares each one as a local entry with its current content as the lock baseline, which is what init does for a machine's existing skills. No file is rewritten, so a later edit shows up as local drift instead of being reset; a directory a stale lock record occupies is left for prune, and two names differing only in case are refused rather than written into a declaration no other machine can materialize. A run without the flag only names the undeclared skills and points at it, so the feature is discoverable from the command that would otherwise ignore them. The summary states a run's preserved local edits instead of printing the same stable no-change line as an idle one; drift stays informational, and verify is the command that judges it.
…vior The README and the agent skill read like a spec: long sentences, abstract nouns, and two sections that mostly repeated each other. Rewrite both in plain language, drop "Why this exists" and "What you get" into one short paragraph, and let the five-minute tour use the github.com default. The Chinese README keeps the same outline, as the structure test requires. The references are updated to match the behavior that changed with them: the address grammar and the discovery layouts in manifests.md, the reference-snapshot freshness window in storage.md, the repository selector and sync --adopt in use-cases.md, and the one-document --json contract with its error field in automation.md. CONTRIBUTING.md picks up the branch protection note that was sitting in the stash: changes reach main only through pull requests, so the tag landing procedure waits on the PR CI run rather than a direct push.
A scan that found no skill reported that it had already generated an empty manifest and then returned from the shared non-interactive confirmation gate, which sits before the write phase. A script or CI run therefore claimed a write that never happened, and the prompt it stopped at is one a non-interactive environment cannot answer. An empty scan has nothing to accept, so the gate now applies only when the run has an entry to confirm; it still closes for a scan that found something, since silently accepting a candidate would declare a skill the user never approved. The empty-scan note picks its tense from the run it belongs to: the write path reports the manifest as generated, and a dry run reports the one it would generate, matching the "no files were written" note that follows it in the same report. TestInit_EmptyRunKeepsTheManifestItReports covers the empty write, the dry-run tense, and the still-closed gate, a path no test had reached.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This branch collects four behavior changes and their fixes, each with tests and documentation.
Discover skills wherever a repository puts them
getonly looked at the repository root and belowskills/, so a repositorythat publishes its skills directly in the root, or under an agent directory
such as
.agents/skillsor.claude/skills, was reported as having no SKILL.mdat all. Discovery now scans the repository root, the conventional
skills/collection at any depth, every hidden
<name>/skillscollection, and theroot-level directories, in a fixed precedence order, collapsing duplicates.
An explicit subdirectory still wins over a name.
Complete a bare
owner/repoaddress withgithub.comA bare path like
openai/skillsused to becomehttps://openai/skills, whichonly resolves when the first segment happens to be a host. A two-segment path
whose first segment carries no dot is now completed with
github.com, soskillmod get openai/skills//gh-fix-ciworks; a dotted first segment stays ahost, ports and Windows drive letters are left alone, and filesystem paths are
untouched. A fetch that fails names the URL it was resolved to.
Remove by repository
removeonly accepted skill names and aliases, so clearing everything thatcame from one repository meant naming each skill. A positional argument that
contains a
/— which a skill name can never do — is now a repositoryselector: one entry needs no choice, several are offered interactively exactly
like
getoffers a repository's skills, a non-interactive run lists them fora rerun, and
--alltakes every entry the repository declares. The--agentunlink path resolves the same selectors to names.
One document under
--json--jsonused to print the human summary on stderr alongside the document onstdout, and a command that failed early printed no document at all. Every
human-readable channel is now closed in that mode — no summary, no progress —
and exactly one document is written whatever happens, with an
errorfieldnaming the failure and the exit code unchanged.
entriesalways marshals asan array. Only a cobra usage error keeps plain text on stderr, because no
command ran to produce a report.
Adopt skills added outside skillmod with
sync --adoptSkills that appear in the installation directory after a manual copy, an npx
install, or a hand edit used to be invisible to sync: no declaration, no
baseline.
sync --adoptdeclares each one as a local entry with its currentcontent as the lock baseline, the same thing
initdoes for a machine'sexisting skills. No file is rewritten, so a later edit shows up as local drift
instead of being reset. A plain
synconly names them and points at the flag,--dry-runplans without asking, and--checkrefuses the combinationinstead of silently ignoring it.
Other fixes
initon a machine with no existing skills claimed an empty manifest it hadnot written, because the non-interactive confirmation gate sat before the
write phase. The gate now applies only when there is something to confirm.
means the wrong directory, so the advice offers
--globalbefore suggestinginit.listanswers from the cached reference snapshot younger than fifteenminutes, which keeps
list --globalfast;updateremains the command thatdeliberately refreshes.
references cover the address grammar, the discovery layouts, the freshness
window, the repository selector,
sync --adopt, and the one-document--jsoncontract.
make check,go test -race,govulncheck, the cross-platform test matrix,and all six release cross-builds pass on this branch.