@mintlify/cli 4.0.1197 → 4.0.1199

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/welcome.js ADDED
@@ -0,0 +1,107 @@
1
+ import chalk from 'chalk';
2
+ import { MINT_GREEN, MINT_LOGO, MINT_LOGO_WIDTH } from './logo.js';
3
+ const mint = chalk.hex(MINT_GREEN);
4
+ const TAGLINE = 'Build, preview, and ship beautiful docs.';
5
+ // Row at which the identity block starts beside the logo, and the gap between
6
+ // the logo's right edge and that block.
7
+ const META_TOP_ROW = 6;
8
+ const META_GAP = 4;
9
+ const GROUPS = [
10
+ {
11
+ title: 'Preview',
12
+ commands: [
13
+ { name: 'dev', description: 'Start a local preview of your docs' },
14
+ { name: 'new', description: 'Scaffold a new documentation site' },
15
+ ],
16
+ },
17
+ {
18
+ title: 'Quality',
19
+ commands: [
20
+ { name: 'validate', description: 'Validate the docs build (strict)' },
21
+ { name: 'broken-links', description: 'Find broken links before you ship' },
22
+ { name: 'a11y', description: 'Check docs for accessibility issues' },
23
+ { name: 'score', description: 'Run agent-readiness checks' },
24
+ ],
25
+ },
26
+ {
27
+ title: 'Insights',
28
+ commands: [
29
+ { name: 'analytics', description: 'View analytics for your docs' },
30
+ { name: 'workflow', description: 'Create and manage workflows' },
31
+ ],
32
+ },
33
+ {
34
+ title: 'Account',
35
+ commands: [
36
+ { name: 'login', description: 'Authenticate with Mintlify' },
37
+ { name: 'logout', description: 'Sign out on this device' },
38
+ { name: 'status', description: 'View authentication status' },
39
+ ],
40
+ },
41
+ {
42
+ title: 'CLI',
43
+ commands: [
44
+ { name: 'config', description: 'Manage CLI configuration' },
45
+ { name: 'update', description: 'Update to the latest version' },
46
+ ],
47
+ },
48
+ ];
49
+ const EXAMPLES = [
50
+ { name: 'dev', description: 'Start a local preview of your docs' },
51
+ { name: 'new docs', description: 'Scaffold a new docs site in ./docs' },
52
+ { name: 'validate', description: 'Validate your docs before shipping' },
53
+ { name: 'broken-links', description: 'Find broken links across your docs' },
54
+ ];
55
+ const COMMAND_NAME_WIDTH = GROUPS.flatMap((group) => group.commands).reduce((max, command) => Math.max(max, command.name.length), 0) + 2;
56
+ // `name description`, name padded to a shared column and colored mint. Padding
57
+ // is computed on the raw name so ANSI codes don't throw off the alignment.
58
+ const commandRow = (name, description, nameWidth, indent) => ' '.repeat(indent) + mint(name.padEnd(nameWidth)) + chalk.dim(description);
59
+ /**
60
+ * Builds the branded welcome / help screen as a ready-to-print string: the
61
+ * Mintlify leaf beside an identity block, then grouped examples and commands.
62
+ */
63
+ export function renderWelcome({ packageName, version, signedIn, subdomain, }) {
64
+ const title = `${packageName.charAt(0).toUpperCase()}${packageName.slice(1)} CLI`;
65
+ // Only prefix "v" for real semver versions; the linked-dev build reports a
66
+ // descriptive string ("linked to local package").
67
+ const versionLabel = version ? (/^\d/.test(version) ? `v${version}` : version) : undefined;
68
+ const identity = [
69
+ chalk.bold(title) + (versionLabel ? chalk.dim(` ${versionLabel}`) : ''),
70
+ '',
71
+ chalk.dim(TAGLINE),
72
+ signedIn
73
+ ? chalk.dim('Signed in to Mintlify')
74
+ : chalk.dim('Run ') + mint(`${packageName} login`) + chalk.dim(' to sign in'),
75
+ ...(subdomain ? [chalk.dim(`Project ${subdomain}`)] : []),
76
+ ];
77
+ const lines = [''];
78
+ // Hero: logo on the left, identity block on the right.
79
+ MINT_LOGO.forEach((logoLine, index) => {
80
+ const identityLine = identity[index - META_TOP_ROW];
81
+ let row = ' ' + mint(logoLine);
82
+ if (identityLine) {
83
+ row += ' '.repeat(MINT_LOGO_WIDTH - logoLine.length + META_GAP) + identityLine;
84
+ }
85
+ lines.push(row);
86
+ });
87
+ // Examples.
88
+ const exampleWidth = EXAMPLES.reduce((max, example) => Math.max(max, `${packageName} ${example.name}`.length), 0) +
89
+ 2;
90
+ lines.push('', ' ' + chalk.bold('EXAMPLES'));
91
+ for (const example of EXAMPLES) {
92
+ lines.push(commandRow(`${packageName} ${example.name}`, example.description, exampleWidth, 3));
93
+ }
94
+ // Commands, grouped.
95
+ lines.push('', ' ' + chalk.bold('COMMANDS'));
96
+ for (const group of GROUPS) {
97
+ lines.push('', ' ' + chalk.bold(group.title));
98
+ for (const command of group.commands) {
99
+ lines.push(commandRow(command.name, command.description, COMMAND_NAME_WIDTH, 5));
100
+ }
101
+ }
102
+ lines.push('', ' ' +
103
+ chalk.dim('Run ') +
104
+ mint(`${packageName} <command> --help`) +
105
+ chalk.dim(' for details on any command.'), '');
106
+ return lines.join('\n') + '\n';
107
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mintlify/cli",
3
- "version": "4.0.1197",
3
+ "version": "4.0.1199",
4
4
  "description": "The Mintlify CLI",
5
5
  "engines": {
6
6
  "node": ">=18.0.0"
@@ -45,12 +45,12 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "@inquirer/prompts": "7.9.0",
48
- "@mintlify/common": "1.0.923",
49
- "@mintlify/link-rot": "3.0.1104",
48
+ "@mintlify/common": "1.0.924",
49
+ "@mintlify/link-rot": "3.0.1105",
50
50
  "@mintlify/models": "0.0.317",
51
- "@mintlify/prebuild": "1.0.1068",
52
- "@mintlify/previewing": "4.0.1129",
53
- "@mintlify/validation": "0.1.721",
51
+ "@mintlify/prebuild": "1.0.1069",
52
+ "@mintlify/previewing": "4.0.1130",
53
+ "@mintlify/validation": "0.1.722",
54
54
  "adm-zip": "0.5.16",
55
55
  "chalk": "5.2.0",
56
56
  "color": "4.2.3",
@@ -90,9 +90,10 @@
90
90
  "oxlint": "1.66.0",
91
91
  "oxlint-tsgolint": "^0.23.0",
92
92
  "rimraf": "5.0.1",
93
+ "sharp": "^0.33.1",
93
94
  "typescript": "5.5.3",
94
95
  "vitest": "2.1.9",
95
96
  "vitest-mock-process": "1.0.4"
96
97
  },
97
- "gitHead": "1e825e6876d8e7ab9d8924383a3f751670189dae"
98
+ "gitHead": "206c7ed073d1f20e312bdb8ff57cea11c73be0af"
98
99
  }
