Skip to content

feat: configure agent skills via sanity init and add sanity skills commands#1124

Open
jwoods02 wants to merge 6 commits into
mainfrom
aigro-4661/configure-agent-skills-2
Open

feat: configure agent skills via sanity init and add sanity skills commands#1124
jwoods02 wants to merge 6 commits into
mainfrom
aigro-4661/configure-agent-skills-2

Conversation

@jwoods02
Copy link
Copy Markdown
Contributor

@jwoods02 jwoods02 commented May 21, 2026

Description

Set up Sanity agent skills for AI editors as part of sanity init, and add a new sanity skills topic for managing them in existing projects.

Both these wrap the npx skills package, added as a dependancy to the CLI

sanity init

Once the project has scaffolded, skills are installed into the new project directory for every detected AI editor. The install is automatic and project scoped — no extra prompt during init — to keep the flow short. Opt out with --no-skills.

  • --no-mcp and --no-skills are independent flags. They map to mcpMode / skillsMode and gate only their own setup, so --no-mcp does not silently opt the user out of skills (or vice versa).
  • Editor detection runs once and is shared between MCP and skills setup. It only fires when at least one of the two is active, so --no-mcp --no-skills short-circuits detection entirely.
  • The install runs after scaffold so it lands in a populated project directory; the call is wrapped defensively so a failure inside setupSkills can never abort an otherwise-successful sanity init.

sanity skills add

Installs Sanity agent skills into the current project for detected AI editors. Same underlying code path as the post-init install, with a few command-specific behaviours:

  • Defaults to interactive prompt when stdin is a TTY, auto when not.
  • Surfaces a hint when no eligible editors are detected, so the command isn't silent.
  • Useful for projects that were scaffolded before this landed, or for re-running after installing a new editor.

sanity skills update

Refreshes installed project skills to their latest published version. Only updates skills whose skills-lock.json source points at a Sanity-owned GitHub org (sanity-io or sanity-labs) — foreign skills (anything the user added themselves) are left untouched.

  • Runs the upstream skills update codepath, so updates are hash-checked and fast.
  • Matches both lockfile shapes the bundled skills CLI actually writes: the org/repo shorthand it normalises HTTP(S) inputs to, and the raw git@github.com:org/repo.git SSH form it keeps when the user passed an SSH URL.

skills is bundled as a dependency

The bundled skills CLI is added as a direct dependency of @sanity/cli and resolved via import.meta.resolve('skills/bin/cli.mjs', import.meta.url). Renovate keeps it pinned.

Trade-off: ~430 KB unpacked in the @sanity/cli package, no other tree growth (yaml was already ours). In return:

  • Deterministic version across user installs — no surprise behaviour drift.
  • No npx cold-start latency on every sanity init.
  • Upstream changes land via a Renovate PR we can review before publishing.

--project is passed explicitly to skills add rather than relying on the CLI's cwd-based scope auto-detect, so behaviour is consistent regardless of where the user runs sanity init.

What to review

  • The init ordering: skills runs after initApp / initStudio / initNextJs. Worth a sanity check that all three flows reach the install reliably.
  • isSanityOwnedSource in runSkillsUpdate.ts — the two matched lockfile shapes are what I observed in the bundled skills dist. If you've seen other shapes in the wild, flag them.
  • --no-skills / --no-mcp matrix on sanity init — covered by init.command.test.ts, but the combinatorics are worth a glance.
  • setupSkills keeps a 'prompt' mode that is only used by sanity skills add today. Fine as-is, but flag if you'd rather inline it.

Testing

Unit

  • setupSkills: skip mode, no eligible editors, install, dedup, error paths.
  • runSkillsUpdate: Sanity-owned source matching across lockfile shapes, no-op when nothing matches.
  • sanity skills add / sanity skills update: command-level mocks for telemetry and error paths.
  • sanity init mocks updated to cover the new skills step; --no-mcp / --no-skills matrix tests added.

Manual

  • sanity init — skills install fires after scaffold.
  • sanity init --no-skills — MCP still runs, skills doesn't.
  • sanity init --no-mcp — skills still runs, MCP doesn't.
  • sanity init --no-mcp --no-skills — neither runs, editor detection skipped.
  • sanity skills add inside a scaffolded project.
  • sanity skills update after editing a Sanity-owned skill in skills-lock.json.

Note for Sanity-internal testers: skills install talks to the bundled CLI only; no Sanity API auth needed beyond what sanity init already requires.


Note

Low Risk
Low risk: only updates a unit test assertion to accept Windows path separators; runtime behavior is unchanged.

Overview
Adjusts the setupSkills unit test to validate SKILLS_BIN_PATH using a path-separator-agnostic regex (skills[\\/]bin[\\/]cli\.mjs), making the test pass on both POSIX and Windows environments.

Reviewed by Cursor Bugbot for commit 3bea22c. Bugbot is set up for automated code reviews on this repo. Configure here.

jwoods02 and others added 4 commits May 21, 2026 09:56
Co-authored-by: Jonah Snider <jonah@jonahsnider.com>
…t-out

