#!/usr/bin/env node import { existsSync, readFileSync } from "node:fs"; import { join } from "node:path"; const root = process.cwd(); const errors = []; const localArtifacts = []; const info = []; const showLocalArtifacts = process.env.PROJECTBRAIN_AUDIT_VERBOSE_LOCAL_ARTIFACTS === "1"; const requireFile = (path, label = path) => { if (!existsSync(join(root, path))) errors.push(`Missing ${label}: ${path}`); }; const requireText = (path, text, label = text) => { const fullPath = join(root, path); if (!existsSync(fullPath)) { errors.push(`Missing file for text check: ${path}`); return; } const content = readFileSync(fullPath, "utf8"); if (!content.includes(text)) errors.push(`Missing ${label} in ${path}`); }; const warnIfExists = (path, label = path) => { if (existsSync(join(root, path))) localArtifacts.push(`Generated or legacy artifact present: ${label}`); }; const rootPackage = JSON.parse(readFileSync(join(root, "package.json"), "utf8")); const backendPackage = JSON.parse(readFileSync(join(root, "backend/package.json"), "utf8")); const parseWorkspaceAllowBuilds = (workspaceYaml) => { const allowed = new Set(); const allowBuildsIndex = workspaceYaml.indexOf("allowBuilds:"); if (allowBuildsIndex === -1) return allowed; const lines = workspaceYaml.slice(allowBuildsIndex).split("\n").slice(1); for (const line of lines) { if (/^[^\s#]/.test(line)) break; const match = line.match(/^\s*[\'"]?([^\'":]+)[\'"]?\s*:\s*(true|false)\s*(?:#.*)?$/); if (match && match[2] === "true") allowed.add(match[1].trim()); } return allowed; }; const workspaceAllowBuilds = existsSync(join(root, "pnpm-workspace.yaml")) ? parseWorkspaceAllowBuilds(readFileSync(join(root, "pnpm-workspace.yaml"), "utf8")) : new Set(); if (rootPackage.packageManager !== "pnpm@11.5.2") { errors.push("Root package.json must pin packageManager to pnpm@11.5.2 so Docker/Corepack does not float to a different pnpm release."); } const requiredPnpmBuiltDeps = ["@prisma/client", "@prisma/engines", "@swc/core", "esbuild", "prisma"]; for (const dependency of requiredPnpmBuiltDeps) { if (!workspaceAllowBuilds.has(dependency)) { errors.push(`pnpm-workspace.yaml allowBuilds must approve ${dependency} for Docker builds with pnpm 11.`); } } console.log("ProjectBrain doctor"); console.log("==================="); [ "README.md", ".env.example", "backend/.env.example", "docs/PROJECTBRAIN_DOCUMENTATION_INDEX.md", "docs/FULL_VERSION_ROADMAP.md", "docs/VERTICAL_SLICE_CORE_FLOW_PLAN.md", "docs/PROJECTBRAIN_STATUS_AND_GATES.md", "docs/P2_LOCAL_RUNTIME_VERIFICATION.md", "docs/RUNTIME_ROUND_5_BACKEND_HEALTH_AND_SUITE.md", "docs/PROJECTBRAIN_RUNTIME_COMMANDS.md", "backend/scripts/smoke-p2.mjs", "backend/scripts/smoke-fixture-api.mjs", "backend/scripts/smoke-real-read-pilot.mjs", "backend/scripts/smoke-vertical-slice.mjs", "backend/scripts/smoke-runtime-suite.mjs", "backend/scripts/docker-entrypoint.sh", "scripts/check-database-readiness.mjs", "public/site.webmanifest", "public/favicon.svg", "public/favicon.ico", ].forEach((file) => requireFile(file)); [ ["validate:all", "root validation"], ["audit:release", "release hygiene audit"], ["audit:runtime", "runtime readiness audit"], ["audit:database", "database readiness audit"], ["smoke:backend:fixtures", "fixture smoke"], ["smoke:backend:real-read-pilot", "real-read pilot smoke"], ["smoke:backend:vertical-slice", "vertical slice smoke"], ["smoke:backend:runtime-suite", "runtime suite smoke"], ["smoke:backend:database-suite", "database suite smoke"], ["smoke:backend:brainstorms", "Brainstorm CPU smoke"], ["smoke:backend:initiatives", "Initiative smoke"], ].forEach(([script, label]) => { if (!rootPackage.scripts?.[script]) errors.push(`Missing ${label} script: ${script}`); }); [ ["smoke:p2", "P2 database smoke"], ["smoke:fixtures", "fixture smoke"], ["smoke:real-read-pilot", "real read smoke"], ["smoke:vertical-slice", "vertical slice smoke"], ["smoke:runtime-suite", "runtime suite smoke"], ["smoke:database-suite", "database suite smoke"], ["smoke:brainstorms", "Brainstorm CPU smoke"], ["smoke:initiatives", "Initiative smoke"], ["db:deploy", "Prisma migrate deploy"], ["db:status", "Prisma migration status"], ["db:validate", "Prisma schema validate"], ].forEach(([script, label]) => { if (!backendPackage.scripts?.[script]) errors.push(`Missing backend ${label} script: ${script}`); }); requireText("index.html", "ProjectBrain", "ProjectBrain title"); requireText("index.html", "ProjectBrain", "ProjectBrain browser metadata"); requireText("src/features/projects/pages/RuntimeReadiness.tsx", "Backend health", "backend health card"); requireText("src/features/projects/pages/RuntimeReadiness.tsx", "Database readiness", "database readiness card"); requireText("src/features/projects/pages/RuntimeReadiness.tsx", "Vertical slice smoke", "vertical slice smoke card"); requireText("src/shared/api/realClient.ts", "getHealth", "real backend health client"); requireText("src/shared/api/realClient.ts", "getSystemReadiness", "real backend system readiness client"); requireText("src/shared/hooks/useProjectData.ts", "useBackendHealthData", "backend health hook"); requireText("src/shared/hooks/useProjectData.ts", "useBackendSystemReadinessData", "backend system readiness hook"); requireText("docs/PROJECTBRAIN_STATUS_AND_GATES.md", "P2", "P2 gate documentation"); [ "node_modules", "backend/node_modules", "dist", "backend/dist", "checkpoints", "src/App.css", "src/globals.css", ].forEach((path) => warnIfExists(path)); info.push(`Package manager: ${rootPackage.packageManager ?? "not pinned"}`); info.push(`Approved pnpm workspace allowBuilds: ${Array.from(workspaceAllowBuilds).sort().join(", ") || "none"}`); info.push(`Frontend package: ${rootPackage.name}@${rootPackage.version}`); info.push(`Backend package: ${backendPackage.name}@${backendPackage.version}`); info.push("Recommended local command order:"); info.push(" 1. pnpm install"); info.push(" 2. pnpm run audit:database"); info.push(" 3. pnpm run validate:all"); info.push(" 4. pnpm run audit:all"); info.push(" 5. docker compose up -d --build"); info.push(" 6. pnpm run smoke:backend:system-readiness"); info.push(" 7. pnpm run smoke:backend:runtime-suite && pnpm --filter projectbrain-api run smoke:blueprints"); info.push(" 8. pnpm run smoke:backend:database-suite"); for (const line of info) console.log(`INFO ${line}`); if (showLocalArtifacts) { for (const line of localArtifacts) console.log(`INFO ${line}`); } for (const line of errors) console.error(`FAIL ${line}`); if (errors.length) { console.error(`\nFAIL: ${errors.length} blocking doctor issue(s) found.`); process.exit(1); } console.log("\nPASS: ProjectBrain doctor found no blocking runtime/release scaffold issues.");