@sanity/cli 7.0.2 → 7.2.0

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
@@ -1237,23 +1237,24 @@ Export a dataset to a local gzipped tarball. Assets returning 401, 403, or 404 a
1237
1237
  ```
1238
1238
  USAGE
1239
1239
  $ sanity datasets export [NAME] [DESTINATION] [-p <id>] [--asset-concurrency <value>] [--mode stream|cursor]
1240
- [--no-assets] [--no-compress] [--no-drafts] [--overwrite] [--raw] [--types <value>]
1240
+ [--no-assets] [--no-compress] [--no-drafts] [--no-strict-asset-verification] [--overwrite] [--raw] [--types <value>]
1241
1241
 
1242
1242
  ARGUMENTS
1243
1243
  [NAME] Name of the dataset to export
1244
1244
  [DESTINATION] Output destination file path
1245
1245
 
1246
1246
  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)
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
+ --no-strict-asset-verification Do not abort the export when an asset fails hash or content-length verification
1255
+ --overwrite Overwrite any file with the same name
1256
+ --raw Extract only documents, without rewriting asset references
1257
+ --types=<value> Defines which document types to export (comma-separated)
1257
1258
 
1258
1259
  OVERRIDE FLAGS
1259
1260
  -p, --project-id=<id> Project ID to export dataset from (overrides CLI configuration)
@@ -1277,6 +1278,10 @@ EXAMPLES
1277
1278
  Export specific document types
1278
1279
 
1279
1280
  $ sanity datasets export staging staging.tar.gz --types products,shops
1281
+
1282
+ Export dataset without aborting on asset verification failures
1283
+
1284
+ $ sanity datasets export moviedb moviedb.tar.gz --no-strict-asset-verification
1280
1285
  ```
1281
1286
 
1282
1287
  ## `sanity datasets import SOURCE [TARGETDATASET]`
@@ -2399,7 +2404,7 @@ USAGE
2399
2404
  <name> | --dataset-default] [--env <filename> | ] [--git <message> | ] [--import-dataset] [--mcp]
2400
2405
  [--nextjs-add-config-files] [--nextjs-append-env] [--nextjs-embed-studio] [--organization <id>] [--output-path
2401
2406
  <path> | ] [--overwrite-files] [--package-manager <manager> | ] [--project <id> | | --project-name <name>]
2402
- [--provider <provider>] [--template <template> | ] [--typescript | ] [--visibility <mode>] [-y]
2407
+ [--provider <provider>] [--skills] [--template <template> | ] [--typescript | ] [--visibility <mode>] [-y]
2403
2408
 
2404
2409
  FLAGS
2405
2410
  -y, --yes Unattended mode, answers "yes" to any "yes/no" prompt and otherwise uses defaults
@@ -2421,6 +2426,7 @@ FLAGS
2421
2426
  --project-name=<name> Create a new project with the given name
2422
2427
  --project-plan=<name> Optionally select a plan for a new project
2423
2428
  --provider=<provider> Login provider to use
2429
+ --[no-]skills Install Sanity agent skills globally for detected AI editors
2424
2430
  --template=<template> Project template to use [default: "clean"]
2425
2431
  --[no-]typescript Enable TypeScript support
2426
2432
  --visibility=<mode> Visibility mode for dataset
