@orkestrel/scaffold 0.0.107 → 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 +368 -27
- 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 +26 -20
- package/dist/host/agents/orchestration.md +98 -58
- package/dist/host/agents/platforms/posix.md +15 -0
- package/dist/host/agents/platforms/windows.md +37 -0
- 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 +12 -14
- package/dist/host/agents/skills/orkestrel-dispatch/SKILL.md +36 -24
- package/dist/host/agents/skills/orkestrel-dispatch/references/launch.md +14 -11
- 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 +428 -27
- 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 +91 -30
- package/dist/host/agents/skills/orkestrel-parallel/agents/openai.yaml +2 -2
- package/dist/host/agents/skills/orkestrel-parallel/references/claude.md +34 -0
- package/dist/host/agents/skills/orkestrel-parallel/references/codex.md +29 -0
- package/dist/host/agents/skills/orkestrel-parallel/references/cursor.md +29 -0
- package/dist/host/agents/skills/orkestrel-parallel/references/t3.md +31 -0
- 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 +12 -10
- 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 +3 -8
- package/dist/host/agents/transports/cursor.md +2 -3
- package/dist/host/claude/AGENTS.md +7 -10
- 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 +55 -0
- package/dist/host/claude/rules/documentation.md +26 -11
- 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 +2 -3
- 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 +8 -10
- package/dist/host/claude/rules/workspace.md +21 -19
- package/dist/host/claude/rules/writing.md +3 -3
- 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-parallel/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/codex/config.toml +3 -3
- package/dist/host/configs/helpers.ts +21 -1
- package/dist/host/configs/policy.ts +405 -16
- package/dist/host/cursor/rules/orchestration.mdc +1 -0
- package/dist/host/dotfiles/oxlintrc.json +141 -5
- 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 +152 -19
- package/dist/host/guides/study.md +534 -440
- package/dist/host/guides/test.md +111 -44
- package/dist/host/manifest.json +199 -242
- package/dist/host/tests/config.test.ts +1558 -219
- package/dist/host/tests/policy.test.ts +127 -1
- package/dist/host/tests/setupPolicy.ts +1389 -39
- package/dist/src/core/index.cjs +239 -87
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +123 -39
- package/dist/src/core/index.d.ts +123 -39
- package/dist/src/core/index.js +234 -86
- 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/agents/skills/orkestrel-parallel/references/harnesses.md +0 -19
- package/dist/host/guides/supervisor.md +0 -4890
|
@@ -5,33 +5,178 @@
|
|
|
5
5
|
// --cursor resolves the Cursor CLI entry (the newest versioned install under LOCALAPPDATA on
|
|
6
6
|
// Windows, `agent` on PATH elsewhere), reads its version (an entry that cannot answer `--version` is
|
|
7
7
|
// dark, with or without --resolve), and runs one bounded prompt through
|
|
8
|
-
// launch.ts into tmp/cursor/bench.jsonl; --resolve
|
|
8
|
+
// launch.ts into tmp/cursor/bench.jsonl; --resolve, which only --cursor takes, stops after printing
|
|
9
|
+
// the entry. --codex reads
|
|
9
10
|
// `codex --version` and `codex login status`, then runs one bounded exec into tmp/codex/bench.jsonl
|
|
10
|
-
// under the sandbox the host allows (danger-full-access on Windows, read-only elsewhere)
|
|
11
|
+
// under the sandbox the host allows (danger-full-access on Windows, read-only elsewhere) unless
|
|
12
|
+
// --sandbox names read-only, workspace-write, or danger-full-access. --claude
|
|
11
13
|
// reads `claude --version` and runs one bounded print-mode prompt into tmp/claude/bench.jsonl. The
|
|
12
|
-
//
|
|
13
|
-
// the
|
|
14
|
+
// script runs launch.ts and result.ts as child processes beside it and imports neither. The JSON
|
|
15
|
+
// summary carries the command, the version, the session id, the answer, and `live`. --cap is a
|
|
16
|
+
// number of seconds above 0 and at most 2147483, checked before any probe runs. Name one bench per
|
|
17
|
+
// run; the script refuses a flag it does not read, the `--flag=value` form included. Exit 0 when the
|
|
18
|
+
// bench answered READY, 3 when it is dark, 64 on usage.
|
|
14
19
|
import { spawnSync } from 'node:child_process';
|
|
15
|
-
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
20
|
+
import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from 'node:fs';
|
|
16
21
|
import { dirname, extname, join } from 'node:path';
|
|
17
22
|
import { fileURLToPath } from 'node:url';
|
|
18
|
-
|
|
19
|
-
const SCRIPTS = dirname(fileURLToPath(import.meta.url));
|
|
20
|
-
|
|
21
|
-
const EXTENSION = extname(fileURLToPath(import.meta.url));
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
/** Names the directory this script runs from, which holds the launch and result siblings. */
|
|
24
|
+
export const SCRIPTS = dirname(fileURLToPath(import.meta.url));
|
|
25
|
+
/** Names the extension of the running file: the checkout runs `.ts` sources and a built twin runs `.js` siblings. */
|
|
26
|
+
export const EXTENSION = extname(fileURLToPath(import.meta.url));
|
|
27
|
+
/** Holds the one-word request every bench round trip sends. */
|
|
28
|
+
export const PROMPT = 'Reply with the single word READY and nothing else.';
|
|
29
|
+
/** Names the Cursor model a round trip uses unless the call names another. */
|
|
30
|
+
export const CURSOR_MODEL = process.env.CURSOR_GROK_MODEL ?? 'grok-4.7-xhigh';
|
|
31
|
+
/** Names the Codex model a round trip uses unless the call names another. */
|
|
32
|
+
export const CODEX_MODEL = process.env.CODEX_ASTRA_MODEL ?? 'gpt-6-astra';
|
|
33
|
+
/** Names the Claude model alias a round trip uses unless the call names another. */
|
|
34
|
+
export const CLAUDE_MODEL = 'opus';
|
|
35
|
+
/** Holds the seconds a round trip may run when the call gives no cap. */
|
|
36
|
+
export const DEFAULT_CAP = 180;
|
|
37
|
+
/** Holds the longest cap in whole seconds that a Node timer carries through the launcher. */
|
|
38
|
+
export const MAX_CAP_SECONDS = 2_147_483;
|
|
39
|
+
/** Lists the sandbox modes `--sandbox` may name, which the Codex CLI reads. */
|
|
40
|
+
export const SANDBOX_MODES = [
|
|
41
|
+
'read-only',
|
|
42
|
+
'workspace-write',
|
|
43
|
+
'danger-full-access',
|
|
44
|
+
];
|
|
45
|
+
/** Lists the flags that each select one bench. */
|
|
46
|
+
export const BENCH_FLAGS = ['--cursor', '--codex', '--claude'];
|
|
47
|
+
/** Lists the flags that take a value. */
|
|
48
|
+
export const VALUE_FLAGS = ['--model', '--cap', '--sandbox'];
|
|
49
|
+
/** Lists every flag the script reads, value flags and switches alike. */
|
|
50
|
+
export const FLAGS = [...BENCH_FLAGS, '--resolve', ...VALUE_FLAGS];
|
|
51
|
+
/** Holds the usage line printed when a call names no bench or more than one. */
|
|
52
|
+
export const USAGE = 'usage: bench.ts --cursor [--resolve] [--model ID] [--cap SECONDS] | --codex [--model ID] [--sandbox MODE] [--cap SECONDS] | --claude [--model ALIAS] [--cap SECONDS]; name one bench';
|
|
53
|
+
/**
|
|
54
|
+
* Reads the value that follows a flag.
|
|
55
|
+
*
|
|
56
|
+
* @remarks
|
|
57
|
+
* The first occurrence of a flag wins, a following value that opens with `--` is a missing value,
|
|
58
|
+
* and the `--flag=value` form is not read.
|
|
59
|
+
*
|
|
60
|
+
* @param argv - The arguments after the script path.
|
|
61
|
+
* @param name - The flag, such as `--cap`.
|
|
62
|
+
* @returns The value after the flag, or undefined when the flag is absent, last, or followed by
|
|
63
|
+
* another flag.
|
|
64
|
+
*/
|
|
65
|
+
export function readOption(argv, name) {
|
|
66
|
+
const index = argv.indexOf(name);
|
|
67
|
+
const value = index === -1 ? undefined : argv[index + 1];
|
|
68
|
+
return value === undefined || value.startsWith('--') ? undefined : value;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Names the value flags a caller gave with no value.
|
|
72
|
+
*
|
|
73
|
+
* @param argv - The arguments after the script path.
|
|
74
|
+
* @param names - The flags that take a value.
|
|
75
|
+
* @returns Each named flag that appears last or is followed by another flag, in argument order.
|
|
76
|
+
*/
|
|
77
|
+
export function readMissingFlags(argv, names) {
|
|
78
|
+
return names.filter((name) => argv.includes(name) && readOption(argv, name) === undefined);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Names the flags a caller gave that the script does not read.
|
|
82
|
+
*
|
|
83
|
+
* @remarks
|
|
84
|
+
* A `--flag=value` argument is unknown, because the script reads no such form. A bare `--` ends the
|
|
85
|
+
* scan.
|
|
86
|
+
*
|
|
87
|
+
* @param argv - The arguments after the script path.
|
|
88
|
+
* @param flags - Every flag the script reads, value flags and switches alike.
|
|
89
|
+
* @returns Each argument that opens with `--` and is not a read flag, in argument order.
|
|
90
|
+
*/
|
|
91
|
+
export function readUnknownFlags(argv, flags) {
|
|
92
|
+
const end = argv.indexOf('--');
|
|
93
|
+
return (end === -1 ? argv : argv.slice(0, end)).filter((argument) => argument.startsWith('--') && !flags.includes(argument));
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Quotes an untrusted text for a diagnostic, JSON-encoded and cut to a bounded length.
|
|
97
|
+
*
|
|
98
|
+
* @param text - The text to quote.
|
|
99
|
+
* @returns The JSON string literal of the text, ending in an ellipsis inside the quotes when cut.
|
|
100
|
+
*/
|
|
101
|
+
export function quote(text) {
|
|
102
|
+
return JSON.stringify(text.length > 80 ? `${text.slice(0, 80)}…` : text);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Reads the last non-empty line of a text as a JSON object.
|
|
106
|
+
*
|
|
107
|
+
* @param text - Text whose last line a script printed as JSON.
|
|
108
|
+
* @returns The parsed object, or undefined when the last line is not a JSON object.
|
|
109
|
+
*/
|
|
110
|
+
export function parseJsonLine(text) {
|
|
111
|
+
const line = text
|
|
112
|
+
.split(/\r\n|\n/)
|
|
113
|
+
.filter((candidate) => candidate.trim() !== '')
|
|
114
|
+
.at(-1);
|
|
115
|
+
if (line === undefined)
|
|
116
|
+
return undefined;
|
|
117
|
+
try {
|
|
118
|
+
const parsed = JSON.parse(line);
|
|
119
|
+
if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
|
|
120
|
+
return Object.fromEntries(Object.entries(parsed));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch { }
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Reads a string member of a record.
|
|
128
|
+
*
|
|
129
|
+
* @param record - The record, or undefined.
|
|
130
|
+
* @param key - The member name.
|
|
131
|
+
* @returns The string, or undefined when the member is absent or not a string.
|
|
132
|
+
*/
|
|
133
|
+
export function readString(record, key) {
|
|
134
|
+
const value = record?.[key];
|
|
135
|
+
return typeof value === 'string' ? value : undefined;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Reads a finite number member of a record.
|
|
139
|
+
*
|
|
140
|
+
* @param record - The record, or undefined.
|
|
141
|
+
* @param key - The member name.
|
|
142
|
+
* @returns The number, or undefined when the member is absent, not a number, or not finite.
|
|
143
|
+
*/
|
|
144
|
+
export function readNumber(record, key) {
|
|
145
|
+
const value = record?.[key];
|
|
146
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Parses the `--cap` value into a number of seconds.
|
|
150
|
+
*
|
|
151
|
+
* @param text - The value after `--cap`, or undefined when the flag is absent.
|
|
152
|
+
* @returns The default cap when the flag is absent, the number when it is finite, above 0, and at
|
|
153
|
+
* most the longest cap, and undefined otherwise.
|
|
154
|
+
*/
|
|
155
|
+
export function parseCap(text) {
|
|
156
|
+
if (text === undefined)
|
|
157
|
+
return DEFAULT_CAP;
|
|
158
|
+
const cap = Number(text);
|
|
159
|
+
return Number.isFinite(cap) && cap > 0 && cap <= MAX_CAP_SECONDS ? cap : undefined;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Reads the version a command prints for `--version`.
|
|
163
|
+
*
|
|
164
|
+
* @param command - The executable and the arguments that precede the flag.
|
|
165
|
+
* @returns The trimmed output, or undefined when the command is empty, cannot start, or exits non-zero.
|
|
166
|
+
*/
|
|
167
|
+
export function readVersion(command) {
|
|
28
168
|
const [file, ...args] = command;
|
|
29
169
|
if (file === undefined)
|
|
30
170
|
return undefined;
|
|
31
171
|
const result = spawnSync(file, [...args, '--version'], { encoding: 'utf8', windowsHide: true });
|
|
32
172
|
return result.status === 0 ? result.stdout.trim() : undefined;
|
|
33
173
|
}
|
|
34
|
-
|
|
174
|
+
/**
|
|
175
|
+
* Resolves the Cursor CLI entry for this host.
|
|
176
|
+
*
|
|
177
|
+
* @returns The command and its version, or the reason the entry did not resolve.
|
|
178
|
+
*/
|
|
179
|
+
export function resolveCursor() {
|
|
35
180
|
if (process.platform !== 'win32') {
|
|
36
181
|
return { command: ['agent'], version: readVersion(['agent']), error: undefined };
|
|
37
182
|
}
|
|
@@ -57,7 +202,16 @@ function resolveCursor() {
|
|
|
57
202
|
}
|
|
58
203
|
return { command: [node, index], version: readVersion([node, index]), error: undefined };
|
|
59
204
|
}
|
|
60
|
-
|
|
205
|
+
/**
|
|
206
|
+
* Runs one bounded round trip through the launch and result scripts beside this one.
|
|
207
|
+
*
|
|
208
|
+
* @param bench - The bench whose journal, errors, and answer files receive the run.
|
|
209
|
+
* @param command - The bench command with its arguments and prompt.
|
|
210
|
+
* @param cap - The seconds the launcher allows the command.
|
|
211
|
+
* @param root - The directory the children run in, which holds the `tmp/<bench>` files.
|
|
212
|
+
* @returns What the launcher and the result reader reported.
|
|
213
|
+
*/
|
|
214
|
+
export function runRoundTrip(bench, command, cap, root) {
|
|
61
215
|
const journal = `tmp/${bench}/bench.jsonl`;
|
|
62
216
|
const errors = `tmp/${bench}/bench.err`;
|
|
63
217
|
const answer = `tmp/${bench}/bench-answer.md`;
|
|
@@ -71,37 +225,54 @@ function runRoundTrip(bench, command, cap) {
|
|
|
71
225
|
String(cap),
|
|
72
226
|
'--',
|
|
73
227
|
...command,
|
|
74
|
-
], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'inherit'], windowsHide: true });
|
|
75
|
-
const launched =
|
|
76
|
-
const result = spawnSync(process.execPath, [join(SCRIPTS, `result${EXTENSION}`), `--${bench}`, journal, '--out', answer], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'inherit'], windowsHide: true });
|
|
77
|
-
const read =
|
|
228
|
+
], { cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'inherit'], windowsHide: true });
|
|
229
|
+
const launched = parseJsonLine(launch.stdout);
|
|
230
|
+
const result = spawnSync(process.execPath, [join(SCRIPTS, `result${EXTENSION}`), `--${bench}`, journal, '--out', answer], { cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'inherit'], windowsHide: true });
|
|
231
|
+
const read = parseJsonLine(result.stdout);
|
|
232
|
+
const answerPath = join(root, answer);
|
|
78
233
|
return {
|
|
79
234
|
session: readString(read, 'session'),
|
|
80
|
-
answer: existsSync(
|
|
235
|
+
answer: existsSync(answerPath)
|
|
236
|
+
? readFileSync(answerPath, 'utf8').trim().slice(0, 200)
|
|
237
|
+
: undefined,
|
|
81
238
|
exit: readNumber(launched, 'exit'),
|
|
82
239
|
capped: launched?.capped === true,
|
|
83
240
|
durationMs: readNumber(launched, 'durationMs'),
|
|
84
241
|
};
|
|
85
242
|
}
|
|
86
|
-
|
|
243
|
+
/**
|
|
244
|
+
* Checks whether a round trip shows a live bench.
|
|
245
|
+
*
|
|
246
|
+
* @param summary - What the round trip observed.
|
|
247
|
+
* @returns True if the run has a session, exited 0 uncapped, and answered READY; false otherwise.
|
|
248
|
+
*/
|
|
249
|
+
export function isLive(summary) {
|
|
87
250
|
return (summary.session !== undefined &&
|
|
88
251
|
summary.exit === 0 &&
|
|
89
252
|
!summary.capped &&
|
|
90
253
|
summary.answer !== undefined &&
|
|
91
254
|
/^READY\.?$/u.test(summary.answer.trim()));
|
|
92
255
|
}
|
|
93
|
-
|
|
256
|
+
/**
|
|
257
|
+
* Probes the Cursor bench, or prints its entry when the call names `--resolve`.
|
|
258
|
+
*
|
|
259
|
+
* @param argv - The arguments after the script path.
|
|
260
|
+
* @param cap - The validated cap in seconds.
|
|
261
|
+
* @param root - The directory the round trip runs in.
|
|
262
|
+
* @returns Exit 0 when live or resolved, 3 when dark, with the report to print.
|
|
263
|
+
*/
|
|
264
|
+
export function probeCursor(argv, cap, root) {
|
|
94
265
|
const entry = resolveCursor();
|
|
95
266
|
const model = readOption(argv, '--model') ?? CURSOR_MODEL;
|
|
96
267
|
if (entry.error !== undefined || entry.version === undefined) {
|
|
97
|
-
|
|
98
|
-
return 3;
|
|
268
|
+
return { report: { bench: 'cursor', ...entry, live: false }, exit: 3 };
|
|
99
269
|
}
|
|
100
270
|
if (argv.includes('--resolve')) {
|
|
101
|
-
|
|
102
|
-
|
|
271
|
+
return {
|
|
272
|
+
report: { bench: 'cursor', command: entry.command, version: entry.version },
|
|
273
|
+
exit: 0,
|
|
274
|
+
};
|
|
103
275
|
}
|
|
104
|
-
const cap = Number(readOption(argv, '--cap') ?? DEFAULT_CAP);
|
|
105
276
|
const summary = runRoundTrip('cursor', [
|
|
106
277
|
...entry.command,
|
|
107
278
|
'-p',
|
|
@@ -112,37 +283,49 @@ function probeCursor(argv) {
|
|
|
112
283
|
'--output-format',
|
|
113
284
|
'stream-json',
|
|
114
285
|
PROMPT,
|
|
115
|
-
], cap);
|
|
286
|
+
], cap, root);
|
|
116
287
|
const live = isLive(summary);
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
288
|
+
return {
|
|
289
|
+
report: {
|
|
290
|
+
bench: 'cursor',
|
|
291
|
+
command: entry.command,
|
|
292
|
+
version: entry.version,
|
|
293
|
+
model,
|
|
294
|
+
...summary,
|
|
295
|
+
live,
|
|
296
|
+
journal: 'tmp/cursor/bench.jsonl',
|
|
297
|
+
},
|
|
298
|
+
exit: live ? 0 : 3,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Probes the Codex bench: its version, its login, and one bounded exec.
|
|
303
|
+
*
|
|
304
|
+
* @param command - The executable and the arguments that precede the Codex arguments, such as `['codex']`.
|
|
305
|
+
* @param argv - The arguments after the script path.
|
|
306
|
+
* @param cap - The validated cap in seconds.
|
|
307
|
+
* @param root - The directory the round trip runs in.
|
|
308
|
+
* @returns Exit 0 when live, 3 when dark, with the report to print.
|
|
309
|
+
*/
|
|
310
|
+
export function probeCodex(command, argv, cap, root) {
|
|
311
|
+
const [file, ...prefix] = command;
|
|
312
|
+
const version = readVersion(command);
|
|
313
|
+
const login = file === undefined
|
|
314
|
+
? undefined
|
|
315
|
+
: spawnSync(file, [...prefix, 'login', 'status'], { encoding: 'utf8', windowsHide: true });
|
|
131
316
|
const model = readOption(argv, '--model') ?? CODEX_MODEL;
|
|
132
317
|
const sandbox = readOption(argv, '--sandbox') ??
|
|
133
318
|
(process.platform === 'win32' ? 'danger-full-access' : 'read-only');
|
|
134
|
-
const status = `${login.stdout}${login.stderr}`.trim();
|
|
135
|
-
if (version === undefined || login
|
|
136
|
-
|
|
137
|
-
return 3;
|
|
319
|
+
const status = login === undefined ? '' : `${login.stdout ?? ''}${login.stderr ?? ''}`.trim();
|
|
320
|
+
if (version === undefined || login?.status !== 0) {
|
|
321
|
+
return { report: { bench: 'codex', version, login: status, live: false }, exit: 3 };
|
|
138
322
|
}
|
|
139
|
-
const cap = Number(readOption(argv, '--cap') ?? DEFAULT_CAP);
|
|
140
323
|
const summary = runRoundTrip('codex', [
|
|
141
|
-
|
|
324
|
+
...command,
|
|
142
325
|
'exec',
|
|
143
326
|
'--json',
|
|
144
327
|
'-C',
|
|
145
|
-
|
|
328
|
+
root,
|
|
146
329
|
'--sandbox',
|
|
147
330
|
sandbox,
|
|
148
331
|
'--model',
|
|
@@ -152,53 +335,105 @@ function probeCodex(argv) {
|
|
|
152
335
|
'--output-last-message',
|
|
153
336
|
'tmp/codex/bench-last.md',
|
|
154
337
|
PROMPT,
|
|
155
|
-
], cap);
|
|
338
|
+
], cap, root);
|
|
156
339
|
const live = isLive(summary);
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
340
|
+
return {
|
|
341
|
+
report: {
|
|
342
|
+
bench: 'codex',
|
|
343
|
+
version,
|
|
344
|
+
login: status,
|
|
345
|
+
model,
|
|
346
|
+
sandbox,
|
|
347
|
+
...summary,
|
|
348
|
+
live,
|
|
349
|
+
journal: 'tmp/codex/bench.jsonl',
|
|
350
|
+
},
|
|
351
|
+
exit: live ? 0 : 3,
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Probes the Claude bench: its version and one bounded print-mode prompt.
|
|
356
|
+
*
|
|
357
|
+
* @param command - The executable and the arguments that precede the Claude arguments, such as `['claude']`.
|
|
358
|
+
* @param argv - The arguments after the script path.
|
|
359
|
+
* @param cap - The validated cap in seconds.
|
|
360
|
+
* @param root - The directory the round trip runs in.
|
|
361
|
+
* @returns Exit 0 when live, 3 when dark, with the report to print.
|
|
362
|
+
*/
|
|
363
|
+
export function probeClaude(command, argv, cap, root) {
|
|
364
|
+
const version = readVersion(command);
|
|
171
365
|
const model = readOption(argv, '--model') ?? CLAUDE_MODEL;
|
|
172
366
|
if (version === undefined) {
|
|
173
|
-
|
|
174
|
-
return 3;
|
|
367
|
+
return { report: { bench: 'claude', version, live: false }, exit: 3 };
|
|
175
368
|
}
|
|
176
|
-
const
|
|
177
|
-
const summary = runRoundTrip('claude', ['claude', '-p', PROMPT, '--output-format', 'stream-json', '--verbose', '--model', model], cap);
|
|
369
|
+
const summary = runRoundTrip('claude', [...command, '-p', PROMPT, '--output-format', 'stream-json', '--verbose', '--model', model], cap, root);
|
|
178
370
|
const live = isLive(summary);
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
371
|
+
return {
|
|
372
|
+
report: {
|
|
373
|
+
bench: 'claude',
|
|
374
|
+
version,
|
|
375
|
+
model,
|
|
376
|
+
...summary,
|
|
377
|
+
live,
|
|
378
|
+
journal: 'tmp/claude/bench.jsonl',
|
|
379
|
+
},
|
|
380
|
+
exit: live ? 0 : 3,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Runs the probe the call names, against the real `codex` or `claude` command.
|
|
385
|
+
*
|
|
386
|
+
* @param argv - The arguments after the script path, which name one bench.
|
|
387
|
+
* @param cap - The validated cap in seconds.
|
|
388
|
+
* @param root - The directory the round trip runs in.
|
|
389
|
+
* @returns What the probe decided.
|
|
390
|
+
*/
|
|
391
|
+
export function selectProbe(argv, cap, root) {
|
|
392
|
+
if (argv.includes('--cursor'))
|
|
393
|
+
return probeCursor(argv, cap, root);
|
|
394
|
+
if (argv.includes('--codex'))
|
|
395
|
+
return probeCodex(['codex'], argv, cap, root);
|
|
396
|
+
return probeClaude(['claude'], argv, cap, root);
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Runs the script on its arguments.
|
|
400
|
+
*
|
|
401
|
+
* @param argv - The arguments after the script path.
|
|
402
|
+
* @returns The exit code: 0 live, 3 dark, 64 on usage.
|
|
403
|
+
*/
|
|
404
|
+
export function main(argv) {
|
|
405
|
+
const unknown = readUnknownFlags(argv, FLAGS);
|
|
406
|
+
if (unknown.length > 0) {
|
|
407
|
+
console.error(`bench: ${unknown.join(', ')} is not a flag this script reads; ${USAGE}`);
|
|
408
|
+
return 64;
|
|
409
|
+
}
|
|
410
|
+
const missing = readMissingFlags(argv, VALUE_FLAGS);
|
|
191
411
|
if (missing.length > 0) {
|
|
192
412
|
console.error(`bench: ${missing.join(', ')} given with no value`);
|
|
193
413
|
return 64;
|
|
194
414
|
}
|
|
195
|
-
if (argv.includes(
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (argv.includes('--
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
415
|
+
if (BENCH_FLAGS.filter((flag) => argv.includes(flag)).length !== 1) {
|
|
416
|
+
console.error(USAGE);
|
|
417
|
+
return 64;
|
|
418
|
+
}
|
|
419
|
+
if (argv.includes('--resolve') && !argv.includes('--cursor')) {
|
|
420
|
+
console.error('bench: --resolve belongs to --cursor and reads nothing for another bench');
|
|
421
|
+
return 64;
|
|
422
|
+
}
|
|
423
|
+
const sandbox = readOption(argv, '--sandbox');
|
|
424
|
+
if (sandbox !== undefined && !SANDBOX_MODES.includes(sandbox)) {
|
|
425
|
+
console.error(`bench: --sandbox takes one of ${SANDBOX_MODES.join(', ')}; got ${quote(sandbox)}`);
|
|
426
|
+
return 64;
|
|
427
|
+
}
|
|
428
|
+
const cap = parseCap(readOption(argv, '--cap'));
|
|
429
|
+
if (cap === undefined) {
|
|
430
|
+
console.error(`bench: --cap takes a number of seconds above 0 and at most ${String(MAX_CAP_SECONDS)}`);
|
|
431
|
+
return 64;
|
|
432
|
+
}
|
|
433
|
+
const probe = selectProbe(argv, cap, process.cwd());
|
|
434
|
+
console.log(JSON.stringify(probe.report));
|
|
435
|
+
return probe.exit;
|
|
436
|
+
}
|
|
437
|
+
if (process.argv[1] !== undefined && realpathSync(process.argv[1]) === import.meta.filename) {
|
|
438
|
+
process.exitCode = main(process.argv.slice(2));
|
|
203
439
|
}
|
|
204
|
-
process.exitCode = main(process.argv.slice(2));
|
|
@@ -8,19 +8,76 @@
|
|
|
8
8
|
// edited. The second form reads every backticked token carrying a slash (a backslash counts and is
|
|
9
9
|
// read as a slash), strips a `:line` or
|
|
10
10
|
// `:line-line` suffix, resolves it from the working directory, and prints the ones that do not
|
|
11
|
-
// exist; a bare filename is outside the check.
|
|
12
|
-
|
|
11
|
+
// exist; a bare filename is outside the check. The two forms are two modes; name one. The script
|
|
12
|
+
// refuses a flag it does not read, the `--flag=value` form included. Exit 3 when any is missing, 64
|
|
13
|
+
// on usage.
|
|
14
|
+
import { existsSync, mkdirSync, readFileSync, realpathSync, statSync, writeFileSync } from 'node:fs';
|
|
13
15
|
import { dirname, join, resolve } from 'node:path';
|
|
14
16
|
import { fileURLToPath } from 'node:url';
|
|
15
|
-
|
|
16
|
-
const TEMPLATE = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..', '..', 'templates', 'brief.md');
|
|
17
|
-
|
|
17
|
+
/** Names the brief template, found from this script's own location. */
|
|
18
|
+
export const TEMPLATE = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..', '..', 'templates', 'brief.md');
|
|
19
|
+
/** Maps each lane name to the directory its briefs are written under. */
|
|
20
|
+
export const LANES = Object.freeze({
|
|
18
21
|
units: 'tmp/units',
|
|
19
22
|
cursor: 'tmp/cursor',
|
|
20
23
|
codex: 'tmp/codex',
|
|
21
24
|
claude: 'tmp/claude',
|
|
22
25
|
});
|
|
23
|
-
|
|
26
|
+
/** Lists the flags that take a value. */
|
|
27
|
+
export const VALUE_FLAGS = ['--unit', '--lane', '--subject', '--check'];
|
|
28
|
+
/** Lists every flag the script reads, value flags and switches alike. */
|
|
29
|
+
export const FLAGS = VALUE_FLAGS;
|
|
30
|
+
/** Holds the usage line printed when a call names no mode. */
|
|
31
|
+
export const USAGE = 'usage: brief.ts --unit UNIT --lane units|cursor|codex|claude [--subject TEXT] | --check PATH';
|
|
32
|
+
/**
|
|
33
|
+
* Reads the value that follows a flag.
|
|
34
|
+
*
|
|
35
|
+
* @remarks
|
|
36
|
+
* The first occurrence of a flag wins, a following value that opens with `--` is a missing value,
|
|
37
|
+
* and the `--flag=value` form is not read.
|
|
38
|
+
*
|
|
39
|
+
* @param argv - The arguments after the script path.
|
|
40
|
+
* @param name - The flag, such as `--unit`.
|
|
41
|
+
* @returns The value after the flag, or undefined when the flag is absent, last, or followed by
|
|
42
|
+
* another flag.
|
|
43
|
+
*/
|
|
44
|
+
export function readOption(argv, name) {
|
|
45
|
+
const index = argv.indexOf(name);
|
|
46
|
+
const value = index === -1 ? undefined : argv[index + 1];
|
|
47
|
+
return value === undefined || value.startsWith('--') ? undefined : value;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Names the value flags a caller gave with no value.
|
|
51
|
+
*
|
|
52
|
+
* @param argv - The arguments after the script path.
|
|
53
|
+
* @param names - The flags that take a value.
|
|
54
|
+
* @returns Each named flag that appears last or is followed by another flag, in argument order.
|
|
55
|
+
*/
|
|
56
|
+
export function readMissingFlags(argv, names) {
|
|
57
|
+
return names.filter((name) => argv.includes(name) && readOption(argv, name) === undefined);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Names the flags a caller gave that the script does not read.
|
|
61
|
+
*
|
|
62
|
+
* @remarks
|
|
63
|
+
* A `--flag=value` argument is unknown, because the script reads no such form. A bare `--` ends the
|
|
64
|
+
* scan.
|
|
65
|
+
*
|
|
66
|
+
* @param argv - The arguments after the script path.
|
|
67
|
+
* @param flags - Every flag the script reads, value flags and switches alike.
|
|
68
|
+
* @returns Each argument that opens with `--` and is not a read flag, in argument order.
|
|
69
|
+
*/
|
|
70
|
+
export function readUnknownFlags(argv, flags) {
|
|
71
|
+
const end = argv.indexOf('--');
|
|
72
|
+
return (end === -1 ? argv : argv.slice(0, end)).filter((argument) => argument.startsWith('--') && !flags.includes(argument));
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Checks whether a backticked token reads as a repository path.
|
|
76
|
+
*
|
|
77
|
+
* @param token - The text between two backticks, with its line suffix already removed.
|
|
78
|
+
* @returns True if the token carries a slash and no URL, placeholder, glob, or flag form; false otherwise.
|
|
79
|
+
*/
|
|
80
|
+
export function readsAsPath(token) {
|
|
24
81
|
if (token.includes('://') || /[<>*{}\s]/u.test(token))
|
|
25
82
|
return false;
|
|
26
83
|
if (/[A-Z]{2,}_[A-Z]/u.test(token))
|
|
@@ -31,17 +88,37 @@ function readsAsPath(token) {
|
|
|
31
88
|
return false;
|
|
32
89
|
return token.includes('/');
|
|
33
90
|
}
|
|
34
|
-
|
|
91
|
+
/**
|
|
92
|
+
* Extracts the slash-bearing paths a brief names.
|
|
93
|
+
*
|
|
94
|
+
* @param content - The brief text.
|
|
95
|
+
* @returns Each distinct path in sorted order, a backslash read as a slash and a line suffix removed.
|
|
96
|
+
*/
|
|
97
|
+
export function extractPaths(content) {
|
|
35
98
|
const found = new Set([...content.matchAll(/`([^`\n]+)`/gu)]
|
|
36
99
|
.map((match) => (match[1] ?? '').replace(/\\/gu, '/').replace(/:\d+(?:[-–]\d+)?$/u, ''))
|
|
37
100
|
.filter(readsAsPath));
|
|
38
101
|
return [...found].sort();
|
|
39
102
|
}
|
|
40
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Resolves the first brief path for a unit that no file occupies.
|
|
105
|
+
*
|
|
106
|
+
* @param directory - The lane directory.
|
|
107
|
+
* @param unit - The unit name.
|
|
108
|
+
* @param count - The brief number to try first. Default: 1.
|
|
109
|
+
* @returns `<unit>-brief.md` for the first brief and `<unit>-brief-<n>.md` for a successor.
|
|
110
|
+
*/
|
|
111
|
+
export function resolveBriefPath(directory, unit, count = 1) {
|
|
41
112
|
const path = join(directory, count === 1 ? `${unit}-brief.md` : `${unit}-brief-${count}.md`);
|
|
42
113
|
return existsSync(path) ? resolveBriefPath(directory, unit, count + 1) : path;
|
|
43
114
|
}
|
|
44
|
-
|
|
115
|
+
/**
|
|
116
|
+
* Writes a brief from the template.
|
|
117
|
+
*
|
|
118
|
+
* @param argv - The arguments after the script path.
|
|
119
|
+
* @returns The exit code: 0 on success, 64 on usage or a missing template.
|
|
120
|
+
*/
|
|
121
|
+
export function writeBrief(argv) {
|
|
45
122
|
const unit = readOption(argv, '--unit');
|
|
46
123
|
const lane = readOption(argv, '--lane');
|
|
47
124
|
const directory = lane === undefined ? undefined : LANES[lane];
|
|
@@ -63,7 +140,13 @@ function writeBrief(argv) {
|
|
|
63
140
|
console.log(JSON.stringify({ brief: path, template: TEMPLATE }));
|
|
64
141
|
return 0;
|
|
65
142
|
}
|
|
66
|
-
|
|
143
|
+
/**
|
|
144
|
+
* Checks that every path a brief names exists.
|
|
145
|
+
*
|
|
146
|
+
* @param path - The brief file.
|
|
147
|
+
* @returns The exit code: 0 when every path exists, 3 when one is missing, 64 when the brief is not a file.
|
|
148
|
+
*/
|
|
149
|
+
export function checkBrief(path) {
|
|
67
150
|
if (!existsSync(path) || !statSync(path).isFile()) {
|
|
68
151
|
console.error(`brief: ${path} is not a file`);
|
|
69
152
|
return 64;
|
|
@@ -73,8 +156,19 @@ function checkBrief(path) {
|
|
|
73
156
|
console.log(JSON.stringify({ brief: path, checked: paths.length, missing }));
|
|
74
157
|
return missing.length === 0 ? 0 : 3;
|
|
75
158
|
}
|
|
76
|
-
|
|
77
|
-
|
|
159
|
+
/**
|
|
160
|
+
* Runs the script on its arguments.
|
|
161
|
+
*
|
|
162
|
+
* @param argv - The arguments after the script path.
|
|
163
|
+
* @returns The exit code: 0 on success, 3 when a named path is missing, 64 on usage.
|
|
164
|
+
*/
|
|
165
|
+
export function main(argv) {
|
|
166
|
+
const unknown = readUnknownFlags(argv, FLAGS);
|
|
167
|
+
if (unknown.length > 0) {
|
|
168
|
+
console.error(`brief: ${unknown.join(', ')} is not a flag this script reads; ${USAGE}`);
|
|
169
|
+
return 64;
|
|
170
|
+
}
|
|
171
|
+
const missing = readMissingFlags(argv, VALUE_FLAGS);
|
|
78
172
|
if (missing.length > 0) {
|
|
79
173
|
console.error(`brief: ${missing.join(', ')} given with no value`);
|
|
80
174
|
return 64;
|
|
@@ -88,7 +182,9 @@ function main(argv) {
|
|
|
88
182
|
return checkBrief(check);
|
|
89
183
|
if (argv.includes('--unit'))
|
|
90
184
|
return writeBrief(argv);
|
|
91
|
-
console.error(
|
|
185
|
+
console.error(USAGE);
|
|
92
186
|
return 64;
|
|
93
187
|
}
|
|
94
|
-
process.
|
|
188
|
+
if (process.argv[1] !== undefined && realpathSync(process.argv[1]) === import.meta.filename) {
|
|
189
|
+
process.exitCode = main(process.argv.slice(2));
|
|
190
|
+
}
|