@sanity/cli 7.1.0 → 7.2.1

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.
package/README.md CHANGED
@@ -372,6 +372,9 @@ DESCRIPTION
372
372
 
373
373
  Set SANITY_ASSET_TIMEOUT (seconds) to override the 60-second timeout for processing resource assets.
374
374
 
375
+ Exit codes: 0 deployed, 2 deployment failed, 75 deployment accepted but completion could not be confirmed (rerun
376
+ 'blueprints info' to check).
377
+
375
378
  EXAMPLES
376
379
  $ sanity blueprints deploy
377
380
 
@@ -415,6 +418,9 @@ DESCRIPTION
415
418
 
416
419
  Use this to clean up test environments or decommission a Stack you no longer need.
417
420
 
421
+ Exit codes: 0 destroyed, 2 destruction failed, 75 destruction accepted but completion could not be confirmed (rerun
422
+ 'blueprints info' to check).
423
+
418
424
  EXAMPLES
419
425
  $ sanity blueprints destroy
420
426
 
@@ -1237,23 +1243,24 @@ Export a dataset to a local gzipped tarball. Assets returning 401, 403, or 404 a
1237
1243
  ```
1238
1244
  USAGE
1239
1245
  $ sanity datasets export [NAME] [DESTINATION] [-p <id>] [--asset-concurrency <value>] [--mode stream|cursor]
1240
- [--no-assets] [--no-compress] [--no-drafts] [--overwrite] [--raw] [--types <value>]
1246
+ [--no-assets] [--no-compress] [--no-drafts] [--no-strict-asset-verification] [--overwrite] [--raw] [--types <value>]
1241
1247
 
1242
1248
  ARGUMENTS
1243
1249
  [NAME] Name of the dataset to export
1244
1250
  [DESTINATION] Output destination file path
1245
1251
 
1246
1252
  FLAGS
1247
- --asset-concurrency=<value> [default: 8] Concurrent number of asset downloads
1248
- --mode=<option> [default: stream] Export mode ('cursor' is faster for large datasets but may miss
1249
- concurrent changes)
1250
- <options: stream|cursor>
1251
- --no-assets Export only non-asset documents and remove references to image assets
1252
- --no-compress Skips compressing tarball entries (still generates a gzip file)
1253
- --no-drafts Export only published versions of documents
1254
- --overwrite Overwrite any file with the same name
1255
- --raw Extract only documents, without rewriting asset references
1256
- --types=<value> Defines which document types to export (comma-separated)
1253
+ --asset-concurrency=<value> [default: 8] Concurrent number of asset downloads
1254
+ --mode=<option> [default: stream] Export mode ('cursor' is faster for large datasets but may miss
1255
+ concurrent changes)
1256
+ <options: stream|cursor>
1257
+ --no-assets Export only non-asset documents and remove references to image assets
1258
+ --no-compress Skips compressing tarball entries (still generates a gzip file)
1259
+ --no-drafts Export only published versions of documents
1260
+ --no-strict-asset-verification Do not abort the export when an asset fails hash or content-length verification
1261
+ --overwrite Overwrite any file with the same name
1262
+ --raw Extract only documents, without rewriting asset references
1263
+ --types=<value> Defines which document types to export (comma-separated)
1257
1264
 
1258
1265
  OVERRIDE FLAGS
1259
1266
  -p, --project-id=<id> Project ID to export dataset from (overrides CLI configuration)
@@ -1277,6 +1284,10 @@ EXAMPLES
1277
1284
  Export specific document types
1278
1285
 
1279
1286
  $ sanity datasets export staging staging.tar.gz --types products,shops
1287
+
1288
+ Export dataset without aborting on asset verification failures
1289
+
1290
+ $ sanity datasets export moviedb moviedb.tar.gz --no-strict-asset-verification
1280
1291
  ```
1281
1292
 
1282
1293
  ## `sanity datasets import SOURCE [TARGETDATASET]`
@@ -2399,7 +2410,7 @@ USAGE
2399
2410
  <name> | --dataset-default] [--env <filename> | ] [--git <message> | ] [--import-dataset] [--mcp]
2400
2411
  [--nextjs-add-config-files] [--nextjs-append-env] [--nextjs-embed-studio] [--organization <id>] [--output-path
2401
2412
  <path> | ] [--overwrite-files] [--package-manager <manager> | ] [--project <id> | | --project-name <name>]
2402
- [--provider <provider>] [--template <template> | ] [--typescript | ] [--visibility <mode>] [-y]
2413
+ [--provider <provider>] [--skills] [--template <template> | ] [--typescript | ] [--visibility <mode>] [-y]
2403
2414
 
2404
2415
  FLAGS
2405
2416
  -y, --yes Unattended mode, answers "yes" to any "yes/no" prompt and otherwise uses defaults
@@ -2421,6 +2432,7 @@ FLAGS
2421
2432
  --project-name=<name> Create a new project with the given name
2422
2433
  --project-plan=<name> Optionally select a plan for a new project
2423
2434
  --provider=<provider> Login provider to use
2435
+ --[no-]skills Install Sanity agent skills globally for detected AI editors
2424
2436
  --template=<template> Project template to use [default: "clean"]
2425
2437
  --[no-]typescript Enable TypeScript support
2426
2438
  --visibility=<mode> Visibility mode for dataset
@@ -126,29 +126,6 @@ export async function initAction(options, context) {
126
126
  trace.complete();
127
127
  return;
128
128
  }
129
- let initNext = flagOrDefault(options.nextjsAddConfigFiles, false);
130
- if (isNextJs && shouldPrompt(options.unattended, options.nextjsAddConfigFiles)) {
131
- initNext = await promptForConfigFiles();
132
- }
133
- trace.log({
134
- detectedFramework: detectedFramework?.name,
135
- selectedOption: initNext ? 'yes' : 'no',
136
- step: 'useDetectedFramework'
137
- });
138
- const sluggedName = deburr(displayName.toLowerCase()).replaceAll(/\s+/g, '-').replaceAll(/[^a-z0-9-]/g, '');
139
- const initFramework = initNext;
140
- const defaults = await getProjectDefaults({
141
- isPlugin: false,
142
- workDir
143
- });
144
- const outputPath = await getProjectOutputPath({
145
- initFramework,
146
- outputPath: options.outputPath,
147
- sluggedName,
148
- unattended: options.unattended,
149
- useEnv: Boolean(options.env),
150
- workDir
151
- });
152
129
  // Detect editors once, then share the result with MCP and skills setup so
153
130
  // we don't pay the detection cost (filesystem probes + CLI execa calls) twice.
154
131
  const detectedEditors = options.mcpMode === 'skip' && options.skillsMode === 'skip' ? [] : await detectAvailableEditors();
@@ -193,6 +170,29 @@ export async function initAction(options, context) {
193
170
  const label = alreadyConfiguredEditors.length === 1 ? `${alreadyConfiguredEditors[0]} already configured for Sanity MCP` : `${alreadyConfiguredEditors.length} editors already configured for Sanity MCP`;
194
171
  spinner(label).start().succeed();
195
172
  }
