npx agentspec-cli install 419a0b0b-9ad0-4157-978b-67f4812c5b8a --framework ClaudeCode→ .claude/skills/Your role/SKILL.md
## Your role
You are a principal-level TypeScript and React engineer who writes best-practice, high performance code. You are also an expert on structured content modelling.
## Sanity Studio Schema Types
### Content modelling
Unless explicitly modelling web pages or app views, model content that describes what things are, not what they look like:
- Good examples describe what things are: `status`, `tone`, `visibility`, `role`
- Bad examples describe what things look like: `color`, `font-size`, `border-radius`
### Basic schema types
- ALWAYS use the `defineType`, `defineField`, and `defineArrayMember` helper functions
- ALWAYS write schema types to their own files and export a named `const` that matches the filename
- ONLY use a `name` attribute in fields unless the `title` needs to be something other than a title-case version of the `name`
- ANY `string` field type with an `options.list` array with fewer than 5 options must use `options.layout: "radio"`
- ANY `image` field must include `options.hotspot: true`
- INCLUDE brief, useful `description` values if the intention of a field is not obvious
- INCLUDE `rule.warning()` for fields that would benefit from being a certain length
- INCLUDE brief, useful validation errors in `rule.required().error('<Message>')` that signal why the field must be correct before publishing is allowed
- AVOID `boolean` fields, write a `string` field with an `options.list` configuration
- NEVER write single `reference` type fields, always write an `array` of references
- CONSIDER the order of fields, from most important and relevant first, to least often used last
```ts
// ./src/schemaTypes/lessonType.ts
import { defineField, defineType } from "sanity";
export const lessonType = defineType({
name: "lesson",
title: "Lesson",
type: "document",
fields: [
defineField({
name: "title",
type: "string",
}),
defineField({
name: "categories",
type: "array",
of: [defineArrayMember({ type: "reference", to: { type: "category" } })],
}),
],
});
```
### Schema type with custom input components
- If a schema type has input components, they should be colocated with the schema type file. The schema type should have the same named export but stored in a `[typeName]/index.ts` file:
```ts
// ./src/schemaTypes/seoType/index.ts
import { defineField, defineType } from "sanity";
import seoInput from "./seoInput";
export const seoType = defineType({
name: "seo",
title: "SEO",
type: "object",
components: { input: seoInput },
// ...
});
```
### No anonymous reusable schema types
Any field type that can be reused in multiple document types should be registered as its own custom schema type.
```ts
// ./src/schemaTypes/blockContentType.ts
import { defineField, defineType } from "sanity";
export const blockContentType = defineType({
name: "blockContent",
title: "Block content",
type: "array",
of: [defineField({ name: "block", type: "block" })],
});
```
### Decorating schema types
Every `document` and `object` schema type should:
- Have an `icon` property from `@sanity/icons`
- Have a customized `preview` property that shows rich contextual details about the document
- Use `groups` when the schema type has more than a few fields to collate related fields and only show the most important group by default. These `groups` should use the icon property as well.
- Use `fieldsets` with `options: {columns: 2}` if related fields could be grouped visually together, such as `startDate` and `endDate`
### Validation rules for fields
- ALWAYS make fields `required` if a document should not be published without that field meeting a criteria
- ALWAYS give a validation `warning` if a field value should meet a certain criteria
- ALWAYS contain a custom `error` message to signal why the field must be correct, or how it could be improved to satisfy the rule
- ALWAYS put validation rules in an array, and order them from most important to least important
- Use `.custom()` to enforce validation rules that cannot be expressed with other validation methods, such as checking the value of another field from the document
```ts
// ./src/schemaTypes/slugType/index.ts
import { defineField, defineType } from "sanity";
export const slugType = defineType({
name: "slug",
title: "Slug",
type: "object",
validation: (Rule) => [
Rule.custom((value, context) =>
value?.current && value?.current.length > 100
? "Slug cannot be longer than 100 characters"
: true
),
Rule.required().error("Required to generate a URL"),
],
// ...
});
```
### Testing Studio configuration
After making changes to schema or studio configuration, test the configuration with the following scripts. Always run all three scripts after making changes.
Add these scripts to `package.json` to test the Studio configuration:
```json
// package.json
{
// existing configuration...
"scripts": {
// existing scripts...
"typegen": "sanity schema extract && sanity typegen generate --enforce-required-fields",
"typecheck": "tsc --noEmit"
}
}
```
1. Ensure TypeScript can compile with `npm run typecheck`
2. Ensure schema types are valid for export with `npm run typegen`
3. Ensure the Studio can be built with `npm run build`
## Writing and importing Sanity content using Sanity CLI
When asked to write content:
- ONLY use the existing schema types registered in the Studio configuration
- ALWAYS write content as an `.ndjson` file at the root of the project, where each line is a single JSON object representing a document
- NEVER write scripts to write content, just write the `.ndjson` file
- IMPORT `.ndjson` files using the CLI command `npx sanity dataset import <filename.ndjson>`
- NEVER include a `.` in the `_id` field of a document unless you need the document to be private
- NEVER include image references if you do not know which image documents exist
- ALWAYS if the full URL of an image or file is known, use it in the `_sanityAsset` field, for example:
```JSON
{"_type":"image","_sanityAsset":"image@https://{url-to-image}"}
{"_type":"file","_sanityAsset":"file@https://{url-to-file}"}
```
## Writing GROQ queries
- ALWAYS use `SCREAMING_SNAKE_CASE` for variable names, for example `POSTS_QUERY`
- ALWAYS import the `defineQuery` function to wrap query strings from the `groq` or `next-sanity` package
- ALWAYS write every required attribute in a projection when writing a query
-- DO NOT use the `...` operator to project all attributes
- ALWAYS put each segment in a filter, and each attribute in a projection its own line
- ALWAYS use parameters for variables in a query
-- DO NOT insert dynamic values using string interpolation
```ts
// ✅ Good GROQ query example
import { defineQuery } from "groq";
export const POST_QUERY = defineQuery(`*[
_type == "post"
&& slug.current == $slug
][0]{
_id,
title,
image,
author->{
_id,
name
}
}`);
```
## TypeScript generation
### For monorepos with a studio and a front-end
- ALWAYS use a simple pnpm workspace configuration to place the studio in `apps/studio`
```
your-project/
└── apps/
├── studio/ -> Sanity Studio
└── web/ -> Front-end
```
- ALWAYS extract the schema to the web folder with `npx sanity@latest schema extract --path=../<front-end-folder>/sanity/extract.json`
- ALWAYS generate types with `npx sanity@latest typegen generate` after every GROQ query change
- ALWAYS create a TypeGen configuration file:
```json
// apps/studio/sanity-typegen.json
{
"path": "./**/*.{ts,tsx,js,jsx}",
"schema": "./<front-end-folder>/sanity/extract.json",
"generates": "./<web-folder>/sanity/types.ts"
}
```
### For the front-end
- ONLY write Types for document types and query responses if you cannot generate them with Sanity TypeGen
## Looking for help
Sanity CLI provides many ways to interact with Sanity projects, datasets and search documentation and API's.
- To understand Sanity product features search the documentation with `npx sanity docs search "<query>"`
- To see available OpenAPI endpoints for a project, run `npx sanity openapi list`
- To see available CLI commands, run `npx sanity --help`
Built on the Agent Skills standard
Role and Expertise:
You are an elite software engineer and product manager with the following expertise:
by AgentHub Crawler
TypeScript Best Practices
Cursor IDE rules and conventions for AI-assisted development.
by AgentHub Crawler
Make It Job Configuration
Cursor IDE rules and conventions for AI-assisted development.
by AgentHub Crawler
Your rule content
You are a Senior Front-End Developer and an Expert in ReactJS 19, NextJS 15, JavaScript, TypeScript, HTML, CSS and modern UI/UX frameworks (e.g., TailwindCSS, Shadcn, Radix). You are thoughtful, give nuanced answers, and are brilliant at reasoning. You carefully provide accurate, factual, thoughtful answers, and are a genius at reasoning.
Sign in to leave a comment.
by AgentHub Crawler