Skip to content

feat: install pulled skills for every agent, not just Claude Code - #227

Merged
quickbeard merged 2 commits into
mainfrom
skill-pull-multi-agent
Jul 30, 2026
Merged

feat: install pulled skills for every agent, not just Claude Code#227
quickbeard merged 2 commits into
mainfrom
skill-pull-multi-agent

Conversation

@quickbeard

Copy link
Copy Markdown
Owner

Closes the gap where a hub skill was usable by Claude Code and nothing else — in a product whose flagship agent is CoDev Code.

Where the layout came from

Read from the agents' source, not their docs. The table baked into the CoDev Code bundle lists only the ~/… paths and omits the project-scope walk, which is the opposite of what the code does. packages/opencode/src/skill/index.ts#discoverSkills:

externalDirs = [".claude", ".agents"]
for (const dir of externalDirs)                  // GLOBAL
  scan(path.join(global.home, dir), "skills/**/SKILL.md", { dot: true })
const upDirs = fsys.up({ targets: externalDirs, start: directory, stop: worktree })
for (const root of upDirs)                       // PROJECT — cwd up to worktree root
  scan(root, "skills/**/SKILL.md", { dot: true })
.agents/skills .claude/skills
Codex yes no
CoDev Code / OpenCode yes yes
Claude Code no yes

Neither directory alone covers all four; together they do, with the same rule at both scopes — scope only picks the root. Nothing needs a per-agent directory, so there is no .codev/skills or .opencode/skills link to write.

Design

resolveTargets extracts once into whichever directory covers the most selected agents, and links the other only when some selected agent can't reach it. When Codex isn't selected, .claude/skills alone serves everyone and no link is created. One hub skill is ~11k files, so a copy per agent is not free.

codevhub skill pull minimax-docx --here --agent claude,codex

Installed minimax-docx@1.0.0 for Claude Code, Codex, CoDev Code
  <project>/.agents/skills/minimax-docx
  <project>/.claude/skills/minimax-docx (symlink)

Links are relative symlinks, matching this repo's own committed skill (.claude/skills/vercel-react-best-practices -> ../../.agents/skills/…, git mode 120000) — relative is what survives git clone. linkOrCopy degrades to a Windows junction (needs neither Developer Mode nor admin, unlike a Windows symlink) and then to a recursive copy, reporting the mode verbatim so a fallback copy is never described as a link.

Claude Code follows symlinked skill dirs only from v2.1.203 — below that the link reads as a file with no SKILL.md and the skill is invisible, so that one link probes claude --version and copies instead.

Flags

--here/--global now select scope only; --agent claude,codex,opencode,codev / --all-agents select agents, reusing the launch names codevhub restore already accepts. Default set (prompt pre-check and non-interactive) is CoDev Code plus whatever detectCodevTools() reports, so CI never has to pass --agent. CoDev Code is always in the set and the picker renders it locked. --dir is unchanged and now rejects --agent, which would have nowhere to go.

Verification

pnpm fix, pnpm typecheck, pnpm test (1285 passed), pnpm build + smoke-run.

Unit coverage: the store/link choice for every agent subset × scope against a temp HOME/cwd; the link is asserted relative and asserted to resolve; copy fallback when symlink is stubbed to throw, reported as copy; the version floor forcing a copy; --force converting a pre-existing real directory into a link (the upgrade path); refusal before anything is deleted.

End-to-end against the real agents, since the whole design rests on what they actually scan:

  • CoDev Code — pulled a real hub skill into a scratch project, ran codev serve, confirmed it lists the skill from .agents/skills/.
  • Codex — verified from its recorded session context rather than by asking the model, with the skill sourced from the project .agents/skills/.
  • Claude Code — follows the relative symlink (this repo's own skill is wired that way and loads).

Two things reviewers should know

/skill vs /api/skill. They are different services. /api/skill is v2 (packages/core/src/skill.ts), which registers only <configdir>/skill{,s} plus skills.paths and reports nothing from .claude/.agents — querying it looks exactly like a broken install. /skill is v1, the service session/system.ts builds the prompt from. If CoDev Code migrates the prompt to v2, .agents/skills will need a skills.paths entry and this design changes. Documented in AGENTS.md.

The duplicate skill name warning is expected. When both directories exist, CoDev Code and OpenCode reach the same skill twice and log it; last wins and it resolves to one entry. It is log-only, and it already happens independently of CoDev for any skill present in both directories.

Follow-up, not in this PR

The landing-page Skills docs (quickbeard/codev-landing-page) describe --here/--global as choosing .claude/skills. That is now scope-only and there is a new agent axis, so those pages need an update.

Unrelated pre-existing bug found while testing: the hub's perplexity skill has a malformed ZIP (ADM-ZIP: No descriptor present). It fails identically on the old --dir path, so it predates this change.

🤖 Generated with Claude Code

quickbeard and others added 2 commits July 30, 2026 23:23
`skill pull` wrote only to `.claude/skills`, so a hub skill was usable by
Claude Code and nothing else — a gap in a product whose flagship agent is
CoDev Code.

Which directory each agent reads was established from the agents' own
source, not their docs: the table baked into the CoDev Code bundle lists
only the `~/…` paths and omits the project-scope walk, which is the
opposite of what `packages/opencode/src/skill/index.ts#discoverSkills`
does. It scans `.claude` AND `.agents` under `$HOME` and again walking cwd
up to the worktree root. Codex reads `.agents/skills` at both scopes;
Claude Code reads `.claude/skills` at both.

So two directories cover all four agents with one rule at both scopes, and
nothing needs a per-agent directory. resolveTargets extracts once into
whichever directory covers the most selected agents and links the other
only when some selected agent can't reach it — when Codex isn't selected,
`.claude/skills` alone serves everyone and no link is created at all. A hub
skill can run to thousands of files, so a copy per agent is not free.

Links are relative symlinks, matching this repo's own committed skill
(`.claude/skills/vercel-… -> ../../.agents/skills/vercel-…`, git mode
120000) — relative is what survives a clone. linkOrCopy degrades to a
Windows junction (no Developer Mode or admin needed) and then to a copy,
reporting the mode verbatim so a copy is never called a link. Claude Code
follows symlinked skill dirs only from v2.1.203, so that one link probes
`claude --version` and copies below the floor.

`--here`/`--global` now select scope only; `--agent <list>`/`--all-agents`
select agents, reusing the launch names `restore` accepts. CoDev Code is
always in the set and the picker renders it locked. `--dir` is unchanged
and rejects `--agent`.

Verified end-to-end against the real agents: CoDev Code lists the skill via
`/skill` (v1 — `/api/skill` is v2 and reports nothing from these
directories, which looks exactly like a broken install), Codex has it in
its session context, and Claude Code follows the relative symlink.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`[x]` reads as "removed" or "wrong" more than "selected". `[✓]` keeps the
checkbox affordance the brackets give while matching the ✓ the CLI already
uses for a completed Step. Same single-column width, so the rows stay
aligned.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@quickbeard
quickbeard merged commit 7f229fd into main Jul 30, 2026
3 checks passed
@quickbeard
quickbeard deleted the skill-pull-multi-agent branch July 30, 2026 16:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant