@cairn-tool/cairn 3.3.1 → 3.5.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 (108) hide show
  1. package/README.md +6 -4
  2. package/dist/agent/manifest.d.ts +10 -0
  3. package/dist/agent/manifest.js +23 -0
  4. package/dist/agent/manifest.js.map +1 -1
  5. package/dist/agent/parser.js +1 -15
  6. package/dist/agent/parser.js.map +1 -1
  7. package/dist/agent/render.js +47 -3
  8. package/dist/agent/render.js.map +1 -1
  9. package/dist/cli.js +48 -3
  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/commands/scripts.d.ts +16 -1
  21. package/dist/commands/scripts.js +27 -4
  22. package/dist/commands/scripts.js.map +1 -1
  23. package/dist/config-schema.d.ts +2 -0
  24. package/dist/config-schema.js +4 -0
  25. package/dist/config-schema.js.map +1 -1
  26. package/dist/config.d.ts +12 -2
  27. package/dist/config.js +36 -7
  28. package/dist/config.js.map +1 -1
  29. package/dist/contract/registry.js +46 -3
  30. package/dist/contract/registry.js.map +1 -1
  31. package/dist/contract/schemas/index.js +2 -0
  32. package/dist/contract/schemas/index.js.map +1 -1
  33. package/dist/contract/schemas/qa.d.ts +2 -0
  34. package/dist/contract/schemas/qa.js +111 -0
  35. package/dist/contract/schemas/qa.js.map +1 -0
  36. package/dist/qa/agent.d.ts +25 -0
  37. package/dist/qa/agent.js +142 -0
  38. package/dist/qa/agent.js.map +1 -0
  39. package/dist/qa/agents/claude-code.d.ts +2 -0
  40. package/dist/qa/agents/claude-code.js +121 -0
  41. package/dist/qa/agents/claude-code.js.map +1 -0
  42. package/dist/qa/agents/cursor.d.ts +5 -0
  43. package/dist/qa/agents/cursor.js +143 -0
  44. package/dist/qa/agents/cursor.js.map +1 -0
  45. package/dist/qa/agents/index.d.ts +32 -0
  46. package/dist/qa/agents/index.js +83 -0
  47. package/dist/qa/agents/index.js.map +1 -0
  48. package/dist/qa/agents/types.d.ts +61 -0
  49. package/dist/qa/agents/types.js +18 -0
  50. package/dist/qa/agents/types.js.map +1 -0
  51. package/dist/qa/async.d.ts +27 -0
  52. package/dist/qa/async.js +91 -0
  53. package/dist/qa/async.js.map +1 -0
  54. package/dist/qa/casefile.d.ts +35 -0
  55. package/dist/qa/casefile.js +181 -0
  56. package/dist/qa/casefile.js.map +1 -0
  57. package/dist/qa/cases.d.ts +51 -0
  58. package/dist/qa/cases.js +144 -0
  59. package/dist/qa/cases.js.map +1 -0
  60. package/dist/qa/config.d.ts +50 -0
  61. package/dist/qa/config.js +105 -0
  62. package/dist/qa/config.js.map +1 -0
  63. package/dist/qa/demo.d.ts +10 -0
  64. package/dist/qa/demo.js +194 -0
  65. package/dist/qa/demo.js.map +1 -0
  66. package/dist/qa/format.d.ts +38 -0
  67. package/dist/qa/format.js +208 -0
  68. package/dist/qa/format.js.map +1 -0
  69. package/dist/qa/harness.d.ts +68 -0
  70. package/dist/qa/harness.js +528 -0
  71. package/dist/qa/harness.js.map +1 -0
  72. package/dist/qa/keys.d.ts +12 -0
  73. package/dist/qa/keys.js +96 -0
  74. package/dist/qa/keys.js.map +1 -0
  75. package/dist/qa/layout.d.ts +102 -0
  76. package/dist/qa/layout.js +278 -0
  77. package/dist/qa/layout.js.map +1 -0
  78. package/dist/qa/lock.d.ts +5 -0
  79. package/dist/qa/lock.js +114 -0
  80. package/dist/qa/lock.js.map +1 -0
  81. package/dist/qa/options.d.ts +38 -0
  82. package/dist/qa/options.js +165 -0
  83. package/dist/qa/options.js.map +1 -0
  84. package/dist/qa/outcome.d.ts +30 -0
  85. package/dist/qa/outcome.js +162 -0
  86. package/dist/qa/outcome.js.map +1 -0
  87. package/dist/qa/reports.d.ts +51 -0
  88. package/dist/qa/reports.js +121 -0
  89. package/dist/qa/reports.js.map +1 -0
  90. package/dist/qa/schedule.d.ts +55 -0
  91. package/dist/qa/schedule.js +145 -0
  92. package/dist/qa/schedule.js.map +1 -0
  93. package/dist/qa/screen.d.ts +31 -0
  94. package/dist/qa/screen.js +146 -0
  95. package/dist/qa/screen.js.map +1 -0
  96. package/dist/qa/slot.d.ts +28 -0
  97. package/dist/qa/slot.js +126 -0
  98. package/dist/qa/slot.js.map +1 -0
  99. package/dist/qa/tracker.d.ts +36 -0
  100. package/dist/qa/tracker.js +357 -0
  101. package/dist/qa/tracker.js.map +1 -0
  102. package/dist/query/execute.js +0 -0
  103. package/dist/query/execute.js.map +1 -1
  104. package/dist/scripts/resolve.js +1 -4
  105. package/dist/scripts/resolve.js.map +1 -1
  106. package/dist/usage/providers/cursor.js +1 -1
  107. package/dist/usage/providers/cursor.js.map +1 -1
  108. package/package.json +2 -1
