Studio router
You are working on Contentstack Studio. The pack has one specialist skill per task, covering install, provisioning, component registration, section/template authoring, data binding, preview wiring, verification, troubleshooting, and migration.
Loading every skill's frontmatter into every turn was the previous design and it burned ~8K always-on tokens. The current design keeps only this skill hot and routes everything else through a lookup file.
Resolving a skill file: three layouts, try each
The pack ships in one shape and installs in another, and the shape differs per host. A Read against the wrong one returns "File does not exist", and the failure is silent: routing stops and the answer gets improvised from memory, which is the exact thing this router exists to prevent.
For a skill named <name>, resolve in this order and use the first that exists:
| # | Path | Where this is the layout |
|---|---|---|
| 1 | ../<name>/SKILL.md | Nested: Claude Code gives every skill its own directory (~/.claude/skills/<name>/SKILL.md). |
| 2 | ./<name>.md | Flat: the repo (skills/src/, docs/prompts/), and Windsurf / Cline / Continue installs. |
| 3 | ./<name>.mdc | Flat, Cursor: .cursor/rules/ loads only .mdc, so the installer changes the extension. Contents are identical. |
Same rule for skills-index.md: try ./skills-index.md, then ../studio/skills-index.md, then ./skills-index.mdc.
If both fail, say so out loud: "the skill pack looks mis-installed, <name> resolved to neither path", and do NOT quietly fall back to general knowledge. A silent fallback looks identical to the pack working, so the user never learns their install is broken. Every cross-reference in this file and in skills-index.md is written as <name>.md for readability. Apply this resolution rule to all of them.
What to do
Setup intent takes precedence over keyword matching. “Enable Studio”, “make this project Studio ready”, “Studio setup”, and equivalent whole-app requests run convert-project-to-studio through verification. install-studio alone is for an explicit SDK-only request. Preserve the active setup objective across replies such as “yes”, “start”, and “OAuth done”; resume the pending step rather than routing that reply as a new task. Use the orchestrator's scoped completion checklist; authoring requirements below apply only when components or compositions are authored.
Discover MCP execution before resolving raw HTTP credentials. Follow install-contentstack-mcp to inspect callable tools in the current host and verify the target with a read call. A stored OAuth session is not evidence of a connected MCP server. Prefer available MCP operations; use OAuth HTTP for missing operations or an explicitly recorded connection limitation. Never infer tool availability solely from a conversation flag.
-
Read
skills-index.md(resolution above). It has two parts. -
Trigger table. Scan the user's most recent message against the phrases in the left column. If one row matches, Read that skill's file and follow it. If several match, run them in the order the table implies (installs before authoring,
analyze-project-fitbefore anything state-changing). -
Topic map. Use this when no trigger phrase fires but the topic is Studio-shaped. The user is exploring or asking a conceptual question. Pick the closest skill by name + one-liner and Read it.
-
If nothing matches after both passes, answer from general Studio knowledge, but say so, and check whether a skill is missing that should exist.
Standing rule: atomic and reusable, every time
This applies on every Studio turn, whatever the user asked for. It is not optional and it is not only a planning concern.
Before creating, registering, or authoring anything:
- Decompose first. Never register or build a component that renders more than one piece of content. The test: does it render more than one heading / image / body / list? If yes, run
decompose-jsx-to-atomicsbefore anything else. One Section per page, or one component swallowing a whole page, is a defect, not a shortcut. - Compose from atoms by default: do NOT reach for a pre-composed composite. A palette usually contains both atoms + layout primitives (
*-atom-heading,*-stack,*-row,*-grid,*-section-shell) and composites that already bundle a whole shape (*-feature-card,*-hero,*-card-grid). Dropping one composite is faster and looks identical on the canvas, and it is the wrong default: its internals are fixed in code, so the author cannot restructure it, the spacing inside it cannot be matched to a comp, and the design is no longer expressed as a composition. Build the shape out of atoms unless the user explicitly asks to reuse an existing component. If a composite would clearly save real work, say so and let them choose. Do not decide it silently. - Reuse atoms, not shapes. Reuse means the same
*-atom-headingeverywhere, not a ready-made card. Two things differing only in spacing are one Section plus an exposed prop, never two. - Shared shells are Sections. Header, footer, nav, menus, breadcrumbs render on every page and are the highest-value reusable pieces in any project. They are never "atomic, no binding". Their menus are Repeaters over a field, never hardcoded lists. Otherwise an author cannot add a menu item without a developer.
- One atomic prop maps to one CT field. A single prop taking a whole object is how binding breaks: the binder recursively unwraps single-key objects for every type except
object/array, so a coarse prop renders blank with no error.
If the user's request would skip this ("just register the page component", "make the whole page one section"), say what it costs (nothing reusable, authors can't reorder, bindings break) and propose the decomposed shape instead. Do it their way only if they confirm after hearing that.
Standing rule: definition of done
Also every Studio turn. Eight deliverables decide whether what you built is usable by a marketer rather than only by a developer. Every one fails silently. The page still renders, nothing errors, and the gap surfaces only when a non-developer opens Studio:
cslp: { appendTags: true }in the SDK init, once per project.studioAttributeson the root +wrap: falsein the register entry +$proptwins, every registered component.thumbnailUrl, everyregisterComponentcall.ui_preview, every Section composition. The editor writes this for itself by screenshotting the canvas on save. A CMA-authored Section has it empty forever unless you supply an asset uid explicitly. So "at write time" means have the asset ready and include it, not "the create call handles it". Seeauthor-composition-via-api§ Section thumbnails for the three ways to do it. Scripted authoring is exactly where this gets skipped, and the only signal is a blank tile in the palette.- Live Preview enabled on the stack + preview token, once per project.
- The
/canvasroute mounted, once per project. - Published to the environment the project targets, every Section and Template, and again after every tree edit. Delivery otherwise serves the old version while the CMA and canvas both look correct.
- Composed of Sections: every Template. Read the saved composition back and count
section-compositionnodes. Zero means the page is one component bound to the whole body. The standing rule above already forbids that shape, and a build still reached it, because a monolith renders correctly and fails no other check here. Counting is the only thing that catches it.
Read complete-the-build for the how and the verification block. The runnable node count is in build-connected-template § Post-build structural check. Never report a build done with any of these outstanding. Say "created, thumbnail pending" and then finish it. State them in the plan up front too, so what the user approves is what they get.
Standing rule: auth preflight
Settle the credential before the first API call of the turn, not when one fails. Every specialist repeats this. It lives here because it must hold even before a specialist is pulled.
- Resolve OAuth-first per
authenticate-cma:CS_OAUTH_ACCESS_TOKEN, else the Contentstack MCP's stored session (~/Library/Application Support/ContentstackMCP/oauth-config.jsonand its platform equivalents). - Never ask the user for a session
authtoken. It is a full user-session credential and is the last rung, requested only after a freshly refreshed OAuth token has been observed to fail. - Access tokens last 59 minutes and refresh tokens rotate. Tooling that refreshes must persist the new pair. A bare
curlloop does neither, so long work belongs in a script. - If nothing resolves, or a refresh returns
400 invalid_refresh_token, hand the user! CONTENTSTACK_REGION=<code> npx @contentstack/mcp --authand wait. It needs a TTY and a browser, you cannot run it for them. The region must be in the command.DEFAULT_REGIONisNA, so omitting it silently authenticates against NA prod. 403 error_code 316is not an auth failure. It is a valid credential aimed at a stack outside its org. Re-authenticating fixes nothing. Check withGET /v3/stacks+organization_uid.
Rules
- Always re-Read the skill file. Never answer from a remembered summary. The source of truth is the file on disk.
- A failed Read is a hard stop, not a shrug. If a skill file won't resolve under either layout above, report it. Improvising over a broken install is worse than erroring: the user believes the pack ran when it didn't.
- Never skip step 1. The lookup is cheap. Missing a skill is expensive.
- If the user's request implies multiple skills (e.g. "install and set up preview routes"), pull each in the order they must run: skills-index.md's Trigger table encodes that ordering.
- Prerequisite skills: many authoring skills assume
install-studio+configure-studioare done. If the user's project state is unclear, runanalyze-project-fitfirst.
Why this exists
The old design put a rich description + when-to-use in every skill's frontmatter so Claude Code's classifier could auto-fire the right one. That worked but cost every downstream customer ~8K tokens per turn, every skill description loaded on every single message, whether the turn was about Studio or not.
The new design collapses those descriptions into skills-index.md, keeps only this file hot, and shifts routing into an explicit two-step read. Per-turn always-on cost drops by ~7.4K tokens. On non-Studio turns, this skill doesn't fire and the whole pack sits cold.
Nothing was deleted. Every trigger phrase from the old per-skill when-to-use fields lives in skills-index.md's Trigger table, verbatim.