@cairn-tool/cairn 3.4.0 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/README.md +6 -4
  2. package/dist/agent/manifest.d.ts +17 -0
  3. package/dist/agent/manifest.js +59 -1
  4. package/dist/agent/manifest.js.map +1 -1
  5. package/dist/agent/parser.js +209 -24
  6. package/dist/agent/parser.js.map +1 -1
  7. package/dist/agent/render.js +64 -3
  8. package/dist/agent/render.js.map +1 -1
  9. package/dist/cli.js +46 -2
  10. package/dist/cli.js.map +1 -1
  11. package/dist/commands/qa-list.d.ts +7 -0
  12. package/dist/commands/qa-list.js +69 -0
  13. package/dist/commands/qa-list.js.map +1 -0
  14. package/dist/commands/qa-run.d.ts +3 -0
  15. package/dist/commands/qa-run.js +106 -0
  16. package/dist/commands/qa-run.js.map +1 -0
  17. package/dist/commands/qa-summary.d.ts +6 -0
  18. package/dist/commands/qa-summary.js +38 -0
  19. package/dist/commands/qa-summary.js.map +1 -0
  20. package/dist/config-schema.d.ts +2 -0
  21. package/dist/config-schema.js +4 -0
  22. package/dist/config-schema.js.map +1 -1
  23. package/dist/config.d.ts +12 -2
  24. package/dist/config.js +36 -7
  25. package/dist/config.js.map +1 -1
  26. package/dist/contract/registry.js +43 -0
  27. package/dist/contract/registry.js.map +1 -1
  28. package/dist/contract/schemas/index.js +2 -0
  29. package/dist/contract/schemas/index.js.map +1 -1
  30. package/dist/contract/schemas/qa.d.ts +2 -0
  31. package/dist/contract/schemas/qa.js +111 -0
  32. package/dist/contract/schemas/qa.js.map +1 -0
  33. package/dist/qa/agent.d.ts +25 -0
  34. package/dist/qa/agent.js +142 -0
  35. package/dist/qa/agent.js.map +1 -0
  36. package/dist/qa/agents/claude-code.d.ts +2 -0
  37. package/dist/qa/agents/claude-code.js +121 -0
  38. package/dist/qa/agents/claude-code.js.map +1 -0
  39. package/dist/qa/agents/cursor.d.ts +5 -0
  40. package/dist/qa/agents/cursor.js +143 -0
  41. package/dist/qa/agents/cursor.js.map +1 -0
  42. package/dist/qa/agents/index.d.ts +32 -0
  43. package/dist/qa/agents/index.js +83 -0
  44. package/dist/qa/agents/index.js.map +1 -0
  45. package/dist/qa/agents/types.d.ts +61 -0
  46. package/dist/qa/agents/types.js +18 -0
  47. package/dist/qa/agents/types.js.map +1 -0
  48. package/dist/qa/async.d.ts +27 -0
  49. package/dist/qa/async.js +91 -0
  50. package/dist/qa/async.js.map +1 -0
  51. package/dist/qa/casefile.d.ts +35 -0
  52. package/dist/qa/casefile.js +181 -0
  53. package/dist/qa/casefile.js.map +1 -0
  54. package/dist/qa/cases.d.ts +51 -0
  55. package/dist/qa/cases.js +144 -0
  56. package/dist/qa/cases.js.map +1 -0
  57. package/dist/qa/config.d.ts +50 -0
  58. package/dist/qa/config.js +105 -0
  59. package/dist/qa/config.js.map +1 -0
  60. package/dist/qa/demo.d.ts +10 -0
  61. package/dist/qa/demo.js +194 -0
  62. package/dist/qa/demo.js.map +1 -0
  63. package/dist/qa/format.d.ts +38 -0
  64. package/dist/qa/format.js +208 -0
  65. package/dist/qa/format.js.map +1 -0
  66. package/dist/qa/harness.d.ts +68 -0
  67. package/dist/qa/harness.js +528 -0
  68. package/dist/qa/harness.js.map +1 -0
  69. package/dist/qa/keys.d.ts +12 -0
  70. package/dist/qa/keys.js +96 -0
  71. package/dist/qa/keys.js.map +1 -0
  72. package/dist/qa/layout.d.ts +102 -0
  73. package/dist/qa/layout.js +278 -0
  74. package/dist/qa/layout.js.map +1 -0
  75. package/dist/qa/lock.d.ts +5 -0
  76. package/dist/qa/lock.js +114 -0
  77. package/dist/qa/lock.js.map +1 -0
  78. package/dist/qa/options.d.ts +38 -0
  79. package/dist/qa/options.js +165 -0
  80. package/dist/qa/options.js.map +1 -0
  81. package/dist/qa/outcome.d.ts +30 -0
  82. package/dist/qa/outcome.js +162 -0
  83. package/dist/qa/outcome.js.map +1 -0
  84. package/dist/qa/reports.d.ts +51 -0
  85. package/dist/qa/reports.js +121 -0
  86. package/dist/qa/reports.js.map +1 -0
  87. package/dist/qa/schedule.d.ts +55 -0
  88. package/dist/qa/schedule.js +145 -0
  89. package/dist/qa/schedule.js.map +1 -0
  90. package/dist/qa/screen.d.ts +31 -0
  91. package/dist/qa/screen.js +146 -0
  92. package/dist/qa/screen.js.map +1 -0
  93. package/dist/qa/slot.d.ts +28 -0
  94. package/dist/qa/slot.js +126 -0
  95. package/dist/qa/slot.js.map +1 -0
  96. package/dist/qa/tracker.d.ts +36 -0
  97. package/dist/qa/tracker.js +357 -0
  98. package/dist/qa/tracker.js.map +1 -0
  99. package/dist/query/execute.js +0 -0
  100. package/dist/query/execute.js.map +1 -1
  101. package/dist/scripts/resolve.js +1 -4
  102. package/dist/scripts/resolve.js.map +1 -1
  103. package/dist/usage/providers/cursor.js +1 -1
  104. package/dist/usage/providers/cursor.js.map +1 -1
  105. package/package.json +3 -2
