@mintlify/cli 4.0.1655 → 4.0.1656

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 (54) hide show
  1. package/bin/cli.js +1 -7
  2. package/bin/tsconfig.build.tsbuildinfo +1 -1
  3. package/package.json +2 -6
  4. package/src/cli.tsx +1 -12
  5. package/__test__/mintTestFilePreview.test.ts +0 -35
  6. package/__test__/mintTestOutput.test.ts +0 -256
  7. package/__test__/runHistory.test.ts +0 -240
  8. package/bin/agent-harness/agentPreflight.js +0 -24
  9. package/bin/agent-harness/buildTaskPrompt.js +0 -86
  10. package/bin/agent-harness/countReport.js +0 -10
  11. package/bin/agent-harness/discoverCodeBlocks.js +0 -101
  12. package/bin/agent-harness/executeCodeBlocks.js +0 -263
  13. package/bin/agent-harness/filterSelectedPages.js +0 -10
  14. package/bin/agent-harness/generateTestCode.js +0 -337
  15. package/bin/agent-harness/index.js +0 -95
  16. package/bin/agent-harness/loadAgentSdk.js +0 -46
  17. package/bin/agent-harness/manifest.js +0 -113
  18. package/bin/agent-harness/mapWithConcurrency.js +0 -26
  19. package/bin/agent-harness/runHistory.js +0 -256
  20. package/bin/agent-harness/runSave.js +0 -47
  21. package/bin/agent-harness/setupFolders.js +0 -73
  22. package/bin/agent-harness/tasks/checkTestability.js +0 -228
  23. package/bin/agent-harness/types.js +0 -66
  24. package/bin/mintTest.js +0 -122
  25. package/bin/mintTestFilePreview.js +0 -39
  26. package/bin/mintTestNav.js +0 -35
  27. package/bin/mintTestTelemetry.js +0 -63
  28. package/bin/mintTestText.js +0 -31
  29. package/bin/mintTestTree.js +0 -94
  30. package/bin/mintTestUi.js +0 -818
  31. package/src/agent-harness/MINT_TEST_SYSTEM_DESIGN.md +0 -64
  32. package/src/agent-harness/agentPreflight.ts +0 -13
  33. package/src/agent-harness/buildTaskPrompt.ts +0 -93
  34. package/src/agent-harness/countReport.ts +0 -17
  35. package/src/agent-harness/discoverCodeBlocks.ts +0 -90
  36. package/src/agent-harness/executeCodeBlocks.ts +0 -351
  37. package/src/agent-harness/filterSelectedPages.ts +0 -14
  38. package/src/agent-harness/generateTestCode.ts +0 -396
  39. package/src/agent-harness/index.ts +0 -127
  40. package/src/agent-harness/loadAgentSdk.ts +0 -35
  41. package/src/agent-harness/manifest.ts +0 -116
  42. package/src/agent-harness/mapWithConcurrency.ts +0 -21
  43. package/src/agent-harness/runHistory.ts +0 -255
  44. package/src/agent-harness/runSave.ts +0 -38
  45. package/src/agent-harness/setupFolders.ts +0 -87
  46. package/src/agent-harness/tasks/checkTestability.ts +0 -256
  47. package/src/agent-harness/types.ts +0 -239
  48. package/src/mintTest.tsx +0 -138
  49. package/src/mintTestFilePreview.ts +0 -32
  50. package/src/mintTestNav.ts +0 -31
  51. package/src/mintTestTelemetry.ts +0 -101
  52. package/src/mintTestText.ts +0 -35
  53. package/src/mintTestTree.ts +0 -134
  54. package/src/mintTestUi.tsx +0 -1378
