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);