This is a Sitecore Content SDK application built with Next.js (App Router) and TypeScript, with Next.js Cache Components (cacheComponents: true) and tag-based on-demand revalidation wired in. AI agents work as developer assistants within this scaffolded head application. The app integrates with Sitecore XM Cloud for content, uses file-based routing with [site] and [locale], next-intl for i18n, and Edge middleware for preview, multisite, redirects, and personalization.
Scope: This file applies to this application only (a scaffolded head app). It is not the Content SDK monorepo — for SDK package development use that repo's AGENTS.md. Here we edit app code and config (app router, components, API routes, cache helpers, i18n); we do not modify SDK packages or CI.
How this template differs from nextjs-app-router: This template enables Cache Components and ships tag-aware data helpers (getSitecorePage, getSitecoreDictionary, getSitecoreErrorPage) plus a single POST /api/revalidate route for on-demand cache invalidation. Use this template when you want deterministic tag-based revalidation; use nextjs-app-router when you don't need it.
npm install
npm run dev # Start development server
npm run build # Build for production
npm run start # Start production server
npm run lint # Run ESLint
npm run type-check # Run TypeScript compilerEnvironment: Copy .env.example to .env.local and set Sitecore API endpoint, key, default site, language, and SITECORE_REVALIDATE_SECRET (used by POST /api/revalidate). Never commit .env or .env.local.
Component maps: .sitecore/component-map.ts (Server) and .sitecore/component-map.client.ts (Client) are auto-generated from src/components/ during npm run dev (watch) and npm run build. No manual action needed; the generator scans src/components/ and creates entries in the appropriate map (Server vs Client based on 'use client').
src/
app/ # Next.js App Router
layout.tsx # Root layout
not-found.tsx # Root 404 (uses getSitecoreErrorPage with scConfig defaults)
global-error.tsx # Root 500 (uses client.getErrorPage; Client Component)
[site]/ # Site segment (multisite)
layout.tsx # Site layout (Bootstrap, draftMode)
[locale]/ # Locale segment (i18n)
[[...path]]/
layout.tsx # Segment layout: setCachedPageParams({ site, locale }) (SSG-safe)
page.tsx # Sitecore page (uses getSitecorePage)
not-found.tsx # Segment 404: getCachedPageParams() + getSitecoreErrorPage
api/ # Route handlers
sitemap/route.ts, robots/route.ts
editing/config/route.ts, editing/render/route.ts
revalidate/route.ts # POST /api/revalidate (OSR)
components/ # React components (Sitecore + app-specific)
lib/
sitecore-client.ts # Single SitecoreClient instance
cache/ # Tag-aware data helpers (this template)
get-sitecore-page.ts # `use cache` + sc:route/sc:item tags
get-sitecore-dictionary.ts # `use cache` + sc:dict tag
get-sitecore-error-page.ts # `use cache` + tags for 404 / 500 content
i18n/ # next-intl
routing.ts # locales, defaultLocale, localePrefix
request.ts # getRequestConfig, getSitecoreDictionary per site
Layout.tsx, Providers.tsx, Bootstrap.tsx, Scripts.tsx
proxy.ts # Edge middleware (preview, bot-tracking, locale, multisite, redirects, personalize)
.sitecore/ # component-map.ts, component-map.client.ts, import-map.*, sites.json, metadata.json
sitecore.config.ts # Sitecore config (api, defaultSite, defaultLanguage, dictionary cache off)
next.config.ts # cacheComponents: true, next-intl plugin, rewrites, images
These are the main head-app–specific concepts. Details are in the sections below.
next.config.tssetscacheComponents: true. This enables Next.jsuse cacheandcacheTagso cached payloads can be invalidated by tag.- Cache helpers in
src/lib/cache/wrap the SDK client and attach Sitecore tags to each cached payload:getSitecorePage({ site, locale, path })→ page data withsc:route:...andsc:item:...tags. Personalization variants are isolated naturally by the URL path / Cache Components key.getSitecoreDictionary({ site, locale })→ dictionary phrases with asc:dict:...tag.getSitecoreErrorPage({ site, locale, code })→ 404 / 500 Sitecore content with the same tag strategy asgetSitecorePage.
POST /api/revalidateis a single Sitecore-webhook endpoint. It accepts the Sitecore Experience Edge / Content Operations payload shape:updates[]— Sitecore publish-event rows; the handler maps each row'sidentifier(with-media/-layoutstripped) tosc:item:<id>:<locale>:latest.tags[]— pass-through array.sc:-prefixed strings are revalidated verbatim (handy for ad-hoc, operational calls); bare item IDs are mapped tosc:item:<id>:<defaultLocale>:latest.- Dictionary tags from
sites(.sitecore/sites.json, including the default site fromgenerateSites) are merged on every call so dictionary changes are covered.
- Auth (optional): leave
SITECORE_REVALIDATE_SECRETempty to skip auth (nox-revalidate-secretheader). When set, callers must send the same value inx-revalidate-secret(configure that header on your Sitecore webhook). - Dictionary cache:
sitecore.config.tsdisables the SDK's in-process dictionary cache (dictionary: { caching: { enabled: false } }). The Cache Components helper is the only dictionary cache layer, sorevalidateTagworks end to end.
- Where:
src/proxy.ts. Next.js runs middleware frommiddleware.tsat root or insrc/— if the app only hasproxy.ts, addsrc/middleware.tsthat re-exports it. - What it does: Runs on each request (respecting the
matcher). Chain order is fixed: PreviewProxy → BotTrackingProxy → LocaleProxy → AppRouterMultisiteProxy → RedirectsProxy → PersonalizeProxy. PreviewProxy authorizes preview requests first; locale must run before multisite for App Router. - Config: Uses
sitecore.config.ts(multisite, redirects, personalize),.sitecore/sites.json, andsrc/i18n/routing.ts(locales). Do not change proxy order. Keep the matcher excluding API,_next/, sitemap, robots, and static assets so the proxy stays lightweight.
- Where: Single shared instance in
src/lib/sitecore-client.ts—new SitecoreClient({ ...scConfig })with config fromsitecore.config.ts. - Use directly for: preview and editing (
getPreview,getDesignLibraryData, internal editing routes), 500 page (client.getErrorPage(ErrorPage.InternalServerError)inglobal-error.tsx), andgetAppRouterStaticParams. - Use the cache helpers for everything else: non-preview page reads go through
getSitecorePage; dictionary reads throughgetSitecoreDictionary; 404 content throughgetSitecoreErrorPage. The cache helpers wrap the same client under'use cache'and attach the right tags. - Do not: Create a second client or instantiate SitecoreClient elsewhere. Pass
siteandlocalefrom route params (orgetCachedPageParams()in the segmentnot-found.tsx, orscConfig.defaultSite/scConfig.defaultLanguagein the rootnot-found.tsx), not from global state.
-
Where:
src/app/[site]/[locale]/[[...path]]/page.tsx. This is the only page component that renders Sitecore content; the optional[[...path]]segment captures the content path. -
Flow:
paramsis a Promise (Next.js 15+) —await paramsto get{ site, locale, path? }. WhendraftMode().isEnabled, useclient.getPreview(editingParams)orclient.getDesignLibraryData(editingParams)fromsearchParams(preview is dynamic, not cached). Otherwise usegetSitecorePage({ site, locale, path: path ?? [] }). CallsetRequestLocale(\${site}_${locale}`)` at the top of the page for next-intl. -
generateMetadataalso goes throughgetSitecorePageso it shares the same cache entry as the page render. - Do not: Add another catch-all or page at a different path for Sitecore pages; keep this single entry point.
-
In the URL: All content routes are
/[site]/[locale]/...path(e.g./default/en,/default/en/about). Middleware (LocaleProxy, then AppRouterMultisiteProxy) rewrites incoming requests into this shape. -
In the app: next-intl uses a single
requestLocaleper request. This app encodes both site and locale asrequestLocale = \${site}${locale}`. In the page, callsetRequestLocale(`${site}${locale}`)so next-intl andsrc/i18n/request.tssee it. Inrequest.ts, parserequestLocale(e.g.split('_')) to get site and locale, then load the dictionary withgetSitecoreDictionary({ locale, site })`. -
Config:
src/i18n/routing.tsdefineslocalesanddefaultLocale; align these with Sitecore languages (e.g. fromsitecore.config.ts). Do not change the{site}_{locale}convention without updating request.ts and all pages that callsetRequestLocale.
- Component maps:
.sitecore/component-map.ts(Server) and.sitecore/component-map.client.ts(Client) — register every Sitecore component here; are auto-generated fromsrc/components/. Do not edit manually unless needed. - Editing/preview: Use
draftMode()in Server Components; when enabled, useclient.getPreview(searchParams)orclient.getDesignLibraryData(searchParams)directly (do not route preview through the cache helpers). Editing API routes live undersrc/app/api/editing/. - Env: All config via environment variables in
sitecore.config.ts. Document vars in.env.example(or.env.remote.example/.env.container.example); never commit.envor.env.local.SITECORE_REVALIDATE_SECRETis optional (see.env.*.examplecomments).
- URL shape:
/[site]/[locale]/...path(e.g./default/en,/default/en/about). Site and locale are in the path; the Edge proxy rewrites incoming requests to this shape. - Page component:
src/app/[site]/[locale]/[[...path]]/page.tsx. Receivesparams: Promise<{ site, locale, path? }>. Useawait params; in draft mode go throughclient.getPreview/client.getDesignLibraryData; otherwise callgetSitecorePage({ site, locale, path: path ?? [] }). - Layout hierarchy:
app/layout.tsx→app/[site]/layout.tsx(per-site; runs Bootstrap withsiteName={site}anddraftMode()) →app/[site]/[locale]/[[...path]]/layout.tsx(callssetCachedPageParams({ site, locale })so segmentnot-found.tsxcan resolve site/locale withoutheaders()) → page. Do not put site/locale-specific data fetching in the root layout; use the[site]or segment layout.
-
Config:
src/i18n/routing.ts—defineRouting({ locales, defaultLocale, localePrefix }). Alignlocaleswith Sitecore languages; often sourced fromsitecore.config.ts(e.g.defaultLanguage). -
Request config:
src/i18n/request.ts—getRequestConfigreceivesrequestLocale. The app uses{site}_{locale}(e.g. set bysetRequestLocale(\${site}${locale}`)in the page). Parse withrequested?.split('')to getparsedSiteandparsedLocale; load dictionary withgetSitecoreDictionary({ locale, site: parsedSite })(Cache Components helper) and return{ locale, messages }`. -
In pages: Call
setRequestLocale(\${site}_${locale}`)` at the top of the page so next-intl and request config see the correct locale.
- Site list:
.sitecore/sites.json— typically generated by the Sitecore CLI or deployment. Used by middleware and API route handlers. Avoid hand-editing unless you know the format. - Edge middleware: Implemented in
src/proxy.ts. Next.js only runs middleware from a file namedmiddleware.tsat root or insrc/. If this app has onlyproxy.ts, addsrc/middleware.tsthat re-exports it (e.g.export { default } from './proxy';) so the proxy runs. - Proxy chain (order is critical):
defineProxy(preview, botTracking, locale, multisite, redirects, personalize).exec(req):- PreviewProxy — authorizes preview requests on the internal editing host; no-op elsewhere.
- BotTrackingProxy — bot detection.
- LocaleProxy — uses
sitesandrouting.localesfromsrc/i18n/routing.ts. Required for App Router so locale is set before multisite. - AppRouterMultisiteProxy — rewrites to
/[site]/[locale]/[...path]; usesscConfig.multisite. - RedirectsProxy — redirects; uses
scConfig.redirects,scConfig.api.edge,scConfig.api.local. - PersonalizeProxy — personalization; uses
scConfig.personalize; often disabled in dev.
- Matcher: Exclude API routes (including
/api/revalidate),_next/, sitemap, robots, healthz, Sitecore paths, and static assets so middleware does not run on every static request. The matcher is defined inconfiginproxy.ts(or inmiddleware.tsif it re-exports the proxy). - Config:
sitecore.config.ts→multisite,redirects,personalize; never commit secrets.
- Page data: In the page (or a Server Component), use
getSitecorePage({ site, locale, path: path ?? [] })fromsrc/lib/cache/get-sitecore-page.ts. For preview, usedraftMode(); ifdraft.isEnabled, callclient.getPreview(editingParams)orclient.getDesignLibraryData(editingParams)directly on the SDK client (preview must stay dynamic, not cached). - Dictionary: Use
getSitecoreDictionary({ site, locale })fromsrc/lib/cache/get-sitecore-dictionary.ts(notclient.getDictionarydirectly). This applies thesc:dict:{site}:{locale}tag so dictionary updates can be invalidated by webhook. - 404 content: Use
getSitecoreErrorPage({ site, locale, code: ErrorPage.NotFound })fromsrc/lib/cache/get-sitecore-error-page.ts. 500 content (inglobal-error.tsx) callsclient.getErrorPage(ErrorPage.InternalServerError, ...)directly sinceglobal-error.tsxis a Client Component. - SSG:
generateStaticParams— useclient.getAppRouterStaticParams(sites, routing.locales)(sites from.sitecore/sites.json). Return at least one default param when not generating full paths (e.g. dev or whengenerateStaticPathsis off). - Metadata:
generateMetadatain the same segment callsgetSitecorePageso it hits the same cache entry as the page.
- Where:
src/app/api/revalidate/route.ts. UsescreateSitecoreRevalidateRouteHandlerfrom@sitecore-content-sdk/nextjs/route-handler— a single Sitecore-webhook endpoint. - Auth: When
SITECORE_REVALIDATE_SECRETis non-empty, callers must send the same value inx-revalidate-secret. When empty, revalidation works without that header. - Webhook payload (
updates[]): Send the Sitecore Experience Edge / Content Operations body (updates,invocation_id,continues). The handler maps eachidentifier(with-media/-layoutstripped) tosc:item:<id>:<locale>:latestand revalidates it. - Ad-hoc invalidation (
tags[]): Reuse the same endpoint with{ "tags": ["sc:route:...", "sc:item:..."] }(sc:-prefixed strings are revalidated verbatim) or{ "tags": ["<itemId>"] }(bare item IDs are mapped tosc:item:<id>:<defaultLocale>:latest). Dictionary tags fromsitesare appended on every call. - Do not: Bypass auth, expose the secret in client code, or call
revalidateTagdirectly from components.
- Default: Components are Server Components. Use
'use client'only for interactivity (e.g. hooks, event handlers). - draftMode: Used in layout and page; call
await draftMode()in Server Components that need to know preview state.
This template ships two not-found components and a segment layout that ties them together while staying compatible with SSG and Cache Components:
- Root not-found:
src/app/not-found.tsx. Used as the fallback when no segment handles the route (e.g. unknown site/locale). Falls back toscConfig.defaultSite/scConfig.defaultLanguageand callsgetSitecoreErrorPage({ site, locale, code: ErrorPage.NotFound }), so 404 content gets the same Sitecore cache tags as a normal page. - Segment not-found:
src/app/[site]/[locale]/[[...path]]/not-found.tsx. Triggered when the catch-all page callsnotFound()(e.g. the requested path resolves to no Sitecore page). Reads site/locale viagetCachedPageParams()(set by the segment layout below) and callsgetSitecoreErrorPage(...). Wrapped inNextIntlClientProviderto keep i18n working in 404 markup. - Segment layout:
src/app/[site]/[locale]/[[...path]]/layout.tsx. CallssetCachedPageParams({ site, locale })on every request for this segment. This uses the SDK's Reactcache()basedset/getCachedPageParamshelpers (from@sitecore-content-sdk/nextjs) so the segmentnot-found.tsxcan read{ site, locale }without callingheaders()— which would opt the route out of SSG. Do not callheaders()in the segment not-found; keep usinggetCachedPageParams(). - Root global error:
src/app/global-error.tsxis a Client Component ('use client') that fetchesclient.getErrorPage(ErrorPage.InternalServerError, ...)on the client; it is not cached (the cache helpers are server-side).
- Sitemap:
src/app/api/sitemap/route.ts—createSitemapRouteHandler({ client, sites }). Export{ GET }; usesitesfrom.sitecore/sites.json. WithcacheComponents: true, the explicitdynamic = 'force-dynamic'is not needed (Next.js handles it automatically). - Robots:
src/app/api/robots/route.ts—createRobotsRouteHandler({ client, sites }). Same pattern. - Editing:
src/app/api/editing/config/route.tsandediting/render/route.ts— usecreateEditingConfigRouteHandlerandcreateEditingRenderRouteHandlerswithcomponents,clientComponents(.sitecore/component-map.client.ts),metadata, andclient. - Revalidate:
src/app/api/revalidate/route.ts—createSitecoreRevalidateRouteHandler({ defaultLocale, sites })withsitesfrom.sitecore/sites.json. Export{ POST }. - Rewrites:
next.config.ts→ rewrites for/sitemap*.xml,/robots.txtwithlocale: falseso they are not localized.
- Client:
src/lib/sitecore-client.ts—new SitecoreClient({ ...scConfig }). Used directly for editing/preview and indirectly (via cache helpers) for cached data. - Config:
sitecore.config.ts—defineConfig({ api, defaultSite, defaultLanguage, editingSecret, dictionary: { caching: { enabled: false } }, redirects, multisite, personalize }). The dictionary cache must remain disabled sorevalidateTaginvalidates dictionary data through the Cache Components layer only.
- Server/client components:
.sitecore/component-map.ts(Server) and.sitecore/component-map.client.ts(Client) — register all Sitecore components. Are auto-generated fromsrc/components/. Do not edit manually unless needed. - Layout:
Layout.tsxrenders page layout and placeholders;Providerswrap page and component context;Bootstrapin[site]/layout.tsxreceivessiteName={site}and preview state.
-
Quick checks: If locale or dictionary is wrong, ensure
setRequestLocale(\${site}_${locale}`)is called at the top of the page andsrc/i18n/request.tsparsesrequestLocaleand callsgetSitecoreDictionary. If a content change does not appear, verify the webhook posted toPOST /api/revalidatewith the right secret and check the tag families (sc:route,sc:item,sc:dict`) returned by the cache helpers. -
Security: Use only environment variables in
sitecore.config.ts; never hardcode API keys, editing secret, orSITECORE_REVALIDATE_SECRET. Do not expose secrets in client-side code or logs. Validate and sanitize user input at boundaries. -
Performance: Keep middleware lightweight; use the proxy
matcherso it does not run on/api/*,_next, sitemap, robots, or static assets. Use Server Components for data fetching and the cache helpers under'use cache'so cached payloads carry the right tags. UsegenerateStaticParamsand caching as in the existing page. -
Sitecore patterns: Use SDK field components (
<Text>,<RichText>,<Image>) and validate field existence before render. Regenerate the component maps withnpm run sitecore-tools:generate-mapornpm run sitecore-tools:generate-map:watch; edit the maps manually only when the generator cannot handle the change. Use the cache helpers insrc/lib/cache/for all non-preview Sitecore reads so tags stay consistent across the app. -
Consistency: Follow the existing patterns in
[site]/[locale]/[[...path]]/page.tsx,not-found.tsx,i18n/request.ts(site_locale +getSitecoreDictionary), and API route handlers. When adding routes or rewrites, keep the middleware matcher and next-intl config in sync.
| DO | DON'T |
|---|---|
Use params as Promise and await params in pages and layouts |
Use params synchronously (Next.js 15+) |
Use the cache helpers in src/lib/cache/ for non-preview reads |
Call client.getPage / client.getDictionary directly in pages or i18n |
Use client.getPreview / client.getDesignLibraryData for preview (uncached) |
Wrap preview/draft data in 'use cache'
|
| Run PreviewProxy → BotTrackingProxy → LocaleProxy → … in middleware | Change proxy order (locale must run before multisite for App Router) |
Call setRequestLocale(\${site}_${locale}`)` in the page for next-intl |
Omit setRequestLocale when adding new page branches |
Document SITECORE_REVALIDATE_SECRET in .env.*.example only |
Hardcode the revalidate secret or expose it client-side |
Set SITECORE_REVALIDATE_SECRET and send x-revalidate-secret when you want the endpoint protected |
Hardcode the revalidate secret or expose it client-side |
Keep sitecore.config.ts dictionary cache disabled |
Re-enable the SDK in-process dictionary cache (bypasses revalidateTag) |
| Use Server Components for async data fetching | Put async data fetching in client components when SSR is intended |
Set site/locale via setCachedPageParams in [site]/[locale]/[[...path]]/layout.tsx and read with getCachedPageParams() in the segment not-found.tsx
|
Call headers() in not-found (opts out of SSG) or hardcode site/locale |
Use createXRouteHandler and .sitecore/sites.json for sitemap/robots |
Hardcode site list or commit .env
|
| Use Sitecore field components and validate fields | Expose API keys or editing secret in client code |
Document required env vars in .env.example only |
Commit .env or .env.local
|
Run npm run build after changes to verify the app builds |
Add npm dependencies without explicit user approval |
- Preserve behavior: Do not change the proxy order (PreviewProxy → BotTrackingProxy → LocaleProxy → AppRouterMultisiteProxy → …), the
[site]/[locale]/[[...path]]route shape, the{site}_{locale}next-intl convention, the cache-helper boundary (cache helpers wrap non-preview Sitecore reads; preview/editing useclient.*directly), or thesetCachedPageParams→getCachedPageParamsflow between the segment layout and segmentnot-found.tsx(this is what keeps the 404 SSG-safe). PreservedraftModehandling in layout and page. - Do not expand scope: Limit edits to the app (app router, components, API routes, cache helpers, i18n, config). Do not modify SDK packages or monorepo tooling unless explicitly asked. Do not change CI, lockfiles, or root config.
- Follow existing patterns: When adding routes, layouts, or components, mirror the existing structure. Use the same Sitecore client, cache helpers, component maps, and env-based config. Do not introduce a different way to resolve site/locale, a second client, or a parallel cache layer.
- Verify and stay safe: After edits, the app should build with
npm run build. Do not commit secrets or.env; only document variables in.env.example. Do not add npm dependencies without explicit approval. When in doubt, prefer the existing implementation and ask for clarification. - If the user asks for something that conflicts with these guardrails (e.g. changing proxy order, committing
.env, re-enabling the SDK dictionary cache, or skipping the component map), explain the constraint and suggest a safe alternative rather than complying.
- Add a new Sitecore component: Create the component under
src/components/(maps regenerate automatically duringnpm run dev; otherwise runnpm run sitecore-tools:generate-map), and ensure it is rendered in the layout/placeholder as in existing components. - Add an API route: Create the route under
src/app/api/(e.g.src/app/api/my-route/route.ts), add a rewrite innext.config.tsif the route should be reached from a public URL, and ensure the proxymatcherinproxy.tsstill excludes it (e.g.api/is already excluded). If the route returns cached data, decide whether to use'use cache'with a Sitecore tag and how it should be invalidated. - Add a new cache helper: Add a file under
src/lib/cache/. Inside the function, declare'use cache';, call the SDK client, compute Sitecore tags via the SDK helpers (collectSitecorePageCacheTags,buildSitecoreDictionaryCacheTag, etc.), and callcacheTag(tag)for each one. Match the style ofget-sitecore-page.ts.
Never edit: .next/, node_modules/.
Environment variables: You may add new env vars when needed. Do it carefully: add the variable to .env.example (or .env.remote.example / .env.container.example in this template) with a placeholder or comment; never put real secrets in example files. If editing .env.local for local dev, add only the variable name and tell the user to set the value. Never commit .env or .env.local — they are gitignored. SITECORE_REVALIDATE_SECRET is optional — see comments in .env.*.example.
Edit with care: next.config.ts (cacheComponents: true, rewrites, next-intl plugin), sitecore.config.ts (env only; keep dictionary cache disabled), proxy.ts (matcher and proxy order), src/i18n/routing.ts and request.ts, src/lib/cache/* (tag computation). When adding routes or rewrites, keep middleware matcher and rewrite rules consistent.
Focus on: src/app/, src/components/, src/lib/, src/lib/cache/, src/i18n/, Layout.tsx, Providers.tsx, sitecore.config.ts, next.config.ts, proxy.ts. .sitecore/component-map.ts and .sitecore/component-map.client.ts are auto-generated — do not edit manually.
- Skills.md — Capability groupings for this app; .agents/skills/ provides each capability as an Agent Skill (when-to-use, hard rules, stop conditions) for tools that support the Agent Skills standard.
- CLAUDE.md — Full coding standards and Sitecore patterns for this template.
- .cursor/rules/ — App Router and Sitecore rules.
- Sitecore Content SDK — Official docs.
- Next.js App Router — Routing, Server Components, data fetching.
- Next.js Cache Components —
use cache,cacheTag,revalidateTag. - next-intl — i18n routing and request config.
For head applications / empty starters: If you use this template for your head application (e.g. App Router + Cache Components starter), keep this AGENTS.md as that head application's guide. Do not replace it with the Content SDK monorepo root AGENTS.md — that file describes the SDK source tree, not the head application. Adjust only what is specific to your project (e.g. custom layout or workflow). See the Content SDK README "AI Development Support" section for more on which AGENTS.md to use.
Remember: When in doubt, follow existing patterns in this app and refer to CLAUDE.md and .cursor/rules/ for Sitecore and code standards.