Skills install is now part of the default `sanity init` flow: no yes/no prompt,
times it post-scaffold so the install lands in the new project dir, and runs
project-wide for every detected editor with a `skillsCliAgent` mapping.

- New `--no-skills` flag mirroring `--no-mcp`, with matching `skillsMode`
  plumbing alongside `mcpMode`. Both share env / non-interactive gating so
  e2e / UI tests don't shell out to `npx skills add`.
- Editor detection is shared between MCP and skills and only runs when at
  least one of the two is active.
- `installSkills()` in initAction wraps the call in a defensive try/catch so
  init never fails on a scaffolded project even if `setupSkills` violates its
  no-throw contract.
- Dropped the `fs.mkdir` workaround from `setupSkills` — the scaffolded
  project directory always exists by the time we run.
- `'prompt'` mode and `promptForSkillsSetup` helper are kept in `setupSkills`
  for a future `sanity skills add` command.
Adds `skills` to @sanity/cli dependencies and resolves the bundled bin via
`import.meta.resolve` so init runs the version we ship rather than paying
the `npx -y` registry lookup at runtime. Also passes `--project` to
`skills add` explicitly instead of relying on the CLI's cwd-based scope
auto-detect.
- `sanity skills add` installs Sanity agent skills into the current
  project for detected AI editors. Same code path as the post-init
  install: interactive prompt when TTY, auto when not. New `explicit`
  flag on `setupSkills` surfaces a hint when no eligible editors are
  detected so the command isn't silent.

- `sanity skills update` refreshes any installed project skill whose
  `skills-lock.json` source points at a Sanity-owned GitHub org
  (currently `sanity-io` and `sanity-labs`), leaving foreign skills
  untouched. Runs the upstream `skills update` codepath rather than
  re-copying via `add`, so it's hash-checked and fast.

Plumbed through oclif as a new `skills` topic; `check-topic-aliases.ts`
updated to allowlist it.
@jwoods02 jwoods02 requested a review from a team as a code owner May 21, 2026 14:19
@claude
Copy link
Copy Markdown

claude Bot commented May 21, 2026

Claude finished @jwoods02's task in 4m 44s —— View job


Review

  • Read changed files
  • Verified Cursor Bugbot's Next.js outputPath finding (valid)
  • Reviewed setupSkills.ts / runSkillsUpdate.ts
  • Reviewed init integration
  • Reviewed skills add / skills update
  • Validated changeset
  • Posted

Issues

1. Next.js + --output-path installs skills in the wrong directory (confirms Bugbot)

packages/@sanity/cli/src/actions/init/initAction.ts:266 calls installSkills() (which uses cwd: outputPath at line 216) right after initNextJs(...). But initNextJs writes all scaffolded files into workDir (see initNextJs.ts:254, 289, 303, 309, 350). When the user passes sanity init --output-path foo against a Next.js project, getProjectOutputPath returns path.resolve(foo) while Next.js files land in workDir, so the skills install runs in an empty / unrelated directory.

Fix: use workDir for the Next.js branch's skills install. Easiest is to pass an explicit cwd into installSkills from each branch, e.g.

async function installSkills(cwd: string): Promise<void> { /* ... */ }
// ...
if (initNext) {
  await initNextJs({ ..., workDir })
  await installSkills(workDir) // ← match where initNextJs wrote files
  ...
}
// later
await installSkills(outputPath) // studio/app branch

Fix this →

2. mcp/__tests__/configure.test.ts regresses to service-level mocking

The diff replaces HTTP-level mockApi() with vi.mock('../../../services/mcp.js', ...) to stub createMCPToken / validateMCPToken. The repo's CLAUDE.md explicitly says:

Mock at the highest level possible: HTTP > Client > Action (never Service). Never mock service files - always use client or HTTP mocking

The coverage report reflects this: services/mcp.ts drops from 100% → 77.8%. These tests previously exercised the real session-create / fetch / /users/me round-trips through mockApi(); that's now bypassed. Either keep mockApi for the token flow or move this to a separate cleanup PR with justification.

3. Out-of-scope test infra change

vitest.config.ts was rewritten to use @vercel/detect-agent (added as a new root devDependency) in place of Boolean(process.env.CLAUDECODE || process.env.CODEX_CI). This is unrelated to the agent-skills feature, adds a new package + top-level await, and makes the CLAUDECODE instruction in CLAUDE.md (lines under "Cursor Cloud specific instructions") inaccurate. Suggest splitting into its own PR with CLAUDE.md updated to match.

4. Drive-by un-export of MCP_API_VERSION

services/mcp.ts:4 changes export const MCP_API_VERSIONconst MCP_API_VERSION solely to satisfy the new test setup. Unrelated to the skills feature and worth keeping in #2's scope.

