@mintlify/cli 4.0.1099 → 4.0.1100

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mintlify/cli",
3
- "version": "4.0.1099",
3
+ "version": "4.0.1100",
4
4
  "description": "The Mintlify CLI",
5
5
  "engines": {
6
6
  "node": ">=18.0.0"
@@ -93,5 +93,5 @@
93
93
  "vitest": "2.1.9",
94
94
  "vitest-mock-process": "1.0.4"
95
95
  },
96
- "gitHead": "9580be43b49210b3a2a21e11e8b6930f67801556"
96
+ "gitHead": "9820ab2d5ccf6764ec1e352a3392955f10afb367"
97
97
  }
@@ -5,6 +5,7 @@ import type { Argv } from 'yargs';
5
5
 
6
6
  import { getConfigValue } from '../config.js';
7
7
  import { terminate } from '../helpers.js';
8
+ import { subdomainMiddleware } from '../middlewares/subdomainMiddleware.js';
8
9
  import {
9
10
  getBucketThreads,
10
11
  getBuckets,
@@ -21,7 +22,6 @@ import type { Conversation } from './types.js';
21
22
  const withSubdomain = <T extends object>(yargs: Argv<T>) =>
22
23
  yargs.option('subdomain', {
23
24
  type: 'string' as const,
24
- default: getConfigValue('subdomain'),
25
25
  description: 'Documentation subdomain (default: mint config set subdomain)',
26
26
  });
27
27
 
@@ -51,16 +51,11 @@ const withDates = <T extends object>(yargs: Argv<T>) =>
51
51
  });
52
52
 
53
53
  const withFormat = <T extends object>(yargs: Argv<T>) =>
54
- yargs
55
- .option('format', {
56
- type: 'string' as const,
57
- choices: ['table', 'plain', 'json', 'graph'] as const,
58
- description: 'Output format (table=pretty, plain=pipeable, json=raw)',
59
- })
60
- .option('agent', {
61
- type: 'boolean' as const,
62
- description: 'Agent-friendly output (equivalent to --format json)',
63
- });
54
+ yargs.option('format', {
55
+ type: 'string' as const,
56
+ choices: ['table', 'plain', 'json', 'graph'] as const,
57
+ description: 'Output format (table=pretty, plain=pipeable, json=raw)',
58
+ });
64
59
 
65
60
  const withAll = <T extends object>(yargs: Argv<T>) => withFormat(withDates(withSubdomain(yargs)));
66
61
 
@@ -73,15 +68,16 @@ function output(format: OutputFormat, text: string) {
73
68
  }
74
69
 
75
70
  export const analyticsBuilder = (yargs: Argv) =>