@@ -0,0 +1,194 @@
1
+ import { RENDER_HZ } from "./config.js";
2
+ import { frame } from "./layout.js";
3
+ import { Screen } from "./screen.js";
4
+ import { sleep } from "./async.js";
5
+ const TRANSCRIPT = [
6
+ "\x1b[2mmodel cursor-grok-4.6-high\x1b[0m",
7
+ "\x1b[2m> \x1b[0mReading the plan to work out which files this case touches.",
8
+ "\x1b[36m! \x1b[0mRead work/tc-runs/_plans/tc-11.yaml",
9
+ "» Starting on the migration checks described in the plan.",
10
+ "\x1b[36m! \x1b[0mShell npm run build && npm test -- --reporter=dot",
11
+ "\x1b[31mx \x1b[0mShell npm run build && npm test -- --reporter=dot",
12
+ "\x1b[2m> \x1b[0mThe build failed on a type error; re-reading the offending module.",
13
+ "\x1b[36m! \x1b[0mGlob src/**/*.{ts,tsx}",
14
+ "» ⚠️ One expected result could not be confirmed — recording it as UNVERIFIED.",
15
+ "\x1b[36m! \x1b[0mWrite work/tc-runs/tc-11-temp/_results.md",
16
+ ];
17
+ const usage = (n) => ({
18
+ inputTokens: 1200 * n + 431,
19
+ outputTokens: 90 * n + 17,
20
+ cacheReadTokens: 5600 * n,
21
+ cacheWriteTokens: n % 3 === 0 ? 0 : 128 * n,
22
+ });
23
+ const MODELS = ["cursor-grok-4.6-high", "composer-2.5", "cursor-grok-4.6-high", "composer-2.5"];
24
+ function demoPanes() {
25
+ const shapes = [
26
+ ["run", false],
27
+ ["run", true],
28
+ ["ok", true],
29
+ ["error", false],
30
+ ["timeout", false],
31
+ ["no-output", false],
32
+ ["no-folder", false],
33
+ ["idle", false],
34
+ ];
35
+ return shapes.map(([status, withUsage], i) => ({
36
+ slot: i,
37
+ name: status === "idle" ? null : `tc-${i * 7 + 11}`,
38
+ model: MODELS[i % MODELS.length],
39
+ status,
40
+ elapsedSeconds: status === "idle" ? 0 : 37 + i * 211,
41
+ tools: status === "idle" ? 0 : 3 + i * 17,
42
+ streamedBytes: status === "idle" ? 0 : 1024 * (i + 1) * 37,
43
+ usage: withUsage ? usage(i + 1) : null,
44
+ note: status === "timeout" ? "agent exceeded the 60 minute deadline" : "",
45
+ transcript: status === "run" || status === "ok" ? TRANSCRIPT : TRANSCRIPT.slice(0, 3),
46
+ thinking: status === "run" ? "Now checking the remaining assertions" : "",
47
+ }));
48
+ }
49
+ function demoTable() {
50
+ const rows = [];
51
+ for (let i = 1; i <= 40; i += 1) {
52
+ const kind = i % 5;
53
+ if (kind === 0) {
54
+ rows.push({
55
+ name: `tc-${i}`,
56
+ status: "Prior",
57
+ outcome: "Passed",
58
+ duration: "—",
59
+ tools: "—",
60
+ inn: "—",
61
+ out: "—",
62
+ cache_r: "—",
63
+ cache_w: "—",
64
+ });
65
+ }
66
+ else if (kind === 1) {
67
+ rows.push({
68
+ name: `tc-${i}`,
69
+ status: "Pending",
70
+ outcome: "—",
71
+ duration: "—",
72
+ tools: "—",
73
+ inn: "—",
74
+ out: "—",
75
+ cache_r: "—",
76
+ cache_w: "—",
77
+ });
78
+ }
79
+ else if (kind === 2) {
80
+ rows.push({
81
+ name: `tc-${i}`,
82
+ status: "Running",
83
+ outcome: "—",
84
+ duration: "04:31",
85
+ tools: "87",
86
+ inn: "—",
87
+ out: "—",
88
+ cache_r: "—",
89
+ cache_w: "—",
90
+ });
91
+ }
92
+ else if (kind === 3) {
93
+ rows.push({
94
+ name: `tc-${i}`,
95
+ status: "Done",
96
+ outcome: "Passed",
97
+ duration: "12:04",
98
+ tools: "143",
99
+ inn: "1.2k",
100
+ out: "340",
101
+ cache_r: "5.6k",
102
+ cache_w: "128",
103
+ });
104
+ }
105
+ else {
106
+ rows.push({
107
+ name: `tc-${i}`,
108
+ status: "Failed",
109
+ outcome: i % 3 === 0 ? "Unverified" : "Timeout",
110
+ duration: "1:02:03",
111
+ tools: "12",
112
+ inn: "1.5M",
113
+ out: "12.3k",
114
+ cache_r: "999",
115
+ cache_w: "0",
116
+ });
117
+ }
118
+ }
119
+ return rows;
120
+ }
121
+ const TOTALS = {
122
+ name: "Total",
123
+ status: "—",
124
+ outcome: "—",
125
+ duration: "3:41:12",
126
+ tools: "1834",
127
+ inn: "1.5M",
128
+ out: "48.2k",
129
+ cache_r: "212.4k",
130
+ cache_w: "3.1k",
131
+ };
132
+ /** A canned frame, so the TUI can be eyeballed at any size without launching an agent. */
133
+ export async function runDemo(opts) {
134
+ const panes = demoPanes();
135
+ const table = demoTable();
136
+ const screen = new Screen(opts.once !== true);
137
+ if (opts.table)
138
+ screen.mode = "table";
139
+ const shown = panes.slice(0, opts.panes ?? panes.length);
140
+ const build = () => {
141
+ screen.visibleSlots = shown.map((p) => p.slot);
142
+ return {
143
+ mode: screen.mode,
144
+ zoom: screen.zoom,
145
+ scroll: screen.scroll,
146
+ running: shown.filter((p) => p.status === "run").length,
147
+ parallel: panes.length,
148
+ queued: 27,
149
+ paused: false,
150
+ draining: false,
151
+ panes: shown,
152
+ panesNote: "waiting on tag: build",
153
+ table,
154
+ totals: TOTALS,
155
+ statusLeft: " 2+27/40 1:14:22 in 1.5M out 48.2k cache-r 212.4k cache-w 3.1k",
156
+ };
157
+ };
158
+ const size = () => {
159
+ const [r, c] = screen.size();
160
+ return [opts.rows ?? r, opts.cols ?? c];
161
+ };
162
+ if (opts.once === true || !screen.enabled) {
163
+ const [rows, cols] = [opts.rows ?? 24, opts.cols ?? 80];
164
+ process.stdout.write(`${frame(build(), rows, cols).lines.join("\n")}\n`);
165
+ return 0;
166
+ }
167
+ let stop = false;
168
+ screen.enter();
169
+ const restore = () => screen.restore();
170
+ process.once("exit", restore);
171
+ screen.listen({
172
+ requestStop: () => {
173
+ stop = true;
174
+ },
175
+ requestDrain: () => { },
176
+ togglePause: () => { },
177
+ });
178
+ try {
179
+ while (!stop) {
180
+ const [rows, cols] = size();
181
+ const laid = frame(build(), rows, cols);
182
+ screen.scroll = laid.scroll;
183
+ screen.page = laid.page;
184
+ screen.render(laid.lines, rows, cols);
185
+ await sleep(1000 / RENDER_HZ);
186
+ }
187
+ }
188
+ finally {
189
+ screen.restore();
190
+ process.off("exit", restore);
191
+ }
192
+ return 0;
193
+ }
194
+ //# sourceMappingURL=demo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"demo.js","sourceRoot":"","sources":["../../src/qa/demo.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ export declare const RESET = "\u001B[0m";
2
+ export declare const BOLD = "\u001B[1m";
3
+ export declare const DIM = "\u001B[2m";
4
+ export declare const REV = "\u001B[7m";
5
+ export declare const RED = "\u001B[31m";
6
+ export declare const GREEN = "\u001B[32m";
7
+ export declare const YELLOW = "\u001B[33m";
8
+ export declare const CYAN = "\u001B[36m";
9
+ /** Terminal cell width of one code point: 0, 1, or 2. Python: char_width. */
10
+ export declare function charWidth(cp: number): 0 | 1 | 2;
11
+ export interface Token {
12
+ readonly text: string;
13
+ readonly w: number;
14
+ readonly ansi: boolean;
15
+ }
16
+ /**
17
+ * One pass over `text`, yielding either a whole CSI sequence (width 0) or a single CODE POINT.
18
+ * Every width-aware function goes through here — Python iterates code points, and JS string
19
+ * indexing iterates UTF-16 units, so a naive port splits every astral emoji in half.
20
+ */
21
+ export declare function tokens(text: string): Generator<Token>;
22
+ /** Visible cell width, ignoring CSI. Wide glyphs count as 2. */
23
+ export declare function displayWidth(text: string): number;
24
+ /** Keep at most `width` display cells, preserving CSI, and always reset. */
25
+ export declare function clipAnsi(text: string, width: number): string;
26
+ /** Full-width reverse-video bar. Re-opens reverse after any RESET in `text`. */
27
+ export declare function invertBar(text: string, width: number): string;
28
+ export declare function padClip(text: string, width: number): string;
29
+ /** Wrap `text` to `width` display cells, breaking on spaces when possible. */
30
+ export declare function wrapAnsi(text: string, width: number): string[];
31
+ export declare function fmtDuration(seconds: number): string;
32
+ export declare function fmtCount(n: number): string;
33
+ export declare function fmtBytes(n: number): string;
34
+ export declare function flatten(text: string): string;
35
+ /** Python: `not line.strip()` */
36
+ export declare function isBlank(text: string): boolean;
37
+ /** Python: text.strip() — the wide whitespace set. */
38
+ export declare function pyStrip(text: string): string;
@@ -0,0 +1,208 @@
1
+ import { eastAsianWidthType } from "get-east-asian-width";
2
+ export const RESET = "\x1b[0m";
3
+ export const BOLD = "\x1b[1m";
4
+ export const DIM = "\x1b[2m";
5
+ export const REV = "\x1b[7m";
6
+ export const RED = "\x1b[31m";
7
+ export const GREEN = "\x1b[32m";
8
+ export const YELLOW = "\x1b[33m";
9
+ export const CYAN = "\x1b[36m";
10
+ /** Sticky so `exec` anchors at `lastIndex`, matching Python's `CSI.match(text, i)`. */
11
+ // eslint-disable-next-line no-control-regex
12
+ const CSI = /\x1b\[[0-9;]*[A-Za-z]/y;
13
+ /** Python: unicodedata.category(ch) in ("Mn", "Me", "Cf") */
14
+ const ZERO_CATEGORY = /^[\p{Mn}\p{Me}\p{Cf}]$/u;
15
+ /**
16
+ * Python's whitespace set for `str` — what `str.strip()` and `re.sub(r"\s+")` act on.
17
+ * Spelled out rather than reusing JS `\s`, which differs at both ends: it omits
18
+ * \x1c-\x1f and \x85, and it *adds* \ufeff (the BOM), which Python treats as ordinary.
19
+ */
20
+ const PY_WS = "\\t\\n\\v\\f\\r\\x1c\\x1d\\x1e\\x1f\\x20\\x85\\xa0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000";
21
+ const PY_SPACE = new RegExp(`[${PY_WS}]`, "u");
22
+ const PY_SPACE_RUN = new RegExp(`[${PY_WS}]+`, "gu");
23
+ const PY_BLANK = new RegExp(`^[${PY_WS}]*$`, "u");
24
+ const bmpCache = new Int8Array(0x10000).fill(-1);
25
+ function computeWidth(cp) {
26
+ if (cp === 0)
27
+ return 0;
28
+ if (ZERO_CATEGORY.test(String.fromCodePoint(cp)))
29
+ return 0;
30
+ if (cp < 32 || (cp >= 0x7f && cp < 0xa0))
31
+ return 0;
32
+ const eaw = eastAsianWidthType(cp);
33
+ if (eaw === "fullwidth" || eaw === "wide")
34
+ return 2;
35
+ if (cp >= 0x1f300)
36
+ return 2;
37
+ return 1;
38
+ }
39
+ /** Terminal cell width of one code point: 0, 1, or 2. Python: char_width. */
40
+ export function charWidth(cp) {
41
+ if (cp < 0x10000) {
42
+ const cached = bmpCache[cp];
43
+ if (cached >= 0)
44
+ return cached;
45
+ const width = computeWidth(cp);
46
+ bmpCache[cp] = width;
47
+ return width;
48
+ }
49
+ return computeWidth(cp);
50
+ }
51
+ /**
52
+ * One pass over `text`, yielding either a whole CSI sequence (width 0) or a single CODE POINT.
53
+ * Every width-aware function goes through here — Python iterates code points, and JS string
54
+ * indexing iterates UTF-16 units, so a naive port splits every astral emoji in half.
55
+ */
56
+ export function* tokens(text) {
57
+ let i = 0;
58
+ while (i < text.length) {
59
+ if (text[i] === "\x1b") {
60
+ CSI.lastIndex = i;
61
+ const match = CSI.exec(text);
62
+ if (match) {
63
+ yield { text: match[0], w: 0, ansi: true };
64
+ i = CSI.lastIndex;
65
+ continue;
66
+ }
67
+ }
68
+ const cp = text.codePointAt(i);
69
+ const s = String.fromCodePoint(cp);
70
+ yield { text: s, w: charWidth(cp), ansi: false };
71
+ i += s.length;
72
+ }
73
+ }
74
+ /** Visible cell width, ignoring CSI. Wide glyphs count as 2. */
75
+ export function displayWidth(text) {
76
+ let width = 0;
77
+ for (const token of tokens(text))
78
+ width += token.w;
79
+ return width;
80
+ }
81
+ /** Keep at most `width` display cells, preserving CSI, and always reset. */
82
+ export function clipAnsi(text, width) {
83
+ if (width <= 0)
84
+ return RESET;
85
+ const out = [];
86
+ let vis = 0;
87
+ for (const token of tokens(text)) {
88
+ if (vis >= width)
89
+ break;
90
+ if (token.ansi) {
91
+ out.push(token.text);
92
+ continue;
93
+ }
94
+ if (vis + token.w > width)
95
+ break;
96
+ out.push(token.text);
97
+ vis += token.w;
98
+ }
99
+ return out.join("") + RESET;
100
+ }
101
+ /** Full-width reverse-video bar. Re-opens reverse after any RESET in `text`. */
102
+ export function invertBar(text, width) {
103
+ const clipped = clipAnsi(text, width);
104
+ const pad = " ".repeat(Math.max(0, width - displayWidth(clipped)));
105
+ const body = clipped.replaceAll(RESET, RESET + REV) + pad;
106
+ return `${REV}${body}${RESET}`;
107
+ }
108
+ export function padClip(text, width) {
109
+ const clipped = clipAnsi(text, width);
110
+ const pad = width - displayWidth(clipped);
111
+ return pad > 0 ? clipped + " ".repeat(pad) : clipped;
112
+ }
113
+ /** Python: text.strip(" ") / text.rstrip(" ") — U+0020 ONLY, never .trim(). */
114
+ const stripSpaces = (s) => s.replace(/^ +/, "").replace(/ +$/, "");
115
+ const rstripSpaces = (s) => s.replace(/ +$/, "");
116
+ /** Wrap `text` to `width` display cells, breaking on spaces when possible. */
117
+ export function wrapAnsi(text, width) {
118
+ if (width < 1)
119
+ return [];
120
+ if (displayWidth(text) <= width)
121
+ return text ? [text] : [];
122
+ const toks = [...tokens(text)];
123
+ const lines = [];
124
+ let current = [];
125
+ let currentW = 0;
126
+ let spaceAt = -1;
127
+ const emit = (parts) => {
128
+ const line = parts.map((t) => t.text).join("");
129
+ if (stripSpaces(line) || parts.some((t) => t.ansi))
130
+ lines.push(rstripSpaces(line));
131
+ };
132
+ for (const token of toks) {
133
+ const { w } = token;
134
+ if (w > 0 && currentW + w > width && current.length > 0) {
135
+ if (spaceAt >= 0) {
136
+ emit(current.slice(0, spaceAt + 1));
137
+ const rest = current.slice(spaceAt + 1);
138
+ current = rest.filter((t) => !(t.text === " " && t.w === 1));
139
+ currentW = current.reduce((sum, t) => sum + t.w, 0);
140
+ spaceAt = -1;
141
+ }
142
+ else {
143
+ emit(current);
144
+ current = [];
145
+ currentW = 0;
146
+ spaceAt = -1;
147
+ }
148
+ if (w > 0 && currentW + w > width) {
149
+ emit([token]);
150
+ continue;
151
+ }
152
+ }
153
+ current.push(token);
154
+ currentW += w;
155
+ if (token.text === " " && w === 1)
156
+ spaceAt = current.length - 1;
157
+ }
158
+ if (current.length > 0)
159
+ emit(current);
160
+ return lines.length > 0 ? lines : text ? [text] : [];
161
+ }
162
+ export function fmtDuration(seconds) {
163
+ const total = Math.max(0, Math.trunc(seconds));
164
+ const hours = Math.floor(total / 3600);
165
+ const minutes = Math.floor((total % 3600) / 60);
166
+ const secs = total % 60;
167
+ const pad = (n) => String(n).padStart(2, "0");
168
+ return hours ? `${hours}:${pad(minutes)}:${pad(secs)}` : `${pad(minutes)}:${pad(secs)}`;
169
+ }
170
+ /**
171
+ * Python f"{x:.1f}". Both `toFixed` and Python's formatter round the *exact* double, and they
172
+ * only disagree on a true midpoint — which needs value = odd/20, never a dyadic rational, so it
173
+ * cannot arise from the n/1000 and n/1000000 divisions here. Do NOT pre-scale by 10: that
174
+ * collapses 1.05000000000000004 to exactly 10.5 and rounds the wrong way.
175
+ */
176
+ const fixed1 = (value) => value.toFixed(1);
177
+ export function fmtCount(n) {
178
+ if (n >= 1_000_000)
179
+ return `${fixed1(n / 1_000_000)}M`;
180
+ if (n >= 1_000)
181
+ return `${fixed1(n / 1_000)}k`;
182
+ return String(n);
183
+ }
184
+ export function fmtBytes(n) {
185
+ if (n >= 1_000_000)
186
+ return `${fixed1(n / 1_000_000)} MB`;
187
+ if (n >= 1_000)
188
+ return `${Math.round(n / 1_000)} KB`;
189
+ return `${n} B`;
190
+ }
191
+ export function flatten(text) {
192
+ return text.replace(PY_SPACE_RUN, " ").replace(/^ +| +$/g, "");
193
+ }
194
+ /** Python: `not line.strip()` */
195
+ export function isBlank(text) {
196
+ return PY_BLANK.test(text);
197
+ }
198
+ /** Python: text.strip() — the wide whitespace set. */
199
+ export function pyStrip(text) {
200
+ let start = 0;
201
+ let end = text.length;
202
+ while (start < end && PY_SPACE.test(text[start]))
203
+ start += 1;
204
+ while (end > start && PY_SPACE.test(text[end - 1]))
205
+ end -= 1;
206
+ return text.slice(start, end);
207
+ }
208
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/qa/format.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ import type { Options } from "./config.js";
2
+ import type { Case } from "./cases.js";
3
+ import type { ViewModel } from "./layout.js";
4
+ import type { Rec, SessionInfo, SessionStats } from "./reports.js";
5
+ /** datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S") */
6
+ export declare function runId(d?: Date): string;
7
+ export declare class Harness {
8
+ private readonly options;
9
+ private readonly cases;
10
+ private readonly allCases;
11
+ private readonly prior;
12
+ private readonly byName;
13
+ private sched;
14
+ private readonly slots;
15
+ private readonly live;
16
+ private readonly records;
17
+ private readonly gate;
18
+ private readonly cleanup;
19
+ private readonly screen;
20
+ private readonly startedMs;
21
+ private readonly runId;
22
+ private readonly pid;
23
+ private readonly logRoot;
24
+ private readonly sessionLog;
25
+ private trackerPath;
26
+ private lock;
27
+ paused: boolean;
28
+ draining: boolean;
29
+ stopLevel: number;
30
+ constructor(options: Options, cases: Case[], allCases: Case[]);
31
+ private get info();
32
+ private progress;
33
+ private finishedRecords;
34
+ sessionStats(): SessionStats;
35
+ private tableRows;
36
+ private tableTotals;
37
+ recordOutcome: (rec: Rec) => string;
38
+ private statusLeft;
39
+ private paneView;
40
+ /**
41
+ * The slots worth a pane: the ones actually running. With nothing running and nothing left to
42
+ * dispatch — the end of a session — the finished slots stand in so the final frame still shows
43
+ * every result. With work still queued, the region stays empty and says why instead, since a
44
+ * queue held up by a barrier or a tag is exactly the state worth seeing.
45
+ */
46
+ private visibleSlots;
47
+ viewModel(): ViewModel;
48
+ /** Why the pane region is empty — without this a tag-blocked queue looks like a hang. */
49
+ private panesNote;
50
+ private renderOnce;
51
+ togglePause(): void;
52
+ requestDrain(): void;
53
+ requestStop(): void;
54
+ private logPlain;
55
+ private writeSummary;
56
+ private writeTracker;
57
+ exitCode(): number;
58
+ private runCase;
59
+ private recordSkipped;
60
+ private recordHarnessError;
61
+ private worker;
62
+ private renderLoop;
63
+ run(): Promise<number>;
64
+ /** Acquire the lock, run inside the alt screen, print the summary, always clean up. */
65
+ main(): Promise<number>;
66
+ get sessionRecords(): readonly Rec[];
67
+ get sessionInfo(): SessionInfo;
68
+ }