@mintlify/cli 4.0.1094 → 4.0.1096

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/workflow.js CHANGED
@@ -13,7 +13,7 @@ import { addLog, addLogs, SuccessLog } from '@mintlify/previewing';
13
13
  import fse from 'fs-extra';
14
14
  import { Text } from 'ink';
15
15
  import path from 'path';
16
- import { CMD_EXEC_PATH } from './helpers.js';
16
+ import { CMD_EXEC_PATH, isAI } from './helpers.js';
17
17
  export function slugify(name) {
18
18
  return name
19
19
  .toLowerCase()
@@ -67,10 +67,7 @@ export function addWorkflow() {
67
67
  if (!(yield fse.pathExists(docsJsonPath))) {
68
68
  throw new Error('docs.json not found in the current directory. Please run this command from your docs repository root.');
69
69
  }
70
- const isInteractive = process.stdin.isTTY;
71
- const isClaudeCode = process.env.CLAUDECODE === '1';
72
- const isAI = !isInteractive || isClaudeCode;
73
- if (isAI) {
70
+ if (isAI()) {
74
71
  sendUsageMessageForAI();
75
72
  return;
76
73
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mintlify/cli",
3
- "version": "4.0.1094",
3
+ "version": "4.0.1096",
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": "242ce26277c86cd6a050b19aff122a1c59162c68"
96
+ "gitHead": "03c44790cda77c434ed44c03ef2130e16195fbf8"
97
97
  }
package/src/helpers.tsx CHANGED
@@ -205,6 +205,12 @@ export const terminate = async (code: number) => {
205
205
 
206
206
  export const execAsync = promisify(exec);
207
207
 
208
+ export function isAI(): boolean {
209
+ return (
210
+ !process.stdin.isTTY || process.env.CLAUDECODE === '1' || process.env.TERM_PROGRAM === 'claude'
211
+ );
212
+ }
213
+
208
214
  export const detectPackageManager = async ({ packageName }: { packageName: string }) => {
209
215
  try {
210
216
  const { stdout: packagePath } = await execAsync(`which ${packageName}`);
package/src/init.tsx CHANGED
@@ -5,6 +5,8 @@ import AdmZip from 'adm-zip';
5
5
  import fse from 'fs-extra';
6
6
  import { Box, Text } from 'ink';
7
7
 
8
+ import { isAI } from './helpers.js';
9
+
8
10
  const sendOnboardingMessage = (installDir: string) => {
9
11
  addLogs(
10
12
  <Text bold>Documentation Setup!</Text>,
@@ -61,10 +63,6 @@ export async function init(
61
63
  // Validate path is within current working directory to prevent path traversal
62
64
  validatePathWithinCwd(installDir);
63
65
 
64
- const isInteractive = process.stdin.isTTY;
65
- const isClaudeCode = process.env.CLAUDECODE === '1';
66
- const isAI = !isInteractive || isClaudeCode;
67
-
68
66
  let selectedTheme = theme;
69
67
  let projectName = name;
70
68
 
@@ -76,17 +74,17 @@ export async function init(
76
74
  const dirContents = await fse.readdir(installDir).catch(() => []);
77
75
  const contentsOccupied = dirContents.length > 0;
78
76
 
79
- if ((!theme || !name) && isAI) {
77
+ if ((!theme || !name) && isAI()) {
80
78
  sendUsageMessageForAI(installDir, contentsOccupied, themes);
81
79
  return;
82
80
  }
83
81
 
84
- if (contentsOccupied && isAI && !force) {
82
+ if (contentsOccupied && isAI() && !force) {
85
83
  sendUsageMessageForAI(installDir, contentsOccupied, themes);
86
84
  return;
87
85
  }
88
86
 
89
- if (contentsOccupied && !isAI) {
87
+ if (contentsOccupied && !isAI()) {
90
88
  const choice = await select({
91
89
  message: `Directory ${installDir} is not empty. What would you like to do?`,
92
90
  choices: [
@@ -114,7 +112,7 @@ export async function init(
114
112
  }
115
113
  }
116
114
 
117
- if (!isAI && (!selectedTheme || !projectName)) {
115
+ if (!isAI() && (!selectedTheme || !projectName)) {
118
116
  const defaultProject =
119
117
  projectName !== undefined ? projectName : installDir === '.' ? 'Mintlify' : installDir;
120
118
  if (!projectName) {
package/src/login.tsx CHANGED
@@ -13,6 +13,7 @@ import {
13
13
  import { startCallbackServer } from './callbackServer.js';
14
14
  import { DASHBOARD_URL, STYTCH_CLIENT_ID, TOKEN_ENDPOINT } from './constants.js';
15
15
  import { storeCredentials } from './keyring.js';
16
+ import { trackLoginAttempt, trackLoginFailed, trackLoginSuccess } from './telemetry/track.js';
16
17
 
17
18
  interface TokenResponse {
18
19
  access_token: string;
@@ -37,6 +38,8 @@ export async function login(): Promise<void> {
37
38
  authorizeUrl.searchParams.set('code_challenge', codeChallenge);
38
39
  const url = authorizeUrl.toString();
39
40
 
41
+ void trackLoginAttempt();
42
+
40
43
  const { codePromise, close: closeServer } = await startCallbackServer();
41
44
 
42
45
  addLog(
@@ -101,15 +104,14 @@ export async function login(): Promise<void> {
101
104
  const body = await res.json().catch(() => ({}));
102
105
 
103
106
  if (!res.ok) {
104
- addLog(
105
- <Text color="red">
106
- Login failed: {body.error_message ?? body.error ?? 'unknown error'}
107
- </Text>
108
- );
107
+ const reason = body.error_message ?? body.error ?? 'unknown error';
108
+ void trackLoginFailed(reason);
109
+ addLog(<Text color="red">✖ Login failed: {reason}</Text>);
109
110
  return;
110
111
  }
111
112
 
112
113
  const token = body as TokenResponse;
113
114
  await storeCredentials(token.access_token, token.refresh_token);
115
+ void trackLoginSuccess();
114
116
  addLog(<Text color="green">✔ Logged in successfully.</Text>);
115
117
  }
@@ -1,3 +1,4 @@
1
+ import { getConfigValue } from '../config.js';
1
2
  import { getVersions } from '../helpers.js';
2
3
  import { trackCommand } from './track.js';
3
4
 
@@ -23,7 +24,8 @@ export function createTelemetryMiddleware(): (argv: { _: (string | number)[] })
23
24
  tracked = true;
24
25
  const sanitizedCommand = getSanitizedCommandForTelemetry(argv._);
25
26
  const { cli: cliVersion } = getVersions();
26
- void trackCommand({ command: sanitizedCommand, cliVersion });
27
+ const subdomain = getConfigValue('subdomain');
28
+ void trackCommand({ command: sanitizedCommand, cliVersion, subdomain });
27
29
  }
28
30
  };
29
31
  }
@@ -2,13 +2,14 @@ import os from 'os';
2
2
 
3
3
  import { isTelemetryEnabled } from '../config.js';
4
4
  import { TELEMETRY_ASYNC_TIMEOUT_MS } from '../constants.js';
5
- import { getVersions } from '../helpers.js';
5
+ import { getVersions, isAI } from '../helpers.js';
6
6
  import { getPostHogClient } from './client.js';
7
7
  import { getDistinctId } from './distinctId.js';
8
8
 
9
9
  export interface TrackCommandOptions {
10
10
  command: string;
11
11
  cliVersion?: string;
12
+ subdomain?: string;
12
13
  }
13
14
 
14
15
  async function captureWithTimeout(
@@ -30,7 +31,11 @@ async function captureWithTimeout(
30
31
  ]);
31
32
  }
32
33
 
33
- export async function trackCommand({ command, cliVersion }: TrackCommandOptions): Promise<void> {
34
+ export async function trackCommand({
35
+ command,
36
+ cliVersion,
37
+ subdomain,
38
+ }: TrackCommandOptions): Promise<void> {
34
39
  if (!isTelemetryEnabled()) return;
35
40
 
36
41
  try {
@@ -40,10 +45,38 @@ export async function trackCommand({ command, cliVersion }: TrackCommandOptions)
40
45
  os: os.platform(),
41
46
  arch: os.arch(),
42
47
  node_version: process.version,
48
+ is_ai_agent: isAI(),
49
+ subdomain,
43
50
  });
44
51
  } catch {}
45
52
  }
46
53
 
54
+ async function trackLoginEvent(event: string, extra?: Record<string, unknown>): Promise<void> {
55
+ if (!isTelemetryEnabled()) return;
56
+ try {
57
+ const { cli: cliVersion } = getVersions();
58
+ await captureWithTimeout(event, {
59
+ ...extra,
60
+ cli_version: cliVersion,
61
+ os: os.platform(),
62
+ arch: os.arch(),
63
+ node_version: process.version,
64
+ });
65
+ } catch {}
66
+ }
67
+
68
+ export async function trackLoginAttempt(): Promise<void> {
69
+ return trackLoginEvent('cli.login.attempted');
70
+ }
71
+
72
+ export async function trackLoginSuccess(): Promise<void> {
73
+ return trackLoginEvent('cli.login.succeeded');
74
+ }
75
+
76
+ export async function trackLoginFailed(reason: string): Promise<void> {
77
+ return trackLoginEvent('cli.login.failed', { reason });
78
+ }
79
+
47
80
  export async function trackTelemetryPreferenceChange(options: { enabled: boolean }): Promise<void> {
48
81
  if (process.env.CLI_TEST_MODE === 'true') return;
49
82
 
package/src/workflow.tsx CHANGED
@@ -4,7 +4,7 @@ import fse from 'fs-extra';
4
4
  import { Text } from 'ink';
5
5
  import path from 'path';
6
6
 
7
- import { CMD_EXEC_PATH } from './helpers.js';
7
+ import { CMD_EXEC_PATH, isAI } from './helpers.js';
8
8
 
9
9
  export function slugify(name: string): string {
10
10
  return name
@@ -92,11 +92,7 @@ export async function addWorkflow(): Promise<void> {
92
92
  );
93
93
  }
94
94
 
95
- const isInteractive = process.stdin.isTTY;
96
- const isClaudeCode = process.env.CLAUDECODE === '1';
97
- const isAI = !isInteractive || isClaudeCode;
98
-
99
- if (isAI) {
95
+ if (isAI()) {
100
96
  sendUsageMessageForAI();
101
97
  return;
102
98
  }