@orkestrel/scaffold 0.0.108 → 0.0.109
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/dist/agents/skills/orkestrel-dispatch/scripts/bench.js +326 -91
- package/dist/agents/skills/orkestrel-dispatch/scripts/brief.js +110 -14
- package/dist/agents/skills/orkestrel-dispatch/scripts/chain.js +280 -0
- package/dist/agents/skills/orkestrel-dispatch/scripts/cite.js +188 -26
- package/dist/agents/skills/orkestrel-dispatch/scripts/launch.js +142 -43
- package/dist/agents/skills/orkestrel-dispatch/scripts/login.js +240 -62
- package/dist/agents/skills/orkestrel-dispatch/scripts/patch.js +323 -0
- package/dist/agents/skills/orkestrel-dispatch/scripts/result.js +169 -32
- package/dist/agents/skills/orkestrel-dispatch/scripts/sweep.js +290 -69
- package/dist/agents/skills/orkestrel-falsify/scripts/mutate.js +560 -0
- package/dist/agents/skills/orkestrel-harden/scripts/discovery.js +1052 -205
- package/dist/agents/skills/orkestrel-harden/scripts/measure.js +361 -0
- package/dist/agents/skills/orkestrel-harden/scripts/titles.js +455 -80
- package/dist/agents/skills/orkestrel-parallel/scripts/cases.js +327 -0
- package/dist/agents/skills/orkestrel-publish/scripts/compare.js +298 -51
- package/dist/agents/skills/orkestrel-publish/scripts/pins.js +170 -13
- package/dist/agents/skills/orkestrel-publish/scripts/swap.js +956 -191
- package/dist/agents/skills/orkestrel-publish/scripts/wave.js +707 -112
- package/dist/agents/skills/orkestrel-publish/scripts/window.js +235 -41
- package/dist/agents/skills/orkestrel-scout/scripts/map.js +341 -54
- package/dist/agents/templates/brief.md +1 -1
- package/dist/bin/main.js +51 -46
- package/dist/bin/main.js.map +1 -1
- package/dist/host/AGENTS.md +23 -21
- package/dist/host/agents/orchestration.md +43 -18
- package/dist/host/agents/platforms/windows.md +3 -3
- package/dist/host/agents/skills/orkestrel-align/SKILL.md +10 -13
- package/dist/host/agents/skills/orkestrel-align/references/fleet.md +4 -10
- package/dist/host/agents/skills/orkestrel-align/references/integration.md +1 -1
- package/dist/host/agents/skills/orkestrel-build/SKILL.md +16 -48
- package/dist/host/agents/skills/orkestrel-build/agents/openai.yaml +1 -1
- package/dist/host/agents/skills/orkestrel-debrief/SKILL.md +19 -16
- package/dist/host/agents/skills/orkestrel-debrief/agents/openai.yaml +1 -1
- package/dist/host/agents/skills/orkestrel-debrief/references/field-testing.md +15 -28
- package/dist/host/agents/skills/orkestrel-debrief/references/instruction-audit.md +5 -6
- package/dist/host/agents/skills/orkestrel-debrief/references/retention.md +4 -6
- package/dist/host/agents/skills/orkestrel-dispatch/SKILL.md +31 -20
- package/dist/host/agents/skills/orkestrel-dispatch/references/launch.md +3 -4
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/bench.ts +366 -75
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/brief.ts +126 -16
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/chain.ts +323 -0
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/cite.ts +220 -31
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/launch.ts +176 -33
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/login.ts +294 -59
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/patch.ts +401 -0
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/result.ts +199 -34
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/sweep.ts +330 -69
- package/dist/host/agents/skills/orkestrel-falsify/SKILL.md +10 -8
- package/dist/host/agents/skills/orkestrel-falsify/references/brief.md +1 -5
- package/dist/host/agents/skills/orkestrel-falsify/references/reconcile.md +1 -1
- package/dist/host/agents/skills/orkestrel-falsify/scripts/mutate.ts +701 -0
- package/dist/host/agents/skills/orkestrel-harden/SKILL.md +16 -12
- package/dist/host/agents/skills/orkestrel-harden/references/bindings.md +23 -81
- package/dist/host/agents/skills/orkestrel-harden/references/centralization.md +4 -5
- package/dist/host/agents/skills/orkestrel-harden/references/contract.md +2 -0
- package/dist/host/agents/skills/orkestrel-harden/references/document.md +33 -0
- package/dist/host/agents/skills/orkestrel-harden/references/equivalence.md +6 -3
- package/dist/host/agents/skills/orkestrel-harden/references/hardening.md +8 -4
- package/dist/host/agents/skills/orkestrel-harden/references/research.md +2 -0
- package/dist/host/agents/skills/orkestrel-harden/scripts/discovery.ts +1272 -279
- package/dist/host/agents/skills/orkestrel-harden/scripts/measure.ts +425 -0
- package/dist/host/agents/skills/orkestrel-harden/scripts/titles.ts +603 -98
- package/dist/host/agents/skills/orkestrel-journey/SKILL.md +75 -98
- package/dist/host/agents/skills/orkestrel-journey/references/captures.md +54 -17
- package/dist/host/agents/skills/orkestrel-journey/references/decide.md +1 -20
- package/dist/host/agents/skills/orkestrel-journey/references/layer.md +21 -31
- package/dist/host/agents/skills/orkestrel-journey/references/styles.md +1 -1
- package/dist/host/agents/skills/orkestrel-parallel/SKILL.md +28 -24
- package/dist/host/agents/skills/orkestrel-parallel/references/claude.md +15 -13
- package/dist/host/agents/skills/orkestrel-parallel/references/codex.md +1 -0
- package/dist/host/agents/skills/orkestrel-parallel/references/cursor.md +13 -13
- package/dist/host/agents/skills/orkestrel-parallel/scripts/cases.ts +389 -0
- package/dist/host/agents/skills/orkestrel-polish/SKILL.md +16 -20
- package/dist/host/agents/skills/orkestrel-polish/references/capture-harness.md +16 -26
- package/dist/host/agents/skills/orkestrel-publish/SKILL.md +12 -26
- package/dist/host/agents/skills/orkestrel-publish/agents/openai.yaml +2 -2
- package/dist/host/agents/skills/orkestrel-publish/references/adopt.md +23 -0
- package/dist/host/agents/skills/orkestrel-publish/references/release.md +4 -4
- package/dist/host/agents/skills/orkestrel-publish/references/wave.md +10 -9
- package/dist/host/agents/skills/orkestrel-publish/references/window.md +20 -25
- package/dist/host/agents/skills/orkestrel-publish/scripts/compare.ts +348 -59
- package/dist/host/agents/skills/orkestrel-publish/scripts/pins.ts +202 -22
- package/dist/host/agents/skills/orkestrel-publish/scripts/swap.ts +1061 -191
- package/dist/host/agents/skills/orkestrel-publish/scripts/wave.ts +812 -140
- package/dist/host/agents/skills/orkestrel-publish/scripts/window.ts +280 -38
- package/dist/host/agents/skills/orkestrel-scout/SKILL.md +3 -3
- package/dist/host/agents/skills/orkestrel-scout/scripts/map.ts +428 -70
- package/dist/host/agents/templates/brief.md +1 -1
- package/dist/host/agents/transports/claude-cli.md +3 -3
- package/dist/host/agents/transports/codex.md +2 -7
- package/dist/host/agents/transports/cursor.md +2 -3
- package/dist/host/claude/AGENTS.md +2 -2
- package/dist/host/claude/agents/analyst.md +3 -3
- package/dist/host/claude/agents/astra.md +3 -3
- package/dist/host/claude/agents/builder.md +1 -1
- package/dist/host/claude/agents/checker.md +1 -1
- package/dist/host/claude/agents/distiller.md +1 -1
- package/dist/host/claude/agents/grok.md +1 -1
- package/dist/host/claude/agents/opus.md +2 -2
- package/dist/host/claude/agents/orkestrel.md +57 -90
- package/dist/host/claude/agents/researcher.md +1 -1
- package/dist/host/claude/agents/reviewer.md +4 -0
- package/dist/host/claude/agents/scout.md +1 -1
- package/dist/host/claude/agents/verifier.md +1 -1
- package/dist/host/claude/rules/application.md +14 -21
- package/dist/host/claude/rules/architecture.md +34 -44
- package/dist/host/claude/rules/browser.md +3 -4
- package/dist/host/claude/rules/campaign.md +2 -2
- package/dist/host/claude/rules/documentation.md +25 -12
- package/dist/host/claude/rules/names.md +9 -4
- package/dist/host/claude/rules/patterns.md +6 -7
- package/dist/host/claude/rules/portability.md +1 -1
- package/dist/host/claude/rules/quality.md +4 -5
- package/dist/host/claude/rules/styles.md +2 -2
- package/dist/host/claude/rules/tests.md +57 -114
- package/dist/host/claude/rules/typescript.md +7 -9
- package/dist/host/claude/rules/workspace.md +21 -19
- package/dist/host/claude/rules/writing.md +2 -5
- package/dist/host/claude/skills/orkestrel-dispatch/SKILL.md +1 -1
- package/dist/host/claude/skills/orkestrel-harden/SKILL.md +1 -1
- package/dist/host/claude/skills/orkestrel-publish/SKILL.md +1 -1
- package/dist/host/claude/skills/orkestrel-scout/SKILL.md +1 -1
- package/dist/host/codex/agents/analyst.toml +9 -3
- package/dist/host/codex/agents/astra.toml +3 -4
- package/dist/host/codex/agents/builder.toml +1 -1
- package/dist/host/codex/agents/opus.toml +3 -4
- package/dist/host/codex/agents/orkestrel.toml +1 -1
- package/dist/host/codex/agents/researcher.toml +2 -2
- package/dist/host/codex/agents/verifier.toml +1 -1
- package/dist/host/configs/helpers.ts +21 -1
- package/dist/host/configs/policy.ts +299 -15
- package/dist/host/dotfiles/oxlintrc.json +137 -3
- package/dist/host/guides/agent.md +111 -134
- package/dist/host/guides/capture.md +801 -0
- package/dist/host/guides/probe.md +27 -8
- package/dist/host/guides/scaffold.md +96 -18
- package/dist/host/guides/study.md +534 -440
- package/dist/host/guides/test.md +111 -44
- package/dist/host/manifest.json +160 -240
- package/dist/host/tests/config.test.ts +971 -187
- package/dist/host/tests/policy.test.ts +127 -1
- package/dist/host/tests/setupPolicy.ts +893 -33
- package/dist/src/core/index.cjs +214 -86
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +122 -38
- package/dist/src/core/index.d.ts +122 -38
- package/dist/src/core/index.js +209 -85
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +20 -27
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.js +21 -28
- package/dist/src/server/index.js.map +1 -1
- package/package.json +11 -11
- package/dist/agents/skills/orkestrel-dispatch/scripts/helpers.js +0 -283
- package/dist/host/agents/skills/orkestrel-dispatch/references/bench.md +0 -25
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/helpers.ts +0 -333
- package/dist/host/guides/supervisor.md +0 -4890
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
// Check a Vitest JSON report against named red and green cases. Run from the checkout root:
|
|
2
|
+
// node .agents/skills/orkestrel-parallel/scripts/cases.ts --report FILE.json [--passed NAME ...] [--failed NAME ...]
|
|
3
|
+
// The report is Vitest's `--reporter=json` output. A name matches every case whose full name (its
|
|
4
|
+
// `describe` titles and its own title, joined by spaces) or whose own title equals it exactly; a name
|
|
5
|
+
// that opens with `--` cannot be given. Each `--passed NAME` requires every case it matches to have
|
|
6
|
+
// passed and each `--failed NAME` requires every one to have failed; a name that matches no case is
|
|
7
|
+
// missing. A failed case that no name matches is refused, so a run that fails more than the request
|
|
8
|
+
// expected reads as a difference. A test file that failed with no failed case (it did not collect,
|
|
9
|
+
// or a hook threw) is an unnamed failure named by its file, and a report whose `success` is false
|
|
10
|
+
// while no case and no file failed is a difference too. Name no case to require a report with no
|
|
11
|
+
// failure. Both flags repeat and a name may not be given to both. The script prints one summary
|
|
12
|
+
// line, one line per difference, and `ok` or `differences=N`. The script refuses a flag it does not
|
|
13
|
+
// read, the `--flag=value` form included, and names an unknown flag before a flag given with no
|
|
14
|
+
// value. Exit 0 when the report matches the names, 3 when it does not, 2 when the report cannot be
|
|
15
|
+
// read as a Vitest report, 64 on usage.
|
|
16
|
+
import { existsSync, readFileSync, realpathSync, statSync } from 'node:fs';
|
|
17
|
+
import { relative } from 'node:path';
|
|
18
|
+
/** Lists the flags that take a value. */
|
|
19
|
+
export const VALUE_FLAGS = ['--report', '--passed', '--failed'];
|
|
20
|
+
/** Lists every flag the script reads, value flags and switches alike. */
|
|
21
|
+
export const FLAGS = VALUE_FLAGS;
|
|
22
|
+
/** Holds the usage line printed when a call names no report. */
|
|
23
|
+
export const USAGE = 'usage: cases.ts --report FILE.json [--passed NAME ...] [--failed NAME ...]; --passed and --failed repeat';
|
|
24
|
+
/**
|
|
25
|
+
* Names the value flags a caller gave with no value, at any occurrence.
|
|
26
|
+
*
|
|
27
|
+
* @param argv - The arguments after the script path.
|
|
28
|
+
* @param names - The flags that take a value.
|
|
29
|
+
* @returns Each named flag that appears last or is followed by another flag, in argument order.
|
|
30
|
+
*/
|
|
31
|
+
export function readMissingFlags(argv, names) {
|
|
32
|
+
return names.filter((name) => argv.some((argument, index) => argument === name && isMissingValue(argv, index)));
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Checks whether the argument after a flag is absent or is another flag.
|
|
36
|
+
*
|
|
37
|
+
* @param argv - The arguments after the script path.
|
|
38
|
+
* @param index - The position of the flag.
|
|
39
|
+
* @returns True when no value follows the flag; false otherwise.
|
|
40
|
+
*/
|
|
41
|
+
export function isMissingValue(argv, index) {
|
|
42
|
+
const value = argv[index + 1];
|
|
43
|
+
return value === undefined || value.startsWith('--');
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Names the flags a caller gave that the script does not read.
|
|
47
|
+
*
|
|
48
|
+
* @remarks
|
|
49
|
+
* A `--flag=value` argument is unknown, because the script reads no such form. A bare `--` ends the
|
|
50
|
+
* scan.
|
|
51
|
+
*
|
|
52
|
+
* @param argv - The arguments after the script path.
|
|
53
|
+
* @param flags - Every flag the script reads, value flags and switches alike.
|
|
54
|
+
* @returns Each argument that opens with `--` and is not a read flag, in argument order.
|
|
55
|
+
*/
|
|
56
|
+
export function readUnknownFlags(argv, flags) {
|
|
57
|
+
const end = argv.indexOf('--');
|
|
58
|
+
return (end === -1 ? argv : argv.slice(0, end)).filter((argument) => argument.startsWith('--') && !flags.includes(argument));
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Reads every value that follows a repeatable flag.
|
|
62
|
+
*
|
|
63
|
+
* @param argv - The arguments after the script path.
|
|
64
|
+
* @param name - The flag, such as `--passed`.
|
|
65
|
+
* @returns The values in order, empty when the flag never appears.
|
|
66
|
+
*/
|
|
67
|
+
export function readOptions(argv, name) {
|
|
68
|
+
return argv.flatMap((argument, index) => {
|
|
69
|
+
const value = argv[index + 1];
|
|
70
|
+
return argument === name && value !== undefined && !value.startsWith('--') ? [value] : [];
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Reads the arguments that are neither a flag nor the value of a value flag.
|
|
75
|
+
*
|
|
76
|
+
* @param argv - The arguments after the script path.
|
|
77
|
+
* @param valueFlags - The flags that take a value.
|
|
78
|
+
* @returns The positional arguments in order.
|
|
79
|
+
*/
|
|
80
|
+
export function readPositionals(argv, valueFlags) {
|
|
81
|
+
return argv.filter((argument, index) => !argument.startsWith('--') && !valueFlags.includes(argv[index - 1] ?? ''));
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Checks whether a value is a non-null, non-array object.
|
|
85
|
+
*
|
|
86
|
+
* @param value - The value to inspect.
|
|
87
|
+
* @returns True if the value is an object other than null or an array; false otherwise.
|
|
88
|
+
*/
|
|
89
|
+
export function isRecord(value) {
|
|
90
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Quotes an untrusted text for a diagnostic, JSON-encoded and cut to a bounded length.
|
|
94
|
+
*
|
|
95
|
+
* @param text - The text to quote.
|
|
96
|
+
* @returns The JSON string literal of the text, ending in an ellipsis inside the quotes when cut.
|
|
97
|
+
*/
|
|
98
|
+
export function quote(text) {
|
|
99
|
+
return JSON.stringify(text.length > 120 ? `${text.slice(0, 120)}…` : text);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Reads one assertion result of a report as a case.
|
|
103
|
+
*
|
|
104
|
+
* @param file - The test file the result belongs to.
|
|
105
|
+
* @param result - The parsed JSON value of one assertion result.
|
|
106
|
+
* @returns The case as a one-member list, or an empty list when the result lacks a title or status.
|
|
107
|
+
*/
|
|
108
|
+
export function readCase(file, result) {
|
|
109
|
+
if (!isRecord(result))
|
|
110
|
+
return [];
|
|
111
|
+
const { title, status, fullName, ancestorTitles } = result;
|
|
112
|
+
if (typeof title !== 'string' || typeof status !== 'string')
|
|
113
|
+
return [];
|
|
114
|
+
const ancestors = Array.isArray(ancestorTitles)
|
|
115
|
+
? ancestorTitles.filter((member) => typeof member === 'string')
|
|
116
|
+
: [];
|
|
117
|
+
return [
|
|
118
|
+
{
|
|
119
|
+
file,
|
|
120
|
+
title,
|
|
121
|
+
status,
|
|
122
|
+
fullName: typeof fullName === 'string' ? fullName : [...ancestors, title].join(' '),
|
|
123
|
+
},
|
|
124
|
+
];
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Reads the cases of a Vitest JSON report.
|
|
128
|
+
*
|
|
129
|
+
* @param report - The parsed JSON value of the report.
|
|
130
|
+
* @returns Every case in file order, or undefined when the value is not a Vitest report: an object
|
|
131
|
+
* whose `testResults` is an array.
|
|
132
|
+
*/
|
|
133
|
+
export function readCases(report) {
|
|
134
|
+
if (!isRecord(report) || !Array.isArray(report.testResults))
|
|
135
|
+
return undefined;
|
|
136
|
+
return report.testResults.flatMap((suite) => {
|
|
137
|
+
if (!isRecord(suite) || !Array.isArray(suite.assertionResults))
|
|
138
|
+
return [];
|
|
139
|
+
const file = typeof suite.name === 'string' ? suite.name : '';
|
|
140
|
+
return suite.assertionResults.flatMap((result) => readCase(file, result));
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Reads the test files of a report that failed with no failed case.
|
|
145
|
+
*
|
|
146
|
+
* @param report - The parsed JSON value of the report.
|
|
147
|
+
* @returns The `name` of each `testResults` entry whose `status` is `failed` and whose assertion
|
|
148
|
+
* results hold no failed case, in file order; an empty text stands for an entry with no name.
|
|
149
|
+
*/
|
|
150
|
+
export function readFailedFiles(report) {
|
|
151
|
+
if (!isRecord(report) || !Array.isArray(report.testResults))
|
|
152
|
+
return [];
|
|
153
|
+
return report.testResults.flatMap((suite) => {
|
|
154
|
+
if (!isRecord(suite) || suite.status !== 'failed')
|
|
155
|
+
return [];
|
|
156
|
+
const results = Array.isArray(suite.assertionResults) ? suite.assertionResults : [];
|
|
157
|
+
if (results.some((result) => isRecord(result) && result.status === 'failed'))
|
|
158
|
+
return [];
|
|
159
|
+
return [typeof suite.name === 'string' ? suite.name : ''];
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Checks whether a report states that the run did not succeed.
|
|
164
|
+
*
|
|
165
|
+
* @param report - The parsed JSON value of the report.
|
|
166
|
+
* @returns True when the report is an object whose `success` is `false`; false otherwise.
|
|
167
|
+
*/
|
|
168
|
+
export function isUnsuccessful(report) {
|
|
169
|
+
return isRecord(report) && report.success === false;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Checks whether a name matches a case.
|
|
173
|
+
*
|
|
174
|
+
* @param entry - The case.
|
|
175
|
+
* @param name - The name from `--passed` or `--failed`.
|
|
176
|
+
* @returns True when the name equals the case's full name or its own title; false otherwise.
|
|
177
|
+
*/
|
|
178
|
+
export function matchesCase(entry, name) {
|
|
179
|
+
return entry.fullName === name || entry.title === name;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Judges the cases of a report against the names a caller gave.
|
|
183
|
+
*
|
|
184
|
+
* @param cases - The report's cases.
|
|
185
|
+
* @param passed - The names that must have passed.
|
|
186
|
+
* @param failed - The names that must have failed.
|
|
187
|
+
* @param base - The directory the file names in each difference are written relative to.
|
|
188
|
+
* @returns Each difference in order: missing names, wrong statuses, then failed cases no name
|
|
189
|
+
* matches; empty when the report matches.
|
|
190
|
+
*/
|
|
191
|
+
export function judgeCases(cases, passed, failed, base) {
|
|
192
|
+
const expectations = [
|
|
193
|
+
...passed.map((name) => ({ name, status: 'passed' })),
|
|
194
|
+
...failed.map((name) => ({ name, status: 'failed' })),
|
|
195
|
+
];
|
|
196
|
+
const named = expectations.flatMap((expectation) => {
|
|
197
|
+
const matches = cases.filter((entry) => matchesCase(entry, expectation.name));
|
|
198
|
+
if (matches.length === 0)
|
|
199
|
+
return [`missing: ${quote(expectation.name)} matches no case`];
|
|
200
|
+
return matches
|
|
201
|
+
.filter((entry) => entry.status !== expectation.status)
|
|
202
|
+
.map((entry) => `status: ${quote(entry.fullName)} is ${entry.status}, expected ${expectation.status}${describeFile(base, entry.file)}`);
|
|
203
|
+
});
|
|
204
|
+
const unnamed = cases
|
|
205
|
+
.filter((entry) => entry.status === 'failed' &&
|
|
206
|
+
!expectations.some((expectation) => matchesCase(entry, expectation.name)))
|
|
207
|
+
.map((entry) => `unnamed failure: ${quote(entry.fullName)}${describeFile(base, entry.file)}`);
|
|
208
|
+
return [...named, ...unnamed];
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Judges the failures a report holds outside its cases.
|
|
212
|
+
*
|
|
213
|
+
* @param cases - The report's cases.
|
|
214
|
+
* @param failedFiles - The test files that failed with no failed case, from {@link readFailedFiles}.
|
|
215
|
+
* @param unsuccessful - Whether the report states that the run did not succeed.
|
|
216
|
+
* @param base - The directory the file names in each difference are written relative to.
|
|
217
|
+
* @returns One unnamed failure per failed file; or, when no file and no case failed yet the report
|
|
218
|
+
* is not successful, one unnamed failure for the report; empty otherwise.
|
|
219
|
+
*/
|
|
220
|
+
export function judgeFailures(cases, failedFiles, unsuccessful, base) {
|
|
221
|
+
if (failedFiles.length > 0) {
|
|
222
|
+
return failedFiles.map((file) => `unnamed failure: ${file === '' ? 'a test file' : quote(relativeFile(base, file))} failed with no failed case`);
|
|
223
|
+
}
|
|
224
|
+
return unsuccessful && cases.every((entry) => entry.status !== 'failed')
|
|
225
|
+
? ['unnamed failure: the report is not successful, yet no case and no file in it failed']
|
|
226
|
+
: [];
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Writes a test file path relative to a directory.
|
|
230
|
+
*
|
|
231
|
+
* @param base - The directory the path is written relative to.
|
|
232
|
+
* @param file - The test file path from the report.
|
|
233
|
+
* @returns The relative path with forward slashes.
|
|
234
|
+
*/
|
|
235
|
+
export function relativeFile(base, file) {
|
|
236
|
+
return relative(base, file).split('\\').join('/');
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Describes the file a case belongs to, written relative to a directory.
|
|
240
|
+
*
|
|
241
|
+
* @param base - The directory the path is written relative to.
|
|
242
|
+
* @param file - The test file path from the report.
|
|
243
|
+
* @returns ` in <path>` with forward slashes, or an empty text when the report names no file.
|
|
244
|
+
*/
|
|
245
|
+
export function describeFile(base, file) {
|
|
246
|
+
return file === '' ? '' : ` in ${relativeFile(base, file)}`;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Counts the cases that carry a status.
|
|
250
|
+
*
|
|
251
|
+
* @param cases - The report's cases.
|
|
252
|
+
* @param status - The status, such as `passed`.
|
|
253
|
+
* @returns How many cases have exactly that status.
|
|
254
|
+
*/
|
|
255
|
+
export function countStatus(cases, status) {
|
|
256
|
+
return cases.filter((entry) => entry.status === status).length;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Reads a report file as JSON.
|
|
260
|
+
*
|
|
261
|
+
* @param path - The report file.
|
|
262
|
+
* @returns The parsed value, or undefined when the file is not readable JSON.
|
|
263
|
+
*/
|
|
264
|
+
export function readReport(path) {
|
|
265
|
+
try {
|
|
266
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
267
|
+
}
|
|
268
|
+
catch {
|
|
269
|
+
return undefined;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Runs the script on its arguments.
|
|
274
|
+
*
|
|
275
|
+
* @param argv - The arguments after the script path.
|
|
276
|
+
* @returns The exit code: 0 when the report matches the names, 3 when it does not, 2 when the report
|
|
277
|
+
* cannot be read as a Vitest report, 64 on usage.
|
|
278
|
+
*/
|
|
279
|
+
export function main(argv) {
|
|
280
|
+
const unknown = readUnknownFlags(argv, FLAGS);
|
|
281
|
+
if (unknown.length > 0) {
|
|
282
|
+
console.error(`cases: ${unknown.join(', ')} is not a flag this script reads; ${USAGE}`);
|
|
283
|
+
return 64;
|
|
284
|
+
}
|
|
285
|
+
const missing = readMissingFlags(argv, VALUE_FLAGS);
|
|
286
|
+
if (missing.length > 0) {
|
|
287
|
+
console.error(`cases: ${missing.join(', ')} given with no value`);
|
|
288
|
+
return 64;
|
|
289
|
+
}
|
|
290
|
+
const reports = readOptions(argv, '--report');
|
|
291
|
+
const path = reports[0];
|
|
292
|
+
const passed = readOptions(argv, '--passed');
|
|
293
|
+
const failed = readOptions(argv, '--failed');
|
|
294
|
+
if (path === undefined || readPositionals(argv, VALUE_FLAGS).length > 0) {
|
|
295
|
+
console.error(USAGE);
|
|
296
|
+
return 64;
|
|
297
|
+
}
|
|
298
|
+
const both = passed.filter((name) => failed.includes(name));
|
|
299
|
+
if (both.length > 0) {
|
|
300
|
+
console.error(`cases: ${both.map(quote).join(', ')} given to both --passed and --failed; ${USAGE}`);
|
|
301
|
+
return 64;
|
|
302
|
+
}
|
|
303
|
+
if (!existsSync(path) || !statSync(path).isFile()) {
|
|
304
|
+
console.error(`cases: ${quote(path)} is not a readable file`);
|
|
305
|
+
return 2;
|
|
306
|
+
}
|
|
307
|
+
const report = readReport(path);
|
|
308
|
+
const cases = readCases(report);
|
|
309
|
+
if (cases === undefined) {
|
|
310
|
+
console.error(`cases: ${quote(path)} is not a Vitest JSON report; it needs a "testResults" array (run vitest with --reporter=json)`);
|
|
311
|
+
return 2;
|
|
312
|
+
}
|
|
313
|
+
const differences = [
|
|
314
|
+
...judgeCases(cases, passed, failed, process.cwd()),
|
|
315
|
+
...judgeFailures(cases, readFailedFiles(report), isUnsuccessful(report), process.cwd()),
|
|
316
|
+
];
|
|
317
|
+
const green = countStatus(cases, 'passed');
|
|
318
|
+
const red = countStatus(cases, 'failed');
|
|
319
|
+
console.log(`cases total=${String(cases.length)} passed=${String(green)} failed=${String(red)} other=${String(cases.length - green - red)}`);
|
|
320
|
+
for (const difference of differences)
|
|
321
|
+
console.log(difference);
|
|
322
|
+
console.log(differences.length === 0 ? 'ok' : `differences=${String(differences.length)}`);
|
|
323
|
+
return differences.length === 0 ? 0 : 3;
|
|
324
|
+
}
|
|
325
|
+
if (process.argv[1] !== undefined && realpathSync(process.argv[1]) === import.meta.filename) {
|
|
326
|
+
process.exitCode = main(process.argv.slice(2));
|
|
327
|
+
}
|