Skip to content

feat(sync): add --recursive for workspace packages - #2446

Merged
antfu merged 1 commit into
vercel-labs:mainfrom
antfubot:feat/sync-recursive
Oct 9, 2026
Merged

antfu merged 1 commit into
vercel-labs:mainfrom
antfubot:feat/sync-recursive

Conversation

@antfubot

@antfubot antfubot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Step 9 of #2323 (RFC: Install skills from npm packages). Builds on #2445.

What changes

skills experimental_sync --recursive (-r) also reads every workspace package of the project and syncs the skills of their dependencies into the project root's agent directories.

Finding workspace packages

  • Patterns come from pnpm-workspace.yaml (packages:) and from package.json#workspaces, in both the array form and Yarn's { "packages": [...] } form.
  • ! negations are honored.
  • Matching uses Node's built-in fs.promises.glob, which expands * and ** at any depth and never descends into node_modules. It is stable and prints no warning on Node 22.20, 24 and 26, so there is no new dependency and no hand-written expander. The helper in feat: sync skills shipped by npm packages #2348 only expanded one-level dir/* patterns and ignored negations.

Resolving their dependencies

Each workspace package's declared dependencies are resolved from that package with Node's lookup: walk up node_modules from its realpath, the same as npm: entries since step 5. This finds pnpm's per-package node_modules as well as npm and Yarn hoisting to the root. A workspace package's own skills field is read like a dependency's: problems in it are warnings, via is the workspace package name, and only the project root's field is strict.

The root's dependencies now go through the same lookup, which also finds a package hoisted above the project root, as Node does.

Which version wins

This answers the monorepo question raised in the RFC discussion. Workspace dependencies count as one level further from the project than root dependencies:

  • a root dependency wins over a workspace dependency with the same skill name ("closer to the project")
  • two workspaces that resolve to different copies of a package, for example two pnpm versions, ship the same skill name and tie. Sync installs neither and warns with --exclude <pkg>#<skill>.
  • the same copy reached from several workspaces counts once (realpath de-duplication)

Without --recursive, nothing changes.

Tests

tests/sync.test.ts adds a --recursive group:

  • npm workspaces with hoisting, where the skill is installed only with --recursive
  • a pnpm workspace with per-package node_modules and a ! negation
  • a root dependency beating a workspace dependency
  • two workspaces with different copies installing neither, with the --exclude hint
  • a workspace package's skills field with via

The full suite passes (959).


This PR was created with the help of an agent.

With --recursive, sync also reads each workspace package declared in
pnpm-workspace.yaml or package.json workspaces (array or { packages }),
honoring ! negations. Each workspace package's declared dependencies
are resolved from the package with Node's lookup, so pnpm's per-package
node_modules and npm or Yarn hoisting both work, and its own skills
field is read like a dependency's.

Workspace dependencies count as one level further from the project
than root dependencies: a root dependency wins over a workspace one
with the same skill name, and two workspaces with different copies of
a skill tie and install neither, with an --exclude hint.

Dependencies are now always resolved with the same lookup, so a
package hoisted above the project root is found too.
@antfubot
antfubot force-pushed the feat/sync-recursive branch from d2f45ad to eb55c37 Compare October 9, 2026 06:04
@antfu
antfu merged commit e878c45 into vercel-labs:main Oct 9, 2026
10 checks passed
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.

2 participants