Files
CursorAi/.cursor/hooks/final-verify.cjs
T
2026-04-24 07:36:40 +08:00

95 lines
2.0 KiB
JavaScript

const fs = require("fs");
const path = require("path");
const { execSync } = require("child_process");
const statePath = path.join(process.cwd(), ".cursor", "pipeline-state.json");
function readState() {
if (!fs.existsSync(statePath)) {
return {
implementation: "pending",
review: "pending",
tests: "pending",
};
}
return JSON.parse(fs.readFileSync(statePath, "utf8"));
}
function fail(msg) {
process.stdout.write(JSON.stringify({ followup_message: msg }));
process.exit(0);
}
function getDndPlayerRoot() {
if (process.env.DND_PLAYER_ROOT) {
const r = path.resolve(process.env.DND_PLAYER_ROOT);
if (fs.existsSync(path.join(r, "package.json"))) return r;
}
const cwd = process.cwd();
const candidates = [
path.join(cwd, "..", "dnd_player"),
path.join(cwd, "dnd_player"),
cwd,
];
for (const root of candidates) {
const pkgPath = path.join(root, "package.json");
if (!fs.existsSync(pkgPath)) continue;
try {
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
if (
pkg.scripts?.lint &&
pkg.scripts?.typecheck &&
pkg.scripts?.test
) {
return root;
}
} catch {
continue;
}
}
return null;
}
const state = readState();
if (state.implementation !== "done") {
fail("Run frontend-senior stage");
}
if (state.review !== "done") {
fail("Run reviewer stage");
}
if (state.tests !== "done") {
fail("Run unit-tests stage");
}
const dndPlayerRoot = getDndPlayerRoot();
if (!dndPlayerRoot) {
fail(
"Cannot find dnd_player (expected sibling ../dnd_player or env DND_PLAYER_ROOT)",
);
}
const opts = { stdio: "pipe", cwd: dndPlayerRoot };
try {
execSync("npm run lint", opts);
} catch {
fail("Lint failed (run from dnd_player root)");
}
try {
execSync("npm run typecheck", opts);
} catch {
fail("Typecheck failed (run from dnd_player root)");
}
try {
execSync("npm run test", opts);
} catch {
fail("Tests failed (run from dnd_player root)");
}
process.exit(0);