@mintlify/cli 4.0.1466 → 4.0.1468
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/__test__/mintTestFilePreview.test.ts +35 -0
- package/__test__/mintTestOutput.test.ts +194 -0
- package/__test__/runHistory.test.ts +164 -0
- package/bin/agent-harness/agentPreflight.js +24 -0
- package/bin/agent-harness/buildTaskPrompt.js +17 -7
- package/bin/agent-harness/countReport.js +10 -0
- package/bin/agent-harness/executeCodeBlocks.js +53 -10
- package/bin/agent-harness/generateTestCode.js +58 -43
- package/bin/agent-harness/index.js +14 -5
- package/bin/agent-harness/manifest.js +113 -0
- package/bin/agent-harness/runHistory.js +252 -0
- package/bin/agent-harness/setupFolders.js +20 -6
- package/bin/agent-harness/tasks/checkTestability.js +7 -5
- package/bin/agent-harness/types.js +33 -2
- package/bin/constants.js +3 -3
- package/bin/mintTest.js +14 -12
- package/bin/mintTestFilePreview.js +39 -0
- package/bin/mintTestText.js +31 -0
- package/bin/mintTestUi.js +295 -105
- package/bin/status.js +26 -0
- package/bin/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/agent-harness/MINT_TEST_SYSTEM_DESIGN.md +15 -9
- package/src/agent-harness/agentPreflight.ts +13 -0
- package/src/agent-harness/buildTaskPrompt.ts +18 -7
- package/src/agent-harness/countReport.ts +17 -0
- package/src/agent-harness/executeCodeBlocks.ts +67 -1
- package/src/agent-harness/generateTestCode.ts +81 -51
- package/src/agent-harness/index.ts +20 -5
- package/src/agent-harness/manifest.ts +116 -0
- package/src/agent-harness/runHistory.ts +250 -0
- package/src/agent-harness/setupFolders.ts +19 -5
- package/src/agent-harness/tasks/checkTestability.ts +15 -6
- package/src/agent-harness/types.ts +81 -5
- package/src/constants.ts +6 -2
- package/src/mintTest.tsx +15 -11
- package/src/mintTestFilePreview.ts +32 -0
- package/src/mintTestText.ts +35 -0
- package/src/mintTestUi.tsx +586 -224
- package/src/status.tsx +24 -0
package/bin/mintTestUi.js
CHANGED
|
@@ -7,27 +7,19 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
11
11
|
import { Box, render, Text, useInput } from 'ink';
|
|
12
12
|
import path from 'node:path';
|
|
13
13
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
14
14
|
import { DEFAULT_COMMAND_TIMEOUT_MS, DEFAULT_CONCURRENCY, clearRunSave, runCodeTests, } from './agent-harness/index.js';
|
|
15
|
+
import { readFilePreview } from './mintTestFilePreview.js';
|
|
16
|
+
import { firstLine, footerSegments, taskProblemLabel } from './mintTestText.js';
|
|
15
17
|
import { toggleScopeSelection, visibleScopeRows } from './mintTestTree.js';
|
|
16
18
|
const HARNESSES = [
|
|
17
19
|
{ agent: 'claude', model: 'claude-opus-4-8', label: 'Claude Opus 4.8' },
|
|
18
20
|
{ agent: 'claude', model: 'claude-sonnet-4-6', label: 'Claude Sonnet 4.6' },
|
|
19
21
|
{ agent: 'codex', model: 'claude-opus-4-8', label: 'Codex' },
|
|
20
22
|
];
|
|
21
|
-
export function countReport(tasks) {
|
|
22
|
-
const counts = { passed: 0, failed: 0, cancelled: 0, agentErrors: 0 };
|
|
23
|
-
for (const task of tasks) {
|
|
24
|
-
if (task.status === 'agent_error')
|
|
25
|
-
counts.agentErrors++;
|
|
26
|
-
else
|
|
27
|
-
counts[task.status]++;
|
|
28
|
-
}
|
|
29
|
-
return counts;
|
|
30
|
-
}
|
|
31
23
|
const STEPS = [
|
|
32
24
|
{ id: 'harnesses', label: 'Harnesses' },
|
|
33
25
|
{ id: 'scope', label: 'Scope' },
|
|
@@ -74,7 +66,7 @@ function useTerminalSize() {
|
|
|
74
66
|
}
|
|
75
67
|
function stepState(step, screen, activeRunStage) {
|
|
76
68
|
const order = ['harnesses', 'scope', 'check', 'generate', 'run', 'report'];
|
|
77
|
-
const screenStep = screen === 'resume'
|
|
69
|
+
const screenStep = screen === 'resume' || screen === 'existing'
|
|
78
70
|
? 'harnesses'
|
|
79
71
|
: screen === 'running' || screen === 'error'
|
|
80
72
|
? activeRunStage
|
|
@@ -99,36 +91,38 @@ function StepGlyph({ state }) {
|
|
|
99
91
|
return _jsx(Text, { dimColor: true, children: "\u25CB" });
|
|
100
92
|
}
|
|
101
93
|
const RESUME_OPTIONS = ['Resume', 'Start fresh'];
|
|
94
|
+
const EXISTING_RUN_OPTIONS = [
|
|
95
|
+
'Update tests',
|
|
96
|
+
'Review last test run',
|
|
97
|
+
'Start a brand new test',
|
|
98
|
+
];
|
|
102
99
|
const EMPTY_SELECTION = new Set();
|
|
103
100
|
function ResumeScreen({ save, cursor }) {
|
|
104
101
|
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Resume previous run?" }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { children: ["Run ", _jsx(Text, { color: "cyan", children: save.runId })] }), _jsxs(Text, { dimColor: true, children: [save.selectedFiles.length, " ", save.selectedFiles.length === 1 ? 'page' : 'pages', " \u00B7", ' ', save.agents.join(', ')] })] }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: RESUME_OPTIONS.map((option, index) => (_jsxs(Text, { color: cursor === index ? 'cyan' : undefined, children: [cursor === index ? '›' : ' ', " ", option] }, option))) })] }));
|
|
105
102
|
}
|
|
103
|
+
function ExistingRunScreen({ report, cursor }) {
|
|
104
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Mint test has already run in this directory." }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { children: ["Last run ", _jsx(Text, { color: "cyan", children: report.runId }), " \u00B7", ' ', _jsx(Text, { color: report.status === 'passed'
|
|
105
|
+
? 'green'
|
|
106
|
+
: report.status === 'completed'
|
|
107
|
+
? 'cyan'
|
|
108
|
+
: report.status === 'cancelled'
|
|
109
|
+
? 'yellow'
|
|
110
|
+
: 'red', children: report.status })] }), _jsxs(Text, { dimColor: true, children: [report.selectedFiles.length, " ", report.selectedFiles.length === 1 ? 'page' : 'pages', " \u00B7", ' ', report.model, " \u00B7 ", new Date(report.completedAt).toLocaleString()] })] }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: EXISTING_RUN_OPTIONS.map((option, index) => (_jsxs(Text, { color: cursor === index ? 'cyan' : undefined, children: [cursor === index ? '›' : ' ', " ", option] }, option))) })] }));
|
|
111
|
+
}
|
|
106
112
|
function HarnessScreen({ cursor }) {
|
|
107
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "
|
|
113
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Select coding harness?" }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: HARNESSES.map((harness, index) => (_jsxs(Text, { color: cursor === index ? 'cyan' : undefined, children: [cursor === index ? '›' : ' ', " ", cursor === index ? '(•)' : '( )', " ", harness.label] }, harness.label))) })] }));
|
|
108
114
|
}
|
|
109
|
-
function ScopeScreen({ rows, cursor, height,
|
|
115
|
+
function ScopeScreen({ rows, cursor, height, }) {
|
|
110
116
|
const viewport = Math.max(4, height - 18);
|
|
111
117
|
const start = Math.max(0, Math.min(cursor - Math.floor(viewport / 2), rows.length - viewport));
|
|
112
118
|
const visible = rows.slice(start, start + viewport);
|
|
113
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, {
|
|
119
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginTop: 1, marginRight: 2, flexDirection: "column", children: _jsx(Text, { bold: true, children: "Select pages to test" }) }), _jsx(Text, { bold: true, color: "green", children: "enter to confirm" })] }), _jsx(Text, { dimColor: true, children: "Folders select or clear every testable page beneath them." }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: visible.map((row, visibleIndex) => {
|
|
114
120
|
const index = start + visibleIndex;
|
|
115
121
|
const selection = row.selection === 'all' ? '[x]' : row.selection === 'some' ? '[-]' : '[ ]';
|
|
116
122
|
const disclosure = row.kind === 'directory' ? (row.expanded ? '▾' : '▸') : ' ';
|
|
117
123
|
return (_jsxs(Text, { color: index === cursor ? 'cyan' : undefined, children: [' '.repeat(row.depth * 2), index === cursor ? '›' : ' ', " ", selection, " ", disclosure, " ", row.label, ' ', _jsxs(Text, { dimColor: true, children: [row.blockCount, " ", row.blockCount === 1 ? 'code block' : 'code blocks'] })] }, row.id));
|
|
118
124
|
}) })] }));
|
|
119
125
|
}
|
|
120
|
-
const PHASE_LABELS = {
|
|
121
|
-
checking: 'validating code can be tested',
|
|
122
|
-
checked: 'code can be tested',
|
|
123
|
-
not_testable: 'no testable code',
|
|
124
|
-
generating: 'generating test code',
|
|
125
|
-
generated: 'test generated',
|
|
126
|
-
running: 'running tests',
|
|
127
|
-
passed: 'passed',
|
|
128
|
-
failed: 'tests failed',
|
|
129
|
-
agent_error: 'test could not be generated',
|
|
130
|
-
cancelled: 'cancelled',
|
|
131
|
-
};
|
|
132
126
|
const ACTIVE_PHASES = new Set(['checking', 'generating', 'running']);
|
|
133
127
|
const DONE_PHASES = new Set([
|
|
134
128
|
'passed',
|
|
@@ -138,13 +132,120 @@ const DONE_PHASES = new Set([
|
|
|
138
132
|
'cancelled',
|
|
139
133
|
]);
|
|
140
134
|
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
135
|
+
const OUTPUT_PHASES = new Set([
|
|
136
|
+
'generating',
|
|
137
|
+
'generated',
|
|
138
|
+
'running',
|
|
139
|
+
'passed',
|
|
140
|
+
'failed',
|
|
141
|
+
'agent_error',
|
|
142
|
+
'cancelled',
|
|
143
|
+
]);
|
|
144
|
+
function generatedPathRows(tasks) {
|
|
145
|
+
const rows = [];
|
|
146
|
+
for (const task of tasks) {
|
|
147
|
+
if (!OUTPUT_PHASES.has(task.phase))
|
|
148
|
+
continue;
|
|
149
|
+
if (task.generatedFiles.length === 0) {
|
|
150
|
+
rows.push({
|
|
151
|
+
id: `${task.id}:directory`,
|
|
152
|
+
path: task.directory,
|
|
153
|
+
previewable: false,
|
|
154
|
+
task,
|
|
155
|
+
});
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
for (const file of task.generatedFiles) {
|
|
159
|
+
rows.push({ id: `${task.id}:${file}`, path: file, previewable: true, task });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return rows.sort((left, right) => left.path.localeCompare(right.path));
|
|
163
|
+
}
|
|
164
|
+
function useFilePreview(row) {
|
|
165
|
+
const [preview, setPreview] = useState({ status: 'empty' });
|
|
166
|
+
useEffect(() => {
|
|
167
|
+
if (!(row === null || row === void 0 ? void 0 : row.previewable)) {
|
|
168
|
+
setPreview({ status: 'empty' });
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
let active = true;
|
|
172
|
+
setPreview({ status: 'loading' });
|
|
173
|
+
void readFilePreview(row.path)
|
|
174
|
+
.then((result) => {
|
|
175
|
+
if (!active)
|
|
176
|
+
return;
|
|
177
|
+
if (result.binary) {
|
|
178
|
+
setPreview({ status: 'error', message: 'Binary files cannot be previewed.' });
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
setPreview({
|
|
182
|
+
status: 'loaded',
|
|
183
|
+
lines: result.text.split(/\r?\n/),
|
|
184
|
+
truncated: result.truncated,
|
|
185
|
+
});
|
|
186
|
+
})
|
|
187
|
+
.catch((error) => {
|
|
188
|
+
if (!active)
|
|
189
|
+
return;
|
|
190
|
+
setPreview({
|
|
191
|
+
status: 'error',
|
|
192
|
+
message: error instanceof Error ? error.message : String(error),
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
return () => {
|
|
196
|
+
active = false;
|
|
197
|
+
};
|
|
198
|
+
}, [row === null || row === void 0 ? void 0 : row.path, row === null || row === void 0 ? void 0 : row.previewable]);
|
|
199
|
+
return preview;
|
|
200
|
+
}
|
|
201
|
+
function generatedPathStatus(phase, spinner) {
|
|
202
|
+
if (phase === 'generating' || phase === 'running')
|
|
203
|
+
return { glyph: spinner, color: 'green' };
|
|
204
|
+
if (phase === 'generated' || phase === 'passed')
|
|
205
|
+
return { glyph: '✓', color: 'green' };
|
|
206
|
+
if (phase === 'failed' || phase === 'agent_error')
|
|
207
|
+
return { glyph: '✗', color: 'red' };
|
|
208
|
+
if (phase === 'cancelled')
|
|
209
|
+
return { glyph: '●', color: 'yellow' };
|
|
210
|
+
return { glyph: '○', dim: true };
|
|
211
|
+
}
|
|
212
|
+
function FilePreview({ row, height }) {
|
|
213
|
+
const preview = useFilePreview(row);
|
|
214
|
+
// Border (2), title, path, margin, and one row kept for the truncation notice.
|
|
215
|
+
const lineLimit = Math.max(1, height - 6);
|
|
216
|
+
return (_jsxs(Box, { width: "52%", height: height, borderStyle: "round", borderColor: "gray", paddingX: 1, flexDirection: "column", overflow: "hidden", children: [_jsx(Text, { bold: true, children: "File preview" }), row ? _jsx(Text, { dimColor: true, children: row.path }) : _jsx(Text, { dimColor: true, children: "Select a generated file." }), !(row === null || row === void 0 ? void 0 : row.previewable) ? (_jsx(Text, { dimColor: true, children: (row === null || row === void 0 ? void 0 : row.task.phase) === 'generating'
|
|
217
|
+
? 'Files will appear here after generation finishes.'
|
|
218
|
+
: 'No generated file is available to preview.' })) : preview.status === 'loading' ? (_jsx(Text, { dimColor: true, children: "Loading preview..." })) : preview.status === 'error' ? (_jsx(Text, { color: "yellow", children: preview.message })) : preview.status === 'loaded' ? (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [preview.lines.slice(0, lineLimit).map((line, index) => (_jsxs(Text, { wrap: "truncate-end", children: [_jsxs(Text, { dimColor: true, children: [String(index + 1).padStart(3), " "] }), line || ' '] }, `${index}:${line}`))), preview.truncated || preview.lines.length > lineLimit ? (_jsx(Text, { dimColor: true, children: "Preview truncated" })) : null] })) : null] }));
|
|
219
|
+
}
|
|
220
|
+
function GeneratedFilesView({ rows, cursor, height, spinner, relativeRoot, emptyMessage = 'Waiting for generated files...', }) {
|
|
221
|
+
const safeCursor = Math.max(0, Math.min(cursor, rows.length - 1));
|
|
222
|
+
const viewport = Math.max(2, height - 3);
|
|
223
|
+
const start = Math.max(0, Math.min(safeCursor - Math.floor(viewport / 2), rows.length - viewport));
|
|
224
|
+
const visible = rows.slice(start, start + viewport);
|
|
225
|
+
const selected = rows[safeCursor];
|
|
226
|
+
const showPreview = (selected === null || selected === void 0 ? void 0 : selected.previewable) === true;
|
|
227
|
+
return (_jsxs(Box, { flexDirection: "row", gap: 1, flexGrow: 1, overflow: "hidden", children: [_jsxs(Box, { width: showPreview ? '48%' : '100%', borderStyle: "round", borderColor: "gray", paddingX: 1, flexDirection: "column", overflow: "hidden", children: [_jsx(Text, { bold: true, children: "Generated files" }), visible.length === 0 ? _jsx(Text, { dimColor: true, children: emptyMessage }) : null, visible.map((row, visibleIndex) => {
|
|
228
|
+
const index = start + visibleIndex;
|
|
229
|
+
const status = generatedPathStatus(row.task.phase, spinner);
|
|
230
|
+
const relativePath = path.relative(relativeRoot, row.path) || '.';
|
|
231
|
+
return (_jsxs(Text, { color: index === safeCursor ? 'cyan' : undefined, wrap: "truncate-end", children: [index === safeCursor ? '›' : ' ', ' ', _jsx(Text, { color: status.color, dimColor: status.dim, children: status.glyph }), ' ', row.previewable
|
|
232
|
+
? row.path
|
|
233
|
+
: row.task.phase === 'generating'
|
|
234
|
+
? `Writing under ${relativePath}`
|
|
235
|
+
: `No generated file in ${relativePath}`, row.task.phase === 'failed' || row.task.phase === 'agent_error' ? (_jsxs(Text, { color: "red", children: [' ', taskProblemLabel(row.task.phase), row.task.error ? _jsxs(Text, { dimColor: true, children: [" \u00B7 ", firstLine(row.task.error)] }) : null] })) : null] }, row.id));
|
|
236
|
+
})] }), showPreview ? _jsx(FilePreview, { row: selected, height: height }) : null] }));
|
|
237
|
+
}
|
|
141
238
|
function fileStatus(row, spinner) {
|
|
142
239
|
if (row && ACTIVE_PHASES.has(row.phase))
|
|
143
240
|
return { glyph: spinner, color: 'green' };
|
|
144
|
-
if ((row === null || row === void 0 ? void 0 : row.phase) === 'not_testable')
|
|
145
|
-
return { glyph: '✗', color: 'yellow' };
|
|
146
241
|
if ((row === null || row === void 0 ? void 0 : row.phase) === 'failed' || (row === null || row === void 0 ? void 0 : row.phase) === 'agent_error')
|
|
147
242
|
return { glyph: '✗', color: 'red' };
|
|
243
|
+
if ((row === null || row === void 0 ? void 0 : row.phase) === 'checked' || (row === null || row === void 0 ? void 0 : row.phase) === 'generated' || (row === null || row === void 0 ? void 0 : row.phase) === 'passed') {
|
|
244
|
+
return { glyph: '✓', color: 'green' };
|
|
245
|
+
}
|
|
246
|
+
if ((row === null || row === void 0 ? void 0 : row.phase) === 'not_testable' || (row === null || row === void 0 ? void 0 : row.phase) === 'cancelled') {
|
|
247
|
+
return { glyph: '●', color: 'yellow' };
|
|
248
|
+
}
|
|
148
249
|
return { glyph: '○', dim: true };
|
|
149
250
|
}
|
|
150
251
|
const SETTLED_PHASES = new Set([...DONE_PHASES, 'checked', 'generated']);
|
|
@@ -153,17 +254,21 @@ function directoryStatus(files, phases, spinner) {
|
|
|
153
254
|
if (rows.some((row) => row && ACTIVE_PHASES.has(row.phase))) {
|
|
154
255
|
return { glyph: spinner, color: 'green' };
|
|
155
256
|
}
|
|
257
|
+
if (rows.some((row) => (row === null || row === void 0 ? void 0 : row.phase) === 'failed' || (row === null || row === void 0 ? void 0 : row.phase) === 'agent_error')) {
|
|
258
|
+
return { glyph: '✗', color: 'red' };
|
|
259
|
+
}
|
|
156
260
|
if (rows.every((row) => row && SETTLED_PHASES.has(row.phase))) {
|
|
157
|
-
if (rows.
|
|
158
|
-
return { glyph: '●', color: '
|
|
261
|
+
if (rows.every((row) => (row === null || row === void 0 ? void 0 : row.phase) === 'not_testable' || (row === null || row === void 0 ? void 0 : row.phase) === 'cancelled')) {
|
|
262
|
+
return { glyph: '●', color: 'yellow' };
|
|
159
263
|
}
|
|
160
|
-
return
|
|
161
|
-
? { glyph: '●', color: 'green' }
|
|
162
|
-
: { glyph: '●', dim: true };
|
|
264
|
+
return { glyph: '✓', color: 'green' };
|
|
163
265
|
}
|
|
164
266
|
return { glyph: '○', dim: true };
|
|
165
267
|
}
|
|
166
|
-
function
|
|
268
|
+
function ActivityLegend({ spinner }) {
|
|
269
|
+
return (_jsxs(Text, { dimColor: true, children: ["\u25CB waiting \u00B7 ", _jsx(Text, { color: "green", children: spinner }), " active \u00B7 ", _jsx(Text, { color: "green", children: "\u2713" }), ' ', "complete \u00B7 ", _jsx(Text, { color: "red", children: "\u2717" }), " failed \u00B7 ", _jsx(Text, { color: "yellow", children: "\u25CF" }), " skipped / cancelled"] }));
|
|
270
|
+
}
|
|
271
|
+
function ActivityScreen({ stage, cancelling, harness, docsRoot, rows, generatedRows, cursor, phases, height, totalTasks, spinnerFrame, runStartedAt, now, }) {
|
|
167
272
|
var _a;
|
|
168
273
|
const spinner = (_a = SPINNER_FRAMES[spinnerFrame % SPINNER_FRAMES.length]) !== null && _a !== void 0 ? _a : '⠋';
|
|
169
274
|
const updates = [...phases.values()];
|
|
@@ -187,46 +292,48 @@ function ActivityScreen({ stage, cancelling, harness, rows, cursor, phases, heig
|
|
|
187
292
|
const viewport = Math.max(4, height - 13);
|
|
188
293
|
const start = Math.max(0, Math.min(cursor - Math.floor(viewport / 2), rows.length - viewport));
|
|
189
294
|
const visible = rows.slice(start, start + viewport);
|
|
190
|
-
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsxs(Box, { borderStyle: "round", borderColor: "gray", paddingX: 1, flexDirection: "column", children: [_jsxs(Text, { bold: true, children: [padDuration(now - runStartedAt), " ", _jsxs(Text, { dimColor: true, children: [padTokens(tokens), " tokens"] })] }), _jsxs(Text, { color: "green", children: [_jsx(Text, { children: spinner }), " ", message] }), _jsxs(Text, { dimColor: true, children: ["Harness: ", harness] }), _jsx(Text, { dimColor: true, children: progress })] }), _jsx(Box, { borderStyle: "round", borderColor: "gray", paddingX: 1,
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
295
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsxs(Box, { borderStyle: "round", borderColor: "gray", paddingX: 1, flexDirection: "column", children: [_jsxs(Text, { bold: true, children: [padDuration(now - runStartedAt), " ", _jsxs(Text, { dimColor: true, children: [padTokens(tokens), " tokens"] })] }), _jsxs(Text, { color: "green", children: [_jsx(Text, { children: spinner }), " ", message] }), _jsxs(Text, { dimColor: true, children: ["Harness: ", harness] }), _jsx(Text, { dimColor: true, children: progress }), stage === 'check' || stage === 'generate' ? _jsx(ActivityLegend, { spinner: spinner }) : null] }), _jsx(Box, { marginTop: 1, flexGrow: 1, overflow: "hidden", children: stage === 'check' ? (_jsx(Box, { borderStyle: "round", borderColor: "gray", paddingX: 1, flexDirection: "column", flexGrow: 1, children: visible.map((row, visibleIndex) => {
|
|
296
|
+
var _a;
|
|
297
|
+
const index = start + visibleIndex;
|
|
298
|
+
const disclosure = row.kind === 'directory' ? (row.expanded ? '▾' : '▸') : ' ';
|
|
299
|
+
const status = row.kind === 'directory'
|
|
300
|
+
? directoryStatus(row.selectableFiles, phases, spinner)
|
|
301
|
+
: fileStatus(row.file ? phases.get(row.file) : undefined, spinner);
|
|
302
|
+
const task = row.file ? phases.get(row.file) : undefined;
|
|
303
|
+
const settled = row.kind === 'directory'
|
|
304
|
+
? row.selectableFiles.filter((file) => {
|
|
305
|
+
var _a;
|
|
306
|
+
const phase = (_a = phases.get(file)) === null || _a === void 0 ? void 0 : _a.phase;
|
|
307
|
+
return phase !== undefined && SETTLED_PHASES.has(phase);
|
|
308
|
+
}).length
|
|
309
|
+
: 0;
|
|
310
|
+
return (_jsxs(Text, { color: index === cursor ? 'cyan' : undefined, wrap: "truncate-end", children: [' '.repeat(row.depth * 2), index === cursor ? '›' : ' ', ' ', _jsx(Text, { color: status.color, dimColor: status.dim, children: status.glyph }), ' ', disclosure, " ", row.label, row.kind === 'directory' ? (_jsxs(Text, { dimColor: true, children: [' ', padCount(settled, row.selectableFiles.length), "/", row.selectableFiles.length, ' ', "complete"] })) : null, task ? (_jsxs(Text, { dimColor: true, children: [" ", padDuration(((_a = task.finishedAt) !== null && _a !== void 0 ? _a : now) - task.startedAt)] })) : null, task && (task.phase === 'failed' || task.phase === 'agent_error') ? (_jsxs(Text, { color: "red", children: [' ', taskProblemLabel(task.phase), task.error ? _jsxs(Text, { dimColor: true, children: [" \u00B7 ", firstLine(task.error)] }) : null] })) : null] }, row.id));
|
|
311
|
+
}) })) : (_jsx(GeneratedFilesView, { rows: generatedRows, cursor: cursor, height: Math.max(4, height - 7), spinner: spinner, relativeRoot: docsRoot })) })] }));
|
|
207
312
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
if (
|
|
212
|
-
return
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
if ((row === null || row === void 0 ? void 0 : row.phase) === 'cancelled')
|
|
216
|
-
return { glyph: '●', color: 'yellow' };
|
|
217
|
-
return { glyph: '○', dim: true };
|
|
313
|
+
const MAX_PROBLEM_ROWS = 6;
|
|
314
|
+
function ProblemsView({ report }) {
|
|
315
|
+
const problems = report.tasks.filter((task) => task.status === 'failed' || task.status === 'agent_error');
|
|
316
|
+
if (problems.length === 0)
|
|
317
|
+
return null;
|
|
318
|
+
const hidden = problems.length - MAX_PROBLEM_ROWS;
|
|
319
|
+
return (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: "red", paddingX: 1, flexDirection: "column", flexShrink: 0, children: [_jsxs(Text, { bold: true, color: "red", children: [problems.length, " ", problems.length === 1 ? 'page needs' : 'pages need', " attention"] }), problems.slice(0, MAX_PROBLEM_ROWS).map((task) => (_jsxs(Text, { wrap: "truncate-end", children: [_jsx(Text, { color: "red", children: "\u2717" }), " ", task.file, ' ', _jsx(Text, { dimColor: true, children: taskProblemLabel(task.status) }), task.error ? _jsxs(Text, { dimColor: true, children: [" \u00B7 ", firstLine(task.error)] }) : null] }, task.id))), hidden > 0 ? _jsxs(Text, { dimColor: true, children: ["+", hidden, " more in the report"] }) : null] }));
|
|
218
320
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
return { glyph: '●', color: 'yellow' };
|
|
321
|
+
/**
|
|
322
|
+
* Rows the report header takes, including its border and the margin below it. The optional
|
|
323
|
+
* lines must be counted, or the screen is one row taller than the terminal and Ink drops the
|
|
324
|
+
* heading (it showed as "Finished in 1m 37s passed").
|
|
325
|
+
*/
|
|
326
|
+
function reportHeaderHeight(report, rows) {
|
|
327
|
+
const optionalLines = (report.historyError ? 1 : 0) + (rows.some((row) => row.previewable) ? 1 : 0);
|
|
328
|
+
return 2 + 4 + optionalLines + 1;
|
|
228
329
|
}
|
|
229
|
-
function
|
|
330
|
+
function problemsViewHeight(report) {
|
|
331
|
+
const problems = report.tasks.filter((task) => task.status === 'failed' || task.status === 'agent_error').length;
|
|
332
|
+
if (problems === 0)
|
|
333
|
+
return 0;
|
|
334
|
+
return Math.min(problems, MAX_PROBLEM_ROWS) + 4 + (problems > MAX_PROBLEM_ROWS ? 1 : 0);
|
|
335
|
+
}
|
|
336
|
+
function ReportScreen({ report, rows, cursor, height, }) {
|
|
230
337
|
const statusColor = report.status === 'passed'
|
|
231
338
|
? 'green'
|
|
232
339
|
: report.status === 'completed'
|
|
@@ -239,46 +346,43 @@ function ReportScreen({ report, rows, cursor, phases, height, }) {
|
|
|
239
346
|
: report.status === 'completed'
|
|
240
347
|
? 'Run completed'
|
|
241
348
|
: `Run ${report.status}`;
|
|
242
|
-
|
|
243
|
-
const start = Math.max(0, Math.min(cursor - Math.floor(viewport / 2), rows.length - viewport));
|
|
244
|
-
const visible = rows.slice(start, start + viewport);
|
|
245
|
-
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsxs(Box, { borderStyle: "round", borderColor: "gray", paddingX: 1, flexDirection: "column", children: [_jsx(Text, { bold: true, color: statusColor, children: heading }), _jsxs(Text, { children: ["Finished in ", _jsx(Text, { bold: true, children: formatDuration(report.durationMs) })] }), _jsxs(Text, { dimColor: true, wrap: "truncate-end", children: ["Report at ", report.reportPath] })] }), _jsx(Box, { borderStyle: "round", borderColor: "gray", paddingX: 1, marginTop: 1, flexDirection: "column", flexGrow: 1, children: visible.map((row, visibleIndex) => {
|
|
246
|
-
const index = start + visibleIndex;
|
|
247
|
-
const disclosure = row.kind === 'directory' ? (row.expanded ? '▾' : '▸') : ' ';
|
|
248
|
-
const status = row.kind === 'directory'
|
|
249
|
-
? reportDirectoryStatus(row.selectableFiles, phases)
|
|
250
|
-
: reportFileStatus(row.file ? phases.get(row.file) : undefined);
|
|
251
|
-
const task = row.file ? phases.get(row.file) : undefined;
|
|
252
|
-
const passed = row.kind === 'directory'
|
|
253
|
-
? row.selectableFiles.filter((file) => { var _a; return ((_a = phases.get(file)) === null || _a === void 0 ? void 0 : _a.phase) === 'passed'; }).length
|
|
254
|
-
: 0;
|
|
255
|
-
return (_jsxs(Text, { color: index === cursor ? 'cyan' : undefined, wrap: "truncate-end", children: [' '.repeat(row.depth * 2), index === cursor ? '›' : ' ', ' ', _jsx(Text, { color: status.color, dimColor: status.dim, children: status.glyph }), ' ', disclosure, " ", row.label, row.kind === 'directory' ? (_jsxs(Text, { dimColor: true, children: [' ', padCount(passed, row.selectableFiles.length), "/", row.selectableFiles.length, " passed"] })) : task ? (_jsxs(Text, { dimColor: true, children: [' ', PHASE_LABELS[task.phase], task.finishedAt ? ` · ${formatDuration(task.finishedAt - task.startedAt)}` : ''] })) : null] }, row.id));
|
|
256
|
-
}) })] }));
|
|
349
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsxs(Box, { borderStyle: "round", borderColor: "gray", paddingX: 1, flexDirection: "column", flexShrink: 0, children: [_jsx(Text, { bold: true, color: statusColor, children: heading }), _jsxs(Text, { children: ["Finished in ", _jsx(Text, { bold: true, children: formatDuration(report.durationMs) })] }), _jsxs(Text, { dimColor: true, wrap: "truncate-end", children: ["Report at ", report.reportPath] }), _jsxs(Text, { dimColor: true, wrap: "truncate-end", children: ["Generated files at ", report.outputDirectory] }), report.historyError ? (_jsxs(Text, { color: "yellow", wrap: "truncate-end", children: ["Warning: ", report.historyError] })) : null, rows.some((row) => row.previewable) ? (_jsx(Text, { children: "Select a file below to preview its contents." })) : null] }), _jsx(ProblemsView, { report: report }), _jsx(Box, { marginTop: 1, flexGrow: 1, overflow: "hidden", children: _jsx(GeneratedFilesView, { rows: rows, cursor: cursor, height: Math.max(4, height - reportHeaderHeight(report, rows) - problemsViewHeight(report)), spinner: "\u25CF", relativeRoot: report.docsRoot, emptyMessage: "No generated files." }) })] }));
|
|
257
350
|
}
|
|
258
|
-
function
|
|
259
|
-
|
|
260
|
-
|
|
351
|
+
function FooterHints({ text }) {
|
|
352
|
+
return (_jsx(_Fragment, { children: footerSegments(text).map((segment, index) => (_jsxs(Text, { children: [index > 0 ? ' · ' : '', _jsx(Text, { bold: true, children: segment.key }), segment.action ? ` ${segment.action}` : ''] }, `${index}:${segment.key}`))) }));
|
|
353
|
+
}
|
|
354
|
+
function footerForScreen(screen, running, cancelled, activeRunStage, reviewingSavedRun) {
|
|
355
|
+
if (running) {
|
|
356
|
+
return activeRunStage === 'check'
|
|
357
|
+
? '↑/↓/j/k move · ←/→/h/l collapse/expand · Ctrl-C cancel'
|
|
358
|
+
: '↑/↓/j/k preview generated files · Ctrl-C cancel';
|
|
359
|
+
}
|
|
261
360
|
if (screen === 'resume')
|
|
262
361
|
return '↑/↓/j/k move · Enter confirm · Ctrl-C cancel';
|
|
362
|
+
if (screen === 'existing')
|
|
363
|
+
return '↑/↓/j/k move · Enter confirm · Ctrl-C cancel';
|
|
263
364
|
if (screen === 'harnesses')
|
|
264
365
|
return '↑/↓/j/k move · Enter continue · Ctrl-C cancel';
|
|
265
366
|
if (screen === 'scope') {
|
|
266
367
|
return '↑/↓/j/k move · ←/→/h/l collapse/expand · Space select · a all · Enter start';
|
|
267
368
|
}
|
|
268
369
|
if (screen === 'report') {
|
|
370
|
+
if (reviewingSavedRun)
|
|
371
|
+
return '↑/↓/j/k preview generated files · Enter exit';
|
|
269
372
|
return cancelled
|
|
270
|
-
? '↑/↓/j/k
|
|
271
|
-
: '↑/↓/j/k
|
|
373
|
+
? '↑/↓/j/k preview generated files · Ctrl-C exit · save kept for resume'
|
|
374
|
+
: '↑/↓/j/k preview generated files · Enter exit';
|
|
272
375
|
}
|
|
273
376
|
return 'Enter exit';
|
|
274
377
|
}
|
|
275
|
-
function MintTestApp({ targetPath, docsRoot, pages, autoSelectedFiles,
|
|
378
|
+
function MintTestApp({ targetPath, docsRoot, pages, autoSelectedFiles, save, lastReport, onFinish, }) {
|
|
276
379
|
var _a;
|
|
277
380
|
const size = useTerminalSize();
|
|
278
381
|
const files = useMemo(() => pages.map((page) => page.file), [pages]);
|
|
279
382
|
const outputDirectory = path.join(docsRoot, 'tests', 'mint-test');
|
|
280
|
-
const [screen, setScreen] = useState(save ? 'resume' : 'harnesses');
|
|
383
|
+
const [screen, setScreen] = useState(save ? 'resume' : lastReport ? 'existing' : 'harnesses');
|
|
281
384
|
const [resumeCursor, setResumeCursor] = useState(0);
|
|
385
|
+
const [existingRunCursor, setExistingRunCursor] = useState(0);
|
|
282
386
|
const [harnessIndex, setHarnessIndex] = useState(0);
|
|
283
387
|
const [runHarness, setRunHarness] = useState('');
|
|
284
388
|
const harness = (_a = HARNESSES[harnessIndex]) !== null && _a !== void 0 ? _a : HARNESSES[0];
|
|
@@ -297,9 +401,11 @@ function MintTestApp({ targetPath, docsRoot, pages, autoSelectedFiles, totalPage
|
|
|
297
401
|
const [runStartedAt, setRunStartedAt] = useState(0);
|
|
298
402
|
const autoCollapsed = useRef(new Set());
|
|
299
403
|
const manualCursor = useRef(false);
|
|
404
|
+
const previousRunStage = useRef(activeRunStage);
|
|
300
405
|
const [spinnerFrame, setSpinnerFrame] = useState(0);
|
|
301
406
|
const [now, setNow] = useState(() => Date.now());
|
|
302
407
|
const [report, setReport] = useState();
|
|
408
|
+
const [reviewingSavedRun, setReviewingSavedRun] = useState(false);
|
|
303
409
|
const [error, setError] = useState();
|
|
304
410
|
const abortController = useRef(undefined);
|
|
305
411
|
const finished = useRef(false);
|
|
@@ -310,6 +416,7 @@ function MintTestApp({ targetPath, docsRoot, pages, autoSelectedFiles, totalPage
|
|
|
310
416
|
const included = new Set(visibleRunFiles);
|
|
311
417
|
return visibleScopeRows(pages.filter((page) => included.has(page.file)), runExpanded, EMPTY_SELECTION);
|
|
312
418
|
}, [pages, visibleRunFiles, runExpanded]);
|
|
419
|
+
const generatedRows = useMemo(() => generatedPathRows(taskPhases.values()), [taskPhases]);
|
|
313
420
|
useEffect(() => {
|
|
314
421
|
if (screen !== 'running')
|
|
315
422
|
return;
|
|
@@ -322,7 +429,7 @@ function MintTestApp({ targetPath, docsRoot, pages, autoSelectedFiles, totalPage
|
|
|
322
429
|
}, [screen]);
|
|
323
430
|
useEffect(() => {
|
|
324
431
|
var _a;
|
|
325
|
-
if (screen !== 'running')
|
|
432
|
+
if (screen !== 'running' || activeRunStage !== 'check')
|
|
326
433
|
return;
|
|
327
434
|
const fileDone = (file) => {
|
|
328
435
|
var _a;
|
|
@@ -364,19 +471,56 @@ function MintTestApp({ targetPath, docsRoot, pages, autoSelectedFiles, totalPage
|
|
|
364
471
|
if (nextIndex !== -1 && nextIndex !== runCursor)
|
|
365
472
|
setRunCursor(nextIndex);
|
|
366
473
|
}
|
|
367
|
-
}, [screen, phasesByFile, visibleRunFiles, runRows, runCursor]);
|
|
474
|
+
}, [screen, activeRunStage, phasesByFile, visibleRunFiles, runRows, runCursor]);
|
|
475
|
+
useEffect(() => {
|
|
476
|
+
if (previousRunStage.current === activeRunStage)
|
|
477
|
+
return;
|
|
478
|
+
previousRunStage.current = activeRunStage;
|
|
479
|
+
manualCursor.current = false;
|
|
480
|
+
setRunCursor(0);
|
|
481
|
+
}, [activeRunStage]);
|
|
368
482
|
const finish = (exitCode) => {
|
|
369
483
|
if (finished.current)
|
|
370
484
|
return;
|
|
371
485
|
finished.current = true;
|
|
372
486
|
onFinish(exitCode);
|
|
373
487
|
};
|
|
488
|
+
const reviewRun = (savedReport) => {
|
|
489
|
+
var _a, _b;
|
|
490
|
+
const startedAt = Date.parse(savedReport.startedAt);
|
|
491
|
+
const finishedAt = Date.parse(savedReport.completedAt);
|
|
492
|
+
setTaskPhases(new Map(savedReport.tasks.map((task) => [
|
|
493
|
+
task.id,
|
|
494
|
+
{
|
|
495
|
+
id: task.id,
|
|
496
|
+
agent: task.agent,
|
|
497
|
+
file: task.file,
|
|
498
|
+
directory: task.directory,
|
|
499
|
+
generatedFiles: task.generatedFiles,
|
|
500
|
+
phase: !task.testable && task.status === 'passed' ? 'not_testable' : task.status,
|
|
501
|
+
tokens: task.tokens,
|
|
502
|
+
startedAt: Number.isNaN(startedAt) ? 0 : startedAt,
|
|
503
|
+
finishedAt: Number.isNaN(finishedAt) ? undefined : finishedAt,
|
|
504
|
+
},
|
|
505
|
+
])));
|
|
506
|
+
setRunFiles(savedReport.selectedFiles);
|
|
507
|
+
setRunHarness((_b = (_a = HARNESSES.find((entry) => entry.agent === savedReport.agents[0] && entry.model === savedReport.model)) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : `${savedReport.agents.join(', ')} · ${savedReport.model}`);
|
|
508
|
+
setReport(savedReport);
|
|
509
|
+
setReviewingSavedRun(true);
|
|
510
|
+
setActiveRunStage('run');
|
|
511
|
+
setRunCursor(0);
|
|
512
|
+
setScreen('report');
|
|
513
|
+
};
|
|
374
514
|
const startRun = ({ runId, runAgents, runModel, runFiles: runFiles_, }) => {
|
|
375
515
|
var _a, _b;
|
|
376
516
|
if (abortController.current)
|
|
377
517
|
return;
|
|
378
518
|
const controller = new AbortController();
|
|
379
519
|
abortController.current = controller;
|
|
520
|
+
setTaskPhases(new Map());
|
|
521
|
+
setReport(undefined);
|
|
522
|
+
setReviewingSavedRun(false);
|
|
523
|
+
setError(undefined);
|
|
380
524
|
setTotalTasks(runAgents.length * runFiles_.length);
|
|
381
525
|
setRunHarness((_b = (_a = HARNESSES.find((entry) => entry.agent === runAgents[0] && entry.model === runModel)) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : `${runAgents.join(', ')} · ${runModel}`);
|
|
382
526
|
setRunCursor(0);
|
|
@@ -462,15 +606,17 @@ function MintTestApp({ targetPath, docsRoot, pages, autoSelectedFiles, totalPage
|
|
|
462
606
|
if (screen === 'running' || screen === 'report') {
|
|
463
607
|
const collapse = key.leftArrow || input === 'h';
|
|
464
608
|
const expand = key.rightArrow || input === 'l';
|
|
609
|
+
const showingGeneratedFiles = screen === 'report' || activeRunStage !== 'check';
|
|
610
|
+
const rowCount = showingGeneratedFiles ? generatedRows.length : runRows.length;
|
|
465
611
|
if (key.upArrow || input === 'k') {
|
|
466
612
|
manualCursor.current = true;
|
|
467
613
|
setRunCursor((current) => Math.max(0, current - 1));
|
|
468
614
|
}
|
|
469
615
|
else if (key.downArrow || input === 'j') {
|
|
470
616
|
manualCursor.current = true;
|
|
471
|
-
setRunCursor((current) => Math.max(0, Math.min(
|
|
617
|
+
setRunCursor((current) => Math.max(0, Math.min(rowCount - 1, current + 1)));
|
|
472
618
|
}
|
|
473
|
-
else if (collapse || expand) {
|
|
619
|
+
else if ((collapse || expand) && !showingGeneratedFiles) {
|
|
474
620
|
manualCursor.current = true;
|
|
475
621
|
const row = runRows[runCursor];
|
|
476
622
|
if ((row === null || row === void 0 ? void 0 : row.kind) !== 'directory')
|
|
@@ -485,7 +631,50 @@ function MintTestApp({ targetPath, docsRoot, pages, autoSelectedFiles, totalPage
|
|
|
485
631
|
});
|
|
486
632
|
}
|
|
487
633
|
else if (screen === 'report' && key.return && report) {
|
|
488
|
-
finish(
|
|
634
|
+
finish(reviewingSavedRun
|
|
635
|
+
? 0
|
|
636
|
+
: report.status === 'passed'
|
|
637
|
+
? 0
|
|
638
|
+
: report.status === 'cancelled'
|
|
639
|
+
? 130
|
|
640
|
+
: 1);
|
|
641
|
+
}
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
if (screen === 'existing' && lastReport) {
|
|
645
|
+
if (key.upArrow || input === 'k') {
|
|
646
|
+
setExistingRunCursor((current) => (current + EXISTING_RUN_OPTIONS.length - 1) % EXISTING_RUN_OPTIONS.length);
|
|
647
|
+
}
|
|
648
|
+
else if (key.downArrow || input === 'j') {
|
|
649
|
+
setExistingRunCursor((current) => (current + 1) % EXISTING_RUN_OPTIONS.length);
|
|
650
|
+
}
|
|
651
|
+
else if (key.return) {
|
|
652
|
+
if (existingRunCursor === 0) {
|
|
653
|
+
const knownFiles = new Set(files);
|
|
654
|
+
const updateFiles = lastReport.selectedFiles.filter((file) => knownFiles.has(file));
|
|
655
|
+
setSelectedFiles(new Set(updateFiles.length > 0 ? updateFiles : autoSelectedFiles));
|
|
656
|
+
if (updateFiles.length === 0) {
|
|
657
|
+
const previousHarness = HARNESSES.findIndex((entry) => entry.agent === lastReport.agents[0] && entry.model === lastReport.model);
|
|
658
|
+
if (previousHarness !== -1)
|
|
659
|
+
setHarnessIndex(previousHarness);
|
|
660
|
+
setScreen('scope');
|
|
661
|
+
}
|
|
662
|
+
else {
|
|
663
|
+
startRun({
|
|
664
|
+
runId: null,
|
|
665
|
+
runAgents: lastReport.agents,
|
|
666
|
+
runModel: lastReport.model,
|
|
667
|
+
runFiles: updateFiles,
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
else if (existingRunCursor === 1) {
|
|
672
|
+
reviewRun(lastReport);
|
|
673
|
+
}
|
|
674
|
+
else {
|
|
675
|
+
setSelectedFiles(new Set(autoSelectedFiles));
|
|
676
|
+
setScreen('harnesses');
|
|
677
|
+
}
|
|
489
678
|
}
|
|
490
679
|
return;
|
|
491
680
|
}
|
|
@@ -568,27 +757,28 @@ function MintTestApp({ targetPath, docsRoot, pages, autoSelectedFiles, totalPage
|
|
|
568
757
|
let content;
|
|
569
758
|
if (screen === 'resume' && save)
|
|
570
759
|
content = _jsx(ResumeScreen, { save: save, cursor: resumeCursor });
|
|
760
|
+
else if (screen === 'existing' && lastReport) {
|
|
761
|
+
content = _jsx(ExistingRunScreen, { report: lastReport, cursor: existingRunCursor });
|
|
762
|
+
}
|
|
571
763
|
else if (screen === 'harnesses') {
|
|
572
764
|
content = _jsx(HarnessScreen, { cursor: harnessIndex });
|
|
573
765
|
}
|
|
574
766
|
else if (screen === 'scope') {
|
|
575
|
-
content =
|
|
767
|
+
content = _jsx(ScopeScreen, { rows: rows, cursor: scopeCursor, height: viewHeight });
|
|
576
768
|
}
|
|
577
769
|
else if (screen === 'report' && report) {
|
|
578
|
-
content = (_jsx(ReportScreen, { report: report, rows:
|
|
770
|
+
content = (_jsx(ReportScreen, { report: report, rows: generatedRows, cursor: runCursor, height: viewHeight }));
|
|
579
771
|
}
|
|
580
772
|
else if (screen === 'error') {
|
|
581
773
|
content = (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "red", bold: true, children: "Mint test stopped" }), _jsx(Text, { children: error !== null && error !== void 0 ? error : 'Unknown error' })] }));
|
|
582
774
|
}
|
|
583
775
|
else {
|
|
584
|
-
content = (_jsx(ActivityScreen, { stage: activeRunStage, cancelling: cancelling, harness: runHarness, rows: runRows, cursor: runCursor, phases: phasesByFile, height: viewHeight, totalTasks: totalTasks, spinnerFrame: spinnerFrame, runStartedAt: runStartedAt, now: now }));
|
|
776
|
+
content = (_jsx(ActivityScreen, { stage: activeRunStage, cancelling: cancelling, harness: runHarness, docsRoot: docsRoot, rows: runRows, generatedRows: generatedRows, cursor: runCursor, phases: phasesByFile, height: viewHeight, totalTasks: totalTasks, spinnerFrame: spinnerFrame, runStartedAt: runStartedAt, now: now }));
|
|
585
777
|
}
|
|
586
778
|
return (_jsxs(Box, { flexDirection: "column", width: size.columns, height: viewHeight, overflow: "hidden", children: [_jsxs(Box, { flexGrow: 1, gap: 1, overflow: "hidden", children: [_jsxs(Box, { width: leftWidth, flexShrink: 0, borderStyle: "round", borderColor: "gray", paddingX: 1, flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Steps" }), STEPS.map((step) => {
|
|
587
779
|
const state = stepState(step.id, screen, activeRunStage);
|
|
588
780
|
return (_jsxs(Text, { color: state === 'running' ? 'cyan' : undefined, children: [_jsx(StepGlyph, { state: state }), " ", step.label] }, step.id));
|
|
589
|
-
})] }), _jsx(Box, { flexGrow: 1, borderStyle: fullBleed ? undefined : 'round', borderColor: "gray", paddingX: fullBleed ? 0 : 1, flexDirection: "column", overflow: "hidden", children: content })] }), _jsx(Text, { dimColor: true, children: ctrlCArmed
|
|
590
|
-
? 'Ctrl-C again to exit'
|
|
591
|
-
: footerForScreen(screen, running, (report === null || report === void 0 ? void 0 : report.status) === 'cancelled') })] }));
|
|
781
|
+
})] }), _jsx(Box, { flexGrow: 1, borderStyle: fullBleed ? undefined : 'round', borderColor: "gray", paddingX: fullBleed ? 0 : 1, flexDirection: "column", overflow: "hidden", children: content })] }), _jsx(Text, { dimColor: true, children: ctrlCArmed ? (_jsx(FooterHints, { text: "Ctrl-C again to exit" })) : (_jsx(FooterHints, { text: footerForScreen(screen, running, (report === null || report === void 0 ? void 0 : report.status) === 'cancelled', activeRunStage, reviewingSavedRun) })) })] }));
|
|
592
782
|
}
|
|
593
783
|
export function runMintTestUi(options) {
|
|
594
784
|
return __awaiter(this, void 0, void 0, function* () {
|