@sanity/cli 6.5.3 → 6.7.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 +18 -5
- package/bin/run.js +3 -1
- package/dist/actions/auth/login/login.js +39 -13
- package/dist/actions/auth/login/login.js.map +1 -1
- package/dist/actions/auth/login/validateToken.js +39 -0
- package/dist/actions/auth/login/validateToken.js.map +1 -0
- package/dist/actions/build/buildApp.js +4 -5
- package/dist/actions/build/buildApp.js.map +1 -1
- package/dist/actions/build/buildStaticFiles.js +12 -4
- package/dist/actions/build/buildStaticFiles.js.map +1 -1
- package/dist/actions/build/buildStudio.js +6 -14
- package/dist/actions/build/buildStudio.js.map +1 -1
- package/dist/actions/build/{getStudioEnvironmentVariables.js → getEnvironmentVariables.js} +15 -19
- package/dist/actions/build/getEnvironmentVariables.js.map +1 -0
- package/dist/actions/deploy/deployStudio.js +1 -2
- package/dist/actions/deploy/deployStudio.js.map +1 -1
- package/dist/actions/deploy/deployStudioSchemasAndManifests.js +1 -2
- package/dist/actions/deploy/deployStudioSchemasAndManifests.js.map +1 -1
- package/dist/actions/deploy/deployStudioSchemasAndManifests.worker.js +1 -1
- package/dist/actions/deploy/deployStudioSchemasAndManifests.worker.js.map +1 -1
- package/dist/actions/dev/startStudioDevServer.js +1 -1
- package/dist/actions/dev/startStudioDevServer.js.map +1 -1
- package/dist/actions/documents/validateDocuments.worker.js +1 -2
- package/dist/actions/documents/validateDocuments.worker.js.map +1 -1
- package/dist/actions/graphql/SchemaError.js +1 -1
- package/dist/actions/graphql/SchemaError.js.map +1 -1
- package/dist/actions/init/initAction.js +32 -1
- package/dist/actions/init/initAction.js.map +1 -1
- package/dist/actions/init/scaffoldTemplate.js +32 -18
- package/dist/actions/init/scaffoldTemplate.js.map +1 -1
- package/dist/actions/init/templates/index.js +2 -0
- package/dist/actions/init/templates/index.js.map +1 -1
- package/dist/actions/init/templates/pageBuilder.js +32 -0
- package/dist/actions/init/templates/pageBuilder.js.map +1 -0
- package/dist/actions/init/types.js +2 -1
- package/dist/actions/init/types.js.map +1 -1
- package/dist/actions/manifest/extractManifest.js +1 -1
- package/dist/actions/manifest/extractManifest.js.map +1 -1
- package/dist/actions/manifest/extractManifest.worker.js +1 -1
- package/dist/actions/manifest/extractManifest.worker.js.map +1 -1
- package/dist/actions/mcp/editorConfigs.js +51 -11
- package/dist/actions/mcp/editorConfigs.js.map +1 -1
- package/dist/actions/mcp/promptForMCPSetup.js +16 -11
- package/dist/actions/mcp/promptForMCPSetup.js.map +1 -1
- package/dist/actions/mcp/setupMCP.js +180 -61
- package/dist/actions/mcp/setupMCP.js.map +1 -1
- package/dist/actions/mcp/types.js.map +1 -1
- package/dist/actions/schema/deploySchemas.js +1 -1
- package/dist/actions/schema/deploySchemas.js.map +1 -1
- package/dist/actions/schema/extractSanityWorkspace.worker.js +1 -1
- package/dist/actions/schema/extractSanityWorkspace.worker.js.map +1 -1
- package/dist/actions/schema/extractSchema.js +1 -4
- package/dist/actions/schema/extractSchema.js.map +1 -1
- package/dist/actions/schema/extractSchemaWatcher.js +1 -4
- package/dist/actions/schema/extractSchemaWatcher.js.map +1 -1
- package/dist/actions/schema/getExtractOptions.js +8 -18
- package/dist/actions/schema/getExtractOptions.js.map +1 -1
- package/dist/actions/schema/types.js +0 -6
- package/dist/actions/schema/types.js.map +1 -1
- package/dist/actions/schema/validateAction.js +1 -1
- package/dist/actions/schema/validateAction.js.map +1 -1
- package/dist/actions/schema/validateSchema.worker.js +1 -2
- package/dist/actions/schema/validateSchema.worker.js.map +1 -1
- package/dist/actions/schema/watchExtractSchema.js +1 -1
- package/dist/actions/schema/watchExtractSchema.js.map +1 -1
- package/dist/actions/skills/readSkillState.js +54 -0
- package/dist/actions/skills/readSkillState.js.map +1 -0
- package/dist/actions/skills/setupSkills.js +73 -0
- package/dist/actions/skills/setupSkills.js.map +1 -0
- package/dist/commands/build.js +9 -22
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/cors/add.js +5 -5
- package/dist/commands/cors/add.js.map +1 -1
- package/dist/commands/datasets/export.js +9 -0
- package/dist/commands/datasets/export.js.map +1 -1
- package/dist/commands/docs/read.js +33 -12
- package/dist/commands/docs/read.js.map +1 -1
- package/dist/commands/init.js +12 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/login.js +28 -5
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/manifest/extract.js +1 -2
- package/dist/commands/manifest/extract.js.map +1 -1
- package/dist/commands/mcp/configure.js +2 -1
- package/dist/commands/mcp/configure.js.map +1 -1
- package/dist/commands/schemas/deploy.js +1 -2
- package/dist/commands/schemas/deploy.js.map +1 -1
- package/dist/exports/_internal.d.ts +2 -1
- package/dist/exports/_internal.js +1 -1
- package/dist/exports/_internal.js.map +1 -1
- package/dist/server/devServer.js +25 -4
- package/dist/server/devServer.js.map +1 -1
- package/dist/server/previewServer.js +1 -1
- package/dist/server/previewServer.js.map +1 -1
- package/dist/services/mcp.js +1 -1
- package/dist/services/mcp.js.map +1 -1
- package/dist/telemetry/init.telemetry.js.map +1 -1
- package/oclif.manifest.json +237 -205
- package/package.json +15 -16
- package/templates/page-builder/README.md +9 -0
- package/templates/page-builder/schemaTypes/hero.js +31 -0
- package/templates/page-builder/schemaTypes/index.js +19 -0
- package/templates/page-builder/static/.gitkeep +0 -0
- package/dist/actions/build/buildDebug.js +0 -4
- package/dist/actions/build/buildDebug.js.map +0 -1
- package/dist/actions/build/buildVendorDependencies.js +0 -149
- package/dist/actions/build/buildVendorDependencies.js.map +0 -1
- package/dist/actions/build/checkStudioDependencyVersions.js +0 -155
- package/dist/actions/build/checkStudioDependencyVersions.js.map +0 -1
- package/dist/actions/build/createExternalFromImportMap.js +0 -11
- package/dist/actions/build/createExternalFromImportMap.js.map +0 -1
- package/dist/actions/build/decorateIndexWithAutoGeneratedWarning.js +0 -13
- package/dist/actions/build/decorateIndexWithAutoGeneratedWarning.js.map +0 -1
- package/dist/actions/build/decorateIndexWithBridgeScript.js +0 -17
- package/dist/actions/build/decorateIndexWithBridgeScript.js.map +0 -1
- package/dist/actions/build/decorateIndexWithStagingScript.js +0 -16
- package/dist/actions/build/decorateIndexWithStagingScript.js.map +0 -1
- package/dist/actions/build/getAppEnvVars.js +0 -9
- package/dist/actions/build/getAppEnvVars.js.map +0 -1
- package/dist/actions/build/getEntryModule.js +0 -46
- package/dist/actions/build/getEntryModule.js.map +0 -1
- package/dist/actions/build/getPossibleDocumentComponentLocations.js +0 -11
- package/dist/actions/build/getPossibleDocumentComponentLocations.js.map +0 -1
- package/dist/actions/build/getStudioEnvVars.js +0 -9
- package/dist/actions/build/getStudioEnvVars.js.map +0 -1
- package/dist/actions/build/getStudioEnvironmentVariables.js.map +0 -1
- package/dist/actions/build/getViteConfig.js +0 -219
- package/dist/actions/build/getViteConfig.js.map +0 -1
- package/dist/actions/build/normalizeBasePath.js +0 -9
- package/dist/actions/build/normalizeBasePath.js.map +0 -1
- package/dist/actions/build/renderDocument.js +0 -50
- package/dist/actions/build/renderDocument.js.map +0 -1
- package/dist/actions/build/renderDocument.worker.js +0 -9
- package/dist/actions/build/renderDocument.worker.js.map +0 -1
- package/dist/actions/build/renderDocumentWorker/addTimestampImportMapScriptToHtml.js +0 -79
- package/dist/actions/build/renderDocumentWorker/addTimestampImportMapScriptToHtml.js.map +0 -1
- package/dist/actions/build/renderDocumentWorker/components/BasicDocument.js +0 -61
- package/dist/actions/build/renderDocumentWorker/components/BasicDocument.js.map +0 -1
- package/dist/actions/build/renderDocumentWorker/components/DefaultDocument.js +0 -165
- package/dist/actions/build/renderDocumentWorker/components/DefaultDocument.js.map +0 -1
- package/dist/actions/build/renderDocumentWorker/components/Favicons.js +0 -28
- package/dist/actions/build/renderDocumentWorker/components/Favicons.js.map +0 -1
- package/dist/actions/build/renderDocumentWorker/components/GlobalErrorHandler.js +0 -178
- package/dist/actions/build/renderDocumentWorker/components/GlobalErrorHandler.js.map +0 -1
- package/dist/actions/build/renderDocumentWorker/components/NoJavascript.js +0 -51
- package/dist/actions/build/renderDocumentWorker/components/NoJavascript.js.map +0 -1
- package/dist/actions/build/renderDocumentWorker/getDocumentComponent.js +0 -41
- package/dist/actions/build/renderDocumentWorker/getDocumentComponent.js.map +0 -1
- package/dist/actions/build/renderDocumentWorker/getDocumentHtml.js +0 -55
- package/dist/actions/build/renderDocumentWorker/getDocumentHtml.js.map +0 -1
- package/dist/actions/build/renderDocumentWorker/renderDocumentWorker.js +0 -31
- package/dist/actions/build/renderDocumentWorker/renderDocumentWorker.js.map +0 -1
- package/dist/actions/build/renderDocumentWorker/tryLoadDocumentComponent.js +0 -30
- package/dist/actions/build/renderDocumentWorker/tryLoadDocumentComponent.js.map +0 -1
- package/dist/actions/build/renderDocumentWorker/types.js +0 -5
- package/dist/actions/build/renderDocumentWorker/types.js.map +0 -1
- package/dist/actions/build/writeSanityRuntime.js +0 -64
- package/dist/actions/build/writeSanityRuntime.js.map +0 -1
- package/dist/actions/docs/normalizeDocsPath.js +0 -15
- package/dist/actions/docs/normalizeDocsPath.js.map +0 -1
- package/dist/actions/schema/extractSanitySchema.worker.js +0 -33
- package/dist/actions/schema/extractSanitySchema.worker.js.map +0 -1
- package/dist/actions/schema/formatSchemaValidation.js +0 -78
- package/dist/actions/schema/formatSchemaValidation.js.map +0 -1
- package/dist/actions/schema/matchSchemaPattern.js +0 -22
- package/dist/actions/schema/matchSchemaPattern.js.map +0 -1
- package/dist/actions/schema/runSchemaExtraction.js +0 -39
- package/dist/actions/schema/runSchemaExtraction.js.map +0 -1
- package/dist/actions/schema/utils/SchemaExtractionError.js +0 -10
- package/dist/actions/schema/utils/SchemaExtractionError.js.map +0 -1
- package/dist/actions/schema/utils/extractValidationFromSchemaError.js +0 -12
- package/dist/actions/schema/utils/extractValidationFromSchemaError.js.map +0 -1
- package/dist/constants.js +0 -8
- package/dist/constants.js.map +0 -1
- package/dist/server/vite/plugin-sanity-build-entries.js +0 -67
- package/dist/server/vite/plugin-sanity-build-entries.js.map +0 -1
- package/dist/server/vite/plugin-sanity-favicons.js +0 -72
- package/dist/server/vite/plugin-sanity-favicons.js.map +0 -1
- package/dist/server/vite/plugin-sanity-runtime-rewrite.js +0 -18
- package/dist/server/vite/plugin-sanity-runtime-rewrite.js.map +0 -1
- package/dist/server/vite/plugin-schema-extraction.js +0 -201
- package/dist/server/vite/plugin-schema-extraction.js.map +0 -1
- package/dist/telemetry/build.telemetry.js +0 -13
- package/dist/telemetry/build.telemetry.js.map +0 -1
- package/dist/telemetry/extractSchema.telemetry.js +0 -18
- package/dist/telemetry/extractSchema.telemetry.js.map +0 -1
- package/dist/util/getWorkspace.js +0 -18
- package/dist/util/getWorkspace.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/mcp/editorConfigs.ts"],"sourcesContent":["import {existsSync} from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\n\nimport {execa} from 'execa'\n\nimport {MCP_SERVER_URL} from '../../services/mcp.js'\n\n/**\n * Environment abstraction for editor detection.\n *\n * Detect functions receive this instead of using module-level imports, making\n * each function independently testable without global mocks.\n */\nexport interface DetectionEnv {\n env: Record<string, string | undefined>\n /** Run a CLI command to check if a tool is installed. Rejects on failure. */\n execCommand: (cmd: string, args: string[]) => Promise<void>\n existsSync: (p: string) => boolean\n homedir: string\n platform: NodeJS.Platform\n}\n\n/** Create the real detection environment backed by process/OS globals. */\nexport function createDetectionEnv(): DetectionEnv {\n return {\n env: process.env,\n execCommand: (cmd, args) => execa(cmd, args, {stdio: 'pipe', timeout: 5000}).then(() => {}),\n existsSync,\n homedir: os.homedir(),\n platform: process.platform,\n }\n}\n\ninterface EditorConfig {\n /** Builds the server config with API token. If oauthOnly is true, the token is not used */\n buildServerConfig: (token: string) => Record<string, unknown>\n configKey: string\n /** Returns the config file path if editor is detected, null otherwise */\n detect: (env: DetectionEnv) => Promise<string | null>\n format: 'jsonc' | 'toml'\n /** Extracts the auth token from a parsed Sanity server config block */\n readToken: (serverConfig: Record<string, unknown>) => string | undefined\n\n /** If true, this editor uses OAuth natively and does not need an embedded API token */\n oauthOnly?: boolean\n}\n\n/**\n * The Sanity MCP server uses OAuth by default\n * If a token is provided, the server will not use OAuth instead tool calls will use the API token\n */\nconst defaultHttpConfig = (token?: string) => {\n const defaultConfig: Record<string, unknown> = {\n type: 'http',\n url: MCP_SERVER_URL,\n }\n\n if (token) {\n defaultConfig.headers = {Authorization: `Bearer ${token}`}\n }\n\n return defaultConfig\n}\n\n// -- Detect functions --\n\nasync function detectClaudeCode(ctx: DetectionEnv): Promise<string | null> {\n try {\n await ctx.execCommand('claude', ['--version'])\n return path.join(ctx.homedir, '.claude.json')\n } catch {\n return null\n }\n}\n\nasync function detectAntigravity(ctx: DetectionEnv): Promise<string | null> {\n const antigravityDir = path.join(ctx.homedir, '.gemini/antigravity')\n return ctx.existsSync(antigravityDir) ? path.join(antigravityDir, 'mcp_config.json') : null\n}\n\nexport function getVSCodeUserDir(\n ctx: DetectionEnv,\n variant: 'insiders' | 'stable' = 'stable',\n): string | null {\n switch (ctx.platform) {\n case 'darwin': {\n return path.join(\n ctx.homedir,\n variant === 'insiders'\n ? 'Library/Application Support/Code - Insiders/User'\n : 'Library/Application Support/Code/User',\n )\n }\n case 'win32': {\n if (!ctx.env.APPDATA) return null\n return path.join(\n ctx.env.APPDATA,\n variant === 'insiders' ? 'Code - Insiders/User' : 'Code/User',\n )\n }\n default: {\n return path.join(\n ctx.homedir,\n variant === 'insiders' ? '.config/Code - Insiders/User' : '.config/Code/User',\n )\n }\n }\n}\n\nasync function detectCline(ctx: DetectionEnv): Promise<string | null> {\n const vscodeUserDir = getVSCodeUserDir(ctx)\n if (!vscodeUserDir) return null\n const clineConfigDir = path.join(vscodeUserDir, 'globalStorage/saoudrizwan.claude-dev/settings')\n return ctx.existsSync(clineConfigDir)\n ? path.join(clineConfigDir, 'cline_mcp_settings.json')\n : null\n}\n\nasync function detectClineCli(ctx: DetectionEnv): Promise<string | null> {\n const clineHome = ctx.env.CLINE_DIR || path.join(ctx.homedir, '.cline')\n if (!ctx.existsSync(clineHome)) return null\n return path.join(clineHome, 'data/settings/cline_mcp_settings.json')\n}\n\nasync function detectCodexCli(ctx: DetectionEnv): Promise<string | null> {\n try {\n await ctx.execCommand('codex', ['--version'])\n const codexHome = ctx.env.CODEX_HOME || path.join(ctx.homedir, '.codex')\n return path.join(codexHome, 'config.toml')\n } catch {\n return null\n }\n}\n\nasync function detectCursor(ctx: DetectionEnv): Promise<string | null> {\n const cursorDir = path.join(ctx.homedir, '.cursor')\n return ctx.existsSync(cursorDir) ? path.join(cursorDir, 'mcp.json') : null\n}\n\nasync function detectGeminiCli(ctx: DetectionEnv): Promise<string | null> {\n // Antigravity stores its config under ~/.gemini/antigravity, so checking\n // only the parent ~/.gemini directory causes false Gemini CLI detection.\n const settingsPath = path.join(ctx.homedir, '.gemini/settings.json')\n return ctx.existsSync(settingsPath) ? settingsPath : null\n}\n\nasync function detectGitHubCopilotCli(ctx: DetectionEnv): Promise<string | null> {\n const copilotDir =\n ctx.platform === 'linux' && ctx.env.XDG_CONFIG_HOME\n ? path.join(ctx.env.XDG_CONFIG_HOME, 'copilot')\n : path.join(ctx.homedir, '.copilot')\n return ctx.existsSync(copilotDir) ? path.join(copilotDir, 'mcp-config.json') : null\n}\n\nasync function detectOpenCode(ctx: DetectionEnv): Promise<string | null> {\n try {\n await ctx.execCommand('opencode', ['--version'])\n return path.join(ctx.homedir, '.config/opencode/opencode.json')\n } catch {\n return null\n }\n}\n\nasync function detectVSCode(ctx: DetectionEnv): Promise<string | null> {\n const configDir = getVSCodeUserDir(ctx)\n return configDir && ctx.existsSync(configDir) ? path.join(configDir, 'mcp.json') : null\n}\n\nasync function detectVSCodeInsiders(ctx: DetectionEnv): Promise<string | null> {\n const configDir = getVSCodeUserDir(ctx, 'insiders')\n return configDir && ctx.existsSync(configDir) ? path.join(configDir, 'mcp.json') : null\n}\n\nasync function detectZed(ctx: DetectionEnv): Promise<string | null> {\n let configDir: string | null = null\n switch (ctx.platform) {\n case 'win32': {\n if (ctx.env.APPDATA) {\n configDir = path.join(ctx.env.APPDATA, 'Zed')\n }\n break\n }\n default: {\n configDir = path.join(ctx.homedir, '.config/zed')\n }\n }\n return configDir && ctx.existsSync(configDir) ? path.join(configDir, 'settings.json') : null\n}\n\nasync function detectMCPorter(ctx: DetectionEnv): Promise<string | null> {\n const mcporterDir = path.join(ctx.homedir, '.mcporter')\n if (!ctx.existsSync(mcporterDir)) return null\n\n const jsonPath = path.join(mcporterDir, 'mcporter.json')\n const jsoncPath = path.join(mcporterDir, 'mcporter.jsonc')\n if (ctx.existsSync(jsonPath)) return jsonPath\n if (ctx.existsSync(jsoncPath)) return jsoncPath\n return jsonPath\n}\n\n// -- Read token helpers --\n\n/**\n * Extract a Bearer token from a headers-like object.\n * Looks for `Authorization: \"Bearer <token>\"` and returns the token portion.\n */\nfunction extractBearerToken(headers: unknown): string | undefined {\n if (typeof headers !== 'object' || headers === null) return undefined\n const auth = (headers as Record<string, unknown>).Authorization\n if (typeof auth !== 'string') return undefined\n const match = auth.match(/^Bearer\\s+(.+)$/)\n return match?.[1]\n}\n\nfunction readTokenFromHeaders(serverConfig: Record<string, unknown>): string | undefined {\n return extractBearerToken(serverConfig.headers)\n}\n\nfunction readTokenFromHttpHeaders(serverConfig: Record<string, unknown>): string | undefined {\n return extractBearerToken(serverConfig.http_headers)\n}\n\n// -- Defaults & build server config functions --\n\n/** Most editors share these values — entries only need to declare `detect` + any overrides. */\nconst EDITOR_DEFAULTS = {\n buildServerConfig: defaultHttpConfig,\n configKey: 'mcpServers',\n format: 'jsonc' as const,\n oauthOnly: false,\n readToken: readTokenFromHeaders,\n}\n\nfunction buildAntigravityServerConfig(token: string): Record<string, unknown> {\n return {\n headers: {Authorization: `Bearer ${token}`},\n serverUrl: MCP_SERVER_URL,\n }\n}\n\nfunction buildClineServerConfig(token: string): Record<string, unknown> {\n return {\n disabled: false,\n headers: {Authorization: `Bearer ${token}`},\n type: 'streamableHttp',\n url: MCP_SERVER_URL,\n }\n}\n\nfunction buildCodexCliServerConfig(token: string): Record<string, unknown> {\n return {\n http_headers: {Authorization: `Bearer ${token}`},\n type: 'http',\n url: MCP_SERVER_URL,\n }\n}\n\nfunction buildGitHubCopilotCliServerConfig(token: string): Record<string, unknown> {\n return {\n headers: {Authorization: `Bearer ${token}`},\n tools: ['*'],\n type: 'http',\n url: MCP_SERVER_URL,\n }\n}\n\nfunction buildOpenCodeServerConfig(token: string): Record<string, unknown> {\n return {\n headers: {Authorization: `Bearer ${token}`},\n type: 'remote',\n url: MCP_SERVER_URL,\n }\n}\n\nfunction buildZedServerConfig(token: string): Record<string, unknown> {\n return {\n headers: {Authorization: `Bearer ${token}`},\n settings: {},\n url: MCP_SERVER_URL,\n }\n}\n\n/**\n * Centralized editor configuration including detection logic.\n * To add a new editor: add an entry here — EditorName type is derived automatically.\n *\n * Each entry includes a doc URL pointing to the source of truth for its\n * config path and format. When updating a path, verify against the linked\n * documentation first.\n */\nexport const EDITOR_CONFIGS = {\n // Doc: https://support.google.com/gemini/answer/16255176 (Antigravity / Project Mariner)\n Antigravity: {\n ...EDITOR_DEFAULTS,\n buildServerConfig: buildAntigravityServerConfig,\n detect: detectAntigravity,\n },\n // Doc: https://docs.anthropic.com/en/docs/claude-code/mcp\n // Path: ~/.claude.json Key: mcpServers\n 'Claude Code': {...EDITOR_DEFAULTS, detect: detectClaudeCode},\n // Doc: https://github.com/cline/cline — VS Code extension (saoudrizwan.claude-dev)\n // Path: <VS Code User>/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json\n Cline: {...EDITOR_DEFAULTS, buildServerConfig: buildClineServerConfig, detect: detectCline},\n // Doc: https://github.com/cline/cline — standalone CLI mode\n // Path: $CLINE_DIR || ~/.cline/data/settings/cline_mcp_settings.json\n 'Cline CLI': {\n ...EDITOR_DEFAULTS,\n buildServerConfig: buildClineServerConfig,\n detect: detectClineCli,\n },\n // Doc: https://platform.openai.com/docs/guides/tools-remote-mcp#codex-cli\n // Path: $CODEX_HOME || ~/.codex/config.toml Key: mcp_servers Format: TOML\n 'Codex CLI': {\n ...EDITOR_DEFAULTS,\n buildServerConfig: buildCodexCliServerConfig,\n configKey: 'mcp_servers',\n detect: detectCodexCli,\n format: 'toml' as const,\n readToken: readTokenFromHttpHeaders,\n },\n // Doc: https://docs.cursor.com/context/model-context-protocol\n // Path: ~/.cursor/mcp.json Key: mcpServers\n Cursor: {\n ...EDITOR_DEFAULTS,\n detect: detectCursor,\n oauthOnly: true,\n },\n // Doc: https://googlegemini.wiki/gemini-cli/mcp-servers\n // Path: ~/.gemini/settings.json Key: mcpServers\n 'Gemini CLI': {...EDITOR_DEFAULTS, detect: detectGeminiCli},\n // Doc: https://docs.github.com/en/copilot/customizing-copilot/extending-copilot-coding-agent-with-mcp\n // Path: ~/.copilot/mcp-config.json (or $XDG_CONFIG_HOME/copilot on Linux) Key: mcpServers\n 'GitHub Copilot CLI': {\n ...EDITOR_DEFAULTS,\n buildServerConfig: buildGitHubCopilotCliServerConfig,\n detect: detectGitHubCopilotCli,\n },\n // Doc: https://github.com/nicobailon/mcporter\n // Path: ~/.mcporter/mcporter.{json,jsonc} Key: mcpServers\n MCPorter: {...EDITOR_DEFAULTS, detect: detectMCPorter},\n // Doc: https://opencode.ai/docs/config\n // Path: ~/.config/opencode/opencode.json Key: mcp\n OpenCode: {\n ...EDITOR_DEFAULTS,\n buildServerConfig: buildOpenCodeServerConfig,\n configKey: 'mcp',\n detect: detectOpenCode,\n },\n // Doc: https://code.visualstudio.com/docs/copilot/chat/mcp-servers\n // Path: <VS Code User dir>/mcp.json Key: servers\n 'VS Code': {...EDITOR_DEFAULTS, configKey: 'servers', detect: detectVSCode},\n // Doc: https://code.visualstudio.com/docs/copilot/chat/mcp-servers\n // Path: <VS Code Insiders User dir>/mcp.json Key: servers\n 'VS Code Insiders': {...EDITOR_DEFAULTS, configKey: 'servers', detect: detectVSCodeInsiders},\n // Doc: https://zed.dev/docs/assistant/model-context-protocol\n // Path: ~/.config/zed/settings.json (or $APPDATA/Zed on Windows) Key: context_servers\n Zed: {\n ...EDITOR_DEFAULTS,\n buildServerConfig: buildZedServerConfig,\n configKey: 'context_servers',\n detect: detectZed,\n },\n} satisfies Record<string, EditorConfig>\n\n/** Derived from EDITOR_CONFIGS keys - add a new editor there and this updates automatically */\nexport type EditorName = keyof typeof EDITOR_CONFIGS\n"],"names":["existsSync","os","path","execa","MCP_SERVER_URL","createDetectionEnv","env","process","execCommand","cmd","args","stdio","timeout","then","homedir","platform","defaultHttpConfig","token","defaultConfig","type","url","headers","Authorization","detectClaudeCode","ctx","join","detectAntigravity","antigravityDir","getVSCodeUserDir","variant","APPDATA","detectCline","vscodeUserDir","clineConfigDir","detectClineCli","clineHome","CLINE_DIR","detectCodexCli","codexHome","CODEX_HOME","detectCursor","cursorDir","detectGeminiCli","settingsPath","detectGitHubCopilotCli","copilotDir","XDG_CONFIG_HOME","detectOpenCode","detectVSCode","configDir","detectVSCodeInsiders","detectZed","detectMCPorter","mcporterDir","jsonPath","jsoncPath","extractBearerToken","undefined","auth","match","readTokenFromHeaders","serverConfig","readTokenFromHttpHeaders","http_headers","EDITOR_DEFAULTS","buildServerConfig","configKey","format","oauthOnly","readToken","buildAntigravityServerConfig","serverUrl","buildClineServerConfig","disabled","buildCodexCliServerConfig","buildGitHubCopilotCliServerConfig","tools","buildOpenCodeServerConfig","buildZedServerConfig","settings","EDITOR_CONFIGS","Antigravity","detect","Cline","Cursor","MCPorter","OpenCode","Zed"],"mappings":"AAAA,SAAQA,UAAU,QAAO,UAAS;AAClC,OAAOC,QAAQ,UAAS;AACxB,OAAOC,UAAU,YAAW;AAE5B,SAAQC,KAAK,QAAO,QAAO;AAE3B,SAAQC,cAAc,QAAO,wBAAuB;AAiBpD,wEAAwE,GACxE,OAAO,SAASC;IACd,OAAO;QACLC,KAAKC,QAAQD,GAAG;QAChBE,aAAa,CAACC,KAAKC,OAASP,MAAMM,KAAKC,MAAM;gBAACC,OAAO;gBAAQC,SAAS;YAAI,GAAGC,IAAI,CAAC,KAAO;QACzFb;QACAc,SAASb,GAAGa,OAAO;QACnBC,UAAUR,QAAQQ,QAAQ;IAC5B;AACF;AAgBA;;;CAGC,GACD,MAAMC,oBAAoB,CAACC;IACzB,MAAMC,gBAAyC;QAC7CC,MAAM;QACNC,KAAKhB;IACP;IAEA,IAAIa,OAAO;QACTC,cAAcG,OAAO,GAAG;YAACC,eAAe,CAAC,OAAO,EAAEL,OAAO;QAAA;IAC3D;IAEA,OAAOC;AACT;AAEA,yBAAyB;AAEzB,eAAeK,iBAAiBC,GAAiB;IAC/C,IAAI;QACF,MAAMA,IAAIhB,WAAW,CAAC,UAAU;YAAC;SAAY;QAC7C,OAAON,KAAKuB,IAAI,CAACD,IAAIV,OAAO,EAAE;IAChC,EAAE,OAAM;QACN,OAAO;IACT;AACF;AAEA,eAAeY,kBAAkBF,GAAiB;IAChD,MAAMG,iBAAiBzB,KAAKuB,IAAI,CAACD,IAAIV,OAAO,EAAE;IAC9C,OAAOU,IAAIxB,UAAU,CAAC2B,kBAAkBzB,KAAKuB,IAAI,CAACE,gBAAgB,qBAAqB;AACzF;AAEA,OAAO,SAASC,iBACdJ,GAAiB,EACjBK,UAAiC,QAAQ;IAEzC,OAAQL,IAAIT,QAAQ;QAClB,KAAK;YAAU;gBACb,OAAOb,KAAKuB,IAAI,CACdD,IAAIV,OAAO,EACXe,YAAY,aACR,qDACA;YAER;QACA,KAAK;YAAS;gBACZ,IAAI,CAACL,IAAIlB,GAAG,CAACwB,OAAO,EAAE,OAAO;gBAC7B,OAAO5B,KAAKuB,IAAI,CACdD,IAAIlB,GAAG,CAACwB,OAAO,EACfD,YAAY,aAAa,yBAAyB;YAEtD;QACA;YAAS;gBACP,OAAO3B,KAAKuB,IAAI,CACdD,IAAIV,OAAO,EACXe,YAAY,aAAa,iCAAiC;YAE9D;IACF;AACF;AAEA,eAAeE,YAAYP,GAAiB;IAC1C,MAAMQ,gBAAgBJ,iBAAiBJ;IACvC,IAAI,CAACQ,eAAe,OAAO;IAC3B,MAAMC,iBAAiB/B,KAAKuB,IAAI,CAACO,eAAe;IAChD,OAAOR,IAAIxB,UAAU,CAACiC,kBAClB/B,KAAKuB,IAAI,CAACQ,gBAAgB,6BAC1B;AACN;AAEA,eAAeC,eAAeV,GAAiB;IAC7C,MAAMW,YAAYX,IAAIlB,GAAG,CAAC8B,SAAS,IAAIlC,KAAKuB,IAAI,CAACD,IAAIV,OAAO,EAAE;IAC9D,IAAI,CAACU,IAAIxB,UAAU,CAACmC,YAAY,OAAO;IACvC,OAAOjC,KAAKuB,IAAI,CAACU,WAAW;AAC9B;AAEA,eAAeE,eAAeb,GAAiB;IAC7C,IAAI;QACF,MAAMA,IAAIhB,WAAW,CAAC,SAAS;YAAC;SAAY;QAC5C,MAAM8B,YAAYd,IAAIlB,GAAG,CAACiC,UAAU,IAAIrC,KAAKuB,IAAI,CAACD,IAAIV,OAAO,EAAE;QAC/D,OAAOZ,KAAKuB,IAAI,CAACa,WAAW;IAC9B,EAAE,OAAM;QACN,OAAO;IACT;AACF;AAEA,eAAeE,aAAahB,GAAiB;IAC3C,MAAMiB,YAAYvC,KAAKuB,IAAI,CAACD,IAAIV,OAAO,EAAE;IACzC,OAAOU,IAAIxB,UAAU,CAACyC,aAAavC,KAAKuB,IAAI,CAACgB,WAAW,cAAc;AACxE;AAEA,eAAeC,gBAAgBlB,GAAiB;IAC9C,yEAAyE;IACzE,yEAAyE;IACzE,MAAMmB,eAAezC,KAAKuB,IAAI,CAACD,IAAIV,OAAO,EAAE;IAC5C,OAAOU,IAAIxB,UAAU,CAAC2C,gBAAgBA,eAAe;AACvD;AAEA,eAAeC,uBAAuBpB,GAAiB;IACrD,MAAMqB,aACJrB,IAAIT,QAAQ,KAAK,WAAWS,IAAIlB,GAAG,CAACwC,eAAe,GAC/C5C,KAAKuB,IAAI,CAACD,IAAIlB,GAAG,CAACwC,eAAe,EAAE,aACnC5C,KAAKuB,IAAI,CAACD,IAAIV,OAAO,EAAE;IAC7B,OAAOU,IAAIxB,UAAU,CAAC6C,cAAc3C,KAAKuB,IAAI,CAACoB,YAAY,qBAAqB;AACjF;AAEA,eAAeE,eAAevB,GAAiB;IAC7C,IAAI;QACF,MAAMA,IAAIhB,WAAW,CAAC,YAAY;YAAC;SAAY;QAC/C,OAAON,KAAKuB,IAAI,CAACD,IAAIV,OAAO,EAAE;IAChC,EAAE,OAAM;QACN,OAAO;IACT;AACF;AAEA,eAAekC,aAAaxB,GAAiB;IAC3C,MAAMyB,YAAYrB,iBAAiBJ;IACnC,OAAOyB,aAAazB,IAAIxB,UAAU,CAACiD,aAAa/C,KAAKuB,IAAI,CAACwB,WAAW,cAAc;AACrF;AAEA,eAAeC,qBAAqB1B,GAAiB;IACnD,MAAMyB,YAAYrB,iBAAiBJ,KAAK;IACxC,OAAOyB,aAAazB,IAAIxB,UAAU,CAACiD,aAAa/C,KAAKuB,IAAI,CAACwB,WAAW,cAAc;AACrF;AAEA,eAAeE,UAAU3B,GAAiB;IACxC,IAAIyB,YAA2B;IAC/B,OAAQzB,IAAIT,QAAQ;QAClB,KAAK;YAAS;gBACZ,IAAIS,IAAIlB,GAAG,CAACwB,OAAO,EAAE;oBACnBmB,YAAY/C,KAAKuB,IAAI,CAACD,IAAIlB,GAAG,CAACwB,OAAO,EAAE;gBACzC;gBACA;YACF;QACA;YAAS;gBACPmB,YAAY/C,KAAKuB,IAAI,CAACD,IAAIV,OAAO,EAAE;YACrC;IACF;IACA,OAAOmC,aAAazB,IAAIxB,UAAU,CAACiD,aAAa/C,KAAKuB,IAAI,CAACwB,WAAW,mBAAmB;AAC1F;AAEA,eAAeG,eAAe5B,GAAiB;IAC7C,MAAM6B,cAAcnD,KAAKuB,IAAI,CAACD,IAAIV,OAAO,EAAE;IAC3C,IAAI,CAACU,IAAIxB,UAAU,CAACqD,cAAc,OAAO;IAEzC,MAAMC,WAAWpD,KAAKuB,IAAI,CAAC4B,aAAa;IACxC,MAAME,YAAYrD,KAAKuB,IAAI,CAAC4B,aAAa;IACzC,IAAI7B,IAAIxB,UAAU,CAACsD,WAAW,OAAOA;IACrC,IAAI9B,IAAIxB,UAAU,CAACuD,YAAY,OAAOA;IACtC,OAAOD;AACT;AAEA,2BAA2B;AAE3B;;;CAGC,GACD,SAASE,mBAAmBnC,OAAgB;IAC1C,IAAI,OAAOA,YAAY,YAAYA,YAAY,MAAM,OAAOoC;IAC5D,MAAMC,OAAO,AAACrC,QAAoCC,aAAa;IAC/D,IAAI,OAAOoC,SAAS,UAAU,OAAOD;IACrC,MAAME,QAAQD,KAAKC,KAAK,CAAC;IACzB,OAAOA,OAAO,CAAC,EAAE;AACnB;AAEA,SAASC,qBAAqBC,YAAqC;IACjE,OAAOL,mBAAmBK,aAAaxC,OAAO;AAChD;AAEA,SAASyC,yBAAyBD,YAAqC;IACrE,OAAOL,mBAAmBK,aAAaE,YAAY;AACrD;AAEA,iDAAiD;AAEjD,6FAA6F,GAC7F,MAAMC,kBAAkB;IACtBC,mBAAmBjD;IACnBkD,WAAW;IACXC,QAAQ;IACRC,WAAW;IACXC,WAAWT;AACb;AAEA,SAASU,6BAA6BrD,KAAa;IACjD,OAAO;QACLI,SAAS;YAACC,eAAe,CAAC,OAAO,EAAEL,OAAO;QAAA;QAC1CsD,WAAWnE;IACb;AACF;AAEA,SAASoE,uBAAuBvD,KAAa;IAC3C,OAAO;QACLwD,UAAU;QACVpD,SAAS;YAACC,eAAe,CAAC,OAAO,EAAEL,OAAO;QAAA;QAC1CE,MAAM;QACNC,KAAKhB;IACP;AACF;AAEA,SAASsE,0BAA0BzD,KAAa;IAC9C,OAAO;QACL8C,cAAc;YAACzC,eAAe,CAAC,OAAO,EAAEL,OAAO;QAAA;QAC/CE,MAAM;QACNC,KAAKhB;IACP;AACF;AAEA,SAASuE,kCAAkC1D,KAAa;IACtD,OAAO;QACLI,SAAS;YAACC,eAAe,CAAC,OAAO,EAAEL,OAAO;QAAA;QAC1C2D,OAAO;YAAC;SAAI;QACZzD,MAAM;QACNC,KAAKhB;IACP;AACF;AAEA,SAASyE,0BAA0B5D,KAAa;IAC9C,OAAO;QACLI,SAAS;YAACC,eAAe,CAAC,OAAO,EAAEL,OAAO;QAAA;QAC1CE,MAAM;QACNC,KAAKhB;IACP;AACF;AAEA,SAAS0E,qBAAqB7D,KAAa;IACzC,OAAO;QACLI,SAAS;YAACC,eAAe,CAAC,OAAO,EAAEL,OAAO;QAAA;QAC1C8D,UAAU,CAAC;QACX3D,KAAKhB;IACP;AACF;AAEA;;;;;;;CAOC,GACD,OAAO,MAAM4E,iBAAiB;IAC5B,yFAAyF;IACzFC,aAAa;QACX,GAAGjB,eAAe;QAClBC,mBAAmBK;QACnBY,QAAQxD;IACV;IACA,0DAA0D;IAC1D,wCAAwC;IACxC,eAAe;QAAC,GAAGsC,eAAe;QAAEkB,QAAQ3D;IAAgB;IAC5D,mFAAmF;IACnF,6FAA6F;IAC7F4D,OAAO;QAAC,GAAGnB,eAAe;QAAEC,mBAAmBO;QAAwBU,QAAQnD;IAAW;IAC1F,4DAA4D;IAC5D,qEAAqE;IACrE,aAAa;QACX,GAAGiC,eAAe;QAClBC,mBAAmBO;QACnBU,QAAQhD;IACV;IACA,0EAA0E;IAC1E,4EAA4E;IAC5E,aAAa;QACX,GAAG8B,eAAe;QAClBC,mBAAmBS;QACnBR,WAAW;QACXgB,QAAQ7C;QACR8B,QAAQ;QACRE,WAAWP;IACb;IACA,8DAA8D;IAC9D,4CAA4C;IAC5CsB,QAAQ;QACN,GAAGpB,eAAe;QAClBkB,QAAQ1C;QACR4B,WAAW;IACb;IACA,wDAAwD;IACxD,iDAAiD;IACjD,cAAc;QAAC,GAAGJ,eAAe;QAAEkB,QAAQxC;IAAe;IAC1D,sGAAsG;IACtG,2FAA2F;IAC3F,sBAAsB;QACpB,GAAGsB,eAAe;QAClBC,mBAAmBU;QACnBO,QAAQtC;IACV;IACA,8CAA8C;IAC9C,2DAA2D;IAC3DyC,UAAU;QAAC,GAAGrB,eAAe;QAAEkB,QAAQ9B;IAAc;IACrD,uCAAuC;IACvC,mDAAmD;IACnDkC,UAAU;QACR,GAAGtB,eAAe;QAClBC,mBAAmBY;QACnBX,WAAW;QACXgB,QAAQnC;IACV;IACA,mEAAmE;IACnE,kDAAkD;IAClD,WAAW;QAAC,GAAGiB,eAAe;QAAEE,WAAW;QAAWgB,QAAQlC;IAAY;IAC1E,mEAAmE;IACnE,2DAA2D;IAC3D,oBAAoB;QAAC,GAAGgB,eAAe;QAAEE,WAAW;QAAWgB,QAAQhC;IAAoB;IAC3F,6DAA6D;IAC7D,uFAAuF;IACvFqC,KAAK;QACH,GAAGvB,eAAe;QAClBC,mBAAmBa;QACnBZ,WAAW;QACXgB,QAAQ/B;IACV;AACF,EAAwC"}
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/mcp/editorConfigs.ts"],"sourcesContent":["import {existsSync} from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\n\nimport {execa} from 'execa'\n\nimport {MCP_SERVER_URL} from '../../services/mcp.js'\n\n/**\n * Environment abstraction for editor detection.\n *\n * Detect functions receive this instead of using module-level imports, making\n * each function independently testable without global mocks.\n */\nexport interface DetectionEnv {\n env: Record<string, string | undefined>\n /** Run a CLI command to check if a tool is installed. Rejects on failure. */\n execCommand: (cmd: string, args: string[]) => Promise<void>\n existsSync: (p: string) => boolean\n homedir: string\n platform: NodeJS.Platform\n}\n\n/** Create the real detection environment backed by process/OS globals. */\nexport function createDetectionEnv(): DetectionEnv {\n return {\n env: process.env,\n execCommand: (cmd, args) => execa(cmd, args, {stdio: 'pipe', timeout: 5000}).then(() => {}),\n existsSync,\n homedir: os.homedir(),\n platform: process.platform,\n }\n}\n\ninterface EditorConfig {\n /** Builds the server config with API token. If oauthOnly is true, the token is not used */\n buildServerConfig: (token: string) => Record<string, unknown>\n configKey: string\n /** Returns the config file path if editor is detected, null otherwise */\n detect: (env: DetectionEnv) => Promise<string | null>\n format: 'jsonc' | 'toml'\n /** Extracts the auth token from a parsed Sanity server config block */\n readToken: (serverConfig: Record<string, unknown>) => string | undefined\n\n /** If true, this editor uses OAuth natively and does not need an embedded API token */\n oauthOnly?: boolean\n /**\n * Corresponding `--agent` value for the `skills` CLI (https://github.com/vercel-labs/skills).\n * Omit when the editor has no skills CLI equivalent.\n */\n skillsCliAgent?: string\n}\n\n/**\n * The Sanity MCP server uses OAuth by default\n * If a token is provided, the server will not use OAuth instead tool calls will use the API token\n */\nconst defaultHttpConfig = (token?: string) => {\n const defaultConfig: Record<string, unknown> = {\n type: 'http',\n url: MCP_SERVER_URL,\n }\n\n if (token) {\n defaultConfig.headers = {Authorization: `Bearer ${token}`}\n }\n\n return defaultConfig\n}\n\n// -- Detect functions --\n\nasync function detectClaudeCode(ctx: DetectionEnv): Promise<string | null> {\n try {\n await ctx.execCommand('claude', ['--version'])\n return path.join(ctx.homedir, '.claude.json')\n } catch {\n return null\n }\n}\n\nasync function detectAntigravity(ctx: DetectionEnv): Promise<string | null> {\n const antigravityDir = path.join(ctx.homedir, '.gemini/antigravity')\n return ctx.existsSync(antigravityDir) ? path.join(antigravityDir, 'mcp_config.json') : null\n}\n\nexport function getVSCodeUserDir(\n ctx: DetectionEnv,\n variant: 'insiders' | 'stable' = 'stable',\n): string | null {\n switch (ctx.platform) {\n case 'darwin': {\n return path.join(\n ctx.homedir,\n variant === 'insiders'\n ? 'Library/Application Support/Code - Insiders/User'\n : 'Library/Application Support/Code/User',\n )\n }\n case 'win32': {\n if (!ctx.env.APPDATA) return null\n return path.join(\n ctx.env.APPDATA,\n variant === 'insiders' ? 'Code - Insiders/User' : 'Code/User',\n )\n }\n default: {\n return path.join(\n ctx.homedir,\n variant === 'insiders' ? '.config/Code - Insiders/User' : '.config/Code/User',\n )\n }\n }\n}\n\nasync function detectCline(ctx: DetectionEnv): Promise<string | null> {\n const vscodeUserDir = getVSCodeUserDir(ctx)\n if (!vscodeUserDir) return null\n const clineConfigDir = path.join(vscodeUserDir, 'globalStorage/saoudrizwan.claude-dev/settings')\n return ctx.existsSync(clineConfigDir)\n ? path.join(clineConfigDir, 'cline_mcp_settings.json')\n : null\n}\n\nasync function detectClineCli(ctx: DetectionEnv): Promise<string | null> {\n const clineHome = ctx.env.CLINE_DIR || path.join(ctx.homedir, '.cline')\n if (!ctx.existsSync(clineHome)) return null\n return path.join(clineHome, 'data/settings/cline_mcp_settings.json')\n}\n\nasync function detectCodexCli(ctx: DetectionEnv): Promise<string | null> {\n try {\n await ctx.execCommand('codex', ['--version'])\n const codexHome = ctx.env.CODEX_HOME || path.join(ctx.homedir, '.codex')\n return path.join(codexHome, 'config.toml')\n } catch {\n return null\n }\n}\n\nasync function detectCursor(ctx: DetectionEnv): Promise<string | null> {\n const cursorDir = path.join(ctx.homedir, '.cursor')\n return ctx.existsSync(cursorDir) ? path.join(cursorDir, 'mcp.json') : null\n}\n\nasync function detectGeminiCli(ctx: DetectionEnv): Promise<string | null> {\n // Antigravity stores its config under ~/.gemini/antigravity, so checking\n // only the parent ~/.gemini directory causes false Gemini CLI detection.\n const settingsPath = path.join(ctx.homedir, '.gemini/settings.json')\n return ctx.existsSync(settingsPath) ? settingsPath : null\n}\n\nasync function detectGitHubCopilotCli(ctx: DetectionEnv): Promise<string | null> {\n const copilotDir =\n ctx.platform === 'linux' && ctx.env.XDG_CONFIG_HOME\n ? path.join(ctx.env.XDG_CONFIG_HOME, 'copilot')\n : path.join(ctx.homedir, '.copilot')\n return ctx.existsSync(copilotDir) ? path.join(copilotDir, 'mcp-config.json') : null\n}\n\nasync function detectOpenCode(ctx: DetectionEnv): Promise<string | null> {\n try {\n await ctx.execCommand('opencode', ['--version'])\n return path.join(ctx.homedir, '.config/opencode/opencode.json')\n } catch {\n return null\n }\n}\n\nasync function detectVSCode(ctx: DetectionEnv): Promise<string | null> {\n const configDir = getVSCodeUserDir(ctx)\n return configDir && ctx.existsSync(configDir) ? path.join(configDir, 'mcp.json') : null\n}\n\nasync function detectVSCodeInsiders(ctx: DetectionEnv): Promise<string | null> {\n const configDir = getVSCodeUserDir(ctx, 'insiders')\n return configDir && ctx.existsSync(configDir) ? path.join(configDir, 'mcp.json') : null\n}\n\nasync function detectZed(ctx: DetectionEnv): Promise<string | null> {\n let configDir: string | null = null\n switch (ctx.platform) {\n case 'win32': {\n if (ctx.env.APPDATA) {\n configDir = path.join(ctx.env.APPDATA, 'Zed')\n }\n break\n }\n default: {\n configDir = path.join(ctx.homedir, '.config/zed')\n }\n }\n return configDir && ctx.existsSync(configDir) ? path.join(configDir, 'settings.json') : null\n}\n\nasync function detectMCPorter(ctx: DetectionEnv): Promise<string | null> {\n const mcporterDir = path.join(ctx.homedir, '.mcporter')\n if (!ctx.existsSync(mcporterDir)) return null\n\n const jsonPath = path.join(mcporterDir, 'mcporter.json')\n const jsoncPath = path.join(mcporterDir, 'mcporter.jsonc')\n if (ctx.existsSync(jsonPath)) return jsonPath\n if (ctx.existsSync(jsoncPath)) return jsoncPath\n return jsonPath\n}\n\n// -- Read token helpers --\n\n/**\n * Extract a Bearer token from a headers-like object.\n * Looks for `Authorization: \"Bearer <token>\"` and returns the token portion.\n */\nfunction extractBearerToken(headers: unknown): string | undefined {\n if (typeof headers !== 'object' || headers === null) return undefined\n const auth = (headers as Record<string, unknown>).Authorization\n if (typeof auth !== 'string') return undefined\n const match = auth.match(/^Bearer\\s+(.+)$/)\n return match?.[1]\n}\n\nfunction readTokenFromHeaders(serverConfig: Record<string, unknown>): string | undefined {\n return extractBearerToken(serverConfig.headers)\n}\n\nfunction readTokenFromHttpHeaders(serverConfig: Record<string, unknown>): string | undefined {\n return extractBearerToken(serverConfig.http_headers)\n}\n\n// -- Defaults & build server config functions --\n\n/** Most editors share these values — entries only need to declare `detect` + any overrides. */\nconst EDITOR_DEFAULTS = {\n buildServerConfig: defaultHttpConfig,\n configKey: 'mcpServers',\n format: 'jsonc' as const,\n oauthOnly: false,\n readToken: readTokenFromHeaders,\n}\n\nfunction buildAntigravityServerConfig(token: string): Record<string, unknown> {\n return {\n headers: {Authorization: `Bearer ${token}`},\n serverUrl: MCP_SERVER_URL,\n }\n}\n\nfunction buildClineServerConfig(token: string): Record<string, unknown> {\n return {\n disabled: false,\n headers: {Authorization: `Bearer ${token}`},\n type: 'streamableHttp',\n url: MCP_SERVER_URL,\n }\n}\n\nfunction buildCodexCliServerConfig(token: string): Record<string, unknown> {\n return {\n http_headers: {Authorization: `Bearer ${token}`},\n type: 'http',\n url: MCP_SERVER_URL,\n }\n}\n\nfunction buildGitHubCopilotCliServerConfig(token: string): Record<string, unknown> {\n return {\n headers: {Authorization: `Bearer ${token}`},\n tools: ['*'],\n type: 'http',\n url: MCP_SERVER_URL,\n }\n}\n\nfunction buildOpenCodeServerConfig(token: string): Record<string, unknown> {\n return {\n headers: {Authorization: `Bearer ${token}`},\n type: 'remote',\n url: MCP_SERVER_URL,\n }\n}\n\nfunction buildZedServerConfig(token: string): Record<string, unknown> {\n return {\n headers: {Authorization: `Bearer ${token}`},\n settings: {},\n url: MCP_SERVER_URL,\n }\n}\n\n/**\n * Centralized editor configuration including detection logic.\n * To add a new editor: add an entry here — EditorName type is derived automatically.\n *\n * Each entry includes a doc URL pointing to the source of truth for its\n * config path and format. When updating a path, verify against the linked\n * documentation first.\n */\nexport const EDITOR_CONFIGS = {\n // Doc: https://support.google.com/gemini/answer/16255176 (Antigravity / Project Mariner)\n Antigravity: {\n ...EDITOR_DEFAULTS,\n buildServerConfig: buildAntigravityServerConfig,\n detect: detectAntigravity,\n skillsCliAgent: 'antigravity',\n },\n // Doc: https://docs.anthropic.com/en/docs/claude-code/mcp\n // Path: ~/.claude.json Key: mcpServers\n 'Claude Code': {\n ...EDITOR_DEFAULTS,\n detect: detectClaudeCode,\n oauthOnly: true,\n skillsCliAgent: 'claude-code',\n },\n // Doc: https://github.com/cline/cline — VS Code extension (saoudrizwan.claude-dev)\n // Path: <VS Code User>/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json\n Cline: {\n ...EDITOR_DEFAULTS,\n buildServerConfig: buildClineServerConfig,\n detect: detectCline,\n skillsCliAgent: 'cline',\n },\n // Doc: https://github.com/cline/cline — standalone CLI mode\n // Path: $CLINE_DIR || ~/.cline/data/settings/cline_mcp_settings.json\n 'Cline CLI': {\n ...EDITOR_DEFAULTS,\n buildServerConfig: buildClineServerConfig,\n detect: detectClineCli,\n skillsCliAgent: 'cline',\n },\n // Doc: https://platform.openai.com/docs/guides/tools-remote-mcp#codex-cli\n // Path: $CODEX_HOME || ~/.codex/config.toml Key: mcp_servers Format: TOML\n 'Codex CLI': {\n ...EDITOR_DEFAULTS,\n buildServerConfig: buildCodexCliServerConfig,\n configKey: 'mcp_servers',\n detect: detectCodexCli,\n format: 'toml' as const,\n readToken: readTokenFromHttpHeaders,\n skillsCliAgent: 'codex',\n },\n // Doc: https://docs.cursor.com/context/model-context-protocol\n // Path: ~/.cursor/mcp.json Key: mcpServers\n Cursor: {\n ...EDITOR_DEFAULTS,\n detect: detectCursor,\n oauthOnly: true,\n skillsCliAgent: 'cursor',\n },\n // Doc: https://googlegemini.wiki/gemini-cli/mcp-servers\n // Path: ~/.gemini/settings.json Key: mcpServers\n 'Gemini CLI': {...EDITOR_DEFAULTS, detect: detectGeminiCli, skillsCliAgent: 'gemini-cli'},\n // Doc: https://docs.github.com/en/copilot/customizing-copilot/extending-copilot-coding-agent-with-mcp\n // Path: ~/.copilot/mcp-config.json (or $XDG_CONFIG_HOME/copilot on Linux) Key: mcpServers\n 'GitHub Copilot CLI': {\n ...EDITOR_DEFAULTS,\n buildServerConfig: buildGitHubCopilotCliServerConfig,\n detect: detectGitHubCopilotCli,\n skillsCliAgent: 'github-copilot',\n },\n // Doc: https://github.com/nicobailon/mcporter\n // Path: ~/.mcporter/mcporter.{json,jsonc} Key: mcpServers\n MCPorter: {...EDITOR_DEFAULTS, detect: detectMCPorter},\n // Doc: https://opencode.ai/docs/config\n // Path: ~/.config/opencode/opencode.json Key: mcp\n OpenCode: {\n ...EDITOR_DEFAULTS,\n buildServerConfig: buildOpenCodeServerConfig,\n configKey: 'mcp',\n detect: detectOpenCode,\n skillsCliAgent: 'opencode',\n },\n // Doc: https://code.visualstudio.com/docs/copilot/chat/mcp-servers\n // Path: <VS Code User dir>/mcp.json Key: servers\n // VS Code uses GitHub Copilot for AI features; skills are installed via the\n // `github-copilot` agent (see https://code.visualstudio.com/docs/copilot/customization/agent-skills).\n 'VS Code': {\n ...EDITOR_DEFAULTS,\n configKey: 'servers',\n detect: detectVSCode,\n skillsCliAgent: 'github-copilot',\n },\n // Doc: https://code.visualstudio.com/docs/copilot/chat/mcp-servers\n // Path: <VS Code Insiders User dir>/mcp.json Key: servers\n 'VS Code Insiders': {\n ...EDITOR_DEFAULTS,\n configKey: 'servers',\n detect: detectVSCodeInsiders,\n skillsCliAgent: 'github-copilot',\n },\n // Doc: https://zed.dev/docs/assistant/model-context-protocol\n // Path: ~/.config/zed/settings.json (or $APPDATA/Zed on Windows) Key: context_servers\n // Zed doesn't support agent skills - https://github.com/zed-industries/zed/issues/49057\n Zed: {\n ...EDITOR_DEFAULTS,\n buildServerConfig: buildZedServerConfig,\n configKey: 'context_servers',\n detect: detectZed,\n },\n} satisfies Record<string, EditorConfig>\n\n/** Derived from EDITOR_CONFIGS keys - add a new editor there and this updates automatically */\nexport type EditorName = keyof typeof EDITOR_CONFIGS\n\nexport function getSkillsCliAgent(editorName: EditorName): string | undefined {\n if (editorName in EDITOR_CONFIGS) {\n const config = EDITOR_CONFIGS[editorName]\n return 'skillsCliAgent' in config ? config.skillsCliAgent : undefined\n }\n}\n\n/**\n * Skills-CLI agent ID → display name. Mirrors `displayName` from\n * `~/git/skills/src/agents.ts` for the subset of agents we install for. Used\n * to match `skills list --json` output (which keys by display name) against\n * our editors.\n */\nconst SKILLS_CLI_AGENT_DISPLAY_NAMES: Record<string, string> = {\n antigravity: 'Antigravity',\n 'claude-code': 'Claude Code',\n cline: 'Cline',\n codex: 'Codex',\n cursor: 'Cursor',\n 'gemini-cli': 'Gemini CLI',\n 'github-copilot': 'GitHub Copilot',\n opencode: 'OpenCode',\n}\n\n/** Display name used by the skills CLI for the given editor, if it has a mapping. */\nexport function getSkillsCliAgentDisplayName(editorName: EditorName): string | undefined {\n const agent = getSkillsCliAgent(editorName)\n return agent ? SKILLS_CLI_AGENT_DISPLAY_NAMES[agent] : undefined\n}\n"],"names":["existsSync","os","path","execa","MCP_SERVER_URL","createDetectionEnv","env","process","execCommand","cmd","args","stdio","timeout","then","homedir","platform","defaultHttpConfig","token","defaultConfig","type","url","headers","Authorization","detectClaudeCode","ctx","join","detectAntigravity","antigravityDir","getVSCodeUserDir","variant","APPDATA","detectCline","vscodeUserDir","clineConfigDir","detectClineCli","clineHome","CLINE_DIR","detectCodexCli","codexHome","CODEX_HOME","detectCursor","cursorDir","detectGeminiCli","settingsPath","detectGitHubCopilotCli","copilotDir","XDG_CONFIG_HOME","detectOpenCode","detectVSCode","configDir","detectVSCodeInsiders","detectZed","detectMCPorter","mcporterDir","jsonPath","jsoncPath","extractBearerToken","undefined","auth","match","readTokenFromHeaders","serverConfig","readTokenFromHttpHeaders","http_headers","EDITOR_DEFAULTS","buildServerConfig","configKey","format","oauthOnly","readToken","buildAntigravityServerConfig","serverUrl","buildClineServerConfig","disabled","buildCodexCliServerConfig","buildGitHubCopilotCliServerConfig","tools","buildOpenCodeServerConfig","buildZedServerConfig","settings","EDITOR_CONFIGS","Antigravity","detect","skillsCliAgent","Cline","Cursor","MCPorter","OpenCode","Zed","getSkillsCliAgent","editorName","config","SKILLS_CLI_AGENT_DISPLAY_NAMES","antigravity","cline","codex","cursor","opencode","getSkillsCliAgentDisplayName","agent"],"mappings":"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"}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { checkbox } from '@sanity/cli-core/ux';
|
|
2
|
-
function getEditorLabel(
|
|
2
|
+
function getEditorLabel(choice) {
|
|
3
|
+
const { action, editor } = choice;
|
|
4
|
+
if (action === 'skill-only') {
|
|
5
|
+
return `${editor.name} (skill only — MCP already configured)`;
|
|
6
|
+
}
|
|
3
7
|
if (editor.configured && editor.authStatus === 'unauthorized') {
|
|
4
8
|
return `${editor.name} (auth expired)`;
|
|
5
9
|
}
|
|
@@ -9,25 +13,26 @@ function getEditorLabel(editor) {
|
|
|
9
13
|
return editor.name;
|
|
10
14
|
}
|
|
11
15
|
/**
|
|
12
|
-
* Prompt user to select
|
|
16
|
+
* Prompt the user to select editors for MCP / skills setup. The caller is
|
|
17
|
+
* responsible for classifying editors into actions (see `EditorAction`) and
|
|
18
|
+
* for choosing an appropriate prompt message.
|
|
13
19
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*/ export async function promptForMCPSetup(
|
|
17
|
-
const editorChoices =
|
|
20
|
+
* Returns the subset of `choices` the user kept, or `null` when the user
|
|
21
|
+
* deselected everything.
|
|
22
|
+
*/ export async function promptForMCPSetup({ choices, message }) {
|
|
23
|
+
const editorChoices = choices.map((choice)=>({
|
|
18
24
|
checked: true,
|
|
19
|
-
name: getEditorLabel(
|
|
20
|
-
value:
|
|
25
|
+
name: getEditorLabel(choice),
|
|
26
|
+
value: choice.editor.name
|
|
21
27
|
}));
|
|
22
28
|
const selectedNames = await checkbox({
|
|
23
29
|
choices: editorChoices,
|
|
24
|
-
message
|
|
30
|
+
message
|
|
25
31
|
});
|
|
26
|
-
// User can deselect all to skip
|
|
27
32
|
if (!selectedNames || selectedNames.length === 0) {
|
|
28
33
|
return null;
|
|
29
34
|
}
|
|
30
|
-
return
|
|
35
|
+
return choices.filter((c)=>selectedNames.includes(c.editor.name));
|
|
31
36
|
}
|
|
32
37
|
|
|
33
38
|
//# sourceMappingURL=promptForMCPSetup.js.map
|
|
@@ -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\nfunction getEditorLabel(
|
|
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"}
|
|
@@ -2,30 +2,116 @@ import { ux } from '@oclif/core';
|
|
|
2
2
|
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
|
+
import { readSkillState } from '../skills/readSkillState.js';
|
|
6
|
+
import { SANITY_SKILL_NAME } from '../skills/setupSkills.js';
|
|
5
7
|
import { detectAvailableEditors } from './detectAvailableEditors.js';
|
|
6
|
-
import { EDITOR_CONFIGS } from './editorConfigs.js';
|
|
8
|
+
import { EDITOR_CONFIGS, getSkillsCliAgent, getSkillsCliAgentDisplayName } from './editorConfigs.js';
|
|
7
9
|
import { promptForMCPSetup } from './promptForMCPSetup.js';
|
|
8
10
|
import { validateEditorTokens } from './validateEditorTokens.js';
|
|
9
11
|
import { writeMCPConfig } from './writeMCPConfig.js';
|
|
10
12
|
const mcpDebug = subdebug('mcp:setup');
|
|
11
13
|
const NO_EDITORS_DETECTED_MESSAGE = `Couldn't auto-configure Sanity MCP server for your editor. Visit ${MCP_SERVER_URL} for setup instructions.`;
|
|
12
14
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
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.
|
|
17
|
+
*/ function classifyEditors(editors) {
|
|
18
|
+
return editors.map((editor)=>{
|
|
19
|
+
const needsMCP = !editor.configured || editor.authStatus !== 'valid';
|
|
20
|
+
const skillsCliAgent = getSkillsCliAgent(editor.name);
|
|
21
|
+
const hasSkillMapping = Boolean(skillsCliAgent);
|
|
22
|
+
const skillInstalled = editor.skillInstalled === true;
|
|
23
|
+
if (needsMCP) {
|
|
24
|
+
return {
|
|
25
|
+
action: hasSkillMapping ? 'mcp-and-skill' : 'mcp-only',
|
|
26
|
+
editor
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
if (hasSkillMapping && !skillInstalled) {
|
|
30
|
+
return {
|
|
31
|
+
action: 'skill-only',
|
|
32
|
+
editor
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
action: 'none',
|
|
37
|
+
editor
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Apply masking based on the configured modes. `skip` modes mute the
|
|
43
|
+
* corresponding action so we never prompt for or run work the user opted out
|
|
44
|
+
* of via `--no-mcp` / `--no-skills`.
|
|
45
|
+
*/ function applyMasking(classified, mcpMode, skillsMode) {
|
|
46
|
+
const actionable = [];
|
|
47
|
+
for (const { action, editor } of classified){
|
|
48
|
+
if (action === 'none') continue;
|
|
49
|
+
if (mcpMode === 'skip' && skillsMode === 'skip') continue;
|
|
50
|
+
if (mcpMode === 'skip') {
|
|
51
|
+
// No MCP writes — keep only skill-only / mcp-and-skill (downgraded to skill-only)
|
|
52
|
+
if (action === 'mcp-only') continue;
|
|
53
|
+
if (action === 'mcp-and-skill') {
|
|
54
|
+
actionable.push({
|
|
55
|
+
action: 'skill-only',
|
|
56
|
+
editor
|
|
57
|
+
});
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
actionable.push({
|
|
61
|
+
action,
|
|
62
|
+
editor
|
|
63
|
+
});
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (skillsMode === 'skip') {
|
|
67
|
+
// No skill install — drop skill-only, downgrade mcp-and-skill → mcp-only
|
|
68
|
+
if (action === 'skill-only') continue;
|
|
69
|
+
if (action === 'mcp-and-skill') {
|
|
70
|
+
actionable.push({
|
|
71
|
+
action: 'mcp-only',
|
|
72
|
+
editor
|
|
73
|
+
});
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
actionable.push({
|
|
77
|
+
action,
|
|
78
|
+
editor
|
|
79
|
+
});
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
actionable.push({
|
|
83
|
+
action,
|
|
84
|
+
editor
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
return actionable;
|
|
88
|
+
}
|
|
89
|
+
function getPromptMessage(mcpMode, skillsMode) {
|
|
90
|
+
if (mcpMode === 'skip') return 'Install Sanity agent skills for these editors?';
|
|
91
|
+
if (skillsMode === 'skip') return 'Configure Sanity MCP server?';
|
|
92
|
+
return 'Configure Sanity MCP and install agent skills for these editors?';
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Main MCP setup orchestration.
|
|
96
|
+
*
|
|
97
|
+
* When `skillsMode !== 'skip'`, the prompt combines MCP and skill offers,
|
|
98
|
+
* and the result includes `skillsToInstall` — agent IDs the caller should
|
|
99
|
+
* install via `setupSkills`. `setupMCP` itself never installs skills.
|
|
15
100
|
*/ export async function setupMCP(options) {
|
|
16
|
-
const { explicit = false, mode = 'prompt' } = options ?? {};
|
|
17
|
-
// 1.
|
|
18
|
-
if (
|
|
19
|
-
mcpDebug('Skipping
|
|
101
|
+
const { explicit = false, mode: mcpMode = 'prompt', skillsMode = 'skip' } = options ?? {};
|
|
102
|
+
// 1. Both opted out → nothing to do.
|
|
103
|
+
if (mcpMode === 'skip' && skillsMode === 'skip') {
|
|
104
|
+
mcpDebug('Skipping setup (mcpMode: skip, skillsMode: skip)');
|
|
20
105
|
return {
|
|
21
106
|
alreadyConfiguredEditors: [],
|
|
22
107
|
configuredEditors: [],
|
|
23
108
|
detectedEditors: [],
|
|
109
|
+
skillsToInstall: [],
|
|
24
110
|
skipped: true
|
|
25
111
|
};
|
|
26
112
|
}
|
|
27
113
|
// 2. Detect available editors (filters out unparseable configs)
|
|
28
|
-
const editors = await detectAvailableEditors();
|
|
114
|
+
const editors = options?.editors ?? await detectAvailableEditors();
|
|
29
115
|
const detectedEditors = editors.map((e)=>e.name);
|
|
30
116
|
mcpDebug('Detected %d editors: %s', detectedEditors.length, detectedEditors);
|
|
31
117
|
if (editors.length === 0) {
|
|
@@ -36,16 +122,31 @@ const NO_EDITORS_DETECTED_MESSAGE = `Couldn't auto-configure Sanity MCP server f
|
|
|
36
122
|
alreadyConfiguredEditors: [],
|
|
37
123
|
configuredEditors: [],
|
|
38
124
|
detectedEditors,
|
|
125
|
+
skillsToInstall: [],
|
|
39
126
|
skipped: true
|
|
40
127
|
};
|
|
41
128
|
}
|
|
42
129
|
// 3. Validate existing tokens against the Sanity API
|
|
43
130
|
await validateEditorTokens(editors);
|
|
44
|
-
// 4.
|
|
45
|
-
|
|
131
|
+
// 4. Read skill state when skills are in scope so classification can dedup
|
|
132
|
+
if (skillsMode !== 'skip') {
|
|
133
|
+
const { installedAgentDisplayNames } = await readSkillState({
|
|
134
|
+
skillName: SANITY_SKILL_NAME
|
|
135
|
+
});
|
|
136
|
+
for (const editor of editors){
|
|
137
|
+
const displayName = getSkillsCliAgentDisplayName(editor.name);
|
|
138
|
+
editor.skillInstalled = displayName ? installedAgentDisplayNames.has(displayName) : false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// 5. Classify + mask
|
|
142
|
+
const classified = classifyEditors(editors);
|
|
143
|
+
const actionable = applyMasking(classified, mcpMode, skillsMode);
|
|
144
|
+
// "Already configured" surfaces editors whose MCP setup is valid (skill
|
|
145
|
+
// state doesn't matter for this signal — that's what skillsToInstall is for).
|
|
146
|
+
const actionableNames = new Set(actionable.map((c)=>c.editor.name));
|
|
147
|
+
const alreadyConfiguredEditors = editors.filter((e)=>e.configured && e.authStatus === 'valid' && !actionableNames.has(e.name)).map((e)=>e.name);
|
|
46
148
|
if (actionable.length === 0) {
|
|
47
|
-
mcpDebug('
|
|
48
|
-
const alreadyConfiguredEditors = editors.filter((e)=>e.configured && e.authStatus === 'valid').map((e)=>e.name);
|
|
149
|
+
mcpDebug('Nothing actionable after classification + masking');
|
|
49
150
|
if (explicit) {
|
|
50
151
|
ux.stdout(`${logSymbols.success} All detected editors are already configured`);
|
|
51
152
|
}
|
|
@@ -53,76 +154,94 @@ const NO_EDITORS_DETECTED_MESSAGE = `Couldn't auto-configure Sanity MCP server f
|
|
|
53
154
|
alreadyConfiguredEditors,
|
|
54
155
|
configuredEditors: [],
|
|
55
156
|
detectedEditors,
|
|
157
|
+
skillsToInstall: [],
|
|
56
158
|
skipped: true
|
|
57
159
|
};
|
|
58
160
|
}
|
|
59
|
-
//
|
|
60
|
-
|
|
61
|
-
//
|
|
62
|
-
|
|
161
|
+
// 6. Select editors to configure — prompt interactively or auto-select all.
|
|
162
|
+
// We only auto when neither side wants a prompt: MCP auto, or (MCP skip
|
|
163
|
+
// + skills auto). Anything that asks `mode: 'prompt'` for MCP wins the
|
|
164
|
+
// prompt even when skills would have auto-installed.
|
|
165
|
+
const shouldAuto = mcpMode === 'auto' || mcpMode === 'skip' && skillsMode === 'auto';
|
|
166
|
+
const selected = shouldAuto ? actionable : await promptForMCPSetup({
|
|
167
|
+
choices: actionable,
|
|
168
|
+
message: getPromptMessage(mcpMode, skillsMode)
|
|
169
|
+
});
|
|
63
170
|
if (!selected || selected.length === 0) {
|
|
64
|
-
// User deselected all editors
|
|
65
171
|
ux.stdout('MCP configuration skipped');
|
|
66
172
|
return {
|
|
67
173
|
alreadyConfiguredEditors,
|
|
68
174
|
configuredEditors: [],
|
|
69
175
|
detectedEditors,
|
|
176
|
+
skillsToInstall: [],
|
|
70
177
|
skipped: true
|
|
71
178
|
};
|
|
72
179
|
}
|
|
73
|
-
//
|
|
180
|
+
// 7. MCP write phase — only for choices that need MCP
|
|
181
|
+
const mcpSelected = selected.filter((c)=>c.action === 'mcp-only' || c.action === 'mcp-and-skill');
|
|
74
182
|
let token;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
183
|
+
const configuredEditors = [];
|
|
184
|
+
let mcpError;
|
|
185
|
+
if (mcpSelected.length > 0) {
|
|
186
|
+
const validEditor = editors.find((e)=>e.authStatus === 'valid' && e.existingToken);
|
|
187
|
+
if (validEditor?.existingToken) {
|
|
188
|
+
mcpDebug('Reusing valid token from %s', validEditor.name);
|
|
189
|
+
token = validEditor.existingToken;
|
|
190
|
+
}
|
|
191
|
+
const allOAuth = mcpSelected.every((c)=>EDITOR_CONFIGS[c.editor.name].oauthOnly);
|
|
192
|
+
if (!token && !allOAuth) {
|
|
193
|
+
try {
|
|
194
|
+
token = await createMCPToken();
|
|
195
|
+
} catch (error) {
|
|
196
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
197
|
+
mcpDebug('Error creating MCP token', error);
|
|
198
|
+
ux.warn(`Could not configure MCP: ${err.message}`);
|
|
199
|
+
ux.warn('You can set up MCP manually later using https://mcp.sanity.io');
|
|
200
|
+
mcpError = err;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (!mcpError) {
|
|
204
|
+
for (const choice of mcpSelected){
|
|
205
|
+
try {
|
|
206
|
+
await writeMCPConfig(choice.editor, token);
|
|
207
|
+
configuredEditors.push(choice.editor.name);
|
|
208
|
+
} catch (error) {
|
|
209
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
210
|
+
mcpDebug('Error writing MCP config for %s: %O', choice.editor.name, error);
|
|
211
|
+
ux.warn(`Could not configure MCP for ${choice.editor.name}: ${err.message}`);
|
|
212
|
+
ux.warn('You can set up MCP manually later using https://mcp.sanity.io');
|
|
213
|
+
mcpError = err;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (configuredEditors.length > 0) {
|
|
218
|
+
ux.stdout(`${logSymbols.success} MCP configured for ${configuredEditors.join(', ')}`);
|
|
99
219
|
}
|
|
100
220
|
}
|
|
101
|
-
//
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
221
|
+
// 8. Build skillsToInstall — only for choices the user kept, only when the
|
|
222
|
+
// associated MCP write succeeded (or wasn't needed).
|
|
223
|
+
const skillsToInstall = [];
|
|
224
|
+
if (skillsMode !== 'skip') {
|
|
225
|
+
for (const choice of selected){
|
|
226
|
+
if (choice.action === 'skill-only') {
|
|
227
|
+
const agent = getSkillsCliAgent(choice.editor.name);
|
|
228
|
+
if (agent) skillsToInstall.push(agent);
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
if (choice.action === 'mcp-and-skill' && configuredEditors.includes(choice.editor.name)) {
|
|
232
|
+
const agent = getSkillsCliAgent(choice.editor.name);
|
|
233
|
+
if (agent) skillsToInstall.push(agent);
|
|
234
|
+
}
|
|
107
235
|
}
|
|
108
|
-
} catch (error) {
|
|
109
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
110
|
-
mcpDebug('Error writing MCP config', error);
|
|
111
|
-
ux.warn(`Could not configure MCP: ${err.message}`);
|
|
112
|
-
ux.warn('You can set up MCP manually later using https://mcp.sanity.io');
|
|
113
|
-
return {
|
|
114
|
-
alreadyConfiguredEditors,
|
|
115
|
-
configuredEditors,
|
|
116
|
-
detectedEditors,
|
|
117
|
-
error: err,
|
|
118
|
-
skipped: false
|
|
119
|
-
};
|
|
120
236
|
}
|
|
121
|
-
ux.stdout(`${logSymbols.success} MCP configured for ${configuredEditors.join(', ')}`);
|
|
122
237
|
return {
|
|
123
238
|
alreadyConfiguredEditors,
|
|
124
239
|
configuredEditors,
|
|
125
240
|
detectedEditors,
|
|
241
|
+
error: mcpError,
|
|
242
|
+
skillsToInstall: [
|
|
243
|
+
...new Set(skillsToInstall)
|
|
244
|
+
],
|
|
126
245
|
skipped: false
|
|
127
246
|
};
|
|
128
247
|
}
|
|
@@ -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 {detectAvailableEditors} from './detectAvailableEditors.js'\nimport {EDITOR_CONFIGS, type EditorName} from './editorConfigs.js'\nimport {promptForMCPSetup} from './promptForMCPSetup.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\ninterface MCPSetupOptions {\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?: 'auto' | 'prompt' | 'skip'\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 skipped: boolean\n\n error?: Error\n}\n\n/**\n * Main MCP setup orchestration\n * Opt-out by default: runs automatically unless skip option is set\n */\nexport async function setupMCP(options?: MCPSetupOptions): Promise<MCPSetupResult> {\n const {explicit = false, mode = 'prompt'} = options ?? {}\n\n // 1. Check for explicit opt-out\n if (mode === 'skip') {\n mcpDebug('Skipping MCP configuration (mode: skip)')\n return {\n alreadyConfiguredEditors: [],\n configuredEditors: [],\n detectedEditors: [],\n skipped: true,\n }\n }\n\n // 2. Detect available editors (filters out unparseable configs)\n const 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 skipped: true,\n }\n }\n\n // 3. Validate existing tokens against the Sanity API\n await validateEditorTokens(editors)\n\n // 4. Check if there's anything actionable\n const actionable = editors.filter((e) => !e.configured || e.authStatus !== 'valid')\n\n if (actionable.length === 0) {\n mcpDebug('All editors configured with valid credentials')\n const alreadyConfiguredEditors = editors\n .filter((e) => e.configured && e.authStatus === 'valid')\n .map((e) => e.name)\n if (explicit) {\n ux.stdout(`${logSymbols.success} All detected editors are already configured`)\n }\n return {\n alreadyConfiguredEditors,\n configuredEditors: [],\n detectedEditors,\n skipped: true,\n }\n }\n\n // Non-actionable editors are already configured with valid credentials\n const alreadyConfiguredEditors = editors.filter((e) => !actionable.includes(e)).map((e) => e.name)\n\n // 5. Select editors to configure — prompt interactively or auto-select all\n const selected = mode === 'auto' ? actionable : await promptForMCPSetup(actionable)\n\n if (!selected || selected.length === 0) {\n // User deselected all editors\n ux.stdout('MCP configuration skipped')\n return {\n alreadyConfiguredEditors,\n configuredEditors: [],\n detectedEditors,\n skipped: true,\n }\n }\n\n // 6. Get a token — reuse a valid existing one or create a new one\n let token: string | undefined\n\n // Look for an existing valid token we can reuse\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 = selected.every((e) => EDITOR_CONFIGS[e.name].oauthOnly)\n\n // Fall back to creating a new token\n // If all editors use OAuth, we don't need to create a token\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 return {\n alreadyConfiguredEditors,\n configuredEditors: [],\n detectedEditors,\n error: err,\n skipped: false,\n }\n }\n }\n\n // 7. Write configs for each selected editor\n const configuredEditors: EditorName[] = []\n try {\n for (const editor of selected) {\n await writeMCPConfig(editor, token)\n configuredEditors.push(editor.name)\n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n mcpDebug('Error writing MCP config', 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 return {\n alreadyConfiguredEditors,\n configuredEditors,\n detectedEditors,\n error: err,\n skipped: false,\n }\n }\n\n ux.stdout(`${logSymbols.success} MCP configured for ${configuredEditors.join(', ')}`)\n\n return {\n alreadyConfiguredEditors,\n configuredEditors,\n detectedEditors,\n skipped: false,\n }\n}\n"],"names":["ux","subdebug","logSymbols","createMCPToken","MCP_SERVER_URL","detectAvailableEditors","EDITOR_CONFIGS","promptForMCPSetup","validateEditorTokens","writeMCPConfig","mcpDebug","NO_EDITORS_DETECTED_MESSAGE","setupMCP","options","explicit","mode","alreadyConfiguredEditors","configuredEditors","detectedEditors","skipped","editors","map","e","name","length","warn","actionable","filter","configured","authStatus","stdout","success","includes","selected","token","validEditor","find","existingToken","allOAuth","every","oauthOnly","error","err","Error","String","message","editor","push","join"],"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_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 +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"],"names":[],"mappings":"AAKA,
|
|
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,6 +1,6 @@
|
|
|
1
|
+
import { SchemaExtractionError } from '@sanity/cli-build/_internal/extract';
|
|
1
2
|
import { studioWorkerTask } from '@sanity/cli-core';
|
|
2
3
|
import { updateWorkspacesSchemas } from './updateWorkspaceSchema.js';
|
|
3
|
-
import { SchemaExtractionError } from './utils/SchemaExtractionError.js';
|
|
4
4
|
export async function deploySchemas(options) {
|
|
5
5
|
const { tag, verbose, workDir, workspaceName } = options;
|
|
6
6
|
const result = await studioWorkerTask(new URL('extractSanityWorkspace.worker.js', import.meta.url), {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/schema/deploySchemas.ts"],"sourcesContent":["import {studioWorkerTask} from '@sanity/cli-core'\nimport {type SchemaValidationProblemGroup} from '@sanity/types'\nimport {type Workspace} from 'sanity'\n\nimport {type ManifestSchemaType} from '../manifest/types.js'\nimport {type ExtractWorkspaceWorkerData} from './types.js'\nimport {updateWorkspacesSchemas, type WorkspaceSchemaInput} from './updateWorkspaceSchema.js'\
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/schema/deploySchemas.ts"],"sourcesContent":["import {SchemaExtractionError} from '@sanity/cli-build/_internal/extract'\nimport {studioWorkerTask} from '@sanity/cli-core'\nimport {type SchemaValidationProblemGroup} from '@sanity/types'\nimport {type Workspace} from 'sanity'\n\nimport {type ManifestSchemaType} from '../manifest/types.js'\nimport {type ExtractWorkspaceWorkerData} from './types.js'\nimport {updateWorkspacesSchemas, type WorkspaceSchemaInput} from './updateWorkspaceSchema.js'\n\ninterface DeploySchemasOptions {\n verbose: boolean\n workDir: string\n\n tag?: string\n workspaceName?: string\n}\n\ntype WorkspaceWithManifest = Workspace & {manifestSchema: ManifestSchemaType[]}\n\ntype ExtractWorkspaceWorkerMessage =\n | {\n error: string\n type: 'error'\n validation?: SchemaValidationProblemGroup[]\n }\n | {\n type: 'success'\n workspaces: WorkspaceWithManifest[]\n }\n\nexport async function deploySchemas(options: DeploySchemasOptions): Promise<void> {\n const {tag, verbose, workDir, workspaceName} = options\n\n const result = await studioWorkerTask<ExtractWorkspaceWorkerMessage>(\n new URL('extractSanityWorkspace.worker.js', import.meta.url),\n {\n name: 'extractSanityWorkspace',\n studioRootPath: workDir,\n workerData: {\n configPath: workDir,\n workDir,\n } satisfies ExtractWorkspaceWorkerData,\n },\n )\n\n if (result.type === 'error') {\n throw new SchemaExtractionError(result.error, result.validation)\n }\n\n const workspaces: WorkspaceSchemaInput[] = result.workspaces\n .filter((workspace) => !workspaceName || workspace.name === workspaceName)\n .map((workspace) => ({\n dataset: workspace.dataset,\n manifestSchema: workspace.manifestSchema,\n name: workspace.name,\n projectId: workspace.projectId,\n title: workspace.title,\n }))\n\n if (workspaces.length === 0) {\n const error = workspaceName\n ? new Error(`Found no workspaces named \"${workspaceName}\"`)\n : new Error('No workspaces found')\n throw error\n }\n\n await updateWorkspacesSchemas({\n tag,\n verbose,\n workspaces,\n })\n}\n"],"names":["SchemaExtractionError","studioWorkerTask","updateWorkspacesSchemas","deploySchemas","options","tag","verbose","workDir","workspaceName","result","URL","url","name","studioRootPath","workerData","configPath","type","error","validation","workspaces","filter","workspace","map","dataset","manifestSchema","projectId","title","length","Error"],"mappings":"AAAA,SAAQA,qBAAqB,QAAO,sCAAqC;AACzE,SAAQC,gBAAgB,QAAO,mBAAkB;AAMjD,SAAQC,uBAAuB,QAAkC,6BAA4B;AAuB7F,OAAO,eAAeC,cAAcC,OAA6B;IAC/D,MAAM,EAACC,GAAG,EAAEC,OAAO,EAAEC,OAAO,EAAEC,aAAa,EAAC,GAAGJ;IAE/C,MAAMK,SAAS,MAAMR,iBACnB,IAAIS,IAAI,oCAAoC,YAAYC,GAAG,GAC3D;QACEC,MAAM;QACNC,gBAAgBN;QAChBO,YAAY;YACVC,YAAYR;YACZA;QACF;IACF;IAGF,IAAIE,OAAOO,IAAI,KAAK,SAAS;QAC3B,MAAM,IAAIhB,sBAAsBS,OAAOQ,KAAK,EAAER,OAAOS,UAAU;IACjE;IAEA,MAAMC,aAAqCV,OAAOU,UAAU,CACzDC,MAAM,CAAC,CAACC,YAAc,CAACb,iBAAiBa,UAAUT,IAAI,KAAKJ,eAC3Dc,GAAG,CAAC,CAACD,YAAe,CAAA;YACnBE,SAASF,UAAUE,OAAO;YAC1BC,gBAAgBH,UAAUG,cAAc;YACxCZ,MAAMS,UAAUT,IAAI;YACpBa,WAAWJ,UAAUI,SAAS;YAC9BC,OAAOL,UAAUK,KAAK;QACxB,CAAA;IAEF,IAAIP,WAAWQ,MAAM,KAAK,GAAG;QAC3B,MAAMV,QAAQT,gBACV,IAAIoB,MAAM,CAAC,2BAA2B,EAAEpB,cAAc,CAAC,CAAC,IACxD,IAAIoB,MAAM;QACd,MAAMX;IACR;IAEA,MAAMf,wBAAwB;QAC5BG;QACAC;QACAa;IACF;AACF"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { isMainThread, parentPort, workerData } from 'node:worker_threads';
|
|
2
|
+
import { extractValidationFromSchemaError } from '@sanity/cli-build/_internal/extract';
|
|
2
3
|
import { getStudioWorkspaces, safeStructuredClone } from '@sanity/cli-core';
|
|
3
4
|
import { extractManifestSchemaTypes } from '../manifest/extractWorkspaceManifest.js';
|
|
4
5
|
import { extractWorkspaceWorkerData } from './types.js';
|
|
5
|
-
import { extractValidationFromSchemaError } from './utils/extractValidationFromSchemaError.js';
|
|
6
6
|
if (isMainThread || !parentPort) {
|
|
7
7
|
throw new Error('Should only be run in a worker!');
|
|
8
8
|
}
|