@sanity/cli 6.3.2 → 6.4.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.
Files changed (69) hide show
  1. package/README.md +16 -10
  2. package/dist/actions/init/bootstrapLocalTemplate.js +16 -1
  3. package/dist/actions/init/bootstrapLocalTemplate.js.map +1 -1
  4. package/dist/actions/init/initApp.js +72 -0
  5. package/dist/actions/init/initApp.js.map +1 -0
  6. package/dist/actions/init/initHelpers.js +37 -0
  7. package/dist/actions/init/initHelpers.js.map +1 -0
  8. package/dist/actions/init/initNextJs.js +246 -0
  9. package/dist/actions/init/initNextJs.js.map +1 -0
  10. package/dist/actions/init/initStudio.js +127 -0
  11. package/dist/actions/init/initStudio.js.map +1 -0
  12. package/dist/actions/init/scaffoldTemplate.js +114 -0
  13. package/dist/actions/init/scaffoldTemplate.js.map +1 -0
  14. package/dist/actions/init/templates/appQuickstart.js +2 -1
  15. package/dist/actions/init/templates/appQuickstart.js.map +1 -1
  16. package/dist/actions/init/templates/appSanityUi.js +2 -1
  17. package/dist/actions/init/templates/appSanityUi.js.map +1 -1
  18. package/dist/actions/init/templates/shopify.js +6 -6
  19. package/dist/actions/init/templates/shopify.js.map +1 -1
  20. package/dist/actions/init/templates/shopifyOnline.js +2 -2
  21. package/dist/actions/init/templates/shopifyOnline.js.map +1 -1
  22. package/dist/actions/mcp/detectAvailableEditors.js +16 -3
  23. package/dist/actions/mcp/detectAvailableEditors.js.map +1 -1
  24. package/dist/actions/mcp/editorConfigs.js +192 -132
  25. package/dist/actions/mcp/editorConfigs.js.map +1 -1
  26. package/dist/actions/mcp/setupMCP.js +4 -1
  27. package/dist/actions/mcp/setupMCP.js.map +1 -1
  28. package/dist/actions/mcp/writeMCPConfig.js +2 -2
  29. package/dist/actions/mcp/writeMCPConfig.js.map +1 -1
  30. package/dist/actions/schema/extractSchema.js +5 -7
  31. package/dist/actions/schema/extractSchema.js.map +1 -1
  32. package/dist/commands/datasets/copy.js +14 -0
  33. package/dist/commands/datasets/copy.js.map +1 -1
  34. package/dist/commands/init.js +149 -482
  35. package/dist/commands/init.js.map +1 -1
  36. package/dist/commands/mcp/configure.js +1 -1
  37. package/dist/commands/mcp/configure.js.map +1 -1
  38. package/dist/hooks/prerun/injectEnvVariables.js +3 -5
  39. package/dist/hooks/prerun/injectEnvVariables.js.map +1 -1
  40. package/dist/services/datasets.js +2 -1
  41. package/dist/services/datasets.js.map +1 -1
  42. package/dist/telemetry/init.telemetry.js.map +1 -1
  43. package/dist/util/packageManager/installationInfo/detectPackages.js +13 -7
  44. package/dist/util/packageManager/installationInfo/detectPackages.js.map +1 -1
  45. package/dist/util/update/fetchUpdateInfo.js +40 -0
  46. package/dist/util/update/fetchUpdateInfo.js.map +1 -0
  47. package/dist/util/update/fetchUpdateInfo.worker.js +19 -0
  48. package/dist/util/update/fetchUpdateInfo.worker.js.map +1 -0
  49. package/dist/util/update/getRunnerUpdateCommand.js +33 -0
  50. package/dist/util/update/getRunnerUpdateCommand.js.map +1 -0
  51. package/dist/util/update/getUpdateCommand.js +6 -7
  52. package/dist/util/update/getUpdateCommand.js.map +1 -1
  53. package/dist/util/update/packageRunner.js +10 -0
  54. package/dist/util/update/packageRunner.js.map +1 -0
  55. package/dist/util/update/resolveRunnerPackage.js +45 -0
  56. package/dist/util/update/resolveRunnerPackage.js.map +1 -0
  57. package/dist/util/update/resolveUpdateTarget.js +31 -0
  58. package/dist/util/update/resolveUpdateTarget.js.map +1 -0
  59. package/dist/util/update/showNotificationUpdate.js +8 -6
  60. package/dist/util/update/showNotificationUpdate.js.map +1 -1
  61. package/dist/util/update/updateChecker.js +73 -38
  62. package/dist/util/update/updateChecker.js.map +1 -1
  63. package/oclif.manifest.json +540 -525
  64. package/package.json +6 -6
  65. package/templates/app-quickstart/src/App.tsx +2 -2
  66. package/templates/app-sanity-ui/src/App.tsx +2 -2
  67. package/templates/shopify/schemaTypes/objects/hotspot/imageWithProductHotspotsType.ts +1 -1
  68. package/dist/util/update/fetchLatestVersion.js +0 -21
  69. package/dist/util/update/fetchLatestVersion.js.map +0 -1