@@ -0,0 +1,79 @@
1
+ // Regenerates `src/logo.ts` — the ASCII Mintlify leaf shown on the welcome
2
+ // screen.
3
+ //
4
+ // Pipeline: flatten the icon's three vector paths into one solid-white
5
+ // silhouette (so brightness-based renderers don't drop the darker leaf
6
+ // segments), rasterize it with `sharp`, let `chafa` do the image -> ASCII shape
7
+ // detection, then normalize every glyph to a solid `#` for the clean
8
+ // single-color mark.
9
+ //
10
+ // Usage: node scripts/generate-logo.mjs
11
+ // Requires `sharp` (a monorepo dependency) and the `chafa` CLI on PATH.
12
+
13
+ import { execFileSync } from 'node:child_process';
14
+ import { mkdtempSync, writeFileSync } from 'node:fs';
15
+ import { tmpdir } from 'node:os';
16
+ import { dirname, join } from 'node:path';
17
+ import { fileURLToPath } from 'node:url';
18
+ import sharp from 'sharp';
19
+
20
+ const MINT_GREEN = '#18E299';
21
+ const MINT_GREEN_DEEP = '#0C8C5E';
22
+
23
+ // Paths from mintlify-icon.svg (viewBox 0 0 19 19), all filled solid white over
24
+ // a black background so chafa sees one uniform-brightness silhouette.
25
+ const PATHS = [
26
+ 'M18.367 7.28888V1.59755C18.367 0.986819 17.8715 0.5 17.2699 0.5H11.5812C10.6877 0.5 9.80295 0.677018 8.98017 1.01336C8.15738 1.35856 7.40539 1.85424 6.77724 2.49152L6.733 2.53578C5.90137 3.37664 5.30862 4.42108 5.00781 5.57174C5.54749 5.43012 6.10483 5.35931 6.6622 5.35046C8.14852 5.33276 9.60831 5.81073 10.7938 6.7047C11.8643 7.50131 12.6783 8.59885 13.1206 9.86458C13.5807 11.148 13.6337 12.5465 13.2887 13.8653C14.43 13.5644 15.4828 12.9714 16.3233 12.1393L16.3675 12.0951C16.9957 11.4667 17.4999 10.7143 17.845 9.89114C18.19 9.06797 18.3581 8.18285 18.3581 7.28888H18.367Z',
27
+ 'M4.83793 7.193C4.84674 5.44706 5.54303 3.77167 6.76814 2.51953L2.03511 7.25472C2.01749 7.27236 1.99985 7.28117 1.98222 7.29881C0.827615 8.44513 0.131342 9.97945 0.0167623 11.6019C-0.0890033 13.1186 0.307609 14.6176 1.15373 15.8698C1.23444 15.9892 1.45343 16.0285 1.57682 15.9139L4.47656 13.0216C5.38438 12.1134 5.66643 10.7642 5.23455 9.55618C4.96132 8.80666 4.82912 8.00424 4.83793 7.193Z',
28
+ 'M16.341 12.0938C15.4332 12.9844 14.2962 13.6016 13.0623 13.875C11.8195 14.1483 10.5327 14.0689 9.33405 13.6457C9.33405 13.6457 9.32522 13.6457 9.31641 13.6457C8.10892 13.2136 6.76042 13.4958 5.8526 14.3952L2.95282 17.2875C2.82943 17.4109 2.84706 17.6137 2.99689 17.7107C4.24845 18.5484 5.74683 18.954 7.26281 18.8482C8.88455 18.7336 10.4093 18.037 11.5639 16.8818L11.608 16.8378L16.341 12.1026V12.0938Z',
29
+ ];
30
+
31
+ const SIZE = '28x14'; // chafa output columns x rows
32
+ const RES = 480; // raster resolution fed to chafa
33
+
34
+ const silhouette = `<svg width="19" height="19" viewBox="0 0 19 19" xmlns="http://www.w3.org/2000/svg"><rect width="19" height="19" fill="#000"/>${PATHS.map(
35
+ (d) => `<path d="${d}" fill="#fff"/>`
36
+ ).join('')}</svg>`;
37
+
38
+ const pngPath = join(mkdtempSync(join(tmpdir(), 'mint-logo-')), 'silhouette.png');
39
+ await sharp(Buffer.from(silhouette)).resize(RES, RES, { fit: 'fill' }).png().toFile(pngPath);
40
+
41
+ const ascii = execFileSync(
42
+ 'chafa',
43
+ ['--symbols', 'ascii', '-c', 'none', '--dither', 'none', '--size', SIZE, pngPath],
44
+ { encoding: 'utf8' }
45
+ );
46
+
47
+ // chafa renders the shape with a brightness ramp of letters; collapse every
48
+ // glyph to `#` for the clean single-color mark, trim trailing space, and drop
49
+ // blank rows at the bottom.
50
+ const art = ascii
51
+ .split('\n')
52
+ .map((line) => line.replace(/\S/g, '#').replace(/\s+$/, ''))
53
+ .filter(
54
+ (line, index, lines) => line.length > 0 || lines.slice(index).some((rest) => rest.trim())
55
+ );
56
+
57
+ const file = `// AUTO-GENERATED by \`scripts/generate-logo.mjs\` from the Mintlify icon mark.
58
+ // Do not edit by hand — run \`node scripts/generate-logo.mjs\` to regenerate.
59
+
60
+ /** Mintlify brand greens. */
61
+ export const MINT_GREEN = '${MINT_GREEN}';
62
+ export const MINT_GREEN_DEEP = '${MINT_GREEN_DEEP}';
63
+
64
+ /**
65
+ * The Mintlify leaf, rendered as a solid \`#\` mark (chafa shape detection,
66
+ * normalized to a single glyph). Drawn in {@link MINT_GREEN} on the welcome
67
+ * screen.
68
+ */
69
+ export const MINT_LOGO: string[] = [
70
+ ${art.map((line) => ` '${line}',`).join('\n')}
71
+ ];
72
+
73
+ /** Width of the widest logo row, for laying out content beside it. */
74
+ export const MINT_LOGO_WIDTH = MINT_LOGO.reduce((max, line) => Math.max(max, line.length), 0);
75
+ `;
76
+
77
+ const out = join(dirname(fileURLToPath(import.meta.url)), '..', 'src', 'logo.ts');
78
+ writeFileSync(out, file);
79
+ console.log(`Wrote ${out} (${art.length} rows, width ${Math.max(...art.map((r) => r.length))})`);
package/src/cli.tsx CHANGED
@@ -30,6 +30,7 @@ import {
30
30
  checkNodeVersion,
31
31
  autoUpgradeIfNeeded,
32
32
  getVersions,
33
+ isAI,
33
34
  suppressConsoleWarnings,
34
35
  terminate,
35
36
  } from './helpers.js';
