Repository navigation
Global Claude skills link exposes Claude Code's claude.ai-synced skills to Codex and OpenCode #193
Description
What happens
Claude Code keeps entries of its own in ~/.claude/skills/. Skills enabled on claude.ai are downloaded to synced/<account>/<skill>/ and refreshed while Claude Code runs; they move to .trash/ when syncing stops, and downloads are staged in .staging/. The skills docs treat synced as a reserved name, and Claude Code loads these skills as anthropic-skills:<name>.
At global scope dotagents replaces ~/.claude/skills/ with a link to ~/.agents/skills/. Claude Code then writes synced/ into the shared directory, and Codex (recursive scan of ~/.agents/skills) and OpenCode (skills/**/SKILL.md) load the claude.ai skills as ordinary skills. On my machine that put 12 claude.ai skills into Codex, where skill-creator and pdf now appear twice next to Codex's own.
Excluding synced/ during the migration is not enough: Claude Code recreates it inside the shared directory on its next sync, because ~/.claude/skills is the link.
(sync also adopts synced, .trash, and .staging as skills and breaks agents.toml; I'm sending a separate fix for that.)
Proposal
At global scope, keep ~/.claude/skills/ a real directory and link each shared skill into it:
~/.claude/skills/<name> -> ~/.agents/skills/<name>
~/.claude/skills/synced/ (left to Claude Code)
init,install, andsynclink every skill in~/.agents/skills/, including projected plugin skills, and remove links whose skill is gone.removeunlinks right away.- An existing directory link is replaced, and
synced/,.trash/, and.staging/move back to~/.claude/skills/. syncmoves a skill directory created in~/.claude/skills/into~/.agents/skills/, links it back, and declares it;installonly links. A name that exists on both sides is left alone, andinstallandsyncwarn.doctorreports a directory link, missing or stale links, and skills not shared yet.- The bundled
dotagentsskill tells agents to create new skills in~/.agents/skills/and link them for Claude Code, so a skill created from any agent is shared right away when the guidance is followed. It is not followed every time (see below), and the nextsyncshares the rest.
Project scope keeps the directory link, since Claude Code does not sync into projects. Cursor uses ~/.claude/skills too, so it gets the same treatment.
What I measured
Claude Code 2.1.283, Codex CLI 0.156.0, OpenCode 1.18.31, macOS.
- Claude Code loads per-skill links in
~/.claude/skills/. A skill written to~/.agents/skills/and linked mid-session could be invoked in the same session about 10 seconds later, in 3 of 3 runs. Skills written directly into~/.claude/skills/need the same wait. --add-dir ~/.agents(with~/.agents/.claude/skills -> ../skills) makes Claude Code read the shared directory directly, but the same directory inpermissions.additionalDirectoriesdoes not load skills. That would need a flag on every launch, so I did not pursue it.- Asked to "turn this review procedure into a global skill" (Sonnet), Claude Code wrote to
~/.claude/skills/in 3 of 3 runs without guidance. With the guidance above in thedotagentsskill it wrote to~/.agents/skills/and linked it in 3 of 3 runs in a clean profile. In my own profile, with a large CLAUDE.md and requests in Japanese, the English trigger phrases matched only 2 of 5 runs; adding the request phrasing in the user's language raised it to 3 of 3. Thesyncfallback above covers the misses. - After switching my own setup: Claude Code loads the 31 shared skills and the 12 claude.ai skills (as
anthropic-skills:*) and keeps refreshing~/.claude/skills/synced/; Codex no longer sees the claude.ai skills. OpenCode still does, because it reads~/.claude/skills/itself; that is OpenCode's own discovery.
I have an implementation of the above with tests, reviewed and run on my machine (pnpm check on Node 20, the CI build and docs steps, and the dotagents-qa Docker checks for init, install, sync, remove, and doctor --fix). Would you take a PR along these lines? In particular:
- Is per-skill linking at global scope acceptable, or would you rather keep the directory link and handle this another way?
- Should Cursor share the per-skill behavior, or keep the directory link?
- Is moving skill directories out of
~/.claude/skills/duringsyncacceptable, or should that be opt-in?
Activity