@@ -0,0 +1,127 @@
1
+ import { styleText } from 'node:util';
2
+ import { getCliToken, subdebug } from '@sanity/cli-core';
3
+ import { confirm } from '@sanity/cli-core/ux';
4
+ import { ImportDatasetCommand } from '../../commands/datasets/import.js';
5
+ import { updateProjectInitializedAt } from '../../services/projects.js';
6
+ import { getPostInitMCPPrompt } from './initHelpers.js';
7
+ import { scaffoldAndInstall, selectTemplate } from './scaffoldTemplate.js';
8
+ const debug = subdebug('init');
9
+ async function promptForDatasetImport(message) {
10
+ return confirm({
11
+ default: true,
12
+ message: message || 'This template includes a sample dataset, would you like to use it?'
13
+ });
14
+ }
15
+ export async function initStudio({ autoUpdates, datasetName, defaults, displayName, error, git, importDataset, isFirstProject, mcpConfigured, noGit, organizationId, output, outputPath, overwriteFiles, packageManager, projectId, remoteTemplateInfo, sluggedName, template, templateToken, trace, typescript, unattended, workDir }) {
16
+ const { template: resolvedTemplate, templateName, useTypeScript } = await selectTemplate({
17
+ remoteTemplateInfo,
18
+ template,
19
+ trace,
20
+ typescript,
21
+ unattended
22
+ });
23
+ if (!remoteTemplateInfo && !resolvedTemplate) {
24
+ error(`Template "${templateName}" not found`, {
25
+ exit: 1
26
+ });
27
+ }
28
+ // If the template has a sample dataset, prompt the user whether or not we should import it
29
+ const shouldImport = resolvedTemplate?.datasetUrl && (importDataset ?? (!unattended && await promptForDatasetImport(resolvedTemplate.importPrompt)));
30
+ trace.log({
31
+ selectedOption: shouldImport ? 'yes' : 'no',
32
+ step: 'importTemplateDataset'
33
+ });
34
+ try {
35
+ await updateProjectInitializedAt(projectId);
36
+ } catch (err) {
37
+ // Non-critical update
38
+ debug('Failed to update cliInitializedAt metadata', err);
39
+ }
40
+ const { pkgManager } = await scaffoldAndInstall({
41
+ autoUpdates,
42
+ datasetName,
43
+ defaults,
44
+ displayName,
45
+ git,
46
+ noGit,
47
+ organizationId,
48
+ output,
49
+ outputPath,
50
+ overwriteFiles,
51
+ packageManager,
52
+ projectId,
53
+ remoteTemplateInfo,
54
+ sluggedName,
55
+ templateName,
56
+ templateToken,
57
+ trace,
58
+ unattended,
59
+ useTypeScript,
60
+ workDir
61
+ });
62
+ // Prompt for dataset import (if a dataset is defined)
63
+ if (shouldImport && resolvedTemplate?.datasetUrl) {
64
+ const token = await getCliToken();
65
+ if (!token) {
66
+ return error('Authentication required to import dataset', {
67
+ exit: 1
68
+ });
69
+ }
70
+ await ImportDatasetCommand.run([
71
+ resolvedTemplate.datasetUrl,
72
+ '--project-id',
73
+ projectId,
74
+ '--dataset',
75
+ datasetName,
76
+ '--token',
77
+ token,
78
+ '--missing'
79
+ ], {
80
+ root: outputPath
81
+ });
82
+ output.log('');
83
+ output.log('If you want to delete the imported data, use');
84
+ output.log(` ${styleText('cyan', `npx sanity dataset delete ${datasetName}`)}`);
85
+ output.log('and create a new clean dataset with');
86
+ output.log(` ${styleText('cyan', `npx sanity dataset create <name>`)}\n`);
87
+ }
88
+ const devCommandMap = {
89
+ bun: 'bun dev',
90
+ manual: 'npm run dev',
91
+ npm: 'npm run dev',
92
+ pnpm: 'pnpm dev',
93
+ yarn: 'yarn dev'
94
+ };
95
+ const devCommand = devCommandMap[pkgManager];
96
+ const isCurrentDir = outputPath === process.cwd();
97
+ const goToProjectDir = `\n(${styleText('cyan', `cd ${outputPath}`)} to navigate to your new project directory)`;
98
+ //output for Studios here
99
+ output.log(`\u2705 ${styleText([
100
+ 'green',
101
+ 'bold'
102
+ ], 'Success!')} Your Studio has been created.`);
103
+ if (!isCurrentDir) output.log(goToProjectDir);
104
+ output.log(`\nGet started by running ${styleText('cyan', devCommand)} to launch your Studio's development server`);
105
+ if (mcpConfigured && mcpConfigured.length > 0) {
106
+ const message = await getPostInitMCPPrompt(mcpConfigured);
107
+ output.log(`\n${message}`);
108
+ output.log(`\nLearn more: ${styleText('cyan', 'https://mcp.sanity.io')}`);
109
+ output.log(`\nHave feedback? Tell us in the community: ${styleText('cyan', 'https://www.sanity.io/community/join')}`);
110
+ }
111
+ output.log('\n');
112
+ output.log(`Other helpful commands:`);
113
+ output.log(`npx sanity docs browse to open the documentation in a browser`);
114
+ output.log(`npx sanity manage to open the project settings in a browser`);
115
+ output.log(`npx sanity help to explore the CLI manual`);
116
+ if (isFirstProject) {
117
+ trace.log({
118
+ selectedOption: 'yes',
119
+ step: 'sendCommunityInvite'
120
+ });
121
+ const DISCORD_INVITE_LINK = 'https://www.sanity.io/community/join';
122
+ output.log(`\nJoin the Sanity community: ${styleText('cyan', DISCORD_INVITE_LINK)}`);
123
+ output.log('We look forward to seeing you there!\n');
124
+ }
125
+ }
126
+
127
+ //# sourceMappingURL=initStudio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/init/initStudio.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {getCliToken, type Output, subdebug, type TelemetryUserProperties} from '@sanity/cli-core'\nimport {confirm} from '@sanity/cli-core/ux'\nimport {type TelemetryTrace} from '@sanity/telemetry'\n\nimport {ImportDatasetCommand} from '../../commands/datasets/import.js'\nimport {updateProjectInitializedAt} from '../../services/projects.js'\nimport {type InitStepResult} from '../../telemetry/init.telemetry.js'\nimport {type PackageManager} from '../../util/packageManager/packageManagerChoice.js'\nimport {type EditorName} from '../mcp/editorConfigs.js'\nimport {getPostInitMCPPrompt} from './initHelpers.js'\nimport {type RepoInfo} from './remoteTemplate.js'\nimport {scaffoldAndInstall, selectTemplate} from './scaffoldTemplate.js'\n\nconst debug = subdebug('init')\n\nasync function promptForDatasetImport(message?: string): Promise<boolean> {\n return confirm({\n default: true,\n message: message || 'This template includes a sample dataset, would you like to use it?',\n })\n}\n\nexport async function initStudio({\n autoUpdates,\n datasetName,\n defaults,\n displayName,\n error,\n git,\n importDataset,\n isFirstProject,\n mcpConfigured,\n noGit,\n organizationId,\n output,\n outputPath,\n overwriteFiles,\n packageManager,\n projectId,\n remoteTemplateInfo,\n sluggedName,\n template,\n templateToken,\n trace,\n typescript,\n unattended,\n workDir,\n}: {\n autoUpdates: boolean\n datasetName: string\n defaults: {projectName: string}\n displayName: string\n error: Output['error']\n git?: boolean | string\n importDataset?: boolean\n isFirstProject: boolean\n mcpConfigured: EditorName[]\n noGit?: boolean\n organizationId: string | undefined\n output: Output\n outputPath: string\n overwriteFiles?: boolean\n packageManager?: string\n projectId: string\n remoteTemplateInfo: RepoInfo | undefined\n sluggedName: string\n template?: string\n templateToken?: string\n trace: TelemetryTrace<TelemetryUserProperties, InitStepResult>\n typescript?: boolean\n unattended: boolean\n workDir: string\n}): Promise<void> {\n const {\n template: resolvedTemplate,\n templateName,\n useTypeScript,\n } = await selectTemplate({\n remoteTemplateInfo,\n template,\n trace,\n typescript,\n unattended,\n })\n\n if (!remoteTemplateInfo && !resolvedTemplate) {\n error(`Template \"${templateName}\" not found`, {exit: 1})\n }\n\n // If the template has a sample dataset, prompt the user whether or not we should import it\n const shouldImport =\n resolvedTemplate?.datasetUrl &&\n (importDataset ??\n (!unattended && (await promptForDatasetImport(resolvedTemplate.importPrompt))))\n\n trace.log({\n selectedOption: shouldImport ? 'yes' : 'no',\n step: 'importTemplateDataset',\n })\n\n try {\n await updateProjectInitializedAt(projectId)\n } catch (err) {\n // Non-critical update\n debug('Failed to update cliInitializedAt metadata', err)\n }\n\n const {pkgManager} = await scaffoldAndInstall({\n autoUpdates,\n datasetName,\n defaults,\n displayName,\n git,\n noGit,\n organizationId,\n output,\n outputPath,\n overwriteFiles,\n packageManager,\n projectId,\n remoteTemplateInfo,\n sluggedName,\n templateName,\n templateToken,\n trace,\n unattended,\n useTypeScript,\n workDir,\n })\n\n // Prompt for dataset import (if a dataset is defined)\n if (shouldImport && resolvedTemplate?.datasetUrl) {\n const token = await getCliToken()\n if (!token) {\n return error('Authentication required to import dataset', {exit: 1})\n }\n await ImportDatasetCommand.run(\n [\n resolvedTemplate.datasetUrl,\n '--project-id',\n projectId,\n '--dataset',\n datasetName,\n '--token',\n token,\n '--missing',\n ],\n {\n root: outputPath,\n },\n )\n\n output.log('')\n output.log('If you want to delete the imported data, use')\n output.log(` ${styleText('cyan', `npx sanity dataset delete ${datasetName}`)}`)\n output.log('and create a new clean dataset with')\n output.log(` ${styleText('cyan', `npx sanity dataset create <name>`)}\\n`)\n }\n\n const devCommandMap: Record<PackageManager, string> = {\n bun: 'bun dev',\n manual: 'npm run dev',\n npm: 'npm run dev',\n pnpm: 'pnpm dev',\n yarn: 'yarn dev',\n }\n const devCommand = devCommandMap[pkgManager]\n\n const isCurrentDir = outputPath === process.cwd()\n const goToProjectDir = `\\n(${styleText('cyan', `cd ${outputPath}`)} to navigate to your new project directory)`\n\n //output for Studios here\n output.log(`\\u2705 ${styleText(['green', 'bold'], 'Success!')} Your Studio has been created.`)\n if (!isCurrentDir) output.log(goToProjectDir)\n output.log(\n `\\nGet started by running ${styleText('cyan', devCommand)} to launch your Studio's development server`,\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 output.log('\\n')\n output.log(`Other helpful commands:`)\n output.log(`npx sanity docs browse to open the documentation in a browser`)\n output.log(`npx sanity manage to open the project settings in a browser`)\n output.log(`npx sanity help to explore the CLI manual`)\n\n if (isFirstProject) {\n trace.log({selectedOption: 'yes', step: 'sendCommunityInvite'})\n\n const DISCORD_INVITE_LINK = 'https://www.sanity.io/community/join'\n\n output.log(`\\nJoin the Sanity community: ${styleText('cyan', DISCORD_INVITE_LINK)}`)\n output.log('We look forward to seeing you there!\\n')\n }\n}\n"],"names":["styleText","getCliToken","subdebug","confirm","ImportDatasetCommand","updateProjectInitializedAt","getPostInitMCPPrompt","scaffoldAndInstall","selectTemplate","debug","promptForDatasetImport","message","default","initStudio","autoUpdates","datasetName","defaults","displayName","error","git","importDataset","isFirstProject","mcpConfigured","noGit","organizationId","output","outputPath","overwriteFiles","packageManager","projectId","remoteTemplateInfo","sluggedName","template","templateToken","trace","typescript","unattended","workDir","resolvedTemplate","templateName","useTypeScript","exit","shouldImport","datasetUrl","importPrompt","log","selectedOption","step","err","pkgManager","token","run","root","devCommandMap","bun","manual","npm","pnpm","yarn","devCommand","isCurrentDir","process","cwd","goToProjectDir","length","DISCORD_INVITE_LINK"],"mappings":"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"}
@@ -0,0 +1,114 @@
1
+ import { select } from '@sanity/cli-core/ux';
2
+ import { promptForTypeScript } from '../../prompts/init/promptForTypescript.js';
3
+ import { installDeclaredPackages } from '../../util/packageManager/installPackages.js';
4
+ import { bootstrapTemplate } from './bootstrapTemplate.js';
5
+ import { tryGitInit } from './git.js';
6
+ import { writeStagingEnvIfNeeded } from './initHelpers.js';
7
+ import { resolvePackageManager } from './resolvePackageManager.js';
8
+ import templates from './templates/index.js';
9
+ async function promptForTemplate(params) {
10
+ const defaultTemplate = params.unattended || params.template ? params.template || 'clean' : null;
11
+ if (defaultTemplate) {
12
+ return defaultTemplate;
13
+ }
14
+ return select({
15
+ choices: [
16
+ {
17
+ name: 'Clean project with no predefined schema types',
18
+ value: 'clean'
19
+ },
20
+ {
21
+ name: 'Blog (schema)',
22
+ value: 'blog'
23
+ },
24
+ {
25
+ name: 'E-commerce (Shopify)',
26
+ value: 'shopify'
27
+ },
28
+ {
29
+ name: 'Movie project (schema + sample data)',
30
+ value: 'moviedb'
31
+ }
32
+ ],
33
+ message: 'Select project template'
34
+ });
35
+ }
36
+ export async function selectTemplate({ remoteTemplateInfo, template, trace, typescript, unattended }) {
37
+ const templateName = await promptForTemplate({
38
+ template,
39
+ unattended
40
+ });
41
+ trace.log({
42
+ selectedOption: templateName,
43
+ step: 'selectProjectTemplate'
44
+ });
45
+ const resolvedTemplate = templates[templateName];
46
+ let useTypeScript = typescript;
47
+ if (!remoteTemplateInfo && resolvedTemplate && resolvedTemplate.typescriptOnly === true) {
48
+ useTypeScript = true;
49
+ } else if (!unattended && typescript === undefined) {
50
+ useTypeScript = await promptForTypeScript();
51
+ trace.log({
52
+ selectedOption: useTypeScript ? 'yes' : 'no',
53
+ step: 'useTypeScript'
54
+ });
55
+ }
56
+ return {
57
+ template: resolvedTemplate,
58
+ templateName,
59
+ useTypeScript
60
+ };
61
+ }
62
+ export async function scaffoldAndInstall({ autoUpdates, datasetName, defaults, displayName, git, noGit, organizationId, output, outputPath, overwriteFiles, packageManager, projectId, remoteTemplateInfo, sluggedName, templateName, templateToken, trace, unattended, useTypeScript, workDir }) {
63
+ try {
64
+ await bootstrapTemplate({
65
+ autoUpdates,
66
+ bearerToken: templateToken,
67
+ dataset: datasetName,
68
+ organizationId,
69
+ output,
70
+ outputPath,
71
+ overwriteFiles: overwriteFiles,
72
+ packageName: sluggedName,
73
+ projectId,
74
+ projectName: displayName || defaults.projectName,
75
+ remoteTemplateInfo,
76
+ templateName,
77
+ useTypeScript: useTypeScript
78
+ });
79
+ } catch (error) {
80
+ if (error instanceof Error) {
81
+ throw error;
82
+ }
83
+ throw new Error(String(error), {
84
+ cause: error
85
+ });
86
+ }
87
+ const pkgManager = await resolvePackageManager({
88
+ interactive: !unattended,
89
+ output,
90
+ packageManager: packageManager,
91
+ targetDir: outputPath
92
+ });
93
+ trace.log({
94
+ selectedOption: pkgManager,
95
+ step: 'selectPackageManager'
96
+ });
97
+ // Now for the slow part... installing dependencies
98
+ await installDeclaredPackages(outputPath, pkgManager, {
99
+ output,
100
+ workDir
101
+ });
102
+ const useGit = !noGit && (git === undefined || Boolean(git));
103
+ const commitMessage = git;
104
+ await writeStagingEnvIfNeeded(output, outputPath);
105
+ // Try initializing a git repository
106
+ if (useGit) {
107
+ tryGitInit(outputPath, typeof commitMessage === 'string' ? commitMessage : undefined);
108
+ }
109
+ return {
110
+ pkgManager
111
+ };
112
+ }
113
+
114
+ //# sourceMappingURL=scaffoldTemplate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/init/scaffoldTemplate.ts"],"sourcesContent":["import {type Output, type TelemetryUserProperties} from '@sanity/cli-core'\nimport {select} from '@sanity/cli-core/ux'\nimport {type TelemetryTrace} from '@sanity/telemetry'\n\nimport {promptForTypeScript} from '../../prompts/init/promptForTypescript.js'\nimport {type InitStepResult} from '../../telemetry/init.telemetry.js'\nimport {installDeclaredPackages} from '../../util/packageManager/installPackages.js'\nimport {type PackageManager} from '../../util/packageManager/packageManagerChoice.js'\nimport {bootstrapTemplate} from './bootstrapTemplate.js'\nimport {tryGitInit} from './git.js'\nimport {writeStagingEnvIfNeeded} from './initHelpers.js'\nimport {type RepoInfo} from './remoteTemplate.js'\nimport {resolvePackageManager} from './resolvePackageManager.js'\nimport templates from './templates/index.js'\nimport {type ProjectTemplate} from './types.js'\n\ninterface SelectedTemplate {\n template: ProjectTemplate | undefined\n templateName: string\n useTypeScript: boolean | undefined\n}\n\nasync function promptForTemplate(params: {\n template?: string\n unattended: boolean\n}): Promise<string> {\n const defaultTemplate = params.unattended || params.template ? params.template || 'clean' : null\n if (defaultTemplate) {\n return defaultTemplate\n }\n\n return select({\n choices: [\n {\n name: 'Clean project with no predefined schema types',\n value: 'clean',\n },\n {\n name: 'Blog (schema)',\n value: 'blog',\n },\n {\n name: 'E-commerce (Shopify)',\n value: 'shopify',\n },\n {\n name: 'Movie project (schema + sample data)',\n value: 'moviedb',\n },\n ],\n message: 'Select project template',\n })\n}\n\nexport async function selectTemplate({\n remoteTemplateInfo,\n template,\n trace,\n typescript,\n unattended,\n}: {\n remoteTemplateInfo: RepoInfo | undefined\n template?: string\n trace: TelemetryTrace<TelemetryUserProperties, InitStepResult>\n typescript?: boolean\n unattended: boolean\n}): Promise<SelectedTemplate> {\n const templateName = await promptForTemplate({template, unattended})\n trace.log({\n selectedOption: templateName,\n step: 'selectProjectTemplate',\n })\n\n const resolvedTemplate = templates[templateName]\n\n let useTypeScript = typescript\n if (!remoteTemplateInfo && resolvedTemplate && resolvedTemplate.typescriptOnly === true) {\n useTypeScript = true\n } else if (!unattended && typescript === undefined) {\n useTypeScript = await promptForTypeScript()\n trace.log({\n selectedOption: useTypeScript ? 'yes' : 'no',\n step: 'useTypeScript',\n })\n }\n\n return {\n template: resolvedTemplate,\n templateName,\n useTypeScript,\n }\n}\n\nexport async function scaffoldAndInstall({\n autoUpdates,\n datasetName,\n defaults,\n displayName,\n git,\n noGit,\n organizationId,\n output,\n outputPath,\n overwriteFiles,\n packageManager,\n projectId,\n remoteTemplateInfo,\n sluggedName,\n templateName,\n templateToken,\n trace,\n unattended,\n useTypeScript,\n workDir,\n}: {\n autoUpdates: boolean\n datasetName: string\n defaults: {projectName: string}\n displayName: string\n git?: boolean | string\n noGit?: boolean\n organizationId: string | undefined\n output: Output\n outputPath: string\n overwriteFiles?: boolean\n packageManager?: string\n projectId: string\n remoteTemplateInfo: RepoInfo | undefined\n sluggedName: string\n templateName: string\n templateToken?: string\n trace: TelemetryTrace<TelemetryUserProperties, InitStepResult>\n unattended: boolean\n useTypeScript: boolean | undefined\n workDir: string\n}): Promise<{pkgManager: PackageManager}> {\n try {\n await bootstrapTemplate({\n autoUpdates,\n bearerToken: templateToken,\n dataset: datasetName,\n organizationId,\n output,\n outputPath,\n overwriteFiles: overwriteFiles as boolean,\n packageName: sluggedName,\n projectId,\n projectName: displayName || defaults.projectName,\n remoteTemplateInfo,\n templateName,\n useTypeScript: useTypeScript as boolean,\n })\n } catch (error) {\n if (error instanceof Error) {\n throw error\n }\n throw new Error(String(error), {cause: error})\n }\n\n const pkgManager = await resolvePackageManager({\n interactive: !unattended,\n output,\n packageManager: packageManager as PackageManager,\n targetDir: outputPath,\n })\n\n trace.log({\n selectedOption: pkgManager,\n step: 'selectPackageManager',\n })\n\n // Now for the slow part... installing dependencies\n await installDeclaredPackages(outputPath, pkgManager, {\n output,\n workDir,\n })\n\n const useGit = !noGit && (git === undefined || Boolean(git))\n const commitMessage = git\n await writeStagingEnvIfNeeded(output, outputPath)\n\n // Try initializing a git repository\n if (useGit) {\n tryGitInit(outputPath, typeof commitMessage === 'string' ? commitMessage : undefined)\n }\n\n return {pkgManager}\n}\n"],"names":["select","promptForTypeScript","installDeclaredPackages","bootstrapTemplate","tryGitInit","writeStagingEnvIfNeeded","resolvePackageManager","templates","promptForTemplate","params","defaultTemplate","unattended","template","choices","name","value","message","selectTemplate","remoteTemplateInfo","trace","typescript","templateName","log","selectedOption","step","resolvedTemplate","useTypeScript","typescriptOnly","undefined","scaffoldAndInstall","autoUpdates","datasetName","defaults","displayName","git","noGit","organizationId","output","outputPath","overwriteFiles","packageManager","projectId","sluggedName","templateToken","workDir","bearerToken","dataset","packageName","projectName","error","Error","String","cause","pkgManager","interactive","targetDir","useGit","Boolean","commitMessage"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  const appTemplate = {
2
2
  entry: './src/App.tsx',