Minor

  • editorConfigs.ts:398-403: getSkillsCliAgent(editorName: EditorName) — given the strict typing, the editorName in EDITOR_CONFIGS runtime check is redundant. EDITOR_CONFIGS[editorName].skillsCliAgent would suffice. Not load-bearing.
  • setupSkills.ts:66: mode = 'prompt' is the function default, but the only caller using 'prompt' is sanity skills add (which sets it explicitly). The PR description already flags this. Consider removing the default to make the call sites obvious.
  • commands/skills/add.ts:26 passes process.cwd() unconditionally. If the user runs sanity skills add outside a project, the bundled skills add --project will fail with a less-than-friendly error. A pre-check (e.g. for package.json or sanity.cli.*) would improve the message, but acceptable to defer.

Changeset

.changeset/pr-1079.md is present, scoped to @sanity/cli minor — correct for a feature addition. Summary is functional but long; consider tightening per product-copy norms, e.g.:

Add sanity skills add / sanity skills update and install Sanity agent skills automatically during sanity init.

Test coverage

promptForSkillsSetup.ts is reported at 0% coverage (per the coverage delta comment). It's tiny but worth a single test or have it covered by an existing prompt test setup.


· Branch: aigro-4661/configure-agent-skills-2

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 21, 2026

📦 Bundle Stats — @sanity/cli

Compared against main (cd110e7c)

@sanity/cli

Metric Value vs main (cd110e7)
Internal (raw) 2.1 KB -
Internal (gzip) 799 B -
Bundled (raw) 10.97 MB -98 B, -0.0%
Bundled (gzip) 2.06 MB -24 B, -0.0%
Import time 812ms +2ms, +0.3%

bin:sanity

Metric Value vs main (cd110e7)
Internal (raw) 1023 B -
Internal (gzip) 486 B -
Bundled (raw) 9.84 MB -
Bundled (gzip) 1.77 MB -
Import time 1.91s +7ms, +0.4%

🗺️ View treemap · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

📦 Bundle Stats — @sanity/cli-core

Compared against main (cd110e7c)

Metric Value vs main (cd110e7)
Internal (raw) 96.3 KB -723 B, -0.7%
Internal (gzip) 22.7 KB -44 B, -0.2%
Bundled (raw) 21.61 MB -723 B, -0.0%
Bundled (gzip) 3.42 MB -65 B, -0.0%
Import time 776ms +1ms, +0.2%

🗺️ View treemap · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

📦 Bundle Stats — create-sanity

Compared against main (cd110e7c)

Metric Value vs main (cd110e7)
Internal (raw) 908 B -
Internal (gzip) 483 B -
Bundled (raw) 931 B -
Bundled (gzip) 491 B -
Import time ❌ ChildProcess denied: node -
Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

@jwoods02 jwoods02 changed the title feat: configure agent skills via sanity init and add sanity skills topic feat: configure agent skills via sanity init and add sanity skills commands May 21, 2026
- Mention the new `sanity skills add` / `sanity skills update` commands
  in the changeset.
- Drop the stale `npx skills add` reference in the init flow comment.
- Soften the `skills update` no-op message so it reads correctly whether
  the lockfile is missing or only contains non-Sanity skills.
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit ec5d6cc. Configure here.

Comment thread packages/@sanity/cli/src/actions/init/initAction.ts
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 21, 2026

Coverage Delta

File Statements
packages/@sanity/cli/scripts/check-topic-aliases.ts 0.0% (±0%)
packages/@sanity/cli/src/actions/init/initAction.ts 93.2% (- 3.9%)
packages/@sanity/cli/src/actions/init/types.ts 100.0% (±0%)
packages/@sanity/cli/src/actions/mcp/editorConfigs.ts 100.0% (±0%)
packages/@sanity/cli/src/actions/mcp/setupMCP.ts 100.0% (±0%)
packages/@sanity/cli/src/actions/skills/promptForSkillsSetup.ts 0.0% (new)
packages/@sanity/cli/src/actions/skills/runSkillsUpdate.ts 95.1% (new)
packages/@sanity/cli/src/actions/skills/setupSkills.ts 95.5% (new)
packages/@sanity/cli/src/commands/init.ts 100.0% (±0%)
packages/@sanity/cli/src/commands/skills/add.ts 100.0% (new)
packages/@sanity/cli/src/commands/skills/update.ts 100.0% (new)
packages/@sanity/cli/src/services/mcp.ts 77.8% (- 22.2%)
packages/@sanity/cli/src/telemetry/init.telemetry.ts 100.0% (±0%)
packages/@sanity/cli/src/telemetry/skills.telemetry.ts 100.0% (new)

Comparing 14 changed files against main @ cd110e7c2b0cdad7438f53d958a0831fea2a3773

Overall Coverage

Metric Coverage
Statements 84.3% (+ 0.1%)
Branches 74.4% (+ 0.1%)
Functions 84.2% (+ 0.0%)
Lines 84.8% (+ 0.1%)

…tion

`fileURLToPath` returns backslash-separated paths on Windows, so the
regex needs `[\\/]` instead of a literal `/` to match on both platforms.
@socket-security
Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​skills@​1.5.77910010097100
Addednpm/​@​vercel/​detect-agent@​1.2.39910010091100

View full report

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