@@ -0,0 +1,144 @@
1
+ import { existsSync, statSync } from "node:fs";
2
+ import { basename, dirname, isAbsolute, join, relative } from "node:path";
3
+ import { displayBinary, resolveAgent, resolveBinary } from "./agents/index.js";
4
+ import { loadCaseFiles } from "./casefile.js";
5
+ export class Case {
6
+ name;
7
+ number;
8
+ /** Absolute path to `_plans/tc-N.yaml`. */
9
+ file;
10
+ outDir;
11
+ repo;
12
+ /** The YAML `name` — a human title, distinct from `name` above, which is the identity. */
13
+ title;
14
+ agentName;
15
+ model;
16
+ parallel;
17
+ tags;
18
+ /** The plan text itself, verbatim from the YAML block scalar. */
19
+ plan;
20
+ constructor(spec, outDir, repo, defaultModel) {
21
+ this.name = spec.name;
22
+ this.number = spec.number;
23
+ this.file = spec.file;
24
+ this.outDir = outDir;
25
+ this.repo = repo;
26
+ this.title = spec.title;
27
+ this.agentName = spec.agent;
28
+ this.model = spec.model ?? defaultModel;
29
+ this.parallel = spec.parallel;
30
+ this.tags = spec.tags;
31
+ this.plan = spec.plan;
32
+ }
33
+ /** Python: Path.relative_to(repo).as_posix(), falling back to the absolute path. */
34
+ rel(path) {
35
+ const rel = relative(this.repo, path);
36
+ if (rel === "" || rel.startsWith("..") || isAbsolute(rel))
37
+ return path;
38
+ return rel;
39
+ }
40
+ get fileRel() {
41
+ return this.rel(this.file);
42
+ }
43
+ get outRel() {
44
+ return `${this.rel(this.outDir)}/`;
45
+ }
46
+ get tempDir() {
47
+ return join(dirname(this.outDir), `${basename(this.outDir)}-temp`);
48
+ }
49
+ get tempRel() {
50
+ return `${this.rel(this.tempDir)}/`;
51
+ }
52
+ /** How this case schedules, for the dry-run listing and the pane note. */
53
+ get constraint() {
54
+ if (this.parallel)
55
+ return "parallel";
56
+ if (this.tags.length === 0)
57
+ return "exclusive";
58
+ return `tags ${this.tags.join(", ")}`;
59
+ }
60
+ /**
61
+ * Load-bearing: handed verbatim to cursor-agent. The dash is an em dash (U+2014). The plan is
62
+ * inlined rather than @-referenced, so the write constraint is stated before it, not after.
63
+ */
64
+ get prompt() {
65
+ return (`Perform the test-case plan below. Write every deliverable to ${this.tempRel}, ` +
66
+ `creating that folder yourself. Do not write to ${this.outRel} — the harness ` +
67
+ `renames the temp folder there only after this run succeeds.\n\n---\n\n${this.plan}`);
68
+ }
69
+ }
70
+ /** Python: int(re.search(r"(\d+)", name).group(1)) */
71
+ export function caseNumber(name) {
72
+ const match = /(\d+)/.exec(name);
73
+ if (!match)
74
+ throw new Error(`no number in case name: ${name}`);
75
+ return Number.parseInt(match[1], 10);
76
+ }
77
+ function isFile(path) {
78
+ try {
79
+ return statSync(path).isFile();
80
+ }
81
+ catch {
82
+ return false;
83
+ }
84
+ }
85
+ function fallbackModel(spec, defaults) {
86
+ if (typeof defaults === "string")
87
+ return spec.model ?? defaults;
88
+ return spec.model ?? defaults[spec.agent] ?? resolveAgent(spec.agent).defaultModel;
89
+ }
90
+ /** Every case under `_plans/`, whether or not it has been run, plus every load error. */
91
+ export function catalog(runs, repo, defaultModel) {
92
+ const loaded = loadCaseFiles(join(runs, "_plans"));
93
+ const cases = loaded.cases.map((spec) => new Case(spec, join(runs, spec.name), repo, fallbackModel(spec, defaultModel)));
94
+ // loadCaseFiles already sorts numerically — tc-2 must precede tc-10.
95
+ return { cases, errors: loaded.errors };
96
+ }
97
+ /** Cases whose run folder does not exist, natural-sorted, then filtered. */
98
+ export function discover(all, only = null, limit = null) {
99
+ let cases = all.filter((c) => !existsSync(c.outDir));
100
+ if (only !== null)
101
+ cases = cases.filter((c) => only.has(c.name));
102
+ if (limit !== null)
103
+ cases = cases.slice(0, limit);
104
+ return cases;
105
+ }
106
+ export function stillEligible(kase) {
107
+ return isFile(kase.file) && !existsSync(kase.outDir);
108
+ }
109
+ export function agentCommand(binary, kase) {
110
+ return resolveAgent(kase.agentName).buildArgv({ repo: kase.repo, model: kase.model, prompt: kase.prompt }, binary);
111
+ }
112
+ export function commandFor(kase, explicitAgent) {
113
+ return agentCommand(resolveBinary(kase.agentName, explicitAgent), kase);
114
+ }
115
+ /** Python: shlex._find_unsafe = re.compile(r'[^\w@%+=:,./-]', re.ASCII).search */
116
+ const SHELL_SAFE = /^[A-Za-z0-9_@%+=:,./-]+$/;
117
+ export function shellQuote(s) {
118
+ if (SHELL_SAFE.test(s))
119
+ return s;
120
+ return `'${s.replaceAll("'", `'"'"'`)}'`;
121
+ }
122
+ export function shellJoin(argv) {
123
+ return argv.map(shellQuote).join(" ");
124
+ }
125
+ /** The argv with the plan body elided, for a `--dry-run` listing that stays readable. */
126
+ export function elidedCommand(binary, kase) {
127
+ const argv = agentCommand(binary, kase);
128
+ const head = kase.prompt.slice(0, kase.prompt.indexOf("\n\n---\n\n"));
129
+ const hidden = kase.plan.length;
130
+ argv[argv.length - 1] = `${head} …[+${hidden.toLocaleString("en-US")} chars of plan]…`;
131
+ return argv;
132
+ }
133
+ /** Argv for the --dry-run listing: never spawned, so an absent backend is not an error. */
134
+ export function displayCommandFor(kase, explicitAgent) {
135
+ return agentCommand(displayBinary(kase.agentName, explicitAgent), kase);
136
+ }
137
+ export function elidedCommandFor(kase, explicitAgent) {
138
+ const argv = displayCommandFor(kase, explicitAgent);
139
+ const head = kase.prompt.slice(0, kase.prompt.indexOf("\n\n---\n\n"));
140
+ const hidden = kase.plan.length;
141
+ argv[argv.length - 1] = `${head} …[+${hidden.toLocaleString("en-US")} chars of plan]…`;
142
+ return argv;
143
+ }
144
+ //# sourceMappingURL=cases.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cases.js","sourceRoot":"","sources":["../../src/qa/cases.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /** Every `raise SystemExit("msg")` in the Python becomes one of these. */
2
+ export declare class UserError extends Error {
3
+ }
4
+ export declare const DEFAULT_PARALLEL = 8;
5
+ export declare const DEFAULT_TIMEOUT_MIN = 60;
6
+ export declare const REPORT = "_report.md";
7
+ export declare const RESULTS = "_results.md";
8
+ export declare const TRANSCRIPT_CAP = 400;
9
+ export declare const RENDER_HZ = 4;
10
+ /** Digits 1..9 zoom a pane; a tenth pane simply has no key. */
11
+ export declare const MAX_ZOOM_KEYS = 9;
12
+ /**
13
+ * A case's plan travels inside the prompt argv, so it is bounded by ARG_MAX (1 MiB here, shared
14
+ * with the environment). Well under it, with a message that names the file instead of E2BIG.
15
+ */
16
+ export declare const MAX_PROMPT_BYTES: number;
17
+ export declare const DONE_OK = "ok";
18
+ export declare const DONE_FAIL: ReadonlySet<string>;
19
+ export interface Options {
20
+ repo: string;
21
+ runsDir: string;
22
+ /** Maximum agents in flight; per-case `parallel`/`tags` may hold it below this. */
23
+ parallel: number;
24
+ /**
25
+ * Session-log label for `--model`. Per-case fallbacks live on the Case via
26
+ * `models` / the profile default — a mixed queue does not share one slug.
27
+ */
28
+ model: string;
29
+ only: ReadonlySet<string> | null;
30
+ limit: number | null;
31
+ dryRun: boolean;
32
+ showPrompts: boolean;
33
+ noTui: boolean;
34
+ timeoutMs: number;
35
+ /** Explicit binary for every backend, or null to resolve per profile on PATH. */
36
+ agent: string | null;
37
+ /** When true, main() does not print the human console summary (JSON owns stdout). */
38
+ json: boolean;
39
+ }
40
+ /** Python: resolve_dir(raw, base, what) */
41
+ export declare function resolveDir(raw: string, base: string, what: string): string;
42
+ /** Python: shutil.which — POSIX PATH walk, no PATHEXT handling. */
43
+ export declare function which(name: string): string | null;
44
+ /**
45
+ * An explicit `--agent` path. Kept expanduser'd as given — it is not absolutized,
46
+ * and the result goes verbatim into command.txt and --dry-run output.
47
+ */
48
+ export declare function findExplicitAgent(explicit: string): string;
49
+ /** Python: parse_only("27, tc-28,") -> {"tc-27", "tc-28"} */
50
+ export declare function parseOnly(raw: string | null | undefined): ReadonlySet<string> | null;
@@ -0,0 +1,105 @@
1
+ import { accessSync, constants, realpathSync, statSync } from "node:fs";
2
+ import { isAbsolute, join, resolve } from "node:path";
3
+ import { homedir } from "node:os";
4
+ /** Every `raise SystemExit("msg")` in the Python becomes one of these. */
5
+ export class UserError extends Error {
6
+ }
7
+ export const DEFAULT_PARALLEL = 8;
8
+ export const DEFAULT_TIMEOUT_MIN = 60;
9
+ export const REPORT = "_report.md";
10
+ export const RESULTS = "_results.md";
11
+ export const TRANSCRIPT_CAP = 400;
12
+ export const RENDER_HZ = 4;
13
+ /** Digits 1..9 zoom a pane; a tenth pane simply has no key. */
14
+ export const MAX_ZOOM_KEYS = 9;
15
+ /**
16
+ * A case's plan travels inside the prompt argv, so it is bounded by ARG_MAX (1 MiB here, shared
17
+ * with the environment). Well under it, with a message that names the file instead of E2BIG.
18
+ */
19
+ export const MAX_PROMPT_BYTES = 256 * 1024;
20
+ export const DONE_OK = "ok";
21
+ export const DONE_FAIL = new Set([
22
+ "error",
23
+ "timeout",
24
+ "no-output",
25
+ "no-folder",
26
+ ]);
27
+ function expandUser(raw) {
28
+ if (raw === "~")
29
+ return homedir();
30
+ if (raw.startsWith("~/"))
31
+ return join(homedir(), raw.slice(2));
32
+ return raw;
33
+ }
34
+ /** Python: resolve_dir(raw, base, what) */
35
+ export function resolveDir(raw, base, what) {
36
+ const expanded = expandUser(raw);
37
+ const joined = isAbsolute(expanded) ? expanded : join(base, expanded);
38
+ // Python's Path.resolve() also resolves symlinks — on macOS /tmp is a link to /private/tmp,
39
+ // so path.resolve() alone would produce a different --workspace and different log paths.
40
+ let path;
41
+ try {
42
+ path = realpathSync(joined);
43
+ }
44
+ catch {
45
+ path = resolve(joined);
46
+ }
47
+ let dir = false;
48
+ try {
49
+ dir = statSync(path).isDirectory();
50
+ }
51
+ catch {
52
+ /* keep false */
53
+ }
54
+ if (!dir)
55
+ throw new UserError(`${what} is not a directory: ${path}`);
56
+ return path;
57
+ }
58
+ function isExecutableFile(path) {
59
+ try {
60
+ if (!statSync(path).isFile())
61
+ return false;
62
+ accessSync(path, constants.X_OK);
63
+ return true;
64
+ }
65
+ catch {
66
+ return false;
67
+ }
68
+ }
69
+ /** Python: shutil.which — POSIX PATH walk, no PATHEXT handling. */
70
+ export function which(name) {
71
+ if (name.includes("/"))
72
+ return isExecutableFile(name) ? name : null;
73
+ const path = process.env["PATH"] ?? "";
74
+ for (const dir of path.split(":")) {
75
+ const candidate = join(dir === "" ? "." : dir, name);
76
+ if (isExecutableFile(candidate))
77
+ return candidate;
78
+ }
79
+ return null;
80
+ }
81
+ /**
82
+ * An explicit `--agent` path. Kept expanduser'd as given — it is not absolutized,
83
+ * and the result goes verbatim into command.txt and --dry-run output.
84
+ */
85
+ export function findExplicitAgent(explicit) {
86
+ const path = expandUser(explicit);
87
+ if (!isExecutableFile(path)) {
88
+ throw new UserError(`--agent is not an executable file: ${explicit}`);
89
+ }
90
+ return path;
91
+ }
92
+ /** Python: parse_only("27, tc-28,") -> {"tc-27", "tc-28"} */
93
+ export function parseOnly(raw) {
94
+ if (raw === null || raw === undefined || raw === "")
95
+ return null;
96
+ const names = new Set();
97
+ for (const part of raw.split(",")) {
98
+ const trimmed = part.trim();
99
+ if (!trimmed)
100
+ continue;
101
+ names.add(trimmed.startsWith("tc-") ? trimmed : `tc-${trimmed}`);
102
+ }
103
+ return names;
104
+ }
105
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/qa/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export interface DemoOptions {
2
+ rows?: number;
3
+ cols?: number;
4
+ table?: boolean;
5
+ once?: boolean;
6
+ /** How many panes to show, so the dynamic pane region can be eyeballed at any width. */
7
+ panes?: number;
8
+ }
9
+ /** A canned frame, so the TUI can be eyeballed at any size without launching an agent. */
10
+ export declare function runDemo(opts: DemoOptions): Promise<number>;
@@ -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;