The problem
Every few months I would open a new assistant and start over. Here is how I want a deck structured for an executive room. Here is what a research pass has to establish before I will write from it. Here is the voice, and here are the four things that break it. The instructions were good, because I had refined them over dozens of conversations. They were also disposable, because they lived in chat.
So I wrote them down as skills, which is the right move and only solves half the problem. Twelve of them accumulated over about a month. Then they scattered. Zip files in one folder, unpacked copies in a hidden directory, and no way to tell which was current. Nine of the twelve carried a copy of the same validation script, and eight of those copies had quietly become different programs. They had the same name, the same job, and different behavior.
Underneath that was a harder problem I could not fix with tidiness.
Every tool wanted the skills somewhere else. Claude Code reads
~/.claude/skills. Cursor reads ~/.cursor/skills. Copilot reads
~/.copilot/skills. Cloud sessions read none of them. Keeping
twelve skills current across four locations by hand is a chore that
gets skipped, and a skill nobody trusts is worse than no skill,
because you still read it before deciding not to use it.
What I built
A single repository, structured around one decision.
The skills live at .agents/skills/. That path is not a
preference. It is the one location that Cursor, GitHub Copilot, VS
Code, OpenAI Codex, Gemini CLI, OpenCode, Goose, and Amp all scan,
at both user scope and project scope. Putting the canonical library
there means any repository that vendors this one is readable by all
of them with no adapter, no build step, and no per-tool
configuration. Claude Code reaches the same folder through a plugin
manifest that points at it, so the repository is simultaneously a
plain skills tree and an installable plugin.
The second decision was a constraint rather than a structure. Every
SKILL.md uses only the six fields in the open Agent Skills
specification. Claude Code accepts roughly twenty more, and every
one of them is a hard error when the skill is uploaded to claude.ai
or the Skills API, not a field that gets politely ignored. One
convenient line of frontmatter is the difference between a skill
that runs anywhere and a skill that runs in one product. Continuous
integration enforces the constraint so I cannot lose the property
by accident.
Around those two decisions sits a small Python toolchain that keeps the library honest: it validates each skill against the specification, catches drift when shared code goes out of sync, packages upload-ready archives with a content hash per skill, and installs the library into whichever directory a given agent expects.
What changed
There is one copy of each skill and the repository is it. When I improve the research procedure, the improvement is in the next session in every tool, not in the one where I happened to make it.
Drift became a build failure instead of a discovery. The nine validator copies were not a hygiene problem I noticed while cleaning up; they were behavior differences I would have found by watching a skill pass a check in one place and fail the same check in another. Now the check runs on every commit and names the file.
Handing the library to a new project takes one command. That is the part I actually wanted. An agent working in a repository that vendors this one arrives already knowing how I want research done and prose written, without me pasting anything into a prompt.
And the twelve skills became a thing rather than twelve things. They cite each other, defer to each other, and share a house layout. Once that was enforceable, adding the thirteenth got easier rather than harder, which is the opposite of how a folder of documents usually behaves.
What I’d do differently
I would choose the canonical directory before writing the first skill, not after twelve. The move itself was cheap. Discovering that a single shared path existed, after months of assuming each tool needed its own copy, was the expensive part.
I would also be slower to eliminate duplication. My first instinct was to delete the nine copies of the validator and keep one at the repository root, which is the obvious refactor and would have broken every one of those skills. A packaged skill has to carry the scripts its own instructions tell an agent to run; the duplication was load-bearing. The right fix was not fewer copies, it was a check that fails when the copies disagree. I nearly shipped the elegant answer to the wrong problem.