3
- type: 'module'
3
+ type: 'module',
4
+ typescriptOnly: true
4
5
  };
5
6
  export default appTemplate;
6
7
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/actions/init/templates/appQuickstart.ts"],"sourcesContent":["import {type ProjectTemplate} from '../types.js'\n\nconst appTemplate: ProjectTemplate = {\n entry: './src/App.tsx',\n type: 'module',\n}\n\nexport default appTemplate\n"],"names":["appTemplate","entry","type"],"mappings":"AAEA,MAAMA,cAA+B;IACnCC,OAAO;IACPC,MAAM;AACR;AAEA,eAAeF,YAAW"}
1
+ {"version":3,"sources":["../../../../src/actions/init/templates/appQuickstart.ts"],"sourcesContent":["import {type ProjectTemplate} from '../types.js'\n\nconst appTemplate: ProjectTemplate = {\n entry: './src/App.tsx',\n type: 'module',\n typescriptOnly: true,\n}\n\nexport default appTemplate\n"],"names":["appTemplate","entry","type","typescriptOnly"],"mappings":"AAEA,MAAMA,cAA+B;IACnCC,OAAO;IACPC,MAAM;IACNC,gBAAgB;AAClB;AAEA,eAAeH,YAAW"}
@@ -4,7 +4,8 @@ const appSanityUiTemplate = {
4
4
  'styled-components': '^6.1.18'
5
5
  },