173
+ let initNext = flagOrDefault(options.nextjsAddConfigFiles, false);
174
+ if (isNextJs && shouldPrompt(options.unattended, options.nextjsAddConfigFiles)) {
175
+ initNext = await promptForConfigFiles();
176
+ }
177
+ trace.log({
178
+ detectedFramework: detectedFramework?.name,
179
+ selectedOption: initNext ? 'yes' : 'no',
180
+ step: 'useDetectedFramework'
181
+ });
182
+ const sluggedName = deburr(displayName.toLowerCase()).replaceAll(/\s+/g, '-').replaceAll(/[^a-z0-9-]/g, '');
183
+ const initFramework = initNext;
184
+ const defaults = await getProjectDefaults({
185
+ isPlugin: false,
186
+ workDir
187
+ });
188
+ const outputPath = await getProjectOutputPath({
189
+ initFramework,
190
+ outputPath: options.outputPath,
191
+ sluggedName,
192
+ unattended: options.unattended,
193
+ useEnv: Boolean(options.env),
194
+ workDir
195
+ });
196
196
  if (isNextJs) {
197
197
  await checkNextJsReactCompatibility({
198
198
  detectedFramework,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/init/initAction.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {type SanityOrgUser, subdebug, type TelemetryUserProperties} from '@sanity/cli-core'\nimport {logSymbols, spinner} from '@sanity/cli-core/ux'\nimport {type TelemetryTrace} from '@sanity/telemetry'\nimport {type Framework, frameworks} from '@vercel/frameworks'\nimport deburr from 'lodash-es/deburr.js'\n\nimport {promptForConfigFiles} from '../../prompts/init/nextjs.js'\nimport {getCliUser} from '../../services/user.js'\nimport {CLIInitStepCompleted, type InitStepResult} from '../../telemetry/init.telemetry.js'\nimport {detectFrameworkRecord} from '../../util/detectFramework.js'\nimport {getProjectDefaults} from '../../util/getProjectDefaults.js'\nimport {validateSession} from '../auth/ensureAuthenticated.js'\nimport {getProviderName} from '../auth/getProviderName.js'\nimport {login} from '../auth/login/login.js'\nimport {detectAvailableEditors} from '../mcp/detectAvailableEditors.js'\nimport {setupMCP} from '../mcp/setupMCP.js'\nimport {setupSkills} from '../skills/setupSkills.js'\nimport {checkNextJsReactCompatibility} from './checkNextJsReactCompatibility.js'\nimport {determineAppTemplate} from './determineAppTemplate.js'\nimport {createOrAppendEnvVars} from './env/createOrAppendEnvVars.js'\nimport {initApp} from './initApp.js'\nimport {InitError} from './initError.js'\nimport {flagOrDefault, shouldPrompt, writeStagingEnvIfNeeded} from './initHelpers.js'\nimport {initNextJs} from './initNextJs.js'\nimport {initStudio} from './initStudio.js'\nimport {getPlan} from './plan/getPlan.js'\nimport {createProjectFromName} from './project/createProjectFromName.js'\nimport {getProjectDetails} from './project/getProjectDetails.js'\nimport {getProjectOutputPath} from './project/getProjectOutputPath.js'\nimport {checkIsRemoteTemplate, getGitHubRepoInfo, type RepoInfo} from './remoteTemplate.js'\nimport {type InitContext, type InitOptions} from './types.js'\n\nconst debug = subdebug('init')\n\nexport async function initAction(options: InitOptions, context: InitContext): Promise<void> {\n const {output, workDir} = context\n\n if (options.argType) {\n throw new InitError(\n options.argType === 'plugin'\n ? 'Initializing plugins through the CLI is no longer supported'\n : `Unknown init type \"${options.argType}\"`,\n 1,\n )\n }\n\n const trace = context.telemetry.trace(CLIInitStepCompleted)\n\n if (options.reconfigure) {\n throw new InitError('--reconfigure is deprecated - manual configuration is now required', 1)\n }\n\n if (options.project && options.organization) {\n throw new InitError(\n 'You have specified both a project and an organization. To move a project to an organization please visit https://www.sanity.io/manage',\n 1,\n )\n }\n\n const defaultConfig = options.datasetDefault\n let showDefaultConfigPrompt = !defaultConfig\n if (options.dataset || options.visibility || options.datasetDefault || options.unattended) {\n showDefaultConfigPrompt = false\n }\n\n const detectedFramework = await detectFrameworkRecord({\n frameworkList: frameworks as readonly Framework[],\n rootPath: workDir,\n })\n const isNextJs = detectedFramework?.slug === 'nextjs'\n\n let remoteTemplateInfo: RepoInfo | undefined\n if (options.template && checkIsRemoteTemplate(options.template)) {\n remoteTemplateInfo = await getGitHubRepoInfo(options.template, options.templateToken)\n }\n\n if (detectedFramework && detectedFramework.slug !== 'sanity' && remoteTemplateInfo) {\n throw new InitError(\n `A remote template cannot be used with a detected framework. Detected: ${detectedFramework.name}`,\n 1,\n )\n }\n\n const isAppTemplate = options.template ? determineAppTemplate(options.template) : false\n\n if (options.unattended) {\n checkFlagsInUnattendedMode(options, {isAppTemplate, isNextJs})\n }\n\n trace.start()\n trace.log({\n flags: {\n bare: options.bare,\n coupon: options.coupon,\n defaultConfig,\n env: options.env,\n git: typeof options.git === 'string' ? options.git : undefined,\n plan: options.projectPlan,\n reconfigure: options.reconfigure,\n unattended: options.unattended,\n },\n step: 'start',\n })\n\n const planId = await getPlan(options, output, trace)\n\n let envFilenameDefault = '.env'\n if (detectedFramework && detectedFramework.slug === 'nextjs') {\n envFilenameDefault = '.env.local'\n }\n const envFilename = typeof options.env === 'string' ? options.env : envFilenameDefault\n\n const {user} = await ensureAuthenticated(options, output, trace)\n if (!isAppTemplate) {\n output.log(`${logSymbols.success} Fetching existing projects`)\n output.log('')\n }\n\n let newProject: string | undefined\n if (options.projectName) {\n newProject = await createProjectFromName({\n coupon: options.coupon,\n createProjectName: options.projectName,\n dataset: options.dataset,\n organization: options.organization,\n planId,\n user,\n visibility: options.visibility,\n })\n }\n\n const {datasetName, displayName, isFirstProject, organizationId, projectId} =\n await getProjectDetails({\n coupon: options.coupon,\n dataset: options.dataset,\n datasetDefault: options.datasetDefault,\n isAppTemplate,\n newProject,\n organization: options.organization,\n output,\n planId,\n project: options.project,\n showDefaultConfigPrompt,\n trace,\n unattended: options.unattended,\n user,\n visibility: options.visibility,\n })\n\n if (options.bare) {\n output.log(`${logSymbols.success} Below are your project details`)\n output.log('')\n output.log(`Project ID: ${styleText('cyan', projectId)}`)\n output.log(`Dataset: ${styleText('cyan', datasetName)}`)\n output.log(\n `\\nYou can find your project on Sanity Manage — https://www.sanity.io/manage/project/${projectId}\\n`,\n )\n trace.complete()\n return\n }\n\n let initNext = flagOrDefault(options.nextjsAddConfigFiles, false)\n if (isNextJs && shouldPrompt(options.unattended, options.nextjsAddConfigFiles)) {\n initNext = await promptForConfigFiles()\n }\n\n trace.log({\n detectedFramework: detectedFramework?.name,\n selectedOption: initNext ? 'yes' : 'no',\n step: 'useDetectedFramework',\n })\n\n const sluggedName = deburr(displayName.toLowerCase())\n .replaceAll(/\\s+/g, '-')\n .replaceAll(/[^a-z0-9-]/g, '')\n\n const initFramework = initNext\n\n const defaults = await getProjectDefaults({isPlugin: false, workDir})\n\n const outputPath = await getProjectOutputPath({\n initFramework,\n outputPath: options.outputPath,\n sluggedName,\n unattended: options.unattended,\n useEnv: Boolean(options.env),\n workDir,\n })\n\n // Detect editors once, then share the result with MCP and skills setup so\n // we don't pay the detection cost (filesystem probes + CLI execa calls) twice.\n const detectedEditors =\n options.mcpMode === 'skip' && options.skillsMode === 'skip'\n ? []\n : await detectAvailableEditors()\n\n const mcpResult = await setupMCP({\n editors: detectedEditors,\n mode: options.mcpMode,\n skillsMode: options.skillsMode,\n })\n\n trace.log({\n configuredEditors: mcpResult.configuredEditors,\n detectedEditors: mcpResult.detectedEditors,\n skipped: mcpResult.skipped,\n step: 'mcpSetup',\n })\n if (mcpResult.error) {\n trace.error(mcpResult.error)\n }\n const mcpConfigured = mcpResult.configuredEditors\n\n async function installSkills(): Promise<void> {\n if (mcpResult.skillsToInstall.length === 0) return\n try {\n const skillsResult = await setupSkills({agents: mcpResult.skillsToInstall})\n trace.log({\n installedAgents: skillsResult.installedAgents,\n skipped: skillsResult.skipped,\n step: 'skillsSetup',\n })\n if (skillsResult.error) {\n trace.error(skillsResult.error)\n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n debug('Unexpected error from setupSkills %O', err)\n output.warn(`Could not install Sanity agent skills: ${err.message}`)\n trace.error(err)\n }\n }\n\n const {alreadyConfiguredEditors} = mcpResult\n if (alreadyConfiguredEditors.length > 0) {\n const label =\n alreadyConfiguredEditors.length === 1\n ? `${alreadyConfiguredEditors[0]} already configured for Sanity MCP`\n : `${alreadyConfiguredEditors.length} editors already configured for Sanity MCP`\n spinner(label).start().succeed()\n }\n\n if (isNextJs) {\n await checkNextJsReactCompatibility({\n detectedFramework,\n output,\n outputPath,\n })\n }\n\n if (initNext) {\n await initNextJs({\n datasetName,\n detectedFramework,\n envFilename,\n mcpConfigured,\n options,\n output,\n projectId,\n trace,\n workDir,\n })\n await installSkills()\n trace.complete()\n return\n }\n\n if (options.env) {\n await createOrAppendEnvVars({\n envVars: {\n DATASET: datasetName,\n PROJECT_ID: projectId,\n },\n filename: envFilename,\n framework: detectedFramework,\n log: false,\n output,\n outputPath,\n })\n await writeStagingEnvIfNeeded(output, outputPath)\n await installSkills()\n trace.complete()\n return\n }\n\n const sharedParams = {\n defaults,\n mcpConfigured,\n options,\n organizationId,\n output,\n outputPath,\n remoteTemplateInfo,\n sluggedName,\n trace,\n workDir,\n }\n\n await (isAppTemplate\n ? initApp({...sharedParams, datasetName, projectId})\n : initStudio({\n ...sharedParams,\n datasetName,\n displayName,\n isFirstProject,\n projectId,\n }))\n\n await installSkills()\n\n trace.complete()\n}\n\nfunction checkFlagsInUnattendedMode(\n options: InitOptions,\n {isAppTemplate, isNextJs}: {isAppTemplate: boolean; isNextJs: boolean},\n): void {\n debug('Unattended mode, validating required options')\n\n if (options.projectName && !options.organization) {\n throw new InitError('`--project-name` requires `--organization <id>` in unattended mode', 1)\n }\n\n if (isAppTemplate) {\n if (!options.outputPath) {\n throw new InitError('`--output-path` must be specified in unattended mode', 1)\n }\n\n const hasProjectFlag = Boolean(options.project || options.projectName)\n\n if (!hasProjectFlag && !options.organization) {\n throw new InitError(\n 'The --organization flag is required for app templates in unattended mode. ' +\n 'Use --organization <id>, or pass --project <id> / --project-name <name>.',\n 1,\n )\n }\n\n return\n }\n\n if (!isNextJs && !options.bare && !options.outputPath) {\n throw new InitError('`--output-path` must be specified in unattended mode', 1)\n }\n\n if (!options.project && !options.projectName) {\n throw new InitError(\n '`--project <id>` or `--project-name <name>` must be specified in unattended mode',\n 1,\n )\n }\n}\n\nasync function ensureAuthenticated(\n options: InitOptions,\n output: InitContext['output'],\n trace: TelemetryTrace<TelemetryUserProperties, InitStepResult>,\n): Promise<{user: SanityOrgUser}> {\n const user = await validateSession()\n\n if (user) {\n trace.log({alreadyLoggedIn: true, step: 'login'})\n output.log(\n `${logSymbols.success} You are logged in as ${user.email} using ${getProviderName(user.provider)}`,\n )\n return {user}\n }\n\n if (options.unattended) {\n throw new InitError(\n 'Must be logged in to run this command in unattended mode, run `sanity login`',\n 1,\n )\n }\n\n trace.log({step: 'login'})\n\n try {\n await login({\n output,\n telemetry: trace.newContext('login'),\n })\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n throw new InitError(`Login failed: ${message}`, 1)\n }\n\n const loggedInUser = await getCliUser()\n\n output.log(\n `${logSymbols.success} You are logged in as ${loggedInUser.email} using ${getProviderName(loggedInUser.provider)}`,\n )\n return {user: loggedInUser}\n}\n"],"names":["styleText","subdebug","logSymbols","spinner","frameworks","deburr","promptForConfigFiles","getCliUser","CLIInitStepCompleted","detectFrameworkRecord","getProjectDefaults","validateSession","getProviderName","login","detectAvailableEditors","setupMCP","setupSkills","checkNextJsReactCompatibility","determineAppTemplate","createOrAppendEnvVars","initApp","InitError","flagOrDefault","shouldPrompt","writeStagingEnvIfNeeded","initNextJs","initStudio","getPlan","createProjectFromName","getProjectDetails","getProjectOutputPath","checkIsRemoteTemplate","getGitHubRepoInfo","debug","initAction","options","context","output","workDir","argType","trace","telemetry","reconfigure","project","organization","defaultConfig","datasetDefault","showDefaultConfigPrompt","dataset","visibility","unattended","detectedFramework","frameworkList","rootPath","isNextJs","slug","remoteTemplateInfo","template","templateToken","name","isAppTemplate","checkFlagsInUnattendedMode","start","log","flags","bare","coupon","env","git","undefined","plan","projectPlan","step","planId","envFilenameDefault","envFilename","user","ensureAuthenticated","success","newProject","projectName","createProjectName","datasetName","displayName","isFirstProject","organizationId","projectId","complete","initNext","nextjsAddConfigFiles","selectedOption","sluggedName","toLowerCase","replaceAll","initFramework","defaults","isPlugin","outputPath","useEnv","Boolean","detectedEditors","mcpMode","skillsMode","mcpResult","editors","mode","configuredEditors","skipped","error","mcpConfigured","installSkills","skillsToInstall","length","skillsResult","agents","installedAgents","err","Error","String","warn","message","alreadyConfiguredEditors","label","succeed","envVars","DATASET","PROJECT_ID","filename","framework","sharedParams","hasProjectFlag","alreadyLoggedIn","email","provider","newContext","loggedInUser"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/actions/init/initAction.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {type SanityOrgUser, subdebug, type TelemetryUserProperties} from '@sanity/cli-core'\nimport {logSymbols, spinner} from '@sanity/cli-core/ux'\nimport {type TelemetryTrace} from '@sanity/telemetry'\nimport {type Framework, frameworks} from '@vercel/frameworks'\nimport deburr from 'lodash-es/deburr.js'\n\nimport {promptForConfigFiles} from '../../prompts/init/nextjs.js'\nimport {getCliUser} from '../../services/user.js'\nimport {CLIInitStepCompleted, type InitStepResult} from '../../telemetry/init.telemetry.js'\nimport {detectFrameworkRecord} from '../../util/detectFramework.js'\nimport {getProjectDefaults} from '../../util/getProjectDefaults.js'\nimport {validateSession} from '../auth/ensureAuthenticated.js'\nimport {getProviderName} from '../auth/getProviderName.js'\nimport {login} from '../auth/login/login.js'\nimport {detectAvailableEditors} from '../mcp/detectAvailableEditors.js'\nimport {setupMCP} from '../mcp/setupMCP.js'\nimport {setupSkills} from '../skills/setupSkills.js'\nimport {checkNextJsReactCompatibility} from './checkNextJsReactCompatibility.js'\nimport {determineAppTemplate} from './determineAppTemplate.js'\nimport {createOrAppendEnvVars} from './env/createOrAppendEnvVars.js'\nimport {initApp} from './initApp.js'\nimport {InitError} from './initError.js'\nimport {flagOrDefault, shouldPrompt, writeStagingEnvIfNeeded} from './initHelpers.js'\nimport {initNextJs} from './initNextJs.js'\nimport {initStudio} from './initStudio.js'\nimport {getPlan} from './plan/getPlan.js'\nimport {createProjectFromName} from './project/createProjectFromName.js'\nimport {getProjectDetails} from './project/getProjectDetails.js'\nimport {getProjectOutputPath} from './project/getProjectOutputPath.js'\nimport {checkIsRemoteTemplate, getGitHubRepoInfo, type RepoInfo} from './remoteTemplate.js'\nimport {type InitContext, type InitOptions} from './types.js'\n\nconst debug = subdebug('init')\n\nexport async function initAction(options: InitOptions, context: InitContext): Promise<void> {\n const {output, workDir} = context\n\n if (options.argType) {\n throw new InitError(\n options.argType === 'plugin'\n ? 'Initializing plugins through the CLI is no longer supported'\n : `Unknown init type \"${options.argType}\"`,\n 1,\n )\n }\n\n const trace = context.telemetry.trace(CLIInitStepCompleted)\n\n if (options.reconfigure) {\n throw new InitError('--reconfigure is deprecated - manual configuration is now required', 1)\n }\n\n if (options.project && options.organization) {\n throw new InitError(\n 'You have specified both a project and an organization. To move a project to an organization please visit https://www.sanity.io/manage',\n 1,\n )\n }\n\n const defaultConfig = options.datasetDefault\n let showDefaultConfigPrompt = !defaultConfig\n if (options.dataset || options.visibility || options.datasetDefault || options.unattended) {\n showDefaultConfigPrompt = false\n }\n\n const detectedFramework = await detectFrameworkRecord({\n frameworkList: frameworks as readonly Framework[],\n rootPath: workDir,\n })\n const isNextJs = detectedFramework?.slug === 'nextjs'\n\n let remoteTemplateInfo: RepoInfo | undefined\n if (options.template && checkIsRemoteTemplate(options.template)) {\n remoteTemplateInfo = await getGitHubRepoInfo(options.template, options.templateToken)\n }\n\n if (detectedFramework && detectedFramework.slug !== 'sanity' && remoteTemplateInfo) {\n throw new InitError(\n `A remote template cannot be used with a detected framework. Detected: ${detectedFramework.name}`,\n 1,\n )\n }\n\n const isAppTemplate = options.template ? determineAppTemplate(options.template) : false\n\n if (options.unattended) {\n checkFlagsInUnattendedMode(options, {isAppTemplate, isNextJs})\n }\n\n trace.start()\n trace.log({\n flags: {\n bare: options.bare,\n coupon: options.coupon,\n defaultConfig,\n env: options.env,\n git: typeof options.git === 'string' ? options.git : undefined,\n plan: options.projectPlan,\n reconfigure: options.reconfigure,\n unattended: options.unattended,\n },\n step: 'start',\n })\n\n const planId = await getPlan(options, output, trace)\n\n let envFilenameDefault = '.env'\n if (detectedFramework && detectedFramework.slug === 'nextjs') {\n envFilenameDefault = '.env.local'\n }\n const envFilename = typeof options.env === 'string' ? options.env : envFilenameDefault\n\n const {user} = await ensureAuthenticated(options, output, trace)\n if (!isAppTemplate) {\n output.log(`${logSymbols.success} Fetching existing projects`)\n output.log('')\n }\n\n let newProject: string | undefined\n if (options.projectName) {\n newProject = await createProjectFromName({\n coupon: options.coupon,\n createProjectName: options.projectName,\n dataset: options.dataset,\n organization: options.organization,\n planId,\n user,\n visibility: options.visibility,\n })\n }\n\n const {datasetName, displayName, isFirstProject, organizationId, projectId} =\n await getProjectDetails({\n coupon: options.coupon,\n dataset: options.dataset,\n datasetDefault: options.datasetDefault,\n isAppTemplate,\n newProject,\n organization: options.organization,\n output,\n planId,\n project: options.project,\n showDefaultConfigPrompt,\n trace,\n unattended: options.unattended,\n user,\n visibility: options.visibility,\n })\n\n if (options.bare) {\n output.log(`${logSymbols.success} Below are your project details`)\n output.log('')\n output.log(`Project ID: ${styleText('cyan', projectId)}`)\n output.log(`Dataset: ${styleText('cyan', datasetName)}`)\n output.log(\n `\\nYou can find your project on Sanity Manage — https://www.sanity.io/manage/project/${projectId}\\n`,\n )\n trace.complete()\n return\n }\n\n // Detect editors once, then share the result with MCP and skills setup so\n // we don't pay the detection cost (filesystem probes + CLI execa calls) twice.\n const detectedEditors =\n options.mcpMode === 'skip' && options.skillsMode === 'skip'\n ? []\n : await detectAvailableEditors()\n\n const mcpResult = await setupMCP({\n editors: detectedEditors,\n mode: options.mcpMode,\n skillsMode: options.skillsMode,\n })\n\n trace.log({\n configuredEditors: mcpResult.configuredEditors,\n detectedEditors: mcpResult.detectedEditors,\n skipped: mcpResult.skipped,\n step: 'mcpSetup',\n })\n if (mcpResult.error) {\n trace.error(mcpResult.error)\n }\n const mcpConfigured = mcpResult.configuredEditors\n\n async function installSkills(): Promise<void> {\n if (mcpResult.skillsToInstall.length === 0) return\n try {\n const skillsResult = await setupSkills({agents: mcpResult.skillsToInstall})\n trace.log({\n installedAgents: skillsResult.installedAgents,\n skipped: skillsResult.skipped,\n step: 'skillsSetup',\n })\n if (skillsResult.error) {\n trace.error(skillsResult.error)\n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n debug('Unexpected error from setupSkills %O', err)\n output.warn(`Could not install Sanity agent skills: ${err.message}`)\n trace.error(err)\n }\n }\n\n const {alreadyConfiguredEditors} = mcpResult\n if (alreadyConfiguredEditors.length > 0) {\n const label =\n alreadyConfiguredEditors.length === 1\n ? `${alreadyConfiguredEditors[0]} already configured for Sanity MCP`\n : `${alreadyConfiguredEditors.length} editors already configured for Sanity MCP`\n spinner(label).start().succeed()\n }\n\n let initNext = flagOrDefault(options.nextjsAddConfigFiles, false)\n if (isNextJs && shouldPrompt(options.unattended, options.nextjsAddConfigFiles)) {\n initNext = await promptForConfigFiles()\n }\n\n trace.log({\n detectedFramework: detectedFramework?.name,\n selectedOption: initNext ? 'yes' : 'no',\n step: 'useDetectedFramework',\n })\n\n const sluggedName = deburr(displayName.toLowerCase())\n .replaceAll(/\\s+/g, '-')\n .replaceAll(/[^a-z0-9-]/g, '')\n\n const initFramework = initNext\n\n const defaults = await getProjectDefaults({isPlugin: false, workDir})\n\n const outputPath = await getProjectOutputPath({\n initFramework,\n outputPath: options.outputPath,\n sluggedName,\n unattended: options.unattended,\n useEnv: Boolean(options.env),\n workDir,\n })\n\n if (isNextJs) {\n await checkNextJsReactCompatibility({\n detectedFramework,\n output,\n outputPath,\n })\n }\n\n if (initNext) {\n await initNextJs({\n datasetName,\n detectedFramework,\n envFilename,\n mcpConfigured,\n options,\n output,\n projectId,\n trace,\n workDir,\n })\n await installSkills()\n trace.complete()\n return\n }\n\n if (options.env) {\n await createOrAppendEnvVars({\n envVars: {\n DATASET: datasetName,\n PROJECT_ID: projectId,\n },\n filename: envFilename,\n framework: detectedFramework,\n log: false,\n output,\n outputPath,\n })\n await writeStagingEnvIfNeeded(output, outputPath)\n await installSkills()\n trace.complete()\n return\n }\n\n const sharedParams = {\n defaults,\n mcpConfigured,\n options,\n organizationId,\n output,\n outputPath,\n remoteTemplateInfo,\n sluggedName,\n trace,\n workDir,\n }\n\n await (isAppTemplate\n ? initApp({...sharedParams, datasetName, projectId})\n : initStudio({\n ...sharedParams,\n datasetName,\n displayName,\n isFirstProject,\n projectId,\n }))\n\n await installSkills()\n\n trace.complete()\n}\n\nfunction checkFlagsInUnattendedMode(\n options: InitOptions,\n {isAppTemplate, isNextJs}: {isAppTemplate: boolean; isNextJs: boolean},\n): void {\n debug('Unattended mode, validating required options')\n\n if (options.projectName && !options.organization) {\n throw new InitError('`--project-name` requires `--organization <id>` in unattended mode', 1)\n }\n\n if (isAppTemplate) {\n if (!options.outputPath) {\n throw new InitError('`--output-path` must be specified in unattended mode', 1)\n }\n\n const hasProjectFlag = Boolean(options.project || options.projectName)\n\n if (!hasProjectFlag && !options.organization) {\n throw new InitError(\n 'The --organization flag is required for app templates in unattended mode. ' +\n 'Use --organization <id>, or pass --project <id> / --project-name <name>.',\n 1,\n )\n }\n\n return\n }\n\n if (!isNextJs && !options.bare && !options.outputPath) {\n throw new InitError('`--output-path` must be specified in unattended mode', 1)\n }\n\n if (!options.project && !options.projectName) {\n throw new InitError(\n '`--project <id>` or `--project-name <name>` must be specified in unattended mode',\n 1,\n )\n }\n}\n\nasync function ensureAuthenticated(\n options: InitOptions,\n output: InitContext['output'],\n trace: TelemetryTrace<TelemetryUserProperties, InitStepResult>,\n): Promise<{user: SanityOrgUser}> {\n const user = await validateSession()\n\n if (user) {\n trace.log({alreadyLoggedIn: true, step: 'login'})\n output.log(\n `${logSymbols.success} You are logged in as ${user.email} using ${getProviderName(user.provider)}`,\n )\n return {user}\n }\n\n if (options.unattended) {\n throw new InitError(\n 'Must be logged in to run this command in unattended mode, run `sanity login`',\n 1,\n )\n }\n\n trace.log({step: 'login'})\n\n try {\n await login({\n output,\n telemetry: trace.newContext('login'),\n })\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n throw new InitError(`Login failed: ${message}`, 1)\n }\n\n const loggedInUser = await getCliUser()\n\n output.log(\n `${logSymbols.success} You are logged in as ${loggedInUser.email} using ${getProviderName(loggedInUser.provider)}`,\n )\n return {user: loggedInUser}\n}\n"],"names":["styleText","subdebug","logSymbols","spinner","frameworks","deburr","promptForConfigFiles","getCliUser","CLIInitStepCompleted","detectFrameworkRecord","getProjectDefaults","validateSession","getProviderName","login","detectAvailableEditors","setupMCP","setupSkills","checkNextJsReactCompatibility","determineAppTemplate","createOrAppendEnvVars","initApp","InitError","flagOrDefault","shouldPrompt","writeStagingEnvIfNeeded","initNextJs","initStudio","getPlan","createProjectFromName","getProjectDetails","getProjectOutputPath","checkIsRemoteTemplate","getGitHubRepoInfo","debug","initAction","options","context","output","workDir","argType","trace","telemetry","reconfigure","project","organization","defaultConfig","datasetDefault","showDefaultConfigPrompt","dataset","visibility","unattended","detectedFramework","frameworkList","rootPath","isNextJs","slug","remoteTemplateInfo","template","templateToken","name","isAppTemplate","checkFlagsInUnattendedMode","start","log","flags","bare","coupon","env","git","undefined","plan","projectPlan","step","planId","envFilenameDefault","envFilename","user","ensureAuthenticated","success","newProject","projectName","createProjectName","datasetName","displayName","isFirstProject","organizationId","projectId","complete","detectedEditors","mcpMode","skillsMode","mcpResult","editors","mode","configuredEditors","skipped","error","mcpConfigured","installSkills","skillsToInstall","length","skillsResult","agents","installedAgents","err","Error","String","warn","message","alreadyConfiguredEditors","label","succeed","initNext","nextjsAddConfigFiles","selectedOption","sluggedName","toLowerCase","replaceAll","initFramework","defaults","isPlugin","outputPath","useEnv","Boolean","envVars","DATASET","PROJECT_ID","filename","framework","sharedParams","hasProjectFlag","alreadyLoggedIn","email","provider","newContext","loggedInUser"],"mappings":"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"}
@@ -198,7 +198,7 @@ export async function initNextJs({ datasetName, detectedFramework, envFilename,
198
198
  {
199
199
  await execa('npm', [
200
200
  'install',
201
- 'next-sanity@12'
201
+ 'next-sanity@13'
202
202
  ], execOptions);
203
203
  break;
204
204
  }
@@ -206,17 +206,17 @@ export async function initNextJs({ datasetName, detectedFramework, envFilename,
206
206
  {
207
207
  await execa('pnpm', [
208
208
  'install',
209
- 'next-sanity@12'
209
+ 'next-sanity@13'
210
210
  ], execOptions);
211
211
  break;
212
212
  }
213
213
  case 'yarn':
214
214
  {
215
- const peerDeps = await getPeerDependencies('next-sanity@12', workDir);
215
+ const peerDeps = await getPeerDependencies('next-sanity@13', workDir);
216
216
  await installNewPackages({
217
217
  packageManager: 'yarn',
218
218
  packages: [
219
- 'next-sanity@12',
219
+ 'next-sanity@13',
220
220
  ...peerDeps
221
221
  ]
222
222
  }, {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/init/initNextJs.ts"],"sourcesContent":["import {existsSync} from 'node:fs'\nimport {mkdir, writeFile} from 'node:fs/promises'\nimport path from 'node:path'\nimport {styleText} from 'node:util'\n\nimport {type Output, subdebug, type TelemetryUserProperties} from '@sanity/cli-core'\nimport {confirm} from '@sanity/cli-core/ux'\nimport {type TelemetryTrace} from '@sanity/telemetry'\nimport {execa, type Options} from 'execa'\n\nimport {\n promptForAppendEnv,\n promptForEmbeddedStudio,\n promptForNextTemplate,\n promptForStudioPath,\n} from '../../prompts/init/nextjs.js'\nimport {promptForTypeScript} from '../../prompts/init/promptForTypescript.js'\nimport {createCorsOrigin, listCorsOrigins} from '../../services/cors.js'\nimport {type InitStepResult} from '../../telemetry/init.telemetry.js'\nimport {getPeerDependencies} from '../../util/packageManager/getPeerDependencies.js'\nimport {installNewPackages} from '../../util/packageManager/installPackages.js'\nimport {\n getPartialEnvWithNpmPath,\n type PackageManager,\n} from '../../util/packageManager/packageManagerChoice.js'\nimport {type EditorName} from '../mcp/editorConfigs.js'\nimport {countNestedFolders} from './countNestedFolders.js'\nimport {createOrAppendEnvVars} from './env/createOrAppendEnvVars.js'\nimport {InitError} from './initError.js'\nimport {\n flagOrDefault,\n getPostInitMCPPrompt,\n shouldPrompt,\n writeStagingEnvIfNeeded,\n} from './initHelpers.js'\nimport {resolvePackageManager} from './resolvePackageManager.js'\nimport {\n sanityCliTemplate,\n sanityConfigTemplate,\n sanityFolder,\n sanityStudioTemplate,\n} from './templates/nextjs/index.js'\nimport {type InitOptions, type VersionedFramework} from './types.js'\n\nconst debug = subdebug('init')\n\nasync function writeOrOverwrite(\n filePath: string,\n content: string,\n workDir: string,\n options: InitOptions,\n) {\n if (existsSync(filePath)) {\n let overwrite = flagOrDefault(options.overwriteFiles, false)\n if (shouldPrompt(options.unattended, options.overwriteFiles)) {\n overwrite = await confirm({\n default: false,\n message: `File ${styleText(\n 'yellow',\n filePath.replace(workDir, ''),\n )} already exists. Do you want to overwrite it?`,\n })\n }\n\n if (!overwrite) {\n return\n }\n }\n\n // make folder if not exists\n const folderPath = path.dirname(filePath)\n\n try {\n await mkdir(folderPath, {recursive: true})\n } catch {\n debug('Error creating folder %s', folderPath)\n }\n\n await writeFile(filePath, content, {\n encoding: 'utf8',\n })\n}\n\n// write sanity folder files\nasync function writeSourceFiles({\n fileExtension,\n files,\n folderPath,\n options,\n srcFolderPrefix,\n workDir,\n}: {\n fileExtension: string\n files: Record<string, Record<string, string> | string>\n folderPath?: string\n options: InitOptions\n srcFolderPrefix?: boolean\n workDir: string\n}) {\n for (const [filePath, content] of Object.entries(files)) {\n // check if file ends with full stop to indicate it's file and not directory (this only works with our template tree structure)\n if (filePath.includes('.') && typeof content === 'string') {\n await writeOrOverwrite(\n path.join(\n workDir,\n srcFolderPrefix ? 'src' : '',\n 'sanity',\n folderPath || '',\n `${filePath}${fileExtension}`,\n ),\n content,\n workDir,\n options,\n )\n } else {\n await mkdir(path.join(workDir, srcFolderPrefix ? 'src' : '', 'sanity', filePath), {\n recursive: true,\n })\n if (typeof content === 'object') {\n await writeSourceFiles({\n fileExtension,\n files: content,\n folderPath: filePath,\n options,\n srcFolderPrefix,\n workDir,\n })\n }\n }\n }\n}\n\nexport async function initNextJs({\n datasetName,\n detectedFramework,\n envFilename,\n mcpConfigured,\n options,\n output,\n projectId,\n trace,\n workDir,\n}: {\n datasetName: string\n detectedFramework: VersionedFramework | null\n envFilename: string\n mcpConfigured: EditorName[]\n options: InitOptions\n output: Output\n projectId: string\n trace: TelemetryTrace<TelemetryUserProperties, InitStepResult>\n workDir: string\n}): Promise<void> {\n const {nextjsAppendEnv, nextjsEmbedStudio, packageManager, template, typescript, unattended} =\n options\n let useTypeScript = flagOrDefault(typescript, true)\n if (shouldPrompt(unattended, typescript)) {\n useTypeScript = await promptForTypeScript()\n }\n trace.log({\n selectedOption: useTypeScript ? 'yes' : 'no',\n step: 'useTypeScript',\n })\n\n const fileExtension = useTypeScript ? 'ts' : 'js'\n let embeddedStudio = flagOrDefault(nextjsEmbedStudio, true)\n if (shouldPrompt(unattended, nextjsEmbedStudio)) {\n embeddedStudio = await promptForEmbeddedStudio()\n }\n let hasSrcFolder = false\n\n if (embeddedStudio) {\n // find source path (app or src/app)\n const appDir = 'app'\n let srcPath = path.join(workDir, appDir)\n\n if (!existsSync(srcPath)) {\n srcPath = path.join(workDir, 'src', appDir)\n hasSrcFolder = true\n if (!existsSync(srcPath)) {\n try {\n await mkdir(srcPath, {recursive: true})\n } catch {\n debug('Error creating folder %s', srcPath)\n }\n }\n }\n\n const studioPath = unattended ? '/studio' : await promptForStudioPath()\n\n const embeddedStudioRouteFilePath = path.join(\n srcPath,\n `${studioPath}/`,\n `[[...tool]]/page.${fileExtension}x`,\n )\n\n // this selects the correct template string based on whether the user is using the app or pages directory and\n // replaces the \":configPath:\" placeholder in the template with the correct path to the sanity.config.ts file.\n // we account for the user-defined embeddedStudioPath (default /studio) is accounted for by creating enough \"../\"\n // relative paths to reach the root level of the project\n await writeOrOverwrite(\n embeddedStudioRouteFilePath,\n sanityStudioTemplate.replace(\n ':configPath:',\n `${'../'.repeat(countNestedFolders(path.dirname(embeddedStudioRouteFilePath.slice(workDir.length))))}sanity.config`,\n ),\n workDir,\n options,\n )\n\n const sanityConfigPath = path.join(workDir, `sanity.config.${fileExtension}`)\n await writeOrOverwrite(\n sanityConfigPath,\n sanityConfigTemplate(hasSrcFolder)\n .replace(':route:', embeddedStudioRouteFilePath.slice(workDir.length).replace('src/', ''))\n .replace(':basePath:', studioPath),\n workDir,\n options,\n )\n }\n\n const sanityCliPath = path.join(workDir, `sanity.cli.${fileExtension}`)\n await writeOrOverwrite(sanityCliPath, sanityCliTemplate, workDir, options)\n\n let templateToUse = template ?? 'clean'\n if (shouldPrompt(unattended, template)) {\n templateToUse = await promptForNextTemplate()\n }\n\n await writeSourceFiles({\n fileExtension,\n files: sanityFolder(useTypeScript, templateToUse as 'blog' | 'clean'),\n folderPath: undefined,\n options,\n srcFolderPrefix: hasSrcFolder,\n workDir,\n })\n\n let appendEnv = flagOrDefault(nextjsAppendEnv, true)\n if (shouldPrompt(unattended, nextjsAppendEnv)) {\n appendEnv = await promptForAppendEnv(envFilename)\n }\n\n if (appendEnv) {\n await createOrAppendEnvVars({\n envVars: {\n DATASET: datasetName,\n PROJECT_ID: projectId,\n },\n filename: envFilename,\n framework: detectedFramework,\n log: true,\n output,\n outputPath: workDir,\n })\n }\n\n if (embeddedStudio) {\n const nextjsLocalDevOrigin = 'http://localhost:3000'\n const existingCorsOrigins = await listCorsOrigins(projectId)\n const hasExistingCorsOrigin = existingCorsOrigins.some(\n (item: {origin: string}) => item.origin === nextjsLocalDevOrigin,\n )\n if (!hasExistingCorsOrigin) {\n try {\n const createCorsRes = await createCorsOrigin({\n allowCredentials: true,\n origin: nextjsLocalDevOrigin,\n projectId,\n })\n\n output.log(\n createCorsRes.id\n ? `Added ${nextjsLocalDevOrigin} to CORS origins`\n : `Failed to add ${nextjsLocalDevOrigin} to CORS origins`,\n )\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n debug(`Error creating new CORS Origin ${nextjsLocalDevOrigin}: ${message}`)\n throw new InitError(`Failed to add ${nextjsLocalDevOrigin} to CORS origins: ${message}`, 1)\n }\n }\n }\n\n const chosen = await resolvePackageManager({\n interactive: !unattended,\n output,\n packageManager: packageManager as PackageManager,\n targetDir: workDir,\n })\n trace.log({selectedOption: chosen, step: 'selectPackageManager'})\n const packages = ['@sanity/vision@5', 'sanity@5', '@sanity/image-url@2', 'styled-components@6']\n if (templateToUse === 'blog') {\n packages.push('@sanity/icons')\n }\n await installNewPackages(\n {\n packageManager: chosen,\n packages,\n },\n {\n output,\n workDir,\n },\n )\n\n // will refactor this later\n const execOptions: Options = {\n cwd: workDir,\n encoding: 'utf8',\n env: getPartialEnvWithNpmPath(workDir),\n stdio: 'inherit',\n }\n\n switch (chosen) {\n case 'npm': {\n await execa('npm', ['install', 'next-sanity@12'], execOptions)\n break\n }\n case 'pnpm': {\n await execa('pnpm', ['install', 'next-sanity@12'], execOptions)\n break\n }\n case 'yarn': {\n const peerDeps = await getPeerDependencies('next-sanity@12', workDir)\n await installNewPackages(\n {packageManager: 'yarn', packages: ['next-sanity@12', ...peerDeps]},\n {output, workDir},\n )\n break\n }\n default: {\n // bun and manual - do nothing or handle differently\n break\n }\n }\n\n output.log(\n `\\n${styleText('green', 'Success!')} Your Sanity configuration files has been added to this project`,\n )\n if (mcpConfigured && mcpConfigured.length > 0) {\n const message = await getPostInitMCPPrompt(mcpConfigured)\n output.log(`\\n${message}`)\n output.log(`\\nLearn more: ${styleText('cyan', 'https://mcp.sanity.io')}`)\n output.log(\n `\\nHave feedback? Tell us in the community: ${styleText('cyan', 'https://www.sanity.io/community/join')}`,\n )\n }\n\n await writeStagingEnvIfNeeded(output, workDir)\n}\n"],"names":["existsSync","mkdir","writeFile","path","styleText","subdebug","confirm","execa","promptForAppendEnv","promptForEmbeddedStudio","promptForNextTemplate","promptForStudioPath","promptForTypeScript","createCorsOrigin","listCorsOrigins","getPeerDependencies","installNewPackages","getPartialEnvWithNpmPath","countNestedFolders","createOrAppendEnvVars","InitError","flagOrDefault","getPostInitMCPPrompt","shouldPrompt","writeStagingEnvIfNeeded","resolvePackageManager","sanityCliTemplate","sanityConfigTemplate","sanityFolder","sanityStudioTemplate","debug","writeOrOverwrite","filePath","content","workDir","options","overwrite","overwriteFiles","unattended","default","message","replace","folderPath","dirname","recursive","encoding","writeSourceFiles","fileExtension","files","srcFolderPrefix","Object","entries","includes","join","initNextJs","datasetName","detectedFramework","envFilename","mcpConfigured","output","projectId","trace","nextjsAppendEnv","nextjsEmbedStudio","packageManager","template","typescript","useTypeScript","log","selectedOption","step","embeddedStudio","hasSrcFolder","appDir","srcPath","studioPath","embeddedStudioRouteFilePath","repeat","slice","length","sanityConfigPath","sanityCliPath","templateToUse","undefined","appendEnv","envVars","DATASET","PROJECT_ID","filename","framework","outputPath","nextjsLocalDevOrigin","existingCorsOrigins","hasExistingCorsOrigin","some","item","origin","createCorsRes","allowCredentials","id","error","Error","String","chosen","interactive","targetDir","packages","push","execOptions","cwd","env","stdio","peerDeps"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/actions/init/initNextJs.ts"],"sourcesContent":["import {existsSync} from 'node:fs'\nimport {mkdir, writeFile} from 'node:fs/promises'\nimport path from 'node:path'\nimport {styleText} from 'node:util'\n\nimport {type Output, subdebug, type TelemetryUserProperties} from '@sanity/cli-core'\nimport {confirm} from '@sanity/cli-core/ux'\nimport {type TelemetryTrace} from '@sanity/telemetry'\nimport {execa, type Options} from 'execa'\n\nimport {\n promptForAppendEnv,\n promptForEmbeddedStudio,\n promptForNextTemplate,\n promptForStudioPath,\n} from '../../prompts/init/nextjs.js'\nimport {promptForTypeScript} from '../../prompts/init/promptForTypescript.js'\nimport {createCorsOrigin, listCorsOrigins} from '../../services/cors.js'\nimport {type InitStepResult} from '../../telemetry/init.telemetry.js'\nimport {getPeerDependencies} from '../../util/packageManager/getPeerDependencies.js'\nimport {installNewPackages} from '../../util/packageManager/installPackages.js'\nimport {\n getPartialEnvWithNpmPath,\n type PackageManager,\n} from '../../util/packageManager/packageManagerChoice.js'\nimport {type EditorName} from '../mcp/editorConfigs.js'\nimport {countNestedFolders} from './countNestedFolders.js'\nimport {createOrAppendEnvVars} from './env/createOrAppendEnvVars.js'\nimport {InitError} from './initError.js'\nimport {\n flagOrDefault,\n getPostInitMCPPrompt,\n shouldPrompt,\n writeStagingEnvIfNeeded,\n} from './initHelpers.js'\nimport {resolvePackageManager} from './resolvePackageManager.js'\nimport {\n sanityCliTemplate,\n sanityConfigTemplate,\n sanityFolder,\n sanityStudioTemplate,\n} from './templates/nextjs/index.js'\nimport {type InitOptions, type VersionedFramework} from './types.js'\n\nconst debug = subdebug('init')\n\nasync function writeOrOverwrite(\n filePath: string,\n content: string,\n workDir: string,\n options: InitOptions,\n) {\n if (existsSync(filePath)) {\n let overwrite = flagOrDefault(options.overwriteFiles, false)\n if (shouldPrompt(options.unattended, options.overwriteFiles)) {\n overwrite = await confirm({\n default: false,\n message: `File ${styleText(\n 'yellow',\n filePath.replace(workDir, ''),\n )} already exists. Do you want to overwrite it?`,\n })\n }\n\n if (!overwrite) {\n return\n }\n }\n\n // make folder if not exists\n const folderPath = path.dirname(filePath)\n\n try {\n await mkdir(folderPath, {recursive: true})\n } catch {\n debug('Error creating folder %s', folderPath)\n }\n\n await writeFile(filePath, content, {\n encoding: 'utf8',\n })\n}\n\n// write sanity folder files\nasync function writeSourceFiles({\n fileExtension,\n files,\n folderPath,\n options,\n srcFolderPrefix,\n workDir,\n}: {\n fileExtension: string\n files: Record<string, Record<string, string> | string>\n folderPath?: string\n options: InitOptions\n srcFolderPrefix?: boolean\n workDir: string\n}) {\n for (const [filePath, content] of Object.entries(files)) {\n // check if file ends with full stop to indicate it's file and not directory (this only works with our template tree structure)\n if (filePath.includes('.') && typeof content === 'string') {\n await writeOrOverwrite(\n path.join(\n workDir,\n srcFolderPrefix ? 'src' : '',\n 'sanity',\n folderPath || '',\n `${filePath}${fileExtension}`,\n ),\n content,\n workDir,\n options,\n )\n } else {\n await mkdir(path.join(workDir, srcFolderPrefix ? 'src' : '', 'sanity', filePath), {\n recursive: true,\n })\n if (typeof content === 'object') {\n await writeSourceFiles({\n fileExtension,\n files: content,\n folderPath: filePath,\n options,\n srcFolderPrefix,\n workDir,\n })\n }\n }\n }\n}\n\nexport async function initNextJs({\n datasetName,\n detectedFramework,\n envFilename,\n mcpConfigured,\n options,\n output,\n projectId,\n trace,\n workDir,\n}: {\n datasetName: string\n detectedFramework: VersionedFramework | null\n envFilename: string\n mcpConfigured: EditorName[]\n options: InitOptions\n output: Output\n projectId: string\n trace: TelemetryTrace<TelemetryUserProperties, InitStepResult>\n workDir: string\n}): Promise<void> {\n const {nextjsAppendEnv, nextjsEmbedStudio, packageManager, template, typescript, unattended} =\n options\n let useTypeScript = flagOrDefault(typescript, true)\n if (shouldPrompt(unattended, typescript)) {\n useTypeScript = await promptForTypeScript()\n }\n trace.log({\n selectedOption: useTypeScript ? 'yes' : 'no',\n step: 'useTypeScript',\n })\n\n const fileExtension = useTypeScript ? 'ts' : 'js'\n let embeddedStudio = flagOrDefault(nextjsEmbedStudio, true)\n if (shouldPrompt(unattended, nextjsEmbedStudio)) {\n embeddedStudio = await promptForEmbeddedStudio()\n }\n let hasSrcFolder = false\n\n if (embeddedStudio) {\n // find source path (app or src/app)\n const appDir = 'app'\n let srcPath = path.join(workDir, appDir)\n\n if (!existsSync(srcPath)) {\n srcPath = path.join(workDir, 'src', appDir)\n hasSrcFolder = true\n if (!existsSync(srcPath)) {\n try {\n await mkdir(srcPath, {recursive: true})\n } catch {\n debug('Error creating folder %s', srcPath)\n }\n }\n }\n\n const studioPath = unattended ? '/studio' : await promptForStudioPath()\n\n const embeddedStudioRouteFilePath = path.join(\n srcPath,\n `${studioPath}/`,\n `[[...tool]]/page.${fileExtension}x`,\n )\n\n // this selects the correct template string based on whether the user is using the app or pages directory and\n // replaces the \":configPath:\" placeholder in the template with the correct path to the sanity.config.ts file.\n // we account for the user-defined embeddedStudioPath (default /studio) is accounted for by creating enough \"../\"\n // relative paths to reach the root level of the project\n await writeOrOverwrite(\n embeddedStudioRouteFilePath,\n sanityStudioTemplate.replace(\n ':configPath:',\n `${'../'.repeat(countNestedFolders(path.dirname(embeddedStudioRouteFilePath.slice(workDir.length))))}sanity.config`,\n ),\n workDir,\n options,\n )\n\n const sanityConfigPath = path.join(workDir, `sanity.config.${fileExtension}`)\n await writeOrOverwrite(\n sanityConfigPath,\n sanityConfigTemplate(hasSrcFolder)\n .replace(':route:', embeddedStudioRouteFilePath.slice(workDir.length).replace('src/', ''))\n .replace(':basePath:', studioPath),\n workDir,\n options,\n )\n }\n\n const sanityCliPath = path.join(workDir, `sanity.cli.${fileExtension}`)\n await writeOrOverwrite(sanityCliPath, sanityCliTemplate, workDir, options)\n\n let templateToUse = template ?? 'clean'\n if (shouldPrompt(unattended, template)) {\n templateToUse = await promptForNextTemplate()\n }\n\n await writeSourceFiles({\n fileExtension,\n files: sanityFolder(useTypeScript, templateToUse as 'blog' | 'clean'),\n folderPath: undefined,\n options,\n srcFolderPrefix: hasSrcFolder,\n workDir,\n })\n\n let appendEnv = flagOrDefault(nextjsAppendEnv, true)\n if (shouldPrompt(unattended, nextjsAppendEnv)) {\n appendEnv = await promptForAppendEnv(envFilename)\n }\n\n if (appendEnv) {\n await createOrAppendEnvVars({\n envVars: {\n DATASET: datasetName,\n PROJECT_ID: projectId,\n },\n filename: envFilename,\n framework: detectedFramework,\n log: true,\n output,\n outputPath: workDir,\n })\n }\n\n if (embeddedStudio) {\n const nextjsLocalDevOrigin = 'http://localhost:3000'\n const existingCorsOrigins = await listCorsOrigins(projectId)\n const hasExistingCorsOrigin = existingCorsOrigins.some(\n (item: {origin: string}) => item.origin === nextjsLocalDevOrigin,\n )\n if (!hasExistingCorsOrigin) {\n try {\n const createCorsRes = await createCorsOrigin({\n allowCredentials: true,\n origin: nextjsLocalDevOrigin,\n projectId,\n })\n\n output.log(\n createCorsRes.id\n ? `Added ${nextjsLocalDevOrigin} to CORS origins`\n : `Failed to add ${nextjsLocalDevOrigin} to CORS origins`,\n )\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n debug(`Error creating new CORS Origin ${nextjsLocalDevOrigin}: ${message}`)\n throw new InitError(`Failed to add ${nextjsLocalDevOrigin} to CORS origins: ${message}`, 1)\n }\n }\n }\n\n const chosen = await resolvePackageManager({\n interactive: !unattended,\n output,\n packageManager: packageManager as PackageManager,\n targetDir: workDir,\n })\n trace.log({selectedOption: chosen, step: 'selectPackageManager'})\n const packages = ['@sanity/vision@5', 'sanity@5', '@sanity/image-url@2', 'styled-components@6']\n if (templateToUse === 'blog') {\n packages.push('@sanity/icons')\n }\n await installNewPackages(\n {\n packageManager: chosen,\n packages,\n },\n {\n output,\n workDir,\n },\n )\n\n // will refactor this later\n const execOptions: Options = {\n cwd: workDir,\n encoding: 'utf8',\n env: getPartialEnvWithNpmPath(workDir),\n stdio: 'inherit',\n }\n\n switch (chosen) {\n case 'npm': {\n await execa('npm', ['install', 'next-sanity@13'], execOptions)\n break\n }\n case 'pnpm': {\n await execa('pnpm', ['install', 'next-sanity@13'], execOptions)\n break\n }\n case 'yarn': {\n const peerDeps = await getPeerDependencies('next-sanity@13', workDir)\n await installNewPackages(\n {packageManager: 'yarn', packages: ['next-sanity@13', ...peerDeps]},\n {output, workDir},\n )\n break\n }\n default: {\n // bun and manual - do nothing or handle differently\n break\n }\n }\n\n output.log(\n `\\n${styleText('green', 'Success!')} Your Sanity configuration files has been added to this project`,\n )\n if (mcpConfigured && mcpConfigured.length > 0) {\n const message = await getPostInitMCPPrompt(mcpConfigured)\n output.log(`\\n${message}`)\n output.log(`\\nLearn more: ${styleText('cyan', 'https://mcp.sanity.io')}`)\n output.log(\n `\\nHave feedback? Tell us in the community: ${styleText('cyan', 'https://www.sanity.io/community/join')}`,\n )\n }\n\n await writeStagingEnvIfNeeded(output, workDir)\n}\n"],"names":["existsSync","mkdir","writeFile","path","styleText","subdebug","confirm","execa","promptForAppendEnv","promptForEmbeddedStudio","promptForNextTemplate","promptForStudioPath","promptForTypeScript","createCorsOrigin","listCorsOrigins","getPeerDependencies","installNewPackages","getPartialEnvWithNpmPath","countNestedFolders","createOrAppendEnvVars","InitError","flagOrDefault","getPostInitMCPPrompt","shouldPrompt","writeStagingEnvIfNeeded","resolvePackageManager","sanityCliTemplate","sanityConfigTemplate","sanityFolder","sanityStudioTemplate","debug","writeOrOverwrite","filePath","content","workDir","options","overwrite","overwriteFiles","unattended","default","message","replace","folderPath","dirname","recursive","encoding","writeSourceFiles","fileExtension","files","srcFolderPrefix","Object","entries","includes","join","initNextJs","datasetName","detectedFramework","envFilename","mcpConfigured","output","projectId","trace","nextjsAppendEnv","nextjsEmbedStudio","packageManager","template","typescript","useTypeScript","log","selectedOption","step","embeddedStudio","hasSrcFolder","appDir","srcPath","studioPath","embeddedStudioRouteFilePath","repeat","slice","length","sanityConfigPath","sanityCliPath","templateToUse","undefined","appendEnv","envVars","DATASET","PROJECT_ID","filename","framework","outputPath","nextjsLocalDevOrigin","existingCorsOrigins","hasExistingCorsOrigin","some","item","origin","createCorsRes","allowCredentials","id","error","Error","String","chosen","interactive","targetDir","packages","push","execOptions","cwd","env","stdio","peerDeps"],"mappings":"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"}
@@ -2,7 +2,7 @@ import { checkbox } from '@sanity/cli-core/ux';
2
2
  function getEditorLabel(choice) {
3
3
  const { action, editor } = choice;
4
4
  if (action === 'skill-only') {
5
- return `${editor.name} (skill only — MCP already configured)`;
5
+ return `${editor.name} (skills only — MCP already configured)`;
6
6
  }
7
7
  if (editor.configured && editor.authStatus === 'unauthorized') {
8
8
  return `${editor.name} (auth expired)`;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/mcp/promptForMCPSetup.ts"],"sourcesContent":["import {checkbox} from '@sanity/cli-core/ux'\n\nimport {type Editor} from './types.js'\n\n/** Action to take for an editor in the combined MCP + skills setup prompt. */\nexport type EditorAction = 'mcp-and-skill' | 'mcp-only' | 'skill-only'\n\nexport interface EditorChoice {\n action: EditorAction\n editor: Editor\n}\n\nfunction getEditorLabel(choice: EditorChoice): string {\n const {action, editor} = choice\n if (action === 'skill-only') {\n return `${editor.name} (skill only — MCP already configured)`\n }\n if (editor.configured && editor.authStatus === 'unauthorized') {\n return `${editor.name} (auth expired)`\n }\n if (editor.configured && !editor.existingToken) {\n return `${editor.name} (missing credentials)`\n }\n return editor.name\n}\n\ninterface PromptOptions {\n choices: EditorChoice[]\n message: string\n}\n\n/**\n * Prompt the user to select editors for MCP / skills setup. The caller is\n * responsible for classifying editors into actions (see `EditorAction`) and\n * for choosing an appropriate prompt message.\n *\n * Returns the subset of `choices` the user kept, or `null` when the user\n * deselected everything.\n */\nexport async function promptForMCPSetup({\n choices,\n message,\n}: PromptOptions): Promise<EditorChoice[] | null> {\n const editorChoices = choices.map((choice) => ({\n checked: true,\n name: getEditorLabel(choice),\n value: choice.editor.name,\n }))\n\n const selectedNames = await checkbox({\n choices: editorChoices,\n message,\n })\n\n if (!selectedNames || selectedNames.length === 0) {\n return null\n }\n\n return choices.filter((c) => selectedNames.includes(c.editor.name))\n}\n"],"names":["checkbox","getEditorLabel","choice","action","editor","name","configured","authStatus","existingToken","promptForMCPSetup","choices","message","editorChoices","map","checked","value","selectedNames","length","filter","c","includes"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,sBAAqB;AAY5C,SAASC,eAAeC,MAAoB;IAC1C,MAAM,EAACC,MAAM,EAAEC,MAAM,EAAC,GAAGF;IACzB,IAAIC,WAAW,cAAc;QAC3B,OAAO,GAAGC,OAAOC,IAAI,CAAC,sCAAsC,CAAC;IAC/D;IACA,IAAID,OAAOE,UAAU,IAAIF,OAAOG,UAAU,KAAK,gBAAgB;QAC7D,OAAO,GAAGH,OAAOC,IAAI,CAAC,eAAe,CAAC;IACxC;IACA,IAAID,OAAOE,UAAU,IAAI,CAACF,OAAOI,aAAa,EAAE;QAC9C,OAAO,GAAGJ,OAAOC,IAAI,CAAC,sBAAsB,CAAC;IAC/C;IACA,OAAOD,OAAOC,IAAI;AACpB;AAOA;;;;;;;CAOC,GACD,OAAO,eAAeI,kBAAkB,EACtCC,OAAO,EACPC,OAAO,EACO;IACd,MAAMC,gBAAgBF,QAAQG,GAAG,CAAC,CAACX,SAAY,CAAA;YAC7CY,SAAS;YACTT,MAAMJ,eAAeC;YACrBa,OAAOb,OAAOE,MAAM,CAACC,IAAI;QAC3B,CAAA;IAEA,MAAMW,gBAAgB,MAAMhB,SAAS;QACnCU,SAASE;QACTD;IACF;IAEA,IAAI,CAACK,iBAAiBA,cAAcC,MAAM,KAAK,GAAG;QAChD,OAAO;IACT;IAEA,OAAOP,QAAQQ,MAAM,CAAC,CAACC,IAAMH,cAAcI,QAAQ,CAACD,EAAEf,MAAM,CAACC,IAAI;AACnE"}
1
+ {"version":3,"sources":["../../../src/actions/mcp/promptForMCPSetup.ts"],"sourcesContent":["import {checkbox} from '@sanity/cli-core/ux'\n\nimport {type Editor} from './types.js'\n\n/** Action to take for an editor in the combined MCP + skills setup prompt. */\nexport type EditorAction = 'mcp-and-skill' | 'mcp-only' | 'skill-only'\n\nexport interface EditorChoice {\n action: EditorAction\n editor: Editor\n}\n\nfunction getEditorLabel(choice: EditorChoice): string {\n const {action, editor} = choice\n if (action === 'skill-only') {\n return `${editor.name} (skills only — MCP already configured)`\n }\n if (editor.configured && editor.authStatus === 'unauthorized') {\n return `${editor.name} (auth expired)`\n }\n if (editor.configured && !editor.existingToken) {\n return `${editor.name} (missing credentials)`\n }\n return editor.name\n}\n\ninterface PromptOptions {\n choices: EditorChoice[]\n message: string\n}\n\n/**\n * Prompt the user to select editors for MCP / skills setup. The caller is\n * responsible for classifying editors into actions (see `EditorAction`) and\n * for choosing an appropriate prompt message.\n *\n * Returns the subset of `choices` the user kept, or `null` when the user\n * deselected everything.\n */\nexport async function promptForMCPSetup({\n choices,\n message,\n}: PromptOptions): Promise<EditorChoice[] | null> {\n const editorChoices = choices.map((choice) => ({\n checked: true,\n name: getEditorLabel(choice),\n value: choice.editor.name,\n }))\n\n const selectedNames = await checkbox({\n choices: editorChoices,\n message,\n })\n\n if (!selectedNames || selectedNames.length === 0) {\n return null\n }\n\n return choices.filter((c) => selectedNames.includes(c.editor.name))\n}\n"],"names":["checkbox","getEditorLabel","choice","action","editor","name","configured","authStatus","existingToken","promptForMCPSetup","choices","message","editorChoices","map","checked","value","selectedNames","length","filter","c","includes"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,sBAAqB;AAY5C,SAASC,eAAeC,MAAoB;IAC1C,MAAM,EAACC,MAAM,EAAEC,MAAM,EAAC,GAAGF;IACzB,IAAIC,WAAW,cAAc;QAC3B,OAAO,GAAGC,OAAOC,IAAI,CAAC,uCAAuC,CAAC;IAChE;IACA,IAAID,OAAOE,UAAU,IAAIF,OAAOG,UAAU,KAAK,gBAAgB;QAC7D,OAAO,GAAGH,OAAOC,IAAI,CAAC,eAAe,CAAC;IACxC;IACA,IAAID,OAAOE,UAAU,IAAI,CAACF,OAAOI,aAAa,EAAE;QAC9C,OAAO,GAAGJ,OAAOC,IAAI,CAAC,sBAAsB,CAAC;IAC/C;IACA,OAAOD,OAAOC,IAAI;AACpB;AAOA;;;;;;;CAOC,GACD,OAAO,eAAeI,kBAAkB,EACtCC,OAAO,EACPC,OAAO,EACO;IACd,MAAMC,gBAAgBF,QAAQG,GAAG,CAAC,CAACX,SAAY,CAAA;YAC7CY,SAAS;YACTT,MAAMJ,eAAeC;YACrBa,OAAOb,OAAOE,MAAM,CAACC,IAAI;QAC3B,CAAA;IAEA,MAAMW,gBAAgB,MAAMhB,SAAS;QACnCU,SAASE;QACTD;IACF;IAEA,IAAI,CAACK,iBAAiBA,cAAcC,MAAM,KAAK,GAAG;QAChD,OAAO;IACT;IAEA,OAAOP,QAAQQ,MAAM,CAAC,CAACC,IAAMH,cAAcI,QAAQ,CAACD,EAAEf,MAAM,CAACC,IAAI;AACnE"}
@@ -3,7 +3,7 @@ import { subdebug } from '@sanity/cli-core';
3
3
  import { logSymbols } from '@sanity/cli-core/ux';
4
4
  import { createMCPToken, MCP_SERVER_URL } from '../../services/mcp.js';
5
5
  import { readSkillState } from '../skills/readSkillState.js';
6
- import { SANITY_SKILL_NAME } from '../skills/setupSkills.js';
6
+ import { SANITY_SKILL_NAMES } from '../skills/setupSkills.js';
7
7
  import { detectAvailableEditors } from './detectAvailableEditors.js';
8
8
  import { EDITOR_CONFIGS, getSkillsCliAgent, getSkillsCliAgentDisplayName } from './editorConfigs.js';
9
9
  import { promptForMCPSetup } from './promptForMCPSetup.js';
@@ -13,7 +13,7 @@ const mcpDebug = subdebug('mcp:setup');
13
13
  const NO_EDITORS_DETECTED_MESSAGE = `Couldn't auto-configure Sanity MCP server for your editor. Visit ${MCP_SERVER_URL} for setup instructions.`;
14
14
  /**
15
15
  * Classify each editor into one of four actions based on MCP status and
16
- * whether a Sanity skill is already installed for its skills-CLI agent.
16
+ * whether the Sanity skills are already installed for its skills-CLI agent.
17
17
  */ function classifyEditors(editors) {
18
18
  return editors.map((editor)=>{
19
19
  const needsMCP = !editor.configured || editor.authStatus !== 'valid';
@@ -89,12 +89,12 @@ const NO_EDITORS_DETECTED_MESSAGE = `Couldn't auto-configure Sanity MCP server f
89
89
  function getPromptMessage(mcpMode, skillsMode) {
90
90
  if (mcpMode === 'skip') return 'Install Sanity agent skills for these editors?';
91
91
  if (skillsMode === 'skip') return 'Configure Sanity MCP server?';
92
- return 'Configure Sanity MCP and install agent skills for these editors?';
92
+ return 'Configure Sanity MCP and agent skills for these editors?';
93
93
  }
94
94
  /**
95
95
  * Main MCP setup orchestration.
96
96
  *
97
- * When `skillsMode !== 'skip'`, the prompt combines MCP and skill offers,
97
+ * When `skillsMode !== 'skip'`, the prompt combines MCP and skills offers,
98
98
  * and the result includes `skillsToInstall` — agent IDs the caller should
99
99
  * install via `setupSkills`. `setupMCP` itself never installs skills.
100
100
  */ export async function setupMCP(options) {
@@ -131,7 +131,7 @@ function getPromptMessage(mcpMode, skillsMode) {
131
131
  // 4. Read skill state when skills are in scope so classification can dedup
132
132
  if (skillsMode !== 'skip') {
133
133
  const { installedAgentDisplayNames } = await readSkillState({
134
- skillName: SANITY_SKILL_NAME
134
+ skillNames: SANITY_SKILL_NAMES
135
135
  });
136
136
  for (const editor of editors){
137
137
  const displayName = getSkillsCliAgentDisplayName(editor.name);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/mcp/setupMCP.ts"],"sourcesContent":["import {ux} from '@oclif/core'\nimport {subdebug} from '@sanity/cli-core'\nimport {logSymbols} from '@sanity/cli-core/ux'\n\nimport {createMCPToken, MCP_SERVER_URL} from '../../services/mcp.js'\nimport {readSkillState} from '../skills/readSkillState.js'\nimport {SANITY_SKILL_NAME} from '../skills/setupSkills.js'\nimport {detectAvailableEditors} from './detectAvailableEditors.js'\nimport {\n EDITOR_CONFIGS,\n type EditorName,\n getSkillsCliAgent,\n getSkillsCliAgentDisplayName,\n} from './editorConfigs.js'\nimport {type EditorAction, type EditorChoice, promptForMCPSetup} from './promptForMCPSetup.js'\nimport {type Editor} from './types.js'\nimport {validateEditorTokens} from './validateEditorTokens.js'\nimport {writeMCPConfig} from './writeMCPConfig.js'\n\nconst mcpDebug = subdebug('mcp:setup')\n\nconst NO_EDITORS_DETECTED_MESSAGE = `Couldn't auto-configure Sanity MCP server for your editor. Visit ${MCP_SERVER_URL} for setup instructions.`\n\ntype Mode = 'auto' | 'prompt' | 'skip'\n\ninterface MCPSetupOptions {\n /**\n * Pre-detected editors. When omitted, `detectAvailableEditors()` is called.\n * Accepting this from the caller avoids re-running detection (which probes\n * the filesystem and shells out to CLI binaries) when the result is already\n * available — e.g. when `sanity init` runs both MCP and skills setup.\n */\n editors?: Editor[]\n\n /**\n * Whether the user explicitly requested MCP configuration (e.g. `sanity mcp configure`).\n * When true, shows status messages even when there's nothing to do.\n * When false/undefined (e.g. called from `sanity init`), stays quiet.\n */\n explicit?: boolean\n\n /**\n * Controls how MCP setup behaves:\n * - 'prompt': Ask the user which editors to configure (default)\n * - 'auto': Auto-configure all detected editors without prompting\n * - 'skip': Skip MCP configuration entirely\n */\n mode?: Mode\n\n /**\n * Controls whether skills install is also offered in the same prompt:\n * - 'prompt': Combine MCP + skills offers in one checkbox\n * - 'auto': Combine, but skip the prompt and select everything\n * - 'skip' (default): Skip skills entirely — today's MCP-only behavior\n */\n skillsMode?: Mode\n}\n\ninterface MCPSetupResult {\n /** Editors that were already configured with valid credentials (nothing to do) */\n alreadyConfiguredEditors: EditorName[]\n configuredEditors: EditorName[]\n detectedEditors: EditorName[]\n /** Skills-CLI agent IDs that the caller should install. Deduplicated. */\n skillsToInstall: string[]\n skipped: boolean\n\n error?: Error\n}\n\ninterface ClassifiedEditor {\n action: 'none' | EditorAction\n editor: Editor\n}\n\n/**\n * Classify each editor into one of four actions based on MCP status and\n * whether a Sanity skill is already installed for its skills-CLI agent.\n */\nfunction classifyEditors(editors: Editor[]): ClassifiedEditor[] {\n return editors.map((editor) => {\n const needsMCP = !editor.configured || editor.authStatus !== 'valid'\n const skillsCliAgent = getSkillsCliAgent(editor.name)\n const hasSkillMapping = Boolean(skillsCliAgent)\n const skillInstalled = editor.skillInstalled === true\n\n if (needsMCP) {\n return {action: hasSkillMapping ? 'mcp-and-skill' : 'mcp-only', editor}\n }\n if (hasSkillMapping && !skillInstalled) {\n return {action: 'skill-only', editor}\n }\n return {action: 'none', editor}\n })\n}\n\n/**\n * Apply masking based on the configured modes. `skip` modes mute the\n * corresponding action so we never prompt for or run work the user opted out\n * of via `--no-mcp` / `--no-skills`.\n */\nfunction applyMasking(\n classified: ClassifiedEditor[],\n mcpMode: Mode,\n skillsMode: Mode,\n): EditorChoice[] {\n const actionable: EditorChoice[] = []\n\n for (const {action, editor} of classified) {\n if (action === 'none') continue\n\n if (mcpMode === 'skip' && skillsMode === 'skip') continue\n\n if (mcpMode === 'skip') {\n // No MCP writes — keep only skill-only / mcp-and-skill (downgraded to skill-only)\n if (action === 'mcp-only') continue\n if (action === 'mcp-and-skill') {\n actionable.push({action: 'skill-only', editor})\n continue\n }\n actionable.push({action, editor})\n continue\n }\n\n if (skillsMode === 'skip') {\n // No skill install — drop skill-only, downgrade mcp-and-skill → mcp-only\n if (action === 'skill-only') continue\n if (action === 'mcp-and-skill') {\n actionable.push({action: 'mcp-only', editor})\n continue\n }\n actionable.push({action, editor})\n continue\n }\n\n actionable.push({action, editor})\n }\n\n return actionable\n}\n\nfunction getPromptMessage(mcpMode: Mode, skillsMode: Mode): string {\n if (mcpMode === 'skip') return 'Install Sanity agent skills for these editors?'\n if (skillsMode === 'skip') return 'Configure Sanity MCP server?'\n return 'Configure Sanity MCP and install agent skills for these editors?'\n}\n\n/**\n * Main MCP setup orchestration.\n *\n * When `skillsMode !== 'skip'`, the prompt combines MCP and skill offers,\n * and the result includes `skillsToInstall` — agent IDs the caller should\n * install via `setupSkills`. `setupMCP` itself never installs skills.\n */\nexport async function setupMCP(options?: MCPSetupOptions): Promise<MCPSetupResult> {\n const {explicit = false, mode: mcpMode = 'prompt', skillsMode = 'skip'} = options ?? {}\n\n // 1. Both opted out → nothing to do.\n if (mcpMode === 'skip' && skillsMode === 'skip') {\n mcpDebug('Skipping setup (mcpMode: skip, skillsMode: skip)')\n return {\n alreadyConfiguredEditors: [],\n configuredEditors: [],\n detectedEditors: [],\n skillsToInstall: [],\n skipped: true,\n }\n }\n\n // 2. Detect available editors (filters out unparseable configs)\n const editors = options?.editors ?? (await detectAvailableEditors())\n const detectedEditors = editors.map((e) => e.name)\n\n mcpDebug('Detected %d editors: %s', detectedEditors.length, detectedEditors)\n\n if (editors.length === 0) {\n if (explicit) {\n ux.warn(NO_EDITORS_DETECTED_MESSAGE)\n }\n return {\n alreadyConfiguredEditors: [],\n configuredEditors: [],\n detectedEditors,\n skillsToInstall: [],\n skipped: true,\n }\n }\n\n // 3. Validate existing tokens against the Sanity API\n await validateEditorTokens(editors)\n\n // 4. Read skill state when skills are in scope so classification can dedup\n if (skillsMode !== 'skip') {\n const {installedAgentDisplayNames} = await readSkillState({skillName: SANITY_SKILL_NAME})\n for (const editor of editors) {\n const displayName = getSkillsCliAgentDisplayName(editor.name)\n editor.skillInstalled = displayName ? installedAgentDisplayNames.has(displayName) : false\n }\n }\n\n // 5. Classify + mask\n const classified = classifyEditors(editors)\n const actionable = applyMasking(classified, mcpMode, skillsMode)\n\n // \"Already configured\" surfaces editors whose MCP setup is valid (skill\n // state doesn't matter for this signal — that's what skillsToInstall is for).\n const actionableNames = new Set(actionable.map((c) => c.editor.name))\n const alreadyConfiguredEditors = editors\n .filter((e) => e.configured && e.authStatus === 'valid' && !actionableNames.has(e.name))\n .map((e) => e.name)\n\n if (actionable.length === 0) {\n mcpDebug('Nothing actionable after classification + masking')\n if (explicit) {\n ux.stdout(`${logSymbols.success} All detected editors are already configured`)\n }\n return {\n alreadyConfiguredEditors,\n configuredEditors: [],\n detectedEditors,\n skillsToInstall: [],\n skipped: true,\n }\n }\n\n // 6. Select editors to configure — prompt interactively or auto-select all.\n // We only auto when neither side wants a prompt: MCP auto, or (MCP skip\n // + skills auto). Anything that asks `mode: 'prompt'` for MCP wins the\n // prompt even when skills would have auto-installed.\n const shouldAuto = mcpMode === 'auto' || (mcpMode === 'skip' && skillsMode === 'auto')\n const selected = shouldAuto\n ? actionable\n : await promptForMCPSetup({\n choices: actionable,\n message: getPromptMessage(mcpMode, skillsMode),\n })\n\n if (!selected || selected.length === 0) {\n ux.stdout('MCP configuration skipped')\n return {\n alreadyConfiguredEditors,\n configuredEditors: [],\n detectedEditors,\n skillsToInstall: [],\n skipped: true,\n }\n }\n\n // 7. MCP write phase — only for choices that need MCP\n const mcpSelected = selected.filter(\n (c) => c.action === 'mcp-only' || c.action === 'mcp-and-skill',\n )\n\n let token: string | undefined\n const configuredEditors: EditorName[] = []\n let mcpError: Error | undefined\n\n if (mcpSelected.length > 0) {\n const validEditor = editors.find((e) => e.authStatus === 'valid' && e.existingToken)\n if (validEditor?.existingToken) {\n mcpDebug('Reusing valid token from %s', validEditor.name)\n token = validEditor.existingToken\n }\n\n const allOAuth = mcpSelected.every((c) => EDITOR_CONFIGS[c.editor.name].oauthOnly)\n\n if (!token && !allOAuth) {\n try {\n token = await createMCPToken()\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n mcpDebug('Error creating MCP token', error)\n ux.warn(`Could not configure MCP: ${err.message}`)\n ux.warn('You can set up MCP manually later using https://mcp.sanity.io')\n mcpError = err\n }\n }\n\n if (!mcpError) {\n for (const choice of mcpSelected) {\n try {\n await writeMCPConfig(choice.editor, token)\n configuredEditors.push(choice.editor.name)\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n mcpDebug('Error writing MCP config for %s: %O', choice.editor.name, error)\n ux.warn(`Could not configure MCP for ${choice.editor.name}: ${err.message}`)\n ux.warn('You can set up MCP manually later using https://mcp.sanity.io')\n mcpError = err\n }\n }\n }\n\n if (configuredEditors.length > 0) {\n ux.stdout(`${logSymbols.success} MCP configured for ${configuredEditors.join(', ')}`)\n }\n }\n\n // 8. Build skillsToInstall — only for choices the user kept, only when the\n // associated MCP write succeeded (or wasn't needed).\n const skillsToInstall: string[] = []\n if (skillsMode !== 'skip') {\n for (const choice of selected) {\n if (choice.action === 'skill-only') {\n const agent = getSkillsCliAgent(choice.editor.name)\n if (agent) skillsToInstall.push(agent)\n continue\n }\n if (choice.action === 'mcp-and-skill' && configuredEditors.includes(choice.editor.name)) {\n const agent = getSkillsCliAgent(choice.editor.name)\n if (agent) skillsToInstall.push(agent)\n }\n }\n }\n\n return {\n alreadyConfiguredEditors,\n configuredEditors,\n detectedEditors,\n error: mcpError,\n skillsToInstall: [...new Set(skillsToInstall)],\n skipped: false,\n }\n}\n"],"names":["ux","subdebug","logSymbols","createMCPToken","MCP_SERVER_URL","readSkillState","SANITY_SKILL_NAME","detectAvailableEditors","EDITOR_CONFIGS","getSkillsCliAgent","getSkillsCliAgentDisplayName","promptForMCPSetup","validateEditorTokens","writeMCPConfig","mcpDebug","NO_EDITORS_DETECTED_MESSAGE","classifyEditors","editors","map","editor","needsMCP","configured","authStatus","skillsCliAgent","name","hasSkillMapping","Boolean","skillInstalled","action","applyMasking","classified","mcpMode","skillsMode","actionable","push","getPromptMessage","setupMCP","options","explicit","mode","alreadyConfiguredEditors","configuredEditors","detectedEditors","skillsToInstall","skipped","e","length","warn","installedAgentDisplayNames","skillName","displayName","has","actionableNames","Set","c","filter","stdout","success","shouldAuto","selected","choices","message","mcpSelected","token","mcpError","validEditor","find","existingToken","allOAuth","every","oauthOnly","error","err","Error","String","choice","join","agent","includes"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/actions/mcp/setupMCP.ts"],"sourcesContent":["import {ux} from '@oclif/core'\nimport {subdebug} from '@sanity/cli-core'\nimport {logSymbols} from '@sanity/cli-core/ux'\n\nimport {createMCPToken, MCP_SERVER_URL} from '../../services/mcp.js'\nimport {readSkillState} from '../skills/readSkillState.js'\nimport {SANITY_SKILL_NAMES} from '../skills/setupSkills.js'\nimport {detectAvailableEditors} from './detectAvailableEditors.js'\nimport {\n EDITOR_CONFIGS,\n type EditorName,\n getSkillsCliAgent,\n getSkillsCliAgentDisplayName,\n} from './editorConfigs.js'\nimport {type EditorAction, type EditorChoice, promptForMCPSetup} from './promptForMCPSetup.js'\nimport {type Editor} from './types.js'\nimport {validateEditorTokens} from './validateEditorTokens.js'\nimport {writeMCPConfig} from './writeMCPConfig.js'\n\nconst mcpDebug = subdebug('mcp:setup')\n\nconst NO_EDITORS_DETECTED_MESSAGE = `Couldn't auto-configure Sanity MCP server for your editor. Visit ${MCP_SERVER_URL} for setup instructions.`\n\ntype Mode = 'auto' | 'prompt' | 'skip'\n\ninterface MCPSetupOptions {\n /**\n * Pre-detected editors. When omitted, `detectAvailableEditors()` is called.\n * Accepting this from the caller avoids re-running detection (which probes\n * the filesystem and shells out to CLI binaries) when the result is already\n * available — e.g. when `sanity init` runs both MCP and skills setup.\n */\n editors?: Editor[]\n\n /**\n * Whether the user explicitly requested MCP configuration (e.g. `sanity mcp configure`).\n * When true, shows status messages even when there's nothing to do.\n * When false/undefined (e.g. called from `sanity init`), stays quiet.\n */\n explicit?: boolean\n\n /**\n * Controls how MCP setup behaves:\n * - 'prompt': Ask the user which editors to configure (default)\n * - 'auto': Auto-configure all detected editors without prompting\n * - 'skip': Skip MCP configuration entirely\n */\n mode?: Mode\n\n /**\n * Controls whether skills install is also offered in the same prompt:\n * - 'prompt': Combine MCP + skills offers in one checkbox\n * - 'auto': Combine, but skip the prompt and select everything\n * - 'skip' (default): Skip skills entirely — today's MCP-only behavior\n */\n skillsMode?: Mode\n}\n\ninterface MCPSetupResult {\n /** Editors that were already configured with valid credentials (nothing to do) */\n alreadyConfiguredEditors: EditorName[]\n configuredEditors: EditorName[]\n detectedEditors: EditorName[]\n /** Skills-CLI agent IDs that the caller should install. Deduplicated. */\n skillsToInstall: string[]\n skipped: boolean\n\n error?: Error\n}\n\ninterface ClassifiedEditor {\n action: 'none' | EditorAction\n editor: Editor\n}\n\n/**\n * Classify each editor into one of four actions based on MCP status and\n * whether the Sanity skills are already installed for its skills-CLI agent.\n */\nfunction classifyEditors(editors: Editor[]): ClassifiedEditor[] {\n return editors.map((editor) => {\n const needsMCP = !editor.configured || editor.authStatus !== 'valid'\n const skillsCliAgent = getSkillsCliAgent(editor.name)\n const hasSkillMapping = Boolean(skillsCliAgent)\n const skillInstalled = editor.skillInstalled === true\n\n if (needsMCP) {\n return {action: hasSkillMapping ? 'mcp-and-skill' : 'mcp-only', editor}\n }\n if (hasSkillMapping && !skillInstalled) {\n return {action: 'skill-only', editor}\n }\n return {action: 'none', editor}\n })\n}\n\n/**\n * Apply masking based on the configured modes. `skip` modes mute the\n * corresponding action so we never prompt for or run work the user opted out\n * of via `--no-mcp` / `--no-skills`.\n */\nfunction applyMasking(\n classified: ClassifiedEditor[],\n mcpMode: Mode,\n skillsMode: Mode,\n): EditorChoice[] {\n const actionable: EditorChoice[] = []\n\n for (const {action, editor} of classified) {\n if (action === 'none') continue\n\n if (mcpMode === 'skip' && skillsMode === 'skip') continue\n\n if (mcpMode === 'skip') {\n // No MCP writes — keep only skill-only / mcp-and-skill (downgraded to skill-only)\n if (action === 'mcp-only') continue\n if (action === 'mcp-and-skill') {\n actionable.push({action: 'skill-only', editor})\n continue\n }\n actionable.push({action, editor})\n continue\n }\n\n if (skillsMode === 'skip') {\n // No skill install — drop skill-only, downgrade mcp-and-skill → mcp-only\n if (action === 'skill-only') continue\n if (action === 'mcp-and-skill') {\n actionable.push({action: 'mcp-only', editor})\n continue\n }\n actionable.push({action, editor})\n continue\n }\n\n actionable.push({action, editor})\n }\n\n return actionable\n}\n\nfunction getPromptMessage(mcpMode: Mode, skillsMode: Mode): string {\n if (mcpMode === 'skip') return 'Install Sanity agent skills for these editors?'\n if (skillsMode === 'skip') return 'Configure Sanity MCP server?'\n return 'Configure Sanity MCP and agent skills for these editors?'\n}\n\n/**\n * Main MCP setup orchestration.\n *\n * When `skillsMode !== 'skip'`, the prompt combines MCP and skills offers,\n * and the result includes `skillsToInstall` — agent IDs the caller should\n * install via `setupSkills`. `setupMCP` itself never installs skills.\n */\nexport async function setupMCP(options?: MCPSetupOptions): Promise<MCPSetupResult> {\n const {explicit = false, mode: mcpMode = 'prompt', skillsMode = 'skip'} = options ?? {}\n\n // 1. Both opted out → nothing to do.\n if (mcpMode === 'skip' && skillsMode === 'skip') {\n mcpDebug('Skipping setup (mcpMode: skip, skillsMode: skip)')\n return {\n alreadyConfiguredEditors: [],\n configuredEditors: [],\n detectedEditors: [],\n skillsToInstall: [],\n skipped: true,\n }\n }\n\n // 2. Detect available editors (filters out unparseable configs)\n const editors = options?.editors ?? (await detectAvailableEditors())\n const detectedEditors = editors.map((e) => e.name)\n\n mcpDebug('Detected %d editors: %s', detectedEditors.length, detectedEditors)\n\n if (editors.length === 0) {\n if (explicit) {\n ux.warn(NO_EDITORS_DETECTED_MESSAGE)\n }\n return {\n alreadyConfiguredEditors: [],\n configuredEditors: [],\n detectedEditors,\n skillsToInstall: [],\n skipped: true,\n }\n }\n\n // 3. Validate existing tokens against the Sanity API\n await validateEditorTokens(editors)\n\n // 4. Read skill state when skills are in scope so classification can dedup\n if (skillsMode !== 'skip') {\n const {installedAgentDisplayNames} = await readSkillState({skillNames: SANITY_SKILL_NAMES})\n for (const editor of editors) {\n const displayName = getSkillsCliAgentDisplayName(editor.name)\n editor.skillInstalled = displayName ? installedAgentDisplayNames.has(displayName) : false\n }\n }\n\n // 5. Classify + mask\n const classified = classifyEditors(editors)\n const actionable = applyMasking(classified, mcpMode, skillsMode)\n\n // \"Already configured\" surfaces editors whose MCP setup is valid (skill\n // state doesn't matter for this signal — that's what skillsToInstall is for).\n const actionableNames = new Set(actionable.map((c) => c.editor.name))\n const alreadyConfiguredEditors = editors\n .filter((e) => e.configured && e.authStatus === 'valid' && !actionableNames.has(e.name))\n .map((e) => e.name)\n\n if (actionable.length === 0) {\n mcpDebug('Nothing actionable after classification + masking')\n if (explicit) {\n ux.stdout(`${logSymbols.success} All detected editors are already configured`)\n }\n return {\n alreadyConfiguredEditors,\n configuredEditors: [],\n detectedEditors,\n skillsToInstall: [],\n skipped: true,\n }\n }\n\n // 6. Select editors to configure — prompt interactively or auto-select all.\n // We only auto when neither side wants a prompt: MCP auto, or (MCP skip\n // + skills auto). Anything that asks `mode: 'prompt'` for MCP wins the\n // prompt even when skills would have auto-installed.\n const shouldAuto = mcpMode === 'auto' || (mcpMode === 'skip' && skillsMode === 'auto')\n const selected = shouldAuto\n ? actionable\n : await promptForMCPSetup({\n choices: actionable,\n message: getPromptMessage(mcpMode, skillsMode),\n })\n\n if (!selected || selected.length === 0) {\n ux.stdout('MCP configuration skipped')\n return {\n alreadyConfiguredEditors,\n configuredEditors: [],\n detectedEditors,\n skillsToInstall: [],\n skipped: true,\n }\n }\n\n // 7. MCP write phase — only for choices that need MCP\n const mcpSelected = selected.filter(\n (c) => c.action === 'mcp-only' || c.action === 'mcp-and-skill',\n )\n\n let token: string | undefined\n const configuredEditors: EditorName[] = []\n let mcpError: Error | undefined\n\n if (mcpSelected.length > 0) {\n const validEditor = editors.find((e) => e.authStatus === 'valid' && e.existingToken)\n if (validEditor?.existingToken) {\n mcpDebug('Reusing valid token from %s', validEditor.name)\n token = validEditor.existingToken\n }\n\n const allOAuth = mcpSelected.every((c) => EDITOR_CONFIGS[c.editor.name].oauthOnly)\n\n if (!token && !allOAuth) {\n try {\n token = await createMCPToken()\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n mcpDebug('Error creating MCP token', error)\n ux.warn(`Could not configure MCP: ${err.message}`)\n ux.warn('You can set up MCP manually later using https://mcp.sanity.io')\n mcpError = err\n }\n }\n\n if (!mcpError) {\n for (const choice of mcpSelected) {\n try {\n await writeMCPConfig(choice.editor, token)\n configuredEditors.push(choice.editor.name)\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n mcpDebug('Error writing MCP config for %s: %O', choice.editor.name, error)\n ux.warn(`Could not configure MCP for ${choice.editor.name}: ${err.message}`)\n ux.warn('You can set up MCP manually later using https://mcp.sanity.io')\n mcpError = err\n }\n }\n }\n\n if (configuredEditors.length > 0) {\n ux.stdout(`${logSymbols.success} MCP configured for ${configuredEditors.join(', ')}`)\n }\n }\n\n // 8. Build skillsToInstall — only for choices the user kept, only when the\n // associated MCP write succeeded (or wasn't needed).\n const skillsToInstall: string[] = []\n if (skillsMode !== 'skip') {\n for (const choice of selected) {\n if (choice.action === 'skill-only') {\n const agent = getSkillsCliAgent(choice.editor.name)\n if (agent) skillsToInstall.push(agent)\n continue\n }\n if (choice.action === 'mcp-and-skill' && configuredEditors.includes(choice.editor.name)) {\n const agent = getSkillsCliAgent(choice.editor.name)\n if (agent) skillsToInstall.push(agent)\n }\n }\n }\n\n return {\n alreadyConfiguredEditors,\n configuredEditors,\n detectedEditors,\n error: mcpError,\n skillsToInstall: [...new Set(skillsToInstall)],\n skipped: false,\n }\n}\n"],"names":["ux","subdebug","logSymbols","createMCPToken","MCP_SERVER_URL","readSkillState","SANITY_SKILL_NAMES","detectAvailableEditors","EDITOR_CONFIGS","getSkillsCliAgent","getSkillsCliAgentDisplayName","promptForMCPSetup","validateEditorTokens","writeMCPConfig","mcpDebug","NO_EDITORS_DETECTED_MESSAGE","classifyEditors","editors","map","editor","needsMCP","configured","authStatus","skillsCliAgent","name","hasSkillMapping","Boolean","skillInstalled","action","applyMasking","classified","mcpMode","skillsMode","actionable","push","getPromptMessage","setupMCP","options","explicit","mode","alreadyConfiguredEditors","configuredEditors","detectedEditors","skillsToInstall","skipped","e","length","warn","installedAgentDisplayNames","skillNames","displayName","has","actionableNames","Set","c","filter","stdout","success","shouldAuto","selected","choices","message","mcpSelected","token","mcpError","validEditor","find","existingToken","allOAuth","every","oauthOnly","error","err","Error","String","choice","join","agent","includes"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/mcp/types.ts"],"sourcesContent":["import {type EditorName} from './editorConfigs.js'\n\n/** Auth credential status for a configured editor */\nexport type AuthStatus = 'unauthorized' | 'valid'\n\nexport interface Editor {\n configPath: string\n /** Whether Sanity MCP is already configured for this editor */\n configured: boolean\n name: EditorName\n\n /**\n * Auth status of the existing token. Only set for editors that have\n * a Sanity MCP config with a token that has been validated against the API.\n */\n authStatus?: AuthStatus\n /** The existing auth token found in the editor config, if any */\n existingToken?: string\n /**\n * Whether the Sanity agent skill is already installed globally for this\n * editor's skills-CLI agent. Populated during setup classification when\n * skill state has been probed; absent when skill installation isn't being\n * considered (e.g. `mcp configure`).\n */\n skillInstalled?: boolean\n}\n"],"names":[],"mappings":"AAKA,WAoBC"}
1
+ {"version":3,"sources":["../../../src/actions/mcp/types.ts"],"sourcesContent":["import {type EditorName} from './editorConfigs.js'\n\n/** Auth credential status for a configured editor */\nexport type AuthStatus = 'unauthorized' | 'valid'\n\nexport interface Editor {\n configPath: string\n /** Whether Sanity MCP is already configured for this editor */\n configured: boolean\n name: EditorName\n\n /**\n * Auth status of the existing token. Only set for editors that have\n * a Sanity MCP config with a token that has been validated against the API.\n */\n authStatus?: AuthStatus\n /** The existing auth token found in the editor config, if any */\n existingToken?: string\n /**\n * Whether the Sanity agent skills are already installed globally for this\n * editor's skills-CLI agent. Populated during setup classification when\n * skill state has been probed; absent when skill installation isn't being\n * considered (e.g. `mcp configure`).\n */\n skillInstalled?: boolean\n}\n"],"names":[],"mappings":"AAKA,WAoBC"}
@@ -4,7 +4,9 @@ import { SKILLS_BIN_PATH } from './setupSkills.js';
4
4
  const debug = subdebug('skills:state');
5
5
  /**
6
6
  * Runs the bundled `skills list -g --json` and returns the set of agent
7
- * display names that have `skillName` installed globally.
7
+ * display names that have every skill in `skillNames` installed globally.
8
+ * Agents missing any of the skills are excluded, so they get a (idempotent)
9
+ * re-install that fills the gap.
8
10
  *
9
11
  * Any failure (spawn, parse, timeout, non-zero exit) is debug-logged and
10
12
  * resolved with an empty set. Callers should treat that as "treat all agents
@@ -41,13 +43,18 @@ const debug = subdebug('skills:state');
41
43
  debug('Unexpected skills list JSON shape (not an array)');
42
44
  return empty;
43
45
  }
44
- const match = parsed.find((entry)=>entry?.name === opts.skillName);
45
- if (!match || !Array.isArray(match.agents)) {
46
- return empty;
47
- }
48
- const displayNames = match.agents.filter((a)=>typeof a === 'string');
46
+ const agentSets = opts.skillNames.map((skillName)=>{
47
+ const match = parsed.find((entry)=>entry?.name === skillName);
48
+ if (!match || !Array.isArray(match.agents)) {
49
+ return new Set();
50
+ }
51
+ return new Set(match.agents.filter((a)=>typeof a === 'string'));
52
+ });
53
+ const installedEverywhere = [
54
+ ...agentSets[0] ?? []
55
+ ].filter((agent)=>agentSets.every((set)=>set.has(agent)));
49
56
  return {
50
- installedAgentDisplayNames: new Set(displayNames)
57
+ installedAgentDisplayNames: new Set(installedEverywhere)
51
58
  };
52
59
  }
53
60
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/skills/readSkillState.ts"],"sourcesContent":["import {subdebug} from '@sanity/cli-core'\nimport {execa} from 'execa'\n\nimport {SKILLS_BIN_PATH} from './setupSkills.js'\n\nconst debug = subdebug('skills:state')\n\ninterface ReadSkillStateOptions {\n /** Name of the skill to look up (matches the `name` field in `skills list --json`). */\n skillName: string\n}\n\ninterface SkillState {\n /** Display names of agents that have this skill installed globally. */\n installedAgentDisplayNames: Set<string>\n}\n\ninterface SkillListEntry {\n agents?: unknown\n name?: unknown\n}\n\n/**\n * Runs the bundled `skills list -g --json` and returns the set of agent\n * display names that have `skillName` installed globally.\n *\n * Any failure (spawn, parse, timeout, non-zero exit) is debug-logged and\n * resolved with an empty set. Callers should treat that as \"treat all agents\n * as not installed\" — re-installing is idempotent, so over-installing is\n * safer than skipping based on a flaky probe.\n */\nexport async function readSkillState(opts: ReadSkillStateOptions): Promise<SkillState> {\n const empty: SkillState = {installedAgentDisplayNames: new Set()}\n\n let stdout: string\n try {\n const result = await execa(process.execPath, [SKILLS_BIN_PATH, 'list', '-g', '--json'], {\n stdio: 'pipe',\n timeout: 10_000,\n })\n stdout = result.stdout\n } catch (error) {\n debug('skills list failed: %O', error)\n return empty\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(stdout)\n } catch (error) {\n debug('Failed to parse skills list JSON: %O', error)\n return empty\n }\n\n if (!Array.isArray(parsed)) {\n debug('Unexpected skills list JSON shape (not an array)')\n return empty\n }\n\n const match = (parsed as SkillListEntry[]).find((entry) => entry?.name === opts.skillName)\n if (!match || !Array.isArray(match.agents)) {\n return empty\n }\n\n const displayNames = match.agents.filter((a): a is string => typeof a === 'string')\n return {installedAgentDisplayNames: new Set(displayNames)}\n}\n"],"names":["subdebug","execa","SKILLS_BIN_PATH","debug","readSkillState","opts","empty","installedAgentDisplayNames","Set","stdout","result","process","execPath","stdio","timeout","error","parsed","JSON","parse","Array","isArray","match","find","entry","name","skillName","agents","displayNames","filter","a"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,mBAAkB;AACzC,SAAQC,KAAK,QAAO,QAAO;AAE3B,SAAQC,eAAe,QAAO,mBAAkB;AAEhD,MAAMC,QAAQH,SAAS;AAiBvB;;;;;;;;CAQC,GACD,OAAO,eAAeI,eAAeC,IAA2B;IAC9D,MAAMC,QAAoB;QAACC,4BAA4B,IAAIC;IAAK;IAEhE,IAAIC;IACJ,IAAI;QACF,MAAMC,SAAS,MAAMT,MAAMU,QAAQC,QAAQ,EAAE;YAACV;YAAiB;YAAQ;YAAM;SAAS,EAAE;YACtFW,OAAO;YACPC,SAAS;QACX;QACAL,SAASC,OAAOD,MAAM;IACxB,EAAE,OAAOM,OAAO;QACdZ,MAAM,0BAA0BY;QAChC,OAAOT;IACT;IAEA,IAAIU;IACJ,IAAI;QACFA,SAASC,KAAKC,KAAK,CAACT;IACtB,EAAE,OAAOM,OAAO;QACdZ,MAAM,wCAAwCY;QAC9C,OAAOT;IACT;IAEA,IAAI,CAACa,MAAMC,OAAO,CAACJ,SAAS;QAC1Bb,MAAM;QACN,OAAOG;IACT;IAEA,MAAMe,QAAQ,AAACL,OAA4BM,IAAI,CAAC,CAACC,QAAUA,OAAOC,SAASnB,KAAKoB,SAAS;IACzF,IAAI,CAACJ,SAAS,CAACF,MAAMC,OAAO,CAACC,MAAMK,MAAM,GAAG;QAC1C,OAAOpB;IACT;IAEA,MAAMqB,eAAeN,MAAMK,MAAM,CAACE,MAAM,CAAC,CAACC,IAAmB,OAAOA,MAAM;IAC1E,OAAO;QAACtB,4BAA4B,IAAIC,IAAImB;IAAa;AAC3D"}
1
+ {"version":3,"sources":["../../../src/actions/skills/readSkillState.ts"],"sourcesContent":["import {subdebug} from '@sanity/cli-core'\nimport {execa} from 'execa'\n\nimport {SKILLS_BIN_PATH} from './setupSkills.js'\n\nconst debug = subdebug('skills:state')\n\ninterface ReadSkillStateOptions {\n /** Names of the skills to look up (match the `name` field in `skills list --json`). */\n skillNames: string[]\n}\n\ninterface SkillState {\n /** Display names of agents that have all of these skills installed globally. */\n installedAgentDisplayNames: Set<string>\n}\n\ninterface SkillListEntry {\n agents?: unknown\n name?: unknown\n}\n\n/**\n * Runs the bundled `skills list -g --json` and returns the set of agent\n * display names that have every skill in `skillNames` installed globally.\n * Agents missing any of the skills are excluded, so they get a (idempotent)\n * re-install that fills the gap.\n *\n * Any failure (spawn, parse, timeout, non-zero exit) is debug-logged and\n * resolved with an empty set. Callers should treat that as \"treat all agents\n * as not installed\" — re-installing is idempotent, so over-installing is\n * safer than skipping based on a flaky probe.\n */\nexport async function readSkillState(opts: ReadSkillStateOptions): Promise<SkillState> {\n const empty: SkillState = {installedAgentDisplayNames: new Set()}\n\n let stdout: string\n try {\n const result = await execa(process.execPath, [SKILLS_BIN_PATH, 'list', '-g', '--json'], {\n stdio: 'pipe',\n timeout: 10_000,\n })\n stdout = result.stdout\n } catch (error) {\n debug('skills list failed: %O', error)\n return empty\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(stdout)\n } catch (error) {\n debug('Failed to parse skills list JSON: %O', error)\n return empty\n }\n\n if (!Array.isArray(parsed)) {\n debug('Unexpected skills list JSON shape (not an array)')\n return empty\n }\n\n const agentSets = opts.skillNames.map((skillName) => {\n const match = (parsed as SkillListEntry[]).find((entry) => entry?.name === skillName)\n if (!match || !Array.isArray(match.agents)) {\n return new Set<string>()\n }\n return new Set(match.agents.filter((a): a is string => typeof a === 'string'))\n })\n\n const installedEverywhere = [...(agentSets[0] ?? [])].filter((agent) =>\n agentSets.every((set) => set.has(agent)),\n )\n return {installedAgentDisplayNames: new Set(installedEverywhere)}\n}\n"],"names":["subdebug","execa","SKILLS_BIN_PATH","debug","readSkillState","opts","empty","installedAgentDisplayNames","Set","stdout","result","process","execPath","stdio","timeout","error","parsed","JSON","parse","Array","isArray","agentSets","skillNames","map","skillName","match","find","entry","name","agents","filter","a","installedEverywhere","agent","every","set","has"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,mBAAkB;AACzC,SAAQC,KAAK,QAAO,QAAO;AAE3B,SAAQC,eAAe,QAAO,mBAAkB;AAEhD,MAAMC,QAAQH,SAAS;AAiBvB;;;;;;;;;;CAUC,GACD,OAAO,eAAeI,eAAeC,IAA2B;IAC9D,MAAMC,QAAoB;QAACC,4BAA4B,IAAIC;IAAK;IAEhE,IAAIC;IACJ,IAAI;QACF,MAAMC,SAAS,MAAMT,MAAMU,QAAQC,QAAQ,EAAE;YAACV;YAAiB;YAAQ;YAAM;SAAS,EAAE;YACtFW,OAAO;YACPC,SAAS;QACX;QACAL,SAASC,OAAOD,MAAM;IACxB,EAAE,OAAOM,OAAO;QACdZ,MAAM,0BAA0BY;QAChC,OAAOT;IACT;IAEA,IAAIU;IACJ,IAAI;QACFA,SAASC,KAAKC,KAAK,CAACT;IACtB,EAAE,OAAOM,OAAO;QACdZ,MAAM,wCAAwCY;QAC9C,OAAOT;IACT;IAEA,IAAI,CAACa,MAAMC,OAAO,CAACJ,SAAS;QAC1Bb,MAAM;QACN,OAAOG;IACT;IAEA,MAAMe,YAAYhB,KAAKiB,UAAU,CAACC,GAAG,CAAC,CAACC;QACrC,MAAMC,QAAQ,AAACT,OAA4BU,IAAI,CAAC,CAACC,QAAUA,OAAOC,SAASJ;QAC3E,IAAI,CAACC,SAAS,CAACN,MAAMC,OAAO,CAACK,MAAMI,MAAM,GAAG;YAC1C,OAAO,IAAIrB;QACb;QACA,OAAO,IAAIA,IAAIiB,MAAMI,MAAM,CAACC,MAAM,CAAC,CAACC,IAAmB,OAAOA,MAAM;IACtE;IAEA,MAAMC,sBAAsB;WAAKX,SAAS,CAAC,EAAE,IAAI,EAAE;KAAE,CAACS,MAAM,CAAC,CAACG,QAC5DZ,UAAUa,KAAK,CAAC,CAACC,MAAQA,IAAIC,GAAG,CAACH;IAEnC,OAAO;QAAC1B,4BAA4B,IAAIC,IAAIwB;IAAoB;AAClE"}
@@ -6,7 +6,10 @@ import { execa } from 'execa';
6
6
  import { getErrorMessage, toError } from '../../util/getErrorMessage.js';
7
7
  const skillsDebug = subdebug('skills:setup');
8
8
  /** Source repo for the bundled `skills` CLI. See https://www.sanity.io/docs/ai/skills. */ export const SANITY_SKILLS_REPO = 'sanity-io/agent-toolkit';
9
- /** Name of the skill we install — must match the entry in the source repo. */ export const SANITY_SKILL_NAME = 'sanity-best-practices';
9
+ /** Names of the skills we install — must match the entries in the source repo. */ export const SANITY_SKILL_NAMES = [
10
+ 'sanity-best-practices',
11
+ 'sanity-migration'
12
+ ];
10
13
  /**
11
14
  * Absolute path to the bundled `skills` CLI bin. Resolved once at module load
12
15
  * via `import.meta.resolve` so we run the version pinned in our package.json
@@ -32,7 +35,7 @@ const skillsDebug = subdebug('skills:setup');
32
35
  'add',
33
36
  SANITY_SKILLS_REPO,
34
37
  '--skill',
35
- SANITY_SKILL_NAME,
38
+ ...SANITY_SKILL_NAMES,
36
39
  '-g',
37
40
  ...uniqueAgents.flatMap((agent)=>[
38
41
  '-a',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/skills/setupSkills.ts"],"sourcesContent":["import {fileURLToPath} from 'node:url'\n\nimport {ux} from '@oclif/core'\nimport {subdebug} from '@sanity/cli-core'\nimport {logSymbols} from '@sanity/cli-core/ux'\nimport {execa} from 'execa'\n\nimport {getErrorMessage, toError} from '../../util/getErrorMessage.js'\n\nconst skillsDebug = subdebug('skills:setup')\n\n/** Source repo for the bundled `skills` CLI. See https://www.sanity.io/docs/ai/skills. */\nexport const SANITY_SKILLS_REPO = 'sanity-io/agent-toolkit'\n\n/** Name of the skill we install — must match the entry in the source repo. */\nexport const SANITY_SKILL_NAME = 'sanity-best-practices'\n\n/**\n * Absolute path to the bundled `skills` CLI bin. Resolved once at module load\n * via `import.meta.resolve` so we run the version pinned in our package.json\n * instead of paying the `npx -y` registry lookup at runtime.\n */\nexport const SKILLS_BIN_PATH = fileURLToPath(\n import.meta.resolve('skills/bin/cli.mjs', import.meta.url),\n)\n\ninterface SetupSkillsOptions {\n /** Skills-CLI agent IDs (e.g. 'cursor', 'claude-code') to install for. */\n agents: string[]\n}\n\ninterface SetupSkillsResult {\n /** Deduplicated `--agent` values passed to `skills add`. */\n installedAgents: string[]\n skipped: boolean\n\n error?: Error\n}\n\n/**\n * Runs the bundled `skills add` globally for the given agents. Failures are\n * surfaced as warnings and never throw — skills install is best-effort and\n * must not abort `sanity init`.\n */\nexport async function setupSkills(options: SetupSkillsOptions): Promise<SetupSkillsResult> {\n const uniqueAgents = [...new Set(options.agents)]\n\n if (uniqueAgents.length === 0) {\n skillsDebug('No agents passed — skipping skills install')\n return {installedAgents: [], skipped: true}\n }\n\n const args = [\n SKILLS_BIN_PATH,\n 'add',\n SANITY_SKILLS_REPO,\n '--skill',\n SANITY_SKILL_NAME,\n '-g',\n ...uniqueAgents.flatMap((agent) => ['-a', agent]),\n '-y',\n ]\n\n skillsDebug('Running: %s %s', process.execPath, args.join(' '))\n\n try {\n const result = await execa(process.execPath, args, {stdio: 'pipe', timeout: 90_000})\n skillsDebug('skills stdout: %s', result.stdout)\n skillsDebug('skills stderr: %s', result.stderr)\n ux.stdout(`${logSymbols.success} Installed Sanity agent skills for ${uniqueAgents.join(', ')}`)\n return {installedAgents: uniqueAgents, skipped: false}\n } catch (error) {\n skillsDebug('Error installing skills %O', error)\n const err = toError(error)\n ux.warn(`Could not install Sanity agent skills: ${getErrorMessage(error)}`)\n if (error && typeof error === 'object') {\n const {stderr, stdout} = error as {stderr?: string; stdout?: string}\n if (stdout) ux.warn(stdout)\n if (stderr) ux.warn(stderr)\n }\n return {error: err, installedAgents: [], skipped: false}\n }\n}\n"],"names":["fileURLToPath","ux","subdebug","logSymbols","execa","getErrorMessage","toError","skillsDebug","SANITY_SKILLS_REPO","SANITY_SKILL_NAME","SKILLS_BIN_PATH","resolve","url","setupSkills","options","uniqueAgents","Set","agents","length","installedAgents","skipped","args","flatMap","agent","process","execPath","join","result","stdio","timeout","stdout","stderr","success","error","err","warn"],"mappings":"AAAA,SAAQA,aAAa,QAAO,WAAU;AAEtC,SAAQC,EAAE,QAAO,cAAa;AAC9B,SAAQC,QAAQ,QAAO,mBAAkB;AACzC,SAAQC,UAAU,QAAO,sBAAqB;AAC9C,SAAQC,KAAK,QAAO,QAAO;AAE3B,SAAQC,eAAe,EAAEC,OAAO,QAAO,gCAA+B;AAEtE,MAAMC,cAAcL,SAAS;AAE7B,wFAAwF,GACxF,OAAO,MAAMM,qBAAqB,0BAAyB;AAE3D,4EAA4E,GAC5E,OAAO,MAAMC,oBAAoB,wBAAuB;AAExD;;;;CAIC,GACD,OAAO,MAAMC,kBAAkBV,cAC7B,YAAYW,OAAO,CAAC,sBAAsB,YAAYC,GAAG,GAC1D;AAeD;;;;CAIC,GACD,OAAO,eAAeC,YAAYC,OAA2B;IAC3D,MAAMC,eAAe;WAAI,IAAIC,IAAIF,QAAQG,MAAM;KAAE;IAEjD,IAAIF,aAAaG,MAAM,KAAK,GAAG;QAC7BX,YAAY;QACZ,OAAO;YAACY,iBAAiB,EAAE;YAAEC,SAAS;QAAI;IAC5C;IAEA,MAAMC,OAAO;QACXX;QACA;QACAF;QACA;QACAC;QACA;WACGM,aAAaO,OAAO,CAAC,CAACC,QAAU;gBAAC;gBAAMA;aAAM;QAChD;KACD;IAEDhB,YAAY,kBAAkBiB,QAAQC,QAAQ,EAAEJ,KAAKK,IAAI,CAAC;IAE1D,IAAI;QACF,MAAMC,SAAS,MAAMvB,MAAMoB,QAAQC,QAAQ,EAAEJ,MAAM;YAACO,OAAO;YAAQC,SAAS;QAAM;QAClFtB,YAAY,qBAAqBoB,OAAOG,MAAM;QAC9CvB,YAAY,qBAAqBoB,OAAOI,MAAM;QAC9C9B,GAAG6B,MAAM,CAAC,GAAG3B,WAAW6B,OAAO,CAAC,mCAAmC,EAAEjB,aAAaW,IAAI,CAAC,OAAO;QAC9F,OAAO;YAACP,iBAAiBJ;YAAcK,SAAS;QAAK;IACvD,EAAE,OAAOa,OAAO;QACd1B,YAAY,8BAA8B0B;QAC1C,MAAMC,MAAM5B,QAAQ2B;QACpBhC,GAAGkC,IAAI,CAAC,CAAC,uCAAuC,EAAE9B,gBAAgB4B,QAAQ;QAC1E,IAAIA,SAAS,OAAOA,UAAU,UAAU;YACtC,MAAM,EAACF,MAAM,EAAED,MAAM,EAAC,GAAGG;YACzB,IAAIH,QAAQ7B,GAAGkC,IAAI,CAACL;YACpB,IAAIC,QAAQ9B,GAAGkC,IAAI,CAACJ;QACtB;QACA,OAAO;YAACE,OAAOC;YAAKf,iBAAiB,EAAE;YAAEC,SAAS;QAAK;IACzD;AACF"}
1
+ {"version":3,"sources":["../../../src/actions/skills/setupSkills.ts"],"sourcesContent":["import {fileURLToPath} from 'node:url'\n\nimport {ux} from '@oclif/core'\nimport {subdebug} from '@sanity/cli-core'\nimport {logSymbols} from '@sanity/cli-core/ux'\nimport {execa} from 'execa'\n\nimport {getErrorMessage, toError} from '../../util/getErrorMessage.js'\n\nconst skillsDebug = subdebug('skills:setup')\n\n/** Source repo for the bundled `skills` CLI. See https://www.sanity.io/docs/ai/skills. */\nexport const SANITY_SKILLS_REPO = 'sanity-io/agent-toolkit'\n\n/** Names of the skills we install — must match the entries in the source repo. */\nexport const SANITY_SKILL_NAMES = ['sanity-best-practices', 'sanity-migration']\n\n/**\n * Absolute path to the bundled `skills` CLI bin. Resolved once at module load\n * via `import.meta.resolve` so we run the version pinned in our package.json\n * instead of paying the `npx -y` registry lookup at runtime.\n */\nexport const SKILLS_BIN_PATH = fileURLToPath(\n import.meta.resolve('skills/bin/cli.mjs', import.meta.url),\n)\n\ninterface SetupSkillsOptions {\n /** Skills-CLI agent IDs (e.g. 'cursor', 'claude-code') to install for. */\n agents: string[]\n}\n\ninterface SetupSkillsResult {\n /** Deduplicated `--agent` values passed to `skills add`. */\n installedAgents: string[]\n skipped: boolean\n\n error?: Error\n}\n\n/**\n * Runs the bundled `skills add` globally for the given agents. Failures are\n * surfaced as warnings and never throw — skills install is best-effort and\n * must not abort `sanity init`.\n */\nexport async function setupSkills(options: SetupSkillsOptions): Promise<SetupSkillsResult> {\n const uniqueAgents = [...new Set(options.agents)]\n\n if (uniqueAgents.length === 0) {\n skillsDebug('No agents passed — skipping skills install')\n return {installedAgents: [], skipped: true}\n }\n\n const args = [\n SKILLS_BIN_PATH,\n 'add',\n SANITY_SKILLS_REPO,\n '--skill',\n ...SANITY_SKILL_NAMES,\n '-g',\n ...uniqueAgents.flatMap((agent) => ['-a', agent]),\n '-y',\n ]\n\n skillsDebug('Running: %s %s', process.execPath, args.join(' '))\n\n try {\n const result = await execa(process.execPath, args, {stdio: 'pipe', timeout: 90_000})\n skillsDebug('skills stdout: %s', result.stdout)\n skillsDebug('skills stderr: %s', result.stderr)\n ux.stdout(`${logSymbols.success} Installed Sanity agent skills for ${uniqueAgents.join(', ')}`)\n return {installedAgents: uniqueAgents, skipped: false}\n } catch (error) {\n skillsDebug('Error installing skills %O', error)\n const err = toError(error)\n ux.warn(`Could not install Sanity agent skills: ${getErrorMessage(error)}`)\n if (error && typeof error === 'object') {\n const {stderr, stdout} = error as {stderr?: string; stdout?: string}\n if (stdout) ux.warn(stdout)\n if (stderr) ux.warn(stderr)\n }\n return {error: err, installedAgents: [], skipped: false}\n }\n}\n"],"names":["fileURLToPath","ux","subdebug","logSymbols","execa","getErrorMessage","toError","skillsDebug","SANITY_SKILLS_REPO","SANITY_SKILL_NAMES","SKILLS_BIN_PATH","resolve","url","setupSkills","options","uniqueAgents","Set","agents","length","installedAgents","skipped","args","flatMap","agent","process","execPath","join","result","stdio","timeout","stdout","stderr","success","error","err","warn"],"mappings":"AAAA,SAAQA,aAAa,QAAO,WAAU;AAEtC,SAAQC,EAAE,QAAO,cAAa;AAC9B,SAAQC,QAAQ,QAAO,mBAAkB;AACzC,SAAQC,UAAU,QAAO,sBAAqB;AAC9C,SAAQC,KAAK,QAAO,QAAO;AAE3B,SAAQC,eAAe,EAAEC,OAAO,QAAO,gCAA+B;AAEtE,MAAMC,cAAcL,SAAS;AAE7B,wFAAwF,GACxF,OAAO,MAAMM,qBAAqB,0BAAyB;AAE3D,gFAAgF,GAChF,OAAO,MAAMC,qBAAqB;IAAC;IAAyB;CAAmB,CAAA;AAE/E;;;;CAIC,GACD,OAAO,MAAMC,kBAAkBV,cAC7B,YAAYW,OAAO,CAAC,sBAAsB,YAAYC,GAAG,GAC1D;AAeD;;;;CAIC,GACD,OAAO,eAAeC,YAAYC,OAA2B;IAC3D,MAAMC,eAAe;WAAI,IAAIC,IAAIF,QAAQG,MAAM;KAAE;IAEjD,IAAIF,aAAaG,MAAM,KAAK,GAAG;QAC7BX,YAAY;QACZ,OAAO;YAACY,iBAAiB,EAAE;YAAEC,SAAS;QAAI;IAC5C;IAEA,MAAMC,OAAO;QACXX;QACA;QACAF;QACA;WACGC;QACH;WACGM,aAAaO,OAAO,CAAC,CAACC,QAAU;gBAAC;gBAAMA;aAAM;QAChD;KACD;IAEDhB,YAAY,kBAAkBiB,QAAQC,QAAQ,EAAEJ,KAAKK,IAAI,CAAC;IAE1D,IAAI;QACF,MAAMC,SAAS,MAAMvB,MAAMoB,QAAQC,QAAQ,EAAEJ,MAAM;YAACO,OAAO;YAAQC,SAAS;QAAM;QAClFtB,YAAY,qBAAqBoB,OAAOG,MAAM;QAC9CvB,YAAY,qBAAqBoB,OAAOI,MAAM;QAC9C9B,GAAG6B,MAAM,CAAC,GAAG3B,WAAW6B,OAAO,CAAC,mCAAmC,EAAEjB,aAAaW,IAAI,CAAC,OAAO;QAC9F,OAAO;YAACP,iBAAiBJ;YAAcK,SAAS;QAAK;IACvD,EAAE,OAAOa,OAAO;QACd1B,YAAY,8BAA8B0B;QAC1C,MAAMC,MAAM5B,QAAQ2B;QACpBhC,GAAGkC,IAAI,CAAC,CAAC,uCAAuC,EAAE9B,gBAAgB4B,QAAQ;QAC1E,IAAIA,SAAS,OAAOA,UAAU,UAAU;YACtC,MAAM,EAACF,MAAM,EAAED,MAAM,EAAC,GAAGG;YACzB,IAAIH,QAAQ7B,GAAGkC,IAAI,CAACL;YACpB,IAAIC,QAAQ9B,GAAGkC,IAAI,CAACJ;QACtB;QACA,OAAO;YAACE,OAAOC;YAAKf,iBAAiB,EAAE;YAAEC,SAAS;QAAK;IACzD;AACF"}
@@ -4,7 +4,6 @@ import { SanityCommand } from '@sanity/cli-core';
4
4
  import { initAction } from '../actions/init/initAction.js';
5
5
  import { InitError } from '../actions/init/initError.js';
6
6
  import { flagsToInitOptions } from '../actions/init/types.js';
7
- import { getSanityEnv } from '../util/getSanityEnv.js';
8
7
  export class InitCommand extends SanityCommand {
9
8
  static args = {
10
9
  type: Args.string({
@@ -244,15 +243,15 @@ export class InitCommand extends SanityCommand {
244
243
  };
245
244
  async run() {
246
245
  let mcpMode = 'prompt';
247
- if (!this.flags.mcp || !this.resolveIsInteractive() || getSanityEnv() !== 'production') {
246
+ if (!this.flags.mcp || !this.resolveIsInteractive()) {
248
247
  mcpMode = 'skip';
249
248
  } else if (this.flags.yes) {
250
249
  mcpMode = 'auto';
251
250
  }
252
- // Mirror MCP's environment gating: skip skills install in non-production
253
- // Sanity envs so e2e / UI tests don't run the bundled skills CLI.
251
+ // Mirror MCP's environment gating: skip install in test environments
252
+ // ensure e2e / CI tests don't run the bundled skills CLI.
254
253
  let skillsMode = 'auto';
255
- if (!this.flags.skills || !this.resolveIsInteractive() || getSanityEnv() !== 'production') {
254
+ if (!this.flags.skills || !this.resolveIsInteractive()) {
256
255
  skillsMode = 'skip';
257
256
  }
258
257
  try {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/commands/init.ts"],"sourcesContent":["import {Args, Command, Flags} from '@oclif/core'\nimport {CLIError} from '@oclif/core/errors'\nimport {SanityCommand} from '@sanity/cli-core'\n\nimport {initAction} from '../actions/init/initAction.js'\nimport {InitError} from '../actions/init/initError.js'\nimport {flagsToInitOptions} from '../actions/init/types.js'\nimport {getSanityEnv} from '../util/getSanityEnv.js'\n\nexport class InitCommand extends SanityCommand<typeof InitCommand> {\n static override args = {type: Args.string({hidden: true})}\n static override description = 'Initialize a new Sanity Studio, project and/or app'\n static override enableJsonFlag = true\n\n static override examples = [\n '<%= config.bin %> <%= command.id %>',\n {\n command: '<%= config.bin %> <%= command.id %> --dataset-default',\n description: 'Initialize a new project with a public dataset named \"production\"',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> -y --project abc123 --dataset production --output-path ~/myproj',\n description: 'Initialize a project with the given project ID and dataset to the given path',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> -y --project abc123 --dataset staging --template moviedb --output-path .',\n description:\n 'Initialize a project with the given project ID and dataset using the moviedb template to the given path',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> -y --project-name \"Movies Unlimited\" --dataset moviedb --visibility private --template moviedb --output-path /Users/espenh/movies-unlimited',\n description: 'Create a brand new project with name \"Movies Unlimited\"',\n },\n ] satisfies Array<Command.Example>\n\n static override flags = {\n 'auto-updates': Flags.boolean({\n allowNo: true,\n default: true,\n description: 'Enable auto updates of studio versions',\n exclusive: ['bare'],\n }),\n bare: Flags.boolean({\n description:\n 'Skip the Studio initialization and only print the selected project ID and dataset name to stdout',\n }),\n coupon: Flags.string({\n description:\n 'Optionally select a coupon for a new project (cannot be used with --project-plan)',\n exclusive: ['project-plan'],\n helpValue: '<code>',\n }),\n 'create-project': Flags.string({\n deprecated: {message: 'Use --project-name instead'},\n description: 'Create a new project with the given name',\n helpValue: '<name>',\n hidden: true,\n }),\n dataset: Flags.string({\n description: 'Dataset name for the studio',\n exclusive: ['dataset-default'],\n helpValue: '<name>',\n }),\n 'dataset-default': Flags.boolean({\n description: 'Set up a project with a public dataset named \"production\"',\n }),\n env: Flags.string({\n description: 'Write environment variables to file',\n exclusive: ['bare'],\n helpValue: '<filename>',\n parse: async (input) => {\n if (!input.startsWith('.env')) {\n throw new CLIError('Env filename (`--env`) must start with `.env`')\n }\n return input\n },\n }),\n 'from-create': Flags.boolean({\n description: 'Internal flag to indicate that the command is run from create-sanity',\n hidden: true,\n }),\n git: Flags.string({\n default: undefined,\n description: 'Specify a commit message for initial commit, or disable git init',\n exclusive: ['bare'],\n // oclif doesn't indent correctly with custom help labels, thus leading space :/\n helpLabel: ' --[no-]git',\n helpValue: '<message>',\n }),\n 'import-dataset': Flags.boolean({\n allowNo: true,\n default: undefined,\n description: 'Import template sample dataset',\n }),\n mcp: Flags.boolean({\n allowNo: true,\n default: true,\n description: 'Enable AI editor integration (MCP) setup',\n }),\n 'nextjs-add-config-files': Flags.boolean({\n allowNo: true,\n default: undefined,\n description: 'Add config files to Next.js project',\n helpGroup: 'Next.js',\n }),\n 'nextjs-append-env': Flags.boolean({\n allowNo: true,\n default: undefined,\n description: 'Append project ID and dataset to .env file',\n helpGroup: 'Next.js',\n }),\n 'nextjs-embed-studio': Flags.boolean({\n allowNo: true,\n default: undefined,\n description: 'Embed the Studio in Next.js application',\n helpGroup: 'Next.js',\n }),\n // oclif doesn't support a boolean/string flag combination, but listing both a\n // `--git` and a `--no-git` flag in help breaks conventions, so we hide this one,\n // but use it to \"combine\" the two in the actual logic.\n 'no-git': Flags.boolean({\n description: 'Disable git initialization',\n exclusive: ['git'],\n hidden: true,\n }),\n organization: Flags.string({\n description: 'Organization ID to use for the project',\n helpValue: '<id>',\n }),\n 'output-path': Flags.string({\n description: 'Path to write studio project to',\n exclusive: ['bare'],\n helpValue: '<path>',\n }),\n 'overwrite-files': Flags.boolean({\n allowNo: true,\n default: undefined,\n description: 'Overwrite existing files',\n }),\n 'package-manager': Flags.string({\n description: 'Specify which package manager to use [allowed: npm, yarn, pnpm]',\n exclusive: ['bare'],\n helpValue: '<manager>',\n options: ['npm', 'yarn', 'pnpm'],\n }),\n project: Flags.string({\n aliases: ['project-id'],\n description: 'Project ID to use for the studio',\n exclusive: ['create-project', 'project-name'],\n helpValue: '<id>',\n }),\n 'project-name': Flags.string({\n description: 'Create a new project with the given name',\n exclusive: ['project', 'create-project'],\n helpValue: '<name>',\n }),\n 'project-plan': Flags.string({\n description: 'Optionally select a plan for a new project',\n helpValue: '<name>',\n }),\n provider: Flags.string({\n description: 'Login provider to use',\n helpValue: '<provider>',\n }),\n quickstart: Flags.boolean({\n deprecated: true,\n description:\n 'Used for initializing a project from a server schema that is saved in the Journey API',\n hidden: true,\n }),\n reconfigure: Flags.boolean({\n deprecated: {\n message: 'This flag is no longer supported',\n version: '3.0.0',\n },\n description: 'Reconfigure an existing project',\n hidden: true,\n }),\n skills: Flags.boolean({\n allowNo: true,\n default: true,\n description: 'Install Sanity agent skills globally for detected AI editors',\n }),\n template: Flags.string({\n description: 'Project template to use [default: \"clean\"]',\n exclusive: ['bare'],\n helpValue: '<template>',\n }),\n // Porting over a beta flag\n // Oclif doesn't seem to support something in beta so hiding for now\n 'template-token': Flags.string({\n description: 'Used for accessing private GitHub repo templates',\n hidden: true,\n }),\n typescript: Flags.boolean({\n allowNo: true,\n default: undefined,\n description: 'Enable TypeScript support',\n exclusive: ['bare'],\n }),\n visibility: Flags.string({\n description: 'Visibility mode for dataset',\n helpValue: '<mode>',\n options: ['public', 'private'],\n }),\n yes: Flags.boolean({\n char: 'y',\n default: false,\n description:\n 'Unattended mode, answers \"yes\" to any \"yes/no\" prompt and otherwise uses defaults',\n }),\n }\n\n public async run(): Promise<void> {\n let mcpMode: 'auto' | 'prompt' | 'skip' = 'prompt'\n if (!this.flags.mcp || !this.resolveIsInteractive() || getSanityEnv() !== 'production') {\n mcpMode = 'skip'\n } else if (this.flags.yes) {\n mcpMode = 'auto'\n }\n\n // Mirror MCP's environment gating: skip skills install in non-production\n // Sanity envs so e2e / UI tests don't run the bundled skills CLI.\n let skillsMode: 'auto' | 'prompt' | 'skip' = 'auto'\n if (!this.flags.skills || !this.resolveIsInteractive() || getSanityEnv() !== 'production') {\n skillsMode = 'skip'\n }\n\n try {\n await initAction(\n flagsToInitOptions(this.flags, this.isUnattended(), this.args, mcpMode, skillsMode),\n {\n output: this.output,\n telemetry: this.telemetry,\n workDir: process.cwd(),\n },\n )\n } catch (error) {\n if (error instanceof InitError) {\n this.error(error.message, {exit: error.exitCode})\n }\n throw error\n }\n }\n}\n"],"names":["Args","Flags","CLIError","SanityCommand","initAction","InitError","flagsToInitOptions","getSanityEnv","InitCommand","args","type","string","hidden","description","enableJsonFlag","examples","command","flags","boolean","allowNo","default","exclusive","bare","coupon","helpValue","deprecated","message","dataset","env","parse","input","startsWith","git","undefined","helpLabel","mcp","helpGroup","organization","options","project","aliases","provider","quickstart","reconfigure","version","skills","template","typescript","visibility","yes","char","run","mcpMode","resolveIsInteractive","skillsMode","isUnattended","output","telemetry","workDir","process","cwd","error","exit","exitCode"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/commands/init.ts"],"sourcesContent":["import {Args, Command, Flags} from '@oclif/core'\nimport {CLIError} from '@oclif/core/errors'\nimport {SanityCommand} from '@sanity/cli-core'\n\nimport {initAction} from '../actions/init/initAction.js'\nimport {InitError} from '../actions/init/initError.js'\nimport {flagsToInitOptions} from '../actions/init/types.js'\n\nexport class InitCommand extends SanityCommand<typeof InitCommand> {\n static override args = {type: Args.string({hidden: true})}\n static override description = 'Initialize a new Sanity Studio, project and/or app'\n static override enableJsonFlag = true\n\n static override examples = [\n '<%= config.bin %> <%= command.id %>',\n {\n command: '<%= config.bin %> <%= command.id %> --dataset-default',\n description: 'Initialize a new project with a public dataset named \"production\"',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> -y --project abc123 --dataset production --output-path ~/myproj',\n description: 'Initialize a project with the given project ID and dataset to the given path',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> -y --project abc123 --dataset staging --template moviedb --output-path .',\n description:\n 'Initialize a project with the given project ID and dataset using the moviedb template to the given path',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> -y --project-name \"Movies Unlimited\" --dataset moviedb --visibility private --template moviedb --output-path /Users/espenh/movies-unlimited',\n description: 'Create a brand new project with name \"Movies Unlimited\"',\n },\n ] satisfies Array<Command.Example>\n\n static override flags = {\n 'auto-updates': Flags.boolean({\n allowNo: true,\n default: true,\n description: 'Enable auto updates of studio versions',\n exclusive: ['bare'],\n }),\n bare: Flags.boolean({\n description:\n 'Skip the Studio initialization and only print the selected project ID and dataset name to stdout',\n }),\n coupon: Flags.string({\n description:\n 'Optionally select a coupon for a new project (cannot be used with --project-plan)',\n exclusive: ['project-plan'],\n helpValue: '<code>',\n }),\n 'create-project': Flags.string({\n deprecated: {message: 'Use --project-name instead'},\n description: 'Create a new project with the given name',\n helpValue: '<name>',\n hidden: true,\n }),\n dataset: Flags.string({\n description: 'Dataset name for the studio',\n exclusive: ['dataset-default'],\n helpValue: '<name>',\n }),\n 'dataset-default': Flags.boolean({\n description: 'Set up a project with a public dataset named \"production\"',\n }),\n env: Flags.string({\n description: 'Write environment variables to file',\n exclusive: ['bare'],\n helpValue: '<filename>',\n parse: async (input) => {\n if (!input.startsWith('.env')) {\n throw new CLIError('Env filename (`--env`) must start with `.env`')\n }\n return input\n },\n }),\n 'from-create': Flags.boolean({\n description: 'Internal flag to indicate that the command is run from create-sanity',\n hidden: true,\n }),\n git: Flags.string({\n default: undefined,\n description: 'Specify a commit message for initial commit, or disable git init',\n exclusive: ['bare'],\n // oclif doesn't indent correctly with custom help labels, thus leading space :/\n helpLabel: ' --[no-]git',\n helpValue: '<message>',\n }),\n 'import-dataset': Flags.boolean({\n allowNo: true,\n default: undefined,\n description: 'Import template sample dataset',\n }),\n mcp: Flags.boolean({\n allowNo: true,\n default: true,\n description: 'Enable AI editor integration (MCP) setup',\n }),\n 'nextjs-add-config-files': Flags.boolean({\n allowNo: true,\n default: undefined,\n description: 'Add config files to Next.js project',\n helpGroup: 'Next.js',\n }),\n 'nextjs-append-env': Flags.boolean({\n allowNo: true,\n default: undefined,\n description: 'Append project ID and dataset to .env file',\n helpGroup: 'Next.js',\n }),\n 'nextjs-embed-studio': Flags.boolean({\n allowNo: true,\n default: undefined,\n description: 'Embed the Studio in Next.js application',\n helpGroup: 'Next.js',\n }),\n // oclif doesn't support a boolean/string flag combination, but listing both a\n // `--git` and a `--no-git` flag in help breaks conventions, so we hide this one,\n // but use it to \"combine\" the two in the actual logic.\n 'no-git': Flags.boolean({\n description: 'Disable git initialization',\n exclusive: ['git'],\n hidden: true,\n }),\n organization: Flags.string({\n description: 'Organization ID to use for the project',\n helpValue: '<id>',\n }),\n 'output-path': Flags.string({\n description: 'Path to write studio project to',\n exclusive: ['bare'],\n helpValue: '<path>',\n }),\n 'overwrite-files': Flags.boolean({\n allowNo: true,\n default: undefined,\n description: 'Overwrite existing files',\n }),\n 'package-manager': Flags.string({\n description: 'Specify which package manager to use [allowed: npm, yarn, pnpm]',\n exclusive: ['bare'],\n helpValue: '<manager>',\n options: ['npm', 'yarn', 'pnpm'],\n }),\n project: Flags.string({\n aliases: ['project-id'],\n description: 'Project ID to use for the studio',\n exclusive: ['create-project', 'project-name'],\n helpValue: '<id>',\n }),\n 'project-name': Flags.string({\n description: 'Create a new project with the given name',\n exclusive: ['project', 'create-project'],\n helpValue: '<name>',\n }),\n 'project-plan': Flags.string({\n description: 'Optionally select a plan for a new project',\n helpValue: '<name>',\n }),\n provider: Flags.string({\n description: 'Login provider to use',\n helpValue: '<provider>',\n }),\n quickstart: Flags.boolean({\n deprecated: true,\n description:\n 'Used for initializing a project from a server schema that is saved in the Journey API',\n hidden: true,\n }),\n reconfigure: Flags.boolean({\n deprecated: {\n message: 'This flag is no longer supported',\n version: '3.0.0',\n },\n description: 'Reconfigure an existing project',\n hidden: true,\n }),\n skills: Flags.boolean({\n allowNo: true,\n default: true,\n description: 'Install Sanity agent skills globally for detected AI editors',\n }),\n template: Flags.string({\n description: 'Project template to use [default: \"clean\"]',\n exclusive: ['bare'],\n helpValue: '<template>',\n }),\n // Porting over a beta flag\n // Oclif doesn't seem to support something in beta so hiding for now\n 'template-token': Flags.string({\n description: 'Used for accessing private GitHub repo templates',\n hidden: true,\n }),\n typescript: Flags.boolean({\n allowNo: true,\n default: undefined,\n description: 'Enable TypeScript support',\n exclusive: ['bare'],\n }),\n visibility: Flags.string({\n description: 'Visibility mode for dataset',\n helpValue: '<mode>',\n options: ['public', 'private'],\n }),\n yes: Flags.boolean({\n char: 'y',\n default: false,\n description:\n 'Unattended mode, answers \"yes\" to any \"yes/no\" prompt and otherwise uses defaults',\n }),\n }\n\n public async run(): Promise<void> {\n let mcpMode: 'auto' | 'prompt' | 'skip' = 'prompt'\n if (!this.flags.mcp || !this.resolveIsInteractive()) {\n mcpMode = 'skip'\n } else if (this.flags.yes) {\n mcpMode = 'auto'\n }\n\n // Mirror MCP's environment gating: skip install in test environments\n // ensure e2e / CI tests don't run the bundled skills CLI.\n let skillsMode: 'auto' | 'prompt' | 'skip' = 'auto'\n if (!this.flags.skills || !this.resolveIsInteractive()) {\n skillsMode = 'skip'\n }\n\n try {\n await initAction(\n flagsToInitOptions(this.flags, this.isUnattended(), this.args, mcpMode, skillsMode),\n {\n output: this.output,\n telemetry: this.telemetry,\n workDir: process.cwd(),\n },\n )\n } catch (error) {\n if (error instanceof InitError) {\n this.error(error.message, {exit: error.exitCode})\n }\n throw error\n }\n }\n}\n"],"names":["Args","Flags","CLIError","SanityCommand","initAction","InitError","flagsToInitOptions","InitCommand","args","type","string","hidden","description","enableJsonFlag","examples","command","flags","boolean","allowNo","default","exclusive","bare","coupon","helpValue","deprecated","message","dataset","env","parse","input","startsWith","git","undefined","helpLabel","mcp","helpGroup","organization","options","project","aliases","provider","quickstart","reconfigure","version","skills","template","typescript","visibility","yes","char","run","mcpMode","resolveIsInteractive","skillsMode","isUnattended","output","telemetry","workDir","process","cwd","error","exit","exitCode"],"mappings":"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"}
@@ -3,9 +3,9 @@ import { getPartialEnvWithNpmPath } from './packageManagerChoice.js';
3
3
  /**
4
4
  * Resolves the peer dependencies of a package by querying the npm registry.
5
5
  *
6
- * @param packageName - Package name with version (e.g. "next-sanity\@11")
6
+ * @param packageName - Package name with version (e.g. "next-sanity\@13")
7
7
  * @param cwd - Working directory (used to resolve local npm paths)
8
- * @returns Array of peer dependency strings (e.g. ["next\@^15.0.0", "react\@^19.0.0"])
8
+ * @returns Array of peer dependency strings (e.g. ["next\@^16.0.0", "react\@^19.0.0"])
9
9
  */ export async function getPeerDependencies(packageName, cwd) {
10
10
  let stdout;
11
11
  try {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/util/packageManager/getPeerDependencies.ts"],"sourcesContent":["import {execa} from 'execa'\n\nimport {getPartialEnvWithNpmPath} from './packageManagerChoice.js'\n\n/**\n * Resolves the peer dependencies of a package by querying the npm registry.\n *\n * @param packageName - Package name with version (e.g. \"next-sanity\\@11\")\n * @param cwd - Working directory (used to resolve local npm paths)\n * @returns Array of peer dependency strings (e.g. [\"next\\@^15.0.0\", \"react\\@^19.0.0\"])\n */\nexport async function getPeerDependencies(packageName: string, cwd: string): Promise<string[]> {\n let stdout: string\n try {\n const result = await execa('npm', ['view', packageName, 'peerDependencies', '--json'], {\n cwd,\n encoding: 'utf8',\n env: getPartialEnvWithNpmPath(cwd),\n })\n stdout = result.stdout\n } catch (error) {\n throw new Error(`Failed to resolve peer dependencies for ${packageName}`, {cause: error})\n }\n\n if (!stdout.trim()) {\n return []\n }\n\n try {\n const peerDeps: Record<string, string> | null = JSON.parse(stdout)\n if (!peerDeps || typeof peerDeps !== 'object') {\n return []\n }\n return Object.entries(peerDeps).map(([name, range]) => `${name}@${range}`)\n } catch (error) {\n throw new Error(`Failed to resolve peer dependencies for ${packageName}`, {cause: error})\n }\n}\n"],"names":["execa","getPartialEnvWithNpmPath","getPeerDependencies","packageName","cwd","stdout","result","encoding","env","error","Error","cause","trim","peerDeps","JSON","parse","Object","entries","map","name","range"],"mappings":"AAAA,SAAQA,KAAK,QAAO,QAAO;AAE3B,SAAQC,wBAAwB,QAAO,4BAA2B;AAElE;;;;;;CAMC,GACD,OAAO,eAAeC,oBAAoBC,WAAmB,EAAEC,GAAW;IACxE,IAAIC;IACJ,IAAI;QACF,MAAMC,SAAS,MAAMN,MAAM,OAAO;YAAC;YAAQG;YAAa;YAAoB;SAAS,EAAE;YACrFC;YACAG,UAAU;YACVC,KAAKP,yBAAyBG;QAChC;QACAC,SAASC,OAAOD,MAAM;IACxB,EAAE,OAAOI,OAAO;QACd,MAAM,IAAIC,MAAM,CAAC,wCAAwC,EAAEP,aAAa,EAAE;YAACQ,OAAOF;QAAK;IACzF;IAEA,IAAI,CAACJ,OAAOO,IAAI,IAAI;QAClB,OAAO,EAAE;IACX;IAEA,IAAI;QACF,MAAMC,WAA0CC,KAAKC,KAAK,CAACV;QAC3D,IAAI,CAACQ,YAAY,OAAOA,aAAa,UAAU;YAC7C,OAAO,EAAE;QACX;QACA,OAAOG,OAAOC,OAAO,CAACJ,UAAUK,GAAG,CAAC,CAAC,CAACC,MAAMC,MAAM,GAAK,GAAGD,KAAK,CAAC,EAAEC,OAAO;IAC3E,EAAE,OAAOX,OAAO;QACd,MAAM,IAAIC,MAAM,CAAC,wCAAwC,EAAEP,aAAa,EAAE;YAACQ,OAAOF;QAAK;IACzF;AACF"}
1
+ {"version":3,"sources":["../../../src/util/packageManager/getPeerDependencies.ts"],"sourcesContent":["import {execa} from 'execa'\n\nimport {getPartialEnvWithNpmPath} from './packageManagerChoice.js'\n\n/**\n * Resolves the peer dependencies of a package by querying the npm registry.\n *\n * @param packageName - Package name with version (e.g. \"next-sanity\\@13\")\n * @param cwd - Working directory (used to resolve local npm paths)\n * @returns Array of peer dependency strings (e.g. [\"next\\@^16.0.0\", \"react\\@^19.0.0\"])\n */\nexport async function getPeerDependencies(packageName: string, cwd: string): Promise<string[]> {\n let stdout: string\n try {\n const result = await execa('npm', ['view', packageName, 'peerDependencies', '--json'], {\n cwd,\n encoding: 'utf8',\n env: getPartialEnvWithNpmPath(cwd),\n })\n stdout = result.stdout\n } catch (error) {\n throw new Error(`Failed to resolve peer dependencies for ${packageName}`, {cause: error})\n }\n\n if (!stdout.trim()) {\n return []\n }\n\n try {\n const peerDeps: Record<string, string> | null = JSON.parse(stdout)\n if (!peerDeps || typeof peerDeps !== 'object') {\n return []\n }\n return Object.entries(peerDeps).map(([name, range]) => `${name}@${range}`)\n } catch (error) {\n throw new Error(`Failed to resolve peer dependencies for ${packageName}`, {cause: error})\n }\n}\n"],"names":["execa","getPartialEnvWithNpmPath","getPeerDependencies","packageName","cwd","stdout","result","encoding","env","error","Error","cause","trim","peerDeps","JSON","parse","Object","entries","map","name","range"],"mappings":"AAAA,SAAQA,KAAK,QAAO,QAAO;AAE3B,SAAQC,wBAAwB,QAAO,4BAA2B;AAElE;;;;;;CAMC,GACD,OAAO,eAAeC,oBAAoBC,WAAmB,EAAEC,GAAW;IACxE,IAAIC;IACJ,IAAI;QACF,MAAMC,SAAS,MAAMN,MAAM,OAAO;YAAC;YAAQG;YAAa;YAAoB;SAAS,EAAE;YACrFC;YACAG,UAAU;YACVC,KAAKP,yBAAyBG;QAChC;QACAC,SAASC,OAAOD,MAAM;IACxB,EAAE,OAAOI,OAAO;QACd,MAAM,IAAIC,MAAM,CAAC,wCAAwC,EAAEP,aAAa,EAAE;YAACQ,OAAOF;QAAK;IACzF;IAEA,IAAI,CAACJ,OAAOO,IAAI,IAAI;QAClB,OAAO,EAAE;IACX;IAEA,IAAI;QACF,MAAMC,WAA0CC,KAAKC,KAAK,CAACV;QAC3D,IAAI,CAACQ,YAAY,OAAOA,aAAa,UAAU;YAC7C,OAAO,EAAE;QACX;QACA,OAAOG,OAAOC,OAAO,CAACJ,UAAUK,GAAG,CAAC,CAAC,CAACC,MAAMC,MAAM,GAAK,GAAGD,KAAK,CAAC,EAAEC,OAAO;IAC3E,EAAE,OAAOX,OAAO;QACd,MAAM,IAAIC,MAAM,CAAC,wCAAwC,EAAEP,aAAa,EAAE;YAACQ,OAAOF;QAAK;IACzF;AACF"}
@@ -37,12 +37,50 @@ const PACKAGE_MANAGER_COMMANDS = {
37
37
  ]
38
38
  }
39
39
  };
40
+ const IGNORED_BUILDS_NOTICE = 'pnpm skipped build scripts for some dependencies. Run "pnpm approve-builds" in the project directory to pick which dependencies should be allowed to run scripts.';
41
+ // Matches pnpm's `ERR_PNPM_IGNORED_BUILDS` error against whitespace-normalized
42
+ // output (pnpm may wrap the message), capturing the list of skipped packages,
43
+ // eg `esbuild@0.25.0, sharp@0.34.0.` - the `<pkg>@<version>` token sequence
44
+ // ends the capture where the trailing `Run "pnpm approve-builds"…` hint starts
45
+ const IGNORED_BUILDS_PATTERN = /ERR_PNPM_IGNORED_BUILDS.*?Ignored build scripts: ?((?:[^\s,]+@[^\s,]+[, ]*)+)/;
46
+ function getIgnoredBuildScripts(commandOutput) {
47
+ const match = commandOutput.replaceAll(/\s+/g, ' ').match(IGNORED_BUILDS_PATTERN);
48
+ if (!match) {
49
+ return undefined;
50
+ }
51
+ // The capture allows both comma and whitespace separators (pnpm may print
52
+ // either, and wrapping can drop the comma), so split on both.
53
+ return match[1].split(/[\s,]+/).map((entry)=>entry.replace(/\.$/, '')).filter(Boolean);
54
+ }
55
+ function isEsbuild(ignoredEntry) {
56
+ // Entries are on the form `<pkg-name>@<version>` - strip the version,
57
+ // keeping in mind that scoped package names also start with `@`
58
+ return ignoredEntry.replace(/@[^@]+$/, '') === 'esbuild';
59
+ }
40
60
  async function executePackageManagerCommand(packageManager, args, execOptions, output, errorMessage) {
41
61
  const progress = spinner(`Running ${packageManager} ${args.join(' ')}\n`).start();
42
62
  const result = await execa(packageManager, args, execOptions);
43
63
  if (result?.exitCode || result?.failed) {
64
+ // pnpm exits non-zero if dependency build scripts were skipped, even though
65
+ // the install itself succeeded. Treat it as a success, but point to
66
+ // `pnpm approve-builds` if anything other than esbuild was skipped
67
+ // (esbuild works without its build script through a JS fallback).
68
+ const commandOutput = [
69
+ result.stdout,
70
+ result.stderr
71
+ ].filter((chunk)=>typeof chunk === 'string').join('\n');
72
+ const ignoredBuilds = packageManager === 'pnpm' ? getIgnoredBuildScripts(commandOutput) : undefined;
73
+ if (ignoredBuilds) {
74
+ progress.succeed();
75
+ if (ignoredBuilds.some((entry)=>!isEsbuild(entry))) {
76
+ output.warn(IGNORED_BUILDS_NOTICE);
77
+ }
78
+ return;
79
+ }
44
80
  progress.fail();
45
- output.log(String(result.stdout));
81
+ // Log both streams - package managers often print the actionable error
82
+ // details to stderr, so logging stdout alone can hide the failure reason.
83
+ output.log(commandOutput);
46
84
  output.error(errorMessage, {
47
85
  exit: 1
48
86
  });
@@ -56,6 +94,7 @@ export async function installDeclaredPackages(cwd, packageManager, context) {
56
94
  cwd,
57
95
  encoding: 'utf8',
58
96
  env: getPartialEnvWithNpmPath(cwd),
97
+ reject: false,
59
98
  stdio: 'pipe'
60
99
  };
61
100
  if (packageManager === 'manual') {
@@ -73,6 +112,7 @@ export async function installNewPackages(options, context) {
73
112
  cwd: workDir,
74
113
  encoding: 'utf8',
75
114
  env: getPartialEnvWithNpmPath(workDir),
115
+ reject: false,
76
116
  stdio: 'pipe'
77
117
  };
78
118
  if (packageManager === 'manual') {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/util/packageManager/installPackages.ts"],"sourcesContent":["import {type Output} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {execa, type Options} from 'execa'\n\nimport {getPartialEnvWithNpmPath, type PackageManager} from './packageManagerChoice.js'\n\ntype PackageManagerLibs = Exclude<PackageManager, 'manual'>\n\n/**\n * @internal\n */\ninterface InstallOptions {\n packageManager: PackageManager\n packages: string[]\n}\n\ninterface PackageManagerCommands {\n add: {[key in PackageManagerLibs]: (packages: string[]) => string[]}\n install: {[key in PackageManagerLibs]: string[]}\n}\n\nconst PACKAGE_MANAGER_COMMANDS: PackageManagerCommands = {\n add: {\n bun: (packages) => ['add', ...packages],\n npm: (packages) => ['install', '--save', ...packages],\n pnpm: (packages) => ['add', '--save-prod', ...packages],\n yarn: (packages) => ['add', ...packages],\n },\n install: {\n bun: ['install'],\n npm: ['install'],\n pnpm: ['install'],\n yarn: ['install'],\n },\n}\n\nasync function executePackageManagerCommand(\n packageManager: PackageManagerLibs,\n args: string[],\n execOptions: Options,\n output: Output,\n errorMessage: string,\n): Promise<void> {\n const progress = spinner(`Running ${packageManager} ${args.join(' ')}\\n`).start()\n\n const result = await execa(packageManager, args, execOptions)\n\n if (result?.exitCode || result?.failed) {\n progress.fail()\n output.log(String(result.stdout))\n output.error(errorMessage, {exit: 1})\n } else {\n progress.succeed()\n }\n}\n\nexport async function installDeclaredPackages(\n cwd: string,\n packageManager: PackageManager,\n context: {output: Output; workDir: string},\n): Promise<void> {\n const {output} = context\n const execOptions: Options = {\n cwd,\n encoding: 'utf8',\n env: getPartialEnvWithNpmPath(cwd),\n stdio: 'pipe',\n }\n\n if (packageManager === 'manual') {\n const npmCommand = PACKAGE_MANAGER_COMMANDS.install.npm\n output.log(`Manual installation selected — run 'npm ${npmCommand.join(' ')}' or equivalent`)\n } else {\n const args = PACKAGE_MANAGER_COMMANDS.install[packageManager]\n await executePackageManagerCommand(\n packageManager,\n args,\n execOptions,\n output,\n 'Dependency installation failed',\n )\n }\n}\n\nexport async function installNewPackages(\n options: InstallOptions,\n context: {output: Output; workDir: string},\n): Promise<void> {\n const {packageManager, packages} = options\n const {output, workDir} = context\n const execOptions: Options = {\n cwd: workDir,\n encoding: 'utf8',\n env: getPartialEnvWithNpmPath(workDir),\n stdio: 'pipe',\n }\n\n if (packageManager === 'manual') {\n const npmCommand = PACKAGE_MANAGER_COMMANDS.add.npm(packages)\n output.log(`Manual installation selected - run 'npm ${npmCommand.join(' ')}' or equivalent`)\n } else {\n const args = PACKAGE_MANAGER_COMMANDS.add[packageManager](packages)\n await executePackageManagerCommand(\n packageManager,\n args,\n execOptions,\n output,\n 'Package installation failed',\n )\n }\n}\n"],"names":["spinner","execa","getPartialEnvWithNpmPath","PACKAGE_MANAGER_COMMANDS","add","bun","packages","npm","pnpm","yarn","install","executePackageManagerCommand","packageManager","args","execOptions","output","errorMessage","progress","join","start","result","exitCode","failed","fail","log","String","stdout","error","exit","succeed","installDeclaredPackages","cwd","context","encoding","env","stdio","npmCommand","installNewPackages","options","workDir"],"mappings":"AACA,SAAQA,OAAO,QAAO,sBAAqB;AAC3C,SAAQC,KAAK,QAAqB,QAAO;AAEzC,SAAQC,wBAAwB,QAA4B,4BAA2B;AAiBvF,MAAMC,2BAAmD;IACvDC,KAAK;QACHC,KAAK,CAACC,WAAa;gBAAC;mBAAUA;aAAS;QACvCC,KAAK,CAACD,WAAa;gBAAC;gBAAW;mBAAaA;aAAS;QACrDE,MAAM,CAACF,WAAa;gBAAC;gBAAO;mBAAkBA;aAAS;QACvDG,MAAM,CAACH,WAAa;gBAAC;mBAAUA;aAAS;IAC1C;IACAI,SAAS;QACPL,KAAK;YAAC;SAAU;QAChBE,KAAK;YAAC;SAAU;QAChBC,MAAM;YAAC;SAAU;QACjBC,MAAM;YAAC;SAAU;IACnB;AACF;AAEA,eAAeE,6BACbC,cAAkC,EAClCC,IAAc,EACdC,WAAoB,EACpBC,MAAc,EACdC,YAAoB;IAEpB,MAAMC,WAAWjB,QAAQ,CAAC,QAAQ,EAAEY,eAAe,CAAC,EAAEC,KAAKK,IAAI,CAAC,KAAK,EAAE,CAAC,EAAEC,KAAK;IAE/E,MAAMC,SAAS,MAAMnB,MAAMW,gBAAgBC,MAAMC;IAEjD,IAAIM,QAAQC,YAAYD,QAAQE,QAAQ;QACtCL,SAASM,IAAI;QACbR,OAAOS,GAAG,CAACC,OAAOL,OAAOM,MAAM;QAC/BX,OAAOY,KAAK,CAACX,cAAc;YAACY,MAAM;QAAC;IACrC,OAAO;QACLX,SAASY,OAAO;IAClB;AACF;AAEA,OAAO,eAAeC,wBACpBC,GAAW,EACXnB,cAA8B,EAC9BoB,OAA0C;IAE1C,MAAM,EAACjB,MAAM,EAAC,GAAGiB;IACjB,MAAMlB,cAAuB;QAC3BiB;QACAE,UAAU;QACVC,KAAKhC,yBAAyB6B;QAC9BI,OAAO;IACT;IAEA,IAAIvB,mBAAmB,UAAU;QAC/B,MAAMwB,aAAajC,yBAAyBO,OAAO,CAACH,GAAG;QACvDQ,OAAOS,GAAG,CAAC,CAAC,wCAAwC,EAAEY,WAAWlB,IAAI,CAAC,KAAK,eAAe,CAAC;IAC7F,OAAO;QACL,MAAML,OAAOV,yBAAyBO,OAAO,CAACE,eAAe;QAC7D,MAAMD,6BACJC,gBACAC,MACAC,aACAC,QACA;IAEJ;AACF;AAEA,OAAO,eAAesB,mBACpBC,OAAuB,EACvBN,OAA0C;IAE1C,MAAM,EAACpB,cAAc,EAAEN,QAAQ,EAAC,GAAGgC;IACnC,MAAM,EAACvB,MAAM,EAAEwB,OAAO,EAAC,GAAGP;IAC1B,MAAMlB,cAAuB;QAC3BiB,KAAKQ;QACLN,UAAU;QACVC,KAAKhC,yBAAyBqC;QAC9BJ,OAAO;IACT;IAEA,IAAIvB,mBAAmB,UAAU;QAC/B,MAAMwB,aAAajC,yBAAyBC,GAAG,CAACG,GAAG,CAACD;QACpDS,OAAOS,GAAG,CAAC,CAAC,wCAAwC,EAAEY,WAAWlB,IAAI,CAAC,KAAK,eAAe,CAAC;IAC7F,OAAO;QACL,MAAML,OAAOV,yBAAyBC,GAAG,CAACQ,eAAe,CAACN;QAC1D,MAAMK,6BACJC,gBACAC,MACAC,aACAC,QACA;IAEJ;AACF"}
1
+ {"version":3,"sources":["../../../src/util/packageManager/installPackages.ts"],"sourcesContent":["import {type Output} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {execa, type Options} from 'execa'\n\nimport {getPartialEnvWithNpmPath, type PackageManager} from './packageManagerChoice.js'\n\ntype PackageManagerLibs = Exclude<PackageManager, 'manual'>\n\n/**\n * @internal\n */\ninterface InstallOptions {\n packageManager: PackageManager\n packages: string[]\n}\n\ninterface PackageManagerCommands {\n add: {[key in PackageManagerLibs]: (packages: string[]) => string[]}\n install: {[key in PackageManagerLibs]: string[]}\n}\n\nconst PACKAGE_MANAGER_COMMANDS: PackageManagerCommands = {\n add: {\n bun: (packages) => ['add', ...packages],\n npm: (packages) => ['install', '--save', ...packages],\n pnpm: (packages) => ['add', '--save-prod', ...packages],\n yarn: (packages) => ['add', ...packages],\n },\n install: {\n bun: ['install'],\n npm: ['install'],\n pnpm: ['install'],\n yarn: ['install'],\n },\n}\n\nconst IGNORED_BUILDS_NOTICE =\n 'pnpm skipped build scripts for some dependencies. Run \"pnpm approve-builds\" in the project directory to pick which dependencies should be allowed to run scripts.'\n\n// Matches pnpm's `ERR_PNPM_IGNORED_BUILDS` error against whitespace-normalized\n// output (pnpm may wrap the message), capturing the list of skipped packages,\n// eg `esbuild@0.25.0, sharp@0.34.0.` - the `<pkg>@<version>` token sequence\n// ends the capture where the trailing `Run \"pnpm approve-builds\"…` hint starts\nconst IGNORED_BUILDS_PATTERN =\n /ERR_PNPM_IGNORED_BUILDS.*?Ignored build scripts: ?((?:[^\\s,]+@[^\\s,]+[, ]*)+)/\n\nfunction getIgnoredBuildScripts(commandOutput: string): string[] | undefined {\n const match = commandOutput.replaceAll(/\\s+/g, ' ').match(IGNORED_BUILDS_PATTERN)\n if (!match) {\n return undefined\n }\n\n // The capture allows both comma and whitespace separators (pnpm may print\n // either, and wrapping can drop the comma), so split on both.\n return match[1]\n .split(/[\\s,]+/)\n .map((entry) => entry.replace(/\\.$/, ''))\n .filter(Boolean)\n}\n\nfunction isEsbuild(ignoredEntry: string): boolean {\n // Entries are on the form `<pkg-name>@<version>` - strip the version,\n // keeping in mind that scoped package names also start with `@`\n return ignoredEntry.replace(/@[^@]+$/, '') === 'esbuild'\n}\n\nasync function executePackageManagerCommand(\n packageManager: PackageManagerLibs,\n args: string[],\n execOptions: Options,\n output: Output,\n errorMessage: string,\n): Promise<void> {\n const progress = spinner(`Running ${packageManager} ${args.join(' ')}\\n`).start()\n\n const result = await execa(packageManager, args, execOptions)\n\n if (result?.exitCode || result?.failed) {\n // pnpm exits non-zero if dependency build scripts were skipped, even though\n // the install itself succeeded. Treat it as a success, but point to\n // `pnpm approve-builds` if anything other than esbuild was skipped\n // (esbuild works without its build script through a JS fallback).\n const commandOutput = [result.stdout, result.stderr]\n .filter((chunk): chunk is string => typeof chunk === 'string')\n .join('\\n')\n const ignoredBuilds =\n packageManager === 'pnpm' ? getIgnoredBuildScripts(commandOutput) : undefined\n\n if (ignoredBuilds) {\n progress.succeed()\n if (ignoredBuilds.some((entry) => !isEsbuild(entry))) {\n output.warn(IGNORED_BUILDS_NOTICE)\n }\n return\n }\n\n progress.fail()\n // Log both streams - package managers often print the actionable error\n // details to stderr, so logging stdout alone can hide the failure reason.\n output.log(commandOutput)\n output.error(errorMessage, {exit: 1})\n } else {\n progress.succeed()\n }\n}\n\nexport async function installDeclaredPackages(\n cwd: string,\n packageManager: PackageManager,\n context: {output: Output; workDir: string},\n): Promise<void> {\n const {output} = context\n const execOptions: Options = {\n cwd,\n encoding: 'utf8',\n env: getPartialEnvWithNpmPath(cwd),\n reject: false,\n stdio: 'pipe',\n }\n\n if (packageManager === 'manual') {\n const npmCommand = PACKAGE_MANAGER_COMMANDS.install.npm\n output.log(`Manual installation selected — run 'npm ${npmCommand.join(' ')}' or equivalent`)\n } else {\n const args = PACKAGE_MANAGER_COMMANDS.install[packageManager]\n await executePackageManagerCommand(\n packageManager,\n args,\n execOptions,\n output,\n 'Dependency installation failed',\n )\n }\n}\n\nexport async function installNewPackages(\n options: InstallOptions,\n context: {output: Output; workDir: string},\n): Promise<void> {\n const {packageManager, packages} = options\n const {output, workDir} = context\n const execOptions: Options = {\n cwd: workDir,\n encoding: 'utf8',\n env: getPartialEnvWithNpmPath(workDir),\n reject: false,\n stdio: 'pipe',\n }\n\n if (packageManager === 'manual') {\n const npmCommand = PACKAGE_MANAGER_COMMANDS.add.npm(packages)\n output.log(`Manual installation selected - run 'npm ${npmCommand.join(' ')}' or equivalent`)\n } else {\n const args = PACKAGE_MANAGER_COMMANDS.add[packageManager](packages)\n await executePackageManagerCommand(\n packageManager,\n args,\n execOptions,\n output,\n 'Package installation failed',\n )\n }\n}\n"],"names":["spinner","execa","getPartialEnvWithNpmPath","PACKAGE_MANAGER_COMMANDS","add","bun","packages","npm","pnpm","yarn","install","IGNORED_BUILDS_NOTICE","IGNORED_BUILDS_PATTERN","getIgnoredBuildScripts","commandOutput","match","replaceAll","undefined","split","map","entry","replace","filter","Boolean","isEsbuild","ignoredEntry","executePackageManagerCommand","packageManager","args","execOptions","output","errorMessage","progress","join","start","result","exitCode","failed","stdout","stderr","chunk","ignoredBuilds","succeed","some","warn","fail","log","error","exit","installDeclaredPackages","cwd","context","encoding","env","reject","stdio","npmCommand","installNewPackages","options","workDir"],"mappings":"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"}
@@ -5116,5 +5116,5 @@
5116
5116
  ]
5117
5117
  }
5118
5118
  },
5119
- "version": "7.1.0"
5119
+ "version": "7.2.1"
5120
5120
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/cli",
3
- "version": "7.1.0",
3
+ "version": "7.2.1",
4
4
  "description": "Sanity CLI tool for managing Sanity projects and organizations",
5
5
  "keywords": [
6
6
  "cli",
@@ -62,11 +62,11 @@
62
62
  "@sanity/id-utils": "^1.0.0",
63
63
  "@sanity/import": "^6.0.2",
64
64
  "@sanity/migrate": "^7.0.1",
65
- "@sanity/runtime-cli": "^15.1.3",
66
- "@sanity/schema": "^5.30.0",
65
+ "@sanity/runtime-cli": "^16.0.0",
66
+ "@sanity/schema": "^5.31.1",
67
67
  "@sanity/telemetry": "^1.1.0",
68
68
  "@sanity/template-validator": "^3.1.0",
69
- "@sanity/types": "^5.30.0",
69
+ "@sanity/types": "^5.31.1",
70
70
  "@sanity/worker-channels": "^2.0.0",
71
71
  "@vercel/frameworks": "3.21.1",
72
72
  "chokidar": "^5.0.0",
@@ -97,9 +97,9 @@
97
97
  "pluralize-esm": "^9.0.5",
98
98
  "pretty-ms": "^9.3.0",
99
99
  "promise-props-recursive": "^2.0.2",
100
- "react": "^19.2.6",
101
- "react-dom": "^19.2.6",
102
- "react-is": "^19.2.6",
100
+ "react": "^19.2.7",
101
+ "react-dom": "^19.2.7",
102
+ "react-is": "^19.2.7",
103
103
  "rxjs": "^7.8.2",
104
104
  "semver": "^7.8.1",
105
105
  "skills": "^1.5.9",
@@ -113,13 +113,13 @@
113
113
  "vite": "^8.0.16",
114
114
  "which": "^6.0.1",
115
115
  "yaml": "^2.9.0",
116
- "zod": "^4.3.6",
117
- "@sanity/cli-build": "^1.0.1",
116
+ "zod": "^4.4.3",
117
+ "@sanity/cli-build": "^1.0.3",
118
118
  "@sanity/cli-core": "^2.0.1"
119
119
  },
120
120
  "devDependencies": {
121
121
  "@eslint/compat": "^2.1.0",
122
- "@sanity/pkg-utils": "^10.5.2",
122
+ "@sanity/pkg-utils": "^10.5.3",
123
123
  "@sanity/ui": "^3.2.0",
124
124
  "@swc/cli": "^0.8.1",
125
125
  "@swc/core": "^1.15.40",
@@ -145,7 +145,7 @@
145
145
  "oclif": "^4.23.10",
146
146
  "publint": "^0.3.21",
147
147
  "rimraf": "^6.0.1",
148
- "sanity": "^5.30.0",
148
+ "sanity": "^5.31.1",
149
149
  "typescript": "^5.9.3",
150
150
  "vitest": "^4.1.8",
151
151
  "@repo/tsconfig": "3.70.0",