@sanity/cli 6.0.0-alpha.6 → 6.0.0-alpha.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/README.md +2907 -109
  2. package/dist/actions/build/renderDocumentWorker/tryLoadDocumentComponent.js +2 -1
  3. package/dist/actions/build/renderDocumentWorker/tryLoadDocumentComponent.js.map +1 -1
  4. package/dist/actions/init/bootstrapLocalTemplate.d.ts +13 -0
  5. package/dist/actions/init/bootstrapLocalTemplate.js +136 -0
  6. package/dist/actions/init/bootstrapLocalTemplate.js.map +1 -0
  7. package/dist/actions/init/bootstrapRemoteTemplate.d.ts +12 -0
  8. package/dist/actions/init/bootstrapRemoteTemplate.js +109 -0
  9. package/dist/actions/init/bootstrapRemoteTemplate.js.map +1 -0
  10. package/dist/actions/init/bootstrapTemplate.d.ts +18 -0
  11. package/dist/actions/init/bootstrapTemplate.js +32 -0
  12. package/dist/actions/init/bootstrapTemplate.js.map +1 -0
  13. package/dist/actions/init/checkNextJsReactCompatibility.d.ts +9 -0
  14. package/dist/actions/init/checkNextJsReactCompatibility.js +21 -0
  15. package/dist/actions/init/checkNextJsReactCompatibility.js.map +1 -0
  16. package/dist/actions/init/countNestedFolders.d.ts +1 -0
  17. package/dist/actions/init/countNestedFolders.js +6 -0
  18. package/dist/actions/init/countNestedFolders.js.map +1 -0
  19. package/dist/actions/init/createAppCliConfig.d.ts +5 -0
  20. package/dist/actions/init/createAppCliConfig.js +19 -0
  21. package/dist/actions/init/createAppCliConfig.js.map +1 -0
  22. package/dist/actions/init/createCliConfig.d.ts +6 -0
  23. package/dist/actions/init/createCliConfig.js +27 -0
  24. package/dist/actions/init/createCliConfig.js.map +1 -0
  25. package/dist/actions/init/createPackageManifest.d.ts +6 -0
  26. package/dist/actions/init/createPackageManifest.js +86 -0
  27. package/dist/actions/init/createPackageManifest.js.map +1 -0
  28. package/dist/actions/init/createStudioConfig.d.ts +13 -0
  29. package/dist/actions/init/createStudioConfig.js +41 -0
  30. package/dist/actions/init/createStudioConfig.js.map +1 -0
  31. package/dist/actions/init/env/createOrAppendEnvVars.d.ts +12 -0
  32. package/dist/actions/init/env/createOrAppendEnvVars.js +25 -0
  33. package/dist/actions/init/env/createOrAppendEnvVars.js.map +1 -0
  34. package/dist/actions/init/env/parseAndUpdateEnvVars.d.ts +9 -0
  35. package/dist/actions/init/env/parseAndUpdateEnvVars.js +42 -0
  36. package/dist/actions/init/env/parseAndUpdateEnvVars.js.map +1 -0
  37. package/dist/actions/init/env/writeEnvVarsToFile.d.ts +12 -0
  38. package/dist/actions/init/env/writeEnvVarsToFile.js +49 -0
  39. package/dist/actions/init/env/writeEnvVarsToFile.js.map +1 -0
  40. package/dist/actions/init/fetchPostInitPrompt.d.ts +6 -0
  41. package/dist/actions/init/fetchPostInitPrompt.js +30 -0
  42. package/dist/actions/init/fetchPostInitPrompt.js.map +1 -0
  43. package/dist/actions/init/git.d.ts +1 -0
  44. package/dist/actions/init/git.js +65 -0
  45. package/dist/actions/init/git.js.map +1 -0
  46. package/dist/actions/init/processTemplate.d.ts +7 -0
  47. package/dist/actions/init/processTemplate.js +56 -0
  48. package/dist/actions/init/processTemplate.js.map +1 -0
  49. package/dist/actions/init/remoteTemplate.d.ts +0 -3
  50. package/dist/actions/init/remoteTemplate.js +2 -40
  51. package/dist/actions/init/remoteTemplate.js.map +1 -1
  52. package/dist/actions/init/resolvePackageManager.d.ts +10 -0
  53. package/dist/actions/init/resolvePackageManager.js +20 -0
  54. package/dist/actions/init/resolvePackageManager.js.map +1 -0
  55. package/dist/actions/init/setupMCP.d.ts +21 -0
  56. package/dist/actions/init/setupMCP.js +258 -0
  57. package/dist/actions/init/setupMCP.js.map +1 -0
  58. package/dist/actions/init/templates/appQuickstart.d.ts +3 -0
  59. package/dist/actions/init/templates/appQuickstart.js +28 -0
  60. package/dist/actions/init/templates/appQuickstart.js.map +1 -0
  61. package/dist/actions/init/templates/appSanityUi.d.ts +3 -0
  62. package/dist/actions/init/templates/appSanityUi.js +30 -0
  63. package/dist/actions/init/templates/appSanityUi.js.map +1 -0
  64. package/dist/actions/init/templates/blog.d.ts +3 -0
  65. package/dist/actions/init/templates/blog.js +4 -0
  66. package/dist/actions/init/templates/blog.js.map +1 -0
  67. package/dist/actions/init/templates/clean.d.ts +3 -0
  68. package/dist/actions/init/templates/clean.js +4 -0
  69. package/dist/actions/init/templates/clean.js.map +1 -0
  70. package/dist/actions/init/templates/getStarted.d.ts +3 -0
  71. package/dist/actions/init/templates/getStarted.js +35 -0
  72. package/dist/actions/init/templates/getStarted.js.map +1 -0
  73. package/dist/actions/init/templates/index.d.ts +3 -0
  74. package/dist/actions/init/templates/index.js +23 -0
  75. package/dist/actions/init/templates/index.js.map +1 -0
  76. package/dist/actions/init/templates/moviedb.d.ts +3 -0
  77. package/dist/actions/init/templates/moviedb.js +34 -0
  78. package/dist/actions/init/templates/moviedb.js.map +1 -0
  79. package/dist/actions/init/templates/nextjs/index.d.ts +6 -0
  80. package/dist/actions/init/templates/nextjs/index.js +213 -0
  81. package/dist/actions/init/templates/nextjs/index.js.map +1 -0
  82. package/dist/actions/init/templates/nextjs/schemaTypes/blog.d.ts +3 -0
  83. package/dist/actions/init/templates/nextjs/schemaTypes/blog.js +247 -0
  84. package/dist/actions/init/templates/nextjs/schemaTypes/blog.js.map +1 -0
  85. package/dist/actions/init/templates/quickstart.d.ts +3 -0
  86. package/dist/actions/init/templates/quickstart.js +4 -0
  87. package/dist/actions/init/templates/quickstart.js.map +1 -0
  88. package/dist/actions/init/templates/shopify.d.ts +3 -0
  89. package/dist/actions/init/templates/shopify.js +77 -0
  90. package/dist/actions/init/templates/shopify.js.map +1 -0
  91. package/dist/actions/init/templates/shopifyOnline.d.ts +3 -0
  92. package/dist/actions/init/templates/shopifyOnline.js +49 -0
  93. package/dist/actions/init/templates/shopifyOnline.js.map +1 -0
  94. package/dist/actions/init/types.d.ts +15 -0
  95. package/dist/actions/init/types.js +3 -0
  96. package/dist/actions/init/types.js.map +1 -0
  97. package/dist/actions/init/updateInitialTemplateMetadata.d.ts +1 -0
  98. package/dist/actions/init/updateInitialTemplateMetadata.js +17 -0
  99. package/dist/actions/init/updateInitialTemplateMetadata.js.map +1 -0
  100. package/dist/actions/telemetry/resolveConsent.d.ts +1 -9
  101. package/dist/actions/telemetry/resolveConsent.js +2 -2
  102. package/dist/actions/telemetry/resolveConsent.js.map +1 -1
  103. package/dist/actions/telemetry/setConsent.d.ts +1 -4
  104. package/dist/actions/telemetry/setConsent.js +4 -8
  105. package/dist/actions/telemetry/setConsent.js.map +1 -1
  106. package/dist/commands/init.d.ts +24 -14
  107. package/dist/commands/init.js +544 -26
  108. package/dist/commands/init.js.map +1 -1
  109. package/dist/commands/telemetry/disable.js +0 -1
  110. package/dist/commands/telemetry/disable.js.map +1 -1
  111. package/dist/commands/telemetry/enable.js +0 -1
  112. package/dist/commands/telemetry/enable.js.map +1 -1
  113. package/dist/commands/telemetry/status.js +1 -3
  114. package/dist/commands/telemetry/status.js.map +1 -1
  115. package/dist/hooks/prerun/flushTelemetry.worker.d.ts +2 -0
  116. package/dist/hooks/prerun/flushTelemetry.worker.js +22 -0
  117. package/dist/hooks/prerun/flushTelemetry.worker.js.map +1 -0
  118. package/dist/hooks/prerun/setupTelemetry.js +65 -1
  119. package/dist/hooks/prerun/setupTelemetry.js.map +1 -1
  120. package/dist/prompts/init/nextjs.d.ts +5 -0
  121. package/dist/prompts/init/nextjs.js +56 -0
  122. package/dist/prompts/init/nextjs.js.map +1 -0
  123. package/dist/prompts/init/promptForTypescript.d.ts +0 -1
  124. package/dist/prompts/init/promptForTypescript.js +0 -6
  125. package/dist/prompts/init/promptForTypescript.js.map +1 -1
  126. package/dist/services/mcp.d.ts +10 -0
  127. package/dist/services/mcp.js +16 -0
  128. package/dist/services/mcp.js.map +1 -1
  129. package/dist/services/projects.d.ts +5 -2
  130. package/dist/services/projects.js +37 -0
  131. package/dist/services/projects.js.map +1 -1
  132. package/dist/services/telemetry.d.ts +2 -0
  133. package/dist/services/telemetry.js +20 -0
  134. package/dist/services/telemetry.js.map +1 -0
  135. package/dist/studioDependencies.d.ts +16 -0
  136. package/dist/studioDependencies.js +24 -0
  137. package/dist/studioDependencies.js.map +1 -0
  138. package/dist/telemetry/cli.telemetry.d.ts +20 -0
  139. package/dist/telemetry/cli.telemetry.js +8 -0
  140. package/dist/telemetry/cli.telemetry.js.map +1 -0
  141. package/dist/telemetry/store/cleanupOldTelemetryFiles.d.ts +5 -0
  142. package/dist/telemetry/store/cleanupOldTelemetryFiles.js +30 -0
  143. package/dist/telemetry/store/cleanupOldTelemetryFiles.js.map +1 -0
  144. package/dist/telemetry/store/createTelemetryStore.d.ts +39 -0
  145. package/dist/telemetry/store/createTelemetryStore.js +95 -0
  146. package/dist/telemetry/store/createTelemetryStore.js.map +1 -0
  147. package/dist/telemetry/store/createTraceId.d.ts +10 -0
  148. package/dist/telemetry/store/createTraceId.js +10 -0
  149. package/dist/telemetry/store/createTraceId.js.map +1 -0
  150. package/dist/telemetry/store/debug.d.ts +5 -0
  151. package/dist/telemetry/store/debug.js +7 -0
  152. package/dist/telemetry/store/debug.js.map +1 -0
  153. package/dist/telemetry/store/findTelemetryFiles.d.ts +13 -0
  154. package/dist/telemetry/store/findTelemetryFiles.js +34 -0
  155. package/dist/telemetry/store/findTelemetryFiles.js.map +1 -0
  156. package/dist/telemetry/store/flushTelemetryFiles.d.ts +20 -0
  157. package/dist/telemetry/store/flushTelemetryFiles.js +107 -0
  158. package/dist/telemetry/store/flushTelemetryFiles.js.map +1 -0
  159. package/dist/telemetry/store/generateTelemetryFilePath.d.ts +17 -0
  160. package/dist/telemetry/store/generateTelemetryFilePath.js +30 -0
  161. package/dist/telemetry/store/generateTelemetryFilePath.js.map +1 -0
  162. package/dist/telemetry/store/getTelemetryBaseInfo.d.ts +27 -0
  163. package/dist/telemetry/store/getTelemetryBaseInfo.js +34 -0
  164. package/dist/telemetry/store/getTelemetryBaseInfo.js.map +1 -0
  165. package/dist/telemetry/store/logger.d.ts +6 -0
  166. package/dist/telemetry/store/logger.js +54 -0
  167. package/dist/telemetry/store/logger.js.map +1 -0
  168. package/dist/telemetry/store/trace.d.ts +6 -0
  169. package/dist/telemetry/store/trace.js +150 -0
  170. package/dist/telemetry/store/trace.js.map +1 -0
  171. package/dist/telemetry/utils/readNDJSON.d.ts +10 -0
  172. package/dist/telemetry/utils/readNDJSON.js +18 -0
  173. package/dist/telemetry/utils/readNDJSON.js.map +1 -0
  174. package/dist/types.d.ts +33 -0
  175. package/dist/types.js.map +1 -1
  176. package/dist/typings/deepSortObject.d.js +2 -0
  177. package/dist/typings/deepSortObject.d.js.map +1 -0
  178. package/dist/util/copy.d.ts +5 -0
  179. package/dist/util/copy.js +37 -0
  180. package/dist/util/copy.js.map +1 -0
  181. package/dist/util/detectRuntime.d.ts +8 -0
  182. package/dist/util/detectRuntime.js +20 -0
  183. package/dist/util/detectRuntime.js.map +1 -0
  184. package/dist/util/frameworkPort.d.ts +12 -0
  185. package/dist/util/frameworkPort.js +61 -0
  186. package/dist/util/frameworkPort.js.map +1 -0
  187. package/dist/util/fsUtils.d.ts +2 -0
  188. package/dist/util/fsUtils.js +34 -0
  189. package/dist/util/fsUtils.js.map +1 -0
  190. package/dist/util/getProjectDefaults.d.ts +11 -0
  191. package/dist/util/getProjectDefaults.js +77 -0
  192. package/dist/util/getProjectDefaults.js.map +1 -0
  193. package/dist/util/isStaging.d.ts +7 -0
  194. package/dist/util/isStaging.js +10 -0
  195. package/dist/util/isStaging.js.map +1 -0
  196. package/dist/util/packageManager/packageManagerChoice.d.ts +2 -0
  197. package/dist/util/packageManager/packageManagerChoice.js +8 -0
  198. package/dist/util/packageManager/packageManagerChoice.js.map +1 -1
  199. package/dist/util/parseArguments.d.ts +35 -0
  200. package/dist/util/parseArguments.js +42 -0
  201. package/dist/util/parseArguments.js.map +1 -0
  202. package/dist/util/readdirRecursive.d.ts +5 -0
  203. package/dist/util/readdirRecursive.js +24 -0
  204. package/dist/util/readdirRecursive.js.map +1 -0
  205. package/dist/util/resolveLatestVersions.d.ts +7 -0
  206. package/dist/util/resolveLatestVersions.js +21 -0
  207. package/dist/util/resolveLatestVersions.js.map +1 -0
  208. package/oclif.manifest.json +154 -141
  209. package/package.json +26 -14
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/init/remoteTemplate.ts"],"sourcesContent":["import {access, readFile, writeFile} from 'node:fs/promises'\nimport {join, posix, sep} from 'node:path'\nimport {Readable} from 'node:stream'\nimport {pipeline} from 'node:stream/promises'\n\nimport {subdebug} from '@sanity/cli-core'\nimport {type SanityClient} from '@sanity/client'\nimport {ENV_TEMPLATE_FILES, REQUIRED_ENV_VAR} from '@sanity/template-validator'\nimport {x} from 'tar'\n\nimport {readPackageJson} from '../../util/readPackageJson.js'\n\nconst debug = subdebug('remoteTemplate')\n\nconst DISALLOWED_PATHS = [\n // Prevent security risks from unknown GitHub Actions\n '/.github/',\n]\n\nconst ENV_VAR = {\n ...REQUIRED_ENV_VAR,\n READ_TOKEN: 'SANITY_API_READ_TOKEN',\n WRITE_TOKEN: 'SANITY_API_WRITE_TOKEN',\n} as const\n\nconst API_READ_TOKEN_ROLE = 'viewer'\nconst API_WRITE_TOKEN_ROLE = 'editor'\n\ntype EnvData = {\n dataset: string\n projectId: string\n readToken?: string\n writeToken?: string\n}\n\ntype GitHubUrlString =\n | `https://github.com/${string}/${string}`\n | `https://www.github.com/${string}/${string}`\n\nexport type RepoInfo = {\n branch: string\n filePath: string\n name: string\n username: string\n}\n\nexport function getGitHubRawContentUrl(repoInfo: RepoInfo): string {\n const {branch, filePath, name, username} = repoInfo\n return `https://raw.githubusercontent.com/${username}/${name}/${branch}/${filePath}`\n}\n\nfunction isGitHubRepoShorthand(value: string): boolean {\n if (URL.canParse(value)) {\n return false\n }\n // This supports :owner/:repo and :owner/:repo/nested/path, e.g.\n // sanity-io/sanity\n // sanity-io/sanity/templates/next-js\n // sanity-io/templates/live-content-api\n // sanity-io/sanity/packages/@sanity/cli/test/test-template\n return /^[\\w-]+\\/[\\w-.]+(\\/[@\\w-.]+)*$/.test(value)\n}\n\nfunction isGitHubRepoUrl(value: string | URL): value is GitHubUrlString | URL {\n if (URL.canParse(value) === false) {\n return false\n }\n const url = new URL(value)\n const pathSegments = url.pathname.slice(1).split('/')\n\n return (\n url.protocol === 'https:' &&\n url.hostname === 'github.com' &&\n // The pathname must have at least 2 segments. If it has more than 2, the\n // third must be \"tree\" and it must have at least 4 segments.\n // https://github.com/:owner/:repo\n // https://github.com/:owner/:repo/tree/:ref\n pathSegments.length >= 2 &&\n (pathSegments.length > 2 ? pathSegments[2] === 'tree' && pathSegments.length >= 4 : true)\n )\n}\n\nasync function downloadTarStream(url: string, bearerToken?: string): Promise<Readable> {\n const headers: Record<string, string> = {}\n if (bearerToken) {\n headers.Authorization = `Bearer ${bearerToken}`\n }\n\n const res = await fetch(url, {headers})\n\n if (!res.body) {\n throw new Error(`Failed to download: ${url}`)\n }\n\n // eslint-disable-next-line n/no-unsupported-features/node-builtins, @typescript-eslint/no-explicit-any\n return Readable.fromWeb(res.body as any)\n}\n\nexport function checkIsRemoteTemplate(templateName?: string): boolean {\n return templateName?.includes('/') ?? false\n}\n\nexport async function getGitHubRepoInfo(value: string, bearerToken?: string): Promise<RepoInfo> {\n let username = ''\n let name = ''\n let branch = ''\n let filePath = ''\n\n if (isGitHubRepoShorthand(value)) {\n const parts = value.split('/')\n username = parts[0]\n name = parts[1]\n // If there are more segments after owner/repo, they form the file path\n if (parts.length > 2) {\n filePath = parts.slice(2).join('/')\n }\n }\n\n if (isGitHubRepoUrl(value)) {\n const url = new URL(value)\n const pathSegments = url.pathname.slice(1).split('/')\n username = pathSegments[0]\n name = pathSegments[1]\n\n // If we have a \"tree\" segment, everything after branch is the file path\n if (pathSegments[2] === 'tree') {\n branch = pathSegments[3]\n if (pathSegments.length > 4) {\n filePath = pathSegments.slice(4).join('/')\n }\n }\n }\n\n if (!username || !name) {\n throw new Error('Invalid GitHub repository format')\n }\n\n const tokenMessage =\n 'GitHub repository not found. For private repositories, use --template-token to provide an access token.\\n\\n' +\n 'You can generate a new token at https://github.com/settings/personal-access-tokens/new\\n' +\n 'Set the token to \"read-only\" with repository access and a short expiry (e.g. 7 days) for security.'\n\n try {\n const headers: Record<string, string> = {}\n if (bearerToken) {\n headers.Authorization = `Bearer ${bearerToken}`\n }\n\n const infoResponse = await fetch(`https://api.github.com/repos/${username}/${name}`, {\n headers,\n })\n\n if (infoResponse.status !== 200) {\n if (infoResponse.status === 404) {\n throw new Error(tokenMessage)\n }\n throw new Error('GitHub repository not found')\n }\n\n const info = await infoResponse.json()\n\n return {\n branch: branch || info.default_branch,\n filePath,\n name,\n username,\n }\n } catch {\n throw new Error(tokenMessage)\n }\n}\n\nexport async function downloadAndExtractRepo(\n root: string,\n {branch, filePath, name, username}: RepoInfo,\n bearerToken?: string,\n): Promise<void> {\n let rootPath: string | null = null\n await pipeline(\n await downloadTarStream(\n `https://codeload.github.com/${username}/${name}/tar.gz/${branch}`,\n bearerToken,\n ),\n x({\n cwd: root,\n filter: (p: string) => {\n const posixPath = p.split(sep).join(posix.sep)\n if (rootPath === null) {\n const pathSegments = posixPath.split(posix.sep)\n rootPath = pathSegments.length > 0 ? pathSegments[0] : null\n }\n for (const disallowedPath of DISALLOWED_PATHS) {\n if (posixPath.includes(disallowedPath)) return false\n }\n return posixPath.startsWith(`${rootPath}${filePath ? `/${filePath}/` : '/'}`)\n },\n strip: filePath ? filePath.split('/').length + 1 : 1,\n }),\n )\n}\n\nexport async function checkIfNeedsApiToken(root: string, type: 'read' | 'write'): Promise<boolean> {\n try {\n const templatePath = await Promise.any(\n ENV_TEMPLATE_FILES.map(async (file) => {\n await access(join(root, file))\n return file\n }),\n )\n const templateContent = await readFile(join(root, templatePath), 'utf8')\n return templateContent.includes(type === 'read' ? ENV_VAR.READ_TOKEN : ENV_VAR.WRITE_TOKEN)\n } catch {\n return false\n }\n}\n\nexport async function applyEnvVariables(\n root: string,\n envData: EnvData,\n targetName = '.env',\n): Promise<void> {\n const templatePath = await Promise.any(\n ENV_TEMPLATE_FILES.map(async (file) => {\n await access(join(root, file))\n return file\n }),\n ).catch(() => {})\n\n if (!templatePath) {\n return // No template .env file found, skip\n }\n\n try {\n const templateContent = await readFile(join(root, templatePath), 'utf8')\n const {dataset, projectId, readToken = '', writeToken = ''} = envData\n\n const findAndReplaceVariable = (\n content: string,\n varRegex: RegExp | string,\n value: string,\n useQuotes: boolean,\n ) => {\n const varPattern = typeof varRegex === 'string' ? varRegex : varRegex.source\n const pattern = new RegExp(`.*${varPattern}=.*$`, 'gm')\n const matches = content.matchAll(pattern)\n\n let result = content\n for (const match of matches) {\n if (!match[0]) continue\n const varName = match[0].split('=')[0].trim()\n result = result.replaceAll(\n new RegExp(`${varName}=.*$`, 'gm'),\n `${varName}=${useQuotes ? `\"${value}\"` : value}`,\n )\n }\n\n return result\n }\n\n let envContent = templateContent\n const vars = [\n {pattern: ENV_VAR.PROJECT_ID, value: projectId},\n {pattern: ENV_VAR.DATASET, value: dataset},\n {pattern: ENV_VAR.READ_TOKEN, value: readToken},\n {pattern: ENV_VAR.WRITE_TOKEN, value: writeToken},\n ]\n const useQuotes = templateContent.includes('=\"')\n\n for (const {pattern, value} of vars) {\n envContent = findAndReplaceVariable(envContent, pattern, value, useQuotes)\n }\n\n await writeFile(join(root, targetName), envContent)\n } catch (err) {\n debug(`Error setting environment variables: ${err}`)\n throw new Error(\n 'Failed to set environment variables. This could be due to file permissions or the .env file format. See https://www.sanity.io/docs/environment-variables for details on environment variable setup.',\n )\n }\n}\n\nexport async function tryApplyPackageName(root: string, name: string): Promise<void> {\n try {\n const pkg = await readPackageJson(join(root, 'package.json'))\n pkg.name = name\n\n await writeFile(join(root, 'package.json'), JSON.stringify(pkg, null, 2))\n } catch {\n // noop\n }\n}\n\nexport async function generateSanityApiToken(\n client: SanityClient,\n label: string,\n type: 'read' | 'write',\n projectId: string,\n): Promise<string> {\n // const client = await getGlobalCliClient({\n // apiVersion: 'v2021-06-07',\n // requireUser: true,\n // })\n const response = await client.request<{key: string}>({\n body: {\n label: `${label} (${Date.now()})`,\n roleName: type === 'read' ? API_READ_TOKEN_ROLE : API_WRITE_TOKEN_ROLE,\n },\n method: 'POST',\n uri: `/projects/${projectId}/tokens`,\n })\n return response.key\n}\n\nexport async function setCorsOrigin(\n client: SanityClient,\n origin: string,\n projectId: string,\n): Promise<void> {\n try {\n // const client = await getGlobalCliClient({\n // apiVersion: 'v2021-06-07',\n // requireUser: true,\n // })\n\n await client.withConfig({projectId}).request({\n body: {allowCredentials: true, origin: origin}, // allowCredentials is true to allow for embedded studios if needed\n method: 'POST',\n url: '/cors',\n })\n } catch (error) {\n // Silent fail, it most likely means that the origin is already set\n debug('Failed to set CORS origin', error)\n }\n}\n"],"names":["access","readFile","writeFile","join","posix","sep","Readable","pipeline","subdebug","ENV_TEMPLATE_FILES","REQUIRED_ENV_VAR","x","readPackageJson","debug","DISALLOWED_PATHS","ENV_VAR","READ_TOKEN","WRITE_TOKEN","API_READ_TOKEN_ROLE","API_WRITE_TOKEN_ROLE","getGitHubRawContentUrl","repoInfo","branch","filePath","name","username","isGitHubRepoShorthand","value","URL","canParse","test","isGitHubRepoUrl","url","pathSegments","pathname","slice","split","protocol","hostname","length","downloadTarStream","bearerToken","headers","Authorization","res","fetch","body","Error","fromWeb","checkIsRemoteTemplate","templateName","includes","getGitHubRepoInfo","parts","tokenMessage","infoResponse","status","info","json","default_branch","downloadAndExtractRepo","root","rootPath","cwd","filter","p","posixPath","disallowedPath","startsWith","strip","checkIfNeedsApiToken","type","templatePath","Promise","any","map","file","templateContent","applyEnvVariables","envData","targetName","catch","dataset","projectId","readToken","writeToken","findAndReplaceVariable","content","varRegex","useQuotes","varPattern","source","pattern","RegExp","matches","matchAll","result","match","varName","trim","replaceAll","envContent","vars","PROJECT_ID","DATASET","err","tryApplyPackageName","pkg","JSON","stringify","generateSanityApiToken","client","label","response","request","Date","now","roleName","method","uri","key","setCorsOrigin","origin","withConfig","allowCredentials","error"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/actions/init/remoteTemplate.ts"],"sourcesContent":["import {access, readFile, writeFile} from 'node:fs/promises'\nimport {join, posix, sep} from 'node:path'\nimport {Readable} from 'node:stream'\nimport {pipeline} from 'node:stream/promises'\n\nimport {subdebug} from '@sanity/cli-core'\nimport {ENV_TEMPLATE_FILES, REQUIRED_ENV_VAR} from '@sanity/template-validator'\nimport {x} from 'tar'\n\nimport {readPackageJson} from '../../util/readPackageJson.js'\n\nconst debug = subdebug('remoteTemplate')\n\nconst DISALLOWED_PATHS = [\n // Prevent security risks from unknown GitHub Actions\n '/.github/',\n]\n\nconst ENV_VAR = {\n ...REQUIRED_ENV_VAR,\n READ_TOKEN: 'SANITY_API_READ_TOKEN',\n WRITE_TOKEN: 'SANITY_API_WRITE_TOKEN',\n} as const\n\ntype EnvData = {\n dataset: string\n projectId: string\n readToken?: string\n writeToken?: string\n}\n\ntype GitHubUrlString =\n | `https://github.com/${string}/${string}`\n | `https://www.github.com/${string}/${string}`\n\nexport type RepoInfo = {\n branch: string\n filePath: string\n name: string\n username: string\n}\n\nexport function getGitHubRawContentUrl(repoInfo: RepoInfo): string {\n const {branch, filePath, name, username} = repoInfo\n return `https://raw.githubusercontent.com/${username}/${name}/${branch}/${filePath}`\n}\n\nfunction isGitHubRepoShorthand(value: string): boolean {\n if (URL.canParse(value)) {\n return false\n }\n // This supports :owner/:repo and :owner/:repo/nested/path, e.g.\n // sanity-io/sanity\n // sanity-io/sanity/templates/next-js\n // sanity-io/templates/live-content-api\n // sanity-io/sanity/packages/@sanity/cli/test/test-template\n return /^[\\w-]+\\/[\\w-.]+(\\/[@\\w-.]+)*$/.test(value)\n}\n\nfunction isGitHubRepoUrl(value: string | URL): value is GitHubUrlString | URL {\n if (!URL.canParse(value)) {\n return false\n }\n const url = new URL(value)\n const pathSegments = url.pathname.slice(1).split('/')\n\n return (\n url.protocol === 'https:' &&\n url.hostname === 'github.com' &&\n // The pathname must have at least 2 segments. If it has more than 2, the\n // third must be \"tree\" and it must have at least 4 segments.\n // https://github.com/:owner/:repo\n // https://github.com/:owner/:repo/tree/:ref\n pathSegments.length >= 2 &&\n (pathSegments.length > 2 ? pathSegments[2] === 'tree' && pathSegments.length >= 4 : true)\n )\n}\n\nasync function downloadTarStream(url: string, bearerToken?: string): Promise<Readable> {\n const headers: Record<string, string> = {}\n if (bearerToken) {\n headers.Authorization = `Bearer ${bearerToken}`\n }\n\n const res = await fetch(url, {headers})\n\n if (!res.body) {\n throw new Error(`Failed to download: ${url}`)\n }\n\n // eslint-disable-next-line n/no-unsupported-features/node-builtins\n return Readable.fromWeb(res.body as Parameters<typeof Readable.fromWeb>[0])\n}\n\nexport function checkIsRemoteTemplate(templateName?: string): boolean {\n return templateName?.includes('/') ?? false\n}\n\nexport async function getGitHubRepoInfo(value: string, bearerToken?: string): Promise<RepoInfo> {\n let username = ''\n let name = ''\n let branch = ''\n let filePath = ''\n\n if (isGitHubRepoShorthand(value)) {\n const parts = value.split('/')\n username = parts[0]\n name = parts[1]\n // If there are more segments after owner/repo, they form the file path\n if (parts.length > 2) {\n filePath = parts.slice(2).join('/')\n }\n }\n\n if (isGitHubRepoUrl(value)) {\n const url = new URL(value)\n const pathSegments = url.pathname.slice(1).split('/')\n username = pathSegments[0]\n name = pathSegments[1]\n\n // If we have a \"tree\" segment, everything after branch is the file path\n if (pathSegments[2] === 'tree') {\n branch = pathSegments[3]\n if (pathSegments.length > 4) {\n filePath = pathSegments.slice(4).join('/')\n }\n }\n }\n\n if (!username || !name) {\n throw new Error('Invalid GitHub repository format')\n }\n\n const tokenMessage =\n 'GitHub repository not found. For private repositories, use --template-token to provide an access token.\\n\\n' +\n 'You can generate a new token at https://github.com/settings/personal-access-tokens/new\\n' +\n 'Set the token to \"read-only\" with repository access and a short expiry (e.g. 7 days) for security.'\n\n try {\n const headers: Record<string, string> = {}\n if (bearerToken) {\n headers.Authorization = `Bearer ${bearerToken}`\n }\n\n const infoResponse = await fetch(`https://api.github.com/repos/${username}/${name}`, {\n headers,\n })\n\n if (infoResponse.status !== 200) {\n if (infoResponse.status === 404) {\n throw new Error(tokenMessage)\n }\n throw new Error('GitHub repository not found')\n }\n\n const info = await infoResponse.json()\n\n return {\n branch: branch || info.default_branch,\n filePath,\n name,\n username,\n }\n } catch {\n throw new Error(tokenMessage)\n }\n}\n\nexport async function downloadAndExtractRepo(\n root: string,\n {branch, filePath, name, username}: RepoInfo,\n bearerToken?: string,\n): Promise<void> {\n let rootPath: string | null = null\n await pipeline(\n await downloadTarStream(\n `https://codeload.github.com/${username}/${name}/tar.gz/${branch}`,\n bearerToken,\n ),\n x({\n cwd: root,\n filter: (p: string) => {\n const posixPath = p.split(sep).join(posix.sep)\n if (rootPath === null) {\n const pathSegments = posixPath.split(posix.sep)\n rootPath = pathSegments.length > 0 ? pathSegments[0] : null\n }\n for (const disallowedPath of DISALLOWED_PATHS) {\n if (posixPath.includes(disallowedPath)) return false\n }\n return posixPath.startsWith(`${rootPath}${filePath ? `/${filePath}/` : '/'}`)\n },\n strip: filePath ? filePath.split('/').length + 1 : 1,\n }),\n )\n}\n\nexport async function checkIfNeedsApiToken(root: string, type: 'read' | 'write'): Promise<boolean> {\n try {\n const templatePath = await Promise.any(\n ENV_TEMPLATE_FILES.map(async (file) => {\n await access(join(root, file))\n return file\n }),\n )\n const templateContent = await readFile(join(root, templatePath), 'utf8')\n return templateContent.includes(type === 'read' ? ENV_VAR.READ_TOKEN : ENV_VAR.WRITE_TOKEN)\n } catch {\n return false\n }\n}\n\nexport async function applyEnvVariables(\n root: string,\n envData: EnvData,\n targetName = '.env',\n): Promise<void> {\n const templatePath = await Promise.any(\n ENV_TEMPLATE_FILES.map(async (file) => {\n await access(join(root, file))\n return file\n }),\n ).catch(() => {})\n\n if (!templatePath) {\n return // No template .env file found, skip\n }\n\n try {\n const templateContent = await readFile(join(root, templatePath), 'utf8')\n const {dataset, projectId, readToken = '', writeToken = ''} = envData\n\n const findAndReplaceVariable = (\n content: string,\n varRegex: RegExp | string,\n value: string,\n useQuotes: boolean,\n ) => {\n const varPattern = typeof varRegex === 'string' ? varRegex : varRegex.source\n const pattern = new RegExp(`.*${varPattern}=.*$`, 'gm')\n const matches = content.matchAll(pattern)\n\n let result = content\n for (const match of matches) {\n if (!match[0]) continue\n const varName = match[0].split('=')[0].trim()\n result = result.replaceAll(\n new RegExp(`${varName}=.*$`, 'gm'),\n `${varName}=${useQuotes ? `\"${value}\"` : value}`,\n )\n }\n\n return result\n }\n\n let envContent = templateContent\n const vars = [\n {pattern: ENV_VAR.PROJECT_ID, value: projectId},\n {pattern: ENV_VAR.DATASET, value: dataset},\n {pattern: ENV_VAR.READ_TOKEN, value: readToken},\n {pattern: ENV_VAR.WRITE_TOKEN, value: writeToken},\n ]\n const useQuotes = templateContent.includes('=\"')\n\n for (const {pattern, value} of vars) {\n envContent = findAndReplaceVariable(envContent, pattern, value, useQuotes)\n }\n\n await writeFile(join(root, targetName), envContent)\n } catch (err) {\n debug(`Error setting environment variables: ${err}`)\n throw new Error(\n 'Failed to set environment variables. This could be due to file permissions or the .env file format. See https://www.sanity.io/docs/environment-variables for details on environment variable setup.',\n )\n }\n}\n\nexport async function tryApplyPackageName(root: string, name: string): Promise<void> {\n try {\n const pkg = await readPackageJson(join(root, 'package.json'))\n pkg.name = name\n\n await writeFile(join(root, 'package.json'), JSON.stringify(pkg, null, 2))\n } catch {\n // noop\n }\n}\n"],"names":["access","readFile","writeFile","join","posix","sep","Readable","pipeline","subdebug","ENV_TEMPLATE_FILES","REQUIRED_ENV_VAR","x","readPackageJson","debug","DISALLOWED_PATHS","ENV_VAR","READ_TOKEN","WRITE_TOKEN","getGitHubRawContentUrl","repoInfo","branch","filePath","name","username","isGitHubRepoShorthand","value","URL","canParse","test","isGitHubRepoUrl","url","pathSegments","pathname","slice","split","protocol","hostname","length","downloadTarStream","bearerToken","headers","Authorization","res","fetch","body","Error","fromWeb","checkIsRemoteTemplate","templateName","includes","getGitHubRepoInfo","parts","tokenMessage","infoResponse","status","info","json","default_branch","downloadAndExtractRepo","root","rootPath","cwd","filter","p","posixPath","disallowedPath","startsWith","strip","checkIfNeedsApiToken","type","templatePath","Promise","any","map","file","templateContent","applyEnvVariables","envData","targetName","catch","dataset","projectId","readToken","writeToken","findAndReplaceVariable","content","varRegex","useQuotes","varPattern","source","pattern","RegExp","matches","matchAll","result","match","varName","trim","replaceAll","envContent","vars","PROJECT_ID","DATASET","err","tryApplyPackageName","pkg","JSON","stringify"],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { Output } from '@sanity/cli-core';
2
+ import { PackageManager } from '../../util/packageManager/packageManagerChoice.js';
3
+ interface ResolvePackageManagerOptions {
4
+ interactive: boolean;
5
+ output: Output;
6
+ packageManager: PackageManager;
7
+ targetDir: string;
8
+ }
9
+ export declare function resolvePackageManager({ interactive, output, packageManager, targetDir, }: ResolvePackageManagerOptions): Promise<PackageManager>;
10
+ export {};
@@ -0,0 +1,20 @@
1
+ import { chalk } from '@sanity/cli-core/ux';
2
+ import { ALLOWED_PACKAGE_MANAGERS, allowedPackageManagersString, getPackageManagerChoice } from '../../util/packageManager/packageManagerChoice.js';
3
+ export async function resolvePackageManager({ interactive, output, packageManager, targetDir }) {
4
+ // If the user has specified a package manager, and it's allowed use that
5
+ if (packageManager && ALLOWED_PACKAGE_MANAGERS.includes(packageManager)) {
6
+ return packageManager;
7
+ }
8
+ // Otherwise, try to find the most optimal package manager to use
9
+ const chosen = (await getPackageManagerChoice(targetDir, {
10
+ interactive
11
+ })).chosen;
12
+ // only log warning if a package manager flag is passed
13
+ if (packageManager) {
14
+ output.warn(chalk.yellow(`Given package manager "${packageManager}" is not supported. Supported package managers are ${allowedPackageManagersString}.`));
15
+ output.log(`Using ${chosen} as package manager`);
16
+ }
17
+ return chosen;
18
+ }
19
+
20
+ //# sourceMappingURL=resolvePackageManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/init/resolvePackageManager.ts"],"sourcesContent":["import {Output} from '@sanity/cli-core'\nimport {chalk} from '@sanity/cli-core/ux'\n\nimport {\n ALLOWED_PACKAGE_MANAGERS,\n allowedPackageManagersString,\n getPackageManagerChoice,\n PackageManager,\n} from '../../util/packageManager/packageManagerChoice.js'\n\ninterface ResolvePackageManagerOptions {\n interactive: boolean\n output: Output\n packageManager: PackageManager\n targetDir: string\n}\n\nexport async function resolvePackageManager({\n interactive,\n output,\n packageManager,\n targetDir,\n}: ResolvePackageManagerOptions): Promise<PackageManager> {\n // If the user has specified a package manager, and it's allowed use that\n if (packageManager && ALLOWED_PACKAGE_MANAGERS.includes(packageManager as PackageManager)) {\n return packageManager\n }\n\n // Otherwise, try to find the most optimal package manager to use\n const chosen = (\n await getPackageManagerChoice(targetDir, {\n interactive,\n })\n ).chosen\n\n // only log warning if a package manager flag is passed\n if (packageManager) {\n output.warn(\n chalk.yellow(\n `Given package manager \"${packageManager}\" is not supported. Supported package managers are ${allowedPackageManagersString}.`,\n ),\n )\n output.log(`Using ${chosen} as package manager`)\n }\n\n return chosen\n}\n"],"names":["chalk","ALLOWED_PACKAGE_MANAGERS","allowedPackageManagersString","getPackageManagerChoice","resolvePackageManager","interactive","output","packageManager","targetDir","includes","chosen","warn","yellow","log"],"mappings":"AACA,SAAQA,KAAK,QAAO,sBAAqB;AAEzC,SACEC,wBAAwB,EACxBC,4BAA4B,EAC5BC,uBAAuB,QAElB,oDAAmD;AAS1D,OAAO,eAAeC,sBAAsB,EAC1CC,WAAW,EACXC,MAAM,EACNC,cAAc,EACdC,SAAS,EACoB;IAC7B,yEAAyE;IACzE,IAAID,kBAAkBN,yBAAyBQ,QAAQ,CAACF,iBAAmC;QACzF,OAAOA;IACT;IAEA,iEAAiE;IACjE,MAAMG,SAAS,AACb,CAAA,MAAMP,wBAAwBK,WAAW;QACvCH;IACF,EAAC,EACDK,MAAM;IAER,uDAAuD;IACvD,IAAIH,gBAAgB;QAClBD,OAAOK,IAAI,CACTX,MAAMY,MAAM,CACV,CAAC,uBAAuB,EAAEL,eAAe,mDAAmD,EAAEL,6BAA6B,CAAC,CAAC;QAGjII,OAAOO,GAAG,CAAC,CAAC,MAAM,EAAEH,OAAO,mBAAmB,CAAC;IACjD;IAEA,OAAOA;AACT"}
@@ -0,0 +1,21 @@
1
+ import { Output } from '@sanity/cli-core';
2
+ export type EditorName = 'Claude Code' | 'Cursor' | 'VS Code';
3
+ export interface Editor {
4
+ configKey: 'mcpServers' | 'servers';
5
+ configPath: string;
6
+ name: EditorName;
7
+ }
8
+ export interface MCPSetupResult {
9
+ configuredEditors: EditorName[];
10
+ detectedEditors: EditorName[];
11
+ skipped: boolean;
12
+ error?: Error;
13
+ }
14
+ /**
15
+ * Main MCP setup orchestration
16
+ * Opt-out by default: runs automatically unless skipMcp flag is set
17
+ */
18
+ export declare function setupMCP(options: {
19
+ mcp?: boolean;
20
+ output: Output;
21
+ }): Promise<MCPSetupResult>;
@@ -0,0 +1,258 @@
1
+ import { existsSync } from 'node:fs';
2
+ import fs from 'node:fs/promises';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { getCliToken, getGlobalCliClient, subdebug } from '@sanity/cli-core';
6
+ import { checkbox, logSymbols } from '@sanity/cli-core/ux';
7
+ import { execa } from 'execa';
8
+ const debug = subdebug('init:setupMCP');
9
+ const MCP_SERVER_URL = 'https://mcp.sanity.io';
10
+ const NO_EDITORS_DETECTED_MESSAGE = `Couldn't auto-configure Sanity MCP server for your editor. Visit ${MCP_SERVER_URL} for setup instructions.`;
11
+ /**
12
+ * Detect which editors are installed on the user's machine
13
+ */ async function detectAvailableEditors() {
14
+ const editors = [];
15
+ const homeDir = os.homedir();
16
+ // Cursor detection
17
+ const cursorDir = path.join(homeDir, '.cursor');
18
+ if (existsSync(cursorDir)) {
19
+ editors.push({
20
+ configKey: 'mcpServers',
21
+ configPath: path.join(cursorDir, 'mcp.json'),
22
+ name: 'Cursor'
23
+ });
24
+ }
25
+ // VS Code detection (platform-specific)
26
+ let vscodeConfigDir = null;
27
+ switch(process.platform){
28
+ case 'darwin':
29
+ {
30
+ vscodeConfigDir = path.join(homeDir, 'Library/Application Support/Code/User');
31
+ break;
32
+ }
33
+ case 'win32':
34
+ {
35
+ // APPDATA is required on Windows for VS Code config path
36
+ if (process.env.APPDATA) {
37
+ vscodeConfigDir = path.join(process.env.APPDATA, 'Code/User');
38
+ }
39
+ break;
40
+ }
41
+ default:
42
+ {
43
+ // linux
44
+ vscodeConfigDir = path.join(homeDir, '.config/Code/User');
45
+ }
46
+ }
47
+ if (vscodeConfigDir && existsSync(vscodeConfigDir)) {
48
+ editors.push({
49
+ configKey: 'servers',
50
+ configPath: path.join(vscodeConfigDir, 'mcp.json'),
51
+ name: 'VS Code'
52
+ });
53
+ }
54
+ // Claude Code detection
55
+ try {
56
+ await execa('claude', [
57
+ '--version'
58
+ ], {
59
+ stdio: 'pipe',
60
+ timeout: 5000
61
+ });
62
+ editors.push({
63
+ configKey: 'mcpServers',
64
+ configPath: path.join(homeDir, '.claude.json'),
65
+ name: 'Claude Code'
66
+ });
67
+ } catch {
68
+ // Not installed
69
+ }
70
+ return editors;
71
+ }
72
+ /**
73
+ * Prompt user to select which editors to configure
74
+ * Shows existing config status - unconfigured editors are pre-selected,
75
+ * configured editors show "(select to reconfigure)" and are not pre-selected
76
+ */ async function promptForMCPSetup(detectedEditors, editorsWithExisting) {
77
+ // Build choices with existing config status
78
+ const editorChoices = detectedEditors.map((e)=>{
79
+ const isConfigured = editorsWithExisting.some((existing)=>existing.name === e.name);
80
+ return {
81
+ checked: !isConfigured,
82
+ name: isConfigured ? `${e.name} (already installed)` : e.name,
83
+ value: e.name
84
+ };
85
+ });
86
+ const result = await checkbox({
87
+ choices: editorChoices,
88
+ message: 'Configure Sanity MCP server?'
89
+ });
90
+ const selectedNames = result;
91
+ // User can deselect all to skip
92
+ if (!selectedNames || selectedNames.length === 0) {
93
+ return null;
94
+ }
95
+ return detectedEditors.filter((e)=>selectedNames.includes(e.name));
96
+ }
97
+ /**
98
+ * Create a child token for MCP usage
99
+ * This token is tied to the parent CLI token and will be invalidated
100
+ * when the parent token is invalidated (e.g., on logout)
101
+ */ async function createMCPToken() {
102
+ const parentToken = getCliToken();
103
+ if (!parentToken) {
104
+ throw new Error('Not authenticated. Please run `sanity login` first.');
105
+ }
106
+ const client = await getGlobalCliClient({
107
+ apiVersion: '2025-12-09'
108
+ });
109
+ const sessionResponse = await client.request({
110
+ body: {
111
+ sourceId: 'sanity-mcp',
112
+ withStamp: false
113
+ },
114
+ method: 'POST',
115
+ uri: '/auth/session/create'
116
+ });
117
+ const tokenResponse = await client.request({
118
+ method: 'GET',
119
+ query: {
120
+ sid: sessionResponse.sid
121
+ },
122
+ uri: '/auth/fetch'
123
+ });
124
+ return tokenResponse.token;
125
+ }
126
+ /**
127
+ * Check which editors already have Sanity MCP configured
128
+ */ async function getEditorsWithExistingConfig(editors) {
129
+ const configured = [];
130
+ for (const editor of editors){
131
+ if (existsSync(editor.configPath)) {
132
+ try {
133
+ const content = await fs.readFile(editor.configPath, 'utf8');
134
+ const config = JSON.parse(content);
135
+ if (config[editor.configKey]?.Sanity) {
136
+ configured.push(editor);
137
+ }
138
+ } catch (err) {
139
+ debug('Could not read MCP config for %s: %s', editor.name, err);
140
+ // Treat as not configured
141
+ }
142
+ }
143
+ }
144
+ return configured;
145
+ }
146
+ /**
147
+ * Write MCP configuration to editor config file
148
+ * Merges with existing config if present
149
+ * Uses existing CLI authentication token
150
+ */ async function writeMCPConfig(editor, token) {
151
+ const configPath = editor.configPath;
152
+ // 1. Read existing config (if exists)
153
+ let existingConfig = {};
154
+ if (existsSync(configPath)) {
155
+ try {
156
+ const content = await fs.readFile(configPath, 'utf8');
157
+ existingConfig = JSON.parse(content);
158
+ } catch {
159
+ debug(`Warning: Could not parse ${configPath}. Creating new config.`);
160
+ // Use empty config (will overwrite)
161
+ }
162
+ }
163
+ // 2. Create/update Sanity server entry
164
+ const serverKey = editor.configKey;
165
+ if (!existingConfig[serverKey]) {
166
+ existingConfig[serverKey] = {};
167
+ }
168
+ existingConfig[serverKey].Sanity = {
169
+ headers: {
170
+ Authorization: `Bearer ${token}`
171
+ },
172
+ type: 'http',
173
+ url: MCP_SERVER_URL
174
+ };
175
+ // 3. Ensure parent directory exists
176
+ await fs.mkdir(path.dirname(configPath), {
177
+ recursive: true
178
+ });
179
+ // 4. Write config
180
+ await fs.writeFile(configPath, JSON.stringify(existingConfig, null, 2), 'utf8');
181
+ }
182
+ /**
183
+ * Main MCP setup orchestration
184
+ * Opt-out by default: runs automatically unless skipMcp flag is set
185
+ */ export async function setupMCP(options) {
186
+ // 1. Check for explicit opt-out
187
+ if (options.mcp === false) {
188
+ options.output.warn('Skipping MCP configuration due to --no-mcp flag');
189
+ return {
190
+ configuredEditors: [],
191
+ detectedEditors: [],
192
+ skipped: true
193
+ };
194
+ }
195
+ // 2. Detect editors
196
+ const detected = await detectAvailableEditors();
197
+ const detectedEditors = detected.map((e)=>e.name);
198
+ if (detected.length === 0) {
199
+ options.output.warn(NO_EDITORS_DETECTED_MESSAGE);
200
+ return {
201
+ configuredEditors: [],
202
+ detectedEditors,
203
+ skipped: true
204
+ };
205
+ }
206
+ // 3. Check for existing config BEFORE prompting
207
+ const editorsWithExisting = await getEditorsWithExistingConfig(detected);
208
+ // 4. Prompt user (shows existing config status, only pre-selects unconfigured editors)
209
+ const selected = await promptForMCPSetup(detected, editorsWithExisting);
210
+ if (!selected || selected.length === 0) {
211
+ // User deselected all editors
212
+ return {
213
+ configuredEditors: [],
214
+ detectedEditors,
215
+ skipped: true
216
+ };
217
+ }
218
+ // 5. Create child token for MCP
219
+ let token;
220
+ try {
221
+ token = await createMCPToken();
222
+ } catch (error) {
223
+ const err = error instanceof Error ? error : new Error(String(error));
224
+ options.output.warn(`Could not configure MCP: ${err.message}`);
225
+ options.output.warn('You can set up MCP manually later using https://mcp.sanity.io');
226
+ return {
227
+ configuredEditors: [],
228
+ detectedEditors,
229
+ error: err,
230
+ skipped: false
231
+ };
232
+ }
233
+ // 6. Write configs for each selected editor
234
+ try {
235
+ for (const editor of selected){
236
+ await writeMCPConfig(editor, token);
237
+ }
238
+ } catch (error) {
239
+ const err = error instanceof Error ? error : new Error(String(error));
240
+ options.output.warn(`Could not configure MCP: ${err.message}`);
241
+ options.output.warn('You can set up MCP manually later using https://mcp.sanity.io');
242
+ return {
243
+ configuredEditors: [],
244
+ detectedEditors,
245
+ error: err,
246
+ skipped: false
247
+ };
248
+ }
249
+ const configuredEditors = selected.map((e)=>e.name);
250
+ options.output.log(`${logSymbols.success} MCP configured for ${configuredEditors.join(', ')}`);
251
+ return {
252
+ configuredEditors,
253
+ detectedEditors,
254
+ skipped: false
255
+ };
256
+ }
257
+
258
+ //# sourceMappingURL=setupMCP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/init/setupMCP.ts"],"sourcesContent":["import {existsSync} from 'node:fs'\nimport fs from 'node:fs/promises'\nimport os from 'node:os'\nimport path from 'node:path'\n\nimport {getCliToken, getGlobalCliClient, Output, subdebug} from '@sanity/cli-core'\nimport {checkbox, logSymbols} from '@sanity/cli-core/ux'\nimport {execa} from 'execa'\n\nconst debug = subdebug('init:setupMCP')\n\nconst MCP_SERVER_URL = 'https://mcp.sanity.io'\n\nconst NO_EDITORS_DETECTED_MESSAGE = `Couldn't auto-configure Sanity MCP server for your editor. Visit ${MCP_SERVER_URL} for setup instructions.`\n\nexport type EditorName = 'Claude Code' | 'Cursor' | 'VS Code'\n\nexport interface Editor {\n configKey: 'mcpServers' | 'servers'\n configPath: string\n name: EditorName\n}\n\ninterface MCPConfig {\n mcpServers?: Record<string, ServerConfig>\n servers?: Record<string, ServerConfig>\n}\n\ninterface ServerConfig {\n headers: {\n Authorization: string\n }\n type: 'http'\n url: string\n}\n\nexport interface MCPSetupResult {\n configuredEditors: EditorName[]\n detectedEditors: EditorName[]\n skipped: boolean\n\n error?: Error\n}\n\n/**\n * Detect which editors are installed on the user's machine\n */\nasync function detectAvailableEditors(): Promise<Editor[]> {\n const editors: Editor[] = []\n const homeDir = os.homedir()\n\n // Cursor detection\n const cursorDir = path.join(homeDir, '.cursor')\n if (existsSync(cursorDir)) {\n editors.push({\n configKey: 'mcpServers',\n configPath: path.join(cursorDir, 'mcp.json'),\n name: 'Cursor',\n })\n }\n\n // VS Code detection (platform-specific)\n let vscodeConfigDir: string | null = null\n switch (process.platform) {\n case 'darwin': {\n vscodeConfigDir = path.join(homeDir, 'Library/Application Support/Code/User')\n break\n }\n case 'win32': {\n // APPDATA is required on Windows for VS Code config path\n if (process.env.APPDATA) {\n vscodeConfigDir = path.join(process.env.APPDATA, 'Code/User')\n }\n break\n }\n default: {\n // linux\n vscodeConfigDir = path.join(homeDir, '.config/Code/User')\n }\n }\n\n if (vscodeConfigDir && existsSync(vscodeConfigDir)) {\n editors.push({\n configKey: 'servers',\n configPath: path.join(vscodeConfigDir, 'mcp.json'),\n name: 'VS Code',\n })\n }\n\n // Claude Code detection\n try {\n await execa('claude', ['--version'], {stdio: 'pipe', timeout: 5000})\n editors.push({\n configKey: 'mcpServers',\n configPath: path.join(homeDir, '.claude.json'),\n name: 'Claude Code',\n })\n } catch {\n // Not installed\n }\n\n return editors\n}\n\n/**\n * Prompt user to select which editors to configure\n * Shows existing config status - unconfigured editors are pre-selected,\n * configured editors show \"(select to reconfigure)\" and are not pre-selected\n */\nasync function promptForMCPSetup(\n detectedEditors: Editor[],\n editorsWithExisting: Editor[],\n): Promise<Editor[] | null> {\n // Build choices with existing config status\n const editorChoices = detectedEditors.map((e) => {\n const isConfigured = editorsWithExisting.some((existing) => existing.name === e.name)\n return {\n checked: !isConfigured, // Only pre-select if NOT already configured\n name: isConfigured ? `${e.name} (already installed)` : e.name,\n value: e.name,\n }\n })\n\n const result = await checkbox({\n choices: editorChoices,\n message: 'Configure Sanity MCP server?',\n })\n\n const selectedNames = result\n\n // User can deselect all to skip\n if (!selectedNames || selectedNames.length === 0) {\n return null\n }\n\n return detectedEditors.filter((e) => selectedNames.includes(e.name))\n}\n\n/**\n * Create a child token for MCP usage\n * This token is tied to the parent CLI token and will be invalidated\n * when the parent token is invalidated (e.g., on logout)\n */\nasync function createMCPToken(): Promise<string> {\n const parentToken = getCliToken()\n if (!parentToken) {\n throw new Error('Not authenticated. Please run `sanity login` first.')\n }\n\n const client = await getGlobalCliClient({\n apiVersion: '2025-12-09',\n })\n\n const sessionResponse = await client.request<{id: string; sid: string}>({\n body: {\n sourceId: 'sanity-mcp',\n withStamp: false,\n },\n method: 'POST',\n uri: '/auth/session/create',\n })\n\n const tokenResponse = await client.request<{label: string; token: string}>({\n method: 'GET',\n query: {sid: sessionResponse.sid},\n uri: '/auth/fetch',\n })\n\n return tokenResponse.token\n}\n\n/**\n * Check which editors already have Sanity MCP configured\n */\nasync function getEditorsWithExistingConfig(editors: Editor[]): Promise<Editor[]> {\n const configured: Editor[] = []\n for (const editor of editors) {\n if (existsSync(editor.configPath)) {\n try {\n const content = await fs.readFile(editor.configPath, 'utf8')\n const config = JSON.parse(content) as MCPConfig\n if (config[editor.configKey]?.Sanity) {\n configured.push(editor)\n }\n } catch (err) {\n debug('Could not read MCP config for %s: %s', editor.name, err)\n // Treat as not configured\n }\n }\n }\n return configured\n}\n\n/**\n * Write MCP configuration to editor config file\n * Merges with existing config if present\n * Uses existing CLI authentication token\n */\nasync function writeMCPConfig(editor: Editor, token: string): Promise<void> {\n const configPath = editor.configPath\n\n // 1. Read existing config (if exists)\n let existingConfig: MCPConfig = {}\n if (existsSync(configPath)) {\n try {\n const content = await fs.readFile(configPath, 'utf8')\n existingConfig = JSON.parse(content)\n } catch {\n debug(`Warning: Could not parse ${configPath}. Creating new config.`)\n // Use empty config (will overwrite)\n }\n }\n\n // 2. Create/update Sanity server entry\n const serverKey = editor.configKey\n if (!existingConfig[serverKey]) {\n existingConfig[serverKey] = {}\n }\n\n existingConfig[serverKey].Sanity = {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n type: 'http',\n url: MCP_SERVER_URL,\n }\n\n // 3. Ensure parent directory exists\n await fs.mkdir(path.dirname(configPath), {recursive: true})\n\n // 4. Write config\n await fs.writeFile(configPath, JSON.stringify(existingConfig, null, 2), 'utf8')\n}\n\n/**\n * Main MCP setup orchestration\n * Opt-out by default: runs automatically unless skipMcp flag is set\n */\nexport async function setupMCP(options: {mcp?: boolean; output: Output}): Promise<MCPSetupResult> {\n // 1. Check for explicit opt-out\n if (options.mcp === false) {\n options.output.warn('Skipping MCP configuration due to --no-mcp flag')\n return {\n configuredEditors: [],\n detectedEditors: [],\n skipped: true,\n }\n }\n\n // 2. Detect editors\n const detected = await detectAvailableEditors()\n const detectedEditors = detected.map((e) => e.name)\n\n if (detected.length === 0) {\n options.output.warn(NO_EDITORS_DETECTED_MESSAGE)\n return {\n configuredEditors: [],\n detectedEditors,\n skipped: true,\n }\n }\n\n // 3. Check for existing config BEFORE prompting\n const editorsWithExisting = await getEditorsWithExistingConfig(detected)\n\n // 4. Prompt user (shows existing config status, only pre-selects unconfigured editors)\n const selected = await promptForMCPSetup(detected, editorsWithExisting)\n\n if (!selected || selected.length === 0) {\n // User deselected all editors\n return {\n configuredEditors: [],\n detectedEditors,\n skipped: true,\n }\n }\n\n // 5. Create child token for MCP\n let token: string\n try {\n token = await createMCPToken()\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n options.output.warn(`Could not configure MCP: ${err.message}`)\n options.output.warn('You can set up MCP manually later using https://mcp.sanity.io')\n return {\n configuredEditors: [],\n detectedEditors,\n error: err,\n skipped: false,\n }\n }\n\n // 6. Write configs for each selected editor\n try {\n for (const editor of selected) {\n await writeMCPConfig(editor, token)\n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n options.output.warn(`Could not configure MCP: ${err.message}`)\n options.output.warn('You can set up MCP manually later using https://mcp.sanity.io')\n return {\n configuredEditors: [],\n detectedEditors,\n error: err,\n skipped: false,\n }\n }\n\n const configuredEditors = selected.map((e) => e.name)\n options.output.log(`${logSymbols.success} MCP configured for ${configuredEditors.join(', ')}`)\n\n return {\n configuredEditors,\n detectedEditors,\n skipped: false,\n }\n}\n"],"names":["existsSync","fs","os","path","getCliToken","getGlobalCliClient","subdebug","checkbox","logSymbols","execa","debug","MCP_SERVER_URL","NO_EDITORS_DETECTED_MESSAGE","detectAvailableEditors","editors","homeDir","homedir","cursorDir","join","push","configKey","configPath","name","vscodeConfigDir","process","platform","env","APPDATA","stdio","timeout","promptForMCPSetup","detectedEditors","editorsWithExisting","editorChoices","map","e","isConfigured","some","existing","checked","value","result","choices","message","selectedNames","length","filter","includes","createMCPToken","parentToken","Error","client","apiVersion","sessionResponse","request","body","sourceId","withStamp","method","uri","tokenResponse","query","sid","token","getEditorsWithExistingConfig","configured","editor","content","readFile","config","JSON","parse","Sanity","err","writeMCPConfig","existingConfig","serverKey","headers","Authorization","type","url","mkdir","dirname","recursive","writeFile","stringify","setupMCP","options","mcp","output","warn","configuredEditors","skipped","detected","selected","error","String","log","success"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { type ProjectTemplate } from '../types.js';
2
+ declare const appTemplate: ProjectTemplate;
3
+ export default appTemplate;
@@ -0,0 +1,28 @@
1
+ const appTemplate = {
2
+ dependencies: {
3
+ '@sanity/sdk': '^0.0.0-rc',
4
+ '@sanity/sdk-react': '^0.0.0-rc',
5
+ react: '^19',
6
+ 'react-dom': '^19'
7
+ },
8
+ devDependencies: {
9
+ /*
10
+ * this will be changed to eslint-config sanity,
11
+ * eslint.config generation will be a fast follow
12
+ */ '@sanity/eslint-config-studio': '^5.0.1',
13
+ '@types/react': '^18.0.25',
14
+ eslint: '^9.9.0',
15
+ prettier: '^3.0.2',
16
+ sanity: '^3',
17
+ typescript: '^5.1.6'
18
+ },
19
+ entry: './src/App.tsx',
20
+ scripts: {
21
+ build: 'sanity build',
22
+ dev: 'sanity dev',
23
+ start: 'sanity start'
24
+ }
25
+ };
26
+ export default appTemplate;
27
+
28
+ //# sourceMappingURL=appQuickstart.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/actions/init/templates/appQuickstart.ts"],"sourcesContent":["import {type ProjectTemplate} from '../types.js'\n\nconst appTemplate: ProjectTemplate = {\n dependencies: {\n '@sanity/sdk': '^0.0.0-rc',\n '@sanity/sdk-react': '^0.0.0-rc',\n react: '^19',\n 'react-dom': '^19',\n },\n devDependencies: {\n /*\n * this will be changed to eslint-config sanity,\n * eslint.config generation will be a fast follow\n */\n '@sanity/eslint-config-studio': '^5.0.1',\n '@types/react': '^18.0.25',\n eslint: '^9.9.0',\n prettier: '^3.0.2',\n sanity: '^3',\n typescript: '^5.1.6',\n },\n entry: './src/App.tsx',\n scripts: {\n build: 'sanity build',\n dev: 'sanity dev',\n start: 'sanity start',\n },\n}\n\nexport default appTemplate\n"],"names":["appTemplate","dependencies","react","devDependencies","eslint","prettier","sanity","typescript","entry","scripts","build","dev","start"],"mappings":"AAEA,MAAMA,cAA+B;IACnCC,cAAc;QACZ,eAAe;QACf,qBAAqB;QACrBC,OAAO;QACP,aAAa;IACf;IACAC,iBAAiB;QACf;;;KAGC,GACD,gCAAgC;QAChC,gBAAgB;QAChBC,QAAQ;QACRC,UAAU;QACVC,QAAQ;QACRC,YAAY;IACd;IACAC,OAAO;IACPC,SAAS;QACPC,OAAO;QACPC,KAAK;QACLC,OAAO;IACT;AACF;AAEA,eAAeZ,YAAW"}
@@ -0,0 +1,3 @@
1
+ import { type ProjectTemplate } from '../types.js';
2
+ declare const appSanityUiTemplate: ProjectTemplate;
3
+ export default appSanityUiTemplate;
@@ -0,0 +1,30 @@
1
+ const appSanityUiTemplate = {
2
+ dependencies: {
3
+ '@sanity/sdk': '^0.0.0-rc',
4
+ '@sanity/sdk-react': '^0.0.0-rc',
5
+ '@sanity/ui': '^2',
6
+ react: '^19',
7
+ 'react-dom': '^19',
8
+ 'styled-components': '^6.1.17'
9
+ },
10
+ devDependencies: {
11
+ /*
12
+ * this will be changed to eslint-config sanity,
13
+ * eslint.config generation will be a fast follow
14
+ */ '@sanity/eslint-config-studio': '^5.0.1',
15
+ '@types/react': '^18.0.25',
16
+ eslint: '^9.9.0',
17
+ prettier: '^3.0.2',
18
+ sanity: '^3',
19
+ typescript: '^5.1.6'
20
+ },
21
+ entry: './src/App.tsx',
22
+ scripts: {
23
+ build: 'sanity build',
24
+ dev: 'sanity dev',
25
+ start: 'sanity start'
26
+ }
27
+ };
28
+ export default appSanityUiTemplate;
29
+
30
+ //# sourceMappingURL=appSanityUi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/actions/init/templates/appSanityUi.ts"],"sourcesContent":["import {type ProjectTemplate} from '../types.js'\n\nconst appSanityUiTemplate: ProjectTemplate = {\n dependencies: {\n '@sanity/sdk': '^0.0.0-rc',\n '@sanity/sdk-react': '^0.0.0-rc',\n '@sanity/ui': '^2',\n react: '^19',\n 'react-dom': '^19',\n 'styled-components': '^6.1.17',\n },\n devDependencies: {\n /*\n * this will be changed to eslint-config sanity,\n * eslint.config generation will be a fast follow\n */\n '@sanity/eslint-config-studio': '^5.0.1',\n '@types/react': '^18.0.25',\n eslint: '^9.9.0',\n prettier: '^3.0.2',\n sanity: '^3',\n typescript: '^5.1.6',\n },\n entry: './src/App.tsx',\n scripts: {\n build: 'sanity build',\n dev: 'sanity dev',\n start: 'sanity start',\n },\n}\n\nexport default appSanityUiTemplate\n"],"names":["appSanityUiTemplate","dependencies","react","devDependencies","eslint","prettier","sanity","typescript","entry","scripts","build","dev","start"],"mappings":"AAEA,MAAMA,sBAAuC;IAC3CC,cAAc;QACZ,eAAe;QACf,qBAAqB;QACrB,cAAc;QACdC,OAAO;QACP,aAAa;QACb,qBAAqB;IACvB;IACAC,iBAAiB;QACf;;;KAGC,GACD,gCAAgC;QAChC,gBAAgB;QAChBC,QAAQ;QACRC,UAAU;QACVC,QAAQ;QACRC,YAAY;IACd;IACAC,OAAO;IACPC,SAAS;QACPC,OAAO;QACPC,KAAK;QACLC,OAAO;IACT;AACF;AAEA,eAAeZ,oBAAmB"}
@@ -0,0 +1,3 @@
1
+ import { type ProjectTemplate } from '../types.js';
2
+ declare const blogTemplate: ProjectTemplate;
3
+ export default blogTemplate;
@@ -0,0 +1,4 @@
1
+ const blogTemplate = {};
2
+ export default blogTemplate;
3
+
4
+ //# sourceMappingURL=blog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/actions/init/templates/blog.ts"],"sourcesContent":["import {type ProjectTemplate} from '../types.js'\n\nconst blogTemplate: ProjectTemplate = {}\n\nexport default blogTemplate\n"],"names":["blogTemplate"],"mappings":"AAEA,MAAMA,eAAgC,CAAC;AAEvC,eAAeA,aAAY"}
@@ -0,0 +1,3 @@
1
+ import { type ProjectTemplate } from '../types.js';
2
+ declare const cleanTemplate: ProjectTemplate;
3
+ export default cleanTemplate;
@@ -0,0 +1,4 @@
1
+ const cleanTemplate = {};
2
+ export default cleanTemplate;
3
+
4
+ //# sourceMappingURL=clean.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/actions/init/templates/clean.ts"],"sourcesContent":["import {type ProjectTemplate} from '../types.js'\n\nconst cleanTemplate: ProjectTemplate = {}\n\nexport default cleanTemplate\n"],"names":["cleanTemplate"],"mappings":"AAEA,MAAMA,gBAAiC,CAAC;AAExC,eAAeA,cAAa"}
@@ -0,0 +1,3 @@
1
+ import { type ProjectTemplate } from '../types.js';
2
+ declare const getStartedTemplate: ProjectTemplate;
3
+ export default getStartedTemplate;
@@ -0,0 +1,35 @@
1
+ const configTemplate = `
2
+ import {defineConfig, isDev} from 'sanity'
3
+ import {visionTool} from '@sanity/vision'
4
+ import {structureTool} from 'sanity/structure'
5
+ import {schemaTypes} from './schemaTypes'
6
+ import {getStartedPlugin} from './plugins/sanity-plugin-tutorial'
7
+
8
+ const devOnlyPlugins = [getStartedPlugin()]
9
+
10
+ export default defineConfig({
11
+ name: '%sourceName%',
12
+ title: '%projectName%',
13
+
14
+ projectId: '%projectId%',
15
+ dataset: '%dataset%',
16
+
17
+ plugins: [structureTool(), visionTool(), ...(isDev ? devOnlyPlugins : [])],
18
+
19
+ schema: {
20
+ types: schemaTypes,
21
+ },
22
+ })
23
+
24
+ `;
25
+ const getStartedTemplate = {
26
+ configTemplate,
27
+ dependencies: {
28
+ '@sanity/icons': '^2.11.0',
29
+ '@sanity/ui': '^2.0.0'
30
+ },
31
+ typescriptOnly: true
32
+ };
33
+ export default getStartedTemplate;
34
+
35
+ //# sourceMappingURL=getStarted.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/actions/init/templates/getStarted.ts"],"sourcesContent":["import {type ProjectTemplate} from '../types.js'\n\nconst configTemplate = `\nimport {defineConfig, isDev} from 'sanity'\nimport {visionTool} from '@sanity/vision'\nimport {structureTool} from 'sanity/structure'\nimport {schemaTypes} from './schemaTypes'\nimport {getStartedPlugin} from './plugins/sanity-plugin-tutorial'\n\nconst devOnlyPlugins = [getStartedPlugin()]\n\nexport default defineConfig({\n name: '%sourceName%',\n title: '%projectName%',\n\n projectId: '%projectId%',\n dataset: '%dataset%',\n\n plugins: [structureTool(), visionTool(), ...(isDev ? devOnlyPlugins : [])],\n\n schema: {\n types: schemaTypes,\n },\n})\n\n`\n\nconst getStartedTemplate: ProjectTemplate = {\n configTemplate,\n dependencies: {\n '@sanity/icons': '^2.11.0',\n '@sanity/ui': '^2.0.0',\n },\n typescriptOnly: true,\n}\n\nexport default getStartedTemplate\n"],"names":["configTemplate","getStartedTemplate","dependencies","typescriptOnly"],"mappings":"AAEA,MAAMA,iBAAiB,CAAC;;;;;;;;;;;;;;;;;;;;;;;AAuBxB,CAAC;AAED,MAAMC,qBAAsC;IAC1CD;IACAE,cAAc;QACZ,iBAAiB;QACjB,cAAc;IAChB;IACAC,gBAAgB;AAClB;AAEA,eAAeF,mBAAkB"}
@@ -0,0 +1,3 @@
1
+ import { type ProjectTemplate } from '../types.js';
2
+ declare const templates: Record<string, ProjectTemplate | undefined>;
3
+ export default templates;
@@ -0,0 +1,23 @@
1
+ import appTemplate from './appQuickstart.js';
2
+ import appSanityUiTemplate from './appSanityUi.js';
3
+ import blog from './blog.js';
4
+ import clean from './clean.js';
5
+ import getStartedTemplate from './getStarted.js';
6
+ import moviedb from './moviedb.js';
7
+ import quickstart from './quickstart.js';
8
+ import shopify from './shopify.js';
9
+ import shopifyOnline from './shopifyOnline.js';
10
+ const templates = {
11
+ 'app-quickstart': appTemplate,
12
+ 'app-sanity-ui': appSanityUiTemplate,
13
+ blog,
14
+ clean,
15
+ 'get-started': getStartedTemplate,
16
+ moviedb,
17
+ quickstart,
18
+ shopify,
19
+ 'shopify-online-storefront': shopifyOnline
20
+ };
21
+ export default templates;
22
+
23
+ //# sourceMappingURL=index.js.map