This guide is for AI agents working on this codebase. Follow these instructions to ensure successful contributions.
Self-Improvement: If you discover undocumented requirements, commands, or workflows during your work (e.g., a reviewer asks you to run something not covered here), update this file on the same PR. Keep this guide accurate and helpful for future agents.
| Task | Command |
|---|---|
| Install dependencies | pnpm install |
| Format code | pnpm format |
| Run linters | pnpm lint |
| Build all packages | pnpm build |
| Run tests | pnpm test |
| Add changeset | pnpm changeset add |
| Start dev server | pnpm dev |
Use the latest LTS release of Node.js.
The exact pnpm version is managed via the packageManager field in root package.json. You only need pnpm v10 or later installed globally—corepack or pnpm itself will auto-install the exact version specified.
# Enable corepack to automatically use the correct pnpm version
corepack enable
# Install all dependencies
pnpm installRun these commands in order. All must pass or CI will fail:
# 1. Format code (oxfmt)
pnpm format
# 2. Run linters (oxlint)
pnpm lint
# 3. Build all packages
pnpm build
# 4. Run tests
pnpm testEvery PR that changes published packages must include changesets. Important: Create a separate changeset file for each plugin you modify.
Each plugin has its own changelog that consumers read. A combined changeset would pollute individual plugin changelogs with irrelevant information. For example, @sanity/code-input's changelog should not mention workflow-specific changes.
Option 1: Manual Creation (Recommended for Multiple Plugins)
Create individual .md files in .changeset/ directory:
---
'plugin-name': patch
---
Brief description of the change specific to this pluginExample: If you modified 3 plugins, create 3 files:
# .changeset/code-input-fix.md
---
"@sanity/code-input": patch
---
Fix input border styling with v2 theme API
# .changeset/workflow-hooks.md
---
"sanity-plugin-workflow": patch
---
Replace deprecated useTimeAgo hook with useRelativeTime
# .changeset/markdown-theme.md
---
"sanity-plugin-markdown": patch
---
Migrate to v2 theme API for color propertiesOption 2: Interactive CLI (For Single Plugin)
pnpm changeset addFollow the prompts to:
- Select one package that changed
- Choose the version bump type (patch/minor/major)
- Write a summary of changes specific to that package
- Repeat for each modified package
- Scope: Each changeset should only mention changes relevant to that specific plugin
- Clarity: Write from the consumer's perspective—what do they need to know?
- Version bumps:
patch: Bug fixes, dependency updates, internal refactorsminor: New features, non-breaking API additionsmajor: Breaking changes
- Format: Use clear, concise language without technical jargon when possible
❌ Bad (combined changeset):
---
'@sanity/code-input': patch
'sanity-plugin-workflow': patch
---
- Migrated to v2 theme APIs
- Replaced useTimeAgo with useRelativeTime
- Fixed input stylingProblem: Workflow's changelog will say "Fixed input styling" which is irrelevant
✅ Good (separate changesets):
# .changeset/code-input-theme.md
---
## "@sanity/code-input": patch
Migrate to v2 theme API for input styling
# .changeset/workflow-hooks.md
---
## "sanity-plugin-workflow": patch
Replace deprecated useTimeAgo hook with useRelativeTimeEach plugin's changelog only shows relevant changes
The CI pipeline runs on every PR:
| Job | What it checks |
|---|---|
| build | pnpm build - All packages compile successfully |
| lint | pnpm lint --format github - Code passes oxlint |
| test | pnpm test - All tests pass (runs after build + lint) |
- oxlint: Type-aware linting with
--deny-warnings(warnings are errors). React Compiler rules run via the react-hooks-js plugin. - TypeScript type checking is included in
pnpm lintvia oxlint — no separatetscneeded - Run
pnpm lint:fixto auto-fix issues when possible
The monorepo uses Vitest v4 for testing.
# Run all tests (non-watch mode)
pnpm test run
# Run tests in watch mode (default vitest behavior)
pnpm test
# Update snapshots
pnpm test -uTests are co-located with source code in the src/ directory:
- Test files use
.test.tsor.spec.tsextensions - Each plugin has a minimal
vitest.config.tsthat inlinesvitest-package-exports - All plugins include a package exports test using
vitest-package-exportsto verify all exports are valid
When creating a new plugin with pnpm generate, test files are automatically created. The root vitest.config.ts uses glob patterns to automatically discover all plugins, so new plugins are included without manual configuration.
Example test file (src/index.test.ts):
import {fileURLToPath} from 'node:url'
import {expect, test} from 'vitest'
import {getPackageExportsManifest} from 'vitest-package-exports'
test('package exports', {timeout: 30_000}, async () => {
const manifest = await getPackageExportsManifest({
importMode: 'dist',
cwd: fileURLToPath(import.meta.url),
})
expect(manifest.exports).toMatchInlineSnapshot()
})Always use the options object syntax for timeouts, placing it as the second argument:
// Correct - options object as second argument
test('my test', {timeout: 30_000}, async () => {
// ... test code
})
// Wrong - timeout as third argument (deprecated style)
test('my test', async () => {
// ...
}, 30000)Use numeric separators (30_000 instead of 30000) for readability.
- Root
vitest.config.tsuses glob patterns['plugins/@sanity/*', 'plugins/sanity-plugin-*']to automatically find all plugin projects - Individual plugins have minimal
vitest.config.tsstarting with just inline deps configuration for vitest-package-exports - Plugins can expand their vitest configs and test suites over time as needed (unit tests, integration tests, etc.)
- Tests run against built
dist/output afterpnpm build - Snapshots are generated with
pnpm test -u
Always open PRs as draft initially. The prompter (person who requested the work) reviews first before marking ready for team review.
All PRs created by AI agents must be labeled with "🤖 bot". This label helps the team identify bot-created PRs and ensures proper tracking and review workflows.
When creating or updating a PR, always ensure the 🤖 bot label is applied.
Once the prompter approves, convert from draft to ready-for-review so the team can review.
After approval, PRs merge to main. The release workflow automatically:
- Creates a "Version Packages" PR that bumps versions
- When that PR merges, packages publish to npm with provenance
pnpm devThis starts the Sanity Studio at http://localhost:3333.
The test studio is a real Sanity Studio that connects to Sanity APIs. You must:
- Have a Sanity account
- Log in via the browser when prompted
- Have access to the configured project (
ppsg7ml5by default)
Simply accessing http://localhost:3333 is not enough—the studio requires browser-based Sanity authentication to function.
Use the generator:
pnpm generate "new plugin"Or to migrate an existing plugin:
pnpm generate "copy plugin"See CONTRIBUTING.md for detailed instructions on:
- Setting up npm trusted publishing (different process for new vs existing packages)
- Creating initial release changesets
- Migrating existing plugins
For brand new packages (not yet on npm):
- Use the "Setup a new npm package with Trusted Publishing" GitHub Actions workflow
- Then run locally:
npm trust github <package-name> --file=release.yml --repository=sanity-io/plugins(requires npm >= 11.10.0)
For existing packages (already on npm):
⚠️ DO NOT use the setup workflow- Run:
npm trust github <package-name> --file=release.yml --repository=sanity-io/plugins(requires npm >= 11.10.0)
Always use lodash-es instead of lodash
When working with lodash utility functions, always use the lodash-es package instead of lodash. The lodash-es package is the ES module version that supports tree-shaking and works correctly with modern build tools.
# Correct
pnpm add lodash-es
# Wrong
pnpm add lodashWe use oxfmt:
pnpm formatWe use oxlint for all linting (type-aware, includes TypeScript type checking and React Compiler rules via the react-hooks-js plugin):
pnpm lint # Run the linter (includes type checking)
pnpm lint:fix # Auto-fix what's possibleplugins/
├── dev/test-studio/ # Test Sanity Studio (localhost:3333)
├── packages/@repo/ # Internal shared packages
├── plugins/ # Published plugins
│ ├── @sanity/ # @sanity/* scoped packages
│ └── sanity-plugin-* # Community-style packages
├── turbo/generators/ # Plugin scaffolding templates
└── .changeset/ # Changeset files for releases
Run pnpm build first—some packages need to be built for type information to be available.
Ensure you have pnpm v10+ installed, then run:
corepack enable- Check you're logged into Sanity in the browser
- Verify you have access to the project
- Check the browser console for specific errors
- CONTRIBUTING.md - Human contributor guide
- Changesets documentation
- Turborepo documentation