@@ -15,7 +15,7 @@ export function getDevServerConfig({ cliConfig, flags, output, workDir }) {
15
15
  configSpinner.succeed();
16
16
  const isApp = cliConfig && 'app' in cliConfig;
17
17
  const env = process.env;
18
- const reactStrictMode = env.SANITY_STUDIO_REACT_STRICT_MODE ? env.SANITY_STUDIO_REACT_STRICT_MODE === 'true' : Boolean(cliConfig?.reactStrictMode);
18
+ const reactStrictMode = env.SANITY_STUDIO_REACT_STRICT_MODE ? env.SANITY_STUDIO_REACT_STRICT_MODE === 'true' : cliConfig?.reactStrictMode;
19
19
  const envBasePath = getSanityEnvVar('BASEPATH', isApp ?? false);
20
20
  if (envBasePath && cliConfig?.project?.basePath) {
21
21
  output.warn(`Overriding configured base path (${cliConfig.project.basePath}) with value from environment variable (${envBasePath})`);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/dev/getDevServerConfig.ts"],"sourcesContent":["import path from 'node:path'\n\nimport {type CliConfig, getSanityEnvVar, type Output} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\n\nimport {type DevServerOptions} from '../../server/devServer.js'\nimport {getSharedServerConfig} from '../../util/getSharedServerConfig.js'\nimport {type DevFlags} from './types.js'\n\nexport function getDevServerConfig({\n cliConfig,\n flags,\n output,\n workDir,\n}: {\n cliConfig?: CliConfig\n flags: DevFlags\n output: Output\n workDir: string\n}): Omit<DevServerOptions, 'spinner'> {\n const configSpinner = spinner('Checking configuration files...')\n\n const baseConfig = getSharedServerConfig({\n cliConfig,\n flags: {\n host: flags.host,\n port: flags.port,\n },\n workDir,\n })\n\n configSpinner.succeed()\n\n const isApp = cliConfig && 'app' in cliConfig\n const env = process.env\n const reactStrictMode = env.SANITY_STUDIO_REACT_STRICT_MODE\n ? env.SANITY_STUDIO_REACT_STRICT_MODE === 'true'\n : Boolean(cliConfig?.reactStrictMode)\n\n const envBasePath = getSanityEnvVar('BASEPATH', isApp ?? false)\n if (envBasePath && cliConfig?.project?.basePath) {\n output.warn(\n `Overriding configured base path (${cliConfig.project.basePath}) with value from environment variable (${envBasePath})`,\n )\n }\n\n return {\n ...baseConfig,\n reactCompiler: cliConfig && 'reactCompiler' in cliConfig ? cliConfig.reactCompiler : undefined,\n reactStrictMode,\n staticPath: path.join(workDir, 'static'),\n typegen: cliConfig?.typegen,\n }\n}\n"],"names":["path","getSanityEnvVar","spinner","getSharedServerConfig","getDevServerConfig","cliConfig","flags","output","workDir","configSpinner","baseConfig","host","port","succeed","isApp","env","process","reactStrictMode","SANITY_STUDIO_REACT_STRICT_MODE","Boolean","envBasePath","project","basePath","warn","reactCompiler","undefined","staticPath","join","typegen"],"mappings":"AAAA,OAAOA,UAAU,YAAW;AAE5B,SAAwBC,eAAe,QAAoB,mBAAkB;AAC7E,SAAQC,OAAO,QAAO,sBAAqB;AAG3C,SAAQC,qBAAqB,QAAO,sCAAqC;AAGzE,OAAO,SAASC,mBAAmB,EACjCC,SAAS,EACTC,KAAK,EACLC,MAAM,EACNC,OAAO,EAMR;IACC,MAAMC,gBAAgBP,QAAQ;IAE9B,MAAMQ,aAAaP,sBAAsB;QACvCE;QACAC,OAAO;YACLK,MAAML,MAAMK,IAAI;YAChBC,MAAMN,MAAMM,IAAI;QAClB;QACAJ;IACF;IAEAC,cAAcI,OAAO;IAErB,MAAMC,QAAQT,aAAa,SAASA;IACpC,MAAMU,MAAMC,QAAQD,GAAG;IACvB,MAAME,kBAAkBF,IAAIG,+BAA+B,GACvDH,IAAIG,+BAA+B,KAAK,SACxCC,QAAQd,WAAWY;IAEvB,MAAMG,cAAcnB,gBAAgB,YAAYa,SAAS;IACzD,IAAIM,eAAef,WAAWgB,SAASC,UAAU;QAC/Cf,OAAOgB,IAAI,CACT,CAAC,iCAAiC,EAAElB,UAAUgB,OAAO,CAACC,QAAQ,CAAC,wCAAwC,EAAEF,YAAY,CAAC,CAAC;IAE3H;IAEA,OAAO;QACL,GAAGV,UAAU;QACbc,eAAenB,aAAa,mBAAmBA,YAAYA,UAAUmB,aAAa,GAAGC;QACrFR;QACAS,YAAY1B,KAAK2B,IAAI,CAACnB,SAAS;QAC/BoB,SAASvB,WAAWuB;IACtB;AACF"}
1
+ {"version":3,"sources":["../../../src/actions/dev/getDevServerConfig.ts"],"sourcesContent":["import path from 'node:path'\n\nimport {type CliConfig, getSanityEnvVar, type Output} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\n\nimport {type DevServerOptions} from '../../server/devServer.js'\nimport {getSharedServerConfig} from '../../util/getSharedServerConfig.js'\nimport {type DevFlags} from './types.js'\n\nexport function getDevServerConfig({\n cliConfig,\n flags,\n output,\n workDir,\n}: {\n cliConfig?: CliConfig\n flags: DevFlags\n output: Output\n workDir: string\n}): Omit<DevServerOptions, 'spinner'> {\n const configSpinner = spinner('Checking configuration files...')\n\n const baseConfig = getSharedServerConfig({\n cliConfig,\n flags: {\n host: flags.host,\n port: flags.port,\n },\n workDir,\n })\n\n configSpinner.succeed()\n\n const isApp = cliConfig && 'app' in cliConfig\n const env = process.env\n const reactStrictMode = env.SANITY_STUDIO_REACT_STRICT_MODE\n ? env.SANITY_STUDIO_REACT_STRICT_MODE === 'true'\n : cliConfig?.reactStrictMode\n\n const envBasePath = getSanityEnvVar('BASEPATH', isApp ?? false)\n if (envBasePath && cliConfig?.project?.basePath) {\n output.warn(\n `Overriding configured base path (${cliConfig.project.basePath}) with value from environment variable (${envBasePath})`,\n )\n }\n\n return {\n ...baseConfig,\n reactCompiler: cliConfig && 'reactCompiler' in cliConfig ? cliConfig.reactCompiler : undefined,\n reactStrictMode,\n staticPath: path.join(workDir, 'static'),\n typegen: cliConfig?.typegen,\n }\n}\n"],"names":["path","getSanityEnvVar","spinner","getSharedServerConfig","getDevServerConfig","cliConfig","flags","output","workDir","configSpinner","baseConfig","host","port","succeed","isApp","env","process","reactStrictMode","SANITY_STUDIO_REACT_STRICT_MODE","envBasePath","project","basePath","warn","reactCompiler","undefined","staticPath","join","typegen"],"mappings":"AAAA,OAAOA,UAAU,YAAW;AAE5B,SAAwBC,eAAe,QAAoB,mBAAkB;AAC7E,SAAQC,OAAO,QAAO,sBAAqB;AAG3C,SAAQC,qBAAqB,QAAO,sCAAqC;AAGzE,OAAO,SAASC,mBAAmB,EACjCC,SAAS,EACTC,KAAK,EACLC,MAAM,EACNC,OAAO,EAMR;IACC,MAAMC,gBAAgBP,QAAQ;IAE9B,MAAMQ,aAAaP,sBAAsB;QACvCE;QACAC,OAAO;YACLK,MAAML,MAAMK,IAAI;YAChBC,MAAMN,MAAMM,IAAI;QAClB;QACAJ;IACF;IAEAC,cAAcI,OAAO;IAErB,MAAMC,QAAQT,aAAa,SAASA;IACpC,MAAMU,MAAMC,QAAQD,GAAG;IACvB,MAAME,kBAAkBF,IAAIG,+BAA+B,GACvDH,IAAIG,+BAA+B,KAAK,SACxCb,WAAWY;IAEf,MAAME,cAAclB,gBAAgB,YAAYa,SAAS;IACzD,IAAIK,eAAed,WAAWe,SAASC,UAAU;QAC/Cd,OAAOe,IAAI,CACT,CAAC,iCAAiC,EAAEjB,UAAUe,OAAO,CAACC,QAAQ,CAAC,wCAAwC,EAAEF,YAAY,CAAC,CAAC;IAE3H;IAEA,OAAO;QACL,GAAGT,UAAU;QACba,eAAelB,aAAa,mBAAmBA,YAAYA,UAAUkB,aAAa,GAAGC;QACrFP;QACAQ,YAAYzB,KAAK0B,IAAI,CAAClB,SAAS;QAC/BmB,SAAStB,WAAWsB;IACtB;AACF"}
@@ -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":"AAAA,SAAQA,SAAS,QAAO,YAAW;AAEnC,SAA4BC,QAAQ,QAAqC,mBAAkB;AAC3F,SAAQC,UAAU,EAAEC,OAAO,QAAO,sBAAqB;AAEvD,SAAwBC,UAAU,QAAO,qBAAoB;AAC7D,OAAOC,YAAY,sBAAqB;AAExC,SAAQC,oBAAoB,QAAO,+BAA8B;AACjE,SAAQC,UAAU,QAAO,yBAAwB;AACjD,SAAQC,oBAAoB,QAA4B,oCAAmC;AAC3F,SAAQC,qBAAqB,QAAO,gCAA+B;AACnE,SAAQC,kBAAkB,QAAO,mCAAkC;AACnE,SAAQC,eAAe,QAAO,iCAAgC;AAC9D,SAAQC,eAAe,QAAO,6BAA4B;AAC1D,SAAQC,KAAK,QAAO,yBAAwB;AAC5C,SAAQC,sBAAsB,QAAO,mCAAkC;AACvE,SAAQC,QAAQ,QAAO,qBAAoB;AAC3C,SAAQC,WAAW,QAAO,2BAA0B;AACpD,SAAQC,6BAA6B,QAAO,qCAAoC;AAChF,SAAQC,oBAAoB,QAAO,4BAA2B;AAC9D,SAAQC,qBAAqB,QAAO,iCAAgC;AACpE,SAAQC,OAAO,QAAO,eAAc;AACpC,SAAQC,SAAS,QAAO,iBAAgB;AACxC,SAAQC,aAAa,EAAEC,YAAY,EAAEC,uBAAuB,QAAO,mBAAkB;AACrF,SAAQC,UAAU,QAAO,kBAAiB;AAC1C,SAAQC,UAAU,QAAO,kBAAiB;AAC1C,SAAQC,OAAO,QAAO,oBAAmB;AACzC,SAAQC,qBAAqB,QAAO,qCAAoC;AACxE,SAAQC,iBAAiB,QAAO,iCAAgC;AAChE,SAAQC,oBAAoB,QAAO,oCAAmC;AACtE,SAAQC,qBAAqB,EAAEC,iBAAiB,QAAsB,sBAAqB;AAG3F,MAAMC,QAAQhC,SAAS;AAEvB,OAAO,eAAeiC,WAAWC,OAAoB,EAAEC,OAAoB;IACzE,MAAM,EAACC,MAAM,EAAEC,OAAO,EAAC,GAAGF;IAE1B,IAAID,QAAQI,OAAO,EAAE;QACnB,MAAM,IAAIlB,UACRc,QAAQI,OAAO,KAAK,WAChB,gEACA,CAAC,mBAAmB,EAAEJ,QAAQI,OAAO,CAAC,CAAC,CAAC,EAC5C;IAEJ;IAEA,MAAMC,QAAQJ,QAAQK,SAAS,CAACD,KAAK,CAAChC;IAEtC,IAAI2B,QAAQO,WAAW,EAAE;QACvB,MAAM,IAAIrB,UAAU,sEAAsE;IAC5F;IAEA,IAAIc,QAAQQ,OAAO,IAAIR,QAAQS,YAAY,EAAE;QAC3C,MAAM,IAAIvB,UACR,yIACA;IAEJ;IAEA,MAAMwB,gBAAgBV,QAAQW,cAAc;IAC5C,IAAIC,0BAA0B,CAACF;IAC/B,IAAIV,QAAQa,OAAO,IAAIb,QAAQc,UAAU,IAAId,QAAQW,cAAc,IAAIX,QAAQe,UAAU,EAAE;QACzFH,0BAA0B;IAC5B;IAEA,MAAMI,oBAAoB,MAAM1C,sBAAsB;QACpD2C,eAAehD;QACfiD,UAAUf;IACZ;IACA,MAAMgB,WAAWH,mBAAmBI,SAAS;IAE7C,IAAIC;IACJ,IAAIrB,QAAQsB,QAAQ,IAAI1B,sBAAsBI,QAAQsB,QAAQ,GAAG;QAC/DD,qBAAqB,MAAMxB,kBAAkBG,QAAQsB,QAAQ,EAAEtB,QAAQuB,aAAa;IACtF;IAEA,IAAIP,qBAAqBA,kBAAkBI,IAAI,KAAK,YAAYC,oBAAoB;QAClF,MAAM,IAAInC,UACR,CAAC,sEAAsE,EAAE8B,kBAAkBQ,IAAI,EAAE,EACjG;IAEJ;IAEA,MAAMC,gBAAgBzB,QAAQsB,QAAQ,GAAGvC,qBAAqBiB,QAAQsB,QAAQ,IAAI;IAElF,IAAItB,QAAQe,UAAU,EAAE;QACtBW,2BAA2B1B,SAAS;YAACyB;YAAeN;QAAQ;IAC9D;IAEAd,MAAMsB,KAAK;IACXtB,MAAMuB,GAAG,CAAC;QACRC,OAAO;YACLC,MAAM9B,QAAQ8B,IAAI;YAClBC,QAAQ/B,QAAQ+B,MAAM;YACtBrB;YACAsB,KAAKhC,QAAQgC,GAAG;YAChBC,KAAK,OAAOjC,QAAQiC,GAAG,KAAK,WAAWjC,QAAQiC,GAAG,GAAGC;YACrDC,MAAMnC,QAAQoC,WAAW;YACzB7B,aAAaP,QAAQO,WAAW;YAChCQ,YAAYf,QAAQe,UAAU;QAChC;QACAsB,MAAM;IACR;IAEA,MAAMC,SAAS,MAAM9C,QAAQQ,SAASE,QAAQG;IAE9C,IAAIkC,qBAAqB;IACzB,IAAIvB,qBAAqBA,kBAAkBI,IAAI,KAAK,UAAU;QAC5DmB,qBAAqB;IACvB;IACA,MAAMC,cAAc,OAAOxC,QAAQgC,GAAG,KAAK,WAAWhC,QAAQgC,GAAG,GAAGO;IAEpE,MAAM,EAACE,IAAI,EAAC,GAAG,MAAMC,oBAAoB1C,SAASE,QAAQG;IAC1D,IAAI,CAACoB,eAAe;QAClBvB,OAAO0B,GAAG,CAAC,GAAG7D,WAAW4E,OAAO,CAAC,2BAA2B,CAAC;QAC7DzC,OAAO0B,GAAG,CAAC;IACb;IAEA,IAAIgB;IACJ,IAAI5C,QAAQ6C,WAAW,EAAE;QACvBD,aAAa,MAAMnD,sBAAsB;YACvCsC,QAAQ/B,QAAQ+B,MAAM;YACtBe,mBAAmB9C,QAAQ6C,WAAW;YACtChC,SAASb,QAAQa,OAAO;YACxBJ,cAAcT,QAAQS,YAAY;YAClC6B;YACAG;YACA3B,YAAYd,QAAQc,UAAU;QAChC;IACF;IAEA,MAAM,EAACiC,WAAW,EAAEC,WAAW,EAAEC,cAAc,EAAEC,cAAc,EAAEC,SAAS,EAAC,GACzE,MAAMzD,kBAAkB;QACtBqC,QAAQ/B,QAAQ+B,MAAM;QACtBlB,SAASb,QAAQa,OAAO;QACxBF,gBAAgBX,QAAQW,cAAc;QACtCc;QACAmB;QACAnC,cAAcT,QAAQS,YAAY;QAClCP;QACAoC;QACA9B,SAASR,QAAQQ,OAAO;QACxBI;QACAP;QACAU,YAAYf,QAAQe,UAAU;QAC9B0B;QACA3B,YAAYd,QAAQc,UAAU;IAChC;IAEF,IAAId,QAAQ8B,IAAI,EAAE;QAChB5B,OAAO0B,GAAG,CAAC,GAAG7D,WAAW4E,OAAO,CAAC,+BAA+B,CAAC;QACjEzC,OAAO0B,GAAG,CAAC;QACX1B,OAAO0B,GAAG,CAAC,CAAC,YAAY,EAAE/D,UAAU,QAAQsF,YAAY;QACxDjD,OAAO0B,GAAG,CAAC,CAAC,SAAS,EAAE/D,UAAU,QAAQkF,cAAc;QACvD7C,OAAO0B,GAAG,CACR,CAAC,oFAAoF,EAAEuB,UAAU,EAAE,CAAC;QAEtG9C,MAAM+C,QAAQ;QACd;IACF;IAEA,IAAIC,WAAWlE,cAAca,QAAQsD,oBAAoB,EAAE;IAC3D,IAAInC,YAAY/B,aAAaY,QAAQe,UAAU,EAAEf,QAAQsD,oBAAoB,GAAG;QAC9ED,WAAW,MAAMlF;IACnB;IAEAkC,MAAMuB,GAAG,CAAC;QACRZ,mBAAmBA,mBAAmBQ;QACtC+B,gBAAgBF,WAAW,QAAQ;QACnChB,MAAM;IACR;IAEA,MAAMmB,cAActF,OAAO8E,YAAYS,WAAW,IAC/CC,UAAU,CAAC,QAAQ,KACnBA,UAAU,CAAC,eAAe;IAE7B,MAAMC,gBAAgBN;IAEtB,MAAMO,WAAW,MAAMrF,mBAAmB;QAACsF,UAAU;QAAO1D;IAAO;IAEnE,MAAM2D,aAAa,MAAMnE,qBAAqB;QAC5CgE;QACAG,YAAY9D,QAAQ8D,UAAU;QAC9BN;QACAzC,YAAYf,QAAQe,UAAU;QAC9BgD,QAAQC,QAAQhE,QAAQgC,GAAG;QAC3B7B;IACF;IAEA,0EAA0E;IAC1E,+EAA+E;IAC/E,MAAM8D,kBACJjE,QAAQkE,OAAO,KAAK,UAAUlE,QAAQmE,UAAU,KAAK,SACjD,EAAE,GACF,MAAMxF;IAEZ,MAAMyF,YAAY,MAAMxF,SAAS;QAC/ByF,SAASJ;QACTK,MAAMtE,QAAQkE,OAAO;QACrBC,YAAYnE,QAAQmE,UAAU;IAChC;IAEA9D,MAAMuB,GAAG,CAAC;QACR2C,mBAAmBH,UAAUG,iBAAiB;QAC9CN,iBAAiBG,UAAUH,eAAe;QAC1CO,SAASJ,UAAUI,OAAO;QAC1BnC,MAAM;IACR;IACA,IAAI+B,UAAUK,KAAK,EAAE;QACnBpE,MAAMoE,KAAK,CAACL,UAAUK,KAAK;IAC7B;IACA,MAAMC,gBAAgBN,UAAUG,iBAAiB;IAEjD,eAAeI;QACb,IAAIP,UAAUQ,eAAe,CAACC,MAAM,KAAK,GAAG;QAC5C,IAAI;YACF,MAAMC,eAAe,MAAMjG,YAAY;gBAACkG,QAAQX,UAAUQ,eAAe;YAAA;YACzEvE,MAAMuB,GAAG,CAAC;gBACRoD,iBAAiBF,aAAaE,eAAe;gBAC7CR,SAASM,aAAaN,OAAO;gBAC7BnC,MAAM;YACR;YACA,IAAIyC,aAAaL,KAAK,EAAE;gBACtBpE,MAAMoE,KAAK,CAACK,aAAaL,KAAK;YAChC;QACF,EAAE,OAAOA,OAAO;YACd,MAAMQ,MAAMR,iBAAiBS,QAAQT,QAAQ,IAAIS,MAAMC,OAAOV;YAC9D3E,MAAM,wCAAwCmF;YAC9C/E,OAAOkF,IAAI,CAAC,CAAC,uCAAuC,EAAEH,IAAII,OAAO,EAAE;YACnEhF,MAAMoE,KAAK,CAACQ;QACd;IACF;IAEA,MAAM,EAACK,wBAAwB,EAAC,GAAGlB;IACnC,IAAIkB,yBAAyBT,MAAM,GAAG,GAAG;QACvC,MAAMU,QACJD,yBAAyBT,MAAM,KAAK,IAChC,GAAGS,wBAAwB,CAAC,EAAE,CAAC,kCAAkC,CAAC,GAClE,GAAGA,yBAAyBT,MAAM,CAAC,0CAA0C,CAAC;QACpF7G,QAAQuH,OAAO5D,KAAK,GAAG6D,OAAO;IAChC;IAEA,IAAIrE,UAAU;QACZ,MAAMrC,8BAA8B;YAClCkC;YACAd;YACA4D;QACF;IACF;IAEA,IAAIT,UAAU;QACZ,MAAM/D,WAAW;YACfyD;YACA/B;YACAwB;YACAkC;YACA1E;YACAE;YACAiD;YACA9C;YACAF;QACF;QACA,MAAMwE;QACNtE,MAAM+C,QAAQ;QACd;IACF;IAEA,IAAIpD,QAAQgC,GAAG,EAAE;QACf,MAAMhD,sBAAsB;YAC1ByG,SAAS;gBACPC,SAAS3C;gBACT4C,YAAYxC;YACd;YACAyC,UAAUpD;YACVqD,WAAW7E;YACXY,KAAK;YACL1B;YACA4D;QACF;QACA,MAAMzE,wBAAwBa,QAAQ4D;QACtC,MAAMa;QACNtE,MAAM+C,QAAQ;QACd;IACF;IAEA,MAAM0C,eAAe;QACnBlC;QACAc;QACA1E;QACAkD;QACAhD;QACA4D;QACAzC;QACAmC;QACAnD;QACAF;IACF;IAEA,MAAOsB,CAAAA,gBACHxC,QAAQ;QAAC,GAAG6G,YAAY;QAAE/C;QAAaI;IAAS,KAChD5D,WAAW;QACT,GAAGuG,YAAY;QACf/C;QACAC;QACAC;QACAE;IACF,EAAC;IAEL,MAAMwB;IAENtE,MAAM+C,QAAQ;AAChB;AAEA,SAAS1B,2BACP1B,OAAoB,EACpB,EAACyB,aAAa,EAAEN,QAAQ,EAA8C;IAEtErB,MAAM;IAEN,IAAIE,QAAQ6C,WAAW,IAAI,CAAC7C,QAAQS,YAAY,EAAE;QAChD,MAAM,IAAIvB,UAAU,sEAAsE;IAC5F;IAEA,IAAIuC,eAAe;QACjB,IAAI,CAACzB,QAAQ8D,UAAU,EAAE;YACvB,MAAM,IAAI5E,UAAU,wDAAwD;QAC9E;QAEA,MAAM6G,iBAAiB/B,QAAQhE,QAAQQ,OAAO,IAAIR,QAAQ6C,WAAW;QAErE,IAAI,CAACkD,kBAAkB,CAAC/F,QAAQS,YAAY,EAAE;YAC5C,MAAM,IAAIvB,UACR,+EACE,4EACF;QAEJ;QAEA;IACF;IAEA,IAAI,CAACiC,YAAY,CAACnB,QAAQ8B,IAAI,IAAI,CAAC9B,QAAQ8D,UAAU,EAAE;QACrD,MAAM,IAAI5E,UAAU,wDAAwD;IAC9E;IAEA,IAAI,CAACc,QAAQQ,OAAO,IAAI,CAACR,QAAQ6C,WAAW,EAAE;QAC5C,MAAM,IAAI3D,UACR,oFACA;IAEJ;AACF;AAEA,eAAewD,oBACb1C,OAAoB,EACpBE,MAA6B,EAC7BG,KAA8D;IAE9D,MAAMoC,OAAO,MAAMjE;IAEnB,IAAIiE,MAAM;QACRpC,MAAMuB,GAAG,CAAC;YAACoE,iBAAiB;YAAM3D,MAAM;QAAO;QAC/CnC,OAAO0B,GAAG,CACR,GAAG7D,WAAW4E,OAAO,CAAC,sBAAsB,EAAEF,KAAKwD,KAAK,CAAC,OAAO,EAAExH,gBAAgBgE,KAAKyD,QAAQ,GAAG;QAEpG,OAAO;YAACzD;QAAI;IACd;IAEA,IAAIzC,QAAQe,UAAU,EAAE;QACtB,MAAM,IAAI7B,UACR,gFACA;IAEJ;IAEAmB,MAAMuB,GAAG,CAAC;QAACS,MAAM;IAAO;IAExB,IAAI;QACF,MAAM3D,MAAM;YACVwB;YACAI,WAAWD,MAAM8F,UAAU,CAAC;QAC9B;IACF,EAAE,OAAO1B,OAAO;QACd,MAAMY,UAAUZ,iBAAiBS,QAAQT,MAAMY,OAAO,GAAGF,OAAOV;QAChE,MAAM,IAAIvF,UAAU,CAAC,cAAc,EAAEmG,SAAS,EAAE;IAClD;IAEA,MAAMe,eAAe,MAAMhI;IAE3B8B,OAAO0B,GAAG,CACR,GAAG7D,WAAW4E,OAAO,CAAC,sBAAsB,EAAEyD,aAAaH,KAAK,CAAC,OAAO,EAAExH,gBAAgB2H,aAAaF,QAAQ,GAAG;IAEpH,OAAO;QAACzD,MAAM2D;IAAY;AAC5B"}
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