Skip to content

feat(init): configure Sanity MCP and install agent skill in one combined step#1124

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

feat(init): configure Sanity MCP and install agent skill in one combined step#1124
jwoods02 wants to merge 10 commits into
mainfrom
aigro-4661/configure-agent-skills-2

Conversation

@jwoods02
Copy link
Copy Markdown
Contributor

@jwoods02 jwoods02 commented May 21, 2026

Description

sanity init now sets up the Sanity MCP server and installs the sanity-best-practices agent skill for detected AI editors in a single combined prompt, so users who already have MCP wired up still get the skill they're missing.

This replaces the earlier separate sanity skills topic — no new commands are added.

What changed in sanity init

A single editor-setup step replaces what used to be two separate flows:

  • Detect once. detectAvailableEditors() runs once per init.
  • Classify per editor. computeEditorSetupPlan decides, for each editor, whether MCP needs configuring (or re-auth) and whether the skill still needs installing — keying off the global ~/.agents/.skill-lock.json lockfile (XDG-aware).
  • Prompt once. A single checkbox prompt covers both MCP setup and skill install. Editors that are already done show up as a one-line "already configured" confirmation, not in the prompt.
  • Install after scaffold. Skills install runs after initApp / initStudio / initNextJs so the project directory exists.

The install is global, not project-scoped. That matches how the bundled skills CLI lays skills out (~/.agents/<skill>), and how almost every editor reads them — installing for one agent effectively makes it available to most others. Claude Code is the one outlier (reads from ~/.claude/skills/), so it still gets its own agent fan-out.

Only sanity-best-practices is installed; init is opinionated about which skill new projects should get.

--no-mcp / --no-skills

  • --no-mcp and --no-skills are independent. They mask only their own work.
  • --no-mcp --no-skills short-circuits editor detection entirely.
  • When MCP is skipped but skills aren't, the prompt still fires so the user can opt in to skill-only install.
  • We never claim "skill installed" when MCP wasn't — messaging tracks the actual outcome.

sanity mcp configure is unchanged

The MCP-only command does not install skills. Skill setup only happens during sanity init.

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.

What to review

  • resolveEditorSetup ordering: detect → validate-tokens → classify → mask (--no-*) → prompt (or auto).
  • computeEditorSetupPlan — the per-editor matrix of mcpAction × skillAction, and how "fully configured" is detected.
  • readSkillState — reads the global ~/.agents/.skill-lock.json (XDG-honoured) to dedup re-installs.
  • --no-mcp / --no-skills matrix.
  • Init ordering: skills install runs after initApp / initStudio / initNextJs.

Testing

Unit

  • setupSkills: agents-provided path, editor-derived path, dedup, default skill, empty inputs.
  • computeEditorSetupPlan: every combination of MCP state × skill state, plus editors without a skills-agent mapping.
  • promptForEditorSetup: choice rendering, selection split between MCP/skill, empty selection.
  • readSkillState: global lockfile via XDG_STATE_HOME and ~/.agents, malformed JSON, absent entries.
  • sanity init mocks updated to cover the combined prompt + skill setup.

Manual

  • sanity init — combined prompt fires, skill installs after scaffold.
  • sanity init --no-skills — MCP still configures, skill is not installed.
  • sanity init --no-mcp — skill still installs, MCP doesn't.
  • sanity init --no-mcp --no-skills — neither runs, editor detection skipped.
  • sanity mcp configure — does not touch skills.

AIGRO-4661


Note

Medium Risk
Changes the init path and runs bundled subprocesses that modify user-global editor/MCP config; failures are best-effort but broaden install side effects beyond MCP alone.

Overview
sanity init now offers a single editor-setup step that can configure Sanity MCP and install the global sanity-best-practices agent skill for detected AI editors. Editor detection runs once; setupMCP classifies each editor (MCP vs skill needs), applies --no-mcp / --no-skills masking, and returns skillsToInstall for setupSkills to run after project scaffolding (including the --env early-exit path).

New --no-skills (default: install in interactive production). Skills are skipped in CI, non-interactive runs, and non-production SANITY_INTERNAL_ENV, mirroring MCP gating. skills is a direct @sanity/cli dependency; install/list use the bundled CLI via import.meta.resolve, not npx.

sanity mcp configure stays MCP-only (skillsMode: 'skip'). Editor configs gain skillsCliAgent mappings (e.g. VS Code → github-copilot); readSkillState probes global installs with skills list -g --json (failures → treat as not installed).

Reviewed by Cursor Bugbot for commit 5c00bb8. 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 17s —— View job


Review

Blocker

1. configure.test.ts now mocks service files — violates CLAUDE.md testing rules

packages/@sanity/cli/src/commands/mcp/__tests__/configure.test.ts:34-40 mocks services/mcp.ts (createMCPToken, validateMCPToken) instead of HTTP. CLAUDE.md is explicit:

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

Across this file the diff replaces a dozen mockApi(...).reply(...) calls (for /auth/session/create, /auth/fetch, /users/me) with mockCreateMCPToken.mockResolvedValueOnce(...) / mockValidateMCPToken.mockResolvedValueOnce(...). This is also why describe(...) had to be downgraded to describe.sequential(...) (mock interference) and why coverage on services/mcp.ts drops -22.2% — the request paths inside createMCPToken/validateMCPToken are no longer exercised. Revert to mockApi(...) for the auth endpoints, or move to client-level mocking. Fix this →

Medium

2. Misleading prompt message when all actionable editors are skill-only