76
- yargs
71
+ withAll(yargs)
72
+ .middleware(subdomainMiddleware)
77
73
  .command(
78
74
  'stats',
79
75
  'display KPI numbers (views, visitors, searches)',
80
76
  (yargs) =>
81
- withAll(yargs)
82
- .option('agents', { type: 'boolean', description: 'Show only agent traffic' })
83
- .option('humans', { type: 'boolean', description: 'Show only human traffic' })
84
- .option('page', { type: 'string', description: 'Filter to a specific page path' }),
77
+ withAll(yargs).option('page', {
78
+ type: 'string',
79
+ description: 'Filter to a specific page path',
80
+ }),
85
81
  async (argv) => {
86
82
  const format = resolveFormat(argv);
87
83
  try {
@@ -123,7 +119,7 @@ export const analyticsBuilder = (yargs: Argv) =>
123
119
  }
124
120
 
125
121
  if (format === 'graph') {
126
- const label = argv.subdomain ?? 'default';
122
+ const label = argv.subdomain ?? '';
127
123
  const lines: string[] = [];
128
124
  lines.push(chalk.bold(`\nAnalytics \u2014 ${label} (${argv.from} to ${argv.to})\n`));
129
125
  lines.push(chalk.bold(' Human vs Agent\n'));
@@ -149,14 +145,8 @@ export const analyticsBuilder = (yargs: Argv) =>
149
145
  return;
150
146
  }
151
147
 
152
- const agentOnly = argv.agents && !argv.humans;
153
- const humanOnly = argv.humans && !argv.agents;
154
- const showHuman = !agentOnly;
155
- const showAgent = !humanOnly;
156
- const showTotal = showHuman && showAgent;
157
-
158
148
  const lines: string[] = [];
159
- const label = argv.subdomain ?? 'default';
149
+ const label = argv.subdomain ?? '';
160
150
  lines.push(chalk.bold(`\nAnalytics \u2014 ${label} (${argv.from} to ${argv.to})\n`));
161
151
 
162
152
  if (argv.page) {
@@ -164,19 +154,14 @@ export const analyticsBuilder = (yargs: Argv) =>
164
154
  }
165
155
 
166
156
  lines.push(chalk.bold(' Views'));
167
- if (showHuman) lines.push(` Human: ${chalk.cyan(num(kpi.humanViews).padStart(8))}`);
168
- if (showAgent)
169
- lines.push(` Agent: ${chalk.magenta(num(kpi.agentViews).padStart(8))}`);
170
- if (showTotal)
171
- lines.push(` Total: ${num(kpi.humanViews + kpi.agentViews).padStart(8)}`);
157
+ lines.push(` Human: ${chalk.cyan(num(kpi.humanViews).padStart(8))}`);
158
+ lines.push(` Agent: ${chalk.magenta(num(kpi.agentViews).padStart(8))}`);
159
+ lines.push(` Total: ${num(kpi.humanViews + kpi.agentViews).padStart(8)}`);
172
160
 
173
161
  lines.push(chalk.bold('\n Visitors'));
174
- if (showHuman)
175
- lines.push(` Human: ${chalk.cyan(num(kpi.humanVisitors).padStart(8))}`);
176
- if (showAgent)
177
- lines.push(` Agent: ${chalk.magenta(num(kpi.agentVisitors).padStart(8))}`);
178
- if (showTotal)
179
- lines.push(` Total: ${num(kpi.humanVisitors + kpi.agentVisitors).padStart(8)}`);
162
+ lines.push(` Human: ${chalk.cyan(num(kpi.humanVisitors).padStart(8))}`);
163
+ lines.push(` Agent: ${chalk.magenta(num(kpi.agentVisitors).padStart(8))}`);
164
+ lines.push(` Total: ${num(kpi.humanVisitors + kpi.agentVisitors).padStart(8)}`);
180
165
 
181
166
  lines.push(`\n Searches: ${chalk.bold(num(kpi.humanSearches))}`);
182
167
  lines.push(` Feedback: ${chalk.bold(num(kpi.humanFeedback))}`);
@@ -228,7 +213,7 @@ export const analyticsBuilder = (yargs: Argv) =>
228
213
  if (format === 'json') {
229
214
  output(format, JSON.stringify(data, null, 2));
230
215
  } else if (format === 'graph') {
231
- const label = argv.subdomain ?? 'default';
216
+ const label = argv.subdomain ?? '';
232
217
  const lines: string[] = [];
233
218
  lines.push(
234
219
  chalk.bold(`\nSearch Queries \u2014 ${label} (${argv.from} to ${argv.to})\n`)
@@ -246,7 +231,7 @@ export const analyticsBuilder = (yargs: Argv) =>
246
231
  } else if (format === 'plain') {
247
232
  output(format, formatOutput(format, headers, tableRows, data));
248
233
  } else {
249
- const label = argv.subdomain ?? 'default';
234
+ const label = argv.subdomain ?? '';
250
235
  const lines: string[] = [];
251
236
  lines.push(
252
237
  chalk.bold(`\nSearch Analytics \u2014 ${label} (${argv.from} to ${argv.to})`)
@@ -301,7 +286,7 @@ export const analyticsBuilder = (yargs: Argv) =>
301
286
  if (format === 'json') {
302
287
  output(format, JSON.stringify(data, null, 2));
303
288
  } else if (format === 'graph') {
304
- const label = argv.subdomain ?? 'default';
289
+ const label = argv.subdomain ?? '';
305
290
  const lines: string[] = [];
306
291
  lines.push(
307
292
  chalk.bold(`\nFeedback by Page \u2014 ${label} (${argv.from} to ${argv.to})\n`)
@@ -317,7 +302,7 @@ export const analyticsBuilder = (yargs: Argv) =>
317
302
  );
318
303
  output('table', lines.join('\n'));
319
304
  } else {
320
- const label = argv.subdomain ?? 'default';
305
+ const label = argv.subdomain ?? '';
321
306
  const lines: string[] = [];
322
307
  if (format === 'table')
323
308
  lines.push(chalk.bold(`\nFeedback \u2014 ${label} (${argv.from} to ${argv.to})\n`));
@@ -350,7 +335,7 @@ export const analyticsBuilder = (yargs: Argv) =>
350
335
  if (format === 'json') {
351
336
  output(format, JSON.stringify(data, null, 2));
352
337
  } else {
353
- const label = argv.subdomain ?? 'default';
338
+ const label = argv.subdomain ?? '';
354
339
  const lines: string[] = [];
355
340
  if (format === 'table')
356
341
  lines.push(chalk.bold(`\nFeedback \u2014 ${label} (${argv.from} to ${argv.to})\n`));
@@ -406,7 +391,7 @@ export const analyticsBuilder = (yargs: Argv) =>
406
391
  if (format === 'json') {
407
392
  output(format, JSON.stringify(data, null, 2));
408
393
  } else {
409
- const label = argv.subdomain ?? 'default';
394
+ const label = argv.subdomain ?? '';
410
395
  const lines: string[] = [];
411
396
  if (format === 'table')
412
397
  lines.push(
@@ -523,7 +508,7 @@ export const analyticsBuilder = (yargs: Argv) =>
523
508
  if (format === 'json') {
524
509
  output(format, JSON.stringify(data, null, 2));
525
510
  } else if (format === 'graph') {
526
- const label = argv.subdomain ?? 'default';
511
+ const label = argv.subdomain ?? '';
527
512
  const lines: string[] = [];
528
513
  lines.push(
529
514
  chalk.bold(
@@ -541,7 +526,7 @@ export const analyticsBuilder = (yargs: Argv) =>
541
526
  );
542
527
  output('table', lines.join('\n'));
543
528
  } else {
544
- const label = argv.subdomain ?? 'default';
529
+ const label = argv.subdomain ?? '';
545
530
  const lines: string[] = [];
546
531
  if (format === 'table')
547
532
  lines.push(
@@ -1,9 +1,10 @@
1
1
  import chalk from 'chalk';
2
2
 
3
+ import { isAI } from '../helpers.js';
4
+
3
5
  export type OutputFormat = 'table' | 'plain' | 'json' | 'graph';
4
6
 
5
- export function resolveFormat(argv: { format?: string; agent?: boolean }): OutputFormat {
6
- if (argv.agent || process.env.CLAUDECODE === '1') return 'json';
7
+ export function resolveFormat(argv: { format?: string }): OutputFormat {
7
8
  if (
8
9
  argv.format === 'table' ||
9
10
  argv.format === 'plain' ||
@@ -11,6 +12,7 @@ export function resolveFormat(argv: { format?: string; agent?: boolean }): Outpu
11
12
  argv.format === 'graph'
12
13
  )
13
14
  return argv.format;
15
+ if (isAI()) return 'json';
14
16
  return 'plain';
15
17
  }
16
18
 
package/src/cli.tsx CHANGED
@@ -38,9 +38,9 @@ import { getAccessToken } from './keyring.js';
38
38
  import { login } from './login.js';
39
39
  import { logout } from './logout.js';
40
40
  import { mdxLinter } from './mdxLinter.js';
41
+ import { createTelemetryMiddleware } from './middlewares/telemetryMiddleware.js';
41
42
  import { checkOpenApiFile, getOpenApiFilenamesFromDocsConfig } from './openApiCheck.js';
42
- import { status, getCliSubdomain } from './status.js';
43
- import { createTelemetryMiddleware } from './telemetry/middleware.js';
43
+ import { status, getCliSubdomains } from './status.js';
44
44
  import { trackTelemetryPreferenceChange } from './telemetry/track.js';
45
45
  import { update } from './update.js';
46
46
  import { addWorkflow } from './workflow.js';
@@ -119,7 +119,7 @@ export const cli = ({ packageName = 'mint' }: { packageName?: string }) => {
119
119
  const configuredSubdomain = getConfigValue('subdomain');
120
120
  subdomain =
121
121
  configuredSubdomain ??
122
- (accessToken ? (await getCliSubdomain(accessToken)) ?? undefined : undefined);
122
+ (accessToken ? (await getCliSubdomains(accessToken))[0] : undefined);
123
123
  } catch {}
124
124
  if (port != undefined) {
125
125
  await dev({
package/src/login.tsx CHANGED
@@ -1,5 +1,5 @@
1
- import { input } from '@inquirer/prompts';
2
- import { addLog, ErrorLog, SuccessLog } from '@mintlify/previewing';
1
+ import { input, search } from '@inquirer/prompts';
2
+ import { addLog, ErrorLog, InfoLog, SuccessLog } from '@mintlify/previewing';
3
3
  import chalk from 'chalk';
4
4
  import { Box, Text } from 'ink';
5
5
  import open from 'open';
@@ -11,8 +11,10 @@ import {
11
11
  } from 'openid-client';
12
12
 
13
13
  import { startCallbackServer } from './callbackServer.js';
14
+ import { setConfigValue } from './config.js';
14
15
  import { DASHBOARD_URL, STYTCH_CLIENT_ID, TOKEN_ENDPOINT } from './constants.js';
15
16
  import { storeCredentials } from './keyring.js';
17
+ import { getCliSubdomains } from './status.js';
16
18
  import { trackLoginAttempt, trackLoginFailed, trackLoginSuccess } from './telemetry/track.js';
17
19
 
18
20
  interface TokenResponse {
@@ -114,4 +116,48 @@ export async function login(): Promise<void> {
114
116
  await storeCredentials(token.access_token, token.refresh_token);
115
117
  void trackLoginSuccess();
116
118
  addLog(<SuccessLog message="logged in successfully" />);
119
+ await promptSubdomainSelection(token.access_token);
120
+ }
121
+
122
+ function isPromptCancellationError(error: unknown): boolean {
123
+ return error instanceof Error && error.name === 'ExitPromptError';
124
+ }
125
+
126
+ async function promptSubdomainSelection(accessToken: string): Promise<void> {
127
+ const subdomains = await getCliSubdomains(accessToken);
128
+ if (subdomains.length === 0) return;
129
+
130
+ if (subdomains.length === 1) {
131
+ await setConfigValue('subdomain', subdomains[0]!);
132
+ addLog(<InfoLog message={`default project set to ${subdomains[0]}`} />);
133
+ return;
134
+ }
135
+
136
+ await new Promise((resolve) => setTimeout(resolve, 50));
137
+
138
+ let chosen: string;
139
+ try {
140
+ chosen = await search<string>({
141
+ message: 'Select a default project',
142
+ source: (term) => {
143
+ const results = term
144
+ ? subdomains.filter((s) => s.toLowerCase().includes(term.toLowerCase()))
145
+ : subdomains;
146
+ return results.map((s) => ({ name: s, value: s }));
147
+ },
148
+ });
149
+ } catch (error) {
150
+ if (isPromptCancellationError(error)) {
151
+ addLog(
152
+ <InfoLog
153
+ message={`No project set. To set a default project, run ${chalk.bold('mintlify config set subdomain <subdomain>')}`}
154
+ />
155
+ );
156
+ return;
157
+ }
158
+ throw error;
159
+ }
160
+
161
+ await setConfigValue('subdomain', chosen);
162
+ addLog(<InfoLog message={`default project set to ${chosen}`} />);
117
163
  }
@@ -0,0 +1,16 @@
1
+ import { getConfigValue } from '../config.js';
2
+ import { getAccessToken } from '../keyring.js';
3
+ import { getCliSubdomains } from '../status.js';
4
+
5
+ export async function subdomainMiddleware(argv: { subdomain?: string }): Promise<void> {
6
+ if (argv.subdomain) return;
7
+ const fromConfig = getConfigValue('subdomain');
8
+ if (fromConfig) {
9
+ argv.subdomain = fromConfig;
10
+ return;
11
+ }
12
+ const accessToken = await getAccessToken();
13
+ if (!accessToken) return;
14
+ const subdomains = await getCliSubdomains(accessToken);
15
+ argv.subdomain = subdomains[0];
16
+ }
@@ -1,6 +1,6 @@
1
1
  import { getConfigValue } from '../config.js';
2
2
  import { getVersions } from '../helpers.js';
3
- import { trackCommand } from './track.js';
3
+ import { trackCommand } from '../telemetry/track.js';
4
4
 
5
5
  const SCRAPE_SUBCOMMANDS = new Set(['page', 'site', 'openapi']);
6
6
 
package/src/status.tsx CHANGED
@@ -11,20 +11,20 @@ import { getAccessToken } from './keyring.js';
11
11
  const StatusResponseSchema = z.object({
12
12
  user: z.object({ email: z.string() }),
13
13
  org: z.object({ name: z.string() }),
14
- subdomain: z.string().nullable().optional(),
14
+ subdomains: z.array(z.string()).default([]),
15
15
  });
16
16
 
17
- export async function getCliSubdomain(accessToken: string): Promise<string | null> {
17
+ export async function getCliSubdomains(accessToken: string): Promise<string[]> {
18
18
  try {
19
19
  const res = await fetch(`${API_URL}/api/cli/status`, {
20
20
  headers: { Authorization: `Bearer ${accessToken}` },
21
21
  });
22
- if (!res.ok) return null;
22
+ if (!res.ok) return [];
23
23
  const json = await res.json().catch(() => null);
24
24
  const parsed = StatusResponseSchema.safeParse(json);
25
- return parsed.success ? parsed.data.subdomain ?? null : null;
25
+ return parsed.success ? parsed.data.subdomains : [];
26
26
  } catch {
27
- return null;
27
+ return [];
28
28
  }
29
29
  }
30
30
 
@@ -52,9 +52,9 @@ export async function status(): Promise<void> {
52
52
  return;
53
53
  }
54
54
 
55
- const { user, org, subdomain: apiSubdomain } = parsed.data;
55
+ const { user, org, subdomains } = parsed.data;
56
56
  const version = getCliVersion();
57
- const subdomain = getConfigValue('subdomain') ?? apiSubdomain ?? null;
57
+ const subdomain = getConfigValue('subdomain') ?? subdomains[0] ?? null;
58
58
  addLog(
59
59
  <Box flexDirection="column" paddingY={1}>
60
60
  {version && (