#!/usr/bin/env node /* eslint-disable */ /** * Verifies repository structure conventions. * Run with: node scripts/check-structure.js */ import fs from "fs"; import path from "path"; import { fileURLToPath } from "url"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const SRC_DIR = path.join(__dirname, "..", "src"); let errors = 0; let warnings = 0; // Known acceptable deviations — documented in naming.md const OVERSIZE_ALLOWLIST = [ // Form-heavy admin tabs: 15+ fields each, splitting would create micro-components "components/features/tool-workshop/ToolTypesTab.tsx", "components/features/tool-workshop/ToolConfigsTab.tsx", // Complex terminal hook: WS lifecycle + ping-pong + echo + resize debouncing "hooks/use-terminal-connection.ts", // Terminal component: xterm lifecycle + resize observer + overlay UI "components/features/terminal/TerminalComponent.tsx", // Instance list with health polling + inline confirmations "components/features/session/InstanceList.tsx", // Dialog with form validation + SSH key handling "components/features/project/RepositoryCreateDialog.tsx", // Test files: complex test coverage "hooks/use-terminal-connection.test.ts", "pages/ToolWorkshopPage.test.tsx", // Global utility CSS: will be further split in future iteration "styles/utilities.css", ]; function checkFileSize(filePath, maxLines = 300) { const content = fs.readFileSync(filePath, "utf-8"); const lines = content.split("\n").length; const relative = path.relative(SRC_DIR, filePath); if (lines > maxLines) { if (OVERSIZE_ALLOWLIST.includes(relative)) { console.warn(`⚠️ OVERSIZED (${lines} lines, allowlisted): ${relative}`); warnings++; } else { console.error(`❌ OVERSIZED (${lines} lines): ${relative}`); errors++; } } } function walk(dir, callback) { for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { const fullPath = path.join(dir, entry.name); if (entry.isDirectory()) { if (entry.name === "node_modules" || entry.name.startsWith(".")) continue; walk(fullPath, callback); } else { callback(fullPath); } } } console.log("Checking file sizes...\n"); walk(SRC_DIR, (filePath) => { const ext = path.extname(filePath); if ([".ts", ".tsx", ".py", ".css"].includes(ext)) { checkFileSize(filePath); } }); console.log("\n---"); if (errors === 0 && warnings === 0) { console.log("✅ All checks passed!"); process.exit(0); } else if (errors === 0) { console.log(`✅ All checks passed with ${warnings} warning(s)`); process.exit(0); } else { console.log(`❌ ${errors} error(s), ${warnings} warning(s)`); process.exit(1); }