package/bin/mintTest.js DELETED
@@ -1,122 +0,0 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- import path from 'node:path';
11
- import { DEFAULT_COMMAND_TIMEOUT_MS, DEFAULT_CONCURRENCY, clearRunSave, countReport, discoverCodeBlocks, loadLatestTestRun, loadRunSave, runCodeTests, } from './agent-harness/index.js';
12
- import { CMD_EXEC_PATH, findDocsRoot, isAI, terminate } from './helpers.js';
13
- import { getAccessToken } from './keyring.js';
14
- import { fileInNavPaths, loadDocsNavScope } from './mintTestNav.js';
15
- import { trackMintTestGenerationResult, trackMintTestRunCompleted, trackMintTestRunStarted, } from './mintTestTelemetry.js';
16
- import { firstLine, taskProblemLabel } from './mintTestText.js';
17
- import { formatDuration, runMintTestUi } from './mintTestUi.js';
18
- export function mintTestHandler() {
19
- return __awaiter(this, void 0, void 0, function* () {
20
- var _a, _b, _c, _d;
21
- try {
22
- const accessToken = yield getAccessToken().catch(() => null);
23
- if (!accessToken) {
24
- process.stderr.write('not logged in. Run `mint login` to authenticate.\n');
25
- yield terminate(1);
26
- return;
27
- }
28
- const targetPath = yield findDocsRoot(CMD_EXEC_PATH);
29
- const interactive = process.stdin.isTTY === true &&
30
- process.stdout.isTTY === true &&
31
- !isAI() &&
32
- !process.env.CI &&
33
- process.env.CLI_TEST_MODE !== 'true';
34
- const { docsRoot, pages: discoveredPages } = yield discoverCodeBlocks(targetPath);
35
- const scope = yield loadDocsNavScope(docsRoot);
36
- const pages = scope
37
- ? discoveredPages.filter((page) => fileInNavPaths(page.file, scope.navPaths))
38
- : discoveredPages;
39
- const autoSelectedFiles = (scope ? pages.filter((page) => fileInNavPaths(page.file, scope.autoSelectedPaths)) : pages).map((page) => page.file);
40
- const totalPages = scope ? scope.navPaths.size : pages.length;
41
- const outputDirectory = path.join(docsRoot, 'tests', 'mint-test');
42
- let save = yield loadRunSave(outputDirectory);
43
- if (save) {
44
- const known = new Set(pages.map((page) => page.file));
45
- const selectedFiles = save.selectedFiles.filter((file) => known.has(file));
46
- if (selectedFiles.length === 0) {
47
- yield clearRunSave(outputDirectory);
48
- save = null;
49
- }
50
- else {
51
- save = Object.assign(Object.assign({}, save), { selectedFiles });
52
- }
53
- }
54
- const lastReport = yield loadLatestTestRun(docsRoot);
55
- if (interactive) {
56
- const exitCode = yield runMintTestUi({
57
- targetPath,
58
- docsRoot,
59
- pages,
60
- autoSelectedFiles,
61
- totalPages,
62
- save,
63
- lastReport,
64
- });
65
- yield terminate(exitCode);
66
- return;
67
- }
68
- const agents = (_a = save === null || save === void 0 ? void 0 : save.agents) !== null && _a !== void 0 ? _a : ['claude'];
69
- const model = (_b = save === null || save === void 0 ? void 0 : save.model) !== null && _b !== void 0 ? _b : 'claude-opus-4-8';
70
- const selectedFiles = (_c = save === null || save === void 0 ? void 0 : save.selectedFiles) !== null && _c !== void 0 ? _c : autoSelectedFiles;
71
- const resumed = save !== null;
72
- if (save)
73
- process.stdout.write(`resuming run ${save.runId}\n`);
74
- void trackMintTestRunStarted({
75
- mode: 'non_interactive',
76
- resumed,
77
- agents,
78
- model,
79
- selectedPageCount: new Set(selectedFiles).size,
80
- totalPageCount: totalPages,
81
- });
82
- const report = yield runCodeTests({
83
- path: targetPath,
84
- agents,
85
- model,
86
- selectedFiles,
87
- outputDirectory,
88
- runId: (_d = save === null || save === void 0 ? void 0 : save.runId) !== null && _d !== void 0 ? _d : null,
89
- concurrency: DEFAULT_CONCURRENCY,
90
- commandTimeoutMs: DEFAULT_COMMAND_TIMEOUT_MS,
91
- signal: new AbortController().signal,
92
- onTaskUpdate: () => { },
93
- onGenerationResult: (result) => {
94
- void trackMintTestGenerationResult(Object.assign(Object.assign({}, result), { mode: 'non_interactive', resumed }));
95
- },
96
- });
97
- yield trackMintTestRunCompleted(report, {
98
- mode: 'non_interactive',
99
- resumed,
100
- totalPageCount: totalPages,
101
- });
102
- const counts = countReport(report.tasks);
103
- process.stdout.write(`mint test ${report.status}: ${counts.passed} passed, ${counts.failed} failed, ${counts.agentErrors} agent errors\n`);
104
- process.stdout.write(`finished in ${formatDuration(report.durationMs)}\n`);
105
- process.stdout.write(`report: ${report.reportPath}\n`);
106
- for (const task of report.tasks) {
107
- if (task.status !== 'failed' && task.status !== 'agent_error')
108
- continue;
109
- const reason = firstLine(task.error);
110
- process.stdout.write(` ✗ ${task.file}: ${taskProblemLabel(task.status)}${reason ? ` (${reason})` : ''}\n`);
111
- process.stdout.write(` output: ${task.directory}\n`);
112
- }
113
- if (report.historyError)
114
- process.stderr.write(`warning: ${report.historyError}\n`);
115
- yield terminate(report.status === 'passed' ? 0 : 1);
116
- }
117
- catch (error) {
118
- process.stderr.write(`mint test failed: ${error instanceof Error ? error.message : 'unknown error'}\n`);
119
- yield terminate(1);
120
- }
121
- });
122
- }
@@ -1,39 +0,0 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- import fs from 'node:fs/promises';
11
- export const PREVIEW_BYTE_LIMIT = 64 * 1024;
12
- export function readFilePreview(filePath) {
13
- return __awaiter(this, void 0, void 0, function* () {
14
- const buffer = new Uint8Array(PREVIEW_BYTE_LIMIT + 1);
15
- const file = yield fs.open(filePath, 'r');
16
- let bytesRead = 0;
17
- try {
18
- while (bytesRead < buffer.length) {
19
- const result = yield file.read(buffer, bytesRead, buffer.length - bytesRead, bytesRead);
20
- if (result.bytesRead === 0)
21
- break;
22
- bytesRead += result.bytesRead;
23
- }
24
- }
25
- finally {
26
- yield file.close();
27
- }
28
- const contents = buffer.subarray(0, bytesRead);
29
- if (contents.includes(0))
30
- return { binary: true };
31
- return {
32
- binary: false,
33
- text: new TextDecoder()
34
- .decode(contents.subarray(0, PREVIEW_BYTE_LIMIT))
35
- .replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, '�'),
36
- truncated: bytesRead > PREVIEW_BYTE_LIMIT,
37
- };
38
- });
39
- }
@@ -1,35 +0,0 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- import { getAllPathsInDocsNav } from '@mintlify/common';
11
- import { getConfigObj } from '@mintlify/prebuild';
12
- export function loadDocsNavScope(docsRoot) {
13
- return __awaiter(this, void 0, void 0, function* () {
14
- const config = (yield getConfigObj(docsRoot, 'docs').catch(() => null));
15
- const navigation = config === null || config === void 0 ? void 0 : config.navigation;
16
- if (!navigation)
17
- return null;
18
- const navPaths = new Set(getAllPathsInDocsNav(navigation));
19
- if (navPaths.size === 0)
20
- return null;
21
- const firstLanguage = 'languages' in navigation ? navigation.languages[0] : null;
22
- const autoSelectedPaths = firstLanguage ? new Set(getAllPathsInDocsNav(firstLanguage)) : navPaths;
23
- return { navPaths, autoSelectedPaths };
24
- });
25
- }
26
- export function navCandidatesForFile(file) {
27
- const base = file.replace(/\.mdx?$/i, '');
28
- const candidates = [base];
29
- if (base.endsWith('/index'))
30
- candidates.push(base.slice(0, -'/index'.length));
31
- return candidates;
32
- }
33
- export function fileInNavPaths(file, paths) {
34
- return navCandidatesForFile(file).some((candidate) => paths.has(candidate));
35
- }
@@ -1,63 +0,0 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- import { countReport, } from './agent-harness/index.js';
11
- import { trackEvent } from './telemetry/track.js';
12
- export function trackMintTestRunStarted(_a) {
13
- return __awaiter(this, arguments, void 0, function* ({ mode, resumed, agents, model, selectedPageCount, totalPageCount, }) {
14
- yield trackEvent('cli.test.started', {
15
- mode,
16
- resumed,
17
- agents,
18
- model,
19
- selected_page_count: selectedPageCount,
20
- total_page_count: totalPageCount,
21
- });
22
- });
23
- }
24
- export function trackMintTestRunCompleted(report_1, _a) {
25
- return __awaiter(this, arguments, void 0, function* (report, { mode, resumed, totalPageCount }) {
26
- const counts = countReport(report.tasks);
27
- const skippedCheckCount = report.tasks.filter((task) => !task.testable && task.status === 'passed').length;
28
- const tokenCount = report.tasks.reduce((total, task) => total + task.tokens, 0);
29
- const runnableCheckCount = report.tasks.filter((task) => task.testable).length;
30
- yield trackEvent('cli.test.completed', {
31
- mode,
32
- resumed,
33
- status: report.status,
34
- agents: report.agents,
35
- model: report.model,
36
- selected_page_count: new Set(report.selectedFiles).size,
37
- total_page_count: totalPageCount,
38
- total_check_count: report.tasks.length,
39
- runnable_check_count: runnableCheckCount,
40
- passed_check_count: counts.passed - skippedCheckCount,
41
- failed_check_count: counts.failed,
42
- skipped_check_count: skippedCheckCount,
43
- agent_error_check_count: counts.agentErrors,
44
- cancelled_check_count: counts.cancelled,
45
- token_count: tokenCount,
46
- duration_ms: report.durationMs,
47
- });
48
- });
49
- }
50
- export function trackMintTestGenerationResult(_a) {
51
- return __awaiter(this, arguments, void 0, function* ({ status, mode, resumed, agent, model, attemptCount, tokenCount, durationMs, generatedFileCount, }) {
52
- yield trackEvent(`cli.test.generation.${status}`, {
53
- mode,
54
- resumed,
55
- agent,
56
- model,
57
- attempt_count: attemptCount,
58
- token_count: tokenCount,
59
- duration_ms: durationMs,
60
- generated_file_count: generatedFileCount,
61
- });
62
- });
63
- }
@@ -1,31 +0,0 @@
1
- /**
2
- * Split a footer hint such as `↑/↓/j/k move · Enter exit` into key/action pairs so the key can
3
- * be rendered bold and the action plain.
4
- */
5
- export function footerSegments(text) {
6
- return text
7
- .split(' · ')
8
- .map((segment) => segment.trim())
9
- .filter((segment) => segment.length > 0)
10
- .map((segment) => {
11
- const split = segment.indexOf(' ');
12
- if (split === -1)
13
- return { key: segment, action: '' };
14
- return { key: segment.slice(0, split), action: segment.slice(split + 1).trim() };
15
- });
16
- }
17
- /** First non-empty line of a multi-line message, trimmed to fit a terminal row. */
18
- export function firstLine(text, maxLength = 160) {
19
- if (!text)
20
- return '';
21
- const line = text
22
- .split(/\r?\n/)
23
- .map((candidate) => candidate.trim())
24
- .find((candidate) => candidate.length > 0);
25
- if (!line)
26
- return '';
27
- return line.length > maxLength ? `${line.slice(0, maxLength - 1)}…` : line;
28
- }
29
- export function taskProblemLabel(status) {
30
- return status === 'agent_error' ? 'test could not be generated' : 'tests failed';
31
- }
@@ -1,94 +0,0 @@
1
- function buildTree(pages) {
2
- var _a;
3
- const root = { id: 'dir:', kind: 'directory', label: '.', blockCount: 0, children: [] };
4
- const directories = new Map([['', root]]);
5
- for (const page of pages) {
6
- const parts = page.file.split('/');
7
- let parent = root;
8
- let directoryPath = '';
9
- for (const part of parts.slice(0, -1)) {
10
- directoryPath = directoryPath ? `${directoryPath}/${part}` : part;
11
- let directory = directories.get(directoryPath);
12
- if (!directory) {
13
- directory = {
14
- id: `dir:${directoryPath}`,
15
- kind: 'directory',
16
- label: part,
17
- blockCount: 0,
18
- children: [],
19
- };
20
- directories.set(directoryPath, directory);
21
- parent.children.push(directory);
22
- }
23
- parent = directory;
24
- }
25
- parent.children.push({
26
- id: `file:${page.file}`,
27
- kind: 'file',
28
- label: (_a = parts[parts.length - 1]) !== null && _a !== void 0 ? _a : page.file,
29
- file: page.file,
30
- blockCount: page.blockCount,
31
- children: [],
32
- });
33
- }
34
- const sumBlocks = (node) => {
35
- if (node.kind === 'file')
36
- return node.blockCount;
37
- node.blockCount = node.children.reduce((total, child) => total + sumBlocks(child), 0);
38
- return node.blockCount;
39
- };
40
- sumBlocks(root);
41
- const sortChildren = (node) => {
42
- node.children.sort((left, right) => {
43
- if (left.kind !== right.kind)
44
- return left.kind === 'directory' ? -1 : 1;
45
- return left.label.localeCompare(right.label);
46
- });
47
- for (const child of node.children)
48
- sortChildren(child);
49
- };
50
- sortChildren(root);
51
- return root;
52
- }
53
- function descendantFiles(node) {
54
- if (node.kind === 'file')
55
- return node.file ? [node.file] : [];
56
- return node.children.flatMap(descendantFiles);
57
- }
58
- function selectionState(files, selectedFiles) {
59
- if (files.length === 0)
60
- return 'none';
61
- const selectedCount = files.filter((file) => selectedFiles.has(file)).length;
62
- if (selectedCount === 0)
63
- return 'none';
64
- return selectedCount === files.length ? 'all' : 'some';
65
- }
66
- export function visibleScopeRows(pages, expandedDirectories, selectedFiles) {
67
- const root = buildTree(pages);
68
- const rows = [];
69
- const visit = (node, depth) => {
70
- const files = descendantFiles(node);
71
- const expanded = node.kind === 'directory' ? expandedDirectories.has(node.id) : undefined;
72
- rows.push(Object.assign(Object.assign(Object.assign({ id: node.id, kind: node.kind, label: node.label, depth }, (node.file ? { file: node.file } : {})), { blockCount: node.blockCount, selectableFiles: files, selection: selectionState(files, selectedFiles) }), (expanded !== undefined ? { expanded } : {})));
73
- if (node.kind === 'directory' && expanded) {
74
- for (const child of node.children)
75
- visit(child, depth + 1);
76
- }
77
- };
78
- for (const child of root.children)
79
- visit(child, 0);
80
- return rows;
81
- }
82
- export function toggleScopeSelection(selectedFiles, row) {
83
- const next = new Set(selectedFiles);
84
- if (row.selectableFiles.length === 0)
85
- return next;
86
- const shouldSelect = row.selectableFiles.some((file) => !next.has(file));
87
- for (const file of row.selectableFiles) {
88
- if (shouldSelect)
89
- next.add(file);
90
- else
91
- next.delete(file);
92
- }
93
- return next;
94
- }