@sanity/cli 6.0.0 → 6.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +78 -111
  2. package/dist/actions/manifest/extractWorkspaceManifest.js +12 -5
  3. package/dist/actions/manifest/extractWorkspaceManifest.js.map +1 -1
  4. package/dist/actions/mcp/detectAvailableEditors.js +19 -10
  5. package/dist/actions/mcp/detectAvailableEditors.js.map +1 -1
  6. package/dist/actions/mcp/editorConfigs.js +222 -158
  7. package/dist/actions/mcp/editorConfigs.js.map +1 -1
  8. package/dist/actions/mcp/promptForMCPSetup.js +16 -7
  9. package/dist/actions/mcp/promptForMCPSetup.js.map +1 -1
  10. package/dist/actions/mcp/setupMCP.js +62 -23
  11. package/dist/actions/mcp/setupMCP.js.map +1 -1
  12. package/dist/actions/mcp/types.js.map +1 -1
  13. package/dist/actions/mcp/validateEditorTokens.js +56 -0
  14. package/dist/actions/mcp/validateEditorTokens.js.map +1 -0
  15. package/dist/actions/schema/uploadSchemaToLexicon.js +6 -4
  16. package/dist/actions/schema/uploadSchemaToLexicon.js.map +1 -1
  17. package/dist/actions/telemetry/telemetryDebug.js +2 -2
  18. package/dist/actions/telemetry/telemetryDebug.js.map +1 -1
  19. package/dist/commands/init.js +9 -4
  20. package/dist/commands/init.js.map +1 -1
  21. package/dist/commands/mcp/configure.js +3 -1
  22. package/dist/commands/mcp/configure.js.map +1 -1
  23. package/dist/commands/preview.js +3 -4
  24. package/dist/commands/preview.js.map +1 -1
  25. package/dist/hooks/prerun/setupTelemetry.js +7 -7
  26. package/dist/hooks/prerun/setupTelemetry.js.map +1 -1
  27. package/dist/services/mcp.js +55 -1
  28. package/dist/services/mcp.js.map +1 -1
  29. package/dist/util/getLocalPackageVersion.js +4 -2
  30. package/dist/util/getLocalPackageVersion.js.map +1 -1
  31. package/dist/util/getProjectDefaults.js +22 -28
  32. package/dist/util/getProjectDefaults.js.map +1 -1
  33. package/dist/util/gitConfig.js +45 -0
  34. package/dist/util/gitConfig.js.map +1 -0
  35. package/dist/util/telemetry/telemetryStoreDebug.js +2 -2
  36. package/dist/util/telemetry/telemetryStoreDebug.js.map +1 -1
  37. package/oclif.manifest.json +83 -83
  38. package/package.json +4 -6
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/hooks/prerun/setupTelemetry.ts"],"sourcesContent":["import {spawn} from 'node:child_process'\nimport {fileURLToPath} from 'node:url'\n\nimport {type Hook} from '@oclif/core'\nimport {\n type CliConfig,\n debug,\n findProjectRoot,\n getCliConfig,\n setCliTelemetry,\n} from '@sanity/cli-core'\nimport {createSessionId} from '@sanity/telemetry'\n\nimport {resolveConsent} from '../../actions/telemetry/resolveConsent.js'\nimport {telemetryDebug} from '../../actions/telemetry/telemetryDebug.js'\nimport {telemetryDisclosure} from '../../actions/telemetry/telemetryDisclosure.js'\nimport {CliCommandTelemetry, type CLITraceData} from '../../telemetry/cli.telemetry.js'\nimport {detectRuntime} from '../../util/detectRuntime.js'\nimport {parseArguments} from '../../util/parseArguments.js'\nimport {createTelemetryStore} from '../../util/telemetry/createTelemetryStore.js'\n\nexport const setupTelemetry: Hook.Prerun = async function ({config}) {\n // Show telemetry disclosure\n telemetryDisclosure()\n\n const sessionId = createSessionId()\n\n const telemetry = createTelemetryStore(sessionId, {\n resolveConsent,\n })\n\n let cliConfig: CliConfig | undefined\n try {\n const projectRoot = await findProjectRoot(process.cwd())\n cliConfig = await getCliConfig(projectRoot.directory)\n } catch {\n // Accept not finding a project root and/or CLI config\n }\n\n telemetry.updateUserProperties({\n cliVersion: config.version,\n cpuArchitecture: process.arch,\n dataset: cliConfig?.api?.dataset,\n machinePlatform: process.platform,\n projectId: cliConfig?.api?.projectId,\n runtime: detectRuntime(),\n runtimeVersion: process.version,\n })\n\n const args = parseArguments()\n\n const traceOptions: CLITraceData = {\n commandArguments: args.argsWithoutOptions,\n coreOptions: {\n debug: args.coreOptions.debug ?? undefined,\n help: args.coreOptions.help ?? undefined,\n version: args.coreOptions.version ?? undefined,\n },\n extraArguments: args.extraArguments,\n groupOrCommand: args.groupOrCommand,\n }\n\n telemetryDebug('Starting command trace', traceOptions)\n\n const cliCommandTrace = telemetry.trace(CliCommandTelemetry, traceOptions)\n cliCommandTrace.start()\n\n // Set the global telemetry store with new context\n setCliTelemetry(cliCommandTrace.newContext(args.groupOrCommand))\n\n // Handle process exit - complete trace and spawn worker to flush all telemetry\n process.once('exit', (status) => {\n if (status === 0) {\n cliCommandTrace.complete()\n } else {\n // TODO: Properly handle errors\n // https://oclif.io/docs/error_handling/#error-handling-in-the-catch-method\n cliCommandTrace.error(new Error('Process exited with status ' + status))\n }\n\n const workerPath = fileURLToPath(new URL('flushTelemetry.worker.js', import.meta.url))\n telemetryDebug(`Spawning \"${process.execPath} ${workerPath}\"`)\n\n // Spawn detached worker to flush all telemetry files\n // unref will ensure the child process can keep doing work even after the parent process exits\n spawn(process.execPath, [workerPath], {\n detached: true,\n env: {\n ...process.env,\n SANITY_TELEMETRY_PROJECT_ID: cliConfig?.api?.projectId || '',\n },\n // If debug is enabled, spawn the worker with stdio inherit to see the output\n stdio: debug.enabled ? 'inherit' : 'ignore',\n }).unref()\n })\n}\n"],"names":["spawn","fileURLToPath","debug","findProjectRoot","getCliConfig","setCliTelemetry","createSessionId","resolveConsent","telemetryDebug","telemetryDisclosure","CliCommandTelemetry","detectRuntime","parseArguments","createTelemetryStore","setupTelemetry","config","sessionId","telemetry","cliConfig","projectRoot","process","cwd","directory","updateUserProperties","cliVersion","version","cpuArchitecture","arch","dataset","api","machinePlatform","platform","projectId","runtime","runtimeVersion","args","traceOptions","commandArguments","argsWithoutOptions","coreOptions","undefined","help","extraArguments","groupOrCommand","cliCommandTrace","trace","start","newContext","once","status","complete","error","Error","workerPath","URL","url","execPath","detached","env","SANITY_TELEMETRY_PROJECT_ID","stdio","enabled","unref"],"mappings":"AAAA,SAAQA,KAAK,QAAO,qBAAoB;AACxC,SAAQC,aAAa,QAAO,WAAU;AAGtC,SAEEC,KAAK,EACLC,eAAe,EACfC,YAAY,EACZC,eAAe,QACV,mBAAkB;AACzB,SAAQC,eAAe,QAAO,oBAAmB;AAEjD,SAAQC,cAAc,QAAO,4CAA2C;AACxE,SAAQC,cAAc,QAAO,4CAA2C;AACxE,SAAQC,mBAAmB,QAAO,iDAAgD;AAClF,SAAQC,mBAAmB,QAA0B,mCAAkC;AACvF,SAAQC,aAAa,QAAO,8BAA6B;AACzD,SAAQC,cAAc,QAAO,+BAA8B;AAC3D,SAAQC,oBAAoB,QAAO,+CAA8C;AAEjF,OAAO,MAAMC,iBAA8B,eAAgB,EAACC,MAAM,EAAC;IACjE,4BAA4B;IAC5BN;IAEA,MAAMO,YAAYV;IAElB,MAAMW,YAAYJ,qBAAqBG,WAAW;QAChDT;IACF;IAEA,IAAIW;IACJ,IAAI;QACF,MAAMC,cAAc,MAAMhB,gBAAgBiB,QAAQC,GAAG;QACrDH,YAAY,MAAMd,aAAae,YAAYG,SAAS;IACtD,EAAE,OAAM;IACN,sDAAsD;IACxD;IAEAL,UAAUM,oBAAoB,CAAC;QAC7BC,YAAYT,OAAOU,OAAO;QAC1BC,iBAAiBN,QAAQO,IAAI;QAC7BC,SAASV,WAAWW,KAAKD;QACzBE,iBAAiBV,QAAQW,QAAQ;QACjCC,WAAWd,WAAWW,KAAKG;QAC3BC,SAAStB;QACTuB,gBAAgBd,QAAQK,OAAO;IACjC;IAEA,MAAMU,OAAOvB;IAEb,MAAMwB,eAA6B;QACjCC,kBAAkBF,KAAKG,kBAAkB;QACzCC,aAAa;YACXrC,OAAOiC,KAAKI,WAAW,CAACrC,KAAK,IAAIsC;YACjCC,MAAMN,KAAKI,WAAW,CAACE,IAAI,IAAID;YAC/Bf,SAASU,KAAKI,WAAW,CAACd,OAAO,IAAIe;QACvC;QACAE,gBAAgBP,KAAKO,cAAc;QACnCC,gBAAgBR,KAAKQ,cAAc;IACrC;IAEAnC,eAAe,0BAA0B4B;IAEzC,MAAMQ,kBAAkB3B,UAAU4B,KAAK,CAACnC,qBAAqB0B;IAC7DQ,gBAAgBE,KAAK;IAErB,kDAAkD;IAClDzC,gBAAgBuC,gBAAgBG,UAAU,CAACZ,KAAKQ,cAAc;IAE9D,+EAA+E;IAC/EvB,QAAQ4B,IAAI,CAAC,QAAQ,CAACC;QACpB,IAAIA,WAAW,GAAG;YAChBL,gBAAgBM,QAAQ;QAC1B,OAAO;YACL,+BAA+B;YAC/B,2EAA2E;YAC3EN,gBAAgBO,KAAK,CAAC,IAAIC,MAAM,gCAAgCH;QAClE;QAEA,MAAMI,aAAapD,cAAc,IAAIqD,IAAI,4BAA4B,YAAYC,GAAG;QACpF/C,eAAe,CAAC,UAAU,EAAEY,QAAQoC,QAAQ,CAAC,CAAC,EAAEH,WAAW,CAAC,CAAC;QAE7D,qDAAqD;QACrD,8FAA8F;QAC9FrD,MAAMoB,QAAQoC,QAAQ,EAAE;YAACH;SAAW,EAAE;YACpCI,UAAU;YACVC,KAAK;gBACH,GAAGtC,QAAQsC,GAAG;gBACdC,6BAA6BzC,WAAWW,KAAKG,aAAa;YAC5D;YACA,6EAA6E;YAC7E4B,OAAO1D,MAAM2D,OAAO,GAAG,YAAY;QACrC,GAAGC,KAAK;IACV;AACF,EAAC"}
1
+ {"version":3,"sources":["../../../src/hooks/prerun/setupTelemetry.ts"],"sourcesContent":["import {spawn} from 'node:child_process'\nimport {fileURLToPath} from 'node:url'\n\nimport {type Hook} from '@oclif/core'\nimport {\n type CliConfig,\n debug,\n findProjectRoot,\n getCliConfig,\n setCliTelemetry,\n} from '@sanity/cli-core'\nimport {createSessionId} from '@sanity/telemetry'\n\nimport {resolveConsent} from '../../actions/telemetry/resolveConsent.js'\nimport {telemetryDebug} from '../../actions/telemetry/telemetryDebug.js'\nimport {telemetryDisclosure} from '../../actions/telemetry/telemetryDisclosure.js'\nimport {CliCommandTelemetry, type CLITraceData} from '../../telemetry/cli.telemetry.js'\nimport {detectRuntime} from '../../util/detectRuntime.js'\nimport {parseArguments} from '../../util/parseArguments.js'\nimport {createTelemetryStore} from '../../util/telemetry/createTelemetryStore.js'\n\nexport const setupTelemetry: Hook.Prerun = async function ({config}) {\n // Show telemetry disclosure\n telemetryDisclosure()\n\n const sessionId = createSessionId()\n\n const telemetry = createTelemetryStore(sessionId, {\n resolveConsent,\n })\n\n let cliConfig: CliConfig | undefined\n try {\n const projectRoot = await findProjectRoot(process.cwd())\n cliConfig = await getCliConfig(projectRoot.directory)\n } catch {\n // Accept not finding a project root and/or CLI config\n }\n\n telemetry.updateUserProperties({\n cliVersion: config.version,\n cpuArchitecture: process.arch,\n dataset: cliConfig?.api?.dataset,\n machinePlatform: process.platform,\n projectId: cliConfig?.api?.projectId,\n runtime: detectRuntime(),\n runtimeVersion: process.version,\n })\n\n const args = parseArguments()\n\n const traceOptions: CLITraceData = {\n commandArguments: args.argsWithoutOptions,\n coreOptions: {\n debug: args.coreOptions.debug ?? undefined,\n help: args.coreOptions.help ?? undefined,\n version: args.coreOptions.version ?? undefined,\n },\n extraArguments: args.extraArguments,\n groupOrCommand: args.groupOrCommand,\n }\n\n telemetryDebug('Starting command trace', traceOptions)\n\n const cliCommandTrace = telemetry.trace(CliCommandTelemetry, traceOptions)\n cliCommandTrace.start()\n\n // Set the global telemetry store and trace error reporter\n setCliTelemetry(cliCommandTrace.newContext(args.groupOrCommand), {\n reportTraceError: (error) => cliCommandTrace.error(error),\n })\n\n // Handle process exit - complete trace on success, spawn worker to flush telemetry.\n // On non-zero exit, the trace is either already errored (from SanityCommand.catch)\n // or it was a user abort (SIGINT/ctrl+c) — left incomplete, matching old CLI behavior.\n process.once('exit', (status) => {\n if (status === 0) {\n cliCommandTrace.complete()\n }\n\n const workerPath = fileURLToPath(new URL('flushTelemetry.worker.js', import.meta.url))\n telemetryDebug(`Spawning \"${process.execPath} ${workerPath}\"`)\n\n // Spawn detached worker to flush all telemetry files\n // unref will ensure the child process can keep doing work even after the parent process exits\n spawn(process.execPath, [workerPath], {\n detached: true,\n env: {\n ...process.env,\n SANITY_TELEMETRY_PROJECT_ID: cliConfig?.api?.projectId || '',\n },\n // If debug is enabled, spawn the worker with stdio inherit to see the output\n stdio: debug.enabled ? 'inherit' : 'ignore',\n }).unref()\n })\n}\n"],"names":["spawn","fileURLToPath","debug","findProjectRoot","getCliConfig","setCliTelemetry","createSessionId","resolveConsent","telemetryDebug","telemetryDisclosure","CliCommandTelemetry","detectRuntime","parseArguments","createTelemetryStore","setupTelemetry","config","sessionId","telemetry","cliConfig","projectRoot","process","cwd","directory","updateUserProperties","cliVersion","version","cpuArchitecture","arch","dataset","api","machinePlatform","platform","projectId","runtime","runtimeVersion","args","traceOptions","commandArguments","argsWithoutOptions","coreOptions","undefined","help","extraArguments","groupOrCommand","cliCommandTrace","trace","start","newContext","reportTraceError","error","once","status","complete","workerPath","URL","url","execPath","detached","env","SANITY_TELEMETRY_PROJECT_ID","stdio","enabled","unref"],"mappings":"AAAA,SAAQA,KAAK,QAAO,qBAAoB;AACxC,SAAQC,aAAa,QAAO,WAAU;AAGtC,SAEEC,KAAK,EACLC,eAAe,EACfC,YAAY,EACZC,eAAe,QACV,mBAAkB;AACzB,SAAQC,eAAe,QAAO,oBAAmB;AAEjD,SAAQC,cAAc,QAAO,4CAA2C;AACxE,SAAQC,cAAc,QAAO,4CAA2C;AACxE,SAAQC,mBAAmB,QAAO,iDAAgD;AAClF,SAAQC,mBAAmB,QAA0B,mCAAkC;AACvF,SAAQC,aAAa,QAAO,8BAA6B;AACzD,SAAQC,cAAc,QAAO,+BAA8B;AAC3D,SAAQC,oBAAoB,QAAO,+CAA8C;AAEjF,OAAO,MAAMC,iBAA8B,eAAgB,EAACC,MAAM,EAAC;IACjE,4BAA4B;IAC5BN;IAEA,MAAMO,YAAYV;IAElB,MAAMW,YAAYJ,qBAAqBG,WAAW;QAChDT;IACF;IAEA,IAAIW;IACJ,IAAI;QACF,MAAMC,cAAc,MAAMhB,gBAAgBiB,QAAQC,GAAG;QACrDH,YAAY,MAAMd,aAAae,YAAYG,SAAS;IACtD,EAAE,OAAM;IACN,sDAAsD;IACxD;IAEAL,UAAUM,oBAAoB,CAAC;QAC7BC,YAAYT,OAAOU,OAAO;QAC1BC,iBAAiBN,QAAQO,IAAI;QAC7BC,SAASV,WAAWW,KAAKD;QACzBE,iBAAiBV,QAAQW,QAAQ;QACjCC,WAAWd,WAAWW,KAAKG;QAC3BC,SAAStB;QACTuB,gBAAgBd,QAAQK,OAAO;IACjC;IAEA,MAAMU,OAAOvB;IAEb,MAAMwB,eAA6B;QACjCC,kBAAkBF,KAAKG,kBAAkB;QACzCC,aAAa;YACXrC,OAAOiC,KAAKI,WAAW,CAACrC,KAAK,IAAIsC;YACjCC,MAAMN,KAAKI,WAAW,CAACE,IAAI,IAAID;YAC/Bf,SAASU,KAAKI,WAAW,CAACd,OAAO,IAAIe;QACvC;QACAE,gBAAgBP,KAAKO,cAAc;QACnCC,gBAAgBR,KAAKQ,cAAc;IACrC;IAEAnC,eAAe,0BAA0B4B;IAEzC,MAAMQ,kBAAkB3B,UAAU4B,KAAK,CAACnC,qBAAqB0B;IAC7DQ,gBAAgBE,KAAK;IAErB,0DAA0D;IAC1DzC,gBAAgBuC,gBAAgBG,UAAU,CAACZ,KAAKQ,cAAc,GAAG;QAC/DK,kBAAkB,CAACC,QAAUL,gBAAgBK,KAAK,CAACA;IACrD;IAEA,oFAAoF;IACpF,mFAAmF;IACnF,uFAAuF;IACvF7B,QAAQ8B,IAAI,CAAC,QAAQ,CAACC;QACpB,IAAIA,WAAW,GAAG;YAChBP,gBAAgBQ,QAAQ;QAC1B;QAEA,MAAMC,aAAapD,cAAc,IAAIqD,IAAI,4BAA4B,YAAYC,GAAG;QACpF/C,eAAe,CAAC,UAAU,EAAEY,QAAQoC,QAAQ,CAAC,CAAC,EAAEH,WAAW,CAAC,CAAC;QAE7D,qDAAqD;QACrD,8FAA8F;QAC9FrD,MAAMoB,QAAQoC,QAAQ,EAAE;YAACH;SAAW,EAAE;YACpCI,UAAU;YACVC,KAAK;gBACH,GAAGtC,QAAQsC,GAAG;gBACdC,6BAA6BzC,WAAWW,KAAKG,aAAa;YAC5D;YACA,6EAA6E;YAC7E4B,OAAO1D,MAAM2D,OAAO,GAAG,YAAY;QACrC,GAAGC,KAAK;IACV;AACF,EAAC"}
@@ -1,7 +1,23 @@
1
- import { getGlobalCliClient } from '@sanity/cli-core';
1
+ import { getGlobalCliClient, subdebug } from '@sanity/cli-core';
2
+ import { createRequester } from '@sanity/cli-core/request';
2
3
  export const MCP_API_VERSION = '2025-12-09';
3
4
  export const MCP_SERVER_URL = 'https://mcp.sanity.io';
4
5
  export const MCP_JOURNEY_API_VERSION = 'v2024-02-23';
6
+ const debug = subdebug('mcp:service');
7
+ let mcpRequester;
8
+ function getMCPRequester() {
9
+ if (!mcpRequester) {
10
+ mcpRequester = createRequester({
11
+ middleware: {
12
+ httpErrors: false,
13
+ promise: {
14
+ onlyBody: false
15
+ }
16
+ }
17
+ });
18
+ }
19
+ return mcpRequester;
20
+ }
5
21
  /**
6
22
  * Create a child token for MCP usage
7
23
  * This token is tied to the parent CLI token and will be invalidated
@@ -31,6 +47,44 @@ export const MCP_JOURNEY_API_VERSION = 'v2024-02-23';
31
47
  });
32
48
  return tokenResponse.token;
33
49
  }
50
+ /**
51
+ * Validate an MCP token against the MCP server.
52
+ *
53
+ * MCP tokens are scoped to mcp.sanity.io and are not valid against
54
+ * api.sanity.io, so we validate against the MCP server itself.
55
+ *
56
+ * Sends a minimal POST with just the Authorization header — the server
57
+ * checks auth before content negotiation, so a valid token gets 406
58
+ * (missing Accept header) while an invalid token gets 401. This avoids
59
+ * the cost of a full initialize handshake.
60
+ *
61
+ * @internal
62
+ */ export async function validateMCPToken(token) {
63
+ const request = getMCPRequester();
64
+ // Use a 2500ms timeout — long enough for VPN/proxy/distant-region latency,
65
+ // short enough to not stall the init flow. If the request times out or the
66
+ // server returns an unexpected status, we assume the token is valid rather
67
+ // than falsely marking it as expired (see below).
68
+ const res = await request({
69
+ body: '{}',
70
+ headers: {
71
+ Authorization: `Bearer ${token}`,
72
+ 'Content-Type': 'application/json'
73
+ },
74
+ method: 'POST',
75
+ timeout: 2500,
76
+ url: MCP_SERVER_URL
77
+ });
78
+ // 401/403 are the only responses that definitively mean "bad token".
79
+ // Everything else (406 = valid, 5xx = server issue, 2xx = unexpected)
80
+ // is treated as "assume valid" — we'd rather skip a re-auth prompt
81
+ // than force users to re-configure because of a transient server error.
82
+ if (res.statusCode === 401 || res.statusCode === 403) {
83
+ debug('MCP token validation failed with %d', res.statusCode);
84
+ return false;
85
+ }
86
+ return true;
87
+ }
34
88
  /**
35
89
  * Fetches the post-init MCP prompt from the Journey API and interpolates editor names.
36
90
  * Falls back to a hardcoded default if the API call fails, times out, or returns empty.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/services/mcp.ts"],"sourcesContent":["import {getGlobalCliClient} from '@sanity/cli-core'\n\nexport const MCP_API_VERSION = '2025-12-09'\nexport const MCP_SERVER_URL = 'https://mcp.sanity.io'\nexport const MCP_JOURNEY_API_VERSION = 'v2024-02-23'\n\ninterface PostInitPromptResponse {\n message?: string\n}\n\n/**\n * Create a child token for MCP usage\n * This token is tied to the parent CLI token and will be invalidated\n * when the parent token is invalidated (e.g., on logout)\n *\n * @returns The MCP token string\n * @internal\n */\nexport async function createMCPToken(): Promise<string> {\n const client = await getGlobalCliClient({\n apiVersion: MCP_API_VERSION,\n requireUser: true,\n })\n\n const sessionResponse = await client.request<{id: string; sid: string}>({\n body: {\n sourceId: 'sanity-mcp',\n withStamp: false,\n },\n method: 'POST',\n uri: '/auth/session/create',\n })\n\n const tokenResponse = await client.request<{label: string; token: string}>({\n method: 'GET',\n query: {sid: sessionResponse.sid},\n uri: '/auth/fetch',\n })\n\n return tokenResponse.token\n}\n\n/**\n * Fetches the post-init MCP prompt from the Journey API and interpolates editor names.\n * Falls back to a hardcoded default if the API call fails, times out, or returns empty.\n * Text wrapped in **markers** will be formatted with cyan color.\n */\nexport async function getPostInitPrompt() {\n const client = await getGlobalCliClient({apiVersion: MCP_JOURNEY_API_VERSION, requireUser: false})\n return await client.request<PostInitPromptResponse | null>({\n method: 'GET',\n timeout: 1000,\n uri: '/journey/mcp/post-init-prompt',\n })\n}\n"],"names":["getGlobalCliClient","MCP_API_VERSION","MCP_SERVER_URL","MCP_JOURNEY_API_VERSION","createMCPToken","client","apiVersion","requireUser","sessionResponse","request","body","sourceId","withStamp","method","uri","tokenResponse","query","sid","token","getPostInitPrompt","timeout"],"mappings":"AAAA,SAAQA,kBAAkB,QAAO,mBAAkB;AAEnD,OAAO,MAAMC,kBAAkB,aAAY;AAC3C,OAAO,MAAMC,iBAAiB,wBAAuB;AACrD,OAAO,MAAMC,0BAA0B,cAAa;AAMpD;;;;;;;CAOC,GACD,OAAO,eAAeC;IACpB,MAAMC,SAAS,MAAML,mBAAmB;QACtCM,YAAYL;QACZM,aAAa;IACf;IAEA,MAAMC,kBAAkB,MAAMH,OAAOI,OAAO,CAA4B;QACtEC,MAAM;YACJC,UAAU;YACVC,WAAW;QACb;QACAC,QAAQ;QACRC,KAAK;IACP;IAEA,MAAMC,gBAAgB,MAAMV,OAAOI,OAAO,CAAiC;QACzEI,QAAQ;QACRG,OAAO;YAACC,KAAKT,gBAAgBS,GAAG;QAAA;QAChCH,KAAK;IACP;IAEA,OAAOC,cAAcG,KAAK;AAC5B;AAEA;;;;CAIC,GACD,OAAO,eAAeC;IACpB,MAAMd,SAAS,MAAML,mBAAmB;QAACM,YAAYH;QAAyBI,aAAa;IAAK;IAChG,OAAO,MAAMF,OAAOI,OAAO,CAAgC;QACzDI,QAAQ;QACRO,SAAS;QACTN,KAAK;IACP;AACF"}
1
+ {"version":3,"sources":["../../src/services/mcp.ts"],"sourcesContent":["import {getGlobalCliClient, subdebug} from '@sanity/cli-core'\nimport {createRequester, type Requester} from '@sanity/cli-core/request'\n\nexport const MCP_API_VERSION = '2025-12-09'\nexport const MCP_SERVER_URL = 'https://mcp.sanity.io'\nexport const MCP_JOURNEY_API_VERSION = 'v2024-02-23'\n\nconst debug = subdebug('mcp:service')\n\nlet mcpRequester: Requester | undefined\n\nfunction getMCPRequester(): Requester {\n if (!mcpRequester) {\n mcpRequester = createRequester({\n middleware: {httpErrors: false, promise: {onlyBody: false}},\n })\n }\n return mcpRequester\n}\n\ninterface PostInitPromptResponse {\n message?: string\n}\n\n/**\n * Create a child token for MCP usage\n * This token is tied to the parent CLI token and will be invalidated\n * when the parent token is invalidated (e.g., on logout)\n *\n * @returns The MCP token string\n * @internal\n */\nexport async function createMCPToken(): Promise<string> {\n const client = await getGlobalCliClient({\n apiVersion: MCP_API_VERSION,\n requireUser: true,\n })\n\n const sessionResponse = await client.request<{id: string; sid: string}>({\n body: {\n sourceId: 'sanity-mcp',\n withStamp: false,\n },\n method: 'POST',\n uri: '/auth/session/create',\n })\n\n const tokenResponse = await client.request<{label: string; token: string}>({\n method: 'GET',\n query: {sid: sessionResponse.sid},\n uri: '/auth/fetch',\n })\n\n return tokenResponse.token\n}\n\n/**\n * Validate an MCP token against the MCP server.\n *\n * MCP tokens are scoped to mcp.sanity.io and are not valid against\n * api.sanity.io, so we validate against the MCP server itself.\n *\n * Sends a minimal POST with just the Authorization header — the server\n * checks auth before content negotiation, so a valid token gets 406\n * (missing Accept header) while an invalid token gets 401. This avoids\n * the cost of a full initialize handshake.\n *\n * @internal\n */\nexport async function validateMCPToken(token: string): Promise<boolean> {\n const request = getMCPRequester()\n\n // Use a 2500ms timeout — long enough for VPN/proxy/distant-region latency,\n // short enough to not stall the init flow. If the request times out or the\n // server returns an unexpected status, we assume the token is valid rather\n // than falsely marking it as expired (see below).\n const res = await request({\n body: '{}',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n timeout: 2500,\n url: MCP_SERVER_URL,\n })\n\n // 401/403 are the only responses that definitively mean \"bad token\".\n // Everything else (406 = valid, 5xx = server issue, 2xx = unexpected)\n // is treated as \"assume valid\" — we'd rather skip a re-auth prompt\n // than force users to re-configure because of a transient server error.\n if (res.statusCode === 401 || res.statusCode === 403) {\n debug('MCP token validation failed with %d', res.statusCode)\n return false\n }\n\n return true\n}\n\n/**\n * Fetches the post-init MCP prompt from the Journey API and interpolates editor names.\n * Falls back to a hardcoded default if the API call fails, times out, or returns empty.\n * Text wrapped in **markers** will be formatted with cyan color.\n */\nexport async function getPostInitPrompt() {\n const client = await getGlobalCliClient({apiVersion: MCP_JOURNEY_API_VERSION, requireUser: false})\n return await client.request<PostInitPromptResponse | null>({\n method: 'GET',\n timeout: 1000,\n uri: '/journey/mcp/post-init-prompt',\n })\n}\n"],"names":["getGlobalCliClient","subdebug","createRequester","MCP_API_VERSION","MCP_SERVER_URL","MCP_JOURNEY_API_VERSION","debug","mcpRequester","getMCPRequester","middleware","httpErrors","promise","onlyBody","createMCPToken","client","apiVersion","requireUser","sessionResponse","request","body","sourceId","withStamp","method","uri","tokenResponse","query","sid","token","validateMCPToken","res","headers","Authorization","timeout","url","statusCode","getPostInitPrompt"],"mappings":"AAAA,SAAQA,kBAAkB,EAAEC,QAAQ,QAAO,mBAAkB;AAC7D,SAAQC,eAAe,QAAuB,2BAA0B;AAExE,OAAO,MAAMC,kBAAkB,aAAY;AAC3C,OAAO,MAAMC,iBAAiB,wBAAuB;AACrD,OAAO,MAAMC,0BAA0B,cAAa;AAEpD,MAAMC,QAAQL,SAAS;AAEvB,IAAIM;AAEJ,SAASC;IACP,IAAI,CAACD,cAAc;QACjBA,eAAeL,gBAAgB;YAC7BO,YAAY;gBAACC,YAAY;gBAAOC,SAAS;oBAACC,UAAU;gBAAK;YAAC;QAC5D;IACF;IACA,OAAOL;AACT;AAMA;;;;;;;CAOC,GACD,OAAO,eAAeM;IACpB,MAAMC,SAAS,MAAMd,mBAAmB;QACtCe,YAAYZ;QACZa,aAAa;IACf;IAEA,MAAMC,kBAAkB,MAAMH,OAAOI,OAAO,CAA4B;QACtEC,MAAM;YACJC,UAAU;YACVC,WAAW;QACb;QACAC,QAAQ;QACRC,KAAK;IACP;IAEA,MAAMC,gBAAgB,MAAMV,OAAOI,OAAO,CAAiC;QACzEI,QAAQ;QACRG,OAAO;YAACC,KAAKT,gBAAgBS,GAAG;QAAA;QAChCH,KAAK;IACP;IAEA,OAAOC,cAAcG,KAAK;AAC5B;AAEA;;;;;;;;;;;;CAYC,GACD,OAAO,eAAeC,iBAAiBD,KAAa;IAClD,MAAMT,UAAUV;IAEhB,2EAA2E;IAC3E,2EAA2E;IAC3E,2EAA2E;IAC3E,kDAAkD;IAClD,MAAMqB,MAAM,MAAMX,QAAQ;QACxBC,MAAM;QACNW,SAAS;YACPC,eAAe,CAAC,OAAO,EAAEJ,OAAO;YAChC,gBAAgB;QAClB;QACAL,QAAQ;QACRU,SAAS;QACTC,KAAK7B;IACP;IAEA,qEAAqE;IACrE,sEAAsE;IACtE,mEAAmE;IACnE,wEAAwE;IACxE,IAAIyB,IAAIK,UAAU,KAAK,OAAOL,IAAIK,UAAU,KAAK,KAAK;QACpD5B,MAAM,uCAAuCuB,IAAIK,UAAU;QAC3D,OAAO;IACT;IAEA,OAAO;AACT;AAEA;;;;CAIC,GACD,OAAO,eAAeC;IACpB,MAAMrB,SAAS,MAAMd,mBAAmB;QAACe,YAAYV;QAAyBW,aAAa;IAAK;IAChG,OAAO,MAAMF,OAAOI,OAAO,CAAgC;QACzDI,QAAQ;QACRU,SAAS;QACTT,KAAK;IACP;AACF"}
@@ -1,4 +1,4 @@
1
- import { join, normalize, resolve } from 'node:path';
1
+ import { dirname, join, normalize, resolve } from 'node:path';
2
2
  import { fileURLToPath, pathToFileURL } from 'node:url';
3
3
  import { readPackageJson } from '@sanity/cli-core';
4
4
  import { moduleResolve } from 'import-meta-resolve';
@@ -11,7 +11,9 @@ import { moduleResolve } from 'import-meta-resolve';
11
11
  * @internal
12
12
  */ export async function getLocalPackageVersion(moduleName, workDir) {
13
13
  try {
14
- const dirUrl = pathToFileURL(resolve(workDir, 'noop.js'));
14
+ // Handle import.meta.url being passed instead of a directory path
15
+ const dir = workDir.startsWith('file://') ? dirname(fileURLToPath(workDir)) : workDir;
16
+ const dirUrl = pathToFileURL(resolve(dir, 'noop.js'));
15
17
  let packageJsonUrl;
16
18
  try {
17
19
  packageJsonUrl = moduleResolve(`${moduleName}/package.json`, dirUrl);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/util/getLocalPackageVersion.ts"],"sourcesContent":["import {join, normalize, resolve} from 'node:path'\nimport {fileURLToPath, pathToFileURL} from 'node:url'\n\nimport {readPackageJson} from '@sanity/cli-core'\nimport {moduleResolve} from 'import-meta-resolve'\n\n/**\n * Get the version of a package installed locally.\n *\n * @param moduleName - The name of the package in npm.\n * @param workDir - The working directory to resolve the module from. (aka project root)\n * @returns The version of the package installed locally.\n * @internal\n */\nexport async function getLocalPackageVersion(\n moduleName: string,\n workDir: string,\n): Promise<string | null> {\n try {\n const dirUrl = pathToFileURL(resolve(workDir, 'noop.js'))\n\n let packageJsonUrl: URL\n try {\n packageJsonUrl = moduleResolve(`${moduleName}/package.json`, dirUrl)\n } catch (err: unknown) {\n if (isErrPackagePathNotExported(err)) {\n // Fallback: resolve main entry point and derive package root\n const mainUrl = moduleResolve(moduleName, dirUrl)\n const mainPath = fileURLToPath(mainUrl)\n const normalizedName = normalize(moduleName)\n const idx = mainPath.lastIndexOf(normalizedName)\n const moduleRoot = mainPath.slice(0, idx + normalizedName.length)\n packageJsonUrl = pathToFileURL(join(moduleRoot, 'package.json'))\n } else {\n throw err\n }\n }\n\n return (await readPackageJson(packageJsonUrl)).version\n } catch {\n return null\n }\n}\n\nfunction isErrPackagePathNotExported(err: unknown): boolean {\n return err instanceof Error && 'code' in err && err.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED'\n}\n"],"names":["join","normalize","resolve","fileURLToPath","pathToFileURL","readPackageJson","moduleResolve","getLocalPackageVersion","moduleName","workDir","dirUrl","packageJsonUrl","err","isErrPackagePathNotExported","mainUrl","mainPath","normalizedName","idx","lastIndexOf","moduleRoot","slice","length","version","Error","code"],"mappings":"AAAA,SAAQA,IAAI,EAAEC,SAAS,EAAEC,OAAO,QAAO,YAAW;AAClD,SAAQC,aAAa,EAAEC,aAAa,QAAO,WAAU;AAErD,SAAQC,eAAe,QAAO,mBAAkB;AAChD,SAAQC,aAAa,QAAO,sBAAqB;AAEjD;;;;;;;CAOC,GACD,OAAO,eAAeC,uBACpBC,UAAkB,EAClBC,OAAe;IAEf,IAAI;QACF,MAAMC,SAASN,cAAcF,QAAQO,SAAS;QAE9C,IAAIE;QACJ,IAAI;YACFA,iBAAiBL,cAAc,GAAGE,WAAW,aAAa,CAAC,EAAEE;QAC/D,EAAE,OAAOE,KAAc;YACrB,IAAIC,4BAA4BD,MAAM;gBACpC,6DAA6D;gBAC7D,MAAME,UAAUR,cAAcE,YAAYE;gBAC1C,MAAMK,WAAWZ,cAAcW;gBAC/B,MAAME,iBAAiBf,UAAUO;gBACjC,MAAMS,MAAMF,SAASG,WAAW,CAACF;gBACjC,MAAMG,aAAaJ,SAASK,KAAK,CAAC,GAAGH,MAAMD,eAAeK,MAAM;gBAChEV,iBAAiBP,cAAcJ,KAAKmB,YAAY;YAClD,OAAO;gBACL,MAAMP;YACR;QACF;QAEA,OAAO,AAAC,CAAA,MAAMP,gBAAgBM,eAAc,EAAGW,OAAO;IACxD,EAAE,OAAM;QACN,OAAO;IACT;AACF;AAEA,SAAST,4BAA4BD,GAAY;IAC/C,OAAOA,eAAeW,SAAS,UAAUX,OAAOA,IAAIY,IAAI,KAAK;AAC/D"}
1
+ {"version":3,"sources":["../../src/util/getLocalPackageVersion.ts"],"sourcesContent":["import {dirname, join, normalize, resolve} from 'node:path'\nimport {fileURLToPath, pathToFileURL} from 'node:url'\n\nimport {readPackageJson} from '@sanity/cli-core'\nimport {moduleResolve} from 'import-meta-resolve'\n\n/**\n * Get the version of a package installed locally.\n *\n * @param moduleName - The name of the package in npm.\n * @param workDir - The working directory to resolve the module from. (aka project root)\n * @returns The version of the package installed locally.\n * @internal\n */\nexport async function getLocalPackageVersion(\n moduleName: string,\n workDir: string,\n): Promise<string | null> {\n try {\n // Handle import.meta.url being passed instead of a directory path\n const dir = workDir.startsWith('file://') ? dirname(fileURLToPath(workDir)) : workDir\n const dirUrl = pathToFileURL(resolve(dir, 'noop.js'))\n\n let packageJsonUrl: URL\n try {\n packageJsonUrl = moduleResolve(`${moduleName}/package.json`, dirUrl)\n } catch (err: unknown) {\n if (isErrPackagePathNotExported(err)) {\n // Fallback: resolve main entry point and derive package root\n const mainUrl = moduleResolve(moduleName, dirUrl)\n const mainPath = fileURLToPath(mainUrl)\n const normalizedName = normalize(moduleName)\n const idx = mainPath.lastIndexOf(normalizedName)\n const moduleRoot = mainPath.slice(0, idx + normalizedName.length)\n packageJsonUrl = pathToFileURL(join(moduleRoot, 'package.json'))\n } else {\n throw err\n }\n }\n\n return (await readPackageJson(packageJsonUrl)).version\n } catch {\n return null\n }\n}\n\nfunction isErrPackagePathNotExported(err: unknown): boolean {\n return err instanceof Error && 'code' in err && err.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED'\n}\n"],"names":["dirname","join","normalize","resolve","fileURLToPath","pathToFileURL","readPackageJson","moduleResolve","getLocalPackageVersion","moduleName","workDir","dir","startsWith","dirUrl","packageJsonUrl","err","isErrPackagePathNotExported","mainUrl","mainPath","normalizedName","idx","lastIndexOf","moduleRoot","slice","length","version","Error","code"],"mappings":"AAAA,SAAQA,OAAO,EAAEC,IAAI,EAAEC,SAAS,EAAEC,OAAO,QAAO,YAAW;AAC3D,SAAQC,aAAa,EAAEC,aAAa,QAAO,WAAU;AAErD,SAAQC,eAAe,QAAO,mBAAkB;AAChD,SAAQC,aAAa,QAAO,sBAAqB;AAEjD;;;;;;;CAOC,GACD,OAAO,eAAeC,uBACpBC,UAAkB,EAClBC,OAAe;IAEf,IAAI;QACF,kEAAkE;QAClE,MAAMC,MAAMD,QAAQE,UAAU,CAAC,aAAaZ,QAAQI,cAAcM,YAAYA;QAC9E,MAAMG,SAASR,cAAcF,QAAQQ,KAAK;QAE1C,IAAIG;QACJ,IAAI;YACFA,iBAAiBP,cAAc,GAAGE,WAAW,aAAa,CAAC,EAAEI;QAC/D,EAAE,OAAOE,KAAc;YACrB,IAAIC,4BAA4BD,MAAM;gBACpC,6DAA6D;gBAC7D,MAAME,UAAUV,cAAcE,YAAYI;gBAC1C,MAAMK,WAAWd,cAAca;gBAC/B,MAAME,iBAAiBjB,UAAUO;gBACjC,MAAMW,MAAMF,SAASG,WAAW,CAACF;gBACjC,MAAMG,aAAaJ,SAASK,KAAK,CAAC,GAAGH,MAAMD,eAAeK,MAAM;gBAChEV,iBAAiBT,cAAcJ,KAAKqB,YAAY;YAClD,OAAO;gBACL,MAAMP;YACR;QACF;QAEA,OAAO,AAAC,CAAA,MAAMT,gBAAgBQ,eAAc,EAAGW,OAAO;IACxD,EAAE,OAAM;QACN,OAAO;IACT;AACF;AAEA,SAAST,4BAA4BD,GAAY;IAC/C,OAAOA,eAAeW,SAAS,UAAUX,OAAOA,IAAIY,IAAI,KAAK;AAC/D"}
@@ -1,47 +1,41 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
- import getGitConfig from '@rexxars/gitconfiglocal';
4
3
  import { getCliToken, subdebug } from '@sanity/cli-core';
5
- import { getGitUserInfo } from 'git-user-info';
6
- import promiseProps from 'promise-props-recursive';
7
4
  import { getCliUser } from '../services/user.js';
5
+ import { getGitRemoteOriginUrl, getGitUserInfo } from './gitConfig.js';
8
6
  const debug = subdebug('getProjectDefaults');
9
- export function getProjectDefaults({ isPlugin, workDir }) {
7
+ /**
8
+ * Gathers sensible defaults for a new Sanity project by reading git config,
9
+ * the current Sanity user, and the local directory/README. Used to pre-fill
10
+ * prompts during `sanity init`.
11
+ *
12
+ * @internal
13
+ */ export async function getProjectDefaults({ isPlugin, workDir }) {
10
14
  const cwd = process.cwd();
11
15
  const isSanityRoot = workDir === cwd;
12
- return promiseProps({
13
- license: 'UNLICENSED',
14
- author: getUserInfo(),
15
- // Don't try to use git remote from main Sanity project for plugins
16
- gitRemote: isPlugin && isSanityRoot ? '' : resolveGitRemote(cwd),
17
- // Don't try to guess plugin name if we're initing from Sanity root
18
- projectName: isPlugin && isSanityRoot ? '' : path.basename(cwd),
19
- // If we're initing a plugin, don't use description from Sanity readme
20
- description: getProjectDescription({
16
+ const [author, gitRemote, description] = await Promise.all([
17
+ getUserInfo(),
18
+ isPlugin && isSanityRoot ? undefined : getGitRemoteOriginUrl(cwd),
19
+ getProjectDescription({
21
20
  isPlugin,
22
21
  isSanityRoot,
23
22
  outputDir: cwd
24
23
  })
25
- });
26
- }
27
- async function resolveGitRemote(cwd) {
28
- try {
29
- await fs.stat(path.join(cwd, '.git'));
30
- const cfg = await getGitConfig(cwd);
31
- return cfg.remote && cfg.remote.origin && cfg.remote.origin.url;
32
- } catch {
33
- return undefined;
34
- }
24
+ ]);
25
+ return {
26
+ author,
27
+ description,
28
+ gitRemote,
29
+ license: 'UNLICENSED',
30
+ projectName: isPlugin && isSanityRoot ? '' : path.basename(cwd)
31
+ };
35
32
  }
36
33
  async function getUserInfo() {
37
34
  const user = await getGitUserInfo();
38
- if (!user) {
39
- return getSanityUserInfo();
40
- }
41
- if (user.name && user.email) {
35
+ if (user) {
42
36
  return `${user.name} <${user.email}>`;
43
37
  }
44
- return undefined;
38
+ return getSanityUserInfo();
45
39
  }
46
40
  async function getSanityUserInfo() {
47
41
  const hasToken = Boolean(getCliToken());
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/util/getProjectDefaults.ts"],"sourcesContent":["import fs from 'node:fs/promises'\nimport path from 'node:path'\n\nimport getGitConfig from '@rexxars/gitconfiglocal'\nimport {getCliToken, subdebug} from '@sanity/cli-core'\nimport {getGitUserInfo} from 'git-user-info'\nimport promiseProps from 'promise-props-recursive'\n\nimport {getCliUser} from '../services/user.js'\n\nconst debug = subdebug('getProjectDefaults')\n\ninterface ProjectDefaults {\n author: string | undefined\n description: string\n gitRemote: string\n license: string\n projectName: string\n}\n\nexport function getProjectDefaults({\n isPlugin,\n workDir,\n}: {\n isPlugin: boolean\n workDir: string\n}): Promise<ProjectDefaults> {\n const cwd = process.cwd()\n const isSanityRoot = workDir === cwd\n\n return promiseProps({\n license: 'UNLICENSED',\n\n author: getUserInfo(),\n\n // Don't try to use git remote from main Sanity project for plugins\n gitRemote: isPlugin && isSanityRoot ? '' : resolveGitRemote(cwd),\n\n // Don't try to guess plugin name if we're initing from Sanity root\n projectName: isPlugin && isSanityRoot ? '' : path.basename(cwd),\n\n // If we're initing a plugin, don't use description from Sanity readme\n description: getProjectDescription({isPlugin, isSanityRoot, outputDir: cwd}),\n })\n}\n\nasync function resolveGitRemote(cwd: string): Promise<string | undefined> {\n try {\n await fs.stat(path.join(cwd, '.git'))\n const cfg = await getGitConfig(cwd)\n return cfg.remote && cfg.remote.origin && cfg.remote.origin.url\n } catch {\n return undefined\n }\n}\n\nasync function getUserInfo(): Promise<string | undefined> {\n const user = await getGitUserInfo()\n if (!user) {\n return getSanityUserInfo()\n }\n\n if (user.name && user.email) {\n return `${user.name} <${user.email}>`\n }\n\n return undefined\n}\n\nasync function getSanityUserInfo(): Promise<string | undefined> {\n const hasToken = Boolean(getCliToken())\n if (!hasToken) {\n return undefined\n }\n\n try {\n const user = await getCliUser()\n return user ? `${user.name} <${user.email}>` : undefined\n } catch {\n return undefined\n }\n}\n\nasync function getProjectDescription({\n isPlugin,\n isSanityRoot,\n outputDir,\n}: {\n isPlugin: boolean\n isSanityRoot: boolean\n outputDir: string\n}): Promise<string> {\n const tryResolve = isSanityRoot && !isPlugin\n if (!tryResolve) {\n return ''\n }\n\n // Try to grab a project description from a standard GitHub-generated readme\n try {\n const readmePath = path.join(outputDir, 'README.md')\n const readme = await fs.readFile(readmePath, {encoding: 'utf8'})\n const match = readme.match(/^# .*?\\n+(\\w.*?)(?:$|\\n)/)\n return ((match && match[1]) || '').replace(/\\.$/, '') || ''\n } catch (err) {\n debug(`Error getting project description: ${err}`)\n return ''\n }\n}\n"],"names":["fs","path","getGitConfig","getCliToken","subdebug","getGitUserInfo","promiseProps","getCliUser","debug","getProjectDefaults","isPlugin","workDir","cwd","process","isSanityRoot","license","author","getUserInfo","gitRemote","resolveGitRemote","projectName","basename","description","getProjectDescription","outputDir","stat","join","cfg","remote","origin","url","undefined","user","getSanityUserInfo","name","email","hasToken","Boolean","tryResolve","readmePath","readme","readFile","encoding","match","replace","err"],"mappings":"AAAA,OAAOA,QAAQ,mBAAkB;AACjC,OAAOC,UAAU,YAAW;AAE5B,OAAOC,kBAAkB,0BAAyB;AAClD,SAAQC,WAAW,EAAEC,QAAQ,QAAO,mBAAkB;AACtD,SAAQC,cAAc,QAAO,gBAAe;AAC5C,OAAOC,kBAAkB,0BAAyB;AAElD,SAAQC,UAAU,QAAO,sBAAqB;AAE9C,MAAMC,QAAQJ,SAAS;AAUvB,OAAO,SAASK,mBAAmB,EACjCC,QAAQ,EACRC,OAAO,EAIR;IACC,MAAMC,MAAMC,QAAQD,GAAG;IACvB,MAAME,eAAeH,YAAYC;IAEjC,OAAON,aAAa;QAClBS,SAAS;QAETC,QAAQC;QAER,mEAAmE;QACnEC,WAAWR,YAAYI,eAAe,KAAKK,iBAAiBP;QAE5D,mEAAmE;QACnEQ,aAAaV,YAAYI,eAAe,KAAKb,KAAKoB,QAAQ,CAACT;QAE3D,sEAAsE;QACtEU,aAAaC,sBAAsB;YAACb;YAAUI;YAAcU,WAAWZ;QAAG;IAC5E;AACF;AAEA,eAAeO,iBAAiBP,GAAW;IACzC,IAAI;QACF,MAAMZ,GAAGyB,IAAI,CAACxB,KAAKyB,IAAI,CAACd,KAAK;QAC7B,MAAMe,MAAM,MAAMzB,aAAaU;QAC/B,OAAOe,IAAIC,MAAM,IAAID,IAAIC,MAAM,CAACC,MAAM,IAAIF,IAAIC,MAAM,CAACC,MAAM,CAACC,GAAG;IACjE,EAAE,OAAM;QACN,OAAOC;IACT;AACF;AAEA,eAAed;IACb,MAAMe,OAAO,MAAM3B;IACnB,IAAI,CAAC2B,MAAM;QACT,OAAOC;IACT;IAEA,IAAID,KAAKE,IAAI,IAAIF,KAAKG,KAAK,EAAE;QAC3B,OAAO,GAAGH,KAAKE,IAAI,CAAC,EAAE,EAAEF,KAAKG,KAAK,CAAC,CAAC,CAAC;IACvC;IAEA,OAAOJ;AACT;AAEA,eAAeE;IACb,MAAMG,WAAWC,QAAQlC;IACzB,IAAI,CAACiC,UAAU;QACb,OAAOL;IACT;IAEA,IAAI;QACF,MAAMC,OAAO,MAAMzB;QACnB,OAAOyB,OAAO,GAAGA,KAAKE,IAAI,CAAC,EAAE,EAAEF,KAAKG,KAAK,CAAC,CAAC,CAAC,GAAGJ;IACjD,EAAE,OAAM;QACN,OAAOA;IACT;AACF;AAEA,eAAeR,sBAAsB,EACnCb,QAAQ,EACRI,YAAY,EACZU,SAAS,EAKV;IACC,MAAMc,aAAaxB,gBAAgB,CAACJ;IACpC,IAAI,CAAC4B,YAAY;QACf,OAAO;IACT;IAEA,4EAA4E;IAC5E,IAAI;QACF,MAAMC,aAAatC,KAAKyB,IAAI,CAACF,WAAW;QACxC,MAAMgB,SAAS,MAAMxC,GAAGyC,QAAQ,CAACF,YAAY;YAACG,UAAU;QAAM;QAC9D,MAAMC,QAAQH,OAAOG,KAAK,CAAC;QAC3B,OAAO,AAAC,CAAA,AAACA,SAASA,KAAK,CAAC,EAAE,IAAK,EAAC,EAAGC,OAAO,CAAC,OAAO,OAAO;IAC3D,EAAE,OAAOC,KAAK;QACZrC,MAAM,CAAC,mCAAmC,EAAEqC,KAAK;QACjD,OAAO;IACT;AACF"}
1
+ {"version":3,"sources":["../../src/util/getProjectDefaults.ts"],"sourcesContent":["import fs from 'node:fs/promises'\nimport path from 'node:path'\n\nimport {getCliToken, subdebug} from '@sanity/cli-core'\n\nimport {getCliUser} from '../services/user.js'\nimport {getGitRemoteOriginUrl, getGitUserInfo} from './gitConfig.js'\n\nconst debug = subdebug('getProjectDefaults')\n\ninterface ProjectDefaults {\n author: string | undefined\n description: string\n gitRemote: string | undefined\n license: string\n projectName: string\n}\n\n/**\n * Gathers sensible defaults for a new Sanity project by reading git config,\n * the current Sanity user, and the local directory/README. Used to pre-fill\n * prompts during `sanity init`.\n *\n * @internal\n */\nexport async function getProjectDefaults({\n isPlugin,\n workDir,\n}: {\n isPlugin: boolean\n workDir: string\n}): Promise<ProjectDefaults> {\n const cwd = process.cwd()\n const isSanityRoot = workDir === cwd\n\n const [author, gitRemote, description] = await Promise.all([\n getUserInfo(),\n isPlugin && isSanityRoot ? undefined : getGitRemoteOriginUrl(cwd),\n getProjectDescription({isPlugin, isSanityRoot, outputDir: cwd}),\n ])\n\n return {\n author,\n description,\n gitRemote,\n license: 'UNLICENSED',\n projectName: isPlugin && isSanityRoot ? '' : path.basename(cwd),\n }\n}\n\nasync function getUserInfo(): Promise<string | undefined> {\n const user = await getGitUserInfo()\n if (user) {\n return `${user.name} <${user.email}>`\n }\n\n return getSanityUserInfo()\n}\n\nasync function getSanityUserInfo(): Promise<string | undefined> {\n const hasToken = Boolean(getCliToken())\n if (!hasToken) {\n return undefined\n }\n\n try {\n const user = await getCliUser()\n return user ? `${user.name} <${user.email}>` : undefined\n } catch {\n return undefined\n }\n}\n\nasync function getProjectDescription({\n isPlugin,\n isSanityRoot,\n outputDir,\n}: {\n isPlugin: boolean\n isSanityRoot: boolean\n outputDir: string\n}): Promise<string> {\n const tryResolve = isSanityRoot && !isPlugin\n if (!tryResolve) {\n return ''\n }\n\n // Try to grab a project description from a standard GitHub-generated readme\n try {\n const readmePath = path.join(outputDir, 'README.md')\n const readme = await fs.readFile(readmePath, {encoding: 'utf8'})\n const match = readme.match(/^# .*?\\n+(\\w.*?)(?:$|\\n)/)\n return ((match && match[1]) || '').replace(/\\.$/, '') || ''\n } catch (err) {\n debug(`Error getting project description: ${err}`)\n return ''\n }\n}\n"],"names":["fs","path","getCliToken","subdebug","getCliUser","getGitRemoteOriginUrl","getGitUserInfo","debug","getProjectDefaults","isPlugin","workDir","cwd","process","isSanityRoot","author","gitRemote","description","Promise","all","getUserInfo","undefined","getProjectDescription","outputDir","license","projectName","basename","user","name","email","getSanityUserInfo","hasToken","Boolean","tryResolve","readmePath","join","readme","readFile","encoding","match","replace","err"],"mappings":"AAAA,OAAOA,QAAQ,mBAAkB;AACjC,OAAOC,UAAU,YAAW;AAE5B,SAAQC,WAAW,EAAEC,QAAQ,QAAO,mBAAkB;AAEtD,SAAQC,UAAU,QAAO,sBAAqB;AAC9C,SAAQC,qBAAqB,EAAEC,cAAc,QAAO,iBAAgB;AAEpE,MAAMC,QAAQJ,SAAS;AAUvB;;;;;;CAMC,GACD,OAAO,eAAeK,mBAAmB,EACvCC,QAAQ,EACRC,OAAO,EAIR;IACC,MAAMC,MAAMC,QAAQD,GAAG;IACvB,MAAME,eAAeH,YAAYC;IAEjC,MAAM,CAACG,QAAQC,WAAWC,YAAY,GAAG,MAAMC,QAAQC,GAAG,CAAC;QACzDC;QACAV,YAAYI,eAAeO,YAAYf,sBAAsBM;QAC7DU,sBAAsB;YAACZ;YAAUI;YAAcS,WAAWX;QAAG;KAC9D;IAED,OAAO;QACLG;QACAE;QACAD;QACAQ,SAAS;QACTC,aAAaf,YAAYI,eAAe,KAAKZ,KAAKwB,QAAQ,CAACd;IAC7D;AACF;AAEA,eAAeQ;IACb,MAAMO,OAAO,MAAMpB;IACnB,IAAIoB,MAAM;QACR,OAAO,GAAGA,KAAKC,IAAI,CAAC,EAAE,EAAED,KAAKE,KAAK,CAAC,CAAC,CAAC;IACvC;IAEA,OAAOC;AACT;AAEA,eAAeA;IACb,MAAMC,WAAWC,QAAQ7B;IACzB,IAAI,CAAC4B,UAAU;QACb,OAAOV;IACT;IAEA,IAAI;QACF,MAAMM,OAAO,MAAMtB;QACnB,OAAOsB,OAAO,GAAGA,KAAKC,IAAI,CAAC,EAAE,EAAED,KAAKE,KAAK,CAAC,CAAC,CAAC,GAAGR;IACjD,EAAE,OAAM;QACN,OAAOA;IACT;AACF;AAEA,eAAeC,sBAAsB,EACnCZ,QAAQ,EACRI,YAAY,EACZS,SAAS,EAKV;IACC,MAAMU,aAAanB,gBAAgB,CAACJ;IACpC,IAAI,CAACuB,YAAY;QACf,OAAO;IACT;IAEA,4EAA4E;IAC5E,IAAI;QACF,MAAMC,aAAahC,KAAKiC,IAAI,CAACZ,WAAW;QACxC,MAAMa,SAAS,MAAMnC,GAAGoC,QAAQ,CAACH,YAAY;YAACI,UAAU;QAAM;QAC9D,MAAMC,QAAQH,OAAOG,KAAK,CAAC;QAC3B,OAAO,AAAC,CAAA,AAACA,SAASA,KAAK,CAAC,EAAE,IAAK,EAAC,EAAGC,OAAO,CAAC,OAAO,OAAO;IAC3D,EAAE,OAAOC,KAAK;QACZjC,MAAM,CAAC,mCAAmC,EAAEiC,KAAK;QACjD,OAAO;IACT;AACF"}
@@ -0,0 +1,45 @@
1
+ import { execFile } from 'node:child_process';
2
+ /**
3
+ * Reads `user.name` and `user.email` from git config, resolving through
4
+ * git's full config chain (system, global, local, worktree).
5
+ *
6
+ * Returns `null` if either value is missing or git is not available.
7
+ *
8
+ * @internal
9
+ */ export async function getGitUserInfo() {
10
+ const [name, email] = await Promise.all([
11
+ gitConfigGet('user.name'),
12
+ gitConfigGet('user.email')
13
+ ]);
14
+ return name && email ? {
15
+ email,
16
+ name
17
+ } : null;
18
+ }
19
+ /**
20
+ * Reads the `remote.origin.url` from git config for the repository at {@link cwd}.
21
+ *
22
+ * Returns `undefined` if no remote is configured or git is not available.
23
+ *
24
+ * @internal
25
+ */ export async function getGitRemoteOriginUrl(cwd) {
26
+ return gitConfigGet('remote.origin.url', {
27
+ cwd
28
+ });
29
+ }
30
+ function gitConfigGet(key, options) {
31
+ return new Promise((resolve)=>{
32
+ execFile('git', [
33
+ 'config',
34
+ '--get',
35
+ key
36
+ ], {
37
+ cwd: options?.cwd,
38
+ timeout: 5000
39
+ }, (error, stdout)=>{
40
+ resolve(error ? undefined : stdout.trim() || undefined);
41
+ });
42
+ });
43
+ }
44
+
45
+ //# sourceMappingURL=gitConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/util/gitConfig.ts"],"sourcesContent":["import {execFile} from 'node:child_process'\n\n/**\n * Reads `user.name` and `user.email` from git config, resolving through\n * git's full config chain (system, global, local, worktree).\n *\n * Returns `null` if either value is missing or git is not available.\n *\n * @internal\n */\nexport async function getGitUserInfo(): Promise<{email: string; name: string} | null> {\n const [name, email] = await Promise.all([gitConfigGet('user.name'), gitConfigGet('user.email')])\n return name && email ? {email, name} : null\n}\n\n/**\n * Reads the `remote.origin.url` from git config for the repository at {@link cwd}.\n *\n * Returns `undefined` if no remote is configured or git is not available.\n *\n * @internal\n */\nexport async function getGitRemoteOriginUrl(cwd: string): Promise<string | undefined> {\n return gitConfigGet('remote.origin.url', {cwd})\n}\n\nfunction gitConfigGet(key: string, options?: {cwd?: string}): Promise<string | undefined> {\n return new Promise((resolve) => {\n execFile(\n 'git',\n ['config', '--get', key],\n {cwd: options?.cwd, timeout: 5000},\n (error, stdout) => {\n resolve(error ? undefined : stdout.trim() || undefined)\n },\n )\n })\n}\n"],"names":["execFile","getGitUserInfo","name","email","Promise","all","gitConfigGet","getGitRemoteOriginUrl","cwd","key","options","resolve","timeout","error","stdout","undefined","trim"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,qBAAoB;AAE3C;;;;;;;CAOC,GACD,OAAO,eAAeC;IACpB,MAAM,CAACC,MAAMC,MAAM,GAAG,MAAMC,QAAQC,GAAG,CAAC;QAACC,aAAa;QAAcA,aAAa;KAAc;IAC/F,OAAOJ,QAAQC,QAAQ;QAACA;QAAOD;IAAI,IAAI;AACzC;AAEA;;;;;;CAMC,GACD,OAAO,eAAeK,sBAAsBC,GAAW;IACrD,OAAOF,aAAa,qBAAqB;QAACE;IAAG;AAC/C;AAEA,SAASF,aAAaG,GAAW,EAAEC,OAAwB;IACzD,OAAO,IAAIN,QAAQ,CAACO;QAClBX,SACE,OACA;YAAC;YAAU;YAASS;SAAI,EACxB;YAACD,KAAKE,SAASF;YAAKI,SAAS;QAAI,GACjC,CAACC,OAAOC;YACNH,QAAQE,QAAQE,YAAYD,OAAOE,IAAI,MAAMD;QAC/C;IAEJ;AACF"}
@@ -1,7 +1,7 @@
1
- import { subdebug } from '@sanity/cli-core';
1
+ import { telemetryDebug } from '../../actions/telemetry/telemetryDebug.js';
2
2
  /**
3
3
  * Debug logger for telemetry store operations
4
4
  * @internal
5
- */ export const telemetryStoreDebug = subdebug('telemetry:telemetryStore');
5
+ */ export const telemetryStoreDebug = telemetryDebug.extend('telemetryStore');
6
6
 
7
7
  //# sourceMappingURL=telemetryStoreDebug.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/util/telemetry/telemetryStoreDebug.ts"],"sourcesContent":["import {subdebug} from '@sanity/cli-core'\n\n/**\n * Debug logger for telemetry store operations\n * @internal\n */\nexport const telemetryStoreDebug = subdebug('telemetry:telemetryStore')\n"],"names":["subdebug","telemetryStoreDebug"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,mBAAkB;AAEzC;;;CAGC,GACD,OAAO,MAAMC,sBAAsBD,SAAS,4BAA2B"}
1
+ {"version":3,"sources":["../../../src/util/telemetry/telemetryStoreDebug.ts"],"sourcesContent":["import {telemetryDebug} from '../../actions/telemetry/telemetryDebug.js'\n\n/**\n * Debug logger for telemetry store operations\n * @internal\n */\nexport const telemetryStoreDebug = telemetryDebug.extend('telemetryStore')\n"],"names":["telemetryDebug","telemetryStoreDebug","extend"],"mappings":"AAAA,SAAQA,cAAc,QAAO,4CAA2C;AAExE;;;CAGC,GACD,OAAO,MAAMC,sBAAsBD,eAAeE,MAAM,CAAC,kBAAiB"}
@@ -859,9 +859,7 @@
859
859
  ]
860
860
  },
861
861
  "preview": {
862
- "aliases": [
863
- "start"
864
- ],
862
+ "aliases": [],
865
863
  "args": {
866
864
  "outputDir": {
867
865
  "description": "Output directory",
@@ -891,7 +889,9 @@
891
889
  }
892
890
  },
893
891
  "hasDynamicHelp": false,
894
- "hiddenAliases": [],
892
+ "hiddenAliases": [
893
+ "start"
894
+ ],
895
895
  "id": "preview",
896
896
  "pluginAlias": "@sanity/cli",
897
897
  "pluginName": "@sanity/cli",
@@ -3628,6 +3628,84 @@
3628
3628
  "list.js"
3629
3629
  ]
3630
3630
  },
3631
+ "telemetry:disable": {
3632
+ "aliases": [],
3633
+ "args": {},
3634
+ "description": "Disable telemetry for your logged in user",
3635
+ "examples": [
3636
+ {
3637
+ "command": "<%= config.bin %> telemetry <%= command.id %>",
3638
+ "description": "Disable telemetry for your logged in user"
3639
+ }
3640
+ ],
3641
+ "flags": {},
3642
+ "hasDynamicHelp": false,
3643
+ "hiddenAliases": [],
3644
+ "id": "telemetry:disable",
3645
+ "pluginAlias": "@sanity/cli",
3646
+ "pluginName": "@sanity/cli",
3647
+ "pluginType": "core",
3648
+ "strict": true,
3649
+ "isESM": true,
3650
+ "relativePath": [
3651
+ "dist",
3652
+ "commands",
3653
+ "telemetry",
3654
+ "disable.js"
3655
+ ]
3656
+ },
3657
+ "telemetry:enable": {
3658
+ "aliases": [],
3659
+ "args": {},
3660
+ "description": "Enable telemetry for your logged in user",
3661
+ "examples": [
3662
+ {
3663
+ "command": "<%= config.bin %> telemetry <%= command.id %>",
3664
+ "description": "Enable telemetry for your logged in user"
3665
+ }
3666
+ ],
3667
+ "flags": {},
3668
+ "hasDynamicHelp": false,
3669
+ "hiddenAliases": [],
3670
+ "id": "telemetry:enable",
3671
+ "pluginAlias": "@sanity/cli",
3672
+ "pluginName": "@sanity/cli",
3673
+ "pluginType": "core",
3674
+ "strict": true,
3675
+ "isESM": true,
3676
+ "relativePath": [
3677
+ "dist",
3678
+ "commands",
3679
+ "telemetry",
3680
+ "enable.js"
3681
+ ]
3682
+ },
3683
+ "telemetry:status": {
3684
+ "aliases": [],
3685
+ "args": {},
3686
+ "description": "Check telemetry consent status for your logged in user",
3687
+ "examples": [
3688
+ {
3689
+ "command": "<%= config.bin %> telemetry <%= command.id %>",
3690
+ "description": "Check telemetry consent status for your logged in user"
3691
+ }
3692
+ ],
3693
+ "flags": {},
3694
+ "hasDynamicHelp": false,
3695
+ "hiddenAliases": [],
3696
+ "id": "telemetry:status",
3697
+ "pluginAlias": "@sanity/cli",
3698
+ "pluginName": "@sanity/cli",
3699
+ "pluginType": "core",
3700
+ "strict": true,
3701
+ "isESM": true,
3702
+ "relativePath": [
3703
+ "dist",
3704
+ "commands",
3705
+ "telemetry",
3706
+ "status.js"
3707
+ ]
3708
+ },
3631
3709
  "schema:delete": {
3632
3710
  "aliases": [],
3633
3711
  "args": {},
@@ -4004,84 +4082,6 @@
4004
4082
  "validate.js"
4005
4083
  ]
4006
4084
  },
4007
- "telemetry:disable": {
4008
- "aliases": [],
4009
- "args": {},
4010
- "description": "Disable telemetry for your logged in user",
4011
- "examples": [
4012
- {
4013
- "command": "<%= config.bin %> telemetry <%= command.id %>",
4014
- "description": "Disable telemetry for your logged in user"
4015
- }
4016
- ],
4017
- "flags": {},
4018
- "hasDynamicHelp": false,
4019
- "hiddenAliases": [],
4020
- "id": "telemetry:disable",
4021
- "pluginAlias": "@sanity/cli",
4022
- "pluginName": "@sanity/cli",
4023
- "pluginType": "core",
4024
- "strict": true,
4025
- "isESM": true,
4026
- "relativePath": [
4027
- "dist",
4028
- "commands",
4029
- "telemetry",
4030
- "disable.js"
4031
- ]
4032
- },
4033
- "telemetry:enable": {
4034
- "aliases": [],
4035
- "args": {},
4036
- "description": "Enable telemetry for your logged in user",
4037
- "examples": [
4038
- {
4039
- "command": "<%= config.bin %> telemetry <%= command.id %>",
4040
- "description": "Enable telemetry for your logged in user"
4041
- }
4042
- ],
4043
- "flags": {},
4044
- "hasDynamicHelp": false,
4045
- "hiddenAliases": [],
4046
- "id": "telemetry:enable",
4047
- "pluginAlias": "@sanity/cli",
4048
- "pluginName": "@sanity/cli",
4049
- "pluginType": "core",
4050
- "strict": true,
4051
- "isESM": true,
4052
- "relativePath": [
4053
- "dist",
4054
- "commands",
4055
- "telemetry",
4056
- "enable.js"
4057
- ]
4058
- },
4059
- "telemetry:status": {
4060
- "aliases": [],
4061
- "args": {},
4062
- "description": "Check telemetry consent status for your logged in user",
4063
- "examples": [
4064
- {
4065
- "command": "<%= config.bin %> telemetry <%= command.id %>",
4066
- "description": "Check telemetry consent status for your logged in user"
4067
- }
4068
- ],
4069
- "flags": {},
4070
- "hasDynamicHelp": false,
4071
- "hiddenAliases": [],
4072
- "id": "telemetry:status",
4073
- "pluginAlias": "@sanity/cli",
4074
- "pluginName": "@sanity/cli",
4075
- "pluginType": "core",
4076
- "strict": true,
4077
- "isESM": true,
4078
- "relativePath": [
4079
- "dist",
4080
- "commands",
4081
- "telemetry",
4082
- "status.js"
4083
- ]
4084
- },
4085
4085
  "tokens:add": {
4086
4086
  "aliases": [],
4087
4087
  "args": {
@@ -4943,5 +4943,5 @@
4943
4943
  ]
4944
4944
  }
4945
4945
  },
4946
- "version": "6.0.0"
4946
+ "version": "6.1.1"
4947
4947
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/cli",
3
- "version": "6.0.0",
3
+ "version": "6.1.1",
4
4
  "description": "Sanity CLI tool for managing Sanity projects and organizations",
5
5
  "keywords": [
6
6
  "cli",
@@ -55,7 +55,6 @@
55
55
  "@oclif/core": "^4.8.3",
56
56
  "@oclif/plugin-help": "^6.2.37",
57
57
  "@oclif/plugin-not-found": "^3.2.74",
58
- "@rexxars/gitconfiglocal": "^3.0.1",
59
58
  "@sanity/client": "^7.17.0",
60
59
  "@sanity/codegen": "^6.0.0",
61
60
  "@sanity/descriptors": "^1.3.0",
@@ -82,7 +81,6 @@
82
81
  "execa": "^9.6.0",
83
82
  "form-data": "^4.0.5",
84
83
  "get-latest-version": "^6.0.1",
85
- "git-user-info": "^2.0.3",
86
84
  "gunzip-maybe": "^1.4.2",
87
85
  "import-meta-resolve": "^4.2.0",
88
86
  "is-installed-globally": "^1.0.0",
@@ -122,7 +120,7 @@
122
120
  "which": "^6.0.1",
123
121
  "yaml": "^2.8.2",
124
122
  "zod": "^4.3.6",
125
- "@sanity/cli-core": "1.0.1"
123
+ "@sanity/cli-core": "1.1.0"
126
124
  },
127
125
  "devDependencies": {
128
126
  "@eslint/compat": "^2.0.3",
@@ -155,9 +153,9 @@
155
153
  "vite-tsconfig-paths": "^6.1.1",
156
154
  "vitest": "^4.0.18",
157
155
  "@repo/tsconfig": "3.70.0",
156
+ "@sanity/cli-test": "0.2.2",
158
157
  "@repo/package.config": "0.0.1",
159
- "@sanity/eslint-config-cli": "1.0.0",
160
- "@sanity/cli-test": "0.2.1"
158
+ "@sanity/eslint-config-cli": "1.0.0"
161
159
  },
162
160
  "engines": {
163
161
  "node": ">=20.19.1 <22 || >=22.12"