Engineering · Aug 19, 2026 · 7 min read

Skills that travel with you, not with the repo

Agent skills live in dotfolders on one machine and die there. LLMBrain makes them account-scoped records you push, pull and share like projects — plus the access bug our tests caught before it shipped.

Agent skills — those little folders of instructions that teach Claude Code your deploy process or your review checklist — have a locality problem. They live in a dotfolder on one machine. Write a good one and it doesn't follow you to your laptop, doesn't reach your teammate, and quietly forks into three diverging copies the moment anyone copies it around.

Skills are memory. So they now live in the brain: account-scoped records with files, pushed and pulled through the CLI, shareable with teammates under the same read/write grants as projects.

llmbrain skills push code-review     # publish your local skill
llmbrain skills pull code-review     # on any other machine
llmbrain skills pull ana:deploy      # a skill shared with you

One store on disk, symlinks for everyone

The install-side design question: where do pulled skills live? Claude Code reads ~/.claude/skills, Codex and opencode read .agents/skills, and putting real copies in both places recreates the forking problem locally.

The answer is one store — .agents/skills, the cross-agent location — and a relative symlink per skill for Claude Code: ../../.agents/skills/<name>. That same relative string resolves correctly from both the global ~/.claude/skills/<name> and a per-project <project>/.claude/skills/<name>, which is what lets one code path serve both scopes. And the CLI never replaces a real directory with a link — ~/.claude/skills legitimately holds hand-written skills that predate the brain, and stomping one would be data loss dressed as tidiness.

Notice what the brain itself doesn't know: scope. Global versus per-project is a fact about your machine, decided at pull time (--global / --local, or a prompt), stored nowhere upstream. The brain stores files; where they land is the CLI's business.

The bug that looked exactly like success

The sharing model produced the best bug of the slice. Skills are addressed like projects: a bare name for your own, handle:name for one shared with you. The first cut ran every save through the same resolver reads use — which meant that if a teammate had granted you write on their deploy skill, your innocent skills push deploy silently overwrote their skill instead of creating your own. And a read-grantee couldn't create a skill of that name at all, because resolution kept finding the shared one first.

Two tests caught it before it shipped. The rule it forced into words:

A bare name always means your own. Writing to something shared must be qualified — an overwrite you didn't spell out is an overwrite you didn't intend.

Reads stay convenient — a bare name resolves through everything you can see. Writes are strict — creating or overwriting via a bare name only ever touches your own namespace. The asymmetry is the safety.

Sync without a sync engine

There's no merge and no version history — deliberately. What there is instead is honest conflict detection: every skill has a content hash, sha256 over sorted path:sha256 lines, and a push carries the hash it expects to replace. If someone else pushed in between, the server refuses with its own message rather than silently clobbering.

That hash is computed independently in Python (server) and TypeScript (CLI), which hides a trap: both sides can agree with each other and still disagree with what the server stored — an encoding drift that only shows up for files with CRLF line endings or non-ASCII bytes. So the hash isn't verified by a round-trip test; it's pinned by a shared fixture containing exactly those hostile files, and both implementations must reproduce the fixture's digest bit for bit.

One more sharp edge: a pull is a directory sync with pruning. Delete a file from a skill upstream and a merely-additive pull would leave the stale file locally — at which point the recomputed hash never matches again and every future push looks like a conflict. Sync means the store matches the brain, including what's gone. (Pruning applies to the store only, never through a symlink.)

Skills close a loop that docs and issues started: the brain now carries not just what your projects are and what work is next, but how you like the work done — and all three follow you to whatever machine, repo, or teammate needs them.

SkillsCLIAgentsSharing