@sanity/cli 7.2.3 → 7.3.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/bin/run.js +3 -12
- package/dist/actions/auth/authServer.js +37 -20
- package/dist/actions/auth/authServer.js.map +1 -1
- package/dist/actions/backup/assertDatasetExist.js +3 -3
- package/dist/actions/backup/assertDatasetExist.js.map +1 -1
- package/dist/actions/build/buildApp.js +15 -5
- package/dist/actions/build/buildApp.js.map +1 -1
- package/dist/actions/build/buildStaticFiles.js +57 -5
- package/dist/actions/build/buildStaticFiles.js.map +1 -1
- package/dist/actions/build/buildStudio.js +13 -5
- package/dist/actions/build/buildStudio.js.map +1 -1
- package/dist/actions/build/handlePrereleaseVersions.js +2 -2
- package/dist/actions/build/handlePrereleaseVersions.js.map +1 -1
- package/dist/actions/dataset/resolveDataset.js +2 -2
- package/dist/actions/dataset/resolveDataset.js.map +1 -1
- package/dist/actions/deploy/deployApp.js +43 -5
- package/dist/actions/deploy/deployApp.js.map +1 -1
- package/dist/actions/deploy/deployStudio.js +14 -2
- package/dist/actions/deploy/deployStudio.js.map +1 -1
- package/dist/actions/deploy/viewDeployment.js +32 -0
- package/dist/actions/deploy/viewDeployment.js.map +1 -0
- package/dist/actions/dev/devAction.js +219 -4
- package/dist/actions/dev/devAction.js.map +1 -1
- package/dist/actions/dev/registration/deriveInterfaces.js +46 -0
- package/dist/actions/dev/registration/deriveInterfaces.js.map +1 -0
- package/dist/actions/dev/registration/extractDevServerManifest.js +30 -0
- package/dist/actions/dev/registration/extractDevServerManifest.js.map +1 -0
- package/dist/actions/dev/registration/interfaceSetId.js +41 -0
- package/dist/actions/dev/registration/interfaceSetId.js.map +1 -0
- package/dist/actions/dev/registration/startDevManifestWatcher.js +104 -0
- package/dist/actions/dev/registration/startDevManifestWatcher.js.map +1 -0
- package/dist/actions/dev/registration/startDevServerRegistration.js +121 -0
- package/dist/actions/dev/registration/startDevServerRegistration.js.map +1 -0
- package/dist/actions/dev/servers/getDashboardAppUrl.js.map +1 -0
- package/dist/actions/dev/servers/getDevServerConfig.js +45 -0
- package/dist/actions/dev/servers/getDevServerConfig.js.map +1 -0
- package/dist/actions/dev/servers/startAppDevServer.js +78 -0
- package/dist/actions/dev/servers/startAppDevServer.js.map +1 -0
- package/dist/actions/dev/{startStudioDevServer.js → servers/startStudioDevServer.js} +20 -16
- package/dist/actions/dev/servers/startStudioDevServer.js.map +1 -0
- package/dist/actions/dev/types.js +7 -1
- package/dist/actions/dev/types.js.map +1 -1
- package/dist/actions/dev/workbench/startWorkbenchDevServer.js +276 -0
- package/dist/actions/dev/workbench/startWorkbenchDevServer.js.map +1 -0
- package/dist/actions/dev/workbench/writeWorkbenchRuntime.js +66 -0
- package/dist/actions/dev/workbench/writeWorkbenchRuntime.js.map +1 -0
- package/dist/actions/init/bootstrapLocalTemplate.js +15 -3
- package/dist/actions/init/bootstrapLocalTemplate.js.map +1 -1
- package/dist/actions/init/bootstrapTemplate.js +3 -2
- package/dist/actions/init/bootstrapTemplate.js.map +1 -1
- package/dist/actions/init/createAppCliConfig.js +5 -2
- package/dist/actions/init/createAppCliConfig.js.map +1 -1
- package/dist/actions/init/createCliConfig.js +5 -3
- package/dist/actions/init/createCliConfig.js.map +1 -1
- package/dist/actions/init/createStudioConfig.js.map +1 -1
- package/dist/actions/init/initAction.js +11 -4
- package/dist/actions/init/initAction.js.map +1 -1
- package/dist/actions/init/initApp.js +2 -1
- package/dist/actions/init/initApp.js.map +1 -1
- package/dist/actions/init/initStudio.js +2 -1
- package/dist/actions/init/initStudio.js.map +1 -1
- package/dist/actions/init/scaffoldTemplate.js +3 -2
- package/dist/actions/init/scaffoldTemplate.js.map +1 -1
- package/dist/actions/init/templates/pageBuilder.js +1 -1
- package/dist/actions/init/templates/pageBuilder.js.map +1 -1
- package/dist/actions/init/types.js +1 -0
- package/dist/actions/init/types.js.map +1 -1
- package/dist/actions/manifest/{extractAppManifest.js → extractCoreAppManifest.js} +30 -11
- package/dist/actions/manifest/extractCoreAppManifest.js.map +1 -0
- package/dist/actions/manifest/extractManifest.js +7 -7
- package/dist/actions/manifest/extractManifest.js.map +1 -1
- package/dist/actions/manifest/iconResolver.js +2 -3
- package/dist/actions/manifest/iconResolver.js.map +1 -1
- package/dist/actions/manifest/sanitizeIcon.js +13 -0
- package/dist/actions/manifest/sanitizeIcon.js.map +1 -0
- package/dist/actions/manifest/types.js +4 -9
- package/dist/actions/manifest/types.js.map +1 -1
- package/dist/actions/manifest/writeManifestFile.js +1 -1
- package/dist/actions/manifest/writeManifestFile.js.map +1 -1
- package/dist/actions/mcp/editorConfigs.js +26 -2
- package/dist/actions/mcp/editorConfigs.js.map +1 -1
- package/dist/actions/mcp/setupMCP.js +24 -25
- package/dist/actions/mcp/setupMCP.js.map +1 -1
- package/dist/actions/mcp/types.js.map +1 -1
- package/dist/actions/skills/configureSkills.js +49 -0
- package/dist/actions/skills/configureSkills.js.map +1 -0
- package/dist/actions/skills/readSkillState.js +40 -5
- package/dist/actions/skills/readSkillState.js.map +1 -1
- package/dist/actions/skills/setupSkills.js +47 -6
- package/dist/actions/skills/setupSkills.js.map +1 -1
- package/dist/actions/skills/skillCandidates.js +40 -0
- package/dist/actions/skills/skillCandidates.js.map +1 -0
- package/dist/actions/telemetry/telemetryDisclosure.js +2 -3
- package/dist/actions/telemetry/telemetryDisclosure.js.map +1 -1
- package/dist/actions/tokens/validateRole.js +3 -3
- package/dist/actions/tokens/validateRole.js.map +1 -1
- package/dist/commands/backups/disable.js +1 -1
- package/dist/commands/backups/disable.js.map +1 -1
- package/dist/commands/backups/download.js +1 -1
- package/dist/commands/backups/download.js.map +1 -1
- package/dist/commands/backups/enable.js +1 -1
- package/dist/commands/backups/enable.js.map +1 -1
- package/dist/commands/backups/list.js +1 -1
- package/dist/commands/backups/list.js.map +1 -1
- package/dist/commands/datasets/embeddings/disable.js +1 -0
- package/dist/commands/datasets/embeddings/disable.js.map +1 -1
- package/dist/commands/datasets/embeddings/enable.js +1 -0
- package/dist/commands/datasets/embeddings/enable.js.map +1 -1
- package/dist/commands/datasets/embeddings/status.js +1 -0
- package/dist/commands/datasets/embeddings/status.js.map +1 -1
- package/dist/commands/dev.js +11 -1
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/init.js +7 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/manifest/extract.js +7 -2
- package/dist/commands/manifest/extract.js.map +1 -1
- package/dist/commands/mcp/configure.js +1 -0
- package/dist/commands/mcp/configure.js.map +1 -1
- package/dist/commands/skills/install.js +40 -0
- package/dist/commands/skills/install.js.map +1 -0
- package/dist/commands/tokens/add.js +1 -1
- package/dist/commands/tokens/add.js.map +1 -1
- package/dist/exports/index.d.ts +6 -0
- package/dist/exports/index.js +8 -0
- package/dist/exports/index.js.map +1 -1
- package/dist/exports/runtime.d.ts +41 -0
- package/dist/exports/runtime.js +13 -0
- package/dist/exports/runtime.js.map +1 -0
- package/dist/hooks/prerun/setupTelemetry.js +3 -1
- package/dist/hooks/prerun/setupTelemetry.js.map +1 -1
- package/dist/server/devServer.js +8 -3
- package/dist/server/devServer.js.map +1 -1
- package/dist/services/documents.js +0 -1
- package/dist/services/documents.js.map +1 -1
- package/dist/telemetry/skills.telemetry.js +8 -0
- package/dist/telemetry/skills.telemetry.js.map +1 -0
- package/dist/util/compareDependencyVersions.js +1 -1
- package/dist/util/compareDependencyVersions.js.map +1 -1
- package/dist/util/determineIsApp.js +12 -1
- package/dist/util/determineIsApp.js.map +1 -1
- package/dist/util/getSharedServerConfig.js +2 -1
- package/dist/util/getSharedServerConfig.js.map +1 -1
- package/dist/util/resolveReactStrictMode.js +11 -0
- package/dist/util/resolveReactStrictMode.js.map +1 -0
- package/oclif.config.js +2 -1
- package/oclif.manifest.json +146 -113
- package/package.json +26 -17
- package/templates/page-builder/schemaTypes/index.js +1 -1
- package/dist/actions/build/checkRequiredDependencies.js +0 -124
- package/dist/actions/build/checkRequiredDependencies.js.map +0 -1
- package/dist/actions/build/getAutoUpdatesImportMap.js +0 -66
- package/dist/actions/build/getAutoUpdatesImportMap.js.map +0 -1
- package/dist/actions/dev/getDashboardAppUrl.js.map +0 -1
- package/dist/actions/dev/getDevServerConfig.js +0 -32
- package/dist/actions/dev/getDevServerConfig.js.map +0 -1
- package/dist/actions/dev/startAppDevServer.js +0 -59
- package/dist/actions/dev/startAppDevServer.js.map +0 -1
- package/dist/actions/dev/startStudioDevServer.js.map +0 -1
- package/dist/actions/manifest/extractAppManifest.js.map +0 -1
- /package/dist/actions/dev/{getDashboardAppUrl.js → servers/getDashboardAppUrl.js} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/manifest/iconResolver.tsx"],"sourcesContent":["import {resolveLocalPackage} from '@sanity/cli-core'\
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/manifest/iconResolver.tsx"],"sourcesContent":["import {resolveLocalPackage} from '@sanity/cli-core'\n\nimport {manifestDebug} from './debug.js'\nimport {resolveSchemaIcon, type SchemaIconProps} from './resolveSchemaIcon.js'\nimport {sanitizeIcon} from './sanitizeIcon.js'\n\n/**\n * Resolves an icon to a sanitized HTML string.\n * Uses react-dom/server to capture styles during SSR.\n *\n * react-dom/server is resolved from the studio's working directory to ensure\n * the same React instance is used by both the server renderer and the studio's\n * components. Using the CLI's own react-dom/server would cause a dual-React\n * instance problem where the dispatcher set by one instance is invisible to the other.\n */\nexport const resolveIcon = async (props: SchemaIconProps): Promise<string | null> => {\n try {\n const [{renderToReadableStream}, element] = await Promise.all([\n resolveLocalPackage<typeof import('react-dom/server')>('react-dom/server', props.workDir),\n resolveSchemaIcon(props),\n ])\n const stream = await renderToReadableStream(element)\n await stream.allReady\n\n const reader = stream.getReader()\n const chunks: Uint8Array[] = []\n while (true) {\n const {done, value} = await reader.read()\n if (done) break\n chunks.push(value)\n }\n const html = new TextDecoder().decode(Buffer.concat(chunks))\n return sanitizeIcon(html)\n } catch (error) {\n manifestDebug('Error resolving icon', error)\n return null\n }\n}\n"],"names":["resolveLocalPackage","manifestDebug","resolveSchemaIcon","sanitizeIcon","resolveIcon","props","renderToReadableStream","element","Promise","all","workDir","stream","allReady","reader","getReader","chunks","done","value","read","push","html","TextDecoder","decode","Buffer","concat","error"],"mappings":"AAAA,SAAQA,mBAAmB,QAAO,mBAAkB;AAEpD,SAAQC,aAAa,QAAO,aAAY;AACxC,SAAQC,iBAAiB,QAA6B,yBAAwB;AAC9E,SAAQC,YAAY,QAAO,oBAAmB;AAE9C;;;;;;;;CAQC,GACD,OAAO,MAAMC,cAAc,OAAOC;IAChC,IAAI;QACF,MAAM,CAAC,EAACC,sBAAsB,EAAC,EAAEC,QAAQ,GAAG,MAAMC,QAAQC,GAAG,CAAC;YAC5DT,oBAAuD,oBAAoBK,MAAMK,OAAO;YACxFR,kBAAkBG;SACnB;QACD,MAAMM,SAAS,MAAML,uBAAuBC;QAC5C,MAAMI,OAAOC,QAAQ;QAErB,MAAMC,SAASF,OAAOG,SAAS;QAC/B,MAAMC,SAAuB,EAAE;QAC/B,MAAO,KAAM;YACX,MAAM,EAACC,IAAI,EAAEC,KAAK,EAAC,GAAG,MAAMJ,OAAOK,IAAI;YACvC,IAAIF,MAAM;YACVD,OAAOI,IAAI,CAACF;QACd;QACA,MAAMG,OAAO,IAAIC,cAAcC,MAAM,CAACC,OAAOC,MAAM,CAACT;QACpD,OAAOZ,aAAaiB;IACtB,EAAE,OAAOK,OAAO;QACdxB,cAAc,wBAAwBwB;QACtC,OAAO;IACT;AACF,EAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import DOMPurify from 'isomorphic-dompurify';
|
|
2
|
+
import { config } from './purifyConfig.js';
|
|
3
|
+
/**
|
|
4
|
+
* Sanitize icon markup against the manifest allowlist (see {@link config}).
|
|
5
|
+
*
|
|
6
|
+
* Shared by the studio manifest icon resolver and the core app manifest so
|
|
7
|
+
* every icon we inline — whether rendered from a React component or read from
|
|
8
|
+
* a file on disk — passes through the exact same trusted subset of SVG/HTML.
|
|
9
|
+
*/ export function sanitizeIcon(html) {
|
|
10
|
+
return DOMPurify.sanitize(html.trim(), config);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=sanitizeIcon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/manifest/sanitizeIcon.ts"],"sourcesContent":["import DOMPurify from 'isomorphic-dompurify'\n\nimport {config} from './purifyConfig.js'\n\n/**\n * Sanitize icon markup against the manifest allowlist (see {@link config}).\n *\n * Shared by the studio manifest icon resolver and the core app manifest so\n * every icon we inline — whether rendered from a React component or read from\n * a file on disk — passes through the exact same trusted subset of SVG/HTML.\n */\nexport function sanitizeIcon(html: string): string {\n return DOMPurify.sanitize(html.trim(), config)\n}\n"],"names":["DOMPurify","config","sanitizeIcon","html","sanitize","trim"],"mappings":"AAAA,OAAOA,eAAe,uBAAsB;AAE5C,SAAQC,MAAM,QAAO,oBAAmB;AAExC;;;;;;CAMC,GACD,OAAO,SAASC,aAAaC,IAAY;IACvC,OAAOH,UAAUI,QAAQ,CAACD,KAAKE,IAAI,IAAIJ;AACzC"}
|
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
import { z } from 'zod/mini';
|
|
2
2
|
export const SANITY_WORKSPACE_SCHEMA_ID_PREFIX = '_.schemas';
|
|
3
3
|
export const CURRENT_WORKSPACE_SCHEMA_VERSION = '2025-05-01';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
*/ export const coreAppManifestSchema = z.object({
|
|
9
|
-
icon: z.optional(z.string()),
|
|
10
|
-
title: z.optional(z.string()),
|
|
11
|
-
version: z.string()
|
|
12
|
-
});
|
|
4
|
+
// The studio/app manifest contract lives in `@sanity/cli-core` so the CLI and
|
|
5
|
+
// the workbench dev-server registry validate against the same schemas without
|
|
6
|
+
// depending on each other.
|
|
7
|
+
export { coreAppManifestSchema } from '@sanity/cli-core';
|
|
13
8
|
export const extractManifestWorkerData = z.object({
|
|
14
9
|
configPath: z.string(),
|
|
15
10
|
workDir: z.string()
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/manifest/types.ts"],"sourcesContent":["import {type SanityDocumentLike} from '@sanity/types'\nimport {type MediaLibraryConfig} from 'sanity'\nimport {z} from 'zod/mini'\n\nexport const SANITY_WORKSPACE_SCHEMA_ID_PREFIX = '_.schemas'\nexport const CURRENT_WORKSPACE_SCHEMA_VERSION = '2025-05-01'\n\nexport type ManifestSerializable =\n | boolean\n | ManifestSerializable[]\n | number\n | string\n | {[k: string]: ManifestSerializable}\n\nexport interface CreateManifest {\n createdAt: string\n studioVersion: string | null\n version: number\n workspaces: ManifestWorkspaceFile[]\n}\n\n
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/manifest/types.ts"],"sourcesContent":["import {type SanityDocumentLike} from '@sanity/types'\nimport {type MediaLibraryConfig} from 'sanity'\nimport {z} from 'zod/mini'\n\nexport const SANITY_WORKSPACE_SCHEMA_ID_PREFIX = '_.schemas'\nexport const CURRENT_WORKSPACE_SCHEMA_VERSION = '2025-05-01'\n\nexport type ManifestSerializable =\n | boolean\n | ManifestSerializable[]\n | number\n | string\n | {[k: string]: ManifestSerializable}\n\nexport interface CreateManifest {\n createdAt: string\n studioVersion: string | null\n version: number\n workspaces: ManifestWorkspaceFile[]\n}\n\n// The studio/app manifest contract lives in `@sanity/cli-core` so the CLI and\n// the workbench dev-server registry validate against the same schemas without\n// depending on each other.\nexport {type CoreAppManifest, coreAppManifestSchema, type StudioManifest} from '@sanity/cli-core'\n\nexport interface ManifestWorkspaceFile extends Omit<CreateWorkspaceManifest, 'schema' | 'tools'> {\n schema: string // filename\n tools: string // filename\n}\n\nexport interface CreateWorkspaceManifest {\n basePath: string\n dataset: string\n /**\n * returns null in the case of the icon not being able to be stringified\n */\n icon: string | null\n name: string\n projectId: string\n schema: ManifestSchemaType[]\n tools: ManifestTool[]\n\n mediaLibrary?: MediaLibraryConfig\n subtitle?: string\n title?: string\n}\n\nexport interface ManifestSchemaType {\n name: string\n type: string\n\n deprecated?: {\n reason: string\n }\n fields?: ManifestField[]\n fieldsets?: ManifestFieldset[]\n hidden?: 'conditional' | boolean\n lists?: ManifestTitledValue[]\n //portable text\n marks?: {\n annotations?: ManifestArrayMember[]\n decorators?: ManifestTitledValue[]\n }\n of?: ManifestArrayMember[]\n options?: Record<string, ManifestSerializable>\n preview?: {\n select: Record<string, string>\n }\n readOnly?: 'conditional' | boolean\n styles?: ManifestTitledValue[]\n title?: string\n to?: ManifestReferenceMember[]\n validation?: ManifestValidationGroup[]\n\n // userland (assignable to ManifestSerializable | undefined)\n // not included to add some typesafty to extractManifest\n // [index: string]: unknown\n}\n\nexport interface ManifestFieldset {\n [index: string]: ManifestSerializable | undefined\n name: string\n\n title?: string\n}\n\nexport interface ManifestTitledValue {\n value: string\n\n title?: string\n}\n\ntype ManifestArrayMember = Omit<ManifestSchemaType, 'name'> & {name?: string}\ntype ManifestReferenceMember = Omit<ManifestSchemaType, 'name'> & {name?: string}\nexport type ManifestField = ManifestSchemaType & {fieldset?: string}\n\nexport interface ManifestValidationGroup {\n rules: ManifestValidationRule[]\n\n level?: 'error' | 'info' | 'warning'\n message?: string\n}\n\nexport type ManifestValidationRule = {\n [index: string]: ManifestSerializable | undefined\n constraint?: ManifestSerializable\n flag: string\n}\n\nexport interface ManifestTool {\n /**\n * returns null in the case of the icon not being able to be stringified\n */\n icon: string | null\n name: string\n title: string\n type: string | null\n}\n\nexport type DefaultWorkspaceSchemaId = `${typeof SANITY_WORKSPACE_SCHEMA_ID_PREFIX}.${string}`\ntype PrefixedWorkspaceSchemaId = `${DefaultWorkspaceSchemaId}.${string}`\nexport type WorkspaceSchemaId = DefaultWorkspaceSchemaId | PrefixedWorkspaceSchemaId\n\nexport interface StoredWorkspaceSchema extends SanityDocumentLike {\n _id: WorkspaceSchemaId\n _type: 'system.schema'\n /**\n * The API expects JSON coming in, but will store a string to save on attribute paths.\n * Consumers must use JSON.parse on the value, but we deploy to the API using ManifestSchemaType[]\n */\n schema: ManifestSchemaType[] | string\n /* api-like version string: date at which the format had a meaningful change */\n version: typeof CURRENT_WORKSPACE_SCHEMA_VERSION | undefined\n workspace: {\n name: string\n title?: string\n }\n\n tag?: string\n}\n\nexport const extractManifestWorkerData = z.object({configPath: z.string(), workDir: z.string()})\n\nexport type ExtractManifestWorkerData = z.infer<typeof extractManifestWorkerData>\n"],"names":["z","SANITY_WORKSPACE_SCHEMA_ID_PREFIX","CURRENT_WORKSPACE_SCHEMA_VERSION","coreAppManifestSchema","extractManifestWorkerData","object","configPath","string","workDir"],"mappings":"AAEA,SAAQA,CAAC,QAAO,WAAU;AAE1B,OAAO,MAAMC,oCAAoC,YAAW;AAC5D,OAAO,MAAMC,mCAAmC,aAAY;AAgB5D,8EAA8E;AAC9E,8EAA8E;AAC9E,2BAA2B;AAC3B,SAA8BC,qBAAqB,QAA4B,mBAAkB;AAsHjG,OAAO,MAAMC,4BAA4BJ,EAAEK,MAAM,CAAC;IAACC,YAAYN,EAAEO,MAAM;IAAIC,SAASR,EAAEO,MAAM;AAAE,GAAE"}
|
|
@@ -2,7 +2,7 @@ import { mkdir, writeFile } from 'node:fs/promises';
|
|
|
2
2
|
import { isAbsolute, join, resolve } from 'node:path';
|
|
3
3
|
import { getLocalPackageVersion, subdebug } from '@sanity/cli-core';
|
|
4
4
|
import { writeWorkspaceFiles } from './writeWorkspaceFiles.js';
|
|
5
|
-
const MANIFEST_FILENAME = 'create-manifest.json';
|
|
5
|
+
export const MANIFEST_FILENAME = 'create-manifest.json';
|
|
6
6
|
const debug = subdebug('writeManifestFile');
|
|
7
7
|
export async function writeManifestFile({ outPath, workDir, workspaceManifests }) {
|
|
8
8
|
const staticPath = isAbsolute(outPath) ? outPath : resolve(join(workDir, outPath));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/manifest/writeManifestFile.ts"],"sourcesContent":["import {mkdir, writeFile} from 'node:fs/promises'\nimport {isAbsolute, join, resolve} from 'node:path'\n\nimport {getLocalPackageVersion, subdebug} from '@sanity/cli-core'\n\nimport {type CreateManifest, type CreateWorkspaceManifest} from './types.js'\nimport {writeWorkspaceFiles} from './writeWorkspaceFiles.js'\n\
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/manifest/writeManifestFile.ts"],"sourcesContent":["import {mkdir, writeFile} from 'node:fs/promises'\nimport {isAbsolute, join, resolve} from 'node:path'\n\nimport {getLocalPackageVersion, subdebug} from '@sanity/cli-core'\n\nimport {type CreateManifest, type CreateWorkspaceManifest} from './types.js'\nimport {writeWorkspaceFiles} from './writeWorkspaceFiles.js'\n\nexport const MANIFEST_FILENAME = 'create-manifest.json'\n\nconst debug = subdebug('writeManifestFile')\n\nexport interface WriteManifestFileOptions {\n /**\n * Target directory for the manifest files. May be absolute or relative — relative\n * paths are resolved against `workDir`.\n */\n outPath: string\n /**\n * Studio root directory. Used to resolve a relative `outPath` and to look up the\n * local `sanity` package version that gets stamped onto the manifest.\n */\n workDir: string\n /** Extracted workspace manifests to serialize alongside the top-level manifest. */\n workspaceManifests: CreateWorkspaceManifest[]\n}\n\nexport async function writeManifestFile({\n outPath,\n workDir,\n workspaceManifests,\n}: WriteManifestFileOptions) {\n const staticPath = isAbsolute(outPath) ? outPath : resolve(join(workDir, outPath))\n debug('Writing manifest to %s', staticPath)\n const path = join(staticPath, MANIFEST_FILENAME)\n\n await mkdir(staticPath, {recursive: true})\n\n const workspaceFiles = await writeWorkspaceFiles(workspaceManifests, staticPath)\n\n const manifest: CreateManifest = {\n /**\n * Version history:\n * 1: Initial release.\n * 2: Added tools file.\n * 3. Added studioVersion field.\n */\n createdAt: new Date().toISOString(),\n studioVersion: await getLocalPackageVersion('sanity', workDir),\n version: 3,\n workspaces: workspaceFiles,\n }\n\n await writeFile(path, JSON.stringify(manifest, null, 2))\n}\n"],"names":["mkdir","writeFile","isAbsolute","join","resolve","getLocalPackageVersion","subdebug","writeWorkspaceFiles","MANIFEST_FILENAME","debug","writeManifestFile","outPath","workDir","workspaceManifests","staticPath","path","recursive","workspaceFiles","manifest","createdAt","Date","toISOString","studioVersion","version","workspaces","JSON","stringify"],"mappings":"AAAA,SAAQA,KAAK,EAAEC,SAAS,QAAO,mBAAkB;AACjD,SAAQC,UAAU,EAAEC,IAAI,EAAEC,OAAO,QAAO,YAAW;AAEnD,SAAQC,sBAAsB,EAAEC,QAAQ,QAAO,mBAAkB;AAGjE,SAAQC,mBAAmB,QAAO,2BAA0B;AAE5D,OAAO,MAAMC,oBAAoB,uBAAsB;AAEvD,MAAMC,QAAQH,SAAS;AAiBvB,OAAO,eAAeI,kBAAkB,EACtCC,OAAO,EACPC,OAAO,EACPC,kBAAkB,EACO;IACzB,MAAMC,aAAaZ,WAAWS,WAAWA,UAAUP,QAAQD,KAAKS,SAASD;IACzEF,MAAM,0BAA0BK;IAChC,MAAMC,OAAOZ,KAAKW,YAAYN;IAE9B,MAAMR,MAAMc,YAAY;QAACE,WAAW;IAAI;IAExC,MAAMC,iBAAiB,MAAMV,oBAAoBM,oBAAoBC;IAErE,MAAMI,WAA2B;QAC/B;;;;;KAKC,GACDC,WAAW,IAAIC,OAAOC,WAAW;QACjCC,eAAe,MAAMjB,uBAAuB,UAAUO;QACtDW,SAAS;QACTC,YAAYP;IACd;IAEA,MAAMhB,UAAUc,MAAMU,KAAKC,SAAS,CAACR,UAAU,MAAM;AACvD"}
|
|
@@ -160,12 +160,14 @@ function readTokenFromHttpHeaders(serverConfig) {
|
|
|
160
160
|
return extractBearerToken(serverConfig.http_headers);
|
|
161
161
|
}
|
|
162
162
|
// -- Defaults & build server config functions --
|
|
163
|
+
export const UNIVERSAL_SKILLS_DIR = '.agents/skills';
|
|
163
164
|
/** Most editors share these values — entries only need to declare `detect` + any overrides. */ const EDITOR_DEFAULTS = {
|
|
164
165
|
buildServerConfig: defaultHttpConfig,
|
|
165
166
|
configKey: 'mcpServers',
|
|
166
167
|
format: 'jsonc',
|
|
167
168
|
oauthOnly: false,
|
|
168
|
-
readToken: readTokenFromHeaders
|
|
169
|
+
readToken: readTokenFromHeaders,
|
|
170
|
+
skillsDir: UNIVERSAL_SKILLS_DIR
|
|
169
171
|
};
|
|
170
172
|
function buildAntigravityServerConfig(token) {
|
|
171
173
|
return {
|
|
@@ -245,7 +247,8 @@ function buildZedServerConfig(token) {
|
|
|
245
247
|
...EDITOR_DEFAULTS,
|
|
246
248
|
detect: detectClaudeCode,
|
|
247
249
|
oauthOnly: true,
|
|
248
|
-
skillsCliAgent: 'claude-code'
|
|
250
|
+
skillsCliAgent: 'claude-code',
|
|
251
|
+
skillsDir: '.claude/skills'
|
|
249
252
|
},
|
|
250
253
|
// Doc: https://github.com/cline/cline — VS Code extension (saoudrizwan.claude-dev)
|
|
251
254
|
// Path: <VS Code User>/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json
|
|
@@ -361,9 +364,30 @@ export function getSkillsCliAgent(editorName) {
|
|
|
361
364
|
'github-copilot': 'GitHub Copilot',
|
|
362
365
|
opencode: 'OpenCode'
|
|
363
366
|
};
|
|
367
|
+
export function isUniversalSkillsCliAgentByEditorName(editorName) {
|
|
368
|
+
return EDITOR_CONFIGS[editorName]?.skillsDir === UNIVERSAL_SKILLS_DIR;
|
|
369
|
+
}
|
|
364
370
|
/** Display name used by the skills CLI for the given editor, if it has a mapping. */ export function getSkillsCliAgentDisplayName(editorName) {
|
|
365
371
|
const agent = getSkillsCliAgent(editorName);
|
|
366
372
|
return agent ? SKILLS_CLI_AGENT_DISPLAY_NAMES[agent] : undefined;
|
|
367
373
|
}
|
|
374
|
+
/** Display name for a skills-CLI agent ID (e.g. `'cursor'` → `'Cursor'`). */ export function getSkillsCliAgentDisplayNameById(agentId) {
|
|
375
|
+
return SKILLS_CLI_AGENT_DISPLAY_NAMES[agentId];
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* The relative, home-anchored directory the `skills` CLI installs into for a
|
|
379
|
+
* given agent ID (e.g. `'cursor'` → `'.agents/skills'`, `'claude-code'` →
|
|
380
|
+
* `'.claude/skills'`). Derived from `EDITOR_CONFIGS` so it stays a single
|
|
381
|
+
* source of truth.
|
|
382
|
+
*/ export function getSkillsCliAgentSkillsDir(agentName) {
|
|
383
|
+
for (const name of Object.keys(EDITOR_CONFIGS)){
|
|
384
|
+
const config = EDITOR_CONFIGS[name];
|
|
385
|
+
const agent = getSkillsCliAgent(name);
|
|
386
|
+
if (agent === agentName) {
|
|
387
|
+
return config.skillsDir;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return undefined;
|
|
391
|
+
}
|
|
368
392
|
|
|
369
393
|
//# sourceMappingURL=editorConfigs.js.map
|
|
@@ -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 * 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
|
+
{"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 * The directory to install skills to.\n */\n skillsDir?: 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\nexport const UNIVERSAL_SKILLS_DIR = '.agents/skills'\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 skillsDir: UNIVERSAL_SKILLS_DIR,\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 skillsDir: '.claude/skills',\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\nexport function isUniversalSkillsCliAgentByEditorName(editorName: EditorName): boolean {\n return EDITOR_CONFIGS[editorName]?.skillsDir === UNIVERSAL_SKILLS_DIR\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\n/** Display name for a skills-CLI agent ID (e.g. `'cursor'` → `'Cursor'`). */\nexport function getSkillsCliAgentDisplayNameById(agentId: string): string | undefined {\n return SKILLS_CLI_AGENT_DISPLAY_NAMES[agentId]\n}\n\n/**\n * The relative, home-anchored directory the `skills` CLI installs into for a\n * given agent ID (e.g. `'cursor'` → `'.agents/skills'`, `'claude-code'` →\n * `'.claude/skills'`). Derived from `EDITOR_CONFIGS` so it stays a single\n * source of truth.\n */\nexport function getSkillsCliAgentSkillsDir(agentName: string): string | undefined {\n for (const name of Object.keys(EDITOR_CONFIGS) as EditorName[]) {\n const config = EDITOR_CONFIGS[name]\n const agent = getSkillsCliAgent(name)\n if (agent === agentName) {\n return config.skillsDir\n }\n }\n return 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","UNIVERSAL_SKILLS_DIR","EDITOR_DEFAULTS","buildServerConfig","configKey","format","oauthOnly","readToken","skillsDir","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","isUniversalSkillsCliAgentByEditorName","getSkillsCliAgentDisplayName","agent","getSkillsCliAgentDisplayNameById","agentId","getSkillsCliAgentSkillsDir","agentName","name","Object","keys"],"mappings":"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"}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import { ux } from '@oclif/core';
|
|
2
1
|
import { subdebug } from '@sanity/cli-core';
|
|
3
2
|
import { logSymbols } from '@sanity/cli-core/ux';
|
|
4
3
|
import { createMCPToken, MCP_SERVER_URL } from '../../services/mcp.js';
|
|
5
|
-
import {
|
|
6
|
-
import { SANITY_SKILL_NAMES } from '../skills/setupSkills.js';
|
|
4
|
+
import { getSkillCandidates } from '../skills/skillCandidates.js';
|
|
7
5
|
import { detectAvailableEditors } from './detectAvailableEditors.js';
|
|
8
|
-
import { EDITOR_CONFIGS, getSkillsCliAgent
|
|
6
|
+
import { EDITOR_CONFIGS, getSkillsCliAgent } from './editorConfigs.js';
|
|
9
7
|
import { promptForMCPSetup } from './promptForMCPSetup.js';
|
|
10
8
|
import { validateEditorTokens } from './validateEditorTokens.js';
|
|
11
9
|
import { writeMCPConfig } from './writeMCPConfig.js';
|
|
@@ -14,19 +12,21 @@ const NO_EDITORS_DETECTED_MESSAGE = `Couldn't auto-configure Sanity MCP server f
|
|
|
14
12
|
/**
|
|
15
13
|
* Classify each editor into one of four actions based on MCP status and
|
|
16
14
|
* whether the Sanity skills are already installed for its skills-CLI agent.
|
|
17
|
-
|
|
15
|
+
*
|
|
16
|
+
* `skillCandidateNames` is the set of editor names that still want a skills
|
|
17
|
+
* install (have a skills-CLI mapping AND aren't already installed), derived
|
|
18
|
+
* once via the shared `getSkillCandidates` primitive.
|
|
19
|
+
*/ function classifyEditors(editors, skillCandidateNames) {
|
|
18
20
|
return editors.map((editor)=>{
|
|
19
21
|
const needsMCP = !editor.configured || editor.authStatus !== 'valid';
|
|
20
|
-
const
|
|
21
|
-
const hasSkillMapping = Boolean(skillsCliAgent);
|
|
22
|
-
const skillInstalled = editor.skillInstalled === true;
|
|
22
|
+
const hasSkillMapping = Boolean(getSkillsCliAgent(editor.name));
|
|
23
23
|
if (needsMCP) {
|
|
24
24
|
return {
|
|
25
25
|
action: hasSkillMapping ? 'mcp-and-skill' : 'mcp-only',
|
|
26
26
|
editor
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
|
-
if (
|
|
29
|
+
if (skillCandidateNames.has(editor.name)) {
|
|
30
30
|
return {
|
|
31
31
|
action: 'skill-only',
|
|
32
32
|
editor
|
|
@@ -98,7 +98,7 @@ function getPromptMessage(mcpMode, skillsMode) {
|
|
|
98
98
|
* and the result includes `skillsToInstall` — agent IDs the caller should
|
|
99
99
|
* install via `setupSkills`. `setupMCP` itself never installs skills.
|
|
100
100
|
*/ export async function setupMCP(options) {
|
|
101
|
-
const { explicit = false, mode: mcpMode = 'prompt', skillsMode = 'skip' } = options
|
|
101
|
+
const { explicit = false, mode: mcpMode = 'prompt', output, skillsMode = 'skip' } = options;
|
|
102
102
|
// 1. Both opted out → nothing to do.
|
|
103
103
|
if (mcpMode === 'skip' && skillsMode === 'skip') {
|
|
104
104
|
mcpDebug('Skipping setup (mcpMode: skip, skillsMode: skip)');
|
|
@@ -116,7 +116,7 @@ function getPromptMessage(mcpMode, skillsMode) {
|
|
|
116
116
|
mcpDebug('Detected %d editors: %s', detectedEditors.length, detectedEditors);
|
|
117
117
|
if (editors.length === 0) {
|
|
118
118
|
if (explicit) {
|
|
119
|
-
|
|
119
|
+
output.warn(NO_EDITORS_DETECTED_MESSAGE);
|
|
120
120
|
}
|
|
121
121
|
return {
|
|
122
122
|
alreadyConfiguredEditors: [],
|
|
@@ -129,17 +129,16 @@ function getPromptMessage(mcpMode, skillsMode) {
|
|
|
129
129
|
// 3. Validate existing tokens against the Sanity API
|
|
130
130
|
await validateEditorTokens(editors);
|
|
131
131
|
// 4. Read skill state when skills are in scope so classification can dedup
|
|
132
|
+
const skillCandidateNames = new Set();
|
|
132
133
|
if (skillsMode !== 'skip') {
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const displayName = getSkillsCliAgentDisplayName(editor.name);
|
|
138
|
-
editor.skillInstalled = displayName ? installedAgentDisplayNames.has(displayName) : false;
|
|
134
|
+
const candidates = await getSkillCandidates(editors);
|
|
135
|
+
for (const candidate of candidates){
|
|
136
|
+
const { editor } = candidate;
|
|
137
|
+
skillCandidateNames.add(editor.name);
|
|
139
138
|
}
|
|
140
139
|
}
|
|
141
140
|
// 5. Classify + mask
|
|
142
|
-
const classified = classifyEditors(editors);
|
|
141
|
+
const classified = classifyEditors(editors, skillCandidateNames);
|
|
143
142
|
const actionable = applyMasking(classified, mcpMode, skillsMode);
|
|
144
143
|
// "Already configured" surfaces editors whose MCP setup is valid (skill
|
|
145
144
|
// state doesn't matter for this signal — that's what skillsToInstall is for).
|
|
@@ -148,7 +147,7 @@ function getPromptMessage(mcpMode, skillsMode) {
|
|
|
148
147
|
if (actionable.length === 0) {
|
|
149
148
|
mcpDebug('Nothing actionable after classification + masking');
|
|
150
149
|
if (explicit) {
|
|
151
|
-
|
|
150
|
+
output.log(`${logSymbols.success} All detected editors are already configured`);
|
|
152
151
|
}
|
|
153
152
|
return {
|
|
154
153
|
alreadyConfiguredEditors,
|
|
@@ -168,7 +167,7 @@ function getPromptMessage(mcpMode, skillsMode) {
|
|
|
168
167
|
message: getPromptMessage(mcpMode, skillsMode)
|
|
169
168
|
});
|
|
170
169
|
if (!selected || selected.length === 0) {
|
|
171
|
-
|
|
170
|
+
output.log('MCP configuration skipped');
|
|
172
171
|
return {
|
|
173
172
|
alreadyConfiguredEditors,
|
|
174
173
|
configuredEditors: [],
|
|
@@ -195,8 +194,8 @@ function getPromptMessage(mcpMode, skillsMode) {
|
|
|
195
194
|
} catch (error) {
|
|
196
195
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
197
196
|
mcpDebug('Error creating MCP token', error);
|
|
198
|
-
|
|
199
|
-
|
|
197
|
+
output.warn(`Could not configure MCP: ${err.message}`);
|
|
198
|
+
output.warn('You can set up MCP manually later using https://mcp.sanity.io');
|
|
200
199
|
mcpError = err;
|
|
201
200
|
}
|
|
202
201
|
}
|
|
@@ -208,14 +207,14 @@ function getPromptMessage(mcpMode, skillsMode) {
|
|
|
208
207
|
} catch (error) {
|
|
209
208
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
210
209
|
mcpDebug('Error writing MCP config for %s: %O', choice.editor.name, error);
|
|
211
|
-
|
|
212
|
-
|
|
210
|
+
output.warn(`Could not configure MCP for ${choice.editor.name}: ${err.message}`);
|
|
211
|
+
output.warn('You can set up MCP manually later using https://mcp.sanity.io');
|
|
213
212
|
mcpError = err;
|
|
214
213
|
}
|
|
215
214
|
}
|
|
216
215
|
}
|
|
217
216
|
if (configuredEditors.length > 0) {
|
|
218
|
-
|
|
217
|
+
output.log(`${logSymbols.success} MCP configured for ${configuredEditors.join(', ')}`);
|
|
219
218
|
}
|
|
220
219
|
}
|
|
221
220
|
// 8. Build skillsToInstall — only for choices the user kept, only when the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/mcp/setupMCP.ts"],"sourcesContent":["import {ux} from '@oclif/core'\nimport {subdebug} from '@sanity/cli-core'\nimport {logSymbols} from '@sanity/cli-core/ux'\n\nimport {createMCPToken, MCP_SERVER_URL} from '../../services/mcp.js'\nimport {readSkillState} from '../skills/readSkillState.js'\nimport {SANITY_SKILL_NAMES} from '../skills/setupSkills.js'\nimport {detectAvailableEditors} from './detectAvailableEditors.js'\nimport {\n EDITOR_CONFIGS,\n type EditorName,\n getSkillsCliAgent,\n getSkillsCliAgentDisplayName,\n} from './editorConfigs.js'\nimport {type EditorAction, type EditorChoice, promptForMCPSetup} from './promptForMCPSetup.js'\nimport {type Editor} from './types.js'\nimport {validateEditorTokens} from './validateEditorTokens.js'\nimport {writeMCPConfig} from './writeMCPConfig.js'\n\nconst mcpDebug = subdebug('mcp:setup')\n\nconst NO_EDITORS_DETECTED_MESSAGE = `Couldn't auto-configure Sanity MCP server for your editor. Visit ${MCP_SERVER_URL} for setup instructions.`\n\ntype Mode = 'auto' | 'prompt' | 'skip'\n\ninterface MCPSetupOptions {\n /**\n * Pre-detected editors. When omitted, `detectAvailableEditors()` is called.\n * Accepting this from the caller avoids re-running detection (which probes\n * the filesystem and shells out to CLI binaries) when the result is already\n * available — e.g. when `sanity init` runs both MCP and skills setup.\n */\n editors?: Editor[]\n\n /**\n * Whether the user explicitly requested MCP configuration (e.g. `sanity mcp configure`).\n * When true, shows status messages even when there's nothing to do.\n * When false/undefined (e.g. called from `sanity init`), stays quiet.\n */\n explicit?: boolean\n\n /**\n * Controls how MCP setup behaves:\n * - 'prompt': Ask the user which editors to configure (default)\n * - 'auto': Auto-configure all detected editors without prompting\n * - 'skip': Skip MCP configuration entirely\n */\n mode?: Mode\n\n /**\n * Controls whether skills install is also offered in the same prompt:\n * - 'prompt': Combine MCP + skills offers in one checkbox\n * - 'auto': Combine, but skip the prompt and select everything\n * - 'skip' (default): Skip skills entirely — today's MCP-only behavior\n */\n skillsMode?: Mode\n}\n\ninterface MCPSetupResult {\n /** Editors that were already configured with valid credentials (nothing to do) */\n alreadyConfiguredEditors: EditorName[]\n configuredEditors: EditorName[]\n detectedEditors: EditorName[]\n /** Skills-CLI agent IDs that the caller should install. Deduplicated. */\n skillsToInstall: string[]\n skipped: boolean\n\n error?: Error\n}\n\ninterface ClassifiedEditor {\n action: 'none' | EditorAction\n editor: Editor\n}\n\n/**\n * Classify each editor into one of four actions based on MCP status and\n * whether the Sanity skills are already installed for its skills-CLI agent.\n */\nfunction classifyEditors(editors: Editor[]): ClassifiedEditor[] {\n return editors.map((editor) => {\n const needsMCP = !editor.configured || editor.authStatus !== 'valid'\n const skillsCliAgent = getSkillsCliAgent(editor.name)\n const hasSkillMapping = Boolean(skillsCliAgent)\n const skillInstalled = editor.skillInstalled === true\n\n if (needsMCP) {\n return {action: hasSkillMapping ? 'mcp-and-skill' : 'mcp-only', editor}\n }\n if (hasSkillMapping && !skillInstalled) {\n return {action: 'skill-only', editor}\n }\n return {action: 'none', editor}\n })\n}\n\n/**\n * Apply masking based on the configured modes. `skip` modes mute the\n * corresponding action so we never prompt for or run work the user opted out\n * of via `--no-mcp` / `--no-skills`.\n */\nfunction applyMasking(\n classified: ClassifiedEditor[],\n mcpMode: Mode,\n skillsMode: Mode,\n): EditorChoice[] {\n const actionable: EditorChoice[] = []\n\n for (const {action, editor} of classified) {\n if (action === 'none') continue\n\n if (mcpMode === 'skip' && skillsMode === 'skip') continue\n\n if (mcpMode === 'skip') {\n // No MCP writes — keep only skill-only / mcp-and-skill (downgraded to skill-only)\n if (action === 'mcp-only') continue\n if (action === 'mcp-and-skill') {\n actionable.push({action: 'skill-only', editor})\n continue\n }\n actionable.push({action, editor})\n continue\n }\n\n if (skillsMode === 'skip') {\n // No skill install — drop skill-only, downgrade mcp-and-skill → mcp-only\n if (action === 'skill-only') continue\n if (action === 'mcp-and-skill') {\n actionable.push({action: 'mcp-only', editor})\n continue\n }\n actionable.push({action, editor})\n continue\n }\n\n actionable.push({action, editor})\n }\n\n return actionable\n}\n\nfunction getPromptMessage(mcpMode: Mode, skillsMode: Mode): string {\n if (mcpMode === 'skip') return 'Install Sanity agent skills for these editors?'\n if (skillsMode === 'skip') return 'Configure Sanity MCP server?'\n return 'Configure Sanity MCP and agent skills for these editors?'\n}\n\n/**\n * Main MCP setup orchestration.\n *\n * When `skillsMode !== 'skip'`, the prompt combines MCP and skills offers,\n * and the result includes `skillsToInstall` — agent IDs the caller should\n * install via `setupSkills`. `setupMCP` itself never installs skills.\n */\nexport async function setupMCP(options?: MCPSetupOptions): Promise<MCPSetupResult> {\n const {explicit = false, mode: mcpMode = 'prompt', skillsMode = 'skip'} = options ?? {}\n\n // 1. Both opted out → nothing to do.\n if (mcpMode === 'skip' && skillsMode === 'skip') {\n mcpDebug('Skipping setup (mcpMode: skip, skillsMode: skip)')\n return {\n alreadyConfiguredEditors: [],\n configuredEditors: [],\n detectedEditors: [],\n skillsToInstall: [],\n skipped: true,\n }\n }\n\n // 2. Detect available editors (filters out unparseable configs)\n const editors = options?.editors ?? (await detectAvailableEditors())\n const detectedEditors = editors.map((e) => e.name)\n\n mcpDebug('Detected %d editors: %s', detectedEditors.length, detectedEditors)\n\n if (editors.length === 0) {\n if (explicit) {\n ux.warn(NO_EDITORS_DETECTED_MESSAGE)\n }\n return {\n alreadyConfiguredEditors: [],\n configuredEditors: [],\n detectedEditors,\n skillsToInstall: [],\n skipped: true,\n }\n }\n\n // 3. Validate existing tokens against the Sanity API\n await validateEditorTokens(editors)\n\n // 4. Read skill state when skills are in scope so classification can dedup\n if (skillsMode !== 'skip') {\n const {installedAgentDisplayNames} = await readSkillState({skillNames: SANITY_SKILL_NAMES})\n for (const editor of editors) {\n const displayName = getSkillsCliAgentDisplayName(editor.name)\n editor.skillInstalled = displayName ? installedAgentDisplayNames.has(displayName) : false\n }\n }\n\n // 5. Classify + mask\n const classified = classifyEditors(editors)\n const actionable = applyMasking(classified, mcpMode, skillsMode)\n\n // \"Already configured\" surfaces editors whose MCP setup is valid (skill\n // state doesn't matter for this signal — that's what skillsToInstall is for).\n const actionableNames = new Set(actionable.map((c) => c.editor.name))\n const alreadyConfiguredEditors = editors\n .filter((e) => e.configured && e.authStatus === 'valid' && !actionableNames.has(e.name))\n .map((e) => e.name)\n\n if (actionable.length === 0) {\n mcpDebug('Nothing actionable after classification + masking')\n if (explicit) {\n ux.stdout(`${logSymbols.success} All detected editors are already configured`)\n }\n return {\n alreadyConfiguredEditors,\n configuredEditors: [],\n detectedEditors,\n skillsToInstall: [],\n skipped: true,\n }\n }\n\n // 6. Select editors to configure — prompt interactively or auto-select all.\n // We only auto when neither side wants a prompt: MCP auto, or (MCP skip\n // + skills auto). Anything that asks `mode: 'prompt'` for MCP wins the\n // prompt even when skills would have auto-installed.\n const shouldAuto = mcpMode === 'auto' || (mcpMode === 'skip' && skillsMode === 'auto')\n const selected = shouldAuto\n ? actionable\n : await promptForMCPSetup({\n choices: actionable,\n message: getPromptMessage(mcpMode, skillsMode),\n })\n\n if (!selected || selected.length === 0) {\n ux.stdout('MCP configuration skipped')\n return {\n alreadyConfiguredEditors,\n configuredEditors: [],\n detectedEditors,\n skillsToInstall: [],\n skipped: true,\n }\n }\n\n // 7. MCP write phase — only for choices that need MCP\n const mcpSelected = selected.filter(\n (c) => c.action === 'mcp-only' || c.action === 'mcp-and-skill',\n )\n\n let token: string | undefined\n const configuredEditors: EditorName[] = []\n let mcpError: Error | undefined\n\n if (mcpSelected.length > 0) {\n const validEditor = editors.find((e) => e.authStatus === 'valid' && e.existingToken)\n if (validEditor?.existingToken) {\n mcpDebug('Reusing valid token from %s', validEditor.name)\n token = validEditor.existingToken\n }\n\n const allOAuth = mcpSelected.every((c) => EDITOR_CONFIGS[c.editor.name].oauthOnly)\n\n if (!token && !allOAuth) {\n try {\n token = await createMCPToken()\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n mcpDebug('Error creating MCP token', error)\n ux.warn(`Could not configure MCP: ${err.message}`)\n ux.warn('You can set up MCP manually later using https://mcp.sanity.io')\n mcpError = err\n }\n }\n\n if (!mcpError) {\n for (const choice of mcpSelected) {\n try {\n await writeMCPConfig(choice.editor, token)\n configuredEditors.push(choice.editor.name)\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n mcpDebug('Error writing MCP config for %s: %O', choice.editor.name, error)\n ux.warn(`Could not configure MCP for ${choice.editor.name}: ${err.message}`)\n ux.warn('You can set up MCP manually later using https://mcp.sanity.io')\n mcpError = err\n }\n }\n }\n\n if (configuredEditors.length > 0) {\n ux.stdout(`${logSymbols.success} MCP configured for ${configuredEditors.join(', ')}`)\n }\n }\n\n // 8. Build skillsToInstall — only for choices the user kept, only when the\n // associated MCP write succeeded (or wasn't needed).\n const skillsToInstall: string[] = []\n if (skillsMode !== 'skip') {\n for (const choice of selected) {\n if (choice.action === 'skill-only') {\n const agent = getSkillsCliAgent(choice.editor.name)\n if (agent) skillsToInstall.push(agent)\n continue\n }\n if (choice.action === 'mcp-and-skill' && configuredEditors.includes(choice.editor.name)) {\n const agent = getSkillsCliAgent(choice.editor.name)\n if (agent) skillsToInstall.push(agent)\n }\n }\n }\n\n return {\n alreadyConfiguredEditors,\n configuredEditors,\n detectedEditors,\n error: mcpError,\n skillsToInstall: [...new Set(skillsToInstall)],\n skipped: false,\n }\n}\n"],"names":["ux","subdebug","logSymbols","createMCPToken","MCP_SERVER_URL","readSkillState","SANITY_SKILL_NAMES","detectAvailableEditors","EDITOR_CONFIGS","getSkillsCliAgent","getSkillsCliAgentDisplayName","promptForMCPSetup","validateEditorTokens","writeMCPConfig","mcpDebug","NO_EDITORS_DETECTED_MESSAGE","classifyEditors","editors","map","editor","needsMCP","configured","authStatus","skillsCliAgent","name","hasSkillMapping","Boolean","skillInstalled","action","applyMasking","classified","mcpMode","skillsMode","actionable","push","getPromptMessage","setupMCP","options","explicit","mode","alreadyConfiguredEditors","configuredEditors","detectedEditors","skillsToInstall","skipped","e","length","warn","installedAgentDisplayNames","skillNames","displayName","has","actionableNames","Set","c","filter","stdout","success","shouldAuto","selected","choices","message","mcpSelected","token","mcpError","validEditor","find","existingToken","allOAuth","every","oauthOnly","error","err","Error","String","choice","join","agent","includes"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/mcp/setupMCP.ts"],"sourcesContent":["import {type Output, subdebug} from '@sanity/cli-core'\nimport {logSymbols} from '@sanity/cli-core/ux'\n\nimport {createMCPToken, MCP_SERVER_URL} from '../../services/mcp.js'\nimport {getSkillCandidates} from '../skills/skillCandidates.js'\nimport {detectAvailableEditors} from './detectAvailableEditors.js'\nimport {EDITOR_CONFIGS, type EditorName, getSkillsCliAgent} 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 * Output to use for user-facing messages, so they go through the calling\n * command rather than directly to stdout/stderr.\n */\n output: Output\n\n /**\n * Pre-detected editors. When omitted, `detectAvailableEditors()` is called.\n * Accepting this from the caller avoids re-running detection (which probes\n * the filesystem and shells out to CLI binaries) when the result is already\n * available — e.g. when `sanity init` runs both MCP and skills setup.\n */\n editors?: Editor[]\n\n /**\n * Whether the user explicitly requested MCP configuration (e.g. `sanity mcp configure`).\n * When true, shows status messages even when there's nothing to do.\n * When false/undefined (e.g. called from `sanity init`), stays quiet.\n */\n explicit?: boolean\n\n /**\n * Controls how MCP setup behaves:\n * - 'prompt': Ask the user which editors to configure (default)\n * - 'auto': Auto-configure all detected editors without prompting\n * - 'skip': Skip MCP configuration entirely\n */\n mode?: Mode\n\n /**\n * Controls whether skills install is also offered in the same prompt:\n * - 'prompt': Combine MCP + skills offers in one checkbox\n * - 'auto': Combine, but skip the prompt and select everything\n * - 'skip' (default): Skip skills entirely — today's MCP-only behavior\n */\n skillsMode?: Mode\n}\n\ninterface MCPSetupResult {\n /** Editors that were already configured with valid credentials (nothing to do) */\n alreadyConfiguredEditors: EditorName[]\n configuredEditors: EditorName[]\n detectedEditors: EditorName[]\n /** Skills-CLI agent IDs that the caller should install. Deduplicated. */\n skillsToInstall: string[]\n skipped: boolean\n\n error?: Error\n}\n\ninterface ClassifiedEditor {\n action: 'none' | EditorAction\n editor: Editor\n}\n\n/**\n * Classify each editor into one of four actions based on MCP status and\n * whether the Sanity skills are already installed for its skills-CLI agent.\n *\n * `skillCandidateNames` is the set of editor names that still want a skills\n * install (have a skills-CLI mapping AND aren't already installed), derived\n * once via the shared `getSkillCandidates` primitive.\n */\nfunction classifyEditors(\n editors: Editor[],\n skillCandidateNames: Set<EditorName>,\n): ClassifiedEditor[] {\n return editors.map((editor) => {\n const needsMCP = !editor.configured || editor.authStatus !== 'valid'\n const hasSkillMapping = Boolean(getSkillsCliAgent(editor.name))\n\n if (needsMCP) {\n return {action: hasSkillMapping ? 'mcp-and-skill' : 'mcp-only', editor}\n }\n if (skillCandidateNames.has(editor.name)) {\n return {action: 'skill-only', editor}\n }\n return {action: 'none', editor}\n })\n}\n\n/**\n * Apply masking based on the configured modes. `skip` modes mute the\n * corresponding action so we never prompt for or run work the user opted out\n * of via `--no-mcp` / `--no-skills`.\n */\nfunction applyMasking(\n classified: ClassifiedEditor[],\n mcpMode: Mode,\n skillsMode: Mode,\n): EditorChoice[] {\n const actionable: EditorChoice[] = []\n\n for (const {action, editor} of classified) {\n if (action === 'none') continue\n\n if (mcpMode === 'skip' && skillsMode === 'skip') continue\n\n if (mcpMode === 'skip') {\n // No MCP writes — keep only skill-only / mcp-and-skill (downgraded to skill-only)\n if (action === 'mcp-only') continue\n if (action === 'mcp-and-skill') {\n actionable.push({action: 'skill-only', editor})\n continue\n }\n actionable.push({action, editor})\n continue\n }\n\n if (skillsMode === 'skip') {\n // No skill install — drop skill-only, downgrade mcp-and-skill → mcp-only\n if (action === 'skill-only') continue\n if (action === 'mcp-and-skill') {\n actionable.push({action: 'mcp-only', editor})\n continue\n }\n actionable.push({action, editor})\n continue\n }\n\n actionable.push({action, editor})\n }\n\n return actionable\n}\n\nfunction getPromptMessage(mcpMode: Mode, skillsMode: Mode): string {\n if (mcpMode === 'skip') return 'Install Sanity agent skills for these editors?'\n if (skillsMode === 'skip') return 'Configure Sanity MCP server?'\n return 'Configure Sanity MCP and agent skills for these editors?'\n}\n\n/**\n * Main MCP setup orchestration.\n *\n * When `skillsMode !== 'skip'`, the prompt combines MCP and skills offers,\n * and the result includes `skillsToInstall` — agent IDs the caller should\n * install via `setupSkills`. `setupMCP` itself never installs skills.\n */\nexport async function setupMCP(options: MCPSetupOptions): Promise<MCPSetupResult> {\n const {explicit = false, mode: mcpMode = 'prompt', output, 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 output.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 const skillCandidateNames = new Set<EditorName>()\n if (skillsMode !== 'skip') {\n const candidates = await getSkillCandidates(editors)\n for (const candidate of candidates) {\n const {editor} = candidate\n skillCandidateNames.add(editor.name)\n }\n }\n\n // 5. Classify + mask\n const classified = classifyEditors(editors, skillCandidateNames)\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 output.log(`${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 output.log('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 output.warn(`Could not configure MCP: ${err.message}`)\n output.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 output.warn(`Could not configure MCP for ${choice.editor.name}: ${err.message}`)\n output.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 output.log(`${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":["subdebug","logSymbols","createMCPToken","MCP_SERVER_URL","getSkillCandidates","detectAvailableEditors","EDITOR_CONFIGS","getSkillsCliAgent","promptForMCPSetup","validateEditorTokens","writeMCPConfig","mcpDebug","NO_EDITORS_DETECTED_MESSAGE","classifyEditors","editors","skillCandidateNames","map","editor","needsMCP","configured","authStatus","hasSkillMapping","Boolean","name","action","has","applyMasking","classified","mcpMode","skillsMode","actionable","push","getPromptMessage","setupMCP","options","explicit","mode","output","alreadyConfiguredEditors","configuredEditors","detectedEditors","skillsToInstall","skipped","e","length","warn","Set","candidates","candidate","add","actionableNames","c","filter","log","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
|
|
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,WAaC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { subdebug } from '@sanity/cli-core';
|
|
2
|
+
import { detectAvailableEditors } from '../mcp/detectAvailableEditors.js';
|
|
3
|
+
import { getSkillsCliAgent } from '../mcp/editorConfigs.js';
|
|
4
|
+
import { setupSkills } from './setupSkills.js';
|
|
5
|
+
const debug = subdebug('skills:configure');
|
|
6
|
+
const NO_EDITORS_MESSAGE = 'No supported editors detected for Sanity agent skills. See https://www.sanity.io/docs/ai/skills for manual setup.';
|
|
7
|
+
/**
|
|
8
|
+
* Standalone, MCP-free orchestration for installing Sanity agent skills for
|
|
9
|
+
* the user's detected AI editors. Forwards every detected agent with a
|
|
10
|
+
* skills-CLI mapping to `setupSkills` without prompting.
|
|
11
|
+
*
|
|
12
|
+
* Re-running is intentional: `skills add ... -g -y` reinstalls (overwrites) the
|
|
13
|
+
* skill files in place, so a subsequent `sanity skills install` updates
|
|
14
|
+
* already-installed skills to the latest version. Failures are surfaced as
|
|
15
|
+
* warnings and never thrown.
|
|
16
|
+
*/ export async function configureSkills(options) {
|
|
17
|
+
const { output } = options;
|
|
18
|
+
const editors = options.editors ?? await detectAvailableEditors();
|
|
19
|
+
const detectedEditors = editors.map((e)=>e.name);
|
|
20
|
+
debug('Detected %d editors: %s', detectedEditors.length, detectedEditors);
|
|
21
|
+
const agents = [
|
|
22
|
+
...new Set(editors.flatMap((editor)=>{
|
|
23
|
+
const agent = getSkillsCliAgent(editor.name);
|
|
24
|
+
return agent ? [
|
|
25
|
+
agent
|
|
26
|
+
] : [];
|
|
27
|
+
}))
|
|
28
|
+
];
|
|
29
|
+
if (agents.length === 0) {
|
|
30
|
+
output.warn(NO_EDITORS_MESSAGE);
|
|
31
|
+
return {
|
|
32
|
+
detectedEditors,
|
|
33
|
+
installedAgents: [],
|
|
34
|
+
skipped: true
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const result = await setupSkills({
|
|
38
|
+
agents,
|
|
39
|
+
output
|
|
40
|
+
});
|
|
41
|
+
return {
|
|
42
|
+
detectedEditors,
|
|
43
|
+
error: result.error,
|
|
44
|
+
installedAgents: result.installedAgents,
|
|
45
|
+
skipped: result.skipped
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
//# sourceMappingURL=configureSkills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/skills/configureSkills.ts"],"sourcesContent":["import {type Output, subdebug} from '@sanity/cli-core'\n\nimport {detectAvailableEditors} from '../mcp/detectAvailableEditors.js'\nimport {type EditorName, getSkillsCliAgent} from '../mcp/editorConfigs.js'\nimport {type Editor} from '../mcp/types.js'\nimport {setupSkills} from './setupSkills.js'\n\nconst debug = subdebug('skills:configure')\n\nconst NO_EDITORS_MESSAGE =\n 'No supported editors detected for Sanity agent skills. See https://www.sanity.io/docs/ai/skills for manual setup.'\n\ninterface ConfigureSkillsOptions {\n /**\n * Output to use for user-facing messages, so they go through the calling\n * command rather than directly to stdout/stderr.\n */\n output: Output\n\n /**\n * Pre-detected editors. When omitted, `detectAvailableEditors()` is called.\n * Accepting this avoids re-running detection when the caller already has it.\n */\n editors?: Editor[]\n}\n\ninterface ConfigureSkillsResult {\n detectedEditors: EditorName[]\n /** Deduplicated skills-CLI agent IDs that were installed. */\n installedAgents: string[]\n skipped: boolean\n\n error?: Error\n}\n\n/**\n * Standalone, MCP-free orchestration for installing Sanity agent skills for\n * the user's detected AI editors. Forwards every detected agent with a\n * skills-CLI mapping to `setupSkills` without prompting.\n *\n * Re-running is intentional: `skills add ... -g -y` reinstalls (overwrites) the\n * skill files in place, so a subsequent `sanity skills install` updates\n * already-installed skills to the latest version. Failures are surfaced as\n * warnings and never thrown.\n */\nexport async function configureSkills(\n options: ConfigureSkillsOptions,\n): Promise<ConfigureSkillsResult> {\n const {output} = options\n const editors = options.editors ?? (await detectAvailableEditors())\n const detectedEditors = editors.map((e) => e.name)\n debug('Detected %d editors: %s', detectedEditors.length, detectedEditors)\n\n const agents = [\n ...new Set(\n editors.flatMap((editor) => {\n const agent = getSkillsCliAgent(editor.name)\n return agent ? [agent] : []\n }),\n ),\n ]\n\n if (agents.length === 0) {\n output.warn(NO_EDITORS_MESSAGE)\n return {detectedEditors, installedAgents: [], skipped: true}\n }\n\n const result = await setupSkills({agents, output})\n\n return {\n detectedEditors,\n error: result.error,\n installedAgents: result.installedAgents,\n skipped: result.skipped,\n }\n}\n"],"names":["subdebug","detectAvailableEditors","getSkillsCliAgent","setupSkills","debug","NO_EDITORS_MESSAGE","configureSkills","options","output","editors","detectedEditors","map","e","name","length","agents","Set","flatMap","editor","agent","warn","installedAgents","skipped","result","error"],"mappings":"AAAA,SAAqBA,QAAQ,QAAO,mBAAkB;AAEtD,SAAQC,sBAAsB,QAAO,mCAAkC;AACvE,SAAyBC,iBAAiB,QAAO,0BAAyB;AAE1E,SAAQC,WAAW,QAAO,mBAAkB;AAE5C,MAAMC,QAAQJ,SAAS;AAEvB,MAAMK,qBACJ;AAyBF;;;;;;;;;CASC,GACD,OAAO,eAAeC,gBACpBC,OAA+B;IAE/B,MAAM,EAACC,MAAM,EAAC,GAAGD;IACjB,MAAME,UAAUF,QAAQE,OAAO,IAAK,MAAMR;IAC1C,MAAMS,kBAAkBD,QAAQE,GAAG,CAAC,CAACC,IAAMA,EAAEC,IAAI;IACjDT,MAAM,2BAA2BM,gBAAgBI,MAAM,EAAEJ;IAEzD,MAAMK,SAAS;WACV,IAAIC,IACLP,QAAQQ,OAAO,CAAC,CAACC;YACf,MAAMC,QAAQjB,kBAAkBgB,OAAOL,IAAI;YAC3C,OAAOM,QAAQ;gBAACA;aAAM,GAAG,EAAE;QAC7B;KAEH;IAED,IAAIJ,OAAOD,MAAM,KAAK,GAAG;QACvBN,OAAOY,IAAI,CAACf;QACZ,OAAO;YAACK;YAAiBW,iBAAiB,EAAE;YAAEC,SAAS;QAAI;IAC7D;IAEA,MAAMC,SAAS,MAAMpB,YAAY;QAACY;QAAQP;IAAM;IAEhD,OAAO;QACLE;QACAc,OAAOD,OAAOC,KAAK;QACnBH,iBAAiBE,OAAOF,eAAe;QACvCC,SAASC,OAAOD,OAAO;IACzB;AACF"}
|