6
6
  entry: './src/App.tsx',
7
- type: 'module'
7
+ type: 'module',
8
+ typescriptOnly: true
8
9
  };
9
10
  export default appSanityUiTemplate;
10
11
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/actions/init/templates/appSanityUi.ts"],"sourcesContent":["import {type ProjectTemplate} from '../types.js'\n\nconst appSanityUiTemplate: ProjectTemplate = {\n dependencies: {\n '@sanity/ui': '^3',\n 'styled-components': '^6.1.18',\n },\n entry: './src/App.tsx',\n type: 'module',\n}\n\nexport default appSanityUiTemplate\n"],"names":["appSanityUiTemplate","dependencies","entry","type"],"mappings":"AAEA,MAAMA,sBAAuC;IAC3CC,cAAc;QACZ,cAAc;QACd,qBAAqB;IACvB;IACAC,OAAO;IACPC,MAAM;AACR;AAEA,eAAeH,oBAAmB"}
1
+ {"version":3,"sources":["../../../../src/actions/init/templates/appSanityUi.ts"],"sourcesContent":["import {type ProjectTemplate} from '../types.js'\n\nconst appSanityUiTemplate: ProjectTemplate = {\n dependencies: {\n '@sanity/ui': '^3',\n 'styled-components': '^6.1.18',\n },\n entry: './src/App.tsx',\n type: 'module',\n typescriptOnly: true,\n}\n\nexport default appSanityUiTemplate\n"],"names":["appSanityUiTemplate","dependencies","entry","type","typescriptOnly"],"mappings":"AAEA,MAAMA,sBAAuC;IAC3CC,cAAc;QACZ,cAAc;QACd,qBAAqB;IACvB;IACAC,OAAO;IACPC,MAAM;IACNC,gBAAgB;AAClB;AAEA,eAAeJ,oBAAmB"}
@@ -56,14 +56,14 @@ export default defineConfig({
56
56
  const shopifyTemplate = {
57
57
  configTemplate,
58
58
  dependencies: {
59
- '@sanity/asset-utils': '^1.3.0',
60
- '@sanity/color-input': '^3.0.2',
61
- '@sanity/icons': '^2.11.0',
62
- '@sanity/ui': '^2.0.0',
59
+ '@sanity/asset-utils': '^2.3.0',
60
+ '@sanity/color-input': '^6.0.4',
61
+ '@sanity/icons': '^3.7.4',
62
+ '@sanity/ui': '^3.1.14',
63
63
  'lodash.get': '^4.4.2',
64
64
  'pluralize-esm': '^9.0.2',
65
- 'sanity-plugin-hotspot-array': '^1.0.1',
66
- 'sanity-plugin-media': '^2.0.5',
65
+ 'sanity-plugin-hotspot-array': '^3.0.2',
66
+ 'sanity-plugin-media': '^4.1.1',
67
67
  slug: '^8.2.2'
68
68
  },
69
69
  devDependencies: {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/actions/init/templates/shopify.ts"],"sourcesContent":["import {type ProjectTemplate} from '../types.js'\n\nconst configTemplate = `\nimport {defineConfig, isDev} from 'sanity'\n\nimport {structureTool} from 'sanity/structure'\nimport {schemaTypes} from './schemaTypes'\nimport {structure} from './structure'\n\nimport {visionTool} from '@sanity/vision'\nimport {colorInput} from '@sanity/color-input'\nimport {imageHotspotArrayPlugin} from 'sanity-plugin-hotspot-array'\nimport {media, mediaAssetSource} from 'sanity-plugin-media'\nimport {customDocumentActions} from './plugins/customDocumentActions'\nimport Navbar from './components/studio/Navbar'\n\nconst devOnlyPlugins = [visionTool()]\n\nexport default defineConfig({\n name: '%sourceName%',\n title: '%projectName%',\n\n projectId: '%projectId%',\n dataset: '%dataset%',\n\n plugins: [\n structureTool({structure}),\n colorInput(),\n imageHotspotArrayPlugin(),\n customDocumentActions(),\n media(),\n ...(isDev ? devOnlyPlugins : []),\n ],\n\n schema: {\n types: schemaTypes,\n },\n\n form: {\n file: {\n assetSources: (previousAssetSources) => {\n return previousAssetSources.filter((assetSource) => assetSource !== mediaAssetSource)\n },\n },\n image: {\n assetSources: (previousAssetSources) => {\n return previousAssetSources.filter((assetSource) => assetSource === mediaAssetSource)\n },\n },\n },\n\n studio: {\n components: {\n navbar: Navbar,\n },\n },\n})`\n\nconst shopifyTemplate: ProjectTemplate = {\n configTemplate,\n dependencies: {\n '@sanity/asset-utils': '^1.3.0',\n '@sanity/color-input': '^3.0.2',\n '@sanity/icons': '^2.11.0',\n '@sanity/ui': '^2.0.0',\n 'lodash.get': '^4.4.2',\n 'pluralize-esm': '^9.0.2',\n 'sanity-plugin-hotspot-array': '^1.0.1',\n 'sanity-plugin-media': '^2.0.5',\n slug: '^8.2.2',\n },\n devDependencies: {\n '@portabletext/types': '^2.0.2',\n '@types/lodash.get': '^4.4.7',\n '@types/slug': '^5.0.3',\n },\n}\nexport default shopifyTemplate\n"],"names":["configTemplate","shopifyTemplate","dependencies","slug","devDependencies"],"mappings":"AAEA,MAAMA,iBAAiB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsDtB,CAAC;AAEH,MAAMC,kBAAmC;IACvCD;IACAE,cAAc;QACZ,uBAAuB;QACvB,uBAAuB;QACvB,iBAAiB;QACjB,cAAc;QACd,cAAc;QACd,iBAAiB;QACjB,+BAA+B;QAC/B,uBAAuB;QACvBC,MAAM;IACR;IACAC,iBAAiB;QACf,uBAAuB;QACvB,qBAAqB;QACrB,eAAe;IACjB;AACF;AACA,eAAeH,gBAAe"}
1
+ {"version":3,"sources":["../../../../src/actions/init/templates/shopify.ts"],"sourcesContent":["import {type ProjectTemplate} from '../types.js'\n\nconst configTemplate = `\nimport {defineConfig, isDev} from 'sanity'\n\nimport {structureTool} from 'sanity/structure'\nimport {schemaTypes} from './schemaTypes'\nimport {structure} from './structure'\n\nimport {visionTool} from '@sanity/vision'\nimport {colorInput} from '@sanity/color-input'\nimport {imageHotspotArrayPlugin} from 'sanity-plugin-hotspot-array'\nimport {media, mediaAssetSource} from 'sanity-plugin-media'\nimport {customDocumentActions} from './plugins/customDocumentActions'\nimport Navbar from './components/studio/Navbar'\n\nconst devOnlyPlugins = [visionTool()]\n\nexport default defineConfig({\n name: '%sourceName%',\n title: '%projectName%',\n\n projectId: '%projectId%',\n dataset: '%dataset%',\n\n plugins: [\n structureTool({structure}),\n colorInput(),\n imageHotspotArrayPlugin(),\n customDocumentActions(),\n media(),\n ...(isDev ? devOnlyPlugins : []),\n ],\n\n schema: {\n types: schemaTypes,\n },\n\n form: {\n file: {\n assetSources: (previousAssetSources) => {\n return previousAssetSources.filter((assetSource) => assetSource !== mediaAssetSource)\n },\n },\n image: {\n assetSources: (previousAssetSources) => {\n return previousAssetSources.filter((assetSource) => assetSource === mediaAssetSource)\n },\n },\n },\n\n studio: {\n components: {\n navbar: Navbar,\n },\n },\n})`\n\nconst shopifyTemplate: ProjectTemplate = {\n configTemplate,\n dependencies: {\n '@sanity/asset-utils': '^2.3.0',\n '@sanity/color-input': '^6.0.4',\n '@sanity/icons': '^3.7.4',\n '@sanity/ui': '^3.1.14',\n 'lodash.get': '^4.4.2',\n 'pluralize-esm': '^9.0.2',\n 'sanity-plugin-hotspot-array': '^3.0.2',\n 'sanity-plugin-media': '^4.1.1',\n slug: '^8.2.2',\n },\n devDependencies: {\n '@portabletext/types': '^2.0.2',\n '@types/lodash.get': '^4.4.7',\n '@types/slug': '^5.0.3',\n },\n}\nexport default shopifyTemplate\n"],"names":["configTemplate","shopifyTemplate","dependencies","slug","devDependencies"],"mappings":"AAEA,MAAMA,iBAAiB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsDtB,CAAC;AAEH,MAAMC,kBAAmC;IACvCD;IACAE,cAAc;QACZ,uBAAuB;QACvB,uBAAuB;QACvB,iBAAiB;QACjB,cAAc;QACd,cAAc;QACd,iBAAiB;QACjB,+BAA+B;QAC/B,uBAAuB;QACvBC,MAAM;IACR;IACAC,iBAAiB;QACf,uBAAuB;QACvB,qBAAqB;QACrB,eAAe;IACjB;AACF;AACA,eAAeH,gBAAe"}
@@ -35,8 +35,8 @@ const shopifyTemplate = {
35
35
  configTemplate,
36
36
  dependencies: {
37
37
  '@portabletext/toolkit': '^2.0.1',
38
- '@sanity/icons': '^2.11.0',
39
- '@sanity/ui': '^2.0.0',
38
+ '@sanity/icons': '^3.7.4',
39
+ '@sanity/ui': '^3.1.14',
40
40
  '@types/lodash.get': '^4.4.7',
41
41
  'lodash.get': '^4.4.2',
42
42
  'pluralize-esm': '^9.0.4',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/actions/init/templates/shopifyOnline.ts"],"sourcesContent":["import {type ProjectTemplate} from '../types.js'\n\nconst configTemplate = `\nimport {shopifyAssets} from 'sanity-plugin-shopify-assets'\nimport {defineConfig, isDev} from 'sanity'\nimport {structureTool} from 'sanity/structure'\nimport {visionTool} from '@sanity/vision'\nimport {shopifyDocumentActions} from './plugins/shopifyDocumentActions'\nimport {schemaTypes} from './schemaTypes'\nimport {structure} from './structure'\nimport {SHOPIFY_STORE_ID} from './constants'\n\nconst devOnlyPlugins = [visionTool()]\n\nexport default defineConfig({\n name: '%sourceName%',\n title: '%projectName%',\n\n projectId: '%projectId%',\n dataset: '%dataset%',\n\n plugins: [\n structureTool({structure}),\n shopifyDocumentActions(),\n shopifyAssets({\n shopifyDomain: SHOPIFY_STORE_ID,\n }),\n ...(isDev ? devOnlyPlugins : []),\n ],\n\n schema: {\n types: schemaTypes,\n },\n})\n`\n\nconst shopifyTemplate: ProjectTemplate = {\n configTemplate,\n dependencies: {\n '@portabletext/toolkit': '^2.0.1',\n '@sanity/icons': '^2.11.0',\n '@sanity/ui': '^2.0.0',\n '@types/lodash.get': '^4.4.7',\n 'lodash.get': '^4.4.2',\n 'pluralize-esm': '^9.0.4',\n 'sanity-plugin-shopify-assets': '^1.1.0',\n },\n typescriptOnly: true,\n}\nexport default shopifyTemplate\n"],"names":["configTemplate","shopifyTemplate","dependencies","typescriptOnly"],"mappings":"AAEA,MAAMA,iBAAiB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCxB,CAAC;AAED,MAAMC,kBAAmC;IACvCD;IACAE,cAAc;QACZ,yBAAyB;QACzB,iBAAiB;QACjB,cAAc;QACd,qBAAqB;QACrB,cAAc;QACd,iBAAiB;QACjB,gCAAgC;IAClC;IACAC,gBAAgB;AAClB;AACA,eAAeF,gBAAe"}
1
+ {"version":3,"sources":["../../../../src/actions/init/templates/shopifyOnline.ts"],"sourcesContent":["import {type ProjectTemplate} from '../types.js'\n\nconst configTemplate = `\nimport {shopifyAssets} from 'sanity-plugin-shopify-assets'\nimport {defineConfig, isDev} from 'sanity'\nimport {structureTool} from 'sanity/structure'\nimport {visionTool} from '@sanity/vision'\nimport {shopifyDocumentActions} from './plugins/shopifyDocumentActions'\nimport {schemaTypes} from './schemaTypes'\nimport {structure} from './structure'\nimport {SHOPIFY_STORE_ID} from './constants'\n\nconst devOnlyPlugins = [visionTool()]\n\nexport default defineConfig({\n name: '%sourceName%',\n title: '%projectName%',\n\n projectId: '%projectId%',\n dataset: '%dataset%',\n\n plugins: [\n structureTool({structure}),\n shopifyDocumentActions(),\n shopifyAssets({\n shopifyDomain: SHOPIFY_STORE_ID,\n }),\n ...(isDev ? devOnlyPlugins : []),\n ],\n\n schema: {\n types: schemaTypes,\n },\n})\n`\n\nconst shopifyTemplate: ProjectTemplate = {\n configTemplate,\n dependencies: {\n '@portabletext/toolkit': '^2.0.1',\n '@sanity/icons': '^3.7.4',\n '@sanity/ui': '^3.1.14',\n '@types/lodash.get': '^4.4.7',\n 'lodash.get': '^4.4.2',\n 'pluralize-esm': '^9.0.4',\n 'sanity-plugin-shopify-assets': '^1.1.0',\n },\n typescriptOnly: true,\n}\nexport default shopifyTemplate\n"],"names":["configTemplate","shopifyTemplate","dependencies","typescriptOnly"],"mappings":"AAEA,MAAMA,iBAAiB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCxB,CAAC;AAED,MAAMC,kBAAmC;IACvCD;IACAE,cAAc;QACZ,yBAAyB;QACzB,iBAAiB;QACjB,cAAc;QACd,qBAAqB;QACrB,cAAc;QACd,iBAAiB;QACjB,gCAAgC;IAClC;IACAC,gBAAgB;AAClB;AACA,eAAeF,gBAAe"}
@@ -3,7 +3,7 @@ import fs from 'node:fs/promises';
3
3
  import { subdebug } from '@sanity/cli-core';
4
4
  import { parse as parseJsonc } from 'jsonc-parser';
5
5
  import { parse as parseToml } from 'smol-toml';
6
- import { EDITOR_CONFIGS } from './editorConfigs.js';
6
+ import { createDetectionEnv, EDITOR_CONFIGS } from './editorConfigs.js';
7
7
  const debug = subdebug('mcp:detectAvailableEditors');
8
8
  /**
9
9
  * Safely parse config file content
@@ -66,6 +66,15 @@ const debug = subdebug('mcp:detectAvailableEditors');
66
66
  if (configured && typeof sanityConfig === 'object' && sanityConfig !== null) {
67
67
  existingToken = readToken(sanityConfig);
68
68
  }
69
+ const { oauthOnly } = EDITOR_CONFIGS[name];
70
+ if (configured && !existingToken && oauthOnly) {
71
+ return {
72
+ authStatus: 'valid',
73
+ configPath,
74
+ configured,
75
+ name
76
+ };
77
+ }
69
78
  return {
70
79
  configPath,
71
80
  configured,
@@ -80,13 +89,17 @@ const debug = subdebug('mcp:detectAvailableEditors');
80
89
  /**
81
90
  * Detect which editors are installed and have parseable configs.
82
91
  * Editors with unparseable configs are skipped to avoid data loss.
83
- */ export async function detectAvailableEditors() {
92
+ *
93
+ * Accepts an optional `DetectionEnv` for testability. When omitted,
94
+ * uses the real process/OS environment.
95
+ */ export async function detectAvailableEditors(env) {
96
+ const ctx = env ?? createDetectionEnv();
84
97
  // Detect all editors in parallel to avoid stacking timeouts —
85
98
  // CLI-based editors (Claude Code, Codex CLI, OpenCode) each have a
86
99
  // 5s execa timeout, so sequential detection can add ~15s on machines
87
100
  // where none are installed.
88
101
  const results = await Promise.all(Object.entries(EDITOR_CONFIGS).map(async ([name, config])=>{
89
- const configPath = await config.detect();
102
+ const configPath = await config.detect(ctx);
90
103
  if (!configPath) return null;
91
104
  return checkEditorConfig(name, configPath);
92
105
  }));
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/mcp/detectAvailableEditors.ts"],"sourcesContent":["import {existsSync} from 'node:fs'\nimport fs from 'node:fs/promises'\n\nimport {subdebug} from '@sanity/cli-core'\nimport {type ParseError, parse as parseJsonc} from 'jsonc-parser'\nimport {parse as parseToml} from 'smol-toml'\n\nimport {EDITOR_CONFIGS, type EditorName} from './editorConfigs.js'\nimport {type Editor} from './types.js'\n\nconst debug = subdebug('mcp:detectAvailableEditors')\n\ninterface MCPConfig {\n [key: string]: Record<string, unknown> | undefined\n}\n\n/**\n * Safely parse config file content\n * Returns parsed config or null if unparseable\n */\nfunction parseConfig(content: string, format: 'jsonc' | 'toml'): MCPConfig | null {\n const trimmed = content.trim()\n if (trimmed === '') {\n return {} // Empty file - safe to write, treat as empty config\n }\n\n if (format === 'toml') {\n try {\n const parsed = parseToml(content)\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return null\n }\n\n return parsed as MCPConfig\n } catch {\n return null\n }\n }\n\n const errors: ParseError[] = []\n const parsed = parseJsonc(content, errors, {allowTrailingComma: true})\n\n if (errors.length > 0 || typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return null // Parse failed\n }\n\n return parsed as MCPConfig\n}\n\n/**\n * Check if an editor's config is usable and whether Sanity MCP is already configured.\n * If configured, extracts the existing auth token.\n * Returns null only if config exists but can't be parsed (to avoid data loss).\n */\nasync function checkEditorConfig(name: EditorName, configPath: string): Promise<Editor | null> {\n const {configKey, format, readToken} = EDITOR_CONFIGS[name]\n\n // Config file doesn't exist - can create it\n if (!existsSync(configPath)) {\n return {configPath, configured: false, name}\n }\n\n // Config exists - try to parse it\n try {\n const content = await fs.readFile(configPath, 'utf8')\n const config = parseConfig(content, format)\n\n if (config === null) {\n debug('Skipping %s: could not parse %s', name, configPath)\n return null // Can't parse - skip this editor\n }\n\n // Check if Sanity MCP is already configured\n const sanityConfig = config[configKey]?.Sanity\n const configured = Boolean(sanityConfig)\n\n // Extract existing token if configured\n let existingToken: string | undefined\n if (configured && typeof sanityConfig === 'object' && sanityConfig !== null) {\n existingToken = readToken(sanityConfig as Record<string, unknown>)\n }\n\n return {configPath, configured, existingToken, name}\n } catch (err) {\n debug('Skipping %s: could not read %s: %s', name, configPath, err)\n return null\n }\n}\n\n/**\n * Detect which editors are installed and have parseable configs.\n * Editors with unparseable configs are skipped to avoid data loss.\n */\nexport async function detectAvailableEditors(): Promise<Editor[]> {\n // Detect all editors in parallel to avoid stacking timeouts —\n // CLI-based editors (Claude Code, Codex CLI, OpenCode) each have a\n // 5s execa timeout, so sequential detection can add ~15s on machines\n // where none are installed.\n const results = await Promise.all(\n Object.entries(EDITOR_CONFIGS).map(async ([name, config]) => {\n const configPath = await config.detect()\n if (!configPath) return null\n return checkEditorConfig(name as EditorName, configPath)\n }),\n )\n\n return results.filter((editor): editor is Editor => editor !== null)\n}\n"],"names":["existsSync","fs","subdebug","parse","parseJsonc","parseToml","EDITOR_CONFIGS","debug","parseConfig","content","format","trimmed","trim","parsed","Array","isArray","errors","allowTrailingComma","length","checkEditorConfig","name","configPath","configKey","readToken","configured","readFile","config","sanityConfig","Sanity","Boolean","existingToken","err","detectAvailableEditors","results","Promise","all","Object","entries","map","detect","filter","editor"],"mappings":"AAAA,SAAQA,UAAU,QAAO,UAAS;AAClC,OAAOC,QAAQ,mBAAkB;AAEjC,SAAQC,QAAQ,QAAO,mBAAkB;AACzC,SAAyBC,SAASC,UAAU,QAAO,eAAc;AACjE,SAAQD,SAASE,SAAS,QAAO,YAAW;AAE5C,SAAQC,cAAc,QAAwB,qBAAoB;AAGlE,MAAMC,QAAQL,SAAS;AAMvB;;;CAGC,GACD,SAASM,YAAYC,OAAe,EAAEC,MAAwB;IAC5D,MAAMC,UAAUF,QAAQG,IAAI;IAC5B,IAAID,YAAY,IAAI;QAClB,OAAO,CAAC,EAAE,oDAAoD;;IAChE;IAEA,IAAID,WAAW,QAAQ;QACrB,IAAI;YACF,MAAMG,SAASR,UAAUI;YACzB,IAAI,OAAOI,WAAW,YAAYA,WAAW,QAAQC,MAAMC,OAAO,CAACF,SAAS;gBAC1E,OAAO;YACT;YAEA,OAAOA;QACT,EAAE,OAAM;YACN,OAAO;QACT;IACF;IAEA,MAAMG,SAAuB,EAAE;IAC/B,MAAMH,SAAST,WAAWK,SAASO,QAAQ;QAACC,oBAAoB;IAAI;IAEpE,IAAID,OAAOE,MAAM,GAAG,KAAK,OAAOL,WAAW,YAAYA,WAAW,QAAQC,MAAMC,OAAO,CAACF,SAAS;QAC/F,OAAO,KAAK,eAAe;;IAC7B;IAEA,OAAOA;AACT;AAEA;;;;CAIC,GACD,eAAeM,kBAAkBC,IAAgB,EAAEC,UAAkB;IACnE,MAAM,EAACC,SAAS,EAAEZ,MAAM,EAAEa,SAAS,EAAC,GAAGjB,cAAc,CAACc,KAAK;IAE3D,4CAA4C;IAC5C,IAAI,CAACpB,WAAWqB,aAAa;QAC3B,OAAO;YAACA;YAAYG,YAAY;YAAOJ;QAAI;IAC7C;IAEA,kCAAkC;IAClC,IAAI;QACF,MAAMX,UAAU,MAAMR,GAAGwB,QAAQ,CAACJ,YAAY;QAC9C,MAAMK,SAASlB,YAAYC,SAASC;QAEpC,IAAIgB,WAAW,MAAM;YACnBnB,MAAM,mCAAmCa,MAAMC;YAC/C,OAAO,KAAK,iCAAiC;;QAC/C;QAEA,4CAA4C;QAC5C,MAAMM,eAAeD,MAAM,CAACJ,UAAU,EAAEM;QACxC,MAAMJ,aAAaK,QAAQF;QAE3B,uCAAuC;QACvC,IAAIG;QACJ,IAAIN,cAAc,OAAOG,iBAAiB,YAAYA,iBAAiB,MAAM;YAC3EG,gBAAgBP,UAAUI;QAC5B;QAEA,OAAO;YAACN;YAAYG;YAAYM;YAAeV;QAAI;IACrD,EAAE,OAAOW,KAAK;QACZxB,MAAM,sCAAsCa,MAAMC,YAAYU;QAC9D,OAAO;IACT;AACF;AAEA;;;CAGC,GACD,OAAO,eAAeC;IACpB,8DAA8D;IAC9D,mEAAmE;IACnE,qEAAqE;IACrE,4BAA4B;IAC5B,MAAMC,UAAU,MAAMC,QAAQC,GAAG,CAC/BC,OAAOC,OAAO,CAAC/B,gBAAgBgC,GAAG,CAAC,OAAO,CAAClB,MAAMM,OAAO;QACtD,MAAML,aAAa,MAAMK,OAAOa,MAAM;QACtC,IAAI,CAAClB,YAAY,OAAO;QACxB,OAAOF,kBAAkBC,MAAoBC;IAC/C;IAGF,OAAOY,QAAQO,MAAM,CAAC,CAACC,SAA6BA,WAAW;AACjE"}
1
+ {"version":3,"sources":["../../../src/actions/mcp/detectAvailableEditors.ts"],"sourcesContent":["import {existsSync} from 'node:fs'\nimport fs from 'node:fs/promises'\n\nimport {subdebug} from '@sanity/cli-core'\nimport {type ParseError, parse as parseJsonc} from 'jsonc-parser'\nimport {parse as parseToml} from 'smol-toml'\n\nimport {\n createDetectionEnv,\n type DetectionEnv,\n EDITOR_CONFIGS,\n type EditorName,\n} from './editorConfigs.js'\nimport {type Editor} from './types.js'\n\nconst debug = subdebug('mcp:detectAvailableEditors')\n\ninterface MCPConfig {\n [key: string]: Record<string, unknown> | undefined\n}\n\n/**\n * Safely parse config file content\n * Returns parsed config or null if unparseable\n */\nfunction parseConfig(content: string, format: 'jsonc' | 'toml'): MCPConfig | null {\n const trimmed = content.trim()\n if (trimmed === '') {\n return {} // Empty file - safe to write, treat as empty config\n }\n\n if (format === 'toml') {\n try {\n const parsed = parseToml(content)\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return null\n }\n\n return parsed as MCPConfig\n } catch {\n return null\n }\n }\n\n const errors: ParseError[] = []\n const parsed = parseJsonc(content, errors, {allowTrailingComma: true})\n\n if (errors.length > 0 || typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return null // Parse failed\n }\n\n return parsed as MCPConfig\n}\n\n/**\n * Check if an editor's config is usable and whether Sanity MCP is already configured.\n * If configured, extracts the existing auth token.\n * Returns null only if config exists but can't be parsed (to avoid data loss).\n */\nasync function checkEditorConfig(name: EditorName, configPath: string): Promise<Editor | null> {\n const {configKey, format, readToken} = EDITOR_CONFIGS[name]\n\n // Config file doesn't exist - can create it\n if (!existsSync(configPath)) {\n return {configPath, configured: false, name}\n }\n\n // Config exists - try to parse it\n try {\n const content = await fs.readFile(configPath, 'utf8')\n const config = parseConfig(content, format)\n\n if (config === null) {\n debug('Skipping %s: could not parse %s', name, configPath)\n return null // Can't parse - skip this editor\n }\n\n // Check if Sanity MCP is already configured\n const sanityConfig = config[configKey]?.Sanity\n const configured = Boolean(sanityConfig)\n\n // Extract existing token if configured\n let existingToken: string | undefined\n if (configured && typeof sanityConfig === 'object' && sanityConfig !== null) {\n existingToken = readToken(sanityConfig as Record<string, unknown>)\n }\n\n const {oauthOnly} = EDITOR_CONFIGS[name]\n if (configured && !existingToken && oauthOnly) {\n return {authStatus: 'valid', configPath, configured, name}\n }\n\n return {configPath, configured, existingToken, name}\n } catch (err) {\n debug('Skipping %s: could not read %s: %s', name, configPath, err)\n return null\n }\n}\n\n/**\n * Detect which editors are installed and have parseable configs.\n * Editors with unparseable configs are skipped to avoid data loss.\n *\n * Accepts an optional `DetectionEnv` for testability. When omitted,\n * uses the real process/OS environment.\n */\nexport async function detectAvailableEditors(env?: DetectionEnv): Promise<Editor[]> {\n const ctx = env ?? createDetectionEnv()\n\n // Detect all editors in parallel to avoid stacking timeouts —\n // CLI-based editors (Claude Code, Codex CLI, OpenCode) each have a\n // 5s execa timeout, so sequential detection can add ~15s on machines\n // where none are installed.\n const results = await Promise.all(\n Object.entries(EDITOR_CONFIGS).map(async ([name, config]) => {\n const configPath = await config.detect(ctx)\n if (!configPath) return null\n return checkEditorConfig(name as EditorName, configPath)\n }),\n )\n\n return results.filter((editor): editor is Editor => editor !== null)\n}\n"],"names":["existsSync","fs","subdebug","parse","parseJsonc","parseToml","createDetectionEnv","EDITOR_CONFIGS","debug","parseConfig","content","format","trimmed","trim","parsed","Array","isArray","errors","allowTrailingComma","length","checkEditorConfig","name","configPath","configKey","readToken","configured","readFile","config","sanityConfig","Sanity","Boolean","existingToken","oauthOnly","authStatus","err","detectAvailableEditors","env","ctx","results","Promise","all","Object","entries","map","detect","filter","editor"],"mappings":"AAAA,SAAQA,UAAU,QAAO,UAAS;AAClC,OAAOC,QAAQ,mBAAkB;AAEjC,SAAQC,QAAQ,QAAO,mBAAkB;AACzC,SAAyBC,SAASC,UAAU,QAAO,eAAc;AACjE,SAAQD,SAASE,SAAS,QAAO,YAAW;AAE5C,SACEC,kBAAkB,EAElBC,cAAc,QAET,qBAAoB;AAG3B,MAAMC,QAAQN,SAAS;AAMvB;;;CAGC,GACD,SAASO,YAAYC,OAAe,EAAEC,MAAwB;IAC5D,MAAMC,UAAUF,QAAQG,IAAI;IAC5B,IAAID,YAAY,IAAI;QAClB,OAAO,CAAC,EAAE,oDAAoD;;IAChE;IAEA,IAAID,WAAW,QAAQ;QACrB,IAAI;YACF,MAAMG,SAAST,UAAUK;YACzB,IAAI,OAAOI,WAAW,YAAYA,WAAW,QAAQC,MAAMC,OAAO,CAACF,SAAS;gBAC1E,OAAO;YACT;YAEA,OAAOA;QACT,EAAE,OAAM;YACN,OAAO;QACT;IACF;IAEA,MAAMG,SAAuB,EAAE;IAC/B,MAAMH,SAASV,WAAWM,SAASO,QAAQ;QAACC,oBAAoB;IAAI;IAEpE,IAAID,OAAOE,MAAM,GAAG,KAAK,OAAOL,WAAW,YAAYA,WAAW,QAAQC,MAAMC,OAAO,CAACF,SAAS;QAC/F,OAAO,KAAK,eAAe;;IAC7B;IAEA,OAAOA;AACT;AAEA;;;;CAIC,GACD,eAAeM,kBAAkBC,IAAgB,EAAEC,UAAkB;IACnE,MAAM,EAACC,SAAS,EAAEZ,MAAM,EAAEa,SAAS,EAAC,GAAGjB,cAAc,CAACc,KAAK;IAE3D,4CAA4C;IAC5C,IAAI,CAACrB,WAAWsB,aAAa;QAC3B,OAAO;YAACA;YAAYG,YAAY;YAAOJ;QAAI;IAC7C;IAEA,kCAAkC;IAClC,IAAI;QACF,MAAMX,UAAU,MAAMT,GAAGyB,QAAQ,CAACJ,YAAY;QAC9C,MAAMK,SAASlB,YAAYC,SAASC;QAEpC,IAAIgB,WAAW,MAAM;YACnBnB,MAAM,mCAAmCa,MAAMC;YAC/C,OAAO,KAAK,iCAAiC;;QAC/C;QAEA,4CAA4C;QAC5C,MAAMM,eAAeD,MAAM,CAACJ,UAAU,EAAEM;QACxC,MAAMJ,aAAaK,QAAQF;QAE3B,uCAAuC;QACvC,IAAIG;QACJ,IAAIN,cAAc,OAAOG,iBAAiB,YAAYA,iBAAiB,MAAM;YAC3EG,gBAAgBP,UAAUI;QAC5B;QAEA,MAAM,EAACI,SAAS,EAAC,GAAGzB,cAAc,CAACc,KAAK;QACxC,IAAII,cAAc,CAACM,iBAAiBC,WAAW;YAC7C,OAAO;gBAACC,YAAY;gBAASX;gBAAYG;gBAAYJ;YAAI;QAC3D;QAEA,OAAO;YAACC;YAAYG;YAAYM;YAAeV;QAAI;IACrD,EAAE,OAAOa,KAAK;QACZ1B,MAAM,sCAAsCa,MAAMC,YAAYY;QAC9D,OAAO;IACT;AACF;AAEA;;;;;;CAMC,GACD,OAAO,eAAeC,uBAAuBC,GAAkB;IAC7D,MAAMC,MAAMD,OAAO9B;IAEnB,8DAA8D;IAC9D,mEAAmE;IACnE,qEAAqE;IACrE,4BAA4B;IAC5B,MAAMgC,UAAU,MAAMC,QAAQC,GAAG,CAC/BC,OAAOC,OAAO,CAACnC,gBAAgBoC,GAAG,CAAC,OAAO,CAACtB,MAAMM,OAAO;QACtD,MAAML,aAAa,MAAMK,OAAOiB,MAAM,CAACP;QACvC,IAAI,CAACf,YAAY,OAAO;QACxB,OAAOF,kBAAkBC,MAAoBC;IAC/C;IAGF,OAAOgB,QAAQO,MAAM,CAAC,CAACC,SAA6BA,WAAW;AACjE"}