packages/@sanity/cli/src/actions/mcp/setupMCP.ts:142-146: getPromptMessage picks "Configure Sanity MCP and install agent skills for these editors?" whenever neither mode is skip. But this state is reachable when every actionable editor is skill-only (MCP already configured) — e.g. mcpMode='prompt' + skillsMode='auto', all editors valid, skill missing. The combined message then promises MCP work that won't happen. The per-row "(skill only — MCP already configured)" label softens it, but the headline is wrong.

Suggest deriving the message from actionable instead of from the modes — if every entry is skill-only, use the skill-only message.

3. PR description / docstring drift around readSkillState

The PR body says the lockfile is read directly from ~/.agents/.skill-lock.json (XDG-honoured). The implementation in packages/@sanity/cli/src/actions/skills/readSkillState.ts:32-67 shells out to skills list -g --json and consults its output. Update the PR description (and the description on MCPSetupResult.skillsToInstall comments) so reviewers / future maintainers aren't looking for filesystem reads that don't exist.

Minor / nits

4. Out-of-scope dependency

package.json:58 adds @vercel/detect-agent as a root dev dependency for vitest.config.ts:3-8. That's a tooling change unrelated to the MCP+skills story this PR sells; consider splitting it out or at least mentioning it in the changeset.

5. createMCPToken failure aborts OAuth-only writes too

setupMCP.ts:267-292 (pre-existing, not introduced here): if mcpSelected contains a mix of OAuth-only and token-needing editors and createMCPToken() throws, the entire write loop is gated by if (!mcpError), so OAuth-only editors that don't need the token never get their config written. Worth a follow-up — could be as small as splitting the loop into OAuth-only vs token-needing passes.

6. initAction.ts coverage drop -3.9%

Coverage on actions/init/initAction.ts went from 97.1% to 93.2%. The new branches worth covering: the options.mcpMode === 'skip' && options.skillsMode === 'skip' short-circuit (line 194-197), and the installSkills() catch-all try/catch (line 228-233). Adding a "setupSkills throws synchronously" case to initAction.test.ts would close most of the gap.

Confirmed good

  • The Cursor Bugbot comment about --env missing installSkills() is fixed at initAction.ts:283 with a regression test at init.staging-env.test.ts:225-261. ✅
  • The Cursor Bugbot "Next.js skills wrong directory" comment is a false positive — setupSkills installs globally (-g) and takes no cwd, so outputPath vs workDir is moot. ✅
  • Changeset summary is concise and conforms to product-copy conventions.
  • setupSkills / readSkillState / setupMCP test matrices are thorough.

  • Read changed files
  • Verify recent fixes
  • Check changeset content + scope
  • Check test coverage for new code
  • Post review
    · Branch

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 21, 2026

📦 Bundle Stats — @sanity/cli

Compared against main (7d2118d4)

@sanity/cli

Metric Value vs main (7d2118d)
Internal (raw) 2.1 KB -
Internal (gzip) 799 B -
Bundled (raw) 10.97 MB -
Bundled (gzip) 2.06 MB -
Import time 831ms +4ms, +0.5%

bin:sanity

Metric Value vs main (7d2118d)
Internal (raw) 1023 B -
Internal (gzip) 486 B -
Bundled (raw) 9.87 MB -
Bundled (gzip) 1.77 MB -
Import time 926ms +0ms, +0.0%

🗺️ 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 (7d2118d4)

Metric Value vs main (7d2118d)
Internal (raw) 96.3 KB -
Internal (gzip) 22.7 KB -
Bundled (raw) 21.64 MB -
Bundled (gzip) 3.43 MB -
Import time 797ms +5ms, +0.6%

🗺️ 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 (7d2118d4)

Metric Value vs main (7d2118d)
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.
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/src/actions/init/initAction.ts 93.3% (- 3.8%)
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/promptForMCPSetup.ts 100.0% (±0%)
packages/@sanity/cli/src/actions/mcp/setupMCP.ts 99.2% (- 0.8%)
packages/@sanity/cli/src/actions/skills/readSkillState.ts 100.0% (new)
packages/@sanity/cli/src/actions/skills/setupSkills.ts 92.6% (new)
packages/@sanity/cli/src/commands/init.ts 100.0% (±0%)
packages/@sanity/cli/src/commands/mcp/configure.ts 90.0% (±0%)
packages/@sanity/cli/src/services/mcp.ts 77.8% (- 22.2%)
packages/@sanity/cli/src/telemetry/init.telemetry.ts 100.0% (±0%)

Comparing 11 changed files against main @ 7d2118d4509f4d0a7406b1e262d17f4b531d1d07

Overall Coverage

Metric Coverage
Statements 84.4% (+ 0.1%)
Branches 74.5% (+ 0.2%)
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.
@jwoods02 jwoods02 changed the title feat: configure agent skills via sanity init and add sanity skills commands feat(init): configure Sanity MCP and install agent skill in one combined step May 27, 2026
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 61275bf. Configure here.

Comment thread packages/@sanity/cli/src/actions/init/initAction.ts
mwritter
mwritter previously approved these changes May 28, 2026
Copy link
Copy Markdown
Contributor

@mwritter mwritter left a comment

Choose a reason for hiding this comment

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

lgtm - test are passing and I was able to verify locally that each command works as intended.

  • sanity init
  • sanity init --no-skills
  • sanity init --no-mcp
  • sanity init --no-mcp --no-skills
  • sanity mcp config

@mwritter mwritter requested a review from a team May 29, 2026 13:02
@jwoods02 jwoods02 requested a review from mwritter June 1, 2026 12:19
Copy link
Copy Markdown
Contributor

@mwritter mwritter left a comment

Choose a reason for hiding this comment

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

LGTM

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