YangZhiqiang98 asked (#1449) that the Library find which skills have
updates and update them. magpie already checked skills from GitHub (a
market install among them) by hand and could update them, but an update
deleted the library's folder, so a skill the user had changed (by hand,
through an agent's link, or in an agent's copy not yet taken in) was lost
without a word, and nothing checked unless the user pressed Check.
- A skill is "changed here" when its library folder no longer hashes to
what was fetched (Finder's .DS_Store aside), or an agent's copy was
edited since its mark and not yet taken in (editState, skill_changed.go).
The view says so (SkillView.edited) and the row shows "Changed here".
- UpdateSkill refuses such a skill with *EditedError unless told to
replace it. The row's Update asks in the page first (Keep mine / Update;
Update sends replace). Update N, the group's Update N and the CLI's
update-all leave it as it is and say so. The CLI takes
`magpie library skill update <name> --replace-edits`.
- The version an update replaces is moved to the backups when it was
changed, or when it can't be told (a skill with no hash from before).
- View.checkDue: no check since magpie started, or none for 12 hours. The
page then runs the check once by itself as the skills open, and says
only what there is to update or add.
Not checked, by design: skills the skills CLI manages (`npx skills
update` updates them; magpie only groups them), linked local folders,
skills imported from an agent with no source, and From-only skills have
no remote; CC Switch-installed skills can still be updated by hand.
Verified:
- go test -tags nogui ./... (sandbox HOME); new TestUpdateKeepsASkillChangedHere,
TestUpdateKeepsACopyEditedInAnAgent, TestUpdateKeepsWhatItCantTell,
TestCheckDue, TestCheckSkillsMarksItDone, all from httptest. With
editState forced to "not changed" the first three fail.
- gofmt -l, go vet, GOOS=linux and GOOS=windows go vet -tags nogui on
./internal/library ./internal/gui ./internal/tui .
- GUI: new skill-update-edited.test.cjs (Chromium and WebKit, 860 and
400px, en and zh; no scroll on click; fails with the row's ask removed),
plus the 22 other library skill tests, gui-ja, gui-de, gui-zh-tw,
i18n-keys and click-scroll: 262 pass.
- Real use in a sandbox HOME with github.com/anthropics/skills: installed
pdf and docx, the real check said both current; an edit through Claude
Code's link was refused by `skill update pdf`, skipped by `skill update`
(docx updated), replaced by `--replace-edits` with the edit in
backups/<time>/library/skills/pdf; docx edited later shows edited, a
.DS_Store doesn't.
Until now the only action on such a row was Bring in: removing it meant
bringing it in and then removing it from the library. RemoveFoundSkill
(skills/remove-found) takes out of each agent that has it its folder, or
its copy of the very same files, and its links; its entry in the shared
~/.agents/skills, which agents read too, and a folder of it in the
library's own folder go as well. Nothing is deleted: folders go to the
backups (setAside), links are taken away, and a folder elsewhere that the
agents only linked to stays where it is. Another skill by that name
(Others) is a different skill and stays. The page asks first and says
which agents it leaves and what stays.
TestRemoveFoundSkill (library), TestLibraryPage's new part (TUI) and
remove-found-skill.test.cjs (GUI) fail without the change. The GUI test
fails 4 of 4 with library.js from before it, and passes in Chromium and
WebKit, in en, zh, ja and de, and at 440px wide.
Run: go test -tags nogui ./internal/library ./internal/tui ./internal/gui,
and go vet for linux, windows and darwin with -tags nogui, in Docker
(golang:1.26.8, a temporary HOME). The Mac's syspolicyd held test binaries
at launch. On the Mac, in Chromium and WebKit: remove-found-skill, gui-ja,
gui-de, i18n-keys, import-all-skills, own-skill, shared-skills,
library-own-folder, library-items, remove-all-skills,
library-desktop-skills, skill-name-many-agents and mobile-web, 133 of 133.
In one earlier run of that set at load ~40, mobile-web's WebKit "idle
scripts cannot scroll" failed once. It passed 3 of 3 alone with this
change, and passed in the full rerun. Not tried: the reporter's own agents,
and a WSL agent's folder.
installFrom stopped the whole set at the first skill whose name was taken,
and returned before change saved library.json. The folders it had already
copied for the skills before that one stayed in the library's folder,
unlisted. Every later try then failed on the first of them with "the
library's folder already has a caveman-commit in it".
Now each skill is installed on its own (InstallSkills, InstallMarketSkill
and AddNewSkills, through installChange):
- the library lists it from the same source: had already (Result.Had),
nothing written, so the user's edits stay;
- a folder in the library's folder holding exactly its files (not a link,
sameTree, which counts a failed read as different): listed where it is,
as had;
- another skill by that name, listed or only a folder, or a folder that
can't be read: left as it is, in Result.Skipped with why;
- the rest are installed (Result.Installed). Only when nothing was
installed or had does the change fail, with the first reason.
The GUI's toast and the TUI's line say how many were installed, the ones
already in the library and each one skipped with why.
Verified (temp HOME, pinned caches):
- go vet -tags nogui ./internal/library ./internal/tui ./internal/gui .;
GOOS=windows go build and GOOS=linux go build -tags nogui of
internal/library and internal/tui: clean
- go test -tags nogui ./internal/library ./internal/tui ./internal/gui: ok
- with the old skills.go, market.go and skillnew.go, TestInstallSetSkipsOneInTheWay,
TestInstallListsItsOwnLeftoverFolder (fails with lc's exact "the library's
folder already has a caveman-commit in it") and
TestInstallSetSkipsAnotherByTheSameName FAIL; TestSkillsFromGitHub's
reinstall now asserts Had
- skill-install-skip.test.cjs (Chromium + WebKit, en and zh at 440px) fails
on the old library.js ("status ok", no names) and passes now; with
gui-ja, gui-de and 11 library GUI files, 99/100 passed, and the one that
failed (by-agent, Chromium, at load average ~150) passes 3 times in a row
in both engines when run alone
- not tried in the real app, or with lc's library