@@ -44,9 +45,44 @@ import { scoreHandler } from './score/index.js';
44
45
  import { status, getCliSubdomains } from './status.js';
45
46
  import { trackTelemetryPreferenceChange } from './telemetry/track.js';
46
47
  import { update } from './update.js';
48
+ import { renderWelcome } from './welcome.js';
47
49
  import { workflowsBuilder } from './workflows/index.js';
48
50
 
51
+ // The branded welcome screen replaces yargs' default help, but only for an
52
+ // interactive human at a TTY. Piped, scripted, AI, and test callers keep the
53
+ // plain yargs help so their output stays parseable and stable.
54
+ const shouldShowWelcome = (args: string[]): boolean => {
55
+ const interactive =
56
+ Boolean(process.stdout.isTTY) && !isAI() && process.env.CLI_TEST_MODE !== 'true';
57
+ if (!interactive) return false;
58
+ if (args.length === 0) return true;
59
+
60
+ const positionals = args.filter((arg) => !arg.startsWith('-'));
61
+ const command = positionals[0];
62
+ // `help` followed by a subcommand (e.g. `mint help dev`) is a subcommand
63
+ // lookup — let yargs handle it rather than intercepting with the welcome screen.
64
+ const isTopLevel = command === undefined || (command === 'help' && positionals.length === 1);
65
+ const helpRequested = command === 'help' || args.includes('--help') || args.includes('-h');
66
+ return isTopLevel && helpRequested;
67
+ };
68
+
69
+ const showWelcome = async (packageName: string): Promise<void> => {
70
+ const { cli: version } = getVersions(packageName);
71
+ const subdomain = getConfigValue('subdomain');
72
+ let signedIn = false;
73
+ try {
74
+ signedIn = Boolean(await getAccessToken());
75
+ } catch {}
76
+
77
+ process.stdout.write(renderWelcome({ packageName, version, signedIn, subdomain }));
78
+ await terminate(0);
79
+ };
80
+
49
81
  export const cli = ({ packageName = 'mint' }: { packageName?: string }) => {
82
+ if (shouldShowWelcome(hideBin(process.argv))) {
83
+ return showWelcome(packageName);
84
+ }
85
+
50
86
  const telemetryMiddleware = createTelemetryMiddleware();
51
87
  render(<Logs />);
52
88
 
package/src/logo.ts ADDED
@@ -0,0 +1,31 @@
1
+ // AUTO-GENERATED by `scripts/generate-logo.mjs` from the Mintlify icon mark.
2
+ // Do not edit by hand — run `node scripts/generate-logo.mjs` to regenerate.
3
+
4
+ /** Mintlify brand greens. */
5
+ export const MINT_GREEN = '#18E299';
6
+ export const MINT_GREEN_DEEP = '#0C8C5E';
7
+
8
+ /**
9
+ * The Mintlify leaf, rendered as a solid `#` mark (chafa shape detection,
10
+ * normalized to a single glyph). Drawn in {@link MINT_GREEN} on the welcome
11
+ * screen.
12
+ */
13
+ export const MINT_LOGO: string[] = [
14
+ ' ##############',
15
+ ' #################',
16
+ ' ###################',
17
+ ' #####################',
18
+ ' #### ##############',
19
+ ' ##### ###########',
20
+ ' ####### #########',
21
+ '######## ########',
22
+ '######## ######',
23
+ '######## ####',
24
+ '###### ##############',
25
+ ' ### ##############',
26
+ ' ##############',
27
+ ' ##########',
28
+ ];
29
+
30
+ /** Width of the widest logo row, for laying out content beside it. */
31
+ export const MINT_LOGO_WIDTH = MINT_LOGO.reduce((max, line) => Math.max(max, line.length), 0);
package/src/welcome.ts ADDED
@@ -0,0 +1,155 @@
1
+ import chalk from 'chalk';
2
+
3
+ import { MINT_GREEN, MINT_LOGO, MINT_LOGO_WIDTH } from './logo.js';
4
+
5
+ const mint = chalk.hex(MINT_GREEN);
6
+
7
+ const TAGLINE = 'Build, preview, and ship beautiful docs.';
8
+
9
+ // Row at which the identity block starts beside the logo, and the gap between
10
+ // the logo's right edge and that block.
11
+ const META_TOP_ROW = 6;
12
+ const META_GAP = 4;
13
+
14
+ interface CommandEntry {
15
+ name: string;
16
+ description: string;
17
+ }
18
+
19
+ interface CommandGroup {
20
+ title: string;
21
+ commands: CommandEntry[];
22
+ }
23
+
24
+ const GROUPS: CommandGroup[] = [
25
+ {
26
+ title: 'Preview',
27
+ commands: [
28
+ { name: 'dev', description: 'Start a local preview of your docs' },
29
+ { name: 'new', description: 'Scaffold a new documentation site' },
30
+ ],
31
+ },
32
+ {
33
+ title: 'Quality',
34
+ commands: [
35
+ { name: 'validate', description: 'Validate the docs build (strict)' },
36
+ { name: 'broken-links', description: 'Find broken links before you ship' },
37
+ { name: 'a11y', description: 'Check docs for accessibility issues' },
38
+ { name: 'score', description: 'Run agent-readiness checks' },
39
+ ],
40
+ },
41
+ {
42
+ title: 'Insights',
43
+ commands: [
44
+ { name: 'analytics', description: 'View analytics for your docs' },
45
+ { name: 'workflow', description: 'Create and manage workflows' },
46
+ ],
47
+ },
48
+ {
49
+ title: 'Account',
50
+ commands: [
51
+ { name: 'login', description: 'Authenticate with Mintlify' },
52
+ { name: 'logout', description: 'Sign out on this device' },
53
+ { name: 'status', description: 'View authentication status' },
54
+ ],
55
+ },
56
+ {
57
+ title: 'CLI',
58
+ commands: [
59
+ { name: 'config', description: 'Manage CLI configuration' },
60
+ { name: 'update', description: 'Update to the latest version' },
61
+ ],
62
+ },
63
+ ];
64
+
65
+ const EXAMPLES: CommandEntry[] = [
66
+ { name: 'dev', description: 'Start a local preview of your docs' },
67
+ { name: 'new docs', description: 'Scaffold a new docs site in ./docs' },
68
+ { name: 'validate', description: 'Validate your docs before shipping' },
69
+ { name: 'broken-links', description: 'Find broken links across your docs' },
70
+ ];
71
+
72
+ const COMMAND_NAME_WIDTH =
73
+ GROUPS.flatMap((group) => group.commands).reduce(
74
+ (max, command) => Math.max(max, command.name.length),
75
+ 0
76
+ ) + 2;
77
+
78
+ // `name description`, name padded to a shared column and colored mint. Padding
79
+ // is computed on the raw name so ANSI codes don't throw off the alignment.
80
+ const commandRow = (name: string, description: string, nameWidth: number, indent: number): string =>
81
+ ' '.repeat(indent) + mint(name.padEnd(nameWidth)) + chalk.dim(description);
82
+
83
+ export interface WelcomeOptions {
84
+ packageName: string;
85
+ version?: string;
86
+ signedIn: boolean;
87
+ subdomain?: string;
88
+ }
89
+
90
+ /**
91
+ * Builds the branded welcome / help screen as a ready-to-print string: the
92
+ * Mintlify leaf beside an identity block, then grouped examples and commands.
93
+ */
94
+ export function renderWelcome({
95
+ packageName,
96
+ version,
97
+ signedIn,
98
+ subdomain,
99
+ }: WelcomeOptions): string {
100
+ const title = `${packageName.charAt(0).toUpperCase()}${packageName.slice(1)} CLI`;
101
+ // Only prefix "v" for real semver versions; the linked-dev build reports a
102
+ // descriptive string ("linked to local package").
103
+ const versionLabel = version ? (/^\d/.test(version) ? `v${version}` : version) : undefined;
104
+
105
+ const identity: string[] = [
106
+ chalk.bold(title) + (versionLabel ? chalk.dim(` ${versionLabel}`) : ''),
107
+ '',
108
+ chalk.dim(TAGLINE),
109
+ signedIn
110
+ ? chalk.dim('Signed in to Mintlify')
111
+ : chalk.dim('Run ') + mint(`${packageName} login`) + chalk.dim(' to sign in'),
112
+ ...(subdomain ? [chalk.dim(`Project ${subdomain}`)] : []),
113
+ ];
114
+
115
+ const lines: string[] = [''];
116
+
117
+ // Hero: logo on the left, identity block on the right.
118
+ MINT_LOGO.forEach((logoLine, index) => {
119
+ const identityLine = identity[index - META_TOP_ROW];
120
+ let row = ' ' + mint(logoLine);
121
+ if (identityLine) {
122
+ row += ' '.repeat(MINT_LOGO_WIDTH - logoLine.length + META_GAP) + identityLine;
123
+ }
124
+ lines.push(row);
125
+ });
126
+
127
+ // Examples.
128
+ const exampleWidth =
129
+ EXAMPLES.reduce((max, example) => Math.max(max, `${packageName} ${example.name}`.length), 0) +
130
+ 2;
131
+ lines.push('', ' ' + chalk.bold('EXAMPLES'));
132
+ for (const example of EXAMPLES) {
133
+ lines.push(commandRow(`${packageName} ${example.name}`, example.description, exampleWidth, 3));
134
+ }
135
+
136
+ // Commands, grouped.
137
+ lines.push('', ' ' + chalk.bold('COMMANDS'));
138
+ for (const group of GROUPS) {
139
+ lines.push('', ' ' + chalk.bold(group.title));
140
+ for (const command of group.commands) {
141
+ lines.push(commandRow(command.name, command.description, COMMAND_NAME_WIDTH, 5));
142
+ }
143
+ }
144
+
145
+ lines.push(
146
+ '',
147
+ ' ' +
148
+ chalk.dim('Run ') +
149
+ mint(`${packageName} <command> --help`) +
150
+ chalk.dim(' for details on any command.'),
151
+ ''
152
+ );
153
+
154
+ return lines.join('\n') + '\n';
155
+ }