Answers Filbert's comment 27251 and Dewey's comment 27253: R1 type-ahead
window, R2 terminal cell widths, B1 cut inside the question, 30 mutants
killed, N1-N8. Candidate manifest sha256 3f2d8f8e. Gate on c506c6ec:
cli 106/106 and every scripts/test-*.sh exit 0. The feature stays
uncommitted.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
58 lines
4.4 KiB
JavaScript
58 lines
4.4 KiB
JavaScript
// Round 2 mutants: each edits one source line, runs the cli suite, restores.
|
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
import { spawnSync } from "node:child_process";
|
|
import { join } from "node:path";
|
|
const CLI = process.argv[2];
|
|
const M = [
|
|
["resolve-ask-skipped-when-note", "cli.mjs", "if (ask && !(await ask(", "if (ask && note === undefined && !(await ask("],
|
|
["unknown-space-only", "app.mjs", "/^outcome[ -]unknown/", "/^outcome unknown/"],
|
|
["restore-no-altleave", "app.mjs", 'stdout.write("\\x1b[?25h\\x1b[?1049l");', ""],
|
|
["restore-no-raw-off", "app.mjs", "stdin.setRawMode?.(false);", ""],
|
|
["no-sigterm", "app.mjs", 'const SIGNALS = ["SIGTERM", "SIGINT", "SIGHUP"];', 'const SIGNALS = ["SIGINT", "SIGHUP"];'],
|
|
["no-sigint", "app.mjs", 'const SIGNALS = ["SIGTERM", "SIGINT", "SIGHUP"];', 'const SIGNALS = ["SIGTERM", "SIGHUP"];'],
|
|
["no-sighup", "app.mjs", 'const SIGNALS = ["SIGTERM", "SIGINT", "SIGHUP"];', 'const SIGNALS = ["SIGTERM", "SIGINT"];'],
|
|
["resize-unguarded", "app.mjs", "const onResize = () => guard(() => app.resize(stdout.columns || 80, stdout.rows || 24));", "const onResize = () => app.resize(stdout.columns || 80, stdout.rows || 24);"],
|
|
["paint-no-reverse", "screens.mjs", "return l.sel ? `\\x1b[7m${body}\\x1b[27m` : body;", "return body;"],
|
|
["cells-no-cjk", "screens.mjs", "(n >= 0x2e80 && n <= 0xa4cf && n !== 0x303f) ||", ""],
|
|
["cells-no-emoji-presentation", "screens.mjs", "/\\p{Emoji_Presentation}/u.test(c) ||", ""],
|
|
["cells-no-fe0f", "screens.mjs", "if (n === 0xfe0f) return prev && /\\p{Emoji}/u.test(prev) && cells(prev) === 1 ? 1 : 0;", ""],
|
|
["overlay-cut-loses-prompt", "screens.mjs", "...tail].slice(-room);", "].slice(0, room);"],
|
|
["b1-cut-keeps-end", "screens.mjs", "return keep >= 1 ? [...head, ...question.slice(0, keep), cutLine(\"question\", w - 4), ...tail] : b;", "return b;"],
|
|
["app-host-not-live", "cli.mjs", "const business = state?.live ? state.business : null;", "const business = state?.business ?? null;"],
|
|
["quit-no-answer", "app.mjs", " this.cancelAsk?.();\n this.wake?.();", " this.answer?.(\"y\");\n this.wake?.();"],
|
|
["refresh-in-overlay", "app.mjs", "s.overlay || s.command !== null ? null : this.#read", "this.#read"],
|
|
["refresh-answers", "app.mjs", 'if (k !== "refresh") this.answer(k);', "this.answer(k);"],
|
|
["refresh-drops-resolved", "app.mjs", 'this.#read(s.view, s.statusStyle === "success")', "this.#read(s.view)"],
|
|
["unknown-esc-stays", "app.mjs", ' if (k === "esc") this.state.overlay = null;', ""],
|
|
["trail-task-id", "app.mjs", 'view === "tasks" ? item.task_ref : view === "trail"', 'view === "tasks" ? item.title : view === "trail"'],
|
|
["business-keeps-data", "app.mjs", " this.state.data = {};", ""],
|
|
["nav-no-brackets", "screens.mjs", "piece(`[${label}]`, \"bold\")", "piece(` ${label} `, \"bold\")"],
|
|
["typeahead-kept", "app.mjs", " this.keys = [];\n this.answer", " this.answer"],
|
|
["no-arm-delay", "app.mjs", " }, this.askMs);", " }, 0);"],
|
|
["other-key-waits", "app.mjs", 'this.answer = (k) => answer(k === "y" || k === "Y");', 'this.answer = (k) => (k === "y" || k === "Y" ? answer(true) : ["n", "N", "esc", "enter", "q"].includes(k) ? answer(false) : null);'],
|
|
["no-capital-y", "app.mjs", 'answer(k === "y" || k === "Y")', 'answer(k === "y")'],
|
|
["command-types-names", "app.mjs", '[...k].length === 1 && k >= " "', 'k >= " "'],
|
|
["press-1-1", "app.mjs", 'n === 1 ? "1" : `1-${n}`', "`1-${n}`"],
|
|
["tiny-no-guard", "screens.mjs", "if (cols < MIN_COLS || rows < MIN_ROWS) return tooSmall(cols, rows, color);", ""],
|
|
];
|
|
const only = process.argv.slice(3);
|
|
let survived = 0;
|
|
for (const [name, file, from, to] of M) {
|
|
if (only.length && !only.includes(name)) continue;
|
|
const path = join(CLI, "src", file);
|
|
const orig = readFileSync(path, "utf8");
|
|
const n = orig.split(from).length - 1;
|
|
if (n !== 1) { console.log(`${name}: SKIP, found ${n} times`); continue; }
|
|
try {
|
|
writeFileSync(path, orig.replace(from, to));
|
|
const r = spawnSync(process.execPath, ["--test", "--test-timeout=20000", "tests/"], { cwd: CLI, encoding: "utf8", timeout: 180000 });
|
|
const fails = (r.stdout.match(/^✖ (.*?) \(/gm) ?? []).slice(0, 2).map((s) => s.slice(2, 60));
|
|
const killed = r.status !== 0;
|
|
if (!killed) survived++;
|
|
console.log(`${name}: ${killed ? "killed" : "SURVIVED"}${killed ? ` by ${[...new Set(fails)].join(" | ")}` : ""}`);
|
|
} finally {
|
|
writeFileSync(path, orig);
|
|
}
|
|
}
|
|
console.log(`survived ${survived}`);
|